King of the Oathbreakers should trigger for Changeling (Discord post) (#6453)

* Fix NPE with Street Spasm
This commit is contained in:
tool4ever
2024-10-29 13:02:32 +01:00
committed by GitHub
parent 5db52c38ec
commit de4b35cc1d
14 changed files with 60 additions and 58 deletions

View File

@@ -249,13 +249,6 @@ public class GameAction {
copied = new CardCopyService(c).copyCard(false);
// CR 707.12 casting of a card copy
if (zoneTo.is(ZoneType.Stack) && c.isRealToken()) {
copied.setCopiedPermanent(c.getCopiedPermanent());
//TODO: Feels like this should fit here and seems to work but it'll take a fair bit more testing to be sure.
//copied.setGamePieceType(GamePieceType.COPIED_SPELL);
}
copied.setGameTimestamp(c.getGameTimestamp());
if (zoneTo.is(ZoneType.Stack)) {
@@ -267,6 +260,13 @@ public class GameAction {
copied.setExiledBy(c.getExiledBy());
copied.setDrawnThisTurn(c.getDrawnThisTurn());
// CR 707.12 casting of a card copy
if (c.isRealToken()) {
copied.setCopiedPermanent(c.getCopiedPermanent());
//TODO: Feels like this should fit here and seems to work but it'll take a fair bit more testing to be sure.
//copied.setGamePieceType(GamePieceType.COPIED_SPELL);
}
if (c.isTransformed()) {
copied.incrementTransformedTimestamp();
}
@@ -277,6 +277,7 @@ public class GameAction {
// CR 112.2 A spells controller is, by default, the player who put it on the stack.
copied.setController(cause.getActivatingPlayer(), 0);
KeywordInterface kw = cause.getKeyword();
if (kw != null) {
copied.addKeywordForStaticAbility(kw);
@@ -290,25 +291,24 @@ public class GameAction {
copied.setBackSide(false);
}
copied.setUnearthed(c.isUnearthed());
// need to copy counters when card enters another zone than hand or library
if (lastKnownInfo.hasKeyword("Counters remain on CARDNAME as it moves to any zone other than a player's hand or library.") &&
!(zoneTo.is(ZoneType.Hand) || zoneTo.is(ZoneType.Library))) {
copied.setCounters(Maps.newHashMap(lastKnownInfo.getCounters()));
}
// perpetual stuff
if (c.hasIntensity()) {
copied.setIntensity(c.getIntensity(false));
}
if (c.isSpecialized()) {
copied.setState(c.getCurrentStateName(), false);
}
if (c.hasPerpetual()) {
copied.setPerpetual(c);
}
}
// perpetual stuff
if (c.hasIntensity()) {
copied.setIntensity(c.getIntensity(false));
}
if (c.isSpecialized()) {
copied.setState(c.getCurrentStateName(), false);
}
if (c.hasPerpetual()) {
copied.setPerpetual(c);
}
// ensure that any leftover keyword/type changes are cleared in the state view
copied.updateStateForView();
@@ -780,8 +780,6 @@ public class GameAction {
final Zone zoneFrom = game.getZoneOf(c);
// String prevName = prev != null ? prev.getZoneName() : "";
// Card lastKnownInfo = c;
// Handle the case that one component of a merged permanent got take to the subgame
if (zoneTo.is(ZoneType.Subgame) && (c.hasMergedCard() || c.isMerged())) {
c.moveMergedToSubgame(cause);
@@ -913,7 +911,7 @@ public class GameAction {
}
return changeZone(game.getZoneOf(c), deck, c, deckPosition, cause, params);
}
public final Card moveToJunkyard(Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
final PlayerZone junkyard = c.getOwner().getZone(ZoneType.Junkyard);
return moveTo(junkyard, c, cause, params);
@@ -931,7 +929,6 @@ public class GameAction {
final PlayerZone removed = c.getOwner().getZone(ZoneType.Exile);
final Card copied = moveTo(removed, c, cause, params);
// Run triggers
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
runParams.put(AbilityKey.Cause, cause);
if (origin != null) { // is generally null when adding via dev mode
@@ -1264,10 +1261,9 @@ public class GameAction {
AbilityKey.addCardZoneTableParams(mapParams, table);
for (final Player p : game.getPlayers()) {
for (final ZoneType zt : ZoneType.values()) {
if (zt == ZoneType.Command)
p.checkKeywordCard();
p.checkKeywordCard();
for (final ZoneType zt : ZoneType.values()) {
if (zt == ZoneType.Battlefield) {
continue;
}
@@ -1275,7 +1271,9 @@ public class GameAction {
checkAgain |= stateBasedAction704_5d(c);
// Dungeon Card won't affect other cards, so don't need to set checkAgain
stateBasedAction_Dungeon(c);
stateBasedAction_Scheme(c);
if (zt == ZoneType.Command) {
stateBasedAction_Scheme(c);
}
}
}
}
@@ -1557,7 +1555,7 @@ public class GameAction {
return;
}
if (!game.getStack().hasSourceOnStack(c, null)) {
moveTo(ZoneType.SchemeDeck, c, null, AbilityKey.newMap());
moveTo(ZoneType.SchemeDeck, c, -1, null, AbilityKey.newMap());
}
}
@@ -1676,7 +1674,6 @@ public class GameAction {
FCollectionView<Player> allPlayers = game.getPlayers();
for (Player p : allPlayers) {
if (p.checkLoseCondition()) { // this will set appropriate outcomes
// Run triggers
if (losers == null) {
losers = Lists.newArrayListWithCapacity(3);
}
@@ -1898,7 +1895,6 @@ public class GameAction {
}
}
for (Map.Entry<Player, Collection<Card>> e : lki.asMap().entrySet()) {
// Run triggers
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(e.getKey());
runParams.put(AbilityKey.Cards, new CardCollection(e.getValue()));
runParams.put(AbilityKey.Cause, source);

View File

@@ -367,7 +367,6 @@ public class DigEffect extends SpellAbilityEffect {
}
}
for (Card c : movedCards) {
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
AbilityKey.addCardZoneTableParams(moveParams, zoneMovements);

View File

@@ -259,8 +259,10 @@ public class Untap extends Phase {
}
public static void doPhasing(final Player turn) {
Game game = turn.getGame();
// Needs to include phased out cards
final List<Card> list = CardLists.filter(turn.getGame().getCardsIncludePhasingIn(ZoneType.Battlefield),
final List<Card> list = CardLists.filter(game.getCardsIncludePhasingIn(ZoneType.Battlefield),
c -> (c.isPhasedOut(turn) && c.isDirectlyPhasedOut())
|| (c.hasKeyword(Keyword.PHASING) && c.getController().equals(turn))
);
@@ -299,11 +301,13 @@ public class Untap extends Phase {
if (!phasedOut.isEmpty()) {
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Cards, phasedOut);
turn.getGame().getTriggerHandler().runTrigger(TriggerType.PhaseOutAll, runParams, false);
game.getTriggerHandler().runTrigger(TriggerType.PhaseOutAll, runParams, false);
}
if (!toPhase.isEmpty()) {
// refresh statics for phased in permanents (e.g. so King of the Oathbreakers sees Changeling)
game.getAction().checkStaticAbilities();
// collect now before some zone change during Untap resets triggers
turn.getGame().getTriggerHandler().collectTriggerForWaiting();
game.getTriggerHandler().collectTriggerForWaiting();
}
}

View File

@@ -694,8 +694,7 @@ public class ReplacementHandler {
// Determine if need to divide shield among affected entity and
// determine if the prevent next N damage shield is large enough to replace all damage
Map<String, String> mapParams = chosenRE.getMapParams();
if ((mapParams.containsKey("PreventionEffect") && mapParams.get("PreventionEffect").equals("NextN"))
if ((chosenRE.hasParam("PreventionEffect") && chosenRE.getParam("PreventionEffect").equals("NextN"))
|| apiType == ApiType.ReplaceSplitDamage) {
if (apiType == ApiType.ReplaceDamage) {
shieldAmount = AbilityUtils.calculateAmount(effectSA.getHostCard(), effectSA.getParamOrDefault("Amount", "1"), effectSA);

View File

@@ -625,9 +625,11 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
}
game.fireEvent(new GameEventSpellResolved(sa, thisHasFizzled));
finishResolving(sa, thisHasFizzled);
game.getAction().checkStaticAbilities();
finishResolving(sa, thisHasFizzled);
game.copyLastState();
if (isEmpty() && !hasSimultaneousStackEntries()) {
// assuming that if the stack is empty, no reason to hold on to old LKI data (everything is a new object)