#ifndef BATTLE_ATTACKCHOICE_H_
#define BATTLE_ATTACKCHOICE_H_
+namespace common {
+ class Item;
+ class Spell;
+}
+
#include "TargetSelection.h"
-#include "../common/fwd.h"
namespace battle {
public:
OrderCompare(BattleState *battle) : battle(battle) { }
bool operator ()(const BattleState::Order &lhs, const BattleState::Order &rhs) {
- int lagl(lhs.isMonster ? battle->MonsterAt(lhs.index).GetStats().Agility() : battle->HeroAt(lhs.index).GetStats().Agility());
- int ragl(rhs.isMonster ? battle->MonsterAt(rhs.index).GetStats().Agility() : battle->HeroAt(rhs.index).GetStats().Agility());
- return lagl > ragl;
+ return lhs.GetStats(*battle).Agility() > rhs.GetStats(*battle).Agility();
}
private:
BattleState *battle;
void BattleState::CalculateAttackOrder() {
attackOrder.reserve(monsters.size() + NumHeroes());
for (int i(0); i < NumHeroes(); ++i) {
- attackOrder.push_back(Order(i, false));
+ attackOrder.push_back(Order(Order::HERO, i));
}
for (vector<Monster>::size_type i(0), end(monsters.size()); i < end; ++i) {
- attackOrder.push_back(Order(i, true));
+ attackOrder.push_back(Order(Order::MONSTER, i));
MonsterAt(i).GetAttackChoice() = AttackChoice(this);
}
+ if (capsule.Health() > 0) {
+ attackOrder.push_back(Order(Order::CAPSULE));
+ }
std::sort(attackOrder.begin(), attackOrder.end(), OrderCompare(this));
}
}
++attackCursor;
while (attackCursor < int(attackOrder.size())) {
- if (attackOrder[attackCursor].isMonster) {
+ if (attackOrder[attackCursor].IsMonster()) {
if (MonsterAt(attackOrder[attackCursor].index).Health() > 0) break;
- } else {
+ } else if (attackOrder[attackCursor].IsHero()) {
if (HeroAt(attackOrder[attackCursor].index).Health() > 0) break;
+ } else {
+ if (capsule.Health() > 0) break;
}
++attackCursor;
}
}
void BattleState::CalculateDamage() {
- if (CurrentAttack().isMonster) {
+ if (CurrentAttack().IsMonster()) {
DecideMonsterAttack(MonsterAt(CurrentAttack().index));
+ } else if (CurrentAttack().IsCapsule()) {
+ DecideCapsuleAttack();
}
- AttackChoice &ac(CurrentAttack().isMonster ? MonsterAt(CurrentAttack().index).GetAttackChoice() : HeroAt(CurrentAttack().index).GetAttackChoice());
+ AttackChoice &ac = CurrentAttack().GetAttackChoice(*this);
if (ac.GetType() == AttackChoice::DEFEND) return;
TargetSelection &ts(ac.Selection());
- if (CurrentAttack().isMonster) {
- const Stats &attackerStats(MonsterAt(CurrentAttack().index).GetStats());
- CalculateDamage(attackerStats, ts);
- } else {
- const Stats &attackerStats(HeroAt(CurrentAttack().index).GetStats());
- CalculateDamage(attackerStats, ts);
- }
+ const Stats &attackerStats = CurrentAttack().GetStats(*this);
+ CalculateDamage(attackerStats, ts);
}
-void BattleState::DecideMonsterAttack(Monster &m) const {
+void BattleState::DecideMonsterAttack(Monster &m) {
AttackChoice &ac(m.GetAttackChoice());
TargetSelection &ts(ac.Selection());
ac.Reset();
ts.Select(target);
}
+void BattleState::DecideCapsuleAttack() {
+ AttackChoice &ac(capsule.GetAttackChoice());
+ TargetSelection &ts(ac.Selection());
+ ac.Reset();
+ int target(rand() % monsters.size());
+ while (!MonsterPositionOccupied(target)) {
+ target = rand() % monsters.size();
+ }
+ ac.SetType(AttackChoice::SWORD);
+ ts.SelectMonsters();
+ ts.SetSingle();
+ ts.Select(target);
+}
+
+AttackChoice &BattleState::Order::GetAttackChoice(BattleState &b) const {
+ switch (by) {
+ case HERO:
+ return b.HeroAt(index).GetAttackChoice();
+ case CAPSULE:
+ return b.GetCapsule().GetAttackChoice();
+ case MONSTER:
+ return b.MonsterAt(index).GetAttackChoice();
+ default:
+ throw std::runtime_error("invalid case in BttleStats::Order::GetAttackChoice()");
+ }
+}
+
+Stats &BattleState::Order::GetStats(BattleState &b) const {
+ switch (by) {
+ case HERO:
+ return b.HeroAt(index).GetStats();
+ case CAPSULE:
+ return b.GetCapsule().GetStats();
+ case MONSTER:
+ return b.MonsterAt(index).GetStats();
+ default:
+ throw std::runtime_error("invalid case in BttleStats::Order::GetAttackChoice()");
+ }
+}
+
void BattleState::CalculateDamage(const Stats &attackerStats, TargetSelection &ts) const {
bool hitSome(false);
if (ts.TargetsMonsters()) {
void BattleState::ApplyDamage() {
if (attackCursor < 0) return;
- AttackChoice &ac(CurrentAttack().isMonster ? MonsterAt(CurrentAttack().index).GetAttackChoice() : HeroAt(CurrentAttack().index).GetAttackChoice());
+ AttackChoice &ac = CurrentAttack().GetAttackChoice(*this);
TargetSelection &ts(ac.Selection());
if (ts.TargetsMonsters()) {
for (int i(0); i < MaxMonsters(); ++i) {
}
AttackChoice &BattleState::CurrentAttackAttackChoice() {
- if (CurrentAttack().isMonster) {
- return MonsterAt(CurrentAttack().index).GetAttackChoice();
- } else {
- return HeroAt(CurrentAttack().index).GetAttackChoice();
- }
+ return CurrentAttack().GetAttackChoice(*this);
}
void BattleState::ClearAllAttacks() {
for (int i(0); i < MaxMonsters(); ++i) {
MonsterAt(i).GetAttackChoice() = AttackChoice(this);
}
+ capsule.GetAttackChoice() = AttackChoice(this);
attackOrder.clear();
}
const HeroTag &HeroTagAt(int index) const { assert(index >= 0 && index < NumHeroes()); return heroTags[index]; }
const geometry::Vector<int> &HeroTagPositionAt(int index) const { assert(index >= 0 && index < NumHeroes()); return heroTagPositions[index]; }
+ Capsule &GetCapsule() { return capsule; }
+
bool HasChosenAttackType() const { return ActiveHero().GetAttackChoice().GetType() != AttackChoice::UNDECIDED; }
bool AttackSelectionDone() const { return activeHero >= numHeroes; }
void SetRunaway() { ranAway = true; }
struct Order {
- Order(int index, bool isMonster)
- : index(index), isMonster(isMonster) { }
+ enum Performer {
+ HERO,
+ CAPSULE,
+ MONSTER,
+ };
+ Order(Performer by, int index = 0)
+ : index(index), by(by) { }
+ AttackChoice &GetAttackChoice(BattleState &) const;
+ common::Stats &GetStats(BattleState &) const;
+ bool IsHero() const { return by == HERO; }
+ bool IsCapsule() const { return by == CAPSULE; }
+ bool IsMonster() const { return by == MONSTER; }
int index;
- bool isMonster;
+ Performer by;
};
void CalculateAttackOrder();
private:
void LoadInventory();
- void DecideMonsterAttack(Monster &) const;
+ void DecideMonsterAttack(Monster &);
+ void DecideCapsuleAttack();
void CalculateDamage(const common::Stats &attackerStats, TargetSelection &targets) const;
Uint16 CalculateDamage(const common::Stats &attacker, const common::Stats &defender) const;
#include "../graphics/fwd.h"
#include "../graphics/Menu.h"
-#include <vector>
-#include <SDL.h>
-
namespace battle {
class Capsule {
#include "NumberAnimation.h"
#include "../geometry/Vector.h"
+#include "../graphics/Sprite.h"
using app::State;
using geometry::Vector;
}
void TargetSelection::MoveRight() {
+ assert(battle);
if (TargetsMonsters()) {
cursor = (cursor + 1) % battle->MaxMonsters();
while (!battle->MonsterPositionOccupied(cursor)) {
}
void TargetSelection::MoveDown() {
+ assert(battle);
if (TargetsMonsters()) {
SelectHeroes();
return;
}
void TargetSelection::MoveLeft() {
+ assert(battle);
if (TargetsMonsters()) {
cursor = (cursor + battle->MaxMonsters() - 1) % battle->MaxMonsters();
FindNextEnemy();
}
void TargetSelection::FindNextEnemy() {
+ assert(battle);
int start(cursor);
while (!battle->MonsterPositionOccupied(cursor)) {
cursor = (cursor + battle->MaxMonsters() - 1) % battle->MaxMonsters();
#ifndef BATTLE_TARGETSELECTION_H_
#define BATTLE_TARGETSELECTION_H_
-#include "fwd.h"
-#include "../common/fwd.h"
+namespace common {
+ class TargetingMode;
+}
#include <vector>
namespace battle {
+class BattleState;
+
class TargetSelection {
public:
battle->CalculateDamage();
- if (battle->CurrentAttack().isMonster) {
+ if (battle->CurrentAttack().IsMonster()) {
Monster &monster(battle->MonsterAt(battle->CurrentAttack().index));
titleBarText = monster.Name();
targetAnimation = AnimationRunner(monster.MeleeAnimation());
moveAnimation = AnimationRunner(monster.AttackAnimation());
monster.SetAnimation(moveAnimation);
AddNumberAnimations(battle->MonsterAt(battle->CurrentAttack().index).GetAttackChoice().Selection());
+ } else if (battle->CurrentAttack().IsCapsule()) {
+ Capsule &capsule(battle->GetCapsule());
+ titleBarText = capsule.Name();
+ targetAnimation = AnimationRunner(capsule.MeleeAnimation());
+ moveAnimation = AnimationRunner(capsule.AttackAnimation());
+ capsule.SetAnimation(moveAnimation);
+ AddNumberAnimations(capsule.GetAttackChoice().Selection());
} else {
Hero &hero(battle->HeroAt(battle->CurrentAttack().index));
const AttackChoice &ac(battle->HeroAt(battle->CurrentAttack().index).GetAttackChoice());
if (titleBarText) titleBarTimer = GraphicsTimers().StartCountdown(850);
if (moveAnimation.Valid()) {
moveAnimation.Start(*this);
- if (battle->CurrentAttack().isMonster) {
+ if (battle->CurrentAttack().IsMonster()) {
battle->MonsterAt(battle->CurrentAttack().index).SetAnimation(moveAnimation);
- } else {
+ } else if (battle->CurrentAttack().IsHero()) {
battle->HeroAt(battle->CurrentAttack().index).SetAnimation(moveAnimation);
+ } else {
+ battle->GetCapsule().SetAnimation(moveAnimation);
}
}
if (targetAnimation.Valid()) {
void PerformAttacks::ResetAnimation() {
if (moveAnimation.Valid()) {
moveAnimation.Clear();
- if (!battle->CurrentAttack().isMonster) {
+ if (!battle->CurrentAttack().IsMonster()) {
battle->HeroAt(battle->CurrentAttack().index).GetAnimation().Clear();
}
}
#include "Animation.h"
#include "Sprite.h"
+#include "../app/Application.h"
+#include "../app/State.h"
#include "../loader/Interpreter.h"
#include "../loader/TypeDescription.h"
+#include <cassert>
+
using loader::FieldDescription;
using loader::Interpreter;
using loader::TypeDescription;
td.AddField("repeat", FieldDescription(((char *)&a.repeat) - ((char *)&a) - offset, Interpreter::BOOLEAN_ID).SetDescription("whether the animation should start over at the beginning after reaching the last frame"));
}
+
+void AnimationRunner::Start(app::State &ctrl) {
+ assert(animation);
+ timer = ctrl.GraphicsTimers().StartInterval(animation->FrameTime());
+}
+
+void AnimationRunner::Start(app::Application &ctrl) {
+ assert(animation);
+ timer = ctrl.GlobalTimers().StartInterval(animation->FrameTime());
+}
+
+void AnimationRunner::Stop() {
+ timer = app::Timer<Uint32>();
+}
+
+bool AnimationRunner::Started() const {
+ return timer.Started();
+}
+
+bool AnimationRunner::Running() const {
+ return animation
+ && timer.Running()
+ && (animation->Repeat()
+ || timer.Iteration() < animation->NumFrames());
+}
+
+bool AnimationRunner::Finished() const {
+ return Started() && !Running();
+}
+
+bool AnimationRunner::JustFinished() const {
+ return animation
+ && timer.JustHit()
+ && timer.Iteration() == animation->NumFrames();
+}
+
+
+void AnimationRunner::Draw(SDL_Surface *dest, geometry::Vector<int> position) const {
+ GetSprite()->Draw(dest,
+ position + animation->Offset(Frame()),
+ animation->Col(Frame()) + ColOffset(),
+ animation->Row(Frame()) + RowOffset());
+}
+
+void AnimationRunner::DrawTopRight(SDL_Surface *dest, geometry::Vector<int> position) const {
+ geometry::Vector<int> offset(-GetSprite()->Width(), 0);
+ Draw(dest, position + offset);
+}
+
+void AnimationRunner::DrawCenter(SDL_Surface *dest, geometry::Vector<int> position) const {
+ Draw(dest, position - (GetSprite()->Size() / 2));
+}
+
+void AnimationRunner::DrawCenterBottom(SDL_Surface *dest, geometry::Vector<int> position) const {
+ geometry::Vector<int> offset(-GetSprite()->Width() / 2, -GetSprite()->Height());
+ Draw(dest, position + offset);
+}
+
+int AnimationRunner::Frame() const {
+ return Running()
+ ? ((timer.Iteration() + frameShift) % animation->NumFrames())
+ : 0;
+}
+
+
}
#ifndef GRAPHICS_ANIMATION_H_
#define GRAPHICS_ANIMATION_H_
-#include "Sprite.h"
-#include "../app/Application.h"
-#include "../app/State.h"
+namespace app {
+ class Application;
+ class State;
+}
+namespace loader {
+ class TypeDescription;
+}
+
#include "../app/Timer.h"
-#include "../loader/fwd.h"
#include "../geometry/Vector.h"
#include <memory>
namespace graphics {
+class Sprite;
+
class Animation {
public:
bool Valid() const { return animation; }
void Clear() { animation = 0; timer = app::Timer<Uint32>(); }
- void Start(app::State &ctrl) {
- timer = ctrl.GraphicsTimers().StartInterval(animation->FrameTime());
- }
- void Start(app::Application &ctrl) {
- timer = ctrl.GlobalTimers().StartInterval(animation->FrameTime());
- }
- void Stop() {
- timer = app::Timer<Uint32>();
- }
- bool Started() const {
- return timer.Started();
- }
- bool Running() const {
- return timer.Running() && (animation->Repeat() || timer.Iteration() < animation->NumFrames());
- }
- bool Finished() const {
- return Started() && !Running();
- }
- bool JustFinished() const {
- return timer.JustHit() && timer.Iteration() == animation->NumFrames();
- }
+ void Start(app::State &ctrl);
+ void Start(app::Application &ctrl);
+ void Stop();
+ bool Started() const;
+ bool Running() const;
+ bool Finished() const;
+ bool JustFinished() const;
const app::Timer<Uint32> &GetTimer() { return timer; }
void ChangeSprite(const Sprite *s) { sprite = s; }
const Sprite *GetSprite() const { return sprite ? sprite : animation->GetSprite(); }
- void Draw(SDL_Surface *dest, geometry::Vector<int> position) const {
- GetSprite()->Draw(dest, position + animation->Offset(Frame()), animation->Col(Frame()) + ColOffset(), animation->Row(Frame()) + RowOffset());
- }
- void DrawTopRight(SDL_Surface *dest, geometry::Vector<int> position) const {
- geometry::Vector<int> offset(-GetSprite()->Width(), 0);
- Draw(dest, position + offset);
- }
- void DrawCenter(SDL_Surface *dest, geometry::Vector<int> position) const {
- Draw(dest, position - (GetSprite()->Size() / 2));
- }
- void DrawCenterBottom(SDL_Surface *dest, geometry::Vector<int> position) const {
- geometry::Vector<int> offset(-GetSprite()->Width() / 2, -GetSprite()->Height());
- Draw(dest, position + offset);
- }
-
- int Frame() const { return Running() ? ((timer.Iteration() + frameShift) % animation->NumFrames()) : 0; }
+ void Draw(SDL_Surface *dest, geometry::Vector<int> position) const;
+ void DrawTopRight(SDL_Surface *dest, geometry::Vector<int> position) const;
+ void DrawCenter(SDL_Surface *dest, geometry::Vector<int> position) const;
+ void DrawCenterBottom(SDL_Surface *dest, geometry::Vector<int> position) const;
+
+ int Frame() const;
private:
const Animation *animation;
#include "../loader/Interpreter.h"
#include "../loader/TypeDescription.h"
+using geometry::Vector;
using loader::FieldDescription;
using loader::Interpreter;
using loader::TypeDescription;
namespace graphics {
+ComplexAnimation::ComplexAnimation()
+: frames(0)
+, numFrames(0) {
+
+}
+
+ComplexAnimation::ComplexAnimation(
+ const Sprite *sprite,
+ int frameTime,
+ bool repeat)
+: Animation(sprite, frameTime, repeat)
+, frames(0)
+, numFrames(0) {
+
+}
+
+
+int ComplexAnimation::NumFrames() const {
+ return numFrames;
+}
+
+int ComplexAnimation::Col(int frame) const {
+ return frames[frame].col;
+}
+
+int ComplexAnimation::Row(int frame) const {
+ return frames[frame].row;
+}
+
+Vector<int> ComplexAnimation::Offset(int frame) const {
+ return frames[frame].disposition;
+}
+
+
void ComplexAnimation::CreateTypeDescription() {
ComplexAnimation ca;
Animation *a(&ca);
static const int TYPE_ID = 402;
public:
- ComplexAnimation() : frames(0), numFrames(0) { }
- ComplexAnimation(const Sprite *sprite, int frameTime, bool repeat = false)
- : Animation(sprite, frameTime, repeat), frames(0), numFrames(0) { }
+ ComplexAnimation();
+ ComplexAnimation(const Sprite *sprite, int frameTime, bool repeat = false);
public:
struct FrameProp {
static void Construct(void *);
protected:
- virtual int NumFrames() const { return numFrames; };
- virtual int Col(int frame) const { return frames[frame].col; }
- virtual int Row(int frame) const { return frames[frame].row; }
- virtual geometry::Vector<int> Offset(int frame) const { return frames[frame].disposition; }
+ virtual int NumFrames() const;
+ virtual int Col(int frame) const;
+ virtual int Row(int frame) const;
+ virtual geometry::Vector<int> Offset(int frame) const;
private:
const FrameProp *frames;
namespace graphics {
+SimpleAnimation::SimpleAnimation()
+: numFrames(0)
+, col(0)
+, row(0) {
+
+}
+
+SimpleAnimation::SimpleAnimation(
+ const Sprite *sprite,
+ int frameTime,
+ int numFrames,
+ int col,
+ int row,
+ bool repeat)
+: Animation(sprite, frameTime, repeat)
+, numFrames(numFrames)
+, col(col)
+, row(row) {
+
+}
+
+
+int SimpleAnimation::NumFrames() const {
+ return numFrames;
+}
+
+int SimpleAnimation::Col(int frame) const {
+ return col;
+}
+
+int SimpleAnimation::Row(int frame) const {
+ return row + frame;
+}
+
+
void SimpleAnimation::CreateTypeDescription() {
SimpleAnimation sa;
Animation *a(&sa);
static const int TYPE_ID = 408;
public:
- SimpleAnimation()
- : numFrames(0), col(0), row(0) { }
- SimpleAnimation(const Sprite *sprite, int frameTime, int numFrames, int col = 0, int row = 0, bool repeat = false)
- : Animation(sprite, frameTime, repeat), numFrames(numFrames), col(col), row(row) { }
+ SimpleAnimation();
+ SimpleAnimation(const Sprite *sprite, int frameTime, int numFrames,
+ int col = 0, int row = 0, bool repeat = false);
public:
void SetNumFrames(int n) { numFrames = n; }
static void Construct(void *);
protected:
- virtual int NumFrames() const { return numFrames; };
- virtual int Col(int frame) const { return col; }
- virtual int Row(int frame) const { return row + frame; }
+ virtual int NumFrames() const;
+ virtual int Col(int frame) const;
+ virtual int Row(int frame) const;
private:
int numFrames;
gameState.heroes[3].MapEntity().SetFlags(Entity::FLAG_NONBLOCKING);
gameState.heroes[2].MapEntity().AddFollower(&gameState.heroes[3].MapEntity());
+ graphics::Sprite flashSprite(IMG_Load("test-data/flash.png"), 96, 96);
+ graphics::ComplexAnimation flashAttackAnimation(&flashSprite, 132);
+ graphics::ComplexAnimation::FrameProp flashAttackFrames[4];
+ flashAttackFrames[0] = graphics::ComplexAnimation::FrameProp(0, 1, Vector<int>(0, -16));
+ flashAttackFrames[1] = graphics::ComplexAnimation::FrameProp(0, 0, Vector<int>(0, -16));
+ flashAttackFrames[2] = graphics::ComplexAnimation::FrameProp(0, 1, Vector<int>(0, -16));
+ flashAttackFrames[3] = graphics::ComplexAnimation::FrameProp(0, 0, Vector<int>(0, -16));
+ flashAttackAnimation.SetFrames(flashAttackFrames, 4);
+ Capsule capsule;
+ capsule.SetName("Flash");
+ capsule.SetHealth(5, 5);
+ capsule.SetLevel(1);
+ capsule.GetStats().SetAttack(12);
+ capsule.GetStats().SetDefense(18);
+ capsule.GetStats().SetStrength(2);
+ capsule.GetStats().SetAgility(11);
+ capsule.GetStats().SetIntelligence(16);
+ capsule.GetStats().SetGut(23);
+ capsule.GetStats().SetMagicResistance(11);
+ capsule.SetBattleSprite(&flashSprite);
+ capsule.SetAttackAnimation(&flashAttackAnimation);
+ capsule.SetMeleeAnimation(gameState.heroes[0].MeleeAnimation());
+
InitScreen screen(width, height);
app::State *state(0);
if (battle) {
- graphics::Sprite flashSprite(IMG_Load("test-data/flash.png"), 96, 96);
- Capsule capsule;
- capsule.SetName("Flash");
- capsule.SetHealth(13, 13);
- capsule.SetBattleSprite(&flashSprite);
-
BattleState *battleState(new BattleState(&gameConfig, bg, &monstersLayout));
battleState->AddMonster(monster);
battleState->AddMonster(monster);
#include "PartyMenu.h"
#include "Resources.h"
+#include "../app/Application.h"
+#include "../app/Input.h"
#include "../common/GameConfig.h"
#include "../common/GameState.h"
#include "../graphics/Font.h"
#include "PartyMenu.h"
#include "Resources.h"
+#include "../app/Application.h"
#include "../app/Input.h"
#include "../common/GameConfig.h"
#include "../common/GameState.h"
#include "PartyMenu.h"
#include "Resources.h"
+#include "../app/Application.h"
#include "../app/Input.h"
#include "../common/GameConfig.h"
#include "../common/GameState.h"
#include "HeroStatus.h"
#include "PartyMenu.h"
#include "Resources.h"
+#include "../app/Application.h"
#include "../app/Input.h"
#include "../common/GameConfig.h"
#include "../common/GameState.h"