mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 20:58:03 +00:00
Illusionary mask
This commit is contained in:
@@ -357,6 +357,15 @@ public abstract class SpellAbilityEffect {
|
||||
addedTrigger.setIntrinsic(true);
|
||||
}
|
||||
|
||||
protected static void addExileOnCounteredTrigger(final Card card) {
|
||||
String trig = "Mode$ Countered | ValidCard$ Card.IsRemembered | TriggerZones$ Command | Static$ True";
|
||||
String effect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile";
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true);
|
||||
parsedTrigger.setOverridingAbility(AbilityFactory.getAbility(effect, card));
|
||||
final Trigger addedTrigger = card.addTrigger(parsedTrigger);
|
||||
addedTrigger.setIntrinsic(true);
|
||||
}
|
||||
|
||||
protected static void addForgetCounterTrigger(final Card card, final String counterType) {
|
||||
String trig = "Mode$ CounterRemoved | TriggerZones$ Command | ValidCard$ Card.IsRemembered | CounterType$ " + counterType + " | NewCounterAmount$ 0 | Static$ True";
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameEntityCounterTable;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
@@ -15,6 +16,7 @@ import forge.game.card.CardDamageMap;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
@@ -128,6 +130,16 @@ public class DamageDealEffect extends DamageBaseEffect {
|
||||
final Card hostCard = sa.getHostCard();
|
||||
final Game game = hostCard.getGame();
|
||||
|
||||
final List<Card> definedSources = AbilityUtils.getDefinedCards(hostCard, sa.getParam("DamageSource"), sa);
|
||||
if (definedSources == null || definedSources.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Card source : definedSources) {
|
||||
// Run replacement effects
|
||||
game.getReplacementHandler().run(ReplacementType.AssignDealDamage, AbilityKey.mapFromAffected(source));
|
||||
}
|
||||
|
||||
final String damage = sa.getParam("NumDmg");
|
||||
int dmg = AbilityUtils.calculateAmount(hostCard, damage, sa);
|
||||
|
||||
@@ -172,11 +184,6 @@ public class DamageDealEffect extends DamageBaseEffect {
|
||||
usedDamageMap = true;
|
||||
}
|
||||
|
||||
final List<Card> definedSources = AbilityUtils.getDefinedCards(hostCard, sa.getParam("DamageSource"), sa);
|
||||
if (definedSources == null || definedSources.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Card source : definedSources) {
|
||||
final Card sourceLKI = hostCard.getGame().getChangeZoneLKIInfo(source);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardDamageMap;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.util.Localizer;
|
||||
@@ -154,6 +155,10 @@ public class FightEffect extends DamageBaseEffect {
|
||||
usedDamageMap = false;
|
||||
}
|
||||
|
||||
// Run replacement effects
|
||||
fighterA.getGame().getReplacementHandler().run(ReplacementType.AssignDealDamage, AbilityKey.mapFromAffected(fighterA));
|
||||
fighterB.getGame().getReplacementHandler().run(ReplacementType.AssignDealDamage, AbilityKey.mapFromAffected(fighterB));
|
||||
|
||||
// 701.12c If a creature fights itself, it deals damage to itself equal to twice its power.
|
||||
|
||||
final int dmg1 = fightToughness ? fighterA.getNetToughness() : fighterA.getNetPower();
|
||||
|
||||
@@ -20,6 +20,7 @@ import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardFactoryUtil;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
@@ -216,8 +217,16 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
tgtCards.remove(original);
|
||||
}
|
||||
|
||||
// only one mode can be used
|
||||
SpellAbility tgtSA = sa.getActivatingPlayer().getController().getAbilityToPlay(tgtCard, sas);
|
||||
SpellAbility tgtSA;
|
||||
|
||||
if (!sa.hasParam("CastFaceDown")) {
|
||||
// only one mode can be used
|
||||
tgtSA = sa.getActivatingPlayer().getController().getAbilityToPlay(tgtCard, sas);
|
||||
} else {
|
||||
// For Illusionary Mask effect
|
||||
tgtSA = CardFactoryUtil.abilityMorphDown(tgtCard);
|
||||
}
|
||||
|
||||
final boolean noManaCost = sa.hasParam("WithoutManaCost");
|
||||
if (noManaCost) {
|
||||
tgtSA = tgtSA.copyWithNoManaCost();
|
||||
@@ -257,7 +266,12 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("ReplaceGraveyard")) {
|
||||
addReplaceGraveyardEffect(tgtCard, sa, sa.getParam("ReplaceGraveyard"));
|
||||
}
|
||||
|
||||
|
||||
// For Illusionary Mask effect
|
||||
if (sa.hasParam("ReplaceIlluMask")) {
|
||||
addIllusionaryMaskReplace(tgtCard, sa);
|
||||
}
|
||||
|
||||
tgtSA.setSVar("IsCastFromPlayEffect", "True");
|
||||
|
||||
if (controller.getController().playSaFromPlayEffect(tgtSA)) {
|
||||
@@ -279,7 +293,7 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
}
|
||||
} // end resolve
|
||||
|
||||
|
||||
|
||||
protected void addReplaceGraveyardEffect(Card c, SpellAbility sa, String zone) {
|
||||
final Card hostCard = sa.getHostCard();
|
||||
final Game game = hostCard.getGame();
|
||||
@@ -326,4 +340,42 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa);
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
}
|
||||
|
||||
|
||||
protected void addIllusionaryMaskReplace(Card c, SpellAbility sa) {
|
||||
final Card hostCard = sa.getHostCard();
|
||||
final Game game = hostCard.getGame();
|
||||
final Player controller = sa.getActivatingPlayer();
|
||||
final String name = hostCard.getName() + "'s Effect";
|
||||
final String image = hostCard.getImageKey();
|
||||
final Card eff = createEffect(sa, controller, name, image);
|
||||
|
||||
eff.addRemembered(c);
|
||||
|
||||
String [] repeffstrs = {
|
||||
"Event$ AssignDealDamage | ValidCard$ Card.IsRemembered+faceDown " +
|
||||
"| Description$ If the creature that spell becomes as it resolves has not been turned face up" +
|
||||
" and would assign or deal damage, be dealt damage, or become tapped, instead it's turned face up" +
|
||||
" and assigns or deals damage, is dealt damage, or becomes tapped.",
|
||||
"Event$ DealtDamage | ValidCard$ Card.IsRemembered+faceDown",
|
||||
"Event$ Tap | ValidCard$ Card.IsRemembered+faceDown"
|
||||
};
|
||||
String effect = "DB$ SetState | Defined$ ReplacedCard | Mode$ TurnFace";
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstrs[i], eff, true);
|
||||
re.setLayer(ReplacementLayer.Other);
|
||||
re.setOverridingAbility(AbilityFactory.getAbility(effect, eff));
|
||||
eff.addReplacementEffect(re);
|
||||
}
|
||||
|
||||
addExileOnMovedTrigger(eff, "Battlefield");
|
||||
addExileOnCounteredTrigger(eff);
|
||||
|
||||
eff.updateStateForView();
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa);
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3677,6 +3677,9 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
public final void tap(boolean attacker) {
|
||||
if (tapped) { return; }
|
||||
|
||||
// Run replacement effects
|
||||
getGame().getReplacementHandler().run(ReplacementType.Tap, AbilityKey.mapFromAffected(this));
|
||||
|
||||
// Run triggers
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(this);
|
||||
runParams.put(AbilityKey.Attacker, attacker);
|
||||
@@ -5096,6 +5099,9 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Run replacement effects
|
||||
getGame().getReplacementHandler().run(ReplacementType.DealtDamage, AbilityKey.mapFromAffected(this));
|
||||
|
||||
addReceivedDamageFromThisTurn(source, damageIn);
|
||||
source.addDealtDamageToThisTurn(this, damageIn);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package forge.game.card;
|
||||
import com.google.common.collect.Iterables;
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaCostShard;
|
||||
import forge.game.Direction;
|
||||
import forge.game.EvenOdd;
|
||||
import forge.game.Game;
|
||||
@@ -14,6 +15,7 @@ import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.combat.AttackingBand;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.mana.Mana;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.OptionalCost;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -24,6 +26,7 @@ import forge.util.TextUtil;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1831,6 +1834,61 @@ public class CardProperty {
|
||||
if (card.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("CanPayManaCost")) {
|
||||
final class CheckCanPayManaCost {
|
||||
private List<Mana> manaPaid;
|
||||
private List<ManaCostShard> manaCost;
|
||||
// check shards recursively
|
||||
boolean checkShard(int index) {
|
||||
if (index >= manaCost.size()) {
|
||||
return true;
|
||||
}
|
||||
ManaCostShard shard = manaCost.get(index);
|
||||
// ignore X cost
|
||||
if (shard == ManaCostShard.X) {
|
||||
return checkShard(index + 1);
|
||||
}
|
||||
for (int i = 0; i < manaPaid.size(); i++) {
|
||||
Mana mana = manaPaid.get(i);
|
||||
if (shard.isColor(mana.getColor()) || (shard.isSnow() && mana.isSnow())) {
|
||||
manaPaid.remove(i);
|
||||
if (checkShard(index + 1)) {
|
||||
return true;
|
||||
}
|
||||
manaPaid.add(i, mana);
|
||||
}
|
||||
if (shard.isGeneric() && !shard.isSnow()) {
|
||||
// Handle 2 generic mana
|
||||
if (shard.getCmc() == 2) {
|
||||
manaCost.add(ManaCostShard.GENERIC);
|
||||
}
|
||||
manaPaid.remove(i);
|
||||
if (checkShard(index + 1)) {
|
||||
return true;
|
||||
}
|
||||
manaPaid.add(i, mana);
|
||||
if (shard.getCmc() == 2) {
|
||||
manaCost.remove(manaCost.size() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
boolean check() {
|
||||
manaPaid = new ArrayList<>(spellAbility.getPayingMana());
|
||||
manaCost = new ArrayList<>();
|
||||
card.getManaCost().forEach(manaCost::add);
|
||||
Collections.sort(manaCost);
|
||||
//It seems the above codes didn't add generic mana cost ?
|
||||
//Add generic cost below to fix it.
|
||||
int genericCost = card.getManaCost().getGenericCost();
|
||||
while (genericCost-- > 0) {
|
||||
manaCost.add(ManaCostShard.GENERIC);
|
||||
}
|
||||
return checkShard(0);
|
||||
}
|
||||
}
|
||||
return new CheckCanPayManaCost().check();
|
||||
} else {
|
||||
// StringType done in CardState
|
||||
if (!card.getCurrentState().hasProperty(property, sourceController, source, spellAbility)) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.*;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
import forge.game.spellability.SpellAbilityStackInstance;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.util.CardTranslation;
|
||||
@@ -673,7 +674,10 @@ public class Combat {
|
||||
if (firstStrikeDamage) {
|
||||
combatantsThatDealtFirstStrikeDamage.add(blocker);
|
||||
}
|
||||
|
||||
|
||||
// Run replacement effects
|
||||
blocker.getGame().getReplacementHandler().run(ReplacementType.AssignDealDamage, AbilityKey.mapFromAffected(blocker));
|
||||
|
||||
CardCollection attackers = attackersOrderedForDamageAssignment.get(blocker);
|
||||
|
||||
final int damage = blocker.getNetCombatDamage();
|
||||
@@ -709,7 +713,10 @@ public class Combat {
|
||||
if (firstStrikeDamage) {
|
||||
combatantsThatDealtFirstStrikeDamage.add(attacker);
|
||||
}
|
||||
|
||||
|
||||
// Run replacement effects
|
||||
attacker.getGame().getReplacementHandler().run(ReplacementType.AssignDealDamage, AbilityKey.mapFromAffected(attacker));
|
||||
|
||||
// If potential damage is 0, continue along
|
||||
final int damageDealt = attacker.getNetCombatDamage();
|
||||
if (damageDealt <= 0) {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package forge.game.replacement;
|
||||
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public class ReplaceAssignDealDamage extends ReplacementEffect {
|
||||
|
||||
/**
|
||||
* Instantiates a new replace tap.
|
||||
*
|
||||
* @param params the params
|
||||
* @param host the host
|
||||
*/
|
||||
public ReplaceAssignDealDamage(final Map<String, String> params, final Card host, final boolean intrinsic) {
|
||||
super(params, host, intrinsic);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
|
||||
*/
|
||||
@Override
|
||||
public boolean canReplace(Map<AbilityKey, Object> runParams) {
|
||||
if (hasParam("ValidCard")) {
|
||||
if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidCard").split(","), this.getHostCard())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
|
||||
sa.setReplacingObject(AbilityKey.Card, runParams.get(AbilityKey.Affected));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package forge.game.replacement;
|
||||
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public class ReplaceDealtDamage extends ReplacementEffect {
|
||||
|
||||
/**
|
||||
* Instantiates a new replace tap.
|
||||
*
|
||||
* @param params the params
|
||||
* @param host the host
|
||||
*/
|
||||
public ReplaceDealtDamage(final Map<String, String> params, final Card host, final boolean intrinsic) {
|
||||
super(params, host, intrinsic);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
|
||||
*/
|
||||
@Override
|
||||
public boolean canReplace(Map<AbilityKey, Object> runParams) {
|
||||
if (hasParam("ValidCard")) {
|
||||
if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidCard").split(","), this.getHostCard())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
|
||||
sa.setReplacingObject(AbilityKey.Card, runParams.get(AbilityKey.Affected));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package forge.game.replacement;
|
||||
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public class ReplaceTap extends ReplacementEffect {
|
||||
|
||||
/**
|
||||
* Instantiates a new replace tap.
|
||||
*
|
||||
* @param params the params
|
||||
* @param host the host
|
||||
*/
|
||||
public ReplaceTap(final Map<String, String> params, final Card host, final boolean intrinsic) {
|
||||
super(params, host, intrinsic);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
|
||||
*/
|
||||
@Override
|
||||
public boolean canReplace(Map<AbilityKey, Object> runParams) {
|
||||
if (hasParam("ValidCard")) {
|
||||
if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidCard").split(","), this.getHostCard())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
|
||||
sa.setReplacingObject(AbilityKey.Card, runParams.get(AbilityKey.Affected));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,17 +6,19 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public enum ReplacementType {
|
||||
AddCounter(ReplaceAddCounter.class),
|
||||
AssignDealDamage(ReplaceAssignDealDamage.class),
|
||||
Attached(ReplaceAttached.class),
|
||||
Counter(ReplaceCounter.class),
|
||||
CopySpell(ReplaceCopySpell.class),
|
||||
CreateToken(ReplaceToken.class),
|
||||
DamageDone(ReplaceDamage.class),
|
||||
DealtDamage(ReplaceDealtDamage.class),
|
||||
Destroy(ReplaceDestroy.class),
|
||||
Discard(ReplaceDiscard.class),
|
||||
Draw(ReplaceDraw.class),
|
||||
@@ -29,6 +31,7 @@ public enum ReplacementType {
|
||||
Scry(ReplaceScry.class),
|
||||
SetInMotion(ReplaceSetInMotion.class),
|
||||
Surveil(ReplaceSurveil.class),
|
||||
Tap(ReplaceTap.class),
|
||||
TurnFaceUp(ReplaceTurnFaceUp.class),
|
||||
Untap(ReplaceUntap.class);
|
||||
|
||||
@@ -46,7 +49,7 @@ public enum ReplacementType {
|
||||
}
|
||||
throw new RuntimeException("Element " + value + " not found in TriggerType enum");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @param mapParams
|
||||
|
||||
@@ -243,7 +243,8 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
source.setController(activator, 0);
|
||||
final Spell spell = (Spell) sp;
|
||||
if (spell.isCastFaceDown()) {
|
||||
source.turnFaceDown();
|
||||
// Need to override for double faced cards
|
||||
source.turnFaceDown(true);
|
||||
} else if (source.isFaceDown()) {
|
||||
source.turnFaceUp(null);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user