Added/Fixed primitives, improved the Double Face Modal cards management: now it's possibile to click on card to flip the side in odrer to read card infos such as name, manacost, text and types, improved the "moveto" keyword in order to allow the usage of the "temp" zone for removing unecessary cards from game (e.g. duplicated card generated from some dual face cards), added the option "nolegend" to the "copy" keyword in order to crerate copy of legendary cards that are not legendary (e.g. Echoing Equation), added the keywords "doublefacedeath" and "gaineddoublefacedeath" to send a card to temp zone after death (e.g. duplicated card generated from some dual face cards), added the keywords "lifefaker" to identify the cards wich modify the life increasement when a @lifeof triggers occours (e.g. Angel of Vitality).

This commit is contained in:
Vittorio Alfieri
2021-04-27 15:35:54 +02:00
parent e265dc3e7f
commit 6fb3feef72
13 changed files with 565 additions and 264 deletions
+6
View File
@@ -591,6 +591,12 @@ int PutInGraveyard::resolve()
card->controller()->game->putInZone(card, zone, card->owner->game->exile);
return 1;
}
if (card->basicAbilities[(int)Constants::DOUBLEFACEDEATH] || card->basicAbilities[(int)Constants::GAINEDDOUBLEFACEDEATH])
{
card->basicAbilities[(int)Constants::GAINEDDOUBLEFACEDEATH] = 0;
card->controller()->game->putInZone(card, zone, card->owner->game->temp);
return 1;
}
if (card->basicAbilities[(int)Constants::HANDDEATH] || card->basicAbilities[(int)Constants::GAINEDHANDDEATH])
{
card->basicAbilities[(int)Constants::GAINEDHANDDEATH] = 0;
+70 -4
View File
@@ -1921,11 +1921,12 @@ AALibraryBottom::~AALibraryBottom()
}
//AACopier
AACopier::AACopier(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
AACopier::AACopier(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost, string optionsList) :
ActivatedAbility(observer, _id, _source, _cost, 0)
{
target = _target;
andAbility = NULL;
options = optionsList;
isactivated = false;
}
@@ -1942,9 +1943,10 @@ int AACopier::resolve()
source->hasCopiedToken = tokencopied;
/*since we look for the real card it will not copy granted haste ability however for token we copy all*/
/*but how to do backup for token so we just copy the backup???*/
bool nolegend = options.find("nolegend") != string::npos; // Check if the copy has to be legendary or not. (e.g. Echoing Equation)
if(tokencopied && !_target->isACopier && !_target->getMTGId())
{
source->copy(_target->tokCard);
source->copy(_target->tokCard, nolegend);
//if the token doesn't have cda/dynamic pt then allow this...
if(!_target->isCDA)
{
@@ -1967,7 +1969,7 @@ int AACopier::resolve()
else
{
source->nameOrig = source->name; // Saves the orignal card name before become a copy
source->copy(_target);
source->copy(_target, nolegend);
}
source->isACopier = true;
source->copiedID = _target->copiedID;
@@ -4332,6 +4334,70 @@ AAMeld * AAMeld::clone() const
return NEW AAMeld(*this);
}
//Turn side of double faced cards
AATurnSide::AATurnSide(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, string SideName) :
ActivatedAbility(observer, id, card, 0), _SideName(SideName)
{
target = _target;
}
int AATurnSide::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
if (_target && _target->currentZone != _target->controller()->game->battlefield) // It's not allowed to turn side on battlefield.
{
if(_target->mutation && _target->parentCards.size() > 0) return 0; // Mutated down cards cannot be turned, they will follow the fate of top-card
MTGCard * fcard;
MTGCardInstance* sideCard;
if(!_target->isFlipped){
fcard = MTGCollection()->getCardByName(_SideName);
if(!fcard) return 0;
sideCard = NEW MTGCardInstance(fcard, _target->controller()->game);
_target->nameOrig = _target->name;
_target->name = sideCard->name;
if(!sideCard) return 0;
if(sideCard->getManaCost()){
if(_target->getManaCost()->getAlternative()){
sideCard->getManaCost()->setAlternative(NEW ManaCost());
sideCard->getManaCost()->getAlternative()->copy(_target->getManaCost()->getAlternative()); // Keep orignal alternative cost to cast card with other.
}
_target->getManaCost()->copy(sideCard->getManaCost()); // Show the other side cost mana symbols.
}
} else {
fcard = MTGCollection()->getCardByName(_target->nameOrig);
if(!fcard) return 0;
_target->name = _target->nameOrig;
_target->nameOrig = "";
sideCard = NEW MTGCardInstance(fcard, _target->controller()->game);
if(!sideCard) return 0;
if(sideCard->getManaCost()){
_target->getManaCost()->resetCosts();
_target->getManaCost()->copy(sideCard->getManaCost()); // Restore the original side cost mana symbols.
}
}
for (int i = ((int)_target->types.size())-1; i >= 0; --i) // Load all the types from the current side
_target->removeType(_target->types[i]);
for (int i = 0; i < ((int)sideCard->types.size()); i++)
_target->addType(sideCard->types[i]);
_target->text = sideCard->text;
_target->formattedText = sideCard->formattedText;
_target->isFlipped = !_target->isFlipped;
SAFE_DELETE(sideCard);
return 1;
}
return 0;
}
const string AATurnSide::getMenuText()
{
return "Flip Side";
}
AATurnSide * AATurnSide::clone() const
{
return NEW AATurnSide(*this);
}
// flip a card
AAFlip::AAFlip(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target,string flipStats, bool isflipcard, bool forcedcopy, string forcetype, bool backfromcopy) :
InstantAbility(observer, id, card, _target),flipStats(flipStats),isflipcard(isflipcard),forcedcopy(forcedcopy),forcetype(forcetype),backfromcopy(backfromcopy)
@@ -4345,7 +4411,7 @@ int AAFlip::resolve()
int activatedanyability = 0;
MTGCardInstance * Flipper = (MTGCardInstance*)source;
this->oneShot = true;
if(Flipper->isFlipped)
if(Flipper->isFlipped && forcetype == "")
{
game->removeObserver(this);
return 0;
+29 -1
View File
@@ -3148,7 +3148,13 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
found = s.find("copy");
if (found != string::npos)
{
MTGAbility * a = NEW AACopier(observer, id, card, target);
string options = "";
vector<string> splitOptions = parseBetween(s, "options(", ")");
if (splitOptions.size())
{
options = splitOptions[1];
}
MTGAbility * a = NEW AACopier(observer, id, card, target, NULL, options);
a->oneShot = 1;
a->canBeInterrupted = false;
((AACopier*)a)->isactivated = activated;
@@ -4297,6 +4303,21 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
return a;
}
//doubleside
vector<string> splitSide = parseBetween(s, "doubleside(", ")", true);
if (splitSide.size())
{
string splitSideName = "";
if (splitSide[1].size())
{
splitSideName = splitSide[1];
replace(splitSideName.begin(), splitSideName.end(), '^', ','); // To allow the usage of ^ instead of , char (e.g. using doubleside keyword inside transforms)
}
MTGAbility * a = NEW AATurnSide(observer, id, card, target, splitSideName);
a->oneShot = true;
return a;
}
//flip
vector<string> splitFlipStat = parseBetween(s, "flip(", ")", true);
if(splitFlipStat.size())
@@ -5088,6 +5109,8 @@ int AbilityFactory::abilityEfficiency(MTGAbility * a, Player * p, int mode, Targ
badAbilities[(int)Constants::GAINEDHANDDEATH] = true;
badAbilities[(int)Constants::INPLAYDEATH] = true;
badAbilities[(int)Constants::INPLAYTAPDEATH] = true;
badAbilities[(int)Constants::DOUBLEFACEDEATH] = true;
badAbilities[(int)Constants::GAINEDDOUBLEFACEDEATH] = true;
badAbilities[(int)Constants::WEAK] = true;
badAbilities[(int)Constants::NOLIFEGAIN] = true;
badAbilities[(int)Constants::NOLIFEGAINOPPONENT] = true;
@@ -5906,6 +5929,11 @@ void AbilityFactory::addAbilities(int _id, Spell * spell)
card->basicAbilities[(int)Constants::GAINEDEXILEDEATH] = 0;
card->controller()->game->putInZone(card, card->getCurrentZone(), card->owner->game->exile);
}
else if (card->basicAbilities[(int)Constants::DOUBLEFACEDEATH] || card->basicAbilities[(int)Constants::GAINEDDOUBLEFACEDEATH])
{
card->basicAbilities[(int)Constants::GAINEDDOUBLEFACEDEATH] = 0;
card->controller()->game->putInZone(card, card->getCurrentZone(), card->owner->game->temp);
}
else if (card->basicAbilities[(int)Constants::HANDDEATH] || card->basicAbilities[(int)Constants::GAINEDHANDDEATH])
{
card->basicAbilities[(int)Constants::GAINEDHANDDEATH] = 0;
+9 -2
View File
@@ -97,7 +97,7 @@ MTGCardInstance::MTGCardInstance(MTGCard * card, MTGPlayerCards * arg_belongs_to
return snapShot;
}
void MTGCardInstance::copy(MTGCardInstance * card)
void MTGCardInstance::copy(MTGCardInstance * card, bool nolegend)
{
MTGCard * source = NULL;
if(card->isACopier && card->copiedID)
@@ -130,7 +130,8 @@ void MTGCardInstance::copy(MTGCardInstance * card)
types.clear();//reset types.. fix copying man lands... the copier becomes an unanimated land...
for (size_t i = 0; i < data->types.size(); i++)
{
types.push_back(data->types[i]);
if(!(nolegend && data->types[i] == Subtypes::TYPE_LEGENDARY)) // Check if the copy has to be legendary or not. (e.g. Echoing Equation)
types.push_back(data->types[i]);
}
colors = data->colors;
@@ -514,6 +515,12 @@ int MTGCardInstance::toGrave( bool forced )
basicAbilities[(int)Constants::GAINEDEXILEDEATH] = 0;
return 1;
}
if (basicAbilities[(int)Constants::DOUBLEFACEDEATH] || basicAbilities[(int)Constants::GAINEDDOUBLEFACEDEATH])
{
p->game->putInZone(this, p->game->inPlay, owner->game->temp);
basicAbilities[(int)Constants::GAINEDDOUBLEFACEDEATH] = 0;
return 1;
}
if (basicAbilities[(int)Constants::HANDDEATH] || basicAbilities[(int)Constants::GAINEDHANDDEATH])
{
p->game->putInZone(this, p->game->inPlay, owner->game->hand);
+4 -1
View File
@@ -211,7 +211,10 @@ const char* Constants::MTGBasicAbilities[] = {
"twoboast", //It has boast twice ability (e.g. Birgi, God of Storytelling)
"replacescry", //It has scry replacement ability
"hasnokicker", //Kicker cost is not a real kicker cost (eg. cards with Fuse cost)
"undamageable" //It cannot be damaged by any source
"undamageable", //It cannot be damaged by any source
"lifefaker", //It's a card wich modify the life increasement when a @lifeof triggers occours (e.g. Angel of Vitality)
"doublefacedeath", //It goes to temp zone after death (e.g. Double face card)
"gaineddoublefacedeath" //It goes to temp after death (use just to give add ability to instants and sorceries which originally have not, e.g. with transforms keyword)
};
map<string,int> Constants::MTGBasicAbilitiesMap;
+33 -1
View File
@@ -404,6 +404,12 @@ MTGCardInstance * MTGPlayerCards::putInGraveyard(MTGCardInstance * card)
ret->basicAbilities[(int)Constants::GAINEDHANDDEATH] = 0;
return ret;
}
else if (card->getCurrentZone() != card->controller()->game->hand && (card->basicAbilities[(int)Constants::DOUBLEFACEDEATH] || card->basicAbilities[(int)Constants::GAINEDDOUBLEFACEDEATH]))
{
MTGCardInstance* ret = putInZone(card, card->getCurrentZone(), card->owner->game->temp);
ret->basicAbilities[(int)Constants::GAINEDDOUBLEFACEDEATH] = 0;
return ret;
}
else if (card->getCurrentZone() != card->controller()->game->hand && (card->basicAbilities[(int)Constants::INPLAYDEATH] || card->basicAbilities[(int)Constants::INPLAYTAPDEATH]))
{
MTGCardInstance* ret = putInZone(card, card->getCurrentZone(), card->owner->game->battlefield);
@@ -823,6 +829,7 @@ MTGCardInstance * MTGGameZone::removeCard(MTGCardInstance * card, int createCopy
copy->basicAbilities[Constants::ISCOMMANDER] = card->basicAbilities[Constants::ISCOMMANDER];
copy->basicAbilities[Constants::GAINEDEXILEDEATH] = card->basicAbilities[Constants::GAINEDEXILEDEATH];
copy->basicAbilities[Constants::GAINEDHANDDEATH] = card->basicAbilities[Constants::GAINEDHANDDEATH];
copy->basicAbilities[Constants::GAINEDDOUBLEFACEDEATH] = card->basicAbilities[Constants::GAINEDDOUBLEFACEDEATH];
copy->damageInflictedAsCommander = card->damageInflictedAsCommander;
copy->numofcastfromcommandzone = card->numofcastfromcommandzone;
for (int i = 0; i < ManaCost::MANA_PAID_WITH_BESTOW +1; i++)
@@ -1358,6 +1365,13 @@ MTGGameZone * MTGGameZone::intToZone(int zoneId, Player * p, Player * p2)
case COMMANDZONE:
return p->game->commandzone;
case MY_TEMP:
return p->game->temp;
case OPPONENT_TEMP:
return p->opponent()->game->temp;
case TEMP:
return p->game->temp;
}
if (!p2) return NULL;
switch (zoneId)
@@ -1389,6 +1403,9 @@ MTGGameZone * MTGGameZone::intToZone(int zoneId, Player * p, Player * p2)
case TARGET_CONTROLLER_COMMANDZONE:
return p2->game->commandzone;
case TARGET_CONTROLLER_TEMP:
return p2->game->temp;
default:
return NULL;
}
@@ -1520,6 +1537,17 @@ MTGGameZone * MTGGameZone::intToZone(GameObserver *g, int zoneId, MTGCardInstanc
return source->playerTarget->game->commandzone;
else return source->controller()->game->commandzone;
case TARGET_OWNER_TEMP:
return target->owner->game->temp;
case TEMP:
return target->owner->game->temp;
case OWNER_TEMP:
return target->owner->game->temp;
case TARGETED_PLAYER_TEMP:
if (source->playerTarget)
return source->playerTarget->game->temp;
else return source->controller()->game->temp;
default:
return NULL;
}
@@ -1553,6 +1581,8 @@ int MTGGameZone::zoneStringToId(string zoneName)
"mycommandzone", "opponentcommandzone", "targetownercommandzone", "targetcontrollercommandzone", "ownercommandzone", "commandzone","targetedpersonscommandzone",
"mytemp", "opponenttemp", "targetownertemp", "targetcontrollertemp", "ownertemp", "temp","targetedpersonstemp",
};
int values[] = { MY_GRAVEYARD, OPPONENT_GRAVEYARD, TARGET_OWNER_GRAVEYARD, TARGET_CONTROLLER_GRAVEYARD, OWNER_GRAVEYARD,
@@ -1578,7 +1608,9 @@ int MTGGameZone::zoneStringToId(string zoneName)
MY_SIDEBOARD, OPPONENT_SIDEBOARD, TARGET_OWNER_SIDEBOARD, TARGET_CONTROLLER_SIDEBOARD, OWNER_SIDEBOARD, SIDEBOARD,TARGETED_PLAYER_SIDEBOARD,
MY_COMMANDZONE, OPPONENT_COMMANDZONE, TARGET_OWNER_COMMANDZONE, TARGET_CONTROLLER_COMMANDZONE, OWNER_COMMANDZONE, COMMANDZONE,TARGETED_PLAYER_COMMANDZONE };
MY_COMMANDZONE, OPPONENT_COMMANDZONE, TARGET_OWNER_COMMANDZONE, TARGET_CONTROLLER_COMMANDZONE, OWNER_COMMANDZONE, COMMANDZONE,TARGETED_PLAYER_COMMANDZONE,
MY_TEMP, OPPONENT_TEMP, TARGET_OWNER_TEMP, TARGET_CONTROLLER_TEMP, OWNER_TEMP, TEMP,TARGETED_PLAYER_TEMP };
int max = sizeof(values) / sizeof *(values);