Introduce Duration.UntilYourNextUpkeepStep (#10600)

* add new Duration

* refactor cards with new Duration.

* fix both Durations and add unit tests.

* fix text
This commit is contained in:
Susucre 2023-07-13 01:40:27 +02:00 committed by GitHub
parent 14235b6320
commit 6a9340f1aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 291 additions and 180 deletions

View file

@ -69,6 +69,8 @@ public interface ContinuousEffect extends Effect {
boolean isYourNextEndStep(Game game);
boolean isYourNextUpkeepStep(Game game);
@Override
void newId();

View file

@ -4,10 +4,6 @@ import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.CompoundAbility;
import mage.abilities.MageSingleton;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.DomainValue;
import mage.abilities.dynamicvalue.common.SignInversionDynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.keyword.ChangelingAbility;
import mage.constants.*;
import mage.filter.Filter;
@ -62,6 +58,10 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
private boolean startingTurnWasActive; // effect started during related players turn and related players turn was already active
private int effectStartingOnTurn = 0; // turn the effect started
private int effectStartingEndStep = 0;
private int nextTurnNumber = Integer.MAX_VALUE; // effect is waiting for a step during your next turn, we store it if found.
// set to the turn number on your next turn.
private int effectStartingStepNum = 0; // Some continuous are waiting for the next step of a kind.
// Avoid miscancelling if the start step is of that kind.
public ContinuousEffectImpl(Duration duration, Outcome outcome) {
super(outcome);
@ -96,6 +96,8 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
this.dependencyTypes = effect.dependencyTypes;
this.dependendToTypes = effect.dependendToTypes;
this.characterDefining = effect.characterDefining;
this.nextTurnNumber = effect.nextTurnNumber;
this.effectStartingStepNum = effect.effectStartingStepNum;
}
@Override
@ -215,6 +217,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
&& activePlayerId.equals(startingController); // you can't use "game" for active player cause it's called from tests/cheat too
this.effectStartingOnTurn = game.getTurnNum();
this.effectStartingEndStep = EndStepCountWatcher.getCount(startingController, game);
this.effectStartingStepNum = game.getState().getStepNum();
}
@Override
@ -228,10 +231,24 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
return EndStepCountWatcher.getCount(startingControllerId, game) > effectStartingEndStep;
}
public boolean isYourNextEndCombatStep(Game game) {
return effectStartingOnTurn < game.getTurnNum()
&& game.isActivePlayer(startingControllerId)
&& game.getPhase().getType() == TurnPhase.POSTCOMBAT_MAIN;
public boolean isEndCombatOfYourNextTurn(Game game) {
int currentTurn = game.getTurnNum();
if(nextTurnNumber != Integer.MAX_VALUE && nextTurnNumber < currentTurn){
return false; // This is a turn after your next turn.
}
if(nextTurnNumber == Integer.MAX_VALUE && isYourNextTurn(game)) {
nextTurnNumber = currentTurn;
}
return isYourNextTurn(game)
&& game.getPhase().getType() == TurnPhase.POSTCOMBAT_MAIN;
}
public boolean isYourNextUpkeepStep(Game game) {
return (effectStartingOnTurn < game.getTurnNum() ||
effectStartingStepNum < game.getState().getStepNum())
&& game.isActivePlayer(startingControllerId)
&& game.getStep().getType() == PhaseStep.UPKEEP;
}
@Override
@ -243,7 +260,8 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
case UntilYourNextTurn:
case UntilEndOfYourNextTurn:
case UntilYourNextEndStep:
case UntilYourNextEndCombatStep:
case UntilEndCombatOfYourNextTurn:
case UntilYourNextUpkeepStep:
break;
default:
return false;
@ -286,9 +304,14 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
return this.isYourNextEndStep(game);
}
break;
case UntilYourNextEndCombatStep:
case UntilEndCombatOfYourNextTurn:
if (player != null && player.isInGame()) {
return this.isYourNextEndCombatStep(game);
return this.isEndCombatOfYourNextTurn(game);
}
break;
case UntilYourNextUpkeepStep:
if (player != null && player.isInGame()) {
return this.isYourNextUpkeepStep(game);
}
break;
}

View file

@ -154,8 +154,9 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
case Custom:
case UntilYourNextTurn:
case UntilEndOfYourNextTurn:
case UntilYourNextEndCombatStep:
case UntilEndCombatOfYourNextTurn:
case UntilYourNextEndStep:
case UntilYourNextUpkeepStep:
// until your turn effects continue until real turn reached, their used it's own inactive method
// 514.2 Second, the following actions happen simultaneously: all damage marked on permanents
// (including phased-out permanents) is removed and all "until end of turn" and "this turn" effects end.

View file

@ -13,7 +13,8 @@ public enum Duration {
EndOfTurn("until end of turn", true, true),
UntilYourNextTurn("until your next turn", true, true),
UntilYourNextEndStep("until your next end step", true, true),
UntilYourNextEndCombatStep("until your next end of combat step", false, true),
UntilEndCombatOfYourNextTurn("until end of combat on your next turn", true, true),
UntilYourNextUpkeepStep("until your next upkeep", true, true),
UntilEndOfYourNextTurn("until the end of your next turn", true, true),
UntilSourceLeavesBattlefield("until {this} leaves the battlefield", true, false), // supported for continuous layered effects
EndOfCombat("until end of combat", true, true),

View file

@ -115,7 +115,7 @@ public interface Permanent extends Card, Controllable {
int getAttachedToZoneChangeCounter();
void attachTo(UUID permanentId, Ability source, Game game);
void attachTo(UUID attachToObjectId, Ability source, Game game);
void unattach(Game game);