<Option object_output="obj\W32_Debug\" />
<Option type="1" />
<Option compiler="gcc" />
- <Option parameters="test-data/constants.l2s test-data/ikaris.l2s test-data/items.l2s test-data/maps.l2s test-data/spells.l2s test-data/test.l2s" />
+ <Option parameters="test-data/capsules.l2s test-data/constants.l2s test-data/ikaris.l2s test-data/items.l2s test-data/maps.l2s test-data/spells.l2s test-data/test.l2s" />
<Option projectLinkerOptionsRelation="2" />
<Compiler>
<Add option="-g" />
<Option object_output="obj\W32\" />
<Option type="1" />
<Option compiler="gcc" />
- <Option parameters="test-data/constants.l2s test-data/ikaris.l2s test-data/items.l2s test-data/maps.l2s test-data/spells.l2s test-data/test.l2s" />
+ <Option parameters="test-data/capsules.l2s test-data/constants.l2s test-data/ikaris.l2s test-data/items.l2s test-data/maps.l2s test-data/spells.l2s test-data/test.l2s" />
<Option projectLinkerOptionsRelation="2" />
<Compiler>
<Add option="-O3" />
<Unit filename="src\battle\AttackTypeMenu.h" />
<Unit filename="src\battle\BattleState.cpp" />
<Unit filename="src\battle\BattleState.h" />
+ <Unit filename="src\battle\Capsule.cpp" />
+ <Unit filename="src\battle\Capsule.h" />
<Unit filename="src\battle\Hero.cpp" />
<Unit filename="src\battle\Hero.h" />
<Unit filename="src\battle\HeroTag.cpp" />
<Unit filename="src\battle\states\SelectTarget.h" />
<Unit filename="src\battle\states\SwapHeroes.cpp" />
<Unit filename="src\battle\states\SwapHeroes.h" />
+ <Unit filename="src\common\Capsule.cpp" />
+ <Unit filename="src\common\Capsule.h" />
<Unit filename="src\common\GameConfig.cpp" />
<Unit filename="src\common\GameConfig.h" />
<Unit filename="src\common\GameState.cpp" />
<Unit filename="src\map\Trigger.cpp" />
<Unit filename="src\map\Trigger.h" />
<Unit filename="src\map\fwd.h" />
+ <Unit filename="src\menu\CapsuleChangeMenu.cpp" />
+ <Unit filename="src\menu\CapsuleChangeMenu.h" />
+ <Unit filename="src\menu\CapsuleFeedMenu.cpp" />
+ <Unit filename="src\menu\CapsuleFeedMenu.h" />
+ <Unit filename="src\menu\CapsuleNameMenu.cpp" />
+ <Unit filename="src\menu\CapsuleNameMenu.h" />
+ <Unit filename="src\menu\CapsuleMenu.cpp" />
+ <Unit filename="src\menu\CapsuleMenu.h" />
<Unit filename="src\menu\ChangeHero.cpp" />
<Unit filename="src\menu\ChangeHero.h" />
<Unit filename="src\menu\ConfigMenu.cpp" />
#ifndef BATTLE_ATTACKCHOICE_H_
#define BATTLE_ATTACKCHOICE_H_
+namespace common {
+ class Item;
+ class Spell;
+}
+
#include "TargetSelection.h"
-#include "../common/fwd.h"
namespace battle {
++numHeroes;
}
+void BattleState::SetCapsule(const Capsule &c) {
+ capsule = c;
+}
+
void BattleState::NextHero() {
++activeHero;
while (activeHero < numHeroes && heroes[activeHero].Health() == 0) {
smallHeroTags[i] = SmallHeroTag(this, i);
}
+ capsule.Position() = heroesLayout->CalculatePosition(4, background->w, background->h);
+
for (int i(0); i < int(monsters.size()); ++i) {
monsters[i].Position() = monstersLayout->CalculatePosition(i, background->w, background->h);
}
public:
OrderCompare(BattleState *battle) : battle(battle) { }
bool operator ()(const BattleState::Order &lhs, const BattleState::Order &rhs) {
- int lagl(lhs.isMonster ? battle->MonsterAt(lhs.index).GetStats().Agility() : battle->HeroAt(lhs.index).GetStats().Agility());
- int ragl(rhs.isMonster ? battle->MonsterAt(rhs.index).GetStats().Agility() : battle->HeroAt(rhs.index).GetStats().Agility());
- return lagl > ragl;
+ return lhs.GetStats(*battle).Agility() > rhs.GetStats(*battle).Agility();
}
private:
BattleState *battle;
void BattleState::CalculateAttackOrder() {
attackOrder.reserve(monsters.size() + NumHeroes());
for (int i(0); i < NumHeroes(); ++i) {
- attackOrder.push_back(Order(i, false));
+ attackOrder.push_back(Order(Order::HERO, i));
}
for (vector<Monster>::size_type i(0), end(monsters.size()); i < end; ++i) {
- attackOrder.push_back(Order(i, true));
+ attackOrder.push_back(Order(Order::MONSTER, i));
MonsterAt(i).GetAttackChoice() = AttackChoice(this);
}
+ if (capsule.Active() && capsule.Health() > 0) {
+ attackOrder.push_back(Order(Order::CAPSULE));
+ }
std::sort(attackOrder.begin(), attackOrder.end(), OrderCompare(this));
}
}
++attackCursor;
while (attackCursor < int(attackOrder.size())) {
- if (attackOrder[attackCursor].isMonster) {
+ if (attackOrder[attackCursor].IsMonster()) {
if (MonsterAt(attackOrder[attackCursor].index).Health() > 0) break;
- } else {
+ } else if (attackOrder[attackCursor].IsHero()) {
if (HeroAt(attackOrder[attackCursor].index).Health() > 0) break;
+ } else {
+ if (capsule.Active() && capsule.Health() > 0) break;
}
++attackCursor;
}
}
void BattleState::CalculateDamage() {
- if (CurrentAttack().isMonster) {
+ if (CurrentAttack().IsMonster()) {
DecideMonsterAttack(MonsterAt(CurrentAttack().index));
+ } else if (CurrentAttack().IsCapsule()) {
+ DecideCapsuleAttack();
}
- AttackChoice &ac(CurrentAttack().isMonster ? MonsterAt(CurrentAttack().index).GetAttackChoice() : HeroAt(CurrentAttack().index).GetAttackChoice());
+ AttackChoice &ac = CurrentAttack().GetAttackChoice(*this);
if (ac.GetType() == AttackChoice::DEFEND) return;
TargetSelection &ts(ac.Selection());
- if (CurrentAttack().isMonster) {
- const Stats &attackerStats(MonsterAt(CurrentAttack().index).GetStats());
- CalculateDamage(attackerStats, ts);
- } else {
- const Stats &attackerStats(HeroAt(CurrentAttack().index).GetStats());
- CalculateDamage(attackerStats, ts);
- }
+ const Stats &attackerStats = CurrentAttack().GetStats(*this);
+ CalculateDamage(attackerStats, ts);
}
-void BattleState::DecideMonsterAttack(Monster &m) const {
+void BattleState::DecideMonsterAttack(Monster &m) {
AttackChoice &ac(m.GetAttackChoice());
TargetSelection &ts(ac.Selection());
ac.Reset();
ts.Select(target);
}
+void BattleState::DecideCapsuleAttack() {
+ AttackChoice &ac(capsule.GetAttackChoice());
+ TargetSelection &ts(ac.Selection());
+ ac.Reset();
+ int target(rand() % monsters.size());
+ while (!MonsterPositionOccupied(target)) {
+ target = rand() % monsters.size();
+ }
+ ac.SetType(AttackChoice::SWORD);
+ ts.SelectMonsters();
+ ts.SetSingle();
+ ts.Select(target);
+}
+
+AttackChoice &BattleState::Order::GetAttackChoice(BattleState &b) const {
+ switch (by) {
+ case HERO:
+ return b.HeroAt(index).GetAttackChoice();
+ case CAPSULE:
+ return b.GetCapsule().GetAttackChoice();
+ case MONSTER:
+ return b.MonsterAt(index).GetAttackChoice();
+ default:
+ throw std::runtime_error("invalid case in BttleStats::Order::GetAttackChoice()");
+ }
+}
+
+Stats &BattleState::Order::GetStats(BattleState &b) const {
+ switch (by) {
+ case HERO:
+ return b.HeroAt(index).GetStats();
+ case CAPSULE:
+ return b.GetCapsule().GetStats();
+ case MONSTER:
+ return b.MonsterAt(index).GetStats();
+ default:
+ throw std::runtime_error("invalid case in BttleStats::Order::GetAttackChoice()");
+ }
+}
+
void BattleState::CalculateDamage(const Stats &attackerStats, TargetSelection &ts) const {
bool hitSome(false);
if (ts.TargetsMonsters()) {
void BattleState::ApplyDamage() {
if (attackCursor < 0) return;
- AttackChoice &ac(CurrentAttack().isMonster ? MonsterAt(CurrentAttack().index).GetAttackChoice() : HeroAt(CurrentAttack().index).GetAttackChoice());
+ AttackChoice &ac = CurrentAttack().GetAttackChoice(*this);
TargetSelection &ts(ac.Selection());
if (ts.TargetsMonsters()) {
for (int i(0); i < MaxMonsters(); ++i) {
}
AttackChoice &BattleState::CurrentAttackAttackChoice() {
- if (CurrentAttack().isMonster) {
- return MonsterAt(CurrentAttack().index).GetAttackChoice();
- } else {
- return HeroAt(CurrentAttack().index).GetAttackChoice();
- }
+ return CurrentAttack().GetAttackChoice(*this);
}
void BattleState::ClearAllAttacks() {
for (int i(0); i < MaxMonsters(); ++i) {
MonsterAt(i).GetAttackChoice() = AttackChoice(this);
}
+ capsule.GetAttackChoice() = AttackChoice(this);
attackOrder.clear();
}
}
}
+void BattleState::RenderCapsule(SDL_Surface *screen, const Vector<int> &offset) {
+ if (!capsule.Active() || capsule.Health() <= 0) return;
+ if (capsule.GetAnimation().Running()) {
+ capsule.GetAnimation().DrawCenter(screen, capsule.Position() + offset);
+ } else {
+ capsule.Sprite()->DrawCenter(screen, capsule.Position() + offset);
+ }
+}
+
void BattleState::RenderHeroTags(SDL_Surface *screen, const Vector<int> &offset) {
assert(screen);
int tagHeight(attackTypeMenu.Height());
#include "fwd.h"
#include "AttackTypeMenu.h"
+#include "Capsule.h"
#include "Hero.h"
#include "HeroTag.h"
#include "Monster.h"
public:
void AddMonster(const Monster &);
void AddHero(const Hero &);
+ void SetCapsule(const Capsule &);
public:
virtual void HandleEvents(const app::Input &);
const HeroTag &HeroTagAt(int index) const { assert(index >= 0 && index < NumHeroes()); return heroTags[index]; }
const geometry::Vector<int> &HeroTagPositionAt(int index) const { assert(index >= 0 && index < NumHeroes()); return heroTagPositions[index]; }
+ Capsule &GetCapsule() { return capsule; }
+
bool HasChosenAttackType() const { return ActiveHero().GetAttackChoice().GetType() != AttackChoice::UNDECIDED; }
bool AttackSelectionDone() const { return activeHero >= numHeroes; }
void SetRunaway() { ranAway = true; }
struct Order {
- Order(int index, bool isMonster)
- : index(index), isMonster(isMonster) { }
+ enum Performer {
+ HERO,
+ CAPSULE,
+ MONSTER,
+ };
+ Order(Performer by, int index = 0)
+ : index(index), by(by) { }
+ AttackChoice &GetAttackChoice(BattleState &) const;
+ common::Stats &GetStats(BattleState &) const;
+ bool IsHero() const { return by == HERO; }
+ bool IsCapsule() const { return by == CAPSULE; }
+ bool IsMonster() const { return by == MONSTER; }
int index;
- bool isMonster;
+ Performer by;
};
void CalculateAttackOrder();
void RenderBackground(SDL_Surface *screen, const geometry::Vector<int> &offset);
void RenderMonsters(SDL_Surface *screen, const geometry::Vector<int> &offset);
void RenderHeroes(SDL_Surface *screen, const geometry::Vector<int> &offset);
+ void RenderCapsule(SDL_Surface *screen, const geometry::Vector<int> &offset);
void RenderHeroTags(SDL_Surface *screen, const geometry::Vector<int> &offset);
void RenderSmallHeroTags(SDL_Surface *screen, const geometry::Vector<int> &offset);
private:
void LoadInventory();
- void DecideMonsterAttack(Monster &) const;
+ void DecideMonsterAttack(Monster &);
+ void DecideCapsuleAttack();
void CalculateDamage(const common::Stats &attackerStats, TargetSelection &targets) const;
Uint16 CalculateDamage(const common::Stats &attacker, const common::Stats &defender) const;
SmallHeroTag smallHeroTags[4];
geometry::Vector<int> heroTagPositions[4];
geometry::Vector<int> smallHeroTagPositions[4];
+ Capsule capsule;
int numHeroes;
int activeHero;
int attackCursor;
--- /dev/null
+#include "Capsule.h"
+
+#include "../common/Capsule.h"
+
+#include <cassert>
+
+using graphics::Animation;
+using graphics::Sprite;
+
+namespace battle {
+
+Capsule::Capsule(common::Capsule *master)
+: master(master)
+, health(master ? master->MaxHealth() : 0) {
+ if (master) {
+ stats = master->GetStats();
+ }
+}
+
+
+const char *Capsule::Name() const {
+ assert(master);
+ return master->Name();
+}
+
+Uint8 Capsule::Level() const {
+ assert(master);
+ return master->Level();
+}
+
+const Sprite *Capsule::Sprite() {
+ assert(master);
+ return master->BattleSprite();
+}
+
+const Animation *Capsule::MeleeAnimation() const {
+ assert(master);
+ return master->MeleeAnimation();
+}
+
+const Animation *Capsule::AttackAnimation() const {
+ assert(master);
+ return master->AttackAnimation();
+}
+
+const Animation *Capsule::SpellAnimation() const {
+ assert(master);
+ return master->SpellAnimation();
+}
+
+
+Uint16 Capsule::MaxHealth() const {
+ assert(master);
+ return master->MaxHealth();
+}
+
+Uint16 Capsule::Health() const {
+ return health;
+}
+
+int Capsule::RelativeHealth(int max) const {
+ return Health() * max / MaxHealth();
+}
+
+void Capsule::SubtractHealth(int amount) {
+ if (amount > health) {
+ health = 0;
+ } else {
+ health -= amount;
+ }
+}
+
+}
--- /dev/null
+#ifndef BATTLE_CAPSULE_H_
+#define BATTLE_CAPSULE_H_
+
+namespace common {
+ class Capsule;
+}
+
+#include "AttackChoice.h"
+#include "../common/Stats.h"
+#include "../geometry/Vector.h"
+#include "../graphics/Animation.h"
+#include "../graphics/fwd.h"
+#include "../graphics/Menu.h"
+
+namespace battle {
+
+class Capsule {
+
+public:
+ Capsule(common::Capsule *master = 0);
+
+public:
+ bool Active() const { return master; }
+
+ const char *Name() const;
+ Uint8 Level() const;
+ const graphics::Sprite *Sprite();
+
+ Uint16 MaxHealth() const;
+ Uint16 Health() const;
+ int RelativeHealth(int max) const;
+ void SubtractHealth(int amount);
+
+ common::Stats &GetStats() { return stats; }
+ const common::Stats &GetStats() const { return stats; }
+
+ graphics::AnimationRunner &GetAnimation() { return animation; }
+ const graphics::AnimationRunner &GetAnimation() const { return animation; }
+ void SetAnimation(const graphics::AnimationRunner &a) { animation = a; }
+
+ const graphics::Animation *MeleeAnimation() const;
+ const graphics::Animation *AttackAnimation() const;
+ const graphics::Animation *SpellAnimation() const;
+
+ geometry::Vector<int> &Position() { return position; }
+ const geometry::Vector<int> &Position() const { return position; }
+
+ AttackChoice &GetAttackChoice() { return attackChoice; }
+ const AttackChoice &GetAttackChoice() const { return attackChoice; }
+
+private:
+ common::Capsule *master;
+
+ int health;
+
+ graphics::AnimationRunner animation;
+ geometry::Vector<int> position;
+ AttackChoice attackChoice;
+ common::Stats stats;
+
+};
+
+}
+
+#endif /* BATTLE_CAPSULE_H_ */
#include "NumberAnimation.h"
#include "../geometry/Vector.h"
+#include "../graphics/Sprite.h"
using app::State;
using geometry::Vector;
}
void TargetSelection::MoveRight() {
+ assert(battle);
if (TargetsMonsters()) {
cursor = (cursor + 1) % battle->MaxMonsters();
while (!battle->MonsterPositionOccupied(cursor)) {
}
void TargetSelection::MoveDown() {
+ assert(battle);
if (TargetsMonsters()) {
SelectHeroes();
return;
}
void TargetSelection::MoveLeft() {
+ assert(battle);
if (TargetsMonsters()) {
cursor = (cursor + battle->MaxMonsters() - 1) % battle->MaxMonsters();
FindNextEnemy();
}
void TargetSelection::FindNextEnemy() {
+ assert(battle);
int start(cursor);
while (!battle->MonsterPositionOccupied(cursor)) {
cursor = (cursor + battle->MaxMonsters() - 1) % battle->MaxMonsters();
#ifndef BATTLE_TARGETSELECTION_H_
#define BATTLE_TARGETSELECTION_H_
-#include "fwd.h"
-#include "../common/fwd.h"
+namespace common {
+ class TargetingMode;
+}
#include <vector>
namespace battle {
+class BattleState;
+
class TargetSelection {
public:
class AttackChoice;
class AttackTypeMenu;
class BattleState;
+class Capsule;
class Hero;
class HeroTag;
class Monster;
#include "../BattleState.h"
#include "../Hero.h"
#include "../Monster.h"
+#include "../TargetSelection.h"
#include "../../app/Application.h"
#include "../../app/Input.h"
#include "../../common/Ikari.h"
battle->CalculateDamage();
- if (battle->CurrentAttack().isMonster) {
+ if (battle->CurrentAttack().IsMonster()) {
Monster &monster(battle->MonsterAt(battle->CurrentAttack().index));
titleBarText = monster.Name();
targetAnimation = AnimationRunner(monster.MeleeAnimation());
moveAnimation = AnimationRunner(monster.AttackAnimation());
monster.SetAnimation(moveAnimation);
AddNumberAnimations(battle->MonsterAt(battle->CurrentAttack().index).GetAttackChoice().Selection());
+ } else if (battle->CurrentAttack().IsCapsule()) {
+ Capsule &capsule(battle->GetCapsule());
+ titleBarText = capsule.Name();
+ targetAnimation = AnimationRunner(capsule.MeleeAnimation());
+ moveAnimation = AnimationRunner(capsule.AttackAnimation());
+ capsule.SetAnimation(moveAnimation);
+ AddNumberAnimations(capsule.GetAttackChoice().Selection());
} else {
Hero &hero(battle->HeroAt(battle->CurrentAttack().index));
const AttackChoice &ac(battle->HeroAt(battle->CurrentAttack().index).GetAttackChoice());
if (titleBarText) titleBarTimer = GraphicsTimers().StartCountdown(850);
if (moveAnimation.Valid()) {
moveAnimation.Start(*this);
- if (battle->CurrentAttack().isMonster) {
+ if (battle->CurrentAttack().IsMonster()) {
battle->MonsterAt(battle->CurrentAttack().index).SetAnimation(moveAnimation);
- } else {
+ } else if (battle->CurrentAttack().IsHero()) {
battle->HeroAt(battle->CurrentAttack().index).SetAnimation(moveAnimation);
+ } else {
+ battle->GetCapsule().SetAnimation(moveAnimation);
}
}
if (targetAnimation.Valid()) {
void PerformAttacks::ResetAnimation() {
if (moveAnimation.Valid()) {
moveAnimation.Clear();
- if (!battle->CurrentAttack().isMonster) {
+ if (!battle->CurrentAttack().IsMonster()) {
battle->HeroAt(battle->CurrentAttack().index).GetAnimation().Clear();
}
}
battle->RenderBackground(screen, offset);
battle->RenderMonsters(screen, offset);
battle->RenderHeroes(screen, offset);
+ battle->RenderCapsule(screen, offset);
battle->RenderSmallHeroTags(screen, offset);
RenderTitleBar(screen, offset);
RenderNumbers(screen, offset);
#include "../../app/State.h"
-#include "../BattleState.h"
#include "../NumberAnimation.h"
#include "../../geometry/Vector.h"
#include "../../graphics/Animation.h"
namespace battle {
+class BattleState;
+class TargetSelection;
+
class PerformAttacks
: public app::State {
--- /dev/null
+#include "Capsule.h"
+
+#include "../common/Spell.h"
+#include "../common/Stats.h"
+#include "../graphics/Animation.h"
+#include "../graphics/Sprite.h"
+#include "../loader/Interpreter.h"
+#include "../loader/TypeDescription.h"
+
+#include <cassert>
+
+using common::Spell;
+using common::Stats;
+using graphics::Animation;
+using graphics::Sprite;
+using loader::FieldDescription;
+using loader::Interpreter;
+using loader::TypeDescription;
+
+namespace common {
+
+Capsule::Capsule()
+: name("")
+, alignment("")
+
+, maxHealth(0)
+
+, level(1)
+, experience(0)
+
+, levelLadder(0)
+, numLevels(0)
+
+, classes(0)
+, numClasses(0)
+, curClass(0)
+, maxClass(0) {
+
+}
+
+
+const char *Capsule::ClassName() const {
+ return GetClass().name;
+}
+
+const char *Capsule::Tribe() const {
+ return GetClass().tribe;
+}
+
+const Spell *Capsule::Attack1() const {
+ return GetClass().attacks[0];
+}
+
+const Spell *Capsule::Attack2() const {
+ return GetClass().attacks[1];
+}
+
+const Spell *Capsule::Attack3() const {
+ return GetClass().attacks[2];
+}
+
+
+Uint16 Capsule::MaxHealth() const {
+ return maxHealth + GetClass().healthBoost;
+}
+
+
+Stats Capsule::GetStats() const {
+ return stats + GetClass().statBoost;
+}
+
+int Capsule::NextLevel() const {
+ int levelOffset(Level() - 1);
+ if (levelOffset < numLevels) {
+ return levelLadder[levelOffset] - Experience();
+ } else {
+ return 0;
+ }
+}
+
+
+Sprite *Capsule::BattleSprite() {
+ return GetClass().battleSprite;
+}
+
+const Sprite *Capsule::BattleSprite() const {
+ return GetClass().battleSprite;
+}
+
+Animation *Capsule::MeleeAnimation() {
+ return GetClass().meleeAnimation;
+}
+
+Animation *Capsule::AttackAnimation() {
+ return GetClass().attackAnimation;
+}
+
+Animation *Capsule::SpellAnimation() {
+ return GetClass().spellAnimation;
+}
+
+
+Capsule::Class &Capsule::GetClass() {
+ assert(classes && curClass < numClasses);
+ return classes[curClass];
+}
+
+const Capsule::Class &Capsule::GetClass() const {
+ assert(classes && curClass < numClasses);
+ return classes[curClass];
+}
+
+
+Capsule::Class::Class()
+: name(0)
+, tribe(0)
+, battleSprite(0)
+, meleeAnimation(0)
+, attackAnimation(0)
+, spellAnimation(0)
+
+, healthBoost(0) {
+ attacks[0] = 0;
+ attacks[1] = 0;
+ attacks[2] = 0;
+}
+
+
+void Capsule::CreateTypeDescription() {
+ Capsule c;
+
+ TypeDescription &td(TypeDescription::Create(TYPE_ID, "Capsule"));
+ td.SetConstructor(&Construct);
+ td.SetSize(sizeof(Capsule));
+
+ td.AddField("name", FieldDescription(((char *)&c.name) - ((char *)&c), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("alignment", FieldDescription(((char *)&c.alignment) - ((char *)&c), Interpreter::STRING_ID).SetReferenced());
+
+ td.AddField("maxHealth", FieldDescription(((char *)&c.maxHealth) - ((char *)&c), Interpreter::NUMBER_ID));
+
+ td.AddField("stats", FieldDescription(((char *)&c.stats) - ((char *)&c), Stats::TYPE_ID));
+
+ td.AddField("level", FieldDescription(((char *)&c.level) - ((char *)&c), Interpreter::NUMBER_ID));
+ td.AddField("experience", FieldDescription(((char *)&c.experience) - ((char *)&c), Interpreter::NUMBER_ID));
+
+ td.AddField("ladder", FieldDescription(((char *)&c.levelLadder) - ((char *)&c), Interpreter::NUMBER_ID).SetReferenced().SetAggregate());
+
+ td.AddField("classes", FieldDescription(((char *)&c.classes) - ((char *)&c), Class::TYPE_ID).SetReferenced().SetAggregate());
+ td.AddField("class", FieldDescription(((char *)&c.curClass) - ((char *)&c), Interpreter::NUMBER_ID));
+ td.AddField("maxClass", FieldDescription(((char *)&c.maxClass) - ((char *)&c), Interpreter::NUMBER_ID));
+
+ Class::CreateTypeDescription();
+}
+
+void Capsule::Construct(void *data) {
+ new (data) Capsule;
+}
+
+
+void Capsule::Class::CreateTypeDescription() {
+ Class c;
+
+ TypeDescription &td(TypeDescription::Create(TYPE_ID, "CapsuleClass"));
+ td.SetConstructor(&Construct);
+ td.SetSize(sizeof(Class));
+
+ td.AddField("name", FieldDescription(((char *)&c.name) - ((char *)&c), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("tribe", FieldDescription(((char *)&c.tribe) - ((char *)&c), Interpreter::STRING_ID).SetReferenced());
+
+ td.AddField("attack1", FieldDescription(((char *)&c.attacks[0]) - ((char *)&c), Spell::TYPE_ID).SetReferenced());
+ td.AddField("attack2", FieldDescription(((char *)&c.attacks[1]) - ((char *)&c), Spell::TYPE_ID).SetReferenced());
+ td.AddField("attack3", FieldDescription(((char *)&c.attacks[2]) - ((char *)&c), Spell::TYPE_ID).SetReferenced());
+
+ td.AddField("battleSprite", FieldDescription(((char *)&c.battleSprite) - ((char *)&c), Sprite::TYPE_ID).SetReferenced());
+ td.AddField("meleeAnimation", FieldDescription(((char *)&c.meleeAnimation) - ((char *)&c), Animation::TYPE_ID).SetReferenced());
+ td.AddField("attackAnimation", FieldDescription(((char *)&c.attackAnimation) - ((char *)&c), Animation::TYPE_ID).SetReferenced());
+ td.AddField("spellAnimation", FieldDescription(((char *)&c.spellAnimation) - ((char *)&c), Animation::TYPE_ID).SetReferenced());
+
+ td.AddField("healthBoost", FieldDescription(((char *)&c.healthBoost) - ((char *)&c), Interpreter::NUMBER_ID));
+ td.AddField("statBoost", FieldDescription(((char *)&c.statBoost) - ((char *)&c), Stats::TYPE_ID));
+}
+
+void Capsule::Class::Construct(void *data) {
+ new (data) Capsule::Class;
+}
+
+}
--- /dev/null
+#ifndef COMMON_CAPSULE_H_
+#define COMMON_CAPSULE_H_
+
+namespace graphics {
+ class Animation;
+ class Sprite;
+}
+
+#include "../common/Stats.h"
+
+#include <SDL.h>
+
+namespace common {
+
+class Spell;
+
+class Capsule {
+
+public:
+ static const int TYPE_ID = 307;
+
+public:
+ Capsule();
+
+ const char *Name() const { return name; }
+ const char *ClassName() const;
+ const char *Alignment() const { return alignment; }
+ const char *Tribe() const;
+ const Spell *Attack1() const;
+ const Spell *Attack2() const;
+ const Spell *Attack3() const;
+
+ Uint16 MaxHealth() const;
+
+ Stats GetStats() const;
+
+ Uint8 Level() const { return level; }
+ int Experience() const { return experience; }
+ int NextLevel() const;
+
+ graphics::Sprite *BattleSprite();
+ const graphics::Sprite *BattleSprite() const;
+ graphics::Animation *MeleeAnimation();
+ graphics::Animation *AttackAnimation();
+ graphics::Animation *SpellAnimation();
+
+ static void CreateTypeDescription();
+ static void Construct(void *);
+
+private:
+ struct Class {
+ static const int TYPE_ID = 308;
+
+ Class();
+
+ static void CreateTypeDescription();
+ static void Construct(void *);
+
+ const char *name;
+ const char *tribe;
+ Spell *attacks[3];
+ graphics::Sprite *battleSprite;
+ graphics::Animation *meleeAnimation;
+ graphics::Animation *attackAnimation;
+ graphics::Animation *spellAnimation;
+
+ int healthBoost;
+ Stats statBoost;
+ };
+
+ Class &GetClass();
+ const Class &GetClass() const;
+
+ const char *name;
+ const char *alignment;
+
+ int maxHealth;
+
+ Stats stats;
+
+ int level;
+ int experience;
+
+ int *levelLadder;
+ int numLevels;
+
+ Class *classes;
+ int numClasses, curClass, maxClass;
+
+};
+
+}
+
+#endif /* COMMON_CAPSULE_H_ */
GameState::GameState()
: partySize(1)
+, capsule(0)
, money(0)
, time(0)
, messageSpeed(MESSAGE_SPEED_NORMAL)
#ifndef COMMON_GAMESTATE_H_
#define COMMON_GAMESTATE_H_
+#include "Capsule.h"
#include "Hero.h"
#include "Inventory.h"
Hero *party[4];
int partySize;
+ Capsule capsules[7];
+ Capsule *capsule;
+
Inventory inventory;
Uint32 money;
};
+
+inline Stats operator +(const Stats &lhs, const Stats &rhs) {
+ return Stats(
+ lhs.Attack() + rhs.Attack(),
+ lhs.Defense() + rhs.Defense(),
+ lhs.Strength() + rhs.Strength(),
+ lhs.Agility() + rhs.Agility(),
+ lhs.Intelligence() + rhs.Intelligence(),
+ lhs.Gut() + rhs.Gut(),
+ lhs.MagicResistance() + rhs.MagicResistance());
+}
+
}
#endif /* COMMON_STATS_H_ */
namespace common {
+class Capsule;
struct GameConfig;
struct GameState;
class Hero;
#include "Animation.h"
#include "Sprite.h"
+#include "../app/Application.h"
+#include "../app/State.h"
#include "../loader/Interpreter.h"
#include "../loader/TypeDescription.h"
+#include <cassert>
+
using loader::FieldDescription;
using loader::Interpreter;
using loader::TypeDescription;
td.AddField("repeat", FieldDescription(((char *)&a.repeat) - ((char *)&a) - offset, Interpreter::BOOLEAN_ID).SetDescription("whether the animation should start over at the beginning after reaching the last frame"));
}
+
+void AnimationRunner::Start(app::State &ctrl) {
+ assert(animation);
+ timer = ctrl.GraphicsTimers().StartInterval(animation->FrameTime());
+}
+
+void AnimationRunner::Start(app::Application &ctrl) {
+ assert(animation);
+ timer = ctrl.GlobalTimers().StartInterval(animation->FrameTime());
+}
+
+void AnimationRunner::Stop() {
+ timer = app::Timer<Uint32>();
+}
+
+bool AnimationRunner::Started() const {
+ return timer.Started();
+}
+
+bool AnimationRunner::Running() const {
+ return animation
+ && timer.Running()
+ && (animation->Repeat()
+ || timer.Iteration() < animation->NumFrames());
+}
+
+bool AnimationRunner::Finished() const {
+ return Started() && !Running();
+}
+
+bool AnimationRunner::JustFinished() const {
+ return animation
+ && timer.JustHit()
+ && timer.Iteration() == animation->NumFrames();
+}
+
+
+void AnimationRunner::Draw(SDL_Surface *dest, geometry::Vector<int> position) const {
+ GetSprite()->Draw(dest,
+ position + animation->Offset(Frame()),
+ animation->Col(Frame()) + ColOffset(),
+ animation->Row(Frame()) + RowOffset());
+}
+
+void AnimationRunner::DrawTopRight(SDL_Surface *dest, geometry::Vector<int> position) const {
+ geometry::Vector<int> offset(-GetSprite()->Width(), 0);
+ Draw(dest, position + offset);
+}
+
+void AnimationRunner::DrawCenter(SDL_Surface *dest, geometry::Vector<int> position) const {
+ Draw(dest, position - (GetSprite()->Size() / 2));
+}
+
+void AnimationRunner::DrawCenterBottom(SDL_Surface *dest, geometry::Vector<int> position) const {
+ geometry::Vector<int> offset(-GetSprite()->Width() / 2, -GetSprite()->Height());
+ Draw(dest, position + offset);
+}
+
+int AnimationRunner::Frame() const {
+ return Running()
+ ? ((timer.Iteration() + frameShift) % animation->NumFrames())
+ : 0;
+}
+
+
}
#ifndef GRAPHICS_ANIMATION_H_
#define GRAPHICS_ANIMATION_H_
-#include "Sprite.h"
-#include "../app/Application.h"
-#include "../app/State.h"
+namespace app {
+ class Application;
+ class State;
+}
+namespace loader {
+ class TypeDescription;
+}
+
#include "../app/Timer.h"
-#include "../loader/fwd.h"
#include "../geometry/Vector.h"
#include <memory>
namespace graphics {
+class Sprite;
+
class Animation {
public:
bool Valid() const { return animation; }
void Clear() { animation = 0; timer = app::Timer<Uint32>(); }
- void Start(app::State &ctrl) {
- timer = ctrl.GraphicsTimers().StartInterval(animation->FrameTime());
- }
- void Start(app::Application &ctrl) {
- timer = ctrl.GlobalTimers().StartInterval(animation->FrameTime());
- }
- void Stop() {
- timer = app::Timer<Uint32>();
- }
- bool Started() const {
- return timer.Started();
- }
- bool Running() const {
- return timer.Running() && (animation->Repeat() || timer.Iteration() < animation->NumFrames());
- }
- bool Finished() const {
- return Started() && !Running();
- }
- bool JustFinished() const {
- return timer.JustHit() && timer.Iteration() == animation->NumFrames();
- }
+ void Start(app::State &ctrl);
+ void Start(app::Application &ctrl);
+ void Stop();
+ bool Started() const;
+ bool Running() const;
+ bool Finished() const;
+ bool JustFinished() const;
const app::Timer<Uint32> &GetTimer() { return timer; }
void ChangeSprite(const Sprite *s) { sprite = s; }
const Sprite *GetSprite() const { return sprite ? sprite : animation->GetSprite(); }
- void Draw(SDL_Surface *dest, geometry::Vector<int> position) const {
- GetSprite()->Draw(dest, position + animation->Offset(Frame()), animation->Col(Frame()) + ColOffset(), animation->Row(Frame()) + RowOffset());
- }
- void DrawTopRight(SDL_Surface *dest, geometry::Vector<int> position) const {
- geometry::Vector<int> offset(-GetSprite()->Width(), 0);
- Draw(dest, position + offset);
- }
- void DrawCenter(SDL_Surface *dest, geometry::Vector<int> position) const {
- Draw(dest, position - (GetSprite()->Size() / 2));
- }
- void DrawCenterBottom(SDL_Surface *dest, geometry::Vector<int> position) const {
- geometry::Vector<int> offset(-GetSprite()->Width() / 2, -GetSprite()->Height());
- Draw(dest, position + offset);
- }
-
- int Frame() const { return Running() ? ((timer.Iteration() + frameShift) % animation->NumFrames()) : 0; }
+ void Draw(SDL_Surface *dest, geometry::Vector<int> position) const;
+ void DrawTopRight(SDL_Surface *dest, geometry::Vector<int> position) const;
+ void DrawCenter(SDL_Surface *dest, geometry::Vector<int> position) const;
+ void DrawCenterBottom(SDL_Surface *dest, geometry::Vector<int> position) const;
+
+ int Frame() const;
private:
const Animation *animation;
#include "../loader/Interpreter.h"
#include "../loader/TypeDescription.h"
+using geometry::Vector;
using loader::FieldDescription;
using loader::Interpreter;
using loader::TypeDescription;
namespace graphics {
+ComplexAnimation::ComplexAnimation()
+: frames(0)
+, numFrames(0) {
+
+}
+
+ComplexAnimation::ComplexAnimation(
+ const Sprite *sprite,
+ int frameTime,
+ bool repeat)
+: Animation(sprite, frameTime, repeat)
+, frames(0)
+, numFrames(0) {
+
+}
+
+
+int ComplexAnimation::NumFrames() const {
+ return numFrames;
+}
+
+int ComplexAnimation::Col(int frame) const {
+ return frames[frame].col;
+}
+
+int ComplexAnimation::Row(int frame) const {
+ return frames[frame].row;
+}
+
+Vector<int> ComplexAnimation::Offset(int frame) const {
+ return frames[frame].disposition;
+}
+
+
void ComplexAnimation::CreateTypeDescription() {
ComplexAnimation ca;
Animation *a(&ca);
static const int TYPE_ID = 402;
public:
- ComplexAnimation() : frames(0), numFrames(0) { }
- ComplexAnimation(const Sprite *sprite, int frameTime, bool repeat = false)
- : Animation(sprite, frameTime, repeat), frames(0), numFrames(0) { }
+ ComplexAnimation();
+ ComplexAnimation(const Sprite *sprite, int frameTime, bool repeat = false);
public:
struct FrameProp {
static void Construct(void *);
protected:
- virtual int NumFrames() const { return numFrames; };
- virtual int Col(int frame) const { return frames[frame].col; }
- virtual int Row(int frame) const { return frames[frame].row; }
- virtual geometry::Vector<int> Offset(int frame) const { return frames[frame].disposition; }
+ virtual int NumFrames() const;
+ virtual int Col(int frame) const;
+ virtual int Row(int frame) const;
+ virtual geometry::Vector<int> Offset(int frame) const;
private:
const FrameProp *frames;
#include "../loader/TypeDescription.h"
#include <cmath>
+#include <cstring>
#include <iostream>
using geometry::Vector;
if (!sprite) return;
int length(0);
- while (length < maxWidth && s[length] != '\0') {
- ++length;
+ if (maxWidth > 0) {
+ while (length < maxWidth && s[length] != '\0') {
+ ++length;
+ }
+ } else {
+ length = std::strlen(s);
}
Vector<int> position(positionIn.X() - length * CharWidth(), positionIn.Y());
void Font::DrawNumberRight(int number, SDL_Surface *dest, const Vector<int> &positionIn, int digits) const {
if (!sprite) return;
- Vector<int> position(positionIn.X() - digits * CharWidth(), positionIn.Y());
+ Vector<int> position(positionIn);
+ if (digits > 0) {
+ position.X() -= digits * CharWidth();
+ } else if (number == 0) {
+ position.X() -= CharWidth();
+ } else {
+ for (int i = number; i > 0; i /= 10) {
+ position.X() -= CharWidth();
+ }
+ }
DrawNumber(number, dest, position, digits);
}
td.AddField("additionalTextGap", FieldDescription(((char *)&p.additionalTextGap) - ((char *)&p), Interpreter::NUMBER_ID).SetDescription("space between normal and additional text in pixels"));
td.AddField("wrapX", FieldDescription(((char *)&p.wrapX) - ((char *)&p), Interpreter::BOOLEAN_ID).SetDescription("horizontally wrap cursor movement"));
td.AddField("wrapY", FieldDescription(((char *)&p.wrapY) - ((char *)&p), Interpreter::BOOLEAN_ID).SetDescription("vertically wrap cursor movement"));
+ td.AddField("thirdColumnHack", FieldDescription(((char *)&p.thirdColumnHack) - ((char *)&p), Interpreter::NUMBER_ID).SetDescription("offset the third column by this many characters"));
}
void MenuProperties::Construct(void *data) {
int charsPerNumber;
int charsPerAdditionalText;
int additionalTextGap;
+ int thirdColumnHack;
char delimiter;
bool wrapX;
bool wrapY;
, charsPerEntry(0), rows(1), rowGap(0)
, iconSpace(0), cols(1), colGap(0)
, charsPerNumber(0), charsPerAdditionalText(0)
- , additionalTextGap(0), delimiter(':')
+ , additionalTextGap(0), thirdColumnHack(0), delimiter(':')
, wrapX(false), wrapY(false) { }
static void CreateTypeDescription();
(i % cols) * (ColWidth() + colGap),
(i / cols) * RowHeight());
- // Third column hack!
- // This fixes the position of the "DROP" item in the inventory menu.
- if (i % cols == 2) {
- iconOffset += geometry::Vector<int>(font->CharWidth(), 0);
+ // This fixes the position of the third column of the inventory and capsule menus.
+ if (thirdColumnHack && i % cols == 2) {
+ iconOffset += geometry::Vector<int>(font->CharWidth() * thirdColumnHack, 0);
}
if (entries[start + i].icon) {
geometry::Vector<int> cursorOffset(
(selected % cols) * (ColWidth() + colGap) - cursor->Width(),
((selected - start) / cols) * RowHeight());
- // Third column hack!
- // This fixes the position of the "DROP" item in the inventory menu.
- if (selected % cols == 2) {
- cursorOffset += geometry::Vector<int>(font->CharWidth(), 0);
+ // This fixes the position of the third column of the inventory and capsule menus.
+ if (thirdColumnHack && selected % cols == 2) {
+ cursorOffset += geometry::Vector<int>(font->CharWidth() * thirdColumnHack, 0);
}
switch (state) {
case STATE_INACTIVE:
namespace graphics {
+SimpleAnimation::SimpleAnimation()
+: numFrames(0)
+, col(0)
+, row(0) {
+
+}
+
+SimpleAnimation::SimpleAnimation(
+ const Sprite *sprite,
+ int frameTime,
+ int numFrames,
+ int col,
+ int row,
+ bool repeat)
+: Animation(sprite, frameTime, repeat)
+, numFrames(numFrames)
+, col(col)
+, row(row) {
+
+}
+
+
+int SimpleAnimation::NumFrames() const {
+ return numFrames;
+}
+
+int SimpleAnimation::Col(int frame) const {
+ return col;
+}
+
+int SimpleAnimation::Row(int frame) const {
+ return row + frame;
+}
+
+
void SimpleAnimation::CreateTypeDescription() {
SimpleAnimation sa;
Animation *a(&sa);
static const int TYPE_ID = 408;
public:
- SimpleAnimation()
- : numFrames(0), col(0), row(0) { }
- SimpleAnimation(const Sprite *sprite, int frameTime, int numFrames, int col = 0, int row = 0, bool repeat = false)
- : Animation(sprite, frameTime, repeat), numFrames(numFrames), col(col), row(row) { }
+ SimpleAnimation();
+ SimpleAnimation(const Sprite *sprite, int frameTime, int numFrames,
+ int col = 0, int row = 0, bool repeat = false);
public:
void SetNumFrames(int n) { numFrames = n; }
static void Construct(void *);
protected:
- virtual int NumFrames() const { return numFrames; };
- virtual int Col(int frame) const { return col; }
- virtual int Row(int frame) const { return row + frame; }
+ virtual int NumFrames() const;
+ virtual int Col(int frame) const;
+ virtual int Row(int frame) const;
private:
int numFrames;
#include "../battle/Resources.h"
#include "../battle/Monster.h"
#include "../battle/PartyLayout.h"
+#include "../common/Capsule.h"
#include "../common/Hero.h"
#include "../common/Item.h"
#include "../common/Spell.h"
using battle::Monster;
using battle::PartyLayout;
+using common::Capsule;
using common::Hero;
using common::Item;
using common::Spell;
intp.GetObject(battle::Resources::TYPE_ID, ident));
}
+Capsule *Caster::GetCapsule(const string &ident) {
+ return reinterpret_cast<Capsule *>(intp.GetObject(Capsule::TYPE_ID, ident));
+}
+
Hero *Caster::GetHero(const string &ident) {
return reinterpret_cast<Hero *>(intp.GetObject(Hero::TYPE_ID, ident));
}
public:
battle::Resources *GetBattleResources(const std::string &identifier);
+ common::Capsule *GetCapsule(const std::string &identifier);
common::Hero *GetHero(const std::string &identifier);
common::Item *GetItem(const std::string &identifier);
map::Map *GetMap(const std::string &identifier);
#include "app/Arguments.h"
#include "app/Input.h"
#include "battle/BattleState.h"
+#include "battle/Capsule.h"
#include "battle/Hero.h"
#include "battle/Monster.h"
#include "battle/PartyLayout.h"
#include "battle/Resources.h"
+#include "common/Capsule.h"
#include "common/GameConfig.h"
#include "common/GameState.h"
#include "common/Hero.h"
using battle::BattleState;
using battle::Monster;
using battle::PartyLayout;
+using common::Capsule;
using common::GameConfig;
using common::GameState;
using common::Hero;
battle::Monster::CreateTypeDescription();
battle::PartyLayout::CreateTypeDescription();
+ common::Capsule::CreateTypeDescription();
common::Hero::CreateTypeDescription();
common::Ikari::CreateTypeDescription();
common::Item::CreateTypeDescription();
gameState.party[3] = &gameState.heroes[3];
gameState.partySize = 4;
+ gameState.capsules[0] = *caster.GetCapsule("flash");
+ gameState.capsule = gameState.capsules;
+
GameConfig gameConfig;
gameConfig.state = &gameState;
gameConfig.heroesLayout = caster.GetPartyLayout("heroesLayout");
battleState->AddMonster(monster);
battleState->AddMonster(monster);
battleState->AddMonster(monster);
+ battleState->SetCapsule(caster.GetCapsule("flash"));
battleState->AddHero(gameState.heroes[0]);
battleState->AddHero(gameState.heroes[1]);
battleState->AddHero(gameState.heroes[2]);
battleState->AddHero(*game->state->party[i]);
}
}
+ if (game->state->capsule) {
+ battleState->SetCapsule(game->state->capsule);
+ }
for (battle::Monster **monster((*e)->MonstersBegin()); monster != (*e)->MonstersEnd(); ++monster) {
battleState->AddMonster(**monster);
}
// TODO: move entity erase to happen after the transition or battle
entities.erase(e);
return true;
- // needed information here:
- // - battle background (from tile/area/map)
- // - monsters + layout (from entity)
}
}
}
--- /dev/null
+#include "CapsuleChangeMenu.h"
+
+#include "CapsuleMenu.h"
+#include "Resources.h"
+#include "../app/Application.h"
+#include "../app/Input.h"
+#include "../common/Inventory.h"
+#include "../common/Item.h"
+#include "../common/GameConfig.h"
+#include "../common/GameState.h"
+#include "../graphics/Font.h"
+#include "../graphics/Frame.h"
+
+using app::Input;
+using common::Capsule;
+using common::Inventory;
+using common::Item;
+using geometry::Vector;
+using graphics::Font;
+using graphics::Frame;
+
+namespace menu {
+
+CapsuleChangeMenu::CapsuleChangeMenu(CapsuleMenu *parent)
+: parent(parent) {
+
+}
+
+
+void CapsuleChangeMenu::OnEnterState(SDL_Surface *) {
+
+}
+
+void CapsuleChangeMenu::OnExitState(SDL_Surface *) {
+
+}
+
+void CapsuleChangeMenu::OnResumeState(SDL_Surface *) {
+
+}
+
+void CapsuleChangeMenu::OnPauseState(SDL_Surface *) {
+
+}
+
+
+void CapsuleChangeMenu::OnResize(int width, int height) {
+
+}
+
+
+void CapsuleChangeMenu::HandleEvents(const Input &input) {
+ if (input.JustPressed(Input::ACTION_B)) {
+ Ctrl().PopState();
+ }
+}
+
+void CapsuleChangeMenu::UpdateWorld(float deltaT) {
+
+}
+
+void CapsuleChangeMenu::Render(SDL_Surface *screen) {
+ const Font &font(*parent->Res().statusFont);
+ const Vector<int> offset((screen->w - Width()) / 2, (screen->h - Height()) / 2);
+ const Vector<int> capsuleOffset(
+ 6 * font.CharWidth(),
+ 12 * font.CharHeight());
+ const Vector<int> infoOffset(
+ 12 * font.CharWidth(),
+ 2 * font.CharHeight() - font.CharHeight() / 8);
+ // TODO: wheel offset: top left, center, or center bottom?
+ const Vector<int> wheelOffset;
+ const Vector<int> menuOffset(
+ font.CharWidth(),
+ 24 * font.CharHeight() - font.CharHeight() / 8);
+
+ parent->RenderBackground(screen);
+ parent->RenderCapsule(screen, offset + capsuleOffset);
+ parent->RenderInfo(screen, offset + infoOffset);
+ parent->RenderWheel(screen, offset + wheelOffset);
+ parent->RenderMenu(screen, offset + menuOffset);
+}
+
+
+int CapsuleChangeMenu::Width() const {
+ return parent->Width();
+}
+
+int CapsuleChangeMenu::Height() const {
+ return parent->Height();
+}
+
+const Capsule &CapsuleChangeMenu::GetCapsule() const {
+ return parent->GetCapsule();
+}
+
+}
--- /dev/null
+#ifndef MENU_CAPSULECHANGEMENU_H_
+#define MENU_CAPSULECHANGEMENU_H_
+
+#include "fwd.h"
+#include "../app/State.h"
+#include "../common/fwd.h"
+#include "../geometry/Vector.h"
+
+namespace menu {
+
+class CapsuleMenu;
+
+class CapsuleChangeMenu
+: public app::State {
+
+public:
+ explicit CapsuleChangeMenu(CapsuleMenu *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);
+
+ const common::Capsule &GetCapsule() const;
+
+private:
+ CapsuleMenu *parent;
+
+};
+
+}
+
+#endif /* MENU_CAPSULECHANGEMENU_H_ */
--- /dev/null
+#include "CapsuleFeedMenu.h"
+
+#include "CapsuleMenu.h"
+#include "Resources.h"
+#include "../app/Application.h"
+#include "../app/Input.h"
+#include "../common/Inventory.h"
+#include "../common/Item.h"
+#include "../common/GameConfig.h"
+#include "../common/GameState.h"
+#include "../graphics/Font.h"
+#include "../graphics/Frame.h"
+
+using app::Input;
+using common::Capsule;
+using common::Inventory;
+using common::Item;
+using geometry::Vector;
+using graphics::Font;
+using graphics::Frame;
+
+namespace menu {
+
+CapsuleFeedMenu::CapsuleFeedMenu(CapsuleMenu *parent)
+: parent(parent)
+, menu(*parent->Res().capsuleFeedMenuProperties)
+, itemMenu(*parent->Res().inventoryMenuProperties) {
+ menu.Add(parent->Res().itemMenuSelectText, CHOICE_SELECT);
+ menu.Add(parent->Res().itemMenuSortText, CHOICE_SORT);
+ LoadInventory();
+}
+
+
+void CapsuleFeedMenu::OnEnterState(SDL_Surface *) {
+ menu.SetSelected();
+ itemMenu.SetActive();
+}
+
+void CapsuleFeedMenu::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) {
+ // TODO: find out which items are impossible to feed to a capsule
+ itemMenu.Add(item->Name(), item, true, item->MenuIcon(), inv.ItemCountAt(i));
+ } else {
+ itemMenu.AddEmptyEntry();
+ }
+ }
+}
+
+void CapsuleFeedMenu::OnExitState(SDL_Surface *) {
+
+}
+
+void CapsuleFeedMenu::OnResumeState(SDL_Surface *) {
+
+}
+
+void CapsuleFeedMenu::OnPauseState(SDL_Surface *) {
+
+}
+
+
+void CapsuleFeedMenu::OnResize(int width, int height) {
+
+}
+
+
+void CapsuleFeedMenu::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_SELECT:
+ if (true /* can feed */) {
+ // TODO: implement capsule feeding
+ }
+ itemMenu.SetActive();
+ break;
+ case CHOICE_SORT:
+ // invalid state, recover
+ menu.SetActive();
+ itemMenu.SetInactive();
+ 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 CapsuleFeedMenu::UpdateWorld(float deltaT) {
+
+}
+
+void CapsuleFeedMenu::Render(SDL_Surface *screen) {
+ const Font &font(*parent->Res().statusFont);
+ const Vector<int> offset((screen->w - Width()) / 2, (screen->h - Height()) / 2);
+ const Vector<int> nameOffset(
+ font.CharWidth(),
+ 2 * font.CharHeight() - font.CharHeight() / 8);
+ const Vector<int> spriteOffset(
+ 3 * font.CharWidth() + font.CharWidth() * 3 / 4,
+ 4 * font.CharHeight() + font.CharHeight() / 4);
+ const Vector<int> hungerOffset(
+ 13 * font.CharWidth(),
+ 9 * font.CharHeight() + font.CharHeight() / 8);
+ const Vector<int> menuOffset(
+ font.CharWidth(),
+ 13 * font.CharHeight() + font.CharHeight() / 8);
+ const Vector<int> itemsOffset(
+ font.CharWidth(),
+ 16 * font.CharHeight() + font.CharHeight() / 8);
+
+ parent->RenderBackground(screen);
+ RenderName(screen, offset + nameOffset);
+ RenderSprite(screen, offset + spriteOffset);
+ RenderHunger(screen, offset + hungerOffset);
+ RenderMenu(screen, offset + menuOffset);
+ RenderItems(screen, offset + itemsOffset);
+}
+
+void CapsuleFeedMenu::RenderName(SDL_Surface *screen, const Vector<int> &offset) const {
+ const Font &font(*parent->Res().statusFont);
+ const Vector<int> separatorOffset(5 * font.CharWidth(), 0);
+ const Vector<int> nameOffset(6 * font.CharWidth(), 0);
+
+ font.DrawString(parent->Res().capsuleNameLabel, screen, offset, 5);
+ font.DrawChar(':', screen, offset + separatorOffset);
+ font.DrawString(GetCapsule().Name(), screen, offset + nameOffset);
+}
+
+void CapsuleFeedMenu::RenderSprite(SDL_Surface *screen, const Vector<int> &offset) const {
+ // TODO: sitting ground
+ GetCapsule().BattleSprite()->Draw(screen, offset);
+}
+
+void CapsuleFeedMenu::RenderHunger(SDL_Surface *screen, const Vector<int> &offset) const {
+ const Font &font(*parent->Res().normalFont);
+ const Frame &frame(*parent->Res().statusFrame);
+ const Vector<int> textOffset(2 * font.CharWidth(), font.CharHeight());
+
+ frame.Draw(screen, offset, 18 * font.CharWidth(), 3 * font.CharHeight());
+ font.DrawString(parent->Res().capsuleNotHungryText, screen, offset + textOffset, 15);
+}
+
+void CapsuleFeedMenu::RenderMenu(SDL_Surface *screen, const Vector<int> &offset) const {
+ const Font &font(*parent->Res().normalFont);
+ const Frame &frame(*parent->Res().statusFrame);
+ const Vector<int> labelOffset(2 * font.CharWidth(), font.CharHeight());
+ const Vector<int> menubgOffset(8 * font.CharWidth(), 0);
+ const Vector<int> menuOffset(11 * font.CharWidth(), font.CharHeight());
+
+ frame.Draw(screen, offset, 8 * font.CharWidth(), 3 * font.CharHeight());
+ font.DrawString(parent->Res().capsuleFeedLabel, screen, offset + labelOffset);
+ frame.Draw(screen, offset + menubgOffset, 22 * font.CharWidth(), 3 * font.CharHeight());
+ menu.Draw(screen, offset + menuOffset);
+}
+
+void CapsuleFeedMenu::RenderItems(SDL_Surface *screen, const Vector<int> &offset) const {
+ const Font &font(*parent->Res().normalFont);
+ const Frame &frame(*parent->Res().statusFrame);
+ const Vector<int> menuOffset(3 * font.CharWidth(), font.CharHeight() * 5 / 4);
+
+ frame.Draw(screen, offset, 30 * font.CharWidth(), 11 * font.CharHeight());
+ itemMenu.Draw(screen, offset + menuOffset);
+}
+
+
+int CapsuleFeedMenu::Width() const {
+ return parent->Width();
+}
+
+int CapsuleFeedMenu::Height() const {
+ return parent->Height();
+}
+
+const Capsule &CapsuleFeedMenu::GetCapsule() const {
+ return parent->GetCapsule();
+}
+
+}
--- /dev/null
+#ifndef MENU_CAPSULEFEEDMENU_H_
+#define MENU_CAPSULEFEEDMENU_H_
+
+#include "fwd.h"
+#include "../app/State.h"
+#include "../common/fwd.h"
+#include "../geometry/Vector.h"
+#include "../graphics/Menu.h"
+
+namespace menu {
+
+class CapsuleMenu;
+
+class CapsuleFeedMenu
+: public app::State {
+
+public:
+ explicit CapsuleFeedMenu(CapsuleMenu *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);
+
+ const common::Capsule &GetCapsule() const;
+
+ void LoadInventory();
+
+ void RenderName(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+ void RenderSprite(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+ void RenderHunger(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+ void RenderMenu(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+ void RenderItems(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+
+private:
+ CapsuleMenu *parent;
+ enum Choice {
+ CHOICE_SELECT,
+ CHOICE_SORT,
+ };
+ graphics::Menu<Choice> menu;
+ graphics::Menu<const common::Item *> itemMenu;
+
+};
+
+}
+
+#endif /* MENU_CAPSULEFEEDMENU_H_ */
--- /dev/null
+#include "CapsuleMenu.h"
+
+#include "CapsuleChangeMenu.h"
+#include "CapsuleFeedMenu.h"
+#include "CapsuleNameMenu.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/Spell.h"
+#include "../common/Stats.h"
+#include "../graphics/Font.h"
+#include "../graphics/Frame.h"
+#include "../graphics/Texture.h"
+
+using app::Input;
+using common::Capsule;
+using common::Stats;
+using geometry::Vector;
+using graphics::Font;
+using graphics::Frame;
+
+namespace menu {
+
+CapsuleMenu::CapsuleMenu(PartyMenu *parent)
+: parent(parent)
+, menu(*Res().capsuleMenuProperties) {
+ menu.Add(Res().capsuleFeedLabel, CHOICE_FEED);
+ menu.Add(Res().capsuleChangeLabel, CHOICE_CHANGE);
+ menu.Add(Res().capsuleNameLabel, CHOICE_NAME);
+}
+
+
+void CapsuleMenu::OnEnterState(SDL_Surface *) {
+
+}
+
+void CapsuleMenu::OnExitState(SDL_Surface *) {
+
+}
+
+void CapsuleMenu::OnResumeState(SDL_Surface *) {
+ menu.SetActive();
+}
+
+void CapsuleMenu::OnPauseState(SDL_Surface *) {
+
+}
+
+
+void CapsuleMenu::OnResize(int width, int height) {
+
+}
+
+
+void CapsuleMenu::HandleEvents(const Input &input) {
+ if (input.JustPressed(Input::PAD_LEFT)) {
+ menu.PreviousItem();
+ }
+ if (input.JustPressed(Input::PAD_RIGHT)) {
+ menu.NextItem();
+ }
+
+ if (input.JustPressed(Input::ACTION_A)) {
+ switch (menu.Selected()) {
+ case CHOICE_FEED:
+ Ctrl().PushState(new CapsuleFeedMenu(this));
+ menu.SetSelected();
+ break;
+ case CHOICE_CHANGE:
+ Ctrl().PushState(new CapsuleChangeMenu(this));
+ menu.SetSelected();
+ break;
+ case CHOICE_NAME:
+ Ctrl().PushState(new CapsuleNameMenu(this));
+ menu.SetSelected();
+ break;
+ }
+ } else if (input.JustPressed(Input::ACTION_B)) {
+ Ctrl().PopState();
+ }
+}
+
+void CapsuleMenu::UpdateWorld(float deltaT) {
+
+}
+
+void CapsuleMenu::Render(SDL_Surface *screen) {
+ const Font &font(*Res().statusFont);
+ const Vector<int> offset((screen->w - Width()) / 2, (screen->h - Height()) / 2);
+ const Vector<int> capsuleOffset(
+ 6 * font.CharWidth(),
+ 12 * font.CharHeight());
+ const Vector<int> infoOffset(
+ 12 * font.CharWidth(),
+ 2 * font.CharHeight() - font.CharHeight() / 8);
+ // TODO: wheel offset: top left, center, or center bottom?
+ const Vector<int> wheelOffset;
+ const Vector<int> statsOffset(
+ 12 * font.CharWidth(),
+ 15 * font.CharHeight() - font.CharHeight() / 8);
+ const Vector<int> menuOffset(
+ font.CharWidth(),
+ 24 * font.CharHeight() - font.CharHeight() / 8);
+
+ RenderBackground(screen);
+ RenderCapsule(screen, offset + capsuleOffset);
+ RenderInfo(screen, offset + infoOffset);
+ RenderWheel(screen, offset + wheelOffset);
+ RenderStats(screen, offset + statsOffset);
+ RenderMenu(screen, offset + menuOffset);
+}
+
+void CapsuleMenu::RenderBackground(SDL_Surface *screen) const {
+ Res().capsulebg->Render(screen, Vector<int>(), Vector<int>(screen->w, screen->h));
+}
+
+void CapsuleMenu::RenderCapsule(SDL_Surface *screen, const Vector<int> &offset) const {
+ GetCapsule().BattleSprite()->DrawCenterBottom(screen, offset);
+}
+
+void CapsuleMenu::RenderInfo(SDL_Surface *screen, const Vector<int> &offset) const {
+ const Capsule &capsule(GetCapsule());
+ const Font &font(*Res().statusFont);
+ const Vector<int> delimiterOffset(5 * font.CharWidth(), 0);
+ const Vector<int> valueOffset(6 * font.CharWidth(), 0);
+ const Vector<int> lineBreak(0, font.CharHeight() + font.CharHeight() / 2);
+ Vector<int> lineHead(offset);
+
+ font.DrawString(Res().capsuleNameLabel, screen, lineHead, 5);
+ font.DrawChar(':', screen, lineHead + delimiterOffset);
+ font.DrawString(capsule.Name(), screen, lineHead + valueOffset);
+
+ lineHead += lineBreak;
+ font.DrawString(Res().capsuleClassLabel, screen, lineHead, 5);
+ font.DrawChar(':', screen, lineHead + delimiterOffset);
+ font.DrawString(capsule.ClassName(), screen, lineHead + valueOffset);
+
+ lineHead += lineBreak;
+ font.DrawString(Res().capsuleAlignmentLabel, screen, lineHead, 5);
+ font.DrawChar(':', screen, lineHead + delimiterOffset);
+ font.DrawString(capsule.Alignment(), screen, lineHead + valueOffset);
+
+ lineHead += lineBreak;
+ font.DrawString(Res().capsuleTribeLabel, screen, lineHead, 5);
+ font.DrawChar(':', screen, lineHead + delimiterOffset);
+ font.DrawString(capsule.Tribe(), screen, lineHead + valueOffset);
+
+ lineHead += lineBreak;
+ font.DrawString(Res().capsuleAttack1Label, screen, lineHead, 5);
+ font.DrawChar(':', screen, lineHead + delimiterOffset);
+ if (capsule.Attack1()) {
+ font.DrawString(capsule.Attack1()->Name(), screen, lineHead + valueOffset);
+ } else {
+ font.DrawString(Res().capsuleNoAttackText, screen, lineHead + valueOffset);
+ }
+
+ lineHead += lineBreak;
+ font.DrawString(Res().capsuleAttack1Label, screen, lineHead, 5);
+ font.DrawChar(':', screen, lineHead + delimiterOffset);
+ if (capsule.Attack2()) {
+ font.DrawString(capsule.Attack2()->Name(), screen, lineHead + valueOffset);
+ } else {
+ font.DrawString(Res().capsuleNoAttackText, screen, lineHead + valueOffset);
+ }
+
+ lineHead += lineBreak;
+ font.DrawString(Res().capsuleAttack1Label, screen, lineHead, 5);
+ font.DrawChar(':', screen, lineHead + delimiterOffset);
+ if (capsule.Attack3()) {
+ font.DrawString(capsule.Attack3()->Name(), screen, lineHead + valueOffset);
+ } else {
+ font.DrawString(Res().capsuleNoAttackText, screen, lineHead + valueOffset);
+ }
+}
+
+void CapsuleMenu::RenderWheel(SDL_Surface *screen, const Vector<int> &offset) const {
+ // later
+}
+
+void CapsuleMenu::RenderStats(SDL_Surface *screen, const Vector<int> &offset) const {
+ const Capsule &capsule(GetCapsule());
+ Stats stats(capsule.GetStats());
+ const Font &font(*Res().statusFont);
+ const Vector<int> lineBreak(0, font.CharHeight());
+ Vector<int> lineHead(offset);
+
+ RenderStatsLine(screen, lineHead, Res().hpLabel, capsule.MaxHealth());
+ lineHead += lineBreak;
+ RenderStatsLine(screen, lineHead, Res().atpLabel, stats.Attack());
+ lineHead += lineBreak;
+ RenderStatsLine(screen, lineHead, Res().dfpLabel, stats.Defense());
+ lineHead += lineBreak;
+ RenderStatsLine(screen, lineHead, Res().strLabel, stats.Strength());
+ lineHead += lineBreak;
+ RenderStatsLine(screen, lineHead, Res().aglLabel, stats.Agility());
+ lineHead += lineBreak;
+ RenderStatsLine(screen, lineHead, Res().intLabel, stats.Intelligence());
+ lineHead += lineBreak;
+ RenderStatsLine(screen, lineHead, Res().gutLabel, stats.Gut());
+ lineHead += lineBreak;
+ RenderStatsLine(screen, lineHead, Res().mgrLabel, stats.MagicResistance());
+
+ lineHead = offset + Vector<int>(18 * font.CharWidth(), 0);
+ font.DrawStringRight(Res().levelLabel, screen, lineHead);
+ lineHead += lineBreak;
+ font.DrawNumberRight(capsule.Level(), screen, lineHead);
+ lineHead += 2 * lineBreak;
+ font.DrawStringRight(Res().experienceLabel, screen, lineHead);
+ lineHead += lineBreak;
+ font.DrawNumberRight(capsule.Experience(), screen, lineHead);
+ lineHead += 2 * lineBreak;
+ font.DrawStringRight(Res().nextLevelLabel, screen, lineHead);
+ lineHead += lineBreak;
+ font.DrawNumberRight(capsule.NextLevel(), screen, lineHead);
+}
+
+void CapsuleMenu::RenderStatsLine(SDL_Surface *screen, const geometry::Vector<int> &offset, const char *name, int value) const {
+ const Font &font(*Res().statusFont);
+ const Vector<int> numberOffset(4 * font.CharWidth(), 0);
+
+ font.DrawString(name, screen, offset, 4);
+ font.DrawNumber(value, screen, offset + numberOffset, 3);
+}
+
+void CapsuleMenu::RenderMenu(SDL_Surface *screen, const Vector<int> &offset) const {
+ const Font &font(*Res().normalFont);
+ const Frame &frame(*Res().statusFrame);
+ const Vector<int> menuOffset(3 * font.CharWidth(), font.CharHeight());
+
+ frame.Draw(screen, offset, 30 * font.CharWidth(), 3 * font.CharHeight());
+ menu.Draw(screen, offset + menuOffset);
+}
+
+
+common::GameConfig &CapsuleMenu::Game() {
+ return parent->Game();
+}
+
+const common::GameConfig &CapsuleMenu::Game() const {
+ return parent->Game();
+}
+
+Resources &CapsuleMenu::Res() {
+ return parent->Res();
+}
+
+const Resources &CapsuleMenu::Res() const {
+ return parent->Res();
+}
+
+
+int CapsuleMenu::Width() const {
+ return parent->Width();
+}
+
+int CapsuleMenu::Height() const {
+ return parent->Height();
+}
+
+const Capsule &CapsuleMenu::GetCapsule() const {
+ return *Game().state->capsule;
+}
+
+}
--- /dev/null
+#ifndef MENU_CAPSULEMENU_H_
+#define MENU_CAPSULEMENU_H_
+
+#include "fwd.h"
+#include "../app/State.h"
+#include "../common/fwd.h"
+#include "../geometry/Vector.h"
+#include "../graphics/Menu.h"
+
+namespace menu {
+
+class CapsuleMenu
+: public app::State {
+
+public:
+ explicit CapsuleMenu(PartyMenu *parent);
+
+public:
+ virtual void HandleEvents(const app::Input &);
+ virtual void UpdateWorld(float deltaT);
+ virtual void Render(SDL_Surface *);
+
+ void RenderBackground(SDL_Surface *screen) const;
+ void RenderCapsule(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+ void RenderInfo(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+ void RenderWheel(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+ void RenderStats(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+ void RenderStatsLine(SDL_Surface *screen, const geometry::Vector<int> &offset, const char *name, int value) const;
+ void RenderMenu(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+
+public:
+ common::GameConfig &Game();
+ const common::GameConfig &Game() const;
+ Resources &Res();
+ const Resources &Res() const;
+ const common::Capsule &GetCapsule() const;
+
+ 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);
+
+private:
+ PartyMenu *parent;
+ enum Choice {
+ CHOICE_FEED,
+ CHOICE_CHANGE,
+ CHOICE_NAME,
+ };
+ graphics::Menu<Choice> menu;
+
+};
+
+}
+
+#endif /* MENU_CAPSULEMENU_H_ */
--- /dev/null
+#include "CapsuleNameMenu.h"
+
+#include "CapsuleMenu.h"
+#include "Resources.h"
+#include "../app/Application.h"
+#include "../app/Input.h"
+#include "../common/Inventory.h"
+#include "../common/Item.h"
+#include "../common/GameConfig.h"
+#include "../common/GameState.h"
+#include "../graphics/Font.h"
+#include "../graphics/Frame.h"
+
+#include <cstring>
+
+using app::Input;
+using common::Capsule;
+using common::Inventory;
+using common::Item;
+using geometry::Vector;
+using graphics::Font;
+using graphics::Frame;
+
+namespace menu {
+
+CapsuleNameMenu::CapsuleNameMenu(CapsuleMenu *parent)
+: parent(parent)
+, cursor(5) {
+ std::strncpy(buffer, GetCapsule().Name(), 6);
+ buffer[5] = '\0';
+}
+
+
+void CapsuleNameMenu::OnEnterState(SDL_Surface *) {
+
+}
+
+void CapsuleNameMenu::OnExitState(SDL_Surface *) {
+
+}
+
+void CapsuleNameMenu::OnResumeState(SDL_Surface *) {
+
+}
+
+void CapsuleNameMenu::OnPauseState(SDL_Surface *) {
+
+}
+
+
+void CapsuleNameMenu::OnResize(int width, int height) {
+
+}
+
+
+void CapsuleNameMenu::HandleEvents(const Input &input) {
+ if (input.JustPressed(Input::START)) {
+ Ctrl().PopState();
+ }
+}
+
+void CapsuleNameMenu::UpdateWorld(float deltaT) {
+
+}
+
+void CapsuleNameMenu::Render(SDL_Surface *screen) {
+ const Font &font(*parent->Res().statusFont);
+ const Vector<int> offset((screen->w - Width()) / 2, (screen->h - Height()) / 2);
+ const Vector<int> nameOffset(
+ 4 * font.CharWidth(),
+ 4 * font.CharHeight() - font.CharHeight() / 8);
+ const Vector<int> alphaOffset(
+ 4 * font.CharWidth(),
+ 7 * font.CharHeight() - font.CharHeight() / 8);
+
+ parent->RenderBackground(screen);
+ RenderName(screen, offset + nameOffset);
+ RenderAlphabet(screen, offset + alphaOffset);
+}
+
+void CapsuleNameMenu::RenderName(SDL_Surface *screen, const Vector<int> &offset) const {
+ const Font &font(*parent->Res().normalFont);
+ const Frame &frame(*parent->Res().statusFrame);
+ const Vector<int> labelOffset(2 * font.CharWidth(), font.CharHeight());
+ const Vector<int> namebgOffset(8 * font.CharWidth(), 0);
+ const Vector<int> nameOffset(13 * font.CharWidth(), font.CharHeight());
+
+ frame.Draw(screen, offset, 8 * font.CharWidth(), 3 * font.CharHeight());
+ font.DrawString(parent->Res().capsuleNameLabel, screen, offset + labelOffset, 5);
+ frame.Draw(screen, offset + namebgOffset, 16 * font.CharWidth(), 3 * font.CharHeight());
+ font.DrawString(buffer, screen, offset + nameOffset, 5);
+}
+
+void CapsuleNameMenu::RenderAlphabet(SDL_Surface *screen, const Vector<int> &offset) const {
+ const Font &font(*parent->Res().normalFont);
+ const Frame &frame(*parent->Res().statusFrame);
+
+ frame.Draw(screen, offset, 24 * font.CharWidth(), 17 * font.CharHeight());
+}
+
+
+int CapsuleNameMenu::Width() const {
+ return parent->Width();
+}
+
+int CapsuleNameMenu::Height() const {
+ return parent->Height();
+}
+
+const Capsule &CapsuleNameMenu::GetCapsule() const {
+ return parent->GetCapsule();
+}
+
+}
--- /dev/null
+#ifndef MENU_CAPSULENAMEMENU_H_
+#define MENU_CAPSULENAMEMENU_H_
+
+#include "fwd.h"
+#include "../app/State.h"
+#include "../common/fwd.h"
+#include "../geometry/Vector.h"
+
+namespace menu {
+
+class CapsuleMenu;
+
+class CapsuleNameMenu
+: public app::State {
+
+public:
+ explicit CapsuleNameMenu(CapsuleMenu *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);
+
+ const common::Capsule &GetCapsule() const;
+
+ void LoadInventory();
+
+ void RenderName(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+ void RenderAlphabet(SDL_Surface *screen, const geometry::Vector<int> &offset) const;
+
+private:
+ CapsuleMenu *parent;
+ int cursor;
+ char buffer[6];
+
+};
+
+}
+
+#endif /* MENU_CAPSULENAMEMENU_H_ */
#include "PartyMenu.h"
#include "Resources.h"
+#include "../app/Application.h"
+#include "../app/Input.h"
#include "../common/GameConfig.h"
#include "../common/GameState.h"
#include "../graphics/Font.h"
#include "PartyMenu.h"
#include "Resources.h"
+#include "../app/Application.h"
#include "../app/Input.h"
#include "../common/GameConfig.h"
#include "../common/GameState.h"
#include "PartyMenu.h"
+#include "CapsuleMenu.h"
#include "ChangeHero.h"
#include "ConfigMenu.h"
#include "EquipMenu.h"
mainMenu.Add(Res().mainMenuStatusText, 4);
mainMenu.Add(Res().mainMenuSpellText, 1);
mainMenu.Add(Res().mainMenuChangeText, 5);
- mainMenu.Add(Res().mainMenuCapsuleText, 2);
+ mainMenu.Add(Res().mainMenuCapsuleText, 2, game->state->capsule);
mainMenu.Add(Res().mainMenuConfigText, 6);
mainMenu.Add(Res().mainMenuEquipmentText, 3);
mainMenu.Add(Res().mainMenuScenarioText, 7);
Ctrl().PushState(new SelectHero(this, this, this, OnSpellSelect));
break;
case MENU_ITEM_CAPSULE:
+ if (game->state->capsule) {
+ Ctrl().PushState(new CapsuleMenu(this));
+ } else {
+ // noise and blur
+ }
break;
case MENU_ITEM_EQUIP:
Ctrl().PushState(new SelectHero(this, this, this, OnEquipSelect));
, gutLabel(0)
, mgrLabel(0)
+, hpLabel(0)
, ipLabel(0)
+, levelLabel(0)
, experienceLabel(0)
, nextLevelLabel(0)
, itemMenuUseText(0)
, itemMenuSortText(0)
, itemMenuDropText(0)
+, itemMenuSelectText(0)
, inventoryMenuProperties(0)
, scenarioMenuProperties(0)
, scenarioMenuHeadline(0)
+, capsulebg(0)
+
+, capsuleMenuProperties(0)
+, capsuleFeedMenuProperties(0)
+
+, capsuleFeedLabel(0)
+, capsuleChangeLabel(0)
+, capsuleNameLabel(0)
+, capsuleClassLabel(0)
+, capsuleAlignmentLabel(0)
+, capsuleTribeLabel(0)
+, capsuleAttack1Label(0)
+, capsuleAttack2Label(0)
+, capsuleAttack3Label(0)
+, capsuleNoAttackText(0)
+, capsuleNotHungryText(0)
+
{ }
td.AddField("gutLabel", FieldDescription(((char *)&r.gutLabel) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
td.AddField("mgrLabel", FieldDescription(((char *)&r.mgrLabel) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("hpLabel", FieldDescription(((char *)&r.hpLabel) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
td.AddField("ipLabel", FieldDescription(((char *)&r.ipLabel) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("levelLabel", FieldDescription(((char *)&r.levelLabel) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
td.AddField("experienceLabel", FieldDescription(((char *)&r.experienceLabel) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
td.AddField("nextLevelLabel", FieldDescription(((char *)&r.nextLevelLabel) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
td.AddField("itemMenuUseText", FieldDescription(((char *)&r.itemMenuUseText) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
td.AddField("itemMenuSortText", FieldDescription(((char *)&r.itemMenuSortText) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
td.AddField("itemMenuDropText", FieldDescription(((char *)&r.itemMenuDropText) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("itemMenuSelectText", FieldDescription(((char *)&r.itemMenuSelectText) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
td.AddField("inventoryMenu", FieldDescription(((char *)&r.inventoryMenuProperties) - ((char *)&r), MenuProperties::TYPE_ID).SetReferenced().SetDescription("properties of the inventory menu"));
td.AddField("spellMenu", FieldDescription(((char *)&r.spellMenuProperties) - ((char *)&r), MenuProperties::TYPE_ID).SetReferenced().SetDescription("properties of the spell menu"));
td.AddField("scenarioMenu", FieldDescription(((char *)&r.scenarioMenuProperties) - ((char *)&r), MenuProperties::TYPE_ID).SetReferenced().SetDescription("properties of the scenario menu"));
td.AddField("scenarioMenuHeadline", FieldDescription(((char *)&r.scenarioMenuHeadline) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
+
+ td.AddField("capsulebg", FieldDescription(((char *)&r.capsulebg) - ((char *)&r), Texture::TYPE_ID).SetReferenced().SetDescription("background texture for the capsule menus"));
+
+ td.AddField("capsuleMenu", FieldDescription(((char *)&r.capsuleMenuProperties) - ((char *)&r), MenuProperties::TYPE_ID).SetReferenced().SetDescription("properties of the capsule main menu (the bottom bar)"));
+ td.AddField("capsuleFeedMenu", FieldDescription(((char *)&r.capsuleFeedMenuProperties) - ((char *)&r), MenuProperties::TYPE_ID).SetReferenced().SetDescription("properties of the capsule feed menu (above the inventory)"));
+
+ td.AddField("capsuleFeedLabel", FieldDescription(((char *)&r.capsuleFeedLabel) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("capsuleChangeLabel", FieldDescription(((char *)&r.capsuleChangeLabel) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("capsuleNameLabel", FieldDescription(((char *)&r.capsuleNameLabel) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("capsuleClassLabel", FieldDescription(((char *)&r.capsuleClassLabel) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("capsuleAlignmentLabel", FieldDescription(((char *)&r.capsuleAlignmentLabel) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("capsuleTribeLabel", FieldDescription(((char *)&r.capsuleTribeLabel) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("capsuleAttack1Label", FieldDescription(((char *)&r.capsuleAttack1Label) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("capsuleAttack2Label", FieldDescription(((char *)&r.capsuleAttack2Label) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("capsuleAttack3Label", FieldDescription(((char *)&r.capsuleAttack3Label) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("capsuleNoAttackText", FieldDescription(((char *)&r.capsuleNoAttackText) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("capsuleNotHungryText", FieldDescription(((char *)&r.capsuleNotHungryText) - ((char *)&r), Interpreter::STRING_ID).SetReferenced());
}
void Resources::Construct(void *data) {
const char *gutLabel;
const char *mgrLabel;
+ const char *hpLabel;
const char *ipLabel;
+ const char *levelLabel;
const char *experienceLabel;
const char *nextLevelLabel;
const char *itemMenuUseText;
const char *itemMenuSortText;
const char *itemMenuDropText;
+ const char *itemMenuSelectText;
graphics::MenuProperties *inventoryMenuProperties;
graphics::MenuProperties *scenarioMenuProperties;
const char *scenarioMenuHeadline;
+ graphics::Texture *capsulebg;
+
+ graphics::MenuProperties *capsuleMenuProperties;
+ graphics::MenuProperties *capsuleFeedMenuProperties;
+
+ const char *capsuleFeedLabel;
+ const char *capsuleChangeLabel;
+ const char *capsuleNameLabel;
+ const char *capsuleClassLabel;
+ const char *capsuleAlignmentLabel;
+ const char *capsuleTribeLabel;
+ const char *capsuleAttack1Label;
+ const char *capsuleAttack2Label;
+ const char *capsuleAttack3Label;
+ const char *capsuleNoAttackText;
+ const char *capsuleNotHungryText;
+
Resources();
static void CreateTypeDescription();
#include "PartyMenu.h"
#include "Resources.h"
+#include "../app/Application.h"
#include "../app/Input.h"
#include "../common/GameConfig.h"
#include "../common/GameState.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"
namespace menu {
+class CapsuleChangeMenu;
+class CapsuleFeedMenu;
+class CapsuleMenu;
+class CapsuleNameMenu;
class ChangeHero;
class ConfigMenu;
class EquipMenu;
--- /dev/null
+Capsule flash
--- /dev/null
+Sprite flashSprite {
+ image: :"flash.png",
+ size: <96, 96>
+}
+
+export Capsule flash {
+ name: "Flash",
+ alignment: "LIGHT",
+ maxHealth: 5,
+ stats: Stats {
+ atp: 12,
+ dfp: 18,
+ str: 2,
+ agl: 11,
+ int: 16,
+ gut: 23,
+ mgr: 11
+ },
+ classes: [ CapsuleClass
+ {
+ name: "4",
+ tribe: "Twinkle",
+ battleSprite: flashSprite,
+ meleeAnimation: SimpleAnimation {
+ sprite: Sprite {
+ image: :"melee-maxim.png",
+ size: <96,96>
+ },
+ frametime: 66, // two "frames"
+ framecount: 4
+ },
+ attackAnimation: ComplexAnimation {
+ sprite: flashSprite,
+ frametime: fourFramesTime,
+ repeat: false,
+ frames:
+ [ ComplexAnimationFrame
+ { column: 0, row: 1, disposition: < 0, -16> },
+ { column: 0, row: 0, disposition: < 0, -16> },
+ { column: 0, row: 1, disposition: < 0, -16> },
+ { column: 0, row: 0, disposition: < 0, -16> }
+ ]
+ },
+ healthBoost: 208,
+ statBoost: Stats {
+ atp: 38,
+ dfp: 71,
+ str: 24,
+ agl: 78,
+ int: 195,
+ gut: 12, // TODO: this is probably higher, but clipped at 199
+ mgr: 135
+ }
+ }
+ ]
+}
< 48,136>,
<128,136>,
< 80,152>,
- <160,152>
+ <160,152>,
+ <216,144>
]
}
colGap: 32,
cursor: menuCursor,
font: menuFont,
+ disabledFont: menuInactiveFont,
wrapX: true,
wrapY: true
},
intLabel: "INT",
gutLabel: "GUT",
mgrLabel: "MGR",
+ hpLabel: "HP",
ipLabel: "IP",
+ levelLabel: "LEVEL",
experienceLabel: "NOW EXP",
nextLevelLabel: "NEXT LEVEL",
statusMenu: MenuProperties {
itemMenuUseText: "USE",
itemMenuSortText: "SORT",
itemMenuDropText: "DROP",
+ itemMenuSelectText: "SELECT",
inventoryMenu: MenuProperties {
cols: 1,
rows: 6,
disabledFont: menuInactiveFont,
iconSpace: 16,
charsPerNumber: 2,
- delimiter: ":"
+ delimiter: ":",
+ thirdColumnHack: 1
},
spellMenu: MenuProperties {
cols: 2,
cursor: menuCursor,
font: menuFont
},
- scenarioMenuHeadline: "SCENARIO ITEM"
+ scenarioMenuHeadline: "SCENARIO ITEM",
+ capsulebg: Texture {
+ image: :"capsulebg.png",
+ size: <64, 64>
+ },
+ capsuleMenu: MenuProperties {
+ cols: 3,
+ rows: 1,
+ charsPerEntry: 7,
+ cursor: menuCursor,
+ selectedCursor: menuActiveCursor,
+ font: menuFont,
+ thirdColumnHack: 2
+ },
+ capsuleFeedMenu: MenuProperties {
+ cols: 2,
+ rows: 1,
+ charsPerEntry: 7,
+ colGap: 32,
+ cursor: menuCursor,
+ selectedCursor: menuActiveCursor,
+ font: menuFont
+ },
+ capsuleFeedLabel: "FEED",
+ capsuleChangeLabel: "CHANGE",
+ capsuleNameLabel: "NAME",
+ capsuleClassLabel: "CLASS",
+ capsuleAlignmentLabel: "ALI.",
+ capsuleTribeLabel: "TRIBE",
+ capsuleAttack1Label: "SP.1",
+ capsuleAttack2Label: "SP.2",
+ capsuleAttack3Label: "SP.3",
+ capsuleNoAttackText: "Nothing",
+ capsuleNotHungryText: "I'm not hungry."
}