1 #include "object.h"
2 
3 #include "core/calc.h"
4 #include "core/image.h"
5 #include "empire/city.h"
6 #include "empire/trade_route.h"
7 #include "empire/type.h"
8 #include "game/animation.h"
9 #include "scenario/empire.h"
10 
11 #define MAX_OBJECTS 200
12 
13 typedef struct {
14     int in_use;
15     int city_type;
16     int city_name_id;
17     int trade_route_open;
18     int trade_route_cost;
19     int city_sells_resource[10];
20     int city_buys_resource[8];
21     int trade40;
22     int trade25;
23     int trade15;
24     empire_object obj;
25 } full_empire_object;
26 
27 static full_empire_object objects[MAX_OBJECTS];
28 
29 static int get_trade_amount_code(int index, int resource);
30 static int is_sea_trade_route(int route_id);
31 
fix_image_ids(void)32 static void fix_image_ids(void)
33 {
34     int image_id = 0;
35     for (int i = 0; i < MAX_OBJECTS; i++) {
36         if (objects[i].in_use
37             && objects[i].obj.type == EMPIRE_OBJECT_CITY
38             && objects[i].city_type == EMPIRE_CITY_OURS) {
39             image_id = objects[i].obj.image_id;
40             break;
41         }
42     }
43     if (image_id > 0 && image_id != image_group(GROUP_EMPIRE_CITY)) {
44         // empire map uses old version of graphics: increase every graphic id
45         int offset = image_group(GROUP_EMPIRE_CITY) - image_id;
46         for (int i = 0; i < MAX_OBJECTS; i++) {
47             if (!objects[i].in_use) {
48                 continue;
49             }
50             if (objects[i].obj.image_id) {
51                 objects[i].obj.image_id += offset;
52                 if (objects[i].obj.expanded.image_id) {
53                     objects[i].obj.expanded.image_id += offset;
54                 }
55             }
56         }
57     }
58 }
59 
empire_object_load(buffer * buf)60 void empire_object_load(buffer *buf)
61 {
62     for (int i = 0; i < MAX_OBJECTS; i++) {
63         full_empire_object *full = &objects[i];
64         empire_object *obj = &full->obj;
65         obj->id = i;
66         obj->type = buffer_read_u8(buf);
67         full->in_use = buffer_read_u8(buf);
68         obj->animation_index = buffer_read_u8(buf);
69         buffer_skip(buf, 1);
70         obj->x = buffer_read_i16(buf);
71         obj->y = buffer_read_i16(buf);
72         obj->width = buffer_read_i16(buf);
73         obj->height = buffer_read_i16(buf);
74         obj->image_id = buffer_read_i16(buf);
75         obj->expanded.image_id = buffer_read_i16(buf);
76         buffer_skip(buf, 1);
77         obj->distant_battle_travel_months = buffer_read_u8(buf);
78         buffer_skip(buf, 2);
79         obj->expanded.x = buffer_read_i16(buf);
80         obj->expanded.y = buffer_read_i16(buf);
81         full->city_type = buffer_read_u8(buf);
82         full->city_name_id = buffer_read_u8(buf);
83         obj->trade_route_id = buffer_read_u8(buf);
84         full->trade_route_open = buffer_read_u8(buf);
85         full->trade_route_cost = buffer_read_i16(buf);
86         for (int r = 0; r < 10; r++) {
87             full->city_sells_resource[r] = buffer_read_u8(buf);
88         }
89         buffer_skip(buf, 2);
90         for (int r = 0; r < 8; r++) {
91             full->city_buys_resource[r] = buffer_read_u8(buf);
92         }
93         obj->invasion_path_id = buffer_read_u8(buf);
94         obj->invasion_years = buffer_read_u8(buf);
95         full->trade40 = buffer_read_u16(buf);
96         full->trade25 = buffer_read_u16(buf);
97         full->trade15 = buffer_read_u16(buf);
98         buffer_skip(buf, 6);
99     }
100 
101     fix_image_ids();
102 }
103 
empire_object_init_cities(void)104 void empire_object_init_cities(void)
105 {
106     empire_city_clear_all();
107     int route_index = 1;
108     for (int i = 0; i < MAX_OBJECTS; i++) {
109         if (!objects[i].in_use || objects[i].obj.type != EMPIRE_OBJECT_CITY) {
110             continue;
111         }
112         full_empire_object *obj = &objects[i];
113         empire_city *city = empire_city_get(route_index++);
114         city->in_use = 1;
115         city->type = obj->city_type;
116         city->name_id = obj->city_name_id;
117         if (obj->obj.trade_route_id < 0) {
118             obj->obj.trade_route_id = 0;
119         }
120         if (obj->obj.trade_route_id >= 20) {
121             obj->obj.trade_route_id = 19;
122         }
123         city->route_id = obj->obj.trade_route_id;
124         city->is_open = obj->trade_route_open;
125         city->cost_to_open = obj->trade_route_cost;
126         city->is_sea_trade = is_sea_trade_route(obj->obj.trade_route_id);
127 
128         for (int resource = RESOURCE_MIN; resource < RESOURCE_MAX; resource++) {
129             city->sells_resource[resource] = 0;
130             city->buys_resource[resource] = 0;
131             if (city->type == EMPIRE_CITY_DISTANT_ROMAN
132                 || city->type == EMPIRE_CITY_DISTANT_FOREIGN
133                 || city->type == EMPIRE_CITY_VULNERABLE_ROMAN
134                 || city->type == EMPIRE_CITY_FUTURE_ROMAN) {
135                 continue;
136             }
137             if (empire_object_city_sells_resource(i, resource)) {
138                 city->sells_resource[resource] = 1;
139             }
140             if (empire_object_city_buys_resource(i, resource)) {
141                 city->buys_resource[resource] = 1;
142             }
143             int amount;
144             switch (get_trade_amount_code(i, resource)) {
145                 case 1: amount = 15; break;
146                 case 2: amount = 25; break;
147                 case 3: amount = 40; break;
148                 default: amount = 0; break;
149             }
150             trade_route_init(city->route_id, resource, amount);
151         }
152         city->trader_entry_delay = 4;
153         city->trader_figure_ids[0] = 0;
154         city->trader_figure_ids[1] = 0;
155         city->trader_figure_ids[2] = 0;
156         city->empire_object_id = i;
157     }
158 }
159 
empire_object_init_distant_battle_travel_months(int object_type)160 int empire_object_init_distant_battle_travel_months(int object_type)
161 {
162     int month = 0;
163     for (int i = 0; i < MAX_OBJECTS; i++) {
164         if (objects[i].in_use && objects[i].obj.type == object_type) {
165             month++;
166             objects[i].obj.distant_battle_travel_months = month;
167         }
168     }
169     return month;
170 }
171 
empire_object_get(int object_id)172 const empire_object *empire_object_get(int object_id)
173 {
174     return &objects[object_id].obj;
175 }
176 
empire_object_get_our_city(void)177 const empire_object *empire_object_get_our_city(void)
178 {
179     for (int i = 0; i < MAX_OBJECTS; i++) {
180         if (objects[i].in_use) {
181             const empire_object *obj = &objects[i].obj;
182             if (obj->type == EMPIRE_OBJECT_CITY && objects[i].city_type == EMPIRE_CITY_OURS) {
183                 return obj;
184             }
185         }
186     }
187     return 0;
188 }
189 
empire_object_foreach(void (* callback)(const empire_object *))190 void empire_object_foreach(void (*callback)(const empire_object *))
191 {
192     for (int i = 0; i < MAX_OBJECTS; i++) {
193         if (objects[i].in_use) {
194             callback(&objects[i].obj);
195         }
196     }
197 }
198 
empire_object_get_battle_icon(int path_id,int year)199 const empire_object *empire_object_get_battle_icon(int path_id, int year)
200 {
201     for (int i = 0; i < MAX_OBJECTS; i++) {
202         if (objects[i].in_use) {
203             empire_object *obj = &objects[i].obj;
204             if (obj->type == EMPIRE_OBJECT_BATTLE_ICON &&
205                 obj->invasion_path_id == path_id && obj->invasion_years == year) {
206                 return obj;
207             }
208         }
209     }
210     return 0;
211 }
212 
empire_object_get_max_invasion_path(void)213 int empire_object_get_max_invasion_path(void)
214 {
215     int max_path = 0;
216     for (int i = 0; i < MAX_OBJECTS; i++) {
217         if (objects[i].in_use && objects[i].obj.type == EMPIRE_OBJECT_BATTLE_ICON) {
218             if (objects[i].obj.invasion_path_id > max_path) {
219                 max_path = objects[i].obj.invasion_path_id;
220             }
221         }
222     }
223     return max_path;
224 }
225 
empire_object_get_closest(int x,int y)226 int empire_object_get_closest(int x, int y)
227 {
228     int min_dist = 10000;
229     int min_obj_id = 0;
230     for (int i = 0; i < MAX_OBJECTS && objects[i].in_use; i++) {
231         const empire_object *obj = &objects[i].obj;
232         int obj_x, obj_y;
233         if (scenario_empire_is_expanded()) {
234             obj_x = obj->expanded.x;
235             obj_y = obj->expanded.y;
236         } else {
237             obj_x = obj->x;
238             obj_y = obj->y;
239         }
240         if (obj_x - 8 > x || obj_x + obj->width + 8 <= x) {
241             continue;
242         }
243         if (obj_y - 8 > y || obj_y + obj->height + 8 <= y) {
244             continue;
245         }
246         int dist = calc_maximum_distance(x, y, obj_x + obj->width / 2, obj_y + obj->height / 2);
247         if (dist < min_dist) {
248             min_dist = dist;
249             min_obj_id = i + 1;
250         }
251     }
252     return min_obj_id;
253 }
254 
empire_object_set_expanded(int object_id,int new_city_type)255 void empire_object_set_expanded(int object_id, int new_city_type)
256 {
257     objects[object_id].city_type = new_city_type;
258     if (new_city_type == EMPIRE_CITY_TRADE) {
259         objects[object_id].obj.expanded.image_id = image_group(GROUP_EMPIRE_CITY_TRADE);
260     } else if (new_city_type == EMPIRE_CITY_DISTANT_ROMAN) {
261         objects[object_id].obj.expanded.image_id = image_group(GROUP_EMPIRE_CITY_DISTANT_ROMAN);
262     }
263 }
264 
empire_object_city_buys_resource(int object_id,int resource)265 int empire_object_city_buys_resource(int object_id, int resource)
266 {
267     const full_empire_object *object = &objects[object_id];
268     for (int i = 0; i < 8; i++) {
269         if (object->city_buys_resource[i] == resource) {
270             return 1;
271         }
272     }
273     return 0;
274 }
275 
empire_object_city_sells_resource(int object_id,int resource)276 int empire_object_city_sells_resource(int object_id, int resource)
277 {
278     const full_empire_object *object = &objects[object_id];
279     for (int i = 0; i < 10; i++) {
280         if (object->city_sells_resource[i] == resource) {
281             return 1;
282         }
283     }
284     return 0;
285 }
286 
is_trade_city(int index)287 static int is_trade_city(int index)
288 {
289     if (objects[index].obj.type != EMPIRE_OBJECT_CITY) {
290         return 0;
291     }
292     return objects[index].city_type > EMPIRE_CITY_OURS && objects[index].city_type < EMPIRE_CITY_FUTURE_ROMAN;
293 }
294 
get_trade_amount_code(int index,int resource)295 static int get_trade_amount_code(int index, int resource)
296 {
297     if (!is_trade_city(index)) {
298         return 0;
299     }
300     int resource_flag = 1 << resource;
301     if (objects[index].trade40 & resource_flag) {
302         return 3;
303     }
304     if (objects[index].trade25 & resource_flag) {
305         return 2;
306     }
307     if (objects[index].trade15 & resource_flag) {
308         return 1;
309     }
310     return 0;
311 }
312 
is_sea_trade_route(int route_id)313 static int is_sea_trade_route(int route_id)
314 {
315     for (int i = 0; i < MAX_OBJECTS; i++) {
316         if (objects[i].in_use && objects[i].obj.trade_route_id == route_id) {
317             if (objects[i].obj.type == EMPIRE_OBJECT_SEA_TRADE_ROUTE) {
318                 return 1;
319             }
320             if (objects[i].obj.type == EMPIRE_OBJECT_LAND_TRADE_ROUTE) {
321                 return 0;
322             }
323         }
324     }
325     return 0;
326 }
327 
get_animation_offset(int image_id,int current_index)328 static int get_animation_offset(int image_id, int current_index)
329 {
330     if (current_index <= 0) {
331         current_index = 1;
332     }
333     const image *img = image_get(image_id);
334     int animation_speed = img->animation_speed_id;
335     if (!game_animation_should_advance(animation_speed)) {
336         return current_index;
337     }
338     if (img->animation_can_reverse) {
339         int is_reverse = 0;
340         if (current_index & 0x80) {
341             is_reverse = 1;
342         }
343         int current_sprite = current_index & 0x7f;
344         if (is_reverse) {
345             current_index = current_sprite - 1;
346             if (current_index < 1) {
347                 current_index = 1;
348                 is_reverse = 0;
349             }
350         } else {
351             current_index = current_sprite + 1;
352             if (current_index > img->num_animation_sprites) {
353                 current_index = img->num_animation_sprites;
354                 is_reverse = 1;
355             }
356         }
357         if (is_reverse) {
358             current_index = current_index | 0x80;
359         }
360     } else {
361         // Absolutely normal case
362         current_index++;
363         if (current_index > img->num_animation_sprites) {
364             current_index = 1;
365         }
366     }
367     return current_index;
368 }
369 
empire_object_update_animation(const empire_object * obj,int image_id)370 int empire_object_update_animation(const empire_object *obj, int image_id)
371 {
372     return objects[obj->id].obj.animation_index = get_animation_offset(image_id, obj->animation_index);
373 }
374