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...
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++;

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();

View File

@@ -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")) {

View File

@@ -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.

View File

@@ -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.