Browse Source

Keep the repo up to date at least once in a while...

Beoran 7 years ago
parent
commit
7ca340cd34

+ 1 - 0
data/script/main.rb

@@ -499,6 +499,7 @@ def eruta_on_bump(t1, t2, h1, h2, kind = nil)
   if kind == 1 # Begin of collision
     # puts "Begin collision!"
   elsif kind == 2 # Collision active
+    puts "Colliding! #{t1}<->#{t2} (#{h1}<->#{h2})"
     thing1 = Thing[t1]
     thing2 = Thing[t2]
     if thing1 && thing2

+ 125 - 73
doc/PLAN.md

@@ -1,9 +1,18 @@
 
-= ERUTA PLAN =
+= ERUTA BLESSED SAVIOR PLAN =
  
 == General ==
 
-Eruta will be a 2D or 2.5D game. 
+Eruta Blesed savior is a refocus of several previous idead. Basically it is 
+based on Eruta's game engine, but the plot will bet hat of Eruta Kei's Quest.
+I decided this because the EKQ plot I have in mind is to e more interesting than 
+the one I imagined for Eruta. Furthermore the dungeon crawling 3d engine I was
+working on for EKQ had several problems, the most important one that it leads to
+what I feel is boring gameplay.
+
+ 
+
+Eruta will be a 2D game with a classic tile map and a top-down view. 
   Rationale: I like the old-school 2D look. 3D games are more resource hungry 
   and time-consuming to implement, so that's for the next project.
 
@@ -19,78 +28,83 @@ Programmed in C with mruby scripting:
   And no, don't even dare mention C++, it complicates matters without being 
   actually that helpful. And too bad that Objective C is not well supported on 
   Windows and messy on Linux.
-
+  
+Low level and libraries in C, game script and configuration in mruby.
+  Change Eruta a to keep the scripting language for scripting, not for 
+  implementing the engine itself anymore.    
+  One problem with the Eruta engine is that the functionality isn't divided well.
+  Arguably, too much ow level stuff is implemented in Ruby, which leads to
+  the interface between C and mruby being huge (too many links). 
+  For example, there is a 2D scene engine in C that is only used to implement 
+  he GUI in mruby. 
+  Furthermore since C compiles pretty fast for me now, there isn't any 
+  compelling reason anymore to use scripting for "rapid" development. I have 
+  started to C's.appreciatethat fact that the C compiler does warn me of some silly 
+  errors that I would only detect on run time on the mruby side. 
+  The EBS engine will push the GUI, the game control and all of the physics back 
+  to C, and limit the interface with mruby to what is needed for scripting.
+  
 Using Allegro 5.x.
   Rationale: SDL may be the de facto standard for open source games, but it's
   very poor in functionality. So I ended up having to write a lot of
   functionality myself. And SDL 2.0 is not as mature as allegro 5 is. I thought
-  Allegro 5 was slower, but when  using plain C, and taking care to
+  Allegro 5 was slower, but when using plain C, and taking care to
   load the images after opening the screen, Allegro 5 was actually plenty
   fast enough. Allegro has so much nice functionality built in, it would be a
-  waste not to use it when using plain C.
-
-Virtual screen resolution exactly 640x480. Color depth minimum 16bpp or better. 
+  waste not to use it when using plain C. Any speed problems with Allegro 5
+  can be dealt with by sing the primitives add on with it's indexed pixel
+  buffer rendering.
+  
+Virtual screen resolution exactly 640x480. Color depth minimum 16bpp or better.
   Also implement scaling to other resolutions trough OpenGL or 2xSai, etc 
   to achieve resolution dependence later.
   Rationale: Old-style feeling and limit art requrements.
 
-Hand drawn art with colors limited to a 12/15 bits palette. 
+Hand drawn style art with colors limited to a 12/15 bits palette. 
   I like bright colors that have a simple rgb expression and generated
-  a palette that fits the limitations for drawing the hand drawn art.
-  Rationale: Old-style feeling.
-
-A role playing game with much onus on the story.
-  Rationale: I want Eruta to be to RPG's what Katawa Shoujo is to Visual Novels.
-
+  a palette that fits the limitations for drawing the hand drawn art. 
+  Art based on SVG, or even generated from 3D models is OK as well as log as 
+  it acieved the desired look.  Rationale: Old-style feeling.
+
+A role playing game with an interesting story.
+  Rationale: That's what RPG's are about.
+
+A role playing game with much onus on exploration.
+  Rationale: Some famous games, like, say FFXII forget this, but one of the main 
+  attractions of RPG's is exploration. For EBS I want to achieve this 
+  by procedurally generating a huge world from the world map. It will not be 
+  random, all players will play the same world, but the procedural generation 
+  will be used to ease game creation. The generated world will be hand-edited 
+  where necessary. It will be split up in to tile maps, one map for every tile 
+  on the world map. 
+  
 Multi player game:
   Rationale: I want to be able to play together with someone on the same PC. 
   To begin just 2 players together is OK. Perhaps implement 
   split screen. 2 to 4 players are probably OK to divide the screen between. The 
   maximum should be the amount of 12 characters, but it will be hard to fit all 
   those on one screen. So perhaps allow networked play as well.
+
+Fewer artificial limits:
+  One thing that plagues RPG's are arbitrary limits such as the amount of 
+  characters in your active party. I'd like to have less of such limits if 
+  possible. All playable characters are part of your party and they are all 
+  AI controlled  except the player's selected character. The player can set up 
+  various strategies and influence the actions of the other in party PC's. 
+  No unreasonable time limits, but also, no limits on the amounts of NPC's or 
+  foes.
   
   
-Full Action RPG or Chrono Trigger style menu-driven tactical or more FF12 style RPG?
-  I'm still unsure actually! Oh my! FF6 shows that a menu driven game can be
-  played by several players. Each player simply controls one character during 
-  battles. And since all characters will always be visible and on the screen,
-  the idea would be to allow controlling them even outside of battle. 
-  They can talk and get small side-dialogs. They can search and help solve puzzles.
-  Only the player who controls the main character will be able to make plot
-  determining decisions, and control of secondary characters will be disabled in 
-  camp.
-  Even if I decide that the battles should be menu based, the players will be able 
-  to use various forceful actions outside of combat as well.
-  Ponderings about the 3 different options: action, tactical and hybrid. Strategic
-  is not under consideration since I don't like strategic RPG's too much due to 
-  too slow and too battle-oriented.
-  Action RPG: 
-  + Easy to get in to. 
-  + Action based battles normally are the most "fun" at least to me.
-  - May be difficult to control 12 or 6 characters all at once.
-  - Action based battles lack depth in RPG's since you'd need the controls 
-    of a fighting game to make the moves complex. That would work, but make the game
-    less accessible again. I don't like to have to button mash or memorize button
-    combinations.
-  +- AI implementation is somewhat more complex. 
-  Tactical RPG: (Classic menu driven)
-  + If done correctly, and with some enhancements, can be deeper than action RPG.
-  + For some players, and for me too the menu-based approach is more relaxed.
-    The game becomes more like a tactical puzzle.
-  - If done 100% classic style, the battles can devolve into boring bouts 
-    of doing nothing but selecting "fight/fight/fight..." (or X X X, FFXIII :p) 
-    over and over. More creativity and challenge even in normal battles is needed.
-  - Could be harder to get in to.
-  Hybrid (FF12/MMORPG style):
-  + Can be as deep as tactical. 
-  + Easier to implement networked and multiplayer.
-  - Isn't as relaxing to play as pure menu driven.
- -+ Game engine is somewhat more complex than tactical and probably even more 
-    than action based alone.
-=> Conclusion: I want this game to be more "SOM"-like with a "PS4"-like 
-atmosphere, so Action RPG it is. Let's see how deep that rabbit hole goes. :)
-My next RPG project will be a more "PERS4" inspired 3D menu driven RPG.
-
+Hybrid Action RPG
+  A live action RPG with a cooldown system like that of SOM. You can attack 
+  wildly but calm attacks are much more effective. Also find a way to allow for 
+  combos  and weapon techniques. The non player contolled PC's will fight much 
+  like in FF12, in a programmabe/commandable way. Also allow for a "RPG" 
+  mode later on where the attack button works like in SD3, that leads to an 
+  almost MMORPG style of control. Items and spells may be menu based but 
+  prevent spell spamming like in SOM by also adding a cooldown.  
+  The players will be able  to use various forceful actions outside of combat 
+  as well.
   
   
 == Needed Functionality ==
@@ -119,18 +133,30 @@ to accelerate with OpenGL later, and fits the resolution of 640x480 well.
 
 Tile map distinguishes between tile layers wich dictate rendering and world
 layers which dictate gameplay (ie, on which levels the sprites can “be”).
-Tile map has 4 drawing layers of tiles. Testing shows 4 is optimal for speed.
+Tile map has at least 4 drawing layers of tiles. Testing shows 4 is optimal 
+for speed. However, to allow for "inside" locations to be mapped easily to 
+the tile map, there may be 4 additional inside layers that are shown if the 
+global game state is "inside". Probably it's easiest to have an arbitrary
+amount of layers, each with a inside/outside flag. Additionally, layer offsets 
+could be used to "compress" inside locations and elevation and prevent 
+tile layers that are mostly empty.   
+
 Layers are usually grouped in pairs for the “world layers”. Layer 1 is the first
 floor, layer 2 is anything that stands on that floor and any fringes/overlaps
 from one kind of floor to another. Layers 1 and 2 are always under all sprites.
 Layer 3 and 4 are normally the “floor above” the player, however, the player may
-walk there in case of bridges, overlays, or parallax.
-
-Every layer may have a background image that is drawn before the tiles of the layer 
-are drawn. the tiles can also be made invisible if the background layer by itself is 
-enough visually. In that case the tiles influence the game play and physics only. 
-Although only one bitmap background can be displayed per layer, it's possible for 
-a layer to change the background depending on what's needed. 
+walk there in case of bridges, overlays, or parallax. Again, to enhance this, 
+the role of the layers could be arranged by layer flags.
+
+Every layer may have a background image that is drawn before the tiles of the 
+layer  are drawn. The tiles of a layer can also be made invisible if the 
+background layer by itself is enough visually. In that case the tiles influence 
+the game play and physics only. Perhaps like tmx it would actually be simpler 
+to allow arbitrary background images with and without an offset and with or 
+without parallax.
+
+Although only one bitmap background can be displayed per layer, it's possible 
+for a layer to change the background depending on what's needed. 
 (night/day/scripting/etc). The background bitmap width and 
 height must have a size that is an integer multiple of 32 pixels.
 
@@ -141,12 +167,14 @@ special "scroll lock" tiles.
 
 View culling will be possible, in that, if enabled, only the direct area around the 
 player sprite will be shown, and anything outside a rectangle set by special 
-culling tiles will not be drawn. This to allow inside areas to be stored more 
-compactly.
+culling tiles will not be drawn. This too allows inside areas to be stored more 
+compactly. Although te solution of "inside layers" may be actually simpler
+and preferable.
 
-Every map layer uses exactly one tile set based on a single tile image. Tile images 
-are are files in .png format with a width and height that are integer multiples of
-32, and that contain many individual tiles.
+Every map layer uses exactly one tile set based on a single tile image. Tile 
+images  are are files in .png format with a width and height that are integer 
+multiples of 32, and that contain many individual tiles. The current term 
+would be "atlas textures".
 
 Keeping with convention over configuration, the tile sets are read in, split up
 in 32x32 tiles and interpreted according to the following rules:
@@ -155,7 +183,8 @@ Every map describes the names of the tile it uses. The file name of the
 set is of the form set_name_xxx000.png where xxx000 is a number with at least 4 
 digits that signifies the start index of the set. Tiles in a set are numbered 
 from left to right and from top to bottom, starting from 0. So, for instance  
-the second tile at  the top left of a tile set will be numbered 1.
+the second tile at  the top left of a tile set will be numbered 1. Alternatively 
+the tmx tile offset is used.
 
 The idea of this is for maps broadly compatible with each other even if 
 they use different tile sets, and also to make it easy to swap tile sets if 
@@ -165,14 +194,21 @@ The map engine loads tiled .tmx map files. The properties of a tile, such as
 solidity or animation are loaded from the Tiled file. Tiled has animated tiles 
 since tiled 0.10 so support those as well.
 
+The tile engine also supports automatic tile blending and automatic shadows
+to lower the art requirements.
+
+Maybe rewrite to use indexed primitives for performance, although it does mean
+that atlases will need to be generated to allow automatic blends to work.
+
 
 Particle and effect engine
 --------------------------
 
-This module can display effects like rain, snow, blood, bursts. Explosions and spell 
-effects would be nice as well. Will use the camera for correct views. 
+This module can display effects like rain, snow, blood, bursts. Explosions 
+and spell effects would be nice as well. Will use the camera for correct views. 
 allow bitmaps as well as drawing for the particles. Need to allow bitmap
-animations for certain effects.  
+animations for certain effects. Probably best to use indexed primitives 
+for performance. 
 
 
 Camera module
@@ -187,7 +223,23 @@ Physics
 Finally got rid of Chipmunk. I was able to implement a useful system 
 that's simpler than chipmunk but that works well and doesn't require bazillions
 of static shapes for the tile map walls. Also need to implement battle related 
-area sensitivity.
+area sensitivity and combat mechanics in C. Trying to split this between
+mruby and C was a bit of a disaster.
+
+UI
+--
+Implement Zori gui in C. I considered an "immediate mode" gui, but that seems 
+to be hard to script. So go with a semi-deferred GUI for now.
+
+
+Game Play
+---------
+
+Based on the system I considered for WOE. Skill based, many different 
+proefficiencies. Level based on skills. Perhaps have a generic 
+"experience" jar as well? Then again games from Bethesda don't seem to need 
+general experience, and those are also all RPG.
+
 
 +---------+
 | Modules |

+ 1 - 0
doc/TODO.md

@@ -14,6 +14,7 @@ o Play
   1 implement player and foe data such as HP, MP; LP, and stats.
 
 o GUI:
+  1 Reimplement gui engine wholly in C, not split over C and Mruby.
   1 game HUD / current HP, MP and LP display 
   * All sorts of widgets for status screens, menus, etc.
   * Keyboard input.

+ 113 - 0
include/miao.h

@@ -0,0 +1,113 @@
+#ifndef MIAO_H_INCLUDED
+#define MIAO_H_INCLUDED
+
+/* MIAO Minimalistic Array Operations, inspired by kvec->h-> */
+
+#include <stdlib.h>
+
+#ifndef miao_realloc
+#define miao_realloc(PTR, SIZE) realloc((PTR), (SIZE))
+#endif
+
+#define miao_of_type(TYPE) { TYPE * a; TYPE * aux; size_t n; size_t m; }
+#define miao_t(TYPE)  struct miao_of_type(TYPE) 
+#define miao_struct(NAME, TYPE)  struct NAME miao_of_type(TYPE)
+#define miao_init(ARR) ((ARR)->n = (ARR)->m = 0, (ARR)->a = NULL, (ARR))
+#define miao_done(ARR) do { free((ARR)->a); miao_init((ARR)); } while (0)
+#define miao_unsafe_get_ptr(ARR, I) (((ARR)->a)+(I))
+#define miao_unsafe_get(ARR, I) (((ARR)->a)[(I)])
+#define miao_elsize(ARR) (sizeof  *((ARR)->a))
+#define miao_cap(ARR) ((ARR)->m)
+#define miao_size(ARR) ((ARR)->n)
+#define miao_size_for(ARR, AMOUNT) (miao_elsize(ARR) * (AMOUNT))
+#define miao_total_size(ARR) miao_size_for(ARR, miao_size(ARR))
+
+#define miao_out_of_bounds(ARR, I) ((size_t)(I) >= (ARR)->m)
+#define miao_get_ptr(ARR, I) (miao_out_of_bounds(ARR, I) ?  NULL: miao_unsafe_get_ptr(ARR, I))
+#define miao_get(ARR, I, DEFAULT) (miao_out_of_bounds(ARR, I) ?  DEFAULT : miao_unsafe_get(ARR, I))
+
+#define miao_resize(ARR, CAP)                                          \
+  (                                                                    \
+    (ARR)->m = (CAP),                                                  \
+    (ARR)->aux = miao_realloc((ARR)->a, miao_size_for(ARR, (ARR)->m)), \
+    (ARR)->aux ? ((ARR)->a = ARR->aux) : NULL                          \
+  )
+
+#define miao_grow(ARR, SIZE)                                           \
+  (SIZE >= miao_cap(ARR) ?  miao_resize(ARR, SIZE) : (ARR)->a )
+
+
+#define miao_unsafe_pop_ptr(ARR) ((ARR)->n--, ((ARR)->a + (ARR)->n))
+#define miao_unsafe_pop(ARR) ((ARR)->n--, (ARR)->a[(ARR)->n])
+
+#define miao_pop(ARR, DEFAULT) (((ARR)->n > 0) ? miao_unsafe_pop(ARR) : DEFAULT)
+#define miao_pop_ptr(ARR) (((ARR)->n > 0) ? miao_unsafe_pop_ptr(ARR) : NULL)
+
+#define miao_unsafe_push(ARR, VAL) ((ARR)->n++, (ARR)->a[(ARR)->n] = (VAL)) 
+
+#define miao_push(ARR, VAL)                                           \
+          (miao_grow(ARR, ((ARR)->n + 1)) ? miao_unsafe_push(ARR, VAL) : NULL)
+          
+#define miao_push_ptr(ARR)                                            \
+  (                                                                   \
+    (miao_grow(ARR, ((ARR)->n + 1))) ?                                \
+    ((ARR)->n++,  miao_get_ptr(ARR, (ARR)->n - 1))                    \
+    : NULL                                                            \
+  )
+
+
+#define miao_copy(DEST, SRC)                                          \
+  (                                                                   \
+    miao_grow(DEST, miao_size(SRC)) ?                                 \
+    ( (DEST)->n = miao_size(SRC),                                     \
+      memcpy((DEST)->a, (SRC)->a, miao_total_size(DEST))              \
+      , DEST                                                          \
+    ) : NULL                                                          \
+  )
+
+#define miao_qsort(ARR, COMPARE)                                      \
+    (                                                                 \
+      (ARR)->a ?                                                      \
+      ( qsort((ARR)->a, miao_size(ARR), miao_elsize(ARR), COMPARE)    \
+      , (ARR)->a )                                                    \
+      : NULL                                                          \
+    )
+
+#define miao_bsearch(ARR, COMPARE, KEY)                               \
+    (bsearch(KEY, (ARR)->a, miao_size(ARR), miao_elsize(ARR), COMPARE))
+
+
+#define miao_each_ptr(ARR, ACTION, DATA)                                      \
+  { size_t miao_i__ ;                                                         \
+    for (miao_i__ = 0; miao_i__ < miao_size(ARR) ; miao_i__ ++) {             \
+      if(ACTION(miao_i__, miao_unsafe_get_ptr(ARR, miao_i__), DATA)) break;   \
+    }                                                                         \
+  } while(0);
+
+
+#define miao_each(ARR, ACTION, DATA)                                          \
+  { size_t miao_i__ ;                                                         \
+    for (miao_i__ = 0; miao_i__ < miao_size(ARR) ; miao_i__ ++) {             \
+      if(ACTION(miao_i__, miao_unsafe_get(ARR, miao_i__), DATA)) break;       \
+    }                                                                         \
+  } while(0);
+
+
+#define miao_each_with_result(ARR, ACTION, RESULT, CHECK, DATA)               \
+  { size_t miao_i__ ;                                                         \
+    for (miao_i__ = 0; miao_i__ < miao_size(ARR) ; miao_i__ ++) {             \
+      (RESULT) = ACTION(miao_i__, miao_unsafe_get(ARR, miao_i__), DATA);      \
+      if (CHECK(RESULT, DATA)) break;                                         \
+    }                                                                         \
+  } while(0);
+
+#define miao_each_ptr_with_result(ARR, ACTION, RESULT, CHECK, DATA)           \
+  { size_t miao_i__ ;                                                         \
+    for (miao_i__ = 0; miao_i__ < miao_size(ARR) ; miao_i__ ++) {             \
+      (RESULT) = ACTION(miao_i__, miao_unsafe_get_ptr(ARR, miao_i__), DATA);  \
+      if (CHECK(RESULT, DATA)) break;                                         \
+    }                                                                         \
+  } while(0);
+    
+
+#endif

+ 151 - 6
include/zori.h

@@ -1,14 +1,159 @@
 #ifndef zori_H_INCLUDED
 #define zori_H_INCLUDED
 
-/* This file was generated with:
-'cfunctions -c -aoff -n -w zori_proto src/zori.c' */
-#ifndef CFH_ZORI_PROTO
-#define CFH_ZORI_PROTO
+#include "eruta.h"
+#include "rebox.h"
+#include "miao.h"
+
+/* Typedefs for possible later portability. */
+typedef ALLEGRO_COLOR zori_color ;
+typedef ALLEGRO_BITMAP zori_bitmap ;
+typedef ALLEGRO_FONT zori_font ;
+
+typedef ALLEGRO_EVENT zori_system_event;
+typedef ALLEGRO_EVENT_TYPE zori_event_type;
+typedef ALLEGRO_DISPLAY zori_display;
+
+typedef Point zori_point;
+typedef Rebox zori_rebox;
+typedef int zori_id;
+
+#define ZORI_ID_OK_P(ID)  ((ID) > -1)
+#define ZORI_ID_OK        ((zori_id)(0))
+#define ZORI_ID_ERROR     ((zori_id)(-1))
+#define ZORI_ID_ENOMEM    ((zori_id)(-2))
+#define ZORI_ID_EINVAL    ((zori_id)(-3))
+
+
+struct zori_widget;
+
+struct zori_event {
+  zori_system_event    sysev;
+  struct zori_widget * widget;
+  void               * data; 
+};
+
+
+struct zori_stylepart {
+  zori_color       color;
+  zori_bitmap    * image;
+  zori_font      * font;
+};
+
+struct zori_style {
+  struct zori_stylepart fore;
+  struct zori_stylepart back;
+  struct zori_stylepart text;
+};
+
+struct zori_widget;
+
+typedef int zori_handler_func(struct zori_event * event);
+
+struct zori_handler {
+  zori_event_type type;
+  zori_handler_func * handler;
+  void * data;
+};
+
+
+/* System event handlers. */
+struct zori_handlers miao_of_type(struct zori_handler);
+
+/*
+struct zori_handlers {
+  size_t size;  
+  struct zori_handler * handlers;
+};
+*/
+
+
+/* Mouse or keyboard/joystick cursor. */
+struct zori_cursor { 
+  zori_point           p; 
+  struct zori_widget * hover;
+  struct zori_widget * focus;
+  zori_bitmap        * bitmap;
+};
+
+
+/* Support multiple cursors...  */
+struct zori_cursors { 
+  struct zori_cursor mouse;
+  struct zori_cursor keyjoy;
+};
+
+
+/*
+on_enter
+on_enter(data = {})
+on_event(*args)
+on_event(*data)
+on_key_down(*args)
+on_leave(name=nil)
+on_mouse_axes(t, x, y, z, w, dx, dy, dz, dw)
+on_mouse_button_down(t, x, y, z, w, b)
+on_mouse_button_up(t, x, y, z, w, b)
+on_mouse_in(x, y, from)
+on_mouse_out(x, y, to)
+on_resize
+*/
+
+struct zori_widget {
+  /* ID of the widget, used in most external API's. */
+  zori_id id;
+  
+  /* Root level widget under which this widget is active. */
+  struct zori_widget  * root;
+  
+  /* Position and size of the widget. */
+  zori_rebox box;
+  /* Z ordering. */
+  int z;
+  
+  /* Style. */
+  struct zori_style    style;
+  
+  /* Handlers. */
+  struct zori_handlers handlers;
+  
+  /* Related widgets. */
+  struct zori_widget * parent;
+  struct zori_widget * child;
+  struct zori_widget * sibling;
+};
+
+
+/* An array of widget pointers. */
+struct zori_widget_array {
+  struct zori_widget * array;
+  size_t size;
+};
+
+/* Root level widget. */
+struct zori_root {  
+  /* A root is a widget. */ 
+  struct zori_widget widget;
+  /* It also manages the cursors*/
+  struct zori_cursors cursors;
+  /* It has an array of all widgets it manages. */
+  struct zori_widget_array * widgets;
+  /* It is linked to a particular display. */
+  zori_display * display;  
+};
+
+
+
+zori_id zori_set_background_color(zori_id id, zori_color color);
+zori_id zori_set_foreground_color(zori_id id, zori_color color);
+zori_id zori_new_root_widget(const struct zori_style * style);
+zori_id zori_new_frame_widget(zori_id parent);
+
+
+zori_id zori_register(zori_id id,  zori_event_type type, zori_handler_func handler, void * extra);
+
 
-/* From 'src/zori.c': */
 
-#endif /* CFH_ZORI_PROTO */
 
 
 #endif

+ 55 - 0
src/zori.c

@@ -1,12 +1,67 @@
 
 #include "zori.h"
+#include "miao.h"
 
 /*
 * Pardon the pun name, but Zori is the submodule that handles the user
 * interface and the menus. 
 */
 
+int zori_handler_compare(const void * v1, const void * v2) {
+  const struct zori_handler * h1 = v1;
+  const struct zori_handler * h2 = v2;
+  
+  if (h1->type < h2->type) return -1;
+  if (h1->type > h2->type) return 1;
+  return 0;  
+}
 
+struct zori_handler * zori_handlers_add(struct zori_handlers  * me, zori_event_type type, zori_handler_func * handler, void * data) {
+  struct zori_handler * result = NULL;
+  
+  result = miao_push_ptr(me);
+  if (result) {
+    result->type    = type;
+    result->handler = handler;
+    result->data    = data;
+  }
+  miao_qsort(me, zori_handler_compare);
+  return result;    
+}
+
+
+void zori_handlers_done(struct zori_handlers * me) {
+  miao_done(me);
+}
+
+void zori_handlers_init(struct zori_handlers * me) {  
+  miao_init(me);
+}
+
+
+struct zori_handler * zori_handlers_search(struct zori_handlers * me,  zori_event_type type) {    
+  struct zori_handler * result;
+  struct zori_handler key;
+  key.type    = type;
+  key.handler = NULL;
+  key.data    = NULL;
+  if (!me) return NULL;
+  result = miao_bsearch(me, zori_handler_compare, &key);
+  return result;   
+}
+
+
+int zori_handlers_handle(struct zori_handlers * me,  struct zori_event * event, struct zori_widget * widget) {    
+  struct zori_handler * handler = zori_handlers_search(me, event->sysev.type);
+  if (!handler) return 0;
+  return handler->handler(event);
+}
+
+struct zori_root zori_the_root = {0};
+
+int zori_init() {
+  return 0;
+}
 
 
 

+ 1 - 1
test/test_bxml.c

@@ -43,7 +43,7 @@ TEST_FUNC(bxml) {
 
 TEST_FUNC(bxml_parse) {
   Bxml * xml;  
-  xml = bxml_parse_filename("data/map/map_0001.tmx");
+  xml = bxmlparser_parse_filename("data/map/map_0001.tmx");
   TEST_NOTNULL(xml);
   bxml_free(xml);
   TEST_DONE();

+ 1 - 1
test/test_collide.c

@@ -2,7 +2,7 @@
 * This is a test for collide in $package$
 */
 #include "si_test.h"
-#include "collide.h"
+/* #include "collide.h" */
 
 
 TEST_FUNC(collide) {

+ 112 - 0
test/test_miao.c

@@ -0,0 +1,112 @@
+/**
+* This is a test for miao in $package$
+*/
+#include "si_test.h"
+#include "miao.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+struct foo {
+  char * s;
+};
+
+struct foo_array miao_of_type(struct foo);
+
+int foo_compare(const void * v1, const void * v2) 
+{ 
+  const struct foo * f1, * f2;
+  f1 = v1;
+  f2 = v2;
+  return strcmp(f1->s, f2->s);  
+}
+
+int foo_each_ptr(size_t i, const struct foo * f, FILE * extra) {
+  fprintf(extra, "%u => %s\n", i, f->s);
+  return 0;
+}
+
+int foo_each(size_t i, struct foo f, FILE * extra) {
+  fprintf(extra, "%u => %s\n", i, f.s);
+  return 0;
+}
+
+struct foo * foo_each_ptr_with_result(size_t i, struct foo * f, char * extra) {
+  printf("%u => %s\n", i, f->s);
+  return f;
+}
+
+int foo_check_ptr(const struct foo * f, char * extra) {
+  return (strcmp(f->s, extra) == 0);
+}  
+
+struct foo foo_each_with_result(size_t i, struct foo f, char * extra) {
+  printf("%u => %s\n", i, f.s);
+  return f;
+}
+
+int foo_check(const struct foo f, char * extra) {
+  return (strcmp(f.s, extra) == 0);
+}  
+
+
+TEST_FUNC(miao) {
+  struct foo f1, f2 , f3;
+  struct foo * e1, * e2, *e3;
+  struct foo_array a[1];
+  struct foo_array d[1];
+  
+  miao_init(a);
+  miao_init(d);
+  e1 = miao_push_ptr(a);
+  TEST_NOTNULL(e1);
+  e1->s = "world";
+  e1 = miao_push_ptr(a);
+  TEST_NOTNULL(e1);
+  e1->s = "hello";
+  miao_qsort(a, foo_compare);
+  TEST_INTEQ(2, miao_size(a));
+  TEST_PTREQ(e1, miao_get_ptr(a, 1));
+  TEST_PTREQ(e1, miao_unsafe_get_ptr(a, 1));
+  TEST_NULL(miao_get_ptr(a, 77));
+  TEST_ASSERT(miao_out_of_bounds(a, 2));
+  TEST_ASSERT(miao_cap(a) >= 2);
+  TEST_ASSERT(miao_size(a) == 2);
+  TEST_NOTNULL(miao_resize(a, 64));
+  TEST_ASSERT(miao_cap(a) >= 64);
+  TEST_ASSERT(miao_size(a) == 2);
+  e1 = miao_get_ptr(a, 1);
+  TEST_STREQ("world", e1->s);
+  TEST_PTREQ(e1, miao_unsafe_get_ptr(a, 1));
+  TEST_NULL(miao_get_ptr(a, 77));
+  TEST_ASSERT(miao_out_of_bounds(a, 77));
+  TEST_INTEQ(sizeof(struct foo), miao_elsize(a));
+  TEST_NOTNULL(miao_copy(d, a));
+  TEST_MEMEQ(a->a, miao_size(a) * miao_elsize(a), d->a);
+  miao_push_ptr(a)->s = "foo";
+  miao_push_ptr(a)->s = "bar";
+  TEST_NOTNULL(miao_qsort(a, foo_compare));
+  miao_each(a, foo_each    , stdout);
+  miao_each_ptr(a, foo_each_ptr, stdout);
+  miao_each_with_result(a, foo_each_with_result, f3, foo_check, "foo");
+  TEST_STREQ("foo", f3.s);
+  miao_each_ptr_with_result(a, foo_each_ptr_with_result, e3, foo_check_ptr, "foo");
+  TEST_STREQ("foo", e3->s);  
+  f1.s = "hello";
+  e3 = miao_bsearch(a, foo_compare, &f1);
+  TEST_STREQ(f1.s, e3->s);
+  e2 = miao_pop_ptr(a);
+  TEST_STREQ("world", e2->s);
+  miao_done(a);
+  TEST_ZERO(miao_size(a)); 
+  TEST_DONE();
+}  
+  
+int main(void) {
+  TEST_INIT();
+  TEST_RUN(miao);
+  TEST_REPORT();
+}
+
+
+

+ 1 - 1
test/test_pachi.c

@@ -2,7 +2,7 @@
 * This is a test for pachi in $package$
 */
 #include "si_test.h"
-#include "pachi.h"
+/* #include "pachi.h" */
 
 
 TEST_FUNC(pachi) {

+ 3 - 1
test/test_sprite.c

@@ -7,6 +7,7 @@
 
 
 TEST_FUNC(sprite) {
+#ifdef COMMENT
   SpriteList    * list;
   Sprite        * sprite;
   SpriteAction  * act;
@@ -69,7 +70,8 @@ TEST_FUNC(sprite) {
   }
   spritelist_free(list);
   sprite_free(sprite);
-  TEST_DONE();
+#endif
+  TEST_DONE();  
 }
 
 

+ 1 - 1
test/test_spritelayout.c

@@ -2,7 +2,7 @@
 * This is a test for spritelayout in $package$
 */
 #include "si_test.h"
-#include "spritelayout.h"
+/* #include "spritelayout.h" */
 
 
 TEST_FUNC(spritelayout) {

+ 1 - 0
test/test_spritelist.c

@@ -2,6 +2,7 @@
 * This is a test for spritelist in $package$
 */
 #include "si_test.h"
+#include "sprite.h"
 #include "spritelist.h"
 
 

+ 1 - 0
test/test_spritestate.c

@@ -2,6 +2,7 @@
 * This is a test for spritestate in $package$
 */
 #include "si_test.h"
+#include "sprite.h"
 #include "spritestate.h"
 
 

+ 1 - 0
test/test_tr_audio.c

@@ -2,6 +2,7 @@
 * This is a test for tr_audio in $package$
 */
 #include "si_test.h"
+#include <mruby.h>
 #include "tr_audio.h"
 
 

+ 1 - 0
test/test_tr_graph.c

@@ -2,6 +2,7 @@
 * This is a test for tr_graph in $package$
 */
 #include "si_test.h"
+#include <mruby.h>
 #include "tr_graph.h"
 
 

+ 1 - 0
test/test_tr_path.c

@@ -2,6 +2,7 @@
 * This is a test for tr_path in $package$
 */
 #include "si_test.h"
+#include <mruby.h>
 #include "tr_path.h"
 
 

+ 1 - 0
test/test_tr_sprite.c

@@ -2,6 +2,7 @@
 * This is a test for tr_sprite in $package$
 */
 #include "si_test.h"
+#include <mruby.h>
 #include "tr_sprite.h"
 
 

+ 1 - 0
test/test_tr_store.c

@@ -2,6 +2,7 @@
 * This is a test for tr_store in $package$
 */
 #include "si_test.h"
+#include <mruby.h>
 #include "tr_store.h"
 
 

+ 1 - 0
test/test_tr_thing.c

@@ -2,6 +2,7 @@
 * This is a test for tr_thing in $package$
 */
 #include "si_test.h"
+#include <mruby.h>
 #include "tr_thing.h"
 
 

+ 1 - 1
test/test_xml.c

@@ -2,7 +2,7 @@
 * This is a test for xml in $package$
 */
 #include "si_test.h"
-#include "xml.h"
+/* #include "xml.h" */
 
 
 TEST_FUNC(xml) {

+ 9 - 0
tool/template/template.c

@@ -0,0 +1,9 @@
+
+#include "$file$.h"
+
+
+
+
+
+
+

+ 10 - 0
tool/template/template.h

@@ -0,0 +1,10 @@
+#ifndef $file$_H_INCLUDED
+#define $file$_H_INCLUDED
+
+
+
+#endif
+
+
+
+

+ 20 - 0
tool/template/test_template.c

@@ -0,0 +1,20 @@
+/**
+* This is a test for $file$ in $package$
+*/
+#include "si_test.h"
+#include "$file$.h"
+
+
+TEST_FUNC($file$) {
+  TEST_DONE();
+}
+
+
+int main(void) {
+  TEST_INIT();
+  TEST_RUN($file$);
+  TEST_REPORT();
+}
+
+
+