1 #include "population.h"
2 
3 #include "building/building.h"
4 #include "building/house_population.h"
5 #include "city/data_private.h"
6 #include "core/calc.h"
7 #include "core/config.h"
8 #include "core/random.h"
9 
10 static const int BIRTHS_PER_AGE_DECENNIUM[10] = {
11     0, 3, 16, 9, 2, 0, 0, 0, 0, 0
12 };
13 
14 static const int DEATHS_PER_HEALTH_PER_AGE_DECENNIUM[11][10] = {
15     {20, 10, 5, 10, 20, 30, 50, 85, 100, 100},
16     {15, 8, 4, 8, 16, 25, 45, 70, 90, 100},
17     {10, 6, 2, 6, 12, 20, 30, 55, 80, 90},
18     {5, 4, 0, 4, 8, 15, 25, 40, 65, 80},
19     {3, 2, 0, 2, 6, 12, 20, 30, 50, 70},
20     {2, 0, 0, 0, 4, 8, 15, 25, 40, 60},
21     {1, 0, 0, 0, 2, 6, 12, 20, 30, 50},
22     {0, 0, 0, 0, 0, 4, 8, 15, 20, 40},
23     {0, 0, 0, 0, 0, 2, 6, 10, 15, 30},
24     {0, 0, 0, 0, 0, 0, 4, 5, 10, 20},
25     {0, 0, 0, 0, 0, 0, 0, 2, 5, 10}
26 };
27 
city_population(void)28 int city_population(void)
29 {
30     return city_data.population.population;
31 }
32 
city_population_school_age(void)33 int city_population_school_age(void)
34 {
35     return city_data.population.school_age;
36 }
37 
city_population_academy_age(void)38 int city_population_academy_age(void)
39 {
40     return city_data.population.academy_age;
41 }
42 
city_population_last_used_house_add(void)43 int city_population_last_used_house_add(void)
44 {
45     return city_data.population.last_used_house_add;
46 }
47 
city_population_set_last_used_house_add(int building_id)48 void city_population_set_last_used_house_add(int building_id)
49 {
50     city_data.population.last_used_house_add = building_id;
51 }
52 
city_population_last_used_house_remove(void)53 int city_population_last_used_house_remove(void)
54 {
55     return city_data.population.last_used_house_remove;
56 }
57 
city_population_set_last_used_house_remove(int building_id)58 void city_population_set_last_used_house_remove(int building_id)
59 {
60     city_data.population.last_used_house_remove = building_id;
61 }
62 
city_population_clear_capacity(void)63 void city_population_clear_capacity(void)
64 {
65     city_data.population.total_capacity = 0;
66     city_data.population.room_in_houses = 0;
67 }
68 
city_population_add_capacity(int people_in_house,int max_people)69 void city_population_add_capacity(int people_in_house, int max_people)
70 {
71     city_data.population.total_capacity += max_people;
72     city_data.population.room_in_houses += max_people - people_in_house;
73 }
74 
recalculate_population(void)75 static void recalculate_population(void)
76 {
77     city_data.population.population = 0;
78     for (int i = 0; i < 100; i++) {
79         city_data.population.population += city_data.population.at_age[i];
80     }
81     if (city_data.population.population > city_data.population.highest_ever) {
82         city_data.population.highest_ever = city_data.population.population;
83     }
84 }
85 
add_to_census(int num_people)86 static void add_to_census(int num_people)
87 {
88     int odd = 0;
89     int index = 0;
90     for (int i = 0; i < num_people; i++, odd = 1 - odd) {
91         int age = random_from_pool(index++) & 0x3f; // 63
92         if (age > 50) {
93             age -= 30;
94         } else if (age < 10 && odd) {
95             age += 20;
96         }
97         city_data.population.at_age[age]++;
98     }
99 }
100 
remove_from_census(int num_people)101 static void remove_from_census(int num_people)
102 {
103     int index = 0;
104     int empty_buckets = 0;
105     // remove people randomly up to age 63
106     while (num_people > 0 && empty_buckets < 100) {
107         int age = random_from_pool(index++) & 0x3f;
108         if (city_data.population.at_age[age] <= 0) {
109             empty_buckets++;
110         } else {
111             city_data.population.at_age[age]--;
112             num_people--;
113             empty_buckets = 0;
114         }
115     }
116     // if random didn't work: remove from age 10 and up
117     empty_buckets = 0;
118     int age = 10;
119     while (num_people > 0 && empty_buckets < 100) {
120         if (city_data.population.at_age[age] <= 0) {
121             empty_buckets++;
122         } else {
123             city_data.population.at_age[age]--;
124             num_people--;
125             empty_buckets = 0;
126         }
127         age++;
128         if (age >= 100) {
129             age = 0;
130         }
131     }
132 }
133 
remove_from_census_in_age_decennium(int decennium,int num_people)134 static void remove_from_census_in_age_decennium(int decennium, int num_people)
135 {
136     int empty_buckets = 0;
137     int age = 0;
138     while (num_people > 0 && empty_buckets < 10) {
139         if (city_data.population.at_age[10 * decennium + age] <= 0) {
140             empty_buckets++;
141         } else {
142             city_data.population.at_age[10 * decennium + age]--;
143             num_people--;
144             empty_buckets = 0;
145         }
146         age++;
147         if (age >= 10) {
148             age = 0;
149         }
150     }
151 }
152 
get_people_in_age_decennium(int decennium)153 static int get_people_in_age_decennium(int decennium)
154 {
155     int pop = 0;
156     for (int i = 0; i < 10; i++) {
157         pop += city_data.population.at_age[10 * decennium + i];
158     }
159     return pop;
160 }
161 
city_population_add(int num_people)162 void city_population_add(int num_people)
163 {
164     city_data.population.last_change = num_people;
165     add_to_census(num_people);
166     recalculate_population();
167 }
168 
city_population_remove(int num_people)169 void city_population_remove(int num_people)
170 {
171     city_data.population.last_change = -num_people;
172     remove_from_census(num_people);
173     recalculate_population();
174 }
175 
city_population_add_homeless(int num_people)176 void city_population_add_homeless(int num_people)
177 {
178     city_data.population.lost_homeless -= num_people;
179     add_to_census(num_people);
180     recalculate_population();
181 }
182 
city_population_remove_homeless(int num_people)183 void city_population_remove_homeless(int num_people)
184 {
185     city_data.population.lost_homeless += num_people;
186     remove_from_census(num_people);
187     recalculate_population();
188 }
189 
city_population_remove_home_removed(int num_people)190 void city_population_remove_home_removed(int num_people)
191 {
192     city_data.population.lost_removal += num_people;
193     remove_from_census(num_people);
194     recalculate_population();
195 }
196 
city_population_remove_for_troop_request(int num_people)197 void city_population_remove_for_troop_request(int num_people)
198 {
199     int removed = house_population_remove_from_city(num_people);
200     remove_from_census(removed);
201     city_data.population.lost_troop_request += num_people;
202     recalculate_population();
203 }
204 
city_population_people_of_working_age(void)205 int city_population_people_of_working_age(void)
206 {
207     return
208         get_people_in_age_decennium(2) +
209         get_people_in_age_decennium(3) +
210         get_people_in_age_decennium(4);
211 }
212 
get_people_aged_between(int min,int max)213 static int get_people_aged_between(int min, int max)
214 {
215     int pop = 0;
216     for (int i = min; i < max; i++) {
217         pop += city_data.population.at_age[i];
218     }
219     return pop;
220 }
221 
city_population_calculate_educational_age(void)222 void city_population_calculate_educational_age(void)
223 {
224     city_data.population.school_age = get_people_aged_between(0, 14);
225     city_data.population.academy_age = get_people_aged_between(14, 21);
226 }
227 
city_population_record_monthly(void)228 void city_population_record_monthly(void)
229 {
230     city_data.population.monthly.values[city_data.population.monthly.next_index++] = city_data.population.population;
231     if (city_data.population.monthly.next_index >= 2400) {
232         city_data.population.monthly.next_index = 0;
233     }
234     ++city_data.population.monthly.count;
235 }
236 
city_population_monthly_count(void)237 int city_population_monthly_count(void)
238 {
239     return city_data.population.monthly.count;
240 }
241 
city_population_at_month(int max_months,int month)242 int city_population_at_month(int max_months, int month)
243 {
244     int start_offset = 0;
245     if (city_data.population.monthly.count > max_months) {
246         start_offset = city_data.population.monthly.count + 2400 - max_months;
247     }
248     int index = (start_offset + month) % 2400;
249     return city_data.population.monthly.values[index];
250 }
251 
city_population_at_age(int age)252 int city_population_at_age(int age)
253 {
254     return city_data.population.at_age[age];
255 }
256 
city_population_at_level(int house_level)257 int city_population_at_level(int house_level)
258 {
259     return city_data.population.at_level[house_level];
260 }
261 
yearly_advance_ages_and_calculate_deaths(void)262 static void yearly_advance_ages_and_calculate_deaths(void)
263 {
264     int aged100 = city_data.population.at_age[99];
265     for (int age = 99; age > 0; age--) {
266         city_data.population.at_age[age] = city_data.population.at_age[age-1];
267     }
268     city_data.population.at_age[0] = 0;
269     city_data.population.yearly_deaths = 0;
270     for (int decennium = 9; decennium >= 0; decennium--) {
271         int people = get_people_in_age_decennium(decennium);
272         int death_percentage = DEATHS_PER_HEALTH_PER_AGE_DECENNIUM[city_data.health.value / 10][decennium];
273         int deaths = calc_adjust_with_percentage(people, death_percentage);
274         int removed = house_population_remove_from_city(deaths + aged100);
275         if (config_get(CONFIG_GP_FIX_100_YEAR_GHOSTS)) {
276             remove_from_census_in_age_decennium(decennium, deaths);
277         } else {
278             // Original C3 removes both deaths and aged100, which creates "ghosts".
279             // It should be deaths only; now aged100 are removed from census while
280             // they weren't *in* the census anymore
281             remove_from_census_in_age_decennium(decennium, removed);
282         }
283         city_data.population.yearly_deaths += removed;
284         aged100 = 0;
285     }
286 }
287 
yearly_calculate_births(void)288 static void yearly_calculate_births(void)
289 {
290     city_data.population.yearly_births = 0;
291     for (int decennium = 9; decennium >= 0; decennium--) {
292         int people = get_people_in_age_decennium(decennium);
293         int births = calc_adjust_with_percentage(people, BIRTHS_PER_AGE_DECENNIUM[decennium]);
294         int added = house_population_add_to_city(births);
295         city_data.population.at_age[0] += added;
296         city_data.population.yearly_births += added;
297     }
298 }
299 
yearly_recalculate_population(void)300 static void yearly_recalculate_population(void)
301 {
302     city_data.population.yearly_update_requested = 0;
303     city_data.population.population_last_year = city_data.population.population;
304     recalculate_population();
305 
306     city_data.population.lost_removal = 0;
307     city_data.population.total_all_years += city_data.population.population;
308     city_data.population.total_years++;
309     city_data.population.average_per_year = city_data.population.total_all_years / city_data.population.total_years;
310 }
311 
calculate_people_per_house_type(void)312 static int calculate_people_per_house_type(void)
313 {
314     city_data.population.people_in_tents_shacks = 0;
315     city_data.population.people_in_villas_palaces = 0;
316     city_data.population.people_in_tents = 0;
317     city_data.population.people_in_large_insula_and_above = 0;
318     int total = 0;
319     for (int i = 1; i < MAX_BUILDINGS; i++) {
320         building *b = building_get(i);
321         if (b->state == BUILDING_STATE_UNUSED ||
322             b->state == BUILDING_STATE_UNDO ||
323             b->state == BUILDING_STATE_DELETED_BY_GAME ||
324             b->state == BUILDING_STATE_DELETED_BY_PLAYER) {
325             continue;
326         }
327         if (b->house_size) {
328             int pop = b->house_population;
329             total += pop;
330             if (b->subtype.house_level <= HOUSE_LARGE_TENT) {
331                 city_data.population.people_in_tents += pop;
332             }
333             if (b->subtype.house_level <= HOUSE_LARGE_SHACK) {
334                 city_data.population.people_in_tents_shacks += pop;
335             }
336             if (b->subtype.house_level >= HOUSE_LARGE_INSULA) {
337                 city_data.population.people_in_large_insula_and_above += pop;
338             }
339             if (b->subtype.house_level >= HOUSE_SMALL_VILLA) {
340                 city_data.population.people_in_villas_palaces += pop;
341             }
342         }
343     }
344     return total;
345 }
346 
city_population_request_yearly_update(void)347 void city_population_request_yearly_update(void)
348 {
349     city_data.population.yearly_update_requested = 1;
350     calculate_people_per_house_type();
351 }
352 
city_population_yearly_update(void)353 void city_population_yearly_update(void)
354 {
355     if (city_data.population.yearly_update_requested) {
356         yearly_advance_ages_and_calculate_deaths();
357         yearly_calculate_births();
358         yearly_recalculate_population();
359     }
360 }
361 
city_population_check_consistency(void)362 void city_population_check_consistency(void)
363 {
364     int people_in_houses = calculate_people_per_house_type();
365     if (people_in_houses < city_data.population.population) {
366         remove_from_census(city_data.population.population - people_in_houses);
367     }
368 }
369 
city_population_graph_order(void)370 int city_population_graph_order(void)
371 {
372     return city_data.population.graph_order;
373 }
374 
city_population_set_graph_order(int order)375 void city_population_set_graph_order(int order)
376 {
377     city_data.population.graph_order = order;
378 }
379