From ff7a0f1ae6453da540c3e4bb4d1b43df9155452d Mon Sep 17 00:00:00 2001 From: Hanmac Date: Tue, 6 Feb 2018 12:56:02 +0100 Subject: [PATCH] Regeneration Rework: - Regenerate or RegenerateAll does create an Effect in Command which does replace Destroy if possible - Trigger Regenerated will be added to the Effect if something does care about "regenerated that way" - new Regeneration Api is the internal effect that does handle the actual regeneration - ReplaceDestroy has Section to handle if Regeneration is possible - CardShields are removed with the option in PlayerController --- .../java/forge/ai/PlayerControllerAi.java | 9 +- .../src/main/java/forge/ai/SpellApiToAi.java | 1 + .../src/main/java/forge/game/GameAction.java | 24 ++---- .../main/java/forge/game/ability/ApiType.java | 1 + .../game/ability/SpellAbilityEffect.java | 11 ++- .../ability/effects/RegenerateAllEffect.java | 30 +++---- .../ability/effects/RegenerateBaseEffect.java | 85 +++++++++++++++++++ .../ability/effects/RegenerateEffect.java | 65 +++----------- .../ability/effects/RegenerationEffect.java | 55 ++++++++++++ .../src/main/java/forge/game/card/Card.java | 16 ++-- .../java/forge/game/card/CardFactoryUtil.java | 9 ++ .../java/forge/game/card/CardShields.java | 79 ----------------- .../forge/game/player/PlayerController.java | 1 - .../game/replacement/ReplaceDestroy.java | 29 +++++-- .../forge/game/trigger/TriggerDestroyed.java | 16 ++-- .../game/trigger/TriggerRegenerated.java | 81 ++++++++++++++++++ .../java/forge/game/trigger/TriggerType.java | 1 + .../util/PlayerControllerForTests.java | 5 -- .../res/cardsfolder/d/debt_of_loyalty.txt | 4 +- forge-gui/res/cardsfolder/m/matopi_golem.txt | 5 +- .../res/cardsfolder/s/skeleton_scavengers.txt | 5 +- .../res/cardsfolder/s/soldevi_sentry.txt | 4 +- .../forge/player/PlayerControllerHuman.java | 12 --- 23 files changed, 319 insertions(+), 229 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java create mode 100644 forge-game/src/main/java/forge/game/ability/effects/RegenerationEffect.java delete mode 100644 forge-game/src/main/java/forge/game/card/CardShields.java create mode 100644 forge-game/src/main/java/forge/game/trigger/TriggerRegenerated.java diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 2a73867ee70..c0e35dfb593 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -1,6 +1,6 @@ package forge.ai; -import com.esotericsoftware.minlog.Log; +//import com.esotericsoftware.minlog.Log; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; @@ -398,7 +398,7 @@ public class PlayerControllerAi extends PlayerController { if (StringUtils.isBlank(chosen) && !validTypes.isEmpty()) { chosen = validTypes.get(0); - Log.warn("AI has no idea how to choose " + kindOfType +", defaulting to 1st element: chosen"); + //Log.warn("AI has no idea how to choose " + kindOfType +", defaulting to 1st element: chosen"); } game.getAction().nofityOfValue(sa, player, chosen, player); return chosen; @@ -866,11 +866,6 @@ public class PlayerControllerAi extends PlayerController { return brains.getBooleanProperty(AiProps.CHEAT_WITH_MANA_ON_SHUFFLE) ? brains.cheatShuffle(list) : list; } - @Override - public CardShields chooseRegenerationShield(Card c) { - return Iterables.getFirst(c.getShields(), null); - } - @Override public List chooseCardsYouWonToAddToDeck(List losses) { // TODO AI takes all by default diff --git a/forge-ai/src/main/java/forge/ai/SpellApiToAi.java b/forge-ai/src/main/java/forge/ai/SpellApiToAi.java index 3fd36fba61b..22340548028 100644 --- a/forge-ai/src/main/java/forge/ai/SpellApiToAi.java +++ b/forge-ai/src/main/java/forge/ai/SpellApiToAi.java @@ -117,6 +117,7 @@ public enum SpellApiToAi { .put(ApiType.RearrangeTopOfLibrary, RearrangeTopOfLibraryAi.class) .put(ApiType.Regenerate, RegenerateAi.class) .put(ApiType.RegenerateAll, RegenerateAllAi.class) + .put(ApiType.Regeneration, AlwaysPlayAi.class) .put(ApiType.RemoveCounter, CountersRemoveAi.class) .put(ApiType.RemoveCounterAll, CannotPlayAi.class) .put(ApiType.RemoveFromCombat, RemoveFromCombatAi.class) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index e02423ffcc3..56d6f863d8e 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -1402,26 +1402,13 @@ public class GameAction { return false; } - if (c.canBeShielded() && (!c.isCreature() || c.getNetToughness() > 0) - && (c.getShieldCount() > 0 || c.hasKeyword("If CARDNAME would be destroyed, regenerate it."))) { - c.subtractShield(c.getController().getController().chooseRegenerationShield(c)); - c.setDamage(0); - c.setHasBeenDealtDeathtouchDamage(false); - c.tap(); - c.addRegeneratedThisTurn(); - if (game.getCombat() != null) { - game.getCombat().removeFromCombat(c); - } - - // Play the Regen sound - game.fireEvent(new GameEventCardRegenerated()); - - return false; - } - return destroyNoRegeneration(c, sa); + return destroy(c, sa, true); + } + public final boolean destroyNoRegeneration(final Card c, final SpellAbility sa) { + return destroy(c, sa, false); } - public final boolean destroyNoRegeneration(final Card c, final SpellAbility sa) { + public final boolean destroy(final Card c, final SpellAbility sa, final boolean regenerate) { Player activator = null; if (!c.canBeDestroyed()) { return false; @@ -1433,6 +1420,7 @@ public class GameAction { repRunParams.put("Source", sa); repRunParams.put("Card", c); repRunParams.put("Affected", c); + repRunParams.put("Regeneration", regenerate); if (game.getReplacementHandler().run(repRunParams) != ReplacementResult.NotReplaced) { return false; 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 df2cc18b4b9..7c0a63d1ceb 100644 --- a/forge-game/src/main/java/forge/game/ability/ApiType.java +++ b/forge-game/src/main/java/forge/game/ability/ApiType.java @@ -115,6 +115,7 @@ public enum ApiType { RearrangeTopOfLibrary (RearrangeTopOfLibraryEffect.class), Regenerate (RegenerateEffect.class), RegenerateAll (RegenerateAllEffect.class), + Regeneration (RegenerationEffect.class), RemoveCounter (CountersRemoveEffect.class), RemoveCounterAll (CountersRemoveAllEffect.class), RemoveFromCombat (RemoveFromCombatEffect.class), diff --git a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java index bed0d1faa09..6670b9bab6d 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -292,9 +292,16 @@ public abstract class SpellAbilityEffect { protected static void addForgetOnMovedTrigger(final Card card, final String zone) { String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ " + zone + " | Destination$ Any | TriggerZones$ Command | Static$ True"; - String effect = "DB$ Pump | ForgetObjects$ TriggeredCard"; + String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard"; + String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile" + + " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0"; + + SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, card); + AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, card); + saForget.setSubAbility(saExile); + final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true); - parsedTrigger.setOverridingAbility(AbilityFactory.getAbility(effect, card)); + parsedTrigger.setOverridingAbility(saForget); final Trigger addedTrigger = card.addTrigger(parsedTrigger); addedTrigger.setIntrinsic(true); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/RegenerateAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RegenerateAllEffect.java index 9460cf32773..626568be339 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RegenerateAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RegenerateAllEffect.java @@ -1,22 +1,27 @@ package forge.game.ability.effects; -import forge.GameCommand; import forge.game.Game; -import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.card.CardCollectionView; import forge.game.card.CardLists; -import forge.game.card.CardShields; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -public class RegenerateAllEffect extends SpellAbilityEffect { +public class RegenerateAllEffect extends RegenerateBaseEffect { + /* + * (non-Javadoc) + * @see forge.game.ability.SpellAbilityEffect#getStackDescription(forge.game.spellability.SpellAbility) + */ @Override protected String getStackDescription(SpellAbility sa) { return "Regenerate all valid cards."; } + /* + * (non-Javadoc) + * @see forge.game.ability.SpellAbilityEffect#resolve(forge.game.spellability.SpellAbility) + */ @Override public void resolve(SpellAbility sa) { final Card hostCard = sa.getHostCard(); @@ -30,21 +35,8 @@ public class RegenerateAllEffect extends SpellAbilityEffect { CardCollectionView list = game.getCardsIn(ZoneType.Battlefield); list = CardLists.getValidCards(list, valid.split(","), hostCard.getController(), hostCard, sa); - for (final Card c : list) { - final GameCommand untilEOT = new GameCommand() { - private static final long serialVersionUID = 259368227093961103L; - - @Override - public void run() { - c.resetShield(); - } - }; - - if (c.isInPlay()) { - c.addShield(new CardShields(sa, null)); - game.getEndOfTurn().addUntil(untilEOT); - } - } + // create Effect for Regeneration + createRengenerationEffect(sa, list); } // regenerateAllResolve } diff --git a/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java new file mode 100644 index 00000000000..2bfb39ad291 --- /dev/null +++ b/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java @@ -0,0 +1,85 @@ +package forge.game.ability.effects; + +import forge.GameCommand; +import forge.game.Game; +import forge.game.ability.AbilityFactory; +import forge.game.ability.AbilityUtils; +import forge.game.ability.SpellAbilityEffect; +import forge.game.card.Card; +import forge.game.replacement.ReplacementEffect; +import forge.game.replacement.ReplacementHandler; +import forge.game.spellability.AbilitySub; +import forge.game.spellability.SpellAbility; +import forge.game.trigger.Trigger; +import forge.game.trigger.TriggerHandler; +import forge.game.trigger.TriggerType; +import forge.game.zone.ZoneType; + +public abstract class RegenerateBaseEffect extends SpellAbilityEffect { + + public void createRengenerationEffect(SpellAbility sa, final Iterable list) { + final Card hostCard = sa.getHostCard(); + final Game game = hostCard.getGame(); + + // create Effect for Regeneration + Card eff = createEffect( + hostCard, sa.getActivatingPlayer(), hostCard.getName() + "'s Regeneration", hostCard.getImageKey()); + + eff.addRemembered(list); + addForgetOnMovedTrigger(eff, "Battlefield"); + + // build ReplacementEffect + String repeffstr = "Event$ Destroy | ActiveZones$ Command | ValidCard$ Card.IsRemembered | Regeneration$ True" + + " | Description$ Regeneration (if creature would be destroyed, regenerate it instead)"; + + String effect = "DB$ Regeneration | Defined$ ReplacedCard"; + String exileEff = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile" + + " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0"; + ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true); + + SpellAbility saReg = AbilityFactory.getAbility(effect, eff); + AbilitySub saExile = (AbilitySub)AbilityFactory.getAbility(exileEff, eff); + + saReg.setSubAbility(saExile); + re.setOverridingAbility(saReg); + eff.addReplacementEffect(re); + + // add extra Remembered + if (sa.hasParam("RememberObjects")) { + eff.addRemembered(AbilityUtils.getDefinedObjects(hostCard, sa.getParam("RememberObjects"), sa)); + } + + if (sa.hasParam("RegenerationTrigger")) { + final String str = sa.getSVar(sa.getParam("RegenerationTrigger")); + + SpellAbility trigSA = AbilityFactory.getAbility(str, eff); + + final String trigStr = "Mode$ Regenerated | ValidCause$ Effect.Self | TriggerZones$ Command " + + " | TriggerDescription$ " + trigSA.getDescription(); + final Trigger trigger = TriggerHandler.parseTrigger(trigStr, eff, true); + trigger.setOverridingAbility(trigSA); + eff.moveTrigger(trigger); + } + + // Copy text changes + if (sa.isIntrinsic()) { + eff.copyChangedTextFrom(hostCard); + } + + eff.updateStateForView(); + + game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); + game.getAction().moveTo(ZoneType.Command, eff, sa); + game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); + + final GameCommand untilEOT = new GameCommand() { + private static final long serialVersionUID = 259368227093961103L; + + @Override + public void run() { + game.getAction().exile(eff, null); + } + }; + game.getEndOfTurn().addUntil(untilEOT); + } +} diff --git a/forge-game/src/main/java/forge/game/ability/effects/RegenerateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RegenerateEffect.java index ef2773675a0..1957cb4f14b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RegenerateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RegenerateEffect.java @@ -1,24 +1,16 @@ package forge.game.ability.effects; -import forge.GameCommand; -import forge.game.Game; -import forge.game.ability.AbilityFactory; -import forge.game.ability.AbilityUtils; -import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; -import forge.game.card.CardShields; -import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; -import forge.util.TextUtil; import java.util.Iterator; import java.util.List; -public class RegenerateEffect extends SpellAbilityEffect { +public class RegenerateEffect extends RegenerateBaseEffect { - /* (non-Javadoc) - * @see forge.card.abilityfactory.SpellEffect#getStackDescription(java.util.Map, forge.card.spellability.SpellAbility) + /* + * (non-Javadoc) + * @see forge.game.ability.SpellAbilityEffect#getStackDescription(forge.game.spellability.SpellAbility) */ @Override protected String getStackDescription(SpellAbility sa) { @@ -47,51 +39,14 @@ public class RegenerateEffect extends SpellAbilityEffect { return sb.toString(); } + /* + * (non-Javadoc) + * @see forge.game.ability.SpellAbilityEffect#resolve(forge.game.spellability.SpellAbility) + */ @Override public void resolve(SpellAbility sa) { - final TargetRestrictions tgt = sa.getTargetRestrictions(); - final Game game = sa.getActivatingPlayer().getGame(); - final Card sourceCard = sa.getHostCard(); - - for (final Card tgtC : getTargetCards(sa)) { - final GameCommand untilEOT = new GameCommand() { - private static final long serialVersionUID = 1922050611313909200L; - - @Override - public void run() { - tgtC.resetShield(); - } - }; - - if (tgtC.isInPlay() && (tgt == null || tgt.canTgtPlayer() || tgtC.canBeTargetedBy(sa))) { - SpellAbility triggerSA = null; - if (sa.hasParam("RegenerationTrigger")) { - String abString = sa.getHostCard().getSVar(sa.getParam("RegenerationTrigger")); - if (sa.hasParam("ReplacePlayerName")) { // Soldevi Sentry - String def = sa.getParam("ReplacePlayerName"); - List replaced = AbilityUtils.getDefinedPlayers(sourceCard, def, sa); - if(replaced.isEmpty()) - abString = TextUtil.fastReplace(abString, def, ""); - else - abString = TextUtil.fastReplace(abString, def, replaced.get(0).getName()); - } else if (sa.hasParam("ReplaceCardUID")) { // Debt of Loyalty - String def = sa.getParam("ReplaceCardUID"); - List replaced = AbilityUtils.getDefinedCards(sourceCard, def, sa); - if(replaced.isEmpty()) - abString = TextUtil.fastReplace(abString, def, ""); - else - abString = TextUtil.fastReplace(abString, def, Integer.toString(replaced.get(0).getId())); - } - triggerSA = AbilityFactory.getAbility(abString, sourceCard); - triggerSA.setActivatingPlayer(sa.getActivatingPlayer()); - triggerSA.setTrigger(true); - triggerSA.setHostCard(sourceCard); - } - CardShields shield = new CardShields(sa, triggerSA); - tgtC.addShield(shield); - game.getEndOfTurn().addUntil(untilEOT); - } - } + // create Effect for Regeneration + createRengenerationEffect(sa, getTargetCards(sa)); } // regenerateResolve } diff --git a/forge-game/src/main/java/forge/game/ability/effects/RegenerationEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RegenerationEffect.java new file mode 100644 index 00000000000..5aec79f7b97 --- /dev/null +++ b/forge-game/src/main/java/forge/game/ability/effects/RegenerationEffect.java @@ -0,0 +1,55 @@ +package forge.game.ability.effects; + +import forge.game.Game; +import forge.game.ability.SpellAbilityEffect; +import forge.game.card.Card; +import forge.game.event.GameEventCardRegenerated; +import forge.game.spellability.SpellAbility; +import forge.game.trigger.TriggerType; + +import java.util.Map; + +import com.google.common.collect.Maps; + +public class RegenerationEffect extends SpellAbilityEffect { + + /* + * (non-Javadoc) + * @see forge.game.ability.SpellAbilityEffect#resolve(forge.game.spellability.SpellAbility) + */ + @Override + public void resolve(SpellAbility sa) { + final Card host = sa.getHostCard(); + final Game game = host.getGame(); + for (Card c : getTargetCards(sa)) { + if (!c.canBeShielded() || !c.isInPlay()) { + continue; + } + + c.setDamage(0); + c.setHasBeenDealtDeathtouchDamage(false); + c.tap(); + c.addRegeneratedThisTurn(); + + if (game.getCombat() != null) { + game.getCombat().removeFromCombat(c); + } + + // Play the Regen sound + game.fireEvent(new GameEventCardRegenerated()); + + if (host.getType().hasStringType("Effect")) { + c.subtractShield(host); + host.removeRemembered(c); + } + + // Run triggers + final Map runParams = Maps.newHashMap(); + runParams.put("Card", c); + runParams.put("Cause", host); + game.getTriggerHandler().runTrigger(TriggerType.Regenerated, runParams, false); + } + + } + +} diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 7b9eaff8470..fe16051038f 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -194,7 +194,7 @@ public class Card extends GameEntity implements Comparable { private boolean hasBeenDealtDeathtouchDamage = false; // regeneration - private List shields = Lists.newArrayList(); + private FCollection shields = new FCollection<>(); private int regeneratedThisTurn = 0; private int turnInZone; @@ -2275,22 +2275,20 @@ public class Card extends GameEntity implements Comparable { } // shield = regeneration - public final Iterable getShields() { + public final Iterable getShields() { return shields; } public final int getShieldCount() { return shields.size(); } - public final void addShield(final CardShields shield) { - shields.add(shield); - view.updateShieldCount(this); + public final void addShield(final Card shield) { + if (shields.add(shield)) { + view.updateShieldCount(this); + } } - public final void subtractShield(CardShields shield) { - if (shield != null && shield.hasTrigger()) { - getGame().getStack().addSimultaneousStackEntry(shield.getTriggerSA()); - } + public final void subtractShield(final Card shield) { if (shields.remove(shield)) { view.updateShieldCount(this); } diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index f2ab941b3c3..dcf7d116840 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -3346,6 +3346,15 @@ public class CardFactoryUtil { final ReplacementEffect re = makeEtbCounter(sb.toString(), card, intrinsic); + inst.addReplacement(re); + } else if (keyword.equals("If CARDNAME would be destroyed, regenerate it.")) { + String repeffstr = "Event$ Destroy | ActiveZones$ Battlefield | ValidCard$ Card.Self" + + " | Secondary$ True | Regeneration$ True | Description$ " + keyword; + String effect = "DB$ Regeneration | Defined$ ReplacedCard"; + ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, card, intrinsic); + SpellAbility sa = AbilityFactory.getAbility(effect, card); + re.setOverridingAbility(sa); + inst.addReplacement(re); } diff --git a/forge-game/src/main/java/forge/game/card/CardShields.java b/forge-game/src/main/java/forge/game/card/CardShields.java deleted file mode 100644 index 50c82166829..00000000000 --- a/forge-game/src/main/java/forge/game/card/CardShields.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Forge: Play Magic: the Gathering. - * Copyright (C) 2011 Forge Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package forge.game.card; - -import forge.game.spellability.SpellAbility; - -/** - *

- * Card_Shields class. - *

- * - * @author Forge - * @version $Id: CardShields.java 23786 2013-11-24 06:59:42Z Max mtg $ - */ -public class CardShields { - // restore the regeneration shields - private final SpellAbility sourceSA; - private final SpellAbility triggerSA; - - /** - * Instantiates a new CardShields. - * - * @param sourceSA - * a SpellAbility - */ - public CardShields(final SpellAbility sourceSA, final SpellAbility triggerSA) { - this.sourceSA = sourceSA; - this.triggerSA = triggerSA; - } - - /** - * - * getSourceSA. - * - * @return sourceSA - */ - public final SpellAbility getSourceSA() { - return this.sourceSA; - } - - /** - * - * getTriggerSA. - * - * @return triggerSA - */ - public final SpellAbility getTriggerSA() { - return this.triggerSA; - } - - public final boolean hasTrigger() { - return this.triggerSA != null; - } - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - String suffix = this.triggerSA != null ? " - " + triggerSA.getDescription() : ""; - return this.sourceSA.getHostCard().getName() + suffix; - } -} diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java index 709668790bd..1d521406df2 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -196,7 +196,6 @@ public abstract class PlayerController { public abstract boolean confirmPayment(CostPart costPart, String string, SpellAbility sa); public abstract ReplacementEffect chooseSingleReplacementEffect(String prompt, List possibleReplacers, Map runParams); public abstract String chooseProtectionType(String string, SpellAbility sa, List choices); - public abstract CardShields chooseRegenerationShield(Card c); // these 4 need some refining. public abstract boolean payCostToPreventEffect(Cost cost, SpellAbility sa, boolean alreadyPaid, FCollectionView allPayers); diff --git a/forge-game/src/main/java/forge/game/replacement/ReplaceDestroy.java b/forge-game/src/main/java/forge/game/replacement/ReplaceDestroy.java index 69e6ddfadad..fa61c0e1123 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplaceDestroy.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplaceDestroy.java @@ -46,18 +46,35 @@ public class ReplaceDestroy extends ReplacementEffect { if (!runParams.get("Event").equals("Destroy")) { return false; } - if (this.getMapParams().containsKey("ValidPlayer")) { - if (!matchesValid(runParams.get("Affected"), this.getMapParams().get("ValidPlayer").split(","), this.getHostCard())) { + if (hasParam("ValidPlayer")) { + if (!matchesValid(runParams.get("Affected"), getParam("ValidPlayer").split(","), getHostCard())) { return false; } } - if (this.getMapParams().containsKey("ValidCard")) { - if (!matchesValid(runParams.get("Card"), this.getMapParams().get("ValidCard").split(","), this.getHostCard())) { + if (hasParam("ValidCard")) { + Card card = (Card)runParams.get("Card"); + if (!matchesValid(card, getParam("ValidCard").split(","), getHostCard())) { return false; } + // extra check for Regeneration + if (hasParam("Regeneration")) { + if (!runParams.containsKey("Regeneration")) { + return false; + } + if (!(Boolean)runParams.get("Regeneration")) { + return false; + } + if (!card.canBeShielded()) { + return false; + } + if (card.isCreature()) { + if (card.getNetToughness() <= 0) + return false; + } + } } - if (this.getMapParams().containsKey("ValidSource")) { - if (!matchesValid(runParams.get("Source"), this.getMapParams().get("ValidSource").split(","), this.getHostCard())) { + if (hasParam("ValidSource")) { + if (!matchesValid(runParams.get("Source"), getParam("ValidSource").split(","), getHostCard())) { return false; } } diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerDestroyed.java b/forge-game/src/main/java/forge/game/trigger/TriggerDestroyed.java index 4d8df07eaef..cd533694d4f 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerDestroyed.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerDestroyed.java @@ -17,6 +17,8 @@ */ package forge.game.trigger; +import java.util.Map; + import forge.game.card.Card; import forge.game.spellability.SpellAbility; @@ -42,22 +44,20 @@ public class TriggerDestroyed extends Trigger { * @param intrinsic * the intrinsic */ - public TriggerDestroyed(final java.util.Map params, final Card host, final boolean intrinsic) { + public TriggerDestroyed(final Map params, final Card host, final boolean intrinsic) { super(params, host, intrinsic); } /** {@inheritDoc} */ @Override - public final boolean performTest(final java.util.Map runParams2) { - if (this.mapParams.containsKey("ValidCauser")) { - if (!matchesValid(runParams2.get("Causer"), this.mapParams.get("ValidCauser").split(","), - this.getHostCard())) { + public final boolean performTest(final Map runParams2) { + if (hasParam("ValidCauser")) { + if (!matchesValid(runParams2.get("Causer"), getParam("ValidCauser").split(","), getHostCard())) { return false; } } - if (this.mapParams.containsKey("ValidCard")) { - if (!matchesValid(runParams2.get("Card"), this.mapParams.get("ValidCard").split(","), - this.getHostCard())) { + if (hasParam("ValidCard")) { + if (!matchesValid(runParams2.get("Card"), getParam("ValidCard").split(","), getHostCard())) { return false; } } diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerRegenerated.java b/forge-game/src/main/java/forge/game/trigger/TriggerRegenerated.java new file mode 100644 index 00000000000..cd9f836c466 --- /dev/null +++ b/forge-game/src/main/java/forge/game/trigger/TriggerRegenerated.java @@ -0,0 +1,81 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.game.trigger; + +import java.util.Map; + +import forge.game.card.Card; +import forge.game.spellability.SpellAbility; + +/** + *

+ * Trigger_Destroyed class. + *

+ * + * @author Forge + * @version $Id: TriggerDestroyed.java 17802 2012-10-31 08:05:14Z Max mtg $ + */ +public class TriggerRegenerated extends Trigger { + + /** + *

+ * Constructor for Trigger_Destroyed. + *

+ * + * @param params + * a {@link java.util.HashMap} object. + * @param host + * a {@link forge.game.card.Card} object. + * @param intrinsic + * the intrinsic + */ + public TriggerRegenerated(final Map params, final Card host, final boolean intrinsic) { + super(params, host, intrinsic); + } + + /** {@inheritDoc} */ + @Override + public final boolean performTest(final Map runParams2) { + if (hasParam("ValidCause")) { + if (!matchesValid(runParams2.get("Cause"), getParam("ValidCause").split(","), getHostCard())) { + return false; + } + } + if (hasParam("ValidCard")) { + if (!matchesValid(runParams2.get("Card"), getParam("ValidCard").split(","), getHostCard())) { + return false; + } + } + return true; + } + + /** {@inheritDoc} */ + @Override + public final void setTriggeringObjects(final SpellAbility sa) { + sa.setTriggeringObject("Card", this.getRunParams().get("Card")); + sa.setTriggeringObject("Cause", this.getRunParams().get("Cause")); + } + + @Override + public String getImportantStackObjects(SpellAbility sa) { + StringBuilder sb = new StringBuilder(); + sb.append("Regenerated: ").append(sa.getTriggeringObject("Card")); + //sb.append("Destroyer: ").append(sa.getTriggeringObject("Causer")); + return sb.toString(); + } +} diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerType.java b/forge-game/src/main/java/forge/game/trigger/TriggerType.java index dd6ff407409..837be50f1fd 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerType.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerType.java @@ -67,6 +67,7 @@ public enum TriggerType { PlanarDice(TriggerPlanarDice.class), PlaneswalkedFrom(TriggerPlaneswalkedFrom.class), PlaneswalkedTo(TriggerPlaneswalkedTo.class), + Regenerated(TriggerRegenerated.class), Revealed(TriggerRevealed.class), Sacrificed(TriggerSacrificed.class), Scry(TriggerScry.class), diff --git a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java index b459412e064..91b1259fd42 100644 --- a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java +++ b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java @@ -566,11 +566,6 @@ public class PlayerControllerForTests extends PlayerController { // test this! } - @Override - public CardShields chooseRegenerationShield(Card c) { - return Iterables.getFirst(c.getShields(), null); - } - @Override public List chooseCardsYouWonToAddToDeck(List losses) { // TODO Auto-generated method stub diff --git a/forge-gui/res/cardsfolder/d/debt_of_loyalty.txt b/forge-gui/res/cardsfolder/d/debt_of_loyalty.txt index b9c44a86f34..617a42929ae 100644 --- a/forge-gui/res/cardsfolder/d/debt_of_loyalty.txt +++ b/forge-gui/res/cardsfolder/d/debt_of_loyalty.txt @@ -1,8 +1,8 @@ Name:Debt of Loyalty ManaCost:1 W W Types:Instant -A:SP$ Regenerate | Cost$ 1 W W | ValidTgts$ Creature | TgtPrompt$ Select target creature | RegenerationTrigger$ TrigGainControl | ReplaceCardUID$ Targeted | SpellDescription$ Regenerate target creature. You gain control of that creature if it regenerates this way. -SVar:TrigGainControl:ST$ GainControl | Cost$ 0 | Defined$ CardUID_Targeted | NewController$ You | SpellDescription$ Source controller gains control of CARDNAME if it regenerates this way. +A:SP$ Regenerate | Cost$ 1 W W | ValidTgts$ Creature | TgtPrompt$ Select target creature | RegenerationTrigger$ TrigGainControl | References$ TrigGainControl | SpellDescription$ Regenerate target creature. You gain control of that creature if it regenerates this way. +SVar:TrigGainControl:ST$ GainControl | Cost$ 0 | Defined$ TriggeredCard | NewController$ You | SpellDescription$ Source controller gains control of CARDNAME if it regenerates this way. SVar:RemAIDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/debt_of_loyalty.jpg Oracle:Regenerate target creature. You gain control of that creature if it regenerates this way. diff --git a/forge-gui/res/cardsfolder/m/matopi_golem.txt b/forge-gui/res/cardsfolder/m/matopi_golem.txt index 93f4d645aba..6cece8d30e9 100644 --- a/forge-gui/res/cardsfolder/m/matopi_golem.txt +++ b/forge-gui/res/cardsfolder/m/matopi_golem.txt @@ -2,7 +2,8 @@ Name:Matopi Golem ManaCost:5 Types:Artifact Creature Golem PT:3/3 -A:AB$ Regenerate | Cost$ 1 | RegenerationTrigger$ TrigPutCounter | SpellDescription$ Regenerate CARDNAME. When it regenerates this way, put a -1/-1 counter on it. -SVar:TrigPutCounter:DB$ PutCounter | CounterType$ M1M1 | CounterNum$ 1 | SpellDescription$ When it regenerates this way, put a -1/-1 counter on it. +A:AB$ Regenerate | Cost$ 1 | RegenerationTrigger$ TrigPutCounter | References$ TrigPutCounter | SpellDescription$ Regenerate CARDNAME. When it regenerates this way, put a -1/-1 counter on it. +SVar:TrigPutCounter:DB$ PutCounter | Defined$ TriggeredCard | CounterType$ M1M1 | CounterNum$ 1 | SpellDescription$ When it regenerates this way, put a -1/-1 counter on it. +DeckHas:Ability$Counters SVar:Picture:http://www.wizards.com/global/images/magic/general/matopi_golem.jpg Oracle:{1}: Regenerate Matopi Golem. When it regenerates this way, put a -1/-1 counter on it. diff --git a/forge-gui/res/cardsfolder/s/skeleton_scavengers.txt b/forge-gui/res/cardsfolder/s/skeleton_scavengers.txt index 21fbfad544d..1c92120167e 100644 --- a/forge-gui/res/cardsfolder/s/skeleton_scavengers.txt +++ b/forge-gui/res/cardsfolder/s/skeleton_scavengers.txt @@ -3,8 +3,9 @@ ManaCost:2 B Types:Creature Skeleton PT:0/0 K:etbCounter:P1P1:1 -A:AB$ Regenerate | Cost$ X | CostDesc$ Pay {1} for each +1/+1 counter on CARDNAME: | References$ X | RegenerationTrigger$ TrigPutCounter | SpellDescription$ Regenerate CARDNAME. When it regenerates this way, put a +1/+1 counter on it. +A:AB$ Regenerate | Cost$ X | CostDesc$ Pay {1} for each +1/+1 counter on CARDNAME: | References$ X,TrigPutCounter | RegenerationTrigger$ TrigPutCounter | SpellDescription$ Regenerate CARDNAME. When it regenerates this way, put a +1/+1 counter on it. +SVar:TrigPutCounter:DB$ PutCounter | Defined$ TriggeredCard | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ When it regenerates this way, put a +1/+1 counter on it. SVar:X:Count$CardCounters.P1P1 -SVar:TrigPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ When it regenerates this way, put a +1/+1 counter on it. +DeckHas:Ability$Counters SVar:Picture:http://www.wizards.com/global/images/magic/general/skeleton_scavengers.jpg Oracle:Skeleton Scavengers enters the battlefield with a +1/+1 counter on it.\nPay {1} for each +1/+1 counter on Skeleton Scavengers: Regenerate Skeleton Scavengers. When it regenerates this way, put a +1/+1 counter on it. diff --git a/forge-gui/res/cardsfolder/s/soldevi_sentry.txt b/forge-gui/res/cardsfolder/s/soldevi_sentry.txt index 4b99963bae3..6e28789291a 100644 --- a/forge-gui/res/cardsfolder/s/soldevi_sentry.txt +++ b/forge-gui/res/cardsfolder/s/soldevi_sentry.txt @@ -2,7 +2,7 @@ Name:Soldevi Sentry ManaCost:1 Types:Artifact Creature Soldier PT:1/1 -A:AB$ Regenerate | Cost$ 1 | ValidTgts$ Opponent | Defined$ Self | RegenerationTrigger$ TrigDraw | ReplacePlayerName$ Targeted | SpellDescription$ Choose target opponent. Regenerate CARDNAME. When it regenerates this way, that player may draw a card. -SVar:TrigDraw:DB$ Draw | Defined$ PlayerNamed_Targeted | NumCards$ 1 | OptionalDecider$ True | SpellDescription$ When it regenerates this way, that player may draw a card. +A:AB$ Regenerate | Cost$ 1 | ValidTgts$ Opponent | Defined$ Self | RegenerationTrigger$ TrigDraw | RememberObjects$ TargetedPlayer | References$ TrigDraw | SpellDescription$ Choose target opponent. Regenerate CARDNAME. When it regenerates this way, that player may draw a card. +SVar:TrigDraw:DB$ Draw | Defined$ Remembered | NumCards$ 1 | OptionalDecider$ True | SpellDescription$ When it regenerates this way, that player may draw a card. SVar:Picture:http://www.wizards.com/global/images/magic/general/soldevi_sentry.jpg Oracle:{1}: Choose target opponent. Regenerate Soldevi Sentry. When it regenerates this way, that player may draw a card. diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 1d66e3cdfe6..8fac6cca207 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -1569,18 +1569,6 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } } - @Override - public CardShields chooseRegenerationShield(final Card c) { - if (c.getShieldCount() < 2) { - return Iterables.getFirst(c.getShields(), null); - } - final List shields = Lists.newArrayList(); - for (final CardShields shield : c.getShields()) { - shields.add(shield); - } - return getGui().one("Choose a regeneration shield:", shields); - } - @Override public List chooseCardsYouWonToAddToDeck(final List losses) { return getGui().many("Select cards to add to your deck", "Add these to my deck", 0, losses.size(), losses,