Merge pull request #185 from Card-Forge/attachSpellAbility

canEquip checks for SpellAbility
This commit is contained in:
Anthony Calosa
2022-05-02 06:01:14 +08:00
committed by GitHub
20 changed files with 187 additions and 198 deletions

View File

@@ -1077,7 +1077,7 @@ public class ComputerUtil {
playNow = false; playNow = false;
break; break;
} }
if (!playNow && c.isCreature() && ComputerUtilCombat.canAttackNextTurn(c) && c.canBeAttached(card)) { if (!playNow && c.isCreature() && ComputerUtilCombat.canAttackNextTurn(c) && c.canBeAttached(card, null)) {
playNow = true; playNow = true;
} }
} }

View File

@@ -1135,7 +1135,7 @@ public abstract class GameState {
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.isAttachment()) {
attacher.attachToEntity(attachedTo); attacher.attachToEntity(attachedTo, null, true);
} }
} }
@@ -1146,7 +1146,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.attachToEntity(attachedTo); attacher.attachToEntity(attachedTo, null);
} }
} }

View File

@@ -618,7 +618,7 @@ public class AttachAi extends SpellAbilityAi {
CardCollection preList = new CardCollection(lki); CardCollection preList = new CardCollection(lki);
preList.add(attachSourceLki); preList.add(attachSourceLki);
c.getGame().getAction().checkStaticAbilities(false, Sets.newHashSet(preList), preList); c.getGame().getAction().checkStaticAbilities(false, Sets.newHashSet(preList), preList);
boolean result = lki.canBeAttached(attachSourceLki); boolean result = lki.canBeAttached(attachSourceLki, null);
//reset static abilities //reset static abilities
c.getGame().getAction().checkStaticAbilities(false); c.getGame().getAction().checkStaticAbilities(false);
@@ -1354,7 +1354,7 @@ public class AttachAi extends SpellAbilityAi {
if (tgt == null) { if (tgt == null) {
list = AbilityUtils.getDefinedCards(attachSource, sa.getParam("Defined"), sa); list = AbilityUtils.getDefinedCards(attachSource, sa.getParam("Defined"), sa);
} else { } else {
list = CardLists.filter(CardUtil.getValidCardsToTarget(tgt, sa), CardPredicates.canBeAttached(attachSource)); list = CardLists.filter(CardUtil.getValidCardsToTarget(tgt, sa), CardPredicates.canBeAttached(attachSource, sa));
} }
if (list.isEmpty()) { if (list.isEmpty()) {
@@ -1627,7 +1627,6 @@ public class AttachAi extends SpellAbilityAi {
* @return true, if is useful keyword * @return true, if is useful keyword
*/ */
private static boolean isUsefulCurseKeyword(final String keyword, final Card card, final SpellAbility sa) { private static boolean isUsefulCurseKeyword(final String keyword, final Card card, final SpellAbility sa) {
final Player ai = sa.getActivatingPlayer();
if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) { if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) {
return false; return false;
} }

View File

@@ -39,7 +39,7 @@ public class ZoneExchangeAi extends SpellAbilityAi {
} }
if (type.equals("Aura")) { if (type.equals("Aura")) {
Card c = object1.getEnchantingCard(); Card c = object1.getEnchantingCard();
if (!c.canBeAttached(object2)) { if (!c.canBeAttached(object2, sa)) {
return false; return false;
} }
} }

View File

@@ -178,7 +178,7 @@ public class ForgeScript {
} else if (property.equals("Modular")) { } else if (property.equals("Modular")) {
return sa.hasParam("Modular"); return sa.hasParam("Modular");
} else if (property.equals("Equip")) { } else if (property.equals("Equip")) {
return sa.hasParam("Equip"); return sa.isEquip();
} else if (property.equals("Boast")) { } else if (property.equals("Boast")) {
return sa.isBoast(); return sa.isBoast();
} else if (property.equals("Mutate")) { } else if (property.equals("Mutate")) {

View File

@@ -162,13 +162,13 @@ public class GameAction {
// need to check before it enters // need to check before it enters
if (c.isAura() && !c.isAttachedToEntity() && toBattlefield && (zoneFrom == null || !zoneFrom.is(ZoneType.Stack))) { if (c.isAura() && !c.isAttachedToEntity() && toBattlefield && (zoneFrom == null || !zoneFrom.is(ZoneType.Stack))) {
boolean found = false; boolean found = false;
if (Iterables.any(game.getPlayers(), PlayerPredicates.canBeAttached(c))) { if (Iterables.any(game.getPlayers(), PlayerPredicates.canBeAttached(c, null))) {
found = true; found = true;
} }
else if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateBattlefield), CardPredicates.canBeAttached(c))) { else if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateBattlefield), CardPredicates.canBeAttached(c, null))) {
found = true; found = true;
} }
else if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateGraveyard), CardPredicates.canBeAttached(c))) { else if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateGraveyard), CardPredicates.canBeAttached(c, null))) {
found = true; found = true;
} }
if (!found) { if (!found) {
@@ -398,13 +398,13 @@ public class GameAction {
if (copied.isAura() && !copied.isAttachedToEntity() && toBattlefield) { if (copied.isAura() && !copied.isAttachedToEntity() && toBattlefield) {
if (zoneFrom != null && zoneFrom.is(ZoneType.Stack) && game.getStack().isResolving(c)) { if (zoneFrom != null && zoneFrom.is(ZoneType.Stack) && game.getStack().isResolving(c)) {
boolean found = false; boolean found = false;
if (Iterables.any(game.getPlayers(), PlayerPredicates.canBeAttached(copied))) { if (Iterables.any(game.getPlayers(), PlayerPredicates.canBeAttached(copied, null))) {
found = true; found = true;
} }
if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateBattlefield), CardPredicates.canBeAttached(copied))) { if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateBattlefield), CardPredicates.canBeAttached(copied, null))) {
found = true; found = true;
} }
if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateGraveyard), CardPredicates.canBeAttached(copied))) { if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateGraveyard), CardPredicates.canBeAttached(copied, null))) {
found = true; found = true;
} }
if (!found) { if (!found) {
@@ -1515,7 +1515,7 @@ public class GameAction {
if (c.isAttachedToEntity()) { if (c.isAttachedToEntity()) {
final GameEntity ge = c.getEntityAttachedTo(); final GameEntity ge = c.getEntityAttachedTo();
if (!ge.canBeAttached(c, true)) { if (!ge.canBeAttached(c, null, true)) {
unAttachList.add(c); unAttachList.add(c);
checkAgain = true; checkAgain = true;
} }
@@ -2381,7 +2381,7 @@ public class GameAction {
final Player pa = p.getController().chooseSingleEntityForEffect(players, aura, final Player pa = p.getController().chooseSingleEntityForEffect(players, aura,
Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", CardTranslation.getTranslatedName(source.getName())), null); Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", CardTranslation.getTranslatedName(source.getName())), null);
if (pa != null) { if (pa != null) {
source.attachToEntity(pa); source.attachToEntity(pa, null);
return true; return true;
} }
} else { } else {
@@ -2408,7 +2408,7 @@ public class GameAction {
final Card o = p.getController().chooseSingleEntityForEffect(list, aura, final Card o = p.getController().chooseSingleEntityForEffect(list, aura,
Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(source.getName())), null); Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(source.getName())), null);
if (o != null) { if (o != null) {
source.attachToEntity(game.getCardState(o), true); source.attachToEntity(game.getCardState(o), null, true);
return true; return true;
} }
} }

View File

@@ -341,7 +341,7 @@ public final class GameActionUtil {
alternatives.add(newSA); alternatives.add(newSA);
} }
} }
if (sa.hasParam("Equip") && activator.hasKeyword("You may pay 0 rather than pay equip costs.")) { if (sa.isEquip() && activator.hasKeyword("You may pay 0 rather than pay equip costs.")) {
for (final KeywordInterface inst : source.getKeywords()) { for (final KeywordInterface inst : source.getKeywords()) {
// need to find the correct Keyword from which this Ability is from // need to find the correct Keyword from which this Ability is from
if (!inst.getAbilities().contains(sa)) { if (!inst.getAbilities().contains(sa)) {

View File

@@ -212,10 +212,10 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
} }
} }
public boolean canBeAttached(final Card attach) { public boolean canBeAttached(final Card attach, SpellAbility sa) {
return canBeAttached(attach, false); return canBeAttached(attach, sa, false);
} }
public boolean canBeAttached(final Card attach, boolean checkSBA) { public boolean canBeAttached(final Card attach, SpellAbility sa, boolean checkSBA) {
// master mode // master mode
if (!attach.isAttachment() || (attach.isCreature() && !attach.hasKeyword(Keyword.RECONFIGURE)) if (!attach.isAttachment() || (attach.isCreature() && !attach.hasKeyword(Keyword.RECONFIGURE))
|| equals(attach)) { || equals(attach)) {
@@ -226,7 +226,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
if (attach.isAura() && !canBeEnchantedBy(attach)) { if (attach.isAura() && !canBeEnchantedBy(attach)) {
return false; return false;
} }
if (attach.isEquipment() && !canBeEquippedBy(attach)) { if (attach.isEquipment() && !canBeEquippedBy(attach, sa)) {
return false; return false;
} }
if (attach.isFortification() && !canBeFortifiedBy(attach)) { if (attach.isFortification() && !canBeFortifiedBy(attach)) {
@@ -242,7 +242,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
return true; return true;
} }
protected boolean canBeEquippedBy(final Card aura) { protected boolean canBeEquippedBy(final Card aura, SpellAbility sa) {
/** /**
* Equip only to Creatures which are cards * Equip only to Creatures which are cards
*/ */

View File

@@ -84,7 +84,7 @@ public class AttachEffect extends SpellAbilityEffect {
choices = AbilityUtils.getDefinedEntities(source, sa.getParam("PlayerChoices"), sa); choices = AbilityUtils.getDefinedEntities(source, sa.getParam("PlayerChoices"), sa);
for (final Card attachment : attachments) { for (final Card attachment : attachments) {
for (GameEntity g : choices) { for (GameEntity g : choices) {
if (!g.canBeAttached(attachment)) { if (!g.canBeAttached(attachment, sa)) {
choices.remove(g); choices.remove(g);
} }
} }
@@ -100,7 +100,7 @@ public class AttachEffect extends SpellAbilityEffect {
if (e != null) if (e != null)
cardChoices.remove(e); cardChoices.remove(e);
} }
cardChoices = CardLists.filter(cardChoices, CardPredicates.canBeAttached(attachment)); cardChoices = CardLists.filter(cardChoices, CardPredicates.canBeAttached(attachment, sa));
} }
choices.addAll(cardChoices); choices.addAll(cardChoices);
} }
@@ -138,7 +138,7 @@ public class AttachEffect extends SpellAbilityEffect {
// TODO add params for message // TODO add params for message
continue; continue;
attachment.attachToEntity(attachTo); attachment.attachToEntity(attachTo, sa);
if (sa.hasParam("RememberAttached") && attachment.isAttachedToEntity(attachTo)) { if (sa.hasParam("RememberAttached") && attachment.isAttachedToEntity(attachTo)) {
source.addRemembered(attachment); source.addRemembered(attachment);
} }

View File

@@ -615,7 +615,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
// only valid choices are when they could be attached // only valid choices are when they could be attached
// TODO for multiple Auras entering attached this way, need to use LKI info // TODO for multiple Auras entering attached this way, need to use LKI info
if (!list.isEmpty()) { if (!list.isEmpty()) {
list = CardLists.filter(list, CardPredicates.canBeAttached(gameCard)); list = CardLists.filter(list, CardPredicates.canBeAttached(gameCard, sa));
} }
if (!list.isEmpty()) { if (!list.isEmpty()) {
Map<String, Object> params = Maps.newHashMap(); Map<String, Object> params = Maps.newHashMap();
@@ -624,7 +624,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
// TODO can't attach later or moveToPlay would attach indirectly // TODO can't attach later or moveToPlay would attach indirectly
// bypass canBeAttached to skip Protection checks when trying to attach multiple auras that would grant protection // bypass canBeAttached to skip Protection checks when trying to attach multiple auras that would grant protection
gameCard.attachToEntity(game.getCardState(attachedTo), true); gameCard.attachToEntity(game.getCardState(attachedTo), sa, true);
} 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;
} }
@@ -636,7 +636,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
Map<String, Object> params = Maps.newHashMap(); Map<String, Object> params = Maps.newHashMap();
params.put("Attach", gameCard); params.put("Attach", gameCard);
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", gameCard.toString()), params); Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", gameCard.toString()), params);
gameCard.attachToEntity(attachedTo); gameCard.attachToEntity(attachedTo, sa);
} }
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;
@@ -708,7 +708,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
Map<String, Object> params = Maps.newHashMap(); Map<String, Object> params = Maps.newHashMap();
params.put("Attach", gameCard); params.put("Attach", gameCard);
Card attachedTo = chooser.getController().chooseSingleEntityForEffect(list, sa, title, params); Card attachedTo = chooser.getController().chooseSingleEntityForEffect(list, sa, title, params);
movedCard.attachToEntity(attachedTo); movedCard.attachToEntity(attachedTo, sa);
} }
} }
} else { } else {
@@ -1287,7 +1287,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
// only valid choices are when they could be attached // only valid choices are when they could be attached
// TODO for multiple Auras entering attached this way, need to use LKI info // TODO for multiple Auras entering attached this way, need to use LKI info
if (!list.isEmpty()) { if (!list.isEmpty()) {
list = CardLists.filter(list, CardPredicates.canBeAttached(c)); list = CardLists.filter(list, CardPredicates.canBeAttached(c, sa));
} }
if (!list.isEmpty()) { if (!list.isEmpty()) {
String title = Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName())); String title = Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName()));
@@ -1297,7 +1297,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
// TODO can't attach later or moveToPlay would attach indirectly // TODO can't attach later or moveToPlay would attach indirectly
// bypass canBeAttached to skip Protection checks when trying to attach multiple auras that would grant protection // bypass canBeAttached to skip Protection checks when trying to attach multiple auras that would grant protection
c.attachToEntity(game.getCardState(attachedTo), true); c.attachToEntity(game.getCardState(attachedTo), sa, true);
} }
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;
@@ -1311,7 +1311,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
Map<String, Object> params = Maps.newHashMap(); Map<String, Object> params = Maps.newHashMap();
params.put("Attach", c); params.put("Attach", c);
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, title, params); Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, title, params);
c.attachToEntity(attachedTo); c.attachToEntity(attachedTo, sa);
} }
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;
@@ -1341,7 +1341,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
Map<String, Object> params = Maps.newHashMap(); Map<String, Object> params = Maps.newHashMap();
params.put("Attach", movedCard); params.put("Attach", movedCard);
Card attachedTo = decider.getController().chooseSingleEntityForEffect(list, sa, title, params); Card attachedTo = decider.getController().chooseSingleEntityForEffect(list, sa, title, params);
movedCard.attachToEntity(attachedTo); movedCard.attachToEntity(attachedTo, sa);
} }
} }
} }

View File

@@ -238,7 +238,7 @@ public abstract class TokenEffectBase extends SpellAbilityEffect {
boolean canAttach = lki.isAttachment(); boolean canAttach = lki.isAttachment();
if (canAttach && !ge.canBeAttached(lki)) { if (canAttach && !ge.canBeAttached(lki, sa)) {
canAttach = false; canAttach = false;
} }
@@ -254,7 +254,7 @@ public abstract class TokenEffectBase extends SpellAbilityEffect {
return false; return false;
} }
tok.attachToEntity(ge); tok.attachToEntity(ge, sa);
return true; return true;
} }
// not a GameEntity, cant be attach // not a GameEntity, cant be attach

View File

@@ -81,14 +81,14 @@ public class ZoneExchangeEffect extends SpellAbilityEffect {
Card c = null; Card c = null;
if (type != null && type.equals("Aura") && object1.getEnchantingCard() != null) { if (type != null && type.equals("Aura") && object1.getEnchantingCard() != null) {
c = object1.getEnchantingCard(); c = object1.getEnchantingCard();
if (!c.canBeAttached(object2)) { if (!c.canBeAttached(object2, sa)) {
return; return;
} }
} }
// Enchant first // Enchant first
if (c != null) { if (c != null) {
object1.unattachFromEntity(c); object1.unattachFromEntity(c);
object2.attachToEntity(c); object2.attachToEntity(c, sa);
} }
Map<AbilityKey, Object> moveParams = AbilityKey.newMap(); Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield());

View File

@@ -3440,22 +3440,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
return this.isAttachedToEntity(); return this.isAttachedToEntity();
} }
public final void equipCard(final Card c) {
if (!isEquipment()) {
return;
}
this.attachToEntity(c);
}
public final void fortifyCard(final Card c) {
if (!isFortification()) {
return;
}
this.attachToEntity(c);
}
public final void unEquipCard(final Card c) { // equipment.unEquipCard(equippedCard); public final void unEquipCard(final Card c) { // equipment.unEquipCard(equippedCard);
this.unattachFromEntity(c); this.unattachFromEntity(c);
} }
@@ -3511,11 +3495,11 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
return getEnchantingCard() != null; return getEnchantingCard() != null;
} }
public final void attachToEntity(final GameEntity entity) { public final void attachToEntity(final GameEntity entity, SpellAbility sa) {
attachToEntity(entity, false); attachToEntity(entity, sa, false);
} }
public final void attachToEntity(final GameEntity entity, boolean overwrite) { public final void attachToEntity(final GameEntity entity, SpellAbility sa, boolean overwrite) {
if (!overwrite && !entity.canBeAttached(this)) { if (!overwrite && !entity.canBeAttached(this, sa)) {
return; return;
} }
@@ -6082,17 +6066,14 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
@Override @Override
protected final boolean canBeEquippedBy(final Card equip) { protected final boolean canBeEquippedBy(final Card equip, SpellAbility sa) {
if (isCreature() && isInPlay()) { if (!isInPlay()) {
return true; return false;
} else if (isPlaneswalker() && isInPlay()) {
for (KeywordInterface inst : equip.getKeywords(Keyword.EQUIP)) {
if (inst.getOriginal().contains("planeswalker")) {
return true;
}
}
} }
return false; if (sa != null && sa.isEquip()) {
return isValid(sa.getTargetRestrictions().getValidTgts(), sa.getActivatingPlayer(), equip, sa);
}
return isCreature();
} }
@Override @Override
@@ -6104,13 +6085,13 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
* @see forge.game.GameEntity#canBeAttached(forge.game.card.Card, boolean) * @see forge.game.GameEntity#canBeAttached(forge.game.card.Card, boolean)
*/ */
@Override @Override
public boolean canBeAttached(Card attach, boolean checkSBA) { public boolean canBeAttached(Card attach, SpellAbility sa, boolean checkSBA) {
// phase check there // phase check there
if (isPhasedOut() && !attach.isPhasedOut()) { if (isPhasedOut() && !attach.isPhasedOut()) {
return false; return false;
} }
return super.canBeAttached(attach, checkSBA); return super.canBeAttached(attach, sa, checkSBA);
} }
public FCollectionView<ReplacementEffect> getReplacementEffects() { public FCollectionView<ReplacementEffect> getReplacementEffects() {

View File

@@ -251,11 +251,11 @@ public final class CardPredicates {
}; };
} }
public static final Predicate<Card> canBeAttached(final Card aura) { public static final Predicate<Card> canBeAttached(final Card aura, final SpellAbility sa) {
return new Predicate<Card>() { return new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
return c.canBeAttached(aura); return c.canBeAttached(aura, sa);
} }
}; };
} }

View File

@@ -477,20 +477,20 @@ public class CardProperty {
} else if (property.startsWith("CanEnchant")) { } else if (property.startsWith("CanEnchant")) {
final String restriction = property.substring(10); final String restriction = property.substring(10);
if (restriction.equals("EquippedBy")) { if (restriction.equals("EquippedBy")) {
if (!source.getEquipping().canBeAttached(card)) return false; if (!source.getEquipping().canBeAttached(card, null)) return false;
} }
if (restriction.equals("Remembered")) { if (restriction.equals("Remembered")) {
for (final Object rem : source.getRemembered()) { for (final Object rem : source.getRemembered()) {
if (!(rem instanceof Card) || !((Card) rem).canBeAttached(card)) if (!(rem instanceof Card) || !((Card) rem).canBeAttached(card, null))
return false; return false;
} }
} else if (restriction.equals("Source")) { } else if (restriction.equals("Source")) {
if (!source.canBeAttached(card)) return false; if (!source.canBeAttached(card, null)) return false;
} }
} else if (property.startsWith("CanBeEnchantedBy")) { } else if (property.startsWith("CanBeEnchantedBy")) {
if (property.substring(16).equals("Targeted")) { if (property.substring(16).equals("Targeted")) {
for (final Card c : AbilityUtils.getDefinedCards(source, "Targeted", spellAbility)) { for (final Card c : AbilityUtils.getDefinedCards(source, "Targeted", spellAbility)) {
if (!card.canBeAttached(c)) { if (!card.canBeAttached(c, null)) {
return false; return false;
} }
} }
@@ -498,13 +498,13 @@ public class CardProperty {
for (final Object rem : source.getRemembered()) { for (final Object rem : source.getRemembered()) {
if (rem instanceof Card) { if (rem instanceof Card) {
final Card c = (Card) rem; final Card c = (Card) rem;
if (!card.canBeAttached(c)) { if (!card.canBeAttached(c, null)) {
return false; return false;
} }
} }
} }
} else { } else {
if (!card.canBeAttached(source)) { if (!card.canBeAttached(source, null)) {
return false; return false;
} }
} }
@@ -530,7 +530,7 @@ public class CardProperty {
return false; return false;
} }
} else if (property.startsWith("CanBeAttachedBy")) { } else if (property.startsWith("CanBeAttachedBy")) {
if (!card.canBeAttached(source)) { if (!card.canBeAttached(source, null)) {
return false; return false;
} }
} else if (property.startsWith("Fortified")) { } else if (property.startsWith("Fortified")) {

View File

@@ -121,11 +121,11 @@ public final class PlayerPredicates {
}; };
} }
public static final Predicate<Player> canBeAttached(final Card aura) { public static final Predicate<Player> canBeAttached(final Card aura, SpellAbility sa) {
return new Predicate<Player>() { return new Predicate<Player>() {
@Override @Override
public boolean apply(final Player p) { public boolean apply(final Player p) {
return p.canBeAttached(aura); return p.canBeAttached(aura, sa);
} }
}; };
} }

View File

@@ -1035,6 +1035,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
return isAlternativeCost(AlternativeCost.Outlast); return isAlternativeCost(AlternativeCost.Outlast);
} }
public boolean isEquip() {
return hasParam("Equip");
}
public boolean isBlessing() { public boolean isBlessing() {
return blessing; return blessing;
} }

View File

@@ -328,7 +328,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
} }
// Log number of Equips // Log number of Equips
if (sp.hasParam("Equip")) { if (sp.isEquip()) {
activator.addEquipped(); activator.addEquipped();
} }

View File

@@ -125,7 +125,7 @@ public class GameSimulationTest extends SimulationTest {
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.attachToEntity(bear); cloak.attachToEntity(bear, null);
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p); game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
game.getAction().checkStateEffects(true); game.getAction().checkStateEffects(true);
AssertJUnit.assertEquals(1, bear.getAmountOfKeyword("Unblockable")); AssertJUnit.assertEquals(1, bear.getAmountOfKeyword("Unblockable"));
@@ -144,7 +144,7 @@ public class GameSimulationTest extends SimulationTest {
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.attachToEntity(bear); lifelink.attachToEntity(bear, null);
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p); game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
game.getAction().checkStateEffects(true); game.getAction().checkStateEffects(true);
AssertJUnit.assertEquals(1, bear.getAmountOfKeyword("Lifelink")); AssertJUnit.assertEquals(1, bear.getAmountOfKeyword("Lifelink"));
@@ -700,7 +700,7 @@ public class GameSimulationTest extends SimulationTest {
Card pridemate = addCard(pridemateName, p1); Card pridemate = addCard(pridemateName, p1);
Card indestructibility = addCard(indestructibilityName, p1); Card indestructibility = addCard(indestructibilityName, p1);
indestructibility.attachToEntity(pridemate); indestructibility.attachToEntity(pridemate, null);
Card ignition = addCardToZone(ignitionName, p1, ZoneType.Hand); Card ignition = addCardToZone(ignitionName, p1, ZoneType.Hand);
SpellAbility ignitionSA = ignition.getFirstSpellAbility(); SpellAbility ignitionSA = ignition.getFirstSpellAbility();
@@ -816,7 +816,7 @@ public class GameSimulationTest extends SimulationTest {
Card pridemate = addCard(pridemateName, p1); Card pridemate = addCard(pridemateName, p1);
Card indestructibility = addCard(indestructibilityName, p1); Card indestructibility = addCard(indestructibilityName, p1);
indestructibility.attachToEntity(pridemate); indestructibility.attachToEntity(pridemate, null);
Card ignition = addCardToZone(ignitionName, p1, ZoneType.Hand); Card ignition = addCardToZone(ignitionName, p1, ZoneType.Hand);
SpellAbility ignitionSA = ignition.getFirstSpellAbility(); SpellAbility ignitionSA = ignition.getFirstSpellAbility();
@@ -1425,7 +1425,7 @@ public class GameSimulationTest extends SimulationTest {
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.attachToEntity(bear); lifelink.attachToEntity(bear, null);
AssertJUnit.assertTrue(bear.isEnchanted()); AssertJUnit.assertTrue(bear.isEnchanted());
AssertJUnit.assertTrue(bear.hasCardAttachment(lifelink)); AssertJUnit.assertTrue(bear.hasCardAttachment(lifelink));
@@ -1466,7 +1466,7 @@ public class GameSimulationTest extends SimulationTest {
final String curseName = "Cruel Reality"; final String curseName = "Cruel Reality";
Card curse = addCard(curseName, p); Card curse = addCard(curseName, p);
curse.attachToEntity(p); curse.attachToEntity(p, null);
game.getAction().checkStateEffects(true); game.getAction().checkStateEffects(true);
AssertJUnit.assertTrue(p.isEnchanted()); AssertJUnit.assertTrue(p.isEnchanted());
AssertJUnit.assertTrue(p.hasCardAttachment(curse)); AssertJUnit.assertTrue(p.hasCardAttachment(curse));
@@ -1500,7 +1500,7 @@ public class GameSimulationTest extends SimulationTest {
Card mountain = addCardToZone("Mountain", p, ZoneType.Battlefield); Card mountain = addCardToZone("Mountain", p, ZoneType.Battlefield);
Card fortification = addCardToZone("Darksteel Garrison", p, ZoneType.Battlefield); Card fortification = addCardToZone("Darksteel Garrison", p, ZoneType.Battlefield);
fortification.attachToEntity(mountain); fortification.attachToEntity(mountain, null);
game.getAction().checkStateEffects(true); game.getAction().checkStateEffects(true);
AssertJUnit.assertTrue(fortification.isFortification()); AssertJUnit.assertTrue(fortification.isFortification());
@@ -1535,7 +1535,7 @@ public class GameSimulationTest extends SimulationTest {
Card dryad = addCardToZone("Dryad Arbor", p, ZoneType.Battlefield); Card dryad = addCardToZone("Dryad Arbor", p, ZoneType.Battlefield);
Card fortification = addCardToZone("Darksteel Garrison", p, ZoneType.Battlefield); Card fortification = addCardToZone("Darksteel Garrison", p, ZoneType.Battlefield);
fortification.attachToEntity(dryad); fortification.attachToEntity(dryad, null);
game.getAction().checkStateEffects(true); game.getAction().checkStateEffects(true);
AssertJUnit.assertTrue(dryad.isFortified()); AssertJUnit.assertTrue(dryad.isFortified());
@@ -1693,7 +1693,7 @@ public class GameSimulationTest extends SimulationTest {
String indestructibilityName = "Indestructibility"; String indestructibilityName = "Indestructibility";
Card indestructibility = addCard(indestructibilityName, p); Card indestructibility = addCard(indestructibilityName, p);
indestructibility.attachToEntity(teysa); indestructibility.attachToEntity(teysa, null);
// update Indestructible state // update Indestructible state
game.getAction().checkStateEffects(true); game.getAction().checkStateEffects(true);
@@ -1732,7 +1732,7 @@ public class GameSimulationTest extends SimulationTest {
String indestructibilityName = "Indestructibility"; String indestructibilityName = "Indestructibility";
Card indestructibility = addCard(indestructibilityName, p); Card indestructibility = addCard(indestructibilityName, p);
indestructibility.attachToEntity(gitrog); indestructibility.attachToEntity(gitrog, null);
// update Indestructible state // update Indestructible state
game.getAction().checkStateEffects(true); game.getAction().checkStateEffects(true);
@@ -2305,7 +2305,7 @@ public class GameSimulationTest extends SimulationTest {
addCard(capridorName, p1); addCard(capridorName, p1);
Card pridemate = addCard(pridemateName, p1); Card pridemate = addCard(pridemateName, p1);
Card indestructibility = addCard(indestructibilityName, p1); Card indestructibility = addCard(indestructibilityName, p1);
indestructibility.attachToEntity(pridemate); indestructibility.attachToEntity(pridemate, null);
addCard(bearName, p1); addCard(bearName, p1);
Card alphaBrawl = addCardToZone(alphaBrawlName, p2, ZoneType.Hand); Card alphaBrawl = addCardToZone(alphaBrawlName, p2, ZoneType.Hand);

View File

@@ -41,11 +41,13 @@ public class GameWrapper {
private final GameLog gameLog; private final GameLog gameLog;
private Game game; private Game game;
public GameWrapper( GameStateSpecification initialGameStateSpecification, PlayerActions playerActions ) { public GameWrapper(GameStateSpecification initialGameStateSpecification, PlayerActions playerActions) {
this( initialGameStateSpecification, playerActions, Arrays.asList( PlayerSpecification.PLAYER_1, PlayerSpecification.PLAYER_2 ) ); this(initialGameStateSpecification, playerActions,
Arrays.asList(PlayerSpecification.PLAYER_1, PlayerSpecification.PLAYER_2));
} }
public GameWrapper( GameStateSpecification initialGameStateSpecification, PlayerActions playerActions, List<PlayerSpecification> players ) { public GameWrapper(GameStateSpecification initialGameStateSpecification, PlayerActions playerActions,
List<PlayerSpecification> players) {
this.initialGameStateSpecification = initialGameStateSpecification; this.initialGameStateSpecification = initialGameStateSpecification;
this.playerActions = playerActions; this.playerActions = playerActions;
this.players = players; this.players = players;
@@ -54,106 +56,109 @@ public class GameWrapper {
} }
/** /**
* This start method attempts to start from the specified game state. * This start method attempts to start from the specified game state. That
* That requires a bit of ugly hackery, possibly breaking after harmless refactorings or improvements to real code, * requires a bit of ugly hackery, possibly breaking after harmless refactorings
* and always casting doubt upon the veracity of test results. * or improvements to real code, and always casting doubt upon the veracity of
* To somewhat minimize those concerns, starting with stuff on the stack and/or combat in progress is pretty much out of the question. * test results. To somewhat minimize those concerns, starting with stuff on the
* Note that if you use this option, regular startup is ignored (using player deck, shuffling, drawing hand, mulligan, ...) * stack and/or combat in progress is pretty much out of the question. Note that
* if you use this option, regular startup is ignored (using player deck,
* shuffling, drawing hand, mulligan, ...)
*/ */
public void runGame() { public void runGame() {
List<RegisteredPlayer> registeredPlayers = new ArrayList<>(); List<RegisteredPlayer> registeredPlayers = new ArrayList<>();
for( PlayerSpecification player : players ) { for (PlayerSpecification player : players) {
RegisteredPlayer registeredPlayer = new RegisteredPlayer(new Deck(player.getName())); RegisteredPlayer registeredPlayer = new RegisteredPlayer(new Deck(player.getName()));
LobbyPlayerForTests lobbyPlayer = new LobbyPlayerForTests( player.getName(), playerActions ); LobbyPlayerForTests lobbyPlayer = new LobbyPlayerForTests(player.getName(), playerActions);
registeredPlayer.setPlayer( lobbyPlayer ); registeredPlayer.setPlayer(lobbyPlayer);
registeredPlayers.add( registeredPlayer ); registeredPlayers.add(registeredPlayer);
} }
GameRules rules = new GameRules(GameType.Constructed); GameRules rules = new GameRules(GameType.Constructed);
rules.setPlayForAnte(FModel.getPreferences().getPrefBoolean(FPref.UI_ANTE)); rules.setPlayForAnte(FModel.getPreferences().getPrefBoolean(FPref.UI_ANTE));
rules.setMatchAnteRarity(FModel.getPreferences().getPrefBoolean(FPref.UI_ANTE_MATCH_RARITY)); rules.setMatchAnteRarity(FModel.getPreferences().getPrefBoolean(FPref.UI_ANTE_MATCH_RARITY));
rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN)); rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN));
rules.setUseGrayText(FModel.getPreferences().getPrefBoolean(FPref.UI_GRAY_INACTIVE_TEXT)); rules.setUseGrayText(FModel.getPreferences().getPrefBoolean(FPref.UI_GRAY_INACTIVE_TEXT));
Match match = new Match(rules, registeredPlayers, "Test"); Match match = new Match(rules, registeredPlayers, "Test");
game = match.createGame(); game = match.createGame();
//GameNew.newGame( game, false, false ) does a bit of internal setup, then prepares libraries etc // GameNew.newGame( game, false, false ) does a bit of internal setup, then
// prepares libraries etc
Trigger.resetIDs(); Trigger.resetIDs();
TriggerHandler trigHandler = game.getTriggerHandler(); TriggerHandler trigHandler = game.getTriggerHandler();
trigHandler.clearDelayedTrigger(); trigHandler.clearDelayedTrigger();
//instead of preparing libraries the normal way, we'll distribute cards across the specified zones // instead of preparing libraries the normal way, we'll distribute cards across
if( initialGameStateSpecification != null && !initialGameStateSpecification.getCards().isEmpty() ) { // the specified zones
for( CardSpecification card : initialGameStateSpecification.getCards() ) { if (initialGameStateSpecification != null && !initialGameStateSpecification.getCards().isEmpty()) {
PaperCard paperCard = CardDatabaseHelper.getCard( card.getName() ); for (CardSpecification card : initialGameStateSpecification.getCards()) {
PaperCard paperCard = CardDatabaseHelper.getCard(card.getName());
PlayerSpecification owner = card.getOwner(); PlayerSpecification owner = card.getOwner();
PlayerSpecification controller = card.getController(); PlayerSpecification controller = card.getController();
if( owner == null ) { if (owner == null) {
owner = controller; owner = controller;
} }
if( controller == null ) { if (controller == null) {
controller = owner; controller = owner;
} }
if( owner == null ) { if (owner == null) {
throw new IllegalStateException( "Cards must specify owner for game state specification" ); throw new IllegalStateException("Cards must specify owner for game state specification");
} }
Player actualOwner = PlayerSpecificationHandler.INSTANCE.find( game, owner ); Player actualOwner = PlayerSpecificationHandler.INSTANCE.find(game, owner);
if( controller == null ) { if (controller == null) {
throw new IllegalStateException( "Cards must specify controller for game state specification" ); throw new IllegalStateException("Cards must specify controller for game state specification");
} }
Player actualController = PlayerSpecificationHandler.INSTANCE.find( game, controller ); Player actualController = PlayerSpecificationHandler.INSTANCE.find(game, controller);
ZoneType zoneType = card.getZoneType(); ZoneType zoneType = card.getZoneType();
if( zoneType == null ) { if (zoneType == null) {
throw new IllegalStateException( "Cards must specify zone for game state specification" ); throw new IllegalStateException("Cards must specify zone for game state specification");
} }
Card actualCard = Card.fromPaperCard( paperCard, actualOwner ); Card actualCard = Card.fromPaperCard(paperCard, actualOwner);
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.isAttachment()) { if (actualCard.isAttachment()) {
if (target.canBeAttached(actualCard)) { if (target.canBeAttached(actualCard, null)) {
actualCard.attachToEntity(target); actualCard.attachToEntity(target, null);
} else { } else {
throw new IllegalStateException( actualCard + " can't attach to " + target ); throw new IllegalStateException(actualCard + " can't attach to " + target);
} }
} else { } else {
throw new IllegalStateException( "Don't know how to make " + actualCard + " target anything" ); throw new IllegalStateException("Don't know how to make " + actualCard + " target anything");
} }
} }
}
}
// may need to tweak players a bit too
if (initialGameStateSpecification != null && !initialGameStateSpecification.getPlayerFacts().isEmpty()) {
for (final PlayerSpecification playerFact : initialGameStateSpecification.getPlayerFacts()) {
final PlayerSpecification basePlayerSpec = new PlayerSpecificationBuilder(playerFact.getName()).build();
Player player = PlayerSpecificationHandler.INSTANCE.find(game, basePlayerSpec);
} if (playerFact.getLife() != null) {
} player.setLife(playerFact.getLife(), null);
}
//may need to tweak players a bit too if (playerFact.getPoison() != null) {
if( initialGameStateSpecification != null && !initialGameStateSpecification.getPlayerFacts().isEmpty() ) { player.setPoisonCounters(playerFact.getPoison(), null);
for( final PlayerSpecification playerFact : initialGameStateSpecification.getPlayerFacts() ) { }
final PlayerSpecification basePlayerSpec = new PlayerSpecificationBuilder( playerFact.getName() ).build(); }
Player player = PlayerSpecificationHandler.INSTANCE.find( game, basePlayerSpec ); }
if( playerFact.getLife() != null ) { // game.getAction().startGame( null ) determines starting player, draws starting
player.setLife( playerFact.getLife(), null ); // hands, handles mulligans, and initiates the first turn
} // skip drawing initial hand and mulliganing
game.setAge(GameStage.Play);
game.getTriggerHandler().runTrigger(TriggerType.NewGame, AbilityKey.newMap(), false);
if( playerFact.getPoison() != null ) { // first player in the list starts, no coin toss etc
player.setPoisonCounters( playerFact.getPoison(), null ); game.getPhaseHandler().startFirstTurn(game.getPlayers().get(0));
} game.fireEvent(new GameEventGameFinished());
}
}
//game.getAction().startGame( null ) determines starting player, draws starting hands, handles mulligans, and initiates the first turn
//skip drawing initial hand and mulliganing
game.setAge( GameStage.Play );
game.getTriggerHandler().runTrigger(TriggerType.NewGame, AbilityKey.newMap(), false);
//first player in the list starts, no coin toss etc
game.getPhaseHandler().startFirstTurn( game.getPlayers().get( 0 ) );
game.fireEvent( new GameEventGameFinished() );
} }
public PlayerActions getPlayerActions() { public PlayerActions getPlayerActions() {
@@ -168,11 +173,11 @@ public class GameWrapper {
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append( "Game log : \r\n" ); sb.append("Game log : \r\n");
List<GameLogEntry> gameLogEntries = gameLog.getLogEntries( GameLogEntryType.PHASE ); List<GameLogEntry> gameLogEntries = gameLog.getLogEntries(GameLogEntryType.PHASE);
Collections.reverse( gameLogEntries ); Collections.reverse(gameLogEntries);
for( GameLogEntry gameLogEntry : gameLogEntries ) { for (GameLogEntry gameLogEntry : gameLogEntries) {
sb.append( gameLogEntry.toString() ).append( "\r\n" ); sb.append(gameLogEntry.toString()).append("\r\n");
} }
return sb.toString(); return sb.toString();