diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 2982df30668..f06378720bd 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -767,7 +767,7 @@ public class AiController { // will need actual logic that determines if the enchantment is able // to disable the permanent or it's still functional and a duplicate is unneeded. boolean disabledByEnemy = false; - for (Card card2 : card.getEnchantedBy(false)) { + for (Card card2 : card.getEnchantedBy()) { if (card2.getOwner() != player) { disabledByEnemy = true; } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 8c085c493d9..703016b4c81 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -823,7 +823,7 @@ public class ComputerUtil { if (c != null && c.isEnchanted()) { // TODO: choose "worst" controlled enchanting Aura - for (Card aura : c.getEnchantedBy(false)) { + for (Card aura : c.getEnchantedBy()) { if (aura.getController().equals(c.getController()) && remaining.contains(aura)) { return aura; } @@ -2924,7 +2924,7 @@ public class ComputerUtil { if (sa.getParam("AITgts").equals("BetterThanSource")) { int value = ComputerUtilCard.evaluateCreature(source); if (source.isEnchanted()) { - for (Card enc : source.getEnchantedBy(false)) { + for (Card enc : source.getEnchantedBy()) { if (enc.getController().equals(ai)) { value += 100; // is 100 per AI's own aura enough? } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index 8d1883aaafc..1eb1fd6913e 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -585,7 +585,7 @@ public class ComputerUtilCard { // Add all cost of all auras with the same controller if (card.isEnchanted()) { - final List auras = CardLists.filterControlledBy(card.getEnchantedBy(false), card.getController()); + final List auras = CardLists.filterControlledBy(card.getEnchantedBy(), card.getController()); curCMC += Aggregates.sum(auras, CardPredicates.Accessors.fnGetCmc) + auras.size(); } @@ -833,7 +833,7 @@ public class ComputerUtilCard { int score = tmp.isTapped() ? 2 : 0; score += tmp.isBasicLand() ? 1 : 0; score -= tmp.isCreature() ? 4 : 0; - for (Card aura : tmp.getEnchantedBy(false)) { + for (Card aura : tmp.getEnchantedBy()) { if (aura.getController().isOpponentOf(tmp.getController())) { score += 5; } else { @@ -857,7 +857,7 @@ public class ComputerUtilCard { int score = tmp.isTapped() ? 0 : 2; score += tmp.isBasicLand() ? 2 : 0; score -= tmp.isCreature() ? 4 : 0; - score -= 5 * tmp.getEnchantedBy(false).size(); + score -= 5 * tmp.getEnchantedBy().size(); if (score >= maxScore) { land = tmp; @@ -1033,7 +1033,7 @@ public class ComputerUtilCard { // interrupt 3: two for one = good if (c.isEnchanted()) { boolean myEnchants = false; - for (Card enc : c.getEnchantedBy(false)) { + for (Card enc : c.getEnchantedBy()) { if (enc.getOwner().equals(ai)) { myEnchants = true; break; diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index e9a0ccee4e8..dd1a4541c33 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -300,7 +300,7 @@ public class ComputerUtilCost { if (!important) { return false; } - if (!CardLists.filterControlledBy(source.getEnchantedBy(false), source.getController()).isEmpty()) { + if (!CardLists.filterControlledBy(source.getEnchantedBy(), source.getController()).isEmpty()) { return false; } continue; diff --git a/forge-ai/src/main/java/forge/ai/GameState.java b/forge-ai/src/main/java/forge/ai/GameState.java index 522281d6884..cd93143f61d 100644 --- a/forge-ai/src/main/java/forge/ai/GameState.java +++ b/forge-ai/src/main/java/forge/ai/GameState.java @@ -196,9 +196,7 @@ public abstract class GameState { cardsReferencedByID.add(card.getExiledWith()); } if (zone == ZoneType.Battlefield) { - if (!card.getEnchantedBy(false).isEmpty() - || !card.getEquippedBy(false).isEmpty() - || !card.getFortifiedBy(false).isEmpty()) { + if (!card.getAttachedBy().isEmpty()) { // Remember the ID of cards that have attachments cardsReferencedByID.add(card); } @@ -290,12 +288,8 @@ public abstract class GameState { } else if (c.getCurrentStateName().equals(CardStateName.Meld)) { newText.append("|Meld"); } - if (c.getEquipping() != null) { - newText.append("|Attaching:").append(c.getEquipping().getId()); - } else if (c.getFortifying() != null) { - newText.append("|Attaching:").append(c.getFortifying().getId()); - } else if (c.getEnchantingCard() != null) { - newText.append("|Attaching:").append(c.getEnchantingCard().getId()); + if (c.isAttaching()) { + newText.append("|Attaching:").append(c.getAttaching().getId()); } if (c.getEnchantingPlayer() != null) { // TODO: improve this for game states with more than two players @@ -959,25 +953,15 @@ public abstract class GameState { // Unattach all permanents first for(Entry entry : cardToAttachId.entrySet()) { Card attachedTo = idToCard.get(entry.getValue()); - - attachedTo.unEnchantAllCards(); - attachedTo.unEquipAllCards(); - for (Card c : attachedTo.getFortifiedBy(true)) { - attachedTo.unFortifyCard(c); - } + attachedTo.unAttachAllCards(); } // Attach permanents by ID for(Entry entry : cardToAttachId.entrySet()) { Card attachedTo = idToCard.get(entry.getValue()); Card attacher = entry.getKey(); - - if (attacher.isEquipment()) { - attacher.equipCard(attachedTo); - } else if (attacher.isAura()) { - attacher.enchantEntity(attachedTo); - } else if (attacher.isFortified()) { - attacher.fortifyCard(attachedTo); + if (attacher.isAttachment()) { + attacher.attachEntity(attachedTo); } } @@ -988,7 +972,7 @@ public abstract class GameState { Game game = attacher.getGame(); Player attachedTo = entry.getValue() == TARGET_AI ? game.getPlayers().get(1) : game.getPlayers().get(0); - attacher.enchantEntity(attachedTo); + attacher.attachEntity(attachedTo); } } @@ -1043,7 +1027,9 @@ public abstract class GameState { if (c.isAura()) { // dummy "enchanting" to indicate that the card will be force-attached elsewhere // (will be overridden later, so the actual value shouldn't matter) - c.setEnchanting(c); + + //FIXME it shouldn't be able to attach itself + c.setAttaching(c); } if (cardsWithoutETBTrigs.contains(c)) { diff --git a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java index 828103b71b5..6deb3089770 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java @@ -408,7 +408,7 @@ public class AttachAi extends SpellAbilityAi { return true; } - final Iterable auras = c.getEnchantedBy(false); + final Iterable auras = c.getEnchantedBy(); final Iterator itr = auras.iterator(); while (itr.hasNext()) { final Card aura = itr.next(); @@ -1276,7 +1276,7 @@ public class AttachAi extends SpellAbilityAi { return null; } // Don't fortify if already fortifying - if (attachSource.getFortifying() != null && attachSource.getFortifying().getController() == aiPlayer) { + if (attachSource.getAttachingCard() != null && attachSource.getAttachingCard().getController() == aiPlayer) { return null; } @@ -1491,7 +1491,7 @@ public class AttachAi extends SpellAbilityAi { return false; } // Also don't play if it would destroy own Aura - for (Card c : card.getEnchantedBy(false)) { + for (Card c : card.getEnchantedBy()) { if ((c.getController().equals(ai)) && (c.isOfColor(cc))) { return false; } diff --git a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java index 839481ced71..e5c52128bef 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java @@ -159,7 +159,7 @@ public class ChangeZoneAi extends SpellAbilityAi { return true; // debuffed by opponent's auras to the level that it becomes useless } int delta = 0; - for (Card enc : sa.getHostCard().getEnchantedBy(false)) { + for (Card enc : sa.getHostCard().getEnchantedBy()) { if (enc.getController().isOpponentOf(aiPlayer)) { delta--; } else { @@ -961,7 +961,7 @@ public class ChangeZoneAi extends SpellAbilityAi { list = CardLists.filter(list, new Predicate() { @Override public boolean apply(final Card c) { - for (Card aura : c.getEnchantedBy(false)) { + for (Card aura : c.getEnchantedBy()) { if (aura.getController().isOpponentOf(ai)) { return true; } else { @@ -1054,7 +1054,7 @@ public class ChangeZoneAi extends SpellAbilityAi { list = CardLists.filter(list, new Predicate() { @Override public boolean apply(final Card c) { - for (Card aura : c.getEnchantedBy(false)) { + for (Card aura : c.getEnchantedBy()) { if (c.getOwner().isOpponentOf(ai) && aura.getController().equals(ai)) { return false; } diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java index 38b1c87d56c..920146771ff 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java @@ -99,7 +99,7 @@ public class CountersPutAi extends SpellAbilityAi { if (sa.hasParam("LevelUp")) { // creatures enchanted by curse auras have low priority if (ph.getPhase().isBefore(PhaseType.MAIN2)) { - for (Card aura : source.getEnchantedBy(false)) { + for (Card aura : source.getEnchantedBy()) { if (aura.getController().isOpponentOf(ai)) { return false; } diff --git a/forge-ai/src/main/java/forge/ai/ability/DamageAiBase.java b/forge-ai/src/main/java/forge/ai/ability/DamageAiBase.java index 49f864cd4e5..1b2dc7ee7fd 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DamageAiBase.java +++ b/forge-ai/src/main/java/forge/ai/ability/DamageAiBase.java @@ -27,7 +27,7 @@ public abstract class DamageAiBase extends SpellAbilityAi { Card hostcard = sa.getHostCard(); boolean lifelink = hostcard.hasKeyword(Keyword.LIFELINK); if (!lifelink) { - for (Card ench : hostcard.getEnchantedBy(false)) { + for (Card ench : hostcard.getEnchantedBy()) { // Treat cards enchanted by older cards with "when enchanted creature deals damage, gain life" as if they had lifelink. if (ench.hasSVar("LikeLifeLink")) { if ("True".equals(ench.getSVar("LikeLifeLink"))) { diff --git a/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java b/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java index 731d9279677..06705baf8bf 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java @@ -273,7 +273,7 @@ public class DestroyAi extends SpellAbilityAi { } else { // Don't destroy stolen permanents when the stealing aura can be destroyed if (choice.getOwner() == ai) { - for (Card aura : choice.getEnchantedBy(false)) { + for (Card aura : choice.getEnchantedBy()) { SpellAbility sp = aura.getFirstSpellAbility(); if (sp != null && "GainControl".equals(sp.getParam("AILogic")) && aura.getController() != ai && sa.canTarget(aura)) { diff --git a/forge-ai/src/main/java/forge/ai/ability/PermanentAi.java b/forge-ai/src/main/java/forge/ai/ability/PermanentAi.java index ad7f587a88a..d0579d313ad 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PermanentAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PermanentAi.java @@ -192,10 +192,10 @@ public class PermanentAi extends SpellAbilityAi { // be better to have a pristine copy of the card - might not always be a correct assumption, but sounds // like a reasonable default for some cards). for (Card c : ctrld) { - if (c.getEnchantedBy(false).isEmpty()) { + if (c.getEnchantedBy().isEmpty()) { numControlled++; } else { - for (Card att : c.getEnchantedBy(false)) { + for (Card att : c.getEnchantedBy()) { if (!att.getController().isOpponentOf(ai)) { numControlled++; } diff --git a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java index 6324ff494e1..d81bf84bdb9 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java +++ b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java @@ -13,6 +13,7 @@ import forge.LobbyPlayer; import forge.ai.LobbyPlayerAi; import forge.card.CardStateName; import forge.game.Game; +import forge.game.GameEntity; import forge.game.GameObject; import forge.game.GameObjectMap; import forge.game.GameRules; @@ -204,19 +205,16 @@ public class GameCopier { } } gameObjectMap = new CopiedGameObjectMap(newGame); + for (Card card : origGame.getCardsIn(ZoneType.Battlefield)) { Card otherCard = cardMap.get(card); otherCard.setTimestamp(card.getTimestamp()); otherCard.setSickness(card.hasSickness()); otherCard.setState(card.getCurrentStateName(), false); - if (card.isEnchanting()) { - otherCard.setEnchanting(gameObjectMap.map(card.getEnchanting())); - } - if (card.isEquipping()) { - otherCard.equipCard(cardMap.get(card.getEquipping())); - } - if (card.isFortifying()) { - otherCard.setFortifying(cardMap.get(card.getFortifying())); + if (card.isAttaching()) { + GameEntity ge = gameObjectMap.map(card.getAttaching()); + otherCard.setAttaching(ge); + ge.addAttachedBy(otherCard); } if (card.getCloneOrigin() != null) { otherCard.setCloneOrigin(cardMap.get(card.getCloneOrigin())); diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 076aa493db5..c36cf6ed7a3 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -36,7 +36,6 @@ import forge.game.replacement.ReplacementResult; import forge.game.spellability.AbilitySub; import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityPredicates; -import forge.game.spellability.TargetRestrictions; import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbilityLayer; import forge.game.trigger.Trigger; @@ -504,45 +503,12 @@ public class GameAction { } private static void unattachCardLeavingBattlefield(final Card copied) { - // Handle unequipping creatures - if (copied.isEquipped()) { - for (final Card equipment : copied.getEquippedBy(true)) { - if (equipment.isInPlay()) { - equipment.unEquipCard(copied); - } - } - } - // Handle unfortifying lands - if (copied.isFortified()) { - for (final Card f : copied.getFortifiedBy(true)) { - if (f.isInPlay()) { - f.unFortifyCard(copied); - } - } - } - // equipment moving off battlefield - if (copied.isEquipping()) { - final Card equippedCreature = copied.getEquipping(); - if (equippedCreature.isInPlay()) { - copied.unEquipCard(equippedCreature); - } - } - // fortifications moving off battlefield - if (copied.isFortifying()) { - final Card fortifiedLand = copied.getFortifying(); - if (fortifiedLand.isInPlay()) { - copied.unFortifyCard(fortifiedLand); - } - } - // remove enchantments from creatures - if (copied.isEnchanted()) { - for (final Card aura : copied.getEnchantedBy(true)) { - aura.unEnchantEntity(copied); - } - } + // remove attachments from creatures + copied.unAttachAllCards(); + // unenchant creature if moving aura - if (copied.isEnchanting()) { - copied.unEnchantEntity(copied.getEnchanting()); + if (copied.isAttaching()) { + copied.unAttachEntity(copied.getAttaching()); } } @@ -1006,11 +972,10 @@ public class GameAction { } checkAgain |= stateBasedAction_Saga(c); - checkAgain |= stateBasedAction704_5n(c); // Auras attached to illegal or not attached go to graveyard - checkAgain |= stateBasedAction704_5p(c); // Equipment and Fortifications + checkAgain |= stateBasedAction704_attach(c); // Attachment - if (c.isCreature() && c.isEnchanting()) { // Rule 704.5q - Creature attached to an object or player, becomes unattached - c.unEnchantEntity(c.getEnchanting()); + if (c.isCreature() && c.isAttaching()) { // Rule 704.5q - Creature attached to an object or player, becomes unattached + c.unAttachEntity(c.getAttaching()); checkAgain = true; } @@ -1124,110 +1089,34 @@ public class GameAction { return checkAgain; } - private boolean stateBasedAction704_5n(Card c) { + private boolean stateBasedAction704_attach(Card c) { boolean checkAgain = false; - if (!c.isAura()) { - return false; - } - // Check if Card Aura is attached to is a legal target - final GameEntity entity = c.getEnchanting(); - SpellAbility sa = c.getFirstAttachSpell(); - - TargetRestrictions tgt = null; - if (sa != null) { - tgt = sa.getTargetRestrictions(); - } - - if (entity instanceof Card) { - final Card perm = (Card) entity; - final ZoneType tgtZone = tgt.getZone().get(0); - - if (!perm.isInZone(tgtZone) || !perm.canBeEnchantedBy(c, true) || (perm.isPhasedOut() && !c.isPhasedOut())) { - c.unEnchantEntity(perm); - moveToGraveyard(c, null, null); - checkAgain = true; - } - } else if (entity instanceof Player) { - final Player pl = (Player) entity; - boolean invalid = false; - - if (!game.getPlayers().contains(pl)) { - // lost player can't have Aura on it - invalid = true; - } else if (tgt.canOnlyTgtOpponent() && !c.getController().isOpponentOf(pl)) { - invalid = true; - } - else if (pl.hasProtectionFrom(c)) { - invalid = true; - } - if (invalid) { - c.unEnchantEntity(pl); - moveToGraveyard(c, null, null); + if (c.isAttaching()) { + final GameEntity ge = c.getAttaching(); + if (!ge.canBeAttachedBy(c)) { + c.unAttachEntity(ge); checkAgain = true; } } - if (c.isInPlay() && !c.isEnchanting()) { + if (c.isAttachedBy()) { + for (final Card attach : Lists.newArrayList(c.getAttachedBy())) { + if (!attach.isInPlay()) { + attach.unAttachEntity(c); + checkAgain = true; + } + } + } + + // cleanup aura + if (c.isAura() && c.isInPlay() && !c.isEnchanting()) { moveToGraveyard(c, null, null); checkAgain = true; } return checkAgain; } - private boolean stateBasedAction704_5p(Card c) { - boolean checkAgain = false; - if (c.isEquipped()) { - for (final Card equipment : c.getEquippedBy(true)) { - if (!equipment.isInPlay()) { - equipment.unEquipCard(c); - checkAgain = true; - } - } - } - - if (c.isFortified()) { - for (final Card f : c.getFortifiedBy(true)) { - if (!f.isInPlay()) { - f.unFortifyCard(c); - checkAgain = true; - } - } - } - - if (c.isEquipping()) { - final Card equippedCreature = c.getEquipping(); - if (!equippedCreature.isCreature() || !equippedCreature.isInPlay() - || !equippedCreature.canBeEquippedBy(c) - || (equippedCreature.isPhasedOut() && !c.isPhasedOut()) - || !c.isEquipment()) { - c.unEquipCard(equippedCreature); - checkAgain = true; - } - // make sure any equipment that has become a creature stops equipping - if (c.isCreature()) { - c.unEquipCard(equippedCreature); - checkAgain = true; - } - } - - if (c.isFortifying()) { - final Card fortifiedLand = c.getFortifying(); - if (!fortifiedLand.isLand() || !fortifiedLand.isInPlay() - || (fortifiedLand.isPhasedOut() && !c.isPhasedOut()) - || !c.isFortification()) { - c.unFortifyCard(fortifiedLand); - checkAgain = true; - } - // make sure any fortification that has become a creature stops fortifying - if (c.isCreature()) { - c.unFortifyCard(fortifiedLand); - checkAgain = true; - } - } - return checkAgain; - } - private boolean stateBasedAction704_5r(Card c) { boolean checkAgain = false; int plusOneCounters = c.getCounters(CounterType.P1P1); diff --git a/forge-game/src/main/java/forge/game/GameEntity.java b/forge-game/src/main/java/forge/game/GameEntity.java index 37a27344866..e040040f2c6 100644 --- a/forge-game/src/main/java/forge/game/GameEntity.java +++ b/forge-game/src/main/java/forge/game/GameEntity.java @@ -6,12 +6,12 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,9 +21,10 @@ import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; import forge.game.card.CardDamageMap; +import forge.game.card.CardLists; +import forge.game.card.CardPredicates; import forge.game.card.CounterType; import forge.game.event.GameEventCardAttachment; -import forge.game.event.GameEventCardAttachment.AttachMethod; import forge.game.keyword.Keyword; import forge.game.player.Player; import forge.game.spellability.SpellAbility; @@ -33,6 +34,7 @@ import forge.util.collect.FCollection; import java.util.Map; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -40,7 +42,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { protected final int id; private String name = ""; private int preventNextDamage = 0; - private CardCollection enchantedBy; + protected CardCollection attachedBy; private Map> preventionShieldsWithEffects = Maps.newTreeMap(); protected Map counters = Maps.newEnumMap(CounterType.class); @@ -93,7 +95,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { source.getDamageHistory().registerCombatDamage(this); } // damage prevention is already checked - return addCombatDamageBase(damageToDo, source, damageMap); + return addCombatDamageBase(damageToDo, source, damageMap); } protected int addCombatDamageBase(final int damage, final Card source, CardDamageMap damageMap) { @@ -204,17 +206,17 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { restDamage = 0; } - // if damage is greater than restDamage, damage was prevented + // if damage is greater than restDamage, damage was prevented if (damage > restDamage) { int prevent = damage - restDamage; preventMap.put(source, this, damage - restDamage); - + final Map runParams = Maps.newHashMap(); runParams.put("DamageTarget", this); runParams.put("DamageAmount", prevent); runParams.put("DamageSource", source); runParams.put("IsCombatDamage", isCombat); - + getGame().getTriggerHandler().runTrigger(TriggerType.DamagePrevented, runParams, false); } @@ -280,77 +282,140 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { public abstract boolean hasKeyword(final String keyword); public abstract boolean hasKeyword(final Keyword keyword); - // GameEntities can now be Enchanted - public final CardCollectionView getEnchantedBy(boolean allowModify) { - return CardCollection.getView(enchantedBy, allowModify); + public final CardCollectionView getEnchantedBy() { + if (attachedBy == null) { + return CardCollection.EMPTY; + } + // enchanted means attached by Aura + return CardLists.filter(attachedBy, CardPredicates.Presets.AURA); } - public final void setEnchantedBy(final CardCollection cards) { - enchantedBy = cards; - getView().updateEnchantedBy(this); + + public final CardCollectionView getAttachedBy() { + return CardCollection.getView(attachedBy); } - public final void setEnchantedBy(final Iterable cards) { + + public final void setAttachedBy(final Iterable cards) { if (cards == null) { - enchantedBy = null; + attachedBy = null; } else { - enchantedBy = new CardCollection(cards); + attachedBy = new CardCollection(cards); } - getView().updateEnchantedBy(this); + + getView().updateAttachedBy(this); } + + public final boolean isAttachedBy() { + return FCollection.hasElements(attachedBy); + } + public final boolean isEnchanted() { - return FCollection.hasElements(enchantedBy); + if (attachedBy == null) { + return false; + } + + // enchanted means attached by Aura + return CardLists.count(attachedBy, CardPredicates.Presets.AURA) > 0; + } + + public final boolean isAttachedBy(Card c) { + return FCollection.hasElement(attachedBy, c); } public final boolean isEnchantedBy(Card c) { - return FCollection.hasElement(enchantedBy, c); + // Rule 303.4k Even if c is no Aura it still counts + return isAttachedBy(c); + } + + public final boolean isAttachedBy(final String cardName) { + if (attachedBy == null) { + return false; + } + return CardLists.count(getAttachedBy(), CardPredicates.nameEquals(cardName)) > 0; } public final boolean isEnchantedBy(final String cardName) { - for (final Card aura : getEnchantedBy(false)) { - if (aura.getName().equals(cardName)) { - return true; - } + // Rule 303.4k Even if c is no Aura it still counts + return isAttachedBy(cardName); + } + + public final void addAttachedBy(final Card c) { + if (attachedBy == null) { + attachedBy = new CardCollection(); } + + if (attachedBy.add(c)) { + getView().updateAttachedBy(this); + getGame().fireEvent(new GameEventCardAttachment(c, null, this)); + } + } + + public final void removeAttachedBy(final Card c) { + if (attachedBy == null) { return; } + + if (attachedBy.remove(c)) { + if (attachedBy.isEmpty()) { + attachedBy = null; + } + getView().updateAttachedBy(this); + getGame().fireEvent(new GameEventCardAttachment(c, this, null)); + } + } + + public final void unAttachAllCards() { + for (Card c : Lists.newArrayList(getAttachedBy())) { + c.unAttachEntity(this); + } + } + + public boolean canBeAttachedBy(final Card attach) { + return canBeAttachedBy(attach, false); + } + + public boolean canBeAttachedBy(final Card attach, boolean checkSBA) { + // master mode + if (!attach.isAttachment()) { + return false; + } + + if (attach.isAura() && !canBeEnchantedBy(attach, checkSBA)) { + return false; + } + if (attach.isEquipment() && !canBeEquippedBy(attach)) { + return false; + } + if (attach.isFortification() && !canBeFortifiedBy(attach)) { + return false; + } + return true; + } + + public boolean canBeEquippedBy(final Card aura) { return false; } - public final void addEnchantedBy(final Card c) { - if (enchantedBy == null) { - enchantedBy = new CardCollection(); - } - if (enchantedBy.add(c)) { - getView().updateEnchantedBy(this); - getGame().fireEvent(new GameEventCardAttachment(c, null, this, AttachMethod.Enchant)); - } - } - public final void removeEnchantedBy(final Card c) { - if (enchantedBy == null) { return; } - if (enchantedBy.remove(c)) { - if (enchantedBy.isEmpty()) { - enchantedBy = null; - } - getView().updateEnchantedBy(this); - getGame().fireEvent(new GameEventCardAttachment(c, this, null, AttachMethod.Enchant)); - } - } - public final void unEnchantAllCards() { - if (isEnchanted()) { - for (Card c : getEnchantedBy(true)) { - c.unEnchantEntity(this); - } - } + public boolean canBeFortifiedBy(final Card aura) { + return false; } - public boolean canBeEnchantedBy(final Card aura) { + public boolean canBeEnchantedBy(final Card aura, final boolean checkSBA) { + if (!aura.isAura()) { + return false; + } + SpellAbility sa = aura.getFirstAttachSpell(); TargetRestrictions tgt = null; if (sa != null) { tgt = sa.getTargetRestrictions(); } - return !(hasProtectionFrom(aura) + return !(hasProtectionFrom(aura, checkSBA) || ((tgt != null) && !isValid(tgt.getValidTgts(), aura.getController(), aura, sa))); } - public abstract boolean hasProtectionFrom(final Card source); + public boolean hasProtectionFrom(final Card source) { + return hasProtectionFrom(source, false); + } + + public abstract boolean hasProtectionFrom(final Card source, final boolean checkSBA); // Counters! public boolean hasCounters() { diff --git a/forge-game/src/main/java/forge/game/GameEntityView.java b/forge-game/src/main/java/forge/game/GameEntityView.java index 6e97e5a68b0..46a8995ca3f 100644 --- a/forge-game/src/main/java/forge/game/GameEntityView.java +++ b/forge-game/src/main/java/forge/game/GameEntityView.java @@ -42,18 +42,19 @@ public abstract class GameEntityView extends TrackableObject { set(TrackableProperty.PreventNextDamage, e.getPreventNextDamageTotalShields()); } - public Iterable getEnchantedBy() { - return get(TrackableProperty.EnchantedBy); + public Iterable getAttachedBy() { + return get(TrackableProperty.AttachedBy); } - protected void updateEnchantedBy(GameEntity e) { - if (e.isEnchanted()) { - set(TrackableProperty.EnchantedBy, CardView.getCollection(e.getEnchantedBy(false))); + public boolean isAttached() { + return getAttachedBy() != null; + } + + protected void updateAttachedBy(GameEntity e) { + if (e.isAttachedBy()) { + set(TrackableProperty.AttachedBy, CardView.getCollection(e.getAttachedBy())); } else { - set(TrackableProperty.EnchantedBy, null); + set(TrackableProperty.AttachedBy, null); } } - public boolean isEnchanted() { - return getEnchantedBy() != null; - } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/AttachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AttachEffect.java index 952fb155e4f..9c3d1a72cba 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AttachEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AttachEffect.java @@ -27,7 +27,7 @@ public class AttachEffect extends SpellAbilityEffect { final Player ap = sa.getActivatingPlayer(); // The Spell_Permanent (Auras) version of this AF needs to // move the card into play before Attaching - + sa.getHostCard().setController(ap, 0); final Card c = ap.getGame().getAction().moveTo(ap.getZone(ZoneType.Battlefield), sa.getHostCard(), sa); sa.setHostCard(c); @@ -82,7 +82,7 @@ public class AttachEffect extends SpellAbilityEffect { /** * Handle attachment. - * + * * @param card * the card * @param o @@ -104,12 +104,8 @@ public class AttachEffect extends SpellAbilityEffect { if (c.canBeEnchantedBy(card)) { handleAura(card, c); } - } else if (card.isEquipment()) { - if(c.canBeEquippedBy(card)) { - card.equipCard(c); - } - } else if (card.isFortification()) { - card.fortifyCard(c); + } else { + card.attachEntity(c); } } else if (o instanceof Player) { // Currently, a few cards can enchant players @@ -124,42 +120,34 @@ public class AttachEffect extends SpellAbilityEffect { /** * Handle aura. - * + * * @param card * the card * @param tgt * the tgt */ public static void handleAura(final Card card, final GameEntity tgt) { - if (card.isEnchanting()) { - // If this Card is already Enchanting something - // Need to unenchant it, then clear out the commands - final GameEntity oldEnchanted = card.getEnchanting(); - oldEnchanted.removeEnchantedBy(card); - card.removeEnchanting(oldEnchanted); - } - final GameCommand onLeavesPlay = new GameCommand() { private static final long serialVersionUID = -639204333673364477L; @Override public void run() { - final GameEntity entity = card.getEnchanting(); + final GameEntity entity = card.getAttaching(); if (entity == null) { return; } - card.unEnchantEntity(entity); + card.unAttachEntity(entity); } }; // Command card.addLeavesPlayCommand(onLeavesPlay); - card.enchantEntity(tgt); + card.attachEntity(tgt); } /** * Attach aura on indirect enter battlefield. - * + * * @param source * the source * @return true, if successful diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index 0c0d1be8836..b67298bcc58 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -504,29 +504,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { } if (!list.isEmpty()) { Card attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, tgtC + " - Select a card to attach to."); - if (tgtC.isAura()) { - if (tgtC.isEnchanting()) { - // If this Card is already Enchanting something, need - // to unenchant it, then clear out the commands - final GameEntity oldEnchanted = tgtC.getEnchanting(); - tgtC.removeEnchanting(oldEnchanted); - } - tgtC.enchantEntity(attachedTo); - } else if (tgtC.isEquipment()) { //Equipment - if (tgtC.isEquipping()) { - final Card oldEquiped = tgtC.getEquipping(); - if ( null != oldEquiped ) - tgtC.unEquipCard(oldEquiped); - } - tgtC.equipCard(attachedTo); - } else { // fortification - if (tgtC.isFortifying()) { - final Card oldFortified = tgtC.getFortifying(); - if( oldFortified != null ) - tgtC.unFortifyCard(oldFortified); - } - tgtC.fortifyCard(attachedTo); - } + tgtC.attachEntity(attachedTo); } else { // When it should enter the battlefield attached to an illegal permanent it fails continue; } @@ -536,15 +514,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { FCollectionView list = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("AttachedToPlayer"), sa); if (!list.isEmpty()) { Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, tgtC + " - Select a player to attach to."); - if (tgtC.isAura()) { - if (tgtC.isEnchanting()) { - // If this Card is already Enchanting something, need - // to unenchant it, then clear out the commands - final GameEntity oldEnchanted = tgtC.getEnchanting(); - tgtC.removeEnchanting(oldEnchanted); - } - tgtC.enchantEntity(attachedTo); - } + tgtC.attachEntity(attachedTo); } else { // When it should enter the battlefield attached to an illegal player it fails continue; @@ -1022,42 +992,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect { else { attachedTo = list.get(0); } - if (c.isAura()) { - if (c.isEnchanting()) { - // If this Card is already Enchanting something, need - // to unenchant it, then clear out the commands - final GameEntity oldEnchanted = c.getEnchanting(); - c.removeEnchanting(oldEnchanted); - } - // TODO: this should ideally be handled with !attachedTo.canBeEnchantedBy(c), but the relevant function is not adapted - // for corner cases tested here and modifying it to accomodate these situations (e.g. Boonweaver Giant + Animate Dead) - // tends to break other things. - if (!checkCanIndirectlyAttachTo(c, attachedTo)) { - // if an aura can't enchant the source, it shouldn't move (303.4i, 303.4j) - continue; - } - if ( ((c.hasKeyword("Enchant creature card in a graveyard") || c.hasKeyword("Enchant instant card in a graveyard")) && !attachedTo.getZone().is(ZoneType.Graveyard)) - || !attachedTo.getZone().is(ZoneType.Battlefield)) { - // if the source of the effect is no longer in the zone where it can be enchanted, aura does not move - continue; - } - c.enchantEntity(attachedTo); - } - else if (c.isEquipment()) { //Equipment - if (c.isEquipping()) { - final Card oldEquiped = c.getEquipping(); - if ( null != oldEquiped ) - c.unEquipCard(oldEquiped); - } - c.equipCard(attachedTo); - } - else { - if (c.isFortifying()) { - final Card oldFortified = c.getFortifying(); - if ( null != oldFortified ) - c.unFortifyCard(oldFortified); - } - c.fortifyCard(attachedTo); + + if (c.isAttachment()) { + c.attachEntity(attachedTo); } } else { // When it should enter the battlefield attached to an illegal permanent it fails @@ -1069,15 +1006,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { FCollectionView list = AbilityUtils.getDefinedPlayers(source, sa.getParam("AttachedToPlayer"), sa); if (!list.isEmpty()) { Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, c + " - Select a player to attach to."); - if (c.isAura()) { - if (c.isEnchanting()) { - // If this Card is already Enchanting something, need - // to unenchant it, then clear out the commands - final GameEntity oldEnchanted = c.getEnchanting(); - c.removeEnchanting(oldEnchanted); - } - c.enchantEntity(attachedTo); - } + c.attachEntity(attachedTo); } else { // When it should enter the battlefield attached to an illegal permanent it fails continue; diff --git a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java index 5298ed95005..504ebad7e4b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java @@ -154,9 +154,9 @@ public class CopyPermanentEffect extends SpellAbilityEffect { choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host); if (!choices.isEmpty()) { String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : "Choose a card "; - + Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false); - + if (choosen != null) { tgtCards.add(choosen); } @@ -238,21 +238,8 @@ public class CopyPermanentEffect extends SpellAbilityEffect { } if (!list.isEmpty()) { Card attachedTo = activator.getController().chooseSingleEntityForEffect(list, sa, copyInPlay + " - Select a card to attach to."); - if (copyInPlay.isAura()) { - if (attachedTo.canBeEnchantedBy(copyInPlay)) { - copyInPlay.enchantEntity(attachedTo); - } else {//can't enchant - continue; - } - } else if (copyInPlay.isEquipment()) { //Equipment - if (attachedTo.canBeEquippedBy(copyInPlay)) { - copyInPlay.equipCard(attachedTo); - } else { - continue; - } - } else { // Fortification - copyInPlay.fortifyCard(attachedTo); - } + + copyInPlay.attachEntity(attachedTo); } else { continue; } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java index ab0e2a0b826..8faf7474655 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java @@ -95,9 +95,7 @@ public class DestroyEffect extends SpellAbilityEffect { boolean destroyed = false; final Card lki = CardUtil.getLKICopy(tgtC); if (remAttached) { - card.addRemembered(tgtC.getEnchantedBy(false)); - card.addRemembered(tgtC.getEquippedBy(false)); - card.addRemembered(tgtC.getFortifiedBy(false)); + card.addRemembered(tgtC.getAttachedBy()); } if (sac) { destroyed = game.getAction().sacrifice(tgtC, sa) != null; diff --git a/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java b/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java index effe1d5784a..abec8e52996 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java @@ -495,32 +495,10 @@ public class TokenEffect extends SpellAbilityEffect { game.getAction().checkStaticAbilities(false, Sets.newHashSet(lki), preList); // TODO update when doing Attach Update - boolean canAttach = lki.isAura() || lki.isEquipment() || lki.isFortification(); + boolean canAttach = lki.isAttachment(); - if (lki.isAura()) { - if (!ge.canBeEnchantedBy(lki)) { - canAttach = false; - } - } - if (lki.isEquipment()) { - if (ge instanceof Card) { - Card gc = (Card) ge; - if (!gc.canBeEquippedBy(lki)) { - canAttach = false; - } - } else { - canAttach = false; - } - } - if (lki.isFortification()) { - if (ge instanceof Card) { - Card gc = (Card) ge; - if (!gc.isLand()) { - canAttach = false; - } - } else { - canAttach = false; - } + if (canAttach && ge.canBeAttachedBy(lki)) { + canAttach = false; } // reset static abilities @@ -531,20 +509,7 @@ public class TokenEffect extends SpellAbilityEffect { return false; } - // TODO update when doing Attach Update - if (lki.isAura()) { - tok.enchantEntity(ge); - } else if (lki.isEquipment()) { - if (ge instanceof Card) { - Card gc = (Card) ge; - tok.equipCard(gc); - } - } else if (lki.isFortification()) { - if (ge instanceof Card) { - Card gc = (Card) ge; - tok.fortifyCard(gc); - } - } + tok.attachEntity(ge); return true; } else { // not a GameEntity, cant be attach diff --git a/forge-game/src/main/java/forge/game/ability/effects/UnattachAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/UnattachAllEffect.java index 9d301e059b2..33cca9dcbed 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/UnattachAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/UnattachAllEffect.java @@ -17,25 +17,8 @@ import java.util.List; public class UnattachAllEffect extends SpellAbilityEffect { private static void handleUnattachment(final GameEntity o, final Card cardToUnattach) { - if (o instanceof Card) { - final Card c = (Card) o; - if (cardToUnattach.isAura()) { - //final boolean gainControl = "GainControl".equals(af.parseParams().get("AILogic")); - //AbilityFactoryAttach.handleUnattachAura(cardToUnattach, c, gainControl); - } else if (cardToUnattach.isEquipment()) { - if (cardToUnattach.isEquipping() && c.isEquippedBy(cardToUnattach)) { - cardToUnattach.unEquipCard(cardToUnattach.getEquipping()); - } - } else if (cardToUnattach.isFortification()) { - if (cardToUnattach.isFortifying() && c.isFortifiedBy(cardToUnattach)) { - cardToUnattach.unFortifyCard(cardToUnattach.getFortifying()); - } - } - } else if (o instanceof Player) { - final Player p = (Player) o; - if (cardToUnattach.isAura() && p.isEnchantedBy(cardToUnattach)) { - //AbilityFactoryAttach.handleUnattachAura(cardToUnattach, p, false); - } + if (cardToUnattach.isAttachment() && o.isAttachedBy(cardToUnattach)) { + cardToUnattach.unAttachEntity(cardToUnattach.getAttaching()); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/UnattachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/UnattachEffect.java index ba6635ee1b2..28d11ca6e9c 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/UnattachEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/UnattachEffect.java @@ -31,13 +31,9 @@ public class UnattachEffect extends SpellAbilityEffect { if (cardToUnattach.isAura()) { //final boolean gainControl = "GainControl".equals(af.parseParams().get("AILogic")); //AbilityFactoryAttach.handleUnattachAura(cardToUnattach, c, gainControl); - } else if (cardToUnattach.isEquipment()) { - if (cardToUnattach.isEquipping()) { - cardToUnattach.unEquipCard(cardToUnattach.getEquipping()); - } - } else if (cardToUnattach.isFortification()) { - if (cardToUnattach.isFortifying()) { - cardToUnattach.unFortifyCard(cardToUnattach.getFortifying()); + } else if (cardToUnattach.isAttachment()) { + if (cardToUnattach.isAttaching()) { + cardToUnattach.unAttachEntity(cardToUnattach.getAttaching()); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ZoneExchangeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ZoneExchangeEffect.java index 1b5359d09bc..bef78d2e537 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ZoneExchangeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ZoneExchangeEffect.java @@ -83,8 +83,8 @@ public class ZoneExchangeEffect extends SpellAbilityEffect { } // Enchant first if (c != null) { - object1.unEnchantEntity(c); - object2.enchantEntity(c); + object1.unAttachEntity(c); + object2.attachEntity(c); } // Exchange Zone game.getAction().moveTo(zone2, object1, sa); diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index f642fe8c117..ae16fd3dfd3 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -37,7 +37,6 @@ import forge.game.combat.Combat; import forge.game.cost.Cost; import forge.game.cost.CostSacrifice; import forge.game.event.*; -import forge.game.event.GameEventCardAttachment.AttachMethod; import forge.game.event.GameEventCardDamaged.DamageType; import forge.game.keyword.Keyword; import forge.game.keyword.KeywordCollection; @@ -98,14 +97,13 @@ public class Card extends GameEntity implements Comparable { private final KeywordCollection hiddenExtrinsicKeyword = new KeywordCollection(); // cards attached or otherwise linked to this card - private CardCollection equippedBy, fortifiedBy, hauntedBy, devouredCards, delvedCards, convokedCards, imprintedCards, encodedCards; + private CardCollection hauntedBy, devouredCards, delvedCards, convokedCards, imprintedCards, encodedCards; private CardCollection mustBlockCards, clones, gainControlTargets, chosenCards, blockedThisTurn, blockedByThisTurn; // if this card is attached or linked to something, what card is it currently attached to - private Card equipping, encoding, fortifying, cloneOrigin, haunting, effectSource, pairedWith, meldedWith; + private Card encoding, cloneOrigin, haunting, effectSource, pairedWith, meldedWith; - // if this card is an Aura, what Entity is it enchanting? - private GameEntity enchanting = null; + private GameEntity attaching = null; private GameEntity mustAttackEntity = null; private GameEntity mustAttackEntityThisTurn = null; @@ -176,7 +174,7 @@ public class Card extends GameEntity implements Comparable { // for Vanguard / Manapool / Emblems etc. private boolean isImmutable = false; - + private int exertThisTurn = 0; private PlayerCollection exertedByPlayer = new PlayerCollection(); @@ -413,7 +411,7 @@ public class Card extends GameEntity implements Comparable { if (!changedCardKeywords.isEmpty()) { currentState.getView().updateKeywords(this, currentState); } - + if (state == CardStateName.FaceDown) { view.updateHiddenId(game.nextHiddenCardId()); } @@ -902,7 +900,7 @@ public class Card extends GameEntity implements Comparable { public final void clearTriggersNew() { currentState.clearTriggers(); } - + public final boolean hasTrigger(final Trigger t) { return currentState.hasTrigger(t); } @@ -1064,7 +1062,7 @@ public class Card extends GameEntity implements Comparable { public final boolean hasSecondStrike() { return hasDoubleStrike() || !hasFirstStrike(); } - + public final boolean hasConverge() { return "Count$Converge".equals(getSVar("X")) || "Count$Converge".equals(getSVar("Y")) || hasKeyword("Sunburst"); } @@ -1290,7 +1288,7 @@ public class Card extends GameEntity implements Comparable { public final void removeSVar(final String var) { currentState.removeSVar(var); } - + public final int sumAllCounters() { int count = 0; for (final Integer value2 : counters.values()) { @@ -1519,7 +1517,7 @@ public class Card extends GameEntity implements Comparable { sbLong.append(parts[0]).append(" ").append(ManaCostParser.parse(parts[1])).append("\r\n"); } } else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph")) { - String[] k = keyword.split(":"); + String[] k = keyword.split(":"); sbLong.append(k[0]); if (k.length > 1) { final Cost mCost = new Cost(k[1], true); @@ -1704,7 +1702,7 @@ public class Card extends GameEntity implements Comparable { if (sbLong.length() > 0) { sbLong.append("\r\n"); } - + i++; } if (sb.length() > 0) { @@ -2223,7 +2221,7 @@ public class Card extends GameEntity implements Comparable { public final FCollectionView getNonManaAbilities() { return currentState.getNonManaAbilities(); } - + public final boolean hasSpellAbility(final SpellAbility sa) { return currentState.hasSpellAbility(sa); } @@ -2410,7 +2408,7 @@ public class Card extends GameEntity implements Comparable { public final void addUntapCommand(final GameCommand c) { untapCommandList.add(c); } - + public final void addUnattachCommand(final GameCommand c) { unattachCommandList.add(c); } @@ -2472,7 +2470,7 @@ public class Card extends GameEntity implements Comparable { public void setStartedTheTurnUntapped(boolean untapped) { startedTheTurnUntapped = untapped; } - + public boolean cameUnderControlSinceLastUpkeep() { return cameUnderControlSinceLastUpkeep; } @@ -2585,186 +2583,126 @@ public class Card extends GameEntity implements Comparable { } } - public final CardCollectionView getEquippedBy(boolean allowModify) { - return CardCollection.getView(equippedBy, allowModify); - } - public final void setEquippedBy(final CardCollection cards) { - equippedBy = view.setCards(equippedBy, cards, TrackableProperty.EquippedBy); - } - public final void setEquippedBy(final Iterable cards) { - equippedBy = view.setCards(equippedBy, cards, TrackableProperty.EquippedBy); - } - public final boolean isEquipped() { - return FCollection.hasElements(equippedBy); - } - public final boolean isEquippedBy(Card c) { - return FCollection.hasElement(equippedBy, c); - } - public final boolean isEquippedBy(final String cardName) { - for (final Card card : getEquippedBy(false)) { - if (card.getName().equals(cardName)) { - return true; - } + public final CardCollectionView getEquippedBy() { + if (this.attachedBy == null) { + return CardCollection.EMPTY; } - return false; + + return CardLists.filter(attachedBy, CardPredicates.Presets.EQUIPMENT); } - public final CardCollectionView getFortifiedBy(boolean allowModify) { - return CardCollection.getView(fortifiedBy, allowModify); + public final boolean isEquipped() { + if (this.attachedBy == null) { + return false; + } + + return CardLists.count(attachedBy, CardPredicates.Presets.EQUIPMENT) > 0; } - public final void setFortifiedBy(final CardCollection cards) { - fortifiedBy = view.setCards(fortifiedBy, cards, TrackableProperty.FortifiedBy); + public final boolean isEquippedBy(Card c) { + return this.isAttachedBy(c); } - public final void setFortifiedBy(final Iterable cards) { - fortifiedBy = view.setCards(fortifiedBy, cards, TrackableProperty.FortifiedBy); + public final boolean isEquippedBy(final String cardName) { + return this.isAttachedBy(cardName); } + + public final CardCollectionView getFortifiedBy() { + if (this.attachedBy == null) { + return CardCollection.EMPTY; + } + + return CardLists.filter(attachedBy, CardPredicates.Presets.FORTIFICATION); + } + public final boolean isFortified() { - return FCollection.hasElements(fortifiedBy); + if (this.attachedBy == null) { + return false; + } + + return CardLists.count(attachedBy, CardPredicates.Presets.FORTIFICATION) > 0; } public final boolean isFortifiedBy(Card c) { - return FCollection.hasElement(fortifiedBy, c); + // 301.5e + 301.6 + return isAttachedBy(c); } public final Card getEquipping() { - return equipping; - } - public final void setEquipping(final Card card) { - equipping = view.setCard(equipping, card, TrackableProperty.Equipping); + return this.getAttachingCard(); } public final boolean isEquipping() { - return equipping != null; + return this.isAttaching(); } - public final Card getFortifying() { - return fortifying; - } - public final void setFortifying(final Card card) { - fortifying = view.setCard(fortifying, card, TrackableProperty.Fortifying); - } public final boolean isFortifying() { - return fortifying != null; + return this.isAttaching(); } public final void equipCard(final Card c) { - if (c.hasKeyword("CARDNAME can't be equipped.")) { - getGame().getGameLog().add(GameLogEntryType.STACK_RESOLVE, "Trying to equip " + c.getName() + " but it can't be equipped."); + if (!isEquipment()) { return; } - - for(KeywordInterface inst : c.getKeywords()) { - String kw = inst.getOriginal(); - if (!kw.startsWith("CantEquip")) { - continue; - } - final String[] k = kw.split(" ", 2); - final String[] restrictions = k[1].split(","); - if (c.isValid(restrictions, getController(), this, null)) { - getGame().getGameLog().add(GameLogEntryType.STACK_RESOLVE, "Trying to equip " + c.getName() + " but it can't be equipped."); - return; - } - } - Card oldTarget = null; - if (isEquipping()) { - oldTarget = equipping; - if (oldTarget.equals(c)) { - // If attempting to reattach to the same object, don't do anything. - return; - } - unEquipCard(oldTarget); - } - - // They use double links... it's doubtful - setEquipping(c); - setTimestamp(getGame().getNextTimestamp()); - c.equippedBy = c.view.addCard(c.equippedBy, this, TrackableProperty.EquippedBy); - - // Play the Equip sound - getGame().fireEvent(new GameEventCardAttachment(this, oldTarget, c, AttachMethod.Equip)); - - // run trigger - final Map runParams = Maps.newHashMap(); - runParams.put("AttachSource", this); - runParams.put("AttachTarget", c); - getController().getGame().getTriggerHandler().runTrigger(TriggerType.Attached, runParams, false); + this.attachEntity(c); } public final void fortifyCard(final Card c) { - Card oldTarget = null; - if (isFortifying()) { - oldTarget = fortifying; - unFortifyCard(oldTarget); + if (!isFortification()) { + return; } - setFortifying(c); - setTimestamp(getGame().getNextTimestamp()); - c.fortifiedBy = c.view.addCard(c.fortifiedBy, this, TrackableProperty.FortifiedBy); - - // Play the Equip sound - getGame().fireEvent(new GameEventCardAttachment(this, oldTarget, c, AttachMethod.Fortify)); - // run trigger - final Map runParams = Maps.newHashMap(); - runParams.put("AttachSource", this); - runParams.put("AttachTarget", c); - getController().getGame().getTriggerHandler().runTrigger(TriggerType.Attached, runParams, false); + this.attachEntity(c); } public final void unEquipCard(final Card c) { // equipment.unEquipCard(equippedCard); - if (equipping != null && equipping.getId() == c.getId()) { - setEquipping(null); - } - c.equippedBy = c.view.removeCard(c.equippedBy, this, TrackableProperty.EquippedBy); - - getGame().fireEvent(new GameEventCardAttachment(this, c, null, AttachMethod.Equip)); - - // Run triggers - final Map runParams = Maps.newTreeMap(); - runParams.put("Equipment", this); - runParams.put("Card", c); - getGame().getTriggerHandler().runTrigger(TriggerType.Unequip, runParams, false); - runUnattachCommands(); - } - - public final void unFortifyCard(final Card c) { // fortification.unEquipCard(fortifiedCard); - if (fortifying == c) { - setFortifying(null); - } - c.fortifiedBy = c.view.removeCard(c.fortifiedBy, this, TrackableProperty.FortifiedBy); - - getGame().fireEvent(new GameEventCardAttachment(this, c, null, AttachMethod.Fortify)); - runUnattachCommands(); + this.unAttachEntity(c); } public final void unEquipAllCards() { if (isEquipped()) { - for (Card c : getEquippedBy(true)) { - c.unEquipCard(this); + for (Card c : Lists.newArrayList(getEquippedBy())) { + c.unAttachEntity(this); } } } - public final GameEntity getEnchanting() { - return enchanting; + public final GameEntity getAttaching() { + return attaching; } - public final void setEnchanting(final GameEntity e) { - if (enchanting == e) { return; } - enchanting = e; - view.updateEnchanting(this); + public final void setAttaching(final GameEntity e) { + if (attaching == e) { return; } + attaching = e; + view.updateAttaching(this); } - public final Card getEnchantingCard() { - if (enchanting instanceof Card) { - return (Card) enchanting; + public final void removeAttaching(final GameEntity e) { + if (attaching == e) { + setAttaching(null); + } + } + public final boolean isAttaching() { + return attaching != null; + } + + public final Card getAttachingCard() { + if (attaching instanceof Card) { + return (Card) attaching; } return null; } + + public final GameEntity getEnchanting() { + return getAttaching(); + } + + public final Card getEnchantingCard() { + return getAttachingCard(); + } public final Player getEnchantingPlayer() { - if (enchanting instanceof Player) { - return (Player) enchanting; + if (attaching instanceof Player) { + return (Player) attaching; } return null; } public final boolean isEnchanting() { - return enchanting != null; + return isAttaching(); } public final boolean isEnchantingCard() { return getEnchantingCard() != null; @@ -2773,43 +2711,63 @@ public class Card extends GameEntity implements Comparable { return getEnchantingPlayer() != null; } - public final void removeEnchanting(final GameEntity e) { - if (enchanting == e) { - setEnchanting(null); - } - } - - public final void enchantEntity(final GameEntity entity) { - if (entity.hasKeyword("CARDNAME can't be enchanted.") - || entity.hasKeyword("CARDNAME can't be enchanted in the future.")) { - getGame().getGameLog().add(GameLogEntryType.STACK_RESOLVE, "Trying to enchant " + entity.getName() - + " but it can't be enchanted."); + public final void attachEntity(final GameEntity entity) { + if (!entity.canBeAttachedBy(this)) { return; } - setEnchanting(entity); - setTimestamp(getGame().getNextTimestamp()); - entity.addEnchantedBy(this); - getGame().fireEvent(new GameEventCardAttachment(this, null, entity, AttachMethod.Enchant)); + GameEntity oldTarget = null; + if (isAttaching()) { + oldTarget = getAttaching(); + // If attempting to reattach to the same object, don't do anything. + if (oldTarget.equals(entity)) { + return; + } + unAttachEntity(oldTarget); + } + + // They use double links... it's doubtful + setAttaching(entity); + setTimestamp(getGame().getNextTimestamp()); + entity.addAttachedBy(this); + + // Play the Equip sound + getGame().fireEvent(new GameEventCardAttachment(this, oldTarget, entity)); // run trigger final Map runParams = Maps.newHashMap(); runParams.put("AttachSource", this); runParams.put("AttachTarget", entity); getController().getGame().getTriggerHandler().runTrigger(TriggerType.Attached, runParams, false); + } - public final void unEnchantEntity(final GameEntity entity) { - if (enchanting == null || !enchanting.equals(entity)) { + public final void enchantEntity(final GameEntity entity) { + if (!isAura()) { + return; + } + this.attachEntity(entity); + } + + public final void unAttachEntity(final GameEntity entity) { + if (attaching == null || !attaching.equals(entity)) { return; } - setEnchanting(null); - entity.removeEnchantedBy(this); + setAttaching(null); + entity.removeAttachedBy(this); + + // Handle Bestowed Aura part if (isBestowed()) { unanimateBestow(); } - getGame().fireEvent(new GameEventCardAttachment(this, entity, null, AttachMethod.Enchant)); + getGame().fireEvent(new GameEventCardAttachment(this, entity, null)); + + // Run triggers + final Map runParams = Maps.newTreeMap(); + runParams.put("Attach", this); + runParams.put("Object", entity); + getGame().getTriggerHandler().runTrigger(TriggerType.Unattach, runParams, false); runUnattachCommands(); } @@ -2820,11 +2778,11 @@ public class Card extends GameEntity implements Comparable { public final void addType(final String type0) { currentState.addType(type0); } - + public final void removeType(final CardType.Supertype st) { currentState.removeType(st); } - + public final void setCreatureTypes(Collection ctypes) { currentState.setCreatureTypes(ctypes); } @@ -2832,7 +2790,7 @@ public class Card extends GameEntity implements Comparable { public final CardTypeView getType() { return getType(currentState); } - + public final CardTypeView getType(CardState state) { if (changedCardTypes.isEmpty()) { return state.getType(); @@ -2843,7 +2801,7 @@ public class Card extends GameEntity implements Comparable { public Iterable getChangedCardTypes() { return Iterables.unmodifiableIterable(changedCardTypes.values()); } - + public Map getChangedCardTypesMap() { return Collections.unmodifiableMap(changedCardTypes); } @@ -3050,7 +3008,7 @@ public class Card extends GameEntity implements Comparable { public final void addNewPT(final Integer power, final Integer toughness, final long timestamp) { addNewPT(power, toughness, timestamp, false); } - + public final void addNewPT(final Integer power, final Integer toughness, final long timestamp, final boolean cda) { if (cda) { newPTCharacterDefining.put(timestamp, Pair.of(power, toughness)); @@ -3063,10 +3021,10 @@ public class Card extends GameEntity implements Comparable { public final void removeNewPT(final long timestamp) { boolean removed = false; - + removed |= newPT.remove(timestamp) != null; removed |= newPTCharacterDefining.remove(timestamp) != null; - + if (removed) { currentState.getView().updatePower(this); currentState.getView().updateToughness(this); @@ -3358,7 +3316,7 @@ public class Card extends GameEntity implements Comparable { final boolean removeAllKeywords, final boolean removeIntrinsicKeywords, final long timestamp) { addChangedCardKeywords(keywords, removeKeywords, removeAllKeywords, removeIntrinsicKeywords, timestamp, true); } - + public final void addChangedCardKeywords(final List keywords, final List removeKeywords, final boolean removeAllKeywords, final boolean removeIntrinsicKeywords, final long timestamp, final boolean updateView) { @@ -3377,12 +3335,12 @@ public class Card extends GameEntity implements Comparable { newCks.addKeywordsToCard(this); changedCardKeywords.put(timestamp, newCks); } - + if (updateView) { updateKeywords(); } } - + public final void addChangedCardKeywordsInternal( final List keywords, final List removeKeywords, final boolean removeAllKeywords, final boolean removeIntrinsicKeywords, @@ -3404,7 +3362,7 @@ public class Card extends GameEntity implements Comparable { newCks.addKeywordsToCard(this); changedCardKeywords.put(timestamp, newCks); } - + if (updateView) { updateKeywords(); } @@ -3432,7 +3390,7 @@ public class Card extends GameEntity implements Comparable { public final KeywordsChange removeChangedCardKeywords(final long timestamp, final boolean updateView) { KeywordsChange change = changedCardKeywords.remove(timestamp); - if (change != null && updateView) { + if (change != null && updateView) { updateKeywords(); } return change; @@ -3445,10 +3403,10 @@ public class Card extends GameEntity implements Comparable { public final Collection getUnhiddenKeywords(CardState state) { return state.getCachedKeywords(); } - + public final void updateKeywordsCache(final CardState state) { KeywordCollection keywords = new KeywordCollection(); - + //final List keywords = Lists.newArrayList(); boolean removeIntrinsic = false; for (final KeywordsChange ck : changedCardKeywords.values()) { @@ -3568,7 +3526,7 @@ public class Card extends GameEntity implements Comparable { String oldtxt = kw.getOriginal(); final String newtxt = AbilityUtils.applyKeywordTextChangeEffects(oldtxt, this); if (!newtxt.equals(oldtxt)) { - KeywordInterface newKw = Keyword.getInstance(newtxt); + KeywordInterface newKw = Keyword.getInstance(newtxt); addKeywords.add(newKw); removeKeywords.add(kw); keywordsGrantedByTextChanges.add(newKw); @@ -3733,7 +3691,7 @@ public class Card extends GameEntity implements Comparable { currentState.getView().updateKeywords(this, currentState); } } - + public final void addHiddenExtrinsicKeyword(KeywordInterface k) { if (hiddenExtrinsicKeyword.insert(k)) { view.updateNonAbilityText(this); @@ -3788,7 +3746,7 @@ public class Card extends GameEntity implements Comparable { public final void removeStaticAbility(StaticAbility stAb) { currentState.removeStaticAbility(stAb); } - + public void updateStaticAbilities(List list, CardState state) { for (KeywordInterface kw : getUnhiddenKeywords(state)) { list.addAll(kw.getStaticAbilities()); @@ -3820,7 +3778,9 @@ public class Card extends GameEntity implements Comparable { public final boolean isPlaneswalker() { return getType().isPlaneswalker(); } public final boolean isEnchantment() { return getType().isEnchantment(); } public final boolean isAura() { return getType().hasSubtype("Aura"); } - public final boolean isHistoric() {return getType().isLegendary() || getType().isArtifact() || getType().hasSubtype("Saga");} + + public final boolean isAttachment() { return isAura() || isEquipment() || isFortification(); } + public final boolean isHistoric() {return getType().isLegendary() || isArtifact() || getType().hasSubtype("Saga");} public final boolean isScheme() { return getType().isScheme(); } public final boolean isPhenomenon() { return getType().isPhenomenon(); } @@ -3886,27 +3846,13 @@ public class Card extends GameEntity implements Comparable { setDirectlyPhasedOut(direct); } - if (isEquipped()) { - for (final Card eq : getEquippedBy(false)) { + if (isAttachedBy()) { + for (final Card eq : getAttachedBy()) { if (eq.isPhasedOut() == phasingIn) { eq.phase(false); } } } - if (isFortified()) { - for (final Card f : getFortifiedBy(false)) { - if (f.isPhasedOut() == phasingIn) { - f.phase(false); - } - } - } - if (isEnchanted()) { - for (final Card aura : getEnchantedBy(false)) { - if (aura.isPhasedOut() == phasingIn) { - aura.phase(false); - } - } - } getGame().fireEvent(new GameEventCardPhased(this, isPhasedOut())); } @@ -4181,7 +4127,7 @@ public class Card extends GameEntity implements Comparable { //do not check for SplitCard anymore return host.getCMC() == n; } - + public final boolean sharesCMCWith(final Card c1) { //need to get GameState for Discarded Cards final Card host = game.getCardState(this); @@ -4784,7 +4730,7 @@ public class Card extends GameEntity implements Comparable { return (c != null ? c.getImageKey() : ""); } - + public final boolean isTributed() { return tributed; } public final void setTributed(final boolean b) { @@ -4808,7 +4754,7 @@ public class Card extends GameEntity implements Comparable { public final int getExertedThisTurn() { return exertThisTurn; } - + public void exert() { exertedByPlayer.add(getController()); exertThisTurn++; @@ -4818,16 +4764,16 @@ public class Card extends GameEntity implements Comparable { runParams.put("Player", getController()); game.getTriggerHandler().runTrigger(TriggerType.Exerted, runParams, false); } - + public boolean isExertedBy(final Player player) { return exertedByPlayer.contains(player); } - + public void removeExertedBy(final Player player) { exertedByPlayer.remove(player); view.updateExertedThisTurn(this, getExertedThisTurn() > 0); } - + protected void resetExtertedThisTurn() { exertThisTurn = 0; view.updateExertedThisTurn(this, false); @@ -4917,8 +4863,8 @@ public class Card extends GameEntity implements Comparable { public final CardCollectionView getDevouredCards() { return CardCollection.getView(devouredCards); - } - + } + public final CardCollectionView getHauntedBy() { return CardCollection.getView(hauntedBy); } @@ -4997,7 +4943,7 @@ public class Card extends GameEntity implements Comparable { return sum; } - @Override + public boolean hasProtectionFrom(final Card source) { return hasProtectionFrom(source, false, false); } @@ -5006,6 +4952,7 @@ public class Card extends GameEntity implements Comparable { return hasProtectionFrom(source, false, true); } + @Override public boolean hasProtectionFrom(final Card source, final boolean checkSBA) { return hasProtectionFrom(source, checkSBA, false); } @@ -5019,6 +4966,11 @@ public class Card extends GameEntity implements Comparable { return true; } + // Protection only works on the Battlefield + if (isInZone(ZoneType.Battlefield)) { + return false; + } + final boolean colorlessDamage = damageSource && source.hasKeyword("Colorless Damage Source"); for (final KeywordInterface inst : getKeywords()) { @@ -5078,11 +5030,11 @@ public class Card extends GameEntity implements Comparable { // if colorlessDamage then it does only check damage color.. if (colorlessDamage) { - if (characteristic.endsWith("White") || characteristic.endsWith("Blue") - || characteristic.endsWith("Black") || characteristic.endsWith("Red") + if (characteristic.endsWith("White") || characteristic.endsWith("Blue") + || characteristic.endsWith("Black") || characteristic.endsWith("Red") || characteristic.endsWith("Green") || characteristic.endsWith("Colorless") || characteristic.endsWith("ChosenColor")) { - characteristic += "Source"; + characteristic += "Source"; } } @@ -5221,14 +5173,50 @@ public class Card extends GameEntity implements Comparable { tgt = sa.getTargetRestrictions(); } + if (aura.isCreature()) { + return false; + } + + // phase check there + if (isPhasedOut() && !aura.isPhasedOut()) { + return false; + } + + if (tgt != null) { + boolean zoneValid = false; + // check the zone types + for (final ZoneType zt : tgt.getZone()) { + if (isInZone(zt)) { + zoneValid = true; + break; + } + } + if (!zoneValid) { + return false; + } + + // check valid + if (!isValid(tgt.getValidTgts(), aura.getController(), aura, sa)) { + return false; + } + } + return !(hasProtectionFrom(aura, checkSBA) || (hasKeyword("CARDNAME can't be enchanted in the future.") && !isEnchantedBy(aura)) || (hasKeyword("CARDNAME can't be enchanted.") && !aura.getName().equals("Anti-Magic Aura") - && !(aura.getName().equals("Consecrate Land") && aura.isInZone(ZoneType.Battlefield))) - || ((tgt != null) && !isValid(tgt.getValidTgts(), aura.getController(), aura, sa))); + && !(aura.getName().equals("Consecrate Land") && aura.isInZone(ZoneType.Battlefield)))); } public final boolean canBeEquippedBy(final Card equip) { + if (!isCreature() || !isInPlay() || equip.isCreature()) { + return false; + } + + // phase check there + if (isPhasedOut() && !equip.isPhasedOut()) { + return false; + } + for(KeywordInterface inst : equip.getKeywords()) { String kw = inst.getOriginal(); if(!kw.startsWith("CantEquip")) { @@ -5241,8 +5229,19 @@ public class Card extends GameEntity implements Comparable { } } return !(hasProtectionFrom(equip) - || hasKeyword("CARDNAME can't be equipped.") - || !isValid("Creature", equip.getController(), equip, null)); + || hasKeyword("CARDNAME can't be equipped.")); + } + + public boolean canBeFortifiedBy(final Card fort) { + if (!isLand() || !isInPlay() || fort.isCreature() || fort.isLand()) { + return false; + } + + // phase check there + if (isPhasedOut() && !fort.isPhasedOut()) { + return false; + } + return !hasProtectionFrom(fort); } public FCollectionView getReplacementEffects() { @@ -5265,7 +5264,7 @@ public class Card extends GameEntity implements Comparable { public void removeReplacementEffect(ReplacementEffect replacementEffect) { currentState.removeReplacementEffect(replacementEffect); } - + public void updateReplacementEffects(List list, CardState state) { for (KeywordInterface kw : getUnhiddenKeywords(state)) { list.addAll(kw.getReplacements()); @@ -5449,7 +5448,7 @@ public class Card extends GameEntity implements Comparable { public final void setLKICMC(final int cmc) { this.lkiCMC = cmc; } - + public final boolean isLKI() { return this.lkiCMC >= 0; } @@ -5758,7 +5757,7 @@ public class Card extends GameEntity implements Comparable { public final void addGoad(Long timestamp, final Player p) { goad.put(timestamp, p); } - + public final void removeGoad(Long timestamp) { goad.remove(timestamp); } diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index 42df8d6e1f7..691b0565988 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -92,12 +92,8 @@ public class CardFactory { out.setState(in.getCurrentStateName(), true); // this's necessary for forge.game.GameAction.unattachCardLeavingBattlefield(Card) - out.setEquipping(in.getEquipping()); - out.setEquippedBy(in.getEquippedBy(false)); - out.setFortifying(in.getFortifying()); - out.setFortifiedBy(in.getFortifiedBy(false)); - out.setEnchantedBy(in.getEnchantedBy(false)); - out.setEnchanting(in.getEnchanting()); + out.setAttachedBy(in.getAttachedBy()); + out.setAttaching(in.getAttaching()); out.setClones(in.getClones()); out.setCastSA(in.getCastSA()); diff --git a/forge-game/src/main/java/forge/game/card/CardPredicates.java b/forge-game/src/main/java/forge/game/card/CardPredicates.java index c6180228a3d..a3eeb8dd5b6 100644 --- a/forge-game/src/main/java/forge/game/card/CardPredicates.java +++ b/forge-game/src/main/java/forge/game/card/CardPredicates.java @@ -470,6 +470,15 @@ public final class CardPredicates { return c.isEnchantment(); } }; + /** + * a Predicate to get all aura. + */ + public static final Predicate AURA = new Predicate() { + @Override + public boolean apply(Card c) { + return c.isAura(); + } + }; /** * a Predicate to get all equipment. */ @@ -482,7 +491,7 @@ public final class CardPredicates { /** * a Predicate to get all fortification. */ - public static final Predicate Fortification = new Predicate() { + public static final Predicate FORTIFICATION = new Predicate() { @Override public boolean apply(Card c) { return c.isFortification(); diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index d2be541192c..f58d8b63f95 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -405,7 +405,7 @@ public class CardProperty { return false; } } else if (property.equals("Attached")) { - if (card.getEquipping() != source && !source.equals(card.getEnchanting()) && card.getFortifying() != source) { + if (card.getEquipping() != source && !source.equals(card.getAttaching())) { return false; } } else if (property.startsWith("AttachedTo")) { @@ -433,9 +433,8 @@ public class CardProperty { return false; } } else { - if ((card.getEnchanting() == null || !card.getEnchanting().isValid(restriction, sourceController, source, spellAbility)) - && (card.getEquipping() == null || !card.getEquipping().isValid(restriction, sourceController, source, spellAbility)) - && (card.getFortifying() == null || !card.getFortifying().isValid(restriction, sourceController, source, spellAbility))) { + if ((card.getAttaching() == null || !card.getAttaching().isValid(restriction, sourceController, source, spellAbility)) + && (card.getEquipping() == null || !card.getEquipping().isValid(restriction, sourceController, source, spellAbility))) { return false; } } @@ -445,7 +444,7 @@ public class CardProperty { return false; } } else if (property.equals("NotAttachedTo")) { - if (card.getEquipping() == source || source.equals(card.getEnchanting()) || card.getFortifying() == source) { + if (card.getEquipping() == source || source.equals(card.getAttaching())) { return false; } } else if (property.startsWith("EnchantedBy")) { @@ -476,7 +475,7 @@ public class CardProperty { } break; default: // EnchantedBy Aura.Other - for (final Card aura : card.getEnchantedBy(false)) { + for (final Card aura : card.getEnchantedBy()) { if (aura.isValid(restriction, sourceController, source, spellAbility)) { return true; } @@ -572,11 +571,12 @@ public class CardProperty { return false; } } else if (property.startsWith("Equipped")) { - if (card.getEquipping() != source) { + if (!source.isAttachedBy(card)) { return false; } } else if (property.startsWith("Fortified")) { - if (card.getFortifying() != source) { + // FIXME TODO what property has this? + if (!source.isAttachedBy(card)) { return false; } } else if (property.startsWith("HauntedBy")) { diff --git a/forge-game/src/main/java/forge/game/card/CardUtil.java b/forge-game/src/main/java/forge/game/card/CardUtil.java index 373889feb2a..821025b66fc 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -6,12 +6,12 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -93,8 +93,8 @@ public final class CardUtil { if (kw.startsWith("HIDDEN")) { kw = kw.substring(7); } - - return !kw.startsWith("Protection") && !kw.startsWith("CantBeBlockedBy") + + return !kw.startsWith("Protection") && !kw.startsWith("CantBeBlockedBy") && !NON_STACKING_LIST.contains(kw); } @@ -108,7 +108,7 @@ public final class CardUtil { /** * getThisTurnEntered. - * + * * @param to zone going to * @param from zone coming from * @param valid a isValid expression @@ -121,7 +121,7 @@ public final class CardUtil { /** * getThisTurnEntered. - * + * * @param to zone going to * @param from zone coming from * @param valid a isValid expression @@ -146,7 +146,7 @@ public final class CardUtil { /** * getLastTurnEntered. - * + * * @param to zone going to * @param from zone coming from * @param valid a isValid expression @@ -159,7 +159,7 @@ public final class CardUtil { /** * getLastTurnEntered. - * + * * @param to zone going to * @param from zone coming from * @param valid a isValid expression @@ -222,7 +222,7 @@ public final class CardUtil { // needed to ensure that the LKI object has correct CMC info no matter what state the original card was in // (e.g. Scrap Trawler + transformed Harvest Hand) - newCopy.setLKICMC(in.getCMC()); + newCopy.setLKICMC(in.getCMC()); // used for the purpose of cards that care about the zone the card was known to be in last newCopy.setLastKnownZone(in.getLastKnownZone()); @@ -264,12 +264,10 @@ public final class CardUtil { newCopy.setReceivedDamageFromThisTurn(in.getReceivedDamageFromThisTurn()); newCopy.setReceivedDamageFromPlayerThisTurn(in.getReceivedDamageFromPlayerThisTurn()); newCopy.getDamageHistory().setCreatureGotBlockedThisTurn(in.getDamageHistory().getCreatureGotBlockedThisTurn()); - newCopy.setEnchanting(in.getEnchanting()); - newCopy.setEnchantedBy(in.getEnchantedBy(false)); - newCopy.setEquipping(in.getEquipping()); - newCopy.setEquippedBy(in.getEquippedBy(false)); - newCopy.setFortifying(in.getFortifying()); - newCopy.setFortifiedBy(in.getFortifiedBy(false)); + + newCopy.setAttachedBy(in.getAttachedBy()); + newCopy.setAttaching(in.getAttaching()); + newCopy.setClones(in.getClones()); newCopy.setHaunting(in.getHaunting()); newCopy.setCopiedPermanent(in.getCopiedPermanent()); @@ -313,9 +311,9 @@ public final class CardUtil { final Game game = source.getGame(); ColorSet cs = CardUtil.getColors(origin); for (byte color : MagicColor.WUBRG) { - if(!cs.hasAnyColor(color)) + if(!cs.hasAnyColor(color)) continue; - + for(final Card c : game.getColoredCardsInPlay(MagicColor.toLongString(color))) { if (!res.contains(c) && c.isValid(valid, source.getController(), source, null) && !c.equals(origin)) { res.add(c); @@ -344,7 +342,7 @@ public final class CardUtil { public static Set getReflectableManaColors(final SpellAbility sa) { return getReflectableManaColors(sa, sa, Sets.newHashSet(), new CardCollection()); } - + private static Set getReflectableManaColors(final SpellAbility abMana, final SpellAbility sa, Set colors, final CardCollection parents) { // Here's the problem with reflectable Mana. If more than one is out, @@ -352,7 +350,7 @@ public final class CardUtil { // so we basically need to have a recursive list that send the parents // so we don't infinite recurse. final Card card = abMana.getHostCard(); - + if (abMana.getApi() != ApiType.ManaReflected) { return colors; } @@ -361,7 +359,7 @@ public final class CardUtil { parents.add(card); } - final String colorOrType = sa.getParam("ColorOrType"); + final String colorOrType = sa.getParam("ColorOrType"); // currently Color or Type, Type is colors + colorless final String validCard = sa.getParam("Valid"); final String reflectProperty = sa.getParam("ReflectProperty"); @@ -459,7 +457,7 @@ public final class CardUtil { } return colors; } - + public static Set canProduce(final int maxChoices, final AbilityManaPart ab, final Set colors) { if (ab == null) { @@ -512,7 +510,7 @@ public final class CardUtil { if (ability.hasParam("MaxTotalTargetCMC")) { int totalCMCTargeted = 0; for (final Card c : targeted) { - totalCMCTargeted += c.getCMC(); + totalCMCTargeted += c.getCMC(); } final List choicesCopy = Lists.newArrayList(choices); diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java index 4dcd3896218..f45e925cb53 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -438,56 +438,32 @@ public class CardView extends GameEntityView { return false; } - public CardView getEquipping() { - return get(TrackableProperty.Equipping); - } - - public FCollectionView getEquippedBy() { - return get(TrackableProperty.EquippedBy); - } - - public boolean isEquipped() { - return getEquippedBy() != null; //isEmpty check not needed since we won't keep an empty collection around - } - public FCollectionView getEncodedCards() { return get(TrackableProperty.EncodedCards); } - public GameEntityView getEnchanting() { - return get(TrackableProperty.Enchanting); + public GameEntityView getAttaching() { + return get(TrackableProperty.Attaching); } - void updateEnchanting(Card c) { - set(TrackableProperty.Enchanting, GameEntityView.get(c.getEnchanting())); + void updateAttaching(Card c) { + set(TrackableProperty.Attaching, GameEntityView.get(c.getAttaching())); } - public CardView getEnchantingCard() { - GameEntityView enchanting = getEnchanting(); + public CardView getAttachingCard() { + GameEntityView enchanting = getAttaching(); if (enchanting instanceof CardView) { return (CardView) enchanting; } return null; } - public PlayerView getEnchantingPlayer() { - GameEntityView enchanting = getEnchanting(); + public PlayerView getAttachingPlayer() { + GameEntityView enchanting = getAttaching(); if (enchanting instanceof PlayerView) { return (PlayerView) enchanting; } return null; } - public CardView getFortifying() { - return get(TrackableProperty.Fortifying); - } - - public FCollectionView getFortifiedBy() { - return get(TrackableProperty.FortifiedBy); - } - - public boolean isFortified() { - return getFortifiedBy() != null; - } - public FCollectionView getGainControlTargets() { return get(TrackableProperty.GainControlTargets); } diff --git a/forge-game/src/main/java/forge/game/combat/Combat.java b/forge-game/src/main/java/forge/game/combat/Combat.java index 7bb0f1bda06..ccfa0a948f9 100644 --- a/forge-game/src/main/java/forge/game/combat/Combat.java +++ b/forge-game/src/main/java/forge/game/combat/Combat.java @@ -424,15 +424,12 @@ public class Combat { public Player getDefendingPlayerRelatedTo(final Card source) { Card attacker = source; - if (source.isAura()) { + if (source.isAura() || source.isFortification()) { attacker = source.getEnchantingCard(); } else if (source.isEquipment()) { attacker = source.getEquipping(); } - else if (source.isFortification()) { - attacker = source.getFortifying(); - } // return the corresponding defender return getDefenderPlayerByAttacker(attacker); diff --git a/forge-game/src/main/java/forge/game/cost/CostUnattach.java b/forge-game/src/main/java/forge/game/cost/CostUnattach.java index a443541daa0..194d97ae060 100644 --- a/forge-game/src/main/java/forge/game/cost/CostUnattach.java +++ b/forge-game/src/main/java/forge/game/cost/CostUnattach.java @@ -83,7 +83,7 @@ public class CostUnattach extends CostPartWithList { return true; } } else { - if (CardLists.getValidCards(source.getEquippedBy(false), type, payer, source).size() > 0) { + if (CardLists.getValidCards(source.getEquippedBy(), type, payer, source).size() > 0) { return true; } } @@ -102,7 +102,7 @@ public class CostUnattach extends CostPartWithList { return originalEquipment; } } else { - List attachees = CardLists.getValidCards(source.getEquippedBy(false), this.getType(), activator, source); + List attachees = CardLists.getValidCards(source.getEquippedBy(), this.getType(), activator, source); if (attachees.size() > 0) { // Just pick the first one, although maybe give a dialog return attachees.get(0); @@ -116,7 +116,7 @@ public class CostUnattach extends CostPartWithList { */ @Override protected Card doPayment(SpellAbility ability, Card targetCard) { - targetCard.unEquipCard(targetCard.getEquipping()); + targetCard.unAttachEntity(targetCard.getAttaching()); return targetCard; } diff --git a/forge-game/src/main/java/forge/game/event/GameEventCardAttachment.java b/forge-game/src/main/java/forge/game/event/GameEventCardAttachment.java index c8df7647cfb..567e9208d48 100644 --- a/forge-game/src/main/java/forge/game/event/GameEventCardAttachment.java +++ b/forge-game/src/main/java/forge/game/event/GameEventCardAttachment.java @@ -4,23 +4,15 @@ import forge.game.GameEntity; import forge.game.card.Card; public class GameEventCardAttachment extends GameEvent { - public enum AttachMethod { - Equip, - Fortify, - Enchant; - } - - + public final Card equipment; public final GameEntity newTarget; // can enchant player, I'm ssaving a class to enchants - it could be incorrect. public final GameEntity oldEntiy; - public final AttachMethod method; - public GameEventCardAttachment(Card attachment, GameEntity formerEntity, GameEntity newEntity, AttachMethod method) { + public GameEventCardAttachment(Card attachment, GameEntity formerEntity, GameEntity newEntity) { this.equipment = attachment; this.newTarget = newEntity; this.oldEntiy = formerEntity; - this.method = method; } @Override diff --git a/forge-game/src/main/java/forge/game/phase/Untap.java b/forge-game/src/main/java/forge/game/phase/Untap.java index e06f7ad7252..d256711e719 100644 --- a/forge-game/src/main/java/forge/game/phase/Untap.java +++ b/forge-game/src/main/java/forge/game/phase/Untap.java @@ -272,20 +272,15 @@ public class Untap extends Phase { } else if (c.hasKeyword(Keyword.PHASING)) { // 702.23g If an object would simultaneously phase out directly // and indirectly, it just phases out indirectly. - if (c.isAura()) { - final GameEntity ent = c.getEnchanting(); - - if ((ent instanceof Card) && list.contains(ent)) { + if (c.isAura() || c.isFortification()) { + final Card ent = c.getAttachingCard(); + if (ent != null && list.contains(ent)) { continue; } } else if (c.isEquipment() && c.isEquipping()) { if (list.contains(c.getEquipping())) { continue; } - } else if (c.isFortification() && c.isFortifying()) { - if (list.contains(c.getFortifying())) { - continue; - } } c.phase(); } diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index cd142b81516..eb5ac5885b5 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -6,12 +6,12 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -67,7 +67,7 @@ import java.util.concurrent.ConcurrentSkipListMap; *

* Abstract Player class. *

- * + * * @author Forge * @version $Id$ */ @@ -109,7 +109,7 @@ public class Player extends GameEntity implements Comparable { private CardCollection sacrificedThisTurn = new CardCollection(); private Map countersAddedtoPermThisTurn = Maps.newEnumMap(CounterType.class); - + /** A list of tokens not in play, but on their way. * This list is kept in order to not break ETB-replacement * on tokens. */ @@ -293,7 +293,7 @@ public class Player extends GameEntity implements Comparable { public final int getOpponentsGreatestLifeTotal() { return Aggregates.max(getOpponents(), Accessors.FN_GET_LIFE_TOTAL); } - + /** * Get the total number of poison counters amongst this player's opponents. */ @@ -382,7 +382,7 @@ public class Player extends GameEntity implements Comparable { } public final boolean gainLife(int lifeGain, final Card source, final SpellAbility sa) { - + // Run any applicable replacement effects. final Map repParams = Maps.newHashMap(); repParams.put("Event", "GainLife"); @@ -410,7 +410,7 @@ public class Player extends GameEntity implements Comparable { return false; } break; - default: + default: return false; } @@ -509,10 +509,10 @@ public class Player extends GameEntity implements Comparable { if (!canPayLife(lifePayment)) { return false; } - - if (lifePayment <= 0) + + if (lifePayment <= 0) return true; - + // rule 118.8 if (life >= lifePayment) { return (loseLife(lifePayment) > 0); @@ -624,7 +624,7 @@ public class Player extends GameEntity implements Comparable { return 0; } - if (hasProtectionFrom(source, true)) { + if (hasProtectionFromDamage(source)) { return 0; } @@ -826,7 +826,7 @@ public class Player extends GameEntity implements Comparable { } return num; } - + public final int getAssignedCombatDamage() { int num = 0; for (final Integer value : assignedCombatDamage.values()) { @@ -852,14 +852,14 @@ public class Player extends GameEntity implements Comparable { } return num; } - + /** * Get the total damage assigned to this player's opponents this turn. */ public final int getOpponentsAssignedDamage() { return Aggregates.sum(getOpponents(), Accessors.FN_GET_ASSIGNED_DAMAGE); } - + /** * Get the greatest amount of damage assigned to a single opponent this turn. */ @@ -1011,8 +1011,7 @@ public class Player extends GameEntity implements Comparable { // if the key already exists - merge entries if (changedKeywords.containsKey(timestamp)) { final KeywordsChange cks = changedKeywords.get(timestamp); - - ; + changedKeywords.put(timestamp, cks.merge(addKeywords, removeKeywords, cks.isRemoveAllKeywords(), cks.isRemoveIntrinsicKeywords())); updateKeywords(); @@ -1061,14 +1060,14 @@ public class Player extends GameEntity implements Comparable { /** * Remove all keyword changes which grant this {@link Player} the specified - * keyword. + * keyword. * @param keyword the keyword to remove. */ public final void removeKeyword(final String keyword) { removeKeyword(keyword, true); } - - + + public final void removeKeyword(final String keyword, final boolean allInstances) { boolean keywordRemoved = false; @@ -1134,7 +1133,7 @@ public class Player extends GameEntity implements Comparable { } return result; } - + public final StaticAbility addStaticAbility(final Card host, final String s) { PlayerZone com = getZone(ZoneType.Command); @@ -1195,12 +1194,17 @@ public class Player extends GameEntity implements Comparable { return true; } - @Override - public boolean hasProtectionFrom(final Card source) { - return hasProtectionFrom(source, false); + + public boolean hasProtectionFromDamage(final Card source) { + return hasProtectionFrom(source, false, false); } - public boolean hasProtectionFrom(final Card source, final boolean damageSource) { + @Override + public boolean hasProtectionFrom(final Card source, final boolean checkSBA) { + return hasProtectionFrom(source, checkSBA, false); + } + + public boolean hasProtectionFrom(final Card source, final boolean checkSBA, final boolean damageSource) { for (String kw : keywords) { if (kw.startsWith("Protection")) { if (kw.startsWith("Protection:")) { // uses isValid @@ -1418,7 +1422,7 @@ public class Player extends GameEntity implements Comparable { if (topCardRevealed) { revealed.add(c); - } + } if (numDrawnThisTurn == 0) { boolean reveal = false; @@ -1436,7 +1440,7 @@ public class Player extends GameEntity implements Comparable { revealed.remove(c); } } - } + } setLastDrawnCard(c); c.setDrawnThisTurn(true); @@ -1654,7 +1658,7 @@ public class Player extends GameEntity implements Comparable { public final CardCollectionView mill(final int n) { return mill(n, ZoneType.Graveyard, false); } - public final CardCollectionView mill(final int n, final ZoneType zone, + public final CardCollectionView mill(final int n, final ZoneType zone, final boolean bottom) { final CardCollectionView lib = getCardsIn(ZoneType.Library); final CardCollection milled = new CardCollection(); @@ -2204,7 +2208,7 @@ public class Player extends GameEntity implements Comparable { public final void resetSacrificedThisTurn() { sacrificedThisTurn.clear(); } - + public final void addCounterToPermThisTurn(final CounterType type, final int x) { countersAddedtoPermThisTurn.put(type, getCounterToPermThisTurn(type) + x); } @@ -2270,7 +2274,7 @@ public class Player extends GameEntity implements Comparable { /** * get the Player object or Card (Planeswalker) object that this Player must * attack this combat. - * + * * @return the Player or Card (Planeswalker) * @since 1.1.01 */ @@ -2625,7 +2629,7 @@ public class Player extends GameEntity implements Comparable { public CardCollectionView getInboundTokens() { return inboundTokens; } - + public void addInboundToken(Card c) { inboundTokens.add(c); } @@ -2710,7 +2714,7 @@ public class Player extends GameEntity implements Comparable { com.add(Card.fromPaperCard(avatar, this)); } } - + // Schemes CardCollection sd = new CardCollection(); for (IPaperCard cp : registeredPlayer.getSchemes()) { @@ -2773,7 +2777,7 @@ public class Player extends GameEntity implements Comparable { eff.setSVar("CommanderMoveReplacement", "DB$ ChangeZone | Origin$ Battlefield,Graveyard,Exile,Library,Hand | Destination$ Command | Defined$ ReplacedCard"); - String moved = "Event$ Moved | ValidCard$ Card.EffectSource+YouOwn | Secondary$ True | Optional$ True | OptionalDecider$ You | ReplaceWith$ CommanderMoveReplacement "; + String moved = "Event$ Moved | ValidCard$ Card.EffectSource+YouOwn | Secondary$ True | Optional$ True | OptionalDecider$ You | ReplaceWith$ CommanderMoveReplacement "; if (game.getRules().hasAppliedVariant(GameType.TinyLeaders)) { moved += " | Destination$ Graveyard,Exile | Description$ If a commander would be put into its owner's graveyard or exile from anywhere, that player may put it into the command zone instead."; } else { @@ -2837,7 +2841,7 @@ public class Player extends GameEntity implements Comparable { monarchEffect.addType("Effect"); { - final String drawTrig = "Mode$ Phase | Phase$ End of Turn | TriggerZones$ Command | " + + final String drawTrig = "Mode$ Phase | Phase$ End of Turn | TriggerZones$ Command | " + "ValidPlayer$ You | TriggerDescription$ At the beginning of your end step, draw a card."; final String drawEff = "AB$Draw | Cost$ 0 | Defined$ You | NumCards$ 1"; @@ -2870,11 +2874,11 @@ public class Player extends GameEntity implements Comparable { this.updateZoneForView(com); } } - + public boolean hasBlessing() { return blessingEffect != null; } - + public void setBlessing(boolean bless) { // no need to to change if ((blessingEffect != null) == bless) { @@ -2882,14 +2886,14 @@ public class Player extends GameEntity implements Comparable { } final PlayerZone com = getZone(ZoneType.Command); - + if(bless) { blessingEffect = new Card(-1, game); blessingEffect.setOwner(this); blessingEffect.setImageKey("t:blessing"); blessingEffect.setName("City's Blessing"); blessingEffect.addType("Effect"); - + blessingEffect.updateStateForView(); @@ -2898,7 +2902,7 @@ public class Player extends GameEntity implements Comparable { com.remove(blessingEffect); blessingEffect = null; } - + this.updateZoneForView(com); } diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index 6c407b678ee..30ae35818e1 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -11,7 +11,7 @@ public enum TrackableProperty { //Shared Text(TrackableTypes.StringType), PreventNextDamage(TrackableTypes.IntegerType), - EnchantedBy(TrackableTypes.CardViewCollectionType), + AttachedBy(TrackableTypes.CardViewCollectionType), Counters(TrackableTypes.CounterMapType), CurrentPlane(TrackableTypes.StringType), PlanarPlayer(TrackableTypes.PlayerViewType), @@ -43,11 +43,7 @@ public enum TrackableProperty { NamedCard(TrackableTypes.StringType), PlayerMayLook(TrackableTypes.PlayerViewCollectionType, FreezeMode.IgnoresFreeze), PlayerMayLookTemp(TrackableTypes.PlayerViewCollectionType, FreezeMode.IgnoresFreeze), - Equipping(TrackableTypes.CardViewType), - EquippedBy(TrackableTypes.CardViewCollectionType), - Enchanting(TrackableTypes.GameEntityViewType), - Fortifying(TrackableTypes.CardViewType), - FortifiedBy(TrackableTypes.CardViewCollectionType), + Attaching(TrackableTypes.GameEntityViewType), EncodedCards(TrackableTypes.CardViewCollectionType), GainControlTargets(TrackableTypes.CardViewCollectionType), CloneOrigin(TrackableTypes.CardViewType), diff --git a/forge-gui-desktop/src/main/java/forge/screens/match/TargetingOverlay.java b/forge-gui-desktop/src/main/java/forge/screens/match/TargetingOverlay.java index 06ff52f9f03..039f4bb85e3 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/match/TargetingOverlay.java +++ b/forge-gui-desktop/src/main/java/forge/screens/match/TargetingOverlay.java @@ -404,56 +404,24 @@ public class TargetingOverlay { return; //don't add arcs for cards if card already visualized } - final CardView enchanting = c.getEnchantingCard(); - final CardView equipping = c.getEquipping(); - final CardView fortifying = c.getFortifying(); - final Iterable enchantedBy = c.getEnchantedBy(); - final Iterable equippedBy = c.getEquippedBy(); - final Iterable fortifiedBy = c.getFortifiedBy(); + final CardView attaching = c.getAttachingCard(); + final Iterable attachedBy = c.getAttachedBy(); final CardView paired = c.getPairedWith(); - if (null != enchanting) { - if (enchanting.getController() != null && !enchanting.getController().equals(c.getController())) { - addArc(endpoints.get(enchanting.getId()), endpoints.get(c.getId()), ArcConnection.Friends); - cardsVisualized.add(enchanting); + if (null != attaching) { + if (attaching.getController() != null && !attaching.getController().equals(c.getController())) { + addArc(endpoints.get(attaching.getId()), endpoints.get(c.getId()), ArcConnection.Friends); + cardsVisualized.add(attaching); } } - if (null != equipping) { - if (equipping.getController() != null && !equipping.getController().equals(c.getController())) { - addArc(endpoints.get(equipping.getId()), endpoints.get(c.getId()), ArcConnection.Friends); - cardsVisualized.add(equipping); - } - } - if (null != fortifying) { - if (fortifying.getController() != null && !fortifying.getController().equals(c.getController())) { - addArc(endpoints.get(fortifying.getId()), endpoints.get(c.getId()), ArcConnection.Friends); - cardsVisualized.add(fortifying); - } - } - if (null != enchantedBy) { - for (final CardView enc : enchantedBy) { + if (null != attachedBy) { + for (final CardView enc : attachedBy) { if (enc.getController() != null && !enc.getController().equals(c.getController())) { addArc(endpoints.get(c.getId()), endpoints.get(enc.getId()), ArcConnection.Friends); cardsVisualized.add(enc); } } } - if (null != equippedBy) { - for (final CardView eq : equippedBy) { - if (eq.getController() != null && !eq.getController().equals(c.getController())) { - addArc(endpoints.get(c.getId()), endpoints.get(eq.getId()), ArcConnection.Friends); - cardsVisualized.add(eq); - } - } - } - if (null != fortifiedBy) { - for (final CardView eq : fortifiedBy) { - if (eq.getController() != null && !eq.getController().equals(c.getController())) { - addArc(endpoints.get(c.getId()), endpoints.get(eq.getId()), ArcConnection.Friends); - cardsVisualized.add(eq); - } - } - } if (null != paired) { addArc(endpoints.get(paired.getId()), endpoints.get(c.getId()), ArcConnection.Friends); cardsVisualized.add(paired); diff --git a/forge-gui-desktop/src/main/java/forge/view/arcane/PlayArea.java b/forge-gui-desktop/src/main/java/forge/view/arcane/PlayArea.java index 434b62f10a7..42defe2a9bd 100644 --- a/forge-gui-desktop/src/main/java/forge/view/arcane/PlayArea.java +++ b/forge-gui-desktop/src/main/java/forge/view/arcane/PlayArea.java @@ -106,7 +106,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen final CardStack stack = allLands.get(i); final CardPanel firstPanel = stack.get(0); if (firstPanel.getCard().getCurrentState().getName().equals(state.getName())) { - if (!firstPanel.getAttachedPanels().isEmpty() || firstPanel.getCard().isEnchanted()) { + if (!firstPanel.getAttachedPanels().isEmpty() || firstPanel.getCard().isAttached()) { // Put this land to the left of lands with the same name // and attachments. insertIndex = i; @@ -114,7 +114,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen } if (!panel.getAttachedPanels().isEmpty() || !panel.getCard().hasSameCounters(firstPanel.getCard()) - || firstPanel.getCard().isEnchanted() || (stack.size() == this.landStackMax)) { + || firstPanel.getCard().isAttached() || (stack.size() == this.landStackMax)) { // If this land has attachments or the stack is full, // put it to the right. insertIndex = i + 1; @@ -683,8 +683,8 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen } toPanel.getAttachedPanels().clear(); - if (card.isEnchanted()) { - final Iterable enchants = card.getEnchantedBy(); + if (card.isAttached()) { + final Iterable enchants = card.getAttachedBy(); for (final CardView e : enchants) { final CardPanel cardE = getCardPanel(e.getId()); if (cardE != null) { @@ -697,43 +697,9 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen } } - if (card.isEquipped()) { - final Iterable equips = card.getEquippedBy(); - for (final CardView e : equips) { - final CardPanel cardE = getCardPanel(e.getId()); - if (cardE != null) { - if (cardE.getAttachedToPanel() != toPanel) { - cardE.setAttachedToPanel(toPanel); - needLayoutRefresh = true; //ensure layout refreshed if any attachments change - } - toPanel.getAttachedPanels().add(cardE); - } - } - } - - if (card.isFortified()) { - final Iterable fortifications = card.getFortifiedBy(); - for (final CardView f : fortifications) { - final CardPanel cardE = getCardPanel(f.getId()); - if (cardE != null) { - if (cardE.getAttachedToPanel() != toPanel) { - cardE.setAttachedToPanel(toPanel); - needLayoutRefresh = true; //ensure layout refreshed if any attachments change - } - toPanel.getAttachedPanels().add(cardE); - } - } - } - CardPanel attachedToPanel; - if (card.getEnchantingCard() != null) { - attachedToPanel = getCardPanel(card.getEnchantingCard().getId()); - } - else if (card.getEquipping() != null) { - attachedToPanel = getCardPanel(card.getEquipping().getId()); - } - else if (card.getFortifying() != null) { - attachedToPanel = getCardPanel(card.getFortifying().getId()); + if (card.getAttachingCard() != null) { + attachedToPanel = getCardPanel(card.getAttachingCard().getId()); } else { attachedToPanel = null; diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java index 8508b594ac1..55a18ccfcbe 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java @@ -115,7 +115,7 @@ public class GameSimulatorTest extends SimulationTestCase { Card bear = addCard(bearCardName, p); bear.setSickness(false); Card cloak = addCard("Whispersilk Cloak", p); - cloak.equipCard(bear); + cloak.attachEntity(bear); game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p); game.getAction().checkStateEffects(true); assertEquals(1, bear.getAmountOfKeyword("Unblockable")); @@ -133,7 +133,7 @@ public class GameSimulatorTest extends SimulationTestCase { Card bear = addCard(bearCardName, p); bear.setSickness(false); Card lifelink = addCard("Lifelink", p); - lifelink.enchantEntity(bear); + lifelink.attachEntity(bear); game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p); game.getAction().checkStateEffects(true); assertEquals(1, bear.getAmountOfKeyword("Lifelink")); @@ -661,7 +661,7 @@ public class GameSimulatorTest extends SimulationTestCase { Card pridemate = addCard(pridemateName, p1); Card indestructibility = addCard(indestructibilityName, p1); - indestructibility.enchantEntity(pridemate); + indestructibility.attachEntity(pridemate); Card ignition = addCardToZone(ignitionName, p1, ZoneType.Hand); SpellAbility ignitionSA = ignition.getFirstSpellAbility(); @@ -681,6 +681,7 @@ public class GameSimulatorTest extends SimulationTestCase { // because it was destroyed assertNull(simBrood); + assertNotNull(simPridemate); assertEquals(0, simKalitas.getDamage()); assertEquals(3, simPridemate.getDamage()); @@ -774,7 +775,7 @@ public class GameSimulatorTest extends SimulationTestCase { Card pridemate = addCard(pridemateName, p1); Card indestructibility = addCard(indestructibilityName, p1); - indestructibility.enchantEntity(pridemate); + indestructibility.attachEntity(pridemate); Card ignition = addCardToZone(ignitionName, p1, ZoneType.Hand); SpellAbility ignitionSA = ignition.getFirstSpellAbility(); @@ -800,6 +801,7 @@ public class GameSimulatorTest extends SimulationTestCase { //destoryed because of to much redirected damage assertNull(simPalisade); + assertNotNull(simPridemate); assertEquals(0, simKalitas.getDamage()); assertEquals(3, simPridemate.getDamage()); diff --git a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/GameWrapper.java b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/GameWrapper.java index 0af6bbac10f..4cdfa37511b 100644 --- a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/GameWrapper.java +++ b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/GameWrapper.java @@ -102,22 +102,16 @@ public class GameWrapper { actualController.getZone( zoneType ).add( actualCard ); if( card.getTarget() != null ) { - Card target = CardSpecificationHandler.INSTANCE.find( game, card.getTarget() ); - if( actualCard.isEnchantment() ) { - if( target.canBeEnchantedBy( actualCard ) ) { - actualCard.enchantEntity( target ); - } else { - throw new IllegalStateException( actualCard + " can't enchant " + target ); - } - } else if( actualCard.isEquipment() ) { - if( target.canBeEquippedBy( actualCard ) ) { - actualCard.equipCard( target ); - } else { - throw new IllegalStateException( actualCard + " can't equip " + target ); - } - } else { - throw new IllegalStateException( "Don't know how to make " + actualCard + " target anything" ); - } + Card target = CardSpecificationHandler.INSTANCE.find( game, card.getTarget() ); + if (actualCard.isAttachment()) { + if (target.canBeAttachedBy(actualCard)) { + actualCard.attachEntity(target); + } else { + throw new IllegalStateException( actualCard + " can't attach " + target ); + } + } else { + throw new IllegalStateException( "Don't know how to make " + actualCard + " target anything" ); + } } diff --git a/forge-gui/src/main/java/forge/card/CardDetailUtil.java b/forge-gui/src/main/java/forge/card/CardDetailUtil.java index 6e94eb844e2..aa7af823f87 100644 --- a/forge-gui/src/main/java/forge/card/CardDetailUtil.java +++ b/forge-gui/src/main/java/forge/card/CardDetailUtil.java @@ -441,48 +441,28 @@ public class CardDetailUtil { area.append(")"); } - // equipping - if (card.getEquipping() != null) { + // attached by + if (card.isAttached()) { if (area.length() != 0) { area.append("\n"); } - area.append("=Equipping "); - area.append(card.getEquipping()); + area.append("=Attached by "); + area.append(StringUtils.join(card.getAttachedBy(), ", ")); area.append("="); } - // equipped by - if (card.isEquipped()) { + // attaching + if (card.getAttachingCard() != null) { if (area.length() != 0) { area.append("\n"); } - area.append("=Equipped by "); - area.append(StringUtils.join(card.getEquippedBy(), ", ")); - area.append("="); + area.append("*Attaching ").append(card.getAttachingCard()).append("*"); } - - // enchanting - if (card.getEnchantingCard() != null) { + if (card.getAttachingPlayer() != null) { if (area.length() != 0) { area.append("\n"); } - area.append("*Enchanting ").append(card.getEnchantingCard()).append("*"); - } - if (card.getEnchantingPlayer() != null) { - if (area.length() != 0) { - area.append("\n"); - } - area.append("*Enchanting ").append(card.getEnchantingPlayer()).append("*"); - } - - // enchanted by - if (card.isEnchanted()) { - if (area.length() != 0) { - area.append("\n"); - } - area.append("*Enchanted by "); - area.append(StringUtils.join(card.getEnchantedBy(), ", ")); - area.append("*"); + area.append("*Enchanting ").append(card.getAttachingPlayer()).append("*"); } // controlling diff --git a/forge-gui/src/main/java/forge/quest/QuestUtil.java b/forge-gui/src/main/java/forge/quest/QuestUtil.java index 7ad6b6cd335..f9989979a51 100644 --- a/forge-gui/src/main/java/forge/quest/QuestUtil.java +++ b/forge-gui/src/main/java/forge/quest/QuestUtil.java @@ -595,7 +595,7 @@ public class QuestUtil { rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN)); rules.setCanCloneUseTargetsImage(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE)); - TreeSet variant = new TreeSet(); + final TreeSet variant = new TreeSet<>(); if(FModel.getQuest().getDeckConstructionRules() == DeckConstructionRules.Commander){ variant.add(GameType.Commander); }