game: fixed miss state triggers check in multi-steps abilities/effects (#10564)

This commit is contained in:
Oleg Agafonov 2023-07-05 09:52:38 +04:00
parent b66189d3da
commit f26b1470fa
4 changed files with 31 additions and 8 deletions

View file

@ -214,16 +214,14 @@ public abstract class AbilityImpl implements Ability {
} else {
game.addEffect((ContinuousEffect) effect, this);
}
/**
* All restrained trigger events are fired now. To restrain the
* events is mainly neccessary because of the movement of multiple
* object at once. If the event is fired directly as one object
* moved, other objects are not already in the correct zone to check
* for their effects. (e.g. Valakut, the Molten Pinnacle)
*/
game.getState().handleSimultaneousEvent(game);
game.resetShortLivingLKI();
/**
* <p>
* game.applyEffects() has to be done at least for every effect that
* moves cards/permanent between zones, or changes control of
* objects so Static effects work as intended if dependant from the
@ -231,8 +229,7 @@ public abstract class AbilityImpl implements Ability {
* abilities with replacement effects deactivated too late Example:
* {@link org.mage.test.cards.replacement.DryadMilitantTest#testDiesByDestroy testDiesByDestroy}
*/
game.applyEffects();
game.getState().getTriggers().checkStateTriggers(game);
game.getState().processAction(game);
}
return result;
}

View file

@ -8,6 +8,15 @@ import mage.game.events.GameEvent;
import java.util.UUID;
/**
* 603.8.
* Some triggered abilities trigger when a game state (such as a player controlling no permanents of
* a particular card type) is true, rather than triggering when an event occurs. These abilities trigger
* as soon as the game state matches the condition. They'll go onto the stack at the next available opportunity.
* These are called state triggers. (Note that state triggers aren't the same as state-based actions.)
* A state-triggered ability doesn't trigger again until the ability has resolved, has been countered,
* or has otherwise left the stack. Then, if the object with the ability is still in the same zone and
* the game state still matches its trigger condition, the ability will trigger again.
*
* @author BetaSteward_at_googlemail.com
*/
public abstract class StateTriggeredAbility extends TriggeredAbilityImpl {
@ -21,7 +30,8 @@ public abstract class StateTriggeredAbility extends TriggeredAbilityImpl {
}
public boolean canTrigger(Game game) {
//20100716 - 603.8
// 603.8 - A state-triggered ability doesn't trigger again until the ability has resolved,
// has been countered, or has otherwise left the stack
return !Boolean.TRUE.equals(game.getState().getValue(getSourceId().toString() + "triggered"));
}

View file

@ -465,6 +465,13 @@ public interface Game extends MageItem, Serializable, Copyable<Game> {
UUID fireReflexiveTriggeredAbility(ReflexiveTriggeredAbility reflexiveAbility, Ability source);
/**
* Inner game engine call to reset game objects to actual versions
* (reset all objects and apply all effects due layer system)
* <p>
* Warning, if you need to process object moves in the middle of the effect/ability
* then call game.getState().processAction(game) instead
*/
void applyEffects();
boolean checkStateAndTriggered();

View file

@ -655,10 +655,19 @@ public class GameState implements Serializable, Copyable<GameState> {
this.gameOver = true;
}
// 608.2e
/**
* Must be called between effects/steps in the ability's resolve
* <p>
* 608.2e
* Some spells and abilities have multiple steps or actions, denoted by separate sentences or clauses,
* that involve multiple players. In these cases, the choices for the first action are made in APNAP order,
* and then the first action is processed simultaneously. Then the choices for the second action are made in
* APNAP order, and then that action is processed simultaneously, and so on. See rule 101.4.
*/
public void processAction(Game game) {
game.getState().handleSimultaneousEvent(game);
game.applyEffects();
game.getState().getTriggers().checkStateTriggers(game);
}
public void applyEffects(Game game) {