1735 lines
56 KiB
C++
1735 lines
56 KiB
C++
#include "PrecompiledHeader.h"
|
|
|
|
#include "CardSelector.h"
|
|
#include "MTGGameZones.h"
|
|
#include "Player.h"
|
|
#include "WEvent.h"
|
|
#include "MTGDeck.h"
|
|
#include "Subtypes.h"
|
|
|
|
#include "Rules.h"
|
|
#include "Token.h"
|
|
|
|
#if defined (WIN32) || defined (LINUX)
|
|
#include <time.h>
|
|
#endif
|
|
//------------------------------
|
|
//Players Game
|
|
//------------------------------
|
|
|
|
MTGPlayerCards::MTGPlayerCards()
|
|
: owner(0)
|
|
{
|
|
init();
|
|
}
|
|
|
|
MTGPlayerCards::MTGPlayerCards(Player* player, int * idList, int idListSize)
|
|
: owner(player)
|
|
{
|
|
init();
|
|
int i;
|
|
|
|
for (i = 0; i < idListSize; i++)
|
|
{
|
|
MTGCard * card = MTGCollection()->getCardById(idList[i]);
|
|
if (card)
|
|
{
|
|
MTGCardInstance * newCard = NEW MTGCardInstance(card, this);
|
|
library->addCard(newCard);
|
|
}
|
|
}
|
|
}
|
|
|
|
MTGPlayerCards::MTGPlayerCards(MTGDeck * deck)
|
|
: owner(0)
|
|
{
|
|
init();
|
|
initDeck(deck);
|
|
}
|
|
|
|
void MTGPlayerCards::initDeck(MTGDeck * deck)
|
|
{
|
|
resetLibrary();
|
|
//commander zone init
|
|
if(deck->CommandZone.size())
|
|
{
|
|
for(unsigned int j = 0; j < deck->CommandZone.size(); j++)
|
|
{
|
|
string cardID = deck->CommandZone[j];
|
|
MTGCard * card = MTGCollection()->getCardById(atoi(cardID.c_str()));
|
|
if(card)
|
|
{
|
|
MTGCardInstance * newCard = NEW MTGCardInstance(card, this);
|
|
//commander zone
|
|
newCard->basicAbilities[Constants::ISCOMMANDER] = 1;
|
|
commandzone->addCard(newCard);
|
|
}
|
|
}
|
|
}
|
|
map<int, int>::iterator it;
|
|
for (it = deck->cards.begin(); it != deck->cards.end(); it++)
|
|
{
|
|
MTGCard * card = deck->getCardById(it->first);
|
|
if (card)
|
|
{
|
|
for (int j = 0; j < it->second; j++)
|
|
{
|
|
MTGCardInstance * newCard = NEW MTGCardInstance(card, this);
|
|
if(!commandzone->cards.size()){ //If no commander in Deck there are no limitations for cards.
|
|
library->addCard(newCard);
|
|
} else {
|
|
if(newCard->hasType("Land") && newCard->hasType("Basic")){ //There are no limitations for basic lands cards.
|
|
library->addCard(newCard);
|
|
} else{
|
|
bool colorFound = false; // All the cards have to share at least one color with commander identity color (any symbol in manacost or magic text).
|
|
bool colorless = false; // Colorless card can be always added to deck.
|
|
for(unsigned int i = 0; i < commandzone->cards.size() && !colorFound; i++){
|
|
if((newCard->hasColor(Constants::MTG_COLOR_WHITE) && commandzone->cards[i]->hasColor(Constants::MTG_COLOR_WHITE)) ||
|
|
(newCard->hasColor(Constants::MTG_COLOR_BLACK) && commandzone->cards[i]->hasColor(Constants::MTG_COLOR_BLACK)) ||
|
|
(newCard->hasColor(Constants::MTG_COLOR_RED) && commandzone->cards[i]->hasColor(Constants::MTG_COLOR_RED)) ||
|
|
(newCard->hasColor(Constants::MTG_COLOR_BLUE) && commandzone->cards[i]->hasColor(Constants::MTG_COLOR_BLUE)) ||
|
|
(newCard->hasColor(Constants::MTG_COLOR_GREEN) && commandzone->cards[i]->hasColor(Constants::MTG_COLOR_GREEN)) ||
|
|
(newCard->hasColor(Constants::MTG_COLOR_WHITE) && commandzone->cards[i]->magicText.find("{w}") != std::string::npos) ||
|
|
(newCard->hasColor(Constants::MTG_COLOR_BLACK) && commandzone->cards[i]->magicText.find("{b}") != std::string::npos)||
|
|
(newCard->hasColor(Constants::MTG_COLOR_RED) && commandzone->cards[i]->magicText.find("{r}") != std::string::npos) ||
|
|
(newCard->hasColor(Constants::MTG_COLOR_BLUE) && commandzone->cards[i]->magicText.find("{u}") != std::string::npos) ||
|
|
(newCard->hasColor(Constants::MTG_COLOR_GREEN) && commandzone->cards[i]->magicText.find("{g}") != std::string::npos) ||
|
|
(newCard->magicText.find("{w}") != std::string::npos && commandzone->cards[i]->hasColor(Constants::MTG_COLOR_WHITE)) ||
|
|
(newCard->magicText.find("{b}") != std::string::npos && commandzone->cards[i]->hasColor(Constants::MTG_COLOR_BLACK)) ||
|
|
(newCard->magicText.find("{r}") != std::string::npos && commandzone->cards[i]->hasColor(Constants::MTG_COLOR_RED)) ||
|
|
(newCard->magicText.find("{u}") != std::string::npos && commandzone->cards[i]->hasColor(Constants::MTG_COLOR_BLUE)) ||
|
|
(newCard->magicText.find("{g}") != std::string::npos && commandzone->cards[i]->hasColor(Constants::MTG_COLOR_GREEN)) ||
|
|
(newCard->magicText.find("{w}") != std::string::npos && commandzone->cards[i]->magicText.find("{w}") != std::string::npos) ||
|
|
(newCard->magicText.find("{b}") != std::string::npos && commandzone->cards[i]->magicText.find("{b}") != std::string::npos)||
|
|
(newCard->magicText.find("{r}") != std::string::npos && commandzone->cards[i]->magicText.find("{r}") != std::string::npos) ||
|
|
(newCard->magicText.find("{u}") != std::string::npos && commandzone->cards[i]->magicText.find("{u}") != std::string::npos) ||
|
|
(newCard->magicText.find("{g}") != std::string::npos && commandzone->cards[i]->magicText.find("{g}") != std::string::npos)){
|
|
colorFound = true;
|
|
}
|
|
}
|
|
if(!colorFound)
|
|
colorless = (newCard->magicText.find("{g}") == std::string::npos && newCard->magicText.find("{w}") == std::string::npos && newCard->magicText.find("{b}") == std::string::npos &&
|
|
newCard->magicText.find("{r}") == std::string::npos && newCard->magicText.find("{u}") == std::string::npos && !newCard->hasColor(Constants::MTG_COLOR_BLUE) &&
|
|
!newCard->hasColor(Constants::MTG_COLOR_RED) && !newCard->hasColor(Constants::MTG_COLOR_WHITE) && !newCard->hasColor(Constants::MTG_COLOR_GREEN) &&
|
|
!newCard->hasColor(Constants::MTG_COLOR_BLACK));
|
|
if(colorFound || colorless){
|
|
bool onlyInstance = true; // In commander format only single cards are allowed if they are not basic lands.
|
|
for(unsigned int k = 0; k < library->cards.size() && onlyInstance; k++){
|
|
if(library->cards[k]->name == newCard->name)
|
|
onlyInstance = false;
|
|
}
|
|
if(onlyInstance)
|
|
library->addCard(newCard);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//sb init
|
|
if(deck->Sideboard.size())
|
|
{
|
|
for(unsigned int j = 0; j < deck->Sideboard.size(); j++)
|
|
{
|
|
string cardID = deck->Sideboard[j];
|
|
MTGCard * card = MTGCollection()->getCardById(atoi(cardID.c_str()));
|
|
if(card)
|
|
{
|
|
MTGCardInstance * newCard = NEW MTGCardInstance(card, this);
|
|
//sb zone
|
|
sideboard->addCard(newCard);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MTGPlayerCards::~MTGPlayerCards()
|
|
{
|
|
SAFE_DELETE(library);
|
|
SAFE_DELETE(graveyard);
|
|
SAFE_DELETE(hand);
|
|
SAFE_DELETE(inPlay);
|
|
SAFE_DELETE(stack);
|
|
SAFE_DELETE(removedFromGame);
|
|
SAFE_DELETE(garbage);
|
|
SAFE_DELETE(reveal);
|
|
SAFE_DELETE(sideboard);
|
|
SAFE_DELETE(commandzone);
|
|
SAFE_DELETE(temp);
|
|
SAFE_DELETE(playRestrictions);
|
|
}
|
|
|
|
void MTGPlayerCards::beforeBeginPhase()
|
|
{
|
|
SAFE_DELETE(garbageLastTurn);
|
|
garbageLastTurn = garbage = NEW MTGGameZone();
|
|
garbage->setOwner(this->owner);
|
|
|
|
library->beforeBeginPhase();
|
|
graveyard->beforeBeginPhase();
|
|
hand->beforeBeginPhase();
|
|
inPlay->beforeBeginPhase();
|
|
stack->beforeBeginPhase();
|
|
removedFromGame->beforeBeginPhase();
|
|
garbage->beforeBeginPhase();
|
|
reveal->beforeBeginPhase();
|
|
sideboard->beforeBeginPhase();
|
|
commandzone->beforeBeginPhase();
|
|
temp->beforeBeginPhase();
|
|
}
|
|
|
|
void MTGPlayerCards::setOwner(Player * player)
|
|
{
|
|
this->owner = player;
|
|
library->setOwner(player);
|
|
graveyard->setOwner(player);
|
|
hand->setOwner(player);
|
|
inPlay->setOwner(player);
|
|
removedFromGame->setOwner(player);
|
|
stack->setOwner(player);
|
|
garbage->setOwner(player);
|
|
garbageLastTurn->setOwner(player);
|
|
reveal->setOwner(player);
|
|
sideboard->setOwner(player);
|
|
commandzone->setOwner(player);
|
|
temp->setOwner(player);
|
|
}
|
|
|
|
void MTGPlayerCards::initGame(int shuffle, int draw)
|
|
{
|
|
if (shuffle)
|
|
library->shuffle();
|
|
if (draw)
|
|
{
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
drawFromLibrary();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MTGPlayerCards::OptimizedHand(Player * who,int amount, int lands, int creatures, int othercards)
|
|
{
|
|
//give the Ai hand adventage to insure a challanging match.
|
|
Player * p = dynamic_cast<Player*>(who);
|
|
MTGCardInstance * card = NULL;
|
|
MTGGameZone * z = p->game->library;
|
|
z->shuffle();
|
|
|
|
int optimizedland = 0;
|
|
int optimizedothercards = 0;
|
|
int optimizedcreatures = 0;
|
|
for (int j = 0; j < z->nb_cards; j++)
|
|
{
|
|
MTGCardInstance * _card = z->cards[j];
|
|
//-------------
|
|
if (_card->isLand() && optimizedland < lands)
|
|
{
|
|
card = _card;
|
|
if (card)
|
|
{
|
|
p->game->putInZone(card, p->game->library, p->game->hand);
|
|
optimizedland += 1;
|
|
}
|
|
}
|
|
//----------------first try to optimize a few cards that cost 2 or less.
|
|
if (_card->getManaCost()->getConvertedCost() <= 2 && optimizedothercards < othercards && !_card->isLand()
|
|
&& !_card->isCreature())
|
|
{
|
|
card = _card;
|
|
if (card)
|
|
{
|
|
p->game->putInZone(card, p->game->library, p->game->hand);
|
|
optimizedothercards += 1;
|
|
}
|
|
}
|
|
if (_card->getManaCost()->getConvertedCost() <= 2 && optimizedcreatures < creatures && _card->isCreature())
|
|
{
|
|
card = _card;
|
|
if (card)
|
|
{
|
|
p->game->putInZone(card, p->game->library, p->game->hand);
|
|
optimizedcreatures += 1;
|
|
}
|
|
}
|
|
}
|
|
//--------------incase none of them cost 2 or less(which makes for a really poorly crafted Ai deck), try for 3 or less at this point we're accepting anything but lands under 3 mana---
|
|
for (int k = 0; k < z->nb_cards; k++)
|
|
{
|
|
MTGCardInstance * _card = z->cards[k];
|
|
|
|
if (_card->getManaCost()->getConvertedCost() <= 3 && optimizedothercards < othercards && (!_card->isLand()
|
|
|| _card->isCreature()))
|
|
{
|
|
card = _card;
|
|
if (card)
|
|
{
|
|
p->game->putInZone(card, p->game->library, p->game->hand);
|
|
optimizedothercards += 1;
|
|
}
|
|
}
|
|
if (_card->getManaCost()->getConvertedCost() <= 3 && optimizedcreatures < creatures && (_card->isCreature()
|
|
|| !_card->isLand()))
|
|
{
|
|
card = _card;
|
|
if (card)
|
|
{
|
|
p->game->putInZone(card, p->game->library, p->game->hand);
|
|
optimizedcreatures += 1;
|
|
}
|
|
}
|
|
}
|
|
//--------------add up remaining. only 7 cards are optimized, the remaining cards (if rules change amount) are just drawn.
|
|
int leftover = 0;
|
|
leftover = amount;
|
|
leftover -= optimizedland;
|
|
leftover -= optimizedcreatures;
|
|
leftover -= optimizedothercards;
|
|
for (int i = leftover; i > 0; i--)
|
|
{
|
|
p->game->drawFromLibrary();
|
|
}
|
|
|
|
}
|
|
//----------------------------
|
|
|
|
void MTGPlayerCards::drawFromLibrary()
|
|
{
|
|
if (!library->nb_cards)
|
|
{
|
|
if (inPlay->hasAbility(Constants::CANTLOSE)
|
|
|| inPlay->hasAbility(Constants::CANTMILLLOSE)
|
|
|| owner->opponent()->game->inPlay->hasAbility(Constants::CANTWIN))
|
|
{
|
|
return;
|
|
}
|
|
|
|
library->owner->getObserver()->setLoser(library->owner);
|
|
return;
|
|
}
|
|
MTGCardInstance * toMove = library->cards[library->nb_cards - 1];
|
|
if (!library->miracle)
|
|
{
|
|
library->miracle = true;
|
|
toMove->miracle = true;
|
|
}
|
|
|
|
bool prefetch = options[Options::CARDPREFETCHING].number?true:false;
|
|
if (prefetch && WResourceManager::Instance()->IsThreaded())
|
|
{
|
|
// useability tweak - assume that the user is probably going to want to see the new card,
|
|
// so prefetch it.
|
|
|
|
// if we're not in text mode, always get the thumb
|
|
if (library->owner->getObserver()->getCardSelector()->GetDrawMode() != DrawMode::kText
|
|
&& library->owner->getObserver()->getResourceManager())
|
|
{
|
|
DebugTrace("Prefetching AI card going into play: " << toMove->getImageName());
|
|
library->owner->getObserver()->getResourceManager()->RetrieveCard(toMove, RETRIEVE_THUMB);
|
|
|
|
// also cache the large image if we're using kNormal mode
|
|
if (library->owner->getObserver()->getCardSelector()->GetDrawMode() == DrawMode::kNormal)
|
|
{
|
|
library->owner->getObserver()->getResourceManager()->RetrieveCard(toMove);
|
|
}
|
|
}
|
|
}
|
|
|
|
MTGCardInstance * ret = putInZone(toMove, library, hand);
|
|
if(ret)
|
|
{
|
|
toMove->currentZone = hand;
|
|
ret->currentZone = hand;
|
|
library->lastCardDrawn = ret;
|
|
}
|
|
}
|
|
|
|
void MTGPlayerCards::resetLibrary()
|
|
{
|
|
SAFE_DELETE(library);
|
|
library = NEW MTGLibrary();
|
|
library->miracle = false;
|
|
}
|
|
|
|
void MTGPlayerCards::init()
|
|
{
|
|
library = NEW MTGLibrary();
|
|
library->miracle = false;
|
|
graveyard = NEW MTGGraveyard();
|
|
hand = NEW MTGHand();
|
|
inPlay = NEW MTGInPlay();
|
|
battlefield = inPlay;
|
|
|
|
stack = NEW MTGStack();
|
|
removedFromGame = NEW MTGRemovedFromGame();
|
|
exile = removedFromGame;
|
|
garbage = NEW MTGGameZone();
|
|
garbageLastTurn = garbage;
|
|
reveal = NEW MTGGameZone();
|
|
sideboard = NEW MTGGameZone();
|
|
commandzone = NEW MTGGameZone();
|
|
temp = NEW MTGGameZone();
|
|
|
|
playRestrictions = NEW PlayRestrictions();
|
|
}
|
|
|
|
void MTGPlayerCards::showHand()
|
|
{
|
|
hand->debugPrint();
|
|
}
|
|
|
|
// Moves a card to its owner's graveyard
|
|
MTGCardInstance * MTGPlayerCards::putInGraveyard(MTGCardInstance * card)
|
|
{
|
|
if (card->basicAbilities[(int)Constants::EXILEDEATH])
|
|
{
|
|
putInZone(card, card->getCurrentZone(), card->owner->game->exile);
|
|
|
|
}
|
|
return putInZone(card, card->currentZone, card->owner->game->graveyard);
|
|
}
|
|
|
|
// Moves a card to its owner's exile
|
|
MTGCardInstance * MTGPlayerCards::putInExile(MTGCardInstance * card)
|
|
{
|
|
return putInZone(card, card->currentZone, card->owner->game->exile);
|
|
}
|
|
|
|
// Moves a card to its owner's library
|
|
MTGCardInstance * MTGPlayerCards::putInLibrary(MTGCardInstance * card)
|
|
{
|
|
return putInZone(card, card->currentZone, card->owner->game->library);
|
|
}
|
|
|
|
// Moves a card to its *owner's* (not controller!) hand
|
|
MTGCardInstance * MTGPlayerCards::putInHand(MTGCardInstance * card)
|
|
{
|
|
return putInZone(card, card->currentZone, card->owner->game->hand);
|
|
}
|
|
|
|
// Moves a card from one zone to another
|
|
// If the card is not actually in the expected "from" zone, does nothing and returns null
|
|
MTGCardInstance * MTGPlayerCards::putInZone(MTGCardInstance * card, MTGGameZone * from, MTGGameZone * to,bool asCopy)
|
|
{
|
|
MTGCardInstance * copy = NULL;
|
|
Player * discarderOwner = NULL;
|
|
GameObserver *g = owner->getObserver();
|
|
if (!from || !to)
|
|
return card; //Error check
|
|
|
|
int doCopy = 1;
|
|
bool shufflelibrary = card->basicAbilities[(int)Constants::SHUFFLELIBRARYDEATH];
|
|
bool inplaytoinplay = false;
|
|
bool ripToken = false;
|
|
if (card->discarderOwner)
|
|
discarderOwner = card->discarderOwner;
|
|
if (g->players[0]->game->battlefield->hasName("Rest in Peace")||g->players[1]->game->battlefield->hasName("Rest in Peace"))
|
|
ripToken = true;
|
|
//Madness or Put in Play...
|
|
for(int i = 0; i < 2; ++i)
|
|
{
|
|
if (card->discarded && (to == g->players[i]->game->graveyard) && (from == g->players[i]->game->hand))
|
|
{
|
|
if(card->basicAbilities[(int)Constants::MADNESS])
|
|
to = g->players[i]->game->exile;
|
|
}
|
|
}
|
|
//Darksteel Colossus, Legacy Weapon ... top priority since we replace destination directly automatically...
|
|
for(int i = 0; i < 2; ++i)
|
|
{
|
|
if ((to == g->players[i]->game->graveyard) && (
|
|
card->basicAbilities[(int)Constants::LIBRARYDEATH]||
|
|
card->basicAbilities[(int)Constants::SHUFFLELIBRARYDEATH]))
|
|
{
|
|
to = g->players[i]->game->library;
|
|
}
|
|
}
|
|
//Leyline of the Void, Yawgmoth's Agenda... effect...
|
|
for(int i = 0; i < 2; ++i)
|
|
{
|
|
if ((to == g->players[i]->game->graveyard) && (
|
|
(g->players[i]->game->battlefield->hasAbility(Constants::MYGCREATUREEXILER) && card->isCreature()) ||
|
|
(g->players[i]->opponent()->game->battlefield->hasAbility(Constants::OPPGCREATUREEXILER) && card->isCreature())||
|
|
g->players[i]->game->battlefield->hasAbility(Constants::MYGRAVEEXILER) ||
|
|
g->players[i]->opponent()->game->battlefield->hasAbility(Constants::OPPGRAVEEXILER)))
|
|
{
|
|
if ((card->isToken && ripToken))
|
|
to = g->players[i]->game->exile;
|
|
if (!card->isToken)
|
|
to = g->players[i]->game->exile;
|
|
}
|
|
|
|
//close the currently open MAIN display.
|
|
if (from == g->players[i]->game->library || from == g->players[i]->game->graveyard || from == g->players[i]->game->exile || from == g->players[i]->game->commandzone)
|
|
{
|
|
if (g->guiOpenDisplay)
|
|
{
|
|
g->ButtonPressed(g->guiOpenDisplay);
|
|
}
|
|
}
|
|
|
|
}
|
|
//all cards that go from the hand to the graveyard is ALWAYS a discard.
|
|
if ((to == g->players[0]->game->graveyard || to == g->players[1]->game->graveyard) && (from == g->players[0]->game->hand || from
|
|
== g->players[1]->game->hand))
|
|
{
|
|
card->discarded = true;
|
|
}
|
|
|
|
//When a card is moved from inPlay to inPlay (controller change, for example), it is still the same object
|
|
if ((to == g->players[0]->game->inPlay || to == g->players[1]->game->inPlay) && (from == g->players[0]->game->inPlay || from
|
|
== g->players[1]->game->inPlay))
|
|
{
|
|
doCopy = 0;
|
|
asCopy = true;//don't send zone change event so it will not destroy the GUI when multiple switching of control...
|
|
inplaytoinplay = true;//try sending different event...
|
|
}
|
|
|
|
//Increase the number of time this card has been casted from commandzone to recalculate cost.
|
|
if(from == g->players[0]->game->commandzone || from == g->players[1]->game->commandzone){
|
|
card->numofcastfromcommandzone++;
|
|
card->controller()->numOfCommandCast++;
|
|
}
|
|
|
|
if (!(copy = from->removeCard(card, doCopy)))
|
|
return NULL; //ERROR
|
|
|
|
// Copy all the counters of the original card... (solving the bug on comparison cards with counter before zone changing events)
|
|
if(card->counters && doCopy && !asCopy && !inplaytoinplay){
|
|
for (unsigned int i = 0; i < card->counters->counters.size(); i++){
|
|
Counter * counter = card->counters->counters[i];
|
|
for(int j = 0; j < counter->nb; j++)
|
|
copy->counters->addCounter(counter->name.c_str(), counter->power, counter->toughness, true);
|
|
}
|
|
}
|
|
|
|
//Commander is going back to Command Zone, so we recalculate cost according to how many times it has been casted from there.
|
|
if(to == g->players[0]->game->commandzone || to == g->players[1]->game->commandzone){
|
|
for(int i = 0; i < copy->numofcastfromcommandzone; i++)
|
|
copy->getManaCost()->add(Constants::MTG_COLOR_ARTIFACT,2);
|
|
}
|
|
|
|
if (card->miracle)
|
|
{
|
|
copy->miracle = true;
|
|
}
|
|
//reset discarder Owner
|
|
if(to == g->players[0]->game->hand || to == g->players[0]->game->stack || to == g->players[0]->game->library ||
|
|
to == g->players[1]->game->hand || to == g->players[1]->game->stack || to == g->players[1]->game->library)
|
|
{
|
|
card->discarderOwner = NULL;
|
|
copy->discarderOwner = NULL;
|
|
}
|
|
//copy discarderowner
|
|
if (discarderOwner)
|
|
{
|
|
copy->discarderOwner = discarderOwner;
|
|
//change to
|
|
if(to == g->players[0]->game->graveyard)
|
|
{
|
|
if(card->has(Constants::DISCARDTOPLAYBYOPPONENT) && discarderOwner == card->controller()->opponent())
|
|
{
|
|
to = g->players[0]->game->battlefield;
|
|
}
|
|
}
|
|
else if(to == g->players[1]->game->graveyard)
|
|
{
|
|
if(card->has(Constants::DISCARDTOPLAYBYOPPONENT) && discarderOwner == card->controller()->opponent())
|
|
{
|
|
to = g->players[1]->game->battlefield;
|
|
}
|
|
}
|
|
}
|
|
if(from == g->players[0]->game->battlefield || from == g->players[1]->game->battlefield)
|
|
if(to != g->players[0]->game->battlefield || to != g->players[1]->game->battlefield)
|
|
{
|
|
card->kicked = 0;
|
|
copy->kicked = 0;//kicked reset everflowing chalice...
|
|
}
|
|
if (card->discarded)
|
|
{//set discarded for madness...
|
|
if(from == g->players[0]->game->hand || from == g->players[1]->game->hand)
|
|
copy->discarded = true;
|
|
else//turn off discarded if its previous zone is not in hand...
|
|
copy->discarded = false;
|
|
}
|
|
if (options[Options::SFXVOLUME].number > 0)
|
|
{
|
|
if (to == g->players[0]->game->graveyard || to == g->players[1]->game->graveyard)
|
|
{
|
|
if (card->isCreature() && g->getResourceManager())
|
|
{
|
|
g->getResourceManager()->PlaySample("graveyard.wav");
|
|
}
|
|
}
|
|
}
|
|
|
|
MTGCardInstance * ret = copy;
|
|
for(int i = 0; i < 2; ++i)
|
|
{
|
|
if(to == g->players[i]->game->library && from == g->players[i]->game->library)//if its going to the library from the library we intend to put it on top.
|
|
{
|
|
g->players[i]->game->temp->addCard(copy);
|
|
g->players[i]->game->library->placeOnTop.push_back(copy);
|
|
return ret;//don't send event
|
|
}
|
|
}
|
|
//before adding card to zone, if its Melded, we break it apart
|
|
if (from == g->players[0]->game->battlefield || from == g->players[1]->game->battlefield)
|
|
{
|
|
if(to != g->players[0]->game->battlefield || to != g->players[1]->game->battlefield)
|
|
if (copy->previous && copy->previous->MeldedFrom.size() && !copy->isACopier && !copy->isToken)//!copier & !token fix kiki-jiki clones crash
|
|
{
|
|
vector<string> names = split(copy->previous->MeldedFrom, '|');
|
|
MTGCard * cardone = MTGCollection()->getCardByName(names[0]);
|
|
MTGCardInstance * cardOne = NEW MTGCardInstance(cardone, copy->owner->game);
|
|
to->addCard(cardOne);
|
|
WEvent * e = NEW WEventZoneChange(cardOne, from, to);
|
|
g->receiveEvent(e);
|
|
MTGCard * cardtwo = MTGCollection()->getCardByName(names[1]);
|
|
MTGCardInstance * cardTwo = NEW MTGCardInstance(cardtwo, copy->owner->game);
|
|
to->addCard(cardTwo);
|
|
WEvent * e2 = NEW WEventZoneChange(cardTwo, from, to);
|
|
g->receiveEvent(e2);
|
|
|
|
if(from == g->players[0]->game->battlefield)
|
|
g->players[0]->game->temp->addCard(copy);
|
|
if (from == g->players[1]->game->battlefield)
|
|
g->players[1]->game->temp->addCard(copy);
|
|
WEvent * e3 = NEW WEventZoneChange(copy, from, to);
|
|
g->receiveEvent(e3);
|
|
return ret;
|
|
}
|
|
|
|
}
|
|
to->addCard(copy);
|
|
//The "Temp" zone are purely for code purposes, and we don't want the abilities engine to
|
|
//Trigger when cards move in this zone
|
|
// Additionally, when they move "from" this zone,
|
|
// we trick the engine into believing that they moved from the zone the card was previously in
|
|
// See http://code.google.com/p/wagic/issues/detail?id=335
|
|
{
|
|
if (to == g->players[0]->game->temp || to == g->players[1]->game->temp)
|
|
{
|
|
//don't send event when moving to temp
|
|
return ret;
|
|
}
|
|
|
|
if (from == g->players[0]->game->temp || from == g->players[1]->game->temp)
|
|
{
|
|
//remove temporary stuff
|
|
MTGCardInstance * previous = copy->previous;
|
|
MTGCardInstance * previous2 = previous->previous;
|
|
from = previous->previousZone;
|
|
copy->previous = previous2;
|
|
if (previous2)
|
|
previous2->next = copy;
|
|
previous->previous = NULL;
|
|
previous->next = NULL;
|
|
SAFE_DELETE(previous);
|
|
}
|
|
|
|
if(to == g->players[0]->game->battlefield || to == g->players[1]->game->battlefield)
|
|
{
|
|
if(ret->alias == 109736 && discarderOwner)
|
|
{
|
|
if(discarderOwner == ret->controller()->opponent())
|
|
{
|
|
AbilityFactory af(g);
|
|
MTGAbility * dodeCounter = af.parseMagicLine("counter(1/1,2)",-1,NULL,ret);
|
|
dodeCounter->oneShot = true;
|
|
dodeCounter->canBeInterrupted = false;
|
|
dodeCounter->resolve();
|
|
SAFE_DELETE(dodeCounter);
|
|
}
|
|
}
|
|
}
|
|
//remove exerted if changing controls
|
|
if((to == g->players[0]->game->battlefield && from == g->players[1]->game->battlefield)||
|
|
(to == g->players[1]->game->battlefield && from == g->players[0]->game->battlefield))
|
|
{
|
|
if(ret->exerted)
|
|
ret->exerted = false;
|
|
}
|
|
}
|
|
if(!asCopy)
|
|
{
|
|
if(shufflelibrary)
|
|
copy->owner->game->library->shuffle();//shouldnt we only ever do this if you clicked close on your library gui??????
|
|
|
|
WEvent * e = NEW WEventZoneChange(copy, from, to);
|
|
g->receiveEvent(e);
|
|
|
|
// Erasing counters from copy after the event has been triggered (no counter can survive to a zone changing)
|
|
if(doCopy && copy->counters && copy->counters->mCount > 0){
|
|
for (unsigned int i = 0; i < copy->counters->counters.size(); i++){
|
|
Counter * counter = copy->counters->counters[i];
|
|
for(int j = counter->nb; j > 0; j--)
|
|
copy->counters->removeCounter(counter->name.c_str(), counter->power, counter->toughness, true);
|
|
}
|
|
}
|
|
}
|
|
if(inplaytoinplay)
|
|
{
|
|
WEvent * ep = NEW WEventCardControllerChange(copy);
|
|
g->receiveEvent(ep);
|
|
}
|
|
return ret;
|
|
|
|
}
|
|
|
|
void MTGPlayerCards::discardRandom(MTGGameZone * from, MTGCardInstance * _stored)
|
|
{
|
|
if (!from->nb_cards)
|
|
return;
|
|
int r = owner->getObserver()->getRandomGenerator()->random() % (from->nb_cards);
|
|
WEvent * e = NEW WEventCardDiscard(from->cards[r]);
|
|
GameObserver * game = owner->getObserver();
|
|
game->receiveEvent(e);
|
|
if(_stored)
|
|
from->cards[r]->discarderOwner = _stored->controller();
|
|
putInZone(from->cards[r], from, graveyard);
|
|
}
|
|
|
|
int MTGPlayerCards::isInPlay(MTGCardInstance * card)
|
|
{
|
|
if (inPlay->hasCard(card))
|
|
{
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
int MTGPlayerCards::isInZone(MTGCardInstance * card,MTGGameZone * zone)
|
|
{
|
|
if (zone->hasCard(card))
|
|
{
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
//--------------------------------------
|
|
// Zones specific code
|
|
//--------------------------------------
|
|
|
|
MTGGameZone::MTGGameZone() :
|
|
nb_cards(0), lastCardDrawn(NULL), needShuffle(false)
|
|
{
|
|
}
|
|
|
|
MTGGameZone::~MTGGameZone()
|
|
{
|
|
for (size_t i = 0; i < cards.size(); i++)
|
|
{
|
|
cards[i]->stillNeeded = false;
|
|
//SAFE_DELETE(cards[i]->previous);
|
|
//SAFE_DELETE( cards[i] );
|
|
//cause crashes for generated cards using castcard named card...??? test fix for now
|
|
if(cards[i]->previous)
|
|
{
|
|
delete cards[i]->previous;
|
|
cards[i]->previous = NULL;
|
|
}
|
|
if(cards[i])
|
|
{
|
|
delete cards[i];
|
|
cards[i] = NULL;
|
|
}
|
|
}
|
|
cards.clear();
|
|
cardsMap.clear();
|
|
owner = NULL;
|
|
}
|
|
|
|
void MTGGameZone::beforeBeginPhase()
|
|
{
|
|
cardsSeenLastTurn.clear();
|
|
for(size_t k = 0; k < cardsSeenThisTurn.size(); k++)
|
|
{
|
|
cardsSeenLastTurn.push_back(cardsSeenThisTurn[k]);
|
|
}
|
|
cardsSeenThisTurn.clear();
|
|
};
|
|
|
|
void MTGGameZone::setOwner(Player * player)
|
|
{
|
|
for (int i = 0; i < nb_cards; i++)
|
|
{
|
|
cards[i]->owner = player;
|
|
cards[i]->lastController = player;
|
|
cards[i]->setObserver(player->getObserver());
|
|
}
|
|
owner = player;
|
|
}
|
|
|
|
MTGCardInstance * MTGGameZone::removeCard(MTGCardInstance * card, int createCopy)
|
|
{
|
|
assert(nb_cards < 10000);
|
|
int i;
|
|
cardsMap.erase(card);
|
|
for (i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (cards[i] == card)
|
|
{
|
|
card->currentZone = NULL;
|
|
nb_cards--;
|
|
cards.erase(cards.begin() + i);
|
|
MTGCardInstance * copy = card;
|
|
//if (card->isToken) //TODO better than this ?
|
|
// return card;
|
|
//card->lastController = card->controller();
|
|
if(!card)
|
|
return NULL;
|
|
if (createCopy)
|
|
{
|
|
copy = card->clone();
|
|
copy->previous = card;
|
|
copy->view = card->view;
|
|
copy->isToken = card->isToken;
|
|
copy->X = card->X;
|
|
copy->castX = card->castX;
|
|
copy->kicked = card->kicked;
|
|
copy->storedCard = card->storedCard;
|
|
copy->storedSourceCard = card->storedSourceCard;
|
|
copy->lastController = card->controller();
|
|
copy->previousController = card->controller();
|
|
copy->basicAbilities[Constants::ISCOMMANDER] = card->basicAbilities[Constants::ISCOMMANDER];
|
|
copy->damageInflictedAsCommander = card->damageInflictedAsCommander;
|
|
copy->numofcastfromcommandzone = card->numofcastfromcommandzone;
|
|
for (int i = 0; i < ManaCost::MANA_PAID_WITH_BESTOW +1; i++)
|
|
copy->alternateCostPaid[i] = card->alternateCostPaid[i];
|
|
|
|
//stupid bug with tokens...
|
|
if (card->model == card)
|
|
copy->model = copy;
|
|
if (card->data == card)
|
|
copy->data = copy;
|
|
|
|
card->next = copy;
|
|
}
|
|
copy->previousZone = this;
|
|
return copy;
|
|
}
|
|
}
|
|
return NULL;
|
|
|
|
}
|
|
|
|
MTGCardInstance * MTGGameZone::hasCard(MTGCardInstance * card)
|
|
{
|
|
if (card->currentZone == this)
|
|
return card;
|
|
return NULL;
|
|
|
|
}
|
|
|
|
size_t MTGGameZone::getIndex(MTGCardInstance * card)
|
|
{
|
|
size_t i;
|
|
for(i = 0; i < cards.size(); i++)
|
|
{
|
|
if(cards[i] == card)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
unsigned int MTGGameZone::countByAlias(int number)
|
|
{
|
|
if(!number)
|
|
return 0;
|
|
int result = 0;
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (cards[i]->alias == number)
|
|
{
|
|
result++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
unsigned int MTGGameZone::countByType(const string &value)
|
|
{
|
|
int result = 0;
|
|
int subTypeId = MTGAllCards::findType(value);
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (cards[i]->hasType(subTypeId))
|
|
{
|
|
result++;
|
|
}
|
|
else if(value == "token" && cards[i]->isToken)
|
|
result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
unsigned int MTGGameZone::countByCanTarget(TargetChooser * tc)
|
|
{
|
|
if(!tc)
|
|
return 0;
|
|
// we don't care if cards have protection.
|
|
bool withoutProtections = true;
|
|
int result = 0;
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (tc->canTarget(cards[i], withoutProtections))
|
|
{
|
|
result++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
unsigned int MTGGameZone::countTotalManaSymbols(TargetChooser * tc, int color)
|
|
{
|
|
if (!tc) {
|
|
return 0;
|
|
}
|
|
// we don't care if cards have protection.
|
|
bool withoutProtections = true;
|
|
int result = 0;
|
|
for (int i = 0; i < nb_cards; i++)
|
|
{
|
|
if (tc->canTarget(cards[i], withoutProtections))
|
|
{
|
|
result += cards[i]->getManaCost()->getManaSymbols(color);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
unsigned int MTGGameZone::countDevotion(TargetChooser * tc, int color1, int color2)
|
|
{
|
|
if (!tc) {
|
|
return 0;
|
|
}
|
|
// we don't care if cards have protection.
|
|
bool withoutProtections = true;
|
|
int result = 0;
|
|
for (int i = 0; i < nb_cards; i++)
|
|
{
|
|
if (tc->canTarget(cards[i], withoutProtections))
|
|
{
|
|
result += cards[i]->getManaCost()->getManaSymbolsHybridMerged(color1);
|
|
}
|
|
if (tc->canTarget(cards[i], withoutProtections))
|
|
{
|
|
result += cards[i]->getManaCost()->getManaSymbolsHybridMerged(color2);
|
|
}
|
|
result -= cards[i]->getManaCost()->countHybridsNoPhyrexian();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
MTGCardInstance * MTGGameZone::findByName(string name)
|
|
{
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (cards[i]->name == name || cards[i]->getLCName()/*tokens*/ == name)
|
|
{
|
|
return cards[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool MTGGameZone::hasType(const char * value)
|
|
{
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (cards[i]->hasType(value))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MTGGameZone::hasTypeSpecificInt(int value1,int value)
|
|
{
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (cards[i]->hasType(value1) && cards[i]->hasType(value))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MTGGameZone::hasPrimaryType(const char * value,const char * secondvalue)
|
|
{
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (cards[i]->hasType(value) && cards[i]->hasType(secondvalue))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool MTGGameZone::hasSpecificType(const char * value,const char * secondvalue)
|
|
{
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (cards[i]->hasType(value) && cards[i]->hasSubtype(secondvalue))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MTGGameZone::hasTypeButNotType(const char * value,const char * secondvalue)
|
|
{
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (cards[i]->hasType(value) && cards[i]->hasSubtype(value) && !cards[i]->hasType(secondvalue) && !cards[i]->hasSubtype(secondvalue))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MTGGameZone::hasName(string value)
|
|
{
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (cards[i]->name == value)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MTGGameZone::hasColor(int value)
|
|
{
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (cards[i]->getManaCost()->hasColor(value) && cards[i]->getManaCost()->getConvertedCost() > 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MTGGameZone::hasX()
|
|
{
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (cards[i]->getManaCost()->hasX())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MTGGameZone::hasAbility(int ability)
|
|
{
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (cards[i]->basicAbilities[ability])
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MTGGameZone::hasAlias(int alias)
|
|
{
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
{
|
|
if (cards[i]->alias == alias)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int MTGGameZone::seenThisTurn(TargetChooser * tc, int castMethod, bool lastTurn)
|
|
{
|
|
//The following 2 lines modify the passed TargetChooser. Call this function with care :/
|
|
tc->setAllZones(); // This is to allow targetting cards without caring about the actual zone
|
|
tc->targetter = NULL;
|
|
|
|
int count = 0;
|
|
if (lastTurn)
|
|
{
|
|
for (vector<MTGCardInstance *>::iterator iter = cardsSeenLastTurn.begin(); iter != cardsSeenLastTurn.end(); ++iter)
|
|
{
|
|
MTGCardInstance * c = (*iter);
|
|
if (c && c->matchesCastFilter(castMethod) && tc->canTarget(c))
|
|
count++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (vector<MTGCardInstance *>::iterator iter = cardsSeenThisTurn.begin(); iter != cardsSeenThisTurn.end(); ++iter)
|
|
{
|
|
MTGCardInstance * c = (*iter);
|
|
if (c->matchesCastFilter(castMethod) && tc->canTarget(c))
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int MTGGameZone::seenThisTurn(string targetChooserDefinition, int castMethod)
|
|
{
|
|
TargetChooserFactory tcf(owner->getObserver());
|
|
TargetChooser *tc = tcf.createTargetChooser(targetChooserDefinition, NULL);
|
|
int result = seenThisTurn(tc, castMethod,false);
|
|
SAFE_DELETE(tc);
|
|
return result;
|
|
}
|
|
|
|
int MTGGameZone::seenLastTurn(string targetChooserDefinition, int castMethod)
|
|
{
|
|
TargetChooserFactory tcf(owner->getObserver());
|
|
TargetChooser *tc = tcf.createTargetChooser(targetChooserDefinition, NULL);
|
|
int result = seenThisTurn(tc, castMethod,true);
|
|
SAFE_DELETE(tc);
|
|
return result;
|
|
}
|
|
|
|
void MTGGameZone::cleanupPhase()
|
|
{
|
|
for (int i = 0; i < (nb_cards); i++)
|
|
(cards[i])->cleanup();
|
|
}
|
|
|
|
void MTGGameZone::shuffle()
|
|
{
|
|
owner->getObserver()->getRandomGenerator()->random_shuffle(cards.begin(), cards.end());
|
|
}
|
|
|
|
void MTGGameZone::addCard(MTGCardInstance * card)
|
|
{
|
|
if (!card)
|
|
return;
|
|
cards.push_back(card);
|
|
cardsSeenThisTurn.push_back(card);
|
|
nb_cards++;
|
|
cardsMap[card] = 1;
|
|
card->lastController = this->owner;
|
|
card->currentZone = this;
|
|
|
|
}
|
|
|
|
void MTGGameZone::debugPrint()
|
|
{
|
|
for (int i = 0; i < nb_cards; i++)
|
|
std::cerr << cards[i]->getName() << endl;
|
|
}
|
|
|
|
//------------------------------
|
|
MTGCardInstance * MTGInPlay::getNextAttacker(MTGCardInstance * previous)
|
|
{
|
|
int foundprevious = 0;
|
|
if (previous == NULL)
|
|
{
|
|
foundprevious = 1;
|
|
}
|
|
for (int i = 0; i < nb_cards; i++)
|
|
{
|
|
MTGCardInstance * current = cards[i];
|
|
if (current == previous)
|
|
{
|
|
foundprevious = 1;
|
|
}
|
|
else if (foundprevious && current->isAttacker())
|
|
{
|
|
return current;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
MTGCardInstance * MTGInPlay::getNextLurer(MTGCardInstance * previous)
|
|
{
|
|
int foundprevious = 0;
|
|
if (previous == NULL)
|
|
{
|
|
foundprevious = 1;
|
|
}
|
|
for (int i = 0; i < nb_cards; i++)
|
|
{
|
|
MTGCardInstance * current = cards[i];
|
|
if (current == previous)
|
|
{
|
|
foundprevious = 1;
|
|
}
|
|
else if (foundprevious && current->isAttacker() && current->has(Constants::LURE))
|
|
{
|
|
return current;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
MTGCardInstance * MTGInPlay::getNextProvoker(MTGCardInstance * previous, MTGCardInstance * thiscard)
|
|
{
|
|
int foundprevious = 0;
|
|
if (previous == NULL)
|
|
{
|
|
foundprevious = 1;
|
|
}
|
|
for (int i = 0; i < nb_cards; i++)
|
|
{
|
|
MTGCardInstance * current = cards[i];
|
|
if (current == previous)
|
|
{
|
|
foundprevious = 1;
|
|
}
|
|
else if (foundprevious && current->isAttacker() && thiscard->isProvoked && current->ProvokeTarget)
|
|
{
|
|
if(thiscard == current->ProvokeTarget)
|
|
return current;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
MTGCardInstance * MTGInPlay::findAProvoker(MTGCardInstance * thiscard)
|
|
{
|
|
for (int i = 0; i < nb_cards; i++)
|
|
{
|
|
MTGCardInstance * current = cards[i];
|
|
if (current->isAttacker() && current->ProvokeTarget)
|
|
{
|
|
if(current->ProvokeTarget == thiscard)
|
|
return current;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
MTGCardInstance * MTGInPlay::findALurer()
|
|
{
|
|
for (int i = 0; i < nb_cards; i++)
|
|
{
|
|
MTGCardInstance * current = cards[i];
|
|
if (current->isAttacker() && current->has(Constants::LURE))
|
|
{
|
|
return current;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void MTGInPlay::untapAll()
|
|
{
|
|
int i;
|
|
for (i = 0; i < nb_cards; i++)
|
|
{
|
|
MTGCardInstance * card = cards[i];
|
|
card->setUntapping();
|
|
if (!card->basicAbilities[(int)Constants::DOESNOTUNTAP] && !card->basicAbilities[(int)Constants::SHACKLER])
|
|
{
|
|
if(card->exerted)
|
|
{
|
|
card->exerted = false;
|
|
if (card->frozen >= 1)
|
|
{
|
|
card->frozen = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
card->exerted = false;
|
|
if (card->frozen < 1)
|
|
{
|
|
card->attemptUntap();
|
|
}
|
|
if (card->frozen >= 1)
|
|
{
|
|
card->frozen = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
MTGGameZone * MTGGameZone::intToZone(int zoneId, Player * p, Player * p2)
|
|
{
|
|
if (p2 != p && p2 && (p != p2->opponent()))
|
|
{
|
|
p = p2->opponent();
|
|
//these cases are generally handled this is a edge case fix.
|
|
}
|
|
switch (zoneId)
|
|
{
|
|
case MY_GRAVEYARD:
|
|
return p->game->graveyard;
|
|
case OPPONENT_GRAVEYARD:
|
|
return p->opponent()->game->graveyard;
|
|
|
|
case MY_BATTLEFIELD:
|
|
return p->game->inPlay;
|
|
case OPPONENT_BATTLEFIELD:
|
|
return p->opponent()->game->inPlay;
|
|
case BATTLEFIELD:
|
|
return p->game->inPlay;
|
|
|
|
case MY_HAND:
|
|
return p->game->hand;
|
|
case OPPONENT_HAND:
|
|
return p->opponent()->game->hand;
|
|
|
|
case MY_EXILE:
|
|
return p->game->removedFromGame;
|
|
case OPPONENT_EXILE:
|
|
return p->opponent()->game->removedFromGame;
|
|
|
|
|
|
case MY_LIBRARY:
|
|
return p->game->library;
|
|
case OPPONENT_LIBRARY:
|
|
return p->opponent()->game->library;
|
|
case LIBRARY:
|
|
return p->game->library;
|
|
|
|
case MY_STACK:
|
|
return p->game->stack;
|
|
case OPPONENT_STACK:
|
|
return p->opponent()->game->stack;
|
|
case STACK:
|
|
return p->game->stack;
|
|
|
|
case MY_REVEAL:
|
|
return p->game->reveal;
|
|
case OPPONENT_REVEAL:
|
|
return p->opponent()->game->reveal;
|
|
case REVEAL:
|
|
return p->game->reveal;
|
|
|
|
case MY_SIDEBOARD:
|
|
return p->game->sideboard;
|
|
case OPPONENT_SIDEBOARD:
|
|
return p->opponent()->game->sideboard;
|
|
case SIDEBOARD:
|
|
return p->game->sideboard;
|
|
|
|
case MY_COMMANDZONE:
|
|
return p->game->commandzone;
|
|
case OPPONENT_COMMANDZONE:
|
|
return p->opponent()->game->commandzone;
|
|
case COMMANDZONE:
|
|
return p->game->commandzone;
|
|
|
|
}
|
|
if (!p2) return NULL;
|
|
switch (zoneId)
|
|
{
|
|
case TARGET_CONTROLLER_GRAVEYARD:
|
|
return p2->game->graveyard;
|
|
|
|
case TARGET_CONTROLLER_BATTLEFIELD:
|
|
return p2->game->inPlay;
|
|
|
|
case TARGET_CONTROLLER_HAND:
|
|
return p2->game->hand;
|
|
|
|
case TARGET_CONTROLLER_EXILE:
|
|
return p2->game->removedFromGame;
|
|
|
|
case TARGET_CONTROLLER_LIBRARY:
|
|
return p2->game->library;
|
|
|
|
case TARGET_CONTROLLER_STACK:
|
|
return p2->game->stack;
|
|
|
|
case TARGET_CONTROLLER_REVEAL:
|
|
return p2->game->reveal;
|
|
|
|
case TARGET_CONTROLLER_SIDEBOARD:
|
|
return p2->game->sideboard;
|
|
|
|
case TARGET_CONTROLLER_COMMANDZONE:
|
|
return p2->game->commandzone;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
MTGGameZone * MTGGameZone::intToZone(GameObserver *g, int zoneId, MTGCardInstance * source, MTGCardInstance * target)
|
|
{
|
|
Player *p, *p2;
|
|
|
|
if (!source)
|
|
p = g->currentlyActing();
|
|
else
|
|
p = source->controller();
|
|
if (!target)
|
|
{
|
|
//TODO source may be NULL, need to handle the case when it is NULL. method declaration has NULL being default value of source and target.
|
|
if(source->target)
|
|
{
|
|
//bug case, this is a patchwork fix for now
|
|
//we need to find the root cause of why the 2nd variable is not returning the target.
|
|
p2 = source->target->controller();
|
|
target = source->target;
|
|
}
|
|
else
|
|
{
|
|
//bug or bug case default to
|
|
p2 = source->controller();
|
|
target = source;
|
|
}
|
|
}
|
|
else
|
|
p2 = target->controller();
|
|
|
|
|
|
MTGGameZone * result = intToZone(zoneId, p, p2);
|
|
if (result) return result;
|
|
switch (zoneId)
|
|
{
|
|
case TARGET_OWNER_GRAVEYARD:
|
|
return target->owner->game->graveyard;
|
|
case GRAVEYARD:
|
|
return target->owner->game->graveyard;
|
|
case OWNER_GRAVEYARD:
|
|
return target->owner->game->graveyard;
|
|
case TARGETED_PLAYER_GRAVEYARD:
|
|
if(source->playerTarget)
|
|
return source->playerTarget->game->graveyard;
|
|
else return source->controller()->game->graveyard;
|
|
|
|
case TARGET_OWNER_BATTLEFIELD:
|
|
return target->owner->game->inPlay;
|
|
case OWNER_BATTLEFIELD:
|
|
return target->owner->game->inPlay;
|
|
case TARGETED_PLAYER_BATTLEFIELD:
|
|
if(source->playerTarget)
|
|
return source->playerTarget->game->inPlay;
|
|
else return source->controller()->game->inPlay;
|
|
|
|
case TARGET_OWNER_HAND:
|
|
return target->owner->game->hand;
|
|
case HAND:
|
|
return target->owner->game->hand;
|
|
case OWNER_HAND:
|
|
return target->owner->game->hand;
|
|
case TARGETED_PLAYER_HAND:
|
|
if(source->playerTarget)
|
|
return source->playerTarget->game->hand;
|
|
else return source->controller()->game->hand;
|
|
|
|
case TARGET_OWNER_EXILE:
|
|
return target->owner->game->removedFromGame;
|
|
case EXILE:
|
|
return target->owner->game->removedFromGame;
|
|
case OWNER_EXILE:
|
|
return target->owner->game->removedFromGame;
|
|
case TARGETED_PLAYER_EXILE:
|
|
if(source->playerTarget)
|
|
return source->playerTarget->game->removedFromGame;
|
|
else return source->controller()->game->removedFromGame;
|
|
|
|
case TARGET_OWNER_LIBRARY:
|
|
return target->owner->game->library;
|
|
case OWNER_LIBRARY:
|
|
return target->owner->game->library;
|
|
case TARGETED_PLAYER_LIBRARY:
|
|
if(source->playerTarget)
|
|
return source->playerTarget->game->library;
|
|
else return source->controller()->game->library;
|
|
|
|
case TARGET_OWNER_STACK:
|
|
return target->owner->game->stack;
|
|
case OWNER_STACK:
|
|
return target->owner->game->stack;
|
|
case TARGETED_PLAYER_STACK:
|
|
if(source->playerTarget)
|
|
return source->playerTarget->game->stack;
|
|
else return source->controller()->game->stack;
|
|
|
|
case TARGET_OWNER_REVEAL:
|
|
return target->owner->game->reveal;
|
|
case REVEAL:
|
|
return target->owner->game->reveal;
|
|
case OWNER_REVEAL:
|
|
return target->owner->game->reveal;
|
|
case TARGETED_PLAYER_REVEAL:
|
|
if (source->playerTarget)
|
|
return source->playerTarget->game->reveal;
|
|
else return source->controller()->game->reveal;
|
|
|
|
case TARGET_OWNER_SIDEBOARD:
|
|
return target->owner->game->sideboard;
|
|
case SIDEBOARD:
|
|
return target->owner->game->sideboard;
|
|
case OWNER_SIDEBOARD:
|
|
return target->owner->game->sideboard;
|
|
case TARGETED_PLAYER_SIDEBOARD:
|
|
if (source->playerTarget)
|
|
return source->playerTarget->game->sideboard;
|
|
else return source->controller()->game->sideboard;
|
|
|
|
case TARGET_OWNER_COMMANDZONE:
|
|
return target->owner->game->commandzone;
|
|
case COMMANDZONE:
|
|
return target->owner->game->commandzone;
|
|
case OWNER_COMMANDZONE:
|
|
return target->owner->game->commandzone;
|
|
case TARGETED_PLAYER_COMMANDZONE:
|
|
if (source->playerTarget)
|
|
return source->playerTarget->game->commandzone;
|
|
else return source->controller()->game->commandzone;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
}
|
|
|
|
int MTGGameZone::zoneStringToId(string zoneName)
|
|
{
|
|
const char * strings[] = { "mygraveyard", "opponentgraveyard", "targetownergraveyard", "targetcontrollergraveyard",
|
|
"ownergraveyard", "graveyard","targetedpersonsgraveyard",
|
|
|
|
"myinplay", "opponentinplay", "targetownerinplay", "targetcontrollerinplay", "ownerinplay", "inplay","targetedpersonsinplay",
|
|
|
|
"mybattlefield", "opponentbattlefield", "targetownerbattlefield", "targetcontrollerbattlefield",
|
|
"ownerbattlefield", "battlefield","targetedpersonsbattlefield",
|
|
|
|
"myhand", "opponenthand", "targetownerhand", "targetcontrollerhand", "ownerhand", "hand","targetedpersonshand",
|
|
|
|
"mylibrary", "opponentlibrary", "targetownerlibrary", "targetcontrollerlibrary", "ownerlibrary", "library","targetedpersonslibrary",
|
|
|
|
"myremovedfromgame", "opponentremovedfromgame", "targetownerremovedfromgame",
|
|
"targetcontrollerremovedfromgame", "ownerremovedfromgame", "removedfromgame","targetedpersonsremovefromgame",
|
|
|
|
"myexile", "opponentexile", "targetownerexile", "targetcontrollerexile", "ownerexile", "exile","targetedpersonsexile",
|
|
|
|
"mystack", "opponentstack", "targetownerstack", "targetcontrollerstack", "ownerstack", "stack","targetedpersonsstack",
|
|
|
|
"myreveal", "opponentreveal", "targetownerreveal", "targetcontrollerreveal", "ownerreveal", "reveal","targetedpersonsreveal",
|
|
|
|
"mysideboard", "opponentsideboard", "targetownersideboard", "targetcontrollersideboard", "ownersideboard", "sideboard","targetedpersonssideboard",
|
|
|
|
"mycommandzone", "opponentcommandzone", "targetownercommandzone", "targetcontrollercommandzone", "ownercommandzone", "commandzone","targetedpersonscommandzone",
|
|
|
|
};
|
|
|
|
int values[] = { MY_GRAVEYARD, OPPONENT_GRAVEYARD, TARGET_OWNER_GRAVEYARD, TARGET_CONTROLLER_GRAVEYARD, OWNER_GRAVEYARD,
|
|
GRAVEYARD,TARGETED_PLAYER_GRAVEYARD,
|
|
|
|
MY_BATTLEFIELD, OPPONENT_BATTLEFIELD, TARGET_OWNER_BATTLEFIELD, TARGET_CONTROLLER_BATTLEFIELD,
|
|
OWNER_BATTLEFIELD, BATTLEFIELD,TARGETED_PLAYER_BATTLEFIELD,
|
|
|
|
MY_BATTLEFIELD, OPPONENT_BATTLEFIELD, TARGET_OWNER_BATTLEFIELD, TARGET_CONTROLLER_BATTLEFIELD,
|
|
OWNER_BATTLEFIELD, BATTLEFIELD,TARGETED_PLAYER_BATTLEFIELD,
|
|
|
|
MY_HAND, OPPONENT_HAND, TARGET_OWNER_HAND, TARGET_CONTROLLER_HAND, OWNER_HAND, HAND,TARGETED_PLAYER_HAND,
|
|
|
|
MY_LIBRARY, OPPONENT_LIBRARY, TARGET_OWNER_LIBRARY, TARGET_CONTROLLER_LIBRARY, OWNER_LIBRARY, LIBRARY,TARGETED_PLAYER_LIBRARY,
|
|
|
|
MY_EXILE, OPPONENT_EXILE, TARGET_OWNER_EXILE, TARGET_CONTROLLER_EXILE, OWNER_EXILE, EXILE,TARGETED_PLAYER_EXILE,
|
|
|
|
MY_EXILE, OPPONENT_EXILE, TARGET_OWNER_EXILE, TARGET_CONTROLLER_EXILE, OWNER_EXILE, EXILE,TARGETED_PLAYER_EXILE,
|
|
|
|
MY_STACK, OPPONENT_STACK, TARGET_OWNER_STACK, TARGET_CONTROLLER_STACK, OWNER_STACK, STACK,TARGETED_PLAYER_STACK,
|
|
|
|
MY_REVEAL, OPPONENT_REVEAL, TARGET_OWNER_REVEAL, TARGET_CONTROLLER_REVEAL, OWNER_REVEAL, REVEAL,TARGETED_PLAYER_REVEAL,
|
|
|
|
MY_SIDEBOARD, OPPONENT_SIDEBOARD, TARGET_OWNER_SIDEBOARD, TARGET_CONTROLLER_SIDEBOARD, OWNER_SIDEBOARD, SIDEBOARD,TARGETED_PLAYER_SIDEBOARD,
|
|
|
|
MY_COMMANDZONE, OPPONENT_COMMANDZONE, TARGET_OWNER_COMMANDZONE, TARGET_CONTROLLER_COMMANDZONE, OWNER_COMMANDZONE, COMMANDZONE,TARGETED_PLAYER_COMMANDZONE };
|
|
|
|
int max = sizeof(values) / sizeof *(values);
|
|
|
|
for (int i = 0; i < max; ++i)
|
|
{
|
|
if (zoneName.compare(strings[i]) == 0)
|
|
{
|
|
return values[i];
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
MTGGameZone * MTGGameZone::stringToZone(GameObserver *g, string zoneName, MTGCardInstance * source, MTGCardInstance * target)
|
|
{
|
|
return intToZone(g, zoneStringToId(zoneName), source, target);
|
|
}
|
|
|
|
ostream& MTGGameZone::toString(ostream& out) const
|
|
{
|
|
return out << "Unknown zone";
|
|
}
|
|
ostream& MTGLibrary::toString(ostream& out) const
|
|
{
|
|
return out << "Library " << owner->getDisplayName();
|
|
}
|
|
ostream& MTGGraveyard::toString(ostream& out) const
|
|
{
|
|
return out << "Graveyard " << owner->getDisplayName();
|
|
}
|
|
ostream& MTGHand::toString(ostream& out) const
|
|
{
|
|
return out << "Hand " << owner->getDisplayName();
|
|
}
|
|
ostream& MTGRemovedFromGame::toString(ostream& out) const
|
|
{
|
|
return out << "RemovedFromGame " << owner->getDisplayName();
|
|
}
|
|
ostream& MTGStack::toString(ostream& out) const
|
|
{
|
|
return out << "Stack " << owner->getDisplayName();
|
|
}
|
|
ostream& MTGInPlay::toString(ostream& out) const
|
|
{
|
|
return out << "InPlay " << owner->getDisplayName();
|
|
}
|
|
ostream& operator<<(ostream& out, const MTGGameZone& z)
|
|
{
|
|
for (int i = 0; i < z.nb_cards; i++)
|
|
{
|
|
out << z.cards[i]->getMTGId();
|
|
if(i < z.nb_cards - 1)
|
|
out << ",";
|
|
}
|
|
return out;
|
|
|
|
// return z.toString(out);
|
|
}
|
|
|
|
bool MTGGameZone::parseLine(const string& ss)
|
|
{
|
|
bool result = false;
|
|
string s = ss;
|
|
|
|
for (int i = 0; i < nb_cards; i++)
|
|
{
|
|
SAFE_DELETE( cards[i] );
|
|
}
|
|
cards.clear();
|
|
cardsMap.clear();
|
|
nb_cards = 0;
|
|
|
|
while(s.size())
|
|
{
|
|
size_t limiter = s.find(",");
|
|
MTGCard * card = 0;
|
|
string toFind;
|
|
if (limiter != string::npos)
|
|
{
|
|
toFind = trim(s.substr(0, limiter));
|
|
s = s.substr(limiter + 1);
|
|
}
|
|
else
|
|
{
|
|
toFind = trim(s);
|
|
s = "";
|
|
}
|
|
|
|
card = MTGCollection()->getCardByName(toFind);
|
|
int id = Rules::getMTGId(toFind);
|
|
|
|
if (card)
|
|
{
|
|
/* For the moment we add the card directly in the final zone.
|
|
This is not the normal way and this prevents to resolve spells.
|
|
We'll need a fusion operation afterward to cast relevant spells */
|
|
MTGCardInstance * newCard = NEW MTGCardInstance(card, owner->game);
|
|
addCard(newCard);
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
if(toFind == "*")
|
|
nb_cards++;
|
|
else if ( id < 0 )
|
|
{
|
|
// For the moment, we create a dummy Token to please the testsuite
|
|
Token* myToken = new Token(id);
|
|
addCard(myToken);
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
DebugTrace("Card unfound " << toFind << " " << id);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
ostream& operator<<(ostream& out, const MTGPlayerCards& z)
|
|
{
|
|
if(z.library->cards.size()) {
|
|
out << "library=";
|
|
out << *(z.library) << endl;
|
|
}
|
|
if(z.battlefield->cards.size()) {
|
|
out << "inplay=";
|
|
out << *(z.battlefield) << endl;
|
|
}
|
|
if(z.graveyard->cards.size()) {
|
|
out << "graveyard=";
|
|
out << *(z.graveyard) << endl;
|
|
}
|
|
if(z.hand->cards.size()) {
|
|
out << "hand=";
|
|
out << *(z.hand) << endl;
|
|
}
|
|
if(z.removedFromGame->cards.size()) {
|
|
out << "exile=";
|
|
out << *(z.hand) << endl;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
bool MTGPlayerCards::parseLine(const string& s)
|
|
{
|
|
size_t limiter = s.find("=");
|
|
if (limiter == string::npos) limiter = s.find(":");
|
|
string areaS;
|
|
if (limiter != string::npos)
|
|
{
|
|
areaS = s.substr(0, limiter);
|
|
if (areaS.compare("graveyard") == 0)
|
|
{
|
|
graveyard->parseLine(s.substr(limiter+1));
|
|
return true;
|
|
}
|
|
else if (areaS.compare("library") == 0)
|
|
{
|
|
library->parseLine(s.substr(limiter+1));
|
|
return true;
|
|
}
|
|
else if (areaS.compare("hand") == 0)
|
|
{
|
|
hand->parseLine(s.substr(limiter+1));
|
|
return true;
|
|
}
|
|
else if (areaS.compare("inplay") == 0 || areaS.compare("battlefield") == 0)
|
|
{
|
|
battlefield->parseLine(s.substr(limiter+1));
|
|
return true;
|
|
}
|
|
else if (areaS.compare("removedfromgame") == 0 || areaS.compare("exile") == 0)
|
|
{
|
|
removedFromGame->parseLine(s.substr(limiter+1));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|