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/GameLossEffect.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/LifeGainEffect.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 (ComputerUtilCost.canPayCost(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);
defenders.add(animatedCopy);
}
@@ -542,7 +542,9 @@ public class AiAttackController {
continue;
}
boolean mustAttack = false;
if (attacker.getSVar("MustAttack").equals("True")) {
if (attacker.isGoaded()) {
mustAttack = true;
} else if (attacker.getSVar("MustAttack").equals("True")) {
mustAttack = true;
} else if (attacker.getSVar("EndOfTurnLeavePlay").equals("True")
&& 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
* still positive after static effects.
*/
final Card creature = CardFactory.copyCard(card, true);
final Card creature = CardFactory.copyCard(card, false);
ComputerUtilCard.applyStaticContPT(game, creature, null);
if (creature.getNetToughness() <= 0 && !creature.hasStartOfKeyword("etbCounter") && mana.countX() == 0
&& !creature.hasETBTrigger(false) && !creature.hasETBReplacement()

View File

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

View File

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

View File

@@ -69,6 +69,7 @@ public enum ApiType {
Fight (FightEffect.class),
FlipACoin (FlipCoinEffect.class),
Fog (FogEffect.class),
Goad (GoadEffect.class),
GainControl (ControlGainEffect.class),
GainLife (LifeGainEffect.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;
// 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, KeywordsChange> changedCardKeywords = new TreeMap<>();
private final SortedMap<Long, CardColor> changedCardColors = new TreeMap<>();
private final Map<Long, CardChangedType> changedCardTypes = Maps.newTreeMap();
private final Map<Long, KeywordsChange> changedCardKeywords = Maps.newTreeMap();
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
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 Map<Player, String> flipResult;
private Map<Card, Integer> receivedDamageFromThisTurn = new TreeMap<>();
private Map<Card, Integer> dealtDamageToThisTurn = new TreeMap<>();
private Map<String, Integer> dealtDamageToPlayerThisTurn = new TreeMap<>();
private final Map<Card, Integer> assignedDamageMap = new TreeMap<>();
private Map<Card, Integer> receivedDamageFromThisTurn = Maps.newTreeMap();
private Map<Card, Integer> dealtDamageToThisTurn = Maps.newTreeMap();
private Map<String, Integer> dealtDamageToPlayerThisTurn = Maps.newTreeMap();
private final Map<Card, Integer> assignedDamageMap = Maps.newTreeMap();
private boolean isCommander = false;
private boolean startsGameInPlay = false;
@@ -209,7 +209,7 @@ public class Card extends GameEntity implements Comparable<Card> {
private Player owner = null;
private Player controller = null;
private long controllerTimestamp = 0;
private NavigableMap<Long, Player> tempControllers = new TreeMap<>();
private NavigableMap<Long, Player> tempControllers = Maps.newTreeMap();
private String originalText = "", text = "";
private Cost miracleCost = null;
@@ -222,6 +222,8 @@ public class Card extends GameEntity implements Comparable<Card> {
private Card exiledWith = null;
private Map<Long, Player> goad = Maps.newTreeMap();
private final List<GameCommand> leavePlayCommandList = new ArrayList<>();
private final List<GameCommand> etbCommandList = new ArrayList<>();
private final List<GameCommand> untapCommandList = new ArrayList<>();
@@ -6911,4 +6913,24 @@ public class Card extends GameEntity implements Comparable<Card> {
getZone().remove(this);
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;
if (attacker.isGoaded()) {
nAttackAnything += attacker.getGoaded().size();
}
for (final String keyword : attacker.getKeywords()) {
if (keyword.startsWith("CARDNAME attacks specific player each combat if able")) {
final String defined = keyword.split(":")[1];

View File

@@ -188,6 +188,25 @@ public class CombatUtil {
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
final boolean canAttackWithDefender = attacker.hasKeyword("CARDNAME can attack as though it didn't have defender.");
for (final String keyword : attacker.getKeywords()) {