mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
GameActionUtil: add getOptionalCostValues to return OptionalCost with its Cost objects
SpellAbility: add canPlayWithOptionalCost PlayerControllerHuman: got formated
This commit is contained in:
@@ -929,4 +929,11 @@ public class PlayerControllerAi extends PlayerController {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OptionalCostValue> chooseOptionalCosts(SpellAbility choosen,
|
||||
List<OptionalCostValue> optionalCostValues) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,6 +221,113 @@ public final class GameActionUtil {
|
||||
return alternatives;
|
||||
}
|
||||
|
||||
public static List<OptionalCostValue> getOptionalCostValues(final SpellAbility sa) {
|
||||
final List<OptionalCostValue> costs = Lists.newArrayList();
|
||||
if (sa == null || !sa.isSpell()) {
|
||||
return costs;
|
||||
}
|
||||
final Card source = sa.getHostCard();
|
||||
for (String keyword : source.getKeywords()) {
|
||||
if (keyword.startsWith("Buyback")) {
|
||||
final Cost cost = new Cost(keyword.substring(8), false);
|
||||
costs.add(new OptionalCostValue(OptionalCost.Buyback, cost));
|
||||
} else if (keyword.equals("Conspire")) {
|
||||
final String conspireCost = "tapXType<2/Creature.SharesColorWith/" +
|
||||
"untapped creature you control that shares a color with " + source.getName() + ">";
|
||||
final Cost cost = new Cost(conspireCost, false);
|
||||
costs.add(new OptionalCostValue(OptionalCost.Conspire, cost));
|
||||
} else if (keyword.startsWith("Entwine")) {
|
||||
String[] k = keyword.split(":");
|
||||
final Cost cost = new Cost(k[1], false);
|
||||
costs.add(new OptionalCostValue(OptionalCost.Entwine, cost));
|
||||
} else if (keyword.startsWith("Kicker")) {
|
||||
String[] sCosts = TextUtil.split(keyword.substring(6), ':');
|
||||
boolean generic = "Generic".equals(sCosts[sCosts.length - 1]);
|
||||
// If this is a "generic kicker" (Undergrowth), ignore value for kicker creations
|
||||
int numKickers = sCosts.length - (generic ? 1 : 0);
|
||||
for (int j = 0; j < numKickers; j++) {
|
||||
final Cost cost = new Cost(sCosts[j], false);
|
||||
OptionalCost type = null;
|
||||
if (!generic) {
|
||||
type = j == 0 ? OptionalCost.Kicker1 : OptionalCost.Kicker2;
|
||||
} else {
|
||||
type = OptionalCost.Generic;
|
||||
}
|
||||
costs.add(new OptionalCostValue(type, cost));
|
||||
}
|
||||
} else if (keyword.equals("Retrace")) {
|
||||
final Cost cost = new Cost("Discard<1/Land>", false);
|
||||
costs.add(new OptionalCostValue(OptionalCost.Retrace, cost));
|
||||
}
|
||||
|
||||
// Surge while having OptionalCost is none of them
|
||||
}
|
||||
return costs;
|
||||
}
|
||||
|
||||
public static SpellAbility addOptionalCosts(final SpellAbility sa, List<OptionalCostValue> list) {
|
||||
if (sa == null || list.isEmpty()) {
|
||||
return sa;
|
||||
}
|
||||
final SpellAbility result = sa.copy();
|
||||
for (OptionalCostValue v : list) {
|
||||
// need to copy cost, otherwise it does alter the original
|
||||
result.setPayCosts(result.getPayCosts().copy().add(v.getCost()));
|
||||
result.addOptionalCost(v.getType());
|
||||
|
||||
// add some extra logic, try to move it to other parts
|
||||
switch (v.getType()) {
|
||||
case Conspire:
|
||||
result.addConspireInstance();
|
||||
break;
|
||||
case Retrace:
|
||||
result.getRestrictions().setZone(ZoneType.Graveyard);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<SpellAbility> getAdditionalCostSpell(final SpellAbility sa) {
|
||||
final List<SpellAbility> abilities = Lists.newArrayList(sa);
|
||||
if (!sa.isSpell()) {
|
||||
return abilities;
|
||||
}
|
||||
final Card source = sa.getHostCard();
|
||||
for (String keyword : source.getKeywords()) {
|
||||
if (keyword.startsWith("AlternateAdditionalCost")) {
|
||||
final List<SpellAbility> newAbilities = Lists.newArrayList();
|
||||
String[] costs = TextUtil.split(keyword, ':');
|
||||
|
||||
final SpellAbility newSA = sa.copy();
|
||||
newSA.setBasicSpell(false);
|
||||
|
||||
final Cost cost1 = new Cost(costs[1], false);
|
||||
newSA.setDescription(sa.getDescription() + " (Additional cost " + cost1.toSimpleString() + ")");
|
||||
newSA.setPayCosts(cost1.add(sa.getPayCosts()));
|
||||
if (newSA.canPlay()) {
|
||||
newAbilities.add(newSA);
|
||||
}
|
||||
|
||||
//second option
|
||||
final SpellAbility newSA2 = sa.copy();
|
||||
newSA2.setBasicSpell(false);
|
||||
|
||||
final Cost cost2 = new Cost(costs[2], false);
|
||||
newSA2.setDescription(sa.getDescription() + " (Additional cost " + cost2.toSimpleString() + ")");
|
||||
newSA2.setPayCosts(cost2.add(sa.getPayCosts()));
|
||||
if (newSA2.canPlay()) {
|
||||
newAbilities.add(newSA2);
|
||||
}
|
||||
|
||||
abilities.clear();
|
||||
abilities.addAll(newAbilities);
|
||||
}
|
||||
}
|
||||
return abilities;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get optional additional costs.
|
||||
*
|
||||
@@ -229,44 +336,17 @@ public final class GameActionUtil {
|
||||
* @return an ArrayList<SpellAbility>.
|
||||
*/
|
||||
public static List<SpellAbility> getOptionalCosts(final SpellAbility original) {
|
||||
final List<SpellAbility> abilities = Lists.newArrayList();
|
||||
final List<SpellAbility> abilities = getAdditionalCostSpell(original);
|
||||
|
||||
final Card source = original.getHostCard();
|
||||
abilities.add(original);
|
||||
|
||||
if (!original.isSpell()) {
|
||||
return abilities;
|
||||
}
|
||||
|
||||
// Buyback, Kicker
|
||||
for (String keyword : source.getKeywords()) {
|
||||
if (keyword.startsWith("AlternateAdditionalCost")) {
|
||||
final List<SpellAbility> newAbilities = Lists.newArrayList();
|
||||
String[] costs = TextUtil.split(keyword, ':');
|
||||
for (SpellAbility sa : abilities) {
|
||||
final SpellAbility newSA = sa.copy();
|
||||
newSA.setBasicSpell(false);
|
||||
|
||||
final Cost cost1 = new Cost(costs[1], false);
|
||||
newSA.setDescription(sa.getDescription() + " (Additional cost " + cost1.toSimpleString() + ")");
|
||||
newSA.setPayCosts(cost1.add(sa.getPayCosts()));
|
||||
if (newSA.canPlay()) {
|
||||
newAbilities.add(newSA);
|
||||
}
|
||||
|
||||
//second option
|
||||
final SpellAbility newSA2 = sa.copy();
|
||||
newSA2.setBasicSpell(false);
|
||||
|
||||
final Cost cost2 = new Cost(costs[2], false);
|
||||
newSA2.setDescription(sa.getDescription() + " (Additional cost " + cost2.toSimpleString() + ")");
|
||||
newSA2.setPayCosts(cost2.add(sa.getPayCosts()));
|
||||
if (newSA2.canPlay()) {
|
||||
newAbilities.add(newAbilities.size(), newSA2);
|
||||
}
|
||||
}
|
||||
abilities.clear();
|
||||
abilities.addAll(newAbilities);
|
||||
} else if (keyword.startsWith("Buyback")) {
|
||||
if (keyword.startsWith("Buyback")) {
|
||||
for (int i = 0; i < abilities.size(); i++) {
|
||||
final SpellAbility newSA = abilities.get(i).copy();
|
||||
newSA.setBasicSpell(false);
|
||||
|
||||
@@ -6874,13 +6874,16 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
final Collection<SpellAbility> toRemove = Lists.newArrayListWithCapacity(abilities.size());
|
||||
for (final SpellAbility sa : abilities) {
|
||||
sa.setActivatingPlayer(player);
|
||||
// fix things like retrace
|
||||
// check only if SA can't be cast normally
|
||||
if (sa.canPlay(true)) {
|
||||
continue;
|
||||
}
|
||||
if ((removeUnplayable && !sa.canPlay()) || !sa.isPossible()) {
|
||||
toRemove.add(sa);
|
||||
}
|
||||
}
|
||||
for (final SpellAbility sa : toRemove) {
|
||||
abilities.remove(sa);
|
||||
}
|
||||
abilities.removeAll(toRemove);
|
||||
|
||||
if (getState(CardStateName.Original).getType().isLand() && player.canPlayLand(this)) {
|
||||
game.PLAY_LAND_SURROGATE.setHostCard(this);
|
||||
|
||||
@@ -4109,25 +4109,6 @@ public class CardFactoryUtil {
|
||||
kws.addSpellAbility(sa);
|
||||
}
|
||||
card.addSpellAbility(sa);
|
||||
} else if (keyword.equals("Retrace")) {
|
||||
final SpellAbility sa = card.getFirstSpellAbility();
|
||||
|
||||
final SpellAbility newSA = sa.copy();
|
||||
newSA.getRestrictions().setZone(ZoneType.Graveyard);
|
||||
newSA.getMapParams().put("CostDesc", "Retrace");
|
||||
newSA.getMapParams().put("Secondary", "True");
|
||||
newSA.setBasicSpell(false);
|
||||
|
||||
final Cost cost = new Cost("Discard<1/Land>", false).add(sa.getPayCosts());
|
||||
newSA.setPayCosts(cost);
|
||||
newSA.setIntrinsic(intrinsic);
|
||||
|
||||
//newSA.setDescription(sa.getDescription() + " (Retrace)");
|
||||
if (!intrinsic) {
|
||||
newSA.setTemporary(true);
|
||||
kws.addSpellAbility(newSA);
|
||||
}
|
||||
card.addSpellAbility(newSA);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import forge.game.cost.CostPartMana;
|
||||
import forge.game.mana.Mana;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.OptionalCostValue;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityStackInstance;
|
||||
import forge.game.spellability.TargetChoices;
|
||||
@@ -254,4 +255,6 @@ public abstract class PlayerController {
|
||||
public AnteResult getAnteResult() {
|
||||
return game.getOutcome().anteResult.get(player);
|
||||
}
|
||||
|
||||
public abstract List<OptionalCostValue> chooseOptionalCosts(SpellAbility choosen, List<OptionalCostValue> optionalCostValues);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.google.common.collect.Maps;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.CardTraitBase;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.IIdentifiable;
|
||||
@@ -309,6 +310,27 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
|
||||
// Spell, and Ability, and other Ability objects override this method
|
||||
public abstract boolean canPlay();
|
||||
|
||||
public boolean canPlay(boolean checkOptionalCosts) {
|
||||
if (canPlay()) {
|
||||
return true;
|
||||
}
|
||||
if (!checkOptionalCosts) {
|
||||
return false;
|
||||
}
|
||||
for (OptionalCostValue val : GameActionUtil.getOptionalCostValues(this)) {
|
||||
if (canPlayWithOptionalCost(val)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canPlayWithOptionalCost(OptionalCostValue opt) {
|
||||
SpellAbility saCopy = this.copy();
|
||||
saCopy = GameActionUtil.addOptionalCosts(saCopy, Lists.newArrayList(opt));
|
||||
return saCopy.canPlay();
|
||||
}
|
||||
|
||||
public boolean isPossible() {
|
||||
return canPlay(); //by default, ability is only possible if it can be played
|
||||
@@ -349,7 +371,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
}
|
||||
}
|
||||
view.updateCanPlay(this);
|
||||
view.updateCanPlay(this, false);
|
||||
}
|
||||
|
||||
public Player getTargetingPlayer() {
|
||||
@@ -1607,7 +1629,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
public SpellAbilityView getView() {
|
||||
view.updateHostCard(this);
|
||||
view.updateDescription(this);
|
||||
view.updateCanPlay(this);
|
||||
view.updateCanPlay(this, true);
|
||||
view.updatePromptIfOnlyPossibleAbility(this);
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ public class SpellAbilityView extends TrackableObject implements IHasCardView {
|
||||
public boolean canPlay() {
|
||||
return get(TrackableProperty.CanPlay);
|
||||
}
|
||||
void updateCanPlay(SpellAbility sa) {
|
||||
set(TrackableProperty.CanPlay, sa.canPlay());
|
||||
void updateCanPlay(SpellAbility sa, boolean optionalCost) {
|
||||
set(TrackableProperty.CanPlay, sa.canPlay(optionalCost));
|
||||
}
|
||||
|
||||
public boolean promptIfOnlyPossibleAbility() {
|
||||
|
||||
@@ -53,6 +53,7 @@ import forge.game.player.PlayerController;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.OptionalCostValue;
|
||||
import forge.game.spellability.Spell;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityStackInstance;
|
||||
@@ -676,4 +677,11 @@ public class PlayerControllerForTests extends PlayerController {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OptionalCostValue> chooseOptionalCosts(SpellAbility choosen,
|
||||
List<OptionalCostValue> optionalCostValues) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import forge.game.cost.*;
|
||||
import forge.game.spellability.OptionalCostValue;
|
||||
import forge.game.spellability.Spell;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -33,6 +34,7 @@ import forge.game.card.CardView;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
@@ -80,6 +82,11 @@ public class HumanPlay {
|
||||
return false;
|
||||
}
|
||||
|
||||
// extra play check
|
||||
if (!sa.canPlay()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flippedToCast && !castFaceDown) {
|
||||
source.turnFaceUp(false, false);
|
||||
}
|
||||
@@ -146,8 +153,19 @@ public class HumanPlay {
|
||||
if (!original.isSpell()) {
|
||||
return original;
|
||||
}
|
||||
final List<SpellAbility> abilities = GameActionUtil.getOptionalCosts(original);
|
||||
return p.getController().getAbilityToPlay(original.getHostCard(), abilities);
|
||||
|
||||
PlayerController c = p.getController();
|
||||
|
||||
// choose alternative additional cost
|
||||
final List<SpellAbility> abilities = GameActionUtil.getAdditionalCostSpell(original);
|
||||
|
||||
final SpellAbility choosen = c.getAbilityToPlay(original.getHostCard(), abilities);
|
||||
|
||||
List<OptionalCostValue> list = GameActionUtil.getOptionalCostValues(choosen);
|
||||
list = c.chooseOptionalCosts(choosen, list);
|
||||
|
||||
return GameActionUtil.addOptionalCosts(choosen, list);
|
||||
//final List<SpellAbility> abilities = GameActionUtil.getOptionalCosts(original);
|
||||
}
|
||||
|
||||
private static boolean payManaCostIfNeeded(final PlayerControllerHuman controller, final Player p, final SpellAbility sa) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user