Added/fixed primitives, refactored and improved almost all transforming human cards (included all the Werewolves), improved "flip ability and "doubleside" ability adding a new "backside" option, fixed a bug on "doubleside" ability for planeswalkers, added "backside=" key to CardPrimitive in order to specify the other side of double-faced cards, added "hasbackside" option to target chooser in order to find cards which have a back side, added "dualfaced" that return 1 if a card has a backside card, fixed loyalty counter ability on planeswalker flip (is was not resolving correctly), changed type of damageToController, damageToOpponent, damageToCreature, wasDealtDamage, combatdamageToOpponent from bool to int in order to retrieve those values if needed, added "totaldmg" keyword that returns the total amount of damage dealt by a creature in the current turn, added new restriction "coven in order to check if a player controls three or more creatures with different powers, added new ability "hasdisturb" when the Retrace cost of a card is a disturb cost (e.g. "Beloved Beggar").

This commit is contained in:
Vittorio Alfieri
2021-09-04 01:48:47 +02:00
parent 236f677f2a
commit cc16db7256
23 changed files with 768 additions and 492 deletions
+18 -4
View File
@@ -4609,12 +4609,15 @@ int AATurnSide::resolve()
if(_target->controller()->isAI() && _target->isFlipped) _target->isFlipped = false; // If it's AI calling back we just have to reset isFLipped flag and then return.
if(!_target->isFlipped && _SideName == "") return 0; // No need to turn front if card has not been flipped before.
if(!_target->isFlipped){
if(_SideName == "backside" && _target->backSide != "")
_SideName = _target->backSide; // Added to allow to turn a card on its backside.
fcard = MTGCollection()->getCardByName(_SideName);
if(!fcard) return 0;
sideCard = NEW MTGCardInstance(fcard, _target->controller()->game);
_target->nameOrig = _target->name;
_target->name = sideCard->name;
_target->setName(sideCard->name);
_target->backSide = sideCard->backSide;
if(!sideCard) return 0;
if(sideCard->getManaCost()){
if(_target->getManaCost()->getAlternative()){
@@ -4625,6 +4628,10 @@ int AATurnSide::resolve()
sideCard->getManaCost()->setMorph(NEW ManaCost());
sideCard->getManaCost()->getMorph()->copy(_target->getManaCost()->getMorph()); // Keep orignal morph cost to cast the original card with morph.
}
if(_target->getManaCost()->getRetrace()){
sideCard->getManaCost()->setRetrace(NEW ManaCost());
sideCard->getManaCost()->getRetrace()->copy(_target->getManaCost()->getRetrace()); // Keep orignal retrace cost to cast the original card with retrace (e.g. cards with disturb cost).
}
_target->getManaCost()->copy(sideCard->getManaCost()); // Show the other side cost mana symbols.
if(_target->numofcastfromcommandzone > 0){ //In case you turn side of a previuosly casted commander
_target->getManaCost()->add(Constants::MTG_COLOR_ARTIFACT,2*_target->numofcastfromcommandzone);
@@ -4733,9 +4740,11 @@ int AAFlip::resolve()
if(flipStats.size())
{
if(flipStats == "myorigname" && _target->nameOrig != "")
flipStats = _target->nameOrig; // Added to undo the copy effect at end of turn for a generic card (es. Shapeshifter transformations).
flipStats = _target->nameOrig; // Added to undo the copy effect at end of turn for a generic card (e.g. Shapeshifter transformations).
else if(flipStats == "chosenname" && _target->chooseaname != "")
flipStats = _target->chooseaname; // Added to allow the transformation of a card in a choosen name.
else if(flipStats == "backside" && _target->backSide != "")
flipStats = _target->backSide; // Added to allow the transformation of a card in its backside (e.g. Werewolves transformations).
MTGCard * fcard = MTGCollection()->getCardByName(flipStats);
if(!fcard) return 0;
MTGCardInstance * myFlip = NEW MTGCardInstance(fcard, _target->controller()->game);
@@ -4747,6 +4756,7 @@ int AAFlip::resolve()
_target->nameOrig = nameOrig; // Saves the orignal card name before to flip the card.
_target->name = myFlip->name;
_target->setName(myFlip->name);
_target->backSide = myFlip->backSide;
if(!isflipcard)//transform card
{
_target->getManaCost()->resetCosts();
@@ -4812,7 +4822,13 @@ int AAFlip::resolve()
{
if (a->oneShot)
{
a->resolve();
if(_target->hasType(Subtypes::TYPE_PLANESWALKER)){ // Fix to don't let planeswalker die on flip (since the counter ability is not resolving correctly during flip).
AACounter * tmp = dynamic_cast<AACounter *>(a);
if(tmp && tmp->counterstring.find("loyalty") != string::npos){
for (int j = 0; j < tmp->nb; j++)
_target->counters->addCounter("loyalty", 0, 0, true);
} else a->resolve();
} else a->resolve();
SAFE_DELETE(a);
}
else
@@ -4820,9 +4836,7 @@ int AAFlip::resolve()
a->addToGame();
MayAbility * dontAdd = dynamic_cast<MayAbility*>(a);
if(!dontAdd)
{
_target->cardsAbilities.push_back(a);
}
}
}
}
+22 -11
View File
@@ -24,6 +24,7 @@ CardDescriptor::CardDescriptor()
zposition = -1;
hasKickerCost = 0;
hasFlashbackCost = 0;
hasBackSide = 0;
hasXCost = 0;
compareName ="";
nameComparisonMode = COMPARISON_NONE;
@@ -72,6 +73,11 @@ void CardDescriptor::unsecureSetHasFlashbackCost(int k)
hasFlashbackCost = k;
}
void CardDescriptor::unsecureSetHasBackSide(int k)
{
hasBackSide = k;
}
void CardDescriptor::unsecureSetTapped(int i)
{
tapped = i;
@@ -277,6 +283,11 @@ MTGCardInstance * CardDescriptor::match(MTGCardInstance * card)
match = NULL;
}
if ((hasBackSide == -1 && card->backSide != "") || (hasBackSide == 1 && card->backSide == ""))
{
match = NULL;
}
if ((hasXCost == -1 && card->getManaCost()->hasX()) || (hasXCost == 1 && !card->getManaCost()->hasX()))
{
match = NULL;
@@ -400,13 +411,13 @@ MTGCardInstance * CardDescriptor::match(MTGCardInstance * card)
{
match = NULL;
}
if ((CDdamaged == -1 && card->wasDealtDamage) || (CDdamaged == 1 && !card->wasDealtDamage))
if ((CDdamaged == -1 && card->wasDealtDamage > 0) || (CDdamaged == 1 && card->wasDealtDamage == 0))
{
match = NULL;
}
if ((CDdamager == -1 && (card->damageToOpponent || card->damageToController || card->damageToCreature))
|| (CDdamager == 1 && !(card->damageToOpponent || card->damageToController || card->damageToCreature)))
if ((CDdamager == -1 && (card->damageToOpponent > 0 || card->damageToController > 0 || card->damageToCreature > 0))
|| (CDdamager == 1 && !(card->damageToOpponent > 0 || card->damageToController > 0 || card->damageToCreature > 0)))
{
match = NULL;
}
@@ -414,17 +425,17 @@ MTGCardInstance * CardDescriptor::match(MTGCardInstance * card)
if(CDopponentDamaged == -1 || CDopponentDamaged == 1 || CDcontrollerDamaged == -1 || CDcontrollerDamaged == 1)
{
Player * p = card->controller();
if ((CDopponentDamaged == -1 && card->damageToOpponent && card->controller() == p)
|| (CDopponentDamaged == 1 && !card->damageToOpponent && card->controller() == p)
|| (CDopponentDamaged == -1 && card->damageToController && card->controller() == p->opponent())
|| (CDopponentDamaged == 1 && !card->damageToController && card->controller() == p->opponent()))
if ((CDopponentDamaged == -1 && card->damageToOpponent > 0 && card->controller() == p)
|| (CDopponentDamaged == 1 && card->damageToOpponent == 0 && card->controller() == p)
|| (CDopponentDamaged == -1 && card->damageToController > 0 && card->controller() == p->opponent())
|| (CDopponentDamaged == 1 && card->damageToController == 0 && card->controller() == p->opponent()))
{
match = NULL;
}
if ((CDcontrollerDamaged == -1 && card->damageToController && card->controller() == p)
|| (CDcontrollerDamaged == 1 && !card->damageToController && card->controller() == p)
|| (CDcontrollerDamaged == -1 && card->damageToOpponent && card->controller() == p->opponent())
|| (CDcontrollerDamaged == 1 && !card->damageToOpponent && card->controller() == p->opponent()))
if ((CDcontrollerDamaged == -1 && card->damageToController > 0 && card->controller() == p)
|| (CDcontrollerDamaged == 1 && card->damageToController == 0 && card->controller() == p)
|| (CDcontrollerDamaged == -1 && card->damageToOpponent > 0 && card->controller() == p->opponent())
|| (CDcontrollerDamaged == 1 && card->damageToOpponent == 0 && card->controller() == p->opponent()))
{
match = NULL;
}
+15 -3
View File
@@ -444,11 +444,11 @@ void CardGui::Render()
renderer->DrawRect(actX - (13 * actZ), actY + ymody + 4 * actZ, 25.5f * actZ, 14 * actZ,
ARGB(((static_cast<unsigned char>(actA))),20,20,20));
//damaged or buffed or powered down
if(card->wasDealtDamage && card->life <= 2)
if(card->wasDealtDamage > 0 && card->life <= 2)
mFont->SetColor(ARGB(static_cast<unsigned char>(actA),255,0,0));//red critical and damaged
else if(!card->wasDealtDamage && card->pbonus < 0)
else if(card->wasDealtDamage == 0 && card->pbonus < 0)
mFont->SetColor(ARGB(static_cast<unsigned char>(actA),216,191,216));//thistle powered down
else if(!card->wasDealtDamage && card->pbonus >= 3)
else if(card->wasDealtDamage == 0 && card->pbonus >= 3)
mFont->SetColor(ARGB(static_cast<unsigned char>(actA),255,255,0));//yellow buff
else if(card->hasType("legendary") && card->hasType("eldrazi") && !card->has(Constants::CHANGELING))
mFont->SetColor(ARGB(static_cast<unsigned char>(actA),238,130,238));//violet legendary eldrazi
@@ -1532,6 +1532,18 @@ bool CardGui::FilterCard(MTGCard * _card,string filter)
cd.unsecureSetHasFlashbackCost(1);
}
}
//Has backside
else if (attribute.find("hasbackside") != string::npos)
{
if (minus)
{
cd.unsecureSetHasBackSide(-1);
}
else
{
cd.unsecureSetHasBackSide(1);
}
}
//Token
else if (attribute.find("token") != string::npos)
{
+5 -5
View File
@@ -255,18 +255,18 @@ int Damage::resolve()
target->nonCombatDamage += damage;
}
if (target->type_as_damageable == Damageable::DAMAGEABLE_MTGCARDINSTANCE && (((MTGCardInstance*)target)->basicAbilities[Constants::UNDAMAGEABLE] == 0)){
((MTGCardInstance*)target)->wasDealtDamage = true;
((MTGCardInstance*)source)->damageToCreature = true;
((MTGCardInstance*)target)->wasDealtDamage += damage;
((MTGCardInstance*)source)->damageToCreature += damage;
}
if (target->type_as_damageable == Damageable::DAMAGEABLE_PLAYER)
{
if(target == source->controller())
{
((MTGCardInstance*)source)->damageToController = true;
((MTGCardInstance*)source)->damageToController += damage;
}
else
{
((MTGCardInstance*)source)->damageToOpponent = true;
((MTGCardInstance*)source)->damageToOpponent += damage;
if(((MTGCardInstance*)source)->basicAbilities[Constants::ISCOMMANDER])
((MTGCardInstance*)source)->damageInflictedAsCommander += damage;
}
@@ -274,7 +274,7 @@ int Damage::resolve()
if ( typeOfDamage == 1 && target == source->controller()->opponent() )//add vector prowledtypes.
{
source->controller()->dealsdamagebycombat = 1; // for restriction check
((MTGCardInstance*)source)->combatdamageToOpponent = true; //check
((MTGCardInstance*)source)->combatdamageToOpponent += damage; //check
vector<string> values = MTGAllCards::getCreatureValuesById();//getting a weird crash here. rarely.
for (size_t i = 0; i < values.size(); ++i)
{
+6 -6
View File
@@ -1026,12 +1026,12 @@ void GameObserver::gameStateBasedEffects()
c->flanked -= 1;
}
c->fresh = 0;
if(c->wasDealtDamage && c->isInPlay(this))
c->wasDealtDamage = false;
c->damageToController = false;
c->damageToOpponent = false;
c->combatdamageToOpponent = false;
c->damageToCreature = false;
if(c->wasDealtDamage > 0 && c->isInPlay(this))
c->wasDealtDamage = 0;
c->damageToController = 0;
c->damageToOpponent = 0;
c->combatdamageToOpponent = 0;
c->damageToCreature = 0;
c->isAttacking = NULL;
c->isProvoked = false;
c->ProvokeTarget = NULL;
+28 -4
View File
@@ -693,7 +693,7 @@ int AbilityFactory::parseCastRestrictions(MTGCardInstance * card, Player * playe
check = restriction[i].find("didcombatdamagetofoe");
if(check != string::npos)
{
if(!card->combatdamageToOpponent)
if(card->combatdamageToOpponent == 0)
return 0;
}
@@ -825,6 +825,28 @@ int AbilityFactory::parseCastRestrictions(MTGCardInstance * card, Player * playe
if(!found)
return 0;
}
check = restriction[i].find("coven"); //Player controls three or more creatures with different powers
if(check != string::npos)
{
if(player != observer->currentPlayer)
return 0;
bool found = false;
for(unsigned int i = 0; i < observer->currentPlayer->game->inPlay->cards.size() && !found; i++){
if(observer->currentPlayer->game->inPlay->cards[i]->hasType(Subtypes::TYPE_CREATURE)){
for(unsigned int j = i+1; j < observer->currentPlayer->game->inPlay->cards.size() && !found; j++){
if(observer->currentPlayer->game->inPlay->cards[j]->hasType(Subtypes::TYPE_CREATURE) && observer->currentPlayer->game->inPlay->cards[j]->power != observer->currentPlayer->game->inPlay->cards[i]->power){
for(unsigned int k = j+1; k < observer->currentPlayer->game->inPlay->cards.size() && !found; k++){
if(observer->currentPlayer->game->inPlay->cards[k]->hasType(Subtypes::TYPE_CREATURE) && (observer->currentPlayer->game->inPlay->cards[k]->power != observer->currentPlayer->game->inPlay->cards[i]->power && observer->currentPlayer->game->inPlay->cards[k]->power != observer->currentPlayer->game->inPlay->cards[j]->power)){
found = true;
}
}
}
}
}
}
if(!found)
return 0;
}
check = restriction[i].find("can play");
if(check != string::npos)
{
@@ -6531,15 +6553,17 @@ int ActivatedAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana)
return 0;
}*/
// Improved the check to avoid the multiple triggers in case of abilities gained from other cards (e.g. Kasmina, Enigma Sage)
for(unsigned int k = 0;k < card->getObserver()->mLayers->actionLayer()->mObjects.size();++k)
bool turnSide = false;
for(unsigned int k = 0; k < card->getObserver()->mLayers->actionLayer()->mObjects.size(); ++k)
{
ActivatedAbility * check = dynamic_cast<ActivatedAbility*>(card->getObserver()->mLayers->actionLayer()->mObjects[k]);
if(check && check->source == card && check->counters)
turnSide = card->isFlipped && !card->isInPlay(card->getObserver());
if(!turnSide && check && check->source == card && check->counters)
return 0;
}
if (player != game->currentPlayer)
return 0;
if (cPhase != MTG_PHASE_FIRSTMAIN && cPhase != MTG_PHASE_SECONDMAIN)
if (!turnSide && (cPhase != MTG_PHASE_FIRSTMAIN && cPhase != MTG_PHASE_SECONDMAIN))
return 0;
}
if (source->has(Constants::NOACTIVATED) || (source->mutation && source->parentCards.size() > 0)) // Mutated Over/Under card doesn't have to react to click anymore
+7 -6
View File
@@ -74,6 +74,7 @@ MTGCardInstance::MTGCardInstance(MTGCard * card, MTGPlayerCards * arg_belongs_to
myconvertedcost = getManaCost()->getConvertedCost();
revealedLast = NULL;
MadnessPlay = false;
backSide = card->data->backSide;
}
MTGCardInstance * MTGCardInstance::createSnapShot()
@@ -140,7 +141,7 @@ void MTGCardInstance::copy(MTGCardInstance * card, bool nolegend)
setText(data->text); //The text is retrieved from the data anyways
setName(data->name);
backSide = data->backSide;
power = data->power;//layer 7a
toughness = data->toughness;//layer 7a
power += pbonus;//layer 7b
@@ -242,14 +243,14 @@ void MTGCardInstance::initMTGCI()
damageInflictedAsCommander = 0;
numofcastfromcommandzone = 0;
auras = 0;
combatdamageToOpponent = false;
damageToOpponent = false;
damageToController = false;
damageToCreature = false;
combatdamageToOpponent = 0;
damageToOpponent = 0;
damageToController = 0;
damageToCreature = 0;
isProvoked = false;
ProvokeTarget = NULL;
Provoker = NULL;
wasDealtDamage = false;
wasDealtDamage = 0;
isDualWielding = false;
suspended = false;
isBestowed = false;
+7 -1
View File
@@ -113,7 +113,7 @@ int MTGAllCards::processConfLine(string &s, MTGCard *card, CardPrimitive * primi
}
break;
case 'b': //buyback/Bestow
case 'b': //buyback/Bestow/backside
if (!primitive) primitive = NEW CardPrimitive();
if (key[1] == 'e' && key[2] == 's')
{ //bestow
@@ -125,6 +125,12 @@ int MTGAllCards::processConfLine(string &s, MTGCard *card, CardPrimitive * primi
cost->setBestow(ManaCost::parseManaCost(value));
}
}
else
if (key[1] == 'a' && key[2] == 'c')
{ //backside
if (!primitive) primitive = NEW CardPrimitive();
primitive->backSide = val;
}
else//buyback
if (ManaCost * cost = primitive->getManaCost())
{
+3 -2
View File
@@ -228,8 +228,9 @@ const char* Constants::MTGBasicAbilities[] = {
"wascommander", //It was the current commander (e.g. after it flipped or morphed)
"showopponenthand", //opponent plays with his hand revealed.
"showcontrollerhand", //controller plays with his hand revealed.
"hasreplicate", //Kicker cost is a replicate cost (eg. "Vacuumelt")
"isprey" //Creature has been haunted by some other card.
"hasreplicate", //Kicker cost is a replicate cost (e.g. "Vacuumelt")
"isprey", //Creature has been haunted by some other card.
"hasdisturb" //Retrace cost is a disturb cost (e.g. "Beloved Beggar")
};
map<string,int> Constants::MTGBasicAbilitiesMap;
+12
View File
@@ -583,6 +583,18 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta
cd->unsecureSetHasFlashbackCost(1);
}
}
//Has backside
else if (attribute.find("hasbackside") != string::npos)
{
if (minus)
{
cd->unsecureSetHasBackSide(-1);
}
else
{
cd->unsecureSetHasBackSide(1);
}
}
//Token
else if (attribute.find("token") != string::npos)
{
+1 -1
View File
@@ -669,7 +669,7 @@ ThisDamaged::ThisDamaged(int wasDealtDamage)
int ThisDamaged::match(MTGCardInstance * card)
{
int result = 0;
if(card->wasDealtDamage)
if(card->wasDealtDamage > 0)
result = 1;
return matchValue(result);
}
+7 -2
View File
@@ -1315,9 +1315,14 @@ void WParsedInt::extendedParse(string s, Spell * spell, MTGCardInstance * card)
{
intValue = (s == "plastshlturn")?card->controller()->lastShuffleTurn:card->controller()->opponent()->lastShuffleTurn;
}
else if (s == "hasprey")
else if (s == "hasprey" || s == "dualfaced" || s == "totaldmg")
{
intValue = (card->hauntedCard)?1:0;
if (s == "hasprey")
intValue = (card->hauntedCard)?1:0;
else if (s == "dualfaced")
intValue = (card->backSide != "")?1:0;
else if (s == "totaldmg")
intValue = (card->damageToController + card->damageToCreature + card->damageToOpponent);
}
else if(!intValue)//found nothing, try parsing a atoi
{