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