Merge branch 'regeneration' into 'master'

Regeneration

See merge request core-developers/forge!168
This commit is contained in:
Michael Kamensky
2018-02-08 12:07:41 +00:00
23 changed files with 324 additions and 229 deletions

View File

@@ -1,6 +1,6 @@
package forge.ai; package forge.ai;
import com.esotericsoftware.minlog.Log; //import com.esotericsoftware.minlog.Log;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@@ -398,7 +398,7 @@ public class PlayerControllerAi extends PlayerController {
if (StringUtils.isBlank(chosen) && !validTypes.isEmpty()) if (StringUtils.isBlank(chosen) && !validTypes.isEmpty())
{ {
chosen = validTypes.get(0); 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); game.getAction().nofityOfValue(sa, player, chosen, player);
return chosen; return chosen;
@@ -866,11 +866,6 @@ public class PlayerControllerAi extends PlayerController {
return brains.getBooleanProperty(AiProps.CHEAT_WITH_MANA_ON_SHUFFLE) ? brains.cheatShuffle(list) : list; 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 @Override
public List<PaperCard> chooseCardsYouWonToAddToDeck(List<PaperCard> losses) { public List<PaperCard> chooseCardsYouWonToAddToDeck(List<PaperCard> losses) {
// TODO AI takes all by default // TODO AI takes all by default

View File

@@ -117,6 +117,7 @@ public enum SpellApiToAi {
.put(ApiType.RearrangeTopOfLibrary, RearrangeTopOfLibraryAi.class) .put(ApiType.RearrangeTopOfLibrary, RearrangeTopOfLibraryAi.class)
.put(ApiType.Regenerate, RegenerateAi.class) .put(ApiType.Regenerate, RegenerateAi.class)
.put(ApiType.RegenerateAll, RegenerateAllAi.class) .put(ApiType.RegenerateAll, RegenerateAllAi.class)
.put(ApiType.Regeneration, AlwaysPlayAi.class)
.put(ApiType.RemoveCounter, CountersRemoveAi.class) .put(ApiType.RemoveCounter, CountersRemoveAi.class)
.put(ApiType.RemoveCounterAll, CannotPlayAi.class) .put(ApiType.RemoveCounterAll, CannotPlayAi.class)
.put(ApiType.RemoveFromCombat, RemoveFromCombatAi.class) .put(ApiType.RemoveFromCombat, RemoveFromCombatAi.class)

View File

@@ -1402,26 +1402,13 @@ public class GameAction {
return false; return false;
} }
if (c.canBeShielded() && (!c.isCreature() || c.getNetToughness() > 0) return destroy(c, sa, true);
&& (c.getShieldCount() > 0 || c.hasKeyword("If CARDNAME would be destroyed, regenerate it."))) { }
c.subtractShield(c.getController().getController().chooseRegenerationShield(c)); public final boolean destroyNoRegeneration(final Card c, final SpellAbility sa) {
c.setDamage(0); return destroy(c, sa, false);
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);
} }
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; Player activator = null;
if (!c.canBeDestroyed()) { if (!c.canBeDestroyed()) {
return false; return false;
@@ -1433,6 +1420,7 @@ public class GameAction {
repRunParams.put("Source", sa); repRunParams.put("Source", sa);
repRunParams.put("Card", c); repRunParams.put("Card", c);
repRunParams.put("Affected", c); repRunParams.put("Affected", c);
repRunParams.put("Regeneration", regenerate);
if (game.getReplacementHandler().run(repRunParams) != ReplacementResult.NotReplaced) { if (game.getReplacementHandler().run(repRunParams) != ReplacementResult.NotReplaced) {
return false; return false;

View File

@@ -115,6 +115,7 @@ public enum ApiType {
RearrangeTopOfLibrary (RearrangeTopOfLibraryEffect.class), RearrangeTopOfLibrary (RearrangeTopOfLibraryEffect.class),
Regenerate (RegenerateEffect.class), Regenerate (RegenerateEffect.class),
RegenerateAll (RegenerateAllEffect.class), RegenerateAll (RegenerateAllEffect.class),
Regeneration (RegenerationEffect.class),
RemoveCounter (CountersRemoveEffect.class), RemoveCounter (CountersRemoveEffect.class),
RemoveCounterAll (CountersRemoveAllEffect.class), RemoveCounterAll (CountersRemoveAllEffect.class),
RemoveFromCombat (RemoveFromCombatEffect.class), RemoveFromCombat (RemoveFromCombatEffect.class),

View File

@@ -292,9 +292,16 @@ public abstract class SpellAbilityEffect {
protected static void addForgetOnMovedTrigger(final Card card, final String zone) { 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 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); final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true);
parsedTrigger.setOverridingAbility(AbilityFactory.getAbility(effect, card)); parsedTrigger.setOverridingAbility(saForget);
final Trigger addedTrigger = card.addTrigger(parsedTrigger); final Trigger addedTrigger = card.addTrigger(parsedTrigger);
addedTrigger.setIntrinsic(true); addedTrigger.setIntrinsic(true);
} }

View File

@@ -1,22 +1,27 @@
package forge.game.ability.effects; package forge.game.ability.effects;
import forge.GameCommand;
import forge.game.Game; import forge.game.Game;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollectionView; import forge.game.card.CardCollectionView;
import forge.game.card.CardLists; import forge.game.card.CardLists;
import forge.game.card.CardShields;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType; 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 @Override
protected String getStackDescription(SpellAbility sa) { protected String getStackDescription(SpellAbility sa) {
return "Regenerate all valid cards."; return "Regenerate all valid cards.";
} }
/*
* (non-Javadoc)
* @see forge.game.ability.SpellAbilityEffect#resolve(forge.game.spellability.SpellAbility)
*/
@Override @Override
public void resolve(SpellAbility sa) { public void resolve(SpellAbility sa) {
final Card hostCard = sa.getHostCard(); final Card hostCard = sa.getHostCard();
@@ -30,21 +35,8 @@ public class RegenerateAllEffect extends SpellAbilityEffect {
CardCollectionView list = game.getCardsIn(ZoneType.Battlefield); CardCollectionView list = game.getCardsIn(ZoneType.Battlefield);
list = CardLists.getValidCards(list, valid.split(","), hostCard.getController(), hostCard, sa); list = CardLists.getValidCards(list, valid.split(","), hostCard.getController(), hostCard, sa);
for (final Card c : list) { // create Effect for Regeneration
final GameCommand untilEOT = new GameCommand() { createRengenerationEffect(sa, list);
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);
}
}
} // regenerateAllResolve } // regenerateAllResolve
} }

View File

@@ -0,0 +1,89 @@
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<Card> 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();
// add RegenEffect as Shield to the Affected Cards
for (final Card c : list) {
c.addShield(eff);
}
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);
}
}

View File

@@ -1,24 +1,16 @@
package forge.game.ability.effects; 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.Card;
import forge.game.card.CardShields;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.util.TextUtil;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; 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 @Override
protected String getStackDescription(SpellAbility sa) { protected String getStackDescription(SpellAbility sa) {
@@ -47,51 +39,14 @@ public class RegenerateEffect extends SpellAbilityEffect {
return sb.toString(); return sb.toString();
} }
/*
* (non-Javadoc)
* @see forge.game.ability.SpellAbilityEffect#resolve(forge.game.spellability.SpellAbility)
*/
@Override @Override
public void resolve(SpellAbility sa) { public void resolve(SpellAbility sa) {
final TargetRestrictions tgt = sa.getTargetRestrictions(); // create Effect for Regeneration
final Game game = sa.getActivatingPlayer().getGame(); createRengenerationEffect(sa, getTargetCards(sa));
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<Player> 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<Card> 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);
}
}
} // regenerateResolve } // regenerateResolve
} }

View File

@@ -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<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", c);
runParams.put("Cause", host);
game.getTriggerHandler().runTrigger(TriggerType.Regenerated, runParams, false);
}
}
}

View File

@@ -194,7 +194,7 @@ public class Card extends GameEntity implements Comparable<Card> {
private boolean hasBeenDealtDeathtouchDamage = false; private boolean hasBeenDealtDeathtouchDamage = false;
// regeneration // regeneration
private List<CardShields> shields = Lists.newArrayList(); private FCollection<Card> shields = new FCollection<>();
private int regeneratedThisTurn = 0; private int regeneratedThisTurn = 0;
private int turnInZone; private int turnInZone;
@@ -2275,22 +2275,20 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
// shield = regeneration // shield = regeneration
public final Iterable<CardShields> getShields() { public final Iterable<Card> getShields() {
return shields; return shields;
} }
public final int getShieldCount() { public final int getShieldCount() {
return shields.size(); return shields.size();
} }
public final void addShield(final CardShields shield) { public final void addShield(final Card shield) {
shields.add(shield); if (shields.add(shield)) {
view.updateShieldCount(this); view.updateShieldCount(this);
}
} }
public final void subtractShield(CardShields shield) { public final void subtractShield(final Card shield) {
if (shield != null && shield.hasTrigger()) {
getGame().getStack().addSimultaneousStackEntry(shield.getTriggerSA());
}
if (shields.remove(shield)) { if (shields.remove(shield)) {
view.updateShieldCount(this); view.updateShieldCount(this);
} }
@@ -5241,6 +5239,7 @@ public class Card extends GameEntity implements Comparable<Card> {
resetDealtDamageToPlayerThisTurn(); resetDealtDamageToPlayerThisTurn();
getDamageHistory().newTurn(); getDamageHistory().newTurn();
setRegeneratedThisTurn(0); setRegeneratedThisTurn(0);
resetShield();
setBecameTargetThisTurn(false); setBecameTargetThisTurn(false);
clearMustAttackEntity(turn); clearMustAttackEntity(turn);
clearMustBlockCards(); clearMustBlockCards();

View File

@@ -3346,6 +3346,15 @@ public class CardFactoryUtil {
final ReplacementEffect re = makeEtbCounter(sb.toString(), card, intrinsic); 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); inst.addReplacement(re);
} }

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package forge.game.card;
import forge.game.spellability.SpellAbility;
/**
* <p>
* Card_Shields class.
* </p>
*
* @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;
}
}

View File

@@ -196,7 +196,6 @@ public abstract class PlayerController {
public abstract boolean confirmPayment(CostPart costPart, String string, SpellAbility sa); public abstract boolean confirmPayment(CostPart costPart, String string, SpellAbility sa);
public abstract ReplacementEffect chooseSingleReplacementEffect(String prompt, List<ReplacementEffect> possibleReplacers, Map<String, Object> runParams); public abstract ReplacementEffect chooseSingleReplacementEffect(String prompt, List<ReplacementEffect> possibleReplacers, Map<String, Object> runParams);
public abstract String chooseProtectionType(String string, SpellAbility sa, List<String> choices); public abstract String chooseProtectionType(String string, SpellAbility sa, List<String> choices);
public abstract CardShields chooseRegenerationShield(Card c);
// these 4 need some refining. // these 4 need some refining.
public abstract boolean payCostToPreventEffect(Cost cost, SpellAbility sa, boolean alreadyPaid, FCollectionView<Player> allPayers); public abstract boolean payCostToPreventEffect(Cost cost, SpellAbility sa, boolean alreadyPaid, FCollectionView<Player> allPayers);

View File

@@ -46,18 +46,35 @@ public class ReplaceDestroy extends ReplacementEffect {
if (!runParams.get("Event").equals("Destroy")) { if (!runParams.get("Event").equals("Destroy")) {
return false; return false;
} }
if (this.getMapParams().containsKey("ValidPlayer")) { if (hasParam("ValidPlayer")) {
if (!matchesValid(runParams.get("Affected"), this.getMapParams().get("ValidPlayer").split(","), this.getHostCard())) { if (!matchesValid(runParams.get("Affected"), getParam("ValidPlayer").split(","), getHostCard())) {
return false; return false;
} }
} }
if (this.getMapParams().containsKey("ValidCard")) { if (hasParam("ValidCard")) {
if (!matchesValid(runParams.get("Card"), this.getMapParams().get("ValidCard").split(","), this.getHostCard())) { Card card = (Card)runParams.get("Card");
if (!matchesValid(card, getParam("ValidCard").split(","), getHostCard())) {
return false; 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 (hasParam("ValidSource")) {
if (!matchesValid(runParams.get("Source"), this.getMapParams().get("ValidSource").split(","), this.getHostCard())) { if (!matchesValid(runParams.get("Source"), getParam("ValidSource").split(","), getHostCard())) {
return false; return false;
} }
} }

View File

@@ -17,6 +17,8 @@
*/ */
package forge.game.trigger; package forge.game.trigger;
import java.util.Map;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -42,22 +44,20 @@ public class TriggerDestroyed extends Trigger {
* @param intrinsic * @param intrinsic
* the intrinsic * the intrinsic
*/ */
public TriggerDestroyed(final java.util.Map<String, String> params, final Card host, final boolean intrinsic) { public TriggerDestroyed(final Map<String, String> params, final Card host, final boolean intrinsic) {
super(params, host, intrinsic); super(params, host, intrinsic);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final boolean performTest(final java.util.Map<String, Object> runParams2) { public final boolean performTest(final Map<String, Object> runParams2) {
if (this.mapParams.containsKey("ValidCauser")) { if (hasParam("ValidCauser")) {
if (!matchesValid(runParams2.get("Causer"), this.mapParams.get("ValidCauser").split(","), if (!matchesValid(runParams2.get("Causer"), getParam("ValidCauser").split(","), getHostCard())) {
this.getHostCard())) {
return false; return false;
} }
} }
if (this.mapParams.containsKey("ValidCard")) { if (hasParam("ValidCard")) {
if (!matchesValid(runParams2.get("Card"), this.mapParams.get("ValidCard").split(","), if (!matchesValid(runParams2.get("Card"), getParam("ValidCard").split(","), getHostCard())) {
this.getHostCard())) {
return false; return false;
} }
} }

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package forge.game.trigger;
import java.util.Map;
import forge.game.card.Card;
import forge.game.spellability.SpellAbility;
/**
* <p>
* Trigger_Destroyed class.
* </p>
*
* @author Forge
* @version $Id: TriggerDestroyed.java 17802 2012-10-31 08:05:14Z Max mtg $
*/
public class TriggerRegenerated extends Trigger {
/**
* <p>
* Constructor for Trigger_Destroyed.
* </p>
*
* @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<String, String> params, final Card host, final boolean intrinsic) {
super(params, host, intrinsic);
}
/** {@inheritDoc} */
@Override
public final boolean performTest(final Map<String, Object> 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();
}
}

View File

@@ -67,6 +67,7 @@ public enum TriggerType {
PlanarDice(TriggerPlanarDice.class), PlanarDice(TriggerPlanarDice.class),
PlaneswalkedFrom(TriggerPlaneswalkedFrom.class), PlaneswalkedFrom(TriggerPlaneswalkedFrom.class),
PlaneswalkedTo(TriggerPlaneswalkedTo.class), PlaneswalkedTo(TriggerPlaneswalkedTo.class),
Regenerated(TriggerRegenerated.class),
Revealed(TriggerRevealed.class), Revealed(TriggerRevealed.class),
Sacrificed(TriggerSacrificed.class), Sacrificed(TriggerSacrificed.class),
Scry(TriggerScry.class), Scry(TriggerScry.class),

View File

@@ -566,11 +566,6 @@ public class PlayerControllerForTests extends PlayerController {
// test this! // test this!
} }
@Override
public CardShields chooseRegenerationShield(Card c) {
return Iterables.getFirst(c.getShields(), null);
}
@Override @Override
public List<PaperCard> chooseCardsYouWonToAddToDeck(List<PaperCard> losses) { public List<PaperCard> chooseCardsYouWonToAddToDeck(List<PaperCard> losses) {
// TODO Auto-generated method stub // TODO Auto-generated method stub

View File

@@ -1,8 +1,8 @@
Name:Debt of Loyalty Name:Debt of Loyalty
ManaCost:1 W W ManaCost:1 W W
Types:Instant 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. 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$ CardUID_Targeted | NewController$ You | SpellDescription$ Source controller gains control of CARDNAME 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:RemAIDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/debt_of_loyalty.jpg 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. Oracle:Regenerate target creature. You gain control of that creature if it regenerates this way.

View File

@@ -2,7 +2,8 @@ Name:Matopi Golem
ManaCost:5 ManaCost:5
Types:Artifact Creature Golem Types:Artifact Creature Golem
PT:3/3 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. 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 | CounterType$ M1M1 | CounterNum$ 1 | SpellDescription$ 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 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. Oracle:{1}: Regenerate Matopi Golem. When it regenerates this way, put a -1/-1 counter on it.

View File

@@ -3,8 +3,9 @@ ManaCost:2 B
Types:Creature Skeleton Types:Creature Skeleton
PT:0/0 PT:0/0
K:etbCounter:P1P1:1 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: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 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. 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.

View File

@@ -2,7 +2,7 @@ Name:Soldevi Sentry
ManaCost:1 ManaCost:1
Types:Artifact Creature Soldier Types:Artifact Creature Soldier
PT:1/1 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. 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$ PlayerNamed_Targeted | NumCards$ 1 | OptionalDecider$ True | SpellDescription$ 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 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. Oracle:{1}: Choose target opponent. Regenerate Soldevi Sentry. When it regenerates this way, that player may draw a card.

View File

@@ -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<CardShields> shields = Lists.newArrayList();
for (final CardShields shield : c.getShields()) {
shields.add(shield);
}
return getGui().one("Choose a regeneration shield:", shields);
}
@Override @Override
public List<PaperCard> chooseCardsYouWonToAddToDeck(final List<PaperCard> losses) { public List<PaperCard> chooseCardsYouWonToAddToDeck(final List<PaperCard> losses) {
return getGui().many("Select cards to add to your deck", "Add these to my deck", 0, losses.size(), losses, return getGui().many("Select cards to add to your deck", "Add these to my deck", 0, losses.size(), losses,