mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
Merge branch 'refactor_fog_protection' into 'master'
Refactor Damage Prevention - Step 3 See merge request core-developers/forge!4680
This commit is contained in:
@@ -79,7 +79,7 @@ public class EffectAi extends SpellAbilityAi {
|
|||||||
if (!game.getStack().isEmpty()) {
|
if (!game.getStack().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (game.getPhaseHandler().isPreventCombatDamageThisTurn()) {
|
if (game.getReplacementHandler().isPreventCombatDamageThisTurn()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ComputerUtilCombat.lifeInDanger(ai, game.getCombat())) {
|
if (!ComputerUtilCombat.lifeInDanger(ai, game.getCombat())) {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class FogAi extends SpellAbilityAi {
|
|||||||
final Card hostCard = sa.getHostCard();
|
final Card hostCard = sa.getHostCard();
|
||||||
|
|
||||||
// Don't cast it, if the effect is already in place
|
// Don't cast it, if the effect is already in place
|
||||||
if (game.getPhaseHandler().isPreventCombatDamageThisTurn()) {
|
if (game.getReplacementHandler().isPreventCombatDamageThisTurn()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -480,7 +480,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}); // leaves all creatures that will be destroyed
|
}); // leaves all creatures that will be destroyed
|
||||||
} // -X/-X end
|
} // -X/-X end
|
||||||
else if (attack < 0 && !game.getPhaseHandler().isPreventCombatDamageThisTurn()) {
|
else if (attack < 0 && !game.getReplacementHandler().isPreventCombatDamageThisTurn()) {
|
||||||
// spells that give -X/0
|
// spells that give -X/0
|
||||||
boolean isMyTurn = game.getPhaseHandler().isPlayerTurn(ai);
|
boolean isMyTurn = game.getPhaseHandler().isPlayerTurn(ai);
|
||||||
if (isMyTurn) {
|
if (isMyTurn) {
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ public class PumpAllAi extends PumpAiBase {
|
|||||||
if (phase.isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
if (phase.isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||||
|| phase.isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
|| phase.isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||||
|| game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())
|
|| game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())
|
||||||
|| game.getPhaseHandler().isPreventCombatDamageThisTurn()) {
|
|| game.getReplacementHandler().isPreventCombatDamageThisTurn()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int totalPower = 0;
|
int totalPower = 0;
|
||||||
|
|||||||
@@ -187,9 +187,6 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
restDamage = 0;
|
restDamage = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// then apply static Damage Prevention effects
|
|
||||||
restDamage = staticDamagePrevention(restDamage, source, isCombat, false);
|
|
||||||
|
|
||||||
// if damage is greater than restDamage, damage was prevented
|
// if damage is greater than restDamage, damage was prevented
|
||||||
if (damage > restDamage) {
|
if (damage > restDamage) {
|
||||||
int prevent = damage - restDamage;
|
int prevent = damage - restDamage;
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
|
import forge.GameCommand;
|
||||||
|
import forge.game.Game;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
|
import forge.game.card.Card;
|
||||||
|
import forge.game.replacement.ReplacementEffect;
|
||||||
|
import forge.game.replacement.ReplacementHandler;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.trigger.TriggerType;
|
||||||
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
public class FogEffect extends SpellAbilityEffect {
|
public class FogEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
@@ -13,7 +20,28 @@ public class FogEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
// Expand Fog keyword here depending on what we need out of it.
|
final Card hostCard = sa.getHostCard();
|
||||||
sa.getActivatingPlayer().getGame().getPhaseHandler().setPreventCombatDamageThisTurn();
|
final Game game = hostCard.getGame();
|
||||||
|
final String name = hostCard.getName() + "'s Effect";
|
||||||
|
final String image = hostCard.getImageKey();
|
||||||
|
StringBuilder sb = new StringBuilder("Event$ DamageDone | ActiveZones$ Command | IsCombat$ True");
|
||||||
|
sb.append(" | Prevent$ True | Description$ Prevent all combat damage this turn.");
|
||||||
|
String repeffstr = sb.toString();
|
||||||
|
|
||||||
|
final Card eff = createEffect(sa, hostCard.getController(), name, image);
|
||||||
|
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
||||||
|
eff.addReplacementEffect(re);
|
||||||
|
eff.updateStateForView();
|
||||||
|
|
||||||
|
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||||
|
game.getAction().moveTo(ZoneType.Command, eff, sa);
|
||||||
|
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||||
|
|
||||||
|
game.getEndOfTurn().addUntil(new GameCommand() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
game.getAction().exile(eff, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5212,7 +5212,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
return damageIn;
|
return damageIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCombat && getGame().getPhaseHandler().isPreventCombatDamageThisTurn()) {
|
if (isCombat && getGame().getReplacementHandler().isPreventCombatDamageThisTurn()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2228,6 +2228,59 @@ public class CardFactoryUtil {
|
|||||||
return re;
|
return re;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create damage prevention replacement effect for protection keyword
|
||||||
|
private static ReplacementEffect createProtectionReplacement(final CardState card, final String kw, final boolean intrinsic) {
|
||||||
|
Card host = card.getCard();
|
||||||
|
String validSource = "Card.";
|
||||||
|
|
||||||
|
if (kw.startsWith("Protection:")) {
|
||||||
|
final String[] kws = kw.split(":");
|
||||||
|
String characteristic = kws[1];
|
||||||
|
if (characteristic.startsWith("Player")) {
|
||||||
|
validSource += "ControlledBy " + characteristic;
|
||||||
|
} else {
|
||||||
|
if (characteristic.endsWith("White") || characteristic.endsWith("Blue")
|
||||||
|
|| characteristic.endsWith("Black") || characteristic.endsWith("Red")
|
||||||
|
|| characteristic.endsWith("Green") || characteristic.endsWith("Colorless")
|
||||||
|
|| characteristic.endsWith("MonoColor") || characteristic.endsWith("MultiColor")) {
|
||||||
|
characteristic += "Source";
|
||||||
|
}
|
||||||
|
validSource = characteristic;
|
||||||
|
}
|
||||||
|
} else if (kw.startsWith("Protection from ")) {
|
||||||
|
String protectType = kw.substring("Protection from ".length());
|
||||||
|
if (protectType.equals("white")) {
|
||||||
|
validSource += "WhiteSource";
|
||||||
|
} else if (protectType.equals("blue")) {
|
||||||
|
validSource += "BlueSource";
|
||||||
|
} else if (protectType.equals("black")) {
|
||||||
|
validSource += "BlackSource";
|
||||||
|
} else if (protectType.equals("red")) {
|
||||||
|
validSource += "RedSource";
|
||||||
|
} else if (protectType.equals("green")) {
|
||||||
|
validSource += "GreenSource";
|
||||||
|
} else if (protectType.equals("all colors")) {
|
||||||
|
validSource += "nonColorless";
|
||||||
|
} else if (protectType.equals("everything")) {
|
||||||
|
validSource = "";
|
||||||
|
} else if (protectType.startsWith("opponent of ")) {
|
||||||
|
final String playerName = protectType.substring("opponent of ".length());
|
||||||
|
validSource += "ControlledBy Player.OpponentOf PlayerNamed_" + playerName;
|
||||||
|
} else {
|
||||||
|
validSource = CardType.getSingularType(protectType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String rep = "Event$ DamageDone | Prevent$ True | ActiveZones$ Battlefield | ValidTarget$ Card.Self";
|
||||||
|
if (!validSource.isEmpty()) {
|
||||||
|
rep += " | ValidSource$ " + validSource;
|
||||||
|
}
|
||||||
|
rep += " | Secondary$ True | TiedToKeyword$ " + kw + " | Description$ " + kw;
|
||||||
|
|
||||||
|
ReplacementEffect re = ReplacementHandler.parseReplacement(rep, host, intrinsic, card);
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
public static ReplacementEffect makeEtbCounter(final String kw, final CardState card, final boolean intrinsic)
|
public static ReplacementEffect makeEtbCounter(final String kw, final CardState card, final boolean intrinsic)
|
||||||
{
|
{
|
||||||
String parse = kw;
|
String parse = kw;
|
||||||
@@ -3915,6 +3968,10 @@ public class CardFactoryUtil {
|
|||||||
inst.addReplacement(re);
|
inst.addReplacement(re);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (keyword.startsWith("Protection")) {
|
||||||
|
ReplacementEffect re = createProtectionReplacement(card, keyword, intrinsic);
|
||||||
|
inst.addReplacement(re);
|
||||||
|
}
|
||||||
|
|
||||||
else if (keyword.startsWith("If CARDNAME would be put into a graveyard "
|
else if (keyword.startsWith("If CARDNAME would be put into a graveyard "
|
||||||
+ "from anywhere, reveal CARDNAME and shuffle it into its owner's library instead.")) {
|
+ "from anywhere, reveal CARDNAME and shuffle it into its owner's library instead.")) {
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
private int nUpkeepsThisGame = 0;
|
private int nUpkeepsThisGame = 0;
|
||||||
private int nCombatsThisTurn = 0;
|
private int nCombatsThisTurn = 0;
|
||||||
private int nMain2sThisTurn = 0;
|
private int nMain2sThisTurn = 0;
|
||||||
private boolean bPreventCombatDamageThisTurn = false;
|
|
||||||
private int planarDiceRolledthisTurn = 0;
|
private int planarDiceRolledthisTurn = 0;
|
||||||
|
|
||||||
private transient Player playerTurn = null;
|
private transient Player playerTurn = null;
|
||||||
@@ -520,7 +519,6 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CLEANUP:
|
case CLEANUP:
|
||||||
bPreventCombatDamageThisTurn = false;
|
|
||||||
if (!bRepeatCleanup) {
|
if (!bRepeatCleanup) {
|
||||||
// only call onCleanupPhase when Cleanup is not repeated
|
// only call onCleanupPhase when Cleanup is not repeated
|
||||||
game.onCleanupPhase();
|
game.onCleanupPhase();
|
||||||
@@ -834,10 +832,6 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
return noCost || blocker.getController().getController().payManaOptional(blocker, blockCost, fakeSA, "Pay cost to declare " + blocker + " a blocker. ", ManaPaymentPurpose.DeclareBlocker);
|
return noCost || blocker.getController().getController().payManaOptional(blocker, blockCost, fakeSA, "Pay cost to declare " + blocker + " a blocker. ", ManaPaymentPurpose.DeclareBlocker);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isPreventCombatDamageThisTurn() {
|
|
||||||
return bPreventCombatDamageThisTurn;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Player handleNextTurn() {
|
private Player handleNextTurn() {
|
||||||
game.getStack().onNextTurn();
|
game.getStack().onNextTurn();
|
||||||
// reset mustAttackEntity
|
// reset mustAttackEntity
|
||||||
@@ -1222,10 +1216,6 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
onPhaseBegin();
|
onPhaseBegin();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setPreventCombatDamageThisTurn() {
|
|
||||||
bPreventCombatDamageThisTurn = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPlanarDiceRolledthisTurn() {
|
public int getPlanarDiceRolledthisTurn() {
|
||||||
return planarDiceRolledthisTurn;
|
return planarDiceRolledthisTurn;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -719,7 +719,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return damage;
|
return damage;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCombat && game.getPhaseHandler().isPreventCombatDamageThisTurn()) {
|
if (isCombat && game.getReplacementHandler().isPreventCombatDamageThisTurn()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1195,7 +1195,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
|
|
||||||
|
|
||||||
public boolean hasProtectionFromDamage(final Card source) {
|
public boolean hasProtectionFromDamage(final Card source) {
|
||||||
return hasProtectionFrom(source, false, false);
|
return hasProtectionFrom(source, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package forge.game.player;
|
package forge.game.player;
|
||||||
|
|
||||||
|
import forge.card.CardType;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
|
import forge.game.replacement.ReplacementEffect;
|
||||||
|
import forge.game.replacement.ReplacementHandler;
|
||||||
import forge.game.staticability.StaticAbility;
|
import forge.game.staticability.StaticAbility;
|
||||||
|
|
||||||
public class PlayerFactoryUtil {
|
public class PlayerFactoryUtil {
|
||||||
@@ -39,6 +42,58 @@ public class PlayerFactoryUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void addReplacementEffect(final KeywordInterface inst, Player player) {
|
public static void addReplacementEffect(final KeywordInterface inst, Player player) {
|
||||||
|
String keyword = inst.getOriginal();
|
||||||
|
String effect = null;
|
||||||
|
|
||||||
|
if (keyword.startsWith("Protection")) {
|
||||||
|
String validSource = "Card.";
|
||||||
|
if (keyword.startsWith("Protection:")) {
|
||||||
|
final String[] kws = keyword.split(":");
|
||||||
|
String characteristic = kws[1];
|
||||||
|
if (characteristic.startsWith("Player")) {
|
||||||
|
validSource += "ControlledBy " + characteristic;
|
||||||
|
} else {
|
||||||
|
if (characteristic.endsWith("White") || characteristic.endsWith("Blue")
|
||||||
|
|| characteristic.endsWith("Black") || characteristic.endsWith("Red")
|
||||||
|
|| characteristic.endsWith("Green") || characteristic.endsWith("Colorless")
|
||||||
|
|| characteristic.endsWith("MonoColor") || characteristic.endsWith("MultiColor")) {
|
||||||
|
characteristic += "Source";
|
||||||
|
}
|
||||||
|
validSource = characteristic;
|
||||||
|
}
|
||||||
|
} else if (keyword.startsWith("Protection from ")) {
|
||||||
|
String protectType = keyword.substring("Protection from ".length());
|
||||||
|
if (protectType.equals("white")) {
|
||||||
|
validSource += "WhiteSource";
|
||||||
|
} else if (protectType.equals("blue")) {
|
||||||
|
validSource += "BlueSource";
|
||||||
|
} else if (protectType.equals("black")) {
|
||||||
|
validSource += "BlackSource";
|
||||||
|
} else if (protectType.equals("red")) {
|
||||||
|
validSource += "RedSource";
|
||||||
|
} else if (protectType.equals("green")) {
|
||||||
|
validSource += "GreenSource";
|
||||||
|
} else if (protectType.equals("all colors")) {
|
||||||
|
validSource += "nonColorless";
|
||||||
|
} else if (protectType.equals("everything")) {
|
||||||
|
validSource = "";
|
||||||
|
} else {
|
||||||
|
validSource = CardType.getSingularType(protectType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
effect = "Event$ DamageDone | Prevent$ True | ActiveZones$ Command | ValidTarget$ You";
|
||||||
|
if (!validSource.isEmpty()) {
|
||||||
|
effect += " | ValidSource$ " + validSource;
|
||||||
|
}
|
||||||
|
effect += " | Secondary$ True | Description$ " + keyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (effect != null) {
|
||||||
|
final Card card = player.getKeywordCard();
|
||||||
|
ReplacementEffect re = ReplacementHandler.parseReplacement(effect, card, false, card.getCurrentState());
|
||||||
|
inst.addReplacement(re);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addSpellAbility(final KeywordInterface inst, Player player) {
|
public static void addSpellAbility(final KeywordInterface inst, Player player) {
|
||||||
|
|||||||
@@ -482,4 +482,28 @@ public class ReplacementHandler {
|
|||||||
}
|
}
|
||||||
return totalAmount;
|
return totalAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to check if combat damage is prevented this turn (fog effect)
|
||||||
|
* @return true if there is some resolved fog effect
|
||||||
|
*/
|
||||||
|
public final boolean isPreventCombatDamageThisTurn() {
|
||||||
|
final List<ReplacementEffect> list = Lists.newArrayList();
|
||||||
|
game.forEachCardInGame(new Visitor<Card>() {
|
||||||
|
@Override
|
||||||
|
public boolean visit(Card c) {
|
||||||
|
for (final ReplacementEffect re : c.getReplacementEffects()) {
|
||||||
|
if (re.getMode() == ReplacementType.DamageDone
|
||||||
|
&& re.getLayer() == ReplacementLayer.Other
|
||||||
|
&& re.hasParam("Prevent") && re.getParam("Prevent").equals("True")
|
||||||
|
&& re.hasParam("IsCombat") && re.getParam("IsCombat").equals("True")
|
||||||
|
&& re.zonesCheck(game.getZoneOf(c))) {
|
||||||
|
list.add(re);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return !list.isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user