Added/fixed primitives, updated the "missing_cards_by_sets" folder, fixed several crash on Commander Format, fixed a possible crash using "and!()!" with "imprint", added a new macros "_REBOUND_" and "_GOAD_" related to rebound and goad abilities and refactored all cards using them, implemented a new keywords "haunt", "hasprey", "preyname" and "isprey" related the haunting ability and improved all cards using it, Added "commander" and "\*" to HINT castpriority for AI decks in order to allow the user to give a cast priority to commanders.
This commit is contained in:
@@ -206,7 +206,7 @@ int AIPlayer::clickSingleTarget(TargetChooser *, vector<Targetable*>& potentialT
|
||||
AIPlayer * AIPlayerFactory::createAIPlayer(GameObserver *observer, MTGAllCards * collection, Player * opponent, int deckid)
|
||||
{
|
||||
char deckFile[512];
|
||||
string avatarFilename; // default imagename
|
||||
string avatarFilename = ""; // default imagename
|
||||
char deckFileSmall[512];
|
||||
|
||||
if (deckid == GameStateDuel::MENUITEM_EVIL_TWIN)
|
||||
@@ -228,7 +228,8 @@ AIPlayer * AIPlayerFactory::createAIPlayer(GameObserver *observer, MTGAllCards *
|
||||
}
|
||||
sprintf(deckFile, "ai/baka/deck%i.txt", deckid);
|
||||
DeckMetaData *aiMeta = observer->getDeckManager()->getDeckMetaDataByFilename( deckFile, true);
|
||||
avatarFilename = aiMeta->getAvatarFilename();
|
||||
if(aiMeta)
|
||||
avatarFilename = aiMeta->getAvatarFilename();
|
||||
sprintf(deckFileSmall, "ai_baka_deck%i", deckid);
|
||||
}
|
||||
|
||||
|
||||
@@ -2748,7 +2748,10 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
|
||||
}
|
||||
CardDescriptor cd;
|
||||
cd.init();
|
||||
cd.setType(type);
|
||||
if(!strcmp(type,"commander")) //Added to allow the casting priority for commanders
|
||||
cd.basicAbilities[Constants::ISCOMMANDER] = 1;
|
||||
else if(strcmp(type,"*")) //Added to allow the wildcard in casting priority
|
||||
cd.setType(type);
|
||||
card = NULL;
|
||||
payAlternative = NONE;
|
||||
gotPayments = vector<MTGAbility*>();
|
||||
|
||||
@@ -2254,6 +2254,7 @@ int AAImprint::resolve()
|
||||
{
|
||||
andAbilityClone->addToGame();
|
||||
}
|
||||
SAFE_DELETE(andAbility); //moved here because in destructor it can cause a crash.
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -2272,7 +2273,61 @@ AAImprint * AAImprint::clone() const
|
||||
|
||||
AAImprint::~AAImprint()
|
||||
{
|
||||
SAFE_DELETE(andAbility);
|
||||
}
|
||||
|
||||
//AAHaunt
|
||||
AAHaunt::AAHaunt(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
|
||||
ActivatedAbility(observer, _id, _source, _cost, 0)
|
||||
{
|
||||
target = _target;
|
||||
andAbility = NULL;
|
||||
}
|
||||
|
||||
int AAHaunt::resolve()
|
||||
{
|
||||
MTGCardInstance * _target = (MTGCardInstance *) target;
|
||||
if (_target && _target->hasType(Subtypes::TYPE_CREATURE))
|
||||
{
|
||||
if(_target->mutation && _target->parentCards.size() > 0) return 0; // Mutated down cards cannot be haunted, they will follow the fate of top-card
|
||||
|
||||
while(_target->next)
|
||||
_target = _target->next;
|
||||
|
||||
_target->basicAbilities[Constants::ISPREY] = 1;
|
||||
source->hauntedCard = _target;
|
||||
|
||||
if(andAbility)
|
||||
{
|
||||
MTGAbility * andAbilityClone = andAbility->clone();
|
||||
andAbilityClone->target = _target;
|
||||
if(andAbility->oneShot)
|
||||
{
|
||||
andAbilityClone->resolve();
|
||||
SAFE_DELETE(andAbilityClone);
|
||||
}
|
||||
else
|
||||
{
|
||||
andAbilityClone->addToGame();
|
||||
}
|
||||
SAFE_DELETE(andAbility); //moved here because in destructor it can cause a crash.
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const string AAHaunt::getMenuText()
|
||||
{
|
||||
return "Haunt";
|
||||
}
|
||||
|
||||
AAHaunt * AAHaunt::clone() const
|
||||
{
|
||||
return NEW AAHaunt(*this);
|
||||
}
|
||||
|
||||
AAHaunt::~AAHaunt()
|
||||
{
|
||||
}
|
||||
|
||||
//AAConjure
|
||||
|
||||
@@ -668,6 +668,14 @@ void GameObserver::gameStateBasedEffects()
|
||||
card->removeColor(i);
|
||||
}
|
||||
}
|
||||
//clear prey
|
||||
if(card && isInExile(card) && card->hauntedCard)
|
||||
{
|
||||
if(!isInPlay(card->hauntedCard))
|
||||
{
|
||||
card->hauntedCard = 0;
|
||||
}
|
||||
}
|
||||
//reset alternate paid
|
||||
if(card && (isInGrave(card)||isInHand(card)||isInExile(card)))
|
||||
{
|
||||
|
||||
@@ -3372,6 +3372,25 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
||||
}
|
||||
}
|
||||
|
||||
//haunt a creature
|
||||
found = s.find("haunt");
|
||||
if (found != string::npos)
|
||||
{
|
||||
if (s.find("haunted") == string::npos)
|
||||
{
|
||||
MTGAbility * a = NEW AAHaunt(observer, id, card, target);
|
||||
a->oneShot = 1;
|
||||
//andability
|
||||
if(storedAndAbility.size())
|
||||
{
|
||||
string stored = storedAndAbility;
|
||||
storedAndAbility.clear();
|
||||
((AAHaunt*)a)->andAbility = parseMagicLine(stored, id, spell, card);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
//Conjure a card
|
||||
found = s.find("conjure");
|
||||
if (found != string::npos)
|
||||
@@ -5306,6 +5325,8 @@ int AbilityFactory::abilityEfficiency(MTGAbility * a, Player * p, int mode, Targ
|
||||
return BAKA_EFFECT_GOOD;
|
||||
if (dynamic_cast<AAImprint *> (a))
|
||||
return BAKA_EFFECT_GOOD;
|
||||
if (dynamic_cast<AAHaunt *> (a))
|
||||
return BAKA_EFFECT_GOOD;
|
||||
if (dynamic_cast<ABestow *> (a))
|
||||
return BAKA_EFFECT_GOOD;
|
||||
if (dynamic_cast<AExert *> (a))
|
||||
|
||||
@@ -280,6 +280,7 @@ void MTGCardInstance::initMTGCI()
|
||||
countTrini = 0;
|
||||
anymanareplacement = false;
|
||||
imprintedCards.clear();
|
||||
hauntedCard = NULL;
|
||||
attackCost = 0;
|
||||
attackCostBackup = 0;
|
||||
attackPlaneswalkerCost = 0;
|
||||
|
||||
@@ -228,7 +228,8 @@ 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")
|
||||
"hasreplicate", //Kicker cost is a replicate cost (eg. "Vacuumelt")
|
||||
"isprey" //Creature has been haunted by some other card.
|
||||
};
|
||||
|
||||
map<string,int> Constants::MTGBasicAbilitiesMap;
|
||||
|
||||
@@ -569,6 +569,10 @@ MTGCardInstance * MTGPlayerCards::putInZone(MTGCardInstance * card, MTGGameZone
|
||||
}
|
||||
}
|
||||
|
||||
// Save the haunted status... (solving the bug on comparison cards with haunted status before zone changing events)
|
||||
if(card->has(Constants::ISPREY) && doCopy && !asCopy && !inplaytoinplay)
|
||||
copy->basicAbilities[Constants::ISPREY] = 1;
|
||||
|
||||
//Commander is going back to Command Zone, so we recalculate costs according to how many times it has been casted from there.
|
||||
if((to == g->players[0]->game->commandzone || to == g->players[1]->game->commandzone) && copy->numofcastfromcommandzone > 0){
|
||||
copy->getManaCost()->add(Constants::MTG_COLOR_ARTIFACT,2*copy->numofcastfromcommandzone);
|
||||
@@ -740,8 +744,12 @@ MTGCardInstance * MTGPlayerCards::putInZone(MTGCardInstance * card, MTGGameZone
|
||||
g->receiveEvent(e);
|
||||
}
|
||||
|
||||
// Reset the haunted status... (if the creature is moving from battlefield is no longer a prey)
|
||||
if(doCopy && !inplaytoinplay && copy->has(Constants::ISPREY))
|
||||
copy->basicAbilities[Constants::ISPREY] = 0;
|
||||
|
||||
// Erasing counters from copy after the event has been triggered (no counter can survive to a zone changing except the perpetual ones)
|
||||
if(doCopy && copy->counters && copy->counters->mCount > 0){
|
||||
if(doCopy && !inplaytoinplay && copy->counters && copy->counters->mCount > 0){
|
||||
for (unsigned int i = 0; i < copy->counters->counters.size(); i++){
|
||||
Counter * counter = copy->counters->counters[i];
|
||||
for(int j = counter->nb; j > 0; j--){
|
||||
@@ -1469,15 +1477,15 @@ MTGGameZone * MTGGameZone::intToZone(int zoneId, Player * p, Player * p2)
|
||||
|
||||
MTGGameZone * MTGGameZone::intToZone(GameObserver *g, int zoneId, MTGCardInstance * source, MTGCardInstance * target)
|
||||
{
|
||||
Player *p, *p2;
|
||||
Player *p = NULL;
|
||||
Player *p2 = NULL;
|
||||
|
||||
if (!source)
|
||||
if (!source && g) //patchwork fix when g is NULL.
|
||||
p = g->currentlyActing();
|
||||
else
|
||||
p = source->controller();
|
||||
if (!target)
|
||||
if (!target && source) //patchwork fix when source is NULL.
|
||||
{
|
||||
//TODO source may be NULL, need to handle the case when it is NULL. method declaration has NULL being default value of source and target.
|
||||
if(source->target)
|
||||
{
|
||||
//bug case, this is a patchwork fix for now
|
||||
@@ -1492,10 +1500,15 @@ MTGGameZone * MTGGameZone::intToZone(GameObserver *g, int zoneId, MTGCardInstanc
|
||||
target = source;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (target)
|
||||
p2 = target->controller();
|
||||
|
||||
|
||||
if(!p) { //patchwork fix when p is NULL.
|
||||
if(!p2)
|
||||
return NULL;
|
||||
else
|
||||
p = p2;
|
||||
}
|
||||
MTGGameZone * result = intToZone(zoneId, p, p2);
|
||||
if (result) return result;
|
||||
switch (zoneId)
|
||||
|
||||
@@ -913,6 +913,7 @@ int MTGAlternativeCostRule::reactToClick(MTGCardInstance * card)
|
||||
return 0;
|
||||
|
||||
ManaCost * alternateCost = card->getManaCost()->getAlternative();
|
||||
if(!alternateCost) return 0;
|
||||
card->paymenttype = MTGAbility::ALTERNATIVE_COST;
|
||||
if(alternateCost->extraCosts)
|
||||
for(unsigned int i = 0; i < alternateCost->extraCosts->costs.size();i++)
|
||||
@@ -1139,6 +1140,7 @@ int MTGBuyBackRule::reactToClick(MTGCardInstance * card)
|
||||
return 0;
|
||||
|
||||
ManaCost * buybackCost = card->getManaCost()->getBuyback();
|
||||
if(!buybackCost) return 0;
|
||||
if(buybackCost->extraCosts)
|
||||
for(unsigned int i = 0; i < buybackCost->extraCosts->costs.size();i++)
|
||||
{
|
||||
@@ -1190,6 +1192,7 @@ int MTGFlashBackRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana)
|
||||
int MTGFlashBackRule::reactToClick(MTGCardInstance * card)
|
||||
{
|
||||
ManaCost * flashbackCost = card->getManaCost()->getFlashback();
|
||||
if(!flashbackCost) return 0;
|
||||
if(flashbackCost->extraCosts)
|
||||
for(unsigned int i = 0; i < flashbackCost->extraCosts->costs.size();i++)
|
||||
{
|
||||
@@ -1239,6 +1242,7 @@ int MTGTempFlashBackRule::isReactingToClick(MTGCardInstance * card, ManaCost * m
|
||||
int MTGTempFlashBackRule::reactToClick(MTGCardInstance * card)
|
||||
{
|
||||
ManaCost * flashbackCost = card->getManaCost();
|
||||
if(!flashbackCost) return 0;
|
||||
if(flashbackCost->extraCosts)
|
||||
for(unsigned int i = 0; i < flashbackCost->extraCosts->costs.size();i++)
|
||||
{
|
||||
@@ -1306,6 +1310,7 @@ int MTGRetraceRule::reactToClick(MTGCardInstance * card)
|
||||
return 0;
|
||||
|
||||
ManaCost * retraceCost = card->getManaCost()->getRetrace();
|
||||
if(!retraceCost) return 0;
|
||||
if(retraceCost->extraCosts)
|
||||
for(unsigned int i = 0; i < retraceCost->extraCosts->costs.size();i++)
|
||||
{
|
||||
@@ -1386,6 +1391,7 @@ int MTGSuspendRule::reactToClick(MTGCardInstance * card)
|
||||
Player *player = game->currentlyActing();
|
||||
ManaCost * playerMana = player->getManaPool();
|
||||
ManaCost * alternateCost = card->getManaCost()->getSuspend();
|
||||
if(!alternateCost) return 0;
|
||||
//this handles extra cost payments at the moment a card is played.
|
||||
if (playerMana->canAfford(alternateCost,card->has(Constants::ANYTYPEOFMANA)))
|
||||
{
|
||||
@@ -1512,6 +1518,7 @@ int MTGMorphCostRule::reactToClick(MTGCardInstance * card)
|
||||
ManaCost * cost = card->getManaCost();
|
||||
ManaCost * playerMana = player->getManaPool();
|
||||
ManaCost * morph = card->getManaCost()->getMorph();
|
||||
if(!morph) return 0;
|
||||
if(morph->extraCosts){
|
||||
for(unsigned int i = 0; i < morph->extraCosts->costs.size();i++)
|
||||
morph->extraCosts->costs[i]->setSource(card);
|
||||
@@ -1647,6 +1654,7 @@ int MTGPayZeroRule::reactToClick(MTGCardInstance * card)
|
||||
|
||||
ManaCost * cost = NEW ManaCost(ManaCost::parseManaCost("{0}",NULL,NULL));
|
||||
ManaCost * newCost = card->computeNewCost(card,cost,cost);
|
||||
if(!newCost) return 0;
|
||||
if(newCost->extraCosts)
|
||||
for(unsigned int i = 0; i < newCost->extraCosts->costs.size();i++)
|
||||
{
|
||||
@@ -1693,6 +1701,7 @@ int MTGOverloadRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana)
|
||||
return 0;
|
||||
}
|
||||
ManaCost * newCost = card->getManaCost()->getAlternative();
|
||||
if(!newCost) return 0;
|
||||
if(newCost->extraCosts)
|
||||
for(unsigned int i = 0; i < newCost->extraCosts->costs.size();i++)
|
||||
{
|
||||
@@ -1707,6 +1716,7 @@ int MTGOverloadRule::reactToClick(MTGCardInstance * card)
|
||||
if (!isReactingToClick(card))
|
||||
return 0;
|
||||
ManaCost * newCost = card->getManaCost()->getAlternative();
|
||||
if(!newCost) return 0;
|
||||
if(newCost->extraCosts)
|
||||
for(unsigned int i = 0; i < newCost->extraCosts->costs.size();i++)
|
||||
{
|
||||
@@ -1768,7 +1778,7 @@ int MTGBestowRule::reactToClick(MTGCardInstance * card)
|
||||
//this new method below in all alternative cost type causes a memleak, however, you cant safedelete the cost here as it cause a crash
|
||||
//TODO::::we need to get to the source of this leak and fix it.
|
||||
ManaCost * newCost = card->getManaCost()->getBestow();
|
||||
|
||||
if(!newCost) return 0;
|
||||
if (newCost->extraCosts)
|
||||
for (unsigned int i = 0; i < newCost->extraCosts->costs.size(); i++)
|
||||
{
|
||||
|
||||
@@ -1052,6 +1052,16 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta
|
||||
cd->nameComparisonMode = COMPARISON_EQUAL;
|
||||
}
|
||||
|
||||
if (attribute.find("preyname") != string::npos && card->hauntedCard)
|
||||
{
|
||||
attributefound = 1;
|
||||
cd->compareName = card->hauntedCard->getName();
|
||||
if (minus)
|
||||
cd->nameComparisonMode = COMPARISON_UNEQUAL;
|
||||
else
|
||||
cd->nameComparisonMode = COMPARISON_EQUAL;
|
||||
}
|
||||
|
||||
if (!attributefound)
|
||||
{
|
||||
//Abilities
|
||||
|
||||
@@ -1315,6 +1315,10 @@ void WParsedInt::extendedParse(string s, Spell * spell, MTGCardInstance * card)
|
||||
{
|
||||
intValue = (s == "plastshlturn")?card->controller()->lastShuffleTurn:card->controller()->opponent()->lastShuffleTurn;
|
||||
}
|
||||
else if (s == "hasprey")
|
||||
{
|
||||
intValue = (card->hauntedCard)?1:0;
|
||||
}
|
||||
else if(!intValue)//found nothing, try parsing a atoi
|
||||
{
|
||||
intValue = atoi(s.c_str());
|
||||
|
||||
Reference in New Issue
Block a user