From 83e2b04547f44b0ea638f1eff2baaca41856fc0b Mon Sep 17 00:00:00 2001 From: "wagic.jeck" Date: Mon, 8 Feb 2010 01:03:07 +0000 Subject: [PATCH] Jeck - Added full filtering system to deck editor and shop. I've spent 24hours on pure debugging, but there are likely a couple bugs I've missed. So please, if you find something, make an issue of it! :P Also split OptionItem classes into separate files, and added support for mixed-set boosters (which I think are way, way cool). --- projects/mtg/Makefile | 2 +- projects/mtg/bin/Res/graphics/Taskboard.png | Bin 79990 -> 80161 bytes projects/mtg/include/DeckDataWrapper.h | 22 +- projects/mtg/include/GameStateAwards.h | 4 +- projects/mtg/include/GameStateDeckViewer.h | 17 +- projects/mtg/include/GameStateShop.h | 63 +- projects/mtg/include/MTGCard.h | 2 +- projects/mtg/include/MTGDeck.h | 6 +- projects/mtg/include/OptionItem.h | 440 +---- projects/mtg/include/ShopItem.h | 93 -- projects/mtg/include/WDataSrc.h | 126 ++ projects/mtg/include/WFilter.h | 136 ++ projects/mtg/include/WGui.h | 479 ++++++ projects/mtg/src/CardGui.cpp | 2 +- projects/mtg/src/Credits.cpp | 2 + projects/mtg/src/DeckDataWrapper.cpp | 209 +-- projects/mtg/src/GameApp.cpp | 3 +- projects/mtg/src/GameStateAwards.cpp | 29 +- projects/mtg/src/GameStateDeckViewer.cpp | 322 ++-- projects/mtg/src/GameStateMenu.cpp | 1 - projects/mtg/src/GameStateShop.cpp | 607 +++++-- projects/mtg/src/GameStateTransitions.cpp | 15 +- projects/mtg/src/MTGCard.cpp | 2 - projects/mtg/src/MTGDeck.cpp | 47 +- projects/mtg/src/OptionItem.cpp | 1207 +------------- projects/mtg/src/ShopItem.cpp | 554 ------- projects/mtg/src/Tasks.cpp | 2 - projects/mtg/src/WDataSrc.cpp | 490 ++++++ projects/mtg/src/WFilter.cpp | 329 ++++ projects/mtg/src/WGui.cpp | 1624 +++++++++++++++++++ projects/mtg/template.vcproj | 32 +- 31 files changed, 4125 insertions(+), 2742 deletions(-) delete mode 100644 projects/mtg/include/ShopItem.h create mode 100644 projects/mtg/include/WDataSrc.h create mode 100644 projects/mtg/include/WFilter.h create mode 100644 projects/mtg/include/WGui.h delete mode 100644 projects/mtg/src/ShopItem.cpp create mode 100644 projects/mtg/src/WDataSrc.cpp create mode 100644 projects/mtg/src/WFilter.cpp create mode 100644 projects/mtg/src/WGui.cpp diff --git a/projects/mtg/Makefile b/projects/mtg/Makefile index ef5a97022..bdb58c8ee 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/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardPrimitive.o objs/CardSelector.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.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/GameStateMenu.o objs/GameStateOptions.o objs/GameStateShop.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/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/MTGRules.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/Pos.o objs/PriceList.o objs/ReplacementEffects.o objs/Rules.o objs/ShopItem.o objs/SimpleMenu.o objs/SimpleMenuItem.o objs/SimplePad.o objs/Subtypes.o objs/TargetChooser.o objs/TargetsList.o objs/TextScroller.o objs/Token.o objs/Translate.o objs/Trash.o objs/utils.o objs/WEvent.o objs/WResourceManager.o objs/WCachedResource.o objs/Tasks.o +OBJS = objs/ActionElement.o objs/ActionLayer.o objs/ActionStack.o objs/AIMomirPlayer.o objs/AIPlayer.o objs/AIStats.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardPrimitive.o objs/CardSelector.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.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/GameStateMenu.o objs/GameStateOptions.o objs/GameStateShop.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/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/MTGRules.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/Pos.o objs/PriceList.o objs/ReplacementEffects.o objs/Rules.o objs/SimpleMenu.o objs/SimpleMenuItem.o objs/SimplePad.o objs/Subtypes.o objs/TargetChooser.o objs/TargetsList.o objs/TextScroller.o objs/Token.o objs/Translate.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 DEPS = $(patsubst objs/%.o, deps/%.d, $(OBJS)) RESULT = $(shell psp-config --psp-prefix 2> Makefile.cache) diff --git a/projects/mtg/bin/Res/graphics/Taskboard.png b/projects/mtg/bin/Res/graphics/Taskboard.png index b9d5437bc4806164b75b0e12da782f57e27a2e81..55086879e071be4d505eb039001a7218349c2a46 100644 GIT binary patch delta 76632 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 J{oQdEWxIn3+pr*4? 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 delta 76443 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
S@w`n@GVQF~a*~rbZ`y|oMd(Q;Oegje)zo7= zxP`vNY3i+hog+agD@EUA{tk6TIBS&X8tc2YRuiJ~Y0OKeumcTT%hQGtqd|+`t(sU9 z%9~tbENGVB%WC44ZAIG5eQ+@ldoW-b6PCqtsGT_7c7rocq{SXIGdzFxu78SW$f*MJ z(NKEMpZ)ZTSg4Bo4(aF&{9wgRoRnE3U>qa-?1R-NQl^sUP@KxXytfWrL9yi&rJ!zp zFZbq|$;UpTImU>WpN1kRTWzj`!*8+~z>(Ulu^b-#42v`FSq2)+u*>CA2ayA53rrR& z#*m{v93ZK})0k;yEKT=zk+}jJ)F6B_^dn z#I3EE+Dui9h+SU~nU6^=b_HT^P9iY$v}6C^Vjw~?$UV+Gprb(VHRz6B`FWLK21uA6 z&^mB`d5PRP@o)^BK5<+&Ulx+gByLEJfFuGXHMrj`yy7(abOUg|k2Aory#G(XHw`H) zqOx$NnE&~#7J8VSWPjwf>1jij)?vOtW89~T-uBvzO`4B2ZI#rRiExa2kZ0yY&+Cin z7%|iLnN*;`-d}Qnlw-~?+60!Jq$JUQ1DGZ$7IPJhJbIM$~g0TM=ee%h{| z)1jm$BgU$D2!QRq6+2Jkrq9G(wM;=ir#(!JP@UI7-lbK-ubX+!(igNiG?)OPXdxd` zsSy-!tytZYi()IR$fA^=F)p@c#hlG)A4{&chdT_PLpZ2M$WZ_zDVreNV;p30b$H6H zo^e~DY9eMFw|^U$B$$3)KQ!*8HcPJkBA3GY99L`f#O!XpjXSpO8g}3)8>p>!%)>3d zrAe89dJ8aW{!Jm}b&9N)l}ptLJg|DXHO3-(FB;m-CA}PD$poMgz5yXajoYw{OkzCr z`06<3qRs^Dr~I?IaYd%mI1z^*n1AA^b68HN2Vr_MAb+#z9(q-g7F7`kqi)p3N>Y-W zclZJxBMnB9Ttwx>)Vq`-v%)~5eMk`)5rgajFA7Pw1}K>6QSRh~;#jyWmd<&1R3i*7 zGZ5x8kI|sjn$9&taKvR!W2V@;w<(URxaC|Df)W=N6mP6HAj4LUmB;}bDvIN)STO@GR2llZgZMYnP$iqEW87F0r~BXEmb z^Hjn{h=_VR&SmQWRxk+tIWwPmg2M71Dod1ny#QAueMc(dOX|-TuR-DBt+~Q!mrI`f z`12-`EkXssn#hL@haX`fL2Im;q zxBYbCWyy&hOby38YO^QsPk_j7^A^dvq0V}Wbfg8>mPBJ6C-IUm@K;fX-x(|?Qj zS&dF^dX8#y!P-MYO)4i5GHeT;MrtBF>Wtz>ZKbTsmTlWMj5*xYBy8e((495#pE+lU zpr{g@GNaO#zGn!l#b=0Cg&8lmo6CGdm^GK%dgTF@nw2DYrSdQ$NpF=siu1PrA0K!O=*c6m_aq8@X>`>H?osIH}4v>3>pJ8qx#7%zX*=3h>xY$J1>c9_weCefpgKrfT;=&4|zYexcWQ^=VLB& z%M4+lP)wbQ!LzTnqC0Z54NkRGC_EEz1+&ciWLMAWvU1pI(r~?A>wkdU_B{wg9;;<= zqfEbL!VAy;#`1$ZgB<+-8*x~$$iv9mT;d9N0!+H%Qjk6(Ep{&7tOXus8ZZ#q^m>{+ zBqOrYcus!=PTV36e@-w>)2{3|11)uhzZ|yO{C}`{S;$2}bjlS+fP0b2 ziypTSi>1AxP`szJPu{g>`UT?Ld!-1a_0_g*WzzWQJ@1(i$KcV*ASsLJoD}MeMdf>>|xW7{;~Ye z975t4DZy#fCx6EwU41;8Fau9={%jGW2K6?EqKWLioea3-`sV=yF8Z^O)U?KB!!4MX z-JP4L#f=wzt(~-vJQU`d4{k+TV=ho0FmdCtROOTb8|CX~t7OkhXT`ZmRaP8sOQ+sG ztC#z66mcnqVq5QMP0(U0Qk7Fm*w={5%Bu@JM3O)HPJgBy8^WTE$|}xB;sp0$L})Kc z%|_?gIQs>Ti0~}$Q-8|*es)EE-l9eGhpqsqpgC;9m`d(f9#`g!AdoH`r=+5%3t3B%He!hPS& zxDsqF#D6)I83%R}dD5`rIELGCx8`F?!b5sd%!?V2G_(kBx(e)A>-=|nFnVb5-vUs} zQEEEkmMSw!=LGw=|pyix$2n}0?gV-lItS3hjADiAh|NjPk{ z<{b5&yz;TykeRzT=7AYbL9mQQC#*Hflivz4e}4ezDjGXmp|dS(-N(l>tketRh)pWe zqD=~4{;(xW9fFteh_oWNi{JWQK^rBzeek`^)(OEwctjgL2cs1sO{f*vmukq0Jj1H_ zy@rUa)?l_}(K*`=!ZE!U9U7;h0b^>#J#(E@jII0T znt!Ruk5e3Kl^vhjf2*H7!krA&2f{2W3E%i45jzcMv!!7~>m9m02f(Ia+8My$< zHfMx0@^pPbfMXfS?j8cMtkyZjSZlr)f`1a$bkVD?2-eg9Yq=^N|9Md69>n%w{k4kZ zv*viO%E~`qB>;I=gN8R?f{3lPFhyzAPj9^kE^-f`_KYU8uyb$mbGda|t8iF=9qgrZ zvIW%BIFWQ(e zB8#zLM0&K8|wNmws zmH^>OC0csQmlKSSCX>-_noxDEb{Q;Y8c7r)(?rwEywqH?AZ1&_c>{WD`=sdFAu$VFkzj(>7Q;z|
7AqzmwBH4`z;h% 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%0DlQa=!8-SRM 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 Sem_M30000 #include using std::map; @@ -23,27 +24,22 @@ class Cmp1 { // compares cards by their name } }; -class DeckDataWrapper{ +class DeckDataWrapper: public WSrcDeck { public: - int colors[Constants::MTG_NB_COLORS+1]; - int currentColor; - map cards; MTGDeck * parent; + int counts[Constants::MTG_NB_COLORS]; DeckDataWrapper(MTGDeck * deck); ~DeckDataWrapper(); - int Add(MTGCard * card, int quantity = 1); + int Add(MTGCard * c, int quantity=1); + int Remove(MTGCard * c, int quantity=1, bool erase=false); int Add(MTGDeck * deck); - int Remove(MTGCard * card); - MTGCard * getNext(MTGCard * previous = NULL, int color = -1); - MTGCard * getPrevious(MTGCard * next = NULL, int color = -1); - void updateCounts(MTGCard * card = NULL, int quantity = 1); - int getCount(int color = -1); - int totalPrice(); + int getCount(int color=-1); + void updateCounts(); + bool next() {currentPos++; return true;}; + bool prev() {currentPos--; return true;}; void save(); - int countByName(MTGCard * card); - int count(MTGCard * card); }; #endif diff --git a/projects/mtg/include/GameStateAwards.h b/projects/mtg/include/GameStateAwards.h index 889248060..d1e297770 100644 --- a/projects/mtg/include/GameStateAwards.h +++ b/projects/mtg/include/GameStateAwards.h @@ -7,7 +7,7 @@ class WGuiList; class WGuiMenu; -class WSrcMTGSet; +class WSrcCards; class GameStateAwards: public GameState, public JGuiListener { @@ -16,7 +16,7 @@ class GameStateAwards: public GameState, public JGuiListener WGuiMenu * detailview; JQuad * mBg; JTexture * mBgTex; - WSrcMTGSet * setSrc; + WSrcCards * setSrc; SimpleMenu * menu; bool showMenu; bool showAlt; diff --git a/projects/mtg/include/GameStateDeckViewer.h b/projects/mtg/include/GameStateDeckViewer.h index 24e969dae..807153ac5 100644 --- a/projects/mtg/include/GameStateDeckViewer.h +++ b/projects/mtg/include/GameStateDeckViewer.h @@ -15,6 +15,8 @@ #include "../include/PlayerData.h" #include "../include/DeckDataWrapper.h" #include "../include/DeckStats.h" +#include "../include/WDataSrc.h" +#include "../include/WGui.h" #define NO_USER_ACTIVITY_HELP_DELAY 10 #define NO_USER_ACTIVITY_SHOWCARD_DELAY 0.1 @@ -28,7 +30,8 @@ enum STAGE_TRANSITION_DOWN = 4, STAGE_ONSCREEN_MENU = 5, STAGE_WELCOME = 6, - STAGE_MENU = 7 + STAGE_MENU = 7, + STAGE_FILTERS = 8 }; @@ -41,6 +44,8 @@ enum #define MED_SPEED 5.0 #define LOW_SPEED 1.5 +#define MAX_SAVED_FILTERS 8 + static const int STATS_FOR_TURNS = 8; static const int STATS_MAX_MANA_COST = 9; @@ -100,11 +105,12 @@ private: int mStage; int nbDecks; int deckNum; - int colorFilter; + int useFilter[2]; JMusic * bgMusic; JQuad * backQuad; + WGuiFilters * filterDeck; + WGuiFilters * filterCollection; SimpleMenu * welcome_menu; - bool showing_user_deck; SimpleMenu * menu; SimpleMenu * sellMenu; PriceList* pricelist; @@ -116,7 +122,6 @@ private: MTGCard * currentCard; MTGCard * cardIndex[7]; int hudAlpha; - float scrollSpeed; int delSellMenu; string newDeckname; StatsWrapper stw; @@ -127,12 +132,12 @@ public: virtual ~GameStateDeckViewer(); void updateDecks(); void rotateCards(int direction); - void loadIndexes(MTGCard * current = NULL); + void loadIndexes(); + void updateFilters(); void switchDisplay(); void Start(); virtual void End(); void addRemove(MTGCard * card); - int Remove(MTGCard * card); virtual void Update(float dt); void renderOnScreenBasicInfo(); void renderSlideBar(); diff --git a/projects/mtg/include/GameStateShop.h b/projects/mtg/include/GameStateShop.h index acea3d472..8c7360cde 100644 --- a/projects/mtg/include/GameStateShop.h +++ b/projects/mtg/include/GameStateShop.h @@ -4,7 +4,11 @@ #include #include "../include/GameState.h" #include "../include/SimpleMenu.h" -#include "../include/ShopItem.h" +#include "../include/OptionItem.h" +#include "../include/PriceList.h" +#include "../include/PlayerData.h" +#include "../include/CardDisplay.h" +#include "../include/DeckDataWrapper.h" #include "../include/Tasks.h" @@ -14,28 +18,74 @@ #define STAGE_SHOP_SHOP 4 #define STAGE_SHOP_TASKS 5 #define STAGE_FADE_IN 6 +#define STAGE_ASK_ABOUT 7 +#define BOOSTER_SLOTS 3 +#define SHOP_SLOTS 11 + +#define SHOP_ITEMS SHOP_SLOTS+1 +#define LIST_FADEIN 11 + +struct ShopBooster{ + MTGSetInfo * mainSet; + MTGSetInfo * altSet; +}; class GameStateShop: public GameState, public JGuiListener { private: - ShopItems * shop; + WSrcCards * srcCards; JTexture * altThumb[8]; JQuad * mBack; JQuad * mBg; JTexture * mBgTex; TaskList * taskList; + float mElapsed; + WGuiMenu * shopMenu; + WGuiFilters * filterMenu; //Filter menu slides in sideways from right, or up from bottom. + WGuiCardImage * bigDisplay; + CardDisplay * boosterDisplay; + vector subBooster; + MTGDeck * booster; + bool bListCards; - SimpleMenu * menu; + void beginFilters(); + void makeDisplay(MTGDeck * d); + void deleteDisplay(); + + WSyncable bigSync; + SimpleMenu * menu; + PriceList * pricelist; + PlayerData * playerdata; + bool mTouched; + int mPrices[SHOP_ITEMS]; + int mCounts[SHOP_ITEMS]; + int mInventory[SHOP_ITEMS]; + int lightAlpha; + int alphaChange; + + DeckDataWrapper * myCollection; + int mStage; - char starterBuffer[128], boosterBuffer[128]; - char setNames[SHOP_BOOSTERS][128]; - int setIds[SHOP_BOOSTERS]; + ShopBooster mBooster[BOOSTER_SLOTS]; + void load(); + void save(bool force=false); + + void assembleBooster(int controlId); + void beginPurchase(int controlId); + void purchaseCard(int controlId); + void purchaseBooster(int controlId); + int purchasePrice(int offset); + string descPurchase(int controlId, bool tiny = false); + + + static int randomKey; public: GameStateShop(GameApp* parent); virtual ~GameStateShop(); + static void passOneDay() {randomKey = rand();}; virtual void Start(); virtual void End(); virtual void Create(); @@ -43,6 +93,7 @@ class GameStateShop: public GameState, public JGuiListener virtual void Update(float dt); virtual void Render(); virtual void ButtonPressed(int controllerId, int controlId); + static float _x1[],_y1[],_x2[],_y2[],_x3[],_y3[],_x4[],_y4[]; }; diff --git a/projects/mtg/include/MTGCard.h b/projects/mtg/include/MTGCard.h index a7db8f198..ab09e3644 100644 --- a/projects/mtg/include/MTGCard.h +++ b/projects/mtg/include/MTGCard.h @@ -19,6 +19,7 @@ using namespace std; class MTGCard { protected: + friend class MTGSetInfo; int mtgid; char rarity; char image_name[MTGCARD_NAME_SIZE]; @@ -38,7 +39,6 @@ class MTGCard { int getId(); - char getRarity(); void setRarity(char _rarity); diff --git a/projects/mtg/include/MTGDeck.h b/projects/mtg/include/MTGDeck.h index 7299aa7f2..6f7e24632 100644 --- a/projects/mtg/include/MTGDeck.h +++ b/projects/mtg/include/MTGDeck.h @@ -61,14 +61,16 @@ public: int Add(const char * subtype); int findSet(string value); int findBlock(string s); - int size(); + int getSetNum(MTGSetInfo*i); + int operator[](string id); //Returns set id index, -1 for failure. string operator[](int id); //Returns set id name, "" for failure. MTGSetInfo* getInfo(int setID); - + MTGSetInfo* randomSet(int blockId = -1, int atleast = -1); //Tries to match, otherwise 100% random unlocked set + protected: vector blocks; vector setinfo; diff --git a/projects/mtg/include/OptionItem.h b/projects/mtg/include/OptionItem.h index f8cf5bdd0..9df935482 100644 --- a/projects/mtg/include/OptionItem.h +++ b/projects/mtg/include/OptionItem.h @@ -6,6 +6,9 @@ #include #include "../include/GameApp.h" #include "../include/GameOptions.h" +#include "../include/WFilter.h" +#include "../include/WDataSrc.h" +#include "../include/WGui.h" using std::string; @@ -16,122 +19,6 @@ using std::string; #define PATH_MAX 4096 #endif -class WGuiColor{ -public: - enum { - SCROLLBAR, - SCROLLBUTTON, - //Foregrounds only after this - TEXT, - TEXT_HEADER, - TEXT_FAIL, - TEXT_TAB, - TEXT_BODY, - //Backgrounds only after this - BACK, - BACK_ALERT, - BACK_HEADER, - BACK_FAIL, - BACK_TAB, - }; -}; - -//Complete item interface -class WGuiBase{ -public: - WGuiBase() {}; - virtual ~WGuiBase() {}; - - virtual bool Selectable() {return true;}; - virtual bool isModal() {return false;}; - virtual bool Visible() {return true;}; - - virtual bool Changed() {return false;}; - virtual void confirmChange(bool confirmed) {}; - virtual PIXEL_TYPE getColor(int type); - - virtual void Entering(u32 key)=0; - virtual bool Leaving(u32 key)=0; - - virtual void Update(float dt)=0; - virtual void updateValue(){}; - virtual void Render()=0; - virtual void setData()=0; - virtual void ButtonPressed(int controllerId, int controlId){}; - virtual void Reload(){}; - virtual void Overlay(){}; - virtual void Underlay(){}; - - virtual bool hasFocus()=0; - virtual void setFocus(bool bFocus)=0; - virtual float getX()=0; - virtual float getY()=0; - virtual float getWidth()=0; - virtual float getHeight()=0; - virtual int getId() {return INVALID_ID;}; - virtual string getDisplay(){return "";}; - - virtual void setModal(bool val){}; - virtual void setDisplay(string s){}; - virtual void setX(float _x){}; - virtual void setY(float _y){}; - virtual void setWidth(float _w){}; - virtual void setHeight(float _h){}; - virtual void setId(int _id){}; - virtual void setHidden(bool bHidden) {}; - virtual void setVisible(bool bVisisble) {}; - - virtual void renderBack(WGuiBase * it); - virtual void subBack(WGuiBase * item) {}; - - virtual bool CheckUserInput(u32 key) {return false;}; -}; - -//This is our base class for concrete items. -class WGuiItem: public WGuiBase{ -public: - virtual void Entering(u32 key); - virtual bool Leaving(u32 key); - virtual bool CheckUserInput(u32 key); - virtual void Update(float dt) {}; - virtual void Render(); - - WGuiItem(string _display, u8 _mF = 0); - virtual ~WGuiItem() {}; - - string _(string input); //Override global with our flag checker. - - virtual void setData(){}; - - virtual bool hasFocus() {return mFocus;}; - virtual void setFocus(bool bFocus) {mFocus = bFocus;}; - - virtual string getDisplay(){return displayValue;}; - virtual void setDisplay(string s){displayValue=s;}; - - virtual int getId() {return INVALID_ID;}; - virtual float getX() {return x;}; - virtual float getY() {return y;}; - virtual float getWidth() {return width;}; - virtual float getHeight() {return height;}; - virtual void setId(int _id){}; - virtual void setX(float _x){x = _x;}; - virtual void setY(float _y){y = _y;}; - virtual void setWidth(float _w){width = _w;}; - virtual void setHeight(float _h){height = _h;}; - enum { - NO_TRANSLATE = (1<<1), - }; - - u8 mFlags; - -protected: - bool mFocus; - float x, y; - float width, height; - string displayValue; -}; - class OptionItem: public WGuiItem{ public: OptionItem( int _id, string _displayValue); @@ -145,327 +32,6 @@ protected: int id; }; -class WDataSource{ -public: - WDataSource() {}; - virtual JQuad * getImage() {return NULL;}; - virtual MTGCard * getCard() {return NULL;}; - virtual bool thisCard(int mtgid) {return false;}; - virtual int getControlID() {return -1;}; //TODO FIXME: Need a "not a valid button" define. - virtual int getPos() {return -1;}; - virtual bool setPos(int pos) {return false;}; - virtual bool next() {return false;}; - virtual bool prev() {return false;}; - virtual void Update(float dt) {}; -}; - -class WSrcImage: public WDataSource{ -public: - virtual JQuad * getImage(); - WSrcImage(string s); - -protected: - string filename; -}; - -class WSrcMTGSet: public WDataSource{ -public: - WSrcMTGSet(int setid, float mDelay=0.2); - - virtual JQuad * getImage(); - virtual MTGCard * getCard(); - - virtual bool thisCard(int mtgid); - virtual bool next(); - virtual bool prev(); - virtual int getPos() {return currentCard;}; - virtual bool setPos(int pos); - virtual void Update(float dt); - -protected: - vector cards; - int currentCard; - float mDelay; - float mLastInput; -}; - - -struct WCardSort{ -public: - virtual bool operator()(const MTGCard*l, const MTGCard*r) = 0; -}; - -struct WCSortCollector: public WCardSort{ - bool operator()(const MTGCard*l, const MTGCard*r); -}; - -struct WCSortAlpha: public WCardSort{ - bool operator()(const MTGCard*l, const MTGCard*r); -}; - -class WGuiImage: public WGuiItem{ -public: - WGuiImage(WDataSource * wds, float _w = 0, float _h = 0, int _margin = 0); - virtual bool Selectable() {return false;}; - virtual void Render(); - virtual float getHeight(); - virtual void imageScale(float _w, float _h); -protected: - int margin; - float imgW, imgH; - WDataSource * source; -}; - -class WGuiCardImage: public WGuiImage{ -public: - WGuiCardImage(WDataSource * wds, int _offset=0); - virtual void Render(); -protected: - int offset; -}; - -//This is our base class for decorators. It wraps everything about WGuiBase. -class WGuiDeco: public WGuiBase{ -public: - WGuiDeco(WGuiBase* _it) {it = _it;}; - virtual ~WGuiDeco() {SAFE_DELETE(it);}; - - virtual bool Selectable() {return it->Selectable();}; - virtual bool Visible() {return it->Visible();}; - virtual bool Changed() {return it->Changed();}; - virtual void confirmChange(bool confirmed) {it->confirmChange(confirmed);}; - - virtual void Entering(u32 key) {it->Entering(key);}; - virtual bool Leaving(u32 key) {return it->Leaving(key);}; - virtual void Update(float dt) {it->Update(dt);}; - virtual void updateValue() {it->updateValue();}; - virtual void Reload() {it->Reload();}; - virtual void Overlay() {it->Overlay();}; - virtual void Underlay() {it->Underlay();}; - virtual void Render() {it->Render();}; - virtual void setData() {it->setData();}; - - virtual void ButtonPressed(int controllerId, int controlId) {it->ButtonPressed(controllerId, controlId);}; - - virtual bool hasFocus() {return it->hasFocus();}; - virtual string getDisplay() {return it->getDisplay();}; - virtual int getId() {return it->getId();}; - virtual float getX() {return it->getX();}; - virtual float getY() {return it->getY();}; - virtual float getWidth() {return it->getWidth();}; - virtual float getHeight() {return it->getHeight();}; - virtual PIXEL_TYPE getColor(int type) {return it->getColor(type);}; - WGuiBase * getDecorated() {return it;}; - - virtual void setFocus(bool bFocus) {it->setFocus(bFocus);}; - virtual void setDisplay(string s) {it->setDisplay(s);}; - virtual void setId(int _id) {it->setId(_id);}; - virtual void setX(float _x) {it->setX(_x);}; - virtual void setY(float _y) {it->setY(_y);}; - virtual void setWidth(float _w) {it->setWidth(_w);}; - virtual void setHeight(float _h) {it->setHeight(_h);}; - virtual void setHidden(bool bHidden) {it->setHidden(bHidden);}; - virtual void setVisible(bool bVisisble) {it->setVisible(bVisisble);}; - virtual bool CheckUserInput(u32 key) {return it->CheckUserInput(key);}; -protected: - WGuiBase * it; -}; - -class WGuiAward: public WGuiItem{ -public: - WGuiAward(int _id, string name, string _text, string _details=""); - virtual ~WGuiAward(); - virtual void Render(); - virtual bool Selectable() {return Visible();}; - virtual bool Visible(); - virtual int getId() {return id;}; - virtual void Underlay(); - virtual void Overlay(); - -protected: - string details; - int id; - string text; -}; - -class WGuiSplit: public WGuiItem{ -public: - WGuiSplit(WGuiBase* _left,WGuiBase* _right); - virtual ~WGuiSplit(); - - virtual void Reload(); - virtual void Overlay(); - virtual void Underlay(); - virtual void setData(); - virtual bool isModal(); - virtual void setModal(bool val); - virtual void Render(); - virtual void Update(float dt); - virtual void setX(float _x); - virtual void setY(float _y); - virtual void setWidth(float _w); - virtual void setHeight(float _h); - virtual float getHeight(); - virtual void ButtonPressed(int controllerId, int controlId); - virtual void confirmChange(bool confirmed); - - virtual void Entering(u32 key); - virtual bool Leaving(u32 key); - virtual bool CheckUserInput(u32 key); - - bool bRight; - float percentRight; - WGuiBase* right; - WGuiBase* left; -}; - -class WDecoConfirm: public WGuiDeco{ -public: - WDecoConfirm(JGuiListener * _listener, WGuiBase * it); - virtual ~WDecoConfirm(); - - virtual bool isModal(); - virtual void setData(); - virtual void setModal(bool val); - virtual void Entering(u32 key); - virtual bool Leaving(u32 key); - virtual void Update(float dt); - virtual void Overlay(); - virtual void ButtonPressed(int controllerId, int controlId); - virtual bool CheckUserInput(u32 key); - - string confirm; - string cancel; -protected: - enum { - OP_UNCONFIRMED, - OP_CONFIRMING, - OP_CONFIRMED, - } mState; - SimpleMenu * confirmMenu; - JGuiListener * listener; - bool bModal; -}; - -class WDecoEnum : public WGuiDeco { - public: - WDecoEnum(WGuiBase * _it,EnumDefinition *_edef = NULL); - virtual void Render(); - string lookupVal(int value); - protected: - EnumDefinition * edef; -}; - -class WDecoCheat: public WGuiDeco { - public: - WDecoCheat(WGuiBase * _it); - virtual bool Visible(); - bool Selectable(); - virtual void Reload(); -protected: - bool bVisible; -}; - -class WGuiButton: public WGuiDeco{ -public: - WGuiButton( WGuiBase* _it, int _controller, int _control, JGuiListener * jgl); - virtual void updateValue(); - virtual bool CheckUserInput(u32 key); - virtual bool Selectable() {return Visible();}; - virtual PIXEL_TYPE getColor(int type); -protected: - int control, controller; - JGuiListener * mListener; -}; - -class WGuiHeader:public WGuiItem{ - public: - - WGuiHeader(string _displayValue): WGuiItem(_displayValue) {}; - virtual bool Selectable() {return false;}; - virtual void Render(); - -}; - -class WDecoStyled: public WGuiDeco{ -public: - WDecoStyled(WGuiItem * _it) : WGuiDeco(_it) {mStyle=DS_DEFAULT;}; - PIXEL_TYPE getColor(int type); - void subBack(WGuiBase * item); - enum { - DS_DEFAULT = (1<<0), - DS_COLOR_BRIGHT = (1<<1), - DS_COLOR_DARK = (1<<2), - DS_STYLE_ALERT = (1<<3), - DS_STYLE_EDGED = (1<<4), - DS_STYLE_BACKLESS = (1<<5), - }; - - u8 mStyle; -}; - -class WGuiMenu: public WGuiItem{ -public: - - virtual ~WGuiMenu(); - WGuiMenu(u32 next, u32 prev); - - virtual void Render(); - virtual void Reload(); - virtual void Update(float dt); - virtual void ButtonPressed(int controllerId, int controlId); - virtual void Add(WGuiBase* item); //Remember, does not set X & Y of items automatically. - virtual void confirmChange(bool confirmed); - virtual bool Leaving(u32 key); - virtual void Entering(u32 key); - virtual void subBack(WGuiBase * item); - virtual bool CheckUserInput(u32 key); - - - WGuiBase * Current(); - virtual bool nextItem(); - virtual bool prevItem(); - virtual bool isModal(); - virtual void setModal(bool val); - - void setData(); - -protected: - u32 buttonNext, buttonPrev; - vector items; - int currentItem; - u32 held; - float duration; -}; - -class WGuiList: public WGuiMenu{ - public: - WGuiList(string name, WDataSource * syncme = NULL); - - string failMsg; - - virtual void Render(); - virtual void confirmChange(bool confirmed); - virtual void ButtonPressed(int controllerId, int controlId); - virtual void setData(); - - virtual bool nextItem(); - virtual bool prevItem(); - - WGuiBase * operator[](int); -protected: - WDataSource * sync; - bool mFocus; -}; - -class WGuiTabMenu: public WGuiMenu { - public: - WGuiTabMenu() : WGuiMenu(PSP_CTRL_RTRIGGER,PSP_CTRL_LTRIGGER) {}; - virtual void Render(); - virtual void Add(WGuiBase * it); - void save(); -}; - class OptionInteger:public OptionItem{ public: int value; //Current value. diff --git a/projects/mtg/include/ShopItem.h b/projects/mtg/include/ShopItem.h deleted file mode 100644 index 55bd3421d..000000000 --- a/projects/mtg/include/ShopItem.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef _SHOP_ITEM_H -#define _SHOP_ITEM_H - -#include -#include -#include "SimpleMenu.h" -#include "MTGDeck.h" -#include "../include/PriceList.h" -#include "../include/PlayerData.h" -#include "../include/CardDisplay.h" -#include "../include/DeckDataWrapper.h" - -#include -using std::string; - -class hgeDistortionMesh; - -#define SHOP_BOOSTERS 3 - -class ShopItem:public JGuiObject{ - private: - friend class ShopItems; - bool mHasFocus; - bool mRelease; - JLBFont *mFont; - string mText; - float xy[8]; - JQuad * quad; - JQuad * thumb; - float mScale; - float mTargetScale; - hgeDistortionMesh* mesh; - - void updateThumb(); - - public: - int nameCount; - int quantity; - MTGCard * card; - int price; - ShopItem(int id, JLBFont * font, int _cardid, float _xy[], bool hasFocus, MTGAllCards * collection, int _price, DeckDataWrapper * ddw); - ShopItem(int id, JLBFont * font, char* text, JQuad * _quad, JQuad * _thumb,float _xy[], bool hasFocus, int _price); - ~ShopItem(); - int updateCount(DeckDataWrapper * ddw); - - virtual void Render(); - virtual void Update(float dt); - - virtual void Entering(); - virtual bool Leaving(u32 key); - virtual bool ButtonPressed(); - - const char * getText(); - virtual ostream& toString(ostream& out) const; -}; - -class ShopItems:public JGuiController,public JGuiListener{ - private: - PlayerData * playerdata; - PriceList * pricelist; - int mX, mY, mHeight; - JLBFont* mFont; - JTexture * mBgAATex; - JQuad * mBgAA; - MTGAllCards * collection; - SimpleMenu * dialog; - int showPriceDialog; - int setIds[SHOP_BOOSTERS]; - MTGCardInstance * displayCards[100]; - CardDisplay * display; - void safeDeleteDisplay(); - DeckDataWrapper * myCollection; - - int lightAlpha; - int alphaChange; - - public: - bool showCardList; - ShopItems(int id, JGuiListener* listener, JLBFont* font, int x, int y, MTGAllCards * _collection, int _setIds[]); - ~ShopItems(); - void Render(); - virtual void Update(float dt); - void Add(int cardid); - void Add(char * text, JQuad * quad, JQuad * thumb,int _price); - void pricedialog(int id, int mode=1); - virtual void ButtonPressed(int controllerId, int controlId); - bool CheckUserInput(u32 key); - void savePriceList(); - void saveAll(); - static float _x1[],_y1[],_x2[],_y2[],_x3[],_y3[],_x4[],_y4[]; -}; - -#endif diff --git a/projects/mtg/include/WDataSrc.h b/projects/mtg/include/WDataSrc.h new file mode 100644 index 000000000..ce61a9386 --- /dev/null +++ b/projects/mtg/include/WDataSrc.h @@ -0,0 +1,126 @@ +#ifndef _WDATASRC_H_ +#define _WDATASRC_H_ + +class WCardFilter; +struct WCardSort; +struct WDistort; +class PriceList; +class MTGDeck; +class MTGAllCards; + +class WSyncable{ +public: + WSyncable(int i=0) {hooked = NULL;currentPos = 0;}; + virtual ~WSyncable() {}; + //Local + virtual bool Hook(WSyncable* s); + virtual int getOffset() {return currentPos;}; + virtual bool setOffset(int i) {currentPos = i; return true;}; + //Recursive + virtual int getPos(); + virtual bool next(); + virtual bool prev(); +protected: + WSyncable * hooked; //Simple link list + int currentPos; +}; + +class WDataSource: public WSyncable{ +public: + WDataSource() {}; + virtual JQuad * getImage(int offset=0) {return NULL;}; + virtual JQuad * getThumb(int offset=0) {return NULL;}; + virtual MTGCard * getCard(int offset=0, bool ignore=false) {return NULL;}; + virtual MTGDeck * getDeck(int offset=0) {return NULL;}; + virtual WDistort * getDistort(int offset=0) {return NULL;}; + virtual bool thisCard(int mtgid) {return false;}; + virtual int getControlID() {return -1;}; //TODO FIXME: Need a "not a valid button" define. + virtual void Update(float dt) {mLastInput += dt;}; + virtual void Touch() {mLastInput = 0;}; + virtual float getElapsed() {return mLastInput;}; + virtual void setElapsed(float f) {mLastInput = f;}; +protected: + float mLastInput; +}; + +class WSrcImage: public WDataSource{ +public: + virtual JQuad * getImage(int offset=0); + WSrcImage(string s); + +protected: + string filename; +}; + +class WSrcCards: public WDataSource{ +public: + WSrcCards(float delay=0.2); + ~WSrcCards(); + + virtual JQuad * getImage(int offset=0); + virtual JQuad * getThumb(int offset=0); + virtual MTGCard * getCard(int offset=0, bool ignore=false); + virtual int Size(bool all=false); //Returns the number of cards currently matched + + virtual void Shuffle(); + virtual bool thisCard(int mtgid); + virtual bool next(); + virtual bool prev(); + + virtual void Sort(int method); + virtual bool setOffset(int pos); + virtual void addFilter(WCardFilter * f); + virtual void clearFilters(); + virtual bool matchesFilters(MTGCard * c); + virtual void validateFilters(); + virtual void bakeFilters(); //Discards all invalidated cards. + virtual float filterFee(); + + virtual int addToDeck(MTGDeck * i, int num=-1); //Returns num that didn't add + virtual int loadMatches(MTGAllCards* ac); //loadMatches adds the cards from something + virtual int loadMatches(MTGDeck * deck); //into this, if it matches our filter + virtual int loadMatches(WSrcCards* src, bool all=false); //If all==true, ignore filters on src. + + enum { + MAX_CYCLES = 4, //How many cycles to search, for addToDeck + SORT_COLLECTOR, + SORT_ALPHA + }; +protected: + vector cards; + vector validated; + WCardFilter * filtersRoot; + float mDelay; +}; + +class WSrcUnlockedCards: public WSrcCards{ //Only unlocked cards. +public: + WSrcUnlockedCards(float mDelay=0.2); +}; + +class WSrcDeck: public WSrcCards{ +public: + WSrcDeck(float delay=0.2) : WSrcCards(delay) {totalCards=0;}; + virtual int loadMatches(MTGDeck * deck); + virtual int Add(MTGCard * c, int quantity=1); + virtual int Remove(MTGCard * c, int quantity=1, bool erase=false); + void Rebuild(MTGDeck * d); + int count(MTGCard * c); + int countByName(MTGCard * card, bool editions=false); + int totalPrice(); + int totalCopies(); +protected: + map copies; //Maps MTGID to card counts. + int totalCards; +}; + +struct WCSortCollector{ + bool operator()(const MTGCard*l, const MTGCard*r); +}; + +struct WCSortAlpha{ + bool operator()(const MTGCard*l, const MTGCard*r); +}; + + +#endif \ No newline at end of file diff --git a/projects/mtg/include/WFilter.h b/projects/mtg/include/WFilter.h new file mode 100644 index 000000000..4ae769a22 --- /dev/null +++ b/projects/mtg/include/WFilter.h @@ -0,0 +1,136 @@ +#ifndef _WFILTER_H_ +#define _WFILTER_H_ + +class WCardFilter; + +class WCFilterFactory{ +public: + WCFilterFactory() {}; + static WCFilterFactory * GetInstance(); + static void Destroy(); + WCardFilter * Construct(string x); +private: + size_t findNext(string src, size_t start, char open='(', char close=')'); + WCardFilter * Leaf(string src); + WCardFilter * Terminal(string type, string arg); + static WCFilterFactory * me; +}; + +class WCardFilter{ +public: + WCardFilter() {}; + virtual ~WCardFilter() {}; + virtual bool isMatch(MTGCard * c) {return true;}; + virtual string getCode() = 0; + virtual float filterFee() {return 0.0f;}; +}; + +class WCFBranch: public WCardFilter{ +public: + WCFBranch(WCardFilter * a, WCardFilter * b) {lhs=a;rhs=b;}; + ~WCFBranch() {SAFE_DELETE(lhs); SAFE_DELETE(rhs);}; + virtual bool isMatch(MTGCard * c) = 0; + virtual string getCode() = 0; + virtual WCardFilter * Right(){return rhs;}; + virtual WCardFilter * Left(){return lhs;}; +protected: + WCardFilter *lhs, *rhs; +}; + +class WCFilterOR: public WCFBranch{ +public: + WCFilterOR(WCardFilter * a, WCardFilter * b): WCFBranch(a,b) {}; + bool isMatch(MTGCard *c); + string getCode(); + float filterFee(); +}; + +class WCFilterAND: public WCFBranch{ +public: + WCFilterAND(WCardFilter * a, WCardFilter * b): WCFBranch(a,b) {}; + bool isMatch(MTGCard *c) {return (lhs->isMatch(c) && rhs->isMatch(c));}; + string getCode(); + float filterFee(); +}; + +class WCFilterGROUP: public WCardFilter{ +public: + WCFilterGROUP(WCardFilter * _k) {kid = _k;}; + ~WCFilterGROUP() {SAFE_DELETE(kid);}; + bool isMatch(MTGCard *c) {return kid->isMatch(c);}; + string getCode(); + float filterFee() {return kid->filterFee();}; +protected: + WCardFilter * kid; +}; + +class WCFilterNOT: public WCardFilter{ +public: + WCFilterNOT(WCardFilter * _k) {kid = _k;}; + ~WCFilterNOT() {SAFE_DELETE(kid);}; + bool isMatch(MTGCard *c) {return !kid->isMatch(c);}; + string getCode(); +protected: + WCardFilter * kid; +}; + +class WCFilterNULL: public WCardFilter{ +public: + WCFilterNULL() {}; + string getCode() {return "NULL";}; + bool isMatch(MTGCard *c) {return true;}; +}; + + +//Filter terminals: +class WCFilterSet: public WCardFilter{ +public: + WCFilterSet(int _setid=-1) {setid=_setid;}; + WCFilterSet(string arg); + bool isMatch(MTGCard *c) {return (setid==-1 || c->setId == setid);}; + string getCode(); + float filterFee() {return 0.1f;}; +protected: + int setid; +}; +class WCFilterColor: public WCardFilter{ +public: + WCFilterColor(int _c) {color = _c;}; + WCFilterColor(string arg); + bool isMatch(MTGCard * c); + string getCode(); + float filterFee() {return 0.1f;}; +protected: + int color; +}; +class WCFilterType: public WCardFilter{ +public: + WCFilterType(string arg) {type = arg;}; + bool isMatch(MTGCard * c); + string getCode(); + float filterFee() {return 0.2f;}; +protected: + string type; +}; +class WCFilterRarity: public WCardFilter{ +public: + WCFilterRarity(char _r) {rarity = _r;}; + WCFilterRarity(string arg); + bool isMatch(MTGCard * c); + string getCode(); + float filterFee(); +protected: + char rarity; +}; +class WCFilterAbility: public WCardFilter{ +public: + WCFilterAbility(int _a) {ability = _a;}; + WCFilterAbility(string arg); + bool isMatch(MTGCard * c); + string getCode(); + float filterFee(); +protected: + int ability; +}; + +#endif \ No newline at end of file diff --git a/projects/mtg/include/WGui.h b/projects/mtg/include/WGui.h new file mode 100644 index 000000000..59b6c1179 --- /dev/null +++ b/projects/mtg/include/WGui.h @@ -0,0 +1,479 @@ +#ifndef _WGUI_H_ +#define _WGUI_H_ + +class hgeDistortionMesh; + +class WGuiColor{ +public: + enum { + SCROLLBAR, + SCROLLBUTTON, + //Foregrounds only after this + TEXT, + TEXT_HEADER, + TEXT_FAIL, + TEXT_TAB, + TEXT_BODY, + //Backgrounds only after this + BACK, + BACK_ALERT, + BACK_HEADER, + BACK_FAIL, + BACK_TAB, + }; +}; + +struct WDistort { + WDistort(); + WDistort(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); + float & operator[](int p); +protected: + float xy[8]; +}; + +//Complete item interface +class WGuiBase: public JGuiListener { +public: + WGuiBase() {}; + virtual ~WGuiBase() {}; + + virtual bool Selectable() {return true;}; + virtual bool isModal() {return false;}; + virtual bool Visible() {return true;}; + + virtual bool Changed() {return false;}; + virtual void confirmChange(bool confirmed) {}; + virtual PIXEL_TYPE getColor(int type); + virtual float getMargin(int type) {return 4;}; + + virtual void Entering(u32 key)=0; + virtual bool Leaving(u32 key)=0; + + virtual void Update(float dt)=0; + virtual void updateValue(){}; + virtual void Render()=0; + virtual void setData()=0; + virtual void ButtonPressed(int controllerId, int controlId){}; + virtual void Reload(){}; + virtual void Overlay(){}; + virtual void Underlay(){}; + + virtual bool hasFocus()=0; + virtual void setFocus(bool bFocus)=0; + virtual float getX()=0; + virtual float getY()=0; + virtual float getWidth()=0; + virtual float getHeight()=0; + virtual int getId() {return INVALID_ID;}; + virtual string getDisplay() const {return "";}; + virtual float minWidth(){return getWidth();}; + virtual float minHeight(){return getHeight();}; + + virtual void setModal(bool val){}; + virtual void setDisplay(string s){}; + virtual void setX(float _x){}; + virtual void setY(float _y){}; + virtual void setWidth(float _w){}; + virtual void setHeight(float _h){}; + virtual void setId(int _id){}; + virtual void setHidden(bool bHidden) {}; + virtual void setVisible(bool bVisisble) {}; + + virtual void renderBack(WGuiBase * it); + virtual void subBack(WGuiBase * item) {}; + + virtual bool CheckUserInput(u32 key) {return false;}; +}; + +//This is our base class for concrete items. +class WGuiItem: public WGuiBase{ +public: + virtual void Entering(u32 key); + virtual bool Leaving(u32 key); + virtual bool CheckUserInput(u32 key); + virtual void Update(float dt) {}; + virtual void Render(); + + WGuiItem(string _display, u8 _mF = 0); + virtual ~WGuiItem() {}; + + string _(string input); //Override global with our flag checker. + + virtual void setData(){}; + + virtual bool hasFocus() {return mFocus;}; + virtual void setFocus(bool bFocus) {mFocus = bFocus;}; + + virtual string getDisplay() const {return displayValue;}; + virtual void setDisplay(string s){displayValue=s;}; + + virtual int getId() {return INVALID_ID;}; + virtual float getX() {return x;}; + virtual float getY() {return y;}; + virtual float getWidth() {return width;}; + virtual float getHeight() {return height;}; + virtual float minWidth(); + virtual float minHeight(); + virtual void setId(int _id){}; + virtual void setX(float _x){x = _x;}; + virtual void setY(float _y){y = _y;}; + virtual void setWidth(float _w){width = _w;}; + virtual void setHeight(float _h){height = _h;}; + enum { + NO_TRANSLATE = (1<<1), + }; + + u8 mFlags; + +protected: + bool mFocus; + float x, y; + float width, height; + string displayValue; +}; + +class WGuiImage: public WGuiItem{ +public: + WGuiImage(WDataSource * wds, float _w = 0, float _h = 0, int _margin = 0); + virtual bool Selectable() {return false;}; + virtual void Render(); + virtual float getHeight(); + virtual void imageScale(float _w, float _h); + virtual void setSource(WDataSource *s) {source = s;}; +protected: + int margin; + float imgW, imgH; + WDataSource * source;}; + +class WGuiCardImage: public WGuiImage{ +public: + WGuiCardImage(WDataSource * wds, bool _thumb=false); + virtual void Render(); + WSyncable mOffset; +protected: + bool bThumb; +}; + +class WGuiCardDistort: public WGuiCardImage{ +public: + WGuiCardDistort(WDataSource * wds, bool _thumb=false, WDataSource * _distort=NULL); + ~WGuiCardDistort(); + virtual void Render(); + + WDistort xy; +protected: + hgeDistortionMesh* mesh; + WDataSource * distortSrc; +}; + +//This is our base class for decorators. It wraps everything about WGuiBase. +class WGuiDeco: public WGuiBase{ +public: + WGuiDeco(WGuiBase* _it) {it = _it;}; + virtual ~WGuiDeco() {SAFE_DELETE(it);}; + + virtual bool Selectable() {return it->Selectable();}; + virtual bool Visible() {return it->Visible();}; + virtual bool Changed() {return it->Changed();}; + virtual void confirmChange(bool confirmed) {it->confirmChange(confirmed);}; + + virtual void Entering(u32 key) {it->Entering(key);}; + virtual bool Leaving(u32 key) {return it->Leaving(key);}; + virtual void Update(float dt) {it->Update(dt);}; + virtual void updateValue() {it->updateValue();}; + virtual void Reload() {it->Reload();}; + virtual void Overlay() {it->Overlay();}; + virtual void Underlay() {it->Underlay();}; + virtual void Render() {it->Render();}; + virtual void setData() {it->setData();}; + + virtual void ButtonPressed(int controllerId, int controlId) {it->ButtonPressed(controllerId, controlId);}; + + virtual bool hasFocus() {return it->hasFocus();}; + virtual string getDisplay() const {return it->getDisplay();}; + virtual int getId() {return it->getId();}; + virtual float getX() {return it->getX();}; + virtual float getY() {return it->getY();}; + virtual float getWidth() {return it->getWidth();}; + virtual float getHeight() {return it->getHeight();}; + virtual PIXEL_TYPE getColor(int type) {return it->getColor(type);}; + WGuiBase * getDecorated() {return it;}; + + virtual void setFocus(bool bFocus) {it->setFocus(bFocus);}; + virtual void setDisplay(string s) {it->setDisplay(s);}; + virtual void setId(int _id) {it->setId(_id);}; + virtual void setX(float _x) {it->setX(_x);}; + virtual void setY(float _y) {it->setY(_y);}; + virtual void setWidth(float _w) {it->setWidth(_w);}; + virtual void setHeight(float _h) {it->setHeight(_h);}; + virtual void setHidden(bool bHidden) {it->setHidden(bHidden);}; + virtual void setVisible(bool bVisisble) {it->setVisible(bVisisble);}; + virtual bool CheckUserInput(u32 key) {return it->CheckUserInput(key);}; +protected: + WGuiBase * it; +}; + +class WGuiAward: public WGuiItem{ +public: + WGuiAward(int _id, string name, string _text, string _details=""); + virtual ~WGuiAward(); + virtual void Render(); + virtual bool Selectable() {return Visible();}; + virtual bool Visible(); + virtual int getId() {return id;}; + virtual void Underlay(); + virtual void Overlay(); + +protected: + string details; + int id; + string text; +}; + +class WGuiSplit: public WGuiItem{ +public: + WGuiSplit(WGuiBase* _left,WGuiBase* _right); + virtual ~WGuiSplit(); + + virtual void Reload(); + virtual void Overlay(); + virtual void Underlay(); + virtual void setData(); + virtual bool isModal(); + virtual void setModal(bool val); + virtual void Render(); + virtual void Update(float dt); + virtual void setX(float _x); + virtual void setY(float _y); + virtual void setWidth(float _w); + virtual void setHeight(float _h); + virtual float getHeight(); + virtual void ButtonPressed(int controllerId, int controlId); + virtual void confirmChange(bool confirmed); + + virtual void Entering(u32 key); + virtual bool Leaving(u32 key); + virtual bool CheckUserInput(u32 key); + + bool bRight; + float percentRight; + WGuiBase* right; + WGuiBase* left; +}; + +class WDecoConfirm: public WGuiDeco{ +public: + WDecoConfirm(JGuiListener * _listener, WGuiBase * it); + virtual ~WDecoConfirm(); + + virtual bool isModal(); + virtual void setData(); + virtual void setModal(bool val); + virtual void Entering(u32 key); + virtual bool Leaving(u32 key); + virtual void Update(float dt); + virtual void Overlay(); + virtual void ButtonPressed(int controllerId, int controlId); + virtual bool CheckUserInput(u32 key); + + string confirm; + string cancel; +protected: + enum { + OP_UNCONFIRMED, + OP_CONFIRMING, + OP_CONFIRMED, + } mState; + SimpleMenu * confirmMenu; + JGuiListener * listener; + bool bModal; +}; + +class WDecoEnum : public WGuiDeco { + public: + WDecoEnum(WGuiBase * _it,EnumDefinition *_edef = NULL); + virtual void Render(); + string lookupVal(int value); + protected: + EnumDefinition * edef; +}; + +class WDecoCheat: public WGuiDeco { + public: + WDecoCheat(WGuiBase * _it); + virtual bool Visible(); + bool Selectable(); + virtual void Reload(); +protected: + bool bVisible; +}; + +class WGuiButton: public WGuiDeco{ +public: + WGuiButton( WGuiBase* _it, int _controller, int _control, JGuiListener * jgl); + virtual void updateValue(); + virtual bool CheckUserInput(u32 key); + virtual bool Selectable() {return Visible();}; + virtual PIXEL_TYPE getColor(int type); + virtual int getControlID() {return control;}; + virtual int getControllerID() {return controller;}; +protected: + int control, controller; + JGuiListener * mListener; +}; + +class WGuiHeader:public WGuiItem{ + public: + WGuiHeader(string _displayValue): WGuiItem(_displayValue) {}; + virtual bool Selectable() {return false;}; + virtual void Render(); +}; + +class WDecoStyled: public WGuiDeco{ +public: + WDecoStyled(WGuiItem * _it) : WGuiDeco(_it) {mStyle=DS_DEFAULT;}; + PIXEL_TYPE getColor(int type); + void subBack(WGuiBase * item); + enum { + DS_DEFAULT = (1<<0), + DS_COLOR_BRIGHT = (1<<1), + DS_COLOR_DARK = (1<<2), + DS_STYLE_ALERT = (1<<3), + DS_STYLE_EDGED = (1<<4), + DS_STYLE_BACKLESS = (1<<5), + }; + + u8 mStyle; +}; + +class WGuiMenu: public WGuiItem{ +public: + friend class WGuiFilters; + virtual ~WGuiMenu(); + WGuiMenu(u32 next, u32 prev, bool mDPad = false, WSyncable * syncme=NULL); + + virtual void Render(); + virtual void Reload(); + virtual void Update(float dt); + virtual void ButtonPressed(int controllerId, int controlId); + virtual void Add(WGuiBase* item); //Remember, does not set X & Y of items automatically. + virtual void confirmChange(bool confirmed); + virtual bool Leaving(u32 key); + virtual void Entering(u32 key); + virtual void subBack(WGuiBase * item); + virtual bool CheckUserInput(u32 key); + WGuiBase * Current(); + virtual int getSelected() {return currentItem;}; + virtual bool nextItem(); + virtual bool prevItem(); + virtual bool isModal(); + virtual void setModal(bool val); + + void setData(); + +protected: + virtual void syncMove(); + virtual bool isButtonDir(u32 key, int dir); //For the DPad override. + u32 buttonNext, buttonPrev; + bool mDPad; + vector items; + int currentItem; + u32 held; + WSyncable * sync; + float duration; +}; + +class WGuiList: public WGuiMenu{ + public: + WGuiList(string name, WSyncable * syncme = NULL); + + string failMsg; + + virtual void Render(); + virtual void confirmChange(bool confirmed); + virtual void ButtonPressed(int controllerId, int controlId); + virtual void setData(); + WGuiBase * operator[](int); +protected: + bool mFocus; +}; +class WGuiTabMenu: public WGuiMenu { + public: + WGuiTabMenu() : WGuiMenu(PSP_CTRL_RTRIGGER,PSP_CTRL_LTRIGGER) {}; + virtual void Render(); + virtual void Add(WGuiBase * it); + void save(); +}; +class WGuiListRow: public WGuiList{ + public: + WGuiListRow(string n, WSyncable * s = NULL); + virtual void Render(); +}; + +class WGuiFilters: public WGuiItem { +public: + friend class WGuiFilterItem; + WGuiFilters(string header, WSrcCards * src); + ~WGuiFilters(); + bool CheckUserInput(u32 key); + string getCode(); //For use in filter factory. + void Update(float dt); + void Render(); + void Entering(u32 key); + void addColumn(); + bool isAvailable(int type); + bool isAvailableCode(string code); + void Finish(); + bool isFinished() {return bFinished;}; + void ButtonPressed(int controllerId, int controlId); + void buildList(); +protected: + bool bFinished; + WSrcCards* source; + SimpleMenu* subMenu; + WGuiList * list; +}; + +class WGuiFilterItem: public WGuiItem { +public: + friend class WGuiFilters; + friend struct WLFiltersSort; + WGuiFilterItem(WGuiFilters * parent); + void updateValue(); + void ButtonPressed(int controllerId, int controlId); + string getCode(); + bool isModal(); + enum { + STATE_UNSET, + STATE_CHOOSE_TYPE, + STATE_CHOOSE_VAL, + STATE_FINISHED, + STATE_REMOVE, + STATE_CANCEL, + BEGIN_FILTERS = 0, + FILTER_SET = BEGIN_FILTERS, + FILTER_RARITY, + FILTER_COLOR, + FILTER_TYPE, + FILTER_BASIC, + END_FILTERS + }; +protected: + void addArg(string display, string code); + int filterType; + int filterVal; + vector< pair > args; + int mState; + bool mNew; + WGuiFilters * mParent; +}; + +struct WListSort{ + virtual bool operator()(const WGuiBase*l, const WGuiBase*r); +}; + +struct WLFiltersSort{ + bool operator()(const WGuiBase*l, const WGuiBase*r); +}; + +#endif \ No newline at end of file diff --git a/projects/mtg/src/CardGui.cpp b/projects/mtg/src/CardGui.cpp index 384884984..54246285a 100644 --- a/projects/mtg/src/CardGui.cpp +++ b/projects/mtg/src/CardGui.cpp @@ -276,7 +276,7 @@ void CardGui::alternateRender(MTGCard * card, const Pos& pos){ #ifdef _DEBUG else{ char buf[2048]; - sprintf(buf, "\n==\nTypeless card: %s %s\n", setlist[card->setId].c_str(), card->data->getName().c_str()); + sprintf(buf, "Typeless card: %s %s (%i)\n", setlist[card->setId].c_str(), card->data->getName().c_str(), card->getId()); OutputDebugString(buf); } #endif diff --git a/projects/mtg/src/Credits.cpp b/projects/mtg/src/Credits.cpp index 91f86cb2e..f8fbb9d66 100644 --- a/projects/mtg/src/Credits.cpp +++ b/projects/mtg/src/Credits.cpp @@ -7,6 +7,7 @@ #include "../include/Translate.h" #include "../include/MTGDeck.h" #include "../include/GameObserver.h" +#include "../include/GameStateShop.h" CreditBonus::CreditBonus(int _value, string _text){ value = _value; @@ -150,6 +151,7 @@ void Credits::compute(Player * _p1, Player * _p2, GameApp * _app){ playerdata->credits += value; + GameStateShop::passOneDay(); playerdata->taskList->passOneDay(); if (playerdata->taskList->getTaskCount() < 6) { playerdata->taskList->addRandomTask(); diff --git a/projects/mtg/src/DeckDataWrapper.cpp b/projects/mtg/src/DeckDataWrapper.cpp index 671d60bac..f414db526 100644 --- a/projects/mtg/src/DeckDataWrapper.cpp +++ b/projects/mtg/src/DeckDataWrapper.cpp @@ -2,180 +2,67 @@ #include "../include/DeckDataWrapper.h" #include "../include/MTGDeck.h" #include "../include/PriceList.h" +#include "../include/WDataSrc.h" DeckDataWrapper::DeckDataWrapper(MTGDeck * deck){ - parent = deck; - for (int i = 0; i <= Constants::MTG_NB_COLORS; i++){ - colors[i] = 0; - } + parent = deck; + for(int c=0;c::iterator it; - for (it = deck->cards.begin(); it!=deck->cards.end(); it++){ - MTGCard * card = deck->getCardById(it->first); - Add(card,it->second); - } - return 1; + if(loadMatches(deck)) + return 1; + return 0; } +int DeckDataWrapper::Remove(MTGCard * c, int quantity,bool erase){ + if(WSrcDeck::Remove(c,quantity,erase)){ + for(int i=0;idata->hasColor(i)) + counts[i]-=quantity; + } + return 1; + } + return 0; +} +int DeckDataWrapper::Add(MTGCard * c, int quantity){ + if(WSrcDeck::Add(c,quantity)){ + for(int i=0;idata && c->data->hasColor(i)) + counts[i]+=quantity; + } + return 1; + } + return 0; +} +int DeckDataWrapper::getCount(int color){ + if(color < 0 || color >=Constants::MTG_NB_COLORS) + return Size(true); + return counts[color]; +} +void DeckDataWrapper::updateCounts(){ + map::iterator it; + for(int c=0;cremoveAll(); - map::iterator it; - for ( it=cards.begin() ; it != cards.end(); it++ ){ - MTGCard * current = (*it).first; - for (int i = 0; i < (*it).second; i++){ - parent->add(current); + for(int i=0;idata->hasColor(c)){ + it = copies.find(card->getMTGId()); + if(it != copies.end()) + counts[c]+=it->second; + } } } +} +void DeckDataWrapper::save(){ + Rebuild(parent); parent->save(); } DeckDataWrapper::~DeckDataWrapper(){ SAFE_DELETE(parent); -} - -void DeckDataWrapper::updateCounts(MTGCard * card, int increment){ - if (!card){ - for (int i = 0; i < Constants::MTG_NB_COLORS+1; i++){ - colors[i] = 0; - } - map::iterator it; - for ( it=cards.begin() ; it != cards.end(); it++ ){ - MTGCard * current = (*it).first; - colors[Constants::MTG_NB_COLORS] += (*it).second; - for (int i = 0; i < Constants::MTG_NB_COLORS; i++){ - if (current->data->hasColor(i)) colors[i]+=(*it).second; - } - } - }else{ - colors[Constants::MTG_NB_COLORS] += increment; - for (int i = 0; i < Constants::MTG_NB_COLORS; i++){ - if (card->data->hasColor(i)) colors[i]+=increment; - } - } -} - -int DeckDataWrapper::Add(MTGCard * card, int quantity){ - if(cards.find(card) == cards.end()){ - cards[card] = quantity; - }else{ - cards[card]+= quantity; - } - updateCounts(card,quantity); - return cards[card]; -} - -int DeckDataWrapper::Remove(MTGCard * card){ - if(cards.find(card) == cards.end() || cards[card] <= 0) return 0; - cards[card]--; - updateCounts(card,-1); - return 1; -} - -int DeckDataWrapper::count(MTGCard * card){ - if(cards.find(card) == cards.end()){ - cards[card] = 0; - } - return cards[card]; -} - -int DeckDataWrapper::countByName(MTGCard * card){ - string name = card->data->name; - int total = 0; - map::iterator it,it_origin; - it = cards.find(card); - if(it == cards.end()){ - cards[card] = 0; - it = cards.find(card); - } - it_origin = it; - - while(it !=cards.end()){ - MTGCard * _card = (*it).first; - if (name.compare(_card->data->name) !=0){ - it = cards.end(); - }else{ - total+= (*it).second; - it++; - } - } - - it = cards.find(card); - if (it == cards.begin()) return total; - it--; - while(1){ - MTGCard * _card = (*it).first; - if (name.compare(_card->data->name) !=0){ - break; - }else{ - total+= (*it).second; - if (it == cards.begin()) break; - it--; - - } - } - return total; -} - - -MTGCard * DeckDataWrapper::getNext(MTGCard * previous, int color){ - map::iterator it; - - it = cards.find(previous); - - while(1){ - if (it == cards.end()){ - it = cards.begin(); - }else{ - it++; - } - if (it == cards.end()) return NULL; - MTGCard * card = (*it).first; - if (card == previous) return NULL; - if ((*it).second >0 && (color ==-1 || card->data->hasColor(color))){ - return card; - } - } -} - -MTGCard * DeckDataWrapper::getPrevious(MTGCard * next, int color){ - map::iterator it; - it = cards.find(next); - - while(1){ - if (it == cards.begin()){ - it = cards.end(); - }else{ - it--; - } - if (it == cards.end()) return NULL; - MTGCard * card = (*it).first; - if (card == next) return NULL; - if ((*it).second >0 && (color ==-1 || card->data->hasColor(color))){ - return card; - } - } -} - -int DeckDataWrapper::getCount(int color){ - if (color == -1) return colors[Constants::MTG_NB_COLORS]; - return colors[color]; -} - -int DeckDataWrapper::totalPrice(){ - int total = 0; - PriceList * pricelist = NEW PriceList(RESPATH"/settings/prices.dat",this->parent->database); - map::iterator it; - for ( it=cards.begin() ; it != cards.end(); it++ ){ - MTGCard * current = (*it).first; - int nb = (*it).second; - if (nb) total += pricelist->getPrice(current->getMTGId()); - } - delete pricelist; - return total; -} +} \ No newline at end of file diff --git a/projects/mtg/src/GameApp.cpp b/projects/mtg/src/GameApp.cpp index b657ea28f..680720945 100644 --- a/projects/mtg/src/GameApp.cpp +++ b/projects/mtg/src/GameApp.cpp @@ -20,6 +20,7 @@ #include "../include/DeckStats.h" #include "../include/DeckMetaData.h" #include "../include/Translate.h" +#include "../include/WFilter.h" #define DEFAULT_DURATION .25 @@ -233,7 +234,7 @@ void GameApp::Destroy() SAFE_DELETE(music); Translator::EndInstance(); - + WCFilterFactory::Destroy(); SimpleMenu::destroy(); options.theGame = NULL; diff --git a/projects/mtg/src/GameStateAwards.cpp b/projects/mtg/src/GameStateAwards.cpp index 59a0a0219..4de33e80b 100644 --- a/projects/mtg/src/GameStateAwards.cpp +++ b/projects/mtg/src/GameStateAwards.cpp @@ -210,20 +210,23 @@ bool GameStateAwards::enterSet(int setid){ SAFE_DELETE(detailview); SAFE_DELETE(setSrc); - setSrc = NEW WSrcMTGSet(setid); + setSrc = NEW WSrcCards(); + setSrc->addFilter(NEW WCFilterSet(setid)); + setSrc->loadMatches(mParent->collection); + setSrc->bakeFilters(); + setSrc->Sort(WSrcCards::SORT_COLLECTOR); + detailview = NEW WGuiMenu(PSP_CTRL_DOWN,PSP_CTRL_UP); WGuiList * spoiler = NEW WGuiList("Spoiler",setSrc); spoiler->setX(210); spoiler->setWidth(SCREEN_WIDTH - 220); - while(true){ - MTGCard * c = setSrc->getCard(); + for(int t=0;tSize();t++){ + MTGCard * c = setSrc->getCard(t); if(c) spoiler->Add(NEW WGuiItem(c->data->name)); - if(!setSrc->next()) - break; } - setSrc->setPos(0); + setSrc->setOffset(0); spoiler->Entering(0); WGuiCardImage * wi = NEW WGuiCardImage(setSrc); wi->setX(105); @@ -257,18 +260,18 @@ bool GameStateAwards::enterStats(int option){ MTGCard * costly = NULL; MTGCard * strong = NULL; MTGCard * tough = NULL; - map::iterator it; - for (it = ddw->cards.begin(); it!=ddw->cards.end(); it++){ - MTGCard * c = it->first; + for (int t=0;tSize();t++){ + MTGCard * c = ddw->getCard(t); if(!c) continue; - if(!c->data->isLand() && (many == NULL || it->second > dupes)){ + int count = ddw->count(c); + if(!c->data->isLand() && (many == NULL || count > dupes)){ many = c; - dupes = it->second; + dupes = count; } unique++; - counts[c->setId]+=it->second; + counts[c->setId]+=count; if(costly == NULL || c->data->getManaCost()->getConvertedCost() > costly->data->getManaCost()->getConvertedCost()) costly = c; @@ -290,7 +293,7 @@ bool GameStateAwards::enterStats(int option){ sprintf(buf,_("Total Value: %ic").c_str(),ddw->totalPrice()); detailview->Add(NEW WGuiItem(buf,WGuiItem::NO_TRANSLATE));//ddw->colors - sprintf(buf,_("Total Cards (including duplicates): %i").c_str(),ddw->getCount()); + sprintf(buf,_("Total Cards (including duplicates): %i").c_str(),ddw->totalCopies()); detailview->Add(NEW WGuiItem(buf,WGuiItem::NO_TRANSLATE));//ddw->colors sprintf(buf,_("Unique Cards: %i").c_str(),unique); diff --git a/projects/mtg/src/GameStateDeckViewer.cpp b/projects/mtg/src/GameStateDeckViewer.cpp index 6c99a6076..f6129e5c2 100644 --- a/projects/mtg/src/GameStateDeckViewer.cpp +++ b/projects/mtg/src/GameStateDeckViewer.cpp @@ -7,6 +7,7 @@ #include "../include/Translate.h" #include "../include/ManaCostHybrid.h" #include "../include/MTGCardInstance.h" +#include "../include/WFilter.h" #include @@ -29,47 +30,78 @@ void StringExplode(string str, string separator, vector* results){ GameStateDeckViewer::GameStateDeckViewer(GameApp* parent): GameState(parent) { bgMusic = NULL; - scrollSpeed = MED_SPEED; nbDecks = 0; deckNum = 0; + useFilter[0] = 0; + useFilter[1] = 0; mSwitching = false; welcome_menu = NULL; + myCollection = NULL; + myDeck = NULL; + filterDeck = NULL; + filterCollection = NULL; } GameStateDeckViewer::~GameStateDeckViewer() { SAFE_DELETE(bgMusic); + SAFE_DELETE(myDeck); + SAFE_DELETE(myCollection); + SAFE_DELETE(filterDeck); + SAFE_DELETE(filterCollection); } void GameStateDeckViewer::rotateCards(int direction){ - int maxCards=displayed_deck->getCount(colorFilter); - if (maxCards==0) - return; int left = direction; - if (left){ - MTGCard * currentCard = displayed_deck->getNext(cardIndex[6],colorFilter); - for (int i = 1; i<7; i++){ - cardIndex[i-1] = cardIndex[i]; - } - cardIndex[6] = currentCard; - }else{ - MTGCard * currentCard = displayed_deck->getPrevious(cardIndex[0],colorFilter); - for (int i = 5; i>=0; i--){ - cardIndex[i+1] = cardIndex[i]; - } - cardIndex[0] = currentCard; - } + if (left) + displayed_deck->next(); + else + displayed_deck->prev(); + loadIndexes(); } +void GameStateDeckViewer::updateFilters(){ + displayed_deck->clearFilters(); + int i = (displayed_deck == myDeck); -void GameStateDeckViewer::loadIndexes(MTGCard * current){ - for (int i = 0; i < 7; i++){ - cardIndex[i] = NULL; + if(useFilter[i] == 0){ + if(i && filterDeck) + filterDeck->Finish(); + else if(filterCollection) + filterCollection->Finish(); + return; } - MTGCard * _current = current; - _current = displayed_deck->getNext(NULL,colorFilter); + WCFilterFactory * wc = WCFilterFactory::GetInstance(); + switch(useFilter[i]-1){ + case Constants::MTG_COLOR_ARTIFACT: + displayed_deck->addFilter(wc->Construct("c:x;")); + break; + case Constants::MTG_COLOR_GREEN: + displayed_deck->addFilter(wc->Construct("c:g;")); + break; + case Constants::MTG_COLOR_BLUE: + displayed_deck->addFilter(wc->Construct("c:u;")); + break; + case Constants::MTG_COLOR_RED: + displayed_deck->addFilter(wc->Construct("c:r;")); + break; + case Constants::MTG_COLOR_BLACK: + displayed_deck->addFilter(wc->Construct("c:b;")); + break; + case Constants::MTG_COLOR_WHITE: + displayed_deck->addFilter(wc->Construct("c:w;")); + break; + case Constants::MTG_COLOR_LAND: + displayed_deck->addFilter(wc->Construct("t:Land;")); + break; + } + //No sanity checking for color filters + //if(!displayed_deck->Size()) + // displayed_deck->clearFilters(); +} +void GameStateDeckViewer::loadIndexes(){ + int x=0; for (int i = 0; i < 7; i++){ - cardIndex[i] = _current; - _current = displayed_deck->getNext(_current,colorFilter); + cardIndex[i] = displayed_deck->getCard(i); } } @@ -80,6 +112,7 @@ void GameStateDeckViewer::switchDisplay(){ displayed_deck = myCollection; } currentCard = NULL; + updateFilters(); loadIndexes(); } @@ -103,7 +136,9 @@ void GameStateDeckViewer::Start() pricelist = NEW PriceList(RESPATH"/settings/prices.dat",mParent->collection); playerdata = NEW PlayerData(mParent->collection); sellMenu = NULL; - myCollection = NEW DeckDataWrapper(NEW MTGDeck(options.profileFile(PLAYER_COLLECTION).c_str(), mParent->collection)); + MTGDeck * myC = NEW MTGDeck(options.profileFile(PLAYER_COLLECTION).c_str(), mParent->collection); + myCollection = NEW DeckDataWrapper(myC); + myCollection->Sort(WSrcCards::SORT_ALPHA); displayed_deck = myCollection; myDeck = NULL; @@ -113,6 +148,7 @@ void GameStateDeckViewer::Start() menu->Add(2,"Switch decks without saving"); if(options[Options::CHEATMODE].number) menu->Add(-1,"*Complete collection & reset*"); + menu->Add(22,"Filter by..."); menu->Add(3,"Back to main menu"); menu->Add(4,"Cancel"); @@ -154,16 +190,14 @@ void GameStateDeckViewer::Start() JSoundSystem::GetInstance()->PlayMusic(GameApp::music, true); } } - colorFilter = ALL_COLORS; mStage = STAGE_WELCOME; - mRotation = 0; mSlide = 0; mAlpha = 255; currentCard = NULL; - loadIndexes(currentCard); + loadIndexes(); last_user_activity = NO_USER_ACTIVITY_HELP_DELAY + 1; onScreenTransition = 0; @@ -187,30 +221,30 @@ void GameStateDeckViewer::End() SAFE_DELETE(myDeck); SAFE_DELETE(pricelist); SAFE_DELETE(playerdata); + SAFE_DELETE(filterDeck); + SAFE_DELETE(filterCollection); } void GameStateDeckViewer::addRemove(MTGCard * card){ if (!card) return; - if (displayed_deck->Remove(card)){ + if (displayed_deck->Remove(card,1,(displayed_deck==myDeck))){ if (displayed_deck == myCollection){ myDeck->Add(card); + myDeck->Sort(WSrcCards::SORT_ALPHA); }else{ myCollection->Add(card); } } stw.needUpdate = true; + loadIndexes(); } -int GameStateDeckViewer::Remove(MTGCard * card){ - if (!card) return 0; - int result = displayed_deck->Remove(card); - return result; -} - - void GameStateDeckViewer::Update(float dt) { + + int myD = (displayed_deck == myDeck); + if(options.keypadActive()){ options.keypadUpdate(dt); @@ -244,25 +278,27 @@ void GameStateDeckViewer::Update(float dt) { case PSP_CTRL_LEFT : last_user_activity = 0; - currentCard = displayed_deck->getNext(currentCard,colorFilter); + currentCard = displayed_deck->getCard(1); mStage = STAGE_TRANSITION_LEFT; break; case PSP_CTRL_RIGHT : last_user_activity = 0; - currentCard = displayed_deck->getPrevious(currentCard,colorFilter); + currentCard = displayed_deck->getCard(-1); mStage = STAGE_TRANSITION_RIGHT; break; case PSP_CTRL_UP : last_user_activity = 0; mStage = STAGE_TRANSITION_UP; - colorFilter--; - if (colorFilter < -1) colorFilter = Constants::MTG_COLOR_LAND; + useFilter[myD]++; + if(useFilter[myD] >= MAX_SAVED_FILTERS) + useFilter[myD] = 0; break; case PSP_CTRL_DOWN : last_user_activity = 0; mStage = STAGE_TRANSITION_DOWN; - colorFilter ++; - if (colorFilter > Constants::MTG_COLOR_LAND) colorFilter =-1; + useFilter[myD]--; + if(useFilter[myD] < 0) + useFilter[myD] = MAX_SAVED_FILTERS-1; break; case PSP_CTRL_TRIANGLE: options[Options::DISABLECARDS].number = !options[Options::DISABLECARDS].number; @@ -284,7 +320,7 @@ void GameStateDeckViewer::Update(float dt) char buffer[4096]; { MTGCard * card = cardIndex[2]; - if (card && displayed_deck->cards[card]){ + if (card && displayed_deck->count(card)){ int rnd = (rand() % 20); price = pricelist->getPrice(card->getMTGId()) / 2; price = price - price * (rnd -10)/100; @@ -308,12 +344,16 @@ void GameStateDeckViewer::Update(float dt) mStage = STAGE_MENU; break; case PSP_CTRL_SELECT : - if (scrollSpeed == HIGH_SPEED) - scrollSpeed = MED_SPEED; - else if (scrollSpeed == MED_SPEED) - scrollSpeed = LOW_SPEED; - else - scrollSpeed = HIGH_SPEED; + mStage = STAGE_FILTERS; + if(displayed_deck == myDeck){ + if(!filterDeck) + filterDeck = NEW WGuiFilters("Filter by...",myDeck); + filterDeck->Entering(0); + }else if(displayed_deck == myCollection){ + if(!filterCollection) + filterCollection = NEW WGuiFilters("Filter by...",myCollection); + filterCollection->Entering(0); + } break; case PSP_CTRL_LTRIGGER : if (last_user_activity < NO_USER_ACTIVITY_HELP_DELAY){ @@ -350,7 +390,7 @@ void GameStateDeckViewer::Update(float dt) } if (mStage == STAGE_TRANSITION_RIGHT || mStage == STAGE_TRANSITION_LEFT) { if (mStage == STAGE_TRANSITION_RIGHT){ - mRotation -= dt * scrollSpeed; + mRotation -= dt * MED_SPEED; if (mRotation < -1.0f){ do { rotateCards(mStage); @@ -360,7 +400,7 @@ void GameStateDeckViewer::Update(float dt) mRotation = 0; } }else if(mStage == STAGE_TRANSITION_LEFT){ - mRotation += dt * scrollSpeed; + mRotation += dt * MED_SPEED; if (mRotation > 1.0f){ do { rotateCards(mStage); @@ -374,7 +414,8 @@ void GameStateDeckViewer::Update(float dt) if (mStage == STAGE_TRANSITION_DOWN){ mSlide -= 0.05f; if (mSlide < -1.0f){ - loadIndexes(currentCard); + updateFilters(); + loadIndexes(); mSlide = 1; }else if (mSlide > 0 && mSlide < 0.05){ mStage = STAGE_WAITING; @@ -383,7 +424,8 @@ void GameStateDeckViewer::Update(float dt) } if (mStage == STAGE_TRANSITION_UP){ mSlide += 0.05f; if (mSlide > 1.0f){ - loadIndexes(currentCard); + updateFilters(); + loadIndexes(); mSlide = -1; }else if (mSlide < 0 && mSlide > -0.05){ mStage = STAGE_WAITING; @@ -396,6 +438,44 @@ void GameStateDeckViewer::Update(float dt) welcome_menu->Update(dt); }else if (mStage == STAGE_MENU){ menu->Update(dt); + }else if(mStage == STAGE_FILTERS){ + u32 key = mEngine->ReadButton(); + + if(displayed_deck == myDeck){ + if(filterDeck){ + if(key == PSP_CTRL_SELECT){ + useFilter[(displayed_deck == myDeck)] = 0; + filterDeck->Finish(); + filterDeck->Update(dt); + loadIndexes(); + return; + } + if(!filterDeck->isFinished()){ + filterDeck->CheckUserInput(key); + filterDeck->Update(dt); + } else { + mStage = STAGE_WAITING; + loadIndexes(); + } + } + }else{ + if(filterCollection ){ + if(key == PSP_CTRL_SELECT){ + useFilter[(displayed_deck == myDeck)] = 0; + filterCollection->Finish(); + filterCollection->Update(dt); + loadIndexes(); + return; + } + if(!filterCollection->isFinished()){ + filterCollection->CheckUserInput(key); + filterCollection->Update(dt); + } else { + mStage = STAGE_WAITING; + loadIndexes(); + } + } + } } @@ -404,43 +484,37 @@ void GameStateDeckViewer::Update(float dt) void GameStateDeckViewer::renderOnScreenBasicInfo(){ JLBFont * mFont = resources.GetJLBFont(Constants::MAIN_FONT); - char buffer[30], buffer2[30]; + char buffer[256]; + int myD = (displayed_deck == myDeck); float y = 0; JRenderer::GetInstance()->FillRoundRect(SCREEN_WIDTH-125,y-5,110,15,5,ARGB(128,0,0,0)); - sprintf(buffer, "DECK: %i", myDeck->getCount()); - mFont->DrawString(buffer, SCREEN_WIDTH-120 , y); - - if (colorFilter != ALL_COLORS){ - sprintf(buffer2, "( %i)", myDeck->getCount(colorFilter)); - mFont->DrawString(buffer2, SCREEN_WIDTH-55 , y); - JRenderer::GetInstance()->RenderQuad(mIcons[colorFilter], SCREEN_WIDTH-42 , y + 6 , 0.0f,0.5,0.5); - } - + int now, total; + now = displayed_deck->Size(); + total = displayed_deck->Size(true); + if(now != total) + sprintf(buffer, "%s%i of %i (%i cards)", (displayed_deck == myDeck) ? "DECK: " : " ", now, total,displayed_deck->totalCopies()); + else + sprintf(buffer, "%s%i (%i cards)", (displayed_deck == myDeck) ? "DECK: " : " " , total,displayed_deck->totalCopies()); + mFont->DrawString(buffer, SCREEN_WIDTH-22, y+5,JGETEXT_RIGHT); + if (useFilter[myD] != 0) + JRenderer::GetInstance()->RenderQuad(mIcons[useFilter[myD]-1], SCREEN_WIDTH-10 , y + 10 , 0.0f,0.5,0.5); } void GameStateDeckViewer::renderSlideBar(){ JLBFont * mFont = resources.GetJLBFont(Constants::MAIN_FONT); - int total = displayed_deck->getCount(colorFilter); + int total = displayed_deck->Size(); float filler = 15; float y = SCREEN_HEIGHT_F-25; float bar_size = SCREEN_WIDTH_F - 2*filler; JRenderer * r = JRenderer::GetInstance(); typedef map::reverse_iterator rit; - - int currentPos = 0; - { - rit end = rit(displayed_deck->cards.begin()); - rit it = rit(displayed_deck->cards.find(cardIndex[2])); - if (-1 == colorFilter) - for (; it != end; ++it) - currentPos += it->second; - else - for (; it != end; ++it) - if (it->first->data->hasColor(colorFilter)) currentPos += it->second; - } + int currentPos = displayed_deck->getOffset(); + if(total == 0) + return; + currentPos = abs(currentPos) % total; float cursor_pos = bar_size * currentPos / total; r->FillRoundRect(filler + 5,y+5,bar_size,0,3,ARGB(hudAlpha/2,0,0,0)); @@ -530,8 +604,8 @@ void GameStateDeckViewer::renderOnScreenMenu(){ font->DrawString(_("Next"), leftPspX + 15, leftPspY-15); font->DrawString(_("card"), leftPspX - 35, leftPspY); font->DrawString(_("card"), leftPspX + 15, leftPspY); - font->DrawString(_("Next color"), leftPspX - 33, leftPspY - 35); - font->DrawString(_("Prev. color"), leftPspX -33 , leftPspY +25); + font->DrawString(_("Next edition"), leftPspX - 33, leftPspY - 35); + font->DrawString(_("Prev. edition"), leftPspX -33 , leftPspY +25); //RIGHT PSP CIRCLE render r->FillCircle(rightPspX+(onScreenTransition*204),rightPspY,40,ARGB(128,50,50,50)); @@ -552,6 +626,7 @@ void GameStateDeckViewer::renderOnScreenMenu(){ font->DrawString(_("Sell card"), rightPspX - 30 , rightPspY+20); //Bottom menus font->DrawString(_("menu"), SCREEN_WIDTH-35 +rightTransition, SCREEN_HEIGHT-15); + font->DrawString(_("filter"), SCREEN_WIDTH-95 +rightTransition, SCREEN_HEIGHT-15); @@ -596,6 +671,7 @@ void GameStateDeckViewer::renderOnScreenMenu(){ r->FillRect(10+leftTransition,10,SCREEN_WIDTH/2-10,SCREEN_HEIGHT-20,ARGB(128,0,0,0)); r->FillRect(SCREEN_WIDTH/2+rightTransition,10,SCREEN_WIDTH/2-10,SCREEN_HEIGHT-20,ARGB(128,0,0,0)); font->DrawString(_("menu"), SCREEN_WIDTH-35 +rightTransition, SCREEN_HEIGHT-15); + font->DrawString(_("filter"), SCREEN_WIDTH-95 +rightTransition, SCREEN_HEIGHT-15); int nb_letters = 0; float posX, posY; @@ -1021,7 +1097,7 @@ void GameStateDeckViewer::updateStats() { stw.totalManaCost = 0; stw.totalCreatureCost = 0; stw.totalSpellCost = 0; - MTGCard * current = myDeck->getNext(); + MTGCard * current = myDeck->getCard(); // Clearing arrays for (int i=0; i<=STATS_MAX_MANA_COST; i++) { @@ -1045,10 +1121,11 @@ void GameStateDeckViewer::updateStats() { } } - while (current){ + for(int ic=0;icSize();ic++){ + current = myDeck->getCard(ic); currentCost = current->data->getManaCost(); convertedCost = currentCost->getConvertedCost(); - currentCount = myDeck->cards[current]; + currentCount = myDeck->count(current); // Add to the cards per cost counters stw.totalManaCost += convertedCost * currentCount; @@ -1133,9 +1210,6 @@ void GameStateDeckViewer::updateStats() { stw.totalCostPerColor[hybridCost->color1] += hybridCost->value1*currentCount; stw.totalCostPerColor[hybridCost->color2] += hybridCost->value2*currentCount; } - - - current = myDeck->getNext(current); } stw.totalColoredSymbols = 0; @@ -1172,13 +1246,11 @@ void GameStateDeckViewer::updateStats() { // or at least be calculated for all common types in one go int GameStateDeckViewer::countCardsByType(const char * _type) { int result = 0; - - MTGCard * current = myDeck->getNext(); - while (current){ + for(int i=0;iSize();i++){ + MTGCard * current = myDeck->getCard(i); if(current->data->hasType(_type)){ - result += myDeck->cards[current]; + result += myDeck->count(current); } - current = myDeck->getNext(current); } return result; } @@ -1221,7 +1293,7 @@ void GameStateDeckViewer::renderCard(int id, float rotation){ if (quad){ showName = 0; int quadAlpha = alpha; - if ( !displayed_deck->cards[card]) quadAlpha /=2; + if ( !displayed_deck->count(card)) quadAlpha /=2; quad->SetColor(ARGB(mAlpha,quadAlpha,quadAlpha,quadAlpha)); float _scale = scale *(285 / quad->mHeight); JRenderer::GetInstance()->RenderQuad(quad, x , y , 0.0f,_scale,_scale); @@ -1249,7 +1321,7 @@ void GameStateDeckViewer::renderCard(int id, float rotation){ float qtY = y -135*scale; float qtX = x + 40*scale; char buffer[4096]; - sprintf(buffer, "x%i", displayed_deck->cards[card]); + sprintf(buffer, "x%i", displayed_deck->count(card)); JLBFont * font = mFont; font->SetColor(ARGB(fontAlpha/2,0,0,0)); JRenderer::GetInstance()->FillRect(qtX, qtY,font->GetStringWidth(buffer) + 6,16,ARGB(fontAlpha/2,0,0,0)); @@ -1271,15 +1343,8 @@ void GameStateDeckViewer::Render() { JRenderer * r = JRenderer::GetInstance(); r->ClearScreen(ARGB(0,0,0,0)); - - - if(displayed_deck == myDeck){ + if(displayed_deck == myDeck) renderDeckBackground(); - } - - - - int order[3] = {1,2,3}; if (mRotation < 0.5 && mRotation > -0.5){ order[1]=3; @@ -1298,7 +1363,7 @@ void GameStateDeckViewer::Render() { renderCard(order[i],mRotation); } - if (displayed_deck->getCount(colorFilter)>0){ + if (displayed_deck->Size()>0){ renderSlideBar(); }else{ mFont->DrawString(_("No Card"), SCREEN_WIDTH/2, SCREEN_HEIGHT/2,JGETEXT_CENTER); @@ -1317,19 +1382,30 @@ void GameStateDeckViewer::Render() { } if (sellMenu) sellMenu->Render(); + if(displayed_deck == myDeck){ + if(filterDeck && !filterDeck->isFinished()) + filterDeck->Render(); + }else{ + if(filterCollection && !filterCollection->isFinished()) + filterCollection->Render(); + } + if(options.keypadActive()) options.keypadRender(); + + } int GameStateDeckViewer::loadDeck(int deckid){ - SAFE_DELETE(myCollection); + stw.currentPage = 0; stw.pageCount = 9; stw.needUpdate = true; - string profile = options[Options::ACTIVE_PROFILE].str; - myCollection = NEW DeckDataWrapper(NEW MTGDeck(options.profileFile(PLAYER_COLLECTION).c_str(), mParent->collection)); + //string profile = options[Options::ACTIVE_PROFILE].str; + //SAFE_DELETE(myCollection); + //myCollection = NEW DeckDataWrapper(NEW MTGDeck(options.profileFile(PLAYER_COLLECTION).c_str(), mParent->collection)); displayed_deck = myCollection; char deckname[256]; sprintf(deckname,"deck%i.txt",deckid); @@ -1337,24 +1413,18 @@ int GameStateDeckViewer::loadDeck(int deckid){ myDeck = NEW DeckDataWrapper(NEW MTGDeck(options.profileFile(deckname,"",false,false).c_str(), mParent->collection)); // Check whether the cards in the deck are actually available in the player's collection: - MTGCard * current = myDeck->getNext(); int cheatmode = options[Options::CHEATMODE].number; - while (current){ - int howmanyinDeck = myDeck->cards[current]; - for (int i = 0; i < howmanyinDeck; i++){ - int deleted = myCollection->Remove(current); - if (!deleted){ // Card was not present in the collection - if (cheatmode) { // (PSY) Are we in cheatmode? - playerdata->collection->add(current); // (PSY) Yes - add the card to the collection - } else { - myDeck->Remove(current); // No - remove the card from the deck - } - } + for(int i=0;iSize();i++){ + MTGCard * current = myDeck->getCard(i); + int howmanyinDeck = myDeck->count(current); + for (int i = myCollection->count(current); i < howmanyinDeck; i++){ + if(cheatmode) //Are we cheating? + playerdata->collection->add(current); //Yup, add it to collection. + else + myDeck->Remove(current); //Nope. Remove it from deck. } - current = myDeck->getNext(current); } currentCard = NULL; - loadIndexes(); // Load deck statistics // TODO: Code cleanup (Copypasted with slight changes from GameStateMenu.cpp) char buffer[512]; @@ -1400,6 +1470,9 @@ int GameStateDeckViewer::loadDeck(int deckid){ stw.gamesPlayed = 0; stw.percentVictories = 0; } + + myDeck->Sort(WSrcCards::SORT_ALPHA); + loadIndexes(); return 1; } @@ -1451,6 +1524,18 @@ void GameStateDeckViewer::ButtonPressed(int controllerId, int controlId) case 4: mStage = STAGE_WAITING; break; + case 22: + mStage = STAGE_FILTERS; + if(displayed_deck == myDeck){ + if(!filterDeck) + filterDeck = NEW WGuiFilters("Filter by...",myDeck); + filterDeck->Entering(0); + }else if(displayed_deck == myCollection){ + if(!filterCollection) + filterCollection = NEW WGuiFilters("Filter by...",myCollection); + filterCollection->Entering(0); + } + break; } break; case 2: @@ -1464,7 +1549,8 @@ void GameStateDeckViewer::ButtonPressed(int controllerId, int controlId) price = price - (rnd * price)/100; pricelist->setPrice(card->getMTGId(),price*2); playerdata->collection->remove(card->getMTGId()); - Remove(card); + displayed_deck->Remove(card,1); + loadIndexes(); } } case 21: diff --git a/projects/mtg/src/GameStateMenu.cpp b/projects/mtg/src/GameStateMenu.cpp index c6c20ec24..35d401011 100644 --- a/projects/mtg/src/GameStateMenu.cpp +++ b/projects/mtg/src/GameStateMenu.cpp @@ -377,7 +377,6 @@ void GameStateMenu::Update(float dt) if (!nextDirectory(RESPATH"/sets/","_cards.dat")){ //Remove temporary translations Translator::GetInstance()->tempValues.clear(); - //Debug #ifdef _DEBUG char buf[4096]; diff --git a/projects/mtg/src/GameStateShop.cpp b/projects/mtg/src/GameStateShop.cpp index 248374549..c4f0e52fa 100644 --- a/projects/mtg/src/GameStateShop.cpp +++ b/projects/mtg/src/GameStateShop.cpp @@ -8,35 +8,100 @@ #include "../include/MTGDeck.h" #include "../include/Translate.h" #include "../include/GameOptions.h" +#include +float GameStateShop::_x1[] = { 79, 19, 27,103,154,187,102,144,198,133,183}; +float GameStateShop::_y1[] = {150,194,222,167,164,156,195,190,175,220,220}; + +float GameStateShop::_x2[] = {103, 48, 74,135,183,215,138,181,231,171,225}; +float GameStateShop::_y2[] = {155,179,218,165,166,155,195,186,177,225,216}; + +float GameStateShop::_x3[] = { 48, 61, 9, 96,139,190, 81,146,187, 97,191}; +float GameStateShop::_y3[] = {164,205,257,184,180,170,219,212,195,251,252}; + +float GameStateShop::_x4[] = { 76, 90, 65,131,171,221,123,187,225,141,237}; +float GameStateShop::_y4[] = {169,188,250,182,182,168,220,208,198,259,245}; + +int GameStateShop::randomKey = 0; GameStateShop::GameStateShop(GameApp* parent): GameState(parent) { - shop = NULL; + menu = NULL; for(int i=0;i<8;i++) altThumb[i] = NULL; mBack = NULL; + boosterDisplay = NULL; mBg = NULL; mBgTex = NULL; taskList = NULL; - menu = NULL; + srcCards = NULL; + shopMenu = NULL; + bigDisplay = NULL; + myCollection = NULL; + pricelist = NULL; + playerdata = NULL; + booster = NULL; + lightAlpha = 0; + filterMenu = NULL; + alphaChange = 0; + for(int i=0;isetElapsed(15); + WCFilterFactory * wff = WCFilterFactory::GetInstance(); + //srcCards->addFilter(wff->Construct("c:red;&t:instant;")); + //srcCards->bakeFilters(); + + bigSync = 0; + shopMenu = NEW WGuiMenu(PSP_CTRL_DOWN,PSP_CTRL_UP,true,&bigSync); + MTGAllCards * ac = GameApp::collection; + playerdata = NEW PlayerData(ac);; + myCollection = NEW DeckDataWrapper(NEW MTGDeck(options.profileFile(PLAYER_COLLECTION).c_str(), ac)); + pricelist = NEW PriceList(RESPATH"/settings/prices.dat",ac); + for(int i=0;imOffset.setOffset(i-BOOSTER_SLOTS); + } + dist->xy = WDistort(_x1[i],_y1[i],_x2[i],_y2[i],_x3[i],_y3[i],_x4[i],_y4[i]); + shopMenu->Add(NEW WGuiButton(dist,-102,i,this)); + } + shopMenu->Entering(0); + + if(!bigDisplay){ + bigDisplay = NEW WGuiCardImage(srcCards); + bigDisplay->mOffset.Hook(&bigSync); + bigDisplay->mOffset.setOffset(-BOOSTER_SLOTS); + bigDisplay->setX(385); + bigDisplay->setY(135); + } + //alternateRender doesn't lock, so lock our thumbnails for hgeDistort. altThumb[0] = resources.RetrieveTexture("artifact_thumb.jpg", RETRIEVE_LOCK); altThumb[1] = resources.RetrieveTexture("green_thumb.jpg", RETRIEVE_LOCK); @@ -47,6 +112,7 @@ void GameStateShop::Start() altThumb[6] = resources.RetrieveTexture("land_thumb.jpg", RETRIEVE_LOCK); altThumb[7] = resources.RetrieveTexture("gold_thumb.jpg", RETRIEVE_LOCK); + mBack = resources.GetQuad("back"); resources.Unmiss("shop.jpg"); //Last resort. mBgTex = resources.RetrieveTexture("shop.jpg", RETRIEVE_LOCK, TEXTURE_SUB_5551); @@ -57,109 +123,294 @@ void GameStateShop::Start() JRenderer::GetInstance()->EnableVSync(true); - shop = NULL; taskList = NULL; load(); } +string GameStateShop::descPurchase(int controlId, bool tiny){ + char buffer[4096]; + string name; + if(controlId < BOOSTER_SLOTS){ + if(mBooster[controlId].altSet == mBooster[controlId].mainSet) + mBooster[controlId].altSet = 0; + if(mBooster[controlId].altSet) + sprintf(buffer,_("%s & %s Booster (15 Cards)").c_str(),mBooster[controlId].mainSet->id.c_str(),mBooster[controlId].altSet->id.c_str()); + else + sprintf(buffer,_("%s Booster (15 Cards)").c_str(),mBooster[controlId].mainSet->id.c_str()); + name = buffer; + } + else{ + MTGCard * c = srcCards->getCard(controlId-BOOSTER_SLOTS); + if(!c) + return ""; + name = c->data->getName(); + } + if(mInventory[controlId] <= 0){ + if(tiny) + sprintf(buffer,_("SOLD OUT").c_str(),name.c_str()); + else + sprintf(buffer,_("%s : SOLD OUT").c_str(),name.c_str()); + return buffer; + } + + if(tiny) + return name; + + if(mCounts[controlId] < 1) + sprintf(buffer,_("%s : %i credits").c_str(),name.c_str(),mPrices[controlId]); + else + sprintf(buffer,_("%s (%i) : %i credits").c_str(),name.c_str(),mCounts[controlId],mPrices[controlId]); + return buffer; +} +void GameStateShop::assembleBooster(int controlId){ + int mSet = -1; + MTGSetInfo * si = setlist.randomSet(-1); + mBooster[controlId].mainSet = si; + mBooster[controlId].altSet = NULL; + + int mSetCount = si->counts[MTGSetInfo::TOTAL_CARDS]; + if(mSetCount < 80){ + if(rand() % 100 < 50){ //50% Chance of picking a pure pack instead. Combo packs are more rare :) + si = setlist.randomSet(-1,80); + mSetCount = si->counts[MTGSetInfo::TOTAL_CARDS]; + mBooster[controlId].mainSet = si; + }else + mBooster[controlId].altSet = setlist.randomSet(si->block,mSetCount); + } + else { + mBooster[controlId].altSet = NULL; + if(rand() % 100 < 10) //10% chance of having a mixed booster anyways. + mBooster[controlId].altSet = setlist.randomSet(si->block); + } + + for(int attempts=0;attempts<10;attempts++){ + if(mBooster[controlId].altSet != mBooster[controlId].mainSet) + break; + mBooster[controlId].altSet = setlist.randomSet(-1,mSetCount); + } + + int price = mBooster[controlId].mainSet->boosterCost(); + mInventory[controlId] = 2+rand()%4; + if(mBooster[controlId].altSet != NULL){ + price += mBooster[controlId].altSet->boosterCost(); + price /= 2; + price = price + .05 * price; //Mixed sets add a 5% premium. + mInventory[controlId] = 1+rand()%2; + } + mPrices[controlId] = price; +} +void GameStateShop::beginPurchase(int controlId){ + JLBFont * mFont = resources.GetJLBFont(Constants::MENU_FONT); + mFont->SetScale(DEFAULT_MENU_FONT_SCALE); + SAFE_DELETE(menu); + if(mInventory[controlId] <= 0){ + menu = NEW SimpleMenu(-145,this,Constants::MENU_FONT,SCREEN_WIDTH-300,SCREEN_HEIGHT/2,_("Sold Out").c_str()); + menu->Add(-1,"Ok"); + } + else if(playerdata->credits - mPrices[controlId] < 0){ + menu = NEW SimpleMenu(-145,this,Constants::MENU_FONT,SCREEN_WIDTH-300,SCREEN_HEIGHT/2,_("Not enough credits").c_str()); + menu->Add(-1,"Ok"); + if(options[Options::CHEATMODE].number) { + menu->Add(-2,"Steal it"); + } + } + else{ + string s; + if(controlId < BOOSTER_SLOTS) + s = _("Purchase Booster"); + else + s = _("Purchase Card"); + menu = NEW SimpleMenu(-145,this,Constants::MENU_FONT,SCREEN_WIDTH-300,SCREEN_HEIGHT/2,s.c_str()); + menu->Add(controlId,"Yes"); + menu->Add(-1,"No"); + } +} + +void GameStateShop::purchaseCard(int controlId){ + MTGCard * c = srcCards->getCard(controlId-BOOSTER_SLOTS); + if(!c || !c->data || playerdata->credits - mPrices[controlId] < 0) + return; + playerdata->collection->add(c); + myCollection->Add(c); + playerdata->credits -= mPrices[controlId]; + mCounts[controlId]++; + mInventory[controlId]--; + mTouched = true; + menu->Close(); +} +void GameStateShop::purchaseBooster(int controlId){ + if(playerdata->credits - mPrices[controlId] < 0) + return; + playerdata->credits -= mPrices[controlId]; + mInventory[controlId]--; + WSrcCards * pool = NEW WSrcCards(); + WCFilterSet *main, *alt; + + int num = setlist.getSetNum(mBooster[controlId].mainSet); + main = NEW WCFilterSet(num); + if(mBooster[controlId].altSet){ + num = setlist.getSetNum(mBooster[controlId].altSet); + alt = NEW WCFilterSet(num); + pool->addFilter(NEW WCFilterOR(main,alt)); + }else + pool->addFilter(main); + pool->loadMatches(srcCards,true); + pool->Shuffle(); + + SAFE_DELETE(booster); + booster = NEW MTGDeck(mParent->collection); + + //Add cards to booster. Pool is shuffled, so just step through. + int carryover = 1; + if(!(rand() % 8)){ + pool->addFilter(NEW WCFilterRarity(Constants::RARITY_M)); + carryover = pool->addToDeck(booster,carryover); + } + pool->clearFilters(); + pool->addFilter(NEW WCFilterRarity(Constants::RARITY_R)); + carryover = pool->addToDeck(booster,carryover); + pool->clearFilters(); + pool->addFilter(NEW WCFilterRarity(Constants::RARITY_U)); + carryover = pool->addToDeck(booster,carryover+3); + pool->clearFilters(); + pool->addFilter(NEW WCFilterRarity(Constants::RARITY_C)); + carryover = pool->addToDeck(booster,carryover+11); + + myCollection->Add(booster); + makeDisplay(booster); + + save(true); + SAFE_DELETE(pool); + menu->Close(); +} + +int GameStateShop::purchasePrice(int offset){ + MTGCard * c = NULL; + if(!pricelist || !srcCards || (c = srcCards->getCard(offset)) == NULL) + return 0; + int rnd = abs(c->getMTGId() + randomKey) % 20; + float price = (float) pricelist->getPrice(c->getMTGId()); + price = price + price * (rnd -10)/100; + return (int) (price + price * srcCards->filterFee()); +} void GameStateShop::load(){ - if (shop) shop->saveAll(); - SAFE_DELETE(shop); - int sets[500]; - int boosterSets[500]; - int unlocked[500]; int nbsets = 0; int nbboostersets = 0; - //Figure out which sets are available. - for (int i = 0; i < setlist.size(); i++){ - unlocked[i] = options[Options::optionSet(i)].number; - } - for (int i = 0; i < setlist.size(); i++){ - if (unlocked[i]){ - sets[nbsets] = i; - nbsets++; - if (mParent->collection->countBySet(i) > 80){ //Only sets with more than 80 cards can get boosters and starters - boosterSets[nbboostersets] = i; - nbboostersets++; - } - } - } - if (nbboostersets){ - for (int i = 0; i < SHOP_BOOSTERS; i++){ - setIds[i] = boosterSets[(rand() % nbboostersets)]; - } - }else{ - for (int i = 0; i < SHOP_BOOSTERS; i++){ - setIds[i] = (rand() % setlist.size()); - } - } JQuad * mBackThumb = resources.GetQuad("back_thumb"); - - - - shop = NEW ShopItems(10, this, resources.GetJLBFont(Constants::MAIN_FONT), 10, 0, mParent->collection, setIds); - MTGSetInfo * si = NULL; - for (int i = 0; i < SHOP_BOOSTERS; i++){ - si = setlist.getInfo(setIds[i]); - if(!si) + for(int i=0;igetCard(i-BOOSTER_SLOTS)) == NULL){ + mPrices[i] = 0; + mCounts[i] = 0; + mInventory[i] = 0; continue; - - sprintf(setNames[i], "%s %s (%i %s)", si->id.c_str(), _("Booster").c_str(), si->boosterSize(), _("Cards").c_str()); - shop->Add(setNames[i],mBack,mBackThumb, si->boosterCost()); - } - - MTGDeck * tempDeck = NEW MTGDeck(mParent->collection); - tempDeck->addRandomCards(8,sets,nbsets); - for (map::iterator it = tempDeck->cards.begin(); it!=tempDeck->cards.end(); it++){ - for (int j = 0; j < it->second; j++){ - shop->Add(it->first); } - } - delete tempDeck; -} + mPrices[i] = purchasePrice(i); + mCounts[i] = myCollection->countByName(c); + switch(c->getRarity()){ + case Constants::RARITY_C: + mInventory[i] = 2 + rand() % 8; + break; + case Constants::RARITY_L: + mInventory[i] = 100; + break; + case Constants::RARITY_U: + mInventory[i] = 1 + rand() % 5; + break; + case Constants::RARITY_R: + mInventory[i] = 1 + rand() % 2; + break; + } + } + + +} +void GameStateShop::save(bool force) +{ + if(mTouched || force){ + if(pricelist) + pricelist->save(); + if(playerdata) + playerdata->save(); + } + mTouched = false; +} void GameStateShop::End() { + save(); JRenderer::GetInstance()->EnableVSync(false); resources.Release(mBgTex); mBgTex = NULL; mBg = NULL; + mElapsed = 0; + SAFE_DELETE(shopMenu); + SAFE_DELETE(bigDisplay); + SAFE_DELETE(srcCards); + SAFE_DELETE(playerdata); + SAFE_DELETE(pricelist); + SAFE_DELETE(myCollection); + SAFE_DELETE(booster); + SAFE_DELETE(filterMenu); + deleteDisplay(); //Release alternate thumbnails. for(int i=0;i<8;i++){ resources.Release(altThumb[i]); + altThumb[i] = NULL; } - SAFE_DELETE(shop); SAFE_DELETE(menu); SAFE_DELETE(taskList); } void GameStateShop::Destroy(){ } - +void GameStateShop::beginFilters(){ + if(!filterMenu){ + filterMenu = NEW WGuiFilters("Ask about...",srcCards); + filterMenu->setY(2); + filterMenu->setHeight(SCREEN_HEIGHT-2); + } + mStage = STAGE_ASK_ABOUT; + filterMenu->Entering(0); +} void GameStateShop::Update(float dt) -{ +{ + if(menu && menu->closed) + SAFE_DELETE(menu); + srcCards->Update(dt); + alphaChange = (500 - (rand() % 1000)) * dt; + lightAlpha+= alphaChange; + if (lightAlpha < 0) lightAlpha = 0; + if (lightAlpha > 50) lightAlpha = 50; // mParent->effect->UpdateSmall(dt); // mParent->effect->UpdateBig(dt); + if(mStage != STAGE_FADE_IN) + mElapsed += dt; + u32 btn; - if (menu){ - menu->Update(dt); - if (menu->closed) SAFE_DELETE(menu); - } switch(mStage){ case STAGE_SHOP_MENU: - if (!menu){ + if (menu){ + menu->Update(dt); + }else{ menu = NEW SimpleMenu(11,this,Constants::MENU_FONT,SCREEN_WIDTH/2-100,20); + menu->Add(22,"Ask about..."); + menu->Add(14,"Check task board"); + if(options[Options::CHEATMODE].number) + menu->Add(-2,"Steal 1,000 credits"); menu->Add(12,"Save & Back to Main Menu"); - menu->Add(14,"See available tasks"); menu->Add(13, "Cancel"); } break; case STAGE_SHOP_TASKS: if(menu){ + menu->Update(dt); return; } if(taskList){ @@ -173,7 +424,7 @@ void GameStateShop::Update(float dt) if(!menu){ menu = NEW SimpleMenu(11,this,Constants::MENU_FONT,SCREEN_WIDTH/2-100,20); menu->Add(12,"Save & Back to Main Menu"); - menu->Add(15,"Close tasks"); + menu->Add(15,"Return to shop"); menu->Add(13, "Cancel"); } } @@ -193,18 +444,67 @@ void GameStateShop::Update(float dt) } #endif break; + case STAGE_ASK_ABOUT: + btn = mEngine->ReadButton(); + if(menu && !menu->closed){ + menu->CheckUserInput(btn); + menu->Update(dt); + return; + } + if(filterMenu){ + if(btn == PSP_CTRL_SELECT){ + filterMenu->Finish(); + filterMenu->Update(dt); + return; + } + if(filterMenu->isFinished()){ + load(); + mStage = STAGE_SHOP_SHOP; + }else{ + filterMenu->CheckUserInput(btn); + filterMenu->Update(dt); + } + return; + } + break; case STAGE_SHOP_SHOP: btn = mEngine->ReadButton(); + if(menu && !menu->closed){ + menu->CheckUserInput(btn); + menu->Update(dt); + return; + } if (btn == PSP_CTRL_START){ + if(boosterDisplay){ + deleteDisplay(); + return; + } mStage = STAGE_SHOP_MENU; return; + }else if(btn == PSP_CTRL_SELECT){ + beginFilters(); }else if(btn == PSP_CTRL_SQUARE){ - load(); - } - if (shop){ - shop->CheckUserInput(btn); - shop->Update(dt); + srcCards->Shuffle(); + load(); + }else if(btn == PSP_CTRL_TRIANGLE){ + options[Options::DISABLECARDS].number = !options[Options::DISABLECARDS].number; + }else if (boosterDisplay){ + if(btn == PSP_CTRL_CROSS) + deleteDisplay(); + else { + boosterDisplay->CheckUserInput(btn); + boosterDisplay->Update(dt);} + return; + }else if(btn == PSP_CTRL_CROSS){ + bListCards = !bListCards; + }else if(shopMenu){ + if(shopMenu->CheckUserInput(btn)) + srcCards->Touch(); } + + if(shopMenu) + shopMenu->Update(dt); + break; case STAGE_FADE_IN: mParent->DoAnimation(TRANSITION_FADE_IN); @@ -213,57 +513,156 @@ void GameStateShop::Update(float dt) } } +void GameStateShop::makeDisplay(MTGDeck * d){ + deleteDisplay(); + boosterDisplay = NEW CardDisplay(12,NULL, SCREEN_WIDTH - 200, SCREEN_HEIGHT/2,this,NULL,5); + map::iterator it; + + for (it = d->cards.begin(); it!=d->cards.end(); it++){ + MTGCard * c = d->getCardById(it->first); + MTGCardInstance * ci = NEW MTGCardInstance(c, NULL); + boosterDisplay->AddCard(ci); + subBooster.push_back(ci); + } +} +void GameStateShop::deleteDisplay(){ + vector::iterator i; + for(i=subBooster.begin();i!=subBooster.end();i++){ + if(!*i) continue; + delete *i; + } + subBooster.clear(); + SAFE_DELETE(boosterDisplay); +} void GameStateShop::Render() { //Erase + JLBFont * mFont = resources.GetJLBFont(Constants::MAIN_FONT); JRenderer * r = JRenderer::GetInstance(); r->ClearScreen(ARGB(0,0,0,0)); if(mStage == STAGE_FADE_IN) return; - if (mBg) r->RenderQuad(mBg,0,0); + if (mBg) + r->RenderQuad(mBg,0,0); + JQuad * quad = resources.RetrieveTempQuad("shop_light.jpg",TEXTURE_SUB_5551); + if (quad){ + r->EnableTextureFilter(false); + r->SetTexBlend(BLEND_SRC_ALPHA, BLEND_ONE); + quad->SetColor(ARGB(lightAlpha,255,255,255)); + r->RenderQuad(quad,0,0); + r->SetTexBlend(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA); + r->EnableTextureFilter(true); + } + + if(shopMenu) + shopMenu->Render(); + if(filterMenu && !filterMenu->isFinished()) + filterMenu->Render(); + else{ + if(boosterDisplay) + boosterDisplay->Render(); + else if(bigDisplay){ + if(bigDisplay->mOffset.getPos() >= 0) + bigDisplay->setSource(srcCards); + else + bigDisplay->setSource(NULL); + bigDisplay->Render(); + float elp = srcCards->getElapsed(); + //Render the card list overlay. + if( bListCards || elp > LIST_FADEIN){ + char alpha = 200; + if(!bListCards && elp < LIST_FADEIN+.25){ + alpha = 800 *(elp-LIST_FADEIN); + } + r->FillRoundRect(300,10, 160, SHOP_SLOTS * 20 + 15,5,ARGB(alpha,0,0,0)); + alpha+=55; + for(int i=0;igetSelected()) + mFont->SetColor(ARGB(alpha,255,255,0)); + else + mFont->SetColor(ARGB(alpha,255,255,255)); + char buffer[512]; + string s = descPurchase(i,true); + sprintf(buffer, "%s", s.c_str()); + float x = 310; + float y = 25 + 20*i; + mFont->DrawString(buffer,x,y); + } + } + } + } + + //Render the info bar + r->FillRect(0,SCREEN_HEIGHT-17,SCREEN_WIDTH,17,ARGB(128,0,0,0)); + char c[512]; + sprintf(c,_("credits: %i").c_str(), playerdata->credits); + mFont->SetColor(ARGB(255,255,255,255)); + mFont->DrawString(c, 5, SCREEN_HEIGHT - 12); + sprintf(c, "%s", _("[]:other cards").c_str()); + unsigned int len = 4 + mFont->GetStringWidth(c); + mFont->DrawString(c,SCREEN_WIDTH-len,SCREEN_HEIGHT-14); + + mFont->SetColor(ARGB(255,255,255,0)); + mFont->DrawString(descPurchase(bigSync.getPos()).c_str(), SCREEN_WIDTH/2, SCREEN_HEIGHT - 14,JGETEXT_CENTER); + mFont->SetColor(ARGB(255,255,255,255)); - if (shop) - shop->Render(); - if (mStage == STAGE_SHOP_TASKS && taskList) { taskList->Render(); } - - if (menu){ + if (menu) menu->Render(); - } } void GameStateShop::ButtonPressed(int controllerId, int controlId) { - if (controllerId == 10){ - if (shop) - shop->pricedialog(controlId); + int sel = bigSync.getOffset(); + + switch(controllerId){ + case -102: //Buying something... + beginPurchase(controlId); + return; + case -145: + if(controlId == -1){ //Nope, don't buy. + menu->Close(); + return; + } + if(sel > -1 && sel < SHOP_ITEMS){ + if(controlId == -2) + playerdata->credits += mPrices[sel]; + if(sel < BOOSTER_SLOTS) //Clicked a booster. + purchaseBooster(sel); + else + purchaseCard(sel); + } + return; } - else{ - switch(controlId){ - case 12: - if (shop) shop->saveAll(); - if (taskList) taskList->save(); - mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); - mStage = STAGE_SHOP_SHOP; - break; - case 14: - mStage = STAGE_SHOP_TASKS; - if (!taskList) - taskList = NEW TaskList(); - taskList->Start(); - break; - case 15: - if(taskList) - taskList->End(); - break; - default: - mStage = STAGE_SHOP_SHOP; - } - if (menu) menu->Close(); + //Basic Menu. + switch(controlId){ + case 12: + if (taskList) taskList->save(); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); + mStage = STAGE_SHOP_SHOP; + break; + case 14: + mStage = STAGE_SHOP_TASKS; + if (!taskList) + taskList = NEW TaskList(); + taskList->Start(); + break; + case 15: + if(taskList) + taskList->End(); + break; + case 22: + beginFilters(); + break; + case -2: + playerdata->credits += 1000; + default: + mStage = STAGE_SHOP_SHOP; } + SAFE_DELETE(menu); } diff --git a/projects/mtg/src/GameStateTransitions.cpp b/projects/mtg/src/GameStateTransitions.cpp index f6826f51d..670df65e4 100644 --- a/projects/mtg/src/GameStateTransitions.cpp +++ b/projects/mtg/src/GameStateTransitions.cpp @@ -7,7 +7,6 @@ #include "../include/Translate.h" #include "../include/OptionItem.h" #include "../include/GameOptions.h" -#include "../include/DeckDataWrapper.h" TransitionBase::TransitionBase(GameApp* parent, GameState* _from, GameState* _to, float duration): GameState(parent){ from = _from; @@ -15,7 +14,12 @@ TransitionBase::TransitionBase(GameApp* parent, GameState* _from, GameState* _to mDuration = duration; bAnimationOnly = false; } - +TransitionBase::~TransitionBase(){ + if(!bAnimationOnly){ + if(from) + from->End(); + } +} void TransitionBase::Update(float dt){ if(from && !Finished()) from->Update(dt); @@ -36,12 +40,7 @@ void TransitionBase::Start() { void TransitionBase::End() { mElapsed = 0; }; -TransitionBase::~TransitionBase(){ - if(!bAnimationOnly){ - if(from) - from->End(); - } -} + void TransitionFade::Render(){ if(from) from->Render(); diff --git a/projects/mtg/src/MTGCard.cpp b/projects/mtg/src/MTGCard.cpp index 1db00b1f4..15b098a4a 100644 --- a/projects/mtg/src/MTGCard.cpp +++ b/projects/mtg/src/MTGCard.cpp @@ -30,7 +30,6 @@ MTGCard::MTGCard(MTGCard * source){ mtgid = source->mtgid; setId = source->setId; data = source->data; - } int MTGCard::init(){ @@ -57,7 +56,6 @@ int MTGCard::getMTGId(){ int MTGCard::getId(){ return mtgid; } - char MTGCard::getRarity(){ return rarity; } diff --git a/projects/mtg/src/MTGDeck.cpp b/projects/mtg/src/MTGDeck.cpp index 7d885fc17..251dc03a9 100644 --- a/projects/mtg/src/MTGDeck.cpp +++ b/projects/mtg/src/MTGDeck.cpp @@ -727,6 +727,43 @@ MTGSetInfo* MTGSets::getInfo(int setID){ return setinfo[setID]; } +MTGSetInfo* MTGSets::randomSet(int blockId, int atleast){ + char * unlocked = (char *)calloc(size(),sizeof(char)); + int attempts = 50; + //Figure out which sets are available. + for (int i = 0; i < size(); i++){ + unlocked[i] = options[Options::optionSet(i)].number; + } + //No luck randomly. Now iterate from a random location. + int a = 0, iter = 0; + while(iter < 3){ + a = rand()%size(); + for(int i=a;iblock == blockId) + && (atleast == -1 || setinfo[i]->totalCards() >= atleast)){ + free(unlocked); + return setinfo[i]; + } + } + for(int i=0;iblock == blockId) + && (atleast == -1 || setinfo[i]->totalCards() >= atleast)){ + free(unlocked); + return setinfo[i]; + } + } + blockId = -1; + iter++; + if(iter == 2) + atleast = -1; + } + free(unlocked); + return NULL; +} + int blockSize(int blockId); + int MTGSets::Add(const char * name){ int setid = findSet(name); if(setid != -1) @@ -781,12 +818,18 @@ string MTGSets::operator[](int id){ return si->id; } - +int MTGSets::getSetNum(MTGSetInfo*i){ + int it; + for(it=0;it #include -//WGuiBase - -PIXEL_TYPE WGuiBase::getColor(int type){ - switch(type){ - case WGuiColor::TEXT_BODY: - case WGuiColor::SCROLLBUTTON: - return ARGB(255,255,255,255); - case WGuiColor::SCROLLBAR: - return ARGB(150,50,50,50); - case WGuiColor::BACK_HEADER: - return ARGB(150,80,80,80); - default: - if(type < WGuiColor::BACK){ - if(hasFocus()) - return ARGB(255,255,255,0); - else - return ARGB(255,255,255,255); - } - else - return ARGB(150,50,50,50); - } - return ARGB(150,50,50,50); -} - - -void WGuiBase::renderBack(WGuiBase * it){ - if(!it) return; - WDecoStyled * styled = dynamic_cast(it); - if(styled) - styled->renderBack(styled->getDecorated()); - else - subBack(it); -} - - -//WGuiItem -void WGuiItem::Entering(u32 key){ - mFocus = true; -} - -bool WGuiItem::Leaving(u32 key){ - mFocus = false; - return true; -} - -void WGuiItem::Render(){ - JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); - mFont->SetColor(getColor(WGuiColor::TEXT)); - JRenderer * renderer = JRenderer::GetInstance(); - float fH = (height-mFont->GetHeight())/2; - string trans = _(displayValue); - float fW = mFont->GetStringWidth(trans.c_str()); - float boxW = getWidth(); - float oldS = mFont->GetScale(); - if(fW > boxW){ - mFont->SetScale(boxW/fW); - } - mFont->DrawString(trans,x+(width/2),y+fH,JGETEXT_CENTER); - mFont->SetScale(oldS); -} - -WGuiItem::WGuiItem(string _display, u8 _mF){ - mFlags=_mF; - displayValue = _display; - mFocus = false; - width=SCREEN_WIDTH; - height=20; - x=0; - y=0; -} - -string WGuiItem::_(string input){ - if(mFlags & WGuiItem::NO_TRANSLATE) - return input; - return ::_(input); -} -bool WGuiItem::CheckUserInput(u32 key){ - if(mFocus && key == PSP_CTRL_CIRCLE){ - updateValue(); - return true; - } - return false; -} - -//WDecoStyled -void WDecoStyled::subBack(WGuiBase * item){ - if(!item) - return; - JRenderer * renderer = JRenderer::GetInstance(); - if(mStyle & DS_STYLE_BACKLESS) - return; - //TODO: if(mStyle & DS_STYLE_EDGED) Draw the edged box ala SimpleMenu - else{ //Draw standard style - WGuiSplit * split = dynamic_cast(item); - if(split && split->left->Visible() && split->right->Visible()){ - if(split->left) - renderer->FillRoundRect(split->left->getX()-2,split->getY()-2,split->left->getWidth()-6,split->getHeight(),2,split->left->getColor(WGuiColor::BACK)); - if(split->right) - renderer->FillRoundRect(split->right->getX()-2,split->getY()-2,split->right->getWidth(),split->getHeight(),2,split->right->getColor(WGuiColor::BACK)); - } - else{ - renderer->FillRoundRect(item->getX()-2,item->getY()-2,item->getWidth(),item->getHeight(),2,getColor(WGuiColor::BACK)); - } - } - -} - -PIXEL_TYPE WDecoStyled::getColor(int type){ - switch(type){ - case WGuiColor::BACK: - case WGuiColor::BACK_HEADER: - if(mStyle & DS_COLOR_DARK) - return ARGB(150,35,35,35); - else if(mStyle & DS_COLOR_BRIGHT) - return ARGB(150,80,80,80); - else if(mStyle & DS_STYLE_ALERT) - return ARGB(150,120,80,80); - else - return ARGB(150,50,50,50); - default: - return WGuiBase::getColor(type); - } - return ARGB(150,50,50,50); -} - -//WGuiHeader -void WGuiHeader::Render(){ - JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); - mFont->SetColor(getColor(WGuiColor::TEXT)); - - JRenderer * renderer = JRenderer::GetInstance(); - mFont->DrawString(_(displayValue).c_str(),x+width/2,y,JGETEXT_CENTER); -} - //OptionItem OptionItem::OptionItem( int _id, string _displayValue): WGuiItem(_displayValue) { id = _id; @@ -476,208 +342,6 @@ OptionDirectory::OptionDirectory(string root, int id, string displayValue, strin initSelections(); } -bool WGuiMenu::Leaving(u32 key){ - int nbitems = (int) items.size(); - if(key == buttonNext && currentItem < nbitems-1) - return false; - else if(key == buttonPrev && currentItem > 0) - return false; - - if(currentItem >= 0 && currentItem < nbitems) - if(!items[currentItem]->Leaving(key)) - return false; - - mFocus = false; - return true; -} -void WGuiMenu::Entering(u32 key){ - mFocus = true; - - //Try to force a selectable option. - if(currentItem == -1){ - for (size_t i = 0 ; i < items.size(); i++){ - if(items[i]->Selectable()) { - currentItem = i; - break; - } - } - } - - if(currentItem >= 0 && currentItem < (int) items.size()) - items[currentItem]->Entering(key); - return; -} - -void WGuiMenu::subBack(WGuiBase * item){ - if(!item) - return; - JRenderer * renderer = JRenderer::GetInstance(); - - WGuiSplit * split = dynamic_cast(item); - if(split && split->left->Visible() && split->right->Visible()){ - if(split->left) - renderer->FillRoundRect(split->left->getX()-2,split->getY()-2,split->left->getWidth()-6,split->getHeight(),2,split->left->getColor(WGuiColor::BACK)); - if(split->right) - renderer->FillRoundRect(split->right->getX()-2,split->getY()-2,split->right->getWidth(),split->getHeight(),2,split->right->getColor(WGuiColor::BACK)); - } - else - renderer->FillRoundRect(item->getX()-2,item->getY()-2,item->getWidth(),item->getHeight(),2,item->getColor(WGuiColor::BACK)); - -} -//WGuiList -WGuiList::WGuiList(string name, WDataSource * syncme): WGuiMenu(PSP_CTRL_DOWN,PSP_CTRL_UP){ - failMsg = "NO OPTIONS AVAILABLE"; - width = SCREEN_WIDTH-10; - height = SCREEN_HEIGHT-10; - y = 5; - x = 5; - mFocus = false; - sync = syncme; - displayValue = name; -} -void WGuiList::confirmChange(bool confirmed){ - for(size_t x=0;xconfirmChange(confirmed); - } -} -void WGuiList::Render(){ - JRenderer * renderer = JRenderer::GetInstance(); - int listHeight=40; - int listSelectable=0; - int adjustedCurrent=0; - int start = 0, nowPos = 0, vHeight=0; - int nbitems = (int) items.size(); - - //List is empty. - if (!items.size() && failMsg != ""){ - JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); - mFont->SetColor(getColor(WGuiColor::TEXT_FAIL)); - mFont->DrawString(_(failMsg).c_str(),x+width/2, y, JGETEXT_RIGHT); - return; - } - - //Force a selectable option. - if(currentItem == -1){ - for (int i = 0 ; i < nbitems; i++){ - if(items[i]->Selectable()) { - currentItem = i; - if(hasFocus()) - items[currentItem]->Entering(0); - break; - } - } - } - //Find out how large our list is, with all items and margin. - for (int pos=0;pos < nbitems; pos++){ - listHeight+=items[pos]->getHeight()+1; //What does the +1 do exactly ? - if(items[pos]->Selectable()){ - listSelectable++; - if(pos < currentItem) adjustedCurrent++; - } - } - - //Always fill screen - if(listHeight > SCREEN_HEIGHT) - { - for (start=currentItem;start > 0; start--){ - if(!items[start]->Visible()) - continue; - - vHeight += items[start]->getHeight()+5; - if(vHeight >= (SCREEN_HEIGHT-60)/2) - break; - } - vHeight = 0; - if(start >= 0) - for (nowPos=nbitems;nowPos > 1; nowPos--){ - if(!items[start]->Visible()) - continue; - vHeight += items[nowPos-1]->getHeight()+5; - } - - if(vHeight <= SCREEN_HEIGHT-40 && nowPos < start) - start = nowPos; - } - - vHeight = 0; - nowPos = 0; - - //Render items. - if(start >= 0) - { - //Render current underlay. - if(currentItem >= 0 && currentItem < nbitems && items[currentItem]->Visible()) - items[currentItem]->Underlay(); - - for (int pos=0;pos < nbitems; pos++){ - if(!items[pos]->Visible()) - continue; - - if(pos < start){ - vHeight += items[pos]->getHeight() + 5; - continue; - } - - items[pos]->setY(y+nowPos); - items[pos]->setX(x); - if(listHeight > SCREEN_HEIGHT && listSelectable > 1) - items[pos]->setWidth(width-10); - else - items[pos]->setWidth(width); - nowPos += items[pos]->getHeight() + 5; - renderBack(items[pos]); - items[pos]->Render(); - if(nowPos > SCREEN_HEIGHT) //Stop displaying things once we reach the bottom of the screen. - break; - } - - //Draw scrollbar - if(listHeight > SCREEN_HEIGHT && listSelectable > 1){ - int barPosition = y-5+((float)adjustedCurrent/listSelectable)*(SCREEN_HEIGHT-y); - int barLength = (SCREEN_HEIGHT-y) / listSelectable; - if(barLength < 4) barLength = 4; - renderer->FillRect(x+width-2,y-1,2,SCREEN_HEIGHT-y, - getColor(WGuiColor::SCROLLBAR)); - renderer->FillRoundRect(x+width-5,barPosition,5,barLength,2, - getColor(WGuiColor::SCROLLBUTTON)); - } - - //Render current overlay. - if(currentItem >= 0 && currentItem < nbitems && items[currentItem]->Visible()) - items[currentItem]->Overlay(); - } -} - -void WGuiList::setData(){ - for (size_t i = 0; i < items.size(); i++){ - items[i]->setData(); - } -} - -bool WGuiList::nextItem(){ - bool rez = WGuiMenu::nextItem(); - if(sync) - sync->setPos(currentItem); - return rez; -} -bool WGuiList::prevItem(){ - bool rez = WGuiMenu::prevItem(); - if(sync) - sync->setPos(currentItem); - return rez; -} - -void WGuiList::ButtonPressed(int controllerId, int controlId){ - WGuiBase * it; - - if(!(it = Current())) - return; - - it->ButtonPressed(controllerId,controlId); -} - const string OptionTheme::DIRTESTER = "preview.png"; OptionTheme::OptionTheme() : OptionDirectory(RESPATH"/themes", Options::ACTIVE_THEME, "Current Theme", DIRTESTER){ addSelection("Default"); @@ -763,873 +427,4 @@ void OptionTheme::confirmChange(bool confirmed){ resources.Refresh(); //Update images prior_value = value; } -} -string WDecoEnum::lookupVal(int value){ - - if(edef == NULL){ - int id = getId(); - if(id != INVALID_ID){ - GameOptionEnum * goEnum = dynamic_cast(options.get(getId())); - if(goEnum) - edef = goEnum->def; - } - } - - if(edef){ - int idx = edef->findIndex(value); - if(idx != INVALID_ID) - return edef->values[idx].second; - } - - char buf[32]; - sprintf(buf,"%d",value); - return buf; -} - -void WDecoEnum::Render() -{ - JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); - mFont->SetColor(getColor(WGuiColor::TEXT)); - JRenderer * renderer = JRenderer::GetInstance(); - mFont->DrawString(_(getDisplay()).c_str(),getX(),getY()); - OptionInteger* opt = dynamic_cast(it); - if(opt) - mFont->DrawString(_(lookupVal(opt->value)).c_str(), getWidth() -10, getY(), JGETEXT_RIGHT); -} - -WDecoEnum::WDecoEnum(WGuiBase * _it, EnumDefinition *_edef) : WGuiDeco(_it) {edef = _edef;} -//WDecoCheat -WDecoCheat::WDecoCheat(WGuiBase * _it): WGuiDeco(_it){ - bVisible = (options[Options::ACTIVE_PROFILE].str == SECRET_PROFILE); -} -void WDecoCheat::Reload(){ - bVisible = (options[Options::ACTIVE_PROFILE].str == SECRET_PROFILE); -} -bool WDecoCheat::Visible(){ - if(bVisible && it && it->Visible()) - return true; - return false; -} -bool WDecoCheat::Selectable(){ - if(!it || !Visible()) - return false; - return it->Selectable(); -} -//WDecoConfirm - -WDecoConfirm::WDecoConfirm(JGuiListener * _listener, WGuiBase * _it): WGuiDeco(_it){ - listener = _listener; - confirm = "Confirm"; - cancel = "Cancel"; - confirmMenu = NULL; - bModal = false; - mState = OP_CONFIRMED; -} - -WDecoConfirm::~WDecoConfirm(){ - SAFE_DELETE(confirmMenu); -} - -void WDecoConfirm::Entering(u32 key){ - setFocus(true); - - if(it) - it->Entering(key); - - SAFE_DELETE(confirmMenu); - mState = OP_CONFIRMED; - confirmMenu = NEW SimpleMenu(444, listener,Constants::MENU_FONT, 50,170); - confirmMenu->Add(1,confirm.c_str()); - confirmMenu->Add(2,cancel.c_str()); -} - -bool WDecoConfirm::isModal(){ - if(bModal || (it && it->isModal())) - return true; - - return false; -} - -void WDecoConfirm::setModal(bool val){ - bModal = val; -} -void WDecoConfirm::setData(){ - if(!it) - return; - - it->setData(); -} - -bool WDecoConfirm::Leaving(u32 key){ - if(!it) - return true; - - //Choice must be confirmed. - if(mState == OP_UNCONFIRMED){ - if(!isModal()) - setModal(true); - if(!it->Changed()) - mState = OP_CONFIRMED; - else - mState = OP_CONFIRMING; - } - - if(mState == OP_CONFIRMED && it->Leaving(key)){ - setFocus(false); - setModal(false); - SAFE_DELETE(confirmMenu); - return true; - } - - return false; -} -bool WDecoConfirm::CheckUserInput(u32 key){ - if(hasFocus()){ - if (mState == OP_CONFIRMED && key == PSP_CTRL_CIRCLE) - mState = OP_UNCONFIRMED; - - if (mState != OP_CONFIRMING && it){ - if(it->CheckUserInput(key)) - return true; - } else if(confirmMenu && confirmMenu->CheckUserInput(key)) - return true; - } - return false; -} - -void WDecoConfirm::Update(float dt){ - if (hasFocus()){ - if (it && mState != OP_CONFIRMING) - it->Update(dt); - else - confirmMenu->Update(dt); - } -} - -void WDecoConfirm::Overlay(){ - if (confirmMenu && mState == OP_CONFIRMING) - confirmMenu->Render(); - - if(it) - it->Overlay(); -} - -void WDecoConfirm::ButtonPressed(int controllerId, int controlId){ - if(controllerId == 444){ - setModal(false); - switch(controlId){ - case 1: - mState = OP_CONFIRMED; - if(it) - it->confirmChange(true); - break; - case 2: - mState = OP_CONFIRMED; - if(it) - it->confirmChange(false); - break; - } - } - else - it->ButtonPressed(controllerId,controlId); -} - -WGuiButton::WGuiButton( WGuiBase* _it, int _controller, int _control, JGuiListener * jgl): WGuiDeco(_it) { - control = _control; - controller = _controller; - mListener = jgl; -} - -void WGuiButton::updateValue(){ - if(mListener) - mListener->ButtonPressed(controller, control); -} - -bool WGuiButton::CheckUserInput(u32 key){ - if (hasFocus() && key == PSP_CTRL_CIRCLE){ - updateValue(); - return true; - } - return false; -} - -PIXEL_TYPE WGuiButton::getColor(int type){ - if(type == WGuiColor::BACK && hasFocus()) - return it->getColor(WGuiColor::BACK_HEADER); - return it->getColor(type); -}; - -WGuiSplit::WGuiSplit(WGuiBase* _left, WGuiBase* _right) : WGuiItem("") { - right = _right; - left = _left; - bRight = false; - percentRight = 0.5f; - if(!left->Selectable()) - bRight = true; -} -WGuiSplit::~WGuiSplit(){ - SAFE_DELETE(left); - SAFE_DELETE(right); -} - -void WGuiSplit::setData(){ - left->setData(); - right->setData(); -} -void WGuiSplit::setX(float _x){ - x = _x; - left->setX(x); - right->setX(x+(1-percentRight)*width); -} -void WGuiSplit::setY(float _y){ - y = _y; - left->setY(y); - right->setY(y); -} -void WGuiSplit::setWidth(float _w){ - width = _w; - if(right->Visible()) - left->setWidth((1-percentRight)*width); - else - left->setWidth(width); - - right->setWidth(percentRight*width); -} -void WGuiSplit::setHeight(float _h){ - left->setHeight(_h); - right->setHeight(_h); - height = _h; -} -float WGuiSplit::getHeight(){ - float lH, rH; - lH = left->getHeight(); - rH = right->getHeight(); - if(lH > rH) - return lH; - - return rH; -} - -void WGuiSplit::Render(){ - if(right->Visible()) - right->Render(); - if(left->Visible()) - left->Render(); -} - -bool WGuiSplit::isModal(){ - if(bRight) - return right->isModal(); - - return left->isModal(); -} -void WGuiSplit::setModal(bool val){ - if(bRight) - return right->setModal(val); - - return left->setModal(val); -} - -bool WGuiSplit::CheckUserInput(u32 key){ - if(hasFocus()){ - if (!bRight){ - if(key == PSP_CTRL_RIGHT && !isModal() - && right->Selectable() && left->Leaving(PSP_CTRL_RIGHT)){ - bRight = !bRight; - right->Entering(PSP_CTRL_RIGHT); - return true; - } - if(left->CheckUserInput(key)) - return true; - } - else - { - if (key == PSP_CTRL_LEFT && !isModal() - && left->Selectable() && right->Leaving(PSP_CTRL_LEFT)){ - bRight = !bRight; - left->Entering(PSP_CTRL_LEFT); - return true; - } - } - if(right->CheckUserInput(key)) - return true; - } - - return false; -} - -void WGuiSplit::Update(float dt){ - if(bRight) - right->Update(dt); - else - left->Update(dt); -} - -void WGuiSplit::Entering(u32 key){ - mFocus = true; - if(bRight) - right->Entering(key); - else - left->Entering(key); -} -bool WGuiSplit::Leaving(u32 key){ - - if(bRight){ - if(right->Leaving(key)){ - mFocus = false; - return true; - } - } - else{ - if(left->Leaving(key)){ - mFocus = false; - return true; - } - } - - return false; -} -void WGuiSplit::Overlay(){ - if(bRight) - right->Overlay(); - else - left->Overlay(); -} -void WGuiSplit::Underlay(){ - if(bRight) - right->Underlay(); - else - left->Underlay(); -} -void WGuiSplit::ButtonPressed(int controllerId, int controlId) -{ - if(bRight) - right->ButtonPressed(controllerId, controlId); - else - left->ButtonPressed(controllerId, controlId); -} -void WGuiSplit::Reload(){ - left->Reload(); - right->Reload(); -} -void WGuiSplit::confirmChange(bool confirmed){ - right->confirmChange(confirmed); - left->confirmChange(confirmed); -} - -//WGuiMenu -WGuiMenu::WGuiMenu(u32 next = PSP_CTRL_RIGHT, u32 prev = PSP_CTRL_LEFT): WGuiItem(""){ - buttonNext = next; - buttonPrev = prev; - currentItem = -1; -} -WGuiMenu::~WGuiMenu(){ - for(vector::iterator it = items.begin();it!=items.end();it++) - SAFE_DELETE(*it); - items.clear(); -} -void WGuiMenu::setData(){ - for(vector::iterator it = items.begin();it!=items.end();it++) - (*it)->setData(); -}; - -void WGuiMenu::Reload(){ - for(vector::iterator it = items.begin();it!=items.end();it++) - (*it)->Reload(); -}; - -void WGuiMenu::Render(){ - for(vector::iterator it = items.begin();it!=items.end();it++) - (*it)->Render(); -} -void WGuiMenu::confirmChange(bool confirmed){ - for(vector::iterator it = items.begin();it!=items.end();it++) - (*it)->confirmChange(confirmed); -} - -void WGuiMenu::ButtonPressed(int controllerId, int controlId){ - WGuiBase * it = Current(); - if(!it) return; - it->ButtonPressed(controllerId,controlId); -} - -WGuiBase * WGuiMenu::Current(){ - if(currentItem >= 0 && currentItem < (int) items.size()) - return items[currentItem]; - return NULL; -} -void WGuiMenu::Add(WGuiBase * it){ - if(it) - items.push_back(it); -} - - -bool WGuiMenu::CheckUserInput(u32 key){ - bool kidModal = false; - bool handledInput = false; - int nbitems = (int) items.size(); - JGE * mEngine = JGE::GetInstance(); - - if(!mEngine->GetButtonState(held)) //Key isn't held down. - held = 0; - - if(currentItem >= 0 && currentItem < nbitems) - kidModal = items[currentItem]->isModal(); - - if(!kidModal && hasFocus()){ - if (key == buttonPrev){ - held = buttonPrev; - duration = 0; - if(prevItem()) - return true; - } - else if(held == buttonPrev && duration > 1){ - duration = .92; - if(prevItem()) - return true; - } - else if (key == buttonNext){ - held = buttonNext; - duration = 0; - if(nextItem()) - return true; - } - else if(held == buttonNext && duration > 1){ - duration = .92; - if(nextItem()) - return true; - } - } - - if(currentItem >= 0 && currentItem < nbitems) - return items[currentItem]->CheckUserInput(key); - - return false; -} - -void WGuiMenu::Update(float dt){ - int nbitems = (int) items.size(); - JGE * mEngine = JGE::GetInstance(); - - if(held) - duration += dt; - - if(currentItem >= 0 && currentItem < nbitems) - items[currentItem]->Update(dt); - - for (int i = 0 ; i < nbitems; i++){ - if(i != currentItem) - items[i]->Update(dt); - } -} - -bool WGuiMenu::nextItem(){ - int potential = currentItem; - int nbitems = (int) items.size(); - - if (potential < nbitems-1){ - potential++; - while(potential < nbitems-1 && items[potential]->Selectable() == false) - potential++; - if(potential == nbitems || !items[potential]->Selectable()) - potential = -1; - else if(potential != currentItem && items[currentItem]->Leaving(buttonNext)){ - currentItem = potential; - items[currentItem]->Entering(buttonNext); - return true; - } - } - return false; -} - -bool WGuiMenu::prevItem(){ - int potential = currentItem; - - if (potential > 0){ - potential--; - while(potential > 0 && items[potential]->Selectable() == false) - potential--; - if(potential < 0 || !items[potential]->Selectable()) - potential = -1; - else if(items[currentItem]->Leaving(buttonPrev)){ - currentItem = potential; - items[currentItem]->Entering(buttonPrev); - return true; - } - } - return false; -} - -void WGuiMenu::setModal(bool val){ - WGuiBase* c = Current(); - if(c) - c->setModal(val); -} -bool WGuiMenu::isModal(){ - WGuiBase* c = Current(); - if(c) - return c->isModal(); - - return false; -} -//WGuiTabMenu -void WGuiTabMenu::Add(WGuiBase * it){ - if (it){ - it->setY(it->getY()+35); - it->setHeight(it->getHeight()-35); - WGuiMenu::Add(it); - } -} - -void WGuiTabMenu::Render(){ - JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); - JRenderer * renderer = JRenderer::GetInstance(); - - if (!items.size()) - return; - - int offset = x; - - for(vector::iterator it = items.begin();it!=items.end();it++){ - int w = mFont->GetStringWidth(_((*it)->getDisplay()).c_str()); - mFont->SetColor((*it)->getColor(WGuiColor::TEXT_TAB)); - renderer->FillRoundRect(offset+5,5,w + 5,25,2,(*it)->getColor(WGuiColor::BACK_TAB)); - mFont->DrawString(_((*it)->getDisplay()).c_str(),offset+10,10); - offset += w + 10 + 2; - } - - WGuiBase * c = Current(); - if(c) - c->Render(); -} - -void WGuiTabMenu::save(){ - confirmChange(true); - setData(); - ::options.save(); -} - - -//WGuiAward -void WGuiAward::Overlay(){ - JRenderer * r = JRenderer::GetInstance(); - JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); - mFont->SetScale(.8); - mFont->SetColor(getColor(WGuiColor::TEXT)); - - string s = details; - if(s.size()){ - float fW = mFont->GetStringWidth(s.c_str()); - float fH = mFont->GetHeight(); - - if(fH < 16) - fH = 18; - JQuad * button = resources.RetrieveQuad("iconspsp.png", (float)4*32, 0, 32, 32,"",RETRIEVE_NORMAL); - - r->FillRoundRect(5,10,fW+32,fH+2,2,getColor(WGuiColor::BACK)); - if(button) - r->RenderQuad(button, 10,12,0,.5,.5); - mFont->DrawString(s,30,16); - } - - mFont->SetScale(1); -} -void WGuiAward::Underlay(){ - char buf[1024]; - JRenderer * r = JRenderer::GetInstance(); - JQuad * trophy = NULL; - - string n = Options::getName(id); - if(n.size()){ - sprintf(buf,"trophy_%s.png",n.c_str()); //Trophy specific to the award - trophy = resources.RetrieveTempQuad(buf); //Themed version... - } - - if(!trophy && id >= Options::SET_UNLOCKS){ - trophy = resources.RetrieveTempQuad("trophy_set.png"); //TODO FIXME: Should look in set dir too. - } - - if(!trophy) //Fallback to basic trophy image. - trophy = resources.RetrieveTempQuad("trophy.png"); - - if(trophy){ - r->RenderQuad(trophy, 0, SCREEN_HEIGHT-trophy->mHeight); - } - -} -void WGuiAward::Render(){ - GameOptionAward * goa = dynamic_cast(&options[id]); - - if(!goa) - return; - - JRenderer * renderer = JRenderer::GetInstance(); - JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); - mFont->SetScale(1); - mFont->SetColor(getColor(WGuiColor::TEXT)); - - float myX = x; - float myY = y; - float fH = mFont->GetHeight(); - float fM = fH / 5; //Font Margin is 20% font height - - renderer->FillRoundRect(x-fM/2,y-fM,getWidth()-fM,fH-fM,fM,getColor(WGuiColor::BACK_TAB)); - myX += fM; - - mFont->DrawString(_(displayValue).c_str(),myX,myY,JGETEXT_LEFT); - myY += fH + 3*fM; - - mFont->SetScale(.75); - fH = mFont->GetHeight(); - if(text.size()){ - mFont->DrawString(_(text).c_str(),myX,myY,JGETEXT_LEFT); - myY+=fH + fM; - } - string s = goa->menuStr(); - if(s.size()){ - mFont->DrawString(s.c_str(),myX,myY,JGETEXT_LEFT); - myY+=fH + fM; - } - setHeight(myY-y); - mFont->SetScale(1); - -} - -WGuiAward::WGuiAward(int _id, string name, string _text, string _details): WGuiItem(name){ - id = _id; - text = _text; - height = 60; - details = _details; -} -WGuiAward::~WGuiAward(){ - GameOptionAward * goa = dynamic_cast(&options[id]); - if(goa) - goa->setViewed(true); -} -bool WGuiAward::Visible(){ - //WGuiAward is only visible when it's tied to an already acchieved award. - GameOptionAward * goa = dynamic_cast(&options[id]); - if(!goa || !goa->number) - return false; - return true; -} - -//WGuiImage -WGuiImage::WGuiImage(WDataSource * wds, float _w, float _h, int _margin): WGuiItem("") { - imgW = _w; - imgH = _h; - margin = _margin; - source = wds; -} - -void WGuiImage::imageScale(float _w, float _h){ - imgH = _h; - imgW = _w; -} - -float WGuiImage::getHeight(){ - JQuad * q = NULL; - - if(imgH == 0 ){ - if(source && (q = source->getImage())) //Intentional assignment. - return MAX(height,q->mHeight+(2*margin)); - } - - return MAX(height,imgH+(2*margin)); -} - -void WGuiImage::Render(){ - if(!source) - return; - - JRenderer * renderer = JRenderer::GetInstance(); - JQuad * q = source->getImage(); - if(q){ - float xS = 1, yS = 1; - if(imgH != 0 && q->mHeight != 0) - yS = imgH / q->mHeight; - if(imgW != 0 && q->mWidth != 0) - xS = imgW / q->mWidth; - - renderer->RenderQuad(q,x+margin, y+margin,0,xS,yS); - } -} - -WGuiCardImage::WGuiCardImage(WDataSource * wds, int _offset) : WGuiImage(wds){ - offset = _offset; -}; - -void WGuiCardImage::Render(){ - if(!source) - return; - - JRenderer * renderer = JRenderer::GetInstance(); - MTGCard * c = source->getCard(); - if(!c) - return; - - Pos p(x+margin, y+margin, 1,0,255); - - int oldpos = source->getPos(); - - if(offset){ - source->setPos(oldpos+offset); - c = source->getCard(); - } - - JQuad * q = source->getImage(); - if(!q || options[Options::DISABLECARDS].number) - CardGui::alternateRender(c,p); - else - CardGui::RenderBig(c,p); - - if(offset) - source->setPos(oldpos); -} -//WSrcImage -JQuad * WSrcImage::getImage(){ - return resources.RetrieveTempQuad(filename); -} - -WSrcImage::WSrcImage(string s){ - filename = s; -} - -bool WCSortAlpha::operator()(const MTGCard*l, const MTGCard*r){ - if(!l || !r || !l->data || !r->data) - return false; - string ln = l->data->getLCName(); - string rn = r->data->getLCName(); - return (ln < rn); -} -bool WCSortCollector::operator()(const MTGCard*l, const MTGCard*r){ - if(!l || !r || !l->data || !r->data) - return false; - - int lc, rc; - lc = l->data->countColors(); rc = r->data->countColors(); - if(lc == 0) lc = 999; - if(rc == 0) rc = 999; - - int isW = (int)l->data->hasColor(Constants::MTG_COLOR_WHITE) - (int) r->data->hasColor(Constants::MTG_COLOR_WHITE); - int isU = (int)l->data->hasColor(Constants::MTG_COLOR_BLUE) - (int) r->data->hasColor(Constants::MTG_COLOR_BLUE); - int isB = (int)l->data->hasColor(Constants::MTG_COLOR_BLACK) - (int) r->data->hasColor(Constants::MTG_COLOR_BLACK); - int isR = (int)l->data->hasColor(Constants::MTG_COLOR_RED) - (int) r->data->hasColor(Constants::MTG_COLOR_RED); - int isG = (int)l->data->hasColor(Constants::MTG_COLOR_GREEN) - (int) r->data->hasColor(Constants::MTG_COLOR_GREEN); - int isArt = (int)l->data->hasType(Subtypes::TYPE_ARTIFACT) - (int) r->data->hasType(Subtypes::TYPE_ARTIFACT); - int isLand = (int)l->data->hasType(Subtypes::TYPE_LAND) - (int) r->data->hasType(Subtypes::TYPE_LAND); - - //Nested if hell. TODO: Farm these out to their own objects as a user-defined filter/sort system. - if(!isLand){ - int isBasic = (int)l->data->hasType("Basic") - (int) r->data->hasType("Basic"); - if(!isBasic){ - if(!isArt){ - if(lc == rc){ - if(!isG){ - if(!isR){ - if(!isB){ - if(!isU){ - if(!isW){ - string ln = l->data->getLCName(); - string rn = r->data->getLCName(); - if(ln.substr(0,4) == "the ") - ln = ln.substr(4); - if(rn.substr(0,4) == "the ") - rn = rn.substr(4); - return (ln < rn); - } - return (isW < 0); - } - return (isU < 0); - } - return (isB < 0); - } - return (isR < 0); - } - return (isG < 0); - } - return (lc < rc); - } - return (isArt < 0); - } - else return(isBasic < 0); - } - return (isLand < 0); -} -//WSrcMTGSet -WSrcMTGSet::WSrcMTGSet(int setid, float delay){ - MTGAllCards * ac = GameApp::collection; - map::iterator it; - mDelay = delay; - mLastInput = 0; - - for(it = ac->collection.begin();it != ac->collection.end();it++){ - if(it->second->setId == setid && it->second->getId() >= 0) - cards.push_back(it->second); - } - - std::sort(cards.begin(),cards.end(),WCSortCollector()); - - currentCard = -1; - if(cards.size()) - currentCard = 0; -} -JQuad * WSrcMTGSet::getImage(){ -#if defined WIN32 || defined LINUX //Loading delay only on PSP. -#else - if(mDelay && mLastInput < mDelay){ - return resources.RetrieveCard(getCard(),RETRIEVE_EXISTING); - } -#endif - - return resources.RetrieveCard(getCard()); -} -void WSrcMTGSet::Update(float dt){ - mLastInput += dt; -} -MTGCard * WSrcMTGSet::getCard(){ - int size = (int) cards.size(); - if(!size || currentCard < 0 || currentCard >= size) - return NULL; - - return cards[currentCard]; -} - -bool WSrcMTGSet::next(){ - if(currentCard < (int) cards.size()) - currentCard++; - else - return false; - - mLastInput = 0; - return true; -} - -bool WSrcMTGSet::prev(){ - if(currentCard > 0) - currentCard--; - else - return false; - - mLastInput = 0; - return true; -} - -bool WSrcMTGSet::setPos(int pos){ - if(pos < 0 || pos >= (int) cards.size()) - return false; - - currentCard = pos; - mLastInput = 0; - return true; -} - -bool WSrcMTGSet::thisCard(int mtgid){ - for(size_t t=0;tgetId() == mtgid){ - currentCard = (int) t; - return true; - } - } - return false; -} +} \ No newline at end of file diff --git a/projects/mtg/src/ShopItem.cpp b/projects/mtg/src/ShopItem.cpp deleted file mode 100644 index 75e8cc32e..000000000 --- a/projects/mtg/src/ShopItem.cpp +++ /dev/null @@ -1,554 +0,0 @@ -#include "../include/config.h" -#include "../include/ShopItem.h" -#include "../include/GameStateShop.h" -#include "../include/CardGui.h" -#include "../include/WResourceManager.h" -#include "../include/Translate.h" -#include - - - - float ShopItems::_x1[] = { 79, 19, 27,103,154,187,102,144,198,133,183}; - float ShopItems::_y1[] = {150,194,222,167,164,156,195,190,175,220,220}; - - float ShopItems::_x2[] = {103, 48, 74,135,183,215,138,181,231,171,225}; - float ShopItems::_y2[] = {155,179,218,165,166,155,195,186,177,225,216}; - - float ShopItems::_x3[] = { 48, 61, 9, 96,139,190, 81,146,187, 97,191}; - float ShopItems::_y3[] = {164,205,257,184,180,170,219,212,195,251,252}; - - float ShopItems::_x4[] = { 76, 90, 65,131,171,221,123,187,225,141,237}; - float ShopItems::_y4[] = {169,188,250,182,182,168,220,208,198,259,245}; - - -ShopItem::ShopItem(int id, JLBFont *font, char* text, JQuad * _quad,JQuad * _thumb, float _xy[], bool hasFocus, int _price): JGuiObject(id), mFont(font), mText(text), quad(_quad), thumb(_thumb), price(_price) -{ - for (int i = 0; i < 8; ++i){ - xy[i] = _xy[i]; - } - quantity = 10; - card = NULL; - mHasFocus = hasFocus; - mRelease = false; - - mScale = 1.0f; - mTargetScale = 1.0f; - - mesh=NEW hgeDistortionMesh(2,2); - mesh->SetTexture(thumb->mTex); - float x0,y0,w0,h0; - thumb->GetTextureRect(&x0,&y0,&w0,&h0); - mesh->SetTextureRect(x0,y0,w0,h0); - mesh->Clear(ARGB(0xFF,0xFF,0xFF,0xFF)); - mesh->SetDisplacement(0, 0, xy[0],xy[1], HGEDISP_NODE); - mesh->SetDisplacement(1, 0, xy[2] - w0,xy[3], HGEDISP_NODE); - mesh->SetDisplacement(0, 1,xy[4],xy[5]-h0, HGEDISP_NODE); - mesh->SetDisplacement(1, 1, xy[6]-w0,xy[7]-h0, HGEDISP_NODE); - mesh->SetColor(1,1,ARGB(255,100,100,100)); - mesh->SetColor(0,1,ARGB(255,100,100,100)); - mesh->SetColor(1,0,ARGB(255,100,100,100)); - mesh->SetColor(0,0,ARGB(255,200,200,200)); - if (hasFocus) - Entering(); -} - -ShopItem::ShopItem(int id, JLBFont *font, int _cardid, float _xy[], bool hasFocus, MTGAllCards * collection, int _price, DeckDataWrapper * ddw): JGuiObject(id), mFont(font), price(_price){ - for (int i = 0; i < 8; ++i){ - xy[i] = _xy[i]; - } - mHasFocus = hasFocus; - mRelease = false; - mScale = 1.0f; - mTargetScale = 1.0f; - - if (hasFocus) - Entering(); - - card = collection->getCardById(_cardid); - updateCount(ddw); - - quantity = 1 + (rand() % 4); - if (card->getRarity() == Constants::RARITY_L) quantity = 50; - quad = NULL; - - mesh = NULL; - thumb = NULL; - updateThumb(); -} - - -int ShopItem::updateCount(DeckDataWrapper * ddw){ - if (!card) return 0; - nameCount = ddw->countByName(card); - return nameCount; -} - -ShopItem::~ShopItem(){ - OutputDebugString("delete shopitem\n"); - if(thumb) - resources.Release(thumb->mTex); - SAFE_DELETE(mesh); -} - -const char * ShopItem::getText(){ - return mText.c_str(); -} - -void ShopItem::updateThumb(){ - if(card == NULL) - return; - - if(thumb && mRelease) - resources.Release(thumb->mTex); - mRelease = false; - - if(mesh) - SAFE_DELETE(mesh); - else if(thumb) - SAFE_DELETE(thumb); - - thumb = resources.RetrieveCard(card,RETRIEVE_LOCK,TEXTURE_SUB_THUMB); -#if defined (WIN32) || defined (LINUX) - //On pcs we render the big image if the thumbnail is not available - if (!thumb) thumb = resources.RetrieveCard(card,RETRIEVE_LOCK); -#endif - if (!thumb) - thumb = CardGui::alternateThumbQuad(card); - else - mRelease = true; - - - if (thumb){ - mesh=NEW hgeDistortionMesh(2,2); - mesh->SetTexture(thumb->mTex); - float x0,y0,w0,h0; - thumb->GetTextureRect(&x0,&y0,&w0,&h0); - mesh->SetTextureRect(x0,y0,w0,h0); - mesh->Clear(ARGB(0xFF,0xFF,0xFF,0xFF)); - mesh->SetDisplacement(0, 0, xy[0],xy[1], HGEDISP_NODE); - mesh->SetDisplacement(1, 0, xy[2] - w0,xy[3], HGEDISP_NODE); - mesh->SetDisplacement(0, 1,xy[4],xy[5]-h0, HGEDISP_NODE); - mesh->SetDisplacement(1, 1, xy[6]-w0,xy[7]-h0, HGEDISP_NODE); - mesh->SetColor(1,1,ARGB(255,100,100,100)); - mesh->SetColor(0,1,ARGB(255,100,100,100)); - mesh->SetColor(1,0,ARGB(255,100,100,100)); - mesh->SetColor(0,0,ARGB(255,200,200,200)); - }else{ - mesh = NULL; - } -} - -void ShopItem::Render(){ - if (mHasFocus){ - mFont->SetColor(ARGB(255,255,255,0)); - }else{ - mFont->SetColor(ARGB(255,255,255,255)); - } - if (!quantity){ - mFont->SetColor(ARGB(255,128,128,128)); - } - - if (card){ - if (nameCount){ - char buffer[512]; - sprintf(buffer, "%s (%i)", _(card->data->name).c_str(), nameCount ); - mText = buffer; - }else{ - mText = _(card->data->name).c_str(); - } - } - - JRenderer * renderer = JRenderer::GetInstance(); - - if (mesh){ - mesh->Render(0,0); - }else{ - //ERROR Management - } - if (mHasFocus){ - if (card) quad = resources.RetrieveCard(card); - if (quad){ - float scale = 0.9 * 285/ quad->mHeight; - quad->SetColor(ARGB(255,255,255,255)); - renderer->RenderQuad(quad,SCREEN_WIDTH - 105,SCREEN_HEIGHT/2 - 5,0, scale,scale); - }else{ - if (card) CardGui::alternateRender(card,Pos(SCREEN_WIDTH - 105,SCREEN_HEIGHT/2 - 5,0.9f* 285/250, 0,255)); - - } - } -} - -void ShopItem::Update(float dt) -{ - if (mScale < mTargetScale){ - mScale += 8.0f*dt; - if (mScale > mTargetScale) - mScale = mTargetScale; - }else if (mScale > mTargetScale){ - mScale -= 8.0f*dt; - if (mScale < mTargetScale) - mScale = mTargetScale; - } -} - - - - -void ShopItem::Entering() -{ - for (int i = 0; i < 2; ++i){ - for (int j = 0; j < 2; ++j){ - mesh->SetColor(i,j,ARGB(255,255,255,255)); - } - } - - - mHasFocus = true; - mTargetScale = 1.2f; -} - - -bool ShopItem::Leaving(u32 key) -{ - mesh->SetColor(1,1,ARGB(255,100,100,100)); - mesh->SetColor(0,1,ARGB(255,100,100,100)); - mesh->SetColor(1,0,ARGB(255,100,100,100)); - mesh->SetColor(0,0,ARGB(255,200,200,200)); - - mHasFocus = false; - mTargetScale = 1.0f; - return true; -} - - -bool ShopItem::ButtonPressed() -{ - return (quantity >0); -} - - -ShopItems::ShopItems(int id, JGuiListener* listener, JLBFont* font, int x, int y, MTGAllCards * _collection, int _setIds[]): JGuiController(id, listener), mX(x), mY(y), mFont(font), collection(_collection){ - mHeight = 0; - showPriceDialog = -1; - dialog = NULL; - pricelist = NEW PriceList(RESPATH"/settings/prices.dat",_collection); - playerdata = NEW PlayerData(_collection); - display = NULL; - for (int i=0; i < SHOP_BOOSTERS; i++){ - setIds[i] = _setIds[i]; - }; - myCollection = NEW DeckDataWrapper(NEW MTGDeck(options.profileFile(PLAYER_COLLECTION).c_str(), _collection)); - showCardList = true; - - mBgAA = NULL; - mBgAATex = resources.RetrieveTexture("shop_aliasing.png",RETRIEVE_LOCK); - if(mBgAATex){ - mBgAA = resources.RetrieveQuad("shop_aliasing.png"); - mBgAA->SetTextureRect(0,1,250,119); - } - - lightAlpha = 0; - alphaChange = 0; -} - - - -void ShopItems::Add(int cardid){ - int rnd = (rand() % 20); - int price = pricelist->getPrice(cardid); - price = price + price * (rnd -10)/100; - float xy[] = {_x1[mCount],_y1[mCount],_x2[mCount],_y2[mCount],_x3[mCount],_y3[mCount],_x4[mCount],_y4[mCount]}; - JGuiController::Add(NEW ShopItem(mCount, mFont, cardid, xy, (mCount == 0),collection, price,myCollection)); - mHeight += 22; -} - -void ShopItems::Add(char * text, JQuad * quad,JQuad * thumb, int price){ - float xy[] = {_x1[mCount],_y1[mCount],_x2[mCount],_y2[mCount],_x3[mCount],_y3[mCount],_x4[mCount],_y4[mCount]}; - JGuiController::Add(NEW ShopItem(mCount, mFont, text, quad, thumb, xy, (mCount == 0), price)); - mHeight += 22; -} - -void ShopItems::Update(float dt){ - - if (display){ - display->Update(dt); - }else{ - if (showPriceDialog!=-1){ - ShopItem * item = ((ShopItem *)mObjects[showPriceDialog]); - int price = item->price; - char buffer[4096]; - sprintf(buffer,"%s : %i credits",item->getText(),price); - if(!dialog){ - dialog = NEW SimpleMenu(1,this,Constants::MENU_FONT,SCREEN_WIDTH-300,SCREEN_HEIGHT/2,buffer); - dialog->Add(1,"Yes"); - dialog->Add(2,"No"); - if(options[Options::CHEATMODE].number) { - dialog->Add(3,"*Steal 1,000 credits*"); - } - } - else{ - dialog->Update(dt); - } - }else{ - SAFE_DELETE(dialog); - JGuiController::Update(dt); - } - - } - - alphaChange = (500 - (rand() % 1000)) * dt; - lightAlpha+= alphaChange; - if (lightAlpha < 0) lightAlpha = 0; - if (lightAlpha > 50) lightAlpha = 50; -} - - -void ShopItems::Render(){ - JGuiController::Render(); - JRenderer * r = JRenderer::GetInstance(); - - - if (mBgAA) - r->RenderQuad(mBgAA,0,SCREEN_HEIGHT-127); - - JQuad * quad = resources.RetrieveTempQuad("shop_light.jpg",TEXTURE_SUB_5551); - if (quad){ - r->EnableTextureFilter(false); - r->SetTexBlend(BLEND_SRC_ALPHA, BLEND_ONE); - quad->SetColor(ARGB(lightAlpha,255,255,255)); - r->RenderQuad(quad,0,0); - r->SetTexBlend(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA); - r->EnableTextureFilter(true); - } - - - if (display) display->Render(); - - if (showPriceDialog!=-1){ - if(dialog){ - dialog->Render(); - } - } - - mFont->SetColor(ARGB(255,255,255,255)); - char c[4096]; - r->FillRect(0,SCREEN_HEIGHT-17,SCREEN_WIDTH,17,ARGB(128,0,0,0)); - sprintf(c, "%s", _("[]:other cards").c_str()); - unsigned int len = 4 + mFont->GetStringWidth(c); - mFont->DrawString(c,SCREEN_WIDTH-len,SCREEN_HEIGHT-14); - - char credits[512]; - sprintf(credits,_("credits: %i").c_str(), playerdata->credits); - mFont->SetColor(ARGB(200,0,0,0)); - mFont->DrawString(credits, 5, SCREEN_HEIGHT - 12); - mFont->SetColor(ARGB(255,255,255,255)); - mFont->DrawString(credits, 5, SCREEN_HEIGHT - 14); - - if(mCurr >= 0){ - mFont->SetColor(ARGB(255,255,255,0)); - ShopItem * item = ((ShopItem *)mObjects[mCurr]); - mFont->DrawString(item->mText.c_str(), SCREEN_WIDTH/2 - 50, SCREEN_HEIGHT - 14,JGETEXT_CENTER); - mFont->SetColor(ARGB(255,255,255,255)); - } - - if (showCardList){ - r->FillRoundRect(290,5, 160, mCount * 20 + 15,5,ARGB(200,0,0,0)); - - for (int i = 0; i< mCount; ++i){ - if (!mObjects[i]) continue; - ShopItem * s = (ShopItem *)(mObjects[i]); - if (i == mCurr) mFont->SetColor(ARGB(255,255,255,0)); - else mFont->SetColor(ARGB(255,255,255,255)); - char buffer[512]; - sprintf(buffer, "%s", s->getText()); - float x = 300; - float y = 10 + 20*i; - mFont->DrawString(buffer,x,y); - } - } -} - -void ShopItems::pricedialog(int id, int mode){ - if (mode){ - showPriceDialog = id; - }else{ - showPriceDialog = -1; - } -} - -void ShopItems::ButtonPressed(int controllerId, int controlId){ - if (controllerId == 12){ - safeDeleteDisplay(); - return; - } - - ShopItem * item = ((ShopItem *)mObjects[showPriceDialog]); - int price = item->price; - switch(controlId){ - case 1: - if (playerdata->credits >= price){ - playerdata->credits -= price; - if (item->card){ - int rnd = (rand() % 25); - price = price + (rnd * price)/100; - pricelist->setPrice(item->card->getMTGId(),price); - playerdata->collection->add(item->card); - item->quantity--; - myCollection->Add(item->card); - item->nameCount++; - item->price = price; - }else{ - safeDeleteDisplay(); - display = NEW CardDisplay(12,NULL, SCREEN_WIDTH - 200, SCREEN_HEIGHT/2,this,NULL,5); - - MTGDeck * tempDeck = NEW MTGDeck(playerdata->collection->database); - int rare_or_mythic = Constants::RARITY_R; - int rnd = rand() % 8; - if (rnd == 0) rare_or_mythic = Constants::RARITY_M; - int sets[] = {setIds[showPriceDialog]}; - MTGSetInfo* si = setlist.getInfo(setIds[showPriceDialog]); - - tempDeck->addRandomCards(si->booster[MTGSetInfo::RARE], sets,1,rare_or_mythic); - tempDeck->addRandomCards(si->booster[MTGSetInfo::UNCOMMON], sets,1,Constants::RARITY_U); - tempDeck->addRandomCards(si->booster[MTGSetInfo::COMMON], sets,1,Constants::RARITY_C); - tempDeck->addRandomCards(si->booster[MTGSetInfo::LAND], sets,1,Constants::RARITY_L); - - //Check for duplicates. Does not guarentee none, just makes them extremely unlikely. - //Code is kind of inefficient, but shouldn't be used often enough to matter. - int loops=0; - for(map::iterator it = tempDeck->cards.begin();it!= tempDeck->cards.end() && loops < 15;it++,loops++){ - int dupes = it->second - 1; - if(dupes <= 0) - continue; - - for(int x=0;xremove(it->first); - - int rarity = (int) tempDeck->database->getCardById(it->first)->getRarity(); - tempDeck->addRandomCards(dupes,sets,1,rarity); - it = tempDeck->cards.begin(); - } - - playerdata->collection->add(tempDeck); - myCollection->Add(tempDeck); - - for (int j = 0; j < mCount; j++){ - ShopItem * si = ((ShopItem *)mObjects[j]); - si->updateCount(myCollection); - } - - int i = 0; - for(int cycle=0;cycle<4;cycle++) - for (map::iterator it = tempDeck->cards.begin(); it!=tempDeck->cards.end(); it++){ - MTGCard * c = tempDeck->getCardById(it->first); - char rarity = c->getRarity(); - if((cycle == 0 && rarity == Constants::RARITY_L) - || (cycle == 1 && rarity == Constants::RARITY_C) - || (cycle == 2 && rarity == Constants::RARITY_U) - || (cycle == 3 && (rarity == Constants::RARITY_R || rarity == Constants::RARITY_M))) - for (int j = 0; j < it->second; j++){ - MTGCardInstance * card = NEW MTGCardInstance(c, NULL); - displayCards[i] = card; - display->AddCard(card); - i++; - } - } - delete tempDeck; - } - //Check if we just scored an award... - if(myCollection && myCollection->totalPrice() > 10000){ - GameOptionAward * goa = dynamic_cast(&options[Options::AWARD_COLLECTOR]); - if(goa) - goa->giveAward(); - } - showPriceDialog = -1; - }else{ - //error not enough money - } - break; - case 2: - if (item->card){ - int rnd = (rand() % 25); - price = price - (rnd * price)/100; - pricelist->setPrice(item->card->getMTGId(),price); - } - showPriceDialog = -1; - break; - case 3: // (PSY) Cheatmode: get free money - playerdata->credits += 1000; - break; - } -} - - -void ShopItems::safeDeleteDisplay(){ - if (!display) return; - for (int i = 0; i < display->mCount; i++){ - delete displayCards[i]; - } - SAFE_DELETE(display); -} - -void ShopItems::saveAll(){ - savePriceList(); - playerdata->save(); -} - -void ShopItems::savePriceList(){ - pricelist->save(); -} - -ShopItems::~ShopItems(){ - SAFE_DELETE(pricelist); - SAFE_DELETE(playerdata); - SAFE_DELETE(dialog); - safeDeleteDisplay(); - SAFE_DELETE(myCollection); - resources.Release(mBgAATex); -} - -ostream& ShopItem::toString(ostream& out) const -{ - return out << "ShopItem ::: mHasFocus : " << mHasFocus - << " ; mFont : " << mFont - << " ; mText : " << mText - << " ; quad : " << quad - << " ; thumb : " << thumb - << " ; mScale : " << mScale - << " ; mTargetScale : " << mTargetScale - << " ; nameCount : " << nameCount - << " ; quantity : " << quantity - << " ; card : " << card - << " ; price : " << price; -} - -bool ShopItems::CheckUserInput(u32 key){ - if (display && display->CheckUserInput(key)) - return true; - if(showPriceDialog==-1){ - u32 buttons[] = {PSP_CTRL_LEFT,PSP_CTRL_DOWN,PSP_CTRL_RIGHT,PSP_CTRL_UP,PSP_CTRL_CIRCLE}; - for (int i = 0; i < 5; ++i){ - if (key == buttons[i]) - showCardList = false; - } - - if(key == PSP_CTRL_TRIANGLE){ - options[Options::DISABLECARDS].number = !options[Options::DISABLECARDS].number; - vector::iterator it; - for(it=mObjects.begin();it!=mObjects.end();it++){ - ShopItem * si = dynamic_cast(*it); - if(si) - si->updateThumb(); - } - return true; - } - if (key == PSP_CTRL_CROSS){ - showCardList = !showCardList; - return true; - } - } - else if(dialog) - return dialog->CheckUserInput(key); - - return JGuiController::CheckUserInput(key); -} diff --git a/projects/mtg/src/Tasks.cpp b/projects/mtg/src/Tasks.cpp index 1bc622cc3..f1ef332ac 100644 --- a/projects/mtg/src/Tasks.cpp +++ b/projects/mtg/src/Tasks.cpp @@ -164,8 +164,6 @@ void Task::loadAIDeckNames() { while (found){ found = 0; char buffer[512]; - char smallDeckName[512]; - char deckDesc[512]; sprintf(buffer, "%s/deck%i.txt",RESPATH"/ai/baka",nbDecks + 1); if(fileExists(buffer)){ diff --git a/projects/mtg/src/WDataSrc.cpp b/projects/mtg/src/WDataSrc.cpp new file mode 100644 index 000000000..6d78dc106 --- /dev/null +++ b/projects/mtg/src/WDataSrc.cpp @@ -0,0 +1,490 @@ +#include "../include/config.h" +#include "../include/OptionItem.h" +#include "../include/PlayerData.h" +#include "../include/Translate.h" +#include "../include/PriceList.h" +#include "../include/Subtypes.h" +#include + +//WSyncable +bool WSyncable::Hook(WSyncable* s){ + if(hooked) + return false; + hooked = s; + return true; +} +int WSyncable::getPos(){ + if(hooked) + return hooked->getPos()+currentPos; + return currentPos; +} +bool WSyncable::next(){ + if(hooked) + return hooked->next(); + ++currentPos; + return true; +} +bool WSyncable::prev(){ + if(hooked) + return hooked->prev(); + --currentPos; + return true; +} + +//WSrcImage +JQuad * WSrcImage::getImage( int offset){ + return resources.RetrieveTempQuad(filename); +} + +WSrcImage::WSrcImage(string s){ + filename = s; +} +//WSrcCards +WSrcCards::WSrcCards(float delay){ + mDelay = delay; + mLastInput = 0; + currentPos = 0; + filtersRoot=NULL; +} +JQuad * WSrcCards::getImage( int offset){ +#if defined WIN32 || defined LINUX //Loading delay only on PSP. +#else + if(mDelay && mLastInput < mDelay){ + return resources.RetrieveCard(getCard(offset),RETRIEVE_EXISTING); + } +#endif + + return resources.RetrieveCard(getCard(offset)); +} +JQuad * WSrcCards::getThumb( int offset){ + return resources.RetrieveCard(getCard(offset),RETRIEVE_THUMB); +} +WSrcCards::~WSrcCards(){ + clearFilters(); + cards.clear(); +} +void WSrcCards::bakeFilters(){ + vector temp; + + setOffset(0); + for(int t=0;tisMatch(c); +} +int WSrcCards::Size(bool all){ + if(!all && filtersRoot) + return (int) validated.size(); + return (int) cards.size(); +} +MTGCard * WSrcCards::getCard(int offset, bool ignore){ + int oldpos; + int size = (int) cards.size(); + MTGCard * c = NULL; + if(!ignore && filtersRoot) + size = (int) validated.size(); + + if(!size) + return NULL; + + oldpos = currentPos; + if(offset != 0) + currentPos += offset; + while(currentPos < 0) + currentPos = size+currentPos; + currentPos = currentPos % size; + + if(!ignore && filtersRoot) + c = cards[validated[currentPos]]; + else + c = cards[currentPos]; + currentPos = oldpos; + return c; +} +int WSrcCards::loadMatches(MTGAllCards* ac){ + map::iterator it; + int count = 0; + if(!ac) + return count; + + for(it=ac->collection.begin();it!=ac->collection.end();it++){ + if(it->second && (matchesFilters(it->second))){ + cards.push_back(it->second); + count++; + } + } + validateFilters(); + return count; +} +int WSrcCards::loadMatches(MTGDeck * deck){ + map::iterator it; + int count = 0; + if(!deck) + return count; + for(it=deck->cards.begin();it!=deck->cards.end();it++){ + MTGCard * c = deck->getCardById(it->first); + if(c && (matchesFilters(c))){ + cards.push_back(c); + count++; + } + } + validateFilters(); + return count; +} +int WSrcCards::loadMatches(WSrcCards* src, bool all){ + int count = 0; + if(!src) + return count; + + MTGCard * c = NULL; + size_t t = 0; + int oldp = src->getOffset(); + src->setOffset(0); + for(int t=0;tSize(all);t++){ + c = src->getCard(t,all); + if(matchesFilters(c)){ + cards.push_back(c); + count++; + } + } + src->setOffset(oldp); + validateFilters(); + return count; +} +int WSrcCards::addToDeck(MTGDeck * i, int num){ + int oldpos = getOffset(); + int added = 0; + int cycles = 0; + + if(!i){ + if(num < 0) + return 0; + return num; + } + + setOffset(0); + if(num < 0){ //Add it all; + MTGCard * c; + for(;;){ + c = getCard(); + if(!c || !next()) + break; + i->add(c); + } + }else while(added < num){ + MTGCard * c = getCard(); + if(!next() || !c){ + if(++cycles == WSrcCards::MAX_CYCLES){ //Abort the search, too many cycles. + setOffset(oldpos); + return num - added; + } + setOffset(0); + continue; + }else{ + i->add(c); + added++; + } + } + setOffset(oldpos); + return 0; +} +bool WSrcCards::next(){ + int oldpos = currentPos; + bool bMatch = true; + int size = (int)cards.size(); + if(filtersRoot) + size = (int)validated.size(); + if(currentPos+1 >= size) + return false; + currentPos++; + return true; +} + +bool WSrcCards::prev(){ + int oldpos = currentPos; + bool bMatch = true; + if(currentPos == 0) + return false; + + currentPos--; + return true; +} + +bool WSrcCards::setOffset(int pos){ + if(pos < 0 || pos >= (int) cards.size()) + return false; + + currentPos = pos; + if(!matchesFilters(cards[currentPos])) + return next(); + + return true; +} +void WSrcCards::Shuffle(){ + vector a; + MTGCard * temp; + vector::iterator k; + + while(cards.size()){ + k = cards.begin(); + k += rand() % cards.size(); + temp = *k; + cards.erase(k); + a.push_back(temp); + } +#if defined WIN32 || defined LINUX //PC performs a double shuffle for less streaking. + while(a.size()){ + k = a.begin(); + k += rand() % a.size(); + temp = *k; + a.erase(k); + cards.push_back(temp); + } +#else //PSP does a straight swap for speed. + cards.swap(a); +#endif + validateFilters(); +} +void WSrcCards::validateFilters(){ + validated.clear(); + for(size_t t=0;tgetId() == mtgid){ + currentPos = (int) t; + return matchesFilters(cards[currentPos]); + } + } + return false; +} + +void WSrcCards::addFilter(WCardFilter * f) { + if(filtersRoot == NULL) + filtersRoot = f; + else + filtersRoot = NEW WCFilterAND(f,filtersRoot); + validateFilters(); + currentPos = 0; +} +float WSrcCards::filterFee(){ + if(filtersRoot) + return filtersRoot->filterFee(); + return 0; +}; +void WSrcCards::clearFilters() { + SAFE_DELETE(filtersRoot); + validated.clear(); +} + +void WSrcCards::Sort(int method){ + switch(method){ + case SORT_COLLECTOR: + std::sort(cards.begin(),cards.end(),WCSortCollector()); + break; + case SORT_ALPHA: + default: + std::sort(cards.begin(),cards.end(),WCSortAlpha()); + break; + } + validateFilters(); +} +//WSrcUnlockedCards +WSrcUnlockedCards::WSrcUnlockedCards(float delay): WSrcCards(mDelay){ + MTGAllCards * ac = GameApp::collection; + map::iterator it; + + char * unlocked = NULL; + unlocked = (char *)calloc(setlist.size(),sizeof(char)); + //Figure out which sets are available. + for (int i = 0; i < setlist.size(); i++){ + unlocked[i] = options[Options::optionSet(i)].number; + } + + for(it=ac->collection.begin();it!=ac->collection.end();it++){ + if(it->second && unlocked[it->second->setId]) + cards.push_back(it->second); + } + if(unlocked){ + free(unlocked); + unlocked = NULL; + } + + + if(cards.size()){ + Shuffle(); + currentPos = 0; + } +} + +//WSrcDeck +int WSrcDeck::loadMatches(MTGDeck * deck){ + map::iterator it; + int count = 0; + if(!deck) + return count; + for(it=deck->cards.begin();it!=deck->cards.end();it++){ + MTGCard * c = deck->getCardById(it->first); + if(c && matchesFilters(c)){ + Add(c,it->second); + count++; + } + } + validateFilters(); + return count; +} + +int WSrcDeck::Add(MTGCard * c, int quantity){ + if(!c) + return 0; + if(copies.find(c->getMTGId()) == copies.end()) + cards.push_back(c); //FIXME Make sure these two stay synced. + copies[c->getMTGId()] += quantity; + totalCards += quantity; + return 1; +} + +int WSrcDeck::Remove(MTGCard * c, int quantity, bool erase){ + if(!c) return 0; + map::iterator it = copies.find(c->getMTGId()); + if(it == copies.end()) return 0; + int amt = it->second; + if(amt < quantity) + return 0; + amt -= quantity; + it->second = amt; + if(erase && amt == 0){ + copies.erase(it); + vector::iterator i = find(cards.begin(),cards.end(),c); + if(i != cards.end()) + cards.erase(i); + } + totalCards -= quantity; + return 1; +} + +void WSrcDeck::Rebuild(MTGDeck * d){ + d->removeAll(); + map::iterator it; + for ( it=copies.begin() ; it != copies.end(); it++ ){ + for (int i = 0; i < it->second; i++){ + d->add(it->first); + } + } +} + +int WSrcDeck::count(MTGCard * c){ + if(copies.find(c->getMTGId()) == copies.end()) + return 0; + return copies[c->getMTGId()]; +} + +int WSrcDeck::countByName(MTGCard * card, bool editions){ + string name = card->data->getLCName(); + int total = 0; + vector::iterator it; + for(it = cards.begin();it!=cards.end();it++){ + if(*it && (*it)->data->getLCName() == card->data->getLCName()){ + if(editions) + total++; + else + total += copies[card->getMTGId()]; + } + } + return total; +} + +int WSrcDeck::totalPrice(){ + int total = 0; + PriceList * pricelist = NEW PriceList(RESPATH"/settings/prices.dat",GameApp::collection); + map::iterator it; + for ( it=copies.begin() ; it != copies.end(); it++ ){ + int nb = it->second; + if (nb) total += pricelist->getPrice(it->first); + } + delete pricelist; + return total; +} + +int WSrcDeck::totalCopies(){ + return totalCards; +} +//Sorting methods: + +bool WCSortAlpha::operator()(const MTGCard*l, const MTGCard*r){ + if(!l || !r || !l->data || !r->data) + return false; + string ln = l->data->getLCName(); + string rn = r->data->getLCName(); + return (ln < rn); +} +bool WCSortCollector::operator()(const MTGCard*l, const MTGCard*r){ + if(!l || !r || !l->data || !r->data) + return false; + + if(l->setId != r->setId) + return (l->setId < r->setId); + + int lc, rc; + lc = l->data->countColors(); rc = r->data->countColors(); + if(lc == 0) lc = 999; + if(rc == 0) rc = 999; + + int isW = (int)l->data->hasColor(Constants::MTG_COLOR_WHITE) - (int) r->data->hasColor(Constants::MTG_COLOR_WHITE); + int isU = (int)l->data->hasColor(Constants::MTG_COLOR_BLUE) - (int) r->data->hasColor(Constants::MTG_COLOR_BLUE); + int isB = (int)l->data->hasColor(Constants::MTG_COLOR_BLACK) - (int) r->data->hasColor(Constants::MTG_COLOR_BLACK); + int isR = (int)l->data->hasColor(Constants::MTG_COLOR_RED) - (int) r->data->hasColor(Constants::MTG_COLOR_RED); + int isG = (int)l->data->hasColor(Constants::MTG_COLOR_GREEN) - (int) r->data->hasColor(Constants::MTG_COLOR_GREEN); + int isArt = (int)l->data->hasType(Subtypes::TYPE_ARTIFACT) - (int) r->data->hasType(Subtypes::TYPE_ARTIFACT); + int isLand = (int)l->data->hasType(Subtypes::TYPE_LAND) - (int) r->data->hasType(Subtypes::TYPE_LAND); + + //Nested if hell. TODO: Farm these out to their own objects as a user-defined filter/sort system. + if(!isLand){ + int isBasic = (int)l->data->hasType("Basic") - (int) r->data->hasType("Basic"); + if(!isBasic){ + if(!isArt){ + if(lc == rc){ + if(!isG){ + if(!isR){ + if(!isB){ + if(!isU){ + if(!isW){ + string ln = l->data->getLCName(); + string rn = r->data->getLCName(); + if(ln.substr(0,4) == "the ") + ln = ln.substr(4); + if(rn.substr(0,4) == "the ") + rn = rn.substr(4); + return (ln < rn); + } + return (isW < 0); + } + return (isU < 0); + } + return (isB < 0); + } + return (isR < 0); + } + return (isG < 0); + } + return (lc < rc); + } + return (isArt < 0); + } + else return(isBasic < 0); + } + return (isLand < 0); +} \ No newline at end of file diff --git a/projects/mtg/src/WFilter.cpp b/projects/mtg/src/WFilter.cpp new file mode 100644 index 000000000..9d01e230f --- /dev/null +++ b/projects/mtg/src/WFilter.cpp @@ -0,0 +1,329 @@ +#include "../include/config.h" +#include "../include/OptionItem.h" +#include "../include/PlayerData.h" +#include "../include/Translate.h" +#include + + +//WCFilterFactory +WCFilterFactory* WCFilterFactory::me = NULL; + +WCFilterFactory* WCFilterFactory::GetInstance() { + if(!me) + me = NEW WCFilterFactory(); + return me; +} +void WCFilterFactory::Destroy(){ + SAFE_DELETE(me); +} +size_t WCFilterFactory::findNext(string src, size_t start,char open, char close){ + int num = 0; + for(size_t x=start;xdata) + return false; + return (c->data->hasColor(color) > 0); +} +string WCFilterColor::getCode(){ + char buf[12]; + char c = '?'; + if(color < 0 || color >= Constants::MTG_NB_COLORS) + c = Constants::MTGColorChars[color]; + sprintf(buf,"color:%c;",c); + return buf; +}; +WCFilterColor::WCFilterColor(string arg){ + color = -1; + char c = tolower(arg[0]); + for(int i=0;idata) + return false; + return (c->getRarity() == rarity); +} +string WCFilterRarity::getCode(){ + char buf[64]; + const char* rarities[7] = {"any","token","land","common","uncommon","rare","mythic"}; + int x = 0; + switch(rarity){ + case 'M': x=6; break; + case 'R': x=5; break; + case 'U': x=4; break; + case 'C': x=3; break; + case 'L': x=2; break; + case 'T': x=1; break; + } + sprintf(buf,"rarity:%s;",rarities[x]); + return buf; +}; +WCFilterRarity::WCFilterRarity(string arg){ + rarity = -1; + char c = toupper(arg[0]); + switch(c){ + case 'M': + case 'R': + case 'U': + case 'C': + case 'L': + case 'T': + rarity = c; + break; + } +} +//WCFilterAbility +bool WCFilterAbility::isMatch(MTGCard * c){ + if(ability < 0) + return false; + map::iterator it = c->data->basicAbilities.find(ability); + + if(it != c->data->basicAbilities.end()){ + if(it->second > 0) + return true; + } + return false; +} +WCFilterAbility::WCFilterAbility(string arg){ + std::transform(arg.begin(),arg.end(),arg.begin(),::tolower); + for(int i = 0;i= Constants::NB_BASIC_ABILITIES) + return ""; + sprintf(buf,"ability:%s;",Constants::MTGBasicAbilities[ability]); + return buf; +}; +float WCFilterAbility::filterFee(){ + switch(ability){ + case Constants::SHROUD: + case Constants::DEATHTOUCH: + case Constants::UNBLOCKABLE: + case Constants::WITHER: + case Constants::PERSIST: + return 0.4f; + case Constants::PROTECTIONBLACK: + case Constants::PROTECTIONWHITE: + case Constants::PROTECTIONBLUE: + case Constants::PROTECTIONRED: + case Constants::PROTECTIONGREEN: + case Constants::DOUBLESTRIKE: + case Constants::LIFELINK: + return 0.35f; + case Constants::TRAMPLE: + case Constants::FLYING: + case Constants::FEAR: + case Constants::VIGILANCE: + case Constants::FIRSTSTRIKE: + return 0.3f; + case Constants::PLAINSHOME: + case Constants::SWAMPHOME: + case Constants::ISLANDHOME: + case Constants::MOUNTAINHOME: + case Constants::FORESTHOME: + return -0.1f; + case Constants::DEFENDER: + case Constants::CLOUD: + return 0.1f; + default: + return 0.2f; + } + return 0.0f; +} +//WCFilterType +bool WCFilterType::isMatch(MTGCard * c){ + return c->data->hasType(type.c_str()); +} +string WCFilterType::getCode(){ + char buf[4068]; + sprintf(buf,"type:%s;",type.c_str()); + return buf; +} +//Misc. filter code +float WCFilterAND::filterFee(){ + return lhs->filterFee() + rhs->filterFee(); +} +float WCFilterOR::filterFee(){ + if(lhs->filterFee() > rhs->filterFee()) + return lhs->filterFee(); + return rhs->filterFee(); +} +string WCFilterNOT::getCode(){ + char buf[4068]; + sprintf(buf,"{%s}",kid->getCode().c_str()); + return buf; +} +string WCFilterGROUP::getCode(){ + char buf[4068]; + sprintf(buf,"(%s)",kid->getCode().c_str()); + return buf; +} + +string WCFilterAND::getCode(){ + char buf[4068]; + sprintf(buf,"%s&%s",lhs->getCode().c_str(),rhs->getCode().c_str()); + return buf; +} +string WCFilterOR::getCode(){ + char buf[4068]; + sprintf(buf,"%s|%s",lhs->getCode().c_str(),rhs->getCode().c_str()); + return buf; +} + + +bool WCFilterOR::isMatch(MTGCard *c){ + if(lhs->isMatch(c)) + return true; + if(rhs->isMatch(c)) + return true; + return false; +}; \ No newline at end of file diff --git a/projects/mtg/src/WGui.cpp b/projects/mtg/src/WGui.cpp new file mode 100644 index 000000000..cee3cb5f2 --- /dev/null +++ b/projects/mtg/src/WGui.cpp @@ -0,0 +1,1624 @@ +#include "../include/config.h" +#include "../include/OptionItem.h" +#include "../include/PlayerData.h" +#include "../include/Translate.h" +#include +#include + +//WGuiBase +PIXEL_TYPE WGuiBase::getColor(int type){ + switch(type){ + case WGuiColor::TEXT_BODY: + case WGuiColor::SCROLLBUTTON: + return ARGB(255,255,255,255); + case WGuiColor::SCROLLBAR: + return ARGB(150,50,50,50); + case WGuiColor::BACK_HEADER: + return ARGB(150,80,80,80); + default: + if(type < WGuiColor::BACK){ + if(hasFocus()) + return ARGB(255,255,255,0); + else + return ARGB(255,255,255,255); + } + else + return ARGB(150,50,50,50); + } + return ARGB(150,50,50,50); +} + + +void WGuiBase::renderBack(WGuiBase * it){ + if(!it) return; + WDecoStyled * styled = dynamic_cast(it); + if(styled) + styled->renderBack(styled->getDecorated()); + else + subBack(it); +} + + +//WGuiItem +void WGuiItem::Entering(u32 key){ + mFocus = true; +} +float WGuiItem::minWidth(){ + JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); + return mFont->GetStringWidth(_(displayValue).c_str())+4; +} +float WGuiItem::minHeight(){ + JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); + return mFont->GetHeight(); +} + +bool WGuiItem::Leaving(u32 key){ + mFocus = false; + return true; +} + +void WGuiItem::Render(){ + JRenderer * renderer = JRenderer::GetInstance(); + JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); + DWORD oldcolor = mFont->GetColor(); + mFont->SetColor(getColor(WGuiColor::TEXT)); + float fH = (height-mFont->GetHeight())/2; + string trans = _(displayValue); + float fW = mFont->GetStringWidth(trans.c_str()); + float boxW = getWidth(); + float oldS = mFont->GetScale(); + if(fW > boxW){ + mFont->SetScale(boxW/fW); + } + mFont->DrawString(trans,x+(width/2),y+fH,JGETEXT_CENTER); + mFont->SetScale(oldS); + mFont->SetColor(oldcolor); +} + +WGuiItem::WGuiItem(string _display, u8 _mF){ + mFlags=_mF; + displayValue = _display; + mFocus = false; + width=SCREEN_WIDTH; + height=20; + x=0; + y=0; +} + +string WGuiItem::_(string input){ + if(mFlags & WGuiItem::NO_TRANSLATE) + return input; + return ::_(input); +} +bool WGuiItem::CheckUserInput(u32 key){ + if(mFocus && key == PSP_CTRL_CIRCLE){ + updateValue(); + return true; + } + return false; +} + +//WDecoStyled +void WDecoStyled::subBack(WGuiBase * item){ + if(!item) + return; + JRenderer * renderer = JRenderer::GetInstance(); + if(mStyle & DS_STYLE_BACKLESS) + return; + //TODO: if(mStyle & DS_STYLE_EDGED) Draw the edged box ala SimpleMenu + else{ //Draw standard style + WGuiSplit * split = dynamic_cast(item); + if(split && split->left->Visible() && split->right->Visible()){ + if(split->left) + renderer->FillRoundRect(split->left->getX()-2,split->getY()-2,split->left->getWidth()-6,split->getHeight(),2,split->left->getColor(WGuiColor::BACK)); + if(split->right) + renderer->FillRoundRect(split->right->getX()-2,split->getY()-2,split->right->getWidth(),split->getHeight(),2,split->right->getColor(WGuiColor::BACK)); + } + else{ + renderer->FillRoundRect(item->getX()-2,item->getY()-2,item->getWidth(),item->getHeight(),2,getColor(WGuiColor::BACK)); + } + } + +} + +PIXEL_TYPE WDecoStyled::getColor(int type){ + switch(type){ + case WGuiColor::BACK: + case WGuiColor::BACK_HEADER: + if(mStyle & DS_COLOR_DARK) + return ARGB(150,35,35,35); + else if(mStyle & DS_COLOR_BRIGHT) + return ARGB(150,80,80,80); + else if(mStyle & DS_STYLE_ALERT) + return ARGB(150,120,80,80); + else + return ARGB(150,50,50,50); + default: + return WGuiBase::getColor(type); + } + return ARGB(150,50,50,50); +} + +//WGuiHeader +void WGuiHeader::Render(){ + JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); + mFont->SetColor(getColor(WGuiColor::TEXT)); + + JRenderer * renderer = JRenderer::GetInstance(); + mFont->DrawString(_(displayValue).c_str(),x+width/2,y,JGETEXT_CENTER); +} + + +bool WGuiMenu::Leaving(u32 key){ + int nbitems = (int) items.size(); + if(key == buttonNext && currentItem < nbitems-1) + return false; + else if(key == buttonPrev && currentItem > 0) + return false; + + if(currentItem >= 0 && currentItem < nbitems) + if(!items[currentItem]->Leaving(key)) + return false; + + mFocus = false; + return true; +} +void WGuiMenu::Entering(u32 key){ + mFocus = true; + + //Try to force a selectable option. + if(currentItem == -1){ + for (size_t i = 0 ; i < items.size(); i++){ + if(items[i]->Selectable()) { + currentItem = i; + break; + } + } + } + + if(currentItem >= 0 && currentItem < (int) items.size()) + items[currentItem]->Entering(key); + return; +} + +void WGuiMenu::subBack(WGuiBase * item){ + if(!item) + return; + JRenderer * renderer = JRenderer::GetInstance(); + + WGuiSplit * split = dynamic_cast(item); + if(split && split->left->Visible() && split->right->Visible()){ + if(split->left) + subBack(split->left);//renderer->FillRoundRect(split->left->getX()-2,split->getY()-2,split->left->getWidth()-6,split->getHeight(),2,split->left->getColor(WGuiColor::BACK)); + if(split->right) + subBack(split->right);//renderer->FillRoundRect(split->right->getX()-2,split->getY()-2,split->right->getWidth(),split->getHeight(),2,split->right->getColor(WGuiColor::BACK)); + } + else + renderer->FillRoundRect(item->getX(),item->getY(),item->getWidth()-4,item->getHeight()-2,2,item->getColor(WGuiColor::BACK)); + +} +//WGuiList +WGuiList::WGuiList(string name, WSyncable * syncme): WGuiMenu(PSP_CTRL_DOWN,PSP_CTRL_UP,false,syncme){ + failMsg = "NO OPTIONS AVAILABLE"; + width = SCREEN_WIDTH-10; + height = SCREEN_HEIGHT-10; + y = 5; + x = 5; + mFocus = false; + sync = syncme; + displayValue = name; +} +void WGuiList::confirmChange(bool confirmed){ + for(size_t x=0;xconfirmChange(confirmed); + } +} +void WGuiList::Render(){ + JRenderer * renderer = JRenderer::GetInstance(); + int listHeight=40; + int listSelectable=0; + int adjustedCurrent=0; + int start = 0, nowPos = 0, vHeight=0; + int nbitems = (int) items.size(); + + //List is empty. + if (!items.size() && failMsg != ""){ + JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); + mFont->SetColor(getColor(WGuiColor::TEXT_FAIL)); + mFont->DrawString(_(failMsg).c_str(),x+width/2, y, JGETEXT_RIGHT); + return; + } + + //Force a selectable option. + if(currentItem == -1){ + for (int i = 0 ; i < nbitems; i++){ + if(items[i]->Selectable()) { + currentItem = i; + if(hasFocus()) + items[currentItem]->Entering(0); + break; + } + } + } + //Find out how large our list is, with all items and margin. + for (int pos=0;pos < nbitems; pos++){ + listHeight+=items[pos]->getHeight()+1; //What does the +1 do exactly ? + if(items[pos]->Selectable()){ + listSelectable++; + if(pos < currentItem) adjustedCurrent++; + } + } + + //Always fill screen + if(listHeight > SCREEN_HEIGHT) + { + for (start=currentItem;start > 0; start--){ + if(!items[start]->Visible()) + continue; + + vHeight += items[start]->getHeight()+5; + if(vHeight >= (SCREEN_HEIGHT-60)/2) + break; + } + vHeight = 0; + if(start >= 0) + for (nowPos=nbitems;nowPos > 1; nowPos--){ + if(!items[start]->Visible()) + continue; + vHeight += items[nowPos-1]->getHeight()+5; + } + + if(vHeight <= SCREEN_HEIGHT-40 && nowPos < start) + start = nowPos; + } + + vHeight = 0; + nowPos = 0; + + //Render items. + if(start >= 0) + { + //Render current underlay. + if(currentItem >= 0 && currentItem < nbitems && items[currentItem]->Visible()) + items[currentItem]->Underlay(); + + for (int pos=0;pos < nbitems; pos++){ + if(!items[pos]->Visible()) + continue; + + if(pos < start){ + vHeight += items[pos]->getHeight() + 5; + continue; + } + + items[pos]->setY(y+nowPos); + items[pos]->setX(x); + if(listHeight > SCREEN_HEIGHT && listSelectable > 1) + items[pos]->setWidth(width-10); + else + items[pos]->setWidth(width); + nowPos += items[pos]->getHeight() + 5; + renderBack(items[pos]); + items[pos]->Render(); + if(nowPos > SCREEN_HEIGHT) //Stop displaying things once we reach the bottom of the screen. + break; + } + + //Draw scrollbar + if(listHeight > SCREEN_HEIGHT && listSelectable > 1){ + int barPosition = y-5+((float)adjustedCurrent/listSelectable)*(SCREEN_HEIGHT-y); + int barLength = (SCREEN_HEIGHT-y) / listSelectable; + if(barLength < 4) barLength = 4; + renderer->FillRect(x+width-2,y-1,2,SCREEN_HEIGHT-y, + getColor(WGuiColor::SCROLLBAR)); + renderer->FillRoundRect(x+width-5,barPosition,5,barLength,2, + getColor(WGuiColor::SCROLLBUTTON)); + } + + //Render current overlay. + if(currentItem >= 0 && currentItem < nbitems && items[currentItem]->Visible()) + items[currentItem]->Overlay(); + } +} + +void WGuiList::setData(){ + for (size_t i = 0; i < items.size(); i++){ + items[i]->setData(); + } +} + +void WGuiList::ButtonPressed(int controllerId, int controlId){ + WGuiBase * it; + + if(!(it = Current())) + return; + + it->ButtonPressed(controllerId,controlId); +} + + +string WDecoEnum::lookupVal(int value){ + + if(edef == NULL){ + int id = getId(); + if(id != INVALID_ID){ + GameOptionEnum * goEnum = dynamic_cast(options.get(getId())); + if(goEnum) + edef = goEnum->def; + } + } + + if(edef){ + int idx = edef->findIndex(value); + if(idx != INVALID_ID) + return edef->values[idx].second; + } + + char buf[32]; + sprintf(buf,"%d",value); + return buf; +} + +void WDecoEnum::Render() +{ + JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); + mFont->SetColor(getColor(WGuiColor::TEXT)); + JRenderer * renderer = JRenderer::GetInstance(); + mFont->DrawString(_(getDisplay()).c_str(),getX(),getY()); + OptionInteger* opt = dynamic_cast(it); + if(opt) + mFont->DrawString(_(lookupVal(opt->value)).c_str(), getWidth() -10, getY(), JGETEXT_RIGHT); +} + +WDecoEnum::WDecoEnum(WGuiBase * _it, EnumDefinition *_edef) : WGuiDeco(_it) {edef = _edef;} +//WDecoCheat +WDecoCheat::WDecoCheat(WGuiBase * _it): WGuiDeco(_it){ + bVisible = (options[Options::ACTIVE_PROFILE].str == SECRET_PROFILE); +} +void WDecoCheat::Reload(){ + bVisible = (options[Options::ACTIVE_PROFILE].str == SECRET_PROFILE); +} +bool WDecoCheat::Visible(){ + if(bVisible && it && it->Visible()) + return true; + return false; +} +bool WDecoCheat::Selectable(){ + if(!it || !Visible()) + return false; + return it->Selectable(); +} +//WDecoConfirm + +WDecoConfirm::WDecoConfirm(JGuiListener * _listener, WGuiBase * _it): WGuiDeco(_it){ + listener = _listener; + confirm = "Confirm"; + cancel = "Cancel"; + confirmMenu = NULL; + bModal = false; + mState = OP_CONFIRMED; +} + +WDecoConfirm::~WDecoConfirm(){ + SAFE_DELETE(confirmMenu); +} + +void WDecoConfirm::Entering(u32 key){ + setFocus(true); + + if(it) + it->Entering(key); + + SAFE_DELETE(confirmMenu); + mState = OP_CONFIRMED; + confirmMenu = NEW SimpleMenu(444, listener,Constants::MENU_FONT, 50,170); + confirmMenu->Add(1,confirm.c_str()); + confirmMenu->Add(2,cancel.c_str()); +} + +bool WDecoConfirm::isModal(){ + if(bModal || (it && it->isModal())) + return true; + + return false; +} + +void WDecoConfirm::setModal(bool val){ + bModal = val; +} +void WDecoConfirm::setData(){ + if(!it) + return; + + it->setData(); +} + +bool WDecoConfirm::Leaving(u32 key){ + if(!it) + return true; + + //Choice must be confirmed. + if(mState == OP_UNCONFIRMED){ + if(!isModal()) + setModal(true); + if(!it->Changed()) + mState = OP_CONFIRMED; + else + mState = OP_CONFIRMING; + } + + if(mState == OP_CONFIRMED && it->Leaving(key)){ + setFocus(false); + setModal(false); + SAFE_DELETE(confirmMenu); + return true; + } + + return false; +} +bool WDecoConfirm::CheckUserInput(u32 key){ + if(hasFocus()){ + if (mState == OP_CONFIRMED && key == PSP_CTRL_CIRCLE) + mState = OP_UNCONFIRMED; + + if (mState != OP_CONFIRMING && it){ + if(it->CheckUserInput(key)) + return true; + } else if(confirmMenu && confirmMenu->CheckUserInput(key)) + return true; + } + return false; +} + +void WDecoConfirm::Update(float dt){ + if (hasFocus()){ + if (it && mState != OP_CONFIRMING) + it->Update(dt); + else + confirmMenu->Update(dt); + } +} + +void WDecoConfirm::Overlay(){ + if (confirmMenu && mState == OP_CONFIRMING) + confirmMenu->Render(); + + if(it) + it->Overlay(); +} + +void WDecoConfirm::ButtonPressed(int controllerId, int controlId){ + if(controllerId == 444){ + setModal(false); + switch(controlId){ + case 1: + mState = OP_CONFIRMED; + if(it) + it->confirmChange(true); + break; + case 2: + mState = OP_CONFIRMED; + if(it) + it->confirmChange(false); + break; + } + } + else + it->ButtonPressed(controllerId,controlId); +} + +WGuiButton::WGuiButton( WGuiBase* _it, int _controller, int _control, JGuiListener * jgl): WGuiDeco(_it) { + control = _control; + controller = _controller; + mListener = jgl; +} + +void WGuiButton::updateValue(){ + if(mListener) + mListener->ButtonPressed(controller, control); +} + +bool WGuiButton::CheckUserInput(u32 key){ + if (hasFocus() && key == PSP_CTRL_CIRCLE){ + updateValue(); + return true; + } + return false; +} + +PIXEL_TYPE WGuiButton::getColor(int type){ + if(type == WGuiColor::BACK && hasFocus()) + return it->getColor(WGuiColor::BACK_HEADER); + return it->getColor(type); +}; + +WGuiSplit::WGuiSplit(WGuiBase* _left, WGuiBase* _right) : WGuiItem("") { + right = _right; + left = _left; + bRight = false; + percentRight = 0.5f; + if(!left->Selectable()) + bRight = true; +} +WGuiSplit::~WGuiSplit(){ + SAFE_DELETE(left); + SAFE_DELETE(right); +} + +void WGuiSplit::setData(){ + left->setData(); + right->setData(); +} +void WGuiSplit::setX(float _x){ + x = _x; + left->setX(x); + right->setX(x+(1-percentRight)*width); +} +void WGuiSplit::setY(float _y){ + y = _y; + left->setY(y); + right->setY(y); +} +void WGuiSplit::setWidth(float _w){ + width = _w; + if(right->Visible()) + left->setWidth((1-percentRight)*width); + else + left->setWidth(width); + + right->setWidth(percentRight*width); +} +void WGuiSplit::setHeight(float _h){ + left->setHeight(_h); + right->setHeight(_h); + height = _h; +} +float WGuiSplit::getHeight(){ + float lH, rH; + lH = left->getHeight(); + rH = right->getHeight(); + if(lH > rH) + return lH; + + return rH; +} + +void WGuiSplit::Render(){ + if(right->Visible()) + right->Render(); + if(left->Visible()) + left->Render(); +} + +bool WGuiSplit::isModal(){ + if(bRight) + return right->isModal(); + + return left->isModal(); +} +void WGuiSplit::setModal(bool val){ + if(bRight) + return right->setModal(val); + + return left->setModal(val); +} + +bool WGuiSplit::CheckUserInput(u32 key){ + if(hasFocus()){ + if (!bRight){ + if(left->CheckUserInput(key)) + return true; + if(key == PSP_CTRL_RIGHT && !isModal() + && right->Selectable() && left->Leaving(PSP_CTRL_RIGHT)){ + bRight = !bRight; + right->Entering(PSP_CTRL_RIGHT); + return true; + } + } + else + { + if(right->CheckUserInput(key)) + return true; + if (key == PSP_CTRL_LEFT && !isModal() + && left->Selectable() && right->Leaving(PSP_CTRL_LEFT)){ + bRight = !bRight; + left->Entering(PSP_CTRL_LEFT); + return true; + } + } + } + + return false; +} + +void WGuiSplit::Update(float dt){ + if(bRight) + right->Update(dt); + else + left->Update(dt); +} + +void WGuiSplit::Entering(u32 key){ + mFocus = true; + if(bRight) + right->Entering(key); + else + left->Entering(key); +} +bool WGuiSplit::Leaving(u32 key){ + + if(bRight){ + if(right->Leaving(key)){ + mFocus = false; + return true; + } + } + else{ + if(left->Leaving(key)){ + mFocus = false; + return true; + } + } + + return false; +} +void WGuiSplit::Overlay(){ + if(bRight) + right->Overlay(); + else + left->Overlay(); +} +void WGuiSplit::Underlay(){ + if(bRight) + right->Underlay(); + else + left->Underlay(); +} +void WGuiSplit::ButtonPressed(int controllerId, int controlId) +{ + if(bRight) + right->ButtonPressed(controllerId, controlId); + else + left->ButtonPressed(controllerId, controlId); +} +void WGuiSplit::Reload(){ + left->Reload(); + right->Reload(); +} +void WGuiSplit::confirmChange(bool confirmed){ + right->confirmChange(confirmed); + left->confirmChange(confirmed); +} + +//WGuiMenu +WGuiMenu::WGuiMenu(u32 next = PSP_CTRL_RIGHT, u32 prev = PSP_CTRL_LEFT, bool m, WSyncable * syncme): WGuiItem(""){ + buttonNext = next; + buttonPrev = prev; + currentItem = -1; + mDPad = m; + sync = syncme; +} +WGuiMenu::~WGuiMenu(){ + for(vector::iterator it = items.begin();it!=items.end();it++) + SAFE_DELETE(*it); +} +void WGuiMenu::setData(){ + for(vector::iterator it = items.begin();it!=items.end();it++) + (*it)->setData(); +}; + +void WGuiMenu::Reload(){ + for(vector::iterator it = items.begin();it!=items.end();it++) + (*it)->Reload(); +}; + +void WGuiMenu::Render(){ + for(vector::iterator it = items.begin();it!=items.end();it++) + (*it)->Render(); +} +void WGuiMenu::confirmChange(bool confirmed){ + for(vector::iterator it = items.begin();it!=items.end();it++) + (*it)->confirmChange(confirmed); +} + +void WGuiMenu::ButtonPressed(int controllerId, int controlId){ + WGuiBase * it = Current(); + if(!it) return; + it->ButtonPressed(controllerId,controlId); +} + +WGuiBase * WGuiMenu::Current(){ + if(currentItem >= 0 && currentItem < (int) items.size()) + return items[currentItem]; + return NULL; +} +void WGuiMenu::Add(WGuiBase * it){ + if(it) + items.push_back(it); +} +bool WGuiMenu::CheckUserInput(u32 key){ + bool kidModal = false; + bool handledInput = false; + int nbitems = (int) items.size(); + JGE * mEngine = JGE::GetInstance(); + + if(!mEngine->GetButtonState(held)) //Key isn't held down. + held = 0; + + if(currentItem >= 0 && currentItem < nbitems) + kidModal = items[currentItem]->isModal(); + + if(!kidModal && hasFocus()){ + if (isButtonDir(key,-1)){ + held = buttonPrev; + duration = 0; + if(prevItem()) + return true; + } + else if(held == buttonPrev && duration > 1){ + duration = .92; + if(prevItem()) + return true; + } + else if (isButtonDir(key,1)){ + held = buttonNext; + duration = 0; + if(nextItem()) + return true; + } + else if(held == buttonNext && duration > 1){ + duration = .92; + if(nextItem()) + return true; + } + } + + if(currentItem >= 0 && currentItem < nbitems) + return items[currentItem]->CheckUserInput(key); + + return false; +} +void WGuiMenu::syncMove(){ + if(!sync) + return; + int i = currentItem - sync->getPos(); + + while(i < 0 && sync->prev()) + i = currentItem - sync->getPos(); + while(i > 0 && sync->next()) + i = currentItem - sync->getPos(); +} +bool WGuiMenu::isButtonDir(u32 key, int dir){ + if(!mDPad) + return ((dir > 0 && key == buttonNext) || (dir <= 0 && key == buttonPrev)); + + if(dir <= 0){ + switch(buttonPrev){ + case PSP_CTRL_LEFT: + if(key == PSP_CTRL_UP) + return true; + break; + case PSP_CTRL_UP: + if(key == PSP_CTRL_LEFT) + return true; + break; + } + return (key == buttonPrev); + }else { + switch(buttonNext){ + case PSP_CTRL_RIGHT: + if(key == PSP_CTRL_DOWN) + return true; + break; + case PSP_CTRL_DOWN: + if(key == PSP_CTRL_RIGHT) + return true; + break; + } + return (key == buttonNext); + } +} +void WGuiMenu::Update(float dt){ + int nbitems = (int) items.size(); + JGE * mEngine = JGE::GetInstance(); + + if(held) + duration += dt; + + if(currentItem >= 0 && currentItem < nbitems) + items[currentItem]->Update(dt); + + for (int i = 0 ; i < nbitems; i++){ + if(i != currentItem) + items[i]->Update(dt); + } +} + +bool WGuiMenu::nextItem(){ + int potential = currentItem; + int nbitems = (int) items.size(); + WGuiBase * now = NULL; + if(currentItem < nbitems && currentItem > -1) + now = items[currentItem]; + + if (potential < nbitems-1){ + potential++; + while(potential < nbitems-1 && items[potential]->Selectable() == false) + potential++; + if(potential == nbitems || !items[potential]->Selectable()) + potential = -1; + else if(potential != currentItem && (!now || now->Leaving(buttonNext))){ + currentItem = potential; + items[currentItem]->Entering(buttonNext); + + if(sync) + syncMove(); + return true; + } + } + if(sync) + syncMove(); + return false; +} + +bool WGuiMenu::prevItem(){ + int potential = currentItem; + WGuiBase * now = NULL; + if(currentItem < (int)items.size() && currentItem > -1) + now = items[currentItem]; + + if (potential > 0){ + potential--; + while(potential > 0 && items[potential]->Selectable() == false) + potential--; + if(potential < 0 || !items[potential]->Selectable()) + potential = -1; + else if(potential != currentItem && (!now || now->Leaving(buttonNext))){ + currentItem = potential; + items[currentItem]->Entering(buttonPrev); + if(sync) + syncMove(); + return true; + } + } + if(sync) + syncMove(); + return false; +} + +void WGuiMenu::setModal(bool val){ + WGuiBase* c = Current(); + if(c) + c->setModal(val); +} +bool WGuiMenu::isModal(){ + WGuiBase* c = Current(); + if(c) + return c->isModal(); + + return false; +} +//WGuiTabMenu +void WGuiTabMenu::Add(WGuiBase * it){ + if (it){ + it->setY(it->getY()+35); + it->setHeight(it->getHeight()-35); + WGuiMenu::Add(it); + } +} + +void WGuiTabMenu::Render(){ + JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); + JRenderer * renderer = JRenderer::GetInstance(); + + if (!items.size()) + return; + + int offset = x; + + for(vector::iterator it = items.begin();it!=items.end();it++){ + int w = mFont->GetStringWidth(_((*it)->getDisplay()).c_str()); + mFont->SetColor((*it)->getColor(WGuiColor::TEXT_TAB)); + renderer->FillRoundRect(offset+5,5,w + 5,25,2,(*it)->getColor(WGuiColor::BACK_TAB)); + mFont->DrawString(_((*it)->getDisplay()).c_str(),offset+10,10); + offset += w + 10 + 2; + } + + WGuiBase * c = Current(); + if(c) + c->Render(); +} + +void WGuiTabMenu::save(){ + confirmChange(true); + setData(); + ::options.save(); +} + + +//WGuiAward +void WGuiAward::Overlay(){ + JRenderer * r = JRenderer::GetInstance(); + JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); + mFont->SetScale(.8); + mFont->SetColor(getColor(WGuiColor::TEXT)); + + string s = details; + if(s.size()){ + float fW = mFont->GetStringWidth(s.c_str()); + float fH = mFont->GetHeight(); + + if(fH < 16) + fH = 18; + JQuad * button = resources.RetrieveQuad("iconspsp.png", (float)4*32, 0, 32, 32,"",RETRIEVE_NORMAL); + + r->FillRoundRect(5,10,fW+32,fH+2,2,getColor(WGuiColor::BACK)); + if(button) + r->RenderQuad(button, 10,12,0,.5,.5); + mFont->DrawString(::_(s),30,16); + } + + mFont->SetScale(1); +} +void WGuiAward::Underlay(){ + char buf[1024]; + JRenderer * r = JRenderer::GetInstance(); + JQuad * trophy = NULL; + + string n = Options::getName(id); + if(n.size()){ + sprintf(buf,"trophy_%s.png",n.c_str()); //Trophy specific to the award + trophy = resources.RetrieveTempQuad(buf); //Themed version... + } + + if(!trophy && id >= Options::SET_UNLOCKS){ + trophy = resources.RetrieveTempQuad("trophy_set.png"); //TODO FIXME: Should look in set dir too. + } + + if(!trophy) //Fallback to basic trophy image. + trophy = resources.RetrieveTempQuad("trophy.png"); + + if(trophy){ + r->RenderQuad(trophy, 0, SCREEN_HEIGHT-trophy->mHeight); + } + +} +void WGuiAward::Render(){ + GameOptionAward * goa = dynamic_cast(&options[id]); + + if(!goa) + return; + + JRenderer * renderer = JRenderer::GetInstance(); + JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); + mFont->SetScale(1); + mFont->SetColor(getColor(WGuiColor::TEXT)); + + float myX = x; + float myY = y; + float fH = mFont->GetHeight(); + float fM = fH / 5; //Font Margin is 20% font height + + myX += fM; + renderer->FillRoundRect(x-fM/2,y-fM,getWidth()-fM,fH-fM,fM,getColor(WGuiColor::BACK_TAB)); + mFont->DrawString(::_(displayValue).c_str(),myX,myY,JGETEXT_LEFT); + + myY += fH + 3*fM; + mFont->SetScale(.75); + fH = mFont->GetHeight(); + if(text.size()){ + mFont->DrawString(_(text.c_str()),myX,myY,JGETEXT_LEFT); + myY+=fH + fM; + } + string s = goa->menuStr(); + if(s.size()){ + mFont->DrawString(s.c_str(),myX,myY,JGETEXT_LEFT); + myY+=fH + fM; + } + setHeight(myY-y); + mFont->SetScale(1); +} + +WGuiAward::WGuiAward(int _id, string name, string _text, string _details): WGuiItem(name){ + id = _id; + text = _text; + height = 60; + details = _details; +} +WGuiAward::~WGuiAward(){ + GameOptionAward * goa = dynamic_cast(&options[id]); + if(goa) + goa->setViewed(true); +} +bool WGuiAward::Visible(){ + //WGuiAward is only visible when it's tied to an already acchieved award. + GameOptionAward * goa = dynamic_cast(&options[id]); + if(!goa || !goa->number) + return false; + return true; +} + +//WGuiImage +WGuiImage::WGuiImage(WDataSource * wds, float _w, float _h, int _margin): WGuiItem("") { + imgW = _w; + imgH = _h; + margin = _margin; + source = wds; +} + +void WGuiImage::imageScale(float _w, float _h){ + imgH = _h; + imgW = _w; +} + +float WGuiImage::getHeight(){ + JQuad * q = NULL; + + if(imgH == 0 ){ + if(source && (q = source->getImage())) //Intentional assignment. + return MAX(height,q->mHeight+(2*margin)); + } + + return MAX(height,imgH+(2*margin)); +} + +void WGuiImage::Render(){ + if(!source) + return; + + JRenderer * renderer = JRenderer::GetInstance(); + JQuad * q = source->getImage(); + if(q){ + float xS = 1, yS = 1; + if(imgH != 0 && q->mHeight != 0) + yS = imgH / q->mHeight; + if(imgW != 0 && q->mWidth != 0) + xS = imgW / q->mWidth; + + renderer->RenderQuad(q,x+margin, y+margin,0,xS,yS); + } +} + +WGuiCardImage::WGuiCardImage(WDataSource * wds, bool _thumb) : WGuiImage(wds){ + bThumb = _thumb; +}; +void WGuiCardImage::Render(){ + + JRenderer * renderer = JRenderer::GetInstance(); + + MTGCard * c = NULL; + Pos p(x+margin, y+margin, 1,0,255); + + if(!source || (c = source->getCard(mOffset.getPos())) == NULL){ + JQuad * q; + if(bThumb) + q = resources.GetQuad("back_thumb"); + else + q = resources.GetQuad("back"); + float scale = p.actZ * 257.f / q->mHeight; + renderer->RenderQuad(q,p.x,p.y,0,scale,scale); + }else{ + if(!c) + return; + if(bThumb){ + JQuad * q = NULL; + if(!options[Options::DISABLECARDS].number) + q = source->getThumb(mOffset.getPos()); + if(!q && (q = CardGui::alternateThumbQuad(c)) == NULL) + return; //TODO Some kind of error image. + renderer->RenderQuad(q,p.x,p.y); + } + else{ + JQuad * q = source->getImage(mOffset.getPos()); + if(!q || options[Options::DISABLECARDS].number) + CardGui::alternateRender(c,p); + else + CardGui::RenderBig(c,p); + } + } +} + +//WGuiCardDistort +WGuiCardDistort::WGuiCardDistort(WDataSource * wds, bool _thumb, WDataSource * _distort): WGuiCardImage(wds,_thumb){ + mesh = NEW hgeDistortionMesh(2,2); + distortSrc = NULL; +} +WGuiCardDistort::~WGuiCardDistort(){ + SAFE_DELETE(mesh); +} + +void WGuiCardDistort::Render(){ + JRenderer * renderer = JRenderer::GetInstance(); + JQuad * q = NULL; + + if(distortSrc) { + WDistort * dt = distortSrc->getDistort(mOffset.getPos()); + if(dt) + xy = *dt; + } + + if(!source){ + //Default to back. + if(bThumb) + q = resources.GetQuad("back_thumb"); + else + q = resources.GetQuad("back"); + }else { + MTGCard * c = source->getCard(mOffset.getPos()); + if(!c) + return; + + if(bThumb){ + q = source->getThumb(mOffset.getPos()); + if(!q || options[Options::DISABLECARDS].number) + q = CardGui::alternateThumbQuad(c); + } + else { + q = source->getImage(mOffset.getPos()); + if(!q || options[Options::DISABLECARDS].number) + q = CardGui::alternateThumbQuad(c); //TODO alternateX should render to texture. + } + } + if(!q) + return; + mesh->SetTexture(q->mTex); + float x0,y0,w0,h0; + q->GetTextureRect(&x0,&y0,&w0,&h0); + mesh->SetTextureRect(x0,y0,w0,h0); + mesh->Clear(ARGB(0xFF,0xFF,0xFF,0xFF)); + mesh->SetDisplacement(0, 0, xy[0],xy[1], HGEDISP_NODE); + mesh->SetDisplacement(1, 0, xy[2] - w0,xy[3], HGEDISP_NODE); + mesh->SetDisplacement(0, 1,xy[4],xy[5]-h0, HGEDISP_NODE); + mesh->SetDisplacement(1, 1, xy[6]-w0,xy[7]-h0, HGEDISP_NODE); + if(hasFocus()){ + mesh->SetColor(1,1,ARGB(255,200,200,200)); + mesh->SetColor(0,1,ARGB(255,200,200,200)); + mesh->SetColor(1,0,ARGB(255,200,200,200)); + mesh->SetColor(0,0,ARGB(255,255,255,200)); + }else{ + mesh->SetColor(1,1,ARGB(255,100,100,100)); + mesh->SetColor(0,1,ARGB(255,100,100,100)); + mesh->SetColor(1,0,ARGB(255,100,100,100)); + mesh->SetColor(0,0,ARGB(255,200,200,200)); + } + mesh->Render(0,0); + +} + +//WDistort +WDistort::WDistort(){ + for(int i=0;i<8;i++) + xy[i] = 0; +} + +float & WDistort::operator[](int p){ + return xy[p]; +} + +WDistort::WDistort(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4){ + xy[0] = x1; xy[1] = y1; + xy[2] = x2; xy[3] = y2; + xy[4] = x3; xy[5] = y3; + xy[6] = x4; xy[7] = y4; +} + +//WGuiListRow + +void WGuiListRow::Render(){ + JRenderer * renderer = JRenderer::GetInstance(); + int listHeight=40; + int listSelectable=0; + int adjustedCurrent=0; + int start = 0, nowPos = 0, vHeight=0; + int nbitems = (int) items.size(); + + //List is empty. + if (!items.size() && failMsg != ""){ + JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); + mFont->SetColor(getColor(WGuiColor::TEXT_FAIL)); + mFont->DrawString(_(failMsg).c_str(),x+width/2, y, JGETEXT_RIGHT); + return; + } + + //Force a selectable option. + if(currentItem == -1){ + for (int i = 0 ; i < nbitems; i++){ + if(items[i]->Selectable()) { + currentItem = i; + if(hasFocus()) + items[currentItem]->Entering(0); + break; + } + } + } + + vHeight = 0; + nowPos = 4; + float nowVPos = 4; + float tallestRow = 0; + int numRows = 1; + float cTallest = 0; + + //Render items. + if(start >= 0) + { + //Render current underlay. + if(currentItem >= 0 && currentItem < nbitems && items[currentItem]->Visible()) + items[currentItem]->Underlay(); + + for (int pos=0;pos < nbitems; pos++){ + if(!items[pos]->Visible()) + continue; + + items[pos]->setX(x+nowPos); + items[pos]->setY(y+nowVPos); + //items[pos]->setWidth(width/nbitems); + items[pos]->setWidth(items[pos]->minWidth()); + float temp = items[pos]->getHeight() + 3; + if(temp > tallestRow) + tallestRow = temp; + if(temp > cTallest) + cTallest = temp; + nowPos += items[pos]->getWidth() + 5; + renderBack(items[pos]); + if(x+nowPos+items[pos]->getWidth()+10 > SCREEN_WIDTH){ + nowPos = 0 + 20; //Indent newlines. + nowVPos += cTallest; + cTallest = 0; + numRows++; + } + items[pos]->Render(); + if(nowVPos > SCREEN_HEIGHT) + break; + } + + //Render current overlay. + if(currentItem >= 0 && currentItem < nbitems && items[currentItem]->Visible()) + items[currentItem]->Overlay(); + } + setHeight(tallestRow*numRows+10); +} +WGuiListRow::WGuiListRow(string n, WSyncable * s) : WGuiList(n,s) { + buttonNext = PSP_CTRL_RIGHT; + buttonPrev = PSP_CTRL_LEFT; + width = SCREEN_WIDTH; + height = 20; +} +//WGuiFilterUI +void WGuiFilters::Finish(){ + bFinished = true; + if(source){ + string src = getCode(); + source->clearFilters(); + if(src.size()){ + WCFilterFactory * wc = WCFilterFactory::GetInstance(); + source->addFilter(wc->Construct(src)); + } + if(!source->Size()) + source->clearFilters(); //TODO: Pop a "No results found" warning. + } +} +void WGuiFilters::ButtonPressed(int controllerId, int controlId){ + if(controllerId == -102){ + if(controlId == -10){ + WGuiListRow * wgl= NEW WGuiListRow(""); + wgl->Add(NEW WGuiFilterItem(this)); + list->Add(wgl); + }else if(controlId == -11){ + Finish(); + } + else{ + source->clearFilters(); + SAFE_DELETE(list); + buildList(); + } + return; + }else{ + if(list != NULL) + list->ButtonPressed(controllerId,controlId); + } +} +void WGuiFilters::buildList(){ + list = NEW WGuiList(""); + WGuiButton * l = NEW WGuiButton(NEW WGuiItem("Add Filter"),-102,-10,this); + WGuiButton * r = NEW WGuiButton(NEW WGuiItem("Done"),-102,-11,this); + WGuiButton * mid = NEW WGuiButton(NEW WGuiItem("Clear"),-102,-66,this); + WGuiSplit * sub = NEW WGuiSplit(mid,r); + WGuiSplit * wgs = NEW WGuiSplit(l,sub); + subMenu = NULL; + list->Add(NEW WGuiHeader(displayValue)); + list->Add(wgs); + list->Entering(0); +} +WGuiFilters::WGuiFilters(string header, WSrcCards * src) : WGuiItem(header) { + bFinished = false; + source = src; + buildList(); +} +string WGuiFilters::getCode(){ + if(!list) + return ""; + string res; + vector::iterator row, col; + for(row=list->items.begin();row!=list->items.end();row++){ + WGuiList * wgl = dynamic_cast(*row); + if(wgl){ + if(res.size()) + res += "|("; + else + res += "("; + for(col=wgl->items.begin();col!=wgl->items.end();col++){ + WGuiFilterItem * fi = dynamic_cast(*col); + if(fi){ + string gc = fi->getCode(); + if(res.size() && gc.size() && res[res.size()-1] != '(') + res += "&"; + res += gc; + } + } + res += ")"; + } + } + return res; +} +void WGuiFilters::Update(float dt){ + if(subMenu && !subMenu->closed) + subMenu->Update(dt); + if(list){ + list->Update(dt); + WGuiList * wgl = dynamic_cast(list->Current()); + if(!wgl) + return; + vector::iterator it; + bool bDeleted=false; + for(it=wgl->items.begin();it!=wgl->items.end();it++){ + WGuiFilterItem * wgfi = dynamic_cast(*it); + if(!wgfi || wgfi->mState != WGuiFilterItem::STATE_REMOVE) continue; + SAFE_DELETE(*it); + it = wgl->items.erase(it); + bDeleted = true; + } + if(bDeleted){ + wgl->Entering(0); + } + } +} +void WGuiFilters::Entering(u32 key){ + bFinished = false; + WGuiItem::Entering(key); +} +void WGuiFilters::Render(){ + if(!list) return; //Hurrah for paranoia. + JRenderer * r = JRenderer::GetInstance(); + float tX, tY; + tX = getX(); + tY = getY(); + r->FillRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,ARGB(128,0,0,0)); + list->setX(tX); list->setY(tY); + list->Render(); + + if(subMenu && !subMenu->closed) + subMenu->Render(); +} +bool WGuiFilters::CheckUserInput(u32 key){ + if(subMenu && !subMenu->closed && subMenu->CheckUserInput(key)) + return true; + if(key == PSP_CTRL_CROSS){//|| key == PSP_CTRL_START){ + //TODO Pop up a "Are you sure?" dialog. + return true; + } + if(list){ + return list->CheckUserInput(key); + } + return WGuiItem::CheckUserInput(key); +} +WGuiFilters::~WGuiFilters(){ + SAFE_DELETE(list); + SAFE_DELETE(subMenu); +} +void WGuiFilters::addColumn(){ + if(!list) + return; + WGuiList * wgl = dynamic_cast(list->Current()); + if(wgl){ + wgl->currentItem = wgl->items.size()-1; + wgl->Add(NEW WGuiFilterItem(this)); + } +} +bool WGuiFilters::isAvailableCode(string code){ +if(!list) return false; + WGuiList * wgl = dynamic_cast(list->Current()); + if(wgl){ + vector::iterator it; + for(it=wgl->items.begin();it!=wgl->items.end();it++){ + WGuiFilterItem * wgfi = dynamic_cast(*it); + if(!wgfi || wgfi->mState != WGuiFilterItem::STATE_FINISHED) continue; + if(wgfi->filterVal < 0 || (int)wgfi->args.size() < wgfi->filterVal) continue; + string s = wgfi->args[wgfi->filterVal].second; + if(s == code) + return false; + } + return true; + } + return false; //For some reason, we don't have any rows? +} +bool WGuiFilters::isAvailable(int type){ + if(!list) return false; + int colors = 0; + WGuiList * wgl = dynamic_cast(list->Current()); + if(wgl){ + vector::iterator it; + for(it=wgl->items.begin();it!=wgl->items.end();it++){ + WGuiFilterItem * wgfi = dynamic_cast(*it); + if(!wgfi || wgfi->mState != WGuiFilterItem::STATE_FINISHED) continue; + switch(type){ + case WGuiFilterItem::FILTER_BASIC: + return true; + case WGuiFilterItem::FILTER_COLOR: + if(wgfi->filterType == type) + colors++; + break; + default: + if(wgfi->filterType == type) + return false; + } + } + if(colors >= 5) + return false; + return true; + } + return false; //For some reason, we don't have any rows? +} +//WGuiFilterItem +WGuiFilterItem::WGuiFilterItem(WGuiFilters * parent): WGuiItem("Cards..."){ + filterType = -1; filterVal = -1; mState = 0; + mParent = parent; mNew = true; +}; +void WGuiFilterItem::updateValue(){ + bool delMenu = true; + if(!mParent) + return; + switch(mState){ + case STATE_CANCEL: + mState = STATE_UNSET; + break; + case STATE_FINISHED: + case STATE_UNSET: + SAFE_DELETE(mParent->subMenu); + mState = STATE_CHOOSE_TYPE; + mParent->subMenu = NEW SimpleMenu(-1234,this,Constants::MENU_FONT,20,20,"Filter By..."); + if(mParent->isAvailable(FILTER_SET)){ + mParent->subMenu->Add(FILTER_SET,"Set"); + delMenu = false; + } + if(mParent->isAvailable(FILTER_COLOR)){ + mParent->subMenu->Add(FILTER_COLOR,"Color"); + delMenu = false; + } + if(mParent->isAvailable(FILTER_TYPE)){ + mParent->subMenu->Add(FILTER_TYPE,"Type"); + delMenu = false; + } + if(mParent->isAvailable(FILTER_RARITY)){ + mParent->subMenu->Add(FILTER_RARITY,"Rarity"); + delMenu = false; + }if(mParent->isAvailable(FILTER_BASIC)){ + mParent->subMenu->Add(FILTER_BASIC,"Ability"); + delMenu = false; + } + if(!mNew) + mParent->subMenu->Add(-2,"Remove"); + mParent->subMenu->Add(-1,"Cancel"); + if(delMenu){ + SAFE_DELETE(mParent->subMenu); + mState = STATE_FINISHED; + } + break; + case STATE_CHOOSE_TYPE: + SAFE_DELETE(mParent->subMenu); + args.clear(); + mState = STATE_CHOOSE_VAL; + mParent->subMenu = NEW SimpleMenu(-1234,this,Constants::MENU_FONT,20,20,"Filter:"); + if(filterType == FILTER_TYPE){ + addArg("Artifact","t:Artifact;"); + addArg("Artifact Creature","t:Artifact;&t:Creature;"); + addArg("Creature","t:Creature;"); + addArg("Enchantment","t:Enchantment;"); + addArg("Instant","t:Instant;"); + addArg("Land","t:Land;"); + addArg("Legendary","t:Legendary;"); + addArg("Sorcery","t:Sorcery;"); + }else if(filterType == FILTER_RARITY){ + addArg("Mythic","r:m;"); + addArg("Rare","r:r;"); + addArg("Uncommon","r:u;"); + addArg("Common","r:c;"); + }else if(filterType == FILTER_COLOR){ + addArg("White","c:w;"); + addArg("Blue","c:u;"); + addArg("Red","c:r;"); + addArg("Green","c:g;"); + addArg("Black","c:b;"); + }else if(filterType == FILTER_BASIC){ + char buf[512]; + for(int i=0;igetName(),buf); + } + } + mParent->subMenu->Add(-1,"Cancel"); + break; + case STATE_CHOOSE_VAL: + mState = STATE_FINISHED; + if(mNew && mParent) + mParent->addColumn(); + mNew = false; + if(filterVal > -1 && filterVal < (int) args.size()) + displayValue = args[filterVal].first; + SAFE_DELETE(mParent->subMenu); + break; + } +} +void WGuiFilterItem::addArg(string display, string code){ + if(!mParent || !mParent->subMenu || !mParent->isAvailableCode(code)) + return; + mParent->subMenu->Add((int)args.size(),display.c_str()); + args.push_back(pair(display,code)); +} + +void WGuiFilterItem::ButtonPressed(int controllerId, int controlId){ + if(!mParent) return; + + switch(mState){ + case STATE_CHOOSE_TYPE: + if(controlId == -1){ + mParent->subMenu->Close(); + mState = STATE_CANCEL; return; + }else if(controlId == -2){ + mParent->subMenu->Close(); + mState = STATE_REMOVE; return; + } + filterType = controlId; + break; + case STATE_CHOOSE_VAL: + if(controlId == -1){ + mParent->subMenu->Close(); + mState = STATE_UNSET; return; + }else if(controlId == -2){ + mParent->subMenu->Close(); + mState = STATE_REMOVE; return; + } + filterVal = controlId; + break; + } + updateValue(); +} + +bool WGuiFilterItem::isModal(){ + switch(mState){ + case STATE_UNSET: + case STATE_REMOVE: + case STATE_CANCEL: + case STATE_FINISHED: + return false; + } + return true; +} + +string WGuiFilterItem::getCode(){ + if(mState != STATE_FINISHED || filterVal < 0 || filterVal > (int) args.size()) + return ""; + return args[filterVal].second; +} \ No newline at end of file diff --git a/projects/mtg/template.vcproj b/projects/mtg/template.vcproj index 3be0b4c90..f7837461b 100644 --- a/projects/mtg/template.vcproj +++ b/projects/mtg/template.vcproj @@ -628,10 +628,6 @@ RelativePath=".\src\Rules.cpp" > - - @@ -688,10 +684,22 @@ RelativePath=".\src\WCachedResource.cpp" > + + + + + + @@ -993,10 +1001,6 @@ RelativePath=".\include\Rules.h" > - - @@ -1057,10 +1061,22 @@ RelativePath=".\include\WCachedResource.h" > + + + + + +