mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
cantBeEnchantedByMsg and cantAttach StaticAbility (#8772)
* cantBeEnchantedByMsg and cantAttach StaticAbility * finish cantBeAttachedMsg
This commit is contained in:
@@ -36,12 +36,15 @@ import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.keyword.KeywordWithType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.staticability.StaticAbilityCantAttach;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
|
||||
public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||
protected int id;
|
||||
@@ -218,63 +221,83 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||
return canBeAttached(attach, sa, false);
|
||||
}
|
||||
public boolean canBeAttached(final Card attach, SpellAbility sa, boolean checkSBA) {
|
||||
// master mode
|
||||
if (!attach.isAttachment() || (attach.isCreature() && !attach.hasKeyword(Keyword.RECONFIGURE))
|
||||
|| equals(attach)) {
|
||||
return false;
|
||||
return cantBeAttachedMsg(attach, sa, checkSBA) == null;
|
||||
}
|
||||
|
||||
public String cantBeAttachedMsg(final Card attach, SpellAbility sa) {
|
||||
return cantBeAttachedMsg(attach, sa, false);
|
||||
}
|
||||
public String cantBeAttachedMsg(final Card attach, SpellAbility sa, boolean checkSBA) {
|
||||
if (!attach.isAttachment()) {
|
||||
return attach.getName() + " is not an attachment";
|
||||
}
|
||||
if (equals(attach)) {
|
||||
return attach.getName() + " can't attach to itself";
|
||||
}
|
||||
|
||||
if (attach.isCreature() && !attach.hasKeyword(Keyword.RECONFIGURE)) {
|
||||
return attach.getName() + " is a creature without reconfigure";
|
||||
}
|
||||
|
||||
if (attach.isPhasedOut()) {
|
||||
return false;
|
||||
return attach.getName() + " is phased out";
|
||||
}
|
||||
|
||||
// check for rules
|
||||
if (attach.isAura() && !canBeEnchantedBy(attach)) {
|
||||
return false;
|
||||
if (attach.isAura()) {
|
||||
String msg = cantBeEnchantedByMsg(attach);
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
if (attach.isEquipment() && !canBeEquippedBy(attach, sa)) {
|
||||
return false;
|
||||
}
|
||||
if (attach.isFortification() && !canBeFortifiedBy(attach)) {
|
||||
return false;
|
||||
if (attach.isEquipment()) {
|
||||
String msg = cantBeEquippedByMsg(attach, sa);
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
if (attach.isFortification()) {
|
||||
String msg = cantBeFortifiedByMsg(attach);
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
// check for can't attach static
|
||||
if (StaticAbilityCantAttach.cantAttach(this, attach, checkSBA)) {
|
||||
return false;
|
||||
StaticAbility stAb = StaticAbilityCantAttach.cantAttach(this, attach, checkSBA);
|
||||
if (stAb != null) {
|
||||
return stAb.toString();
|
||||
}
|
||||
|
||||
// true for all
|
||||
return true;
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean canBeEquippedBy(final Card aura, SpellAbility sa) {
|
||||
/**
|
||||
* Equip only to Creatures which are cards
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean canBeFortifiedBy(final Card aura) {
|
||||
protected String cantBeEquippedByMsg(final Card aura, SpellAbility sa) {
|
||||
/**
|
||||
* Equip only to Lands which are cards
|
||||
*/
|
||||
return false;
|
||||
return getName() + " is not a Creature";
|
||||
}
|
||||
|
||||
protected boolean canBeEnchantedBy(final Card aura) {
|
||||
protected String cantBeFortifiedByMsg(final Card fort) {
|
||||
/**
|
||||
* Equip only to Lands which are cards
|
||||
*/
|
||||
return getName() + " is not a Land";
|
||||
}
|
||||
|
||||
protected String cantBeEnchantedByMsg(final Card aura) {
|
||||
if (!aura.hasKeyword(Keyword.ENCHANT)) {
|
||||
return false;
|
||||
return "No Enchant Keyword";
|
||||
}
|
||||
for (KeywordInterface ki : aura.getKeywords(Keyword.ENCHANT)) {
|
||||
String k = ki.getOriginal();
|
||||
String m[] = k.split(":");
|
||||
String v = m[1];
|
||||
if (ki instanceof KeywordWithType kwt) {
|
||||
String v = kwt.getValidType();
|
||||
String desc = kwt.getTypeDescription();
|
||||
if (!isValid(v.split(","), aura.getController(), aura, null)) {
|
||||
return false;
|
||||
return getName() + " is not " + Lang.nounWithAmount(1, desc);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean hasCounters() {
|
||||
|
||||
@@ -2453,17 +2453,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
} else if (keyword.startsWith("DeckLimit")) {
|
||||
final String[] k = keyword.split(":");
|
||||
sbLong.append(k[2]).append("\r\n");
|
||||
} else if (keyword.startsWith("Enchant")) {
|
||||
String m[] = keyword.split(":");
|
||||
String desc;
|
||||
if (m.length > 2) {
|
||||
desc = m[2];
|
||||
} else {
|
||||
desc = m[1];
|
||||
if (CardType.isACardType(desc) || "Permanent".equals(desc) || "Player".equals(desc) || "Opponent".equals(desc)) {
|
||||
desc = desc.toLowerCase();
|
||||
}
|
||||
}
|
||||
} else if (keyword.startsWith("Enchant") && inst instanceof KeywordWithType kwt) {
|
||||
String desc = kwt.getTypeDescription();
|
||||
sbLong.append("Enchant ").append(desc).append("\r\n");
|
||||
} else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph")
|
||||
|| keyword.startsWith("Disguise") || keyword.startsWith("Reflect")
|
||||
@@ -7171,51 +7162,62 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean canBeEnchantedBy(final Card aura) {
|
||||
protected final String cantBeEnchantedByMsg(final Card aura) {
|
||||
if (!aura.hasKeyword(Keyword.ENCHANT)) {
|
||||
return false;
|
||||
return "No Enchant Keyword";
|
||||
}
|
||||
for (KeywordInterface ki : aura.getKeywords(Keyword.ENCHANT)) {
|
||||
String k = ki.getOriginal();
|
||||
String m[] = k.split(":");
|
||||
String v = m[1];
|
||||
if (!isValid(v.split(","), aura.getController(), aura, null)) {
|
||||
return false;
|
||||
}
|
||||
if (!v.contains("inZone") && !isInPlay()) {
|
||||
return false;
|
||||
if (ki instanceof KeywordWithType kwt) {
|
||||
String v = kwt.getValidType();
|
||||
String desc = kwt.getTypeDescription();
|
||||
if (!isValid(v.split(","), aura.getController(), aura, null) || (!v.contains("inZone") && !isInPlay())) {
|
||||
return getName() + " is not " + Lang.nounWithAmount(1, desc);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected final boolean canBeEquippedBy(final Card equip, SpellAbility sa) {
|
||||
protected String cantBeEquippedByMsg(final Card equip, SpellAbility sa) {
|
||||
if (!isInPlay()) {
|
||||
return false;
|
||||
return getName() + " is not in play";
|
||||
}
|
||||
if (sa != null && sa.isEquip()) {
|
||||
return isValid(sa.getTargetRestrictions().getValidTgts(), sa.getActivatingPlayer(), equip, sa);
|
||||
if (!isValid(sa.getTargetRestrictions().getValidTgts(), sa.getActivatingPlayer(), equip, sa)) {
|
||||
Equip eq = (Equip) sa.getKeyword();
|
||||
return getName() + " is not " + Lang.nounWithAmount(1, eq.getValidDescription());
|
||||
}
|
||||
return isCreature();
|
||||
return null;
|
||||
}
|
||||
if (!isCreature()) {
|
||||
return getName() + " is not a creature";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeFortifiedBy(final Card fort) {
|
||||
return isLand() && isInPlay() && !fort.isLand();
|
||||
protected String cantBeFortifiedByMsg(final Card fort) {
|
||||
if (!isLand()) {
|
||||
return getName() + " is not a Land";
|
||||
}
|
||||
if (!isInPlay()) {
|
||||
return getName() + " is not in play";
|
||||
}
|
||||
if (fort.isLand()) {
|
||||
return fort.getName() + " is a Land";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.game.GameEntity#canBeAttached(forge.game.card.Card, boolean)
|
||||
*/
|
||||
@Override
|
||||
public boolean canBeAttached(Card attach, SpellAbility sa, boolean checkSBA) {
|
||||
// phase check there
|
||||
public String cantBeAttachedMsg(final Card attach, SpellAbility sa, boolean checkSBA) {
|
||||
if (isPhasedOut() && !attach.isPhasedOut()) {
|
||||
return false;
|
||||
return getName() + " is phased out";
|
||||
}
|
||||
|
||||
return super.canBeAttached(attach, sa, checkSBA);
|
||||
return super.cantBeAttachedMsg(attach, sa, checkSBA);
|
||||
}
|
||||
|
||||
public final boolean canBeSacrificedBy(final SpellAbility source, final boolean effect) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import forge.game.card.CardView.CardStateView;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.keyword.KeywordCollection;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.keyword.KeywordWithType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.spellability.LandAbility;
|
||||
@@ -501,15 +502,8 @@ public class CardState extends GameObject implements IHasSVars, ITranslatable {
|
||||
String desc = "";
|
||||
String extra = "";
|
||||
for (KeywordInterface ki : this.getCachedKeyword(Keyword.ENCHANT)) {
|
||||
String o = ki.getOriginal();
|
||||
String m[] = o.split(":");
|
||||
if (m.length > 2) {
|
||||
desc = m[2];
|
||||
} else {
|
||||
desc = m[1];
|
||||
if (CardType.isACardType(desc) || "Permanent".equals(desc) || "Player".equals(desc) || "Opponent".equals(desc)) {
|
||||
desc = desc.toLowerCase();
|
||||
}
|
||||
if (ki instanceof KeywordWithType kwt) {
|
||||
desc = kwt.getTypeDescription();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ public class Equip extends KeywordWithCost {
|
||||
public Equip() {
|
||||
}
|
||||
|
||||
public String getValidDescription() { return type; }
|
||||
|
||||
@Override
|
||||
protected void parse(String details) {
|
||||
String[] k = details.split(":");
|
||||
|
||||
@@ -3,38 +3,50 @@ package forge.game.keyword;
|
||||
import forge.card.CardType;
|
||||
|
||||
public class KeywordWithType extends KeywordInstance<KeywordWithType> {
|
||||
protected String type;
|
||||
protected String type = null;
|
||||
protected String descType = null;
|
||||
protected String reminderType = null;
|
||||
|
||||
public String getValidType() { return type; }
|
||||
public String getTypeDescription() { return descType; }
|
||||
|
||||
@Override
|
||||
protected void parse(String details) {
|
||||
if (CardType.isACardType(details)) {
|
||||
type = details.toLowerCase();
|
||||
} else if (details.contains(":")) {
|
||||
String k[];
|
||||
if (details.contains(":")) {
|
||||
switch (getKeyword()) {
|
||||
case AFFINITY:
|
||||
type = details.split(":")[1];
|
||||
// type lists defined by rules should not be changed by TextChange in reminder text
|
||||
if (type.equalsIgnoreCase("Outlaw")) {
|
||||
type = "Assassin, Mercenary, Pirate, Rogue, and/or Warlock";
|
||||
} else if (type.equalsIgnoreCase("historic permanent")) {
|
||||
type = "artifact, legendary, and/or Saga permanent";
|
||||
}
|
||||
break;
|
||||
case BANDSWITH:
|
||||
case ENCHANT:
|
||||
case HEXPROOF:
|
||||
case LANDWALK:
|
||||
type = details.split(":")[1];
|
||||
k = details.split(":");
|
||||
type = k[0];
|
||||
descType = k[1];
|
||||
break;
|
||||
default:
|
||||
type = details.split(":")[0];
|
||||
k = details.split(":");
|
||||
type = k[1];
|
||||
descType = k[0];
|
||||
}
|
||||
} else {
|
||||
type = details;
|
||||
descType = type = details;
|
||||
}
|
||||
|
||||
if (CardType.isACardType(descType) || "Permanent".equals(descType) || "Player".equals(descType) || "Opponent".equals(descType)) {
|
||||
descType = descType.toLowerCase();
|
||||
} else if (descType.equalsIgnoreCase("Outlaw")) {
|
||||
reminderType = "Assassin, Mercenary, Pirate, Rogue, and/or Warlock";
|
||||
} else if (type.equalsIgnoreCase("historic permanent")) {
|
||||
reminderType = "artifact, legendary, and/or Saga permanent";
|
||||
}
|
||||
if (reminderType == null) {
|
||||
reminderType = type;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String formatReminderText(String reminderText) {
|
||||
return String.format(reminderText, type);
|
||||
return String.format(reminderText, reminderType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import forge.game.zone.ZoneType;
|
||||
|
||||
public class StaticAbilityCantAttach {
|
||||
|
||||
public static boolean cantAttach(final GameEntity target, final Card card, boolean checkSBA) {
|
||||
public static StaticAbility cantAttach(final GameEntity target, final Card card, boolean checkSBA) {
|
||||
// CantTarget static abilities
|
||||
for (final Card ca : target.getGame().getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||
@@ -15,11 +15,11 @@ public class StaticAbilityCantAttach {
|
||||
}
|
||||
|
||||
if (applyCantAttachAbility(stAb, card, target, checkSBA)) {
|
||||
return true;
|
||||
return stAb;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean applyCantAttachAbility(final StaticAbility stAb, final Card card, final GameEntity target, boolean checkSBA) {
|
||||
|
||||
@@ -169,10 +169,13 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
// TODO should use sa.canTarget(card) instead?
|
||||
// it doesn't have messages
|
||||
|
||||
if (sa.isSpell() && sa.getHostCard().isAura() && !card.canBeAttached(sa.getHostCard(), sa)) {
|
||||
showMessage(sa.getHostCard() + " - Cannot enchant this card (Shroud? Protection? Restrictions?).");
|
||||
if (sa.isSpell() && sa.getHostCard().isAura()) {
|
||||
String msg = card.cantBeAttachedMsg(sa.getHostCard(), sa);
|
||||
if (msg != null) {
|
||||
showMessage(sa.getHostCard() + " - " + msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//If the card is not a valid target
|
||||
if (!card.canBeTargetedBy(sa)) {
|
||||
showMessage(sa.getHostCard() + " - Cannot target this card (Shroud? Protection? Restrictions).");
|
||||
|
||||
Reference in New Issue
Block a user