Re-implement skip turn using replacement effect

This commit is contained in:
Alumi
2021-03-14 04:53:55 +00:00
committed by Michael Kamensky
parent 84aaccc3da
commit c5ad28a65e
11 changed files with 101 additions and 52 deletions

View File

@@ -61,6 +61,7 @@ public enum AbilityKey {
EffectOnly("EffectOnly"),
Exploited("Exploited"),
Explorer("Explorer"),
ExtraTurn("ExtraTurn"),
Event("Event"),
Fighter("Fighter"),
Fighters("Fighters"),

View File

@@ -1,9 +1,19 @@
package forge.game.ability.effects;
import forge.game.Game;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect;
import forge.game.replacement.ReplacementHandler;
import forge.game.replacement.ReplacementLayer;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.util.Lang;
import java.util.List;
@@ -25,12 +35,33 @@ public class SkipTurnEffect extends SpellAbilityEffect {
@Override
public void resolve(SpellAbility sa) {
final Card hostCard = sa.getHostCard();
final Game game = hostCard.getGame();
final String name = hostCard.getName() + "'s Effect";
final String image = hostCard.getImageKey();
final int numTurns = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumTurns"), sa);
String repeffstr = "Event$ BeginTurn | ActiveZones$ Command | ValidPlayer$ You " +
"| Description$ Skip your next " + (numTurns > 1 ? Lang.getNumeral(numTurns) + " turns." : "turn.");
String effect = "DB$ StoreSVar | SVar$ NumTurns | Type$ CountSVar | Expression$ NumTurns/Minus.1";
String exile = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile " +
"| ConditionCheckSVar$ NumTurns | ConditionSVarCompare$ EQ0";
List<Player> tgtPlayers = getTargetPlayers(sa);
for (final Player player : tgtPlayers) {
for(int i = 0; i < numTurns; i++) {
player.addKeyword("Skip your next turn.");
}
final Card eff = createEffect(sa, player, name, image);
eff.setSVar("NumTurns", "Number$" + numTurns);
SpellAbility calcTurn = AbilityFactory.getAbility(effect, eff);
calcTurn.setSubAbility((AbilitySub) AbilityFactory.getAbility(exile, eff));
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
re.setLayer(ReplacementLayer.Other);
re.setOverridingAbility(calcTurn);
eff.addReplacementEffect(re);
eff.updateStateForView();
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
game.getAction().moveTo(ZoneType.Command, eff, sa);
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
}
}
}

View File

@@ -36,8 +36,9 @@ import forge.game.cost.Cost;
import forge.game.event.*;
import forge.game.keyword.Keyword;
import forge.game.player.Player;
import forge.game.player.PlayerController.BinaryChoiceType;
import forge.game.player.PlayerController.ManaPaymentPurpose;
import forge.game.replacement.ReplacementResult;
import forge.game.replacement.ReplacementType;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.LandAbility;
import forge.game.staticability.StaticAbility;
@@ -837,55 +838,28 @@ public class PhaseHandler implements java.io.Serializable {
private Player getNextActivePlayer() {
ExtraTurn extraTurn = !extraTurns.isEmpty() ? extraTurns.pop() : null;
Player nextPlayer = extraTurn != null ? extraTurn.getPlayer() : game.getNextPlayerAfter(playerTurn);
// The bottom of the extra turn stack is the normal turn
boolean isExtraTurn = !extraTurns.isEmpty();
// update ExtraTurn Count for all players
for (final Player p : game.getPlayers()) {
p.setExtraTurnCount(getExtraTurnForPlayer(p));
}
// update ExtraTurn Count
nextPlayer.setExtraTurnCount(getExtraTurnForPlayer(nextPlayer));
if (extraTurn != null) {
// The bottom of the extra turn stack is the normal turn
nextPlayer.setExtraTurn(!extraTurns.isEmpty());
if (nextPlayer.hasKeyword("If you would begin an extra turn, skip that turn instead.")) {
return getNextActivePlayer();
}
for (Trigger deltrig : extraTurn.getDelayedTriggers()) {
game.getTriggerHandler().registerThisTurnDelayedTrigger(deltrig);
}
}
else {
nextPlayer.setExtraTurn(false);
}
if (nextPlayer.hasKeyword("Skip your next turn.")) {
nextPlayer.removeKeyword("Skip your next turn.", false);
// Replacement effects
final Map<AbilityKey, Object> repRunParams = AbilityKey.mapFromAffected(nextPlayer);
repRunParams.put(AbilityKey.ExtraTurn, isExtraTurn);
ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.BeginTurn, repRunParams);
if (repres != ReplacementResult.NotReplaced) {
if (extraTurn == null) {
setPlayerTurn(nextPlayer);
}
return getNextActivePlayer();
}
// TODO: This shouldn't filter by Time Vault, just in case Time Vault doesn't have it's normal ability.
CardCollection vaults = CardLists.filter(nextPlayer.getCardsIn(ZoneType.Battlefield, "Time Vault"), Presets.TAPPED);
if (!vaults.isEmpty()) {
Card crd = vaults.getFirst();
SpellAbility fakeSA = new SpellAbility.EmptySa(crd, nextPlayer);
boolean untapTimeVault = nextPlayer.getController().chooseBinary(fakeSA, "Skip a turn to untap a Time Vault?", BinaryChoiceType.UntapTimeVault, false);
if (untapTimeVault) {
if (vaults.size() > 1) {
Card c = nextPlayer.getController().chooseSingleEntityForEffect(vaults, fakeSA, "Which Time Vault do you want to Untap?", null);
if (c != null)
crd = c;
}
crd.untap();
if (extraTurn == null) {
setPlayerTurn(nextPlayer);
}
return getNextActivePlayer();
}
}
nextPlayer.setExtraTurn(isExtraTurn);
if (extraTurn != null) {
for (Trigger deltrig : extraTurn.getDelayedTriggers()) {
game.getTriggerHandler().registerThisTurnDelayedTrigger(deltrig);
}
if (extraTurn.isSkipUntap()) {
nextPlayer.addKeyword("Skip the untap step of this turn.");
}

View File

@@ -0,0 +1,34 @@
package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.spellability.SpellAbility;
import java.util.Map;
public class ReplaceBeginTurn extends ReplacementEffect {
public ReplaceBeginTurn(final Map<String, String> mapParams, final Card host, final boolean intrinsic) {
super(mapParams, host, intrinsic);
}
@Override
public boolean canReplace(Map<AbilityKey, Object> runParams) {
if (hasParam("ValidPlayer")) {
if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidPlayer").split(","), getHostCard())) {
return false;
}
}
if (hasParam("ExtraTurn")) {
if (!(boolean) runParams.get(AbilityKey.ExtraTurn)) {
return false;
}
}
return true;
}
@Override
public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject(AbilityKey.Player, runParams.get(AbilityKey.Affected));
}
}

View File

@@ -349,6 +349,12 @@ public class ReplacementHandler {
}
}
if (mapParams.containsKey("Skip")) {
if (mapParams.get("Skip").equals("True")) {
return ReplacementResult.Skipped; // Event is skipped.
}
}
Player player = host.getController();
player.getController().playSpellAbilityNoStack(effectSA, true);

View File

@@ -8,5 +8,6 @@ public enum ReplacementResult {
Replaced,
NotReplaced,
Prevented,
Updated
Updated,
Skipped
}

View File

@@ -14,6 +14,7 @@ public enum ReplacementType {
AddCounter(ReplaceAddCounter.class),
AssignDealDamage(ReplaceAssignDealDamage.class),
Attached(ReplaceAttached.class),
BeginTurn(ReplaceBeginTurn.class),
Counter(ReplaceCounter.class),
CopySpell(ReplaceCopySpell.class),
CreateToken(ReplaceToken.class),