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.card.CounterType;
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
|
import forge.game.keyword.KeywordWithType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.replacement.ReplacementEffect;
|
import forge.game.replacement.ReplacementEffect;
|
||||||
import forge.game.replacement.ReplacementType;
|
import forge.game.replacement.ReplacementType;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.staticability.StaticAbility;
|
||||||
import forge.game.staticability.StaticAbilityCantAttach;
|
import forge.game.staticability.StaticAbilityCantAttach;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
import forge.util.Lang;
|
||||||
|
|
||||||
public abstract class GameEntity extends GameObject implements IIdentifiable {
|
public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||||
protected int id;
|
protected int id;
|
||||||
@@ -218,63 +221,83 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
return canBeAttached(attach, sa, false);
|
return canBeAttached(attach, sa, false);
|
||||||
}
|
}
|
||||||
public boolean canBeAttached(final Card attach, SpellAbility sa, boolean checkSBA) {
|
public boolean canBeAttached(final Card attach, SpellAbility sa, boolean checkSBA) {
|
||||||
// master mode
|
return cantBeAttachedMsg(attach, sa, checkSBA) == null;
|
||||||
if (!attach.isAttachment() || (attach.isCreature() && !attach.hasKeyword(Keyword.RECONFIGURE))
|
}
|
||||||
|| equals(attach)) {
|
|
||||||
return false;
|
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()) {
|
if (attach.isPhasedOut()) {
|
||||||
return false;
|
return attach.getName() + " is phased out";
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for rules
|
if (attach.isAura()) {
|
||||||
if (attach.isAura() && !canBeEnchantedBy(attach)) {
|
String msg = cantBeEnchantedByMsg(attach);
|
||||||
return false;
|
if (msg != null) {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (attach.isEquipment() && !canBeEquippedBy(attach, sa)) {
|
if (attach.isEquipment()) {
|
||||||
return false;
|
String msg = cantBeEquippedByMsg(attach, sa);
|
||||||
|
if (msg != null) {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (attach.isFortification() && !canBeFortifiedBy(attach)) {
|
if (attach.isFortification()) {
|
||||||
return false;
|
String msg = cantBeFortifiedByMsg(attach);
|
||||||
|
if (msg != null) {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for can't attach static
|
StaticAbility stAb = StaticAbilityCantAttach.cantAttach(this, attach, checkSBA);
|
||||||
if (StaticAbilityCantAttach.cantAttach(this, attach, checkSBA)) {
|
if (stAb != null) {
|
||||||
return false;
|
return stAb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// true for all
|
return null;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean canBeEquippedBy(final Card aura, SpellAbility sa) {
|
protected String cantBeEquippedByMsg(final Card aura, SpellAbility sa) {
|
||||||
/**
|
|
||||||
* Equip only to Creatures which are cards
|
|
||||||
*/
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean canBeFortifiedBy(final Card aura) {
|
|
||||||
/**
|
/**
|
||||||
* Equip only to Lands which are cards
|
* 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)) {
|
if (!aura.hasKeyword(Keyword.ENCHANT)) {
|
||||||
return false;
|
return "No Enchant Keyword";
|
||||||
}
|
}
|
||||||
for (KeywordInterface ki : aura.getKeywords(Keyword.ENCHANT)) {
|
for (KeywordInterface ki : aura.getKeywords(Keyword.ENCHANT)) {
|
||||||
String k = ki.getOriginal();
|
if (ki instanceof KeywordWithType kwt) {
|
||||||
String m[] = k.split(":");
|
String v = kwt.getValidType();
|
||||||
String v = m[1];
|
String desc = kwt.getTypeDescription();
|
||||||
if (!isValid(v.split(","), aura.getController(), aura, null)) {
|
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() {
|
public boolean hasCounters() {
|
||||||
|
|||||||
@@ -2453,17 +2453,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
|||||||
} else if (keyword.startsWith("DeckLimit")) {
|
} else if (keyword.startsWith("DeckLimit")) {
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
sbLong.append(k[2]).append("\r\n");
|
sbLong.append(k[2]).append("\r\n");
|
||||||
} else if (keyword.startsWith("Enchant")) {
|
} else if (keyword.startsWith("Enchant") && inst instanceof KeywordWithType kwt) {
|
||||||
String m[] = keyword.split(":");
|
String desc = kwt.getTypeDescription();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sbLong.append("Enchant ").append(desc).append("\r\n");
|
sbLong.append("Enchant ").append(desc).append("\r\n");
|
||||||
} else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph")
|
} else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph")
|
||||||
|| keyword.startsWith("Disguise") || keyword.startsWith("Reflect")
|
|| keyword.startsWith("Disguise") || keyword.startsWith("Reflect")
|
||||||
@@ -3797,7 +3788,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
|||||||
public final void addLeavesPlayCommand(final GameCommand c) {
|
public final void addLeavesPlayCommand(final GameCommand c) {
|
||||||
leavePlayCommandList.add(c);
|
leavePlayCommandList.add(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addStaticCommandList(Object[] objects) {
|
public void addStaticCommandList(Object[] objects) {
|
||||||
staticCommandList.add(objects);
|
staticCommandList.add(objects);
|
||||||
}
|
}
|
||||||
@@ -4812,7 +4803,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
|||||||
public void addDraftAction(String s) {
|
public void addDraftAction(String s) {
|
||||||
draftActions.add(s);
|
draftActions.add(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int intensity = 0;
|
private int intensity = 0;
|
||||||
public final void addIntensity(final int n) {
|
public final void addIntensity(final int n) {
|
||||||
intensity += n;
|
intensity += n;
|
||||||
@@ -7171,51 +7162,62 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final boolean canBeEnchantedBy(final Card aura) {
|
protected final String cantBeEnchantedByMsg(final Card aura) {
|
||||||
if (!aura.hasKeyword(Keyword.ENCHANT)) {
|
if (!aura.hasKeyword(Keyword.ENCHANT)) {
|
||||||
return false;
|
return "No Enchant Keyword";
|
||||||
}
|
}
|
||||||
for (KeywordInterface ki : aura.getKeywords(Keyword.ENCHANT)) {
|
for (KeywordInterface ki : aura.getKeywords(Keyword.ENCHANT)) {
|
||||||
String k = ki.getOriginal();
|
if (ki instanceof KeywordWithType kwt) {
|
||||||
String m[] = k.split(":");
|
String v = kwt.getValidType();
|
||||||
String v = m[1];
|
String desc = kwt.getTypeDescription();
|
||||||
if (!isValid(v.split(","), aura.getController(), aura, null)) {
|
if (!isValid(v.split(","), aura.getController(), aura, null) || (!v.contains("inZone") && !isInPlay())) {
|
||||||
return false;
|
return getName() + " is not " + Lang.nounWithAmount(1, desc);
|
||||||
}
|
}
|
||||||
if (!v.contains("inZone") && !isInPlay()) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final boolean canBeEquippedBy(final Card equip, SpellAbility sa) {
|
protected String cantBeEquippedByMsg(final Card equip, SpellAbility sa) {
|
||||||
if (!isInPlay()) {
|
if (!isInPlay()) {
|
||||||
return false;
|
return getName() + " is not in play";
|
||||||
}
|
}
|
||||||
if (sa != null && sa.isEquip()) {
|
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 null;
|
||||||
}
|
}
|
||||||
return isCreature();
|
if (!isCreature()) {
|
||||||
|
return getName() + " is not a creature";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canBeFortifiedBy(final Card fort) {
|
protected String cantBeFortifiedByMsg(final Card fort) {
|
||||||
return isLand() && isInPlay() && !fort.isLand();
|
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
|
@Override
|
||||||
public boolean canBeAttached(Card attach, SpellAbility sa, boolean checkSBA) {
|
public String cantBeAttachedMsg(final Card attach, SpellAbility sa, boolean checkSBA) {
|
||||||
// phase check there
|
|
||||||
if (isPhasedOut() && !attach.isPhasedOut()) {
|
if (isPhasedOut() && !attach.isPhasedOut()) {
|
||||||
return false;
|
return getName() + " is phased out";
|
||||||
}
|
}
|
||||||
|
return super.cantBeAttachedMsg(attach, sa, checkSBA);
|
||||||
return super.canBeAttached(attach, sa, checkSBA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean canBeSacrificedBy(final SpellAbility source, final boolean effect) {
|
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.Keyword;
|
||||||
import forge.game.keyword.KeywordCollection;
|
import forge.game.keyword.KeywordCollection;
|
||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
|
import forge.game.keyword.KeywordWithType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.replacement.ReplacementEffect;
|
import forge.game.replacement.ReplacementEffect;
|
||||||
import forge.game.spellability.LandAbility;
|
import forge.game.spellability.LandAbility;
|
||||||
@@ -501,15 +502,8 @@ public class CardState extends GameObject implements IHasSVars, ITranslatable {
|
|||||||
String desc = "";
|
String desc = "";
|
||||||
String extra = "";
|
String extra = "";
|
||||||
for (KeywordInterface ki : this.getCachedKeyword(Keyword.ENCHANT)) {
|
for (KeywordInterface ki : this.getCachedKeyword(Keyword.ENCHANT)) {
|
||||||
String o = ki.getOriginal();
|
if (ki instanceof KeywordWithType kwt) {
|
||||||
String m[] = o.split(":");
|
desc = kwt.getTypeDescription();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ public class Equip extends KeywordWithCost {
|
|||||||
public Equip() {
|
public Equip() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getValidDescription() { return type; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void parse(String details) {
|
protected void parse(String details) {
|
||||||
String[] k = details.split(":");
|
String[] k = details.split(":");
|
||||||
|
|||||||
@@ -3,38 +3,50 @@ package forge.game.keyword;
|
|||||||
import forge.card.CardType;
|
import forge.card.CardType;
|
||||||
|
|
||||||
public class KeywordWithType extends KeywordInstance<KeywordWithType> {
|
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
|
@Override
|
||||||
protected void parse(String details) {
|
protected void parse(String details) {
|
||||||
if (CardType.isACardType(details)) {
|
String k[];
|
||||||
type = details.toLowerCase();
|
if (details.contains(":")) {
|
||||||
} else if (details.contains(":")) {
|
|
||||||
switch (getKeyword()) {
|
switch (getKeyword()) {
|
||||||
case AFFINITY:
|
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 BANDSWITH:
|
||||||
|
case ENCHANT:
|
||||||
case HEXPROOF:
|
case HEXPROOF:
|
||||||
case LANDWALK:
|
case LANDWALK:
|
||||||
type = details.split(":")[1];
|
k = details.split(":");
|
||||||
|
type = k[0];
|
||||||
|
descType = k[1];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
type = details.split(":")[0];
|
k = details.split(":");
|
||||||
|
type = k[1];
|
||||||
|
descType = k[0];
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
@Override
|
||||||
protected String formatReminderText(String reminderText) {
|
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 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
|
// CantTarget static abilities
|
||||||
for (final Card ca : target.getGame().getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
for (final Card ca : target.getGame().getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||||
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||||
@@ -15,11 +15,11 @@ public class StaticAbilityCantAttach {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (applyCantAttachAbility(stAb, card, target, checkSBA)) {
|
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) {
|
public static boolean applyCantAttachAbility(final StaticAbility stAb, final Card card, final GameEntity target, boolean checkSBA) {
|
||||||
|
|||||||
@@ -169,9 +169,12 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
|||||||
// TODO should use sa.canTarget(card) instead?
|
// TODO should use sa.canTarget(card) instead?
|
||||||
// it doesn't have messages
|
// it doesn't have messages
|
||||||
|
|
||||||
if (sa.isSpell() && sa.getHostCard().isAura() && !card.canBeAttached(sa.getHostCard(), sa)) {
|
if (sa.isSpell() && sa.getHostCard().isAura()) {
|
||||||
showMessage(sa.getHostCard() + " - Cannot enchant this card (Shroud? Protection? Restrictions?).");
|
String msg = card.cantBeAttachedMsg(sa.getHostCard(), sa);
|
||||||
return false;
|
if (msg != null) {
|
||||||
|
showMessage(sa.getHostCard() + " - " + msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//If the card is not a valid target
|
//If the card is not a valid target
|
||||||
if (!card.canBeTargetedBy(sa)) {
|
if (!card.canBeTargetedBy(sa)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user