diff --git a/projects/mtg/bin/Res/sets/FEM/_cards.dat b/projects/mtg/bin/Res/sets/FEM/_cards.dat index 5f36b5208..645490ce8 100644 --- a/projects/mtg/bin/Res/sets/FEM/_cards.dat +++ b/projects/mtg/bin/Res/sets/FEM/_cards.dat @@ -798,6 +798,11 @@ id=1924 rarity=C [/card] [card] +primitive=Saproling +id=-1924 +rarity=T +[/card] +[card] primitive=Thallid Devourer id=1928 rarity=U diff --git a/projects/mtg/bin/Res/sets/mtg_todo.dat b/projects/mtg/bin/Res/sets/mtg_todo.dat index 1ece04e14..173dc46f3 100644 --- a/projects/mtg/bin/Res/sets/mtg_todo.dat +++ b/projects/mtg/bin/Res/sets/mtg_todo.dat @@ -40161,15 +40161,7 @@ auto={T}: Add {U} text={T}: Add {1} to your mana pool. -- {T}: Add {W} or {U} to your mana pool. Thalakos Lowlands doesn't untap during your next untap step. type=Land [/card] -[card] -name=Thallid -text=At the beginning of your upkeep, put a spore counter on Thallid. -- Remove three spore counters from Thallid: Put a 1/1 green Saproling creature token onto the battlefield. -mana={G} -type=Creature -subtype=Fungus -power=1 -toughness=1 -[/card] + [card] name=Thallid Devourer text=At the beginning of your upkeep, put a spore counter on Thallid Devourer. -- Remove three spore counters from Thallid Devourer: Put a 1/1 green Saproling creature token onto the battlefield. -- Sacrifice a Saproling: Thallid Devourer gets +1/+2 until end of turn. diff --git a/projects/mtg/bin/Res/sets/primitives/mtg.txt b/projects/mtg/bin/Res/sets/primitives/mtg.txt index 4e981445a..02713c47e 100644 --- a/projects/mtg/bin/Res/sets/primitives/mtg.txt +++ b/projects/mtg/bin/Res/sets/primitives/mtg.txt @@ -31887,6 +31887,14 @@ power=5 toughness=5 [/card] [card] +name=Saproling +type=Creature +subtype=Saproling +power=1 +toughness=1 +color=green +[/card] +[card] name=Sapseep Forest auto=tap auto={T}:Add {G} @@ -38996,6 +39004,17 @@ power=1 toughness=2 [/card] [card] +name=Thallid +text=At the beginning of your upkeep, put a spore counter on Thallid. -- Remove three spore counters from Thallid: Put a 1/1 green Saproling creature token onto the battlefield. +mana={G} +type=Creature +subtype=Fungus +auto=@each my upkeep:counter(0/0,1,Spore) +auto={C(0/0,-3,Spore)}:Token(-1924) +power=1 +toughness=1 +[/card] +[card] name=Thaumatog auto={S(land|myBattlefield)}:1/1 auto={S(enchantment|myBattlefield)}:1/1 diff --git a/projects/mtg/bin/Res/test/_tests.txt b/projects/mtg/bin/Res/test/_tests.txt index 73209343c..046788002 100644 --- a/projects/mtg/bin/Res/test/_tests.txt +++ b/projects/mtg/bin/Res/test/_tests.txt @@ -397,6 +397,7 @@ terror.txt terror2.txt threaten.txt throne_of_bone.txt +thallid.txt titanic_ultimatum.txt torture.txt tranquil_domain.txt diff --git a/projects/mtg/bin/Res/test/bugs/fog_effect_i286.txt b/projects/mtg/bin/Res/test/bugs/fog_effect_i286.txt new file mode 100644 index 000000000..a1c9445d9 --- /dev/null +++ b/projects/mtg/bin/Res/test/bugs/fog_effect_i286.txt @@ -0,0 +1,41 @@ +#Checking savage hunger Cycle only bug +[INIT] +FIRSTMAIN +[PLAYER1] +inplay:Craw Wurm +[PLAYER2] +hand:Fog +inplay:Forest,Spore Frog +[DO] +next +#startcombat +next +#declare attackers +Craw Wurm +next +#declare blockers +Spore Frog +choice 1 +eot +eot +next +next +next +#First Main +next +next +Craw Wurm +next +next +next +next +[ASSERT] +SECONDMAIN +[PLAYER1] +inplay:Craw Wurm +[PLAYER2] +hand:Fog +inplay:Forest +graveyard:Spore Frog +life:14 +[END] diff --git a/projects/mtg/bin/Res/test/thallid.txt b/projects/mtg/bin/Res/test/thallid.txt new file mode 100644 index 000000000..93c4cdf41 --- /dev/null +++ b/projects/mtg/bin/Res/test/thallid.txt @@ -0,0 +1,23 @@ +[INIT] +UNTAP +[PLAYER1] +inplay:Thallid +[PLAYER2] +[DO] +eot +eot +eot +eot +eot +eot +next +next +next +thallid +thallid +[ASSERT] +FIRSTMAIN +[PLAYER1] +inplay:Thallid,Saproling +[PLAYER2] +[END] diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index d8892d595..af4dd1815 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -202,10 +202,11 @@ public: //counters class AACounter: public ActivatedAbility{ public: + string name; int nb; int power; int toughness; - AACounter(int id, MTGCardInstance * source, MTGCardInstance * target, int power, int toughness, int nb, ManaCost * cost = NULL, int doTap = 0) : ActivatedAbility(id, source, cost, 0, doTap), nb(nb), power(power), toughness(toughness) { + AACounter(int id, MTGCardInstance * source, MTGCardInstance * target, const char * _name, int power, int toughness, int nb, ManaCost * cost = NULL, int doTap = 0) : ActivatedAbility(id, source, cost, 0, doTap), nb(nb), power(power), toughness(toughness), name(_name) { this->target = target; } @@ -215,11 +216,11 @@ class AACounter: public ActivatedAbility{ MTGCardInstance * _target = (MTGCardInstance *)target; if (nb>0){ for (int i=0; i < nb; i++){ - _target->counters->addCounter(power, toughness); + _target->counters->addCounter(name.c_str(), power, toughness); } }else{ for (int i=0; i < -nb; i++){ - _target->counters->removeCounter(power, toughness); + _target->counters->removeCounter(name.c_str(), power, toughness); } } return nb; diff --git a/projects/mtg/include/CardGui.h b/projects/mtg/include/CardGui.h index e2a82fbbf..a732c6b3a 100644 --- a/projects/mtg/include/CardGui.h +++ b/projects/mtg/include/CardGui.h @@ -29,6 +29,7 @@ struct CardGui : public PlayGuiObject { void RenderBig(const Pos&); //Tries to render the Big version of a card picture, backups to text version in case of failure static void RenderBig(MTGCard * card, const Pos& pos); void alternateRenderBig(const Pos&); //Renders Text Version of a card + void renderCountersBig(const Pos& pos); virtual void Update(float dt); static void alternateRender(MTGCard * card, const Pos& pos); static JQuad * alternateThumbQuad(MTGCard * card); diff --git a/projects/mtg/include/ExtraCost.h b/projects/mtg/include/ExtraCost.h index 2483ce80a..2f00edc52 100644 --- a/projects/mtg/include/ExtraCost.h +++ b/projects/mtg/include/ExtraCost.h @@ -2,6 +2,7 @@ #define _EXTRACOST_H_ #include +#include "Counters.h" using std::vector; class TargetChooser; @@ -13,9 +14,10 @@ public: TargetChooser * tc; MTGCardInstance * source; ExtraCost(TargetChooser *_tc = NULL); - ~ExtraCost(); + virtual ~ExtraCost(); virtual int setPayment(MTGCardInstance * card) = 0; virtual int isPaymentSet() = 0; + virtual int canPay() = 0; virtual int doPay() = 0; virtual void Render(){}; virtual int setSource(MTGCardInstance * _source); @@ -32,6 +34,7 @@ public: void Render(); int tryToSetPayment(MTGCardInstance * card); int isPaymentSet(); + int canPay(); int doPay(); int reset(); int setAction(MTGAbility * _action, MTGCardInstance * _source); @@ -45,10 +48,27 @@ public: SacrificeCost(TargetChooser *_tc = NULL); virtual int setPayment(MTGCardInstance * card); virtual int isPaymentSet(); + virtual int canPay(); virtual int doPay(); virtual void Render(); virtual int setSource(MTGCardInstance * _source); virtual SacrificeCost * clone() const; }; +class CounterCost: public ExtraCost{ +public: + Counter * counter; + MTGCardInstance * target; + int hasCounters; + CounterCost(Counter * _counter,TargetChooser *_tc = NULL); + ~CounterCost(); + virtual int setPayment(MTGCardInstance * card); + virtual int isPaymentSet(); + virtual int canPay(); + virtual int doPay(); + virtual void Render(); + virtual int setSource(MTGCardInstance * _source); + virtual CounterCost * clone() const; +}; + #endif diff --git a/projects/mtg/include/MTGAbility.h b/projects/mtg/include/MTGAbility.h index d80ab2f0c..da8767fd4 100644 --- a/projects/mtg/include/MTGAbility.h +++ b/projects/mtg/include/MTGAbility.h @@ -252,10 +252,10 @@ class GenericTriggeredAbility:public TriggeredAbility{ class AbilityFactory{ private: int countCards(TargetChooser * tc, Player * player = NULL, int option = 0); - int parsePowerToughness(string s, int *power, int *toughness); TriggeredAbility * parseTrigger(string s, int id, Spell * spell, MTGCardInstance *card, Targetable * target); int parseRestriction(string s); public: + int parsePowerToughness(string s, int *power, int *toughness); int getAbilities(vector * v, Spell * spell, MTGCardInstance * card = NULL, int id = 0,MTGGameZone * dest = NULL); MTGAbility * parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, int activated = 0, int forceUEOT = 0,MTGGameZone * dest = NULL); int abilityEfficiency(MTGAbility * a, Player * p, int mode = MODE_ABILITY, TargetChooser * tc = NULL); diff --git a/projects/mtg/include/ManaCost.h b/projects/mtg/include/ManaCost.h index b7bc0a9f1..3705ff520 100644 --- a/projects/mtg/include/ManaCost.h +++ b/projects/mtg/include/ManaCost.h @@ -46,11 +46,12 @@ class ManaCost{ int add(int color, int value); // - // Extra Costs (sacrifice...) + // Extra Costs (sacrifice,counters...) // int addExtraCost(ExtraCost * _cost); int setExtraCostsAction(MTGAbility * action, MTGCardInstance * card); int isExtraPaymentSet(); + int canPayExtra(); int doPayExtra(); int addHybrid(int c1, int v1, int c2, int v2); diff --git a/projects/mtg/src/CardGui.cpp b/projects/mtg/src/CardGui.cpp index 719542055..179510681 100644 --- a/projects/mtg/src/CardGui.cpp +++ b/projects/mtg/src/CardGui.cpp @@ -8,7 +8,7 @@ #include "../include/GameObserver.h" #include #include - +#include "../include/Counters.h" const float CardGui::Width = 28.0; const float CardGui::Height = 40.0; @@ -113,6 +113,22 @@ void CardGui::Render() mFont->SetScale(1); } + if (card->counters->mCount > 0) { + unsigned c = -1; + for (int i = 0; i < card->counters->mCount; i++) { + if (card->counters->counters[i]->name != "") c = i; break; + } + if (c + 1) { + mFont->SetScale(DEFAULT_MAIN_FONT_SCALE); + char buffer[200]; + sprintf(buffer, "%i",card->counters->counters[0]->nb); + mFont->SetColor(ARGB(static_cast(actA),255,255,255)); + mFont->SetScale(actZ); + mFont->DrawString(buffer, actX - 10*actZ , actY -(12 * actZ)); + mFont->SetScale(1); + } + } + if (tc && !tc->canTarget(card)) { if (!shadow) shadow = resources.GetQuad("shadow"); shadow->SetColor(ARGB(200,255,255,255)); @@ -334,6 +350,7 @@ void CardGui::alternateRender(MTGCard * card, const Pos& pos){ void CardGui::alternateRenderBig(const Pos& pos){ alternateRender(card,pos); + renderCountersBig(pos); } void CardGui::RenderBig(MTGCard* card, const Pos& pos){ @@ -362,8 +379,39 @@ void CardGui::RenderBig(MTGCard* card, const Pos& pos){ alternateRender(card,pos); } +void CardGui::renderCountersBig(const Pos& pos){ + // Write Named Counters + if (card->counters) { + JLBFont * font = resources.GetJLBFont("magic"); + font->SetColor(ARGB((int)pos.actA, 0, 0, 0)); + font->SetScale(0.8 * pos.actZ); + std::vector txt = card->formattedText(); + unsigned i = txt.size() + 1; + Counter * c = NULL; + for (int t = 0; t < card->counters->mCount; t++, i++) { + if (c) { + c = card->counters->getNext(c); + }else{ + c = card->counters->counters[0]; + } + if (c->nb > 0) { + char buf[512]; + if (c->name != "") { + std::string s = c->name; + s[0] = toupper(s[0]); + sprintf(buf,_("%s counters: %i").c_str(),s.c_str(),c->nb); + }else{ + sprintf(buf,_("%i/%i counters: %i").c_str(),c->power,c->toughness,c->nb); + } + font->DrawString(buf, pos.actX + (22 - BigWidth / 2)*pos.actZ, pos.actY + (-BigHeight/2 + 80 + 11 * i)*pos.actZ); + } + } + } +} + void CardGui::RenderBig(const Pos& pos){ RenderBig(card,pos); + renderCountersBig(pos); } diff --git a/projects/mtg/src/ExtraCost.cpp b/projects/mtg/src/ExtraCost.cpp index fcbe0f5e7..71511ee8e 100644 --- a/projects/mtg/src/ExtraCost.cpp +++ b/projects/mtg/src/ExtraCost.cpp @@ -4,6 +4,7 @@ #include "../include/Translate.h" #include "../include/config.h" #include "../include/Player.h" +#include "../include/Counters.h" #include ExtraCost::ExtraCost( TargetChooser *_tc):tc(_tc){ @@ -55,6 +56,11 @@ int SacrificeCost::isPaymentSet(){ return 0; } +int SacrificeCost::canPay(){ + //Sacrifice does not have any additional restrictions. + return 1; +} + int SacrificeCost::doPay(){ if(target){ target->controller()->game->putInGraveyard(target); @@ -75,6 +81,100 @@ void SacrificeCost::Render(){ mFont->DrawString(buffer, 20 ,20, JGETEXT_LEFT); } +//Counter costs + +CounterCost * CounterCost::clone() const{ + CounterCost * ec = NEW CounterCost(*this); + if (tc) ec->tc = tc->clone(); + if (counter) ec->counter = NEW Counter(counter->target, counter->name.c_str(),counter->power, counter->toughness); + return ec; +} + +CounterCost::CounterCost(Counter * _counter,TargetChooser *_tc):ExtraCost(_tc) { + if (tc) tc->targetter = NULL; + target = NULL; + counter = _counter; + hasCounters = 0; +} + +int CounterCost::setSource(MTGCardInstance * _source){ + ExtraCost::setSource(_source); + if (tc) tc->targetter = NULL; + if (!tc) target = _source; + return 1; +} + +int CounterCost::setPayment(MTGCardInstance *card){ + if (tc) { + int result = tc->addTarget(card); + if (result) { + if (counter->nb >= 0) return 1; //add counters always possible + target = card; + Counter * targetCounter = target->counters->hasCounter(counter->name.c_str(),counter->power,counter->toughness); + if (targetCounter && targetCounter->nb >= - counter->nb) { + hasCounters = 1; + return result; + } + } + } + return 0; +} + +int CounterCost::isPaymentSet(){ + if (!target) return 0; + if (counter->nb >=0) return 1; //add counters always possible + Counter * targetCounter = target->counters->hasCounter(counter->name.c_str(),counter->power,counter->toughness); + if (targetCounter && targetCounter->nb >= - counter->nb) { + hasCounters = 1; + } + if (target && hasCounters) return 1; + return 0; +} + +int CounterCost::canPay(){ + // if target needs to be chosen, then move on. + if (tc) return 1; + if (counter->nb >=0) return 1; //add counters always possible + // otherwise, move on only if target has enough counters + Counter * targetCounter = target->counters->hasCounter(counter->name.c_str(),counter->power,counter->toughness); + if (targetCounter && targetCounter->nb >= - counter->nb) return 1; + return 0; +} + +int CounterCost::doPay(){ + if (!target) return 0; + + if (counter->nb >=0) { //Add counters as a cost + for (int i = 0; i < counter->nb; i++) { + target->counters->addCounter(counter->name.c_str(),counter->power,counter->toughness); + } + return 1; + } + + //remove counters as a cost + if (hasCounters) { + for (int i = 0; i < - counter->nb; i++) { + target->counters->removeCounter(counter->name.c_str(),counter->power,counter->toughness); + } + hasCounters = 0; + return 1; + } + return 0; +} + +void CounterCost::Render(){ + JLBFont * mFont = resources.GetJLBFont(Constants::MAIN_FONT); + mFont->SetScale(DEFAULT_MAIN_FONT_SCALE); + mFont->SetColor(ARGB(255,255,255,255)); + char buffer[200]; + sprintf(buffer, "%s", _("counters").c_str()); + mFont->DrawString(buffer, 20 ,20, JGETEXT_LEFT); +} + +CounterCost::~CounterCost(){ + SAFE_DELETE(counter); +} + // //Container // @@ -129,6 +229,13 @@ int ExtraCosts::isPaymentSet(){ return 1; } +int ExtraCosts::canPay(){ + for (size_t i = 0; i < costs.size(); i++){ + if (!costs[i]->canPay()) return 0; + } + return 1; +} + int ExtraCosts::doPay(){ int result = 0; for (size_t i = 0; i < costs.size(); i++){ diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index 6824eb833..a179501df 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -702,17 +702,23 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG if (found != string::npos){ found+=8; int nb = 1; + string name = ""; size_t end = s.find(")", found); size_t separator = s.find(",", found); if (separator != string::npos){ - string nbstr = s.substr(separator+1,end-separator-1); + size_t separator2 = s.find(",", separator+1); + if (separator2 != string::npos) { + name = s.substr(separator2+1,end-separator2-1); + } + string nbstr = s.substr(separator+1,separator2-separator-1); nb = atoi(nbstr.c_str()); end = separator; } + string spt = s.substr(found,end-found); int power, toughness; if ( parsePowerToughness(spt,&power, &toughness)){ - MTGAbility * a = NEW AACounter(id,card,target,power,toughness,nb); + MTGAbility * a = NEW AACounter(id,card,target,name.c_str(),power,toughness,nb); a->oneShot = 1; return a; } @@ -1742,17 +1748,19 @@ int ActivatedAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana) if (card == source && source->controller()==player && (!needsTapping || (!source->isTapped() && !source->hasSummoningSickness()))){ if (!cost) return 1; + cost->setExtraCostsAction(this, card); if (!mana) mana = player->getManaPool(); if (!mana->canAfford(cost)) return 0; + if (!cost->canPayExtra()) return 0; return 1; } return 0; } int ActivatedAbility::reactToClick(MTGCardInstance * card){ +// if (cost) cost->setExtraCostsAction(this, card); if (!isReactingToClick(card)) return 0; if (cost){ - cost->setExtraCostsAction(this, card); if (!cost->isExtraPaymentSet()){ game->waitForExtraPayment = cost->extraCosts; return 0; diff --git a/projects/mtg/src/ManaCost.cpp b/projects/mtg/src/ManaCost.cpp index 0c53c569e..67ec21d32 100644 --- a/projects/mtg/src/ManaCost.cpp +++ b/projects/mtg/src/ManaCost.cpp @@ -6,6 +6,7 @@ #include "../include/Targetable.h" #include "../include/Player.h" #include "../include/WEvent.h" +#include "../include/MTGAbility.h" #if defined (WIN32) @@ -64,6 +65,46 @@ ManaCost * ManaCost::parseManaCost(string s, ManaCost * _manaCost, MTGCardInstan tc = tcf.createTargetChooser(target,c); } manaCost->addExtraCost(NEW SacrificeCost(tc)); + }else if (value[0] == 'c'){ + //Counters + OutputDebugString("Counter\n"); + size_t counter_start = value.find("("); + int nb = 1; + string name = ""; + size_t counter_end = value.find(")", counter_start); + size_t end = value.find(")", counter_start); + size_t separator = value.find(",", counter_start); + size_t separator2 = string::npos; + if (separator != string::npos){ + separator2 = value.find(",", separator+1); + if (separator2 != string::npos) { + name = value.substr(separator2+1,counter_end-separator2-1); + } + string nbstr = value.substr(separator+1,separator2-separator-1); + nb = atoi(nbstr.c_str()); + counter_end = separator; + } + + string spt = value.substr(counter_start+1,counter_end-counter_start-1); + int power, toughness; + Counter * counter = NULL; + AbilityFactory abf; + if ( abf.parsePowerToughness(spt,&power, &toughness)){ + counter = NEW Counter(c,name.c_str(),power,toughness); + counter->nb = nb; + } + TargetChooserFactory tcf; + TargetChooser * tc = NULL; + size_t target_start = string::npos; + if (separator2 != string::npos) { + target_start = value.find(",",separator2+1); + } + size_t target_end = 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)); }else{ int intvalue = atoi(value.c_str()); int colors[2]; @@ -247,7 +288,7 @@ int ManaCost::addHybrid(int c1, int v1, int c2, int v2){ int ManaCost::addExtraCost(ExtraCost * _cost){ if (!extraCosts) extraCosts = NEW ExtraCosts(); extraCosts->costs.push_back(_cost); - OutputDebugString("Adding Sacrifice\n"); + OutputDebugString("Adding Extra cost\n"); return 1; } @@ -258,6 +299,12 @@ int ManaCost::isExtraPaymentSet(){ return extraCosts->isPaymentSet(); } +int ManaCost::canPayExtra(){ + if (!extraCosts) return 1; + OutputDebugString("Checking costs for payability\n"); + return extraCosts->canPay(); +} + int ManaCost::doPayExtra(){ if (!extraCosts) return 0; return extraCosts->doPay(); //TODO reset ?