[gnome-games] mahjongg: Port from C to Vala
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-games] mahjongg: Port from C to Vala
- Date: Mon, 24 Oct 2011 21:22:21 +0000 (UTC)
commit 15ed6a32a5c99b238deb076eef8be75093138a79
Author: Robert Ancell <robert ancell canonical com>
Date: Sat Oct 22 16:28:20 2011 +1100
mahjongg: Port from C to Vala
configure.in | 4 +-
libgames-support/GnomeGamesSupport-1.0.vapi | 235 ++++
libgames-support/Makefile.am | 4 -
libgames-support/games-files.c | 16 +-
libgames-support/games-files.h | 3 +-
libgames-support/games-scores.c | 11 +-
libgames-support/games-scores.h | 2 +-
mahjongg/Makefile.am | 96 +--
mahjongg/data/Makefile.am | 46 +
mahjongg/{ => data}/mahjongg.6 | 0
mahjongg/{ => data}/mahjongg.desktop.in.in | 0
mahjongg/{ => data}/mahjongg.map | 0
.../{ => data}/org.gnome.mahjongg.gschema.xml.in | 0
mahjongg/{ => data}/postmodern.svg | 0
mahjongg/{ => data}/smooth.png | Bin 336403 -> 336403 bytes
mahjongg/{ => data}/translatable_game_names.h | 0
mahjongg/drawing.c | 460 ------
mahjongg/drawing.h | 27 -
mahjongg/get_titles.pl | 41 -
mahjongg/mahjongg.c | 1457 --------------------
mahjongg/mahjongg.h | 44 -
mahjongg/maps.c | 578 --------
mahjongg/maps.h | 38 -
mahjongg/solubility.c | 530 -------
mahjongg/solubility.h | 21 -
mahjongg/src/Makefile.am | 54 +
mahjongg/src/config.vapi | 2 +
mahjongg/src/game-view.vala | 258 ++++
mahjongg/src/game.vala | 383 +++++
mahjongg/src/mahjongg.vala | 799 +++++++++++
mahjongg/src/map.vala | 358 +++++
po/POTFILES.in | 13 +-
po/POTFILES.skip | 6 +-
33 files changed, 2173 insertions(+), 3313 deletions(-)
---
diff --git a/configure.in b/configure.in
index 636879d..6052009 100644
--- a/configure.in
+++ b/configure.in
@@ -853,7 +853,9 @@ gnomine/help/Makefile
swell-foop/Makefile
swell-foop/help/Makefile
mahjongg/Makefile
+mahjongg/data/Makefile
mahjongg/help/Makefile
+mahjongg/src/Makefile
gtali/Makefile
gtali/pix/Makefile
gtali/help/Makefile
@@ -905,7 +907,7 @@ gnobots2/gnobots2.desktop.in
gnibbles/gnibbles.desktop.in
gnotski/gnotski.desktop.in
glchess/glchess.desktop.in
-mahjongg/mahjongg.desktop.in
+mahjongg/data/mahjongg.desktop.in
gtali/gtali.desktop.in
gnome-sudoku/gnome-sudoku.desktop.in
iagno/iagno.desktop.in
diff --git a/libgames-support/GnomeGamesSupport-1.0.vapi b/libgames-support/GnomeGamesSupport-1.0.vapi
new file mode 100644
index 0000000..56e3f2e
--- /dev/null
+++ b/libgames-support/GnomeGamesSupport-1.0.vapi
@@ -0,0 +1,235 @@
+/* We should probably be using the GIR files, but I can't get them to work in
+ * Vala. This works for now but means it needs to be updated when the library
+ * changes -- Robert Ancell */
+
+[CCode (cprefix = "Games", lower_case_cprefix = "games_")]
+namespace GnomeGamesSupport
+{
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_SCORES;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_PAUSE_GAME;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_RESUME_GAME;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_FULLSCREEN;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_LEAVE_FULLSCREEN;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_NEW_GAME;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_START_NEW_GAME;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_NETWORK_GAME;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_NETWORK_LEAVE;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_PLAYER_LIST;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_RESTART_GAME;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_UNDO_MOVE;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_REDO_MOVE;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_HINT;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_END_GAME;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_CONTENTS;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_RESET;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_TELEPORT;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_RTELEPORT;
+ [CCode (cheader_filename = "games-stock.h")]
+ public const string STOCK_DEAL_CARDS;
+
+ [CCode (cheader_filename = "games-stock.h")]
+ public static void stock_init ();
+ [CCode (cheader_filename = "games-stock.h")]
+ public static void stock_prepare_for_statusbar_tooltips (Gtk.UIManager ui_manager, Gtk.Widget statusbar);
+ [CCode (cheader_filename = "games-stock.h")]
+ public static string get_license (string game_name);
+
+ [CCode (cheader_filename = "games-setgid-io.h", cname = "setgid_io_init")]
+ public static void setgid_io_init ();
+
+ [CCode (cprefix = "GAMES_RUNTIME_", cheader_filename = "games-runtime.h")]
+ public enum RuntimeDirectory
+ {
+ PREFIX,
+ DATA_DIRECTORY,
+ COMMON_DATA_DIRECTORY,
+ PKG_DATA_DIRECTORY,
+ SCORES_DIRECTORY,
+ LOCALE_DIRECTORY,
+ COMMON_PIXMAP_DIRECTORY,
+ PRERENDERED_CARDS_DIRECTORY,
+ SCALABLE_CARDS_DIRECTORY,
+ ICON_THEME_DIRECTORY,
+ PIXMAP_DIRECTORY,
+ SOUND_DIRECTORY,
+ GAME_DATA_DIRECTORY,
+ GAME_GAMES_DIRECTORY,
+ GAME_PIXMAP_DIRECTORY,
+ GAME_THEME_DIRECTORY,
+ GAME_HELP_DIRECTORY,
+ LAST_DIRECTORY,
+ FIRST_DERIVED_DIRECTORY
+ }
+
+ [CCode (cheader_filename = "games-runtime.h")]
+ public static bool runtime_init (string name);
+ [CCode (cheader_filename = "games-runtime.h")]
+ public static void runtime_shutdown ();
+ [CCode (cheader_filename = "games-runtime.h")]
+ public static unowned string runtime_get_directory (RuntimeDirectory directory);
+ [CCode (cheader_filename = "games-runtime.h")]
+ public static unowned string runtime_get_file (RuntimeDirectory directory, string name);
+ [CCode (cheader_filename = "games-runtime.h")]
+ public static int runtime_get_gpl_version ();
+ [CCode (cheader_filename = "games-runtime.h")]
+ public static bool runtime_is_system_prefix ();
+
+ [CCode (cheader_filename = "games-help.h")]
+ public static void help_display (Gtk.Widget window, string doc_module, string? section);
+ [CCode (cheader_filename = "games-help.h")]
+ public static bool help_display_full (Gtk.Widget window, string doc_module, string? section) throws GLib.Error;
+
+ [CCode (cheader_filename = "games-settings.h")]
+ public static void settings_bind_window_state (string path, Gtk.Window window);
+
+ [CCode (cheader_filename = "games-clock.h")]
+ public class Clock : Gtk.Label
+ {
+ public Clock ();
+ public void start ();
+ public bool is_started ();
+ public void stop ();
+ public void reset ();
+ public time_t get_seconds ();
+ public void add_seconds (time_t seconds);
+ public void set_updated (bool do_update);
+ }
+
+ [CCode (cheader_filename = "games-pause-action.h")]
+ public class PauseAction : Gtk.Action
+ {
+ public signal void state_changed ();
+ public PauseAction (string name);
+ public bool get_is_paused ();
+ }
+
+ [CCode (cprefix = "GAMES_FULLSCREEN_ACTION_VISIBLE_")]
+ public enum VisiblePolicy
+ {
+ ALWAYS,
+ ON_FULLSCREEN,
+ ON_UNFULLSCREEN
+ }
+
+ [CCode (cheader_filename = "games-fullscreen-action.h")]
+ public class FullscreenAction : Gtk.Action
+ {
+ public FullscreenAction (string name, Gtk.Window window);
+ public void set_visible_policy (VisiblePolicy visible_policy);
+ public VisiblePolicy get_visible_policy ();
+ public void set_is_fullscreen (bool is_fullscreen);
+ public bool get_is_fullscreen ();
+ }
+
+ [CCode (cprefix = "GAMES_SCORES_STYLE_", cheader_filename = "games-score.h")]
+ public enum ScoreStyle
+ {
+ PLAIN_DESCENDING,
+ PLAIN_ASCENDING,
+ TIME_DESCENDING,
+ TIME_ASCENDING
+ }
+
+ [CCode (cheader_filename = "games-scores.h", destroy_function = "")]
+ public struct ScoresCategory
+ {
+ string key;
+ string name;
+ }
+
+ [CCode (cheader_filename = "games-score.h")]
+ public class Score : GLib.Object
+ {
+ public Score ();
+ }
+
+ [CCode (cheader_filename = "games-scores.h")]
+ public class Scores : GLib.Object
+ {
+ public Scores (string app_name, ScoresCategory[] categories, string? categories_context, string? categories_domain, int default_category_index, ScoreStyle style);
+ public void set_category (string category);
+ public int add_score (Score score);
+ public int add_plain_score (uint32 value);
+ public int add_time_score (double value);
+ public void update_score (string new_name);
+ public void update_score_name (string new_name, string old_name);
+ public ScoreStyle get_style ();
+ public unowned string get_category ();
+ public void add_category (string key, string name);
+ }
+
+ [CCode (cprefix = "GAMES_SCORES_", cheader_filename = "games-scores-dialog.h")]
+ public enum ScoresButtons
+ {
+ CLOSE_BUTTON,
+ NEW_GAME_BUTTON,
+ UNDO_BUTTON,
+ QUIT_BUTTON
+ }
+
+ [CCode (cheader_filename = "games-scores-dialog.h")]
+ public class ScoresDialog : Gtk.Dialog
+ {
+ public ScoresDialog (Gtk.Window parent_window, Scores scores, string title);
+ public void set_category_description (string description);
+ public void set_hilight (uint pos);
+ public void set_message (string message);
+ public void set_buttons (uint buttons);
+ }
+
+ [CCode (cheader_filename = "games-frame.h")]
+ public class Frame : Gtk.Box
+ {
+ public Frame (string label);
+ void set_label (string label);
+ }
+
+ [CCode (cheader_filename = "games-preimage.h")]
+ public class Preimage : GLib.Object
+ {
+ public Preimage ();
+ public Preimage.from_file (string filename) throws GLib.Error;
+ public void set_font_options (Cairo.FontOptions font_options);
+ public Gdk.Pixbuf render (int width, int height);
+ public void render_cairo (Cairo.Context cr, int width, int height);
+ public Gdk.Pixbuf render_sub (string node, int width, int height, double xoffset, double yoffset, double xzoom, double yzoom);
+ public void render_cairo_sub (Cairo.Context cr, string node, int width, int height, double xoffset, double yoffset, double xzoom, double yzoom);
+ public bool is_scalable ();
+ public int get_width ();
+ public int get_height ();
+ public Gdk.Pixbuf render_unscaled_pixbuf ();
+ }
+
+ [CCode (cheader_filename = "games-files.h")]
+ public class FileList : GLib.Object
+ {
+ public FileList (string glob, ...);
+ public FileList.images (string path1, ...);
+ public void transform_basename ();
+ public size_t length ();
+ public void for_each (GLib.Func<string> function);
+ public string find (GLib.CompareFunc function);
+ public unowned string? get_nth (int n);
+ public Gtk.Widget create_widget (string selection, uint flags);
+ }
+}
+
diff --git a/libgames-support/Makefile.am b/libgames-support/Makefile.am
index c963b80..095921c 100644
--- a/libgames-support/Makefile.am
+++ b/libgames-support/Makefile.am
@@ -192,16 +192,12 @@ GnomeGamesSupport-1.0.gir: $(INTROSPECTION_SCANNER) libgames-support-gi.la $(lib
--namespace GnomeGamesSupport --nsversion=1.0 \
--identifier-prefix=Games --symbol-prefix=games_ --accept-unprefixed \
--add-include-path=$(srcdir) --add-include=path=. \
- --include=Clutter-1.0 \
- --include=Cogl-1.0 \
--include=Gtk-$(GTK_API_VERSION) \
--library=games-support-gi \
--libtool="$(LIBTOOL)" \
--output $@ \
--pkg gobject-2.0 \
--pkg gtk+-$(GTK_API_VERSION) \
- --pkg clutter-1.0 \
- --pkg cogl-1.0 \
--warn-all \
-I$(top_srcdir) \
-I$(top_builddir) \
diff --git a/libgames-support/games-files.c b/libgames-support/games-files.c
index eb2a646..6b577e6 100644
--- a/libgames-support/games-files.c
+++ b/libgames-support/games-files.c
@@ -350,6 +350,18 @@ games_file_list_create_widget (GamesFileList * filelist,
}
/**
+ * games_file_list_length:
+ * @filelist: The list of files to use.
+ *
+ * Get the number of elements in the file list.
+ **/
+gsize
+games_file_list_length (GamesFileList * filelist)
+{
+ return g_list_length (filelist->priv->list);
+}
+
+/**
* games_file_list_for_each:
* @filelist: The file list to iterate over.
* @function: (scope call): The function to call on each item. It gets called with two
@@ -403,10 +415,10 @@ games_file_list_find (GamesFileList * filelist, GCompareFunc function,
* Return value:
**/
/* Return the nth filename in the list. */
-gchar *
+const gchar *
games_file_list_get_nth (GamesFileList * filelist, gint n)
{
- return (gchar *) g_list_nth_data (filelist->priv->list, n);
+ return (const gchar *) g_list_nth_data (filelist->priv->list, n);
}
static void
diff --git a/libgames-support/games-files.h b/libgames-support/games-files.h
index 30e1294..0e1fcc8 100644
--- a/libgames-support/games-files.h
+++ b/libgames-support/games-files.h
@@ -50,13 +50,14 @@ GamesFileList *games_file_list_new (const gchar * glob,
GamesFileList *games_file_list_new_images (const gchar * path1,
...) G_GNUC_NULL_TERMINATED;
void games_file_list_transform_basename (GamesFileList * list);
+gsize games_file_list_length (GamesFileList * filelist);
void games_file_list_for_each (GamesFileList * filelist,
GFunc function,
gpointer userdata);
gchar *games_file_list_find (GamesFileList * filelist,
GCompareFunc function,
gpointer userdata);
-gchar *games_file_list_get_nth (GamesFileList * filelist,
+const gchar *games_file_list_get_nth (GamesFileList * filelist,
gint n);
GtkWidget *games_file_list_create_widget (GamesFileList * filelist,
const gchar * selection,
diff --git a/libgames-support/games-scores.c b/libgames-support/games-scores.c
index 3532f75..d385297 100644
--- a/libgames-support/games-scores.c
+++ b/libgames-support/games-scores.c
@@ -128,10 +128,6 @@ games_scores_new (const char *app_name,
/* FIXME: Input sanity checks. */
- self->priv->categories = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free,
- (GDestroyNotify) games_scores_category_free);
-
/* catsordered is a record of the ordering of the categories.
* Its data is shared with the hash table. */
self->priv->catsordered = NULL;
@@ -210,7 +206,7 @@ games_scores_add_category (GamesScores *self,
*
**/
void
-games_scores_set_category (GamesScores * self, gchar * category)
+games_scores_set_category (GamesScores * self, const gchar * category)
{
GamesScoresPrivate *priv = self->priv;
@@ -494,6 +490,9 @@ games_scores_init (GamesScores * self)
priv->last_score_significant = FALSE;
priv->last_score_position = 0;
priv->last_score = games_score_new ();
+ priv->categories = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) games_scores_category_free);
}
static void
@@ -502,7 +501,7 @@ games_scores_finalize (GObject * object)
GamesScores *scores = GAMES_SCORES (object);
g_hash_table_unref (scores->priv->categories);
- g_free (scores->priv->catsordered);
+ g_slist_free (scores->priv->catsordered);
g_free (scores->priv->currentcat);
g_free (scores->priv->defcat);
g_free (scores->priv->basename);
diff --git a/libgames-support/games-scores.h b/libgames-support/games-scores.h
index 8b2ea74..388d68d 100644
--- a/libgames-support/games-scores.h
+++ b/libgames-support/games-scores.h
@@ -70,7 +70,7 @@ GamesScores *games_scores_new (const char *app_name,
const char *categories_domain,
int default_category_index,
GamesScoreStyle style);
-void games_scores_set_category (GamesScores * self, gchar * category);
+void games_scores_set_category (GamesScores * self, const gchar * category);
gint games_scores_add_score (GamesScores * self, GamesScore *score);
gint games_scores_add_plain_score (GamesScores * self, guint32 value);
gint games_scores_add_time_score (GamesScores * self, gdouble value);
diff --git a/mahjongg/Makefile.am b/mahjongg/Makefile.am
index b99619d..b814dd2 100644
--- a/mahjongg/Makefile.am
+++ b/mahjongg/Makefile.am
@@ -1,101 +1,7 @@
-SUBDIRS =
+SUBDIRS = data src
if BUILD_HELP
SUBDIRS += help
endif
-NULL =
-
-mapdir = $(pkgdatadir)/mahjongg/games
-map_DATA = \
- mahjongg.map \
- $(NULL)
-
-pixmapdir = $(pkgdatadir)/mahjongg/pixmaps
-pixmap_DATA = \
- smooth.png \
- postmodern.svg \
- $(NULL)
-
-bin_PROGRAMS = mahjongg
-
-mahjongg_SOURCES = \
- drawing.c \
- drawing.h \
- mahjongg.c \
- mahjongg.h \
- maps.c \
- maps.h \
- solubility.c \
- solubility.h \
- translatable_game_names.h \
- $(NULL)
-
-mahjongg_CPPFLAGS = \
- -I$(top_srcdir) \
- $(AM_CPPFLAGS)
-
-mahjongg_CFLAGS = \
- $(GTK_CFLAGS) \
- $(AM_CFLAGS)
-
-mahjongg_LDADD = \
- $(top_builddir)/libgames-support/libgames-support.la \
- $(GTK_LIBS) \
- $(INTLLIBS) \
- $(NULL)
-
-if HAVE_GNOME
-mahjongg_CFLAGS += $(GNOME_CFLAGS)
-mahjongg_LDADD += $(GNOME_LIBS)
-endif
-
-if HAVE_RSVG
-mahjongg_CFLAGS += $(RSVG_CFLAGS)
-mahjongg_LDADD += $(RSVG_LIBS)
-endif
-
-if WITH_GTHREAD
-mahjongg_CFLAGS += $(GHTREAD_CFLAGS)
-mahjongg_LDADD += $(GTHREAD_LIBS)
-endif
-
-gsettings_in_file = org.gnome.mahjongg.gschema.xml.in
-gsettings_SCHEMAS = $(gsettings_in_file:.xml.in=.xml)
- INTLTOOL_XML_NOMERGE_RULE@
- GSETTINGS_RULES@
-
-man_MANS = mahjongg.6
-
-desktop_in_files = mahjongg.desktop.in.in
-desktopdir = $(datadir)/applications
-desktop_DATA = $(desktop_in_files:.desktop.in.in=.desktop)
-
-EXTRA_DIST = \
- $(pixmap_DATA) \
- $(gsettings_in_file) \
- $(man_MANS) \
- $(map_DATA) \
- $(NULL)
-
-CLEANFILES = $(desktop_DATA) $(gsettings_SCHEMAS)
-DISTCLEANFILES = $(desktop_DATA) $(gsettings_SCHEMAS)
-
-install-scorefiles-local:
- -$(mkinstalldirs) $(DESTDIR)$(scoredir)
- -for i in easy difficult confounding pyramid tictactoe cloud dragon bridges ziggurat; do \
- touch $(DESTDIR)$(scoredir)/mahjongg.$$i.scores; \
- chown $(scores_user):$(scores_group) $(DESTDIR)$(scoredir)/mahjongg.$$i.scores; \
- chmod 664 $(DESTDIR)$(scoredir)/mahjongg.$$i.scores; \
- done
-
-install-exec-hook:
- -if test "$(setgid)" = "true"; then \
- chgrp $(scores_group) $(DESTDIR)$(bindir)/mahjongg && chmod 2555 $(DESTDIR)$(bindir)/mahjongg ;\
- fi
-
-install-data-local: install-scorefiles-local
-
- INTLTOOL_DESKTOP_RULE@
-
-include $(top_srcdir)/git.mk
diff --git a/mahjongg/data/Makefile.am b/mahjongg/data/Makefile.am
new file mode 100644
index 0000000..8c47317
--- /dev/null
+++ b/mahjongg/data/Makefile.am
@@ -0,0 +1,46 @@
+mapdir = $(pkgdatadir)/mahjongg/games
+map_DATA = \
+ mahjongg.map \
+ $(NULL)
+
+pixmapdir = $(pkgdatadir)/mahjongg/pixmaps
+pixmap_DATA = \
+ smooth.png \
+ postmodern.svg \
+ $(NULL)
+
+gsettings_in_file = org.gnome.mahjongg.gschema.xml.in
+gsettings_SCHEMAS = $(gsettings_in_file:.xml.in=.xml)
+ INTLTOOL_XML_NOMERGE_RULE@
+ GSETTINGS_RULES@
+
+man_MANS = mahjongg.6
+
+desktop_in_files = mahjongg.desktop.in.in
+desktopdir = $(datadir)/applications
+desktop_DATA = $(desktop_in_files:.desktop.in.in=.desktop)
+
+EXTRA_DIST = \
+ $(pixmap_DATA) \
+ $(gsettings_in_file) \
+ $(man_MANS) \
+ $(map_DATA) \
+ translatable_game_names.h \
+ $(NULL)
+
+CLEANFILES = $(desktop_DATA) $(gsettings_SCHEMAS)
+DISTCLEANFILES = $(desktop_DATA) $(gsettings_SCHEMAS)
+
+install-scorefiles-local:
+ -$(mkinstalldirs) $(DESTDIR)$(scoredir)
+ -for i in easy difficult confounding pyramid tictactoe cloud dragon bridges ziggurat; do \
+ touch $(DESTDIR)$(scoredir)/mahjongg.$$i.scores; \
+ chown $(scores_user):$(scores_group) $(DESTDIR)$(scoredir)/mahjongg.$$i.scores; \
+ chmod 664 $(DESTDIR)$(scoredir)/mahjongg.$$i.scores; \
+ done
+
+install-data-local: install-scorefiles-local
+
+ INTLTOOL_DESKTOP_RULE@
+
+-include $(top_srcdir)/git.mk
diff --git a/mahjongg/mahjongg.6 b/mahjongg/data/mahjongg.6
similarity index 100%
rename from mahjongg/mahjongg.6
rename to mahjongg/data/mahjongg.6
diff --git a/mahjongg/mahjongg.desktop.in.in b/mahjongg/data/mahjongg.desktop.in.in
similarity index 100%
rename from mahjongg/mahjongg.desktop.in.in
rename to mahjongg/data/mahjongg.desktop.in.in
diff --git a/mahjongg/mahjongg.map b/mahjongg/data/mahjongg.map
similarity index 100%
rename from mahjongg/mahjongg.map
rename to mahjongg/data/mahjongg.map
diff --git a/mahjongg/org.gnome.mahjongg.gschema.xml.in b/mahjongg/data/org.gnome.mahjongg.gschema.xml.in
similarity index 100%
rename from mahjongg/org.gnome.mahjongg.gschema.xml.in
rename to mahjongg/data/org.gnome.mahjongg.gschema.xml.in
diff --git a/mahjongg/postmodern.svg b/mahjongg/data/postmodern.svg
similarity index 100%
rename from mahjongg/postmodern.svg
rename to mahjongg/data/postmodern.svg
diff --git a/mahjongg/smooth.png b/mahjongg/data/smooth.png
similarity index 100%
rename from mahjongg/smooth.png
rename to mahjongg/data/smooth.png
diff --git a/mahjongg/translatable_game_names.h b/mahjongg/data/translatable_game_names.h
similarity index 100%
rename from mahjongg/translatable_game_names.h
rename to mahjongg/data/translatable_game_names.h
diff --git a/mahjongg/src/Makefile.am b/mahjongg/src/Makefile.am
new file mode 100644
index 0000000..87ee864
--- /dev/null
+++ b/mahjongg/src/Makefile.am
@@ -0,0 +1,54 @@
+bin_PROGRAMS = mahjongg
+
+mahjongg_SOURCES = \
+ config.vapi \
+ game.vala \
+ game-view.vala \
+ mahjongg.vala \
+ map.vala \
+ $(NULL)
+
+mahjongg_VALAFLAGS = \
+ --pkg posix \
+ --pkg gtk+-3.0 \
+ --vapidir $(top_srcdir)/libgames-support \
+ --pkg GnomeGamesSupport-1.0
+
+mahjongg_CFLAGS = \
+ -I$(top_srcdir)/libgames-support \
+ -DVERSION=\"$(VERSION)\" \
+ -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \
+ $(GTK_CFLAGS) \
+ $(AM_CFLAGS)
+
+if ENABLE_SETGID
+mahjongg_VALAFLAGS += -D ENABLE_SETGID
+endif
+
+mahjongg_LDADD = \
+ $(top_builddir)/libgames-support/libgames-support.la \
+ $(GTK_LIBS) \
+ $(INTLLIBS) \
+ $(NULL)
+
+if HAVE_GNOME
+mahjongg_CFLAGS += $(GNOME_CFLAGS)
+mahjongg_LDADD += $(GNOME_LIBS)
+endif
+
+if HAVE_RSVG
+mahjongg_CFLAGS += $(RSVG_CFLAGS)
+mahjongg_LDADD += $(RSVG_LIBS)
+endif
+
+if WITH_GTHREAD
+mahjongg_CFLAGS += $(GHTREAD_CFLAGS)
+mahjongg_LDADD += $(GTHREAD_LIBS)
+endif
+
+install-exec-hook:
+ -if test "$(setgid)" = "true"; then \
+ chgrp $(scores_group) $(DESTDIR)$(bindir)/mahjongg && chmod 2555 $(DESTDIR)$(bindir)/mahjongg ;\
+ fi
+
+-include $(top_srcdir)/git.mk
diff --git a/mahjongg/src/config.vapi b/mahjongg/src/config.vapi
new file mode 100644
index 0000000..6477226
--- /dev/null
+++ b/mahjongg/src/config.vapi
@@ -0,0 +1,2 @@
+public const string VERSION;
+public const string GETTEXT_PACKAGE;
\ No newline at end of file
diff --git a/mahjongg/src/game-view.vala b/mahjongg/src/game-view.vala
new file mode 100644
index 0000000..cea2be2
--- /dev/null
+++ b/mahjongg/src/game-view.vala
@@ -0,0 +1,258 @@
+public class GameView : Gtk.DrawingArea
+{
+ public Gdk.Color background_color;
+ private Gdk.Pixbuf? tile_textures = null;
+ private int tile_texture_width = 0;
+ private int tile_texture_height = 0;
+
+ private int x_offset;
+ private int y_offset;
+ private int tile_width;
+ private int tile_height;
+ private int tile_layer_offset_x;
+ private int tile_layer_offset_y;
+
+ private Game? _game;
+ public Game? game
+ {
+ get { return _game; }
+ set
+ {
+ _game = value;
+ _game.redraw_tile.connect (redraw_tile_cb);
+ queue_draw ();
+ }
+ }
+
+ private GnomeGamesSupport.Preimage? _theme = null;
+ public GnomeGamesSupport.Preimage? theme
+ {
+ get { return _theme; }
+ set { _theme = value; tile_textures = null; queue_draw (); }
+ }
+
+ private bool _paused = false;
+ public bool paused
+ {
+ get { return _paused; }
+ set { _paused = value; queue_draw (); }
+ }
+
+ public GameView ()
+ {
+ can_focus = true;
+ add_events (Gdk.EventMask.BUTTON_PRESS_MASK);
+ }
+
+ public void set_background (string? colour)
+ {
+ if (colour == null || !Gdk.Color.parse (colour, out background_color))
+ background_color.red = background_color.green = background_color.blue = 0;
+ queue_draw ();
+ }
+
+ public override bool configure_event (Gdk.EventConfigure event)
+ {
+ /* Regenerate images */
+ tile_textures = null;
+
+ return false;
+ }
+
+ private void draw_game (Cairo.Context cr, bool render_indexes = false)
+ {
+ if (theme == null)
+ return;
+
+ update_dimensions ();
+
+ /* The images are bigger than the tile as they contain the isometric extension in the z-axis */
+ var image_width = tile_width + tile_layer_offset_x;
+ var image_height = tile_height + tile_layer_offset_y;
+
+ /* Render the tiles */
+ if (!render_indexes && (tile_textures == null || tile_texture_width != image_width || tile_texture_height != image_height))
+ {
+ tile_texture_width = image_width;
+ tile_texture_height = image_height;
+ tile_textures = theme.render ((int) (image_width * 43), image_height * 2);
+ if (tile_textures == null)
+ return;
+ }
+
+ /* This works because of the way the tiles are sorted. We could
+ * reverse them to make this look a little nicer, but when searching
+ * for a tile we want it the other way around. */
+
+ foreach (var tile in game.tiles)
+ {
+ if (!tile.visible)
+ continue;
+
+ int x, y;
+ get_tile_position (tile, out x, out y);
+
+ /* Select image for this tile, or blank image if paused */
+ var texture_x = get_image_offset (tile.number) * image_width;
+ var texture_y = 0;
+ if (paused)
+ {
+ texture_x = get_image_offset (-1) * image_width;
+ texture_y = 0;
+ }
+ else if (tile == game.selected_tile)
+ texture_y = image_height;
+ else if (game.hint_blink_counter % 2 == 1 && (tile == game.hint_tiles[0] || tile == game.hint_tiles[1]))
+ texture_y = image_height;
+
+ if (render_indexes)
+ cr.set_source_rgb (tile.number / 255.0, tile.number / 255.0, tile.number / 255.0);
+ else
+ Gdk.cairo_set_source_pixbuf (cr, tile_textures, x - texture_x, y - texture_y);
+ cr.rectangle (x, y, image_width, image_height);
+ cr.fill ();
+ }
+ }
+
+ private void update_dimensions ()
+ {
+ var width = get_allocated_width ();
+ var height = get_allocated_height ();
+
+ if (theme == null)
+ return;
+
+ /* Get aspect ratio from theme - contains 43x2 tiles */
+ var aspect = ((double) theme.get_height () / 2) / ((double) theme.get_width () / 43);
+
+ /* Need enough space for the whole map and one unit border */
+ var map_width = game.map.width + 2.0;
+ var map_height = (game.map.height + 2.0) * aspect;
+
+ /* Scale the map to fit */
+ var unit_width = double.min (width / map_width, height / map_height);
+ var unit_height = unit_width * aspect;
+
+ /* The size of one tile is two units wide, and the correct aspect ratio */
+ tile_width = (int) (unit_width * 2);
+ tile_height = (int) (unit_height * 2);
+
+ /* Offset the tiles when on a higher layer (themes must use these hard-coded ratios) */
+ tile_layer_offset_x = tile_width / 7;
+ tile_layer_offset_y = tile_height / 10;
+
+ /* Center the map */
+ x_offset = (int) (width - game.map.width * unit_width) / 2;
+ y_offset = (int) (height - game.map.height * unit_height) / 2;
+ }
+
+ private void get_tile_position (Tile tile, out int x, out int y)
+ {
+ x = x_offset + tile.slot.x * tile_width / 2 + tile.slot.layer * tile_layer_offset_x;
+ y = y_offset + tile.slot.y * tile_height / 2 - tile.slot.layer * tile_layer_offset_y;
+ }
+
+ private int get_image_offset (int number)
+ {
+ var set = number / 4;
+
+ /* Invalid numbers use the blank tile */
+ if (number < 0 || set >= 36)
+ return 42;
+
+ /* The bonus tiles have different images for each */
+ if (set == 33)
+ return 33 + number % 4;
+ if (set == 35)
+ return 38 + number % 4;
+ /* The white dragons are inbetween the bonus tiles just to be confusing */
+ if (set == 34)
+ return 37;
+
+ /* Everything else is in set order */
+ return set;
+ }
+
+ private void redraw_tile_cb (Tile tile)
+ {
+ update_dimensions ();
+ int x, y;
+ get_tile_position (tile, out x, out y);
+ queue_draw_area (x, y, tile_texture_width, tile_texture_height);
+ }
+
+ public override bool draw (Cairo.Context cr)
+ {
+ if (game == null)
+ return false;
+
+ Gdk.cairo_set_source_color (cr, background_color);
+ cr.paint ();
+ draw_game (cr);
+
+ return true;
+ }
+
+ public override bool button_press_event (Gdk.EventButton event)
+ {
+ if (game == null || paused)
+ return false;
+
+ /* Ignore the 2BUTTON and 3BUTTON events. */
+ if (event.type != Gdk.EventType.BUTTON_PRESS)
+ return false;
+
+ /* Get the tile under the square */
+ var tile = find_tile ((uint) event.x, (uint) event.y);
+
+ /* If not a valid tile then ignore the event */
+ if (tile == null || !game.tile_can_move (tile))
+ return true;
+
+ if (event.button == 1)
+ {
+ /* Select first tile */
+ if (game.selected_tile == null)
+ {
+ game.selected_tile = tile;
+ return true;
+ }
+
+ /* Unselect tile by clicking on it again */
+ if (tile == game.selected_tile)
+ {
+ game.selected_tile = null;
+ return true;
+ }
+
+ /* Attempt to match second tile to the selected one */
+ if (game.selected_tile.matches (tile))
+ {
+ game.remove_pair (game.selected_tile, tile);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private Tile? find_tile (uint x, uint y)
+ {
+ /* Render a 1x1 image where the cursor is using a different color for each tile */
+ var surface = new Cairo.ImageSurface (Cairo.Format.RGB24, 1, 1);
+ var cr = new Cairo.Context (surface);
+ cr.set_source_rgba (255, 255, 255, 255);
+ cr.paint ();
+ cr.translate (-x, -y);
+ draw_game (cr, true);
+
+ /* The color value is the tile under the cursor */
+ unowned uchar[] data = surface.get_data ();
+ var number = data[0];
+ foreach (var tile in game.tiles)
+ if (tile.number == number)
+ return tile;
+
+ return null;
+ }
+}
diff --git a/mahjongg/src/game.vala b/mahjongg/src/game.vala
new file mode 100644
index 0000000..9f095b1
--- /dev/null
+++ b/mahjongg/src/game.vala
@@ -0,0 +1,383 @@
+public class Tile
+{
+ public int number;
+ public Slot slot;
+ public bool visible = true;
+ public int move_number;
+
+ public int set
+ {
+ get { return number / 4; }
+ }
+
+ public Tile (Slot slot)
+ {
+ this.slot = slot;
+ }
+
+ public bool matches (Tile tile)
+ {
+ return tile.set == set;
+ }
+}
+
+private static int compare_tiles (Tile a, Tile b)
+{
+ return compare_slots (a.slot, b.slot);
+}
+
+public class Match
+{
+ public Tile tile0;
+ public Tile tile1;
+
+ public Match (Tile tile0, Tile tile1)
+ {
+ this.tile0 = tile0;
+ this.tile1 = tile1;
+ }
+}
+
+public class Game
+{
+ public Map map;
+ public List<Tile> tiles = null;
+ public Tile? hint_tiles[2];
+
+ public int move_number;
+
+ /* Hint animation */
+ private uint hint_timer = 0;
+ public uint hint_blink_counter = 0;
+
+ public signal void redraw_tile (Tile tile);
+ public signal void moved ();
+
+ public bool started
+ {
+ get { return move_number > 1; }
+ }
+
+ private Tile? _selected_tile = null;
+ public Tile? selected_tile
+ {
+ get { return _selected_tile; }
+ set
+ {
+ if (_selected_tile != null)
+ redraw_tile (_selected_tile);
+ _selected_tile = value;
+ if (value != null)
+ redraw_tile (value);
+ }
+ }
+
+ public int visible_tiles
+ {
+ get
+ {
+ var n = 0;
+ foreach (var tile in tiles)
+ if (tile.visible)
+ n++;
+ return n;
+ }
+ }
+
+ public uint moves_left
+ {
+ get { return find_matches ().length (); }
+ }
+
+ public bool complete
+ {
+ get { return visible_tiles == 0; }
+ }
+
+ public bool can_move
+ {
+ get { return moves_left != 0; }
+ }
+
+ public Game (Map map)
+ {
+ this.map = map;
+ move_number = 1;
+
+ /* Create the tiles in the locations required in the map */
+ foreach (var slot in map.slots)
+ {
+ var tile = new Tile (slot);
+ tile.number = 0;
+ tiles.insert_sorted (tile, compare_tiles);
+ }
+
+ /* Come up with a random solution by picking random pairs and assigning them
+ * with a random value. If end up with an invalid solution, then choose the
+ * next avaiable pair */
+ var n_pairs = (int) tiles.length () / 2;
+ var numbers = new int[n_pairs];
+ for (var i = 0; i < n_pairs; i++)
+ numbers[i] = i*2;
+ for (var i = 0; i < n_pairs; i++)
+ {
+ var n = Random.int_range (i, n_pairs);
+ var t = numbers[i];
+ numbers[i] = numbers[n];
+ numbers[n] = t;
+ }
+ shuffle (numbers);
+
+ /* Make everything visible again */
+ reset ();
+ }
+
+ private bool shuffle (int[] numbers, int depth = 0)
+ {
+ /* All shuffled */
+ if (depth == tiles.length () / 2)
+ return true;
+
+ var matches = find_matches ();
+ var n_matches = matches.length ();
+
+ /* No matches on this branch, rewind */
+ if (n_matches == 0)
+ return false;
+
+ var n = Random.int_range (0, (int) n_matches);
+ for (var i = 0; i < n_matches; i++)
+ {
+ var match = matches.nth_data ((n + i) % n_matches);
+ match.tile0.number = numbers[depth];
+ match.tile0.visible = false;
+ match.tile1.number = numbers[depth] + 1;
+ match.tile1.visible = false;
+
+ if (shuffle (numbers, depth + 1))
+ return true;
+
+ /* Undo this move */
+ match.tile0.number = 0;
+ match.tile0.visible = true;
+ match.tile1.number = 0;
+ match.tile1.visible = true;
+ }
+
+ return false;
+ }
+
+ public void reset ()
+ {
+ selected_tile = null;
+ set_hint (null, null);
+ foreach (var tile in tiles)
+ {
+ tile.visible = true;
+ tile.move_number = 0;
+ }
+ }
+
+ public void set_hint (Tile? tile0, Tile? tile1)
+ {
+ /* Stop hints */
+ if (tile0 == null && tile1 == null)
+ {
+ hint_blink_counter = 0;
+ hint_timeout_cb ();
+ return;
+ }
+
+ hint_tiles[0] = tile0;
+ hint_tiles[1] = tile1;
+ hint_blink_counter = 6;
+ if (hint_timer != 0)
+ Source.remove (hint_timer);
+ hint_timer = Timeout.add (250, hint_timeout_cb);
+ hint_timeout_cb ();
+ }
+
+ private bool hint_timeout_cb ()
+ {
+ if (hint_blink_counter == 0)
+ {
+ if (hint_timer != 0)
+ Source.remove (hint_timer);
+ hint_timer = 0;
+ return false;
+ }
+ hint_blink_counter--;
+
+ if (hint_tiles[0] != null)
+ redraw_tile (hint_tiles[0]);
+ if (hint_tiles[1] != null)
+ redraw_tile (hint_tiles[1]);
+
+ return true;
+ }
+
+ public bool tile_can_move (Tile tile)
+ {
+ if (!tile.visible)
+ return false;
+
+ var blocked_left = false;
+ var blocked_right = false;
+ var slot = tile.slot;
+ foreach (var t in tiles)
+ {
+ if (t == tile || !t.visible)
+ continue;
+
+ var s = t.slot;
+
+ /* Can't move if blocked by a tile above */
+ if (s.layer == slot.layer + 1 &&
+ (s.x >= slot.x - 1 && s.x <= slot.x + 1) &&
+ (s.y >= slot.y - 1 && s.y <= slot.y + 1))
+ return false;
+
+ /* Can't move if blocked both on the left and the right */
+ if (s.layer == slot.layer && (s.y >= slot.y - 1 && s.y <= slot.y + 1))
+ {
+ if (s.x == slot.x - 2)
+ blocked_left = true;
+ if (s.x == slot.x + 2)
+ blocked_right = true;
+ if (blocked_left && blocked_right)
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public List<Match> find_matches (Tile? tile = null)
+ {
+ List<Match> matches = null;
+
+ if (tile != null && !tile_can_move (tile))
+ return matches;
+
+ if (tile == null)
+ {
+ foreach (var t in tiles)
+ {
+ foreach (var match in find_matches (t))
+ matches.append (match);
+ }
+ }
+ else
+ {
+ foreach (var t in tiles)
+ {
+ if (t == tile || !tile_can_move (t))
+ continue;
+
+ if (!t.matches (tile))
+ continue;
+
+ var already_matched = false;
+ foreach (var match in matches)
+ {
+ if (match.tile0 == tile && match.tile1 == t)
+ {
+ already_matched = true;
+ break;
+ }
+ }
+
+ if (!already_matched)
+ matches.append (new Match (t, tile));
+ }
+ }
+
+ return matches;
+ }
+
+ public bool remove_pair (Tile tile0, Tile tile1)
+ {
+ if (!tile0.visible || !tile1.visible)
+ return false;
+
+ if (tile0.set != tile1.set)
+ return false;
+
+ selected_tile = null;
+ set_hint (null, null);
+
+ tile0.visible = false;
+ tile0.move_number = move_number;
+ tile1.visible = false;
+ tile1.move_number = move_number;
+
+ move_number++;
+
+ redraw_tile (tile0);
+ redraw_tile (tile1);
+
+ /* You lose your re-do queue when you make a move */
+ foreach (var tile in tiles)
+ if (tile.move_number >= move_number)
+ tile.move_number = 0;
+
+ moved ();
+
+ return true;
+ }
+
+ public bool can_undo
+ {
+ get { return move_number > 1; }
+ }
+
+ public void undo ()
+ {
+ if (!can_undo)
+ return;
+
+ selected_tile = null;
+ set_hint (null, null);
+
+ /* Re-show tiles that were removed */
+ move_number--;
+ foreach (var tile in tiles)
+ {
+ if (tile.move_number == move_number)
+ {
+ tile.visible = true;
+ redraw_tile (tile);
+ }
+ }
+ }
+
+ public bool can_redo
+ {
+ get
+ {
+ foreach (var tile in tiles)
+ if (tile.move_number >= move_number)
+ return true;
+ return false;
+ }
+ }
+
+ public void redo ()
+ {
+ if (!can_redo)
+ return;
+
+ selected_tile = null;
+ set_hint (null, null);
+
+ foreach (var tile in tiles)
+ {
+ if (tile.move_number == move_number)
+ {
+ tile.visible = false;
+ redraw_tile (tile);
+ }
+ }
+ move_number++;
+ }
+}
diff --git a/mahjongg/src/mahjongg.vala b/mahjongg/src/mahjongg.vala
new file mode 100644
index 0000000..7b19cee
--- /dev/null
+++ b/mahjongg/src/mahjongg.vala
@@ -0,0 +1,799 @@
+public class Mahjongg
+{
+ private Settings settings;
+
+ private GnomeGamesSupport.Scores highscores;
+
+ private List<Map> maps = null;
+
+ private Gtk.Window window;
+ private GameView game_view;
+ private Gtk.Statusbar statusbar;
+ private Gtk.UIManager ui_manager;
+ private Gtk.Label tiles_label;
+ private Gtk.Toolbar toolbar;
+ private Gtk.Label moves_label;
+ private GnomeGamesSupport.Clock game_clock;
+ private Gtk.Dialog? preferences_dialog = null;
+
+ private GnomeGamesSupport.PauseAction pause_action;
+ private Gtk.Action hint_action;
+ private Gtk.Action redo_action;
+ private Gtk.Action undo_action;
+ private Gtk.Action restart_action;
+ private GnomeGamesSupport.FullscreenAction fullscreen_action;
+ private GnomeGamesSupport.FullscreenAction leave_fullscreen_action;
+
+ public Mahjongg ()
+ {
+ settings = new Settings ("org.gnome.mahjongg");
+
+ load_maps ();
+
+ highscores = new GnomeGamesSupport.Scores ("mahjongg",
+ new GnomeGamesSupport.ScoresCategory[0],
+ null, null, 0,
+ GnomeGamesSupport.ScoreStyle.TIME_ASCENDING);
+ foreach (var map in maps)
+ {
+ var display_name = dpgettext2 (null, "mahjongg map name", map.name);
+ highscores.add_category (map.score_name, display_name);
+ }
+
+ window = new Gtk.Window (Gtk.WindowType.TOPLEVEL);
+ window.title = _("Mahjongg");
+ window.set_default_size (530, 440);
+ GnomeGamesSupport.settings_bind_window_state ("/org/gnome/mahjongg/", window);
+
+ var status_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 10);
+
+ var group_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
+ var label = new Gtk.Label (_("Tiles Left:"));
+ group_box.pack_start (label, false, false, 0);
+ var spacer = new Gtk.Label (" ");
+ group_box.pack_start (spacer, false, false, 0);
+ tiles_label = new Gtk.Label ("");
+ group_box.pack_start (tiles_label, false, false, 0);
+ status_box.pack_start (group_box, false, false, 0);
+
+ group_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
+ label = new Gtk.Label (_("Moves Left:"));
+ group_box.pack_start (label, false, false, 0);
+ spacer = new Gtk.Label (" ");
+ group_box.pack_start (spacer, false, false, 0);
+ moves_label = new Gtk.Label ("");
+ group_box.pack_start (moves_label, false, false, 0);
+ status_box.pack_start (group_box, false, false, 0);
+
+ group_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
+ var game_clock_label = new Gtk.Label (_("Time:"));
+ group_box.pack_start (game_clock_label, false, false, 0);
+ spacer = new Gtk.Label (" ");
+ group_box.pack_start (spacer, false, false, 0);
+ game_clock = new GnomeGamesSupport.Clock ();
+ group_box.pack_start (game_clock, false, false, 0);
+ status_box.pack_start (group_box, false, false, 0);
+
+ /* show the status bar items */
+ statusbar = new Gtk.Statusbar ();
+ ui_manager = new Gtk.UIManager ();
+
+ GnomeGamesSupport.stock_prepare_for_statusbar_tooltips (ui_manager, statusbar);
+
+ create_menus (ui_manager);
+ window.add_accel_group (ui_manager.get_accel_group ());
+ var box = ui_manager.get_widget ("/MainMenu");
+
+ window.delete_event.connect (window_delete_event_cb);
+
+ game_view = new GameView ();
+ game_view.set_size_request (320, 200);
+
+ toolbar = (Gtk.Toolbar) ui_manager.get_widget ("/Toolbar");
+ toolbar.get_style_context ().add_class (Gtk.STYLE_CLASS_PRIMARY_TOOLBAR);
+
+ var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+
+ vbox.pack_start (box, false, false, 0);
+ vbox.pack_start (toolbar, false, false, 0);
+ vbox.pack_start (game_view, true, true, 0);
+
+ statusbar.pack_end (status_box, false, false, 6);
+ vbox.pack_end (statusbar, false, false, 0);
+
+ window.add (vbox);
+
+ settings.changed.connect (conf_value_changed_cb);
+
+ new_game ();
+
+ game_view.grab_focus ();
+ }
+
+ public void start ()
+ {
+ window.show_all ();
+
+ leave_fullscreen_action.set_visible_policy (GnomeGamesSupport.VisiblePolicy.ON_FULLSCREEN);
+ conf_value_changed_cb (settings, "tileset");
+ conf_value_changed_cb (settings, "bgcolour");
+ conf_value_changed_cb (settings, "show-toolbar");
+ }
+
+ private void update_ui ()
+ {
+ pause_action.sensitive = game_view.game.move_number > 1;
+ restart_action.sensitive = game_view.game.move_number > 1;
+
+ if (game_view.paused)
+ {
+ hint_action.sensitive = false;
+ undo_action.sensitive = false;
+ redo_action.sensitive = false;
+ }
+ else
+ {
+ hint_action.sensitive = game_view.game.moves_left > 0;
+ undo_action.sensitive = game_view.game.can_undo;
+ redo_action.sensitive = game_view.game.can_redo;
+ }
+
+ moves_label.set_text ("%2u".printf (game_view.game.moves_left));
+ tiles_label.set_text ("%3d".printf (game_view.game.visible_tiles));
+ }
+
+ private void theme_changed_cb (Gtk.ComboBox widget)
+ {
+ Gtk.TreeIter iter;
+ widget.get_active_iter (out iter);
+ string theme;
+ widget.model.get (iter, 1, out theme);
+ settings.set_string ("tileset", theme);
+ }
+
+ private void conf_value_changed_cb (Settings settings, string key)
+ {
+ if (key == "tileset")
+ {
+ var theme = settings.get_string ("tileset");
+ game_view.theme = load_theme_texture (theme);
+ if (game_view.theme == null)
+ {
+ warning ("Unable to load theme %s, falling back to default", theme);
+ game_view.theme = load_theme_texture ("postmodern.svg", true);
+ }
+ }
+ else if (key == "show-toolbar")
+ {
+ toolbar.visible = settings.get_boolean ("show-toolbar");
+ }
+ else if (key == "bgcolour")
+ {
+ game_view.set_background (settings.get_string ("bgcolour"));
+ }
+ else if (key == "mapset")
+ {
+ /* Prompt user if already made a move */
+ if (game_view.game.started)
+ {
+ var dialog = new Gtk.MessageDialog (window,
+ Gtk.DialogFlags.MODAL,
+ Gtk.MessageType.QUESTION,
+ Gtk.ButtonsType.NONE,
+ "%s", _("Do you want to start a new game with this map?"));
+ dialog.format_secondary_text (_("If you continue playing the next game will use the new map."));
+ dialog.add_buttons (_("_Continue playing"), Gtk.ResponseType.REJECT,
+ _("Use _new map"), Gtk.ResponseType.ACCEPT,
+ null);
+ dialog.set_default_response (Gtk.ResponseType.ACCEPT);
+ var response = dialog.run ();
+ if (response == Gtk.ResponseType.ACCEPT)
+ new_game ();
+ dialog.destroy ();
+ }
+ else
+ new_game ();
+ }
+ }
+
+ private GnomeGamesSupport.Preimage? load_theme_texture (string filename, bool fail_on_error = false)
+ {
+ var pixmap_directory = GnomeGamesSupport.runtime_get_directory (GnomeGamesSupport.RuntimeDirectory.GAME_PIXMAP_DIRECTORY);
+ var path = Path.build_filename (pixmap_directory, filename);
+ try
+ {
+ return new GnomeGamesSupport.Preimage.from_file (path);
+ }
+ catch (Error e)
+ {
+ warning ("Failed to load theme %s: %s", filename, path);
+ return null;
+ }
+ }
+
+ private void show_toolbar_cb (Gtk.Action action)
+ {
+ var toggle_action = (Gtk.ToggleAction) action;
+ settings.set_boolean ("show-toolbar", toggle_action.active);
+ }
+
+ private void background_changed_cb (Gtk.ColorButton widget)
+ {
+ Gdk.Color colour;
+ widget.get_color (out colour);
+ settings.set_string ("bgcolour", "#%04x%04x%04x".printf (colour.red, colour.green, colour.blue));
+ }
+
+ private void map_changed_cb (Gtk.ComboBox widget)
+ {
+ settings.set_string ("mapset", maps.nth_data (widget.active).name);
+ }
+
+ private void moved_cb ()
+ {
+ /* Start game once moved */
+ if (game_clock.get_seconds () == 0)
+ game_clock.start ();
+
+ update_ui ();
+
+ if (game_view.game.complete)
+ {
+ game_clock.stop ();
+
+ var seconds = game_clock.get_seconds ();
+
+ var p = highscores.add_time_score ((seconds / 60) * 1.0 + (seconds % 60) / 100.0);
+ var scores_dialog = new GnomeGamesSupport.ScoresDialog (window, highscores, _("Mahjongg Scores"));
+ scores_dialog.set_category_description (_("Map:"));
+ var title = _("Puzzle solved!");
+ var message = _("You didn't make the top ten, better luck next time.");
+ if (p == 1)
+ message = _("Your score is the best!");
+ else if (p > 1)
+ message = _("Your score has made the top ten.");
+ scores_dialog.set_message ("<b>%s</b>\n\n%s".printf (title, message));
+ scores_dialog.set_buttons (GnomeGamesSupport.ScoresButtons.QUIT_BUTTON | GnomeGamesSupport.ScoresButtons.NEW_GAME_BUTTON);
+ if (p > 0)
+ scores_dialog.set_hilight (p);
+
+ switch (scores_dialog.run ())
+ {
+ case Gtk.ResponseType.REJECT:
+ Gtk.main_quit ();
+ break;
+ default:
+ new_game ();
+ break;
+ }
+ scores_dialog.destroy ();
+ }
+ else if (!game_view.game.can_move)
+ {
+ var dialog = new Gtk.MessageDialog (window, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.INFO,
+ Gtk.ButtonsType.NONE,
+ "%s", _("There are no more moves."));
+ dialog.format_secondary_text (_("Each puzzle has at least one solution. You can undo your moves and try and find the solution for a time penalty, restart this game or start an new one."));
+ dialog.add_buttons (Gtk.Stock.UNDO, Gtk.ResponseType.REJECT,
+ _("_Restart"), Gtk.ResponseType.CANCEL,
+ _("_New game"), Gtk.ResponseType.ACCEPT);
+
+ dialog.set_default_response (Gtk.ResponseType.ACCEPT);
+ switch (dialog.run ())
+ {
+ case Gtk.ResponseType.REJECT:
+ undo_cb ();
+ break;
+ case Gtk.ResponseType.CANCEL:
+ restart_game ();
+ break;
+ default:
+ case Gtk.ResponseType.ACCEPT:
+ new_game ();
+ break;
+ }
+ dialog.destroy ();
+ }
+ }
+
+ private void properties_cb ()
+ {
+ if (preferences_dialog != null)
+ {
+ preferences_dialog.present ();
+ return;
+ }
+
+ preferences_dialog = new Gtk.Dialog.with_buttons (_("Mahjongg Preferences"),
+ window,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.Stock.CLOSE,
+ Gtk.ResponseType.CLOSE, null);
+ preferences_dialog.set_border_width (5);
+ var dialog_content_area = (Gtk.Box) preferences_dialog.get_content_area ();
+ dialog_content_area.set_spacing (2);
+ preferences_dialog.set_resizable (false);
+ preferences_dialog.set_default_response (Gtk.ResponseType.CLOSE);
+ preferences_dialog.response.connect (preferences_dialog_response_cb);
+
+ var top_table = new Gtk.Table (4, 1, false);
+ top_table.border_width = 5;
+ top_table.set_row_spacings (18);
+ top_table.set_col_spacings (0);
+
+ var frame = new GnomeGamesSupport.Frame (_("Tiles"));
+ top_table.attach_defaults (frame, 0, 1, 0, 1);
+
+ var table = new Gtk.Table (2, 2, false);
+ table.set_row_spacings (6);
+ table.set_col_spacings (12);
+
+ var label = new Gtk.Label.with_mnemonic (_("_Tile set:"));
+ label.set_alignment (0, 0.5f);
+ table.attach (label, 0, 1, 0, 1, Gtk.AttachOptions.FILL, 0, 0, 0);
+
+ var themes = load_themes ();
+ 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);
+ foreach (var theme in themes)
+ {
+ var tokens = theme.split (".", -1);
+ var name = tokens[0];
+
+ Gtk.TreeIter iter;
+ theme_store.append (out iter);
+ theme_store.set (iter, 0, name, 1, theme, -1);
+
+ if (theme == settings.get_string ("tileset"))
+ theme_combo.set_active_iter (iter);
+ }
+ theme_combo.changed.connect (theme_changed_cb);
+ table.attach_defaults (theme_combo, 1, 2, 0, 1);
+ label.set_mnemonic_widget (theme_combo);
+
+ frame.add (table);
+
+ frame = new GnomeGamesSupport.Frame (_("Maps"));
+ top_table.attach_defaults (frame, 0, 1, 1, 2);
+
+ table = new Gtk.Table (1, 2, false);
+ table.set_row_spacings (6);
+ table.set_col_spacings (12);
+
+ label = new Gtk.Label.with_mnemonic (_("_Select map:"));
+ label.set_alignment (0, 0.5f);
+ table.attach (label, 0, 1, 0, 1, Gtk.AttachOptions.FILL, 0, 0, 0);
+
+ var map_combo = new Gtk.ComboBox ();
+ var map_store = new Gtk.ListStore (2, typeof (string), typeof (string));
+ map_combo.model = map_store;
+ renderer = new Gtk.CellRendererText ();
+ map_combo.pack_start (renderer, true);
+ map_combo.add_attribute (renderer, "text", 0);
+ foreach (var map in maps)
+ {
+ var display_name = dpgettext2 (null, "mahjongg map name", map.name);
+
+ Gtk.TreeIter iter;
+ map_store.append (out iter);
+ map_store.set (iter, 0, display_name, 1, map, -1);
+
+ if (settings.get_string ("mapset") == map.name)
+ map_combo.set_active_iter (iter);
+ }
+ map_combo.changed.connect (map_changed_cb);
+ table.attach_defaults (map_combo, 1, 2, 0, 1);
+ label.set_mnemonic_widget (map_combo);
+
+ frame.add (table);
+
+ frame = new GnomeGamesSupport.Frame (_("Colors"));
+ top_table.attach_defaults (frame, 0, 1, 2, 3);
+
+ table = new Gtk.Table (1, 2, false);
+ table.set_row_spacings (6);
+ table.set_col_spacings (12);
+
+ label = new Gtk.Label.with_mnemonic (_("_Background color:"));
+ label.set_alignment (0, 0.5f);
+ table.attach (label, 0, 1, 0, 1, Gtk.AttachOptions.FILL, 0, 0, 0);
+
+ var widget = new Gtk.ColorButton ();
+ widget.set_color (game_view.background_color);
+ widget.color_set.connect (background_changed_cb);
+ table.attach_defaults (widget, 1, 2, 0, 1);
+ label.set_mnemonic_widget (widget);
+
+ frame.add (table);
+
+ dialog_content_area.pack_start (top_table, true, true, 0);
+
+ preferences_dialog.show_all ();
+ }
+
+ private void preferences_dialog_response_cb (Gtk.Dialog dialog, int response)
+ {
+ preferences_dialog.destroy ();
+ preferences_dialog = null;
+ }
+
+ private List<string> load_themes ()
+ {
+ List<string> themes = null;
+
+ var path = GnomeGamesSupport.runtime_get_directory (GnomeGamesSupport.RuntimeDirectory.GAME_PIXMAP_DIRECTORY);
+ Dir dir;
+ try
+ {
+ dir = Dir.open (path);
+ }
+ catch (FileError e)
+ {
+ return themes;
+ }
+
+ while (true)
+ {
+ var s = dir.read_name ();
+ if (s == null)
+ break;
+
+ if (s.has_suffix (".xpm") || s.has_suffix (".svg") || s.has_suffix (".gif") ||
+ s.has_suffix (".png") || s.has_suffix (".jpg") || s.has_suffix (".xbm"))
+ themes.append (s);
+ }
+
+ return themes;
+ }
+
+ private void hint_cb ()
+ {
+ var matches = game_view.game.find_matches (game_view.game.selected_tile);
+ var n_matches = matches.length ();
+
+ /* No match, just flash the selected tile */
+ if (n_matches == 0)
+ {
+ if (game_view.game.selected_tile == null)
+ return;
+ game_view.game.set_hint (game_view.game.selected_tile, null);
+ }
+ else
+ {
+ var n = Random.int_range (0, (int) n_matches);
+ var match = matches.nth_data (n);
+ game_view.game.set_hint (match.tile0, match.tile1);
+ }
+
+ /* 30s penalty */
+ game_clock.start ();
+ game_clock.add_seconds (30);
+ }
+
+ private void about_cb ()
+ {
+ string[] authors =
+ {
+ _("Main game:"),
+ "Francisco Bustamante",
+ "Max Watson",
+ "Heinz Hempe",
+ "Michael Meeks",
+ "Philippe Chavin",
+ "Callum McKenzie",
+ "Robert Ancell",
+ "",
+ _("Maps:"),
+ "Rexford Newbould",
+ "Krzysztof Foltman",
+ null
+ };
+
+ string[] artists =
+ {
+ _("Tiles:"),
+ "Jonathan Buzzard",
+ "Jim Evans",
+ "Richard Hoelscher",
+ "Gonzalo Odiard",
+ "Max Watson",
+ null
+ };
+
+ string[] documenters =
+ {
+ "Eric Baudais",
+ null
+ };
+
+ Gtk.show_about_dialog (window,
+ "program-name", _("Mahjongg"),
+ "version", VERSION,
+ "comments",
+ _("A matching game played with Mahjongg tiles.\n\nMahjongg is a part of GNOME GnomeGamesSupport."),
+ "copyright", "Copyright \xc2\xa9 1998-2008 Free Software Foundation, Inc.",
+ "license", GnomeGamesSupport.get_license (_("Mahjongg")),
+ "wrap-license", true,
+ "authors", authors,
+ "artists", artists,
+ "documenters", documenters,
+ "translator-credits", _("translator-credits"),
+ "logo-icon-name", "gnome-mahjongg",
+ "website", "http://www.gnome.org/projects/gnome-games",
+ "website-label", _("GNOME Games web site"),
+ null);
+ }
+
+ private void pause_cb (GnomeGamesSupport.PauseAction action)
+ {
+ game_view.paused = action.get_is_paused ();
+ game_view.game.set_hint (null, null);
+ game_view.game.selected_tile = null;
+
+ if (game_view.paused)
+ game_clock.stop ();
+ else
+ game_clock.start ();
+
+ update_ui ();
+ }
+
+ private void scores_cb (Gtk.Action action)
+ {
+ var map_scores_dialog = new GnomeGamesSupport.ScoresDialog (window, highscores, _("Mahjongg Scores"));
+ map_scores_dialog.set_category_description (_("Map:"));
+ map_scores_dialog.run ();
+ map_scores_dialog.destroy ();
+ }
+
+ private void new_game_cb (Gtk.Action action)
+ {
+ new_game ();
+ }
+
+ private void restart_game_cb (Gtk.Action action)
+ {
+ game_view.game.reset ();
+ game_view.queue_draw ();
+ }
+
+ private bool window_delete_event_cb (Gdk.EventAny event)
+ {
+ Gtk.main_quit ();
+ return true;
+ }
+
+ private void quit_cb ()
+ {
+ Gtk.main_quit ();
+ }
+
+ private void redo_cb (Gtk.Action action)
+ {
+ if (game_view.paused)
+ return;
+
+ game_view.game.redo ();
+ update_ui ();
+ }
+
+ private void undo_cb ()
+ {
+ game_view.game.undo ();
+ update_ui ();
+ }
+
+ private void restart_game ()
+ {
+ game_view.game.reset ();
+
+ /* Prepare clock */
+ game_clock.stop ();
+ game_clock.reset ();
+
+ update_ui ();
+ }
+
+ private void new_game ()
+ {
+ Map? map = null;
+ foreach (var m in maps)
+ {
+ if (m.name == settings.get_string ("mapset"))
+ {
+ map = m;
+ break;
+ }
+ }
+ if (map == null)
+ map = maps.nth_data (0);
+
+ game_view.game = new Game (map);
+ game_view.game.moved.connect (moved_cb);
+ highscores.set_category (game_view.game.map.score_name);
+
+ /* Set window title */
+ var display_name = dpgettext2 (null, "mahjongg map name", game_view.game.map.name);
+ /* Translators: This is the window title for Mahjongg which contains the map name, e.g. 'Mahjongg - Red Dragon' */
+ window.set_title (_("Mahjongg - %s").printf (display_name));
+
+ /* Prepare clock */
+ game_clock.stop ();
+ game_clock.reset ();
+
+ update_ui ();
+ }
+
+ private void help_cb (Gtk.Action action)
+ {
+ GnomeGamesSupport.help_display (window, "mahjongg", null);
+ }
+
+ private const Gtk.ActionEntry actions[] =
+ {
+ {"GameMenu", null, N_("_Game")},
+ {"SettingsMenu", null, N_("_Settings")},
+ {"HelpMenu", null, N_("_Help")},
+ {"NewGame", GnomeGamesSupport.STOCK_NEW_GAME, null, null, N_("Start a new game"), new_game_cb},
+ {"RestartGame", GnomeGamesSupport.STOCK_RESTART_GAME, null, null, N_("Restart the current game"), restart_game_cb},
+ {"UndoMove", GnomeGamesSupport.STOCK_UNDO_MOVE, null, null, N_("Undo the last move"), undo_cb},
+ {"RedoMove", GnomeGamesSupport.STOCK_REDO_MOVE, null, null, N_("Redo the last move"), redo_cb},
+ {"Hint", GnomeGamesSupport.STOCK_HINT, null, null, N_("Show a hint"), hint_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, properties_cb},
+ {"Contents", GnomeGamesSupport.STOCK_CONTENTS, null, null, null, help_cb},
+ {"About", Gtk.Stock.ABOUT, null, null, null, about_cb}
+ };
+
+ private const Gtk.ToggleActionEntry toggle_actions[] =
+ {
+ {"ShowToolbar", null, N_("_Toolbar"), null, N_("Show or hide the toolbar"), show_toolbar_cb}
+ };
+
+ private const string ui_description =
+ "<ui>" +
+ " <menubar name='MainMenu'>" +
+ " <menu action='GameMenu'>" +
+ " <menuitem action='NewGame'/>" +
+ " <menuitem action='RestartGame'/>" +
+ " <menuitem action='PauseGame'/>" +
+ " <separator/>" +
+ " <menuitem action='UndoMove'/>" +
+ " <menuitem action='RedoMove'/>" +
+ " <menuitem action='Hint'/>" +
+ " <separator/>" +
+ " <menuitem action='Scores'/>" +
+ " <separator/>" +
+ " <menuitem action='Quit'/>" +
+ " </menu>" +
+ " <menu action='SettingsMenu'>" +
+ " <menuitem action='Fullscreen'/>" +
+ " <menuitem action='ShowToolbar'/>" +
+ " <separator/>" +
+ " <menuitem action='Preferences'/>" +
+ " </menu>" +
+ " <menu action='HelpMenu'>" +
+ " <menuitem action='Contents'/>" +
+ " <menuitem action='About'/>" +
+ " </menu>" +
+ " </menubar>" +
+ " <toolbar name='Toolbar'>" +
+ " <toolitem action='NewGame'/>" +
+ " <toolitem action='UndoMove'/>" +
+ " <toolitem action='Hint'/>" +
+ " <toolitem action='PauseGame'/>" +
+ " <toolitem action='LeaveFullscreen'/>" +
+ " </toolbar>" +
+ "</ui>";
+
+ private void create_menus (Gtk.UIManager ui_manager)
+ {
+ var action_group = new Gtk.ActionGroup ("group");
+
+ action_group.set_translation_domain (GETTEXT_PACKAGE);
+ action_group.add_actions (actions, this);
+ action_group.add_toggle_actions (toggle_actions, this);
+
+ ui_manager.insert_action_group (action_group, 0);
+ try
+ {
+ ui_manager.add_ui_from_string (ui_description, -1);
+ }
+ catch (Error e)
+ {
+ }
+ restart_action = action_group.get_action ("RestartGame");
+ pause_action = new GnomeGamesSupport.PauseAction ("PauseGame");
+ pause_action.is_important = true;
+ pause_action.state_changed.connect (pause_cb);
+ action_group.add_action_with_accel (pause_action, null);
+ hint_action = action_group.get_action ("Hint");
+ hint_action.is_important = true;
+ undo_action = action_group.get_action ("UndoMove");
+ undo_action.is_important = true;
+ redo_action = action_group.get_action ("RedoMove");
+ var show_toolbar_action = (Gtk.ToggleAction) action_group.get_action ("ShowToolbar");
+
+ fullscreen_action = new GnomeGamesSupport.FullscreenAction ("Fullscreen", window);
+ action_group.add_action_with_accel (fullscreen_action, null);
+
+ leave_fullscreen_action = new GnomeGamesSupport.FullscreenAction ("LeaveFullscreen", window);
+ action_group.add_action_with_accel (leave_fullscreen_action, null);
+
+ show_toolbar_action.active = settings.get_boolean ("show-toolbar");
+ }
+
+ private void load_maps ()
+ {
+ maps = null;
+
+ /* Add the builtin map */
+ maps.append (new Map.builtin ());
+
+ var path = GnomeGamesSupport.runtime_get_directory (GnomeGamesSupport.RuntimeDirectory.GAME_GAMES_DIRECTORY);
+ var filelist = new GnomeGamesSupport.FileList ("*.map", ".", path, null);
+ for (var i = 0; i < filelist.length (); i++)
+ {
+ var filename = filelist.get_nth (i);
+
+ var loader = new MapLoader ();
+ try
+ {
+ loader.load (filename);
+ }
+ catch (Error e)
+ {
+ warning ("Could not load map %s: %s\n", filename, e.message);
+ continue;
+ }
+ foreach (var map in loader.maps)
+ maps.append (map);
+ }
+ }
+
+ public static int main (string[] args)
+ {
+ Gtk.init (ref args);
+
+ var context = new OptionContext ("");
+ context.set_translation_domain (GETTEXT_PACKAGE);
+ context.add_group (Gtk.get_option_group (true));
+
+ try
+ {
+ context.parse (ref args);
+ }
+ catch (Error e)
+ {
+ stdout.printf ("%s\n", e.message);
+ return Posix.EXIT_FAILURE;
+ }
+
+ if (!GnomeGamesSupport.runtime_init ("mahjongg"))
+ return Posix.EXIT_FAILURE;
+#if ENABLE_SETGID
+ GnomeGamesSupport.setgid_io_init ();
+#endif
+ GnomeGamesSupport.stock_init ();
+
+ Environment.set_application_name (_("Mahjongg"));
+ Gtk.Window.set_default_icon_name ("gnome-mahjongg");
+
+ var app = new Mahjongg ();
+ app.start ();
+
+ Gtk.main ();
+
+ Settings.sync();
+
+ GnomeGamesSupport.runtime_shutdown ();
+
+ return Posix.EXIT_SUCCESS;
+ }
+}
diff --git a/mahjongg/src/map.vala b/mahjongg/src/map.vala
new file mode 100644
index 0000000..acc00fb
--- /dev/null
+++ b/mahjongg/src/map.vala
@@ -0,0 +1,358 @@
+public class Slot
+{
+ public int x;
+ public int y;
+ public int layer;
+
+ public Slot (int x, int y, int layer)
+ {
+ this.x = x;
+ this.y = y;
+ this.layer = layer;
+ }
+}
+
+private static int compare_slots (Slot a, Slot b)
+{
+ /* Sort lowest to highest */
+ var dl = a.layer - b.layer;
+ if (dl != 0)
+ return dl;
+
+ /* Sort diagonally, top left to bottom right */
+ var dx = a.x - b.x;
+ var dy = a.y - b.y;
+ if (dx > dy)
+ return -1;
+ if (dx < dy)
+ return 1;
+
+ return 0;
+}
+
+public class Map
+{
+ public string? name = null;
+ public string? score_name = null;
+ public List<Slot> slots = null;
+
+ public Map.test ()
+ {
+ name = dpgettext2 (null, "mahjongg map name", "Test");
+ score_name = "test";
+ slots.append (new Slot (0, 0, 0));
+ slots.append (new Slot (2, 0, 0));
+ slots.append (new Slot (2, 0, 1));
+ slots.append (new Slot (4, 0, 0));
+ slots.append (new Slot (0, 2, 0));
+ slots.append (new Slot (2, 2, 0));
+ slots.append (new Slot (2, 2, 1));
+ slots.append (new Slot (4, 2, 0));
+ }
+
+ public Map.builtin ()
+ {
+ name = dpgettext2 (null, "mahjongg map name", "Easy");
+ score_name = "easy";
+ slots.append (new Slot (13, 7, 4));
+ slots.append (new Slot (12, 8, 3));
+ slots.append (new Slot (14, 8, 3));
+ slots.append (new Slot (12, 6, 3));
+ slots.append (new Slot (14, 6, 3));
+ slots.append (new Slot (10, 10, 2));
+ slots.append (new Slot (12, 10, 2));
+ slots.append (new Slot (14, 10, 2));
+ slots.append (new Slot (16, 10, 2));
+ slots.append (new Slot (10, 8, 2));
+ slots.append (new Slot (12, 8, 2));
+ slots.append (new Slot (14, 8, 2));
+ slots.append (new Slot (16, 8, 2));
+ slots.append (new Slot (10, 6, 2));
+ slots.append (new Slot (12, 6, 2));
+ slots.append (new Slot (14, 6, 2));
+ slots.append (new Slot (16, 6, 2));
+ slots.append (new Slot (10, 4, 2));
+ slots.append (new Slot (12, 4, 2));
+ slots.append (new Slot (14, 4, 2));
+ slots.append (new Slot (16, 4, 2));
+ slots.append (new Slot (8, 12, 1));
+ slots.append (new Slot (10, 12, 1));
+ slots.append (new Slot (12, 12, 1));
+ slots.append (new Slot (14, 12, 1));
+ slots.append (new Slot (16, 12, 1));
+ slots.append (new Slot (18, 12, 1));
+ slots.append (new Slot (8, 10, 1));
+ slots.append (new Slot (10, 10, 1));
+ slots.append (new Slot (12, 10, 1));
+ slots.append (new Slot (14, 10, 1));
+ slots.append (new Slot (16, 10, 1));
+ slots.append (new Slot (18, 10, 1));
+ slots.append (new Slot (8, 8, 1));
+ slots.append (new Slot (10, 8, 1));
+ slots.append (new Slot (12, 8, 1));
+ slots.append (new Slot (14, 8, 1));
+ slots.append (new Slot (16, 8, 1));
+ slots.append (new Slot (18, 8, 1));
+ slots.append (new Slot (8, 6, 1));
+ slots.append (new Slot (10, 6, 1));
+ slots.append (new Slot (12, 6, 1));
+ slots.append (new Slot (14, 6, 1));
+ slots.append (new Slot (16, 6, 1));
+ slots.append (new Slot (18, 6, 1));
+ slots.append (new Slot (8, 4, 1));
+ slots.append (new Slot (10, 4, 1));
+ slots.append (new Slot (12, 4, 1));
+ slots.append (new Slot (14, 4, 1));
+ slots.append (new Slot (16, 4, 1));
+ slots.append (new Slot (18, 4, 1));
+ slots.append (new Slot (8, 2, 1));
+ slots.append (new Slot (10, 2, 1));
+ slots.append (new Slot (12, 2, 1));
+ slots.append (new Slot (14, 2, 1));
+ slots.append (new Slot (16, 2, 1));
+ slots.append (new Slot (18, 2, 1));
+ slots.append (new Slot (2, 14, 0));
+ slots.append (new Slot (4, 14, 0));
+ slots.append (new Slot (6, 14, 0));
+ slots.append (new Slot (8, 14, 0));
+ slots.append (new Slot (10, 14, 0));
+ slots.append (new Slot (12, 14, 0));
+ slots.append (new Slot (14, 14, 0));
+ slots.append (new Slot (16, 14, 0));
+ slots.append (new Slot (18, 14, 0));
+ slots.append (new Slot (20, 14, 0));
+ slots.append (new Slot (22, 14, 0));
+ slots.append (new Slot (24, 14, 0));
+ slots.append (new Slot (6, 12, 0));
+ slots.append (new Slot (8, 12, 0));
+ slots.append (new Slot (10, 12, 0));
+ slots.append (new Slot (12, 12, 0));
+ slots.append (new Slot (14, 12, 0));
+ slots.append (new Slot (16, 12, 0));
+ slots.append (new Slot (18, 12, 0));
+ slots.append (new Slot (20, 12, 0));
+ slots.append (new Slot (4, 10, 0));
+ slots.append (new Slot (6, 10, 0));
+ slots.append (new Slot (8, 10, 0));
+ slots.append (new Slot (10, 10, 0));
+ slots.append (new Slot (12, 10, 0));
+ slots.append (new Slot (14, 10, 0));
+ slots.append (new Slot (16, 10, 0));
+ slots.append (new Slot (18, 10, 0));
+ slots.append (new Slot (20, 10, 0));
+ slots.append (new Slot (22, 10, 0));
+ slots.append (new Slot (0, 7, 0));
+ slots.append (new Slot (2, 8, 0));
+ slots.append (new Slot (4, 8, 0));
+ slots.append (new Slot (6, 8, 0));
+ slots.append (new Slot (8, 8, 0));
+ slots.append (new Slot (10, 8, 0));
+ slots.append (new Slot (12, 8, 0));
+ slots.append (new Slot (14, 8, 0));
+ slots.append (new Slot (16, 8, 0));
+ slots.append (new Slot (18, 8, 0));
+ slots.append (new Slot (20, 8, 0));
+ slots.append (new Slot (22, 8, 0));
+ slots.append (new Slot (24, 8, 0));
+ slots.append (new Slot (2, 6, 0));
+ slots.append (new Slot (4, 6, 0));
+ slots.append (new Slot (6, 6, 0));
+ slots.append (new Slot (8, 6, 0));
+ slots.append (new Slot (10, 6, 0));
+ slots.append (new Slot (12, 6, 0));
+ slots.append (new Slot (14, 6, 0));
+ slots.append (new Slot (16, 6, 0));
+ slots.append (new Slot (18, 6, 0));
+ slots.append (new Slot (20, 6, 0));
+ slots.append (new Slot (22, 6, 0));
+ slots.append (new Slot (24, 6, 0));
+ slots.append (new Slot (4, 4, 0));
+ slots.append (new Slot (6, 4, 0));
+ slots.append (new Slot (8, 4, 0));
+ slots.append (new Slot (10, 4, 0));
+ slots.append (new Slot (12, 4, 0));
+ slots.append (new Slot (14, 4, 0));
+ slots.append (new Slot (16, 4, 0));
+ slots.append (new Slot (18, 4, 0));
+ slots.append (new Slot (20, 4, 0));
+ slots.append (new Slot (22, 4, 0));
+ slots.append (new Slot (6, 2, 0));
+ slots.append (new Slot (8, 2, 0));
+ slots.append (new Slot (10, 2, 0));
+ slots.append (new Slot (12, 2, 0));
+ slots.append (new Slot (14, 2, 0));
+ slots.append (new Slot (16, 2, 0));
+ slots.append (new Slot (18, 2, 0));
+ slots.append (new Slot (20, 2, 0));
+ slots.append (new Slot (2, 0, 0));
+ slots.append (new Slot (4, 0, 0));
+ slots.append (new Slot (6, 0, 0));
+ slots.append (new Slot (8, 0, 0));
+ slots.append (new Slot (10, 0, 0));
+ slots.append (new Slot (12, 0, 0));
+ slots.append (new Slot (14, 0, 0));
+ slots.append (new Slot (16, 0, 0));
+ slots.append (new Slot (18, 0, 0));
+ slots.append (new Slot (20, 0, 0));
+ slots.append (new Slot (22, 0, 0));
+ slots.append (new Slot (24, 0, 0));
+ slots.append (new Slot (26, 7, 0));
+ slots.append (new Slot (28, 7, 0));
+ }
+
+ public uint width
+ {
+ get
+ {
+ var w = 0;
+ foreach (var slot in slots)
+ {
+ if (slot.x > w)
+ w = slot.x;
+ }
+
+ /* Width is x location of right most tile and the width of that tile (2 units) */
+ return w + 2;
+ }
+ }
+
+ public uint height
+ {
+ get
+ {
+ var h = 0;
+ foreach (var slot in slots)
+ {
+ if (slot.y > h)
+ h = slot.y;
+ }
+
+ /* Height is x location of bottom most tile and the height of that tile (2 units) */
+ return h + 2;
+ }
+ }
+}
+
+public class MapLoader
+{
+ public List<Map> maps = null;
+ private Map map;
+ private int layer_z = 0;
+
+ public void load (string filename) throws Error
+ {
+ string data;
+ size_t length;
+ FileUtils.get_contents (filename, out data, out length);
+
+ var parser = MarkupParser ();
+ parser.start_element = start_element_cb;
+ parser.end_element = end_element_cb;
+ parser.text = null;
+ parser.passthrough = null;
+ parser.error = null;
+ var parse_context = new MarkupParseContext (parser, 0, this, null);
+ try
+ {
+ parse_context.parse (data, (ssize_t) length);
+ }
+ catch (MarkupError e)
+ {
+ }
+ }
+
+ private string? get_attribute (string[] attribute_names, string[] attribute_values, string name, string? default = null)
+ {
+ for (var i = 0; attribute_names[i] != null; i++)
+ {
+ if (attribute_names[i].down() == name)
+ return attribute_values[i];
+ }
+
+ return default;
+ }
+
+ private double get_attribute_d (string[] attribute_names, string[] attribute_values, string name, double default = 0.0)
+ {
+ var a = get_attribute (attribute_names, attribute_values, name);
+ if (a == null)
+ return default;
+ else
+ return double.parse (a);
+ }
+
+ private void start_element_cb (MarkupParseContext context, string element_name, string[] attribute_names, string[] attribute_values) throws MarkupError
+ {
+ /* Identify the tag. */
+ switch (element_name.down ())
+ {
+ case "mahjongg":
+ break;
+
+ case "map":
+ map = new Map ();
+ map.name = get_attribute (attribute_names, attribute_values, "name", "");
+ map.score_name = get_attribute (attribute_names, attribute_values, "scorename", "");
+ break;
+
+ case "layer":
+ layer_z = (int) get_attribute_d (attribute_names, attribute_values, "z");
+ break;
+
+ case "row":
+ var x1 = (int) (get_attribute_d (attribute_names, attribute_values, "left") * 2);
+ var x2 = (int) (get_attribute_d (attribute_names, attribute_values, "right") * 2);
+ var y = (int) (get_attribute_d (attribute_names, attribute_values, "y") * 2);
+ var z = (int) get_attribute_d (attribute_names, attribute_values, "z", layer_z);
+ for (; x1 <= x2; x1 += 2)
+ map.slots.append (new Slot (x1, y, z));
+ break;
+
+ case "column":
+ var x = (int) (get_attribute_d (attribute_names, attribute_values, "x") * 2);
+ var y1 = (int) (get_attribute_d (attribute_names, attribute_values, "top") * 2);
+ var y2 = (int) (get_attribute_d (attribute_names, attribute_values, "bottom") * 2);
+ var z = (int) get_attribute_d (attribute_names, attribute_values, "z", layer_z);
+ for (; y1 <= y2; y1 += 2)
+ map.slots.append (new Slot (x, y1, z));
+ break;
+
+ case "block":
+ var x1 = (int) (get_attribute_d (attribute_names, attribute_values, "left") * 2);
+ var x2 = (int) (get_attribute_d (attribute_names, attribute_values, "right") * 2);
+ var y1 = (int) (get_attribute_d (attribute_names, attribute_values, "top") * 2);
+ var y2 = (int) (get_attribute_d (attribute_names, attribute_values, "bottom") * 2);
+ var z = (int) get_attribute_d (attribute_names, attribute_values, "z", layer_z);
+ for (; x1 <= x2; x1 += 2)
+ for (var y = y1; y <= y2; y += 2)
+ map.slots.append (new Slot (x1, y, z));
+ break;
+
+ case "tile":
+ var x = (int) (get_attribute_d (attribute_names, attribute_values, "x") * 2);
+ var y = (int) (get_attribute_d (attribute_names, attribute_values, "y") * 2);
+ var z = (int) get_attribute_d (attribute_names, attribute_values, "z", layer_z);
+ map.slots.append (new Slot (x, y, z));
+ break;
+ }
+ }
+
+ private void end_element_cb (MarkupParseContext context, string element_name) throws MarkupError
+ {
+ switch (element_name.down ())
+ {
+ case "map":
+ var n_slots = map.slots.length ();
+ if (map.name != null && map.score_name != null && n_slots <= 144 && n_slots % 2 == 0)
+ maps.append (map);
+ else
+ warning ("Invalid map");
+ map = null;
+ break;
+
+ case "layer":
+ layer_z = 0;
+ break;
+ }
+ }
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e151791..f39954e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -121,12 +121,13 @@ lightsoff/lightsoff.schemas.in
[type: gettext/glade]lightsoff/data/lightsoff.ui
[type: gettext/glade]lightsoff/data/settings.ui
lightsoff/src/About.js
-mahjongg/drawing.c
-mahjongg/mahjongg.c
-mahjongg/mahjongg.desktop.in.in
-mahjongg/org.gnome.mahjongg.gschema.xml.in
-mahjongg/maps.c
-mahjongg/translatable_game_names.h
+mahjongg/src/game.vala
+mahjongg/src/game-view.vala
+mahjongg/src/mahjongg.vala
+mahjongg/src/map.vala
+mahjongg/data/mahjongg.desktop.in.in
+mahjongg/data/org.gnome.mahjongg.gschema.xml.in
+mahjongg/data/translatable_game_names.h
[type: gettext/glade]swell-foop/data/swell-foop.ui
[type: gettext/glade]swell-foop/data/settings.ui
swell-foop/swell-foop.desktop.in.in
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 766ce0f..1b36745 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -14,6 +14,10 @@ gtali/gtali.desktop.in
iagno/iagno.desktop.in
libgames-support/org.gnome.Games.WindowState.gschema.xml.in
lightsoff/lightsoff.desktop.in
-mahjongg/mahjongg.desktop.in
+mahjongg/src/game.c
+mahjongg/src/game-view.c
+mahjongg/src/mahjongg.c
+mahjongg/src/map.c
+mahjongg/data/mahjongg.desktop.in
swell-foop/swell-foop-c.desktop.in
swell-foop/swell-foop.desktop.in
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]