From d33e5d78b252336008302d72fec178b713b70799 Mon Sep 17 00:00:00 2001 From: Instafluff Date: Tue, 7 Jul 2026 08:25:18 -0700 Subject: [PATCH 1/4] Allow pollution spawns to alternatively reduce population instead of polluting a tile --- C3X.h | 1 + civ_prog_objects.csv | 1 + default.c3x_config.ini | 1 + injected_code.c | 14 ++++++++++++++ 4 files changed, 17 insertions(+) diff --git a/C3X.h b/C3X.h index c8965638..c692e596 100644 --- a/C3X.h +++ b/C3X.h @@ -281,6 +281,7 @@ struct c3x_config { bool dont_end_units_turn_after_airdrop; bool allow_airdrop_without_airport; bool enable_negative_pop_pollution; + bool pollution_spawns_reduce_population; enum retreat_rules land_retreat_rules; enum retreat_rules sea_retreat_rules; bool allow_defensive_retreat_on_water; diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index b51b05dc..75e2b6b4 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -214,6 +214,7 @@ inlead, 0x4BED80, 0x4C6350, 0x4BEE10, "City_cycle_specialist_type", "bool ( inlead, 0x4B1C10, 0x4B8BC0, 0x4B1CA0, "City_get_total_pollution", "int (__fastcall *) (City * this)" define, 0x4B1B40, 0x4B8B00, 0x4B1BD0, "City_get_pollution_from_buildings", "int (__fastcall *) (City * this)" inlead, 0x4B1A50, 0x4B8A10, 0x4B1AE0, "City_get_pollution_from_pop", "int (__fastcall *) (City * this)" +inlead, 0x4B2F80, 0x4B9F00, 0x4B3010, "City_update_spawn_pollution", "void (__fastcall *) (City * this)" inlead, 0x4ACF40, 0x4B3F20, 0x4ACFD0, "City_add_or_remove_improvement", "void (__fastcall *) (City * this, int edx, int improv_id, int add, bool param_3)" define, 0x57E943, 0x58B68E, 0x57E6A3, "ADDR_RESOURCE_TILE_COUNT_MASK", "void *" define, 0x57E5EB, 0x58B328, 0x57E34B, "ADDR_RESOURCE_TILE_COUNT_ZERO_COMPARE", "void *" diff --git a/default.c3x_config.ini b/default.c3x_config.ini index d65fc6a0..b9bcb452 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -485,6 +485,7 @@ dont_end_units_turn_after_airdrop = false allow_airdrop_without_airport = false enable_negative_pop_pollution = true +pollution_spawns_reduce_population = false ; Here it's possible to set buildings as prerequisites for unit production. It's possible for a unit type to have multiple prereq buildings up to a ; maximum of 10, and all must be present in a city for the unit to be buildable. diff --git a/injected_code.c b/injected_code.c index 4bf1973a..6576fb68 100644 --- a/injected_code.c +++ b/injected_code.c @@ -18381,6 +18381,7 @@ patch_init_floating_point () {"dont_end_units_turn_after_airdrop" , false, offsetof (struct c3x_config, dont_end_units_turn_after_airdrop)}, {"allow_airdrop_without_airport" , false, offsetof (struct c3x_config, allow_airdrop_without_airport)}, {"enable_negative_pop_pollution" , true , offsetof (struct c3x_config, enable_negative_pop_pollution)}, + {"pollution_spawns_reduce_population" , false, offsetof (struct c3x_config, pollution_spawns_reduce_population)}, {"allow_defensive_retreat_on_water" , false, offsetof (struct c3x_config, allow_defensive_retreat_on_water)}, {"promote_wonder_decorruption_effect" , false, offsetof (struct c3x_config, promote_wonder_decorruption_effect)}, {"allow_military_leaders_to_hurry_wonders" , false, offsetof (struct c3x_config, allow_military_leaders_to_hurry_wonders)}, @@ -25018,6 +25019,19 @@ patch_City_get_total_pollution (City * this) return City_get_pollution_from_buildings (this) + patch_City_get_pollution_from_pop (this); } +void __fastcall +patch_City_update_spawn_pollution (City * this) +{ + if (! is->current_config.pollution_spawns_reduce_population) { + City_update_spawn_pollution (this); + return; + } + + int pollution = patch_City_get_total_pollution (this); + if ((pollution > 0) && (rand_int (p_rand_object, __, 100) < pollution) && (this->Body.Population.Size > 1)) + City_remove_population (this, __, 1, -1, '\0'); +} + void remove_extra_palaces (City * city, City * excluded_destination); void From 2b498ea7c977a2a21ca06621c571243e668e281a Mon Sep 17 00:00:00 2001 From: Instafluff Date: Tue, 7 Jul 2026 08:29:14 -0700 Subject: [PATCH 2/4] Fix address copy/paste error --- civ_prog_objects.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 75e2b6b4..a6239cd0 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -214,7 +214,7 @@ inlead, 0x4BED80, 0x4C6350, 0x4BEE10, "City_cycle_specialist_type", "bool ( inlead, 0x4B1C10, 0x4B8BC0, 0x4B1CA0, "City_get_total_pollution", "int (__fastcall *) (City * this)" define, 0x4B1B40, 0x4B8B00, 0x4B1BD0, "City_get_pollution_from_buildings", "int (__fastcall *) (City * this)" inlead, 0x4B1A50, 0x4B8A10, 0x4B1AE0, "City_get_pollution_from_pop", "int (__fastcall *) (City * this)" -inlead, 0x4B2F80, 0x4B9F00, 0x4B3010, "City_update_spawn_pollution", "void (__fastcall *) (City * this)" +inlead, 0x4B2F80, 0x0, 0x0, "City_update_spawn_pollution", "void (__fastcall *) (City * this)" inlead, 0x4ACF40, 0x4B3F20, 0x4ACFD0, "City_add_or_remove_improvement", "void (__fastcall *) (City * this, int edx, int improv_id, int add, bool param_3)" define, 0x57E943, 0x58B68E, 0x57E6A3, "ADDR_RESOURCE_TILE_COUNT_MASK", "void *" define, 0x57E5EB, 0x58B328, 0x57E34B, "ADDR_RESOURCE_TILE_COUNT_ZERO_COMPARE", "void *" From 6e0a3b185f0d516ca8658bf9a77db7fe1592c376 Mon Sep 17 00:00:00 2001 From: Instafluff Date: Tue, 7 Jul 2026 08:56:12 -0700 Subject: [PATCH 3/4] Add map message if pollution reduces population --- C3X.h | 1 + Text/c3x-labels.txt | 3 +++ injected_code.c | 10 +++++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/C3X.h b/C3X.h index c692e596..66b607a2 100644 --- a/C3X.h +++ b/C3X.h @@ -603,6 +603,7 @@ enum c3x_label { CL_OBSOLETED_BY, CL_NO_STEALTH_ATTACK, CL_DODGED_SAM, + CL_POLLUTION_REDUCES_POP, CL_PREVIEW, CL_CITY_TOO_CLOSE_BUTTON_TOOLTIP, CL_TOTAL_CITIES, diff --git a/Text/c3x-labels.txt b/Text/c3x-labels.txt index 80d84e88..86b41c7e 100644 --- a/Text/c3x-labels.txt +++ b/Text/c3x-labels.txt @@ -52,6 +52,9 @@ Obsoleted By ; Pops up on the map when a bomber gets intercepted by enemy AA buildings but dodges the damage We were chased off by enemy air defenses. +; Pops up on the map when pollution reduces a city's population instead of spawning on a tile. +Pollution reduces population of + ; As in a "preview" version of the mod. Used on the mod info popup. Preview diff --git a/injected_code.c b/injected_code.c index 6576fb68..23988577 100644 --- a/injected_code.c +++ b/injected_code.c @@ -25028,8 +25028,16 @@ patch_City_update_spawn_pollution (City * this) } int pollution = patch_City_get_total_pollution (this); - if ((pollution > 0) && (rand_int (p_rand_object, __, 100) < pollution) && (this->Body.Population.Size > 1)) + if ((pollution > 0) && (rand_int (p_rand_object, __, 100) < pollution) && (this->Body.Population.Size > 1)) { City_remove_population (this, __, 1, -1, '\0'); + + if (this->Body.CivID == p_main_screen_form->Player_CivID) { + char msg[160]; + snprintf (msg, sizeof msg, "%s %s", is->c3x_labels[CL_POLLUTION_REDUCES_POP], this->Body.CityName); + msg[(sizeof msg) - 1] = '\0'; + show_map_specific_text (this->Body.X, this->Body.Y, msg, true); + } + } } void remove_extra_palaces (City * city, City * excluded_destination); From c4fc1f87df48b974183c92bd5cb22059a7e837cc Mon Sep 17 00:00:00 2001 From: Instafluff Date: Mon, 13 Jul 2026 11:19:38 -0700 Subject: [PATCH 4/4] Updated to allow both pop reduction and tile spawining of pollution --- C3X.h | 8 +++++++- default.c3x_config.ini | 6 +++++- injected_code.c | 22 +++++++++++++++++++--- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/C3X.h b/C3X.h index 061430d4..3e6b43bc 100644 --- a/C3X.h +++ b/C3X.h @@ -227,6 +227,12 @@ enum ai_auto_build_great_wall_strategy { AAGWS_OTHER_CIV_BORDERED_ONLY }; +enum pollution_spawn_effect { + PSE_DEFAULT = 0, + PSE_REDUCE_POPULATION, + PSE_REDUCE_POPULATION_AND_POLLUTE_TILE +}; + enum perfume_kind { PK_PRODUCTION = 0, PK_TECHNOLOGY, @@ -327,7 +333,7 @@ struct c3x_config { bool dont_end_units_turn_after_airdrop; bool allow_airdrop_without_airport; bool enable_negative_pop_pollution; - bool pollution_spawns_reduce_population; + enum pollution_spawn_effect pollution_spawn_effect; bool enable_pollution_from_free_improvements; enum retreat_rules land_retreat_rules; enum retreat_rules sea_retreat_rules; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index c8358c46..81fd15ba 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -485,7 +485,11 @@ dont_end_units_turn_after_airdrop = false allow_airdrop_without_airport = false enable_negative_pop_pollution = true -pollution_spawns_reduce_population = false + +; Controls what happens when pollution may spawn from a city. +; Options are "default", "reduce-population", and "reduce-population-and-pollute-tile". +; In "reduce-population-and-pollute-tile" mode, the population reduction and tile pollution checks run independently. +pollution_spawn_effect = default ; Causes improvements granted for free by great or small wonders to contribute pollution as though they had been built in the city. enable_pollution_from_free_improvements = false diff --git a/injected_code.c b/injected_code.c index c609a2dc..889a76b7 100644 --- a/injected_code.c +++ b/injected_code.c @@ -2182,6 +2182,17 @@ read_ai_auto_build_great_wall_strategy (struct string_slice const * s, int * out return false; } +bool +read_pollution_spawn_effect (struct string_slice const * s, int * out_val) +{ + struct string_slice trimmed = trim_string_slice (s, 1); + if (slice_matches_str (&trimmed, "default" )) { *out_val = PSE_DEFAULT; return true; } + else if (slice_matches_str (&trimmed, "reduce-population" )) { *out_val = PSE_REDUCE_POPULATION; return true; } + else if (slice_matches_str (&trimmed, "reduce-population-and-pollute-tile" )) { *out_val = PSE_REDUCE_POPULATION_AND_POLLUTE_TILE; return true; } + else + return false; +} + bool read_tile_terrain_type_value (struct string_slice const * s, enum SquareTypes * out_type) { @@ -2754,6 +2765,9 @@ load_config (char const * file_path, int path_is_relative_to_mod_dir) } else if (slice_matches_str (&p.key, "sea_retreat_rules")) { if (! read_retreat_rules (&value, (int *)&cfg->sea_retreat_rules)) handle_config_error (&p, CPE_BAD_VALUE); + } else if (slice_matches_str (&p.key, "pollution_spawn_effect")) { + if (! read_pollution_spawn_effect (&value, (int *)&cfg->pollution_spawn_effect)) + handle_config_error (&p, CPE_BAD_VALUE); } else if (slice_matches_str (&p.key, "draw_lines_using_gdi_plus")) { if (! read_line_drawing_override (&value, (int *)&cfg->draw_lines_using_gdi_plus)) handle_config_error (&p, CPE_BAD_VALUE); @@ -19259,7 +19273,6 @@ patch_init_floating_point () {"dont_end_units_turn_after_airdrop" , false, offsetof (struct c3x_config, dont_end_units_turn_after_airdrop)}, {"allow_airdrop_without_airport" , false, offsetof (struct c3x_config, allow_airdrop_without_airport)}, {"enable_negative_pop_pollution" , true , offsetof (struct c3x_config, enable_negative_pop_pollution)}, - {"pollution_spawns_reduce_population" , false, offsetof (struct c3x_config, pollution_spawns_reduce_population)}, {"enable_pollution_from_free_improvements" , false, offsetof (struct c3x_config, enable_pollution_from_free_improvements)}, {"allow_defensive_retreat_on_water" , false, offsetof (struct c3x_config, allow_defensive_retreat_on_water)}, {"promote_wonder_decorruption_effect" , false, offsetof (struct c3x_config, promote_wonder_decorruption_effect)}, @@ -19533,6 +19546,7 @@ patch_init_floating_point () base_config.distribution_hub_yield_division_mode = DHYDM_FLAT; base_config.ai_distribution_hub_build_strategy = ADHBS_BY_CITY_COUNT; base_config.ai_auto_build_great_wall_strategy = AAGWS_ALL_BORDERS; + base_config.pollution_spawn_effect = PSE_DEFAULT; base_config.great_wall_auto_build_wonder_improv_id = -1; for (int n = 0; n < ARRAY_LEN (boolean_config_options); n++) *((char *)&base_config + boolean_config_options[n].offset) = boolean_config_options[n].base_val; @@ -26291,10 +26305,12 @@ patch_City_get_total_pollution (City * this) void __fastcall patch_City_update_spawn_pollution (City * this) { - if (! is->current_config.pollution_spawns_reduce_population) { + enum pollution_spawn_effect effect = is->current_config.pollution_spawn_effect; + if (effect == PSE_DEFAULT) { City_update_spawn_pollution (this); return; - } + } else if (effect == PSE_REDUCE_POPULATION_AND_POLLUTE_TILE) + City_update_spawn_pollution (this); int pollution = patch_City_get_total_pollution (this); if ((pollution > 0) && (rand_int (p_rand_object, __, 100) < pollution) && (this->Body.Population.Size > 1)) {