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