1 #include "config.h"
2 #include "game.h"
3 #include <gtk/gtk.h>
4 #include <string.h>
5 #include <glib.h>
6 
7 #include "game-rules.h"
8 #include "game.h"
9 
10 static void game_rules_init(GameRules * sg, gboolean show_all_rules);
11 static void verify_island_discovery_bonus(GtkButton * button,
12 					  gpointer user_data);
13 
dice_deck_toggled_callback(GtkToggleButton * dice_deck,gpointer user_data)14 static void dice_deck_toggled_callback(GtkToggleButton * dice_deck,
15 				       gpointer user_data)
16 {
17 	GameRules *gr = user_data;
18 	gtk_widget_set_sensitive(GTK_WIDGET(gr->num_dice_decks),
19 				 gtk_toggle_button_get_active(dice_deck));
20 	gtk_widget_set_sensitive(GTK_WIDGET(gr->num_removed_dice_cards),
21 				 gtk_toggle_button_get_active(dice_deck));
22 }
23 
24 /* Register the class */
game_rules_get_type(void)25 GType game_rules_get_type(void)
26 {
27 	static GType gp_type = 0;
28 
29 	if (!gp_type) {
30 		static const GTypeInfo gp_info = {
31 			sizeof(GameRulesClass),
32 			NULL,	/* base_init */
33 			NULL,	/* base_finalize */
34 			NULL,	/* class init */
35 			NULL,	/* class_finalize */
36 			NULL,	/* class_data */
37 			sizeof(GameRules),
38 			0,
39 			NULL,
40 			NULL
41 		};
42 		gp_type =
43 		    g_type_register_static(GTK_TYPE_GRID,
44 					   "GameRules", &gp_info, 0);
45 	}
46 	return gp_type;
47 }
48 
add_row(GameRules * gr,const gchar * name,const gchar * tooltip,guint row,GtkCheckButton ** check)49 static void add_row(GameRules * gr, const gchar * name,
50 		    const gchar * tooltip, guint row,
51 		    GtkCheckButton ** check)
52 {
53 	GtkWidget *check_btn;
54 
55 	check_btn = gtk_check_button_new_with_label(name);
56 	gtk_widget_show(check_btn);
57 	gtk_grid_attach(GTK_GRID(gr), check_btn, 0, row, 2, 1);
58 	*check = GTK_CHECK_BUTTON(check_btn);
59 
60 	gtk_widget_set_tooltip_text(check_btn, tooltip);
61 }
62 
63 /* Build the composite widget */
game_rules_init(GameRules * gr,gboolean show_all_rules)64 static void game_rules_init(GameRules * gr, gboolean show_all_rules)
65 {
66 	GtkWidget *label;
67 	GtkWidget *vbox_sevens;
68 	GtkWidget *hbox;
69 	GtkWidget *widget;
70 	gint idx;
71 	guint row;
72 
73 	gtk_grid_set_row_spacing(GTK_GRID(gr), 3);
74 	gtk_grid_set_column_spacing(GTK_GRID(gr), 5);
75 
76 	row = 0;
77 
78 	/* Label */
79 	label = gtk_label_new(_("Sevens rule"));
80 	gtk_widget_show(label);
81 	gtk_grid_attach(GTK_GRID(gr), label, 0, row, 1, 1);
82 	gtk_label_set_xalign(GTK_LABEL(label), 0.0);
83 	gtk_label_set_yalign(GTK_LABEL(label), 0.0);
84 
85 	gr->radio_sevens[0] = gtk_radio_button_new_with_label(NULL,
86 							      /* Sevens rule: normal */
87 							      _("Normal"));
88 	gr->radio_sevens[1] =
89 	    gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON
90 							(gr->radio_sevens
91 							 [0]),
92 							/* Sevens rule: reroll on 1st 2 turns */
93 							_(""
94 							  "Reroll on 1st 2 turns"));
95 	gr->radio_sevens[2] =
96 	    gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON
97 							(gr->radio_sevens
98 							 [0]),
99 							/* Sevens rule: reroll all 7s */
100 							_(""
101 							  "Reroll all 7s"));
102 
103 	vbox_sevens = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
104 	gtk_box_set_homogeneous(GTK_BOX(vbox_sevens), TRUE);
105 	gtk_widget_show(vbox_sevens);
106 	gtk_widget_set_tooltip_text(gr->radio_sevens[0],
107 				    /* Tooltip for sevens rule normal */
108 				    _(""
109 				      "All sevens move the robber or pirate"));
110 	gtk_widget_set_tooltip_text(gr->radio_sevens[1],
111 				    /* Tooltip for sevens rule reroll on 1st 2 turns */
112 				    _(""
113 				      "In the first two turns all sevens are rerolled"));
114 	gtk_widget_set_tooltip_text(gr->radio_sevens[2],
115 				    /* Tooltip for sevens rule reroll all */
116 				    _("All sevens are rerolled"));
117 
118 	for (idx = 0; idx < 3; ++idx) {
119 		gtk_widget_show(gr->radio_sevens[idx]);
120 		gtk_box_pack_start(GTK_BOX(vbox_sevens),
121 				   gr->radio_sevens[idx], TRUE, TRUE, 0);
122 	}
123 	gtk_grid_attach(GTK_GRID(gr), vbox_sevens, 1, row, 1, 1);
124 	gtk_widget_set_hexpand(vbox_sevens, TRUE);
125 
126 	row++;
127 	add_row(gr, _("Randomize terrain"), _("Randomize the terrain"),
128 		row++, &gr->random_terrain);
129 	if (show_all_rules) {
130 		add_row(gr, _("Use pirate"),
131 			_("Use the pirate to block ships"), row++,
132 			&gr->use_pirate);
133 		add_row(gr, _("Strict trade"),
134 			_("Allow trade only before building or buying"),
135 			row++, &gr->strict_trade);
136 		add_row(gr, _("Domestic trade"),
137 			_("Allow trade between players"), row++,
138 			&gr->domestic_trade);
139 		add_row(gr, _("Victory at end of turn"),
140 			_("Check for victory only at end of turn"), row++,
141 			&gr->check_victory_at_end_of_turn);
142 
143 		add_row(gr, _("Use dice deck"),
144 			_
145 			("Use a deck of 36 dice cards instead of real dice"),
146 			row++, &gr->use_dice_deck);
147 		g_signal_connect(G_OBJECT(gr->use_dice_deck), "toggled",
148 				 G_CALLBACK(dice_deck_toggled_callback),
149 				 gr);
150 
151 
152 		/* Label */
153 		label = gtk_label_new(_("Number of dice decks"));
154 		gtk_widget_show(label);
155 		gtk_grid_attach(GTK_GRID(gr), label, 0, row, 1, 1);
156 		gtk_label_set_xalign(GTK_LABEL(label), 0.0);
157 		gr->num_dice_decks =
158 		    GTK_SPIN_BUTTON(gtk_spin_button_new_with_range
159 				    (1, 5, 1));
160 		gtk_entry_set_alignment(GTK_ENTRY(gr->num_dice_decks),
161 					1.0);
162 		gtk_widget_show(GTK_WIDGET(gr->num_dice_decks));
163 		gtk_widget_set_sensitive(GTK_WIDGET(gr->num_dice_decks),
164 					 gtk_toggle_button_get_active
165 					 (GTK_TOGGLE_BUTTON
166 					  (gr->use_dice_deck)));
167 		gtk_widget_set_tooltip_text(GTK_WIDGET(gr->num_dice_decks),
168 					    _
169 					    ("The number of dice decks (of 36 cards each)"));
170 		gtk_grid_attach(GTK_GRID(gr),
171 				GTK_WIDGET(gr->num_dice_decks), 1, row, 1,
172 				1);
173 
174 		row++;
175 		/* Label */
176 		label = gtk_label_new(_("Number of removed dice cards"));
177 		gtk_widget_show(label);
178 		gtk_grid_attach(GTK_GRID(gr), label, 0, row, 1, 1);
179 		gtk_label_set_xalign(GTK_LABEL(label), 0.0);
180 		gr->num_removed_dice_cards =
181 		    GTK_SPIN_BUTTON(gtk_spin_button_new_with_range
182 				    (0, 30, 1));
183 		gtk_entry_set_alignment(GTK_ENTRY
184 					(gr->num_removed_dice_cards), 1.0);
185 		gtk_widget_show(GTK_WIDGET(gr->num_removed_dice_cards));
186 		gtk_widget_set_sensitive(GTK_WIDGET
187 					 (gr->num_removed_dice_cards),
188 					 gtk_toggle_button_get_active
189 					 (GTK_TOGGLE_BUTTON
190 					  (gr->use_dice_deck)));
191 		gtk_widget_set_tooltip_text(GTK_WIDGET
192 					    (gr->num_removed_dice_cards),
193 					    _
194 					    ("The number of dice cards that are removed after shuffling the deck"));
195 		gtk_grid_attach(GTK_GRID(gr),
196 				GTK_WIDGET(gr->num_removed_dice_cards), 1,
197 				row, 1, 1);
198 
199 		row++;
200 		/* Label */
201 		label = gtk_label_new(_("Island discovery bonuses"));
202 		gtk_widget_show(label);
203 		gtk_grid_attach(GTK_GRID(gr), label, 0, row, 1, 1);
204 		gtk_label_set_xalign(GTK_LABEL(label), 0.0);
205 
206 		hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
207 
208 		gr->island_bonus = GTK_ENTRY(gtk_entry_new());
209 		gtk_widget_show(GTK_WIDGET(gr->island_bonus));
210 		gtk_widget_set_tooltip_text(GTK_WIDGET(gr->island_bonus),
211 					    /* Tooltip for island bonus */
212 					    _(""
213 					      "A comma separated list of "
214 					      "bonus points for discovering "
215 					      "islands"));
216 		gtk_box_pack_start(GTK_BOX(hbox),
217 				   GTK_WIDGET(gr->island_bonus), TRUE,
218 				   TRUE, 0);
219 
220 		widget = gtk_button_new();
221 		gtk_button_set_image(GTK_BUTTON(widget),
222 				     gtk_image_new_from_icon_name
223 				     ("pioneers-checkmark",
224 				      GTK_ICON_SIZE_BUTTON));
225 		g_signal_connect(G_OBJECT(widget), "clicked",
226 				 G_CALLBACK(verify_island_discovery_bonus),
227 				 (gpointer) gr);
228 		gtk_widget_set_tooltip_text(widget,
229 					    /* Tooltip for the check button */
230 					    _("Check and correct island "
231 					      "discovery bonuses"));
232 		gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
233 
234 		gtk_grid_attach(GTK_GRID(gr), GTK_WIDGET(hbox), 1, row, 1,
235 				1);
236 		row++;
237 	} else {
238 		gr->use_pirate = NULL;
239 		gr->strict_trade = NULL;
240 		gr->domestic_trade = NULL;
241 		gr->check_victory_at_end_of_turn = NULL;
242 		gr->island_bonus = NULL;
243 	}
244 }
245 
246 /* Create a new instance of the widget */
game_rules_new(void)247 GtkWidget *game_rules_new(void)
248 {
249 	GameRules *widget =
250 	    GAMERULES(g_object_new(game_rules_get_type(), NULL));
251 	game_rules_init(widget, TRUE);
252 	return GTK_WIDGET(widget);
253 }
254 
255 
256 /* Create a new instance with only the changes that can be applied by the metaserver */
game_rules_new_metaserver(void)257 GtkWidget *game_rules_new_metaserver(void)
258 {
259 	GameRules *widget =
260 	    GAMERULES(g_object_new(game_rules_get_type(), NULL));
261 	game_rules_init(widget, FALSE);
262 	return GTK_WIDGET(widget);
263 }
264 
game_rules_set_random_terrain(GameRules * gr,gboolean val)265 void game_rules_set_random_terrain(GameRules * gr, gboolean val)
266 {
267 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gr->random_terrain),
268 				     val);
269 }
270 
game_rules_get_random_terrain(GameRules * gr)271 gboolean game_rules_get_random_terrain(GameRules * gr)
272 {
273 	return
274 	    gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
275 					 (gr->random_terrain));
276 }
277 
278 /* Set the sevens rule
279  * 0 = Normal
280  * 1 = Reroll first two turns
281  * 2 = Reroll all
282  */
game_rules_set_sevens_rule(GameRules * gr,guint sevens_rule)283 void game_rules_set_sevens_rule(GameRules * gr, guint sevens_rule)
284 {
285 	g_return_if_fail(sevens_rule < G_N_ELEMENTS(gr->radio_sevens));
286 
287 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
288 				     (gr->radio_sevens[sevens_rule]),
289 				     TRUE);
290 
291 }
292 
293 /* Get the sevens rule */
game_rules_get_sevens_rule(GameRules * gr)294 guint game_rules_get_sevens_rule(GameRules * gr)
295 {
296 	guint idx;
297 
298 	for (idx = 0; idx < G_N_ELEMENTS(gr->radio_sevens); idx++)
299 		if (gtk_toggle_button_get_active
300 		    (GTK_TOGGLE_BUTTON(gr->radio_sevens[idx])))
301 			return idx;
302 	return 0;
303 }
304 
game_rules_set_use_dice_deck(GameRules * gr,gboolean val)305 void game_rules_set_use_dice_deck(GameRules * gr, gboolean val)
306 {
307 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gr->use_dice_deck),
308 				     val);
309 }
310 
game_rules_get_use_dice_deck(GameRules * gr)311 gboolean game_rules_get_use_dice_deck(GameRules * gr)
312 {
313 	return
314 	    gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
315 					 (gr->use_dice_deck));
316 }
317 
game_rules_set_num_dice_decks(GameRules * gr,guint val)318 void game_rules_set_num_dice_decks(GameRules * gr, guint val)
319 {
320 	gtk_spin_button_set_value(gr->num_dice_decks, val);
321 }
322 
game_rules_get_num_dice_decks(GameRules * gr)323 guint game_rules_get_num_dice_decks(GameRules * gr)
324 {
325 	return gtk_spin_button_get_value_as_int(gr->num_dice_decks);
326 }
327 
game_rules_set_num_removed_dice_cards(GameRules * gr,guint val)328 void game_rules_set_num_removed_dice_cards(GameRules * gr, guint val)
329 {
330 	gtk_spin_button_set_value(gr->num_removed_dice_cards, val);
331 }
332 
game_rules_get_num_removed_dice_cards(GameRules * gr)333 guint game_rules_get_num_removed_dice_cards(GameRules * gr)
334 {
335 	return
336 	    gtk_spin_button_get_value_as_int(gr->num_removed_dice_cards);
337 }
338 
game_rules_set_use_pirate(GameRules * gr,gboolean val,gint num_ships)339 void game_rules_set_use_pirate(GameRules * gr, gboolean val,
340 			       gint num_ships)
341 {
342 	if (num_ships == 0) {
343 		gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON
344 						   (gr->use_pirate), TRUE);
345 		gtk_widget_set_sensitive(GTK_WIDGET(gr->use_pirate),
346 					 FALSE);
347 	} else {
348 		gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON
349 						   (gr->use_pirate),
350 						   FALSE);
351 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
352 					     (gr->use_pirate), val);
353 		gtk_widget_set_sensitive(GTK_WIDGET(gr->use_pirate), TRUE);
354 	}
355 }
356 
game_rules_get_use_pirate(GameRules * gr)357 gboolean game_rules_get_use_pirate(GameRules * gr)
358 {
359 	return
360 	    gtk_toggle_button_get_inconsistent(GTK_TOGGLE_BUTTON
361 					       (gr->use_pirate)) ? FALSE :
362 	    gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
363 					 (gr->use_pirate));
364 }
365 
game_rules_set_strict_trade(GameRules * gr,gboolean val)366 void game_rules_set_strict_trade(GameRules * gr, gboolean val)
367 {
368 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gr->strict_trade),
369 				     val);
370 }
371 
game_rules_get_strict_trade(GameRules * gr)372 gboolean game_rules_get_strict_trade(GameRules * gr)
373 {
374 	return
375 	    gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
376 					 (gr->strict_trade));
377 }
378 
game_rules_set_domestic_trade(GameRules * gr,gboolean val)379 void game_rules_set_domestic_trade(GameRules * gr, gboolean val)
380 {
381 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gr->domestic_trade),
382 				     val);
383 }
384 
game_rules_get_domestic_trade(GameRules * gr)385 gboolean game_rules_get_domestic_trade(GameRules * gr)
386 {
387 	return
388 	    gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
389 					 (gr->domestic_trade));
390 }
391 
game_rules_set_victory_at_end_of_turn(GameRules * gr,gboolean val)392 void game_rules_set_victory_at_end_of_turn(GameRules * gr, gboolean val)
393 {
394 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
395 				     (gr->check_victory_at_end_of_turn),
396 				     val);
397 }
398 
game_rules_get_victory_at_end_of_turn(GameRules * gr)399 gboolean game_rules_get_victory_at_end_of_turn(GameRules * gr)
400 {
401 	return
402 	    gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
403 					 (gr->
404 					  check_victory_at_end_of_turn));
405 }
406 
game_rules_set_island_discovery_bonus(GameRules * gr,GArray * val)407 void game_rules_set_island_discovery_bonus(GameRules * gr, GArray * val)
408 {
409 	gchar *text;
410 
411 	text = format_int_list(NULL, val);
412 	if (text != NULL) {
413 		gtk_entry_set_text(gr->island_bonus, text);
414 	} else {
415 		gtk_entry_set_text(gr->island_bonus, "");
416 	}
417 	g_free(text);
418 }
419 
game_rules_get_island_discovery_bonus(GameRules * gr)420 GArray *game_rules_get_island_discovery_bonus(GameRules * gr)
421 {
422 	return build_int_list(gtk_entry_get_text(gr->island_bonus));
423 }
424 
verify_island_discovery_bonus(G_GNUC_UNUSED GtkButton * button,gpointer user_data)425 static void verify_island_discovery_bonus(G_GNUC_UNUSED GtkButton * button,
426 					  gpointer user_data)
427 {
428 	GameRules *gr = GAMERULES(user_data);
429 	GArray *bonuses;
430 
431 	bonuses = game_rules_get_island_discovery_bonus(gr);
432 	game_rules_set_island_discovery_bonus(gr, bonuses);
433 	if (bonuses != NULL)
434 		g_array_free(bonuses, TRUE);
435 }
436