diff --git a/.gitattributes b/.gitattributes index 191a180bb99..664e8192ce2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -354,6 +354,7 @@ forge-game/src/main/java/forge/game/ability/effects/BecomesBlockedEffect.java -t forge-game/src/main/java/forge/game/ability/effects/BidLifeEffect.java -text forge-game/src/main/java/forge/game/ability/effects/BondEffect.java -text forge-game/src/main/java/forge/game/ability/effects/BranchEffect.java -text +forge-game/src/main/java/forge/game/ability/effects/ChangeCombatantsEffect.java -text forge-game/src/main/java/forge/game/ability/effects/ChangeTargetsEffect.java -text forge-game/src/main/java/forge/game/ability/effects/ChangeTextEffect.java -text forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java -text @@ -12326,6 +12327,7 @@ forge-gui/res/cardsfolder/p/pore_over_the_pages.txt -text forge-gui/res/cardsfolder/p/porphyry_nodes.txt svneol=native#text/plain forge-gui/res/cardsfolder/p/port_inspector.txt svneol=native#text/plain forge-gui/res/cardsfolder/p/port_town.txt -text +forge-gui/res/cardsfolder/p/portal_mage.txt -text forge-gui/res/cardsfolder/p/portcullis.txt svneol=native#text/plain forge-gui/res/cardsfolder/p/portent.txt svneol=native#text/plain forge-gui/res/cardsfolder/p/portent_of_betrayal.txt -text diff --git a/forge-ai/src/main/java/forge/ai/SpellApiToAi.java b/forge-ai/src/main/java/forge/ai/SpellApiToAi.java index 092234bb2f8..35eb2784a16 100644 --- a/forge-ai/src/main/java/forge/ai/SpellApiToAi.java +++ b/forge-ai/src/main/java/forge/ai/SpellApiToAi.java @@ -31,6 +31,7 @@ public enum SpellApiToAi { .put(ApiType.BidLife, BidLifeAi.class) .put(ApiType.Bond, BondAi.class) .put(ApiType.Branch, AlwaysPlayAi.class) + .put(ApiType.ChangeCombatants, CannotPlayAi.class) .put(ApiType.ChangeTargets, ChangeTargetsAi.class) .put(ApiType.ChangeZone, ChangeZoneAi.class) .put(ApiType.ChangeZoneAll, ChangeZoneAllAi.class) diff --git a/forge-game/src/main/java/forge/game/ability/ApiType.java b/forge-game/src/main/java/forge/game/ability/ApiType.java index ae0011e8bde..6e619b26c58 100644 --- a/forge-game/src/main/java/forge/game/ability/ApiType.java +++ b/forge-game/src/main/java/forge/game/ability/ApiType.java @@ -27,6 +27,7 @@ public enum ApiType { BidLife (BidLifeEffect.class), Bond (BondEffect.class), Branch (BranchEffect.class), + ChangeCombatants (ChangeCombatantsEffect.class), ChangeTargets (ChangeTargetsEffect.class), ChangeText (ChangeTextEffect.class), ChangeZone (ChangeZoneEffect.class), diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeCombatantsEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeCombatantsEffect.java new file mode 100644 index 00000000000..42ae107ffa6 --- /dev/null +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeCombatantsEffect.java @@ -0,0 +1,62 @@ +package forge.game.ability.effects; + +import forge.game.Game; +import forge.game.GameEntity; +import forge.game.ability.SpellAbilityEffect; +import forge.game.card.Card; +import forge.game.combat.AttackingBand; +import forge.game.combat.Combat; +import forge.game.event.GameEventCombatChanged; +import forge.game.spellability.SpellAbility; +import forge.game.spellability.TargetRestrictions; +import forge.util.collect.FCollectionView; + +import org.apache.commons.lang3.StringUtils; + +import java.util.List; + +public class ChangeCombatantsEffect extends SpellAbilityEffect { + + @Override + protected String getStackDescription(SpellAbility sa) { + final StringBuilder sb = new StringBuilder(); + + final List tgtCards = getTargetCards(sa); + // should update when adding effects for defined blocker + sb.append("Reselect the defender of "); + sb.append(StringUtils.join(tgtCards, ", ")); + + return sb.toString(); + } + + @Override + public void resolve(SpellAbility sa) { + boolean isCombatChanged = false; + final Game game = sa.getActivatingPlayer().getGame(); + final TargetRestrictions tgt = sa.getTargetRestrictions(); + // TODO: may expand this effect for defined blocker (False Orders, General Jarkeld, Sorrow's Path, Ydwen Efreet) + for (final Card c : getTargetCards(sa)) { + if ((tgt == null) || c.canBeTargetedBy(sa)) { + final Combat combat = game.getCombat(); + final GameEntity orginalDefender = combat.getDefenderByAttacker(c); + final FCollectionView defs = combat.getDefenders(); + final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(defs, sa, + "Choose which defender to attack with " + c, false); + if (orginalDefender != null && !orginalDefender.equals(defender)) { + AttackingBand ab = combat.getBandOfAttacker(c); + if (ab != null) { + combat.unregisterAttacker(c, ab); + ab.removeAttacker(c); + } + combat.addAttacker(c, defender); + isCombatChanged = true; + } + } + } + + if (isCombatChanged) { + game.updateCombatForView(); + game.fireEvent(new GameEventCombatChanged()); + } + } +} diff --git a/forge-game/src/main/java/forge/game/combat/Combat.java b/forge-game/src/main/java/forge/game/combat/Combat.java index d9f54e0988a..33b7aa9d4df 100644 --- a/forge-game/src/main/java/forge/game/combat/Combat.java +++ b/forge-game/src/main/java/forge/game/combat/Combat.java @@ -529,7 +529,7 @@ public class Combat { } // removes references to this attacker from all indices and orders - private void unregisterAttacker(final Card c, AttackingBand ab) { + public void unregisterAttacker(final Card c, AttackingBand ab) { blockersOrderedForDamageAssignment.remove(c); Collection blockers = blockedBands.get(ab); @@ -544,7 +544,7 @@ public class Combat { } // removes references to this defender from all indices and orders - private void unregisterDefender(final Card c, AttackingBand bandBeingBlocked) { + public void unregisterDefender(final Card c, AttackingBand bandBeingBlocked) { attackersOrderedForDamageAssignment.remove(c); for (Card atk : bandBeingBlocked.getAttackers()) { if (blockersOrderedForDamageAssignment.containsKey(atk)) { diff --git a/forge-gui/res/cardsfolder/p/portal_mage.txt b/forge-gui/res/cardsfolder/p/portal_mage.txt new file mode 100644 index 00000000000..2c8e47a3dc1 --- /dev/null +++ b/forge-gui/res/cardsfolder/p/portal_mage.txt @@ -0,0 +1,10 @@ +Name:Portal Mage +ManaCost:2 U +Types:Creature Human Wizard +PT:2/2 +K:Flash +T:Mode$ ChangesZone | Phase$ Declare Attackers | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeAttacker | OptionalDecider$ You | TriggerDescription$ When Portal Mage enters the battlefield during the declare attackers step, you may reselect which player or planeswalker target attacking creature is attacking. +SVar:TrigChangeAttacker:DB$ ChangeCombatants | ValidTgts$ Creature.attacking | TgtPrompt$ Select target attacking creature +SVar:RemAIDeck:True +SVar:Picture:http://www.wizards.com/global/images/magic/general/portal_mage.jpg +Oracle:Flash\nWhen Portal Mage enters the battlefield during the declare attackers step, you may reselect which player or planeswalker target attacking creature is attacking. (It can't attack its controller or its controller's planeswalkers.)