]> git.localhorst.tv Git - l2e.git/commitdiff
Merge branch 'menus'
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Fri, 30 Nov 2012 14:00:50 +0000 (15:00 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Fri, 30 Nov 2012 14:00:50 +0000 (15:00 +0100)
* stalled capsule menu until capsule monsters are implemented
* item using and spell invocation also stalled

64 files changed:
.gitignore
l2e.cbp
src/battle/Hero.h
src/common/GameConfig.cpp
src/common/GameConfig.h
src/common/GameState.cpp
src/common/GameState.h
src/common/Hero.cpp
src/common/Hero.h
src/common/HeroGroup.h [deleted file]
src/common/Inventory.cpp
src/common/Inventory.h
src/common/Item.cpp
src/common/Item.h
src/common/Spell.cpp
src/common/Spell.h
src/common/fwd.h
src/geometry/Vector.h
src/graphics/Font.cpp
src/graphics/Font.h
src/graphics/Frame.cpp
src/graphics/Menu.h
src/graphics/Texture.cpp [new file with mode: 0644]
src/graphics/Texture.h [new file with mode: 0644]
src/graphics/fwd.h
src/main.cpp
src/map/MapState.cpp
src/menu/ChangeHero.cpp [new file with mode: 0644]
src/menu/ChangeHero.h [new file with mode: 0644]
src/menu/ConfigMenu.cpp [new file with mode: 0644]
src/menu/ConfigMenu.h [new file with mode: 0644]
src/menu/EquipMenu.cpp [new file with mode: 0644]
src/menu/EquipMenu.h [new file with mode: 0644]
src/menu/HeroStatus.cpp [new file with mode: 0644]
src/menu/HeroStatus.h [new file with mode: 0644]
src/menu/InventoryMenu.cpp [new file with mode: 0644]
src/menu/InventoryMenu.h [new file with mode: 0644]
src/menu/PartyMenu.cpp [new file with mode: 0644]
src/menu/PartyMenu.h [new file with mode: 0644]
src/menu/Resources.cpp [new file with mode: 0644]
src/menu/Resources.h [new file with mode: 0644]
src/menu/ScenarioMenu.cpp [new file with mode: 0644]
src/menu/ScenarioMenu.h [new file with mode: 0644]
src/menu/SelectHero.cpp [new file with mode: 0644]
src/menu/SelectHero.h [new file with mode: 0644]
src/menu/SpellMenu.cpp [new file with mode: 0644]
src/menu/SpellMenu.h [new file with mode: 0644]
src/menu/StatusMenu.cpp [new file with mode: 0644]
src/menu/StatusMenu.h [new file with mode: 0644]
src/menu/fwd.h [new file with mode: 0644]
test-data/constants.l2h
test-data/constants.l2s
test-data/hero-cursor.png [new file with mode: 0644]
test-data/items.l2s
test-data/menu-cursor-active.png [new file with mode: 0644]
test-data/menu-cursor.png [new file with mode: 0644]
test-data/menu-font-inactive.png [new file with mode: 0644]
test-data/menu-font.png [new file with mode: 0644]
test-data/menubg.png [new file with mode: 0644]
test-data/normal-font.png
test-data/shoulder-nav.png [new file with mode: 0644]
test-data/status-frame.png [new file with mode: 0644]
test-data/status-labels.png [new file with mode: 0644]
test-data/test.l2s

index c85cc2696aaf3593c68d01657a17c746d4a1edd3..d266c4eab2bccaec02aad4a0bfe62596cf65e7a9 100644 (file)
@@ -4,7 +4,8 @@
 *.o
 *.d
 *.l2o
-build/*/l2e
+/build/*/l2e
+/build/*/local.mk
 shots/
 bin/*
 l2e.depend
diff --git a/l2e.cbp b/l2e.cbp
index f221067c48b32358314edde3735e9c76052b86ac..7b8f77bd94333d4e36a0e14298cc4f57e9499ee9 100644 (file)
--- a/l2e.cbp
+++ b/l2e.cbp
                <Unit filename="src\common\GameState.h" />
                <Unit filename="src\common\Hero.cpp" />
                <Unit filename="src\common\Hero.h" />
-               <Unit filename="src\common\HeroGroup.h" />
                <Unit filename="src\common\Ikari.cpp" />
                <Unit filename="src\common\Ikari.h" />
                <Unit filename="src\common\Inventory.cpp" />
                <Unit filename="src\map\Trigger.cpp" />
                <Unit filename="src\map\Trigger.h" />
                <Unit filename="src\map\fwd.h" />
+               <Unit filename="src\menu\ChangeHero.cpp" />
+               <Unit filename="src\menu\ChangeHero.h" />
+               <Unit filename="src\menu\ConfigMenu.cpp" />
+               <Unit filename="src\menu\ConfigMenu.h" />
+               <Unit filename="src\menu\EquipMenu.cpp" />
+               <Unit filename="src\menu\EquipMenu.h" />
+               <Unit filename="src\menu\fwd.h" />
+               <Unit filename="src\menu\HeroStatus.cpp" />
+               <Unit filename="src\menu\HeroStatus.h" />
+               <Unit filename="src\menu\InventoryMenu.cpp" />
+               <Unit filename="src\menu\InventoryMenu.h" />
+               <Unit filename="src\menu\PartyMenu.cpp" />
+               <Unit filename="src\menu\PartyMenu.h" />
+               <Unit filename="src\menu\Resources.cpp" />
+               <Unit filename="src\menu\Resources.h" />
+               <Unit filename="src\menu\ScenarioMenu.cpp" />
+               <Unit filename="src\menu\ScenarioMenu.h" />
+               <Unit filename="src\menu\SelectHero.cpp" />
+               <Unit filename="src\menu\SelectHero.h" />
+               <Unit filename="src\menu\SpellMenu.cpp" />
+               <Unit filename="src\menu\SpellMenu.h" />
+               <Unit filename="src\menu\StatusMenu.cpp" />
+               <Unit filename="src\menu\StatusMenu.h" />
                <Unit filename="src\sdl\InitImage.cpp" />
                <Unit filename="src\sdl\InitImage.h" />
                <Unit filename="src\sdl\InitSDL.cpp" />
index d719288b376f2e687e64c5f5924c204483d696a4..5e86f88dae783da4f8dbaf47d1666e32568fb82c 100644 (file)
@@ -54,26 +54,19 @@ public:
        common::Stats &GetStats() { return stats; }
        const common::Stats &GetStats() const { return stats; }
 
-       common::Item *Weapon() { return master->Weapon(); }
-       common::Item *Armor() { return master->Armor(); }
-       common::Item *Shield() { return master->Shield(); }
-       common::Item *Helmet() { return master->Helmet(); }
-       common::Item *Ring() { return master->Ring(); }
-       common::Item *Jewel() { return master->Jewel(); }
-
-       const common::Item *Weapon() const { return master->Weapon(); }
-       const common::Item *Armor() const { return master->Armor(); }
-       const common::Item *Shield() const { return master->Shield(); }
-       const common::Item *Helmet() const { return master->Helmet(); }
-       const common::Item *Ring() const { return master->Ring(); }
-       const common::Item *Jewel() const { return master->Jewel(); }
-
-       bool HasWeapon() const { return master->HasWeapon(); }
-       bool HasArmor() const { return master->HasArmor(); }
-       bool HasShield() const { return master->HasShield(); }
-       bool HasHelmet() const { return master->HasHelmet(); }
-       bool HasRing() const { return master->HasRing(); }
-       bool HasJewel() const { return master->HasJewel(); }
+       const common::Item *Weapon() const { return master->Equipment(common::Hero::EQUIP_WEAPON); }
+       const common::Item *Armor() const { return master->Equipment(common::Hero::EQUIP_ARMOR); }
+       const common::Item *Shield() const { return master->Equipment(common::Hero::EQUIP_SHIELD); }
+       const common::Item *Helmet() const { return master->Equipment(common::Hero::EQUIP_HELMET); }
+       const common::Item *Ring() const { return master->Equipment(common::Hero::EQUIP_RING); }
+       const common::Item *Jewel() const { return master->Equipment(common::Hero::EQUIP_JEWEL); }
+
+       bool HasWeapon() const { return master->Equipped(common::Hero::EQUIP_WEAPON); }
+       bool HasArmor() const { return master->Equipped(common::Hero::EQUIP_ARMOR); }
+       bool HasShield() const { return master->Equipped(common::Hero::EQUIP_SHIELD); }
+       bool HasHelmet() const { return master->Equipped(common::Hero::EQUIP_HELMET); }
+       bool HasRing() const { return master->Equipped(common::Hero::EQUIP_RING); }
+       bool HasJewel() const { return master->Equipped(common::Hero::EQUIP_JEWEL); }
 
        graphics::AnimationRunner &GetAnimation() { return animation; }
        const graphics::AnimationRunner &GetAnimation() const { return animation; }
index b35e99161d437b19ff05c2888e97831be641d3c3..6673a326686a90200ae10f9c370add18233dc0c2 100644 (file)
@@ -12,7 +12,8 @@ namespace common {
 GameConfig::GameConfig()
 : state(0)
 , battleResources(0)
-, heroesLayout(0) {
+, heroesLayout(0)
+, menuResources(0) {
 
 }
 
index 16b0142b426e058aca16564c648bd61f7f06b619..c13b4ab3efc60609684c9af47499ca45bdf3196d 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "fwd.h"
 #include "../battle/fwd.h"
+#include "../menu/fwd.h"
 
 namespace common {
 
@@ -22,6 +23,8 @@ struct GameConfig {
        battle::Resources *battleResources;
        battle::PartyLayout *heroesLayout;
 
+       menu::Resources *menuResources;
+
 };
 
 }
index 7992d43980e0a9888835e3c7dba8793079e8b4ed..480c305e94007441cb8ed137cd9f6fe3430ef6f5 100644 (file)
 namespace common {
 
 GameState::GameState()
-: money(0) {
+: partySize(1)
+, money(0)
+, time(0)
+, messageSpeed(MESSAGE_SPEED_NORMAL)
+, battleCursor(CURSOR_CLEAR)
+, statusCursor(CURSOR_CLEAR)
+, music(MUSIC_STEREO) {
        party[0] = heroes;
        party[1] = 0;
        party[2] = 0;
index fca50c1f846138168edd40cba990363ad49b6ea8..1cd54282a345d2ab2ce8b4fb86ee44a512e1bfa5 100644 (file)
@@ -21,10 +21,30 @@ struct GameState {
 
        Hero heroes[7];
        Hero *party[4];
+       int partySize;
 
        Inventory inventory;
 
        Uint32 money;
+       Uint32 time;
+
+       enum MessageSpeed {
+               MESSAGE_SPEED_FAST,
+               MESSAGE_SPEED_NORMAL,
+               MESSAGE_SPEED_SLOW,
+       };
+       enum Cursor {
+               CURSOR_CLEAR,
+               CURSOR_MEMORY,
+       };
+       enum Music {
+               MUSIC_STEREO,
+               MUSIC_MONO,
+       };
+       int messageSpeed;
+       int battleCursor;
+       int statusCursor;
+       int music;
 
 };
 
index 02c4dcc05b8e4fc8807f0d266e31d9652d539c14..7150af0a35b8c0528058d5c845794875c4483c18 100644 (file)
@@ -7,18 +7,24 @@
 
 #include "Hero.h"
 
+#include "Item.h"
+#include "Spell.h"
 #include "../graphics/Animation.h"
 #include "../graphics/Sprite.h"
 #include "../loader/Interpreter.h"
 #include "../loader/TypeDescription.h"
 #include "../map/Entity.h"
 
+#include <cstring>
+
 using graphics::Animation;
 using graphics::Sprite;
 using loader::FieldDescription;
 using loader::Interpreter;
 using loader::TypeDescription;
 using map::Entity;
+using std::memset;
+
 
 namespace common {
 
@@ -32,19 +38,17 @@ Hero::Hero()
 , ip(0)
 
 , level(0)
+, experience(0)
+, levelLadder(0)
+, numLevels(0)
 
-, weapon(0)
-, armor(0)
-, shield(0)
-, helmet(0)
-, ring(0)
-, jewel(0)
+, useMask(0)
 
 , battleSprite(0)
 , meleeAnimation(0)
 , attackAnimation(0)
 , spellAnimation(0) {
-
+       memset(equipment, 0, sizeof(equipment));
 }
 
 
@@ -63,6 +67,25 @@ void Hero::SubtractHealth(int amount) {
 }
 
 
+int Hero::NextLevel() const {
+       int levelOffset(Level() - 1);
+       if (levelOffset < numLevels) {
+               return levelLadder[levelOffset] - Experience();
+       } else {
+               return 0;
+       }
+}
+
+
+bool Hero::CanEquip(const Item &item) const {
+       return useMask & item.HeroMask();
+}
+
+bool Hero::CanInvoke(const Spell &spell) const {
+       return useMask & spell.HeroMask();
+}
+
+
 void Hero::CreateTypeDescription() {
        Hero h;
 
@@ -81,6 +104,9 @@ void Hero::CreateTypeDescription() {
        td.AddField("stats", FieldDescription(((char *)&h.stats) - ((char *)&h), Stats::TYPE_ID));
 
        td.AddField("level", FieldDescription(((char *)&h.level) - ((char *)&h), Interpreter::NUMBER_ID));
+       td.AddField("ladder", FieldDescription(((char *)&h.levelLadder) - ((char *)&h), Interpreter::NUMBER_ID).SetReferenced().SetAggregate());
+
+       td.AddField("useMask", FieldDescription(((char *)&h.useMask) - ((char *)&h), Interpreter::NUMBER_ID));
 
        td.AddField("battleSprite", FieldDescription(((char *)&h.battleSprite) - ((char *)&h), Sprite::TYPE_ID).SetReferenced().SetDescription("the sprite used for battle scenes"));
        td.AddField("attackAnimation", FieldDescription(((char *)&h.attackAnimation) - ((char *)&h), Animation::TYPE_ID).SetReferenced().SetDescription("the animation played for physical attacks"));
index ca4651c947d66003583879145d4b44b4c304b9c7..00924d400ef7eaf3f2fca291d80c695132141076 100644 (file)
@@ -27,6 +27,16 @@ public:
        ~Hero() { }
 
 public:
+       enum EquipSlot {
+               EQUIP_WEAPON,
+               EQUIP_ARMOR,
+               EQUIP_SHIELD,
+               EQUIP_HELMET,
+               EQUIP_RING,
+               EQUIP_JEWEL,
+               EQUIP_COUNT,
+       };
+
        const char *Name() const { return name; }
 
        Uint16 MaxHealth() const { return maxHealth; }
@@ -47,31 +57,22 @@ public:
        const Stats &GetStats() const { return stats; }
 
        Uint8 Level() const { return level; }
+       int Experience() const { return experience; }
+       int NextLevel() const;
+
+       bool CanEquip(const Item &) const;
+       bool CanInvoke(const Spell &) const;
 
-       Item *Weapon() { return weapon; }
-       Item *Armor() { return armor; }
-       Item *Shield() { return shield; }
-       Item *Helmet() { return helmet; }
-       Item *Ring() { return ring; }
-       Item *Jewel() { return jewel; }
-
-       const Item *Weapon() const { return weapon; }
-       const Item *Armor() const { return armor; }
-       const Item *Shield() const { return shield; }
-       const Item *Helmet() const { return helmet; }
-       const Item *Ring() const { return ring; }
-       const Item *Jewel() const { return jewel; }
-
-       bool HasWeapon() const { return weapon; }
-       bool HasArmor() const { return armor; }
-       bool HasShield() const { return shield; }
-       bool HasHelmet() const { return helmet; }
-       bool HasRing() const { return ring; }
-       bool HasJewel() const { return jewel; }
+       const Item *Equipment(EquipSlot i) const { return equipment[i]; }
+       bool Equipped(EquipSlot i) const { return equipment[i]; }
+       void RemoveEquipment(EquipSlot i) { equipment[i] = 0; }
+       void SetEquipment(EquipSlot i, const Item *item) { equipment[i] = item; }
 
+       std::vector<const Spell *> &Spells() { return spells; }
        const std::vector<const Spell *> &Spells() const { return spells; }
 
        graphics::Sprite *BattleSprite() { return battleSprite; }
+       const graphics::Sprite *BattleSprite() const { return battleSprite; }
        graphics::Animation *MeleeAnimation() { return meleeAnimation; }
        graphics::Animation *AttackAnimation() { return attackAnimation; }
        graphics::Animation *SpellAnimation() { return spellAnimation; }
@@ -84,13 +85,6 @@ public:
 
 // temporary setters
 public:
-       void SetWeapon(common::Item *i) { weapon = i; }
-       void SetArmor(common::Item *i) { armor = i; }
-       void SetShield(common::Item *i) { shield = i; }
-       void SetHelmet(common::Item *i) { helmet = i; }
-       void SetRing(common::Item *i) { ring = i; }
-       void SetJewel(common::Item *i) { jewel = i; }
-
        void AddSpell(Spell *s) { spells.push_back(s); }
 
 private:
@@ -103,13 +97,14 @@ private:
        Stats stats;
 
        int level;
+       int experience;
+
+       int *levelLadder;
+       int numLevels;
+
+       int useMask;
 
-       Item *weapon;
-       Item *armor;
-       Item *shield;
-       Item *helmet;
-       Item *ring;
-       Item *jewel;
+       const Item *equipment[EQUIP_COUNT];
 
        // TODO: vector does not seem to be a good choice
        std::vector<const Spell *> spells;
diff --git a/src/common/HeroGroup.h b/src/common/HeroGroup.h
deleted file mode 100644 (file)
index ef6fd25..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * HeroGroup.h
- *
- *  Created on: Aug 10, 2012
- *      Author: holy
- */
-
-#ifndef COMMON_HEROGROUP_H_
-#define COMMON_HEROGROUP_H_
-
-#include <SDL.h>
-
-namespace common {
-
-class HeroGroup {
-
-public:
-       HeroGroup() : members(NOBODY) { }
-
-public:
-       bool HasMaxim() const { return members & MAXIM; }
-       bool HasSelan() const { return members & SELAN; }
-       bool HasGuy() const { return members & GUY; }
-       bool HasArtea() const { return members & ARTEA; }
-       bool HasTia() const { return members & TIA; }
-       bool HasDekar() const { return members & DEKAR; }
-       bool HasLexis() const { return members & LEXIS; }
-
-       void AddMaxim() { members |= MAXIM; }
-       void AddSelan() { members |= SELAN; }
-       void AddGuy() { members |= GUY; }
-       void AddArtea() { members |= ARTEA; }
-       void AddTia() { members |= TIA; }
-       void AddDekar() { members |= DEKAR; }
-       void AddLexis() { members |= LEXIS; }
-       void AddAll() { members = (MAXIM | SELAN | GUY | ARTEA | TIA | DEKAR | LEXIS); }
-
-       void RemoveMaxim() { members &= ~MAXIM; }
-       void RemoveSelan() { members &= ~SELAN; }
-       void RemoveGuy() { members &= ~GUY; }
-       void RemoveArtea() { members &= ~ARTEA; }
-       void RemoveTia() { members &= ~TIA; }
-       void RemoveDekar() { members &= ~DEKAR; }
-       void RemoveLexis() { members &= ~LEXIS; }
-       void RemoveAll() { members = NOBODY; }
-
-public:
-       enum {
-               NOBODY = 0,
-               MAXIM = 1,
-               SELAN = 2,
-               GUY = 4,
-               ARTEA = 8,
-               TIA = 16,
-               DEKAR = 32,
-               LEXIS = 64,
-       };
-       Uint8 members;
-
-};
-
-}
-
-#endif /* COMMON_HEROGROUP_H_ */
index d118f2187daeb9bb28f84d2ccd48882bad6c8c58..275de538f49f83eb344b549d55f1b41fad81f10f 100644 (file)
@@ -7,9 +7,15 @@
 
 #include "Inventory.h"
 
+#include "Item.h"
+
+#include <algorithm>
+
+
 namespace common {
 
-Inventory::Inventory() {
+Inventory::Inventory()
+: scenarioEnd(0) {
 
 }
 
@@ -48,6 +54,10 @@ void Inventory::Remove(const Item *item, int count) {
        }
 }
 
+void Inventory::RemoveAll(const Item *item) {
+       Remove(item, 255);
+}
+
 Inventory::Entry *Inventory::FindItem(const Item *item) {
        for (int i(0); i < MaxItems(); ++i) {
                if (item == ItemAt(i)) {
@@ -61,4 +71,36 @@ bool Inventory::SloteFree(int offset) const {
        return !ItemAt(offset);
 }
 
+
+bool Inventory::AddScenarioItem(const Item *i) {
+       if (scenarioEnd < MaxScenarioItems()) {
+               scenario[scenarioEnd] = i;
+               ++scenarioEnd;
+               return true;
+       } else {
+               return false;
+       }
+}
+
+
+void Inventory::Sort() {
+       std::sort(entries, entries + 96, Entry::Less);
+}
+
+bool Inventory::Entry::Less(const Entry &lhs, const Entry &rhs) {
+       if (lhs.item) {
+               if (rhs.item) {
+                       return Item::Less(*lhs.item, *rhs.item);
+               } else {
+                       return true;
+               }
+       } else {
+               return false;
+       }
+}
+
+void Inventory::SwapEntriesAt(int lhs, int rhs) {
+       std::swap(entries[lhs], entries[rhs]);
+}
+
 }
index f60b9abecb49e71817e5c3fe7a4b74fe3d011f3d..e7c569605999170b924f1fc4ad53cd5b62d73717 100644 (file)
@@ -22,17 +22,27 @@ public:
 public:
        bool Add(const Item *, int count = 1);
        void Remove(const Item *, int count = 1);
+       void RemoveAll(const Item *);
 
        int MaxItems() const { return 96; }
 
+       bool AddScenarioItem(const Item *);
+       const Item *ScenarioItemAt(int offset) const { return scenario[offset]; }
+       int NumScenarioItems() const { return scenarioEnd; }
+       int MaxScenarioItems() const { return 64; }
+
        const Item *ItemAt(int offset) const { return entries[offset].item; }
        int ItemCountAt(int offset) const { return entries[offset].count; }
 
+       void Sort();
+       void SwapEntriesAt(int lhs, int rhs);
+
 private:
        struct Entry {
                Entry() : item(0), count(0) { }
                const Item *item;
                Uint8 count;
+               static bool Less(const Entry &, const Entry &);
        };
 
 private:
@@ -42,6 +52,8 @@ private:
 
 private:
        Entry entries[96];
+       const Item *scenario[64];
+       int scenarioEnd;
 
 };
 
index 2bd337d061321d2b3a07de749d65b24be52514b4..955f48c9e6204954e08c3faef26a8a7d75346e2e 100644 (file)
@@ -33,9 +33,9 @@ Item::Item()
 , properties(0)
 
 , equipability(0)
+, heroMask(0)
 
 , mostUseful(false)
-, equipable(false)
 , cursed(false)
 , fruit(false)
 , scenario(false)
@@ -57,7 +57,6 @@ void Item::CreateTypeDescription() {
        td.AddField("menuicon", FieldDescription(((char *)&i.menuIcon) - ((char *)&i), Sprite::TYPE_ID).SetReferenced().SetDescription("icon that is displayed in menus"));
 
        td.AddField("mostUseful", FieldDescription(((char *)&i.mostUseful) - ((char *)&i), Interpreter::BOOLEAN_ID));
-       td.AddField("equipable", FieldDescription(((char *)&i.equipable) - ((char *)&i), Interpreter::BOOLEAN_ID));
        td.AddField("cursed", FieldDescription(((char *)&i.cursed) - ((char *)&i), Interpreter::BOOLEAN_ID));
        td.AddField("fruit", FieldDescription(((char *)&i.fruit) - ((char *)&i), Interpreter::BOOLEAN_ID));
        td.AddField("scenario", FieldDescription(((char *)&i.scenario) - ((char *)&i), Interpreter::BOOLEAN_ID));
@@ -67,10 +66,35 @@ void Item::CreateTypeDescription() {
        td.AddField("targets", FieldDescription(((char *)&i.targettingMode) - ((char *)&i), TargetingMode::TYPE_ID).SetDescription("how target selection is to be performed"));
        td.AddField("ikari", FieldDescription(((char *)&i.ikari) - ((char *)&i), Ikari::TYPE_ID).SetReferenced().SetDescription("ikari attack of the item (sensible only for equipment)"));
        td.AddField("attackanimation", FieldDescription(((char *)&i.attackAnimation) - ((char *)&i), Animation::TYPE_ID).SetReferenced().SetDescription("animation that is run when the item is used for attacking"));
+       td.AddField("equipability", FieldDescription(((char *)&i.equipability) - ((char *)&i), Interpreter::NUMBER_ID).SetDescription("how this item can be equipped"));
+       td.AddField("heroMask", FieldDescription(((char *)&i.heroMask) - ((char *)&i), Interpreter::NUMBER_ID).SetDescription("which heroes may equip this item"));
 }
 
 void Item::Construct(void *data) {
        new (data) Item;
 }
 
+
+bool Item::Less(const Item &lhs, const Item &rhs) {
+       if (lhs.IsMostUseful()) {
+               return !rhs.IsMostUseful();
+       }
+       if (lhs.IsEquipable()) {
+               if (rhs.IsMostUseful()) {
+                       return false;
+               }
+               if (!rhs.IsEquipable()) {
+                       return true;
+               }
+               return lhs.equipability < rhs.equipability;
+       }
+       if (lhs.IsFruit()) {
+               if (rhs.IsMostUseful() || rhs.IsEquipable()) {
+                       return true;
+               }
+               return !rhs.IsFruit();
+       }
+       return false;
+}
+
 }
index 279235572768662e820ea60b3fd15533f31a099a..df361fe97cdec1a80038bbc1046bc1a74a51aeb4 100644 (file)
@@ -9,7 +9,7 @@
 #define COMMON_ITEM_H_
 
 #include "fwd.h"
-#include "HeroGroup.h"
+#include "Hero.h"
 #include "TargetingMode.h"
 #include "../graphics/fwd.h"
 
@@ -29,7 +29,7 @@ public:
        const char *Name() const { return name; }
 
        bool IsMostUseful() const { return mostUseful; }
-       bool IsEquipable() const { return equipable; }
+       bool IsEquipable() const { return equipability; }
        bool IsCursed() const { return cursed; }
        bool IsFruit() const { return fruit; }
        bool IsScenario() const { return scenario; }
@@ -54,15 +54,9 @@ public:
 
        Uint16 Value() const { return value; }
 
-       bool CanEquipWeapon() const { return equipability & EQUIPPABLE_WEAPON; }
-       bool CanEquipArmor() const { return equipability & EQUIPPABLE_ARMOR; }
-       bool CanEquipShield() const { return equipability & EQUIPPABLE_SHIELD; }
-       bool CanEquipHelmet() const { return equipability & EQUIPPABLE_HELMET; }
-       bool CanEquipRing() const { return equipability & EQUIPPABLE_RING; }
-       bool CanEquipJewel() const { return equipability & EQUIPPABLE_JEWEL; }
+       bool EquipableAt(Hero::EquipSlot slot) const { return equipability & (1 << slot); }
 
-       HeroGroup &EquipableBy() { return equipableBy; }
-       const HeroGroup &EquipableBy() const { return equipableBy; }
+       int HeroMask() const { return heroMask; }
 
        bool HasEffectOnStatusScreen() const { return properties & PROPERTY_HAS_EFFECT_STATUS; }
        bool HasEffectInBattle() const { return properties & PROPERTY_HAS_EFFECT_BATTLE; }
@@ -78,6 +72,8 @@ public:
        bool HasBattleAnimation() const { return properties & PROPERTY_HAS_BATTLE_ANIMATION; }
        bool HasIkariEffect() const { return properties & PROPERTY_HAS_IKARI_EFFECT; }
 
+       static bool Less(const Item &, const Item &);
+
 // temporary setters
 public:
        void SetName(const char *n) { name = n; }
@@ -90,15 +86,6 @@ public:
        static void Construct(void *);
 
 private:
-       enum Equipable {
-               EQUIPPABLE_NONE = 0,
-               EQUIPPABLE_WEAPON = 1,
-               EQUIPPABLE_ARMOR = 2,
-               EQUIPPABLE_SHIELD = 4,
-               EQUIPPABLE_HELMET = 8,
-               EQUIPPABLE_RING = 16,
-               EQUIPPABLE_JEWEL = 32,
-       };
        enum Property {
                PROPERTY_HAS_EFFECT_STATUS = 1,
                PROPERTY_HAS_EFFECT_BATTLE = 2,
@@ -129,12 +116,11 @@ private:
        Uint16 properties;
 
        TargetingMode targettingMode;
-       Uint8 equipability;
-       HeroGroup equipableBy;
+       int equipability;
+       int heroMask;
 
        // TODO: turn these back into bits as soon as fields are implemented in the loader
        bool mostUseful;
-       bool equipable;
        bool cursed;
        bool fruit;
        bool scenario;
index 4fce22f1e42ea3e15992b969a952c8296cd9ee60..1b4142b006822f7fb8dd8023d773084eb64b6a25 100644 (file)
@@ -18,11 +18,22 @@ using loader::TypeDescription;
 namespace common {
 
 Spell::Spell()
-: name(""), value(0), cost(0), status(false), battle(false) {
+: name("")
+, value(0)
+, cost(0)
+, heroMask(0)
+, status(false)
+, battle(false) {
 
 }
 
 
+bool Spell::Less(const Spell *lhs, const Spell *rhs) {
+       // TODO: find out real spell sorting order
+       return lhs->Cost() < rhs->Cost();
+}
+
+
 void Spell::CreateTypeDescription() {
        Spell s;
 
@@ -32,8 +43,9 @@ void Spell::CreateTypeDescription() {
        td.SetSize(sizeof(Spell));
 
        td.AddField("name", FieldDescription(((char *)&s.name) - ((char *)&s), Interpreter::STRING_ID).SetReferenced().SetDescription("the spell's name"));
-       td.AddField("cost", FieldDescription(((char *)&s.cost) - ((char *)&s), Interpreter::NUMBER_ID).SetDescription("Amount of magic points needed and deducted for invocation"));
+       td.AddField("cost", FieldDescription(((char *)&s.cost) - ((char *)&s), Interpreter::NUMBER_ID).SetDescription("amount of magic points needed and deducted for invocation"));
        td.AddField("targets", FieldDescription(((char *)&s.targetingMode) - ((char *)&s), TargetingMode::TYPE_ID).SetDescription("how target selection is to be performed"));
+       td.AddField("heroMask", FieldDescription(((char *)&s.heroMask) - ((char *)&s), Interpreter::NUMBER_ID).SetDescription("which heroes can invoke this spell"));
        td.AddField("status", FieldDescription(((char *)&s.status) - ((char *)&s), Interpreter::BOOLEAN_ID).SetDescription("if the spell can be used at the status screen"));
        td.AddField("battle", FieldDescription(((char *)&s.battle) - ((char *)&s), Interpreter::BOOLEAN_ID).SetDescription("if the spell can be used in battle"));
 }
index 0f864b81bda0c3c8bd42373e24cf324e82c9a3b1..f300197427c2d9567ec5c7b878675e7aed3006c4 100644 (file)
@@ -8,7 +8,6 @@
 #ifndef COMMON_SPELL_H_
 #define COMMON_SPELL_H_
 
-#include "HeroGroup.h"
 #include "TargetingMode.h"
 
 namespace common {
@@ -32,8 +31,9 @@ public:
        TargetingMode &GetTargetingMode() { return targetingMode; }
        const TargetingMode &GetTargetingMode() const { return targetingMode; }
 
-       HeroGroup &UsableBy() { return usableBy; }
-       const HeroGroup &UsableBy() const { return usableBy; }
+       int HeroMask() const { return heroMask; }
+
+       static bool Less(const Spell *, const Spell *);
 
 // temporary setters
 public:
@@ -51,7 +51,7 @@ private:
 
        int cost;
        TargetingMode targetingMode;
-       HeroGroup usableBy;
+       int heroMask;
 
        bool status;
        bool battle;
index 1cd06715cea3bb927d82c02b49e0b96932f296d4..3dc6438b07765a62accaadd2d9aa795901128775 100644 (file)
@@ -13,7 +13,6 @@ namespace common {
 struct GameConfig;
 struct GameState;
 class Hero;
-class HeroGroup;
 class Ikari;
 class Inventory;
 class Item;
index ebff4b3905d14e209d9a877f66982700b925755f..c0b22e51d9b93d154d6dcb3ca1234d8fb7c3ae00 100644 (file)
@@ -31,6 +31,9 @@ public:
        Scalar X() const { return x; }
        Scalar Y() const { return y; }
 
+       Scalar &X() { return x; }
+       Scalar &Y() { return y; }
+
        Scalar Index(Scalar lineLength) const { return Y() * lineLength + X(); }
        static Vector<Scalar> FromIndex(Scalar index, Scalar lineLength) {
                return Vector<Scalar>(index % lineLength, index / lineLength);
index be96a2c104b860f0e9dda819388c92c0fa206d81..42772b2612088275307447d5cd5c91443cedc5c4 100644 (file)
@@ -21,6 +21,35 @@ using std::pow;
 
 namespace graphics {
 
+int Font::StringWidth(const char *s) const {
+       int width(0), col(0);
+       for (int i(0); s[i]; ++i) {
+               if (s[i] == '\n') {
+                       if (width < col) {
+                               width = col;
+                       }
+                       col = 0;
+               } else {
+                       ++col;
+               }
+       }
+       return (width < col ? col : width) * CharWidth();
+}
+
+int Font::StringHeight(const char *s) const {
+       if (*s == '\0') {
+               return 0;
+       }
+       int height(1);
+       for (; *s; ++s) {
+               if (*s == '\n') {
+                       ++height;
+               }
+       }
+       return height * CharHeight();
+}
+
+
 void Font::DrawChar(char c, SDL_Surface *dest, const Vector<int> &position) const {
        if (!sprite) return;
 
@@ -29,16 +58,39 @@ void Font::DrawChar(char c, SDL_Surface *dest, const Vector<int> &position) cons
        sprite->Draw(dest, position, col, row);
 }
 
-void Font::DrawString(const char *s, SDL_Surface *dest, const Vector<int> &positionIn, int maxChars) const {
+void Font::DrawString(const char *s, SDL_Surface *dest, const Vector<int> &positionIn, int maxWidth) const {
        if (!sprite) return;
 
        Vector<int> position(positionIn);
+       Vector<int> lineHead(positionIn);
        Vector<int> step(CharWidth(), 0);
-       for (int i(0); s[i] && (maxChars <= 0 || i < maxChars); ++i, position += step) {
-               DrawChar(s[i], dest, position);
+       Vector<int> lineBreak(0, CharHeight());
+       for (int i(0), col(0); s[i] && (maxWidth <= 0 || col < maxWidth); ++i) {
+               if (s[i] == '\n') {
+                       lineHead += lineBreak;
+                       position = lineHead;
+                       col = 0;
+               } else {
+                       DrawChar(s[i], dest, position);
+                       position += step;
+                       ++col;
+               }
        }
 }
 
+void Font::DrawStringRight(const char *s, SDL_Surface *dest, const Vector<int> &positionIn, int maxWidth) const {
+       // NOTE: this does not handle line breaks
+       if (!sprite) return;
+
+       int length(0);
+       while (length < maxWidth && s[length] != '\0') {
+               ++length;
+       }
+       Vector<int> position(positionIn.X() - length * CharWidth(), positionIn.Y());
+
+       DrawString(s, dest, position, length);
+}
+
 void Font::DrawDigit(int digit, SDL_Surface *dest, const Vector<int> &position) const {
        if (!sprite) return;
 
@@ -76,6 +128,14 @@ void Font::DrawNumber(int numberIn, SDL_Surface *dest, const Vector<int> &positi
        }
 }
 
+void Font::DrawNumberRight(int number, SDL_Surface *dest, const Vector<int> &positionIn, int digits) const {
+       if (!sprite) return;
+
+       Vector<int> position(positionIn.X() - digits * CharWidth(), positionIn.Y());
+
+       DrawNumber(number, dest, position, digits);
+}
+
 
 void Font::CreateTypeDescription() {
        Font f;
index 07cd35f0a5f32867e33487fbb082b578d589478d..a5d7b12d690019f0dfd070cd759cc1591521a94d 100644 (file)
@@ -29,10 +29,15 @@ public:
 public:
        int CharWidth() const { return sprite->Width(); }
        int CharHeight() const { return sprite->Height(); }
+       int StringWidth(const char *) const;
+       int StringHeight(const char *) const;
+
        void DrawChar(char c, SDL_Surface *dest, const geometry::Vector<int> &position) const;
-       void DrawString(const char *s, SDL_Surface *dest, const geometry::Vector<int> &position, int maxChars = 0) const;
+       void DrawString(const char *s, SDL_Surface *dest, const geometry::Vector<int> &position, int maxWidth = 0) const;
+       void DrawStringRight(const char *s, SDL_Surface *dest, const geometry::Vector<int> &position, int maxWidth = 0) const;
        void DrawDigit(int d, SDL_Surface *dest, const geometry::Vector<int> &position) const;
        void DrawNumber(int n, SDL_Surface *dest, const geometry::Vector<int> &position, int digits = 0) const;
+       void DrawNumberRight(int n, SDL_Surface *dest, const geometry::Vector<int> &position, int digits = 0) const;
 
 public:
        void SetSprite(const Sprite *s) { sprite = s; }
index 46fa1b59db7c799255c282f90e9fe92c654a509d..86b9cd25d5d7b5d06c0213aea81ef303db945b96 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "Frame.h"
 
+#include "Texture.h"
 #include "../loader/Interpreter.h"
 #include "../loader/TypeDescription.h"
 
@@ -40,55 +41,26 @@ void Frame::Draw(SDL_Surface *dest, const Vector<int> &position, int width, int
        SDL_BlitSurface(surface, &srcRect, dest, &destRect);
 
        // top border
-       srcRect.x += BorderWidth();
-       srcRect.w = RepeatWidth();
-       destRect.x += BorderWidth();
-       int fullRepeatWidth(width - (2 * BorderWidth()));
-       int repeatCursor(0);
-       while (repeatCursor < fullRepeatWidth) {
-               SDL_BlitSurface(surface, &srcRect, dest, &destRect);
-               destRect.x += RepeatWidth();
-               repeatCursor += RepeatWidth();
-       }
+       Texture(surface, Vector<int>(RepeatWidth(), BorderHeight()), Vector<int>(offset.X() + BorderWidth(), offset.Y()))
+       .Render(dest, Vector<int>(position.X() + BorderWidth(), position.Y()), Vector<int>(position.X() + width - BorderWidth(), position.Y() + BorderHeight()));
 
        // top-right corner
-       srcRect.x += RepeatWidth();
+       srcRect.x = offset.X() + RepeatWidth() + BorderWidth();
        srcRect.w = BorderWidth();
+       destRect.x = position.X() + width - BorderWidth();
        SDL_BlitSurface(surface, &srcRect, dest, &destRect);
 
-       // middle
-       destRect.y += BorderHeight();
-       int fullRepeatHeight(height - (2 * BorderHeight()));
-       int hRepeatCursor(0);
-       while (hRepeatCursor < fullRepeatHeight) {
-
-               // left border
-               srcRect.x = offset.X();
-               srcRect.y = offset.Y() + BorderHeight();
-               srcRect.w = BorderWidth();
-               srcRect.h = RepeatHeight();
-               destRect.x = position.X();
-               SDL_BlitSurface(surface, &srcRect, dest, &destRect);
-
-               // fill
-               repeatCursor = 0;
-               srcRect.x += BorderWidth();
-               srcRect.w = RepeatWidth();
-               destRect.x += BorderWidth();
-               while (repeatCursor < fullRepeatWidth) {
-                       SDL_BlitSurface(surface, &srcRect, dest, &destRect);
-                       destRect.x += RepeatWidth();
-                       repeatCursor += RepeatWidth();
-               }
-
-               // right border
-               srcRect.x += RepeatWidth();
-               srcRect.w = BorderWidth();
-               SDL_BlitSurface(surface, &srcRect, dest, &destRect);
-
-               destRect.y += RepeatHeight();
-               hRepeatCursor += RepeatHeight();
-       }
+       // left border
+       Texture(surface, Vector<int>(BorderWidth(), RepeatHeight()), Vector<int>(offset.X(), offset.Y() + BorderHeight()))
+       .Render(dest, Vector<int>(position.X(), position.Y() + BorderHeight()), Vector<int>(position.X() + BorderWidth(), position.Y() + height - BorderHeight()));
+
+       // center fill
+       Texture(surface, RepeatSize(), Vector<int>(offset.X() + BorderWidth(), offset.Y() + BorderHeight()))
+       .Render(dest, position + BorderSize(), position + Vector<int>(width, height) - BorderSize());
+
+       // right border
+       Texture(surface, Vector<int>(BorderWidth(), RepeatHeight()), Vector<int>(offset.X() + BorderWidth() + RepeatWidth(), offset.Y() + BorderHeight()))
+       .Render(dest, Vector<int>(position.X() + width - BorderWidth(), position.Y() + BorderHeight()), Vector<int>(position.X() + width, position.Y() + height - BorderHeight()));
 
        // bottom-left corner
        srcRect.x = offset.X();
@@ -96,27 +68,17 @@ void Frame::Draw(SDL_Surface *dest, const Vector<int> &position, int width, int
        srcRect.w = BorderWidth();
        srcRect.h = BorderHeight();
        destRect.x = position.X();
+       destRect.y = position.Y() + height - BorderHeight();
        SDL_BlitSurface(surface, &srcRect, dest, &destRect);
 
        // bottom border
-       srcRect.x += BorderWidth();
-       srcRect.w = RepeatWidth();
-       destRect.x += BorderWidth();
-       repeatCursor = 0;
-       while (repeatCursor < fullRepeatWidth) {
-               SDL_BlitSurface(surface, &srcRect, dest, &destRect);
-               destRect.x += RepeatWidth();
-               repeatCursor += RepeatWidth();
-       }
-       if (fullRepeatWidth < fullRepeatWidth) {
-               srcRect.w = fullRepeatWidth - fullRepeatWidth;
-               SDL_BlitSurface(surface, &srcRect, dest, &destRect);
-               destRect.x += fullRepeatWidth - fullRepeatWidth;
-       }
+       Texture(surface, Vector<int>(RepeatWidth(), BorderHeight()), Vector<int>(offset.X() + BorderWidth(), offset.Y() + BorderHeight() + RepeatHeight()))
+       .Render(dest, Vector<int>(position.X() + BorderWidth(), position.Y() + height - BorderHeight()), Vector<int>(position.X() + width - BorderWidth(), position.Y() + height));
 
        // bottom-right corner
-       srcRect.x += RepeatWidth();
+       srcRect.x = offset.X() + BorderWidth() + RepeatWidth();
        srcRect.w = BorderWidth();
+       destRect.x = position.X() + width - BorderWidth();
        SDL_BlitSurface(surface, &srcRect, dest, &destRect);
 }
 
index 4dc60323571cbeb37134fe51d2bf6f911a231a70..179c457d6f9f4a2639f7f8ce416851fb1eeb3990 100644 (file)
@@ -13,6 +13,7 @@
 #include "Sprite.h"
 #include "../geometry/Vector.h"
 
+#include <algorithm>
 #include <vector>
 #include <SDL.h>
 
@@ -24,6 +25,7 @@ struct MenuProperties {
        const Font *font;
        const Font *disabledFont;
        const Sprite *cursor;
+       const Sprite *selectedCursor;
        int charsPerEntry;
        int rows;
        int rowGap;
@@ -34,16 +36,16 @@ struct MenuProperties {
        int charsPerAdditionalText;
        int additionalTextGap;
        char delimiter;
+       bool wrapX;
+       bool wrapY;
 
        MenuProperties()
-       : font(0), disabledFont(0), cursor(0)
-       , charsPerEntry(0), rows(0), rowGap(0)
-       , iconSpace(0), cols(0), colGap(0)
+       : font(0), disabledFont(0), cursor(0), selectedCursor(0)
+       , charsPerEntry(0), rows(1), rowGap(0)
+       , iconSpace(0), cols(1), colGap(0)
        , charsPerNumber(0), charsPerAdditionalText(0)
-       , additionalTextGap(0), delimiter(':') { }
-
-       MenuProperties(const Font *font, const Font *disabledFont, const Sprite *cursor, int charsPerEntry, int rows, int rowGap, int iconSpace, int cols, int colGap, int charsPerNumber, char delimiter, int charsPerAdditionalText, int additionalTextGap)
-       : font(font), disabledFont(disabledFont), cursor(cursor), charsPerEntry(charsPerEntry), rows(rows), rowGap(rowGap), iconSpace(iconSpace), cols(cols), colGap(colGap), charsPerNumber(charsPerNumber), charsPerAdditionalText(charsPerAdditionalText), additionalTextGap(additionalTextGap), delimiter(delimiter) { }
+       , additionalTextGap(0), delimiter(':')
+       , wrapX(false), wrapY(false) { }
 
        static void CreateTypeDescription();
        static void Construct(void *);
@@ -57,9 +59,16 @@ class Menu
 public:
        Menu();
        Menu(const MenuProperties &);
-       Menu(const Font *font, const Font *disabledFont, const Sprite *cursor, int charsPerEntry, int rows, int rowGap = 0, int iconSpace = 0, int cols = 1, int colGap = 0, int charsPerNumber = 0, char delimiter = ':', int charsPerAdditionalText = 0, int additionalTextGap = 0);
 
 public:
+       void SetInactive() { state = STATE_INACTIVE; }
+       void SetActive() { state = STATE_ACTIVE; }
+       void SetSelected() { state = STATE_SELECTED; }
+       void SetDualSelection() { state = STATE_DUAL; secondarySelection = selected; }
+       bool IsActive() const { return state == STATE_ACTIVE; }
+       bool HasSelected() const { return state == STATE_SELECTED; }
+       bool InDualMode() const { return state == STATE_DUAL; }
+
        int Width() const;
        int Height() const;
        int ColWidth() const;
@@ -72,12 +81,22 @@ public:
        int SelectedNumber() const { return entries[selected].number; }
        bool SelectedIsEnabled() const { return entries[selected].enabled; }
 
+       T &SecondarySelection() { return entries[secondarySelection].value; }
+       const T &SecondarySelection() const { return entries[secondarySelection].value; }
+       const char *SecondaryTitle() const { return entries[secondarySelection].title; }
+       int SecondaryNumber() const { return entries[secondarySelection].number; }
+       bool SecondaryIsEnabled() const { return entries[secondarySelection].enabled; }
+
+       void SwapSelected() { SwapEntriesAt(selected, secondarySelection); }
+       void SwapEntriesAt(int lhs, int rhs) { std::swap(entries[lhs], entries[rhs]); }
+
        void NextItem();
        void PreviousItem();
        void NextRow();
        void PreviousRow();
        void SelectIndex(int index);
        int SelectedIndex() const { return selected; }
+       int SecondaryIndex() const { return secondarySelection; }
        bool IsSelected(int index) const { return index == selected; }
 
        int EntryCount() const { return entries.size(); }
@@ -90,6 +109,7 @@ public:
        void Enable(int index) { entries[index].enabled = true; }
        void Reserve(int n) { entries.reserve(n); }
        void Clear() { entries.clear(); }
+       void ClearEntry(int at) { entries[at] = Entry(0, T(), false); }
 
        void Draw(SDL_Surface *dest, const geometry::Vector<int> &position) const;
 
@@ -110,7 +130,15 @@ private:
        };
        std::vector<Entry> entries;
        int selected;
+       int secondarySelection;
        int topRow;
+       enum State {
+               STATE_INACTIVE,
+               STATE_ACTIVE,
+               STATE_SELECTED,
+               STATE_DUAL,
+       };
+       State state;
 
 };
 
@@ -119,7 +147,9 @@ template<class T>
 Menu<T>::Menu()
 : MenuProperties()
 , selected(0)
-, topRow(0) {
+, secondarySelection(0)
+, topRow(0)
+, state(STATE_ACTIVE) {
 
 }
 
@@ -127,22 +157,9 @@ template<class T>
 Menu<T>::Menu(const MenuProperties &p)
 : MenuProperties(p)
 , selected(0)
-, topRow(0) {
-
-}
-
-template<class T>
-Menu<T>::Menu(const Font *font, const Font *disabledFont, const Sprite *cursor, int charsPerEntry, int rows, int rowGap, int iconSpace, int cols, int colGap, int charsPerNumber, char delimiter, int charsPerAdditionalText, int additionalTextGap)
-: MenuProperties(
-               font, disabledFont ? disabledFont : font,
-               cursor, charsPerEntry,
-               rows, rowGap, iconSpace,
-               cols, colGap, charsPerNumber,
-               delimiter,
-               charsPerAdditionalText,
-               additionalTextGap)
-, selected(0)
-, topRow(0) {
+, secondarySelection(0)
+, topRow(0)
+, state(STATE_ACTIVE) {
 
 }
 
@@ -173,22 +190,38 @@ int Menu<T>::Height() const {
 
 template<class T>
 void Menu<T>::NextItem() {
-       SelectIndex(selected + 1);
+       int index(selected + 1);
+       if (wrapX && index % cols == 0) {
+               index -= cols;
+       }
+       SelectIndex(index);
 }
 
 template<class T>
 void Menu<T>::PreviousItem() {
-       SelectIndex(selected - 1);
+       int index(selected - 1);
+       if (wrapX && selected % cols == 0) {
+               index += cols;
+       }
+       SelectIndex(index);
 }
 
 template<class T>
 void Menu<T>::NextRow() {
-       SelectIndex(selected + cols);
+       int index(selected + cols);
+       if (wrapY && index >= int(entries.size())) {
+               index -= entries.size();
+       }
+       SelectIndex(index);
 }
 
 template<class T>
 void Menu<T>::PreviousRow() {
-       SelectIndex(selected - cols);
+       int index(selected - cols);
+       if (wrapY && index < 0) {
+               index += entries.size();
+       }
+       SelectIndex(index);
 }
 
 template<class T>
@@ -214,6 +247,13 @@ void Menu<T>::Draw(SDL_Surface *dest, const geometry::Vector<int> &position) con
                geometry::Vector<int> iconOffset(
                                (i % cols) * (ColWidth() + colGap),
                                (i / cols) * RowHeight());
+
+               // Third column hack!
+               // This fixes the position of the "DROP" item in the inventory menu.
+               if (i % cols == 2) {
+                       iconOffset += geometry::Vector<int>(font->CharWidth(), 0);
+               }
+
                if (entries[start + i].icon) {
                        entries[start + i].icon->Draw(dest, position + iconOffset);
                }
@@ -234,13 +274,37 @@ void Menu<T>::Draw(SDL_Surface *dest, const geometry::Vector<int> &position) con
                if (charsPerNumber) {
                        usedFont->DrawChar(delimiter, dest, position + textOffset);
                        textOffset += geometry::Vector<int>(usedFont->CharWidth(), 0);
-                       usedFont->DrawNumber(entries[start + i].number, dest, position + textOffset);
+                       usedFont->DrawNumber(entries[start + i].number, dest, position + textOffset, charsPerNumber);
                }
        }
        geometry::Vector<int> cursorOffset(
                        (selected % cols) * (ColWidth() + colGap) - cursor->Width(),
                        ((selected - start) / cols) * RowHeight());
-       cursor->Draw(dest, position + cursorOffset);
+       // Third column hack!
+       // This fixes the position of the "DROP" item in the inventory menu.
+       if (selected % cols == 2) {
+               cursorOffset += geometry::Vector<int>(font->CharWidth(), 0);
+       }
+       switch (state) {
+               case STATE_INACTIVE:
+                       break;
+               case STATE_ACTIVE:
+                       cursor->Draw(dest, position + cursorOffset);
+                       break;
+               case STATE_SELECTED:
+                       selectedCursor->Draw(dest, position + cursorOffset);
+                       break;
+               case STATE_DUAL:
+                       cursor->Draw(dest, position + cursorOffset
+                                       - geometry::Vector<int>(selectedCursor->Width(), 0));
+                       if (secondarySelection >= start && secondarySelection <= end) {
+                               geometry::Vector<int> secondaryOffset(
+                                               (secondarySelection % cols) * (ColWidth() + colGap) - cursor->Width(),
+                                               ((secondarySelection - start) / cols) * RowHeight());
+                               selectedCursor->Draw(dest, position + secondaryOffset);
+                       }
+                       break;
+       }
 }
 
 }
diff --git a/src/graphics/Texture.cpp b/src/graphics/Texture.cpp
new file mode 100644 (file)
index 0000000..2bb0e97
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Texture.cpp
+ *
+ *  Created on: Oct 21, 2012
+ *      Author: holy
+ */
+
+#include "Texture.h"
+
+#include "../sdl/utility.h"
+
+using geometry::Vector;
+
+namespace graphics {
+
+Texture::Texture(
+               SDL_Surface *surface,
+               const Vector<int> &size,
+               const Vector<int> &offset)
+: surface(surface)
+, size(size)
+, offset(offset) {
+
+}
+
+Texture::~Texture() {
+
+}
+
+
+void Texture::Render(SDL_Surface *dest, const Vector<int> &from, const Vector<int> &to) const {
+       SDL_Rect destRect;
+       destRect.x = from.X();
+       destRect.y = from.Y();
+       if (!surface || size == Vector<int>()) {
+               destRect.w = to.X() - from.X();
+               destRect.h = to.Y() - from.Y();
+               SDL_FillRect(dest, &destRect, SDL_MapRGB(dest->format, 0xFF, 0x00, 0x00));
+               return;
+       }
+
+       SDL_Rect srcRect;
+       srcRect.x = offset.X();
+       srcRect.y = offset.Y();
+
+       for (destRect.y = from.Y(); destRect.y < to.Y(); destRect.y += size.Y()) {
+               srcRect.h = size.Y();
+               destRect.h = size.Y();
+               if (destRect.y + destRect.h > to.Y()) {
+                       srcRect.h = to.Y() - destRect.y;
+                       destRect.h = to.Y() - destRect.y;
+               }
+               for (destRect.x = from.X(); destRect.x < to.X(); destRect.x += size.X()) {
+                       srcRect.w = size.X();
+                       destRect.w = size.X();
+                       if (destRect.x + destRect.w > to.X()) {
+                               srcRect.w = to.X() - destRect.x;
+                               destRect.w = to.X() - destRect.x;
+                       }
+                       SDL_BlitSurface(surface, &srcRect, dest, &destRect);
+               }
+       }
+}
+
+}
diff --git a/src/graphics/Texture.h b/src/graphics/Texture.h
new file mode 100644 (file)
index 0000000..ecb516a
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Texture.h
+ *
+ *  Created on: Oct 21, 2012
+ *      Author: holy
+ */
+
+#ifndef GRAPHICS_TEXTURE_H_
+#define GRAPHICS_TEXTURE_H_
+
+#include "../geometry/Vector.h"
+
+#include <SDL.h>
+
+namespace graphics {
+
+class Texture {
+
+public:
+       explicit Texture(
+                       SDL_Surface *surface = 0,
+                       const geometry::Vector<int> &size = geometry::Vector<int>(),
+                       const geometry::Vector<int> &offset = geometry::Vector<int>());
+       ~Texture();
+
+public:
+       void Render(SDL_Surface *dest, const geometry::Vector<int> &from, const geometry::Vector<int> &to) const;
+
+public:
+       void SetSurface(SDL_Surface *s) { surface = s; }
+       void SetSize(const geometry::Vector<int> &s) { size = s; }
+       void SetOffset(const geometry::Vector<int> &o) { offset = o; }
+
+private:
+       SDL_Surface *surface;
+       geometry::Vector<int> size;
+       geometry::Vector<int> offset;
+
+};
+
+}
+
+#endif /* GRAPHICS_TEXTURE_H_ */
index 31dbf27f8bace3d9fc9815a83aa1b245969e079a..961890b3471981b6237744f42bd6c231bdc0900e 100644 (file)
@@ -24,6 +24,7 @@ class Menu;
 struct MenuProperties;
 class SimpleAnimation;
 class Sprite;
+class Texture;
 
 }
 
index f70103a0dd411da6755c9cd85240f320855a5ee8..f3103995f27b92677c714d0991f7f03d44ed1d51 100644 (file)
@@ -30,6 +30,7 @@
 #include "graphics/Menu.h"
 #include "graphics/SimpleAnimation.h"
 #include "graphics/Sprite.h"
+#include "graphics/Texture.h"
 #include "loader/Caster.h"
 #include "loader/Interpreter.h"
 #include "loader/ParsedSource.h"
@@ -41,6 +42,7 @@
 #include "map/MapState.h"
 #include "map/Tile.h"
 #include "map/Trigger.h"
+#include "menu/Resources.h"
 #include "sdl/InitImage.h"
 #include "sdl/InitScreen.h"
 #include "sdl/InitSDL.h"
@@ -62,8 +64,10 @@ using battle::Monster;
 using battle::PartyLayout;
 using common::GameConfig;
 using common::GameState;
+using common::Hero;
 using common::Spell;
 using geometry::Vector;
+using graphics::Texture;
 using loader::Caster;
 using loader::Interpreter;
 using loader::ParsedSource;
@@ -83,8 +87,8 @@ using std::string;
 using std::vector;
 
 int main(int argc, char **argv) {
-       const int width = 800;
-       const int height = 480;
+       const int width = 512;
+       const int height = 448;
 
        const float walkSpeed = 128.0f;
 
@@ -188,6 +192,7 @@ int main(int argc, char **argv) {
                gameState.party[1] = &gameState.heroes[1];
                gameState.party[2] = &gameState.heroes[2];
                gameState.party[3] = &gameState.heroes[3];
+               gameState.partySize = 4;
 
                GameConfig gameConfig;
                gameConfig.state = &gameState;
@@ -218,41 +223,48 @@ int main(int argc, char **argv) {
                gameState.heroes[0].AddSpell(valorSpell);
                gameState.heroes[1].AddSpell(valorSpell);
 
+               gameState.inventory.Add(caster.GetItem("zirconPlateItem"));
                gameState.inventory.Add(caster.GetItem("antidoteItem"), 9);
+               gameState.inventory.Add(caster.GetItem("powerRingItem"));
                gameState.inventory.Add(caster.GetItem("magicJarItem"), 4);
+               gameState.inventory.Add(caster.GetItem("sProRingItem"));
                gameState.inventory.Add(caster.GetItem("hiPotionItem"), 4);
+               gameState.inventory.Add(caster.GetItem("powerRingItem"));
                gameState.inventory.Add(caster.GetItem("powerPotionItem"), 4);
+               gameState.inventory.Add(caster.GetItem("zircoSwordItem"));
                gameState.inventory.Add(caster.GetItem("escapeItem"), 2);
+               gameState.inventory.Add(caster.GetItem("zircoHelmetItem"));
                gameState.inventory.Add(caster.GetItem("sleepBallItem"), 1);
-
-               gameState.heroes[0].SetWeapon(caster.GetItem("zircoSwordItem"));
-               gameState.heroes[0].SetArmor(caster.GetItem("zirconArmorItem"));
-               gameState.heroes[0].SetShield(caster.GetItem("holyShieldItem"));
-               gameState.heroes[0].SetHelmet(caster.GetItem("legendHelmItem"));
-               gameState.heroes[0].SetRing(caster.GetItem("sProRingItem"));
-               gameState.heroes[0].SetJewel(caster.GetItem("evilJewelItem"));
-
-//             gameState.heroes[1].SetWeapon(cst.GetItem("zircoWhipItem"));
-               gameState.heroes[1].SetArmor(caster.GetItem("zirconPlateItem"));
-               gameState.heroes[1].SetShield(caster.GetItem("zircoGlovesItem"));
-               gameState.heroes[1].SetHelmet(caster.GetItem("holyCapItem"));
-               gameState.heroes[1].SetRing(caster.GetItem("ghostRingItem"));
-               gameState.heroes[1].SetJewel(caster.GetItem("eagleRockItem"));
-
-//             gameState.heroes[2].SetWeapon(cst.GetItem("zircoAxItem"));
-               gameState.heroes[2].SetArmor(caster.GetItem("zirconArmorItem"));
-               gameState.heroes[2].SetShield(caster.GetItem("megaShieldItem"));
-               gameState.heroes[2].SetHelmet(caster.GetItem("zircoHelmetItem"));
-               gameState.heroes[2].SetRing(caster.GetItem("powerRingItem"));
-               gameState.heroes[2].SetJewel(caster.GetItem("evilJewelItem"));
+               gameState.inventory.Add(caster.GetItem("zirconPlateItem"));
+
+               gameState.heroes[0].SetEquipment(Hero::EQUIP_WEAPON, caster.GetItem("zircoSwordItem"));
+               gameState.heroes[0].SetEquipment(Hero::EQUIP_ARMOR, caster.GetItem("zirconArmorItem"));
+               gameState.heroes[0].SetEquipment(Hero::EQUIP_SHIELD, caster.GetItem("holyShieldItem"));
+               gameState.heroes[0].SetEquipment(Hero::EQUIP_HELMET, caster.GetItem("legendHelmItem"));
+               gameState.heroes[0].SetEquipment(Hero::EQUIP_RING, caster.GetItem("sProRingItem"));
+               gameState.heroes[0].SetEquipment(Hero::EQUIP_JEWEL, caster.GetItem("evilJewelItem"));
+
+//             gameState.heroes[1].SetEquipment(Hero::EQUIP_WEAPON, caster.GetItem("zircoWhipItem"));
+               gameState.heroes[1].SetEquipment(Hero::EQUIP_ARMOR, caster.GetItem("zirconPlateItem"));
+               gameState.heroes[1].SetEquipment(Hero::EQUIP_SHIELD, caster.GetItem("zircoGlovesItem"));
+               gameState.heroes[1].SetEquipment(Hero::EQUIP_HELMET, caster.GetItem("holyCapItem"));
+               gameState.heroes[1].SetEquipment(Hero::EQUIP_RING, caster.GetItem("ghostRingItem"));
+               gameState.heroes[1].SetEquipment(Hero::EQUIP_JEWEL, caster.GetItem("eagleRockItem"));
+
+//             gameState.heroes[2].SetEquipment(Hero::EQUIP_WEAPON, caster.GetItem("zircoAxItem"));
+               gameState.heroes[2].SetEquipment(Hero::EQUIP_ARMOR, caster.GetItem("zirconArmorItem"));
+               gameState.heroes[2].SetEquipment(Hero::EQUIP_SHIELD, caster.GetItem("megaShieldItem"));
+               gameState.heroes[2].SetEquipment(Hero::EQUIP_HELMET, caster.GetItem("zircoHelmetItem"));
+               gameState.heroes[2].SetEquipment(Hero::EQUIP_RING, caster.GetItem("powerRingItem"));
+               gameState.heroes[2].SetEquipment(Hero::EQUIP_JEWEL, caster.GetItem("evilJewelItem"));
 
                // NOTE: this is actually Artea equipment
-//             gameState.heroes[3].SetWeapon(cst.GetItem("lizardBlowItem"));
-               gameState.heroes[3].SetArmor(caster.GetItem("holyRobeItem"));
-               gameState.heroes[3].SetShield(caster.GetItem("zircoGlovesItem"));
-               gameState.heroes[3].SetHelmet(caster.GetItem("holyCapItem"));
-               gameState.heroes[3].SetRing(caster.GetItem("rocketRingItem"));
-               gameState.heroes[3].SetJewel(caster.GetItem("krakenRockItem"));
+//             gameState.heroes[3].SetEquipment(Hero::EQUIP_WEAPON, caster.GetItem("lizardBlowItem"));
+               gameState.heroes[3].SetEquipment(Hero::EQUIP_ARMOR, caster.GetItem("holyRobeItem"));
+               gameState.heroes[3].SetEquipment(Hero::EQUIP_SHIELD, caster.GetItem("zircoGlovesItem"));
+               gameState.heroes[3].SetEquipment(Hero::EQUIP_HELMET, caster.GetItem("holyCapItem"));
+               gameState.heroes[3].SetEquipment(Hero::EQUIP_RING, caster.GetItem("rocketRingItem"));
+               gameState.heroes[3].SetEquipment(Hero::EQUIP_JEWEL, caster.GetItem("krakenRockItem"));
 
                gameState.heroes[0].MapEntity().Position() = Vector<float>(64, 128);
 
@@ -268,6 +280,200 @@ int main(int argc, char **argv) {
                gameState.heroes[3].MapEntity().SetFlags(Entity::FLAG_NONBLOCKING);
                gameState.heroes[2].MapEntity().AddFollower(&gameState.heroes[3].MapEntity());
 
+               menu::Resources menuResources;
+               gameConfig.menuResources = &menuResources;
+
+               Texture menubg;
+               menubg.SetSurface(IMG_Load("test-data/menubg.png"));
+               menubg.SetSize(Vector<int>(64, 64));
+               menuResources.menubg = &menubg;
+
+               menuResources.statusFont = gameConfig.battleResources->normalFont;
+
+               graphics::Sprite statusLabels(IMG_Load("test-data/status-labels.png"), 32, 16);
+               menuResources.statusLabels = &statusLabels;
+
+               graphics::Frame statusFrame(IMG_Load("test-data/status-frame.png"), 32, 32, 32, 32);
+               menuResources.statusFrame = &statusFrame;
+
+               graphics::Sprite menuFontSprite(IMG_Load("test-data/menu-font.png"), 16, 16);
+               graphics::Font menuFont(&menuFontSprite, 0, -2);
+               graphics::Sprite menuInactiveFontSprite(IMG_Load("test-data/menu-font-inactive.png"), 16, 16);
+               graphics::Font menuInactiveFont(&menuInactiveFontSprite, 0, -2);
+
+               menuResources.normalFont = &menuFont;
+               menuResources.inactiveFont = &menuInactiveFont;
+
+               graphics::Sprite menuCursor(IMG_Load("test-data/menu-cursor.png"), 32, 16);
+               menuResources.menuCursor = &menuCursor;
+               graphics::Sprite menuActiveCursor(IMG_Load("test-data/menu-cursor-active.png"), 32, 18);
+               menuResources.menuActiveCursor = &menuActiveCursor;
+
+               graphics::MenuProperties mainMenuProperties;
+               mainMenuProperties.cols = 2;
+               mainMenuProperties.rows = 4;
+               mainMenuProperties.charsPerEntry = 8;
+               mainMenuProperties.rowGap = 8;
+               mainMenuProperties.colGap = 32;
+               mainMenuProperties.cursor = &menuCursor;
+               mainMenuProperties.font = &menuFont;
+               mainMenuProperties.wrapX = true;
+               mainMenuProperties.wrapY = true;
+               menuResources.mainMenuProperties = &mainMenuProperties;
+
+               menuResources.mainMenuItemText = "ITEM";
+               menuResources.mainMenuSpellText = "SPELL";
+               menuResources.mainMenuCapsuleText = "CAPSULE";
+               menuResources.mainMenuEquipmentText = "EQUIP";
+               menuResources.mainMenuStatusText = "STATUS";
+               menuResources.mainMenuChangeText = "CHANGE";
+               menuResources.mainMenuConfigText = "CONFIG";
+               menuResources.mainMenuScenarioText = "SCENARIO";
+
+               menuResources.mainMenuTimeText = "TIME";
+               menuResources.mainMenuGoldText = "GOLD";
+
+               graphics::Sprite heroCursor(IMG_Load("test-data/hero-cursor.png"), 64, 16);
+               menuResources.heroCursor = &heroCursor;
+               menuResources.heroCursorBlinkTime = 532;
+
+               menuResources.noEquipmentText = "No equip";
+
+               graphics::Sprite shoulderNav(IMG_Load("test-data/shoulder-nav.png"), 160, 16);
+               menuResources.shoulderNav = &shoulderNav;
+
+               menuResources.atpLabel = "ATP";
+               menuResources.dfpLabel = "DFP";
+               menuResources.strLabel = "STR";
+               menuResources.aglLabel = "AGL";
+               menuResources.intLabel = "INT";
+               menuResources.gutLabel = "GUT";
+               menuResources.mgrLabel = "MGR";
+
+               menuResources.ipLabel = "IP";
+               menuResources.experienceLabel = "NOW EXP";
+               menuResources.nextLevelLabel = "NEXT LEVEL";
+
+               graphics::MenuProperties statusMenuProperties;
+               statusMenuProperties.cols = 2;
+               statusMenuProperties.rows = 1;
+               statusMenuProperties.charsPerEntry = 6;
+               statusMenuProperties.rowGap = 0;
+               statusMenuProperties.colGap = 16;
+               statusMenuProperties.cursor = &menuCursor;
+               statusMenuProperties.font = &menuFont;
+               statusMenuProperties.wrapX = true;
+               menuResources.statusMenuProperties = &statusMenuProperties;
+
+               menuResources.nextLabel = "NEXT";
+               menuResources.returnLabel = "RETURN";
+
+               graphics::MenuProperties itemMenuProperties;
+               itemMenuProperties.cols = 3;
+               itemMenuProperties.rows = 1;
+               itemMenuProperties.charsPerEntry = 5;
+               itemMenuProperties.rowGap = 8;
+               itemMenuProperties.colGap = 16;
+               itemMenuProperties.cursor = &menuCursor;
+               itemMenuProperties.selectedCursor = &menuActiveCursor;
+               itemMenuProperties.font = &menuFont;
+               itemMenuProperties.wrapX = true;
+               itemMenuProperties.wrapY = true;
+               menuResources.itemMenuProperties = &itemMenuProperties;
+               menuResources.itemMenuUseText = "USE";
+               menuResources.itemMenuSortText = "SORT";
+               menuResources.itemMenuDropText = "DROP";
+
+               graphics::MenuProperties inventoryMenuProperties;
+               inventoryMenuProperties.cols = 1;
+               inventoryMenuProperties.rows = 6;
+               inventoryMenuProperties.charsPerEntry = 13;
+               inventoryMenuProperties.rowGap = 8;
+               inventoryMenuProperties.cursor = &menuCursor;
+               inventoryMenuProperties.selectedCursor = &menuActiveCursor;
+               inventoryMenuProperties.font = &menuFont;
+               inventoryMenuProperties.disabledFont = &menuInactiveFont;
+               inventoryMenuProperties.iconSpace = 16;
+               inventoryMenuProperties.charsPerNumber = 2;
+               inventoryMenuProperties.delimiter = ':';
+               menuResources.inventoryMenuProperties = &inventoryMenuProperties;
+
+               graphics::MenuProperties spellMenuProperties;
+               spellMenuProperties.cols = 2;
+               spellMenuProperties.rows = 6;
+               spellMenuProperties.charsPerEntry = 8;
+               spellMenuProperties.rowGap = 8;
+               spellMenuProperties.colGap = 48;
+               spellMenuProperties.cursor = &menuCursor;
+               spellMenuProperties.selectedCursor = &menuActiveCursor;
+               spellMenuProperties.font = &menuFont;
+               spellMenuProperties.disabledFont = &menuInactiveFont;
+               spellMenuProperties.iconSpace = 0;
+               spellMenuProperties.charsPerNumber = 2;
+               spellMenuProperties.delimiter = ':';
+               menuResources.spellMenuProperties = &spellMenuProperties;
+
+               graphics::MenuProperties equipmentActionMenuProperties;
+               equipmentActionMenuProperties.cols = 1;
+               equipmentActionMenuProperties.rows = 5;
+               equipmentActionMenuProperties.charsPerEntry = 10;
+               equipmentActionMenuProperties.rowGap = 8;
+               equipmentActionMenuProperties.cursor = &menuCursor;
+               equipmentActionMenuProperties.selectedCursor = &menuActiveCursor;
+               equipmentActionMenuProperties.font = &menuFont;
+               equipmentActionMenuProperties.iconSpace = 0;
+               menuResources.equipmentActionMenuProperties = &equipmentActionMenuProperties;
+
+               graphics::MenuProperties equipmentMenuProperties;
+               equipmentMenuProperties.cols = 1;
+               equipmentMenuProperties.rows = 6;
+               equipmentMenuProperties.charsPerEntry = 12;
+               equipmentMenuProperties.rowGap = 16;
+               equipmentMenuProperties.cursor = &menuCursor;
+               equipmentMenuProperties.selectedCursor = &menuActiveCursor;
+               equipmentMenuProperties.font = menuResources.statusFont;
+               equipmentMenuProperties.iconSpace = 16;
+               equipmentMenuProperties.wrapY = true;
+               menuResources.equipmentMenuProperties = &equipmentMenuProperties;
+
+               menuResources.equipMenuEquipLabel = "EQUIP";
+               menuResources.equipMenuStrongestLabel = "STRONGEST";
+               menuResources.equipMenuRemoveLabel = "REMOVE";
+               menuResources.equipMenuRemoveAllLabel = "REMOVE ALL";
+               menuResources.equipMenuDropLabel = "DROP";
+
+               graphics::MenuProperties configMenuProperties;
+               configMenuProperties.cols = 1;
+               configMenuProperties.rows = 4;
+               configMenuProperties.charsPerEntry = 8;
+               configMenuProperties.rowGap = 32;
+               configMenuProperties.cursor = &menuCursor;
+               configMenuProperties.font = &menuFont;
+               configMenuProperties.wrapY = true;
+               menuResources.configMenuProperties = &configMenuProperties;
+
+               menuResources.configMessageSpeedLabel = "MESSAGE\n   SPEED";
+               menuResources.configMessageSpeedFast = "FAST";
+               menuResources.configMessageSpeedNormal = "NORMAL";
+               menuResources.configMessageSpeedSlow = "SLOW";
+               menuResources.configBattleCursorLabel = "BATTLE\n  CURSOR";
+               menuResources.configStatusCursorLabel = "STATUS\n  CURSOR";
+               menuResources.configCursorClear = "CLEAR";
+               menuResources.configCursorMemory = "MEMORY";
+               menuResources.configMusicLabel = "MUSIC";
+               menuResources.configMusicStereo = "STEREO";
+               menuResources.configMusicMono = "MONO";
+
+               graphics::MenuProperties scenarioMenuProperties;
+               scenarioMenuProperties.cols = 1;
+               scenarioMenuProperties.rows = 6;
+               scenarioMenuProperties.charsPerEntry = 14;
+               scenarioMenuProperties.rowGap = 8;
+               scenarioMenuProperties.cursor = &menuCursor;
+               scenarioMenuProperties.font = &menuFont;
+               menuResources.scenarioMenuProperties = &scenarioMenuProperties;
+               menuResources.scenarioMenuHeadline = "SCENARIO ITEM";
+
                InitScreen screen(width, height);
 
                app::State *state(0);
index 5ec95071963cbfccac3f3920b3ffc0c1ca56343d..af156304b9412c6523e7aec39f7b0547e6fadd3f 100644 (file)
@@ -17,6 +17,7 @@
 #include "../common/GameConfig.h"
 #include "../common/GameState.h"
 #include "../graphics/ColorFade.h"
+#include "../menu/PartyMenu.h"
 
 #include <algorithm>
 
@@ -26,6 +27,7 @@ using battle::BattleState;
 using common::GameConfig;
 using geometry::Vector;
 using graphics::ColorFade;
+using menu::PartyMenu;
 
 namespace map {
 
@@ -69,6 +71,11 @@ void MapState::OnResize(int width, int height) {
 
 
 void MapState::HandleEvents(const Input &input) {
+       if (input.JustPressed(Input::ACTION_X)) {
+               Ctrl().PushState(new PartyMenu(game));
+               return;
+       }
+
        if (!controlled) return;
 
        if (input.IsDown(Input::PAD_UP)) {
diff --git a/src/menu/ChangeHero.cpp b/src/menu/ChangeHero.cpp
new file mode 100644 (file)
index 0000000..f1d871d
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * ChangeHero.cpp
+ *
+ *  Created on: Nov 4, 2012
+ *      Author: holy
+ */
+
+#include "ChangeHero.h"
+
+#include "HeroStatus.h"
+#include "PartyMenu.h"
+#include "Resources.h"
+#include "SelectHero.h"
+#include "../app/Application.h"
+#include "../app/Input.h"
+#include "../common/GameConfig.h"
+#include "../common/GameState.h"
+
+#include <algorithm>
+
+using app::Input;
+using geometry::Vector;
+using std::swap;
+
+namespace menu {
+
+ChangeHero::ChangeHero(PartyMenu *parent)
+: parent(parent)
+, highlight(0)
+, selection(0) {
+
+}
+
+
+void ChangeHero::OnEnterState(SDL_Surface *) {
+       const HeroStatus &status(parent->GetHeroStatus(0));
+       highlight = SDL_CreateRGBSurface(0, status.Width(), status.Height(), 32, 0xFF000000, 0xFF0000, 0xFF00, 0);
+       SDL_FillRect(highlight, 0, SDL_MapRGB(highlight->format, 0xFF, 0xFF, 0xFF));
+       SDL_SetAlpha(highlight, SDL_SRCALPHA|SDL_RLEACCEL, 0x20);
+}
+
+void ChangeHero::OnExitState(SDL_Surface *) {
+       SDL_FreeSurface(highlight);
+}
+
+void ChangeHero::OnResumeState(SDL_Surface *) {
+       if (selection < 0) {
+               Ctrl().PopState();
+       } else {
+               selection = -1;
+               Ctrl().PushState(new SelectHero(this, parent, this, OnHeroSelected));
+       }
+}
+
+void ChangeHero::OnPauseState(SDL_Surface *) {
+
+}
+
+
+void ChangeHero::OnResize(int width, int height) {
+
+}
+
+
+int ChangeHero::Width() const {
+       return parent->Width();
+}
+
+int ChangeHero::Height() const {
+       return parent->Height();
+}
+
+
+void ChangeHero::HandleEvents(const Input &input) {
+
+}
+
+void ChangeHero::UpdateWorld(float deltaT) {
+
+}
+
+void ChangeHero::Render(SDL_Surface *screen) {
+       Vector<int> offset((screen->w - Width()) / 2, (screen->h - Height()) / 2);
+
+       parent->RenderBackground(screen);
+       RenderHighlight(screen, offset);
+       parent->RenderHeros(screen, offset);
+       parent->RenderMenu(screen, offset + Vector<int>(8 * parent->Res().normalFont->CharWidth(), 13 * parent->Res().normalFont->CharHeight() + parent->Res().normalFont->CharHeight() / 8));
+       parent->RenderInfo(screen, offset + Vector<int>(14 * parent->Res().normalFont->CharWidth(), 21 * parent->Res().normalFont->CharHeight() + parent->Res().normalFont->CharHeight() / 8));
+}
+
+void ChangeHero::RenderHighlight(SDL_Surface *screen, const Vector<int> &offset) const {
+       if (selection < 0) return;
+       Vector<int> statusOffset(parent->StatusOffset(selection));
+       statusOffset -= Vector<int>(0, parent->Res().normalFont->CharHeight() / 8);
+       SDL_Rect rect;
+       rect.x = statusOffset.X();
+       rect.y = statusOffset.Y();
+       SDL_BlitSurface(highlight, 0, screen, &rect);
+}
+
+
+void ChangeHero::OnHeroSelected(void *ref, int index) {
+       ChangeHero *self(reinterpret_cast<ChangeHero *>(ref));
+       self->SelectedHero(index);
+}
+
+void ChangeHero::SelectedHero(int index) {
+       if (selection < 0) {
+               selection = index;
+       } else {
+               if (index != selection) {
+                       swap(parent->Game().state->party[selection],
+                                       parent->Game().state->party[index]);
+               }
+               selection = -1;
+       }
+}
+
+}
diff --git a/src/menu/ChangeHero.h b/src/menu/ChangeHero.h
new file mode 100644 (file)
index 0000000..6bb5f31
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * ChangeHero.h
+ *
+ *  Created on: Nov 4, 2012
+ *      Author: holy
+ */
+
+#ifndef MENU_CHANGEHERO_H_
+#define MENU_CHANGEHERO_H_
+
+#include "fwd.h"
+#include "../app/State.h"
+#include "../geometry/Vector.h"
+
+#include <SDL.h>
+
+namespace menu {
+
+class ChangeHero
+: public app::State {
+
+public:
+       explicit ChangeHero(PartyMenu *parent);
+
+public:
+       virtual void HandleEvents(const app::Input &);
+       virtual void UpdateWorld(float deltaT);
+       virtual void Render(SDL_Surface *);
+
+public:
+       int Width() const;
+       int Height() const;
+
+private:
+       virtual void OnEnterState(SDL_Surface *screen);
+       virtual void OnExitState(SDL_Surface *screen);
+       virtual void OnResumeState(SDL_Surface *screen);
+       virtual void OnPauseState(SDL_Surface *screen);
+
+       virtual void OnResize(int width, int height);
+
+       void SelectedHero(int index);
+
+       void RenderHighlight(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+
+       static void OnHeroSelected(void *, int);
+
+private:
+       PartyMenu *parent;
+       SDL_Surface *highlight;
+       int selection;
+
+};
+
+}
+
+#endif /* MENU_CHANGEHERO_H_ */
diff --git a/src/menu/ConfigMenu.cpp b/src/menu/ConfigMenu.cpp
new file mode 100644 (file)
index 0000000..b5a2b7d
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * ConfigMenu.cpp
+ *
+ *  Created on: Nov 29, 2012
+ *      Author: holy
+ */
+
+#include "ConfigMenu.h"
+
+#include "PartyMenu.h"
+#include "Resources.h"
+#include "../common/GameConfig.h"
+#include "../common/GameState.h"
+#include "../graphics/Font.h"
+#include "../graphics/Frame.h"
+
+using app::Input;
+using common::GameState;
+using geometry::Vector;
+using graphics::Font;
+using graphics::Frame;
+
+namespace menu {
+
+ConfigMenu::ConfigMenu(PartyMenu *parent)
+: parent(parent)
+, configMenu(*parent->Res().configMenuProperties) {
+       configMenu.Add(parent->Res().configMessageSpeedLabel, 0);
+       configMenu.Add(parent->Res().configBattleCursorLabel, 1);
+       configMenu.Add(parent->Res().configStatusCursorLabel, 2);
+       configMenu.Add(parent->Res().configMusicLabel, 3);
+}
+
+
+void ConfigMenu::OnEnterState(SDL_Surface *) {
+
+}
+
+void ConfigMenu::OnExitState(SDL_Surface *) {
+
+}
+
+void ConfigMenu::OnResumeState(SDL_Surface *) {
+
+}
+
+void ConfigMenu::OnPauseState(SDL_Surface *) {
+
+}
+
+
+void ConfigMenu::OnResize(int width, int height) {
+
+}
+
+
+int ConfigMenu::Width() const {
+       return parent->Width();
+}
+
+int ConfigMenu::Height() const {
+       return parent->Height();
+}
+
+
+void ConfigMenu::HandleEvents(const Input &input) {
+       if (input.JustPressed(Input::ACTION_B)) {
+               Ctrl().PopState();
+       }
+       if (input.JustPressed(Input::PAD_DOWN)) {
+               configMenu.NextRow();
+       }
+       if (input.JustPressed(Input::PAD_UP)) {
+               configMenu.PreviousRow();
+       }
+
+       GameState &state(*parent->Game().state);
+       int *property(0);
+       int mod(0);
+       switch (configMenu.Selected()) {
+               case 0:
+                       property = &state.messageSpeed;
+                       mod = 3;
+                       break;
+               case 1:
+                       property = &state.battleCursor;
+                       mod = 2;
+                       break;
+               case 2:
+                       property = &state.statusCursor;
+                       mod = 2;
+                       break;
+               case 3:
+                       property = &state.music;
+                       mod = 2;
+                       break;
+       }
+       if (input.JustPressed(Input::ACTION_A) || input.JustPressed(Input::PAD_RIGHT)) {
+               *property = (*property + 1) % mod;
+       }
+       if (input.JustPressed(Input::PAD_LEFT)) {
+               *property = (*property + mod - 1) % mod;
+       }
+}
+
+void ConfigMenu::UpdateWorld(float deltaT) {
+
+}
+
+void ConfigMenu::Render(SDL_Surface *screen) {
+       const Font &font(*parent->Res().normalFont);
+       const Vector<int> offset((screen->w - Width()) / 2, (screen->h - Height()) / 2);
+       const Vector<int> headlineOffset(
+                       font.CharWidth(), 2 * font.CharHeight() - font.CharHeight() / 8);
+       const Vector<int> menuOffset(
+                       font.CharWidth(), 5 * font.CharHeight() - font.CharHeight() / 8);
+
+       parent->RenderBackground(screen);
+       RenderHeadline(screen, offset + headlineOffset);
+       RenderMenu(screen, offset + menuOffset);
+}
+
+void ConfigMenu::RenderHeadline(SDL_Surface *screen, const geometry::Vector<int> &offset) const {
+       const Font &font(*parent->Res().normalFont);
+       const Frame &frame(*parent->Res().statusFrame);
+       const Vector<int> textOffset(
+                       2 * font.CharWidth(), font.CharHeight());
+
+       frame.Draw(screen, offset, 10 * font.CharWidth(), 3 * font.CharHeight());
+       font.DrawString(parent->Res().mainMenuConfigText, screen, offset + textOffset, 6);
+}
+
+void ConfigMenu::RenderMenu(SDL_Surface *screen, const geometry::Vector<int> &offset) const {
+       const Resources &res(parent->Res());
+       const Font &font(*res.normalFont);
+       const Font &inactiveFont(*res.inactiveFont);
+       const Frame &frame(*res.statusFrame);
+       const GameState &state(*parent->Game().state);
+       const Vector<int> menuOffset(
+                       3 * font.CharWidth(), 2 * font.CharHeight());
+
+       frame.Draw(screen, offset, 30 * font.CharWidth(), 14 * font.CharHeight());
+       configMenu.Draw(screen, offset + menuOffset);
+
+       Vector<int> lineOffset(
+                       menuOffset.X() + configMenu.Width() + 2 * font.CharWidth(),
+                       menuOffset.Y());
+       Vector<int> colOffset(lineOffset);
+
+       if (state.messageSpeed == GameState::MESSAGE_SPEED_FAST) {
+               font.DrawString(res.configMessageSpeedFast, screen, offset + colOffset);
+               colOffset.X() += font.StringWidth(res.configMessageSpeedFast) + font.CharWidth();
+       } else {
+               inactiveFont.DrawString(res.configMessageSpeedFast, screen, offset + colOffset);
+               colOffset.X() += inactiveFont.StringWidth(res.configMessageSpeedFast) + inactiveFont.CharWidth();
+       }
+       if (state.messageSpeed == GameState::MESSAGE_SPEED_NORMAL) {
+               font.DrawString(res.configMessageSpeedNormal, screen, offset + colOffset);
+               colOffset.X() += font.StringWidth(res.configMessageSpeedNormal) + font.CharWidth();
+       } else {
+               inactiveFont.DrawString(res.configMessageSpeedNormal, screen, offset + colOffset);
+               colOffset.X() += inactiveFont.StringWidth(res.configMessageSpeedNormal) + inactiveFont.CharWidth();
+       }
+       if (state.messageSpeed == GameState::MESSAGE_SPEED_SLOW) {
+               font.DrawString(res.configMessageSpeedSlow, screen, offset + colOffset);
+       } else {
+               inactiveFont.DrawString(res.configMessageSpeedSlow, screen, offset + colOffset);
+       }
+
+       lineOffset.Y() += configMenu.RowHeight();
+       colOffset = lineOffset;
+
+       if (state.battleCursor == GameState::CURSOR_CLEAR) {
+               font.DrawString(res.configCursorClear, screen, offset + colOffset);
+               colOffset.X() += font.StringWidth(res.configCursorClear) + 2 * font.CharWidth();
+       } else {
+               inactiveFont.DrawString(res.configCursorClear, screen, offset + colOffset);
+               colOffset.X() += inactiveFont.StringWidth(res.configCursorClear) + 2 * inactiveFont.CharWidth();
+       }
+       if (state.battleCursor == GameState::CURSOR_MEMORY) {
+               font.DrawString(res.configCursorMemory, screen, offset + colOffset);
+       } else {
+               inactiveFont.DrawString(res.configCursorMemory, screen, offset + colOffset);
+       }
+
+       lineOffset.Y() += configMenu.RowHeight();
+       colOffset = lineOffset;
+
+       if (state.statusCursor == GameState::CURSOR_CLEAR) {
+               font.DrawString(res.configCursorClear, screen, offset + colOffset);
+               colOffset.X() += font.StringWidth(res.configCursorClear) + 2 * font.CharWidth();
+       } else {
+               inactiveFont.DrawString(res.configCursorClear, screen, offset + colOffset);
+               colOffset.X() += inactiveFont.StringWidth(res.configCursorClear) + 2 * inactiveFont.CharWidth();
+       }
+       if (state.statusCursor == GameState::CURSOR_MEMORY) {
+               font.DrawString(res.configCursorMemory, screen, offset + colOffset);
+       } else {
+               inactiveFont.DrawString(res.configCursorMemory, screen, offset + colOffset);
+       }
+
+       lineOffset.Y() += configMenu.RowHeight();
+       colOffset = lineOffset;
+
+       if (state.music == GameState::MUSIC_STEREO) {
+               font.DrawString(res.configMusicStereo, screen, offset + colOffset);
+               colOffset.X() += font.StringWidth(res.configMusicStereo) + font.CharWidth();
+       } else {
+               inactiveFont.DrawString(res.configMusicStereo, screen, offset + colOffset);
+               colOffset.X() += inactiveFont.StringWidth(res.configMusicStereo) + inactiveFont.CharWidth();
+       }
+       if (state.music == GameState::MUSIC_MONO) {
+               font.DrawString(res.configMusicMono, screen, offset + colOffset);
+       } else {
+               inactiveFont.DrawString(res.configMusicMono, screen, offset + colOffset);
+       }
+}
+
+}
diff --git a/src/menu/ConfigMenu.h b/src/menu/ConfigMenu.h
new file mode 100644 (file)
index 0000000..8bb81f4
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * ConfigMenu.h
+ *
+ *  Created on: Nov 29, 2012
+ *      Author: holy
+ */
+
+#ifndef MENU_CONFIGMENU_H_
+#define MENU_CONFIGMENU_H_
+
+#include "fwd.h"
+#include "../app/State.h"
+#include "../geometry/Vector.h"
+#include "../graphics/Menu.h"
+
+namespace menu {
+
+class ConfigMenu
+: public app::State {
+
+public:
+       explicit ConfigMenu(PartyMenu *parent);
+
+public:
+       virtual void HandleEvents(const app::Input &);
+       virtual void UpdateWorld(float deltaT);
+       virtual void Render(SDL_Surface *);
+
+public:
+       int Width() const;
+       int Height() const;
+
+private:
+       virtual void OnEnterState(SDL_Surface *screen);
+       virtual void OnExitState(SDL_Surface *screen);
+       virtual void OnResumeState(SDL_Surface *screen);
+       virtual void OnPauseState(SDL_Surface *screen);
+
+       virtual void OnResize(int width, int height);
+
+       void RenderHeadline(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderMenu(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+
+private:
+       PartyMenu *parent;
+       graphics::Menu<int> configMenu;
+
+};
+
+}
+
+#endif /* MENU_CONFIGMENU_H_ */
diff --git a/src/menu/EquipMenu.cpp b/src/menu/EquipMenu.cpp
new file mode 100644 (file)
index 0000000..b917c9a
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * EquipMenu.cpp
+ *
+ *  Created on: Nov 18, 2012
+ *      Author: holy
+ */
+
+#include "EquipMenu.h"
+
+#include "HeroStatus.h"
+#include "PartyMenu.h"
+#include "Resources.h"
+#include "../app/Application.h"
+#include "../app/Input.h"
+#include "../common/GameConfig.h"
+#include "../common/GameState.h"
+#include "../common/Hero.h"
+#include "../common/Inventory.h"
+#include "../common/Item.h"
+#include "../common/Stats.h"
+#include "../graphics/Font.h"
+#include "../graphics/Frame.h"
+
+using app::Input;
+using common::Hero;
+using common::Inventory;
+using common::Item;
+using common::Stats;
+using geometry::Vector;
+using graphics::Font;
+using graphics::Frame;
+
+namespace menu {
+
+EquipMenu::EquipMenu(PartyMenu *parent, int cursor)
+: parent(parent)
+, cursor(cursor)
+, actionMenu(*parent->Res().equipmentActionMenuProperties)
+, equipmentMenu(*parent->Res().equipmentMenuProperties)
+, inventoryMenu(*parent->Res().inventoryMenuProperties) {
+       actionMenu.Add(parent->Res().equipMenuEquipLabel, CHOICE_EQUIP);
+       actionMenu.Add(parent->Res().equipMenuStrongestLabel, CHOICE_STRONGEST);
+       actionMenu.Add(parent->Res().equipMenuRemoveLabel, CHOICE_REMOVE);
+       actionMenu.Add(parent->Res().equipMenuRemoveAllLabel, CHOICE_REMOVE_ALL);
+       actionMenu.Add(parent->Res().equipMenuDropLabel, CHOICE_DROP);
+
+       LoadEquipment();
+}
+
+
+void EquipMenu::OnEnterState(SDL_Surface *) {
+       equipmentMenu.SetInactive();
+       inventoryMenu.SetInactive();
+}
+
+void EquipMenu::OnExitState(SDL_Surface *) {
+
+}
+
+void EquipMenu::OnResumeState(SDL_Surface *) {
+
+}
+
+void EquipMenu::OnPauseState(SDL_Surface *) {
+
+}
+
+
+void EquipMenu::OnResize(int width, int height) {
+
+}
+
+
+void EquipMenu::HandleEvents(const Input &input) {
+       if (input.JustPressed(Input::SHOULDER_LEFT)) {
+               PreviousHero();
+       }
+       if (input.JustPressed(Input::SHOULDER_RIGHT)) {
+               NextHero();
+       }
+       if (actionMenu.IsActive()) {
+               if (input.JustPressed(Input::PAD_UP)) {
+                       actionMenu.PreviousRow();
+               }
+               if (input.JustPressed(Input::PAD_DOWN)) {
+                       actionMenu.NextRow();
+               }
+               if (input.JustPressed(Input::ACTION_A)) {
+                       switch (actionMenu.Selected()) {
+                               case CHOICE_EQUIP:
+                                       LoadEquipment();
+                                       actionMenu.SetSelected();
+                                       equipmentMenu.SetActive();
+                                       break;
+                               case CHOICE_STRONGEST:
+                                       // TODO: implement "equip strongest" when items' stat effects are done
+                                       break;
+                               case CHOICE_REMOVE:
+                                       actionMenu.SetSelected();
+                                       equipmentMenu.SetActive();
+                                       break;
+                               case CHOICE_REMOVE_ALL:
+                                       RemoveAllEquipment();
+                                       break;
+                               case CHOICE_DROP:
+                                       actionMenu.SetSelected();
+                                       equipmentMenu.SetActive();
+                                       break;
+                       }
+               } else if (input.JustPressed(Input::ACTION_B)) {
+                       Ctrl().PopState();
+               }
+       } else if (equipmentMenu.IsActive()) {
+               if (input.JustPressed(Input::PAD_UP)) {
+                       equipmentMenu.PreviousRow();
+                       if (InventoryVisible()) {
+                               LoadInventory();
+                       }
+               }
+               if (input.JustPressed(Input::PAD_DOWN)) {
+                       equipmentMenu.NextRow();
+                       if (InventoryVisible()) {
+                               LoadInventory();
+                       }
+               }
+               if (input.JustPressed(Input::ACTION_B)) {
+                       equipmentMenu.SetInactive();
+                       actionMenu.SetActive();
+               } else if (input.JustPressed(Input::ACTION_A)) {
+                       switch (actionMenu.Selected()) {
+                               case CHOICE_EQUIP:
+                                       equipmentMenu.SetSelected();
+                                       inventoryMenu.SetActive();
+                                       break;
+                               case CHOICE_STRONGEST:
+                               case CHOICE_REMOVE_ALL:
+                                       // invalid state, recover
+                                       equipmentMenu.SetInactive();
+                                       actionMenu.SetActive();
+                                       break;
+                               case CHOICE_REMOVE:
+                                       RemoveItem();
+                                       break;
+                               case CHOICE_DROP:
+                                       DropItem();
+                                       break;
+                       }
+               }
+       } else {
+               if (input.JustPressed(Input::PAD_UP)) {
+                       inventoryMenu.PreviousRow();
+               }
+               if (input.JustPressed(Input::PAD_DOWN)) {
+                       inventoryMenu.NextRow();
+               }
+               if (input.JustPressed(Input::ACTION_A)) {
+                       EquipSelected();
+                       inventoryMenu.SetInactive();
+                       equipmentMenu.SetActive();
+               } else if (input.JustPressed(Input::ACTION_B)) {
+                       inventoryMenu.SetInactive();
+                       equipmentMenu.SetActive();
+               }
+       }
+}
+
+void EquipMenu::UpdateWorld(float deltaT) {
+
+}
+
+void EquipMenu::Render(SDL_Surface *screen) {
+       Vector<int> offset((screen->w - Width()) / 2, (screen->h - Height()) / 2);
+       Vector<int> shoulderNavOffset(
+                       5 * parent->Res().statusFont->CharWidth(),
+                       parent->Res().statusFont->CharHeight());
+       Vector<int> statsOffset(
+                       4 * parent->Res().statusFont->CharWidth(),
+                       8 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8);
+       Vector<int> equipOffset(
+                       17 * parent->Res().statusFont->CharWidth(),
+                       4 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8);
+
+       parent->RenderBackground(screen);
+       parent->Res().shoulderNav->Draw(screen, offset + shoulderNavOffset);
+       RenderStatus(screen, offset + parent->StatusOffset(0));
+       RenderStats(screen, offset + statsOffset);
+       RenderEquipmentMenu(screen, offset + equipOffset);
+       if (InventoryVisible()) {
+               Vector<int> inventoryOffset(
+                               parent->Res().statusFont->CharWidth(),
+                               17 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8);
+               RenderInventoryMenu(screen, offset + inventoryOffset);
+       } else {
+               Vector<int> menuOffset(
+                               15 * parent->Res().statusFont->CharWidth(),
+                               17 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8);
+               RenderActionMenu(screen, offset + menuOffset);
+       }
+}
+
+int EquipMenu::Width() const {
+       return parent->Width();
+}
+
+int EquipMenu::Height() const {
+       return parent->Height();
+}
+
+void EquipMenu::RenderStatus(SDL_Surface *screen, const Vector<int> &offset) const {
+       parent->GetHeroStatus(cursor).Render(screen, offset);
+}
+
+void EquipMenu::RenderStats(SDL_Surface *screen, const Vector<int> &offset) const {
+       const Stats &stats(GetHero().GetStats());
+       Vector<int> lineBreak(0, parent->Res().statusFont->CharHeight());
+
+       Vector<int> position(offset);
+       RenderStatsLine(parent->Res().atpLabel, stats.Attack(), screen, position);
+
+       position += lineBreak;
+       RenderStatsLine(parent->Res().dfpLabel, stats.Defense(), screen, position);
+
+       position += lineBreak;
+       RenderStatsLine(parent->Res().strLabel, stats.Strength(), screen, position);
+
+       position += lineBreak;
+       RenderStatsLine(parent->Res().aglLabel, stats.Agility(), screen, position);
+
+       position += lineBreak;
+       RenderStatsLine(parent->Res().intLabel, stats.Intelligence(), screen, position);
+
+       position += lineBreak;
+       RenderStatsLine(parent->Res().gutLabel, stats.Gut(), screen, position);
+
+       position += lineBreak;
+       RenderStatsLine(parent->Res().mgrLabel, stats.MagicResistance(), screen, position);
+}
+
+void EquipMenu::RenderStatsLine(const char *label, int number, SDL_Surface *screen, const Vector<int> &position) const {
+       const Font &font(*parent->Res().statusFont);
+       const Vector<int> numberOffset(4 * font.CharWidth(), 0);
+
+       font.DrawString(label, screen, position, 3);
+       font.DrawNumber(number, screen, position + numberOffset, 3);
+}
+
+void EquipMenu::RenderEquipmentMenu(SDL_Surface *screen, const Vector<int> &offset) const {
+       equipmentMenu.Draw(screen, offset);
+}
+
+void EquipMenu::RenderActionMenu(SDL_Surface *screen, const Vector<int> &offset) const {
+       const Font &font(*parent->Res().statusFont);
+       const Frame &frame(*parent->Res().statusFrame);
+       const Vector<int> menuOffset(3 * font.CharWidth(), font.CharHeight() + font.CharHeight() / 2);
+
+       frame.Draw(screen, offset, 15 * font.CharWidth(), 10 * font.CharHeight());
+       actionMenu.Draw(screen, offset + menuOffset);
+}
+
+void EquipMenu::RenderInventoryMenu(SDL_Surface *screen, const Vector<int> &offset) const {
+       const Font &font(*parent->Res().normalFont);
+       const Frame &frame(*parent->Res().statusFrame);
+       const Vector<int> menuOffset(3 * font.CharWidth(), font.CharHeight() + font.CharHeight() / 4);
+
+       frame.Draw(screen, offset, 30 * font.CharWidth(), 11 * font.CharHeight());
+       inventoryMenu.Draw(screen, offset + menuOffset);
+}
+
+
+void EquipMenu::NextHero() {
+       cursor = (cursor + 1) % parent->Game().state->partySize;
+       LoadEquipment();
+       if (InventoryVisible()) {
+               LoadInventory();
+       }
+}
+
+void EquipMenu::PreviousHero() {
+       cursor = (cursor + parent->Game().state->partySize - 1) % parent->Game().state->partySize;
+       LoadEquipment();
+       if (InventoryVisible()) {
+               LoadInventory();
+       }
+}
+
+Hero &EquipMenu::GetHero() {
+       return *parent->Game().state->party[cursor];
+}
+
+const Hero &EquipMenu::GetHero() const {
+       return *parent->Game().state->party[cursor];
+}
+
+
+void EquipMenu::LoadEquipment() {
+       equipmentMenu.Clear();
+       for (int i = 0; i < Hero::EQUIP_COUNT; ++i) {
+               if (GetHero().Equipped(Hero::EquipSlot(i))) {
+                       const Item *item(GetHero().Equipment(Hero::EquipSlot(i)));
+                       equipmentMenu.Add(item->Name(), item, true, item->MenuIcon());
+               } else {
+                       equipmentMenu.Add(parent->Res().noEquipmentText, 0);
+               }
+       }
+}
+
+void EquipMenu::RemoveAllEquipment() {
+       Inventory &inv(parent->Game().state->inventory);
+       for (int i = 0; i < Hero::EQUIP_COUNT; ++i) {
+               if (GetHero().Equipped(Hero::EquipSlot(i))
+                               && inv.Add(GetHero().Equipment(Hero::EquipSlot(i)), 1)) {
+                       GetHero().RemoveEquipment(Hero::EquipSlot(i));
+               }
+       }
+       LoadEquipment();
+}
+
+void EquipMenu::RemoveItem() {
+       Inventory &inv(parent->Game().state->inventory);
+       Hero::EquipSlot slot = Hero::EquipSlot(equipmentMenu.SelectedIndex());
+
+       if (GetHero().Equipped(slot) && inv.Add(GetHero().Equipment(slot), 1)) {
+               GetHero().RemoveEquipment(slot);
+       }
+
+       LoadEquipment();
+}
+
+void EquipMenu::DropItem() {
+       GetHero().RemoveEquipment(Hero::EquipSlot(equipmentMenu.SelectedIndex()));
+       LoadEquipment();
+}
+
+
+bool EquipMenu::InventoryVisible() const {
+       return !actionMenu.IsActive() && actionMenu.Selected() == CHOICE_EQUIP;
+}
+
+void EquipMenu::LoadInventory() {
+       const Inventory &inv = parent->Game().state->inventory;
+       const Hero &hero = GetHero();
+       const Hero::EquipSlot slot = Hero::EquipSlot(equipmentMenu.SelectedIndex());
+
+       inventoryMenu.Clear();
+       for (int i = 0; i < inv.MaxItems(); ++i) {
+               const Item *item = inv.ItemAt(i);
+               if (item && item->EquipableAt(slot)) {
+                       inventoryMenu.Add(item->Name(), item, hero.CanEquip(*item),
+                                       item->MenuIcon(), inv.ItemCountAt(i));
+               }
+       }
+}
+
+void EquipMenu::EquipSelected() {
+       Inventory &inv = parent->Game().state->inventory;
+       Hero &hero = GetHero();
+       const Hero::EquipSlot slot = Hero::EquipSlot(equipmentMenu.SelectedIndex());
+
+       const Item *selected = inventoryMenu.Selected();
+       const Item *equipped = equipmentMenu.Selected();
+
+       if (!hero.CanEquip(*selected)) {
+               // TODO: error noise and blur
+               return;
+       }
+
+       inv.Remove(selected, 1);
+       if (!inv.Add(equipped, 1)) {
+               // roll back
+               inv.Add(selected, 1);
+               // TODO: error noise, blur, message?
+               return;
+       }
+
+       hero.SetEquipment(slot, selected);
+       LoadEquipment();
+       LoadInventory();
+}
+
+}
diff --git a/src/menu/EquipMenu.h b/src/menu/EquipMenu.h
new file mode 100644 (file)
index 0000000..f8e9e78
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * EquipMenu.h
+ *
+ *  Created on: Nov 18, 2012
+ *      Author: holy
+ */
+
+#ifndef MENU_EQUIPMENU_H_
+#define MENU_EQUIPMENU_H_
+
+#include "fwd.h"
+#include "../app/State.h"
+#include "../common/fwd.h"
+#include "../geometry/Vector.h"
+#include "../graphics/Menu.h"
+
+namespace menu {
+
+class EquipMenu
+: public app::State {
+
+public:
+       EquipMenu(PartyMenu *parent, int heroIndex);
+
+public:
+       virtual void HandleEvents(const app::Input &);
+       virtual void UpdateWorld(float deltaT);
+       virtual void Render(SDL_Surface *);
+
+public:
+       int Width() const;
+       int Height() const;
+
+private:
+       virtual void OnEnterState(SDL_Surface *screen);
+       virtual void OnExitState(SDL_Surface *screen);
+       virtual void OnResumeState(SDL_Surface *screen);
+       virtual void OnPauseState(SDL_Surface *screen);
+
+       virtual void OnResize(int width, int height);
+
+       void NextHero();
+       void PreviousHero();
+
+       common::Hero &GetHero();
+       const common::Hero &GetHero() const;
+
+       void LoadEquipment();
+       void RemoveAllEquipment();
+       void RemoveItem();
+       void DropItem();
+
+       bool InventoryVisible() const;
+       void LoadInventory();
+       void EquipSelected();
+
+       void RenderStatus(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderStats(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderStatsLine(const char *label, int number, SDL_Surface *screen, const geometry::Vector<int> &position) const;
+       void RenderEquipmentMenu(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderActionMenu(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderInventoryMenu(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+
+private:
+       PartyMenu *parent;
+       int cursor;
+       enum Choice {
+               CHOICE_EQUIP,
+               CHOICE_STRONGEST,
+               CHOICE_REMOVE,
+               CHOICE_REMOVE_ALL,
+               CHOICE_DROP,
+       };
+       graphics::Menu<Choice> actionMenu;
+       graphics::Menu<const common::Item *> equipmentMenu;
+       graphics::Menu<const common::Item *> inventoryMenu;
+
+};
+
+}
+
+#endif /* MENU_EQUIPMENU_H_ */
diff --git a/src/menu/HeroStatus.cpp b/src/menu/HeroStatus.cpp
new file mode 100644 (file)
index 0000000..5bc0c3b
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * HeroStatus.cpp
+ *
+ *  Created on: Oct 21, 2012
+ *      Author: holy
+ */
+
+#include "HeroStatus.h"
+
+#include "Resources.h"
+#include "../common/Hero.h"
+#include "../graphics/Font.h"
+#include "../graphics/Sprite.h"
+
+using geometry::Vector;
+
+namespace menu {
+
+HeroStatus::HeroStatus()
+: res(0)
+, party(0)
+, hero(0) {
+
+}
+
+HeroStatus::~HeroStatus() {
+
+}
+
+
+int HeroStatus::Width() const {
+       return party[hero]->BattleSprite()->Width() + res->statusFont->CharWidth() * 11;
+}
+
+int HeroStatus::Height() const {
+       return party[hero]->BattleSprite()->Height() + res->statusFont->CharWidth();
+}
+
+
+void HeroStatus::Render(SDL_Surface *screen, const Vector<int> &offset) const {
+       if (!party) return;
+
+       party[hero]->BattleSprite()->Draw(screen, offset, 0, 0);
+
+       // for some reason, fonts are shifted by one pixel in the original
+       Vector<int> nameOffset(
+                       party[hero]->BattleSprite()->Width(),
+                       res->statusFont->CharHeight() * 7 / 8);
+       nameOffset += offset;
+       res->statusFont->DrawString(party[hero]->Name(), screen, nameOffset, 5);
+
+       Vector<int> levelLabelOffset(nameOffset.X() + 6 * res->statusFont->CharWidth(), nameOffset.Y());
+       res->statusLabels->Draw(screen, levelLabelOffset, 0, 0);
+
+       Vector<int> levelOffset(levelLabelOffset.X() + 2 * res->statusFont->CharWidth(), levelLabelOffset.Y());
+       res->statusFont->DrawNumber(party[hero]->Level(), screen, levelOffset, 2);
+
+       Vector<int> healthLabelOffset(nameOffset.X(), nameOffset.Y() + res->statusFont->CharHeight());
+       res->statusLabels->Draw(screen, healthLabelOffset, 0, 1);
+
+       Vector<int> healthOffset(nameOffset.X() + 3 * res->statusFont->CharWidth(), nameOffset.Y() + res->statusFont->CharHeight());
+       res->statusFont->DrawNumber(party[hero]->Health(), screen, healthOffset, 3);
+
+       Vector<int> healthSeparatorOffset(healthOffset.X() + 3 * res->statusFont->CharWidth(), healthOffset.Y());
+       res->statusFont->DrawChar('/', screen, healthSeparatorOffset);
+
+       Vector<int> maxHealthOffset(healthSeparatorOffset.X() + res->statusFont->CharWidth(), healthOffset.Y());
+       res->statusFont->DrawNumber(party[hero]->MaxHealth(), screen, maxHealthOffset, 3);
+
+       Vector<int> manaLabelOffset(healthLabelOffset.X(), healthLabelOffset.Y() + res->statusFont->CharHeight());
+       res->statusLabels->Draw(screen, manaLabelOffset, 0, 2);
+
+       Vector<int> manaOffset(healthOffset.X(), healthOffset.Y() + res->statusFont->CharHeight());
+       res->statusFont->DrawNumber(party[hero]->Mana(), screen, manaOffset, 3);
+
+       Vector<int> manaSeparatorOffset(healthSeparatorOffset.X(), manaOffset.Y());
+       res->statusFont->DrawChar('/', screen, manaSeparatorOffset);
+
+       Vector<int> maxManaOffset(maxHealthOffset.X(), manaOffset.Y());
+       res->statusFont->DrawNumber(party[hero]->MaxMana(), screen, maxManaOffset, 3);
+}
+
+}
diff --git a/src/menu/HeroStatus.h b/src/menu/HeroStatus.h
new file mode 100644 (file)
index 0000000..27c048e
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * HeroStatus.h
+ *
+ *  Created on: Oct 21, 2012
+ *      Author: holy
+ */
+
+#ifndef MENU_HEROSTATUS_H_
+#define MENU_HEROSTATUS_H_
+
+#include "fwd.h"
+#include "../common/fwd.h"
+#include "../geometry/Vector.h"
+
+#include <SDL.h>
+
+namespace menu {
+
+class HeroStatus {
+
+public:
+       HeroStatus();
+       ~HeroStatus();
+
+public:
+       void SetResources(const Resources *r) { res = r; }
+       void SetHero(common::Hero **p, int h) { party = p; hero = h; }
+
+       int Width() const;
+       int Height() const;
+       geometry::Vector<int> Size() const { return geometry::Vector<int>(Width(), Height()); }
+
+       void Render(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+
+private:
+       const Resources *res;
+       common::Hero **party;
+       int hero;
+
+};
+
+}
+
+#endif /* MENU_HEROSTATUS_H_ */
diff --git a/src/menu/InventoryMenu.cpp b/src/menu/InventoryMenu.cpp
new file mode 100644 (file)
index 0000000..437cd9c
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * InventoryMenu.cpp
+ *
+ *  Created on: Nov 4, 2012
+ *      Author: holy
+ */
+
+#include "InventoryMenu.h"
+
+#include "PartyMenu.h"
+#include "Resources.h"
+#include "../app/Input.h"
+#include "../common/GameConfig.h"
+#include "../common/GameState.h"
+#include "../common/Inventory.h"
+#include "../common/Item.h"
+#include "../graphics/Font.h"
+#include "../graphics/Frame.h"
+
+using app::Input;
+using common::Inventory;
+using common::Item;
+using geometry::Vector;
+using graphics::Font;
+using graphics::Frame;
+using std::swap;
+
+namespace menu {
+
+InventoryMenu::InventoryMenu(PartyMenu *parent)
+: parent(parent)
+, menu(*parent->Res().itemMenuProperties)
+, itemMenu(*parent->Res().inventoryMenuProperties) {
+       menu.Add(parent->Res().itemMenuUseText, CHOICE_USE);
+       menu.Add(parent->Res().itemMenuSortText, CHOICE_SORT);
+       menu.Add(parent->Res().itemMenuDropText, CHOICE_DROP);
+}
+
+
+void InventoryMenu::OnEnterState(SDL_Surface *) {
+       menu.SetSelected();
+       LoadInventory();
+}
+
+void InventoryMenu::LoadInventory() {
+       const Inventory &inv(parent->Game().state->inventory);
+       itemMenu.Clear();
+       itemMenu.Reserve(inv.MaxItems());
+       for (int i(0); i < inv.MaxItems(); ++i) {
+               const Item *item(inv.ItemAt(i));
+               if (item) {
+                       itemMenu.Add(item->Name(), item, item->CanUseOnStatusScreen(), item->MenuIcon(), inv.ItemCountAt(i));
+               } else {
+                       itemMenu.AddEmptyEntry();
+               }
+       }
+}
+
+void InventoryMenu::OnExitState(SDL_Surface *) {
+
+}
+
+void InventoryMenu::OnResumeState(SDL_Surface *) {
+
+}
+
+void InventoryMenu::OnPauseState(SDL_Surface *) {
+
+}
+
+
+void InventoryMenu::OnResize(int width, int height) {
+
+}
+
+
+void InventoryMenu::HandleEvents(const Input &input) {
+       if (menu.IsActive()) {
+               if (input.JustPressed(Input::PAD_LEFT)) {
+                       menu.PreviousItem();
+               }
+               if (input.JustPressed(Input::PAD_RIGHT)) {
+                       menu.NextItem();
+               }
+       } else {
+               if (input.JustPressed(Input::PAD_UP)) {
+                       itemMenu.PreviousItem();
+               }
+               if (input.JustPressed(Input::PAD_DOWN)) {
+                       itemMenu.NextItem();
+               }
+       }
+
+       if (input.JustPressed(Input::ACTION_A)) {
+               if (menu.IsActive()) {
+                       if (menu.Selected() == CHOICE_SORT) {
+                               parent->Game().state->inventory.Sort();
+                               LoadInventory();
+                       } else {
+                               menu.SetSelected();
+                               itemMenu.SetActive();
+                       }
+               } else if (itemMenu.IsActive()) {
+                       itemMenu.SetDualSelection();
+               } else if (itemMenu.SelectedIndex() == itemMenu.SecondaryIndex()) {
+                       switch (menu.Selected()) {
+                               case CHOICE_USE:
+                                       if (itemMenu.Selected()->CanUseOnStatusScreen()) {
+                                               // TODO: implement item use
+                                       }
+                                       itemMenu.SetActive();
+                                       break;
+                               case CHOICE_SORT:
+                                       // invalid state, recover
+                                       menu.SetActive();
+                                       itemMenu.SetInactive();
+                                       break;
+                               case CHOICE_DROP:
+                                       if (itemMenu.Selected()->CanDrop()) {
+                                               parent->Game().state->inventory.RemoveAll(itemMenu.Selected());
+                                               itemMenu.ClearEntry(itemMenu.SelectedIndex());
+                                       }
+                                       itemMenu.SetActive();
+                                       break;
+                       }
+               } else {
+                       parent->Game().state->inventory.SwapEntriesAt(
+                                       itemMenu.SelectedIndex(),
+                                       itemMenu.SecondaryIndex());
+                       itemMenu.SwapSelected();
+                       itemMenu.SetActive();
+               }
+       }
+       if (input.JustPressed(Input::ACTION_B)) {
+               if (menu.IsActive()) {
+                       Ctrl().PopState();
+               } else if (itemMenu.IsActive()) {
+                       menu.SetActive();
+                       itemMenu.SetInactive();
+               } else {
+                       itemMenu.SetActive();
+               }
+       }
+}
+
+void InventoryMenu::UpdateWorld(float deltaT) {
+
+}
+
+
+void InventoryMenu::Render(SDL_Surface *screen) {
+       const Font &font(*parent->Res().normalFont);
+       Vector<int> offset((screen->w - Width()) / 2, (screen->h - Height()) / 2);
+       Vector<int> menuOffset(font.CharWidth(), 13 * font.CharHeight() + font.CharHeight() / 8);
+       Vector<int> inventoryOffset(font.CharWidth(), 16 * font.CharHeight() + font.CharHeight() / 8);
+
+       parent->RenderBackground(screen);
+       parent->RenderHeros(screen, offset);
+       RenderMenu(screen, menuOffset + offset);
+       RenderInventory(screen, inventoryOffset + offset);
+}
+
+int InventoryMenu::Width() const {
+       return parent->Width();
+}
+
+int InventoryMenu::Height() const {
+       return parent->Height();
+}
+
+void InventoryMenu::RenderMenu(SDL_Surface *screen, const Vector<int> &offset) const {
+       const Font &font(*parent->Res().normalFont);
+       const Frame &frame(*parent->Res().statusFrame);
+
+       const Vector<int> labelOffset(2 * font.CharWidth(), font.CharHeight());
+       const Vector<int> menuFrameOffset(offset.X() + 8 * font.CharWidth(), offset.Y());
+       const Vector<int> menuOffset(menuFrameOffset.X() + 3 * font.CharWidth(), menuFrameOffset.Y() + font.CharHeight());
+
+       frame.Draw(screen, offset, 8 * font.CharWidth(), 3 * font.CharHeight());
+       font.DrawString(parent->Res().mainMenuItemText, screen, labelOffset + offset);
+       frame.Draw(screen, menuFrameOffset, 22 * font.CharWidth(), 3 * font.CharHeight());
+       menu.Draw(screen, menuOffset);
+}
+
+void InventoryMenu::RenderInventory(SDL_Surface *screen, const Vector<int> &offset) const {
+       const Font &font(*parent->Res().normalFont);
+       const Frame &frame(*parent->Res().statusFrame);
+       const Vector<int> menuOffset(3 * font.CharWidth(), font.CharHeight() + font.CharHeight() / 4);
+
+       frame.Draw(screen, offset, 30 * font.CharWidth(), 11 * font.CharHeight());
+       itemMenu.Draw(screen, offset + menuOffset);
+}
+
+}
diff --git a/src/menu/InventoryMenu.h b/src/menu/InventoryMenu.h
new file mode 100644 (file)
index 0000000..c1d458b
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * InventoryMenu.h
+ *
+ *  Created on: Nov 4, 2012
+ *      Author: holy
+ */
+
+#ifndef MENU_INVENTORYMENU_H_
+#define MENU_INVENTORYMENU_H_
+
+#include "fwd.h"
+#include "../app/State.h"
+#include "../common/fwd.h"
+#include "../geometry/Vector.h"
+#include "../graphics/Menu.h"
+
+namespace menu {
+
+class InventoryMenu
+: public app::State {
+
+public:
+       explicit InventoryMenu(PartyMenu *parent);
+
+public:
+       virtual void HandleEvents(const app::Input &);
+       virtual void UpdateWorld(float deltaT);
+       virtual void Render(SDL_Surface *);
+
+       int Width() const;
+       int Height() const;
+
+private:
+       virtual void OnEnterState(SDL_Surface *screen);
+       virtual void OnExitState(SDL_Surface *screen);
+       virtual void OnResumeState(SDL_Surface *screen);
+       virtual void OnPauseState(SDL_Surface *screen);
+
+       virtual void OnResize(int width, int height);
+
+       void LoadInventory();
+
+       void RenderMenu(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderInventory(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+
+private:
+       PartyMenu *parent;
+       enum Choice {
+               CHOICE_USE,
+               CHOICE_SORT,
+               CHOICE_DROP,
+       };
+       graphics::Menu<Choice> menu;
+       graphics::Menu<const common::Item *> itemMenu;
+
+};
+
+}
+
+#endif /* MENU_INVENTORYMENU_H_ */
diff --git a/src/menu/PartyMenu.cpp b/src/menu/PartyMenu.cpp
new file mode 100644 (file)
index 0000000..619d7e1
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * PartyMenu.cpp
+ *
+ *  Created on: Oct 21, 2012
+ *      Author: holy
+ */
+
+#include "PartyMenu.h"
+
+#include "ChangeHero.h"
+#include "ConfigMenu.h"
+#include "EquipMenu.h"
+#include "InventoryMenu.h"
+#include "Resources.h"
+#include "ScenarioMenu.h"
+#include "SelectHero.h"
+#include "SpellMenu.h"
+#include "StatusMenu.h"
+#include "../app/Application.h"
+#include "../app/Input.h"
+#include "../common/GameConfig.h"
+#include "../common/GameState.h"
+#include "../geometry/Vector.h"
+#include "../graphics/Font.h"
+#include "../graphics/Frame.h"
+#include "../graphics/Texture.h"
+
+using app::Input;
+using common::GameConfig;
+using geometry::Vector;
+
+namespace menu {
+
+PartyMenu::PartyMenu(GameConfig *game)
+: game(game)
+, mainMenu(*game->menuResources->mainMenuProperties) {
+       for (int i(0); i < 4; ++i) {
+               status[i].SetHero(game->state->party, i);
+               status[i].SetResources(game->menuResources);
+       }
+       statusPositions[0] = Vector<int>(0, 0);
+       statusPositions[1] = Vector<int>(status[0].Width(), 0);
+       statusPositions[2] = Vector<int>(0, status[0].Height());
+       statusPositions[3] = Vector<int>(status[0].Width(), status[0].Height());
+
+       mainMenu.Add(Res().mainMenuItemText, 0);
+       mainMenu.Add(Res().mainMenuStatusText, 4);
+       mainMenu.Add(Res().mainMenuSpellText, 1);
+       mainMenu.Add(Res().mainMenuChangeText, 5);
+       mainMenu.Add(Res().mainMenuCapsuleText, 2);
+       mainMenu.Add(Res().mainMenuConfigText, 6);
+       mainMenu.Add(Res().mainMenuEquipmentText, 3);
+       mainMenu.Add(Res().mainMenuScenarioText, 7);
+}
+
+PartyMenu::~PartyMenu() {
+
+}
+
+
+void PartyMenu::OnEnterState(SDL_Surface *) {
+
+}
+
+void PartyMenu::OnExitState(SDL_Surface *) {
+
+}
+
+void PartyMenu::OnResumeState(SDL_Surface *) {
+
+}
+
+void PartyMenu::OnPauseState(SDL_Surface *) {
+
+}
+
+
+void PartyMenu::OnResize(int width, int height) {
+
+}
+
+
+void PartyMenu::HandleEvents(const Input &input) {
+       if (input.JustPressed(Input::ACTION_B)) {
+               Ctrl().PopState();
+               return;
+       }
+
+       if (input.JustPressed(Input::PAD_UP)) {
+               mainMenu.PreviousRow();
+       } else if (input.JustPressed(Input::PAD_RIGHT)) {
+               mainMenu.NextItem();
+       } else if (input.JustPressed(Input::PAD_DOWN)) {
+               mainMenu.NextRow();
+       } else if (input.JustPressed(Input::PAD_LEFT)) {
+               mainMenu.PreviousItem();
+       }
+
+       if (input.JustPressed(Input::ACTION_A)) {
+               switch (mainMenu.Selected()) {
+                       case MENU_ITEM_ITEM:
+                               Ctrl().PushState(new InventoryMenu(this));
+                               break;
+                       case MENU_ITEM_SPELL:
+                               Ctrl().PushState(new SelectHero(this, this, this, OnSpellSelect));
+                               break;
+                       case MENU_ITEM_CAPSULE:
+                               break;
+                       case MENU_ITEM_EQUIP:
+                               Ctrl().PushState(new SelectHero(this, this, this, OnEquipSelect));
+                               break;
+                       case MENU_ITEM_STATUS:
+                               Ctrl().PushState(new SelectHero(this, this, this, OnStatusSelect));
+                               break;
+                       case MENU_ITEM_CHANGE:
+                               Ctrl().PushState(new ChangeHero(this));
+                               break;
+                       case MENU_ITEM_CONFIG:
+                               Ctrl().PushState(new ConfigMenu(this));
+                               break;
+                       case MENU_ITEM_SCENARIO:
+                               Ctrl().PushState(new ScenarioMenu(this));
+                               break;
+                       default:
+                               break;
+               }
+       }
+}
+
+void PartyMenu::UpdateWorld(float deltaT) {
+
+}
+
+void PartyMenu::Render(SDL_Surface *screen) {
+       Vector<int> offset((screen->w - Width()) / 2, (screen->h - Height()) / 2);
+
+       RenderBackground(screen);
+       RenderHeros(screen, offset);
+       RenderMenu(screen, offset + Vector<int>(8 * Res().normalFont->CharWidth(), 13 * Res().normalFont->CharHeight() + Res().normalFont->CharHeight() / 8));
+       RenderInfo(screen, offset + Vector<int>(14 * Res().normalFont->CharWidth(), 21 * Res().normalFont->CharHeight() + Res().normalFont->CharHeight() / 8));
+}
+
+int PartyMenu::Width() const {
+       return 2 * (status[0].Width() + Res().normalFont->CharWidth());
+}
+
+int PartyMenu::Height() const {
+       return 2 * Res().normalFont->CharHeight()
+                       + 2 * status[0].Height()
+                       + Res().normalFont->CharHeight()
+                       + 8 * Res().normalFont->CharHeight()
+                       + 5 * Res().normalFont->CharHeight()
+                       + 2 * Res().normalFont->CharHeight();
+}
+
+void PartyMenu::RenderBackground(SDL_Surface *screen) const {
+       Res().menubg->Render(screen, Vector<int>(), Vector<int>(screen->w, screen->h));
+}
+
+void PartyMenu::RenderHeros(SDL_Surface *screen, const Vector<int> &offset) const {
+       for (int i(0); i < 4; ++i) {
+               status[i].Render(screen, offset + StatusOffset(i));
+       }
+}
+
+Vector<int> PartyMenu::StatusOffset(int index) const {
+       return statusPositions[index] + Vector<int>(Res().normalFont->CharWidth(), 2 * Res().normalFont->CharHeight());
+}
+
+void PartyMenu::RenderMenu(SDL_Surface *screen, const Vector<int> &offset) const {
+       Vector<int> menuOffset(3 * Res().normalFont->CharWidth(), Res().normalFont->CharHeight() + Res().normalFont->CharHeight() / 4);
+
+       Res().statusFrame->Draw(screen, offset, 23 * Res().normalFont->CharWidth(), 8 * Res().normalFont->CharHeight());
+       mainMenu.Draw(screen, offset + menuOffset);
+}
+
+void PartyMenu::RenderInfo(SDL_Surface *screen, const Vector<int> &offset) const {
+       Res().statusFrame->Draw(screen, offset, 17 * Res().normalFont->CharWidth(), 5 * Res().normalFont->CharHeight());
+
+       Vector<int> timeLabelOffset(2 * Res().normalFont->CharWidth(), Res().normalFont->CharHeight() + Res().normalFont->CharHeight() / 4);
+       Res().normalFont->DrawString(Res().mainMenuTimeText, screen, offset + timeLabelOffset);
+
+       Vector<int> hoursOffset(timeLabelOffset.X() + 6 * Res().normalFont->CharWidth(), timeLabelOffset.Y());
+       Res().normalFont->DrawNumber(game->state->time / 60 / 60, screen, offset + hoursOffset, 4);
+
+       Vector<int> timeSeparatorOffset(hoursOffset.X() + 4 * Res().normalFont->CharWidth(), hoursOffset.Y());
+       Res().normalFont->DrawChar(':', screen, offset + timeSeparatorOffset);
+
+       Vector<int> minutesOffset(timeSeparatorOffset.X() + Res().normalFont->CharWidth(), timeSeparatorOffset.Y());
+       Res().normalFont->DrawNumber(game->state->time / 60, screen, offset + minutesOffset, 2);
+       if (game->state->time / 60 < 10) {
+               Res().normalFont->DrawChar('0', screen, offset + minutesOffset);
+       }
+
+       Vector<int> goldLabelOffset(2 * Res().normalFont->CharWidth(), 2 * Res().normalFont->CharHeight() + Res().normalFont->CharHeight() * 3 / 4);
+       Res().normalFont->DrawString(Res().mainMenuGoldText, screen, offset + goldLabelOffset);
+
+       Vector<int> goldOffset(goldLabelOffset.X() + 6 * Res().normalFont->CharWidth(), goldLabelOffset.Y());
+       Res().normalFont->DrawNumber(game->state->money, screen, offset + goldOffset, 7);
+}
+
+
+Resources &PartyMenu::Res() {
+       return *game->menuResources;
+}
+
+const Resources &PartyMenu::Res() const {
+       return *game->menuResources;
+}
+
+void PartyMenu::OnEquipSelect(void *ref, int index) {
+       PartyMenu *self(reinterpret_cast<PartyMenu *>(ref));
+       self->Ctrl().ChangeState(
+                       new EquipMenu(self, index));
+}
+
+void PartyMenu::OnSpellSelect(void *ref, int index) {
+       PartyMenu *self(reinterpret_cast<PartyMenu *>(ref));
+       self->Ctrl().ChangeState(
+                       new SpellMenu(self, index));
+}
+
+void PartyMenu::OnStatusSelect(void *ref, int index) {
+       PartyMenu *self(reinterpret_cast<PartyMenu *>(ref));
+       self->Ctrl().ChangeState(
+                       new StatusMenu(self, index));
+}
+
+}
diff --git a/src/menu/PartyMenu.h b/src/menu/PartyMenu.h
new file mode 100644 (file)
index 0000000..f915d30
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * PartyMenu.h
+ *
+ *  Created on: Oct 21, 2012
+ *      Author: holy
+ */
+
+#ifndef MENU_PARTYMENU_H_
+#define MENU_PARTYMENU_H_
+
+#include "fwd.h"
+#include "HeroStatus.h"
+#include "../app/State.h"
+#include "../common/fwd.h"
+#include "../geometry/Vector.h"
+#include "../graphics/Menu.h"
+
+namespace menu {
+
+class PartyMenu
+: public app::State {
+
+public:
+       explicit PartyMenu(common::GameConfig *);
+       virtual ~PartyMenu();
+
+public:
+       virtual void HandleEvents(const app::Input &);
+       virtual void UpdateWorld(float deltaT);
+       virtual void Render(SDL_Surface *);
+
+public:
+       common::GameConfig &Game() { return *game; }
+       const common::GameConfig &Game() const { return *game; }
+       Resources &Res();
+       const Resources &Res() const;
+
+       int Width() const;
+       int Height() const;
+
+public:
+       void RenderBackground(SDL_Surface *screen) const;
+       void RenderHeros(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderMenu(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderInfo(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+
+       geometry::Vector<int> StatusOffset(int index) const;
+       const HeroStatus &GetHeroStatus(int index) const { return status[index]; }
+
+       static void OnEquipSelect(void *, int);
+       static void OnSpellSelect(void *, int);
+       static void OnStatusSelect(void *, int);
+
+private:
+       virtual void OnEnterState(SDL_Surface *screen);
+       virtual void OnExitState(SDL_Surface *screen);
+       virtual void OnResumeState(SDL_Surface *screen);
+       virtual void OnPauseState(SDL_Surface *screen);
+
+       virtual void OnResize(int width, int height);
+
+private:
+       enum MenuItem {
+               MENU_ITEM_ITEM,
+               MENU_ITEM_SPELL,
+               MENU_ITEM_CAPSULE,
+               MENU_ITEM_EQUIP,
+               MENU_ITEM_STATUS,
+               MENU_ITEM_CHANGE,
+               MENU_ITEM_CONFIG,
+               MENU_ITEM_SCENARIO,
+       };
+
+private:
+       HeroStatus status[4];
+       geometry::Vector<int> statusPositions[4];
+       common::GameConfig *game;
+       graphics::Menu<int> mainMenu;
+
+};
+
+}
+
+#endif /* MENU_PARTYMENU_H_ */
diff --git a/src/menu/Resources.cpp b/src/menu/Resources.cpp
new file mode 100644 (file)
index 0000000..38cf6f8
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Resources.cpp
+ *
+ *  Created on: Oct 21, 2012
+ *      Author: holy
+ */
+
+#include "Resources.h"
+
+namespace menu {
+
+Resources::Resources()
+: menubg(0)
+, normalFont(0)
+, inactiveFont(0)
+, statusFont(0)
+
+, menuCursor(0)
+, menuActiveCursor(0)
+
+, statusLabels(0)
+, statusFrame(0)
+
+, mainMenuProperties(0)
+, mainMenuItemText(0)
+, mainMenuSpellText(0)
+, mainMenuCapsuleText(0)
+, mainMenuEquipmentText(0)
+, mainMenuStatusText(0)
+, mainMenuChangeText(0)
+, mainMenuConfigText(0)
+, mainMenuScenarioText(0)
+
+, mainMenuTimeText(0)
+, mainMenuGoldText(0)
+
+, heroCursor(0)
+, heroCursorBlinkTime(0)
+
+, noEquipmentText(0)
+
+, shoulderNav(0)
+
+, atpLabel(0)
+, dfpLabel(0)
+, strLabel(0)
+, aglLabel(0)
+, intLabel(0)
+, gutLabel(0)
+, mgrLabel(0)
+
+, ipLabel(0)
+, experienceLabel(0)
+, nextLevelLabel(0)
+
+, statusMenuProperties(0)
+
+, nextLabel(0)
+, returnLabel(0)
+
+, itemMenuProperties(0)
+, itemMenuUseText(0)
+, itemMenuSortText(0)
+, itemMenuDropText(0)
+
+, inventoryMenuProperties(0)
+
+, spellMenuProperties(0)
+
+, equipmentActionMenuProperties(0)
+, equipmentMenuProperties(0)
+, equipMenuEquipLabel(0)
+, equipMenuStrongestLabel(0)
+, equipMenuRemoveLabel(0)
+, equipMenuRemoveAllLabel(0)
+, equipMenuDropLabel(0)
+
+, configMenuProperties(0)
+, configMessageSpeedLabel(0)
+, configMessageSpeedFast(0)
+, configMessageSpeedNormal(0)
+, configMessageSpeedSlow(0)
+, configBattleCursorLabel(0)
+, configStatusCursorLabel(0)
+, configCursorClear(0)
+, configCursorMemory(0)
+, configMusicLabel(0)
+, configMusicStereo(0)
+, configMusicMono(0)
+
+, scenarioMenuProperties(0)
+, scenarioMenuHeadline(0) {
+
+}
+
+}
diff --git a/src/menu/Resources.h b/src/menu/Resources.h
new file mode 100644 (file)
index 0000000..62b825d
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Resources.h
+ *
+ *  Created on: Oct 21, 2012
+ *      Author: holy
+ */
+
+#ifndef MENU_RESOURCES_H_
+#define MENU_RESOURCES_H_
+
+#include "../graphics/fwd.h"
+
+namespace menu {
+
+struct Resources {
+
+       graphics::Texture *menubg;
+
+       graphics::Font *normalFont;
+       graphics::Font *inactiveFont;
+       graphics::Font *statusFont;
+
+       graphics::Sprite *menuCursor;
+       graphics::Sprite *menuActiveCursor;
+
+       graphics::Sprite *statusLabels;
+       graphics::Frame *statusFrame;
+
+       graphics::MenuProperties *mainMenuProperties;
+       const char *mainMenuItemText;
+       const char *mainMenuSpellText;
+       const char *mainMenuCapsuleText;
+       const char *mainMenuEquipmentText;
+       const char *mainMenuStatusText;
+       const char *mainMenuChangeText;
+       const char *mainMenuConfigText;
+       const char *mainMenuScenarioText;
+
+       const char *mainMenuTimeText;
+       const char *mainMenuGoldText;
+
+       graphics::Sprite *heroCursor;
+       int heroCursorBlinkTime;
+
+       const char *noEquipmentText;
+
+       graphics::Sprite *shoulderNav;
+
+       const char *atpLabel;
+       const char *dfpLabel;
+       const char *strLabel;
+       const char *aglLabel;
+       const char *intLabel;
+       const char *gutLabel;
+       const char *mgrLabel;
+
+       const char *ipLabel;
+       const char *experienceLabel;
+       const char *nextLevelLabel;
+
+       graphics::MenuProperties *statusMenuProperties;
+
+       const char *nextLabel;
+       const char *returnLabel;
+
+       graphics::MenuProperties *itemMenuProperties;
+       const char *itemMenuUseText;
+       const char *itemMenuSortText;
+       const char *itemMenuDropText;
+
+       graphics::MenuProperties *inventoryMenuProperties;
+
+       graphics::MenuProperties *spellMenuProperties;
+
+       graphics::MenuProperties *equipmentActionMenuProperties;
+       graphics::MenuProperties *equipmentMenuProperties;
+       const char *equipMenuEquipLabel;
+       const char *equipMenuStrongestLabel;
+       const char *equipMenuRemoveLabel;
+       const char *equipMenuRemoveAllLabel;
+       const char *equipMenuDropLabel;
+
+       graphics::MenuProperties *configMenuProperties;
+       const char *configMessageSpeedLabel;
+       const char *configMessageSpeedFast;
+       const char *configMessageSpeedNormal;
+       const char *configMessageSpeedSlow;
+       const char *configBattleCursorLabel;
+       const char *configStatusCursorLabel;
+       const char *configCursorClear;
+       const char *configCursorMemory;
+       const char *configMusicLabel;
+       const char *configMusicStereo;
+       const char *configMusicMono;
+
+       graphics::MenuProperties *scenarioMenuProperties;
+       const char *scenarioMenuHeadline;
+
+       Resources();
+
+};
+
+}
+
+#endif /* MENU_RESOURCES_H_ */
diff --git a/src/menu/ScenarioMenu.cpp b/src/menu/ScenarioMenu.cpp
new file mode 100644 (file)
index 0000000..a2aca87
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * ScenarioMenu.cpp
+ *
+ *  Created on: Nov 30, 2012
+ *      Author: holy
+ */
+
+#include "ScenarioMenu.h"
+
+#include "PartyMenu.h"
+#include "Resources.h"
+#include "../app/Input.h"
+#include "../common/GameConfig.h"
+#include "../common/GameState.h"
+#include "../common/Inventory.h"
+#include "../common/Item.h"
+#include "../graphics/Font.h"
+#include "../graphics/Frame.h"
+
+using app::Input;
+using common::Inventory;
+using common::Item;
+using geometry::Vector;
+using graphics::Font;
+using graphics::Frame;
+
+namespace menu {
+
+ScenarioMenu::ScenarioMenu(PartyMenu *parent)
+: parent(parent)
+, itemMenu(*parent->Res().scenarioMenuProperties) {
+
+}
+
+
+void ScenarioMenu::OnEnterState(SDL_Surface *) {
+       LoadItems();
+}
+
+void ScenarioMenu::LoadItems() {
+       const Inventory &inv(parent->Game().state->inventory);
+       itemMenu.Clear();
+       itemMenu.Reserve(inv.MaxScenarioItems());
+       int i = 0;
+       for (; i < inv.NumScenarioItems(); ++i) {
+               const Item *item(inv.ScenarioItemAt(i));
+               itemMenu.Add(item->Name(), item, item->CanUseOnStatusScreen(), item->MenuIcon(), inv.ItemCountAt(i));
+       }
+       for (; i < inv.MaxScenarioItems(); ++i) {
+               itemMenu.AddEmptyEntry();
+       }
+}
+
+void ScenarioMenu::OnExitState(SDL_Surface *) {
+
+}
+
+void ScenarioMenu::OnResumeState(SDL_Surface *) {
+
+}
+
+void ScenarioMenu::OnPauseState(SDL_Surface *) {
+
+}
+
+
+void ScenarioMenu::OnResize(int width, int height) {
+
+}
+
+
+void ScenarioMenu::HandleEvents(const Input &input) {
+       if (input.JustPressed(Input::PAD_DOWN)) {
+               itemMenu.NextRow();
+       }
+       if (input.JustPressed(Input::PAD_UP)) {
+               itemMenu.PreviousRow();
+       }
+       if (input.JustPressed(Input::ACTION_B)) {
+               Ctrl().PopState();
+       }
+}
+
+void ScenarioMenu::UpdateWorld(float deltaT) {
+
+}
+
+
+void ScenarioMenu::Render(SDL_Surface *screen) {
+       const Font &font(*parent->Res().normalFont);
+       Vector<int> offset((screen->w - Width()) / 2, (screen->h - Height()) / 2);
+       Vector<int> headlineOffset(font.CharWidth(), 13 * font.CharHeight() + font.CharHeight() / 8);
+       Vector<int> itemsOffset(font.CharWidth(), 16 * font.CharHeight() + font.CharHeight() / 8);
+
+       parent->RenderBackground(screen);
+       parent->RenderHeros(screen, offset);
+       RenderHeadline(screen, offset + headlineOffset);
+       RenderItems(screen, offset + itemsOffset);
+}
+
+int ScenarioMenu::Width() const {
+       return parent->Width();
+}
+
+int ScenarioMenu::Height() const {
+       return parent->Height();
+}
+
+void ScenarioMenu::RenderHeadline(SDL_Surface *screen, const geometry::Vector<int> &offset) const {
+       const Font &font(*parent->Res().normalFont);
+       const Frame &frame(*parent->Res().statusFrame);
+       const Vector<int> textOffset(2 * font.CharWidth(), font.CharHeight());
+
+       int width = font.StringWidth(parent->Res().scenarioMenuHeadline) + 4 * font.CharWidth();
+       frame.Draw(screen, offset, width, 3 * font.CharHeight());
+       font.DrawString(parent->Res().scenarioMenuHeadline, screen, offset + textOffset);
+}
+
+void ScenarioMenu::RenderItems(SDL_Surface *screen, const geometry::Vector<int> &offset) const {
+       const Font &font(*parent->Res().normalFont);
+       const Frame &frame(*parent->Res().statusFrame);
+       const Vector<int> menuOffset(3 * font.CharWidth(), font.CharHeight() + font.CharHeight() / 4);
+
+       frame.Draw(screen, offset, 30 * font.CharWidth(), 11 * font.CharHeight());
+       itemMenu.Draw(screen, offset + menuOffset);
+}
+
+}
diff --git a/src/menu/ScenarioMenu.h b/src/menu/ScenarioMenu.h
new file mode 100644 (file)
index 0000000..bcc2bbc
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * ScenarioMenu.h
+ *
+ *  Created on: Nov 30, 2012
+ *      Author: holy
+ */
+
+#ifndef MENU_SCENARIOMENU_H_
+#define MENU_SCENARIOMENU_H_
+
+#include "fwd.h"
+#include "../app/State.h"
+#include "../common/fwd.h"
+#include "../geometry/Vector.h"
+#include "../graphics/Menu.h"
+
+namespace menu {
+
+class ScenarioMenu
+: public app::State {
+
+public:
+       explicit ScenarioMenu(PartyMenu *parent);
+
+public:
+       virtual void HandleEvents(const app::Input &);
+       virtual void UpdateWorld(float deltaT);
+       virtual void Render(SDL_Surface *);
+
+       int Width() const;
+       int Height() const;
+
+private:
+       virtual void OnEnterState(SDL_Surface *screen);
+       virtual void OnExitState(SDL_Surface *screen);
+       virtual void OnResumeState(SDL_Surface *screen);
+       virtual void OnPauseState(SDL_Surface *screen);
+
+       virtual void OnResize(int width, int height);
+
+       void LoadItems();
+
+       void RenderHeadline(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderItems(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+
+private:
+       PartyMenu *parent;
+       graphics::Menu<const common::Item *> itemMenu;
+
+};
+
+}
+
+#endif /* MENU_SCENARIOMENU_H_ */
diff --git a/src/menu/SelectHero.cpp b/src/menu/SelectHero.cpp
new file mode 100644 (file)
index 0000000..12690db
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * SelectHero.cpp
+ *
+ *  Created on: Oct 22, 2012
+ *      Author: holy
+ */
+
+#include "SelectHero.h"
+
+#include "HeroStatus.h"
+#include "PartyMenu.h"
+#include "Resources.h"
+#include "../app/Application.h"
+#include "../app/Input.h"
+#include "../common/GameConfig.h"
+#include "../common/GameState.h"
+#include "../common/Hero.h"
+#include "../geometry/Vector.h"
+#include "../graphics/Sprite.h"
+
+using app::Input;
+using geometry::Vector;
+
+namespace menu {
+
+SelectHero::SelectHero(app::State *parent, PartyMenu *pm, void *ref, Callback cb, int cursor)
+: parent(parent)
+, partyMenu(pm)
+, ref(ref)
+, callback(cb)
+, cursor(cursor) {
+
+}
+
+
+void SelectHero::OnEnterState(SDL_Surface *) {
+       cursorBlink = GraphicsTimers().StartInterval(Res().heroCursorBlinkTime);
+}
+
+void SelectHero::OnExitState(SDL_Surface *) {
+
+}
+
+void SelectHero::OnResumeState(SDL_Surface *) {
+
+}
+
+void SelectHero::OnPauseState(SDL_Surface *) {
+
+}
+
+
+void SelectHero::OnResize(int width, int height) {
+
+}
+
+
+void SelectHero::HandleEvents(const Input &input) {
+       if (input.JustPressed(Input::ACTION_A)) {
+               callback(ref, cursor);
+       }
+       if (input.JustPressed(Input::ACTION_B)) {
+               Ctrl().PopState();
+       }
+
+       if (input.JustPressed(Input::PAD_UP)) {
+               SelectUp();
+       } else if (input.JustPressed(Input::PAD_RIGHT)) {
+               SelectRight();
+       } else if (input.JustPressed(Input::PAD_DOWN)) {
+               SelectDown();
+       } else if (input.JustPressed(Input::PAD_LEFT)) {
+               SelectLeft();
+       }
+}
+
+void SelectHero::SelectUp() {
+       cursor = (cursor + 2) % partyMenu->Game().state->partySize;
+       cursorBlink.Restart();
+}
+
+void SelectHero::SelectRight() {
+       cursor = (cursor + 1) % partyMenu->Game().state->partySize;
+       cursorBlink.Restart();
+}
+
+void SelectHero::SelectDown() {
+       cursor = (cursor + 2) % partyMenu->Game().state->partySize;
+       cursorBlink.Restart();
+}
+
+void SelectHero::SelectLeft() {
+       cursor = (cursor + 3) % partyMenu->Game().state->partySize;
+       cursorBlink.Restart();
+}
+
+
+common::GameConfig &SelectHero::Game() {
+       return partyMenu->Game();
+}
+
+const common::GameConfig &SelectHero::Game() const {
+       return partyMenu->Game();
+}
+
+Resources &SelectHero::Res() {
+       return partyMenu->Res();
+}
+
+const Resources &SelectHero::Res() const {
+       return partyMenu->Res();
+}
+
+
+void SelectHero::UpdateWorld(float deltaT) {
+
+}
+
+
+void SelectHero::Render(SDL_Surface *screen) {
+       parent->Render(screen);
+       if (cursorBlink.Iteration() % 2 == 0) {
+               RenderCursor(screen);
+       }
+}
+
+void SelectHero::RenderCursor(SDL_Surface *screen) const {
+       Vector<int> position(
+                       0, Game().state->party[cursor]->BattleSprite()->Height());
+       position += partyMenu->StatusOffset(cursor);
+       Res().heroCursor->Draw(screen, position);
+}
+
+}
diff --git a/src/menu/SelectHero.h b/src/menu/SelectHero.h
new file mode 100644 (file)
index 0000000..5e67152
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * SelectHero.h
+ *
+ *  Created on: Oct 22, 2012
+ *      Author: holy
+ */
+
+#ifndef MENU_SELECTHERO_H_
+#define MENU_SELECTHERO_H_
+
+#include "fwd.h"
+#include "../app/State.h"
+#include "../app/Timer.h"
+#include "../common/fwd.h"
+
+#include <SDL.h>
+
+namespace menu {
+
+class SelectHero
+: public app::State {
+
+public:
+       typedef void (*Callback)(void *, int selection);
+
+public:
+       SelectHero(app::State *parent, PartyMenu *partyMenu, void *ref, Callback, int initialHero = 0);
+
+public:
+       virtual void HandleEvents(const app::Input &);
+       virtual void UpdateWorld(float deltaT);
+       virtual void Render(SDL_Surface *);
+
+private:
+       virtual void OnEnterState(SDL_Surface *screen);
+       virtual void OnExitState(SDL_Surface *screen);
+       virtual void OnResumeState(SDL_Surface *screen);
+       virtual void OnPauseState(SDL_Surface *screen);
+
+       virtual void OnResize(int width, int height);
+
+private:
+       common::GameConfig &Game();
+       const common::GameConfig &Game() const;
+       Resources &Res();
+       const Resources &Res() const;
+
+       void SelectUp();
+       void SelectRight();
+       void SelectDown();
+       void SelectLeft();
+
+       void RenderCursor(SDL_Surface *screen) const;
+
+private:
+       app::State *parent;
+       PartyMenu *partyMenu;
+       void *ref;
+       Callback callback;
+       app::Timer<Uint32> cursorBlink;
+       int cursor;
+
+};
+
+}
+
+#endif /* MENU_SELECTHERO_H_ */
diff --git a/src/menu/SpellMenu.cpp b/src/menu/SpellMenu.cpp
new file mode 100644 (file)
index 0000000..57989ed
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * SpellMenu.cpp
+ *
+ *  Created on: Nov 18, 2012
+ *      Author: holy
+ */
+
+#include "SpellMenu.h"
+
+#include "HeroStatus.h"
+#include "PartyMenu.h"
+#include "Resources.h"
+#include "../app/Input.h"
+#include "../common/GameConfig.h"
+#include "../common/GameState.h"
+#include "../common/Hero.h"
+#include "../common/Spell.h"
+#include "../graphics/Font.h"
+#include "../graphics/Frame.h"
+
+#include <algorithm>
+#include <SDL.h>
+#include <vector>
+
+using app::Input;
+using common::Hero;
+using common::Spell;
+using geometry::Vector;
+using graphics::Font;
+using graphics::Frame;
+using std::vector;
+
+namespace menu {
+
+SpellMenu::SpellMenu(PartyMenu *parent, int cursor)
+: parent(parent)
+, highlight(0)
+, cursor(cursor)
+, actionMenu(*parent->Res().itemMenuProperties)
+, spellMenu(*parent->Res().spellMenuProperties) {
+       actionMenu.Add(parent->Res().itemMenuUseText, CHOICE_USE);
+       actionMenu.Add(parent->Res().itemMenuSortText, CHOICE_SORT);
+}
+
+
+void SpellMenu::OnEnterState(SDL_Surface *) {
+       const HeroStatus &status(parent->GetHeroStatus(0));
+       highlight = SDL_CreateRGBSurface(0, status.Width(), status.Height(), 32, 0xFF000000, 0xFF0000, 0xFF00, 0);
+       SDL_FillRect(highlight, 0, SDL_MapRGB(highlight->format, 0xFF, 0xFF, 0xFF));
+       SDL_SetAlpha(highlight, SDL_SRCALPHA|SDL_RLEACCEL, 0x20);
+
+       actionMenu.SetSelected();
+       LoadSpells();
+}
+
+void SpellMenu::LoadSpells() {
+       spellMenu.Clear();
+       // TODO: set to max spells once implementation is changed
+       spellMenu.Reserve(GetHero().Spells().size());
+       for (vector<const Spell *>::const_iterator
+                       i = GetHero().Spells().begin(), end = GetHero().Spells().end();
+                       i != end; ++i) {
+               const Spell *spell = *i;
+               spellMenu.Add(spell->Name(), spell, spell->CanUseOnStatusScreen(), 0, spell->Cost());
+       }
+}
+
+const Hero &SpellMenu::GetHero() const {
+       return *parent->Game().state->party[cursor];
+}
+
+Hero &SpellMenu::GetHero() {
+       return *parent->Game().state->party[cursor];
+}
+
+void SpellMenu::OnExitState(SDL_Surface *) {
+       SDL_FreeSurface(highlight);
+}
+
+void SpellMenu::OnResumeState(SDL_Surface *) {
+
+}
+
+void SpellMenu::OnPauseState(SDL_Surface *) {
+
+}
+
+
+void SpellMenu::OnResize(int width, int height) {
+
+}
+
+
+void SpellMenu::HandleEvents(const Input &input) {
+       if (actionMenu.IsActive()) {
+               if (input.JustPressed(Input::PAD_LEFT)) {
+                       actionMenu.PreviousItem();
+               }
+               if (input.JustPressed(Input::PAD_RIGHT)) {
+                       actionMenu.NextItem();
+               }
+       } else {
+               if (input.JustPressed(Input::PAD_UP)) {
+                       spellMenu.PreviousRow();
+               }
+               if (input.JustPressed(Input::PAD_RIGHT)) {
+                       spellMenu.NextItem();
+               }
+               if (input.JustPressed(Input::PAD_DOWN)) {
+                       spellMenu.NextRow();
+               }
+               if (input.JustPressed(Input::PAD_LEFT)) {
+                       spellMenu.PreviousItem();
+               }
+       }
+
+       if (input.JustPressed(Input::ACTION_A)) {
+               if (actionMenu.IsActive()) {
+                       if (actionMenu.Selected() == CHOICE_SORT) {
+                               std::sort(GetHero().Spells().begin(),
+                                               GetHero().Spells().end(),
+                                               Spell::Less);
+                               LoadSpells();
+                       } else {
+                               actionMenu.SetSelected();
+                               spellMenu.SetActive();
+                       }
+               } else if (spellMenu.IsActive()) {
+                       spellMenu.SetDualSelection();
+               } else if (spellMenu.SelectedIndex() == spellMenu.SecondaryIndex()) {
+                       if (spellMenu.Selected()->CanUseOnStatusScreen()) {
+                               // TODO: use spell
+                       }
+               } else {
+                       std::swap(GetHero().Spells().at(spellMenu.SelectedIndex()),
+                                       GetHero().Spells().at(spellMenu.SecondaryIndex()));
+                       spellMenu.SwapSelected();
+                       spellMenu.SetActive();
+               }
+       }
+
+       if (input.JustPressed(Input::ACTION_B)) {
+               if (actionMenu.IsActive()) {
+                       Ctrl().PopState();
+               } else if (spellMenu.IsActive()) {
+                       actionMenu.SetActive();
+                       spellMenu.SetInactive();
+               } else {
+                       spellMenu.SetActive();
+               }
+       }
+}
+
+void SpellMenu::UpdateWorld(float deltaT) {
+
+}
+
+void SpellMenu::Render(SDL_Surface *screen) {
+       const Font &font(*parent->Res().normalFont);
+       Vector<int> offset((screen->w - Width()) / 2, (screen->h - Height()) / 2);
+       Vector<int> menuOffset(font.CharWidth(), 13 * font.CharHeight() + font.CharHeight() / 8);
+       Vector<int> spellsOffset(font.CharWidth(), 16 * font.CharHeight() + font.CharHeight() / 8);
+
+       parent->RenderBackground(screen);
+       RenderHighlight(screen, offset);
+       parent->RenderHeros(screen, offset);
+       RenderMenu(screen, menuOffset + offset);
+       RenderSpells(screen, spellsOffset + offset);
+}
+
+int SpellMenu::Width() const {
+       return parent->Width();
+}
+
+int SpellMenu::Height() const {
+       return parent->Height();
+}
+
+void SpellMenu::RenderHighlight(SDL_Surface *screen, const Vector<int> &offset) const {
+       if (cursor < 0) return;
+       Vector<int> statusOffset(parent->StatusOffset(cursor));
+       statusOffset -= Vector<int>(0, parent->Res().normalFont->CharHeight() / 8);
+       SDL_Rect rect;
+       rect.x = statusOffset.X();
+       rect.y = statusOffset.Y();
+       SDL_BlitSurface(highlight, 0, screen, &rect);
+}
+
+void SpellMenu::RenderMenu(SDL_Surface *screen, const Vector<int> &offset) const {
+       const Font &font(*parent->Res().normalFont);
+       const Frame &frame(*parent->Res().statusFrame);
+
+       const Vector<int> labelOffset(2 * font.CharWidth(), font.CharHeight());
+       const Vector<int> menuFrameOffset(offset.X() + 9 * font.CharWidth(), offset.Y());
+       const Vector<int> menuOffset(menuFrameOffset.X() + 3 * font.CharWidth(), menuFrameOffset.Y() + font.CharHeight());
+
+       frame.Draw(screen, offset, 9 * font.CharWidth(), 3 * font.CharHeight());
+       font.DrawString(parent->Res().mainMenuSpellText, screen, labelOffset + offset);
+       frame.Draw(screen, menuFrameOffset, 21 * font.CharWidth(), 3 * font.CharHeight());
+       actionMenu.Draw(screen, menuOffset);
+}
+
+void SpellMenu::RenderSpells(SDL_Surface *screen, const Vector<int> &offset) const {
+       const Font &font(*parent->Res().normalFont);
+       const Frame &frame(*parent->Res().statusFrame);
+       const Vector<int> menuOffset(3 * font.CharWidth(), font.CharHeight() + font.CharHeight() / 4);
+
+       frame.Draw(screen, offset, 30 * font.CharWidth(), 11 * font.CharHeight());
+       spellMenu.Draw(screen, offset + menuOffset);
+}
+
+}
diff --git a/src/menu/SpellMenu.h b/src/menu/SpellMenu.h
new file mode 100644 (file)
index 0000000..d289794
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * SpellMenu.h
+ *
+ *  Created on: Nov 18, 2012
+ *      Author: holy
+ */
+
+#ifndef MENU_SPELLMENU_H_
+#define MENU_SPELLMENU_H_
+
+#include "fwd.h"
+#include "../app/State.h"
+#include "../common/fwd.h"
+#include "../geometry/Vector.h"
+#include "../graphics/Menu.h"
+
+namespace menu {
+
+class SpellMenu
+: public app::State {
+
+public:
+       SpellMenu(PartyMenu *parent, int heroIndex);
+
+public:
+       virtual void HandleEvents(const app::Input &);
+       virtual void UpdateWorld(float deltaT);
+       virtual void Render(SDL_Surface *);
+
+public:
+       int Width() const;
+       int Height() const;
+
+private:
+       virtual void OnEnterState(SDL_Surface *screen);
+       virtual void OnExitState(SDL_Surface *screen);
+       virtual void OnResumeState(SDL_Surface *screen);
+       virtual void OnPauseState(SDL_Surface *screen);
+
+       virtual void OnResize(int width, int height);
+
+       common::Hero &GetHero();
+       const common::Hero &GetHero() const;
+
+       void LoadSpells();
+
+       void RenderHighlight(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderMenu(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderSpells(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+
+private:
+       PartyMenu *parent;
+       SDL_Surface *highlight;
+       int cursor;
+       enum Choice {
+               CHOICE_USE,
+               CHOICE_SORT,
+       };
+       graphics::Menu<Choice> actionMenu;
+       graphics::Menu<const common::Spell *> spellMenu;
+
+};
+
+}
+
+#endif /* MENU_SPELLMENU_H_ */
diff --git a/src/menu/StatusMenu.cpp b/src/menu/StatusMenu.cpp
new file mode 100644 (file)
index 0000000..be67743
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * StatusMenu.cpp
+ *
+ *  Created on: Oct 22, 2012
+ *      Author: holy
+ */
+
+#include "StatusMenu.h"
+
+#include "HeroStatus.h"
+#include "PartyMenu.h"
+#include "Resources.h"
+#include "../app/Application.h"
+#include "../app/Input.h"
+#include "../common/GameConfig.h"
+#include "../common/GameState.h"
+#include "../common/Hero.h"
+#include "../common/Item.h"
+#include "../common/Stats.h"
+#include "../graphics/Font.h"
+#include "../graphics/Frame.h"
+
+using app::Input;
+using common::Hero;
+using common::Item;
+using common::Stats;
+using geometry::Vector;
+using graphics::Font;
+using graphics::Frame;
+
+namespace menu {
+
+StatusMenu::StatusMenu(PartyMenu *parent, int cursor)
+: parent(parent)
+, cursor(cursor)
+, menu(*parent->Res().statusMenuProperties) {
+       menu.Add(parent->Res().nextLabel, 0);
+       menu.Add(parent->Res().returnLabel, 1);
+}
+
+
+void StatusMenu::OnEnterState(SDL_Surface *) {
+
+}
+
+void StatusMenu::OnExitState(SDL_Surface *) {
+
+}
+
+void StatusMenu::OnResumeState(SDL_Surface *) {
+
+}
+
+void StatusMenu::OnPauseState(SDL_Surface *) {
+
+}
+
+
+void StatusMenu::OnResize(int width, int height) {
+
+}
+
+
+void StatusMenu::HandleEvents(const Input &input) {
+       if (input.JustPressed(Input::SHOULDER_RIGHT)) {
+               NextHero();
+       }
+       if (input.JustPressed(Input::SHOULDER_LEFT)) {
+               PreviousHero();
+       }
+
+       if (input.JustPressed(Input::PAD_LEFT)) {
+               menu.PreviousItem();
+       }
+       if (input.JustPressed(Input::PAD_RIGHT)) {
+               menu.NextItem();
+       }
+
+       if (input.JustPressed(Input::ACTION_A)) {
+               if (menu.Selected() == 0) {
+                       NextHero();
+               } else if (menu.Selected() == 1) {
+                       Ctrl().PopState();
+               }
+       }
+       if (input.JustPressed(Input::ACTION_B)) {
+               Ctrl().PopState();
+       }
+}
+
+void StatusMenu::UpdateWorld(float deltaT) {
+
+}
+
+void StatusMenu::Render(SDL_Surface *screen) {
+       Vector<int> offset((screen->w - Width()) / 2, (screen->h - Height()) / 2);
+       Vector<int> shoulderNavOffset(
+                       5 * parent->Res().statusFont->CharWidth(),
+                       parent->Res().statusFont->CharHeight());
+       Vector<int> statsOffset(
+                       4 * parent->Res().statusFont->CharWidth(),
+                       8 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8);
+       Vector<int> equipOffset(
+                       17 * parent->Res().statusFont->CharWidth(),
+                       4 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8);
+       Vector<int> experienceOffset(
+                       11 * parent->Res().statusFont->CharWidth(),
+                       17 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8);
+       Vector<int> nextLevelOffset(
+                       11 * parent->Res().statusFont->CharWidth(),
+                       20 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8);
+       Vector<int> ikariOffset(
+                       17 * parent->Res().statusFont->CharWidth(),
+                       17 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8);
+       Vector<int> menuOffset(
+                       parent->Res().statusFont->CharWidth(),
+                       23 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8);
+
+       parent->RenderBackground(screen);
+       parent->Res().shoulderNav->Draw(screen, offset + shoulderNavOffset);
+       RenderStatus(screen, offset + parent->StatusOffset(0));
+       RenderStats(screen, offset + statsOffset);
+       RenderEquipment(screen, offset + equipOffset);
+       RenderExperience(screen, experienceOffset);
+       RenderNextLevel(screen, nextLevelOffset);
+       RenderIkari(screen, ikariOffset);
+       RenderMenu(screen, menuOffset);
+}
+
+int StatusMenu::Width() const {
+       return parent->Width();
+}
+
+int StatusMenu::Height() const {
+       return parent->Height();
+}
+
+void StatusMenu::RenderStatus(SDL_Surface *screen, const Vector<int> &offset) const {
+       parent->GetHeroStatus(cursor).Render(screen, offset);
+}
+
+void StatusMenu::RenderStats(SDL_Surface *screen, const Vector<int> &offset) const {
+       const Stats &stats(GetHero().GetStats());
+       Vector<int> lineBreak(0, parent->Res().statusFont->CharHeight());
+
+       Vector<int> position(offset);
+       RenderStatsLine(parent->Res().atpLabel, stats.Attack(), screen, position);
+
+       position += lineBreak;
+       RenderStatsLine(parent->Res().dfpLabel, stats.Defense(), screen, position);
+
+       position += lineBreak;
+       RenderStatsLine(parent->Res().strLabel, stats.Strength(), screen, position);
+
+       position += lineBreak;
+       RenderStatsLine(parent->Res().aglLabel, stats.Agility(), screen, position);
+
+       position += lineBreak;
+       RenderStatsLine(parent->Res().intLabel, stats.Intelligence(), screen, position);
+
+       position += lineBreak;
+       RenderStatsLine(parent->Res().gutLabel, stats.Gut(), screen, position);
+
+       position += lineBreak;
+       RenderStatsLine(parent->Res().mgrLabel, stats.MagicResistance(), screen, position);
+}
+
+void StatusMenu::RenderStatsLine(const char *label, int number, SDL_Surface *screen, const Vector<int> &position) const {
+       const Font &font(*parent->Res().statusFont);
+       const Vector<int> numberOffset(4 * font.CharWidth(), 0);
+
+       font.DrawString(label, screen, position, 3);
+       font.DrawNumber(number, screen, position + numberOffset, 3);
+}
+
+void StatusMenu::RenderEquipment(SDL_Surface *screen, const Vector<int> &offset) const {
+       const Hero &hero(GetHero());
+       Vector<int> lineBreak(0, 2 * parent->Res().statusFont->CharHeight());
+
+       Vector<int> position(offset);
+       for (int i = 0; i < Hero::EQUIP_COUNT; ++i) {
+               RenderEquipmentLine(hero.Equipment(Hero::EquipSlot(i)), screen, position);
+               position += lineBreak;
+       }
+}
+
+void StatusMenu::RenderEquipmentLine(const Item *item, SDL_Surface *screen, const Vector<int> &position) const {
+       const Font &font(*parent->Res().statusFont);
+       const Vector<int> textOffset(font.CharWidth(), 0);
+       if (item) {
+               if (item->MenuIcon()) {
+                       item->MenuIcon()->Draw(screen, position);
+               }
+               font.DrawString(item->Name(), screen, position + textOffset);
+       } else {
+               font.DrawString(parent->Res().noEquipmentText, screen, position + textOffset);
+       }
+}
+
+void StatusMenu::RenderExperience(SDL_Surface *screen, const geometry::Vector<int> &offset) const {
+       const Font &font(*parent->Res().statusFont);
+       font.DrawStringRight(parent->Res().experienceLabel, screen, offset, 10);
+
+       Vector<int> numberOffset(offset.X(), offset.Y() + font.CharHeight());
+       font.DrawNumberRight(GetHero().Experience(), screen, numberOffset, 7);
+}
+
+void StatusMenu::RenderNextLevel(SDL_Surface *screen, const geometry::Vector<int> &offset) const {
+       const Font &font(*parent->Res().statusFont);
+       font.DrawStringRight(parent->Res().nextLevelLabel, screen, offset, 10);
+
+       Vector<int> numberOffset(offset.X(), offset.Y() + font.CharHeight());
+       font.DrawNumberRight(GetHero().NextLevel(), screen, numberOffset, 7);
+}
+
+void StatusMenu::RenderIkari(SDL_Surface *screen, const geometry::Vector<int> &offset) const {
+       const Font &font(*parent->Res().statusFont);
+       font.DrawString(parent->Res().ipLabel, screen, offset, 5);
+
+       Vector<int> numberOffset(offset.X() + 5 * font.CharWidth(), offset.Y());
+       font.DrawNumber(GetHero().RelativeIP(100), screen, numberOffset, 3);
+
+       Vector<int> percentOffset(offset.X() + 8 * font.CharWidth(), offset.Y());
+       font.DrawChar('%', screen, percentOffset);
+}
+
+void StatusMenu::RenderMenu(SDL_Surface *screen, const geometry::Vector<int> &offset) const {
+       const Font &font(*parent->Res().normalFont);
+       const Frame &frame(*parent->Res().statusFrame);
+
+       Vector<int> labelOffset(2 * font.CharWidth(), font.CharHeight());
+       frame.Draw(screen, offset, 10 * font.CharWidth(), 3 * font.CharHeight());
+       font.DrawString(parent->Res().mainMenuStatusText, screen, offset + labelOffset);
+
+       Vector<int> menuFrameOffset(10 * font.CharWidth(), 0);
+       Vector<int> menuOffset(13 * font.CharWidth(), font.CharHeight());
+       frame.Draw(screen, offset + menuFrameOffset, 20 * font.CharWidth(), 3 * font.CharHeight());
+       menu.Draw(screen, offset + menuOffset);
+}
+
+
+void StatusMenu::NextHero() {
+       cursor = (cursor + 1) % parent->Game().state->partySize;
+}
+
+void StatusMenu::PreviousHero() {
+       cursor = (cursor + parent->Game().state->partySize - 1) % parent->Game().state->partySize;
+}
+
+const Hero &StatusMenu::GetHero() const {
+       return *parent->Game().state->party[cursor];
+}
+
+}
diff --git a/src/menu/StatusMenu.h b/src/menu/StatusMenu.h
new file mode 100644 (file)
index 0000000..5f6f986
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * StatusMenu.h
+ *
+ *  Created on: Oct 22, 2012
+ *      Author: holy
+ */
+
+#ifndef MENU_STATUSMENU_H_
+#define MENU_STATUSMENU_H_
+
+#include "fwd.h"
+#include "../app/State.h"
+#include "../common/fwd.h"
+#include "../geometry/Vector.h"
+#include "../graphics/Menu.h"
+
+namespace menu {
+
+class StatusMenu
+: public app::State {
+
+public:
+       StatusMenu(PartyMenu *parent, int heroIndex);
+
+public:
+       virtual void HandleEvents(const app::Input &);
+       virtual void UpdateWorld(float deltaT);
+       virtual void Render(SDL_Surface *);
+
+public:
+       int Width() const;
+       int Height() const;
+
+private:
+       virtual void OnEnterState(SDL_Surface *screen);
+       virtual void OnExitState(SDL_Surface *screen);
+       virtual void OnResumeState(SDL_Surface *screen);
+       virtual void OnPauseState(SDL_Surface *screen);
+
+       virtual void OnResize(int width, int height);
+
+       void NextHero();
+       void PreviousHero();
+
+       const common::Hero &GetHero() const;
+
+       void RenderStatus(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderStats(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderStatsLine(const char *label, int number, SDL_Surface *screen, const geometry::Vector<int> &position) const;
+       void RenderEquipment(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderEquipmentLine(const common::Item *, SDL_Surface *screen, const geometry::Vector<int> &position) const;
+       /// @param offset the top right corner!
+       void RenderExperience(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       /// @param offset the top right corner!
+       void RenderNextLevel(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderIkari(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+       void RenderMenu(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+
+private:
+       PartyMenu *parent;
+       int cursor;
+       graphics::Menu<int> menu;
+
+};
+
+}
+
+#endif /* MENU_STATUSMENU_H_ */
diff --git a/src/menu/fwd.h b/src/menu/fwd.h
new file mode 100644 (file)
index 0000000..b4b96e5
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * fwd.h
+ *
+ *  Created on: Oct 21, 2012
+ *      Author: holy
+ */
+
+#ifndef MENU_FWD_H_
+#define MENU_FWD_H_
+
+namespace menu {
+
+class ChangeHero;
+class ConfigMenu;
+class EquipMenu;
+class HeroStatus;
+class InventoryMenu;
+class PartyMenu;
+struct Resources;
+class ScenarioMenu;
+class SelectHero;
+class SpellMenu;
+class StatusMenu;
+
+}
+
+#endif /* MENU_FWD_H_ */
index 5ace629e05f3d0376fa2629fd55a53dfc9f3dd8c..fea7abf4a1fba122d4bc46216403c20f9fc243ba 100644 (file)
@@ -6,10 +6,28 @@ Number all
 Number single
 Number multiple
 
+// Hero masks
+Number maskMaxim
+Number maskSelan
+Number maskGuy
+Number maskArtea
+Number maskDekar
+Number maskTia
+Number maskLexis
+Number maskAll
+
 // Ikari
 Boolean magical
 Boolean physical
 
+// Items
+Number weapon
+Number armor
+Number shield
+Number helmet
+Number ring
+Number jewel
+
 // Trigger type
 Number triggerNorth
 Number triggerEast
index fa01f2b2eb9275794c2c7703f988c297fe4b4921..47dcb9a291db6d6ecac94daa1873ed30d64f23aa 100644 (file)
@@ -6,10 +6,28 @@ export Number all 0
 export Number multiple 1
 export Number single 2
 
+// Hero masks
+export Number maskMaxim 1
+export Number maskSelan 2
+export Number maskGuy 4
+export Number maskArtea 8
+export Number maskDekar 16
+export Number maskTia 32
+export Number maskLexis 64
+export Number maskAll 127
+
 // Ikari
 export Boolean magical false
 export Boolean physical true
 
+// Items
+export Number weapon 1
+export Number armor 2
+export Number shield 4
+export Number helmet 8
+export Number ring 16
+export Number jewel 32
+
 // Trigger type
 export Number triggerNorth 0
 export Number triggerEast 1
diff --git a/test-data/hero-cursor.png b/test-data/hero-cursor.png
new file mode 100644 (file)
index 0000000..0496815
Binary files /dev/null and b/test-data/hero-cursor.png differ
index 4b49dd6dd457fc763860d46c8d1872a01740aef6..231aa3f626f498c93b51369b10f4c9396121cae4 100644 (file)
@@ -78,26 +78,34 @@ export Item antidoteItem {
        targets: TargetingMode {
                faction: ally,
                mode: single
-       }
+       },
+       mostUseful: true
 }
 export Item eagleRockItem {
        name: "Eagle rock",
        menuicon: jewelIcon,
-       ikari: diveIkari
+       ikari: diveIkari,
+       equipability: jewel,
+       heroMask: maskAll
 }
 export Item escapeItem {
        name: "Escape",
-       battle: false
+       battle: false,
+       mostUseful: true
 }
 export Item evilJewelItem {
        name: "Evil jewel",
        menuicon: jewelIcon,
-       ikari: gloomyIkari
+       ikari: gloomyIkari,
+       equipability: jewel,
+       heroMask: maskAll
 }
 export Item ghostRingItem {
        name: "Ghost ring",
        menuicon: ringIcon,
-       ikari: destroyIkari
+       ikari: destroyIkari,
+       equipability: ring,
+       heroMask: maskAll
 }
 export Item hiPotionItem {
        name: "Hi-Potion",
@@ -106,32 +114,43 @@ export Item hiPotionItem {
        targets: TargetingMode {
                faction: ally,
                mode: single
-       }
+       },
+       mostUseful: true
 }
 export Item holyCapItem {
        name: "Holy cap",
        menuicon: helmetIcon,
-       ikari: vulnerableIkari
+       ikari: vulnerableIkari,
+       equipability: helmet,
+       heroMask: 42 // Selan, Artea, Tia
 }
 export Item holyRobeItem {
        name: "Holy robe",
        menuicon: armorIcon,
-       ikari: crisisCureIkari
+       ikari: crisisCureIkari,
+       equipability: armor,
+       heroMask: 42 // Selan, Artea, Tia
 }
 export Item holyShieldItem {
        name: "Holy shield",
        menuicon: shieldIcon,
-       ikari: lightGuardIkari
+       ikari: lightGuardIkari,
+       equipability: shield,
+       heroMask: 21 // Maxim, Guy, Dekar
 }
 export Item krakenRockItem {
        name: "Kraken rock",
        menuicon: jewelIcon,
-       ikari: tenLeggerIkari
+       ikari: tenLeggerIkari,
+       equipability: jewel,
+       heroMask: maskAll
 }
 export Item legendHelmItem {
        name: "Legend helm",
        menuicon: helmetIcon,
-       ikari: boomerangIkari
+       ikari: boomerangIkari,
+       equipability: helmet,
+       heroMask: 21 // Maxim, Guy, Dekar
 }
 export Item lizardBlowItem {
        name: "Lizard blow",
@@ -140,7 +159,9 @@ export Item lizardBlowItem {
        targets: TargetingMode {
                faction: enemy,
                mode: single
-       }
+       },
+       equipability: weapon,
+       heroMask: maskAll
 }
 export Item magicJarItem {
        name: "Magic jar",
@@ -149,27 +170,35 @@ export Item magicJarItem {
        targets: TargetingMode {
                faction: ally,
                mode: single
-       }
+       },
+       mostUseful: true
 }
 export Item megaShieldItem {
        name: "Mega shield",
        menuicon: shieldIcon,
-       ikari: ironBarrierIkari
+       ikari: ironBarrierIkari,
+       equipability: shield,
+       heroMask: 20 // Guy, Dekar
 }
 export Item powerPotionItem {
        name: "Power potion",
        menuicon: potionIcon,
-       battle: false
+       battle: false,
+       mostUseful: true
 }
 export Item powerRingItem {
        name: "Power ring",
        menuicon: ringIcon,
-       ikari: trickIkari
+       ikari: trickIkari,
+       equipability: ring,
+       heroMask: maskAll
 }
 export Item rocketRingItem {
        name: "Rocket ring",
        menuicon: ringIcon,
-       ikari: fakeIkari
+       ikari: fakeIkari,
+       equipability: ring,
+       heroMask: maskAll
 }
 export Item sleepBallItem {
        name: "Sleep ball",
@@ -178,12 +207,15 @@ export Item sleepBallItem {
        targets: TargetingMode {
                faction: enemy,
                mode: single
-       }
+       },
+       mostUseful: true
 }
 export Item sProRingItem {
        name: "S-pro ring",
        menuicon: ringIcon,
-       ikari: courageIkari
+       ikari: courageIkari,
+       equipability: ring,
+       heroMask: maskAll
 }
 export Item zircoAxItem {
        name: "Zirco ax",
@@ -192,28 +224,38 @@ export Item zircoAxItem {
        targets: TargetingMode {
                faction: enemy,
                mode: single
-       }
+       },
+       equipability: weapon,
+       heroMask: 20 // Guy, Dekar
 }
 export Item zircoGlovesItem {
        name: "Zirco gloves",
        menuicon: shieldIcon,
-       ikari: forcefieldIkari
+       ikari: forcefieldIkari,
+       equipability: shield,
+       heroMask: 106 // Selan, Artea, Tia, Lexis
 }
 export Item zircoHelmetItem {
        name: "Zirco helmet",
        menuicon: helmetIcon,
-       ikari: slowIkari
+       ikari: slowIkari,
+       equipability: helmet,
+       heroMask: 21 // Maxim, Guy, Dekar
 }
 export Item zirconArmorItem {
        name: "Zircon armor",
        menuicon: armorIcon,
        battle: false,
-       ikari: magicCureIkari
+       ikari: magicCureIkari,
+       equipability: armor,
+       heroMask: 21 // Maxim, Guy, Dekar
 }
 export Item zirconPlateItem {
        name: "Zircon plate",
        menuicon: armorIcon,
-       ikari: suddenCureIkari
+       ikari: suddenCureIkari,
+       equipability: armor,
+       heroMask: 74 // Selan, Artea, Lexis
 }
 export Item zircoSwordItem {
        name: "Zirco sword",
@@ -224,7 +266,9 @@ export Item zircoSwordItem {
                mode: single
        },
        ikari: firestormIkari,
-       attackanimation: swordAttackAnimation
+       attackanimation: swordAttackAnimation,
+       equipability: weapon,
+       heroMask: maskMaxim
 }
 export Item zircoWhipItem {
        name: "Zirco whip",
@@ -233,5 +277,7 @@ export Item zircoWhipItem {
                faction: enemy,
                mode: single
        },
-       ikari: thundershriekIkari
+       ikari: thundershriekIkari,
+       equipability: weapon,
+       heroMask: 34 // Selan, Tia
 }
diff --git a/test-data/menu-cursor-active.png b/test-data/menu-cursor-active.png
new file mode 100644 (file)
index 0000000..bee2487
Binary files /dev/null and b/test-data/menu-cursor-active.png differ
diff --git a/test-data/menu-cursor.png b/test-data/menu-cursor.png
new file mode 100644 (file)
index 0000000..b5743f1
Binary files /dev/null and b/test-data/menu-cursor.png differ
diff --git a/test-data/menu-font-inactive.png b/test-data/menu-font-inactive.png
new file mode 100644 (file)
index 0000000..7ffc57a
Binary files /dev/null and b/test-data/menu-font-inactive.png differ
diff --git a/test-data/menu-font.png b/test-data/menu-font.png
new file mode 100644 (file)
index 0000000..a8122a8
Binary files /dev/null and b/test-data/menu-font.png differ
diff --git a/test-data/menubg.png b/test-data/menubg.png
new file mode 100644 (file)
index 0000000..1b42927
Binary files /dev/null and b/test-data/menubg.png differ
index 8a1532b71e4b679183ffd0079da7f2890df107b4..0c49e2c35f597457411e5ea6b566a0e3f584b252 100644 (file)
Binary files a/test-data/normal-font.png and b/test-data/normal-font.png differ
diff --git a/test-data/shoulder-nav.png b/test-data/shoulder-nav.png
new file mode 100644 (file)
index 0000000..54edbe6
Binary files /dev/null and b/test-data/shoulder-nav.png differ
diff --git a/test-data/status-frame.png b/test-data/status-frame.png
new file mode 100644 (file)
index 0000000..c6ea569
Binary files /dev/null and b/test-data/status-frame.png differ
diff --git a/test-data/status-labels.png b/test-data/status-labels.png
new file mode 100644 (file)
index 0000000..93fcebc
Binary files /dev/null and b/test-data/status-labels.png differ
index 4a4bb059b16dc9de5e1d57922bad2d2224354121..5f4fe2c034e0bb2a7c07b3b410625e22f5987c64 100644 (file)
@@ -88,6 +88,10 @@ export Hero maxim {
                gut: 100,
                mgr:  10
        },
+       ladder: [
+               10
+       ],
+       useMask: maskMaxim,
        attackAnimation: ComplexAnimation {
                sprite: maximSprite,
                frametime: frameTime,
@@ -177,6 +181,7 @@ export Hero selan {
                gut: 80,
                mgr: 13
        },
+       useMask: maskSelan,
        attackAnimation: ComplexAnimation {
                sprite: selanSprite,
                frametime: frameTime,
@@ -260,6 +265,7 @@ export Hero guy {
                gut: 90,
                mgr:  8
        },
+       useMask: maskGuy,
        attackAnimation: ComplexAnimation {
                sprite: guySprite,
                frametime: frameTime,
@@ -325,6 +331,7 @@ export Hero dekar {
                gut: 100,
                mgr:   5
        },
+       useMask: maskDekar,
        attackAnimation: ComplexAnimation {
                sprite: dekarSprite,
                frametime: frameTime,