From 7cc072bf772221ee78cb2b47417f26246361c45a Mon Sep 17 00:00:00 2001 From: "wagic.the.homebrew@gmail.com" Date: Sun, 13 Dec 2009 03:28:50 +0000 Subject: [PATCH] Erwan - fix a memory leak with "Prevent All combat damages" ability - Fix issue 242 (Equip can be used outside of the main phases) - introducing "attach" keyword. Same as equip but can be used anytime. Untested - introducing "asSorcery" keyword. Can be used the same way as "myTurnOnly" on activated abilities to restrict their usage. Untested. Other similar keywords will follow, please let me know which ones would be useful --- projects/mtg/bin/Res/test/_tests.txt | 1 + .../mtg/bin/Res/test/behemoth_sledge5.txt | 24 +++++++ projects/mtg/include/AllAbilities.h | 23 ++++--- projects/mtg/include/MTGAbility.h | 10 ++- projects/mtg/src/GameStateMenu.cpp | 2 +- projects/mtg/src/GameStateOptions.cpp | 6 +- projects/mtg/src/MTGAbility.cpp | 64 ++++++++++++------- 7 files changed, 93 insertions(+), 37 deletions(-) create mode 100644 projects/mtg/bin/Res/test/behemoth_sledge5.txt diff --git a/projects/mtg/bin/Res/test/_tests.txt b/projects/mtg/bin/Res/test/_tests.txt index 836bb5676..00cb00ed0 100644 --- a/projects/mtg/bin/Res/test/_tests.txt +++ b/projects/mtg/bin/Res/test/_tests.txt @@ -102,6 +102,7 @@ behemoth_sledge.txt behemoth_sledge2.txt behemoth_sledge3.txt behemoth_sledge4.txt +behemoth_sledge5.txt belligerent_hatchling.txt benalish_knight.txt black_vise.txt diff --git a/projects/mtg/bin/Res/test/behemoth_sledge5.txt b/projects/mtg/bin/Res/test/behemoth_sledge5.txt new file mode 100644 index 000000000..60a1997b8 --- /dev/null +++ b/projects/mtg/bin/Res/test/behemoth_sledge5.txt @@ -0,0 +1,24 @@ +#Test:equipment outside of main phase +[INIT] +COMBATATTACKERS +[PLAYER1] +manapool:{3} +inplay:grizzly bears,behemoth sledge +[PLAYER2] +[DO] +behemoth sledge +grizzly bears +next +#blockers +next +#damage +next +#end combat +[ASSERT] +COMBATEND +[PLAYER1] +life:20 +inplay:grizzly bears,behemoth sledge +[PLAYER2] +life:18 +[END] \ No newline at end of file diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index d2fd2a50d..1a081efdf 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -384,7 +384,7 @@ class GenericActivatedAbility:public ActivatedAbility{ int limitPerTurn; int counters; MTGGameZone * activeZone; - GenericActivatedAbility(int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int _tap = 0, int limit = 0, int myTurnOnly = 0, MTGGameZone * dest = NULL):ActivatedAbility(_id, card,_cost,myTurnOnly,_tap),ability(a),limitPerTurn(limit),activeZone(dest){ + GenericActivatedAbility(int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int _tap = 0, int limit = 0, int restrictions = 0, MTGGameZone * dest = NULL):ActivatedAbility(_id, card,_cost,restrictions,_tap),ability(a),limitPerTurn(limit),activeZone(dest){ counters = 0; target = ability->target; } @@ -441,7 +441,7 @@ public: int limitPerTurn; int counters; MTGGameZone * activeZone; - GenericTargetAbility(int _id, MTGCardInstance * _source, TargetChooser * _tc,MTGAbility * a, ManaCost * _cost = NULL, int _tap=0, int limit = 0, int myTurnOnly = 0, MTGGameZone * dest = NULL):TargetAbility(_id,_source, _tc,_cost,myTurnOnly,_tap),limitPerTurn(limit), activeZone(dest){ + GenericTargetAbility(int _id, MTGCardInstance * _source, TargetChooser * _tc,MTGAbility * a, ManaCost * _cost = NULL, int _tap=0, int limit = 0, int restrictions = 0, MTGGameZone * dest = NULL):TargetAbility(_id,_source, _tc,_cost,restrictions,_tap),limitPerTurn(limit), activeZone(dest){ ability = a; counters = 0; } @@ -988,7 +988,7 @@ class ABasicAbilityAuraModifierUntilEOT: public ActivatedAbility{ class AEquip:public TargetAbility{ public: vector currentAbilities; - AEquip(int _id, MTGCardInstance * _source, ManaCost * _cost=NULL, int doTap=0, int myturnOnly = 1):TargetAbility(_id,_source,NULL,_cost,myturnOnly,doTap){ + AEquip(int _id, MTGCardInstance * _source, ManaCost * _cost=NULL, int doTap=0, int restrictions = ActivatedAbility::AS_SORCERY):TargetAbility(_id,_source,NULL,_cost,restrictions,doTap){ } @@ -2316,12 +2316,12 @@ class APreventAllCombatDamage:public MTGAbility{ if (fromTc) fromTc->targetter = NULL; re = NEW REDamagePrevention (this, fromTc, toTc, -1, false, DAMAGE_COMBAT); game->replacementEffects->add(re); - return 1; + return MTGAbility::addToGame(); } int destroy(){ game->replacementEffects->remove(re); - delete re; + SAFE_DELETE(re); return 1; } @@ -2337,18 +2337,25 @@ class APreventAllCombatDamage:public MTGAbility{ class APreventAllCombatDamageUEOT: public InstantAbility{ public: APreventAllCombatDamage * ability; + vector clones; APreventAllCombatDamageUEOT(int id,MTGCardInstance * source,string to, string from):InstantAbility(id,source){ ability = NEW APreventAllCombatDamage(id,source,to, from); } int resolve(){ - ability->target = this->target; - ability->addToGame(); + APreventAllCombatDamage * a = ability->clone(); + a->target = this->target; + a->forceDestroy = -1; //Prevent the effect from getting destroyed because its source is not inplay + a->addToGame(); + clones.push_back(a); return 1; } int destroy(){ - ability->destroy(); + for (size_t i = 0; i < clones.size(); ++i){ + clones[i]->forceDestroy = 0; + } + clones.clear(); return 1; } diff --git a/projects/mtg/include/MTGAbility.h b/projects/mtg/include/MTGAbility.h index 4e5dc7012..9d249fee6 100644 --- a/projects/mtg/include/MTGAbility.h +++ b/projects/mtg/include/MTGAbility.h @@ -100,9 +100,14 @@ class TriggeredAbility:public MTGAbility{ class ActivatedAbility:public MTGAbility{ public: - int playerturnonly; + enum { + NO_RESTRICTION = 0, + PLAYER_TURN_ONLY = 1, + AS_SORCERY = 2 + }; + int restrictions; int needsTapping; - ActivatedAbility(int id, MTGCardInstance * card,ManaCost * _cost = NULL, int _playerturnonly = 0,int tap = 1); + ActivatedAbility(int id, MTGCardInstance * card,ManaCost * _cost = NULL, int _restrictions = NO_RESTRICTION,int tap = 1); virtual int reactToClick(MTGCardInstance * card); virtual int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL); virtual int reactToTargetClick(Targetable * object); @@ -202,6 +207,7 @@ class AbilityFactory{ 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 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); diff --git a/projects/mtg/src/GameStateMenu.cpp b/projects/mtg/src/GameStateMenu.cpp index a9bca13be..d0fee35cb 100644 --- a/projects/mtg/src/GameStateMenu.cpp +++ b/projects/mtg/src/GameStateMenu.cpp @@ -11,7 +11,7 @@ #include "../include/utils.h" #include "../include/DeckDataWrapper.h" -static const char* GAME_VERSION = "WTH?! 0.9.3 - by wololo"; +static const char* GAME_VERSION = "WTH?! 0.10.0 - by wololo"; #define DEFAULT_ANGLE_MULTIPLIER 0.4 #define MAX_ANGLE_MULTIPLIER (3*M_PI) diff --git a/projects/mtg/src/GameStateOptions.cpp b/projects/mtg/src/GameStateOptions.cpp index f0ea6b4c2..8214c887c 100644 --- a/projects/mtg/src/GameStateOptions.cpp +++ b/projects/mtg/src/GameStateOptions.cpp @@ -144,12 +144,12 @@ void GameStateOptions::Render() "Art: Ilya B, Julio, Jeck, J", "Check themeinfo.txt for the full credits of each theme!", "", - "Dev Team: Abrasax, Daddy32, Dr.Solomat, J,", - "Jeck, Leungclj, Superhiro, Psyringe, Wololo", + "Dev Team: Abrasax, Daddy32, Dr.Solomat, J, Jeck", + "Leungclj, Superhiro, Psyringe, Wololo, Yeshua", "", "Thanks to everyone who contributes code/content on the forums!", "", - "Developed with the JGE++ Library (http://jge.khors.com)", + "Developed with the JGE++ Library (http://code.google.com/p/wagic)", "SFX From www.soundsnap.com", "", "Music by Celestial Aeon Project, http://www.jamendo.com", diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index d80f968e1..796c107e4 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -143,6 +143,11 @@ TriggeredAbility * AbilityFactory::parseTrigger(string magicText, int id, Spell return NULL; } +int AbilityFactory::parseRestriction(string s){ + if (s.find("myturnonly") != string::npos) return ActivatedAbility::PLAYER_TURN_ONLY; + if (s.find("assorcery") != string::npos) return ActivatedAbility::AS_SORCERY; + return ActivatedAbility::NO_RESTRICTION; +} //Parses a string and returns the corresponding MTGAbility object @@ -186,8 +191,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG int doTap = 0; //Tap in the cost ? if (s.find("{t}") != string::npos) doTap = 1; - int myTurnOnly = 0; - if (s.find("myturnonly") != string::npos) myTurnOnly = 1; + int restrictions = parseRestriction(s); size_t delimiter = s.find("}:"); size_t firstNonSpace = s.find_first_not_of(" "); @@ -213,15 +217,6 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return amp; } - AEquip *ae = dynamic_cast(a); - if (ae){ - ae->cost = cost; - TargetChooserFactory tcf; - ae->tc = tcf.createTargetChooser("creature|myBattlefield", card); - return ae; - } - - int limit = 0; unsigned int limit_str = s.find("limit:"); if (limit_str != string::npos){ @@ -238,8 +233,19 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG tc = tcf.createTargetChooser(starget, card); } - if (tc) return NEW GenericTargetAbility(id, card, tc, a,cost, doTap,limit,myTurnOnly,dest); - return NEW GenericActivatedAbility(id, card, a,cost,doTap,limit,myTurnOnly,dest); + AEquip *ae = dynamic_cast(a); + if (ae){ + ae->cost = cost; + if (!tc) { + TargetChooserFactory tcf; + tc = tcf.createTargetChooser("creature|myBattlefield", card); + } + ae->tc = tc; + return ae; + } + + if (tc) return NEW GenericTargetAbility(id, card, tc, a,cost, doTap,limit,restrictions,dest); + return NEW GenericActivatedAbility(id, card, a,cost,doTap,limit,restrictions,dest); } SAFE_DELETE(cost); } @@ -426,6 +432,13 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return a; } + //Equipment (attach) + found = s.find("attach"); + if (found != string::npos){ + MTGAbility * a = NEW AEquip(id,card,0,0,ActivatedAbility::NO_RESTRICTION); + return a; + } + //MoveTo Move a card from a zone to another found = s.find("moveto("); @@ -1702,15 +1715,25 @@ ostream& MTGAbility::toString(ostream& out) const // -ActivatedAbility::ActivatedAbility(int id, MTGCardInstance * card, ManaCost * _cost, int _playerturnonly,int tap):MTGAbility(id,card), playerturnonly(_playerturnonly), needsTapping(tap){ +ActivatedAbility::ActivatedAbility(int id, MTGCardInstance * card, ManaCost * _cost, int restrictions,int tap):MTGAbility(id,card), restrictions(restrictions), needsTapping(tap){ cost = _cost; } int ActivatedAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana){ - Player * player = game->currentPlayer; - if (!playerturnonly) player = game->currentlyActing(); - if (card == source && source->controller()==player && player==game->currentlyActing() && (!needsTapping || (!source->isTapped() && !source->hasSummoningSickness()))){ + Player * player = game->currentlyActing(); + int cPhase = game->getCurrentGamePhase(); + switch(restrictions) { + case PLAYER_TURN_ONLY: + if (player != game->currentPlayer) return 0; + break; + case AS_SORCERY: + if (player != game->currentPlayer) return 0; + if (cPhase != Constants::MTG_PHASE_FIRSTMAIN && cPhase != Constants::MTG_PHASE_SECONDMAIN) return 0; + break; + } + + if (card == source && source->controller()==player && (!needsTapping || (!source->isTapped() && !source->hasSummoningSickness()))){ if (!cost) return 1; if (!mana) mana = player->getManaPool(); if (!mana->canAfford(cost)) return 0; @@ -1756,18 +1779,13 @@ int ActivatedAbility::reactToTargetClick(Targetable * object){ ostream& ActivatedAbility::toString(ostream& out) const { - out << "ActivatedAbility ::: playerturnonly : " << playerturnonly + out << "ActivatedAbility ::: restrictions : " << restrictions << " ; needsTapping : " << needsTapping << " ("; return MTGAbility::toString(out) << ")"; } -//The whole targetAbility mechanism is messed up, mainly because of its interactions with -// the ActionLayer, GameObserver, and parent class ActivatedAbility. -// Currently choosing a target is a complete different mechanism for put into play and for other abilities. -// It probably shouldn't be the case. - TargetAbility::TargetAbility(int id, MTGCardInstance * card, TargetChooser * _tc,ManaCost * _cost, int _playerturnonly,int tap):ActivatedAbility(id, card,_cost,_playerturnonly, tap){ tc = _tc; ability = NULL;