add Goad Effect, add it to AttackRequirement and CombatUtil

also add it to Ai logic
This commit is contained in:
Hanmac
2016-08-25 12:15:54 +00:00
parent 198c6d4a04
commit 453c1e7f9f
10 changed files with 111 additions and 12 deletions

1
.gitattributes vendored
View File

@@ -398,6 +398,7 @@ forge-game/src/main/java/forge/game/ability/effects/FlipCoinEffect.java -text
forge-game/src/main/java/forge/game/ability/effects/FogEffect.java -text forge-game/src/main/java/forge/game/ability/effects/FogEffect.java -text
forge-game/src/main/java/forge/game/ability/effects/GameLossEffect.java -text forge-game/src/main/java/forge/game/ability/effects/GameLossEffect.java -text
forge-game/src/main/java/forge/game/ability/effects/GameWinEffect.java -text forge-game/src/main/java/forge/game/ability/effects/GameWinEffect.java -text
forge-game/src/main/java/forge/game/ability/effects/GoadEffect.java -text svneol=unset#text/plain
forge-game/src/main/java/forge/game/ability/effects/LifeExchangeEffect.java -text forge-game/src/main/java/forge/game/ability/effects/LifeExchangeEffect.java -text
forge-game/src/main/java/forge/game/ability/effects/LifeGainEffect.java -text forge-game/src/main/java/forge/game/ability/effects/LifeGainEffect.java -text
forge-game/src/main/java/forge/game/ability/effects/LifeLoseEffect.java -text forge-game/src/main/java/forge/game/ability/effects/LifeLoseEffect.java -text

View File

@@ -125,7 +125,7 @@ public class AiAttackController {
if (sa.getApi() == ApiType.Animate) { if (sa.getApi() == ApiType.Animate) {
if (ComputerUtilCost.canPayCost(sa, defender) if (ComputerUtilCost.canPayCost(sa, defender)
&& sa.getRestrictions().checkOtherRestrictions(c, sa, defender)) { && sa.getRestrictions().checkOtherRestrictions(c, sa, defender)) {
Card animatedCopy = CardFactory.copyCard(c, true); Card animatedCopy = CardFactory.copyCard(c, false);
AnimateAi.becomeAnimated(animatedCopy, c.hasSickness(), sa); AnimateAi.becomeAnimated(animatedCopy, c.hasSickness(), sa);
defenders.add(animatedCopy); defenders.add(animatedCopy);
} }
@@ -542,7 +542,9 @@ public class AiAttackController {
continue; continue;
} }
boolean mustAttack = false; boolean mustAttack = false;
if (attacker.getSVar("MustAttack").equals("True")) { if (attacker.isGoaded()) {
mustAttack = true;
} else if (attacker.getSVar("MustAttack").equals("True")) {
mustAttack = true; mustAttack = true;
} else if (attacker.getSVar("EndOfTurnLeavePlay").equals("True") } else if (attacker.getSVar("EndOfTurnLeavePlay").equals("True")
&& isEffectiveAttacker(ai, attacker, combat)) { && isEffectiveAttacker(ai, attacker, combat)) {

View File

@@ -1206,7 +1206,7 @@ public class AiController {
* 5. needs to be updated to ensure that the net toughness is * 5. needs to be updated to ensure that the net toughness is
* still positive after static effects. * still positive after static effects.
*/ */
final Card creature = CardFactory.copyCard(card, true); final Card creature = CardFactory.copyCard(card, false);
ComputerUtilCard.applyStaticContPT(game, creature, null); ComputerUtilCard.applyStaticContPT(game, creature, null);
if (creature.getNetToughness() <= 0 && !creature.hasStartOfKeyword("etbCounter") && mana.countX() == 0 if (creature.getNetToughness() <= 0 && !creature.hasStartOfKeyword("etbCounter") && mana.countX() == 0
&& !creature.hasETBTrigger(false) && !creature.hasETBReplacement() && !creature.hasETBTrigger(false) && !creature.hasETBReplacement()

View File

@@ -1240,7 +1240,7 @@ public class ComputerUtilCard {
public static Card getPumpedCreature(final Player ai, final SpellAbility sa, public static Card getPumpedCreature(final Player ai, final SpellAbility sa,
final Card c, final int toughness, final int power, final Card c, final int toughness, final int power,
final List<String> keywords) { final List<String> keywords) {
Card pumped = CardFactory.copyCard(c, true); Card pumped = CardFactory.copyCard(c, false);
pumped.setSickness(c.hasSickness()); pumped.setSickness(c.hasSickness());
final long timestamp = c.getGame().getNextTimestamp(); final long timestamp = c.getGame().getNextTimestamp();
final List<String> kws = new ArrayList<String>(); final List<String> kws = new ArrayList<String>();

View File

@@ -27,6 +27,7 @@ public enum SpellApiToAi {
.put(ApiType.AnimateAll, AnimateAllAi.class) .put(ApiType.AnimateAll, AnimateAllAi.class)
.put(ApiType.Attach, AttachAi.class) .put(ApiType.Attach, AttachAi.class)
.put(ApiType.Balance, BalanceAi.class) .put(ApiType.Balance, BalanceAi.class)
.put(ApiType.BecomeMonarch, AlwaysPlayAi.class)
.put(ApiType.BecomesBlocked, BecomesBlockedAi.class) .put(ApiType.BecomesBlocked, BecomesBlockedAi.class)
.put(ApiType.BidLife, BidLifeAi.class) .put(ApiType.BidLife, BidLifeAi.class)
.put(ApiType.Bond, BondAi.class) .put(ApiType.Bond, BondAi.class)
@@ -77,6 +78,7 @@ public enum SpellApiToAi {
.put(ApiType.GainLife, LifeGainAi.class) .put(ApiType.GainLife, LifeGainAi.class)
.put(ApiType.GainOwnership, CannotPlayAi.class) .put(ApiType.GainOwnership, CannotPlayAi.class)
.put(ApiType.GenericChoice, ChooseGenericEffectAi.class) .put(ApiType.GenericChoice, ChooseGenericEffectAi.class)
.put(ApiType.Goad, AlwaysPlayAi.class)
.put(ApiType.LoseLife, LifeLoseAi.class) .put(ApiType.LoseLife, LifeLoseAi.class)
.put(ApiType.LosesGame, GameLossAi.class) .put(ApiType.LosesGame, GameLossAi.class)
.put(ApiType.Mana, ManaEffectAi.class) .put(ApiType.Mana, ManaEffectAi.class)

View File

@@ -69,6 +69,7 @@ public enum ApiType {
Fight (FightEffect.class), Fight (FightEffect.class),
FlipACoin (FlipCoinEffect.class), FlipACoin (FlipCoinEffect.class),
Fog (FogEffect.class), Fog (FogEffect.class),
Goad (GoadEffect.class),
GainControl (ControlGainEffect.class), GainControl (ControlGainEffect.class),
GainLife (LifeGainEffect.class), GainLife (LifeGainEffect.class),
GainOwnership (OwnershipGainEffect.class), GainOwnership (OwnershipGainEffect.class),

View File

@@ -0,0 +1,47 @@
package forge.game.ability.effects;
import forge.GameCommand;
import forge.game.Game;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
public class GoadEffect extends SpellAbilityEffect {
@Override
public void resolve(SpellAbility sa) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Player player = sa.getActivatingPlayer();
final Game game = player.getGame();
final long timestamp = game.getNextTimestamp();
for (final Card tgtC : getTargetCards(sa)) {
// only pump things in PumpZone
if (!game.getCardsIn(ZoneType.Battlefield).contains(tgtC)) {
continue;
}
// if pump is a target, make sure we can still target now
if ((tgt != null) && !tgtC.canBeTargetedBy(sa)) {
continue;
}
tgtC.addGoad(timestamp, player);
final GameCommand untilEOT = new GameCommand() {
private static final long serialVersionUID = -1731759226844770852L;
@Override
public void run() {
tgtC.removeGoad(timestamp);
}
};
game.getCleanup().addUntil(player, untilEOT);
}
}
}

View File

@@ -116,9 +116,9 @@ public class Card extends GameEntity implements Comparable<Card> {
private int mayPlayTurn = 0; private int mayPlayTurn = 0;
// changes by AF animate and continuous static effects - timestamp is the key of maps // changes by AF animate and continuous static effects - timestamp is the key of maps
private final Map<Long, CardChangedType> changedCardTypes = new TreeMap<>(); private final Map<Long, CardChangedType> changedCardTypes = Maps.newTreeMap();
private final Map<Long, KeywordsChange> changedCardKeywords = new TreeMap<>(); private final Map<Long, KeywordsChange> changedCardKeywords = Maps.newTreeMap();
private final SortedMap<Long, CardColor> changedCardColors = new TreeMap<>(); private final SortedMap<Long, CardColor> changedCardColors = Maps.newTreeMap();
// changes that say "replace each instance of one [color,type] by another - timestamp is the key of maps // changes that say "replace each instance of one [color,type] by another - timestamp is the key of maps
private final CardChangedWords changedTextColors = new CardChangedWords(); private final CardChangedWords changedTextColors = new CardChangedWords();
@@ -132,10 +132,10 @@ public class Card extends GameEntity implements Comparable<Card> {
private final MapOfLists<GameEntity, Object> rememberMap = new HashMapOfLists<>(CollectionSuppliers.arrayLists()); private final MapOfLists<GameEntity, Object> rememberMap = new HashMapOfLists<>(CollectionSuppliers.arrayLists());
private Map<Player, String> flipResult; private Map<Player, String> flipResult;
private Map<Card, Integer> receivedDamageFromThisTurn = new TreeMap<>(); private Map<Card, Integer> receivedDamageFromThisTurn = Maps.newTreeMap();
private Map<Card, Integer> dealtDamageToThisTurn = new TreeMap<>(); private Map<Card, Integer> dealtDamageToThisTurn = Maps.newTreeMap();
private Map<String, Integer> dealtDamageToPlayerThisTurn = new TreeMap<>(); private Map<String, Integer> dealtDamageToPlayerThisTurn = Maps.newTreeMap();
private final Map<Card, Integer> assignedDamageMap = new TreeMap<>(); private final Map<Card, Integer> assignedDamageMap = Maps.newTreeMap();
private boolean isCommander = false; private boolean isCommander = false;
private boolean startsGameInPlay = false; private boolean startsGameInPlay = false;
@@ -209,7 +209,7 @@ public class Card extends GameEntity implements Comparable<Card> {
private Player owner = null; private Player owner = null;
private Player controller = null; private Player controller = null;
private long controllerTimestamp = 0; private long controllerTimestamp = 0;
private NavigableMap<Long, Player> tempControllers = new TreeMap<>(); private NavigableMap<Long, Player> tempControllers = Maps.newTreeMap();
private String originalText = "", text = ""; private String originalText = "", text = "";
private Cost miracleCost = null; private Cost miracleCost = null;
@@ -222,6 +222,8 @@ public class Card extends GameEntity implements Comparable<Card> {
private Card exiledWith = null; private Card exiledWith = null;
private Map<Long, Player> goad = Maps.newTreeMap();
private final List<GameCommand> leavePlayCommandList = new ArrayList<>(); private final List<GameCommand> leavePlayCommandList = new ArrayList<>();
private final List<GameCommand> etbCommandList = new ArrayList<>(); private final List<GameCommand> etbCommandList = new ArrayList<>();
private final List<GameCommand> untapCommandList = new ArrayList<>(); private final List<GameCommand> untapCommandList = new ArrayList<>();
@@ -6911,4 +6913,24 @@ public class Card extends GameEntity implements Comparable<Card> {
getZone().remove(this); getZone().remove(this);
getGame().getTriggerHandler().clearSuppression(TriggerType.ChangesZone); getGame().getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
} }
public final void addGoad(Long timestamp, final Player p) {
goad.put(timestamp, p);
}
public final void removeGoad(Long timestamp) {
goad.remove(timestamp);
}
public final boolean isGoaded() {
return !goad.isEmpty();
}
public final boolean isGoadedBy(final Player p) {
return goad.containsValue(p);
}
public final Collection<Player> getGoaded() {
return goad.values();
}
} }

View File

@@ -34,6 +34,11 @@ public class AttackRequirement {
} }
int nAttackAnything = 0; int nAttackAnything = 0;
if (attacker.isGoaded()) {
nAttackAnything += attacker.getGoaded().size();
}
for (final String keyword : attacker.getKeywords()) { for (final String keyword : attacker.getKeywords()) {
if (keyword.startsWith("CARDNAME attacks specific player each combat if able")) { if (keyword.startsWith("CARDNAME attacks specific player each combat if able")) {
final String defined = keyword.split(":")[1]; final String defined = keyword.split(":")[1];

View File

@@ -188,6 +188,25 @@ public class CombatUtil {
return false; return false;
} }
// Goad logic
// a goaded creature does need to attack a player which does not goaded her
// or if not possible a planeswalker or a player which does goaded her
if (attacker.isGoaded()) {
final boolean goadedByDefender = defender instanceof Player && attacker.isGoadedBy((Player) defender);
// attacker got goaded by defender or defender is not player
if (goadedByDefender || !(defender instanceof Player)) {
for (GameEntity ge : getAllPossibleDefenders(attacker.getController())) {
if (!defender.equals(ge) && ge instanceof Player) {
// found a player which does not goad that creature
// and creature can attack this player or planeswalker
if (!attacker.isGoadedBy((Player) ge) && canAttack(attacker, ge)) {
return false;
}
}
}
}
}
// Keywords // Keywords
final boolean canAttackWithDefender = attacker.hasKeyword("CARDNAME can attack as though it didn't have defender."); final boolean canAttackWithDefender = attacker.hasKeyword("CARDNAME can attack as though it didn't have defender.");
for (final String keyword : attacker.getKeywords()) { for (final String keyword : attacker.getKeywords()) {