mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 04:08:01 +00:00
add Goad Effect, add it to AttackRequirement and CombatUtil
also add it to Ai logic
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -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
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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>();
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
@@ -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()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user