<Option object_output="obj\W32_Debug\" />
<Option type="1" />
<Option compiler="gcc" />
- <Option parameters="test-data/constants.l2s test-data/ikaris.l2s test-data/items.l2s test-data/maps.l2s test-data/spells.l2s test-data/test.l2s" />
+ <Option parameters="test-data/capsules.l2s test-data/constants.l2s test-data/ikaris.l2s test-data/items.l2s test-data/maps.l2s test-data/spells.l2s test-data/test.l2s" />
<Option projectLinkerOptionsRelation="2" />
<Compiler>
<Add option="-g" />
<Option object_output="obj\W32\" />
<Option type="1" />
<Option compiler="gcc" />
- <Option parameters="test-data/constants.l2s test-data/ikaris.l2s test-data/items.l2s test-data/maps.l2s test-data/spells.l2s test-data/test.l2s" />
+ <Option parameters="test-data/capsules.l2s test-data/constants.l2s test-data/ikaris.l2s test-data/items.l2s test-data/maps.l2s test-data/spells.l2s test-data/test.l2s" />
<Option projectLinkerOptionsRelation="2" />
<Compiler>
<Add option="-O3" />
<Unit filename="src\battle\AttackTypeMenu.h" />
<Unit filename="src\battle\BattleState.cpp" />
<Unit filename="src\battle\BattleState.h" />
+ <Unit filename="src\battle\Capsule.cpp" />
+ <Unit filename="src\battle\Capsule.h" />
<Unit filename="src\battle\Hero.cpp" />
<Unit filename="src\battle\Hero.h" />
<Unit filename="src\battle\HeroTag.cpp" />
<Unit filename="src\battle\states\SelectTarget.h" />
<Unit filename="src\battle\states\SwapHeroes.cpp" />
<Unit filename="src\battle\states\SwapHeroes.h" />
+ <Unit filename="src\common\Capsule.cpp" />
+ <Unit filename="src\common\Capsule.h" />
<Unit filename="src\common\GameConfig.cpp" />
<Unit filename="src\common\GameConfig.h" />
<Unit filename="src\common\GameState.cpp" />
attackOrder.push_back(Order(Order::MONSTER, i));
MonsterAt(i).GetAttackChoice() = AttackChoice(this);
}
- if (capsule.Health() > 0) {
+ if (capsule.Active() && capsule.Health() > 0) {
attackOrder.push_back(Order(Order::CAPSULE));
}
std::sort(attackOrder.begin(), attackOrder.end(), OrderCompare(this));
} else if (attackOrder[attackCursor].IsHero()) {
if (HeroAt(attackOrder[attackCursor].index).Health() > 0) break;
} else {
- if (capsule.Health() > 0) break;
+ if (capsule.Active() && capsule.Health() > 0) break;
}
++attackCursor;
}
}
void BattleState::RenderCapsule(SDL_Surface *screen, const Vector<int> &offset) {
- if (capsule.Health() <= 0) return;
+ if (!capsule.Active() || capsule.Health() <= 0) return;
if (capsule.GetAnimation().Running()) {
capsule.GetAnimation().DrawCenter(screen, capsule.Position() + offset);
} else {
#include "Capsule.h"
+#include "../common/Capsule.h"
+
+#include <cassert>
+
+using graphics::Animation;
+using graphics::Sprite;
+
namespace battle {
-Capsule::Capsule()
-: name(0)
+Capsule::Capsule(common::Capsule *master)
+: master(master)
+, health(master ? master->MaxHealth() : 0) {
+ if (master) {
+ stats = master->GetStats();
+ }
+}
+
-, maxHealth(0)
-, health(0)
-, maxMana(0)
-, mana(0)
+const char *Capsule::Name() const {
+ assert(master);
+ return master->Name();
+}
-, level(0)
+Uint8 Capsule::Level() const {
+ assert(master);
+ return master->Level();
+}
-, battleSprite(0)
-, meleeAnimation(0)
-, attackAnimation(0)
-, spellAnimation(0) {
+const Sprite *Capsule::Sprite() {
+ assert(master);
+ return master->BattleSprite();
+}
+const Animation *Capsule::MeleeAnimation() const {
+ assert(master);
+ return master->MeleeAnimation();
}
+const Animation *Capsule::AttackAnimation() const {
+ assert(master);
+ return master->AttackAnimation();
+}
+
+const Animation *Capsule::SpellAnimation() const {
+ assert(master);
+ return master->SpellAnimation();
+}
+
+
+Uint16 Capsule::MaxHealth() const {
+ assert(master);
+ return master->MaxHealth();
+}
+
+Uint16 Capsule::Health() const {
+ return health;
+}
+
+int Capsule::RelativeHealth(int max) const {
+ return Health() * max / MaxHealth();
+}
void Capsule::SubtractHealth(int amount) {
if (amount > health) {
#ifndef BATTLE_CAPSULE_H_
#define BATTLE_CAPSULE_H_
+namespace common {
+ class Capsule;
+}
+
#include "AttackChoice.h"
-#include "../common/fwd.h"
#include "../common/Stats.h"
#include "../geometry/Vector.h"
#include "../graphics/Animation.h"
class Capsule {
public:
- Capsule();
+ Capsule(common::Capsule *master = 0);
public:
- const char *Name() const { return name; }
- Uint8 Level() const { return level; }
- const graphics::Sprite *Sprite() const { return battleSprite; }
+ bool Active() const { return master; }
- Uint16 MaxHealth() const { return maxHealth; }
- Uint16 Health() const { return health; }
- int RelativeHealth(int max) const { return Health() * max / MaxHealth(); }
- void SubtractHealth(int amount);
+ const char *Name() const;
+ Uint8 Level() const;
+ const graphics::Sprite *Sprite();
- Uint16 MaxMana() const { return maxMana; }
- Uint16 Mana() const { return mana; }
- int RelativeMana(int max) const { return MaxMana() == 0 ? 0 : Mana() * max / MaxMana(); }
- bool CanUseMagic() const { return MaxMana() > 0; }
+ Uint16 MaxHealth() const;
+ Uint16 Health() const;
+ int RelativeHealth(int max) const;
+ void SubtractHealth(int amount);
common::Stats &GetStats() { return stats; }
const common::Stats &GetStats() const { return stats; }
const graphics::AnimationRunner &GetAnimation() const { return animation; }
void SetAnimation(const graphics::AnimationRunner &a) { animation = a; }
- const graphics::Animation *MeleeAnimation() const { return meleeAnimation; }
- const graphics::Animation *AttackAnimation() const { return attackAnimation; }
- const graphics::Animation *SpellAnimation() const { return spellAnimation; }
+ const graphics::Animation *MeleeAnimation() const;
+ const graphics::Animation *AttackAnimation() const;
+ const graphics::Animation *SpellAnimation() const;
geometry::Vector<int> &Position() { return position; }
const geometry::Vector<int> &Position() const { return position; }
AttackChoice &GetAttackChoice() { return attackChoice; }
const AttackChoice &GetAttackChoice() const { return attackChoice; }
-// temporary setters
-public:
- void SetName(const char *n) { name = n; }
- void SetHealth(int max, int cur) { maxHealth = max; health = cur; }
- void SetMana(int max, int cur) { maxMana = max; mana = cur; }
- void SetLevel(int l) { level = l; }
- void SetBattleSprite(graphics::Sprite *s) { battleSprite = s; }
- void SetMeleeAnimation(graphics::Animation *a) { meleeAnimation = a; }
- void SetAttackAnimation(graphics::Animation *a) { attackAnimation = a; }
- void SetSpellAnimation(graphics::Animation *a) { spellAnimation = a; }
-
private:
- const char *name;
-
- int maxHealth, health;
- int maxMana, mana;
-
- int level;
+ common::Capsule *master;
- graphics::Sprite *battleSprite;
- graphics::Animation *meleeAnimation;
- graphics::Animation *attackAnimation;
- graphics::Animation *spellAnimation;
+ int health;
graphics::AnimationRunner animation;
geometry::Vector<int> position;
--- /dev/null
+#include "Capsule.h"
+
+#include "../common/Spell.h"
+#include "../common/Stats.h"
+#include "../graphics/Animation.h"
+#include "../graphics/Sprite.h"
+#include "../loader/Interpreter.h"
+#include "../loader/TypeDescription.h"
+
+#include <cassert>
+
+using common::Spell;
+using common::Stats;
+using graphics::Animation;
+using graphics::Sprite;
+using loader::FieldDescription;
+using loader::Interpreter;
+using loader::TypeDescription;
+
+namespace common {
+
+Capsule::Capsule()
+: name("")
+, alignment("")
+
+, maxHealth(0)
+
+, level(1)
+, experience(0)
+
+, levelLadder(0)
+, numLevels(0)
+
+, classes(0)
+, numClasses(0)
+, curClass(0)
+, maxClass(0) {
+
+}
+
+
+Uint16 Capsule::MaxHealth() const {
+ return maxHealth + GetClass().healthBoost;
+}
+
+
+Stats Capsule::GetStats() {
+ return stats + GetClass().statBoost;
+}
+
+int Capsule::NextLevel() const {
+ int levelOffset(Level() - 1);
+ if (levelOffset < numLevels) {
+ return levelLadder[levelOffset] - Experience();
+ } else {
+ return 0;
+ }
+}
+
+
+Sprite *Capsule::BattleSprite() {
+ return GetClass().battleSprite;
+}
+
+const Sprite *Capsule::BattleSprite() const {
+ return GetClass().battleSprite;
+}
+
+Animation *Capsule::MeleeAnimation() {
+ return GetClass().meleeAnimation;
+}
+
+Animation *Capsule::AttackAnimation() {
+ return GetClass().attackAnimation;
+}
+
+Animation *Capsule::SpellAnimation() {
+ return GetClass().spellAnimation;
+}
+
+
+Capsule::Class &Capsule::GetClass() {
+ assert(classes && curClass < numClasses);
+ return classes[curClass];
+}
+
+const Capsule::Class &Capsule::GetClass() const {
+ assert(classes && curClass < numClasses);
+ return classes[curClass];
+}
+
+
+Capsule::Class::Class()
+: name(0)
+, tribe(0)
+, battleSprite(0)
+, meleeAnimation(0)
+, attackAnimation(0)
+, spellAnimation(0)
+
+, healthBoost(0) {
+ attacks[0] = 0;
+ attacks[1] = 0;
+ attacks[2] = 0;
+}
+
+
+void Capsule::CreateTypeDescription() {
+ Capsule c;
+
+ TypeDescription &td(TypeDescription::Create(TYPE_ID, "Capsule"));
+ td.SetConstructor(&Construct);
+ td.SetSize(sizeof(Capsule));
+
+ td.AddField("name", FieldDescription(((char *)&c.name) - ((char *)&c), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("alignment", FieldDescription(((char *)&c.alignment) - ((char *)&c), Interpreter::STRING_ID).SetReferenced());
+
+ td.AddField("maxHealth", FieldDescription(((char *)&c.maxHealth) - ((char *)&c), Interpreter::NUMBER_ID));
+
+ td.AddField("stats", FieldDescription(((char *)&c.stats) - ((char *)&c), Stats::TYPE_ID));
+
+ td.AddField("level", FieldDescription(((char *)&c.level) - ((char *)&c), Interpreter::NUMBER_ID));
+ td.AddField("experience", FieldDescription(((char *)&c.experience) - ((char *)&c), Interpreter::NUMBER_ID));
+
+ td.AddField("ladder", FieldDescription(((char *)&c.levelLadder) - ((char *)&c), Interpreter::NUMBER_ID).SetReferenced().SetAggregate());
+
+ td.AddField("classes", FieldDescription(((char *)&c.classes) - ((char *)&c), Class::TYPE_ID).SetReferenced().SetAggregate());
+ td.AddField("class", FieldDescription(((char *)&c.curClass) - ((char *)&c), Interpreter::NUMBER_ID));
+ td.AddField("maxClass", FieldDescription(((char *)&c.maxClass) - ((char *)&c), Interpreter::NUMBER_ID));
+
+ Class::CreateTypeDescription();
+}
+
+void Capsule::Construct(void *data) {
+ new (data) Capsule;
+}
+
+
+void Capsule::Class::CreateTypeDescription() {
+ Class c;
+
+ TypeDescription &td(TypeDescription::Create(TYPE_ID, "CapsuleClass"));
+ td.SetConstructor(&Construct);
+ td.SetSize(sizeof(Class));
+
+ td.AddField("name", FieldDescription(((char *)&c.name) - ((char *)&c), Interpreter::STRING_ID).SetReferenced());
+ td.AddField("tribe", FieldDescription(((char *)&c.tribe) - ((char *)&c), Interpreter::STRING_ID).SetReferenced());
+
+ td.AddField("attack1", FieldDescription(((char *)&c.attacks[0]) - ((char *)&c), Spell::TYPE_ID).SetReferenced());
+ td.AddField("attack2", FieldDescription(((char *)&c.attacks[1]) - ((char *)&c), Spell::TYPE_ID).SetReferenced());
+ td.AddField("attack3", FieldDescription(((char *)&c.attacks[2]) - ((char *)&c), Spell::TYPE_ID).SetReferenced());
+
+ td.AddField("battleSprite", FieldDescription(((char *)&c.battleSprite) - ((char *)&c), Sprite::TYPE_ID).SetReferenced());
+ td.AddField("meleeAnimation", FieldDescription(((char *)&c.meleeAnimation) - ((char *)&c), Animation::TYPE_ID).SetReferenced());
+ td.AddField("attackAnimation", FieldDescription(((char *)&c.attackAnimation) - ((char *)&c), Animation::TYPE_ID).SetReferenced());
+ td.AddField("spellAnimation", FieldDescription(((char *)&c.spellAnimation) - ((char *)&c), Animation::TYPE_ID).SetReferenced());
+
+ td.AddField("healthBoost", FieldDescription(((char *)&c.healthBoost) - ((char *)&c), Interpreter::NUMBER_ID));
+ td.AddField("statBoost", FieldDescription(((char *)&c.statBoost) - ((char *)&c), Stats::TYPE_ID));
+}
+
+void Capsule::Class::Construct(void *data) {
+ new (data) Capsule::Class;
+}
+
+}
--- /dev/null
+#ifndef COMMON_CAPSULE_H_
+#define COMMON_CAPSULE_H_
+
+namespace graphics {
+ class Animation;
+ class Sprite;
+}
+
+#include "../common/Stats.h"
+
+#include <SDL.h>
+
+namespace common {
+
+class Spell;
+
+class Capsule {
+
+public:
+ static const int TYPE_ID = 307;
+
+public:
+ Capsule();
+
+ const char *Name() const { return name; }
+ const char *Alignment() const { return alignment; }
+
+ Uint16 MaxHealth() const;
+
+ Stats GetStats();
+
+ Uint8 Level() const { return level; }
+ int Experience() const { return experience; }
+ int NextLevel() const;
+
+ graphics::Sprite *BattleSprite();
+ const graphics::Sprite *BattleSprite() const;
+ graphics::Animation *MeleeAnimation();
+ graphics::Animation *AttackAnimation();
+ graphics::Animation *SpellAnimation();
+
+ static void CreateTypeDescription();
+ static void Construct(void *);
+
+private:
+ struct Class {
+ static const int TYPE_ID = 308;
+
+ Class();
+
+ static void CreateTypeDescription();
+ static void Construct(void *);
+
+ const char *name;
+ const char *tribe;
+ Spell *attacks[3];
+ graphics::Sprite *battleSprite;
+ graphics::Animation *meleeAnimation;
+ graphics::Animation *attackAnimation;
+ graphics::Animation *spellAnimation;
+
+ int healthBoost;
+ Stats statBoost;
+ };
+
+ Class &GetClass();
+ const Class &GetClass() const;
+
+ const char *name;
+ const char *alignment;
+
+ int maxHealth;
+
+ Stats stats;
+
+ int level;
+ int experience;
+
+ int *levelLadder;
+ int numLevels;
+
+ Class *classes;
+ int numClasses, curClass, maxClass;
+
+};
+
+}
+
+#endif /* COMMON_CAPSULE_H_ */
};
+
+inline Stats operator +(const Stats &lhs, const Stats &rhs) {
+ return Stats(
+ lhs.Attack() + rhs.Attack(),
+ lhs.Defense() + rhs.Defense(),
+ lhs.Strength() + rhs.Strength(),
+ lhs.Agility() + rhs.Agility(),
+ lhs.Intelligence() + rhs.Intelligence(),
+ lhs.Gut() + rhs.Gut(),
+ lhs.MagicResistance() + rhs.MagicResistance());
+}
+
}
#endif /* COMMON_STATS_H_ */
namespace common {
+class Capsule;
struct GameConfig;
struct GameState;
class Hero;
#include "../battle/Resources.h"
#include "../battle/Monster.h"
#include "../battle/PartyLayout.h"
+#include "../common/Capsule.h"
#include "../common/Hero.h"
#include "../common/Item.h"
#include "../common/Spell.h"
using battle::Monster;
using battle::PartyLayout;
+using common::Capsule;
using common::Hero;
using common::Item;
using common::Spell;
intp.GetObject(battle::Resources::TYPE_ID, ident));
}
+Capsule *Caster::GetCapsule(const string &ident) {
+ return reinterpret_cast<Capsule *>(intp.GetObject(Capsule::TYPE_ID, ident));
+}
+
Hero *Caster::GetHero(const string &ident) {
return reinterpret_cast<Hero *>(intp.GetObject(Hero::TYPE_ID, ident));
}
public:
battle::Resources *GetBattleResources(const std::string &identifier);
+ common::Capsule *GetCapsule(const std::string &identifier);
common::Hero *GetHero(const std::string &identifier);
common::Item *GetItem(const std::string &identifier);
map::Map *GetMap(const std::string &identifier);
#include "battle/Monster.h"
#include "battle/PartyLayout.h"
#include "battle/Resources.h"
+#include "common/Capsule.h"
#include "common/GameConfig.h"
#include "common/GameState.h"
#include "common/Hero.h"
using app::Arguments;
using app::Input;
using battle::BattleState;
-using battle::Capsule;
using battle::Monster;
using battle::PartyLayout;
+using common::Capsule;
using common::GameConfig;
using common::GameState;
using common::Hero;
battle::Monster::CreateTypeDescription();
battle::PartyLayout::CreateTypeDescription();
+ common::Capsule::CreateTypeDescription();
common::Hero::CreateTypeDescription();
common::Ikari::CreateTypeDescription();
common::Item::CreateTypeDescription();
gameState.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);
battleState->AddMonster(monster);
battleState->AddMonster(monster);
battleState->AddMonster(monster);
- battleState->SetCapsule(capsule);
+ battleState->SetCapsule(caster.GetCapsule("flash"));
battleState->AddHero(gameState.heroes[0]);
battleState->AddHero(gameState.heroes[1]);
battleState->AddHero(gameState.heroes[2]);
--- /dev/null
+Capsule flash
--- /dev/null
+Sprite flashSprite {
+ image: :"flash.png",
+ size: <96, 96>
+}
+
+export Capsule flash {
+ name: "Flash",
+ alignment: "LIGHT",
+ maxHealth: 5,
+ stats: Stats {
+ atp: 12,
+ dfp: 18,
+ str: 2,
+ agl: 11,
+ int: 16,
+ gut: 23,
+ mgr: 11
+ },
+ classes: [ CapsuleClass
+ {
+ name: "4",
+ tribe: "Twinkle",
+ battleSprite: flashSprite,
+ meleeAnimation: SimpleAnimation {
+ sprite: Sprite {
+ image: :"melee-maxim.png",
+ size: <96,96>
+ },
+ frametime: 66, // two "frames"
+ framecount: 4
+ },
+ attackAnimation: ComplexAnimation {
+ sprite: flashSprite,
+ frametime: fourFramesTime,
+ repeat: false,
+ frames:
+ [ ComplexAnimationFrame
+ { column: 0, row: 1, disposition: < 0, -16> },
+ { column: 0, row: 0, disposition: < 0, -16> },
+ { column: 0, row: 1, disposition: < 0, -16> },
+ { column: 0, row: 0, disposition: < 0, -16> }
+ ]
+ },
+ healthBoost: 208,
+ statBoost: Stats {
+ atp: 38,
+ dfp: 71,
+ str: 24,
+ agl: 78,
+ int: 195,
+ gut: 12, // TODO: this is probably higher, but clipped at 199
+ mgr: 135
+ }
+ }
+ ]
+}