diff --git a/projects/mtg/Makefile b/projects/mtg/Makefile index 5f60bff1c..4d0d84091 100644 --- a/projects/mtg/Makefile +++ b/projects/mtg/Makefile @@ -1,4 +1,4 @@ -OBJS = objs/ActionElement.o objs/ActionLayer.o objs/ActionStack.o objs/AIPlayer.o objs/AIStats.o objs/Blocker.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/ConstraintResolver.o objs/Counters.o objs/Damage.o objs/DamagerDamaged.o objs/DamageResolverLayer.o objs/DeckDataWrapper.o objs/DeckStats.o objs/DuelLayers.o objs/ExtraCost.o objs/GameApp.o objs/GameLauncher.o objs/GameObserver.o objs/GameOptions.o objs/GameStateDuel.o objs/GameStateMenu.o objs/GameStateOptions.o objs/GameStateShop.o objs/GuiCardsController.o objs/GuiLayers.o objs/Logger.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/MTGGuiHand.o objs/MTGGuiPlay.o objs/MTGRules.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/PriceList.o objs/ShopItem.o objs/SimpleMenu.o objs/SimpleMenuItem.o objs/Subtypes.o objs/TargetChooser.o objs/TargetsList.o objs/TexturesCache.o objs/Token.o objs/utils.o objs/WEvent.o +OBJS = objs/ActionElement.o objs/ActionLayer.o objs/ActionStack.o objs/AIMomirPlayer.o objs/AIPlayer.o objs/AIStats.o objs/Blocker.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/ConstraintResolver.o objs/Counters.o objs/Damage.o objs/DamagerDamaged.o objs/DamageResolverLayer.o objs/DeckDataWrapper.o objs/DeckStats.o objs/DuelLayers.o objs/ExtraCost.o objs/GameApp.o objs/GameLauncher.o objs/GameObserver.o objs/GameOptions.o objs/GameStateDuel.o objs/GameStateMenu.o objs/GameStateOptions.o objs/GameStateShop.o objs/GuiCardsController.o objs/GuiLayers.o objs/Logger.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/MTGGuiHand.o objs/MTGGuiPlay.o objs/MTGRules.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/PriceList.o objs/ShopItem.o objs/SimpleMenu.o objs/SimpleMenuItem.o objs/Subtypes.o objs/TargetChooser.o objs/TargetsList.o objs/TexturesCache.o objs/Token.o objs/utils.o objs/WEvent.o DEPS = $(patsubst objs/%.o, deps/%.d, $(OBJS)) RESULT = $(shell psp-config --psp-prefix 2> Makefile.cache) diff --git a/projects/mtg/bin/Res/graphics/momir_unlocked.png b/projects/mtg/bin/Res/graphics/momir_unlocked.png new file mode 100644 index 000000000..9f66dc718 Binary files /dev/null and b/projects/mtg/bin/Res/graphics/momir_unlocked.png differ diff --git a/projects/mtg/bin/Res/player/momir.txt b/projects/mtg/bin/Res/player/momir.txt new file mode 100644 index 000000000..3b0c041f0 --- /dev/null +++ b/projects/mtg/bin/Res/player/momir.txt @@ -0,0 +1,5 @@ +Swamp *12 +Island *12 +Plains *12 +Mountain *12 +Forest *12 \ No newline at end of file diff --git a/projects/mtg/include/AIMomirPlayer.h b/projects/mtg/include/AIMomirPlayer.h new file mode 100644 index 000000000..67acd1815 --- /dev/null +++ b/projects/mtg/include/AIMomirPlayer.h @@ -0,0 +1,17 @@ +#ifndef _AIMOMIRPLAYER_H_ +#define _AIMOMIRPLAYER_H_ + +#include "AIPlayer.h" + + +class AIMomirPlayer:public AIPlayerBaka{ +public: + AIMomirPlayer(MTGPlayerCards * _deck, char * file, char * avatarFile); + int getEfficiency(AIAction * action); + int momir(); + int computeActions(); + static MTGAbility * momirAbility; + static MTGAbility * getMomirAbility(); +}; + +#endif \ No newline at end of file diff --git a/projects/mtg/include/AIPlayer.h b/projects/mtg/include/AIPlayer.h index 436b8048a..c93689fc8 100644 --- a/projects/mtg/include/AIPlayer.h +++ b/projects/mtg/include/AIPlayer.h @@ -71,6 +71,7 @@ class AIPlayer: public Player{ int selectAbility(); int createAbilityTargets(MTGAbility * a, MTGCardInstance * c, map * ranking); int useAbility(); + virtual int getEfficiency(AIAction * action); }; @@ -84,7 +85,7 @@ class AIPlayerBaka: public AIPlayer{ AIPlayerBaka(MTGPlayerCards * _deck, char * deckFile, char * avatarFile); virtual int Act(float dt); void initTimer(); - int computeActions(); + virtual int computeActions(); }; class AIPlayerFactory{ diff --git a/projects/mtg/include/ActionLayer.h b/projects/mtg/include/ActionLayer.h index 3a24f5214..0ff6fab21 100644 --- a/projects/mtg/include/ActionLayer.h +++ b/projects/mtg/include/ActionLayer.h @@ -10,6 +10,7 @@ #include "GuiLayers.h" #include "ActionElement.h" #include "SimpleMenu.h" +#include "MTGAbility.h" class GuiLayer; class Targetable; @@ -36,6 +37,7 @@ class ActionLayer: public GuiLayer, public JGuiListener{ void ButtonPressed(int controllerid, int controlid); void doReactTo(int menuIndex); TargetChooser * getCurrentTargetChooser(); + MTGAbility * getAbility(int type); }; diff --git a/projects/mtg/include/GameApp.h b/projects/mtg/include/GameApp.h index 3eccd69da..5f3f1d110 100644 --- a/projects/mtg/include/GameApp.h +++ b/projects/mtg/include/GameApp.h @@ -41,6 +41,9 @@ #define PLAYER_TYPE_TESTSUITE 2 +#define GAME_TYPE_CLASSIC 0 +#define GAME_TYPE_MOMIR 1 + class MTGAllCards; class TexturesCache; @@ -63,6 +66,7 @@ class GameApp: public JApp public: int players[2]; MTGAllCards * collection; + int gameType; TexturesCache * cache; GameApp(); diff --git a/projects/mtg/include/GameOptions.h b/projects/mtg/include/GameOptions.h index 745042bc5..f76c34824 100644 --- a/projects/mtg/include/GameOptions.h +++ b/projects/mtg/include/GameOptions.h @@ -9,6 +9,7 @@ using std::string; #define OPTIONS_MUSICVOLUME "musicVolume" #define OPTIONS_SFXVOLUME "sfxVolume" #define OPTIONS_DIFFICULTY_MODE_UNLOCKED "prx_handler" //huhu +#define OPTIONS_MOMIR_MODE_UNLOCKED "prx_rimom" //haha #define OPTIONS_DIFFICULTY "difficulty" // WALDORF - added diff --git a/projects/mtg/include/GameStateDuel.h b/projects/mtg/include/GameStateDuel.h index cc14b0b21..1c34d4be5 100644 --- a/projects/mtg/include/GameStateDuel.h +++ b/projects/mtg/include/GameStateDuel.h @@ -33,7 +33,9 @@ class GameStateDuel: public GameState, public JGuiListener JQuad * unlockedQuad; JTexture * unlockedTex; int isDifficultyUnlocked(); + int isMomirUnlocked(); void loadPlayer(int playerId, int decknb = 0, int isAI = 0); + void loadPlayerMomir(int playerId, int isAI); public: GameStateDuel(GameApp* parent); virtual ~GameStateDuel(); diff --git a/projects/mtg/include/GameStateMenu.h b/projects/mtg/include/GameStateMenu.h index a488a3756..ff0d9147d 100644 --- a/projects/mtg/include/GameStateMenu.h +++ b/projects/mtg/include/GameStateMenu.h @@ -11,6 +11,8 @@ class GameStateMenu: public GameState, public JGuiListener private: JGuiController* mGuiController; SimpleMenu* subMenuController; + SimpleMenu* gameTypeMenu; + int hasChosenGameType; JQuad * mIcons[10]; JTexture * mIconsTexture; JTexture * bgTexture; diff --git a/projects/mtg/include/MTGAbility.h b/projects/mtg/include/MTGAbility.h index 6e1987218..852aa04ba 100644 --- a/projects/mtg/include/MTGAbility.h +++ b/projects/mtg/include/MTGAbility.h @@ -60,12 +60,16 @@ class MTGAbility: public ActionElement{ virtual int resolve(){return 0;}; /*Poor man's casting */ + /* Todo replace that crap with dynamic casting */ enum { UNKNOWN = 0, MANA_PRODUCER = 1, MTG_ATTACK_RULE = 2, DAMAGER = 3, STANDARD_REGENERATE = 4, + PUT_INTO_PLAY = 5, + MOMIR = 6, + MTG_BLOCK_RULE = 7, }; }; diff --git a/projects/mtg/include/MTGGameZones.h b/projects/mtg/include/MTGGameZones.h index 1fa83811b..e57bc494b 100644 --- a/projects/mtg/include/MTGGameZones.h +++ b/projects/mtg/include/MTGGameZones.h @@ -85,6 +85,7 @@ class MTGPlayerCards { MTGInPlay * inPlay; MTGStack * stack; MTGRemovedFromGame * removedFromGame; + MTGGameZone * garbage; MTGAllCards * collection; diff --git a/projects/mtg/include/MTGRules.h b/projects/mtg/include/MTGRules.h index 3b782e519..10a18bab7 100644 --- a/projects/mtg/include/MTGRules.h +++ b/projects/mtg/include/MTGRules.h @@ -81,62 +81,6 @@ OutputDebugString("Receive5\n"); }; -/* -class MTGPersistRule:public ListMaintainerAbility{ - public: - MTGPersistRule(int _id):ListMaintainerAbility(_id){}; - - virtual void Update(float dt){ - map::iterator it; - - for ( it=cards.begin() ; it != cards.end(); it++ ){ - MTGCardInstance * card = ((*it).first); - Player * p = card->controller(); - if (p->game->graveyard->hasCard(card)){ - p->game->putInZone(card, p->game->graveyard, p->game->hand); - Spell * spell = NEW Spell(card); - p->game->putInZone(card, p->game->hand, p->game->stack); - spell->resolve(); - delete spell; - card->counters->addCounter(-1,-1); - } - } - - // Dirtiest Code Ever, we remove the counters here - - for (int i = 0; i < 2; i++){ - Player * p = game->players[i]; - MTGGameZone * zones[] = {p->game->graveyard, p->game->hand, p->game->library, p->game->removedFromGame}; - for (int j = 0; j < 5; j++){ - MTGGameZone * zone = zones[j]; - for (int k=0; k < zone->nb_cards; k++){ - zone->cards[k]->counters->init(); - } - } - } - - - ListMaintainerAbility::Update(dt); - } - - int canBeInList(MTGCardInstance * card){ - if (card->basicAbilities[Constants::PERSIST] && !card->counters->hasCounter(-1,-1) ){ -#if defined (WIN32) || defined (LINUX) - OutputDebugString("yay, persist !\n"); -#endif - return 1; - } - return 0; - } - - int added(MTGCardInstance * card){return 1;} - - int removed(MTGCardInstance * card){return 0;} - - int testDestroy(){return 0;} -}; - -*/ /* * Rule 420.5e (Legend Rule) @@ -176,4 +120,18 @@ class MTGLegendRule:public ListMaintainerAbility{ int testDestroy(){return 0;} }; + +class MTGMomirRule:public MTGAbility{ +public: + int alreadyplayed; + MTGAllCards * collection; + MTGCardInstance * genRandomCreature(int convertedCost); + int testDestroy(); + void Update(float dt); + MTGMomirRule(int _id, MTGAllCards * _collection); + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL); + int reactToClick(MTGCardInstance * card); + const char * getMenuText(){return "Momir";} +}; + #endif diff --git a/projects/mtg/include/ManaCost.h b/projects/mtg/include/ManaCost.h index ac3d6759f..5846bee92 100644 --- a/projects/mtg/include/ManaCost.h +++ b/projects/mtg/include/ManaCost.h @@ -22,7 +22,7 @@ class ManaCost{ static ManaCost * parseManaCost(string value, ManaCost * _manacost = NULL); void init(); void x(); - ManaCost(int _cost[], int nb_elems); + ManaCost(int _cost[], int nb_elems = 1); ManaCost(); ~ManaCost(); ManaCost(ManaCost * _manaCost); diff --git a/projects/mtg/src/AIMomirPlayer.cpp b/projects/mtg/src/AIMomirPlayer.cpp new file mode 100644 index 000000000..883f3d220 --- /dev/null +++ b/projects/mtg/src/AIMomirPlayer.cpp @@ -0,0 +1,192 @@ +#include "../include/config.h" +#include "../include/AIMomirPlayer.h" +#include "../include/CardDescriptor.h" +#include "../include/DamageResolverLayer.h" +#include "../include/DamagerDamaged.h" +#include "../include/AIStats.h" +#include "../include/AllAbilities.h" + + +MTGAbility * AIMomirPlayer::momirAbility = NULL; + +AIMomirPlayer::AIMomirPlayer(MTGPlayerCards * _deck, char * file, char * avatarFile): AIPlayerBaka(_deck,file, avatarFile){ +// TODO count min and max number of mana (should probably be part of the gameobserver so that human players don't make mistakes) + momirAbility = NULL; +} + +int AIMomirPlayer::getEfficiency(AIAction * action){ + + +int efficiency = AIPlayerBaka::getEfficiency(action); +if (efficiency < 15) return 0; +GameObserver * g = GameObserver::GetInstance(); +if (g->getCurrentGamePhase() < Constants::MTG_PHASE_FIRSTMAIN) return 0; + return efficiency; +} + +MTGAbility * AIMomirPlayer::getMomirAbility(){ + if (momirAbility) return momirAbility; + + GameObserver * g = GameObserver::GetInstance(); + momirAbility = g->mLayers->actionLayer()->getAbility(MTGAbility::MOMIR); + return momirAbility; +} + +int AIMomirPlayer::momir(){ + if (!game->hand->nb_cards) return 0; //nothing to discard :/ + getPotentialMana(); + int converted = potentialMana->getConvertedCost(); + int efficiency = 100; + int chance = 1 + (rand() % 100); + if (converted == 5) efficiency = 5 ; //Strategy: skip 5 drop + if (converted == 7) efficiency = 50; //Strategy: 7 drops have bad upkeep costs and the AI doesn't handle those right now... + if (converted > 8 ) converted = 8; + if (converted == 8) efficiency = 100 - game->inPlay->nb_cards; + + if (efficiency >= chance){ + int _cost[] = {Constants::MTG_COLOR_ARTIFACT,converted}; + ManaCost * cost = NEW ManaCost(_cost); + tapLandsForMana(potentialMana,cost); + delete cost; + MTGAbility * ability = getMomirAbility(); + MTGCardInstance * card = game->hand->cards[0]; + if (ability->isReactingToClick(card,cost)){ + AIAction * a = NEW AIAction(ability,card); + clickstream.push(a); + return 1; + } + } + return 0; +} + +int AIMomirPlayer::computeActions(){ +//Part of the strategy goes here. When should we put a land into play ? +/* +Another gift from Alex Majlaton on my first day playing Momir, and it has served me well ever since. It goes a little something like this: (a) if you are on the play, hit your Two through Four, skip your Five, and then hit all the way to Eight; (b) if you are on the draw and your opponent skips his One, you make Two through Eight; (c) if you are on the draw and your opponent hits a One, you match him drop-for-drop for the rest of the game. + +You skip your Five on the play because it is the weakest drop. There are plenty of serviceable guys there, but very few bombs compared to other drops +the general rule is this: if you want to get to Eight, you have to skip two drops on the play and one drop on the draw. +*/ +GameObserver * g = GameObserver::GetInstance(); + Player * p = g->currentPlayer; + if (!(g->currentlyActing() == this)) return 0; + if (chooseTarget()) return 1; + int currentGamePhase = g->getCurrentGamePhase(); + if (g->isInterrupting == this){ // interrupting + selectAbility(); + return 1; + }else if (p == this && g->mLayers->stackLayer()->count(0,NOT_RESOLVED) == 0){ //standard actions + CardDescriptor cd; + MTGCardInstance * card = NULL; + //No mana, try to get some + getPotentialMana(); + + + switch(currentGamePhase){ + case Constants::MTG_PHASE_FIRSTMAIN: + if (canPutLandsIntoPlay && (potentialMana->getConvertedCost() <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){ + MTGAbility * putIntoPlay = g->mLayers->actionLayer()->getAbility(MTGAbility::PUT_INTO_PLAY); + AIAction * a = NEW AIAction(putIntoPlay,card); //TODO putinplay action + clickstream.push(a); + return 1; + } + } + momir(); + return 1; + break; + case Constants::MTG_PHASE_SECONDMAIN: + selectAbility(); + return 1; + break; + default: + return AIPlayerBaka::computeActions(); + break; + } + }else{ + return AIPlayerBaka::computeActions(); + } +} + +/* +int AIPlayerBaka::computeActions(){ + GameObserver * g = GameObserver::GetInstance(); + Player * p = g->currentPlayer; + if (!(g->currentlyActing() == this)) return 0; + if (chooseTarget()) return 1; + int currentGamePhase = g->getCurrentGamePhase(); + if (g->isInterrupting == this){ // interrupting + selectAbility(); + return 1; + }else if (p == this && g->mLayers->stackLayer()->count(0,NOT_RESOLVED) == 0){ //standard actions + CardDescriptor cd; + MTGCardInstance * card = NULL; + switch(currentGamePhase){ + case Constants::MTG_PHASE_FIRSTMAIN: + case Constants::MTG_PHASE_SECONDMAIN: + if (canPutLandsIntoPlay){ + //Attempt to put land into play + cd.init(); + cd.setColor(Constants::MTG_COLOR_LAND); + card = cd.match(game->hand); + if (card){ + AIAction * a = NEW AIAction(card); + clickstream.push(a); + return 1; + } + } + + //No mana, try to get some + getPotentialMana(); + if (potentialMana->getConvertedCost() > 0){ + + + //look for the most expensive creature we can afford + nextCardToPlay = FindCardToPlay(potentialMana, "creature"); + //Let's Try an enchantment maybe ? + if (!nextCardToPlay) nextCardToPlay = FindCardToPlay(potentialMana, "enchantment"); + if (!nextCardToPlay) nextCardToPlay = FindCardToPlay(potentialMana, "artifact"); + if (!nextCardToPlay) nextCardToPlay = FindCardToPlay(potentialMana, "instant"); + if (!nextCardToPlay) nextCardToPlay = FindCardToPlay(potentialMana, "sorcery"); + if (nextCardToPlay){ +#if defined (WIN32) || defined (LINUX) + char buffe[4096]; + sprintf(buffe, "Putting Card Into Play: %s", nextCardToPlay->getName()); + OutputDebugString(buffe); +#endif + + tapLandsForMana(potentialMana,nextCardToPlay->getManaCost()); + AIAction * a = NEW AIAction(nextCardToPlay); + clickstream.push(a); + return 1; + }else{ + selectAbility(); + } + }else{ + selectAbility(); + } + break; + case Constants::MTG_PHASE_COMBATATTACKERS: + chooseAttackers(); + break; + default: + selectAbility(); + break; + } + }else{ + switch(currentGamePhase){ + case Constants::MTG_PHASE_COMBATBLOCKERS: + chooseBlockers(); + break; + default: + break; + } + return 1; + } + return 1; +}; +*/ diff --git a/projects/mtg/src/AIPlayer.cpp b/projects/mtg/src/AIPlayer.cpp index 3abcacb6e..6a7ae2193 100644 --- a/projects/mtg/src/AIPlayer.cpp +++ b/projects/mtg/src/AIPlayer.cpp @@ -121,6 +121,10 @@ ManaCost * AIPlayer::getPotentialMana(){ } +int AIPlayer::getEfficiency(AIAction * action){ + return action->getEfficiency(); +} + int AIAction::getEfficiency(){ //TODO add multiplier according to what the player wants if (efficiency != -1) return efficiency; @@ -129,6 +133,7 @@ int AIAction::getEfficiency(){ ActionStack * s = g->mLayers->stackLayer(); Player * p = g->currentlyActing(); if (s->has(ability)) return 0; + if (ability->cost && !(ability->cost->isExtraPaymentSet())) return 0; //Does not handle abilities with sacrifice yet switch (ability->aType){ case MTGAbility::DAMAGER: { @@ -226,7 +231,7 @@ int AIPlayer::selectAbility(){ OutputDebugString("We have a winner\n"); AIAction * a = ranking.begin()->first; int chance = 1 + rand() % 100; - if (a->getEfficiency() < chance){ + if (getEfficiency(a) < chance){ a = NULL; }else{ OutputDebugString("We REALLY have a winner\n"); @@ -262,6 +267,8 @@ int AIPlayer::effectBadOrGood(MTGCardInstance * card){ return BAKA_EFFECT_DONTKNOW; } + + int AIPlayer::chooseTarget(TargetChooser * tc){ Targetable * potentialTargets[50]; int nbtargets = 0; @@ -393,24 +400,25 @@ int AIPlayer::chooseBlockers(){ cd.setType("Creature"); cd.tapped = -1; MTGCardInstance * card = NULL; + GameObserver * g = GameObserver::GetInstance(); while((card = cd.nextmatch(game->inPlay, card))){ - GameObserver::GetInstance()->cardClick(card); + g->cardClick(card); + if (g->mLayers->actionLayer()->menuObject) g->mLayers->actionLayer()->doReactTo(0); int set = 0; while(!set){ if (!card->defenser){ - set = 1; + set = 1; }else{ - MTGCardInstance * attacker = card->defenser; - map::iterator it = opponentsToughness.find(attacker); - if ( it == opponentsToughness.end()){ - opponentsToughness[attacker] = attacker->toughness; - it = opponentsToughness.find(attacker); - } - if (opponentsToughness[attacker] > 0 && getStats() && getStats()->isInTop(attacker,3,false)){ - opponentsToughness[attacker]-= card->power; - set = 1; - }else{ - GameObserver * g = GameObserver::GetInstance(); + MTGCardInstance * attacker = card->defenser; + map::iterator it = opponentsToughness.find(attacker); + if ( it == opponentsToughness.end()){ + opponentsToughness[attacker] = attacker->toughness; + it = opponentsToughness.find(attacker); + } + if (opponentsToughness[attacker] > 0 && getStats() && getStats()->isInTop(attacker,3,false)){ + opponentsToughness[attacker]-= card->power; + set = 1; + }else{ g->cardClick(card); if (g->mLayers->actionLayer()->menuObject) g->mLayers->actionLayer()->doReactTo(0); } @@ -421,7 +429,7 @@ int AIPlayer::chooseBlockers(){ while((card = cd.nextmatch(game->inPlay, card))){ if (card->defenser && opponentsToughness[card->defenser] > 0){ while (card->defenser){ - GameObserver * g = GameObserver::GetInstance(); + g->cardClick(card); if (g->mLayers->actionLayer()->menuObject) g->mLayers->actionLayer()->doReactTo(0); } @@ -430,7 +438,6 @@ int AIPlayer::chooseBlockers(){ card = NULL; while((card = cd.nextmatch(game->inPlay, card))){ if(!card->defenser){ - GameObserver * g = GameObserver::GetInstance(); g->cardClick(card); if (g->mLayers->actionLayer()->menuObject) g->mLayers->actionLayer()->doReactTo(0); int set = 0; @@ -440,7 +447,6 @@ int AIPlayer::chooseBlockers(){ }else{ MTGCardInstance * attacker = card->defenser; if (opponentsToughness[attacker] <= 0 || (card->toughness <= card->defenser->power && opponentForce*2 defenser->nbOpponents()>1){ - GameObserver * g = GameObserver::GetInstance(); g->cardClick(card); if (g->mLayers->actionLayer()->menuObject) g->mLayers->actionLayer()->doReactTo(0); }else{ diff --git a/projects/mtg/src/ActionLayer.cpp b/projects/mtg/src/ActionLayer.cpp index af83bfbd3..b8e198922 100644 --- a/projects/mtg/src/ActionLayer.cpp +++ b/projects/mtg/src/ActionLayer.cpp @@ -4,6 +4,16 @@ #include "../include/Targetable.h" #include "../include/WEvent.h" +MTGAbility* ActionLayer::getAbility(int type){ + for (int i = 1; i < mCount; i++){ + MTGAbility * a = ((MTGAbility *)mObjects[i]); + if (a->aType == type){ + return a; + } + } + return NULL; +} + int ActionLayer::unstopableRenderInProgress(){ for (int i=0;istackLayer()->garbageCollect(); //clean stack history for this turn; mLayers->actionLayer()->Update(0); + for (int i=0; i < 2; i++){ + delete (players[i]->game->garbage); + players[i]->game->garbage = NEW MTGGameZone(); + } return nextGamePhase(); } diff --git a/projects/mtg/src/GameStateDuel.cpp b/projects/mtg/src/GameStateDuel.cpp index 44acb5304..e82798ba0 100644 --- a/projects/mtg/src/GameStateDuel.cpp +++ b/projects/mtg/src/GameStateDuel.cpp @@ -3,9 +3,10 @@ #include "../include/GameOptions.h" #include "../include/utils.h" #include "../include/AIPlayer.h" +#include "../include/AIMomirPlayer.h" #include "../include/PlayerData.h" #include "../include/DeckStats.h" - +#include "../include/MTGRules.h" #ifdef TESTSUITE #include "../include/TestSuiteAI.h" @@ -89,18 +90,18 @@ void GameStateDuel::Start() for (int i = 0; i<2; i ++){ if (mParent->players[i] == PLAYER_TYPE_HUMAN){ if (!deckmenu){ - decksneeded = 1; - deckmenu = NEW SimpleMenu(DUEL_MENU_CHOOSE_DECK, this, mFont, 35, 25, "Choose a Deck"); - char buffer[100]; - for (int j=1; j<6; j++){ - sprintf(buffer, RESPATH"/player/deck%i.txt",j); - std::ifstream file(buffer); - if(file){ - deckmenu->Add(j, GameState::menuTexts[j]); - file.close(); - decksneeded = 0; - } - } + decksneeded = 1; + deckmenu = NEW SimpleMenu(DUEL_MENU_CHOOSE_DECK, this, mFont, 35, 25, "Choose a Deck"); + char buffer[100]; + for (int j=1; j<6; j++){ + sprintf(buffer, RESPATH"/player/deck%i.txt",j); + std::ifstream file(buffer); + if(file){ + deckmenu->Add(j, GameState::menuTexts[j]); + file.close(); + decksneeded = 0; + } + } } } } @@ -110,6 +111,19 @@ void GameStateDuel::Start() } +void GameStateDuel::loadPlayerMomir(int playerId, int isAI){ + char * deckFile = RESPATH"/player/momir.txt"; + char * deckFileSmall = "momir"; + MTGDeck * tempDeck = NEW MTGDeck(deckFile, NULL, mParent->collection); + deck[playerId] = NEW MTGPlayerCards(mParent->collection,tempDeck); + if (!isAI){ //Human Player + mPlayers[playerId] = NEW HumanPlayer(deck[playerId],deckFileSmall); + }else{ + mPlayers[playerId] = NEW AIMomirPlayer(deck[playerId],deckFile,""); + } + delete tempDeck; +} + void GameStateDuel::loadPlayer(int playerId, int decknb, int isAI){ if (decknb){ if (!isAI){ //Human Player @@ -197,7 +211,14 @@ void GameStateDuel::Update(float dt) mParent->SetNextState(GAME_STATE_DECK_VIEWER); break; case DUEL_STATE_CHOOSE_DECK1: - if (mParent->players[0] == PLAYER_TYPE_HUMAN) + if (mParent->gameType == GAME_TYPE_MOMIR){ + for (int i = 0; i < 2; i++){ + int isAI = 1; + if (mParent->players[i] == PLAYER_TYPE_HUMAN) isAI = 0; + loadPlayerMomir(i, isAI); + } + mGamePhase = DUEL_STATE_PLAY; + }else if (mParent->players[0] == PLAYER_TYPE_HUMAN) deckmenu->Update(dt); #ifdef TESTSUITE else if (mParent->players[1] == PLAYER_TYPE_TESTSUITE){ @@ -280,13 +301,19 @@ void GameStateDuel::Update(float dt) case DUEL_STATE_PLAY: //Stop the music before starting the game if (GameApp::music){ - JSoundSystem::GetInstance()->StopMusic(GameApp::music); - SAFE_DELETE(GameApp::music); + JSoundSystem::GetInstance()->StopMusic(GameApp::music); + SAFE_DELETE(GameApp::music); } if (!game){ - GameObserver::Init(mPlayers, 2); - game = GameObserver::GetInstance(); - game->startGame(); + GameObserver::Init(mPlayers, 2); + game = GameObserver::GetInstance(); + game->startGame(); + if (mParent->gameType == GAME_TYPE_MOMIR){ + game->addObserver(NEW MTGMomirRule(-1, mParent->collection)); + for (int i = 0; i < 2; i++){ + game->players[i]->life+=4; + } + } } game->Update(dt); if (game->gameOver){ @@ -303,6 +330,16 @@ void GameStateDuel::Update(float dt) unlockedQuad = NEW JQuad(unlockedTex, 2, 2, 396, 96); GameOptions::GetInstance()->values[OPTIONS_DIFFICULTY_MODE_UNLOCKED] = GameOption(1); GameOptions::GetInstance()->save(); + }else{ + unlocked = isMomirUnlocked(); + if (unlocked){ + unlockedTex = JRenderer::GetInstance()->LoadTexture("graphics/momir_unlocked.png", TEX_TYPE_USE_VRAM); + unlockedQuad = NEW JQuad(unlockedTex, 2, 2, 396, 96); + GameOptions::GetInstance()->values[OPTIONS_MOMIR_MODE_UNLOCKED] = GameOption(1); + GameOptions::GetInstance()->save(); + } + } + if (unlocked){ JSample * sample = SampleCache::GetInstance()->getSample("sound/sfx/bonus.wav"); if (sample) JSoundSystem::GetInstance()->PlaySample(sample); } @@ -496,4 +533,11 @@ int GameStateDuel::isDifficultyUnlocked(){ } } return 0; +} + +int GameStateDuel::isMomirUnlocked(){ + if (GameOptions::GetInstance()->values[OPTIONS_MOMIR_MODE_UNLOCKED].getIntValue()) return 0; + Player *p = mPlayers[0]; + if (p->game->inPlay->countByType("land") == 8) return 1; + return 0; } \ No newline at end of file diff --git a/projects/mtg/src/GameStateMenu.cpp b/projects/mtg/src/GameStateMenu.cpp index e4f289e45..58f715e19 100644 --- a/projects/mtg/src/GameStateMenu.cpp +++ b/projects/mtg/src/GameStateMenu.cpp @@ -47,7 +47,9 @@ enum SUBMENUITEM_2PLAYER, SUBMENUITEM_DEMO, SUBMENUITEM_CANCEL, - SUBMENUITEM_TESTSUITE + SUBMENUITEM_TESTSUITE, + SUBMENUITEM_MOMIR, + SUBMENUITEM_CLASSIC, }; @@ -55,6 +57,7 @@ GameStateMenu::GameStateMenu(GameApp* parent): GameState(parent) { mGuiController = NULL; subMenuController = NULL; + gameTypeMenu = NULL; mIconsTexture = NULL; //bgMusic = NULL; timeIndex = 0; @@ -118,6 +121,7 @@ void GameStateMenu::Destroy() { SAFE_DELETE(mGuiController); SAFE_DELETE(subMenuController); + SAFE_DELETE(gameTypeMenu); SAFE_DELETE(mIconsTexture); for (int i = 0; i < 10 ; i++){ @@ -148,6 +152,9 @@ void GameStateMenu::Start(){ JSoundSystem::GetInstance()->StopMusic(GameApp::music); SAFE_DELETE(GameApp::music); } + + hasChosenGameType = 1; + if (GameOptions::GetInstance()->values[OPTIONS_MOMIR_MODE_UNLOCKED].getIntValue()) hasChosenGameType =0; } @@ -176,13 +183,7 @@ int GameStateMenu::nextCardSet(){ void GameStateMenu::End() { - //mEngine->EnableVSync(false); - // if (bgMusic) - // { - //JSoundSystem::GetInstance()->StopMusic(bgMusic); - //SAFE_DELETE(bgMusic); - // } JRenderer::GetInstance()->EnableVSync(false); } @@ -190,14 +191,6 @@ void GameStateMenu::End() void GameStateMenu::Update(float dt) { - /* - if (GameApp::music){ - if (mVolume < 2*GameOptions::GetInstance()->values[OPTIONS_MUSICVOLUME]){ - mVolume++; - JSoundSystem::GetInstance()->SetVolume(mVolume/2); - } - } - */ timeIndex += dt * 2; switch (MENU_STATE_MAJOR & currentState) @@ -258,8 +251,19 @@ void GameStateMenu::Update(float dt) case MENU_STATE_MAJOR_DUEL : if (MENU_STATE_MINOR_NONE == (currentState & MENU_STATE_MINOR)) { - mParent->SetNextState(GAME_STATE_DUEL); - currentState = MENU_STATE_MAJOR_MAINMENU; + if (!hasChosenGameType){ + currentState = MENU_STATE_MAJOR_SUBMENU; + JLBFont * mFont = GameApp::CommonRes->GetJLBFont(Constants::MENU_FONT); + subMenuController = NEW SimpleMenu(102, this, mFont, 150,60); + if (subMenuController){ + subMenuController->Add(SUBMENUITEM_CLASSIC,"Classic"); + subMenuController->Add(SUBMENUITEM_MOMIR, "Momir Basic"); + subMenuController->Add(SUBMENUITEM_CANCEL, "Cancel"); + } + }else{ + mParent->SetNextState(GAME_STATE_DUEL); + currentState = MENU_STATE_MAJOR_MAINMENU; + } } } switch (MENU_STATE_MINOR & currentState) @@ -423,23 +427,18 @@ JLBFont * mFont = GameApp::CommonRes->GetJLBFont(Constants::MENU_FONT); switch (controlId) { case MENUITEM_PLAY: - + subMenuController = NEW SimpleMenu(102, this, mFont, 150,60); + if (subMenuController){ + subMenuController->Add(SUBMENUITEM_1PLAYER,"1 Player"); + subMenuController->Add(SUBMENUITEM_2PLAYER, "2 Players"); + subMenuController->Add(SUBMENUITEM_DEMO,"Demo"); + subMenuController->Add(SUBMENUITEM_CANCEL, "Cancel"); #ifdef TESTSUITE - subMenuController = NEW SimpleMenu(102, this, mFont, 150,60); -#else - subMenuController = NEW SimpleMenu(102, this, mFont, 150,60); -#endif - if (subMenuController){ - subMenuController->Add(SUBMENUITEM_1PLAYER,"1 Player"); - subMenuController->Add(SUBMENUITEM_2PLAYER, "2 Players"); - subMenuController->Add(SUBMENUITEM_DEMO,"Demo"); - subMenuController->Add(SUBMENUITEM_CANCEL, "Cancel"); -#ifdef TESTSUITE - subMenuController->Add(SUBMENUITEM_TESTSUITE, "Test Suite"); + subMenuController->Add(SUBMENUITEM_TESTSUITE, "Test Suite"); #endif currentState = MENU_STATE_MAJOR_SUBMENU | MENU_STATE_MINOR_NONE; - } - break; + } + break; case MENUITEM_DECKEDITOR: mParent->SetNextState(GAME_STATE_DECK_VIEWER); break; @@ -474,6 +473,20 @@ JLBFont * mFont = GameApp::CommonRes->GetJLBFont(Constants::MENU_FONT); subMenuController->Close(); currentState = MENU_STATE_MAJOR_MAINMENU | MENU_STATE_MINOR_SUBMENU_CLOSING; break; + + case SUBMENUITEM_CLASSIC: + this->hasChosenGameType = 1; + mParent->gameType = GAME_TYPE_CLASSIC; + subMenuController->Close(); + currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING; + break; + + case SUBMENUITEM_MOMIR: + this->hasChosenGameType = 1; + mParent->gameType = GAME_TYPE_MOMIR; + subMenuController->Close(); + currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING; + break; #ifdef TESTSUITE case SUBMENUITEM_TESTSUITE: mParent->players[0] = PLAYER_TYPE_TESTSUITE; diff --git a/projects/mtg/src/MTGGameZones.cpp b/projects/mtg/src/MTGGameZones.cpp index 001de4683..07e7ac2c8 100644 --- a/projects/mtg/src/MTGGameZones.cpp +++ b/projects/mtg/src/MTGGameZones.cpp @@ -54,6 +54,7 @@ MTGPlayerCards::~MTGPlayerCards(){ SAFE_DELETE(inPlay); SAFE_DELETE(stack); SAFE_DELETE(removedFromGame); + SAFE_DELETE(garbage); } void MTGPlayerCards::setOwner(Player * player){ @@ -82,6 +83,7 @@ void MTGPlayerCards::init(){ inPlay = NEW MTGInPlay(); stack = NEW MTGStack(); removedFromGame = NEW MTGRemovedFromGame(); + garbage = NEW MTGGameZone(); } @@ -130,8 +132,7 @@ MTGCardInstance * MTGPlayerCards::putInZone(MTGCardInstance * card, MTGGameZone if (card->isToken){ if (to != g->players[0]->game->inPlay && to != g->players[1]->game->inPlay){ - //Token leaves play: we destroy it - //TODO DELETE Object + garbage->addCard(copy); return NULL; } } diff --git a/projects/mtg/src/MTGRules.cpp b/projects/mtg/src/MTGRules.cpp index 120d3e71b..f276cab93 100644 --- a/projects/mtg/src/MTGRules.cpp +++ b/projects/mtg/src/MTGRules.cpp @@ -2,7 +2,7 @@ #include "../include/MTGRules.h" MTGPutInPlayRule::MTGPutInPlayRule(int _id):MTGAbility(_id, NULL){ - + aType=MTGAbility::PUT_INTO_PLAY; } int MTGPutInPlayRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana){ @@ -78,6 +78,7 @@ int MTGPutInPlayRule::testDestroy(){ } MTGAttackRule::MTGAttackRule(int _id):MTGAbility(_id,NULL){ + aType=MTGAbility::MTG_ATTACK_RULE; } int MTGAttackRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana){ @@ -102,6 +103,7 @@ int MTGAttackRule::testDestroy(){ MTGBlockRule::MTGBlockRule(int _id):MTGAbility(_id,NULL){ + aType=MTGAbility::MTG_BLOCK_RULE; } int MTGBlockRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana){ @@ -134,3 +136,74 @@ int MTGBlockRule::reactToClick(MTGCardInstance * card){ int MTGBlockRule::testDestroy(){ return 0; } + + +// +// * Momir +// + +MTGMomirRule::MTGMomirRule(int _id, MTGAllCards * _collection):MTGAbility(_id, NULL){ + collection = _collection; + alreadyplayed = 0; + aType=MTGAbility::MOMIR; +} + +int MTGMomirRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana){ + if (alreadyplayed) return 0; + Player * player = game->currentlyActing(); + Player * currentPlayer = game->currentPlayer; + LOG("CANPUTINPLAY- check if card belongs to current player\n"); + if (!player->game->hand->hasCard(card)) return 0; + LOG("CANPUTINPLAY- check if card is land or can be played\n"); + if (player == currentPlayer && !game->isInterrupting && (game->currentGamePhase == Constants::MTG_PHASE_FIRSTMAIN || game->currentGamePhase == Constants::MTG_PHASE_SECONDMAIN)){ + LOG("CANPUTINPLAY- correct time to play\n"); + return 1; + } + return 0; +} + +int MTGMomirRule::reactToClick(MTGCardInstance * card_to_discard){ + if (!isReactingToClick(card_to_discard)) return 0; + Player * player = game->currentlyActing(); + ManaCost * cost = player->getManaPool(); + int converted = cost->getConvertedCost(); + player->getManaPool()->pay(cost); + player->game->putInZone(card_to_discard, player->game->hand, player->game->graveyard); + MTGCardInstance * card = genRandomCreature(converted); //TODO code this function + player->game->stack->addCard(card); + Spell * spell = NEW Spell(card); + spell->resolve(); + spell->source->isToken = 1; + delete spell; + alreadyplayed = 1; + return 1; +} + +MTGCardInstance * MTGMomirRule::genRandomCreature(int convertedCost){ + Player * p = game->currentlyActing(); + int total_cards = collection->totalCards(); + int start = (rand() % total_cards); + int id2 = start; + while (id2 < total_cards){ + MTGCard * card = collection->collection[id2]; + if (card->isACreature() && card->getManaCost()->getConvertedCost() == convertedCost){ + return NEW MTGCardInstance(card,p->game); + } + id2++; + if (id2 == start) return NULL; + if (id2 == total_cards) id2 = 0; + } + return NULL; +} + +//The Momir rule is never destroyed +int MTGMomirRule::testDestroy(){ + return 0; +} + +void MTGMomirRule::Update(float dt){ + if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UNTAP){ + alreadyplayed = 0; + } + MTGAbility::Update(dt); +} diff --git a/projects/mtg/template.vcproj b/projects/mtg/template.vcproj index b6942707e..376d9a920 100644 --- a/projects/mtg/template.vcproj +++ b/projects/mtg/template.vcproj @@ -228,6 +228,10 @@ RelativePath=".\src\ActionStack.cpp" > + + @@ -529,6 +533,10 @@ RelativePath=".\include\ActionStack.h" > + +