From: Daniel Karbach Date: Sun, 3 Feb 2013 14:29:47 +0000 (+0100) Subject: extracted battle logic into a class X-Git-Url: http://git.localhorst.tv/?p=l2e.git;a=commitdiff_plain;h=087783315ac5955c17bb3b051c9351f321653df6 extracted battle logic into a class fixes #35 --- diff --git a/src/battle/AttackChoice.h b/src/battle/AttackChoice.h index 1da0cdc..0049f1e 100644 --- a/src/battle/AttackChoice.h +++ b/src/battle/AttackChoice.h @@ -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 index 0000000..7bfa940 --- /dev/null +++ b/src/battle/Battle.cpp @@ -0,0 +1,385 @@ +#include "Battle.h" + +#include "AttackChoice.h" +#include "PartyLayout.h" +#include "TargetSelection.h" +#include "../common/Stats.h" + +#include +#include + +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::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 index 0000000..e7cf8a2 --- /dev/null +++ b/src/battle/Battle.h @@ -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 +#include + + +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::const_iterator HeroesBegin() const { return heroes.begin(); } + std::vector::const_iterator HeroesEnd() const { return heroes.end(); } + Hero &HeroAt(int index); + const Hero &HeroAt(int index) const; + + std::vector::const_iterator MonstersBegin() const { return monsters.begin(); } + std::vector::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 heroes; + std::vector monsters; + Capsule capsule; + + int activeHero; + + std::vector attackOrder; + int attackCursor; + + int expReward; + int goldReward; + +}; + +} + +#endif diff --git a/src/battle/BattleState.cpp b/src/battle/BattleState.cpp index 57ff327..a17732c 100644 --- a/src/battle/BattleState.cpp +++ b/src/battle/BattleState.cpp @@ -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::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::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::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); } } diff --git a/src/battle/BattleState.h b/src/battle/BattleState.h index cff0e43..0f3216e 100644 --- a/src/battle/BattleState.h +++ b/src/battle/BattleState.h @@ -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 &ItemMenu() { return itemMenu; } const graphics::Menu &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 &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 &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 monsters; - std::vector attackOrder; - Hero heroes[4]; graphics::Menu itemMenu; HeroTag heroTags[4]; SmallHeroTag smallHeroTags[4]; @@ -176,12 +121,6 @@ private: math::Vector offset; - Capsule capsule; - int numHeroes; - int activeHero; - int attackCursor; - int expReward; - int goldReward; bool ranAway; }; diff --git a/src/battle/Capsule.cpp b/src/battle/Capsule.cpp index 316dd14..8e90e21 100644 --- a/src/battle/Capsule.cpp +++ b/src/battle/Capsule.cpp @@ -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(); } diff --git a/src/battle/Capsule.h b/src/battle/Capsule.h index dc2ac6e..ef4e46d 100644 --- a/src/battle/Capsule.h +++ b/src/battle/Capsule.h @@ -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; diff --git a/src/battle/SmallHeroTag.cpp b/src/battle/SmallHeroTag.cpp index c6f8daf..408be3b 100644 --- a/src/battle/SmallHeroTag.cpp +++ b/src/battle/SmallHeroTag.cpp @@ -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()); diff --git a/src/battle/TargetSelection.cpp b/src/battle/TargetSelection.cpp index 961f57c..573200a 100644 --- a/src/battle/TargetSelection.cpp +++ b/src/battle/TargetSelection.cpp @@ -1,18 +1,31 @@ #include "TargetSelection.h" -#include "BattleState.h" +#include "Battle.h" #include "../common/TargetingMode.h" +#include + + 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(); } } diff --git a/src/battle/TargetSelection.h b/src/battle/TargetSelection.h index 3c70518..52b0d00 100644 --- a/src/battle/TargetSelection.h +++ b/src/battle/TargetSelection.h @@ -1,20 +1,23 @@ #ifndef BATTLE_TARGETSELECTION_H_ #define BATTLE_TARGETSELECTION_H_ +namespace battle { + class Battle; +} namespace common { class TargetingMode; } #include -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 selected; int selection; int cursor; diff --git a/src/battle/states/PerformAttacks.cpp b/src/battle/states/PerformAttacks.cpp index 92e5185..2274cfc 100644 --- a/src/battle/states/PerformAttacks.cpp +++ b/src/battle/states/PerformAttacks.cpp @@ -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( - 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( - (battle->Width() - std::strlen(titleBarText) * battle->Res().titleFont->CharWidth()) / 2, - battle->Res().titleFrame->BorderHeight()); + textPosition = parent->ScreenOffset() + Vector( + (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 >::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 >::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()); } } } diff --git a/src/battle/states/PerformAttacks.h b/src/battle/states/PerformAttacks.h index 3d8b2ff..37328dc 100644 --- a/src/battle/states/PerformAttacks.h +++ b/src/battle/states/PerformAttacks.h @@ -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" @@ -9,17 +15,14 @@ #include -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; diff --git a/src/battle/states/SelectAttackType.cpp b/src/battle/states/SelectAttackType.cpp index b4f5dcc..d0f1f39 100644 --- a/src/battle/states/SelectAttackType.cpp +++ b/src/battle/states/SelectAttackType.cpp @@ -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 offset(battle->ScreenOffset()); + Vector offset(parent->ScreenOffset()); Vector 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 &SelectAttackType::ItemMenu() { + return parent->ItemMenu(); +} + +const graphics::Menu &SelectAttackType::ItemMenu() const { + return parent->ItemMenu(); +} + +const HeroTag &SelectAttackType::HeroTagAt(int index) const { + return parent->HeroTagAt(index); +} +const Vector &SelectAttackType::HeroTagPositionAt(int index) const { + return parent->HeroTagPositionAt(index); +} + +const Vector &SelectAttackType::ScreenOffset() const { + return parent->ScreenOffset(); +} + +int SelectAttackType::Width() const { + return parent->Width(); +} + +int SelectAttackType::Height() const { + return parent->Height(); } } diff --git a/src/battle/states/SelectAttackType.h b/src/battle/states/SelectAttackType.h index 8794a48..51dd0f4 100644 --- a/src/battle/states/SelectAttackType.h +++ b/src/battle/states/SelectAttackType.h @@ -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 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 &ItemMenu(); + const graphics::Menu &ItemMenu() const; + const HeroTag &HeroTagAt(int index) const; + const math::Vector &HeroTagPositionAt(int index) const; + + const math::Vector &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 menuOffset; }; diff --git a/src/battle/states/SelectIkari.cpp b/src/battle/states/SelectIkari.cpp index 2ab66a9..2f07fa2 100644 --- a/src/battle/states/SelectIkari.cpp +++ b/src/battle/states/SelectIkari.cpp @@ -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 offset = battle->ScreenOffset(); + const Vector offset = parent->ScreenOffset(); - const Resources &res = battle->Res(); + const Resources &res = parent->Res(); const Frame &frame = *res.selectFrame; framePosition = offset + frame.BorderSize(); frameSize = Vector( - battle->Width() - 2 * frame.BorderWidth(), + parent->Width() - 2 * frame.BorderWidth(), res.normalFont->CharHeight() * 13); headlinePosition = offset + Vector( @@ -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); } diff --git a/src/battle/states/SelectIkari.h b/src/battle/states/SelectIkari.h index 254da72..9b46deb 100644 --- a/src/battle/states/SelectIkari.h +++ b/src/battle/states/SelectIkari.h @@ -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 framePosition; math::Vector frameSize; diff --git a/src/battle/states/SelectItem.cpp b/src/battle/states/SelectItem.cpp index 0aaf7a3..e4ccc14 100644 --- a/src/battle/states/SelectItem.cpp +++ b/src/battle/states/SelectItem.cpp @@ -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 offset = battle->ScreenOffset(); + const Vector offset = parent->ScreenOffset(); - const Resources &res = battle->Res(); + const Resources &res = parent->Res(); const Frame &frame = *res.selectFrame; framePosition = offset + frame.BorderSize(); frameSize = Vector( - battle->Width() - 2 * frame.BorderWidth(), + parent->Width() - 2 * frame.BorderWidth(), res.normalFont->CharHeight() * 13); headlinePosition = offset + Vector( @@ -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); } } diff --git a/src/battle/states/SelectItem.h b/src/battle/states/SelectItem.h index 9e9bcd0..f8a6881 100644 --- a/src/battle/states/SelectItem.h +++ b/src/battle/states/SelectItem.h @@ -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 framePosition; math::Vector frameSize; diff --git a/src/battle/states/SelectMoveAction.cpp b/src/battle/states/SelectMoveAction.cpp index c8405da..79aa8b9 100644 --- a/src/battle/states/SelectMoveAction.cpp +++ b/src/battle/states/SelectMoveAction.cpp @@ -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( - (battle->Width() - battle->GetMoveMenu().Width()) / 2, - battle->Height() - battle->GetMoveMenu().Height() - battle->GetMoveMenu().Height() / 2); + position = parent->ScreenOffset() + Vector( + (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 &SelectMoveAction::ScreenOffset() const { + return parent->ScreenOffset(); +} + +const HeroTag &SelectMoveAction::HeroTagAt(int index) const { + return parent->HeroTagAt(index); +} + +const Vector &SelectMoveAction::HeroTagPositionAt(int index) const { + return parent->HeroTagPositionAt(index); } } diff --git a/src/battle/states/SelectMoveAction.h b/src/battle/states/SelectMoveAction.h index eb299ad..156eec1 100644 --- a/src/battle/states/SelectMoveAction.h +++ b/src/battle/states/SelectMoveAction.h @@ -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 &ScreenOffset() const; + const HeroTag &HeroTagAt(int index) const; + const math::Vector &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 position; }; diff --git a/src/battle/states/SelectSpell.cpp b/src/battle/states/SelectSpell.cpp index 7b918b4..2b22e70 100644 --- a/src/battle/states/SelectSpell.cpp +++ b/src/battle/states/SelectSpell.cpp @@ -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 offset = battle->ScreenOffset(); + const Vector offset = parent->ScreenOffset(); - const Resources &res = battle->Res(); + const Resources &res = parent->Res(); const Frame &frame = *res.selectFrame; framePosition = offset + frame.BorderSize(); frameSize = Vector( - battle->Width() - 2 * frame.BorderWidth(), + parent->Width() - 2 * frame.BorderWidth(), res.normalFont->CharHeight() * 13); headlinePosition = offset + Vector( @@ -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); } diff --git a/src/battle/states/SelectSpell.h b/src/battle/states/SelectSpell.h index 64cc852..84df91e 100644 --- a/src/battle/states/SelectSpell.h +++ b/src/battle/states/SelectSpell.h @@ -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 framePosition; math::Vector frameSize; diff --git a/src/battle/states/SelectTarget.cpp b/src/battle/states/SelectTarget.cpp index dc8c123..f9507a1 100644 --- a/src/battle/states/SelectTarget.cpp +++ b/src/battle/states/SelectTarget.cpp @@ -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 offset(battle->ScreenOffset()); + Vector offset(parent->ScreenOffset()); cursorOffset = Vector(cursorIcon->Width() / -2, cursorIcon->Height()) + offset; // offset the indicator by 1/8th to the right and top indicatorOffset = cursorOffset + Vector(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()); } } diff --git a/src/battle/states/SelectTarget.h b/src/battle/states/SelectTarget.h index 9bdc470..29035c8 100644 --- a/src/battle/states/SelectTarget.h +++ b/src/battle/states/SelectTarget.h @@ -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; diff --git a/src/battle/states/SwapHeroes.cpp b/src/battle/states/SwapHeroes.cpp index af86747..799cff8 100644 --- a/src/battle/states/SwapHeroes.cpp +++ b/src/battle/states/SwapHeroes.cpp @@ -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 offset(battle->ScreenOffset()); + Vector offset(parent->ScreenOffset()); // offset the cursor by 1/8th to the left and bottom - cursorOffset = Vector(battle->Res().swapCursor->Width() / -8, battle->Res().swapCursor->Height() / 8); + cursorOffset = Vector(parent->Res().swapCursor->Width() / -8, parent->Res().swapCursor->Height() / 8); indicatorOffset = Vector(0, 0); positions.clear(); positions.reserve(battle->NumHeroes()); for (int i(0), end(battle->NumHeroes()); i < end; ++i) { Vector 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 >::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); } } diff --git a/src/battle/states/SwapHeroes.h b/src/battle/states/SwapHeroes.h index 1f98772..d1b4d92 100644 --- a/src/battle/states/SwapHeroes.h +++ b/src/battle/states/SwapHeroes.h @@ -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 > positions; math::Vector cursorOffset; diff --git a/src/common/Hero.h b/src/common/Hero.h index 7872419..aa12b25 100644 --- a/src/common/Hero.h +++ b/src/common/Hero.h @@ -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 index 0000000..4d35549 --- /dev/null +++ b/tests/battle/BattleTest.cpp @@ -0,0 +1,227 @@ +#include "BattleTest.h" + +#include "../../src/battle/Battle.h" +#include "../../src/battle/PartyLayout.h" + +#include + +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(0, 0); + positions[1] = Vector(1, 1); + twoLayout = new PartyLayout; + twoLayout->SetPositions(positions, 2); + + positions[2] = Vector(0, 0); + positions[3] = Vector(1, 1); + positions[4] = Vector(2, 2); + positions[5] = Vector(3, 3); + positions[6] = Vector(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 index 0000000..2034c33 --- /dev/null +++ b/tests/battle/BattleTest.h @@ -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 + + +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 positions[7]; + + common::Capsule capsule; + common::Hero hero; + battle::Monster monster; + +}; + +} + +#endif