#ifndef BATTLE_ATTACKCHOICE_H_
#define BATTLE_ATTACKCHOICE_H_
+namespace battle {
+ class Battle;
+}
namespace common {
class Item;
class Spell;
};
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:
--- /dev/null
+#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;
+}
+
+}
--- /dev/null
+#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
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);
}
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());
itemMenu = *res->itemMenuProperties;
LoadInventory();
- ClearAllAttacks();
+ battle.ClearAllAttacks();
}
void BattleState::LoadInventory() {
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) {
}
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);
}
}
}
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);
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));
}
}
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);
}
}
}
#include "AttackTypeMenu.h"
+#include "Battle.h"
#include "Capsule.h"
#include "Hero.h"
#include "HeroTag.h"
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 &);
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; }
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; }
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];
math::Vector<int> offset;
- Capsule capsule;
- int numHeroes;
- int activeHero;
- int attackCursor;
- int expReward;
- int goldReward;
bool ranAway;
};
return master->Level();
}
-const Sprite *Capsule::Sprite() {
+const Sprite *Capsule::Sprite() const {
assert(master);
return master->BattleSprite();
}
const char *Name() const;
Uint8 Level() const;
- const graphics::Sprite *Sprite();
+ const graphics::Sprite *Sprite() const;
Uint16 MaxHealth() const;
Uint16 Health() const;
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());
#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();
}
}
#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; }
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;
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);
}
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());
}
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:
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);
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());
}
}
}
}
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);
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 {
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());
}
}
}
#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 &);
void RenderTargetAnimation(SDL_Surface *screen) const;
private:
- BattleState *battle;
+ Battle *battle;
+ BattleState *parent;
graphics::AnimationRunner moveAnimation;
graphics::AnimationRunner targetAnimation;
const char *titleBarText;
namespace battle {
+SelectAttackType::SelectAttackType(Battle *battle, BattleState *parent)
+: battle(battle)
+, parent(parent) {
+
+}
+
+
void SelectAttackType::OnEnterState(SDL_Surface *screen) {
OnResize(screen->w, screen->h);
}
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()) {
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()) {
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()) {
ac.Reset();
battle->PreviousHero();
if (battle->BeforeFirstHero()) {
- Ctrl().ChangeState(new SelectMoveAction(battle));
+ Ctrl().ChangeState(new SelectMoveAction(battle, parent));
} else {
battle->ActiveHero().GetAttackChoice().Reset();
}
}
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();
}
}
#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"
: 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);
void RenderMenu(SDL_Surface *screen);
private:
- BattleState *battle;
+ Battle *battle;
+ BattleState *parent;
math::Vector<int> menuOffset;
};
namespace battle {
+SelectIkari::SelectIkari(Battle *battle, SelectAttackType *parent)
+: battle(battle)
+, parent(parent) {
+
+}
+
+
void SelectIkari::OnEnterState(SDL_Surface *screen) {
OnResize(screen->w, screen->h);
}
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>(
} 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));
}
}
}
}
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);
}
#define BATTLE_SELECTIKARI_H_
namespace battle {
- class BattleState;
+ class Battle;
class SelectAttackType;
}
: 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 &);
void RenderMenu(SDL_Surface *);
private:
- BattleState *battle;
+ Battle *battle;
SelectAttackType *parent;
math::Vector<int> framePosition;
math::Vector<int> frameSize;
namespace battle {
+SelectItem::SelectItem(Battle *battle, SelectAttackType *parent)
+: battle(battle)
+, parent(parent) {
+
+}
+
+
void SelectItem::OnEnterState(SDL_Surface *screen) {
OnResize(screen->w, screen->h);
}
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();
}
}
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>(
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();
} 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));
}
}
}
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();
}
}
}
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);
}
}
#define BATTLE_SELECTITEM_H_
namespace battle {
- class BattleState;
+ class Battle;
class SelectAttackType;
}
: 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 &);
void RenderMenu(SDL_Surface *);
private:
- BattleState *battle;
+ Battle *battle;
SelectAttackType *parent;
math::Vector<int> framePosition;
math::Vector<int> frameSize;
namespace battle {
+SelectMoveAction::SelectMoveAction(Battle *battle, BattleState *parent)
+: battle(battle)
+, parent(parent) {
+
+}
+
+
void SelectMoveAction::OnEnterState(SDL_Surface *screen) {
OnResize(screen->w, screen->h);
}
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;
}
}
}
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);
}
}
#define BATTLE_SELECTMOVEACTION_H_
namespace battle {
+ class Battle;
class BattleState;
+ class HeroTag;
+ struct Resources;
}
#include "../../app/State.h"
: 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);
void RenderMenu(SDL_Surface *screen);
private:
- BattleState *battle;
+ Battle *battle;
+ BattleState *parent;
math::Vector<int> position;
};
namespace battle {
+SelectSpell::SelectSpell(Battle *battle, SelectAttackType *parent)
+: battle(battle)
+, parent(parent) {
+
+}
+
+
void SelectSpell::OnEnterState(SDL_Surface *screen) {
OnResize(screen->w, screen->h);
}
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>(
} 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));
}
}
}
}
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);
}
#define BATTLE_SELECTSPELL_H_
namespace battle {
- class BattleState;
+ class Battle;
class SelectAttackType;
}
: 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 &);
void RenderMenu(SDL_Surface *);
private:
- BattleState *battle;
+ Battle *battle;
SelectAttackType *parent;
math::Vector<int> framePosition;
math::Vector<int> frameSize;
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);
}
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());
}
}
#define BATTLE_SELECTTARGET_H_
namespace battle {
- class BattleState;
+ class Battle;
class SelectAttackType;
class TargetSelection;
}
: 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 &);
void RenderCursors(SDL_Surface *screen);
private:
- BattleState *battle;
+ Battle *battle;
SelectAttackType *parent;
TargetSelection *selection;
const graphics::Sprite *cursorIcon;
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);
}
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);
}
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);
}
}
#define BATTLE_SWAPHEROES_H_
namespace battle {
- class BattleState;
+ class Battle;
class SelectMoveAction;
}
: 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 &);
void RenderCursors(SDL_Surface *screen);
private:
- BattleState *battle;
+ Battle *battle;
SelectMoveAction *parent;
std::vector<math::Vector<int> > positions;
math::Vector<int> cursorOffset;
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:
--- /dev/null
+#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();
+}
+
+
+}
--- /dev/null
+#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