1 #include "service.h"
2 
3 #include "building/building.h"
4 #include "building/model.h"
5 #include "figuretype/crime.h"
6 #include "game/resource.h"
7 #include "map/building.h"
8 #include "map/grid.h"
9 
10 #define MAX_COVERAGE 96
11 
provide_culture(int x,int y,void (* callback)(building *))12 static int provide_culture(int x, int y, void (*callback)(building *))
13 {
14     int serviced = 0;
15     int x_min, y_min, x_max, y_max;
16     map_grid_get_area(x, y, 1, 2, &x_min, &y_min, &x_max, &y_max);
17     for (int yy = y_min; yy <= y_max; yy++) {
18         for (int xx = x_min; xx <= x_max; xx++) {
19             int grid_offset = map_grid_offset(xx, yy);
20             int building_id = map_building_at(grid_offset);
21             if (building_id) {
22                 building *b = building_get(building_id);
23                 if (b->house_size && b->house_population > 0) {
24                     callback(b);
25                     serviced++;
26                 }
27             }
28         }
29     }
30     return serviced;
31 }
32 
provide_entertainment(int x,int y,int shows,void (* callback)(building *,int))33 static int provide_entertainment(int x, int y, int shows, void (*callback)(building *, int))
34 {
35     int serviced = 0;
36     int x_min, y_min, x_max, y_max;
37     map_grid_get_area(x, y, 1, 2, &x_min, &y_min, &x_max, &y_max);
38     for (int yy = y_min; yy <= y_max; yy++) {
39         for (int xx = x_min; xx <= x_max; xx++) {
40             int grid_offset = map_grid_offset(xx, yy);
41             int building_id = map_building_at(grid_offset);
42             if (building_id) {
43                 building *b = building_get(building_id);
44                 if (b->house_size && b->house_population > 0) {
45                     callback(b, shows);
46                     serviced++;
47                 }
48             }
49         }
50     }
51     return serviced;
52 }
53 
labor_seeker_coverage(building * b)54 static void labor_seeker_coverage(building *b)
55 {
56 }
57 
theater_coverage(building * b)58 static void theater_coverage(building *b)
59 {
60     b->data.house.theater = MAX_COVERAGE;
61 }
62 
amphitheater_coverage(building * b,int shows)63 static void amphitheater_coverage(building *b, int shows)
64 {
65     b->data.house.amphitheater_actor = MAX_COVERAGE;
66     if (shows == 2) {
67         b->data.house.amphitheater_gladiator = MAX_COVERAGE;
68     }
69 }
70 
colosseum_coverage(building * b,int shows)71 static void colosseum_coverage(building *b, int shows)
72 {
73     b->data.house.colosseum_gladiator = MAX_COVERAGE;
74     if (shows == 2) {
75         b->data.house.colosseum_lion = MAX_COVERAGE;
76     }
77 }
78 
hippodrome_coverage(building * b)79 static void hippodrome_coverage(building *b)
80 {
81     b->data.house.hippodrome = MAX_COVERAGE;
82 }
83 
bathhouse_coverage(building * b)84 static void bathhouse_coverage(building *b)
85 {
86     b->data.house.bathhouse = MAX_COVERAGE;
87 }
88 
religion_coverage_ceres(building * b)89 static void religion_coverage_ceres(building *b)
90 {
91     b->data.house.temple_ceres = MAX_COVERAGE;
92 }
93 
religion_coverage_neptune(building * b)94 static void religion_coverage_neptune(building *b)
95 {
96     b->data.house.temple_neptune = MAX_COVERAGE;
97 }
98 
religion_coverage_mercury(building * b)99 static void religion_coverage_mercury(building *b)
100 {
101     b->data.house.temple_mercury = MAX_COVERAGE;
102 }
103 
religion_coverage_mars(building * b)104 static void religion_coverage_mars(building *b)
105 {
106     b->data.house.temple_mars = MAX_COVERAGE;
107 }
108 
religion_coverage_venus(building * b)109 static void religion_coverage_venus(building *b)
110 {
111     b->data.house.temple_venus = MAX_COVERAGE;
112 }
113 
school_coverage(building * b)114 static void school_coverage(building *b)
115 {
116     b->data.house.school = MAX_COVERAGE;
117 }
118 
academy_coverage(building * b)119 static void academy_coverage(building *b)
120 {
121     b->data.house.academy = MAX_COVERAGE;
122 }
123 
library_coverage(building * b)124 static void library_coverage(building *b)
125 {
126     b->data.house.library = MAX_COVERAGE;
127 }
128 
barber_coverage(building * b)129 static void barber_coverage(building *b)
130 {
131     b->data.house.barber = MAX_COVERAGE;
132 }
133 
clinic_coverage(building * b)134 static void clinic_coverage(building *b)
135 {
136     b->data.house.clinic = MAX_COVERAGE;
137 }
138 
hospital_coverage(building * b)139 static void hospital_coverage(building *b)
140 {
141     b->data.house.hospital = MAX_COVERAGE;
142 }
143 
provide_missionary_coverage(int x,int y)144 static int provide_missionary_coverage(int x, int y)
145 {
146     int x_min, y_min, x_max, y_max;
147     map_grid_get_area(x, y, 1, 4, &x_min, &y_min, &x_max, &y_max);
148     for (int yy = y_min; yy <= y_max; yy++) {
149         for (int xx = x_min; xx <= x_max; xx++) {
150             int building_id = map_building_at(map_grid_offset(xx, yy));
151             if (building_id) {
152                 building *b = building_get(building_id);
153                 if (b->type == BUILDING_NATIVE_HUT || b->type == BUILDING_NATIVE_MEETING) {
154                     b->sentiment.native_anger = 0;
155                 }
156             }
157         }
158     }
159     return 1;
160 }
161 
provide_service(int x,int y,int * data,void (* callback)(building *,int *))162 static int provide_service(int x, int y, int *data, void (*callback)(building *, int *))
163 {
164     int serviced = 0;
165     int x_min, y_min, x_max, y_max;
166     map_grid_get_area(x, y, 1, 2, &x_min, &y_min, &x_max, &y_max);
167     for (int yy = y_min; yy <= y_max; yy++) {
168         for (int xx = x_min; xx <= x_max; xx++) {
169             int grid_offset = map_grid_offset(xx, yy);
170             int building_id = map_building_at(grid_offset);
171             if (building_id) {
172                 building *b = building_get(building_id);
173                 callback(b, data);
174                 if (b->house_size && b->house_population > 0) {
175                     serviced++;
176                 }
177             }
178         }
179     }
180     return serviced;
181 }
182 
engineer_coverage(building * b,int * max_damage_seen)183 static void engineer_coverage(building *b, int *max_damage_seen)
184 {
185     if (b->type == BUILDING_HIPPODROME) {
186         b = building_main(b);
187     }
188     if (b->damage_risk > *max_damage_seen) {
189         *max_damage_seen = b->damage_risk;
190     }
191     b->damage_risk = 0;
192 }
193 
prefect_coverage(building * b,int * min_happiness_seen)194 static void prefect_coverage(building *b, int *min_happiness_seen)
195 {
196     if (b->type == BUILDING_HIPPODROME) {
197         b = building_main(b);
198     }
199     b->fire_risk = 0;
200     if (b->sentiment.house_happiness < *min_happiness_seen) {
201         *min_happiness_seen = b->sentiment.house_happiness;
202     }
203 }
204 
tax_collector_coverage(building * b,int * max_tax_multiplier)205 static void tax_collector_coverage(building *b, int *max_tax_multiplier)
206 {
207     if (b->house_size && b->house_population > 0) {
208         int tax_multiplier = model_get_house(b->subtype.house_level)->tax_multiplier;
209         if (tax_multiplier > *max_tax_multiplier) {
210             *max_tax_multiplier = tax_multiplier;
211         }
212         b->house_tax_coverage = 50;
213     }
214 }
215 
distribute_good(building * b,building * market,int stock_wanted,int inventory_resource)216 static void distribute_good(building *b, building *market, int stock_wanted, int inventory_resource)
217 {
218     int amount_wanted = stock_wanted - b->data.house.inventory[inventory_resource];
219     if (market->data.market.inventory[inventory_resource] > 0 && amount_wanted > 0) {
220         if (amount_wanted <= market->data.market.inventory[inventory_resource]) {
221             b->data.house.inventory[inventory_resource] += amount_wanted;
222             market->data.market.inventory[inventory_resource] -= amount_wanted;
223         } else {
224             b->data.house.inventory[inventory_resource] += market->data.market.inventory[inventory_resource];
225             market->data.market.inventory[inventory_resource] = 0;
226         }
227     }
228 }
229 
distribute_market_resources(building * b,building * market)230 static void distribute_market_resources(building *b, building *market)
231 {
232     int level = b->subtype.house_level;
233     if (level < HOUSE_LUXURY_PALACE) {
234         level++;
235     }
236     int max_food_stocks = 4 * b->house_highest_population;
237     int food_types_stored_max = 0;
238     for (int i = INVENTORY_MIN_FOOD; i < INVENTORY_MAX_FOOD; i++) {
239         if (b->data.house.inventory[i] >= max_food_stocks) {
240             food_types_stored_max++;
241         }
242     }
243     const model_house *model = model_get_house(level);
244     if (model->food_types > food_types_stored_max) {
245         for (int i = INVENTORY_MIN_FOOD; i < INVENTORY_MAX_FOOD; i++) {
246             if (b->data.house.inventory[i] >= max_food_stocks) {
247                 continue;
248             }
249             if (market->data.market.inventory[i] >= max_food_stocks) {
250                 b->data.house.inventory[i] += max_food_stocks;
251                 market->data.market.inventory[i] -= max_food_stocks;
252                 break;
253             } else if (market->data.market.inventory[i]) {
254                 b->data.house.inventory[i] += market->data.market.inventory[i];
255                 market->data.market.inventory[i] = 0;
256                 break;
257             }
258         }
259     }
260     if (model->pottery) {
261         market->data.market.pottery_demand = 10;
262         distribute_good(b, market, 8 * model->pottery, INVENTORY_POTTERY);
263     }
264     if (model->furniture) {
265         market->data.market.furniture_demand = 10;
266         distribute_good(b, market, 4 * model->furniture, INVENTORY_FURNITURE);
267     }
268     if (model->oil) {
269         market->data.market.oil_demand = 10;
270         distribute_good(b, market, 4 * model->oil, INVENTORY_OIL);
271     }
272     if (model->wine) {
273         market->data.market.wine_demand = 10;
274         distribute_good(b, market, 4 * model->wine, INVENTORY_WINE);
275     }
276 }
277 
provide_market_goods(int market_building_id,int x,int y)278 static int provide_market_goods(int market_building_id, int x, int y)
279 {
280     int serviced = 0;
281     building *market = building_get(market_building_id);
282     int x_min, y_min, x_max, y_max;
283     map_grid_get_area(x, y, 1, 2, &x_min, &y_min, &x_max, &y_max);
284     for (int yy = y_min; yy <= y_max; yy++) {
285         for (int xx = x_min; xx <= x_max; xx++) {
286             int grid_offset = map_grid_offset(xx, yy);
287             int building_id = map_building_at(grid_offset);
288             if (building_id) {
289                 building *b = building_get(building_id);
290                 if (b->house_size && b->house_population > 0) {
291                     distribute_market_resources(b, market);
292                     serviced++;
293                 }
294             }
295         }
296     }
297     return serviced;
298 }
299 
get_entertainment_building(const figure * f)300 static building *get_entertainment_building(const figure *f)
301 {
302     if (f->action_state == FIGURE_ACTION_94_ENTERTAINER_ROAMING ||
303         f->action_state == FIGURE_ACTION_95_ENTERTAINER_RETURNING) {
304         return building_get(f->building_id);
305     } else { // going to venue
306         return building_get(f->destination_building_id);
307     }
308 }
309 
figure_service_provide_coverage(figure * f)310 int figure_service_provide_coverage(figure *f)
311 {
312     int houses_serviced = 0;
313     int x = f->x;
314     int y = f->y;
315     building *b;
316     switch (f->type) {
317         case FIGURE_PATRICIAN:
318             return 0;
319         case FIGURE_LABOR_SEEKER:
320             houses_serviced = provide_culture(x, y, labor_seeker_coverage);
321             break;
322         case FIGURE_TAX_COLLECTOR: {
323             int max_tax_rate = 0;
324             houses_serviced = provide_service(x, y, &max_tax_rate, tax_collector_coverage);
325             f->min_max_seen = max_tax_rate;
326             break;
327         }
328         case FIGURE_MARKET_TRADER:
329         case FIGURE_MARKET_BUYER:
330             houses_serviced = provide_market_goods(f->building_id, x, y);
331             break;
332         case FIGURE_BATHHOUSE_WORKER:
333             houses_serviced = provide_culture(x, y, bathhouse_coverage);
334             break;
335         case FIGURE_SCHOOL_CHILD:
336             houses_serviced = provide_culture(x, y, school_coverage);
337             break;
338         case FIGURE_TEACHER:
339             houses_serviced = provide_culture(x, y, academy_coverage);
340             break;
341         case FIGURE_LIBRARIAN:
342             houses_serviced = provide_culture(x, y, library_coverage);
343             break;
344         case FIGURE_BARBER:
345             houses_serviced = provide_culture(x, y, barber_coverage);
346             break;
347         case FIGURE_DOCTOR:
348             houses_serviced = provide_culture(x, y, clinic_coverage);
349             break;
350         case FIGURE_SURGEON:
351             houses_serviced = provide_culture(x, y, hospital_coverage);
352             break;
353         case FIGURE_MISSIONARY:
354             houses_serviced = provide_missionary_coverage(x, y);
355             break;
356         case FIGURE_PRIEST:
357             switch (building_get(f->building_id)->type) {
358                 case BUILDING_SMALL_TEMPLE_CERES:
359                 case BUILDING_LARGE_TEMPLE_CERES:
360                     houses_serviced = provide_culture(x, y, religion_coverage_ceres);
361                     break;
362                 case BUILDING_SMALL_TEMPLE_NEPTUNE:
363                 case BUILDING_LARGE_TEMPLE_NEPTUNE:
364                     houses_serviced = provide_culture(x, y, religion_coverage_neptune);
365                     break;
366                 case BUILDING_SMALL_TEMPLE_MERCURY:
367                 case BUILDING_LARGE_TEMPLE_MERCURY:
368                     houses_serviced = provide_culture(x, y, religion_coverage_mercury);
369                     break;
370                 case BUILDING_SMALL_TEMPLE_MARS:
371                 case BUILDING_LARGE_TEMPLE_MARS:
372                     houses_serviced = provide_culture(x, y, religion_coverage_mars);
373                     break;
374                 case BUILDING_SMALL_TEMPLE_VENUS:
375                 case BUILDING_LARGE_TEMPLE_VENUS:
376                     houses_serviced = provide_culture(x, y, religion_coverage_venus);
377                     break;
378                 default:
379                     break;
380             }
381             break;
382         case FIGURE_ACTOR:
383             b = get_entertainment_building(f);
384             if (b->type == BUILDING_THEATER) {
385                 houses_serviced = provide_culture(x, y, theater_coverage);
386             } else if (b->type == BUILDING_AMPHITHEATER) {
387                 houses_serviced = provide_entertainment(x, y,
388                     b->data.entertainment.days1 ? 2 : 1, amphitheater_coverage);
389             }
390             break;
391         case FIGURE_GLADIATOR:
392             b = get_entertainment_building(f);
393             if (b->type == BUILDING_AMPHITHEATER) {
394                 houses_serviced = provide_entertainment(x, y,
395                     b->data.entertainment.days2 ? 2 : 1, amphitheater_coverage);
396             } else if (b->type == BUILDING_COLOSSEUM) {
397                 houses_serviced = provide_entertainment(x, y,
398                     b->data.entertainment.days1 ? 2 : 1, colosseum_coverage);
399             }
400             break;
401         case FIGURE_LION_TAMER:
402             b = get_entertainment_building(f);
403             houses_serviced = provide_entertainment(x, y,
404                 b->data.entertainment.days2 ? 2 : 1, colosseum_coverage);
405             break;
406         case FIGURE_CHARIOTEER:
407             houses_serviced = provide_culture(x, y, hippodrome_coverage);
408             break;
409         case FIGURE_ENGINEER: {
410             int max_damage = 0;
411             houses_serviced = provide_service(x, y, &max_damage, engineer_coverage);
412             if (max_damage > f->min_max_seen) {
413                 f->min_max_seen = max_damage;
414             } else if (f->min_max_seen <= 10) {
415                 f->min_max_seen = 0;
416             } else {
417                 f->min_max_seen -= 10;
418             }
419             break;
420         }
421         case FIGURE_PREFECT: {
422             int min_happiness = 100;
423             houses_serviced = provide_service(x, y, &min_happiness, prefect_coverage);
424             f->min_max_seen = min_happiness;
425             break;
426         }
427         case FIGURE_RIOTER:
428             if (figure_rioter_collapse_building(f) == 1) {
429                 return 1;
430             }
431             break;
432     }
433     if (f->building_id) {
434         b = building_get(f->building_id);
435         b->houses_covered += houses_serviced;
436         if (b->houses_covered > 300) {
437             b->houses_covered = 300;
438         }
439     }
440     return 0;
441 }
442