[gnome-robots] rewrite window and game area modules in Vala



commit 26233e41f9906d4d54f98d85a816d53472451ae3
Author: Andrey Kutejko <andy128k gmail com>
Date:   Sun Aug 30 10:31:47 2020 +0200

    rewrite window and game area modules in Vala

 src/game-area.vala |  98 +++++++++
 src/game.vala      |   4 +-
 src/gnome-robots.c | 600 -----------------------------------------------------
 src/gnome-robots.h |  25 ---
 src/graphics.vala  |  16 --
 src/keyboard.c     | 108 ----------
 src/keyboard.h     |  17 --
 src/main.vapi      |  18 --
 src/meson.build    |   9 +-
 src/robots.vala    | 429 ++++++++++++++++++++++++++++++++++++++
 10 files changed, 532 insertions(+), 792 deletions(-)
---
diff --git a/src/game-area.vala b/src/game-area.vala
new file mode 100644
index 0000000..3675d02
--- /dev/null
+++ b/src/game-area.vala
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2020 Andrey Kutejko <andy128k gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * For more details see the file COPYING.
+ */
+
+using Gtk;
+using Cairo;
+
+public class GameArea : DrawingArea {
+
+    const int MINIMUM_TILE_WIDTH = 8;
+    const int MINIMUM_TILE_HEIGHT = 8;
+
+    private GestureMultiPress click_controller;
+    private EventControllerMotion motion_controller;
+    private Game game;
+
+    public GameArea (Game game) {
+        this.game = game;
+
+        add_events (Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK | 
Gdk.EventMask.POINTER_MOTION_MASK);
+        configure_event.connect (event => resize_cb (event));
+        draw.connect ((cr) => draw_cb (cr));
+
+        click_controller = new GestureMultiPress (this);
+        click_controller.pressed.connect ((n_pressed, x, y) => mouse_cb (n_pressed, x, y));
+
+        motion_controller = new EventControllerMotion (this);
+        motion_controller.motion.connect ((x, y) => move_cb (x, y));
+
+        set_size_request (MINIMUM_TILE_WIDTH * game.arena.width,
+                          MINIMUM_TILE_HEIGHT * game.arena.height);
+    }
+
+    private bool resize_cb (Gdk.EventConfigure e) {
+        int trial_width = e.width / game.arena.width;
+        int trial_height = e.height / game.arena.height;
+
+        if (trial_width != tile_width || trial_height != tile_height) {
+            tile_width = trial_width;
+            tile_height = trial_height;
+            rerender_needed = true;
+        }
+
+        return false;
+    }
+
+    private bool draw_cb (Context cr) {
+        for (int j = 0; j < game.arena.height; j++) {
+            for (int i = 0; i < game.arena.width; i++) {
+                draw_object (i, j, game.check_location (i, j), cr);
+            }
+        }
+
+        draw_bubble (cr);
+
+        return true;
+    }
+
+    private void mouse_cb (int n_press, double x, double y) {
+        if (game.get_state () != Game.State.PLAYING) {
+            return;
+        }
+
+        int dx, dy;
+        game.get_dir ((int)x, (int)y, out dx, out dy);
+
+        if (game.player_move (dx, dy)) {
+            game.move_robots ();
+        }
+    }
+
+    private void move_cb (double x, double y) {
+        var window = get_window ();
+        if (game.get_state () != Game.State.PLAYING) {
+            set_cursor_default (window);
+        } else {
+            int dx, dy;
+            game.get_dir ((int)x, (int)y, out dx, out dy);
+            set_cursor_by_direction (window, dx, dy);
+        }
+    }
+}
+
diff --git a/src/game.vala b/src/game.vala
index 477aa3c..631b2d8 100644
--- a/src/game.vala
+++ b/src/game.vala
@@ -15,7 +15,7 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  *
  * For more details see the file COPYING.
-*/
+ */
 
 using Games;
 
@@ -216,7 +216,7 @@ public class Game {
      * Returns:
      * type of object if present or ObjectType.NONE
      **/
-    public int check_location (int x, int y) {
+    public ObjectType check_location (int x, int y) {
         return arena.@get (x, y);
     }
 
diff --git a/src/graphics.vala b/src/graphics.vala
index bedc22f..1f95f8c 100644
--- a/src/graphics.vala
+++ b/src/graphics.vala
@@ -85,22 +85,6 @@ void render_graphics () {
     rerender_needed = false;
 }
 
-public bool resize_cb (Widget w, EventConfigure e) {
-  int trial_width;
-  int trial_height;
-
-  trial_width = e.width / GAME_WIDTH;
-  trial_height = e.height / GAME_HEIGHT;
-
-  if ((trial_width != tile_width) || (trial_height != tile_height)) {
-    tile_width = trial_width;
-    tile_height = trial_height;
-    rerender_needed = true;
-  }
-
-  return false;
-}
-
 /**
  * Loads all of the 'speech bubble' graphics
  **/
diff --git a/src/meson.build b/src/meson.build
index e3b42bd..9603e42 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -8,7 +8,6 @@ config_header = configure_file(
 
 vala_sources = files(
     'config.vapi',
-    'main.vapi',
 
     'image-suffix-list.vala',
     'file-list.vala',
@@ -22,6 +21,8 @@ vala_sources = files(
     'graphics.vala',
     'arena.vala',
     'game.vala',
+    'game-area.vala',
+    'robots.vala',
 )
 
 vala_lib = static_library('riiv',
@@ -43,10 +44,6 @@ riiv_dependency = declare_dependency(
     link_with: vala_lib,
 )
 
-sources = files(
-    'gnome-robots.c',
-    'keyboard.c',
-)
 resources = gnome.compile_resources(
     'resources',
     '@0  gresource xml'.format(meson.project_name()),
@@ -55,7 +52,7 @@ resources = gnome.compile_resources(
 
 executable(
     meson.project_name(),
-    sources + resources,
+    resources,
     config_header,
     dependencies: [
         riiv_dependency,
diff --git a/src/robots.vala b/src/robots.vala
new file mode 100644
index 0000000..deacdcb
--- /dev/null
+++ b/src/robots.vala
@@ -0,0 +1,429 @@
+/*
+ * Gnome Robots II
+ * written by Mark Rae <m rae inpharmatica co uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more details see the file COPYING.
+ */
+
+using Gtk;
+using Cairo;
+
+const string KEY_GEOMETRY_GROUP = "geometry";
+
+ApplicationWindow window = null;
+int window_width = 0;
+int window_height = 0;
+bool window_is_maximized = false;
+GameArea game_area = null;
+Games.Scores.Context highscores;
+GLib.Settings settings;
+
+const GLib.ActionEntry[] app_entries = {
+    { "new-game",         new_game_cb    },
+    { "preferences",      preferences_cb },
+    { "scores",           scores_cb,     },
+    { "help",             help_cb,       },
+    { "about",            about_cb,      },
+    { "quit",             quit_cb,       },
+};
+
+const GLib.ActionEntry[] win_entries = {
+    { "random-teleport",  random_teleport_cb },
+    { "safe-teleport",    safe_teleport_cb   },
+    { "wait",             wait_cb            },
+};
+
+int safe_teleports = 0;
+Label safe_teleports_label;
+HeaderBar headerbar;
+
+EventControllerKey key_controller;
+uint control_keys[12];
+
+public void set_move_action_sensitivity (bool state) {
+    var action1 = (SimpleAction) window.lookup_action ("random-teleport");
+    action1.set_enabled (state);
+
+    var action2 = (SimpleAction) window.lookup_action ("safe-teleport");
+    action2.set_enabled (state && safe_teleports > 0);
+
+    var action3 = (SimpleAction) window.lookup_action ("wait");
+    action3.set_enabled (state);
+}
+
+public void update_game_status (int score, int current_level, int safes) {
+    /* Window subtitle. The first %d is the level, the second is the score. \t creates a tab. */
+    var subtitle = _("Level: %d\tScore: %d").printf (current_level, score);
+    headerbar.set_subtitle (subtitle);
+
+    safe_teleports = safes;
+
+    var action = (SimpleAction) window.lookup_action ("safe-teleport");
+    action.set_enabled (safe_teleports > 0);
+
+    /* Second line of safe teleports button label. %d is the number of teleports remaining. */
+    var remaining_teleports_text = _("(Remaining: %d)").printf (safe_teleports);
+    /* First line of safe teleports button label. */
+    var button_text = "%s\n<small>%s</small>".printf (_("Teleport _Safely"), remaining_teleports_text);
+    safe_teleports_label.set_markup_with_mnemonic (button_text);
+}
+
+public string? category_name_from_key (string key) {
+    switch (key) {
+    case "classic_robots":
+        return N_("Classic robots");
+    case "classic_robots-safe":
+        return N_("Classic robots with safe moves");
+    case "classic_robots-super-safe":
+        return N_("Classic robots with super-safe moves");
+    case "nightmare":
+        return N_("Nightmare");
+    case "nightmare-safe":
+        return N_("Nightmare with safe moves");
+    case "nightmare-super-safe":
+        return N_("Nightmare with super-safe moves");
+    case "robots2":
+        return N_("Robots2");
+    case "robots2-safe":
+        return N_("Robots2 with safe moves");
+    case "robots2-super-safe":
+        return N_("Robots2 with super-safe moves");
+    case "robots2_easy":
+        return N_("Robots2 easy");
+    case "robots2_easy-safe":
+        return N_("Robots2 easy with safe moves");
+    case "robots2_easy-super-safe":
+        return N_("Robots2 easy with super-safe moves");
+    case "robots_with_safe_teleport":
+        return N_("Robots with safe teleport");
+    case "robots_with_safe_teleport-safe":
+        return N_("Robots with safe teleport with safe moves");
+    case "robots_with_safe_teleport-super-safe":
+        return N_("Robots with safe teleport with super-safe moves");
+    default:
+        return null;
+    }
+}
+
+void preferences_cb () {
+    show_properties_dialog ();
+}
+
+void scores_cb () {
+    game.show_scores ();
+}
+
+void help_cb () {
+    try {
+        show_uri_on_window (window, "help:gnome-robots", get_current_event_time ());
+    } catch (Error error) {
+        warning ("Failed to show help: %s", error.message);
+    }
+}
+
+void about_cb () {
+  string[] authors = { "Mark Rae <m rae inpharmatica co uk>" };
+
+  string[] artists = { "Kirstie Opstad <K Opstad ed ac uk>", "Rasoul M.P. Aghdam (player death sound)" };
+
+  string[] documenters = { "Aruna Sankaranarayanan" };
+
+  show_about_dialog (window,
+                     "name", _("Robots"),
+                     "version", VERSION,
+                     "copyright", "Copyright © 1998–2008 Mark Rae\nCopyright © 2014–2016 Michael Catanzaro",
+                     "license-type", License.GPL_3_0,
+                     "comments", _("Based on classic BSD Robots"),
+                     "authors", authors,
+                     "artists", artists,
+                     "documenters", documenters,
+                     "translator-credits", _("translator-credits"),
+                     "logo-icon-name", "org.gnome.Robots",
+                     "website",
+                     "https://wiki.gnome.org/Apps/Robots";);
+}
+
+void quit_cb () {
+    quit_game ();
+}
+
+void new_game_cb () {
+    var dialog = new MessageDialog (window,
+                                    DialogFlags.MODAL,
+                                    MessageType.QUESTION,
+                                    ButtonsType.NONE,
+                                    _("Are you sure you want to discard the current game?"));
+
+    dialog.add_button (_("Keep _Playing"), ResponseType.REJECT);
+    dialog.add_button (_("_New Game"), ResponseType.ACCEPT);
+
+    var ret = dialog.run ();
+    dialog.destroy ();
+
+    if (ret == ResponseType.ACCEPT) {
+        game.start_new_game ();
+    }
+}
+
+void random_teleport_cb () {
+    game.keypress (Game.KeyboardControl.RTEL);
+}
+
+void safe_teleport_cb () {
+    game.keypress (Game.KeyboardControl.TELE);
+}
+
+void wait_cb () {
+    game.keypress (Game.KeyboardControl.WAIT);
+}
+
+bool window_configure_event_cb () {
+    if (!window_is_maximized)
+        window.get_size (out window_width, out window_height);
+    return false;
+}
+
+bool window_state_event_cb (Widget widget, Gdk.EventWindowState event) {
+    if ((event.changed_mask & Gdk.WindowState.MAXIMIZED) != 0)
+        window_is_maximized = (event.new_window_state & Gdk.WindowState.MAXIMIZED) != 0;
+    return false;
+}
+
+public void quit_game () {
+    window.close ();
+}
+
+void startup (Gtk.Application app) {
+    Environment.set_application_name (_("Robots"));
+
+    settings = new GLib.Settings ("org.gnome.Robots");
+
+    Window.set_default_icon_name ("org.gnome.Robots");
+
+    app.add_action_entries (app_entries, app);
+
+    app.set_accels_for_action ("app.new-game", { "<Primary>n" });
+    app.set_accels_for_action ("app.help", { "F1" });
+    app.set_accels_for_action ("app.quit", { "<Primary>q" });
+
+    make_cursors ();
+}
+
+void shutdown (Gtk.Application app) {
+    settings.set_int ("window-width", window_width);
+    settings.set_int ("window-height", window_height);
+    settings.set_boolean ("window-is-maximized", window_is_maximized);
+}
+
+Games.Scores.Category? create_category_from_key (string key) {
+    string name = category_name_from_key (key);
+    if (name == null)
+        return null;
+    return new Games.Scores.Category (key, name);
+}
+
+/**
+ * Initialises the keyboard actions when the game first starts up
+ **/
+void init_keyboard () {
+    key_controller = new EventControllerKey (window);
+    key_controller.key_pressed.connect (keyboard_cb);
+}
+
+void keyboard_set (uint[] keys) {
+    for (int i = 0; i < 12; ++i) {
+        control_keys[i] = keys[i];
+    }
+}
+
+bool keyboard_cb (EventControllerKey controller, uint keyval, uint keycode, Gdk.ModifierType state) {
+    /* This is a bit of a kludge to let through accelerator keys, otherwise
+     * if N is used as a key, then Ctrl-N is never picked up. The cleaner
+     * option, making the signal a connect_after signal skims the arrow keys
+     * before we can get to them which is a bigger problem. */
+    if ((state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)) != 0) {
+        return false;
+    }
+
+    char pressed = ((char) keyval).toupper ();
+
+    for (var i = 0; i < control_keys.length; ++i) {
+        if (pressed == ((char)control_keys[i]).toupper ()) {
+            game.keypress ((Game.KeyboardControl)i);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void activate (Gtk.Application app) {
+    if (window != null) {
+        window.present_with_time (get_current_event_time ());
+        return;
+    }
+
+    game = new Game ();
+
+    headerbar = new HeaderBar ();
+    headerbar.set_title (_("Robots"));
+    headerbar.set_show_close_button (true);
+
+    var appmenu = app.get_menu_by_id ("primary-menu");
+    var menu_button = new MenuButton ();
+    var icon = new Image.from_icon_name ("open-menu-symbolic", IconSize.BUTTON);
+    menu_button.set_image (icon);
+    menu_button.set_menu_model (appmenu);
+    menu_button.show ();
+    headerbar.pack_end (menu_button);
+
+    window = new ApplicationWindow (app);
+    window.set_titlebar (headerbar);
+    window.configure_event.connect (window_configure_event_cb);
+    window.window_state_event.connect (window_state_event_cb);
+    window.set_default_size (settings.get_int ("window-width"), settings.get_int ("window-height"));
+    if (settings.get_boolean ("window-is-maximized")) {
+        window.maximize ();
+    }
+
+    window.add_action_entries (win_entries, app);
+
+    game_area = new GameArea (game);
+    game_area.destroy.connect (() => game_area = null);
+
+    var gridframe = new Games.GridFrame (GAME_WIDTH, GAME_HEIGHT);
+    gridframe.add (game_area);
+
+    var hbox = new Box (Orientation.HORIZONTAL, 0);
+    var size_group = new SizeGroup (SizeGroupMode.BOTH);
+
+    var style_context = hbox.get_style_context ();
+    style_context.add_class ("linked");
+
+    {
+        var label = new Label.with_mnemonic (_("Teleport _Randomly"));
+        label.margin_top = 15;
+        label.margin_bottom = 15;
+        var button = new Button ();
+        button.add (label);
+        button.set_action_name ("win.random-teleport");
+        size_group.add_widget (button);
+        hbox.pack_start (button, true, true, 0);
+    }
+
+    {
+        safe_teleports_label = new Label (null);
+        safe_teleports_label.set_justify (Justification.CENTER);
+        safe_teleports_label.margin_top = 15;
+        safe_teleports_label.margin_bottom = 15;
+        var button = new Button ();
+        button.add (safe_teleports_label);
+        button.set_action_name ("win.safe-teleport");
+        size_group.add_widget (button);
+        hbox.pack_start (button, true, true, 0);
+    }
+
+    {
+        var label = new Label.with_mnemonic (_("_Wait for Robots"));
+        label.margin_top = 15;
+        label.margin_bottom = 15;
+        var button = new Button ();
+        button.add (label);
+        button.set_action_name ("win.wait");
+        size_group.add_widget (button);
+        hbox.pack_start (button, true, true, 0);
+    }
+
+    var vbox = new Box (Orientation.VERTICAL, 0);
+    vbox.pack_start (gridframe, true, true, 0);
+    vbox.pack_start (hbox, false, false, 0);
+
+    window.add (vbox);
+
+    var importer = new Games.Scores.DirectoryImporter ();
+    highscores = new Games.Scores.Context.with_importer_and_icon_name ("gnome-robots",
+                                                                       /* Label on the scores dialog, next 
to map type dropdown */
+                                                                       _("Game Type:"),
+                                                                       window,
+                                                                       create_category_from_key,
+                                                                       
Games.Scores.Style.POINTS_GREATER_IS_BETTER,
+                                                                       importer,
+                                                                       "org.gnome.Robots");
+
+    window.show_all ();
+
+    try {
+        game_configs = new GameConfigs.load ();
+    } catch (Error e) {
+        /* Oops, no configs, we probably haven't been installed properly. */
+        var errordialog = new MessageDialog.with_markup (window,
+                                                         DialogFlags.MODAL,
+                                                         MessageType.ERROR,
+                                                         ButtonsType.OK,
+                                                         "<b>%s</b>\n\n%s",
+                                                         _("No game data could be found."),
+                                                         _("The program Robots was unable to find any valid 
game configuration files. Please check that the program is installed correctly."));
+        errordialog.set_resizable (false);
+        errordialog.run ();
+        app.quit ();
+    }
+
+    try {
+        load_properties ();
+    } catch (Error e) {
+        // error ("%s", e.message);
+        // TODO message box
+        app.quit ();
+    }
+
+    try {
+        load_game_graphics ();
+    } catch (Error e) {
+        error ("%s", e.message);
+        /* Oops, no graphics, we probably haven't been installed properly. */
+        var errordialog = new MessageDialog.with_markup (window,
+                                                         DialogFlags.MODAL,
+                                                         MessageType.ERROR,
+                                                         ButtonsType.OK,
+                                                         "<b>%s</b>\n\n%s",
+                                                         _("Some graphics files are missing or corrupt."),
+                                                         _("The program Robots was unable to load all the 
necessary graphics files. Please check that the program is installed correctly."));
+        errordialog.run ();
+        app.quit ();
+    }
+
+    init_keyboard ();
+    game.init_game ();
+
+    GLib.Settings.sync ();
+}
+
+public static int main (string[] args) {
+    Intl.setlocale ();
+    Intl.bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+    Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+    Intl.textdomain (GETTEXT_PACKAGE);
+
+    var app = new Gtk.Application ("org.gnome.Robots", ApplicationFlags.FLAGS_NONE);
+
+    app.startup.connect (() => startup (app));
+    app.shutdown.connect (() => shutdown (app));
+    app.activate.connect (() => activate (app));
+
+    return app.run (args);
+}
+


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