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