]> git.localhorst.tv Git - l2e.git/commitdiff
Merge branch 'loader'
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Mon, 3 Sep 2012 19:58:28 +0000 (21:58 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Mon, 3 Sep 2012 19:58:28 +0000 (21:58 +0200)
This introduces the capability of loading l2 formatted source files.
The syntax for those is described here:
http://luke.redirectme.net/redmine/projects/l2e/wiki/LoaderSource

Please note that this method of loading data is very inefficient, but
convenient. Efficiency issues will be adressed by a binary file format
which mentioned source files can be compiled to.

Also, some representations have been adapted to a more link-friendly
structure and most of the configuration code in main has been moved to
their respective l2s files residing in /test-data/ .

40 files changed:
Debug/makefile
Debug/sources.mk
Debug/src/loader/subdir.mk [new file with mode: 0644]
Release/makefile
Release/sources.mk
Release/src/loader/subdir.mk [new file with mode: 0644]
src/battle/BattleState.cpp
src/battle/Resources.h
src/battle/Stats.h
src/common/TargetingMode.h
src/graphics/Animation.h
src/graphics/Color.h [new file with mode: 0644]
src/graphics/ComplexAnimation.h
src/graphics/Font.cpp
src/graphics/Font.h
src/graphics/Frame.cpp
src/graphics/Frame.h
src/graphics/Gauge.cpp
src/graphics/Gauge.h
src/graphics/Menu.h
src/graphics/SimpleAnimation.h
src/graphics/Sprite.h
src/loader/Interpreter.cpp [new file with mode: 0644]
src/loader/Interpreter.h [new file with mode: 0644]
src/loader/ParsedSource.cpp [new file with mode: 0644]
src/loader/ParsedSource.h [new file with mode: 0644]
src/loader/Parser.cpp [new file with mode: 0644]
src/loader/Parser.h [new file with mode: 0644]
src/loader/Tokenizer.cpp [new file with mode: 0644]
src/loader/Tokenizer.h [new file with mode: 0644]
src/loader/utility.cpp [new file with mode: 0644]
src/loader/utility.h [new file with mode: 0644]
src/main.cpp
test-data/ikaris.l2h [new file with mode: 0644]
test-data/ikaris.l2s [new file with mode: 0644]
test-data/items.l2h [new file with mode: 0644]
test-data/items.l2s [new file with mode: 0644]
test-data/spells.l2h [new file with mode: 0644]
test-data/spells.l2s [new file with mode: 0644]
test-data/test.l2s [new file with mode: 0644]

index 571f839038d5b7a6f429940f4538574e0a8e5b9e..13878f316e329a04cd70183f468b17dd1ae5ccd1 100644 (file)
@@ -9,6 +9,7 @@ RM := rm -rf
 # All of the sources participating in the build are defined here
 -include sources.mk
 -include src/sdl/subdir.mk
+-include src/loader/subdir.mk
 -include src/graphics/subdir.mk
 -include src/common/subdir.mk
 -include src/battle/states/subdir.mk
index a9593d6f382b32a74482e79f6b2196a922794e3b..7aaa6cfb7a6cbee251461aacc619ec53c1a97322 100644 (file)
@@ -25,6 +25,7 @@ C_UPPER_DEPS :=
 SUBDIRS := \
 src/sdl \
 src \
+src/loader \
 src/graphics \
 src/common \
 src/battle/states \
diff --git a/Debug/src/loader/subdir.mk b/Debug/src/loader/subdir.mk
new file mode 100644 (file)
index 0000000..19b1d87
--- /dev/null
@@ -0,0 +1,36 @@
+################################################################################
+# Automatically-generated file. Do not edit!
+################################################################################
+
+# Add inputs and outputs from these tool invocations to the build variables 
+CPP_SRCS += \
+../src/loader/Interpreter.cpp \
+../src/loader/ParsedSource.cpp \
+../src/loader/Parser.cpp \
+../src/loader/Tokenizer.cpp \
+../src/loader/utility.cpp 
+
+OBJS += \
+./src/loader/Interpreter.o \
+./src/loader/ParsedSource.o \
+./src/loader/Parser.o \
+./src/loader/Tokenizer.o \
+./src/loader/utility.o 
+
+CPP_DEPS += \
+./src/loader/Interpreter.d \
+./src/loader/ParsedSource.d \
+./src/loader/Parser.d \
+./src/loader/Tokenizer.d \
+./src/loader/utility.d 
+
+
+# Each subdirectory must supply rules for building sources it contributes
+src/loader/%.o: ../src/loader/%.cpp
+       @echo 'Building file: $<'
+       @echo 'Invoking: GCC C++ Compiler'
+       g++ -I/usr/include/SDL -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o "$@" "$<"
+       @echo 'Finished building: $<'
+       @echo ' '
+
+
index 571f839038d5b7a6f429940f4538574e0a8e5b9e..13878f316e329a04cd70183f468b17dd1ae5ccd1 100644 (file)
@@ -9,6 +9,7 @@ RM := rm -rf
 # All of the sources participating in the build are defined here
 -include sources.mk
 -include src/sdl/subdir.mk
+-include src/loader/subdir.mk
 -include src/graphics/subdir.mk
 -include src/common/subdir.mk
 -include src/battle/states/subdir.mk
index a9593d6f382b32a74482e79f6b2196a922794e3b..7aaa6cfb7a6cbee251461aacc619ec53c1a97322 100644 (file)
@@ -25,6 +25,7 @@ C_UPPER_DEPS :=
 SUBDIRS := \
 src/sdl \
 src \
+src/loader \
 src/graphics \
 src/common \
 src/battle/states \
diff --git a/Release/src/loader/subdir.mk b/Release/src/loader/subdir.mk
new file mode 100644 (file)
index 0000000..c619e85
--- /dev/null
@@ -0,0 +1,36 @@
+################################################################################
+# Automatically-generated file. Do not edit!
+################################################################################
+
+# Add inputs and outputs from these tool invocations to the build variables 
+CPP_SRCS += \
+../src/loader/Interpreter.cpp \
+../src/loader/ParsedSource.cpp \
+../src/loader/Parser.cpp \
+../src/loader/Tokenizer.cpp \
+../src/loader/utility.cpp 
+
+OBJS += \
+./src/loader/Interpreter.o \
+./src/loader/ParsedSource.o \
+./src/loader/Parser.o \
+./src/loader/Tokenizer.o \
+./src/loader/utility.o 
+
+CPP_DEPS += \
+./src/loader/Interpreter.d \
+./src/loader/ParsedSource.d \
+./src/loader/Parser.d \
+./src/loader/Tokenizer.d \
+./src/loader/utility.d 
+
+
+# Each subdirectory must supply rules for building sources it contributes
+src/loader/%.o: ../src/loader/%.cpp
+       @echo 'Building file: $<'
+       @echo 'Invoking: GCC C++ Compiler'
+       g++ -I/usr/include/SDL -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o "$@" "$<"
+       @echo 'Finished building: $<'
+       @echo ' '
+
+
index 5ba60d67bb02ac29061df9509834b307145c40ba..d9586636d9667ee2d97756e83c28fcc3913bc77a 100644 (file)
@@ -80,9 +80,9 @@ void BattleState::Resize(int w, int h) {
 void BattleState::EnterState(Application &ctrl, SDL_Surface *screen) {
        for (int i(0); i < 4; ++i) {
                heroes[i].Position() = heroesLayout->CalculatePosition(i, background->w, background->h);
-               heroes[i].SpellMenu() = res->spellMenuPrototype;
+               heroes[i].SpellMenu() = *res->spellMenuProperties;
                heroes[i].UpdateSpellMenu();
-               heroes[i].IkariMenu() = res->ikariMenuPrototype;
+               heroes[i].IkariMenu() = *res->ikariMenuProperties;
                heroes[i].UpdateIkariMenu(res);
                heroTags[i] = HeroTag(this, i);
                smallHeroTags[i] = SmallHeroTag(this, i);
@@ -109,7 +109,7 @@ void BattleState::EnterState(Application &ctrl, SDL_Surface *screen) {
        smallHeroTagPositions[2] = Vector<int>(xOffset + tagWidth, yOffset);
        smallHeroTagPositions[3] = Vector<int>(xOffset + 3 * tagWidth, yOffset);
 
-       itemMenu = res->itemMenuPrototype;
+       itemMenu = *res->itemMenuProperties;
        LoadInventory();
 }
 
@@ -422,7 +422,7 @@ void BattleState::RenderSmallHeroTags(SDL_Surface *screen, const Vector<int> &of
        SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0, 0, 0));
        rect.y += res->normalFont->CharHeight() / 8;
        rect.h -= res->normalFont->CharHeight() / 4;
-       SDL_FillRect(screen, &rect, res->heroesBgColor);
+       SDL_FillRect(screen, &rect, res->heroesBgColor.MapRGB(screen->format));
 
        for (int i(0); i < numHeroes; ++i) {
                smallHeroTags[i].Render(screen, tagWidth, tagHeight, smallHeroTagPositions[i] + offset);
index 49d30c1fd49bf7693fcae0deeecd7a7e0a8c993d..719eec48f6428a94a9e126c4f908ac499c6161a1 100644 (file)
@@ -8,7 +8,7 @@
 #ifndef BATTLE_RESOURCES_H_
 #define BATTLE_RESOURCES_H_
 
-#include "../graphics/Menu.h"
+#include "../graphics/Color.h"
 
 #include <SDL.h>
 
@@ -60,14 +60,14 @@ struct Resources {
        graphics::Sprite *itemTargetCursor;
 
        const char *spellMenuHeadline;
-       graphics::Menu<const common::Spell *> spellMenuPrototype;
+       graphics::MenuProperties *spellMenuProperties;
 
        common::Inventory *inventory;
        const char *itemMenuHeadline;
-       graphics::Menu<const common::Item *> itemMenuPrototype;
+       graphics::MenuProperties *itemMenuProperties;
 
        const char *ikariMenuHeadline;
-       graphics::Menu<const common::Item *> ikariMenuPrototype;
+       graphics::MenuProperties *ikariMenuProperties;
        const char *noEquipmentText;
 
        const char *escapeText;
@@ -95,7 +95,7 @@ struct Resources {
        int ikariLabelCol;
        int ikariLabelRow;
 
-       Uint32 heroesBgColor;
+       graphics::Color heroesBgColor;
 
 
        Resources()
@@ -130,9 +130,12 @@ struct Resources {
        , itemTargetCursor(0)
 
        , spellMenuHeadline("")
+       , spellMenuProperties(0)
        , inventory(0)
        , itemMenuHeadline("")
+       , itemMenuProperties(0)
        , ikariMenuHeadline("")
+       , ikariMenuProperties(0)
        , noEquipmentText("")
 
        , escapeText("")
@@ -159,7 +162,6 @@ struct Resources {
        , ikariLabelCol(0)
        , ikariLabelRow(0)
 
-       , heroesBgColor(0)
        { }
 
 };
index b935e02a73f28ba7aedc4c1c9feb0f78faa26daa..ae885bf4aa6021a01d02ad244757f9be856eb5cb 100644 (file)
@@ -27,6 +27,14 @@ public:
        Uint8 Gut() const { return gut; }
        Uint16 MagicResistance() const { return magicResistance; }
 
+       void SetAttack(Uint16 a) { attack = a; }
+       void SetDefense(Uint16 d) { defense = d; }
+       void SetStrength(Uint16 s) { strength = s; }
+       void SetAgility(Uint16 a) { agility = a; }
+       void SetIntelligence(Uint16 i) { intelligence = i; }
+       void SetGut(Uint8 g) { gut = g; }
+       void SetMagicResistance(Uint16 m) { magicResistance = m; }
+
 private:
        Uint16 attack;
        Uint16 defense;
index 5376d1344b1d73a9385f9655f2e0e6b8f341b4ce..90414485599168a349a1cf4572b4e1506f539621 100644 (file)
@@ -24,6 +24,12 @@ public:
        bool TargetsMultiple() const { return (mode & COUNT_MASK) == MULTIPLE; }
        bool TargetsSingle() const { return (mode & COUNT_MASK) == SINGLE; }
 
+       void TargetAll() { mode = (mode & FACTION_MASK) | ALL; }
+       void TargetMultiple() { mode = (mode & FACTION_MASK) | MULTIPLE; }
+       void TargetSingle() { mode = (mode & FACTION_MASK) | SINGLE; }
+       void TargetAlly() { mode = ALLY | (mode & COUNT_MASK); }
+       void TargetEnemy() { mode = ENEMY | (mode & COUNT_MASK); }
+
        void TargetAllEnemies() { mode = ENEMY | ALL; }
        void TargetMultipleEnemies() { mode = ENEMY | MULTIPLE; }
        void TargetSingleEnemy() { mode = ENEMY | SINGLE; }
index 208f6bdb19e7b95d9dbc20a417498608f14dc64b..2696694a4f4235cc9454af5b2528ad1a80788567 100644 (file)
@@ -28,11 +28,15 @@ public:
        virtual ~Animation() { };
 
 public:
-
        const Sprite *GetSprite() const { return sprite; }
        int FrameTime() const { return frameTime; }
        bool Repeat() const { return repeat; }
 
+public:
+       void SetSprite(const Sprite *s) { sprite = s; }
+       void SetFrameTime(int t) { frameTime = t; }
+       void SetRepeat(bool r) { repeat = r; }
+
 public:
        virtual int NumFrames() const = 0;
        virtual int Col(int frame) const = 0;
diff --git a/src/graphics/Color.h b/src/graphics/Color.h
new file mode 100644 (file)
index 0000000..42cbc89
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Color.h
+ *
+ *  Created on: Sep 1, 2012
+ *      Author: holy
+ */
+
+#ifndef GRAPHICS_COLOR_H_
+#define GRAPHICS_COLOR_H_
+
+#include <SDL.h>
+
+namespace graphics {
+
+class Color {
+
+public:
+       Color() :r(0), g(0), b(0), a(255) { }
+       Color(Uint8 r, Uint8 g, Uint8 b, Uint8 a = 255) : r(r), g(g), b(b), a(a) { }
+
+public:
+       Uint8 RedChannel() const { return r; }
+       Uint8 GreenChannel() const { return g; }
+       Uint8 BlueChannel() const { return b; }
+       Uint8 AlphaChannel() const { return a; }
+
+       Uint32 MapRGB(SDL_PixelFormat *f) const { return SDL_MapRGB(f, r, g, b); }
+       Uint32 MapRGBA(SDL_PixelFormat *f) const { return SDL_MapRGBA(f, r, g, b, a); }
+
+       void SetRedChannel(Uint8 i) { r = i; }
+       void SetGreenChannel(Uint8 i) { g = i; }
+       void SetBlueChannel(Uint8 i) { b = i; }
+       void SetAlphaChannel(Uint8 i) { a = i; }
+
+private:
+       Uint8 r, g, b, a;
+
+};
+
+}
+
+#endif /* GRAPHICS_COLOR_H_ */
index 2eeea943b6103740c3114f71b835b14c11953408..815e9c5a6e4183c88de7225e4721b67a4521033c 100644 (file)
@@ -32,20 +32,24 @@ public:
                }
        }
 
-protected:
-       virtual int NumFrames() const { return frames.size(); };
-       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; }
-
-private:
+public:
        struct FrameProp {
+               FrameProp() : col(0), row(0) { }
                FrameProp(int col, int row, const geometry::Vector<int> &disposition)
                : col(col), row(row), disposition(disposition) {}
                int col;
                int row;
                geometry::Vector<int> disposition;
        };
+       void AddFrame(const FrameProp &f) { frames.push_back(f); }
+
+protected:
+       virtual int NumFrames() const { return frames.size(); };
+       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; }
+
+private:
        std::vector<FrameProp> frames;
 
 };
index a11850001717eae97289cdb2158c8aa129d32c1c..ca06a076a80c251a17f7fead31171d7ce5c6a1a3 100644 (file)
@@ -8,6 +8,7 @@
 #include "Font.h"
 
 #include <cmath>
+#include <iostream>
 
 using geometry::Vector;
 using std::pow;
@@ -15,12 +16,16 @@ using std::pow;
 namespace graphics {
 
 void Font::DrawChar(char c, SDL_Surface *dest, const Vector<int> &position) const {
+       if (!sprite) return;
+
        int col(colOffset + (c % 0x10));
        int row(rowOffset + (c / 0x10));
        sprite->Draw(dest, position, col, row);
 }
 
 void Font::DrawString(const char *s, SDL_Surface *dest, const Vector<int> &positionIn, int maxChars) const {
+       if (!sprite) return;
+
        Vector<int> position(positionIn);
        Vector<int> step(CharWidth(), 0);
        for (int i(0); s[i] && (maxChars <= 0 || i < maxChars); ++i, position += step) {
@@ -29,10 +34,14 @@ void Font::DrawString(const char *s, SDL_Surface *dest, const Vector<int> &posit
 }
 
 void Font::DrawDigit(int digit, SDL_Surface *dest, const Vector<int> &position) const {
+       if (!sprite) return;
+
        DrawChar(digit + 0x30, dest, position);
 }
 
 void Font::DrawNumber(int numberIn, SDL_Surface *dest, const Vector<int> &positionIn, int digits) const {
+       if (!sprite) return;
+
        int number(numberIn);
        if (digits > 0 && numberIn >= pow(10.0, digits)) {
                numberIn = pow(10.0, digits) - 1;
index 86448025992fcbd509e30c1996738a9e8adaba20..571431df74dca406a73572c881669eda69208513 100644 (file)
@@ -18,7 +18,7 @@ namespace graphics {
 class Font {
 
 public:
-       explicit Font(const Sprite *sprite, int colOffset = 0, int rowOffset = 0)
+       explicit Font(const Sprite *sprite = 0, int colOffset = 0, int rowOffset = 0)
        : sprite(sprite), colOffset(colOffset), rowOffset(rowOffset) {
 
        }
@@ -31,6 +31,11 @@ public:
        void DrawDigit(int d, SDL_Surface *dest, const geometry::Vector<int> &position) const;
        void DrawNumber(int n, SDL_Surface *dest, const geometry::Vector<int> &position, int digits = 0) const;
 
+public:
+       void SetSprite(const Sprite *s) { sprite = s; }
+       void SetColOffset(int n) { colOffset = n; }
+       void SetRowOffset(int n) { rowOffset = n; }
+
 private:
        const Sprite *sprite;
        int colOffset;
index a152ee76c8efa152825dc514938e100df97c3736..14856071662a8ba80494c6b536c4b1cc71e9fbf8 100644 (file)
@@ -13,6 +13,15 @@ namespace graphics {
 
 // TODO: maybe create a cache for frames?
 void Frame::Draw(SDL_Surface *dest, const Vector<int> &position, int width, int height) const {
+       if (!surface) {
+               SDL_Rect rect;
+               rect.x = position.X();
+               rect.y = position.Y();
+               rect.w = width;
+               rect.h = height;
+               SDL_FillRect(dest, &rect, SDL_MapRGB(dest->format, 0xFF, 0x00, 0x00));
+               return;
+       }
        // top-left corner
        SDL_Rect srcRect;
        srcRect.x = offset.X();
index 09674cc62d2f63a97dc50a327a2c6df2c33a0fd4..59aa1fb0c74486b99c05be713030cd5a9d7a9650 100644 (file)
@@ -17,7 +17,7 @@ namespace graphics {
 class Frame {
 
 public:
-       Frame(SDL_Surface *s, int borderWidth, int borderHeight, int repeatWidth = 1, int repeatHeight = 1, int xOffset = 0, int yOffset = 0)
+       explicit Frame(SDL_Surface *s = 0, int borderWidth = 1, int borderHeight = 1, int repeatWidth = 1, int repeatHeight = 1, int xOffset = 0, int yOffset = 0)
        : surface(s), borderSize(borderWidth, borderHeight), repeatSize(repeatWidth, repeatHeight), offset(xOffset, yOffset) { }
 
 public:
@@ -31,6 +31,12 @@ public:
        const geometry::Vector<int> RepeatSize() const { return repeatSize; }
        void Draw(SDL_Surface *dest, const geometry::Vector<int> &position, int width, int height) const;
 
+public:
+       void SetSurface(SDL_Surface *s) { surface = s; }
+       void SetBorderSize(const geometry::Vector<int> &s) { borderSize = s; }
+       void SetRepeatSize(const geometry::Vector<int> &s) { repeatSize = s; }
+       void SetOffset(const geometry::Vector<int> &o) { offset = o; }
+
 private:
        SDL_Surface *surface;
        geometry::Vector<int> borderSize;
index eda9942d4cdb5697b294bf706af3749739d1a8a5..0d72ac0676eb11233a5d67df37e795f596e1d70a 100644 (file)
@@ -17,6 +17,18 @@ void Gauge::Draw(SDL_Surface *dest, const Vector<int> &position, int width, Uint
        int filledWidth = fill * (width - startWidth - endWidth) / 255;
        int emptyWidth = (255 - fill) * (width - startWidth - endWidth) / 255;
 
+       if (!surface) {
+               destRect.x = position.X();
+               destRect.y = position.Y();
+               destRect.w = filledWidth + startWidth;
+               destRect.h = height;
+               SDL_FillRect(dest, &destRect, SDL_MapRGB(dest->format, 0x00, 0xFF, 0x00));
+               destRect.x += destRect.w;
+               destRect.w = emptyWidth + endWidth;
+               SDL_FillRect(dest, &destRect, SDL_MapRGB(dest->format, 0xFF, 0x00, 0x00));
+               return;
+       }
+
        // start
        srcRect.w = startWidth;
        srcRect.h = height;
@@ -24,28 +36,28 @@ void Gauge::Draw(SDL_Surface *dest, const Vector<int> &position, int width, Uint
        destRect.y = position.Y();
        // full part
        if (fill == 0) {
-               srcRect.x = emptyX;
-               srcRect.y = emptyY;
+               srcRect.x = emptyOffset.X();
+               srcRect.y = emptyOffset.Y();
                SDL_BlitSurface(surface, &srcRect, dest, &destRect);
        } else {
-               srcRect.x = fullX;
-               srcRect.y = fullY;
+               srcRect.x = fullOffset.X();
+               srcRect.y = fullOffset.Y();
                SDL_BlitSurface(surface, &srcRect, dest, &destRect);
        }
 
        destRect.x = position.X() + startWidth;
 
        // fill
-       srcRect.x = fullX + startWidth;
-       srcRect.y = fullY;
+       srcRect.x = fullOffset.X() + startWidth;
+       srcRect.y = fullOffset.Y();
        srcRect.w = repeatWidth;
        while (filledWidth > repeatWidth) {
                SDL_BlitSurface(surface, &srcRect, dest, &destRect);
                destRect.x += repeatWidth;
                filledWidth -= repeatWidth;
        }
-       srcRect.x = emptyX + startWidth;
-       srcRect.y = emptyY;
+       srcRect.x = emptyOffset.X() + startWidth;
+       srcRect.y = emptyOffset.Y();
        while (emptyWidth > repeatWidth) {
                SDL_BlitSurface(surface, &srcRect, dest, &destRect);
                destRect.x += repeatWidth;
@@ -55,12 +67,12 @@ void Gauge::Draw(SDL_Surface *dest, const Vector<int> &position, int width, Uint
        // end
        srcRect.w = endWidth;
        if (fill == 255) {
-               srcRect.x = fullX + startWidth + repeatWidth;
-               srcRect.y = fullY;
+               srcRect.x = fullOffset.X() + startWidth + repeatWidth;
+               srcRect.y = fullOffset.Y();
                SDL_BlitSurface(surface, &srcRect, dest, &destRect);
        } else {
-               srcRect.x = emptyX + startWidth + repeatWidth;
-               srcRect.y = emptyY;
+               srcRect.x = emptyOffset.X() + startWidth + repeatWidth;
+               srcRect.y = emptyOffset.Y();
                SDL_BlitSurface(surface, &srcRect, dest, &destRect);
        }
 
index fd81877e0e0828ff0c32dbf7eec20d0166c10aba..3251ec2e0eccf0b7a9d11cce4966ae307964db32 100644 (file)
@@ -17,20 +17,27 @@ namespace graphics {
 class Gauge {
 
 public:
-       Gauge(SDL_Surface *s, int fullX, int fullY, int emptyX, int emptyY, int height, int startWidth, int repeatWidth, int endWidth)
-       : surface(s), fullX(fullX), fullY(fullY), emptyX(emptyX), emptyY(emptyY), height(height), startWidth(startWidth), repeatWidth(repeatWidth), endWidth(endWidth) { }
+       explicit Gauge(SDL_Surface *s = 0, int fullX = 0, int fullY = 0, int emptyX = 0, int emptyY = 0, int height = 1, int startWidth = 0, int repeatWidth = 1, int endWidth = 0)
+       : surface(s), fullOffset(fullX, fullY), emptyOffset(emptyX, emptyY), height(height), startWidth(startWidth), repeatWidth(repeatWidth), endWidth(endWidth) { }
 
 public:
        int MinWidth() const { return startWidth + endWidth; }
        int Height() const { return height; }
        void Draw(SDL_Surface *dest, const geometry::Vector<int> &position, int width, Uint8 fill) const;
 
+public:
+       void SetSurface(SDL_Surface *s) { surface = s; }
+       void SetFullOffset(const geometry::Vector<int> &o) { fullOffset = o; }
+       void SetEmptyOffset(const geometry::Vector<int> &o) { emptyOffset = o; }
+       void SetHeight(int h) { height = h; }
+       void SetStartWidth(int w) { startWidth = w; }
+       void SetRepeatWidth(int w) { repeatWidth = w; }
+       void SetEndWidth(int w) { endWidth = w; }
+
 private:
        SDL_Surface *surface;
-       int fullX;
-       int fullY;
-       int emptyX;
-       int emptyY;
+       geometry::Vector<int> fullOffset;
+       geometry::Vector<int> emptyOffset;
        int height;
        int startWidth;
        int repeatWidth;
index 02a88c0be13853a9c59ec6f5393d3ea218afbb73..175a7114501afcd4ad1e75bfb61115c8b790f28c 100644 (file)
@@ -19,11 +19,39 @@ namespace graphics {
 
 class Sprite;
 
+struct MenuProperties {
+       const Font *font;
+       const Font *disabledFont;
+       const Sprite *cursor;
+       int charsPerEntry;
+       int rows;
+       int rowGap;
+       int iconSpace;
+       int cols;
+       int colGap;
+       int charsPerNumber;
+       int charsPerAdditionalText;
+       int additionalTextGap;
+       char delimiter;
+
+       MenuProperties()
+       : font(0), disabledFont(0), cursor(0)
+       , charsPerEntry(0), rows(0), rowGap(0)
+       , iconSpace(0), cols(0), colGap(0)
+       , charsPerNumber(0), charsPerAdditionalText(0)
+       , additionalTextGap(0), delimiter(':') { }
+
+       MenuProperties(const Font *font, const Font *disabledFont, const Sprite *cursor, int charsPerEntry, int rows, int rowGap, int iconSpace, int cols, int colGap, int charsPerNumber, char delimiter, int charsPerAdditionalText, int additionalTextGap)
+       : font(font), disabledFont(disabledFont), cursor(cursor), charsPerEntry(charsPerEntry), rows(rows), rowGap(rowGap), iconSpace(iconSpace), cols(cols), colGap(colGap), charsPerNumber(charsPerNumber), charsPerAdditionalText(charsPerAdditionalText), additionalTextGap(additionalTextGap), delimiter(delimiter) { }
+};
+
 template<class T>
-class Menu {
+class Menu
+: private MenuProperties {
 
 public:
        Menu();
+       Menu(const MenuProperties &);
        Menu(const Font *font, const Font *disabledFont, const Sprite *cursor, int charsPerEntry, int rows, int rowGap = 0, int iconSpace = 0, int cols = 1, int colGap = 0, int charsPerNumber = 0, char delimiter = ':', int charsPerAdditionalText = 0, int additionalTextGap = 0);
 
 public:
@@ -75,63 +103,41 @@ private:
                T value;
                bool enabled;
        };
-       const Font *font;
-       const Font *disabledFont;
-       const Sprite *cursor;
        std::vector<Entry> entries;
-       int charsPerEntry;
-       int rows;
-       int rowGap;
-       int iconSpace;
-       int cols;
-       int colGap;
        int selected;
        int topRow;
-       int charsPerNumber;
-       int charsPerAdditionalText;
-       int additionalTextGap;
-       char delimiter;
 
 };
 
 
 template<class T>
 Menu<T>::Menu()
-: font(0)
-, disabledFont(0)
-, cursor(0)
-, charsPerEntry(0)
-, rows(0)
-, rowGap(0)
-, iconSpace(0)
-, cols(0)
-, colGap(0)
+: MenuProperties()
+, selected(0)
+, topRow(0) {
+
+}
+
+template<class T>
+Menu<T>::Menu(const MenuProperties &p)
+: MenuProperties(p)
 , selected(0)
-, topRow(0)
-, charsPerNumber(0)
-, charsPerAdditionalText(0)
-, additionalTextGap(0)
-, delimiter(':') {
+, topRow(0) {
 
 }
 
 template<class T>
 Menu<T>::Menu(const Font *font, const Font *disabledFont, const Sprite *cursor, int charsPerEntry, int rows, int rowGap, int iconSpace, int cols, int colGap, int charsPerNumber, char delimiter, int charsPerAdditionalText, int additionalTextGap)
-: font(font)
-, disabledFont(disabledFont ? disabledFont : font)
-, cursor(cursor)
-, charsPerEntry(charsPerEntry)
-, rows(rows)
-, rowGap(rowGap)
-, iconSpace(iconSpace)
-, cols(cols)
-, colGap(colGap)
+: MenuProperties(
+               font, disabledFont ? disabledFont : font,
+               cursor, charsPerEntry,
+               rows, rowGap, iconSpace,
+               cols, colGap, charsPerNumber,
+               delimiter,
+               charsPerAdditionalText,
+               additionalTextGap)
 , selected(0)
-, topRow(0)
-, charsPerNumber(charsPerNumber)
-, charsPerAdditionalText(charsPerAdditionalText)
-, additionalTextGap(additionalTextGap)
-, delimiter(delimiter) {
+, topRow(0) {
 
 }
 
index d15c1d9a1258e19bd1ff1f08021fe2f3620c74bd..003a03120c5b451908e3f72443e55251d2c15cb1 100644 (file)
@@ -21,6 +21,11 @@ public:
        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) { }
 
+public:
+       void SetNumFrames(int n) { numFrames = n; }
+       void SetCol(int c) { col = c; }
+       void SetRow(int r) { row = r; }
+
 protected:
        virtual int NumFrames() const { return numFrames; };
        virtual int Col(int frame) const { return col; }
index f6f3b7b566d58cfa969d973767353c39cfea8b7e..2af95f858d12ac104814fa73ec201e42c39d9c53 100644 (file)
@@ -17,6 +17,7 @@ namespace graphics {
 class Sprite {
 
 public:
+       Sprite() : surface(0), size(64, 64), offset() { }
        Sprite(SDL_Surface *s, int width, int height, int xOffset = 0, int yOffset = 0)
        : surface(s), size(width, height), offset(xOffset, yOffset) { }
 
@@ -37,6 +38,11 @@ public:
                Draw(dest, position + offset, col, row);
        }
 
+public:
+       void SetSurface(SDL_Surface *s) { surface = s; }
+       void SetSize(const geometry::Vector<int> &s) { size = s; }
+       void SetOffset(const geometry::Vector<int> &o) { offset = o; }
+
 private:
        SDL_Surface *surface;
        geometry::Vector<int> size;
diff --git a/src/loader/Interpreter.cpp b/src/loader/Interpreter.cpp
new file mode 100644 (file)
index 0000000..fcfe473
--- /dev/null
@@ -0,0 +1,1249 @@
+/*
+ * Interpreter.cpp
+ *
+ *  Created on: Aug 26, 2012
+ *      Author: holy
+ */
+
+#include "Interpreter.h"
+
+#include "ParsedSource.h"
+#include "../battle/Hero.h"
+#include "../battle/Monster.h"
+#include "../battle/PartyLayout.h"
+#include "../battle/Resources.h"
+#include "../common/Ikari.h"
+#include "../common/Item.h"
+#include "../common/Spell.h"
+#include "../common/TargetingMode.h"
+#include "../graphics/ComplexAnimation.h"
+#include "../graphics/Font.h"
+#include "../graphics/Frame.h"
+#include "../graphics/Gauge.h"
+#include "../graphics/Menu.h"
+#include "../graphics/SimpleAnimation.h"
+#include "../graphics/Sprite.h"
+
+#include <algorithm>
+#include <cstring>
+#include <SDL_image.h>
+
+using battle::Hero;
+using battle::Monster;
+using battle::PartyLayout;
+using battle::Stats;
+using common::Ikari;
+using common::Item;
+using common::Spell;
+using common::TargetingMode;
+using graphics::Animation;
+using graphics::Color;
+using graphics::Font;
+using graphics::Frame;
+using graphics::Gauge;
+using graphics::ComplexAnimation;
+using graphics::SimpleAnimation;
+using graphics::Sprite;
+using geometry::Vector;
+using std::make_pair;
+using std::map;
+using std::set;
+using std::string;
+using std::vector;
+
+namespace loader {
+
+Interpreter::~Interpreter() {
+       for (vector<battle::Resources *>::const_iterator i(battleResources.begin()), end(battleResources.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<ComplexAnimation *>::const_iterator i(complexAnimations.begin()), end(complexAnimations.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<Font *>::const_iterator i(fonts.begin()), end(fonts.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<Frame *>::const_iterator i(frames.begin()), end(frames.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<Gauge *>::const_iterator i(gauges.begin()), end(gauges.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<Hero *>::const_iterator i(heroes.begin()), end(heroes.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<Ikari *>::const_iterator i(ikaris.begin()), end(ikaris.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<SDL_Surface *>::const_iterator i(images.begin()), end(images.end()); i != end; ++i) {
+               SDL_FreeSurface(*i);
+       }
+       for (vector<Item *>::const_iterator i(items.begin()), end(items.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<graphics::MenuProperties *>::const_iterator i(menuProperties.begin()), end(menuProperties.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<Monster *>::const_iterator i(monsters.begin()), end(monsters.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<PartyLayout *>::const_iterator i(partyLayouts.begin()), end(partyLayouts.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<SimpleAnimation *>::const_iterator i(simpleAnimations.begin()), end(simpleAnimations.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<Spell *>::const_iterator i(spells.begin()), end(spells.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<Sprite *>::const_iterator i(sprites.begin()), end(sprites.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<const char *>::const_iterator i(strings.begin()), end(strings.end()); i != end; ++i) {
+               delete *i;
+       }
+       for (vector<TargetingMode *>::const_iterator i(targetingModes.begin()), end(targetingModes.end()); i != end; ++i) {
+               delete *i;
+       }
+}
+
+
+Animation *Interpreter::GetAnimation(const std::string &name) {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == COMPLEX_ANIMATION) {
+                       return complexAnimations[i->second.index];
+               } else if (i->second.type == SIMPLE_ANIMATION) {
+                       return simpleAnimations[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Animation");
+               }
+       } else {
+               throw Error("access to undefined Animation " + name);
+       }
+}
+
+battle::Resources *Interpreter::GetBattleResources(const std::string &name) {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == BATTLE_RESOURCES) {
+                       return battleResources[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to BattleResources");
+               }
+       } else {
+               throw Error("access to undefined BattleResources " + name);
+       }
+}
+
+bool Interpreter::GetBoolean(const std::string &name) const {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == BOOLEAN) {
+                       return booleans[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Boolean");
+               }
+       } else {
+               throw Error("access to undefined Boolean " + name);
+       }
+}
+
+const Color &Interpreter::GetColor(const std::string &name) const {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == COLOR) {
+                       return colors[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Color");
+               }
+       } else {
+               throw Error("access to undefined Color " + name);
+       }
+}
+
+Font *Interpreter::GetFont(const std::string &name) {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == FONT) {
+                       return fonts[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Font");
+               }
+       } else {
+               throw Error("access to undefined Font " + name);
+       }
+}
+
+Frame *Interpreter::GetFrame(const std::string &name) {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == FRAME) {
+                       return frames[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Frame");
+               }
+       } else {
+               throw Error("access to undefined Frame " + name);
+       }
+}
+
+Gauge *Interpreter::GetGauge(const std::string &name) {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == GAUGE) {
+                       return gauges[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Gauge");
+               }
+       } else {
+               throw Error("access to undefined Gauge " + name);
+       }
+}
+
+Hero *Interpreter::GetHero(const std::string &name) {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == HERO) {
+                       return heroes[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Hero");
+               }
+       } else {
+               throw Error("access to undefined Hero " + name);
+       }
+}
+
+Ikari *Interpreter::GetIkari(const std::string &name) {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == IKARI) {
+                       return ikaris[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Ikari");
+               }
+       } else {
+               throw Error("access to undefined Ikari " + name);
+       }
+}
+
+Item *Interpreter::GetItem(const std::string &name) {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == ITEM) {
+                       return items[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Item");
+               }
+       } else {
+               throw Error("access to undefined Item " + name);
+       }
+}
+
+graphics::MenuProperties *Interpreter::GetMenuProperties(const std::string &name) {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == MENU_PROPERTIES) {
+                       return menuProperties[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to MenuProperties");
+               }
+       } else {
+               throw Error("access to undefined MenuProperties " + name);
+       }
+}
+
+Monster *Interpreter::GetMonster(const std::string &name) {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == MONSTER) {
+                       return monsters[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Monster");
+               }
+       } else {
+               throw Error("access to undefined Monster " + name);
+       }
+}
+
+int Interpreter::GetNumber(const std::string &name) const {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == NUMBER) {
+                       return numbers[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Number");
+               }
+       } else {
+               throw Error("access to undefined Number " + name);
+       }
+}
+
+PartyLayout *Interpreter::GetPartyLayout(const std::string &name) {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == PARTY_LAYOUT) {
+                       return partyLayouts[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to PartyLayout");
+               }
+       } else {
+               throw Error("access to undefined PartyLayout " + name);
+       }
+}
+
+const char *Interpreter::GetPath(const std::string &name) const {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == PATH) {
+                       return strings[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Path");
+               }
+       } else {
+               throw Error("access to undefined Path " + name);
+       }
+}
+
+Spell *Interpreter::GetSpell(const std::string &name) {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == SPELL) {
+                       return spells[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Spell");
+               }
+       } else {
+               throw Error("access to undefined Spell " + name);
+       }
+}
+
+Sprite *Interpreter::GetSprite(const std::string &name) {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == SPRITE) {
+                       return sprites[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Sprite");
+               }
+       } else {
+               throw Error("access to undefined Sprite " + name);
+       }
+}
+
+const char *Interpreter::GetString(const std::string &name) const {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               // TODO: enable path to string casting some time
+               if (i->second.type == STRING /* || i->second.type == PATH */) {
+                       return strings[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to String");
+               }
+       } else {
+               throw Error("access to undefined String " + name);
+       }
+}
+
+TargetingMode *Interpreter::GetTargetingMode(const std::string &name) {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == TARGETING_MODE) {
+                       return targetingModes[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to TargetingMode");
+               }
+       } else {
+               throw Error("access to undefined TargetingMode " + name);
+       }
+}
+
+Vector<int> Interpreter::GetVector(const std::string &name) const {
+       map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
+       if (i != parsedDefinitions.end()) {
+               if (i->second.type == VECTOR) {
+                       return vectors[i->second.index];
+               } else {
+                       throw Error("cannot cast " + i->second.dfn->TypeName() + " to Vector");
+               }
+       } else {
+               throw Error("access to undefined Vector " + name);
+       }
+}
+
+
+void Interpreter::ReadSource() {
+       for (set<string>::const_iterator i(source.Exports().begin()), end(source.Exports().end()); i != end; ++i) {
+               ReadDefinition(source.GetDefinition(*i));
+       }
+}
+
+void Interpreter::ReadDefinition(const Definition &dfn) {
+       if (parsedDefinitions.find(dfn.Identifier()) != parsedDefinitions.end()) {
+               return;
+       }
+       if (dfn.HasLiteralValue()) {
+               ReadLiteral(dfn);
+       } else {
+               ReadObject(dfn);
+       }
+}
+
+void Interpreter::ReadLiteral(const Definition &dfn) {
+       switch (dfn.GetLiteral()->GetType()) {
+               case Literal::ARRAY_VALUES:
+                       valueArrays.push_back(dfn.GetLiteral()->GetValues());
+                       parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, VALUE_ARRAY, valueArrays.size() - 1)));
+                       break;
+               case Literal::ARRAY_PROPS:
+                       propertyListArrays.push_back(dfn.GetLiteral()->GetPropertyLists());
+                       parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, PROPERTY_LIST_ARRAY, propertyListArrays.size() - 1)));
+                       break;
+               case Literal::BOOLEAN:
+                       booleans.push_back(dfn.GetLiteral()->GetBoolean());
+                       parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, BOOLEAN, booleans.size() - 1)));
+                       break;
+               case Literal::COLOR:
+                       colors.push_back(Color(dfn.GetLiteral()->GetRed(), dfn.GetLiteral()->GetGreen(), dfn.GetLiteral()->GetBlue(), dfn.GetLiteral()->GetAlpha()));
+                       parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, COLOR, colors.size() - 1)));
+                       break;
+               case Literal::NUMBER:
+                       numbers.push_back(dfn.GetLiteral()->GetNumber());
+                       parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, NUMBER, numbers.size() - 1)));
+                       break;
+               case Literal::PATH:
+                       {
+                               char *str(new char[dfn.GetLiteral()->GetString().size() + 1]);
+                               std::memcpy(str, dfn.GetLiteral()->GetString().c_str(), dfn.GetLiteral()->GetString().size());
+                               str[dfn.GetLiteral()->GetString().size()] = '\0';
+                               strings.push_back(str);
+                       }
+                       parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, PATH, strings.size() - 1)));
+                       break;
+               case Literal::STRING:
+                       {
+                               char *str(new char[dfn.GetLiteral()->GetString().size() + 1]);
+                               std::memcpy(str, dfn.GetLiteral()->GetString().c_str(), dfn.GetLiteral()->GetString().size());
+                               str[dfn.GetLiteral()->GetString().size()] = '\0';
+                               strings.push_back(str);
+                       }
+                       parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, STRING, strings.size() - 1)));
+                       break;
+               case Literal::VECTOR:
+                       vectors.push_back(Vector<int>(dfn.GetLiteral()->GetX(), dfn.GetLiteral()->GetY()));
+                       parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, VECTOR, vectors.size() - 1)));
+                       break;
+               case Literal::OBJECT:
+                       ReadObject(dfn);
+                       break;
+       }
+}
+
+Animation *Interpreter::GetAnimation(const Value &v) {
+       if (v.IsLiteral()) {
+               if (v.GetLiteral().GetTypeName() == "ComplexAnimation") {
+                       ComplexAnimation *a(new ComplexAnimation);
+                       ReadComplexAnimation(*a, *v.GetLiteral().GetProperties());
+                       complexAnimations.push_back(a);
+                       return a;
+               } else {
+                       SimpleAnimation *a(new SimpleAnimation);
+                       ReadSimpleAnimation(*a, *v.GetLiteral().GetProperties());
+                       simpleAnimations.push_back(a);
+                       return a;
+               }
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetAnimation(v.GetIdentifier());
+       }
+}
+
+battle::Resources *Interpreter::GetBattleResources(const Value &v) {
+       if (v.IsLiteral()) {
+               battle::Resources *r(new battle::Resources);
+               ReadBattleResources(*r, *v.GetLiteral().GetProperties());
+               return r;
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetBattleResources(v.GetIdentifier());
+       }
+}
+
+bool Interpreter::GetBoolean(const Value &v) {
+       if (v.IsLiteral()) {
+               return v.GetLiteral().GetBoolean();
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetBoolean(v.GetIdentifier());
+       }
+}
+
+Color Interpreter::GetColor(const Value &v) {
+       if (v.IsLiteral()) {
+               return Color(v.GetLiteral().GetRed(), v.GetLiteral().GetGreen(), v.GetLiteral().GetBlue(), v.GetLiteral().GetAlpha());
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetColor(v.GetIdentifier());
+       }
+}
+
+Font *Interpreter::GetFont(const Value &v) {
+       if (v.IsLiteral()) {
+               Font *f(new Font);
+               ReadFont(*f, *v.GetLiteral().GetProperties());
+               return f;
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetFont(v.GetIdentifier());
+       }
+}
+
+Frame *Interpreter::GetFrame(const Value &v) {
+       if (v.IsLiteral()) {
+               Frame *f(new Frame);
+               ReadFrame(*f, *v.GetLiteral().GetProperties());
+               return f;
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetFrame(v.GetIdentifier());
+       }
+}
+
+Gauge *Interpreter::GetGauge(const Value &v) {
+       if (v.IsLiteral()) {
+               Gauge *g(new Gauge);
+               ReadGauge(*g, *v.GetLiteral().GetProperties());
+               return g;
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetGauge(v.GetIdentifier());
+       }
+}
+
+Hero *Interpreter::GetHero(const Value &v) {
+       if (v.IsLiteral()) {
+               Hero *h(new Hero);
+               ReadHero(*h, *v.GetLiteral().GetProperties());
+               return h;
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetHero(v.GetIdentifier());
+       }
+}
+
+Ikari *Interpreter::GetIkari(const Value &v) {
+       if (v.IsLiteral()) {
+               Ikari *i(new Ikari);
+               ReadIkari(*i, *v.GetLiteral().GetProperties());
+               return i;
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetIkari(v.GetIdentifier());
+       }
+}
+
+SDL_Surface *Interpreter::GetImage(const Value &v) {
+       string path(GetPath(v));
+       map<string, SDL_Surface *>::const_iterator i(imageCache.find(path));
+       if (i == imageCache.end()) {
+               SDL_Surface *image(IMG_Load(path.c_str()));
+               images.push_back(image);
+               imageCache.insert(make_pair(path, image));
+               return image;
+       } else {
+               return i->second;
+       }
+}
+
+Item *Interpreter::GetItem(const Value &v) {
+       if (v.IsLiteral()) {
+               Item *i(new Item);
+               ReadItem(*i, *v.GetLiteral().GetProperties());
+               return i;
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetItem(v.GetIdentifier());
+       }
+}
+
+graphics::MenuProperties *Interpreter::GetMenuProperties(const Value &v) {
+       if (v.IsLiteral()) {
+               graphics::MenuProperties *m(new graphics::MenuProperties);
+               ReadMenuProperties(*m, *v.GetLiteral().GetProperties());
+               return m;
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetMenuProperties(v.GetIdentifier());
+       }
+}
+
+Monster *Interpreter::GetMonster(const Value &v) {
+       if (v.IsLiteral()) {
+               Monster *m(new Monster);
+               ReadMonster(*m, *v.GetLiteral().GetProperties());
+               return m;
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetMonster(v.GetIdentifier());
+       }
+}
+
+int Interpreter::GetNumber(const Value &v) {
+       if (v.IsLiteral()) {
+               return v.GetLiteral().GetNumber();
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetNumber(v.GetIdentifier());
+       }
+}
+
+PartyLayout *Interpreter::GetPartyLayout(const Value &v) {
+       if (v.IsLiteral()) {
+               PartyLayout *l(new PartyLayout);
+               ReadPartyLayout(*l, *v.GetLiteral().GetProperties());
+               return l;
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetPartyLayout(v.GetIdentifier());
+       }
+}
+
+const char *Interpreter::GetPath(const Value &v) {
+       if (v.IsLiteral()) {
+               return v.GetLiteral().GetString().c_str();
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetPath(v.GetIdentifier());
+       }
+}
+
+const PropertyList *Interpreter::GetPropertyList(const Value &v) {
+       if (v.IsLiteral()) {
+               return v.GetLiteral().GetProperties();
+       } else {
+               throw Error("cannot reference property lists");
+       }
+}
+
+const vector<PropertyList *> &Interpreter::GetPropertyListArray(const Value &v) {
+       if (v.IsLiteral()) {
+               return v.GetLiteral().GetPropertyLists();
+       } else {
+               throw Error("cannot reference property list arrays");
+       }
+}
+
+Spell *Interpreter::GetSpell(const Value &v) {
+       if (v.IsLiteral()) {
+               Spell *s(new Spell);
+               ReadSpell(*s, *v.GetLiteral().GetProperties());
+               return s;
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetSpell(v.GetIdentifier());
+       }
+}
+
+Sprite *Interpreter::GetSprite(const Value &v) {
+       if (v.IsLiteral()) {
+               Sprite *s(new Sprite);
+               ReadSprite(*s, *v.GetLiteral().GetProperties());
+               return s;
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetSprite(v.GetIdentifier());
+       }
+}
+
+const char *Interpreter::GetString(const Value &v) {
+       if (v.IsLiteral()) {
+               return v.GetLiteral().GetString().c_str();
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetString(v.GetIdentifier());
+       }
+}
+
+TargetingMode *Interpreter::GetTargetingMode(const Value &v) {
+       if (v.IsLiteral()) {
+               TargetingMode *t(new TargetingMode);
+               ReadTargetingMode(*t, *v.GetLiteral().GetProperties());
+               return t;
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetTargetingMode(v.GetIdentifier());
+       }
+}
+
+Vector<int> Interpreter::GetVector(const Value &v) {
+       if (v.IsLiteral()) {
+               return Vector<int>(v.GetLiteral().GetX(), v.GetLiteral().GetY());
+       } else {
+               ReadDefinition(source.GetDefinition(v.GetIdentifier()));
+               return GetVector(v.GetIdentifier());
+       }
+}
+
+const vector<Value *> &Interpreter::GetValueArray(const Value &v) {
+       if (v.IsLiteral()) {
+               return v.GetLiteral().GetValues();
+       } else {
+               throw Error("cannot reference value arrays");
+       }
+}
+
+
+void Interpreter::ReadObject(const Definition &dfn) {
+       if (dfn.TypeName() == "BattleResources") {
+               battle::Resources *res(new battle::Resources);
+               int index(battleResources.size());
+               battleResources.push_back(res);
+               ReadBattleResources(*res, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, BATTLE_RESOURCES, index)));
+       } else if (dfn.TypeName() == "ComplexAnimation") {
+               ComplexAnimation *animation(new ComplexAnimation);
+               int index(complexAnimations.size());
+               complexAnimations.push_back(animation);
+               ReadComplexAnimation(*animation, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, COMPLEX_ANIMATION, index)));
+       } else if (dfn.TypeName() == "Font") {
+               Font *font(new Font);
+               int index(fonts.size());
+               fonts.push_back(font);
+               ReadFont(*font, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, FONT, index)));
+       } else if (dfn.TypeName() == "Frame") {
+               Frame *frame(new Frame);
+               int index(frames.size());
+               frames.push_back(frame);
+               ReadFrame(*frame, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, FRAME, index)));
+       } else if (dfn.TypeName() == "Gauge") {
+               Gauge *gauge(new Gauge);
+               int index(gauges.size());
+               gauges.push_back(gauge);
+               ReadGauge(*gauge, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, GAUGE, index)));
+       } else if (dfn.TypeName() == "Hero") {
+               Hero *hero(new Hero);
+               int index(heroes.size());
+               heroes.push_back(hero);
+               ReadHero(*hero, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, HERO, index)));
+       } else if (dfn.TypeName() == "Ikari") {
+               Ikari *ikari(new Ikari);
+               int index(ikaris.size());
+               ikaris.push_back(ikari);
+               ReadIkari(*ikari, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, IKARI, index)));
+       } else if (dfn.TypeName() == "Item") {
+               Item *item(new Item);
+               int index(items.size());
+               items.push_back(item);
+               ReadItem(*item, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, ITEM, index)));
+       } else if (dfn.TypeName() == "MenuProperties") {
+               graphics::MenuProperties *mprops(new graphics::MenuProperties);
+               int index(menuProperties.size());
+               menuProperties.push_back(mprops);
+               ReadMenuProperties(*mprops, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, MENU_PROPERTIES, index)));
+       } else if (dfn.TypeName() == "Monster") {
+               Monster *monster(new Monster);
+               int index(monsters.size());
+               monsters.push_back(monster);
+               ReadMonster(*monster, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, MONSTER, index)));
+       } else if (dfn.TypeName() == "PartyLayout") {
+               PartyLayout *layout(new PartyLayout);
+               int index(partyLayouts.size());
+               partyLayouts.push_back(layout);
+               ReadPartyLayout(*layout, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, PARTY_LAYOUT, index)));
+       } else if (dfn.TypeName() == "SimpleAnimation") {
+               SimpleAnimation *animation(new SimpleAnimation);
+               int index(simpleAnimations.size());
+               simpleAnimations.push_back(animation);
+               ReadSimpleAnimation(*animation, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, SIMPLE_ANIMATION, index)));
+       } else if (dfn.TypeName() == "Spell") {
+               Spell *spell(new Spell);
+               int index(spells.size());
+               spells.push_back(spell);
+               ReadSpell(*spell, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, SPELL, index)));
+       } else if (dfn.TypeName() == "Sprite") {
+               Sprite *sprite(new Sprite);
+               int index(sprites.size());
+               sprites.push_back(sprite);
+               ReadSprite(*sprite, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, SPRITE, index)));
+       } else if (dfn.TypeName() == "TargetingMode") {
+               TargetingMode *mode(new TargetingMode);
+               int index(targetingModes.size());
+               targetingModes.push_back(mode);
+               ReadTargetingMode(*mode, *dfn.GetProperties());
+               parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, TARGETING_MODE, index)));
+       } else {
+               throw Error("unhandled object type: " + dfn.TypeName());
+       }
+}
+
+
+void Interpreter::ReadBattleResources(battle::Resources &res, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "swapCursor") {
+                       res.swapCursor = GetSprite(*i->second);
+               } else if (i->first == "moveIcons") {
+                       res.moveIcons = GetSprite(*i->second);
+               } else if (i->first == "attackIcons") {
+                       res.attackIcons = GetSprite(*i->second);
+               } else if (i->first == "attackChoiceIcons") {
+                       res.attackChoiceIcons = GetSprite(*i->second);
+               } else if (i->first == "titleFrame") {
+                       res.titleFrame = GetFrame(*i->second);
+               } else if (i->first == "titleFont") {
+                       res.titleFont = GetFont(*i->second);
+               } else if (i->first == "heroTagFrame") {
+                       res.heroTagFrame = GetFrame(*i->second);
+               } else if (i->first == "activeHeroTagFrame") {
+                       res.activeHeroTagFrame = GetFrame(*i->second);
+               } else if (i->first == "smallHeroTagFrame") {
+                       res.smallHeroTagFrame = GetFrame(*i->second);
+               } else if (i->first == "lastSmallHeroTagFrame") {
+                       res.lastSmallHeroTagFrame = GetFrame(*i->second);
+               } else if (i->first == "heroTagFont") {
+                       res.heroTagFont = GetFont(*i->second);
+               } else if (i->first == "heroTagLabels") {
+                       res.heroTagLabels = GetSprite(*i->second);
+               } else if (i->first == "healthGauge") {
+                       res.healthGauge = GetGauge(*i->second);
+               } else if (i->first == "manaGauge") {
+                       res.manaGauge = GetGauge(*i->second);
+               } else if (i->first == "ikariGauge") {
+                       res.ikariGauge = GetGauge(*i->second);
+               } else if (i->first == "selectFrame") {
+                       res.selectFrame = GetFrame(*i->second);
+               } else if (i->first == "normalFont") {
+                       res.normalFont = GetFont(*i->second);
+               } else if (i->first == "disabledFont") {
+                       res.disabledFont = GetFont(*i->second);
+               } else if (i->first == "menuCursor") {
+                       res.menuCursor = GetSprite(*i->second);
+               } else if (i->first == "weaponTargetCursor") {
+                       res.weaponTargetCursor = GetSprite(*i->second);
+               } else if (i->first == "magicTargetCursor") {
+                       res.magicTargetCursor = GetSprite(*i->second);
+               } else if (i->first == "itemTargetCursor") {
+                       res.itemTargetCursor = GetSprite(*i->second);
+               } else if (i->first == "spellMenuHeadline") {
+                       res.spellMenuHeadline = GetString(*i->second);
+               } else if (i->first == "spellMenuProperties") {
+                       res.spellMenuProperties = GetMenuProperties(*i->second);
+               } else if (i->first == "itemMenuHeadline") {
+                       res.itemMenuHeadline = GetString(*i->second);
+               } else if (i->first == "itemMenuProperties") {
+                       res.itemMenuProperties = GetMenuProperties(*i->second);
+               } else if (i->first == "ikariMenuHeadline") {
+                       res.ikariMenuHeadline = GetString(*i->second);
+               } else if (i->first == "ikariMenuProperties") {
+                       res.ikariMenuProperties = GetMenuProperties(*i->second);
+               } else if (i->first == "noEquipmentText") {
+                       res.noEquipmentText = GetString(*i->second);
+               } else if (i->first == "escapeText") {
+                       res.escapeText = GetString(*i->second);
+               } else if (i->first == "numberAnimationPrototype") {
+                       res.numberAnimationPrototype = GetAnimation(*i->second);
+               } else if (i->first == "bigNumberSprite") {
+                       res.bigNumberSprite = GetSprite(*i->second);
+               } else if (i->first == "greenNumberSprite") {
+                       res.greenNumberSprite = GetSprite(*i->second);
+               } else if (i->first == "weaponMenuIcon") {
+                       res.weaponMenuIcon = GetSprite(*i->second);
+               } else if (i->first == "armorMenuIcon") {
+                       res.armorMenuIcon = GetSprite(*i->second);
+               } else if (i->first == "shieldMenuIcon") {
+                       res.shieldMenuIcon = GetSprite(*i->second);
+               } else if (i->first == "helmetMenuIcon") {
+                       res.helmetMenuIcon = GetSprite(*i->second);
+               } else if (i->first == "ringMenuIcon") {
+                       res.ringMenuIcon = GetSprite(*i->second);
+               } else if (i->first == "jewelMenuIcon") {
+                       res.jewelMenuIcon = GetSprite(*i->second);
+               } else if (i->first == "levelLabelCol") {
+                       res.levelLabelCol = GetNumber(*i->second);
+               } else if (i->first == "levelLabelRow") {
+                       res.levelLabelRow = GetNumber(*i->second);
+               } else if (i->first == "healthLabelCol") {
+                       res.healthLabelCol = GetNumber(*i->second);
+               } else if (i->first == "healthLabelRow") {
+                       res.healthLabelRow = GetNumber(*i->second);
+               } else if (i->first == "manaLabelCol") {
+                       res.manaLabelCol = GetNumber(*i->second);
+               } else if (i->first == "manaLabelRow") {
+                       res.manaLabelRow = GetNumber(*i->second);
+               } else if (i->first == "moveLabelCol") {
+                       res.moveLabelCol = GetNumber(*i->second);
+               } else if (i->first == "moveLabelRow") {
+                       res.moveLabelRow = GetNumber(*i->second);
+               } else if (i->first == "ikariLabelCol") {
+                       res.ikariLabelCol = GetNumber(*i->second);
+               } else if (i->first == "ikariLabelRow") {
+                       res.ikariLabelRow = GetNumber(*i->second);
+               } else if (i->first == "heroesBgColor") {
+                       res.heroesBgColor = GetColor(*i->second);
+               } else {
+                       throw Error("unknown BattleResources property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadComplexAnimation(ComplexAnimation &a, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "sprite") {
+                       a.SetSprite(GetSprite(*i->second));
+               } else if (i->first == "frametime") {
+                       a.SetFrameTime(GetNumber(*i->second));
+               } else if (i->first == "repeat") {
+                       a.SetRepeat(GetBoolean(*i->second));
+               } else if (i->first == "frames") {
+                       const vector<PropertyList *> &values(GetPropertyListArray(*i->second));
+                       for (vector<PropertyList *>::const_iterator i(values.begin()), end(values.end()); i != end; ++i) {
+                               ComplexAnimation::FrameProp frame;
+                               ReadComplexAnimationFrame(frame, **i);
+                               a.AddFrame(frame);
+                       }
+               } else {
+                       throw Error("unknown ComplexAnimation property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadComplexAnimationFrame(ComplexAnimation::FrameProp &f, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "column") {
+                       f.col = GetNumber(*i->second);
+               } else if (i->first == "row") {
+                       f.row = GetNumber(*i->second);
+               } else if (i->first == "disposition") {
+                       f.disposition = GetVector(*i->second);
+               } else {
+                       throw Error("unknown ComplexAnimationFrame property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadFont(Font &f, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "sprite") {
+                       f.SetSprite(GetSprite(*i->second));
+               } else if (i->first == "columnoffset") {
+                       f.SetColOffset(GetNumber(*i->second));
+               } else if (i->first == "rowoffset") {
+                       f.SetRowOffset(GetNumber(*i->second));
+               } else {
+                       throw Error("unknown Font property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadFrame(Frame &f, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "image") {
+                       f.SetSurface(GetImage(*i->second));
+               } else if (i->first == "border") {
+                       f.SetBorderSize(GetVector(*i->second));
+               } else if (i->first == "repeat") {
+                       f.SetRepeatSize(GetVector(*i->second));
+               } else if (i->first == "offset") {
+                       f.SetOffset(GetVector(*i->second));
+               } else {
+                       throw Error("unknown Frame property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadGauge(Gauge &g, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "image") {
+                       g.SetSurface(GetImage(*i->second));
+               } else if (i->first == "full") {
+                       g.SetFullOffset(GetVector(*i->second));
+               } else if (i->first == "empty") {
+                       g.SetEmptyOffset(GetVector(*i->second));
+               } else if (i->first == "height") {
+                       g.SetHeight(GetNumber(*i->second));
+               } else if (i->first == "start") {
+                       g.SetStartWidth(GetNumber(*i->second));
+               } else if (i->first == "repeat") {
+                       g.SetRepeatWidth(GetNumber(*i->second));
+               } else if (i->first == "end") {
+                       g.SetEndWidth(GetNumber(*i->second));
+               } else {
+                       throw Error("unknown Gauge property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadIkari(Ikari &ikari, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "name") {
+                       ikari.SetName(GetString(*i->second));
+               } else if (i->first == "cost") {
+                       ikari.SetCost(GetNumber(*i->second));
+               } else if (i->first == "targets") {
+                       ikari.GetTargetingMode() = *GetTargetingMode(*i->second);
+               } else if (i->first == "magical") {
+                       if (GetBoolean(*i->second)) {
+                               ikari.SetMagical();
+                       }
+               } else if (i->first == "physical") {
+                       if (GetBoolean(*i->second)) {
+                               ikari.SetPhysical();
+                       }
+               } else {
+                       throw Error("unknown Ikari property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadItem(Item &item, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "name") {
+                       item.SetName(GetString(*i->second));
+               } else if (i->first == "menuicon") {
+                       item.SetMenuIcon(GetSprite(*i->second));
+               } else if (i->first == "battle") {
+                       if (GetBoolean(*i->second)) {
+                               item.SetUsableInBattle();
+                       }
+               } else if (i->first == "targets") {
+                       item.GetTargetingMode() = *GetTargetingMode(*i->second);
+               } else if (i->first == "ikari") {
+                       item.SetIkari(GetIkari(*i->second));
+               } else if (i->first == "attackanimation") {
+                       item.SetAttackAnimation(GetAnimation(*i->second));
+               } else {
+                       throw Error("unknown Item property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadHero(Hero &h, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "name") {
+                       h.SetName(GetString(*i->second));
+               } else if (i->first == "sprite") {
+                       h.SetSprite(GetSprite(*i->second));
+               } else if (i->first == "level") {
+                       h.SetLevel(GetNumber(*i->second));
+               } else if (i->first == "maxHealth") {
+                       h.SetMaxHealth(GetNumber(*i->second));
+               } else if (i->first == "health") {
+                       h.SetHealth(GetNumber(*i->second));
+               } else if (i->first == "maxMana") {
+                       h.SetMaxMana(GetNumber(*i->second));
+               } else if (i->first == "mana") {
+                       h.SetMana(GetNumber(*i->second));
+               } else if (i->first == "ip") {
+                       h.SetIP(GetNumber(*i->second));
+               } else if (i->first == "stats") {
+                       battle::Stats stats;
+                       ReadStats(stats, *GetPropertyList(*i->second));
+                       h.SetStats(stats);
+               } else if (i->first == "attackAnimation") {
+                       h.SetAttackAnimation(GetAnimation(*i->second));
+               } else if (i->first == "spellAnimation") {
+                       h.SetSpellAnimation(GetAnimation(*i->second));
+               } else if (i->first == "meleeAnimation") {
+                       h.SetMeleeAnimation(GetAnimation(*i->second));
+               } else {
+                       throw Error("unknown Hero property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadMenuProperties(graphics::MenuProperties &mprops, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "font") {
+                       mprops.font = GetFont(*i->second);
+               } else if (i->first == "disabledFont") {
+                       mprops.disabledFont = GetFont(*i->second);
+               } else if (i->first == "cursor") {
+                       mprops.cursor = GetSprite(*i->second);
+               } else if (i->first == "charsPerEntry") {
+                       mprops.charsPerEntry = GetNumber(*i->second);
+               } else if (i->first == "rows") {
+                       mprops.rows = GetNumber(*i->second);
+               } else if (i->first == "rowGap") {
+                       mprops.rowGap = GetNumber(*i->second);
+               } else if (i->first == "iconSpace") {
+                       mprops.iconSpace = GetNumber(*i->second);
+               } else if (i->first == "cols") {
+                       mprops.cols = GetNumber(*i->second);
+               } else if (i->first == "colGap") {
+                       mprops.colGap = GetNumber(*i->second);
+               } else if (i->first == "delimiter") {
+                       mprops.delimiter = *GetString(*i->second);
+               } else if (i->first == "charsPerNumber") {
+                       mprops.charsPerNumber = GetNumber(*i->second);
+               } else if (i->first == "charsPerAdditionalText") {
+                       mprops.charsPerAdditionalText = GetNumber(*i->second);
+               } else if (i->first == "additionalTextGap") {
+                       mprops.additionalTextGap = GetNumber(*i->second);
+               } else {
+                       throw Error("unknown MenuProperties property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadMonster(Monster &m, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "name") {
+                       m.SetName(GetString(*i->second));
+               } else if (i->first == "sprite") {
+                       m.SetSprite(GetSprite(*i->second));
+               } else if (i->first == "level") {
+                       m.SetLevel(GetNumber(*i->second));
+               } else if (i->first == "maxHealth") {
+                       m.SetMaxHealth(GetNumber(*i->second));
+               } else if (i->first == "health") {
+                       m.SetHealth(GetNumber(*i->second));
+               } else if (i->first == "maxMana") {
+                       m.SetMaxMana(GetNumber(*i->second));
+               } else if (i->first == "mana") {
+                       m.SetMana(GetNumber(*i->second));
+               } else if (i->first == "stats") {
+                       battle::Stats stats;
+                       ReadStats(stats, *GetPropertyList(*i->second));
+                       m.SetStats(stats);
+               } else if (i->first == "attackAnimation") {
+                       m.SetAttackAnimation(GetAnimation(*i->second));
+               } else if (i->first == "meleeAnimation") {
+                       m.SetMeleeAnimation(GetAnimation(*i->second));
+               } else {
+                       throw Error("unknown Monster property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadPartyLayout(PartyLayout &p, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "positions") {
+                       const vector<Value *> &positions(GetValueArray(*i->second));
+                       for (vector<Value *>::const_iterator j(positions.begin()), end(positions.end()); j != end; ++j) {
+                               p.AddPosition(GetVector(**j));
+                       }
+               } else {
+                       throw Error("unknown PartyLayout property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadSimpleAnimation(SimpleAnimation &a, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "sprite") {
+                       a.SetSprite(GetSprite(*i->second));
+               } else if (i->first == "frametime") {
+                       a.SetFrameTime(GetNumber(*i->second));
+               } else if (i->first == "repeat") {
+                       a.SetRepeat(GetBoolean(*i->second));
+               } else if (i->first == "framecount") {
+                       a.SetNumFrames(GetNumber(*i->second));
+               } else if (i->first == "col") {
+                       a.SetCol(GetNumber(*i->second));
+               } else if (i->first == "row") {
+                       a.SetRow(GetNumber(*i->second));
+               } else {
+                       throw Error("unknown SimpleAnimation property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadSpell(Spell &s, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "name") {
+                       s.SetName(GetString(*i->second));
+               } else if (i->first == "cost") {
+                       s.SetCost(GetNumber(*i->second));
+               } else if (i->first == "battle") {
+                       if (GetBoolean(*i->second)) {
+                               s.SetUsableInBattle();
+                       }
+               } else if (i->first == "targets") {
+                       s.GetTargetingMode() = *GetTargetingMode(*i->second);
+               } else {
+                       throw Error("unknown Spell property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadSprite(Sprite &s, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "image") {
+                       s.SetSurface(GetImage(*i->second));
+               } else if (i->first == "size") {
+                       s.SetSize(GetVector(*i->second));
+               } else if (i->first == "offset") {
+                       s.SetOffset(GetVector(*i->second));
+               } else {
+                       throw Error("unknown Sprite property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadStats(Stats &s, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "atp") {
+                       s.SetAttack(GetNumber(*i->second));
+               } else if (i->first == "dfp") {
+                       s.SetDefense(GetNumber(*i->second));
+               } else if (i->first == "str") {
+                       s.SetStrength(GetNumber(*i->second));
+               } else if (i->first == "agl") {
+                       s.SetAgility(GetNumber(*i->second));
+               } else if (i->first == "int") {
+                       s.SetIntelligence(GetNumber(*i->second));
+               } else if (i->first == "gut") {
+                       s.SetGut(GetNumber(*i->second));
+               } else if (i->first == "mgr") {
+                       s.SetMagicResistance(GetNumber(*i->second));
+               } else {
+                       throw Error("unknown Stats property: " + i->first);
+               }
+       }
+}
+
+void Interpreter::ReadTargetingMode(TargetingMode &t, const PropertyList &props) {
+       for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
+               if (i->first == "ally") {
+                       if (GetBoolean(*i->second)) {
+                               t.TargetAlly();
+                       } else {
+                               t.TargetEnemy();
+                       }
+               } else if (i->first == "enemy") {
+                       if (GetBoolean(*i->second)) {
+                               t.TargetEnemy();
+                       } else {
+                               t.TargetAlly();
+                       }
+               } else if (i->first == "all") {
+                       if (GetBoolean(*i->second)) {
+                               t.TargetAll();
+                       }
+               } else if (i->first == "multiple") {
+                       if (GetBoolean(*i->second)) {
+                               t.TargetMultiple();
+                       }
+               } else if (i->first == "single") {
+                       if (GetBoolean(*i->second)) {
+                               t.TargetSingle();
+                       }
+               } else {
+                       throw Error("unknown TargetingMode property: " + i->first);
+               }
+       }
+}
+
+}
diff --git a/src/loader/Interpreter.h b/src/loader/Interpreter.h
new file mode 100644 (file)
index 0000000..044a3c0
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Interpreter.h
+ *
+ *  Created on: Aug 26, 2012
+ *      Author: holy
+ */
+
+#ifndef LOADER_INTERPRETER_H_
+#define LOADER_INTERPRETER_H_
+
+#include "../geometry/Vector.h"
+#include "../graphics/Color.h"
+#include "../graphics/ComplexAnimation.h"
+
+#include <map>
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <SDL.h>
+
+namespace battle {
+       class Hero;
+       class Monster;
+       class PartyLayout;
+       struct Resources;
+       class Stats;
+}
+
+namespace common {
+       class Ikari;
+       class Item;
+       class Spell;
+       class TargetingMode;
+}
+
+namespace graphics {
+       class Animation;
+       class Font;
+       class Frame;
+       class Gauge;
+       struct MenuProperties;
+       class SimpleAnimation;
+       class Sprite;
+}
+
+namespace loader {
+
+class Definition;
+class ParsedSource;
+class PropertyList;
+class Value;
+
+class Interpreter {
+
+public:
+       class Error: public std::runtime_error {
+       public:
+               Error(const std::string &msg) : std::runtime_error("interpreter error: " + msg) { }
+       };
+
+public:
+       Interpreter(const ParsedSource &source) : source(source) { }
+       ~Interpreter();
+private:
+       Interpreter(const Interpreter &);
+       Interpreter &operator =(const Interpreter &);
+
+public:
+       void ReadSource();
+
+public:
+       graphics::Animation *GetAnimation(const std::string &name);
+       battle::Resources *GetBattleResources(const std::string &name);
+       bool GetBoolean(const std::string &name) const;
+       const graphics::Color &GetColor(const std::string &name) const;
+       graphics::Font *GetFont(const std::string &name);
+       graphics::Frame *GetFrame(const std::string &name);
+       graphics::Gauge *GetGauge(const std::string &name);
+       battle::Hero *GetHero(const std::string &name);
+       common::Ikari *GetIkari(const std::string &name);
+       common::Item *GetItem(const std::string &name);
+       graphics::MenuProperties *GetMenuProperties(const std::string &name);
+       battle::Monster *GetMonster(const std::string &name);
+       int GetNumber(const std::string &name) const;
+       battle::PartyLayout *GetPartyLayout(const std::string &name);
+       const char *GetPath(const std::string &name) const;
+       common::Spell *GetSpell(const std::string &name);
+       graphics::Sprite *GetSprite(const std::string &name);
+       const char *GetString(const std::string &name) const;
+       common::TargetingMode *GetTargetingMode(const std::string &name);
+       geometry::Vector<int> GetVector(const std::string &name) const;
+
+public:
+       const std::vector<battle::Resources *> &BattleResources() const { return battleResources; }
+       const std::vector<bool> &Booleans() const { return booleans; }
+       const std::vector<graphics::Color> &Colors() const { return colors; }
+       const std::vector<graphics::ComplexAnimation *> &ComplexAnimations() const { return complexAnimations; }
+       const std::vector<graphics::Font *> &Fonts() const { return fonts; }
+       const std::vector<graphics::Frame *> &Frames() const { return frames; }
+       const std::vector<graphics::Gauge *> &Gauges() const { return gauges; }
+       const std::vector<battle::Hero *> &Heroes() const { return heroes; }
+       const std::vector<common::Ikari *> &Ikaris() const { return ikaris; }
+       const std::vector<SDL_Surface *> &Images() const { return images; }
+       const std::vector<common::Item *> &Items() const { return items; }
+       const std::vector<graphics::MenuProperties *> &MenuProperties() const { return menuProperties; }
+       const std::vector<battle::Monster *> &Monsters() const { return monsters; }
+       const std::vector<int> &Numbers() const { return numbers; }
+       const std::vector<battle::PartyLayout *> &PartyLayouts() const { return partyLayouts; }
+       const std::vector<graphics::SimpleAnimation *> &SimpleAnimations() const { return simpleAnimations; }
+       const std::vector<common::Spell *> &Spells() const { return spells; }
+       const std::vector<graphics::Sprite *> &Sprites() const { return sprites; }
+       const std::vector<const char *> &Strings() const { return strings; }
+       const std::vector<common::TargetingMode *> &TargetingModes() const { return targetingModes; }
+       const std::vector<geometry::Vector<int> > &Vectors() const { return vectors; }
+
+private:
+       void ReadDefinition(const Definition &);
+       void ReadLiteral(const Definition &);
+       void ReadObject(const Definition &);
+
+       graphics::Animation *GetAnimation(const Value &);
+       battle::Resources *GetBattleResources(const Value &);
+       graphics::Color GetColor(const Value &);
+       bool GetBoolean(const Value &);
+       graphics::Font *GetFont(const Value &);
+       graphics::Frame *GetFrame(const Value &);
+       graphics::Gauge *GetGauge(const Value &);
+       battle::Hero *GetHero(const Value &);
+       common::Ikari *GetIkari(const Value &);
+       SDL_Surface *GetImage(const Value &);
+       common::Item *GetItem(const Value &);
+       graphics::MenuProperties *GetMenuProperties(const Value &);
+       battle::Monster *GetMonster(const Value &);
+       int GetNumber(const Value &);
+       battle::PartyLayout *GetPartyLayout(const Value &);
+       const PropertyList *GetPropertyList(const Value &);
+       const std::vector<PropertyList *> &GetPropertyListArray(const Value &);
+       const char *GetPath(const Value &);
+       common::Spell *GetSpell(const Value &);
+       graphics::Sprite *GetSprite(const Value &);
+       const char *GetString(const Value &);
+       common::TargetingMode *GetTargetingMode(const Value &);
+       const std::vector<Value *> &GetValueArray(const Value &);
+       geometry::Vector<int> GetVector(const Value &);
+
+       void ReadBattleResources(battle::Resources &, const PropertyList &);
+       void ReadComplexAnimation(graphics::ComplexAnimation &, const PropertyList &);
+       void ReadComplexAnimationFrame(graphics::ComplexAnimation::FrameProp &, const PropertyList &);
+       void ReadFont(graphics::Font &, const PropertyList &);
+       void ReadFrame(graphics::Frame &, const PropertyList &);
+       void ReadGauge(graphics::Gauge &, const PropertyList &);
+       void ReadHero(battle::Hero &, const PropertyList &);
+       void ReadIkari(common::Ikari &, const PropertyList &);
+       void ReadItem(common::Item &, const PropertyList &);
+       void ReadMenuProperties(graphics::MenuProperties &, const PropertyList &);
+       void ReadMonster(battle::Monster &, const PropertyList &);
+       void ReadPartyLayout(battle::PartyLayout &, const PropertyList &);
+       void ReadSimpleAnimation(graphics::SimpleAnimation &, const PropertyList &);
+       void ReadSpell(common::Spell &, const PropertyList &);
+       void ReadSprite(graphics::Sprite &, const PropertyList &);
+       void ReadStats(battle::Stats &, const PropertyList &);
+       void ReadTargetingMode(common::TargetingMode &, const PropertyList &);
+
+private:
+       const ParsedSource &source;
+       enum Type {
+               BATTLE_RESOURCES,
+               BOOLEAN,
+               COLOR,
+               COMPLEX_ANIMATION,
+               FONT,
+               FRAME,
+               GAUGE,
+               HERO,
+               IKARI,
+               IMAGE,
+               ITEM,
+               MENU_PROPERTIES,
+               MONSTER,
+               NUMBER,
+               PARTY_LAYOUT,
+               PATH,
+               PROPERTY_LIST_ARRAY,
+               SIMPLE_ANIMATION,
+               SPELL,
+               SPRITE,
+               STRING,
+               TARGETING_MODE,
+               VECTOR,
+               VALUE_ARRAY,
+       };
+       struct ParsedDefinition {
+               ParsedDefinition(const Definition *dfn, Type type, int index)
+               : dfn(dfn), type(type), index(index) { }
+               const Definition *dfn;
+               Type type;
+               int index;
+       };
+       std::map<std::string, ParsedDefinition> parsedDefinitions;
+
+       std::map<std::string, SDL_Surface *> imageCache;
+
+       std::vector<battle::Resources *> battleResources;
+       std::vector<bool> booleans;
+       std::vector<graphics::Color> colors;
+       std::vector<graphics::ComplexAnimation *> complexAnimations;
+       std::vector<graphics::Font *> fonts;
+       std::vector<graphics::Frame *> frames;
+       std::vector<graphics::Gauge *> gauges;
+       std::vector<battle::Hero *> heroes;
+       std::vector<common::Ikari *> ikaris;
+       std::vector<SDL_Surface *> images;
+       std::vector<common::Item *> items;
+       std::vector<graphics::MenuProperties *> menuProperties;
+       std::vector<battle::Monster *> monsters;
+       std::vector<int> numbers;
+       std::vector<battle::PartyLayout *> partyLayouts;
+       std::vector<PropertyList *> propertyLists;
+       std::vector<std::vector<PropertyList *> > propertyListArrays;
+       std::vector<graphics::SimpleAnimation *> simpleAnimations;
+       std::vector<common::Spell *> spells;
+       std::vector<graphics::Sprite *> sprites;
+       std::vector<const char *> strings;
+       std::vector<common::TargetingMode *> targetingModes;
+       std::vector<std::vector<Value *> > valueArrays;
+       std::vector<geometry::Vector<int> > vectors;
+
+};
+
+}
+
+#endif /* LOADER_INTERPRETER_H_ */
diff --git a/src/loader/ParsedSource.cpp b/src/loader/ParsedSource.cpp
new file mode 100644 (file)
index 0000000..c0d3a41
--- /dev/null
@@ -0,0 +1,479 @@
+/*
+ * ParsedSource.cpp
+ *
+ *  Created on: Aug 26, 2012
+ *      Author: holy
+ */
+
+#include "ParsedSource.h"
+
+#include "utility.h"
+
+#include <ostream>
+#include <stdexcept>
+
+using std::map;
+using std::runtime_error;
+using std::string;
+using std::vector;
+
+namespace loader {
+
+ParsedSource::~ParsedSource() {
+       for (map<string, Declaration *>::const_iterator i(declarations.begin()), end(declarations.end()); i != end; ++i) {
+               delete i->second;
+       }
+}
+
+void ParsedSource::AddDeclaration(Declaration *d) {
+       map<string, Declaration *>::iterator i(declarations.find(d->Identifier()));
+       if (i != declarations.end()) {
+               if (d->TypeName() != i->second->TypeName()) {
+                       throw runtime_error("invalid redeclaration of " + i->second->TypeName() + " " + d->Identifier());
+               }
+       } else {
+               declarations.insert(std::make_pair(d->Identifier(), d));
+       }
+}
+
+void ParsedSource::AddDefinition(Definition *d) {
+       map<string, Definition *>::iterator i(definitions.find(d->Identifier()));
+       if (i != definitions.end()) {
+               throw runtime_error("redefinition of " + i->second->TypeName() + " " + d->Identifier());
+       } else {
+               definitions.insert(std::make_pair(d->Identifier(), d));
+       }
+}
+
+void ParsedSource::ExportDeclaration(Declaration *d) {
+       AddDeclaration(d);
+       exports.insert(d->Identifier());
+}
+
+void ParsedSource::ExportIdentifier(const std::string &i) {
+       if (declarations.count(i)) {
+               exports.insert(i);
+       } else {
+               throw runtime_error("cannot export undeclared identifier " + i);
+       }
+}
+
+
+bool ParsedSource::IsDeclared(const std::string &name) const {
+       return declarations.count(name);
+}
+
+Declaration &ParsedSource::GetDeclaration(const std::string &name) {
+       map<string, Declaration *>::const_iterator i(declarations.find(name));
+       if (i != declarations.end()) {
+               return *i->second;
+       } else {
+               throw runtime_error("undeclared identifier " + name);
+       }
+}
+
+const Declaration &ParsedSource::GetDeclaration(const std::string &name) const {
+       map<string, Declaration *>::const_iterator i(declarations.find(name));
+       if (i != declarations.end()) {
+               return *i->second;
+       } else {
+               throw runtime_error("undeclared identifier " + name);
+       }
+}
+
+bool ParsedSource::IsDefined(const std::string &name) const {
+       return definitions.count(name);
+}
+
+Definition &ParsedSource::GetDefinition(const std::string &name) {
+       map<string, Definition *>::const_iterator i(definitions.find(name));
+       if (i != definitions.end()) {
+               return *i->second;
+       } else {
+               string msg("undefined identifier " + name);
+               map<string, Declaration *>::const_iterator i(declarations.find(name));
+               if (i != declarations.end()) {
+                       msg += ", declared as " + i->second->TypeName();
+               } else {
+                       msg += ", not declared";
+               }
+               throw runtime_error(msg);
+       }
+}
+
+const Definition &ParsedSource::GetDefinition(const std::string &name) const {
+       map<string, Definition *>::const_iterator i(definitions.find(name));
+       if (i != definitions.end()) {
+               return *i->second;
+       } else {
+               string msg("undefined identifier " + name);
+               map<string, Declaration *>::const_iterator i(declarations.find(name));
+               if (i != declarations.end()) {
+                       msg += ", declared as " + i->second->TypeName();
+               } else {
+                       msg += ", not declared";
+               }
+               throw runtime_error(msg);
+       }
+}
+
+void ParsedSource::WriteHeader(std::ostream &out) const {
+       for (std::set<string>::const_iterator i(exports.begin()), end(exports.end()); i != end; ++i) {
+               out << GetDeclaration(*i).TypeName() << ' ' << *i << std::endl;
+       }
+}
+
+
+Definition::~Definition() {
+       if (isLiteral) {
+               delete reinterpret_cast<Literal *>(value);
+       } else {
+               delete reinterpret_cast<PropertyList *>(value);
+       }
+}
+
+void Definition::SetValue(Literal *v) {
+       value = v;
+       isLiteral = true;
+}
+
+void Definition::SetValue(PropertyList *v) {
+       value = v;
+       isLiteral = false;
+}
+
+Literal *Definition::GetLiteral() {
+       if (isLiteral) {
+               return reinterpret_cast<Literal *>(value);
+       } else {
+               throw runtime_error("tried to access properties as literal");
+       }
+}
+
+const Literal *Definition::GetLiteral() const {
+       if (isLiteral) {
+               return reinterpret_cast<Literal *>(value);
+       } else {
+               throw runtime_error("tried to access properties as literal");
+       }
+}
+
+PropertyList *Definition::GetProperties() {
+       if (!isLiteral) {
+               return reinterpret_cast<PropertyList *>(value);
+       } else {
+               throw runtime_error("tried to access literal value as property list");
+       }
+}
+
+const PropertyList *Definition::GetProperties() const {
+       if (!isLiteral) {
+               return reinterpret_cast<PropertyList *>(value);
+       } else if (GetLiteral()->GetType() == Literal::OBJECT) {
+               return GetLiteral()->GetProperties();
+       } else {
+               throw runtime_error("tried to access literal value as property list");
+       }
+}
+
+
+PropertyList::~PropertyList() {
+       for (map<string, Value *>::iterator i(props.begin()), end(props.end()); i != end; ++i) {
+               delete i->second;
+       }
+}
+
+
+Literal::Literal(const vector<Value *> &v)
+: props(0)
+, values(v)
+, i1(0), i2(0)
+, i3(0), i4(0)
+, b(false)
+, type(ARRAY_VALUES) {
+
+}
+
+Literal::Literal(const std::vector<PropertyList *> &pls)
+: props(0)
+, propertyLists(pls)
+, i1(0), i2(0)
+, i3(0), i4(0)
+, b(false)
+, type(ARRAY_PROPS) {
+
+}
+
+Literal::Literal(bool b)
+: props(0)
+, i1(0), i2(0)
+, i3(0), i4(0)
+, b(b)
+, type(BOOLEAN) {
+
+}
+
+Literal::Literal(int r, int g, int b, int a)
+: props(0)
+, i1(r), i2(g)
+, i3(b), i4(a)
+, b(false)
+, type(COLOR) {
+
+}
+
+Literal::Literal(int number)
+: props(0)
+, i1(number), i2(0)
+, i3(0), i4(0)
+, b(false)
+, type(NUMBER) {
+
+}
+
+Literal::Literal(const string &dir, const string &path)
+: props(0)
+, str(CatPath(dir, path))
+, i1(0), i2(0)
+, i3(0), i4(0)
+, b(false)
+, type(STRING) {
+
+}
+
+Literal::Literal(const string &str)
+: props(0)
+, str(str)
+, i1(0), i2(0)
+, i3(0), i4(0)
+, b(false)
+, type(STRING) {
+
+}
+
+Literal::Literal(int x, int y)
+: props(0)
+, i1(x), i2(y)
+, i3(0), i4(0)
+, b(false)
+, type(VECTOR) {
+
+}
+
+Literal::Literal(const string &typeName, PropertyList *properties)
+: props(properties)
+, str(typeName)
+, i1(0), i2(0)
+, i3(0), i4(0)
+, b(false)
+, type(OBJECT) {
+
+}
+
+Literal::~Literal() {
+       switch (type) {
+               case ARRAY_VALUES:
+                       for (vector<Value *>::const_iterator i(values.begin()), end(values.end()); i != end; ++i) {
+                               delete *i;
+                       }
+                       break;
+               case ARRAY_PROPS:
+                       for (vector<PropertyList *>::const_iterator i(propertyLists.begin()), end(propertyLists.end()); i != end; ++i) {
+                               delete *i;
+                       }
+                       break;
+               case OBJECT:
+                       delete props;
+                       break;
+               default:
+                       break;
+       }
+}
+
+
+const vector<Value *> &Literal::GetValues() const {
+       if (type == ARRAY_VALUES) {
+               return values;
+       } else {
+               throw runtime_error("tried to access values of non-array literal");
+       }
+}
+
+const vector<PropertyList *> &Literal::GetPropertyLists() const {
+       if (type == ARRAY_PROPS) {
+               return propertyLists;
+       } else {
+               throw runtime_error("tried to access property lists of non-array literal");
+       }
+}
+
+bool Literal::GetBoolean() const {
+       if (type == BOOLEAN) {
+               return b;
+       } else {
+               throw runtime_error("tried to access boolean value of non-boolean literal");
+       }
+}
+
+int Literal::GetRed() const {
+       if (type == COLOR) {
+               return i1;
+       } else {
+               throw runtime_error("tried to access red component of non-color literal");
+       }
+}
+
+int Literal::GetGreen() const {
+       if (type == COLOR) {
+               return i2;
+       } else {
+               throw runtime_error("tried to access green component of non-color literal");
+       }
+}
+
+int Literal::GetBlue() const {
+       if (type == COLOR) {
+               return i3;
+       } else {
+               throw runtime_error("tried to access blue component of non-color literal");
+       }
+}
+
+int Literal::GetAlpha() const {
+       if (type == COLOR) {
+               return i4;
+       } else {
+               throw runtime_error("tried to access alpha component of non-color literal");
+       }
+}
+
+int Literal::GetNumber() const {
+       if (type == NUMBER) {
+               return i1;
+       } else {
+               throw runtime_error("tried to access numerical value of non-number literal");
+       }
+}
+
+const string &Literal::GetString() const {
+       if (type == STRING) {
+               return str;
+       } else {
+               throw runtime_error("tried to access string value of non-color literal");
+       }
+}
+
+int Literal::GetX() const {
+       if (type == VECTOR) {
+               return i1;
+       } else {
+               throw runtime_error("tried to access x component of non-vector literal");
+       }
+}
+
+int Literal::GetY() const {
+       if (type == VECTOR) {
+               return i2;
+       } else {
+               throw runtime_error("tried to access y component of non-vector literal");
+       }
+}
+
+const string &Literal::GetTypeName() const {
+       if (type == OBJECT) {
+               return str;
+       } else {
+               throw runtime_error("tried to access type name of non-object literal");
+       }
+}
+
+const PropertyList *Literal::GetProperties() const {
+       if (type == OBJECT) {
+               return props;
+       } else {
+               throw runtime_error("tried to access properties of non-object literal");
+       }
+}
+
+
+Value::~Value() {
+       if (isLiteral) {
+               delete literal;
+       }
+}
+
+const Literal &Value::GetLiteral() const {
+       if (isLiteral) {
+               return *literal;
+       } else {
+               throw runtime_error("tried to access literal of identifier value");
+       }
+}
+
+const std::string &Value::GetIdentifier() const {
+       if (!isLiteral) {
+               return identifier;
+       } else {
+               throw runtime_error("tried to access identifier of literal value");
+       }
+}
+
+}
+
+
+namespace std {
+
+ostream &operator <<(ostream &out, const loader::ParsedSource &source) {
+       out << "parsed source file" << endl;
+       out << "declared objects: " << endl;
+       for (map<string, loader::Declaration *>::const_iterator i(source.Declarations().begin()), end(source.Declarations().end()); i != end; ++i) {
+               out << " - " << i->first << " of type " << i->second->TypeName() << endl;
+       }
+       out << "defined objects: " << endl;
+       for (map<string, loader::Definition *>::const_iterator i(source.Definitions().begin()), end(source.Definitions().end()); i != end; ++i) {
+               out << " - " << i->first << " of type " << i->second->TypeName() << endl;
+               if (i->second->HasLiteralValue()) {
+                       out << "     literal value: " << *i->second->GetLiteral() << endl;
+               }
+       }
+       out << "exported objects: " << endl;
+       for (set<string>::const_iterator i(source.Exports().begin()), end(source.Exports().end()); i != end; ++i) {
+               out << " - " << *i << endl;
+       }
+       return out;
+}
+
+ostream &operator <<(ostream &out, const loader::Literal &l) {
+       switch (l.GetType()) {
+               case loader::Literal::ARRAY_VALUES:
+                       out << "array of values";
+                       break;
+               case loader::Literal::ARRAY_PROPS:
+                       out << "array of property lists";
+                       break;
+               case loader::Literal::BOOLEAN:
+                       out << "boolean, " << (l.GetBoolean() ? "true" : "false");
+                       break;
+               case loader::Literal::COLOR:
+                       out << "color, (" << l.GetRed() << ',' << l.GetGreen() << ',' << l.GetBlue() << ',' << l.GetAlpha() << ')';
+                       break;
+               case loader::Literal::NUMBER:
+                       out << "number, " << l.GetNumber();
+                       break;
+               case loader::Literal::PATH:
+                       out << "path, \"" << l.GetString() << '"';
+                       break;
+               case loader::Literal::STRING:
+                       out << "string, \"" << l.GetString() << '"';
+                       break;
+               case loader::Literal::VECTOR:
+                       out << "vector, <" << l.GetX() << ',' << l.GetY() << '>';
+                       break;
+               case loader::Literal::OBJECT:
+                       out << "object of type " << l.GetTypeName();
+                       break;
+       }
+       return out;
+}
+
+}
diff --git a/src/loader/ParsedSource.h b/src/loader/ParsedSource.h
new file mode 100644 (file)
index 0000000..98ecd84
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * ParsedSource.h
+ *
+ *  Created on: Aug 26, 2012
+ *      Author: holy
+ */
+
+#ifndef LOADER_PARSEDSOURCE_H_
+#define LOADER_PARSEDSOURCE_H_
+
+#include <iosfwd>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+namespace loader {
+
+class PropertyList;
+class Value;
+
+class Literal {
+
+public:
+       enum Type {
+               ARRAY_VALUES,
+               ARRAY_PROPS,
+               BOOLEAN,
+               COLOR,
+               NUMBER,
+               PATH,
+               STRING,
+               VECTOR,
+               OBJECT
+       };
+
+public:
+       explicit Literal(const std::vector<Value *> &);
+       explicit Literal(const std::vector<PropertyList *> &);
+       explicit Literal(bool);
+       Literal(int r, int g, int b, int a = 255);
+       explicit Literal(int number);
+       Literal(const std::string &dir, const std::string &path);
+       Literal(const std::string &);
+       Literal(int x, int y);
+       Literal(const std::string &typeName, PropertyList *properties);
+       ~Literal();
+private:
+       Literal(const Literal &);
+       Literal &operator =(const Literal &);
+
+public:
+       Type GetType() const { return type; }
+
+       const std::vector<Value *> &GetValues() const;
+       const std::vector<PropertyList *> &GetPropertyLists() const;
+       bool GetBoolean() const;
+       int GetRed() const;
+       int GetGreen() const;
+       int GetBlue() const;
+       int GetAlpha() const;
+       int GetNumber() const;
+       const std::string &GetString() const;
+       int GetX() const;
+       int GetY() const;
+       const std::string &GetTypeName() const;
+       const PropertyList *GetProperties() const;
+
+private:
+       PropertyList *props;
+       std::string str;
+       std::vector<Value *> values;
+       std::vector<PropertyList *> propertyLists;
+       int i1, i2, i3, i4;
+       bool b;
+       Type type;
+
+};
+
+
+class Value {
+
+public:
+       explicit Value(const std::string &identifier)
+       : literal(0), identifier(identifier), isLiteral(false) { }
+       explicit Value(Literal *literal)
+       : literal(literal), isLiteral(true) { }
+       ~Value();
+private:
+       Value(const Value &);
+       Value &operator =(const Value &);
+
+public:
+       bool IsLiteral() const { return isLiteral; }
+       const Literal &GetLiteral() const;
+       const std::string &GetIdentifier() const;
+
+private:
+       Literal *literal;
+       std::string identifier;
+       bool isLiteral;
+
+};
+
+
+class PropertyList {
+
+public:
+       PropertyList() { }
+       ~PropertyList();
+private:
+       PropertyList(const PropertyList &);
+       PropertyList &operator =(const PropertyList &);
+
+public:
+       void SetProperty(const std::string &name, Value *value) {
+               props[name] = value;
+       }
+
+       typedef std::map<std::string, Value *>::iterator Iterator;
+       typedef std::map<std::string, Value *>::const_iterator ConstIterator;
+       Iterator Begin() { return props.begin(); }
+       ConstIterator Begin() const { return props.begin(); }
+       Iterator End() { return props.end(); }
+       ConstIterator End() const { return props.end(); }
+
+private:
+       std::map<std::string, Value *> props;
+
+};
+
+
+class Declaration {
+
+public:
+       Declaration(const std::string &typeName, const std::string &identifier)
+       : typeName(typeName), identifier(identifier) { }
+       virtual ~Declaration() { }
+private:
+       Declaration(const Declaration &);
+       Declaration &operator =(const Declaration &);
+
+public:
+       const std::string &TypeName() const { return typeName; }
+       const std::string &Identifier() const { return identifier; }
+
+private:
+       std::string typeName;
+       std::string identifier;
+
+};
+
+
+class Definition
+: public Declaration {
+
+public:
+       Definition(const std::string &typeName, const std::string &identifier)
+       : Declaration(typeName, identifier), value(0), isLiteral(false) { }
+       virtual ~Definition();
+private:
+       Definition(const Definition &);
+       Definition &operator =(const Definition &);
+
+public:
+       void SetValue(Literal *);
+       void SetValue(PropertyList *);
+
+       bool HasLiteralValue() const { return isLiteral && value; }
+       bool HasProperties() const { return !isLiteral && value; }
+       Literal *GetLiteral();
+       const Literal *GetLiteral() const;
+       PropertyList *GetProperties();
+       const PropertyList *GetProperties() const;
+
+private:
+       void *value;
+       bool isLiteral;
+
+};
+
+
+class ParsedSource {
+
+public:
+       ParsedSource() { }
+       ~ParsedSource();
+private:
+       ParsedSource(const ParsedSource &);
+       ParsedSource &operator =(const ParsedSource &);
+
+public:
+       void AddDeclaration(Declaration *);
+       void AddDefinition(Definition *);
+       void ExportDeclaration(Declaration *);
+       void ExportIdentifier(const std::string &);
+
+       bool IsDeclared(const std::string &) const;
+       Declaration &GetDeclaration(const std::string &);
+       const Declaration &GetDeclaration(const std::string &) const;
+       bool IsDefined(const std::string &) const;
+       Definition &GetDefinition(const std::string &);
+       const Definition &GetDefinition(const std::string &) const;
+
+       const std::map<std::string, Declaration *> &Declarations() const { return declarations; }
+       const std::map<std::string, Definition *> &Definitions() const { return definitions; }
+       const std::set<std::string> &Exports() const { return exports; }
+
+public:
+       void WriteHeader(std::ostream &) const;
+
+private:
+       std::map<std::string, Declaration *> declarations;
+       std::map<std::string, Definition *> definitions;
+       std::set<std::string> exports;
+
+};
+
+}
+
+
+namespace std {
+
+ostream &operator <<(ostream &, const loader::ParsedSource &);
+ostream &operator <<(ostream &, const loader::Literal &);
+
+}
+
+#endif /* LOADER_PARSEDSOURCE_H_ */
diff --git a/src/loader/Parser.cpp b/src/loader/Parser.cpp
new file mode 100644 (file)
index 0000000..aa770ff
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * Parser.cpp
+ *
+ *  Created on: Aug 26, 2012
+ *      Author: holy
+ */
+
+#include "Parser.h"
+
+#include "utility.h"
+
+#include <auto_ptr.h>
+#include <fstream>
+
+using std::auto_ptr;
+using std::ifstream;
+using std::string;
+using std::vector;
+
+namespace loader {
+
+Parser::Parser(const string &file, ParsedSource &product)
+: file(file)
+, dirname(Dirname(file))
+, in(this->file.c_str())
+, tok(in)
+, product(product) {
+       if (!in) {
+               throw Error(file, 0, "unable to read file");
+       }
+}
+
+void Parser::Parse() {
+       while (tok.HasMore()) {
+               ParseStatement();
+       }
+}
+
+void Parser::ParseStatement() {
+       Tokenizer::Token t(GetToken());
+       switch (t.type) {
+               case Tokenizer::Token::KEYWORD_EXPORT:
+                       ParseExportDirective();
+                       break;
+               case Tokenizer::Token::KEYWORD_INCLUDE:
+                       ParseIncludeDirective();
+                       break;
+               case Tokenizer::Token::TYPE_NAME:
+                       tok.Putback(t);
+                       {
+                               Declaration *decl(ProbeDefinition());
+                               product.AddDeclaration(decl);
+                       }
+                       break;
+               default:
+                       throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type));
+       }
+}
+
+Tokenizer::Token Parser::GetToken() try {
+       return tok.GetNext();
+} catch (Tokenizer::LexerError &e) {
+       throw Error(file, e.Line(), e.what());
+}
+
+void Parser::ParseExportDirective() {
+       Tokenizer::Token t(GetToken());
+       if (t.type != Tokenizer::Token::IDENTIFIER) {
+               tok.Putback(t);
+               Declaration *decl(ProbeDefinition());
+               product.ExportDeclaration(decl);
+       } else {
+               product.ExportIdentifier(t.str);
+       }
+}
+
+void Parser::ParseIncludeDirective() {
+       Tokenizer::Token t(GetToken());
+       AssertTokenType(t.type, Tokenizer::Token::STRING);
+       Parser sub(CatPath(dirname, t.str), product);
+       sub.Parse();
+}
+
+Declaration *Parser::ProbeDefinition() {
+       string typeName(ParseTypeName());
+       string identifier(ParseIdentifier());
+
+       if (tok.HasMore()) {
+               Tokenizer::Token t(GetToken());
+               tok.Putback(t);
+               if (BeginOfPropertyList(t)) {
+                       auto_ptr<PropertyList> propertyList(ParsePropertyList());
+                       auto_ptr<Definition> dfn(new Definition(typeName, identifier));
+                       dfn->SetValue(propertyList.release());
+                       product.AddDefinition(dfn.get());
+                       return dfn.release();
+               } else if (BeginningOfPrimitiveLiteral(t)) {
+                       auto_ptr<Literal> literal(ParseLiteral());
+                       auto_ptr<Definition> dfn(new Definition(typeName, identifier));
+                       dfn->SetValue(literal.release());
+                       product.AddDefinition(dfn.get());
+                       return dfn.release();
+               }
+       }
+       return new Declaration(typeName, identifier);
+}
+
+bool Parser::BeginningOfLiteral(const Tokenizer::Token &t) const {
+       switch (t.type) {
+               case Tokenizer::Token::CHEVRON_OPEN:
+               case Tokenizer::Token::COLON:
+               case Tokenizer::Token::BRACKET_OPEN:
+               case Tokenizer::Token::PARENTHESIS_OPEN:
+               case Tokenizer::Token::NUMBER:
+               case Tokenizer::Token::STRING:
+               case Tokenizer::Token::KEYWORD_FALSE:
+               case Tokenizer::Token::KEYWORD_TRUE:
+               case Tokenizer::Token::TYPE_NAME:
+                       return true;
+               default:
+                       return false;
+       }
+}
+
+bool Parser::BeginningOfPrimitiveLiteral(const Tokenizer::Token &t) const {
+       switch (t.type) {
+               case Tokenizer::Token::CHEVRON_OPEN:
+               case Tokenizer::Token::COLON:
+               case Tokenizer::Token::BRACKET_OPEN:
+               case Tokenizer::Token::PARENTHESIS_OPEN:
+               case Tokenizer::Token::NUMBER:
+               case Tokenizer::Token::STRING:
+               case Tokenizer::Token::KEYWORD_FALSE:
+               case Tokenizer::Token::KEYWORD_TRUE:
+                       return true;
+               default:
+                       return false;
+       }
+}
+
+bool Parser::BeginOfPropertyList(const Tokenizer::Token &t) const {
+       return t.type == Tokenizer::Token::ANGLE_BRACKET_OPEN;
+}
+
+Definition *Parser::ParseDefinition() {
+       string typeName(ParseTypeName());
+       string identifier(ParseIdentifier());
+
+       Tokenizer::Token t(GetToken());
+       tok.Putback(t);
+       if (BeginOfPropertyList(t)) {
+               PropertyList *propertyList(ParsePropertyList());
+               Definition *dfn(new Definition(typeName, identifier));
+               dfn->SetValue(propertyList);
+               return dfn;
+       } else if (BeginningOfLiteral(t)) {
+               Literal *literal(ParseLiteral());
+               Definition *dfn(new Definition(typeName, identifier));
+               dfn->SetValue(literal);
+               return dfn;
+       } else {
+               throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type) + ", expected property-list or literal");
+       }
+}
+
+string Parser::ParseIdentifier() {
+       Tokenizer::Token t(GetToken());
+       AssertTokenType(t.type, Tokenizer::Token::IDENTIFIER);
+       return t.str;
+}
+
+string Parser::ParseTypeName() {
+       Tokenizer::Token t(GetToken());
+       AssertTokenType(t.type, Tokenizer::Token::TYPE_NAME);
+       return t.str;
+}
+
+PropertyList *Parser::ParsePropertyList() {
+       Tokenizer::Token t(GetToken());
+       AssertTokenType(t.type, Tokenizer::Token::ANGLE_BRACKET_OPEN);
+
+       auto_ptr<PropertyList> props(new PropertyList);
+
+       while (t.type != Tokenizer::Token::ANGLE_BRACKET_CLOSE) {
+               Tokenizer::Token name(GetToken());
+               AssertTokenType(name.type, Tokenizer::Token::IDENTIFIER);
+
+               t = GetToken();
+               AssertTokenType(t.type, Tokenizer::Token::COLON);
+
+               Value *value(ParseValue());
+               props->SetProperty(name.str, value);
+
+               t = GetToken();
+               if (t.type != Tokenizer::Token::ANGLE_BRACKET_CLOSE && t.type != Tokenizer::Token::COMMA) {
+                       throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type) + ", expected , or }");
+               }
+       }
+
+       return props.release();
+}
+
+Value *Parser::ParseValue() {
+       Tokenizer::Token t(GetToken());
+       if (t.type == Tokenizer::Token::IDENTIFIER) {
+               return new Value(t.str);
+       } else if (BeginningOfLiteral(t)) {
+               tok.Putback(t);
+               Literal *literal(ParseLiteral());
+               return new Value(literal);
+       } else {
+               throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type) + ", expected literal or identifier");
+       }
+}
+
+Literal *Parser::ParseLiteral() {
+       Tokenizer::Token t(GetToken());
+       if (t.type == Tokenizer::Token::TYPE_NAME) {
+               PropertyList *props(ParsePropertyList());
+               return new Literal(t.str, props);
+       } else if (BeginningOfLiteral(t)) {
+               switch (t.type) {
+                       case Tokenizer::Token::CHEVRON_OPEN:
+                               tok.Putback(t);
+                               return ParseVector();
+                       case Tokenizer::Token::COLON:
+                               t = GetToken();
+                               AssertTokenType(t.type, Tokenizer::Token::STRING);
+                               return new Literal(dirname, t.str);
+                       case Tokenizer::Token::BRACKET_OPEN:
+                               tok.Putback(t);
+                               return ParseArray();
+                       case Tokenizer::Token::PARENTHESIS_OPEN:
+                               tok.Putback(t);
+                               return ParseColor();
+                       case Tokenizer::Token::NUMBER:
+                               return new Literal(t.number);
+                       case Tokenizer::Token::STRING:
+                               return new Literal(t.str);
+                       case Tokenizer::Token::KEYWORD_FALSE:
+                               return new Literal(false);
+                       case Tokenizer::Token::KEYWORD_TRUE:
+                               return new Literal(true);
+                       default:
+                               throw std::logic_error("literal switch reached impossible default branch oO");
+               }
+       } else {
+               throw new Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type) + ", expected type-name or primitive");
+       }
+}
+
+Literal *Parser::ParseArray() {
+       Tokenizer::Token t(GetToken());
+       AssertTokenType(t.type, Tokenizer::Token::BRACKET_OPEN);
+
+       Tokenizer::Token probe(GetToken());
+       tok.Putback(probe);
+
+       if (probe.type == Tokenizer::Token::ANGLE_BRACKET_OPEN) {
+               vector<PropertyList *> values;
+               while (t.type != Tokenizer::Token::BRACKET_CLOSE) {
+                       PropertyList *value(ParsePropertyList());
+                       values.push_back(value);
+
+                       t = GetToken();
+                       if (t.type != Tokenizer::Token::BRACKET_CLOSE && t.type != Tokenizer::Token::COMMA) {
+                               throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type) + ", expected , or ]");
+                       }
+               }
+               return new Literal(values);
+       } else {
+               vector<Value *> values;
+               while (t.type != Tokenizer::Token::BRACKET_CLOSE) {
+                       Value *value(ParseValue());
+                       values.push_back(value);
+
+                       t = GetToken();
+                       if (t.type != Tokenizer::Token::BRACKET_CLOSE && t.type != Tokenizer::Token::COMMA) {
+                               throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type) + ", expected , or ]");
+                       }
+               }
+               return new Literal(values);
+       }
+}
+
+Literal *Parser::ParseColor() {
+       string msg("error parsing color");
+       Tokenizer::Token t(GetToken());
+       AssertTokenType(t.type, Tokenizer::Token::PARENTHESIS_OPEN, msg);
+
+       Tokenizer::Token red(GetToken());
+       AssertTokenType(red.type, Tokenizer::Token::NUMBER, "error parsing red component of color");
+
+       t = GetToken();
+       AssertTokenType(t.type, Tokenizer::Token::COMMA, msg);
+
+       Tokenizer::Token green(GetToken());
+       AssertTokenType(green.type, Tokenizer::Token::NUMBER, "error parsing green component of color");
+
+       t = GetToken();
+       AssertTokenType(t.type, Tokenizer::Token::COMMA, msg);
+
+       Tokenizer::Token blue(GetToken());
+       AssertTokenType(blue.type, Tokenizer::Token::NUMBER, "error parsing blue component of color");
+
+       t = GetToken();
+       if (t.type == Tokenizer::Token::PARENTHESIS_CLOSE) {
+               return new Literal(red.number, green.number, blue.number);
+       } else if (t.type != Tokenizer::Token::COMMA) {
+               Tokenizer::Token alpha(GetToken());
+               AssertTokenType(alpha.type, Tokenizer::Token::NUMBER, "error parsing alpha component of color");
+
+               t = GetToken();
+               AssertTokenType(t.type, Tokenizer::Token::PARENTHESIS_CLOSE, msg);
+
+               return new Literal(red.number, green.number, blue.number, alpha.number);
+       } else {
+               throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type) + ", expected , or ]");
+       }
+}
+
+Literal *Parser::ParseVector() {
+       std::string msg("error parsing vector");
+       Tokenizer::Token t(GetToken());
+       AssertTokenType(t.type, Tokenizer::Token::CHEVRON_OPEN, msg);
+
+       Tokenizer::Token x(GetToken());
+       AssertTokenType(x.type, Tokenizer::Token::NUMBER, "error parsing x component of vector");
+
+       t = GetToken();
+       AssertTokenType(t.type, Tokenizer::Token::COMMA, msg);
+
+       Tokenizer::Token y(GetToken());
+       AssertTokenType(y.type, Tokenizer::Token::NUMBER, "error parsing y component of vector");
+
+       t = GetToken();
+       AssertTokenType(t.type, Tokenizer::Token::CHEVRON_CLOSE, msg);
+
+       return new Literal(x.number, y.number);
+}
+
+void Parser::AssertTokenType(Tokenizer::Token::Type actual, Tokenizer::Token::Type expected) {
+       if (expected != actual) {
+               throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(actual) + ", expected " + TokenTypeToString(expected));
+       }
+}
+
+void Parser::AssertTokenType(Tokenizer::Token::Type actual, Tokenizer::Token::Type expected, const string &msg) {
+       if (expected != actual) {
+               throw Error(file, tok.Line(), msg + ": unexpected token " + TokenTypeToString(actual) + ", expected " + TokenTypeToString(expected));
+       }
+}
+
+}
diff --git a/src/loader/Parser.h b/src/loader/Parser.h
new file mode 100644 (file)
index 0000000..fd8f2cf
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Parser.h
+ *
+ *  Created on: Aug 26, 2012
+ *      Author: holy
+ */
+
+#ifndef LOADER_PARSER_H_
+#define LOADER_PARSER_H_
+
+#include "ParsedSource.h"
+#include "Tokenizer.h"
+
+#include <fstream>
+#include <iosfwd>
+#include <stdexcept>
+#include <string>
+
+namespace loader {
+
+class Declaration;
+class Definition;
+class Literal;
+class PropertyList;
+
+class Parser {
+
+public:
+       Parser(const std::string &file, ParsedSource &product);
+       ~Parser() { }
+private:
+       Parser(const Parser &);
+       Parser &operator =(const Parser &);
+
+public:
+       void Parse();
+
+public:
+       class Error: public std::runtime_error {
+       public:
+               Error(const std::string &file, int line, const std::string &msg)
+               : std::runtime_error(msg), file(file), line(line) { };
+               ~Error() throw() { }
+               const std::string &File() const { return file; }
+               int Line() const { return line; }
+       private:
+               std::string file;
+               int line;
+       };
+
+private:
+       Tokenizer::Token GetToken();
+       void ParseStatement();
+       void ParseExportDirective();
+       void ParseIncludeDirective();
+
+       Declaration *ProbeDefinition();
+       Definition *ParseDefinition();
+
+       std::string ParseIdentifier();
+       std::string ParseTypeName();
+
+       Value *ParseValue();
+       PropertyList *ParsePropertyList();
+       Literal *ParseLiteral();
+       Literal *ParseArray();
+       Literal *ParseColor();
+       Literal *ParseVector();
+
+private:
+       void AssertTokenType(Tokenizer::Token::Type actual, Tokenizer::Token::Type expected);
+       void AssertTokenType(Tokenizer::Token::Type actual, Tokenizer::Token::Type expected, const std::string &msg);
+       bool BeginningOfLiteral(const Tokenizer::Token &) const;
+       bool BeginningOfPrimitiveLiteral(const Tokenizer::Token &) const;
+       bool BeginOfPropertyList(const Tokenizer::Token &) const;
+
+private:
+       std::string file;
+       std::string dirname;
+       std::ifstream in;
+       Tokenizer tok;
+       ParsedSource &product;
+
+};
+
+}
+
+#endif /* LOADER_PARSER_H_ */
diff --git a/src/loader/Tokenizer.cpp b/src/loader/Tokenizer.cpp
new file mode 100644 (file)
index 0000000..6acda5f
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Tokenizer.cpp
+ *
+ *  Created on: Aug 26, 2012
+ *      Author: holy
+ */
+
+#include "Tokenizer.h"
+
+#include <istream>
+
+namespace loader {
+
+bool Tokenizer::HasMore() {
+       if (isPutback) return true;
+       ScanSpace();
+       if (!in) return false;
+
+       putback = ReadToken();
+       isPutback = true;
+       if (!skipComments || putback.type != Token::COMMENT) return true;
+
+       while (in && putback.type == Token::COMMENT) {
+               putback = ReadToken();
+               ScanSpace();
+       }
+       return putback.type != Token::COMMENT;
+}
+
+void Tokenizer::ScanSpace() {
+       std::istream::char_type c;
+       in.get(c);
+       while (in && std::isspace(c)) {
+               if (c == '\n') {
+                       ++line;
+               }
+               in.get(c);
+       }
+       if (in) {
+               in.putback(c);
+       }
+}
+
+void Tokenizer::Putback(const Token &t) {
+       if (isPutback) {
+               throw LexerError(line, "Tokenizer: double putback not supported");
+       } else {
+               putback = t;
+               isPutback = true;
+       }
+}
+
+const Tokenizer::Token &Tokenizer::Peek() {
+       if (!isPutback) {
+               putback = GetNext();
+               isPutback = true;
+       }
+       return putback;
+}
+
+Tokenizer::Token Tokenizer::GetNext() {
+       if (!HasMore()) {
+               throw LexerError(line, "read beyond last token");
+       }
+       if (isPutback) {
+               isPutback = false;
+               return putback;
+       } else {
+               return ReadToken();
+       }
+}
+
+Tokenizer::Token Tokenizer::ReadToken() {
+       ScanSpace();
+       std::istream::char_type c;
+       in.get(c);
+       switch (c) {
+               case Token::ANGLE_BRACKET_OPEN:
+               case Token::ANGLE_BRACKET_CLOSE:
+               case Token::CHEVRON_OPEN:
+               case Token::CHEVRON_CLOSE:
+               case Token::COLON:
+               case Token::COMMA:
+               case Token::BRACKET_OPEN:
+               case Token::BRACKET_CLOSE:
+               case Token::PARENTHESIS_OPEN:
+               case Token::PARENTHESIS_CLOSE:
+                       return Token ((Token::Type) c);
+               case '+': case '-':
+               case '0': case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9':
+                       in.putback(c);
+                       return ReadNumber();
+               case '"':
+                       in.putback(c);
+                       return ReadString();
+               case '/':
+                       {
+                               std::istream::char_type c2;
+                               in.get(c2);
+                               if (c2 == '/') {
+                                       return ReadComment();
+                               } else if (c2 == '*') {
+                                       return ReadMultilineComment();
+                               } else {
+                                       throw LexerError(line, std::string("Tokenizer: cannot parse token: ") + c + c2 + ": expected / or *");
+                               }
+                       }
+                       break;
+               default:
+                       in.putback(c);
+                       {
+                               Token t(ReadIdentifier());
+                               if (std::isupper(c)) {
+                                       t.type = Token::TYPE_NAME;
+                               } else if (std::islower(c)) {
+                                       CheckKeyword(t);
+                               } else {
+                                       throw LexerError(line, std::string("Tokenizer: cannot parse token: ") + c);
+                               }
+                               return t;
+                       }
+       }
+}
+
+Tokenizer::Token Tokenizer::ReadNumber() {
+       Token t(Token::NUMBER);
+       bool isNegative(false);
+
+       std::istream::char_type c;
+       in.get(c);
+       if (c == '-') {
+               isNegative = true;
+       } else if (c != '+') {
+               in.putback(c);
+       }
+
+       while (in.get(c)) {
+               if (!std::isdigit(c)) {
+                       in.putback(c);
+                       break;
+               }
+               t.number *= 10;
+               t.number += c - '0';
+       }
+
+       if (isNegative) t.number *= -1;
+
+       return t;
+}
+
+Tokenizer::Token Tokenizer::ReadString() {
+       Token t(Token::STRING);
+       bool escape(false);
+
+       std::istream::char_type c;
+       in.get(c);
+       if (c != '"') {
+               throw LexerError(line, "Tokenizer: strings must begin with '\"'");
+       }
+
+       while (in.get(c)) {
+               if (escape) {
+                       escape = false;
+                       switch (c) {
+                               case 'n':
+                                       t.str.push_back('\n');
+                                       break;
+                               case 'r':
+                                       t.str.push_back('\r');
+                                       break;
+                               case 't':
+                                       t.str.push_back('\t');
+                                       break;
+                               default:
+                                       t.str.push_back(c);
+                                       break;
+                       }
+               } else if (c == '"') {
+                       break;
+               } else if (c == '\\') {
+                       escape = true;
+               } else {
+                       t.str.push_back(c);
+               }
+       }
+
+       return t;
+}
+
+Tokenizer::Token Tokenizer::ReadIdentifier() {
+       Token t(Token::IDENTIFIER);
+
+       std::istream::char_type c;
+       while (in.get(c)) {
+               if (std::isalnum(c) || c == '_') {
+                       t.str.push_back(c);
+               } else {
+                       in.putback(c);
+                       break;
+               }
+       }
+
+       return t;
+}
+
+Tokenizer::Token Tokenizer::ReadComment() {
+       std::istream::char_type c;
+       while (in.get(c) && c != '\n');
+       ++line;
+       return Token(Token::COMMENT);
+}
+
+Tokenizer::Token Tokenizer::ReadMultilineComment() {
+       std::istream::char_type c;
+       while (in.get(c)) {
+               if (c == '*') {
+                       std::istream::char_type c2;
+                       if (in.get(c2) && c2 == '/') {
+                               break;
+                       }
+               } else if (c == '\n') {
+                       ++line;
+               }
+       }
+       return Token(Token::COMMENT);
+}
+
+bool Tokenizer::CheckKeyword(Token &t) {
+       if (t.str == "export") {
+               t.type = Token::KEYWORD_EXPORT;
+               return true;
+       } else if (t.str == "false") {
+               t.type = Token::KEYWORD_FALSE;
+               return true;
+       } else if (t.str == "include") {
+               t.type = Token::KEYWORD_INCLUDE;
+               return true;
+       } else if (t.str == "true") {
+               t.type = Token::KEYWORD_TRUE;
+               return true;
+       } else {
+               return false;
+       }
+}
+
+}
diff --git a/src/loader/Tokenizer.h b/src/loader/Tokenizer.h
new file mode 100644 (file)
index 0000000..dff96fc
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Tokenizer.h
+ *
+ *  Created on: Aug 26, 2012
+ *      Author: holy
+ */
+
+#ifndef LOADER_TOKENIZER_H_
+#define LOADER_TOKENIZER_H_
+
+#include <iosfwd>
+#include <ostream>
+#include <stdexcept>
+#include <string>
+
+namespace loader {
+
+class Tokenizer {
+
+public:
+       explicit Tokenizer(std::istream &in)
+       : in(in), line(1), isPutback(false), skipComments(true) { }
+       ~Tokenizer() { }
+private:
+       Tokenizer(const Tokenizer &);
+       Tokenizer &operator =(const Tokenizer &);
+
+public:
+       struct Token {
+
+               enum Type {
+                       UNKNOWN = 0,
+                       ANGLE_BRACKET_OPEN = '{',
+                       ANGLE_BRACKET_CLOSE = '}',
+                       CHEVRON_OPEN = '<',
+                       CHEVRON_CLOSE = '>',
+                       COLON = ':',
+                       COMMA = ',',
+                       BRACKET_OPEN = '[',
+                       BRACKET_CLOSE = ']',
+                       PARENTHESIS_OPEN = '(',
+                       PARENTHESIS_CLOSE = ')',
+                       NUMBER = '0',
+                       STRING = '"',
+                       KEYWORD_EXPORT = 'e',
+                       KEYWORD_FALSE = 'f',
+                       KEYWORD_INCLUDE = 'i',
+                       KEYWORD_TRUE = 't',
+                       IDENTIFIER = 'x',
+                       TYPE_NAME = 'n',
+                       COMMENT = 'c'
+               };
+
+               Token() : type(UNKNOWN), number(0) { }
+               explicit Token(Type t) : type(t), number(0) { }
+
+               Type type;
+               std::string str;
+               int number;
+
+       };
+
+       class LexerError: public std::runtime_error {
+       public:
+               LexerError(int line, const std::string &msg)
+               : std::runtime_error(msg), line(line) { }
+               int Line() const { return line; }
+       private:
+               int line;
+       };
+
+       bool HasMore();
+       Token GetNext();
+       const Token &Peek();
+       void Putback(const Token &);
+       int Line() const { return line; }
+
+private:
+       void ScanSpace();
+       Token ReadToken();
+
+       Token ReadNumber();
+       Token ReadString();
+       Token ReadIdentifier();
+
+       Token ReadComment();
+       Token ReadMultilineComment();
+
+       bool CheckKeyword(Token &);
+
+private:
+       std::istream &in;
+       Token putback;
+       int line;
+       bool isPutback;
+       bool skipComments;
+
+};
+
+inline const char *TokenTypeToString(Tokenizer::Token::Type t) {
+       switch (t) {
+               case Tokenizer::Token::ANGLE_BRACKET_OPEN:
+                       return "ANGLE_BRACKET_OPEN";
+               case Tokenizer::Token::ANGLE_BRACKET_CLOSE:
+                       return "ANGLE_BRACKET_CLOSE";
+               case Tokenizer::Token::CHEVRON_OPEN:
+                       return "CHEVRON_OPEN";
+               case Tokenizer::Token::CHEVRON_CLOSE:
+                       return "CHEVRON_CLOSE";
+               case Tokenizer::Token::COLON:
+                       return "COLON";
+               case Tokenizer::Token::COMMA:
+                       return "COMMA";
+               case Tokenizer::Token::BRACKET_OPEN:
+                       return "BRACKET_OPEN";
+               case Tokenizer::Token::BRACKET_CLOSE:
+                       return "BRACKET_CLOSE";
+               case Tokenizer::Token::PARENTHESIS_OPEN:
+                       return "PARENTHESIS_OPEN";
+               case Tokenizer::Token::PARENTHESIS_CLOSE:
+                       return "PARENTHESIS_CLOSE";
+               case Tokenizer::Token::NUMBER:
+                       return "NUMBER";
+               case Tokenizer::Token::STRING:
+                       return "STRING";
+               case Tokenizer::Token::KEYWORD_EXPORT:
+                       return "KEYWORD_EXPORT";
+               case Tokenizer::Token::KEYWORD_FALSE:
+                       return "KEYWORD_FALSE";
+               case Tokenizer::Token::KEYWORD_INCLUDE:
+                       return "KEYWORD_INCLUDE";
+               case Tokenizer::Token::KEYWORD_TRUE:
+                       return "KEYWORD_TRUE";
+               case Tokenizer::Token::IDENTIFIER:
+                       return "IDENTIFIER";
+               case Tokenizer::Token::TYPE_NAME:
+                       return "TYPE_NAME";
+               default:
+                       return "UNKNOWN";
+       }
+}
+
+inline std::ostream &operator <<(std::ostream &out, Tokenizer::Token::Type t) {
+       out << TokenTypeToString(t);
+       return out;
+}
+
+}
+
+#endif /* LOADER_TOKENIZER_H_ */
diff --git a/src/loader/utility.cpp b/src/loader/utility.cpp
new file mode 100644 (file)
index 0000000..ee88ea0
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * utility.cpp
+ *
+ *  Created on: Sep 1, 2012
+ *      Author: holy
+ */
+
+#include "utility.h"
+
+#include <cstring>
+#include <libgen.h>
+
+using std::string;
+
+namespace loader {
+
+string Dirname(const string &path) {
+       // unix version
+       char *str(new char[path.size() + 1]);
+       std::memcpy(str, path.c_str(), path.size());
+       str[path.size()] = '\0';
+       string dn(dirname(str));
+       delete str;
+       return dn;
+}
+
+string CatPath(const string &lhs, const string &rhs) {
+       // unix version
+       string path(lhs);
+       if (!path.empty() && path[path.size() - 1] != '/') {
+               path += '/';
+       }
+       if (!rhs.empty() && rhs[0] == '/') {
+               path.append(rhs, 1, string::npos);
+       } else {
+               path += rhs;
+       }
+       return path;
+}
+
+}
diff --git a/src/loader/utility.h b/src/loader/utility.h
new file mode 100644 (file)
index 0000000..b8ccd10
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * utility.h
+ *
+ *  Created on: Sep 1, 2012
+ *      Author: holy
+ */
+
+#ifndef LOADER_UTILITY_H_
+#define LOADER_UTILITY_H_
+
+#include <string>
+
+namespace loader {
+
+std::string Dirname(const std::string &path);
+
+std::string CatPath(const std::string &lhs, const std::string &rhs);
+
+}
+
+#endif /* LOADER_UTILITY_H_ */
index 3dc37d83327f89d558784f150833ce1d8c7b9e34..4280985185a5e8e144aa6f46d855c463dcf635d1 100644 (file)
@@ -25,6 +25,9 @@
 #include "graphics/Menu.h"
 #include "graphics/SimpleAnimation.h"
 #include "graphics/Sprite.h"
+#include "loader/Interpreter.h"
+#include "loader/ParsedSource.h"
+#include "loader/Parser.h"
 #include "sdl/InitImage.h"
 #include "sdl/InitScreen.h"
 #include "sdl/InitSDL.h"
@@ -55,6 +58,9 @@ using graphics::Gauge;
 using graphics::Menu;
 using graphics::SimpleAnimation;
 using graphics::Sprite;
+using loader::Interpreter;
+using loader::ParsedSource;
+using loader::Parser;
 using sdl::InitImage;
 using sdl::InitScreen;
 using sdl::InitSDL;
@@ -68,638 +74,92 @@ int main(int argc, char **argv) {
        const int width = 800;
        const int height = 480;
 
-       const int framerate = 33;
-
 //     std::srand(std::time(0));
 
        try {
                InitSDL sdl;
                InitImage image(IMG_INIT_PNG);
+
+               ParsedSource source;
+               Parser("test-data/test.l2s", source).Parse();
+               Parser("test-data/ikaris.l2s", source).Parse();
+               Parser("test-data/items.l2s", source).Parse();
+               Parser("test-data/spells.l2s", source).Parse();
+               Interpreter intp(source);
+               intp.ReadSource();
+
                InitScreen screen(width, height);
 
                // temporary test data
                SDL_Surface *bg(IMG_Load("test-data/battle-bg.png"));
-               PartyLayout monstersLayout;
-               monstersLayout.AddPosition(Vector<Uint8>(88, 88));
-               monstersLayout.AddPosition(Vector<Uint8>(128, 88));
-               monstersLayout.AddPosition(Vector<Uint8>(168, 88));
-               monstersLayout.AddPosition(Vector<Uint8>(208, 88));
-               PartyLayout heroesLayout;
-               heroesLayout.AddPosition(Vector<Uint8>(48, 136));
-               heroesLayout.AddPosition(Vector<Uint8>(128, 136));
-               heroesLayout.AddPosition(Vector<Uint8>(80, 152));
-               heroesLayout.AddPosition(Vector<Uint8>(160, 152));
-
-               SDL_Surface *monsterImg(IMG_Load("test-data/monster.png"));
-               Sprite monsterSprite(monsterImg, 64, 64);
-               Monster monster;
-               monster.SetName("Lizard");
-               monster.SetSprite(&monsterSprite);
-               monster.SetLevel(1);
-               monster.SetMaxHealth(8);
-               monster.SetHealth(8);
-               monster.SetStats(Stats(14, 6, 6, 6, 6, 6, 6));
-               monster.SetReward(3, 5);
-               ComplexAnimation monsterAttackAnimation(&monsterSprite, 4 * framerate);
-               monsterAttackAnimation.AddFrame(0, 1, Vector<int>(0, 16));
-               monsterAttackAnimation.AddFrame(0, 0, Vector<int>(0, 16));
-               monsterAttackAnimation.AddFrame(0, 1, Vector<int>(0, 16));
-               monster.SetAttackAnimation(&monsterAttackAnimation);
-               SDL_Surface *monsterMeleeImg(IMG_Load("test-data/attack-monster.png"));
-               Sprite monsterMeleeSprite(monsterMeleeImg, 96, 64);
-               SimpleAnimation monsterMeleeAnimation(&monsterMeleeSprite, framerate, 14);
-               monster.SetMeleeAnimation(&monsterMeleeAnimation);
-
-               SDL_Surface *maximImg(IMG_Load("test-data/maxim.png"));
-               Sprite maximSprite(maximImg, 64, 64);
-               Hero maxim;
-               maxim.SetName("Maxim");
-               maxim.SetLevel(1);
-               maxim.SetSprite(&maximSprite);
-               maxim.SetMaxHealth(33);
-               maxim.SetHealth(33);
-               maxim.SetMaxMana(20);
-               maxim.SetMana(20);
-               maxim.SetIP(0);
-               maxim.SetStats(Stats(28, 22, 28, 17, 14, 100, 10));
-               ComplexAnimation maximAttackAnimation(&maximSprite, framerate);
-               maximAttackAnimation.AddFrames(1, 0, Vector<int>(0,  0), 7);
-               maximAttackAnimation.AddFrames(1, 0, Vector<int>(4, -1), 2);
-               maximAttackAnimation.AddFrames(2, 0, Vector<int>(4, -2), 2);
-               maximAttackAnimation.AddFrames(2, 0, Vector<int>(6, -2), 2);
-               maximAttackAnimation.AddFrames(2, 1, Vector<int>(6, -1), 1);
-               maximAttackAnimation.AddFrames(2, 1, Vector<int>(3, -1), 2);
-               maximAttackAnimation.AddFrames(2, 1, Vector<int>(0,  0), 1);
-               maximAttackAnimation.AddFrames(2, 2, Vector<int>(0,  0), 2);
-               maximAttackAnimation.AddFrames(2, 2, Vector<int>(2,  0), 1);
-               maximAttackAnimation.AddFrames(1, 0, Vector<int>(0,  0), 7);
-               maxim.SetAttackAnimation(&maximAttackAnimation);
-               ComplexAnimation maximSpellAnimation(&maximSprite, 5 * framerate);
-               maximSpellAnimation.AddFrames(3, 0, Vector<int>(), 2);
-               maximSpellAnimation.AddFrame(3, 1);
-               maxim.SetSpellAnimation(&maximSpellAnimation);
-               SDL_Surface *maximMeleeImg(IMG_Load("test-data/melee-maxim.png"));
-               Sprite maximMeleeSprite(maximMeleeImg, 96, 96);
-               SimpleAnimation maximMeleeAnimation(&maximMeleeSprite, 2 * framerate, 4);
-               maxim.SetMeleeAnimation(&maximMeleeAnimation);
-
-               SDL_Surface *selanImg(IMG_Load("test-data/selan.png"));
-               Sprite selanSprite(selanImg, 64, 64);
-               Hero selan;
-               selan.SetName("Selan");
-               selan.SetLevel(1);
-               selan.SetSprite(&selanSprite);
-               selan.SetMaxHealth(28);
-               selan.SetHealth(28);
-               selan.SetMaxMana(23);
-               selan.SetMana(23);
-               selan.SetIP(0);
-               selan.SetStats(Stats(23, 21, 23, 19, 22, 80, 13));
-               ComplexAnimation selanAttackAnimation(&selanSprite, framerate);
-               selanAttackAnimation.AddFrames(1, 0, Vector<int>(4, 0), 2);
-               selanAttackAnimation.AddFrame(1, 0, Vector<int>(8, 2));
-               selanAttackAnimation.AddFrame(2, 0, Vector<int>(10, 4));
-               selanAttackAnimation.AddFrame(2, 0, Vector<int>(14, 4));
-               selanAttackAnimation.AddFrames(2, 0, Vector<int>(12, 2), 3);
-               selanAttackAnimation.AddFrames(2, 1, Vector<int>(14, 2), 2);
-               selanAttackAnimation.AddFrame(2, 1, Vector<int>(2, 0));
-               selanAttackAnimation.AddFrame(2, 2, Vector<int>(-2, -4));
-               selanAttackAnimation.AddFrame(2, 2, Vector<int>(-8, -8));
-               selanAttackAnimation.AddFrame(2, 2);
-               selan.SetAttackAnimation(&selanAttackAnimation);
-               ComplexAnimation selanSpellAnimation(&selanSprite, framerate);
-               selanSpellAnimation.AddFrames(2, 0, Vector<int>(), 3);
-               selanSpellAnimation.AddFrames(2, 1, Vector<int>(), 2);
-               selanSpellAnimation.AddFrames(2, 2, Vector<int>(), 3);
-               selanSpellAnimation.AddFrames(2, 3, Vector<int>(), 2);
-               selan.SetSpellAnimation(&selanSpellAnimation);
-               SDL_Surface *selanMeleeImg(IMG_Load("test-data/melee-selan.png"));
-               Sprite selanMeleeSprite(selanMeleeImg, 96, 96);
-               SimpleAnimation selanMeleeAnimation(&selanMeleeSprite, 2 * framerate, 4);
-               selan.SetMeleeAnimation(&selanMeleeAnimation);
-
-               SDL_Surface *guyImg(IMG_Load("test-data/guy.png"));
-               Sprite guySprite(guyImg, 64, 64);
-               Hero guy;
-               guy.SetName("Guy");
-               guy.SetLevel(1);
-               guy.SetSprite(&guySprite);
-               guy.SetMaxHealth(38);
-               guy.SetHealth(38);
-               guy.SetMaxMana(0);
-               guy.SetMana(0);
-               guy.SetIP(0);
-               guy.SetStats(Stats(38, 25, 38, 13, 8, 90, 8));
-               ComplexAnimation guyAttackAnimation(&guySprite, framerate);
-               guyAttackAnimation.AddFrames(1, 0, Vector<int>(-4, 0), 2);
-               guyAttackAnimation.AddFrames(1, 0, Vector<int>(-8, 0), 2);
-               guyAttackAnimation.AddFrames(2, 0, Vector<int>(-8, 0), 2);
-               guyAttackAnimation.AddFrame(2, 0, Vector<int>(-4, 0));
-               guyAttackAnimation.AddFrames(2, 0, Vector<int>(), 2);
-               guyAttackAnimation.AddFrame(2, 1);
-               guyAttackAnimation.AddFrame(2, 1, Vector<int>(4, 0));
-               guyAttackAnimation.AddFrame(2, 1, Vector<int>(10, 0));
-               guyAttackAnimation.AddFrame(2, 2, Vector<int>(10, 0));
-               guyAttackAnimation.AddFrame(2, 2);
-               guy.SetAttackAnimation(&guyAttackAnimation);
-               SDL_Surface *guyMeleeImg(IMG_Load("test-data/melee-guy.png"));
-               Sprite guyMeleeSprite(guyMeleeImg, 96, 96);
-               SimpleAnimation guyMeleeAnimation(&guyMeleeSprite, 2 * framerate, 4);
-               guy.SetMeleeAnimation(&guyMeleeAnimation);
-
-               SDL_Surface *dekarImg(IMG_Load("test-data/dekar.png"));
-               Sprite dekarSprite(dekarImg, 64, 64);
-               Hero dekar;
-               dekar.SetName("Dekar");
-               dekar.SetLevel(1);
-               dekar.SetSprite(&dekarSprite);
-               dekar.SetMaxHealth(38);
-               dekar.SetHealth(38);
-               dekar.SetMaxMana(0);
-               dekar.SetMana(0);
-               dekar.SetIP(0);
-               dekar.SetStats(Stats(46, 29, 46, 13, 7, 100, 5));
-               ComplexAnimation dekarAttackAnimation(&dekarSprite, framerate);
-               dekarAttackAnimation.AddFrame(1, 0, Vector<int>(4, 0));
-               dekarAttackAnimation.AddFrame(1, 0, Vector<int>(8, 2));
-               dekarAttackAnimation.AddFrame(2, 0, Vector<int>(12, 4));
-               dekarAttackAnimation.AddFrame(2, 0, Vector<int>(16, 4));
-               dekarAttackAnimation.AddFrames(2, 0, Vector<int>(10, 2), 4);
-               dekarAttackAnimation.AddFrame(2, 1, Vector<int>(6, 2));
-               dekarAttackAnimation.AddFrame(2, 1, Vector<int>());
-               dekarAttackAnimation.AddFrame(2, 2, Vector<int>(-2, 0));
-               dekarAttackAnimation.AddFrames(2, 2, Vector<int>(0, 0), 3);
-               dekar.SetAttackAnimation(&dekarAttackAnimation);
-               ComplexAnimation dekarSpellAnimation(&dekarSprite, framerate);
-               dekarSpellAnimation.AddFrames(2, 0, Vector<int>(), 6);
-               dekarSpellAnimation.AddFrames(2, 1, Vector<int>(), 2);
-               dekarSpellAnimation.AddFrames(2, 2, Vector<int>(), 3);
-               dekar.SetSpellAnimation(&dekarSpellAnimation);
-               SDL_Surface *dekarMeleeImg(IMG_Load("test-data/melee-dekar.png"));
-               Sprite dekarMeleeSprite(dekarMeleeImg, 96, 96);
-               SimpleAnimation dekarMeleeAnimation(&dekarMeleeSprite, 2 * framerate, 4);
-               dekar.SetMeleeAnimation(&dekarMeleeAnimation);
-
-               battle::Resources battleRes;
-
-               SDL_Surface *swapCursorImg(IMG_Load("test-data/swap-cursor.png"));
-               Sprite swapCursorSprite(swapCursorImg, 32, 32);
-               battleRes.swapCursor = &swapCursorSprite;
-               SDL_Surface *attackIconsImg(IMG_Load("test-data/attack-type-icons.png"));
-               Sprite attackIconsSprite(attackIconsImg, 32, 32);
-               battleRes.attackIcons = &attackIconsSprite;
-               SDL_Surface *attackChoiceIconsImg(IMG_Load("test-data/attack-choice-icons.png"));
-               Sprite attackChoiceIconsSprite(attackChoiceIconsImg, 16, 16);
-               battleRes.attackChoiceIcons = &attackChoiceIconsSprite;
-               SDL_Surface *moveIconsImg(IMG_Load("test-data/move-icons.png"));
-               Sprite moveIconsSprite(moveIconsImg, 32, 32);
-               battleRes.moveIcons = &moveIconsSprite;
-
-               SDL_Surface *titleFrameImg(IMG_Load("test-data/title-frame.png"));
-               Frame titleFrame(titleFrameImg, 16, 16);
-               battleRes.titleFrame = &titleFrame;
-
-               SDL_Surface *largeFontImg(IMG_Load("test-data/large-font.png"));
-               Sprite largeFontSprite(largeFontImg, 16, 32);
-               Font largeFont(&largeFontSprite, 0, -2);
-               battleRes.titleFont = &largeFont;
-
-               ComplexAnimation numberAnimationPrototype(0, framerate);
-               numberAnimationPrototype.AddFrame(0, 0);
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -26));
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -42));
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -48));
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -42));
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -26));
-               numberAnimationPrototype.AddFrame(0, 0);
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -12));
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -20));
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -24));
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -20));
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -12));
-               numberAnimationPrototype.AddFrame(0, 0);
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -6));
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -10));
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -12));
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -10));
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -6));
-               numberAnimationPrototype.AddFrames(0, 0, Vector<int>(), 14);
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -36));
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -32));
-               numberAnimationPrototype.AddFrame(0, 0, Vector<int>(0, -18));
-               battleRes.numberAnimationPrototype = &numberAnimationPrototype;
-
-               SDL_Surface *bigNumbersImg(IMG_Load("test-data/big-numbers.png"));
-               Sprite bigNumbersSprite(bigNumbersImg, 16, 32);
-               battleRes.bigNumberSprite = &bigNumbersSprite;
-               SDL_Surface *bigGreenNumbersImg(IMG_Load("test-data/big-green-numbers.png"));
-               Sprite bigGreenNumbersSprite(bigGreenNumbersImg, 16, 32);
-               battleRes.greenNumberSprite = &bigGreenNumbersSprite;
-
-               SDL_Surface *heroTagImg(IMG_Load("test-data/hero-tag-sprites.png"));
-               Sprite heroTagSprite(heroTagImg, 32, 16);
-               battleRes.heroTagLabels = &heroTagSprite;
-               battleRes.levelLabelCol = 0;
-               battleRes.levelLabelRow = 0;
-               battleRes.healthLabelCol = 0;
-               battleRes.healthLabelRow = 1;
-               battleRes.manaLabelCol = 0;
-               battleRes.manaLabelRow = 2;
-               battleRes.moveLabelCol = 0;
-               battleRes.moveLabelRow = 3;
-               battleRes.ikariLabelCol = 0;
-               battleRes.ikariLabelRow = 4;
-
-               SDL_Surface *numbersImg(IMG_Load("test-data/numbers.png"));
-               Sprite numbersSprite(numbersImg, 16, 16);
-               Font heroTagFont(&numbersSprite, 0, -3);
-               battleRes.heroTagFont = &heroTagFont;
-               SDL_Surface *tagFramesImg(IMG_Load("test-data/tag-frames.png"));
-               Frame heroTagFrame(tagFramesImg, 16, 16, 1, 1, 0, 33);
-               battleRes.heroTagFrame = &heroTagFrame;
-               Frame activeHeroTagFrame(tagFramesImg, 16, 16);
-               battleRes.activeHeroTagFrame = &activeHeroTagFrame;
-               SDL_Surface *smallTagFrameImg(IMG_Load("test-data/small-tag-frame.png"));
-               Frame smallTagFrame(smallTagFrameImg, 8, 16);
-               battleRes.smallHeroTagFrame = &smallTagFrame;
-               Frame lastSmallTagFrame(smallTagFrameImg, 8, 16, 1, 1, 0, 33);
-               battleRes.lastSmallHeroTagFrame = &lastSmallTagFrame;
-               battleRes.heroesBgColor = SDL_MapRGB(screen.Screen()->format, 0x18, 0x28, 0x31);
-
-               SDL_Surface *gauges(IMG_Load("test-data/gauges.png"));
-               Gauge healthGauge(gauges, 0, 16, 0, 0, 16, 6, 1, 6);
-               battleRes.healthGauge = &healthGauge;
-               Gauge manaGauge(gauges, 0, 32, 0, 0, 16, 6, 1, 6);
-               battleRes.manaGauge = &manaGauge;
-               Gauge ikariGauge(gauges, 0, 48, 0, 0, 16, 6, 1, 6);
-               battleRes.ikariGauge = &ikariGauge;
-
-               SDL_Surface *selectFrameImg(IMG_Load("test-data/select-frame.png"));
-               Frame selectFrame(selectFrameImg, 16, 16);
-               battleRes.selectFrame = &selectFrame;
-
-               SDL_Surface *normalFontImg(IMG_Load("test-data/normal-font.png"));
-               Sprite normalFontSprite(normalFontImg, 16, 16);
-               Font normalFont(&normalFontSprite, 0, -2);
-               battleRes.normalFont = &normalFont;
-
-               SDL_Surface *disabledFontImg(IMG_Load("test-data/disabled-font.png"));
-               Sprite disabledFontSprite(disabledFontImg, 16, 16);
-               Font disabledFont(&disabledFontSprite, 0, -2);
-               battleRes.disabledFont = &disabledFont;
-
-               SDL_Surface *handCursorImg(IMG_Load("test-data/cursor-hand.png"));
-               Sprite handCursorSprite(handCursorImg, 32, 32);
-               battleRes.menuCursor = &handCursorSprite;
-
-               SDL_Surface *targetingIconsImg(IMG_Load("test-data/targeting-icons.png"));
-               Sprite weaponTargetCursor(targetingIconsImg, 32, 32);
-               Sprite magicTargetCursor(targetingIconsImg, 32, 32, 0, 32);
-               Sprite itemTargetCursor(targetingIconsImg, 32, 32, 0, 64);
-               battleRes.weaponTargetCursor = &weaponTargetCursor;
-               battleRes.magicTargetCursor = &magicTargetCursor;
-               battleRes.itemTargetCursor = &itemTargetCursor;
-
-               Spell resetSpell;
-               resetSpell.SetName("Reset");
-               maxim.AddSpell(&resetSpell);
-               Spell strongSpell;
-               strongSpell.SetName("Strong");
-               strongSpell.SetCost(3);
-               strongSpell.SetUsableInBattle();
-               strongSpell.GetTargetingMode().TargetMultipleAllies();
-               maxim.AddSpell(&strongSpell);
-               selan.AddSpell(&strongSpell);
-               Spell strongerSpell;
-               strongerSpell.SetName("Stronger");
-               strongerSpell.SetCost(8);
-               strongerSpell.SetUsableInBattle();
-               strongerSpell.GetTargetingMode().TargetMultipleAllies();
-               maxim.AddSpell(&strongerSpell);
-               selan.AddSpell(&strongerSpell);
-               Spell championSpell;
-               championSpell.SetName("Champion");
-               championSpell.SetCost(16);
-               championSpell.SetUsableInBattle();
-               championSpell.GetTargetingMode().TargetMultipleAllies();
-               maxim.AddSpell(&championSpell);
-               selan.AddSpell(&championSpell);
-               Spell rallySpell;
-               rallySpell.SetName("Rally");
-               rallySpell.SetCost(10);
-               rallySpell.SetUsableInBattle();
-               rallySpell.GetTargetingMode().TargetMultipleAllies();
-               maxim.AddSpell(&rallySpell);
-               selan.AddSpell(&rallySpell);
-               Spell escapeSpell;
-               escapeSpell.SetName("Escape");
-               escapeSpell.SetCost(8);
-               selan.AddSpell(&escapeSpell);
-               Spell valorSpell;
-               valorSpell.SetName("Valor");
-               valorSpell.SetCost(30);
-               valorSpell.SetUsableInBattle();
-               valorSpell.GetTargetingMode().TargetMultipleAllies();
-               maxim.AddSpell(&valorSpell);
-               selan.AddSpell(&valorSpell);
-
-               battleRes.spellMenuHeadline = "Please choose a spell.";
-               battleRes.spellMenuPrototype = Menu<const Spell *>(&normalFont, &disabledFont, &handCursorSprite, 9, 6, 8, 0, 2, 32, 2, ':');
-
-               SDL_Surface *itemIcons(IMG_Load("test-data/item-icons.png"));
-               Sprite potionIcon(itemIcons, 16, 16);
-               Sprite ballIcon(itemIcons, 16, 16, 0, 16);
-               Sprite crankIcon(itemIcons, 16, 16, 0, 32);
-               Sprite spearIcon(itemIcons, 16, 16, 0, 48);
-               Sprite swordIcon(itemIcons, 16, 16, 0, 64);
-               Sprite axIcon(itemIcons, 16, 16, 0, 80);
-               Sprite rodIcon(itemIcons, 16, 16, 0, 96);
-               Sprite armorIcon(itemIcons, 16, 16, 0, 112);
-               Sprite shieldIcon(itemIcons, 16, 16, 0, 128);
-               Sprite helmetIcon(itemIcons, 16, 16, 0, 144);
-               Sprite ringIcon(itemIcons, 16, 16, 0, 160);
-               Sprite jewelIcon(itemIcons, 16, 16, 0, 176);
-
-               battleRes.weaponMenuIcon = &swordIcon;
-               battleRes.armorMenuIcon = &armorIcon;
-               battleRes.shieldMenuIcon = &shieldIcon;
-               battleRes.helmetMenuIcon = &helmetIcon;
-               battleRes.ringMenuIcon = &ringIcon;
-               battleRes.jewelMenuIcon = &jewelIcon;
+               PartyLayout monstersLayout(*intp.GetPartyLayout("monstersLayout"));
+               PartyLayout heroesLayout(*intp.GetPartyLayout("heroesLayout"));
+
+               Monster monster(*intp.GetMonster("lizard"));
+               Hero maxim(*intp.GetHero("maxim"));
+               Hero selan(*intp.GetHero("selan"));
+               Hero guy(*intp.GetHero("guy"));
+               Hero dekar(*intp.GetHero("dekar"));
+
+               battle::Resources *battleRes(intp.GetBattleResources("battleResources"));
+
+               maxim.AddSpell(intp.GetSpell("resetSpell"));
+               Spell *strongSpell(intp.GetSpell("strongSpell"));
+               maxim.AddSpell(strongSpell);
+               selan.AddSpell(strongSpell);
+               Spell *strongerSpell(intp.GetSpell("strongerSpell"));
+               maxim.AddSpell(strongerSpell);
+               selan.AddSpell(strongerSpell);
+               Spell *championSpell(intp.GetSpell("championSpell"));
+               maxim.AddSpell(championSpell);
+               selan.AddSpell(championSpell);
+               Spell *rallySpell(intp.GetSpell("rallySpell"));
+               maxim.AddSpell(rallySpell);
+               selan.AddSpell(rallySpell);
+               selan.AddSpell(intp.GetSpell("escapeSpell"));
+               Spell *valorSpell(intp.GetSpell("valorSpell"));
+               maxim.AddSpell(valorSpell);
+               selan.AddSpell(valorSpell);
 
                Inventory inventory;
-               Item antidote;
-               antidote.SetName("Antidote");
-               antidote.SetMenuIcon(&potionIcon);
-               antidote.SetUsableInBattle();
-               antidote.GetTargetingMode().TargetSingleAlly();
-               inventory.Add(&antidote, 9);
-               Item magicJar;
-               magicJar.SetName("Magic jar");
-               magicJar.SetMenuIcon(&potionIcon);
-               magicJar.SetUsableInBattle();
-               magicJar.GetTargetingMode().TargetSingleAlly();
-               inventory.Add(&magicJar, 4);
-               Item hiPotion;
-               hiPotion.SetName("Hi-Potion");
-               hiPotion.SetMenuIcon(&potionIcon);
-               hiPotion.SetUsableInBattle();
-               hiPotion.GetTargetingMode().TargetSingleAlly();
-               inventory.Add(&hiPotion, 4);
-               Item powerPotion;
-               powerPotion.SetName("Power potion");
-               powerPotion.SetMenuIcon(&potionIcon);
-               inventory.Add(&powerPotion, 4);
-               Item escape;
-               escape.SetName("Escape");
-               inventory.Add(&escape, 2);
-               Item sleepBall;
-               sleepBall.SetName("Sleep ball");
-               sleepBall.SetMenuIcon(&ballIcon);
-               sleepBall.SetUsableInBattle();
-               sleepBall.GetTargetingMode().TargetSingleEnemy();
-               inventory.Add(&sleepBall, 1);
-               Item multiBall;
-               multiBall.SetName("Multi-ball!");
-               multiBall.SetMenuIcon(&ballIcon);
-               multiBall.SetUsableInBattle();
-               multiBall.GetTargetingMode().TargetMultipleEnemies();
-               inventory.Add(&multiBall, 1);
-               Item figgoru;
-               figgoru.SetName("Figgoru");
-               figgoru.SetMenuIcon(&crankIcon);
-               figgoru.GetTargetingMode().TargetAllEnemies();
-               inventory.Add(&figgoru, 1);
-               battleRes.inventory = &inventory;
-
-               battleRes.itemMenuHeadline = "Please choose an item.";
-               battleRes.itemMenuPrototype = Menu<const common::Item *>(&normalFont, &disabledFont, &handCursorSprite, 15, 6, 8, 16, 1, 32, 2, ':');
-
-               SDL_Surface *swordAttackImg(IMG_Load("test-data/attack-sword.png"));
-               Sprite swordAttackSprite(swordAttackImg, 96, 96);
-               SimpleAnimation swordAttackAnimation(&swordAttackSprite, 2 * framerate, 4);
-
-               Item zircoSword;
-               zircoSword.SetName("Zirco sword");
-               zircoSword.SetMenuIcon(&swordIcon);
-               zircoSword.GetTargetingMode().TargetSingleEnemy();
-               Ikari firestorm;
-               firestorm.SetName("Firestorm");
-               firestorm.SetCost(224);
-               firestorm.GetTargetingMode().TargetAllEnemies();
-               firestorm.SetPhysical();
-               zircoSword.SetIkari(&firestorm);
-               zircoSword.SetAttackAnimation(&swordAttackAnimation);
-               maxim.SetWeapon(&zircoSword);
-               Item zirconArmor;
-               zirconArmor.SetName("Zircon armor");
-               zirconArmor.SetMenuIcon(&armorIcon);
-               Ikari magicCure;
-               magicCure.SetName("Magic cure");
-               magicCure.SetCost(128);
-               magicCure.GetTargetingMode().TargetSingleAlly();
-               magicCure.SetMagical();
-               zirconArmor.SetIkari(&magicCure);
-               maxim.SetArmor(&zirconArmor);
-               Item holyShield;
-               holyShield.SetName("Holy shield");
-               holyShield.SetMenuIcon(&shieldIcon);
-               Ikari lightGuard;
-               lightGuard.SetName("Light guard");
-               lightGuard.SetCost(128);
-               lightGuard.GetTargetingMode().TargetAllAllies(); // actually only targets self
-               lightGuard.SetMagical();
-               holyShield.SetIkari(&lightGuard);
-               maxim.SetShield(&holyShield);
-               Item legendHelm;
-               legendHelm.SetName("Legend helm");
-               legendHelm.SetMenuIcon(&helmetIcon);
-               Ikari boomerang;
-               boomerang.SetName("Boomerang");
-               boomerang.SetCost(164);
-               boomerang.GetTargetingMode().TargetAllAllies(); // actually only targets self
-               boomerang.SetMagical();
-               legendHelm.SetIkari(&boomerang);
-               maxim.SetHelmet(&legendHelm);
-               Item sProRing;
-               sProRing.SetName("S-pro ring");
-               sProRing.SetMenuIcon(&ringIcon);
-               Ikari courage;
-               courage.SetName("Courage");
-               courage.SetCost(64);
-               courage.GetTargetingMode().TargetMultipleAllies();
-               courage.SetMagical();
-               sProRing.SetIkari(&courage);
-               maxim.SetRing(&sProRing);
-               Item evilJewel;
-               evilJewel.SetName("Evil jewel");
-               evilJewel.SetMenuIcon(&jewelIcon);
-               Ikari gloomy;
-               gloomy.SetName("Gloomy");
-               gloomy.SetCost(164);
-               gloomy.GetTargetingMode().TargetAllEnemies();
-               gloomy.SetMagical();
-               evilJewel.SetIkari(&gloomy);
-               maxim.SetJewel(&evilJewel);
-
-               Item zircoWhip;
-               zircoWhip.SetName("Zirco whip");
-               zircoWhip.SetMenuIcon(&rodIcon);
-               zircoWhip.GetTargetingMode().TargetSingleEnemy();
-               Ikari thundershriek;
-               thundershriek.SetName("Thundershriek");
-               thundershriek.SetCost(224);
-               thundershriek.GetTargetingMode().TargetAllEnemies();
-               thundershriek.SetPhysical();
-               zircoWhip.SetIkari(&thundershriek);
-//             selan.SetWeapon(&zircoWhip);
-               Item zirconPlate;
-               zirconPlate.SetName("Zircon plate");
-               zirconPlate.SetMenuIcon(&armorIcon);
-               Ikari suddenCure;
-               suddenCure.SetName("Sudden cure");
-               suddenCure.SetCost(96);
-               suddenCure.GetTargetingMode().TargetAllAllies();
-               suddenCure.SetMagical();
-               zirconPlate.SetIkari(&suddenCure);
-               selan.SetArmor(&zirconPlate);
-               Item zircoGloves;
-               zircoGloves.SetName("Zirco gloves");
-               zircoGloves.SetMenuIcon(&shieldIcon);
-               Ikari forcefield;
-               forcefield.SetName("Forcefield");
-               forcefield.SetCost(64);
-               forcefield.GetTargetingMode().TargetAllAllies();
-               forcefield.SetMagical();
-               zircoGloves.SetIkari(&forcefield);
-               selan.SetShield(&zircoGloves);
-               Item holyCap;
-               holyCap.SetName("Holy cap");
-               holyCap.SetMenuIcon(&helmetIcon);
-               Ikari vulnerable;
-               vulnerable.SetName("Vulnerable");
-               vulnerable.SetCost(196);
-               vulnerable.GetTargetingMode().TargetAllEnemies();
-               vulnerable.SetPhysical();
-               holyCap.SetIkari(&vulnerable);
-               selan.SetHelmet(&holyCap);
-               Item ghostRing;
-               ghostRing.SetName("Ghost ring");
-               ghostRing.SetMenuIcon(&ringIcon);
-               Ikari destroy;
-               destroy.SetName("Destroy");
-               destroy.SetCost(128);
-               destroy.GetTargetingMode().TargetMultipleEnemies();
-               destroy.SetMagical();
-               ghostRing.SetIkari(&destroy);
-               selan.SetRing(&ghostRing);
-               Item eagleRock;
-               eagleRock.SetName("Eagle rock");
-               eagleRock.SetMenuIcon(&jewelIcon);
-               Ikari dive;
-               dive.SetName("Dive");
-               dive.SetCost(128);
-               dive.GetTargetingMode().TargetSingleEnemy();
-               dive.SetPhysical();
-               eagleRock.SetIkari(&dive);
-               selan.SetJewel(&eagleRock);
-
-               Item zircoAx;
-               zircoAx.SetName("Zirco ax");
-               zircoAx.SetMenuIcon(&axIcon);
-               zircoAx.GetTargetingMode().TargetSingleEnemy();
-               Ikari torrent;
-               torrent.SetName("Torrent");
-               torrent.SetCost(224);
-               torrent.GetTargetingMode().TargetAllEnemies();
-               torrent.SetPhysical();
-               zircoAx.SetIkari(&torrent);
-//             guy.SetWeapon(&zircoAx);
-               guy.SetArmor(&zirconArmor);
-               Item megaShield;
-               megaShield.SetName("Mega shield");
-               megaShield.SetMenuIcon(&shieldIcon);
-               Ikari ironBarrier;
-               ironBarrier.SetName("Iron barrier");
-               ironBarrier.SetCost(255);
-               ironBarrier.GetTargetingMode().TargetAllAllies(); // actually only targets self
-               ironBarrier.SetMagical();
-               megaShield.SetIkari(&ironBarrier);
-               guy.SetShield(&megaShield);
-               Item zircoHelmet;
-               zircoHelmet.SetName("Zirco helmet");
-               zircoHelmet.SetMenuIcon(&helmetIcon);
-               Ikari slow;
-               slow.SetName("Slow");
-               slow.SetCost(196);
-               slow.GetTargetingMode().TargetAllEnemies();
-               slow.SetPhysical();
-               zircoHelmet.SetIkari(&slow);
-               guy.SetHelmet(&zircoHelmet);
-               Item powerRing;
-               powerRing.SetName("Power ring");
-               powerRing.SetMenuIcon(&ringIcon);
-               Ikari trick;
-               trick.SetName("Trick");
-               trick.SetCost(32);
-               trick.GetTargetingMode().TargetAllEnemies();
-               trick.SetMagical();
-               zircoHelmet.SetIkari(&trick);
-               guy.SetRing(&powerRing);
-               guy.SetJewel(&evilJewel);
+               inventory.Add(intp.GetItem("antidoteItem"), 9);
+               inventory.Add(intp.GetItem("magicJarItem"), 4);
+               inventory.Add(intp.GetItem("hiPotionItem"), 4);
+               inventory.Add(intp.GetItem("powerPotionItem"), 4);
+               inventory.Add(intp.GetItem("escapeItem"), 2);
+               inventory.Add(intp.GetItem("sleepBallItem"), 1);
+               battleRes->inventory = &inventory;
+
+               maxim.SetWeapon(intp.GetItem("zircoSwordItem"));
+               maxim.SetArmor(intp.GetItem("zirconArmorItem"));
+               maxim.SetShield(intp.GetItem("holyShieldItem"));
+               maxim.SetHelmet(intp.GetItem("legendHelmItem"));
+               maxim.SetRing(intp.GetItem("sProRingItem"));
+               maxim.SetJewel(intp.GetItem("evilJewelItem"));
+
+//             selan.SetWeapon(intp.GetItem("zircoWhipItem"));
+               selan.SetArmor(intp.GetItem("zirconPlateItem"));
+               selan.SetShield(intp.GetItem("zircoGlovesItem"));
+               selan.SetHelmet(intp.GetItem("holyCapItem"));
+               selan.SetRing(intp.GetItem("ghostRingItem"));
+               selan.SetJewel(intp.GetItem("eagleRockItem"));
+
+//             guy.SetWeapon(intp.GetItem("zircoAxItem"));
+               guy.SetArmor(intp.GetItem("zirconArmorItem"));
+               guy.SetShield(intp.GetItem("megaShieldItem"));
+               guy.SetHelmet(intp.GetItem("zircoHelmetItem"));
+               guy.SetRing(intp.GetItem("powerRingItem"));
+               guy.SetJewel(intp.GetItem("evilJewelItem"));
 
                // NOTE: this is actually Artea equipment
-               Item lizardBlow;
-               lizardBlow.SetName("Lizard blow");
-               lizardBlow.SetMenuIcon(&swordIcon);
-               lizardBlow.GetTargetingMode().TargetSingleEnemy();
-               Ikari dragonRush;
-               dragonRush.SetName("Dragon rush");
-               dragonRush.SetCost(164);
-               dragonRush.GetTargetingMode().TargetSingleEnemy();
-               dragonRush.SetPhysical();
-               lizardBlow.SetIkari(&dragonRush);
-//             dekar.SetWeapon(&lizardBlow);
-               Item holyRobe;
-               holyRobe.SetName("Holy robe");
-               holyRobe.SetMenuIcon(&armorIcon);
-               Ikari crisisCure;
-               crisisCure.SetName("Crisis cure");
-               crisisCure.SetCost(164);
-               crisisCure.GetTargetingMode().TargetAllAllies();
-               crisisCure.SetMagical();
-               holyRobe.SetIkari(&crisisCure);
-               dekar.SetArmor(&holyRobe);
-               dekar.SetShield(&zircoGloves);
-               dekar.SetHelmet(&holyCap);
-               Item rocketRing;
-               rocketRing.SetName("Rocket ring");
-               rocketRing.SetMenuIcon(&ringIcon);
-               Ikari fake;
-               fake.SetName("Fake");
-               fake.SetCost(32);
-               fake.GetTargetingMode().TargetSingleAlly();
-               fake.SetMagical();
-               rocketRing.SetIkari(&fake);
-               dekar.SetRing(&rocketRing);
-               Item krakenRock;
-               krakenRock.SetName("Kraken rock");
-               krakenRock.SetMenuIcon(&jewelIcon);
-               Ikari tenLegger;
-               tenLegger.SetName("Ten-legger");
-               tenLegger.SetCost(164);
-               tenLegger.GetTargetingMode().TargetAllEnemies();
-               tenLegger.SetPhysical();
-               rocketRing.SetIkari(&tenLegger);
-               dekar.SetJewel(&krakenRock);
-
-               battleRes.ikariMenuHeadline = "Please choose equipment.";
-               battleRes.noEquipmentText = "No equip";
-               battleRes.ikariMenuPrototype = Menu<const Item *>(&normalFont, &disabledFont, &handCursorSprite, 12, 6, normalFont.CharHeight() / 2, normalFont.CharWidth(), 1, normalFont.CharWidth() * 2, 0, ':', 12, normalFont.CharWidth());
-
-               battleRes.escapeText = "Escapes.";
-
-               BattleState *battleState(new BattleState(bg, monstersLayout, heroesLayout, &battleRes));
+//             dekar.SetWeapon(intp.GetItem("lizardBlowItem"));
+               dekar.SetArmor(intp.GetItem("holyRobeItem"));
+               dekar.SetShield(intp.GetItem("zircoGlovesItem"));
+               dekar.SetHelmet(intp.GetItem("holyCapItem"));
+               dekar.SetRing(intp.GetItem("rocketRingItem"));
+               dekar.SetJewel(intp.GetItem("krakenRockItem"));
+
+               BattleState *battleState(new BattleState(bg, monstersLayout, heroesLayout, battleRes));
                battleState->AddMonster(monster);
                battleState->AddMonster(monster);
                battleState->AddMonster(monster);
@@ -724,6 +184,9 @@ int main(int argc, char **argv) {
                app.Run();
 
                return 0;
+       } catch (Parser::Error &e) {
+               cerr << "parsing exception in file " << e.File() << " on line " << e.Line() << ": " << e.what() << endl;
+               return 1;
        } catch (exception &e) {
                cerr << "exception in main(): " << e.what() << endl;
                return 1;
diff --git a/test-data/ikaris.l2h b/test-data/ikaris.l2h
new file mode 100644 (file)
index 0000000..f55cc91
--- /dev/null
@@ -0,0 +1,20 @@
+Ikari boomerangIkari
+Ikari courageIkari
+Ikari crisisCureIkari
+Ikari destroyIkari
+Ikari diveIkari
+Ikari dragonRushIkari
+Ikari fakeIkari
+Ikari firestormIkari
+Ikari forcefieldIkari
+Ikari gloomyIkari
+Ikari ironBarrierIkari
+Ikari lightGuardIkari
+Ikari magicCureIkari
+Ikari slowIkari
+Ikari suddenCureIkari
+Ikari tenLeggerIkari
+Ikari thundershriekIkari
+Ikari torrentIkari
+Ikari trickIkari
+Ikari vulnerableIkari
diff --git a/test-data/ikaris.l2s b/test-data/ikaris.l2s
new file mode 100644 (file)
index 0000000..e76414d
--- /dev/null
@@ -0,0 +1,180 @@
+export Ikari boomerangIkari {
+       name: "Boomerang",
+       cost: 164,
+       targets: TargetingMode {
+               ally: true,
+               all: true
+       },
+       magical: true
+}
+export Ikari courageIkari {
+       name: "Courage",
+       cost: 64,
+       targets: TargetingMode {
+               ally: true,
+               multiple: true
+       },
+       magical: true
+}
+export Ikari crisisCureIkari {
+       name: "Crisis cure",
+       cost: 164,
+       targets: TargetingMode {
+               ally: true,
+               all: true
+       },
+       magical: true
+}
+export Ikari destroyIkari {
+       name: "Destroy",
+       cost: 128,
+       targets: TargetingMode {
+               enemy: true,
+               multiple: true
+       },
+       magical: true
+}
+export Ikari diveIkari {
+       name: "Dive",
+       cost: 128,
+       targets: TargetingMode {
+               enemy: true,
+               single: true
+       },
+       physical: true
+}
+export Ikari dragonRushIkari {
+       name: "Dragon rush",
+       cost: 164,
+       targets: TargetingMode {
+               enemy: true,
+               single: true
+       },
+       physical: true
+}
+export Ikari fakeIkari {
+       name: "Fake",
+       cost: 32,
+       targets: TargetingMode {
+               ally: true,
+               single: true
+       },
+       magical: true
+}
+export Ikari firestormIkari {
+       name: "Firestorm",
+       cost: 224,
+       targets: TargetingMode {
+               enemy: true,
+               all: true
+       },
+       physical: true
+}
+export Ikari forcefieldIkari {
+       name: "Forcefield",
+       cost: 64,
+       targets: TargetingMode {
+               ally: true,
+               all: true
+       },
+       magical: true
+}
+export Ikari gloomyIkari {
+       name: "Gloomy",
+       cost: 164,
+       targets: TargetingMode {
+               enemy: true,
+               all: true
+       },
+       magical: true
+}
+export Ikari ironBarrierIkari {
+       name: "Iron barrier",
+       cost: 255,
+       targets: TargetingMode {
+               ally: true,
+               all: true
+       },
+       magical: true
+}
+export Ikari lightGuardIkari {
+       name: "Light guard",
+       cost: 128,
+       targets: TargetingMode {
+               ally: true,
+               all: true
+       },
+       magical: true
+}
+export Ikari magicCureIkari {
+       name: "Magic cure",
+       cost: 128,
+       targets: TargetingMode {
+               ally: true,
+               single: true
+       },
+       magical: true
+}
+export Ikari slowIkari {
+       name: "Slow",
+       cost: 196,
+       targets: TargetingMode {
+               enemy: true,
+               all: true
+       },
+       physical: true
+}
+export Ikari suddenCureIkari {
+       name: "Sudden cure",
+       cost: 96,
+       targets: TargetingMode {
+               ally: true,
+               all: true
+       },
+       magical: true
+}
+export Ikari tenLeggerIkari {
+       name: "Ten-legger",
+       cost: 164,
+       targets: TargetingMode {
+               enemy: true,
+               all: true
+       },
+       physical: true
+}
+export Ikari thundershriekIkari {
+       name: "Thundershriek",
+       cost: 224,
+       targets: TargetingMode {
+               enemy: true,
+               all: true
+       },
+       physical: true
+}
+export Ikari torrentIkari {
+       name: "Torrent",
+       cost: 224,
+       targets: TargetingMode {
+               enemy: true,
+               all: true
+       },
+       physical: true
+}
+export Ikari trickIkari {
+       name: "Trick",
+       cost: 32,
+       targets: TargetingMode {
+               enemy: true,
+               all: true
+       },
+       magical: true
+}
+export Ikari vulnerableIkari {
+       name: "Vulnerable",
+       cost: 196,
+       targets: TargetingMode {
+               enemy: true,
+               all: true
+       },
+       physical: true
+}
diff --git a/test-data/items.l2h b/test-data/items.l2h
new file mode 100644 (file)
index 0000000..c2461ec
--- /dev/null
@@ -0,0 +1,39 @@
+Item antidoteItem
+Sprite armorIcon
+Sprite axIcon
+Sprite ballIcon
+Sprite crankIcon
+Item eagleRockItem
+Item escapeItem
+Item evilJewelItem
+Item ghostRingItem
+Sprite helmetIcon
+Item hiPotionItem
+Item holyCapItem
+Item holyRobeItem
+Item holyShieldItem
+Sprite jewelIcon
+Item krakenRockItem
+Item legendHelmItem
+Item lizardBlowItem
+Item magicJarItem
+Item megaShieldItem
+Sprite potionIcon
+Item powerPotionItem
+Item powerRingItem
+Sprite ringIcon
+Item rocketRingItem
+Sprite rodIcon
+Item sProRingItem
+Sprite shieldIcon
+Item sleepBallItem
+Sprite spearIcon
+SimpleAnimation swordAttackAnimation
+Sprite swordIcon
+Item zircoAxItem
+Item zircoGlovesItem
+Item zircoHelmetItem
+Item zircoSwordItem
+Item zircoWhipItem
+Item zirconArmorItem
+Item zirconPlateItem
diff --git a/test-data/items.l2s b/test-data/items.l2s
new file mode 100644 (file)
index 0000000..76dd5aa
--- /dev/null
@@ -0,0 +1,236 @@
+include "ikaris.l2h"
+
+export Sprite potionIcon {
+       image: :"item-icons.png",
+       size: <16,16>
+}
+export Sprite ballIcon {
+       image: :"item-icons.png",
+       size: <16,16>,
+       offset: <0,16>
+}
+export Sprite crankIcon {
+       image: :"item-icons.png",
+       size: <16,16>,
+       offset: <0,32>
+}
+export Sprite spearIcon {
+       image: :"item-icons.png",
+       size: <16,16>,
+       offset: <0,48>
+}
+export Sprite swordIcon {
+       image: :"item-icons.png",
+       size: <16,16>,
+       offset: <0,64>
+}
+export Sprite axIcon {
+       image: :"item-icons.png",
+       size: <16,16>,
+       offset: <0,80>
+}
+export Sprite rodIcon {
+       image: :"item-icons.png",
+       size: <16,16>,
+       offset: <0,96>
+}
+export Sprite armorIcon {
+       image: :"item-icons.png",
+       size: <16,16>,
+       offset: <0,112>
+}
+export Sprite shieldIcon {
+       image: :"item-icons.png",
+       size: <16,16>,
+       offset: <0,128>
+}
+export Sprite helmetIcon {
+       image: :"item-icons.png",
+       size: <16,16>,
+       offset: <0,144>
+}
+export Sprite ringIcon {
+       image: :"item-icons.png",
+       size: <16,16>,
+       offset: <0,160>
+}
+export Sprite jewelIcon {
+       image: :"item-icons.png",
+       size: <16,16>,
+       offset: <0,176>
+}
+
+export SimpleAnimation swordAttackAnimation {
+       sprite: Sprite {
+               image: :"attack-sword.png",
+               size: <96,96>
+       },
+       frametime: twoFramesTime,
+       repeat: false,
+       framecount: 4
+}
+
+export Item antidoteItem {
+       name: "Antidote",
+       menuicon: potionIcon,
+       battle: true,
+       targets: TargetingMode {
+               ally: true,
+               single: true
+       }
+}
+export Item eagleRockItem {
+       name: "Eagle rock",
+       menuicon: jewelIcon,
+       ikari: diveIkari
+}
+export Item escapeItem {
+       name: "Escape",
+       battle: false
+}
+export Item evilJewelItem {
+       name: "Evil jewel",
+       menuicon: jewelIcon,
+       ikari: gloomyIkari
+}
+export Item ghostRingItem {
+       name: "Ghost ring",
+       menuicon: ringIcon,
+       ikari: destroyIkari
+}
+export Item hiPotionItem {
+       name: "Hi-Potion",
+       menuicon: potionIcon,
+       battle: true,
+       targets: TargetingMode {
+               ally: true,
+               single: true
+       }
+}
+export Item holyCapItem {
+       name: "Holy cap",
+       menuicon: helmetIcon,
+       ikari: vulnerableIkari
+}
+export Item holyRobeItem {
+       name: "Holy robe",
+       menuicon: armorIcon,
+       ikari: crisisCureIkari
+}
+export Item holyShieldItem {
+       name: "Holy shield",
+       menuicon: shieldIcon,
+       ikari: lightGuardIkari
+}
+export Item krakenRockItem {
+       name: "Kraken rock",
+       menuicon: jewelIcon,
+       ikari: tenLeggerIkari
+}
+export Item legendHelmItem {
+       name: "Legend helm",
+       menuicon: helmetIcon,
+       ikari: boomerangIkari
+}
+export Item lizardBlowItem {
+       name: "Lizard blow",
+       menuicon: swordIcon,
+       ikari: dragonRushIkari,
+       targets: TargetingMode {
+               enemy: true,
+               single: true
+       }
+}
+export Item magicJarItem {
+       name: "Magic jar",
+       menuicon: potionIcon,
+       battle: true,
+       targets: TargetingMode {
+               ally: true,
+               single: true
+       }
+}
+export Item megaShieldItem {
+       name: "Mega shield",
+       menuicon: shieldIcon,
+       ikari: ironBarrierIkari
+}
+export Item powerPotionItem {
+       name: "Power potion",
+       menuicon: potionIcon,
+       battle: false
+}
+export Item powerRingItem {
+       name: "Power ring",
+       menuicon: ringIcon,
+       ikari: trickIkari
+}
+export Item rocketRingItem {
+       name: "Rocket ring",
+       menuicon: ringIcon,
+       ikari: fakeIkari
+}
+export Item sleepBallItem {
+       name: "Sleep ball",
+       menuicon: ballIcon,
+       battle: true,
+       targets: TargetingMode {
+               enemy: true,
+               single: true
+       }
+}
+export Item sProRingItem {
+       name: "S-pro ring",
+       menuicon: ringIcon,
+       ikari: courageIkari
+}
+export Item zircoAxItem {
+       name: "Zirco ax",
+       menuicon: axIcon,
+       ikari: torrentIkari,
+       targets: TargetingMode {
+               enemy: true,
+               single: true
+       }
+}
+export Item zircoGlovesItem {
+       name: "Zirco gloves",
+       menuicon: shieldIcon,
+       ikari: forcefieldIkari
+}
+export Item zircoHelmetItem {
+       name: "Zirco helmet",
+       menuicon: helmetIcon,
+       ikari: slowIkari
+}
+export Item zirconArmorItem {
+       name: "Zircon armor",
+       menuicon: armorIcon,
+       battle: false,
+       ikari: magicCureIkari
+}
+export Item zirconPlateItem {
+       name: "Zircon plate",
+       menuicon: armorIcon,
+       ikari: suddenCureIkari
+}
+export Item zircoSwordItem {
+       name: "Zirco sword",
+       menuicon: swordIcon,
+       battle: false,
+       targets: TargetingMode {
+               enemy: true,
+               single: true
+       },
+       ikari: firestormIkari,
+       attackanimation: swordAttackAnimation
+}
+export Item zircoWhipItem {
+       name: "Zirco whip",
+       menuicon: rodIcon,
+       targets: TargetingMode {
+               enemy: true,
+               single: true
+       },
+       ikari: thundershriekIkari
+}
diff --git a/test-data/spells.l2h b/test-data/spells.l2h
new file mode 100644 (file)
index 0000000..2da4057
--- /dev/null
@@ -0,0 +1,7 @@
+Spell championSpell
+Spell escapeSpell
+Spell rallySpell
+Spell resetSpell
+Spell strongSpell
+Spell strongerSpell
+Spell valorSpell
diff --git a/test-data/spells.l2s b/test-data/spells.l2s
new file mode 100644 (file)
index 0000000..ec3f1c8
--- /dev/null
@@ -0,0 +1,61 @@
+export Spell championSpell {
+       name: "Champion",
+       cost: 16,
+       battle: true,
+       targets: TargetingMode {
+               ally: true,
+               multiple: true
+       }
+}
+
+export Spell escapeSpell {
+       name: "Escape",
+       cost: 8,
+       battle: false
+}
+
+export Spell rallySpell {
+       name: "Rally",
+       cost: 10,
+       battle: true,
+       targets: TargetingMode {
+               ally: true,
+               multiple: true
+       }
+}
+
+export Spell resetSpell {
+       name: "Reset",
+       cost: 0,
+       battle: false
+}
+
+export Spell strongSpell {
+       name: "Strong",
+       cost: 3,
+       battle: true,
+       targets: TargetingMode {
+               ally: true,
+               multiple: true
+       }
+}
+
+export Spell strongerSpell {
+       name: "Stronger",
+       cost: 8,
+       battle: true,
+       targets: TargetingMode {
+               ally: true,
+               multiple: true
+       }
+}
+
+export Spell valorSpell {
+       name: "Valor",
+       cost: 30,
+       battle: true,
+       targets: TargetingMode {
+               ally: true,
+               multiple: true
+       }
+}
diff --git a/test-data/test.l2s b/test-data/test.l2s
new file mode 100644 (file)
index 0000000..48817f4
--- /dev/null
@@ -0,0 +1,590 @@
+include "ikaris.l2h"
+include "items.l2h"
+include "spells.l2h"
+
+Number frameTime 33
+Number twoFramesTime 66
+Number fourFramesTime 132
+Number fiveFramesTime 165 // darn, i really need to implement expressions
+
+export PartyLayout monstersLayout {
+       positions: [
+               < 88, 88>,
+               <128, 88>,
+               <168, 88>,
+               <208, 88>
+       ]
+}
+export PartyLayout heroesLayout {
+       positions: [
+               < 48,136>,
+               <128,136>,
+               < 80,152>,
+               <160,152>
+       ]
+}
+
+Sprite lizardSprite {
+       image: :"monster.png",
+       size: <64,64>
+}
+
+export Monster lizard {
+       name: "Lizard",
+       sprite: lizardSprite,
+       level: 1,
+       maxHealth: 8,
+       health: 8,
+       stats: Stats {
+               atp: 14,
+               dfp:  6,
+               str:  6,
+               agl:  6,
+               int:  6,
+               gut:  6,
+               mgr:  6
+       },
+       attackAnimation: ComplexAnimation {
+               sprite: lizardSprite,
+               frametime: fourFramesTime,
+               repeat: false,
+               frames: [
+                       { 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> }
+               ]
+       },
+       meleeAnimation: SimpleAnimation {
+               sprite: Sprite {
+                       image: :"attack-monster.png",
+                       size: <96,64>
+               },
+               frametime: frameTime,
+               framecount: 14
+       }
+}
+
+Sprite maximSprite {
+       image: :"maxim.png",
+       size: <64,64>
+}
+export Hero maxim {
+       name: "Maxim",
+       level: 1,
+       sprite: maximSprite,
+       maxHealth: 33,
+       health: 33,
+       maxMana: 20,
+       mana: 20,
+       ip: 0,
+       stats: Stats {
+               atp:  28,
+               dfp:  22,
+               str:  28,
+               agl:  17,
+               int:  14,
+               gut: 100,
+               mgr:  10
+       },
+       attackAnimation: ComplexAnimation {
+               sprite: maximSprite,
+               frametime: frameTime,
+               repeat: false,
+               frames: [
+                       { column: 1, row: 0, disposition: < 0, 0> },
+                       { column: 1, row: 0, disposition: < 0, 0> },
+                       { column: 1, row: 0, disposition: < 0, 0> },
+                       { column: 1, row: 0, disposition: < 0, 0> },
+                       { column: 1, row: 0, disposition: < 0, 0> },
+                       { column: 1, row: 0, disposition: < 0, 0> },
+                       { column: 1, row: 0, disposition: < 0, 0> },
+                       { column: 1, row: 0, disposition: < 4,-1> },
+                       { column: 1, row: 0, disposition: < 4,-1> },
+                       { column: 2, row: 0, disposition: < 4,-2> },
+                       { column: 2, row: 0, disposition: < 4,-2> },
+                       { column: 2, row: 0, disposition: < 6,-2> },
+                       { column: 2, row: 0, disposition: < 6,-2> },
+                       { column: 2, row: 1, disposition: < 6,-1> },
+                       { column: 2, row: 1, disposition: < 3,-1> },
+                       { column: 2, row: 1, disposition: < 3,-1> },
+                       { column: 2, row: 1, disposition: < 0, 0> },
+                       { column: 2, row: 2, disposition: < 0, 0> },
+                       { column: 2, row: 2, disposition: < 0, 0> },
+                       { column: 2, row: 2, disposition: < 2, 0> },
+                       { column: 1, row: 0, disposition: < 0, 0> },
+                       { column: 1, row: 0, disposition: < 0, 0> },
+                       { column: 1, row: 0, disposition: < 0, 0> },
+                       { column: 1, row: 0, disposition: < 0, 0> },
+                       { column: 1, row: 0, disposition: < 0, 0> },
+                       { column: 1, row: 0, disposition: < 0, 0> },
+                       { column: 1, row: 0, disposition: < 0, 0> }
+               ]
+       },
+       spellAnimation: ComplexAnimation {
+               sprite: maximSprite,
+               frametime: fiveFramesTime,
+               repeat: false,
+               frames: [
+                       { column: 3, row: 0, disposition: < 0, 0> },
+                       { column: 3, row: 0, disposition: < 0, 0> },
+                       { column: 3, row: 1, disposition: < 0, 0> }
+               ]
+       },
+       meleeAnimation: SimpleAnimation {
+               sprite: Sprite {
+                       image: :"melee-maxim.png",
+                       size: <96,96>
+               },
+               frametime: twoFramesTime,
+               framecount: 4
+       }
+}
+
+Sprite selanSprite {
+       image: :"selan.png",
+       size: <64,64>
+}
+export Hero selan {
+       name: "Selan",
+       level: 1,
+       sprite: selanSprite,
+       maxHealth: 28,
+       health: 28,
+       maxMana: 23,
+       mana: 23,
+       ip: 0,
+       stats: Stats {
+               atp: 23,
+               dfp: 21,
+               str: 23,
+               agl: 19,
+               int: 22,
+               gut: 80,
+               mgr: 13
+       },
+       attackAnimation: ComplexAnimation {
+               sprite: selanSprite,
+               frametime: frameTime,
+               repeat: false,
+               frames: [
+                       { column: 1, row: 0, disposition: < 4, 0> },
+                       { column: 1, row: 0, disposition: < 4, 0> },
+                       { column: 1, row: 0, disposition: < 8, 2> },
+                       { column: 2, row: 0, disposition: <10, 4> },
+                       { column: 2, row: 0, disposition: <14, 4> },
+                       { column: 2, row: 0, disposition: <12, 2> },
+                       { column: 2, row: 0, disposition: <12, 2> },
+                       { column: 2, row: 0, disposition: <12, 2> },
+                       { column: 2, row: 1, disposition: <14, 2> },
+                       { column: 2, row: 1, disposition: <14, 2> },
+                       { column: 2, row: 1, disposition: < 2, 0> },
+                       { column: 2, row: 2, disposition: <-2,-4> },
+                       { column: 2, row: 2, disposition: <-8,-8> },
+                       { column: 2, row: 2, disposition: < 0, 0> }
+               ]
+       },
+       spellAnimation: ComplexAnimation {
+               sprite: selanSprite,
+               frametime: frameTime,
+               repeat: false,
+               frames: [
+                       { column: 3, row: 0, disposition: < 0, 0> },
+                       { column: 3, row: 0, disposition: < 0, 0> },
+                       { column: 3, row: 0, disposition: < 0, 0> },
+                       { column: 3, row: 1, disposition: < 0, 0> },
+                       { column: 3, row: 1, disposition: < 0, 0> },
+                       { column: 3, row: 2, disposition: < 0, 0> },
+                       { column: 3, row: 2, disposition: < 0, 0> },
+                       { column: 3, row: 2, disposition: < 0, 0> },
+                       { column: 3, row: 3, disposition: < 0, 0> },
+                       { column: 3, row: 3, disposition: < 0, 0> }
+               ]
+       },
+       meleeAnimation: SimpleAnimation {
+               sprite: Sprite {
+                       image: :"melee-selan.png",
+                       size: <96,96>
+               },
+               frametime: twoFramesTime,
+               framecount: 4
+       }
+}
+
+Sprite guySprite {
+       image: :"guy.png",
+       size: <64,64>
+}
+export Hero guy {
+       name: "Guy",
+       level: 1,
+       sprite: guySprite,
+       maxHealth: 38,
+       health: 38,
+       maxMana: 0,
+       mana: 0,
+       ip: 0,
+       stats: Stats {
+               atp: 38,
+               dfp: 25,
+               str: 38,
+               agl: 13,
+               int:  8,
+               gut: 90,
+               mgr:  8
+       },
+       attackAnimation: ComplexAnimation {
+               sprite: guySprite,
+               frametime: frameTime,
+               repeat: false,
+               frames: [
+                       { column: 1, row: 0, disposition: <-4, 0> },
+                       { column: 1, row: 0, disposition: <-4, 0> },
+                       { column: 1, row: 0, disposition: <-8, 0> },
+                       { column: 1, row: 0, disposition: <-8, 0> },
+                       { column: 2, row: 0, disposition: <-8, 0> },
+                       { column: 2, row: 0, disposition: <-8, 0> },
+                       { column: 2, row: 0, disposition: <-4, 0> },
+                       { column: 2, row: 0, disposition: < 0, 0> },
+                       { column: 2, row: 0, disposition: < 0, 0> },
+                       { column: 2, row: 1, disposition: < 0, 0> },
+                       { column: 2, row: 1, disposition: < 4, 0> },
+                       { column: 2, row: 1, disposition: <10, 0> },
+                       { column: 2, row: 2, disposition: <10, 0> },
+                       { column: 2, row: 2, disposition: < 0, 0> }
+               ]
+       },
+       meleeAnimation: SimpleAnimation {
+               sprite: Sprite {
+                       image: :"melee-guy.png",
+                       size: <96,96>
+               },
+               frametime: fourFramesTime,
+               framecount: 4
+       }
+}
+
+Sprite dekarSprite {
+       image: :"dekar.png",
+       size: <64,64>
+}
+export Hero dekar {
+       name: "Dekar",
+       level: 1,
+       sprite: dekarSprite,
+       maxHealth: 38,
+       health: 38,
+       maxMana: 0,
+       mana: 0,
+       ip: 0,
+       stats: Stats {
+               atp:  46,
+               dfp:  29,
+               str:  46,
+               agl:  13,
+               int:   7,
+               gut: 100,
+               mgr:   5
+       },
+       attackAnimation: ComplexAnimation {
+               sprite: dekarSprite,
+               frametime: frameTime,
+               repeat: false,
+               frames: [
+                       { column: 1, row: 0, disposition: < 4, 0> },
+                       { column: 1, row: 0, disposition: < 8, 2> },
+                       { column: 2, row: 0, disposition: <12, 4> },
+                       { column: 2, row: 0, disposition: <16, 4> },
+                       { column: 2, row: 0, disposition: <10, 2> },
+                       { column: 2, row: 0, disposition: <10, 2> },
+                       { column: 2, row: 0, disposition: <10, 2> },
+                       { column: 2, row: 0, disposition: <10, 2> },
+                       { column: 2, row: 1, disposition: < 6, 2> },
+                       { column: 2, row: 1, disposition: < 0, 0> },
+                       { column: 2, row: 2, disposition: <-2, 0> },
+                       { column: 2, row: 2, disposition: < 0, 0> },
+                       { column: 2, row: 2, disposition: < 0, 0> },
+                       { column: 2, row: 2, disposition: < 0, 0> }
+               ]
+       },
+       spellAnimation: ComplexAnimation {
+               sprite: dekarSprite,
+               frametime: twoFramesTime,
+               repeat: false,
+               frames: [
+                       { column: 3, row: 0, disposition: < 0, 0> },
+                       { column: 3, row: 0, disposition: < 0, 0> },
+                       { column: 3, row: 0, disposition: < 0, 0> },
+                       { column: 3, row: 0, disposition: < 0, 0> },
+                       { column: 3, row: 0, disposition: < 0, 0> },
+                       { column: 3, row: 0, disposition: < 0, 0> },
+                       { column: 3, row: 1, disposition: < 0, 0> },
+                       { column: 3, row: 1, disposition: < 0, 0> },
+                       { column: 3, row: 2, disposition: < 0, 0> },
+                       { column: 3, row: 2, disposition: < 0, 0> },
+                       { column: 3, row: 2, disposition: < 0, 0> }
+               ]
+       },
+       meleeAnimation: SimpleAnimation {
+               sprite: Sprite {
+                       image: :"melee-dekar.png",
+                       size: <96,96>
+               },
+               frametime: twoFramesTime,
+               framecount: 4
+       }
+}
+
+Sprite handCursor {
+       image: :"cursor-hand.png",
+       size: <32,32>
+}
+
+Font normalFont {
+       sprite: Sprite {
+               image: :"normal-font.png",
+               size: <16,16>
+       },
+       rowoffset: -2
+}
+
+Font disabledFont {
+       sprite: Sprite {
+               image: :"disabled-font.png",
+               size: <16,16>
+       },
+       rowoffset: -2
+}
+
+export BattleResources battleResources {
+       swapCursor: Sprite {
+               image: :"swap-cursor.png",
+               size: <32,32>
+       },
+       attackIcons: Sprite {
+               image: :"attack-type-icons.png",
+               size: <32,32>
+       },
+       attackChoiceIcons: Sprite {
+               image: :"attack-choice-icons.png",
+               size: <16,16>
+       },
+       moveIcons: Sprite {
+               image: :"move-icons.png",
+               size: <32,32>
+       },
+       
+       titleFrame: Frame {
+               image: :"title-frame.png",
+               border: <16,16>
+       },
+       titleFont: Font {
+               sprite: Sprite {
+                       image: :"large-font.png",
+                       size: <16,32>
+               },
+               rowoffset: -2
+       },
+       
+       numberAnimationPrototype: ComplexAnimation {
+               frametime: frameTime,
+               repeat: false,
+               frames: [
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,-26> },
+                       { column: 0, row: 0, disposition: <  0,-42> },
+                       { column: 0, row: 0, disposition: <  0,-48> },
+                       { column: 0, row: 0, disposition: <  0,-42> },
+                       { column: 0, row: 0, disposition: <  0,-26> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,-12> },
+                       { column: 0, row: 0, disposition: <  0,-20> },
+                       { column: 0, row: 0, disposition: <  0,-24> },
+                       { column: 0, row: 0, disposition: <  0,-20> },
+                       { column: 0, row: 0, disposition: <  0,-12> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0, -6> },
+                       { column: 0, row: 0, disposition: <  0,-10> },
+                       { column: 0, row: 0, disposition: <  0,-12> },
+                       { column: 0, row: 0, disposition: <  0,-10> },
+                       { column: 0, row: 0, disposition: <  0, -6> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,  0> },
+                       { column: 0, row: 0, disposition: <  0,-36> },
+                       { column: 0, row: 0, disposition: <  0,-32> },
+                       { column: 0, row: 0, disposition: <  0,-18> }
+               ]
+       },
+       bigNumberSprite: Sprite {
+               image: :"big-numbers.png",
+               size: <16,32>
+       },
+       greenNumberSprite: Sprite {
+               image: :"big-green-numbers.png",
+               size: <16,32>
+       },
+       
+       heroTagLabels: Sprite {
+               image: :"hero-tag-sprites.png",
+               size: <32,16>
+       },
+       levelLabelCol: 0,
+       levelLabelRow: 0,
+       healthLabelCol: 0,
+       healthLabelRow: 1,
+       manaLabelCol: 0,
+       manaLabelRow: 2,
+       moveLabelCol: 0,
+       moveLabelRow: 3,
+       ikariLabelCol: 0,
+       ikariLabelRow: 4,
+       heroTagFont: Font {
+               sprite: Sprite {
+                       image: :"numbers.png",
+                       size: <16,16>
+               },
+               rowoffset: -3
+       },
+       
+       activeHeroTagFrame: Frame {
+               image: :"tag-frames.png",
+               border: <16,16>
+       },
+       heroTagFrame: Frame {
+               image: :"tag-frames.png",
+               border: <16,16>,
+               offset: < 0,33>
+       },
+       
+       smallHeroTagFrame: Frame {
+               image: :"small-tag-frame.png",
+               border: <8,16>
+       },
+       lastSmallHeroTagFrame: Frame {
+               image: :"small-tag-frame.png",
+               border: <8,16>,
+               offset: <0,33>
+       },
+       heroesBgColor: (24, 40, 49),
+       
+       healthGauge: Gauge {
+               image: :"gauges.png",
+               full:  <0,16>,
+               empty: <0, 0>,
+               height: 16,
+               start:   6,
+               repeat:  1,
+               end:     6
+       },
+       manaGauge: Gauge {
+               image: :"gauges.png",
+               full:  <0,32>,
+               empty: <0, 0>,
+               height: 16,
+               start:   6,
+               repeat:  1,
+               end:     6
+       },
+       ikariGauge: Gauge {
+               image: :"gauges.png",
+               full:  <0,48>,
+               empty: <0, 0>,
+               height: 16,
+               start:   6,
+               repeat:  1,
+               end:     6
+       },
+       
+       selectFrame: Frame {
+               image: :"select-frame.png",
+               border: <16,16>
+       },
+       normalFont: normalFont,
+       disabledFont: disabledFont,
+       menuCursor: Sprite {
+               image: :"cursor-hand.png",
+               size: <32,32>
+       },
+       
+       weaponTargetCursor: Sprite {
+               image: :"targeting-icons.png",
+               size: <32,32>
+       },
+       magicTargetCursor: Sprite {
+               image: :"targeting-icons.png",
+               size: <32,32>,
+               offset: <0,32>
+       },
+       itemTargetCursor: Sprite {
+               image: :"targeting-icons.png",
+               size: <32,32>,
+               offset: <0,64>
+       },
+       
+       weaponMenuIcon: swordIcon,
+       armorMenuIcon: armorIcon,
+       shieldMenuIcon: shieldIcon,
+       helmetMenuIcon: helmetIcon,
+       ringMenuIcon: ringIcon,
+       jewelMenuIcon: jewelIcon,
+       
+       spellMenuHeadline: "Please choose a spell.",
+       spellMenuProperties: MenuProperties {
+               font: normalFont,
+               disabledFont: disabledFont,
+               cursor: handCursor,
+               charsPerEntry: 9,
+               rows: 6,
+               rowGap: 8,
+               iconSpace: 0,
+               cols: 2,
+               colGap: 32,
+               charsPerNumber: 2,
+               delimiter: ":"
+       },
+       
+       itemMenuHeadline: "Please choose an item.",
+       itemMenuProperties: MenuProperties {
+               font: normalFont,
+               disabledFont: disabledFont,
+               cursor: handCursor,
+               charsPerEntry: 15,
+               rows: 6,
+               rowGap: 8,
+               iconSpace: 16,
+               cols: 1,
+               colGap: 32,
+               charsPerNumber: 2,
+               delimiter: ":"
+       },
+       
+       ikariMenuHeadline: "Please choose equipment.",
+       ikariMenuProperties: MenuProperties {
+               font: normalFont,
+               disabledFont: disabledFont,
+               cursor: handCursor,
+               charsPerEntry: 12,
+               rows: 6,
+               rowGap: 8,
+               iconSpace: 16,
+               cols: 1,
+               colGap: 32,
+               charsPerAdditionalText: 12,
+               additionalTextGap: 16
+       },
+       noEquipmentText: "No equip",
+       
+       escapeText: "Escapes."
+}
\ No newline at end of file