Files
wagic/projects/mtg/src/ManaCost.cpp
T
Eduardo ab1fbaa806 New game modes, bug fixes in primitives, improving AI, new planeswalkers type rule
New rules based on vanguard, 3 new random game modes, one is tribal and one uses any card in the game.

Several corrections and bugs fixes. Cards with x in their cost and that can target any player used to crash the game.

Teaching AI new things and changing values of efficiency.

You can have multiple "Jace" planeswalkers, you can't have two of the exact same name (no two Jace, the mind sculptor).
2019-04-14 19:57:01 -05:00

1317 lines
38 KiB
C++

#include "PrecompiledHeader.h"
#include "ManaCost.h"
#include "ManaCostHybrid.h"
#include "ExtraCost.h"
#include "TargetChooser.h"
#include "Targetable.h"
#include "Player.h"
#include "WEvent.h"
#include "MTGAbility.h"
#include "AllAbilities.h"
#include "iterator"
SUPPORT_OBJECT_ANALYTICS(ManaCost)
ManaCost * ManaCost::parseManaCost(string s, ManaCost * _manaCost, MTGCardInstance * c)
{
ManaCost * manaCost;
GameObserver* g = c?c->getObserver():NULL;
if (_manaCost)
{
manaCost = _manaCost;
}
else
{
manaCost = NEW ManaCost();
}
manaCost->xColor = -1;
int state = 0;
size_t start = 0;
size_t end = 0;
while (!s.empty() && state != -1)
{
switch (state)
{
case 0:
start = s.find_first_of("{");
if(s.find_first_of("{") != string::npos && start > 0)
{
string value = s.substr(start -1,end);
if(value == "n{")//"restrictio n{m orbid} would read the n{m as {m} millcost
return manaCost;
else if(value == "e{")//"variable{ test fix
return manaCost;
}
if (start == string::npos)
{
return manaCost;
}
else
{
state = 1;
}
break;
case 1:
end = s.find_first_of("}");
if (end == string::npos)
{
state = -1;
}
else
{
string value = s.substr(start + 1, end - 1 - start);
if (value == "u")
{
manaCost->add(Constants::MTG_COLOR_BLUE, 1);
}
else if (value == "b")
{
manaCost->add(Constants::MTG_COLOR_BLACK, 1);
}
else if (value == "w")
{
manaCost->add(Constants::MTG_COLOR_WHITE, 1);
}
else if (value == "g")
{
manaCost->add(Constants::MTG_COLOR_GREEN, 1);
}
else if (value == "r")
{
manaCost->add(Constants::MTG_COLOR_RED, 1);
}
else
{
//Parse target for extraCosts
TargetChooserFactory tcf(g);
TargetChooser * tc = NULL;
size_t target_start = value.find("(");
size_t target_end = value.find(")");
if (target_start != string::npos && target_end != string::npos)
{
string target = value.substr(target_start + 1, target_end - 1 - target_start);
tc = tcf.createTargetChooser(target, c);
}
//switch on the first letter. If two costs share their first letter, add an "if" within the switch
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
switch (value[0])
{
case 'x':
if(value == "x")
{
manaCost->x();
}
else
{
vector<string>colorSplit = parseBetween(value,"x:"," ",false);
if(colorSplit.size())
{
int color = -1;
const string ColorStrings[] = { Constants::kManaColorless, Constants::kManaGreen, Constants::kManaBlue, Constants::kManaRed, Constants::kManaBlack, Constants::kManaWhite };
for (unsigned int i = 0; i < sizeof(ColorStrings)/sizeof(ColorStrings[0]); ++i)
{
if (s.find(ColorStrings[i]) != string::npos)
{
color = i;
}
}
manaCost->specificX(color);
}
}
break;
case 'v':
if (value.find("value:") != string::npos) {
vector<string> splitParsedVar = parseBetween(value, "value:", " ", false);
WParsedInt* res = NEW WParsedInt(splitParsedVar[1], NULL, c);
manaCost->add(Constants::MTG_COLOR_ARTIFACT, res->getValue());
SAFE_DELETE(res);
}
break;
case 't': //Tap
if (value == "t")
{
manaCost->addExtraCost(NEW TapCost);
}
else
{
manaCost->addExtraCost(NEW TapTargetCost(tc));
}
break;
case 's':
if (value.find("s2l") != string::npos)
{ //Send To Library Cost (move from anywhere to Library)
manaCost->addExtraCost(NEW ToLibraryCost(tc));
}
else if (value.find("s2g") != string::npos)
{ //Send to Graveyard Cost (move from anywhere to Graveyard)
manaCost->addExtraCost(NEW ToGraveCost(tc));
}
else if (value.find("saclands") != string::npos)
{ //Sac all lands
manaCost->addExtraCost(NEW SacLandsCost(tc));
}
else
{ //Sacrifice
manaCost->addExtraCost(NEW SacrificeCost(tc));
}
break;
case 'e':
if (value == "emerge")
{
if (!tc)
tc = tcf.createTargetChooser("creature|mybattlefield", c);
manaCost->addExtraCost(NEW Offering(tc,true));
}
else if(value.substr(0,2) == "e:")
{//Energy Cost
vector<string>valSplit = parseBetween(value,"e:"," ",false);
if (valSplit.size()) {
WParsedInt* energytopay = NEW WParsedInt(valSplit[1], NULL, c);
manaCost->addExtraCost(NEW EnergyCost(energytopay->getValue()));
SAFE_DELETE(energytopay);
}
}
else
//Exile
manaCost->addExtraCost(NEW ExileTargetCost(tc));
break;
case 'h': //bounce (move to Hand)
manaCost->addExtraCost(NEW BounceTargetCost(tc));
break;
case 'l':
if (value == "l2e")
{ //Mill to exile yourself as a cost (Library 2 Exile)
manaCost->addExtraCost(NEW MillExileCost(tc));
}
else if (value == "l")
{ //Life cost
manaCost->addExtraCost(NEW LifeCost(tc));
}
else
{ //Specific Life cost
vector<string>valSplit = parseBetween(value,"l:"," ",false);
if (valSplit.size()) {
WParsedInt* lifetopay = NEW WParsedInt(valSplit[1], NULL, c);
manaCost->addExtraCost(NEW SpecificLifeCost(tc,lifetopay->getValue()));
SAFE_DELETE(lifetopay);
}
}
break;
case 'd': //DiscardRandom cost
if (value.find("delve") != string::npos)
{
if(!tc)
tc = tcf.createTargetChooser("*|mygraveyard", c);
manaCost->addExtraCost(NEW Delve(tc));
}
else if (value == "d")
{
manaCost->addExtraCost(NEW DiscardRandomCost(tc));
}
else
{
manaCost->addExtraCost(NEW DiscardCost(tc));
}
break;
case 'm': //Mill yourself as a cost
{
if (value == "mycost")
{
if(c && c->model)
manaCost->add(c->model->data->getManaCost());
else
break;
}
else
manaCost->addExtraCost(NEW MillCost(tc));
}
break;
case 'n': //return unblocked attacker cost
{
TargetChooserFactory tcf(g);
tc = tcf.createTargetChooser("creature|myBattlefield", c);
manaCost->addExtraCost(NEW Ninja(tc));
break;
}
case 'k': //kill offering
{
TargetChooserFactory tcf(g);
if (value == "kgoblin")
{
tc = tcf.createTargetChooser("creature[goblin]|myBattlefield", c);
}
else if (value == "kfox")
{
tc = tcf.createTargetChooser("creature[fox]|myBattlefield", c);
}
else if (value == "kmoonfolk")
{
tc = tcf.createTargetChooser("creature[moonfolk]|myBattlefield", c);
}
else if (value == "krat")
{
tc = tcf.createTargetChooser("creature[rat]|myBattlefield", c);
}
else if (value == "ksnake")
{
tc = tcf.createTargetChooser("creature[snake]|myBattlefield", c);
}
//TODO iterate subtypes of creatures
manaCost->addExtraCost(NEW Offering(tc));
break;
}
case 'p' :
{
SAFE_DELETE(tc);
size_t start = value.find("(");
size_t end = value.rfind(")");
string manaType = value.substr(start + 1, end - start - 1);
manaCost->addExtraCost(NEW LifeorManaCost(NULL,manaType));
break;
}
case 'i' :
if(value == "improvise")
{
if(!tc)
tc = tcf.createTargetChooser("artifact[-tapped]|myBattlefield", c);
manaCost->addExtraCost(NEW Improvise(tc));
}
else
{
SAFE_DELETE(tc);
manaCost->add(0,1);
manaCost->addExtraCost(NEW SnowCost);
}
break;
case 'q':
if(value == "q")
{
manaCost->addExtraCost(NEW UnTapCost);
}
else
{
manaCost->addExtraCost(NEW UnTapTargetCost(tc));
}
break;
case 'c': //Counters or cycle
{
if (value.find("convoke") != string::npos)
{
if (!tc)
tc = tcf.createTargetChooser("creature|mybattlefield", c);
manaCost->addExtraCost(NEW Convoke(tc));
}
else if(value == "chosencolor")
{
if(c)
manaCost->add(c->chooseacolor, 1);
}
else if(value == "cycle")
{
manaCost->addExtraCost(NEW CycleCost(tc));
}
else if(value.substr(0,4) == "crew")
{//tap crew
manaCost->addExtraCost(NEW TapTargetCost(tc,true));
}
else if(value.find("(") != string::npos)
{
size_t counter_start = value.find("(");
size_t counter_end = value.find(")", counter_start);
AbilityFactory abf(g);
string counterString = value.substr(counter_start + 1, counter_end - counter_start - 1);
Counter * counter = abf.parseCounter(counterString, c);
size_t separator = value.find(",", counter_start);
size_t separator2 = string::npos;
if (separator != string::npos)
{
separator2 = value.find(",", counter_end + 1);
}
SAFE_DELETE(tc);
size_t target_start = string::npos;
if (separator2 != string::npos)
{
target_start = value.find(",", counter_end + 1);
}
size_t target_end = value.length();
if (target_start != string::npos && target_end != string::npos)
{
string target = value.substr(target_start + 1, target_end - 1 - target_start);
tc = tcf.createTargetChooser(target, c);
}
manaCost->addExtraCost(NEW CounterCost(counter, tc));
break;
}
else if(value == "c")
{
manaCost->add(Constants::MTG_COLOR_WASTE, 1);
break;
}
break;
}
default: //uncolored cost and hybrid costs and special cost
{
if(value == "unattach")
{
manaCost->addExtraCost(NEW UnattachCost(c));
break;
}
int intvalue = atoi(value.c_str());
int colors[2];
int values[2];
if (intvalue < 10 && value.size() > 1)
{
for (int i = 0; i < 2; i++)
{
char c = value[i];
if (c >= '0' && c <= '9')
{
colors[i] = Constants::MTG_COLOR_ARTIFACT;
values[i] = c - '0';
}
else
{
for (int j = 0; j < Constants::NB_Colors; j++)
{
if (c == Constants::MTGColorChars[j])
{
colors[i] = j;
values[i] = 1;
}
}
}
}
if (values[0] > 0 || values[1] > 0)
manaCost->addHybrid(colors[0], values[0], colors[1], values[1]);
}
else
{
manaCost->add(Constants::MTG_COLOR_ARTIFACT, intvalue);
}
break;
}
}
}
s = s.substr(end + 1);
state = 0;
}
break;
default:
break;
}
}
return manaCost;
}
ManaCost::ManaCost()
{
init();
}
ManaCost::ManaCost(vector<int16_t>& _cost, int nb_elems)
{
init();
for (int i = 0; i < nb_elems; i++)
{
cost[_cost[i * 2]] = _cost[i * 2 + 1];
}
}
// pointer copy constructor
ManaCost::ManaCost(ManaCost * manaCost)
{
init();
if ( !manaCost )
return;
for (int i = 0; i <= Constants::NB_Colors; i++)
{
cost[i] = manaCost->getCost(i);
}
hybrids = manaCost->hybrids;
kicker = NEW ManaCost(manaCost->kicker);
if (kicker)
kicker->isMulti = manaCost->isMulti;
Retrace = NEW ManaCost( manaCost->Retrace );
BuyBack = NEW ManaCost( manaCost->BuyBack );
alternative = NEW ManaCost( manaCost->alternative );
FlashBack = NEW ManaCost( manaCost->FlashBack );
morph = NEW ManaCost( manaCost->morph );
suspend = NEW ManaCost( manaCost->suspend );
Bestow = NEW ManaCost(manaCost->Bestow);
extraCosts = NULL;
if (manaCost->extraCosts)
{
extraCosts = manaCost->extraCosts->clone();
}
manaUsedToCast = NULL;
xColor = manaCost->xColor;
}
// Copy Constructor
ManaCost::ManaCost(const ManaCost& manaCost)
#ifdef TRACK_OBJECT_USAGE
: InstanceCounter<ManaCost>(manaCost)
#endif
{
for (int i = 0; i <= Constants::NB_Colors; i++)
{
cost.push_back(manaCost.cost[i]);
}
hybrids = manaCost.hybrids;
// make new copies of the pointers for the deep copy
kicker = NEW ManaCost( manaCost.kicker );
Retrace = NEW ManaCost( manaCost.Retrace );
BuyBack = NEW ManaCost( manaCost.BuyBack );
alternative = NEW ManaCost( manaCost.alternative );
FlashBack = NEW ManaCost( manaCost.FlashBack );
morph = NEW ManaCost( manaCost.morph );
suspend = NEW ManaCost( manaCost.suspend );
Bestow = NEW ManaCost(manaCost.Bestow);
extraCosts = NULL;
if (manaCost.extraCosts)
{
extraCosts = manaCost.extraCosts->clone();
}
manaUsedToCast = NULL;
xColor = manaCost.xColor;
}
// operator=
ManaCost & ManaCost::operator= (const ManaCost & manaCost)
{
if ( this != &manaCost )
{
for (int i = 0; i < Constants::NB_Colors; i++)
cost[i] = manaCost.cost[i];
hybrids = manaCost.hybrids;
extraCosts = manaCost.extraCosts;
kicker = manaCost.kicker;
Retrace = manaCost.Retrace;
BuyBack = manaCost.BuyBack;
alternative = manaCost.alternative;
FlashBack = manaCost.FlashBack;
morph = manaCost.morph;
suspend = manaCost.suspend;
Bestow = manaCost.Bestow;
manaUsedToCast = manaCost.manaUsedToCast;
xColor = manaCost.xColor;
}
return *this;
}
ManaCost::~ManaCost()
{
SAFE_DELETE(extraCosts);
SAFE_DELETE(kicker);
SAFE_DELETE(alternative);
SAFE_DELETE(BuyBack);
SAFE_DELETE(FlashBack);
SAFE_DELETE(Retrace);
SAFE_DELETE(morph);
SAFE_DELETE(suspend);
SAFE_DELETE(Bestow);
SAFE_DELETE(manaUsedToCast);
cost.erase(cost.begin() ,cost.end());
}
void ManaCost::x()
{
if (cost.size() <= (size_t)Constants::NB_Colors)
{
return;
}
cost[Constants::NB_Colors] = 1;
}
int ManaCost::hasX()
{
if (cost.size() <= (size_t)Constants::NB_Colors)
{
return 0;
}
if (xColor > 0)
return 0;
return cost[Constants::NB_Colors];
}
void ManaCost::specificX(int color)
{
if (cost.size() <= (size_t)Constants::NB_Colors)
{
return;
}
xColor = color;
cost[Constants::NB_Colors] = 1;
}
int ManaCost::hasSpecificX()
{
if (cost.size() <= (size_t)Constants::NB_Colors)
{
return 0;
}
if(xColor > 0)
return cost[Constants::NB_Colors];
return 0;
}
int ManaCost::hasAnotherCost()
{
if (cost.size() <= (size_t)Constants::NB_Colors)
{
DebugTrace("Seems ManaCost was not properly initialized");
return 0;
}
int result = 0;
if(kicker)
result = 1;
//kicker is the only one ai knows for now, later hasAnotherCost() can be used to determine other cost types.
if(Retrace || BuyBack || alternative || FlashBack || morph || suspend || Bestow)
result = 1;
return result;
}
void ManaCost::init()
{
int i;
cost.erase(cost.begin() ,cost.end());
for (i = 0; i <= Constants::NB_Colors; i++)
{
cost.push_back(0);
}
extraCosts = NULL;
kicker = NULL;
alternative = NULL;
BuyBack = NULL;
FlashBack = NULL;
Retrace = NULL;
morph = NULL;
suspend = NULL;
Bestow = NULL;
manaUsedToCast = NULL;
isMulti = false;
xColor = -1;
}
void ManaCost::resetCosts()
{
int i;
cost.erase(cost.begin() ,cost.end());
for (i = 0; i <= Constants::NB_Colors; i++)
{
cost.push_back(0);
}
SAFE_DELETE(extraCosts);
SAFE_DELETE(kicker);
SAFE_DELETE(alternative);
SAFE_DELETE(BuyBack);
SAFE_DELETE(FlashBack);
SAFE_DELETE(Retrace);
SAFE_DELETE(morph);
SAFE_DELETE(suspend);
SAFE_DELETE(Bestow);
}
void ManaCost::copy(ManaCost * _manaCost)
{
if (!_manaCost)
return;
cost.erase(cost.begin() ,cost.end());
for (int i = 0; i <= Constants::NB_Colors; i++)
{
cost.push_back(_manaCost->getCost(i));
}
hybrids = _manaCost->hybrids;
SAFE_DELETE(extraCosts);
if (_manaCost->extraCosts)
{
extraCosts = _manaCost->extraCosts->clone();
}
SAFE_DELETE(kicker);
if (_manaCost->kicker)
{
kicker = NEW ManaCost();
kicker->copy(_manaCost->kicker);
kicker->isMulti = _manaCost->kicker->isMulti;
}
SAFE_DELETE(alternative);
if (_manaCost->alternative)
{
alternative = NEW ManaCost();
alternative->copy(_manaCost->alternative);
}
SAFE_DELETE(BuyBack);
if (_manaCost->BuyBack)
{
BuyBack = NEW ManaCost();
BuyBack->copy(_manaCost->BuyBack);
}
SAFE_DELETE(FlashBack);
if (_manaCost->FlashBack)
{
FlashBack = NEW ManaCost();
FlashBack->copy(_manaCost->FlashBack);
}
SAFE_DELETE(Retrace);
if (_manaCost->Retrace)
{
Retrace = NEW ManaCost();
Retrace->copy(_manaCost->Retrace);
}
SAFE_DELETE(morph);
if (_manaCost->morph)
{
morph = NEW ManaCost();
morph->copy(_manaCost->morph);
}
SAFE_DELETE(suspend);
if (_manaCost->suspend)
{
suspend = NEW ManaCost();
suspend->copy(_manaCost->suspend);
}
SAFE_DELETE(Bestow);
if (_manaCost->Bestow)
{
Bestow = NEW ManaCost();
Bestow->copy(_manaCost->Bestow);
}
xColor = _manaCost->xColor;
}
void ManaCost::changeCostTo(ManaCost * _manaCost)
{
if (!_manaCost)
return;
cost.erase(cost.begin() ,cost.end());
for (int i = 0; i <= Constants::NB_Colors; i++)
{
cost.push_back(_manaCost->getCost(i));
}
hybrids = _manaCost->hybrids;
SAFE_DELETE(extraCosts);
if (_manaCost->extraCosts)
{
extraCosts = _manaCost->extraCosts->clone();
}
xColor = _manaCost->xColor;
}
int ManaCost::getCost(int color)
{
if (cost.size() <= (size_t)color)
{
DebugTrace("in GetCost Seems ManaCost was not properly initialized");
return 0;
}
return cost[color];
}
int ManaCost::getManaSymbols(int color)
{
int result = cost[color];
for (size_t i = 0; i < hybrids.size(); ++i)
{
result += hybrids[i].getManaSymbols(color);
}
if (extraCosts && extraCosts->costs.size())
{
for (size_t i = 0; i < extraCosts->costs.size(); ++i)
{
LifeorManaCost * phyrexianMana = dynamic_cast<LifeorManaCost*>(extraCosts->costs[i]);
if (phyrexianMana)
{
result += phyrexianMana->getManaCost()->getManaSymbols(color);
}
}
}
return result;
}
int ManaCost::getManaSymbolsHybridMerged(int color)
{
int result = cost[color];
for (size_t i = 0; i < hybrids.size(); ++i)
{
result += hybrids[i].getManaSymbolsHybridMerged(color);
}
if (extraCosts && extraCosts->costs.size())
{
for (size_t i = 0; i < extraCosts->costs.size(); ++i)
{
LifeorManaCost * phyrexianMana = dynamic_cast<LifeorManaCost*>(extraCosts->costs[i]);
if (phyrexianMana)
{
result += phyrexianMana->getManaCost()->getManaSymbolsHybridMerged(color);
}
}
}
return result;
}
int ManaCost::countHybridsNoPhyrexian()
{
int result = 0;
for (size_t i = 0; i < hybrids.size(); i++)
result ++;
return result;
}
void ManaCost::removeHybrid(ManaCost * _manaCost)
{
if (!_manaCost)
return;
vector<int> colors;
int match = 0;
for(int j = 0; j < 7; j++)
{//populate colors values
colors.push_back(_manaCost->getCost(j));
}
for (size_t i = 0; i < hybrids.size(); i++)
{
for(int j = 0; j < 7; j++)
{
if(colors[j])
{
if(hybrids[i].hasColor(j))
{
hybrids[i].reduceValue(j, colors[j]);
colors[j] -= 1;
match++;
}
}
}
}
return;
}
int ManaCost::parseManaSymbol(char symbol)
{
switch (symbol)
{
case 'g':
return Constants::MTG_COLOR_GREEN;
case 'u':
return Constants::MTG_COLOR_BLUE;
case 'r':
return Constants::MTG_COLOR_RED;
case 'b':
return Constants::MTG_COLOR_BLACK;
case 'w':
return Constants::MTG_COLOR_WHITE;
}
DebugTrace( "Failed to parse mana symbol" );
return -1;
}
ManaCostHybrid * ManaCost::getHybridCost(unsigned int i)
{
if (hybrids.size() <= i)
return NULL;
return &hybrids[i];
}
ExtraCost * ManaCost::getExtraCost(unsigned int i)
{
if(extraCosts && extraCosts->costs.size())
{
if (extraCosts->costs.size() <= i)
return NULL;
return extraCosts->costs[i];
}
return NULL;
}
int ManaCost::hasColor(int color)
{
if (getCost(color))
return 1;
for (size_t i = 0; i < hybrids.size(); i++)
{
if (hybrids[i].hasColor(color))
return 1;
}
return 0;
}
int ManaCost::isNull()
{
if (getConvertedCost())
return 0;
if (extraCosts)
return 0;
return 1;
}
int ManaCost::getConvertedCost()
{
int result = 0;
for (int i = 0; i < Constants::NB_Colors; i++)
{
result += cost[i];
}
for (size_t i = 0; i < hybrids.size(); i++)
{
result += hybrids[i].getConvertedCost();
}
if (extraCosts && extraCosts->costs.size())
{
for (unsigned int i = 0; i < extraCosts->costs.size(); i++)
{
ExtraCost * pMana = dynamic_cast<LifeorManaCost*>(extraCosts->costs[i]);
if (pMana)
result++;
//snow cost???
ExtraCost * sMana = dynamic_cast<SnowCost*>(extraCosts->costs[i]);
if (sMana)
result++;
}
}
return result;
}
int ManaCost::remove(int color, int value)
{
assert (value >= 0);
int16_t toRemove = min(cost[color], (int16_t)value);
cost[color] -= toRemove;
return 1;
}
int ManaCost::add(int color, int value)
{
if (value < 0)
value = 0;
cost[color] += value;
return 1;
}
int ManaCost::add(ManaCost * _cost)
{
if (!_cost)
return 0;
for ( int i = 0; i < Constants::NB_Colors; i++)
{
cost[i] += _cost->getCost(i);
}
std::copy(_cost->hybrids.begin(), _cost->hybrids.end(), std::back_inserter(hybrids));
return 1;
}
int ManaCost::remove(ManaCost * _cost)
{
if (!_cost)
return 0;
for ( int i = 0; i < Constants::NB_Colors; i++)
{
int16_t toRemove = min(cost[i], (int16_t)_cost->getCost(i)); //we don't want to be negative
cost[i] -= toRemove;
assert(cost[i] >= 0);
}
return 1;
}
int ManaCost::removeAll(int color)
{
cost[color] = 0;
return 1;
}
int ManaCost::addHybrid(int c1, int v1, int c2, int v2)
{
hybrids.push_back(ManaCostHybrid(c1, v1, c2, v2));
return hybrids.size();
}
int ManaCost::addExtraCost(ExtraCost * _cost)
{
if (!extraCosts)
extraCosts = NEW ExtraCosts();
extraCosts->costs.push_back(_cost);
return 1;
}
int ManaCost::addExtraCosts(ExtraCosts *_ecost)
{
if(!_ecost)
{
extraCosts = NULL;
return 1;
}
if (!extraCosts)
extraCosts = NEW ExtraCosts();
for(size_t i = 0; i < _ecost->costs.size(); i++)
extraCosts->costs.push_back(_ecost->costs[i]->clone());
return 1;
}
int ManaCost::isExtraPaymentSet()
{
if (!extraCosts)
return 1;
return extraCosts->isPaymentSet();
}
int ManaCost::canPayExtra()
{
if (!extraCosts)
return 1;
return extraCosts->canPay();
}
int ManaCost::doPayExtra()
{
if (!extraCosts)
return 0;
return extraCosts->doPay(); //TODO reset ?
}
int ManaCost::setExtraCostsAction(MTGAbility * action, MTGCardInstance * card)
{
if (extraCosts)
extraCosts->setAction(action, card);
return 1;
}
int ManaCost::pay(ManaCost * _cost)
{
int result = MANA_PAID;
ManaCost * toPay = NEW ManaCost();
toPay->copy(_cost);
ManaCost * diff = Diff(toPay);
for (int i = 0; i < Constants::NB_Colors; i++)
{
cost[i] = diff->getCost(i);
}
delete diff;
delete toPay;
return result;
//TODO return 0 if can't afford the cost!
}
//return 1 if _cost can be paid with current data, 0 otherwise
int ManaCost::canAfford(ManaCost * _cost)
{
ManaCost * diff = Diff(_cost);
int positive = diff->isPositive();
delete diff;
if (positive)
{
return 1;
}
return 0;
}
int ManaCost::isPositive()
{
for (int i = 0; i < Constants::NB_Colors; i++)
{
if (cost[i] < 0)
{
return 0;
}
}
return 1;
}
void ManaCost::randomDiffHybrids(ManaCost * _cost, std::vector<int16_t>& diff)
{
for (size_t i = 0; i < _cost->hybrids.size(); i++)
{
ManaCostHybrid& h = _cost->hybrids[i];
diff[h.color1 * 2 + 1] -= h.value1;
}
}
/**
starting from the end of the array (diff)
*/
int ManaCost::tryToPayHybrids(const std::vector<ManaCostHybrid>& _hybrids, int _nbhybrids, std::vector<int16_t>& diff)
{
if (!_nbhybrids)
return 1;
int result = 0;
const ManaCostHybrid& h = _hybrids[_nbhybrids - 1];
if (diff[h.color1 * 2 + 1] >= h.value1)
{
diff[h.color1 * 2 + 1] -= h.value1;
result = tryToPayHybrids(_hybrids, _nbhybrids - 1, diff);
if (result)
return 1;
diff[h.color1 * 2 + 1] += h.value1;
}
if (diff[h.color2 * 2 + 1] >= h.value2)
{
diff[h.color2 * 2 + 1] -= h.value2;
result = tryToPayHybrids(_hybrids, _nbhybrids - 1, diff);
if (result)
return 1;
diff[h.color2 * 2 + 1] += h.value2;
}
return 0;
}
//compute the difference between two mana costs
ManaCost * ManaCost::Diff(ManaCost * _cost)
{
if (!_cost)
return NEW ManaCost(*this); //diff with null is equivalent to diff with 0
vector<int16_t> diff;
diff.resize((Constants::NB_Colors + 1) * 2);
diff[Constants::NB_Colors * 2] = Constants::NB_Colors;
for (int i = 0; i < Constants::NB_Colors; i++)
{
diff[i * 2] = i;
diff[i * 2 + 1] = cost[i] - _cost->getCost(i);
}
int hybridResult = tryToPayHybrids(_cost->hybrids, _cost->hybrids.size(), diff);
if (!hybridResult)
randomDiffHybrids(_cost, diff);
//Colorless mana, special case
int colorless_idx = Constants::MTG_COLOR_ARTIFACT * 2 + 1;
if (diff[colorless_idx] < 0)
{
for (int i = 0; i < Constants::NB_Colors; i++)
{
if (diff[i * 2 + 1] > 0)
{
if (diff[i * 2 + 1] + diff[colorless_idx] > 0)
{
diff[i * 2 + 1] += diff[colorless_idx];
diff[colorless_idx] = 0;
break;
}
else
{
diff[colorless_idx] += diff[i * 2 + 1];
diff[i * 2 + 1] = 0;
}
}
}
}
//Cost X
if (_cost->hasX())
{
diff[Constants::NB_Colors * 2 + 1] = 0;
for (int i = 0; i < Constants::NB_Colors; i++)
{
if (diff[i * 2 + 1] > 0)
{
diff[Constants::NB_Colors * 2 + 1] += diff[i * 2 + 1];
diff[i * 2 + 1] = 0;
}
}
}
//cost x where x is specific.
if (_cost->hasSpecificX())
{
diff[Constants::NB_Colors * 2 + 1] = 0;
if (diff[_cost->xColor * 2 + 1] > 0)
{
diff[Constants::NB_Colors * 2 + 1] += diff[_cost->xColor * 2 + 1];
diff[_cost->xColor * 2 + 1] = 0;
}
}
ManaCost * result = NEW ManaCost(diff, Constants::NB_Colors + 1);
return result;
}
string ManaCost::toString()
{
ostringstream oss;
for (int i = 0; i <= Constants::NB_Colors; i++)
{
if (cost[i])
{
if ( i == Constants::MTG_COLOR_ARTIFACT)
oss << "{" << getCost(i) << "}";
else
for (int colorCount = 0; colorCount < getCost(i); colorCount++ )
oss << "{" << Constants::MTGColorChars[i] << "}";
}
}
for (size_t i = 0; i < hybrids.size(); i++)
{
oss << hybrids[i];
}
return oss.str();
}
#ifdef WIN32
void ManaCost::Dump()
{
//if(this->getConvertedCost())//uncomment when this is far too loud and clutters your output making other traces pointless.
//{
DebugTrace( "\n===ManaCost===" );
DebugTrace( this->toString() );
DebugTrace( "\n=============" );
//}
}
#endif
ostream& operator<<(ostream& out, ManaCost& m)
{
return out << m.toString();
}
ostream& operator<<(ostream& out, ManaCost* m)
{
return out << m->toString();
}
ostream& operator<<(ostream& out, ManaCost m)
{
return out << m.toString();
}
void ManaPool::Empty()
{
SAFE_DELETE(extraCosts);
SAFE_DELETE(kicker);
SAFE_DELETE(alternative);
SAFE_DELETE(BuyBack);
SAFE_DELETE(FlashBack);
SAFE_DELETE(Retrace);
SAFE_DELETE(morph);
SAFE_DELETE(suspend);
SAFE_DELETE(Bestow);
SAFE_DELETE(manaUsedToCast);
init();
WEvent * e = NEW WEventEmptyManaPool(this);
player->getObserver()->receiveEvent(e);
}
ManaPool::ManaPool(Player * player) :
ManaCost(), player(player)
{
}
ManaPool::ManaPool(ManaCost * _manaCost, Player * player) :
ManaCost(_manaCost), player(player)
{
}
int ManaPool::remove(int color, int value)
{
int result = ManaCost::remove(color, value);
for (int i = 0; i < value; ++i)
{
WEvent * e = NEW WEventConsumeMana(color, this);
player->getObserver()->receiveEvent(e);
}
return result;
}
int ManaPool::add(int color, int value, MTGCardInstance * source, bool extra)
{
if (color == Constants::MTG_COLOR_ARTIFACT)
color = Constants::MTG_COLOR_WASTE;
int result = ManaCost::add(color, value);
for (int i = 0; i < value; ++i)
{
WEvent * e = NEW WEvent;
if(extra)
e = NEW WEventEngageManaExtra(color, source, this);
else
e = NEW WEventEngageMana(color, source, this);
player->getObserver()->receiveEvent(e);
}
return result;
}
int ManaPool::add(ManaCost * _cost, MTGCardInstance * source)
{
if (!_cost)
return 0;
//while colorless is still exactly the same, there are now cards that require
//true colorless mana, ei:eldrazi. so whenever we add mana, we now replace it with the
//new type. keeping the old type intact for payment methods {1}{c} ....
int replaceArtifact = _cost->getCost(Constants::MTG_COLOR_ARTIFACT);
if (replaceArtifact)
{
_cost->add(Constants::MTG_COLOR_WASTE, replaceArtifact);
_cost->remove(Constants::MTG_COLOR_ARTIFACT, replaceArtifact);
}
int result = ManaCost::add(_cost);
for (int i = 0; i < Constants::NB_Colors; i++)
{
for (int j = 0; j < _cost->getCost(i); j++)
{
WEvent * e = NEW WEventEngageMana(i, source, this);
player->getObserver()->receiveEvent(e);
}
}
return result;
}
int ManaPool::pay(ManaCost * _cost)
{
vector<int> current;
for (int i = 0; i < Constants::NB_Colors; i++)
{
current.push_back(cost[i]);
}
int result = ManaCost::pay(_cost);
for (int i = 0; i < Constants::NB_Colors; i++)
{
int value = current[i] - cost[i];
for (int j = 0; j < value; j++)
{
WEvent * e = NEW WEventConsumeMana(i, this);
player->getObserver()->receiveEvent(e);
}
}
current.erase(current.begin(),current.end());
return result;
}