diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 94ef70f394d..fa600d9bbce 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -624,7 +624,7 @@ public class AiAttackController { // Attempt to see if there's a defined entity that must be attacked strictly this turn... GameEntity entity = ai.getMustAttackEntityThisTurn(); - if (entity == null) { + if (nextTurn || entity == null) { // ...or during the attacking creature controller's turn entity = ai.getMustAttackEntity(); } @@ -720,6 +720,7 @@ public class AiAttackController { continue; } boolean mustAttack = false; + // TODO for nextTurn check if it was temporary if (attacker.isGoaded()) { mustAttack = true; } else if (attacker.getSVar("MustAttack").equals("True")) { @@ -736,7 +737,7 @@ public class AiAttackController { mustAttack = true; } } - if (mustAttack || attacker.getController().getMustAttackEntity() != null || attacker.getController().getMustAttackEntityThisTurn() != null) { + if (mustAttack || (attacker.getController().getMustAttackEntity() != null && nextTurn) || (attacker.getController().getMustAttackEntityThisTurn() != null && !nextTurn)) { combat.addAttacker(attacker, defender); attackersLeft.remove(attacker); numForcedAttackers++; diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 2ba324f8ddc..bc36997fe18 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -3040,11 +3040,11 @@ public class ComputerUtil { public static boolean aiLifeInDanger(Player ai, boolean serious, int payment) { // TODO should also consider them as teams for (Player opponent: ai.getOpponents()) { - // test whether the human can kill the ai next turn Combat combat = new Combat(opponent); boolean containsAttacker = false; + boolean thisCombat = ai.getGame().getPhaseHandler().isPlayerTurn(opponent) && ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_BEGIN); for (Card att : opponent.getCreaturesInPlay()) { - if (ComputerUtilCombat.canAttackNextTurn(att, ai)) { + if ((thisCombat && CombatUtil.canAttack(att, ai)) || (!thisCombat && ComputerUtilCombat.canAttackNextTurn(att, ai))) { combat.addAttacker(att, ai); containsAttacker = true; } @@ -3052,6 +3052,8 @@ public class ComputerUtil { if (!containsAttacker) { continue; } + + // TODO if it's next turn ignore mustBlockCards AiBlockController block = new AiBlockController(ai, false); block.assignBlockersForCombat(combat); diff --git a/forge-ai/src/main/java/forge/ai/ability/GoadAi.java b/forge-ai/src/main/java/forge/ai/ability/GoadAi.java index f4c63e06b8f..4d95e1a15eb 100644 --- a/forge-ai/src/main/java/forge/ai/ability/GoadAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/GoadAi.java @@ -39,6 +39,10 @@ public class GoadAi extends SpellAbilityAi { if (ComputerUtilCard.isUselessCreature(ai, c)) { return false; } + // useless + if (c.isGoadedBy(ai)) { + return false; + } // select creatures which can attack an Opponent other than ai for (Player o : ai.getOpponents()) { if (ComputerUtilCombat.canAttackNextTurn(c, o)) { @@ -64,6 +68,10 @@ public class GoadAi extends SpellAbilityAi { if (ComputerUtilCard.isUselessCreature(ai, c)) { return false; } + // useless + if (c.isGoadedBy(ai)) { + return false; + } // select only creatures AI can block return ComputerUtilCard.canBeBlockedProfitably(ai, c, false); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/GoadEffect.java b/forge-game/src/main/java/forge/game/ability/effects/GoadEffect.java index 3080170b353..3bd6edd41d0 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/GoadEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/GoadEffect.java @@ -22,6 +22,11 @@ public class GoadEffect extends SpellAbilityEffect { continue; } + // 701.38d + if (tgtC.isGoadedBy(player)) { + continue; + } + // if pump is a target, make sure we can still target now if (sa.usesTargeting() && !sa.getTargetRestrictions().canTgtPlayer() && !tgtC.canBeTargetedBy(sa)) { continue; diff --git a/forge-game/src/main/java/forge/game/ability/effects/MustAttackEffect.java b/forge-game/src/main/java/forge/game/ability/effects/MustAttackEffect.java index 56c7224834c..586e01158d0 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/MustAttackEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/MustAttackEffect.java @@ -70,7 +70,7 @@ public class MustAttackEffect extends SpellAbilityEffect { // TODO these should not override but add another requirement for (final Player p : tgtPlayers) { if ((tgt == null) || p.canBeTargetedBy(sa)) { - if (thisTurn) { + if (thisTurn || !p.getGame().getPhaseHandler().isPlayerTurn(p)) { p.setMustAttackEntityThisTurn(entity); } else { p.setMustAttackEntity(entity); diff --git a/forge-game/src/main/java/forge/game/combat/AttackRequirement.java b/forge-game/src/main/java/forge/game/combat/AttackRequirement.java index 6da720bdc43..9ad0ca64658 100644 --- a/forge-game/src/main/java/forge/game/combat/AttackRequirement.java +++ b/forge-game/src/main/java/forge/game/combat/AttackRequirement.java @@ -35,10 +35,6 @@ public class AttackRequirement { this.causesToAttack = causesToAttack; - final GameEntity mustAttack = attacker.getController().getMustAttackEntity(); - if (mustAttack != null) { - defenderSpecific.add(mustAttack); - } final GameEntity mustAttackThisTurn = attacker.getController().getMustAttackEntityThisTurn(); if (mustAttackThisTurn != null) { defenderSpecific.add(mustAttackThisTurn); @@ -47,6 +43,7 @@ public class AttackRequirement { int nAttackAnything = 0; if (attacker.isGoaded()) { + // Goad has two requirements but the other is handled by CombatUtil currently nAttackAnything += attacker.getGoaded().size(); } @@ -70,11 +67,7 @@ public class AttackRequirement { nAttackAnything++; } } - - final GameEntity mustAttack3 = attacker.getMustAttackEntity(); - if (mustAttack3 != null) { - defenderSpecific.add(mustAttack3); - } + final GameEntity mustAttackThisTurn3 = attacker.getMustAttackEntityThisTurn(); if (mustAttackThisTurn3 != null) { defenderSpecific.add(mustAttackThisTurn3); diff --git a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java index fa4e6b36b6e..e5e357034bb 100644 --- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java +++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java @@ -825,6 +825,7 @@ public class PhaseHandler implements java.io.Serializable { private Player handleNextTurn() { game.getStack().onNextTurn(); // reset mustAttackEntity + playerTurn.setMustAttackEntityThisTurn(playerTurn.getMustAttackEntity()); playerTurn.setMustAttackEntity(null); game.getTriggerHandler().clearThisTurnDelayedTrigger(); diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java index d11e720f017..b1a39885c8d 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java @@ -906,7 +906,7 @@ public final class StaticAbilityContinuous { } // add Types - if ((addTypes != null) || (removeTypes != null) || addAllCreatureTypes + if (addTypes != null || removeTypes != null || addAllCreatureTypes || removeSuperTypes || removeCardTypes || removeLandTypes || removeCreatureTypes || removeArtifactTypes || removeEnchantmentTypes) { affectedCard.addChangedCardTypes(addTypes, removeTypes, addAllCreatureTypes, removeSuperTypes, removeCardTypes, removeSubTypes, removeLandTypes, removeCreatureTypes, removeArtifactTypes, removeEnchantmentTypes, @@ -919,7 +919,7 @@ public final class StaticAbilityContinuous { } if (layer == StaticAbilityLayer.RULES) { - if (params.containsKey("Goad")) { + if (params.containsKey("Goad") && !affectedCard.isGoadedBy(hostCard.getController())) { affectedCard.addGoad(se.getTimestamp(), hostCard.getController()); } if (params.containsKey("CanBlockAny")) { diff --git a/forge-gui/res/cardsfolder/a/avalanche_tusker.txt b/forge-gui/res/cardsfolder/a/avalanche_tusker.txt index df65f8bb3af..8373d40fa42 100644 --- a/forge-gui/res/cardsfolder/a/avalanche_tusker.txt +++ b/forge-gui/res/cardsfolder/a/avalanche_tusker.txt @@ -2,7 +2,6 @@ Name:Avalanche Tusker ManaCost:2 G U R Types:Creature Elephant Warrior PT:6/4 -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigCantBlock | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks, target creature defending player controls blocks this combat if able. -SVar:TrigCantBlock:DB$ MustBlock | ValidTgts$ Creature.DefenderCtrl | TgtPrompt$ Select target creature | SpellDescription$ Target creature blocks CARDNAME this turn if able. -SVar:Picture:http://www.wizards.com/global/images/magic/general/avalanche_tusker.jpg +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigCantBlock | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks, target creature defending player controls blocks it this combat if able. +SVar:TrigCantBlock:DB$ MustBlock | ValidTgts$ Creature.DefenderCtrl | TgtPrompt$ Select target creature Oracle:Whenever Avalanche Tusker attacks, target creature defending player controls blocks it this combat if able. diff --git a/forge-gui/res/cardsfolder/i/impetuous_devils.txt b/forge-gui/res/cardsfolder/i/impetuous_devils.txt index e55525ccaff..e0f8673e432 100644 --- a/forge-gui/res/cardsfolder/i/impetuous_devils.txt +++ b/forge-gui/res/cardsfolder/i/impetuous_devils.txt @@ -5,10 +5,9 @@ PT:6/1 K:Haste K:Trample T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigProvoke | TriggerDescription$ When CARDNAME attacks, up to one target creature defending player controls blocks it this combat if able. -SVar:TrigProvoke:DB$ MustBlock | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Creature.DefenderCtrl | Duration$ UntilEndOfCombat | TgtPrompt$ Select up to one target creature defending player controls +SVar:TrigProvoke:DB$ MustBlock | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Creature.DefenderCtrl | TgtPrompt$ Select up to one target creature defending player controls T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | Execute$ TrigSac | TriggerDescription$ At the beginning of the end step, sacrifice CARDNAME. SVar:TrigSac:DB$ Sacrifice | SacValid$ Self SVar:EndOfTurnLeavePlay:True SVar:PlayMain1:TRUE -SVar:Picture:http://www.wizards.com/global/images/magic/general/impetuous_devils.jpg Oracle:Trample, haste\nWhen Impetuous Devils attacks, up to one target creature defending player controls blocks it this combat if able.\nAt the beginning of the end step, sacrifice Impetuous Devils.