mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 19:58:00 +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/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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
Reference in New Issue
Block a user