mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-14 09:48:02 +00:00
Re-implement skip turn using replacement effect
This commit is contained in:
@@ -71,6 +71,8 @@ public class UntapAi extends SpellAbilityAi {
|
||||
if (!sa.usesTargeting()) {
|
||||
if (mandatory) {
|
||||
return true;
|
||||
} else if ("Never".equals(sa.getParam("AILogic"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: use Defined to determine, if this is an unfavorable result
|
||||
|
||||
@@ -61,6 +61,7 @@ public enum AbilityKey {
|
||||
EffectOnly("EffectOnly"),
|
||||
Exploited("Exploited"),
|
||||
Explorer("Explorer"),
|
||||
ExtraTurn("ExtraTurn"),
|
||||
Event("Event"),
|
||||
Fighter("Fighter"),
|
||||
Fighters("Fighters"),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -8,5 +8,6 @@ public enum ReplacementResult {
|
||||
Replaced,
|
||||
NotReplaced,
|
||||
Prevented,
|
||||
Updated
|
||||
Updated,
|
||||
Skipped
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -2,7 +2,7 @@ Name:Stranglehold
|
||||
ManaCost:3 R
|
||||
Types:Enchantment
|
||||
S:Mode$ Continuous | Affected$ Opponent | AddKeyword$ CantSearchLibrary | Description$ Your opponents can't search libraries.
|
||||
S:Mode$ Continuous | Affected$ Opponent | AddKeyword$ If you would begin an extra turn, skip that turn instead. | Description$ If an opponent would begin an extra turn, that player skips that turn instead.
|
||||
R:Event$ BeginTurn | ActiveZones$ Battlefield | ValidPlayer$ Opponent | ExtraTurn$ True | Skip$ True | Description$ If an opponent would begin an extra turn, that player skips that turn instead.
|
||||
SVar:NonStackingEffect:True
|
||||
AI:RemoveDeck:Random
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/stranglehold.jpg
|
||||
Oracle:Your opponents can't search libraries.\nIf an opponent would begin an extra turn, that player skips that turn instead.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Name:Time Vault
|
||||
ManaCost:2
|
||||
Types:Artifact
|
||||
Text:If you would begin your turn while CARDNAME is tapped, you may skip that turn instead. If you do, untap CARDNAME.
|
||||
K:CARDNAME doesn't untap during your untap step.
|
||||
K:CARDNAME enters the battlefield tapped.
|
||||
K:CARDNAME doesn't untap during your untap step.
|
||||
R:Event$ BeginTurn | ActiveZones$ Battlefield | ValidPlayer$ You | IsPresent$ Card.Self+tapped | Optional$ True | ReplaceWith$ DBUntap | Description$ If you would begin your turn while CARDNAME is tapped, you may skip that turn instead. If you do, untap CARDNAME.
|
||||
SVar:DBUntap:DB$ Untap | Defined$ Self | AILogic$ Never
|
||||
A:AB$ AddTurn | Cost$ T | NumTurns$ 1 | SpellDescription$ Take an extra turn after this one.
|
||||
SVar:PlayMain1:ALWAYS
|
||||
AI:RemoveDeck:Random
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/time_vault.jpg
|
||||
Oracle:Time Vault enters the battlefield tapped.\nTime Vault doesn't untap during your untap step.\nIf you would begin your turn while Time Vault is tapped, you may skip that turn instead. If you do, untap Time Vault.\n{T}: Take an extra turn after this one.
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
Name:Ugin's Nexus
|
||||
ManaCost:5
|
||||
Types:Legendary Artifact
|
||||
S:Mode$ Continuous | Affected$ Player | AddKeyword$ If you would begin an extra turn, skip that turn instead. | Description$ If a player would begin an extra turn, that player skips that turn instead.
|
||||
R:Event$ BeginTurn | ActiveZones$ Battlefield | ExtraTurn$ True | Skip$ True | Description$ If a player would begin an extra turn, that player skips that turn instead.
|
||||
R:Event$ Moved | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | ReplaceWith$ DBExile | Description$ If CARDNAME would be put into a graveyard from the battlefield, instead exile it and take an extra turn after this one.
|
||||
SVar:DBExile:DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Battlefield | Destination$ Exile | SubAbility$ DBAddTurn
|
||||
SVar:DBAddTurn:DB$ AddTurn | Defined$ ReplacedCardLKIController | NumTurns$ 1
|
||||
AI:RemoveDeck:Random
|
||||
SVar:SacMe:5
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/ugins_nexus.jpg
|
||||
Oracle:If a player would begin an extra turn, that player skips that turn instead.\nIf Ugin's Nexus would be put into a graveyard from the battlefield, instead exile it and take an extra turn after this one.
|
||||
|
||||
Reference in New Issue
Block a user