]> git.localhorst.tv Git - l2e.git/commitdiff
extracted battle logic into a class
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Sun, 3 Feb 2013 14:29:47 +0000 (15:29 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Sun, 3 Feb 2013 14:29:47 +0000 (15:29 +0100)
fixes #35

29 files changed:
src/battle/AttackChoice.h
src/battle/Battle.cpp [new file with mode: 0644]
src/battle/Battle.h [new file with mode: 0644]
src/battle/BattleState.cpp
src/battle/BattleState.h
src/battle/Capsule.cpp
src/battle/Capsule.h
src/battle/SmallHeroTag.cpp
src/battle/TargetSelection.cpp
src/battle/TargetSelection.h
src/battle/states/PerformAttacks.cpp
src/battle/states/PerformAttacks.h
src/battle/states/SelectAttackType.cpp
src/battle/states/SelectAttackType.h
src/battle/states/SelectIkari.cpp
src/battle/states/SelectIkari.h
src/battle/states/SelectItem.cpp
src/battle/states/SelectItem.h
src/battle/states/SelectMoveAction.cpp
src/battle/states/SelectMoveAction.h
src/battle/states/SelectSpell.cpp
src/battle/states/SelectSpell.h
src/battle/states/SelectTarget.cpp
src/battle/states/SelectTarget.h
src/battle/states/SwapHeroes.cpp
src/battle/states/SwapHeroes.h
src/common/Hero.h
tests/battle/BattleTest.cpp [new file with mode: 0644]
tests/battle/BattleTest.h [new file with mode: 0644]

index 1da0cdcde3a5b0a0e1f7b3969a384640f5de35a4..0049f1e2be198bd068713be96d26be3ddf124646 100644 (file)
@@ -1,6 +1,9 @@
 #ifndef BATTLE_ATTACKCHOICE_H_
 #define BATTLE_ATTACKCHOICE_H_
 
+namespace battle {
+       class Battle;
+}
 namespace common {
        class Item;
        class Spell;
@@ -23,7 +26,10 @@ public:
        };
 
 public:
-       explicit AttackChoice(BattleState *b = 0) : thing(0), selection(b), type(UNDECIDED) { }
+       AttackChoice()
+       : thing(0), selection(), type(UNDECIDED) { }
+       explicit AttackChoice(Battle *b)
+       : thing(0), selection(b), type(UNDECIDED) { }
        ~AttackChoice() { }
 
 public:
diff --git a/src/battle/Battle.cpp b/src/battle/Battle.cpp
new file mode 100644 (file)
index 0000000..7bfa940
--- /dev/null
@@ -0,0 +1,385 @@
+#include "Battle.h"
+
+#include "AttackChoice.h"
+#include "PartyLayout.h"
+#include "TargetSelection.h"
+#include "../common/Stats.h"
+
+#include <cassert>
+#include <stdexcept>
+
+using common::Stats;
+
+
+namespace battle {
+
+Battle::Battle(
+               const PartyLayout *heroesLayout,
+               const PartyLayout *monstersLayout)
+: heroesLayout(heroesLayout)
+, monstersLayout(monstersLayout)
+, activeHero(-1)
+, attackCursor(-1)
+, expReward(0)
+, goldReward(0) {
+       if (!heroesLayout) {
+               throw std::invalid_argument("construct battle without heroes layout");
+       }
+       if (!monstersLayout) {
+               throw std::invalid_argument("construct battle without monsters layout");
+       }
+       heroes.reserve(4);
+       monsters.resize(monstersLayout->NumPositions());
+}
+
+
+void Battle::AddHero(const Hero &h) {
+       if (NumHeroes() == MaxHeroes()) {
+               throw std::overflow_error("max heroes breached");
+       }
+       heroes.push_back(h);
+       heroes.back().GetAttackChoice() = AttackChoice(this);
+}
+
+void Battle::AddMonster(const Monster &m) {
+       for (int i = 0; i < MaxMonsters(); ++i) {
+               if (!MonsterPositionOccupied(i)) {
+                       MonsterAt(i) = m;
+                       MonsterAt(i).GetAttackChoice() = AttackChoice(this);
+                       return;
+               }
+       }
+       throw std::overflow_error("all monster positions occupied");
+}
+
+void Battle::SetCapsule(const Capsule &c) {
+       capsule = c;
+}
+
+
+int Battle::NumHeroes() const {
+       return heroes.size();
+}
+
+int Battle::MaxHeroes() const {
+       return heroesLayout->NumPositions() - 1;
+}
+
+int Battle::NumMonsters() const {
+       return monsters.size();
+}
+
+int Battle::MaxMonsters() const {
+       return monstersLayout->NumPositions();
+}
+
+bool Battle::HasCapsule() const {
+       return capsule.Active();
+}
+
+
+Hero &Battle::HeroAt(int index) {
+       assert(index >= 0 && index < NumHeroes() && "hero index out of bounds");
+       return heroes[index];
+}
+
+const Hero &Battle::HeroAt(int index) const {
+       assert(index >= 0 && index < NumHeroes() && "hero index out of bounds");
+       return heroes[index];
+}
+
+
+Monster &Battle::MonsterAt(int index) {
+       assert(index >= 0 && index < NumMonsters() && "monster index out of bounds");
+       return monsters[index];
+}
+
+const Monster &Battle::MonsterAt(int index) const {
+       assert(index >= 0 && index < NumMonsters() && "monster index out of bounds");
+       return monsters[index];
+}
+
+bool Battle::HeroPositionOccupied(int index) const {
+       return index >= 0 && index < NumHeroes();
+}
+
+bool Battle::HeroAlive(int index) const {
+       return HeroPositionOccupied(index) && HeroAt(index).Health() > 0;
+}
+
+
+bool Battle::MonsterPositionOccupied(int index) const {
+       return MonsterAlive(index);
+}
+
+bool Battle::MonsterAlive(int index) const {
+       return index >= 0 && index < NumMonsters() && MonsterAt(index).Health() > 0;
+}
+
+bool Battle::CapsuleAlive() const {
+       return capsule.Active() && capsule.Health() > 0;
+}
+
+
+void Battle::NextHero() {
+       ++activeHero;
+       while (activeHero < NumHeroes() && HeroAt(activeHero).Health() == 0) {
+               ++activeHero;
+       }
+}
+
+void Battle::PreviousHero() {
+       --activeHero;
+       while (activeHero >= 0 && HeroAt(activeHero).Health() == 0) {
+               --activeHero;
+       }
+}
+
+void Battle::SwapHeroes(int lhs, int rhs) {
+       if (lhs < 0 || lhs >= NumHeroes() || rhs < 0 || rhs >= NumHeroes() || lhs == rhs) return;
+       std::swap(HeroAt(lhs), HeroAt(rhs));
+}
+
+bool Battle::HasChosenAttackType() const {
+       return ActiveHero().GetAttackChoice().GetType() != AttackChoice::UNDECIDED;
+}
+
+bool Battle::AttackSelectionDone() const {
+       return activeHero >= NumHeroes();
+}
+
+
+class OrderCompare {
+public:
+       OrderCompare(Battle *battle) : battle(battle) { }
+       bool operator ()(const Battle::Order &lhs, const Battle::Order &rhs) {
+               return lhs.GetStats(*battle).Agility() > rhs.GetStats(*battle).Agility();
+       }
+private:
+       Battle *battle;
+};
+
+
+void Battle::CalculateAttackOrder() {
+       attackOrder.reserve(NumMonsters() + NumHeroes() + 1);
+       for (int i(0); i < NumHeroes(); ++i) {
+               attackOrder.push_back(Order(Order::HERO, i));
+       }
+       for (std::vector<Monster>::size_type i(0), end(NumMonsters()); i < end; ++i) {
+               attackOrder.push_back(Order(Order::MONSTER, i));
+               MonsterAt(i).GetAttackChoice() = AttackChoice(this);
+       }
+       if (GetCapsule().Active() && GetCapsule().Health() > 0) {
+               attackOrder.push_back(Order(Order::CAPSULE));
+       }
+       std::sort(attackOrder.begin(), attackOrder.end(), OrderCompare(this));
+}
+
+void Battle::NextAttack() {
+       if (Victory() || Defeat()) {
+               attackCursor = attackOrder.size();
+               return;
+       }
+       ++attackCursor;
+       while (attackCursor < int(attackOrder.size())) {
+               if (attackOrder[attackCursor].IsMonster()) {
+                       if (MonsterAlive(attackOrder[attackCursor].index)) break;
+               } else if (attackOrder[attackCursor].IsHero()) {
+                       if (HeroAlive(attackOrder[attackCursor].index)) break;
+               } else {
+                       if (CapsuleAlive()) break;
+               }
+               ++attackCursor;
+       }
+}
+
+bool Battle::AttacksFinished() const {
+       return attackCursor >= int(attackOrder.size())
+                       || Victory() || Defeat();
+}
+
+void Battle::CalculateDamage() {
+       if (CurrentAttack().IsMonster()) {
+               DecideMonsterAttack(MonsterAt(CurrentAttack().index));
+       } else if (CurrentAttack().IsCapsule()) {
+               DecideCapsuleAttack();
+       }
+       AttackChoice &ac = CurrentAttack().GetAttackChoice(*this);
+       if (ac.GetType() == AttackChoice::DEFEND) return;
+       TargetSelection &ts(ac.Selection());
+
+       const Stats &attackerStats = CurrentAttack().GetStats(*this);
+       CalculateDamage(attackerStats, ts);
+}
+
+AttackChoice &Battle::Order::GetAttackChoice(Battle &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 Battle::Order::GetAttackChoice()");
+       }
+}
+
+Stats &Battle::Order::GetStats(Battle &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 Battle::ApplyDamage() {
+       if (attackCursor < 0) return;
+       AttackChoice &ac = CurrentAttack().GetAttackChoice(*this);
+       TargetSelection &ts(ac.Selection());
+       if (ts.TargetsMonsters()) {
+               for (int i(0); i < NumMonsters(); ++i) {
+                       Monster &monster(MonsterAt(i));
+                       if (ts.IsBad(i)) {
+                               monster.SubtractHealth(ts.GetAmount(i));
+                               if (monster.Health() == 0) {
+                                       expReward += monster.ExpReward();
+                                       goldReward += monster.GoldReward();
+                               }
+                       }
+               }
+       } else {
+               for (int i(0); i < NumHeroes(); ++i) {
+                       Hero &hero(HeroAt(i));
+                       if (ts.IsBad(i)) {
+                               hero.SubtractHealth(ts.GetAmount(i));
+                       }
+               }
+       }
+}
+
+const Battle::Order &Battle::CurrentAttack() const {
+       assert(attackCursor >= 0 && attackCursor < int(attackOrder.size()));
+       return attackOrder[attackCursor];
+}
+
+AttackChoice &Battle::CurrentAttackAttackChoice() {
+       return CurrentAttack().GetAttackChoice(*this);
+}
+
+void Battle::ClearAllAttacks() {
+       attackCursor = -1;
+       activeHero = -1;
+       for (int i(0); i < NumHeroes(); ++i) {
+               HeroAt(i).GetAttackChoice() = AttackChoice(this);
+       }
+       for (int i(0); i < NumMonsters(); ++i) {
+               MonsterAt(i).GetAttackChoice() = AttackChoice(this);
+       }
+       GetCapsule().GetAttackChoice() = AttackChoice(this);
+       attackOrder.clear();
+}
+
+
+void Battle::DecideMonsterAttack(Monster &m) {
+       AttackChoice &ac(m.GetAttackChoice());
+       TargetSelection &ts(ac.Selection());
+       ac.Reset();
+       int target(rand() % NumHeroes());
+       while (!HeroAlive(target)) {
+               target = rand() % NumHeroes();
+       }
+       ac.SetType(AttackChoice::SWORD);
+       ts.SelectHeroes();
+       ts.SetSingle();
+       ts.Select(target);
+}
+
+void Battle::DecideCapsuleAttack() {
+       AttackChoice &ac(GetCapsule().GetAttackChoice());
+       TargetSelection &ts(ac.Selection());
+       ac.Reset();
+       int target(rand() % NumMonsters());
+       while (!MonsterAlive(target)) {
+               target = rand() % NumMonsters();
+       }
+       ac.SetType(AttackChoice::SWORD);
+       ts.SelectMonsters();
+       ts.SetSingle();
+       ts.Select(target);
+}
+
+void Battle::CalculateDamage(const Stats &attackerStats, TargetSelection &ts) const {
+       bool hitSome(false);
+       if (ts.TargetsMonsters()) {
+               for (int i(0); i < MaxMonsters(); ++i) {
+                       if (ts.IsSelected(i)) {
+                               if (MonsterAt(i).Health() > 0) {
+                                       const Stats &defenderStats(MonsterAt(i).GetStats());
+                                       Uint16 damage(CalculateDamage(attackerStats, defenderStats));
+                                       ts.SetBad(i, damage);
+                                       hitSome = true;
+                               } else {
+                                       ts.Unselect(i);
+                               }
+                       }
+               }
+               if (hitSome) return;
+               for (int i(0); i < MaxMonsters(); ++i) {
+                       if (MonsterAt(i).Health() > 0) {
+                               const Stats &defenderStats(MonsterAt(i).GetStats());
+                               Uint16 damage(CalculateDamage(attackerStats, defenderStats));
+                               ts.SetBad(i, damage);
+                               break;
+                       }
+               }
+       } else {
+               for (int i(0); i < NumHeroes(); ++i) {
+                       if (ts.IsSelected(i)) {
+                               if (HeroAt(i).Health() > 0) {
+                                       const Stats &defenderStats(HeroAt(i).GetStats());
+                                       Uint16 damage(CalculateDamage(attackerStats, defenderStats));
+                                       ts.SetBad(i, damage);
+                                       hitSome = true;
+                               } else {
+                                       ts.Unselect(i);
+                               }
+                       }
+               }
+               if (hitSome) return;
+               for (int i(0); i < NumHeroes(); ++i) {
+                       if (HeroAt(i).Health() > 0) {
+                               const Stats &defenderStats(HeroAt(i).GetStats());
+                               Uint16 damage(CalculateDamage(attackerStats, defenderStats));
+                               ts.SetBad(i, damage);
+                               break;
+                       }
+               }
+       }
+}
+
+Uint16 Battle::CalculateDamage(const Stats &attacker, const Stats &defender) const {
+       return attacker.Attack() / 2 - defender.Defense() / 4;
+}
+
+
+bool Battle::Victory() const {
+       for (int i(0); i < NumMonsters(); ++i) {
+               if (MonsterAlive(i)) return false;
+       }
+       return true;
+}
+
+bool Battle::Defeat() const {
+       for (int i(0); i < NumHeroes(); ++i) {
+               if (HeroAlive(i)) return false;
+       }
+       return true;
+}
+
+}
diff --git a/src/battle/Battle.h b/src/battle/Battle.h
new file mode 100644 (file)
index 0000000..e7cf8a2
--- /dev/null
@@ -0,0 +1,129 @@
+#ifndef BATTLE_BATTLE_H_
+#define BATTLE_BATTLE_H_
+
+namespace battle {
+       class Hero;
+       class Monster;
+       class PartyLayout;
+       class TargetSelection;
+}
+namespace common {
+       class Stats;
+}
+
+#include "Capsule.h"
+#include "Hero.h"
+#include "Monster.h"
+
+#include <vector>
+#include <SDL.h>
+
+
+namespace battle {
+
+/// This class models a battle between a party of monsters and one
+/// of heroes.
+/// See http://luke.redirectme.net/redmine/projects/l2e/wiki/Battle for
+/// an explanation of how to use this interface.
+class Battle {
+
+public:
+       Battle(const PartyLayout *heroesLayout, const PartyLayout *monstersLayout);
+
+public:
+       void AddHero(const Hero &);
+       void AddMonster(const Monster &);
+       void SetCapsule(const Capsule &);
+
+       int NumHeroes() const;
+       int MaxHeroes() const;
+       int NumMonsters() const;
+       int MaxMonsters() const;
+       bool HasCapsule() const;
+
+       bool HeroPositionOccupied(int index) const;
+       bool HeroAlive(int index) const;
+       bool MonsterPositionOccupied(int index) const;
+       bool MonsterAlive(int index) const;
+       bool CapsuleAlive() const;
+
+       std::vector<Hero>::const_iterator HeroesBegin() const { return heroes.begin(); }
+       std::vector<Hero>::const_iterator HeroesEnd() const { return heroes.end(); }
+       Hero &HeroAt(int index);
+       const Hero &HeroAt(int index) const;
+
+       std::vector<Monster>::const_iterator MonstersBegin() const { return monsters.begin(); }
+       std::vector<Monster>::const_iterator MonstersEnd() const { return monsters.end(); }
+       Monster &MonsterAt(int index);
+       const Monster &MonsterAt(int index) const;
+
+       Capsule &GetCapsule() { return capsule; }
+       const Capsule &GetCapsule() const { return capsule; }
+
+       const PartyLayout &HeroesLayout() const { return *heroesLayout; }
+       const PartyLayout &MonstersLayout() const { return *monstersLayout; }
+
+       void NextHero();
+       bool BeforeFirstHero() const { return activeHero < 0; }
+       void PreviousHero();
+       void SwapHeroes(int lhs, int rhs);
+       Hero &ActiveHero() { return HeroAt(activeHero); }
+       const Hero &ActiveHero() const { return HeroAt(activeHero); }
+       bool IsActiveHero(int index) const { return index == activeHero; }
+       bool HasChosenAttackType() const;
+       bool AttackSelectionDone() const;
+
+       struct Order {
+               enum Performer {
+                       HERO,
+                       CAPSULE,
+                       MONSTER,
+               };
+               Order(Performer by, int index = 0)
+               : index(index), by(by) { }
+               AttackChoice &GetAttackChoice(Battle &) const;
+               common::Stats &GetStats(Battle &) const;
+               bool IsHero() const { return by == HERO; }
+               bool IsCapsule() const { return by == CAPSULE; }
+               bool IsMonster() const { return by == MONSTER; }
+               int index;
+               Performer by;
+       };
+
+       void CalculateAttackOrder();
+       void NextAttack();
+       bool AttacksFinished() const;
+       void CalculateDamage();
+       void ApplyDamage();
+       const Order &CurrentAttack() const;
+       AttackChoice &CurrentAttackAttackChoice();
+       void ClearAllAttacks();
+
+       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;
+
+       bool Victory() const;
+       bool Defeat() const;
+
+private:
+       const PartyLayout *heroesLayout;
+       const PartyLayout *monstersLayout;
+       std::vector<Hero> heroes;
+       std::vector<Monster> monsters;
+       Capsule capsule;
+
+       int activeHero;
+
+       std::vector<Order> attackOrder;
+       int attackCursor;
+
+       int expReward;
+       int goldReward;
+
+};
+
+}
+
+#endif
index 57ff3270d170daab546e8c51c461bcb6195b5ba3..a17732ca8c612c0c3bc810d46c73050e491e46d6 100644 (file)
@@ -34,41 +34,15 @@ using std::vector;
 namespace battle {
 
 void BattleState::AddMonster(const Monster &m) {
-       if (monsters.size() >= monstersLayout->NumPositions()) {
-               throw std::overflow_error("too many monsters for layout");
-       }
-       monsters.push_back(m);
+       battle.AddMonster(m);
 }
 
 void BattleState::AddHero(const Hero &h) {
-       if (numHeroes >= 4 || numHeroes >= (int)heroesLayout->NumPositions()) {
-               throw std::overflow_error("too many heroes for layout");
-       }
-       heroes[numHeroes] = h;
-       ++numHeroes;
+       battle.AddHero(h);
 }
 
 void BattleState::SetCapsule(const Capsule &c) {
-       capsule = c;
-}
-
-void BattleState::NextHero() {
-       ++activeHero;
-       while (activeHero < numHeroes && heroes[activeHero].Health() == 0) {
-               ++activeHero;
-       }
-}
-
-void BattleState::PreviousHero() {
-       --activeHero;
-       while (activeHero >= 0 && heroes[activeHero].Health() == 0) {
-               --activeHero;
-       }
-}
-
-void BattleState::SwapHeroes(int lhs, int rhs) {
-       if (lhs < 0 || lhs >= numHeroes || rhs < 0 || rhs >= numHeroes || lhs == rhs) return;
-       std::swap(heroes[lhs], heroes[rhs]);
+       battle.SetCapsule(c);
 }
 
 
@@ -81,19 +55,20 @@ void BattleState::OnResize(int w, int h) {
 
 void BattleState::OnEnterState(SDL_Surface *screen) {
        for (int i(0); i < 4; ++i) {
-               heroes[i].Position() = heroesLayout->CalculatePosition(i, background->w, background->h);
-               heroes[i].SpellMenu() = *res->spellMenuProperties;
-               heroes[i].UpdateSpellMenu();
-               heroes[i].IkariMenu() = *res->ikariMenuProperties;
-               heroes[i].UpdateIkariMenu(res);
+               Hero &hero = HeroAt(i);
+               hero.Position() = battle.HeroesLayout().CalculatePosition(i, background->w, background->h);
+               hero.SpellMenu() = *res->spellMenuProperties;
+               hero.UpdateSpellMenu();
+               hero.IkariMenu() = *res->ikariMenuProperties;
+               hero.UpdateIkariMenu(res);
                heroTags[i] = HeroTag(this, i);
                smallHeroTags[i] = SmallHeroTag(this, i);
        }
 
-       capsule.Position() = heroesLayout->CalculatePosition(4, background->w, background->h);
+       battle.GetCapsule().Position() = battle.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);
+       for (int i(0); i < battle.NumMonsters(); ++i) {
+               MonsterAt(i).Position() = battle.MonstersLayout().CalculatePosition(i, background->w, background->h);
        }
 
        int tagHeight(attackTypeMenu.Height());
@@ -117,7 +92,7 @@ void BattleState::OnEnterState(SDL_Surface *screen) {
 
        itemMenu = *res->itemMenuProperties;
        LoadInventory();
-       ClearAllAttacks();
+       battle.ClearAllAttacks();
 }
 
 void BattleState::LoadInventory() {
@@ -143,253 +118,26 @@ void BattleState::OnResumeState(SDL_Surface *screen) {
                Ctrl().PopState(); // quit the battle scene
                return;
        }
-       if (Victory()) {
+       if (battle.Victory()) {
                Ctrl().PopState();
                return;
        }
-       if (Defeat()) {
+       if (battle.Defeat()) {
                Ctrl().PopState();
                return;
        }
        // TODO: this should not push a state while quitting
-       if (AttackSelectionDone()) {
-               Ctrl().PushState(new PerformAttacks(this));
+       if (battle.AttackSelectionDone()) {
+               Ctrl().PushState(new PerformAttacks(&battle, this));
        } else {
-               Ctrl().PushState(new SelectMoveAction(this));
+               Ctrl().PushState(new SelectMoveAction(&battle, this));
        }
 }
 
-bool BattleState::Victory() const {
-       for (int i(0); i < MaxMonsters(); ++i) {
-               if (MonsterAt(i).Health() > 0) return false;
-       }
-       return true;
-}
-
-bool BattleState::Defeat() const {
-       for (int i(0); i < NumHeroes(); ++i) {
-               if (HeroAt(i).Health() > 0) return false;
-       }
-       return true;
-}
-
 void BattleState::OnPauseState(SDL_Surface *screen) {
 
 }
 
-
-class OrderCompare {
-       public:
-               OrderCompare(BattleState *battle) : battle(battle) { }
-               bool operator ()(const BattleState::Order &lhs, const BattleState::Order &rhs) {
-                       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(Order::HERO, i));
-       }
-       for (vector<Monster>::size_type i(0), end(monsters.size()); i < end; ++i) {
-               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));
-}
-
-void BattleState::NextAttack() {
-       if (Victory() || Defeat()) {
-               attackCursor = attackOrder.size();
-               return;
-       }
-       ++attackCursor;
-       while (attackCursor < int(attackOrder.size())) {
-               if (attackOrder[attackCursor].IsMonster()) {
-                       if (MonsterAt(attackOrder[attackCursor].index).Health() > 0) break;
-               } else if (attackOrder[attackCursor].IsHero()) {
-                       if (HeroAt(attackOrder[attackCursor].index).Health() > 0) break;
-               } else {
-                       if (capsule.Active() && capsule.Health() > 0) break;
-               }
-               ++attackCursor;
-       }
-}
-
-bool BattleState::AttacksFinished() const {
-       return attackCursor >= int(attackOrder.size())
-                       || Victory() || Defeat();
-}
-
-void BattleState::CalculateDamage() {
-       if (CurrentAttack().IsMonster()) {
-               DecideMonsterAttack(MonsterAt(CurrentAttack().index));
-       } else if (CurrentAttack().IsCapsule()) {
-               DecideCapsuleAttack();
-       }
-       AttackChoice &ac = CurrentAttack().GetAttackChoice(*this);
-       if (ac.GetType() == AttackChoice::DEFEND) return;
-       TargetSelection &ts(ac.Selection());
-
-       const Stats &attackerStats = CurrentAttack().GetStats(*this);
-       CalculateDamage(attackerStats, ts);
-}
-
-void BattleState::DecideMonsterAttack(Monster &m) {
-       AttackChoice &ac(m.GetAttackChoice());
-       TargetSelection &ts(ac.Selection());
-       ac.Reset();
-       int target(rand() % NumHeroes());
-       while (!HeroPositionOccupied(target)) {
-               target = rand() % NumHeroes();
-       }
-       ac.SetType(AttackChoice::SWORD);
-       ts.SelectHeroes();
-       ts.SetSingle();
-       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()) {
-               for (int i(0); i < MaxMonsters(); ++i) {
-                       if (ts.IsSelected(i)) {
-                               if (MonsterAt(i).Health() > 0) {
-                                       const Stats &defenderStats(MonsterAt(i).GetStats());
-                                       Uint16 damage(CalculateDamage(attackerStats, defenderStats));
-                                       ts.SetBad(i, damage);
-                                       hitSome = true;
-                               } else {
-                                       ts.Unselect(i);
-                               }
-                       }
-               }
-               if (hitSome) return;
-               for (int i(0); i < MaxMonsters(); ++i) {
-                       if (MonsterAt(i).Health() > 0) {
-                               const Stats &defenderStats(MonsterAt(i).GetStats());
-                               Uint16 damage(CalculateDamage(attackerStats, defenderStats));
-                               ts.SetBad(i, damage);
-                               break;
-                       }
-               }
-       } else {
-               for (int i(0); i < NumHeroes(); ++i) {
-                       if (ts.IsSelected(i)) {
-                               if (HeroAt(i).Health() > 0) {
-                                       const Stats &defenderStats(HeroAt(i).GetStats());
-                                       Uint16 damage(CalculateDamage(attackerStats, defenderStats));
-                                       ts.SetBad(i, damage);
-                                       hitSome = true;
-                               } else {
-                                       ts.Unselect(i);
-                               }
-                       }
-               }
-               if (hitSome) return;
-               for (int i(0); i < NumHeroes(); ++i) {
-                       if (HeroAt(i).Health() > 0) {
-                               const Stats &defenderStats(HeroAt(i).GetStats());
-                               Uint16 damage(CalculateDamage(attackerStats, defenderStats));
-                               ts.SetBad(i, damage);
-                               break;
-                       }
-               }
-       }
-}
-
-Uint16 BattleState::CalculateDamage(const Stats &attacker, const Stats &defender) const {
-       return attacker.Attack() / 2 - defender.Defense() / 4;
-}
-
-void BattleState::ApplyDamage() {
-       if (attackCursor < 0) return;
-       AttackChoice &ac = CurrentAttack().GetAttackChoice(*this);
-       TargetSelection &ts(ac.Selection());
-       if (ts.TargetsMonsters()) {
-               for (int i(0); i < MaxMonsters(); ++i) {
-                       Monster &monster(MonsterAt(i));
-                       if (ts.IsBad(i)) {
-                               monster.SubtractHealth(ts.GetAmount(i));
-                               if (monster.Health() == 0) {
-                                       expReward += monster.ExpReward();
-                                       goldReward += monster.GoldReward();
-                               }
-                       }
-               }
-       } else {
-               for (int i(0); i < NumHeroes(); ++i) {
-                       Hero &hero(HeroAt(i));
-                       if (ts.IsBad(i)) {
-                               hero.SubtractHealth(ts.GetAmount(i));
-                       }
-               }
-       }
-}
-
-AttackChoice &BattleState::CurrentAttackAttackChoice() {
-       return CurrentAttack().GetAttackChoice(*this);
-}
-
-void BattleState::ClearAllAttacks() {
-       attackCursor = -1;
-       activeHero = -1;
-       for (int i(0); i < NumHeroes(); ++i) {
-               HeroAt(i).GetAttackChoice() = AttackChoice(this);
-       }
-       for (int i(0); i < MaxMonsters(); ++i) {
-               MonsterAt(i).GetAttackChoice() = AttackChoice(this);
-       }
-       capsule.GetAttackChoice() = AttackChoice(this);
-       attackOrder.clear();
-}
-
-
 void BattleState::HandleEvents(const Input &input) {
 
 }
@@ -418,12 +166,13 @@ void BattleState::RenderBackground(SDL_Surface *screen) {
 
 void BattleState::RenderMonsters(SDL_Surface *screen) {
        assert(screen);
-       for (vector<Monster>::size_type i(0), end(monsters.size()); i < end; ++i) {
-               if (MonsterPositionOccupied(i)) {
-                       if (monsters[i].GetAnimation().Running()) {
-                               monsters[i].GetAnimation().DrawCenter(screen, monsters[i].Position() + offset);
+       for (vector<Monster>::size_type i(0), end(battle.NumMonsters()); i < end; ++i) {
+               if (battle.MonsterPositionOccupied(i)) {
+                       Monster &monster(battle.MonsterAt(i));
+                       if (monster.GetAnimation().Running()) {
+                               monster.GetAnimation().DrawCenter(screen, monster.Position() + offset);
                        } else {
-                               monsters[i].Sprite()->DrawCenter(screen, monsters[i].Position() + offset);
+                               monster.Sprite()->DrawCenter(screen, monster.Position() + offset);
                        }
                }
        }
@@ -431,17 +180,19 @@ void BattleState::RenderMonsters(SDL_Surface *screen) {
 
 void BattleState::RenderHeroes(SDL_Surface *screen) {
        assert(screen);
-       for (int i(0); i < numHeroes; ++i) {
-               if (heroes[i].GetAnimation().Running()) {
-                       heroes[i].GetAnimation().DrawCenter(screen, heroes[i].Position() + offset);
+       for (int i(0); i < NumHeroes(); ++i) {
+               Hero &hero(battle.HeroAt(i));
+               if (hero.GetAnimation().Running()) {
+                       hero.GetAnimation().DrawCenter(screen, hero.Position() + offset);
                } else {
-                       int row(heroes[i].Health() > 0 ? 0 : 2);
-                       heroes[i].Sprite()->DrawCenter(screen, heroes[i].Position() + offset, 1, row);
+                       int row(hero.Health() > 0 ? 0 : 2);
+                       hero.Sprite()->DrawCenter(screen, hero.Position() + offset, 1, row);
                }
        }
 }
 
 void BattleState::RenderCapsule(SDL_Surface *screen) {
+       const Capsule &capsule(battle.GetCapsule());
        if (!capsule.Active() || capsule.Health() <= 0) return;
        if (capsule.GetAnimation().Running()) {
                capsule.GetAnimation().DrawCenter(screen, capsule.Position() + offset);
@@ -455,8 +206,8 @@ void BattleState::RenderHeroTags(SDL_Surface *screen) {
        int tagHeight(attackTypeMenu.Height());
        int tagWidth(attackTypeMenu.Width() * 2 + attackTypeMenu.Width() / 2);
 
-       for (int i(0); i < numHeroes; ++i) {
-               heroTags[i].Render(screen, tagWidth, tagHeight, heroTagPositions[i] + offset, (int)i == activeHero);
+       for (int i(0); i < battle.NumHeroes(); ++i) {
+               heroTags[i].Render(screen, tagWidth, tagHeight, heroTagPositions[i] + offset, battle.IsActiveHero(i));
        }
 }
 
@@ -475,7 +226,7 @@ void BattleState::RenderSmallHeroTags(SDL_Surface *screen) {
        rect.h -= res->normalFont->CharHeight() / 4;
        SDL_FillRect(screen, &rect, res->heroesBgColor.MapRGB(screen->format));
 
-       for (int i(0); i < numHeroes; ++i) {
+       for (int i(0); i < battle.NumHeroes(); ++i) {
                smallHeroTags[i].Render(screen, tagWidth, tagHeight, smallHeroTagPositions[i] + offset);
        }
 }
index cff0e43b01a0a53d9e75111dd60c39e9fe8b134f..0f3216e4618b51fe3a0f822086dd2ca2eb013ced 100644 (file)
@@ -14,6 +14,7 @@ namespace math {
 }
 
 #include "AttackTypeMenu.h"
+#include "Battle.h"
 #include "Capsule.h"
 #include "Hero.h"
 #include "HeroTag.h"
@@ -40,17 +41,11 @@ public:
        BattleState(common::GameConfig *game, SDL_Surface *background, const PartyLayout *monstersLayout)
        : game(game)
        , background(background)
-       , monstersLayout(monstersLayout)
-       , heroesLayout(game->heroesLayout)
        , res(game->battleResources)
+       , battle(game->heroesLayout, monstersLayout)
        , attackTypeMenu(res->attackIcons)
        , moveMenu(res->moveIcons)
-       , numHeroes(0)
-       , activeHero(-1)
-       , attackCursor(-1)
-       , expReward(0)
-       , goldReward(0)
-       , ranAway(false) { assert(background && monstersLayout && game); }
+       , ranAway(false) { assert(background && game); }
 
 public:
        void AddMonster(const Monster &);
@@ -63,6 +58,8 @@ public:
        virtual void Render(SDL_Surface *);
 
 public:
+       Battle &GetBattle() { return battle; }
+       const Battle &GetBattle() const { return battle; }
        const Resources &Res() const { return *res; }
        AttackTypeMenu &GetAttackTypeMenu() { return attackTypeMenu; }
        MoveMenu &GetMoveMenu() { return moveMenu; }
@@ -70,64 +67,21 @@ public:
        graphics::Menu<const common::Item *> &ItemMenu() { return itemMenu; }
        const graphics::Menu<const common::Item *> &ItemMenu() const { return itemMenu; }
 
-       void NextHero();
-       bool BeforeFirstHero() const { return activeHero < 0; }
-       void PreviousHero();
-       void SwapHeroes(int lhs, int rhs);
-       Hero &ActiveHero() { assert(activeHero >= 0 && activeHero < NumHeroes()); return heroes[activeHero]; }
-       const Hero &ActiveHero() const { assert(activeHero >= 0 && activeHero < NumHeroes()); return heroes[activeHero]; }
-
-       Hero &HeroAt(int index) { assert(index >= 0 && index < NumHeroes()); return heroes[index]; }
-       const Hero &HeroAt(int index) const { assert(index >= 0 && index < NumHeroes()); return heroes[index]; }
-       Monster &MonsterAt(int index) { assert(index >= 0 && index < NumHeroes()); return monsters[index]; }
-       const Monster &MonsterAt(int index) const { assert(index >= 0 && index < NumHeroes()); return monsters[index]; }
+       Hero &HeroAt(int index) { return battle.HeroAt(index); }
+       const Hero &HeroAt(int index) const { return battle.HeroAt(index); }
+       Monster &MonsterAt(int index) { return battle.MonsterAt(index); }
+       const Monster &MonsterAt(int index) const { return battle.MonsterAt(index); }
 
        const HeroTag &HeroTagAt(int index) const { assert(index >= 0 && index < NumHeroes()); return heroTags[index]; }
        const math::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; }
-
-       int NumHeroes() const { return numHeroes; }
-       int MaxHeroes() const { return 4; }
-       int MaxMonsters() const { return monsters.size(); }
-
-       bool MonsterPositionOccupied(int index) { return index >= 0 && index < int(monsters.size()) && monsters[index].Health() > 0; }
-       bool HeroPositionOccupied(int index) const { return index >= 0 && index < numHeroes; }
+       int NumHeroes() const { return battle.NumHeroes(); }
+       int MaxHeroes() const { return battle.MaxHeroes(); }
+       int NumMonsters() const { return battle.NumMonsters(); }
+       int MaxMonsters() const { return battle.MaxMonsters(); }
 
        void SetRunaway() { ranAway = true; }
 
-       struct Order {
-               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;
-               Performer by;
-       };
-
-       void CalculateAttackOrder();
-       void NextAttack();
-       bool AttacksFinished() const;
-       void CalculateDamage();
-       void ApplyDamage();
-       const Order &CurrentAttack() const { assert(attackCursor >= 0 && attackCursor < int(attackOrder.size())); return attackOrder[attackCursor]; };
-       AttackChoice &CurrentAttackAttackChoice();
-       void ClearAllAttacks();
-
-       bool Victory() const;
-       bool Defeat() const;
-
 public:
        const math::Vector<int> &ScreenOffset() const { return offset; }
        int Width() const { return background->w; }
@@ -152,22 +106,13 @@ private:
 private:
        void LoadInventory();
 
-       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;
-
 private:
        common::GameConfig *game;
        SDL_Surface *background;
-       const PartyLayout *monstersLayout;
-       const PartyLayout *heroesLayout;
        const Resources *res;
+       Battle battle;
        AttackTypeMenu attackTypeMenu;
        MoveMenu moveMenu;
-       std::vector<Monster> monsters;
-       std::vector<Order> attackOrder;
-       Hero heroes[4];
        graphics::Menu<const common::Item *> itemMenu;
        HeroTag heroTags[4];
        SmallHeroTag smallHeroTags[4];
@@ -176,12 +121,6 @@ private:
 
        math::Vector<int> offset;
 
-       Capsule capsule;
-       int numHeroes;
-       int activeHero;
-       int attackCursor;
-       int expReward;
-       int goldReward;
        bool ranAway;
 
 };
index 316dd140fc98dae9019d8c333043e290fd09c266..8e90e21bf9818d07eee525d3709abc6490eeb449 100644 (file)
@@ -29,7 +29,7 @@ Uint8 Capsule::Level() const {
        return master->Level();
 }
 
-const Sprite *Capsule::Sprite() {
+const Sprite *Capsule::Sprite() const {
        assert(master);
        return master->BattleSprite();
 }
index dc2ac6e994826bd4e7357a9fa9b6180e6d26c5a3..ef4e46da07f2e342d223852087684ca8b2e1d3c7 100644 (file)
@@ -28,7 +28,7 @@ public:
 
        const char *Name() const;
        Uint8 Level() const;
-       const graphics::Sprite *Sprite();
+       const graphics::Sprite *Sprite() const;
 
        Uint16 MaxHealth() const;
        Uint16 Health() const;
index c6f8dafa4a3a1a71aebf9b71228c59087a17cd97..408be3bdb547794760bd5c322ac207cb1322df8a 100644 (file)
@@ -22,7 +22,7 @@ void SmallHeroTag::Render(SDL_Surface *screen, int width, int height, const math
 
        frame->Draw(screen, position, width, height);
 
-       if (battle->HeroPositionOccupied(index)) {
+       if (battle->GetBattle().HeroPositionOccupied(index)) {
                const Hero &hero(battle->HeroAt(index));
 
                int gaugeWidth(width - 2 * frame->BorderWidth() - labels->Width());
index 961f57cf845cdf703014c83345dc569803a231f9..573200a799c1caf96e3819f47850e5eb67fd4a29 100644 (file)
@@ -1,18 +1,31 @@
 #include "TargetSelection.h"
 
-#include "BattleState.h"
+#include "Battle.h"
 #include "../common/TargetingMode.h"
 
+#include <cassert>
+
+
 namespace battle {
 
-TargetSelection::TargetSelection(BattleState *battle, bool multiple, bool atEnemy)
+TargetSelection::TargetSelection()
+: battle(0)
+, selected()
+, selection(-1)
+, cursor(0)
+, multiple(false)
+, enemy(true) {
+
+}
+
+TargetSelection::TargetSelection(Battle *battle, bool multiple, bool atEnemy)
 : battle(battle)
-, selected(battle ? (battle->MaxMonsters() > battle->NumHeroes() ? battle->MaxMonsters() : battle->NumHeroes()) : 0, State())
+, selected(battle->MaxMonsters() > battle->NumHeroes() ? battle->MaxMonsters() : battle->NumHeroes(), State())
 , selection(-1)
 , cursor(0)
 , multiple(multiple)
 , enemy(atEnemy) {
-       if (battle && enemy) {
+       if (enemy) {
                FindNextEnemy();
        }
 }
index 3c70518f21613540c7c0cc2dd029acb02f95cdf2..52b0d00bcb519bbc7d1819a31708261a1099ca5e 100644 (file)
@@ -1,20 +1,23 @@
 #ifndef BATTLE_TARGETSELECTION_H_
 #define BATTLE_TARGETSELECTION_H_
 
+namespace battle {
+       class Battle;
+}
 namespace common {
        class TargetingMode;
 }
 
 #include <vector>
 
-namespace battle {
 
-class BattleState;
+namespace battle {
 
 class TargetSelection {
 
 public:
-       explicit TargetSelection(BattleState *battle = 0, bool multiple = false, bool atEnemy = true);
+       TargetSelection();
+       explicit TargetSelection(Battle *battle, bool multiple = false, bool atEnemy = true);
 
 public:
        bool TargetsMonsters() const { return enemy; }
@@ -73,7 +76,7 @@ private:
                int number;
                explicit State(Type type = IGNORE, int num = 0) : type(type), number(num) { }
        };
-       BattleState *battle;
+       Battle *battle;
        std::vector<State> selected;
        int selection;
        int cursor;
index 92e5185cf70488a5ce8baf9a330011ef7ba51934..2274cfc10a390a15f3dca0f9f4c3fca936528b85 100644 (file)
@@ -23,9 +23,22 @@ using std::vector;
 
 namespace battle {
 
+PerformAttacks::PerformAttacks(Battle *battle, BattleState *parent)
+: battle(battle)
+, parent(parent)
+, moveAnimation(0)
+, targetAnimation(0)
+, titleBarText(0)
+, cursor(-1) {
+
+}
+
+
 void PerformAttacks::OnEnterState(SDL_Surface *screen) {
        battle->CalculateAttackOrder();
-       numberAnimation.reserve(battle->MaxMonsters() > battle->NumHeroes() + 1 ? battle->MaxMonsters() : battle->NumHeroes() + 1);
+       numberAnimation.reserve(battle->MaxMonsters() > battle->NumHeroes() + 1
+                       ? battle->MaxMonsters()
+                       : battle->NumHeroes() + 1);
        numberPosition.reserve(numberAnimation.size());
        OnResize(screen->w, screen->h);
 }
@@ -44,10 +57,10 @@ void PerformAttacks::OnPauseState(SDL_Surface *screen) {
 
 
 void PerformAttacks::OnResize(int width, int height) {
-       const Resources &res = battle->Res();
-       framePosition = battle->ScreenOffset();
+       const Resources &res = parent->Res();
+       framePosition = parent->ScreenOffset();
        frameSize = Vector<int>(
-                       battle->Width(),
+                       parent->Width(),
                        res.titleFrame->BorderHeight() * 2 + res.titleFont->CharHeight());
 }
 
@@ -81,7 +94,7 @@ void PerformAttacks::HandleEvents(const Input &input) {
                AddNumberAnimations(capsule.GetAttackChoice().Selection());
        } else {
                Hero &hero(battle->HeroAt(battle->CurrentAttack().index));
-               const AttackChoice &ac(battle->HeroAt(battle->CurrentAttack().index).GetAttackChoice());
+               const AttackChoice &ac(battle->CurrentAttackAttackChoice());
 
                switch (ac.GetType()) {
                        case AttackChoice::SWORD:
@@ -126,9 +139,9 @@ void PerformAttacks::HandleEvents(const Input &input) {
 
        if (titleBarText) {
                titleBarTimer = GraphicsTimers().StartCountdown(850);
-               textPosition = battle->ScreenOffset() + Vector<int>(
-                               (battle->Width() - std::strlen(titleBarText) * battle->Res().titleFont->CharWidth()) / 2,
-                               battle->Res().titleFrame->BorderHeight());
+               textPosition = parent->ScreenOffset() + Vector<int>(
+                               (parent->Width() - std::strlen(titleBarText) * parent->Res().titleFont->CharWidth()) / 2,
+                               parent->Res().titleFrame->BorderHeight());
        }
        if (moveAnimation.Valid()) {
                moveAnimation.Start(*this);
@@ -151,25 +164,25 @@ void PerformAttacks::AddNumberAnimations(const TargetSelection &ts) {
        if (ts.TargetsMonsters()) {
                for (int i(0); i < battle->MaxMonsters(); ++i) {
                        if (ts.IsBad(i)) {
-                               numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), battle->Res().numberAnimationPrototype, battle->Res().bigNumberSprite));
+                               numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), parent->Res().numberAnimationPrototype, parent->Res().bigNumberSprite));
                                numberPosition.push_back(
-                                               battle->MonsterAt(i).Position() + battle->ScreenOffset());
+                                               battle->MonsterAt(i).Position() + parent->ScreenOffset());
                        } else if (ts.IsGood(i)) {
-                               numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), battle->Res().numberAnimationPrototype, battle->Res().greenNumberSprite));
+                               numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), parent->Res().numberAnimationPrototype, parent->Res().greenNumberSprite));
                                numberPosition.push_back(
-                                               battle->MonsterAt(i).Position() + battle->ScreenOffset());
+                                               battle->MonsterAt(i).Position() + parent->ScreenOffset());
                        }
                }
        } else {
                for (int i(0); i < battle->NumHeroes(); ++i) {
                        if (ts.IsBad(i)) {
-                               numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), battle->Res().numberAnimationPrototype, battle->Res().bigNumberSprite));
+                               numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), parent->Res().numberAnimationPrototype, parent->Res().bigNumberSprite));
                                numberPosition.push_back(
-                                               battle->HeroAt(i).Position() + battle->ScreenOffset());
+                                               battle->HeroAt(i).Position() + parent->ScreenOffset());
                        } else if (ts.IsGood(i)) {
-                               numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), battle->Res().numberAnimationPrototype, battle->Res().greenNumberSprite));
+                               numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), parent->Res().numberAnimationPrototype, parent->Res().greenNumberSprite));
                                numberPosition.push_back(
-                                               battle->HeroAt(i).Position() + battle->ScreenOffset());
+                                               battle->HeroAt(i).Position() + parent->ScreenOffset());
                        }
                }
        }
@@ -225,11 +238,11 @@ void PerformAttacks::UpdateWorld(Uint32 deltaT) {
 }
 
 void PerformAttacks::Render(SDL_Surface *screen) {
-       battle->RenderBackground(screen);
-       battle->RenderMonsters(screen);
-       battle->RenderHeroes(screen);
-       battle->RenderCapsule(screen);
-       battle->RenderSmallHeroTags(screen);
+       parent->RenderBackground(screen);
+       parent->RenderMonsters(screen);
+       parent->RenderHeroes(screen);
+       parent->RenderCapsule(screen);
+       parent->RenderSmallHeroTags(screen);
        RenderTitleBar(screen);
        RenderNumbers(screen);
        RenderTargetAnimation(screen);
@@ -238,9 +251,9 @@ void PerformAttacks::Render(SDL_Surface *screen) {
 void PerformAttacks::RenderTitleBar(SDL_Surface *screen) const {
        if (!titleBarText || !titleBarTimer.Running()) return;
 
-       battle->Res().titleFrame->Draw(screen, framePosition, frameSize.X(), frameSize.Y());
+       parent->Res().titleFrame->Draw(screen, framePosition, frameSize.X(), frameSize.Y());
 
-       battle->Res().titleFont->DrawString(titleBarText, screen, textPosition);
+       parent->Res().titleFont->DrawString(titleBarText, screen, textPosition);
 }
 
 void PerformAttacks::RenderNumbers(SDL_Surface *screen) const {
@@ -258,13 +271,13 @@ void PerformAttacks::RenderTargetAnimation(SDL_Surface *screen) const {
        if (ts.TargetsHeroes()) {
                for (vector<Vector<int> >::size_type i(0), end(battle->NumHeroes()); i < end; ++i) {
                        if (ts.IsSelected(i)) {
-                               targetAnimation.DrawCenter(screen, battle->HeroAt(i).Position() + battle->ScreenOffset());
+                               targetAnimation.DrawCenter(screen, battle->HeroAt(i).Position() + parent->ScreenOffset());
                        }
                }
        } else {
                for (vector<Vector<int> >::size_type i(0), end(battle->MaxMonsters()); i < end; ++i) {
                        if (ts.IsSelected(i)) {
-                               targetAnimation.DrawCenter(screen, battle->MonsterAt(i).Position() + battle->ScreenOffset());
+                               targetAnimation.DrawCenter(screen, battle->MonsterAt(i).Position() + parent->ScreenOffset());
                        }
                }
        }
index 3d8b2ff6d5e23d6b7853cc4931c7356d49bdbd59..37328dc89db6e01700a94855ac2aa6c9c95c9091 100644 (file)
@@ -1,6 +1,12 @@
 #ifndef BATTLE_PERFORMATTACKS_H_
 #define BATTLE_PERFORMATTACKS_H_
 
+namespace battle {
+       class Battle;
+       class BattleState;
+       class TargetSelection;
+}
+
 #include "../../app/State.h"
 
 #include "../NumberAnimation.h"
 
 #include <vector>
 
-namespace battle {
 
-class BattleState;
-class TargetSelection;
+namespace battle {
 
 class PerformAttacks
 : public app::State {
 
 public:
-       explicit PerformAttacks(BattleState *battle)
-       : battle(battle), moveAnimation(0), targetAnimation(0), titleBarText(0), cursor(-1) { }
+       PerformAttacks(Battle *battle, BattleState *parent);
 
 public:
        virtual void HandleEvents(const app::Input &);
@@ -47,7 +50,8 @@ private:
        void RenderTargetAnimation(SDL_Surface *screen) const;
 
 private:
-       BattleState *battle;
+       Battle *battle;
+       BattleState *parent;
        graphics::AnimationRunner moveAnimation;
        graphics::AnimationRunner targetAnimation;
        const char *titleBarText;
index b4f5dcc73f69a071bc4877d682dc27d2c52da2c4..d0f1f392973cd19e28a10e8dc500c70088b82f3d 100644 (file)
@@ -21,6 +21,13 @@ using math::Vector;
 
 namespace battle {
 
+SelectAttackType::SelectAttackType(Battle *battle, BattleState *parent)
+: battle(battle)
+, parent(parent) {
+
+}
+
+
 void SelectAttackType::OnEnterState(SDL_Surface *screen) {
        OnResize(screen->w, screen->h);
 }
@@ -31,7 +38,7 @@ void SelectAttackType::OnExitState(SDL_Surface *screen) {
 
 void SelectAttackType::OnResumeState(SDL_Surface *screen) {
        if (battle->ActiveHero().GetAttackChoice().Selection().HasSelected()) {
-               battle->ActiveHero().GetAttackChoice().SetType(battle->GetAttackTypeMenu().Selected());
+               battle->ActiveHero().GetAttackChoice().SetType(parent->GetAttackTypeMenu().Selected());
                battle->NextHero();
        }
        if (battle->AttackSelectionDone()) {
@@ -46,31 +53,31 @@ void SelectAttackType::OnPauseState(SDL_Surface *screen) {
 
 
 void SelectAttackType::OnResize(int width, int height) {
-       Vector<int> offset(battle->ScreenOffset());
+       Vector<int> offset(parent->ScreenOffset());
        Vector<int> position(
-                       (battle->Width() - battle->GetAttackTypeMenu().Width()) / 2,
-                       battle->Height() - battle->GetAttackTypeMenu().Height() - battle->GetAttackTypeMenu().Height() / 2);
+                       (parent->Width() - parent->GetAttackTypeMenu().Width()) / 2,
+                       parent->Height() - parent->GetAttackTypeMenu().Height() - parent->GetAttackTypeMenu().Height() / 2);
        menuOffset = offset + position;
 }
 
 
 void SelectAttackType::HandleEvents(const Input &input) {
        if (input.IsDown(Input::PAD_UP)) {
-               battle->GetAttackTypeMenu().Select(AttackChoice::MAGIC);
+               parent->GetAttackTypeMenu().Select(AttackChoice::MAGIC);
        } else if (input.IsDown(Input::PAD_RIGHT)) {
-               battle->GetAttackTypeMenu().Select(AttackChoice::DEFEND);
+               parent->GetAttackTypeMenu().Select(AttackChoice::DEFEND);
        } else if (input.IsDown(Input::PAD_DOWN)) {
-               battle->GetAttackTypeMenu().Select(AttackChoice::IKARI);
+               parent->GetAttackTypeMenu().Select(AttackChoice::IKARI);
        } else if (input.IsDown(Input::PAD_LEFT)) {
-               battle->GetAttackTypeMenu().Select(AttackChoice::ITEM);
+               parent->GetAttackTypeMenu().Select(AttackChoice::ITEM);
        } else {
-               battle->GetAttackTypeMenu().Select(AttackChoice::SWORD);
+               parent->GetAttackTypeMenu().Select(AttackChoice::SWORD);
        }
 
        Hero &hero(battle->ActiveHero());
        AttackChoice &ac(hero.GetAttackChoice());
        if (input.JustPressed(Input::ACTION_A)) {
-               switch (battle->GetAttackTypeMenu().Selected()) {
+               switch (parent->GetAttackTypeMenu().Selected()) {
                        case AttackChoice::SWORD:
                                if (hero.HasWeapon()) {
                                        if (hero.Weapon()->GetTargetingMode().TargetsAll()) {
@@ -84,7 +91,7 @@ void SelectAttackType::HandleEvents(const Input &input) {
                                        ac.Selection().SetSingle();
                                }
                                ac.Selection().Reset();
-                               Ctrl().PushState(new SelectTarget(battle, this, &ac.Selection(), battle->Res().weaponTargetCursor));
+                               Ctrl().PushState(new SelectTarget(battle, this, &ac.Selection(), parent->Res().weaponTargetCursor));
                                break;
                        case AttackChoice::MAGIC:
                                if (battle->ActiveHero().CanUseMagic()) {
@@ -108,7 +115,7 @@ void SelectAttackType::HandleEvents(const Input &input) {
                ac.Reset();
                battle->PreviousHero();
                if (battle->BeforeFirstHero()) {
-                       Ctrl().ChangeState(new SelectMoveAction(battle));
+                       Ctrl().ChangeState(new SelectMoveAction(battle, parent));
                } else {
                        battle->ActiveHero().GetAttackChoice().Reset();
                }
@@ -124,14 +131,46 @@ void SelectAttackType::UpdateWorld(Uint32 deltaT) {
 }
 
 void SelectAttackType::Render(SDL_Surface *screen) {
-       battle->RenderBackground(screen);
-       battle->RenderMonsters(screen);
-       battle->RenderHeroTags(screen);
+       parent->RenderBackground(screen);
+       parent->RenderMonsters(screen);
+       parent->RenderHeroTags(screen);
        RenderMenu(screen);
 }
 
 void SelectAttackType::RenderMenu(SDL_Surface *screen) {
-       battle->GetAttackTypeMenu().Render(screen, menuOffset);
+       parent->GetAttackTypeMenu().Render(screen, menuOffset);
+}
+
+
+const Resources &SelectAttackType::Res() const {
+       return parent->Res();
+}
+
+graphics::Menu<const Item *> &SelectAttackType::ItemMenu() {
+       return parent->ItemMenu();
+}
+
+const graphics::Menu<const Item *> &SelectAttackType::ItemMenu() const {
+       return parent->ItemMenu();
+}
+
+const HeroTag &SelectAttackType::HeroTagAt(int index) const {
+       return parent->HeroTagAt(index);
+}
+const Vector<int> &SelectAttackType::HeroTagPositionAt(int index) const {
+       return parent->HeroTagPositionAt(index);
+}
+
+const Vector<int> &SelectAttackType::ScreenOffset() const {
+       return parent->ScreenOffset();
+}
+
+int SelectAttackType::Width() const {
+       return parent->Width();
+}
+
+int SelectAttackType::Height() const {
+       return parent->Height();
 }
 
 }
index 8794a48c2f45cbbcbc945bd32aec188ba6b0c16b..51dd0f478d87f895cdc7a2d9ae5ad977db6e210e 100644 (file)
@@ -2,7 +2,17 @@
 #define BATTLE_SELECTATTACKTYPE_H_
 
 namespace battle {
+       class Battle;
        class BattleState;
+       class HeroTag;
+       struct Resources;
+}
+namespace common {
+       class Item;
+}
+namespace graphics {
+       template<class>
+       class Menu;
 }
 
 #include "../../app/State.h"
@@ -14,14 +24,24 @@ class SelectAttackType
 : public app::State {
 
 public:
-       explicit SelectAttackType(BattleState *battle)
-       : battle(battle) { }
+       SelectAttackType(Battle *battle, BattleState *parent);
 
 public:
        virtual void HandleEvents(const app::Input &);
        virtual void UpdateWorld(Uint32 deltaT);
        virtual void Render(SDL_Surface *);
 
+public:
+       const Resources &Res() const;
+       graphics::Menu<const common::Item *> &ItemMenu();
+       const graphics::Menu<const common::Item *> &ItemMenu() const;
+       const HeroTag &HeroTagAt(int index) const;
+       const math::Vector<int> &HeroTagPositionAt(int index) const;
+
+       const math::Vector<int> &ScreenOffset() const;
+       int Width() const;
+       int Height() const;
+
 private:
        virtual void OnEnterState(SDL_Surface *screen);
        virtual void OnExitState(SDL_Surface *screen);
@@ -34,7 +54,8 @@ private:
        void RenderMenu(SDL_Surface *screen);
 
 private:
-       BattleState *battle;
+       Battle *battle;
+       BattleState *parent;
        math::Vector<int> menuOffset;
 
 };
index 2ab66a95dc002bec89458e297a0d5e7f7872dc00..2f07fa2ef56d1065fd18d7a460151f8e91af2f4a 100644 (file)
@@ -18,6 +18,13 @@ using graphics::Frame;
 
 namespace battle {
 
+SelectIkari::SelectIkari(Battle *battle, SelectAttackType *parent)
+: battle(battle)
+, parent(parent) {
+
+}
+
+
 void SelectIkari::OnEnterState(SDL_Surface *screen) {
        OnResize(screen->w, screen->h);
 }
@@ -40,14 +47,14 @@ void SelectIkari::OnPauseState(SDL_Surface *screen) {
 
 
 void SelectIkari::OnResize(int width, int height) {
-       const Vector<int> offset = battle->ScreenOffset();
+       const Vector<int> offset = parent->ScreenOffset();
 
-       const Resources &res = battle->Res();
+       const Resources &res = parent->Res();
        const Frame &frame = *res.selectFrame;
 
        framePosition = offset + frame.BorderSize();
        frameSize = Vector<int>(
-                       battle->Width() - 2 * frame.BorderWidth(),
+                       parent->Width() - 2 * frame.BorderWidth(),
                        res.normalFont->CharHeight() * 13);
 
        headlinePosition = offset + Vector<int>(
@@ -82,7 +89,7 @@ void SelectIkari::HandleEvents(const Input &input) {
                                } else {
                                        ac.Selection().SetMultiple();
                                }
-                               Ctrl().PushState(new SelectTarget(battle, parent, &ac.Selection(), ikari->IsMagical() ? battle->Res().magicTargetCursor : battle->Res().weaponTargetCursor));
+                               Ctrl().PushState(new SelectTarget(battle, parent, &ac.Selection(), ikari->IsMagical() ? parent->Res().magicTargetCursor : parent->Res().weaponTargetCursor));
                        }
                }
        }
@@ -115,12 +122,12 @@ void SelectIkari::Render(SDL_Surface *screen) {
 }
 
 void SelectIkari::RenderFrame(SDL_Surface *screen) {
-       const Frame &frame = *battle->Res().selectFrame;
+       const Frame &frame = *parent->Res().selectFrame;
        frame.Draw(screen, framePosition, frameSize.X(), frameSize.Y());
 }
 
 void SelectIkari::RenderHeadline(SDL_Surface *screen) {
-       const Resources &res = battle->Res();
+       const Resources &res = parent->Res();
        res.normalFont->DrawString(res.ikariMenuHeadline, screen, headlinePosition);
 }
 
index 254da726baa71831fac9fcf7713bf1d83dd362c3..9b46deb5e88a8305783cabff32eb67bd6b6dbd50 100644 (file)
@@ -2,7 +2,7 @@
 #define BATTLE_SELECTIKARI_H_
 
 namespace battle {
-       class BattleState;
+       class Battle;
        class SelectAttackType;
 }
 
@@ -16,8 +16,7 @@ class SelectIkari
 : public app::State {
 
 public:
-       SelectIkari(BattleState *battle, SelectAttackType *parent)
-       : battle(battle), parent(parent) { }
+       SelectIkari(Battle *battle, SelectAttackType *parent);
 
 public:
        virtual void HandleEvents(const app::Input &);
@@ -38,7 +37,7 @@ private:
        void RenderMenu(SDL_Surface *);
 
 private:
-       BattleState *battle;
+       Battle *battle;
        SelectAttackType *parent;
        math::Vector<int> framePosition;
        math::Vector<int> frameSize;
index 0aaf7a36a79776ee9023a7162115aa7e49704a42..e4ccc14527d95ca4ea5bbd2665b4284195500c1c 100644 (file)
@@ -17,6 +17,13 @@ using graphics::Frame;
 
 namespace battle {
 
+SelectItem::SelectItem(Battle *battle, SelectAttackType *parent)
+: battle(battle)
+, parent(parent) {
+
+}
+
+
 void SelectItem::OnEnterState(SDL_Surface *screen) {
        OnResize(screen->w, screen->h);
 }
@@ -28,7 +35,7 @@ void SelectItem::OnExitState(SDL_Surface *screen) {
 void SelectItem::OnResumeState(SDL_Surface *screen) {
        if (battle->ActiveHero().GetAttackChoice().Selection().HasSelected()) {
                battle->ActiveHero().GetAttackChoice().SetType(AttackChoice::ITEM);
-               battle->ActiveHero().GetAttackChoice().SetItem(battle->ItemMenu().Selected());
+               battle->ActiveHero().GetAttackChoice().SetItem(parent->ItemMenu().Selected());
                Ctrl().PopState();
        }
 }
@@ -39,14 +46,14 @@ void SelectItem::OnPauseState(SDL_Surface *screen) {
 
 
 void SelectItem::OnResize(int width, int height) {
-       const Vector<int> offset = battle->ScreenOffset();
+       const Vector<int> offset = parent->ScreenOffset();
 
-       const Resources &res = battle->Res();
+       const Resources &res = parent->Res();
        const Frame &frame = *res.selectFrame;
 
        framePosition = offset + frame.BorderSize();
        frameSize = Vector<int>(
-                       battle->Width() - 2 * frame.BorderWidth(),
+                       parent->Width() - 2 * frame.BorderWidth(),
                        res.normalFont->CharHeight() * 13);
 
        headlinePosition = offset + Vector<int>(
@@ -61,9 +68,9 @@ void SelectItem::OnResize(int width, int height) {
 
 void SelectItem::HandleEvents(const Input &input) {
        if (input.JustPressed(Input::ACTION_A)) {
-               if (battle->ItemMenu().SelectedIsEnabled()) {
+               if (parent->ItemMenu().SelectedIsEnabled()) {
                        AttackChoice &ac(battle->ActiveHero().GetAttackChoice());
-                       const Item *item(battle->ItemMenu().Selected());
+                       const Item *item(parent->ItemMenu().Selected());
                        ac.Selection().Reset();
                        if (item->GetTargetingMode().TargetsAlly()) {
                                ac.Selection().SelectHeroes();
@@ -81,7 +88,7 @@ void SelectItem::HandleEvents(const Input &input) {
                                } else {
                                        ac.Selection().SetMultiple();
                                }
-                               Ctrl().PushState(new SelectTarget(battle, parent, &ac.Selection(), battle->Res().itemTargetCursor));
+                               Ctrl().PushState(new SelectTarget(battle, parent, &ac.Selection(), parent->Res().itemTargetCursor));
                        }
                }
        }
@@ -89,16 +96,16 @@ void SelectItem::HandleEvents(const Input &input) {
                Ctrl().PopState(); // return control to parent
        }
        if (input.JustPressed(Input::PAD_UP)) {
-               battle->ItemMenu().PreviousRow();
+               parent->ItemMenu().PreviousRow();
        }
        if (input.JustPressed(Input::PAD_RIGHT)) {
-               battle->ItemMenu().NextItem();
+               parent->ItemMenu().NextItem();
        }
        if (input.JustPressed(Input::PAD_DOWN)) {
-               battle->ItemMenu().NextRow();
+               parent->ItemMenu().NextRow();
        }
        if (input.JustPressed(Input::PAD_LEFT)) {
-               battle->ItemMenu().PreviousItem();
+               parent->ItemMenu().PreviousItem();
        }
 }
 
@@ -114,17 +121,17 @@ void SelectItem::Render(SDL_Surface *screen) {
 }
 
 void SelectItem::RenderFrame(SDL_Surface *screen) {
-       const Frame &frame = *battle->Res().selectFrame;
+       const Frame &frame = *parent->Res().selectFrame;
        frame.Draw(screen, framePosition, frameSize.X(), frameSize.Y());
 }
 
 void SelectItem::RenderHeadline(SDL_Surface *screen) {
-       const Resources &res = battle->Res();
+       const Resources &res = parent->Res();
        res.normalFont->DrawString(res.itemMenuHeadline, screen, headlinePosition);
 }
 
 void SelectItem::RenderMenu(SDL_Surface *screen) {
-       battle->ItemMenu().Draw(screen, menuPosition);
+       parent->ItemMenu().Draw(screen, menuPosition);
 }
 
 }
index 9e9bcd0458536bd1dd0b71f38a51b8ea4787522a..f8a6881c9deeb49e9c9b6b877d894cd53790ff68 100644 (file)
@@ -2,7 +2,7 @@
 #define BATTLE_SELECTITEM_H_
 
 namespace battle {
-       class BattleState;
+       class Battle;
        class SelectAttackType;
 }
 
@@ -15,8 +15,7 @@ class SelectItem
 : public app::State {
 
 public:
-       SelectItem(BattleState *battle, SelectAttackType *parent)
-       : battle(battle), parent(parent) { }
+       SelectItem(Battle *battle, SelectAttackType *parent);
 
 public:
        virtual void HandleEvents(const app::Input &);
@@ -37,7 +36,7 @@ private:
        void RenderMenu(SDL_Surface *);
 
 private:
-       BattleState *battle;
+       Battle *battle;
        SelectAttackType *parent;
        math::Vector<int> framePosition;
        math::Vector<int> frameSize;
index c8405da7968340d01aa58f5c6c4ee7ee48f7697b..79aa8b949feab80f216c93bb2e02466c83f2b086 100644 (file)
@@ -15,6 +15,13 @@ using math::Vector;
 
 namespace battle {
 
+SelectMoveAction::SelectMoveAction(Battle *battle, BattleState *parent)
+: battle(battle)
+, parent(parent) {
+
+}
+
+
 void SelectMoveAction::OnEnterState(SDL_Surface *screen) {
        OnResize(screen->w, screen->h);
 }
@@ -33,32 +40,32 @@ void SelectMoveAction::OnPauseState(SDL_Surface *screen) {
 
 
 void SelectMoveAction::OnResize(int width, int height) {
-       position = battle->ScreenOffset() + Vector<int>(
-                       (battle->Width() - battle->GetMoveMenu().Width()) / 2,
-                       battle->Height() - battle->GetMoveMenu().Height() - battle->GetMoveMenu().Height() / 2);
+       position = parent->ScreenOffset() + Vector<int>(
+                       (parent->Width() - parent->GetMoveMenu().Width()) / 2,
+                       parent->Height() - parent->GetMoveMenu().Height() - parent->GetMoveMenu().Height() / 2);
 }
 
 
 void SelectMoveAction::HandleEvents(const Input &input) {
        if (input.IsDown(Input::PAD_UP)) {
-               battle->GetMoveMenu().Select(MoveMenu::CHANGE);
+               parent->GetMoveMenu().Select(MoveMenu::CHANGE);
        } else if (input.IsDown(Input::PAD_DOWN)) {
-               battle->GetMoveMenu().Select(MoveMenu::RUN);
+               parent->GetMoveMenu().Select(MoveMenu::RUN);
        } else {
-               battle->GetMoveMenu().Select(MoveMenu::ATTACK);
+               parent->GetMoveMenu().Select(MoveMenu::ATTACK);
        }
 
        if (input.JustPressed(Input::ACTION_A)) {
-               switch (battle->GetMoveMenu().Selected()) {
+               switch (parent->GetMoveMenu().Selected()) {
                        case MoveMenu::ATTACK:
-                               Ctrl().ChangeState(new SelectAttackType(battle));
+                               Ctrl().ChangeState(new SelectAttackType(battle, parent));
                                battle->NextHero();
                                break;
                        case MoveMenu::CHANGE:
                                Ctrl().PushState(new SwapHeroes(battle, this));
                                break;
                        case MoveMenu::RUN:
-                               Ctrl().ChangeState(new RunState(battle));
+                               Ctrl().ChangeState(new RunState(parent));
                                break;
                }
        }
@@ -69,14 +76,31 @@ void SelectMoveAction::UpdateWorld(Uint32 deltaT) {
 }
 
 void SelectMoveAction::Render(SDL_Surface *screen) {
-       battle->RenderBackground(screen);
-       battle->RenderMonsters(screen);
-       battle->RenderHeroTags(screen);
+       parent->RenderBackground(screen);
+       parent->RenderMonsters(screen);
+       parent->RenderHeroTags(screen);
        RenderMenu(screen);
 }
 
 void SelectMoveAction::RenderMenu(SDL_Surface *screen) {
-       battle->GetMoveMenu().Render(screen, position);
+       parent->GetMoveMenu().Render(screen, position);
+}
+
+
+const Resources &SelectMoveAction::Res() const {
+       return parent->Res();
+}
+
+const Vector<int> &SelectMoveAction::ScreenOffset() const {
+       return parent->ScreenOffset();
+}
+
+const HeroTag &SelectMoveAction::HeroTagAt(int index) const {
+       return parent->HeroTagAt(index);
+}
+
+const Vector<int> &SelectMoveAction::HeroTagPositionAt(int index) const {
+       return parent->HeroTagPositionAt(index);
 }
 
 }
index eb299ad828eae589c091fb2002439ecbf8f06432..156eec16cbec2e22b5ff3647debda3165bfb733d 100644 (file)
@@ -2,7 +2,10 @@
 #define BATTLE_SELECTMOVEACTION_H_
 
 namespace battle {
+       class Battle;
        class BattleState;
+       class HeroTag;
+       struct Resources;
 }
 
 #include "../../app/State.h"
@@ -14,14 +17,19 @@ class SelectMoveAction
 : public app::State {
 
 public:
-       explicit SelectMoveAction(BattleState *battle)
-       : battle(battle) { }
+       SelectMoveAction(Battle *battle, BattleState *parent);
 
 public:
        virtual void HandleEvents(const app::Input &);
        virtual void UpdateWorld(Uint32 deltaT);
        virtual void Render(SDL_Surface *);
 
+public:
+       const Resources &Res() const;
+       const math::Vector<int> &ScreenOffset() const;
+       const HeroTag &HeroTagAt(int index) const;
+       const math::Vector<int> &HeroTagPositionAt(int index) const;
+
 private:
        virtual void OnEnterState(SDL_Surface *screen);
        virtual void OnExitState(SDL_Surface *screen);
@@ -34,7 +42,8 @@ private:
        void RenderMenu(SDL_Surface *screen);
 
 private:
-       BattleState *battle;
+       Battle *battle;
+       BattleState *parent;
        math::Vector<int> position;
 
 };
index 7b918b4c98c762b71015c4213870bcf093c6bf4f..2b22e70fcd856e124fac407f047bd682b8aa6ed8 100644 (file)
@@ -18,6 +18,13 @@ using graphics::Frame;
 
 namespace battle {
 
+SelectSpell::SelectSpell(Battle *battle, SelectAttackType *parent)
+: battle(battle)
+, parent(parent) {
+
+}
+
+
 void SelectSpell::OnEnterState(SDL_Surface *screen) {
        OnResize(screen->w, screen->h);
 }
@@ -40,14 +47,14 @@ void SelectSpell::OnPauseState(SDL_Surface *screen) {
 
 
 void SelectSpell::OnResize(int width, int height) {
-       const Vector<int> offset = battle->ScreenOffset();
+       const Vector<int> offset = parent->ScreenOffset();
 
-       const Resources &res = battle->Res();
+       const Resources &res = parent->Res();
        const Frame &frame = *res.selectFrame;
 
        framePosition = offset + frame.BorderSize();
        frameSize = Vector<int>(
-                       battle->Width() - 2 * frame.BorderWidth(),
+                       parent->Width() - 2 * frame.BorderWidth(),
                        res.normalFont->CharHeight() * 13);
 
        headlinePosition = offset + Vector<int>(
@@ -82,7 +89,7 @@ void SelectSpell::HandleEvents(const Input &input) {
                                } else {
                                        ac.Selection().SetMultiple();
                                }
-                               Ctrl().PushState(new SelectTarget(battle, parent, &ac.Selection(), battle->Res().magicTargetCursor));
+                               Ctrl().PushState(new SelectTarget(battle, parent, &ac.Selection(), parent->Res().magicTargetCursor));
                        }
                }
        }
@@ -115,12 +122,12 @@ void SelectSpell::Render(SDL_Surface *screen) {
 }
 
 void SelectSpell::RenderFrame(SDL_Surface *screen) {
-       const Frame &frame = *battle->Res().selectFrame;
+       const Frame &frame = *parent->Res().selectFrame;
        frame.Draw(screen, framePosition, frameSize.X(), frameSize.Y());
 }
 
 void SelectSpell::RenderHeadline(SDL_Surface *screen) {
-       const Resources &res = battle->Res();
+       const Resources &res = parent->Res();
        res.normalFont->DrawString(res.spellMenuHeadline, screen, headlinePosition);
 }
 
index 64cc85206c8c24f424d241d37d79f1c8454b513a..84df91eefc4751da7be3b4e9dea1724d439f0482 100644 (file)
@@ -2,7 +2,7 @@
 #define BATTLE_SELECTSPELL_H_
 
 namespace battle {
-       class BattleState;
+       class Battle;
        class SelectAttackType;
 }
 
@@ -15,8 +15,7 @@ class SelectSpell
 : public app::State {
 
 public:
-       SelectSpell(BattleState *battle, SelectAttackType *parent)
-       : battle(battle), parent(parent) { }
+       SelectSpell(Battle *battle, SelectAttackType *parent);
 
 public:
        virtual void HandleEvents(const app::Input &);
@@ -37,7 +36,7 @@ private:
        void RenderMenu(SDL_Surface *);
 
 private:
-       BattleState *battle;
+       Battle *battle;
        SelectAttackType *parent;
        math::Vector<int> framePosition;
        math::Vector<int> frameSize;
index dc8c1239145f9a7cfbd95828a2c16612eea25e27..f9507a17024966158efca526d50193961bd2c8b4 100644 (file)
@@ -13,6 +13,20 @@ using std::vector;
 
 namespace battle {
 
+SelectTarget::SelectTarget(
+               Battle *battle,
+               SelectAttackType *parent,
+               TargetSelection *selection,
+               const graphics::Sprite *cursorIcon)
+: battle(battle)
+, parent(parent)
+, selection(selection)
+, cursorIcon(cursorIcon)
+, flipFlop(true) {
+
+}
+
+
 void SelectTarget::OnEnterState(SDL_Surface *screen) {
        OnResize(screen->w, screen->h);
 }
@@ -31,21 +45,21 @@ void SelectTarget::OnPauseState(SDL_Surface *screen) {
 
 
 void SelectTarget::OnResize(int width, int height) {
-       Vector<int> offset(battle->ScreenOffset());
+       Vector<int> offset(parent->ScreenOffset());
        cursorOffset = Vector<int>(cursorIcon->Width() / -2, cursorIcon->Height()) + offset;
        // offset the indicator by 1/8th to the right and top
        indicatorOffset = cursorOffset + Vector<int>(cursorIcon->Width() / 8, cursorIcon->Height() / -8);
 
        monsterPositions.clear();
-       monsterPositions.reserve(battle->MaxMonsters());
-       for (int i(0), end(battle->MaxMonsters()); i < end; ++i) {
+       monsterPositions.reserve(battle->NumMonsters());
+       for (int i(0), end(battle->NumMonsters()); i < end; ++i) {
                monsterPositions.push_back(battle->MonsterAt(i).Position());
        }
 
        heroPositions.clear();
        heroPositions.reserve(battle->NumHeroes());
        for (int i(0), end(battle->NumHeroes()); i < end; ++i) {
-               heroPositions.push_back(battle->HeroTagPositionAt(i) + battle->HeroTagAt(i).HeroOffset());
+               heroPositions.push_back(parent->HeroTagPositionAt(i) + parent->HeroTagAt(i).HeroOffset());
        }
 }
 
index 9bdc470519a7d1ce98e2006f2ffff954547cc2fe..29035c8352b6d7f975067559da0f5228f97fcc3b 100644 (file)
@@ -2,7 +2,7 @@
 #define BATTLE_SELECTTARGET_H_
 
 namespace battle {
-       class BattleState;
+       class Battle;
        class SelectAttackType;
        class TargetSelection;
 }
@@ -21,8 +21,10 @@ class SelectTarget
 : public app::State {
 
 public:
-       SelectTarget(BattleState *battle, SelectAttackType *parent, TargetSelection *selection, const graphics::Sprite *cursorIcon)
-       : battle(battle), parent(parent), selection(selection), cursorIcon(cursorIcon), flipFlop(true) { }
+       SelectTarget(Battle *battle,
+                       SelectAttackType *parent,
+                       TargetSelection *selection,
+                       const graphics::Sprite *cursorIcon);
 
 public:
        virtual void HandleEvents(const app::Input &);
@@ -41,7 +43,7 @@ private:
        void RenderCursors(SDL_Surface *screen);
 
 private:
-       BattleState *battle;
+       Battle *battle;
        SelectAttackType *parent;
        TargetSelection *selection;
        const graphics::Sprite *cursorIcon;
index af867477bc3d44477ebf933dddbc67656e3bc94e..799cff8d07213cd56d8d4dee59f1e9cb59240feb 100644 (file)
@@ -13,6 +13,16 @@ using std::vector;
 
 namespace battle {
 
+SwapHeroes::SwapHeroes(Battle *battle, SelectMoveAction *parent)
+: battle(battle)
+, parent(parent)
+, cursor(0)
+, selected(-1)
+, flipFlop(true) {
+
+}
+
+
 void SwapHeroes::OnEnterState(SDL_Surface *screen) {
        OnResize(screen->w, screen->h);
 }
@@ -31,21 +41,21 @@ void SwapHeroes::OnPauseState(SDL_Surface *screen) {
 
 
 void SwapHeroes::OnResize(int width, int height) {
-       Vector<int> offset(battle->ScreenOffset());
+       Vector<int> offset(parent->ScreenOffset());
        // offset the cursor by 1/8th to the left and bottom
-       cursorOffset = Vector<int>(battle->Res().swapCursor->Width() / -8, battle->Res().swapCursor->Height() / 8);
+       cursorOffset = Vector<int>(parent->Res().swapCursor->Width() / -8, parent->Res().swapCursor->Height() / 8);
        indicatorOffset = Vector<int>(0, 0);
 
        positions.clear();
        positions.reserve(battle->NumHeroes());
        for (int i(0), end(battle->NumHeroes()); i < end; ++i) {
                Vector<int> positionCorrection(
-                               battle->Res().swapCursor->Width() / 2,
-                               battle->HeroTagAt(i).HeroSprite()->Height() - battle->Res().swapCursor->Height() / 2);
+                               parent->Res().swapCursor->Width() / 2,
+                               parent->HeroTagAt(i).HeroSprite()->Height() - parent->Res().swapCursor->Height() / 2);
                // indicator offsets are inverted for heroes
                positionCorrection -= cursorOffset;
-               positions.push_back(battle->HeroTagPositionAt(i)
-                               + battle->HeroTagAt(i).HeroOffset()
+               positions.push_back(parent->HeroTagPositionAt(i)
+                               + parent->HeroTagAt(i).HeroOffset()
                                + positionCorrection
                                + offset);
        }
@@ -135,12 +145,12 @@ void SwapHeroes::RenderCursors(SDL_Surface *screen) {
                for (vector<Vector<int> >::size_type i(0), end(positions.size());
                                i != end; ++i) {
                        if (selected == int(i)) {
-                               battle->Res().swapCursor->DrawTopRight(screen, positions[i]);
+                               parent->Res().swapCursor->DrawTopRight(screen, positions[i]);
                        }
                }
        }
        flipFlop = !flipFlop;
-       battle->Res().swapCursor->DrawTopRight(screen, positions[cursor] + cursorOffset);
+       parent->Res().swapCursor->DrawTopRight(screen, positions[cursor] + cursorOffset);
 }
 
 }
index 1f98772a1952e208844c422c38f6d5c0761ed457..d1b4d923e48c4723c57ecf0348c3ae53c5178d10 100644 (file)
@@ -2,7 +2,7 @@
 #define BATTLE_SWAPHEROES_H_
 
 namespace battle {
-       class BattleState;
+       class Battle;
        class SelectMoveAction;
 }
 
@@ -17,8 +17,7 @@ class SwapHeroes
 : public app::State {
 
 public:
-       SwapHeroes(BattleState *battle, SelectMoveAction *parent)
-       : battle(battle), parent(parent), cursor(0), selected(-1), flipFlop(true) { }
+       SwapHeroes(Battle *battle, SelectMoveAction *parent);
 
 public:
        virtual void HandleEvents(const app::Input &);
@@ -43,7 +42,7 @@ private:
        void RenderCursors(SDL_Surface *screen);
 
 private:
-       BattleState *battle;
+       Battle *battle;
        SelectMoveAction *parent;
        std::vector<math::Vector<int> > positions;
        math::Vector<int> cursorOffset;
index 787241906a96c35a92d035691a6c02815b3d8a32..aa12b2559cdfe69aa1221847aded744bfbd8549e 100644 (file)
@@ -83,8 +83,9 @@ public:
        static void CreateTypeDescription();
        static void Construct(void *);
 
-// temporary setters
 public:
+       void SetMaxHealth(int h) { maxHealth = h; }
+       void SetHealth(int h) { health = h; }
        void AddSpell(Spell *s) { spells.push_back(s); }
 
 private:
diff --git a/tests/battle/BattleTest.cpp b/tests/battle/BattleTest.cpp
new file mode 100644 (file)
index 0000000..4d35549
--- /dev/null
@@ -0,0 +1,227 @@
+#include "BattleTest.h"
+
+#include "../../src/battle/Battle.h"
+#include "../../src/battle/PartyLayout.h"
+
+#include <stdexcept>
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_battle::BattleTest);
+
+using battle::AttackChoice;
+using battle::Battle;
+using battle::PartyLayout;
+using math::Vector;
+
+
+namespace test_battle {
+
+void BattleTest::setUp() {
+       positions[0] = Vector<int>(0, 0);
+       positions[1] = Vector<int>(1, 1);
+       twoLayout = new PartyLayout;
+       twoLayout->SetPositions(positions, 2);
+
+       positions[2] = Vector<int>(0, 0);
+       positions[3] = Vector<int>(1, 1);
+       positions[4] = Vector<int>(2, 2);
+       positions[5] = Vector<int>(3, 3);
+       positions[6] = Vector<int>(4, 4);
+       fiveLayout = new PartyLayout;
+       fiveLayout->SetPositions(positions + 2, 5);
+
+       hero.SetMaxHealth(8);
+       hero.SetHealth(8);
+       hero.GetStats().SetAgility(10);
+
+       monster.SetMaxHealth(8);
+       monster.SetHealth(8);
+       monster.GetStats().SetAgility(20);
+}
+
+void BattleTest::tearDown() {
+       monster = battle::Monster();
+       hero = common::Hero();
+       capsule = common::Capsule();
+       delete fiveLayout;
+       delete twoLayout;
+}
+
+
+void BattleTest::testSetup() {
+       CPPUNIT_ASSERT_THROW(Battle battle(0, twoLayout), std::invalid_argument);
+       CPPUNIT_ASSERT_THROW(Battle battle(fiveLayout, 0), std::invalid_argument);
+
+       Battle battle(fiveLayout, twoLayout);
+       CPPUNIT_ASSERT_EQUAL(0, battle.NumHeroes());
+       CPPUNIT_ASSERT_EQUAL(4, battle.MaxHeroes());
+       CPPUNIT_ASSERT(!battle.HeroPositionOccupied(0));
+       // number of monsters is always max
+       CPPUNIT_ASSERT_EQUAL(2, battle.NumMonsters());
+       CPPUNIT_ASSERT_EQUAL(2, battle.MaxMonsters());
+       CPPUNIT_ASSERT(!battle.MonsterPositionOccupied(0));
+       CPPUNIT_ASSERT(!battle.HasCapsule());
+
+       battle.AddHero(hero);
+       CPPUNIT_ASSERT_EQUAL(1, battle.NumHeroes());
+       CPPUNIT_ASSERT_EQUAL(4, battle.MaxHeroes());
+       CPPUNIT_ASSERT(battle.HeroPositionOccupied(0));
+       CPPUNIT_ASSERT_EQUAL(2, battle.NumMonsters());
+       CPPUNIT_ASSERT_EQUAL(2, battle.MaxMonsters());
+       CPPUNIT_ASSERT(!battle.HasCapsule());
+
+       battle.AddHero(hero);
+       CPPUNIT_ASSERT(battle.HeroPositionOccupied(1));
+       battle.AddHero(hero);
+       CPPUNIT_ASSERT(battle.HeroPositionOccupied(2));
+       battle.AddHero(hero);
+       CPPUNIT_ASSERT(battle.HeroPositionOccupied(3));
+       CPPUNIT_ASSERT_EQUAL(4, battle.NumHeroes());
+       CPPUNIT_ASSERT_THROW(battle.AddHero(hero), std::overflow_error);
+       CPPUNIT_ASSERT_EQUAL(4, battle.NumHeroes());
+
+       battle.AddMonster(monster);
+       CPPUNIT_ASSERT_EQUAL(4, battle.NumHeroes());
+       CPPUNIT_ASSERT_EQUAL(4, battle.MaxHeroes());
+       CPPUNIT_ASSERT_EQUAL(2, battle.NumMonsters());
+       CPPUNIT_ASSERT_EQUAL(2, battle.MaxMonsters());
+       CPPUNIT_ASSERT(battle.MonsterPositionOccupied(0));
+       CPPUNIT_ASSERT(!battle.HasCapsule());
+
+       battle.AddMonster(monster);
+       CPPUNIT_ASSERT(battle.MonsterPositionOccupied(1));
+       CPPUNIT_ASSERT_EQUAL(2, battle.NumMonsters());
+       CPPUNIT_ASSERT_THROW(battle.AddMonster(monster), std::overflow_error);
+       CPPUNIT_ASSERT_EQUAL(2, battle.NumMonsters());
+}
+
+void BattleTest::testHeroAttackSelection() {
+       Battle battle(fiveLayout, twoLayout);
+       loadBattle(battle);
+
+       // turns start out with the cursor before the first hero
+       battle.NextHero();
+       CPPUNIT_ASSERT(!battle.AttackSelectionDone());
+       CPPUNIT_ASSERT(!battle.HasChosenAttackType());
+
+       battle.ActiveHero().GetAttackChoice().SetType(AttackChoice::SWORD);
+       battle.ActiveHero().GetAttackChoice().Selection().SetSingle();
+       battle.ActiveHero().GetAttackChoice().Selection().Select();
+       CPPUNIT_ASSERT(battle.HasChosenAttackType());
+
+       battle.NextHero();
+       CPPUNIT_ASSERT(!battle.AttackSelectionDone());
+       CPPUNIT_ASSERT(!battle.HasChosenAttackType());
+
+       battle.ActiveHero().GetAttackChoice().SetType(AttackChoice::DEFEND);
+       CPPUNIT_ASSERT(battle.HasChosenAttackType());
+       CPPUNIT_ASSERT(!battle.AttackSelectionDone());
+
+       battle.NextHero();
+       CPPUNIT_ASSERT(!battle.AttackSelectionDone());
+       CPPUNIT_ASSERT(!battle.HasChosenAttackType());
+       CPPUNIT_ASSERT(!battle.AttackSelectionDone());
+
+       battle.ActiveHero().GetAttackChoice().SetType(AttackChoice::DEFEND);
+       CPPUNIT_ASSERT(battle.HasChosenAttackType());
+
+       battle.NextHero();
+       CPPUNIT_ASSERT(!battle.AttackSelectionDone());
+       CPPUNIT_ASSERT(!battle.HasChosenAttackType());
+       CPPUNIT_ASSERT(!battle.AttackSelectionDone());
+
+       battle.ActiveHero().GetAttackChoice().SetType(AttackChoice::DEFEND);
+       CPPUNIT_ASSERT(battle.HasChosenAttackType());
+       CPPUNIT_ASSERT(!battle.AttackSelectionDone());
+
+       battle.NextHero();
+       CPPUNIT_ASSERT(battle.AttackSelectionDone());
+}
+
+void BattleTest::testBattleRound() {
+       Battle battle(fiveLayout, twoLayout);
+       loadBattle(battle);
+       selectHeroAttacks(battle);
+
+       battle.CalculateAttackOrder();
+
+       // 2 monsters with agl 20, 4 heroes with agl 10
+       // atk of all participants is 0, so no damage should be dealt
+
+       battle.NextAttack();
+       battle.CalculateDamage();
+       const Battle::Order *attack = &battle.CurrentAttack();
+       CPPUNIT_ASSERT(!battle.AttacksFinished());
+       CPPUNIT_ASSERT(attack->IsMonster());
+       CPPUNIT_ASSERT(attack->index >= 0 && attack->index < battle.MaxMonsters());
+       battle.ApplyDamage();
+
+       battle.NextAttack();
+       battle.CalculateDamage();
+       attack = &battle.CurrentAttack();
+       CPPUNIT_ASSERT(!battle.AttacksFinished());
+       CPPUNIT_ASSERT(attack->IsMonster());
+       CPPUNIT_ASSERT(attack->index >= 0 && attack->index < battle.MaxMonsters());
+       battle.ApplyDamage();
+
+       battle.NextAttack();
+       battle.CalculateDamage();
+       attack = &battle.CurrentAttack();
+       CPPUNIT_ASSERT(!battle.AttacksFinished());
+       CPPUNIT_ASSERT(attack->IsHero());
+       CPPUNIT_ASSERT(attack->index >= 0 && attack->index < battle.NumHeroes());
+       battle.ApplyDamage();
+
+       battle.NextAttack();
+       battle.CalculateDamage();
+       attack = &battle.CurrentAttack();
+       CPPUNIT_ASSERT(!battle.AttacksFinished());
+       CPPUNIT_ASSERT(attack->IsHero());
+       CPPUNIT_ASSERT(attack->index >= 0 && attack->index < battle.NumHeroes());
+       battle.ApplyDamage();
+
+       battle.NextAttack();
+       battle.CalculateDamage();
+       attack = &battle.CurrentAttack();
+       CPPUNIT_ASSERT(!battle.AttacksFinished());
+       CPPUNIT_ASSERT(attack->IsHero());
+       CPPUNIT_ASSERT(attack->index >= 0 && attack->index < battle.NumHeroes());
+       battle.ApplyDamage();
+
+       battle.NextAttack();
+       battle.CalculateDamage();
+       attack = &battle.CurrentAttack();
+       CPPUNIT_ASSERT(!battle.AttacksFinished());
+       CPPUNIT_ASSERT(attack->IsHero());
+       CPPUNIT_ASSERT(attack->index >= 0 && attack->index < battle.NumHeroes());
+       battle.ApplyDamage();
+
+       battle.NextAttack();
+       CPPUNIT_ASSERT(battle.AttacksFinished());
+}
+
+
+void BattleTest::loadBattle(Battle &battle) {
+       battle.AddHero(hero);
+       battle.AddHero(hero);
+       battle.AddHero(hero);
+       battle.AddHero(hero);
+       battle.AddMonster(monster);
+       battle.AddMonster(monster);
+}
+
+void BattleTest::selectHeroAttacks(Battle &battle) {
+       battle.NextHero();
+       battle.ActiveHero().GetAttackChoice().SetType(AttackChoice::SWORD);
+       battle.ActiveHero().GetAttackChoice().Selection().SetSingle();
+       battle.ActiveHero().GetAttackChoice().Selection().Select();
+       battle.NextHero();
+       battle.ActiveHero().GetAttackChoice().SetType(AttackChoice::DEFEND);
+       battle.NextHero();
+       battle.ActiveHero().GetAttackChoice().SetType(AttackChoice::DEFEND);
+       battle.NextHero();
+       battle.ActiveHero().GetAttackChoice().SetType(AttackChoice::DEFEND);
+       battle.NextHero();
+}
+
+
+}
diff --git a/tests/battle/BattleTest.h b/tests/battle/BattleTest.h
new file mode 100644 (file)
index 0000000..2034c33
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef TEST_BATTLE_BATTLETEST_H_
+#define TEST_BATTLE_BATTLETEST_H_
+
+namespace battle {
+       class Battle;
+       class PartyLayout;
+}
+
+#include "../../src/battle/Monster.h"
+#include "../../src/common/Capsule.h"
+#include "../../src/common/Hero.h"
+#include "../../src/math/Vector.h"
+
+#include <cppunit/extensions/HelperMacros.h>
+
+
+namespace test_battle {
+
+class BattleTest
+: public CppUnit::TestFixture {
+       CPPUNIT_TEST_SUITE(BattleTest);
+       CPPUNIT_TEST(testSetup);
+       CPPUNIT_TEST(testHeroAttackSelection);
+       CPPUNIT_TEST(testBattleRound);
+       CPPUNIT_TEST_SUITE_END();
+
+public:
+       void setUp();
+       void tearDown();
+
+       void testSetup();
+       void testHeroAttackSelection();
+       void testBattleRound();
+
+private:
+       void loadBattle(battle::Battle &);
+       void selectHeroAttacks(battle::Battle &);
+
+private:
+       battle::PartyLayout *twoLayout;
+       battle::PartyLayout *fiveLayout;
+       math::Vector<int> positions[7];
+
+       common::Capsule capsule;
+       common::Hero hero;
+       battle::Monster monster;
+
+};
+
+}
+
+#endif