- Added "loseSubtypesOf(type)" ability. For example, loseSubtypesOf(land) means "target loses all its land subtypes"
- Added Evil Presence, as an example of the new keywords loseabilities and losesubtypes. It's quite experimental but I added 3 tests that cover the basics. Please report if you find bugs. - moved the "lands produce mana" rules outside of the primitives, and into the external rules. This was a necessary step to create cards such as Evil Presence. - real support for subtypes. Needs some more testing, but there are now functions in Subtypes.cpp to know if a given subtype is a creature subtype, or a land subtype, etc... - minor refactor of MTGDeck.cpp Notes: - I checked that the AI can still use lands - This change has a bad impact on primitives loading performance (thanks Wil for the loading time output). This is probably due to suboptimal algorithms and data structures for subtypes. If the impact is strong on lowend devices, I can probably optimize a bit (the map subtypesOf could be changed into a vector with some work) - The test suite passes, added 3 tests for evil presence.
This commit is contained in:
@@ -2857,6 +2857,51 @@ ALoseAbilities * ALoseAbilities::clone() const
|
||||
return a;
|
||||
}
|
||||
|
||||
//ALoseSubtypes
|
||||
ALoseSubtypes::ALoseSubtypes(int id, MTGCardInstance * source, MTGCardInstance * _target, int parentType) :
|
||||
MTGAbility(id, source), parentType(parentType)
|
||||
{
|
||||
target = _target;
|
||||
}
|
||||
|
||||
int ALoseSubtypes::addToGame()
|
||||
{
|
||||
if (storedSubtypes.size())
|
||||
{
|
||||
DebugTrace("FATAL:storedSubtypes shouldn't be already set inALoseSubtypes\n");
|
||||
return 0;
|
||||
}
|
||||
MTGCardInstance * _target = (MTGCardInstance *)target;
|
||||
|
||||
for (int i = ((int)_target->types.size())-1; i >= 0; --i)
|
||||
{
|
||||
int subtype = _target->types[i];
|
||||
if (Subtypes::subtypesList->isSubtypeOfType(subtype, parentType))
|
||||
{
|
||||
storedSubtypes.push_back(subtype);
|
||||
_target->removeType(subtype);
|
||||
}
|
||||
}
|
||||
|
||||
return MTGAbility::addToGame();
|
||||
}
|
||||
|
||||
int ALoseSubtypes::destroy()
|
||||
{
|
||||
MTGCardInstance * _target = (MTGCardInstance *)target;
|
||||
for (size_t i = 0; i < storedSubtypes.size(); ++i)
|
||||
_target->addType(storedSubtypes[i]);
|
||||
storedSubtypes.clear();
|
||||
return 1;
|
||||
}
|
||||
|
||||
ALoseSubtypes * ALoseSubtypes::clone() const
|
||||
{
|
||||
ALoseSubtypes * a = NEW ALoseSubtypes(*this);
|
||||
a->isClone = 1;
|
||||
return a;
|
||||
}
|
||||
|
||||
//APreventDamageTypes
|
||||
APreventDamageTypes::APreventDamageTypes(int id, MTGCardInstance * source, string to, string from, int type) :
|
||||
MTGAbility(id, source), to(to), from(from), type(type)
|
||||
|
||||
@@ -196,7 +196,17 @@ void CardPrimitive::addType(char * _type_text)
|
||||
|
||||
void CardPrimitive::setSubtype(const string& value)
|
||||
{
|
||||
int id = Subtypes::subtypesList->find(value);
|
||||
//find the parent type for this card
|
||||
int parentType = 0;
|
||||
for (size_t i = 0; i < types.size(); ++i)
|
||||
{
|
||||
if (Subtypes::subtypesList->isType(types[i]))
|
||||
{
|
||||
parentType = types[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
int id = Subtypes::subtypesList->add(value, parentType);
|
||||
addType(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -1814,6 +1814,14 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
||||
return a;
|
||||
}
|
||||
|
||||
//Lose subtypes of a given type
|
||||
vector<string> splitLoseTypes = parseBetween(s, "losesubtypesof(", ")");
|
||||
if (splitLoseTypes.size())
|
||||
{
|
||||
int parentType = Subtypes::subtypesList->find(splitLoseTypes[1]);
|
||||
return NEW ALoseSubtypes(id, card, target, parentType);
|
||||
}
|
||||
|
||||
//Cast/Play Restrictions
|
||||
for (size_t i = 0; i < kMaxCastKeywordsCount; ++i)
|
||||
{
|
||||
|
||||
@@ -78,25 +78,13 @@ int MTGAllCards::processConfLine(string &s, MTGCard *card, CardPrimitive * primi
|
||||
string value = val;
|
||||
//Specific Abilities
|
||||
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
|
||||
while (value.size())
|
||||
vector<string> values = split(value, ',');
|
||||
for (size_t values_i = 0; values_i < values.size(); ++values_i)
|
||||
{
|
||||
string attribute;
|
||||
size_t found2 = value.find(',');
|
||||
if (found2 != string::npos)
|
||||
{
|
||||
attribute = value.substr(0, found2);
|
||||
value = value.substr(found2 + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
attribute = value;
|
||||
value = "";
|
||||
}
|
||||
|
||||
for (int j = Constants::NB_BASIC_ABILITIES - 1; j >= 0; --j)
|
||||
{
|
||||
size_t found = attribute.find(Constants::MTGBasicAbilities[j]);
|
||||
if (found != string::npos)
|
||||
if (values[values_i].find(Constants::MTGBasicAbilities[j]) != string::npos)
|
||||
{
|
||||
primitive->basicAbilities[j] = 1;
|
||||
break;
|
||||
@@ -249,7 +237,7 @@ int MTGAllCards::processConfLine(string &s, MTGCard *card, CardPrimitive * primi
|
||||
card->setRarity(val[0]);
|
||||
}
|
||||
break;
|
||||
case 's': //subtype
|
||||
case 's': //subtype, suspend
|
||||
{
|
||||
if (s.find("suspend") != string::npos)
|
||||
{
|
||||
@@ -269,21 +257,9 @@ int MTGAllCards::processConfLine(string &s, MTGCard *card, CardPrimitive * primi
|
||||
else
|
||||
{
|
||||
if (!primitive) primitive = NEW CardPrimitive();
|
||||
while (true)
|
||||
{
|
||||
char* found = strchr(val, ' ');
|
||||
if (found)
|
||||
{
|
||||
string value(val, found - val);
|
||||
primitive->setSubtype(value);
|
||||
val = found + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
primitive->setSubtype(val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
vector<string> values = split(val, ' ');
|
||||
for (size_t values_i = 0; values_i < values.size(); ++values_i)
|
||||
primitive->setSubtype(values[values_i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -299,21 +275,9 @@ int MTGAllCards::processConfLine(string &s, MTGCard *card, CardPrimitive * primi
|
||||
primitive->setText(val);
|
||||
else if (0 == strcmp("type", key))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
char* found = strchr(val, ' ');
|
||||
if (found)
|
||||
{
|
||||
string value(val, found - val);
|
||||
primitive->setType(value);
|
||||
val = found + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
primitive->setType(val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
vector<string> values = split(val, ' ');
|
||||
for (size_t values_i = 0; values_i < values.size(); ++values_i)
|
||||
primitive->setType(values[values_i]);
|
||||
}
|
||||
else if (0 == strcmp("toughness", key)) primitive->setToughness(atoi(val));
|
||||
break;
|
||||
|
||||
@@ -15,9 +15,16 @@ Subtypes::Subtypes()
|
||||
find("Land");
|
||||
find("Artifact");
|
||||
find("Legendary");
|
||||
find("Snow");
|
||||
find("Basic");
|
||||
find("World");
|
||||
find("Equipment");
|
||||
find("Aura");
|
||||
find("Planeswalker");
|
||||
find("Tribal");
|
||||
find("Plane");
|
||||
find("Scheme");
|
||||
find("Vanguard");
|
||||
}
|
||||
|
||||
int Subtypes::find(string value, bool forceAdd)
|
||||
@@ -32,8 +39,57 @@ int Subtypes::find(string value, bool forceAdd)
|
||||
return id;
|
||||
}
|
||||
|
||||
// Adds a subtype to the list, and associated it with a parent type.
|
||||
//The association can happen only once, a subtype is then definitely associated to its parent type.
|
||||
// If you associate "goblin" to "creature", trying to associate "goblin" to "land" afterwards will fail. "goblin" will stay associated to its first parent.
|
||||
int Subtypes::add(string value, int parentType)
|
||||
{
|
||||
int subtype = find(value);
|
||||
if (parentType && isSubType(subtype) && !subtypesToType[subtype])
|
||||
subtypesToType[subtype] = parentType;
|
||||
return subtype;
|
||||
}
|
||||
|
||||
string Subtypes::find(unsigned int id)
|
||||
{
|
||||
if (valuesById.size() < id || !id) return "";
|
||||
return valuesById[id - 1];
|
||||
}
|
||||
|
||||
bool Subtypes::isSubtypeOfType(string subtype, string type)
|
||||
{
|
||||
unsigned int subtypeInt = find(subtype);
|
||||
unsigned int typeInt = find(type);
|
||||
return isSubtypeOfType(subtypeInt, typeInt);
|
||||
}
|
||||
bool Subtypes::isSubtypeOfType(unsigned int subtype, unsigned int type)
|
||||
{
|
||||
return (subtypesToType[subtype] == type);
|
||||
}
|
||||
|
||||
bool Subtypes::isSuperType(int type)
|
||||
{
|
||||
return (type == TYPE_BASIC || type == TYPE_WORLD || type == TYPE_SNOW || type == TYPE_LEGENDARY);
|
||||
}
|
||||
|
||||
bool Subtypes::isType(int type)
|
||||
{
|
||||
return (
|
||||
type == TYPE_CREATURE ||
|
||||
type == TYPE_ENCHANTMENT ||
|
||||
type == TYPE_SORCERY ||
|
||||
type == TYPE_INSTANT ||
|
||||
type == TYPE_LAND ||
|
||||
type == TYPE_ARTIFACT ||
|
||||
type ==TYPE_PLANESWALKER ||
|
||||
type == TYPE_TRIBAL ||
|
||||
type == TYPE_PLANE ||
|
||||
type == TYPE_SCHEME ||
|
||||
type == TYPE_VANGUARD
|
||||
);
|
||||
}
|
||||
|
||||
bool Subtypes::isSubType(int type)
|
||||
{
|
||||
return (!isSuperType(type) && !isType(type));
|
||||
}
|
||||
Reference in New Issue
Block a user