1 #include "empire.h"
2 
3 #include "core/image_group_editor.h"
4 #include "empire/city.h"
5 #include "empire/empire.h"
6 #include "empire/object.h"
7 #include "empire/trade_route.h"
8 #include "empire/type.h"
9 #include "graphics/arrow_button.h"
10 #include "graphics/generic_button.h"
11 #include "graphics/graphics.h"
12 #include "graphics/image.h"
13 #include "graphics/lang_text.h"
14 #include "graphics/screen.h"
15 #include "graphics/text.h"
16 #include "graphics/window.h"
17 #include "input/input.h"
18 #include "input/scroll.h"
19 #include "scenario/editor.h"
20 #include "scenario/empire.h"
21 #include "window/editor/map.h"
22 
23 #define MAX_WIDTH 2032
24 #define MAX_HEIGHT 1136
25 
26 static void button_change_empire(int is_up, int param2);
27 static void button_ok(int param1, int param2);
28 
29 static arrow_button arrow_buttons_empire[] = {
30     {8, 48, 17, 24, button_change_empire, 1, 0},
31     {32, 48, 15, 24, button_change_empire, 0, 0}
32 };
33 static generic_button generic_button_ok[] = {
34     {84, 48, 100, 24, button_ok, button_none, 0, 0}
35 };
36 
37 static struct {
38     int selected_button;
39     int selected_city;
40     int x_min, x_max, y_min, y_max;
41     int x_draw_offset, y_draw_offset;
42     int focus_button_id;
43     int is_scrolling;
44     int finished_scroll;
45     int show_battle_objects;
46 } data;
47 
init(void)48 static void init(void)
49 {
50     data.selected_button = 0;
51     int selected_object = empire_selected_object();
52     if (selected_object) {
53         data.selected_city = empire_city_get_for_object(selected_object - 1);
54     } else {
55         data.selected_city = 0;
56     }
57     data.focus_button_id = 0;
58 }
59 
map_viewport_width(void)60 static int map_viewport_width(void)
61 {
62     return data.x_max - data.x_min - 32;
63 }
64 
map_viewport_height(void)65 static int map_viewport_height(void)
66 {
67     return data.y_max - data.y_min - 136;
68 }
69 
draw_paneling(void)70 static void draw_paneling(void)
71 {
72     int image_base = image_group(GROUP_EDITOR_EMPIRE_PANELS);
73     // bottom panel background
74     graphics_set_clip_rectangle(data.x_min, data.y_min, data.x_max - data.x_min, data.y_max - data.y_min);
75     for (int x = data.x_min; x < data.x_max; x += 70) {
76         image_draw(image_base + 3, x, data.y_max - 120);
77         image_draw(image_base + 3, x, data.y_max - 80);
78         image_draw(image_base + 3, x, data.y_max - 40);
79     }
80 
81     // horizontal bar borders
82     for (int x = data.x_min; x < data.x_max; x += 86) {
83         image_draw(image_base + 1, x, data.y_min);
84         image_draw(image_base + 1, x, data.y_max - 120);
85         image_draw(image_base + 1, x, data.y_max - 16);
86     }
87 
88     // vertical bar borders
89     for (int y = data.y_min + 16; y < data.y_max; y += 86) {
90         image_draw(image_base, data.x_min, y);
91         image_draw(image_base, data.x_max - 16, y);
92     }
93 
94     // crossbars
95     image_draw(image_base + 2, data.x_min, data.y_min);
96     image_draw(image_base + 2, data.x_min, data.y_max - 120);
97     image_draw(image_base + 2, data.x_min, data.y_max - 16);
98     image_draw(image_base + 2, data.x_max - 16, data.y_min);
99     image_draw(image_base + 2, data.x_max - 16, data.y_max - 120);
100     image_draw(image_base + 2, data.x_max - 16, data.y_max - 16);
101 
102     graphics_reset_clip_rectangle();
103 }
104 
draw_background(void)105 static void draw_background(void)
106 {
107     int s_width = screen_width();
108     int s_height = screen_height();
109     data.x_min = s_width <= MAX_WIDTH ? 0 : (s_width - MAX_WIDTH) / 2;
110     data.x_max = s_width <= MAX_WIDTH ? s_width : data.x_min + MAX_WIDTH;
111     data.y_min = s_height <= MAX_HEIGHT ? 0 : (s_height - MAX_HEIGHT) / 2;
112     data.y_max = s_height <= MAX_HEIGHT ? s_height : data.y_min + MAX_HEIGHT;
113 
114     if (data.x_min || data.y_min) {
115         graphics_clear_screen();
116     }
117     draw_paneling();
118 }
119 
draw_shadowed_number(int value,int x,int y,color_t color)120 static void draw_shadowed_number(int value, int x, int y, color_t color)
121 {
122     text_draw_number_colored(value, '@', " ", x + 1, y - 1, FONT_SMALL_PLAIN, COLOR_BLACK);
123     text_draw_number_colored(value, '@', " ", x, y, FONT_SMALL_PLAIN, color);
124 }
125 
draw_empire_object(const empire_object * obj)126 static void draw_empire_object(const empire_object *obj)
127 {
128     int x = obj->x;
129     int y = obj->y;
130     int image_id = obj->image_id;
131 
132     if (!data.show_battle_objects && (
133         obj->type == EMPIRE_OBJECT_BATTLE_ICON ||
134         obj->type == EMPIRE_OBJECT_ROMAN_ARMY ||
135         obj->type == EMPIRE_OBJECT_ENEMY_ARMY)) {
136         return;
137     }
138     if (obj->type == EMPIRE_OBJECT_CITY) {
139         const empire_city *city = empire_city_get(empire_city_get_for_object(obj->id));
140         if (city->type == EMPIRE_CITY_DISTANT_FOREIGN ||
141             city->type == EMPIRE_CITY_FUTURE_ROMAN) {
142             image_id = image_group(GROUP_EDITOR_EMPIRE_FOREIGN_CITY);
143         }
144     } else if (obj->type == EMPIRE_OBJECT_BATTLE_ICON) {
145         draw_shadowed_number(obj->invasion_path_id,
146             data.x_draw_offset + x - 9, data.y_draw_offset + y - 9, COLOR_WHITE);
147         draw_shadowed_number(obj->invasion_years,
148             data.x_draw_offset + x + 15, data.y_draw_offset + y - 9, COLOR_FONT_RED);
149     } else if (obj->type == EMPIRE_OBJECT_ROMAN_ARMY || obj->type == EMPIRE_OBJECT_ENEMY_ARMY) {
150         draw_shadowed_number(obj->distant_battle_travel_months,
151             data.x_draw_offset + x + 7, data.y_draw_offset + y - 9,
152             obj->type == EMPIRE_OBJECT_ROMAN_ARMY ? COLOR_WHITE : COLOR_FONT_RED);
153     }
154     image_draw(image_id, data.x_draw_offset + x, data.y_draw_offset + y);
155     const image *img = image_get(image_id);
156     if (img->animation_speed_id) {
157         int new_animation = empire_object_update_animation(obj, image_id);
158         image_draw(image_id + new_animation,
159             data.x_draw_offset + x + img->sprite_offset_x,
160             data.y_draw_offset + y + img->sprite_offset_y);
161     }
162 }
163 
draw_map(void)164 static void draw_map(void)
165 {
166     int viewport_width = map_viewport_width();
167     int viewport_height = map_viewport_height();
168     graphics_set_clip_rectangle(data.x_min + 16, data.y_min + 16, viewport_width, viewport_height);
169 
170     empire_set_viewport(viewport_width, viewport_height);
171 
172     data.x_draw_offset = data.x_min + 16;
173     data.y_draw_offset = data.y_min + 16;
174     empire_adjust_scroll(&data.x_draw_offset, &data.y_draw_offset);
175     image_draw(image_group(GROUP_EDITOR_EMPIRE_MAP), data.x_draw_offset, data.y_draw_offset);
176 
177     empire_object_foreach(draw_empire_object);
178 
179     graphics_reset_clip_rectangle();
180 }
181 
draw_resource(resource_type resource,int trade_max,int x_offset,int y_offset)182 static void draw_resource(resource_type resource, int trade_max, int x_offset, int y_offset)
183 {
184     graphics_draw_inset_rect(x_offset, y_offset, 26, 26);
185     int image_id = resource + image_group(GROUP_EDITOR_EMPIRE_RESOURCES);
186     int resource_offset = resource_image_offset(resource, RESOURCE_IMAGE_ICON);
187     image_draw(image_id + resource_offset, x_offset + 1, y_offset + 1);
188     switch (trade_max) {
189         case 15:
190             image_draw(image_group(GROUP_EDITOR_TRADE_AMOUNT), x_offset + 21, y_offset - 1);
191             break;
192         case 25:
193             image_draw(image_group(GROUP_EDITOR_TRADE_AMOUNT) + 1, x_offset + 17, y_offset - 1);
194             break;
195         case 40:
196             image_draw(image_group(GROUP_EDITOR_TRADE_AMOUNT) + 2, x_offset + 13, y_offset - 1);
197             break;
198     }
199 }
200 
draw_city_info(const empire_city * city)201 static void draw_city_info(const empire_city *city)
202 {
203     int x_offset = data.x_min + 28;
204     int y_offset = data.y_max - 85;
205 
206     int width = lang_text_draw(21, city->name_id, x_offset, y_offset, FONT_NORMAL_WHITE);
207 
208     switch (city->type) {
209         case EMPIRE_CITY_DISTANT_ROMAN:
210         case EMPIRE_CITY_VULNERABLE_ROMAN:
211             lang_text_draw(47, 12, x_offset + 20 + width, y_offset, FONT_NORMAL_GREEN);
212             break;
213         case EMPIRE_CITY_FUTURE_TRADE:
214         case EMPIRE_CITY_DISTANT_FOREIGN:
215         case EMPIRE_CITY_FUTURE_ROMAN:
216             lang_text_draw(47, 0, x_offset + 20 + width, y_offset, FONT_NORMAL_GREEN);
217             break;
218         case EMPIRE_CITY_OURS: {
219             width += lang_text_draw(47, 1, x_offset + 20 + width, y_offset, FONT_NORMAL_GREEN);
220             int resource_x_offset = x_offset + 30 + width;
221             for (int r = RESOURCE_MIN; r < RESOURCE_MAX; r++) {
222                 if (empire_object_city_sells_resource(city->empire_object_id, r)) {
223                     draw_resource(r, 0, resource_x_offset, y_offset - 9);
224                     resource_x_offset += 32;
225                 }
226             }
227             break;
228         }
229         case EMPIRE_CITY_TRADE: {
230             width += lang_text_draw(47, 5, x_offset + 20 + width, y_offset, FONT_NORMAL_GREEN);
231             int resource_x_offset = x_offset + 30 + width;
232             for (int r = RESOURCE_MIN; r < RESOURCE_MAX; r++) {
233                 if (empire_object_city_sells_resource(city->empire_object_id, r)) {
234                     draw_resource(r, trade_route_limit(city->route_id, r), resource_x_offset, y_offset - 9);
235                     resource_x_offset += 32;
236                 }
237             }
238             resource_x_offset += 50;
239             resource_x_offset += lang_text_draw(47, 4, resource_x_offset, y_offset, FONT_NORMAL_GREEN);
240             resource_x_offset += 10;
241             for (int r = RESOURCE_MIN; r < RESOURCE_MAX; r++) {
242                 if (empire_object_city_buys_resource(city->empire_object_id, r)) {
243                     draw_resource(r, trade_route_limit(city->route_id, r), resource_x_offset, y_offset - 9);
244                     resource_x_offset += 32;
245                 }
246             }
247             break;
248         }
249     }
250 }
251 
draw_panel_buttons(const empire_city * city)252 static void draw_panel_buttons(const empire_city *city)
253 {
254     arrow_buttons_draw(data.x_min + 20, data.y_max - 100, arrow_buttons_empire, 2);
255 
256     if (city) {
257         draw_city_info(city);
258     } else {
259         lang_text_draw_centered(150, scenario_empire_id(),
260             data.x_min, data.y_max - 85, data.x_max - data.x_min, FONT_NORMAL_GREEN);
261     }
262     lang_text_draw(151, scenario_empire_id(), data.x_min + 220, data.y_max - 45, FONT_NORMAL_GREEN);
263 
264     button_border_draw(data.x_min + 104, data.y_max - 52, 100, 24, data.focus_button_id == 1);
265     lang_text_draw_centered(44, 7, data.x_min + 104, data.y_max - 45, 100, FONT_NORMAL_GREEN);
266 }
267 
draw_foreground(void)268 static void draw_foreground(void)
269 {
270     draw_map();
271 
272     const empire_city *city = 0;
273     int selected_object = empire_selected_object();
274     if (selected_object) {
275         const empire_object *object = empire_object_get(selected_object - 1);
276         if (object->type == EMPIRE_OBJECT_CITY) {
277             data.selected_city = empire_city_get_for_object(object->id);
278             city = empire_city_get(data.selected_city);
279         }
280     }
281     draw_panel_buttons(city);
282 }
283 
is_outside_map(int x,int y)284 static int is_outside_map(int x, int y)
285 {
286     return (x < data.x_min + 16 || x >= data.x_max - 16 ||
287             y < data.y_min + 16 || y >= data.y_max - 120);
288 }
289 
determine_selected_object(const mouse * m)290 static void determine_selected_object(const mouse *m)
291 {
292     if (!m->left.went_up || data.finished_scroll || is_outside_map(m->x, m->y)) {
293         data.finished_scroll = 0;
294         return;
295     }
296     empire_select_object(m->x - data.x_min - 16, m->y - data.y_min - 16);
297     window_invalidate();
298 }
299 
handle_input(const mouse * m,const hotkeys * h)300 static void handle_input(const mouse *m, const hotkeys *h)
301 {
302     pixel_offset position;
303     if (scroll_get_delta(m, &position, SCROLL_TYPE_EMPIRE)) {
304         empire_scroll_map(position.x, position.y);
305     }
306     if (h->toggle_editor_battle_info) {
307         data.show_battle_objects = !data.show_battle_objects;
308     }
309     if (m->is_touch) {
310         const touch *t = touch_get_earliest();
311         if (!is_outside_map(t->current_point.x, t->current_point.y)) {
312             if (t->has_started) {
313                 data.is_scrolling = 1;
314                 scroll_drag_start(1);
315             }
316         }
317         if (t->has_ended) {
318             data.is_scrolling = 0;
319             data.finished_scroll = !touch_was_click(t);
320             scroll_drag_end();
321         }
322     }
323     data.focus_button_id = 0;
324     if (!arrow_buttons_handle_mouse(m, data.x_min + 20, data.y_max - 100, arrow_buttons_empire, 2, 0)) {
325         if (!generic_buttons_handle_mouse(m, data.x_min + 20, data.y_max - 100,
326             generic_button_ok, 1, &data.focus_button_id)) {
327             determine_selected_object(m);
328             int selected_object = empire_selected_object();
329             if (selected_object) {
330                 if (empire_object_get(selected_object - 1)->type == EMPIRE_OBJECT_CITY) {
331                     data.selected_city = empire_city_get_for_object(selected_object - 1);
332                 }
333             } else if (input_go_back_requested(m, h)) {
334                 window_editor_map_show();
335             }
336         }
337     }
338 }
339 
button_change_empire(int is_down,int param2)340 static void button_change_empire(int is_down, int param2)
341 {
342     scenario_editor_change_empire(is_down ? -1 : 1);
343     empire_load_editor(scenario_empire_id(), map_viewport_width(), map_viewport_height());
344     window_request_refresh();
345 }
346 
button_ok(int param1,int param2)347 static void button_ok(int param1, int param2)
348 {
349     window_editor_map_show();
350 }
351 
window_editor_empire_show(void)352 void window_editor_empire_show(void)
353 {
354     window_type window = {
355         WINDOW_EDITOR_EMPIRE,
356         draw_background,
357         draw_foreground,
358         handle_input
359     };
360     init();
361     window_show(&window);
362 }
363