mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 10:18:01 +00:00
@@ -265,7 +265,7 @@ public class AiController {
|
||||
}
|
||||
}
|
||||
|
||||
if (!re.requirementsCheck(game, AbilityKey.newMap())) {
|
||||
if (!re.requirementsCheck(game)) {
|
||||
continue;
|
||||
}
|
||||
SpellAbility exSA = re.getOverridingAbility();
|
||||
|
||||
@@ -96,7 +96,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||
if (!re.zonesCheck(getGame().getZoneOf(ca))) {
|
||||
continue;
|
||||
}
|
||||
if (!re.requirementsCheck(getGame(), AbilityKey.newMap())) {
|
||||
if (!re.requirementsCheck(getGame())) {
|
||||
continue;
|
||||
}
|
||||
// Immortal Coil prevents the damage but has a similar negative effect
|
||||
|
||||
@@ -140,10 +140,6 @@ public enum AbilityKey {
|
||||
Valiant("Valiant"),
|
||||
Won("Won"),
|
||||
|
||||
// for AI prediction
|
||||
Phase("Phase"),
|
||||
PlayerTurn("PlayerTurn"),
|
||||
|
||||
// below used across different Replacements, don't reuse
|
||||
InternalTriggerTable("InternalTriggerTable"),
|
||||
SimultaneousETB("SimultaneousETB"); // for CR 614.13c
|
||||
|
||||
@@ -1856,6 +1856,10 @@ public class AbilityUtils {
|
||||
return doXMath(list.size(), expr, c, ctb);
|
||||
}
|
||||
|
||||
if (sq[0].equals("ActivatedThisGame")) {
|
||||
return doXMath(sa.getActivationsThisGame(), expr, c, ctb);
|
||||
}
|
||||
|
||||
if (sq[0].equals("ResolvedThisTurn")) {
|
||||
return doXMath(sa.getResolvedThisTurn(), expr, c, ctb);
|
||||
}
|
||||
|
||||
@@ -65,8 +65,7 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
|
||||
ctype = CounterType.getType(sa.getParam("CounterType"));
|
||||
}
|
||||
|
||||
final Player pl = !sa.hasParam("DefinedPlayer") ? sa.getActivatingPlayer() :
|
||||
AbilityUtils.getDefinedPlayers(source, sa.getParam("DefinedPlayer"), sa).getFirst();
|
||||
final Player pl = AbilityUtils.getDefinedPlayers(source, sa.getParam("DefinedPlayer"), sa).getFirst();
|
||||
final boolean eachExisting = sa.hasParam("EachExistingCounter");
|
||||
|
||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
@@ -79,7 +78,7 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
|
||||
if (gameCard == null || !tgtCard.equalsWithGameTimestamp(gameCard)) {
|
||||
continue;
|
||||
}
|
||||
if (!eachExisting && sa.hasParam("Optional") && !pl.getController().confirmAction(sa, null,
|
||||
if (sa.hasParam("Optional") && !pl.getController().confirmAction(sa, null,
|
||||
Localizer.getInstance().getMessage("lblWouldYouLikePutRemoveCounters", ctype.getName(),
|
||||
CardTranslation.getTranslatedName(gameCard.getName())), null)) {
|
||||
continue;
|
||||
@@ -114,8 +113,6 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
|
||||
String prompt = Localizer.getInstance().getMessage("lblSelectCounterTypeToAddOrRemove");
|
||||
CounterType chosenType = pc.chooseCounterType(list, sa, prompt, params);
|
||||
|
||||
params.put("CounterType", chosenType);
|
||||
prompt = Localizer.getInstance().getMessage("lblWhatToDoWithTargetCounter", chosenType.getName(), CardTranslation.getTranslatedName(tgtCard.getName())) + " ";
|
||||
boolean putCounter;
|
||||
if (sa.hasParam("RemoveConditionSVar")) {
|
||||
final Card host = sa.getHostCard();
|
||||
@@ -137,6 +134,8 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
|
||||
} else if (!canReceive && canRemove) {
|
||||
putCounter = false;
|
||||
} else {
|
||||
params.put("CounterType", chosenType);
|
||||
prompt = Localizer.getInstance().getMessage("lblWhatToDoWithTargetCounter", chosenType.getName(), CardTranslation.getTranslatedName(tgtCard.getName())) + " ";
|
||||
putCounter = pc.chooseBinary(sa, prompt, BinaryChoiceType.AddOrRemove, params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ import org.apache.commons.lang3.tuple.Triple;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
|
||||
@@ -4912,22 +4913,23 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
return true;
|
||||
}
|
||||
|
||||
public final boolean canUntap(Player phase, boolean predict) {
|
||||
if (!tapped) { return false; }
|
||||
public final boolean canUntap(Player phase, Boolean predict) {
|
||||
if (predict != null && predict) {
|
||||
FutureTask<Boolean> proc = new FutureTask<>(() -> {
|
||||
return canUntap(phase, null);
|
||||
});
|
||||
return getGame().getPhaseHandler().withContext(proc, phase, PhaseType.UNTAP);
|
||||
}
|
||||
if (predict != null && !tapped) { return false; }
|
||||
if (phase != null && isExertedBy(phase)) {
|
||||
return false;
|
||||
}
|
||||
if (phase != null &&
|
||||
(hasKeyword("CARDNAME doesn't untap during your untap step.")
|
||||
|| hasKeyword("This card doesn't untap during your next untap step.")
|
||||
|| hasKeyword("This card doesn't untap during your next two untap steps."))) {
|
||||
|| hasKeyword("This card doesn't untap during your next untap step."))) {
|
||||
return false;
|
||||
}
|
||||
Map<AbilityKey, Object> runParams = AbilityKey.mapFromAffected(this);
|
||||
if (predict) {
|
||||
runParams.put(AbilityKey.PlayerTurn, phase);
|
||||
runParams.put(AbilityKey.Phase, PhaseType.UNTAP);
|
||||
}
|
||||
return !getGame().getReplacementHandler().cantHappenCheck(ReplacementType.Untap, runParams);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.time.StopWatch;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
|
||||
/**
|
||||
@@ -137,6 +139,23 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
setPriority(playerTurn);
|
||||
}
|
||||
|
||||
public <T> T withContext(FutureTask<T> original, Player active, PhaseType pt) {
|
||||
Player oldTurn = playerTurn;
|
||||
PhaseType oldPhase = phase;
|
||||
playerTurn = active;
|
||||
phase = pt;
|
||||
original.run();
|
||||
try {
|
||||
return original.get();
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
playerTurn = oldTurn;
|
||||
phase = oldPhase;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public final boolean inCombat() { return combat != null; }
|
||||
public final Combat getCombat() { return combat; }
|
||||
|
||||
@@ -185,9 +204,7 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
playerTurn.setNumPowerSurgeLands(lands);
|
||||
}
|
||||
|
||||
// Replacement effects
|
||||
final Map<AbilityKey, Object> repRunParams = AbilityKey.mapFromAffected(playerTurn);
|
||||
repRunParams.put(AbilityKey.Phase, phase);
|
||||
ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.BeginPhase, repRunParams);
|
||||
if (repres != ReplacementResult.NotReplaced) {
|
||||
// Currently there is no effect to skip entire beginning phase
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.game.Game;
|
||||
@@ -164,10 +163,6 @@ public class Untap extends Phase {
|
||||
// TODO Replace with Static Abilities
|
||||
for (final Card c : active.getCardsIn(ZoneType.Battlefield)) {
|
||||
c.removeHiddenExtrinsicKeyword("This card doesn't untap during your next untap step.");
|
||||
if (c.hasKeyword("This card doesn't untap during your next two untap steps.")) {
|
||||
c.removeHiddenExtrinsicKeyword("This card doesn't untap during your next two untap steps.");
|
||||
c.addHiddenExtrinsicKeywords(game.getNextTimestamp(), 0, Lists.newArrayList("This card doesn't untap during your next untap step."));
|
||||
}
|
||||
}
|
||||
|
||||
// remove exerted flags from all things in play
|
||||
|
||||
@@ -154,28 +154,26 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
public boolean requirementsCheck(Game game, Map<AbilityKey, Object> runParams) {
|
||||
public boolean requirementsCheck(Game game) {
|
||||
if (this.isSuppressed()) {
|
||||
return false; // Effect removed by effect
|
||||
}
|
||||
|
||||
if (hasParam("PlayerTurn")) {
|
||||
Player active = (Player) runParams.getOrDefault(AbilityKey.PlayerTurn, game.getPhaseHandler().getPlayerTurn());
|
||||
if (getParam("PlayerTurn").equals("True")) {
|
||||
if (!active.equals(getHostCard().getController())) {
|
||||
if (!game.getPhaseHandler().isPlayerTurn(getHostCard().getController())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
List<Player> players = AbilityUtils.getDefinedPlayers(getHostCard(), getParam("PlayerTurn"), this);
|
||||
if (!players.contains(active)) {
|
||||
if (!players.contains(game.getPhaseHandler().getPlayerTurn())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasParam("ActivePhases")) {
|
||||
PhaseType phase = (PhaseType) runParams.getOrDefault(AbilityKey.Phase, game.getPhaseHandler().getPhase());
|
||||
if (!PhaseType.parseRange(getParam("ActivePhases")).contains(phase)) {
|
||||
if (!PhaseType.parseRange(getParam("ActivePhases")).contains(game.getPhaseHandler().getPhase())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package forge.game.replacement;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import forge.game.card.*;
|
||||
@@ -127,7 +128,7 @@ public class ReplacementHandler {
|
||||
&& replacementEffect.modeCheck(event, runParams)
|
||||
&& !possibleReplacers.contains(replacementEffect)
|
||||
&& replacementEffect.zonesCheck(cardZone)
|
||||
&& replacementEffect.requirementsCheck(game, runParams)
|
||||
&& replacementEffect.requirementsCheck(game)
|
||||
&& replacementEffect.canReplace(runParams)) {
|
||||
possibleReplacers.add(replacementEffect);
|
||||
}
|
||||
@@ -855,15 +856,17 @@ public class ReplacementHandler {
|
||||
* Helper function to check if a phase would be skipped for AI.
|
||||
*/
|
||||
public boolean wouldPhaseBeSkipped(final Player player, final PhaseType phase) {
|
||||
final Map<AbilityKey, Object> repParams = AbilityKey.newMap();
|
||||
repParams.put(AbilityKey.PlayerTurn, player);
|
||||
repParams.put(AbilityKey.Phase, phase);
|
||||
List<ReplacementEffect> list = getReplacementList(ReplacementType.BeginPhase, repParams, ReplacementLayer.Control);
|
||||
if (list.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
FutureTask<Boolean> proc = new FutureTask<>(() -> {
|
||||
final Map<AbilityKey, Object> repParams = AbilityKey.newMap();
|
||||
List<ReplacementEffect> list = getReplacementList(ReplacementType.BeginPhase, repParams, ReplacementLayer.Control);
|
||||
if (list.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return player.getGame().getPhaseHandler().withContext(proc, player, phase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to check if an extra turn would be skipped for AI.
|
||||
*/
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:1 U U
|
||||
Types:Creature Human Wizard
|
||||
PT:1/2
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ Opponent | Execute$ TrigSkipPhase | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of each opponent's upkeep, that player chooses draw step, main phase, or combat phase. The player skips each instance of the chosen step or phase this turn.
|
||||
SVar:TrigSkipPhase:DB$ GenericChoice | Defined$ TriggeredPlayer | Choices$ FatespinnerSkipDraw,FatespinnerSkipMain,FatespinnerSkipCombat | ShowChoice$ ExceptSelf | AILogic$ Random
|
||||
SVar:TrigSkipPhase:DB$ GenericChoice | Defined$ TriggeredPlayer | Choices$ FatespinnerSkipDraw,FatespinnerSkipMain,FatespinnerSkipCombat | ShowChoice$ ExceptSelf | AILogic$ Fatespinner
|
||||
SVar:FatespinnerSkipDraw:DB$ SkipPhase | Defined$ TriggeredPlayer | Step$ Draw | Duration$ EndOfTurn | SpellDescription$ Draw step
|
||||
SVar:FatespinnerSkipMain:DB$ SkipPhase | Defined$ TriggeredPlayer | Phase$ Main | Duration$ EndOfTurn | SpellDescription$ Main phase
|
||||
SVar:FatespinnerSkipCombat:DB$ SkipPhase | Defined$ TriggeredPlayer | Phase$ BeginCombat | Duration$ EndOfTurn | SpellDescription$ Combat phase
|
||||
|
||||
@@ -3,6 +3,9 @@ ManaCost:U U
|
||||
Types:Instant
|
||||
A:SP$ Tap | ValidTgts$ Creature | SubAbility$ DBEffect | SpellDescription$ Tap target creature.
|
||||
SVar:DBEffect:DB$ Effect | ReplacementEffects$ RPrevent | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | SubAbility$ DBPump | SpellDescription$ Prevent all combat damage that would be dealt by that creature this turn.
|
||||
SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ HIDDEN This card doesn't untap during your next two untap steps. | Duration$ Permanent | SpellDescription$ It doesn't untap during its controller's next two untap steps. | StackDescription$ SpellDescription
|
||||
SVar:DBPump:DB$ Effect | ReplacementEffects$ RUntap | Triggers$ ExileEff | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | Duration$ Permanent | StackDescription$ SpellDescription
|
||||
SVar:RPrevent:Event$ DamageDone | Prevent$ True | IsCombat$ True | ValidSource$ Card.IsRemembered | Description$ Prevent all combat damage that would be dealt by that creature this turn.
|
||||
SVar:RUntap:Event$ Untap | ValidCard$ Creature.IsRemembered+ActivePlayerCtrl | Layer$ CantHappen | ActivePhases$ Untap | Description$ It doesn't untap during its controller's next two untap steps.
|
||||
SVar:ExileEff:Mode$ Phase | Phase$ Untap | ValidPlayer$ Player.controlsCard.IsRemembered | Execute$ Exile | Static$ True
|
||||
SVar:Exile:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile | ConditionCheckSVar$ Count$ActivatedThisGame
|
||||
Oracle:Tap target creature. Prevent all combat damage that would be dealt by that creature this turn. It doesn't untap during its controller's next two untap steps.
|
||||
|
||||
@@ -6,7 +6,7 @@ SVar:GainControl:DB$ GainControl | ValidTgts$ Creature.nonLegendary+OppCtrl | Lo
|
||||
SVar:OneEach:PlayerCountOpponents$Amount
|
||||
SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ X | SpellDescription$ Draw a card for each creature you control but don't own.
|
||||
SVar:X:Count$Valid Creature.YouDontOwn+YouCtrl
|
||||
SVar:DBChoose:DB$ ChooseCard | Defined$ Player | StartingWith$ You | Choices$ Creature | ChoiceTitle$ Choose a creature | Mandatory$ True | SpellDescription$ Starting with you, each player chooses a creature. Destroy each creature chosen this way.
|
||||
SVar:DBChoose:DB$ ChooseCard | Defined$ Player | StartingWith$ You | Choices$ Creature | ChoiceTitle$ Choose a creature | Mandatory$ True | SubAbility$ DBDestroy | SpellDescription$ Starting with you, each player chooses a creature. Destroy each creature chosen this way.
|
||||
SVar:DBDestroy:DB$ Destroy | Defined$ ChosenCard | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
|
||||
Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI — For each opponent, gain control of up to one target nonlegendary creature that player controls for as long as The Horus Heresy remains on the battlefield.\nII — Draw a card for each creature you control but don't own.\nIII — Starting with you, each player chooses a creature. Destroy each creature chosen this way.
|
||||
|
||||
Reference in New Issue
Block a user