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