mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Fix getTotalPreventionShieldAmount (#3654)
* Fix scripts * Fix damage prevention display * Optimize attachment tracking for netplay * Phasing fix * Fix NPE * Tweak logic order * Clean up * Fix scripts * Clean up * Fix FailedToTarget --------- Co-authored-by: TRT <>
This commit is contained in:
@@ -398,10 +398,8 @@ public class ComputerUtilCost {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (final CostPart part : cost.getCostParts()) {
|
for (final CostPart part : cost.getCostParts()) {
|
||||||
if (part instanceof CostSacrifice) {
|
if (part instanceof CostSacrifice && part.payCostFromSource()) {
|
||||||
if ("CARDNAME".equals(part.getType())) {
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ public class DamagePreventAi extends SpellAbilityAi {
|
|||||||
// check stack for something on the stack will kill anything i control
|
// check stack for something on the stack will kill anything i control
|
||||||
final List<GameObject> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
|
final List<GameObject> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
|
||||||
|
|
||||||
if (objects.contains(ai)) {
|
if (objects.contains(ai) && sa.canTarget(ai)) {
|
||||||
tcs.add(ai);
|
tcs.add(ai);
|
||||||
chance = true;
|
chance = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ public class EffectAi extends SpellAbilityAi {
|
|||||||
} else if (logic.equals("Fight")) {
|
} else if (logic.equals("Fight")) {
|
||||||
return FightAi.canFightAi(ai, sa, 0, 0);
|
return FightAi.canFightAi(ai, sa, 0, 0);
|
||||||
} else if (logic.equals("Pump")) {
|
} else if (logic.equals("Pump")) {
|
||||||
|
sa.resetTargets();
|
||||||
List<Card> options = CardUtil.getValidCardsToTarget(sa);
|
List<Card> options = CardUtil.getValidCardsToTarget(sa);
|
||||||
options = CardLists.filterControlledBy(options, ai);
|
options = CardLists.filterControlledBy(options, ai);
|
||||||
if (sa.getPayCosts().hasTapCost()) {
|
if (sa.getPayCosts().hasTapCost()) {
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public class ManaEffectAi extends SpellAbilityAi {
|
|||||||
if (logic.startsWith("ManaRitual")) {
|
if (logic.startsWith("ManaRitual")) {
|
||||||
return ph.is(PhaseType.MAIN2, ai) || ph.is(PhaseType.MAIN1, ai);
|
return ph.is(PhaseType.MAIN2, ai) || ph.is(PhaseType.MAIN1, ai);
|
||||||
} else if ("AtOppEOT".equals(logic)) {
|
} else if ("AtOppEOT".equals(logic)) {
|
||||||
return !ai.getManaPool().hasBurn() && ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == ai;
|
return (!ai.getManaPool().hasBurn() || !ai.canLoseLife() || ai.cantLoseForZeroOrLessLife()) && ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == ai;
|
||||||
}
|
}
|
||||||
return super.checkPhaseRestrictions(ai, sa, ph, logic);
|
return super.checkPhaseRestrictions(ai, sa, ph, logic);
|
||||||
}
|
}
|
||||||
@@ -261,7 +261,8 @@ public class ManaEffectAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean improvesPosition(Player ai, SpellAbility sa) {
|
private boolean improvesPosition(Player ai, SpellAbility sa) {
|
||||||
boolean activateForTrigger = Iterables.any(Iterables.filter(sa.getHostCard().getTriggers(), CardTraitPredicates.hasParam("AILogic", "ActivateOnce")),
|
boolean activateForTrigger = (!ai.getManaPool().hasBurn() || !ai.canLoseLife() || ai.cantLoseForZeroOrLessLife()) &&
|
||||||
|
Iterables.any(Iterables.filter(sa.getHostCard().getTriggers(), CardTraitPredicates.hasParam("AILogic", "ActivateOnce")),
|
||||||
t -> sa.getHostCard().getAbilityActivatedThisTurn(t.getOverridingAbility()) == 0);
|
t -> sa.getHostCard().getAbilityActivatedThisTurn(t.getOverridingAbility()) == 0);
|
||||||
|
|
||||||
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package forge.game;
|
package forge.game;
|
||||||
|
|
||||||
import forge.game.card.CardCollectionView;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
import forge.game.card.CardView;
|
import forge.game.card.CardView;
|
||||||
import forge.trackable.TrackableCollection;
|
import forge.trackable.TrackableCollection;
|
||||||
import forge.trackable.TrackableObject;
|
import forge.trackable.TrackableObject;
|
||||||
@@ -45,35 +46,34 @@ public abstract class GameEntityView extends TrackableObject {
|
|||||||
public int getPreventNextDamage() {
|
public int getPreventNextDamage() {
|
||||||
return get(TrackableProperty.PreventNextDamage);
|
return get(TrackableProperty.PreventNextDamage);
|
||||||
}
|
}
|
||||||
protected void updatePreventNextDamage(GameEntity e) {
|
public void updatePreventNextDamage(GameEntity e) {
|
||||||
set(TrackableProperty.PreventNextDamage, e.getPreventNextDamageTotalShields());
|
set(TrackableProperty.PreventNextDamage, e.getPreventNextDamageTotalShields());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterable<CardView> getAttachedCards() {
|
public Iterable<CardView> getAttachedCards() {
|
||||||
return get(TrackableProperty.AttachedCards);
|
if (hasAnyCardAttachments()) {
|
||||||
|
Iterable<CardView> active = Iterables.filter(get(TrackableProperty.AttachedCards), c -> !c.isPhasedOut());
|
||||||
|
if (!Iterables.isEmpty(active)) {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
public boolean hasCardAttachments() {
|
public boolean hasCardAttachments() {
|
||||||
return getAttachedCards() != null;
|
return getAttachedCards() != null;
|
||||||
}
|
}
|
||||||
public Iterable<CardView> getAllAttachedCards() {
|
public Iterable<CardView> getAllAttachedCards() {
|
||||||
return get(TrackableProperty.AllAttachedCards);
|
return get(TrackableProperty.AttachedCards);
|
||||||
}
|
}
|
||||||
public boolean hasAnyCardAttachments() {
|
public boolean hasAnyCardAttachments() {
|
||||||
return getAllAttachedCards() != null;
|
return getAllAttachedCards() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateAttachedCards(GameEntity e) {
|
protected void updateAttachedCards(GameEntity e) {
|
||||||
if (e.hasCardAttachments()) {
|
if (!e.getAllAttachedCards().isEmpty()) {
|
||||||
set(TrackableProperty.AttachedCards, CardView.getCollection(e.getAttachedCards()));
|
set(TrackableProperty.AttachedCards, CardView.getCollection(e.getAllAttachedCards()));
|
||||||
}
|
|
||||||
else {
|
|
||||||
set(TrackableProperty.AttachedCards, null);
|
|
||||||
}
|
|
||||||
CardCollectionView all = e.getAllAttachedCards();
|
|
||||||
if (all.isEmpty()) {
|
|
||||||
set(TrackableProperty.AllAttachedCards, null);
|
|
||||||
} else {
|
} else {
|
||||||
set(TrackableProperty.AllAttachedCards, CardView.getCollection(all));
|
set(TrackableProperty.AttachedCards, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1999,13 +1999,6 @@ public class AbilityUtils {
|
|||||||
return doXMath(calculateAmount(c, sq[c.isOptionalCostPaid(OptionalCost.Generic) ? 1 : 2], ctb), expr, c, ctb);
|
return doXMath(calculateAmount(c, sq[c.isOptionalCostPaid(OptionalCost.Generic) ? 1 : 2], ctb), expr, c, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sq[0].equals("TotalDamageDoneByThisTurn")) {
|
|
||||||
return doXMath(c.getTotalDamageDoneBy(), expr, c, ctb);
|
|
||||||
}
|
|
||||||
if (sq[0].equals("TotalDamageReceivedThisTurn")) {
|
|
||||||
return doXMath(c.getAssignedDamage(), expr, c, ctb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sq[0].contains("CardPower")) {
|
if (sq[0].contains("CardPower")) {
|
||||||
return doXMath(c.getNetPower(), expr, c, ctb);
|
return doXMath(c.getNetPower(), expr, c, ctb);
|
||||||
}
|
}
|
||||||
@@ -2335,6 +2328,13 @@ public class AbilityUtils {
|
|||||||
return doXMath(player.getOpponentsTotalPoisonCounters(), expr, c, ctb);
|
return doXMath(player.getOpponentsTotalPoisonCounters(), expr, c, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sq[0].equals("TotalDamageDoneByThisTurn")) {
|
||||||
|
return doXMath(c.getTotalDamageDoneBy(), expr, c, ctb);
|
||||||
|
}
|
||||||
|
if (sq[0].equals("TotalDamageReceivedThisTurn")) {
|
||||||
|
return doXMath(c.getAssignedDamage(), expr, c, ctb);
|
||||||
|
}
|
||||||
|
|
||||||
if (sq[0].equals("MaxOppDamageThisTurn")) {
|
if (sq[0].equals("MaxOppDamageThisTurn")) {
|
||||||
return doXMath(player.getMaxOpponentAssignedDamage(), expr, c, ctb);
|
return doXMath(player.getMaxOpponentAssignedDamage(), expr, c, ctb);
|
||||||
}
|
}
|
||||||
@@ -3493,6 +3493,7 @@ public class AbilityUtils {
|
|||||||
if (value.equals("RingTemptedYou")) {
|
if (value.equals("RingTemptedYou")) {
|
||||||
return doXMath(player.getNumRingTemptedYou(), m, source, ctb);
|
return doXMath(player.getNumRingTemptedYou(), m, source, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.startsWith("DungeonCompletedNamed")) {
|
if (value.startsWith("DungeonCompletedNamed")) {
|
||||||
String [] full = value.split("_");
|
String [] full = value.split("_");
|
||||||
String name = full[1];
|
String name = full[1];
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import java.util.List;
|
|||||||
|
|
||||||
import forge.GameCommand;
|
import forge.GameCommand;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameEntity;
|
||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.event.GameEventPlayerStatsChanged;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.replacement.ReplacementEffect;
|
import forge.game.replacement.ReplacementEffect;
|
||||||
import forge.game.replacement.ReplacementHandler;
|
import forge.game.replacement.ReplacementHandler;
|
||||||
@@ -20,7 +21,7 @@ import forge.game.zone.ZoneType;
|
|||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
|
|
||||||
public abstract class DamagePreventEffectBase extends SpellAbilityEffect {
|
public abstract class DamagePreventEffectBase extends SpellAbilityEffect {
|
||||||
public static void addPreventNextDamage(SpellAbility sa, GameObject o, int numDam) {
|
public static void addPreventNextDamage(SpellAbility sa, GameEntity o, int numDam) {
|
||||||
final Card hostCard = sa.getHostCard();
|
final Card hostCard = sa.getHostCard();
|
||||||
final Game game = hostCard.getGame();
|
final Game game = hostCard.getGame();
|
||||||
final Player player = hostCard.getController();
|
final Player player = hostCard.getController();
|
||||||
@@ -40,9 +41,9 @@ public abstract class DamagePreventEffectBase extends SpellAbilityEffect {
|
|||||||
if (sa.hasParam("PreventionSubAbility")) {
|
if (sa.hasParam("PreventionSubAbility")) {
|
||||||
String subAbString = sa.getSVar(sa.getParam("PreventionSubAbility"));
|
String subAbString = sa.getSVar(sa.getParam("PreventionSubAbility"));
|
||||||
if (sa.hasParam("ShieldEffectTarget")) {
|
if (sa.hasParam("ShieldEffectTarget")) {
|
||||||
List<GameObject> effTgts = AbilityUtils.getDefinedObjects(hostCard, sa.getParam("ShieldEffectTarget"), sa);
|
List<GameEntity> effTgts = AbilityUtils.getDefinedEntities(hostCard, sa.getParam("ShieldEffectTarget"), sa);
|
||||||
String effTgtString = "";
|
String effTgtString = "";
|
||||||
for (final GameObject effTgt : effTgts) {
|
for (final GameEntity effTgt : effTgts) {
|
||||||
if (effTgt instanceof Card) {
|
if (effTgt instanceof Card) {
|
||||||
effTgtString = "CardUID_" + String.valueOf(((Card) effTgt).getId());
|
effTgtString = "CardUID_" + String.valueOf(((Card) effTgt).getId());
|
||||||
} else if (effTgt instanceof Player) {
|
} else if (effTgt instanceof Player) {
|
||||||
@@ -70,12 +71,21 @@ public abstract class DamagePreventEffectBase extends SpellAbilityEffect {
|
|||||||
eff.updateStateForView();
|
eff.updateStateForView();
|
||||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||||
|
|
||||||
|
o.getView().updatePreventNextDamage(o);
|
||||||
|
if (o instanceof Player) {
|
||||||
|
game.fireEvent(new GameEventPlayerStatsChanged((Player) o, false));
|
||||||
|
}
|
||||||
|
|
||||||
game.getEndOfTurn().addUntil(new GameCommand() {
|
game.getEndOfTurn().addUntil(new GameCommand() {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
game.getAction().exile(eff, null);
|
game.getAction().exile(eff, null);
|
||||||
|
o.getView().updatePreventNextDamage(o);
|
||||||
|
if (o instanceof Player) {
|
||||||
|
game.fireEvent(new GameEventPlayerStatsChanged((Player) o, false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5636,7 +5636,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
/**
|
/**
|
||||||
* Gets the total damage done by card this turn (after prevention and redirects).
|
* Gets the total damage done by card this turn (after prevention and redirects).
|
||||||
*
|
*
|
||||||
* @return the damage done to player p this turn
|
* @return the damage done by the card this turn
|
||||||
*/
|
*/
|
||||||
public final int getTotalDamageDoneBy() {
|
public final int getTotalDamageDoneBy() {
|
||||||
return getDamageHistory().getDamageDoneThisTurn(null, false, null, null, this, getController(), null);
|
return getDamageHistory().getDamageDoneThisTurn(null, false, null, null, this, getController(), null);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import forge.game.Game;
|
|||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.GameObjectPredicates;
|
import forge.game.GameObjectPredicates;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
|
import forge.game.event.GameEventPlayerStatsChanged;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerCollection;
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
@@ -48,6 +49,11 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
|||||||
runParams.put(AbilityKey.IsCombatDamage, isCombat);
|
runParams.put(AbilityKey.IsCombatDamage, isCombat);
|
||||||
|
|
||||||
ge.getGame().getTriggerHandler().runTrigger(TriggerType.DamagePreventedOnce, runParams, false);
|
ge.getGame().getTriggerHandler().runTrigger(TriggerType.DamagePreventedOnce, runParams, false);
|
||||||
|
|
||||||
|
ge.getView().updatePreventNextDamage(ge);
|
||||||
|
if (ge instanceof Player) {
|
||||||
|
ge.getGame().fireEvent(new GameEventPlayerStatsChanged((Player) ge, false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -362,7 +362,6 @@ public class AttackConstraints {
|
|||||||
MapToAmount<GameEntity> sortedPlayerReqs = new LinkedHashMapToAmount<>();
|
MapToAmount<GameEntity> sortedPlayerReqs = new LinkedHashMapToAmount<>();
|
||||||
sortedPlayerReqs.addAll(Iterables.concat(playerReqs));
|
sortedPlayerReqs.addAll(Iterables.concat(playerReqs));
|
||||||
while (!sortedPlayerReqs.isEmpty()) {
|
while (!sortedPlayerReqs.isEmpty()) {
|
||||||
sortedPlayerReqs.keySet().removeAll(excludedDefenders);
|
|
||||||
Pair<GameEntity, Integer> playerReq = MapToAmountUtil.max(sortedPlayerReqs);
|
Pair<GameEntity, Integer> playerReq = MapToAmountUtil.max(sortedPlayerReqs);
|
||||||
// find best attack to also fulfill the additional requirements
|
// find best attack to also fulfill the additional requirements
|
||||||
Attack bestMatch = Iterables.getLast(Iterables.filter(result, att -> !usedAttackers.contains(att.attacker) && att.defender.equals(playerReq.getLeft())), null);
|
Attack bestMatch = Iterables.getLast(Iterables.filter(result, att -> !usedAttackers.contains(att.attacker) && att.defender.equals(playerReq.getLeft())), null);
|
||||||
@@ -376,6 +375,7 @@ public class AttackConstraints {
|
|||||||
} else {
|
} else {
|
||||||
excludedDefenders.add(playerReq.getLeft());
|
excludedDefenders.add(playerReq.getLeft());
|
||||||
}
|
}
|
||||||
|
sortedPlayerReqs.keySet().removeAll(excludedDefenders);
|
||||||
}
|
}
|
||||||
if (!usedAttackers.isEmpty()) {
|
if (!usedAttackers.isEmpty()) {
|
||||||
// order could have changed
|
// order could have changed
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import forge.game.keyword.KeywordInterface;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerController.BinaryChoiceType;
|
import forge.game.player.PlayerController.BinaryChoiceType;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.staticability.StaticAbilityCantPhaseOut;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -266,14 +267,14 @@ public class Untap extends Phase {
|
|||||||
// If c is attached to something, it will phase out on its own, and try
|
// If c is attached to something, it will phase out on its own, and try
|
||||||
// to attach back to that thing when it comes back
|
// to attach back to that thing when it comes back
|
||||||
for (final Card c : list) {
|
for (final Card c : list) {
|
||||||
if (c.isPhasedOut()) {
|
if (c.isPhasedOut() && c.isDirectlyPhasedOut()) {
|
||||||
c.phase(true);
|
c.phase(true);
|
||||||
} else if (c.hasKeyword(Keyword.PHASING)) {
|
} else if (c.hasKeyword(Keyword.PHASING)) {
|
||||||
// CR 702.26h If an object would simultaneously phase out directly
|
// CR 702.26h If an object would simultaneously phase out directly
|
||||||
// and indirectly, it just phases out indirectly.
|
// and indirectly, it just phases out indirectly.
|
||||||
if (c.isAttachment()) {
|
if (c.isAttachment()) {
|
||||||
final Card ent = c.getAttachedTo();
|
final Card ent = c.getAttachedTo();
|
||||||
if (ent != null && list.contains(ent)) {
|
if (ent != null && list.contains(ent) && !StaticAbilityCantPhaseOut.cantPhaseOut(ent)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,6 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
|||||||
public boolean requirementsCheck(Game game) {
|
public boolean requirementsCheck(Game game) {
|
||||||
return this.requirementsCheck(game, this.getMapParams());
|
return this.requirementsCheck(game, this.getMapParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean requirementsCheck(Game game, Map<String,String> params) {
|
public boolean requirementsCheck(Game game, Map<String,String> params) {
|
||||||
if (this.isSuppressed()) {
|
if (this.isSuppressed()) {
|
||||||
return false; // Effect removed by effect
|
return false; // Effect removed by effect
|
||||||
|
|||||||
@@ -911,7 +911,8 @@ public class ReplacementHandler {
|
|||||||
&& re.hasParam("PreventionEffect")
|
&& re.hasParam("PreventionEffect")
|
||||||
&& re.zonesCheck(game.getZoneOf(c))
|
&& re.zonesCheck(game.getZoneOf(c))
|
||||||
&& re.getOverridingAbility() != null
|
&& re.getOverridingAbility() != null
|
||||||
&& re.getOverridingAbility().getApi() == ApiType.ReplaceDamage) {
|
&& re.getOverridingAbility().getApi() == ApiType.ReplaceDamage
|
||||||
|
&& re.matchesValidParam("ValidTarget", o)) {
|
||||||
list.add(re);
|
list.add(re);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ public enum TrackableProperty {
|
|||||||
Text(TrackableTypes.StringType),
|
Text(TrackableTypes.StringType),
|
||||||
PreventNextDamage(TrackableTypes.IntegerType),
|
PreventNextDamage(TrackableTypes.IntegerType),
|
||||||
AttachedCards(TrackableTypes.CardViewCollectionType),
|
AttachedCards(TrackableTypes.CardViewCollectionType),
|
||||||
AllAttachedCards(TrackableTypes.CardViewCollectionType),
|
|
||||||
Counters(TrackableTypes.CounterMapType),
|
Counters(TrackableTypes.CounterMapType),
|
||||||
CurrentPlane(TrackableTypes.StringType),
|
CurrentPlane(TrackableTypes.StringType),
|
||||||
PlanarPlayer(TrackableTypes.PlayerViewType),
|
PlanarPlayer(TrackableTypes.PlayerViewType),
|
||||||
|
|||||||
@@ -595,8 +595,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
|
|||||||
Iterable<CardView> cards = model.getCards(zone);
|
Iterable<CardView> cards = model.getCards(zone);
|
||||||
if (cards != null) {
|
if (cards != null) {
|
||||||
modelCopy = Lists.newArrayList(cards);
|
modelCopy = Lists.newArrayList(cards);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
modelCopy = Lists.newArrayList();
|
modelCopy = Lists.newArrayList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -648,17 +647,17 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
|
|||||||
doLayout();
|
doLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidate(); //pfps do the extra invalidate before any scrolling
|
invalidate(); //pfps do the extra invalidate before any scrolling
|
||||||
if (!newPanels.isEmpty()) {
|
if (!newPanels.isEmpty()) {
|
||||||
int i = newPanels.size();
|
int i = newPanels.size();
|
||||||
for (final CardPanel toPanel : newPanels) {
|
for (final CardPanel toPanel : newPanels) {
|
||||||
if ( --i == 0 ) { // only scroll to last panel to be added
|
if ( --i == 0 ) { // only scroll to last panel to be added
|
||||||
scrollRectToVisible(new Rectangle(toPanel.getCardX(), toPanel.getCardY(), toPanel.getCardWidth(), toPanel.getCardHeight()));
|
scrollRectToVisible(new Rectangle(toPanel.getCardX(), toPanel.getCardY(), toPanel.getCardWidth(), toPanel.getCardHeight()));
|
||||||
}
|
}
|
||||||
Animation.moveCard(toPanel);
|
Animation.moveCard(toPanel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean updateCard(final CardView card, boolean fromRefresh) {
|
public boolean updateCard(final CardView card, boolean fromRefresh) {
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ Name:Break of Day
|
|||||||
ManaCost:1 W
|
ManaCost:1 W
|
||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ PumpAll | Cost$ 1 W | ValidCards$ Creature.YouCtrl | NumAtt$ +1 | NumDef$ +1 | SubAbility$ FatefulHourPump | SpellDescription$ Creatures you control get +1/+1 until end of turn.
|
A:SP$ PumpAll | Cost$ 1 W | ValidCards$ Creature.YouCtrl | NumAtt$ +1 | NumDef$ +1 | SubAbility$ FatefulHourPump | SpellDescription$ Creatures you control get +1/+1 until end of turn.
|
||||||
SVar:FatefulHourPump:DB$ PumpAll | ValidCards$ Creature.YouCtrl | KW$ Indestructible | FatefulHour$ True | SpellDescription$ Fateful hour — If you have 5 or less life, those creatures gain indestructible until end of turn.
|
SVar:FatefulHourPump:DB$ PumpAll | ValidCards$ Creature.YouCtrl | KW$ Indestructible | ConditionCheckSVar$ FatefulHour | ConditionSVarCompare$ LE5 | SpellDescription$ Fateful hour — If you have 5 or less life, those creatures gain indestructible until end of turn.
|
||||||
|
SVar:FatefulHour:Count$YourLifeTotal
|
||||||
Oracle:Creatures you control get +1/+1 until end of turn.\nFateful hour — If you have 5 or less life, those creatures gain indestructible until end of turn. (Damage and effects that say "destroy" don't destroy them.)
|
Oracle:Creatures you control get +1/+1 until end of turn.\nFateful hour — If you have 5 or less life, those creatures gain indestructible until end of turn. (Damage and effects that say "destroy" don't destroy them.)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Ogre Slumlord
|
|||||||
ManaCost:3 B B
|
ManaCost:3 B B
|
||||||
Types:Creature Ogre Rogue
|
Types:Creature Ogre Rogue
|
||||||
PT:3/3
|
PT:3/3
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.nonToken+Other | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever another nontoken creature dies, you may create a 1/1 black Rat creature token.
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.nonToken+Other | OptionalDecider$ You | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever another nontoken creature dies, you may create a 1/1 black Rat creature token.
|
||||||
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ b_1_1_rat | TokenOwner$ You
|
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ b_1_1_rat | TokenOwner$ You
|
||||||
S:Mode$ Continuous | Affected$ Creature.Rat+YouCtrl | AddKeyword$ Deathtouch | Description$ Rats you control have deathtouch.
|
S:Mode$ Continuous | Affected$ Creature.Rat+YouCtrl | AddKeyword$ Deathtouch | Description$ Rats you control have deathtouch.
|
||||||
DeckHas:Ability$Token
|
DeckHas:Ability$Token
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Types:Legendary Creature Spirit Naga
|
|||||||
PT:0/0
|
PT:0/0
|
||||||
K:etbCounter:P1P1:X
|
K:etbCounter:P1P1:X
|
||||||
SVar:X:Count$xPaid
|
SVar:X:Count$xPaid
|
||||||
T:Mode$ CounterAddedOnce | ValidCard$ Creature.Other+inZoneBattlefield+Colorless | OptionalDecider$ You | TriggerZones$ Battlefield | CounterType$ P1P1 | Execute$ TrigPutCounter | TriggerDescription$ Whenever you put one or more +1/+1 counters on another colorless creature, you may put a +1/+1 counter on NICKNAME.
|
T:Mode$ CounterAddedOnce | ValidCard$ Creature.Other+inZoneBattlefield+Colorless | ValidPlayer$ You | OptionalDecider$ You | TriggerZones$ Battlefield | CounterType$ P1P1 | Execute$ TrigPutCounter | TriggerDescription$ Whenever you put one or more +1/+1 counters on another colorless creature, you may put a +1/+1 counter on NICKNAME.
|
||||||
SVar:TrigPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1
|
SVar:TrigPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigManifest | TriggerDescription$ When NICKNAME dies, manifest a number of cards from the top of your library equal to the number of counters on it.
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigManifest | TriggerDescription$ When NICKNAME dies, manifest a number of cards from the top of your library equal to the number of counters on it.
|
||||||
SVar:TrigManifest:DB$ Manifest | Amount$ Y | Defined$ TopOfLibrary
|
SVar:TrigManifest:DB$ Manifest | Amount$ Y | Defined$ TopOfLibrary
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Types:Enchantment Aura
|
|||||||
K:Flash
|
K:Flash
|
||||||
K:Enchant creature you control
|
K:Enchant creature you control
|
||||||
A:SP$ Attach | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | AILogic$ Pump
|
A:SP$ Attach | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | AILogic$ Pump
|
||||||
T:Mode$ ChangesZone | ValidCard$ Creature.EnchantedBy,Creature.modified+NotEnchantedBy | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigToken | TriggerDescription$ Whenever enchanted creature or another modified creature you control dies, create X 1/1 colorless Spirit creature tokens, where X is that creature's power. (Equipment, Auras you control, and counters are modifications.)
|
T:Mode$ ChangesZone | ValidCard$ Creature.EnchantedBy,Creature.YouCtrl+modified+NotEnchantedBy | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigToken | TriggerDescription$ Whenever enchanted creature or another modified creature you control dies, create X 1/1 colorless Spirit creature tokens, where X is that creature's power. (Equipment, Auras you control, and counters are modifications.)
|
||||||
SVar:TrigToken:DB$ Token | TokenScript$ c_1_1_spirit | TokenAmount$ X
|
SVar:TrigToken:DB$ Token | TokenScript$ c_1_1_spirit | TokenAmount$ X
|
||||||
SVar:X:TriggeredCard$CardPower
|
SVar:X:TriggeredCard$CardPower
|
||||||
DeckHas:Ability$Token & Type$Spirit
|
DeckHas:Ability$Token & Type$Spirit
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ ManaCost:2 R
|
|||||||
Types:Creature Goblin Rogue
|
Types:Creature Goblin Rogue
|
||||||
PT:3/2
|
PT:3/2
|
||||||
T:Mode$ TapsForMana | ValidCard$ Artifact | Activator$ Opponent | TriggerZones$ Battlefield | Execute$ TrigControl | TriggerDescription$ Whenever an opponent taps an artifact for mana, gain control of that artifact until the end of your next turn.
|
T:Mode$ TapsForMana | ValidCard$ Artifact | Activator$ Opponent | TriggerZones$ Battlefield | Execute$ TrigControl | TriggerDescription$ Whenever an opponent taps an artifact for mana, gain control of that artifact until the end of your next turn.
|
||||||
SVar:TrigControl:DB$ GainControl | Defined$ TriggeredCard | LoseControl$ UntilTheEndOfYourNextTurn
|
SVar:TrigControl:DB$ GainControl | Defined$ TriggeredCardLKICopy | LoseControl$ UntilTheEndOfYourNextTurn
|
||||||
Oracle:Whenever an opponent taps an artifact for mana, gain control of that artifact until the end of your next turn.
|
Oracle:Whenever an opponent taps an artifact for mana, gain control of that artifact until the end of your next turn.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Unexpected Allies
|
|||||||
ManaCost:1 R
|
ManaCost:1 R
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ Pump | ValidTgts$ Creature.YouCtrl+nonToken | TgtPrompt$ Select target nontoken creature you control | KW$ Double team | NumAtt$ 2 | SubAbility$ TrigEffect | SpellDescription$ Target nontoken creature you control gets +2/+0 and gains double team until end of turn. It also gains first strike until end of turn if it has the same name as another creature you control or a creature card in your graveyard.
|
A:SP$ Pump | ValidTgts$ Creature.YouCtrl+nonToken | TgtPrompt$ Select target nontoken creature you control | KW$ Double team | NumAtt$ 2 | SubAbility$ TrigEffect | SpellDescription$ Target nontoken creature you control gets +2/+0 and gains double team until end of turn. It also gains first strike until end of turn if it has the same name as another creature you control or a creature card in your graveyard.
|
||||||
SVar:TrigEffect:DB$ Pump | ConditionCheckSVar$ X | Defined$ Targeted | KW$ First strike
|
SVar:TrigEffect:DB$ Pump | ConditionCheckSVar$ X | Defined$ Targeted | KW$ First Strike
|
||||||
SVar:X:Count$Valid Creature.NotDefinedTargeted+YouCtrl+sharesNameWith Targeted/Plus.Y
|
SVar:X:Count$Valid Creature.NotDefinedTargeted+YouCtrl+sharesNameWith Targeted/Plus.Y
|
||||||
SVar:Y:Count$ValidGraveyard Creature.YouOwn+sharesNameWith Targeted
|
SVar:Y:Count$ValidGraveyard Creature.YouOwn+sharesNameWith Targeted
|
||||||
DeckHas:Keyword$Double Team|First Strike
|
DeckHas:Keyword$Double Team|First Strike
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ public class InputAttack extends InputSyncronizedBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void showMessage() {
|
public final void showMessage() {
|
||||||
// TODO still seems to have some issues with multiple planeswalkers
|
|
||||||
setCurrentDefender(defenders.getFirst());
|
setCurrentDefender(defenders.getFirst());
|
||||||
|
|
||||||
if (currentDefender == null) {
|
if (currentDefender == null) {
|
||||||
@@ -295,7 +294,9 @@ public class InputAttack extends InputSyncronizedBase {
|
|||||||
getController().getGui().setHighlighted(PlayerView.get((Player) ge), ge == def);
|
getController().getGui().setHighlighted(PlayerView.get((Player) ge), ge == def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
potentialBanding = isBandingPossible();
|
if (def != null) {
|
||||||
|
potentialBanding = isBandingPossible();
|
||||||
|
}
|
||||||
|
|
||||||
updateMessage();
|
updateMessage();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user