Files
wagic/projects/mtg/src/MTGCardInstance.cpp
2016-08-14 21:26:33 +08:00

1694 lines
46 KiB
C++

/*---------------------------------------------
Card Instance
Instance of a given MTGCard in the game
Although there is only one MTGCard of each type, there can be as much Instances of it as needed in the game
--------------------------------------------
*/
#include "PrecompiledHeader.h"
#include "MTGCardInstance.h"
#include "CardDescriptor.h"
#include "Counters.h"
#include "Subtypes.h"
#include "AIPlayerBaka.h"
using namespace std;
SUPPORT_OBJECT_ANALYTICS(MTGCardInstance)
MTGCardInstance MTGCardInstance::AnyCard = MTGCardInstance();
MTGCardInstance MTGCardInstance::NoCard = MTGCardInstance();
MTGCardInstance::MTGCardInstance() :
CardPrimitive(), MTGCard(), Damageable(0, 0), view(NULL)
{
initMTGCI();
}
MTGCardInstance::MTGCardInstance(MTGCard * card, MTGPlayerCards * arg_belongs_to) :
CardPrimitive(card->data), MTGCard(card), Damageable(0, card->data->getToughness()), view(NULL)
{
if(arg_belongs_to)
if(arg_belongs_to->owner)
observer = arg_belongs_to->owner->getObserver();
initMTGCI();
model = card;
attacker = 0;
lifeOrig = life;
origpower = power;
basepower = origpower;
pbonus = 0;
origtoughness = toughness;
basetoughness = origtoughness;
tbonus = 0;
belongs_to = arg_belongs_to;
owner = NULL;
if (arg_belongs_to)
owner = arg_belongs_to->library->owner;
lastController = owner;
previousController = owner;
defenser = NULL;
banding = NULL;
life = toughness;
preventable = 0;
thatmuch = 0;
flanked = 0;
castMethod = Constants::NOT_CAST;
isSettingBase = 0;
isCDA = false;
isSwitchedPT = false;
isACopier = false;
bypassTC = false;
discarded = false;
copiedID = getId();
LKIpower = power;
LKItoughness = toughness;
cardistargetted = 0;
cardistargetter = 0;
myconvertedcost = getManaCost()->getConvertedCost();
revealedLast = NULL;
MadnessPlay = false;
}
MTGCardInstance * MTGCardInstance::createSnapShot()
{
//the below section of code was changed without all possible side effects checked
//the reason was becuase while NEW MTGCardInstance(*this); does indeed return an exact copy
//the lower layer cardprimitive data is pointed to from the original source.
//this would cause cards like lotus bloom, which contain a restriction, to already has deleted the restriction
//which belonged to the original card before getting to the safe_delete,
//it was leaving a dangling pointer which leads to
//a total crash on "cleanup()" calls from garbage zone.
//snapshots are created for extra cost, they are used for abilities contained after the cost through storecard variable.
//TODO:fix this correctly. I want this to use an exact copy of the card in its current state for stored.
//making it safe_delete these "copies" leads to the same crash, as they are still pointing to the original data.
MTGCardInstance * snapShot = this;
//below is how we used to handle this.
// MTGCardInstance * snapShot = NEW MTGCardInstance(*this);
//snapShot->previous = NULL;
// snapShot->counters = NEW Counters(snapShot);
//controller()->game->garbage->addCard(snapShot);
return snapShot;
}
void MTGCardInstance::copy(MTGCardInstance * card)
{
MTGCard * source = NULL;
if(card->isToken || card->hasCopiedToken)
{
source = card;
}
else
source = MTGCollection()->getCardById(card->copiedID);
CardPrimitive * data = source->data;
basicAbilities = data->basicAbilities;
types.clear();//reset types.. fix copying man lands... the copier becomes an unanimated land...
for (size_t i = 0; i < data->types.size(); i++)
{
types.push_back(data->types[i]);
}
colors = data->colors;
manaCost.copy(data->getManaCost());
setText(data->text); //The text is retrieved from the data anyways
setName(data->name);
power = data->power;//layer 7a
toughness = data->toughness;//layer 7a
power += pbonus;//layer 7b
toughness += tbonus;//layer 7b
life = toughness;
lifeOrig = life;
magicText = data->magicText;
spellTargetType = data->spellTargetType;
alias = data->alias;
copiedID = card->copiedID;
doubleFaced = data->doubleFaced;
origpower = card->origpower;//for flip
origtoughness = card->origtoughness;//for flip
//Now this is dirty...
int backupid = mtgid;
int castMethodBackUP = this->castMethod;
mtgid = source->getId();
MTGCardInstance * oldStored = this->storedSourceCard;
/*Spell * spell = NEW Spell(observer, this);
observer = card->observer;
AbilityFactory af(observer);
af.addAbilities(observer->mLayers->actionLayer()->getMaxId(), spell);
delete spell;*/
if(observer->players[1]->playMode == Player::MODE_TEST_SUITE)
mtgid = backupid; // there must be a way to get the token id...
else
{
mtgid = card->getMTGId(); ///////////////////////////////////////////////////
setId = card->setId; // Copier/Cloner cards produces the same token...//
rarity = card->getRarity(); ///////////////////////////////////////////////////
setMTGId(card->copiedID); //**************sets copier image****************//
}
castMethod = castMethodBackUP;
backupTargets = this->backupTargets;
storedCard = oldStored;
miracle = false;
mPropertiesChangedSinceLastUpdate = true;
}
MTGCardInstance::~MTGCardInstance()
{
SAFE_DELETE(counters);
if (previous != NULL)
{
//DebugTrace("MTGCardInstance::~MTGCardInstance(): deleting " << ToHex(previous));
SAFE_DELETE(previous);
}
}
int MTGCardInstance::init()
{
MTGCard::init();
CardPrimitive::init();
data = this;
X = 0;
castX = 0;
setX = -1;
return 1;
}
void MTGCardInstance::initMTGCI()
{
X = 0;
setX = -1;
sample = "";
model = NULL;
isToken = false;
lifeOrig = 0;
doDamageTest = 1;
skipDamageTestOnce = false;
belongs_to = NULL;
tapped = 0;
untapping = 0;
frozen = 0;
fresh = 0;
isMultiColored = 0;
isLeveler = 0;
enchanted = false;
CDenchanted = 0;
CDdamaged = 0;
blinked = false;
isExtraCostTarget = false;
morphed = false;
turningOver = false;
isMorphed = false;
MeldedFrom = "";
isFlipped = false;
isPhased = false;
isCascaded = false;
phasedTurn = -1;
didattacked = 0;
didblocked = 0;
notblocked = 0;
sunburst = 0;
equipment = 0;
auras = 0;
damageToOpponent = false;
damageToController = false;
damageToCreature = false;
wasDealtDamage = false;
isDualWielding = false;
suspended = false;
isBestowed = false;
castMethod = Constants::NOT_CAST;
mPropertiesChangedSinceLastUpdate = false;
stillNeeded = true;
kicked = 0;
dredge = 0;
chooseacolor = -1;
chooseasubtype = "";
coinSide = -1;
isAttacking = NULL;
storedCard = NULL;
storedSourceCard = NULL;
myPair = NULL;
shackled = NULL;
miracle = false;
hasCopiedToken = false;
countTrini = 0;
imprintedCards.clear();
attackCost = 0;
attackCostBackup = 0;
attackPlaneswalkerCost = 0;
attackPlaneswalkerCostBackup = 0;
blockCost = 0;
blockCostBackup = 0;
imprintG = 0;
imprintU = 0;
imprintR = 0;
imprintB = 0;
imprintW = 0;
currentimprintName = "";
imprintedNames.clear();
CountedObjects = 0;
for (int i = 0; i < ManaCost::MANA_PAID_WITH_SUSPEND +1; i++)
alternateCostPaid[i] = 0;
paymenttype = MTGAbility::PUT_INTO_PLAY;
reduxamount = 0;
summoningSickness = 1;
preventable = 0;
thatmuch = 0;
flanked = 0;
target = NULL;
playerTarget = NULL;
type_as_damageable = DAMAGEABLE_MTGCARDINSTANCE;
banding = NULL;
owner = NULL;
counters = NEW Counters(this);
previousZone = NULL;
previous = NULL;
next = NULL;
TokenAndAbility = NULL;
lastController = NULL;
regenerateTokens = 0;
blocked = false;
graveEffects = false;
exileEffects = false;
currentZone = NULL;
cardsAbilities = vector<MTGAbility *>();
data = this; //an MTGCardInstance point to itself for data, allows to update it without killing the underlying database item
if (observer && basicAbilities[(int)Constants::CHANGELING])
{//if the card is a changeling, it gains all creature subtypes
vector<string> values = MTGAllCards::getCreatureValuesById();
for (size_t i = 0; i < values.size(); ++i)
{
//Don' want to send any event to the gameObserver inside of initMCGI, so calling the parent setSubtype method instead of mine
CardPrimitive::setSubtype(values[i].c_str());
}
}
int colored = 0;
for (int i = Constants::MTG_COLOR_GREEN; i <= Constants::MTG_COLOR_WHITE; ++i)
{
if (this->hasColor(i))
++colored;
}
if (colored > 1)
{
isMultiColored = 1;
}
if(previous && previous->morphed && !turningOver)
{
morphed = true;
isMorphed = true;
}
}
const string MTGCardInstance::getDisplayName() const
{
return getName();
}
void MTGCardInstance::addType(int type)
{
bool before = hasType(type);
CardPrimitive::addType(type);
if (!before)
mPropertiesChangedSinceLastUpdate = true;
// If the card name is not set, set it to the type.
//This is a hack for "transform", used in combination with "losesubtypes" on Basic Lands
//See removeType below
if (!name.length())
setName(MTGAllCards::findType(type));
WEvent * e = NEW WEventCardChangeType(this, type, before, true);
if (observer)
observer->receiveEvent(e);
else
SAFE_DELETE(e);
}
void MTGCardInstance::addType(const string& type_text)
{
setSubtype(type_text);
}
void MTGCardInstance::setType(const string& type_text)
{
setSubtype(type_text);
}
void MTGCardInstance::setSubtype(const string& value)
{
int id = MTGAllCards::findType(value);
addType(id);
}
int MTGCardInstance::removeType(const string& value, int removeAll)
{
int id = MTGAllCards::findType(value);
return removeType(id, removeAll);
}
int MTGCardInstance::removeType(int id, int removeAll)
{
bool before = hasType(id);
int result = CardPrimitive::removeType(id, removeAll);
bool after = hasType(id);
if (before != after)
{
mPropertiesChangedSinceLastUpdate = true;
// Basic lands have the same name as their subtypes, and TargetChoosers don't make a distinction between name and type,
// so if we remove a subtype "Forest", we also need to remove its name.
//This means the card might lose its name, but usually when we force remove a type, we add another one just after that.
//see "AddType" above which force sets a name if necessary
if (name.compare(MTGAllCards::findType(id)) == 0)
setName("");
}
WEvent * e = NEW WEventCardChangeType(this, id, before, after);
if (observer)
observer->receiveEvent(e);
else
SAFE_DELETE(e);
return result;
}
int MTGCardInstance::isInPlay(GameObserver* game)
{
for (int i = 0; i < 2; i++)
{
MTGGameZone * zone = game->players[i]->game->inPlay;
if (zone->hasCard(this) && !isPhased)
return 1;
}
return 0;
}
int MTGCardInstance::afterDamage()
{
if(skipDamageTestOnce)
{
skipDamageTestOnce = false;
return 0;
}
if (!doDamageTest)
return 0;
doDamageTest = 0;
if (!isCreature())
return 0;
if (life <= 0 && isInPlay(observer))
{
return destroy();
}
return 0;
}
int MTGCardInstance::bury()
{
Player * p = controller();
if (basicAbilities[(int)Constants::EXILEDEATH])
{
p->game->putInZone(this, p->game->inPlay, owner->game->exile);
return 1;
}
if (!basicAbilities[(int)Constants::INDESTRUCTIBLE])
{
p->game->putInZone(this, p->game->inPlay, owner->game->graveyard);
return 1;
}
return 0;
}
int MTGCardInstance::destroy()
{
if (!triggerRegenerate())
return bury();
return 0;
}
MTGGameZone * MTGCardInstance::getCurrentZone()
{
return currentZone;
}
int MTGCardInstance::has(int basicAbility)
{
return basicAbilities[basicAbility];
}
ManaCost* MTGCardInstance::getReducedManaCost()
{
return &reducedCost;
}
ManaCost* MTGCardInstance::getIncreasedManaCost()
{
return &increasedCost;
}
//sets card as attacked and sends events
void MTGCardInstance::eventattacked()
{
didattacked = 1;
WEvent * e = NEW WEventCardAttacked(this);
observer->receiveEvent(e);
}
//sets card as attacked alone and sends events
void MTGCardInstance::eventattackedAlone()
{
WEvent * e = NEW WEventCardAttackedAlone(this);
observer->receiveEvent(e);
}
//sets card as attacked and sends events
void MTGCardInstance::eventattackednotblocked()
{
didattacked = 1;
WEvent * e = NEW WEventCardAttackedNotBlocked(this);
observer->receiveEvent(e);
}
//sets card as attacked and sends events
void MTGCardInstance::eventattackedblocked(MTGCardInstance * opponent)
{
didattacked = 1;
WEvent * e = NEW WEventCardAttackedBlocked(this,opponent);
observer->receiveEvent(e);
}
//sets card as blocking and sends events
void MTGCardInstance::eventblocked(MTGCardInstance * opponent)
{
didblocked = 1;
WEvent * e = NEW WEventCardBlocked(this,opponent);
observer->receiveEvent(e);
}
//Taps the card
void MTGCardInstance::tap(bool sendNoEvent)
{
if (tapped)
return;
tapped = 1;
WEvent * e = NEW WEventCardTap(this, 0, 1);
if (sendNoEvent)
dynamic_cast<WEventCardTap*>(e)->noTrigger = true;
observer->receiveEvent(e);
}
void MTGCardInstance::untap()
{
if (!tapped)
return;
tapped = 0;
WEvent * e = NEW WEventCardTap(this, 1, 0);
observer->receiveEvent(e);
}
void MTGCardInstance::setUntapping()
{
untapping = 1;
}
int MTGCardInstance::isUntapping()
{
return untapping;
}
//Tries to Untap the card
void MTGCardInstance::attemptUntap()
{
if (untapping)
{
untap();
untapping = 0;
}
}
//Tells if the card is tapped or not
int MTGCardInstance::isTapped()
{
return tapped;
}
int MTGCardInstance::regenerate()
{
if (has(Constants::CANTREGEN))
return 0;
return ++regenerateTokens;
}
int MTGCardInstance::triggerRegenerate()
{
if (!regenerateTokens)
return 0;
if (has(Constants::CANTREGEN))
return 0;
regenerateTokens--;
tap();
if(isCreature())
{
life = toughness;
initAttackersDefensers();
if (life < 1)
return 0; //regeneration didn't work (wither ?)
}
return 1;
}
int MTGCardInstance::initAttackersDefensers()
{
setAttacker(0);
setDefenser(NULL);
banding = NULL;
blockers.clear();
blocked = 0;
didattacked = 0;
didblocked = 0;
return 1;
}
//Function to cleanup flags on a card (generally at the end of the turn)
int MTGCardInstance::cleanup()
{
initAttackersDefensers();
if (!observer || observer->currentPlayer == controller())
{
summoningSickness = 0;
}
if (previous && !previous->stillInUse())
{
//DebugTrace("MTGCardInstance::cleanup(): deleting " << ToHex(previous));
SAFE_DELETE(previous);
}
regenerateTokens = 0;
preventable = 0;
thatmuch = 0;
return 1;
}
int MTGCardInstance::stillInUse()
{
if (observer->mLayers->actionLayer()->stillInUse(this))
return 1;
if (!previous)
return 0;
if (previous->stillNeeded)
{
previous->stillNeeded = false;
return 1;
}
return previous->stillInUse();
}
/* Summoning Sickness
* 212.3f A creature's activated ability with the tap symbol or the untap symbol in its activation cost
* can't be played unless the creature has been under its controller's control since the start of his or
* her most recent turn. A creature can't attack unless it has been under its controller's control
* since the start of his or her most recent turn. This rule is informally called the "summoning
* sickness" rule. Ignore this rule for creatures with haste (see rule 502.5).
*/
int MTGCardInstance::hasSummoningSickness()
{
if (!summoningSickness)
return 0;
if (has(Constants::HASTE))
return 0;
if (!isCreature())
return 0;
return 1;
}
MTGCardInstance * MTGCardInstance::changeController(Player * newController,bool notZone)
{
if(notZone)
{
lastController = newController;
return this;
}
Player * originalOwner = controller();
MTGCardInstance * copy = originalOwner->game->putInZone(this, this->currentZone, newController->game->inPlay);
//copy->summoningSickness = 1;
return copy;
}
Player * MTGCardInstance::controller()
{
return lastController;
}
int MTGCardInstance::canAttack()
{
if (has(Constants::CANTATTACK))
return 0;
if (has(Constants::FLYERSONLY) && !has(Constants::FLYING))
return 0;
if (tapped)
return 0;
if (hasSummoningSickness())
return 0;
if (has(Constants::DEFENSER) && !has(Constants::CANATTACK))
return 0;
if (!isCreature())
return 0;
if (!isInPlay(observer))
return 0;
return 1;
}
int MTGCardInstance::addToToughness(int value)
{
toughness += value;
life += value;
doDamageTest = 1;
return 1;
}
int MTGCardInstance::setToughness(int value)
{
toughness = value;
life = value;
doDamageTest = 1;
return 1;
}
void MTGCardInstance::stripPTbonus()
{
power -= pbonus;
addToToughness(-tbonus);
}
void MTGCardInstance::plusPTbonus(int p, int t)
{
pbonus += p;
tbonus += t;
}
void MTGCardInstance::minusPTbonus(int p, int t)
{
pbonus -= p;
tbonus -= t;
}
void MTGCardInstance::applyPTbonus()
{
power += pbonus;
addToToughness(tbonus);
}
void MTGCardInstance::addcounter(int p, int t)
{
stripPTbonus();
plusPTbonus(p,t);
applyPTbonus();
}
void MTGCardInstance::addptbonus(int p, int t)
{
stripPTbonus();
plusPTbonus(p,t);
applyPTbonus();
}
void MTGCardInstance::removecounter(int p, int t)
{
stripPTbonus();
minusPTbonus(p,t);
applyPTbonus();
}
void MTGCardInstance::removeptbonus(int p, int t)
{
stripPTbonus();
minusPTbonus(p,t);
applyPTbonus();
}
void MTGCardInstance::addbaseP(int p)
{
basepower = p;
power -= pbonus;
power = p;
power += pbonus;
}
void MTGCardInstance::addbaseT(int t)
{
basetoughness = t;
addToToughness(-tbonus);
addToToughness(t - toughness);
addToToughness(tbonus);
}
void MTGCardInstance::revertbaseP()
{
power -= pbonus;
power += origpower;
power -= basepower;
power += pbonus;
basepower = origpower;
}
void MTGCardInstance::revertbaseT()
{
addToToughness(-tbonus);
addToToughness(origtoughness);
addToToughness(-basetoughness);
addToToughness(tbonus);
basetoughness = origtoughness;
}
void MTGCardInstance::cdaPT(int p, int t)
{
origpower = p;
origtoughness = t;
setPower(p);
setToughness(t);
applyPTbonus();
}
void MTGCardInstance::switchPT(bool apply)
{
stripPTbonus();
swapP = power;
swapT = toughness;
power += origpower;
power -= swapP;
addToToughness(origtoughness);
addToToughness(-swapT);
applyPTbonus();
if(apply)
{
swapP = toughness;
swapT = power;
addToToughness(swapT);
addToToughness(-swapP);
setPower(swapP);
}
}
int MTGCardInstance::getCurrentPower()
{
if(!isInPlay(observer))
return LKIpower;
return power;
}
int MTGCardInstance::getCurrentToughness()
{
if(!isInPlay(observer))
return LKItoughness;
return toughness;
}
//check stack
bool MTGCardInstance::StackIsEmptyandSorcerySpeed()
{
Player * whoInterupts = getObserver()->isInterrupting;//leave this so we can actually debug who is interupting/current.
Player * whoCurrent = getObserver()->currentPlayer;
if((getObserver()->mLayers->stackLayer()->count(0, NOT_RESOLVED) == 0) &&
(getObserver()->getCurrentGamePhase() == MTG_PHASE_FIRSTMAIN ||
getObserver()->getCurrentGamePhase() == MTG_PHASE_SECONDMAIN) &&
controller() == whoCurrent &&
(!whoInterupts || whoInterupts == whoCurrent))
{
return true;
}
return false;
}
//check targetted?
bool MTGCardInstance::isTargetted()
{
if(controller()->game->reveal->cards.size() || controller()->opponent()->game->reveal->cards.size())
return false;
if(getObserver()->mLayers->stackLayer()->count(0, NOT_RESOLVED) != 0)
{
ActionStack * stack = observer->mLayers->stackLayer();
for (int i = stack->mObjects.size() - 1; i >= 0; i--)
{
Interruptible * current = ((Interruptible *) stack->mObjects[i]);
if ((current->type == ACTION_SPELL || current->type == ACTION_ABILITY) && current->state == NOT_RESOLVED)
{
if(current->type == ACTION_SPELL)
{
Spell * spell = (Spell *) current;
if(spell->getNextTarget() && spell->getNextTarget() == (Targetable*)this)
return true;
}
}
}
}
if(cardistargetted)
return true;
return false;
}
//check targetter?
bool MTGCardInstance::isTargetter()
{
if(controller()->game->reveal->cards.size() || controller()->opponent()->game->reveal->cards.size())
return false;
if(getObserver()->mLayers->stackLayer()->count(0, NOT_RESOLVED) != 0)
{
ActionStack * stack = observer->mLayers->stackLayer();
for (int i = stack->mObjects.size() - 1; i >= 0; i--)
{
Interruptible * current = ((Interruptible *) stack->mObjects[i]);
if ((current->type == ACTION_SPELL || current->type == ACTION_ABILITY) && current->state == NOT_RESOLVED)
{
if(current->type == ACTION_SPELL)
{
Spell * spell = (Spell *) current;
if(spell && spell->source == this)
return true;
}
}
}
}
if(cardistargetter)
return true;
return false;
}
int MTGCardInstance::canBlock()
{
if (tapped)
return 0;
if (basicAbilities[(int)Constants::CANTBLOCK])
return 0;
if (!isCreature())
return 0;
if (!isInPlay(observer))
return 0;
return 1;
}
int MTGCardInstance::canBlock(MTGCardInstance * opponent)
{
if (!canBlock())
return 0;
if (!opponent)
return 1;
if (!opponent->isAttacker())
return 0;
// Comprehensive rule 502.7f : If a creature with protection attacks, it can't be blocked by creatures that have the stated quality.
if (opponent->protectedAgainst(this))
return 0;
if (opponent->cantBeBlockedBy(this))
return 0;
if (this->cantBeBlockerOf(opponent))
return 0;
if (this->cantBeBlockerOfCard(opponent))
return 0;
if (opponent->basicAbilities[(int)Constants::UNBLOCKABLE])
return 0;
if (opponent->basicAbilities[(int)Constants::ONEBLOCKER] && opponent->blocked)
return 0;
if((opponent->basicAbilities[(int)Constants::EVADEBIGGER]|| opponent->basicAbilities[(int)Constants::SKULK]) && power > opponent->power)
return 0;
if(opponent->basicAbilities[(int)Constants::STRONG] && power < opponent->power)
return 0;
if(this->basicAbilities[(int)Constants::WEAK] && power < opponent->power)
return 0;
if (opponent->basicAbilities[(int)Constants::FEAR] && !(this->hasType(Subtypes::TYPE_ARTIFACT) || this->hasColor(Constants::MTG_COLOR_BLACK)))
return 0;
if (opponent->controller()->game->battlefield->hasAbility(Constants::LURE) && !opponent->has(Constants::LURE))
return 0;
//intimidate
if (opponent->basicAbilities[(int)Constants::INTIMIDATE] && !(this->hasType(Subtypes::TYPE_ARTIFACT)))
{
int canblock = 0;
for (int i = Constants::MTG_COLOR_GREEN; i <= Constants::MTG_COLOR_WHITE; ++i)
{
if (this->hasColor(i) && opponent->hasColor(i))
{
canblock = 1;
break;
}
}
if (!canblock)
return 0;
}
if (opponent->basicAbilities[(int)Constants::FLYING] && !(basicAbilities[(int)Constants::FLYING] || basicAbilities[(int)Constants::REACH]))
return 0;
//Can block only creatures with flying if has cloud
if (basicAbilities[(int)Constants::CLOUD] && !(opponent->basicAbilities[(int)Constants::FLYING]))
return 0;
// If opponent has shadow and a creature does not have either shadow or reachshadow it cannot be blocked
if (opponent->basicAbilities[(int)Constants::SHADOW] && !(basicAbilities[(int)Constants::SHADOW]
|| basicAbilities[(int)Constants::REACHSHADOW]))
return 0;
// If opponent does not have shadow and a creature has shadow it cannot be blocked
if (!opponent->basicAbilities[(int)Constants::SHADOW] && basicAbilities[(int)Constants::SHADOW])
return 0;
if (opponent->basicAbilities[(int)Constants::HORSEMANSHIP] && !basicAbilities[(int)Constants::HORSEMANSHIP])
return 0;
if (opponent->basicAbilities[(int)Constants::SWAMPWALK] && controller()->game->inPlay->hasType("swamp"))
return 0;
if (opponent->basicAbilities[(int)Constants::FORESTWALK] && controller()->game->inPlay->hasType("forest"))
return 0;
if (opponent->basicAbilities[(int)Constants::ISLANDWALK] && controller()->game->inPlay->hasType("island"))
return 0;
if (opponent->basicAbilities[(int)Constants::MOUNTAINWALK] && controller()->game->inPlay->hasType("mountain"))
return 0;
if (opponent->basicAbilities[(int)Constants::PLAINSWALK] && controller()->game->inPlay->hasType("plains"))
return 0;
if (opponent->basicAbilities[(int)Constants::LEGENDARYWALK] && controller()->game->inPlay->hasPrimaryType("legendary","land"))
return 0;
if (opponent->basicAbilities[(int)Constants::DESERTWALK] && controller()->game->inPlay->hasSpecificType("land","desert"))
return 0;
if (opponent->basicAbilities[(int)Constants::SNOWSWAMPWALK] && controller()->game->inPlay->hasSpecificType("snow","swamp"))
return 0;
if (opponent->basicAbilities[(int)Constants::SNOWFORESTWALK] && controller()->game->inPlay->hasSpecificType("snow","forest"))
return 0;
if (opponent->basicAbilities[(int)Constants::SNOWISLANDWALK] && controller()->game->inPlay->hasSpecificType("snow","island"))
return 0;
if (opponent->basicAbilities[(int)Constants::SNOWMOUNTAINWALK] && controller()->game->inPlay->hasSpecificType("snow","mountain"))
return 0;
if (opponent->basicAbilities[(int)Constants::SNOWPLAINSWALK] && controller()->game->inPlay->hasSpecificType("snow","plains"))
return 0;
if (opponent->basicAbilities[(int)Constants::SNOWWALK] && controller()->game->inPlay->hasPrimaryType("snow","land"))
return 0;
if (opponent->basicAbilities[(int)Constants::NONBASICWALK] && controller()->game->inPlay->hasTypeButNotType("land","basic"))
return 0;
return 1;
}
JQuadPtr MTGCardInstance::getIcon()
{
return WResourceManager::Instance()->RetrieveCard(this, CACHE_THUMB);
}
ManaCost * MTGCardInstance::computeNewCost(MTGCardInstance * card,ManaCost * Cost, ManaCost * Data, bool noTrinisphere)
{
int color = 0;
string type = "";
ManaCost * original = NEW ManaCost();
ManaCost * excess = NEW ManaCost();
original->copy(Data);
Cost->copy(original);
if (Cost->extraCosts)
{
for (unsigned int i = 0; i < Cost->extraCosts->costs.size(); i++)
{
Cost->extraCosts->costs[i]->setSource(card);
}
}
if (card->getIncreasedManaCost()->getConvertedCost() || card->getReducedManaCost()->getConvertedCost())
{//start1
if (card->getIncreasedManaCost()->getConvertedCost())
original->add(card->getIncreasedManaCost());
//before removing get the diff for excess
if(card->getReducedManaCost()->getConvertedCost())
{
for(int xc = 0; xc < 7;xc++)
{//if the diff is more than 0
if(card->getReducedManaCost()->getCost(xc) > original->getCost(xc))
{
int count = card->getReducedManaCost()->getCost(xc) - original->getCost(xc);
excess->add(xc,count);
}
}
}
//apply reduced
if (card->getReducedManaCost()->getConvertedCost())
original->remove(card->getReducedManaCost());
//try to reduce hybrid
if (excess->getConvertedCost())
{
original->removeHybrid(excess);
}
Cost->copy(original);
if (Cost->extraCosts)
{
for (unsigned int i = 0; i < Cost->extraCosts->costs.size(); i++)
{
Cost->extraCosts->costs[i]->setSource(card);
}
}
}//end1
int reducem = 0;
bool resetCost = false;
for (unsigned int na = 0; na < card->cardsAbilities.size(); na++)
{//start2
if (!card->cardsAbilities[na])
break;
ANewAffinity * newAff = dynamic_cast<ANewAffinity*>(card->cardsAbilities[na]);
if (newAff)
{
if (!resetCost)
{
resetCost = true;
Cost->copy(original);
if (Cost->extraCosts)
{
for (unsigned int i = 0; i < Cost->extraCosts->costs.size(); i++)
{
Cost->extraCosts->costs[i]->setSource(card);
}
}
}
TargetChooserFactory tf(getObserver());
TargetChooser * tcn = tf.createTargetChooser(newAff->tcString, card, NULL);
for (int w = 0; w < 2; ++w)
{
Player *p = getObserver()->players[w];
MTGGameZone * zones[] = { p->game->inPlay, p->game->graveyard, p->game->hand, p->game->library, p->game->stack, p->game->exile };
for (int k = 0; k < 6; k++)
{
MTGGameZone * z = zones[k];
if (tcn->targetsZone(z))
{
reducem += z->countByCanTarget(tcn);
}
}
}
SAFE_DELETE(tcn);
ManaCost * removingCost = ManaCost::parseManaCost(newAff->manaString);
for (int j = 0; j < reducem; j++)
original->remove(removingCost);
SAFE_DELETE(removingCost);
}
}//end2
if (card->has(Constants::AFFINITYARTIFACTS) ||
card->has(Constants::AFFINITYFOREST) ||
card->has(Constants::AFFINITYGREENCREATURES) ||
card->has(Constants::AFFINITYISLAND) ||
card->has(Constants::AFFINITYMOUNTAIN) ||
card->has(Constants::AFFINITYPLAINS) ||
card->has(Constants::AFFINITYSWAMP))
{//start3
if (card->has(Constants::AFFINITYARTIFACTS))
{
type = "artifact";
}
else if (card->has(Constants::AFFINITYSWAMP))
{
type = "swamp";
}
else if (card->has(Constants::AFFINITYMOUNTAIN))
{
type = "mountain";
}
else if (card->has(Constants::AFFINITYPLAINS))
{
type = "plains";
}
else if (card->has(Constants::AFFINITYISLAND))
{
type = "island";
}
else if (card->has(Constants::AFFINITYFOREST))
{
type = "forest";
}
else if (card->has(Constants::AFFINITYGREENCREATURES))
{
color = 1;
type = "creature";
}
Cost->copy(original);
if (Cost->extraCosts)
{
for (unsigned int i = 0; i < Cost->extraCosts->costs.size(); i++)
{
Cost->extraCosts->costs[i]->setSource(card);
}
}
if (Cost->extraCosts)
{
for (unsigned int i = 0; i < Cost->extraCosts->costs.size(); i++)
{
Cost->extraCosts->costs[i]->setSource(card);
}
}
int reduce = 0;
if (card->has(Constants::AFFINITYGREENCREATURES))
{
TargetChooserFactory tf(getObserver());
TargetChooser * tc = tf.createTargetChooser("creature[green]", NULL);
reduce = card->controller()->game->battlefield->countByCanTarget(tc);
SAFE_DELETE(tc);
}
else
{
reduce = card->controller()->game->battlefield->countByType(type);
}
for (int i = 0; i < reduce; i++)
{
if (Cost->getCost(color) > 0)
Cost->remove(color, 1);
}
}//end3
//trinisphere... now how to implement kicker recomputation
if (card->has(Constants::TRINISPHERE))
{
for (int jj = Cost->getConvertedCost(); jj < 3; jj++)
{
Cost->add(Constants::MTG_COLOR_ARTIFACT, 1);
card->countTrini++;
}
}
else
{
if (card->countTrini)
{
Cost->remove(Constants::MTG_COLOR_ARTIFACT, card->countTrini);
card->countTrini = 0;
}
}
SAFE_DELETE(original);
SAFE_DELETE(excess);
return Cost;
}
MTGCardInstance * MTGCardInstance::getNextPartner()
{
MTGInPlay * inplay = controller()->game->inPlay;
MTGCardInstance * bandingPartner = inplay->getNextAttacker(banding);
while (bandingPartner)
{
if (basicAbilities[(int)Constants::BANDING] || bandingPartner->basicAbilities[(int)Constants::BANDING])
return bandingPartner;
bandingPartner = inplay->getNextAttacker(bandingPartner);
}
return NULL;
}
int MTGCardInstance::DangerRanking()
{
int danger;
int result;
danger = 0;
result = 0;
result += power;
result += toughness;
result += getManaCost()->getConvertedCost();
for (int j = 0; j < Constants::NB_BASIC_ABILITIES; j++)
{
if (basicAbilities[j])
{
result += 1;
}
}
if (result > 1)
danger += 1;
if (result > 2)
danger += 1;
if (result > 4)
danger += 1;
if (result > 6)
danger += 1;
if (result > 10)
danger += 1;
return danger;
}
int MTGCardInstance::setAttacker(int value)
{
Targetable * previousTarget = NULL;
Targetable * target = NULL;
Player * p = controller()->opponent();
if (value)
target = p;
if (attacker)
previousTarget = p;
attacker = value;
WEvent * e = NEW WEventCreatureAttacker(this, previousTarget, target);
if (observer)
observer->receiveEvent(e);
else
SAFE_DELETE(e);
return 1;
}
int MTGCardInstance::toggleAttacker()
{
if (!attacker)
{
//if (!basicAbilities[Constants::VIGILANCE]) tap();
setAttacker(1);
return 1;
}
else
{
//untap();
setAttacker(0);
isAttacking = NULL;
return 1;
}
return 0;
}
int MTGCardInstance::isAttacker()
{
return attacker;
}
MTGCardInstance * MTGCardInstance::isDefenser()
{
return defenser;
}
int MTGCardInstance::nbOpponents()
{
int result = 0;
MTGCardInstance* opponent = getNextOpponent();
while (opponent)
{
result++;
opponent = getNextOpponent(opponent);
}
return result;
}
int MTGCardInstance::raiseBlockerRankOrder(MTGCardInstance * blocker)
{
list<MTGCardInstance *>::iterator it1 = find(blockers.begin(), blockers.end(), blocker);
list<MTGCardInstance *>::iterator it2 = it1;
if (blockers.begin() == it2)
++it2;
else
--it2;
std::iter_swap(it1, it2);
WEvent* e = NEW WEventCreatureBlockerRank(*it1, *it2, this);
if (observer)
observer->receiveEvent(e);
else
SAFE_DELETE(e);
//delete(e);
return 1;
}
int MTGCardInstance::getDefenserRank(MTGCardInstance * blocker)
{
int result = 0;
for (list<MTGCardInstance *>::iterator it1 = blockers.begin(); it1 != blockers.end(); ++it1)
{
result++;
if ((*it1) == blocker)
return result;
}
return 0;
}
;
int MTGCardInstance::removeBlocker(MTGCardInstance * blocker)
{
blockers.remove(blocker);
// Blockers can be removed "manually" (by the blocking player) at the Blockers step,
// Or "automatically" in the damage phase, when they die and regenerate (see http://code.google.com/p/wagic/issues/detail?id=563 )
// In the second case, we still want the card to be marked as "blocked" this turn
if (!blockers.size() && observer->getCurrentGamePhase() == MTG_PHASE_COMBATBLOCKERS)
{
blocked = false;
}
return 1;
}
int MTGCardInstance::addBlocker(MTGCardInstance * blocker)
{
blockers.push_back(blocker);
blocked = true;
return 1;
}
//Returns opponents to this card for this turn. This * should * take into account banding
MTGCardInstance * MTGCardInstance::getNextOpponent(MTGCardInstance * previous)
{
int foundprevious = 0;
if (!previous)
foundprevious = 1;
if (attacker)
{
MTGInPlay * inPlay = observer->opponent()->game->inPlay;
for (int i = 0; i < inPlay->nb_cards; i++)
{
MTGCardInstance * current = inPlay->cards[i];
if (current == previous)
{
foundprevious = 1;
}
else if (foundprevious)
{
MTGCardInstance * defensersOpponent = current->isDefenser();
if (defensersOpponent && (defensersOpponent == this || (banding && defensersOpponent->banding == banding)))
{
return current;
}
}
}
}
else if (defenser)
{
MTGInPlay * inPlay = observer->currentPlayer->game->inPlay;
for (int i = 0; i < inPlay->nb_cards; i++)
{
MTGCardInstance * current = inPlay->cards[i];
if (current == previous)
{
foundprevious = 1;
}
else if (foundprevious)
{
if (defenser == current || (current->banding && defenser->banding == current->banding))
{
return current;
}
}
}
}
return NULL;
}
int MTGCardInstance::setDefenser(MTGCardInstance * opponent)
{
if (defenser)
{
if (observer->players[0]->game->battlefield->hasCard(defenser) || observer->players[1]->game->battlefield->hasCard(defenser))
{//remove blocker "this" from the attackers list of blockers.
defenser->removeBlocker(this);
}
}
WEvent * e = NULL;
if (defenser != opponent)
e = NEW WEventCreatureBlocker(this, defenser, opponent);
defenser = opponent;
if (defenser)
defenser->addBlocker(this);
if (e)
observer->receiveEvent(e);
return 1;
}
int MTGCardInstance::toggleDefenser(MTGCardInstance * opponent)
{
if (canBlock())
{
if (canBlock(opponent))
{
setDefenser(opponent);
didblocked = 1;
if (opponent && opponent->controller()->isAI() && opponent->controller()->playMode != Player::MODE_TEST_SUITE)
{
if(opponent->view != NULL)
{
//todo: quote wololo "change this into a cool blinking effects when opposing creature has cursor focus."
opponent->view->actZ += .8f;
opponent->view->actT -= .2f;
}
}
if (!opponent)
didblocked = 0;
return 1;
}
}
return 0;
}
bool MTGCardInstance::matchesCastFilter(int castFilter) {
if(castFilter == Constants::CAST_DONT_CARE)
return true; //everything
if(castFilter == Constants::CAST_ALL)
return (castMethod != Constants::NOT_CAST); //everything except "not cast"
if (castFilter == Constants::CAST_ALTERNATE && castMethod > Constants::CAST_NORMALLY)
return true; //all alternate casts
return (castFilter == castMethod);
};
int MTGCardInstance::addProtection(TargetChooser * tc)
{
tc->targetter = NULL;
protections.push_back(tc);
return protections.size();
}
int MTGCardInstance::removeProtection(TargetChooser * tc, int erase)
{
for (size_t i = 0; i < protections.size(); i++)
{
if (protections[i] == tc)
{
if (erase)
delete (protections[i]);
protections.erase(protections.begin() + i);
return 1;
}
}
return 0;
}
int MTGCardInstance::protectedAgainst(MTGCardInstance * card)
{
//Basic protections
for (int i = Constants::PROTECTIONGREEN; i <= Constants::PROTECTIONWHITE; i++)
{
if (basicAbilities[i] && card->hasColor(i - Constants::PROTECTIONGREEN + Constants::MTG_COLOR_GREEN))
return 1;
}
//General protections
for (size_t i = 0; i < protections.size(); i++)
{
if (protections[i]->canTarget(card))
return 1;
}
return 0;
}
int MTGCardInstance::addCantBeTarget(TargetChooser * tc)
{
tc->targetter = NULL;
canttarget.push_back(tc);
return canttarget.size();
}
int MTGCardInstance::removeCantBeTarget(TargetChooser * tc, int erase)
{
for (size_t i = 0; i < canttarget.size(); i++)
{
if (canttarget[i] == tc)
{
if (erase)
delete (canttarget[i]);
canttarget.erase(canttarget.begin() + i);
return 1;
}
}
return 0;
}
int MTGCardInstance::CantBeTargetby(MTGCardInstance * card)
{
for (size_t i = 0; i < canttarget.size(); i++)
{
if (canttarget[i]->canTarget(card))
return 1;
}
return 0;
}
int MTGCardInstance::addCantBeBlockedBy(TargetChooser * tc)
{
cantBeBlockedBys.push_back(tc);
return cantBeBlockedBys.size();
}
int MTGCardInstance::removeCantBeBlockedBy(TargetChooser * tc, int erase)
{
for (size_t i = 0; i < cantBeBlockedBys.size(); i++)
{
if (cantBeBlockedBys[i] == tc)
{
if (erase)
delete (cantBeBlockedBys[i]);
cantBeBlockedBys.erase(cantBeBlockedBys.begin() + i);
return 1;
}
}
return 0;
}
int MTGCardInstance::cantBeBlockedBy(MTGCardInstance * card)
{
for (size_t i = 0; i < cantBeBlockedBys.size(); i++)
{
if (cantBeBlockedBys[i]->canTarget(card, true))
return 1;
}
return 0;
}
//cant be the block of
int MTGCardInstance::addCantBeBlockerOf(TargetChooser * tc)
{
cantBeBlockerOfs.push_back(tc);
return cantBeBlockerOfs.size();
}
int MTGCardInstance::removeCantBeBlockerOf(TargetChooser * tc, int erase)
{
for (size_t i = 0; i < cantBeBlockerOfs.size(); i++)
{
if (cantBeBlockerOfs[i] == tc)
{
if (erase)
delete (cantBeBlockerOfs[i]);
cantBeBlockerOfs.erase(cantBeBlockerOfs.begin() + i);
return 1;
}
}
return 0;
}
int MTGCardInstance::cantBeBlockerOf(MTGCardInstance * card)
{
for (size_t i = 0; i < cantBeBlockerOfs.size(); i++)
{
if (cantBeBlockerOfs[i]->canTarget(card, true))
return 1;
}
return 0;
}
//cant be the block of
int MTGCardInstance::addCantBeBlockerOfCard(MTGCardInstance * card)
{
cantBeBlockerOfCards.push_back(card);
return cantBeBlockerOfCards.size();
}
int MTGCardInstance::removeCantBeBlockerOfCard(MTGCardInstance * card, int erase)
{
for (size_t i = 0; i < cantBeBlockerOfCards.size(); i++)
{
if (cantBeBlockerOfCards[i] == card)
{
if (erase)
delete (cantBeBlockerOfCards[i]);
cantBeBlockerOfCards.erase(cantBeBlockerOfCards.begin() + i);
return 1;
}
}
return 0;
}
int MTGCardInstance::cantBeBlockerOfCard(MTGCardInstance * card)
{
for (size_t i = 0; i < cantBeBlockerOfCards.size(); i++)
{
if (cantBeBlockerOfCards[i] == card)
return 1;
}
return 0;
}
/* Choose a sound sample to associate to that card */
const string& MTGCardInstance::getSample()
{
if (sample.size())
return sample;
for (int i = types.size() - 1; i > 0; i--)
{
string type = MTGAllCards::findType(types[i]);
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
type = type + ".wav";
if(getObserver() && getObserver()->getResourceManager())
{
if (getObserver()->getResourceManager()->RetrieveSample(type))
{
sample = string(type);
return sample;
}
}
}
if (basicAbilities.any())
{
for (size_t x = 0; x < basicAbilities.size(); ++x)
{
if (!basicAbilities.test(x))
continue;
string type = Constants::MTGBasicAbilities[x];
type = type + ".wav";
if(getObserver() && getObserver()->getResourceManager())
{
if (getObserver()->getResourceManager()->RetrieveSample(type))
{
sample = string(type);
return sample;
}
}
}
}
string type = "";
if(!types.size())
return sample;
type = MTGAllCards::findType(types[0]);
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
type.append(".wav");
if(getObserver() && getObserver()->getResourceManager())
{
if (getObserver()->getResourceManager()->RetrieveSample(type))
{
sample = string(type);
return sample;
}
}
return sample;
}
int MTGCardInstance::stepPower(CombatStep step)
{
int damage = has(Constants::COMBATTOUGHNESS) ? toughness : power;
switch (step)
{
case FIRST_STRIKE:
case END_FIRST_STRIKE:
if (has(Constants::FIRSTSTRIKE) || has(Constants::DOUBLESTRIKE))
return MAX(0, damage);
else
return 0;
case DAMAGE:
case END_DAMAGE:
default:
if (has(Constants::FIRSTSTRIKE) && !has(Constants::DOUBLESTRIKE))
return 0;
else
return MAX(0, damage);
}
}
std::ostream& MTGCardInstance::toString(std::ostream& out) const
{
return out << name;
}
std::ostream& operator<<(std::ostream& out, const MTGCardInstance& c)
{
return c.toString(out);
}
MTGCardInstance* MTGCardInstance::clone()
{
return NEW MTGCardInstance(model, owner->game);
}