CardRules: fix case with null tokens list (#5395)

* CardRules: fix NPE

* clean up

---------

Co-authored-by: tool4EvEr <tool4EvEr@192.168.0.60>
This commit is contained in:
tool4ever
2024-06-09 06:08:45 +02:00
committed by GitHub
parent cc08849132
commit a9849696d3
9 changed files with 23 additions and 21 deletions

View File

@@ -92,6 +92,7 @@ public final class CardRules implements ICardCharacteristics {
colorIdentity = newRules.colorIdentity;
meldWith = newRules.meldWith;
partnerWith = newRules.partnerWith;
tokens = newRules.tokens;
}
private static byte calculateColorIdentity(final ICardFace face) {
@@ -386,7 +387,7 @@ public final class CardRules implements ICardCharacteristics {
private int deltaHand;
private int deltaLife;
private List<String> tokens;
private List<String> tokens = Collections.emptyList();
public List<String> getTokens() {
return tokens;
@@ -484,7 +485,9 @@ public final class CardRules implements ICardCharacteristics {
result.setNormalizedName(this.normalizedName);
result.meldWith = this.meldWith;
result.partnerWith = this.partnerWith;
result.tokens = tokens.isEmpty() ? Collections.emptyList() : tokens;
if (!tokens.isEmpty()) {
result.tokens = tokens;
}
if (StringUtils.isNotBlank(handLife))
result.setVanguardProperties(handLife);
return result;
@@ -742,7 +745,10 @@ public final class CardRules implements ICardCharacteristics {
}
public boolean hasStartOfKeyword(final String k) {
for (final String inst : mainPart.getKeywords()) {
return hasStartOfKeyword(k, mainPart);
}
public boolean hasStartOfKeyword(final String k, ICardFace cf) {
for (final String inst : cf.getKeywords()) {
final String[] parts = inst.split(":");
if (parts[0].equals(k)) {
return true;

View File

@@ -8,6 +8,7 @@ import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import forge.util.CardTranslation;
import forge.util.ComparableOp;
@@ -186,7 +187,7 @@ public final class CardRulesPredicates {
return new Predicate<CardRules>() {
@Override
public boolean apply(final CardRules card) {
return card.hasStartOfKeyword(keyword);
return Iterables.any(card.getAllFaces(), cf -> cf != null && card.hasStartOfKeyword(keyword, cf));
}
};
}

View File

@@ -180,12 +180,8 @@ public class RepeatEachEffect extends SpellAbilityEffect {
}
}
for (final Player p : repeatPlayers) {
if (optional) {
if (!p.getController().confirmAction(repeat, null, sa.getParam("RepeatOptionalMessage"), null)) {
continue;
} else if (sa.hasParam("RememberDeciders")) {
source.addRemembered(p);
}
if (optional && !p.getController().confirmAction(repeat, null, sa.getParam("RepeatOptionalMessage"), null)) {
continue;
}
if (nextTurn) {
game.getCleanup().addUntil(p, new GameCommand() {

View File

@@ -1,7 +1,7 @@
Name:Desecrate Reality
ManaCost:7
Types:Instant
A:SP$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Permanent.nonLand+OppCtrl+cmcEven | TargetMin$ 0 | TargetMax$ OneEach | TargetsForEachPlayer$ True | TgtPrompt$ Select up to one target nonland permanent each opponent controls | SubAbility$ DBReturn | SpellDescription$ For each opponent, exile up to one target permanent that player controls with an even mana value. (Zero is even.)
A:SP$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Permanent.OppCtrl+cmcEven | TargetMin$ 0 | TargetMax$ OneEach | TargetsForEachPlayer$ True | TgtPrompt$ Select up to one target permanent with an even mana value each opponent controls | SubAbility$ DBReturn | SpellDescription$ For each opponent, exile up to one target permanent that player controls with an even mana value. (Zero is even.)
SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Permanent.cmcOdd+YouOwn | Hidden$ True | Mandatory$ True | ChangeTypeDesc$ permanent card with an odd mana value | ChangeNum$ 1 | ConditionCheckSVar$ X | SpellDescription$ Adamant — If at least three colorless mana was spent to cast this spell, return a permanent card with an odd mana value from your graveyard to the battlefield.
SVar:X:Count$Adamant.Colorless.1.0
SVar:OneEach:PlayerCountOpponents$Amount

View File

@@ -7,7 +7,7 @@ T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.S
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigSac | Secondary$ True | TriggerDescription$ Whenever CARDNAME enters the battlefield or attacks, sacrifice another permanent.
SVar:TrigSac:DB$ Sacrifice | Defined$ You | SacValid$ Permanent.Other
SVar:NeedsToPlay:Permanent.YouCtrl+cmcLE2
T:Mode$ Sacrificed | ValidCard$ Permanent | ValidPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever you sacrifice a permanent, put a +1/+1 counter on CARDNAME and draw a card.
T:Mode$ Sacrificed | ValidCard$ Permanent | ValidPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever you sacrifice a permanent, put a +1/+1 counter on NICKNAME and draw a card.
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBDraw
SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 1
AI:RemoveDeck:Random

View File

@@ -2,10 +2,10 @@ Name:Tempting Contract
ManaCost:4
Types:Artifact
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ DBRepeat | SubAbility$ DBToken | TriggerDescription$ At the beginning of your upkeep, each opponent may create a Treasure token. For each opponent who does, you create a Treasure token.
SVar:DBRepeat:DB$ RepeatEach | RepeatSubAbility$ DBOppToken | RepeatPlayers$ Opponent | RepeatOptionalForEachPlayer$ True | RememberDeciders$ True | RepeatOptionalMessage$ Do you want to create a Treasure token? | ChangeZoneTable$ True | SubAbility$ DBToken
SVar:DBOppToken:DB$ Token | TokenScript$ c_a_treasure_sac | TokenOwner$ Player.IsRemembered
SVar:DBRepeat:DB$ RepeatEach | RepeatSubAbility$ DBOppToken | RepeatPlayers$ Opponent | RepeatOptionalForEachPlayer$ True | RepeatOptionalMessage$ Do you want to create a Treasure token? | ChangeZoneTable$ True | SubAbility$ DBToken
SVar:DBOppToken:DB$ Token | TokenScript$ c_a_treasure_sac | TokenOwner$ Player.IsRemembered | RememberTokens$ True
SVar:DBToken:DB$ Token | TokenAmount$ X | TokenScript$ c_a_treasure_sac | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:Count$RememberedSize
SVar:X:PlayerCountRememberedOwner$Amount
DeckHas:Ability$Token|Sacrifice
Oracle:At the beginning of your upkeep, each opponent may create a Treasure token. For each opponent who does, you create a Treasure token.

View File

@@ -3,8 +3,8 @@ ManaCost:G
Types:Legendary Creature Insect
PT:1/2
K:Deathtouch
T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Battlefield | ValidCard$ Card.Self,Creature.YouCtrl | Execute$ TrigTransform | TriggerDescription$ When CARDNAME or another creature enters the battlefield under your control, if it entered from your graveyard or you cast it from your graveyard, you may pay {G}. If you do, exile NICKNAME, then return it to the battlefield transformed under its owner's control.
T:Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self+wasCastFromYourGraveyardByYou,Creature.YouCtrl+wasCastFromYourGraveyardByYou | Execute$ TrigTransform | Secondary$ True | TriggerDescription$ When CARDNAME enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, you may pay {G}. If you do, exile NICKNAME, then return it to the battlefield transformed under its owner's control.
T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Battlefield | ValidCard$ Card.Self,Creature.YouCtrl | Execute$ TrigTransform | TriggerZones$ Battlefield | TriggerDescription$ When CARDNAME or another creature enters the battlefield under your control, if it entered from your graveyard or you cast it from your graveyard, you may pay {G}. If you do, exile NICKNAME, then return it to the battlefield transformed under its owner's control.
T:Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self+wasCastFromYourGraveyardByYou,Creature.YouCtrl+wasCastFromYourGraveyardByYou | Execute$ TrigTransform | TriggerZones$ Battlefield | Secondary$ True | TriggerDescription$ When CARDNAME enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, you may pay {G}. If you do, exile NICKNAME, then return it to the battlefield transformed under its owner's control.
SVar:TrigTransform:AB$ ChangeZone | Cost$ G | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DBReturn
SVar:DBReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Battlefield | Transformed$ True | ForgetOtherRemembered$ True | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True

View File

@@ -1,9 +1,9 @@
Name:Tempt with Mayhem
ManaCost:1 R R
Types:Instant
A:SP$ RepeatEach | ValidTgts$ Instant,Sorcery | TgtZone$ Stack | TgtPrompt$ Select target instant or sorcery spell | RepeatSubAbility$ DBCopy | RepeatPlayers$ Player.Opponent | SubAbility$ DBCopySelf | RepeatOptionalForEachPlayer$ True | RememberDeciders$ True | RepeatOptionalMessage$ Do you want to copy the targeted spell? | SpellDescription$ Tempting offer — Choose target instant or sorcery spell. Each opponent may copy that spell and may choose new targets for the copy they control. You copy that spell once plus an additional time for each opponent who copied the spell this way. You may choose new targets for the copies you control.
SVar:DBCopy:DB$ CopySpellAbility | Defined$ Targeted | Controller$ Remembered | Amount$ 1 | MayChooseTarget$ True
A:SP$ RepeatEach | ValidTgts$ Instant,Sorcery | TgtZone$ Stack | TgtPrompt$ Select target instant or sorcery spell | RepeatSubAbility$ DBCopy | RepeatPlayers$ Player.Opponent | SubAbility$ DBCopySelf | RepeatOptionalForEachPlayer$ True | RepeatOptionalMessage$ Do you want to copy the targeted spell? | SpellDescription$ Tempting offer — Choose target instant or sorcery spell. Each opponent may copy that spell and may choose new targets for the copy they control. You copy that spell once plus an additional time for each opponent who copied the spell this way. You may choose new targets for the copies you control.
SVar:DBCopy:DB$ CopySpellAbility | Defined$ Targeted | Controller$ Remembered | Amount$ 1 | MayChooseTarget$ True | RememberCopies$ True
SVar:DBCopySelf:DB$ CopySpellAbility | Defined$ Targeted | Amount$ X | MayChooseTarget$ True | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:Count$RememberedSize/Plus.1
SVar:X:PlayerCountRememberedController$Amount/Plus.1
Oracle:Tempting offer — Choose target instant or sorcery spell. Each opponent may copy that spell and may choose new targets for the copy they control. You copy that spell once plus an additional time for each opponent who copied the spell this way. You may choose new targets for the copies you control.

View File

@@ -223,7 +223,6 @@ public class SFilterUtil {
}
};
}
public static Predicate<PaperCard> buildColorFilter(Map<SItemManagerUtil.StatTypes, ? extends IButton> buttonMap) {