From cd07248df5ab7e607edbbfe0951b2f881089d00f Mon Sep 17 00:00:00 2001 From: "wagic.the.homebrew" Date: Fri, 29 Apr 2011 17:30:57 +0000 Subject: [PATCH] UPDATE YOUR rules FOLDER!!! - This is some Work in progress to make Wagic less "game" dependent. This change especially is an attempt at moving away from some dangerous patents owned by some company. It introduces "modrules.xml", a global configuration file describing dynamic settings for any given Wagic mod. It is very basic for now, but allows to customize a bit. In particular, it allows to remove the concept of shop and deck editor from the game, dynamically generate the main menu, and represent card activation with a mask rather than a rotation. I have a sample in progress which I hope to submit in the days to come, a proof of concept (nothing fancy yet) for another type of game using these ideas, as well as a few other things I introduced recently. In the future, I am hoping to extend modrules.xml so that it entirely describes the rules of a given card game. the other files in rules.txt will describe "extensions" to the core rules, just like they do right now, so this new file does not make them obsolete. - Also fixed minor bugs I stumbled upon while developing --- projects/mtg/Android/jni/Android.mk | 1 + projects/mtg/Makefile | 2 +- projects/mtg/bin/Res/rules/modrules.xml | 16 ++ projects/mtg/include/CardGui.h | 31 +++ projects/mtg/include/GameStateMenu.h | 4 +- projects/mtg/include/ModRules.h | 110 ++++++++++ projects/mtg/include/Pos.h | 1 + projects/mtg/src/CardGui.cpp | 33 +++ projects/mtg/src/GameApp.cpp | 4 + projects/mtg/src/GameStateDuel.cpp | 23 +- projects/mtg/src/GameStateMenu.cpp | 253 ++++++++++++++-------- projects/mtg/src/GuiPlay.cpp | 28 ++- projects/mtg/src/MenuItem.cpp | 22 +- projects/mtg/src/ModRules.cpp | 269 ++++++++++++++++++++++++ projects/mtg/src/Pos.cpp | 4 + projects/mtg/src/Rules.cpp | 3 + projects/mtg/template.vcxproj | 2 + projects/mtg/template.vcxproj.filters | 6 + 18 files changed, 698 insertions(+), 114 deletions(-) create mode 100644 projects/mtg/bin/Res/rules/modrules.xml create mode 100644 projects/mtg/include/ModRules.h create mode 100644 projects/mtg/src/ModRules.cpp diff --git a/projects/mtg/Android/jni/Android.mk b/projects/mtg/Android/jni/Android.mk index 4a5c8871c..996b008bd 100644 --- a/projects/mtg/Android/jni/Android.mk +++ b/projects/mtg/Android/jni/Android.mk @@ -82,6 +82,7 @@ LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.cpp \ $(MTG_PATH)/src/ManaCost.cpp \ $(MTG_PATH)/src/ManaCostHybrid.cpp \ $(MTG_PATH)/src/MenuItem.cpp \ + $(MTG_PATH)/src/ModRules.cpp \ $(MTG_PATH)/src/MTGAbility.cpp \ $(MTG_PATH)/src/MTGCard.cpp \ $(MTG_PATH)/src/MTGCardInstance.cpp \ diff --git a/projects/mtg/Makefile b/projects/mtg/Makefile index 9e950c02d..d027502c6 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/ObjectAnalytics.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 +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/ModRules.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/ObjectAnalytics.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/rules/modrules.xml b/projects/mtg/bin/Res/rules/modrules.xml new file mode 100644 index 000000000..f214c6cb5 --- /dev/null +++ b/projects/mtg/bin/Res/rules/modrules.xml @@ -0,0 +1,16 @@ + + + + +
+ + + + + +
+ + + + +
\ No newline at end of file diff --git a/projects/mtg/include/CardGui.h b/projects/mtg/include/CardGui.h index 4372e8c21..5bfbfc1c1 100644 --- a/projects/mtg/include/CardGui.h +++ b/projects/mtg/include/CardGui.h @@ -43,6 +43,8 @@ public: static const float BigWidth; static const float BigHeight; + PIXEL_TYPE mMask; + MTGCardInstance* card; CardGui(MTGCardInstance* card, float x, float y); CardGui(MTGCardInstance* card, const Pos& ref); @@ -95,4 +97,33 @@ public: TransientCardView(MTGCardInstance* card, const Pos& ref); }; + +class SimpleCardEffect +{ +public: + virtual void doEffect(Pos * card) = 0; + virtual void undoEffect(Pos * card) = 0; +}; + +class SimpleCardEffectRotate:public SimpleCardEffect +{ +protected: + float mRotation; +public: + SimpleCardEffectRotate(float rotation); + void doEffect(Pos * card); + void undoEffect(Pos * card); +}; + +class SimpleCardEffectMask:public SimpleCardEffect +{ +protected: + PIXEL_TYPE mMask; +public: + SimpleCardEffectMask(PIXEL_TYPE mask); + void doEffect(Pos * card); + void undoEffect(Pos * card); +}; + + #endif diff --git a/projects/mtg/include/GameStateMenu.h b/projects/mtg/include/GameStateMenu.h index ec46d8aec..1c6a09132 100644 --- a/projects/mtg/include/GameStateMenu.h +++ b/projects/mtg/include/GameStateMenu.h @@ -22,7 +22,6 @@ private: JTexture * splashTex; float mCreditsYPos; int currentState; - //JMusic * bgMusic; int mVolume; char nbcardsStr[400]; vector langs; @@ -48,7 +47,10 @@ private: void genNbCardsStr(); //computes the contents of nbCardsStr void ensureMGuiController(); //creates the MGuiController if it doesn't exist string loadRandomWallpaper(); //loads a list of string of textures that can be randolmy shown on the loading screen + + void RenderTopMenu(); public: + GameStateMenu(GameApp* parent); virtual ~GameStateMenu(); virtual void Create(); diff --git a/projects/mtg/include/ModRules.h b/projects/mtg/include/ModRules.h new file mode 100644 index 000000000..b543dc314 --- /dev/null +++ b/projects/mtg/include/ModRules.h @@ -0,0 +1,110 @@ +#ifndef _MODRULES_H_ +#define _MODRULES_H_ + +#include +#include +using namespace std; + +#include "CardGui.h" + +class TiXmlElement; + + +enum +{ + SUBMENUITEM_CANCEL = kCancelMenuID, + MENUITEM_PLAY, + MENUITEM_DECKEDITOR, + MENUITEM_SHOP, + MENUITEM_OPTIONS, + MENUITEM_EXIT, + MENUITEM_TROPHIES, + SUBMENUITEM_1PLAYER, +#ifdef NETWORK_SUPPORT + SUBMENUITEM_2PLAYERS, + SUBMENUITEM_HOST_GAME, + SUBMENUITEM_JOIN_GAME, +#endif //NETWORK_SUPPORT + SUBMENUITEM_DEMO, + SUBMENUITEM_TESTSUITE, + SUBMENUITEM_END_OFFSET +}; + +class ModRulesMenuItem +{ +protected: + static int strToAction(string str); +public: + int mActionId; + string mDisplayName; + ModRulesMenuItem(string actionIdStr, string displayName); + //most actionIds are associated to a game state. e.g. MENUITEM_DECKEDITOR <--> GAME_STATE_DECK_VIEWER + //This function returns the game state that matches the actionId, if any + int getMatchingGameState(); + static int getMatchingGameState(int actionId); +}; + +class ModRulesMainMenuItem: public ModRulesMenuItem +{ +public: + int mIconId; + string mParticleFile; + ModRulesMainMenuItem(string actionIdStr, string displayName, int iconId, string particleFile); +}; + +class ModRulesOtherMenuItem: public ModRulesMenuItem +{ +public: + JButton mKey; + ModRulesOtherMenuItem(string actionIdStr, string displayName, string keyStr); + static JButton strToJButton(string keyStr); +}; + +class ModRulesMenu +{ +public: + vector main; + vector other; + + void parse(TiXmlElement* element); + ~ModRulesMenu(); +}; + +class ModRulesGeneral +{ +protected: + bool mHasDeckEditor; + bool mHasShop; +public: + bool hasDeckEditor() {return mHasDeckEditor;}; + bool hasShop() {return mHasShop;}; + ModRulesGeneral(); + void parse(TiXmlElement* element); +}; + +class ModRulesCards +{ +public: + SimpleCardEffect * activateEffect; + static SimpleCardEffect * parseEffect(string str); + ModRulesCards(); + ~ModRulesCards(); + void parse(TiXmlElement* element); +}; + +class ModRules +{ +public: + ModRulesGeneral general; + ModRulesCards cards; + ModRulesMenu menu; + + bool load(string filename); + static int getValueAsInt(TiXmlElement* element, string childName); + +}; + +extern ModRules gModRules; + + +#endif diff --git a/projects/mtg/include/Pos.h b/projects/mtg/include/Pos.h index d2d65646f..76bdaf1ff 100644 --- a/projects/mtg/include/Pos.h +++ b/projects/mtg/include/Pos.h @@ -7,6 +7,7 @@ struct Pos { float actX, actY, actZ, actT, actA; float x, y, zoom, t, alpha; + PIXEL_TYPE mask; Pos(float, float, float, float, float); virtual void Update(float dt); void UpdateNow(); diff --git a/projects/mtg/src/CardGui.cpp b/projects/mtg/src/CardGui.cpp index 72b0373a2..2484c6563 100644 --- a/projects/mtg/src/CardGui.cpp +++ b/projects/mtg/src/CardGui.cpp @@ -266,6 +266,10 @@ void CardGui::Render() renderer->RenderQuad(shadow.get(), actX, actY, actT, (28 * actZ + 1) / 16, 40 * actZ / 16); } + // Render a mask over the card, if set + if (mask && quad) + JRenderer::GetInstance()->FillRect(actX - (scale * quad->mWidth / 2),actY - (scale * quad->mHeight / 2), scale * quad->mWidth, scale* quad->mHeight, mask); + PlayGuiObject::Render(); } @@ -866,3 +870,32 @@ ostream& CardGui::toString(ostream& out) const { return (out << "CardGui ::: x,y " << x << "," << y); } + + +SimpleCardEffectRotate::SimpleCardEffectRotate(float rotation): mRotation(rotation) +{ +} + +void SimpleCardEffectRotate::doEffect(Pos * card) +{ + card->t = mRotation; +} + +void SimpleCardEffectRotate::undoEffect(Pos * card) +{ + card->t = 0; +} + +SimpleCardEffectMask::SimpleCardEffectMask(PIXEL_TYPE mask): mMask(mask) +{ +} + +void SimpleCardEffectMask::doEffect(Pos * card) +{ + card->mask = mMask; +} + +void SimpleCardEffectMask::undoEffect(Pos * card) +{ + card->mask = 0; +} \ No newline at end of file diff --git a/projects/mtg/src/GameApp.cpp b/projects/mtg/src/GameApp.cpp index 7366f0d36..1372ab537 100644 --- a/projects/mtg/src/GameApp.cpp +++ b/projects/mtg/src/GameApp.cpp @@ -27,6 +27,7 @@ #include "Translate.h" #include "WFilter.h" #include "Rules.h" +#include "ModRules.h" #define DEFAULT_DURATION .25 @@ -117,6 +118,9 @@ void GameApp::Create() LOG("Res Root:"); LOG(JFileSystem::GetInstance()->GetResourceRoot().c_str()); + //Load Mod Rules before everything else + gModRules.load("rules/modrules.xml"); + //Link this to our settings manager. options.theGame = this; diff --git a/projects/mtg/src/GameStateDuel.cpp b/projects/mtg/src/GameStateDuel.cpp index 23e225294..f90e702f9 100644 --- a/projects/mtg/src/GameStateDuel.cpp +++ b/projects/mtg/src/GameStateDuel.cpp @@ -14,6 +14,7 @@ #include "Credits.h" #include "Translate.h" #include "Rules.h" +#include "ModRules.h" #ifdef TESTSUITE #include "TestSuiteAI.h" @@ -124,7 +125,7 @@ void GameStateDuel::Start() vector playerDeckList = BuildDeckList(options.profileFile()); int nbDecks = playerDeckList.size(); - if (nbDecks) + if (nbDecks > 1) { decksneeded = 0; deckmenu->Add(MENUITEM_RANDOM_PLAYER, "Random", "Play with a random deck."); @@ -143,17 +144,21 @@ void GameStateDuel::Start() { if (decksneeded) { - //translate deck creating desc - Translator * t = Translator::GetInstance(); - map::iterator it = t->deckValues.find("Create your Deck!"); - if (it != t->deckValues.end()) - deckmenu->Add(MENUITEM_NEW_DECK, "Create your Deck!", it->second); - else - deckmenu->Add(MENUITEM_NEW_DECK, "Create your Deck!", "Highly recommended to get\nthe full Wagic experience!"); + if (gModRules.general.hasDeckEditor()) + { + //translate deck creating desc + Translator * t = Translator::GetInstance(); + string desc = "Highly recommended to get\nthe full Wagic experience!"; + map::iterator it = t->deckValues.find("Create your Deck!"); + if (it != t->deckValues.end()) + desc = it->second; + + deckmenu->Add(MENUITEM_NEW_DECK, "Create your Deck!", desc); + } premadeDeck = true; fillDeckMenu(deckmenu, JGE_GET_RES("player/premade")); } - if (!decksneeded) + else if (gModRules.general.hasDeckEditor()) { deckmenu->Add(MENUITEM_NEW_DECK, "New Deck...", "Create a new deck to play with."); } diff --git a/projects/mtg/src/GameStateMenu.cpp b/projects/mtg/src/GameStateMenu.cpp index d416bac11..d0fd7fe33 100644 --- a/projects/mtg/src/GameStateMenu.cpp +++ b/projects/mtg/src/GameStateMenu.cpp @@ -19,6 +19,8 @@ #include "WFont.h" #include #include "Rules.h" +#include "ModRules.h" + #ifdef NETWORK_SUPPORT #include #endif//NETWORK_SUPPORT @@ -49,25 +51,6 @@ enum ENUM_MENU_STATE_MINOR MENU_STATE_MINOR = 0xF00 }; -enum -{ - SUBMENUITEM_CANCEL = kCancelMenuID, - MENUITEM_PLAY, - MENUITEM_DECKEDITOR, - MENUITEM_SHOP, - MENUITEM_OPTIONS, - MENUITEM_EXIT, - SUBMENUITEM_1PLAYER, -#ifdef NETWORK_SUPPORT - SUBMENUITEM_2PLAYERS, - SUBMENUITEM_HOST_GAME, - SUBMENUITEM_JOIN_GAME, -#endif //NETWORK_SUPPORT - SUBMENUITEM_DEMO, - SUBMENUITEM_TESTSUITE, - SUBMENUITEM_END_OFFSET -}; - GameStateMenu::GameStateMenu(GameApp* parent) : GameState(parent) { @@ -150,10 +133,6 @@ void GameStateMenu::Start() hasChosenGameType = false; mParent->gameType = GAME_TYPE_CLASSIC; - /* - if (options[Options::MOMIR_MODE_UNLOCKED].number) hasChosenGameType = 0; - if (options[Options::RANDOMDECK_MODE_UNLOCKED].number) hasChosenGameType = 0; - */ bgTexture = WResourceManager::Instance()->RetrieveTexture("menutitle.png", RETRIEVE_LOCK); mBg = WResourceManager::Instance()->RetrieveQuad("menutitle.png", 0, 0, 256, 166); // Create background quad for rendering. @@ -170,13 +149,25 @@ void GameStateMenu::genNbCardsStr() { //How many cards total ? PlayerData * playerdata = NEW PlayerData(MTGCollection()); - if (playerdata && !options[Options::ACTIVE_PROFILE].isDefault()) - sprintf(nbcardsStr, _("%s: %i cards (%i) (%i unique)").c_str(), options[Options::ACTIVE_PROFILE].str.c_str(), - playerdata->collection->totalCards(), MTGCollection()->totalCards(), - MTGCollection()->primitives.size()); + size_t totalUnique = MTGCollection()->primitives.size(); + size_t totalPrints = MTGCollection()->totalCards(); + + if (totalUnique != totalPrints) + { + if (playerdata && !options[Options::ACTIVE_PROFILE].isDefault()) + sprintf(nbcardsStr, _("%s: %i cards (%i) (%i unique)").c_str(), options[Options::ACTIVE_PROFILE].str.c_str(), + playerdata->collection->totalCards(), totalPrints,totalUnique); + else + sprintf(nbcardsStr, _("%i cards (%i unique)").c_str(),totalPrints,totalUnique); + } else - sprintf(nbcardsStr, _("%i cards (%i unique)").c_str(), MTGCollection()->totalCards(), - MTGCollection()->primitives.size()); + { + if (playerdata && !options[Options::ACTIVE_PROFILE].isDefault()) + sprintf(nbcardsStr, _("%s: %i cards (%i)").c_str(), options[Options::ACTIVE_PROFILE].str.c_str(), + playerdata->collection->totalCards(), totalPrints); + else + sprintf(nbcardsStr, _("%i cards").c_str(),totalPrints); + } SAFE_DELETE(playerdata); } @@ -239,18 +230,22 @@ void GameStateMenu::fillScroller() scroller->Add(buff2); PlayerData * playerdata = NEW PlayerData(MTGCollection()); - int totalCards = playerdata->collection->totalCards(); - if (totalCards) + + if (gModRules.general.hasDeckEditor() && gModRules.general.hasShop()) { - sprintf(buff2, _("You have a total of %i cards in your collection").c_str(), totalCards); - scroller->Add(buff2); + int totalCards = playerdata->collection->totalCards(); + if (totalCards) + { + sprintf(buff2, _("You have a total of %i cards in your collection").c_str(), totalCards); + scroller->Add(buff2); - int estimatedValue = playerdata->collection->totalPrice(); - sprintf(buff2, _("The shopkeeper would buy your entire collection for around %i credits").c_str(), estimatedValue / 2); - scroller->Add(buff2); + int estimatedValue = playerdata->collection->totalPrice(); + sprintf(buff2, _("The shopkeeper would buy your entire collection for around %i credits").c_str(), estimatedValue / 2); + scroller->Add(buff2); - sprintf(buff2, _("The cards in your collection have an average value of %i credits").c_str(), estimatedValue / totalCards); - scroller->Add(buff2); + sprintf(buff2, _("The cards in your collection have an average value of %i credits").c_str(), estimatedValue / totalCards); + scroller->Add(buff2); + } } sprintf(buff2, _("You currently have %i credits").c_str(), playerdata->credits); @@ -400,6 +395,13 @@ void GameStateMenu::listPrimitives() mDip = opendir(JGE_GET_RES("sets/primitives/").c_str()); } + if (!mDip) + { + DebugTrace("GameStateMenu.cpp:WARNING:Primitives folder is missing"); + primitivesLoadCounter = 0; + return; + } + while ((mDit = readdir(mDip))) { string filename = JGE_GET_RES("sets/primitives/"); @@ -424,16 +426,28 @@ void GameStateMenu::ensureMGuiController() { WFont * mFont = WResourceManager::Instance()->GetWFont(Fonts::MENU_FONT); mFont->SetColor(ARGB(255,255,255,255)); - mGuiController->Add(NEW MenuItem(MENUITEM_PLAY, mFont, "Play", 80, 50 + SCREEN_HEIGHT / 2, mIcons[8].get(), mIcons[9].get(), - "particle1.psi", WResourceManager::Instance()->GetQuad("particles").get(), true)); - mGuiController->Add(NEW MenuItem(MENUITEM_DECKEDITOR, mFont, "Deck Editor", 160, 50 + SCREEN_HEIGHT / 2, mIcons[2].get(), - mIcons[3].get(), "particle2.psi", WResourceManager::Instance()->GetQuad("particles").get())); - mGuiController->Add(NEW MenuItem(MENUITEM_SHOP, mFont, "Shop", 240, 50 + SCREEN_HEIGHT / 2, mIcons[0].get(), mIcons[1].get(), - "particle3.psi", WResourceManager::Instance()->GetQuad("particles").get())); - mGuiController->Add(NEW MenuItem(MENUITEM_OPTIONS, mFont, "Options", 320, 50 + SCREEN_HEIGHT / 2, mIcons[6].get(), mIcons[7].get(), - "particle4.psi", WResourceManager::Instance()->GetQuad("particles").get())); - mGuiController->Add(NEW MenuItem(MENUITEM_EXIT, mFont, "Exit", 400, 50 + SCREEN_HEIGHT / 2, mIcons[4].get(), mIcons[5].get(), - "particle5.psi", WResourceManager::Instance()->GetQuad("particles").get())); + vectoritems = gModRules.menu.main; + + int numItems = (int)items.size(); + float startX = 80.f; + float totalSize = SCREEN_WIDTH_F - (2 * startX); + float space = 0; + if (numItems < 2) + startX = SCREEN_WIDTH_F/2; + else + space = totalSize/(numItems - 1); + + for (size_t i = 0; i < items.size(); ++i) { + ModRulesMainMenuItem * item = items[i]; + int iconId = (item->mIconId - 1) * 2; + mGuiController->Add(NEW MenuItem( + item->mActionId, + mFont, item->mDisplayName, + startX + (i * space), 50 + SCREEN_HEIGHT / 2, + mIcons[iconId].get(), mIcons[iconId + 1].get(), + item->mParticleFile.c_str(), WResourceManager::Instance()->GetQuad("particles").get(), + (i == 0))); + } } } } @@ -527,15 +541,23 @@ void GameStateMenu::Update(float dt) currentState &= MENU_STATE_MAJOR_MAINMENU; options.reloadProfile(); //Handles building a new deck, if needed. break; - case MENU_STATE_MAJOR_MAINMENU: - if (!scrollerSet) - fillScroller(); - ensureMGuiController(); - if (mGuiController) - mGuiController->Update(dt); - if (mEngine->GetButtonState(JGE_BTN_NEXT)) //Hook for GameStateAward state - mParent->DoTransition(TRANSITION_FADE, GAME_STATE_AWARDS); //TODO: A slide transition would be nice. - break; + case MENU_STATE_MAJOR_MAINMENU: + { + if (!scrollerSet) + fillScroller(); + ensureMGuiController(); + if (mGuiController) + mGuiController->Update(dt); + + //Hook for Top Menu actions + vectoritems = gModRules.menu.other; + for (size_t i = 0; i < items.size(); ++i) + { + if (mEngine->GetButtonState(items[i]->mKey) && items[i]->getMatchingGameState()) + mParent->DoTransition(TRANSITION_FADE, items[i]->getMatchingGameState()); //TODO: Add the transition as a parameter in the rules file + } + break; + } #ifdef NETWORK_SUPPORT case MENU_STATE_NETWORK_DEFINE: currentState = MENU_STATE_MAJOR_SUBMENU; @@ -639,6 +661,83 @@ void GameStateMenu::Update(float dt) } } +//Renders the "sub" menu with shoulder button links +void GameStateMenu::RenderTopMenu() +{ + float leftTextPos = 10; + float rightTextPos = SCREEN_WIDTH - 10; + + vectoritems = gModRules.menu.other; + for (size_t i = 0; i < items.size(); ++i) + { + switch(items[i]->mKey) + { + case JGE_BTN_PREV: + leftTextPos += 64; + break; + case JGE_BTN_NEXT: + rightTextPos -= 64; + break; + default: + DebugTrace("not supported yet!"); + break; + } + } + + WFont * mFont = WResourceManager::Instance()->GetWFont(Fonts::MAIN_FONT); + mFont->SetScale(DEFAULT_MAIN_FONT_SCALE); + mFont->SetColor(ARGB(128,255,255,255)); + mFont->DrawString(GAME_VERSION, rightTextPos, 5, JGETEXT_RIGHT); + mFont->DrawString(nbcardsStr, leftTextPos, 5); + mFont->SetScale(1.f); + mFont->SetColor(ARGB(255,255,255,255)); + + if (!items.size()) + return; + + JQuadPtr jq = WResourceManager::Instance()->RetrieveTempQuad("button_shoulder.png"); + if (!jq.get()) + return; + + mFont = WResourceManager::Instance()->GetWFont(Fonts::OPTION_FONT); + float olds = mFont->GetScale(); + + for (size_t i = 0; i < items.size(); ++i) + { + ModRulesOtherMenuItem * item = items[i]; + + int alpha = 255; + if (item->mActionId == MENUITEM_TROPHIES && options.newAward()) + alpha = (int) (sin(timeIndex) * 255); + + float xPos = SCREEN_WIDTH - 64; + float xTextPos = xPos + 54; + int textAlign = JGETEXT_RIGHT; + jq->SetHFlip(false); + + switch(item->mKey) + { + case JGE_BTN_PREV: + xPos = 5; + xTextPos = xPos + 10; + textAlign = JGETEXT_LEFT; + jq->SetHFlip(true); + break; + default: + break; + } + + jq->SetColor(ARGB(abs(alpha),255,255,255)); + mFont->SetColor(ARGB(abs(alpha),0,0,0)); + string s = _(item->mDisplayName); + mFont->SetScale(1.0f); + mFont->SetScale(50.0f / mFont->GetStringWidth(s.c_str())); + JRenderer::GetInstance()->RenderQuad(jq.get(), xPos, 2); + mFont->DrawString(s, xTextPos, 9, textAlign); + mFont->SetScale(olds); + } +} + void GameStateMenu::Render() { if ((currentState & MENU_STATE_MINOR) == MENU_STATE_MINOR_FADEIN) @@ -690,38 +789,14 @@ void GameStateMenu::Render() if (mGuiController) mGuiController->Render(); - mFont->SetScale(DEFAULT_MAIN_FONT_SCALE); - mFont->SetColor(ARGB(128,255,255,255)); - mFont->DrawString(GAME_VERSION, SCREEN_WIDTH - 74, 5, JGETEXT_RIGHT); - mFont->DrawString(nbcardsStr, 10, 5); - mFont->SetScale(1.f); - mFont->SetColor(ARGB(255,255,255,255)); - renderer->FillRoundRect(SCREEN_WIDTH / 2 - 100, SCREEN_HEIGHT, 191, 6, 5, ARGB(100,10,5,0)); scroller->Render(); if (mBg.get()) renderer->RenderQuad(mBg.get(), SCREEN_WIDTH / 2, 50); - JQuadPtr jq = WResourceManager::Instance()->RetrieveTempQuad("button_shoulder.png"); - if (jq.get()) - { - int alp = 255; - if (options.newAward()) - alp = (int) (sin(timeIndex) * 255); - float olds = mFont->GetScale(); - mFont = WResourceManager::Instance()->GetWFont(Fonts::OPTION_FONT); - jq->SetColor(ARGB(abs(alp),255,255,255)); - mFont->SetColor(ARGB(abs(alp),0,0,0)); - string s = _("Trophy Room"); - ; - mFont->SetScale(1.0f); - mFont->SetScale(50.0f / mFont->GetStringWidth(s.c_str())); - renderer->RenderQuad(jq.get(), SCREEN_WIDTH - 64, 2); - mFont->DrawString(s, SCREEN_WIDTH - 10, 9, JGETEXT_RIGHT); - mFont = WResourceManager::Instance()->GetWFont(Fonts::MENU_FONT); - mFont->SetScale(olds); - } + RenderTopMenu(); + } if (subMenuController) { @@ -762,20 +837,20 @@ void GameStateMenu::ButtonPressed(int controllerId, int controlId) subMenuController->Add(SUBMENUITEM_DEMO, "Demo"); subMenuController->Add(SUBMENUITEM_CANCEL, "Cancel"); #ifdef TESTSUITE - subMenuController->Add(SUBMENUITEM_TESTSUITE, "Test Suite"); + if (Rules::getRulesByFilename("testsuite.txt")) + subMenuController->Add(SUBMENUITEM_TESTSUITE, "Test Suite"); #endif currentState = MENU_STATE_MAJOR_SUBMENU | MENU_STATE_MINOR_NONE; } break; + case MENUITEM_DECKEDITOR: - mParent->DoTransition(TRANSITION_FADE, GAME_STATE_DECK_VIEWER); - break; case MENUITEM_SHOP: - mParent->DoTransition(TRANSITION_FADE, GAME_STATE_SHOP); - break; case MENUITEM_OPTIONS: - mParent->DoTransition(TRANSITION_FADE, GAME_STATE_OPTIONS); + case MENUITEM_TROPHIES: + mParent->DoTransition(TRANSITION_FADE, ModRulesMenuItem::getMatchingGameState(controlId)); break; + case MENUITEM_EXIT: mEngine->End(); break; diff --git a/projects/mtg/src/GuiPlay.cpp b/projects/mtg/src/GuiPlay.cpp index fec6b0d60..af036e1fa 100644 --- a/projects/mtg/src/GuiPlay.cpp +++ b/projects/mtg/src/GuiPlay.cpp @@ -5,6 +5,7 @@ #include "GuiPlay.h" #include "Subtypes.h" #include "Trash.h" +#include "ModRules.h" #define CARD_WIDTH (31) @@ -236,12 +237,12 @@ void GuiPlay::Replace() } } float x = 24 + opponentSpells.nextX(); - //seperated the varible X into 2 different varibles. There are 2 players here!! - //we should not be using a single varible to determine the positioning of cards!! + //seperated the variable X into 2 different variables. There are 2 players here!! + //we should not be using a single variable to determine the positioning of cards!! float myx = 24 + selfSpells.nextX(); opponentLands.reset(opponentLandsN,x, 50); opponentCreatures.reset(opponentCreaturesN, x, 95); - battleField.reset(x, 145);//what does this varible do? i can comment it out with no reprocussions...is this being double handled? + battleField.reset(x, 145);//what does this variable do? I can comment it out with no repercussions...is this being double handled? selfCreatures.reset(selfCreaturesN, myx, 195); selfLands.reset(selfLandsN, myx, 240); @@ -346,7 +347,12 @@ int GuiPlay::receiveEventPlus(WEvent * e) else card = NEW CardView(CardView::playZone, event->card, 0, 0); cards.push_back(card); - card->t = event->card->isTapped() ? M_PI / 2 : 0; + + if (event->card->isTapped()) + gModRules.cards.activateEffect->doEffect(card); + else + gModRules.cards.activateEffect->undoEffect(card); + card->alpha = 255; // Make sure that the card is repositioned before adding it to the CardSelector, as @@ -371,10 +377,20 @@ int GuiPlay::receiveEventPlus(WEvent * e) else if (WEventCardTap* event = dynamic_cast(e)) { if (CardView* cv = dynamic_cast(event->card->view)) - cv->t = event->after ? M_PI / 2 : 0; + { + if (event->after) + gModRules.cards.activateEffect->doEffect(cv); + else + gModRules.cards.activateEffect->undoEffect(cv); + //cv->t = event->after ? M_PI / 2 : 0; + } else if (event->card->view != NULL) { - event->card->view->actT = event->after ? M_PI / 2 : 0; + if (event->after) + gModRules.cards.activateEffect->doEffect(event->card->view); + else + gModRules.cards.activateEffect->undoEffect(event->card->view); + //event->card->view->actT = event->after ? M_PI / 2 : 0; } else { diff --git a/projects/mtg/src/MenuItem.cpp b/projects/mtg/src/MenuItem.cpp index 1aaf70ab0..9f203411b 100644 --- a/projects/mtg/src/MenuItem.cpp +++ b/projects/mtg/src/MenuItem.cpp @@ -10,8 +10,13 @@ MenuItem::MenuItem(int id, WFont *font, string text, float x, float y, JQuad * _ { mText = _(text); updatedSinceLastRender = 1; - mParticleSys = NEW hgeParticleSystem(WResourceManager::Instance()->RetrievePSI(particle, particleTex)); - mParticleSys->MoveTo(mX, mY); + mParticleSys = NULL; + hgeParticleSystemInfo * psi = WResourceManager::Instance()->RetrievePSI(particle, particleTex); + if (psi) + { + mParticleSys = NEW hgeParticleSystem(psi); + mParticleSys->MoveTo(mX, mY); + } mHasFocus = hasFocus; lastDt = 0.001f; @@ -73,20 +78,22 @@ void MenuItem::Update(float dt) mScale = mTargetScale; } - mParticleSys->Update(dt); + if (mParticleSys) + mParticleSys->Update(dt); } void MenuItem::Entering() { - - mParticleSys->Fire(); + if (mParticleSys) + mParticleSys->Fire(); mHasFocus = true; mTargetScale = 1.3f; } bool MenuItem::Leaving(JButton key) { - mParticleSys->Stop(true); + if (mParticleSys) + mParticleSys->Stop(true); mHasFocus = false; mTargetScale = 1.0f; return true; @@ -99,8 +106,7 @@ bool MenuItem::ButtonPressed() MenuItem::~MenuItem() { - if (mParticleSys) - delete mParticleSys; + SAFE_DELETE(mParticleSys); } ostream& MenuItem::toString(ostream& out) const diff --git a/projects/mtg/src/ModRules.cpp b/projects/mtg/src/ModRules.cpp new file mode 100644 index 000000000..acf57f1cf --- /dev/null +++ b/projects/mtg/src/ModRules.cpp @@ -0,0 +1,269 @@ +#include "PrecompiledHeader.h" + +#include "ModRules.h" +#include "utils.h" +#include "GameState.h" +#include "../../../JGE/src/tinyxml/tinyxml.h" + + +ModRules gModRules; + +bool ModRules::load(string filename) +{ + JFileSystem *fileSystem = JFileSystem::GetInstance(); + if (!fileSystem) return false; + + if (!fileSystem->OpenFile(filename.c_str())) + { + DebugTrace("FATAL: " << filename << "Does not exist"); + return false; + } + + int size = fileSystem->GetFileSize(); + char *xmlBuffer = NEW char[size]; + fileSystem->ReadFile(xmlBuffer, size); + + TiXmlDocument doc; + doc.Parse(xmlBuffer); + + fileSystem->CloseFile(); + delete[] xmlBuffer; + + for (TiXmlNode* node = doc.FirstChild(); node; node = node->NextSibling()) + { + TiXmlElement* element = node->ToElement(); + if (element != NULL) + { + if (strcmp(element->Value(), "menu") == 0) + { + menu.parse(element); + } + else if (strcmp(element->Value(), "general") == 0) + { + general.parse(element); + } + else if (strcmp(element->Value(), "cards") == 0) + { + cards.parse(element); + } + } + } + return true; +} + +int ModRulesMenuItem::strToAction(string str) +{ + if (str.compare("playMenu") == 0) + return MENUITEM_PLAY; + if (str.compare("deckEditor") == 0) + return MENUITEM_DECKEDITOR; + if (str.compare("shop") == 0) + return MENUITEM_SHOP; + if (str.compare("options") == 0) + return MENUITEM_OPTIONS; + if (str.compare("quit") == 0) + return MENUITEM_EXIT; + if (str.compare("trophies") == 0) + return MENUITEM_TROPHIES; + + return MENUITEM_PLAY; +} + +ModRulesMenuItem::ModRulesMenuItem(string actionIdStr, string displayName) +{ + mActionId = strToAction(actionIdStr); + mDisplayName = displayName; +} + + +int ModRulesMenuItem::getMatchingGameState() +{ + return getMatchingGameState(mActionId); +} + +int ModRulesMenuItem::getMatchingGameState(int actionId) +{ + switch (actionId) + { + case MENUITEM_DECKEDITOR: + return GAME_STATE_DECK_VIEWER; + case MENUITEM_SHOP: + return GAME_STATE_SHOP; + case MENUITEM_OPTIONS: + return GAME_STATE_OPTIONS; + case MENUITEM_TROPHIES: + return GAME_STATE_AWARDS; + default: + return GAME_STATE_NONE; + } +} + +ModRulesMainMenuItem::ModRulesMainMenuItem(string actionIdStr, string displayName, int iconId, string particleFile): + ModRulesMenuItem(actionIdStr, displayName), mIconId(iconId), mParticleFile(particleFile) +{ +} + +JButton ModRulesOtherMenuItem::strToJButton(string str) +{ + if (str.compare("btn_next") == 0) + return JGE_BTN_NEXT; + if (str.compare("btn_prev") == 0) + return JGE_BTN_PREV; + if (str.compare("btn_ctrl") == 0) + return JGE_BTN_CTRL; + if (str.compare("btn_menu") == 0) + return JGE_BTN_MENU; + if (str.compare("btn_cancel") == 0) + return JGE_BTN_CANCEL; + if (str.compare("btn_pri") == 0) + return JGE_BTN_PRI; + if (str.compare("btn_sec") == 0) + return JGE_BTN_SEC; + + return JGE_BTN_NEXT; +} + + +ModRulesOtherMenuItem::ModRulesOtherMenuItem(string actionIdStr, string displayName, string keyStr): ModRulesMenuItem(actionIdStr, displayName) +{ + mKey = strToJButton(keyStr); +} + +void ModRulesMenu::parse(TiXmlElement* element) +{ + TiXmlNode* mainNode = element->FirstChild("main"); + if (mainNode) { + for (TiXmlNode* node = mainNode->ToElement()->FirstChild("item"); node; node = node->NextSibling("item")) + { + TiXmlElement* element = node->ToElement(); + { + main.push_back(NEW ModRulesMainMenuItem( + element->Attribute("action"), + element->Attribute("displayName"), + atoi(element->Attribute("iconId")), + element->Attribute("particleFile"))); + } + } + } + + TiXmlNode* otherNode = element->FirstChild("other"); + if (otherNode) { + for (TiXmlNode* node = otherNode->ToElement()->FirstChild("item"); node; node = node->NextSibling("item")) + { + TiXmlElement* element = node->ToElement(); + if (element) + { + other.push_back(NEW ModRulesOtherMenuItem( + element->Attribute("action"), + element->Attribute("displayName"), + element->Attribute("key"))); + } + } + } +} + +ModRulesMenu::~ModRulesMenu() +{ + for (size_t i = 0; i < main.size(); ++i) + SAFE_DELETE(main[i]); + + for (size_t i = 0; i < other.size(); ++i) + SAFE_DELETE(other[i]); + + main.clear(); + other.clear(); +} + +ModRulesGeneral::ModRulesGeneral() +{ + mHasDeckEditor = true; + mHasShop = true; +} + +int ModRules::getValueAsInt(TiXmlElement* element, string childName){ + TiXmlNode* node = element->FirstChild(childName.c_str()); + if (node) { + const char* value = node->ToElement()->GetText(); + return atoi(value); + } + return -1; +} + +void ModRulesGeneral::parse(TiXmlElement* element) +{ + int value = ModRules::getValueAsInt(element, "hasDeckEditor"); + if (value != -1) + mHasDeckEditor = value; + + value = ModRules::getValueAsInt(element, "hasShop"); + if (value != -1) + mHasShop = value; + +} + +ModRulesCards::ModRulesCards() +{ + activateEffect = NEW SimpleCardEffectRotate(M_PI/2); //Default activation effect +} + +SimpleCardEffect * ModRulesCards::parseEffect(string s) +{ + size_t limiter = s.find("("); + string function, params; + if (limiter != string::npos) + { + function = s.substr(0, limiter); + params = s.substr(limiter+1, s.size() - 2 - limiter); + } + else + { + function = s; + } + + if (function.compare("rotate") == 0) + { + return NEW SimpleCardEffectRotate(M_PI*atoi(params.c_str())/180); + } + + if (function.compare("mask") == 0) + { + vector argb = split( params, ','); + if (argb.size() < 4) + { + DebugTrace("not enough params in mask"); + return NULL; + } + PIXEL_TYPE mask = ARGB( + atoi(argb[0].c_str()), + atoi(argb[1].c_str()), + atoi(argb[2].c_str()), + atoi(argb[3].c_str()) + ); + return NEW SimpleCardEffectMask(mask); + } + return NULL; +} + +void ModRulesCards::parse(TiXmlElement* element) +{ + TiXmlNode* node = element->FirstChild("general"); + if (node) { + TiXmlElement* generalElement = node->ToElement(); + TiXmlNode* nodeActivation = generalElement->FirstChild("activate"); + if (nodeActivation) { + TiXmlElement* activateElement = nodeActivation->ToElement(); + TiXmlNode* nodeUIEvent = activateElement->FirstChild("uiEvent"); + if (nodeUIEvent) { + const char* event = nodeUIEvent->ToElement()->GetText(); + SAFE_DELETE(activateEffect); + activateEffect = parseEffect(event); + } + } + } + +} + +ModRulesCards::~ModRulesCards() +{ + SAFE_DELETE(activateEffect); +} diff --git a/projects/mtg/src/Pos.cpp b/projects/mtg/src/Pos.cpp index d8f952fd3..88c9ecae2 100644 --- a/projects/mtg/src/Pos.cpp +++ b/projects/mtg/src/Pos.cpp @@ -6,6 +6,7 @@ Pos::Pos(float x, float y, float z, float t, float a) : actX(x), actY(y), actZ(z), actT(t), actA(a), x(x), y(y), zoom(z), t(t), alpha(a) { + mask = 0; } void Pos::Update(float dt) { @@ -32,4 +33,7 @@ void Pos::Render(JQuad* quad) { quad->SetColor(ARGB((int)actA, 255, 255, 255)); JRenderer::GetInstance()->RenderQuad(quad, actX, actY, actT, actZ, actZ); + if (mask && !actT) + JRenderer::GetInstance()->FillRect(actX,actY,actZ * quad->mWidth, actZ* quad->mHeight, mask); + } diff --git a/projects/mtg/src/Rules.cpp b/projects/mtg/src/Rules.cpp index a35563a45..35bfa1c01 100644 --- a/projects/mtg/src/Rules.cpp +++ b/projects/mtg/src/Rules.cpp @@ -555,6 +555,9 @@ bool Rules::canChooseDeck() int Rules::load(string _filename) { + if (_filename.size() < 5 || _filename.find(".txt") == string::npos) + return 0; + if (!filename.size()) //this check is necessary because of the recursive calls (a fil loads other files) filename = _filename; char c_filename[4096]; diff --git a/projects/mtg/template.vcxproj b/projects/mtg/template.vcxproj index 21db2caab..ab4af142b 100644 --- a/projects/mtg/template.vcxproj +++ b/projects/mtg/template.vcxproj @@ -369,6 +369,7 @@ + @@ -483,6 +484,7 @@ + diff --git a/projects/mtg/template.vcxproj.filters b/projects/mtg/template.vcxproj.filters index 7f6da0455..25457ba1e 100644 --- a/projects/mtg/template.vcxproj.filters +++ b/projects/mtg/template.vcxproj.filters @@ -304,6 +304,9 @@ src + + src + @@ -630,6 +633,9 @@ inc + + inc +