From: Daniel Karbach Date: Fri, 30 Nov 2012 14:00:50 +0000 (+0100) Subject: Merge branch 'menus' X-Git-Url: http://git.localhorst.tv/?a=commitdiff_plain;h=350055a7ff27c74882aff8a4d6af2014782f830b;hp=95bbd3d0b18007bf8614b1bf16042949b12fc641;p=l2e.git Merge branch 'menus' * stalled capsule menu until capsule monsters are implemented * item using and spell invocation also stalled --- diff --git a/.gitignore b/.gitignore index c85cc26..d266c4e 100644 --- a/.gitignore +++ b/.gitignore @@ -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 f221067..7b8f77b 100644 --- a/l2e.cbp +++ b/l2e.cbp @@ -111,7 +111,6 @@ - @@ -187,6 +186,29 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/battle/Hero.h b/src/battle/Hero.h index d719288..5e86f88 100644 --- a/src/battle/Hero.h +++ b/src/battle/Hero.h @@ -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; } diff --git a/src/common/GameConfig.cpp b/src/common/GameConfig.cpp index b35e991..6673a32 100644 --- a/src/common/GameConfig.cpp +++ b/src/common/GameConfig.cpp @@ -12,7 +12,8 @@ namespace common { GameConfig::GameConfig() : state(0) , battleResources(0) -, heroesLayout(0) { +, heroesLayout(0) +, menuResources(0) { } diff --git a/src/common/GameConfig.h b/src/common/GameConfig.h index 16b0142..c13b4ab 100644 --- a/src/common/GameConfig.h +++ b/src/common/GameConfig.h @@ -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; + }; } diff --git a/src/common/GameState.cpp b/src/common/GameState.cpp index 7992d43..480c305 100644 --- a/src/common/GameState.cpp +++ b/src/common/GameState.cpp @@ -10,7 +10,13 @@ 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; diff --git a/src/common/GameState.h b/src/common/GameState.h index fca50c1..1cd5428 100644 --- a/src/common/GameState.h +++ b/src/common/GameState.h @@ -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; }; diff --git a/src/common/Hero.cpp b/src/common/Hero.cpp index 02c4dcc..7150af0 100644 --- a/src/common/Hero.cpp +++ b/src/common/Hero.cpp @@ -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 + 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")); diff --git a/src/common/Hero.h b/src/common/Hero.h index ca4651c..00924d4 100644 --- a/src/common/Hero.h +++ b/src/common/Hero.h @@ -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 &Spells() { return spells; } const std::vector &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 spells; diff --git a/src/common/HeroGroup.h b/src/common/HeroGroup.h deleted file mode 100644 index ef6fd25..0000000 --- a/src/common/HeroGroup.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * HeroGroup.h - * - * Created on: Aug 10, 2012 - * Author: holy - */ - -#ifndef COMMON_HEROGROUP_H_ -#define COMMON_HEROGROUP_H_ - -#include - -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_ */ diff --git a/src/common/Inventory.cpp b/src/common/Inventory.cpp index d118f21..275de53 100644 --- a/src/common/Inventory.cpp +++ b/src/common/Inventory.cpp @@ -7,9 +7,15 @@ #include "Inventory.h" +#include "Item.h" + +#include + + 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]); +} + } diff --git a/src/common/Inventory.h b/src/common/Inventory.h index f60b9ab..e7c5696 100644 --- a/src/common/Inventory.h +++ b/src/common/Inventory.h @@ -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; }; diff --git a/src/common/Item.cpp b/src/common/Item.cpp index 2bd337d..955f48c 100644 --- a/src/common/Item.cpp +++ b/src/common/Item.cpp @@ -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; +} + } diff --git a/src/common/Item.h b/src/common/Item.h index 2792355..df361fe 100644 --- a/src/common/Item.h +++ b/src/common/Item.h @@ -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; diff --git a/src/common/Spell.cpp b/src/common/Spell.cpp index 4fce22f..1b4142b 100644 --- a/src/common/Spell.cpp +++ b/src/common/Spell.cpp @@ -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")); } diff --git a/src/common/Spell.h b/src/common/Spell.h index 0f864b8..f300197 100644 --- a/src/common/Spell.h +++ b/src/common/Spell.h @@ -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; diff --git a/src/common/fwd.h b/src/common/fwd.h index 1cd0671..3dc6438 100644 --- a/src/common/fwd.h +++ b/src/common/fwd.h @@ -13,7 +13,6 @@ namespace common { struct GameConfig; struct GameState; class Hero; -class HeroGroup; class Ikari; class Inventory; class Item; diff --git a/src/geometry/Vector.h b/src/geometry/Vector.h index ebff4b3..c0b22e5 100644 --- a/src/geometry/Vector.h +++ b/src/geometry/Vector.h @@ -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 FromIndex(Scalar index, Scalar lineLength) { return Vector(index % lineLength, index / lineLength); diff --git a/src/graphics/Font.cpp b/src/graphics/Font.cpp index be96a2c..42772b2 100644 --- a/src/graphics/Font.cpp +++ b/src/graphics/Font.cpp @@ -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 &position) const { if (!sprite) return; @@ -29,16 +58,39 @@ void Font::DrawChar(char c, SDL_Surface *dest, const Vector &position) cons sprite->Draw(dest, position, col, row); } -void Font::DrawString(const char *s, SDL_Surface *dest, const Vector &positionIn, int maxChars) const { +void Font::DrawString(const char *s, SDL_Surface *dest, const Vector &positionIn, int maxWidth) const { if (!sprite) return; Vector position(positionIn); + Vector lineHead(positionIn); Vector step(CharWidth(), 0); - for (int i(0); s[i] && (maxChars <= 0 || i < maxChars); ++i, position += step) { - DrawChar(s[i], dest, position); + Vector 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 &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 position(positionIn.X() - length * CharWidth(), positionIn.Y()); + + DrawString(s, dest, position, length); +} + void Font::DrawDigit(int digit, SDL_Surface *dest, const Vector &position) const { if (!sprite) return; @@ -76,6 +128,14 @@ void Font::DrawNumber(int numberIn, SDL_Surface *dest, const Vector &positi } } +void Font::DrawNumberRight(int number, SDL_Surface *dest, const Vector &positionIn, int digits) const { + if (!sprite) return; + + Vector position(positionIn.X() - digits * CharWidth(), positionIn.Y()); + + DrawNumber(number, dest, position, digits); +} + void Font::CreateTypeDescription() { Font f; diff --git a/src/graphics/Font.h b/src/graphics/Font.h index 07cd35f..a5d7b12 100644 --- a/src/graphics/Font.h +++ b/src/graphics/Font.h @@ -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 &position) const; - void DrawString(const char *s, SDL_Surface *dest, const geometry::Vector &position, int maxChars = 0) const; + void DrawString(const char *s, SDL_Surface *dest, const geometry::Vector &position, int maxWidth = 0) const; + void DrawStringRight(const char *s, SDL_Surface *dest, const geometry::Vector &position, int maxWidth = 0) const; void DrawDigit(int d, SDL_Surface *dest, const geometry::Vector &position) const; void DrawNumber(int n, SDL_Surface *dest, const geometry::Vector &position, int digits = 0) const; + void DrawNumberRight(int n, SDL_Surface *dest, const geometry::Vector &position, int digits = 0) const; public: void SetSprite(const Sprite *s) { sprite = s; } diff --git a/src/graphics/Frame.cpp b/src/graphics/Frame.cpp index 46fa1b5..86b9cd2 100644 --- a/src/graphics/Frame.cpp +++ b/src/graphics/Frame.cpp @@ -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 &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(RepeatWidth(), BorderHeight()), Vector(offset.X() + BorderWidth(), offset.Y())) + .Render(dest, Vector(position.X() + BorderWidth(), position.Y()), Vector(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(BorderWidth(), RepeatHeight()), Vector(offset.X(), offset.Y() + BorderHeight())) + .Render(dest, Vector(position.X(), position.Y() + BorderHeight()), Vector(position.X() + BorderWidth(), position.Y() + height - BorderHeight())); + + // center fill + Texture(surface, RepeatSize(), Vector(offset.X() + BorderWidth(), offset.Y() + BorderHeight())) + .Render(dest, position + BorderSize(), position + Vector(width, height) - BorderSize()); + + // right border + Texture(surface, Vector(BorderWidth(), RepeatHeight()), Vector(offset.X() + BorderWidth() + RepeatWidth(), offset.Y() + BorderHeight())) + .Render(dest, Vector(position.X() + width - BorderWidth(), position.Y() + BorderHeight()), Vector(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 &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(RepeatWidth(), BorderHeight()), Vector(offset.X() + BorderWidth(), offset.Y() + BorderHeight() + RepeatHeight())) + .Render(dest, Vector(position.X() + BorderWidth(), position.Y() + height - BorderHeight()), Vector(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); } diff --git a/src/graphics/Menu.h b/src/graphics/Menu.h index 4dc6032..179c457 100644 --- a/src/graphics/Menu.h +++ b/src/graphics/Menu.h @@ -13,6 +13,7 @@ #include "Sprite.h" #include "../geometry/Vector.h" +#include #include #include @@ -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 &position) const; @@ -110,7 +130,15 @@ private: }; std::vector entries; int selected; + int secondarySelection; int topRow; + enum State { + STATE_INACTIVE, + STATE_ACTIVE, + STATE_SELECTED, + STATE_DUAL, + }; + State state; }; @@ -119,7 +147,9 @@ template Menu::Menu() : MenuProperties() , selected(0) -, topRow(0) { +, secondarySelection(0) +, topRow(0) +, state(STATE_ACTIVE) { } @@ -127,22 +157,9 @@ template Menu::Menu(const MenuProperties &p) : MenuProperties(p) , selected(0) -, topRow(0) { - -} - -template -Menu::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::Height() const { template void Menu::NextItem() { - SelectIndex(selected + 1); + int index(selected + 1); + if (wrapX && index % cols == 0) { + index -= cols; + } + SelectIndex(index); } template void Menu::PreviousItem() { - SelectIndex(selected - 1); + int index(selected - 1); + if (wrapX && selected % cols == 0) { + index += cols; + } + SelectIndex(index); } template void Menu::NextRow() { - SelectIndex(selected + cols); + int index(selected + cols); + if (wrapY && index >= int(entries.size())) { + index -= entries.size(); + } + SelectIndex(index); } template void Menu::PreviousRow() { - SelectIndex(selected - cols); + int index(selected - cols); + if (wrapY && index < 0) { + index += entries.size(); + } + SelectIndex(index); } template @@ -214,6 +247,13 @@ void Menu::Draw(SDL_Surface *dest, const geometry::Vector &position) con geometry::Vector 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(font->CharWidth(), 0); + } + if (entries[start + i].icon) { entries[start + i].icon->Draw(dest, position + iconOffset); } @@ -234,13 +274,37 @@ void Menu::Draw(SDL_Surface *dest, const geometry::Vector &position) con if (charsPerNumber) { usedFont->DrawChar(delimiter, dest, position + textOffset); textOffset += geometry::Vector(usedFont->CharWidth(), 0); - usedFont->DrawNumber(entries[start + i].number, dest, position + textOffset); + usedFont->DrawNumber(entries[start + i].number, dest, position + textOffset, charsPerNumber); } } geometry::Vector 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(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(selectedCursor->Width(), 0)); + if (secondarySelection >= start && secondarySelection <= end) { + geometry::Vector 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 index 0000000..2bb0e97 --- /dev/null +++ b/src/graphics/Texture.cpp @@ -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 &size, + const Vector &offset) +: surface(surface) +, size(size) +, offset(offset) { + +} + +Texture::~Texture() { + +} + + +void Texture::Render(SDL_Surface *dest, const Vector &from, const Vector &to) const { + SDL_Rect destRect; + destRect.x = from.X(); + destRect.y = from.Y(); + if (!surface || size == Vector()) { + 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 index 0000000..ecb516a --- /dev/null +++ b/src/graphics/Texture.h @@ -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 + +namespace graphics { + +class Texture { + +public: + explicit Texture( + SDL_Surface *surface = 0, + const geometry::Vector &size = geometry::Vector(), + const geometry::Vector &offset = geometry::Vector()); + ~Texture(); + +public: + void Render(SDL_Surface *dest, const geometry::Vector &from, const geometry::Vector &to) const; + +public: + void SetSurface(SDL_Surface *s) { surface = s; } + void SetSize(const geometry::Vector &s) { size = s; } + void SetOffset(const geometry::Vector &o) { offset = o; } + +private: + SDL_Surface *surface; + geometry::Vector size; + geometry::Vector offset; + +}; + +} + +#endif /* GRAPHICS_TEXTURE_H_ */ diff --git a/src/graphics/fwd.h b/src/graphics/fwd.h index 31dbf27..961890b 100644 --- a/src/graphics/fwd.h +++ b/src/graphics/fwd.h @@ -24,6 +24,7 @@ class Menu; struct MenuProperties; class SimpleAnimation; class Sprite; +class Texture; } diff --git a/src/main.cpp b/src/main.cpp index f70103a..f310399 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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(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(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); diff --git a/src/map/MapState.cpp b/src/map/MapState.cpp index 5ec9507..af15630 100644 --- a/src/map/MapState.cpp +++ b/src/map/MapState.cpp @@ -17,6 +17,7 @@ #include "../common/GameConfig.h" #include "../common/GameState.h" #include "../graphics/ColorFade.h" +#include "../menu/PartyMenu.h" #include @@ -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 index 0000000..f1d871d --- /dev/null +++ b/src/menu/ChangeHero.cpp @@ -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 + +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 offset((screen->w - Width()) / 2, (screen->h - Height()) / 2); + + parent->RenderBackground(screen); + RenderHighlight(screen, offset); + parent->RenderHeros(screen, offset); + parent->RenderMenu(screen, offset + Vector(8 * parent->Res().normalFont->CharWidth(), 13 * parent->Res().normalFont->CharHeight() + parent->Res().normalFont->CharHeight() / 8)); + parent->RenderInfo(screen, offset + Vector(14 * parent->Res().normalFont->CharWidth(), 21 * parent->Res().normalFont->CharHeight() + parent->Res().normalFont->CharHeight() / 8)); +} + +void ChangeHero::RenderHighlight(SDL_Surface *screen, const Vector &offset) const { + if (selection < 0) return; + Vector statusOffset(parent->StatusOffset(selection)); + statusOffset -= Vector(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(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 index 0000000..6bb5f31 --- /dev/null +++ b/src/menu/ChangeHero.h @@ -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 + +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 &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 index 0000000..b5a2b7d --- /dev/null +++ b/src/menu/ConfigMenu.cpp @@ -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 offset((screen->w - Width()) / 2, (screen->h - Height()) / 2); + const Vector headlineOffset( + font.CharWidth(), 2 * font.CharHeight() - font.CharHeight() / 8); + const Vector 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 &offset) const { + const Font &font(*parent->Res().normalFont); + const Frame &frame(*parent->Res().statusFrame); + const Vector 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 &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 menuOffset( + 3 * font.CharWidth(), 2 * font.CharHeight()); + + frame.Draw(screen, offset, 30 * font.CharWidth(), 14 * font.CharHeight()); + configMenu.Draw(screen, offset + menuOffset); + + Vector lineOffset( + menuOffset.X() + configMenu.Width() + 2 * font.CharWidth(), + menuOffset.Y()); + Vector 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 index 0000000..8bb81f4 --- /dev/null +++ b/src/menu/ConfigMenu.h @@ -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 &offset) const; + void RenderMenu(SDL_Surface *screen, const geometry::Vector &offset) const; + +private: + PartyMenu *parent; + graphics::Menu configMenu; + +}; + +} + +#endif /* MENU_CONFIGMENU_H_ */ diff --git a/src/menu/EquipMenu.cpp b/src/menu/EquipMenu.cpp new file mode 100644 index 0000000..b917c9a --- /dev/null +++ b/src/menu/EquipMenu.cpp @@ -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 offset((screen->w - Width()) / 2, (screen->h - Height()) / 2); + Vector shoulderNavOffset( + 5 * parent->Res().statusFont->CharWidth(), + parent->Res().statusFont->CharHeight()); + Vector statsOffset( + 4 * parent->Res().statusFont->CharWidth(), + 8 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8); + Vector 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 inventoryOffset( + parent->Res().statusFont->CharWidth(), + 17 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8); + RenderInventoryMenu(screen, offset + inventoryOffset); + } else { + Vector 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 &offset) const { + parent->GetHeroStatus(cursor).Render(screen, offset); +} + +void EquipMenu::RenderStats(SDL_Surface *screen, const Vector &offset) const { + const Stats &stats(GetHero().GetStats()); + Vector lineBreak(0, parent->Res().statusFont->CharHeight()); + + Vector 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 &position) const { + const Font &font(*parent->Res().statusFont); + const Vector 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 &offset) const { + equipmentMenu.Draw(screen, offset); +} + +void EquipMenu::RenderActionMenu(SDL_Surface *screen, const Vector &offset) const { + const Font &font(*parent->Res().statusFont); + const Frame &frame(*parent->Res().statusFrame); + const Vector 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 &offset) const { + const Font &font(*parent->Res().normalFont); + const Frame &frame(*parent->Res().statusFrame); + const Vector 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 index 0000000..f8e9e78 --- /dev/null +++ b/src/menu/EquipMenu.h @@ -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 &offset) const; + void RenderStats(SDL_Surface *screen, const geometry::Vector &offset) const; + void RenderStatsLine(const char *label, int number, SDL_Surface *screen, const geometry::Vector &position) const; + void RenderEquipmentMenu(SDL_Surface *screen, const geometry::Vector &offset) const; + void RenderActionMenu(SDL_Surface *screen, const geometry::Vector &offset) const; + void RenderInventoryMenu(SDL_Surface *screen, const geometry::Vector &offset) const; + +private: + PartyMenu *parent; + int cursor; + enum Choice { + CHOICE_EQUIP, + CHOICE_STRONGEST, + CHOICE_REMOVE, + CHOICE_REMOVE_ALL, + CHOICE_DROP, + }; + graphics::Menu actionMenu; + graphics::Menu equipmentMenu; + graphics::Menu inventoryMenu; + +}; + +} + +#endif /* MENU_EQUIPMENU_H_ */ diff --git a/src/menu/HeroStatus.cpp b/src/menu/HeroStatus.cpp new file mode 100644 index 0000000..5bc0c3b --- /dev/null +++ b/src/menu/HeroStatus.cpp @@ -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 &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 nameOffset( + party[hero]->BattleSprite()->Width(), + res->statusFont->CharHeight() * 7 / 8); + nameOffset += offset; + res->statusFont->DrawString(party[hero]->Name(), screen, nameOffset, 5); + + Vector levelLabelOffset(nameOffset.X() + 6 * res->statusFont->CharWidth(), nameOffset.Y()); + res->statusLabels->Draw(screen, levelLabelOffset, 0, 0); + + Vector levelOffset(levelLabelOffset.X() + 2 * res->statusFont->CharWidth(), levelLabelOffset.Y()); + res->statusFont->DrawNumber(party[hero]->Level(), screen, levelOffset, 2); + + Vector healthLabelOffset(nameOffset.X(), nameOffset.Y() + res->statusFont->CharHeight()); + res->statusLabels->Draw(screen, healthLabelOffset, 0, 1); + + Vector healthOffset(nameOffset.X() + 3 * res->statusFont->CharWidth(), nameOffset.Y() + res->statusFont->CharHeight()); + res->statusFont->DrawNumber(party[hero]->Health(), screen, healthOffset, 3); + + Vector healthSeparatorOffset(healthOffset.X() + 3 * res->statusFont->CharWidth(), healthOffset.Y()); + res->statusFont->DrawChar('/', screen, healthSeparatorOffset); + + Vector maxHealthOffset(healthSeparatorOffset.X() + res->statusFont->CharWidth(), healthOffset.Y()); + res->statusFont->DrawNumber(party[hero]->MaxHealth(), screen, maxHealthOffset, 3); + + Vector manaLabelOffset(healthLabelOffset.X(), healthLabelOffset.Y() + res->statusFont->CharHeight()); + res->statusLabels->Draw(screen, manaLabelOffset, 0, 2); + + Vector manaOffset(healthOffset.X(), healthOffset.Y() + res->statusFont->CharHeight()); + res->statusFont->DrawNumber(party[hero]->Mana(), screen, manaOffset, 3); + + Vector manaSeparatorOffset(healthSeparatorOffset.X(), manaOffset.Y()); + res->statusFont->DrawChar('/', screen, manaSeparatorOffset); + + Vector 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 index 0000000..27c048e --- /dev/null +++ b/src/menu/HeroStatus.h @@ -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 + +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 Size() const { return geometry::Vector(Width(), Height()); } + + void Render(SDL_Surface *screen, const geometry::Vector &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 index 0000000..437cd9c --- /dev/null +++ b/src/menu/InventoryMenu.cpp @@ -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 offset((screen->w - Width()) / 2, (screen->h - Height()) / 2); + Vector menuOffset(font.CharWidth(), 13 * font.CharHeight() + font.CharHeight() / 8); + Vector 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 &offset) const { + const Font &font(*parent->Res().normalFont); + const Frame &frame(*parent->Res().statusFrame); + + const Vector labelOffset(2 * font.CharWidth(), font.CharHeight()); + const Vector menuFrameOffset(offset.X() + 8 * font.CharWidth(), offset.Y()); + const Vector 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 &offset) const { + const Font &font(*parent->Res().normalFont); + const Frame &frame(*parent->Res().statusFrame); + const Vector 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 index 0000000..c1d458b --- /dev/null +++ b/src/menu/InventoryMenu.h @@ -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 &offset) const; + void RenderInventory(SDL_Surface *screen, const geometry::Vector &offset) const; + +private: + PartyMenu *parent; + enum Choice { + CHOICE_USE, + CHOICE_SORT, + CHOICE_DROP, + }; + graphics::Menu menu; + graphics::Menu itemMenu; + +}; + +} + +#endif /* MENU_INVENTORYMENU_H_ */ diff --git a/src/menu/PartyMenu.cpp b/src/menu/PartyMenu.cpp new file mode 100644 index 0000000..619d7e1 --- /dev/null +++ b/src/menu/PartyMenu.cpp @@ -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(0, 0); + statusPositions[1] = Vector(status[0].Width(), 0); + statusPositions[2] = Vector(0, status[0].Height()); + statusPositions[3] = Vector(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 offset((screen->w - Width()) / 2, (screen->h - Height()) / 2); + + RenderBackground(screen); + RenderHeros(screen, offset); + RenderMenu(screen, offset + Vector(8 * Res().normalFont->CharWidth(), 13 * Res().normalFont->CharHeight() + Res().normalFont->CharHeight() / 8)); + RenderInfo(screen, offset + Vector(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(), Vector(screen->w, screen->h)); +} + +void PartyMenu::RenderHeros(SDL_Surface *screen, const Vector &offset) const { + for (int i(0); i < 4; ++i) { + status[i].Render(screen, offset + StatusOffset(i)); + } +} + +Vector PartyMenu::StatusOffset(int index) const { + return statusPositions[index] + Vector(Res().normalFont->CharWidth(), 2 * Res().normalFont->CharHeight()); +} + +void PartyMenu::RenderMenu(SDL_Surface *screen, const Vector &offset) const { + Vector 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 &offset) const { + Res().statusFrame->Draw(screen, offset, 17 * Res().normalFont->CharWidth(), 5 * Res().normalFont->CharHeight()); + + Vector timeLabelOffset(2 * Res().normalFont->CharWidth(), Res().normalFont->CharHeight() + Res().normalFont->CharHeight() / 4); + Res().normalFont->DrawString(Res().mainMenuTimeText, screen, offset + timeLabelOffset); + + Vector hoursOffset(timeLabelOffset.X() + 6 * Res().normalFont->CharWidth(), timeLabelOffset.Y()); + Res().normalFont->DrawNumber(game->state->time / 60 / 60, screen, offset + hoursOffset, 4); + + Vector timeSeparatorOffset(hoursOffset.X() + 4 * Res().normalFont->CharWidth(), hoursOffset.Y()); + Res().normalFont->DrawChar(':', screen, offset + timeSeparatorOffset); + + Vector 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 goldLabelOffset(2 * Res().normalFont->CharWidth(), 2 * Res().normalFont->CharHeight() + Res().normalFont->CharHeight() * 3 / 4); + Res().normalFont->DrawString(Res().mainMenuGoldText, screen, offset + goldLabelOffset); + + Vector 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(ref)); + self->Ctrl().ChangeState( + new EquipMenu(self, index)); +} + +void PartyMenu::OnSpellSelect(void *ref, int index) { + PartyMenu *self(reinterpret_cast(ref)); + self->Ctrl().ChangeState( + new SpellMenu(self, index)); +} + +void PartyMenu::OnStatusSelect(void *ref, int index) { + PartyMenu *self(reinterpret_cast(ref)); + self->Ctrl().ChangeState( + new StatusMenu(self, index)); +} + +} diff --git a/src/menu/PartyMenu.h b/src/menu/PartyMenu.h new file mode 100644 index 0000000..f915d30 --- /dev/null +++ b/src/menu/PartyMenu.h @@ -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 &offset) const; + void RenderMenu(SDL_Surface *screen, const geometry::Vector &offset) const; + void RenderInfo(SDL_Surface *screen, const geometry::Vector &offset) const; + + geometry::Vector 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 statusPositions[4]; + common::GameConfig *game; + graphics::Menu mainMenu; + +}; + +} + +#endif /* MENU_PARTYMENU_H_ */ diff --git a/src/menu/Resources.cpp b/src/menu/Resources.cpp new file mode 100644 index 0000000..38cf6f8 --- /dev/null +++ b/src/menu/Resources.cpp @@ -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 index 0000000..62b825d --- /dev/null +++ b/src/menu/Resources.h @@ -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 index 0000000..a2aca87 --- /dev/null +++ b/src/menu/ScenarioMenu.cpp @@ -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 offset((screen->w - Width()) / 2, (screen->h - Height()) / 2); + Vector headlineOffset(font.CharWidth(), 13 * font.CharHeight() + font.CharHeight() / 8); + Vector 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 &offset) const { + const Font &font(*parent->Res().normalFont); + const Frame &frame(*parent->Res().statusFrame); + const Vector 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 &offset) const { + const Font &font(*parent->Res().normalFont); + const Frame &frame(*parent->Res().statusFrame); + const Vector 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 index 0000000..bcc2bbc --- /dev/null +++ b/src/menu/ScenarioMenu.h @@ -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 &offset) const; + void RenderItems(SDL_Surface *screen, const geometry::Vector &offset) const; + +private: + PartyMenu *parent; + graphics::Menu itemMenu; + +}; + +} + +#endif /* MENU_SCENARIOMENU_H_ */ diff --git a/src/menu/SelectHero.cpp b/src/menu/SelectHero.cpp new file mode 100644 index 0000000..12690db --- /dev/null +++ b/src/menu/SelectHero.cpp @@ -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 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 index 0000000..5e67152 --- /dev/null +++ b/src/menu/SelectHero.h @@ -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 + +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 cursorBlink; + int cursor; + +}; + +} + +#endif /* MENU_SELECTHERO_H_ */ diff --git a/src/menu/SpellMenu.cpp b/src/menu/SpellMenu.cpp new file mode 100644 index 0000000..57989ed --- /dev/null +++ b/src/menu/SpellMenu.cpp @@ -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 +#include +#include + +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_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 offset((screen->w - Width()) / 2, (screen->h - Height()) / 2); + Vector menuOffset(font.CharWidth(), 13 * font.CharHeight() + font.CharHeight() / 8); + Vector 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 &offset) const { + if (cursor < 0) return; + Vector statusOffset(parent->StatusOffset(cursor)); + statusOffset -= Vector(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 &offset) const { + const Font &font(*parent->Res().normalFont); + const Frame &frame(*parent->Res().statusFrame); + + const Vector labelOffset(2 * font.CharWidth(), font.CharHeight()); + const Vector menuFrameOffset(offset.X() + 9 * font.CharWidth(), offset.Y()); + const Vector 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 &offset) const { + const Font &font(*parent->Res().normalFont); + const Frame &frame(*parent->Res().statusFrame); + const Vector 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 index 0000000..d289794 --- /dev/null +++ b/src/menu/SpellMenu.h @@ -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 &offset) const; + void RenderMenu(SDL_Surface *screen, const geometry::Vector &offset) const; + void RenderSpells(SDL_Surface *screen, const geometry::Vector &offset) const; + +private: + PartyMenu *parent; + SDL_Surface *highlight; + int cursor; + enum Choice { + CHOICE_USE, + CHOICE_SORT, + }; + graphics::Menu actionMenu; + graphics::Menu spellMenu; + +}; + +} + +#endif /* MENU_SPELLMENU_H_ */ diff --git a/src/menu/StatusMenu.cpp b/src/menu/StatusMenu.cpp new file mode 100644 index 0000000..be67743 --- /dev/null +++ b/src/menu/StatusMenu.cpp @@ -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 offset((screen->w - Width()) / 2, (screen->h - Height()) / 2); + Vector shoulderNavOffset( + 5 * parent->Res().statusFont->CharWidth(), + parent->Res().statusFont->CharHeight()); + Vector statsOffset( + 4 * parent->Res().statusFont->CharWidth(), + 8 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8); + Vector equipOffset( + 17 * parent->Res().statusFont->CharWidth(), + 4 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8); + Vector experienceOffset( + 11 * parent->Res().statusFont->CharWidth(), + 17 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8); + Vector nextLevelOffset( + 11 * parent->Res().statusFont->CharWidth(), + 20 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8); + Vector ikariOffset( + 17 * parent->Res().statusFont->CharWidth(), + 17 * parent->Res().statusFont->CharHeight() - parent->Res().statusFont->CharHeight() / 8); + Vector 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 &offset) const { + parent->GetHeroStatus(cursor).Render(screen, offset); +} + +void StatusMenu::RenderStats(SDL_Surface *screen, const Vector &offset) const { + const Stats &stats(GetHero().GetStats()); + Vector lineBreak(0, parent->Res().statusFont->CharHeight()); + + Vector 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 &position) const { + const Font &font(*parent->Res().statusFont); + const Vector 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 &offset) const { + const Hero &hero(GetHero()); + Vector lineBreak(0, 2 * parent->Res().statusFont->CharHeight()); + + Vector 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 &position) const { + const Font &font(*parent->Res().statusFont); + const Vector 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 &offset) const { + const Font &font(*parent->Res().statusFont); + font.DrawStringRight(parent->Res().experienceLabel, screen, offset, 10); + + Vector numberOffset(offset.X(), offset.Y() + font.CharHeight()); + font.DrawNumberRight(GetHero().Experience(), screen, numberOffset, 7); +} + +void StatusMenu::RenderNextLevel(SDL_Surface *screen, const geometry::Vector &offset) const { + const Font &font(*parent->Res().statusFont); + font.DrawStringRight(parent->Res().nextLevelLabel, screen, offset, 10); + + Vector numberOffset(offset.X(), offset.Y() + font.CharHeight()); + font.DrawNumberRight(GetHero().NextLevel(), screen, numberOffset, 7); +} + +void StatusMenu::RenderIkari(SDL_Surface *screen, const geometry::Vector &offset) const { + const Font &font(*parent->Res().statusFont); + font.DrawString(parent->Res().ipLabel, screen, offset, 5); + + Vector numberOffset(offset.X() + 5 * font.CharWidth(), offset.Y()); + font.DrawNumber(GetHero().RelativeIP(100), screen, numberOffset, 3); + + Vector percentOffset(offset.X() + 8 * font.CharWidth(), offset.Y()); + font.DrawChar('%', screen, percentOffset); +} + +void StatusMenu::RenderMenu(SDL_Surface *screen, const geometry::Vector &offset) const { + const Font &font(*parent->Res().normalFont); + const Frame &frame(*parent->Res().statusFrame); + + Vector 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 menuFrameOffset(10 * font.CharWidth(), 0); + Vector 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 index 0000000..5f6f986 --- /dev/null +++ b/src/menu/StatusMenu.h @@ -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 &offset) const; + void RenderStats(SDL_Surface *screen, const geometry::Vector &offset) const; + void RenderStatsLine(const char *label, int number, SDL_Surface *screen, const geometry::Vector &position) const; + void RenderEquipment(SDL_Surface *screen, const geometry::Vector &offset) const; + void RenderEquipmentLine(const common::Item *, SDL_Surface *screen, const geometry::Vector &position) const; + /// @param offset the top right corner! + void RenderExperience(SDL_Surface *screen, const geometry::Vector &offset) const; + /// @param offset the top right corner! + void RenderNextLevel(SDL_Surface *screen, const geometry::Vector &offset) const; + void RenderIkari(SDL_Surface *screen, const geometry::Vector &offset) const; + void RenderMenu(SDL_Surface *screen, const geometry::Vector &offset) const; + +private: + PartyMenu *parent; + int cursor; + graphics::Menu menu; + +}; + +} + +#endif /* MENU_STATUSMENU_H_ */ diff --git a/src/menu/fwd.h b/src/menu/fwd.h new file mode 100644 index 0000000..b4b96e5 --- /dev/null +++ b/src/menu/fwd.h @@ -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_ */ diff --git a/test-data/constants.l2h b/test-data/constants.l2h index 5ace629..fea7abf 100644 --- a/test-data/constants.l2h +++ b/test-data/constants.l2h @@ -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 diff --git a/test-data/constants.l2s b/test-data/constants.l2s index fa01f2b..47dcb9a 100644 --- a/test-data/constants.l2s +++ b/test-data/constants.l2s @@ -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 index 0000000..0496815 Binary files /dev/null and b/test-data/hero-cursor.png differ diff --git a/test-data/items.l2s b/test-data/items.l2s index 4b49dd6..231aa3f 100644 --- a/test-data/items.l2s +++ b/test-data/items.l2s @@ -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 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 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 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 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 index 0000000..1b42927 Binary files /dev/null and b/test-data/menubg.png differ diff --git a/test-data/normal-font.png b/test-data/normal-font.png index 8a1532b..0c49e2c 100644 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 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 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 index 0000000..93fcebc Binary files /dev/null and b/test-data/status-labels.png differ diff --git a/test-data/test.l2s b/test-data/test.l2s index 4a4bb05..5f4fe2c 100644 --- a/test-data/test.l2s +++ b/test-data/test.l2s @@ -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,