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