Files
wagic/projects/mtg/src/AllAbilities.cpp
T
wagic.the.homebrew 52b83a135c - Added TutorialMessage ability
-- Tutorial Messages are an ability like any other, except it can only be displayed once. Subsequent calls are ignored, the ability is removed from the game as soon as it is added
-- This allows to add event triggered messages ingame. Messages are either text, or images (I don't have an image sample, but rules/classic.txt has a few examples that might help)
-- only tested on Windows, although I made sure the PSP version compiles. Hopefully I also made the necessary for it to work in the touch version (touching the screen should be enough to close the tuto message)
-- Room for improvement: possibility to choose a title in text mode, possibility to have some messages depending on others (e.g.: don't show message X until message Y has been shown), improve some of the abilities and triggers to give more flexibility, add events outside of game, to allow tuto messages in deck creator, etc...
2011-07-03 08:47:51 +00:00

3898 lines
107 KiB
C++

#include "PrecompiledHeader.h"
#include "AllAbilities.h"
#include "Translate.h"
#include <boost/algorithm/string.hpp>
//Activated Abilities
//Generic Activated Abilities
GenericActivatedAbility::GenericActivatedAbility(string newName,int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost,
string limit,MTGAbility * sideEffects,string usesBeforeSideEffects, int restrictions, MTGGameZone * dest) :
ActivatedAbility(_id, card, _cost, restrictions,limit,sideEffects,usesBeforeSideEffects), NestedAbility(a), activeZone(dest),newName(newName)
{
counters = 0;
target = ability->target;
}
int GenericActivatedAbility::resolve()
{
ManaCost * diff = abilityCost->Diff(cost);
source->X = diff->hasX();
SAFE_DELETE(diff);
//SAFE_DELETE(abilityCost); this line has been reported as a bug. removing it doesn't seem to break anything, although I didn't get any error in the test suite by leaving it either, so... leaving it for now as a comment, in case.
ability->target = target; //may have been updated...
if (ability)
return ability->resolve();
return 0;
}
const char * GenericActivatedAbility::getMenuText()
{
if(newName.size())
return newName.c_str();
if (ability)
return ability->getMenuText();
return "Error";
}
int GenericActivatedAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana)
{
if (dynamic_cast<AAMorph*> (ability) && !card->isMorphed && !card->morphed && card->turningOver)
return 0;
return ActivatedAbility::isReactingToClick(card, mana);
}
void GenericActivatedAbility::Update(float dt)
{
ActivatedAbility::Update(dt);
}
int GenericActivatedAbility::testDestroy()
{
if (!activeZone)
return ActivatedAbility::testDestroy();
if (activeZone->hasCard(source))
return 0;
return 1;
}
GenericActivatedAbility * GenericActivatedAbility::clone() const
{
GenericActivatedAbility * a = NEW GenericActivatedAbility(*this);
a->cost = NEW ManaCost();
a->cost->copy(cost);
a->ability = ability->clone();
return a;
}
GenericActivatedAbility::~GenericActivatedAbility()
{
SAFE_DELETE(ability);
}
//AA Alter Poison
AAAlterPoison::AAAlterPoison(int _id, MTGCardInstance * _source, Targetable * _target, int poison, ManaCost * _cost,
int who) :
ActivatedAbilityTP(_id, _source, _target, _cost, who), poison(poison)
{
}
int AAAlterPoison::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (_target)
{
_target->poisonCount += poison;
}
return 0;
}
const char * AAAlterPoison::getMenuText()
{
return "Poison";
}
AAAlterPoison * AAAlterPoison::clone() const
{
AAAlterPoison * a = NEW AAAlterPoison(*this);
a->isClone = 1;
return a;
}
AAAlterPoison::~AAAlterPoison()
{
}
//Damage Prevent
AADamagePrevent::AADamagePrevent(int _id, MTGCardInstance * _source, Targetable * _target, int preventing, ManaCost * _cost,
int who) :
ActivatedAbilityTP(_id, _source, _target, _cost, who), preventing(preventing)
{
aType = MTGAbility::STANDARD_PREVENT;
}
int AADamagePrevent::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (_target)
{
_target->preventable += preventing;
}
return 0;
}
const char * AADamagePrevent::getMenuText()
{
return "Prevent Damage";
}
AADamagePrevent * AADamagePrevent::clone() const
{
AADamagePrevent * a = NEW AADamagePrevent(*this);
a->isClone = 1;
return a;
}
AADamagePrevent::~AADamagePrevent()
{
}
//AADamager
AADamager::AADamager(int _id, MTGCardInstance * _source, Targetable * _target, string d, ManaCost * _cost,
int who) :
ActivatedAbilityTP(_id, _source, _target, _cost, who), d(d)
{
aType = MTGAbility::DAMAGER;
}
int AADamager::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (_target)
{
WParsedInt damage(d, NULL, (MTGCardInstance *)source);
game->mLayers->stackLayer()->addDamage(source, _target, damage.getValue());
game->mLayers->stackLayer()->resolve();
return 1;
}
return 0;
}
int AADamager::getDamage()
{
WParsedInt damage(d, NULL, (MTGCardInstance *)source);
return damage.getValue();
}
const char * AADamager::getMenuText()
{
return "Damage";
}
AADamager * AADamager::clone() const
{
AADamager * a = NEW AADamager(*this);
a->isClone = 1;
return a;
}
//AADepleter
AADepleter::AADepleter(int _id, MTGCardInstance * card, Targetable * _target,string nbcardsStr, ManaCost * _cost, int who) :
ActivatedAbilityTP(_id, card, _target, _cost, who),nbcardsStr(nbcardsStr)
{
}
int AADepleter::resolve()
{
Targetable * _target = getTarget();
Player * player;
if (_target)
{
WParsedInt numCards(nbcardsStr, NULL, source);
if (_target->typeAsTarget() == TARGET_CARD)
{
player = ((MTGCardInstance *) _target)->controller();
}
else
{
player = (Player *) _target;
}
MTGLibrary * library = player->game->library;
for (int i = 0; i < numCards.getValue(); i++)
{
if (library->nb_cards)
player->game->putInZone(library->cards[library->nb_cards - 1], library, player->game->graveyard);
}
}
return 1;
}
const char * AADepleter::getMenuText()
{
return "Deplete";
}
AADepleter * AADepleter::clone() const
{
AADepleter * a = NEW AADepleter(*this);
a->isClone = 1;
return a;
}
//AACopier
AACopier::AACopier(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(_id, _source, _cost, 0)
{
target = _target;
}
int AACopier::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
source->copy(_target);
return 1;
}
return 0;
}
const char * AACopier::getMenuText()
{
return "Copy";
}
AACopier * AACopier::clone() const
{
AACopier * a = NEW AACopier(*this);
a->isClone = 1;
return a;
}
//phaser
AAPhaseOut::AAPhaseOut(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(_id, _source, _cost, 0)
{
target = _target;
}
int AAPhaseOut::resolve()
{GameObserver * g = g->GetInstance();
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
_target->isPhased = true;
_target->phasedTurn = game->turn;
if(_target->view)
_target->view->alpha = 50;
_target->initAttackersDefensers();
return 1;
}
return 0;
}
const char * AAPhaseOut::getMenuText()
{
return "Phase Out";
}
AAPhaseOut * AAPhaseOut::clone() const
{
AAPhaseOut * a = NEW AAPhaseOut(*this);
a->isClone = 1;
return a;
}
//Counters
AACounter::AACounter(int id, MTGCardInstance * source, MTGCardInstance * target,string counterstring, const char * _name, int power, int toughness,
int nb,int maxNb, ManaCost * cost) :
ActivatedAbility(id, source, cost, 0),counterstring(counterstring), nb(nb),maxNb(maxNb), power(power), toughness(toughness), name(_name)
{
this->target = target;
if (name.find("Level") != string::npos || name.find("level") != string::npos)
aType = MTGAbility::STANDARD_LEVELUP;
else
aType = MTGAbility::COUNTERS;
menu = "";
}
int AACounter::resolve()
{
if (target)
{
MTGCardInstance * _target = (MTGCardInstance *) target;
AbilityFactory af;
Counter * checkcounter = af.parseCounter(counterstring, source, NULL);
nb = checkcounter->nb;
delete checkcounter;
if (nb > 0)
{
for (int i = 0; i < nb; i++)
{
while (_target->next)
_target = _target->next;
Counter * targetCounter = NULL;
int currentAmount = 0;
if (_target->counters && _target->counters->hasCounter(name.c_str(), power, toughness))
{
targetCounter = _target->counters->hasCounter(name.c_str(), power, toughness);
currentAmount = targetCounter->nb;
}
if(!maxNb || (maxNb && currentAmount < maxNb))
_target->counters->addCounter(name.c_str(), power, toughness);
}
}
else
{
for (int i = 0; i < -nb; i++)
{
while (_target->next)
_target = _target->next;
_target->counters->removeCounter(name.c_str(), power, toughness);
}
}
//specail cases, indestructible creatures which recieve enough counters to kill it are destroyed as a state based effect
if(_target->toughness <= 0 && _target->has(Constants::INDESTRUCTIBLE) && toughness < 0)
_target->controller()->game->putInGraveyard(_target);
return nb;
}
return 0;
}
const char* AACounter::getMenuText()
{
if (menu.size())
{
return menu.c_str();
}
char buffer[128];
if (name.size())
{
string s = name;
menu.append(s.c_str());
}
if (power != 0 || toughness != 0)
{
sprintf(buffer, " %i/%i", power, toughness);
menu.append(buffer);
}
menu.append(" Counter");
if (nb != 1 && !(nb < -1000))
{
sprintf(buffer, ": %i", nb);
menu.append(buffer);
}
sprintf(menuText, "%s", menu.c_str());
return menuText;
}
AACounter * AACounter::clone() const
{
AACounter * a = NEW AACounter(*this);
a->isClone = 1;
return a;
}
//Counters
AARemoveAllCounter::AARemoveAllCounter(int id, MTGCardInstance * source, MTGCardInstance * target, const char * _name, int power, int toughness,
int nb,bool all, ManaCost * cost) :
ActivatedAbility(id, source, cost, 0), nb(nb), power(power), toughness(toughness), name(_name),all(all)
{
this->target = target;
menu = "";
}
int AARemoveAllCounter::resolve()
{
if (target)
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (all )
{
for(int amount = 0;amount < _target->counters->mCount;amount++)
{
while(_target->counters->counters[amount]->nb > 0)
_target->counters->removeCounter(_target->counters->counters[amount]->name.c_str(),_target->counters->counters[amount]->power,_target->counters->counters[amount]->toughness);
}
}
Counter * targetCounter = NULL;
if (_target->counters && _target->counters->hasCounter(name.c_str(), power, toughness))
{
targetCounter = _target->counters->hasCounter(name.c_str(), power, toughness);
nb = targetCounter->nb;
}
if (nb > 0)
{
for (int i = 0; i < nb; i++)
{
while (_target->next)
_target = _target->next;
_target->counters->removeCounter(name.c_str(), power, toughness);
}
}
return nb;
}
return 0;
}
const char* AARemoveAllCounter::getMenuText()
{
if (menu.size())
{
return menu.c_str();
}
char buffer[128];
if (name.size())
{
string s = name;
menu.append(s.c_str());
}
if (power != 0 || toughness != 0)
{
sprintf(buffer, " %i/%i", power, toughness);
menu.append(buffer);
}
menu.append(" Counter Removed");
if (nb != 1)
{
sprintf(buffer, ": %i", nb);
menu.append(buffer);
}
sprintf(menuText, "%s", menu.c_str());
return menuText;
}
AARemoveAllCounter * AARemoveAllCounter::clone() const
{
AARemoveAllCounter * a = NEW AARemoveAllCounter(*this);
a->isClone = 1;
return a;
}
//Reset Damage on creatures
AAResetDamage::AAResetDamage(int id, MTGCardInstance * source, MTGCardInstance * _target, ManaCost * cost):
ActivatedAbility(id, source, cost, 0)
{
this->target = _target;
}
int AAResetDamage::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
_target->life = _target->toughness;
return 1;
}
const char* AAResetDamage::getMenuText()
{
return "Reset Damages";
}
AAResetDamage * AAResetDamage::clone() const
{
AAResetDamage * a = NEW AAResetDamage(*this);
a->isClone = 1;
return a;
}
// Fizzler
AAFizzler::AAFizzler(int _id, MTGCardInstance * card, Spell * _target, ManaCost * _cost) :
ActivatedAbility(_id, card, _cost, 0)
{
aType = MTGAbility::STANDARD_FIZZLER;
target = _target;
}
int AAFizzler::resolve()
{
ActionStack * stack = game->mLayers->stackLayer();
//the next section helps Ai correctly recieve its targets for this effect
if(!target && source->target)
{
//ai is casting a spell from it's hand to fizzle.
target = stack->getAt(stack->getActionElementFromCard(source->target));
}
else if(target->typeAsTarget() == TARGET_CARD)
{
//ai targeted using an ability on a card to fizzle.
target = stack->getAt(stack->getActionElementFromCard((MTGCardInstance*)target));
}
Spell * sTarget = (Spell *) target;
MTGCardInstance* sCard = (MTGCardInstance*)sTarget->source;
if(!sCard || !sTarget || sCard->has(Constants::NOFIZZLE))
return 0;
stack->Fizzle(sTarget);
return 1;
}
const char * AAFizzler::getMenuText()
{
return "Fizzle";
}
AAFizzler* AAFizzler::clone() const
{
AAFizzler * a = NEW AAFizzler(*this);
a->isClone = 1;
return a;
}
// BanishCard implementations
// Bury
AABuryCard::AABuryCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
ActivatedAbility(_id, _source)
{
target = _target;
}
int AABuryCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
return _target->bury();
}
return 0;
}
const char * AABuryCard::getMenuText()
{
return "Bury";
}
AABuryCard * AABuryCard::clone() const
{
AABuryCard * a = NEW AABuryCard(*this);
a->isClone = 1;
return a;
}
// Destroy
AADestroyCard::AADestroyCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
ActivatedAbility(_id, _source)
{
target = _target;
}
int AADestroyCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
return _target->destroy();
}
return 0;
}
const char * AADestroyCard::getMenuText()
{
return "Destroy";
}
AADestroyCard * AADestroyCard::clone() const
{
AADestroyCard * a = NEW AADestroyCard(*this);
a->isClone = 1;
return a;
}
// Sacrifice
AASacrificeCard::AASacrificeCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
ActivatedAbility(_id, _source)
{
target = _target;
}
int AASacrificeCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
Player * p = _target->controller();
WEvent * e = NEW WEventCardSacrifice(_target);
GameObserver * game = GameObserver::GetInstance();
game->receiveEvent(e);
p->game->putInGraveyard(_target);
return 1;
}
return 0;
}
const char * AASacrificeCard::getMenuText()
{
return "Sacrifice";
}
AASacrificeCard * AASacrificeCard::clone() const
{
AASacrificeCard * a = NEW AASacrificeCard(*this);
a->isClone = 1;
return a;
}
// Discard
AADiscardCard::AADiscardCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
ActivatedAbility(_id, _source)
{
target = _target;
}
int AADiscardCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
Player * p = _target->controller();
WEvent * e = NEW WEventCardDiscard(_target);
GameObserver * game = GameObserver::GetInstance();
game->receiveEvent(e);
p->game->putInGraveyard(_target);
return 1;
}
return 0;
}
const char * AADiscardCard::getMenuText()
{
return "Discard";
}
AADiscardCard * AADiscardCard::clone() const
{
AADiscardCard * a = NEW AADiscardCard(*this);
a->isClone = 1;
return a;
}
AADrawer::AADrawer(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, string nbcardsStr,
int who) :
ActivatedAbilityTP(_id, card, _target, _cost, who), nbcardsStr(nbcardsStr)
{
aType = MTGAbility::STANDARD_DRAW;
}
int AADrawer::resolve()
{
Targetable * _target = getTarget();
Player * player;
if (_target)
{
WParsedInt numCards(nbcardsStr, NULL, source);
if (_target->typeAsTarget() == TARGET_CARD)
{
player = ((MTGCardInstance *) _target)->controller();
}
else
{
player = (Player *) _target;
}
game->mLayers->stackLayer()->addDraw(player, numCards.getValue());
game->mLayers->stackLayer()->resolve();
GameObserver *g = GameObserver::GetInstance();
for(int i = numCards.getValue(); i > 0;i--)
{
WEvent * e = NEW WEventcardDraw(player, 1);
g->receiveEvent(e);
}
}
return 1;
}
int AADrawer::getNumCards()
{
WParsedInt numCards(nbcardsStr, NULL, source);
return numCards.getValue();
}
const char * AADrawer::getMenuText()
{
return "Draw";
}
AADrawer * AADrawer::clone() const
{
AADrawer * a = NEW AADrawer(*this);
a->isClone = 1;
return a;
}
// AAFrozen: Prevent a card from untapping during next untap phase
AAFrozen::AAFrozen(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(id, card, _cost, 0)
{
target = _target;
}
int AAFrozen::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next; //This is for cards such as rampant growth
_target->frozen += 1;
}
return 1;
}
const char * AAFrozen::getMenuText()
{
return "Freeze";
}
AAFrozen * AAFrozen::clone() const
{
AAFrozen * a = NEW AAFrozen(*this);
a->isClone = 1;
return a;
}
// chose a new target for an aura or enchantment and equip it note: VERY basic right now.
AANewTarget::AANewTarget(int id, MTGCardInstance * card, MTGCardInstance * _target,bool retarget, ManaCost * _cost) :
ActivatedAbility(id, card, _cost, 0),retarget(retarget)
{
target = _target;
}
int AANewTarget::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if(retarget)
{
_target = source;
source = (MTGCardInstance *) target;
}
if (_target)
{
while (_target->next)
_target = _target->next;
_target->controller()->game->putInZone(_target, _target->currentZone,
_target->owner->game->exile);
_target = _target->next;
MTGCardInstance * refreshed = source->controller()->game->putInZone(_target,_target->currentZone,source->controller()->game->battlefield);
Spell * reUp = NEW Spell(refreshed);
if(reUp->source->hasSubtype(Subtypes::TYPE_AURA))
{
reUp->source->target = source;
reUp->resolve();
}
if(_target->hasSubtype(Subtypes::TYPE_EQUIPMENT))
{
reUp->resolve();
GameObserver * g = g->GetInstance();
for (size_t i = 1; i < g->mLayers->actionLayer()->mObjects.size(); i++)
{
MTGAbility * a = ((MTGAbility *) g->mLayers->actionLayer()->mObjects[i]);
AEquip * eq = dynamic_cast<AEquip*> (a);
if (eq && eq->source == reUp->source)
{
((AEquip*)a)->unequip();
((AEquip*)a)->equip(source);
}
}
}
delete reUp;
if(retarget)
{
target = source;
source = _target;
}
}
return 1;
}
const char * AANewTarget::getMenuText()
{
return "New Target";
}
AANewTarget * AANewTarget::clone() const
{
AANewTarget * a = NEW AANewTarget(*this);
a->isClone = 1;
a->oneShot = 1;
return a;
}
// morph a card
AAMorph::AAMorph(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(id, card, _cost, restrictions)
{
target = _target;
}
int AAMorph::resolve()
{
MTGCardInstance * Morpher = (MTGCardInstance*)source;
if(!Morpher->isMorphed && !Morpher->morphed && Morpher->turningOver)
return 0;
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next;
AbilityFactory af;
_target->morphed = false;
_target->isMorphed = false;
_target->turningOver = true;
af.getAbilities(&currentAbilities, NULL, _target, 0);
for (size_t i = 0; i < currentAbilities.size(); ++i)
{
MTGAbility * a = currentAbilities[i];
a->source = (MTGCardInstance *) _target;
if( a && dynamic_cast<AAMorph *> (a))
{
a->removeFromGame();
GameObserver * g = g->GetInstance();
g->removeObserver(a);
}
if (a)
{
if (a->oneShot)
{
a->resolve();
delete (a);
}
else
{
a->addToGame();
}
}
}
currentAbilities.clear();
testDestroy();
}
return 1;
}
int AAMorph::testDestroy()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if(target)
{
if(_target->turningOver && !_target->isMorphed && !_target->morphed)
{
GameObserver * g = g->GetInstance();
g->removeObserver(this);
return 1;
}
}
return 0;
}
const char * AAMorph::getMenuText()
{
return "Morph";
}
AAMorph * AAMorph::clone() const
{
AAMorph * a = NEW AAMorph(*this);
a->isClone = 1;
a->forceDestroy = 1;
return a;
}
// AADYNAMIC: dynamic ability builder
AADynamic::AADynamic(int id, MTGCardInstance * card, Damageable * _target,int type,int effect,int who,int amountsource,MTGAbility * storedAbility, ManaCost * _cost) :
ActivatedAbility(id, card, _cost, 0),type(type),effect(effect),who(who),amountsource(amountsource),storedAbility(storedAbility)
{
target = _target;
sourceamount = 0;
targetamount = 0;
eachother = false;
tosrc = false;
menu = "";
OriginalSrc = source;
clonedStored = NULL;
}
int AADynamic::resolve()
{
Damageable * _target = (Damageable *) target;
Damageable * secondaryTarget = NULL;
if(amountsource == 2)
source = (MTGCardInstance * )_target;
switch(who)
{
case DYNAMIC_ABILITY_WHO_EACHOTHER://each other, both take the effect
eachother = true;
break;
case DYNAMIC_ABILITY_WHO_ITSELF:
source = ((MTGCardInstance *) _target);
_target = _target;
break;
case DYNAMIC_ABILITY_WHO_TARGETCONTROLLER:
_target = _target;
secondaryTarget = ((MTGCardInstance *) _target)->controller();
break;
case DYNAMIC_ABILITY_WHO_TARGETOPPONENT:
_target = _target;
secondaryTarget = ((MTGCardInstance *) _target)->controller()->opponent();
break;
case DYNAMIC_ABILITY_WHO_TOSOURCE:
tosrc = true;
break;
case DYNAMIC_ABILITY_WHO_SOURCECONTROLLER:
_target = _target;
secondaryTarget = ((MTGCardInstance *) OriginalSrc)->controller();
break;
case DYNAMIC_ABILITY_WHO_SOURCEOPPONENT:
secondaryTarget = OriginalSrc->controller()->opponent();
break;
default:
_target = _target;
break;
}
if(amountsource == DYNAMIC_MYSELF_AMOUNT)
_target = OriginalSrc->controller();//looking at controller for amount
if(amountsource == DYNAMIC_MYFOE_AMOUNT)
_target = OriginalSrc->controller()->opponent();//looking at controllers opponent for amount
if(!_target)
return 0;
while (_target->typeAsTarget() == TARGET_CARD && ((MTGCardInstance *)_target)->next)
_target = ((MTGCardInstance *)_target)->next;
//find the amount variables that will be used
sourceamount = 0;
targetamount = 0;
int colored = 0;
switch(type)
{
case DYNAMIC_ABILITY_TYPE_POWER:
sourceamount = ((MTGCardInstance *) source)->power;
targetamount = ((MTGCardInstance *) _target)->power;
if(eachother )
sourceamount = ((MTGCardInstance *) source)->power;
break;
case DYNAMIC_ABILITY_TYPE_TOUGHNESS:
sourceamount = ((MTGCardInstance *) source)->toughness;
targetamount = ((MTGCardInstance *) _target)->toughness;
if(eachother )
sourceamount = ((MTGCardInstance *) source)->toughness;
break;
case DYNAMIC_ABILITY_TYPE_MANACOST:
if(amountsource == 1)
sourceamount = ((MTGCardInstance *) source)->getManaCost()->getConvertedCost();
else
sourceamount = ((MTGCardInstance *) _target)->getManaCost()->getConvertedCost();
break;
case DYNAMIC_ABILITY_TYPE_COLORS:
for (int i = Constants::MTG_COLOR_GREEN; i <= Constants::MTG_COLOR_WHITE; ++i)
{
if (amountsource == 1 && ((MTGCardInstance *)source)->hasColor(i))
++colored;
else
if (amountsource == 2 && ((MTGCardInstance *)_target)->hasColor(i))
++colored;
}
sourceamount = colored;
break;
case DYNAMIC_ABILITY_TYPE_AGE:
{
Counter * targetCounter = NULL;
if(amountsource == 2)
{
if (((MTGCardInstance *)_target)->counters && ((MTGCardInstance *)_target)->counters->hasCounter("age", 0, 0))
{
targetCounter = ((MTGCardInstance *)_target)->counters->hasCounter("age", 0, 0);
sourceamount = targetCounter->nb;
}
}
else
{
if (((MTGCardInstance *)source)->counters && ((MTGCardInstance *)source)->counters->hasCounter("age", 0, 0))
{
targetCounter = ((MTGCardInstance *)source)->counters->hasCounter("age", 0, 0);
sourceamount = targetCounter->nb;
}
}
break;
}
case DYNAMIC_ABILITY_TYPE_CHARGE:
{
Counter * targetCounter = NULL;
if(amountsource == 2)
{
if (((MTGCardInstance *)_target)->counters && ((MTGCardInstance *)_target)->counters->hasCounter("charge", 0, 0))
{
targetCounter = ((MTGCardInstance *)_target)->counters->hasCounter("charge", 0, 0);
sourceamount = targetCounter->nb;
}
}
else
{
if (((MTGCardInstance *)source)->counters && ((MTGCardInstance *)source)->counters->hasCounter("charge", 0, 0))
{
targetCounter = ((MTGCardInstance *)source)->counters->hasCounter("charge", 0, 0);
sourceamount = targetCounter->nb;
}
}
break;
}
case DYNAMIC_ABILITY_TYPE_ONEONECOUNTERS:
{
Counter * targetCounter = NULL;
if(amountsource == 2)
{
if (((MTGCardInstance *)_target)->counters && ((MTGCardInstance *)_target)->counters->hasCounter(1, 1))
{
targetCounter = ((MTGCardInstance *)_target)->counters->hasCounter(1,1);
sourceamount = targetCounter->nb;
}
}
else
{
if (((MTGCardInstance *)source)->counters && ((MTGCardInstance *)source)->counters->hasCounter(1, 1))
{
targetCounter = ((MTGCardInstance *)source)->counters->hasCounter(1,1);
sourceamount = targetCounter->nb;
}
}
break;
}
case DYNAMIC_ABILITY_TYPE_THATMUCH:
{
sourceamount = _target->thatmuch;
break;
}
default:
break;
}
if(secondaryTarget != NULL)
{
_target = secondaryTarget;
}
if (_target)
{
while (_target->typeAsTarget() == TARGET_CARD && ((MTGCardInstance *)_target)->next)
_target = ((MTGCardInstance *)_target)->next;
if(sourceamount < 0)
sourceamount = 0;
if(targetamount < 0)
targetamount = 0;
//set values less then 0 to 0, it was reported that negitive numbers such as a creature who get -3/-3 having the power become
//negitive, if then used as the amount, would cuase weird side effects on resolves.
switch(effect)
{
case DYNAMIC_ABILITY_EFFECT_STRIKE://deal damage
if(storedAbility)
activateStored();
if(tosrc == false)
{
game->mLayers->stackLayer()->addDamage((MTGCardInstance *)source, _target, sourceamount);
game->mLayers->stackLayer()->resolve();
}
else
{
game->mLayers->stackLayer()->addDamage((MTGCardInstance *)source, OriginalSrc, sourceamount);
game->mLayers->stackLayer()->resolve();
}
if(eachother )
{
game->mLayers->stackLayer()->addDamage((MTGCardInstance *)_target, source, targetamount);
game->mLayers->stackLayer()->resolve();
}
return 1;
break;
case DYNAMIC_ABILITY_EFFECT_DRAW://draw cards
if(storedAbility)
activateStored();
game->mLayers->stackLayer()->addDraw((Player *)_target,sourceamount);
game->mLayers->stackLayer()->resolve();
return 1;
break;
case DYNAMIC_ABILITY_EFFECT_LIFEGAIN://gain life
if(storedAbility)
activateStored();
game->mLayers->stackLayer()->addLife(_target,sourceamount);
game->mLayers->stackLayer()->resolve();
return 1;
break;
case DYNAMIC_ABILITY_EFFECT_PUMPPOWER://pump power
{
if(storedAbility)
activateStored();
if(tosrc == false)
{
PTInstant * a = NEW PTInstant(this->GetId(), source, (MTGCardInstance*)_target,NEW WParsedPT(sourceamount,0));
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source,(MTGCardInstance*)_target, a);
wrapper->addToGame();
return 1;
}
else
{
PTInstant * a = NEW PTInstant(this->GetId(), source, OriginalSrc,NEW WParsedPT(sourceamount,0));
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source,OriginalSrc, a);
wrapper->addToGame();
return 1;
}
break;
}
case DYNAMIC_ABILITY_EFFECT_PUMPTOUGHNESS://pump toughness
{
if(storedAbility)
activateStored();
if(tosrc == false)
{
PTInstant * a = NEW PTInstant(this->GetId(), source, (MTGCardInstance*)_target,NEW WParsedPT(0,sourceamount));
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source,(MTGCardInstance*)_target, a);
wrapper->addToGame();
return 1;
}
else
{
PTInstant * a = NEW PTInstant(this->GetId(), source, OriginalSrc,NEW WParsedPT(0,sourceamount));
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source,OriginalSrc, a);
wrapper->addToGame();
return 1;
}
break;
}
case DYNAMIC_ABILITY_EFFECT_PUMPBOTH://pump both
{
if(storedAbility)
activateStored();
if(tosrc == false)
{
PTInstant * a = NEW PTInstant(this->GetId(), source, (MTGCardInstance*)_target,NEW WParsedPT(sourceamount,sourceamount));
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source,(MTGCardInstance*)_target, a);
wrapper->addToGame();
return 1;
}
else
{
PTInstant * a = NEW PTInstant(this->GetId(), source, OriginalSrc,NEW WParsedPT(sourceamount,sourceamount));
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source,OriginalSrc, a);
wrapper->addToGame();
return 1;
}
break;
}
case DYNAMIC_ABILITY_EFFECT_LIFELOSS://lose life
if(storedAbility)
activateStored();
game->mLayers->stackLayer()->addLife(_target,(sourceamount * -1));
game->mLayers->stackLayer()->resolve();
return 1;
break;
case DYNAMIC_ABILITY_EFFECT_DEPLETE://deplete cards
{
if(storedAbility)
activateStored();
Player * player = (Player *)_target;
MTGLibrary * library = player->game->library;
for (int i = 0; i < sourceamount; i++)
{
if (library->nb_cards)
player->game->putInZone(library->cards[library->nb_cards - 1], library, player->game->graveyard);
}
return 1;
break;
}
case DYNAMIC_ABILITY_EFFECT_COUNTERSONEONE:
{
if(_target->typeAsTarget() != TARGET_CARD)
_target = OriginalSrc;
for(int j = 0;j < sourceamount;j++)
((MTGCardInstance*)_target)->counters->addCounter(1,1);
break;
}
default:
return 0;
}
}
return 0;
}
int AADynamic::activateStored()
{
clonedStored = storedAbility->clone();
clonedStored->target = target;
if (clonedStored->oneShot)
{
clonedStored->resolve();
delete (clonedStored);
}
else
{
clonedStored->addToGame();
}
return 1;
}
const char * AADynamic::getMenuText()
{
if (menu.size())
{
return menu.c_str();
}
switch(type)
{
case 0:
menu.append("Power");
break;
case 1:
menu.append("Tough");
break;
case 2:
menu.append("Mana");
break;
case 3:
menu.append("color");
break;
case 4:
menu.append("Elder");
break;
case 5:
menu.append("Charged");
break;
case 6:
menu.append("Counter");
break;
case 7:
menu.append("That Many ");
break;
default:
break;
}
switch(effect)
{
case 0:
menu.append("Strike");
break;
case 1:
menu.append("Draw");
break;
case 2:
menu.append("Life");
break;
case 3:
menu.append("Pump");
break;
case 4:
menu.append("Fortify");
break;
case 5:
menu.append("Buff");
break;
case 6:
menu.append("Drain");
break;
case 7:
menu.append("Deplete!");
break;
case 8:
menu.append("Counters!");
break;
default:
break;
}
sprintf(menuText, "%s", menu.c_str());
return menuText;
}
AADynamic * AADynamic::clone() const
{
AADynamic * a = NEW AADynamic(*this);
a->isClone = 1;
return a;
}
AADynamic::~AADynamic()
{
if (!isClone)
SAFE_DELETE(storedAbility);
}
//AALifer
AALifer::AALifer(int _id, MTGCardInstance * card, Targetable * _target, string life_s, ManaCost * _cost, int who) :
ActivatedAbilityTP(_id, card, _target, _cost, who),life_s(life_s)
{
aType = MTGAbility::LIFER;
}
int AALifer::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (!_target)
return 0;
WParsedInt life(life_s, NULL, source);
if (_target->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE)
{
_target = ((MTGCardInstance *) _target)->controller();
}
Player *player = (Player*)_target;
player->gainOrLoseLife(life.getValue());
return 1;
}
int AALifer::getLife()
{
WParsedInt life(life_s, NULL, source);
return life.getValue();
}
const char * AALifer::getMenuText()
{
if(getLife() < 0)
return "Life Loss";
return "Life";
}
AALifer * AALifer::clone() const
{
AALifer * a = NEW AALifer(*this);
a->isClone = 1;
return a;
}
//players max hand size
AASetHand::AASetHand(int _id, MTGCardInstance * _source, Targetable * _target, int hand, ManaCost * _cost,
int who) :
ActivatedAbilityTP(_id, _source, _target, _cost, who), hand(hand)
{
}
int AASetHand::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (!_target)
return 0;
Player * p = NULL;
if (_target->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE)
{
p = ((MTGCardInstance *) _target)->controller();
}
else
{
p = (Player*)_target;
}
p->handsize = hand;
return 1;
}
const char * AASetHand::getMenuText()
{
return "Set Hand Size";
}
AASetHand * AASetHand::clone() const
{
AASetHand * a = NEW AASetHand(*this);
a->isClone = 1;
return a;
}
//Lifeset
AALifeSet::AALifeSet(int _id, MTGCardInstance * _source, Targetable * _target, WParsedInt * life, ManaCost * _cost,
int who) :
ActivatedAbilityTP(_id, _source, _target, _cost, who), life(life)
{
}
int AALifeSet::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (!_target)
return 0;
Player * p = NULL;
if (_target->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE)
{
p = ((MTGCardInstance *) _target)->controller();
}
else
{
p = (Player*)_target;
}
int lifeDiff = life->getValue() - p->life ;
p->gainOrLoseLife(lifeDiff);
return 1;
}
const char * AALifeSet::getMenuText()
{
return "Set Life";
}
AALifeSet * AALifeSet::clone() const
{
AALifeSet * a = NEW AALifeSet(*this);
a->life = NEW WParsedInt(*(a->life));
a->isClone = 1;
return a;
}
AALifeSet::~AALifeSet()
{
SAFE_DELETE(life);
}
//AACloner
//cloning...this makes a token thats a copy of the target.
AACloner::AACloner(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost, int who,
string abilitiesStringList) :
ActivatedAbility(_id, _source, _cost, 0), who(who)
{
aType = MTGAbility::CLONING;
target = _target;
source = _source;
if (abilitiesStringList.size() > 0)
{
PopulateAbilityIndexVector(awith, abilitiesStringList);
PopulateColorIndexVector(colors, abilitiesStringList);
}
}
int AACloner::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
MTGCardInstance * myClone = NULL;
MTGCard* clone = (_target->isToken ? _target: MTGCollection()->getCardByName(_target->name));
if (who != 1)
{
myClone = NEW MTGCardInstance(clone, source->controller()->game);
}
if (who == 1)
{
myClone = NEW MTGCardInstance(clone, source->controller()->opponent()->game);
}
if (who != 1)
source->controller()->game->temp->addCard(myClone);
else
source->controller()->opponent()->game->temp->addCard(myClone);
Spell * spell = NEW Spell(myClone);
spell->resolve();
spell->source->isToken = 1;
spell->source->fresh = 1;
if(_target->isToken)
{
spell->source->power = _target->origpower;
spell->source->toughness = _target->origtoughness;
spell->source->life = _target->origtoughness;
}
list<int>::iterator it;
for (it = awith.begin(); it != awith.end(); it++)
{
spell->source->basicAbilities[*it] = 1;
}
for (it = colors.begin(); it != colors.end(); it++)
{
spell->source->setColor(*it);
}
delete spell;
return 1;
}
return 0;
}
const char * AACloner::getMenuText()
{
if (who == 1)
return "Clone For Opponent";
return "Clone";
}
ostream& AACloner::toString(ostream& out) const
{
out << "AACloner ::: with : ?" // << abilities
<< " (";
return ActivatedAbility::toString(out) << ")";
}
AACloner * AACloner::clone() const
{
AACloner * a = NEW AACloner(*this);
a->isClone = 1;
return a;
}
AACloner::~AACloner()
{
}
// Cast/Play Restriction modifier
ACastRestriction::ACastRestriction(int _id, MTGCardInstance * card, Targetable * _target, TargetChooser * _restrictionsScope, WParsedInt * _value, bool _modifyExisting, int _zoneId, int who) :
AbilityTP(_id, card, _target, who), restrictionsScope(_restrictionsScope), value(_value), modifyExisting(_modifyExisting),zoneId(_zoneId)
{
existingRestriction = NULL;
targetPlayer = NULL;
}
int ACastRestriction::addToGame()
{
Targetable * _target = getTarget();
if (_target)
{
if (_target->typeAsTarget() == TARGET_CARD)
{
targetPlayer = ((MTGCardInstance *) _target)->controller();
}
else
{
targetPlayer = (Player *) _target;
}
if (modifyExisting)
{
//For now the only modifying rule is the one for lands, so this is hardcoded here.
//This means that a modifying rule for anything lands will actually modify the lands rule.
//In the future, we need a way to "identify" rules that modify an existing restriction, probably by doing a comparison of the TargetChoosers
existingRestriction = targetPlayer->game->playRestrictions->getMaxPerTurnRestrictionByTargetChooser(restrictionsScope);
if(existingRestriction && existingRestriction->maxPerTurn != MaxPerTurnRestriction::NO_MAX)
existingRestriction->maxPerTurn += value->getValue();
}
else
{
TargetChooser * _tc = restrictionsScope->clone();
existingRestriction = NEW MaxPerTurnRestriction(_tc, value->getValue(), MTGGameZone::intToZone(zoneId, targetPlayer));
targetPlayer->game->playRestrictions->addRestriction(existingRestriction);
}
AbilityTP::addToGame();
}
return 0;
}
int ACastRestriction::destroy()
{
if (!existingRestriction)
return 0;
if (modifyExisting)
{
if(existingRestriction->maxPerTurn != MaxPerTurnRestriction::NO_MAX)
existingRestriction->maxPerTurn -= value->getValue();
}
else
{
targetPlayer->game->playRestrictions->removeRestriction(existingRestriction);
SAFE_DELETE(existingRestriction);
}
return 1;
}
const char * ACastRestriction::getMenuText()
{
if (modifyExisting)
return "Additional Lands"; //hardoced because only the lands rule allows to modify existing rule for now
return "Cast Restriction";
}
ACastRestriction * ACastRestriction::clone() const
{
ACastRestriction * a = NEW ACastRestriction(*this);
a->value = NEW WParsedInt(*(a->value));
a->restrictionsScope = restrictionsScope->clone();
a->isClone = 1;
return a;
}
ACastRestriction::~ACastRestriction()
{
SAFE_DELETE(value);
SAFE_DELETE(restrictionsScope);
}
AInstantCastRestrictionUEOT::AInstantCastRestrictionUEOT(int _id, MTGCardInstance * card, Targetable * _target, TargetChooser * _restrictionsScope, WParsedInt * _value, bool _modifyExisting, int _zoneId, int who) :
InstantAbilityTP(_id, card, _target, who)
{
ability = NEW ACastRestriction(_id, card, _target, _restrictionsScope, _value, _modifyExisting, _zoneId, who);
}
int AInstantCastRestrictionUEOT::resolve()
{
ACastRestriction * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
return 1;
}
const char * AInstantCastRestrictionUEOT::getMenuText()
{
return ability->getMenuText();
}
AInstantCastRestrictionUEOT * AInstantCastRestrictionUEOT::clone() const
{
AInstantCastRestrictionUEOT * a = NEW AInstantCastRestrictionUEOT(*this);
a->ability = this->ability->clone();
a->isClone = 1;
return a;
}
AInstantCastRestrictionUEOT::~AInstantCastRestrictionUEOT()
{
SAFE_DELETE(ability);
}
//AAMover
AAMover::AAMover(int _id, MTGCardInstance * _source, MTGCardInstance * _target, string dest, ManaCost * _cost) :
ActivatedAbility(_id, _source, _cost, 0), destination(dest)
{
if (_target)
target = _target;
}
MTGGameZone * AAMover::destinationZone(Targetable * target)
{
MTGCardInstance * _target = (MTGCardInstance *) target;
return MTGGameZone::stringToZone(destination, source, _target);
}
int AAMover::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (target)
{
Player* p = _target->controller();
if (p)
{
GameObserver * g = GameObserver::GetInstance();
MTGGameZone * fromZone = _target->getCurrentZone();
MTGGameZone * destZone = destinationZone(target);
//inplay is a special zone !
for (int i = 0; i < 2; i++)
{
if (destZone == g->players[i]->game->inPlay && fromZone != g->players[i]->game->inPlay && fromZone
!= g->players[i]->opponent()->game->inPlay)
{
MTGCardInstance * copy = g->players[i]->game->putInZone(_target, fromZone, g->players[i]->game->temp);
Spell * spell = NEW Spell(copy);
spell->resolve();
delete spell;
return 1;
}
}
p->game->putInZone(_target, fromZone, destZone);
return 1;
}
}
return 0;
}
const char * AAMover::getMenuText()
{
return "Move";
}
const char * AAMover::getMenuText(TargetChooser * tc)
{
GameObserver * g = GameObserver::GetInstance();
MTGGameZone * dest = destinationZone();
for (int i = 0; i < 2; i++)
{
// Move card to hand
if (dest == g->players[i]->game->hand)
{
if (tc->targetsZone(g->players[i]->game->inPlay))
return "Bounce";
if (tc->targetsZone(g->players[i]->game->graveyard))
return "Reclaim";
if (tc->targetsZone(g->opponent()->game->hand))
return "Steal";
}
// Move card to graveyard
else if (dest == g->players[i]->game->graveyard)
{
if (tc->targetsZone(g->players[i]->game->inPlay))
return "Sacrifice";
if (tc->targetsZone(g->players[i]->game->hand))
return "Discard";
if (tc->targetsZone(g->opponent()->game->hand))
return "Opponent Discards";
}
// move card to library
else if (dest == g->players[i]->game->library)
{
if (tc->targetsZone(g->players[i]->game->graveyard))
return "Recycle";
return "Put in Library";
}
// move card to battlefield
else if (dest == g->players[i]->game->battlefield)
{
if (tc->targetsZone(g->players[i]->game->graveyard))
return "Reanimate";
return "Put in Play";
}
// move card into exile
else if (dest == g->players[i]->game->exile)
{
return "Exile";
}
// move card from Library
else if (tc->targetsZone(g->players[i]->game->library))
{
return "Fetch";
}
}
return "Move";
}
AAMover * AAMover::clone() const
{
AAMover * a = NEW AAMover(*this);
a->isClone = 1;
return a;
}
//Random Discard
AARandomDiscarder::AARandomDiscarder(int _id, MTGCardInstance * card, Targetable * _target,string nbcardsStr, ManaCost * _cost,
int who) :
ActivatedAbilityTP(_id, card, _target, _cost, who), nbcardsStr(nbcardsStr)
{
}
int AARandomDiscarder::resolve()
{
Targetable * _target = getTarget();
Player * player;
if (_target)
{
if (_target->typeAsTarget() == TARGET_CARD)
{
player = ((MTGCardInstance *) _target)->controller();
}
else
{
player = (Player *) _target;
}
WParsedInt numCards(nbcardsStr, NULL, source);
for (int i = 0; i < numCards.intValue; i++)
{
player->game->discardRandom(player->game->hand, source);
}
}
return 1;
}
const char * AARandomDiscarder::getMenuText()
{
return "Discard Random";
}
AARandomDiscarder * AARandomDiscarder::clone() const
{
AARandomDiscarder * a = NEW AARandomDiscarder(*this);
a->isClone = 1;
return a;
}
// Shuffle
AAShuffle::AAShuffle(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, int who) :
ActivatedAbilityTP(_id, card, _target, _cost, who)
{
}
int AAShuffle::resolve()
{
Targetable * _target = getTarget();
Player * player;
if (_target)
{
if (_target->typeAsTarget() == TARGET_CARD)
{
player = ((MTGCardInstance *) _target)->controller();
}
else
{
player = (Player *) _target;
}
MTGLibrary * library = player->game->library;
library->shuffle();
}
return 1;
}
const char * AAShuffle::getMenuText()
{
return "Shuffle";
}
AAShuffle * AAShuffle::clone() const
{
AAShuffle * a = NEW AAShuffle(*this);
a->isClone = 1;
return a;
}
// Remove Mana From ManaPool
AARemoveMana::AARemoveMana(int _id, MTGCardInstance * card, Targetable * _target, string manaDesc, int who) :
ActivatedAbilityTP(_id, card, _target, NULL, who)
{
if (!manaDesc.size())
{
DebugTrace("ALL_ABILITIES: AARemoveMana ctor error");
return;
}
mRemoveAll = (manaDesc[0] == '*');
if (mRemoveAll)
manaDesc = manaDesc.substr(1);
mManaDesc = (manaDesc.size()) ? ManaCost::parseManaCost(manaDesc) : NULL;
}
int AARemoveMana::resolve()
{
Targetable * _target = getTarget();
Player * player;
if (_target)
{
if (_target->typeAsTarget() == TARGET_CARD)
{
player = ((MTGCardInstance *) _target)->controller();
}
else
{
player = (Player *) _target;
}
ManaPool * manaPool = player->getManaPool();
if (mRemoveAll)
{
if (mManaDesc) // Remove all mana Matching a description
{
for (unsigned int i = 0; i < Constants::MTG_NB_COLORS; i++)
{
if (mManaDesc->hasColor(i))
manaPool->removeAll(i);
}
}
else //Remove all mana
{
manaPool->init();
}
}
else //remove a "standard" mana Description
{
((ManaCost *)manaPool)->remove(mManaDesc); //why do I have to cast here?
}
}
return 1;
}
const char * AARemoveMana::getMenuText()
{
if (mRemoveAll && !mManaDesc)
return "Empty Manapool";
return "Remove Mana";
}
AARemoveMana * AARemoveMana::clone() const
{
AARemoveMana * a = NEW AARemoveMana(*this);
a->mManaDesc = mManaDesc ? NEW ManaCost(mManaDesc) : NULL;
a->isClone = 1;
return a;
}
AARemoveMana::~AARemoveMana()
{
SAFE_DELETE(mManaDesc);
}
//Tapper
AATapper::AATapper(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(id, card, _cost, 0)
{
target = _target;
aType = MTGAbility::TAPPER;
}
int AATapper::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next; //This is for cards such as rampant growth
_target->tap();
}
return 1;
}
const char * AATapper::getMenuText()
{
return "Tap";
}
AATapper * AATapper::clone() const
{
AATapper * a = NEW AATapper(*this);
a->isClone = 1;
return a;
}
//AA Untapper
AAUntapper::AAUntapper(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(id, card, _cost, 0)
{
target = _target;
aType = MTGAbility::UNTAPPER;
}
int AAUntapper::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next; //This is for cards such as rampant growth
_target->untap();
}
return 1;
}
const char * AAUntapper::getMenuText()
{
return "Untap";
}
AAUntapper * AAUntapper::clone() const
{
AAUntapper * a = NEW AAUntapper(*this);
a->isClone = 1;
return a;
}
AAWhatsMax::AAWhatsMax(int id, MTGCardInstance * card, MTGCardInstance * source, ManaCost * _cost, int value) :
ActivatedAbility(id, card, _cost, 0), value(value)
{
}
int AAWhatsMax::resolve()
{
if (source)
{
source->MaxLevelUp = value;
source->isLeveler = 1;
}
return 1;
}
AAWhatsMax * AAWhatsMax::clone() const
{
AAWhatsMax * a = NEW AAWhatsMax(*this);
a->isClone = 1;
return a;
}
// Win Game
AAWinGame::AAWinGame(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, int who) :
ActivatedAbilityTP(_id, card, _target, _cost, who)
{
}
int AAWinGame::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (_target)
{
if (_target->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE)
{
_target = ((MTGCardInstance *) _target)->controller();
}
int cantlosers = 0;
MTGGameZone * z = ((Player *) _target)->opponent()->game->inPlay;
int nbcards = z->nb_cards;
for (int i = 0; i < nbcards; i++)
{
MTGCardInstance * c = z->cards[i];
if (c->has(Constants::CANTLOSE))
{
cantlosers++;
}
}
MTGGameZone * k = ((Player *) _target)->game->inPlay;
int onbcards = k->nb_cards;
for (int m = 0; m < onbcards; ++m)
{
MTGCardInstance * e = k->cards[m];
if (e->has(Constants::CANTWIN))
{
cantlosers++;
}
}
if (cantlosers < 1)
{
game->gameOver = ((Player *) _target)->opponent();
}
}
return 1;
}
const char * AAWinGame::getMenuText()
{
return "Win Game";
}
AAWinGame * AAWinGame::clone() const
{
AAWinGame * a = NEW AAWinGame(*this);
a->isClone = 1;
return a;
}
//Generic Abilities
//May Abilities
MayAbility::MayAbility(int _id, MTGAbility * _ability, MTGCardInstance * _source, bool must) :
MTGAbility(_id, _source), NestedAbility(_ability), must(must)
{
triggered = 0;
mClone = NULL;
}
void MayAbility::Update(float dt)
{
MTGAbility::Update(dt);
if (!triggered)
{
triggered = 1;
if (TargetAbility * ta = dynamic_cast<TargetAbility *>(ability))
{
if (!ta->tc->validTargetsExist())
return;
}
game->mLayers->actionLayer()->setMenuObject(source, must);
previousInterrupter = game->isInterrupting;
game->mLayers->stackLayer()->setIsInterrupting(source->controller());
}
}
const char * MayAbility::getMenuText()
{
return ability->getMenuText();
}
int MayAbility::testDestroy()
{
if (!triggered)
return 0;
if (game->mLayers->actionLayer()->menuObject)
return 0;
if (game->mLayers->actionLayer()->getIndexOf(mClone) != -1)
return 0;
if(game->currentPlayer == source->controller() && game->isInterrupting == source->controller() && dynamic_cast<AManaProducer*>(AbilityFactory::getCoreAbility(ability)))
//if its my turn, and im interrupting myself(why?) then set interrupting to previous interrupter if the ability was a manaability
//special case since they don't use the stack.
game->mLayers->stackLayer()->setIsInterrupting(previousInterrupter);
return 1;
}
int MayAbility::isReactingToTargetClick(Targetable * card)
{
if (card == source)
return 1;
return 0;
}
int MayAbility::reactToTargetClick(Targetable * object)
{
mClone = ability->clone();
mClone->addToGame();
mClone->forceDestroy = 1;
return mClone->reactToTargetClick(object);
}
MayAbility * MayAbility::clone() const
{
MayAbility * a = NEW MayAbility(*this);
a->ability = ability->clone();
a->isClone = 1;
return a;
}
MayAbility::~MayAbility()
{
SAFE_DELETE(ability);
}
//MultiAbility : triggers several actions for a cost
MultiAbility::MultiAbility(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost) :
ActivatedAbility(_id, card, _cost, 0)
{
if (_target)
target = _target;
}
int MultiAbility::Add(MTGAbility * ability)
{
abilities.push_back(ability);
return 1;
}
int MultiAbility::resolve()
{
Targetable * Phaseactiontarget = NULL;
vector<int>::size_type sz = abilities.size();
for (unsigned int i = 0; i < sz; i++)
{
if (abilities[i] == NULL)
continue;
Targetable * backup = abilities[i]->target;
if (target && target != source && abilities[i]->target == abilities[i]->source)
{
abilities[i]->target = target;
Phaseactiontarget = target;
}
abilities[i]->resolve();
abilities[i]->target = backup;
if(Phaseactiontarget && dynamic_cast<APhaseActionGeneric *> (abilities[i]))
abilities[i]->target = Phaseactiontarget;
}
return 1;
}
int MultiAbility::addToGame()
{
for (unsigned int i = 0; i < abilities.size(); i++)
{
if (abilities[i] == NULL)
continue;
MTGAbility * a = abilities[i]->clone();
a->target = target;
a->addToGame();
clones.push_back(a);
}
MTGAbility::addToGame();
return 1;
}
int MultiAbility::destroy()
{
for (size_t i = 0; i < clones.size(); ++i)
{
//I'd like to call game->removeObserver here instead of using forceDestroy, but I get a weird crash after that, need to investigate a bit
clones[i]->forceDestroy = 1;
}
clones.clear();
return ActivatedAbility::destroy();
}
const char * MultiAbility::getMenuText()
{
if (abilities.size())
return abilities[0]->getMenuText();
return "";
}
MultiAbility * MultiAbility::clone() const
{
MultiAbility * a = NEW MultiAbility(*this);
a->isClone = 1;
return a;
}
MultiAbility::~MultiAbility()
{
if (!isClone)
{
vector<int>::size_type sz = abilities.size();
for (size_t i = 0; i < sz; i++)
{
SAFE_DELETE(abilities[i]);
}
}
abilities.clear();
}
//Generic Target Ability
GenericTargetAbility::GenericTargetAbility(string newName,int _id, MTGCardInstance * _source, TargetChooser * _tc, MTGAbility * a,
ManaCost * _cost, string limit,MTGAbility * sideEffects,string usesBeforeSideEffects, int restrictions, MTGGameZone * dest) :
TargetAbility(_id, _source, _tc, _cost, restrictions), limit(limit), activeZone(dest),newName(newName)
{
ability = a;
MTGAbility * core = AbilityFactory::getCoreAbility(a);
if (dynamic_cast<AACopier *> (core))
tc->other = true; //http://code.google.com/p/wagic/issues/detail?id=209 (avoid inifinite loop)
counters = 0;
}
const char * GenericTargetAbility::getMenuText()
{
if (!ability)
return "Error";
if (newName.size())
return newName.c_str();
//Special case for move
MTGAbility * core = AbilityFactory::getCoreAbility(ability);
if (AAMover * move = dynamic_cast<AAMover *>(core))
return (move->getMenuText(tc));
return ability->getMenuText();
}
int GenericTargetAbility::resolve()
{
counters++;
return TargetAbility::resolve();
}
int GenericTargetAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana)
{
limitPerTurn = 0;
if(limit.size())
{
WParsedInt * value = NEW WParsedInt(limit.c_str(),NULL,source);
limitPerTurn = value->getValue();
delete value;
}
if (limitPerTurn && counters >= limitPerTurn)
return 0;
return TargetAbility::isReactingToClick(card, mana);
}
void GenericTargetAbility::Update(float dt)
{
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_AFTER_EOT)
{
counters = 0;
}
TargetAbility::Update(dt);
}
int GenericTargetAbility::testDestroy()
{
if (!activeZone)
return TargetAbility::testDestroy();
if (activeZone->hasCard(source))
return 0;
return 1;
}
GenericTargetAbility * GenericTargetAbility::clone() const
{
GenericTargetAbility * a = NEW GenericTargetAbility(*this);
a->ability = ability->clone();
a->cost = NEW ManaCost();
a->cost->copy(cost);
if (tc)
a->tc = tc->clone();
return a;
}
GenericTargetAbility::~GenericTargetAbility()
{
SAFE_DELETE(ability);
}
//Alter Cost
AAlterCost::AAlterCost(int id, MTGCardInstance * source, MTGCardInstance * target, int amount, int type) :
MTGAbility(id, source, target), amount(amount), type(type)
{
manaReducer = source;
}
int AAlterCost::addToGame()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if(!_target || _target->hasType("land"))
{
this->forceDestroy = 1;
return MTGAbility::addToGame();
}
if (amount > 0)
{
if(!_target->getIncreasedManaCost()->getConvertedCost())
{
ManaCost * increased = NEW ManaCost();
increased->init();
_target->getIncreasedManaCost()->copy(increased);
delete increased;
}
_target->getIncreasedManaCost()->add(type,amount);
}
else
{
if(!_target->getReducedManaCost()->getConvertedCost())
{
ManaCost * reduced = NEW ManaCost();
reduced->init();
_target->getReducedManaCost()->copy(reduced);
delete reduced;
}
_target->getReducedManaCost()->add(type,abs(amount));
}
return MTGAbility::addToGame();
}
int AAlterCost::testDestroy()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
if(!this->manaReducer->isInPlay())
{
if (amount > 0)
{
_target->getIncreasedManaCost()->remove(type,amount);
refreshCost(_target);//special case for 0 cost.
}
else
{
_target->getReducedManaCost()->remove(type,abs(amount));
refreshCost(_target);//special case for 0 cost.
}
return MTGAbility::testDestroy();
}
return 0;
}
void AAlterCost::refreshCost(MTGCardInstance * card)
{
ManaCost * original = NEW ManaCost();
original->copy(card->model->data->getManaCost());
original->add(card->getIncreasedManaCost());
original->remove(card->getReducedManaCost());
card->getManaCost()->copy(original);
delete original;
return;
}
void AAlterCost::increaseTheCost(MTGCardInstance * card)
{
if(card->getIncreasedManaCost()->getConvertedCost())
{
for(int k = Constants::MTG_COLOR_ARTIFACT; k < Constants::MTG_NB_COLORS;k++)
{
card->getManaCost()->add(k,card->getIncreasedManaCost()->getCost(k));
if (card->getManaCost()->alternative)
{
card->getManaCost()->alternative->add(k,card->getIncreasedManaCost()->getCost(k));
}
if (card->getManaCost()->BuyBack)
{
card->getManaCost()->BuyBack->add(k,card->getIncreasedManaCost()->getCost(k));
}
}
}
return;
}
void AAlterCost::decreaseTheCost(MTGCardInstance * card)
{
if(card->getReducedManaCost()->getConvertedCost())
{
for(int k = Constants::MTG_COLOR_ARTIFACT; k < Constants::MTG_NB_COLORS;k++)
{
card->getManaCost()->remove(k,card->getReducedManaCost()->getCost(k));
if (card->getManaCost()->alternative)
{
card->getManaCost()->alternative->remove(k,card->getReducedManaCost()->getCost(k));
}
if (card->getManaCost()->BuyBack)
{
card->getManaCost()->BuyBack->remove(k,card->getReducedManaCost()->getCost(k));
}
}
}
return;
}
AAlterCost * AAlterCost::clone() const
{
AAlterCost * a = NEW AAlterCost(*this);
a->isClone = 1;
return a;
}
AAlterCost::~AAlterCost()
{
}
// ATransformer
ATransformer::ATransformer(int id, MTGCardInstance * source, MTGCardInstance * target, string stypes, string sabilities,string newpower,bool newpowerfound,string newtoughness,bool newtoughnessfound,vector<string> newAbilitiesList,bool newAbilityFound,bool aForever) :
MTGAbility(id, source, target),newpower(newpower),newpowerfound(newpowerfound),newtoughness(newtoughness),newtoughnessfound(newtoughnessfound),newAbilitiesList(newAbilitiesList),newAbilityFound(newAbilityFound),aForever(aForever)
{
PopulateAbilityIndexVector(abilities, sabilities);
PopulateColorIndexVector(colors, sabilities);
//this subkeyword adds a color without removing the existing colors.
addNewColors = (sabilities.find("newcolors") != string::npos);
remove = (stypes.find("removealltypes") != string::npos);
removeCreatureSubtypes = (stypes.find("removecreaturesubtypes") != string::npos);
removeTypes = (stypes.find("removetypes") != string::npos);
if (stypes.find("allsubtypes") != string::npos || stypes.find("removecreaturesubtypes") != string::npos)
{
const vector<string> values = Subtypes::subtypesList->getValuesById();
for (size_t i = 0; i <values.size(); ++i)
{
if (!Subtypes::subtypesList->isSubtypeOfType(i,Subtypes::TYPE_CREATURE))
continue;
types.push_back(i);
}
}
else
{
PopulateSubtypesIndexVector(types, stypes);
}
menu = stypes;
}
int ATransformer::addToGame()
{
MTGCardInstance * _target = NULL;
Interruptible * action = (Interruptible *) target;
if (action->type == ACTION_SPELL && action->state == NOT_RESOLVED)
{
Spell * spell = (Spell *) action;
_target = spell->source;
aForever = true;
//when targeting the stack, set the effect to forever, incase the person does not use it
//otherwise we will end up with a null pointer on the destroy.
}
else
{
_target = (MTGCardInstance *) target;
}
if (!_target)
{
DebugTrace("ALL_ABILITIES: Target not set in ATransformer::addToGame\n");
return 0;
}
while (_target->next)
_target = _target->next;
for (int j = 0; j < Constants::MTG_NB_COLORS; j++)
{
if (_target->hasColor(j))
oldcolors.push_back(j);
}
for (size_t j = 0; j < _target->types.size(); ++j)
oldtypes.push_back( _target->types[j]);
list<int>::iterator it;
for (it = colors.begin(); it != colors.end(); it++)
{
if(!addNewColors)
_target->setColor(0, 1);
}
if (removeTypes)
{
//remove the main types from a card, ie: hidden enchantment cycle.
for (int i = 0; i < Subtypes::LAST_TYPE; ++ i)
_target->removeType(i,1);
}
else if (remove)
{
for (it = oldtypes.begin(); it != oldtypes.end(); it++)
{
_target->removeType(*it);
}
}
else
{
for (it = types.begin(); it != types.end(); it++)
{
if(removeCreatureSubtypes)
{
_target->removeType(*it);
}
else if(_target->hasSubtype(*it))
{
//we generally don't want to give a creature type creature again
//all it does is create a sloppy mess of the subtype line on alternative quads
//also creates instances where a card gained a type from an ability like this one
//then loses the type through another ability, when this effect is destroyed the creature regains
//the type, which is wrong.
dontremove.push_back(*it);
}
else
{
_target->addType(*it);
}
}
}
for (it = colors.begin(); it != colors.end(); it++)
{
_target->setColor(*it);
}
for (it = abilities.begin(); it != abilities.end(); it++)
{
_target->basicAbilities.set(*it);
}
if(newAbilityFound)
{
for (unsigned int k = 0 ; k < newAbilitiesList.size();k++)
{
AbilityFactory af;
MTGAbility * aNew = af.parseMagicLine(newAbilitiesList[k], 0, NULL, _target);
aNew->isClone = 1;
GenericTargetAbility * gta = dynamic_cast<GenericTargetAbility*> (aNew);
if (gta)
{
((GenericTargetAbility *)aNew)->source = _target;
((GenericTargetAbility *)aNew)->ability->source = _target;
}
GenericActivatedAbility * gaa = dynamic_cast<GenericActivatedAbility*> (aNew);
if (gaa)
{
((GenericActivatedAbility *)aNew)->source = _target;
((GenericActivatedAbility *)aNew)->ability->source = _target;
}
MultiAbility * abi = dynamic_cast<MultiAbility*>(aNew);
if (abi)
{
((MultiAbility *)aNew)->source = _target;
((MultiAbility *)aNew)->abilities[0]->source = _target;
}
aNew->target = _target;
aNew->source = (MTGCardInstance *) _target;
if(aNew->oneShot)
{
aNew->resolve();
delete aNew;
}
else
aNew->addToGame();
newAbilities[_target].push_back(aNew);
}
}
if(newpowerfound )
{
WParsedInt * val = NEW WParsedInt(newpower,NULL, source);
oldpower = _target->power;
_target->power += val->getValue();
_target->power -= oldpower;
_target->power += reapplyCountersBonus(_target,false,true);
delete val;
}
if(newtoughnessfound )
{
WParsedInt * val = NEW WParsedInt(newtoughness,NULL, source);
oldtoughness = _target->toughness;
_target->addToToughness(val->getValue());
_target->addToToughness(-oldtoughness);
_target->addToToughness(reapplyCountersBonus(_target,true,false));
_target->life = _target->toughness;
delete val;
}
return MTGAbility::addToGame();
}
int ATransformer::reapplyCountersBonus(MTGCardInstance * rtarget,bool powerapplied,bool toughnessapplied)
{
if(!rtarget->counters)
return 0;
Counter * c = NULL;
c = rtarget->counters->counters[0];
int rNewPower = 0;
int rNewToughness = 0;
for (int t = 0; t < rtarget->counters->mCount; t++)
{
if (c)
{
for(int i = 0;i < c->nb;i++)
{
rNewPower += c->power;
rNewToughness += c->toughness;
}
}
c = rtarget->counters->getNext(c);
}
if(toughnessapplied)
return rNewToughness;
return rNewPower;
}
int ATransformer::destroy()
{
if(aForever)
return 0;
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next;
list<int>::iterator it;
if (!remove)
{
for (it = types.begin(); it != types.end(); it++)
{
bool removing = true;
for(unsigned int k = 0;k < dontremove.size();k++)
{
if(dontremove[k] == *it)
removing = false;
}
if(removing)
_target->removeType(*it);
}
//iterators annoy me :/
}
for (it = colors.begin(); it != colors.end(); it++)
{
_target->removeColor(*it);
}
for (it = abilities.begin(); it != abilities.end(); it++)
{
_target->basicAbilities.reset(*it);
}
for (it = oldcolors.begin(); it != oldcolors.end(); it++)
{
_target->setColor(*it);
}
if(newpowerfound )
{
_target->power = oldpower;
}
if(newtoughnessfound )
{
_target->toughness = oldtoughness;
}
if(newAbilityFound)
{
for (unsigned int i = 0;i < newAbilities[_target].size(); i++)
{
newAbilities[_target].at(i)->forceDestroy = 1;
}
if (newAbilities.find(_target) != newAbilities.end())
{
newAbilities.erase(_target);
}
}
if (remove)
{
for (it = oldtypes.begin(); it != oldtypes.end(); it++)
{
_target->addType(*it);
}
}
//in the case that we removed or added types to a card, so that it retains its original name when the effect is removed.
if(_target->model->data->name.size())//tokens don't have a model name.
_target->setName(_target->model->data->name.c_str());
}
return 1;
}
const char * ATransformer::getMenuText()
{
string s = menu;
sprintf(menuText, "Becomes %s", s.c_str());
return menuText;
}
ATransformer * ATransformer::clone() const
{
ATransformer * a = NEW ATransformer(*this);
a->isClone = 1;
return a;
}
ATransformer::~ATransformer()
{
}
//ATransformerInstant
ATransformerInstant::ATransformerInstant(int id, MTGCardInstance * source, MTGCardInstance * target, string types, string abilities,string newpower,bool newpowerfound,string newtoughness,bool newtoughnessfound,vector<string>newAbilitiesList,bool newAbilityFound,bool aForever) :
InstantAbility(id, source, target),newpower(newpower),newpowerfound(newpowerfound),newtoughness(newtoughness),newtoughnessfound(newtoughnessfound),newAbilitiesList(newAbilitiesList),newAbilityFound(newAbilityFound),aForever(aForever)
{
ability = NEW ATransformer(id, source, target, types, abilities,newpower,newpowerfound,newtoughness,newtoughnessfound,newAbilitiesList,newAbilityFound,aForever);
aType = MTGAbility::STANDARD_BECOMES;
}
int ATransformerInstant::resolve()
{
ATransformer * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
return 1;
}
const char * ATransformerInstant::getMenuText()
{
return ability->getMenuText();
}
ATransformerInstant * ATransformerInstant::clone() const
{
ATransformerInstant * a = NEW ATransformerInstant(*this);
a->ability = this->ability->clone();
a->isClone = 1;
return a;
}
ATransformerInstant::~ATransformerInstant()
{
SAFE_DELETE(ability);
}
//P/t ueot
PTInstant::PTInstant(int id, MTGCardInstance * source, MTGCardInstance * target, WParsedPT * wppt,string s,bool nonstatic) :
InstantAbility(id, source, target), wppt(wppt),s(s),nonstatic(nonstatic)
{
ability = NEW APowerToughnessModifier(id, source, target, wppt,s,nonstatic);
aType = MTGAbility::STANDARD_PUMP;
}
int PTInstant::resolve()
{
APowerToughnessModifier * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
return 1;
}
const char * PTInstant::getMenuText()
{
return ability->getMenuText();
}
PTInstant * PTInstant::clone() const
{
PTInstant * a = NEW PTInstant(*this);
a->ability = this->ability->clone();
a->isClone = 1;
return a;
}
PTInstant::~PTInstant()
{
SAFE_DELETE(ability);
}
// ASwapPTUEOT
ASwapPTUEOT::ASwapPTUEOT(int id, MTGCardInstance * source, MTGCardInstance * target) :
InstantAbility(id, source, target)
{
ability = NEW ASwapPT(id, source, target);
}
int ASwapPTUEOT::resolve()
{
ASwapPT * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
return 1;
}
const char * ASwapPTUEOT::getMenuText()
{
return ability->getMenuText();
}
ASwapPTUEOT * ASwapPTUEOT::clone() const
{
ASwapPTUEOT * a = NEW ASwapPTUEOT(*this);
a->ability = this->ability->clone();
a->isClone = 1;
return a;
}
ASwapPTUEOT::~ASwapPTUEOT()
{
SAFE_DELETE(ability);
}
//ALoseAbilities
ALoseAbilities::ALoseAbilities(int id, MTGCardInstance * source, MTGCardInstance * _target) :
MTGAbility(id, source)
{
target = _target;
}
int ALoseAbilities::addToGame()
{
if (storedAbilities.size())
{
DebugTrace("FATAL:storedAbilities shouldn't be already set inALoseAbilitie\n");
return 0;
}
MTGCardInstance * _target = (MTGCardInstance *)target;
ActionLayer * al = game->mLayers->actionLayer();
for (int i = (int)(al->mObjects.size()) - 1; i > 0; i--) //0 is not a mtgability...hackish
{
if (al->mObjects[i])
{
MTGAbility * currentAction = (MTGAbility *) al->mObjects[i];
ALoseAbilities * la = dynamic_cast<ALoseAbilities*> (currentAction);
if(la)
continue;
if (currentAction->source == _target)
{
storedAbilities.push_back(currentAction);
al->removeFromGame(currentAction);
}
}
}
return MTGAbility::addToGame();
}
int ALoseAbilities::destroy()
{
for (size_t i = 0; i < storedAbilities.size(); ++i)
{
MTGAbility * a = storedAbilities[i];
//OneShot abilities are not supposed to stay in the game for long.
// If we copied one, something wrong probably happened
ALoseAbilities * la = dynamic_cast<ALoseAbilities*> (a);
if (a->oneShot||la)
{
if(la)
{
DebugTrace("Dangerous chance of infinate loop avoided!");
continue;
}
else
{
DebugTrace("ALLABILITIES: Ability should not be one shot");
continue;
}
}
a->addToGame();
}
storedAbilities.clear();
return 1;
}
ALoseAbilities * ALoseAbilities::clone() const
{
ALoseAbilities * a = NEW ALoseAbilities(*this);
a->isClone = 1;
return a;
}
//ALoseSubtypes
ALoseSubtypes::ALoseSubtypes(int id, MTGCardInstance * source, MTGCardInstance * _target, int parentType) :
MTGAbility(id, source), parentType(parentType)
{
target = _target;
}
int ALoseSubtypes::addToGame()
{
if (storedSubtypes.size())
{
DebugTrace("FATAL:storedSubtypes shouldn't be already set inALoseSubtypes\n");
return 0;
}
MTGCardInstance * _target = (MTGCardInstance *)target;
for (int i = ((int)_target->types.size())-1; i >= 0; --i)
{
int subtype = _target->types[i];
if (Subtypes::subtypesList->isSubtypeOfType(subtype, parentType))
{
storedSubtypes.push_back(subtype);
_target->removeType(subtype);
}
}
return MTGAbility::addToGame();
}
int ALoseSubtypes::destroy()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
for (size_t i = 0; i < storedSubtypes.size(); ++i)
_target->addType(storedSubtypes[i]);
storedSubtypes.clear();
return 1;
}
ALoseSubtypes * ALoseSubtypes::clone() const
{
ALoseSubtypes * a = NEW ALoseSubtypes(*this);
a->isClone = 1;
return a;
}
//APreventDamageTypes
APreventDamageTypes::APreventDamageTypes(int id, MTGCardInstance * source, string to, string from, int type) :
MTGAbility(id, source), to(to), from(from), type(type)
{
re = NULL;
}
int APreventDamageTypes::addToGame()
{
if (re)
{
DebugTrace("FATAL:re shouldn't be already set in APreventDamageTypes\n");
return 0;
}
TargetChooserFactory tcf;
TargetChooser *toTc = tcf.createTargetChooser(to, source, this);
if (toTc)
toTc->targetter = NULL;
TargetChooser *fromTc = tcf.createTargetChooser(from, source, this);
if (fromTc)
fromTc->targetter = NULL;
if (type != 1 && type != 2)
{//not adding this creates a memory leak.
re = NEW REDamagePrevention(this, fromTc, toTc, -1, false, DAMAGE_COMBAT);
}
else if (type == 1)
{
re = NEW REDamagePrevention(this, fromTc, toTc, -1, false, DAMAGE_ALL_TYPES);
}
else if (type == 2)
{
re = NEW REDamagePrevention(this, fromTc, toTc, -1, false, DAMAGE_OTHER);
}
game->replacementEffects->add(re);
return MTGAbility::addToGame();
}
int APreventDamageTypes::destroy()
{
game->replacementEffects->remove(re);
SAFE_DELETE(re);
return 1;
}
APreventDamageTypes * APreventDamageTypes::clone() const
{
APreventDamageTypes * a = NEW APreventDamageTypes(*this);
a->isClone = 1;
return a;
}
APreventDamageTypes::~APreventDamageTypes()
{
SAFE_DELETE(re);
}
//APreventDamageTypesUEOT
APreventDamageTypesUEOT::APreventDamageTypesUEOT(int id, MTGCardInstance * source, string to, string from, int type) :
InstantAbility(id, source)
{
ability = NEW APreventDamageTypes(id, source, to, from, type);
}
int APreventDamageTypesUEOT::resolve()
{
APreventDamageTypes * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
return 1;
}
int APreventDamageTypesUEOT::destroy()
{
for (size_t i = 0; i < clones.size(); ++i)
{
clones[i]->forceDestroy = 0;
}
clones.clear();
return 1;
}
const char * APreventDamageTypesUEOT::getMenuText()
{
return ability->getMenuText();
}
APreventDamageTypesUEOT * APreventDamageTypesUEOT::clone() const
{
APreventDamageTypesUEOT * a = NEW APreventDamageTypesUEOT(*this);
a->ability = this->ability->clone();
a->isClone = 1;
return a;
}
APreventDamageTypesUEOT::~APreventDamageTypesUEOT()
{
SAFE_DELETE(ability);
}
//AVanishing creature also fading
AVanishing::AVanishing(int _id, MTGCardInstance * card, ManaCost * _cost, int restrictions, int amount, string counterName) :
MTGAbility(_id, source, target),amount(amount),counterName(counterName)
{
target = card;
source = card;
next = 0;
for(int i = 0;i< amount;i++)
source->counters->addCounter(counterName.c_str(),0,0);
}
void AVanishing::Update(float dt)
{
if (newPhase != currentPhase && source->controller() == game->currentPlayer)
{
if(newPhase == Constants::MTG_PHASE_UPKEEP)
{
source->counters->removeCounter(counterName.c_str(),0,0);
Counter * targetCounter = NULL;
timeLeft = 0;
if (source->counters && source->counters->hasCounter(counterName.c_str(), 0, 0))
{
targetCounter = source->counters->hasCounter(counterName.c_str(), 0, 0);
timeLeft = targetCounter->nb;
}
else
{
timeLeft = 0;
if(counterName.find("fade") != string::npos && next == 0)
{
next = 1;
}
else
{
next = 0;
}
if (newPhase == Constants::MTG_PHASE_UPKEEP && timeLeft <= 0 && next == 0)
{
WEvent * e = NEW WEventCardSacrifice(source);
GameObserver * game = GameObserver::GetInstance();
game->receiveEvent(e);
source->controller()->game->putInGraveyard(source);
}
}
}
}
MTGAbility::Update(dt);
}
int AVanishing::resolve()
{
return 1;
}
const char * AVanishing::getMenuText()
{
if(counterName.find("fade") != string::npos)
return "Fading";
return "Vanishing";
}
AVanishing * AVanishing::clone() const
{
AVanishing * a = NEW AVanishing(*this);
a->isClone = 1;
return a;
}
AVanishing::~AVanishing()
{
}
//AUpkeep
AUpkeep::AUpkeep(int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int restrictions, int _phase,
int _once,bool Cumulative) :
ActivatedAbility(_id, card, _cost, restrictions), NestedAbility(a), phase(_phase), once(_once),Cumulative(Cumulative)
{
paidThisTurn = 0;
aType = MTGAbility::UPCOST;
}
int AUpkeep::receiveEvent(WEvent * event)
{
if (WEventPhaseChange* pe = dynamic_cast<WEventPhaseChange*>(event))
{
if (Constants::MTG_PHASE_DRAW == pe->to->id)
{
if (source->controller() == game->currentPlayer && once < 2 && paidThisTurn < 1)
{
ability->resolve();
}
}
}
return 1;
}
void AUpkeep::Update(float dt)
{
// once: 0 means always go off, 1 means go off only once, 2 means go off only once and already has.
if (newPhase != currentPhase && source->controller() == game->currentPlayer && once < 2)
{
if (newPhase == Constants::MTG_PHASE_UNTAP)
{
paidThisTurn = 0;
}
else if(newPhase == Constants::MTG_PHASE_UPKEEP && Cumulative )
{
source->counters->addCounter("age",0,0);
Counter * targetCounter = NULL;
currentage = 0;
if (source->counters && source->counters->hasCounter("age", 0, 0))
{
targetCounter = source->counters->hasCounter("age", 0, 0);
currentage = targetCounter->nb - 1;
}
if(currentage)
paidThisTurn -= currentage;
}
if (newPhase == phase + 1 && once)
once = 2;
}
ActivatedAbility::Update(dt);
}
int AUpkeep::isReactingToClick(MTGCardInstance * card, ManaCost * mana)
{
if (currentPhase != phase || paidThisTurn > 0 || once >= 2)
return 0;
return ActivatedAbility::isReactingToClick(card, mana);
}
int AUpkeep::resolve()
{
paidThisTurn += 1;
return 1;
}
const char * AUpkeep::getMenuText()
{
return "Upkeep";
}
ostream& AUpkeep::toString(ostream& out) const
{
out << "AUpkeep ::: paidThisTurn : " << paidThisTurn << " (";
return ActivatedAbility::toString(out) << ")";
}
AUpkeep * AUpkeep::clone() const
{
AUpkeep * a = NEW AUpkeep(*this);
a->isClone = 1;
return a;
}
AUpkeep::~AUpkeep()
{
if (!isClone)
SAFE_DELETE(ability);
}
//A Phase based Action
APhaseAction::APhaseAction(int _id, MTGCardInstance * card, MTGCardInstance * target, string sAbility, int restrictions, int _phase,bool forcedestroy,bool next,bool myturn,bool opponentturn,bool once) :
MTGAbility(_id, card),sAbility(sAbility), phase(_phase),forcedestroy(forcedestroy),next(next),myturn(myturn),opponentturn(opponentturn),once(once)
{
abilityId = _id;
abilityOwner = card->controller();
psMenuText = "";
AbilityFactory af;
ability = af.parseMagicLine(sAbility, abilityId, NULL, NULL);
if(ability)
psMenuText = ability->getMenuText();
else
psMenuText = sAbility.c_str();
delete (ability);
}
void APhaseAction::Update(float dt)
{
if (newPhase != currentPhase)
{
if((myturn && game->currentPlayer == source->controller())||
(opponentturn && game->currentPlayer != source->controller())/*||*/
/*(myturn && opponentturn)*/)
{
if(newPhase == phase && next )
{
MTGCardInstance * _target = NULL;
if(target)
_target = (MTGCardInstance *) target;
if(!sAbility.size() || (!target||(!_target->currentZone && _target != this->source)))
{
this->forceDestroy = 1;
return;
}
else
{
while(_target->next)
_target = _target->next;
}
AbilityFactory af;
MTGAbility * ability = af.parseMagicLine(sAbility, abilityId, NULL, _target);
MTGAbility * a = ability->clone();
a->target = _target;
a->resolve();
delete (a);
delete (ability);
if(this->oneShot || once)
{
this->forceDestroy = 1;
}
}
else if(newPhase == phase && next == false)
next = true;
}
}
MTGAbility::Update(dt);
}
int APhaseAction::resolve()
{
return 0;
}
const char * APhaseAction::getMenuText()
{
if(psMenuText.size())
return psMenuText.c_str();
else
return "Phase Based Action";
}
APhaseAction * APhaseAction::clone() const
{
APhaseAction * a = NEW APhaseAction(*this);
if(forcedestroy == false)
a->forceDestroy = -1;// we want this ability to stay alive until it resolves.
a->isClone = 1;
return a;
}
APhaseAction::~APhaseAction()
{
}
// the main ability
APhaseActionGeneric::APhaseActionGeneric(int _id, MTGCardInstance * card, MTGCardInstance * target, string sAbility, int restrictions, int _phase,bool forcedestroy,bool next,bool myturn,bool opponentturn,bool once) :
InstantAbility(_id, source, target)
{
MTGCardInstance * _target = target;
ability = NEW APhaseAction(_id, card,_target, sAbility, restrictions, _phase,forcedestroy,next,myturn,opponentturn,once);
}
int APhaseActionGeneric::resolve()
{
APhaseAction * a = ability->clone();
a->target = target;
a->addToGame();
return 1;
}
const char * APhaseActionGeneric::getMenuText()
{
return ability->getMenuText();
}
APhaseActionGeneric * APhaseActionGeneric::clone() const
{
APhaseActionGeneric * a = NEW APhaseActionGeneric(*this);
a->ability = this->ability->clone();
a->oneShot = 1;
a->isClone = 1;
return a;
}
APhaseActionGeneric::~APhaseActionGeneric()
{
SAFE_DELETE(ability);
}
//a blink
ABlink::ABlink(int _id, MTGCardInstance * card, MTGCardInstance * _target,bool blinkueot,bool blinkForSource,bool blinkhand,MTGAbility * stored) :
MTGAbility(_id, card),blinkueot(blinkueot),blinkForSource(blinkForSource),blinkhand(blinkhand),stored(stored)
{
target = _target;
Blinked = NULL;
resolved = false;
}
void ABlink::Update(float dt)
{
if(resolved == false)
{
resolved = true;
resolveBlink();
}
GameObserver * game = game->GetInstance();
if ((blinkueot && currentPhase == Constants::MTG_PHASE_ENDOFTURN)||(blinkForSource && !source->isInPlay()))
{
if(Blinked == NULL)
MTGAbility::Update(dt);
MTGCardInstance * _target = Blinked;
MTGCardInstance * Blinker = NULL;
if(!blinkhand)
Blinker = _target->controller()->game->putInZone(_target, _target->currentZone,
_target->owner->game->battlefield);
if(blinkhand)
{
_target->controller()->game->putInZone(_target, _target->currentZone,
_target->owner->game->hand);
return;
}
Spell * spell = NEW Spell(Blinker);
spell->source->counters->init();
if(spell->source->hasSubtype(Subtypes::TYPE_AURA) && !blinkhand)
{
TargetChooserFactory tcf;
TargetChooser * tc = tcf.createTargetChooser(spell->source->spellTargetType,spell->source);
if(!tc->validTargetsExist())
{
spell->source->owner->game->putInExile(spell->source);
delete spell;
delete tc;
this->forceDestroy = 1;
return;
}
MTGGameZone * inplay = spell->source->owner->game->inPlay;
spell->source->target = NULL;
for(int i = WRand()%inplay->nb_cards;;i = WRand()%inplay->nb_cards)
{
if(tc->canTarget(inplay->cards[i]) && spell->source->target == NULL)
{
spell->source->target = inplay->cards[i];
spell->getNextCardTarget();
spell->resolve();
delete spell;
delete tc;
this->forceDestroy = 1;
return;
}
if(!tc->validTargetsExist())
return;
}
}
spell->source->power = spell->source->origpower;
spell->source->toughness = spell->source->origtoughness;
if(!spell->source->hasSubtype(Subtypes::TYPE_AURA))
{
spell->resolve();
if(stored)
{
MTGAbility * clonedStored = stored->clone();
clonedStored->target = spell->source;
if (clonedStored->oneShot)
{
clonedStored->resolve();
delete (clonedStored);
}
else
{
clonedStored->addToGame();
}
}
}
delete spell;
this->forceDestroy = 1;
Blinker = NULL;
return;
}
MTGAbility::Update(dt);
}
void ABlink::resolveBlink()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
if(blinkhand && !_target->controller()->game->isInZone(_target,_target->controller()->game->hand))
{
this->forceDestroy = 1;
return;
}
else if(!blinkhand && !_target->controller()->game->isInZone(_target,_target->controller()->game->battlefield))
{
this->forceDestroy = 1;
return;
}
_target->controller()->game->putInZone(_target, _target->currentZone,
_target->owner->game->exile);
if(_target->isToken)
{
//if our target is a token, we're done as soon as its sent to exile.
this->forceDestroy = 1;
return;
}
_target = _target->next;
Blinked = _target;
if(!blinkueot && !blinkForSource)
{
MTGCardInstance * Blinker = NULL;
if(!blinkhand)
Blinker = _target->controller()->game->putInZone(_target, _target->currentZone,
_target->owner->game->battlefield);
if(blinkhand)
{
_target->controller()->game->putInZone(_target, _target->currentZone,
_target->owner->game->hand);
return;
}
Spell * spell = NEW Spell(Blinker);
spell->source->counters->init();
if(spell->source->hasSubtype(Subtypes::TYPE_AURA) && !blinkhand)
{
TargetChooserFactory tcf;
TargetChooser * tc = tcf.createTargetChooser(spell->source->spellTargetType,spell->source);
if(!tc->validTargetsExist())
{
spell->source->owner->game->putInExile(spell->source);
delete spell;
delete tc;
this->forceDestroy = 1;
return;
}
MTGGameZone * inplay = spell->source->owner->game->inPlay;
spell->source->target = NULL;
for(int i = WRand()%inplay->nb_cards;;i = WRand()%inplay->nb_cards)
{
if(tc->canTarget(inplay->cards[i]) && spell->source->target == NULL)
{
spell->source->target = inplay->cards[i];
spell->getNextCardTarget();
spell->resolve();
delete spell;
delete tc;
this->forceDestroy = 1;
return;
}
}
}
spell->source->power = spell->source->origpower;
spell->source->toughness = spell->source->origtoughness;
spell->resolve();
if(stored)
{
MTGAbility * clonedStored = stored->clone();
clonedStored->target = spell->source;
if (clonedStored->oneShot)
{
clonedStored->resolve();
delete (clonedStored);
}
else
{
clonedStored->addToGame();
}
}
delete tc;
delete spell;
this->forceDestroy = 1;
if(stored)
delete(stored);
Blinked = NULL;
}
}
}
int ABlink::resolve()
{
return 0;
}
const char * ABlink::getMenuText()
{
return "Blink";
}
ABlink * ABlink::clone() const
{
ABlink * a = NEW ABlink(*this);
a->isClone = 1;
a->forceDestroy = -1;
return a;
};
ABlink::~ABlink()
{
if (!isClone)
SAFE_DELETE(stored);
}
ABlinkGeneric::ABlinkGeneric(int _id, MTGCardInstance * card, MTGCardInstance * _target,bool blinkueot,bool blinkForSource,bool blinkhand,MTGAbility * stored) :
InstantAbility(_id, source, _target)
{
ability = NEW ABlink(_id,card,_target,blinkueot,blinkForSource,blinkhand,stored);
}
int ABlinkGeneric::resolve()
{
ABlink * a = ability->clone();
a->target = target;
a->addToGame();
return 1;
}
const char * ABlinkGeneric::getMenuText()
{
return "Blink";
}
ABlinkGeneric * ABlinkGeneric::clone() const
{
ABlinkGeneric * a = NEW ABlinkGeneric(*this);
a->ability = this->ability->clone();
a->oneShot = 1;
a->isClone = 1;
return a;
}
ABlinkGeneric::~ABlinkGeneric()
{
SAFE_DELETE(ability);
}
//Tutorial Messaging
ATutorialMessage * ATutorialMessage::Current = NULL;
ATutorialMessage::ATutorialMessage(MTGCardInstance * source, string message) : MTGAbility(0, source), IconButtonsController(0, 0)
{
mBgTex = NULL;
mElapsed = 0;
mIsImage = false;
for (int i = 0; i < 9; i++)
mBg[i] = NULL;
string gfx = WResourceManager::Instance()->graphicsFile(message);
if (fileExists(gfx.c_str()))
{
mIsImage = true;
mMessage = message;
}
else
{
mMessage = _(message); //translate directly here, remove this and translate at rendering time if it bites us
boost::replace_all(mMessage, "\\n", "\n");
}
if (mIsImage)
{
mX = SCREEN_WIDTH_F / 2;
mY = SCREEN_HEIGHT_F / 2;
}
else
{
mX = 0;
mY = -SCREEN_HEIGHT_F - 0.1f; //Offscreen
}
mDontShow = mUserCloseRequest = alreadyShown();
if(mDontShow)
forceDestroy = 1;
}
string ATutorialMessage::getOptionName()
{
std::stringstream out;
out << "tuto_";
out << hash_djb2(mMessage.c_str());
return out.str();
}
bool ATutorialMessage::alreadyShown()
{
return options[getOptionName()].number ? true : false;
}
bool ATutorialMessage::CheckUserInput(JButton key)
{
if (mUserCloseRequest) return false;
if(key == JGE_BTN_SEC || key == JGE_BTN_OK)
{
ButtonPressed(0, 1);
return true;
}
//Required for Mouse/touch input
IconButtonsController::CheckUserInput(key);
return true; //this ability is modal, so it catches all key events until it gets closed
}
void ATutorialMessage::Update(float dt)
{
if (!Current && !mDontShow)
Current = this;
if (Current != this)
return;
if (mUserCloseRequest && mY < -SCREEN_HEIGHT)
mDontShow = true;
if (mDontShow)
{
Current = NULL;
forceDestroy = 1;
return;
}
mElapsed += dt;
IconButtonsController::Update(dt);
if (mIsImage)
return;
//Below this only affects "text" mode
if (!mUserCloseRequest && mY < 0)
{
mY = -SCREEN_HEIGHT + (SCREEN_HEIGHT * mElapsed / 0.75f); //Todo: more physical drop-in.
if (mY >= 0)
mY = 0;
}
else if (mUserCloseRequest && mY > -SCREEN_HEIGHT)
{
mY = -(SCREEN_HEIGHT * mElapsed / 0.75f);
}
}
void ATutorialMessage::ButtonPressed(int controllerId, int controlId)
{
//TODO : cancel ALL tips/tutorials for JGE_BTN_SEC?
options[getOptionName()].number = 1;
options.save(); //TODO: if we experience I/O slowness in tutorials, move this save at the end of a turn, or at the end of the game.
mElapsed = 0;
mUserCloseRequest = true;
}
void ATutorialMessage::Render()
{
if (mDontShow)
return;
if (mY < -SCREEN_HEIGHT)
return;
if (!mBgTex)
{
if (mIsImage)
{
mBgTex = WResourceManager::Instance()->RetrieveTexture(mMessage, RETRIEVE_LOCK);
if (mBgTex)
{
mBg[0] = NEW JQuad(mBgTex, 0, 0, (float) mBgTex->mWidth, (float) mBgTex->mHeight);
mBg[0]->SetHotSpot(mBg[0]->mWidth / 2, mBg[0]->mHeight / 2);
//Continue Button
JQuadPtr quad = WResourceManager::Instance()->RetrieveQuad("iconspsp.png", 4 * 32, 0, 32, 32, "iconpsp4", RETRIEVE_MANAGE);
quad->SetHotSpot(16, 16);
IconButton * iconButton = NEW IconButton(1, this, quad.get(), 0, mBg[0]->mHeight / 2, 0.7f, Fonts::MAGIC_FONT, _("continue"), 0, 16, true);
Add(iconButton);
}
if (options[Options::SFXVOLUME].number > 0)
{
JSample * sample = WResourceManager::Instance()->RetrieveSample("tutorial.wav");
if (sample)
JSoundSystem::GetInstance()->PlaySample(sample);
}
}
else
{
mBgTex = WResourceManager::Instance()->RetrieveTexture("taskboard.png", RETRIEVE_LOCK);
float unitH = static_cast<float> (mBgTex->mHeight / 4);
float unitW = static_cast<float> (mBgTex->mWidth / 4);
if (unitH == 0 || unitW == 0) return;
if (mBgTex)
{
mBg[0] = NEW JQuad(mBgTex, 0, 0, unitW, unitH);
mBg[1] = NEW JQuad(mBgTex, unitW, 0, unitW * 2, unitH);
mBg[2] = NEW JQuad(mBgTex, unitW * 3, 0, unitW, unitH);
mBg[3] = NEW JQuad(mBgTex, 0, unitH, unitW, unitH * 2);
mBg[4] = NEW JQuad(mBgTex, unitW, unitH, unitW * 2, unitH * 2);
mBg[5] = NEW JQuad(mBgTex, unitW * 3, unitH, unitW, unitH * 2);
mBg[6] = NEW JQuad(mBgTex, 0, unitH * 3, unitW, unitH);
mBg[7] = NEW JQuad(mBgTex, unitW, unitH * 3, unitW * 2, unitH);
mBg[8] = NEW JQuad(mBgTex, unitW * 3, unitH * 3, unitW, unitH);
}
//Continue Button
JQuadPtr quad = WResourceManager::Instance()->RetrieveQuad("iconspsp.png", 4 * 32, 0, 32, 32, "iconpsp4", RETRIEVE_MANAGE);
quad->SetHotSpot(16, 16);
IconButton * iconButton = NEW IconButton(1, this, quad.get(), SCREEN_WIDTH_F / 2, SCREEN_HEIGHT_F - 60, 0.7f, Fonts::MAGIC_FONT, _("continue"), 0, 16, true);
Add(iconButton);
mSH = 64 / unitH;
mSW = 64 / unitW;
if (options[Options::SFXVOLUME].number > 0)
{
JSample * sample = WResourceManager::Instance()->RetrieveSample("chain.wav");
if (sample)
JSoundSystem::GetInstance()->PlaySample(sample);
}
}
}
JRenderer * r = JRenderer::GetInstance();
//Render background board
if (mBgTex)
{
if (mIsImage)
{
int alpha = mUserCloseRequest ? MAX(0, 255 - (int)(mElapsed * 500)) : MIN(255, (int)(mElapsed * 500)) ;
if (mUserCloseRequest && alpha == 0)
mDontShow = true;
r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(alpha / 2,0,0,0));
mBg[0]->SetColor(ARGB(alpha,255,255,255));
r->RenderQuad(mBg[0], SCREEN_WIDTH_F /2 , SCREEN_HEIGHT_F / 2 , 0);
IconButtonsController::SetColor(ARGB(alpha,255,255,255));
}
else
{
//Setup fonts.
WFont * f2 = WResourceManager::Instance()->GetWFont(Fonts::MAGIC_FONT);
f2->SetColor(ARGB(255, 205, 237, 240));
r->FillRect(0, mY, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(128,0,0,0));
r->RenderQuad(mBg[0], 0, mY, 0, mSW, mSH); //TL
r->RenderQuad(mBg[2], SCREEN_WIDTH - 64, mY, 0, mSW, mSH); //TR
r->RenderQuad(mBg[6], 0, mY + SCREEN_HEIGHT - 64, 0, mSW, mSH); //BL
r->RenderQuad(mBg[8], SCREEN_WIDTH - 64, mY + SCREEN_HEIGHT - 64, 0, mSW, mSH); //BR
//Stretch the sides
float stretchV = (144.0f / 128.0f) * mSH;
float stretchH = (176.0f / 128.0f) * mSW;
r->RenderQuad(mBg[3], 0, mY + 64, 0, mSW, stretchV); //L
r->RenderQuad(mBg[5], SCREEN_WIDTH - 64, mY + 64, 0, mSW, stretchV); //R
r->RenderQuad(mBg[1], 64, mY, 0, stretchH, mSH); //T1
r->RenderQuad(mBg[1], 240, mY, 0, stretchH, mSH); //T1
r->RenderQuad(mBg[7], 64, mY + 208, 0, stretchH, mSH); //B1
r->RenderQuad(mBg[7], 240, mY + 208, 0, stretchH, mSH); //B1
r->RenderQuad(mBg[4], 64, mY + 64, 0, stretchH, stretchV); //Center1
r->RenderQuad(mBg[4], 240, mY + 64, 0, stretchH, stretchV); //Center2
}
}
else
{
r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(128,0,0,0));
r->FillRect(10, 10 + mY, SCREEN_WIDTH - 10, SCREEN_HEIGHT - 10, ARGB(128,0,0,0));
}
if (!mBgTex || !mIsImage)
{
float posX = 40, posY = mY + 20;
string title = _("Help");
WFont * f = WResourceManager::Instance()->GetWFont(Fonts::MAGIC_FONT);
WFont * f3 = WResourceManager::Instance()->GetWFont(Fonts::MENU_FONT); //OPTION_FONT
f->SetColor(ARGB(255, 55, 46, 34));
f3->SetColor(ARGB(255, 219, 206, 151));
f3->DrawString(title.c_str(), static_cast<float> ((SCREEN_WIDTH - 20) / 2 - title.length() * 4), posY);
posY += 30;
f->DrawString(_(mMessage).c_str(), posX, posY);
posY += 20;
f->SetScale(1);
}
IconButtonsController::Render();
}
ATutorialMessage * ATutorialMessage::clone() const
{
ATutorialMessage * copy = NEW ATutorialMessage(*this);
copy->mUserCloseRequest = copy->alreadyShown();
return copy;
}
ATutorialMessage::~ATutorialMessage()
{
if (mBgTex)
{
WResourceManager::Instance()->Release(mBgTex);
for (int i = 0; i < 9; i++)
SAFE_DELETE(mBg[i]);
}
}
// utility functions
// Given a delimited string of abilities, add the ones to the list that are "Basic" MTG abilities
void PopulateAbilityIndexVector(list<int>& abilities, const string& abilityStringList, char delimiter)
{
vector<string> abilitiesList = split(abilityStringList, delimiter);
for (vector<string>::iterator iter = abilitiesList.begin(); iter != abilitiesList.end(); ++iter)
{
int abilityIndex = Constants::GetBasicAbilityIndex(*iter);
if (abilityIndex != -1)
abilities.push_back(abilityIndex);
}
}
void PopulateColorIndexVector(list<int>& colors, const string& colorStringList, char delimiter)
{
vector<string> abilitiesList = split(colorStringList, delimiter);
for (vector<string>::iterator iter = abilitiesList.begin(); iter != abilitiesList.end(); ++iter)
{
for (int colorIndex = Constants::MTG_COLOR_ARTIFACT; colorIndex < Constants::MTG_NB_COLORS; ++colorIndex)
{
// if the text is not a basic ability but contains a valid color add it to the color vector
if ((Constants::GetBasicAbilityIndex(*iter) == -1)
&& ((*iter).find(Constants::MTGColorStrings[colorIndex]) != string::npos))
colors.push_back(colorIndex);
}
}
}
void PopulateSubtypesIndexVector(list<int>& types, const string& subTypesStringList, char delimiter)
{
vector<string> subTypesList = split(subTypesStringList, delimiter);
for (vector<string>::iterator it = subTypesList.begin(); it != subTypesList.end(); ++it)
{
string subtype = *it;
size_t id = Subtypes::subtypesList->find(subtype);
if (id != string::npos)
types.push_back(id);
}
}