1 #include "empire.h"
2 
3 #include "building/count.h"
4 #include "city/constants.h"
5 #include "city/population.h"
6 #include "city/resource.h"
7 #include "core/calc.h"
8 #include "core/log.h"
9 #include "core/io.h"
10 #include "empire/city.h"
11 #include "empire/object.h"
12 #include "empire/trade_route.h"
13 
14 #include <string.h>
15 
16 enum {
17     EMPIRE_WIDTH = 2000,
18     EMPIRE_HEIGHT = 1000,
19     EMPIRE_HEADER_SIZE = 1280,
20     EMPIRE_DATA_SIZE = 12800
21 };
22 
23 static struct {
24     int initial_scroll_x;
25     int initial_scroll_y;
26     int scroll_x;
27     int scroll_y;
28     int selected_object;
29     int viewport_width;
30     int viewport_height;
31 } data;
32 
empire_load(int is_custom_scenario,int empire_id)33 void empire_load(int is_custom_scenario, int empire_id)
34 {
35     char raw_data[EMPIRE_DATA_SIZE];
36     const char *filename = is_custom_scenario ? "c32.emp" : "c3.emp";
37 
38     // read header with scroll positions
39     if (!io_read_file_part_into_buffer(filename, NOT_LOCALIZED, raw_data, 4, 32 * empire_id)) {
40         memset(raw_data, 0, 4);
41     }
42     buffer buf;
43     buffer_init(&buf, raw_data, 4);
44     data.initial_scroll_x = buffer_read_i16(&buf);
45     data.initial_scroll_y = buffer_read_i16(&buf);
46 
47     // read data section with objects
48     int offset = EMPIRE_HEADER_SIZE + EMPIRE_DATA_SIZE * empire_id;
49     int read_size = io_read_file_part_into_buffer(filename, NOT_LOCALIZED, raw_data, EMPIRE_DATA_SIZE, offset);
50     if (read_size != EMPIRE_DATA_SIZE) {
51         // load empty empire when loading fails
52         log_error("Unable to load empire data from file", filename, 0);
53         memset(raw_data, 0, EMPIRE_DATA_SIZE);
54     }
55     buffer_init(&buf, raw_data, EMPIRE_DATA_SIZE);
56     empire_object_load(&buf);
57 }
58 
check_scroll_boundaries(void)59 static void check_scroll_boundaries(void)
60 {
61     int max_x = EMPIRE_WIDTH - data.viewport_width;
62     int max_y = EMPIRE_HEIGHT - data.viewport_height;
63 
64     data.scroll_x = calc_bound(data.scroll_x, 0, max_x);
65     data.scroll_y = calc_bound(data.scroll_y, 0, max_y);
66 }
67 
empire_load_editor(int empire_id,int viewport_width,int viewport_height)68 void empire_load_editor(int empire_id, int viewport_width, int viewport_height)
69 {
70     empire_load(1, empire_id);
71     empire_object_init_cities();
72 
73     const empire_object *our_city = empire_object_get_our_city();
74 
75     data.viewport_width = viewport_width;
76     data.viewport_height = viewport_height;
77     if (our_city) {
78         data.scroll_x = our_city->x - data.viewport_width / 2;
79         data.scroll_y = our_city->y - data.viewport_height / 2;
80     } else {
81         data.scroll_x = data.initial_scroll_x;
82         data.scroll_y = data.initial_scroll_y;
83     }
84     check_scroll_boundaries();
85 }
86 
empire_init_scenario(void)87 void empire_init_scenario(void)
88 {
89     data.scroll_x = data.initial_scroll_x;
90     data.scroll_y = data.initial_scroll_y;
91     data.viewport_width = EMPIRE_WIDTH;
92     data.viewport_height = EMPIRE_HEIGHT;
93 
94     empire_object_init_cities();
95 }
96 
empire_set_viewport(int width,int height)97 void empire_set_viewport(int width, int height)
98 {
99     data.viewport_width = width;
100     data.viewport_height = height;
101     check_scroll_boundaries();
102 }
103 
empire_adjust_scroll(int * x_offset,int * y_offset)104 void empire_adjust_scroll(int *x_offset, int *y_offset)
105 {
106     *x_offset = *x_offset - data.scroll_x;
107     *y_offset = *y_offset - data.scroll_y;
108 }
109 
empire_scroll_map(int x,int y)110 void empire_scroll_map(int x, int y)
111 {
112     data.scroll_x += x;
113     data.scroll_y += y;
114     check_scroll_boundaries();
115 }
116 
empire_selected_object(void)117 int empire_selected_object(void)
118 {
119     return data.selected_object;
120 }
121 
empire_clear_selected_object(void)122 void empire_clear_selected_object(void)
123 {
124     data.selected_object = 0;
125 }
126 
empire_select_object(int x,int y)127 void empire_select_object(int x, int y)
128 {
129     int map_x = x + data.scroll_x;
130     int map_y = y + data.scroll_y;
131 
132     data.selected_object = empire_object_get_closest(map_x, map_y);
133 }
134 
empire_can_export_resource_to_city(int city_id,int resource)135 int empire_can_export_resource_to_city(int city_id, int resource)
136 {
137     empire_city *city = empire_city_get(city_id);
138     if (city_id && trade_route_limit_reached(city->route_id, resource)) {
139         // quota reached
140         return 0;
141     }
142     if (city_resource_count(resource) <= city_resource_export_over(resource)) {
143         // stocks too low
144         return 0;
145     }
146     if (city_id == 0 || city->buys_resource[resource]) {
147         return city_resource_trade_status(resource) == TRADE_STATUS_EXPORT;
148     } else {
149         return 0;
150     }
151 }
152 
get_max_stock_for_population(void)153 static int get_max_stock_for_population(void)
154 {
155     int population = city_population();
156     if (population < 2000) {
157         return 10;
158     } else if (population < 4000) {
159         return 20;
160     } else if (population < 6000) {
161         return 30;
162     } else {
163         return 40;
164     }
165 }
166 
empire_can_import_resource_from_city(int city_id,int resource)167 int empire_can_import_resource_from_city(int city_id, int resource)
168 {
169     empire_city *city = empire_city_get(city_id);
170     if (!city->sells_resource[resource]) {
171         return 0;
172     }
173     if (city_resource_trade_status(resource) != TRADE_STATUS_IMPORT) {
174         return 0;
175     }
176     if (trade_route_limit_reached(city->route_id, resource)) {
177         return 0;
178     }
179 
180     int in_stock = city_resource_count(resource);
181     int max_in_stock = 0;
182     int finished_good = RESOURCE_NONE;
183     switch (resource) {
184         // food and finished materials
185         case RESOURCE_WHEAT:
186         case RESOURCE_VEGETABLES:
187         case RESOURCE_FRUIT:
188         case RESOURCE_MEAT:
189         case RESOURCE_POTTERY:
190         case RESOURCE_FURNITURE:
191         case RESOURCE_OIL:
192         case RESOURCE_WINE:
193             max_in_stock = get_max_stock_for_population();
194             break;
195 
196         case RESOURCE_MARBLE:
197         case RESOURCE_WEAPONS:
198             max_in_stock = 10;
199             break;
200 
201         case RESOURCE_CLAY:
202             finished_good = RESOURCE_POTTERY;
203             break;
204         case RESOURCE_TIMBER:
205             finished_good = RESOURCE_FURNITURE;
206             break;
207         case RESOURCE_OLIVES:
208             finished_good = RESOURCE_OIL;
209             break;
210         case RESOURCE_VINES:
211             finished_good = RESOURCE_WINE;
212             break;
213         case RESOURCE_IRON:
214             finished_good = RESOURCE_WEAPONS;
215             break;
216     }
217     if (finished_good) {
218         max_in_stock = 2 + 2 * building_count_industry_active(finished_good);
219     }
220     return in_stock < max_in_stock ? 1 : 0;
221 }
222 
empire_save_state(buffer * buf)223 void empire_save_state(buffer *buf)
224 {
225     buffer_write_i32(buf, data.scroll_x);
226     buffer_write_i32(buf, data.scroll_y);
227     buffer_write_i32(buf, data.selected_object);
228 }
229 
empire_load_state(buffer * buf)230 void empire_load_state(buffer *buf)
231 {
232     data.scroll_x = buffer_read_i32(buf);
233     data.scroll_y = buffer_read_i32(buf);
234     data.selected_object = buffer_read_i32(buf);
235 }
236