Moved hasProperty code (it's forge script parsing in fact) out of CardState object to keep the class clean.

This commit is contained in:
Maxmtg
2017-08-15 21:40:03 +00:00
parent d539ddf018
commit 244d5bba47
11 changed files with 179 additions and 147 deletions

1
.gitattributes vendored
View File

@@ -304,6 +304,7 @@ forge-game/src/main/java/forge/GameCommand.java svneol=native#text/plain
forge-game/src/main/java/forge/game/CardTraitBase.java -text forge-game/src/main/java/forge/game/CardTraitBase.java -text
forge-game/src/main/java/forge/game/CardTraitPredicates.java -text svneol=unset#text/plain forge-game/src/main/java/forge/game/CardTraitPredicates.java -text svneol=unset#text/plain
forge-game/src/main/java/forge/game/Direction.java -text forge-game/src/main/java/forge/game/Direction.java -text
forge-game/src/main/java/forge/game/ForgeScript.java -text
forge-game/src/main/java/forge/game/Game.java -text forge-game/src/main/java/forge/game/Game.java -text
forge-game/src/main/java/forge/game/GameAction.java svneol=native#text/plain forge-game/src/main/java/forge/game/GameAction.java svneol=native#text/plain
forge-game/src/main/java/forge/game/GameActionUtil.java svneol=native#text/plain forge-game/src/main/java/forge/game/GameActionUtil.java svneol=native#text/plain

View File

@@ -723,7 +723,6 @@ public abstract class GameState {
private void setupPlayerState(int life, Map<ZoneType, String> cardTexts, final Player p) { private void setupPlayerState(int life, Map<ZoneType, String> cardTexts, final Player p) {
// Lock check static as we setup player state // Lock check static as we setup player state
final Game game = p.getGame();
Map<ZoneType, CardCollectionView> playerCards = new EnumMap<ZoneType, CardCollectionView>(ZoneType.class); Map<ZoneType, CardCollectionView> playerCards = new EnumMap<ZoneType, CardCollectionView>(ZoneType.class);
for (Entry<ZoneType, String> kv : cardTexts.entrySet()) { for (Entry<ZoneType, String> kv : cardTexts.entrySet()) {

View File

@@ -303,7 +303,7 @@ public class GameCopier {
newCard.setSemiPermanentToughnessBoost(c.getSemiPermanentToughnessBoost()); newCard.setSemiPermanentToughnessBoost(c.getSemiPermanentToughnessBoost());
newCard.setDamage(c.getDamage()); newCard.setDamage(c.getDamage());
newCard.setChangedCardTypes(c.getChangedCardTypes()); newCard.setChangedCardTypes(c.getChangedCardTypesMap());
newCard.setChangedCardKeywords(c.getChangedCardKeywords()); newCard.setChangedCardKeywords(c.getChangedCardKeywords());
// TODO: Is this correct? Does it not duplicate keywords from enchantments and such? // TODO: Is this correct? Does it not duplicate keywords from enchantments and such?
for (String kw : c.getHiddenExtrinsicKeywords()) for (String kw : c.getHiddenExtrinsicKeywords())

View File

@@ -411,11 +411,13 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
} }
@Override @Override
public CardTypeView getTypeWithChanges(final Map<Long, CardChangedType> changedCardTypes) { public CardTypeView getTypeWithChanges(final Iterable<CardChangedType> changedCardTypes) {
if (changedCardTypes.isEmpty()) { return this; } CardType newType = null;
// we assume that changes are already correctly ordered (taken from TreeMap.values())
for (final CardChangedType ct : changedCardTypes) {
if(null == newType)
newType = new CardType(CardType.this);
final CardType newType = new CardType(CardType.this);
for (final CardChangedType ct : changedCardTypes.values()) {
if (ct.isRemoveCardTypes()) { if (ct.isRemoveCardTypes()) {
newType.coreTypes.clear(); newType.coreTypes.clear();
} }
@@ -441,7 +443,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
newType.addAll(ct.getAddType()); newType.addAll(ct.getAddType());
} }
} }
return newType; return newType == null ? this : newType;
} }
@Override @Override

View File

@@ -1,7 +1,6 @@
package forge.card; package forge.card;
import java.io.Serializable; import java.io.Serializable;
import java.util.Map;
import java.util.Set; import java.util.Set;
import forge.card.CardType.CoreType; import forge.card.CardType.CoreType;
@@ -39,5 +38,5 @@ public interface CardTypeView extends Iterable<String>, Serializable {
boolean isPhenomenon(); boolean isPhenomenon();
boolean isEmblem(); boolean isEmblem();
boolean isTribal(); boolean isTribal();
CardTypeView getTypeWithChanges(Map<Long, CardChangedType> changedCardTypes); CardTypeView getTypeWithChanges(Iterable<CardChangedType> changedCardTypes);
} }

View File

@@ -0,0 +1,152 @@
package forge.game;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardState;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.util.Expressions;
public class ForgeScript {
public static boolean cardStateHasProperty(CardState cardState, String property, Player sourceController,
Card source, SpellAbility spellAbility) {
final boolean isColorlessSource = cardState.getCard().hasKeyword("Colorless Damage Source", cardState);
final ColorSet colors = cardState.getCard().determineColor(cardState);
if (property.contains("White") || property.contains("Blue") || property.contains("Black")
|| property.contains("Red") || property.contains("Green")) {
boolean mustHave = !property.startsWith("non");
boolean withSource = property.endsWith("Source");
if (withSource && isColorlessSource) {
return false;
}
final String colorName = property.substring(mustHave ? 0 : 3, property.length() - (withSource ? 6 : 0));
int desiredColor = MagicColor.fromName(colorName);
boolean hasColor = colors.hasAnyColor(desiredColor);
if (mustHave != hasColor)
return false;
} else if (property.contains("Colorless")) { // ... Card is colorless
boolean non = property.startsWith("non");
boolean withSource = property.endsWith("Source");
if (non && withSource && isColorlessSource) {
return false;
}
if (non == colors.isColorless()) return false;
} else if (property.contains("MultiColor")) {
// ... Card is multicolored
if (property.endsWith("Source") && isColorlessSource)
return false;
if (property.startsWith("non") == colors.isMulticolor())
return false;
} else if (property.contains("MonoColor")) { // ... Card is monocolored
if (property.endsWith("Source") && isColorlessSource)
return false;
if (property.startsWith("non") == colors.isMonoColor())
return false;
} else if (property.startsWith("ChosenColor")) {
if (property.endsWith("Source") && isColorlessSource)
return false;
if (!source.hasChosenColor() || !colors.hasAnyColor(MagicColor.fromName(source.getChosenColor())))
return false;
} else if (property.startsWith("AnyChosenColor")) {
if (property.endsWith("Source") && isColorlessSource)
return false;
if (!source.hasChosenColor()
|| !colors.hasAnyColor(ColorSet.fromNames(source.getChosenColors()).getColor()))
return false;
} else if (property.startsWith("non")) {
// ... Other Card types
if (cardState.getTypeWithChanges().hasStringType(property.substring(3))) {
return false;
}
} else if (property.equals("CostsPhyrexianMana")) {
if (!cardState.getManaCost().hasPhyrexian()) {
return false;
}
} else if (property.startsWith("HasSVar")) {
final String svar = property.substring(8);
if (!cardState.hasSVar(svar)) {
return false;
}
} else if (property.equals("ChosenType")) {
if (!cardState.getTypeWithChanges().hasStringType(source.getChosenType())) {
return false;
}
} else if (property.equals("IsNotChosenType")) {
if (cardState.getTypeWithChanges().hasStringType(source.getChosenType())) {
return false;
}
} else if (property.startsWith("HasSubtype")) {
final String subType = property.substring(11);
if (!cardState.getTypeWithChanges().hasSubtype(subType)) {
return false;
}
} else if (property.startsWith("HasNoSubtype")) {
final String subType = property.substring(13);
if (cardState.getTypeWithChanges().hasSubtype(subType)) {
return false;
}
} else if (property.equals("hasActivatedAbilityWithTapCost")) {
for (final SpellAbility sa : cardState.getSpellAbilities()) {
if (sa.isAbility() && (sa.getPayCosts() != null) && sa.getPayCosts().hasTapCost()) {
return true;
}
}
return false;
} else if (property.equals("hasActivatedAbility")) {
for (final SpellAbility sa : cardState.getSpellAbilities()) {
if (sa.isAbility()) {
return true;
}
}
return false;
} else if (property.equals("hasManaAbility")) {
for (final SpellAbility sa : cardState.getSpellAbilities()) {
if (sa.isManaAbility()) {
return true;
}
}
return false;
} else if (property.equals("hasNonManaActivatedAbility")) {
for (final SpellAbility sa : cardState.getSpellAbilities()) {
if (sa.isAbility() && !sa.isManaAbility()) {
return true;
}
}
return false;
} else if (property.startsWith("cmc")) {
int x;
String rhs = property.substring(5);
int y = cardState.getManaCost().getCMC();
try {
x = Integer.parseInt(rhs);
} catch (final NumberFormatException e) {
x = AbilityUtils.calculateAmount(source, rhs, spellAbility);
}
if (!Expressions.compare(y, property, x)) {
return false;
}
} else if (!cardState.getTypeWithChanges().hasStringType(property)) {
return false;
}
return true;
}
}

View File

@@ -2709,10 +2709,14 @@ public class Card extends GameEntity implements Comparable<Card> {
if (changedCardTypes.isEmpty()) { if (changedCardTypes.isEmpty()) {
return state.getType(); return state.getType();
} }
return state.getType().getTypeWithChanges(changedCardTypes); return state.getType().getTypeWithChanges(getChangedCardTypes());
} }
public Map<Long, CardChangedType> getChangedCardTypes() { public Iterable<CardChangedType> getChangedCardTypes() {
return Iterables.unmodifiableIterable(changedCardTypes.values());
}
public Map<Long, CardChangedType> getChangedCardTypesMap() {
return Collections.unmodifiableMap(changedCardTypes); return Collections.unmodifiableMap(changedCardTypes);
} }

View File

@@ -151,7 +151,7 @@ public class CardFactory {
// information about the latest state of the card as it left the battlefield) // information about the latest state of the card as it left the battlefield)
out.setChangedCardColors(in.getChangedCardColors()); out.setChangedCardColors(in.getChangedCardColors());
out.setChangedCardKeywords(in.getChangedCardKeywords()); out.setChangedCardKeywords(in.getChangedCardKeywords());
out.setChangedCardTypes(in.getChangedCardTypes()); out.setChangedCardTypes(in.getChangedCardTypesMap());
return out; return out;
} }

View File

@@ -29,19 +29,17 @@ import forge.card.CardEdition;
import forge.card.CardRarity; import forge.card.CardRarity;
import forge.card.CardType; import forge.card.CardType;
import forge.card.CardTypeView; import forge.card.CardTypeView;
import forge.card.ColorSet;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostParser; import forge.card.mana.ManaCostParser;
import forge.game.ForgeScript;
import forge.game.GameObject; import forge.game.GameObject;
import forge.game.ability.AbilityUtils;
import forge.game.card.CardView.CardStateView; import forge.game.card.CardView.CardStateView;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbility;
import forge.game.trigger.Trigger; import forge.game.trigger.Trigger;
import forge.util.Expressions;
import forge.util.collect.FCollection; import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView; import forge.util.collect.FCollectionView;
@@ -377,6 +375,7 @@ public class CardState extends GameObject {
view.updateKeywords(c, this); view.updateKeywords(c, this);
} }
public CardRarity getRarity() { public CardRarity getRarity() {
return rarity; return rarity;
} }
@@ -388,6 +387,11 @@ public class CardState extends GameObject {
public String getSetCode() { public String getSetCode() {
return setCode; return setCode;
} }
public CardTypeView getTypeWithChanges() {
return getType().getTypeWithChanges(card.getChangedCardTypes());
}
public void setSetCode(String setCode0) { public void setSetCode(String setCode0) {
setCode = setCode0; setCode = setCode0;
view.updateSetCode(this); view.updateSetCode(this);
@@ -398,136 +402,7 @@ public class CardState extends GameObject {
*/ */
@Override @Override
public boolean hasProperty(String property, Player sourceController, Card source, SpellAbility spellAbility) { public boolean hasProperty(String property, Player sourceController, Card source, SpellAbility spellAbility) {
final boolean isColorlessSource = card.hasKeyword("Colorless Damage Source", this); return ForgeScript.cardStateHasProperty(this, property, sourceController, source, spellAbility);
final ColorSet colors = card.determineColor(this);
if (property.contains("White") || property.contains("Blue") || property.contains("Black")
|| property.contains("Red") || property.contains("Green")) {
boolean mustHave = !property.startsWith("non");
boolean withSource = property.endsWith("Source");
if (withSource && isColorlessSource) {
return false;
}
final String colorName = property.substring(mustHave ? 0 : 3, property.length() - (withSource ? 6 : 0));
int desiredColor = MagicColor.fromName(colorName);
boolean hasColor = colors.hasAnyColor(desiredColor);
if (mustHave != hasColor)
return false;
} else if (property.contains("Colorless")) { // ... Card is colorless
boolean non = property.startsWith("non");
boolean withSource = property.endsWith("Source");
if (non && withSource && isColorlessSource) {
return false;
}
if (non == colors.isColorless()) return false;
} else if (property.contains("MultiColor")) {
// ... Card is multicolored
if (property.endsWith("Source") && isColorlessSource)
return false;
if (property.startsWith("non") == colors.isMulticolor())
return false;
} else if (property.contains("MonoColor")) { // ... Card is monocolored
if (property.endsWith("Source") && isColorlessSource)
return false;
if (property.startsWith("non") == colors.isMonoColor())
return false;
} else if (property.startsWith("ChosenColor")) {
if (property.endsWith("Source") && isColorlessSource)
return false;
if (!source.hasChosenColor() || !colors.hasAnyColor(MagicColor.fromName(source.getChosenColor())))
return false;
} else if (property.startsWith("AnyChosenColor")) {
if (property.endsWith("Source") && isColorlessSource)
return false;
if (!source.hasChosenColor()
|| !colors.hasAnyColor(ColorSet.fromNames(source.getChosenColors()).getColor()))
return false;
} else if (property.startsWith("non")) {
// ... Other Card types
if (card.getType(this).hasStringType(property.substring(3))) {
return false;
}
} else if (property.equals("CostsPhyrexianMana")) {
if (!getManaCost().hasPhyrexian()) {
return false;
}
} else if (property.startsWith("HasSVar")) {
final String svar = property.substring(8);
if (!hasSVar(svar)) {
return false;
}
} else if (property.equals("ChosenType")) {
if (!card.getType(this).hasStringType(source.getChosenType())) {
return false;
}
} else if (property.equals("IsNotChosenType")) {
if (card.getType(this).hasStringType(source.getChosenType())) {
return false;
}
} else if (property.startsWith("HasSubtype")) {
final String subType = property.substring(11);
if (!card.getType(this).hasSubtype(subType)) {
return false;
}
} else if (property.startsWith("HasNoSubtype")) {
final String subType = property.substring(13);
if (card.getType(this).hasSubtype(subType)) {
return false;
}
} else if (property.equals("hasActivatedAbilityWithTapCost")) {
for (final SpellAbility sa : getSpellAbilities()) {
if (sa.isAbility() && (sa.getPayCosts() != null) && sa.getPayCosts().hasTapCost()) {
return true;
}
}
return false;
} else if (property.equals("hasActivatedAbility")) {
for (final SpellAbility sa : getSpellAbilities()) {
if (sa.isAbility()) {
return true;
}
}
return false;
} else if (property.equals("hasManaAbility")) {
for (final SpellAbility sa : getSpellAbilities()) {
if (sa.isManaAbility()) {
return true;
}
}
return false;
} else if (property.equals("hasNonManaActivatedAbility")) {
for (final SpellAbility sa : getSpellAbilities()) {
if (sa.isAbility() && !sa.isManaAbility()) {
return true;
}
}
return false;
} else if (property.startsWith("cmc")) {
int x;
String rhs = property.substring(5);
int y = getManaCost().getCMC();
try {
x = Integer.parseInt(rhs);
} catch (final NumberFormatException e) {
x = AbilityUtils.calculateAmount(source, rhs, spellAbility);
}
if (!Expressions.compare(y, property, x)) {
return false;
}
} else if (!card.getType(this).hasStringType(property)) {
return false;
}
return true;
} }

View File

@@ -538,7 +538,7 @@ public final class CardUtil {
newCopy.setChangedCardColors(in.getChangedCardColors()); newCopy.setChangedCardColors(in.getChangedCardColors());
newCopy.setChangedCardKeywords(in.getChangedCardKeywords()); newCopy.setChangedCardKeywords(in.getChangedCardKeywords());
newCopy.setChangedCardTypes(in.getChangedCardTypes()); newCopy.setChangedCardTypes(in.getChangedCardTypesMap());
newCopy.setMeldedWith(in.getMeldedWith()); newCopy.setMeldedWith(in.getMeldedWith());

View File

@@ -1337,7 +1337,7 @@ public class GameSimulatorTest extends SimulationTestCase {
public void testDeathsShadow() { public void testDeathsShadow() {
Game game = initAndCreateGame(); Game game = initAndCreateGame();
Player p = game.getPlayers().get(0); Player p = game.getPlayers().get(0);
Player opp = game.getPlayers().get(1); //Player opp = game.getPlayers().get(1);
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p); game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
addCardToZone("Platinum Angel", p, ZoneType.Battlefield); addCardToZone("Platinum Angel", p, ZoneType.Battlefield);