mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
declareAttackers fixes
This commit is contained in:
committed by
Anthony Calosa
parent
bd4e3b56ac
commit
c0ede21c31
@@ -47,6 +47,7 @@ import forge.game.cost.Cost;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityPredicates;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.Zone;
|
||||
@@ -135,8 +136,7 @@ public class AiAttackController {
|
||||
if (c.isToken() && c.getCopiedPermanent() == null) {
|
||||
continue;
|
||||
}
|
||||
for (SpellAbility sa : c.getSpellAbilities()) {
|
||||
if (sa.getApi() == ApiType.Animate) {
|
||||
for (SpellAbility sa : Iterables.filter(c.getSpellAbilities(), SpellAbilityPredicates.isApi(ApiType.Animate))) {
|
||||
if (ComputerUtilCost.canPayCost(sa, defender, false)
|
||||
&& sa.getRestrictions().checkOtherRestrictions(c, sa, defender)) {
|
||||
Card animatedCopy = AnimateAi.becomeAnimated(c, sa);
|
||||
@@ -144,7 +144,6 @@ public class AiAttackController {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return defenders;
|
||||
}
|
||||
|
||||
|
||||
@@ -1150,8 +1150,8 @@ public class AiBlockController {
|
||||
//Check for validity of blocks in case something slipped through
|
||||
for (Card attacker : attackers) {
|
||||
if (!CombatUtil.canAttackerBeBlockedWithAmount(attacker, combat.getBlockers(attacker).size(), combat)) {
|
||||
for (final Card blocker : combat.getBlockers(attacker)) {
|
||||
if (blocker.getController() == ai) // don't touch other player's blockers
|
||||
for (final Card blocker : CardLists.filterControlledBy(combat.getBlockers(attacker), ai)) {
|
||||
// don't touch other player's blockers
|
||||
combat.removeFromCombat(blocker);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2037,7 +2037,7 @@ public class ComputerUtilCombat {
|
||||
|
||||
} // attacker no double strike
|
||||
return false;// should never arrive here
|
||||
} // canDestroyBlocker
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
|
||||
@@ -108,7 +108,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("CanTargetPlayer")) {
|
||||
// Radiate
|
||||
// Remove targeted players because getAllCandidates include all the valid players
|
||||
for(Player p : targetedSA.getTargets().getTargetPlayers())
|
||||
for (Player p : targetedSA.getTargets().getTargetPlayers())
|
||||
candidates.remove(p);
|
||||
|
||||
for (GameEntity o : candidates) {
|
||||
|
||||
@@ -165,7 +165,7 @@ public class AttackRequirement {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isAttackingDefender && CombatUtil.getAttackCost(attacker.getGame(), attacker, defender) == null) {
|
||||
if (!isAttackingDefender && CombatUtil.getAttackCost(attacker.getGame(), attacker, def) == null) {
|
||||
violations++; // no one is attacking that defender or any of his PWs
|
||||
}
|
||||
}
|
||||
@@ -173,14 +173,7 @@ public class AttackRequirement {
|
||||
}
|
||||
|
||||
// now, count everything else
|
||||
final MapToAmount<GameEntity> defenderSpecificCostFree = new LinkedHashMapToAmount<>();
|
||||
for (GameEntity e : defenderSpecific.keySet()) {
|
||||
if (CombatUtil.getAttackCost(attacker.getGame(), attacker, defender) == null) {
|
||||
defenderSpecificCostFree.put(e, defenderSpecific.get(e));
|
||||
}
|
||||
}
|
||||
|
||||
violations += defenderSpecificCostFree.countAll() - (isAttacking ? defenderSpecificCostFree.count(defender) : 0);
|
||||
violations += defenderSpecific.countAll() - (isAttacking ? defenderSpecific.count(defender) : 0);
|
||||
if (isAttacking) {
|
||||
final Combat combat = defender.getGame().getCombat();
|
||||
final Map<Card, AttackRestriction> constraints = combat.getAttackConstraints().getRestrictions();
|
||||
|
||||
@@ -567,24 +567,34 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
continue;
|
||||
}
|
||||
|
||||
final CardCollection untapFromCancel = new CardCollection();
|
||||
// do a full loop first so attackers can't be used to pay for Propaganda
|
||||
for (final Card attacker : combat.getAttackers()) {
|
||||
final boolean shouldTapForAttack = !attacker.hasKeyword(Keyword.VIGILANCE) && !attacker.hasKeyword("Attacking doesn't cause CARDNAME to tap.");
|
||||
if (shouldTapForAttack) {
|
||||
// set tapped to true without firing triggers because it may affect propaganda costs
|
||||
attacker.setTapped(true);
|
||||
untapFromCancel.add(attacker);
|
||||
}
|
||||
}
|
||||
|
||||
for (final Card attacker : combat.getAttackers()) {
|
||||
// TODO currently doesn't refund (can really only happen if you cancel paying for a creature with an attacking requirement that could be satisfied without a tax)
|
||||
final boolean canAttack = CombatUtil.checkPropagandaEffects(game, attacker, combat);
|
||||
attacker.setTapped(false);
|
||||
|
||||
if (canAttack) {
|
||||
if (shouldTapForAttack) {
|
||||
attacker.tap(true, true);
|
||||
}
|
||||
} else {
|
||||
if (!canAttack) {
|
||||
combat.removeFromCombat(attacker);
|
||||
if (untapFromCancel.contains(attacker)) {
|
||||
attacker.setTapped(false);
|
||||
}
|
||||
success = CombatUtil.validateAttackers(combat);
|
||||
if (!success) {
|
||||
for (Card c : untapFromCancel) {
|
||||
c.setTapped(false);
|
||||
}
|
||||
// might have been sacrificed while paying
|
||||
combat.removeAbsentCombatants();
|
||||
combat.initConstraints();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -592,6 +602,14 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
|
||||
} while (!success);
|
||||
|
||||
for (final Card attacker : combat.getAttackers()) {
|
||||
final boolean shouldTapForAttack = !attacker.hasKeyword(Keyword.VIGILANCE) && !attacker.hasKeyword("Attacking doesn't cause CARDNAME to tap.");
|
||||
if (shouldTapForAttack) {
|
||||
attacker.setTapped(false);
|
||||
attacker.tap(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Exert creatures here
|
||||
List<Card> possibleExerters = CardLists.getKeyword(combat.getAttackers(),
|
||||
"You may exert CARDNAME as it attacks.");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name:Chef's Kiss
|
||||
ManaCost:1 R R
|
||||
Types:Instant
|
||||
A:SP$ ControlSpell | ValidTgts$ Card | TgtPrompt$ Select target spell that targets only a single permanent or player | TargetType$ Spell | Mode$ Gain | TargetsSingleTarget$ True | TargetValidTargeting$ Permanent,Player | SubAbility$ DBCopy | StackDescription$ SpellDescription | SpellDescription$ Gain control of target spell that targets only a single permanent or player. Copy it, then reselect the targets at random for the spell and the copy. The new targets can't be you or a permanent you control.
|
||||
A:SP$ ControlSpell | ValidTgts$ Card | TgtPrompt$ Select target spell that targets only a single permanent or player | TargetType$ Spell | Mode$ Gain | TargetsSingleTarget$ True | TargetValidTargeting$ Permanent.inZoneBattlefield,Player | SubAbility$ DBCopy | StackDescription$ SpellDescription | SpellDescription$ Gain control of target spell that targets only a single permanent or player. Copy it, then reselect the targets at random for the spell and the copy. The new targets can't be you or a permanent you control.
|
||||
SVar:DBCopy:DB$ CopySpellAbility | Defined$ Targeted | RandomTarget$ True | RandomTargetRestriction$ Player.Other,Permanent.YouDontCtrl | SubAbility$ DBChangeTargets | StackDescription$ None
|
||||
SVar:DBChangeTargets:DB$ ChangeTargets | Defined$ Targeted | RandomTarget$ True | RandomTargetRestriction$ Player.Other,Permanent.YouDontCtrl
|
||||
Oracle:Gain control of target spell that targets only a single permanent or player. Copy it, then reselect the targets at random for the spell and the copy. The new targets can't be you or a permanent you control.
|
||||
|
||||
@@ -4,5 +4,5 @@ Types:Creature Human Wizard
|
||||
PT:2/2
|
||||
K:Flash
|
||||
T:Mode$ ChangesZone | ValidCard$ Card.wasCastFromYourHandByYou+Self | Destination$ Battlefield | Execute$ TrigRadiate | TriggerDescription$ When CARDNAME enters the battlefield, if you cast it from your hand, choose target spell or ability that targets only a single permanent or player. Copy that spell or ability for each other permanent or player the spell or ability could target. Each copy targets a different one of those permanents and players.
|
||||
SVar:TrigRadiate:DB$ CopySpellAbility | ValidTgts$ Card | TgtPrompt$ Select target spell or ability that targets a single permanent or player | TargetType$ Spell,Activated,Triggered | TargetsSingleTarget$ True | TargetValidTargeting$ Permanent,Player | Controller$ You | CopyForEachCanTarget$ True | CanTargetPlayer$ True | SpellDescription$ Choose target spell or ability spell that targets only a single permanent or player. Copy that spell for each other permanent or player the spell could target. Each copy targets a different one of those permanents and players.
|
||||
SVar:TrigRadiate:DB$ CopySpellAbility | ValidTgts$ Card | TgtPrompt$ Select target spell or ability that targets a single permanent or player | TargetType$ Spell,Activated,Triggered | TargetsSingleTarget$ True | TargetValidTargeting$ Permanent.inZoneBattlefield,Player | Controller$ You | CopyForEachCanTarget$ True | CanTargetPlayer$ True | SpellDescription$ Choose target spell or ability that targets only a single permanent or player. Copy that spell for each other permanent or player the spell could target. Each copy targets a different one of those permanents and players.
|
||||
Oracle:Flash\nWhen Radiant Performer enters the battlefield, if you cast it from your hand, choose target spell or ability that targets only a single permanent or player. Copy that spell or ability for each other permanent or player the spell or ability could target. Each copy targets a different one of those permanents and players.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Name:Radiate
|
||||
ManaCost:3 R R
|
||||
Types:Instant
|
||||
A:SP$ CopySpellAbility | Cost$ 3 R R | ValidTgts$ Instant,Sorcery | TargetType$ Spell | TargetsSingleTarget$ True | TargetValidTargeting$ Permanent,Player | Controller$ You | CopyForEachCanTarget$ True | CanTargetPlayer$ True | SpellDescription$ Choose target instant or sorcery spell that targets only a single permanent or player. Copy that spell for each other permanent or player the spell could target. Each copy targets a different one of those permanents and players.
|
||||
A:SP$ CopySpellAbility | Cost$ 3 R R | ValidTgts$ Instant,Sorcery | TargetType$ Spell | TargetsSingleTarget$ True | TargetValidTargeting$ Permanent.inZoneBattlefield,Player | Controller$ You | CopyForEachCanTarget$ True | CanTargetPlayer$ True | SpellDescription$ Choose target instant or sorcery spell that targets only a single permanent or player. Copy that spell for each other permanent or player the spell could target. Each copy targets a different one of those permanents and players.
|
||||
AI:RemoveDeck:Random
|
||||
Oracle:Choose target instant or sorcery spell that targets only a single permanent or player. Copy that spell for each other permanent or player the spell could target. Each copy targets a different one of those permanents and players.
|
||||
|
||||
Reference in New Issue
Block a user