Merge branch 'goad' into 'master'

Goad doesn't stack

See merge request core-developers/forge!6074
This commit is contained in:
Michael Kamensky
2022-01-23 20:20:36 +00:00
10 changed files with 29 additions and 21 deletions

View File

@@ -624,7 +624,7 @@ public class AiAttackController {
// Attempt to see if there's a defined entity that must be attacked strictly this turn... // Attempt to see if there's a defined entity that must be attacked strictly this turn...
GameEntity entity = ai.getMustAttackEntityThisTurn(); GameEntity entity = ai.getMustAttackEntityThisTurn();
if (entity == null) { if (nextTurn || entity == null) {
// ...or during the attacking creature controller's turn // ...or during the attacking creature controller's turn
entity = ai.getMustAttackEntity(); entity = ai.getMustAttackEntity();
} }
@@ -720,6 +720,7 @@ public class AiAttackController {
continue; continue;
} }
boolean mustAttack = false; boolean mustAttack = false;
// TODO for nextTurn check if it was temporary
if (attacker.isGoaded()) { if (attacker.isGoaded()) {
mustAttack = true; mustAttack = true;
} else if (attacker.getSVar("MustAttack").equals("True")) { } else if (attacker.getSVar("MustAttack").equals("True")) {
@@ -736,7 +737,7 @@ public class AiAttackController {
mustAttack = true; 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); combat.addAttacker(attacker, defender);
attackersLeft.remove(attacker); attackersLeft.remove(attacker);
numForcedAttackers++; numForcedAttackers++;

View File

@@ -3040,11 +3040,11 @@ public class ComputerUtil {
public static boolean aiLifeInDanger(Player ai, boolean serious, int payment) { public static boolean aiLifeInDanger(Player ai, boolean serious, int payment) {
// TODO should also consider them as teams // TODO should also consider them as teams
for (Player opponent: ai.getOpponents()) { for (Player opponent: ai.getOpponents()) {
// test whether the human can kill the ai next turn
Combat combat = new Combat(opponent); Combat combat = new Combat(opponent);
boolean containsAttacker = false; boolean containsAttacker = false;
boolean thisCombat = ai.getGame().getPhaseHandler().isPlayerTurn(opponent) && ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_BEGIN);
for (Card att : opponent.getCreaturesInPlay()) { 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); combat.addAttacker(att, ai);
containsAttacker = true; containsAttacker = true;
} }
@@ -3052,6 +3052,8 @@ public class ComputerUtil {
if (!containsAttacker) { if (!containsAttacker) {
continue; continue;
} }
// TODO if it's next turn ignore mustBlockCards
AiBlockController block = new AiBlockController(ai, false); AiBlockController block = new AiBlockController(ai, false);
block.assignBlockersForCombat(combat); block.assignBlockersForCombat(combat);

View File

@@ -39,6 +39,10 @@ public class GoadAi extends SpellAbilityAi {
if (ComputerUtilCard.isUselessCreature(ai, c)) { if (ComputerUtilCard.isUselessCreature(ai, c)) {
return false; return false;
} }
// useless
if (c.isGoadedBy(ai)) {
return false;
}
// select creatures which can attack an Opponent other than ai // select creatures which can attack an Opponent other than ai
for (Player o : ai.getOpponents()) { for (Player o : ai.getOpponents()) {
if (ComputerUtilCombat.canAttackNextTurn(c, o)) { if (ComputerUtilCombat.canAttackNextTurn(c, o)) {
@@ -64,6 +68,10 @@ public class GoadAi extends SpellAbilityAi {
if (ComputerUtilCard.isUselessCreature(ai, c)) { if (ComputerUtilCard.isUselessCreature(ai, c)) {
return false; return false;
} }
// useless
if (c.isGoadedBy(ai)) {
return false;
}
// select only creatures AI can block // select only creatures AI can block
return ComputerUtilCard.canBeBlockedProfitably(ai, c, false); return ComputerUtilCard.canBeBlockedProfitably(ai, c, false);
} }

View File

@@ -22,6 +22,11 @@ public class GoadEffect extends SpellAbilityEffect {
continue; continue;
} }
// 701.38d
if (tgtC.isGoadedBy(player)) {
continue;
}
// if pump is a target, make sure we can still target now // if pump is a target, make sure we can still target now
if (sa.usesTargeting() && !sa.getTargetRestrictions().canTgtPlayer() && !tgtC.canBeTargetedBy(sa)) { if (sa.usesTargeting() && !sa.getTargetRestrictions().canTgtPlayer() && !tgtC.canBeTargetedBy(sa)) {
continue; continue;

View File

@@ -70,7 +70,7 @@ public class MustAttackEffect extends SpellAbilityEffect {
// TODO these should not override but add another requirement // TODO these should not override but add another requirement
for (final Player p : tgtPlayers) { for (final Player p : tgtPlayers) {
if ((tgt == null) || p.canBeTargetedBy(sa)) { if ((tgt == null) || p.canBeTargetedBy(sa)) {
if (thisTurn) { if (thisTurn || !p.getGame().getPhaseHandler().isPlayerTurn(p)) {
p.setMustAttackEntityThisTurn(entity); p.setMustAttackEntityThisTurn(entity);
} else { } else {
p.setMustAttackEntity(entity); p.setMustAttackEntity(entity);

View File

@@ -35,10 +35,6 @@ public class AttackRequirement {
this.causesToAttack = causesToAttack; this.causesToAttack = causesToAttack;
final GameEntity mustAttack = attacker.getController().getMustAttackEntity();
if (mustAttack != null) {
defenderSpecific.add(mustAttack);
}
final GameEntity mustAttackThisTurn = attacker.getController().getMustAttackEntityThisTurn(); final GameEntity mustAttackThisTurn = attacker.getController().getMustAttackEntityThisTurn();
if (mustAttackThisTurn != null) { if (mustAttackThisTurn != null) {
defenderSpecific.add(mustAttackThisTurn); defenderSpecific.add(mustAttackThisTurn);
@@ -47,6 +43,7 @@ public class AttackRequirement {
int nAttackAnything = 0; int nAttackAnything = 0;
if (attacker.isGoaded()) { if (attacker.isGoaded()) {
// Goad has two requirements but the other is handled by CombatUtil currently
nAttackAnything += attacker.getGoaded().size(); nAttackAnything += attacker.getGoaded().size();
} }
@@ -71,10 +68,6 @@ public class AttackRequirement {
} }
} }
final GameEntity mustAttack3 = attacker.getMustAttackEntity();
if (mustAttack3 != null) {
defenderSpecific.add(mustAttack3);
}
final GameEntity mustAttackThisTurn3 = attacker.getMustAttackEntityThisTurn(); final GameEntity mustAttackThisTurn3 = attacker.getMustAttackEntityThisTurn();
if (mustAttackThisTurn3 != null) { if (mustAttackThisTurn3 != null) {
defenderSpecific.add(mustAttackThisTurn3); defenderSpecific.add(mustAttackThisTurn3);

View File

@@ -825,6 +825,7 @@ public class PhaseHandler implements java.io.Serializable {
private Player handleNextTurn() { private Player handleNextTurn() {
game.getStack().onNextTurn(); game.getStack().onNextTurn();
// reset mustAttackEntity // reset mustAttackEntity
playerTurn.setMustAttackEntityThisTurn(playerTurn.getMustAttackEntity());
playerTurn.setMustAttackEntity(null); playerTurn.setMustAttackEntity(null);
game.getTriggerHandler().clearThisTurnDelayedTrigger(); game.getTriggerHandler().clearThisTurnDelayedTrigger();

View File

@@ -906,7 +906,7 @@ public final class StaticAbilityContinuous {
} }
// add Types // add Types
if ((addTypes != null) || (removeTypes != null) || addAllCreatureTypes if (addTypes != null || removeTypes != null || addAllCreatureTypes
|| removeSuperTypes || removeCardTypes || removeLandTypes || removeCreatureTypes || removeArtifactTypes || removeEnchantmentTypes) { || removeSuperTypes || removeCardTypes || removeLandTypes || removeCreatureTypes || removeArtifactTypes || removeEnchantmentTypes) {
affectedCard.addChangedCardTypes(addTypes, removeTypes, addAllCreatureTypes, removeSuperTypes, removeCardTypes, removeSubTypes, affectedCard.addChangedCardTypes(addTypes, removeTypes, addAllCreatureTypes, removeSuperTypes, removeCardTypes, removeSubTypes,
removeLandTypes, removeCreatureTypes, removeArtifactTypes, removeEnchantmentTypes, removeLandTypes, removeCreatureTypes, removeArtifactTypes, removeEnchantmentTypes,
@@ -919,7 +919,7 @@ public final class StaticAbilityContinuous {
} }
if (layer == StaticAbilityLayer.RULES) { if (layer == StaticAbilityLayer.RULES) {
if (params.containsKey("Goad")) { if (params.containsKey("Goad") && !affectedCard.isGoadedBy(hostCard.getController())) {
affectedCard.addGoad(se.getTimestamp(), hostCard.getController()); affectedCard.addGoad(se.getTimestamp(), hostCard.getController());
} }
if (params.containsKey("CanBlockAny")) { if (params.containsKey("CanBlockAny")) {

View File

@@ -2,7 +2,6 @@ Name:Avalanche Tusker
ManaCost:2 G U R ManaCost:2 G U R
Types:Creature Elephant Warrior Types:Creature Elephant Warrior
PT:6/4 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. 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 | SpellDescription$ Target creature blocks CARDNAME this turn if able. SVar:TrigCantBlock:DB$ MustBlock | ValidTgts$ Creature.DefenderCtrl | TgtPrompt$ Select target creature
SVar:Picture:http://www.wizards.com/global/images/magic/general/avalanche_tusker.jpg
Oracle:Whenever Avalanche Tusker attacks, target creature defending player controls blocks it this combat if able. Oracle:Whenever Avalanche Tusker attacks, target creature defending player controls blocks it this combat if able.

View File

@@ -5,10 +5,9 @@ PT:6/1
K:Haste K:Haste
K:Trample 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. 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. 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:TrigSac:DB$ Sacrifice | SacValid$ Self
SVar:EndOfTurnLeavePlay:True SVar:EndOfTurnLeavePlay:True
SVar:PlayMain1: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. 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.