[gnome-nibbles/wip/vala: 7/16] Add worms



commit a772615c80fa23661e033bc4f10bf17d6f5126bc
Author: Iulian Radu <iulian radu67 gmail com>
Date:   Fri Jun 26 21:17:12 2015 +0300

    Add worms

 configure.ac                       |    2 +-
 data/org.gnome.nibbles.gschema.xml |   81 ++++++++++
 src/Makefile.am                    |    3 +-
 src/gnome-nibbles.vala             |   40 ++++-
 src/nibbles-game.vala              |   96 ++++++++++-
 src/nibbles-view.vala              |  305 ++++++++++++++++++++++++++++-------
 src/worm.vala                      |  282 +++++++++++++++++++++++++++++++++
 7 files changed, 730 insertions(+), 79 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 0b1226f..78b9ddd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -9,7 +9,7 @@ GNOME_MAINTAINER_MODE_DEFINES
 AC_PROG_CC
 AC_PROG_CXX
 
-AM_PROG_VALAC([0.26.0.91])
+AM_PROG_VALAC([0.28.0])
 AM_PROG_CC_C_O
 
 GLIB_GSETTINGS
diff --git a/data/org.gnome.nibbles.gschema.xml b/data/org.gnome.nibbles.gschema.xml
index 406dc85..7be2d8c 100644
--- a/data/org.gnome.nibbles.gschema.xml
+++ b/data/org.gnome.nibbles.gschema.xml
@@ -28,4 +28,85 @@
       <description>Game level to start on.</description>
     </key>
   </schema>
+  <schema id="org.gnome.nibbles.worm0" path="/org/gnome/nibbles/worm0/">
+    <key name="color" type="s">
+      <default>'red'</default>
+      <summary>Color to use for worm</summary>
+      <description>Color to use for worm.</description>
+    </key>
+    <key name="key-up" type="i">
+      <default>65362</default>
+      <summary>Move up</summary>
+      <description>Key to use for motion up.</description>
+    </key>
+    <key name="key-down" type="i">
+      <default>65364</default>
+      <summary>Move down</summary>
+      <description>Key to use for motion down.</description>
+    </key>
+    <key name="key-left" type="i">
+      <default>65361</default>
+      <summary>Move left</summary>
+      <description>Key to use for motion left.</description>
+    </key>
+    <key name="key-right" type="i">
+      <default>65363</default>
+      <summary>Move right</summary>
+      <description>Key to use for motion right.</description>
+    </key>
+  </schema>
+  <schema id="org.gnome.nibbles.worm1" path="/org/gnome/nibbles/worm1/">
+    <key name="color" type="s">
+      <default>'green'</default>
+      <summary>Color to use for worm</summary>
+      <description>Color to use for worm.</description>
+    </key>
+    <key name="key-up" type="i">
+      <default>65362</default>
+      <summary>Move up</summary>
+      <description>Key to use for motion up.</description>
+    </key>
+    <key name="key-down" type="i">
+      <default>65364</default>
+      <summary>Move down</summary>
+      <description>Key to use for motion down.</description>
+    </key>
+    <key name="key-left" type="i">
+      <default>65361</default>
+      <summary>Move left</summary>
+      <description>Key to use for motion left.</description>
+    </key>
+    <key name="key-right" type="i">
+      <default>65363</default>
+      <summary>Move right</summary>
+      <description>Key to use for motion right.</description>
+    </key>
+  </schema>
+  <schema id="org.gnome.nibbles.worm2" path="/org/gnome/nibbles/worm2/">
+    <key name="color" type="s">
+      <default>'blue'</default>
+      <summary>Color to use for worm</summary>
+      <description>Color to use for worm.</description>
+    </key>
+    <key name="key-up" type="i">
+      <default>65362</default>
+      <summary>Move up</summary>
+      <description>Key to use for motion up.</description>
+    </key>
+    <key name="key-down" type="i">
+      <default>65364</default>
+      <summary>Move down</summary>
+      <description>Key to use for motion down.</description>
+    </key>
+    <key name="key-left" type="i">
+      <default>65361</default>
+      <summary>Move left</summary>
+      <description>Key to use for motion left.</description>
+    </key>
+    <key name="key-right" type="i">
+      <default>65363</default>
+      <summary>Move right</summary>
+      <description>Key to use for motion right.</description>
+    </key>
+  </schema>
 </schemalist>
diff --git a/src/Makefile.am b/src/Makefile.am
index 78ed01d..07183d3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,9 +9,10 @@ gnome_nibbles_SOURCES = \
        nibbles-game.vala \
        games-gridframe.h \
        games-gridframe.c \
+       worm.vala \
        $(BUILT_SOURCES)
 
-gnome_nibbles_CFLAGS = -w `pkg-config --cflags glib-2.0` `pkg-config --cflags gtk+-3.0` `pkg-config --libs 
glib-2.0` `pkg-config --libs gtk+-3.0` `pkg-config --cflags clutter-1.0` `pkg-config --cflags 
clutter-gtk-1.0` `pkg-config --libs clutter-1.0` `pkg-config --libs clutter-gtk-1.0`
+gnome_nibbles_CFLAGS = -w `pkg-config --cflags glib-2.0` `pkg-config --cflags gtk+-3.0` `pkg-config --libs 
glib-2.0` `pkg-config --libs gtk+-3.0` `pkg-config --cflags clutter-1.0` `pkg-config --cflags 
clutter-gtk-1.0` `pkg-config --libs clutter-1.0` `pkg-config --libs clutter-gtk-1.0` `pkg-config --cflags 
gee-0.8` `pkg-config --libs gee-0.8`
 
 gnome_nibbles_CPPFLAGS = \
        -DVERSION=\"$(VERSION)\" \
diff --git a/src/gnome-nibbles.vala b/src/gnome-nibbles.vala
index 5ef82f2..7c461dd 100644
--- a/src/gnome-nibbles.vala
+++ b/src/gnome-nibbles.vala
@@ -3,6 +3,7 @@ using Gtk;
 public class Nibbles : Gtk.Application
 {
     private GLib.Settings settings;
+    private Gee.ArrayList<GLib.Settings> worm_settings;
 
     private bool is_maximized;
     private bool is_tiled;
@@ -58,6 +59,12 @@ public class Nibbles : Gtk.Application
         add_action_entries (action_entries, this);
 
         settings = new GLib.Settings ("org.gnome.nibbles");
+        worm_settings = new Gee.ArrayList<GLib.Settings> ();
+        for (int i = 0; i < NibblesGame.NUMWORMS; i++)
+        {
+            var name = "org.gnome.nibbles.worm%d".printf(i);
+            worm_settings.add (new GLib.Settings (name));
+        }
 
         set_accels_for_action ("app.quit", {"<Primary>q"});
 
@@ -120,20 +127,21 @@ public class Nibbles : Gtk.Application
         /* Compute the new tile size based on the size of the
          * drawing area, rounded down.
          */
-        ts_x = event.width / game.width;
-        ts_y = event.height / game.height;
-        if (ts_x * game.width > event.width)
+        ts_x = event.width / NibblesGame.WIDTH;
+        ts_y = event.height / NibblesGame.HEIGHT;
+        if (ts_x * NibblesGame.WIDTH > event.width)
             ts_x--;
-        if (ts_y * game.height > event.height)
+        if (ts_y * NibblesGame.HEIGHT > event.height)
             ts_y--;
         tile_size = int.min (ts_x, ts_y);
 
         if (game.tile_size != tile_size)
         {
-
-            view.stage.set_size (tile_size * game.width, tile_size * game.height);
+            view.stage.set_size (tile_size * NibblesGame.WIDTH, tile_size * NibblesGame.HEIGHT);
 
             view.board_rescale (tile_size);
+            foreach (var worm in game.worms)
+                worm.rescaled (tile_size);
 
             game.tile_size = tile_size;
         }
@@ -168,16 +176,34 @@ public class Nibbles : Gtk.Application
         view = new NibblesView (game);
         view.configure_event.connect (configure_event_cb);
 
-        frame = new GamesGridFrame (game.width, game.height);
+        frame = new GamesGridFrame (NibblesGame.WIDTH, NibblesGame.HEIGHT);
         main_stack.add_named (frame, "frame");
 
         frame.add (view);
         frame.show_all ();
 
+        /* TODO Fix problem and remove this call
+         * For some reason tile_size gets set to 0 after calling
+         * frame.add (view). start_level stays the same
+         */
         game.load_properties (settings);
         game.current_level = game.start_level;
         view.new_level (game.current_level);
+
+        foreach (var worm in game.worms)
+        {
+            var actors = view.worm_actors.lookup (worm);
+            if (actors.get_stage () == null) {
+                view.stage.add_child (actors);
+            }
+            actors.show ();
+        }
+        game.load_worm_properties (worm_settings);
+
+        stderr.printf("[Debug] Showing game view\n");
         show_game_view ();
+
+        game.start ();
     }
 
     public static int main (string[] args)
diff --git a/src/nibbles-game.vala b/src/nibbles-game.vala
index 31e0c0f..81c907f 100644
--- a/src/nibbles-game.vala
+++ b/src/nibbles-game.vala
@@ -3,26 +3,80 @@ public class NibblesGame : Object
     public int tile_size;
     public int start_level;
 
-    public int DEFAULTGAMEDELAY = 35;
-    public int GAMEDELAY = 35;
-    public int NETDELAY = 2;
-    public int BONUSDELAY = 100;
+    public const int MINIMUM_TILE_SIZE = 7;
 
-    public int width = 92;
-    public int height = 66;
+    public const int DEFAULTGAMEDELAY = 35;
+    public const int GAMEDELAY = 35;
+    public const int NETDELAY = 2;
+    public const int BONUSDELAY = 100;
 
-    public char EMPTYCHAR = 'a';
-    public char WORMCHAR = 'w';
+    public const int NUMWORMS = 1;
+
+    public const int WIDTH = 92;
+    public const int HEIGHT = 66;
+
+    public const char EMPTYCHAR = 'a';
+    public const char WORMCHAR = 'w';
 
     public int current_level;
     public int[,] walls;
 
+    public Gee.LinkedList<Worm> worms;
+
+    public int numworms = NUMWORMS;
+
+    public int game_speed = 4;
+
+    public signal void worm_moved (Worm worm);
+
+    public HashTable<Worm, WormProperties?> worm_props;
+
     public NibblesGame (Settings settings)
     {
-        walls = new int[width, height];
+        walls = new int[WIDTH, HEIGHT];
+        worms = new Gee.LinkedList<Worm> ();
+        worm_props = new HashTable<Worm, WormProperties?> (direct_hash, direct_equal);
         load_properties (settings);
     }
 
+    public void start ()
+    {
+        add_worms ();
+        Timeout.add (game_speed * (GAMEDELAY + NETDELAY), main_loop_cb);
+    }
+
+    public void add_worms ()
+    {
+        stderr.printf("[Debug] Loading worms\n");
+        stderr.printf("[Debug] worms: %d\n", worms.size);
+        foreach (var worm in worms) {
+            stderr.printf("[Debug] worm size %d\n", worm.list.size);
+            worm.spawn (walls);
+        }
+    }
+
+    public void move_worms ()
+    {
+        foreach (var worm in worms)
+        {
+            if (worm.stop)
+                continue;
+            if (!worm.can_move_to (walls, numworms)) {
+                stderr.printf("[Debug] died\n");
+                worm.die (walls);
+                continue;
+            }
+
+            worm.move (walls, true);
+        }
+    }
+
+    public bool main_loop_cb ()
+    {
+        move_worms ();
+        return Source.CONTINUE;
+    }
+
     public void load_properties (Settings settings)
     {
         tile_size = settings.get_int ("tile-size");
@@ -34,4 +88,28 @@ public class NibblesGame : Object
         tile_size = settings.get_int ("tile-size");
         start_level = settings.get_int ("start-level");
     }
+
+    public void load_worm_properties (Gee.ArrayList<Settings> worm_settings)
+    {
+        foreach (var worm in worms)
+        {
+            var properties = WormProperties ();
+            properties.up = worm_settings[worm.id].get_int ("key-up");
+            properties.down = worm_settings[worm.id].get_int ("key-down");
+            properties.left = worm_settings[worm.id].get_int ("key-left");
+            properties.right = worm_settings[worm.id].get_int ("key-right");
+
+            worm_props.insert (worm, properties);
+        }
+    }
+
+    public bool handle_keypress (uint keyval)
+    {
+        foreach (var worm in worms)
+            if (worm.human)
+                if (worm.handle_keypress (keyval, worm_props))
+                    return true;
+
+        return false;
+    }
 }
diff --git a/src/nibbles-view.vala b/src/nibbles-view.vala
index 841e5da..f451570 100644
--- a/src/nibbles-view.vala
+++ b/src/nibbles-view.vala
@@ -1,22 +1,28 @@
 public class NibblesView : GtkClutter.Embed
 {
     /* Game being played */
-    public NibblesGame game { get; private set; }
+    private NibblesGame? _game = null;
+    public NibblesGame? game
+    {
+        get { return _game; }
+        set
+        {
+            if (_game != null)
+                SignalHandler.disconnect_matched (_game, SignalMatchType.DATA, 0, 0, null, null, this);
+            _game = value;
+        }
+    }
 
-    public GtkClutter.Texture surface;
-    public Clutter.Stage stage;
+    public Clutter.Stage stage { get; private set; }
+    private GtkClutter.Texture surface;
     private Clutter.Actor level;
 
-    Gdk.Pixbuf[] wall_pixmaps = { null, null, null, null, null,
-                                  null, null, null, null, null,
-                                  null
-    };
-    Gdk.Pixbuf[] worm_pixmaps = { null, null, null, null, null,
-                                   null, null
-    };
-    Gdk.Pixbuf[] boni_pixmaps = { null, null, null, null, null,
-                                   null, null, null, null
-    };
+    private Gdk.Pixbuf wall_pixmaps[11];
+    public Gdk.Pixbuf worm_pixmaps[7];
+    private Gdk.Pixbuf boni_pixmaps[9];
+
+    // public Gee.ArrayList<GtkClutter.Actor> worm_actors;
+    public HashTable<Worm, WormActor> worm_actors;
 
     public NibblesView (NibblesGame game)
     {
@@ -26,7 +32,8 @@ public class NibblesView : GtkClutter.Embed
         Clutter.Color stage_color = { 0x00, 0x00, 0x00, 0xff };
         stage.set_background_color (stage_color);
 
-        set_size_request (7 * game.width, 7 * game.height);
+        set_size_request (NibblesGame.MINIMUM_TILE_SIZE * NibblesGame.WIDTH,
+                          NibblesGame.MINIMUM_TILE_SIZE * NibblesGame.HEIGHT);
 
         try
         {
@@ -42,7 +49,6 @@ public class NibblesView : GtkClutter.Embed
             surface.set_property ("repeat-y", val);
 
             surface.set_position (0, 0);
-            surface.show ();
         }
         catch (Clutter.TextureError e)
         {
@@ -53,18 +59,24 @@ public class NibblesView : GtkClutter.Embed
             error ("Failed to load textures: %s", e.message);
         }
 
+        worm_actors = new HashTable<Worm, WormActor> (direct_hash, direct_equal);
         load_pixmap ();
 
         stage.add_child (surface);
     }
 
+    public override bool key_press_event (Gdk.EventKey event)
+    {
+        return game.handle_keypress (event.keyval);
+    }
+
     public void new_level (int level)
     {
         string level_name;
         string filename;
         string tmpboard;
+        int count = 0;
 
-        warning("%d\n", game.tile_size);
         level_name = "level%03d.gnl".printf (level);
         filename = Path.build_filename (PKGDATADIR, "levels", level_name, null);
 
@@ -74,7 +86,9 @@ public class NibblesView : GtkClutter.Embed
             error (_("Nibbles couldn't find pixmap file: %s"), filename);
         }
 
-        for (int i = 0; i < game.height; i++)
+
+        stderr.printf("[Debug] %d\n", game.tile_size);
+        for (int i = 0; i < NibblesGame.HEIGHT; i++)
         {
             if ((tmpboard = file.read_line ()) == null)
             {
@@ -82,22 +96,74 @@ public class NibblesView : GtkClutter.Embed
                 error (_("Level file appears to be damaged: %s"), filename);
             }
 
-            for (int j = 0; j < game.width; j++)
+            for (int j = 0; j < NibblesGame.WIDTH; j++)
             {
                 game.walls[j, i] = tmpboard  get(j);
                 switch (game.walls[j, i])
                 {
                     case 'm':
-                        game.walls[j, i] = game.EMPTYCHAR;
+                        game.walls[j, i] = NibblesGame.EMPTYCHAR;
+                        if (count < game.numworms)
+                        {
+                            var worm = new Worm (count++, WormDirection.UP);
+                            worm.added.connect (worm_added_cb);
+                            worm.moved.connect (worm_moved_cb);
+                            worm.rescaled.connect (worm_rescaled_cb);
+                            worm.died.connect (worm_died_cb);
+                            worm.set_start (j, i);
+                            game.worms.add (worm);
+
+                            var actors = new WormActor ();
+                            worm_actors.insert (worm, actors);
+                        }
                         break;
                     case 'n':
-                        game.walls[j, i] = game.EMPTYCHAR;
+                        game.walls[j, i] = NibblesGame.EMPTYCHAR;
+                        if (count < game.numworms)
+                        {
+                            var worm = new Worm (count++, WormDirection.DOWN);
+                            worm.added.connect (worm_added_cb);
+                            worm.moved.connect (worm_moved_cb);
+                            worm.rescaled.connect (worm_rescaled_cb);
+                            worm.died.connect (worm_died_cb);
+                            worm.set_start (j, i);
+                            game.worms.add (worm);
+
+                            var actors = new WormActor ();
+                            worm_actors.insert (worm, actors);
+                        }
                         break;
                     case 'o':
-                        game.walls[j, i] = game.EMPTYCHAR;
+                        game.walls[j, i] = NibblesGame.EMPTYCHAR;
+                        if (count < game.numworms)
+                        {
+                            var worm = new Worm (count++, WormDirection.LEFT);
+                            worm.added.connect (worm_added_cb);
+                            worm.moved.connect (worm_moved_cb);
+                            worm.rescaled.connect (worm_rescaled_cb);
+                            worm.died.connect (worm_died_cb);
+                            worm.set_start (j, i);
+                            game.worms.add (worm);
+
+                            var actors = new WormActor ();
+                            worm_actors.insert (worm, actors);
+                        }
                         break;
                     case 'p':
-                        game.walls[j, i] = game.EMPTYCHAR;
+                        game.walls[j, i] = NibblesGame.EMPTYCHAR;
+                        if (count < game.numworms)
+                        {
+                            var worm = new Worm (count++, WormDirection.RIGHT);
+                            worm.added.connect (worm_added_cb);
+                            worm.moved.connect (worm_moved_cb);
+                            worm.rescaled.connect (worm_rescaled_cb);
+                            worm.died.connect (worm_died_cb);
+                            worm.set_start (j, i);
+                            game.worms.add (worm);
+
+                            var actors = new WormActor ();
+                            worm_actors.insert (worm, actors);
+                        }
                         break;
                     default:
                         break;
@@ -105,6 +171,8 @@ public class NibblesView : GtkClutter.Embed
             }
         }
 
+        stderr.printf("[Debug] %d\n", game.tile_size);
+        stderr.printf("[Debug] Loading level\n");
         load_level ();
     }
 
@@ -167,41 +235,34 @@ public class NibblesView : GtkClutter.Embed
             "snake-grey.svg"
         };
 
-        int tile_size = game.tile_size;
         for (int i = 0; i < 8; i++) {
             boni_pixmaps[i] = load_pixmap_file (bonus_files[i],
-                                                2 * tile_size, 2 * tile_size);
+                                                2 * game.tile_size, 2 * game.tile_size);
         }
 
         for (int i = 0; i < 11; i++) {
             wall_pixmaps[i] = load_pixmap_file (small_files[i],
-                                                2 * tile_size, 2 * tile_size);
+                                                2 * game.tile_size, 2 * game.tile_size);
         }
 
         for (int i = 0; i < 7; i++) {
             worm_pixmaps[i] = load_pixmap_file (worm_files[i],
-                                                tile_size, tile_size);
+                                                game.tile_size, game.tile_size);
         }
     }
 
     void load_level ()
     {
         int x_pos, y_pos;
-        Clutter.Actor tmp = null;
+        GtkClutter.Texture tmp = null;
         bool is_wall = true;
         level = new Clutter.Actor ();
 
-        // if (level != null)
-        // {
-        //     warning("here");
-        //     level.remove_all_children ();
-        //     stage.remove_child (level);
-        // }
         /* Load wall_pixmaps onto the surface */
-        for (int i = 0; i < game.height; i++)
+        for (int i = 0; i < NibblesGame.HEIGHT; i++)
         {
             y_pos = i * game.tile_size;
-            for (int j = 0; j < game.width; j++)
+            for (int j = 0; j < NibblesGame.WIDTH; j++)
             {
                 is_wall = true;
                 try
@@ -213,56 +274,56 @@ public class NibblesView : GtkClutter.Embed
                             break;
                         case 'b': // straight up
                             tmp = new GtkClutter.Texture ();
-                            ((GtkClutter.Texture) tmp).set_from_pixbuf (wall_pixmaps[0]);
+                            tmp.set_from_pixbuf (wall_pixmaps[0]);
                             break;
                         case 'c': // straight side
                             tmp = new GtkClutter.Texture ();
-                            ((GtkClutter.Texture) tmp).set_from_pixbuf (wall_pixmaps[1]);
+                            tmp.set_from_pixbuf (wall_pixmaps[1]);
                             break;
                         case 'd': // corner bottom left
                             tmp = new GtkClutter.Texture ();
-                            ((GtkClutter.Texture) tmp).set_from_pixbuf (wall_pixmaps[2]);
+                            tmp.set_from_pixbuf (wall_pixmaps[2]);
                             break;
                         case 'e': // corner bottom right
                             tmp = new GtkClutter.Texture ();
-                            ((GtkClutter.Texture) tmp).set_from_pixbuf (wall_pixmaps[3]);
+                            tmp.set_from_pixbuf (wall_pixmaps[3]);
                             break;
                         case 'f': // corner up left
                             tmp = new GtkClutter.Texture ();
-                            ((GtkClutter.Texture) tmp).set_from_pixbuf (wall_pixmaps[4]);
+                            tmp.set_from_pixbuf (wall_pixmaps[4]);
                             break;
                         case 'g': // corner up right
                             tmp = new GtkClutter.Texture ();
-                            ((GtkClutter.Texture) tmp).set_from_pixbuf (wall_pixmaps[5]);
+                            tmp.set_from_pixbuf (wall_pixmaps[5]);
                             break;
                         case 'h': // tee up
                             tmp = new GtkClutter.Texture ();
-                            ((GtkClutter.Texture) tmp).set_from_pixbuf (wall_pixmaps[6]);
+                            tmp.set_from_pixbuf (wall_pixmaps[6]);
                             break;
                         case 'i': // tee right
                             tmp = new GtkClutter.Texture ();
-                            ((GtkClutter.Texture) tmp).set_from_pixbuf (wall_pixmaps[7]);
+                            tmp.set_from_pixbuf (wall_pixmaps[7]);
                             break;
                         case 'j': // tee left
                             tmp = new GtkClutter.Texture ();
-                            ((GtkClutter.Texture) tmp).set_from_pixbuf (wall_pixmaps[8]);
+                            tmp.set_from_pixbuf (wall_pixmaps[8]);
                             break;
                         case 'k': // tee down
                             tmp = new GtkClutter.Texture ();
-                            ((GtkClutter.Texture) tmp).set_from_pixbuf (wall_pixmaps[9]);
+                            tmp.set_from_pixbuf (wall_pixmaps[9]);
                             break;
                         case 'l': // tee cross
                             tmp = new GtkClutter.Texture ();
-                            ((GtkClutter.Texture) tmp).set_from_pixbuf (wall_pixmaps[10]);
+                            tmp.set_from_pixbuf (wall_pixmaps[10]);
                             break;
                         default:
                             is_wall = false;
                             break;
                     }
                 }
-                catch (GLib.Error e)
+                catch (Error e)
                 {
-
+                    error (e.message);
                 }
 
                 if (is_wall)
@@ -272,7 +333,6 @@ public class NibblesView : GtkClutter.Embed
                     ((Clutter.Actor) tmp).set_size (game.tile_size,
                                                     game.tile_size);
                     ((Clutter.Actor) tmp).set_position (x_pos, y_pos);
-                    ((Clutter.Actor) tmp).show ();
                     level.add_child ((Clutter.Actor) tmp);
                 }
             }
@@ -285,7 +345,7 @@ public class NibblesView : GtkClutter.Embed
 
         level.save_easing_state ();
         level.set_easing_mode (Clutter.AnimationMode.EASE_OUT_BOUNCE);
-        level.set_easing_duration (game.GAMEDELAY * game.GAMEDELAY);
+        level.set_easing_duration (NibblesGame.GAMEDELAY * NibblesGame.GAMEDELAY);
         level.set_scale (1.0, 1.0);
         level.set_pivot_point (0.5f, 0.5f);
         level.set_opacity (0xff);
@@ -294,30 +354,153 @@ public class NibblesView : GtkClutter.Embed
 
     public void board_rescale (int tile_size)
     {
-        int count;
         int board_width, board_height;
         float x_pos, y_pos;
-        Clutter.Actor tmp;
 
         if (level == null)
             return;
         if (surface == null)
             return;
 
-        board_width = game.width * tile_size;
-        board_height = game.height * tile_size;
+        board_width = NibblesGame.WIDTH * tile_size;
+        board_height = NibblesGame.HEIGHT * tile_size;
 
         surface.set_size (board_width, board_height);
 
-        count = level.get_n_children ();
+        foreach (var actor in level.get_children ())
+        {
+            actor.get_position (out x_pos, out y_pos);
+            actor.set_position ((x_pos / game.tile_size) * tile_size,
+                                (y_pos / game.tile_size) * tile_size);
+            actor.set_size (tile_size, tile_size);
+        }
+    }
+
+    public void worm_added_cb (Worm worm)
+    {
+        var actor = new GtkClutter.Texture ();
+        try
+        {
+            actor.set_from_pixbuf (worm_pixmaps[0]);
+        }
+        catch (Clutter.TextureError e)
+        {
+            error ("Failed to set texture: %s", e.message);
+        }
+        catch (GLib.Error e)
+        {
+            error ("Failed to set texture: %s", e.message);
+        }
+
+        actor.set_size (game.tile_size, game.tile_size);
+        actor.set_position (worm.list.first ().x * game.tile_size, worm.list.first ().y * game.tile_size);
+
+        var actors = worm_actors.lookup (worm);
+        actors.add_child (actor);
+
+        for (int i = 0; i < 13; i++) {
+            for (int j = 0; j < 20; j++)
+                stderr.printf("%c ", game.walls[j, i]);
+            stderr.printf("\n");
+        }
+        stderr.printf("\n");
+    }
+
+    public void worm_moved_cb (Worm worm)
+    {
+        var actors = worm_actors.lookup (worm);
+
+        /* Make the worms last actor (the tail) the new head. Then remove it
+         * and add it again so the head is always the last child added */
+        var tail_actor = actors.first_child;
+        tail_actor.set_position (worm.list.first ().x * game.tile_size, worm.list.first ().y * 
game.tile_size);
+        actors.remove_child (tail_actor);
+        actors.add_child (tail_actor);
+
+        for (int i = 0; i < 13; i++) {
+            for (int j = 0; j < 20; j++)
+                stderr.printf("%c ", game.walls[j, i]);
+            stderr.printf("\n");
+        }
+        stderr.printf("\n");
+    }
+
+    public void worm_rescaled_cb (Worm worm, int tile_size)
+    {
+        float x_pos, y_pos;
+        var actors = worm_actors.lookup (worm);
+        if (actors == null)
+            return;
 
-        for (int i = 0; i < count; i++)
+        foreach (var actor in actors.get_children ())
         {
-            tmp = level.get_child_at_index (i);
-            ((Clutter.Actor) tmp).get_position (out x_pos, out y_pos);
-            ((Clutter.Actor) tmp).set_position ((x_pos / game.tile_size) * tile_size,
-                                                (y_pos / game.tile_size) * tile_size);
-            ((Clutter.Actor) tmp).set_size (tile_size, tile_size);
+            actor.get_position (out x_pos, out y_pos);
+            actor.set_position ((x_pos / game.tile_size) * tile_size,
+                                (y_pos / game.tile_size) * tile_size);
+            actor.set_size (tile_size, tile_size);
         }
     }
+
+    public void worm_died_cb (Worm worm)
+    {
+        float x, y;
+        var group = new Clutter.Actor ();
+        var actors = worm_actors.lookup (worm);
+        foreach (var actor in actors.get_children ())
+        {
+            stderr.printf("[Debug] Before\n");
+            GtkClutter.Texture tmp = new GtkClutter.Texture ();
+            var color = game.worm_props.lookup (worm).color;
+            try
+            {
+                tmp.set_from_pixbuf (worm_pixmaps[color]);
+            }
+            catch (Clutter.TextureError e)
+            {
+                error ("Failed to set texture: %s", e.message);
+            }
+            catch (GLib.Error e)
+            {
+                error ("Failed to set texture: %s", e.message);
+            }
+
+            actor.get_position (out x, out y);
+
+            tmp.set_position (x, y);
+            tmp.set_size (game.tile_size, game.tile_size);
+            group.add_child (tmp);
+            stderr.printf("[Debug] After\n");
+        }
+
+        actors.remove_all_children ();
+
+        stage.add_child (group);
+
+        group.save_easing_state ();
+        group.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD);
+        group.set_easing_duration (NibblesGame.GAMEDELAY * 9);
+        group.set_scale (2.0f, 2.0f);
+        group.set_pivot_point (5f, 0.5f);
+        group.set_opacity (0);
+        group.restore_easing_state ();
+    }
+}
+
+public class WormActor : Clutter.Actor
+{
+    public override void show ()
+    {
+        base.show ();
+
+        set_opacity (0);
+        set_scale (3.0, 3.0);
+
+        save_easing_state ();
+        set_easing_mode (Clutter.AnimationMode.EASE_OUT_CIRC);
+        set_easing_duration (NibblesGame.GAMEDELAY * 26);
+        set_scale (1.0, 1.0);
+        set_pivot_point (0.5f, 0.5f);
+        set_opacity (0xff);
+        restore_easing_state ();
+    }
 }
diff --git a/src/worm.vala b/src/worm.vala
new file mode 100644
index 0000000..453406d
--- /dev/null
+++ b/src/worm.vala
@@ -0,0 +1,282 @@
+public struct Position
+{
+    int x;
+    int y;
+}
+
+public enum WormDirection
+{
+    UP,
+    DOWN,
+    LEFT,
+    RIGHT
+}
+
+public struct WormProperties
+{
+    int color;
+    uint left;
+    uint right;
+    uint up;
+    uint down;
+}
+
+public class Worm : Object
+{
+    public const int STARTING_LENGTH = 5;
+    private const int STARTING_LIVES = 6;
+
+    public Position starting_position { get; private set; }
+
+    public int id { get; private set; }
+
+    public bool human;
+    public bool keypress = false;
+    public bool stop = false;
+
+    public int lives { get; private set; }
+    private WormDirection _direction;
+    public WormDirection direction
+    {
+        get { return _direction; }
+        set
+        {
+            if (keypress)
+            {
+                queue_keypress (value);
+                return;
+            }
+
+            _direction = value;
+            keypress = true;
+        }
+    }
+
+    public WormDirection starting_direction;
+
+    private Gee.ArrayQueue<WormDirection> key_queue;
+
+    public Gee.LinkedList<Position?> list { get; private set; }
+
+    public signal void added ();
+    public signal void moved ();
+    public signal void rescaled (int tile_size);
+    public signal void died ();
+
+    public Worm (int id, WormDirection direction)
+    {
+        this.id = id;
+        human = true;
+        starting_direction = direction;
+        lives = STARTING_LIVES;
+        list = new Gee.LinkedList<Position?> ();
+        key_queue = new Gee.ArrayQueue<WormDirection> ();
+    }
+
+    public Position head ()
+    {
+        return list.first ();
+    }
+
+    public void set_start (int xhead, int yhead)
+    {
+        starting_position = Position () {
+            x = xhead,
+            y = yhead
+        };
+
+        list.add (starting_position);
+
+        this.direction = starting_direction;
+    }
+
+    public void move (int[,] walls, bool remove)
+    {
+        if (human)
+            keypress = false;
+
+        var position = head ();
+        switch (direction)
+        {
+            case WormDirection.UP:
+                position.y = --head ().y;
+                if (position.y < 0)
+                    position.y = NibblesGame.HEIGHT - 1;
+                break;
+            case WormDirection.DOWN:
+                position.y = ++head ().y;
+                if (position.y >= NibblesGame.HEIGHT)
+                    position.y = 0;
+                break;
+            case WormDirection.LEFT:
+                position.x = --head ().x;
+                if (position.x < 0)
+                    position.x = NibblesGame.WIDTH - 1;
+                break;
+            case WormDirection.RIGHT:
+                position.x = ++head ().x;
+                if (position.x >= NibblesGame.WIDTH)
+                    position.x = 0;
+                break;
+            default:
+                break;
+        }
+
+        stderr.printf("[Debug] x %d y %d\n", position.x, position.y);
+        /* Add a new body piece */
+        list.offer_head (position);
+        /* Mark the tile as occupied by the worm's body */
+        walls[head ().x, head ().y] = NibblesGame.WORMCHAR + id;
+
+        if (remove)
+        {
+            walls[list.last ().x, list.last ().y] = NibblesGame.EMPTYCHAR;
+            list.poll_tail ();
+            moved ();
+        }
+        else
+            added ();
+
+        if (!key_queue.is_empty)
+            dequeue_keypress ();
+    }
+
+    public bool can_move_to (int[,] walls, int numworms)
+    {
+        Position position = position_move ();
+
+        if (walls[position.x, position.y] > NibblesGame.EMPTYCHAR &&
+            walls[position.x, position.y] < 'z' + numworms)
+            return false;
+
+        return true;
+    }
+
+    public void spawn (int[,] walls)
+    {
+        for (int i = 0; i < STARTING_LENGTH; i++)
+            move (walls, false);
+    }
+
+    public void lose_life ()
+    {
+        lives--;
+    }
+
+    public void die (int[,] walls)
+    {
+        stop = true;
+        lose_life ();
+
+        died ();
+        foreach (var pos in list)
+            walls[pos.x, pos.y] = NibblesGame.EMPTYCHAR;
+
+        list.clear ();
+        list.add (starting_position);
+        direction = starting_direction;
+        spawn (walls);
+
+        key_queue.clear ();
+
+        stop = false;
+    }
+
+    private Position position_move ()
+    {
+        Position position = head ();
+
+        switch (direction)
+        {
+            case WormDirection.UP:
+                position.y = --head ().y;
+                if (position.y < 0)
+                    position.y = NibblesGame.HEIGHT - 1;
+                break;
+            case WormDirection.DOWN:
+                position.y = ++head ().y;
+                if (position.y >= NibblesGame.HEIGHT)
+                    position.y = 0;
+                break;
+            case WormDirection.LEFT:
+                position.x = --head ().x;
+                if (position.x < 0)
+                    position.x = NibblesGame.WIDTH - 1;
+                break;
+            case WormDirection.RIGHT:
+                position.x = ++head ().x;
+                if (position.x >= NibblesGame.WIDTH)
+                    position.x = 0;
+                break;
+            default:
+                break;
+        }
+
+        return position;
+    }
+
+    public bool handle_keypress (uint keyval, HashTable<Worm, WormProperties?> worm_props)
+    {
+        WormProperties properties;
+        uint propsUp, propsDown, propsLeft, propsRight, keyvalUpper;
+
+        if (lives <= 0)
+            return false;
+
+        properties = worm_props.lookup (this);
+        propsUp = upper_key (properties.up);
+        propsLeft = upper_key (properties.left);
+        propsDown = upper_key (properties.down);
+        propsRight = upper_key (properties.right);
+        keyvalUpper = upper_key (keyval);
+
+        if ((keyvalUpper == propsUp) && (direction != WormDirection.DOWN))
+        {
+            handle_direction (WormDirection.UP);
+            return true;
+        }
+        if ((keyvalUpper == propsDown) && (direction != WormDirection.UP)) {
+            handle_direction (WormDirection.DOWN);
+            return true;
+        }
+        if ((keyvalUpper == propsRight) && (direction != WormDirection.LEFT)) {
+            handle_direction (WormDirection.RIGHT);
+            return true;
+        }
+        if ((keyvalUpper == propsLeft) && (direction != WormDirection.RIGHT)) {
+            handle_direction (WormDirection.LEFT);
+            return true;
+        }
+
+        return false;
+    }
+
+    private uint upper_key (uint keyval)
+    {
+        if (keyval > 255)
+            return keyval;
+        return ((char) keyval).toupper ();
+    }
+
+    public void handle_direction (WormDirection dir)
+    {
+        direction = dir;
+    }
+
+    public void queue_keypress (WormDirection dir)
+                requires (!key_queue.is_empty)
+    {
+        /* Ignore duplicates in normal movement mode. This resolves the key
+         * repeat issue
+         */
+        if (!key_queue.is_empty && dir == key_queue.peek ())
+            return;
+
+        key_queue.add (dir);
+    }
+
+    public void dequeue_keypress ()
+    {
+        direction = key_queue.poll ();
+    }
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]