Support for Display of Power (#3876)

* Support for Display of Power

* Clean up

---------

Co-authored-by: tool4EvEr <tool4EvEr@192.168.0.60>
This commit is contained in:
tool4ever
2023-10-10 04:53:12 +02:00
committed by GitHub
parent 269eb6a1f0
commit 2a2e4d7819
10 changed files with 128 additions and 123 deletions

View File

@@ -87,142 +87,148 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
for (Player controller : controllers) {
List<SpellAbility> copies = Lists.newArrayList();
SpellAbility chosenSA = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa,
Localizer.getInstance().getMessage("lblSelectASpellCopy"), ImmutableMap.of());
if (isOptional && !controller.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoyouWantCopyTheSpell", CardTranslation.getTranslatedName(chosenSA.getHostCard().getName())), null)) {
continue;
List<SpellAbility> copySpells = tgtSpells;
if (sa.hasParam("SingleChoice")) {
SpellAbility chosenSA = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa,
Localizer.getInstance().getMessage("lblSelectASpellCopy"), ImmutableMap.of());
copySpells = Lists.newArrayList(chosenSA);
}
// CR 707.10d
if (sa.hasParam("CopyForEachCanTarget")) {
// Find subability or rootability that has targets
SpellAbility targetedSA = chosenSA;
while (targetedSA != null) {
if (targetedSA.usesTargeting() && !targetedSA.getTargets().isEmpty()) {
break;
}
targetedSA = targetedSA.getSubAbility();
}
if (targetedSA == null) {
for (SpellAbility chosenSA : copySpells) {
if (isOptional && !controller.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoyouWantCopyTheSpell", CardTranslation.getTranslatedName(chosenSA.getHostCard().getName())), null)) {
continue;
}
FCollection<GameEntity> all = new FCollection<>(Iterables.filter(targetedSA.getTargetRestrictions().getAllCandidates(targetedSA, true), GameObjectPredicates.restriction(sa.getParam("CopyForEachCanTarget").split(","), sa.getActivatingPlayer(), card, sa)));
// Remove targeted players because getAllCandidates include all the valid players
all.removeAll(getTargetPlayers(chosenSA));
// CR 707.10d
if (sa.hasParam("CopyForEachCanTarget")) {
// Find subability or rootability that has targets
SpellAbility targetedSA = chosenSA;
while (targetedSA != null) {
if (targetedSA.usesTargeting() && !targetedSA.getTargets().isEmpty()) {
break;
}
targetedSA = targetedSA.getSubAbility();
}
if (targetedSA == null) {
continue;
}
if (sa.hasParam("ChooseOnlyOne")) { // Beamsplitter Mage
GameEntity choice = controller.getController().chooseSingleEntityForEffect(all, sa, Localizer.getInstance().getMessage("lblChooseOne"), null);
if (choice != null) {
FCollection<GameEntity> all = new FCollection<>(Iterables.filter(targetedSA.getTargetRestrictions().getAllCandidates(targetedSA, true), GameObjectPredicates.restriction(sa.getParam("CopyForEachCanTarget").split(","), sa.getActivatingPlayer(), card, sa)));
// Remove targeted players because getAllCandidates include all the valid players
all.removeAll(getTargetPlayers(chosenSA));
if (sa.hasParam("ChooseOnlyOne")) { // Beamsplitter Mage
GameEntity choice = controller.getController().chooseSingleEntityForEffect(all, sa, Localizer.getInstance().getMessage("lblChooseOne"), null);
if (choice != null) {
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
if (changeToLegalTarget(copy, choice)) {
copies.add(copy);
}
}
} else {
for (final GameEntity ge : all) {
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
resetFirstTargetOnCopy(copy, ge, targetedSA);
copies.add(copy);
}
}
} else if (sa.hasParam("DefinedTarget")) { // CR 707.10e
final List<GameEntity> tgts = AbilityUtils.getDefinedEntities(card, sa.getParam("DefinedTarget"), sa);
if (tgts.isEmpty()) {
continue;
}
FCollection<GameEntity> newTgts = new FCollection<>();
for (GameEntity e : tgts) {
if (e instanceof Player) { // Zevlor
FCollection<GameEntity> choices = new FCollection<>(e);
choices.addAll(((Player) e).getCardsIn(ZoneType.Battlefield));
newTgts.add(controller.getController().chooseSingleEntityForEffect(choices, sa, Localizer.getInstance().getMessage("lblChooseOne"), null));
} else { // Ivy
newTgts.add(e);
}
}
for (GameEntity e : newTgts) {
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
if (changeToLegalTarget(copy, choice)) {
if (changeToLegalTarget(copy, e)) {
copies.add(copy);
}
}
} else {
for (final GameEntity ge : all) {
for (int i = 0; i < amount; i++) {
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
resetFirstTargetOnCopy(copy, ge, targetedSA);
if (sa.hasParam("IgnoreFreeze")) {
copy.putParam("IgnoreFreeze", "True");
}
if (sa.hasParam("MayChooseTarget")) {
copy.setMayChooseNewTargets(true);
}
if (sa.hasParam("RandomTarget")) {
List<GameEntity> candidates = copy.getTargetRestrictions().getAllCandidates(chosenSA, true);
if (sa.hasParam("RandomTargetRestriction")) {
candidates.removeIf(new Predicate<GameEntity>() {
@Override
public boolean test(GameEntity c) {
return !c.isValid(sa.getParam("RandomTargetRestriction").split(","), sa.getActivatingPlayer(), card, sa);
}
});
}
if (!candidates.isEmpty()) {
GameEntity choice = Aggregates.random(candidates);
resetFirstTargetOnCopy(copy, choice, chosenSA);
}
}
// extra case for Epic to remove the keyword and the last part of the SpellAbility
if (sa.hasParam("Epic")) {
copy.getHostCard().removeIntrinsicKeyword("Epic");
SpellAbility sub = copy;
while (sub.getSubAbility() != null && !sub.hasParam("Epic")) {
sub = sub.getSubAbility();
}
if (sub != null) {
sub.getParent().setSubAbility(sub.getSubAbility());
}
}
copies.add(copy);
}
}
} else if (sa.hasParam("DefinedTarget")) { // CR 707.10e
final List<GameEntity> tgts = AbilityUtils.getDefinedEntities(card, sa.getParam("DefinedTarget"), sa);
if (tgts.isEmpty()) {
if (copies.isEmpty()) {
continue;
}
FCollection<GameEntity> newTgts = new FCollection<>();
for (GameEntity e : tgts) {
if (e instanceof Player) { // Zevlor
FCollection<GameEntity> choices = new FCollection<>(e);
choices.addAll(((Player) e).getCardsIn(ZoneType.Battlefield));
newTgts.add(controller.getController().chooseSingleEntityForEffect(choices, sa, Localizer.getInstance().getMessage("lblChooseOne"), null));
} else { // Ivy
newTgts.add(e);
}
int addAmount = copies.size();
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(controller);
repParams.put(AbilityKey.SpellAbility, chosenSA);
repParams.put(AbilityKey.Amount, addAmount);
switch (game.getReplacementHandler().run(ReplacementType.CopySpell, repParams)) {
case NotReplaced:
break;
case Updated: {
addAmount = (int) repParams.get(AbilityKey.Amount);
break;
}
default:
addAmount = 0;
}
for (GameEntity e : newTgts) {
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
if (changeToLegalTarget(copy, e)) {
copies.add(copy);
}
if (addAmount <= 0) {
continue;
}
} else {
for (int i = 0; i < amount; i++) {
int extraAmount = addAmount - copies.size();
for (int i = 0; i < extraAmount; i++) {
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
if (sa.hasParam("IgnoreFreeze")) {
copy.putParam("IgnoreFreeze", "True");
}
if (sa.hasParam("MayChooseTarget")) {
copy.setMayChooseNewTargets(true);
}
if (sa.hasParam("RandomTarget")) {
List<GameEntity> candidates = copy.getTargetRestrictions().getAllCandidates(chosenSA, true);
if (sa.hasParam("RandomTargetRestriction")) {
candidates.removeIf(new Predicate<GameEntity>() {
@Override
public boolean test(GameEntity c) {
return !c.isValid(sa.getParam("RandomTargetRestriction").split(","), sa.getActivatingPlayer(), card, sa);
}
});
}
if (!candidates.isEmpty()) {
GameEntity choice = Aggregates.random(candidates);
resetFirstTargetOnCopy(copy, choice, chosenSA);
}
}
// extra case for Epic to remove the keyword and the last part of the SpellAbility
if (sa.hasParam("Epic")) {
copy.getHostCard().removeIntrinsicKeyword("Epic");
SpellAbility sub = copy;
while (sub.getSubAbility() != null && !sub.hasParam("Epic")) {
sub = sub.getSubAbility();
}
if (sub != null) {
sub.getParent().setSubAbility(sub.getSubAbility());
}
}
// extra copies added with CopySpellReplacenment currently always has new choose targets
copy.setMayChooseNewTargets(true);
copies.add(copy);
}
}
if (copies.isEmpty()) {
continue;
}
int addAmount = copies.size();
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(controller);
repParams.put(AbilityKey.SpellAbility, chosenSA);
repParams.put(AbilityKey.Amount, addAmount);
switch (game.getReplacementHandler().run(ReplacementType.CopySpell, repParams)) {
case NotReplaced:
break;
case Updated: {
addAmount = (int) repParams.get(AbilityKey.Amount);
break;
}
default:
addAmount = 0;
}
if (addAmount <= 0) {
continue;
}
int extraAmount = addAmount - copies.size();
for (int i = 0; i < extraAmount; i++) {
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
// extra copies added with CopySpellReplacenment currently always has new choose targets
copy.setMayChooseNewTargets(true);
copies.add(copy);
}
controller.getController().orderAndPlaySimultaneousSa(copies);
if (sa.hasParam("RememberCopies")) {

View File

@@ -3,7 +3,7 @@ ManaCost:X X X
Types:Artifact
K:etbCounter:CHARGE:X
SVar:X:Count$xPaid
A:AB$ ChooseColor | Cost$ T | AILogic$ MostProminentInComputerDeck | SubAbility$ DBMana | SpellDescription$ Choose a color. Add one mana of that color for each charge counter on Astral Cornucopia.
A:AB$ ChooseColor | Cost$ T | AILogic$ MostProminentInComputerDeck | SubAbility$ DBMana | SpellDescription$ Choose a color. Add one mana of that color for each charge counter on CARDNAME.
SVar:DBMana:DB$ Mana | Produced$ Chosen | Amount$ Y
SVar:Y:Count$CardCounters.CHARGE
Oracle:Astral Cornucopia enters the battlefield with X charge counters on it.\n{T}: Choose a color. Add one mana of that color for each charge counter on Astral Cornucopia.

View File

@@ -4,7 +4,7 @@ Types:Artifact Creature Thopter
PT:1/1
K:Flying
T:Mode$ ChangesZone | ValidCard$ Card.Self | Destination$ Battlefield | Execute$ TrigDig | DestinationZone2$ Graveyard | TriggerDescription$ When CARDNAME enters the battlefield, reveal the top two cards of your library. Put one of them into your hand and the other into your graveyard. If you put an artifact card into your hand this way, you gain 3 life.
SVar:TrigDig:DB$ Dig | DigNum$ 2 | ChangeNum$ 1 | Reveal$ True | ChangeType$ Card | RememberChanged$ True | SubAbility$ DBGainLife
SVar:TrigDig:DB$ Dig | DigNum$ 2 | ChangeNum$ 1 | Reveal$ True | ChangeType$ Card | DestinationZone2$ Graveyard | RememberChanged$ True | SubAbility$ DBGainLife
SVar:DBGainLife:DB$ GainLife | LifeAmount$ 3 | ConditionDefined$ Remembered | ConditionPresent$ Artifact | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
DeckHints:Type$Artifact

View File

@@ -1,9 +1,8 @@
Name:Faith's Shield
ManaCost:W
Types:Instant
A:SP$ Protection | Cost$ W | CheckSVar$ FatefulHour | SVarCompare$ GT5 | ValidTgts$ Permanent.YouCtrl | TgtPrompt$ Select target permanent you control | Gains$ Choice | Choices$ AnyColor | SpellDescription$ Target permanent you control gains protection from the color of your choice until end of turn.
A:SP$ ChooseColor | Cost$ W | CheckSVar$ FatefulHour | SVarCompare$ LE5 | Defined$ You | AILogic$ MostProminentAttackers | SubAbility$ DBProtection | SpellDescription$ Fateful Hour — If you have 5 or less life, instead you and each permanent you control gain protection from the color of your choice until end of turn.
SVar:DBProtection:DB$ ProtectionAll | ValidCards$ Permanent.YouCtrl | ValidPlayers$ You | Gains$ ChosenColor
A:SP$ Protection | Cost$ W | ConditionCheckSVar$ FatefulHour | ConditionSVarCompare$ GT5 | ValidTgts$ Permanent.YouCtrl | TgtPrompt$ Select target permanent you control | Gains$ Choice | Choices$ AnyColor | SubAbility$ DBProtection | SpellDescription$ Target permanent you control gains protection from the color of your choice until end of turn.
SVar:DBProtection:DB$ ProtectionAll | ValidCards$ Permanent.YouCtrl | ValidPlayers$ You | Gains$ Choice | Choices$ AnyColor | ConditionCheckSVar$ FatefulHour | ConditionSVarCompare$ LE5
SVar:FatefulHour:Count$YourLifeTotal
AI:RemoveDeck:All
Oracle:Target permanent you control gains protection from the color of your choice until end of turn.\nFateful hour — If you have 5 or less life, instead you and each permanent you control gain protection from the color of your choice until end of turn.

View File

@@ -2,7 +2,7 @@ Name:Skull Storm
ManaCost:7 B B
Types:Sorcery
T:Mode$ SpellCast | ValidCard$ Card.Self | Execute$ TrigCopy | TriggerDescription$ When you cast this spell, copy it for each time you've cast your commander from the command zone this game.
SVar:TrigCopy:DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | Amount$ X | MayChooseTarget$ True
SVar:TrigCopy:DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | Amount$ X
SVar:X:Count$TotalCommanderCastFromCommandZone
A:SP$ Sacrifice | Cost$ 7 B B | Defined$ Player.Opponent | SacValid$ Creature | RememberSacrificed$ True | SubAbility$ DBRepeatEach
SVar:DBRepeatEach:DB$ RepeatEach | RepeatPlayers$ Player.Opponent | RepeatSubAbility$ DBLoseLife | SubAbility$ DBCleanup

View File

@@ -3,6 +3,6 @@ ManaCost:1 W U
Types:Enchantment Aura
K:Enchant creature
A:SP$ Attach | Cost$ 1 W U | ValidTgts$ Creature | AILogic$ Pump
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddToughness$ 2 | Description$ Enchanted creature gets +0/+2.
S:Mode$ CantTarget | ValidCard$ Creature.EnchantedBy | ValidSA$ Spell | Description$ Enchanted creature can't be the target of spells.
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddToughness$ 2 | Description$ Enchanted creature gets +0/+2 and can't be the target of spells.
S:Mode$ CantTarget | ValidCard$ Creature.EnchantedBy | ValidSA$ Spell | Secondary$ True | Description$ Enchanted creature gets +0/+2 and can't be the target of spells.
Oracle:Enchant creature\nEnchanted creature gets +0/+2 and can't be the target of spells.

View File

@@ -4,8 +4,8 @@ Types:Creature Human Knight
PT:3/2
K:Trample
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigEffect | TriggerDescription$ When CARDNAME dies, you may cast it from your graveyard as an Adventure until the end of your next turn.
SVar:TrigEffect:DB$ Effect | StaticAbilities$ STPlay | ForgetOnMoved$ Graveyard | Duration$ UntilTheEndOfYourNextTurn
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.EffectSource+Adventure | AffectedZone$ Graveyard | Description$ Until the end of your next turn, you may play EFFECTSOURCE as an adventure.
SVar:TrigEffect:DB$ Effect | StaticAbilities$ STPlay | ForgetOnMoved$ Graveyard | RememberObjects$ Self | Duration$ UntilTheEndOfYourNextTurn
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+Adventure | AffectedZone$ Graveyard | Description$ Until the end of your next turn, you may play EFFECTSOURCE as an adventure.
DeckHas:Ability$Graveyard
AlternateMode:Adventure
Oracle:Trample\nWhen Mosswood Dreadknight dies, you may cast it from your graveyard as an Adventure until the end of your next turn.

View File

@@ -5,7 +5,7 @@ PT:0/0
K:etbCounter:P1P1:Y:no Condition:CARDNAME enters the battlefield with a +1/+1 counter on it for each mana spent to cast it.
SVar:X:Count$xPaid
SVar:Y:Count$CastTotalManaSpent
T:Mode$ SpellCast | ValidSA$ Spell.Kicked | ValidActivatingPlayer$ You | Execute$ DBRemoveCounters | TriggerZones$ Battlefield | OptionalDecider$ You | TriggerDescription$ Whenever you cast a kicked spell, you may remove two +1/+1 counters from CARDNAME.
T:Mode$ SpellCast | ValidSA$ Spell.Kicked | ValidActivatingPlayer$ You | Execute$ DBRemoveCounters | TriggerZones$ Battlefield | OptionalDecider$ You | TriggerDescription$ Whenever you cast a kicked spell, you may remove two +1/+1 counters from NICKNAME.
SVar:DBRemoveCounters:DB$ RemoveCounter | CounterType$ P1P1 | CounterNum$ 2 | RememberRemoved$ True | SubAbility$ DBCopy
SVar:DBCopy:DB$ CopySpellAbility | ConditionCheckSVar$ Z | ConditionSVarCompare$ GE1 | SubAbility$ DBCleanup | Defined$ TriggeredSpellAbility | AILogic$ Always | MayChooseTarget$ True | SpellDescription$ If you do, copy that spell. You may choose new targets for that copy. (A copy of a permanent spell becomes a token.)
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True

View File

@@ -4,7 +4,7 @@ Types:Legendary Creature Hyena Beast
PT:3/3
K:Partner:Nikara, Lair Scavenger:Nikara
K:Vigilance
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChoose | TriggerDescription$ When CARDNAME enters the battlefield, exile another creature you control until CARDNAME leaves the battlefield. When you do, distribute +1/+1 counters among any number of target creatures, where X is the exiled creature's power.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChoose | TriggerDescription$ When CARDNAME enters the battlefield, exile another creature you control until NICKNAME leaves the battlefield. When you do, distribute +1/+1 counters among any number of target creatures, where X is the exiled creature's power.
SVar:TrigChoose:DB$ ChooseCard | Choices$ Creature.YouCtrl+Other | ChoiceZone$ Battlefield | ChoiceTitle$ Select another creature you control | Mandatory$ True | SubAbility$ DBExile
SVar:DBExile:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Battlefield | Destination$ Exile | RememberLKI$ True | Duration$ UntilHostLeavesPlay | SubAbility$ DBImmediateTrigger
SVar:DBImmediateTrigger:DB$ ImmediateTrigger | Execute$ TrigPutCounters | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1 | TriggerDescription$ When you do, distribute X +1/+1 counters among any number of target creatures, where X is the exiled creature's power.

View File

@@ -4,7 +4,7 @@ Types:Legendary Creature Bird Serpent
PT:4/5
K:Companion:Special:DeckSizePlus20:Your starting deck contains at least twenty cards more than the minimum deck size.
K:Flying
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile any number of other cards you own and control. Return those cards to the battlefield at the beginning of the next end step.
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigExile | TriggerDescription$ When NICKNAME enters the battlefield, exile any number of other cards you own and control. Return those cards to the battlefield at the beginning of the next end step.
SVar:TrigExile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ChangeType$ Permanent.nonLand+Other+YouOwn+YouCtrl | SelectPrompt$ Select any number of other nonland permanents you own and control | Hidden$ True | ChangeNum$ X | RememberChanged$ True | SubAbility$ DelTrig
SVar:DelTrig:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ TrigReturn | RememberObjects$ RememberedLKI | TriggerDescription$ Return those cards to the battlefield at the beginning of the next end step. | SubAbility$ DBCleanup
SVar:TrigReturn:DB$ ChangeZone | Origin$ Exile | Destination$ Battlefield | Defined$ DelayTriggerRememberedLKI