Fixed WAR, C18, PAL00, UST set, added/fixed primitives, improved Android downloader, implemented Foretell ability, improved castcard keyword, improved "can play" restriction, improved primitives parsed in order to allow the nesting of transforms, ability$! reveal, scry, pay, grant keywords.

This commit is contained in:
Vittorio Alfieri
2021-01-14 17:51:13 +01:00
parent 69885cf90a
commit 91d19e2852
19 changed files with 340 additions and 312 deletions
+43 -5
View File
@@ -2025,6 +2025,44 @@ AAImprint * AAImprint::clone() const
return NEW AAImprint(*this);
}
//AAForetell
AAForetell::AAForetell(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, _id, _source, _cost, 0)
{
target = _target;
}
int AAForetell::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
if(_target->mutation && _target->parentCards.size() > 0) return 0; // Mutated down cards cannot be foretell, they will follow the fate of top-card
Player * p = _target->controller();
if(p){
MTGCardInstance * tmp = p->game->putInExile(_target);
if(tmp)
tmp->foretellTurn = source->getObserver()->turn;
}
while(_target->next)
_target = _target->next;
return 1;
}
return 0;
}
const string AAForetell::getMenuText()
{
return "Foretell";
}
AAForetell * AAForetell::clone() const
{
return NEW AAForetell(*this);
}
//Counters
AACounter::AACounter(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target,string counterstring, const char * _name, int power, int toughness,
int nb,int maxNb, ManaCost * cost) :
@@ -4076,7 +4114,7 @@ int AAFlip::resolve()
while (_target->next)
_target = _target->next;
if(forcetype != "" && _target) // Added to flip Zendikar Rising Modal Double Faced cards.
if(forcetype != "" && _target) // Added to flip Modal Double Faced cards (e.g. Zendikar Rising).
{
for (int i = ((int)_target->types.size())-1; i >= 0; --i)
_target->removeType(_target->types[i]);
@@ -4224,7 +4262,7 @@ int AAFlip::resolve()
else
_target->isFacedown = true;
if(forcetype != "" && _target) // Added to flip Zendikar Rising Modal Double Faced cards.
if(forcetype != "" && _target && _target->isPermanent()) // Added to flip Modal Double Faced cards (e.g. Zendikar Rising).
{
_target->castMethod = Constants::CAST_NORMALLY;
_target->controller()->game->battlefield->addCard(_target);
@@ -8849,8 +8887,8 @@ AEquip * AEquip::clone() const
}
// casting a card for free, or casting a copy of a card.
AACastCard::AACastCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target,bool _restricted,bool _copied,bool asNormal,string _namedCard,string _name,bool _noEvent,bool putinplay,bool madness, bool alternative, int kicked) :
MTGAbility(observer, _id, _source),restricted(_restricted),asCopy(_copied),normal(asNormal),cardNamed(_namedCard),nameThis(_name),noEvent(_noEvent),putinplay(putinplay), asNormalMadness(madness), alternative(alternative), kicked(kicked)
AACastCard::AACastCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target,bool _restricted,bool _copied,bool asNormal,string _namedCard,string _name,bool _noEvent,bool putinplay,bool madness, bool alternative, int kicked, bool flipped) :
MTGAbility(observer, _id, _source),restricted(_restricted),asCopy(_copied),normal(asNormal),cardNamed(_namedCard),nameThis(_name),noEvent(_noEvent),putinplay(putinplay), asNormalMadness(madness), alternative(alternative), kicked(kicked), flipped(flipped)
{
target = _target;
andAbility = NULL;
@@ -9148,7 +9186,7 @@ int AACastCard::resolveSpell()
spell = NEW Spell(game, 0,copy,NULL,NULL, 1);
spell->resolve();
}
else
else if(!flipped)
spell = game->mLayers->stackLayer()->addSpell(copy, NULL, NULL, 1, 0);
}
+5
View File
@@ -13,6 +13,7 @@ CardDescriptor::CardDescriptor()
counterToughness = 0;
counterNB = 0;
mode = CD_AND;
foretoldComparisonMode = COMPARISON_NONE;
kickedComparisonMode = COMPARISON_NONE;
powerComparisonMode = COMPARISON_NONE;
toughnessComparisonMode = COMPARISON_NONE;
@@ -159,6 +160,8 @@ MTGCardInstance * CardDescriptor::match_or(MTGCardInstance * card)
}
// Quantified restrictions are always AND-ed:
if (foretoldComparisonMode && !valueInRange(foretoldComparisonMode, card->foretellTurn, foretellTurn))
return NULL;
if (kickedComparisonMode && !valueInRange(kickedComparisonMode, card->kicked, kicked))
return NULL;
if (powerComparisonMode && !valueInRange(powerComparisonMode, card->getPower(), power))
@@ -204,6 +207,8 @@ MTGCardInstance * CardDescriptor::match_and(MTGCardInstance * card)
match = NULL;
}
if (foretoldComparisonMode && !valueInRange(foretoldComparisonMode, card->foretellTurn, foretellTurn))
match = NULL;
if (kickedComparisonMode && !valueInRange(kickedComparisonMode, card->kicked, kicked))
match = NULL;
if (powerComparisonMode && !valueInRange(powerComparisonMode, card->getPower(), power))
+9 -2
View File
@@ -257,7 +257,8 @@ void CardGui::Render()
if(game)
{
if((card->has(Constants::CANPLAYFROMEXILE)||card->has(Constants::PAYZERO))||
((card->has(Constants::CANPLAYFROMGRAVEYARD) || card->has(Constants::TEMPFLASHBACK) || card->getManaCost()->getFlashback()) && game->isInGrave(card)))
((card->has(Constants::CANPLAYFROMGRAVEYARD) || card->has(Constants::TEMPFLASHBACK) || card->getManaCost()->getFlashback()) && game->isInGrave(card)) ||
(card->has(Constants::FORETELL) && card->foretellTurn > -1 && game->turn > card->foretellTurn && game->isInExile(card)))
fakeborder->SetColor(ARGB((int)(actA),7,235,7));//green border
else
fakeborder->SetColor(ARGB((int)(actA),15,15,15));
@@ -1485,6 +1486,12 @@ bool CardGui::FilterCard(MTGCard * _card,string filter)
cd.unsecureSetTapped(1);
}
}
//Has been foretold
else if (attribute.find("foretold") != string::npos)
{
cd.foretellTurn = comparisonCriterion;
cd.foretoldComparisonMode = comparisonMode;
}
//Has been kicked
else if (attribute.find("kicked") != string::npos)
{
@@ -1550,7 +1557,7 @@ bool CardGui::FilterCard(MTGCard * _card,string filter)
cd.unsecuresetrecent(1);
}
}
else if (attribute.find("geared") != string::npos)
else if (attribute.find("geared") != string::npos || attribute.find("equipped") != string::npos)
{
if (minus)
{
+91 -32
View File
@@ -825,19 +825,25 @@ int AbilityFactory::parseCastRestrictions(MTGCardInstance * card, Player * playe
if(!found)
return 0;
}
check = restriction[i].find("can play land");
check = restriction[i].find("can play");
if(check != string::npos)
{
bool isLand = card->hasType("Land");
if(!card->getId())
return 0; //Fixed a crash when AI plays.
string type = restriction[i];
type = type.replace(0,9,"");
MTGCardInstance* cardDummy = card->clone();
for (int i = ((int)cardDummy->types.size())-1; i >= 0; --i)
cardDummy->removeType(cardDummy->types[i]);
cardDummy->addType(type);
bool canplay = true;
if(!isLand)
card->addType("Land");
if (observer->currentActionPlayer->game->playRestrictions->canPutIntoZone(card, observer->currentActionPlayer->game->inPlay) == PlayRestriction::CANT_PLAY)
if (cardDummy->isLand() && observer->currentActionPlayer->game->playRestrictions->canPutIntoZone(cardDummy, observer->currentActionPlayer->game->inPlay) == PlayRestriction::CANT_PLAY)
canplay = false;
if (!card->getObserver() || !card->StackIsEmptyandSorcerySpeed())
if (!cardDummy->isLand() && observer->currentActionPlayer->game->playRestrictions->canPutIntoZone(cardDummy, observer->currentActionPlayer->game->stack) == PlayRestriction::CANT_PLAY)
canplay = false;
if(!isLand)
card->removeType("Land");
if (!cardDummy->hasType(Subtypes::TYPE_INSTANT) && !cardDummy->StackIsEmptyandSorcerySpeed())
canplay = false;
SAFE_DELETE(cardDummy);
if(!canplay)
return 0;
}
@@ -1780,56 +1786,93 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
vector<string>transfound = parseBetween(s,"newability[reveal:"," ");//if we are using reveal inside a newability, let transforms remove the string instead.
vector<string>abilfound = parseBetween(s, "ability$!name(reveal) reveal:", " ");
if(!abilfound.size())
abilfound = parseBetween(s, "ability$!reveal:", " ");//see above. this allows us to nest reveals inside these 2 other master classes. while also allowing us to nest them inside reveals.
abilfound = parseBetween(s, "ability$!reveal:", " ");//see above. this allows us to nest reveals inside these 2 other master classes. while also allowing us to nest them inside reveals.
found = s.find("pay(");
if (found != string::npos && storedPayString.empty() && !transPayfound.size())
{
size_t pos1 = s.find("transforms(("); // Try to handle pay ability inside ability$! or transforms keywords.
size_t pos2 = s.find("ability$!");
vector<string> splitMayPaystr = parseBetween(s, "pay(", ")", true);
if (splitMayPaystr.size())
{
storedPayString.append(splitMayPaystr[2]);
s = splitMayPaystr[0];
s.append("pay(");
s.append(splitMayPaystr[1]);
s.append(")");
}
if((pos1 == string::npos && pos2 == string::npos) || (pos2 != string::npos && pos1 != string::npos && found < pos1 && found < pos2) ||
(pos2 == string::npos && pos1 != string::npos && found < pos1) || (pos1 == string::npos && pos2 != string::npos && found < pos2)){
if (splitMayPaystr.size()){
storedPayString.append(splitMayPaystr[2]);
s = splitMayPaystr[0];
s.append("pay(");
s.append(splitMayPaystr[1]);
s.append(")");
}
} else
storedPayString.clear();
}
vector<string> splitGrant = parseBetween(s, "grant ", " grantend", false);
if (splitGrant.size() && storedAbilityString.empty())
{
storedAbilityString = splitGrant[1];
s = splitGrant[0];
s.append("grant ");
s.append(splitGrant[2]);
size_t pos1 = s.find("transforms(("); // Try to handle grant ability inside ability$! or transforms keywords.
size_t pos2 = s.find("ability$!");
size_t pos3 = s.find(splitGrant[1]);
if((pos1 == string::npos && pos2 == string::npos) || (pos2 != string::npos && pos1 != string::npos && pos3 < pos1 && pos3 < pos2) ||
(pos2 == string::npos && pos1 != string::npos && pos3 < pos1) || (pos1 == string::npos && pos2 != string::npos && pos3 < pos2)){
s = splitGrant[0];
s.append("grant ");
s.append(splitGrant[2]);
} else
storedAbilityString.clear();
}
vector<string> splitRevealx = parseBetween(s, "reveal:", " revealend", false);
if (!abilfound.size() && !transfound.size() && splitRevealx.size() && storedAbilityString.empty())
{
storedAbilityString = splitRevealx[1];
s = splitRevealx[0];
s.append("reveal: ");
s.append(splitRevealx[2]);
size_t pos1 = s.find("transforms(("); // Try to handle reveal ability inside ability$! or transforms keywords.
size_t pos2 = s.find("ability$!");
size_t pos3 = s.find(splitRevealx[1]);
if((pos1 == string::npos && pos2 == string::npos) || (pos2 != string::npos && pos1 != string::npos && pos3 < pos1 && pos3 < pos2) ||
(pos2 == string::npos && pos1 != string::npos && pos3 < pos1) || (pos1 == string::npos && pos2 != string::npos && pos3 < pos2)){
s = splitRevealx[0];
s.append("reveal: ");
s.append(splitRevealx[2]);
} else
storedAbilityString.clear();
}
vector<string> splitScryx = parseBetween(s, "scry:", " scryend", false);
if (splitScryx.size() && storedAbilityString.empty())
{
storedAbilityString = splitScryx[1];
s = splitScryx[0];
s.append("scry: ");
s.append(splitScryx[2]);
size_t pos1 = s.find("transforms(("); // Try to handle scry ability inside ability$! or transforms keywords.
size_t pos2 = s.find("ability$!");
size_t pos3 = s.find(splitScryx[1]);
if((pos1 == string::npos && pos2 == string::npos) || (pos2 != string::npos && pos1 != string::npos && pos3 < pos1 && pos3 < pos2) ||
(pos2 == string::npos && pos1 != string::npos && pos3 < pos1) || (pos1 == string::npos && pos2 != string::npos && pos3 < pos2)){
s = splitScryx[0];
s.append("scry: ");
s.append(splitScryx[2]);
} else
storedAbilityString.clear();
}
found = s.find("transforms((");
if (found != string::npos && storedString.empty())
{
size_t real_end = s.find("))", found);
size_t stypesStartIndex = found + 12;
storedString.append(s.substr(stypesStartIndex, real_end - stypesStartIndex).c_str());
s.erase(stypesStartIndex, real_end - stypesStartIndex);
size_t pos1 = s.find("ability$!"); // Try to handle transforms ability inside ability$! keyword.
if(pos1 == string::npos || found < pos1){
size_t real_end = s.find("))", found);
size_t stypesStartIndex = found + 12;
for(unsigned int i = stypesStartIndex; i < s.size(); i++){
size_t pos2 = s.find("transforms((", i); // Try to handle transforms ability inside transforms keyword.
if(pos2 != string::npos && pos2 < real_end){
i = pos2 + 11;
real_end = s.find("))", real_end + 2);
} else
break;
}
storedString.append(s.substr(stypesStartIndex, real_end - stypesStartIndex).c_str());
s.erase(stypesStartIndex, real_end - stypesStartIndex);
}
}
found = s.find("ability$!");
@@ -3099,6 +3142,15 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
}
}
//foretell
found = s.find("foretell");
if (found != string::npos)
{
MTGAbility * a = NEW AAForetell(observer, id, card, target);
a->oneShot = 1;
return a;
}
//phaseout
found = s.find("phaseout");
if (found != string::npos)
@@ -3267,6 +3319,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
bool sendNoEvent = splitCastCard[1].find("noevent") != string::npos;
bool putinplay = splitCastCard[1].find("putinplay") != string::npos;
bool alternative = splitCastCard[1].find("alternative") != string::npos;
bool flipped = splitCastCard[1].find("flipped") != string::npos;
string nameCard = "";
if(splitCastCard[1].find("named!:") != string::npos)
{
@@ -3285,7 +3338,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
kicked = atoi(splitCastKicked[1].c_str());
}
}
MTGAbility *a = NEW AACastCard(observer, id, card, target,withRestrictions,asCopy,asNormal,nameCard,newName,sendNoEvent,putinplay, asNormalMadness, alternative, kicked);
MTGAbility *a = NEW AACastCard(observer, id, card, target,withRestrictions,asCopy,asNormal,nameCard,newName,sendNoEvent,putinplay, asNormalMadness, alternative, kicked, flipped);
a->oneShot = false;
if(splitCastCard[1].find("trigger[to]") != string::npos)
{
@@ -4074,7 +4127,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
string transformsParamsString = "";
transformsParamsString.append(storedString);//the string between found and real end is removed at start.
found = transformsParamsString.find("transforms((");
found = transformsParamsString.find("transforms(("); // Try to handle transforms ability inside transforms keyword.
if (found != string::npos && extraTransforms.empty())
{
size_t real_end = transformsParamsString.find("))", found);
@@ -4120,10 +4173,16 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
size_t NewSkill = abilities[j].find("newability[");
size_t NewSkillEnd = abilities[j].find_last_of("]");
string newAbilities = abilities[j].substr(NewSkill + 11,NewSkillEnd - NewSkill - 11);
size_t pos = newAbilities.find("transforms(())"); // Try to handle transforms ability inside transforms keyword.
if(pos != string::npos)
newAbilities.replace(pos, 14, "transforms((" + storedString + "))");
newAbilitiesList.push_back(newAbilities);
newAbilityFound = true;
}
}
size_t pos = sabilities.find("transforms(())"); // Try to handle transforms ability inside transforms keyword.
if(pos != string::npos)
sabilities.replace(pos, 14, "transforms((" + storedString + "))");
if (oneShot || forceUEOT || forceForever)
return NEW ATransformerInstant(observer, id, card, target, stypes, sabilities,newpower,newpowerfound,newtoughness,newtoughnessfound,newAbilitiesList,newAbilityFound,forceForever,untilYourNextTurn,newName);
+1
View File
@@ -287,6 +287,7 @@ void MTGCardInstance::initMTGCI()
imprintR = 0;
imprintB = 0;
imprintW = 0;
foretellTurn = -1;
bushidoPoints = 0;
modularPoints = 0;
entersBattlefield = 0;
+2 -1
View File
@@ -204,7 +204,8 @@ const char* Constants::MTGBasicAbilities[] = {
"inplaytapdeath", //It goes back in play tapped after death.
"gainedexiledeath", //It goes to exile after death (use just to give add ability to instants and sorceries which originally have not, e.g. with transforms keyword)
"gainedhanddeath", //It goes to hand after death (use just to give add ability to instants and sorceries which originally have not, e.g. with transforms keyword)
"cycling" //It has cycling ability
"cycling", //It has cycling ability
"foretell" //It has foretell cost
};
map<string,int> Constants::MTGBasicAbilitiesMap;
+8 -2
View File
@@ -524,6 +524,12 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta
cd->unsecureSetTapped(1);
}
}
//Has been foretold
else if (attribute.find("foretold") != string::npos)
{
cd->foretellTurn = comparisonCriterion;
cd->foretoldComparisonMode = comparisonMode;
}
//Has been kicked
else if (attribute.find("kicked") != string::npos)
{
@@ -589,7 +595,7 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta
cd->unsecuresetrecent(1);
}
}
else if (attribute.find("geared") != string::npos)
else if (attribute.find("geared") != string::npos || attribute.find("equipped") != string::npos)
{
if (minus)
{
@@ -1002,7 +1008,7 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta
ctc->setAllZones();
return ctc;
}
else if (typeName.compare("mystored") == 0)
else if (typeName.compare("mysource") == 0)
{
return NEW CardTargetChooser(observer, card->storedSourceCard, card, zones, nbzones);
}