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();
|
||||
@@ -258,6 +267,11 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
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)) {
|
||||
@@ -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;
|
||||
@@ -674,6 +675,9 @@ public class Combat {
|
||||
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();
|
||||
@@ -710,6 +714,9 @@ public class Combat {
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,11 +12,13 @@ import java.util.Map;
|
||||
*/
|
||||
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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
7
forge-gui/res/cardsfolder/i/illusionary_mask.txt
Normal file
7
forge-gui/res/cardsfolder/i/illusionary_mask.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Name:Illusionary Mask
|
||||
ManaCost:2
|
||||
Types:Artifact
|
||||
A:AB$ Play | Cost$ X | Valid$ Card.Creature+YouOwn+CanPayManaCost | ValidZone$ Hand | WithoutManaCost$ True | Amount$ 1 | Controller$ You | Optional$ True | CastFaceDown$ True | ReplaceIlluMask$ True | References$ X | SorcerySpeed$ True
|
||||
SVar:X:Count$xPaid
|
||||
AI:RemoveDeck:All
|
||||
Oracle:{X}: You may choose a creature card in your hand whose mana cost could be paid by some amount of, or all of, the mana you spent on X. If you do, you may cast that card face down as a 2/2 creature spell without paying its mana cost. 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. Activate this ability only any time you could cast a sorcery.
|
||||
Reference in New Issue
Block a user