1 /***********************************************************************
2 Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
13
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17
18 #include <gtk/gtk.h>
19
20 /* utility */
21 #include "astring.h"
22 #include "support.h"
23
24 /* common */
25 #include "actions.h"
26 #include "game.h"
27 #include "traderoutes.h"
28 #include "movement.h"
29 #include "research.h"
30 #include "unit.h"
31 #include "unitlist.h"
32
33 /* client */
34 #include "dialogs_g.h"
35 #include "chatline.h"
36 #include "choice_dialog.h"
37 #include "client_main.h"
38 #include "climisc.h"
39 #include "connectdlg_common.h"
40 #include "control.h"
41 #include "gui_main.h"
42 #include "gui_stuff.h"
43 #include "mapview.h"
44 #include "packhand.h"
45 #include "text.h"
46
47 /* client/gui-gtk-3.22 */
48 #include "citydlg.h"
49 #include "dialogs.h"
50 #include "unitselunitdlg.h"
51 #include "wldlg.h"
52
53 /* Locations for non action enabler controlled buttons. */
54 #define BUTTON_MOVE ACTION_COUNT
55 #define BUTTON_NEW_UNIT_TGT BUTTON_MOVE + 1
56 #define BUTTON_LOCATION BUTTON_MOVE + 2
57 #define BUTTON_WAIT BUTTON_MOVE + 3
58 #define BUTTON_CANCEL BUTTON_MOVE + 4
59 #define BUTTON_COUNT BUTTON_MOVE + 5
60
61 #define BUTTON_NOT_THERE -1
62
63
64 static GtkWidget *act_sel_dialog;
65 static int action_button_map[BUTTON_COUNT];
66
67 static int actor_unit_id;
68 static int target_ids[ATK_COUNT];
69 static bool is_more_user_input_needed = FALSE;
70 static bool did_not_decide = FALSE;
71 static bool action_selection_restart = FALSE;
72
73 static GtkWidget *spy_tech_shell;
74
75 static GtkWidget *spy_sabotage_shell;
76
77 /* A structure to hold parameters for actions inside the GUI in stead of
78 * storing the needed data in a global variable. */
79 struct action_data {
80 int actor_unit_id;
81 int target_city_id;
82 int target_unit_id;
83 int target_tile_id;
84 int value;
85 };
86
87 /****************************************************************
88 Create a new action data structure that can be stored in the
89 dialogs.
90 *****************************************************************/
act_data(int actor_id,int target_city_id,int target_unit_id,int target_tile_id,int value)91 static struct action_data *act_data(int actor_id,
92 int target_city_id,
93 int target_unit_id,
94 int target_tile_id,
95 int value)
96 {
97 struct action_data *data = fc_malloc(sizeof(*data));
98
99 data->actor_unit_id = actor_id;
100 data->target_city_id = target_city_id;
101 data->target_unit_id = target_unit_id;
102 data->target_tile_id = target_tile_id;
103 data->value = value;
104
105 return data;
106 }
107
108 /**************************************************************************
109 Move the queue of units that need user input forward unless the current
110 unit is going to need more input.
111 **************************************************************************/
diplomat_queue_handle_primary(void)112 static void diplomat_queue_handle_primary(void)
113 {
114 if (!is_more_user_input_needed) {
115 /* The client isn't waiting for information for any unanswered follow
116 * up questions. */
117
118 struct unit *actor_unit;
119
120 if ((actor_unit = game_unit_by_number(actor_unit_id))) {
121 /* The action selection dialog wasn't closed because the actor unit
122 * was lost. */
123
124 /* The probabilities didn't just disappear, right? */
125 fc_assert_action(actor_unit->client.act_prob_cache,
126 client_unit_init_act_prob_cache(actor_unit));
127
128 FC_FREE(actor_unit->client.act_prob_cache);
129 }
130
131 if (action_selection_restart) {
132 /* The action selection dialog was closed but only so it can be
133 * redrawn with fresh data. */
134
135 action_selection_restart = FALSE;
136 } else {
137 /* The action selection process is over, at least for now. */
138 action_selection_no_longer_in_progress(actor_unit_id);
139 }
140
141 if (did_not_decide) {
142 /* The action selection dialog was closed but the player didn't
143 * decide what the unit should do. */
144
145 /* Reset so the next action selection dialog does the right thing. */
146 did_not_decide = FALSE;
147 } else {
148 /* An action, or no action at all, was selected. */
149 action_decision_clear_want(actor_unit_id);
150 action_selection_next_in_focus(actor_unit_id);
151 }
152 }
153 }
154
155 /**************************************************************************
156 Move the queue of units that need user input forward since the
157 current unit doesn't require the extra input any more.
158 **************************************************************************/
diplomat_queue_handle_secondary(void)159 static void diplomat_queue_handle_secondary(void)
160 {
161 /* Stop waiting. Move on to the next queued unit. */
162 is_more_user_input_needed = FALSE;
163 diplomat_queue_handle_primary();
164 }
165
166 /**************************************************************************
167 Let the non shared client code know that the action selection process
168 no longer is in progress for the specified unit.
169
170 This allows the client to clean up any client specific assumptions.
171 **************************************************************************/
action_selection_no_longer_in_progress_gui_specific(int actor_id)172 void action_selection_no_longer_in_progress_gui_specific(int actor_id)
173 {
174 /* Stop assuming the answer to a follow up question will arrive. */
175 is_more_user_input_needed = FALSE;
176 }
177
178 /****************************************************************
179 User selected enter market place from caravan dialog
180 *****************************************************************/
caravan_marketplace_callback(GtkWidget * w,gpointer data)181 static void caravan_marketplace_callback(GtkWidget *w, gpointer data)
182 {
183 struct action_data *args = (struct action_data *)data;
184
185 if (NULL != game_unit_by_number(args->actor_unit_id)
186 && NULL != game_city_by_number(args->target_city_id)) {
187 request_do_action(ACTION_MARKETPLACE, args->actor_unit_id,
188 args->target_city_id, 0);
189 }
190
191 gtk_widget_destroy(act_sel_dialog);
192 free(args);
193 }
194
195 /****************************************************************
196 User selected traderoute from caravan dialog
197 *****************************************************************/
caravan_establish_trade_callback(GtkWidget * w,gpointer data)198 static void caravan_establish_trade_callback(GtkWidget *w, gpointer data)
199 {
200 struct action_data *args = (struct action_data *)data;
201
202 if (NULL != game_unit_by_number(args->actor_unit_id)
203 && NULL != game_city_by_number(args->target_city_id)) {
204 request_do_action(ACTION_TRADE_ROUTE, args->actor_unit_id,
205 args->target_city_id, 0);
206 }
207
208 gtk_widget_destroy(act_sel_dialog);
209 free(args);
210 }
211
212 /****************************************************************
213 User selected wonder building helping from caravan dialog
214 *****************************************************************/
caravan_help_build_wonder_callback(GtkWidget * w,gpointer data)215 static void caravan_help_build_wonder_callback(GtkWidget *w, gpointer data)
216 {
217 struct action_data *args = (struct action_data *)data;
218
219 if (NULL != game_unit_by_number(args->actor_unit_id)
220 && NULL != game_city_by_number(args->target_city_id)) {
221 request_do_action(ACTION_HELP_WONDER, args->actor_unit_id,
222 args->target_city_id, 0);
223 }
224
225 gtk_widget_destroy(act_sel_dialog);
226 free(args);
227 }
228
229 /**********************************************************************
230 User responded to bribe dialog
231 **********************************************************************/
bribe_response(GtkWidget * w,gint response,gpointer data)232 static void bribe_response(GtkWidget *w, gint response, gpointer data)
233 {
234 struct action_data *args = (struct action_data *)data;
235
236 if (response == GTK_RESPONSE_YES) {
237 request_do_action(ACTION_SPY_BRIBE_UNIT, args->actor_unit_id,
238 args->target_unit_id, 0);
239 }
240
241 gtk_widget_destroy(w);
242 free(args);
243
244 /* The user have answered the follow up question. Move on. */
245 diplomat_queue_handle_secondary();
246 }
247
248 /****************************************************************
249 Ask the server how much the bribe is
250 *****************************************************************/
diplomat_bribe_callback(GtkWidget * w,gpointer data)251 static void diplomat_bribe_callback(GtkWidget *w, gpointer data)
252 {
253 struct action_data *args = (struct action_data *)data;
254
255 if (NULL != game_unit_by_number(args->actor_unit_id)
256 && NULL != game_unit_by_number(args->target_unit_id)) {
257 request_action_details(ACTION_SPY_BRIBE_UNIT, args->actor_unit_id,
258 args->target_unit_id);
259 }
260
261 /* Wait for the server's reply before moving on to the next unit that
262 * needs to know what action to take. */
263 is_more_user_input_needed = TRUE;
264
265 gtk_widget_destroy(act_sel_dialog);
266 free(args);
267 }
268
269 /*************************************************************************
270 Popup unit bribe dialog
271 **************************************************************************/
popup_bribe_dialog(struct unit * actor,struct unit * punit,int cost)272 void popup_bribe_dialog(struct unit *actor, struct unit *punit, int cost)
273 {
274 GtkWidget *shell;
275 char buf[1024];
276
277 fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Treasury contains %d gold.",
278 "Treasury contains %d gold.",
279 client_player()->economic.gold),
280 client_player()->economic.gold);
281
282 if (cost <= client_player()->economic.gold) {
283 shell = gtk_message_dialog_new(NULL, 0,
284 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
285 /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */
286 PL_("Bribe unit for %d gold?\n%s",
287 "Bribe unit for %d gold?\n%s", cost), cost, buf);
288 gtk_window_set_title(GTK_WINDOW(shell), _("Bribe Enemy Unit"));
289 setup_dialog(shell, toplevel);
290 } else {
291 shell = gtk_message_dialog_new(NULL, 0,
292 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
293 /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */
294 PL_("Bribing the unit costs %d gold.\n%s",
295 "Bribing the unit costs %d gold.\n%s", cost), cost, buf);
296 gtk_window_set_title(GTK_WINDOW(shell), _("Traitors Demand Too Much!"));
297 setup_dialog(shell, toplevel);
298 }
299 gtk_window_present(GTK_WINDOW(shell));
300
301 g_signal_connect(shell, "response", G_CALLBACK(bribe_response),
302 act_data(actor->id, 0, punit->id, 0, cost));
303 }
304
305 /****************************************************************
306 User selected sabotaging from choice dialog
307 *****************************************************************/
diplomat_sabotage_callback(GtkWidget * w,gpointer data)308 static void diplomat_sabotage_callback(GtkWidget *w, gpointer data)
309 {
310 struct action_data *args = (struct action_data *)data;
311
312 if (NULL != game_unit_by_number(args->actor_unit_id)
313 && NULL != game_city_by_number(args->target_city_id)) {
314 request_do_action(ACTION_SPY_SABOTAGE_CITY, args->actor_unit_id,
315 args->target_city_id, B_LAST + 1);
316 }
317
318 gtk_widget_destroy(act_sel_dialog);
319 free(args);
320 }
321
322 /****************************************************************
323 User selected investigating from choice dialog
324 *****************************************************************/
diplomat_investigate_callback(GtkWidget * w,gpointer data)325 static void diplomat_investigate_callback(GtkWidget *w, gpointer data)
326 {
327 struct action_data *args = (struct action_data *)data;
328
329 if (NULL != game_city_by_number(args->target_city_id)
330 && NULL != game_unit_by_number(args->actor_unit_id)) {
331 request_do_action(ACTION_SPY_INVESTIGATE_CITY, args->actor_unit_id,
332 args->target_city_id, 0);
333 }
334
335 gtk_widget_destroy(act_sel_dialog);
336 free(args);
337 }
338
339 /****************************************************************
340 User selected unit sabotaging from choice dialog
341 *****************************************************************/
spy_sabotage_unit_callback(GtkWidget * w,gpointer data)342 static void spy_sabotage_unit_callback(GtkWidget *w, gpointer data)
343 {
344 struct action_data *args = (struct action_data *)data;
345
346 request_do_action(ACTION_SPY_SABOTAGE_UNIT, args->actor_unit_id,
347 args->target_unit_id, 0);
348
349 gtk_widget_destroy(act_sel_dialog);
350 free(args);
351 }
352
353 /****************************************************************
354 User selected embassy establishing from choice dialog
355 *****************************************************************/
diplomat_embassy_callback(GtkWidget * w,gpointer data)356 static void diplomat_embassy_callback(GtkWidget *w, gpointer data)
357 {
358 struct action_data *args = (struct action_data *)data;
359
360 if (NULL != game_unit_by_number(args->actor_unit_id)
361 && NULL != game_city_by_number(args->target_city_id)) {
362 request_do_action(ACTION_ESTABLISH_EMBASSY, args->actor_unit_id,
363 args->target_city_id, 0);
364 }
365
366 gtk_widget_destroy(act_sel_dialog);
367 free(args);
368 }
369
370 /****************************************************************
371 User selected to steal gold from choice dialog
372 *****************************************************************/
spy_steal_gold_callback(GtkWidget * w,gpointer data)373 static void spy_steal_gold_callback(GtkWidget *w, gpointer data)
374 {
375 struct action_data *args = (struct action_data *)data;
376
377 if (NULL != game_unit_by_number(args->actor_unit_id)
378 && NULL != game_city_by_number(args->target_city_id)) {
379 request_do_action(ACTION_SPY_STEAL_GOLD, args->actor_unit_id,
380 args->target_city_id, 0);
381 }
382
383 gtk_widget_destroy(act_sel_dialog);
384 free(args);
385 }
386
387 /****************************************************************
388 User selected poisoning from choice dialog
389 *****************************************************************/
spy_poison_callback(GtkWidget * w,gpointer data)390 static void spy_poison_callback(GtkWidget *w, gpointer data)
391 {
392 struct action_data *args = (struct action_data *)data;
393
394 if (NULL != game_unit_by_number(args->actor_unit_id)
395 && NULL != game_city_by_number(args->target_city_id)) {
396 request_do_action(ACTION_SPY_POISON, args->actor_unit_id,
397 args->target_city_id, 0);
398 }
399
400 gtk_widget_destroy(act_sel_dialog);
401 free(args);
402 }
403
404 /****************************************************************
405 User selected stealing from choice dialog
406 *****************************************************************/
diplomat_steal_callback(GtkWidget * w,gpointer data)407 static void diplomat_steal_callback(GtkWidget *w, gpointer data)
408 {
409 struct action_data *args = (struct action_data *)data;
410
411 if (NULL != game_unit_by_number(args->actor_unit_id)
412 && NULL != game_city_by_number(args->target_city_id)) {
413 request_do_action(ACTION_SPY_STEAL_TECH, args->actor_unit_id,
414 args->target_city_id, A_UNSET);
415 }
416
417 gtk_widget_destroy(act_sel_dialog);
418 free(args);
419 }
420
421 /****************************************************************
422 User responded to steal advances dialog
423 *****************************************************************/
spy_advances_response(GtkWidget * w,gint response,gpointer data)424 static void spy_advances_response(GtkWidget *w, gint response,
425 gpointer data)
426 {
427 struct action_data *args = (struct action_data *)data;
428
429 if (response == GTK_RESPONSE_ACCEPT && args->value > 0) {
430 if (NULL != game_unit_by_number(args->actor_unit_id)
431 && NULL != game_city_by_number(args->target_city_id)) {
432 if (args->value == A_UNSET) {
433 /* This is the untargeted version. */
434 request_do_action(ACTION_SPY_STEAL_TECH,
435 args->actor_unit_id, args->target_city_id,
436 args->value);
437 } else {
438 /* This is the targeted version. */
439 request_do_action(ACTION_SPY_TARGETED_STEAL_TECH,
440 args->actor_unit_id, args->target_city_id,
441 args->value);
442 }
443 }
444 }
445
446 gtk_widget_destroy(spy_tech_shell);
447 spy_tech_shell = NULL;
448 free(data);
449
450 /* The user have answered the follow up question. Move on. */
451 diplomat_queue_handle_secondary();
452 }
453
454 /****************************************************************
455 User selected entry in steal advances dialog
456 *****************************************************************/
spy_advances_callback(GtkTreeSelection * select,gpointer data)457 static void spy_advances_callback(GtkTreeSelection *select,
458 gpointer data)
459 {
460 struct action_data *args = (struct action_data *)data;
461
462 GtkTreeModel *model;
463 GtkTreeIter it;
464
465 if (gtk_tree_selection_get_selected(select, &model, &it)) {
466 gtk_tree_model_get(model, &it, 1, &(args->value), -1);
467
468 gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_tech_shell),
469 GTK_RESPONSE_ACCEPT, TRUE);
470 } else {
471 args->value = 0;
472
473 gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_tech_shell),
474 GTK_RESPONSE_ACCEPT, FALSE);
475 }
476 }
477
478 /****************************************************************
479 Create spy's tech stealing dialog
480 *****************************************************************/
create_advances_list(struct player * pplayer,struct player * pvictim,struct action_data * args)481 static void create_advances_list(struct player *pplayer,
482 struct player *pvictim,
483 struct action_data *args)
484 {
485 GtkWidget *sw, *label, *vbox, *view;
486 GtkListStore *store;
487 GtkCellRenderer *rend;
488 GtkTreeViewColumn *col;
489
490 struct unit *actor_unit = game_unit_by_number(args->actor_unit_id);
491
492 spy_tech_shell = gtk_dialog_new_with_buttons(_("Steal Technology"),
493 NULL, 0,
494 _("_Cancel"), GTK_RESPONSE_CANCEL,
495 _("_Steal"), GTK_RESPONSE_ACCEPT,
496 NULL);
497 setup_dialog(spy_tech_shell, toplevel);
498 gtk_window_set_position(GTK_WINDOW(spy_tech_shell), GTK_WIN_POS_MOUSE);
499
500 gtk_dialog_set_default_response(GTK_DIALOG(spy_tech_shell),
501 GTK_RESPONSE_ACCEPT);
502
503 label = gtk_frame_new(_("Select Advance to Steal"));
504 gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(spy_tech_shell))), label);
505
506 vbox = gtk_grid_new();
507 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox),
508 GTK_ORIENTATION_VERTICAL);
509 gtk_grid_set_row_spacing(GTK_GRID(vbox), 6);
510 gtk_container_add(GTK_CONTAINER(label), vbox);
511
512 store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
513
514 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
515 gtk_widget_set_hexpand(view, TRUE);
516 gtk_widget_set_vexpand(view, TRUE);
517 g_object_unref(store);
518 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
519
520 rend = gtk_cell_renderer_text_new();
521 col = gtk_tree_view_column_new_with_attributes(NULL, rend,
522 "text", 0, NULL);
523 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
524
525 label = g_object_new(GTK_TYPE_LABEL,
526 "use-underline", TRUE,
527 "mnemonic-widget", view,
528 "label", _("_Advances:"),
529 "xalign", 0.0,
530 "yalign", 0.5,
531 NULL);
532 gtk_container_add(GTK_CONTAINER(vbox), label);
533
534 sw = gtk_scrolled_window_new(NULL, NULL);
535 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
536 GTK_SHADOW_ETCHED_IN);
537 gtk_container_add(GTK_CONTAINER(sw), view);
538
539 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
540 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
541 gtk_widget_set_size_request(sw, -1, 200);
542
543 gtk_container_add(GTK_CONTAINER(vbox), sw);
544
545 /* Now populate the list */
546 if (pvictim) { /* you don't want to know what lag can do -- Syela */
547 const struct research *presearch = research_get(pplayer);
548 const struct research *vresearch = research_get(pvictim);
549 GtkTreeIter it;
550 GValue value = { 0, };
551
552 advance_index_iterate(A_FIRST, i) {
553 if (research_invention_gettable(presearch, i,
554 game.info.tech_steal_allow_holes)
555 && research_invention_state(vresearch, i) == TECH_KNOWN
556 && research_invention_state(presearch, i) != TECH_KNOWN) {
557 gtk_list_store_append(store, &it);
558
559 g_value_init(&value, G_TYPE_STRING);
560 g_value_set_static_string(&value, research_advance_name_translation
561 (presearch, i));
562 gtk_list_store_set_value(store, &it, 0, &value);
563 g_value_unset(&value);
564 gtk_list_store_set(store, &it, 1, i, -1);
565 }
566 } advance_index_iterate_end;
567
568 if (action_prob_possible(
569 actor_unit->client.act_prob_cache[ACTION_SPY_STEAL_TECH])) {
570 gtk_list_store_append(store, &it);
571
572 g_value_init(&value, G_TYPE_STRING);
573 {
574 struct astring str = ASTRING_INIT;
575 /* TRANS: %s is a unit name, e.g., Spy */
576 astr_set(&str, _("At %s's Discretion"),
577 unit_name_translation(actor_unit));
578 g_value_set_string(&value, astr_str(&str));
579 astr_free(&str);
580 }
581 gtk_list_store_set_value(store, &it, 0, &value);
582 g_value_unset(&value);
583 gtk_list_store_set(store, &it, 1, A_UNSET, -1);
584 }
585 }
586
587 gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_tech_shell),
588 GTK_RESPONSE_ACCEPT, FALSE);
589
590 gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(spy_tech_shell)));
591
592 g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), "changed",
593 G_CALLBACK(spy_advances_callback), args);
594 g_signal_connect(spy_tech_shell, "response",
595 G_CALLBACK(spy_advances_response), args);
596
597 args->value = 0;
598
599 gtk_tree_view_focus(GTK_TREE_VIEW(view));
600 }
601
602 /****************************************************************
603 User has responded to spy's sabotage building dialog
604 *****************************************************************/
spy_improvements_response(GtkWidget * w,gint response,gpointer data)605 static void spy_improvements_response(GtkWidget *w, gint response, gpointer data)
606 {
607 struct action_data *args = (struct action_data *)data;
608
609 if (response == GTK_RESPONSE_ACCEPT && args->value > -2) {
610 if (NULL != game_unit_by_number(args->actor_unit_id)
611 && NULL != game_city_by_number(args->target_city_id)) {
612 if (args->value == B_LAST) {
613 /* This is the untargeted version. */
614 request_do_action(ACTION_SPY_SABOTAGE_CITY,
615 args->actor_unit_id,
616 args->target_city_id,
617 args->value + 1);
618 } else {
619 /* This is the targeted version. */
620 request_do_action(ACTION_SPY_TARGETED_SABOTAGE_CITY,
621 args->actor_unit_id,
622 args->target_city_id,
623 args->value + 1);
624 }
625 }
626 }
627
628 gtk_widget_destroy(spy_sabotage_shell);
629 spy_sabotage_shell = NULL;
630 free(args);
631
632 /* The user have answered the follow up question. Move on. */
633 diplomat_queue_handle_secondary();
634 }
635
636 /****************************************************************
637 User has selected new building from spy's sabotage dialog
638 *****************************************************************/
spy_improvements_callback(GtkTreeSelection * select,gpointer data)639 static void spy_improvements_callback(GtkTreeSelection *select, gpointer data)
640 {
641 struct action_data *args = (struct action_data *)data;
642
643 GtkTreeModel *model;
644 GtkTreeIter it;
645
646 if (gtk_tree_selection_get_selected(select, &model, &it)) {
647 gtk_tree_model_get(model, &it, 1, &(args->value), -1);
648
649 gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_sabotage_shell),
650 GTK_RESPONSE_ACCEPT, TRUE);
651 } else {
652 args->value = -2;
653
654 gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_sabotage_shell),
655 GTK_RESPONSE_ACCEPT, FALSE);
656 }
657 }
658
659 /****************************************************************
660 Creates spy's building sabotaging dialog
661 *****************************************************************/
create_improvements_list(struct player * pplayer,struct city * pcity,struct action_data * args)662 static void create_improvements_list(struct player *pplayer,
663 struct city *pcity,
664 struct action_data *args)
665 {
666 GtkWidget *sw, *label, *vbox, *view;
667 GtkListStore *store;
668 GtkCellRenderer *rend;
669 GtkTreeViewColumn *col;
670 GtkTreeIter it;
671
672 struct unit *actor_unit = game_unit_by_number(args->actor_unit_id);
673
674 spy_sabotage_shell = gtk_dialog_new_with_buttons(_("Sabotage Improvements"),
675 NULL, 0,
676 _("_Cancel"), GTK_RESPONSE_CANCEL,
677 _("_Sabotage"), GTK_RESPONSE_ACCEPT,
678 NULL);
679 setup_dialog(spy_sabotage_shell, toplevel);
680 gtk_window_set_position(GTK_WINDOW(spy_sabotage_shell), GTK_WIN_POS_MOUSE);
681
682 gtk_dialog_set_default_response(GTK_DIALOG(spy_sabotage_shell),
683 GTK_RESPONSE_ACCEPT);
684
685 label = gtk_frame_new(_("Select Improvement to Sabotage"));
686 gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(spy_sabotage_shell))), label);
687
688 vbox = gtk_grid_new();
689 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox),
690 GTK_ORIENTATION_VERTICAL);
691 gtk_grid_set_row_spacing(GTK_GRID(vbox), 6);
692 gtk_container_add(GTK_CONTAINER(label), vbox);
693
694 store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
695
696 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
697 gtk_widget_set_hexpand(view, TRUE);
698 gtk_widget_set_vexpand(view, TRUE);
699 g_object_unref(store);
700 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
701
702 rend = gtk_cell_renderer_text_new();
703 col = gtk_tree_view_column_new_with_attributes(NULL, rend,
704 "text", 0, NULL);
705 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
706
707 label = g_object_new(GTK_TYPE_LABEL,
708 "use-underline", TRUE,
709 "mnemonic-widget", view,
710 "label", _("_Improvements:"),
711 "xalign", 0.0,
712 "yalign", 0.5,
713 NULL);
714 gtk_container_add(GTK_CONTAINER(vbox), label);
715
716 sw = gtk_scrolled_window_new(NULL, NULL);
717 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
718 GTK_SHADOW_ETCHED_IN);
719 gtk_container_add(GTK_CONTAINER(sw), view);
720
721 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
722 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
723 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw), 200);
724
725 gtk_container_add(GTK_CONTAINER(vbox), sw);
726
727 /* Now populate the list */
728 gtk_list_store_append(store, &it);
729 gtk_list_store_set(store, &it, 0, _("City Production"), 1, -1, -1);
730
731 city_built_iterate(pcity, pimprove) {
732 if (pimprove->sabotage > 0) {
733 gtk_list_store_append(store, &it);
734 gtk_list_store_set(store, &it,
735 0, city_improvement_name_translation(pcity, pimprove),
736 1, improvement_number(pimprove),
737 -1);
738 }
739 } city_built_iterate_end;
740
741 if (action_prob_possible(
742 actor_unit->client.act_prob_cache[ACTION_SPY_SABOTAGE_CITY])) {
743 struct astring str = ASTRING_INIT;
744
745 gtk_list_store_append(store, &it);
746
747 /* TRANS: %s is a unit name, e.g., Spy */
748 astr_set(&str, _("At %s's Discretion"),
749 unit_name_translation(actor_unit));
750 gtk_list_store_set(store, &it, 0, astr_str(&str), 1, B_LAST, -1);
751
752 astr_free(&str);
753 }
754
755 gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_sabotage_shell),
756 GTK_RESPONSE_ACCEPT, FALSE);
757
758 gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(spy_sabotage_shell)));
759
760 g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), "changed",
761 G_CALLBACK(spy_improvements_callback), args);
762 g_signal_connect(spy_sabotage_shell, "response",
763 G_CALLBACK(spy_improvements_response), args);
764
765 args->value = -2;
766
767 gtk_tree_view_focus(GTK_TREE_VIEW(view));
768 }
769
770 /****************************************************************
771 Popup tech stealing dialog with list of possible techs
772 *****************************************************************/
spy_steal_popup(GtkWidget * w,gpointer data)773 static void spy_steal_popup(GtkWidget *w, gpointer data)
774 {
775 struct action_data *args = (struct action_data *)data;
776
777 struct city *pvcity = game_city_by_number(args->target_city_id);
778 struct player *pvictim = NULL;
779
780 if (pvcity) {
781 pvictim = city_owner(pvcity);
782 }
783
784 /* it is concievable that pvcity will not be found, because something
785 has happened to the city during latency. Therefore we must initialize
786 pvictim to NULL and account for !pvictim in create_advances_list. -- Syela */
787
788 /* FIXME: Don't discard the second tech choice dialog. */
789 if (!spy_tech_shell) {
790 create_advances_list(client.conn.playing, pvictim, args);
791 gtk_window_present(GTK_WINDOW(spy_tech_shell));
792 } else {
793 free(args);
794 }
795
796 /* Wait for the server's reply before moving on to the next unit that
797 * needs to know what action to take. */
798 is_more_user_input_needed = TRUE;
799
800 gtk_widget_destroy(act_sel_dialog);
801 }
802
803 /****************************************************************
804 Requests up-to-date list of improvements, the return of
805 which will trigger the popup_sabotage_dialog() function.
806 *****************************************************************/
spy_request_sabotage_list(GtkWidget * w,gpointer data)807 static void spy_request_sabotage_list(GtkWidget *w, gpointer data)
808 {
809 struct action_data *args = (struct action_data *)data;
810
811 if (NULL != game_unit_by_number(args->actor_unit_id)
812 && NULL != game_city_by_number(args->target_city_id)) {
813 request_action_details(ACTION_SPY_TARGETED_SABOTAGE_CITY,
814 args->actor_unit_id,
815 args->target_city_id);
816 }
817
818 /* Wait for the server's reply before moving on to the next unit that
819 * needs to know what action to take. */
820 is_more_user_input_needed = TRUE;
821
822 gtk_widget_destroy(act_sel_dialog);
823 free(args);
824 }
825
826 /*************************************************************************
827 Pops-up the Spy sabotage dialog, upon return of list of
828 available improvements requested by the above function.
829 **************************************************************************/
popup_sabotage_dialog(struct unit * actor,struct city * pcity)830 void popup_sabotage_dialog(struct unit *actor, struct city *pcity)
831 {
832 /* FIXME: Don't discard the second target choice dialog. */
833 if (!spy_sabotage_shell) {
834 create_improvements_list(client.conn.playing, pcity,
835 act_data(actor->id, pcity->id, 0, 0, 0));
836 gtk_window_present(GTK_WINDOW(spy_sabotage_shell));
837 }
838 }
839
840 /****************************************************************
841 ... Ask the server how much the revolt is going to cost us
842 *****************************************************************/
diplomat_incite_callback(GtkWidget * w,gpointer data)843 static void diplomat_incite_callback(GtkWidget *w, gpointer data)
844 {
845 struct action_data *args = (struct action_data *)data;
846
847 if (NULL != game_unit_by_number(args->actor_unit_id)
848 && NULL != game_city_by_number(args->target_city_id)) {
849 request_action_details(ACTION_SPY_INCITE_CITY, args->actor_unit_id,
850 args->target_city_id);
851 }
852
853 /* Wait for the server's reply before moving on to the next unit that
854 * needs to know what action to take. */
855 is_more_user_input_needed = TRUE;
856
857 gtk_widget_destroy(act_sel_dialog);
858 free(args);
859 }
860
861 /************************************************************************
862 User has responded to incite dialog
863 ************************************************************************/
incite_response(GtkWidget * w,gint response,gpointer data)864 static void incite_response(GtkWidget *w, gint response, gpointer data)
865 {
866 struct action_data *args = (struct action_data *)data;
867
868 if (response == GTK_RESPONSE_YES) {
869 request_do_action(ACTION_SPY_INCITE_CITY, args->actor_unit_id,
870 args->target_city_id, 0);
871 }
872
873 gtk_widget_destroy(w);
874 free(args);
875
876 /* The user have answered the follow up question. Move on. */
877 diplomat_queue_handle_secondary();
878 }
879
880 /*************************************************************************
881 Popup the yes/no dialog for inciting, since we know the cost now
882 **************************************************************************/
popup_incite_dialog(struct unit * actor,struct city * pcity,int cost)883 void popup_incite_dialog(struct unit *actor, struct city *pcity, int cost)
884 {
885 GtkWidget *shell;
886 char buf[1024];
887
888 fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Treasury contains %d gold.",
889 "Treasury contains %d gold.",
890 client_player()->economic.gold),
891 client_player()->economic.gold);
892
893 if (INCITE_IMPOSSIBLE_COST == cost) {
894 shell = gtk_message_dialog_new(NULL, 0,
895 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
896 _("You can't incite a revolt in %s."),
897 city_name_get(pcity));
898 gtk_window_set_title(GTK_WINDOW(shell), _("City can't be incited!"));
899 setup_dialog(shell, toplevel);
900 } else if (cost <= client_player()->economic.gold) {
901 shell = gtk_message_dialog_new(NULL, 0,
902 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
903 /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */
904 PL_("Incite a revolt for %d gold?\n%s",
905 "Incite a revolt for %d gold?\n%s", cost), cost, buf);
906 gtk_window_set_title(GTK_WINDOW(shell), _("Incite a Revolt!"));
907 setup_dialog(shell, toplevel);
908 } else {
909 shell = gtk_message_dialog_new(NULL,
910 0,
911 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
912 /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */
913 PL_("Inciting a revolt costs %d gold.\n%s",
914 "Inciting a revolt costs %d gold.\n%s", cost), cost, buf);
915 gtk_window_set_title(GTK_WINDOW(shell), _("Traitors Demand Too Much!"));
916 setup_dialog(shell, toplevel);
917 }
918 gtk_window_present(GTK_WINDOW(shell));
919
920 g_signal_connect(shell, "response", G_CALLBACK(incite_response),
921 act_data(actor->id, pcity->id, 0, 0, cost));
922 }
923
924 /**************************************************************************
925 Callback from the unit target selection dialog.
926 **************************************************************************/
tgt_unit_change_callback(GtkWidget * dlg,gint arg)927 static void tgt_unit_change_callback(GtkWidget *dlg, gint arg)
928 {
929 int act_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dlg), "actor"));
930
931 if (arg == GTK_RESPONSE_YES) {
932 struct unit *actor = game_unit_by_number(act_id);
933
934 if (actor != NULL) {
935 int tgt_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dlg),
936 "target"));
937 struct unit *tgt_unit = game_unit_by_number(tgt_id);
938 struct tile *tgt_tile = g_object_get_data(G_OBJECT(dlg), "tile");
939
940 if (tgt_unit == NULL) {
941 /* Make the action dialog pop up again. */
942 dsend_packet_unit_get_actions(&client.conn,
943 actor->id,
944 /* Let the server choose the target
945 * unit. */
946 IDENTITY_NUMBER_ZERO,
947 /* Let the server choose the target
948 * city. */
949 IDENTITY_NUMBER_ZERO,
950 tgt_tile->index,
951 TRUE);
952 } else {
953 dsend_packet_unit_get_actions(&client.conn,
954 actor->id,
955 tgt_id,
956 /* Let the server choose the target
957 * city. */
958 IDENTITY_NUMBER_ZERO,
959 tgt_tile->index,
960 TRUE);
961 }
962 }
963 } else {
964 /* Dialog canceled. This ends the action selection process. */
965 action_selection_no_longer_in_progress(act_id);
966 }
967
968 gtk_widget_destroy(dlg);
969 }
970
971 /**************************************************************************
972 Callback from action selection dialog for "Change unit target".
973 **************************************************************************/
act_sel_new_unit_tgt_callback(GtkWidget * w,gpointer data)974 static void act_sel_new_unit_tgt_callback(GtkWidget *w, gpointer data)
975 {
976 struct action_data *args = (struct action_data *)data;
977
978 struct unit *punit;
979 struct unit *tunit;
980 struct tile *ptile;
981
982 if ((punit = game_unit_by_number(args->actor_unit_id))
983 && (ptile = index_to_tile(args->target_tile_id))
984 && (tunit = game_unit_by_number(args->target_unit_id))) {
985 select_tgt_unit(punit, ptile, ptile->units, tunit,
986 _("Target unit selection"),
987 _("Looking for target unit:"),
988 _("Units at tile:"),
989 _("Select"),
990 G_CALLBACK(tgt_unit_change_callback));
991 }
992
993 did_not_decide = TRUE;
994 action_selection_restart = TRUE;
995 gtk_widget_destroy(act_sel_dialog);
996 free(args);
997 }
998
999 /**************************************************************************
1000 Callback from action selection dialog for "Show Location".
1001 **************************************************************************/
act_sel_location_callback(GtkWidget * w,gpointer data)1002 static void act_sel_location_callback(GtkWidget *w, gpointer data)
1003 {
1004 struct action_data *args = (struct action_data *)data;
1005
1006 struct unit *punit;
1007
1008 if ((punit = game_unit_by_number(args->actor_unit_id))) {
1009 center_tile_mapcanvas(unit_tile(punit));
1010 }
1011 }
1012
1013 /**************************************************************************
1014 Callback from action selection dialog for "keep moving".
1015 (This should only occur when entering a tile that has an allied city or
1016 an allied unit.)
1017 **************************************************************************/
act_sel_keep_moving_callback(GtkWidget * w,gpointer data)1018 static void act_sel_keep_moving_callback(GtkWidget *w, gpointer data)
1019 {
1020 struct action_data *args = (struct action_data *)data;
1021
1022 struct unit *punit;
1023 struct tile *ptile;
1024
1025 if ((punit = game_unit_by_number(args->actor_unit_id))
1026 && (ptile = index_to_tile(args->target_tile_id))
1027 && !same_pos(unit_tile(punit), ptile)) {
1028 request_unit_non_action_move(punit, ptile);
1029 }
1030
1031 gtk_widget_destroy(act_sel_dialog);
1032 free(args);
1033 }
1034
1035 /**************************************************************************
1036 Delay selection of what action to take.
1037 **************************************************************************/
act_sel_wait_callback(GtkWidget * w,gpointer data)1038 static void act_sel_wait_callback(GtkWidget *w, gpointer data)
1039 {
1040 struct action_data *args = (struct action_data *)data;
1041
1042 key_unit_wait();
1043
1044 /* the dialog was destroyed when key_unit_wait() resulted in
1045 * action_selection_close() being called. */
1046
1047 free(args);
1048 }
1049
1050 /****************************************************************
1051 Action selection dialog has been destroyed
1052 *****************************************************************/
act_sel_destroy_callback(GtkWidget * w,gpointer data)1053 static void act_sel_destroy_callback(GtkWidget *w, gpointer data)
1054 {
1055 act_sel_dialog = NULL;
1056 diplomat_queue_handle_primary();
1057 }
1058
1059 /****************************************************************
1060 Action selection dialog has been canceled
1061 *****************************************************************/
act_sel_cancel_callback(GtkWidget * w,gpointer data)1062 static void act_sel_cancel_callback(GtkWidget *w, gpointer data)
1063 {
1064 gtk_widget_destroy(act_sel_dialog);
1065 free(data);
1066 }
1067
1068 /****************************************************************
1069 Action selection dialog has been closed
1070 *****************************************************************/
act_sel_close_callback(GtkWidget * w,gint response_id,gpointer data)1071 static void act_sel_close_callback(GtkWidget *w,
1072 gint response_id,
1073 gpointer data)
1074 {
1075 gtk_widget_destroy(act_sel_dialog);
1076 free(data);
1077 }
1078
1079 /* Mapping from an action to the function to call when its button is
1080 * pushed. */
1081 static const GCallback af_map[ACTION_COUNT] = {
1082 /* Unit acting against a city target. */
1083 [ACTION_ESTABLISH_EMBASSY] = (GCallback)diplomat_embassy_callback,
1084 [ACTION_SPY_INVESTIGATE_CITY] = (GCallback)diplomat_investigate_callback,
1085 [ACTION_SPY_POISON] = (GCallback)spy_poison_callback,
1086 [ACTION_SPY_STEAL_GOLD] = (GCallback)spy_steal_gold_callback,
1087 [ACTION_SPY_SABOTAGE_CITY] = (GCallback)diplomat_sabotage_callback,
1088 [ACTION_SPY_TARGETED_SABOTAGE_CITY] =
1089 (GCallback)spy_request_sabotage_list,
1090 [ACTION_SPY_STEAL_TECH] = (GCallback)diplomat_steal_callback,
1091 [ACTION_SPY_TARGETED_STEAL_TECH] = (GCallback)spy_steal_popup,
1092 [ACTION_SPY_INCITE_CITY] = (GCallback)diplomat_incite_callback,
1093 [ACTION_TRADE_ROUTE] = (GCallback)caravan_establish_trade_callback,
1094 [ACTION_MARKETPLACE] = (GCallback)caravan_marketplace_callback,
1095 [ACTION_HELP_WONDER] = (GCallback)caravan_help_build_wonder_callback,
1096
1097 /* Unit acting against a unit target. */
1098 [ACTION_SPY_BRIBE_UNIT] = (GCallback)diplomat_bribe_callback,
1099 [ACTION_SPY_SABOTAGE_UNIT] = (GCallback)spy_sabotage_unit_callback
1100 };
1101
1102 /******************************************************************
1103 Show the user the action if it is enabled.
1104 *******************************************************************/
action_entry(GtkWidget * shl,int act_id,const struct act_prob * act_probs,const char * custom,struct action_data * handler_args)1105 static void action_entry(GtkWidget *shl,
1106 int act_id,
1107 const struct act_prob *act_probs,
1108 const char *custom,
1109 struct action_data *handler_args)
1110 {
1111 const gchar *label;
1112 const gchar *tooltip;
1113
1114 if (act_id == ACTION_SPY_SABOTAGE_CITY
1115 && action_prob_possible(
1116 act_probs[ACTION_SPY_TARGETED_SABOTAGE_CITY])) {
1117 /* The player can select Sabotage City from the target selection dialog
1118 * of Targeted Sabotage City. */
1119 return;
1120 }
1121
1122 if (act_id == ACTION_SPY_STEAL_TECH
1123 && action_prob_possible(
1124 act_probs[ACTION_SPY_TARGETED_STEAL_TECH])) {
1125 /* The player can select Steal Tech from the target selection dialog of
1126 * Targeted Steal Tech. */
1127 return;
1128 }
1129
1130 /* Don't show disabled actions. */
1131 if (!action_prob_possible(act_probs[act_id])) {
1132 return;
1133 }
1134
1135 label = action_prepare_ui_name(act_id, "_",
1136 act_probs[act_id],
1137 custom);
1138
1139 tooltip = action_get_tool_tip(act_id,
1140 act_probs[act_id]);
1141
1142 action_button_map[act_id] = choice_dialog_get_number_of_buttons(shl);
1143 choice_dialog_add(shl, label, af_map[act_id], handler_args,
1144 FALSE, tooltip);
1145 }
1146
1147 /******************************************************************
1148 Update an existing button.
1149 *******************************************************************/
action_entry_update(GtkWidget * shl,int act_id,const struct act_prob * act_probs,const char * custom,struct action_data * handler_args)1150 static void action_entry_update(GtkWidget *shl,
1151 int act_id,
1152 const struct act_prob *act_probs,
1153 const char *custom,
1154 struct action_data *handler_args)
1155 {
1156 const gchar *label;
1157 const gchar *tooltip;
1158
1159 /* An action that just became impossible has its button disabled.
1160 * An action that became possible again must be reenabled. */
1161 choice_dialog_button_set_sensitive(act_sel_dialog,
1162 action_button_map[act_id],
1163 action_prob_possible(act_probs[act_id]));
1164
1165 /* The probability may have changed. */
1166 label = action_prepare_ui_name(act_id, "_",
1167 act_probs[act_id], custom);
1168
1169 tooltip = action_get_tool_tip(act_id,
1170 act_probs[act_id]);
1171
1172 choice_dialog_button_set_label(act_sel_dialog,
1173 action_button_map[act_id],
1174 label);
1175 choice_dialog_button_set_tooltip(act_sel_dialog,
1176 action_button_map[act_id],
1177 tooltip);
1178 }
1179
1180 /**************************************************************************
1181 Popup a dialog that allows the player to select what action a unit
1182 should take.
1183 **************************************************************************/
popup_action_selection(struct unit * actor_unit,struct city * target_city,struct unit * target_unit,struct tile * target_tile,const struct act_prob * act_probs)1184 void popup_action_selection(struct unit *actor_unit,
1185 struct city *target_city,
1186 struct unit *target_unit,
1187 struct tile *target_tile,
1188 const struct act_prob *act_probs)
1189 {
1190 GtkWidget *shl;
1191 struct astring title = ASTRING_INIT, text = ASTRING_INIT;
1192 struct city *actor_homecity;
1193
1194 int button_id;
1195
1196 struct action_data *data =
1197 act_data(actor_unit->id,
1198 (target_city) ? target_city->id : 0,
1199 (target_unit) ? target_unit->id : 0,
1200 (target_tile) ? target_tile->index : 0,
1201 0);
1202
1203 /* Could be caused by the server failing to reply to a request for more
1204 * information or a bug in the client code. */
1205 fc_assert_msg(!is_more_user_input_needed,
1206 "Diplomat queue problem. Is another diplomat window open?");
1207
1208 /* No extra input is required as no action has been chosen yet. */
1209 is_more_user_input_needed = FALSE;
1210
1211 /* No buttons are added yet. */
1212 for (button_id = 0; button_id < BUTTON_COUNT; button_id++) {
1213 action_button_map[button_id] = BUTTON_NOT_THERE;
1214 }
1215
1216 actor_homecity = game_city_by_number(actor_unit->homecity);
1217
1218 actor_unit_id = actor_unit->id;
1219 target_ids[ATK_CITY] = target_city ?
1220 target_city->id :
1221 IDENTITY_NUMBER_ZERO;
1222 target_ids[ATK_UNIT] = target_unit ?
1223 target_unit->id :
1224 IDENTITY_NUMBER_ZERO;
1225
1226 astr_set(&title,
1227 /* TRANS: %s is a unit name, e.g., Spy */
1228 _("Choose Your %s's Strategy"),
1229 unit_name_translation(actor_unit));
1230
1231 if (target_city && actor_homecity) {
1232 astr_set(&text,
1233 _("Your %s from %s reaches the city of %s.\nWhat now?"),
1234 unit_name_translation(actor_unit),
1235 city_name_get(actor_homecity),
1236 city_name_get(target_city));
1237 } else if (target_city) {
1238 astr_set(&text,
1239 _("Your %s has arrived at %s.\nWhat is your command?"),
1240 unit_name_translation(actor_unit),
1241 city_name_get(target_city));
1242 } else if (target_unit) {
1243 astr_set(&text,
1244 /* TRANS: Your Spy is ready to act against Roman Freight. */
1245 _("Your %s is ready to act against %s %s."),
1246 unit_name_translation(actor_unit),
1247 nation_adjective_for_player(unit_owner(target_unit)),
1248 unit_name_translation(target_unit));
1249 } else {
1250 fc_assert_msg(target_unit || target_city,
1251 "No target unit or target city specified.");
1252 astr_set(&text,
1253 /* TRANS: %s is a unit name, e.g., Diplomat, Spy */
1254 _("Your %s is waiting for your command."),
1255 unit_name_translation(actor_unit));
1256 }
1257
1258 shl = choice_dialog_start(GTK_WINDOW(toplevel), astr_str(&title),
1259 astr_str(&text));
1260
1261 /* Unit acting against a city */
1262
1263 action_iterate(act) {
1264 if (action_id_get_actor_kind(act) == AAK_UNIT
1265 && action_id_get_target_kind(act) == ATK_CITY) {
1266 action_entry(shl,
1267 (enum gen_action)act,
1268 act_probs,
1269 get_act_sel_action_custom_text(action_by_number(act),
1270 act_probs[act],
1271 actor_unit,
1272 target_city),
1273 data);
1274 }
1275 } action_iterate_end;
1276
1277 /* Unit acting against another unit */
1278
1279 action_iterate(act) {
1280 if (action_id_get_actor_kind(act) == AAK_UNIT
1281 && action_id_get_target_kind(act) == ATK_UNIT) {
1282 action_entry(shl,
1283 (enum gen_action)act,
1284 act_probs,
1285 get_act_sel_action_custom_text(action_by_number(act),
1286 act_probs[act],
1287 actor_unit,
1288 target_city),
1289 data);
1290 }
1291 } action_iterate_end;
1292
1293 if (unit_can_move_to_tile(actor_unit, target_tile, FALSE)
1294 || (is_military_unit(actor_unit) || is_attack_unit(actor_unit))
1295 || (can_unit_bombard(actor_unit) && !is_ocean_tile(target_tile))
1296 || (!target_city && unit_has_type_flag(actor_unit, UTYF_CAPTURER))) {
1297 action_button_map[BUTTON_MOVE] =
1298 choice_dialog_get_number_of_buttons(shl);
1299 choice_dialog_add(shl, _("_Keep moving"),
1300 (GCallback)act_sel_keep_moving_callback,
1301 data, FALSE, NULL);
1302 }
1303
1304 if (target_unit != NULL
1305 && unit_list_size(target_tile->units) > 1) {
1306 action_button_map[BUTTON_NEW_UNIT_TGT] =
1307 choice_dialog_get_number_of_buttons(shl);
1308 choice_dialog_add(shl, _("Change unit target"),
1309 (GCallback)act_sel_new_unit_tgt_callback,
1310 data, TRUE, NULL);
1311 }
1312
1313 action_button_map[BUTTON_LOCATION] =
1314 choice_dialog_get_number_of_buttons(shl);
1315 choice_dialog_add(shl, _("Show Location"),
1316 (GCallback)act_sel_location_callback, data,
1317 TRUE, NULL);
1318
1319 action_button_map[BUTTON_WAIT] =
1320 choice_dialog_get_number_of_buttons(shl);
1321 choice_dialog_add(shl, _("_Wait"),
1322 (GCallback)act_sel_wait_callback, data,
1323 TRUE, NULL);
1324
1325 action_button_map[BUTTON_CANCEL] =
1326 choice_dialog_get_number_of_buttons(shl);
1327 choice_dialog_add(shl, _("_Cancel"),
1328 (GCallback)act_sel_cancel_callback, data,
1329 FALSE, NULL);
1330
1331 choice_dialog_end(shl);
1332
1333 act_sel_dialog = shl;
1334
1335 choice_dialog_set_hide(shl, TRUE);
1336 g_signal_connect(shl, "destroy",
1337 G_CALLBACK(act_sel_destroy_callback), NULL);
1338 g_signal_connect(shl, "delete_event",
1339 G_CALLBACK(act_sel_close_callback), data);
1340
1341 /* Give follow up questions access to action probabilities. */
1342 client_unit_init_act_prob_cache(actor_unit);
1343 action_iterate(act) {
1344 actor_unit->client.act_prob_cache[act] = act_probs[act];
1345 } action_iterate_end;
1346
1347 astr_free(&title);
1348 astr_free(&text);
1349 }
1350
1351 /**************************************************************************
1352 Returns the id of the actor unit currently handled in action selection
1353 dialog when the action selection dialog is open.
1354 Returns IDENTITY_NUMBER_ZERO if no action selection dialog is open.
1355 **************************************************************************/
action_selection_actor_unit(void)1356 int action_selection_actor_unit(void)
1357 {
1358 if (act_sel_dialog == NULL) {
1359 return IDENTITY_NUMBER_ZERO;
1360 }
1361 return actor_unit_id;
1362 }
1363
1364 /**************************************************************************
1365 Returns id of the target city of the actions currently handled in action
1366 selection dialog when the action selection dialog is open and it has a
1367 city target. Returns IDENTITY_NUMBER_ZERO if no action selection dialog
1368 is open or no city target is present in the action selection dialog.
1369 **************************************************************************/
action_selection_target_city(void)1370 int action_selection_target_city(void)
1371 {
1372 if (act_sel_dialog == NULL) {
1373 return IDENTITY_NUMBER_ZERO;
1374 }
1375 return target_ids[ATK_CITY];
1376 }
1377
1378 /**************************************************************************
1379 Returns id of the target unit of the actions currently handled in action
1380 selection dialog when the action selection dialog is open and it has a
1381 unit target. Returns IDENTITY_NUMBER_ZERO if no action selection dialog
1382 is open or no unit target is present in the action selection dialog.
1383 **************************************************************************/
action_selection_target_unit(void)1384 int action_selection_target_unit(void)
1385 {
1386 if (act_sel_dialog == NULL) {
1387 return IDENTITY_NUMBER_ZERO;
1388 }
1389
1390 return target_ids[ATK_UNIT];
1391 }
1392
1393 /**************************************************************************
1394 Updates the action selection dialog with new information.
1395 **************************************************************************/
action_selection_refresh(struct unit * actor_unit,struct city * target_city,struct unit * target_unit,struct tile * target_tile,const struct act_prob * act_probs)1396 void action_selection_refresh(struct unit *actor_unit,
1397 struct city *target_city,
1398 struct unit *target_unit,
1399 struct tile *target_tile,
1400 const struct act_prob *act_probs)
1401 {
1402 struct action_data *data;
1403
1404 if (act_sel_dialog == NULL) {
1405 fc_assert_msg(act_sel_dialog != NULL,
1406 "The action selection dialog should have been open");
1407 return;
1408 }
1409
1410 if (actor_unit->id != action_selection_actor_unit()) {
1411 fc_assert_msg(actor_unit->id == action_selection_actor_unit(),
1412 "The action selection dialog is for another actor unit.");
1413 return;
1414 }
1415
1416 data = act_data(actor_unit->id,
1417 (target_city) ? target_city->id : IDENTITY_NUMBER_ZERO,
1418 (target_unit) ? target_unit->id : IDENTITY_NUMBER_ZERO,
1419 (target_tile) ? target_tile->index : 0,
1420 0);
1421
1422 action_iterate(act) {
1423 const char *custom;
1424
1425 if (action_id_get_actor_kind(act) != AAK_UNIT) {
1426 /* Not relevant. */
1427 continue;
1428 }
1429
1430 custom = get_act_sel_action_custom_text(action_by_number(act),
1431 act_probs[act],
1432 actor_unit,
1433 target_city);
1434
1435 if (BUTTON_NOT_THERE == action_button_map[act]) {
1436 /* Add the button (unless its probability is 0). */
1437 action_entry(act_sel_dialog, act, act_probs, custom, data);
1438 } else {
1439 /* Update the existing button. */
1440 action_entry_update(act_sel_dialog, act, act_probs, custom, data);
1441 }
1442 } action_iterate_end;
1443
1444 /* DO NOT change the action_button_map[] for any button to reflect its
1445 * new position. A button keeps its choice dialog internal name when its
1446 * position changes. A button's id number is therefore based on when
1447 * it was added, not on its current position. */
1448
1449 if (BUTTON_NOT_THERE != action_button_map[BUTTON_WAIT]) {
1450 /* Move the wait button below the recently added button. */
1451 choice_dialog_button_move_to_the_end(act_sel_dialog,
1452 action_button_map[BUTTON_WAIT]);
1453 }
1454
1455 if (BUTTON_NOT_THERE != action_button_map[BUTTON_CANCEL]) {
1456 /* Move the cancel button below the recently added button. */
1457 choice_dialog_button_move_to_the_end(act_sel_dialog,
1458 action_button_map[BUTTON_CANCEL]);
1459 }
1460
1461 choice_dialog_end(act_sel_dialog);
1462 }
1463
1464 /****************************************************************
1465 Closes the action selection dialog
1466 ****************************************************************/
action_selection_close(void)1467 void action_selection_close(void)
1468 {
1469 if (act_sel_dialog != NULL) {
1470 did_not_decide = TRUE;
1471 gtk_widget_destroy(act_sel_dialog);
1472 }
1473 }
1474