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