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:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -287,6 +287,7 @@ void MTGCardInstance::initMTGCI()
|
||||
imprintR = 0;
|
||||
imprintB = 0;
|
||||
imprintW = 0;
|
||||
foretellTurn = -1;
|
||||
bushidoPoints = 0;
|
||||
modularPoints = 0;
|
||||
entersBattlefield = 0;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user