#include #include #include #include "senkenconfig.h" #include "map.h" #include "tiles.h" #include "happy.h" #include "population.h" extern game_t *game; extern population_t *population; extern map_t *map; extern mapobj_t *entertainment_types_list; extern tiles_t *tiles; static int count_list_employ(mapspot_list_t *list); struct happy_s { int nominal_income_tax; int nominal_leave_perc; int need_services_density; int unemployment_rate; mapspot_list_t *police_list; mapspot_list_t *firedept_list; mapspot_list_t *hospital_list; mapspot_list_t *ent_list; int hospital_coverage; int police_coverage; }; extern happy_t * happy_init(void) { happy_t *ret; int p_per_citizen = config_getint("policeofficer_per_citizen", 100); int h_per_citizen = config_getint("hospitalworker_per_citizen", 100); ret = calloc(1, sizeof(happy_t)); ret->nominal_income_tax = config_getint("nominal_income_taxrate", 10); ret->nominal_leave_perc = config_getint("nominal_leave_percentage", 3); ret->need_services_density = config_getint("needservices_density", 50); ret->unemployment_rate = population_unemployment_rate(population); ret->police_list = map_find_tile_list(map, MAPTYPE_POLICE); ret->firedept_list = map_find_tile_list(map, MAPTYPE_FIRE); ret->hospital_list = map_find_tile_list(map, MAPTYPE_HOSPITAL); ret->ent_list = map_find_tile_list_fromlist(map, entertainment_types_list); /* * See if there are enough police officers and hospital workers */ ret->police_coverage = p_per_citizen * count_list_employ(ret->police_list); if (ret->police_coverage > 2) ret->police_coverage = 2; ret->hospital_coverage = h_per_citizen * count_list_employ(ret->hospital_list); if (ret->hospital_coverage > 2) ret->hospital_coverage = 2; return ret; } extern void happy_free(happy_t *happy) { free(happy); } static void attends_school(person_t *child, void *rock) { person_t *parent = (person_t *)rock; if (child->livex == -1) { happy_down(parent, 10, REASON_NOSCHOOL); } } static int entertainment_cb(int x, int y, void *data, float dist, void *rock) { mapobj_t obj = (mapobj_t)data; person_t *person = (person_t *)rock; int range; range = tiles_getrange(tiles, obj); if (dist <= range) { happy_up(person, 1, REASON_NATURE); } return 0; } static int count_employ_cb(int x, int y, void *data, float dist, void *rock) { int *countp = (int *)rock; (*countp) += tiles_getnumemploy(tiles, (mapobj_t)data); return 0; } static int count_list_employ(mapspot_list_t *list) { int count = 0; mapspot_iterate(list, &count_employ_cb, &count); return count; } extern int happy_calc_person(happy_t *happy, person_t *person) { int density; /* * If no job become less happy */ if (person->workx == -1) { happy_down(person, 5, REASON_UNEMPLOYED); } else { if (labor_table[person->worklevel].edlevel < person->edlevel - 4) { happy_down(person, 3, REASON_UNDEREMPLOYED); } else { happy_up(person, 2, REASON_EMPLOYED); } /* worry about income tax */ happy_up(person, happy->nominal_income_tax - game->tax_rates[TAX_TYPE_INCOME], REASON_TAXES); } /* * Coverage */ happy_up(person, happy->police_coverage, REASON_POLICE); happy_up(person, happy->hospital_coverage, REASON_HOSPITAL); /* * If unemployment rate high */ if (happy->unemployment_rate > 6) { /* xxx */ happy_down(person, (happy->unemployment_rate - 6), REASON_ECONOMY); } if (person->livex == -1) { happy_down(person, 10, REASON_HOMELESS); density = 0; /* xxx? */ } else { int watercnt; happy_up(person, 1, REASON_HOUSED); density = map_density(map, person->livex, person->livey, 5) * 25; if (map_isflagset(map, person->livex, person->livey, MAPFLAG_NOPOWER)) { happy_down(person, 5, REASON_NOPOWER); } if (map_isflagset(map, person->livex, person->livey, MAPFLAG_NOWATER)) { happy_down(person, 7, REASON_NOWATER); } /* * happier if live near water * * XXX configurable */ watercnt = map_within_count_type(map, person->livex, person->livey, 15, MAPTYPE_WATER); if (watercnt > 5) { happy_up(person, 3, REASON_NATURE); } else if (watercnt > 0) { happy_up(person, 1, REASON_NATURE); } } /* * Garbage not being picked up */ if (game->garbage_excess > 750) { happy_down(person, 4, REASON_GARBAGE); } /* * Less happy if friends leaving */ if (game->totalpop) { float leftperc = ((float)game->left_lastmonth*100.0)/((float)game->totalpop); leftperc -= happy->nominal_leave_perc; if (leftperc >= 1.0) { leftperc = leftperc/2 + 1; if (leftperc > 5) leftperc = 5; happy_down(person, leftperc, REASON_SOCIAL); } } /* * Less happy if children not attending school */ population_foreach_child(population, person->uid, &attends_school, person); /* * Less happy if not enough schools * * XXX old people shouldn't care */ if (game->totalkids) { float noschoolperc = ((float)game->schoolless*100.0)/((float)game->totalkids); if (noschoolperc > 1.0) { if (noschoolperc > 5) noschoolperc = 5; happy_down(person, noschoolperc, REASON_SCHOOLS); } } if (density > happy->need_services_density) { /* * Less happy if no nearby police */ if (!mapspot_within_range(happy->police_list, person->livex, person->livey, map, tiles)) { happy_downto(person, 95, 8, REASON_POLICE); } else { happy_up(person, 1, REASON_POLICE); } /* * Less happy if no nearby fire to live or work */ if (!mapspot_within_range(happy->firedept_list, person->livex, person->livey, map, tiles)) { happy_downto(person, 95, 5, REASON_FIRE); } else { happy_up(person, 1, REASON_FIRE); } /* * Less happy if no nearby hospital */ if (!mapspot_within_range(happy->hospital_list, person->livex, person->livey, map, tiles)) { happy_downto(person, 95, 8, REASON_HOSPITAL); } else { happy_up(person, 1, REASON_HOSPITAL); } } /* * Entertainment! */ mapspot_within_iterate(happy->ent_list, person->livex, person->livey, 50, &entertainment_cb, person); return person->happy; } extern void happy_up(person_t *person, int n, happy_reason_t reason) { if (person->happy + n > 255) { person->happy = 255; } else { person->happy+=n; } game->reasons[reason] += n; } extern void happy_down(person_t *person, int n, happy_reason_t reason) { if (person->happy - n < 0) { person->happy = 0; } else { person->happy -= n; } game->reasons[reason] -= n; } extern void happy_upto(person_t *person, int to, int n, happy_reason_t reason) { if (person->happy < to) { game->reasons[reason] += (to - person->happy); person->happy = to; } else { happy_up(person, n, reason); } } extern void happy_downto(person_t *person, int to, int n, happy_reason_t reason) { if (person->happy > to) { game->reasons[reason] -= (person->happy - to); person->happy = to; } else { happy_down(person, n, reason); } }