Regenerate rework (#3385)

* CardFactoryUtil: remove false keyword

* CardFactoryUtil: remove wrong regenerate Keyword

* CantRegenerate as Static

* ~small fixes

* RegenerationAbility as SubAbility to the Regeneration Effect

* Card: use shieldCount instead of Collection

* remove deprecated trigger

* fix AiLogic vs AILogic

* EffectAi: start of logic for CantRegenerate

* EffectAi: try to do StackPeek for CantRegenerate

* ~ use wither damage

* AI prediction against Damage

* CantRegenerate: begin logic against Combat Damage

* AnimateAi: start logic for Bone Shaman

* fix Runesword
This commit is contained in:
Hans Mackowiak
2023-07-29 09:58:14 +02:00
committed by GitHub
parent 7bf4b5c126
commit c15542f949
74 changed files with 658 additions and 496 deletions

View File

@@ -63,6 +63,7 @@ public final class AbilityFactory {
"ChooseSubAbility", // Can choose a player via ChoosePlayer
"CantChooseSubAbility", // Can't choose a player via ChoosePlayer
"AnimateSubAbility", // For ChangeZone Effects to Animate before ETB
"RegenerationAbility", // for Regeneration Effect
"ReturnAbility" // for Delayed Trigger on Magpie
);

View File

@@ -38,6 +38,8 @@ public class FogEffect extends SpellAbilityEffect {
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
game.getEndOfTurn().addUntil(new GameCommand() {
private static final long serialVersionUID = -3297629217432253089L;
@Override
public void run() {
game.getAction().exile(eff, null);

View File

@@ -2,7 +2,6 @@ package forge.game.ability.effects;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
@@ -28,11 +27,8 @@ public class RegenerateAllEffect extends RegenerateBaseEffect {
final Game game = hostCard.getGame();
final String valid = sa.getParamOrDefault("ValidCards", "");
CardCollectionView list = game.getCardsIn(ZoneType.Battlefield);
list = CardLists.getValidCards(list, valid, hostCard.getController(), hostCard, sa);
// create Effect for Regeneration
createRegenerationEffect(sa, list);
createRegenerationEffect(sa, CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), valid, hostCard.getController(), hostCard, sa));
}
}

View File

@@ -1,5 +1,7 @@
package forge.game.ability.effects;
import java.util.Collection;
import forge.GameCommand;
import forge.game.Game;
import forge.game.ability.AbilityFactory;
@@ -11,14 +13,15 @@ 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 createRegenerationEffect(SpellAbility sa, final Iterable<Card> list) {
public void createRegenerationEffect(SpellAbility sa, final Collection<Card> list) {
if (list.isEmpty()) {
return;
}
final Card hostCard = sa.getHostCard();
final Game game = hostCard.getGame();
@@ -41,6 +44,11 @@ public abstract class RegenerateBaseEffect extends SpellAbilityEffect {
SpellAbility saReg = AbilityFactory.getAbility(effect, eff);
AbilitySub saExile = (AbilitySub)AbilityFactory.getAbility(exileEff, eff);
if (sa.hasAdditionalAbility("RegenerationAbility")) {
AbilitySub trigSA = (AbilitySub)sa.getAdditionalAbility("RegenerationAbility").copy(eff, sa.getActivatingPlayer(), false);
saExile.setSubAbility(trigSA);
}
saReg.setSubAbility(saExile);
re.setOverridingAbility(saReg);
eff.addReplacementEffect(re);
@@ -50,18 +58,6 @@ public abstract class RegenerateBaseEffect extends SpellAbilityEffect {
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.addTrigger(trigger);
}
// Copy text changes
if (sa.isIntrinsic()) {
eff.copyChangedTextFrom(hostCard);
@@ -69,7 +65,7 @@ public abstract class RegenerateBaseEffect extends SpellAbilityEffect {
// add RegenEffect as Shield to the Affected Cards
for (final Card c : list) {
c.addShield(eff);
c.incShieldCount();
}
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
game.getAction().moveTo(ZoneType.Command, eff, sa, AbilityKey.newMap());

View File

@@ -1,10 +1,12 @@
package forge.game.ability.effects;
import java.util.Iterator;
import java.util.List;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.spellability.SpellAbility;
import forge.util.Lang;
public class RegenerateEffect extends RegenerateBaseEffect {
@@ -15,26 +17,13 @@ public class RegenerateEffect extends RegenerateBaseEffect {
@Override
protected String getStackDescription(SpellAbility sa) {
final StringBuilder sb = new StringBuilder();
final List<Card> tgtCards = getTargetCards(sa);
final List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
if (tgtCards.size() > 0) {
if (!tgtCards.isEmpty()) {
sb.append("Regenerate ");
final Iterator<Card> it = tgtCards.iterator();
while (it.hasNext()) {
final Card tgtC = it.next();
if (tgtC.isFaceDown()) {
sb.append("Morph");
} else {
sb.append(tgtC);
}
if (it.hasNext()) {
sb.append(", ");
}
}
sb.append(Lang.joinHomogenous(tgtCards));
sb.append(".");
}
sb.append(".");
return sb.toString();
}
@@ -45,8 +34,26 @@ public class RegenerateEffect extends RegenerateBaseEffect {
*/
@Override
public void resolve(SpellAbility sa) {
final Game game = sa.getHostCard().getGame();
CardCollection result = new CardCollection();
for (Card c : getDefinedCardsOrTargeted(sa)) {
if (!c.isInPlay()) {
continue;
}
// check if the object is still in game or if it was moved
Card gameCard = game.getCardState(c, null);
// gameCard is LKI in that case, the card is not in game anymore
// or the timestamp did change
// this should check Self too
if (gameCard == null || !c.equalsWithTimestamp(gameCard)) {
continue;
}
result.add(gameCard);
}
// create Effect for Regeneration
createRegenerationEffect(sa, getDefinedCardsOrTargeted(sa));
createRegenerationEffect(sa, result);
}
}

View File

@@ -1,14 +1,10 @@
package forge.game.ability.effects;
import java.util.Map;
import forge.game.Game;
import forge.game.ability.AbilityKey;
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;
public class RegenerationEffect extends SpellAbilityEffect {
@@ -21,9 +17,7 @@ public class RegenerationEffect extends SpellAbilityEffect {
final Card host = sa.getHostCard();
final Game game = host.getGame();
for (Card c : getTargetCards(sa)) {
if (!c.canBeShielded() || !c.isInPlay()) {
continue;
}
// checks already done in ReplacementEffect
c.setDamage(0);
c.setHasBeenDealtDeathtouchDamage(false);
@@ -39,14 +33,9 @@ public class RegenerationEffect extends SpellAbilityEffect {
game.fireEvent(new GameEventCardRegenerated(c));
if (host.isImmutable()) {
c.subtractShield(host);
c.decShieldCount();
host.removeRemembered(c);
}
// Run triggers
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
runParams.put(AbilityKey.Cause, host);
game.getTriggerHandler().runTrigger(TriggerType.Regenerated, runParams, false);
}
}

View File

@@ -5,7 +5,6 @@ import java.util.List;
import org.apache.commons.lang3.StringUtils;
import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
@@ -14,7 +13,6 @@ import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
@@ -83,7 +81,6 @@ public class RevealEffect extends SpellAbilityEffect {
game.getAction().reveal(revealed, p);
}
for (final Card c : revealed) {
game.getTriggerHandler().runTrigger(TriggerType.Revealed, AbilityKey.mapFromCard(c), false);
if (sa.hasParam("RememberRevealed")) {
host.addRemembered(c);
}

View File

@@ -268,7 +268,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
private boolean hasBeenDealtExcessDamageThisTurn;
// regeneration
private FCollection<Card> shields = new FCollection<>();
private int shieldCount = 0;
private int regeneratedThisTurn;
private int turnInZone;
@@ -3230,28 +3230,22 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
}
// shield = regeneration
public final Iterable<Card> getShields() {
return shields;
}
public final int getShieldCount() {
return shields.size();
return shieldCount;
}
public final void addShield(final Card shield) {
if (shields.add(shield)) {
view.updateShieldCount(this);
}
public final void incShieldCount() {
shieldCount++;
view.updateShieldCount(this);
}
public final void subtractShield(final Card shield) {
if (shields.remove(shield)) {
view.updateShieldCount(this);
}
public final void decShieldCount() {
shieldCount--;
view.updateShieldCount(this);
}
public final void resetShield() {
if (shields.isEmpty()) { return; }
shields.clear();
public final void resetShieldCount() {
shieldCount = 0;
view.updateShieldCount(this);
}
@@ -3267,7 +3261,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
}
public final boolean canBeShielded() {
return !hasKeyword("CARDNAME can't be regenerated.");
return !StaticAbilityCantRegenerate.cantRegenerate(this);
}
// is this "Card" supposed to be a token?
@@ -6597,7 +6591,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
setHasBeenDealtDeathtouchDamage(false);
setHasBeenDealtExcessDamageThisTurn(false);
setRegeneratedThisTurn(0);
resetShield();
resetShieldCount();
setBecameTargetThisTurn(false);
setFoughtThisTurn(false);
clearMustBlockCards();

View File

@@ -2574,15 +2574,6 @@ 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, host, intrinsic, card);
SpellAbility sa = AbilityFactory.getAbility(effect, card);
re.setOverridingAbility(sa);
inst.addReplacement(re);
}
@@ -2639,30 +2630,6 @@ public class CardFactoryUtil {
ReplacementEffect re = ReplacementHandler.parseReplacement(rep, host, intrinsic, card);
inst.addReplacement(re);
}
else if (keyword.startsWith("If CARDNAME would be put into a graveyard "
+ "from anywhere, reveal CARDNAME and shuffle it into its owner's library instead.")) {
StringBuilder sb = new StringBuilder("Event$ Moved | Destination$ Graveyard | ValidCard$ Card.Self ");
// to show it on Nexus
if (host.isPermanent()) {
sb.append("| Secondary$ True");
}
sb.append("| Description$ ").append(keyword);
String ab = "DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Library | Defined$ ReplacedCard | Reveal$ True | Shuffle$ True";
SpellAbility sa = AbilityFactory.getAbility(ab, card);
if (!intrinsic) {
sa.setIntrinsic(false);
}
ReplacementEffect re = ReplacementHandler.parseReplacement(sb.toString(), host, intrinsic, card);
re.setOverridingAbility(sa);
inst.addReplacement(re);
}
if (keyword.equals("CARDNAME enters the battlefield tapped.")) {
String effect = "DB$ Tap | Defined$ Self | ETB$ True "

View File

@@ -0,0 +1,33 @@
package forge.game.staticability;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.zone.ZoneType;
public class StaticAbilityCantRegenerate {
static String MODE = "CantRegenerate";
public static boolean cantRegenerate(final Card card) {
final Game game = card.getGame();
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
for (final StaticAbility stAb : ca.getStaticAbilities()) {
if (!stAb.checkConditions(MODE)) {
continue;
}
if (applyCantRegenerateAbility(stAb, card)) {
return true;
}
}
}
return false;
}
public static boolean applyCantRegenerateAbility(final StaticAbility stAb, final Card card) {
if (!stAb.matchesValidParam("ValidCard", card)) {
return false;
}
return true;
}
}

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.trigger;
import java.util.Map;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.spellability.SpellAbility;
import forge.util.Localizer;
/**
* <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}
* @param runParams*/
@Override
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
if (!matchesValidParam("ValidCause", runParams.get(AbilityKey.Cause))) {
return false;
}
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Card))) {
return false;
}
return true;
}
/** {@inheritDoc} */
@Override
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Card, AbilityKey.Cause);
}
@Override
public String getImportantStackObjects(SpellAbility sa) {
StringBuilder sb = new StringBuilder();
sb.append(Localizer.getInstance().getMessage("lblRegenerated")).append(": ").append(sa.getTriggeringObject(AbilityKey.Card));
//sb.append("Destroyer: ").append(sa.getTriggeringObject("Causer"));
return sb.toString();
}
}

View File

@@ -1,37 +0,0 @@
package forge.game.trigger;
import java.util.Map;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.spellability.SpellAbility;
import forge.util.Localizer;
@Deprecated
public class TriggerRevealed extends Trigger {
public TriggerRevealed(Map<String, String> params, Card host, boolean intrinsic) {
super(params, host, intrinsic);
}
@Override
public boolean performTest(Map<AbilityKey, Object> runParams) {
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Card))) {
return false;
}
return true;
}
@Override
public void setTriggeringObjects(SpellAbility sa, Map<AbilityKey, Object> runParams) {
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Card);
}
@Override
public String getImportantStackObjects(SpellAbility sa) {
StringBuilder sb = new StringBuilder();
sb.append(Localizer.getInstance().getMessage("lblRevealed")).append(": ").append(sa.getTriggeringObject(AbilityKey.Card));
return sb.toString();
}
}

View File

@@ -1,184 +1,182 @@
package forge.game.trigger;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import forge.game.card.Card;
/**
* TODO: Write javadoc for this type.
*
*/
public enum TriggerType {
Abandoned(TriggerAbandoned.class),
AbilityCast(TriggerSpellAbilityCastOrCopy.class),
AbilityResolves(TriggerAbilityResolves.class),
AbilityTriggered(TriggerAbilityTriggered.class),
Adapt(TriggerAdapt.class),
Always(TriggerAlways.class),
Attached(TriggerAttached.class),
AttackerBlocked(TriggerAttackerBlocked.class),
AttackerBlockedOnce(TriggerAttackerBlockedOnce.class),
AttackerBlockedByCreature(TriggerAttackerBlockedByCreature.class),
AttackersDeclared(TriggerAttackersDeclared.class),
AttackersDeclaredOneTarget(TriggerAttackersDeclared.class),
AttackerUnblocked(TriggerAttackerUnblocked.class),
AttackerUnblockedOnce(TriggerAttackerUnblockedOnce.class),
Attacks(TriggerAttacks.class),
BecomeMonarch(TriggerBecomeMonarch.class),
BecomeMonstrous(TriggerBecomeMonstrous.class),
BecomeRenowned(TriggerBecomeRenowned.class),
BecomesCrewed(TriggerBecomesCrewed.class),
BecomesTarget(TriggerBecomesTarget.class),
BecomesTargetOnce(TriggerBecomesTargetOnce.class),
BlockersDeclared(TriggerBlockersDeclared.class),
Blocks(TriggerBlocks.class),
Championed(TriggerChampioned.class),
ChangesController(TriggerChangesController.class),
ChangesZone(TriggerChangesZone.class),
ChangesZoneAll(TriggerChangesZoneAll.class),
ChaosEnsues(TriggerChaosEnsues.class),
Clashed(TriggerClashed.class),
ClassLevelGained(TriggerClassLevelGained.class),
ConjureAll(TriggerConjureAll.class),
CounterAdded(TriggerCounterAdded.class),
CounterAddedOnce(TriggerCounterAddedOnce.class),
CounterPlayerAddedAll(TriggerCounterPlayerAddedAll.class),
CounterAddedAll(TriggerCounterAddedAll.class),
Countered(TriggerCountered.class),
CounterRemoved(TriggerCounterRemoved.class),
CounterRemovedOnce(TriggerCounterRemovedOnce.class),
Crewed(TriggerCrewed.class),
Cycled(TriggerCycled.class),
DamageAll(TriggerDamageAll.class),
DamageDealtOnce(TriggerDamageDealtOnce.class),
DamageDone(TriggerDamageDone.class),
DamageDoneOnce(TriggerDamageDoneOnce.class),
DamageDoneOnceByController(TriggerDamageDoneOnceByController.class),
DamagePrevented(TriggerDamagePrevented.class),
DamagePreventedOnce(TriggerDamagePreventedOnce.class),
DayTimeChanges (TriggerDayTimeChanges.class),
Destroyed(TriggerDestroyed.class),
Devoured(TriggerDevoured.class),
Discarded(TriggerDiscarded.class),
DiscardedAll(TriggerDiscardedAll.class),
Drawn(TriggerDrawn.class),
DungeonCompleted(TriggerCompletedDungeon.class),
Evolved(TriggerEvolved.class),
ExcessDamage(TriggerExcessDamage.class),
Enlisted(TriggerEnlisted.class),
Exerted(TriggerExerted.class),
Exiled(TriggerExiled.class),
Exploited(TriggerExploited.class),
Explores(TriggerExplores.class),
Fight(TriggerFight.class),
FightOnce(TriggerFightOnce.class),
FlippedCoin(TriggerFlippedCoin.class),
Foretell(TriggerForetell.class),
Immediate(TriggerImmediate.class),
Investigated(TriggerInvestigated.class),
IsForetold(TriggerIsForetold.class),
LandPlayed(TriggerLandPlayed.class),
LifeGained(TriggerLifeGained.class),
LifeLost(TriggerLifeLost.class),
LifeLostAll(TriggerLifeLostAll.class),
LosesGame(TriggerLosesGame.class),
ManaAdded(TriggerManaAdded.class),
MilledAll(TriggerMilledAll.class),
Mutates(TriggerMutates.class),
NewGame(TriggerNewGame.class),
PayCumulativeUpkeep(TriggerPayCumulativeUpkeep.class),
PayEcho(TriggerPayEcho.class),
PayLife(TriggerPayLife.class),
Phase(TriggerPhase.class),
PhaseIn(TriggerPhaseIn.class),
PhaseOut(TriggerPhaseOut.class),
PlanarDice(TriggerPlanarDice.class),
PlaneswalkedFrom(TriggerPlaneswalkedFrom.class),
PlaneswalkedTo(TriggerPlaneswalkedTo.class),
Proliferate(TriggerProliferate.class),
Regenerated(TriggerRegenerated.class),
Revealed(TriggerRevealed.class),
RingTemptsYou(TriggerRingTemptsYou.class),
RolledDie(TriggerRolledDie.class),
RolledDieOnce(TriggerRolledDieOnce.class),
RoomEntered(TriggerEnteredRoom.class),
Sacrificed(TriggerSacrificed.class),
Scry(TriggerScry.class),
SearchedLibrary(TriggerSearchedLibrary.class),
SeekAll(TriggerSeekAll.class),
SetInMotion(TriggerSetInMotion.class),
Shuffled(TriggerShuffled.class),
Specializes(TriggerSpecializes.class),
SpellAbilityCast(TriggerSpellAbilityCastOrCopy.class),
SpellAbilityCopy(TriggerSpellAbilityCastOrCopy.class),
SpellCast(TriggerSpellAbilityCastOrCopy.class),
SpellCastOrCopy(TriggerSpellAbilityCastOrCopy.class),
SpellCopy(TriggerSpellAbilityCastOrCopy.class),
Surveil(TriggerSurveil.class),
TakesInitiative(TriggerTakesInitiative.class),
Taps(TriggerTaps.class),
TapsForMana(TriggerTapsForMana.class),
TokenCreated(TriggerTokenCreated.class),
TokenCreatedOnce(TriggerTokenCreatedOnce.class),
Trains(TriggerTrains.class),
Transformed(TriggerTransformed.class),
TurnBegin(TriggerTurnBegin.class),
TurnFaceUp(TriggerTurnFaceUp.class),
Unattach(TriggerUnattach.class),
Untaps(TriggerUntaps.class),
Vote(TriggerVote.class);
private final Constructor<? extends Trigger> constructor;
TriggerType(Class<? extends Trigger> clasz) {
constructor = findConstructor(clasz);
}
private static Constructor<? extends Trigger> findConstructor(Class<? extends Trigger> clasz) {
@SuppressWarnings("unchecked")
Constructor<? extends Trigger>[] cc = (Constructor<? extends Trigger>[]) clasz.getDeclaredConstructors();
for (Constructor<? extends Trigger> c : cc) {
Class<?>[] pp = c.getParameterTypes();
if (pp[0].isAssignableFrom(Map.class)) {
return c;
}
}
throw new RuntimeException("No constructor found that would take Map as 1st parameter in class " + clasz.getName());
}
/**
* TODO: Write javadoc for this method.
* @param value
* @return
*/
public static TriggerType smartValueOf(String value) {
final String valToCompate = value.trim();
for (final TriggerType v : TriggerType.values()) {
if (v.name().compareToIgnoreCase(valToCompate) == 0) {
return v;
}
}
throw new RuntimeException("Element " + value + " not found in TriggerType enum");
}
/**
* TODO: Write javadoc for this method.
* @param mapParams
* @param host
* @param intrinsic
* @return
*/
public Trigger createTrigger(Map<String, String> mapParams, Card host, boolean intrinsic) {
try {
Trigger res = constructor.newInstance(mapParams, host, intrinsic);
res.setMode(this);
return res;
} catch (IllegalArgumentException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
package forge.game.trigger;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import forge.game.card.Card;
/**
* TODO: Write javadoc for this type.
*
*/
public enum TriggerType {
Abandoned(TriggerAbandoned.class),
AbilityCast(TriggerSpellAbilityCastOrCopy.class),
AbilityResolves(TriggerAbilityResolves.class),
AbilityTriggered(TriggerAbilityTriggered.class),
Adapt(TriggerAdapt.class),
Always(TriggerAlways.class),
Attached(TriggerAttached.class),
AttackerBlocked(TriggerAttackerBlocked.class),
AttackerBlockedOnce(TriggerAttackerBlockedOnce.class),
AttackerBlockedByCreature(TriggerAttackerBlockedByCreature.class),
AttackersDeclared(TriggerAttackersDeclared.class),
AttackersDeclaredOneTarget(TriggerAttackersDeclared.class),
AttackerUnblocked(TriggerAttackerUnblocked.class),
AttackerUnblockedOnce(TriggerAttackerUnblockedOnce.class),
Attacks(TriggerAttacks.class),
BecomeMonarch(TriggerBecomeMonarch.class),
BecomeMonstrous(TriggerBecomeMonstrous.class),
BecomeRenowned(TriggerBecomeRenowned.class),
BecomesCrewed(TriggerBecomesCrewed.class),
BecomesTarget(TriggerBecomesTarget.class),
BecomesTargetOnce(TriggerBecomesTargetOnce.class),
BlockersDeclared(TriggerBlockersDeclared.class),
Blocks(TriggerBlocks.class),
Championed(TriggerChampioned.class),
ChangesController(TriggerChangesController.class),
ChangesZone(TriggerChangesZone.class),
ChangesZoneAll(TriggerChangesZoneAll.class),
ChaosEnsues(TriggerChaosEnsues.class),
Clashed(TriggerClashed.class),
ClassLevelGained(TriggerClassLevelGained.class),
ConjureAll(TriggerConjureAll.class),
CounterAdded(TriggerCounterAdded.class),
CounterAddedOnce(TriggerCounterAddedOnce.class),
CounterPlayerAddedAll(TriggerCounterPlayerAddedAll.class),
CounterAddedAll(TriggerCounterAddedAll.class),
Countered(TriggerCountered.class),
CounterRemoved(TriggerCounterRemoved.class),
CounterRemovedOnce(TriggerCounterRemovedOnce.class),
Crewed(TriggerCrewed.class),
Cycled(TriggerCycled.class),
DamageAll(TriggerDamageAll.class),
DamageDealtOnce(TriggerDamageDealtOnce.class),
DamageDone(TriggerDamageDone.class),
DamageDoneOnce(TriggerDamageDoneOnce.class),
DamageDoneOnceByController(TriggerDamageDoneOnceByController.class),
DamagePrevented(TriggerDamagePrevented.class),
DamagePreventedOnce(TriggerDamagePreventedOnce.class),
DayTimeChanges (TriggerDayTimeChanges.class),
Destroyed(TriggerDestroyed.class),
Devoured(TriggerDevoured.class),
Discarded(TriggerDiscarded.class),
DiscardedAll(TriggerDiscardedAll.class),
Drawn(TriggerDrawn.class),
DungeonCompleted(TriggerCompletedDungeon.class),
Evolved(TriggerEvolved.class),
ExcessDamage(TriggerExcessDamage.class),
Enlisted(TriggerEnlisted.class),
Exerted(TriggerExerted.class),
Exiled(TriggerExiled.class),
Exploited(TriggerExploited.class),
Explores(TriggerExplores.class),
Fight(TriggerFight.class),
FightOnce(TriggerFightOnce.class),
FlippedCoin(TriggerFlippedCoin.class),
Foretell(TriggerForetell.class),
Immediate(TriggerImmediate.class),
Investigated(TriggerInvestigated.class),
IsForetold(TriggerIsForetold.class),
LandPlayed(TriggerLandPlayed.class),
LifeGained(TriggerLifeGained.class),
LifeLost(TriggerLifeLost.class),
LifeLostAll(TriggerLifeLostAll.class),
LosesGame(TriggerLosesGame.class),
ManaAdded(TriggerManaAdded.class),
MilledAll(TriggerMilledAll.class),
Mutates(TriggerMutates.class),
NewGame(TriggerNewGame.class),
PayCumulativeUpkeep(TriggerPayCumulativeUpkeep.class),
PayEcho(TriggerPayEcho.class),
PayLife(TriggerPayLife.class),
Phase(TriggerPhase.class),
PhaseIn(TriggerPhaseIn.class),
PhaseOut(TriggerPhaseOut.class),
PlanarDice(TriggerPlanarDice.class),
PlaneswalkedFrom(TriggerPlaneswalkedFrom.class),
PlaneswalkedTo(TriggerPlaneswalkedTo.class),
Proliferate(TriggerProliferate.class),
RingTemptsYou(TriggerRingTemptsYou.class),
RolledDie(TriggerRolledDie.class),
RolledDieOnce(TriggerRolledDieOnce.class),
RoomEntered(TriggerEnteredRoom.class),
Sacrificed(TriggerSacrificed.class),
Scry(TriggerScry.class),
SearchedLibrary(TriggerSearchedLibrary.class),
SeekAll(TriggerSeekAll.class),
SetInMotion(TriggerSetInMotion.class),
Shuffled(TriggerShuffled.class),
Specializes(TriggerSpecializes.class),
SpellAbilityCast(TriggerSpellAbilityCastOrCopy.class),
SpellAbilityCopy(TriggerSpellAbilityCastOrCopy.class),
SpellCast(TriggerSpellAbilityCastOrCopy.class),
SpellCastOrCopy(TriggerSpellAbilityCastOrCopy.class),
SpellCopy(TriggerSpellAbilityCastOrCopy.class),
Surveil(TriggerSurveil.class),
TakesInitiative(TriggerTakesInitiative.class),
Taps(TriggerTaps.class),
TapsForMana(TriggerTapsForMana.class),
TokenCreated(TriggerTokenCreated.class),
TokenCreatedOnce(TriggerTokenCreatedOnce.class),
Trains(TriggerTrains.class),
Transformed(TriggerTransformed.class),
TurnBegin(TriggerTurnBegin.class),
TurnFaceUp(TriggerTurnFaceUp.class),
Unattach(TriggerUnattach.class),
Untaps(TriggerUntaps.class),
Vote(TriggerVote.class);
private final Constructor<? extends Trigger> constructor;
TriggerType(Class<? extends Trigger> clasz) {
constructor = findConstructor(clasz);
}
private static Constructor<? extends Trigger> findConstructor(Class<? extends Trigger> clasz) {
@SuppressWarnings("unchecked")
Constructor<? extends Trigger>[] cc = (Constructor<? extends Trigger>[]) clasz.getDeclaredConstructors();
for (Constructor<? extends Trigger> c : cc) {
Class<?>[] pp = c.getParameterTypes();
if (pp[0].isAssignableFrom(Map.class)) {
return c;
}
}
throw new RuntimeException("No constructor found that would take Map as 1st parameter in class " + clasz.getName());
}
/**
* TODO: Write javadoc for this method.
* @param value
* @return
*/
public static TriggerType smartValueOf(String value) {
final String valToCompate = value.trim();
for (final TriggerType v : TriggerType.values()) {
if (v.name().compareToIgnoreCase(valToCompate) == 0) {
return v;
}
}
throw new RuntimeException("Element " + value + " not found in TriggerType enum");
}
/**
* TODO: Write javadoc for this method.
* @param mapParams
* @param host
* @param intrinsic
* @return
*/
public Trigger createTrigger(Map<String, String> mapParams, Card host, boolean intrinsic) {
try {
Trigger res = constructor.newInstance(mapParams, host, intrinsic);
res.setMode(this);
return res;
} catch (IllegalArgumentException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -66,6 +66,10 @@ public class WrappedAbility extends Ability {
ApiType.SacrificeAll,
ApiType.Pump,
ApiType.Regenerate, // Updated
ApiType.RegenerateAll, // No Triggered
ApiType.Regeneration, // Replacement Effect only
ApiType.DelayedTrigger
);