diff --git a/projects/mtg/Makefile b/projects/mtg/Makefile index 0d7e9f764..7b268f7e9 100644 --- a/projects/mtg/Makefile +++ b/projects/mtg/Makefile @@ -1,4 +1,4 @@ -OBJS = objs/ActionElement.o objs/ActionLayer.o objs/ActionStack.o objs/AIMomirPlayer.o objs/AIPlayer.o objs/AIStats.o objs/AllAbilities.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardPrimitive.o objs/CardSelector.o objs/CardSelectorSingleton.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.o objs/DeckEditorMenu.o objs/DeckMenu.o objs/DeckMenuItem.o objs/DeckMetaData.o objs/DeckStats.o objs/DuelLayers.o objs/Effects.o objs/ExtraCost.o objs/GameApp.o objs/GameLauncher.o objs/GameObserver.o objs/GameOptions.o objs/GameState.o objs/GameStateAwards.o objs/GameStateDeckViewer.o objs/GameStateDuel.o objs/DeckManager.o objs/GameStateMenu.o objs/GameStateOptions.o objs/GameStateShop.o objs/GameStateStory.o objs/GameStateTransitions.o objs/GuiAvatars.o objs/GuiBackground.o objs/GuiCardsController.o objs/GuiCombat.o objs/GuiFrame.o objs/GuiHand.o objs/GuiLayers.o objs/GuiMana.o objs/GuiPhaseBar.o objs/GuiPlay.o objs/GuiStatic.o objs/ManaCost.o objs/ManaCostHybrid.o objs/MenuItem.o objs/MTGAbility.o objs/MTGCardInstance.o objs/MTGCard.o objs/MTGDeck.o objs/MTGDefinitions.o objs/MTGGamePhase.o objs/MTGGameZones.o objs/MTGPack.o objs/MTGRules.o objs/Navigator.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/Pos.o objs/PrecompiledHeader.o objs/PriceList.o objs/ReplacementEffects.o objs/Rules.o objs/SimpleMenu.o objs/SimpleMenuItem.o objs/SimplePad.o objs/SimplePopup.o objs/StoryFlow.o objs/StyleManager.o objs/Subtypes.o objs/TargetChooser.o objs/TargetsList.o objs/TextScroller.o objs/ThisDescriptor.o objs/Token.o objs/Translate.o objs/TranslateKeys.o objs/Trash.o objs/utils.o objs/WEvent.o objs/WResourceManager.o objs/WCachedResource.o objs/WDataSrc.o objs/WGui.o objs/WFilter.o objs/Tasks.o objs/WFont.o +OBJS = objs/ActionElement.o objs/ActionLayer.o objs/ActionStack.o objs/AIMomirPlayer.o objs/AIPlayer.o objs/AIStats.o objs/AllAbilities.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardPrimitive.o objs/CardSelector.o objs/CardSelectorSingleton.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.o objs/DeckEditorMenu.o objs/DeckMenu.o objs/DeckMenuItem.o objs/DeckMetaData.o objs/DeckStats.o objs/DuelLayers.o objs/Effects.o objs/ExtraCost.o objs/GameApp.o objs/GameLauncher.o objs/GameObserver.o objs/GameOptions.o objs/GameState.o objs/GameStateAwards.o objs/GameStateDeckViewer.o objs/GameStateDuel.o objs/DeckManager.o objs/GameStateMenu.o objs/GameStateOptions.o objs/GameStateShop.o objs/GameStateStory.o objs/GameStateTransitions.o objs/GuiAvatars.o objs/GuiBackground.o objs/GuiCardsController.o objs/GuiCombat.o objs/GuiFrame.o objs/GuiHand.o objs/GuiLayers.o objs/GuiMana.o objs/GuiPhaseBar.o objs/GuiPlay.o objs/GuiStatic.o objs/ManaCost.o objs/ManaCostHybrid.o objs/MenuItem.o objs/MTGAbility.o objs/MTGCardInstance.o objs/MTGCard.o objs/MTGDeck.o objs/MTGDefinitions.o objs/MTGGamePhase.o objs/MTGGameZones.o objs/MTGPack.o objs/MTGRules.o objs/Navigator.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/PlayRestrictions.o objs/Pos.o objs/PrecompiledHeader.o objs/PriceList.o objs/ReplacementEffects.o objs/Rules.o objs/SimpleMenu.o objs/SimpleMenuItem.o objs/SimplePad.o objs/SimplePopup.o objs/StoryFlow.o objs/StyleManager.o objs/Subtypes.o objs/TargetChooser.o objs/TargetsList.o objs/TextScroller.o objs/ThisDescriptor.o objs/Token.o objs/Translate.o objs/TranslateKeys.o objs/Trash.o objs/utils.o objs/WEvent.o objs/WResourceManager.o objs/WCachedResource.o objs/WDataSrc.o objs/WGui.o objs/WFilter.o objs/Tasks.o objs/WFont.o DEPS = $(patsubst objs/%.o, deps/%.d, $(OBJS)) RESULT = $(shell psp-config --psp-prefix 2> Makefile.cache) diff --git a/projects/mtg/bin/Res/test/_tests.txt b/projects/mtg/bin/Res/test/_tests.txt index bd2b2b604..d5dc866e0 100644 --- a/projects/mtg/bin/Res/test/_tests.txt +++ b/projects/mtg/bin/Res/test/_tests.txt @@ -36,6 +36,7 @@ generic/hybrid_mana_3.txt generic/indestructible.txt generic/kicker.txt generic/kicker2.txt +generic/lands_max_per_turn.txt generic/landwalk.txt generic/legendary.txt generic/level_up.txt diff --git a/projects/mtg/bin/Res/test/generic/lands_max_per_turn.txt b/projects/mtg/bin/Res/test/generic/lands_max_per_turn.txt new file mode 100644 index 000000000..79ffd6bec --- /dev/null +++ b/projects/mtg/bin/Res/test/generic/lands_max_per_turn.txt @@ -0,0 +1,16 @@ +#Tests that more than 1 land cannot be played per turn +[INIT] +FIRSTMAIN +[PLAYER1] +hand:swamp,plains +[PLAYER2] +[DO] +swamp +plains +[ASSERT] +FIRSTMAIN +[PLAYER1] +inplay:swamp +hand:plains +[PLAYER2] +[END] \ No newline at end of file diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index ec43ca453..b27f61cb3 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -1281,18 +1281,18 @@ public: }; //lands, allows to play more land during a turn: - -class AAMoreLandPlz: public ActivatedAbilityTP +class AMoreLandPlzUEOT: public InstantAbilityTP { public: WParsedInt *additional; + MaxPerTurnRestriction * landsRestriction; - AAMoreLandPlz(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, WParsedInt * _additional, int _tap = 0, - int who = TargetChooser::UNSET); - int resolve(); + AMoreLandPlzUEOT(int _id, MTGCardInstance * card, Targetable * _target, WParsedInt * _additional, int who = TargetChooser::UNSET); + int addToGame(); + int destroy(); const char * getMenuText(); - AAMoreLandPlz * clone() const; - ~AAMoreLandPlz(); + AMoreLandPlzUEOT * clone() const; + ~AMoreLandPlzUEOT(); }; @@ -4709,43 +4709,39 @@ public: class AFastbond: public TriggeredAbility { public: - int alreadyPlayedALand; - int previous; + + TargetChooser * counter; + MaxPerTurnRestriction * landsRestriction; + int landsPlayedThisTurn; AFastbond(int _id, MTGCardInstance * card) : TriggeredAbility(_id, card) { - alreadyPlayedALand = 0; - if (source->controller()->landsPlayerCanStillPlay == 0) - { - alreadyPlayedALand = 1; - source->controller()->landsPlayerCanStillPlay += 1; - source->controller()->canPutLandsIntoPlay = true; - } - previous = source->controller()->landsPlayerCanStillPlay; + counter = NEW TypeTargetChooser("land"); + landsPlayedThisTurn = source->controller()->game->inPlay->seenThisTurn(counter); + PlayRestrictions * restrictions = source->controller()->game->playRestrictions; + landsRestriction = (MaxPerTurnRestriction *) (restrictions->getRestrictionById(PlayRestriction::LANDS_RULE_ID)); + restrictions->removeRestriction(landsRestriction); + } void Update(float dt) { if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UNTAP) { - alreadyPlayedALand = 0; + landsPlayedThisTurn = 0; } TriggeredAbility::Update(dt); } int trigger() { - if (source->controller()->landsPlayerCanStillPlay == 0 && previous >= 1) + int landsPlayedThisTurnUpdated = source->controller()->game->inPlay->seenThisTurn(counter); + if (landsPlayedThisTurnUpdated > 1 && landsPlayedThisTurnUpdated > landsPlayedThisTurn) { - previous = 0; - source->controller()->canPutLandsIntoPlay = true; - source->controller()->landsPlayerCanStillPlay += 1; - if (alreadyPlayedALand) return 1; - alreadyPlayedALand = 1; - return 0; + landsPlayedThisTurn = landsPlayedThisTurnUpdated; + return 1; } - previous = source->controller()->landsPlayerCanStillPlay; return 0; } @@ -4756,9 +4752,18 @@ public: return 1; } + int destroy() + { + PlayRestrictions * restrictions = source->controller()->game->playRestrictions; + if(restrictions->getRestrictionById(PlayRestriction::LANDS_RULE_ID)) + return 1; + + restrictions->addRestriction(landsRestriction); + return 1; + } + virtual ostream& toString(ostream& out) const { - out << "AFastbond ::: alreadyPlayedALand : " << alreadyPlayedALand << " ; previous : " << previous << " ("; return TriggeredAbility::toString(out) << ")"; } AFastbond * clone() const @@ -4767,6 +4772,11 @@ public: a->isClone = 1; return a; } + + ~AFastbond() + { + delete counter; + } }; //1165 Hypnotic Specter diff --git a/projects/mtg/include/MTGAbility.h b/projects/mtg/include/MTGAbility.h index 2da4acd19..ee3d7c1c2 100644 --- a/projects/mtg/include/MTGAbility.h +++ b/projects/mtg/include/MTGAbility.h @@ -289,7 +289,7 @@ class InstantAbility:public MTGAbility{ virtual void Update(float dt); virtual int testDestroy(); InstantAbility(int _id, MTGCardInstance * source); - InstantAbility(int _id, MTGCardInstance * source,Damageable * _target); + InstantAbility(int _id, MTGCardInstance * source,Targetable * _target); virtual int resolve(){return 0;}; virtual InstantAbility* clone() const = 0; virtual ostream& toString(ostream& out) const; @@ -399,6 +399,13 @@ public: Targetable * getTarget(); }; +class InstantAbilityTP:public InstantAbility{ +public: + int who; + InstantAbilityTP(int id, MTGCardInstance * card, Targetable * _target = NULL, int who = TargetChooser::UNSET); + Targetable * getTarget(); +}; + class AManaProducer: public ActivatedAbilityTP{ protected: diff --git a/projects/mtg/include/MTGGameZones.h b/projects/mtg/include/MTGGameZones.h index ed76f7526..e2e95491f 100644 --- a/projects/mtg/include/MTGGameZones.h +++ b/projects/mtg/include/MTGGameZones.h @@ -6,6 +6,7 @@ using std::map; #include "MTGDeck.h" #include "MTGCardInstance.h" +#include "playRestrictions.h" #include "TargetChooser.h" #define MTG_MAX_PLAYER_CARDS 100 @@ -68,9 +69,12 @@ class MTGGameZone { }; Player * owner; - //Both cards and cardsMap contain the cards of a zone. The long term objective is to get rid of the array - vector cards; //[MTG_MAX_PLAYER_CARDS]; + //Both cards and cardsMap contain the cards of a zone. The vector is used to keep the order, useful for some zones such as the stack, library, etc... + vector cards; map cardsMap; + + //list of cards that have been through this zone in the current turn + vector cardsSeenThisTurn; int nb_cards; MTGGameZone(); ~MTGGameZone(); @@ -80,6 +84,8 @@ class MTGGameZone { MTGCardInstance * removeCard(MTGCardInstance * card, int createCopy = 1); MTGCardInstance * hasCard(MTGCardInstance * card); void cleanupPhase(); + void beforeBeginPhase(); + int countByType(const char * value); int countByCanTarget(TargetChooser * tc); MTGCardInstance * findByName(string name); @@ -91,6 +97,7 @@ class MTGGameZone { int hasName(string value); int hasColor(int value); //returns 1 if one of the cards in the zone has the color, 0 otherwise int hasX(); + int seenThisTurn(TargetChooser * tc); //How many cards matching a TargetChooser have been put in this zone during the turn void setOwner(Player * player); MTGCardInstance * lastCardDrawn; static MTGGameZone * stringToZone(string zoneName, MTGCardInstance * source, MTGCardInstance * target); @@ -145,7 +152,9 @@ class MTGPlayerCards { protected: void init(); - public: +public: + Player * owner; + PlayRestrictions * playRestrictions; MTGLibrary * library; MTGGraveyard * graveyard; MTGHand * hand; @@ -170,6 +179,7 @@ class MTGPlayerCards { void showHand(); void resetLibrary(); void initDeck(MTGDeck * deck); + void beforeBeginPhase(); MTGCardInstance * putInGraveyard(MTGCardInstance * card); MTGCardInstance * putInExile(MTGCardInstance * card); MTGCardInstance * putInLibrary(MTGCardInstance * card); diff --git a/projects/mtg/include/PlayRestrictions.h b/projects/mtg/include/PlayRestrictions.h new file mode 100644 index 000000000..9770edba6 --- /dev/null +++ b/projects/mtg/include/PlayRestrictions.h @@ -0,0 +1,61 @@ +#ifndef _PLAY_RESTRICTIONS_H_ +#define _PLAY_RESTRICTIONS_H_ + + +class TargetChooser; +class MTGCardInstance; +class MTGGameZone; + +class PlayRestriction +{ + +public: + enum + { + LANDS_RULE_ID, + UNDEF_ID + }; + + enum + { + CAN_PLAY, + CANT_PLAY, + NO_OPINION + }; + + unsigned int id; + TargetChooser * tc; + + virtual int canPutIntoZone(MTGCardInstance * card, MTGGameZone * destZone) = 0; + + PlayRestriction(unsigned int id, TargetChooser * tc); + ~PlayRestriction(); +}; + +class MaxPerTurnRestriction: public PlayRestriction +{ +public: + enum + { + NO_MAX = -1, + }; + int maxPerTurn; + MTGGameZone * zone; + MaxPerTurnRestriction(unsigned int id, TargetChooser * tc, int maxPerTurn, MTGGameZone * zone); + int canPutIntoZone(MTGCardInstance * card, MTGGameZone * destZone); +}; + + +class PlayRestrictions +{ +protected: + vectorrestrictions; +public: + PlayRestriction * getRestrictionById(unsigned int id); + void addRestriction(PlayRestriction * restriction); + void removeRestriction(PlayRestriction * restriction); + int canPutIntoZone(MTGCardInstance * card, MTGGameZone * destZone); + ~PlayRestrictions(); + +}; +#endif \ No newline at end of file diff --git a/projects/mtg/include/Player.h b/projects/mtg/include/Player.h index ace14be32..e8f9f2245 100644 --- a/projects/mtg/include/Player.h +++ b/projects/mtg/include/Player.h @@ -27,8 +27,6 @@ public: JTexture * mAvatarTex; JQuadPtr mAvatar; int playMode; - bool canPutLandsIntoPlay; - int landsPlayerCanStillPlay; bool nomaxhandsize; int castedspellsthisturn; bool onlyonecast; diff --git a/projects/mtg/src/AIMomirPlayer.cpp b/projects/mtg/src/AIMomirPlayer.cpp index 847deb7af..fdb6352f4 100644 --- a/projects/mtg/src/AIMomirPlayer.cpp +++ b/projects/mtg/src/AIMomirPlayer.cpp @@ -100,13 +100,15 @@ int AIMomirPlayer::computeActions() ManaCost * potentialMana = getPotentialMana(); int converted = potentialMana->getConvertedCost(); SAFE_DELETE(potentialMana); - if (canPutLandsIntoPlay && (converted < 8 || game->hand->nb_cards > 1)) + + if (converted < 8 || game->hand->nb_cards > 1) { //Attempt to put land into play cd.init(); cd.setColor(Constants::MTG_COLOR_LAND); card = cd.match(game->hand); - if (card) + int canPutLandsIntoPlay = game->playRestrictions->canPutIntoZone(card, game->inPlay); + if (card && (canPutLandsIntoPlay == PlayRestriction::CAN_PLAY)) { MTGAbility * putIntoPlay = g->mLayers->actionLayer()->getAbility(MTGAbility::PUT_INTO_PLAY); AIAction * a = NEW AIAction(putIntoPlay, card); //TODO putinplay action diff --git a/projects/mtg/src/AIPlayer.cpp b/projects/mtg/src/AIPlayer.cpp index 0312f3f03..8f77dab32 100644 --- a/projects/mtg/src/AIPlayer.cpp +++ b/projects/mtg/src/AIPlayer.cpp @@ -1221,7 +1221,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty continue; if (card->hasType(Subtypes::TYPE_INSTANT) && this->castrestrictedspell ) continue; - if (card->hasType(Subtypes::TYPE_LAND) && !this->canPutLandsIntoPlay) + if (card->hasType(Subtypes::TYPE_LAND) && (game->playRestrictions->canPutIntoZone(card, game->inPlay) == PlayRestriction::CANT_PLAY)) continue; if (card->hasType(Subtypes::TYPE_LEGENDARY) && game->inPlay->findByName(card->name)) continue; diff --git a/projects/mtg/src/AllAbilities.cpp b/projects/mtg/src/AllAbilities.cpp index 3d4e3bdfc..9c028599e 100644 --- a/projects/mtg/src/AllAbilities.cpp +++ b/projects/mtg/src/AllAbilities.cpp @@ -1456,13 +1456,13 @@ AACloner::~AACloner() } // More Land - allow more lands to be played on a turn -AAMoreLandPlz::AAMoreLandPlz(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, WParsedInt * _additional, - int _tap, int who) : - ActivatedAbilityTP(_id, card, _target, _cost, _tap, who), additional(_additional) +AMoreLandPlzUEOT::AMoreLandPlzUEOT(int _id, MTGCardInstance * card, Targetable * _target, WParsedInt * _additional, int who) : + InstantAbilityTP(_id, card, _target, who), additional(_additional) { + landsRestriction = NULL; } -int AAMoreLandPlz::resolve() +int AMoreLandPlzUEOT::addToGame() { Targetable * _target = getTarget(); Player * player; @@ -1476,25 +1476,38 @@ int AAMoreLandPlz::resolve() { player = (Player *) _target; } - player->landsPlayerCanStillPlay += additional->getValue(); + landsRestriction = (MaxPerTurnRestriction *) (player->game->playRestrictions->getRestrictionById(PlayRestriction::LANDS_RULE_ID)); + if(landsRestriction && landsRestriction->maxPerTurn != MaxPerTurnRestriction::NO_MAX) + landsRestriction->maxPerTurn += additional->getValue(); + return InstantAbility::addToGame(); } + return 0; +} + +int AMoreLandPlzUEOT::destroy() +{ + if (!landsRestriction) + return 0; + + if(landsRestriction && landsRestriction->maxPerTurn != MaxPerTurnRestriction::NO_MAX) + landsRestriction->maxPerTurn -= additional->getValue(); return 1; } -const char * AAMoreLandPlz::getMenuText() +const char * AMoreLandPlzUEOT::getMenuText() { return "Additional Lands"; } -AAMoreLandPlz * AAMoreLandPlz::clone() const +AMoreLandPlzUEOT * AMoreLandPlzUEOT::clone() const { - AAMoreLandPlz * a = NEW AAMoreLandPlz(*this); + AMoreLandPlzUEOT * a = NEW AMoreLandPlzUEOT(*this); a->additional = NEW WParsedInt(*(a->additional)); a->isClone = 1; return a; } -AAMoreLandPlz::~AAMoreLandPlz() +AMoreLandPlzUEOT::~AMoreLandPlzUEOT() { SAFE_DELETE(additional); } diff --git a/projects/mtg/src/GameObserver.cpp b/projects/mtg/src/GameObserver.cpp index 38ff0dc86..1ba21be7e 100644 --- a/projects/mtg/src/GameObserver.cpp +++ b/projects/mtg/src/GameObserver.cpp @@ -108,8 +108,6 @@ void GameObserver::nextGamePhase() if (currentGamePhase == Constants::MTG_PHASE_BEFORE_BEGIN) { cleanupPhase(); - currentPlayer->canPutLandsIntoPlay = true; - currentPlayer->landsPlayerCanStillPlay = 1; currentPlayer->castedspellsthisturn = 0; currentPlayer->opponent()->castedspellsthisturn = 0; currentPlayer->castcount = 0; @@ -124,9 +122,8 @@ void GameObserver::nextGamePhase() mLayers->actionLayer()->Update(0); for (int i = 0; i < 2; i++) { - delete (players[i]->game->garbage); - players[i]->game->garbage = NEW MTGGameZone(); - players[i]->game->garbage->setOwner(players[i]); + //Cleanup of each player's gamezones + players[i]->game->beforeBeginPhase(); } combatStep = BLOCKERS; return nextGamePhase(); @@ -377,14 +374,6 @@ void GameObserver::gameStateBasedEffects() //effects or menus actions for (int i = 0; i < 2; i++) { - if (players[i]->landsPlayerCanStillPlay <= 0) - { - players[i]->canPutLandsIntoPlay = false; - } - else - { - players[i]->canPutLandsIntoPlay = true; - } if(players[i]->poisonCount > 0) { players[i]->isPoisoned = true; diff --git a/projects/mtg/src/GameStateMenu.cpp b/projects/mtg/src/GameStateMenu.cpp index 64a519ace..5cd8c7968 100644 --- a/projects/mtg/src/GameStateMenu.cpp +++ b/projects/mtg/src/GameStateMenu.cpp @@ -725,9 +725,6 @@ void GameStateMenu::Render() void GameStateMenu::ButtonPressed(int controllerId, int controlId) { - int deckId; - int result; - DebugTrace("GameStateMenu: controllerId " << controllerId << " selected"); switch (controllerId) { diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index 171e8c9ce..9a9ebfff3 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -2206,9 +2206,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG Targetable * t = NULL; if (spell) t = spell->getNextTarget(); - MTGAbility * a = NEW AAMoreLandPlz(id, card, t, NULL, additional, 0, who); - a->oneShot = 1; - return a; + return NEW AMoreLandPlzUEOT(id, card, t, additional, who); } //Deplete @@ -4238,7 +4236,7 @@ void InstantAbility::Update(float dt) } } -InstantAbility::InstantAbility(int _id, MTGCardInstance * source, Damageable * _target) : +InstantAbility::InstantAbility(int _id, MTGCardInstance * source, Targetable * _target) : MTGAbility(_id, source, _target) { init = 0; @@ -4785,3 +4783,41 @@ Targetable * ActivatedAbilityTP::getTarget() } return NULL; } + +InstantAbilityTP::InstantAbilityTP(int id, MTGCardInstance * card, Targetable * _target,int who) : + InstantAbility(id, card), who(who) +{ + if (_target) + target = _target; +} + +//This is the same as Targetable * ActivatedAbilityTP::getTarget(), anyway to move them together? +Targetable * InstantAbilityTP::getTarget() +{ + switch (who) + { + case TargetChooser::TARGET_CONTROLLER: + if (target) + { + switch (target->typeAsTarget()) + { + case TARGET_CARD: + return ((MTGCardInstance *) target)->controller(); + case TARGET_STACKACTION: + return ((Interruptible *) target)->source->controller(); + default: + return (Player *) target; + } + } + return NULL; + case TargetChooser::CONTROLLER: + return source->controller(); + case TargetChooser::OPPONENT: + return source->controller()->opponent(); + case TargetChooser::OWNER: + return source->owner; + default: + return target; + } + return NULL; +} diff --git a/projects/mtg/src/MTGGameZones.cpp b/projects/mtg/src/MTGGameZones.cpp index df755bed5..ccc1ab117 100644 --- a/projects/mtg/src/MTGGameZones.cpp +++ b/projects/mtg/src/MTGGameZones.cpp @@ -68,10 +68,28 @@ MTGPlayerCards::~MTGPlayerCards() SAFE_DELETE(removedFromGame); SAFE_DELETE(garbage); SAFE_DELETE(temp); + SAFE_DELETE(playRestrictions); +} + +void MTGPlayerCards::beforeBeginPhase() +{ + delete garbage; + garbage = NEW MTGGameZone(); + garbage->setOwner(this->owner); + + library->beforeBeginPhase(); + graveyard->beforeBeginPhase(); + hand->beforeBeginPhase(); + inPlay->beforeBeginPhase(); + stack->beforeBeginPhase(); + removedFromGame->beforeBeginPhase(); + garbage->beforeBeginPhase(); + temp->beforeBeginPhase(); } void MTGPlayerCards::setOwner(Player * player) { + this->owner = player; library->setOwner(player); graveyard->setOwner(player); hand->setOwner(player); @@ -241,6 +259,11 @@ void MTGPlayerCards::init() exile = removedFromGame; garbage = NEW MTGGameZone(); temp = NEW MTGGameZone(); + + //This is a Rule that should ideally be moved as an ability in the game... + playRestrictions = NEW PlayRestrictions(); + TargetChooser * tc = NEW TypeTargetChooser("land"); + playRestrictions->addRestriction(NEW MaxPerTurnRestriction(PlayRestriction::LANDS_RULE_ID, tc, 1, inPlay)); } void MTGPlayerCards::showHand() @@ -312,7 +335,7 @@ MTGCardInstance * MTGPlayerCards::putInZone(MTGCardInstance * card, MTGGameZone //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 mve "from" 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 { @@ -391,6 +414,11 @@ MTGGameZone::~MTGGameZone() owner = NULL; } +void MTGGameZone::beforeBeginPhase() +{ + cardsSeenThisTurn.clear(); +}; + void MTGGameZone::setOwner(Player * player) { for (int i = 0; i < nb_cards; i++) @@ -583,6 +611,22 @@ int MTGGameZone::hasAbility(int ability) return 0; } +int MTGGameZone::seenThisTurn(TargetChooser * tc) +{ + //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; + for (vector::iterator iter = cardsSeenThisTurn.begin(); iter != cardsSeenThisTurn.end(); ++iter) + { + if (tc->canTarget(*iter)) + count++; + } + return count; +} + + void MTGGameZone::cleanupPhase() { for (int i = 0; i < (nb_cards); i++) @@ -599,6 +643,7 @@ 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; diff --git a/projects/mtg/src/MTGRules.cpp b/projects/mtg/src/MTGRules.cpp index 0333d6260..5a12709e1 100644 --- a/projects/mtg/src/MTGRules.cpp +++ b/projects/mtg/src/MTGRules.cpp @@ -35,11 +35,14 @@ int MTGPutInPlayRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana) } return 1; } -if(!allowedToCast(card,player)) - return 0; + if(!allowedToCast(card,player)) + return 0; + if (card->hasType("land")) { - if (player == currentPlayer && currentPlayer->canPutLandsIntoPlay + if (currentPlayer->game->playRestrictions->canPutIntoZone(card, currentPlayer->game->inPlay) == PlayRestriction::CANT_PLAY) + return 0; + if (player == currentPlayer && (game->currentGamePhase == Constants::MTG_PHASE_FIRSTMAIN || game->currentGamePhase == Constants::MTG_PHASE_SECONDMAIN) ) { @@ -58,7 +61,7 @@ if(!allowedToCast(card,player)) #ifdef WIN32 cost->Dump(); #endif - if (player->castrestrictedspell && !card->hasType("land")) + if (player->castrestrictedspell) { return 0; } @@ -154,7 +157,6 @@ int MTGPutInPlayRule::reactToClick(MTGCardInstance * card) spell->resolve(); delete spellCost; delete spell; - player->landsPlayerCanStillPlay--; } else { @@ -259,7 +261,9 @@ int MTGAlternativeCostRule::isReactingToClick(MTGCardInstance * card, ManaCost * if (card->hasType("land")) { - if (player == currentPlayer && currentPlayer->canPutLandsIntoPlay + if (currentPlayer->game->playRestrictions->canPutIntoZone(card, currentPlayer->game->inPlay) == PlayRestriction::CANT_PLAY) + return 0; + if (player == currentPlayer && (game->currentGamePhase == Constants::MTG_PHASE_FIRSTMAIN || game->currentGamePhase == Constants::MTG_PHASE_SECONDMAIN) ) @@ -358,7 +362,6 @@ int MTGAlternativeCostRule::reactToClick(MTGCardInstance * card, ManaCost *alter copy->alternateCostPaid[alternateCostType] = 1; spell->resolve(); SAFE_DELETE(spell); - player->landsPlayerCanStillPlay--; game->mLayers->stackLayer()->addSpell(copy, NULL, NULL, alternateCostType, 1); } else diff --git a/projects/mtg/src/PlayRestrictions.cpp b/projects/mtg/src/PlayRestrictions.cpp new file mode 100644 index 000000000..57123410d --- /dev/null +++ b/projects/mtg/src/PlayRestrictions.cpp @@ -0,0 +1,95 @@ +#include "PrecompiledHeader.h" + +#include "playRestrictions.h" +#include "TargetChooser.h" +#include "MTGCardInstance.h" + + +PlayRestriction::PlayRestriction(unsigned int id, TargetChooser * tc): id(id), tc(tc) +{ + tc->setAllZones(); // This is to allow targetting cards without caring about the actual zone + tc->targetter = NULL; +}; + +PlayRestriction::~PlayRestriction() +{ + SAFE_DELETE(tc); +}; + + +MaxPerTurnRestriction::MaxPerTurnRestriction(unsigned int id, TargetChooser * tc, int maxPerTurn, MTGGameZone * zone): + PlayRestriction(id, tc), maxPerTurn(maxPerTurn), zone(zone) + {} + +int MaxPerTurnRestriction::canPutIntoZone(MTGCardInstance * card, MTGGameZone * destZone) +{ + if (destZone != zone) + return PlayRestriction::NO_OPINION; + + if (!tc->canTarget(card)) + return PlayRestriction::NO_OPINION; + + if (maxPerTurn == NO_MAX) return PlayRestriction::CAN_PLAY; + + if (zone->seenThisTurn(tc) >= maxPerTurn) + return PlayRestriction::CANT_PLAY; + + return PlayRestriction::CAN_PLAY; +}; + + +PlayRestriction * PlayRestrictions::getRestrictionById(unsigned int id) +{ + if (id == PlayRestriction::UNDEF_ID) + return NULL; // do not request Restrictions that don't have an id, there are several of them + + for (vector::iterator iter = restrictions.begin(); iter != restrictions.end(); ++iter) + { + if ((*iter)->id == id) + return *iter; + } + + return NULL; +} + +void PlayRestrictions::addRestriction(PlayRestriction * restriction) +{ + //TODO control that the id does not already exist? + restrictions.push_back(restriction); + +} + +void PlayRestrictions::removeRestriction(PlayRestriction * restriction) +{ + for (vector::iterator iter = restrictions.begin(); iter != restrictions.end(); ++iter) + { + if(*iter == restriction) + { + restrictions.erase(iter); + return; + } + } +} + +int PlayRestrictions::canPutIntoZone(MTGCardInstance * card, MTGGameZone * destZone) +{ + if (!card) + return PlayRestriction::CANT_PLAY; + + for (vector::iterator iter = restrictions.begin(); iter != restrictions.end(); ++iter) + { + if ((*iter)->canPutIntoZone(card, destZone) == PlayRestriction::CANT_PLAY) + return PlayRestriction::CANT_PLAY; + } + + return PlayRestriction::CAN_PLAY; +} + +PlayRestrictions::~PlayRestrictions() +{ + for (vector::iterator iter = restrictions.begin(); iter != restrictions.end(); ++iter) + { + SAFE_DELETE(*iter); + } + restrictions.clear(); +} diff --git a/projects/mtg/src/Player.cpp b/projects/mtg/src/Player.cpp index 65dedf6df..4ad722e34 100644 --- a/projects/mtg/src/Player.cpp +++ b/projects/mtg/src/Player.cpp @@ -12,8 +12,6 @@ Damageable(20) deckFile = file; deckFileSmall = fileSmall; manaPool = NEW ManaPool(this); - canPutLandsIntoPlay = true; - landsPlayerCanStillPlay = 1; nomaxhandsize = false; castedspellsthisturn = 0; castrestrictedspell = false; diff --git a/projects/mtg/src/TestSuiteAI.cpp b/projects/mtg/src/TestSuiteAI.cpp index aa0d8059b..367c15050 100644 --- a/projects/mtg/src/TestSuiteAI.cpp +++ b/projects/mtg/src/TestSuiteAI.cpp @@ -421,6 +421,7 @@ void TestSuite::initGame() } } } + zone->cardsSeenThisTurn.clear(); //don't consider those cards as having moved in this area during this turn } } DebugTrace("TESTUITE Init Game Done !"); diff --git a/projects/mtg/template.vcproj b/projects/mtg/template.vcproj index 7baa10400..e86477df2 100644 --- a/projects/mtg/template.vcproj +++ b/projects/mtg/template.vcproj @@ -769,6 +769,10 @@ RelativePath=".\src\PlayGuiObjectController.cpp" > + + @@ -1234,6 +1238,10 @@ RelativePath=".\include\PlayGuiObjectController.h" > + +