diff --git a/src/city/health.c b/src/city/health.c index 637325c8eb..db27b03a85 100644 --- a/src/city/health.c +++ b/src/city/health.c @@ -295,6 +295,43 @@ static void adjust_sickness_level_in_plague_buildings(int hospital_coverage_bonu } } +int city_health_get_house_health_level(building *b) +{ + int house_health = 0; + + if (building_is_house(b->type)) { + house_health = calc_bound(b->subtype.house_level, 0, 10); + if (b->data.house.clinic && b->data.house.hospital) { + house_health += 50; + city_data.health.population_access.clinic += b->house_population; + } else if (b->data.house.hospital) { + house_health += 40; + } else if (b->data.house.clinic) { + house_health += 30; + city_data.health.population_access.clinic += b->house_population; + } + if (b->data.house.bathhouse) { + house_health += 20; + city_data.health.population_access.baths += b->house_population; + } + if (b->data.house.barber) { + house_health += 10; + city_data.health.population_access.barber += b->house_population; + } + house_health += b->data.house.num_foods * 15; + + int mausoleum_health = building_count_active(BUILDING_SMALL_MAUSOLEUM); + mausoleum_health += building_count_active(BUILDING_LARGE_MAUSOLEUM) * 2; + + house_health += calc_bound(mausoleum_health, 0, 10); + + int health_cap = (model_get_house(b->subtype.house_level)->food_types && !b->data.house.num_foods) ? + 40 : 100; + house_health = calc_bound(house_health, 0, health_cap); + } + return house_health; +} + void city_health_update(void) { if (city_data.population.population < 200 || scenario_is_tutorial_1() || scenario_is_tutorial_2()) { @@ -319,34 +356,8 @@ void city_health_update(void) b->sickness_level = 0; continue; } - int house_health = calc_bound(b->subtype.house_level, 0, 10); - if (b->data.house.clinic && b->data.house.hospital) { - house_health += 50; - city_data.health.population_access.clinic += b->house_population; - } else if (b->data.house.hospital) { - house_health += 40; - } else if (b->data.house.clinic) { - house_health += 30; - city_data.health.population_access.clinic += b->house_population; - } - if (b->data.house.bathhouse) { - house_health += 20; - city_data.health.population_access.baths += b->house_population; - } - if (b->data.house.barber) { - house_health += 10; - city_data.health.population_access.barber += b->house_population; - } - house_health += b->data.house.num_foods * 15; - - int mausoleum_health = building_count_active(BUILDING_SMALL_MAUSOLEUM); - mausoleum_health += building_count_active(BUILDING_LARGE_MAUSOLEUM) * 2; - - house_health += calc_bound(mausoleum_health, 0, 10); + int house_health = city_health_get_house_health_level(b); - int health_cap = (model_get_house(b->subtype.house_level)->food_types && !b->data.house.num_foods) ? - 40 : 100; - house_health = calc_bound(house_health, 0, health_cap); total_population += b->house_population; healthy_population += calc_adjust_with_percentage(b->house_population, house_health); adjust_sickness_level_in_house(b, house_health, population_health_offset, hospital_coverage_bonus); diff --git a/src/city/health.h b/src/city/health.h index bd5d6ee2af..9559ecf60f 100644 --- a/src/city/health.h +++ b/src/city/health.h @@ -19,6 +19,8 @@ void city_health_change(int amount); void city_health_set(int new_value); +int city_health_get_house_health_level(building *b); + void city_health_update(void); void city_health_update_sickness_level_in_building(int building_id); diff --git a/src/core/hotkey_config.c b/src/core/hotkey_config.c index c26cd4b6d9..538ad2cb22 100644 --- a/src/core/hotkey_config.c +++ b/src/core/hotkey_config.c @@ -107,6 +107,7 @@ static const char *ini_keys[] = { "show_overlay_school", "show_overlay_library", "show_overlay_academy", + "show_overlay_health", "show_overlay_barber", "show_overlay_bathhouse", "show_overlay_clinic", diff --git a/src/core/hotkey_config.h b/src/core/hotkey_config.h index d9a038b9a5..943d037875 100644 --- a/src/core/hotkey_config.h +++ b/src/core/hotkey_config.h @@ -93,6 +93,7 @@ typedef enum { HOTKEY_SHOW_OVERLAY_SCHOOL, HOTKEY_SHOW_OVERLAY_LIBRARY, HOTKEY_SHOW_OVERLAY_ACADEMY, + HOTKEY_SHOW_OVERLAY_HEALTH, HOTKEY_SHOW_OVERLAY_BARBER, HOTKEY_SHOW_OVERLAY_BATHHOUSE, HOTKEY_SHOW_OVERLAY_CLINIC, diff --git a/src/game/state.h b/src/game/state.h index 5513adee76..25bd2749f3 100644 --- a/src/game/state.h +++ b/src/game/state.h @@ -38,6 +38,7 @@ enum { OVERLAY_SICKNESS = 38, OVERLAY_EFFICIENCY = 39, OVERLAY_STORAGES = 40, + OVERLAY_HEALTH = 41, }; void game_state_init(void); diff --git a/src/input/hotkey.c b/src/input/hotkey.c index 0e10c059ee..deb7ce5d8d 100644 --- a/src/input/hotkey.c +++ b/src/input/hotkey.c @@ -359,6 +359,10 @@ static void set_definition_for_action(hotkey_action action, hotkey_definition *d def->action = &data.hotkey_state.show_overlay; def->value = OVERLAY_ACADEMY; break; + case HOTKEY_SHOW_OVERLAY_HEALTH: + def->action = &data.hotkey_state.show_overlay; + def->value = OVERLAY_HEALTH; + break; case HOTKEY_SHOW_OVERLAY_BARBER: def->action = &data.hotkey_state.show_overlay; def->value = OVERLAY_BARBER; diff --git a/src/translation/english.c b/src/translation/english.c index 24b6bac30e..22844fc7df 100644 --- a/src/translation/english.c +++ b/src/translation/english.c @@ -788,13 +788,26 @@ static translation_string all_strings[] = { {TR_BUILDING_WAREHOUSE_NO_GOODS, "No goods stored at this warehouse."}, {TR_BUILDING_HOUSE_DISEASE_DESC, "Pestilence has struck! Without adequate healthcare, residents dwelling here have died. This house is in quarantine while a doctor or surgeon decontaminates the area."}, {TR_BUILDING_FUMIGATION_DESC, "The building is being cleansed of disease. The fumigation should last a few days."}, + {TR_OVERLAY_HEALTH, "Rating"}, {TR_OVERLAY_SICKNESS, "Sickness"}, {TR_ADVISOR_HEALTH_RATING, "Rating:"}, {TR_ADVISOR_HEALTH_SURVEILLANCE, "Disease surveillance:"}, {TR_ADVISOR_SICKNESS_LEVEL_LOW, "Disease is a rare occurrence in the city. The high standard of healthcare provided to the vast majority of residents effectively prevents epidemics spreading here."}, {TR_ADVISOR_SICKNESS_LEVEL_MEDIUM, "Some infectious diseases have appeared in areas of the city with a lacking hygiene, but the situation is under control. A sufficiently high level of healthcare will mitigate the impact of epidemics in the city."}, {TR_ADVISOR_SICKNESS_LEVEL_HIGH, "Infectious diseases spread in areas of the city having a poor level of hygiene. If the situation is not remedied soon, some buildings may be quarantined. Greater access to healthcare would effectively prevent epidemics from breaking out."}, - {TR_ADVISOR_SICKNESS_LEVEL_PLAGUE, "The plague has come to some unhealthy areas in the city! Citizens fall ill and buildings have been quarantined or burned to prevent the further spread of disease. Available doctors and surgeons have been requisitioned to decontaminate the afflicted places. Action must be taken!"}, + {TR_ADVISOR_SICKNESS_LEVEL_PLAGUE, "The plague has come to some unhealthy areas in the city! Buildings have been quarantined or burned to prevent the further spread of disease. Doctors and surgeons have been requisitioned to decontaminate the afflicted places."}, + {TR_TOOLTIP_OVERLAY_HEALTH_NONE, "No citizen lives here yet"}, + {TR_TOOLTIP_OVERLAY_HEALTH_0, "Health in this house is appalling (0-9)"}, + {TR_TOOLTIP_OVERLAY_HEALTH_1, "Health in this house is terrible (10-19)"}, + {TR_TOOLTIP_OVERLAY_HEALTH_2, "Health in this house is bad (20-29)"}, + {TR_TOOLTIP_OVERLAY_HEALTH_3, "Health in this house is poor (30-39)"}, + {TR_TOOLTIP_OVERLAY_HEALTH_4, "Health in this house is below average (40-49)"}, + {TR_TOOLTIP_OVERLAY_HEALTH_5, "Health in this house is average (50-59)"}, + {TR_TOOLTIP_OVERLAY_HEALTH_6, "Health in this house is good (60-69)"}, + {TR_TOOLTIP_OVERLAY_HEALTH_7, "Health in this house is very good (70-79)"}, + {TR_TOOLTIP_OVERLAY_HEALTH_8, "Health in this house is excellent (80-89)"}, + {TR_TOOLTIP_OVERLAY_HEALTH_9, "Health in this house is almost perfect (90-99)"}, + {TR_TOOLTIP_OVERLAY_HEALTH_10, "Health in this house is perfect (100)"}, {TR_TOOLTIP_OVERLAY_SICKNESS_NONE, "No disease"}, {TR_TOOLTIP_OVERLAY_SICKNESS_LOW, "Very few diseases"}, {TR_TOOLTIP_OVERLAY_SICKNESS_MEDIUM, "A few infectious diseases"}, @@ -825,6 +838,7 @@ static translation_string all_strings[] = { {TR_HOTKEY_SHOW_OVERLAY_SCHOOL, "Schools overlay" }, {TR_HOTKEY_SHOW_OVERLAY_LIBRARY, "Library overlay" }, {TR_HOTKEY_SHOW_OVERLAY_ACADEMY, "Academy overlay" }, + {TR_HOTKEY_SHOW_OVERLAY_HEALTH, "Health overlay" }, {TR_HOTKEY_SHOW_OVERLAY_BARBER, "Barber overlay" }, {TR_HOTKEY_SHOW_OVERLAY_BATHHOUSE, "Baths overlay" }, {TR_HOTKEY_SHOW_OVERLAY_CLINIC, "Clinics overlay" }, diff --git a/src/translation/translation.h b/src/translation/translation.h index 487ecca6a2..3449589e2f 100644 --- a/src/translation/translation.h +++ b/src/translation/translation.h @@ -783,6 +783,7 @@ typedef enum { TR_BUILDING_WAREHOUSE_NO_GOODS, TR_BUILDING_HOUSE_DISEASE_DESC, TR_BUILDING_FUMIGATION_DESC, + TR_OVERLAY_HEALTH, TR_OVERLAY_SICKNESS, TR_ADVISOR_HEALTH_RATING, TR_ADVISOR_HEALTH_SURVEILLANCE, @@ -790,6 +791,18 @@ typedef enum { TR_ADVISOR_SICKNESS_LEVEL_MEDIUM, TR_ADVISOR_SICKNESS_LEVEL_HIGH, TR_ADVISOR_SICKNESS_LEVEL_PLAGUE, + TR_TOOLTIP_OVERLAY_HEALTH_NONE, + TR_TOOLTIP_OVERLAY_HEALTH_0, + TR_TOOLTIP_OVERLAY_HEALTH_1, + TR_TOOLTIP_OVERLAY_HEALTH_2, + TR_TOOLTIP_OVERLAY_HEALTH_3, + TR_TOOLTIP_OVERLAY_HEALTH_4, + TR_TOOLTIP_OVERLAY_HEALTH_5, + TR_TOOLTIP_OVERLAY_HEALTH_6, + TR_TOOLTIP_OVERLAY_HEALTH_7, + TR_TOOLTIP_OVERLAY_HEALTH_8, + TR_TOOLTIP_OVERLAY_HEALTH_9, + TR_TOOLTIP_OVERLAY_HEALTH_10, TR_TOOLTIP_OVERLAY_SICKNESS_NONE, TR_TOOLTIP_OVERLAY_SICKNESS_LOW, TR_TOOLTIP_OVERLAY_SICKNESS_MEDIUM, @@ -820,6 +833,7 @@ typedef enum { TR_HOTKEY_SHOW_OVERLAY_SCHOOL, TR_HOTKEY_SHOW_OVERLAY_LIBRARY, TR_HOTKEY_SHOW_OVERLAY_ACADEMY, + TR_HOTKEY_SHOW_OVERLAY_HEALTH, TR_HOTKEY_SHOW_OVERLAY_BARBER, TR_HOTKEY_SHOW_OVERLAY_BATHHOUSE, TR_HOTKEY_SHOW_OVERLAY_CLINIC, diff --git a/src/widget/city_overlay_health.c b/src/widget/city_overlay_health.c index 1588671807..7a81d44e6f 100644 --- a/src/widget/city_overlay_health.c +++ b/src/widget/city_overlay_health.c @@ -4,6 +4,13 @@ #include "game/state.h" #include "translation/translation.h" +static int show_building_health(const building *b) +{ + return b->type == BUILDING_HOSPITAL || b->type == BUILDING_DOCTOR || + b->type == BUILDING_BARBER || b->type == BUILDING_BATHHOUSE || + b->type == BUILDING_SMALL_MAUSOLEUM || b->type == BUILDING_LARGE_MAUSOLEUM; +} + static int show_building_barber(const building *b) { return b->type == BUILDING_BARBER; @@ -31,6 +38,12 @@ static int show_building_sickness(const building *b) b->type == BUILDING_SMALL_MAUSOLEUM || b->type == BUILDING_LARGE_MAUSOLEUM; } +static int show_figure_health(const figure *f) +{ + return f->type == FIGURE_SURGEON || f->type == FIGURE_DOCTOR || + f->type == FIGURE_BARBER || f->type == FIGURE_BATHHOUSE_WORKER; +} + static int show_figure_barber(const figure *f) { return f->type == FIGURE_BARBER; @@ -69,6 +82,17 @@ static int show_figure_sickness(const figure *f) return 0; } +static int get_column_height_health(const building *b) +{ + int house_health = city_health_get_house_health_level(b); + + if (b->house_population > 0 && house_health < 1 ) { + house_health += 1; + } + + return b->house_size && house_health ? house_health / 10 : NO_COLUMN; +} + static int get_column_height_barber(const building *b) { return b->house_size && b->data.house.barber ? b->data.house.barber / 10 : NO_COLUMN; @@ -94,6 +118,48 @@ static int get_column_height_sickness(const building *b) return b->sickness_level ? b->sickness_level / 10 : NO_COLUMN; } +static int get_tooltip_health(tooltip_context *c, const building *b) +{ + if (building_is_house(b->type)) { + int house_health = city_health_get_house_health_level(b); + + if (house_health < 40) { + if (b->house_population < 1 && house_health < 1) { + c->translation_key = TR_TOOLTIP_OVERLAY_HEALTH_NONE; + } else if (b->house_population >= 1 && house_health < 10) { + c->translation_key = TR_TOOLTIP_OVERLAY_HEALTH_0; + } else if (house_health < 20) { + c->translation_key = TR_TOOLTIP_OVERLAY_HEALTH_1; + } else if (house_health < 30) { + c->translation_key = TR_TOOLTIP_OVERLAY_HEALTH_2; + } else { + c->translation_key = TR_TOOLTIP_OVERLAY_HEALTH_3; + } + } else if (house_health < 60) { + if (house_health < 50) { + c->translation_key = TR_TOOLTIP_OVERLAY_HEALTH_4; + } else { + c->translation_key = TR_TOOLTIP_OVERLAY_HEALTH_5; + } + } else if (house_health < 80) { + if (house_health < 70) { + c->translation_key = TR_TOOLTIP_OVERLAY_HEALTH_6; + } else { + c->translation_key = TR_TOOLTIP_OVERLAY_HEALTH_7; + } + } else if (house_health < 100) { + if (house_health < 90) { + c->translation_key = TR_TOOLTIP_OVERLAY_HEALTH_8; + } else { + c->translation_key = TR_TOOLTIP_OVERLAY_HEALTH_9; + } + } else { + c->translation_key = TR_TOOLTIP_OVERLAY_HEALTH_10; + } + } + return 0; +} + static int get_tooltip_barber(tooltip_context *c, const building *b) { if (b->data.house.barber <= 0) { @@ -165,6 +231,22 @@ static int get_tooltip_sickness(tooltip_context *c, const building *b) return 0; } +const city_overlay *city_overlay_for_health(void) +{ + static city_overlay overlay = { + OVERLAY_HEALTH, + COLUMN_COLOR_GREEN_TO_RED, + show_building_health, + show_figure_health, + get_column_height_health, + 0, + get_tooltip_health, + 0, + 0 + }; + return &overlay; +} + const city_overlay *city_overlay_for_barber(void) { static city_overlay overlay = { diff --git a/src/widget/city_overlay_health.h b/src/widget/city_overlay_health.h index aab3febb8d..c9b7b6cae6 100644 --- a/src/widget/city_overlay_health.h +++ b/src/widget/city_overlay_health.h @@ -3,6 +3,8 @@ #include "city_overlay.h" +const city_overlay *city_overlay_for_health(void); + const city_overlay *city_overlay_for_bathhouse(void); const city_overlay *city_overlay_for_barber(void); diff --git a/src/widget/city_with_overlay.c b/src/widget/city_with_overlay.c index de08a4b336..00da36335d 100644 --- a/src/widget/city_with_overlay.c +++ b/src/widget/city_with_overlay.c @@ -91,6 +91,8 @@ static const city_overlay *get_city_overlay(void) return city_overlay_for_library(); case OVERLAY_ACADEMY: return city_overlay_for_academy(); + case OVERLAY_HEALTH: + return city_overlay_for_health(); case OVERLAY_BARBER: return city_overlay_for_barber(); case OVERLAY_BATHHOUSE: @@ -704,7 +706,9 @@ int city_with_overlay_get_tooltip_text(tooltip_context *c, int grid_offset) overlay_type != OVERLAY_WATER && overlay_type != OVERLAY_FIRE && overlay_type != OVERLAY_LEVY && overlay_type != OVERLAY_DAMAGE && overlay_type != OVERLAY_NATIVE && overlay_type != OVERLAY_DESIRABILITY && overlay_type != OVERLAY_PROBLEMS && overlay_type != OVERLAY_MOTHBALL && overlay_type != OVERLAY_ENEMY && - overlay_type != OVERLAY_LOGISTICS && overlay_type != OVERLAY_SICKNESS && overlay_type != OVERLAY_EFFICIENCY; + overlay_type != OVERLAY_LOGISTICS && overlay_type != OVERLAY_SICKNESS && overlay_type != OVERLAY_EFFICIENCY && + overlay_type != OVERLAY_HEALTH + ; building *b = building_get(building_id); if (overlay_requires_house && !b->house_size) { return 0; diff --git a/src/window/hotkey_config.c b/src/window/hotkey_config.c index f916510e82..014672e649 100644 --- a/src/window/hotkey_config.c +++ b/src/window/hotkey_config.c @@ -129,6 +129,7 @@ static hotkey_widget hotkey_widgets[] = { {HOTKEY_SHOW_OVERLAY_SCHOOL, TR_HOTKEY_SHOW_OVERLAY_SCHOOL}, {HOTKEY_SHOW_OVERLAY_LIBRARY, TR_HOTKEY_SHOW_OVERLAY_LIBRARY}, {HOTKEY_SHOW_OVERLAY_ACADEMY, TR_HOTKEY_SHOW_OVERLAY_ACADEMY}, + {HOTKEY_SHOW_OVERLAY_HEALTH, TR_HOTKEY_SHOW_OVERLAY_HEALTH}, {HOTKEY_SHOW_OVERLAY_BARBER, TR_HOTKEY_SHOW_OVERLAY_BARBER}, {HOTKEY_SHOW_OVERLAY_BATHHOUSE, TR_HOTKEY_SHOW_OVERLAY_BATHHOUSE}, {HOTKEY_SHOW_OVERLAY_CLINIC, TR_HOTKEY_SHOW_OVERLAY_CLINIC}, diff --git a/src/window/overlay_menu.c b/src/window/overlay_menu.c index 8b0e0e830d..27ff78bdd9 100644 --- a/src/window/overlay_menu.c +++ b/src/window/overlay_menu.c @@ -54,14 +54,14 @@ static generic_button submenu_buttons[] = { static const int MENU_ID_TO_OVERLAY[OVERLAY_BUTTONS] = { OVERLAY_NONE, OVERLAY_WATER, 1, 3, 5, 6, 7, OVERLAY_RELIGION, OVERLAY_ROADS, OVERLAY_DESIRABILITY, OVERLAY_SENTIMENT }; static const int MENU_ID_TO_SUBMENU_ID[OVERLAY_BUTTONS] = { 0, 0, 1, 2, 3, 4, 5, 0, 0 }; -static const int ADDITIONAL_OVERLAY_TR[] = { TR_OVERLAY_ROADS, TR_OVERLAY_LEVY, TR_OVERLAY_TAVERN, TR_OVERLAY_ARENA_COL, TR_OVERLAY_SENTIMENT, TR_OVERLAY_MOTHBALL, TR_OVERLAY_ENEMY, TR_OVERLAY_LOGISTICS, TR_OVERLAY_SICKNESS, TR_OVERLAY_EFFICIENCY, TR_OVERLAY_STORAGES }; +static const int ADDITIONAL_OVERLAY_TR[] = { TR_OVERLAY_ROADS, TR_OVERLAY_LEVY, TR_OVERLAY_TAVERN, TR_OVERLAY_ARENA_COL, TR_OVERLAY_SENTIMENT, TR_OVERLAY_MOTHBALL, TR_OVERLAY_ENEMY, TR_OVERLAY_LOGISTICS, TR_OVERLAY_SICKNESS, TR_OVERLAY_EFFICIENCY, TR_OVERLAY_STORAGES, TR_OVERLAY_HEALTH }; static const int SUBMENU_ID_TO_OVERLAY[6][OVERLAY_BUTTONS] = { {0}, {OVERLAY_FIRE, OVERLAY_DAMAGE, OVERLAY_CRIME, OVERLAY_NATIVE, OVERLAY_PROBLEMS, OVERLAY_ENEMY, 0}, {OVERLAY_ENTERTAINMENT, OVERLAY_TAVERN, OVERLAY_THEATER, OVERLAY_AMPHITHEATER, OVERLAY_ARENA, OVERLAY_COLOSSEUM, OVERLAY_HIPPODROME, 0}, {OVERLAY_EDUCATION, OVERLAY_SCHOOL, OVERLAY_LIBRARY, OVERLAY_ACADEMY, 0}, - {OVERLAY_BARBER, OVERLAY_BATHHOUSE, OVERLAY_CLINIC, OVERLAY_HOSPITAL, OVERLAY_SICKNESS, 0}, + {OVERLAY_HEALTH, OVERLAY_BARBER, OVERLAY_BATHHOUSE, OVERLAY_CLINIC, OVERLAY_HOSPITAL, OVERLAY_SICKNESS, 0}, {OVERLAY_TAX_INCOME, OVERLAY_LEVY, OVERLAY_EFFICIENCY, OVERLAY_FOOD_STOCKS, OVERLAY_MOTHBALL, OVERLAY_LOGISTICS, 0}, };