1 /* Pioneers - Implementation of the excellent Settlers of Catan board game.
2  *   Go buy a copy.
3  *
4  * Copyright (C) 1999 Dave Cole
5  * Copyright (C) 2003 Bas Wijnen <shevek@fmf.nl>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 /* This file consists of gui state functions and callbacks to set them */
23 #include "config.h"
24 #include "frontend.h"
25 #include "cost.h"
26 #include "histogram.h"
27 #include "audio.h"
28 #include "notification.h"
29 #include "client.h"
30 
31 /* local functions */
32 static void frontend_state_robber(GuiEvent event);
33 static void frontend_state_turn(GuiEvent event);
34 static void build_road_cb(MapElement edge, MapElement extra);
35 static void build_ship_cb(MapElement edge, MapElement extra);
36 static void build_bridge_cb(MapElement edge, MapElement extra);
37 static void move_ship_cb(MapElement edge, MapElement extra);
38 static void build_settlement_cb(MapElement node, MapElement extra);
39 static void build_city_cb(MapElement node, MapElement extra);
40 static void build_city_wall_cb(MapElement node, MapElement extra);
41 
dummy_state(G_GNUC_UNUSED GuiEvent event)42 static void dummy_state(G_GNUC_UNUSED GuiEvent event)
43 {
44 }
45 
46 /* for gold and discard, remember the previous gui state */
47 static GuiState previous_state = dummy_state;
48 
49 static gboolean discard_busy = FALSE, robber_busy = FALSE;
50 
51 static GtkWidget *game_over_dlg = NULL;
52 
frontend_init_game(void)53 void frontend_init_game(void)
54 {
55 	notification_close();
56 	player_clear_summary();
57 	chat_clear_names();
58 	develop_reset();
59 	histogram_reset();
60 	gui_reset();
61 	if (game_over_dlg != NULL) {
62 		gtk_widget_destroy(game_over_dlg);
63 	}
64 }
65 
frontend_state_idle(G_GNUC_UNUSED GuiEvent event)66 static void frontend_state_idle(G_GNUC_UNUSED GuiEvent event)
67 {
68 	/* don't react on any event when idle. */
69 	/* (except of course chat and name change events, but they are
70 	 * handled in route_event) */
71 }
72 
build_road_cb(MapElement edge,G_GNUC_UNUSED MapElement extra)73 void build_road_cb(MapElement edge, G_GNUC_UNUSED MapElement extra)
74 {
75 	cb_build_road(edge.edge);
76 }
77 
build_ship_cb(MapElement edge,G_GNUC_UNUSED MapElement extra)78 void build_ship_cb(MapElement edge, G_GNUC_UNUSED MapElement extra)
79 {
80 	cb_build_ship(edge.edge);
81 }
82 
do_move_ship_cb(MapElement edge,MapElement ship_from)83 static void do_move_ship_cb(MapElement edge, MapElement ship_from)
84 {
85 	cb_move_ship(ship_from.edge, edge.edge);
86 	gui_prompt_hide();
87 }
88 
89 /** Edge cursor check function.
90  *
91  * Determine whether or not a ship can be moved to this edge by the
92  * specified player.  Perform the following checks:
93  * 1 - Ship cannot be moved to where it comes from
94  * 2 - A ship must be buildable at the destination if the ship is moved away
95  *     from its current location.
96  */
can_ship_be_moved_to(MapElement ship_to,G_GNUC_UNUSED gint owner,MapElement ship_from)97 static gboolean can_ship_be_moved_to(MapElement ship_to,
98 				     G_GNUC_UNUSED gint owner,
99 				     MapElement ship_from)
100 {
101 	return can_move_ship(ship_from.edge, ship_to.edge);
102 }
103 
build_bridge_cb(MapElement edge,G_GNUC_UNUSED MapElement extra)104 void build_bridge_cb(MapElement edge, G_GNUC_UNUSED MapElement extra)
105 {
106 	cb_build_bridge(edge.edge);
107 }
108 
cancel_move_ship_cb(void)109 static void cancel_move_ship_cb(void)
110 {
111 	callbacks.instructions(_("Ship movement canceled."));
112 	gui_prompt_hide();
113 }
114 
move_ship_cb(MapElement edge,G_GNUC_UNUSED MapElement extra)115 void move_ship_cb(MapElement edge, G_GNUC_UNUSED MapElement extra)
116 {
117 	MapElement ship_from;
118 	ship_from.edge = edge.edge;
119 	callbacks.instructions(_("Select a new location for the ship."));
120 	gui_prompt_show(_("Select a new location for the ship."));
121 	gui_cursor_set(SHIP_CURSOR, can_ship_be_moved_to, do_move_ship_cb,
122 		       cancel_move_ship_cb, &ship_from);
123 }
124 
build_settlement_cb(MapElement node,G_GNUC_UNUSED MapElement extra)125 void build_settlement_cb(MapElement node, G_GNUC_UNUSED MapElement extra)
126 {
127 	cb_build_settlement(node.node);
128 }
129 
build_city_cb(MapElement node,G_GNUC_UNUSED MapElement extra)130 void build_city_cb(MapElement node, G_GNUC_UNUSED MapElement extra)
131 {
132 	cb_build_city(node.node);
133 }
134 
build_city_wall_cb(MapElement node,G_GNUC_UNUSED MapElement extra)135 void build_city_wall_cb(MapElement node, G_GNUC_UNUSED MapElement extra)
136 {
137 	cb_build_city_wall(node.node);
138 }
139 
140 /* trade */
frontend_state_trade(GuiEvent event)141 static void frontend_state_trade(GuiEvent event)
142 {
143 	static gboolean trading = FALSE;
144 	const QuoteInfo *quote;
145 	switch (event) {
146 	case GUI_UPDATE:
147 		frontend_gui_check(GUI_TRADE_CALL, can_call_for_quotes());
148 		frontend_gui_check(GUI_TRADE_ACCEPT,
149 				   trade_valid_selection());
150 		frontend_gui_check(GUI_TRADE_FINISH, TRUE);
151 		frontend_gui_check(GUI_TRADE, TRUE);
152 		gui_cursor_none();	/* Finish single click build */
153 		break;
154 	case GUI_TRADE_CALL:
155 		trading = TRUE;
156 		trade_new_trade();
157 		cb_domestic(trade_we_supply(), trade_we_receive());
158 		return;
159 	case GUI_TRADE_ACCEPT:
160 		quote = trade_current_quote();
161 		g_assert(quote != NULL);
162 		if (quote->is_domestic) {
163 			trade_perform_domestic(my_player_num(),
164 					       quote->var.d.player_num,
165 					       quote->var.d.quote_num,
166 					       quote->var.d.supply,
167 					       quote->var.d.receive);
168 		} else {
169 			trade_perform_maritime(quote->var.m.ratio,
170 					       quote->var.m.supply,
171 					       quote->var.m.receive);
172 		}
173 		return;
174 	case GUI_TRADE_FINISH:
175 	case GUI_TRADE:
176 		/* stop trading.  Only let the network know about it if it
177 		 * knew we were trading in the first place. */
178 		if (trading)
179 			cb_end_trade();
180 		trading = FALSE;
181 		trade_finish();
182 		set_gui_state(frontend_state_turn);
183 		return;
184 	default:
185 		break;
186 	}
187 }
188 
frontend_trade_add_quote(int player_num,int quote_num,const gint * they_supply,const gint * they_receive)189 void frontend_trade_add_quote(int player_num, int quote_num,
190 			      const gint * they_supply,
191 			      const gint * they_receive)
192 {
193 	trade_add_quote(player_num, quote_num, they_supply, they_receive);
194 	frontend_gui_update();
195 }
196 
frontend_trade_remove_quote(int player_num,int quote_num)197 void frontend_trade_remove_quote(int player_num, int quote_num)
198 {
199 	trade_delete_quote(player_num, quote_num);
200 	frontend_gui_update();
201 }
202 
frontend_trade_player_end(gint player_num)203 void frontend_trade_player_end(gint player_num)
204 {
205 	trade_player_finish(player_num);
206 	frontend_gui_update();
207 }
208 
frontend_state_quote(GuiEvent event)209 static void frontend_state_quote(GuiEvent event)
210 {
211 	switch (event) {
212 	case GUI_UPDATE:
213 		frontend_gui_check(GUI_QUOTE_SUBMIT, can_submit_quote());
214 		frontend_gui_check(GUI_QUOTE_DELETE, can_delete_quote());
215 		frontend_gui_check(GUI_QUOTE_REJECT, can_reject_quote());
216 		break;
217 	case GUI_QUOTE_SUBMIT:
218 		cb_quote(quote_next_num(), quote_we_supply(),
219 			 quote_we_receive());
220 		return;
221 	case GUI_QUOTE_DELETE:
222 		cb_delete_quote(quote_current_quote()->var.d.quote_num);
223 		return;
224 	case GUI_QUOTE_REJECT:
225 		quote_player_finish(my_player_num());
226 		cb_end_quote();
227 		return;
228 	default:
229 		break;
230 	}
231 }
232 
frontend_quote(gint player_num,gint * they_supply,gint * they_receive)233 void frontend_quote(gint player_num, gint * they_supply,
234 		    gint * they_receive)
235 {
236 	if (get_gui_state() == frontend_state_quote) {
237 		quote_begin_again(player_num, they_supply, they_receive);
238 	} else {
239 		quote_begin(player_num, they_supply, they_receive);
240 		set_gui_state(frontend_state_quote);
241 	}
242 	frontend_gui_update();
243 }
244 
frontend_quote_add(int player_num,int quote_num,const gint * they_supply,const gint * they_receive)245 void frontend_quote_add(int player_num, int quote_num,
246 			const gint * they_supply,
247 			const gint * they_receive)
248 {
249 	quote_add_quote(player_num, quote_num, they_supply, they_receive);
250 	frontend_gui_update();
251 }
252 
frontend_quote_remove(int player_num,int quote_num)253 void frontend_quote_remove(int player_num, int quote_num)
254 {
255 	quote_delete_quote(player_num, quote_num);
256 	frontend_gui_update();
257 }
258 
frontend_quote_player_end(gint player_num)259 void frontend_quote_player_end(gint player_num)
260 {
261 	quote_player_finish(player_num);
262 	frontend_gui_update();
263 }
264 
frontend_quote_end(void)265 void frontend_quote_end(void)
266 {
267 	if (get_gui_state() == frontend_state_quote) {
268 		quote_finish();
269 		set_gui_state(frontend_state_idle);
270 	}
271 }
272 
frontend_quote_start(void)273 void frontend_quote_start(void)
274 {
275 	/*set_gui_state (frontend_state_quote); */
276 }
277 
frontend_quote_monitor(void)278 void frontend_quote_monitor(void)
279 {
280 }
281 
check_road(MapElement element,gint owner,G_GNUC_UNUSED MapElement extra)282 static gboolean check_road(MapElement element, gint owner,
283 			   G_GNUC_UNUSED MapElement extra)
284 {
285 	return can_road_be_built(element.edge, owner);
286 }
287 
check_ship(MapElement element,gint owner,G_GNUC_UNUSED MapElement extra)288 static gboolean check_ship(MapElement element, gint owner,
289 			   G_GNUC_UNUSED MapElement extra)
290 {
291 	return can_ship_be_built(element.edge, owner);
292 }
293 
check_ship_move(MapElement element,gint owner,G_GNUC_UNUSED MapElement extra)294 static gboolean check_ship_move(MapElement element, gint owner,
295 				G_GNUC_UNUSED MapElement extra)
296 {
297 	return can_ship_be_moved(element.edge, owner);
298 }
299 
check_bridge(MapElement element,gint owner,G_GNUC_UNUSED MapElement extra)300 static gboolean check_bridge(MapElement element, gint owner,
301 			     G_GNUC_UNUSED MapElement extra)
302 {
303 	return can_bridge_be_built(element.edge, owner);
304 }
305 
check_settlement(MapElement element,gint owner,G_GNUC_UNUSED MapElement extra)306 static gboolean check_settlement(MapElement element, gint owner,
307 				 G_GNUC_UNUSED MapElement extra)
308 {
309 	return can_settlement_be_built(element.node, owner);
310 }
311 
check_city(MapElement element,gint owner,G_GNUC_UNUSED MapElement extra)312 static gboolean check_city(MapElement element, gint owner,
313 			   G_GNUC_UNUSED MapElement extra)
314 {
315 	return can_settlement_be_upgraded(element.node, owner);
316 }
317 
check_city_wall(MapElement element,gint owner,G_GNUC_UNUSED MapElement extra)318 static gboolean check_city_wall(MapElement element, gint owner,
319 				G_GNUC_UNUSED MapElement extra)
320 {
321 	return can_city_wall_be_built(element.node, owner);
322 }
323 
324 /* turn */
frontend_state_turn(GuiEvent event)325 static void frontend_state_turn(GuiEvent event)
326 {
327 	switch (event) {
328 	case GUI_UPDATE:
329 		frontend_gui_check(GUI_ROLL, !have_rolled_dice());
330 		frontend_gui_check(GUI_UNDO, can_undo());
331 		frontend_gui_check(GUI_ROAD, turn_can_build_road());
332 		frontend_gui_check(GUI_SHIP, turn_can_build_ship());
333 		frontend_gui_check(GUI_MOVE_SHIP, turn_can_move_ship());
334 		frontend_gui_check(GUI_BRIDGE, turn_can_build_bridge());
335 		frontend_gui_check(GUI_SETTLEMENT,
336 				   turn_can_build_settlement());
337 		frontend_gui_check(GUI_CITY, turn_can_build_city());
338 		frontend_gui_check(GUI_CITY_WALL,
339 				   turn_can_build_city_wall());
340 		frontend_gui_check(GUI_TRADE, turn_can_trade());
341 		frontend_gui_check(GUI_PLAY_DEVELOP,
342 				   can_play_develop(develop_current_idx
343 						    ()));
344 		frontend_gui_check(GUI_BUY_DEVELOP, can_buy_develop());
345 		frontend_gui_check(GUI_FINISH, have_rolled_dice());
346 
347 		guimap_single_click_set_functions(check_road,
348 						  build_road_cb,
349 						  check_ship,
350 						  build_ship_cb,
351 						  check_bridge,
352 						  build_bridge_cb,
353 						  check_settlement,
354 						  build_settlement_cb,
355 						  check_city,
356 						  build_city_cb,
357 						  check_city_wall,
358 						  build_city_wall_cb,
359 						  check_ship_move,
360 						  move_ship_cb,
361 						  cancel_move_ship_cb);
362 		break;
363 	case GUI_ROLL:
364 		cb_roll();
365 		break;
366 	case GUI_UNDO:
367 		cb_undo();
368 		return;
369 	case GUI_ROAD:
370 		gui_cursor_set(ROAD_CURSOR, check_road, build_road_cb,
371 			       NULL, NULL);
372 		return;
373 	case GUI_SHIP:
374 		gui_cursor_set(SHIP_CURSOR, check_ship, build_ship_cb,
375 			       NULL, NULL);
376 		return;
377 	case GUI_MOVE_SHIP:
378 		gui_cursor_set(SHIP_CURSOR, check_ship_move, move_ship_cb,
379 			       NULL, NULL);
380 		return;
381 	case GUI_BRIDGE:
382 		gui_cursor_set(BRIDGE_CURSOR, check_bridge,
383 			       build_bridge_cb, NULL, NULL);
384 		return;
385 	case GUI_SETTLEMENT:
386 		gui_cursor_set(SETTLEMENT_CURSOR, check_settlement,
387 			       build_settlement_cb, NULL, NULL);
388 		return;
389 	case GUI_CITY:
390 		gui_cursor_set(CITY_CURSOR, check_city, build_city_cb,
391 			       NULL, NULL);
392 		return;
393 	case GUI_CITY_WALL:
394 		gui_cursor_set(CITY_WALL_CURSOR, check_city_wall,
395 			       build_city_wall_cb, NULL, NULL);
396 		return;
397 	case GUI_TRADE:
398 		trade_begin();
399 		set_gui_state(frontend_state_trade);
400 		return;
401 	case GUI_PLAY_DEVELOP:
402 		cb_play_develop(develop_current_idx());
403 		return;
404 	case GUI_BUY_DEVELOP:
405 		cb_buy_develop();
406 		return;
407 	case GUI_FINISH:
408 		cb_end_turn();
409 		gui_cursor_none();	/* Finish single click build */
410 		set_gui_state(frontend_state_idle);
411 		return;
412 	default:
413 		break;
414 	}
415 }
416 
frontend_turn(void)417 void frontend_turn(void)
418 {
419 	/* if it already is our turn, just update the gui (maybe something
420 	 * happened), but don't beep */
421 	if (get_gui_state() == frontend_state_turn
422 	    || get_gui_state() == frontend_state_trade
423 	    || get_gui_state() == frontend_state_robber) {
424 		/* this is in the if, because it gets called from set_gui_state
425 		 * anyway. */
426 		frontend_gui_update();
427 		return;
428 	}
429 	set_gui_state(frontend_state_turn);
430 	play_sound(SOUND_TURN);
431 	/* Notification */
432 	notification_send(_("It is your turn."), ICON_DICE);
433 
434 	if (!have_rolled_dice()) {
435 		gboolean have_resource_card;
436 		gboolean have_soldier_card;
437 		guint i;
438 		const Deck *deck = get_devel_deck();
439 		TAutomaticRoll automatic_roll = get_automatic_roll();
440 
441 		have_soldier_card = FALSE;
442 		for (i = 0; i < deck_count(deck); i++) {
443 			DevelType cardtype = deck_get_guint(deck, i);
444 			if (cardtype == DEVEL_SOLDIER
445 			    && can_play_develop(i)) {
446 				have_soldier_card = TRUE;
447 				break;
448 			}
449 		}
450 		have_resource_card = (deck_count(deck) != 0);
451 
452 		switch (automatic_roll) {
453 		case ROLL_MANUALLY:
454 			break;
455 		case ROLL_AUTOMATICALLY_EXCEPT_WITH_SOLDIER_CARD:
456 			if (!have_soldier_card) {
457 				cb_roll();
458 			}
459 			break;
460 		case ROLL_AUTOMATICALLY_EXCEPT_WITH_RESOURCE_CARD:
461 			if (!have_resource_card) {
462 				cb_roll();
463 			}
464 			break;
465 		case ROLL_AUTOMATICALLY:
466 			cb_roll();
467 			break;
468 		}
469 	}
470 }
471 
472 /* development card actions */
473 /* road building */
frontend_state_roadbuilding(GuiEvent event)474 static void frontend_state_roadbuilding(GuiEvent event)
475 {
476 	switch (event) {
477 	case GUI_UPDATE:
478 		frontend_gui_check(GUI_UNDO, can_undo());
479 		frontend_gui_check(GUI_ROAD,
480 				   road_building_can_build_road());
481 		frontend_gui_check(GUI_SHIP,
482 				   road_building_can_build_ship());
483 		frontend_gui_check(GUI_BRIDGE,
484 				   road_building_can_build_bridge());
485 		frontend_gui_check(GUI_FINISH, road_building_can_finish());
486 		guimap_single_click_set_functions(check_road,
487 						  build_road_cb,
488 						  check_ship,
489 						  build_ship_cb,
490 						  check_bridge,
491 						  build_bridge_cb,
492 						  NULL, NULL, NULL,
493 						  NULL, NULL, NULL,
494 						  NULL, NULL, NULL);
495 		break;
496 	case GUI_UNDO:
497 		cb_undo();
498 		return;
499 	case GUI_ROAD:
500 		gui_cursor_set(ROAD_CURSOR, check_road, build_road_cb,
501 			       NULL, NULL);
502 		return;
503 	case GUI_SHIP:
504 		gui_cursor_set(SHIP_CURSOR, check_ship, build_ship_cb,
505 			       NULL, NULL);
506 		return;
507 	case GUI_BRIDGE:
508 		gui_cursor_set(BRIDGE_CURSOR, check_bridge,
509 			       build_bridge_cb, NULL, NULL);
510 		return;
511 	case GUI_FINISH:
512 		cb_end_turn();
513 		gui_cursor_none();	/* Finish single click build */
514 		/** @todo RC 2018-03-14 The line below allows trade after the
515 		 * road building development card is played. There is still a
516 		 * loophole: build a road, play the development card. Now trade
517 		 * is enabled, but should not have been.
518 		 * Even the server allows trade in this scenario. */
519 		build_new_turn();	/* Re-enable trade */
520 		set_gui_state(frontend_state_turn);
521 		gui_prompt_hide();
522 		return;
523 	default:
524 		break;
525 	}
526 }
527 
frontend_roadbuilding(gint num_roads)528 void frontend_roadbuilding(gint num_roads)
529 {
530 	gui_prompt_show(road_building_message(num_roads));
531 	if (get_gui_state() == frontend_state_roadbuilding)
532 		return;
533 	set_gui_state(frontend_state_roadbuilding);
534 }
535 
536 /* monopoly */
frontend_state_monopoly(GuiEvent event)537 static void frontend_state_monopoly(GuiEvent event)
538 {
539 	switch (event) {
540 	case GUI_UPDATE:
541 		frontend_gui_check(GUI_MONOPOLY, TRUE);
542 		break;
543 	case GUI_MONOPOLY:
544 		cb_choose_monopoly(monopoly_type());
545 		monopoly_destroy_dlg();
546 		set_gui_state(frontend_state_turn);
547 		return;
548 	default:
549 		break;
550 	}
551 }
552 
frontend_monopoly(void)553 void frontend_monopoly(void)
554 {
555 	monopoly_create_dlg();
556 	set_gui_state(frontend_state_monopoly);
557 }
558 
559 /* year of plenty */
frontend_state_plenty(GuiEvent event)560 static void frontend_state_plenty(GuiEvent event)
561 {
562 	gint plenty[NO_RESOURCE];
563 	switch (event) {
564 	case GUI_UPDATE:
565 		frontend_gui_check(GUI_PLENTY, plenty_can_activate());
566 		break;
567 	case GUI_PLENTY:
568 		plenty_resources(plenty);
569 		cb_choose_plenty(plenty);
570 		plenty_destroy_dlg();
571 		set_gui_state(frontend_state_turn);
572 		return;
573 	default:
574 		break;
575 	}
576 }
577 
frontend_plenty(const gint * bank)578 void frontend_plenty(const gint * bank)
579 {
580 	plenty_create_dlg(bank);
581 	set_gui_state(frontend_state_plenty);
582 }
583 
584 /* general actions */
585 /* discard */
frontend_state_discard(GuiEvent event)586 static void frontend_state_discard(GuiEvent event)
587 {
588 	gint discards[NO_RESOURCE];
589 
590 	switch (event) {
591 	case GUI_UPDATE:
592 		frontend_gui_check(GUI_DISCARD, can_discard());
593 		break;
594 	case GUI_DISCARD:
595 		discard_get_list(discards);
596 		cb_discard(discards);
597 		return;
598 	default:
599 		break;
600 	}
601 }
602 
frontend_discard(void)603 void frontend_discard(void)
604 {
605 	/* set state to idle until we must discard (or discard ends) */
606 	if (!discard_busy) {
607 		discard_busy = TRUE;
608 		discard_begin();
609 		g_assert(previous_state == dummy_state);
610 		previous_state = get_gui_state();
611 		set_gui_state(frontend_state_idle);
612 		gui_cursor_none();	/* Clear possible cursor */
613 	}
614 }
615 
frontend_discard_add(gint player_num,gint discard_num)616 void frontend_discard_add(gint player_num, gint discard_num)
617 {
618 	if (player_num == my_player_num())
619 		g_assert(callback_mode == MODE_DISCARD);
620 	discard_player_must(player_num, discard_num);
621 	if (player_num == my_player_num())
622 		set_gui_state(frontend_state_discard);
623 	frontend_gui_update();
624 }
625 
frontend_discard_remove(gint player_num)626 void frontend_discard_remove(gint player_num)
627 {
628 	if (discard_busy) {
629 		discard_player_did(player_num);
630 		if (player_num == my_player_num())
631 			set_gui_state(frontend_state_idle);
632 	}
633 	frontend_gui_update();
634 }
635 
frontend_discard_done(void)636 void frontend_discard_done(void)
637 {
638 	discard_busy = FALSE;
639 	discard_end();
640 	if (previous_state != dummy_state) {
641 		set_gui_state(previous_state);
642 		previous_state = dummy_state;
643 	}
644 }
645 
646 /* gold */
frontend_state_gold(GuiEvent event)647 static void frontend_state_gold(GuiEvent event)
648 {
649 	gint gold[NO_RESOURCE];
650 	switch (event) {
651 	case GUI_UPDATE:
652 		frontend_gui_check(GUI_CHOOSE_GOLD, can_choose_gold());
653 		break;
654 	case GUI_CHOOSE_GOLD:
655 		choose_gold_get_list(gold);
656 		cb_choose_gold(gold);
657 		return;
658 	default:
659 		break;
660 	}
661 }
662 
frontend_gold(void)663 void frontend_gold(void)
664 {
665 	if (get_gui_state() != frontend_state_gold) {
666 		gold_choose_begin();
667 		g_assert(previous_state == dummy_state);
668 		previous_state = get_gui_state();
669 		set_gui_state(frontend_state_gold);
670 		gui_cursor_none();	/* Clear possible cursor */
671 	}
672 }
673 
frontend_gold_add(gint player_num,gint gold_num)674 void frontend_gold_add(gint player_num, gint gold_num)
675 {
676 	gold_choose_player_prepare(player_num, gold_num);
677 	frontend_gui_update();
678 }
679 
frontend_gold_choose(gint gold_num,const gint * bank)680 void frontend_gold_choose(gint gold_num, const gint * bank)
681 {
682 	gold_choose_player_must(gold_num, bank);
683 	frontend_gui_update();
684 }
685 
frontend_gold_remove(gint player_num,gint * resources)686 void frontend_gold_remove(gint player_num, gint * resources)
687 {
688 	gold_choose_player_did(player_num, resources);
689 	frontend_gui_update();
690 }
691 
frontend_gold_done(void)692 void frontend_gold_done(void)
693 {
694 	gold_choose_end();
695 	if (previous_state != dummy_state) {
696 		set_gui_state(previous_state);
697 		previous_state = dummy_state;
698 	}
699 }
700 
frontend_game_over(gint player,gint points)701 void frontend_game_over(gint player, gint points)
702 {
703 	gui_cursor_none();	/* Clear possible (robber) cursor */
704 	if (robber_busy) {
705 		robber_busy = FALSE;
706 		gui_prompt_hide();
707 	}
708 	game_over_dlg = gameover_create_dlg(player, points);
709 	g_signal_connect(G_OBJECT(game_over_dlg), "destroy",
710 			 G_CALLBACK(gtk_widget_destroyed), &game_over_dlg);
711 	notification_close();
712 	set_gui_state(frontend_state_idle);
713 }
714 
frontend_rolled_dice(gint die1,gint die2,gint player_num)715 void frontend_rolled_dice(gint die1, gint die2, gint player_num)
716 {
717 	histogram_dice_rolled(die1 + die2, player_num);
718 	identity_set_dice(die1, die2);
719 	gui_highlight_chits(die1 + die2);
720 	notification_close();
721 	frontend_gui_update();
722 }
723 
frontend_state_robber(GuiEvent event)724 static void frontend_state_robber(GuiEvent event)
725 {
726 	switch (event) {
727 	case GUI_UPDATE:
728 		frontend_gui_check(GUI_UNDO, callback_mode == MODE_ROB);
729 		break;
730 	case GUI_UNDO:
731 		cb_undo();
732 		/* restart robber placement.  */
733 		frontend_robber();
734 		break;
735 	default:
736 		break;
737 	}
738 }
739 
rob_building(MapElement node,G_GNUC_UNUSED MapElement extra)740 static void rob_building(MapElement node, G_GNUC_UNUSED MapElement extra)
741 {
742 	cb_rob(node.node->owner);
743 }
744 
rob_edge(MapElement edge,G_GNUC_UNUSED MapElement extra)745 static void rob_edge(MapElement edge, G_GNUC_UNUSED MapElement extra)
746 {
747 	cb_rob(edge.edge->owner);
748 }
749 
750 /* Return TRUE if the node can be robbed. */
can_building_be_robbed(MapElement node,G_GNUC_UNUSED int owner,MapElement robber)751 static gboolean can_building_be_robbed(MapElement node,
752 				       G_GNUC_UNUSED int owner,
753 				       MapElement robber)
754 {
755 	guint idx;
756 
757 	/* Can only steal from buildings that are not owned by me */
758 	if (node.node->type == BUILD_NONE
759 	    || node.node->owner == my_player_num())
760 		return FALSE;
761 
762 	/* Can only steal if the owner has some resources */
763 	if (player_get(node.node->owner)->statistics[STAT_RESOURCES] == 0)
764 		return FALSE;
765 
766 	/* Can only steal from buildings adjacent to hex with robber */
767 	for (idx = 0; idx < G_N_ELEMENTS(node.node->hexes); idx++)
768 		if (node.node->hexes[idx] == robber.hex)
769 			return TRUE;
770 	return FALSE;
771 }
772 
773 /** Returns TRUE if the edge can be robbed. */
can_edge_be_robbed(MapElement edge,G_GNUC_UNUSED int owner,MapElement pirate)774 static gboolean can_edge_be_robbed(MapElement edge,
775 				   G_GNUC_UNUSED int owner,
776 				   MapElement pirate)
777 {
778 	guint idx;
779 
780 	/* Can only steal from ships that are not owned by me */
781 	if (edge.edge->type != BUILD_SHIP
782 	    || edge.edge->owner == my_player_num())
783 		return FALSE;
784 
785 	/* Can only steal if the owner has some resources */
786 	if (player_get(edge.edge->owner)->statistics[STAT_RESOURCES] == 0)
787 		return FALSE;
788 
789 	/* Can only steal from edges adjacent to hex with pirate */
790 	for (idx = 0; idx < G_N_ELEMENTS(edge.edge->hexes); idx++)
791 		if (edge.edge->hexes[idx] == pirate.hex)
792 			return TRUE;
793 	return FALSE;
794 }
795 
796 /* User just placed the robber
797  */
place_robber_or_pirate_cb(MapElement hex,G_GNUC_UNUSED MapElement extra)798 static void place_robber_or_pirate_cb(MapElement hex,
799 				      G_GNUC_UNUSED MapElement extra)
800 {
801 	cb_place_robber(hex.hex);
802 }
803 
frontend_steal_ship(void)804 void frontend_steal_ship(void)
805 {
806 	MapElement hex;
807 	hex.hex = map_pirate_hex(callbacks.get_map());
808 
809 	gui_cursor_set(STEAL_SHIP_CURSOR, can_edge_be_robbed, rob_edge,
810 		       NULL, &hex);
811 	gui_prompt_show(_("Select the ship to steal from."));
812 }
813 
frontend_steal_building(void)814 void frontend_steal_building(void)
815 {
816 	MapElement hex;
817 	hex.hex = map_robber_hex(callbacks.get_map());
818 
819 	gui_cursor_set(STEAL_BUILDING_CURSOR, can_building_be_robbed,
820 		       rob_building, NULL, &hex);
821 	gui_prompt_show(_("Select the building to steal from."));
822 }
823 
frontend_robber_done(void)824 void frontend_robber_done(void)
825 {
826 	robber_busy = FALSE;
827 	if (previous_state != dummy_state) {
828 		set_gui_state(previous_state);
829 		previous_state = dummy_state;
830 	}
831 	gui_prompt_hide();
832 }
833 
check_move_robber_or_pirate(MapElement element,G_GNUC_UNUSED int owner,G_GNUC_UNUSED MapElement extra)834 static gboolean check_move_robber_or_pirate(MapElement element,
835 					    G_GNUC_UNUSED int owner,
836 					    G_GNUC_UNUSED MapElement extra)
837 {
838 	return can_robber_or_pirate_be_moved(element.hex);
839 }
840 
frontend_robber(void)841 void frontend_robber(void)
842 {
843 	if (!robber_busy) {
844 		/* Do this only once.  */
845 		robber_busy = TRUE;
846 		g_assert(previous_state == dummy_state);
847 		previous_state = get_gui_state();
848 	}
849 	/* These things are redone at undo.  */
850 	set_gui_state(frontend_state_robber);
851 	gui_cursor_set(ROBBER_CURSOR, check_move_robber_or_pirate,
852 		       place_robber_or_pirate_cb, NULL, NULL);
853 	gui_prompt_show(_("Place the robber."));
854 	frontend_gui_update();
855 }
856 
check_road_setup(MapElement element,G_GNUC_UNUSED gint owner,G_GNUC_UNUSED MapElement extra)857 static gboolean check_road_setup(MapElement element,
858 				 G_GNUC_UNUSED gint owner,
859 				 G_GNUC_UNUSED MapElement extra)
860 {
861 	return setup_check_road(element.edge);
862 }
863 
check_ship_setup(MapElement element,G_GNUC_UNUSED gint owner,G_GNUC_UNUSED MapElement extra)864 static gboolean check_ship_setup(MapElement element,
865 				 G_GNUC_UNUSED gint owner,
866 				 G_GNUC_UNUSED MapElement extra)
867 {
868 	return setup_check_ship(element.edge);
869 }
870 
check_bridge_setup(MapElement element,G_GNUC_UNUSED gint owner,G_GNUC_UNUSED MapElement extra)871 static gboolean check_bridge_setup(MapElement element,
872 				   G_GNUC_UNUSED gint owner,
873 				   G_GNUC_UNUSED MapElement extra)
874 {
875 	return setup_check_bridge(element.edge);
876 }
877 
check_settlement_setup(MapElement element,G_GNUC_UNUSED gint owner,G_GNUC_UNUSED MapElement extra)878 static gboolean check_settlement_setup(MapElement element,
879 				       G_GNUC_UNUSED gint owner,
880 				       G_GNUC_UNUSED MapElement extra)
881 {
882 	return setup_check_settlement(element.node);
883 }
884 
frontend_mode_setup(GuiEvent event)885 static void frontend_mode_setup(GuiEvent event)
886 {
887 	switch (event) {
888 	case GUI_UPDATE:
889 		frontend_gui_check(GUI_UNDO, can_undo());
890 		frontend_gui_check(GUI_ROAD, setup_can_build_road());
891 		frontend_gui_check(GUI_BRIDGE, setup_can_build_bridge());
892 		frontend_gui_check(GUI_SHIP, setup_can_build_ship());
893 		frontend_gui_check(GUI_SETTLEMENT,
894 				   setup_can_build_settlement());
895 		frontend_gui_check(GUI_FINISH, setup_can_finish());
896 		guimap_single_click_set_functions(check_road_setup,
897 						  build_road_cb,
898 						  check_ship_setup,
899 						  build_ship_cb,
900 						  check_bridge_setup,
901 						  build_bridge_cb,
902 						  check_settlement_setup,
903 						  build_settlement_cb,
904 						  NULL, NULL, NULL,
905 						  NULL, NULL, NULL, NULL);
906 		break;
907 	case GUI_UNDO:
908 		/* The user has pressed the "Undo" button.  Send a
909 		 * command to the server to attempt the undo.  The
910 		 * server will respond telling us whether the undo was
911 		 * successful or not.
912 		 */
913 		cb_undo();
914 		return;
915 	case GUI_ROAD:
916 		gui_cursor_set(ROAD_CURSOR,
917 			       check_road_setup, build_road_cb, NULL,
918 			       NULL);
919 		return;
920 	case GUI_SHIP:
921 		gui_cursor_set(SHIP_CURSOR,
922 			       check_ship_setup, build_ship_cb, NULL,
923 			       NULL);
924 		return;
925 	case GUI_BRIDGE:
926 		gui_cursor_set(BRIDGE_CURSOR,
927 			       check_bridge_setup, build_bridge_cb, NULL,
928 			       NULL);
929 		return;
930 	case GUI_SETTLEMENT:
931 		gui_cursor_set(SETTLEMENT_CURSOR,
932 			       check_settlement_setup,
933 			       build_settlement_cb, NULL, NULL);
934 		return;
935 	case GUI_FINISH:
936 		cb_end_turn();
937 		gui_cursor_none();	/* Finish single click build */
938 		set_gui_state(frontend_state_idle);
939 		notification_close();
940 		return;
941 	default:
942 		break;
943 	}
944 }
945 
frontend_setup(G_GNUC_UNUSED gint num_settlements,G_GNUC_UNUSED gint num_roads)946 void frontend_setup(G_GNUC_UNUSED gint num_settlements,
947 		    G_GNUC_UNUSED gint num_roads)
948 {
949 	if (get_gui_state() == frontend_mode_setup) {
950 		frontend_gui_update();
951 		return;
952 	}
953 	set_gui_state(frontend_mode_setup);
954 	play_sound(SOUND_TURN);
955 	/* Notification */
956 	notification_send(_("It is your turn to setup."), ICON_SETTLEMENT);
957 }
958