[gnome-robots] rewrite window and game area modules in Vala
- From: Andrey Kutejko <akutejko src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-robots] rewrite window and game area modules in Vala
- Date: Tue, 6 Oct 2020 19:31:41 +0000 (UTC)
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]