Files
wagic/projects/mtg/src/ManaCost.cpp

725 lines
20 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"
ManaCost * ManaCost::parseManaCost(string s, ManaCost * _manaCost, MTGCardInstance * c)
{
ManaCost * manaCost;
if (_manaCost)
{
manaCost = _manaCost;
}
else
{
manaCost = NEW ManaCost();
}
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 (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;
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
switch (value[0])
{
case 'x':
manaCost->x();
break;
case 't': //Tap
if (value == "t")
{
//default Tap is handled outside of Manacost
}
else
{
manaCost->addExtraCost(NEW TapTargetCost(tc));
}
break;
case 's':
if (value == "s2l")
{ //Send To Library Cost (move from anywhere to Library)
manaCost->addExtraCost(NEW ToLibraryCost(tc));
}
else
{ //Sacrifice
manaCost->addExtraCost(NEW SacrificeCost(tc));
}
break;
case 'e': //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
{ //Life cost
manaCost->addExtraCost(NEW LifeCost(tc));
}
break;
case 'd': //DiscardRandom cost
if (value == "d")
{
manaCost->addExtraCost(NEW DiscardRandomCost(tc));
}
else
{
manaCost->addExtraCost(NEW DiscardCost(tc));
}
break;
case 'm': //Mill yourself as a cost
manaCost->addExtraCost(NEW MillCost(tc));
break;
case 'n': //return unblocked attacker cost
TargetChooserFactory tcf;
tc = tcf.createTargetChooser("creature|myBattlefield", c);
manaCost->addExtraCost(NEW Ninja(tc));
break;
case 'c': //Counters
{
size_t counter_start = value.find("(");
size_t counter_end = value.find(")", counter_start);
AbilityFactory abf;
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(",", separator + 1);
}
SAFE_DELETE(tc);
size_t target_start = string::npos;
if (separator2 != string::npos)
{
target_start = value.find(",", separator2 + 1);
}
size_t target_end = counter_end;
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;
}
default: //uncolored cost and hybrid costs
{
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::MTG_NB_COLORS; j++)
{
if (c == Constants::MTGColorChars[j])
{
colors[i] = j;
values[i] = 1;
}
}
}
}
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(int _cost[], int nb_elems)
{
init();
int i;
int total = nb_elems;
for (i = 0; i < total; i++)
{
cost[_cost[i * 2]] = _cost[i * 2 + 1];
}
}
ManaCost::ManaCost(ManaCost * _manaCost)
{
init();
int i;
for (i = 0; i <= Constants::MTG_NB_COLORS; i++)
{
cost[i] = _manaCost->getCost(i);
}
}
ManaCost::~ManaCost()
{
for (unsigned int i = 0; i < nbhybrids; i++)
{
SAFE_DELETE(hybrids[i]);
}
SAFE_DELETE(extraCosts);
SAFE_DELETE(kicker);
SAFE_DELETE(alternative);
SAFE_DELETE(BuyBack);
SAFE_DELETE(FlashBack);
SAFE_DELETE(Retrace);
}
void ManaCost::x()
{
cost[Constants::MTG_NB_COLORS] = 1;
}
int ManaCost::hasX()
{
return cost[Constants::MTG_NB_COLORS];
}
void ManaCost::init()
{
int i;
for (i = 0; i <= Constants::MTG_NB_COLORS; i++)
{
cost[i] = 0;
}
nbhybrids = 0;
extraCosts = NULL;
extraCostsIsCopy = 0;
kicker = NULL;
alternative = NULL;
BuyBack = NULL;
FlashBack = NULL;
Retrace = NULL;
}
void ManaCost::copy(ManaCost * _manaCost)
{
if (!_manaCost)
return;
for (unsigned int i = 0; i <= Constants::MTG_NB_COLORS; i++)
{
cost[i] = _manaCost->getCost(i);
}
for (unsigned int i = 0; i < nbhybrids; i++)
{
SAFE_DELETE(hybrids[i]);
}
for (unsigned int i = 0; i < _manaCost->nbhybrids; i++)
{
hybrids[i] = NEW ManaCostHybrid((*_manaCost->hybrids[i]));
}
nbhybrids = _manaCost->nbhybrids;
SAFE_DELETE(extraCosts);
if (_manaCost->extraCosts)
{
extraCosts = _manaCost->extraCosts->clone();
}
SAFE_DELETE(kicker);
if (_manaCost->kicker)
{
kicker = NEW ManaCost();
kicker->copy(_manaCost->kicker);
}
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);
}
}
int ManaCost::getCost(int color)
{
return cost[color];
}
ManaCostHybrid * ManaCost::getHybridCost(unsigned int i)
{
if (nbhybrids <= i)
return NULL;
return hybrids[i];
}
int ManaCost::hasColor(int color)
{
if (cost[color])
return 1;
for (unsigned int i = 0; i < nbhybrids; 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 (unsigned int i = 0; i < Constants::MTG_NB_COLORS; i++)
{
result += cost[i];
}
for (unsigned int i = 0; i < nbhybrids; i++)
{
result += hybrids[i]->getConvertedCost();
}
return result;
}
int ManaCost::remove(int color, int value)
{
cost[color] -= value;
if (cost[color] < 0)
{
cost[color] = 0;
}
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 (unsigned int i = 0; i < Constants::MTG_NB_COLORS; i++)
{
cost[i] += _cost->getCost(i);
}
for (unsigned int i = 0; i < _cost->nbhybrids; i++)
{
hybrids[nbhybrids] = NEW ManaCostHybrid((*_cost->hybrids[i]));
nbhybrids++;
}
return 1;
}
string ManaCost::toString()
{
return "ManaCost - Todo";
}
int ManaCost::addHybrid(int c1, int v1, int c2, int v2)
{
ManaCostHybrid * h = NEW ManaCostHybrid(c1, v1, c2, v2);
hybrids[nbhybrids] = h;
nbhybrids++;
return nbhybrids;
}
int ManaCost::addExtraCost(ExtraCost * _cost)
{
if (!extraCosts)
extraCosts = NEW ExtraCosts();
extraCosts->costs.push_back(_cost);
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);
if (toPay->kicker)
{
toPay->add(toPay->kicker);
if (!canAfford(toPay))
{
toPay->copy(_cost);
}
else
{
result = MANA_PAID_WITH_KICKER;
}
}
ManaCost * diff = Diff(toPay);
for (int i = 0; i < Constants::MTG_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::MTG_NB_COLORS; i++)
{
if (cost[i] < 0)
{
return 0;
}
}
return 1;
}
void ManaCost::randomDiffHybrids(ManaCost * _cost, int diff[])
{
int _nbhybrids = _cost->nbhybrids;
for (int i = 0; i < _nbhybrids; i++)
{
ManaCostHybrid * h = _cost->hybrids[i];
diff[h->color1 * 2 + 1] -= h->value1;
}
}
int ManaCost::tryToPayHybrids(ManaCostHybrid * _hybrids[], int _nbhybrids, int diff[])
{
if (!_nbhybrids)
return 1;
int result = 0;
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)
{
int diff[(Constants::MTG_NB_COLORS + 1) * 2];
diff[Constants::MTG_NB_COLORS * 2] = Constants::MTG_NB_COLORS;
for (int i = 0; i < Constants::MTG_NB_COLORS; i++)
{
diff[i * 2] = i;
diff[i * 2 + 1] = cost[i] - _cost->getCost(i);
}
int hybridResult = tryToPayHybrids(_cost->hybrids, _cost->nbhybrids, 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::MTG_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::MTG_NB_COLORS * 2 + 1] = 0;
for (int i = 0; i < Constants::MTG_NB_COLORS; i++)
{
if (diff[i * 2 + 1] > 0)
{
diff[Constants::MTG_NB_COLORS * 2 + 1] += diff[i * 2 + 1];
diff[i * 2 + 1] = 0;
}
}
}
ManaCost * result = NEW ManaCost(diff, Constants::MTG_NB_COLORS + 1);
return result;
}
#ifdef WIN32
void ManaCost::Dump()
{
DebugTrace("\n===ManaCost===\n");
for (int i=0; i<= Constants::MTG_NB_COLORS; i++)
{
if (cost[i])
{
DebugTrace(Constants::MTGColorChars[i] << ":" << cost[i] << " - ");
}
}
for (unsigned int i=0; i< nbhybrids; i++)
{
ManaCostHybrid * h = hybrids[i];
DebugTrace("H:{" << Constants::MTGColorChars[h->color1] << ":" << h->value1 << "}/{" << Constants::MTGColorChars[h->color2] << ":" << h->value2 << "}");
}
DebugTrace("\n=============\n");
}
#endif
ostream& operator<<(ostream& out, const ManaCost& m)
{
return out << "(manacost)";
}
void ManaPool::init()
{
ManaCost::init();
WEvent * e = NEW WEventEmptyManaPool(this);
GameObserver::GetInstance()->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);
GameObserver::GetInstance()->receiveEvent(e);
}
return result;
}
int ManaPool::add(int color, int value, MTGCardInstance * source)
{
int result = ManaCost::add(color, value);
for (int i = 0; i < value; ++i)
{
WEvent * e = NEW WEventEngageMana(color, source, this);
GameObserver::GetInstance()->receiveEvent(e);
}
return result;
}
int ManaPool::add(ManaCost * _cost, MTGCardInstance * source)
{
if (!_cost)
return 0;
int result = ManaCost::add(_cost);
for (unsigned int i = 0; i < Constants::MTG_NB_COLORS; i++)
{
for (int j = 0; j < _cost->getCost(i); j++)
{
WEvent * e = NEW WEventEngageMana(i, source, this);
GameObserver::GetInstance()->receiveEvent(e);
}
}
return result;
}
int ManaPool::pay(ManaCost * _cost)
{
int current[Constants::MTG_NB_COLORS];
for (unsigned int i = 0; i < Constants::MTG_NB_COLORS; i++)
{
current[i] = cost[i];
}
int result = ManaCost::pay(_cost);
for (unsigned int i = 0; i < Constants::MTG_NB_COLORS; i++)
{
int value = current[i] - cost[i];
for (int j = 0; j < value; j++)
{
WEvent * e = NEW WEventConsumeMana(i, this);
GameObserver::GetInstance()->receiveEvent(e);
}
}
return result;
}