Attached: combine Enchant+Equip+Fortify

This commit is contained in:
Hanmac
2018-11-17 18:50:43 +01:00
parent 382e78536f
commit 2f913724cb
41 changed files with 576 additions and 919 deletions

View File

@@ -767,7 +767,7 @@ public class AiController {
// will need actual logic that determines if the enchantment is able // 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. // to disable the permanent or it's still functional and a duplicate is unneeded.
boolean disabledByEnemy = false; boolean disabledByEnemy = false;
for (Card card2 : card.getEnchantedBy(false)) { for (Card card2 : card.getEnchantedBy()) {
if (card2.getOwner() != player) { if (card2.getOwner() != player) {
disabledByEnemy = true; disabledByEnemy = true;
} }

View File

@@ -823,7 +823,7 @@ public class ComputerUtil {
if (c != null && c.isEnchanted()) { if (c != null && c.isEnchanted()) {
// TODO: choose "worst" controlled enchanting Aura // 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)) { if (aura.getController().equals(c.getController()) && remaining.contains(aura)) {
return aura; return aura;
} }
@@ -2924,7 +2924,7 @@ public class ComputerUtil {
if (sa.getParam("AITgts").equals("BetterThanSource")) { if (sa.getParam("AITgts").equals("BetterThanSource")) {
int value = ComputerUtilCard.evaluateCreature(source); int value = ComputerUtilCard.evaluateCreature(source);
if (source.isEnchanted()) { if (source.isEnchanted()) {
for (Card enc : source.getEnchantedBy(false)) { for (Card enc : source.getEnchantedBy()) {
if (enc.getController().equals(ai)) { if (enc.getController().equals(ai)) {
value += 100; // is 100 per AI's own aura enough? value += 100; // is 100 per AI's own aura enough?
} }

View File

@@ -585,7 +585,7 @@ public class ComputerUtilCard {
// Add all cost of all auras with the same controller // Add all cost of all auras with the same controller
if (card.isEnchanted()) { if (card.isEnchanted()) {
final List<Card> auras = CardLists.filterControlledBy(card.getEnchantedBy(false), card.getController()); final List<Card> auras = CardLists.filterControlledBy(card.getEnchantedBy(), card.getController());
curCMC += Aggregates.sum(auras, CardPredicates.Accessors.fnGetCmc) + auras.size(); curCMC += Aggregates.sum(auras, CardPredicates.Accessors.fnGetCmc) + auras.size();
} }
@@ -833,7 +833,7 @@ public class ComputerUtilCard {
int score = tmp.isTapped() ? 2 : 0; int score = tmp.isTapped() ? 2 : 0;
score += tmp.isBasicLand() ? 1 : 0; score += tmp.isBasicLand() ? 1 : 0;
score -= tmp.isCreature() ? 4 : 0; score -= tmp.isCreature() ? 4 : 0;
for (Card aura : tmp.getEnchantedBy(false)) { for (Card aura : tmp.getEnchantedBy()) {
if (aura.getController().isOpponentOf(tmp.getController())) { if (aura.getController().isOpponentOf(tmp.getController())) {
score += 5; score += 5;
} else { } else {
@@ -857,7 +857,7 @@ public class ComputerUtilCard {
int score = tmp.isTapped() ? 0 : 2; int score = tmp.isTapped() ? 0 : 2;
score += tmp.isBasicLand() ? 2 : 0; score += tmp.isBasicLand() ? 2 : 0;
score -= tmp.isCreature() ? 4 : 0; score -= tmp.isCreature() ? 4 : 0;
score -= 5 * tmp.getEnchantedBy(false).size(); score -= 5 * tmp.getEnchantedBy().size();
if (score >= maxScore) { if (score >= maxScore) {
land = tmp; land = tmp;
@@ -1033,7 +1033,7 @@ public class ComputerUtilCard {
// interrupt 3: two for one = good // interrupt 3: two for one = good
if (c.isEnchanted()) { if (c.isEnchanted()) {
boolean myEnchants = false; boolean myEnchants = false;
for (Card enc : c.getEnchantedBy(false)) { for (Card enc : c.getEnchantedBy()) {
if (enc.getOwner().equals(ai)) { if (enc.getOwner().equals(ai)) {
myEnchants = true; myEnchants = true;
break; break;

View File

@@ -300,7 +300,7 @@ public class ComputerUtilCost {
if (!important) { if (!important) {
return false; return false;
} }
if (!CardLists.filterControlledBy(source.getEnchantedBy(false), source.getController()).isEmpty()) { if (!CardLists.filterControlledBy(source.getEnchantedBy(), source.getController()).isEmpty()) {
return false; return false;
} }
continue; continue;

View File

@@ -196,9 +196,7 @@ public abstract class GameState {
cardsReferencedByID.add(card.getExiledWith()); cardsReferencedByID.add(card.getExiledWith());
} }
if (zone == ZoneType.Battlefield) { if (zone == ZoneType.Battlefield) {
if (!card.getEnchantedBy(false).isEmpty() if (!card.getAttachedBy().isEmpty()) {
|| !card.getEquippedBy(false).isEmpty()
|| !card.getFortifiedBy(false).isEmpty()) {
// Remember the ID of cards that have attachments // Remember the ID of cards that have attachments
cardsReferencedByID.add(card); cardsReferencedByID.add(card);
} }
@@ -290,12 +288,8 @@ public abstract class GameState {
} else if (c.getCurrentStateName().equals(CardStateName.Meld)) { } else if (c.getCurrentStateName().equals(CardStateName.Meld)) {
newText.append("|Meld"); newText.append("|Meld");
} }
if (c.getEquipping() != null) { if (c.isAttaching()) {
newText.append("|Attaching:").append(c.getEquipping().getId()); newText.append("|Attaching:").append(c.getAttaching().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.getEnchantingPlayer() != null) { if (c.getEnchantingPlayer() != null) {
// TODO: improve this for game states with more than two players // TODO: improve this for game states with more than two players
@@ -959,25 +953,15 @@ public abstract class GameState {
// Unattach all permanents first // Unattach all permanents first
for(Entry<Card, Integer> entry : cardToAttachId.entrySet()) { for(Entry<Card, Integer> entry : cardToAttachId.entrySet()) {
Card attachedTo = idToCard.get(entry.getValue()); Card attachedTo = idToCard.get(entry.getValue());
attachedTo.unAttachAllCards();
attachedTo.unEnchantAllCards();
attachedTo.unEquipAllCards();
for (Card c : attachedTo.getFortifiedBy(true)) {
attachedTo.unFortifyCard(c);
}
} }
// Attach permanents by ID // Attach permanents by ID
for(Entry<Card, Integer> entry : cardToAttachId.entrySet()) { for(Entry<Card, Integer> entry : cardToAttachId.entrySet()) {
Card attachedTo = idToCard.get(entry.getValue()); Card attachedTo = idToCard.get(entry.getValue());
Card attacher = entry.getKey(); Card attacher = entry.getKey();
if (attacher.isAttachment()) {
if (attacher.isEquipment()) { attacher.attachEntity(attachedTo);
attacher.equipCard(attachedTo);
} else if (attacher.isAura()) {
attacher.enchantEntity(attachedTo);
} else if (attacher.isFortified()) {
attacher.fortifyCard(attachedTo);
} }
} }
@@ -988,7 +972,7 @@ public abstract class GameState {
Game game = attacher.getGame(); Game game = attacher.getGame();
Player attachedTo = entry.getValue() == TARGET_AI ? game.getPlayers().get(1) : game.getPlayers().get(0); 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()) { if (c.isAura()) {
// dummy "enchanting" to indicate that the card will be force-attached elsewhere // dummy "enchanting" to indicate that the card will be force-attached elsewhere
// (will be overridden later, so the actual value shouldn't matter) // (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)) { if (cardsWithoutETBTrigs.contains(c)) {

View File

@@ -408,7 +408,7 @@ public class AttachAi extends SpellAbilityAi {
return true; return true;
} }
final Iterable<Card> auras = c.getEnchantedBy(false); final Iterable<Card> auras = c.getEnchantedBy();
final Iterator<Card> itr = auras.iterator(); final Iterator<Card> itr = auras.iterator();
while (itr.hasNext()) { while (itr.hasNext()) {
final Card aura = itr.next(); final Card aura = itr.next();
@@ -1276,7 +1276,7 @@ public class AttachAi extends SpellAbilityAi {
return null; return null;
} }
// Don't fortify if already fortifying // Don't fortify if already fortifying
if (attachSource.getFortifying() != null && attachSource.getFortifying().getController() == aiPlayer) { if (attachSource.getAttachingCard() != null && attachSource.getAttachingCard().getController() == aiPlayer) {
return null; return null;
} }
@@ -1491,7 +1491,7 @@ public class AttachAi extends SpellAbilityAi {
return false; return false;
} }
// Also don't play if it would destroy own Aura // 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))) { if ((c.getController().equals(ai)) && (c.isOfColor(cc))) {
return false; return false;
} }

View File

@@ -159,7 +159,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
return true; // debuffed by opponent's auras to the level that it becomes useless return true; // debuffed by opponent's auras to the level that it becomes useless
} }
int delta = 0; int delta = 0;
for (Card enc : sa.getHostCard().getEnchantedBy(false)) { for (Card enc : sa.getHostCard().getEnchantedBy()) {
if (enc.getController().isOpponentOf(aiPlayer)) { if (enc.getController().isOpponentOf(aiPlayer)) {
delta--; delta--;
} else { } else {
@@ -961,7 +961,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
for (Card aura : c.getEnchantedBy(false)) { for (Card aura : c.getEnchantedBy()) {
if (aura.getController().isOpponentOf(ai)) { if (aura.getController().isOpponentOf(ai)) {
return true; return true;
} else { } else {
@@ -1054,7 +1054,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { 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)) { if (c.getOwner().isOpponentOf(ai) && aura.getController().equals(ai)) {
return false; return false;
} }

View File

@@ -99,7 +99,7 @@ public class CountersPutAi extends SpellAbilityAi {
if (sa.hasParam("LevelUp")) { if (sa.hasParam("LevelUp")) {
// creatures enchanted by curse auras have low priority // creatures enchanted by curse auras have low priority
if (ph.getPhase().isBefore(PhaseType.MAIN2)) { if (ph.getPhase().isBefore(PhaseType.MAIN2)) {
for (Card aura : source.getEnchantedBy(false)) { for (Card aura : source.getEnchantedBy()) {
if (aura.getController().isOpponentOf(ai)) { if (aura.getController().isOpponentOf(ai)) {
return false; return false;
} }

View File

@@ -27,7 +27,7 @@ public abstract class DamageAiBase extends SpellAbilityAi {
Card hostcard = sa.getHostCard(); Card hostcard = sa.getHostCard();
boolean lifelink = hostcard.hasKeyword(Keyword.LIFELINK); boolean lifelink = hostcard.hasKeyword(Keyword.LIFELINK);
if (!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. // Treat cards enchanted by older cards with "when enchanted creature deals damage, gain life" as if they had lifelink.
if (ench.hasSVar("LikeLifeLink")) { if (ench.hasSVar("LikeLifeLink")) {
if ("True".equals(ench.getSVar("LikeLifeLink"))) { if ("True".equals(ench.getSVar("LikeLifeLink"))) {

View File

@@ -273,7 +273,7 @@ public class DestroyAi extends SpellAbilityAi {
} else { } else {
// Don't destroy stolen permanents when the stealing aura can be destroyed // Don't destroy stolen permanents when the stealing aura can be destroyed
if (choice.getOwner() == ai) { if (choice.getOwner() == ai) {
for (Card aura : choice.getEnchantedBy(false)) { for (Card aura : choice.getEnchantedBy()) {
SpellAbility sp = aura.getFirstSpellAbility(); SpellAbility sp = aura.getFirstSpellAbility();
if (sp != null && "GainControl".equals(sp.getParam("AILogic")) if (sp != null && "GainControl".equals(sp.getParam("AILogic"))
&& aura.getController() != ai && sa.canTarget(aura)) { && aura.getController() != ai && sa.canTarget(aura)) {

View File

@@ -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 // 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). // like a reasonable default for some cards).
for (Card c : ctrld) { for (Card c : ctrld) {
if (c.getEnchantedBy(false).isEmpty()) { if (c.getEnchantedBy().isEmpty()) {
numControlled++; numControlled++;
} else { } else {
for (Card att : c.getEnchantedBy(false)) { for (Card att : c.getEnchantedBy()) {
if (!att.getController().isOpponentOf(ai)) { if (!att.getController().isOpponentOf(ai)) {
numControlled++; numControlled++;
} }

View File

@@ -13,6 +13,7 @@ import forge.LobbyPlayer;
import forge.ai.LobbyPlayerAi; import forge.ai.LobbyPlayerAi;
import forge.card.CardStateName; import forge.card.CardStateName;
import forge.game.Game; import forge.game.Game;
import forge.game.GameEntity;
import forge.game.GameObject; import forge.game.GameObject;
import forge.game.GameObjectMap; import forge.game.GameObjectMap;
import forge.game.GameRules; import forge.game.GameRules;
@@ -204,19 +205,16 @@ public class GameCopier {
} }
} }
gameObjectMap = new CopiedGameObjectMap(newGame); gameObjectMap = new CopiedGameObjectMap(newGame);
for (Card card : origGame.getCardsIn(ZoneType.Battlefield)) { for (Card card : origGame.getCardsIn(ZoneType.Battlefield)) {
Card otherCard = cardMap.get(card); Card otherCard = cardMap.get(card);
otherCard.setTimestamp(card.getTimestamp()); otherCard.setTimestamp(card.getTimestamp());
otherCard.setSickness(card.hasSickness()); otherCard.setSickness(card.hasSickness());
otherCard.setState(card.getCurrentStateName(), false); otherCard.setState(card.getCurrentStateName(), false);
if (card.isEnchanting()) { if (card.isAttaching()) {
otherCard.setEnchanting(gameObjectMap.map(card.getEnchanting())); GameEntity ge = gameObjectMap.map(card.getAttaching());
} otherCard.setAttaching(ge);
if (card.isEquipping()) { ge.addAttachedBy(otherCard);
otherCard.equipCard(cardMap.get(card.getEquipping()));
}
if (card.isFortifying()) {
otherCard.setFortifying(cardMap.get(card.getFortifying()));
} }
if (card.getCloneOrigin() != null) { if (card.getCloneOrigin() != null) {
otherCard.setCloneOrigin(cardMap.get(card.getCloneOrigin())); otherCard.setCloneOrigin(cardMap.get(card.getCloneOrigin()));

View File

@@ -36,7 +36,6 @@ import forge.game.replacement.ReplacementResult;
import forge.game.spellability.AbilitySub; import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityPredicates; import forge.game.spellability.SpellAbilityPredicates;
import forge.game.spellability.TargetRestrictions;
import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbility;
import forge.game.staticability.StaticAbilityLayer; import forge.game.staticability.StaticAbilityLayer;
import forge.game.trigger.Trigger; import forge.game.trigger.Trigger;
@@ -504,45 +503,12 @@ public class GameAction {
} }
private static void unattachCardLeavingBattlefield(final Card copied) { private static void unattachCardLeavingBattlefield(final Card copied) {
// Handle unequipping creatures // remove attachments from creatures
if (copied.isEquipped()) { copied.unAttachAllCards();
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);
}
}
// unenchant creature if moving aura // unenchant creature if moving aura
if (copied.isEnchanting()) { if (copied.isAttaching()) {
copied.unEnchantEntity(copied.getEnchanting()); copied.unAttachEntity(copied.getAttaching());
} }
} }
@@ -1006,11 +972,10 @@ public class GameAction {
} }
checkAgain |= stateBasedAction_Saga(c); checkAgain |= stateBasedAction_Saga(c);
checkAgain |= stateBasedAction704_5n(c); // Auras attached to illegal or not attached go to graveyard checkAgain |= stateBasedAction704_attach(c); // Attachment
checkAgain |= stateBasedAction704_5p(c); // Equipment and Fortifications
if (c.isCreature() && c.isEnchanting()) { // Rule 704.5q - Creature attached to an object or player, becomes unattached if (c.isCreature() && c.isAttaching()) { // Rule 704.5q - Creature attached to an object or player, becomes unattached
c.unEnchantEntity(c.getEnchanting()); c.unAttachEntity(c.getAttaching());
checkAgain = true; checkAgain = true;
} }
@@ -1124,110 +1089,34 @@ public class GameAction {
return checkAgain; return checkAgain;
} }
private boolean stateBasedAction704_5n(Card c) { private boolean stateBasedAction704_attach(Card c) {
boolean checkAgain = false; boolean checkAgain = false;
if (!c.isAura()) {
return false;
}
// Check if Card Aura is attached to is a legal target if (c.isAttaching()) {
final GameEntity entity = c.getEnchanting(); final GameEntity ge = c.getAttaching();
SpellAbility sa = c.getFirstAttachSpell(); if (!ge.canBeAttachedBy(c)) {
c.unAttachEntity(ge);
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);
checkAgain = true; 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); moveToGraveyard(c, null, null);
checkAgain = true; checkAgain = true;
} }
return checkAgain; 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) { private boolean stateBasedAction704_5r(Card c) {
boolean checkAgain = false; boolean checkAgain = false;
int plusOneCounters = c.getCounters(CounterType.P1P1); int plusOneCounters = c.getCounters(CounterType.P1P1);

View File

@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
@@ -21,9 +21,10 @@ import forge.game.card.Card;
import forge.game.card.CardCollection; import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView; import forge.game.card.CardCollectionView;
import forge.game.card.CardDamageMap; import forge.game.card.CardDamageMap;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterType; import forge.game.card.CounterType;
import forge.game.event.GameEventCardAttachment; import forge.game.event.GameEventCardAttachment;
import forge.game.event.GameEventCardAttachment.AttachMethod;
import forge.game.keyword.Keyword; import forge.game.keyword.Keyword;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -33,6 +34,7 @@ import forge.util.collect.FCollection;
import java.util.Map; import java.util.Map;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@@ -40,7 +42,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
protected final int id; protected final int id;
private String name = ""; private String name = "";
private int preventNextDamage = 0; private int preventNextDamage = 0;
private CardCollection enchantedBy; protected CardCollection attachedBy;
private Map<Card, Map<String, String>> preventionShieldsWithEffects = Maps.newTreeMap(); private Map<Card, Map<String, String>> preventionShieldsWithEffects = Maps.newTreeMap();
protected Map<CounterType, Integer> counters = Maps.newEnumMap(CounterType.class); protected Map<CounterType, Integer> counters = Maps.newEnumMap(CounterType.class);
@@ -93,7 +95,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
source.getDamageHistory().registerCombatDamage(this); source.getDamageHistory().registerCombatDamage(this);
} }
// damage prevention is already checked // 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) { 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; restDamage = 0;
} }
// if damage is greater than restDamage, damage was prevented // if damage is greater than restDamage, damage was prevented
if (damage > restDamage) { if (damage > restDamage) {
int prevent = damage - restDamage; int prevent = damage - restDamage;
preventMap.put(source, this, damage - restDamage); preventMap.put(source, this, damage - restDamage);
final Map<String, Object> runParams = Maps.newHashMap(); final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("DamageTarget", this); runParams.put("DamageTarget", this);
runParams.put("DamageAmount", prevent); runParams.put("DamageAmount", prevent);
runParams.put("DamageSource", source); runParams.put("DamageSource", source);
runParams.put("IsCombatDamage", isCombat); runParams.put("IsCombatDamage", isCombat);
getGame().getTriggerHandler().runTrigger(TriggerType.DamagePrevented, runParams, false); 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 String keyword);
public abstract boolean hasKeyword(final Keyword keyword); public abstract boolean hasKeyword(final Keyword keyword);
// GameEntities can now be Enchanted public final CardCollectionView getEnchantedBy() {
public final CardCollectionView getEnchantedBy(boolean allowModify) { if (attachedBy == null) {
return CardCollection.getView(enchantedBy, allowModify); return CardCollection.EMPTY;
}
// enchanted means attached by Aura
return CardLists.filter(attachedBy, CardPredicates.Presets.AURA);
} }
public final void setEnchantedBy(final CardCollection cards) {
enchantedBy = cards; public final CardCollectionView getAttachedBy() {
getView().updateEnchantedBy(this); return CardCollection.getView(attachedBy);
} }
public final void setEnchantedBy(final Iterable<Card> cards) {
public final void setAttachedBy(final Iterable<Card> cards) {
if (cards == null) { if (cards == null) {
enchantedBy = null; attachedBy = null;
} }
else { 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() { 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) { 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) { public final boolean isEnchantedBy(final String cardName) {
for (final Card aura : getEnchantedBy(false)) { // Rule 303.4k Even if c is no Aura it still counts
if (aura.getName().equals(cardName)) { return isAttachedBy(cardName);
return true; }
}
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; 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)) { public boolean canBeFortifiedBy(final Card aura) {
if (enchantedBy.isEmpty()) { return false;
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 canBeEnchantedBy(final Card aura) { public boolean canBeEnchantedBy(final Card aura, final boolean checkSBA) {
if (!aura.isAura()) {
return false;
}
SpellAbility sa = aura.getFirstAttachSpell(); SpellAbility sa = aura.getFirstAttachSpell();
TargetRestrictions tgt = null; TargetRestrictions tgt = null;
if (sa != null) { if (sa != null) {
tgt = sa.getTargetRestrictions(); tgt = sa.getTargetRestrictions();
} }
return !(hasProtectionFrom(aura) return !(hasProtectionFrom(aura, checkSBA)
|| ((tgt != null) && !isValid(tgt.getValidTgts(), aura.getController(), aura, sa))); || ((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! // Counters!
public boolean hasCounters() { public boolean hasCounters() {

View File

@@ -42,18 +42,19 @@ public abstract class GameEntityView extends TrackableObject {
set(TrackableProperty.PreventNextDamage, e.getPreventNextDamageTotalShields()); set(TrackableProperty.PreventNextDamage, e.getPreventNextDamageTotalShields());
} }
public Iterable<CardView> getEnchantedBy() { public Iterable<CardView> getAttachedBy() {
return get(TrackableProperty.EnchantedBy); return get(TrackableProperty.AttachedBy);
} }
protected void updateEnchantedBy(GameEntity e) { public boolean isAttached() {
if (e.isEnchanted()) { return getAttachedBy() != null;
set(TrackableProperty.EnchantedBy, CardView.getCollection(e.getEnchantedBy(false))); }
protected void updateAttachedBy(GameEntity e) {
if (e.isAttachedBy()) {
set(TrackableProperty.AttachedBy, CardView.getCollection(e.getAttachedBy()));
} }
else { else {
set(TrackableProperty.EnchantedBy, null); set(TrackableProperty.AttachedBy, null);
} }
} }
public boolean isEnchanted() {
return getEnchantedBy() != null;
}
} }

View File

@@ -27,7 +27,7 @@ public class AttachEffect extends SpellAbilityEffect {
final Player ap = sa.getActivatingPlayer(); final Player ap = sa.getActivatingPlayer();
// The Spell_Permanent (Auras) version of this AF needs to // The Spell_Permanent (Auras) version of this AF needs to
// move the card into play before Attaching // move the card into play before Attaching
sa.getHostCard().setController(ap, 0); sa.getHostCard().setController(ap, 0);
final Card c = ap.getGame().getAction().moveTo(ap.getZone(ZoneType.Battlefield), sa.getHostCard(), sa); final Card c = ap.getGame().getAction().moveTo(ap.getZone(ZoneType.Battlefield), sa.getHostCard(), sa);
sa.setHostCard(c); sa.setHostCard(c);
@@ -82,7 +82,7 @@ public class AttachEffect extends SpellAbilityEffect {
/** /**
* Handle attachment. * Handle attachment.
* *
* @param card * @param card
* the card * the card
* @param o * @param o
@@ -104,12 +104,8 @@ public class AttachEffect extends SpellAbilityEffect {
if (c.canBeEnchantedBy(card)) { if (c.canBeEnchantedBy(card)) {
handleAura(card, c); handleAura(card, c);
} }
} else if (card.isEquipment()) { } else {
if(c.canBeEquippedBy(card)) { card.attachEntity(c);
card.equipCard(c);
}
} else if (card.isFortification()) {
card.fortifyCard(c);
} }
} else if (o instanceof Player) { } else if (o instanceof Player) {
// Currently, a few cards can enchant players // Currently, a few cards can enchant players
@@ -124,42 +120,34 @@ public class AttachEffect extends SpellAbilityEffect {
/** /**
* Handle aura. * Handle aura.
* *
* @param card * @param card
* the card * the card
* @param tgt * @param tgt
* the tgt * the tgt
*/ */
public static void handleAura(final Card card, final GameEntity 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() { final GameCommand onLeavesPlay = new GameCommand() {
private static final long serialVersionUID = -639204333673364477L; private static final long serialVersionUID = -639204333673364477L;
@Override @Override
public void run() { public void run() {
final GameEntity entity = card.getEnchanting(); final GameEntity entity = card.getAttaching();
if (entity == null) { if (entity == null) {
return; return;
} }
card.unEnchantEntity(entity); card.unAttachEntity(entity);
} }
}; // Command }; // Command
card.addLeavesPlayCommand(onLeavesPlay); card.addLeavesPlayCommand(onLeavesPlay);
card.enchantEntity(tgt); card.attachEntity(tgt);
} }
/** /**
* Attach aura on indirect enter battlefield. * Attach aura on indirect enter battlefield.
* *
* @param source * @param source
* the source * the source
* @return true, if successful * @return true, if successful

View File

@@ -504,29 +504,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
} }
if (!list.isEmpty()) { if (!list.isEmpty()) {
Card attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, tgtC + " - Select a card to attach to."); Card attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, tgtC + " - Select a card to attach to.");
if (tgtC.isAura()) { tgtC.attachEntity(attachedTo);
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);
}
} else { // When it should enter the battlefield attached to an illegal permanent it fails } else { // When it should enter the battlefield attached to an illegal permanent it fails
continue; continue;
} }
@@ -536,15 +514,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
FCollectionView<Player> list = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("AttachedToPlayer"), sa); FCollectionView<Player> list = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("AttachedToPlayer"), sa);
if (!list.isEmpty()) { if (!list.isEmpty()) {
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, tgtC + " - Select a player to attach to."); Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, tgtC + " - Select a player to attach to.");
if (tgtC.isAura()) { tgtC.attachEntity(attachedTo);
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 { // When it should enter the battlefield attached to an illegal player it fails else { // When it should enter the battlefield attached to an illegal player it fails
continue; continue;
@@ -1022,42 +992,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
else { else {
attachedTo = list.get(0); attachedTo = list.get(0);
} }
if (c.isAura()) {
if (c.isEnchanting()) { if (c.isAttachment()) {
// If this Card is already Enchanting something, need c.attachEntity(attachedTo);
// 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);
} }
} }
else { // When it should enter the battlefield attached to an illegal permanent it fails else { // When it should enter the battlefield attached to an illegal permanent it fails
@@ -1069,15 +1006,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
FCollectionView<Player> list = AbilityUtils.getDefinedPlayers(source, sa.getParam("AttachedToPlayer"), sa); FCollectionView<Player> list = AbilityUtils.getDefinedPlayers(source, sa.getParam("AttachedToPlayer"), sa);
if (!list.isEmpty()) { if (!list.isEmpty()) {
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, c + " - Select a player to attach to."); Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, c + " - Select a player to attach to.");
if (c.isAura()) { c.attachEntity(attachedTo);
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);
}
} }
else { // When it should enter the battlefield attached to an illegal permanent it fails else { // When it should enter the battlefield attached to an illegal permanent it fails
continue; continue;

View File

@@ -154,9 +154,9 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host); choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host);
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : "Choose a card "; String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : "Choose a card ";
Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false); Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false);
if (choosen != null) { if (choosen != null) {
tgtCards.add(choosen); tgtCards.add(choosen);
} }
@@ -238,21 +238,8 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
} }
if (!list.isEmpty()) { if (!list.isEmpty()) {
Card attachedTo = activator.getController().chooseSingleEntityForEffect(list, sa, copyInPlay + " - Select a card to attach to."); Card attachedTo = activator.getController().chooseSingleEntityForEffect(list, sa, copyInPlay + " - Select a card to attach to.");
if (copyInPlay.isAura()) {
if (attachedTo.canBeEnchantedBy(copyInPlay)) { copyInPlay.attachEntity(attachedTo);
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);
}
} else { } else {
continue; continue;
} }

View File

@@ -95,9 +95,7 @@ public class DestroyEffect extends SpellAbilityEffect {
boolean destroyed = false; boolean destroyed = false;
final Card lki = CardUtil.getLKICopy(tgtC); final Card lki = CardUtil.getLKICopy(tgtC);
if (remAttached) { if (remAttached) {
card.addRemembered(tgtC.getEnchantedBy(false)); card.addRemembered(tgtC.getAttachedBy());
card.addRemembered(tgtC.getEquippedBy(false));
card.addRemembered(tgtC.getFortifiedBy(false));
} }
if (sac) { if (sac) {
destroyed = game.getAction().sacrifice(tgtC, sa) != null; destroyed = game.getAction().sacrifice(tgtC, sa) != null;

View File

@@ -495,32 +495,10 @@ public class TokenEffect extends SpellAbilityEffect {
game.getAction().checkStaticAbilities(false, Sets.newHashSet(lki), preList); game.getAction().checkStaticAbilities(false, Sets.newHashSet(lki), preList);
// TODO update when doing Attach Update // TODO update when doing Attach Update
boolean canAttach = lki.isAura() || lki.isEquipment() || lki.isFortification(); boolean canAttach = lki.isAttachment();
if (lki.isAura()) { if (canAttach && ge.canBeAttachedBy(lki)) {
if (!ge.canBeEnchantedBy(lki)) { canAttach = false;
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;
}
} }
// reset static abilities // reset static abilities
@@ -531,20 +509,7 @@ public class TokenEffect extends SpellAbilityEffect {
return false; return false;
} }
// TODO update when doing Attach Update tok.attachEntity(ge);
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);
}
}
return true; return true;
} else { } else {
// not a GameEntity, cant be attach // not a GameEntity, cant be attach

View File

@@ -17,25 +17,8 @@ import java.util.List;
public class UnattachAllEffect extends SpellAbilityEffect { public class UnattachAllEffect extends SpellAbilityEffect {
private static void handleUnattachment(final GameEntity o, final Card cardToUnattach) { private static void handleUnattachment(final GameEntity o, final Card cardToUnattach) {
if (o instanceof Card) { if (cardToUnattach.isAttachment() && o.isAttachedBy(cardToUnattach)) {
final Card c = (Card) o; cardToUnattach.unAttachEntity(cardToUnattach.getAttaching());
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);
}
} }
} }

View File

@@ -31,13 +31,9 @@ public class UnattachEffect extends SpellAbilityEffect {
if (cardToUnattach.isAura()) { if (cardToUnattach.isAura()) {
//final boolean gainControl = "GainControl".equals(af.parseParams().get("AILogic")); //final boolean gainControl = "GainControl".equals(af.parseParams().get("AILogic"));
//AbilityFactoryAttach.handleUnattachAura(cardToUnattach, c, gainControl); //AbilityFactoryAttach.handleUnattachAura(cardToUnattach, c, gainControl);
} else if (cardToUnattach.isEquipment()) { } else if (cardToUnattach.isAttachment()) {
if (cardToUnattach.isEquipping()) { if (cardToUnattach.isAttaching()) {
cardToUnattach.unEquipCard(cardToUnattach.getEquipping()); cardToUnattach.unAttachEntity(cardToUnattach.getAttaching());
}
} else if (cardToUnattach.isFortification()) {
if (cardToUnattach.isFortifying()) {
cardToUnattach.unFortifyCard(cardToUnattach.getFortifying());
} }
} }
} }

View File

@@ -83,8 +83,8 @@ public class ZoneExchangeEffect extends SpellAbilityEffect {
} }
// Enchant first // Enchant first
if (c != null) { if (c != null) {
object1.unEnchantEntity(c); object1.unAttachEntity(c);
object2.enchantEntity(c); object2.attachEntity(c);
} }
// Exchange Zone // Exchange Zone
game.getAction().moveTo(zone2, object1, sa); game.getAction().moveTo(zone2, object1, sa);

View File

@@ -37,7 +37,6 @@ import forge.game.combat.Combat;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.cost.CostSacrifice; import forge.game.cost.CostSacrifice;
import forge.game.event.*; import forge.game.event.*;
import forge.game.event.GameEventCardAttachment.AttachMethod;
import forge.game.event.GameEventCardDamaged.DamageType; import forge.game.event.GameEventCardDamaged.DamageType;
import forge.game.keyword.Keyword; import forge.game.keyword.Keyword;
import forge.game.keyword.KeywordCollection; import forge.game.keyword.KeywordCollection;
@@ -98,14 +97,13 @@ public class Card extends GameEntity implements Comparable<Card> {
private final KeywordCollection hiddenExtrinsicKeyword = new KeywordCollection(); private final KeywordCollection hiddenExtrinsicKeyword = new KeywordCollection();
// cards attached or otherwise linked to this card // 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; private CardCollection mustBlockCards, clones, gainControlTargets, chosenCards, blockedThisTurn, blockedByThisTurn;
// if this card is attached or linked to something, what card is it currently attached to // 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 attaching = null;
private GameEntity enchanting = null;
private GameEntity mustAttackEntity = null; private GameEntity mustAttackEntity = null;
private GameEntity mustAttackEntityThisTurn = null; private GameEntity mustAttackEntityThisTurn = null;
@@ -176,7 +174,7 @@ public class Card extends GameEntity implements Comparable<Card> {
// for Vanguard / Manapool / Emblems etc. // for Vanguard / Manapool / Emblems etc.
private boolean isImmutable = false; private boolean isImmutable = false;
private int exertThisTurn = 0; private int exertThisTurn = 0;
private PlayerCollection exertedByPlayer = new PlayerCollection(); private PlayerCollection exertedByPlayer = new PlayerCollection();
@@ -413,7 +411,7 @@ public class Card extends GameEntity implements Comparable<Card> {
if (!changedCardKeywords.isEmpty()) { if (!changedCardKeywords.isEmpty()) {
currentState.getView().updateKeywords(this, currentState); currentState.getView().updateKeywords(this, currentState);
} }
if (state == CardStateName.FaceDown) { if (state == CardStateName.FaceDown) {
view.updateHiddenId(game.nextHiddenCardId()); view.updateHiddenId(game.nextHiddenCardId());
} }
@@ -902,7 +900,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public final void clearTriggersNew() { public final void clearTriggersNew() {
currentState.clearTriggers(); currentState.clearTriggers();
} }
public final boolean hasTrigger(final Trigger t) { public final boolean hasTrigger(final Trigger t) {
return currentState.hasTrigger(t); return currentState.hasTrigger(t);
} }
@@ -1064,7 +1062,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public final boolean hasSecondStrike() { public final boolean hasSecondStrike() {
return hasDoubleStrike() || !hasFirstStrike(); return hasDoubleStrike() || !hasFirstStrike();
} }
public final boolean hasConverge() { public final boolean hasConverge() {
return "Count$Converge".equals(getSVar("X")) || "Count$Converge".equals(getSVar("Y")) || hasKeyword("Sunburst"); return "Count$Converge".equals(getSVar("X")) || "Count$Converge".equals(getSVar("Y")) || hasKeyword("Sunburst");
} }
@@ -1290,7 +1288,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public final void removeSVar(final String var) { public final void removeSVar(final String var) {
currentState.removeSVar(var); currentState.removeSVar(var);
} }
public final int sumAllCounters() { public final int sumAllCounters() {
int count = 0; int count = 0;
for (final Integer value2 : counters.values()) { for (final Integer value2 : counters.values()) {
@@ -1519,7 +1517,7 @@ public class Card extends GameEntity implements Comparable<Card> {
sbLong.append(parts[0]).append(" ").append(ManaCostParser.parse(parts[1])).append("\r\n"); sbLong.append(parts[0]).append(" ").append(ManaCostParser.parse(parts[1])).append("\r\n");
} }
} else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph")) { } else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph")) {
String[] k = keyword.split(":"); String[] k = keyword.split(":");
sbLong.append(k[0]); sbLong.append(k[0]);
if (k.length > 1) { if (k.length > 1) {
final Cost mCost = new Cost(k[1], true); final Cost mCost = new Cost(k[1], true);
@@ -1704,7 +1702,7 @@ public class Card extends GameEntity implements Comparable<Card> {
if (sbLong.length() > 0) { if (sbLong.length() > 0) {
sbLong.append("\r\n"); sbLong.append("\r\n");
} }
i++; i++;
} }
if (sb.length() > 0) { if (sb.length() > 0) {
@@ -2223,7 +2221,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public final FCollectionView<SpellAbility> getNonManaAbilities() { public final FCollectionView<SpellAbility> getNonManaAbilities() {
return currentState.getNonManaAbilities(); return currentState.getNonManaAbilities();
} }
public final boolean hasSpellAbility(final SpellAbility sa) { public final boolean hasSpellAbility(final SpellAbility sa) {
return currentState.hasSpellAbility(sa); return currentState.hasSpellAbility(sa);
} }
@@ -2410,7 +2408,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public final void addUntapCommand(final GameCommand c) { public final void addUntapCommand(final GameCommand c) {
untapCommandList.add(c); untapCommandList.add(c);
} }
public final void addUnattachCommand(final GameCommand c) { public final void addUnattachCommand(final GameCommand c) {
unattachCommandList.add(c); unattachCommandList.add(c);
} }
@@ -2472,7 +2470,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public void setStartedTheTurnUntapped(boolean untapped) { public void setStartedTheTurnUntapped(boolean untapped) {
startedTheTurnUntapped = untapped; startedTheTurnUntapped = untapped;
} }
public boolean cameUnderControlSinceLastUpkeep() { public boolean cameUnderControlSinceLastUpkeep() {
return cameUnderControlSinceLastUpkeep; return cameUnderControlSinceLastUpkeep;
} }
@@ -2585,186 +2583,126 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
} }
public final CardCollectionView getEquippedBy(boolean allowModify) { public final CardCollectionView getEquippedBy() {
return CardCollection.getView(equippedBy, allowModify); if (this.attachedBy == null) {
} return CardCollection.EMPTY;
public final void setEquippedBy(final CardCollection cards) {
equippedBy = view.setCards(equippedBy, cards, TrackableProperty.EquippedBy);
}
public final void setEquippedBy(final Iterable<Card> 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;
}
} }
return false;
return CardLists.filter(attachedBy, CardPredicates.Presets.EQUIPMENT);
} }
public final CardCollectionView getFortifiedBy(boolean allowModify) { public final boolean isEquipped() {
return CardCollection.getView(fortifiedBy, allowModify); if (this.attachedBy == null) {
return false;
}
return CardLists.count(attachedBy, CardPredicates.Presets.EQUIPMENT) > 0;
} }
public final void setFortifiedBy(final CardCollection cards) { public final boolean isEquippedBy(Card c) {
fortifiedBy = view.setCards(fortifiedBy, cards, TrackableProperty.FortifiedBy); return this.isAttachedBy(c);
} }
public final void setFortifiedBy(final Iterable<Card> cards) { public final boolean isEquippedBy(final String cardName) {
fortifiedBy = view.setCards(fortifiedBy, cards, TrackableProperty.FortifiedBy); 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() { 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) { public final boolean isFortifiedBy(Card c) {
return FCollection.hasElement(fortifiedBy, c); // 301.5e + 301.6
return isAttachedBy(c);
} }
public final Card getEquipping() { public final Card getEquipping() {
return equipping; return this.getAttachingCard();
}
public final void setEquipping(final Card card) {
equipping = view.setCard(equipping, card, TrackableProperty.Equipping);
} }
public final boolean isEquipping() { 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() { public final boolean isFortifying() {
return fortifying != null; return this.isAttaching();
} }
public final void equipCard(final Card c) { public final void equipCard(final Card c) {
if (c.hasKeyword("CARDNAME can't be equipped.")) { if (!isEquipment()) {
getGame().getGameLog().add(GameLogEntryType.STACK_RESOLVE, "Trying to equip " + c.getName() + " but it can't be equipped.");
return; 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; this.attachEntity(c);
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<String, Object> runParams = Maps.newHashMap();
runParams.put("AttachSource", this);
runParams.put("AttachTarget", c);
getController().getGame().getTriggerHandler().runTrigger(TriggerType.Attached, runParams, false);
} }
public final void fortifyCard(final Card c) { public final void fortifyCard(final Card c) {
Card oldTarget = null; if (!isFortification()) {
if (isFortifying()) { return;
oldTarget = fortifying;
unFortifyCard(oldTarget);
} }
setFortifying(c); this.attachEntity(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<String, Object> runParams = Maps.newHashMap();
runParams.put("AttachSource", this);
runParams.put("AttachTarget", c);
getController().getGame().getTriggerHandler().runTrigger(TriggerType.Attached, runParams, false);
} }
public final void unEquipCard(final Card c) { // equipment.unEquipCard(equippedCard); public final void unEquipCard(final Card c) { // equipment.unEquipCard(equippedCard);
if (equipping != null && equipping.getId() == c.getId()) { this.unAttachEntity(c);
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<String, Object> 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();
} }
public final void unEquipAllCards() { public final void unEquipAllCards() {
if (isEquipped()) { if (isEquipped()) {
for (Card c : getEquippedBy(true)) { for (Card c : Lists.newArrayList(getEquippedBy())) {
c.unEquipCard(this); c.unAttachEntity(this);
} }
} }
} }
public final GameEntity getEnchanting() { public final GameEntity getAttaching() {
return enchanting; return attaching;
} }
public final void setEnchanting(final GameEntity e) { public final void setAttaching(final GameEntity e) {
if (enchanting == e) { return; } if (attaching == e) { return; }
enchanting = e; attaching = e;
view.updateEnchanting(this); view.updateAttaching(this);
} }
public final Card getEnchantingCard() { public final void removeAttaching(final GameEntity e) {
if (enchanting instanceof Card) { if (attaching == e) {
return (Card) enchanting; setAttaching(null);
}
}
public final boolean isAttaching() {
return attaching != null;
}
public final Card getAttachingCard() {
if (attaching instanceof Card) {
return (Card) attaching;
} }
return null; return null;
} }
public final GameEntity getEnchanting() {
return getAttaching();
}
public final Card getEnchantingCard() {
return getAttachingCard();
}
public final Player getEnchantingPlayer() { public final Player getEnchantingPlayer() {
if (enchanting instanceof Player) { if (attaching instanceof Player) {
return (Player) enchanting; return (Player) attaching;
} }
return null; return null;
} }
public final boolean isEnchanting() { public final boolean isEnchanting() {
return enchanting != null; return isAttaching();
} }
public final boolean isEnchantingCard() { public final boolean isEnchantingCard() {
return getEnchantingCard() != null; return getEnchantingCard() != null;
@@ -2773,43 +2711,63 @@ public class Card extends GameEntity implements Comparable<Card> {
return getEnchantingPlayer() != null; return getEnchantingPlayer() != null;
} }
public final void removeEnchanting(final GameEntity e) { public final void attachEntity(final GameEntity entity) {
if (enchanting == e) { if (!entity.canBeAttachedBy(this)) {
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.");
return; 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 // run trigger
final Map<String, Object> runParams = Maps.newHashMap(); final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("AttachSource", this); runParams.put("AttachSource", this);
runParams.put("AttachTarget", entity); runParams.put("AttachTarget", entity);
getController().getGame().getTriggerHandler().runTrigger(TriggerType.Attached, runParams, false); getController().getGame().getTriggerHandler().runTrigger(TriggerType.Attached, runParams, false);
} }
public final void unEnchantEntity(final GameEntity entity) { public final void enchantEntity(final GameEntity entity) {
if (enchanting == null || !enchanting.equals(entity)) { if (!isAura()) {
return;
}
this.attachEntity(entity);
}
public final void unAttachEntity(final GameEntity entity) {
if (attaching == null || !attaching.equals(entity)) {
return; return;
} }
setEnchanting(null); setAttaching(null);
entity.removeEnchantedBy(this); entity.removeAttachedBy(this);
// Handle Bestowed Aura part
if (isBestowed()) { if (isBestowed()) {
unanimateBestow(); unanimateBestow();
} }
getGame().fireEvent(new GameEventCardAttachment(this, entity, null, AttachMethod.Enchant)); getGame().fireEvent(new GameEventCardAttachment(this, entity, null));
// Run triggers
final Map<String, Object> runParams = Maps.newTreeMap();
runParams.put("Attach", this);
runParams.put("Object", entity);
getGame().getTriggerHandler().runTrigger(TriggerType.Unattach, runParams, false);
runUnattachCommands(); runUnattachCommands();
} }
@@ -2820,11 +2778,11 @@ public class Card extends GameEntity implements Comparable<Card> {
public final void addType(final String type0) { public final void addType(final String type0) {
currentState.addType(type0); currentState.addType(type0);
} }
public final void removeType(final CardType.Supertype st) { public final void removeType(final CardType.Supertype st) {
currentState.removeType(st); currentState.removeType(st);
} }
public final void setCreatureTypes(Collection<String> ctypes) { public final void setCreatureTypes(Collection<String> ctypes) {
currentState.setCreatureTypes(ctypes); currentState.setCreatureTypes(ctypes);
} }
@@ -2832,7 +2790,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public final CardTypeView getType() { public final CardTypeView getType() {
return getType(currentState); return getType(currentState);
} }
public final CardTypeView getType(CardState state) { public final CardTypeView getType(CardState state) {
if (changedCardTypes.isEmpty()) { if (changedCardTypes.isEmpty()) {
return state.getType(); return state.getType();
@@ -2843,7 +2801,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public Iterable<CardChangedType> getChangedCardTypes() { public Iterable<CardChangedType> getChangedCardTypes() {
return Iterables.unmodifiableIterable(changedCardTypes.values()); return Iterables.unmodifiableIterable(changedCardTypes.values());
} }
public Map<Long, CardChangedType> getChangedCardTypesMap() { public Map<Long, CardChangedType> getChangedCardTypesMap() {
return Collections.unmodifiableMap(changedCardTypes); return Collections.unmodifiableMap(changedCardTypes);
} }
@@ -3050,7 +3008,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public final void addNewPT(final Integer power, final Integer toughness, final long timestamp) { public final void addNewPT(final Integer power, final Integer toughness, final long timestamp) {
addNewPT(power, toughness, timestamp, false); addNewPT(power, toughness, timestamp, false);
} }
public final void addNewPT(final Integer power, final Integer toughness, final long timestamp, final boolean cda) { public final void addNewPT(final Integer power, final Integer toughness, final long timestamp, final boolean cda) {
if (cda) { if (cda) {
newPTCharacterDefining.put(timestamp, Pair.of(power, toughness)); newPTCharacterDefining.put(timestamp, Pair.of(power, toughness));
@@ -3063,10 +3021,10 @@ public class Card extends GameEntity implements Comparable<Card> {
public final void removeNewPT(final long timestamp) { public final void removeNewPT(final long timestamp) {
boolean removed = false; boolean removed = false;
removed |= newPT.remove(timestamp) != null; removed |= newPT.remove(timestamp) != null;
removed |= newPTCharacterDefining.remove(timestamp) != null; removed |= newPTCharacterDefining.remove(timestamp) != null;
if (removed) { if (removed) {
currentState.getView().updatePower(this); currentState.getView().updatePower(this);
currentState.getView().updateToughness(this); currentState.getView().updateToughness(this);
@@ -3358,7 +3316,7 @@ public class Card extends GameEntity implements Comparable<Card> {
final boolean removeAllKeywords, final boolean removeIntrinsicKeywords, final long timestamp) { final boolean removeAllKeywords, final boolean removeIntrinsicKeywords, final long timestamp) {
addChangedCardKeywords(keywords, removeKeywords, removeAllKeywords, removeIntrinsicKeywords, timestamp, true); addChangedCardKeywords(keywords, removeKeywords, removeAllKeywords, removeIntrinsicKeywords, timestamp, true);
} }
public final void addChangedCardKeywords(final List<String> keywords, final List<String> removeKeywords, public final void addChangedCardKeywords(final List<String> keywords, final List<String> removeKeywords,
final boolean removeAllKeywords, final boolean removeIntrinsicKeywords, final long timestamp, final boolean updateView) { final boolean removeAllKeywords, final boolean removeIntrinsicKeywords, final long timestamp, final boolean updateView) {
@@ -3377,12 +3335,12 @@ public class Card extends GameEntity implements Comparable<Card> {
newCks.addKeywordsToCard(this); newCks.addKeywordsToCard(this);
changedCardKeywords.put(timestamp, newCks); changedCardKeywords.put(timestamp, newCks);
} }
if (updateView) { if (updateView) {
updateKeywords(); updateKeywords();
} }
} }
public final void addChangedCardKeywordsInternal( public final void addChangedCardKeywordsInternal(
final List<KeywordInterface> keywords, final List<KeywordInterface> removeKeywords, final List<KeywordInterface> keywords, final List<KeywordInterface> removeKeywords,
final boolean removeAllKeywords, final boolean removeIntrinsicKeywords, final boolean removeAllKeywords, final boolean removeIntrinsicKeywords,
@@ -3404,7 +3362,7 @@ public class Card extends GameEntity implements Comparable<Card> {
newCks.addKeywordsToCard(this); newCks.addKeywordsToCard(this);
changedCardKeywords.put(timestamp, newCks); changedCardKeywords.put(timestamp, newCks);
} }
if (updateView) { if (updateView) {
updateKeywords(); updateKeywords();
} }
@@ -3432,7 +3390,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public final KeywordsChange removeChangedCardKeywords(final long timestamp, final boolean updateView) { public final KeywordsChange removeChangedCardKeywords(final long timestamp, final boolean updateView) {
KeywordsChange change = changedCardKeywords.remove(timestamp); KeywordsChange change = changedCardKeywords.remove(timestamp);
if (change != null && updateView) { if (change != null && updateView) {
updateKeywords(); updateKeywords();
} }
return change; return change;
@@ -3445,10 +3403,10 @@ public class Card extends GameEntity implements Comparable<Card> {
public final Collection<KeywordInterface> getUnhiddenKeywords(CardState state) { public final Collection<KeywordInterface> getUnhiddenKeywords(CardState state) {
return state.getCachedKeywords(); return state.getCachedKeywords();
} }
public final void updateKeywordsCache(final CardState state) { public final void updateKeywordsCache(final CardState state) {
KeywordCollection keywords = new KeywordCollection(); KeywordCollection keywords = new KeywordCollection();
//final List<KeywordInterface> keywords = Lists.newArrayList(); //final List<KeywordInterface> keywords = Lists.newArrayList();
boolean removeIntrinsic = false; boolean removeIntrinsic = false;
for (final KeywordsChange ck : changedCardKeywords.values()) { for (final KeywordsChange ck : changedCardKeywords.values()) {
@@ -3568,7 +3526,7 @@ public class Card extends GameEntity implements Comparable<Card> {
String oldtxt = kw.getOriginal(); String oldtxt = kw.getOriginal();
final String newtxt = AbilityUtils.applyKeywordTextChangeEffects(oldtxt, this); final String newtxt = AbilityUtils.applyKeywordTextChangeEffects(oldtxt, this);
if (!newtxt.equals(oldtxt)) { if (!newtxt.equals(oldtxt)) {
KeywordInterface newKw = Keyword.getInstance(newtxt); KeywordInterface newKw = Keyword.getInstance(newtxt);
addKeywords.add(newKw); addKeywords.add(newKw);
removeKeywords.add(kw); removeKeywords.add(kw);
keywordsGrantedByTextChanges.add(newKw); keywordsGrantedByTextChanges.add(newKw);
@@ -3733,7 +3691,7 @@ public class Card extends GameEntity implements Comparable<Card> {
currentState.getView().updateKeywords(this, currentState); currentState.getView().updateKeywords(this, currentState);
} }
} }
public final void addHiddenExtrinsicKeyword(KeywordInterface k) { public final void addHiddenExtrinsicKeyword(KeywordInterface k) {
if (hiddenExtrinsicKeyword.insert(k)) { if (hiddenExtrinsicKeyword.insert(k)) {
view.updateNonAbilityText(this); view.updateNonAbilityText(this);
@@ -3788,7 +3746,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public final void removeStaticAbility(StaticAbility stAb) { public final void removeStaticAbility(StaticAbility stAb) {
currentState.removeStaticAbility(stAb); currentState.removeStaticAbility(stAb);
} }
public void updateStaticAbilities(List<StaticAbility> list, CardState state) { public void updateStaticAbilities(List<StaticAbility> list, CardState state) {
for (KeywordInterface kw : getUnhiddenKeywords(state)) { for (KeywordInterface kw : getUnhiddenKeywords(state)) {
list.addAll(kw.getStaticAbilities()); list.addAll(kw.getStaticAbilities());
@@ -3820,7 +3778,9 @@ public class Card extends GameEntity implements Comparable<Card> {
public final boolean isPlaneswalker() { return getType().isPlaneswalker(); } public final boolean isPlaneswalker() { return getType().isPlaneswalker(); }
public final boolean isEnchantment() { return getType().isEnchantment(); } public final boolean isEnchantment() { return getType().isEnchantment(); }
public final boolean isAura() { return getType().hasSubtype("Aura"); } 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 isScheme() { return getType().isScheme(); }
public final boolean isPhenomenon() { return getType().isPhenomenon(); } public final boolean isPhenomenon() { return getType().isPhenomenon(); }
@@ -3886,27 +3846,13 @@ public class Card extends GameEntity implements Comparable<Card> {
setDirectlyPhasedOut(direct); setDirectlyPhasedOut(direct);
} }
if (isEquipped()) { if (isAttachedBy()) {
for (final Card eq : getEquippedBy(false)) { for (final Card eq : getAttachedBy()) {
if (eq.isPhasedOut() == phasingIn) { if (eq.isPhasedOut() == phasingIn) {
eq.phase(false); 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())); getGame().fireEvent(new GameEventCardPhased(this, isPhasedOut()));
} }
@@ -4181,7 +4127,7 @@ public class Card extends GameEntity implements Comparable<Card> {
//do not check for SplitCard anymore //do not check for SplitCard anymore
return host.getCMC() == n; return host.getCMC() == n;
} }
public final boolean sharesCMCWith(final Card c1) { public final boolean sharesCMCWith(final Card c1) {
//need to get GameState for Discarded Cards //need to get GameState for Discarded Cards
final Card host = game.getCardState(this); final Card host = game.getCardState(this);
@@ -4784,7 +4730,7 @@ public class Card extends GameEntity implements Comparable<Card> {
return (c != null ? c.getImageKey() : ""); return (c != null ? c.getImageKey() : "");
} }
public final boolean isTributed() { return tributed; } public final boolean isTributed() { return tributed; }
public final void setTributed(final boolean b) { public final void setTributed(final boolean b) {
@@ -4808,7 +4754,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public final int getExertedThisTurn() { public final int getExertedThisTurn() {
return exertThisTurn; return exertThisTurn;
} }
public void exert() { public void exert() {
exertedByPlayer.add(getController()); exertedByPlayer.add(getController());
exertThisTurn++; exertThisTurn++;
@@ -4818,16 +4764,16 @@ public class Card extends GameEntity implements Comparable<Card> {
runParams.put("Player", getController()); runParams.put("Player", getController());
game.getTriggerHandler().runTrigger(TriggerType.Exerted, runParams, false); game.getTriggerHandler().runTrigger(TriggerType.Exerted, runParams, false);
} }
public boolean isExertedBy(final Player player) { public boolean isExertedBy(final Player player) {
return exertedByPlayer.contains(player); return exertedByPlayer.contains(player);
} }
public void removeExertedBy(final Player player) { public void removeExertedBy(final Player player) {
exertedByPlayer.remove(player); exertedByPlayer.remove(player);
view.updateExertedThisTurn(this, getExertedThisTurn() > 0); view.updateExertedThisTurn(this, getExertedThisTurn() > 0);
} }
protected void resetExtertedThisTurn() { protected void resetExtertedThisTurn() {
exertThisTurn = 0; exertThisTurn = 0;
view.updateExertedThisTurn(this, false); view.updateExertedThisTurn(this, false);
@@ -4917,8 +4863,8 @@ public class Card extends GameEntity implements Comparable<Card> {
public final CardCollectionView getDevouredCards() { public final CardCollectionView getDevouredCards() {
return CardCollection.getView(devouredCards); return CardCollection.getView(devouredCards);
} }
public final CardCollectionView getHauntedBy() { public final CardCollectionView getHauntedBy() {
return CardCollection.getView(hauntedBy); return CardCollection.getView(hauntedBy);
} }
@@ -4997,7 +4943,7 @@ public class Card extends GameEntity implements Comparable<Card> {
return sum; return sum;
} }
@Override
public boolean hasProtectionFrom(final Card source) { public boolean hasProtectionFrom(final Card source) {
return hasProtectionFrom(source, false, false); return hasProtectionFrom(source, false, false);
} }
@@ -5006,6 +4952,7 @@ public class Card extends GameEntity implements Comparable<Card> {
return hasProtectionFrom(source, false, true); return hasProtectionFrom(source, false, true);
} }
@Override
public boolean hasProtectionFrom(final Card source, final boolean checkSBA) { public boolean hasProtectionFrom(final Card source, final boolean checkSBA) {
return hasProtectionFrom(source, checkSBA, false); return hasProtectionFrom(source, checkSBA, false);
} }
@@ -5019,6 +4966,11 @@ public class Card extends GameEntity implements Comparable<Card> {
return true; return true;
} }
// Protection only works on the Battlefield
if (isInZone(ZoneType.Battlefield)) {
return false;
}
final boolean colorlessDamage = damageSource && source.hasKeyword("Colorless Damage Source"); final boolean colorlessDamage = damageSource && source.hasKeyword("Colorless Damage Source");
for (final KeywordInterface inst : getKeywords()) { for (final KeywordInterface inst : getKeywords()) {
@@ -5078,11 +5030,11 @@ public class Card extends GameEntity implements Comparable<Card> {
// if colorlessDamage then it does only check damage color.. // if colorlessDamage then it does only check damage color..
if (colorlessDamage) { if (colorlessDamage) {
if (characteristic.endsWith("White") || characteristic.endsWith("Blue") if (characteristic.endsWith("White") || characteristic.endsWith("Blue")
|| characteristic.endsWith("Black") || characteristic.endsWith("Red") || characteristic.endsWith("Black") || characteristic.endsWith("Red")
|| characteristic.endsWith("Green") || characteristic.endsWith("Colorless") || characteristic.endsWith("Green") || characteristic.endsWith("Colorless")
|| characteristic.endsWith("ChosenColor")) { || characteristic.endsWith("ChosenColor")) {
characteristic += "Source"; characteristic += "Source";
} }
} }
@@ -5221,14 +5173,50 @@ public class Card extends GameEntity implements Comparable<Card> {
tgt = sa.getTargetRestrictions(); 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) return !(hasProtectionFrom(aura, checkSBA)
|| (hasKeyword("CARDNAME can't be enchanted in the future.") && !isEnchantedBy(aura)) || (hasKeyword("CARDNAME can't be enchanted in the future.") && !isEnchantedBy(aura))
|| (hasKeyword("CARDNAME can't be enchanted.") && !aura.getName().equals("Anti-Magic Aura") || (hasKeyword("CARDNAME can't be enchanted.") && !aura.getName().equals("Anti-Magic Aura")
&& !(aura.getName().equals("Consecrate Land") && aura.isInZone(ZoneType.Battlefield))) && !(aura.getName().equals("Consecrate Land") && aura.isInZone(ZoneType.Battlefield))));
|| ((tgt != null) && !isValid(tgt.getValidTgts(), aura.getController(), aura, sa)));
} }
public final boolean canBeEquippedBy(final Card equip) { 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()) { for(KeywordInterface inst : equip.getKeywords()) {
String kw = inst.getOriginal(); String kw = inst.getOriginal();
if(!kw.startsWith("CantEquip")) { if(!kw.startsWith("CantEquip")) {
@@ -5241,8 +5229,19 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
} }
return !(hasProtectionFrom(equip) return !(hasProtectionFrom(equip)
|| hasKeyword("CARDNAME can't be equipped.") || hasKeyword("CARDNAME can't be equipped."));
|| !isValid("Creature", equip.getController(), equip, null)); }
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<ReplacementEffect> getReplacementEffects() { public FCollectionView<ReplacementEffect> getReplacementEffects() {
@@ -5265,7 +5264,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public void removeReplacementEffect(ReplacementEffect replacementEffect) { public void removeReplacementEffect(ReplacementEffect replacementEffect) {
currentState.removeReplacementEffect(replacementEffect); currentState.removeReplacementEffect(replacementEffect);
} }
public void updateReplacementEffects(List<ReplacementEffect> list, CardState state) { public void updateReplacementEffects(List<ReplacementEffect> list, CardState state) {
for (KeywordInterface kw : getUnhiddenKeywords(state)) { for (KeywordInterface kw : getUnhiddenKeywords(state)) {
list.addAll(kw.getReplacements()); list.addAll(kw.getReplacements());
@@ -5449,7 +5448,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public final void setLKICMC(final int cmc) { public final void setLKICMC(final int cmc) {
this.lkiCMC = cmc; this.lkiCMC = cmc;
} }
public final boolean isLKI() { public final boolean isLKI() {
return this.lkiCMC >= 0; return this.lkiCMC >= 0;
} }
@@ -5758,7 +5757,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public final void addGoad(Long timestamp, final Player p) { public final void addGoad(Long timestamp, final Player p) {
goad.put(timestamp, p); goad.put(timestamp, p);
} }
public final void removeGoad(Long timestamp) { public final void removeGoad(Long timestamp) {
goad.remove(timestamp); goad.remove(timestamp);
} }

View File

@@ -92,12 +92,8 @@ public class CardFactory {
out.setState(in.getCurrentStateName(), true); out.setState(in.getCurrentStateName(), true);
// this's necessary for forge.game.GameAction.unattachCardLeavingBattlefield(Card) // this's necessary for forge.game.GameAction.unattachCardLeavingBattlefield(Card)
out.setEquipping(in.getEquipping()); out.setAttachedBy(in.getAttachedBy());
out.setEquippedBy(in.getEquippedBy(false)); out.setAttaching(in.getAttaching());
out.setFortifying(in.getFortifying());
out.setFortifiedBy(in.getFortifiedBy(false));
out.setEnchantedBy(in.getEnchantedBy(false));
out.setEnchanting(in.getEnchanting());
out.setClones(in.getClones()); out.setClones(in.getClones());
out.setCastSA(in.getCastSA()); out.setCastSA(in.getCastSA());

View File

@@ -470,6 +470,15 @@ public final class CardPredicates {
return c.isEnchantment(); return c.isEnchantment();
} }
}; };
/**
* a Predicate<Card> to get all aura.
*/
public static final Predicate<Card> AURA = new Predicate<Card>() {
@Override
public boolean apply(Card c) {
return c.isAura();
}
};
/** /**
* a Predicate<Card> to get all equipment. * a Predicate<Card> to get all equipment.
*/ */
@@ -482,7 +491,7 @@ public final class CardPredicates {
/** /**
* a Predicate<Card> to get all fortification. * a Predicate<Card> to get all fortification.
*/ */
public static final Predicate<Card> Fortification = new Predicate<Card>() { public static final Predicate<Card> FORTIFICATION = new Predicate<Card>() {
@Override @Override
public boolean apply(Card c) { public boolean apply(Card c) {
return c.isFortification(); return c.isFortification();

View File

@@ -405,7 +405,7 @@ public class CardProperty {
return false; return false;
} }
} else if (property.equals("Attached")) { } 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; return false;
} }
} else if (property.startsWith("AttachedTo")) { } else if (property.startsWith("AttachedTo")) {
@@ -433,9 +433,8 @@ public class CardProperty {
return false; return false;
} }
} else { } else {
if ((card.getEnchanting() == null || !card.getEnchanting().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)) && (card.getEquipping() == null || !card.getEquipping().isValid(restriction, sourceController, source, spellAbility))) {
&& (card.getFortifying() == null || !card.getFortifying().isValid(restriction, sourceController, source, spellAbility))) {
return false; return false;
} }
} }
@@ -445,7 +444,7 @@ public class CardProperty {
return false; return false;
} }
} else if (property.equals("NotAttachedTo")) { } 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; return false;
} }
} else if (property.startsWith("EnchantedBy")) { } else if (property.startsWith("EnchantedBy")) {
@@ -476,7 +475,7 @@ public class CardProperty {
} }
break; break;
default: // EnchantedBy Aura.Other default: // EnchantedBy Aura.Other
for (final Card aura : card.getEnchantedBy(false)) { for (final Card aura : card.getEnchantedBy()) {
if (aura.isValid(restriction, sourceController, source, spellAbility)) { if (aura.isValid(restriction, sourceController, source, spellAbility)) {
return true; return true;
} }
@@ -572,11 +571,12 @@ public class CardProperty {
return false; return false;
} }
} else if (property.startsWith("Equipped")) { } else if (property.startsWith("Equipped")) {
if (card.getEquipping() != source) { if (!source.isAttachedBy(card)) {
return false; return false;
} }
} else if (property.startsWith("Fortified")) { } else if (property.startsWith("Fortified")) {
if (card.getFortifying() != source) { // FIXME TODO what property has this?
if (!source.isAttachedBy(card)) {
return false; return false;
} }
} else if (property.startsWith("HauntedBy")) { } else if (property.startsWith("HauntedBy")) {

View File

@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
@@ -93,8 +93,8 @@ public final class CardUtil {
if (kw.startsWith("HIDDEN")) { if (kw.startsWith("HIDDEN")) {
kw = kw.substring(7); kw = kw.substring(7);
} }
return !kw.startsWith("Protection") && !kw.startsWith("CantBeBlockedBy") return !kw.startsWith("Protection") && !kw.startsWith("CantBeBlockedBy")
&& !NON_STACKING_LIST.contains(kw); && !NON_STACKING_LIST.contains(kw);
} }
@@ -108,7 +108,7 @@ public final class CardUtil {
/** /**
* getThisTurnEntered. * getThisTurnEntered.
* *
* @param to zone going to * @param to zone going to
* @param from zone coming from * @param from zone coming from
* @param valid a isValid expression * @param valid a isValid expression
@@ -121,7 +121,7 @@ public final class CardUtil {
/** /**
* getThisTurnEntered. * getThisTurnEntered.
* *
* @param to zone going to * @param to zone going to
* @param from zone coming from * @param from zone coming from
* @param valid a isValid expression * @param valid a isValid expression
@@ -146,7 +146,7 @@ public final class CardUtil {
/** /**
* getLastTurnEntered. * getLastTurnEntered.
* *
* @param to zone going to * @param to zone going to
* @param from zone coming from * @param from zone coming from
* @param valid a isValid expression * @param valid a isValid expression
@@ -159,7 +159,7 @@ public final class CardUtil {
/** /**
* getLastTurnEntered. * getLastTurnEntered.
* *
* @param to zone going to * @param to zone going to
* @param from zone coming from * @param from zone coming from
* @param valid a isValid expression * @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 // 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) // (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 // used for the purpose of cards that care about the zone the card was known to be in last
newCopy.setLastKnownZone(in.getLastKnownZone()); newCopy.setLastKnownZone(in.getLastKnownZone());
@@ -264,12 +264,10 @@ public final class CardUtil {
newCopy.setReceivedDamageFromThisTurn(in.getReceivedDamageFromThisTurn()); newCopy.setReceivedDamageFromThisTurn(in.getReceivedDamageFromThisTurn());
newCopy.setReceivedDamageFromPlayerThisTurn(in.getReceivedDamageFromPlayerThisTurn()); newCopy.setReceivedDamageFromPlayerThisTurn(in.getReceivedDamageFromPlayerThisTurn());
newCopy.getDamageHistory().setCreatureGotBlockedThisTurn(in.getDamageHistory().getCreatureGotBlockedThisTurn()); newCopy.getDamageHistory().setCreatureGotBlockedThisTurn(in.getDamageHistory().getCreatureGotBlockedThisTurn());
newCopy.setEnchanting(in.getEnchanting());
newCopy.setEnchantedBy(in.getEnchantedBy(false)); newCopy.setAttachedBy(in.getAttachedBy());
newCopy.setEquipping(in.getEquipping()); newCopy.setAttaching(in.getAttaching());
newCopy.setEquippedBy(in.getEquippedBy(false));
newCopy.setFortifying(in.getFortifying());
newCopy.setFortifiedBy(in.getFortifiedBy(false));
newCopy.setClones(in.getClones()); newCopy.setClones(in.getClones());
newCopy.setHaunting(in.getHaunting()); newCopy.setHaunting(in.getHaunting());
newCopy.setCopiedPermanent(in.getCopiedPermanent()); newCopy.setCopiedPermanent(in.getCopiedPermanent());
@@ -313,9 +311,9 @@ public final class CardUtil {
final Game game = source.getGame(); final Game game = source.getGame();
ColorSet cs = CardUtil.getColors(origin); ColorSet cs = CardUtil.getColors(origin);
for (byte color : MagicColor.WUBRG) { for (byte color : MagicColor.WUBRG) {
if(!cs.hasAnyColor(color)) if(!cs.hasAnyColor(color))
continue; continue;
for(final Card c : game.getColoredCardsInPlay(MagicColor.toLongString(color))) { for(final Card c : game.getColoredCardsInPlay(MagicColor.toLongString(color))) {
if (!res.contains(c) && c.isValid(valid, source.getController(), source, null) && !c.equals(origin)) { if (!res.contains(c) && c.isValid(valid, source.getController(), source, null) && !c.equals(origin)) {
res.add(c); res.add(c);
@@ -344,7 +342,7 @@ public final class CardUtil {
public static Set<String> getReflectableManaColors(final SpellAbility sa) { public static Set<String> getReflectableManaColors(final SpellAbility sa) {
return getReflectableManaColors(sa, sa, Sets.<String>newHashSet(), new CardCollection()); return getReflectableManaColors(sa, sa, Sets.<String>newHashSet(), new CardCollection());
} }
private static Set<String> getReflectableManaColors(final SpellAbility abMana, final SpellAbility sa, private static Set<String> getReflectableManaColors(final SpellAbility abMana, final SpellAbility sa,
Set<String> colors, final CardCollection parents) { Set<String> colors, final CardCollection parents) {
// Here's the problem with reflectable Mana. If more than one is out, // 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 basically need to have a recursive list that send the parents
// so we don't infinite recurse. // so we don't infinite recurse.
final Card card = abMana.getHostCard(); final Card card = abMana.getHostCard();
if (abMana.getApi() != ApiType.ManaReflected) { if (abMana.getApi() != ApiType.ManaReflected) {
return colors; return colors;
} }
@@ -361,7 +359,7 @@ public final class CardUtil {
parents.add(card); parents.add(card);
} }
final String colorOrType = sa.getParam("ColorOrType"); final String colorOrType = sa.getParam("ColorOrType");
// currently Color or Type, Type is colors + colorless // currently Color or Type, Type is colors + colorless
final String validCard = sa.getParam("Valid"); final String validCard = sa.getParam("Valid");
final String reflectProperty = sa.getParam("ReflectProperty"); final String reflectProperty = sa.getParam("ReflectProperty");
@@ -459,7 +457,7 @@ public final class CardUtil {
} }
return colors; return colors;
} }
public static Set<String> canProduce(final int maxChoices, final AbilityManaPart ab, public static Set<String> canProduce(final int maxChoices, final AbilityManaPart ab,
final Set<String> colors) { final Set<String> colors) {
if (ab == null) { if (ab == null) {
@@ -512,7 +510,7 @@ public final class CardUtil {
if (ability.hasParam("MaxTotalTargetCMC")) { if (ability.hasParam("MaxTotalTargetCMC")) {
int totalCMCTargeted = 0; int totalCMCTargeted = 0;
for (final Card c : targeted) { for (final Card c : targeted) {
totalCMCTargeted += c.getCMC(); totalCMCTargeted += c.getCMC();
} }
final List<Card> choicesCopy = Lists.newArrayList(choices); final List<Card> choicesCopy = Lists.newArrayList(choices);

View File

@@ -438,56 +438,32 @@ public class CardView extends GameEntityView {
return false; return false;
} }
public CardView getEquipping() {
return get(TrackableProperty.Equipping);
}
public FCollectionView<CardView> 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<CardView> getEncodedCards() { public FCollectionView<CardView> getEncodedCards() {
return get(TrackableProperty.EncodedCards); return get(TrackableProperty.EncodedCards);
} }
public GameEntityView getEnchanting() { public GameEntityView getAttaching() {
return get(TrackableProperty.Enchanting); return get(TrackableProperty.Attaching);
} }
void updateEnchanting(Card c) { void updateAttaching(Card c) {
set(TrackableProperty.Enchanting, GameEntityView.get(c.getEnchanting())); set(TrackableProperty.Attaching, GameEntityView.get(c.getAttaching()));
} }
public CardView getEnchantingCard() { public CardView getAttachingCard() {
GameEntityView enchanting = getEnchanting(); GameEntityView enchanting = getAttaching();
if (enchanting instanceof CardView) { if (enchanting instanceof CardView) {
return (CardView) enchanting; return (CardView) enchanting;
} }
return null; return null;
} }
public PlayerView getEnchantingPlayer() { public PlayerView getAttachingPlayer() {
GameEntityView enchanting = getEnchanting(); GameEntityView enchanting = getAttaching();
if (enchanting instanceof PlayerView) { if (enchanting instanceof PlayerView) {
return (PlayerView) enchanting; return (PlayerView) enchanting;
} }
return null; return null;
} }
public CardView getFortifying() {
return get(TrackableProperty.Fortifying);
}
public FCollectionView<CardView> getFortifiedBy() {
return get(TrackableProperty.FortifiedBy);
}
public boolean isFortified() {
return getFortifiedBy() != null;
}
public FCollectionView<CardView> getGainControlTargets() { public FCollectionView<CardView> getGainControlTargets() {
return get(TrackableProperty.GainControlTargets); return get(TrackableProperty.GainControlTargets);
} }

View File

@@ -424,15 +424,12 @@ public class Combat {
public Player getDefendingPlayerRelatedTo(final Card source) { public Player getDefendingPlayerRelatedTo(final Card source) {
Card attacker = source; Card attacker = source;
if (source.isAura()) { if (source.isAura() || source.isFortification()) {
attacker = source.getEnchantingCard(); attacker = source.getEnchantingCard();
} }
else if (source.isEquipment()) { else if (source.isEquipment()) {
attacker = source.getEquipping(); attacker = source.getEquipping();
} }
else if (source.isFortification()) {
attacker = source.getFortifying();
}
// return the corresponding defender // return the corresponding defender
return getDefenderPlayerByAttacker(attacker); return getDefenderPlayerByAttacker(attacker);

View File

@@ -83,7 +83,7 @@ public class CostUnattach extends CostPartWithList {
return true; return true;
} }
} else { } else {
if (CardLists.getValidCards(source.getEquippedBy(false), type, payer, source).size() > 0) { if (CardLists.getValidCards(source.getEquippedBy(), type, payer, source).size() > 0) {
return true; return true;
} }
} }
@@ -102,7 +102,7 @@ public class CostUnattach extends CostPartWithList {
return originalEquipment; return originalEquipment;
} }
} else { } else {
List<Card> attachees = CardLists.getValidCards(source.getEquippedBy(false), this.getType(), activator, source); List<Card> attachees = CardLists.getValidCards(source.getEquippedBy(), this.getType(), activator, source);
if (attachees.size() > 0) { if (attachees.size() > 0) {
// Just pick the first one, although maybe give a dialog // Just pick the first one, although maybe give a dialog
return attachees.get(0); return attachees.get(0);
@@ -116,7 +116,7 @@ public class CostUnattach extends CostPartWithList {
*/ */
@Override @Override
protected Card doPayment(SpellAbility ability, Card targetCard) { protected Card doPayment(SpellAbility ability, Card targetCard) {
targetCard.unEquipCard(targetCard.getEquipping()); targetCard.unAttachEntity(targetCard.getAttaching());
return targetCard; return targetCard;
} }

View File

@@ -4,23 +4,15 @@ import forge.game.GameEntity;
import forge.game.card.Card; import forge.game.card.Card;
public class GameEventCardAttachment extends GameEvent { public class GameEventCardAttachment extends GameEvent {
public enum AttachMethod {
Equip,
Fortify,
Enchant;
}
public final Card equipment; 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 newTarget; // can enchant player, I'm ssaving a class to enchants - it could be incorrect.
public final GameEntity oldEntiy; 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.equipment = attachment;
this.newTarget = newEntity; this.newTarget = newEntity;
this.oldEntiy = formerEntity; this.oldEntiy = formerEntity;
this.method = method;
} }
@Override @Override

View File

@@ -272,20 +272,15 @@ public class Untap extends Phase {
} else if (c.hasKeyword(Keyword.PHASING)) { } else if (c.hasKeyword(Keyword.PHASING)) {
// 702.23g If an object would simultaneously phase out directly // 702.23g If an object would simultaneously phase out directly
// and indirectly, it just phases out indirectly. // and indirectly, it just phases out indirectly.
if (c.isAura()) { if (c.isAura() || c.isFortification()) {
final GameEntity ent = c.getEnchanting(); final Card ent = c.getAttachingCard();
if (ent != null && list.contains(ent)) {
if ((ent instanceof Card) && list.contains(ent)) {
continue; continue;
} }
} else if (c.isEquipment() && c.isEquipping()) { } else if (c.isEquipment() && c.isEquipping()) {
if (list.contains(c.getEquipping())) { if (list.contains(c.getEquipping())) {
continue; continue;
} }
} else if (c.isFortification() && c.isFortifying()) {
if (list.contains(c.getFortifying())) {
continue;
}
} }
c.phase(); c.phase();
} }

View File

@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
@@ -67,7 +67,7 @@ import java.util.concurrent.ConcurrentSkipListMap;
* <p> * <p>
* Abstract Player class. * Abstract Player class.
* </p> * </p>
* *
* @author Forge * @author Forge
* @version $Id$ * @version $Id$
*/ */
@@ -109,7 +109,7 @@ public class Player extends GameEntity implements Comparable<Player> {
private CardCollection sacrificedThisTurn = new CardCollection(); private CardCollection sacrificedThisTurn = new CardCollection();
private Map<CounterType, Integer> countersAddedtoPermThisTurn = Maps.newEnumMap(CounterType.class); private Map<CounterType, Integer> countersAddedtoPermThisTurn = Maps.newEnumMap(CounterType.class);
/** A list of tokens not in play, but on their way. /** A list of tokens not in play, but on their way.
* This list is kept in order to not break ETB-replacement * This list is kept in order to not break ETB-replacement
* on tokens. */ * on tokens. */
@@ -293,7 +293,7 @@ public class Player extends GameEntity implements Comparable<Player> {
public final int getOpponentsGreatestLifeTotal() { public final int getOpponentsGreatestLifeTotal() {
return Aggregates.max(getOpponents(), Accessors.FN_GET_LIFE_TOTAL); return Aggregates.max(getOpponents(), Accessors.FN_GET_LIFE_TOTAL);
} }
/** /**
* Get the total number of poison counters amongst this player's opponents. * Get the total number of poison counters amongst this player's opponents.
*/ */
@@ -382,7 +382,7 @@ public class Player extends GameEntity implements Comparable<Player> {
} }
public final boolean gainLife(int lifeGain, final Card source, final SpellAbility sa) { public final boolean gainLife(int lifeGain, final Card source, final SpellAbility sa) {
// Run any applicable replacement effects. // Run any applicable replacement effects.
final Map<String, Object> repParams = Maps.newHashMap(); final Map<String, Object> repParams = Maps.newHashMap();
repParams.put("Event", "GainLife"); repParams.put("Event", "GainLife");
@@ -410,7 +410,7 @@ public class Player extends GameEntity implements Comparable<Player> {
return false; return false;
} }
break; break;
default: default:
return false; return false;
} }
@@ -509,10 +509,10 @@ public class Player extends GameEntity implements Comparable<Player> {
if (!canPayLife(lifePayment)) { if (!canPayLife(lifePayment)) {
return false; return false;
} }
if (lifePayment <= 0) if (lifePayment <= 0)
return true; return true;
// rule 118.8 // rule 118.8
if (life >= lifePayment) { if (life >= lifePayment) {
return (loseLife(lifePayment) > 0); return (loseLife(lifePayment) > 0);
@@ -624,7 +624,7 @@ public class Player extends GameEntity implements Comparable<Player> {
return 0; return 0;
} }
if (hasProtectionFrom(source, true)) { if (hasProtectionFromDamage(source)) {
return 0; return 0;
} }
@@ -826,7 +826,7 @@ public class Player extends GameEntity implements Comparable<Player> {
} }
return num; return num;
} }
public final int getAssignedCombatDamage() { public final int getAssignedCombatDamage() {
int num = 0; int num = 0;
for (final Integer value : assignedCombatDamage.values()) { for (final Integer value : assignedCombatDamage.values()) {
@@ -852,14 +852,14 @@ public class Player extends GameEntity implements Comparable<Player> {
} }
return num; return num;
} }
/** /**
* Get the total damage assigned to this player's opponents this turn. * Get the total damage assigned to this player's opponents this turn.
*/ */
public final int getOpponentsAssignedDamage() { public final int getOpponentsAssignedDamage() {
return Aggregates.sum(getOpponents(), Accessors.FN_GET_ASSIGNED_DAMAGE); return Aggregates.sum(getOpponents(), Accessors.FN_GET_ASSIGNED_DAMAGE);
} }
/** /**
* Get the greatest amount of damage assigned to a single opponent this turn. * Get the greatest amount of damage assigned to a single opponent this turn.
*/ */
@@ -1011,8 +1011,7 @@ public class Player extends GameEntity implements Comparable<Player> {
// if the key already exists - merge entries // if the key already exists - merge entries
if (changedKeywords.containsKey(timestamp)) { if (changedKeywords.containsKey(timestamp)) {
final KeywordsChange cks = changedKeywords.get(timestamp); final KeywordsChange cks = changedKeywords.get(timestamp);
;
changedKeywords.put(timestamp, cks.merge(addKeywords, removeKeywords, changedKeywords.put(timestamp, cks.merge(addKeywords, removeKeywords,
cks.isRemoveAllKeywords(), cks.isRemoveIntrinsicKeywords())); cks.isRemoveAllKeywords(), cks.isRemoveIntrinsicKeywords()));
updateKeywords(); updateKeywords();
@@ -1061,14 +1060,14 @@ public class Player extends GameEntity implements Comparable<Player> {
/** /**
* Remove all keyword changes which grant this {@link Player} the specified * Remove all keyword changes which grant this {@link Player} the specified
* keyword. * keyword.
* @param keyword the keyword to remove. * @param keyword the keyword to remove.
*/ */
public final void removeKeyword(final String keyword) { public final void removeKeyword(final String keyword) {
removeKeyword(keyword, true); removeKeyword(keyword, true);
} }
public final void removeKeyword(final String keyword, final boolean allInstances) { public final void removeKeyword(final String keyword, final boolean allInstances) {
boolean keywordRemoved = false; boolean keywordRemoved = false;
@@ -1134,7 +1133,7 @@ public class Player extends GameEntity implements Comparable<Player> {
} }
return result; return result;
} }
public final StaticAbility addStaticAbility(final Card host, final String s) { public final StaticAbility addStaticAbility(final Card host, final String s) {
PlayerZone com = getZone(ZoneType.Command); PlayerZone com = getZone(ZoneType.Command);
@@ -1195,12 +1194,17 @@ public class Player extends GameEntity implements Comparable<Player> {
return true; return true;
} }
@Override
public boolean hasProtectionFrom(final Card source) { public boolean hasProtectionFromDamage(final Card source) {
return hasProtectionFrom(source, false); 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) { for (String kw : keywords) {
if (kw.startsWith("Protection")) { if (kw.startsWith("Protection")) {
if (kw.startsWith("Protection:")) { // uses isValid if (kw.startsWith("Protection:")) { // uses isValid
@@ -1418,7 +1422,7 @@ public class Player extends GameEntity implements Comparable<Player> {
if (topCardRevealed) { if (topCardRevealed) {
revealed.add(c); revealed.add(c);
} }
if (numDrawnThisTurn == 0) { if (numDrawnThisTurn == 0) {
boolean reveal = false; boolean reveal = false;
@@ -1436,7 +1440,7 @@ public class Player extends GameEntity implements Comparable<Player> {
revealed.remove(c); revealed.remove(c);
} }
} }
} }
setLastDrawnCard(c); setLastDrawnCard(c);
c.setDrawnThisTurn(true); c.setDrawnThisTurn(true);
@@ -1654,7 +1658,7 @@ public class Player extends GameEntity implements Comparable<Player> {
public final CardCollectionView mill(final int n) { public final CardCollectionView mill(final int n) {
return mill(n, ZoneType.Graveyard, false); 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 boolean bottom) {
final CardCollectionView lib = getCardsIn(ZoneType.Library); final CardCollectionView lib = getCardsIn(ZoneType.Library);
final CardCollection milled = new CardCollection(); final CardCollection milled = new CardCollection();
@@ -2204,7 +2208,7 @@ public class Player extends GameEntity implements Comparable<Player> {
public final void resetSacrificedThisTurn() { public final void resetSacrificedThisTurn() {
sacrificedThisTurn.clear(); sacrificedThisTurn.clear();
} }
public final void addCounterToPermThisTurn(final CounterType type, final int x) { public final void addCounterToPermThisTurn(final CounterType type, final int x) {
countersAddedtoPermThisTurn.put(type, getCounterToPermThisTurn(type) + x); countersAddedtoPermThisTurn.put(type, getCounterToPermThisTurn(type) + x);
} }
@@ -2270,7 +2274,7 @@ public class Player extends GameEntity implements Comparable<Player> {
/** /**
* get the Player object or Card (Planeswalker) object that this Player must * get the Player object or Card (Planeswalker) object that this Player must
* attack this combat. * attack this combat.
* *
* @return the Player or Card (Planeswalker) * @return the Player or Card (Planeswalker)
* @since 1.1.01 * @since 1.1.01
*/ */
@@ -2625,7 +2629,7 @@ public class Player extends GameEntity implements Comparable<Player> {
public CardCollectionView getInboundTokens() { public CardCollectionView getInboundTokens() {
return inboundTokens; return inboundTokens;
} }
public void addInboundToken(Card c) { public void addInboundToken(Card c) {
inboundTokens.add(c); inboundTokens.add(c);
} }
@@ -2710,7 +2714,7 @@ public class Player extends GameEntity implements Comparable<Player> {
com.add(Card.fromPaperCard(avatar, this)); com.add(Card.fromPaperCard(avatar, this));
} }
} }
// Schemes // Schemes
CardCollection sd = new CardCollection(); CardCollection sd = new CardCollection();
for (IPaperCard cp : registeredPlayer.getSchemes()) { for (IPaperCard cp : registeredPlayer.getSchemes()) {
@@ -2773,7 +2777,7 @@ public class Player extends GameEntity implements Comparable<Player> {
eff.setSVar("CommanderMoveReplacement", "DB$ ChangeZone | Origin$ Battlefield,Graveyard,Exile,Library,Hand | Destination$ Command | Defined$ ReplacedCard"); 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)) { 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."; 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 { } else {
@@ -2837,7 +2841,7 @@ public class Player extends GameEntity implements Comparable<Player> {
monarchEffect.addType("Effect"); 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."; "ValidPlayer$ You | TriggerDescription$ At the beginning of your end step, draw a card.";
final String drawEff = "AB$Draw | Cost$ 0 | Defined$ You | NumCards$ 1"; final String drawEff = "AB$Draw | Cost$ 0 | Defined$ You | NumCards$ 1";
@@ -2870,11 +2874,11 @@ public class Player extends GameEntity implements Comparable<Player> {
this.updateZoneForView(com); this.updateZoneForView(com);
} }
} }
public boolean hasBlessing() { public boolean hasBlessing() {
return blessingEffect != null; return blessingEffect != null;
} }
public void setBlessing(boolean bless) { public void setBlessing(boolean bless) {
// no need to to change // no need to to change
if ((blessingEffect != null) == bless) { if ((blessingEffect != null) == bless) {
@@ -2882,14 +2886,14 @@ public class Player extends GameEntity implements Comparable<Player> {
} }
final PlayerZone com = getZone(ZoneType.Command); final PlayerZone com = getZone(ZoneType.Command);
if(bless) { if(bless) {
blessingEffect = new Card(-1, game); blessingEffect = new Card(-1, game);
blessingEffect.setOwner(this); blessingEffect.setOwner(this);
blessingEffect.setImageKey("t:blessing"); blessingEffect.setImageKey("t:blessing");
blessingEffect.setName("City's Blessing"); blessingEffect.setName("City's Blessing");
blessingEffect.addType("Effect"); blessingEffect.addType("Effect");
blessingEffect.updateStateForView(); blessingEffect.updateStateForView();
@@ -2898,7 +2902,7 @@ public class Player extends GameEntity implements Comparable<Player> {
com.remove(blessingEffect); com.remove(blessingEffect);
blessingEffect = null; blessingEffect = null;
} }
this.updateZoneForView(com); this.updateZoneForView(com);
} }

View File

@@ -11,7 +11,7 @@ public enum TrackableProperty {
//Shared //Shared
Text(TrackableTypes.StringType), Text(TrackableTypes.StringType),
PreventNextDamage(TrackableTypes.IntegerType), PreventNextDamage(TrackableTypes.IntegerType),
EnchantedBy(TrackableTypes.CardViewCollectionType), AttachedBy(TrackableTypes.CardViewCollectionType),
Counters(TrackableTypes.CounterMapType), Counters(TrackableTypes.CounterMapType),
CurrentPlane(TrackableTypes.StringType), CurrentPlane(TrackableTypes.StringType),
PlanarPlayer(TrackableTypes.PlayerViewType), PlanarPlayer(TrackableTypes.PlayerViewType),
@@ -43,11 +43,7 @@ public enum TrackableProperty {
NamedCard(TrackableTypes.StringType), NamedCard(TrackableTypes.StringType),
PlayerMayLook(TrackableTypes.PlayerViewCollectionType, FreezeMode.IgnoresFreeze), PlayerMayLook(TrackableTypes.PlayerViewCollectionType, FreezeMode.IgnoresFreeze),
PlayerMayLookTemp(TrackableTypes.PlayerViewCollectionType, FreezeMode.IgnoresFreeze), PlayerMayLookTemp(TrackableTypes.PlayerViewCollectionType, FreezeMode.IgnoresFreeze),
Equipping(TrackableTypes.CardViewType), Attaching(TrackableTypes.GameEntityViewType),
EquippedBy(TrackableTypes.CardViewCollectionType),
Enchanting(TrackableTypes.GameEntityViewType),
Fortifying(TrackableTypes.CardViewType),
FortifiedBy(TrackableTypes.CardViewCollectionType),
EncodedCards(TrackableTypes.CardViewCollectionType), EncodedCards(TrackableTypes.CardViewCollectionType),
GainControlTargets(TrackableTypes.CardViewCollectionType), GainControlTargets(TrackableTypes.CardViewCollectionType),
CloneOrigin(TrackableTypes.CardViewType), CloneOrigin(TrackableTypes.CardViewType),

View File

@@ -404,56 +404,24 @@ public class TargetingOverlay {
return; //don't add arcs for cards if card already visualized return; //don't add arcs for cards if card already visualized
} }
final CardView enchanting = c.getEnchantingCard(); final CardView attaching = c.getAttachingCard();
final CardView equipping = c.getEquipping(); final Iterable<CardView> attachedBy = c.getAttachedBy();
final CardView fortifying = c.getFortifying();
final Iterable<CardView> enchantedBy = c.getEnchantedBy();
final Iterable<CardView> equippedBy = c.getEquippedBy();
final Iterable<CardView> fortifiedBy = c.getFortifiedBy();
final CardView paired = c.getPairedWith(); final CardView paired = c.getPairedWith();
if (null != enchanting) { if (null != attaching) {
if (enchanting.getController() != null && !enchanting.getController().equals(c.getController())) { if (attaching.getController() != null && !attaching.getController().equals(c.getController())) {
addArc(endpoints.get(enchanting.getId()), endpoints.get(c.getId()), ArcConnection.Friends); addArc(endpoints.get(attaching.getId()), endpoints.get(c.getId()), ArcConnection.Friends);
cardsVisualized.add(enchanting); cardsVisualized.add(attaching);
} }
} }
if (null != equipping) { if (null != attachedBy) {
if (equipping.getController() != null && !equipping.getController().equals(c.getController())) { for (final CardView enc : attachedBy) {
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 (enc.getController() != null && !enc.getController().equals(c.getController())) { if (enc.getController() != null && !enc.getController().equals(c.getController())) {
addArc(endpoints.get(c.getId()), endpoints.get(enc.getId()), ArcConnection.Friends); addArc(endpoints.get(c.getId()), endpoints.get(enc.getId()), ArcConnection.Friends);
cardsVisualized.add(enc); 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) { if (null != paired) {
addArc(endpoints.get(paired.getId()), endpoints.get(c.getId()), ArcConnection.Friends); addArc(endpoints.get(paired.getId()), endpoints.get(c.getId()), ArcConnection.Friends);
cardsVisualized.add(paired); cardsVisualized.add(paired);

View File

@@ -106,7 +106,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
final CardStack stack = allLands.get(i); final CardStack stack = allLands.get(i);
final CardPanel firstPanel = stack.get(0); final CardPanel firstPanel = stack.get(0);
if (firstPanel.getCard().getCurrentState().getName().equals(state.getName())) { 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 // Put this land to the left of lands with the same name
// and attachments. // and attachments.
insertIndex = i; insertIndex = i;
@@ -114,7 +114,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
} }
if (!panel.getAttachedPanels().isEmpty() if (!panel.getAttachedPanels().isEmpty()
|| !panel.getCard().hasSameCounters(firstPanel.getCard()) || !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, // If this land has attachments or the stack is full,
// put it to the right. // put it to the right.
insertIndex = i + 1; insertIndex = i + 1;
@@ -683,8 +683,8 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
} }
toPanel.getAttachedPanels().clear(); toPanel.getAttachedPanels().clear();
if (card.isEnchanted()) { if (card.isAttached()) {
final Iterable<CardView> enchants = card.getEnchantedBy(); final Iterable<CardView> enchants = card.getAttachedBy();
for (final CardView e : enchants) { for (final CardView e : enchants) {
final CardPanel cardE = getCardPanel(e.getId()); final CardPanel cardE = getCardPanel(e.getId());
if (cardE != null) { if (cardE != null) {
@@ -697,43 +697,9 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
} }
} }
if (card.isEquipped()) {
final Iterable<CardView> 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<CardView> 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; CardPanel attachedToPanel;
if (card.getEnchantingCard() != null) { if (card.getAttachingCard() != null) {
attachedToPanel = getCardPanel(card.getEnchantingCard().getId()); attachedToPanel = getCardPanel(card.getAttachingCard().getId());
}
else if (card.getEquipping() != null) {
attachedToPanel = getCardPanel(card.getEquipping().getId());
}
else if (card.getFortifying() != null) {
attachedToPanel = getCardPanel(card.getFortifying().getId());
} }
else { else {
attachedToPanel = null; attachedToPanel = null;

View File

@@ -115,7 +115,7 @@ public class GameSimulatorTest extends SimulationTestCase {
Card bear = addCard(bearCardName, p); Card bear = addCard(bearCardName, p);
bear.setSickness(false); bear.setSickness(false);
Card cloak = addCard("Whispersilk Cloak", p); Card cloak = addCard("Whispersilk Cloak", p);
cloak.equipCard(bear); cloak.attachEntity(bear);
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p); game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
game.getAction().checkStateEffects(true); game.getAction().checkStateEffects(true);
assertEquals(1, bear.getAmountOfKeyword("Unblockable")); assertEquals(1, bear.getAmountOfKeyword("Unblockable"));
@@ -133,7 +133,7 @@ public class GameSimulatorTest extends SimulationTestCase {
Card bear = addCard(bearCardName, p); Card bear = addCard(bearCardName, p);
bear.setSickness(false); bear.setSickness(false);
Card lifelink = addCard("Lifelink", p); Card lifelink = addCard("Lifelink", p);
lifelink.enchantEntity(bear); lifelink.attachEntity(bear);
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p); game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
game.getAction().checkStateEffects(true); game.getAction().checkStateEffects(true);
assertEquals(1, bear.getAmountOfKeyword("Lifelink")); assertEquals(1, bear.getAmountOfKeyword("Lifelink"));
@@ -661,7 +661,7 @@ public class GameSimulatorTest extends SimulationTestCase {
Card pridemate = addCard(pridemateName, p1); Card pridemate = addCard(pridemateName, p1);
Card indestructibility = addCard(indestructibilityName, p1); Card indestructibility = addCard(indestructibilityName, p1);
indestructibility.enchantEntity(pridemate); indestructibility.attachEntity(pridemate);
Card ignition = addCardToZone(ignitionName, p1, ZoneType.Hand); Card ignition = addCardToZone(ignitionName, p1, ZoneType.Hand);
SpellAbility ignitionSA = ignition.getFirstSpellAbility(); SpellAbility ignitionSA = ignition.getFirstSpellAbility();
@@ -681,6 +681,7 @@ public class GameSimulatorTest extends SimulationTestCase {
// because it was destroyed // because it was destroyed
assertNull(simBrood); assertNull(simBrood);
assertNotNull(simPridemate);
assertEquals(0, simKalitas.getDamage()); assertEquals(0, simKalitas.getDamage());
assertEquals(3, simPridemate.getDamage()); assertEquals(3, simPridemate.getDamage());
@@ -774,7 +775,7 @@ public class GameSimulatorTest extends SimulationTestCase {
Card pridemate = addCard(pridemateName, p1); Card pridemate = addCard(pridemateName, p1);
Card indestructibility = addCard(indestructibilityName, p1); Card indestructibility = addCard(indestructibilityName, p1);
indestructibility.enchantEntity(pridemate); indestructibility.attachEntity(pridemate);
Card ignition = addCardToZone(ignitionName, p1, ZoneType.Hand); Card ignition = addCardToZone(ignitionName, p1, ZoneType.Hand);
SpellAbility ignitionSA = ignition.getFirstSpellAbility(); SpellAbility ignitionSA = ignition.getFirstSpellAbility();
@@ -800,6 +801,7 @@ public class GameSimulatorTest extends SimulationTestCase {
//destoryed because of to much redirected damage //destoryed because of to much redirected damage
assertNull(simPalisade); assertNull(simPalisade);
assertNotNull(simPridemate);
assertEquals(0, simKalitas.getDamage()); assertEquals(0, simKalitas.getDamage());
assertEquals(3, simPridemate.getDamage()); assertEquals(3, simPridemate.getDamage());

View File

@@ -102,22 +102,16 @@ public class GameWrapper {
actualController.getZone( zoneType ).add( actualCard ); actualController.getZone( zoneType ).add( actualCard );
if( card.getTarget() != null ) { if( card.getTarget() != null ) {
Card target = CardSpecificationHandler.INSTANCE.find( game, card.getTarget() ); Card target = CardSpecificationHandler.INSTANCE.find( game, card.getTarget() );
if( actualCard.isEnchantment() ) { if (actualCard.isAttachment()) {
if( target.canBeEnchantedBy( actualCard ) ) { if (target.canBeAttachedBy(actualCard)) {
actualCard.enchantEntity( target ); actualCard.attachEntity(target);
} else { } else {
throw new IllegalStateException( actualCard + " can't enchant " + target ); throw new IllegalStateException( actualCard + " can't attach " + target );
} }
} else if( actualCard.isEquipment() ) { } else {
if( target.canBeEquippedBy( actualCard ) ) { throw new IllegalStateException( "Don't know how to make " + actualCard + " target anything" );
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" );
}
} }

View File

@@ -441,48 +441,28 @@ public class CardDetailUtil {
area.append(")"); area.append(")");
} }
// equipping // attached by
if (card.getEquipping() != null) { if (card.isAttached()) {
if (area.length() != 0) { if (area.length() != 0) {
area.append("\n"); area.append("\n");
} }
area.append("=Equipping "); area.append("=Attached by ");
area.append(card.getEquipping()); area.append(StringUtils.join(card.getAttachedBy(), ", "));
area.append("="); area.append("=");
} }
// equipped by // attaching
if (card.isEquipped()) { if (card.getAttachingCard() != null) {
if (area.length() != 0) { if (area.length() != 0) {
area.append("\n"); area.append("\n");
} }
area.append("=Equipped by "); area.append("*Attaching ").append(card.getAttachingCard()).append("*");
area.append(StringUtils.join(card.getEquippedBy(), ", "));
area.append("=");
} }
if (card.getAttachingPlayer() != null) {
// enchanting
if (card.getEnchantingCard() != null) {
if (area.length() != 0) { if (area.length() != 0) {
area.append("\n"); area.append("\n");
} }
area.append("*Enchanting ").append(card.getEnchantingCard()).append("*"); area.append("*Enchanting ").append(card.getAttachingPlayer()).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("*");
} }
// controlling // controlling

View File

@@ -595,7 +595,7 @@ public class QuestUtil {
rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN)); rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN));
rules.setCanCloneUseTargetsImage(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE)); rules.setCanCloneUseTargetsImage(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE));
TreeSet<GameType> variant = new TreeSet(); final TreeSet<GameType> variant = new TreeSet<>();
if(FModel.getQuest().getDeckConstructionRules() == DeckConstructionRules.Commander){ if(FModel.getQuest().getDeckConstructionRules() == DeckConstructionRules.Commander){
variant.add(GameType.Commander); variant.add(GameType.Commander);
} }