1 #include "empire.h"
2 
3 #include "building/menu.h"
4 #include "city/military.h"
5 #include "city/warning.h"
6 #include "core/image_group.h"
7 #include "empire/city.h"
8 #include "empire/empire.h"
9 #include "empire/object.h"
10 #include "empire/trade_route.h"
11 #include "empire/type.h"
12 #include "game/tutorial.h"
13 #include "graphics/generic_button.h"
14 #include "graphics/graphics.h"
15 #include "graphics/image.h"
16 #include "graphics/image_button.h"
17 #include "graphics/lang_text.h"
18 #include "graphics/screen.h"
19 #include "graphics/text.h"
20 #include "graphics/window.h"
21 #include "input/input.h"
22 #include "input/scroll.h"
23 #include "scenario/empire.h"
24 #include "scenario/invasion.h"
25 #include "window/advisors.h"
26 #include "window/city.h"
27 #include "window/message_dialog.h"
28 #include "window/popup_dialog.h"
29 #include "window/resource_settings.h"
30 #include "window/trade_opened.h"
31 
32 #define MAX_WIDTH 2032
33 #define MAX_HEIGHT 1136
34 
35 static void button_help(int param1, int param2);
36 static void button_return_to_city(int param1, int param2);
37 static void button_advisor(int advisor, int param2);
38 static void button_open_trade(int param1, int param2);
39 static void button_show_resource_window(int resource, int param2);
40 
41 static image_button image_button_help[] = {
42     {0, 0, 27, 27, IB_NORMAL, GROUP_CONTEXT_ICONS, 0, button_help, button_none, 0, 0, 1}
43 };
44 static image_button image_button_return_to_city[] = {
45     {0, 0, 24, 24, IB_NORMAL, GROUP_CONTEXT_ICONS, 4, button_return_to_city, button_none, 0, 0, 1}
46 };
47 static image_button image_button_advisor[] = {
48     {-4, 0, 24, 24, IB_NORMAL, GROUP_MESSAGE_ADVISOR_BUTTONS, 12, button_advisor, button_none, ADVISOR_TRADE, 0, 1}
49 };
50 static generic_button generic_button_trade_resource[] = {
51     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_WHEAT, 0},
52     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_VEGETABLES , 0},
53     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_FRUIT , 0},
54     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_OLIVES , 0},
55     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_VINES , 0},
56     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_MEAT, 0},
57     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_WINE , 0},
58     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_OIL , 0},
59     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_IRON , 0},
60     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_TIMBER, 0},
61     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_CLAY, 0},
62     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_MARBLE, 0},
63     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_WEAPONS, 0},
64     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_FURNITURE, 0},
65     {0, 0, 101, 27, button_show_resource_window, button_none, RESOURCE_POTTERY, 0}
66 };
67 static generic_button generic_button_open_trade[] = {
68     {30, 56, 440, 26, button_open_trade, button_none, 0, 0}
69 };
70 
71 static struct {
72     int selected_button;
73     int selected_city;
74     int x_min, x_max, y_min, y_max;
75     int x_draw_offset, y_draw_offset;
76     int focus_button_id;
77     int is_scrolling;
78     int finished_scroll;
79     int focus_resource;
80 } data = { 0, 1 };
81 
init(void)82 static void init(void)
83 {
84     data.selected_button = 0;
85     int selected_object = empire_selected_object();
86     if (selected_object) {
87         data.selected_city = empire_city_get_for_object(selected_object - 1);
88     } else {
89         data.selected_city = 0;
90     }
91     data.focus_button_id = 0;
92 }
93 
draw_paneling(void)94 static void draw_paneling(void)
95 {
96     int image_base = image_group(GROUP_EMPIRE_PANELS);
97     // bottom panel background
98     graphics_set_clip_rectangle(data.x_min, data.y_min, data.x_max - data.x_min, data.y_max - data.y_min);
99     for (int x = data.x_min; x < data.x_max; x += 70) {
100         image_draw(image_base + 3, x, data.y_max - 120);
101         image_draw(image_base + 3, x, data.y_max - 80);
102         image_draw(image_base + 3, x, data.y_max - 40);
103     }
104 
105     // horizontal bar borders
106     for (int x = data.x_min; x < data.x_max; x += 86) {
107         image_draw(image_base + 1, x, data.y_min);
108         image_draw(image_base + 1, x, data.y_max - 120);
109         image_draw(image_base + 1, x, data.y_max - 16);
110     }
111 
112     // vertical bar borders
113     for (int y = data.y_min + 16; y < data.y_max; y += 86) {
114         image_draw(image_base, data.x_min, y);
115         image_draw(image_base, data.x_max - 16, y);
116     }
117 
118     // crossbars
119     image_draw(image_base + 2, data.x_min, data.y_min);
120     image_draw(image_base + 2, data.x_min, data.y_max - 120);
121     image_draw(image_base + 2, data.x_min, data.y_max - 16);
122     image_draw(image_base + 2, data.x_max - 16, data.y_min);
123     image_draw(image_base + 2, data.x_max - 16, data.y_max - 120);
124     image_draw(image_base + 2, data.x_max - 16, data.y_max - 16);
125 
126     graphics_reset_clip_rectangle();
127 }
128 
draw_trade_resource(resource_type resource,int trade_max,int x_offset,int y_offset)129 static void draw_trade_resource(resource_type resource, int trade_max, int x_offset, int y_offset)
130 {
131     graphics_draw_inset_rect(x_offset, y_offset, 26, 26);
132 
133     int image_id = resource + image_group(GROUP_EMPIRE_RESOURCES);
134     int resource_offset = resource_image_offset(resource, RESOURCE_IMAGE_ICON);
135     image_draw(image_id + resource_offset, x_offset + 1, y_offset + 1);
136 
137     if (data.focus_resource == resource) {
138         button_border_draw(x_offset - 2, y_offset - 2, 101 + 4, 30, 1);
139     }
140 
141     switch (trade_max) {
142         case 15:
143             image_draw(image_group(GROUP_TRADE_AMOUNT), x_offset + 21, y_offset - 1);
144             break;
145         case 25:
146             image_draw(image_group(GROUP_TRADE_AMOUNT) + 1, x_offset + 17, y_offset - 1);
147             break;
148         case 40:
149             image_draw(image_group(GROUP_TRADE_AMOUNT) + 2, x_offset + 13, y_offset - 1);
150             break;
151     }
152 }
153 
draw_trade_city_info(const empire_object * object,const empire_city * city)154 static void draw_trade_city_info(const empire_object *object, const empire_city *city)
155 {
156     int x_offset = (data.x_min + data.x_max - 500) / 2;
157     int y_offset = data.y_max - 113;
158     if (city->is_open) {
159         // city sells
160         lang_text_draw(47, 10, x_offset + 44, y_offset + 40, FONT_NORMAL_GREEN);
161         int index = 0;
162         for (int resource = RESOURCE_MIN; resource < RESOURCE_MAX; resource++) {
163             if (!city->sells_resource[resource]) {
164                 continue;
165             }
166             int trade_max = trade_route_limit(city->route_id, resource);
167             draw_trade_resource(resource, trade_max, x_offset + 104 * index + 120, y_offset + 31);
168             int trade_now = trade_route_traded(city->route_id, resource);
169             if (trade_now > trade_max) {
170                 trade_max = trade_now;
171             }
172             int text_width = text_draw_number(trade_now, '@', "",
173                 x_offset + 104 * index + 150, y_offset + 40, FONT_NORMAL_GREEN, 0);
174             text_width += lang_text_draw(47, 11,
175                 x_offset + 104 * index + 148 + text_width, y_offset + 40, FONT_NORMAL_GREEN);
176             text_draw_number(trade_max, '@', "",
177                 x_offset + 104 * index + 138 + text_width, y_offset + 40, FONT_NORMAL_GREEN, 0);
178             index++;
179         }
180         // city buys
181         lang_text_draw(47, 9, x_offset + 44, y_offset + 71, FONT_NORMAL_GREEN);
182         index = 0;
183         for (int resource = RESOURCE_MIN; resource < RESOURCE_MAX; resource++) {
184             if (!city->buys_resource[resource]) {
185                 continue;
186             }
187             int trade_max = trade_route_limit(city->route_id, resource);
188             draw_trade_resource(resource, trade_max, x_offset + 104 * index + 120, y_offset + 62);
189             int trade_now = trade_route_traded(city->route_id, resource);
190             if (trade_now > trade_max) {
191                 trade_max = trade_now;
192             }
193             int text_width = text_draw_number(trade_now, '@', "",
194                 x_offset + 104 * index + 150, y_offset + 71, FONT_NORMAL_GREEN, 0);
195             text_width += lang_text_draw(47, 11,
196                 x_offset + 104 * index + 148 + text_width, y_offset + 71, FONT_NORMAL_GREEN);
197             text_draw_number(trade_max, '@', "",
198                 x_offset + 104 * index + 138 + text_width, y_offset + 71, FONT_NORMAL_GREEN, 0);
199             index++;
200         }
201     } else { // trade is closed
202         int index = lang_text_draw(47, 5, x_offset + 50, y_offset + 42, FONT_NORMAL_GREEN);
203         for (int resource = RESOURCE_MIN; resource < RESOURCE_MAX; resource++) {
204             if (!city->sells_resource[resource]) {
205                 continue;
206             }
207             int trade_max = trade_route_limit(city->route_id, resource);
208             draw_trade_resource(resource, trade_max, x_offset + index + 60, y_offset + 33);
209             index += 32;
210         }
211         index += lang_text_draw(47, 4, x_offset + index + 100, y_offset + 42, FONT_NORMAL_GREEN);
212         for (int resource = RESOURCE_MIN; resource < RESOURCE_MAX; resource++) {
213             if (!city->buys_resource[resource]) {
214                 continue;
215             }
216             int trade_max = trade_route_limit(city->route_id, resource);
217             draw_trade_resource(resource, trade_max, x_offset + index + 110, y_offset + 33);
218             index += 32;
219         }
220         index = lang_text_draw_amount(8, 0, city->cost_to_open,
221             x_offset + 40, y_offset + 73, FONT_NORMAL_GREEN);
222         lang_text_draw(47, 6, x_offset + index + 40, y_offset + 73, FONT_NORMAL_GREEN);
223         int image_id = image_group(GROUP_EMPIRE_TRADE_ROUTE_TYPE) + 1 - city->is_sea_trade;
224         image_draw(image_id, x_offset + 430, y_offset + 65 + 2 * city->is_sea_trade);
225     }
226 }
227 
draw_city_info(const empire_object * object)228 static void draw_city_info(const empire_object *object)
229 {
230     int x_offset = (data.x_min + data.x_max - 240) / 2;
231     int y_offset = data.y_max - 88;
232 
233     const empire_city *city = empire_city_get(data.selected_city);
234     switch (city->type) {
235         case EMPIRE_CITY_DISTANT_ROMAN:
236             lang_text_draw_centered(47, 12, x_offset, y_offset + 42, 240, FONT_NORMAL_GREEN);
237             break;
238         case EMPIRE_CITY_VULNERABLE_ROMAN:
239             if (city_military_distant_battle_city_is_roman()) {
240                 lang_text_draw_centered(47, 12, x_offset, y_offset + 42, 240, FONT_NORMAL_GREEN);
241             } else {
242                 lang_text_draw_centered(47, 13, x_offset, y_offset + 42, 240, FONT_NORMAL_GREEN);
243             }
244             break;
245         case EMPIRE_CITY_FUTURE_TRADE:
246         case EMPIRE_CITY_DISTANT_FOREIGN:
247         case EMPIRE_CITY_FUTURE_ROMAN:
248             lang_text_draw_centered(47, 0, x_offset, y_offset + 42, 240, FONT_NORMAL_GREEN);
249             break;
250         case EMPIRE_CITY_OURS:
251             lang_text_draw_centered(47, 1, x_offset, y_offset + 42, 240, FONT_NORMAL_GREEN);
252             break;
253         case EMPIRE_CITY_TRADE:
254             draw_trade_city_info(object, city);
255             break;
256     }
257 }
258 
draw_roman_army_info(const empire_object * object)259 static void draw_roman_army_info(const empire_object *object)
260 {
261     if (city_military_distant_battle_roman_army_is_traveling()) {
262         if (city_military_distant_battle_roman_months_traveled() == object->distant_battle_travel_months) {
263             int x_offset = (data.x_min + data.x_max - 240) / 2;
264             int y_offset = data.y_max - 68;
265             int text_id;
266             if (city_military_distant_battle_roman_army_is_traveling_forth()) {
267                 text_id = 15;
268             } else {
269                 text_id = 16;
270             }
271             lang_text_draw_multiline(47, text_id, x_offset, y_offset, 240, FONT_NORMAL_GREEN);
272         }
273     }
274 }
275 
draw_enemy_army_info(const empire_object * object)276 static void draw_enemy_army_info(const empire_object *object)
277 {
278     if (city_military_months_until_distant_battle() > 0) {
279         if (city_military_distant_battle_enemy_months_traveled() == object->distant_battle_travel_months) {
280             lang_text_draw_multiline(47, 14,
281                 (data.x_min + data.x_max - 240) / 2,
282                 data.y_max - 68,
283                 240, FONT_NORMAL_GREEN);
284         }
285     }
286 }
287 
draw_object_info(void)288 static void draw_object_info(void)
289 {
290     int selected_object = empire_selected_object();
291     if (selected_object) {
292         const empire_object *object = empire_object_get(selected_object - 1);
293         switch (object->type) {
294             case EMPIRE_OBJECT_CITY:
295                 draw_city_info(object);
296                 break;
297             case EMPIRE_OBJECT_ROMAN_ARMY:
298                 draw_roman_army_info(object);
299                 break;
300             case EMPIRE_OBJECT_ENEMY_ARMY:
301                 draw_enemy_army_info(object);
302                 break;
303         }
304     } else {
305         lang_text_draw_centered(47, 8, data.x_min, data.y_max - 48, data.x_max - data.x_min, FONT_NORMAL_GREEN);
306     }
307 }
308 
draw_background(void)309 static void draw_background(void)
310 {
311     int s_width = screen_width();
312     int s_height = screen_height();
313     data.x_min = s_width <= MAX_WIDTH ? 0 : (s_width - MAX_WIDTH) / 2;
314     data.x_max = s_width <= MAX_WIDTH ? s_width : data.x_min + MAX_WIDTH;
315     data.y_min = s_height <= MAX_HEIGHT ? 0 : (s_height - MAX_HEIGHT) / 2;
316     data.y_max = s_height <= MAX_HEIGHT ? s_height : data.y_min + MAX_HEIGHT;
317 
318     if (data.x_min || data.y_min) {
319         graphics_clear_screens();
320     }
321 }
322 
draw_empire_object(const empire_object * obj)323 static void draw_empire_object(const empire_object *obj)
324 {
325     if (obj->type == EMPIRE_OBJECT_LAND_TRADE_ROUTE || obj->type == EMPIRE_OBJECT_SEA_TRADE_ROUTE) {
326         if (!empire_city_is_trade_route_open(obj->trade_route_id)) {
327             return;
328         }
329     }
330     int x, y, image_id;
331     if (scenario_empire_is_expanded()) {
332         x = obj->expanded.x;
333         y = obj->expanded.y;
334         image_id = obj->expanded.image_id;
335     } else {
336         x = obj->x;
337         y = obj->y;
338         image_id = obj->image_id;
339     }
340 
341     if (obj->type == EMPIRE_OBJECT_CITY) {
342         const empire_city *city = empire_city_get(empire_city_get_for_object(obj->id));
343         if (city->type == EMPIRE_CITY_DISTANT_FOREIGN ||
344             city->type == EMPIRE_CITY_FUTURE_ROMAN) {
345             image_id = image_group(GROUP_EMPIRE_FOREIGN_CITY);
346         } else if (city->type == EMPIRE_CITY_TRADE) {
347             // Fix cases where empire map still gives a blue flag for new trade cities
348             // (e.g. Massilia in campaign Lugdunum)
349             image_id = image_group(GROUP_EMPIRE_CITY_TRADE);
350         }
351     }
352     if (obj->type == EMPIRE_OBJECT_BATTLE_ICON) {
353         // handled later
354         return;
355     }
356     if (obj->type == EMPIRE_OBJECT_ENEMY_ARMY) {
357         if (city_military_months_until_distant_battle() <= 0) {
358             return;
359         }
360         if (city_military_distant_battle_enemy_months_traveled() != obj->distant_battle_travel_months) {
361             return;
362         }
363     }
364     if (obj->type == EMPIRE_OBJECT_ROMAN_ARMY) {
365         if (!city_military_distant_battle_roman_army_is_traveling()) {
366             return;
367         }
368         if (city_military_distant_battle_roman_months_traveled() != obj->distant_battle_travel_months) {
369             return;
370         }
371     }
372     image_draw(image_id, data.x_draw_offset + x, data.y_draw_offset + y);
373     const image *img = image_get(image_id);
374     if (img->animation_speed_id) {
375         int new_animation = empire_object_update_animation(obj, image_id);
376         image_draw(image_id + new_animation,
377             data.x_draw_offset + x + img->sprite_offset_x,
378             data.y_draw_offset + y + img->sprite_offset_y);
379     }
380 }
381 
draw_invasion_warning(int x,int y,int image_id)382 static void draw_invasion_warning(int x, int y, int image_id)
383 {
384     image_draw(image_id, data.x_draw_offset + x, data.y_draw_offset + y);
385 }
386 
draw_map(void)387 static void draw_map(void)
388 {
389     graphics_set_clip_rectangle(data.x_min + 16, data.y_min + 16,
390         data.x_max - data.x_min - 32, data.y_max - data.y_min - 136);
391 
392     empire_set_viewport(data.x_max - data.x_min - 32, data.y_max - data.y_min - 136);
393 
394     data.x_draw_offset = data.x_min + 16;
395     data.y_draw_offset = data.y_min + 16;
396     empire_adjust_scroll(&data.x_draw_offset, &data.y_draw_offset);
397     image_draw(image_group(GROUP_EMPIRE_MAP), data.x_draw_offset, data.y_draw_offset);
398 
399     empire_object_foreach(draw_empire_object);
400 
401     scenario_invasion_foreach_warning(draw_invasion_warning);
402 
403     graphics_reset_clip_rectangle();
404 }
405 
draw_city_name(const empire_city * city)406 static void draw_city_name(const empire_city *city)
407 {
408     int image_base = image_group(GROUP_EMPIRE_PANELS);
409     image_draw(image_base + 6, data.x_min + 2, data.y_max - 199);
410     image_draw(image_base + 7, data.x_max - 84, data.y_max - 199);
411     image_draw(image_base + 8, (data.x_min + data.x_max - 332) / 2, data.y_max - 181);
412     if (city) {
413         lang_text_draw_centered(21, city->name_id,
414             (data.x_min + data.x_max - 332) / 2 + 64, data.y_max - 118, 268, FONT_LARGE_BLACK);
415     }
416 }
417 
draw_panel_buttons(const empire_city * city)418 static void draw_panel_buttons(const empire_city *city)
419 {
420     image_buttons_draw(data.x_min + 20, data.y_max - 44, image_button_help, 1);
421     image_buttons_draw(data.x_max - 44, data.y_max - 44, image_button_return_to_city, 1);
422     image_buttons_draw(data.x_max - 44, data.y_max - 100, image_button_advisor, 1);
423     if (city) {
424         if (city->type == EMPIRE_CITY_TRADE && !city->is_open) {
425             button_border_draw((data.x_min + data.x_max - 500) / 2 + 30, data.y_max - 49, 440,
426                 26, data.selected_button);
427         }
428     }
429 }
430 
draw_foreground(void)431 static void draw_foreground(void)
432 {
433     draw_map();
434 
435     const empire_city *city = 0;
436     int selected_object = empire_selected_object();
437     if (selected_object) {
438         const empire_object *object = empire_object_get(selected_object - 1);
439         if (object->type == EMPIRE_OBJECT_CITY) {
440             data.selected_city = empire_city_get_for_object(object->id);
441             city = empire_city_get(data.selected_city);
442         }
443     }
444     draw_paneling();
445     draw_city_name(city);
446     draw_panel_buttons(city);
447     draw_object_info();
448 }
449 
is_outside_map(int x,int y)450 static int is_outside_map(int x, int y)
451 {
452     return (x < data.x_min + 16 || x >= data.x_max - 16 ||
453         y < data.y_min + 16 || y >= data.y_max - 120);
454 }
455 
determine_selected_object(const mouse * m)456 static void determine_selected_object(const mouse *m)
457 {
458     if (!m->left.went_up || data.finished_scroll || is_outside_map(m->x, m->y)) {
459         data.finished_scroll = 0;
460         return;
461     }
462     empire_select_object(m->x - data.x_min - 16, m->y - data.y_min - 16);
463     window_invalidate();
464 }
465 
handle_input(const mouse * m,const hotkeys * h)466 static void handle_input(const mouse *m, const hotkeys *h)
467 {
468     pixel_offset position;
469     if (scroll_get_delta(m, &position, SCROLL_TYPE_EMPIRE)) {
470         empire_scroll_map(position.x, position.y);
471     }
472     if (m->is_touch) {
473         const touch *t = touch_get_earliest();
474         if (!is_outside_map(t->current_point.x, t->current_point.y)) {
475             if (t->has_started) {
476                 data.is_scrolling = 1;
477                 scroll_drag_start(1);
478             }
479         }
480         if (t->has_ended) {
481             data.is_scrolling = 0;
482             data.finished_scroll = !touch_was_click(t);
483             scroll_drag_end();
484         }
485     }
486     data.focus_button_id = 0;
487     data.focus_resource = 0;
488     int button_id;
489     image_buttons_handle_mouse(m, data.x_min + 20, data.y_max - 44, image_button_help, 1, &button_id);
490     if (button_id) {
491         data.focus_button_id = 1;
492     }
493     image_buttons_handle_mouse(m, data.x_max - 44, data.y_max - 44, image_button_return_to_city, 1, &button_id);
494     if (button_id) {
495         data.focus_button_id = 2;
496     }
497     image_buttons_handle_mouse(m, data.x_max - 44, data.y_max - 100, image_button_advisor, 1, &button_id);
498     if (button_id) {
499         data.focus_button_id = 3;
500     }
501     determine_selected_object(m);
502     int selected_object = empire_selected_object();
503     if (selected_object) {
504         const empire_object *obj = empire_object_get(selected_object - 1);
505         if (obj->type == EMPIRE_OBJECT_CITY) {
506             data.selected_city = empire_city_get_for_object(selected_object - 1);
507             const empire_city *city = empire_city_get(data.selected_city);
508             if (city->type == EMPIRE_CITY_TRADE) {
509                 if (city->is_open) {
510                     int x_offset = (data.x_min + data.x_max - 500) / 2;
511                     int y_offset = data.y_max - 113;
512                     int index_sell = 0;
513                     int index_buy = 0;
514 
515                     // we only want to handle resource buttons that the selected city trades
516                     for (int resource = RESOURCE_MIN; resource < RESOURCE_MAX; resource++) {
517                         if (empire_object_city_sells_resource(obj->id, resource)) {
518                             generic_buttons_handle_mouse(m, x_offset + 120 + 104 * index_sell, y_offset + 31,
519                                 generic_button_trade_resource + resource - 1, 1, &button_id);
520                             index_sell++;
521                         } else if (empire_object_city_buys_resource(obj->id, resource)) {
522                             generic_buttons_handle_mouse(m, x_offset + 120 + 104 * index_buy, y_offset + 62,
523                                 generic_button_trade_resource + resource - 1, 1, &button_id);
524                             index_buy++;
525                         }
526 
527                         if (button_id) {
528                             data.focus_resource = resource;
529                             // if we're focusing any button we can skip further checks
530                             break;
531                         }
532                     }
533                 } else {
534                     generic_buttons_handle_mouse(
535                         m, (data.x_min + data.x_max - 500) / 2, data.y_max - 105,
536                         generic_button_open_trade, 1, &data.selected_button);
537                 }
538             }
539         }
540         if (input_go_back_requested(m, h)) {
541             empire_clear_selected_object();
542             window_invalidate();
543         }
544     } else {
545         if (input_go_back_requested(m, h)) {
546             window_city_show();
547         }
548     }
549 }
550 
is_mouse_hit(tooltip_context * c,int x,int y,int size)551 static int is_mouse_hit(tooltip_context *c, int x, int y, int size)
552 {
553     int mx = c->mouse_x;
554     int my = c->mouse_y;
555     return x <= mx && mx < x + size && y <= my && my < y + size;
556 }
557 
get_tooltip_resource(tooltip_context * c)558 static int get_tooltip_resource(tooltip_context *c)
559 {
560     const empire_city *city = empire_city_get(data.selected_city);
561     // we only want to check tooltips on our own closed cities.
562     // open city resource tooltips are handled by their respective buttons directly
563     if (city->type != EMPIRE_CITY_TRADE || city->is_open) {
564         return 0;
565     }
566     int object_id = empire_selected_object() - 1;
567     int x_offset = (data.x_min + data.x_max - 500) / 2;
568     int y_offset = data.y_max - 113;
569 
570     int item_offset = lang_text_get_width(47, 5, FONT_NORMAL_GREEN);
571     for (int r = RESOURCE_MIN; r < RESOURCE_MAX; r++) {
572         if (empire_object_city_sells_resource(object_id, r)) {
573             if (is_mouse_hit(c, x_offset + 60 + item_offset, y_offset + 33, 26)) {
574                 return r;
575             }
576             item_offset += 32;
577         }
578     }
579     item_offset += lang_text_get_width(47, 4, FONT_NORMAL_GREEN);
580     for (int r = RESOURCE_MIN; r <= RESOURCE_MAX; r++) {
581         if (empire_object_city_buys_resource(object_id, r)) {
582             if (is_mouse_hit(c, x_offset + 110 + item_offset, y_offset + 33, 26)) {
583                 return r;
584             }
585             item_offset += 32;
586         }
587     }
588 
589     return 0;
590 }
591 
get_tooltip_trade_route_type(tooltip_context * c)592 static void get_tooltip_trade_route_type(tooltip_context *c)
593 {
594     int selected_object = empire_selected_object();
595     if (!selected_object || empire_object_get(selected_object - 1)->type != EMPIRE_OBJECT_CITY) {
596         return;
597     }
598 
599     data.selected_city = empire_city_get_for_object(selected_object - 1);
600     const empire_city *city = empire_city_get(data.selected_city);
601     if (city->type != EMPIRE_CITY_TRADE || city->is_open) {
602         return;
603     }
604 
605     int x_offset = (data.x_min + data.x_max + 355) / 2;
606     int y_offset = data.y_max - 41;
607     int y_offset_max = y_offset + 22 - 2 * city->is_sea_trade;
608     if (c->mouse_x >= x_offset && c->mouse_x < x_offset + 32 &&
609         c->mouse_y >= y_offset && c->mouse_y < y_offset_max) {
610         c->type = TOOLTIP_BUTTON;
611         c->text_group = 44;
612         c->text_id = 28 + city->is_sea_trade;
613     }
614 }
615 
get_tooltip(tooltip_context * c)616 static void get_tooltip(tooltip_context *c)
617 {
618     int resource = data.focus_resource ? data.focus_resource : get_tooltip_resource(c);
619     if (resource) {
620         c->type = TOOLTIP_BUTTON;
621         c->text_id = 131 + resource;
622     } else if (data.focus_button_id) {
623         c->type = TOOLTIP_BUTTON;
624         switch (data.focus_button_id) {
625             case 1: c->text_id = 1; break;
626             case 2: c->text_id = 2; break;
627             case 3: c->text_id = 69; break;
628         }
629     } else {
630         get_tooltip_trade_route_type(c);
631     }
632 }
633 
button_help(int param1,int param2)634 static void button_help(int param1, int param2)
635 {
636     window_message_dialog_show(MESSAGE_DIALOG_EMPIRE_MAP, 0);
637 }
638 
button_return_to_city(int param1,int param2)639 static void button_return_to_city(int param1, int param2)
640 {
641     window_city_show();
642 }
643 
button_advisor(int advisor,int param2)644 static void button_advisor(int advisor, int param2)
645 {
646     window_advisors_show_advisor(advisor);
647 }
648 
button_show_resource_window(int resource,int param2)649 static void button_show_resource_window(int resource, int param2)
650 {
651     window_resource_settings_show(resource);
652 }
653 
confirmed_open_trade(int accepted,int checked)654 static void confirmed_open_trade(int accepted, int checked)
655 {
656     if (accepted) {
657         empire_city_open_trade(data.selected_city);
658         building_menu_update();
659         window_trade_opened_show(data.selected_city);
660     }
661 }
662 
button_open_trade(int param1,int param2)663 static void button_open_trade(int param1, int param2)
664 {
665     window_popup_dialog_show(POPUP_DIALOG_OPEN_TRADE, confirmed_open_trade, 2);
666 }
667 
window_empire_show(void)668 void window_empire_show(void)
669 {
670     window_type window = {
671         WINDOW_EMPIRE,
672         draw_background,
673         draw_foreground,
674         handle_input,
675         get_tooltip
676     };
677     init();
678     window_show(&window);
679 }
680 
window_empire_show_checked(void)681 void window_empire_show_checked(void)
682 {
683     tutorial_availability avail = tutorial_advisor_empire_availability();
684     if (avail == AVAILABLE) {
685         window_empire_show();
686     } else {
687         city_warning_show(avail == NOT_AVAILABLE ? WARNING_NOT_AVAILABLE : WARNING_NOT_AVAILABLE_YET);
688     }
689 }
690