1 /*
2 * Gyatzee: Gnomified Dice game.
3 * (C) 1998 the Free Software Foundation
4 *
5 * File: gyahtzee.c
6 *
7 * Author: Scott Heavner
8 *
9 * Gnome specific yahtzee routines.
10 *
11 * Other gnome specific code is in setup.c and clist.c
12 *
13 * Window manager independent routines are in yahtzee.c and computer.c
14 *
15 * Variables are exported in yahtzee.h
16 *
17 * This program is based on based on orest zborowski's curses based
18 * yahtze (c)1992.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 */
34 #include <config.h>
35
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <stdarg.h>
40 #include <locale.h>
41
42 #include <glib/gi18n.h>
43 #include <gtk/gtk.h>
44 #include <gdk/gdkkeysyms.h>
45 #include <libgnome-games-support.h>
46
47 #include "yahtzee.h"
48 #include "gyahtzee.h"
49
50 #define DELAY_MS 600
51
52 static char *appName = N_("Tali");
53 static GtkApplication *application;
54 static guint last_timeout = 0;
55 static gboolean ready_to_advance_player;
56
57 #define NUMBER_OF_PIXMAPS 7
58 #define GAME_TYPES 2
59 #define DIE_SELECTED_PIXMAP (NUMBER_OF_PIXMAPS-1)
60 #define SCORES_CATEGORY (game_type == GAME_KISMET ? "Colors" : NULL)
61
62 static char *dicefiles[NUMBER_OF_PIXMAPS] = { "gnome-dice-1.svg",
63 "gnome-dice-2.svg",
64 "gnome-dice-3.svg",
65 "gnome-dice-4.svg",
66 "gnome-dice-5.svg",
67 "gnome-dice-6.svg",
68 "gnome-dice-none.svg"
69 };
70
71 static char *kdicefiles[NUMBER_OF_PIXMAPS] = { "kismet1.svg",
72 "kismet2.svg",
73 "kismet3.svg",
74 "kismet4.svg",
75 "kismet5.svg",
76 "kismet6.svg",
77 "kismet-none.svg"
78 };
79
80 static GtkWidget *dicePixmaps[NUMBER_OF_DICE][NUMBER_OF_PIXMAPS][GAME_TYPES];
81
82 GSettings *settings;
83 GtkWidget *window;
84 GtkWidget *ScoreList;
85 static GtkToolItem *diceBox[NUMBER_OF_DICE];
86 static GtkWidget *rollLabel;
87 static GtkWidget *mbutton;
88 static GtkWidget *hbar;
89 static GAction *scores_action;
90 static GAction *undo_action;
91 static gchar *game_type_string = NULL;
92 static gint test_computer_play = 0;
93 gint NUM_TRIALS = 0;
94
95 static const GOptionEntry yahtzee_options[] = {
96 {"delay", 'd', 0, G_OPTION_ARG_NONE, &DoDelay,
97 N_("Delay computer moves"), NULL},
98 {"thoughts", 't', 0, G_OPTION_ARG_NONE, &DisplayComputerThoughts,
99 N_("Display computer thoughts"), NULL},
100 {"computers", 'n', 0, G_OPTION_ARG_INT, &NumberOfComputers,
101 N_("Number of computer opponents"), N_("NUMBER")},
102 {"humans", 'p', 0, G_OPTION_ARG_INT, &NumberOfHumans,
103 N_("Number of human opponents"), N_("NUMBER")},
104 {"game", 'g', 0, G_OPTION_ARG_STRING, &game_type_string,
105 N_("Game choice: Regular or Colors"), N_("STRING")},
106 {"computer-test", 'c', 0, G_OPTION_ARG_INT, &test_computer_play,
107 N_("Number of computer-only games to play"), N_("NUMBER")},
108 {"monte-carlo-trials", 'm', 0, G_OPTION_ARG_INT, &NUM_TRIALS,
109 N_("Number of trials for each roll for the computer"), N_("NUMBER")},
110 {NULL}
111 };
112
113 typedef struct
114 {
115 gchar *key;
116 gchar *name;
117 } key_value;
118
119 static const key_value category_array[] = {
120 /* Order must match GameType enum order */
121 {"Regular", NC_("game type", "Regular")},
122 {"Colors", NC_("game type", "Colors")}
123 };
124
125 const gchar *
category_name_from_key(const gchar * key)126 category_name_from_key (const gchar *key)
127 {
128 int i;
129 for (i = 0; i < G_N_ELEMENTS (category_array); ++i) {
130 if (g_strcmp0 (category_array[i].key, key) == 0)
131 return g_dpgettext2(NULL, "game type", category_array[i].name);
132 }
133 return NULL;
134 }
135
136 static GamesScoresCategory *
create_category_from_key(const gchar * key,gpointer user_data)137 create_category_from_key (const gchar *key,
138 gpointer user_data)
139 {
140 const gchar *name = category_name_from_key (key);
141 if (name == NULL)
142 return NULL;
143 return games_scores_category_new (key, name);
144 }
145
146 GamesScoresContext *highscores;
147
148 static void modify_dice (GtkToolButton * widget, gpointer data);
149 static void UpdateRollLabel (void);
150
151 static void
update_roll_button_sensitivity(void)152 update_roll_button_sensitivity (void)
153 {
154 gboolean state = FALSE;
155 gint i;
156
157 for (i = 0; i < NUMBER_OF_DICE; i++)
158 state |=
159 gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (diceBox[i]));
160
161 if (!state) {
162 gtk_button_set_label (GTK_BUTTON (mbutton), _("Roll all!"));
163 state = TRUE;
164 } else {
165 gtk_button_set_label (GTK_BUTTON (mbutton), _("Roll!"));
166 state = TRUE;
167 }
168
169 state &= NumberOfRolls < 3;
170 state &= !players[CurrentPlayer].comp;
171
172 if (GameIsOver ()) {
173 state = FALSE;
174 }
175
176 gtk_widget_set_sensitive (GTK_WIDGET (mbutton), state);
177 }
178
179 static void
add_score_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)180 add_score_cb (GObject *source_object,
181 GAsyncResult *res,
182 gpointer user_data)
183 {
184 GamesScoresContext *context = GAMES_SCORES_CONTEXT (source_object);
185 GError *error = NULL;
186
187 games_scores_context_add_score_finish (context, res, &error);
188 if (error != NULL) {
189 g_warning ("Failed to add score: %s", error->message);
190 g_error_free (error);
191 }
192 }
193
194 static void
CheerWinner(void)195 CheerWinner (void)
196 {
197 int winner;
198 int i;
199
200 ShowoffPlayer (ScoreList, CurrentPlayer, 0);
201
202 winner = FindWinner ();
203
204 /* draw. The score is returned as a negative value */
205 if (winner < 0) {
206 for (i = 0; i < NumberOfPlayers; i++) {
207 if (total_score (i) == -winner) {
208 ShowoffPlayer (ScoreList, i, 1);
209 }
210 }
211
212 say (_("The game is a draw!"));
213 return;
214 }
215
216 ShowoffPlayer (ScoreList, winner, 1);
217
218 if (winner < NumberOfHumans) {
219 GamesScoresCategory *category;
220 category = create_category_from_key (category_array[game_type].key, NULL);
221 games_scores_context_add_score (highscores, (guint32) WinningScore,
222 category, NULL,
223 add_score_cb, NULL);
224 }
225
226 if (players[winner].name)
227 say (ngettext ("%s wins the game with %d point",
228 "%s wins the game with %d points", WinningScore),
229 players[winner].name, WinningScore);
230 else
231 say (_("Game over!"));
232 }
233
234 static gboolean
do_computer_turns(void)235 do_computer_turns (void)
236 {
237 if (!players[CurrentPlayer].comp) {
238 last_timeout = 0;
239 return FALSE;
240 }
241
242 if (ready_to_advance_player) {
243 NextPlayer ();
244 return TRUE;
245 }
246
247 if (players[CurrentPlayer].finished) {
248 NextPlayer ();
249 return TRUE;
250 }
251
252 ComputerRolling (CurrentPlayer);
253 if (NoDiceSelected () || (NumberOfRolls >= NUM_ROLLS)) {
254 ComputerScoring (CurrentPlayer);
255 ready_to_advance_player = TRUE;
256 } else {
257 RollSelectedDice ();
258 UpdateRollLabel ();
259 }
260
261 if (!DoDelay)
262 do_computer_turns ();
263
264 return TRUE;
265 }
266
267 /* Show the current score and prompt for current player state */
268
269 void
DisplayCurrentPlayer(void)270 DisplayCurrentPlayer (void) {
271 ShowoffPlayer (ScoreList, CurrentPlayer, 1);
272
273 if (players[CurrentPlayer].name) {
274 if (players[CurrentPlayer].comp) {
275 say (_("Computer playing for %s"), players[CurrentPlayer].name);
276 } else {
277 say (_("%s! – You’re up."), players[CurrentPlayer].name);
278 }
279 }
280 }
281
282 /* Display current player and refresh dice/display */
283 void
DisplayCurrentPlayerRefreshDice(void)284 DisplayCurrentPlayerRefreshDice (void) {
285 DisplayCurrentPlayer ();
286 UpdateAllDicePixmaps ();
287 DeselectAllDice ();
288 UpdateRollLabel ();
289 }
290
291 void
NextPlayer(void)292 NextPlayer (void)
293 {
294 if (GameIsOver ()) {
295 if (last_timeout) {
296 g_source_remove (last_timeout);
297 last_timeout = 0;
298 }
299 if (DoDelay && NumberOfComputers > 0)
300 NumberOfRolls = NUM_ROLLS;
301 else
302 NumberOfRolls = LastHumanNumberOfRolls;
303 /* update_roll_button_sensitivity () needs to be called in
304 this context however UpdateRollLabel () also calls that method */
305 UpdateRollLabel ();
306 CheerWinner ();
307 return;
308 }
309
310 NumberOfRolls = 0;
311 ready_to_advance_player = FALSE;
312 ShowoffPlayer (ScoreList, CurrentPlayer, 0);
313
314 /* Find the next player with rolls left */
315 do {
316 CurrentPlayer = (CurrentPlayer + 1) % NumberOfPlayers;
317 } while (players[CurrentPlayer].finished);
318
319 DisplayCurrentPlayer ();
320 SelectAllDice ();
321 RollSelectedDice ();
322 FreeRedoList ();
323
324 /* Remember the roll count if this turn is for a
325 human player for display at the end of the game */
326 if (!players[CurrentPlayer].comp)
327 LastHumanNumberOfRolls = NumberOfRolls;
328
329 if (players[CurrentPlayer].comp) {
330 if (DoDelay) {
331 if (!last_timeout)
332 last_timeout = g_timeout_add (DELAY_MS,
333 (GSourceFunc) do_computer_turns, NULL);
334 } else {
335 do_computer_turns ();
336 }
337 }
338 /* Only update the roll label if we are running in
339 delay mode or if this turn is for a human player */
340 if (DoDelay || (!players[CurrentPlayer].comp))
341 UpdateRollLabel ();
342 }
343
344 /* Go back to the previous player */
345
346 void
PreviousPlayer(void)347 PreviousPlayer (void)
348 {
349 if (UndoPossible ()) {
350 NumberOfRolls = 1;
351 ready_to_advance_player = FALSE;
352 ShowoffPlayer (ScoreList, CurrentPlayer, 0);
353
354 /* Find the next player with rolls left */
355 do {
356 CurrentPlayer = (UndoLastMove () + NumberOfPlayers) % NumberOfPlayers;
357 } while (players[CurrentPlayer].comp && UndoPossible ());
358
359 DisplayCurrentPlayerRefreshDice ();
360 }
361 }
362
363 void
RedoPlayer(void)364 RedoPlayer (void)
365 {
366 if (RedoPossible ()) {
367 NumberOfRolls = 1;
368 ready_to_advance_player = FALSE;
369 ShowoffPlayer (ScoreList, CurrentPlayer, 0);
370 /* The first element of the list is the undone turn, so
371 * we need to remove it from the list before redoing other
372 * turns. */
373 FreeRedoListHead ();
374
375 /* Redo all computer players */
376 do {
377 CurrentPlayer = RedoLastMove ();
378 } while (players[CurrentPlayer].comp && RedoPossible ());
379
380 RestoreLastRoll ();
381 DisplayCurrentPlayerRefreshDice ();
382 }
383 }
384
385 void
ShowPlayer(int num,int field)386 ShowPlayer (int num, int field)
387 {
388 int i;
389 int line;
390 int upper_tot;
391 int lower_tot;
392 int bonus = -1;
393 int score;
394
395 for (i = 0; i < NUM_FIELDS; ++i) {
396
397 if (i == field || field == -1) {
398
399 line = i;
400
401 if (i >= NUM_UPPER)
402 line += 3;
403
404 if (players[num].used[i])
405 score = players[num].score[i];
406 else
407 score = -1;
408
409 if (test_computer_play == 0) update_score_cell (ScoreList, line, num + 1, score);
410 }
411 }
412
413 upper_tot = upper_total (num);
414 lower_tot = lower_total (num);
415
416 if (upper_tot >= 63) {
417 bonus = 35;
418 if (game_type == GAME_KISMET) {
419 if (upper_tot >= 78)
420 bonus = 75;
421 else if (upper_tot >= 71)
422 bonus = 55;
423 }
424 upper_tot += bonus;
425 }
426
427 if (test_computer_play == 0) {
428 update_score_cell (ScoreList, R_BONUS, num + 1, bonus);
429 update_score_cell (ScoreList, R_UTOTAL, num + 1, upper_tot);
430 update_score_cell (ScoreList, R_LTOTAL, num + 1, lower_tot);
431 update_score_cell (ScoreList, R_GTOTAL, num + 1, upper_tot + lower_tot);
432 }
433 }
434
435 static void
quit_cb(GSimpleAction * action,GVariant * parameter,gpointer data)436 quit_cb (GSimpleAction * action, GVariant * parameter, gpointer data)
437 {
438 gtk_widget_destroy (window);
439 }
440
441 static void
preferences_cb(GSimpleAction * action,GVariant * parameter,gpointer data)442 preferences_cb (GSimpleAction * action, GVariant * parameter, gpointer data)
443 {
444 setup_game ();
445 }
446
447 /* This handles the keys 1..5 for the dice. */
448 static gint
key_press(GtkWidget * widget,GdkEventKey * event,gpointer data)449 key_press (GtkWidget * widget, GdkEventKey * event, gpointer data)
450 {
451 gint offset;
452
453 offset = event->keyval - GDK_KEY_1;
454
455 if ((offset < 0) || (offset >= NUMBER_OF_DICE)) {
456 return FALSE;
457 }
458
459 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (diceBox[offset]),
460 !DiceValues[offset].sel);
461
462 return FALSE;
463 }
464
465 static void
GyahtzeeNewGame(void)466 GyahtzeeNewGame (void)
467 {
468 int i;
469
470 say (_("Select dice to roll or choose a score slot."));
471
472 game_type = get_new_game_type ();
473 NewGame ();
474 setup_score_list (ScoreList);
475 UpdateRollLabel ();
476
477 for (i = 0; i < NumberOfPlayers; i++)
478 ShowoffPlayer (ScoreList, i, 0);
479 ShowoffPlayer (ScoreList, 0, 1);
480 }
481
482
483 static void
new_game_cb(GSimpleAction * action,GVariant * parameter,gpointer data)484 new_game_cb (GSimpleAction * action, GVariant * parameter, gpointer data)
485 {
486 GyahtzeeNewGame ();
487 }
488
489 static void
UpdateRollLabel(void)490 UpdateRollLabel (void)
491 {
492 static GString *str = NULL;
493
494 if (!str)
495 str = g_string_sized_new (22);
496
497 g_string_printf (str, "<b>%s %d/3</b>", _("Roll"), NumberOfRolls);
498 gtk_label_set_label (GTK_LABEL (rollLabel), str->str);
499
500 update_score_tooltips ();
501 update_roll_button_sensitivity ();
502 update_undo_sensitivity ();
503 }
504
505 static void
UpdateDiePixmap(int n,int prev_game_type)506 UpdateDiePixmap (int n, int prev_game_type)
507 {
508 static int last_val[NUMBER_OF_DICE] = { 0 };
509
510 gtk_widget_hide (dicePixmaps[n][last_val[n]][prev_game_type]);
511
512 last_val[n] = DiceValues[n].sel ? DIE_SELECTED_PIXMAP :
513 DiceValues[n].val - 1;
514 gtk_widget_show (dicePixmaps[n][last_val[n]][game_type]);
515 }
516
517 void
UpdateAllDicePixmaps(void)518 UpdateAllDicePixmaps (void)
519 {
520 int i;
521 static int prev_game_type = 0;
522
523 for (i = 0; test_computer_play == 0 && i < NUMBER_OF_DICE; i++) {
524 UpdateDiePixmap (i, prev_game_type);
525 }
526 prev_game_type = game_type;
527 }
528
529 void
DeselectAllDice(void)530 DeselectAllDice (void)
531 {
532 int i;
533
534 if (test_computer_play > 0) return;
535 for (i = 0; i < NUMBER_OF_DICE; i++)
536 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (diceBox[i]),
537 FALSE);
538 }
539
540 /* Callback on dice press */
541 static void
modify_dice(GtkToolButton * widget,gpointer data)542 modify_dice (GtkToolButton * widget, gpointer data)
543 {
544 DiceInfo *tmp = (DiceInfo *) data;
545 GtkToggleToolButton *button = GTK_TOGGLE_TOOL_BUTTON (widget);
546
547 /* Don't modify dice if player is marked finished or computer is playing */
548 if (players[CurrentPlayer].finished || players[CurrentPlayer].comp) {
549 if (gtk_toggle_tool_button_get_active (button))
550 gtk_toggle_tool_button_set_active (button, FALSE);
551 return;
552 }
553
554 if (NumberOfRolls >= NUM_ROLLS) {
555 say (_("You are only allowed three rolls. Choose a score slot."));
556 gtk_toggle_tool_button_set_active (button, FALSE);
557 return;
558 }
559
560 tmp->sel = gtk_toggle_tool_button_get_active (button);
561
562 UpdateAllDicePixmaps ();
563
564 update_roll_button_sensitivity ();
565 return;
566 }
567
568 static void
roll(void)569 roll (void)
570 {
571 if (!players[CurrentPlayer].comp) {
572 RollSelectedDice ();
573 if (NumberOfRolls > 1)
574 FreeUndoRedoLists ();
575 UpdateRollLabel ();
576 LastHumanNumberOfRolls = NumberOfRolls;
577 }
578 }
579
580 static void
roll_button_pressed_cb(GtkButton * button,gpointer data)581 roll_button_pressed_cb (GtkButton * button, gpointer data)
582 {
583 roll ();
584 }
585
586 static void
roll_cb(GSimpleAction * action,GVariant * parameter,gpointer data)587 roll_cb (GSimpleAction * action, GVariant * parameter, gpointer data)
588 {
589 roll ();
590 }
591
592 void
say(char * fmt,...)593 say (char *fmt, ...)
594 {
595 va_list ap;
596 char buf[200];
597
598 if (test_computer_play > 0) return;
599 va_start (ap, fmt);
600 g_vsnprintf (buf, 200, fmt, ap);
601 va_end (ap);
602
603 gtk_header_bar_set_subtitle (GTK_HEADER_BAR (hbar), buf);
604
605 }
606
607
608 static void
about_cb(GSimpleAction * action,GVariant * parameter,gpointer data)609 about_cb (GSimpleAction * action, GVariant * parameter, gpointer data)
610 {
611 const gchar *authors[] = {
612 N_("GNOME version (1998):"),
613 "Scott Heavner",
614 "",
615 N_("Console version (1992):"),
616 "Orest Zborowski",
617 "",
618 N_("Colors game and multi-level AI (2006):"),
619 "Geoff Buchan",
620 NULL
621 };
622
623 const gchar *documenters[] = {
624 "Scott D Heavner",
625 "Callum McKenzie",
626 NULL
627 };
628
629 gtk_show_about_dialog (GTK_WINDOW (window),
630 "name", appName,
631 "version", VERSION,
632 "copyright", "Copyright © 1998–2008 "
633 "Free Software Foundation, Inc.",
634 "license-type", GTK_LICENSE_GPL_2_0,
635 "comments", _("A variation on poker with "
636 "dice and less money."),
637 "authors", authors,
638 "documenters", documenters,
639 "translator-credits", _("translator-credits"),
640 "logo-icon-name", "org.gnome.Tali",
641 "website",
642 "https://wiki.gnome.org/Apps/Tali",
643 NULL);
644 }
645
646 void
ShowHighScores(void)647 ShowHighScores (void)
648 {
649 games_scores_context_run_dialog (highscores);
650 }
651
652 static void
score_cb(GSimpleAction * action,GVariant * parameter,gpointer data)653 score_cb (GSimpleAction * action, GVariant * parameter, gpointer data)
654 {
655 ShowHighScores ();
656 }
657
658 static void
undo_cb(GSimpleAction * action,GVariant * parameter,gpointer data)659 undo_cb (GSimpleAction * action, GVariant * parameter, gpointer data)
660 {
661 PreviousPlayer ();
662 }
663
664 static void
LoadDicePixmaps(void)665 LoadDicePixmaps (void)
666 {
667 int i, j;
668 char *path, *path_kismet;
669 GError *error = NULL;
670
671 for (i = 0; i < NUMBER_OF_PIXMAPS; i++) {
672 /* This is not efficient, but it lets us load animated types,
673 * there is no way for us to copy a general GtkImage (the old
674 * code had a way for static images). */
675 path = g_build_filename (DATA_DIRECTORY, dicefiles[i], NULL);
676 path_kismet = g_build_filename (DATA_DIRECTORY, kdicefiles[i], NULL);
677
678 if (g_file_test (path, G_FILE_TEST_EXISTS) &&
679 g_file_test (path_kismet, G_FILE_TEST_EXISTS)) {
680
681 for (j = 0; j < NUMBER_OF_DICE; j++) {
682 GdkPixbuf *pixbuf;
683
684 pixbuf = gdk_pixbuf_new_from_file_at_size (path, 60, 60, &error);
685 dicePixmaps[j][i][GAME_YAHTZEE] = gtk_image_new_from_pixbuf (pixbuf);
686 if (error) {
687 g_warning ("Loading dice image %s: %s", path, error->message);
688 g_clear_error (&error);
689 }
690 g_object_unref (pixbuf);
691
692 pixbuf = gdk_pixbuf_new_from_file_at_size (path_kismet, 60, 60, &error);
693 dicePixmaps[j][i][GAME_KISMET] = gtk_image_new_from_pixbuf (pixbuf);
694 if (error) {
695 g_warning ("Loading dice image %s: %s", path_kismet, error->message);
696 g_clear_error (&error);
697 }
698 g_object_unref (pixbuf);
699 }
700
701 } /* FIXME: What happens if the file isn't found. */
702 g_free (path);
703 g_free (path_kismet);
704 }
705 }
706
707 void
update_undo_sensitivity(void)708 update_undo_sensitivity (void)
709 {
710 g_simple_action_set_enabled (((GSimpleAction *) undo_action), UndoVisible ());
711 }
712
713 static void
help_cb(GSimpleAction * action,GVariant * parameter,gpointer data)714 help_cb (GSimpleAction * action, GVariant * parameter, gpointer data)
715 {
716 GError *error = NULL;
717
718 gtk_show_uri_on_window (GTK_WINDOW (window), "help:tali", gtk_get_current_event_time (), &error);
719 if (error)
720 g_warning ("Failed to show help: %s", error->message);
721 g_clear_error (&error);
722 }
723
724 static const GActionEntry app_entries[] = {
725 {"new-game", new_game_cb, NULL, NULL, NULL},
726 {"undo", undo_cb, NULL, NULL, NULL},
727 {"scores", score_cb, NULL, NULL, NULL},
728 {"quit", quit_cb, NULL, NULL, NULL},
729 {"preferences", preferences_cb, NULL, NULL, NULL},
730 {"help", help_cb, NULL, NULL, NULL},
731 {"about", about_cb, NULL, NULL, NULL},
732 {"roll", roll_cb, NULL, NULL, NULL}
733 };
734
735
736 static void
GyahtzeeCreateMainWindow(GApplication * app,gpointer user_data)737 GyahtzeeCreateMainWindow (GApplication *app, gpointer user_data)
738 {
739 GtkWidget *hbox, *vbox;
740 GtkWidget *toolbar;
741 GtkWidget *tmp;
742 GtkWidget *dicebox;
743 GtkWidget *undo_button;
744 GtkWidget *menu_button;
745 GtkWidget *icon;
746 GtkBuilder *builder;
747 GMenuModel *appmenu;
748 int i, j;
749
750 window = gtk_application_window_new (application);
751 gtk_window_set_application (GTK_WINDOW (window), application);
752 gtk_window_set_title (GTK_WINDOW (window), _(appName));
753 gtk_window_set_hide_titlebar_when_maximized (GTK_WINDOW (window), FALSE);
754 gtk_window_set_icon_name (GTK_WINDOW (window), "org.gnome.Tali");
755
756 //games_conf_add_window (GTK_WINDOW (window), NULL);
757
758 g_signal_connect (GTK_WIDGET (window), "key_press_event",
759 G_CALLBACK (key_press), NULL);
760
761 g_action_map_add_action_entries (G_ACTION_MAP (application), app_entries, G_N_ELEMENTS (app_entries), application);
762 const gchar *vaccels_help[] = {"F1", NULL};
763 const gchar *vaccels_new[] = {"<Primary>n", NULL};
764 const gchar *vaccels_roll[] = {"<Primary>r", NULL};
765 const gchar *vaccels_undo[] = {"<Primary>z", NULL};
766 const gchar *vaccels_quit[] = {"<Primary>q", NULL};
767
768 gtk_application_set_accels_for_action (application, "app.help", vaccels_help);
769 gtk_application_set_accels_for_action (application, "app.new-game", vaccels_new);
770 gtk_application_set_accels_for_action (application, "app.roll", vaccels_roll);
771 gtk_application_set_accels_for_action (application, "app.undo", vaccels_undo);
772 gtk_application_set_accels_for_action (application, "app.quit", vaccels_quit);
773
774 scores_action = g_action_map_lookup_action (G_ACTION_MAP (application), "scores");
775 undo_action = g_action_map_lookup_action (G_ACTION_MAP (application), "undo");
776 update_undo_sensitivity ();
777
778 /*--- Headerbar ---*/
779 hbar = gtk_header_bar_new ();
780 gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (hbar), TRUE);
781 gtk_header_bar_set_title (GTK_HEADER_BAR (hbar), _(appName));
782 gtk_widget_show (hbar);
783 gtk_window_set_titlebar (GTK_WINDOW (window), hbar);
784
785 if (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL)
786 undo_button = gtk_button_new_from_icon_name ("edit-undo-rtl-symbolic", GTK_ICON_SIZE_BUTTON);
787 else
788 undo_button = gtk_button_new_from_icon_name ("edit-undo-symbolic", GTK_ICON_SIZE_BUTTON);
789 gtk_widget_set_valign (undo_button, GTK_ALIGN_CENTER);
790 gtk_actionable_set_action_name (GTK_ACTIONABLE (undo_button), "app.undo");
791 gtk_widget_set_tooltip_text (undo_button, _("Undo your most recent move"));
792 gtk_widget_show (undo_button);
793 gtk_header_bar_pack_start (GTK_HEADER_BAR (hbar), undo_button);
794
795 builder = gtk_builder_new_from_resource ("/org/gnome/Tali/ui/menus.ui");
796 appmenu = (GMenuModel *) gtk_builder_get_object (builder, "app-menu");
797
798 menu_button = gtk_menu_button_new();
799 icon = gtk_image_new_from_icon_name ("open-menu-symbolic", GTK_ICON_SIZE_BUTTON);
800 gtk_button_set_image (GTK_BUTTON (menu_button), icon);
801 gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (menu_button), appmenu);
802 gtk_widget_show (menu_button);
803 gtk_header_bar_pack_end (GTK_HEADER_BAR (hbar), menu_button);
804
805 /*---- Content ----*/
806
807 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
808 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
809
810 gtk_container_add (GTK_CONTAINER (window), vbox);
811
812 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
813
814 /* Retreive dice pixmaps from memory or files */
815 LoadDicePixmaps ();
816
817 /* Put all the dice in a vertical column */
818 dicebox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
819 gtk_box_pack_start (GTK_BOX (hbox), dicebox, FALSE, TRUE, 0);
820 gtk_widget_show (dicebox);
821
822 rollLabel = gtk_label_new (NULL);
823 gtk_label_set_use_markup (GTK_LABEL (rollLabel), TRUE);
824 gtk_widget_show (rollLabel);
825 gtk_box_pack_start (GTK_BOX (dicebox), rollLabel, FALSE, TRUE, 5);
826
827 mbutton = gtk_button_new_with_label (_("Roll!"));
828 gtk_box_pack_end (GTK_BOX (dicebox), mbutton, FALSE, FALSE, 5);
829 g_signal_connect (GTK_BUTTON (mbutton), "clicked",
830 G_CALLBACK (roll_button_pressed_cb), NULL);
831 gtk_widget_show (GTK_WIDGET (mbutton));
832
833 toolbar = gtk_toolbar_new ();
834 gtk_orientable_set_orientation (GTK_ORIENTABLE (toolbar),
835 GTK_ORIENTATION_VERTICAL);
836 gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
837 gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), FALSE);
838 gtk_box_pack_end (GTK_BOX (dicebox), toolbar, TRUE, TRUE, 0);
839
840 for (i = 0; i < NUMBER_OF_DICE; i++) {
841 tmp = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
842
843 for (j = 0; j < NUMBER_OF_PIXMAPS; j++) {
844 gtk_box_pack_start (GTK_BOX (tmp), dicePixmaps[i][j][GAME_YAHTZEE], FALSE, FALSE, 0);
845 gtk_box_pack_start (GTK_BOX (tmp), dicePixmaps[i][j][GAME_KISMET], FALSE, FALSE, 0);
846 }
847
848 diceBox[i] = gtk_toggle_tool_button_new ();
849 gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (diceBox[i]), tmp);
850 g_signal_connect (GTK_TOOL_BUTTON (diceBox[i]), "clicked",
851 G_CALLBACK (modify_dice), &DiceValues[i]);
852
853 gtk_toolbar_insert (GTK_TOOLBAR (toolbar),
854 GTK_TOOL_ITEM (diceBox[i]), -1);
855
856 gtk_widget_show (GTK_WIDGET (diceBox[i]));
857 gtk_widget_show (tmp);
858 /*gtk_widget_show (dicePixmaps[i][0][game_type]);*/
859 }
860 gtk_widget_show (toolbar);
861
862 /* Scores displayed in score list */
863 ScoreList = create_score_list ();
864 gtk_box_pack_end (GTK_BOX (hbox), ScoreList, TRUE, TRUE, 0);
865 setup_score_list (ScoreList);
866 gtk_widget_show (ScoreList);
867
868 gtk_widget_show (hbox);
869 gtk_widget_show (vbox);
870 }
871
872 static void
GyahtzeeActivateGame(GApplication * app,gpointer user_data)873 GyahtzeeActivateGame (GApplication *app, gpointer user_data)
874 {
875 GamesScoresImporter *importer;
876
877 importer = GAMES_SCORES_IMPORTER (games_scores_directory_importer_new ());
878 highscores = games_scores_context_new_with_importer ("tali",
879 _("Game Type:"),
880 GTK_WINDOW (window),
881 create_category_from_key, NULL,
882 GAMES_SCORES_STYLE_POINTS_GREATER_IS_BETTER,
883 importer);
884 g_object_unref (importer);
885
886 if (!gtk_widget_is_visible (window)) {
887 gtk_widget_show (window);
888 GyahtzeeNewGame ();
889 }
890 }
891
892 int
main(int argc,char * argv[])893 main (int argc, char *argv[])
894 {
895 char **player_names;
896 gsize n_player_names;
897 guint i;
898 GOptionContext *context;
899 gboolean retval;
900 GError *error = NULL;
901
902 setlocale (LC_ALL, "");
903 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
904 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
905 textdomain (GETTEXT_PACKAGE);
906
907 application = gtk_application_new ("org.gnome.Tali", 0);
908 g_signal_connect (application, "startup", G_CALLBACK (GyahtzeeCreateMainWindow), NULL);
909 g_signal_connect (application, "activate", G_CALLBACK (GyahtzeeActivateGame), NULL);
910
911 /* Reset all yahtzee variables before parsing args */
912 YahtzeeInit ();
913
914 context = g_option_context_new (NULL);
915 g_option_context_add_group (context, gtk_get_option_group (TRUE));
916 g_option_context_add_main_entries (context, yahtzee_options,
917 GETTEXT_PACKAGE);
918 retval = g_option_context_parse (context, &argc, &argv, &error);
919 g_option_context_free (context);
920 if (!retval) {
921 g_print ("%s", error->message);
922 g_error_free (error);
923 exit (1);
924 }
925
926 settings = g_settings_new ("org.gnome.Tali");
927
928 g_set_application_name (_(appName));
929
930 /* If we're in computer test mode, just run some tests, no GUI */
931 if (test_computer_play > 0) {
932 gint ii, jj, kk;
933 gdouble sum_scores = 0.0;
934 game_type = GAME_YAHTZEE;
935 if (game_type_string)
936 game_type = game_type_from_string (game_type_string);
937 g_message ("In test computer play section - Using %d trials for simulation", NUM_TRIALS);
938 for (ii = 0; ii < test_computer_play; ii++) {
939 int num_rolls = 0;
940 NumberOfHumans = 0;
941 NumberOfComputers = 1;
942 NewGame ();
943
944 while (!GameIsOver () && num_rolls < 100) {
945 ComputerRolling (CurrentPlayer);
946 if (NoDiceSelected () || (NumberOfRolls >= NUM_ROLLS)) {
947 ComputerScoring (CurrentPlayer);
948 NumberOfRolls = 0;
949 SelectAllDice ();
950 RollSelectedDice ();
951 } else {
952 RollSelectedDice ();
953 }
954 num_rolls++;
955 }
956 for (kk = NumberOfHumans; kk < NumberOfPlayers; kk++) {
957 printf ("Computer score: %d\n", total_score (kk));
958 sum_scores += total_score (kk);
959 if (num_rolls > 98) {
960 for (jj = 0; jj < NUM_FIELDS; jj++)
961 g_message ("Category %d is score %d", jj, players[kk].score[jj]);
962 }
963 }
964 }
965 printf ("Computer average: %.2f for %d trials\n", sum_scores / test_computer_play, NUM_TRIALS);
966 exit (0);
967 }
968
969 gtk_window_set_default_icon_name ("org.gnome.Tali");
970
971 if (NumberOfComputers == 0) /* Not set on the command-line. */
972 NumberOfComputers = g_settings_get_int (settings, "number-of-computer-opponents");
973
974 if (NumberOfHumans == 0) /* Not set on the command-line. */
975 NumberOfHumans = g_settings_get_int (settings, "number-of-human-opponents");
976
977 if (NumberOfHumans < 1)
978 NumberOfHumans = 1;
979 if (NumberOfComputers < 0)
980 NumberOfComputers = 0;
981
982 if (NumberOfHumans > MAX_NUMBER_OF_PLAYERS)
983 NumberOfHumans = MAX_NUMBER_OF_PLAYERS;
984 if ((NumberOfHumans + NumberOfComputers) > MAX_NUMBER_OF_PLAYERS)
985 NumberOfComputers = MAX_NUMBER_OF_PLAYERS - NumberOfHumans;
986
987 if (game_type_string)
988 game_type = game_type_from_string (game_type_string);
989 else {
990 char *type;
991
992 type = g_settings_get_string (settings, "game-type");
993 game_type = game_type_from_string (type);
994 }
995
996 set_new_game_type (game_type);
997
998 if (NUM_TRIALS <= 0)
999 NUM_TRIALS = g_settings_get_int (settings, "monte-carlo-trials");
1000
1001 if (DoDelay == 0) /* Not set on the command-line */
1002 DoDelay = g_settings_get_boolean (settings, "delay-between-rolls");
1003 if (DisplayComputerThoughts == 0) /* Not set on the command-line */
1004 DisplayComputerThoughts = g_settings_get_boolean (settings, "display-computer-thoughts");
1005
1006 /* Read in new player names */
1007 player_names = g_settings_get_strv (settings, "player-names");
1008 n_player_names = g_strv_length (player_names);
1009 if (player_names) {
1010 n_player_names = MIN (n_player_names, MAX_NUMBER_OF_PLAYERS);
1011
1012 for (i = 0; i < n_player_names; ++i) {
1013 if (i == 0 && strcasecmp (player_names[i], _("Human")) == 0) {
1014 const char *realname;
1015
1016 realname = g_get_real_name ();
1017 if (realname && realname[0] && strcmp (realname, "Unknown") != 0) {
1018 players[i].name = g_locale_to_utf8 (realname, -1, NULL, NULL, NULL);
1019 }
1020 if (!players[i].name) {
1021 players[i].name = g_strdup (player_names[i]);
1022 }
1023 } else {
1024 players[i].name = g_strdup (player_names[i]);
1025 }
1026 }
1027
1028 g_strfreev (player_names);
1029 }
1030
1031 g_application_run (G_APPLICATION (application), argc, argv);
1032
1033 exit (0);
1034 }
1035