[gnome-games/quadrapassel-vala] quadrapassel: Port from C++ to Vala
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-games/quadrapassel-vala] quadrapassel: Port from C++ to Vala
- Date: Tue, 17 Jan 2012 23:51:52 +0000 (UTC)
commit f70ae959bb38d6bea2bca5435456f3f5334a115f
Author: Robert Ancell <robert ancell canonical com>
Date: Wed Jan 4 16:03:56 2012 +1100
quadrapassel: Port from C++ to Vala
configure.ac | 20 +-
libgames-support/GnomeGamesSupport-1.0.vapi | 8 +
.../data/org.gnome.quadrapassel.gschema.xml.in | 12 +-
quadrapassel/src/Makefile.am | 54 +-
quadrapassel/src/blockops.cpp | 894 --------------
quadrapassel/src/blockops.h | 145 ---
quadrapassel/src/blocks-cache.cpp | 379 ------
quadrapassel/src/blocks-cache.h | 70 --
quadrapassel/src/blocks.cpp | 398 ------
quadrapassel/src/blocks.h | 73 --
quadrapassel/src/config.vapi | 4 +
quadrapassel/src/game-view.vala | 475 +++++++
quadrapassel/src/game.vala | 558 +++++++++
quadrapassel/src/highscores.cpp | 53 -
quadrapassel/src/highscores.h | 41 -
quadrapassel/src/main.cpp | 85 --
quadrapassel/src/preview.cpp | 154 ---
quadrapassel/src/preview.h | 66 -
quadrapassel/src/preview.vala | 107 ++
quadrapassel/src/quadrapassel.vala | 762 ++++++++++++
quadrapassel/src/renderer.cpp | 296 -----
quadrapassel/src/renderer.h | 64 -
quadrapassel/src/scoreframe.cpp | 163 ---
quadrapassel/src/scoreframe.h | 75 --
quadrapassel/src/scoreframe.vala | 139 +++
quadrapassel/src/sound.cpp | 61 -
quadrapassel/src/sound.h | 32 -
quadrapassel/src/sound.vala | 21 +
quadrapassel/src/tetris.cpp | 1304 --------------------
quadrapassel/src/tetris.h | 175 ---
30 files changed, 2103 insertions(+), 4585 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 1b4e6f6..77f6aa8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -107,7 +107,6 @@ AC_SUBST([gamelist])
# Feature matrix
-need_cxx=no
need_vala=no
need_rsvg=no
need_sqlite=no
@@ -118,11 +117,7 @@ need_clutter=no
for game in $gamelist; do
case $game in
- quadrapassel) need_cxx=yes ;;
- *) ;;
- esac
- case $game in
- glchess|gnomine|gnotravex|iagno|lightsoff|mahjongg) need_vala=yes ;;
+ glchess|gnomine|gnotravex|iagno|lightsoff|mahjongg|quadrapassel) need_vala=yes ;;
*) ;;
esac
case $game in
@@ -189,16 +184,6 @@ if test "$need_vala" = "yes"; then
AM_PROG_VALAC([0.13.0])
fi
-if test "$need_cxx" = "yes"; then
- AC_PROG_CXX
-
- # Check whether a C++ was found (AC_PROG_CXX sets $CXX to "g++" even when it
- # doesn't exist)
- AC_LANG_PUSH([C++])
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],[],[AC_MSG_ERROR([No C++ compiler found])])
- AC_LANG_POP([C++])
-fi
-
AM_PROG_CC_C_O
LT_INIT
@@ -206,7 +191,6 @@ LT_INIT
GNOME_COMMON_INIT
GNOME_DEBUG_CHECK
GNOME_COMPILE_WARNINGS([maximum])
-GNOME_CXX_WARNINGS([yes])
GNOME_MAINTAINER_MODE_DEFINES
dnl ****************************************************************************
@@ -451,7 +435,6 @@ fi
# ********
AM_CFLAGS="$AM_CFLAGS $WARN_CFLAGS"
-AM_CXXFLAGS="$AM_CXXFLAGS $WARN_CXXFLAGS"
# ****
# i18n
@@ -552,7 +535,6 @@ GOBJECT_INTROSPECTION_CHECK([0.6.3])
AC_SUBST([AM_CPPFLAGS])
AC_SUBST([AM_CFLAGS])
-AC_SUBST([AM_CXXFLAGS])
AC_SUBST([AM_LDFLAGS])
##############################################
diff --git a/libgames-support/GnomeGamesSupport-1.0.vapi b/libgames-support/GnomeGamesSupport-1.0.vapi
index 5f95ae1..778e5ac 100644
--- a/libgames-support/GnomeGamesSupport-1.0.vapi
+++ b/libgames-support/GnomeGamesSupport-1.0.vapi
@@ -192,5 +192,13 @@ namespace GnomeGamesSupport
public unowned string? get_nth (int n);
public Gtk.Widget create_widget (string selection, uint flags);
}
+
+ [CCode (cheader_filename = "games-controls.h")]
+ public class ControlsList : Gtk.ScrolledWindow
+ {
+ public ControlsList (GLib.Settings settings);
+ public void add_control (string conf_key, string label, uint default_keyval);
+ public void add_controls (string first_conf_key, ...);
+ }
}
diff --git a/quadrapassel/data/org.gnome.quadrapassel.gschema.xml.in b/quadrapassel/data/org.gnome.quadrapassel.gschema.xml.in
index 80adf4d..538e1e3 100644
--- a/quadrapassel/data/org.gnome.quadrapassel.gschema.xml.in
+++ b/quadrapassel/data/org.gnome.quadrapassel.gschema.xml.in
@@ -6,7 +6,7 @@
<_description>Image to use for drawing blocks.</_description>
</key>
<key name="theme" type="s">
- <default>'tangoshaded'</default>
+ <default>'clean'</default>
<_summary>The theme used for rendering the blocks</_summary>
<_description>The name of the theme used for rendering the blocks and the background.</_description>
</key>
@@ -15,16 +15,6 @@
<_summary>Level to start with</_summary>
<_description>Level to start with.</_description>
</key>
- <key name="use-bg-image" type="b">
- <default>true</default>
- <_summary>Whether to use the background image</_summary>
- <_description>This selects whether or not to draw the background image over the background color.</_description>
- </key>
- <key name="bg-color" type="s">
- <default>'Black'</default>
- <_summary>The background color</_summary>
- <_description>The background color, in a format gdk_color_parse understands.</_description>
- </key>
<key name="do-preview" type="b">
<default>true</default>
<_summary>Whether to preview the next block</_summary>
diff --git a/quadrapassel/src/Makefile.am b/quadrapassel/src/Makefile.am
index f860056..74cec05 100644
--- a/quadrapassel/src/Makefile.am
+++ b/quadrapassel/src/Makefile.am
@@ -1,38 +1,38 @@
bin_PROGRAMS = quadrapassel
quadrapassel_SOURCES = \
- main.cpp \
- blocks.cpp \
- blocks.h \
- highscores.cpp \
- highscores.h \
- scoreframe.cpp \
- scoreframe.h \
- tetris.cpp \
- tetris.h \
- preview.cpp \
- preview.h \
- blockops.cpp \
- blockops.h \
- renderer.cpp \
- renderer.h \
- blocks-cache.cpp \
- blocks-cache.h \
- sound.cpp \
- sound.h
+ config.vapi \
+ fixes.vapi \
+ scoreframe.vala \
+ quadrapassel.vala \
+ preview.vala \
+ game.vala \
+ game-view.vala \
+ sound.vala
-quadrapassel_CPPFLAGS = \
- -I$(top_srcdir) \
- $(AM_CPPFLAGS)
-
-quadrapassel_CXXFLAGS = \
+quadrapassel_CFLAGS = \
+ -I$(top_srcdir)/libgames-support \
+ -DVERSION=\"$(VERSION)\" \
+ -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \
-DDATA_DIRECTORY=\"$(datadir)/quadrapassel\" \
-DSOUND_DIRECTORY=\"$(pkgdatadir)/sounds\" \
$(GTK_CFLAGS) \
$(CANBERRA_GTK_CFLAGS) \
$(CLUTTER_GTK_CFLAGS) \
- $(CLUTTER_CFLAGS) \
- $(AM_CXXFLAGS)
+ $(CLUTTER_CFLAGS)
+
+quadrapassel_VALAFLAGS = \
+ --pkg posix \
+ --pkg gtk+-3.0 \
+ --pkg pango \
+ --pkg pangocairo \
+ --pkg clutter-1.0 \
+ --pkg clutter-gtk-1.0 \
+ --pkg cogl-1.0 \
+ --pkg libcanberra \
+ --pkg libcanberra-gtk \
+ --vapidir $(top_srcdir)/libgames-support \
+ --pkg GnomeGamesSupport-1.0
quadrapassel_LDADD = \
$(top_builddir)/libgames-support/libgames-support.la \
@@ -43,7 +43,7 @@ quadrapassel_LDADD = \
$(INTLLIBS)
if HAVE_RSVG
-quadrapassel_CXXFLAGS += $(RSVG_CFLAGS)
+quadrapassel_CFLAGS += $(RSVG_CFLAGS)
quadrapassel_LDADD += $(RSVG_LIBS)
endif
diff --git a/quadrapassel/src/config.vapi b/quadrapassel/src/config.vapi
new file mode 100644
index 0000000..e90b1fe
--- /dev/null
+++ b/quadrapassel/src/config.vapi
@@ -0,0 +1,4 @@
+public const string VERSION;
+public const string GETTEXT_PACKAGE;
+public const string DATA_DIRECTORY;
+public const string SOUND_DIRECTORY;
diff --git a/quadrapassel/src/game-view.vala b/quadrapassel/src/game-view.vala
new file mode 100644
index 0000000..7c5fc7e
--- /dev/null
+++ b/quadrapassel/src/game-view.vala
@@ -0,0 +1,475 @@
+//FIXME: Drop animation
+//var timeline = new Clutter.Timeline (360);
+//b.actor.animate_with_timeline (Clutter.AnimationMode.EASE_IN_QUAD, timeline, "x", (float) x, "y", (float) y);
+//timeline.set_duration (360 / (5 - num_full_lines));
+//timeline.completed.connect (fall_completed_cb);
+
+private class BlockActor : Clutter.Clone
+{
+ public Block block;
+
+ public BlockActor (Block block, Clutter.Actor texture)
+ {
+ Object (source: texture);
+ this.block = block;
+ }
+
+ public void move (float x, float y)
+ {
+ animate (Clutter.AnimationMode.EASE_IN_QUAD, 60, "x", x, "y", y);
+ }
+
+ public void explode ()
+ {
+ raise_top ();
+ var timeline = new Clutter.Timeline (720);
+ timeline.completed.connect (explode_complete_cb);
+ animate_with_timeline (Clutter.AnimationMode.EASE_OUT_QUINT, timeline, "opacity", 0, "scale-x", 2f, "scale-y", 2f);
+ }
+
+ private void explode_complete_cb ()
+ {
+ destroy ();
+ }
+}
+
+private class TextOverlay : Clutter.CairoTexture
+{
+ private string? _text = null;
+ public string text
+ {
+ get { return _text; }
+ set { _text = value; invalidate (); }
+ }
+
+ public TextOverlay (int width, int height)
+ {
+ auto_resize = true;
+ set_surface_size (width, height);
+ }
+
+ protected override bool draw (Cairo.Context cr)
+ {
+ clear ();
+
+ if (text == null)
+ return false;
+
+ // Center coordinates
+ uint w, h;
+ get_surface_size (out w, out h);
+ cr.translate (w / 2, h / 2);
+
+ var desc = Pango.FontDescription.from_string ("Sans");
+
+ var layout = Pango.cairo_create_layout (cr);
+ layout.set_text (text, -1);
+
+ var dummy_layout = layout.copy ();
+ dummy_layout.set_font_description (desc);
+ int lw, lh;
+ dummy_layout.get_size (out lw, out lh);
+
+ // desired height : lh = widget width * 0.9 : lw
+ desc.set_absolute_size (((float) lh / lw) * Pango.SCALE * w * 0.7);
+ layout.set_font_description (desc);
+
+ layout.get_size (out lw, out lh);
+ cr.move_to (-((double)lw / Pango.SCALE) / 2, -((double)lh / Pango.SCALE) / 2);
+ Pango.cairo_layout_path (cr, layout);
+ cr.set_source_rgb (0.333333333333, 0.341176470588, 0.32549019607);
+
+ /* A linewidth of 2 pixels at the default size. */
+ cr.set_line_width (width / 100.0);
+ cr.stroke_preserve ();
+
+ cr.set_source_rgb (1.0, 1.0, 1.0);
+ cr.fill ();
+
+ return false;
+ }
+}
+
+public class BlockTexture : Clutter.CairoTexture
+{
+ private int color;
+ private string? _theme = null;
+ public string? theme
+ {
+ get { return _theme; }
+ set
+ {
+ if (_theme == value)
+ return;
+ _theme = value;
+ invalidate ();
+ }
+ }
+
+ public BlockTexture (int color, int size)
+ {
+ auto_resize = true;
+ set_surface_size (size, size);
+ this.color = color.clamp (0, 6);
+ }
+
+ protected override bool draw (Cairo.Context cr)
+ {
+ clear ();
+
+ uint w, h;
+ get_surface_size (out w, out h);
+ cr.scale (w, h);
+
+ switch (theme)
+ {
+ default:
+ case "plain":
+ draw_plain (cr);
+ break;
+ case "clean":
+ draw_clean (cr);
+ break;
+ case "tangoflat":
+ draw_tango (cr, false);
+ break;
+ case "tangoshaded":
+ draw_tango (cr, true);
+ break;
+ }
+
+ return false;
+ }
+
+ private void draw_plain (Cairo.Context cr)
+ {
+ const double colors[32] =
+ {
+ 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0,
+ 0.0, 0.0, 1.0,
+ 1.0, 1.0, 1.0,
+ 1.0, 1.0, 0.0,
+ 1.0, 0.0, 1.0,
+ 0.0, 1.0, 1.0
+ };
+
+ cr.set_source_rgb(colors[color * 3], colors[color * 3 + 1], colors[color * 3 + 2]);
+ cr.paint ();
+ }
+
+ private void draw_rounded_rectangle (Cairo.Context cr, double x, double y, double w, double h, double r)
+ {
+ cr.move_to (x + r, y);
+ cr.line_to (x + w - r, y);
+ cr.curve_to (x + w - (r/2), y, x + w, y + r / 2, x + w, y + r);
+ cr.line_to (x + w, y + h - r);
+ cr.curve_to (x + w, y + h - r / 2, x + w - r / 2, y + h, x + w - r, y + h);
+ cr.line_to (x + r, y + h);
+ cr.curve_to (x + r / 2, y + h, x, y + h - r / 2, x, y + h - r);
+ cr.line_to (x, y + r);
+ cr.curve_to (x, y + r / 2, x + r / 2, y, x + r, y);
+ }
+
+ private void draw_clean (Cairo.Context cr)
+ {
+ /* The colors, first the lighter then the darker fill (for the gradient)
+ and then the stroke color */
+ const double colors[72] =
+ {
+ 0.780392156863, 0.247058823529, 0.247058823529,
+ 0.713725490196, 0.192156862745, 0.192156862745,
+ 0.61568627451, 0.164705882353, 0.164705882353, /* red */
+
+ 0.552941176471, 0.788235294118, 0.32549019607,
+ 0.474509803922, 0.713725490196, 0.243137254902,
+ 0.388235294118, 0.596078431373, 0.18431372549, /* green */
+
+ 0.313725490196, 0.450980392157, 0.623529411765,
+ 0.239215686275, 0.345098039216, 0.474509803922,
+ 0.21568627451, 0.313725490196, 0.435294117647, /* blue */
+
+ 1.0, 1.0, 1.0,
+ 0.909803921569, 0.909803921569, 0.898039215686,
+ 0.701960784314, 0.701960784314, 0.670588235294, /* white */
+
+ 0.945098039216, 0.878431372549, 0.321568627451,
+ 0.929411764706, 0.839215686275, 0.113725490196,
+ 0.760784313725, 0.682352941176, 0.0274509803922, /* yellow */
+
+ 0.576470588235, 0.364705882353, 0.607843137255,
+ 0.443137254902, 0.282352941176, 0.46666666666,
+ 0.439215686275, 0.266666666667, 0.46666666666, /* purple */
+
+ 0.890196078431, 0.572549019608, 0.258823529412,
+ 0.803921568627, 0.450980392157, 0.101960784314,
+ 0.690196078431, 0.388235294118, 0.0901960784314, /* orange */
+
+ 0.392156862745, 0.392156862745, 0.392156862745,
+ 0.262745098039, 0.262745098039, 0.262745098039,
+ 0.21568627451, 0.235294117647, 0.23921568627 /* grey */
+ };
+
+ /* Layout the block */
+ draw_rounded_rectangle (cr, 0.05, 0.05, 0.9, 0.9, 0.05);
+
+ /* Draw outline */
+ cr.set_source_rgb (colors[color * 9 + 6], colors[color * 9 + 7], colors[color * 9 + 8]);
+ cr.set_line_width (0.1);
+ cr.stroke_preserve ();
+
+ /* Fill with gradient */
+ var pat = new Cairo.Pattern.linear (0.35, 0, 0.55, 0.9);
+ pat.add_color_stop_rgb (0.0, colors[color * 9], colors[color * 9 + 1], colors[color * 9 + 2]);
+ pat.add_color_stop_rgb (1.0, colors[color * 9 + 3], colors[color * 9 + 4], colors[color * 9 + 5]);
+ cr.set_source (pat);
+ cr.fill ();
+ }
+
+ private void draw_tango (Cairo.Context cr, bool use_gradients)
+ {
+ /* the following garbage is derived from the official tango style guide */
+ const double colors[72] =
+ {
+ 0.93725490196078431, 0.16078431372549021, 0.16078431372549021,
+ 0.8, 0.0, 0.0,
+ 0.64313725490196083, 0.0, 0.0, /* red */
+
+ 0.54117647058823526, 0.88627450980392153, 0.20392156862745098,
+ 0.45098039215686275, 0.82352941176470584, 0.086274509803921567,
+ 0.30588235294117649, 0.60392156862745094, 0.023529411764705882, /* green */
+
+ 0.44705882352941179, 0.62352941176470589, 0.81176470588235294,
+ 0.20392156862745098, 0.396078431372549, 0.64313725490196083,
+ 0.12549019607843137, 0.29019607843137257, 0.52941176470588236, /* blue */
+
+ 0.93333333333333335, 0.93333333333333335, 0.92549019607843142,
+ 0.82745098039215681, 0.84313725490196079, 0.81176470588235294,
+ 0.72941176470588232, 0.74117647058823533, 0.71372549019607845, /* white */
+
+ 0.9882352941176471, 0.9137254901960784, 0.30980392156862746,
+ 0.92941176470588238, 0.83137254901960789, 0.0,
+ 0.7686274509803922, 0.62745098039215685, 0.0, /* yellow */
+
+ 0.67843137254901964, 0.49803921568627452, 0.6588235294117647,
+ 0.45882352941176469, 0.31372549019607843, 0.4823529411764706,
+ 0.36078431372549019, 0.20784313725490197, 0.4, /* purple */
+
+ 0.9882352941176471, 0.68627450980392157, 0.24313725490196078,
+ 0.96078431372549022, 0.47450980392156861, 0.0,
+ 0.80784313725490198, 0.36078431372549019, 0.0, /* orange (replacing cyan) */
+
+ 0.33, 0.34, 0.32,
+ 0.18, 0.2, 0.21,
+ 0.10, 0.12, 0.13 /* grey */
+ };
+
+ if (use_gradients)
+ {
+ var pat = new Cairo.Pattern.linear (0.35, 0, 0.55, 0.9);
+ pat.add_color_stop_rgb (0.0, colors[color * 9], colors[color * 9 + 1], colors[color * 9 + 2]);
+ pat.add_color_stop_rgb (1.0, colors[color * 9 + 3], colors[color * 9 + 4], colors[color * 9 + 5]);
+ cr.set_source (pat);
+ }
+ else
+ cr.set_source_rgb (colors[color * 9], colors[color * 9 + 1], colors[color * 9 + 2]);
+
+ draw_rounded_rectangle (cr, 0.05, 0.05, 0.9, 0.9, 0.2);
+ cr.fill_preserve (); /* fill with shaded gradient */
+
+ cr.set_source_rgb (colors[color * 9 + 6], colors[color * 9 + 7], colors[color * 9 + 8]);
+
+ cr.set_line_width (0.1);
+ cr.stroke (); /* add darker outline */
+
+ draw_rounded_rectangle (cr, 0.15, 0.15, 0.7, 0.7, 0.08);
+ if (use_gradients)
+ {
+ var pat = new Cairo.Pattern.linear (-0.3, -0.3, 0.8, 0.8);
+ /* yellow and white blocks need a brighter highlight */
+ switch (color)
+ {
+ case 3:
+ case 4:
+ pat.add_color_stop_rgba (0.0, 1.0, 1.0, 1.0, 1.0);
+ pat.add_color_stop_rgba (1.0, 1.0, 1.0, 1.0, 0.0);
+ break;
+ default:
+ pat.add_color_stop_rgba (0.0, 0.9295, 0.9295, 0.9295, 1.0);
+ pat.add_color_stop_rgba (1.0, 0.9295, 0.9295, 0.9295, 0.0);
+ break;
+ }
+ cr.set_source (pat);
+ }
+ else
+ cr.set_source_rgba (1.0, 1.0, 1.0, 0.35);
+ cr.stroke (); /* add inner edge highlight */
+ }
+}
+
+public class GameView : GtkClutter.Embed
+{
+ /* Game being shown */
+ private Game game;
+
+ private Clutter.Group playing_field;
+
+ /* Overlay to draw messages on */
+ private TextOverlay text_overlay;
+
+ /* Textures used to draw blocks */
+ private BlockTexture[] block_textures;
+
+ /* Blocks */
+ private HashTable<Block, BlockActor> blocks;
+
+ // FIXME: Remove
+ private bool show_pause;
+ private bool show_game_over;
+
+ private int num_full_lines;
+
+ private int cell_size
+ {
+ get { return int.min (get_allocated_width () / COLUMNS, get_allocated_height () / LINES); }
+ }
+
+ public string theme
+ {
+ set
+ {
+ foreach (var texture in block_textures)
+ texture.theme = value;
+ }
+ }
+
+ public GameView (Game game)
+ {
+ blocks = new HashTable<Block, BlockActor> (direct_hash, direct_equal);
+
+ this.game = game;
+ game.block_added.connect (block_added_cb);
+ game.block_moved.connect (block_moved_cb);
+ game.block_destroyed.connect (block_destroyed_cb);
+
+ size_allocate.connect (size_allocate_cb);
+
+ set_size_request (COLUMNS * 190 / LINES, 190);
+
+ var stage = (Clutter.Stage) get_stage ();
+ Clutter.Color stage_color = { 0x0, 0x0, 0x0, 0xff };
+ stage.set_color (stage_color);
+
+ playing_field = new Clutter.Group ();
+ stage.add_actor (playing_field);
+
+ block_textures = new BlockTexture[NCOLORS];
+ for (var i = 0; i < block_textures.length; i++)
+ {
+ // FIXME: Have to set a size to avoid an assertion in Clutter
+ block_textures[i] = new BlockTexture (i, 1);
+ block_textures[i].hide ();
+ playing_field.add_actor (block_textures[i]);
+ }
+ }
+
+ private void block_added_cb (Block block)
+ {
+ var actor = new BlockActor (block, block_textures[block.color]);
+ actor.set_size (cell_size, cell_size);
+ actor.set_position (block.x * cell_size, block.y * cell_size);
+ playing_field.add_actor (actor);
+ blocks.insert (block, actor);
+ }
+
+ private void block_moved_cb (Block block, bool animate)
+ {
+ var actor = blocks.lookup (block);
+ if (animate)
+ actor.move (block.x * cell_size, block.y * cell_size);
+ else
+ actor.set_position (block.x * cell_size, block.y * cell_size);
+ }
+
+ private void block_destroyed_cb (Block block)
+ {
+ var actor = blocks.lookup (block);
+ actor.explode ();
+ }
+
+ private void fall_completed_cb (Clutter.Timeline timeline)
+ {
+ //After fall, start the earthquake effect
+ float x, y;
+ playing_field.get_position (out x, out y);
+ playing_field.set_position (x, y + cell_size * num_full_lines * 0.25f);
+ playing_field.animate (Clutter.AnimationMode.EASE_OUT_BOUNCE, 720 / (5 - num_full_lines), "x", x, "y", y);
+ }
+
+ public void size_allocate_cb (Gtk.Widget widget, Gtk.Allocation allocation)
+ {
+ var stage = (Clutter.Stage) get_stage ();
+
+ foreach (var texture in block_textures)
+ texture.set_size (cell_size, cell_size);
+
+ var iter = HashTableIter<Block, BlockActor> (blocks);
+ while (true)
+ {
+ Block block;
+ BlockActor actor;
+ if (!iter.next (out block, out actor))
+ break;
+ actor.set_position (block.x * cell_size, block.y * cell_size);
+ }
+
+ if (text_overlay != null)
+ text_overlay.set_size (get_allocated_width (), get_allocated_height ());
+ else
+ {
+ text_overlay = new TextOverlay (get_allocated_width (), get_allocated_height ());
+ stage.add (text_overlay);
+ }
+
+ draw_message ();
+
+ text_overlay.raise_top ();
+ var x = (int) ((get_allocated_width () - cell_size * COLUMNS) / 2);
+ var y = (int) ((get_allocated_height () - cell_size * LINES) / 2);
+ playing_field.set_position (x, y);
+ }
+
+ public void draw_message ()
+ {
+ if (show_pause)
+ text_overlay.text = _("Paused");
+ else if (show_game_over)
+ text_overlay.text = _("Game Over");
+ else
+ text_overlay.text = null;
+ }
+
+ public void show_pause_message ()
+ {
+ show_pause = true;
+ draw_message ();
+ }
+
+ public void hide_pause_message ()
+ {
+ show_pause = false;
+ draw_message ();
+ }
+
+ public void show_game_over_message ()
+ {
+ show_game_over = true;
+ draw_message ();
+ }
+
+ public void hide_game_over_message ()
+ {
+ show_game_over = false;
+ draw_message ();
+ }
+}
diff --git a/quadrapassel/src/game.vala b/quadrapassel/src/game.vala
new file mode 100644
index 0000000..bd1b35e
--- /dev/null
+++ b/quadrapassel/src/game.vala
@@ -0,0 +1,558 @@
+// FIXME: Bad globals
+int LINES = 20;
+int COLUMNS = 14;
+
+int block_rotation_next;
+int block_color_next;
+
+const int NCOLORS = 7;
+
+const int block_table[448] =
+{
+ /* *** */
+ /* * */
+ 0, 0, 0, 0,
+ 1, 1, 1, 0,
+ 1, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ 0, 1, 0, 0,
+ 0, 1, 0, 0,
+ 0, 1, 1, 0,
+ 0, 0, 0, 0,
+
+ 0, 0, 1, 0,
+ 1, 1, 1, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ 1, 1, 0, 0,
+ 0, 1, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 0, 0,
+
+ /* *** */
+ /* * */
+ 0, 0, 0, 0,
+ 1, 1, 1, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 0,
+
+ 0, 1, 1, 0,
+ 0, 1, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 0, 0,
+
+ 1, 0, 0, 0,
+ 1, 1, 1, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ 0, 1, 0, 0,
+ 0, 1, 0, 0,
+ 1, 1, 0, 0,
+ 0, 0, 0, 0,
+
+ /* *** */
+ /* * */
+ 0, 0, 0, 0,
+ 1, 1, 1, 0,
+ 0, 1, 0, 0,
+ 0, 0, 0, 0,
+
+ 0, 1, 0, 0,
+ 0, 1, 1, 0,
+ 0, 1, 0, 0,
+ 0, 0, 0, 0,
+
+ 0, 1, 0, 0,
+ 1, 1, 1, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ 0, 1, 0, 0,
+ 1, 1, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 0, 0,
+
+ /* ** */
+ /* ** */
+
+ 0, 0, 0, 0,
+ 0, 1, 1, 0,
+ 1, 1, 0, 0,
+ 0, 0, 0, 0,
+
+ 0, 1, 0, 0,
+ 0, 1, 1, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 0,
+
+ 0, 0, 0, 0,
+ 0, 1, 1, 0,
+ 1, 1, 0, 0,
+ 0, 0, 0, 0,
+
+ 0, 1, 0, 0,
+ 0, 1, 1, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 0,
+
+ /* ** */
+ /* ** */
+
+ 0, 0, 0, 0,
+ 1, 1, 0, 0,
+ 0, 1, 1, 0,
+ 0, 0, 0, 0,
+
+ 0, 0, 1, 0,
+ 0, 1, 1, 0,
+ 0, 1, 0, 0,
+ 0, 0, 0, 0,
+
+ 0, 0, 0, 0,
+ 1, 1, 0, 0,
+ 0, 1, 1, 0,
+ 0, 0, 0, 0,
+
+ 0, 0, 1, 0,
+ 0, 1, 1, 0,
+ 0, 1, 0, 0,
+ 0, 0, 0, 0,
+
+ /* **** */
+ 0, 0, 0, 0,
+ 1, 1, 1, 1,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ 0, 1, 0, 0,
+ 0, 1, 0, 0,
+ 0, 1, 0, 0,
+ 0, 1, 0, 0,
+
+ 0, 0, 0, 0,
+ 1, 1, 1, 1,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ 0, 1, 0, 0,
+ 0, 1, 0, 0,
+ 0, 1, 0, 0,
+ 0, 1, 0, 0,
+
+ /* ** */
+ /* ** */
+ 0, 0, 0, 0,
+ 0, 1, 1, 0,
+ 0, 1, 1, 0,
+ 0, 0, 0, 0,
+
+ 0, 0, 0, 0,
+ 0, 1, 1, 0,
+ 0, 1, 1, 0,
+ 0, 0, 0, 0,
+
+ 0, 0, 0, 0,
+ 0, 1, 1, 0,
+ 0, 1, 1, 0,
+ 0, 0, 0, 0,
+
+ 0, 0, 0, 0,
+ 0, 1, 1, 0,
+ 0, 1, 1, 0,
+ 0, 0, 0, 0
+};
+
+public class Block
+{
+ public int x;
+ public int y;
+ public int color;
+}
+
+public class Game
+{
+ private Block[,] blocks;
+ private List<Block> shape = null;
+
+ /* Position of current block */
+ private int block_x;
+ private int block_y;
+
+ private int block_color;
+
+ /* Rotation of block */
+ private int block_rotation;
+
+ /* Timer to animate */
+ private uint drop_timeout = 0;
+
+ public signal void block_added (Block block);
+ public signal void block_moved (Block block, bool animate);
+ public signal void block_destroyed (Block block);
+
+ public Game (int lines = 20, int columns = 14)
+ {
+ blocks = new Block[columns, lines];
+ }
+
+ public void start (int filled_lines = 0, int fill_prob = 5)
+ {
+ /* Start with some pre-filled spaces */
+ for (var y = 0; y < LINES; y++)
+ {
+ /* Pick at least one column to be empty */
+ var blank = Random.int_range (0, COLUMNS);
+
+ for (var x = 0; x < COLUMNS; x++)
+ {
+ if (y >= (LINES - filled_lines) && x != blank && Random.int_range (0, 10) < fill_prob)
+ {
+ blocks[x, y] = new Block ();
+ blocks[x, y].color = Random.int_range (0, NCOLORS);
+ }
+ else
+ blocks[x, y] = null;
+ }
+ }
+
+ pick_next_shape ();
+ add_shape ();
+
+ set_timestep (1);
+ }
+
+ public bool move_left ()
+ {
+ return move_shape (-1, 0, 0);
+ }
+
+ public bool move_right ()
+ {
+ return move_shape (1, 0, 0);
+ }
+
+ public bool rotate_left ()
+ {
+ return move_shape (0, 0, -1);
+ }
+
+ public bool rotate_right ()
+ {
+ return move_shape (0, 0, 1);
+ }
+
+ public void fast_forward (bool enable)
+ {
+ if (enable)
+ set_timestep (-1);
+ else
+ set_timestep (1);
+ }
+
+ public void drop ()
+ {
+ while (move_shape (0, 1, 0));
+ fall_timeout_cb ();
+ }
+
+ private void set_timestep (int level)
+ {
+ var timestep = 0;
+ if (level < 0)
+ timestep = 40;
+ else
+ timestep = (int) Math.round (80 + 800.0 * Math.pow (0.75, level - 1));
+ timestep = int.max (10, timestep);
+
+ if (drop_timeout != 0)
+ Source.remove (drop_timeout);
+ drop_timeout = Timeout.add (timestep, fall_timeout_cb);
+ }
+
+ private bool fall_timeout_cb ()
+ {
+ /* Drop the shape down, and create a new one when it can't move */
+ if (!move_shape (0, 1, 0))
+ {
+ destroy_lines ();
+ add_shape ();
+ }
+
+ return true;
+ }
+
+ private void add_shape ()
+ {
+ block_rotation = block_rotation_next;
+ block_color = block_color_next;
+
+ pick_next_shape ();
+
+ /* Place this block at top of the field */
+ var offset = block_color * 64 + block_rotation * 16;
+ shape = null;
+ var min_width = 4, max_width = 0, min_height = 4, max_height = 0;
+ for (var x = 0; x < 4; x++)
+ {
+ for (var y = 0; y < 4; y++)
+ {
+ if (block_table[offset + y * 4 + x] != 0)
+ {
+ min_width = int.min (x, min_width);
+ max_width = int.max (x + 1, max_width);
+ min_height = int.min (y, min_height);
+ max_height = int.max (y + 1, max_height);
+ var b = new Block ();
+ b.color = block_color;
+ b.x = x;
+ b.y = y;
+ shape.append (b);
+ }
+ }
+ }
+ block_x = (COLUMNS - max_width) / 2;
+ block_y = -min_height;
+ foreach (var b in shape)
+ {
+ b.x += block_x;
+ b.y += block_y;
+
+ /* Abort if can't place there */
+ if (blocks[b.x, b.y] != null)
+ {
+ shape = null;
+ return;
+ }
+ }
+
+ foreach (var b in shape)
+ {
+ blocks[b.x, b.y] = b;
+ block_added (b);
+ }
+ }
+
+ private void pick_next_shape ()
+ {
+ /*if (difficult_blocks)
+ {
+ bastard_pick ();
+ block_color_next = -1;
+ }
+ else*/
+
+ block_rotation_next = Random.int_range (0, 4);
+ block_color_next = Random.int_range (0, NCOLORS);
+ }
+
+ private void destroy_lines ()
+ {
+ var fall_distance = 0;
+ for (var y = blocks.length[1] - 1; y >= 0; y--)
+ {
+ var explode = true;
+ for (var x = 0; x < blocks.length[0]; x++)
+ {
+ if (blocks[x, y] == null)
+ {
+ explode = false;
+ break;
+ }
+ }
+
+ /* Either destroy this line or drop it down */
+ if (explode)
+ {
+ fall_distance++;
+ for (var x = 0; x < blocks.length[0]; x++)
+ {
+ block_destroyed (blocks[x, y]);
+ blocks[x, y] = null;
+ }
+ }
+ else if (fall_distance > 0)
+ {
+ for (var x = 0; x < blocks.length[0]; x++)
+ {
+ var b = blocks[x, y];
+ if (b != null)
+ {
+ b.y += fall_distance;
+ blocks[b.x, b.y] = b;
+ blocks[x, y] = null;
+ block_moved (b, true);
+ }
+ }
+ }
+ }
+ }
+
+#if 0
+ /*
+ * An implementation of "Bastard" algorithm
+ * it comes from Federico Poloni's "bastet"
+ */
+ private void bastard_pick ()
+ {
+ var scores = new int[NCOLORS];
+ var blocks = new int[NCOLORS];
+ var chance = new int[NCOLORS];
+
+ animate = false;
+ /* This generates a priority for each block */
+ save_field ();
+ for (block_index = 0; block_index < NCOLORS; block_index++)
+ {
+ scores[block_index] = -32000;
+ for (block_rotation = 0; block_rotation < 4; block_rotation++)
+ {
+ for (block_x = 0; block_x < COLUMNS; block_x++)
+ {
+ int this_score = 0;
+ int x, y;
+
+ if (!block_ok_here (block_x, block_y = 0, block_index, block_rotation))
+ continue;
+
+ drop_block ();
+
+ /* Count the completed lines */
+ for (y = int.min (block_y + 4, LINES); y > 0; --y)
+ if (check_full_line (y))
+ this_score += 5000;
+
+ /* Count heights of columns */
+ for (x = 0; x < COLUMNS; x++)
+ {
+ for (y = 0; y < LINES; y++)
+ if (blocks[x, y].what == SlotType.LAYING)
+ break;
+ this_score -= 5 * (LINES - y);
+ }
+
+ restore_field ();
+ if (scores[block_index] < this_score)
+ scores[block_index] = this_score;
+ }
+ }
+ }
+
+ for (var i = 0; i < NCOLORS; i++)
+ {
+ /* Initialize chances table */
+ chance[i] = 100;
+ /* Initialize block/priority table */
+ blocks[i] = i;
+ /* Perturb score (-2 to +2), to avoid stupid tie handling */
+ scores[i] += Random.int_range (-2, 2);
+ }
+
+ /* Sorts blocks by priorities, worst (interesting to us) first*/
+ for (var i = 0; i < NCOLORS; i++)
+ {
+ for (var ii = 0; ii < NCOLORS - 1; ii++)
+ {
+ if (scores[blocks[ii]] > scores[blocks[ii+1]])
+ {
+ int t = blocks[ii];
+ blocks[ii] = blocks[ii+1];
+ blocks[ii+1] = t;
+ }
+ }
+ }
+
+ /* Lower the chances we're giving the worst one */
+ chance[0] = 75;
+ chance[1] = 92;
+ chance[2] = 98;
+
+ /* Actually choose a piece */
+ int rnd = Random.int_range (0, 99);
+ for (var i = 0; i < NCOLORS; i++)
+ {
+ block_index = blocks[i];
+ if (rnd < chance[i])
+ break;
+ }
+
+ /* This will almost certainly not given next */
+ animate = true;
+ }
+#endif
+
+ private bool move_shape (int x_step, int y_step, int r_step)
+ {
+ /* Lift up block and rotate it */
+ foreach (var b in shape)
+ blocks[b.x, b.y] = null;
+ rotate_shape (r_step);
+
+ /* Check it can fit into the new location */
+ var can_move = true;
+ foreach (var b in shape)
+ {
+ var x = b.x + x_step;
+ var y = b.y + y_step;
+ if (x < 0 || x >= COLUMNS || y >= LINES || blocks[x, y] != null)
+ {
+ can_move = false;
+ break;
+ }
+ }
+
+ /* Place in the new location or put it back where it was */
+ if (can_move)
+ {
+ foreach (var b in shape)
+ {
+ b.x += x_step;
+ b.y += y_step;
+ blocks[b.x, b.y] = b;
+ var animate = x_step != 0 || y_step != 0;
+ block_moved (b, animate);
+ }
+ block_x += x_step;
+ block_y += y_step;
+ }
+ else
+ {
+ rotate_shape (-r_step);
+ foreach (var b in shape)
+ blocks[b.x, b.y] = b;
+ }
+
+ return can_move;
+ }
+
+ private void rotate_shape (int r_step)
+ {
+ var r = block_rotation + r_step;
+ if (r < 0)
+ r += 4;
+ if (r >= 4)
+ r -= 4;
+
+ if (r == block_rotation)
+ return;
+ block_rotation = r;
+
+ /* Rearrange current blocks */
+ unowned List<Block> b = shape;
+ var offset = block_color * 64 + r * 16;
+ for (var x = 0; x < 4; x++)
+ {
+ for (var y = 0; y < 4; y++)
+ {
+ if (block_table[offset + y * 4 + x] != 0)
+ {
+ b.data.x = block_x + x;
+ b.data.y = block_y + y;
+ b = b.next;
+ }
+ }
+ }
+ }
+}
diff --git a/quadrapassel/src/preview.vala b/quadrapassel/src/preview.vala
new file mode 100644
index 0000000..992e7b3
--- /dev/null
+++ b/quadrapassel/src/preview.vala
@@ -0,0 +1,107 @@
+public class Preview : GtkClutter.Embed
+{
+ /* Textures used to draw blocks */
+ private BlockTexture[] block_textures;
+
+ /* Blocks being shown */
+ private const int PREVIEW_WIDTH = 4;
+ private const int PREVIEW_HEIGHT = 4;
+ private Clutter.Clone[,] blocks;
+
+ /* The block being previewed */
+ private int blocknr = -1;
+
+ /* The color of the block */
+ private int color;
+
+ /* Clutter representation of a piece */
+ private Clutter.Group piece;
+
+ public string theme
+ {
+ set
+ {
+ foreach (var texture in block_textures)
+ texture.theme = value;
+ set_block (blocknr, color, true);
+ }
+ }
+
+ private int cell_size
+ {
+ get { return (get_allocated_width () + get_allocated_height ()) / 2 / 5; }
+ }
+
+ public Preview ()
+ {
+ blocks = new Clutter.Clone[PREVIEW_WIDTH, PREVIEW_HEIGHT];
+
+ size_allocate.connect (size_allocate_cb);
+
+ /* FIXME: We should scale with the rest of the UI, but that requires
+ * changes to the widget layout - i.e. wrap the preview in an
+ * fixed-aspect box. */
+ set_size_request (120, 120);
+ var stage = (Clutter.Stage) get_stage ();
+
+ Clutter.Color stage_color = { 0x0, 0x0, 0x0, 0xff };
+ stage.set_color (stage_color);
+ piece = new Clutter.Group ();
+ stage.add (piece);
+
+ block_textures = new BlockTexture[NCOLORS];
+ for (var i = 0; i < block_textures.length; i++)
+ {
+ // FIXME: Have to set a size to avoid an assertion in Clutter
+ block_textures[i] = new BlockTexture (i, 1);
+ block_textures[i].hide ();
+ piece.add_actor (block_textures[i]);
+ }
+ }
+
+ public void set_block (int bnr, int bcol, bool force)
+ {
+ blocknr = bnr;
+ color = bcol;
+
+ // FIXME: Move logic outside this class
+ var disable = false;
+ if (!force && (!do_preview || difficult_blocks))
+ disable = true;
+
+ var min_width = 4, max_width = 0, min_height = 4, max_height = 0;
+ for (var x = 0; x < PREVIEW_WIDTH; x++)
+ {
+ for (var y = 0; y < PREVIEW_HEIGHT; y++)
+ {
+ if (blocks[x, y] != null)
+ blocks[x, y].destroy ();
+ blocks[x, y] = null;
+ if (!disable && blocknr != -1 && block_table[blocknr * 64 + block_rotation_next * 16 + y * 4 + x] != 0)
+ {
+ min_width = int.min (x, min_width);
+ max_width = int.max (x + 1, max_width);
+ min_height = int.min (y, min_height);
+ max_height = int.max (y + 1, max_height);
+
+ blocks[x, y] = new Clutter.Clone (block_textures[color]);
+ blocks[x, y].set_size (cell_size, cell_size);
+ blocks[x, y].set_position (x * cell_size, y * cell_size);
+ piece.add_actor (blocks[x, y]);
+ }
+ }
+ }
+
+ piece.set_anchor_point ((min_width + max_width) * 0.5f * cell_size, (min_height + max_height) * 0.5f * cell_size);
+ piece.set_position (get_allocated_width () / 2, get_allocated_height () / 2);
+ piece.set_scale (0.6, 0.6);
+ piece.animate (Clutter.AnimationMode.EASE_IN_OUT_SINE, 180, "scale-x", 1.0, "scale-y", 1.0);
+ }
+
+ private void size_allocate_cb (Gtk.Allocation allocation)
+ {
+ foreach (var texture in block_textures)
+ texture.set_size (cell_size, cell_size);
+ set_block (blocknr, color, true);
+ }
+}
diff --git a/quadrapassel/src/quadrapassel.vala b/quadrapassel/src/quadrapassel.vala
new file mode 100644
index 0000000..cd370fe
--- /dev/null
+++ b/quadrapassel/src/quadrapassel.vala
@@ -0,0 +1,762 @@
+bool difficult_blocks = false;
+bool do_preview = true;
+
+public class Quadrapassel
+{
+ /* Application settings */
+ private Settings settings;
+
+ /* Main window */
+ private Gtk.Window w;
+
+ /* Game being played */
+ private Game game;
+
+ /* Rendering of game */
+ private GameView view;
+
+ /* Preview of the next shape */
+ private Preview preview;
+
+ private ScoreFrame score_frame;
+ private GnomeGamesSupport.Scores high_scores;
+
+ private bool paused;
+ private bool one_pause;
+
+ private bool in_play;
+
+ private const int TILE_THRESHOLD = 65;
+
+ private const int DEFAULT_WIDTH = 500;
+ private const int DEFAULT_HEIGHT = 550;
+
+ private Gtk.Dialog preferences_dialog;
+ private Gtk.SpinButton starting_level_spin;
+ private Preview theme_preview;
+ private int starting_level;
+ private static int cmdline_level = 0;
+
+ private int line_fill_height;
+ private int line_fill_prob;
+
+ private Gtk.SpinButton fill_height_spinner;
+ private Gtk.SpinButton fill_prob_spinner;
+ private Gtk.CheckButton do_preview_toggle;
+ private Gtk.CheckButton difficult_blocks_toggle;
+ private Gtk.CheckButton rotate_counter_clock_wise_toggle;
+ private Gtk.CheckButton use_target_toggle;
+ private Gtk.CheckButton sound_toggle;
+
+ private int move_left;
+ private int move_right;
+ private int move_down;
+ private int move_drop;
+ private int move_rotate;
+ private int move_pause;
+
+ private Gtk.Action new_game_action;
+ private GnomeGamesSupport.PauseAction pause_action;
+ private Gtk.Action scores_action;
+ private Gtk.Action preferences_action;
+
+ private bool fast_fall;
+
+ private const Gtk.ActionEntry actions[] =
+ {
+ { "GameMenu", null, N_("_Game") },
+ { "SettingsMenu", null, N_("_Settings") },
+ { "HelpMenu", null, N_("_Help") },
+ { "NewGame", GnomeGamesSupport.STOCK_NEW_GAME, null, null, null, new_game_cb },
+ { "Scores", GnomeGamesSupport.STOCK_SCORES, null, null, null, scores_cb },
+ { "Quit", Gtk.Stock.QUIT, null, null, null, quit_cb },
+ { "Preferences", Gtk.Stock.PREFERENCES, null, null, null, preferences_cb },
+ { "Contents", GnomeGamesSupport.STOCK_CONTENTS, null, null, null, help_cb },
+ { "About", Gtk.Stock.ABOUT, null, null, null, about_cb }
+ };
+
+ public Quadrapassel (int cmd_level)
+ {
+ var ui_description =
+ "<ui>" +
+ " <menubar name='MainMenu'>" +
+ " <menu action='GameMenu'>" +
+ " <menuitem action='NewGame'/>" +
+ " <menuitem action='_pause'/>" +
+ " <separator/>" +
+ " <menuitem action='Scores'/>" +
+ " <separator/>" +
+ " <menuitem action='Quit'/>" +
+ " </menu>" +
+ " <menu action='SettingsMenu'>" +
+ " <menuitem action='Preferences'/>" +
+ " </menu>" +
+ " <menu action='HelpMenu'>" +
+ " <menuitem action='Contents'/>" +
+ " <menuitem action='About'/>" +
+ " </menu>" +
+ " </menubar>" +
+ "</ui>";
+
+ settings = new Settings ("org.gnome.quadrapassel");
+
+ w = new Gtk.Window (Gtk.WindowType.TOPLEVEL);
+ w.set_title (_("Quadrapassel"));
+
+ w.delete_event.connect (window_delete_event_cb);
+
+ line_fill_height = 0;
+ line_fill_prob = 5;
+
+ w.set_default_size (DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ //games_conf_add_window (w, KEY_SAVED_GROUP);
+
+ game = new Game ();
+
+ preview = new Preview ();
+ view = new GameView (game);
+
+ init_options ();
+
+ /* prepare menus */
+ GnomeGamesSupport.stock_init ();
+ var action_group = new Gtk.ActionGroup ("MenuActions");
+ action_group.set_translation_domain (GETTEXT_PACKAGE);
+ action_group.add_actions (actions, this);
+ var ui_manager = new Gtk.UIManager ();
+ ui_manager.insert_action_group (action_group, 0);
+ try
+ {
+ ui_manager.add_ui_from_string (ui_description, -1);
+ }
+ catch (Error e)
+ {
+ }
+ w.add_accel_group (ui_manager.get_accel_group ());
+
+ new_game_action = action_group.get_action ("NewGame");
+ pause_action = new GnomeGamesSupport.PauseAction ("_pause");
+ pause_action.state_changed.connect (pause_cb);
+ action_group.add_action_with_accel (pause_action, null);
+ scores_action = action_group.get_action ("Scores");
+ preferences_action = action_group.get_action ("Preferences");
+
+ var menubar = ui_manager.get_widget ("/MainMenu");
+
+ var hb = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
+
+ var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+ w.add (vbox);
+ vbox.pack_start (menubar, false, false, 0);
+ vbox.pack_start (hb, true, true, 0);
+
+ var aspect_frame = new Gtk.AspectFrame ("", 0.5f, 0.5f, (float) COLUMNS / (float) LINES, false);
+ aspect_frame.set_shadow_type (Gtk.ShadowType.NONE);
+ aspect_frame.add (view);
+
+ w.set_events (w.get_events () | Gdk.EventMask.KEY_PRESS_MASK | Gdk.EventMask.KEY_RELEASE_MASK);
+
+ var vb1 = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+ vb1.set_border_width (10);
+ vb1.pack_start (aspect_frame, true, true, 0);
+ hb.pack_start (vb1, true, true, 0);
+
+ w.key_press_event.connect (key_press_event_cb);
+ w.key_release_event.connect (key_release_event_cb);
+
+ var vb2 = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+ vb2.set_border_width (10);
+ hb.pack_end (vb2, false, false, 0);
+
+ vb2.pack_start (preview, false, false, 0);
+
+ if (cmdline_level <= 0)
+ cmdline_level = settings.get_int ("starting-level");
+ score_frame = new ScoreFrame (cmdline_level);
+
+ vb2.pack_end (score_frame.get_widget (), true, false, 0);
+ high_scores = new GnomeGamesSupport.Scores ("quadrapassel",
+ new GnomeGamesSupport.ScoresCategory[0],
+ null,
+ null,
+ 0,
+ GnomeGamesSupport.ScoreStyle.PLAIN_DESCENDING);
+
+ score_frame.set_level (starting_level);
+ score_frame.set_starting_level (starting_level);
+
+ pause_action.set_sensitive (false);
+ preferences_action.set_sensitive (true);
+ }
+
+ public void show ()
+ {
+ w.show_all ();
+ }
+
+ private void preferences_dialog_close_cb (Gtk.Widget widget)
+ {
+ preferences_dialog.destroy ();
+ preferences_dialog = null;
+ new_game_action.sensitive = true;
+ }
+
+ private void preferences_dialog_response_cb (Gtk.Widget dialog, int response_id)
+ {
+ preferences_dialog_close_cb (preferences_dialog);
+ }
+
+ private void init_options ()
+ {
+ view.theme = settings.get_string ("theme");
+ preview.theme = settings.get_string ("theme");
+
+ starting_level = settings.get_int ("starting-level");
+ if (starting_level < 1)
+ starting_level = 1;
+ if (starting_level > 20)
+ starting_level = 20;
+
+ sound_enable (settings.get_boolean ("sound"));
+
+ do_preview = settings.get_boolean ("do-preview");
+
+ difficult_blocks = settings.get_boolean ("pick-difficult-blocks");
+
+ line_fill_height = settings.get_int ("line-fill-height");
+ if (line_fill_height < 0)
+ line_fill_height = 0;
+ if (line_fill_height > 19)
+ line_fill_height = 19;
+
+ line_fill_prob = settings.get_int ("line-fill-probability");
+ if (line_fill_prob < 0)
+ line_fill_prob = 0;
+ if (line_fill_prob > 10)
+ line_fill_prob = 10;
+
+ move_left = settings.get_int ("key-left");
+ move_right = settings.get_int ("key-right");
+ move_down = settings.get_int ("key-down");
+ move_drop = settings.get_int ("key-drop");
+ move_rotate = settings.get_int ("key-rotate");
+ move_pause = settings.get_int ("key-pause");
+ }
+
+ private void preferences_cb (Gtk.Action action)
+ {
+ if (preferences_dialog != null)
+ {
+ preferences_dialog.present ();
+ return;
+ }
+
+ /* create the dialog */
+ preferences_dialog = new Gtk.Dialog.with_buttons (_("Quadrapassel Preferences"), w, (Gtk.DialogFlags)0, Gtk.Stock.CLOSE, Gtk.ResponseType.CLOSE, null);
+ preferences_dialog.set_border_width (5);
+ var vbox = (Gtk.Box) preferences_dialog.get_content_area ();
+ vbox.set_spacing (2);
+ preferences_dialog.close.connect (preferences_dialog_close_cb);
+ preferences_dialog.response.connect (preferences_dialog_response_cb);
+
+ var notebook = new Gtk.Notebook ();
+ notebook.set_border_width (5);
+ vbox.pack_start (notebook, true, true, 0);
+
+ /* game page */
+ vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 18);
+ vbox.set_border_width (12);
+ var label = new Gtk.Label (_("Game"));
+ notebook.append_page (vbox, label);
+
+ var frame = new GnomeGamesSupport.Frame (_("Setup"));
+ var grid = new Gtk.Grid ();
+ grid.set_row_spacing (6);
+ grid.set_column_spacing (12);
+
+ /* pre-filled rows */
+ label = new Gtk.Label.with_mnemonic (_("_Number of pre-filled rows:"));
+ label.set_alignment (0, 0.5f);
+ label.set_hexpand (true);
+ grid.attach (label, 0, 0, 1, 1);
+
+ var adj = new Gtk.Adjustment (line_fill_height, 0, LINES-1, 1, 5, 0);
+ fill_height_spinner = new Gtk.SpinButton (adj, 10, 0);
+ fill_height_spinner.set_update_policy (Gtk.SpinButtonUpdatePolicy.ALWAYS);
+ fill_height_spinner.set_snap_to_ticks (true);
+ fill_height_spinner.value_changed.connect (fill_height_spinner_value_changed_cb);
+ grid.attach (fill_height_spinner, 1, 0, 2, 1);
+ label.set_mnemonic_widget (fill_height_spinner);
+
+ /* pre-filled rows density */
+ label = new Gtk.Label.with_mnemonic (_("_Density of blocks in a pre-filled row:"));
+ label.set_alignment (0, 0.5f);
+ label.set_hexpand (true);
+ grid.attach (label, 0, 1, 1, 1);
+
+ adj = new Gtk.Adjustment (line_fill_prob, 0, 10, 1, 5, 0);
+ fill_prob_spinner = new Gtk.SpinButton (adj, 10, 0);
+ fill_prob_spinner.set_update_policy (Gtk.SpinButtonUpdatePolicy.ALWAYS);
+ fill_prob_spinner.set_snap_to_ticks (true);
+ fill_prob_spinner.value_changed.connect (fill_prob_spinner_value_changed_cb);
+ grid.attach (fill_prob_spinner, 1, 1, 1, 1);
+ label.set_mnemonic_widget (fill_prob_spinner);
+
+ /* starting level */
+ label = new Gtk.Label.with_mnemonic (_("_Starting level:"));
+ label.set_alignment (0, 0.5f);
+ label.set_hexpand (true);
+ grid.attach (label, 0, 2, 1, 1);
+
+ adj = new Gtk.Adjustment (starting_level, 1, 20, 1, 5, 0);
+ starting_level_spin = new Gtk.SpinButton (adj, 10.0, 0);
+ starting_level_spin.set_update_policy (Gtk.SpinButtonUpdatePolicy.ALWAYS);
+ starting_level_spin.set_snap_to_ticks (true);
+ starting_level_spin.value_changed.connect (starting_level_value_changed_cb);
+ grid.attach (starting_level_spin, 1, 2, 1, 1);
+ label.set_mnemonic_widget (starting_level_spin);
+
+ frame.add (grid);
+ vbox.pack_start (frame, false, false, 0);
+
+ frame = new GnomeGamesSupport.Frame (_("Operation"));
+ var fvbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
+
+ /* sound */
+ sound_toggle = new Gtk.CheckButton.with_mnemonic (_("_Enable sounds"));
+ sound_toggle.set_active (sound_is_enabled ());
+ sound_toggle.toggled.connect (sound_toggle_toggled_cb);
+ fvbox.pack_start (sound_toggle, false, false, 0);
+
+ /* preview next block */
+ do_preview_toggle = new Gtk.CheckButton.with_mnemonic (_("_Preview next block"));
+ do_preview_toggle.set_active (do_preview);
+ do_preview_toggle.toggled.connect (do_preview_toggle_toggled_cb);
+ fvbox.pack_start (do_preview_toggle, false, false, 0);
+
+ /* bastard mode */
+ difficult_blocks_toggle = new Gtk.CheckButton.with_mnemonic (_("Choose difficult _blocks"));
+ difficult_blocks_toggle.set_active (difficult_blocks);
+ difficult_blocks_toggle.toggled.connect (difficult_blocks_toggled_cb);
+ fvbox.pack_start (difficult_blocks_toggle, false, false, 0);
+
+ /* If bastard mode is active then disable the preview option
+ to indicate that it is unavailable in bastard mode */
+ if (difficult_blocks_toggle.get_active ())
+ do_preview_toggle.set_sensitive (false);
+
+ /* rotate counter clock wise */
+ rotate_counter_clock_wise_toggle = new Gtk.CheckButton.with_mnemonic (_("_Rotate blocks counterclockwise"));
+ rotate_counter_clock_wise_toggle.set_active (settings.get_boolean ("rotate-counter-clock-wise"));
+ rotate_counter_clock_wise_toggle.toggled.connect (set_rotate_counter_clock_wise);
+ fvbox.pack_start (rotate_counter_clock_wise_toggle, false, false, 0);
+
+ use_target_toggle = new Gtk.CheckButton.with_mnemonic (_("Show _where the block will land"));
+ fvbox.pack_start (use_target_toggle, false, false, 0);
+
+ frame.add (fvbox);
+ vbox.pack_start (frame, false, false, 0);
+
+ frame = new GnomeGamesSupport.Frame (_("Theme"));
+ grid = new Gtk.Grid ();
+ grid.set_border_width (0);
+ grid.set_row_spacing (6);
+ grid.set_column_spacing (12);
+
+ /* controls page */
+ vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+ vbox.set_border_width (12);
+ label = new Gtk.Label (_("Controls"));
+ notebook.append_page (vbox, label);
+
+ frame = new GnomeGamesSupport.Frame (_("Keyboard Controls"));
+ vbox.pack_start (frame, true, true, 0);
+
+ fvbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
+ frame.add (fvbox);
+
+ var controls_list = new GnomeGamesSupport.ControlsList (settings);
+ controls_list.add_controls ("key-left", _("Move left"), 0,
+ "key-right", _("Move right"), 0,
+ "key-down", _("Move down"), 0,
+ "key-drop", _("Drop"), 0,
+ "key-rotate", _("Rotate"), 0,
+ "key-pause", _("_pause"), 0,
+ null);
+
+ fvbox.pack_start (controls_list, true, true, 0);
+
+ /* theme page */
+ vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+ vbox.set_border_width (12);
+ label = new Gtk.Label (_("Theme"));
+ notebook.append_page (vbox, label);
+
+ frame = new GnomeGamesSupport.Frame (_("Block Style"));
+ vbox.pack_start (frame, true, true, 0);
+
+ fvbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
+ frame.add (fvbox);
+
+ var theme_combo = new Gtk.ComboBox ();
+ var theme_store = new Gtk.ListStore (2, typeof (string), typeof (string));
+ theme_combo.model = theme_store;
+ var renderer = new Gtk.CellRendererText ();
+ theme_combo.pack_start (renderer, true);
+ theme_combo.add_attribute (renderer, "text", 0);
+
+ Gtk.TreeIter iter;
+
+ theme_store.append (out iter);
+ theme_store.set (iter, 0, _("Plain"), 1, "plain", -1);
+ if (settings.get_string ("theme") == "plain")
+ theme_combo.set_active_iter (iter);
+
+ theme_store.append (out iter);
+ theme_store.set (iter, 0, _("Tango Flat"), 1, "tangoflat", -1);
+ if (settings.get_string ("theme") == "tangoflat")
+ theme_combo.set_active_iter (iter);
+
+ theme_store.append (out iter);
+ theme_store.set (iter, 0, _("Tango Shaded"), 1, "tangoshaded", -1);
+ if (settings.get_string ("theme") == "tangoshaded")
+ theme_combo.set_active_iter (iter);
+
+ theme_store.append (out iter);
+ theme_store.set (iter, 0, _("Clean"), 1, "clean", -1);
+ if (settings.get_string ("theme") == "clean")
+ theme_combo.set_active_iter (iter);
+
+ theme_combo.changed.connect (theme_combo_changed_cb);
+ fvbox.pack_start (theme_combo, false, false, 0);
+
+ theme_preview = new Preview ();
+ theme_preview.theme = settings.get_string ("theme");
+ fvbox.pack_start (theme_preview, true, true, 0);
+
+ theme_preview.set_block (4, 0, true);
+
+ preferences_dialog.show_all ();
+ new_game_action.sensitive = false;
+ }
+
+ private void sound_toggle_toggled_cb ()
+ {
+ settings.set_boolean ("sound", sound_toggle.get_active ());
+ }
+
+ private void do_preview_toggle_toggled_cb ()
+ {
+ settings.set_boolean ("do-preview", do_preview_toggle.get_active ());
+ }
+
+ private void difficult_blocks_toggled_cb ()
+ {
+ settings.set_boolean ("pick-difficult-blocks", difficult_blocks_toggle.get_active ());
+
+ /* Disable the preview option to indicate that it is unavailable in bastard mode */
+ do_preview_toggle.set_sensitive (difficult_blocks_toggle.get_active () ? false : true);
+ }
+
+ private void set_rotate_counter_clock_wise ()
+ {
+ settings.set_boolean ("rotate-counter-clock-wise", rotate_counter_clock_wise_toggle.get_active ());
+ }
+
+ private void theme_combo_changed_cb (Gtk.ComboBox widget)
+ {
+ Gtk.TreeIter iter;
+ widget.get_active_iter (out iter);
+ string theme;
+ widget.model.get (iter, 1, out theme);
+ view.theme = theme;
+ preview.theme = theme;
+ if (theme_preview != null)
+ theme_preview.theme = theme;
+ settings.set_string ("theme", theme);
+ }
+
+ private void fill_height_spinner_value_changed_cb (Gtk.SpinButton spin)
+ {
+ int value = spin.get_value_as_int ();
+ settings.set_int ("line-fill-height", value);
+ }
+
+ private void fill_prob_spinner_value_changed_cb (Gtk.SpinButton spin)
+ {
+ int value = spin.get_value_as_int ();
+ settings.set_int ("line-fill-probability", value);
+ }
+
+ private void starting_level_value_changed_cb (Gtk.SpinButton spin)
+ {
+ int value = spin.get_value_as_int ();
+ settings.set_int ("starting-level", value);
+ }
+
+ private void pause_cb ()
+ {
+ toggle_pause ();
+ }
+
+ private bool window_delete_event_cb (Gtk.Widget window, Gdk.EventAny event)
+ {
+ quit ();
+ return true;
+ }
+
+ private void quit_cb (Gtk.Action action)
+ {
+ quit ();
+ }
+
+ private void quit ()
+ {
+ /* Record the score if the game isn't over. */
+ if (in_play && score_frame.getScore () > 0)
+ high_scores.add_plain_score (score_frame.getScore ());
+
+ Gtk.main_quit ();
+ }
+
+#if 0
+ private void manage_fallen ()
+ {
+ sound_play ("land");
+
+ var level_before = score_frame.get_level ();
+
+ int level_after = score_frame.score_lines (game.check_full_lines ());
+ if (level_after != level_before)
+ sound_play ("quadrapassel");
+ if (level_before != level_after || fast_fall)
+ generate_timer (level_after);
+
+ if (game.is_field_empty ())
+ score_frame.score_last_line_bonus ();
+
+ generate ();
+ }
+#endif
+
+ private bool key_press_event_cb (Gtk.Widget widget, Gdk.EventKey event)
+ {
+ var keyval = upper_key (event.keyval);
+
+ if (keyval == upper_key (move_pause))
+ {
+ toggle_pause ();
+ return true;
+ }
+
+ if (paused)
+ return false;
+
+ if (keyval == upper_key (move_left))
+ {
+ if (game.move_left ())
+ sound_play ("slide");
+ one_pause = false;
+ return true;
+ }
+ else if (keyval == upper_key (move_right))
+ {
+ if (game.move_right ())
+ sound_play ("slide");
+ one_pause = false;
+ return true;
+ }
+ else if (keyval == upper_key (move_rotate))
+ {
+ var result = false;
+ if (settings.get_boolean ("rotate-counter-clock-wise"))
+ result = game.rotate_left ();
+ else
+ result = game.rotate_right ();
+ if (result)
+ sound_play ("turn");
+ one_pause = false;
+ return true;
+ }
+ else if (keyval == upper_key (move_down))
+ {
+ game.fast_forward (true);
+ return true;
+ }
+ else if (keyval == upper_key (move_drop))
+ {
+ game.drop ();
+ return true;
+ }
+
+ return false;
+ }
+
+ private bool key_release_event_cb (Gtk.Widget widget, Gdk.EventKey event)
+ {
+ var keyval = upper_key (event.keyval);
+
+ if (keyval == upper_key (move_down))
+ {
+ game.fast_forward (false);
+ return true;
+ }
+
+ return false;
+ }
+
+ private uint upper_key (uint keyval)
+ {
+ if (keyval > 255)
+ return keyval;
+ return ((char) keyval).toupper ();
+ }
+
+ private void toggle_pause ()
+ {
+ paused = !paused;
+
+ if (paused)
+ view.show_pause_message ();
+ else
+ view.hide_pause_message ();
+ }
+
+#if 0
+ private void end_of_game ()
+ {
+ if (paused)
+ toggle_pause ();
+ pause_action.sensitive = false;
+ preferences_action.sensitive = true;
+
+ block_color_next = -1;
+ block_index_next = -1;
+ block_rotation_next = -1;
+ preview.set_block (-1, -1, false);
+ view.hide_pause_message ();
+ view.show_game_over_message ();
+ sound_play ("gameover");
+ in_play = false;
+
+ if (score_frame.getScore () > 0)
+ {
+ var pos = high_scores.add_plain_score (score_frame.getScore ());
+ var dialog = new GnomeGamesSupport.ScoresDialog (w, high_scores, _("Quadrapassel Scores"));
+ dialog.set_hilight (pos);
+ dialog.run ();
+ dialog.destroy ();
+ }
+ }
+#endif
+
+ private void new_game_cb (Gtk.Action action)
+ {
+ in_play = true;
+
+ int level = cmdline_level != 0 ? cmdline_level : starting_level;
+
+ fast_fall = false;
+
+ score_frame.set_level (level);
+ score_frame.set_starting_level (level);
+
+ score_frame.reset_score ();
+ paused = false;
+
+ game.start (line_fill_height, line_fill_prob);
+ if (do_preview)
+ preview.set_block (block_color_next, block_color_next, false);
+
+ pause_action.sensitive = true;
+ preferences_action.sensitive = false;
+
+ view.hide_pause_message ();
+ view.hide_game_over_message ();
+
+ sound_play ("quadrapassel");
+ }
+
+ private void help_cb (Gtk.Action action)
+ {
+ try
+ {
+ Gtk.show_uri (w.get_screen (), "ghelp:quadrapassel", Gtk.get_current_event_time ());
+ }
+ catch (Error e)
+ {
+ warning ("Failed to show help: %s", e.message);
+ }
+ }
+
+ private void about_cb (Gtk.Action action)
+ {
+ string[] authors = { "Gnome Games Team", null };
+ string[] documenters = { "Angela Boyle", null };
+
+ Gtk.show_about_dialog (w,
+ "program-name", _("Quadrapassel"),
+ "version", VERSION,
+ "comments", _("A classic game of fitting falling blocks together.\n\nQuadrapassel is a part of GNOME Games."),
+ "copyright", "Copyright \xc2\xa9 1999 J. Marcin Gorycki, 2000-2009 Others",
+ "license", GnomeGamesSupport.get_license (_("Quadrapassel")),
+ "website-label", _("GNOME Games web site"),
+ "authors", authors,
+ "documenters", documenters,
+ "translator-credits", _("translator-credits"),
+ "logo-icon-name", "quadrapassel",
+ "website", "http://www.gnome.org/projects/gnome-games/",
+ "wrap-license", true,
+ null);
+ }
+
+ private void scores_cb (Gtk.Action action)
+ {
+ var dialog = new GnomeGamesSupport.ScoresDialog (w, high_scores, _("Quadrapassel Scores"));
+ dialog.set_hilight (0);
+ dialog.run ();
+ dialog.destroy ();
+ }
+
+ public static int main (string args[])
+ {
+ var context = new OptionContext ("");
+
+ context.add_group (Gtk.get_option_group (true));
+ context.add_group (Clutter.get_option_group_without_init ());
+
+ try
+ {
+ context.parse (ref args);
+ }
+ catch (Error e)
+ {
+ stderr.printf ("%s\n", e.message);
+ return Posix.EXIT_FAILURE;
+ }
+
+ Environment.set_application_name (_("Quadrapassel"));
+
+ Gtk.Window.set_default_icon_name ("quadrapassel");
+
+ try
+ {
+ GtkClutter.init_with_args (ref args, "", new OptionEntry[0], null);
+ }
+ catch (Error e)
+ {
+ var dialog = new Gtk.MessageDialog (null, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.NONE, "Unable to initialize Clutter:\n%s", e.message);
+ dialog.set_title (Environment.get_application_name ());
+ dialog.run ();
+ dialog.destroy ();
+ return Posix.EXIT_FAILURE;
+ }
+
+ var app = new Quadrapassel (cmdline_level);
+ app.show ();
+
+ Gtk.main ();
+
+ return Posix.EXIT_SUCCESS;
+ }
+}
diff --git a/quadrapassel/src/scoreframe.vala b/quadrapassel/src/scoreframe.vala
new file mode 100644
index 0000000..39e4f02
--- /dev/null
+++ b/quadrapassel/src/scoreframe.vala
@@ -0,0 +1,139 @@
+public class ScoreFrame
+{
+ private Gtk.Grid w;
+ private Gtk.Label scorew;
+ private Gtk.Label linesw;
+ private Gtk.Label levelw;
+ private int level;
+ private int score;
+ private int lines;
+ private int starting_level;
+
+ public ScoreFrame (int cmdl_level)
+ {
+ starting_level = cmdl_level;
+ starting_level = starting_level.clamp (1, 20);
+
+ level = starting_level;
+
+ w = new Gtk.Grid ();
+
+ var score_label = new Gtk.Label (_("Score:"));
+ score_label.set_alignment (0.0f, 0.5f);
+ w.attach (score_label, 0, 0, 1, 1);
+ scorew = new Gtk.Label ("0");
+ scorew.set_alignment (1.0f, 0.5f);
+ w.attach (scorew, 1, 0, 1, 1);
+
+ var linesLabel = new Gtk.Label (_("Lines:"));
+ linesLabel.set_alignment (0.0f, 0.5f);
+ w.attach (linesLabel, 0, 1, 1, 1);
+ linesw = new Gtk.Label ("0");
+ linesw.set_alignment (1.0f, 0.5f);
+ w.attach (linesw, 1, 1, 1, 1);
+
+ var levelLabel = new Gtk.Label (_("Level:"));
+ levelLabel.set_alignment (0.0f, 0.5f);
+ w.attach (levelLabel, 0, 2, 1, 1);
+ levelw = new Gtk.Label ("0");
+ levelw.set_alignment (1.0f, 0.5f);
+ w.attach (levelw, 1, 2, 1, 1);
+ }
+
+ public void show ()
+ {
+ w.show_all ();
+ }
+
+ public Gtk.Widget get_widget ()
+ {
+ return w;
+ }
+
+ public int getScore ()
+ {
+ return score;
+ }
+
+ public void set_score (int s)
+ {
+ score = s;
+ scorew.set_text ("%7d".printf (score));
+ }
+
+ public void inc_score (int s)
+ {
+ set_score (score + s);
+ }
+
+ // The bonus for clearing everything.
+ public void score_last_line_bonus ()
+ {
+ inc_score (10000*level);
+ // FIXME: Get it its own sound?
+ sound_play ("quadrapassel");
+ }
+
+ public int score_lines (int newlines)
+ {
+ int linescore = 0;
+
+ switch (newlines)
+ {
+ case 0:
+ return level;
+ case 1:
+ linescore = 40;
+ sound_play ("lines1");
+ break;
+ case 2:
+ linescore = 100;
+ sound_play ("lines2");
+ break;
+ case 3:
+ linescore = 300;
+ sound_play ("lines3");
+ break;
+ case 4:
+ linescore = 1200;
+ sound_play ("lines3");
+ break;
+ }
+ inc_score (level * linescore);
+
+ // check the level
+ set_lines (lines + newlines);
+ int l = starting_level + lines / 10;
+ set_level (l);
+
+ return level;
+ }
+
+ public int get_level ()
+ {
+ return level;
+ }
+
+ public void set_level (int l)
+ {
+ level = l;
+ levelw.set_text ("%7d".printf (level));
+ }
+
+ public void set_lines (int l)
+ {
+ lines = l;
+ linesw.set_text ("%7d".printf (lines));
+ }
+
+ public void reset_score ()
+ {
+ set_lines (0);
+ set_score (0);
+ }
+
+ public void set_starting_level (int l)
+ {
+ starting_level = l;
+ }
+}
diff --git a/quadrapassel/src/sound.vala b/quadrapassel/src/sound.vala
new file mode 100644
index 0000000..5017732
--- /dev/null
+++ b/quadrapassel/src/sound.vala
@@ -0,0 +1,21 @@
+static bool enabled = true;
+
+void sound_enable (bool enable)
+{
+ enabled = enable;
+}
+
+bool sound_is_enabled ()
+{
+ return enabled;
+}
+
+void sound_play (string name)
+{
+ if (!enabled)
+ return;
+
+ CanberraGtk.context_get_for_screen (null).play (0,
+ Canberra.PROP_MEDIA_NAME, name,
+ Canberra.PROP_MEDIA_FILENAME, Path.build_filename (SOUND_DIRECTORY, "%s.ogg".printf (name)));
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]