From 1a983f6a9948ed5b3b74a96d28893a13cfb0b4df Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 21 Nov 2025 16:33:47 -0800 Subject: [PATCH 001/356] Experimenting with art --- Art/Districts/1200/Port.PCX | Bin 0 -> 11053 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Art/Districts/1200/Port.PCX diff --git a/Art/Districts/1200/Port.PCX b/Art/Districts/1200/Port.PCX new file mode 100644 index 0000000000000000000000000000000000000000..985461602ab6bddc6054f745010f456633aab14c GIT binary patch literal 11053 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 literal 0 HcmV?d00001 From 8e39ccd11edf95c0fc12ecf157dcc61be266b1c7 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 21 Nov 2025 17:42:13 -0800 Subject: [PATCH 002/356] Enable workers to walk on coast; Add granular settings for districts & buildable square types --- C3X.h | 31 +++- Civ3Conquests.h | 1 + civ_prog_objects.csv | 1 + default.c3x_config.ini | 1 + default.districts_config.txt | 12 ++ injected_code.c | 309 +++++++++++++++++++++++++---------- 6 files changed, 264 insertions(+), 91 deletions(-) diff --git a/C3X.h b/C3X.h index 6a9246d7..558f4b64 100644 --- a/C3X.h +++ b/C3X.h @@ -17,7 +17,7 @@ typedef unsigned char byte; #define MAX_BUILDING_PREREQS_FOR_UNIT 10 #define COUNT_SPECIAL_DISTRICT_TYPES 10 -#define USED_SPECIAL_DISTRICT_TYPES 5 +#define USED_SPECIAL_DISTRICT_TYPES 6 #define MAX_DYNAMIC_DISTRICT_TYPES 22 #define COUNT_DISTRICT_TYPES (COUNT_SPECIAL_DISTRICT_TYPES + MAX_DYNAMIC_DISTRICT_TYPES) #define MAX_WONDER_DISTRICT_TYPES 32 @@ -323,12 +323,12 @@ struct c3x_config { bool enable_wonder_districts; bool enable_distribution_hub_districts; bool enable_aerodrome_districts; + bool enable_port_districts; bool cities_with_mutual_district_receive_buildings; bool cities_with_mutual_district_receive_wonders; bool air_units_use_aerodrome_districts_not_cities; - int maximum_pop_before_neighborhood_needed; int per_neighborhood_pop_growth_enabled; @@ -519,6 +519,10 @@ enum { MAX_DISTRICT_DEPENDENTS = 64 }; +enum { + DEFAULT_DISTRICT_BUILDABLE_MASK = (1 << SQ_Desert) | (1 << SQ_Plains) | (1 << SQ_Grassland) | (1 << SQ_Tundra) | (1 << SQ_FloodPlain) | (1 << SQ_Hills) +}; + struct district_config { enum Unit_Command_Values command; char const * name; @@ -526,10 +530,12 @@ struct district_config { char const * advance_prereq; char const * dependent_improvements[MAX_DISTRICT_DEPENDENTS]; char const * img_paths[10]; + unsigned short buildable_square_types_mask; bool allow_multiple; bool vary_img_by_era; bool vary_img_by_culture; bool is_dynamic; + bool is_maritime; int dependent_improvement_count; int img_path_count; int max_building_index; @@ -597,6 +603,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx", "Neighborhood_Abandoned.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 6, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .defense_bonus_percent = 25 @@ -605,6 +612,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_WonderDistrict, .name = "Wonder District", .tooltip = "Build Wonder District", .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"WonderDistrict.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 @@ -613,23 +621,34 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_DistributionHub, .name = "Distribution Hub", .tooltip = "Build Distribution Hub", .advance_prereq = "Construction", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"DistributionHub.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 2, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 }, { .command = UCV_Build_Aerodrome, .name = "Aerodrome", .tooltip = "Build Aerodrome", - .advance_prereq = "Flight", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereq = "Flight", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 1, .img_paths = {"Aerodrome.pcx"}, .dependent_improvements = {"Airport"}, - .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, + .img_path_count = 1, .max_building_index = 1, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 }, { - .command = -1, .name = "Natural Wonder", .tooltip = NULL, + .command = -1, .name = "Natural Wonder", .tooltip = NULL, .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {0}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 0, .max_building_index = 0, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 + }, + { + .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", + .advance_prereq = "Map Making", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, + .img_paths = {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, + .img_path_count = 8, .max_building_index = 2, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 2, .gold_bonus = 2, .shield_bonus = 0, .defense_bonus_percent = 0 } }; @@ -652,6 +671,7 @@ struct parsed_district_definition { int food_bonus; int gold_bonus; int shield_bonus; + unsigned short buildable_square_types_mask; bool has_name; bool has_tooltip; bool has_advance_prereq; @@ -668,6 +688,7 @@ struct parsed_district_definition { bool has_food_bonus; bool has_gold_bonus; bool has_shield_bonus; + bool has_buildable_on; }; struct scenario_district_entry { diff --git a/Civ3Conquests.h b/Civ3Conquests.h index 2a964d89..c8305ad1 100644 --- a/Civ3Conquests.h +++ b/Civ3Conquests.h @@ -772,6 +772,7 @@ enum Unit_Command_Values UCV_Build_WonderDistrict = -10000002, UCV_Build_DistributionHub = -10000003, UCV_Build_Aerodrome = -10000004, + UCV_Build_Port = -10000005, }; enum Unit_Mode_Actions diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 7a87fbdc..4f54d469 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -100,6 +100,7 @@ define, 0x5B2B20, 0x5C1440, 0x5B2830, "Unit_next_escorter_id", "int (__fast define, 0x5BC300, 0x5CAE50, 0x5BC010, "Unit_disband", "void (__fastcall *) (Unit * this)" inlead, 0x5C00A0, 0x5CEC20, 0x5BFDB0, "Unit_can_hurry_production", "bool (__fastcall *) (Unit * this, int edx, City * city, bool exclude_cheap_improvements)" inlead, 0x5B3AB0, 0x5C2400, 0x5B37C0, "Unit_can_pillage", "bool (__fastcall *) (Unit *this, int edx, int tile_x, int tile_y)" +inlead, 0x5CCBB0, 0x0, 0x0, "Unit_can_pass_between", "bool (__fastcall *) (Unit *this, int edx, int from_x, int from_y, int to_x, int to_y, byte param_5)" define, 0x5C0420, 0x5CEFC0, 0x5C0130, "Unit_hurry_production", "void (__fastcall *) (Unit * this)" define, 0x5C0300, 0x5CEE90, 0x5C0010, "Unit_ai_can_start_science_age", "bool (__fastcall *) (Unit * this)" define, 0x5C03B0, 0x5CEF50, 0x5C00C0, "Unit_start_science_age", "void (__fastcall *) (Unit * this)" diff --git a/default.c3x_config.ini b/default.c3x_config.ini index a12e08b5..f1e38491 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -817,6 +817,7 @@ enable_neighborhood_districts = false enable_wonder_districts = false enable_distribution_hub_districts = false enable_aerodrome_districts = false +enable_port_districts = false ; When multiple cities share a district (i.e., the same district tile is within multiple cities' work radii), these options control whether those ; cities automatically share the benefits of buildings and wonders constructed in that district. For example, if Rome and Veii both have the same diff --git a/default.districts_config.txt b/default.districts_config.txt index e232cb6e..97938c38 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -149,4 +149,16 @@ culture_bonus = 0 science_bonus = 0 food_bonus = 0 gold_bonus = 0 +shield_bonus = 0 + +#District +name = Port +advance_prereq = Map Making +dependent_improvs = Harbor, "Commercial Dock" +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 2 +gold_bonus = 2 shield_bonus = 0 \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index d8fa0a70..cbb520bb 100644 --- a/injected_code.c +++ b/injected_code.c @@ -71,7 +71,7 @@ struct injected_state * is = ADDR_INJECTED_STATE; #define DISTRIBUTION_HUB_DISTRICT_ID 2 #define AERODROME_DISTRICT_ID 3 #define NATURAL_WONDER_DISTRICT_ID 4 - +#define PORT_DISTRICT_ID 5 char const * const hotseat_replay_save_path = "Saves\\Auto\\ai-move-replay-before-interturn.SAV"; char const * const hotseat_resume_save_path = "Saves\\Auto\\ai-move-replay-resume.SAV"; @@ -1639,6 +1639,40 @@ read_square_type_value (struct string_slice const * s, enum SquareTypes * out_ty return false; } +unsigned short +square_type_mask_bit (enum SquareTypes type) +{ + if ((int)type < 0 || type > SQ_RIVER) + return 0; + return (unsigned short)(1u << type); +} + +unsigned short +all_square_types_mask (void) +{ + return (unsigned short)((1u << (SQ_RIVER + 1)) - 1); +} + +unsigned short +district_default_buildable_mask (void) +{ + return (unsigned short)DEFAULT_DISTRICT_BUILDABLE_MASK; +} + +bool +district_is_buildable_on_square_type (struct district_config const * cfg, enum SquareTypes base_type) +{ + if (cfg == NULL) + return false; + + unsigned short mask = cfg->buildable_square_types_mask; + if (mask == 0) + mask = district_default_buildable_mask (); + + unsigned short bit = square_type_mask_bit (base_type); + return (bit != 0) && ((mask & bit) != 0); +} + bool read_natural_wonder_terrain_type (struct string_slice const * s, enum SquareTypes * out_type) { @@ -4624,6 +4658,7 @@ init_parsed_district_definition (struct parsed_district_definition * def) memset (def, 0, sizeof *def); def->img_path_count = -1; def->defense_bonus_percent = 100; + def->buildable_square_types_mask = district_default_buildable_mask (); } void @@ -4800,6 +4835,70 @@ parse_config_string_list (char * value_text, return true; } +bool +parse_buildable_square_type_mask (struct string_slice const * value, + unsigned short * out_mask, + struct error_line ** parse_errors, + int line_number) +{ + char * value_text = trim_and_extract_slice (value, 0); + unsigned short mask = 0; + int entry_count = 0; + + if (value_text != NULL) { + char * cursor = value_text; + while (1) { + while (is_space_char (*cursor)) + cursor++; + + char * item_start = cursor; + while ((*cursor != '\0') && (*cursor != ',')) + cursor++; + + char * item_end = cursor; + while ((item_end > item_start) && is_space_char (item_end[-1])) + item_end--; + + struct string_slice item_slice = { .str = item_start, .len = (int)(item_end - item_start) }; + if (item_slice.len > 0) { + enum SquareTypes parsed; + if (read_square_type_value (&item_slice, &parsed)) { + if (parsed == (enum SquareTypes)SQ_INVALID) + mask = all_square_types_mask (); + else + mask |= square_type_mask_bit (parsed); + entry_count += 1; + } else { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %.*s (invalid buildable_on entry)", line_number, item_slice.len, item_slice.str); + err->text[(sizeof err->text) - 1] = '\0'; + free (value_text); + return false; + } + } + + if (*cursor == ',') { + cursor++; + continue; + } + break; + } + } + + if (value_text != NULL) + free (value_text); + + if ((entry_count == 0) || (mask == 0)) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: buildable_on (expected at least one square type)", line_number); + err->text[(sizeof err->text) - 1] = '\0'; + return false; + } + + *out_mask = mask; + return true; +} + bool override_special_district_from_definition (struct parsed_district_definition * def, int section_start_line) { @@ -4853,6 +4952,8 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->gold_bonus = def->gold_bonus; if (def->has_shield_bonus) cfg->shield_bonus = def->shield_bonus; + if (def->has_buildable_on) + cfg->buildable_square_types_mask = def->buildable_square_types_mask; if (def->has_dependent_improvements) { for (int i = 0; i < ARRAY_LEN (cfg->dependent_improvements); i++) { @@ -4964,6 +5065,7 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.food_bonus = def->has_food_bonus ? def->food_bonus : 0; new_cfg.gold_bonus = def->has_gold_bonus ? def->gold_bonus : 0; new_cfg.shield_bonus = def->has_shield_bonus ? def->shield_bonus : 0; + new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); new_cfg.dependent_improvement_count = def->has_dependent_improvements ? def->dependent_improvement_count : 0; const int max_dependent_entries = ARRAY_LEN (is->district_configs[0].dependent_improvements); @@ -5091,7 +5193,7 @@ handle_district_definition_key (struct parsed_district_definition * def, &list_count, parse_errors, line_number, - "dependent_improvs")) { + "dependent_improvs")) { def->dependent_improvement_count = list_count; def->has_dependent_improvements = true; } else { @@ -5100,6 +5202,13 @@ handle_district_definition_key (struct parsed_district_definition * def, } free (value_text); + } else if (slice_matches_str (key, "buildable_on")) { + unsigned short mask; + if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number)) { + def->buildable_square_types_mask = mask; + def->has_buildable_on = true; + } + } else if (slice_matches_str (key, "allow_multiple")) { struct string_slice val_slice = *value; int ival; @@ -12015,8 +12124,7 @@ set_up_district_buttons (Main_GUI * this) Tile * tile = tile_at (selected_unit->Body.X, selected_unit->Body.Y); if ((tile == NULL) || (tile == p_null_tile) || (tile->CityID >= 0)) return; - int base_type = tile->vtable->m50_Get_Square_BaseType (tile); - if (base_type == SQ_Mountains || base_type == SQ_Forest || base_type == SQ_Jungle || base_type == SQ_Swamp) return; + enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); if (tile->vtable->m21_Check_Crates (tile, __, 0)) return; if (tile->vtable->m20_Check_Pollution (tile, __, 0)) return; @@ -12095,6 +12203,8 @@ set_up_district_buttons (Main_GUI * this) int prereq_id = is->district_infos[dc].advance_prereq_id; if ((prereq_id >= 0) && !Leader_has_tech(&leaders[selected_unit->Body.CivID], __, prereq_id)) continue; + if (! district_is_buildable_on_square_type (&is->district_configs[dc], base_type)) + continue; // This district should be shown active_districts[active_count++] = dc; @@ -12721,107 +12831,105 @@ issue_district_worker_command (Unit * unit, int command) if (! is->current_config.enable_districts) return; + if (unit == NULL) + return; + int tile_x = unit->Body.X; int tile_y = unit->Body.Y; Tile * tile = tile_at (tile_x, tile_y); - int unit_type_id = unit->Body.UnitTypeID; - int unit_id = unit->Body.ID; + if ((tile == NULL) || (tile == p_null_tile)) + return; if (! is_worker(unit)) return; + int district_id = -1; + if (! itable_look_up (&is->command_id_to_district_id, command, &district_id)) + return; + if ((district_id < 0) || (district_id >= is->district_count)) + return; + // Check tech prerequisite for the selected district, if any - int district_id; - if (itable_look_up (&is->command_id_to_district_id, command, &district_id)) { - if (district_id < 0 || district_id >= is->district_count) - return; - int prereq_id = is->district_infos[district_id].advance_prereq_id; - // Only enforce if a prereq is configured - if (prereq_id >= 0 && !Leader_has_tech (&leaders[unit->Body.CivID], __, prereq_id)) { - return; // Civ lacks required tech; do not issue command - } + int prereq_id = is->district_infos[district_id].advance_prereq_id; + // Only enforce if a prereq is configured + if ((prereq_id >= 0) && !Leader_has_tech (&leaders[unit->Body.CivID], __, prereq_id)) { + return; // Civ lacks required tech; do not issue command } - // Disallow placing districts on invalid terrain, pollution, or cratered tiles - if (tile != NULL && tile != p_null_tile) { - if (tile->vtable->m21_Check_Crates (tile, __, 0)) - return; - if (tile->vtable->m20_Check_Pollution (tile, __, 0)) - return; - enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType(tile); - if (base_type == SQ_Mountains || base_type == SQ_Forest || base_type == SQ_Jungle || base_type == SQ_Swamp) { - return; - } - } - - if (tile != NULL && tile != p_null_tile) { - // If District will be replaced by another District - struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && district_is_complete(tile, inst->district_type)) { - int district_id = inst->district_type; - int inst_x, inst_y; - if (! district_instance_get_coords (inst, tile, &inst_x, &inst_y)) - return; + // Disallow placing districts on invalid terrain, pollution, or cratered tiles + if (tile->vtable->m21_Check_Crates (tile, __, 0)) + return; + if (tile->vtable->m20_Check_Pollution (tile, __, 0)) + return; - int civ_id = unit->Body.CivID; - bool redundant_district = district_instance_is_redundant (inst, tile); - bool would_lose_buildings = any_nearby_city_would_lose_district_benefits (district_id, civ_id, inst_x, inst_y); - if (redundant_district) - would_lose_buildings = false; + enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); + if (! district_is_buildable_on_square_type (&is->district_configs[district_id], base_type)) + return; - bool remove_existing = false; - - PopupForm * popup = get_popup_form (); - set_popup_str_param (0, (char*)is->district_configs[district_id].name, -1, -1); - set_popup_str_param (1, (char*)is->district_configs[district_id].name, -1, -1); - popup->vtable->set_text_key_and_flags ( - popup, __, is->mod_script_path, - would_lose_buildings - ? "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT" - : "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT_SAFE", - -1, 0, 0, 0 - ); - - int sel = patch_show_popup (popup, __, 0, 0); - if (sel == 0) - remove_existing = true; - else - return; + // If District will be replaced by another District + struct district_instance * inst = get_district_instance (tile); + if (inst != NULL && district_is_complete(tile, inst->district_type)) { + int existing_district_id = inst->district_type; + int inst_x, inst_y; + if (! district_instance_get_coords (inst, tile, &inst_x, &inst_y)) + return; - if (remove_existing) { - remove_district_instance (tile); - tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); - tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, inst_x, inst_y); - handle_district_removed (tile, district_id, inst_x, inst_y, false); - } - } + int civ_id = unit->Body.CivID; + bool redundant_district = district_instance_is_redundant (inst, tile); + bool would_lose_buildings = any_nearby_city_would_lose_district_benefits (existing_district_id, civ_id, inst_x, inst_y); + if (redundant_district) + would_lose_buildings = false; - // If District will replace an improvement - if (itable_look_up (&is->command_id_to_district_id, command, &district_id)) { - unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); - unsigned int removable_flags = overlay_flags & 0xfc; + bool remove_existing = false; + + PopupForm * popup = get_popup_form (); + set_popup_str_param (0, (char*)is->district_configs[existing_district_id].name, -1, -1); + set_popup_str_param (1, (char*)is->district_configs[existing_district_id].name, -1, -1); + popup->vtable->set_text_key_and_flags ( + popup, __, is->mod_script_path, + would_lose_buildings + ? "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT" + : "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT_SAFE", + -1, 0, 0, 0 + ); - if (removable_flags != 0) { - PopupForm * popup = get_popup_form (); - set_popup_str_param (0, (char*)is->district_configs[district_id].name, -1, -1); - popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_CONFIRM_BUILD_DISTRICT_OVER_IMPROVEMENT", -1, 0, 0, 0); - int sel = patch_show_popup (popup, __, 0, 0); - if (sel != 0) - return; - } + int sel = patch_show_popup (popup, __, 0, 0); + if (sel == 0) + remove_existing = true; + else + return; - if (removable_flags != 0) - tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, removable_flags, tile_x, tile_y); - tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, tile_x, tile_y); - } + if (remove_existing) { + remove_district_instance (tile); + tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, inst_x, inst_y); + handle_district_removed (tile, existing_district_id, inst_x, inst_y, false); + } + } - inst = ensure_district_instance (tile, district_id, tile_x, tile_y); - if (inst != NULL) - inst->state = DS_UNDER_CONSTRUCTION; + // If District will replace an improvement + unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); + unsigned int removable_flags = overlay_flags & 0xfc; - Unit_set_state(unit, __, UnitState_Build_Mines); - unit->Body.Job_ID = WJ_Build_Mines; + if (removable_flags != 0) { + PopupForm * popup = get_popup_form (); + set_popup_str_param (0, (char*)is->district_configs[district_id].name, -1, -1); + popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_CONFIRM_BUILD_DISTRICT_OVER_IMPROVEMENT", -1, 0, 0, 0); + int sel = patch_show_popup (popup, __, 0, 0); + if (sel != 0) + return; } + + if (removable_flags != 0) + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, removable_flags, tile_x, tile_y); + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, tile_x, tile_y); + + inst = ensure_district_instance (tile, district_id, tile_x, tile_y); + if (inst != NULL) + inst->state = DS_UNDER_CONSTRUCTION; + + Unit_set_state (unit, __, UnitState_Build_Mines); + unit->Body.Job_ID = WJ_Build_Mines; } void @@ -23515,6 +23623,19 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i return false; } +int +get_port_district_variant_for_tile (Tile * tile) +{ + int variant = 0; + + int sheet_index = (tile->SquareParts >> 8) & 0xFF; + int sprite_index = tile->SquareParts & 0xFF; + + // TODO + + return variant; +} + void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { @@ -24630,5 +24751,21 @@ patch_Tile_m71_Check_Worker_Job (Tile * this) return Tile_m71_Check_Worker_Job (this); } +PassBetweenValidity __fastcall +patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int to_x, int to_y, int param_5) +{ + PassBetweenValidity base = Unit_can_pass_between (this, __, from_x, from_y, to_x, to_y, param_5); + + if (base != PBV_OK && is->current_config.enable_port_districts && is_worker(this)) { + Tile * dest = tile_at (to_x, to_y); + if ((dest != NULL) && + dest->vtable->m35_Check_Is_Water (dest) && + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) + return PBV_OK; // Let workers treat coast as passable when port districts are on + } + + return base; +} + // TCC requires a main function be defined even though it's never used. int main () { return 0; } From 2b7076c79eabd599295152871bb59341e4325b7d Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 22 Nov 2025 08:20:06 -0800 Subject: [PATCH 003/356] Add to config --- C3X.h | 2 +- injected_code.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/C3X.h b/C3X.h index 558f4b64..a426bdbd 100644 --- a/C3X.h +++ b/C3X.h @@ -645,7 +645,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", .advance_prereq = "Map Making", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, - .img_paths = {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, + .img_paths = {0},// {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 8, .max_building_index = 2, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 2, .gold_bonus = 2, .shield_bonus = 0, .defense_bonus_percent = 0 diff --git a/injected_code.c b/injected_code.c index cbb520bb..b195c736 100644 --- a/injected_code.c +++ b/injected_code.c @@ -10873,6 +10873,7 @@ patch_init_floating_point () {"enable_natural_wonders" , false, offsetof (struct c3x_config, enable_natural_wonders)}, {"enable_distribution_hub_districts" , false, offsetof (struct c3x_config, enable_distribution_hub_districts)}, {"enable_aerodrome_districts" , false, offsetof (struct c3x_config, enable_aerodrome_districts)}, + {"enable_port_districts" , false, offsetof (struct c3x_config, enable_port_districts)}, {"completed_wonder_districts_can_be_destroyed" , false, offsetof (struct c3x_config, completed_wonder_districts_can_be_destroyed)}, {"destroyed_wonders_can_be_built_again" , false, offsetof (struct c3x_config, destroyed_wonders_can_be_built_again)}, {"cities_with_mutual_district_receive_buildings" , false, offsetof (struct c3x_config, cities_with_mutual_district_receive_buildings)}, From 6ecec4d135e5b0de8211b9b1b7602911c968d655 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 21 Nov 2025 16:33:47 -0800 Subject: [PATCH 004/356] Experimenting with art --- Art/Districts/1200/Port.PCX | Bin 0 -> 11053 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Art/Districts/1200/Port.PCX diff --git a/Art/Districts/1200/Port.PCX b/Art/Districts/1200/Port.PCX new file mode 100644 index 0000000000000000000000000000000000000000..985461602ab6bddc6054f745010f456633aab14c GIT binary patch literal 11053 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 literal 0 HcmV?d00001 From 5e5ea07e5a32f1f51b72b15b8b7e6b9bab814926 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 21 Nov 2025 17:42:13 -0800 Subject: [PATCH 005/356] Enable workers to walk on coast; Add granular settings for districts & buildable square types --- C3X.h | 31 +++- Civ3Conquests.h | 1 + civ_prog_objects.csv | 1 + default.c3x_config.ini | 1 + default.districts_config.txt | 12 ++ injected_code.c | 267 +++++++++++++++++++++++++---------- 6 files changed, 230 insertions(+), 83 deletions(-) diff --git a/C3X.h b/C3X.h index 2706ea87..520adba9 100644 --- a/C3X.h +++ b/C3X.h @@ -17,7 +17,7 @@ typedef unsigned char byte; #define MAX_BUILDING_PREREQS_FOR_UNIT 10 #define COUNT_SPECIAL_DISTRICT_TYPES 10 -#define USED_SPECIAL_DISTRICT_TYPES 5 +#define USED_SPECIAL_DISTRICT_TYPES 6 #define MAX_DYNAMIC_DISTRICT_TYPES 22 #define COUNT_DISTRICT_TYPES (COUNT_SPECIAL_DISTRICT_TYPES + MAX_DYNAMIC_DISTRICT_TYPES) #define MAX_WONDER_DISTRICT_TYPES 32 @@ -331,12 +331,12 @@ struct c3x_config { bool enable_wonder_districts; bool enable_distribution_hub_districts; bool enable_aerodrome_districts; + bool enable_port_districts; bool cities_with_mutual_district_receive_buildings; bool cities_with_mutual_district_receive_wonders; bool air_units_use_aerodrome_districts_not_cities; - int maximum_pop_before_neighborhood_needed; int per_neighborhood_pop_growth_enabled; @@ -538,6 +538,10 @@ enum { MAX_DISTRICT_DEPENDENTS = 64 }; +enum { + DEFAULT_DISTRICT_BUILDABLE_MASK = (1 << SQ_Desert) | (1 << SQ_Plains) | (1 << SQ_Grassland) | (1 << SQ_Tundra) | (1 << SQ_FloodPlain) | (1 << SQ_Hills) +}; + struct district_config { enum Unit_Command_Values command; char const * name; @@ -545,10 +549,12 @@ struct district_config { char const * advance_prereq; char const * dependent_improvements[MAX_DISTRICT_DEPENDENTS]; char const * img_paths[10]; + unsigned short buildable_square_types_mask; bool allow_multiple; bool vary_img_by_era; bool vary_img_by_culture; bool is_dynamic; + bool is_maritime; int dependent_improvement_count; int img_path_count; int max_building_index; @@ -616,6 +622,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx", "Neighborhood_Abandoned.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 6, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .defense_bonus_percent = 25 @@ -624,6 +631,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_WonderDistrict, .name = "Wonder District", .tooltip = "Build Wonder District", .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"WonderDistrict.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 @@ -632,23 +640,34 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_DistributionHub, .name = "Distribution Hub", .tooltip = "Build Distribution Hub", .advance_prereq = "Construction", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"DistributionHub.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 2, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 }, { .command = UCV_Build_Aerodrome, .name = "Aerodrome", .tooltip = "Build Aerodrome", - .advance_prereq = "Flight", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereq = "Flight", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 1, .img_paths = {"Aerodrome.pcx"}, .dependent_improvements = {"Airport"}, - .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, + .img_path_count = 1, .max_building_index = 1, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 }, { - .command = -1, .name = "Natural Wonder", .tooltip = NULL, + .command = -1, .name = "Natural Wonder", .tooltip = NULL, .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {0}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 0, .max_building_index = 0, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 + }, + { + .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", + .advance_prereq = "Map Making", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, + .img_paths = {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, + .img_path_count = 8, .max_building_index = 2, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 2, .gold_bonus = 2, .shield_bonus = 0, .defense_bonus_percent = 0 } }; @@ -671,6 +690,7 @@ struct parsed_district_definition { int food_bonus; int gold_bonus; int shield_bonus; + unsigned short buildable_square_types_mask; bool has_name; bool has_tooltip; bool has_advance_prereq; @@ -687,6 +707,7 @@ struct parsed_district_definition { bool has_food_bonus; bool has_gold_bonus; bool has_shield_bonus; + bool has_buildable_on; }; struct parsed_wonder_definition { diff --git a/Civ3Conquests.h b/Civ3Conquests.h index 2a964d89..c8305ad1 100644 --- a/Civ3Conquests.h +++ b/Civ3Conquests.h @@ -772,6 +772,7 @@ enum Unit_Command_Values UCV_Build_WonderDistrict = -10000002, UCV_Build_DistributionHub = -10000003, UCV_Build_Aerodrome = -10000004, + UCV_Build_Port = -10000005, }; enum Unit_Mode_Actions diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 1a2d2ceb..74d98120 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -100,6 +100,7 @@ define, 0x5B2B20, 0x5C1440, 0x5B2830, "Unit_next_escorter_id", "int (__fast define, 0x5BC300, 0x5CAE50, 0x5BC010, "Unit_disband", "void (__fastcall *) (Unit * this)" inlead, 0x5C00A0, 0x5CEC20, 0x5BFDB0, "Unit_can_hurry_production", "bool (__fastcall *) (Unit * this, int edx, City * city, bool exclude_cheap_improvements)" inlead, 0x5B3AB0, 0x5C2400, 0x5B37C0, "Unit_can_pillage", "bool (__fastcall *) (Unit *this, int edx, int tile_x, int tile_y)" +inlead, 0x5CCBB0, 0x0, 0x0, "Unit_can_pass_between", "bool (__fastcall *) (Unit *this, int edx, int from_x, int from_y, int to_x, int to_y, byte param_5)" define, 0x5C0420, 0x5CEFC0, 0x5C0130, "Unit_hurry_production", "void (__fastcall *) (Unit * this)" define, 0x5C0300, 0x5CEE90, 0x5C0010, "Unit_ai_can_start_science_age", "bool (__fastcall *) (Unit * this)" define, 0x5C03B0, 0x5CEF50, 0x5C00C0, "Unit_start_science_age", "void (__fastcall *) (Unit * this)" diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 42eacf65..ba090732 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -825,6 +825,7 @@ enable_neighborhood_districts = false enable_wonder_districts = false enable_distribution_hub_districts = false enable_aerodrome_districts = false +enable_port_districts = false ; When multiple cities share a district (i.e., the same district tile is within multiple cities' work radii), these options control whether those ; cities automatically share the benefits of buildings and wonders constructed in that district. For example, if Rome and Veii both have the same diff --git a/default.districts_config.txt b/default.districts_config.txt index e232cb6e..97938c38 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -149,4 +149,16 @@ culture_bonus = 0 science_bonus = 0 food_bonus = 0 gold_bonus = 0 +shield_bonus = 0 + +#District +name = Port +advance_prereq = Map Making +dependent_improvs = Harbor, "Commercial Dock" +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 2 +gold_bonus = 2 shield_bonus = 0 \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index 469bdd27..47b746d9 100644 --- a/injected_code.c +++ b/injected_code.c @@ -71,7 +71,7 @@ struct injected_state * is = ADDR_INJECTED_STATE; #define DISTRIBUTION_HUB_DISTRICT_ID 2 #define AERODROME_DISTRICT_ID 3 #define NATURAL_WONDER_DISTRICT_ID 4 - +#define PORT_DISTRICT_ID 5 char const * const hotseat_replay_save_path = "Saves\\Auto\\ai-move-replay-before-interturn.SAV"; char const * const hotseat_resume_save_path = "Saves\\Auto\\ai-move-replay-resume.SAV"; @@ -1722,6 +1722,40 @@ read_square_type_value (struct string_slice const * s, enum SquareTypes * out_ty return false; } +unsigned short +square_type_mask_bit (enum SquareTypes type) +{ + if ((int)type < 0 || type > SQ_RIVER) + return 0; + return (unsigned short)(1u << type); +} + +unsigned short +all_square_types_mask (void) +{ + return (unsigned short)((1u << (SQ_RIVER + 1)) - 1); +} + +unsigned short +district_default_buildable_mask (void) +{ + return (unsigned short)DEFAULT_DISTRICT_BUILDABLE_MASK; +} + +bool +district_is_buildable_on_square_type (struct district_config const * cfg, enum SquareTypes base_type) +{ + if (cfg == NULL) + return false; + + unsigned short mask = cfg->buildable_square_types_mask; + if (mask == 0) + mask = district_default_buildable_mask (); + + unsigned short bit = square_type_mask_bit (base_type); + return (bit != 0) && ((mask & bit) != 0); +} + bool read_natural_wonder_terrain_type (struct string_slice const * s, enum SquareTypes * out_type) { @@ -4713,6 +4747,7 @@ init_parsed_district_definition (struct parsed_district_definition * def) memset (def, 0, sizeof *def); def->img_path_count = -1; def->defense_bonus_percent = 100; + def->buildable_square_types_mask = district_default_buildable_mask (); } void @@ -4889,6 +4924,70 @@ parse_config_string_list (char * value_text, return true; } +bool +parse_buildable_square_type_mask (struct string_slice const * value, + unsigned short * out_mask, + struct error_line ** parse_errors, + int line_number) +{ + char * value_text = trim_and_extract_slice (value, 0); + unsigned short mask = 0; + int entry_count = 0; + + if (value_text != NULL) { + char * cursor = value_text; + while (1) { + while (is_space_char (*cursor)) + cursor++; + + char * item_start = cursor; + while ((*cursor != '\0') && (*cursor != ',')) + cursor++; + + char * item_end = cursor; + while ((item_end > item_start) && is_space_char (item_end[-1])) + item_end--; + + struct string_slice item_slice = { .str = item_start, .len = (int)(item_end - item_start) }; + if (item_slice.len > 0) { + enum SquareTypes parsed; + if (read_square_type_value (&item_slice, &parsed)) { + if (parsed == (enum SquareTypes)SQ_INVALID) + mask = all_square_types_mask (); + else + mask |= square_type_mask_bit (parsed); + entry_count += 1; + } else { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %.*s (invalid buildable_on entry)", line_number, item_slice.len, item_slice.str); + err->text[(sizeof err->text) - 1] = '\0'; + free (value_text); + return false; + } + } + + if (*cursor == ',') { + cursor++; + continue; + } + break; + } + } + + if (value_text != NULL) + free (value_text); + + if ((entry_count == 0) || (mask == 0)) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: buildable_on (expected at least one square type)", line_number); + err->text[(sizeof err->text) - 1] = '\0'; + return false; + } + + *out_mask = mask; + return true; +} + bool override_special_district_from_definition (struct parsed_district_definition * def, int section_start_line) { @@ -4942,6 +5041,8 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->gold_bonus = def->gold_bonus; if (def->has_shield_bonus) cfg->shield_bonus = def->shield_bonus; + if (def->has_buildable_on) + cfg->buildable_square_types_mask = def->buildable_square_types_mask; if (def->has_dependent_improvements) { for (int i = 0; i < ARRAY_LEN (cfg->dependent_improvements); i++) { @@ -5053,6 +5154,7 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.food_bonus = def->has_food_bonus ? def->food_bonus : 0; new_cfg.gold_bonus = def->has_gold_bonus ? def->gold_bonus : 0; new_cfg.shield_bonus = def->has_shield_bonus ? def->shield_bonus : 0; + new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); new_cfg.dependent_improvement_count = def->has_dependent_improvements ? def->dependent_improvement_count : 0; const int max_dependent_entries = ARRAY_LEN (is->district_configs[0].dependent_improvements); @@ -5180,7 +5282,7 @@ handle_district_definition_key (struct parsed_district_definition * def, &list_count, parse_errors, line_number, - "dependent_improvs")) { + "dependent_improvs")) { def->dependent_improvement_count = list_count; def->has_dependent_improvements = true; } else { @@ -5189,6 +5291,13 @@ handle_district_definition_key (struct parsed_district_definition * def, } free (value_text); + } else if (slice_matches_str (key, "buildable_on")) { + unsigned short mask; + if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number)) { + def->buildable_square_types_mask = mask; + def->has_buildable_on = true; + } + } else if (slice_matches_str (key, "allow_multiple")) { struct string_slice val_slice = *value; int ival; @@ -12091,8 +12200,7 @@ set_up_district_buttons (Main_GUI * this) Tile * tile = tile_at (selected_unit->Body.X, selected_unit->Body.Y); if ((tile == NULL) || (tile == p_null_tile) || (tile->CityID >= 0)) return; - int base_type = tile->vtable->m50_Get_Square_BaseType (tile); - if (base_type == SQ_Mountains || base_type == SQ_Forest || base_type == SQ_Jungle || base_type == SQ_Swamp || base_type == SQ_Volcano) return; + enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); if (tile->vtable->m21_Check_Crates (tile, __, 0)) return; if (tile->vtable->m20_Check_Pollution (tile, __, 0)) return; @@ -12171,6 +12279,8 @@ set_up_district_buttons (Main_GUI * this) int prereq_id = is->district_infos[dc].advance_prereq_id; if ((prereq_id >= 0) && !Leader_has_tech(&leaders[selected_unit->Body.CivID], __, prereq_id)) continue; + if (! district_is_buildable_on_square_type (&is->district_configs[dc], base_type)) + continue; // This district should be shown active_districts[active_count++] = dc; @@ -12797,11 +12907,14 @@ issue_district_worker_command (Unit * unit, int command) if (! is->current_config.enable_districts) return; + if (unit == NULL) + return; + int tile_x = unit->Body.X; int tile_y = unit->Body.Y; Tile * tile = tile_at (tile_x, tile_y); - int unit_type_id = unit->Body.UnitTypeID; - int unit_id = unit->Body.ID; + if ((tile == NULL) || (tile == p_null_tile)) + return; if (! is_worker(unit)) return; @@ -12824,80 +12937,77 @@ issue_district_worker_command (Unit * unit, int command) if (tile->vtable->m20_Check_Pollution (tile, __, 0)) return; enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType(tile); - if (base_type == SQ_Mountains || base_type == SQ_Forest || base_type == SQ_Jungle || base_type == SQ_Swamp || base_type == SQ_Volcano) { + if (base_type == SQ_Mountains || base_type == SQ_Forest || base_type == SQ_Jungle || base_type == SQ_Swamp) { return; } } if (tile != NULL && tile != p_null_tile) { - // If District will be replaced by another District - struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && district_is_complete(tile, inst->district_type)) { - int district_id = inst->district_type; - int inst_x, inst_y; - if (! district_instance_get_coords (inst, tile, &inst_x, &inst_y)) - return; + // If District will be replaced by another District + struct district_instance * inst = get_district_instance (tile); + if (inst != NULL && district_is_complete(tile, inst->district_type)) { + int existing_district_id = inst->district_type; + int inst_x, inst_y; + if (! district_instance_get_coords (inst, tile, &inst_x, &inst_y)) + return; - int civ_id = unit->Body.CivID; - bool redundant_district = district_instance_is_redundant (inst, tile); - bool would_lose_buildings = any_nearby_city_would_lose_district_benefits (district_id, civ_id, inst_x, inst_y); - if (redundant_district) - would_lose_buildings = false; + int civ_id = unit->Body.CivID; + bool redundant_district = district_instance_is_redundant (inst, tile); + bool would_lose_buildings = any_nearby_city_would_lose_district_benefits (existing_district_id, civ_id, inst_x, inst_y); + if (redundant_district) + would_lose_buildings = false; - bool remove_existing = false; - - PopupForm * popup = get_popup_form (); - set_popup_str_param (0, (char*)is->district_configs[district_id].name, -1, -1); - set_popup_str_param (1, (char*)is->district_configs[district_id].name, -1, -1); - popup->vtable->set_text_key_and_flags ( - popup, __, is->mod_script_path, - would_lose_buildings - ? "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT" - : "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT_SAFE", - -1, 0, 0, 0 - ); - - int sel = patch_show_popup (popup, __, 0, 0); - if (sel == 0) - remove_existing = true; - else - return; + bool remove_existing = false; + + PopupForm * popup = get_popup_form (); + set_popup_str_param (0, (char*)is->district_configs[existing_district_id].name, -1, -1); + set_popup_str_param (1, (char*)is->district_configs[existing_district_id].name, -1, -1); + popup->vtable->set_text_key_and_flags ( + popup, __, is->mod_script_path, + would_lose_buildings + ? "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT" + : "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT_SAFE", + -1, 0, 0, 0 + ); - if (remove_existing) { - remove_district_instance (tile); - tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); - tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, inst_x, inst_y); - handle_district_removed (tile, district_id, inst_x, inst_y, false); - } - } + int sel = patch_show_popup (popup, __, 0, 0); + if (sel == 0) + remove_existing = true; + else + return; + + if (remove_existing) { + remove_district_instance (tile); + tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, inst_x, inst_y); + handle_district_removed (tile, existing_district_id, inst_x, inst_y, false); + } + } - // If District will replace an improvement - if (itable_look_up (&is->command_id_to_district_id, command, &district_id)) { - unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); - unsigned int removable_flags = overlay_flags & 0xfc; + // If District will replace an improvement + unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); + unsigned int removable_flags = overlay_flags & 0xfc; - if (removable_flags != 0) { - PopupForm * popup = get_popup_form (); - set_popup_str_param (0, (char*)is->district_configs[district_id].name, -1, -1); - popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_CONFIRM_BUILD_DISTRICT_OVER_IMPROVEMENT", -1, 0, 0, 0); - int sel = patch_show_popup (popup, __, 0, 0); - if (sel != 0) - return; - } + if (removable_flags != 0) { + PopupForm * popup = get_popup_form (); + set_popup_str_param (0, (char*)is->district_configs[district_id].name, -1, -1); + popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_CONFIRM_BUILD_DISTRICT_OVER_IMPROVEMENT", -1, 0, 0, 0); + int sel = patch_show_popup (popup, __, 0, 0); + if (sel != 0) + return; + } - if (removable_flags != 0) - tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, removable_flags, tile_x, tile_y); - tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, tile_x, tile_y); - } + if (removable_flags != 0) + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, removable_flags, tile_x, tile_y); + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, tile_x, tile_y); - inst = ensure_district_instance (tile, district_id, tile_x, tile_y); - if (inst != NULL) - inst->state = DS_UNDER_CONSTRUCTION; + inst = ensure_district_instance (tile, district_id, tile_x, tile_y); + if (inst != NULL) + inst->state = DS_UNDER_CONSTRUCTION; - Unit_set_state(unit, __, UnitState_Build_Mines); - unit->Body.Job_ID = WJ_Build_Mines; - } + Unit_set_state (unit, __, UnitState_Build_Mines); + unit->Body.Job_ID = WJ_Build_Mines; } void @@ -23338,6 +23448,19 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i return false; } +int +get_port_district_variant_for_tile (Tile * tile) +{ + int variant = 0; + + int sheet_index = (tile->SquareParts >> 8) & 0xFF; + int sprite_index = tile->SquareParts & 0xFF; + + // TODO + + return variant; +} + void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { @@ -24464,17 +24587,5 @@ patch_Tile_m71_Check_Worker_Job (Tile * this) return Tile_m71_Check_Worker_Job (this); } -int __fastcall -patch_Tile_get_road_bonus (Tile * this) -{ - if (is->current_config.enable_natural_wonders) { - struct district_instance * inst = get_district_instance (this); - if ((inst != NULL) && (inst->district_type == NATURAL_WONDER_DISTRICT_ID)) { - return 0; - } - } - return Tile_get_road_bonus (this); -} - // TCC requires a main function be defined even though it's never used. int main () { return 0; } From 5160bd9abce3874aefdc5aaa0a5badd8231570b2 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 22 Nov 2025 08:20:06 -0800 Subject: [PATCH 006/356] Add to config --- C3X.h | 2 +- injected_code.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/C3X.h b/C3X.h index 520adba9..8c6ecb7a 100644 --- a/C3X.h +++ b/C3X.h @@ -664,7 +664,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", .advance_prereq = "Map Making", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, - .img_paths = {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, + .img_paths = {0},// {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 8, .max_building_index = 2, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 2, .gold_bonus = 2, .shield_bonus = 0, .defense_bonus_percent = 0 diff --git a/injected_code.c b/injected_code.c index 47b746d9..f9537cc7 100644 --- a/injected_code.c +++ b/injected_code.c @@ -10949,6 +10949,7 @@ patch_init_floating_point () {"enable_natural_wonders" , false, offsetof (struct c3x_config, enable_natural_wonders)}, {"enable_distribution_hub_districts" , false, offsetof (struct c3x_config, enable_distribution_hub_districts)}, {"enable_aerodrome_districts" , false, offsetof (struct c3x_config, enable_aerodrome_districts)}, + {"enable_port_districts" , false, offsetof (struct c3x_config, enable_port_districts)}, {"completed_wonder_districts_can_be_destroyed" , false, offsetof (struct c3x_config, completed_wonder_districts_can_be_destroyed)}, {"destroyed_wonders_can_be_built_again" , false, offsetof (struct c3x_config, destroyed_wonders_can_be_built_again)}, {"cities_with_mutual_district_receive_buildings" , false, offsetof (struct c3x_config, cities_with_mutual_district_receive_buildings)}, From cc544fca68c51c6343d9855efa270aa939fcafe6 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 26 Nov 2025 06:43:19 -0800 Subject: [PATCH 007/356] Move square type check --- injected_code.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/injected_code.c b/injected_code.c index 2690aa34..a009e66e 100644 --- a/injected_code.c +++ b/injected_code.c @@ -12202,8 +12202,6 @@ set_up_district_buttons (Main_GUI * this) if ((tile == NULL) || (tile == p_null_tile) || (tile->CityID >= 0)) return; enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); - if (! district_is_buildable_on_square_type (&is->district_configs[dc], base_type)) - continue; if (tile->vtable->m21_Check_Crates (tile, __, 0)) return; if (tile->vtable->m20_Check_Pollution (tile, __, 0)) return; From 90c7b801e6ed28e90033cc7d292c3e89217f97c7 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 26 Nov 2025 06:47:28 -0800 Subject: [PATCH 008/356] Finish rebase edits, compiling --- injected_code.c | 57 +++++++++++-------------------------------------- 1 file changed, 12 insertions(+), 45 deletions(-) diff --git a/injected_code.c b/injected_code.c index a009e66e..d0928eb4 100644 --- a/injected_code.c +++ b/injected_code.c @@ -12988,51 +12988,6 @@ issue_district_worker_command (Unit * unit, int command) unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); unsigned int removable_flags = overlay_flags & 0xfc; - // If District will be replaced by another District - struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && district_is_complete(tile, inst->district_type)) { - int existing_district_id = inst->district_type; - int inst_x, inst_y; - if (! district_instance_get_coords (inst, tile, &inst_x, &inst_y)) - return; - - int civ_id = unit->Body.CivID; - bool redundant_district = district_instance_is_redundant (inst, tile); - bool would_lose_buildings = any_nearby_city_would_lose_district_benefits (existing_district_id, civ_id, inst_x, inst_y); - if (redundant_district) - would_lose_buildings = false; - - bool remove_existing = false; - - PopupForm * popup = get_popup_form (); - set_popup_str_param (0, (char*)is->district_configs[existing_district_id].name, -1, -1); - set_popup_str_param (1, (char*)is->district_configs[existing_district_id].name, -1, -1); - popup->vtable->set_text_key_and_flags ( - popup, __, is->mod_script_path, - would_lose_buildings - ? "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT" - : "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT_SAFE", - -1, 0, 0, 0 - ); - - int sel = patch_show_popup (popup, __, 0, 0); - if (sel == 0) - remove_existing = true; - else - return; - - if (remove_existing) { - remove_district_instance (tile); - tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); - tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, inst_x, inst_y); - handle_district_removed (tile, existing_district_id, inst_x, inst_y, false); - } - } - - // If District will replace an improvement - unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); - unsigned int removable_flags = overlay_flags & 0xfc; - if (removable_flags != 0) { PopupForm * popup = get_popup_form (); set_popup_str_param (0, (char*)is->district_configs[district_id].name, -1, -1); @@ -24631,6 +24586,18 @@ patch_Tile_m71_Check_Worker_Job (Tile * this) return Tile_m71_Check_Worker_Job (this); } +int __fastcall +patch_Tile_get_road_bonus (Tile * this) +{ + if (is->current_config.enable_natural_wonders) { + struct district_instance * inst = get_district_instance (this); + if ((inst != NULL) && (inst->district_type == NATURAL_WONDER_DISTRICT_ID)) { + return 0; + } + } + return Tile_get_road_bonus (this); +} + PassBetweenValidity __fastcall patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int to_x, int to_y, int param_5) { From af954fd4a10cd1e0a69cc284145ebf61a54cf78e Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 26 Nov 2025 14:20:31 -0800 Subject: [PATCH 009/356] Workers can enter coast; working on art --- Art/Districts/1200/Port.PCX | Bin 11053 -> 0 bytes Art/Districts/1200/Port_NE.PCX | Bin 0 -> 8885 bytes Art/Districts/1200/Port_NW.PCX | Bin 0 -> 8117 bytes Art/Districts/1200/Port_SE.PCX | Bin 0 -> 8885 bytes Art/Districts/1200/Port_SW.PCX | Bin 0 -> 8885 bytes Art/Districts/WorkerDistrictButtonsNorm.pcx | Bin 29281 -> 29858 bytes C3X.h | 16 +- civ_prog_objects.csv | 1 + default.c3x_config.ini | 2 + injected_code.c | 177 ++++++++++++++++---- 10 files changed, 155 insertions(+), 41 deletions(-) delete mode 100644 Art/Districts/1200/Port.PCX create mode 100644 Art/Districts/1200/Port_NE.PCX create mode 100644 Art/Districts/1200/Port_NW.PCX create mode 100644 Art/Districts/1200/Port_SE.PCX create mode 100644 Art/Districts/1200/Port_SW.PCX diff --git a/Art/Districts/1200/Port.PCX b/Art/Districts/1200/Port.PCX deleted file mode 100644 index 985461602ab6bddc6054f745010f456633aab14c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11053 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 diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX new file mode 100644 index 0000000000000000000000000000000000000000..d9d9d15be531146383906202fe4efbdbf4e6cffb GIT binary patch literal 8885 zcmeHMeNa@_6_;o;X_Iz_W~S}%l6liGI}V~|m#;;aUAhoZRza4qBB6kbK`jIXg&2)! zrjjg56u%G&0g@0QtfAIKLQpg5dka#&1qBpTM1qK*1h;6AcyG_UXlva)o#v1Jk(S-r z-z?m7@4N4w-#O=Z-u~<-4o}U17j>XBW;nq6hrS$UJn_RP{qrJ}h?J6tD6qc>!~5hk zj7&28p5Z;f`|zIz`2Sso-!Z%cI7uc*Gc^mRDY;Erz~FBTrSB79+)B+W&6JFib{P7W z;Vr4eb{hBpAmi4{hLbLN#YQLom2l10fN=?0VQ46gxN$rvnC z6)m%<)J`Glax>UmC6~eG8-~*NF}Q38ElG+siYjABiI{2@V7!-H1-!!WGTvbS#k^T9K&|NA-JdWRKb@Dox)f~lBg9EG|_t5hN^ln zYbCb<&oFEO93%te0u@Ufy%q>oVJtIBo0$|V(>NNNz^swn0c>K}060MU;e;|oYm|(W zKjkHiWR_@CRk8fc#AdLoBliIR%J4K`Kj|YEsR*kI)+dr=8cwc-}o6TJh*jld}h#~JpJ3uK7G{@R5x#e5%~zfP`k6m>EFezSN5w<@=V z-Uho?ppk@Q3@?B)N2pK}mdJ}bxsD6>;rLl$&RiU~$`F(Ix`i4V=q<233zU)YCc`e$ z0kd8|B*rBAi#k4;^Ldif$V5)#$0f({y2Q=lPp)pFH^8 z3$8d{l!b_6!id@!YgAac8lF?M&}(4Z1KyW#fZ3@oQCBKLZF?4idi zG88`YwVkvR<_yy=h8+wWNxgInVx=~;$t4C^5b zh9CEuMHGN*S#8qXTfMXeEUmPaVKc)zQVX*l^O61mN zXm^T*T&HqBMY`YAaf6qvqPSHGQ+pe#h13T(IHFK#)Nr=K$YDW*0~XFn%iFu(Iw@Y+ z9z>CgHi}kCYwVt6J^6;M42frCk5RI+=cPtS8CxOpf!`1A$sP?P?+n@{nB={Fy9}0i z&}%n3B%buxjXa5VU3OzxqUBk;*+HUutKDoPg+im<+$BX>o!xXOMWMxRa_wW;*)~)6 zvqW#3^aohEuq_>ftmfF(pCMMbY>U`1D}AKLdFy|rob`9jwyVMyI2Q-_Z)big7-FfKZN&Cc;AKh zYIr8XdqTXAl$!cs!ie$a$nJ)N z85{IHb#I61g36J+gIh z)AD#Hb4R&UW{5JyM;O{;twg5UGot9g#4%vL-Kny{@7z-7k2q>w=%} zE6h0AWeSe;mU}xFS8q@+;{!rH-29x9-U@S3{_3}L9V1>1e9vs^8vlH2?yFvYuC?dC zcxkcraD7fyZ}B`&m!L>(mU&~*sRMJoXD9Anvm$MIxw-t!j5k5%dxfZyOEWf}%-Pyd zm_K+Tr}s1Os6`*&u6w7WT$>nf+L85M;rkzzl>F)F(c_hsCr_QS)YP=Kw_oY&YiMdZ q-`#!f`t>Dx{X9>lQti9%K$SBAVMkIvCV9=}t1Obgi zVoFJ76ct~9Kv-jl%8X%M6UhWMmF?y+;q9P;3JOXTpD50%QDWaceTikV+vh`Sw`%u; zL-C`y-F^GsbI<*sfA{?2XKv4qfe&#bW5&3_&rkihjd|v$U-aZAL=*@SACss5iIk7T zMmRD;`5om$;3H^hhW7iE-%{QKj))J$c4&7|{)6%k@By6J3GKHiZ&MBd?~8ZEE@;0_ zd4uvA@GkUl7TO0XuTWkFy2N4eJhTr`enWW?I1D{sg!Vqle#-N}TjEXeGPL(l_EMe& z-h?||gZ6I9)0ADn>*7^$2-;6jo}}yqUWJj|f%Xo{2I4bpgtU-* zl=ms`Quc^v;BN=HL+PR%raS{P?kBu+(`_;Y4JRN#5^qv=i>DxDd_QxI*!adqat&I$ zAtVy7Ql5gqd_!y+rsN8-CD|P0GBo!<6eM1vJRv&83#3dSn^v+)CN-KTYjz9}Ymz3r z3eLX>=k!4oBwnC&LQDrpRY94+xYseynr#9}Uw5AHCv8b8!N3`+EDmxW&Kw|8ga(Kd zjRDdjwuzSrF9^S8>lnSjxIJfy78w2{-*4<8_IOpDhVzKvtTsy{ISUsI5-C)JL<-9w zX@ljuMC^QTrB0|+z$o=0f--$<$XIucrI%EuC7J|{DWX!yw0%vw;EL;{oAMN8i`XPy zA$(V5p5Th(6skZ*$ppF!Zh_3$)E<(r%SsYB6Bof5Y|W$-uDC@`P&z4_ASJF#19$Pm zyz3cWP$g-nCMS#&nCDXU>06Z1r{UnTthD@BxjIw4t(COHWiFVn#5PKY*Z>*HJNY{P zIPY2;x6Ggz&$4?`$WBVW302YeIgUEFtttrdekLCuQ&MCy=p<95EAUpu*$_ntijzfp88EVEoYN9e(x!9PHx>6wPjK6Ip?9@Pgh+eH##8)O3 z2Wz=R4~`e)r`;evAQk~~Xc&4M;%}$~r8ZcZp3LY|bEC&CSy@l``lwV!(5v;z=s@Mq zRq-Z8w%sC>waBi9LmZJp-UV~M!I=_GY@M>6QEFL%tlT9eq=pE3CS4ggPN|)+)nwU} zE1wlj(1|q7F8-!Zd_ZxwNl-&-3Q8vZ!}R6p5I`8tR6!*~*aX4SCYzfr;&+7S)9PKt zksRyBOjV{QOwCBnNKTEK$S4Ejk|J##vOA)U-umx+lavP4ky*v+kSIo-oSp!AWz?Ir zKG|szW$m(Cu>&qW^f-H}^AzgjgypQqnuKI#)(S)pDbtWtE1F z)-u7eu2ohOyTxzGrm|TE1v{UaP;B7`+GWM{44hV`PSOU$?$2+R73Utv)UuUF8k~dY7f0Fl7l2ej*O_C3icSe%Qk+(gPnUS|Nl2MVjE0Xh&cOjCkkT(~SQINL_ zQpF#w`=jK^lgf`DlRScakCG9QABAL8zFoRT7qd0rd61> zVOof3C#JQSHe*_jX+K^q@FQ4X*uAl_Vspb{fyEAsHMTa`T4HODu3Fj_E#L zDexm$U)a5|v0`(39E)zy)X_J-KYrKS1MQ=4e4oDSb-=rH>AmlN_OAD74Z&6ahc)Es zvxwZ&=hHg!@81JGtt0Ry75!##{55MvtvP1;6qU0)rD;ceM?>wQLeuWGMyomYNOAJj zfirt|-t3d{#Evhn7eJUXPb-jvJ5?r_oDRbb=k9TI&*Ga{F{00;-)XRef637lMl0v z;q0yr#$&c6;j_I;%=0#{`BSDz6&Ewx5UwdHOj~LSJFsW9e$KR*C{E+A%1PDjFNscx zW7p^SS@)GV8#Yby{C&mN1+8DiBrjw#(_T4To!{0_)ZbO{QF+E!hvHwEKe2HA^o{1s zgjgSAzlJR;@Q#WMIP!V!C;OMo3!6O6=Y^JrH+4ZT&G3J*raGp2 zZ}_&2L4lfy1?IUy+>2qM%$AKacbEE%JgD8VZC*urNUeST8%rkb+&g_MwXN}ps&3~q5 zN<^furNCHJ9+sUuY4^SvtgoWxOW%D5=WPGLZ+(GkcBp@K&7Th)d)1U2=g&QFF7lqr zG8;?gFcW|I?iOuOuy5}A*p9)~3sXF}u;0vG^!$>yrY&CO>Feixpw^PJ$=_TatB>`J zSvI+*z9eOZzS@x!96s55#;>+lYZ1bd{t8|CHyyFX4= z#JoPI%x3Ps``J51ulogiH=O=_)?D4*rb2t)?kUr~!eVt6+p51D+c_z~Gj+?-McE6j zHtXAYZ-dYGi_oO+%UgA{@SWz0(!nEzeV+!zg?@a;@%|~RE;ZV`!BST7!AF&qe?55c za81q8W5??1>z$pQ{TD7Yx3-=>d-n2`EAtG7DbxM^xuESki*DT>Hpax7-pB|I4U--s K@Xr_`{rnS}lF^R< literal 0 HcmV?d00001 diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX new file mode 100644 index 0000000000000000000000000000000000000000..d9d9d15be531146383906202fe4efbdbf4e6cffb GIT binary patch literal 8885 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 UNzY2&gUb>Kre{8WWu;d6eButton.vtable->m01_Show_Enabled ((Base_Form *)&free_button->Button, __, 0); } -bool __fastcall patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value); -bool __fastcall patch_Unit_can_pillage (Unit * this, int edx, int tile_x, int tile_y); - void init_district_command_buttons () { @@ -12188,6 +12188,56 @@ compute_highlighted_worker_tiles_for_districts () } } +bool +can_build_district_on_tile (Unit * unit, Tile * tile, int district_id, enum SquareTypes base_type) +{ + if ((! is->current_config.enable_districts) || + (unit == NULL) || + (tile == NULL) || (tile == p_null_tile) || + (! is_worker (unit)) || + (tile->CityID >= 0) || + tile->vtable->m21_Check_Crates (tile, __, 0) || + tile->vtable->m20_Check_Pollution (tile, __, 0) || + (district_id < 0) || (district_id >= is->district_count)) + return false; + + struct district_config const * cfg = &is->district_configs[district_id]; + if (cfg->command == -1) + return false; + + if ((cfg->command == UCV_Build_Neighborhood) && !is->current_config.enable_neighborhood_districts) return false; + if ((cfg->command == UCV_Build_WonderDistrict) && !is->current_config.enable_wonder_districts) return false; + if ((cfg->command == UCV_Build_DistributionHub) && !is->current_config.enable_distribution_hub_districts) return false; + if ((cfg->command == UCV_Build_Aerodrome) && !is->current_config.enable_aerodrome_districts) return false; + if ((cfg->command == UCV_Build_Port) && !is->current_config.enable_port_districts) return false; + + if (! district_is_buildable_on_square_type (cfg, base_type)) + return false; + + int prereq_id = is->district_infos[district_id].advance_prereq_id; + if ((prereq_id >= 0) && !Leader_has_tech (&leaders[unit->Body.CivID], __, prereq_id)) + return false; + + struct district_instance * existing_inst = get_district_instance (tile); + int existing_district_id = (existing_inst != NULL) ? existing_inst->district_type : -1; + bool district_completed = district_is_complete (tile, existing_district_id); + + if ((existing_district_id == district_id) && district_completed) + return false; + if ((existing_district_id >= 0) && (existing_district_id != district_id) && (! district_completed)) + return false; + + if (! cfg->allow_multiple) { + FOR_TILES_AROUND(tai, is->workable_tile_count, unit->Body.X, unit->Body.Y) { + struct district_instance * nearby_inst = get_district_instance (tai.tile); + if ((nearby_inst != NULL) && (nearby_inst->district_type == district_id)) + return false; + } + } + + return true; +} + void set_up_district_buttons (Main_GUI * this) { @@ -12232,16 +12282,19 @@ set_up_district_buttons (Main_GUI * this) struct district_instance * existing_inst = get_district_instance (tile); if (existing_inst != NULL) { existing_district_id = existing_inst->district_type; - if (patch_Unit_can_perform_command(selected_unit, __, UCV_Build_Mine)) { - for (int n = 0; n < 42; n++) { - if (this->Unit_Command_Buttons[n].Command == UCV_Build_Mine) { - Command_Button * mine_button = &this->Unit_Command_Buttons[n]; - if ((mine_button->Button.Base_Data.Status2 & 1) == 0) { - mine_button->Button.field_5FC[13] = 0; - mine_button->Button.vtable->m01_Show_Enabled ((Base_Form *)&mine_button->Button, __, 0); - } + + for (int n = 0; n < 42; n++) { + if (this->Unit_Command_Buttons[n].Command == UCV_Build_Mine) { + Command_Button * mine_button = &this->Unit_Command_Buttons[n]; + if (base_type == SQ_Coast) { + mine_button->Button.vtable->m02_Show_Disabled ((Base_Form *)&mine_button->Button); break; } + if ((mine_button->Button.Base_Data.Status2 & 1) == 0) { + mine_button->Button.field_5FC[13] = 0; + mine_button->Button.vtable->m01_Show_Enabled ((Base_Form *)&mine_button->Button, __, 0); + } + break; } } } @@ -12257,30 +12310,10 @@ set_up_district_buttons (Main_GUI * this) if (is->district_configs[dc].command == -1) continue; - if ((is->district_configs[dc].command == UCV_Build_Neighborhood) && !is->current_config.enable_neighborhood_districts) continue; - if ((is->district_configs[dc].command == UCV_Build_WonderDistrict) && !is->current_config.enable_wonder_districts) continue; - if ((is->district_configs[dc].command == UCV_Build_DistributionHub) && !is->current_config.enable_distribution_hub_districts) continue; - if ((is->district_configs[dc].command == UCV_Build_Aerodrome) && !is->current_config.enable_aerodrome_districts) continue; - if (existing_district_id == dc && district_completed) continue; if ((existing_district_id >= 0) && (existing_district_id != dc) && (! district_completed)) continue; - if (!is->district_configs[dc].allow_multiple) { - bool found_same_district_nearby = false; - FOR_TILES_AROUND(tai, is->workable_tile_count, selected_unit->Body.X, selected_unit->Body.Y) { - struct district_instance * nearby_inst = get_district_instance (tai.tile); - if (nearby_inst != NULL && nearby_inst->district_type == dc) { - found_same_district_nearby = true; - break; - } - } - if (found_same_district_nearby) - continue; - } - - int prereq_id = is->district_infos[dc].advance_prereq_id; - if ((prereq_id >= 0) && !Leader_has_tech(&leaders[selected_unit->Body.CivID], __, prereq_id)) continue; - if (! district_is_buildable_on_square_type (&is->district_configs[dc], base_type)) + if (! can_build_district_on_tile (selected_unit, tile, dc, base_type)) continue; // This district should be shown @@ -12720,6 +12753,8 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) { if (is->current_config.enable_districts) { Tile * tile = tile_at (this->Body.X, this->Body.Y); + enum SquareTypes base_type = (tile != NULL && tile != p_null_tile) ? tile->vtable->m50_Get_Square_BaseType (tile) : SQ_INVALID; + if ((tile != NULL) && (tile != p_null_tile) && is->current_config.enable_natural_wonders) { struct district_instance * inst = get_district_instance (tile); if ((inst != NULL) && @@ -12729,13 +12764,16 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) return false; } } - enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); if (is_district_command (unit_command_value)) { - return (base_type != SQ_Mountains && base_type != SQ_Forest && base_type != SQ_Jungle && base_type != SQ_Swamp); + int district_id; + if (! itable_look_up (&is->command_id_to_district_id, unit_command_value, &district_id)) + return false; + + return can_build_district_on_tile (this, tile, district_id, base_type); } else if (unit_command_value == UCV_Build_Mine) { - bool has_district = (get_district_instance (tile) != NULL); + bool has_district = (tile != NULL) && (tile != p_null_tile) && (get_district_instance (tile) != NULL); if (has_district) { return (base_type != SQ_FloodPlain && base_type != SQ_Forest && base_type != SQ_Jungle); @@ -13616,6 +13654,19 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, { AdjacentMoveValidity base_validity = Unit_can_move_to_adjacent_tile (this, __, neighbor_index, param_2); + // Let workers step onto coast tiles when the config flag is enabled (base logic treats this as an invalid sea move) + if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + is_worker (this) && + ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { + int nx, ny; + get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); + Tile * dest = tile_at (nx, ny); + if ((dest != NULL) && + dest->vtable->m35_Check_Is_Water (dest) && + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) + base_validity = AMV_OK; + } + // Apply unit count per tile limit enum UnitTypeClasses class = p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class; if ((base_validity == AMV_OK) && (is->current_config.limit_units_per_tile[class] > 0)) { @@ -19007,6 +19058,7 @@ patch_Trade_Net_get_move_cost_after_zoc (Trade_Net * this, int edx, int from_x, patch_Trade_Net_get_movement_cost (this, __, from_x, from_y, to_x, to_y, unit, civ_id, param_7, neighbor_index, dist_info) : -1; } + AdjacentMoveValidity __fastcall patch_Unit_can_move_after_zoc (Unit * this, int edx, int neighbor_index, int param_2) { @@ -19036,11 +19088,41 @@ patch_Unit_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, bool { is->moving_unit_to_adjacent_tile = true; + bool const allow_worker_coast = is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && is_worker (this); + bool coast_override_active = false; + enum UnitStateType prev_state = this->Body.UnitState; + int prev_container = this->Body.Container_Unit; + + if (allow_worker_coast) { + int nx, ny; + get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); + Tile * dest = tile_at (nx, ny); + if ((dest != NULL) && + dest->vtable->m35_Check_Is_Water (dest) && + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) { + coast_override_active = true; + is->coast_walk_unit = this; + is->coast_walk_transport_override = false; + is->coast_walk_prev_state = prev_state; + is->coast_walk_prev_container = prev_container; + } + } + is->zoc_interceptor = is->zoc_defender = NULL; int tr = Unit_move_to_adjacent_tile (this, __, neighbor_index, param_2, param_3, param_4); if ((this == is->zoc_defender) && check_life_after_zoc (this, is->zoc_interceptor)) tr = ! is_online_game (); // This is what the original method returns when the unit was destroyed in combat + if (coast_override_active) { + is->coast_walk_unit = NULL; + is->coast_walk_transport_override = false; + + if (this->Body.Container_Unit == this->Body.ID) { + this->Body.Container_Unit = is->coast_walk_prev_container; + Unit_set_state (this, __, is->coast_walk_prev_state); + } + } + is->temporarily_disallow_lethal_zoc = false; is->moving_unit_to_adjacent_tile = false; return tr; @@ -23463,6 +23545,7 @@ get_port_district_variant_for_tile (Tile * tile) void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -24603,16 +24686,36 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t { PassBetweenValidity base = Unit_can_pass_between (this, __, from_x, from_y, to_x, to_y, param_5); - if (base != PBV_OK && is->current_config.enable_port_districts && is_worker(this)) { + if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + base != PBV_OK && is_worker(this)) { Tile * dest = tile_at (to_x, to_y); if ((dest != NULL) && dest->vtable->m35_Check_Is_Water (dest) && (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) - return PBV_OK; // Let workers treat coast as passable when port districts are on + return PBV_OK; // Let workers treat coast as passable when allow_workers_to_enter_coast is on } return base; } +Unit * __fastcall +patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool do_show_popup) +{ + Unit * transport = Unit_select_transport (this, __, tile_x, tile_y, do_show_popup); + + if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + (transport == NULL) && (this == is->coast_walk_unit) && is_worker (this)) { + Tile * dest = tile_at (tile_x, tile_y); + if ((dest != NULL) && + dest->vtable->m35_Check_Is_Water (dest) && + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) { + is->coast_walk_transport_override = true; + return this; // Fake a transport so the move logic proceeds + } + } + + return transport; +} + // TCC requires a main function be defined even though it's never used. int main () { return 0; } From 2ffc45cc28959eb38e74c441cd6f0744be4bb9ba Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 26 Nov 2025 16:15:02 -0800 Subject: [PATCH 010/356] Update port art with better lighthouse; Rudimentary variant & pixel setting function working --- Art/Districts/1200/Port_NE.PCX | Bin 8885 -> 8341 bytes Art/Districts/1200/Port_NW.PCX | Bin 8117 -> 8428 bytes Art/Districts/1200/Port_SE.PCX | Bin 8885 -> 8433 bytes Art/Districts/1200/Port_SW.PCX | Bin 8885 -> 8433 bytes C3X.h | 6 +- injected_code.c | 159 +++++++++++++++++++++++++++++++-- 6 files changed, 153 insertions(+), 12 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index d9d9d15be531146383906202fe4efbdbf4e6cffb..782ef2635f96ecfb9b89b0ab9e071d40ffe27953 100644 GIT binary patch delta 1549 zcmZ9MUu;uV9LLkFl>gXzxw~u^xS2O~qEK%uG2q(9ZZm1_T?U=oBF=TaUE;%<7RbF~ zqCtf;rY{=E3B@Ps(x9vfPiXdhPV2_T{*_H84g?YTyRnptfXexKI^zSpd|uA^ocw;@ z^E>DJw~hslBv<@IPs9g+UPt{B=XgSgV20xR@k~fkQb4aB?dMlb)41u|>jHWO=^VFp z5~kP=Bj61Ky-X(n=pReG&-j==q|-1#GluDvfzF}8D$X+t=cq{{raj2*0Xj=BFlBy; zcNibgGM$67bbx%^tav|^V?cjInB&}6V3?*ex@(W@c^l|7{e@}Qmw1h_L>K8b7@}Gv zooxxq-vTY762~hA+NqgoyT{I)NqU1hmzMZ5;{v@)?*OJ|vuR{yuD%ZFII3IEy}AG< zF-&K@D%*jcqxYCKv&8d^x9Lr~2wFxnk}8qwfsUa4tGR6@AZ9YtnXf3r^iQUYEpe3b zclsMGgO)ctw)WNU2 zOdqqtG$So@VYz_nR`BcQ=-;53&BT=LTyO~W3;f!P^a*s5NTxHGm(QYrShLF1lBQ1% z*4sx>msqn7L&nsyruL$2|H5?+x%nD9292bfwTO}Bcw|lxDBxK_~I%&{bvt)m6?CB^(f1Y5#Bt=;T!pq~33>XpO z(|xVJP;T_uKyV2Y)++(PtO@>=VLA$1FzGYm$#;Euy*r17pN&S}j_;kU><`!l*D&D4 z9m&2x9|`#UyLNXC5=a~-(9|X=(L`ABCOTBz@2S-OPDODn7d95)8+-wm4#r7<^!d9( zPGd0DP9!{ouXdo!a{@YFUA>jS>x1}GHrW+O#(SK8hb|_nnyRb~Dfb7B|AmJ~aG8)# zDyN2)3oqp%dk~kC*}(pW9!ZqER$}o=Rj~%YkA;WlPh!Fn#52I=On89+DdW;kT<(z~ zepfVXBht#4n)p&|7rS**1|V!`h1vpq+SM0wtZ`c#MN5@MB4S(k_z}Ibd}xy(46$p- z`kgxKQWCAA^xjt;7;;b88{s$1vcE9vcq7oQi<0{JKDDYzNe$rQUnnjJ)kA>GEcCIH z3Y*NPMyuo&dyhRr#Wj5G9A$}?cPf&#O%|hKZ$~(GfWYAnevv&_p@y0B_#U2j64Bo% zCZduW`>5Ua{(%Mdz6A$U=Y4vQ?#_z^N%Xt#vX4OEn6u#N-jOzSqOfF$DBVU2f?zKI z#_V$&YX6vaQqAYD9=hIE_s)!(_Dqb_NiQTIoxGXG7@wIkjaAM_qiRvgj?-^7ix;6wKwQ}9P{2dYz98lhuaG8UR#UM0Dp)OMfI1)=viI8eJWbE zVggv<7BO0+XMbQomEKw>ezp&L6sj2r>+ekH-hO#{5z0j}-zehy?$3;OyA4&PC#?7D zvL;YH)gP_H3OapZjdZn3*k-U2&tR&{IvNTiTo*rx(&%&|l{ss%#OLJW7OBf-XD`xJ zrl0EAMBxUcg(%XZ$V_8B^X5>)%S9siYp=`?&2f`=;A%ZIG(f9htHy|-SZD@K@DLuRUHB0Z7L#1CQ=9tInn>#)Tq@$2E_vDq^Ql`+CJ{K`6E0kM1CtuW3Zj^(tO z;xrPvp#kN_spZU_0LZR5vxBPQ!Q%**$^3rNTXHMpnR+Q`F?a;_b@&ukYpkvePR>Tn zsJuy(R_RcMJ%OSt@_1TH8b|5ekyW}A1|FX|O99lu7fDAC^R#*sr2JjeA{Sl=KG*Wt2`u+6!nCzYmkxiXG}J38;V_*vdc@?z;8y4V5Q*W5gmr^Bmg-48m}yrB_`rhZ2! zHb<5LqrwWh=m*{&Bsyf`?7^F8v;%alx>fSQuo!($$St80n*`I!gbxY>`xg-3v9X}( zrk#ij-!#zZ=6phfCNse7pF@w^Hw54?G;D$G-~Ain{hI(WV5naem@DYlO@?M^Mo}d~ z|15g!1D&fbfkkV=5g39#ZwK2}UCn_LLN3mrOTL~BMU6mtZ@VbZa8+c(ltX)(g5E3bi7X zg?K-63+1=&WM`=bH5+Ps1?IExGR61#XGrP>J+o9J=CGz4HY=oZ>N2fG_;b(D$v$q+ z4fisIrftA9F%oQ8NM+f1S|;@LF)7IB4q$p5Jwan_+}=g^Dm9>O=q51+a$$if@j`=E zJ}ncz+2Pwy)d(%jJVtmsC(XOpsHN!>rlFZum_6)Sm1s~*h+lGemGE0SN(Ts!Z{hpn zcW4b?xs8%t;I#&IpypVnD&^S2DRqw4@tPeYcEG!cIz)nnrlF?hNXgsKYb)pm%XMFK z7pazshzZNig`=KZ4fI_T=(tU*dIVNHQeX$zzDx&rg^ja0CN36@OBCO#=^>Jo zlaxohP<=xPpdVUx^$?2J@zoFWBu1u`$z;f=6I#9LozEfK(gVc7FEToQ?kgUO z74zwk63A35rTP+jz4g`J=uF|`EH4oyRHaz8p_fkrdQuuq`y13BY=Zl;^y! z;?Rp$f6uY=B$XQ?DqqO*twF_iCQ~~7{&nyF!LAn+-@p!D`?BzLVY)!nlqVD>Au>`U zq}1@z1h^M8SV)DBWRGNX`vg81J*1D-7Q8?L?jO337qf+w7%hye8D#+GN6PbFin-o9 z?sFOtv#DvR?|3T955E&I>XmAxffn09_Y?YtI4He4s7m5=Rw+)x3VPbs%RZuSOQIA} zTu7w_uO;8>v#8FIE5EB z1@S%M@a-$Dxu&K?9Pxx&KT@)YzEYP=Pvb3Xg*Bh3tDs9?sGD|Y@CeU~3ylxd0XjJ@ zizq)p=n1?;%V}k-#PG#V0WHzeIR}t;3avil0yQVzQS!n~has-UOZE(Ic!aH=DFJAg zr;q`t$!`w;A}`3|;2T;=gr^iW7*orxJ&orB!nV(}Lf#g2C~{2C=mF96^mx3A{gxIB zsSGHYSP_o6k8z*Kd!97D;G15d{SCGB(KM(THKxYhr?}=7c6?irTqZxs@+t!1v=+%G zeSrLV!#pnzyk=l-v|t@nr!on57O!jJJ&zi7ytg^j{)$>TJqNm~?e80^RPm3s0zTa= zb}Ufxp3IyqfDSn=5f6`6fR^X*)eSs;(A?EorFY0Ysgvb4Ye=^Wsj$Xn$|bu}!@rt$ zg=Q&PATLRMXOj$?ZWZ+-N{W*WB~GA*cl(r$(^QT`jUGeNxI{5^v0GD9L6yJ?zknUz z=HNq09`@%E7?L3lM8%eHBEyYUY;c~j33j-Kx(DBQV%=dtSOfl*cwYCcxd-C|o zg4;KOh|H_zun9(z(L^8&F&Ds|V|MZQaLc-YXeJ{g*^z9%zYB@cp>&^HV>S!;dJ7lm zM^e^IN>Ovr$Y?vq3Nfe3&c%1t8vp366_;7nZ=B?$Y;sr*o=9fVzN6e|rBp7>VgKqa z0W!Ns>Xwgn8?rQ<)vUx=sm9X7cRpwR%$oWkS&|h^?H%mpz`7<)%(3hVfxq;O79Ji1 zO%~&4%MRERvn-_i`d5uVbkf-u=j2}0cF`tFRh(`Y*8HK4l-9c0;jJpZ&kL=;sWToe cO%Up^zL4PkO)pom?iX(RJ3_127aZXK0}^9z8vp3k;& zc|-lF5!`bIg<}CEf&}L&<9rJ=_f>qGa>?W(GS=fAGOCpub#yqZo!VmWT_WSSFFrsp!mBjtjL9j=J9rr<{YnIR zWpAT5log_hu;59wh_VkNxCW6?=(|R!{PlPy1ccPzc%8;injEKGq45xPDe>gXUPErr zcDr{T3B%lp`qyk|tq&IZZjn(O?^V6suD|eo8aih3GUX4&rMvuH$nW*-+ut^16Wu~L zyaC$f=TP6C6>^a-s`x$O2lxSvyJT{N@)q?0t{sgdKkD+ehn&ighihnQ$ku|>cgY2u zQB)8A2gWpJ*d)SOAUCOAfr>iQ{+30D7xl@Vw5)*6DH=$wQNWnq0=; zVJ;DN9f}?b(Sqm zwyu!vF}Jmjvy@r@;+n#zZxR(Ls$0hn7NNKIy14W1d%AY-rj7LSp)>FhjhC^bP+RQ432@%|2TbnFX(UbjG< z6}Y+1kC%vKsI7b~$XwDw8|~Fs=p{W5m*C`31kTaz(kwLsur~2rl$9fPs-eR96 zs)W=GVtD4VmMgbEJBsrpg~(7*H(Z5zC*pi{Tp|j@*qhB|ulCH6eGy3=U=4muvsF5t zTqp24oTIApUb#bd8{9D9d~TW+NlKb$!~D4R`36VHefk1f*{vLyC6Zc?RGwjjlbWU6 zvGXB;Fg{#J@AtYbz{SmxRusv!1`U2nJ5^=}klvc}@EbTTv}#LnOIYXvpX>mxmfL zLhDtrWrkD(9~)$2Opw1o>tFHcG;s;OPQl|kJv;~;6D5OL_M?))cPh_YHW2!rpuw}Y HO}2jk2Elaa delta 2286 zcmeH{T}T{P6vx?WY=cHw%{X=Ml1niAP(o$K1W9mb%B;=HAl4OyP`j(Mfws8psx=Lz z)TDu>_*5*H+CV`XBqc87AvGnhJ$Folbv14<=A+h>G)+^TD#K9dL%FwiHf>3L=xZtX za1X=G`Ja#9xpQ3O%VXn(T?;%9Uw^b%zv~9i_vcCV^Yv$z_;i2k$%}c;HrNX1%qiJ* zVSi;FTjH>s?~l;97QZ;g*-z}AaCuy^kA(G@%Wm?kiHO~*g*vq9yzqIz)`ZW)lJ%cm zw#9DosZ5_mTdj^MI-Cdb{XTmj?Dvc~l~pyjdCnN=r<$d8Xz{#Fj9NK(n^(R7bYLWt zdsVZoME)8HR>^gDco}RR$qg6-)X?--I(x5PC&RUJ{T;62&tV1J(F`@{fS!m)My}2g zqe`y7%js0GrZ1ugtpYh;E6exzDD`?HNO`UWoZOT#+}elB(hFp`N)E4b zDiq*7lZog^i3@!n-kmnHAENF8Db~vS);JWwh9&UM^bW+WC<;&|P!_-xbtOMpAi;VG zf5(2{U%YdOdQ%xg2d`R*W-%&9__cFA!1MFurLc71TlOOds?p=`2gC^eKycQs-Z?F9$CE3hi2 z@k?{!$izvDDi~oJed7i>Und{j;DSUKR#Mrd;L&4RI#AI#wY|w>0GU~DqEk}?co5+t zF(j#Zfo<>-l;ofqdM}y6FTWL}?j(4VHis>6uE19?=hM5?IWiH;#*}3;h@^w_LiRT( zQ&k32dcrzPClAf>%YU)HG^$3GH&@73oqRx)^)$Nnp7u#_$F0RDQ(pZP3P)E7?2+-t zc6v4iF~PB-@40!O>_<}gnn=Elp5LM%Lw_S@L$bWF9h!9*HZS~6tZE5?5V!pUP_tjj zan6AZ@0G*rJgB6eg5gK?&lu-aI7kgPd(4%X`gZI|+k%cq>1obw5nc{G;-VBsZPhi= zBRCg;ibi4C7oQDDd$0TVu@sR~l(4AB`K;(ku6bC}iJ!t5xBPdoM+(l13w~PMTQTuJ z^KaEjGi%iqftjK3`vDTJY~R^AJ3D7*=lq{{PBA3!1NN_5rxov~? F{{WxjxyS$j diff --git a/Art/Districts/1200/Port_SW.PCX b/Art/Districts/1200/Port_SW.PCX index d9d9d15be531146383906202fe4efbdbf4e6cffb..8a88cd34da5b6f4825c76420d256413ebfac917b 100644 GIT binary patch delta 567 zcmWMhO=}ZD7{;ivvYYI*a|nVGbcpfLE}LjAXj%zGSuz1Bg_U@STYKwTJj{Rx1wY84 z7ZH461rhhK2dN+wVt+tp+geImlGraq6ssOQ>JP~49-nWXmFs`Y%SRK*gTdQ{r4BgS zsJ1s~|62&Av)1AU6cVFD`-25JdEfj5%i7q<{a6&`QezFiX~kFygeF%sR>=7tVj_unm_vkZpFkZN+o12UuEPwyhxSxb>smT&8lMguh2`CwGC9V88{Im zU!Z;LhPH`3k`D81asao}*Yplbu4JGHBYW^VeMQ^Gn=Y42G-5ljn;zG@@dOt>qYc&v z&9L-7a#>N55o&j#WQ=J$=qNXE?1*i{ype^8B++-`+NPXjcAjj(8{-_zCrP@8f#jj& z=NWl!cA#gBkuBuPuW(m{lr_9+2hgxaHc|Sakav`*QIj?9(hgYU0^L9%L*e>L6g`uX z#VqSUo#f~`2HbUg!TrFbbMf=q7Ca`oku{7Yj{?71@aSBliuf6(Ph>_`kEr}vGv=HBUQS*J_&_)Ci}AMGCrH!#clv}%NceJ#wU~6IaGlz zvgc6U{D6;zadUx81LI^FZl8^3%ozvH)snCOI|s=dB;~~!*yM$JCE6Js{5T;o;{X4H flP^k%Gavj1@{iKw8&X0<_-S&14BO^c{Op1Na}ApE diff --git a/C3X.h b/C3X.h index 4de4cc59..e3536434 100644 --- a/C3X.h +++ b/C3X.h @@ -666,10 +666,10 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", .advance_prereq = "", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, - .img_paths = {"Port_NW.pcx"},// {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, + .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .buildable_square_types_mask = (1 << SQ_Coast), - .img_path_count = 8, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 2, .gold_bonus = 2, .shield_bonus = 0, .defense_bonus_percent = 0 + .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 } }; diff --git a/injected_code.c b/injected_code.c index 62218c92..62885d11 100644 --- a/injected_code.c +++ b/injected_code.c @@ -13701,7 +13701,17 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr (unit == NULL) && ((flags == 0x1009) || (flags == 0x9))) // this call can be accelerated by TNX return is->get_move_cost_for_sea_trade (this, is->tnx_cache, from_x, from_y, to_x, to_y, civ_id, flags, neighbor_index, dist_info); - int const base_cost = Trade_Net_get_movement_cost (this, __, from_x, from_y, to_x, to_y, unit, civ_id, flags, neighbor_index, dist_info); + int base_cost = Trade_Net_get_movement_cost (this, __, from_x, from_y, to_x, to_y, unit, civ_id, flags, neighbor_index, dist_info); + + // Let the pathfinder consider coastal tiles reachable for workers when the config flag is on + if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + (base_cost < 0) && (unit != NULL) && is_worker (unit)) { + Tile * dest = tile_at (to_x, to_y); + if ((dest != NULL) && + dest->vtable->m35_Check_Is_Water (dest) && + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) + base_cost = Unit_get_max_move_points (unit); + } // Apply unit count per tile limit if ((unit != NULL) && ! is_below_stack_limit (tile_at (to_x, to_y), unit->Body.CivID, p_bic_data->UnitTypes[unit->Body.UnitTypeID].Unit_Class)) @@ -23529,23 +23539,146 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i return false; } -int -get_port_district_variant_for_tile (Tile * tile) +void +get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) { - int variant = 0; + int NW = 0; + int NE = 1; + int SE = 2; + int SW = 3; int sheet_index = (tile->SquareParts >> 8) & 0xFF; - int sprite_index = tile->SquareParts & 0xFF; + int sprite_index = tile->SquareParts & 0xFF; + + char ss[200]; + snprintf (ss, sizeof ss, "Port district has sheet index %d, sprite index %d\n", sheet_index, sprite_index); + (*p_OutputDebugStringA) (ss); + + if ((tile == NULL) || (tile == p_null_tile) || (out_variant == NULL)) + return; + + int owner_id = tile->Territory_OwnerID; + if (owner_id <= 0) + return; + + struct district_instance * inst = get_district_instance (tile); + if (inst == NULL) + return; + + int tile_x = inst->tile_x; + int tile_y = inst->tile_y; + Map * map = &p_bic_data->Map; + + City * closest_city = NULL; + int closest_dx = 0, closest_dy = 0; + int city_work_radius = is->current_config.city_work_radius; + + for (int ring = 1; (ring <= city_work_radius) && (closest_city == NULL); ring++) { + int start_ni = workable_tile_counts[ring - 1]; + int end_ni = workable_tile_counts[ring]; - // TODO + for (int ni = start_ni; ni < end_ni; ni++) { + int dx, dy; + patch_ni_to_diff_for_work_area (ni, &dx, &dy); + int tx = tile_x + dx; + int ty = tile_y + dy; + wrap_tile_coords (map, &tx, &ty); - return variant; + Tile * t = tile_at (tx, ty); + if ((t == NULL) || (t == p_null_tile)) + continue; + + int city_id = t->vtable->m45_Get_City_ID (t); + City * city = get_city_ptr (city_id); + if ((city != NULL) && (city->Body.CivID == owner_id)) { + int ndx = city->Body.X - tile_x; + int ndy = city->Body.Y - tile_y; + + if (map->Flags & 1) { + int half_width = map->Width / 2; + if (ndx > half_width) + ndx -= map->Width; + else if (ndx < -half_width) + ndx += map->Width; + } + if (map->Flags & 2) { + int half_height = map->Height / 2; + if (ndy > half_height) + ndy -= map->Height; + else if (ndy < -half_height) + ndy += map->Height; + } + + closest_city = city; + closest_dx = ndx; + closest_dy = ndy; + break; + } + } + } + + if (closest_city == NULL) + return; + + // Check the four diagonal neighbors explicitly + if ((closest_dx == 1) && (closest_dy == -1)) { + *out_variant = SW; + return; + } else if ((closest_dx == -1) && (closest_dy == -1)) { + *out_variant = SE; + return; + } else if ((closest_dx == 1) && (closest_dy == 1)) { + *out_variant = NW; + return; + } else if ((closest_dx == -1) && (closest_dy == 1)) { + *out_variant = NE; + return; + } + + // Otherwise, face roughly away from the city based on its relative position + int face_dx = -closest_dx; + int face_dy = -closest_dy; + int abs_face_dx = int_abs (face_dx); + int abs_face_dy = int_abs (face_dy); + + if ((abs_face_dx == 0) && (abs_face_dy == 0)) + return; + + if (abs_face_dx > abs_face_dy) { + if (face_dx > 0) + *out_variant = (face_dy >= 0) ? 2 : 1; // city is west; face east (toward +x), bias south if city is south, else bias north + else + *out_variant = (face_dy >= 0) ? 3 : 0; // city is east; face west (toward -x), bias south if city is south, else bias north + } else if (abs_face_dy > abs_face_dx) { + if (face_dy > 0) + *out_variant = (face_dx >= 0) ? 2 : 3; // city is north; face south (toward +y), bias east if city is east, else bias west + else + *out_variant = (face_dx >= 0) ? 1 : 0; // city is south; face north (toward -y), bias east if city is east, else bias west + } else { + if (face_dx >= 0) + *out_variant = (face_dy >= 0) ? 2 : 1; // city northwest/southwest-ish; pick the closer diagonal + else + *out_variant = (face_dy >= 0) ? 3 : 0; // city northeast/southeast-ish; pick the closer diagonal + } + +finalize: + + if (out_pixel_y != NULL) { + if ((*out_variant == 0) || (*out_variant == 1)) { + *out_pixel_y += 16; // Facing north-ish, nudge up + } else if ((*out_variant == 2) || (*out_variant == 3)) { + *out_pixel_y -= 16; // Facing south-ish, nudge down + if (*out_variant == 2) + *out_pixel_x += 8; // Facing southeast, nudge right + else + *out_pixel_x -= 8; // Facing southwest, nudge left + } + } } void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -23651,6 +23784,14 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } break; } + case PORT_DISTRICT_ID: + { + get_port_district_variant_for_tile (tile, &variant, &pixel_x, &pixel_y); + char ss[200]; + snprintf (ss, sizeof ss, "Port district at tile (%d,%d) using variant %d\n", tile_x, tile_y, variant); + (*p_OutputDebugStringA) (ss); + break; + } default: { struct district_infos * info = &is->district_infos[district_id]; @@ -23678,7 +23819,7 @@ bool __fastcall patch_Tile_has_city_or_district (Tile * this) { bool has_city = Tile_has_city (this); - if (is->current_config.enable_districts) { + if (is->current_config.enable_districts || is->current_config.enable_natural_wonders) { return has_city || (get_district_instance (this) != NULL); } return has_city; From b95b7545607464bf07afd0db250e89b0d511ee58 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 26 Nov 2025 22:31:44 -0800 Subject: [PATCH 011/356] First attempt at port-enabled build checks, movement checks, and spawning --- C3X.h | 2 + default.c3x_config.ini | 1 + injected_code.c | 280 ++++++++++++++++++++++++++++------------- 3 files changed, 198 insertions(+), 85 deletions(-) diff --git a/C3X.h b/C3X.h index e3536434..f9082502 100644 --- a/C3X.h +++ b/C3X.h @@ -335,7 +335,9 @@ struct c3x_config { bool cities_with_mutual_district_receive_buildings; bool cities_with_mutual_district_receive_wonders; + bool air_units_use_aerodrome_districts_not_cities; + bool naval_units_use_port_districts_not_cities; int maximum_pop_before_neighborhood_needed; int per_neighborhood_pop_growth_enabled; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 2378590b..370aa49c 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -842,6 +842,7 @@ cities_with_mutual_district_receive_wonders = true ; limited to Aerodromes. This adds strategic depth by requiring infrastructure for air power and making air bases visible and vulnerable on the map. ; Only applies when enable_aerodrome_districts is also set to true. air_units_use_aerodrome_districts_not_cities = true +naval_units_use_port_districts_not_cities = true ; Neighborhood district limit population growth. Once a city reaches maximum_pop_before_neighborhood_needed, it cannot ; grow further until it has at least one Neighborhood district in its work radius. Each Neighborhood then enables additional population growth equal to diff --git a/injected_code.c b/injected_code.c index 62885d11..8b58da4c 100644 --- a/injected_code.c +++ b/injected_code.c @@ -7316,6 +7316,56 @@ clear_city_district_request (City * city, int district_id) } } +bool +can_build_district_on_tile (Tile * tile, int district_id) +{ + if ((! is->current_config.enable_districts) || + (tile == NULL) || (tile == p_null_tile) || + (tile->CityID >= 0) || + tile->vtable->m21_Check_Crates (tile, __, 0) || + tile->vtable->m20_Check_Pollution (tile, __, 0) || + (district_id < 0) || (district_id >= is->district_count)) + return false; + + struct district_config const * cfg = &is->district_configs[district_id]; + if (cfg->command == -1) + return false; + + if ((cfg->command == UCV_Build_Neighborhood) && !is->current_config.enable_neighborhood_districts) return false; + if ((cfg->command == UCV_Build_WonderDistrict) && !is->current_config.enable_wonder_districts) return false; + if ((cfg->command == UCV_Build_DistributionHub) && !is->current_config.enable_distribution_hub_districts) return false; + if ((cfg->command == UCV_Build_Aerodrome) && !is->current_config.enable_aerodrome_districts) return false; + if ((cfg->command == UCV_Build_Port) && !is->current_config.enable_port_districts) return false; + + enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); + if (! district_is_buildable_on_square_type (cfg, base_type)) + return false; + + int prereq_id = is->district_infos[district_id].advance_prereq_id; + if ((prereq_id >= 0) && !Leader_has_tech (&leaders[tile->Territory_OwnerID], __, prereq_id)) + return false; + + struct district_instance * existing_inst = get_district_instance (tile); + int existing_district_id = (existing_inst != NULL) ? existing_inst->district_type : -1; + bool district_completed = district_is_complete (tile, existing_district_id); + + if ((existing_district_id == district_id) && district_completed) + return false; + if ((existing_district_id >= 0) && (existing_district_id != district_id) && (! district_completed)) + return false; + + if (! cfg->allow_multiple) { + int x, y; + tile_coords_from_ptr (&p_bic_data->Map, tile, &x, &y); + FOR_TILES_AROUND(tai, is->workable_tile_count, x, y) { + struct district_instance * nearby_inst = get_district_instance (tai.tile); + if ((nearby_inst != NULL) && (nearby_inst->district_type == district_id)) + return false; + } + } + + return true; +} bool tile_suitable_for_district (Tile * tile, int district_id, City * city, bool * out_has_resource) @@ -7326,17 +7376,11 @@ tile_suitable_for_district (Tile * tile, int district_id, City * city, bool * ou if (out_has_resource != NULL) *out_has_resource = has_resource; - if ((tile == NULL) || (tile == p_null_tile)) - return false; - if (tile->CityID >= 0) - return false; - if (tile->vtable->m38_Get_Territory_OwnerID (tile) != city->Body.CivID) - return false; - if (tile->vtable->m35_Check_Is_Water (tile)) - return false; - enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); - if ((base_type == SQ_Mountains) || (base_type == SQ_Volcano)) - return false; + if ((tile == NULL) || (tile == p_null_tile)) return false; + if (tile->CityID >= 0) return false; + if (tile->vtable->m38_Get_Territory_OwnerID (tile) != city->Body.CivID) return false; + if (! can_build_district_on_tile (tile, district_id)) return false; + struct district_instance * inst = get_district_instance (tile); if (inst != NULL) { if (inst->district_type != district_id) @@ -7808,19 +7852,14 @@ tile_has_friendly_aerodrome_district (Tile * tile, int civ_id, bool require_avai if (! is->current_config.enable_districts || ! is->current_config.enable_aerodrome_districts || (tile == NULL) || - (tile == p_null_tile) || - (civ_id < 0) || (civ_id >= 32)) - return false; - - int aerodrome_id = AERODROME_DISTRICT_ID; - if (aerodrome_id < 0) + (tile == p_null_tile)) return false; struct district_instance * inst = get_district_instance (tile); - if (inst == NULL || inst->district_type != aerodrome_id) + if (inst == NULL || inst->district_type != AERODROME_DISTRICT_ID) return false; - if (! district_is_complete (tile, aerodrome_id)) + if (! district_is_complete (tile, AERODROME_DISTRICT_ID)) return false; int territory_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); @@ -7837,6 +7876,25 @@ tile_has_friendly_aerodrome_district (Tile * tile, int civ_id, bool require_avai return true; } +bool +tile_has_friendly_port_district (Tile * tile, int civ_id) +{ + if (! is->current_config.enable_districts || + ! is->current_config.enable_port_districts || + ! is->current_config.naval_units_use_port_districts_not_cities || + (tile == NULL) || (tile == p_null_tile)) + return false; + + struct district_instance * inst = get_district_instance (tile); + if ((inst == NULL) || (inst->district_type != PORT_DISTRICT_ID)) + return false; + + if (! district_is_complete (tile, PORT_DISTRICT_ID)) + return false; + + return tile->vtable->m38_Get_Territory_OwnerID (tile) == civ_id; +} + bool city_has_required_district (City * city, int district_id) { @@ -10957,6 +11015,7 @@ patch_init_floating_point () {"cities_with_mutual_district_receive_buildings" , false, offsetof (struct c3x_config, cities_with_mutual_district_receive_buildings)}, {"cities_with_mutual_district_receive_wonders" , false, offsetof (struct c3x_config, cities_with_mutual_district_receive_wonders)}, {"air_units_use_aerodrome_districts_not_cities" , false, offsetof (struct c3x_config, air_units_use_aerodrome_districts_not_cities)}, + {"naval_units_use_port_districts_not_cities" , false, offsetof (struct c3x_config, naval_units_use_port_districts_not_cities)}, {"show_natural_wonder_name_on_map" , false, offsetof (struct c3x_config, show_natural_wonder_name_on_map)}, {"ai_defends_districts" , false, offsetof (struct c3x_config, ai_defends_districts)}, {"allow_workers_to_enter_coast" , false, offsetof (struct c3x_config, allow_workers_to_enter_coast)}, @@ -12188,56 +12247,6 @@ compute_highlighted_worker_tiles_for_districts () } } -bool -can_build_district_on_tile (Unit * unit, Tile * tile, int district_id, enum SquareTypes base_type) -{ - if ((! is->current_config.enable_districts) || - (unit == NULL) || - (tile == NULL) || (tile == p_null_tile) || - (! is_worker (unit)) || - (tile->CityID >= 0) || - tile->vtable->m21_Check_Crates (tile, __, 0) || - tile->vtable->m20_Check_Pollution (tile, __, 0) || - (district_id < 0) || (district_id >= is->district_count)) - return false; - - struct district_config const * cfg = &is->district_configs[district_id]; - if (cfg->command == -1) - return false; - - if ((cfg->command == UCV_Build_Neighborhood) && !is->current_config.enable_neighborhood_districts) return false; - if ((cfg->command == UCV_Build_WonderDistrict) && !is->current_config.enable_wonder_districts) return false; - if ((cfg->command == UCV_Build_DistributionHub) && !is->current_config.enable_distribution_hub_districts) return false; - if ((cfg->command == UCV_Build_Aerodrome) && !is->current_config.enable_aerodrome_districts) return false; - if ((cfg->command == UCV_Build_Port) && !is->current_config.enable_port_districts) return false; - - if (! district_is_buildable_on_square_type (cfg, base_type)) - return false; - - int prereq_id = is->district_infos[district_id].advance_prereq_id; - if ((prereq_id >= 0) && !Leader_has_tech (&leaders[unit->Body.CivID], __, prereq_id)) - return false; - - struct district_instance * existing_inst = get_district_instance (tile); - int existing_district_id = (existing_inst != NULL) ? existing_inst->district_type : -1; - bool district_completed = district_is_complete (tile, existing_district_id); - - if ((existing_district_id == district_id) && district_completed) - return false; - if ((existing_district_id >= 0) && (existing_district_id != district_id) && (! district_completed)) - return false; - - if (! cfg->allow_multiple) { - FOR_TILES_AROUND(tai, is->workable_tile_count, unit->Body.X, unit->Body.Y) { - struct district_instance * nearby_inst = get_district_instance (tai.tile); - if ((nearby_inst != NULL) && (nearby_inst->district_type == district_id)) - return false; - } - } - - return true; -} - void set_up_district_buttons (Main_GUI * this) { @@ -12313,7 +12322,7 @@ set_up_district_buttons (Main_GUI * this) if (existing_district_id == dc && district_completed) continue; if ((existing_district_id >= 0) && (existing_district_id != dc) && (! district_completed)) continue; - if (! can_build_district_on_tile (selected_unit, tile, dc, base_type)) + if (! can_build_district_on_tile (tile, dc)) continue; // This district should be shown @@ -12770,7 +12779,7 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) if (! itable_look_up (&is->command_id_to_district_id, unit_command_value, &district_id)) return false; - return can_build_district_on_tile (this, tile, district_id, base_type); + return is_worker (this) && can_build_district_on_tile (tile, district_id); } else if (unit_command_value == UCV_Build_Mine) { bool has_district = (tile != NULL) && (tile != p_null_tile) && (get_district_instance (tile) != NULL); @@ -13667,6 +13676,18 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, base_validity = AMV_OK; } + if ((base_validity == AMV_OK) && + is->current_config.enable_districts && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea)) { + int nx, ny; + get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); + Tile * dest = tile_at (nx, ny); + if ((dest != NULL) && (dest != p_null_tile) && Tile_has_city (dest)) + return AMV_INVALID_SEA_MOVE; + } + // Apply unit count per tile limit enum UnitTypeClasses class = p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class; if ((base_validity == AMV_OK) && (is->current_config.limit_units_per_tile[class] > 0)) { @@ -13713,6 +13734,17 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr base_cost = Unit_get_max_move_points (unit); } + if ((unit != NULL) && + (base_cost >= 0) && + is->current_config.enable_districts && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities && + (p_bic_data->UnitTypes[unit->Body.UnitTypeID].Unit_Class == UTC_Sea)) { + Tile * dest = tile_at (to_x, to_y); + if ((dest != NULL) && (dest != p_null_tile) && Tile_has_city (dest)) + return -1; + } + // Apply unit count per tile limit if ((unit != NULL) && ! is_below_stack_limit (tile_at (to_x, to_y), unit->Body.CivID, p_bic_data->UnitTypes[unit->Body.UnitTypeID].Unit_Class)) return -1; @@ -14841,9 +14873,15 @@ patch_City_can_build_unit (City * this, int edx, int unit_type_id, bool exclude_ is->current_config.enable_aerodrome_districts && is->current_config.air_units_use_aerodrome_districts_not_cities) { UnitType * type = &p_bic_data->UnitTypes[unit_type_id]; - int aerodrome_id = AERODROME_DISTRICT_ID; - if ((type->Unit_Class == UTC_Air) && (aerodrome_id >= 0) && - ! city_has_required_district (this, aerodrome_id)) + if (type->Unit_Class == UTC_Air && ! city_has_required_district (this, AERODROME_DISTRICT_ID)) + return false; + } + + if (is->current_config.enable_districts && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities) { + UnitType * type = &p_bic_data->UnitTypes[unit_type_id]; + if (type->Unit_Class == UTC_Sea && ! city_has_required_district (this, PORT_DISTRICT_ID)) return false; } } @@ -14856,8 +14894,25 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply { // First defer to the base game's logic bool base = City_can_build_improvement (this, __, i_improv, apply_strict_rules); - if (! base) return false; - if (! is->current_config.enable_districts) return base; + + if (! is->current_config.enable_districts) + return base; + + if (! base) { + // Allow harbor-like improvements when port districts replace coastal adjacency + Improvement * improv = &p_bic_data->Improvements[i_improv]; + if (improv->ImprovementFlags & ITF_Allows_Water_Trade && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities) { + int tx, ty; + + // If the city has no coastal tiles within its workable area, disallow + if (find_tile_for_district (this, PORT_DISTRICT_ID, &tx, &ty) == NULL) + return false; + + // Else proceed with standard improvement checks + } + } // Different logic for human vs AI players bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; @@ -20052,15 +20107,31 @@ patch_Leader_spawn_unit (Leader * this, int edx, int type_id, int tile_x, int ti int spawn_x = tile_x, spawn_y = tile_y; - if (is->current_config.enable_districts && - is->current_config.air_units_use_aerodrome_districts_not_cities) { + if (is->current_config.enable_districts) { UnitType * type = &p_bic_data->UnitTypes[type_id]; - int aerodrome_id = AERODROME_DISTRICT_ID; - if ((type->Unit_Class == UTC_Air) && (aerodrome_id >= 0)) { + if (is->current_config.air_units_use_aerodrome_districts_not_cities) { + if (type->Unit_Class == UTC_Air) { + City * spawn_city = city_at (tile_x, tile_y); + if ((spawn_city != NULL) && (spawn_city->Body.CivID == this->ID)) { + int district_x, district_y; + Tile * district_tile = get_completed_district_tile_for_city (spawn_city, AERODROME_DISTRICT_ID, &district_x, &district_y); + if ((district_tile != NULL) && (district_tile != p_null_tile) && + (district_tile->Territory_OwnerID == this->ID) && + is_below_stack_limit (district_tile, this->ID, type->Unit_Class)) { + spawn_x = district_x; + spawn_y = district_y; + } + } + } + } + + if ((type->Unit_Class == UTC_Sea) && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities) { City * spawn_city = city_at (tile_x, tile_y); if ((spawn_city != NULL) && (spawn_city->Body.CivID == this->ID)) { int district_x, district_y; - Tile * district_tile = get_completed_district_tile_for_city (spawn_city, aerodrome_id, &district_x, &district_y); + Tile * district_tile = get_completed_district_tile_for_city (spawn_city, PORT_DISTRICT_ID, &district_x, &district_y); if ((district_tile != NULL) && (district_tile != p_null_tile) && (district_tile->Territory_OwnerID == this->ID) && is_below_stack_limit (district_tile, this->ID, type->Unit_Class)) { @@ -21563,6 +21634,46 @@ patch_Tile_m7_Check_Barbarian_Camp (Tile * this, int edx, int visible_to_civ) return Tile_m7_Check_Barbarian_Camp (this, __, visible_to_civ); } +bool __fastcall +patch_Unit_can_heal_at (Unit * this, int edx, int tile_x, int tile_y) +{ + if (is->current_config.enable_districts && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea)) { + Tile * tile = tile_at (tile_x, tile_y); + if (tile_has_friendly_port_district (tile, this->Body.CivID)) { + int occupier_id = get_tile_occupier_id (tile_x, tile_y, -1, true); + return (occupier_id == -1) || (occupier_id == this->Body.CivID); + } + } + + return Unit_can_heal_at (this, __, tile_x, tile_y); +} + +void __fastcall +patch_Unit_heal_at_start_of_turn (Unit * this) +{ + bool healed_in_port = false; + + if (is->current_config.enable_districts && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea)) { + Tile * tile = tile_at (this->Body.X, this->Body.Y); + if (tile_has_friendly_port_district (tile, this->Body.CivID)) + healed_in_port = true; + } + + Unit_heal_at_start_of_turn (this); + + if (healed_in_port && (this->Body.Damage > 0)) { + int heal_amt = Unit_get_max_hp (this) - 1; + int new_damage = this->Body.Damage - heal_amt; + this->Body.Damage = (new_damage < 0) ? 0 : new_damage; + } +} + bool __fastcall patch_Unit_can_airdrop (Unit * this) { @@ -22006,9 +22117,8 @@ patch_Unit_check_rebase_target (Unit * this, int edx, int tile_x, int tile_y) struct district_instance * inst = get_district_instance (tile); if (inst != NULL) { int district_id = inst->district_type; - int aerodrome_id = AERODROME_DISTRICT_ID; // Check if this is an aerodrome district owned by this unit's civ - if ((aerodrome_id >= 0) && (district_id == aerodrome_id) && (tile->Territory_OwnerID == this->Body.CivID)) { + if ((district_id == AERODROME_DISTRICT_ID) && (tile->Territory_OwnerID == this->Body.CivID)) { // Check if aerodrome is complete if (district_is_complete (tile, district_id)) { // Perform range check @@ -23679,6 +23789,7 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -23718,8 +23829,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par // Districts if (is->current_config.enable_districts) { - if (district_id < 0 || district_id >= is->district_count) - return; + if (district_id < 0 || district_id >= is->district_count) return; bool completed = district_is_complete (tile, district_id); if (! completed) From 100bc3f1818f132d0780d0a8d198eb9395c7aa10 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 27 Nov 2025 08:08:42 -0800 Subject: [PATCH 012/356] Fix patch_City_can_build_improvement bug with ports; Add sprite & sheet indices to tile terrain info for debugging --- C3X.h | 4 ++-- injected_code.c | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/C3X.h b/C3X.h index f9082502..790ebf7a 100644 --- a/C3X.h +++ b/C3X.h @@ -667,8 +667,8 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", - .advance_prereq = "", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, - .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, + .advance_prereq = "Map Making", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, + .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .dependent_improvements = {"Harbor", "Commercial Dock"}, .buildable_square_types_mask = (1 << SQ_Coast), .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 diff --git a/injected_code.c b/injected_code.c index 8b58da4c..81e08dbc 100644 --- a/injected_code.c +++ b/injected_code.c @@ -14911,6 +14911,8 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply return false; // Else proceed with standard improvement checks + } else { + return base; } } @@ -15980,6 +15982,7 @@ patch_PCX_Image_draw_tile_info_terrain (PCX_Image * this, int edx, char * str, i if (is->current_config.enable_districts || is->current_config.enable_natural_wonders) { // Draw district name to the right of terrain name if tile has one struct district_instance * dist = get_district_instance (tile); + if (dist != NULL) { show_district_name = true; char const * display_name = is->district_configs[dist->district_type].name; @@ -16003,7 +16006,11 @@ patch_PCX_Image_draw_tile_info_terrain (PCX_Image * this, int edx, char * str, i } } - snprintf (s, sizeof s, "%s", display_name); + // Show sprites indexes for debugging + int sheet_index = (tile->SquareParts >> 8) & 0xFF; + int sprite_index = tile->SquareParts & 0xFF; + + snprintf (s, sizeof s, "%s, (%d, %d)", display_name, sheet_index, sprite_index); PCX_Image_draw_text (this, __, s, x + 68, y, strlen (s)); } } @@ -23789,7 +23796,7 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - *p_debug_mode_bits |= 0xC; + //*p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; From 35da1214a8bb313762d60e690d9ad81edc1805a4 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 27 Nov 2025 08:43:57 -0800 Subject: [PATCH 013/356] Update get_port_district_variant_for_tile to include more detailed variables on surrounding tiles; Will use for more detailed offsets and variant selection --- injected_code.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/injected_code.c b/injected_code.c index 81e08dbc..757ff987 100644 --- a/injected_code.c +++ b/injected_code.c @@ -23656,6 +23656,15 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i return false; } +bool +tile_offset_is_land (int adj_x, int adj_y) +{ + Map * map = &p_bic_data->Map; + wrap_tile_coords (map, &adj_x, &adj_y); + Tile * adj_tile = tile_at (adj_x, adj_y); + return (adj_tile != NULL) && (adj_tile != p_null_tile) && (! adj_tile->vtable->m35_Check_Is_Water (adj_tile)); +} + void get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) { @@ -23667,10 +23676,6 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi int sheet_index = (tile->SquareParts >> 8) & 0xFF; int sprite_index = tile->SquareParts & 0xFF; - char ss[200]; - snprintf (ss, sizeof ss, "Port district has sheet index %d, sprite index %d\n", sheet_index, sprite_index); - (*p_OutputDebugStringA) (ss); - if ((tile == NULL) || (tile == p_null_tile) || (out_variant == NULL)) return; @@ -23752,6 +23757,19 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi return; } + bool city_is_west_of_port = (closest_dx < 0); + bool city_is_north_of_port = (closest_dy < 0); + bool city_is_directly_above_port = (closest_dx == 0) && (closest_dy < 0); + bool city_is_directly_below_port = (closest_dx == 0) && (closest_dy > 0); + bool northwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y - 1); + bool north_tile_is_land = tile_offset_is_land (tile_x, tile_y - 1); + bool northeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y - 1); + bool east_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y); + bool southeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y + 1); + bool south_tile_is_land = tile_offset_is_land (tile_x, tile_y + 1); + bool southwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y + 1); + bool west_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y); + // Otherwise, face roughly away from the city based on its relative position int face_dx = -closest_dx; int face_dy = -closest_dy; From 226c1dc7732da66e265594868d36f667438cd5ca Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 27 Nov 2025 11:24:52 -0800 Subject: [PATCH 014/356] Update art, lighthouse goes on land, harbor hugs outside of diamond --- Art/Districts/1200/Port_NE.PCX | Bin 8341 -> 8344 bytes Art/Districts/1200/Port_NW.PCX | Bin 8428 -> 8420 bytes Art/Districts/1200/Port_SE.PCX | Bin 8433 -> 8433 bytes Art/Districts/1200/Port_SW.PCX | Bin 8433 -> 8377 bytes C3X.h | 2 +- default.c3x_config.ini | 2 +- injected_code.c | 14 +++++++------- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index 782ef2635f96ecfb9b89b0ab9e071d40ffe27953..43960159456ebf8b9ee0e89ff9a78364ec720fcc 100644 GIT binary patch delta 1086 zcmW+#T}T{P7-izRt47_~jMM8nZ3Z*lSOT5SZl6NN1%Dov8EG367t*woU4=e$aJ9_E zLi>TNbSg_J21*h8UhbVWiBYqzNFr@$wLfW-Y(y*ct@rlsz8vQJ&iQ`k z%r`d%76z`*u9VxtLd!P&YI13Z9-zfm`Q#SG|4jN9Rby@oRifaPDxHM=Q+kE|%NSj6 z;6?-Qar~23x!fBtolWFN!MQ5+J!op{lyY&t> z)Fg+eenZn?SzM*}m<@*s)I=%6*dJ(L0fLTbwH!-V_|ED!N{X_4NHLz|K!j zro^((XT(?V0>*KI{C0CJiE!&gS=1+S405&;(-5ZN3i%)VTp&J&r!a*h;P8|aN{D+9 zrkEgz(PgbvGbL(vdr##vgB4twCVI(A+Y8#W`9;9m;e@Ohp15FWrh zI^1@`4W<@C7=Z;cT=jW{_yF#~BeZgs-D`5K0pT({A;W~vG2%VA3ya8|-dsmvWrIO@ zM*67FVPfN5xC2XQ6`T$&lOV$-cu9sKp991@@E1Hq$Kp`ULzU~dj z>UEm5L_=tp?UbbT*8tJOoMYu3Yl{{-rjM}Es=APe7I#~%l${r~WX3EpU6eO2U{r|2 z$I)@~wzHqxZKAFvJH@fSYFV8}Z!x0B%ofv#m(X_dcFN^;G^1;wqpC)o2PLgU&3VQ4 zjLJ>JRCdKiF=wSZT-$PUy4XcMww2k%I$2%7gQs~i)51+PtHgpOEI3vn=bDj>*p)OV zS37lLsQ3zDahftzXpe#WUX0nPt`(kcBc^uG}pA$4&&K3J)e&Eci|ZbxUhm7-!!F z!vc_Rpj_0&BvgQoVsP{kg1p&0D1NzG}&wr10UrpMP9a6$$K6gbG3BN_+Cu@igf; zOZPraW4gk-J}2pa8^C;bJ|0dRv8<{D6!p{7yFEPjnjKQ)TGDFXZ{C+yRP%=(v!cZ~ z+V7QL(h#jk-V&8M`jbJo(Xu0;MwG6T%QSA%rfC=?ahstA+BKz9>Dryl9OU@@Zs`%t zr(8$LId8$63v(sDL&P1w?UnFcQ+j_oQc#Mj5|2FWtCvLz%}4jW z=Q?R_s0y7*eZVFpIaEZ?>n@_Zvne8|x^kZx_41m_xYKrxmiv9Ra&QQL{kG;FOV!JP XLEJWb!KgheTsT(Kx9Y-FwYUC%uo2Np diff --git a/Art/Districts/1200/Port_NW.PCX b/Art/Districts/1200/Port_NW.PCX index 12d0be1bba53605e30da3dfa20ea83651d8a95a6..d0d5aa10285402a249c7a76a535555366202943e 100644 GIT binary patch delta 1160 zcmZvaT}&KR6vwkJ-@CK}v%_QyW=d`=w9(1zK51B1A^Wh|*(kJSlSaE;mKT!^A9b$v z!6q8ANr_@&a+9g9iGx{_0xv%J;FBhE2bNN_fV4$JVEJgpT5J*0RAarz%j#oae)rsa zaz5_=oSlw49Us3>7N0#rzLno3pL)Kmm}j5&Pdp*7cqPvqv-kR~ZbcjUlZ@3%Ro7WR zZXZ-1lOeBEeT_Sfj!^OuS*Vw*3d}Na2W>&0_BAOoD%K-X|wx~qcD0g(5Ph2*l>yL@K&<89YtG&r-wM~9*lpOqe_{hR9&8= zFfjZpSXj?v{W&Cjq~3r6BYB_i`E9T%Jsfb97*$-=BWI{$-?Z0zmSNH^TnC++5j|z( zMU>3MzUN<&+%s&8WXhWAuK_mm20l-XxTvCJTAbwy$gWNDRhc3dz`%JUWl|%UQIRmB zpaNG+a?i01vR`&WoB~TvB}_v%EmiE}Q;1J-nPt~HdAaC)OJd;{I22e66cx zX$9)EbaF6o%cpc4tp$(G@Lc@zoa5;Q@u!n{Njolwi@AyIa7l;u`EyQ7g**9XWojq&rXR4_9OO~Ex;%C K*JCyDhJOJGLwoE1 delta 1177 zcmZvbUrZcD9LKXg{#?a!dtH|e(70(P72DW#_udT0MaVr|ZZ9Zpxuj9A$MJaKPu#IS zv}r0iQ=*tyCfS-Zabxa5;Gx#=VB(WIOKE{S{uhL!&_XrERzRAzu{1Ld8y|i7Wac-& z@BHTXXC5>@Zv6Uk*byep&wHgqY=sM0STTw#Ys zV=(a>y6zrX1`L7)w8~3LkwBb;d_*`qOBN_4k6b=>`6uI3vP=f?g|Hg@6r`uobzVAI zB&OA-oCSf?lVp`iHe5b%xz6}EDH1(t=3)`>LmpjngT!@c@&W$zIQfGqiY`}NE;Ihc zg1|5|azLm%dK)$K(%Z~{4S1|B3iwg7#WZs+=UvV+ZZeH-=|(1sLEQ*?otNHPCKzU{ zxC3}ho-xUU%e>2R#^3iP4vd3zgh$)HDkgwBh8E@l|C0GTgfNb{#Ekcu|9eRt4hjw+ z=vSXux&JENFoBQ&l-lUVtNSJ$hA{-Z5TVpT)1v5GA~ucVq!o`i%1O+5gLV;#6?K`j4%IPyUy@+<`I!k4ZZn(Vfa)wNiThxF# zBWYqIkX8WSD}y{8_%e&-5=Zu48N#%)XB+KQidun;QAT44B1T~hu$t|KAC8EbBPtnyfrFw~CmLsdbOZz|COv z=6H{C6RcVi_X6#Ck8pX^EK1@FTHBdWx>Mb$^eG_M20uu?KQzhuCW+7KDOpW*Ce&bO zYqTvCfE~@*0&BKdvq+v$znV&PX*CxUDLMSH-}rf8a3Jr_h#)SJ9r}*iqeNxFo`b}Xt XRyD&7FZufq4F6d33pae%SML88esh3r diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index d5ac5dbec945ad46e1b5282a2c9135fe4079e954..88698227cdb99a2d0f61246639c8d509d4f961b3 100644 GIT binary patch delta 852 zcmX9)O=uHA7-jq8Ut?XnSxeo@TE`ltW!Xd!#WWJ_A!Gx#sDU1gi3J5?{M$i65fyqU zh#-8h9t2m-K`Qi6DhQsO*;-Q@D^;wZqNpgfrqNid2VvIPdYH$@dvD(N_$tv#^y z&)ALJz-RWtby=A`=lEj*z4;G&%E-^0-L2~VAJ{9d&9Etsr3Linc{agvxZ}81WOtvl zknozTUzmr}3+T;r+(Ukv(Qv8=y0C7^1hY*XM&qzYXsdh5Fim4ICiN8Hf)Ml-*#JG) zsfW*?>|+=bws3=GXS5y^+EajOVLhB`@N>>cTlqu`-6_C^Dg--y9+xz*C5A-qJ5;q6 zZa4UO81BSo9!Y_g0^F=x1E+lcR!&kAw+7-!e9TDYe2zxxk;wk;R`Y&XN$gA9-L77n zg~ZHy?2A(r7zJN-3pA|q*A*GXeRk48;wU7lRbd8pG|QYd9Gpbim^cELs{_JU z=w9jtV1r_tib$j|S-nyC1{I&YtjKn!4Ll$@2%2g%p#&AhOTg4HEhME79Igp+>rm4| z1>8w75~Ub{2Q{0yIk>_zCu!PF#vDXpW$h+j;+FJ(T_5g1v4H46s3DfXhlJx?^R^G%1sF{#7lsYKA zbNi`#pHxVRlu4<5suBs7phc0}A2G+Zjdap74a?S~UVi1oC-)wCOQy(dX#Z9VXQ-|!Dpk}n zTw2|zj=Byu9NUTqvr=y={>3FEH!^2+bp!=fQ!0?Dml0uOu94ZUC+vJy3-hy0^TdZ_ zj2E99>@qEV*F2o`H!9<9NR3D_DJ#a%H+RUB`6~~eU-_Hsa+AZJxSAg6)x+AgS(i)_ lhrGQ`ireP-mZrtTW6HmHD|z^8{a#`CMDs;qxVi47{101crMUnA delta 917 zcmW-eUuYav6vmm|O6tt9`Ygt4vY^%N7)C>Lxoa7=#%$Ol4jF1G~FZ{X<}%_{;}KDq*|rwy`JpD{5ah2 zn|sbZ-%e&HGks^@U$Ul@Y{KCqq^pkAuW}vPXmPLpQ*Og)ty|lMSGD8XPw=7Et8Kxz znyqcZL+u5u&xD?fFUt#KGfE2C#GhANyor1uw_rR(k1k5SJLbQhNKO4GchPa9#bx9@ zbX*D@i&y0s>mRrNl}}J!YH<kbZFvjc4);WbWTMFQEIIoJ*fFBrf^Wi!jt6Q+d@`6ye?ZH-km)Ack8nSn zjNVh8U$V~9zLDRdhY+m}cH|VEIz_KCl%-!cVd4-8tx5POVja7y@(%O#K7FKtt!wJH z79X`(muOmu*wH(R4KlYvNwNo~jm~gQ24J>5-*HER9KGpE9wlT`oAs-eMvFFPY~zQO~(`2}Bkv92894LU`) zLTI6!TfsiDBV3`OSy#-fxa^#8hloWB=gBh8Xu$Nr2-#3BtGGo^xP4`d>Qf1N4IdUG z>&kbEdCzq`-y)-!#!waaV!*H&(brVKoWS>TcA1P8a#wM|F@04zrI%7eex+=saSfQJ r89T75`Vs>qOUind&S7w|XiZtEfzedj`g|{5j zQk6uJeBcs^Y9yzptk5cLuzG1D744zIU&~lo~Ov7FXbMZi>QnxJfDO)mUNt2mXQuIs>>= zR0AXxOI;mb0$j&dj8n+m$;J_-)%eqQpTC6*kVp>x-q7uLw6K>YH z!RSECE>qUGV=|)W@*l=*ZJ@Xa22l+z!JqmUjrsZ18g=8{xAPkLIdk+Ofo7IVB?;|k*wc+7mm;+J>{J7;r5CL{5IR6w6zgo3FBPe~mopP@-V|NcEn ztj1&S;3{_V{c=7x7>GA&B~a9Y)s>LG+JhbDTdHw^aT^}MIz3CO{Q)6#@E9)VEqN?5 zD7v$YkXDQW*TJ2vpm{&<+CX2w(44phMyH`$65rPP6rM4wpKF|B`~!Z6O^WBSjeo(_ z^Zg?!Eh@N3$mMp4MCi?2y2%Eh8-cc@#O)y05k9(2zy0)0Qnhk&99O-e=%^@o1o5LU z`Y;sdKrt0PpCnRS8Gsd>djX|P!@j)pw#Fvo6I&)CwfSL?PO*y@hwKtWr}^(*<2Am_dG+&Atnx zR>AlB4^01FuNWE{&9H0gOKR~B>`Z7MtW(=iY_pIIXZhhw(nc4Wo7hkrUi5PtRM8@# zF4M{eqiirgBF*cQFznIL@$kpTli|?mp*Wt!RYJ}^pdDhIPw-hTAyL$6-+L(Kxvx>L-FBIGTtY0(2@`$OysyCSh6buCNrpM{c|osvRH zpdgBWTAU75`<11Lwy{16t)Zr$Giy7xBiWW8vK!onq)%FrL=mC+D04e@J22(ROI@G0wy9c3tz6<|*>k;UvL6jMcDkJ&Z=dvc7AIR4F^u`QTS1<`HZ{fkw3 zw|0zwa(u=byi0bADGyDA6Srr9{m$<3kw1>{H;xTf=QN8G9y1s|H5{HV@|lO&-7DJn zppczR(_nhIKxc{s*B!EbA97JWMZ@Xva5zykF|o>yeXyPjZr_5;v@qyKf%AobjMG?$(9L>%c7c6UlI?XxTjU-jMLZ@5ky;~uBKqo34r zh3HIh($$<}m?A&sW2C*t%cIOU}4g^YUTBj?2uPIO8q_L-rh zkYci*@L|(wOBuUQOWrC&G=+qicQOHIkq+#=iuDF~nPS;zXjS?u{#g3EG zD|>97zBl9#T~jg-fzUwzksRNT`YZt&NjqKY2XCv*gZ|lhQYqoI{fIqpqnrlssiYou86XA14dFBnioxF7oTW5W4Vt%?~!cFJf4VZ zdR`He$~GQp>$N<`^>)c-@=$7Mh$2N0V_SHxO|txhT|KgO7shDH7>k+82Ht9ungIyT zw#%KnTsDWoO3W*iaaVh91b_l%z?XT9UXGA8 z(xCJPKf<-7mzP-?G@2jMQhGsDk{PuG+|ODd7zxKmSd_hN( zRF8{3C3#uo^EEDL`k{J zR>06wV|iW68$NN0`w}n14W6kQ-*om!^`=ekUIr6srjb_z8F4D90_<>qq@GRgvz89} z<3=Il3-NCR*ye`H_9f_ZMZ}nx5n>8Jg?lgAcbEx2e`LxZ@_h=h#e=f2@I-2ATKL-W Lanr){F4FxkEc{{} diff --git a/C3X.h b/C3X.h index 790ebf7a..e93dbf0d 100644 --- a/C3X.h +++ b/C3X.h @@ -349,7 +349,7 @@ struct c3x_config { int distribution_hub_shield_yield_divisor; int ai_ideal_distribution_hub_count_per_100_cities; - bool allow_workers_to_enter_coast; + bool workers_can_enter_coast; bool ai_defends_districts; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 370aa49c..a4b3855c 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -871,7 +871,7 @@ distribution_hub_food_yield_divisor = 3 distribution_hub_shield_yield_divisor = 4 ai_ideal_distribution_hub_count_per_100_cities = 25 -allow_workers_to_enter_coast = true +workers_can_enter_coast = true ; When enabled, AI defensive units will actively seek out and defend districts within their territory, treating them as valuable assets like colonies. ; The AI prioritizes defending Wonder districts (if destructible wonders are enabled) over regular districts, searching within a 20-tile radius for diff --git a/injected_code.c b/injected_code.c index 757ff987..4262915f 100644 --- a/injected_code.c +++ b/injected_code.c @@ -11018,7 +11018,7 @@ patch_init_floating_point () {"naval_units_use_port_districts_not_cities" , false, offsetof (struct c3x_config, naval_units_use_port_districts_not_cities)}, {"show_natural_wonder_name_on_map" , false, offsetof (struct c3x_config, show_natural_wonder_name_on_map)}, {"ai_defends_districts" , false, offsetof (struct c3x_config, ai_defends_districts)}, - {"allow_workers_to_enter_coast" , false, offsetof (struct c3x_config, allow_workers_to_enter_coast)}, + {"workers_can_enter_coast" , false, offsetof (struct c3x_config, workers_can_enter_coast)}, {"enable_city_work_radii_highlights" , false, offsetof (struct c3x_config, enable_city_work_radii_highlights)}, {"introduce_all_human_players_at_start_of_hotseat_game", false, offsetof (struct c3x_config, introduce_all_human_players_at_start_of_hotseat_game)}, }; @@ -13664,7 +13664,7 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, AdjacentMoveValidity base_validity = Unit_can_move_to_adjacent_tile (this, __, neighbor_index, param_2); // Let workers step onto coast tiles when the config flag is enabled (base logic treats this as an invalid sea move) - if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + if (is->current_config.enable_districts && is->current_config.workers_can_enter_coast && is_worker (this) && ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { int nx, ny; @@ -13725,7 +13725,7 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr int base_cost = Trade_Net_get_movement_cost (this, __, from_x, from_y, to_x, to_y, unit, civ_id, flags, neighbor_index, dist_info); // Let the pathfinder consider coastal tiles reachable for workers when the config flag is on - if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + if (is->current_config.enable_districts && is->current_config.workers_can_enter_coast && (base_cost < 0) && (unit != NULL) && is_worker (unit)) { Tile * dest = tile_at (to_x, to_y); if ((dest != NULL) && @@ -19160,7 +19160,7 @@ patch_Unit_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, bool { is->moving_unit_to_adjacent_tile = true; - bool const allow_worker_coast = is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && is_worker (this); + bool const allow_worker_coast = is->current_config.enable_districts && is->current_config.workers_can_enter_coast && is_worker (this); bool coast_override_active = false; enum UnitStateType prev_state = this->Body.UnitState; int prev_container = this->Body.Container_Unit; @@ -24962,13 +24962,13 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t { PassBetweenValidity base = Unit_can_pass_between (this, __, from_x, from_y, to_x, to_y, param_5); - if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + if (is->current_config.enable_districts && is->current_config.workers_can_enter_coast && base != PBV_OK && is_worker(this)) { Tile * dest = tile_at (to_x, to_y); if ((dest != NULL) && dest->vtable->m35_Check_Is_Water (dest) && (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) - return PBV_OK; // Let workers treat coast as passable when allow_workers_to_enter_coast is on + return PBV_OK; // Let workers treat coast as passable when workers_can_enter_coast is on } return base; @@ -24979,7 +24979,7 @@ patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool { Unit * transport = Unit_select_transport (this, __, tile_x, tile_y, do_show_popup); - if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + if (is->current_config.enable_districts && is->current_config.workers_can_enter_coast && (transport == NULL) && (this == is->coast_walk_unit) && is_worker (this)) { Tile * dest = tile_at (tile_x, tile_y); if ((dest != NULL) && From 797071ff14c74b8d0b8d04439ee5259835585cf4 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 27 Nov 2025 15:02:37 -0800 Subject: [PATCH 015/356] Working through port angle & pixel offset logic --- injected_code.c | 169 +++++++++++++++++++++++++++--------------------- 1 file changed, 94 insertions(+), 75 deletions(-) diff --git a/injected_code.c b/injected_code.c index 4262915f..70587797 100644 --- a/injected_code.c +++ b/injected_code.c @@ -23659,23 +23659,29 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i bool tile_offset_is_land (int adj_x, int adj_y) { - Map * map = &p_bic_data->Map; - wrap_tile_coords (map, &adj_x, &adj_y); + wrap_tile_coords (&p_bic_data->Map, &adj_x, &adj_y); Tile * adj_tile = tile_at (adj_x, adj_y); return (adj_tile != NULL) && (adj_tile != p_null_tile) && (! adj_tile->vtable->m35_Check_Is_Water (adj_tile)); } void -get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) +get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * out_sprite_index) { - int NW = 0; - int NE = 1; - int SE = 2; - int SW = 3; + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); - int sheet_index = (tile->SquareParts >> 8) & 0xFF; - int sprite_index = tile->SquareParts & 0xFF; + if (tile != NULL & tile != p_null_tile) { + *out_sheet_index = (tile->SquareParts >> 8) & 0xFF; + *out_sprite_index = tile->SquareParts & 0xFF; + } else { + *out_sheet_index = -1; + *out_sprite_index = -1; + } +} +void +get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) +{ if ((tile == NULL) || (tile == p_null_tile) || (out_variant == NULL)) return; @@ -23742,72 +23748,85 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi if (closest_city == NULL) return; - // Check the four diagonal neighbors explicitly - if ((closest_dx == 1) && (closest_dy == -1)) { - *out_variant = SW; - return; - } else if ((closest_dx == -1) && (closest_dy == -1)) { - *out_variant = SE; - return; - } else if ((closest_dx == 1) && (closest_dy == 1)) { - *out_variant = NW; - return; - } else if ((closest_dx == -1) && (closest_dy == 1)) { - *out_variant = NE; - return; - } - - bool city_is_west_of_port = (closest_dx < 0); - bool city_is_north_of_port = (closest_dy < 0); - bool city_is_directly_above_port = (closest_dx == 0) && (closest_dy < 0); - bool city_is_directly_below_port = (closest_dx == 0) && (closest_dy > 0); - bool northwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y - 1); - bool north_tile_is_land = tile_offset_is_land (tile_x, tile_y - 1); - bool northeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y - 1); - bool east_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y); - bool southeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y + 1); - bool south_tile_is_land = tile_offset_is_land (tile_x, tile_y + 1); - bool southwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y + 1); - bool west_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y); - - // Otherwise, face roughly away from the city based on its relative position - int face_dx = -closest_dx; - int face_dy = -closest_dy; - int abs_face_dx = int_abs (face_dx); - int abs_face_dy = int_abs (face_dy); - - if ((abs_face_dx == 0) && (abs_face_dy == 0)) - return; - - if (abs_face_dx > abs_face_dy) { - if (face_dx > 0) - *out_variant = (face_dy >= 0) ? 2 : 1; // city is west; face east (toward +x), bias south if city is south, else bias north - else - *out_variant = (face_dy >= 0) ? 3 : 0; // city is east; face west (toward -x), bias south if city is south, else bias north - } else if (abs_face_dy > abs_face_dx) { - if (face_dy > 0) - *out_variant = (face_dx >= 0) ? 2 : 3; // city is north; face south (toward +y), bias east if city is east, else bias west - else - *out_variant = (face_dx >= 0) ? 1 : 0; // city is south; face north (toward -y), bias east if city is east, else bias west - } else { - if (face_dx >= 0) - *out_variant = (face_dy >= 0) ? 2 : 1; // city northwest/southwest-ish; pick the closer diagonal - else - *out_variant = (face_dy >= 0) ? 3 : 0; // city northeast/southeast-ish; pick the closer diagonal - } - -finalize: - - if (out_pixel_y != NULL) { - if ((*out_variant == 0) || (*out_variant == 1)) { - *out_pixel_y += 16; // Facing north-ish, nudge up - } else if ((*out_variant == 2) || (*out_variant == 3)) { - *out_pixel_y -= 16; // Facing south-ish, nudge down - if (*out_variant == 2) - *out_pixel_x += 8; // Facing southeast, nudge right - else - *out_pixel_x -= 8; // Facing southwest, nudge left - } + int sheet_index = -1, sprite_index = -1; + + bool city_is_directly_above_port = (closest_dx == 0) && (closest_dy < 0); + bool city_is_directly_below_port = (closest_dx == 0) && (closest_dy > 0); + bool city_is_directly_west_of_port = (closest_dx < 0) && (closest_dy == 0); + bool city_is_directly_east_of_port = (closest_dx > 0) && (closest_dy == 0); + bool city_is_directly_northeast_of_port = (closest_dx == 1) && (closest_dy == -1); + bool city_is_directly_southeast_of_port = (closest_dx == 1) && (closest_dy == 1); + bool city_is_directly_southwest_of_port = (closest_dx == -1) && (closest_dy == 1); + bool city_is_directly_northwest_of_port = (closest_dx == -1) && (closest_dy == -1); + bool city_is_west_of_port = (closest_dx < 0); + bool city_is_east_of_port = (closest_dx > 0); + bool city_is_north_of_port = (closest_dy < 0); + bool city_is_south_of_port = (closest_dy > 0); + bool northwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y - 1); + bool north_tile_is_land = tile_offset_is_land (tile_x, tile_y - 1); + bool northeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y - 1); + bool east_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y); + bool southeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y + 1); + bool south_tile_is_land = tile_offset_is_land (tile_x, tile_y + 1); + bool southwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y + 1); + bool west_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y); + + // Variant indices + int NONE = -1; + int NW = 0; + int NE = 1; + int SE = 2; + int SW = 3; + *out_variant = NONE; + + // Additional anchor indices + int W = 0; + int N = 1; + int E = 2; + int S = 3; + int anchor = NONE; + + // Direct diagonals + if (city_is_directly_northeast_of_port) { *out_variant = SW; anchor = NE; } + else if (city_is_directly_southeast_of_port) { *out_variant = NW; anchor = SE; } + else if (city_is_directly_southwest_of_port) { *out_variant = NE; anchor = SW; } + else if (city_is_directly_northwest_of_port) { *out_variant = SE; anchor = NW; } + + // Direct cardinals + else if (city_is_directly_above_port) { *out_variant = SW; anchor = NW; } + else if (city_is_directly_below_port) { *out_variant = NE; anchor = SE; } + else if (city_is_directly_west_of_port) { *out_variant = SE; anchor = SW; } + else if (city_is_directly_east_of_port) { *out_variant = SW; anchor = SE; } + + // City is not adjacent, check relative directions + else if (city_is_north_of_port && city_is_west_of_port) { + if (northwest_tile_is_land) { *out_variant = SE; anchor = NW; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = SW; } + else if (west_tile_is_land) { *out_variant = NE; anchor = W; } + } else if (city_is_north_of_port && city_is_east_of_port) { + if (northeast_tile_is_land) { *out_variant = SW; anchor = NE; } + else if (southeast_tile_is_land) { *out_variant = NW; anchor = SE; } + else if (east_tile_is_land) { *out_variant = SW; anchor = E; } + } else if (city_is_south_of_port && city_is_east_of_port) { + if (southeast_tile_is_land) { *out_variant = NW; anchor = SE; } + else if (northeast_tile_is_land) { *out_variant = SW; anchor = NE; } + else if (east_tile_is_land) { *out_variant = SW; anchor = E; } + } else if (city_is_south_of_port && city_is_west_of_port) { + if (southwest_tile_is_land) { *out_variant = NE; anchor = SW; } + else if (northwest_tile_is_land) { *out_variant = SE; anchor = NW; } + else if (west_tile_is_land) { *out_variant = NE; anchor = W; } + } + + // No ideal direction, pick based on any land tiles around port + if (*out_variant == NONE) { + if (north_tile_is_land) *out_variant = SW; + else if (east_tile_is_land) *out_variant = SW; + else if (south_tile_is_land) *out_variant = NE; + else if (west_tile_is_land) *out_variant = NE; + else if (northeast_tile_is_land) *out_variant = SW; + else if (southeast_tile_is_land) *out_variant = NW; + else if (southwest_tile_is_land) *out_variant = NE; + else if (northwest_tile_is_land) *out_variant = SE; } } From ab2b363cd6338ad1a5ba40606250587a5e96a9aa Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 27 Nov 2025 23:52:31 -0800 Subject: [PATCH 016/356] Port direction & pixel offset system working --- injected_code.c | 99 ++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/injected_code.c b/injected_code.c index 70587797..2235e260 100644 --- a/injected_code.c +++ b/injected_code.c @@ -23657,11 +23657,12 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i } bool -tile_offset_is_land (int adj_x, int adj_y) +tile_offset_is_owner_land (int civ_id, int adj_x, int adj_y) { wrap_tile_coords (&p_bic_data->Map, &adj_x, &adj_y); Tile * adj_tile = tile_at (adj_x, adj_y); - return (adj_tile != NULL) && (adj_tile != p_null_tile) && (! adj_tile->vtable->m35_Check_Is_Water (adj_tile)); + return (adj_tile != NULL) && (adj_tile != p_null_tile) && + (adj_tile->Territory_OwnerID == civ_id) && (! adj_tile->vtable->m35_Check_Is_Water (adj_tile)); } void @@ -23762,16 +23763,16 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi bool city_is_east_of_port = (closest_dx > 0); bool city_is_north_of_port = (closest_dy < 0); bool city_is_south_of_port = (closest_dy > 0); - bool northwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y - 1); - bool north_tile_is_land = tile_offset_is_land (tile_x, tile_y - 1); - bool northeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y - 1); - bool east_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y); - bool southeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y + 1); - bool south_tile_is_land = tile_offset_is_land (tile_x, tile_y + 1); - bool southwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y + 1); - bool west_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y); - - // Variant indices + bool northwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y - 1); + bool north_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y - 1); + bool northeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y - 1); + bool east_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y); + bool southeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y + 1); + bool south_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y + 1); + bool southwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y + 1); + bool west_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y); + + // Variant indices; can't use direction enum as values are slightly different int NONE = -1; int NW = 0; int NE = 1; @@ -23779,55 +23780,56 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi int SW = 3; *out_variant = NONE; - // Additional anchor indices - int W = 0; - int N = 1; - int E = 2; - int S = 3; - int anchor = NONE; + enum direction anchor = NONE; // Direct diagonals - if (city_is_directly_northeast_of_port) { *out_variant = SW; anchor = NE; } - else if (city_is_directly_southeast_of_port) { *out_variant = NW; anchor = SE; } - else if (city_is_directly_southwest_of_port) { *out_variant = NE; anchor = SW; } - else if (city_is_directly_northwest_of_port) { *out_variant = SE; anchor = NW; } + if (city_is_directly_northeast_of_port) { *out_variant = SW; anchor = DIR_NE; } + else if (city_is_directly_southeast_of_port) { *out_variant = NW; anchor = DIR_SE; } + else if (city_is_directly_southwest_of_port) { *out_variant = NE; anchor = DIR_SW; } + else if (city_is_directly_northwest_of_port) { *out_variant = SE; anchor = DIR_NW; } // Direct cardinals - else if (city_is_directly_above_port) { *out_variant = SW; anchor = NW; } - else if (city_is_directly_below_port) { *out_variant = NE; anchor = SE; } - else if (city_is_directly_west_of_port) { *out_variant = SE; anchor = SW; } - else if (city_is_directly_east_of_port) { *out_variant = SW; anchor = SE; } + else if (city_is_directly_above_port) { *out_variant = SW; anchor = DIR_NW; } + else if (city_is_directly_below_port) { *out_variant = NE; anchor = DIR_SE; } + else if (city_is_directly_west_of_port) { *out_variant = SE; anchor = DIR_SW; } + else if (city_is_directly_east_of_port) { *out_variant = SW; anchor = DIR_SE; } // City is not adjacent, check relative directions else if (city_is_north_of_port && city_is_west_of_port) { - if (northwest_tile_is_land) { *out_variant = SE; anchor = NW; } - else if (southwest_tile_is_land) { *out_variant = NE; anchor = SW; } - else if (west_tile_is_land) { *out_variant = NE; anchor = W; } + if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else if (west_tile_is_land) { *out_variant = SE; anchor = DIR_W; } } else if (city_is_north_of_port && city_is_east_of_port) { - if (northeast_tile_is_land) { *out_variant = SW; anchor = NE; } - else if (southeast_tile_is_land) { *out_variant = NW; anchor = SE; } - else if (east_tile_is_land) { *out_variant = SW; anchor = E; } + if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } } else if (city_is_south_of_port && city_is_east_of_port) { - if (southeast_tile_is_land) { *out_variant = NW; anchor = SE; } - else if (northeast_tile_is_land) { *out_variant = SW; anchor = NE; } - else if (east_tile_is_land) { *out_variant = SW; anchor = E; } + if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } } else if (city_is_south_of_port && city_is_west_of_port) { - if (southwest_tile_is_land) { *out_variant = NE; anchor = SW; } - else if (northwest_tile_is_land) { *out_variant = SE; anchor = NW; } - else if (west_tile_is_land) { *out_variant = NE; anchor = W; } + if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } } // No ideal direction, pick based on any land tiles around port if (*out_variant == NONE) { - if (north_tile_is_land) *out_variant = SW; - else if (east_tile_is_land) *out_variant = SW; - else if (south_tile_is_land) *out_variant = NE; - else if (west_tile_is_land) *out_variant = NE; - else if (northeast_tile_is_land) *out_variant = SW; - else if (southeast_tile_is_land) *out_variant = NW; - else if (southwest_tile_is_land) *out_variant = NE; - else if (northwest_tile_is_land) *out_variant = SE; + if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (north_tile_is_land) { *out_variant = SW; anchor = DIR_N; } + else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } + else if (south_tile_is_land) { *out_variant = NE; anchor = DIR_S; } + else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } + else { *out_variant = SW; anchor = DIR_NW; } // Shouldn't happen but just in case } + + // Determine pixel offsets based on direction & anchor + if (*out_variant == SW && anchor == DIR_NE) { *out_pixel_x -= 0; *out_pixel_y += 6; } + else if (*out_variant == SE && anchor == DIR_NW) { *out_pixel_x -= 2; *out_pixel_y += 6; } + else if (*out_variant == SE && anchor == DIR_W) { *out_pixel_x -= 30; *out_pixel_y += 8; } } void __fastcall @@ -23941,10 +23943,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par case PORT_DISTRICT_ID: { get_port_district_variant_for_tile (tile, &variant, &pixel_x, &pixel_y); - char ss[200]; - snprintf (ss, sizeof ss, "Port district at tile (%d,%d) using variant %d\n", tile_x, tile_y, variant); - (*p_OutputDebugStringA) (ss); - break; + // Don't break, let fall through to default to count buildings } default: { From 499ba1f21b26401b8d23ceedbf0e8058ee71f04e Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 28 Nov 2025 09:27:03 -0800 Subject: [PATCH 017/356] Added sheet index info temporarily to debug terrain info --- injected_code.c | 127 ++++++++++++++++++++++++++---------------------- 1 file changed, 69 insertions(+), 58 deletions(-) diff --git a/injected_code.c b/injected_code.c index 2235e260..446652f6 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16006,14 +16006,17 @@ patch_PCX_Image_draw_tile_info_terrain (PCX_Image * this, int edx, char * str, i } } - // Show sprites indexes for debugging - int sheet_index = (tile->SquareParts >> 8) & 0xFF; - int sprite_index = tile->SquareParts & 0xFF; - - snprintf (s, sizeof s, "%s, (%d, %d)", display_name, sheet_index, sprite_index); + snprintf (s, sizeof s, "%s", display_name); PCX_Image_draw_text (this, __, s, x + 68, y, strlen (s)); } } + + // Show sprites indexes for port position debugging + int sheet_index = (tile->SquareParts >> 8) & 0xFF; + int sprite_index = tile->SquareParts & 0xFF; + snprintf (s, sizeof s, "(%d, %d)", sheet_index, sprite_index); + PCX_Image_draw_text (this, __, s, x, y - 18, strlen (s)); + // Draw tile coords on line below terrain name snprintf (s, sizeof s, "(%d, %d)", is->viewing_tile_info_x, is->viewing_tile_info_y); PCX_Image_draw_text (this, __, s, x, y + 14, strlen (s)); @@ -23681,7 +23684,7 @@ get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * ou } void -get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) +set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) { if ((tile == NULL) || (tile == p_null_tile) || (out_variant == NULL)) return; @@ -23749,8 +23752,6 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi if (closest_city == NULL) return; - int sheet_index = -1, sprite_index = -1; - bool city_is_directly_above_port = (closest_dx == 0) && (closest_dy < 0); bool city_is_directly_below_port = (closest_dx == 0) && (closest_dy > 0); bool city_is_directly_west_of_port = (closest_dx < 0) && (closest_dy == 0); @@ -23759,18 +23760,6 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi bool city_is_directly_southeast_of_port = (closest_dx == 1) && (closest_dy == 1); bool city_is_directly_southwest_of_port = (closest_dx == -1) && (closest_dy == 1); bool city_is_directly_northwest_of_port = (closest_dx == -1) && (closest_dy == -1); - bool city_is_west_of_port = (closest_dx < 0); - bool city_is_east_of_port = (closest_dx > 0); - bool city_is_north_of_port = (closest_dy < 0); - bool city_is_south_of_port = (closest_dy > 0); - bool northwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y - 1); - bool north_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y - 1); - bool northeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y - 1); - bool east_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y); - bool southeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y + 1); - bool south_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y + 1); - bool southwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y + 1); - bool west_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y); // Variant indices; can't use direction enum as values are slightly different int NONE = -1; @@ -23789,53 +23778,69 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi else if (city_is_directly_northwest_of_port) { *out_variant = SE; anchor = DIR_NW; } // Direct cardinals - else if (city_is_directly_above_port) { *out_variant = SW; anchor = DIR_NW; } - else if (city_is_directly_below_port) { *out_variant = NE; anchor = DIR_SE; } - else if (city_is_directly_west_of_port) { *out_variant = SE; anchor = DIR_SW; } - else if (city_is_directly_east_of_port) { *out_variant = SW; anchor = DIR_SE; } - - // City is not adjacent, check relative directions - else if (city_is_north_of_port && city_is_west_of_port) { - if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } - else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } - else if (west_tile_is_land) { *out_variant = SE; anchor = DIR_W; } - } else if (city_is_north_of_port && city_is_east_of_port) { - if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } - else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } - else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } - } else if (city_is_south_of_port && city_is_east_of_port) { - if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } - else if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } - else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } - } else if (city_is_south_of_port && city_is_west_of_port) { - if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } - else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } - else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } - } - - // No ideal direction, pick based on any land tiles around port - if (*out_variant == NONE) { - if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } - else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } - else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } - else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } - else if (north_tile_is_land) { *out_variant = SW; anchor = DIR_N; } - else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } - else if (south_tile_is_land) { *out_variant = NE; anchor = DIR_S; } - else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } - else { *out_variant = SW; anchor = DIR_NW; } // Shouldn't happen but just in case + else if (city_is_directly_above_port) { *out_variant = SW; anchor = DIR_N; } + else if (city_is_directly_below_port) { *out_variant = NE; anchor = DIR_S; } + else if (city_is_directly_west_of_port) { *out_variant = NE; anchor = DIR_W; } + else if (city_is_directly_east_of_port) { *out_variant = SW; anchor = DIR_E; } + + // City not adjacent, check relative directions + else { + bool city_is_west_of_port = (closest_dx < 0); + bool city_is_east_of_port = (closest_dx > 0); + bool city_is_north_of_port = (closest_dy < 0); + bool city_is_south_of_port = (closest_dy > 0); + bool northwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y - 1); + bool north_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y - 2); + bool northeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y - 1); + bool east_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 2, tile_y); + bool southeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y + 1); + bool south_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y + 2); + bool southwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y + 1); + bool west_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 2, tile_y); + + if (city_is_north_of_port && city_is_west_of_port) { + if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (west_tile_is_land) { *out_variant = SE; anchor = DIR_W; } + } else if (city_is_north_of_port && city_is_east_of_port) { + if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } + } else if (city_is_south_of_port && city_is_east_of_port) { + if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } + } else if (city_is_south_of_port && city_is_west_of_port) { + if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } + } + + // No ideal direction, pick based on any owner land tiles around port + if (*out_variant == NONE) { + if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (north_tile_is_land) { *out_variant = SW; anchor = DIR_N; } + else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } + else if (south_tile_is_land) { *out_variant = NE; anchor = DIR_S; } + else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } + else { *out_variant = SW; anchor = DIR_NW; } // Shouldn't happen but just in case + } } // Determine pixel offsets based on direction & anchor if (*out_variant == SW && anchor == DIR_NE) { *out_pixel_x -= 0; *out_pixel_y += 6; } else if (*out_variant == SE && anchor == DIR_NW) { *out_pixel_x -= 2; *out_pixel_y += 6; } - else if (*out_variant == SE && anchor == DIR_W) { *out_pixel_x -= 30; *out_pixel_y += 8; } + else if (*out_variant == SE && anchor == DIR_W) { *out_pixel_x -= 30; *out_pixel_y += 12; } } void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - //*p_debug_mode_bits |= 0xC; + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -23858,6 +23863,12 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par if (is->dc_img_state != IS_OK) return; + /* + if (is->current_config.show_detailed_tile_info) { + tile = tile_at (is->viewing_tile_info_x, is->viewing_tile_info_y); + } + */ + // Natural Wonder if (district_id == NATURAL_WONDER_DISTRICT_ID) { if (! is->current_config.enable_natural_wonders) @@ -23942,7 +23953,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } case PORT_DISTRICT_ID: { - get_port_district_variant_for_tile (tile, &variant, &pixel_x, &pixel_y); + set_port_variant_and_pixel_offsets (tile, &variant, &pixel_x, &pixel_y); // Don't break, let fall through to default to count buildings } default: From a4385c66bf0107e19254a7bc57a2a12461b8f489 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 29 Nov 2025 09:14:38 -0800 Subject: [PATCH 018/356] Refine port placement logic --- injected_code.c | 74 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/injected_code.c b/injected_code.c index 446652f6..5d6b0d3c 100644 --- a/injected_code.c +++ b/injected_code.c @@ -23660,12 +23660,15 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i } bool -tile_offset_is_owner_land (int civ_id, int adj_x, int adj_y) +tile_offset_is_land (int civ_id, int tile_x, int tile_y, bool must_be_same_owner) { - wrap_tile_coords (&p_bic_data->Map, &adj_x, &adj_y); - Tile * adj_tile = tile_at (adj_x, adj_y); - return (adj_tile != NULL) && (adj_tile != p_null_tile) && - (adj_tile->Territory_OwnerID == civ_id) && (! adj_tile->vtable->m35_Check_Is_Water (adj_tile)); + if (must_be_same_owner && (civ_id <= 0)) + return false; + + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + return (tile != NULL) && (tile != p_null_tile) && (! tile->vtable->m35_Check_Is_Water (tile)) && + ((! must_be_same_owner) || (tile->Territory_OwnerID == civ_id)); } void @@ -23674,7 +23677,7 @@ get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * ou wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); Tile * tile = tile_at (tile_x, tile_y); - if (tile != NULL & tile != p_null_tile) { + if (tile != NULL && tile != p_null_tile) { *out_sheet_index = (tile->SquareParts >> 8) & 0xFF; *out_sprite_index = tile->SquareParts & 0xFF; } else { @@ -23789,14 +23792,14 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi bool city_is_east_of_port = (closest_dx > 0); bool city_is_north_of_port = (closest_dy < 0); bool city_is_south_of_port = (closest_dy > 0); - bool northwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y - 1); - bool north_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y - 2); - bool northeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y - 1); - bool east_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 2, tile_y); - bool southeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y + 1); - bool south_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y + 2); - bool southwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y + 1); - bool west_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 2, tile_y); + bool northwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y - 1, true); + bool north_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y - 2, true); + bool northeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y - 1, true); + bool east_tile_is_land = tile_offset_is_land (owner_id, tile_x + 2, tile_y, true); + bool southeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y + 1, true); + bool south_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y + 2, true); + bool southwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y + 1, true); + bool west_tile_is_land = tile_offset_is_land (owner_id, tile_x - 2, tile_y, true); if (city_is_north_of_port && city_is_west_of_port) { if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } @@ -23827,14 +23830,55 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } else if (south_tile_is_land) { *out_variant = NE; anchor = DIR_S; } else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } - else { *out_variant = SW; anchor = DIR_NW; } // Shouldn't happen but just in case + } + + // Same civ doesn't own any adjacent land tiles, pick based on *any* land tiles + if (*out_variant == NONE) { + bool northwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y - 1, false); + bool north_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y - 2, false); + bool northeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y - 1, false); + bool east_tile_is_land = tile_offset_is_land (owner_id, tile_x + 2, tile_y, false); + bool southeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y + 1, false); + bool south_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y + 2, false); + bool southwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y + 1, false); + bool west_tile_is_land = tile_offset_is_land (owner_id, tile_x - 2, tile_y, false); + if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (north_tile_is_land) { *out_variant = SW; anchor = DIR_N; } + else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } + else if (south_tile_is_land) { *out_variant = NE; anchor = DIR_S; } + else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } + else { *out_variant = SW; anchor = DIR_NE; } // Somehow no land tiles at all? Default to NE } } + int anchor_sheet_index, anchor_sprite_index; + switch (anchor) { + case DIR_N: get_tile_sprite_indices (tile_x, tile_y - 2, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_NE: get_tile_sprite_indices (tile_x + 1, tile_y - 1, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_E: get_tile_sprite_indices (tile_x + 2, tile_y, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_SE: get_tile_sprite_indices (tile_x + 1, tile_y + 1, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_S: get_tile_sprite_indices (tile_x, tile_y + 2, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_SW: get_tile_sprite_indices (tile_x - 1, tile_y + 1, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_W: get_tile_sprite_indices (tile_x - 2, tile_y, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_NW: get_tile_sprite_indices (tile_x - 1, tile_y - 1, &anchor_sheet_index, &anchor_sprite_index); break; + default: anchor_sheet_index = -1; anchor_sprite_index = -1; break; + } + + bool anchor_is_hill = anchor_sprite_index >= 50; + // Determine pixel offsets based on direction & anchor if (*out_variant == SW && anchor == DIR_NE) { *out_pixel_x -= 0; *out_pixel_y += 6; } else if (*out_variant == SE && anchor == DIR_NW) { *out_pixel_x -= 2; *out_pixel_y += 6; } else if (*out_variant == SE && anchor == DIR_W) { *out_pixel_x -= 30; *out_pixel_y += 12; } + + // 0 & 5 tend to be recessed compared to other sheets + if (! anchor_is_hill & (anchor_sheet_index == 0 || anchor_sheet_index == 5)) { + if (anchor == DIR_W || anchor == DIR_NW || anchor == DIR_SW) *out_pixel_x -= 6; + else if (anchor == DIR_E || anchor == DIR_NE || anchor == DIR_SE) *out_pixel_x += 6; + } } void __fastcall From 003d7f73bf66d8b230aac82130a03af7438e3c34 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 29 Nov 2025 15:52:30 -0800 Subject: [PATCH 019/356] More port positioning logic --- injected_code.c | 81 +++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/injected_code.c b/injected_code.c index 5d6b0d3c..cf58abfa 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16014,7 +16014,7 @@ patch_PCX_Image_draw_tile_info_terrain (PCX_Image * this, int edx, char * str, i // Show sprites indexes for port position debugging int sheet_index = (tile->SquareParts >> 8) & 0xFF; int sprite_index = tile->SquareParts & 0xFF; - snprintf (s, sizeof s, "(%d, %d)", sheet_index, sprite_index); + snprintf (s, sizeof s, "%d, %d", sheet_index, sprite_index); PCX_Image_draw_text (this, __, s, x, y - 18, strlen (s)); // Draw tile coords on line below terrain name @@ -23671,7 +23671,7 @@ tile_offset_is_land (int civ_id, int tile_x, int tile_y, bool must_be_same_owner ((! must_be_same_owner) || (tile->Territory_OwnerID == civ_id)); } -void +Tile * get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * out_sprite_index) { wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); @@ -23684,6 +23684,8 @@ get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * ou *out_sheet_index = -1; *out_sprite_index = -1; } + + return tile; } void @@ -23755,8 +23757,8 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi if (closest_city == NULL) return; - bool city_is_directly_above_port = (closest_dx == 0) && (closest_dy < 0); - bool city_is_directly_below_port = (closest_dx == 0) && (closest_dy > 0); + bool city_is_directly_above_port = (closest_dx == 0) && (closest_dy == -1); + bool city_is_directly_below_port = (closest_dx == 0) && (closest_dy == 1); bool city_is_directly_west_of_port = (closest_dx < 0) && (closest_dy == 0); bool city_is_directly_east_of_port = (closest_dx > 0) && (closest_dy == 0); bool city_is_directly_northeast_of_port = (closest_dx == 1) && (closest_dy == -1); @@ -23780,13 +23782,7 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi else if (city_is_directly_southwest_of_port) { *out_variant = NE; anchor = DIR_SW; } else if (city_is_directly_northwest_of_port) { *out_variant = SE; anchor = DIR_NW; } - // Direct cardinals - else if (city_is_directly_above_port) { *out_variant = SW; anchor = DIR_N; } - else if (city_is_directly_below_port) { *out_variant = NE; anchor = DIR_S; } - else if (city_is_directly_west_of_port) { *out_variant = NE; anchor = DIR_W; } - else if (city_is_directly_east_of_port) { *out_variant = SW; anchor = DIR_E; } - - // City not adjacent, check relative directions + // City either in a direct cardinal direction or not adjacent, check relative directions else { bool city_is_west_of_port = (closest_dx < 0); bool city_is_east_of_port = (closest_dx > 0); @@ -23801,7 +23797,23 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi bool southwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y + 1, true); bool west_tile_is_land = tile_offset_is_land (owner_id, tile_x - 2, tile_y, true); - if (city_is_north_of_port && city_is_west_of_port) { + if (city_is_directly_above_port) { + if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else { *out_variant = SW; anchor = DIR_N; } + } else if (city_is_directly_below_port) { + if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else { *out_variant = NE; anchor = DIR_S; } + } else if (city_is_directly_west_of_port) { + if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else { *out_variant = SE; anchor = DIR_W; } + } else if (city_is_directly_east_of_port) { + if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else { *out_variant = SW; anchor = DIR_E; } + } else if (city_is_north_of_port && city_is_west_of_port) { if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } else if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } @@ -23817,6 +23829,7 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi } else if (city_is_south_of_port && city_is_west_of_port) { if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (west_tile_is_land && ! north_tile_is_land) { *out_variant = NE; anchor = DIR_W; } else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } } @@ -23829,7 +23842,7 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi else if (north_tile_is_land) { *out_variant = SW; anchor = DIR_N; } else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } else if (south_tile_is_land) { *out_variant = NE; anchor = DIR_S; } - else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } + else if (west_tile_is_land) { *out_variant = SE; anchor = DIR_W; } } // Same civ doesn't own any adjacent land tiles, pick based on *any* land tiles @@ -23849,35 +23862,43 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi else if (north_tile_is_land) { *out_variant = SW; anchor = DIR_N; } else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } else if (south_tile_is_land) { *out_variant = NE; anchor = DIR_S; } - else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } + else if (west_tile_is_land) { *out_variant = SE; anchor = DIR_W; } else { *out_variant = SW; anchor = DIR_NE; } // Somehow no land tiles at all? Default to NE } } + Tile * anchor_tile; int anchor_sheet_index, anchor_sprite_index; switch (anchor) { - case DIR_N: get_tile_sprite_indices (tile_x, tile_y - 2, &anchor_sheet_index, &anchor_sprite_index); break; - case DIR_NE: get_tile_sprite_indices (tile_x + 1, tile_y - 1, &anchor_sheet_index, &anchor_sprite_index); break; - case DIR_E: get_tile_sprite_indices (tile_x + 2, tile_y, &anchor_sheet_index, &anchor_sprite_index); break; - case DIR_SE: get_tile_sprite_indices (tile_x + 1, tile_y + 1, &anchor_sheet_index, &anchor_sprite_index); break; - case DIR_S: get_tile_sprite_indices (tile_x, tile_y + 2, &anchor_sheet_index, &anchor_sprite_index); break; - case DIR_SW: get_tile_sprite_indices (tile_x - 1, tile_y + 1, &anchor_sheet_index, &anchor_sprite_index); break; - case DIR_W: get_tile_sprite_indices (tile_x - 2, tile_y, &anchor_sheet_index, &anchor_sprite_index); break; - case DIR_NW: get_tile_sprite_indices (tile_x - 1, tile_y - 1, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_N: anchor_tile = get_tile_sprite_indices (tile_x, tile_y - 2, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_NE: anchor_tile = get_tile_sprite_indices (tile_x + 1, tile_y - 1, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_E: anchor_tile = get_tile_sprite_indices (tile_x + 2, tile_y, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_SE: anchor_tile = get_tile_sprite_indices (tile_x + 1, tile_y + 1, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_S: anchor_tile = get_tile_sprite_indices (tile_x, tile_y + 2, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_SW: anchor_tile = get_tile_sprite_indices (tile_x - 1, tile_y + 1, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_W: anchor_tile = get_tile_sprite_indices (tile_x - 2, tile_y, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_NW: anchor_tile = get_tile_sprite_indices (tile_x - 1, tile_y - 1, &anchor_sheet_index, &anchor_sprite_index); break; default: anchor_sheet_index = -1; anchor_sprite_index = -1; break; } - bool anchor_is_hill = anchor_sprite_index >= 50; - - // Determine pixel offsets based on direction & anchor + // Determine general pixel offsets based on direction & anchor if (*out_variant == SW && anchor == DIR_NE) { *out_pixel_x -= 0; *out_pixel_y += 6; } else if (*out_variant == SE && anchor == DIR_NW) { *out_pixel_x -= 2; *out_pixel_y += 6; } - else if (*out_variant == SE && anchor == DIR_W) { *out_pixel_x -= 30; *out_pixel_y += 12; } + else if (*out_variant == NW && anchor == DIR_SE) { *out_pixel_x -= 0; *out_pixel_y -= 2; } + else if (*out_variant == SW && anchor == DIR_N) { *out_pixel_x -= 6; *out_pixel_y -= 10; } + else if (*out_variant == SW && anchor == DIR_E) { *out_pixel_x += 36; *out_pixel_y += 18; } + else if (*out_variant == NE && anchor == DIR_S) { *out_pixel_x += 20; *out_pixel_y += 20; } + + bool anchor_is_hill = anchor_tile != NULL && anchor_tile->vtable->m50_Get_Square_BaseType (anchor_tile) == SQ_Hills; + bool anchor_is_mountain = anchor_tile != NULL && anchor_tile->vtable->m50_Get_Square_BaseType (anchor_tile) == SQ_Mountains; + + // Handle edge cases. Annoying, but looks quite a bit better so worth it + if (! (anchor_is_hill | anchor_is_mountain)) { + if ((*out_variant == SW || *out_variant == NW) && anchor_sheet_index == 0) { *out_pixel_x += 24; } + else if ((*out_variant == SE || *out_variant == NE) && anchor_sheet_index == 0) { *out_pixel_x -= 24; } - // 0 & 5 tend to be recessed compared to other sheets - if (! anchor_is_hill & (anchor_sheet_index == 0 || anchor_sheet_index == 5)) { - if (anchor == DIR_W || anchor == DIR_NW || anchor == DIR_SW) *out_pixel_x -= 6; - else if (anchor == DIR_E || anchor == DIR_NE || anchor == DIR_SE) *out_pixel_x += 6; + if (*out_variant == SW && anchor == DIR_NE && anchor_sheet_index == 0 && anchor_sprite_index == 26) { *out_pixel_x -= 4; *out_pixel_y -= 6; } + else if (*out_variant == NE && anchor == DIR_SW && anchor_sheet_index == 0 && anchor_sprite_index == 20) { *out_pixel_x -= 2; *out_pixel_y += 4; } } } From 7a49a33b87bf817e70d312b992042c4f2c8242f2 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 09:02:10 -0800 Subject: [PATCH 020/356] Tentatively done with port placement algorithm --- injected_code.c | 107 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 79 insertions(+), 28 deletions(-) diff --git a/injected_code.c b/injected_code.c index cf58abfa..692ac75a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -23689,7 +23689,7 @@ get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * ou } void -set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) +align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) { if ((tile == NULL) || (tile == p_null_tile) || (out_variant == NULL)) return; @@ -23775,12 +23775,13 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi *out_variant = NONE; enum direction anchor = NONE; + bool direct_diagonal = false; // Direct diagonals - if (city_is_directly_northeast_of_port) { *out_variant = SW; anchor = DIR_NE; } - else if (city_is_directly_southeast_of_port) { *out_variant = NW; anchor = DIR_SE; } - else if (city_is_directly_southwest_of_port) { *out_variant = NE; anchor = DIR_SW; } - else if (city_is_directly_northwest_of_port) { *out_variant = SE; anchor = DIR_NW; } + if (city_is_directly_northeast_of_port) { *out_variant = SW; anchor = DIR_NE; direct_diagonal = true; } + else if (city_is_directly_southeast_of_port) { *out_variant = NW; anchor = DIR_SE; direct_diagonal = true; } + else if (city_is_directly_southwest_of_port) { *out_variant = NE; anchor = DIR_SW; direct_diagonal = true; } + else if (city_is_directly_northwest_of_port) { *out_variant = SE; anchor = DIR_NW; direct_diagonal = true; } // City either in a direct cardinal direction or not adjacent, check relative directions else { @@ -23797,40 +23798,46 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi bool southwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y + 1, true); bool west_tile_is_land = tile_offset_is_land (owner_id, tile_x - 2, tile_y, true); - if (city_is_directly_above_port) { + if (city_is_directly_above_port) { if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } - else { *out_variant = SW; anchor = DIR_N; } + else { + *out_variant = SW; anchor = DIR_NE; + *out_pixel_x -= 4; *out_pixel_y -= 4; + } } else if (city_is_directly_below_port) { if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } - else { *out_variant = NE; anchor = DIR_S; } + else { + *out_variant = NE; anchor = DIR_SW; + *out_pixel_x += 4; *out_pixel_y += 4; + } } else if (city_is_directly_west_of_port) { if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } - else { *out_variant = SE; anchor = DIR_W; } + else { + *out_variant = SE; anchor = DIR_NW; + *out_pixel_x -= 50; *out_pixel_y += 14; + } } else if (city_is_directly_east_of_port) { if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } - else { *out_variant = SW; anchor = DIR_E; } + else { + *out_variant = SW; anchor = DIR_NE; + *out_pixel_x += 50; *out_pixel_y -= 14; + } } else if (city_is_north_of_port && city_is_west_of_port) { if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } - else if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } - else if (west_tile_is_land) { *out_variant = SE; anchor = DIR_W; } } else if (city_is_north_of_port && city_is_east_of_port) { if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } - else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } } else if (city_is_south_of_port && city_is_east_of_port) { if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } else if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } - else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } } else if (city_is_south_of_port && city_is_west_of_port) { if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } - else if (west_tile_is_land && ! north_tile_is_land) { *out_variant = NE; anchor = DIR_W; } - else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } } // No ideal direction, pick based on any owner land tiles around port @@ -23881,25 +23888,69 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi default: anchor_sheet_index = -1; anchor_sprite_index = -1; break; } + bool anchor_is_hill = anchor_tile != NULL && anchor_tile->vtable->m50_Get_Square_BaseType (anchor_tile) == SQ_Hills; + bool anchor_is_mountain = anchor_tile != NULL && anchor_tile->vtable->m50_Get_Square_BaseType (anchor_tile) == SQ_Mountains; + // Determine general pixel offsets based on direction & anchor if (*out_variant == SW && anchor == DIR_NE) { *out_pixel_x -= 0; *out_pixel_y += 6; } else if (*out_variant == SE && anchor == DIR_NW) { *out_pixel_x -= 2; *out_pixel_y += 6; } else if (*out_variant == NW && anchor == DIR_SE) { *out_pixel_x -= 0; *out_pixel_y -= 2; } - else if (*out_variant == SW && anchor == DIR_N) { *out_pixel_x -= 6; *out_pixel_y -= 10; } + else if (*out_variant == SW && anchor == DIR_N) { *out_pixel_x -= 56; *out_pixel_y -= 26; } else if (*out_variant == SW && anchor == DIR_E) { *out_pixel_x += 36; *out_pixel_y += 18; } - else if (*out_variant == NE && anchor == DIR_S) { *out_pixel_x += 20; *out_pixel_y += 20; } + else if (*out_variant == NE && anchor == DIR_S) { *out_pixel_x += 70; *out_pixel_y += 30; } + else if (*out_variant == SE && anchor == DIR_W) { *out_pixel_x -= 20; *out_pixel_y += 14; } - bool anchor_is_hill = anchor_tile != NULL && anchor_tile->vtable->m50_Get_Square_BaseType (anchor_tile) == SQ_Hills; - bool anchor_is_mountain = anchor_tile != NULL && anchor_tile->vtable->m50_Get_Square_BaseType (anchor_tile) == SQ_Mountains; + // Handle edge cases. Tedious, but looks quite a bit better so worth it + if (! (anchor_is_hill || anchor_is_mountain) && ! direct_diagonal) { + if (*out_variant == SE && anchor == DIR_W) { *out_pixel_x -= 20; *out_pixel_y += 20; } - // Handle edge cases. Annoying, but looks quite a bit better so worth it - if (! (anchor_is_hill | anchor_is_mountain)) { - if ((*out_variant == SW || *out_variant == NW) && anchor_sheet_index == 0) { *out_pixel_x += 24; } - else if ((*out_variant == SE || *out_variant == NE) && anchor_sheet_index == 0) { *out_pixel_x -= 24; } + // Sheet 0 + if (anchor_sheet_index == 0) { + if (*out_variant == SW || *out_variant == NW) { *out_pixel_x += 32; } + else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 32; } - if (*out_variant == SW && anchor == DIR_NE && anchor_sheet_index == 0 && anchor_sprite_index == 26) { *out_pixel_x -= 4; *out_pixel_y -= 6; } - else if (*out_variant == NE && anchor == DIR_SW && anchor_sheet_index == 0 && anchor_sprite_index == 20) { *out_pixel_x -= 2; *out_pixel_y += 4; } - } + if (*out_variant == SW && anchor == DIR_NE && anchor_sprite_index == 26) { *out_pixel_x -= 4; *out_pixel_y -= 6; } + else if (*out_variant == NE && anchor == DIR_SW && anchor_sprite_index == 20) { *out_pixel_x -= 2; *out_pixel_y += 4; } + + if (*out_variant == SW && (anchor_sprite_index == 0 || anchor_sprite_index == 4 || anchor_sprite_index == 10 || anchor_sprite_index == 1)) { *out_pixel_x +=2; *out_pixel_y -= 8; } + else if (*out_variant == SW && anchor == DIR_N && (anchor_sprite_index == 6)) { *out_pixel_x -= 6; *out_pixel_y -= 8; } + else if (*out_variant == SE && anchor == DIR_W && (anchor_sprite_index == 18)) { *out_pixel_x += 6; *out_pixel_y += 4; } + } + // Sheet 1 + else if (anchor_sheet_index == 1) { + if (*out_variant == SW || *out_variant == NW) { *out_pixel_x += 10; } + else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 10; } + + if (*out_variant == SW && (anchor_sprite_index == 7 || anchor_sprite_index == 17)) { *out_pixel_x +=2; *out_pixel_y -= 8; } + } + // Sheet 3 + else if (anchor_sheet_index == 2) { + if (*out_variant == SW && (anchor_sprite_index == 6)) { *out_pixel_x +=2; *out_pixel_y -= 8; } + } + // Sheet 3 + else if (anchor_sheet_index == 3) { + if (*out_variant == SW || *out_variant == NW) { *out_pixel_x += 16; } + else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 16; } + + if (*out_variant == SW && (anchor_sprite_index == 43 || anchor_sprite_index == 40)) { *out_pixel_x +=6; *out_pixel_y -= 12; } + if (*out_variant == SW && (anchor_sprite_index == 35 || anchor_sprite_index == 39)) { *out_pixel_x +=6; *out_pixel_y -= 12; } + } + // Sheet 4 + else if (anchor_sheet_index == 4) { + if (*out_variant == SW && (anchor_sprite_index == 71)) { *out_pixel_x +=2; *out_pixel_y -= 8; } + } + // Sheet 5 + else if (anchor_sheet_index == 5) { + if (*out_variant == SW || *out_variant == NW) { *out_pixel_x += 18; } + else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 18; } + + if ((*out_variant == SW || *out_variant == SE) && anchor_sprite_index == 18) { *out_pixel_y -= 10; } + else if (*out_variant == SW && anchor_sprite_index == 73) { *out_pixel_x -=4; *out_pixel_y -= 10; } + else if (*out_variant == NW && anchor == DIR_SE && anchor_sprite_index == 42) { *out_pixel_y -= 8; } + else if ((*out_variant == NW || *out_variant == SW) && anchor_sprite_index == 6) { *out_pixel_x += 8; } + else if ((*out_variant == SW) && anchor_sprite_index == 16) { *out_pixel_x -=8; *out_pixel_y -= 10; } + } + } else if (direct_diagonal && *out_variant == SW && anchor == DIR_NE) { *out_pixel_x -=8; *out_pixel_y -= 8; } } void __fastcall @@ -24018,7 +24069,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } case PORT_DISTRICT_ID: { - set_port_variant_and_pixel_offsets (tile, &variant, &pixel_x, &pixel_y); + align_variant_and_pixel_offsets_with_coastline (tile, &variant, &pixel_x, &pixel_y); // Don't break, let fall through to default to count buildings } default: From f79f9b6e1b2c13d17f0b56f06eaf34546cbbee53 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 09:12:02 -0800 Subject: [PATCH 021/356] Add find_tile_for_port_district function --- injected_code.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/injected_code.c b/injected_code.c index 692ac75a..7b35738a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -7584,6 +7584,77 @@ find_tile_for_neighborhood_district (City * city, int * out_x, int * out_y) return NULL; } +Tile * +find_tile_for_port_district (City * city, int * out_x, int * out_y) +{ + if (city == NULL) + return NULL; + + int city_x = city->Body.X; + int city_y = city->Body.Y; + int city_work_radius = is->current_config.city_work_radius; + int threshold_for_body_of_water = 21; // Mimic vanilla behavior so AI doesn't build ports in small lakes + + // Search in order: ring 1, then rings 2..N + int ring_order[8]; + int ring_count = 0; + for (int r = 1; r <= city_work_radius; r++) + ring_order[ring_count++] = r; + + for (int r_idx = 0; r_idx < ring_count; r_idx++) { + int ring = ring_order[r_idx]; + int start_ni = workable_tile_counts[ring - 1]; + int end_ni = workable_tile_counts[ring]; + + Tile * best_tile = NULL; + int best_yield = INT_MAX; + + for (int ni = start_ni; ni < end_ni; ni++) { + int dx, dy; + patch_ni_to_diff_for_work_area (ni, &dx, &dy); + int tx = city_x + dx; + int ty = city_y + dy; + wrap_tile_coords (&p_bic_data->Map, &tx, &ty); + Tile * tile = tile_at (tx, ty); + + if ((tile == NULL) || (tile == p_null_tile)) + continue; + if (is->current_config.enable_distribution_hub_districts) { + int covered = itable_look_up_or (&is->distribution_hub_coverage_counts, (int)tile, 0); + if (covered > 0) + continue; + } + + if (! tile_suitable_for_district (tile, PORT_DISTRICT_ID, city, NULL)) + continue; + if (get_district_instance (tile) != NULL) + continue; + if (! tile->vtable->m35_Check_Is_Water (tile)) + continue; + + int continent_id = tile->vtable->m46_Get_ContinentID (tile); + if ((continent_id < 0) || (continent_id >= p_bic_data->Map.Continent_Count)) + continue; + Continent * continent = &p_bic_data->Map.Continents[continent_id]; + if (continent->Body.TileCount < threshold_for_body_of_water) + continue; + + int yield = compute_city_tile_yield_sum (city, tx, ty); + if (yield < best_yield) { + best_yield = yield; + best_tile = tile; + *out_x = tx; + *out_y = ty; + } + } + + if (best_tile != NULL) + return best_tile; + } + + return NULL; +} + Tile * find_tile_for_distribution_hub_district (City * city, int * out_x, int * out_y) { @@ -7738,6 +7809,8 @@ find_tile_for_district (City * city, int district_id, int * out_x, int * out_y) return find_tile_for_neighborhood_district (city, out_x, out_y); if (district_id == DISTRIBUTION_HUB_DISTRICT_ID) return find_tile_for_distribution_hub_district (city, out_x, out_y); + if (district_id == PORT_DISTRICT_ID) + return find_tile_for_port_district (city, out_x, out_y); int city_x = city->Body.X; int city_y = city->Body.Y; From 8e78818642f2de8fe53ab129adc305a1640a7d31 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 11:52:34 -0800 Subject: [PATCH 022/356] Add middle ages port art; Separate harbor from non-harbor art; More placement logic --- Art/Districts/1200/Port_NE.PCX | Bin 8344 -> 10517 bytes Art/Districts/1200/Port_NW.PCX | Bin 8420 -> 10581 bytes Art/Districts/1200/Port_SE.PCX | Bin 8433 -> 10704 bytes Art/Districts/1200/Port_SW.PCX | Bin 8377 -> 10508 bytes injected_code.c | 12 ++++++------ 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index 43960159456ebf8b9ee0e89ff9a78364ec720fcc..d394d027ec9ef33d0db8708d39ba71fb0a42d19c 100644 GIT binary patch delta 4029 zcma)9YfxLq6&_@)N4Sy@sx2XbWF+5UAP5OC7LveRVmw5oc5o86DGf%>kGl3?w?Uo! zNaRa9_lKu5nW4Q?H#5vYU4|xh#z`m1#J@U|U12cB7-QpC{KAf3JZ+G%n>gLmbLFNn z7`OSMvsZh*J-d6(*>BIjC;VyC-+!O-%ov-7hpeM{|A^dVUAF20BeTt&^-k}e6KocM zJ(9=q`xd*w?!$f79Ie>p8Is$q%bvZ5;1(g&ILN+1=#ixCMfnxG${5VD&X#D%Js^K! zwWQr)QQ8zuuQk8cCr7kqc?+lJCfR=a_BwCqE%%PY@<$n&X%lR3XT=v9)%0V`O?F5`*JrJ#<(N3DK z47)6kW?J;Uu(7Ewy5Agk1B}W$^%lbsh|;>2y>4?Oc3qQCm@S5*P#0ZCqwaKbEx>iz zW3yBpgUz(5b8~IneHV&Te3diMhm$;n!$}!uii-d!JC0LI(F%8MZu%6E8J{NTKB9w{aw!lke*FDoHXnW+g}OYg?Q4!BRhmC{~kiG{C;YVY&0yF zFQ=}wD%*Mh{YiP4^|3Q>K-v3o*|N0IHiX>?7);7O*3U-ZKohFZ!AbeGrKQ=5>+gW# z-8*PufDOa`reO2~M2x^0`L9chS119UfYV7izy@(A2by?VUs3^=3 z{E^zR6rc}o%KuR3%TwwV_^$`XEq|uTS-r~>UXr?vZmpifG%q_uE_O)`g4Dc)uQWyMbf=T12KV@WQyuns zcWtdC{Ukx@?lC!`ap`m^sp_5HS@@^CM7v7%X>-y-;gB=M{)!z>w_K%NQ3L3OIQ%Oq zudq=jHf*mqalX`QdrE4GJ+zaC|J)Xtz++4vS1n0#Qv0UH|ES{fUTvO*pclI;`G_~P z(`iq&JAeM(s{{_*l)unECl6~g<-6KEnPHo)E07CxcKJo!bMnu0S@Ju&l_vDyWJP3) zHy#f4IPI0b5qY03PyR%=Tt2Q_x$GJvq?rI2B?*^P>rBx(J@#RFPUqI+xKT1$LS};A z=ZEFYXR`S?93<1G6S&|>OhI7dDCgJ%j8XXz{=Jj@kS{M!$nTogXH)jcMRpDQN0ah0 z$~$a|-NYX8OF5FVEcaVRrbyz$kI%4i>^zs0%J&3Q81ic-#mMxJSFM(}SeGM7N+KEQ zSYsJplq2Vu!Ry(meIZGbMKkOJLq(9>&9AE7b!<_hyu*UcMpscqUWMtIeuXXdQ>?*B zr6PmRQT^6``BFc@R7E~lT81vH%Y8(c<4iIL#s9o16A37q4ofrehOO#1bvY`Csd^{>EdK*paH9|&|?NTuW+SF zB}yxKjkanM$ZS!Dtzt}J%rFIg%oyO!Ip%RlBe3>O6Vq=(rB10=1`rI@CeSKCZG@GLi(DiWw$h zKJCTX1t>3V2e<^iNGk~HQ_-TVpvz=;_9~il@Zv8uYb{kKwJD=4uPb1R)tL}Ad-n;( zz64^Ryi`;vwy0ujmdv!N0^NopjH{bl@?H-l^&{Z65?-# zSi4GSZQz1-=i8=L^ML)}iEuU(AEfzdH4aHI^6_ahgIky%x;}OayaW_q1XR`lbrgyQ zEF0jsLhm>SZbB5Km|yVQw0zK^nU6K}LRlJD=1}KlY+G;9WIO&KbZN*mP27itci#~B z3S(rXy)D*8jRqCX$j&1D%85M5FcT(KBF?n7l^l~q-X^5gE&}d>4%~%>b2+lhf=1p} zq>o7*baoCF-lGFh80GaUjo3=FN>zG`hI84_tAluv(l(Na$0}b~UszVK2#A7>6J3G z-)8dACqYZeXubl`s=&&5=~&%&fXIjoc-gh7b8)F7u=-ZWx<&AxBE-2 zSv9Ip*VI`3T7V-sKnWG;+rGNeCj@e6$znhYXZP_omqsgisZo?nWcEJHF9}XyUhVD@ z>AuXE3(fV5-C5;ED@o++5e32OHzgCDCW!`; zNGu@V(@b$;cSor}21&m}ZK~4MT!T@Y&DkrW%^W7o@M88JwkgcnG(=& zLY*O|xiuK8%EHENY6sW6`NMnoc9;)&5;6ZBfcY@1itKB{wPHaP;H%VTzS7_ys#O!n-ZqY9 z!4E+_M4NqxRpcdhnf;{$QyVw`$ghcmNVNNSF5i)>3#xg%n;QKYx3N-N2wMy_zpA^6 zUMo@jipdhkZ19UJTBg=|xHmRoUfjaEhAxH>=RxB5bW?x~lNbxKbGs|i3*WC6`KnzN zEL8KR#^<%(uMpb%LqLx|4sT#yxsvgxQ+O-_qD>p#L3!(ESJB$Q0_EenWB33Cb`<3e zb_)Y43Y87rLI*iU``ermeYwWsyn=wCr>h=+&WUb*LKLh!3En{Wgs@MAq# d6`M_Tmh&kqxso)7-=FSY{&LEv@0!w#{|2x|4v_!= delta 848 zcmXAnT}YE*6vuhlben7Qb>H^7&F!_6z}VJBMQT_QV{cHYjk;(#Z^iYoH@XNaBtp9A z>rbnj8dMMlMYL|a*fY&)+NMPx7Nyb$CfSEj(8crg?Cs|Foc}q0&N=X$OWjvG@0}4^ zrs=i8kr#A{G5TEN|9G0m3$#r0zXr3r{Gs78zBjsCrs$yBeqe2h{^s_g#tDs2IX}@U zZclf%B_`nyxYtb4Y+7xTia?+l;{XK))i_yDi4fd ze^b4=Of%W!+P!k5r-02;MJs)o8{4Dutvs#<>O*B3SJaFc5qlV$W=nj^bM{^1H;u1o ziK<;{`i5+84lwot7bK^Zr$|~|)f|&W#$IAhuB4Q#l0Fq9ax-JEal-4glqgY8W#fg4 zX$Bu8w}mjJpvL8aD9^k@d(dqiWvW8dED>&Ba#j7x(OC1ec5=lApc+yA>8p3 zb+S{)qf@^U_Zl2LI;i*Y$c3BwM%ZK*X7o+y=aq4|s#-yLB~i5whl38Gh`Uu0M7%Bt z)nVrhANz&9)%Zr^BrRcgbp*4LLnz~Rbr>hPcd^o`Rq7kYQXA%h@8obA)gI@OcH)KidbLc$9=9()cEj->#-US z=DBA4&2YWZ?1AWa;HcS#&mNnh!0zOi>dI*MyHPZoa<2N*y4-B=fna8pS$s2o&ADvT S5djnaArx0^%k5k_X8i|qLAAdC diff --git a/Art/Districts/1200/Port_NW.PCX b/Art/Districts/1200/Port_NW.PCX index d0d5aa10285402a249c7a76a535555366202943e..eff55508dd0956c8b2b13c4df4c37c4c4c46a756 100644 GIT binary patch delta 4265 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 GxZppiKGuu? diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index 88698227cdb99a2d0f61246639c8d509d4f961b3..95aaf7ae13cd2001f986771d8b72592a149f2609 100644 GIT binary patch delta 3813 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 delta 716 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 In=Yf}|J_xJoB#j- delta 783 zcmZwD%TH556bA52TUs8$fG`45LTFJ4pjLb>pfM!wjE{<%urRiN0Ev;VSSSk@B)ULP z!pc|^H^9P8-LP|RQC?!fR%@Z9yo=aW+A7a*#?#Ryi~Gy{zB!Y7bDrHAZtD{ki90wU zC2o=XxQn0V7!EpH+yg_i_^NYI;uuU<@TL(I!6D}f&0%*8^nOE;?({gSZM_ml@C)7E zR3^!7`~wbF!F(@{bLeJA;3dnq37n;&b>$kF!4yX2leR9WW_Q`I+Z{te^mMsft%&n9 zxB?MY1%d2g(>m;jN*LuTvVOrDok#MsW@dISp(L zdTKa0a~k*x^?uq%)|4`gA@Jrju_bs=$)VVIi7!wel0TI{lwUCb4~-Z2Jf%4qSFS1p zI0FG=3y)BW(Z6S1RL-Cu&YLdsFr{S~Q%043oPwvOb38;TF6WflskofNNeG*4EC@F( z9IA60`3&{HWI*}veH=&Oer_XNHXno6xpjPk&bH;Ga$NZVeGs7uVJ>9?%z0L>QBV6) zivJ3@omYGEuoE9#kNgEIw2@AKMj=6zgIs`rL0@0eGX6&C*mLX;yB diff --git a/injected_code.c b/injected_code.c index 7b35738a..53445a8e 100644 --- a/injected_code.c +++ b/injected_code.c @@ -2402,8 +2402,6 @@ is_tile_earmarked_for_district (int tile_x, int tile_y) return false; int civ_id = tile->vtable->m38_Get_Territory_OwnerID (tile); - if ((civ_id < 0) || (civ_id >= 32)) - return false; FOR_TABLE_ENTRIES (tei, &is->city_pending_district_requests[civ_id]) { struct pending_district_request * req = (struct pending_district_request *)(long)tei.value; @@ -23982,7 +23980,7 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, if (*out_variant == SW || *out_variant == NW) { *out_pixel_x += 32; } else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 32; } - if (*out_variant == SW && anchor == DIR_NE && anchor_sprite_index == 26) { *out_pixel_x -= 4; *out_pixel_y -= 6; } + if ((*out_variant == NE || *out_variant == NW) && anchor == DIR_S && anchor_sprite_index == 26) { *out_pixel_x -= 4; *out_pixel_y += 30; } else if (*out_variant == NE && anchor == DIR_SW && anchor_sprite_index == 20) { *out_pixel_x -= 2; *out_pixel_y += 4; } if (*out_variant == SW && (anchor_sprite_index == 0 || anchor_sprite_index == 4 || anchor_sprite_index == 10 || anchor_sprite_index == 1)) { *out_pixel_x +=2; *out_pixel_y -= 8; } @@ -23994,7 +23992,7 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, if (*out_variant == SW || *out_variant == NW) { *out_pixel_x += 10; } else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 10; } - if (*out_variant == SW && (anchor_sprite_index == 7 || anchor_sprite_index == 17)) { *out_pixel_x +=2; *out_pixel_y -= 8; } + if (*out_variant == SW && (anchor_sprite_index == 7 || anchor_sprite_index == 17)) { *out_pixel_x +=10; *out_pixel_y -= 8; } } // Sheet 3 else if (anchor_sheet_index == 2) { @@ -24020,10 +24018,12 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, if ((*out_variant == SW || *out_variant == SE) && anchor_sprite_index == 18) { *out_pixel_y -= 10; } else if (*out_variant == SW && anchor_sprite_index == 73) { *out_pixel_x -=4; *out_pixel_y -= 10; } else if (*out_variant == NW && anchor == DIR_SE && anchor_sprite_index == 42) { *out_pixel_y -= 8; } - else if ((*out_variant == NW || *out_variant == SW) && anchor_sprite_index == 6) { *out_pixel_x += 8; } + else if ((*out_variant == NW || *out_variant == SW) && anchor_sprite_index == 6) { *out_pixel_x += 12; *out_pixel_y -= 6; } else if ((*out_variant == SW) && anchor_sprite_index == 16) { *out_pixel_x -=8; *out_pixel_y -= 10; } } - } else if (direct_diagonal && *out_variant == SW && anchor == DIR_NE) { *out_pixel_x -=8; *out_pixel_y -= 8; } + } + else if (direct_diagonal && *out_variant == SW && anchor == DIR_NE) { *out_pixel_x -=8; *out_pixel_y -= 8; } + else if (direct_diagonal && *out_variant == NW && anchor == DIR_SE) { *out_pixel_x +=6; *out_pixel_y += 6; } } void __fastcall From df894a5868aeab3dba06c12c6c519c2d5215df83 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 13:36:31 -0800 Subject: [PATCH 023/356] Add intial industrial port SE --- Art/Districts/1200/Port_SW.PCX | Bin 10508 -> 12094 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_SW.PCX b/Art/Districts/1200/Port_SW.PCX index 029e70e938066833a500f7c7ea30905bd6f4ed71..b95ee048e80d5cbaf15ae2d061a3de2e8fad92cd 100644 GIT binary patch delta 2424 zcmZ8j?@wFT84hMDC4&njBhbXH)+`-KE3KN0h+)#E`ypMWiD}vwcS7nPP^tUMr+cs4 z-7nK7Etx+eS+c12rL(rAQ8cAWn)h5|&jcC=+@K~(u%QV`;vca+LNM+7?l~uPn+EcG zF6chbdEV!J&VfJv`s%xX|NTqWA}c?fF$^L`6;7aOer+dx%5ltsN%+=e=b@^p>z0aFdw zB~~G8sOO9Z=M12Zy2IOi3*g6GlB^#JN6${h?Wp3^++QeIfYe2{)xc~06OfxVZ$9#{ zY(wd7z_#%0+m7<>@yvkb48*Z(dU`U)RCNBy%Ch^czFA>qUVebu<|f|*aFf@Bs=ggE zn>x{Wz#r3cY%o;MYJ3-$QM1jUV9o4kVO)MW1xc`5LD)O1|K zQK5!yBt9o>P-R|$k?bqyuUyDs07HFU2gcflVG;E{X5Rr@p1%@mrIcfPam=#rQd#*_ zK!qw*BIT=j{|a^9M;LNEJ~90>jIjx*r}Nv9O|CG5C*X(TXMAm)5v2UM(bdo`gGmKG zbx4JLJ%ey$`qY@3Z5X6Nt?)WX0%5KRj!VmIU3SqBuvypt%%vsZuev@3T1D*iL2vdq zHG}!a)OEg#H7^}lukm`@eQQh9gy%?DHLg^YmbH+j-`Uhe82&YR^7PmbX!o%J@0xlS znH0G$PeBEo=26LP2^>wGcGMPP;n}3-3>l*9r(@L2f{y-^golP>+tfS!CDt3#(&Ea1 z!<86Rmhh^mCO)x1MSY89{Qvzn`-$PNZJ_o>!M|r>|4g03lXn zOLtrMFbyrUkc?2lraVSVl^FI$h6(2K2jB7?xDR=&I7_;&@fTRv5;G{N zrm8xD?W7zy=O%G9$~4@-iNIFqAP%~SI{EY2!Og_kW5ZrU;1lFPR9Ux+ceFhH)THX1 zv9hk5jIy2i6dGTln_4~E!JMd_CYg&U_pP_bDiCuAan*H|Z(?1S4Spg?!(1`V0J8y^ zW-@IjK2CWrD^3=zr*TT#diu1C$>F?INvJR}ojf%*s3W(I=;?YB=Tu}STs-8zArVyY)Mco{!#$!#!dhqP6C zNc!pdXG6~&rjAp4S>S9M_enC2%UgUMBzL;2TkYO9BsxA@vW&AY@>p)^_V=yTvxLIu zR|(`Kshx2y6<-!ALMzTvfKM?HT#6<<0WK*@42DNe>qY^t;H+&JUAT|hnME=N!Xg%*H6!aot|zroK3tN);jPG+zg-s z5rDrMjtkbA^r%&l(A3G7kN2se*ZR6%Io$oz9QGC26TSDI;4d;(5rxau!#F{-p+{t< zjx|YDRmcAoUrA_uqVL7tANO=0d*Sd41pqY+;KbX#{qM9S1EUnx(LETn2zN4~Q;w68 ze<1yD9v|sF-20uw*Z&JNa80u(Ih{ECb-?wD0|Ru`nM6~!zD^{2M-Dv^Pazn{F}xr( zdUP*w?#Rd+Pb7q^B98pk?f%aHIa>g4Lsr1+S>e442jA@ak`C{`FwNj?Z_MBqO|NsA=yjDqkv8^Wa ZeDZp#l`g0~r2;AOK{&K+*sJ From cb7fecce799aa65d5414bb06e8f8ea1fbcf91d74 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 13:55:45 -0800 Subject: [PATCH 024/356] Add port SE & SW art --- Art/Districts/1200/Port_SE.PCX | Bin 10704 -> 12928 bytes Art/Districts/1200/Port_SW.PCX | Bin 12094 -> 12713 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index 95aaf7ae13cd2001f986771d8b72592a149f2609..462de498e0087544842b7203d36ce2b670663dfa 100644 GIT binary patch delta 3048 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 Z%M|rhc$>LK~OSQ2Z&d^BILMdFJ ziKvN0Bu`UO1NV_V`W3xk-pP^{)p6-;6LXz4d?yW^FtHWgk<;KQ9;10tlP3tdMNx}K tfO{NhGZwsDu)_mfcqfLBFYn0cqZm8D>&hIev3+9*6+bk_<*Rsd@gI1ij4}WK From a28c2ab40d99823c24cdadd12880e7a7060538e9 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 14:10:25 -0800 Subject: [PATCH 025/356] Add port NE art --- Art/Districts/1200/Port_NE.PCX | Bin 10517 -> 11982 bytes Art/Districts/1200/Port_SE.PCX | Bin 12928 -> 12935 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index d394d027ec9ef33d0db8708d39ba71fb0a42d19c..d7b71d4516f0717b8f2a7010dd7748689d5348bd 100644 GIT binary patch delta 2221 zcmZ8iQA`}y6*c216dM*3cfA|Dpn`F+T{V%l78$6jYScQd6w%iGaVpx6eDo($l~2Cj z%(|Z6N}G=QWofGs+5V*4meh(WTTXo6ENs>WgYh&Zc3r^Kacp+kE^EMG_1&H~U@IzU z?tA;@-uuoy@7y=P|JjXS{`DK3FH9)0KxiC5-kCs^9U>bjJ5T zRnmxcG@qvWXWm;)@qJs$lI=&^2GXB;=Q`sjHkFK7t+Ce8P^~hn9Xp5lL-jzd@ndBg z>C^HhIXdT!BiouXW9ei4qq6^iH`{cm^*<^$y#Mu=P8=WIJMArX#@lL&)?>-eX4<5! zDet>IiMFbe>$E|e@(FES^EwW{+_v(Sl~25PdS3qaL!|9!ZQ9d6shF?xW@OhRdq;ha zafTnLQv<`_!^TDLWZyyW&&G@U@2gaL_;|k@mCEyD$yb-*a`v@-qd#Y!=M_ZFqY7>R zp>AQk!%K=D4<381{|Id{f9{PRjwcteM9=X(Vw$2=o5G6M-Wxx27scT9F8GH`C0dh< zL0I7h33M-P0#xt;ei zR}AeO)u};c?-#wvcP98kTH3GwsQ=%L=r!aB&mvujX&5ckZK~&Ks>-^s8sCg;S(Pw0 z_=YMml?R4%$NLXVuyj+m86_cZ2~Q|%NxEuMnA1OOT6Nr-_ik^(DRXRK`0pnU>@Bhp zGh>E7&w-6CYg83Ed;B9z0WeFeVzAEFvC$@%BRio2j8Az|U1L|XX%9E;m0OaHp;$_q znW2yoiTx$0kOBziX!~P~RH1Uga7ZQ_T=LGR;?8xpTp1A_KrI9c4KtONF>~nco4mth zWUJDlMf=iUt`O-wje>7wiLJJE)=-#f*Y1oGzzQP~G<}xEC(Q&_P^0Pi8Oxt$THeXs zMOBBY5zrD{rIOGTQRT8B2C1>-{q}{#4@xWy>v<}TvLjw}jg*-k%2?(#ooY!Z({&=c zrcXHd;{_jUq3!qr#Xj+7QVDB^R!m)P0aDu4QOA(sZ&A!9Du}lH+vsO?}d# zJ$RO;-*=>M`7~v0@(fsNuof@)SNbq(Jg0DOrv_TT)^f6Wuhyf?1Jsy1;#zHH{(N=< zNj0d8moEP9efs*Wo1_M^3R+E5#qhEprgYh?UHn*|+JI4n#-k zC!{$FzGD$t=Pn^oIF`^tXn1G35>Al`snF3-3uI?gMSveOo74MJZE{g-`n@5d%joP` zI7(`W!OZ&qwYToB1-=JnK)j$SeGCXk$CeFrQ7))G@5@DOviK)C_HN-rlKWgD4L~Lz zd(%e}L&UC-kU+(ux_H)+&`Q*a8sY9lh96uRzk-@vNn^U2HSZ5yiC1(x_)=pvcck&vgCM*ne&2z4SQnv$F$~xCj-7T(0 zc7FG=T|=!@cccXwuR;T0T3-d4Zv6zV{Sj@uG-ZG6+N)l*E71>d4dDZuRWPUl)q@>u zMs@2vc!lq@DP^YO{iH8(RI=k+BcL)vWiVHdQs-SvBF78hOg!>>x)Tnvh628p81doT z&@TES(UQ_RW&SLMU*fyP9Poc5yP%d7Cb*My2^Jvvjz#5MI!6^iq7r_fhn@?HQ4*n_m$NQHWNjF%0qz-%^FPuAv981a1b$zuQ00{un*M3 z=o@_t5n5H3ealIMlhxdS&QImLaQx~Ad__IN$~@cyOXR6^IhSeAXs6Gj7oWnesH*w` zD|d9svq*|Y2y`=Ow|}Ohcei1ys;2H?X+ir*AT8hh+tcoimcBRkwfyu@>^I$q-s?R) V`$}K6C-zPITlxx){`Ro`{~t%f?Lq(m delta 80 zcmX>XJ2hy7g4ARQ#7OF53ok$3V*QtOro&nWOKCR*d5|>nUWP9}=Xu{#iv8r~guYghqC(luJWVD=oPgM_W fG{2e>=Mj+jg@Y$QPj(QO+&o+73DahCO;1h$&(Jp_ delta 119 zcmV--0EqvGWq@U{{UHOxL4A_}B7y>~!jpy~b_UD;#6iKylLRAa0lSlKBVGZ+lg=Y$ z1i{C_@sl+qOa{Ti!O+3(lY}Hg1n2+3?32qROaj6|lNlvJ1MC09lVBx20qv8cB|!nD ZljS8Y13|&UlNKf-1jYZs!m~UkOAP+kHkAMX From a1647e73d67f5c392332208806ef80c7a51928d6 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 14:55:45 -0800 Subject: [PATCH 026/356] Finish industrial port art --- Art/Districts/1200/Port_NE.PCX | Bin 11982 -> 12005 bytes Art/Districts/1200/Port_NW.PCX | Bin 10581 -> 12176 bytes Art/Districts/1200/Port_SE.PCX | Bin 12935 -> 12974 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index d7b71d4516f0717b8f2a7010dd7748689d5348bd..7eb5ad96d87c1f7e0a9e84e5cafcce882926100b 100644 GIT binary patch delta 117 zcmX>X`!se#mK@{4$vJZIjEY9*8sd{(UGAWo(&zT|S)2 z@#|!Lg-Diz|64v!?o{vtF+NVd0c0Hg-|~L4x?%v!vHy;5Cs!y2upj+@sO8|vSCbcL Vicfx^sLOT&$ZvSDnNR5e4*)A+H$VUY delta 93 zcmaDFdoFfEmK@W;E0eS3;u&{OJ}no`xNx$%d<3K8N*z0MRlet^fc4 diff --git a/Art/Districts/1200/Port_NW.PCX b/Art/Districts/1200/Port_NW.PCX index eff55508dd0956c8b2b13c4df4c37c4c4c46a756..3c7f3584d35b9bb8b43db9f4dba4e07404a56c18 100644 GIT binary patch delta 2452 zcmZ8iU2GKB6~=29^Wz<3(6WoMX%aRV2dRJSm|dzi8dW5%TDMgq)l^o}_ey=>$xkz5 z+Ig-_=Mib8m9u?GQR)Om6%m1R$M$;38viYr;yB*50S9dFXm>GSSKQO@&e}AptvNG$ z@A>)8ch0%@_Pc|B_-8cK(0=N4yVDu^-FF)I{NhxjV~5^r5bfg4Pv=9ahTZq1!7}Su zt)V@0(rAgX9CLnC7uuhfMxeQc$}C;y)HSI!8bbl>z_Dp9SsLZC2eSt)<$|pn^ot$b|mOZu|ZGinRrf{W#|0SBSHGs ziswwe8s2w{<628o-L8&xXF)r%r>nc?EyP%LSPOkH&s5mpwpdSRhvL0BriOXFEc$!oIKreoHm8?-TH9f z0#n7!yYN7Gk{uE*_IA!pA{{RlcyJyKCH8Xbwk227kA`&<1MGTJ^T}tYzi2FToEMl#&y~fGgJT*H4pGKjf9avBBsD3Re2SR zme7~Z`Qu@$#6m=@mz)_y*FL_gN+B!&DJl*pdBhI1VSF@TfH^&PpEZimI8UOVs;s)I z${Cr(+v6Fzj@%4UV>6}l$!Dm@QH5sCfL>uVe4JgoI5AtiGh~ao9d1os{iSj z^L~4HhgYG4S2K0#@8qJB$P)&-iwZZH)@(hXFbx{cY(1#i@xi@xe{aK~QRia2;sR_cGSNTTk7%M-8GBSY zM>mE^d?NC_BMjFSDkKMN{j#w8n&Wyr4N%3g{~NWP!7ZU_z^_d?!!qEJsu7!}KKk30 zt5;AV9~MfiP5|-_s+#Gx=+h$uwl);UbUMLkII+e8jG{kN##K-`rNiafE1&%uO-dCt z$#9dzT&k`EF>2YPwPZp#i%9A*+b$;KvXL+XS1;&9`j5%Qc;tO^Xy$tb=1&wL6^v?E zDngZ26!U+l)f|4!a!G!}_80~-LPE!+WIiW4^C1BEW${^tBjpxV@*mI5_oBN?mL4tn zF4lcHC;yH2Bv<4^m)wNFODgiv}2Z&Fqy|EO{Sg6R$CVl-qGF$$E;AO%|t+1F_6PpYgiszt+F z85dzvhM^kG996e~HN%^}T#)KSby>CAY z7$;Qh++Q&I^pLaWglP6~0!H#Mnjrx7jLbtXk6?7@A` zzd&z7K9DQ^#X5=mLY?|l73o^UvZ=aGQB<*DS715ytt zoXqN!L5EP?dk=D5dSH^}qMYW-=w{(F0As%d2_PJ3 z23Vz_)V_xFWLbWV?`1iu?wXu6fHc(gy=M5>R5tg0jU~vktjc>>S%J1CaM)+xK}R5p zh_tf&#@~AgS(O{|4)j)`7oaShVq)~5T2RZ6AvfeRIfHLcxzgI4A2d}y{p%}3d!oPG gxA(V)wLc#_{NZayr@j|me^onSp5mRqU9A593+srb>;M1& delta 101 zcmbObe>G@>iYTMR+I&)NF^39J5D#GZ4}t($X*z8H diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index f08f0560a203a60a49954098b882f03bd3ad8877..1578f8c03335c51cff896692f28300a933b1812f 100644 GIT binary patch delta 119 zcmZoqU6;CnNt$Wl!O6_hfh>prJ06}K3S=DlUwd@&a%o?dqyKACTN Vc=F0*cKLWV5WC^p=5%=lVE~7|J0bu8 delta 80 zcmV-W0I&b9Wrt<30viFslLQ-40mqYB8&Ltuldc<00nL;D8(sm>lVKcg0n(Gx9CrcK mlTICZ0oRku9drTOlS3YF0o{|f9&7>MlPMox0pYWbA0QI~WFM>m From c1f6c692f56b86d81e08fc2924328fa62f4b2390 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 15:22:57 -0800 Subject: [PATCH 027/356] Add port modern SE & SW --- Art/Districts/1200/Port_SE.PCX | Bin 12974 -> 15431 bytes Art/Districts/1200/Port_SW.PCX | Bin 12713 -> 17015 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index 1578f8c03335c51cff896692f28300a933b1812f..1c9053014e7fe25f93c9a64eeea058975502390e 100644 GIT binary patch delta 3285 zcma);yKiG#6~;%oitZ(1E~0UADL_J^0Ioy<%qR%SoZ=P~som00@ej}(?-bKer$R`U zMkoaZFo^ONu=a5t&SUbh^O$Fj?Icbd=Qzhs;^fZE-u%{aW;DzU4=HEa&RNfIeQWLg zmtQaZ_Q7wyZ&sb8Qaf|jTPFj#mMiaG@XI@w$yaWk?@Lbl=rxO{uI&FIXR>7n-d$(! zzCyloFI@$_1!q;SF0u4ZT;#>dgez@#gtZVM6@$nVju z{GG@LicrfvMjj&j?zx*wDim~Ay-OGWy~upe5j3$T?6?X*>z9cN+Q~sHEHH>Z3$PpH9U>niPmyzp^yhgb zku(R(08DYSRVwQOx3;JIQ(F={v2FaNlU|XKb!m=OK4c3V1-UkqIG~$#WNHT_vQ88q zY!GmeT^TUmv{y&weUicQQ&iHK#bFShl& zQv|31Y{Q=oqc!B{rn+}+qI^MW2Ab;D8RUvI>|8D@eq@JGEAA4xgghW%6qbW9N{sYJ zY^fN?M!sC1B4o*?K)LS|N6?tenKO`lUwznsDmtkK45p>_h14oOG>-_g=)OYEBbx-1 zm2e>lt(iu*B)vdtjI~mS7#;AhXb+Bj=XHI!S1oxsKG*QYdQ(lNkUITP!THi!AaUo5}kHmWRq`Zc7);k~H%<9!aM)yiE;KY9m%Dla^+K+kJmHL{)2Q z%Tc8J;zMRwY4mzj$nlXvq02AePmg|;Y&_?s$2i0+dGD85(kh?0>dls`<^h4VP%oCm z^lvhO%PDX(VAp#J;wgLvHNdk*y-y zvP}LDt65orY{d~B7+f`r$~Y~tpe+Zv!jWNRdcVU8Q@N!*O0x;;6N-!uv{jTad}?&4 zGOd()K1_y_)wXES|Fns`xyOh?cLs!P>VT3;Z_#l2<^iQe{3q-hl~wJJrx_fM)F01r zeq`*n6H26~!;TOd?zd^lhgCTOZ4gf04+FAG@YTzz)^N-l-}T@>Hmn@)_n3c`8*Yuu zlno<;O_!oclW%>SXaiRL<2O|^n(gv`(1iX8hl!mC!av@p1)EaKM;a8S7Nc0Y>|K6R z^`7pZCiAIs&8>1X10)zs-DvlXtSC#^iLX0P#q;m!tiTT@dU96JL>rfH3LQqF+^)oJ z=Fj^U+*i5kmbu5VV?)D+8+M+k8idnOYm+7?5AS)s=>uxHADACYx`?x>%%P%Z3vIuH z!xM<3a@ke5*%`FGf9yB8+??vku8eKvrV5MMs~X7$;jAnuuwp8JqhQcrT|+!&eoFNO z8hGEOB`Pc2Y`3y0c8~+JR z=tyt)8u0U@xh4sP^hD{GK%<-5G?^lLz3J{u{Jqb9sOtt|azkMY+3F#T`g1r5ZAx6h zGMsfzJJO#`XV!C{eZBzKITkaV$e|kL+uW=tqJW(@E7bx!M^>5Nj*-sHC*$A#u5u2_ zpOOw+MC_MYA6Y7;JL*;{-I-sEkAM2oD}Q9+%hL2WNbOhS<3D-{oVcb>5zl(}9poxm TUpCzf^}H^XzrXtYZ=?SL6ydWz delta 242 zcmX?Ju`YE(w&vs?JUSCMNlkt%DK`0xg@pdsf7ky1|M&XebubBHzXFPU{rCCbEg<{( zzuRCE#C`@8`8>HnQgU*Rg~a4QNr}leKw1$<^8o3O5)zZ|m`hA9;Fg%Y%3Nyl1Rz%8 zmYN&`q@B#AB(MJa|Nr{`S77orn0yH)uTJJQm!AAlTx#+yGpWf3fLNY`ZE^&c$mG>( zK(;E|-{y+JGm?W5fV+x{wPj(QO+}x=4glV&z HB_lHcaGr)- diff --git a/Art/Districts/1200/Port_SW.PCX b/Art/Districts/1200/Port_SW.PCX index 24a99a5261e94f3ac7be99e60ad32e261cd3f2aa..7e8b9cdea66626af8a9b3199d01bfdfa39391cf4 100644 GIT binary patch delta 5324 zcmb_gzi%7a5%$p#0_dUuX$T{A4hT}D%K=J5Y;i@3s~1R{E`7@7Q{n$0RSL z&cOWv$-Lzc(UL98TuCSUPFj+dC{ff>yDO3U5zpkCrD&WLiWcPnB$wKKGw++3Z@zid z*MA&7{^ZG@er29Bozn#kzh{am5MF01H}9?Cp72|)N`)yBKD+)_Wjmw|I{DW&QwsBT zQTX|kXRCPjmaq7!5Le(Q=BFn63b(s-1{mk~w3}?zTa!XcgjcSYPg_&Rnk&BG6`{z~ z;Q4s`wamGn(U8uceC>G7I?%&)LMpAYYpq{5gFge$Ww_IXpzIE9Jz=y)aaQ^(MzaP!A^5DI@@ctG1#{yUw0PUnBEX>}3`P?5cP zI-zA@JF~?rNm1`LDP!k)v}7YsGqr1O-17NvGyoBn%S`{a10R(5_3z7Ew0+vMU2NWw znJwP(j>O#Ob%gCac08oJZX=T`0fG_!Vd4Yq-+Q3U@fnfh4?3m6Lpre+coI{e_UEW$ zyK`?p%KFN1OZuQ|Lwv97cz$Fk&Dm1Xp>^TclWtcy_tS@SVBk>SG{`A6s$4?1H2nO3 zW;8Ldx#WF0<|Ez^TJ>sTu!`V0leQ54kq13WztMW4m}6uYG}#ad{v`UU>5uTH9zGUv`+oA&iKD~Xf4|*{Pk~VUa@Idys&9B9?$0#JABhTqI zaH?Zj;8Iv{ZNa)=SMY^!ZOUp7_z~&5)f6l_8TuGoHjwcESJP5WJ4!y*&&bUQUrHNt zx+8Q#L9-ho(R}r%`c^Bejpx5A0ikTDO69K6x6kO`tkb*+Z)nbH2p1RZDL*@~#A1KZ0Z~cM~Oj+UxL4y6I3Fz#Zok&?X+g|VJ12W z0rRyq@MB1%tfwODc>hF(bee*vfuCW{GOQ`ZebIe2f69%9ThS?{c}I33n#OY$eDDhi zPavo&r!91x>K_6axzJi6=N;;X_j5JD(+=?O$~v#%iXUCp_r3xKXa#1jZO|L+uk59s zM@d+p@;b zaD5I84s@kgbQL|dkWj^MyqdfMd<^?mC2LO=N6m2`I?@AuD6OfjxA?v>4n=AWI*llg z^b-W^9-byS+6Lr~Ji~8Tk(ZEWh&VPMKSzud8hfenUsP<{h;`WU)<+v!Hc* zlw~SPoZ1%RFe2gIeVGxCNzOI^`bbu|j_al%bx0HB(bg$ilb6^DV3i=a z;H6|h_LUH!l77jWXW}fpXJ&EOJkH3cM9+We`eGXYCr8?-77OC)z{2Fiyq?Q1Bf_!l-Opb0W3B{)5?@3t z$AU}*zEY}S^T30WwE$$KqfI@cVF0*3Fe=@i!gOYn(OwvIqdm>YgC;ysF7IL6k+>;Q z0v(u3Zv;>EEU(dH$ohqq@y;{bjkou){gqVN^x-@^ecU9XtML0Y9m^=zPt0)p_BWK|pM7n-vCOjPTL)$4u6Gb`&Avg#YkWxdd zmuQbkcgM_E4}IuFq|K-;c8P)dqd&$CyTZR!o!P`QgP<=UV&iXP1yYwC==(89ID|T9 S*KdzL`*ZHwfBfc?hyMi}XUrD> delta 280 zcmW;FJ8J?#6a`=wD??)CW8GFD3riE}nxJp1Xy3lTo>(xmQtOimnf zfWG6EN!_=Yg!GY1Q_r*M75D4VgGCsF_i*jn6udap$9Z=hdV>v|GP(z2@D2u9>|~vX zUluva%;cOl@kB)M%QcgF7Ev Date: Sun, 30 Nov 2025 15:48:42 -0800 Subject: [PATCH 028/356] Add port modern NW art --- Art/Districts/1200/Port_NW.PCX | Bin 12176 -> 14750 bytes Art/Districts/1200/Port_SE.PCX | Bin 15431 -> 15433 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_NW.PCX b/Art/Districts/1200/Port_NW.PCX index 3c7f3584d35b9bb8b43db9f4dba4e07404a56c18..690f7ce0c8c1cc77d0e36edee74318ea0aadbe67 100644 GIT binary patch delta 3642 zcmai1PfR1{6<>@{RT6fSRt$t?6{)JKD2lXdvR!3!=x7dms4A59Pus@{94dL9pv za&IJ`DkQ6@;-08d552VS8^DkZ!06j8u>Uay-c>wKbPy@~r>(3hHGysuzo57DVReDddnfn8M)7q%T z5xsPf9lt-+Hz`j_RO!KkDEo`lJAEp764IXV9Nr)7TVj}6qEL)I$le_u>|0?Yb@Alk zu=U9{Q#s}g7(?0z+rDPr;DoN}B7SZ1uHI(L_S2C_8|P`C_Oic!&l-k!i%MxK+i6ug zN=`3Qh3d4YvhQ{e{1yx$6@|e^Ak+9!rr=N*TVj~%o#Y)TsUX!tm-GUzHKbNfR%KPR zRazAx6xu>W+rgz1yHxL;&VF@o;Da5eyfMR~ss)}4IRF7>QF$*h9Qq)U{pEw9_bET& znc#>OMQc)$ToE5tNTuC`9Z6QT+3Z){0}m_gPPm?v5%ur`LaL~25rRK{dgN&(u?Zqn zqk4As!4Etzr~MGI5GIL9h$ti>C_=PU{*M+!v%UzBFn73M=#=(x-DGO+@U-4zm(-x+ z{$-rzDaee#V7Q6kB#xT6nAogCpm5=-7>Z0m1C^BAuR>s&>CmJfAQXeCvVtQRf5dVquIBKU?B$zVtbVFn?7-}C z$NPwyf`8SaH9GnS0#>w;RhV6ZH%r3rvqy(wf~2Tsjii^e_S^m5I?p%V=ZHX)oj%8% zK$0Vh9h0~d7F)EI+5=U7`VTRu_0ZiJe2yaO$yrC#_m4#L0iAlgD)SCpiu})3+rB0f1f6x2Lmj zeZSxH-ATvwn;ykDIHRIDGy*t54a$2HV|0p~&!CmS=8*oWPJKY&{hvvGP?k4DZFy+t zc@sw0_=TYhTB4R`ns@V#tGrQ)V1FB+3JScf4?V^$Q~YZ}z8ZHBvNI3m7FaOgm`=Ot zNS#Yp`WAUA&)jHa617P*v79^Oq*OH3&LIs`9O?shkX1F9db7L71Nu6q^g48!@+3*f z3mm#3Rl#99TsCyYv}I!OSvD0s=W|rC=SS2;gKFd^)H8*az0b}R_qnL;Q`H%#jsrhC zbnMZ&oG;nAE$m%L*Pv$;(@AJiuj$uJ=)>s3Hm($niar&2fL4&oLFJRWuCbd(qGD9! zIqjopj)7>zEN%TC(F16?95~(@=I$B40kEL^R4w1ms8cAD_Jxj_NZuAaf}SOg41J+Db;J)6+?MGT3Ibt)41g~| zmHB`c`3$(vjea~qQLM}C&MT5lQm0$@|TYGg1qA>*Z}W1&}wxwnMAff}C^ zx}(e3)c|4mB%WcsDhmJ&l_kK&(^nV?W7K>^jlbE9T~VSL04iAMD9Axw8PlVNv>MS1 zA@8~rX{gi+w2Vb9#FivEGx2LlFhyhaB0jIuPbgMjTm`;a3*E=!9k3oi;Pv<0KqqUVEgk7)%xGWMTM4ju7kz}L zeb1ph>(b?0`5?LZ)u8D4U0Dq`WFb|ob_+8_z#$- zZM3o7+SmrWBq1!#k_H=e(V$D`>&)mOxqRNdnc26y@4YuqUOs+Xxv_8naGx8d#>aes zpPccb#3{!rU2$Hx;9ZJtdPVRj{w3Kh@rV=tBXQOIKm*5@E46vtCY0keVUyg>%`q(#XqyL6jaw)!= z+pH@kXsLAjs()Yh2Z_wu=$N0!wlI}kEQh4lSyyl1*rd7nsk4BdL^$tBq-XQqq8f&; z7QE*`6}j|Q@!M>&?tcYr!aOzG+|eXg%yn~3?2KOyyCR!n!bf(2YUb!=O8JMZ6RFpD z*Csn+$~``jLf!mg?uk7LonYb{ccm6{mkQT?df*f$zH>`53AZSe%(PRWmFY|LFnIdz I^ diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index 1c9053014e7fe25f93c9a64eeea058975502390e..64c72d2a96987e251162219653d35041a79f104e 100644 GIT binary patch delta 37 vcmV+=0NVe@c*%IMEHMM|p_`L1F@yxc$w8r$xG{+b!T-VXp+TdwMKS^kVD%B; delta 35 tcmV+;0Nnq{c*l6KEHMPheZljSFENA!f5HEyleaO61jGM-gtJF70t!RO5byv1 From f1e14f6b816ad2b988647e4c1c48cd0f1910666b Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 16:04:03 -0800 Subject: [PATCH 029/356] Tentatively finish port district harbor art --- Art/Districts/1200/Port_NE.PCX | Bin 12005 -> 15118 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index 7eb5ad96d87c1f7e0a9e84e5cafcce882926100b..a8acb10b87210d637d3c2669a28ea4bb4937c35a 100644 GIT binary patch delta 4531 zcmZWsU2I!t8D3mfqZM|yvARi@w)tc0HeifST@pa%AgYOr@gog+s6vdJUGxqiad&)z zmMbn$(UD+K$zV;s;390%s5{f_hFCTW}4X_~f4j_o9F9RJjIews}CUY_@JI@!yRpvD$qiQKX#a|D{x#xM%e#N>e7OT4aquJd&;HF?_5aXqTD90>J?0tw;%+o_jdr76Kp+W1aac@lg(oHle+dwPxo zgP~53%**b%)89P{%!U=* zVh?Q0v`cGL5}Z1vM}~&25jA}Am?*7$ z_x-LnyTwc?O@$iUeC7dMraZg~go*y^aWG>do z-*N|f-t1FMbw^psrYdZO^q8a|at4~z7s)H?`Uscu8Fgl$1sT=0c253o3;6GX|J zo9sRB=6at$qGCfI3{gGdyLWrv=um8@fS#lq)D(M?zU;4BjufENi=Y0d z@3pq4$2D}$mi{<%;%G`Ex9U`g72SHriFZf?r%pNO-B>}2v)({eur7ga>88HTpKGdu zr$KqdCa3S#U)( zpzF}4!3`}D%6Q?M_`~{!q1Ql*dR1@peNAXkK13IeX7Qi(l%ZGM#kM1%DxN|wh&W0c zvu>^Jc(5!-J{1n*p4M?P;m*9!@l3uY4}PF^x;LiXM5z6JzjZT11uD_K6uQnl>=(Lj=$w}J zd54#TuO>p(QKf54xe|<&C#!~Lv^3adzWY#z zmr+p_p`82rv2f3=76&v`#SM;n5rnZonMf22_#me4d&k<(7$1olMJlbcxG)t_`B7oZ zsKUAo0*BZ(-fdZ18e^*ZK+J<9Ogpqo4sS2>(S2&8Zz_RE;8MnkalnwO5{)CiA!`h*$L}Q^lx<;3F6%SP5y)cy)mzUM>UMoUC?FdD}55vjM9#61#3^t9nlnb1$s=6u|t)Z(@GF3hTop5&OuqqM<|b8_K#11 zyAuk)t8knsn|1OsPs6@ub>#SR8UBr(JD2^@Il+C6JeBy;eZAtH2wIg zr(^iZsGi|30Bo9N&LFI4e^phdMpQX@&$cjoVP;xY zoj{!G@d^InEA+tIRolo@lh-r>Teh#jTm)jscJ>a0c3fYF5s%>*Su;pV8BbJ~*+tnD zn!4>(9cl(t93yrdIgnAibpH@-(`HQaJS<^*DP}ds*lzM2z!!PJ&}n@gWsR#cf*r>i#=tayu*@bOfIi(~PDv+CU#2N#4_v$i z7FBnEj4c4Rpb<*DlW48NA3M)lINdsVn08~d8K4R(vChj7SKviMucPaF2sRW#jIenO ziBv!u%mH!OX!K)dw0_x#RlA+El2{qA0$~dPc&#NcFFqA#VogyJNJKOogQc6ao#sG| z3lO)=IYVbK63|{%1n~6?&35I$XqnS!KvPsvXgS8tCi)7jY@tD1)bmH9>h0Jzm$XWw z?$LkYFNjl^2=FJQ9Y*!*nHm4dK3^_*X3b2c)?;I85NTE8QoGl}*h%H9S zxNhizz5@(}DwY&L0jnGYw89j{A}qhL)rL#FZDd*YfQ=>8aOPnb6{2AY7V=~J0<@6I zM6aO+9JazckTHqnP3Rp*FA7B20{~LWEcr+WIMg@Z!w*;~y(;Xx@Sb(Zwc&vbu14kh&+jb7LyaLvg2C!D3IbD!zoO0=@lF2vehz*3ifq~OXZaByu$b)S{?>YDy(|f4fRl!XT(NtWVFK;mL zJ6wb}89>8o@vAftG$%bNj`{XOEx^9|o`0Qef@=9mk%- zolMIsCU>w<%TmyQLP*fGKt8&&Z-5ouA$xKZ%qhCGDj=D-yRbU={ zJ*Yr4z%-o^6u!#7E%yzh+?Ezp2Z#=G^p0E}b^!(B+m69VtDh^Bg0J=EXke_nh-Qzu$Sz`JVOOPF;V! z*JeUAz;1Jp{S4=AuAK`gMntw##J)k7%~dv!A|#genINs-+4fh7n8=A45!2}>mNIao z$_!@vp0fT}=TosH@?t0hr|j+4PO;P{MiwzD5px(}4#^PNJoMQQZS54Z64M6bsS&@r67y&qV5D8M}Nf%sso7Z zCT@hQ>ra>Df53w+X1G&-g|1nES+;pN=jiaL?8c;yCC6IIx9>Qz&7`iN_vzjK=iS%3 z{S|F}_Z|(vgrk{GW?;Y5VG^jr1!rq1Vj3r*%UK7yvx!zooQCht254_MNP83Tu;CKf zIP7v=CTl>C>n!bxViqE<8Yp+Gl82$geUiMxxC*^)3yiuQvzlW}ChhU)QM-XC zGjf8sKwOb|IZ4lH8CQgQ?PfE|sLawT#vCKgQ+$~`Vdg0F6EQ7Qa+UnU5YcK(C_|8A z8w9`AOxaaeS}41z0~X5emDNJoMXZ*Rtb}Kkwe1Vk9RoAWdFD8Mm`a3m%rrB_j7lY$ z1b$T{A=Z>m(^HZpXr{8ImF!xnA!v{{gX7F$sf452saQmK_=zHUr8zoVC?+->q#f(U zWQICL5Oy{04k(TB>Nmw7#j#+EzC%g2fFGFuikwGcBQp$BJdYolq0Md+p!ra*$lwI? z-x8?9q)TR;`I-5VddV7PCYV#q@ZY7zB>56J%ZwClk6(2xDpk!5EY?$%5XqdGV=gic zsZt4}623UI Date: Sun, 30 Nov 2025 16:20:07 -0800 Subject: [PATCH 030/356] Working on commerical hub art, add buildable_on prop to configs --- Art/Districts/1200/Port_SW.PCX | Bin 17015 -> 19424 bytes default.districts_config.txt | 16 ++++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Art/Districts/1200/Port_SW.PCX b/Art/Districts/1200/Port_SW.PCX index 7e8b9cdea66626af8a9b3199d01bfdfa39391cf4..fd680ece52138bf8a481a9bd2f4def3f23398572 100644 GIT binary patch delta 2436 zcmZ`){clrc6!vz80d9qDB3svWu(b?CgQRnGGb$GxU}PFYbSP0&e()n|G=A&{y|-Pw ze*pUapmE7HIBTM?AV>(o3^?Z&O2>e1Tp5aEcg!#1w%XP?SoFT0_qH4JD_P&0_P*yi z=RD7I&L%aQrSB27Qq zN=cC0J*>>L-gAOO#uf4g(H&}f0VYOl#kHUW(%g=-ZJzZt1`olpa)YBraG~(7i2M#r zST?%cC56r6T8wpi?pk`BAK9ns3XH`xmp8PtDcTf)sUcg&1%cuQ%7tviBm3iAISNzQ z58O~tlk2+{?2JC&4i=;aZFI{;?xHr%R#Z0E^>78Cd+%XQ#xRVbgBfjv0kq)4w|2-C zY{pV-`;)y^x#jj=t|48I!!d}7wK=6z&MlA=sVfu$?a zmPQS?$>mW&vgL=F zb}>^Ap0MG!#^r13YT?=y)>i%EcH$vNsNg|dPvJ0(X^_w%+oc>NtD1lsc52-)^^&$< zH*yA|tO-FEnP?y2ewCOs4XYK>F-ib4-ua3BT>WNPa6;h2lr8VmA8gLg@^9<``wuZ+=`QF+ zSqaO)-deIl>SsN*zUC8L)sCvrOUaI?8W>Iz`+MaJM<$-DIQ?APba!z zgpDkDdV3$INp;NQ6h$i1e4*7!eT^xQ4MT57oJSBta9VWM`;JE3m`IXM8@so;%hChh4CWfByNEIK3<7=>{dO>h`hWWWw5hYD#en!6(A zp@R;jThmsCah1d)Bha4%b(%;@1X10vscwj!Fth=T%C@d?6 z!T6hD$(opprs2j>Nbe$6Ce#+G0IywmuXcOscRY+rRJ5BVuCUybVAZu1cM5MR2miyF zVoeg!g4}EeDWRd`KZEupuxDzwY_+%x65)stNG=v#{@eH=$03l6|I3UyBf@y3y+e(X zWAc*}yIP3sQ0=BU-}A8Bn;<7w1%>_uFxz1=wdgnu)4;`XAoO`-ZqsfP3BB^6kM@W3mtz#n{9C``9yn-|B?eo;0?z9FgCi!VYnR{@X9F zswu*fK#}AtE4b$b`@p}BedhPwmY5M9tr_X_`|%`q)i)H)owvHMpmI*xoCF*6-^0fJ zJ}FKT*A$)N#j9!-&nugEW7#R@3#??T0=Gy)uWIQM+ZLW~Ik zN_Q`ovclmNgM5(UNBCK1s)qPw`ZmNz?6fc~^DQecP5-mpOYEV$7q3juE_9Gzp|}D5 ZHLW$y&+&2ROxYCc_Fnq(rDgNIe*rDQH2MGl delta 393 zcmWm8O(?^07zgmy{0`=&)X@GL^ESpdiz0EFm#Cd2Zj2mUCjL#vqaw|U!Cf^k zGOP$3)eA*AR#?u_gPep-Ux{|TU3~B?gaiGw_+bU$sF@Km>j;|K2|?teU-)V#SwZ^7 za@{QJgx)b!zsNeHR~Q>QtPnff{N=r;GlR;WhYY~fBAwwSQX-ES$!W`!D3 pYnjAnbjqK0^-e_Bi^asrU>AC;_ diff --git a/default.districts_config.txt b/default.districts_config.txt index 97938c38..2b0ebec3 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -8,6 +8,7 @@ vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereq = Warrior Code dependent_improvs = Barracks,"SAM Missile Battery" +buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 50 allow_multiple = 1 culture_bonus = 0 @@ -26,6 +27,7 @@ vary_img_by_era = 0 vary_img_by_culture = 1 advance_prereq = "Ceremonial Burial" dependent_improvs = Temple,Cathedral +buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 2 @@ -44,6 +46,7 @@ vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereq = Literature dependent_improvs = Library, University, "Research Lab" +buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 1 @@ -62,6 +65,7 @@ vary_img_by_era = 1 vary_img_by_culture = 1 advance_prereq = Construction dependent_improvs = Colosseum +buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 1 @@ -80,6 +84,7 @@ vary_img_by_era = 1 vary_img_by_culture = 1 advance_prereq = Currency dependent_improvs = Marketplace, Bank, "Stock Exchange" +buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 0 @@ -98,6 +103,7 @@ vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereq = Industrialization dependent_improvs = Factory, "Manufacturing Plant" +buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 0 @@ -108,6 +114,8 @@ shield_bonus = 4 #District name = Neighborhood +tooltip = Build Neighborhood +buildable_on = desert,plains,grassland,tundra,floodplain,hills advance_prereq = defense_bonus_percent = 25 allow_multiple = 1 @@ -119,6 +127,8 @@ shield_bonus = 0 #District name = Wonder District +tooltip = Build Wonder District +buildable_on = desert,plains,grassland,tundra,floodplain,hills advance_prereq = defense_bonus_percent = 0 allow_multiple = 1 @@ -130,6 +140,8 @@ shield_bonus = 0 #District name = Distribution Hub +tooltip = Build Distribution Hub +buildable_on = desert,plains,grassland,tundra,floodplain,hills advance_prereq = Construction defense_bonus_percent = 0 allow_multiple = 1 @@ -141,6 +153,8 @@ shield_bonus = 0 #District name = Aerodrome +tooltip = Build Aerodrome +buildable_on = desert,plains,grassland,tundra,floodplain,hills advance_prereq = Flight dependent_improvs = Airport defense_bonus_percent = 0 @@ -153,6 +167,8 @@ shield_bonus = 0 #District name = Port +tooltip = Build Port +buildable_on = coast advance_prereq = Map Making dependent_improvs = Harbor, "Commercial Dock" defense_bonus_percent = 0 From e386d0aa1d7d0f72523b63b7d7147b96a71c6121 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 18:57:23 -0800 Subject: [PATCH 031/356] Preparing commercial docks --- Art/Districts/1200/Port_NE.PCX | Bin 15118 -> 18298 bytes Art/Districts/1200/Port_NW.PCX | Bin 14750 -> 18174 bytes Art/Districts/1200/Port_SE.PCX | Bin 15433 -> 18809 bytes Art/Districts/1200/Port_SW.PCX | Bin 19424 -> 21433 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index a8acb10b87210d637d3c2669a28ea4bb4937c35a..a6f926a418e378c0f5c06b799ca55fcb2b3b3355 100644 GIT binary patch delta 2385 zcmb7`X-rgC6vz8Cwgv$Q5EN!(n2lK(W)tcHBe+yc>ym0+ic%Z3iyv%K>5^gEgxDq3 zHk`(oH1%7X*4Wh#P1`ihePf};DwIK}AP9pnFcgLv1Z2>Adma-NMoHTboOjOuoOkb; z``-ESP8^M%;J!JT>ERipj0*NPtwlQ(XK$lYkNQNMouZTU66&LIb`mR_P!Gk~2|7kw zP#>IaC2S0rXv4&>)ZIPX34kRlqm-xa6k8|l#F8GgVO-hID0R2CBz5tN)k#iUOMJj+D@ucOfDW?>$|t)LlM^eO z;{!&0C=Jj6jj$WEV4iI)pUPJ@-4`+1fLeg|(s34X(1K)JuC|ibiW-D?cF5=j6npU} zM%f5WE?cxNRj;d@e-T9DC0rdC#F=0hr*d8>6HC*EC_hHV$pn9sOx9Dzyo|yTvAk$- zhW;Dsbu2nM z{I1-_4a1+xbVyW*&{L}Rz!sI6i@-6}E^Zu7s4}4uGvg4zI0_-v(v)fvt|HNDa`9ur zyWBw*g$?RwI0~E9VmPHvLhr147j&p~_+XPXTj5cS8Xxun%|o$tvIh&6Ln{{QJ&&7o)PaHyGP??$&JTWZ&cl&Pu zTQq}X(3!h5sg9B`X%5B+2DfsvMCU0PMXjeQ5W^QpbkYNN*=1DlSBB!52Cie5YH= zwL_yW%QHr+_z>ykgJgsRt{{|VqG?oUJVg2ykN_WC(26j+UzrK12tAGM`@9(~$fH@o zOhk=P5~;$vZqk!rZblr<6ohytq=vDWGz(26kijM>*Jt3H`-VOfs`UbFSU<%Tgo0iy_+u_93IqCV7}uxaXo(E+wCl7AZ}XD@yh)&( z04b+I0VRedIF{uGMS53kV;|`uLDDaPtQF~W3^ScVCM4|0C-5}HNOzF+!B+;=va5HR z?IXbiO!!I2TNGq|xMDCupFxg%NHpd{rcs6DC^hE67Gn-F<#nSA-ZlyV&dRFQuej_13OSeNq;L4wtW z#45Eu2S=?|B-Yo~gV1et;Gsxu2jB^t16lQ+Z9n{Av*S_TupI=Q-HN1o)?N-L?Run9 zoqaouqUQybV>6UFw8ArWJPltuG)SX1$2LfGD!2x)J2ydvQ-(M?<}8M9o#|XFTy*A( z1pc@^?m{x`>BP6C9TqIlMd~1w_+^4{2S9{*-DH@Ad9;^N@1d@wtFx}cKYDzp*?G5+ izlWok|9_xVA$*BU%=xu4*VoNlFnIuu&Mho>c>X_$7FEaq delta 1467 zcmZ9MTTIhe9LHD8a;k*svMdv_CGaql zE%Skd{ETld+ruJzSYond4`vTLE#eeK1_H`OfeN*@%GwGYkp0en{O!&*U%uyi&iS3o z@Av=bDTgZN0q(W|LX)+G1bq>jKRI!-=_0gQ^r z=uUDqdiCC(aq0;iQ*@LxfXlcpqUhGRimg3fs}9Cg26)A&g{VreUPZp9$;@Uv&Nr?> zSe^BPca5*}xc5)KaTRnLBiu9UO1Dsm%VI;(d7PzogwAzqQj%5AScjEvT+ubKm`rd= zlLpNuEqqVym!=dJqPb}igda>rEI@u%EJM&_XMXZ`#Uj+^RI$6{{bC-@E78U*BbMW$vuD0|3IGAS@4NAaR=HhBu4N$94Iuy{z4DiED1TKi4Z<1*CS(xK8; z!LAVk0I`D zkjHq0`}+boP9c9N`UqaD$k`FXDd?^+YXuRx{R~z}V;F&7D>Am9fq4^o*c&;*C;0L< z{ZG9F$gyWbhe`(r?WyYj(GPw0jH0K3kK#9cflv4hQ~%{1ez9l4O_d&!9I5bFrE`Xn zM=8$#?DIS{rj#jBEb@!E4V_OscoMV3?^DnyL7xR@9I3L}gN{7*58ZQ)T!n6e&ZlcTS%PS=?(L8i`}V#qYj$j3r>y1LzE(+YU)^CzuBdyRCCG)I`lFK9 z_4-4S*AG2SlGjO3o#eHw!6A9=YA9xj|0{gkkehlB?+|B8q!ElE7el?1u$X!Nr_Wwi G?f4s_``z;Z diff --git a/Art/Districts/1200/Port_NW.PCX b/Art/Districts/1200/Port_NW.PCX index 690f7ce0c8c1cc77d0e36edee74318ea0aadbe67..da5a3156a4c6e77cb3531e2adc509dc971d5029b 100644 GIT binary patch delta 2458 zcmb`JTTGNk6vsOnw57O$7%y~hunWuXBDcK=UW%Zgv_ed5#I|aiR%~LEwuwopv9kJL z`(S)%{%K><#26dXrfqCveP~~5=0g#og4`7pm)&w(uFF+KXL>$}1QvYj%YWzm&zUnb z-?#J2cj1^g$9`{I?@^=_QmFb&l#@<{=reLE$)5<(DKROk$sY;PNvfl!MA|G)MmeOoIe$1P`b8&Iw30T6 zlS@`c@gN;EB=U61e%e!j3Y$r5g|b8wwLF&x#e%3;^oRkft0%1y*~-fJ6>i7V(4dD7 zGE7x9q<4g($&S!OlspLyxZIs{ZxvKC5~KWl~VF}LDWI#e?o<~NGn8!#*t_# zQ7n;T3q!wX7lJCwNXvz$;MKfUHfzb!k%eKqctnL|QZ0`ylQ#t))2xeG&L{Zf@N{CH zgP&eAL=TaFOO^>`jF0mPb}#Mi?@s!v?7f8xMl zR-4?|W7>p6rX(itjmd=zrbJeTJEk<;GbONF2s5W4(wxAmu+^M`U1l?@!7;NFrDiLu z#}#uj{xX|bGyLXM44C6tD`Mi4XnzUuM(mDH#Qu0K^W#Lk(bFYrq(?b#6(um-@ZlA6Af~IDWMQk z3A*T;1jKtnQO$Pl<->$P7%ci73W2au@BzvUbDz?1LB*a=&IMw<;7!vLy-EYGlLW;O z2S+V2ktzf01n*OVMZBJO2%NPTJyX-f|=}bm023HRGYdTnbhQK3qz5?c5(eO0b(3 zV^gAj^$hOHsgBM<8wlsU!+0<8Iqz<$2`~4Dkx$nv(YShscgl?KNcW9<-J@@kf?^dJ_$F1r3${J7C@~Slh*R6I!Nzl3# z!kS8WaoM&Y$Cg6K`NFmhXKapXpylHq+ZIC8O8Xud?OBAYBldkbZr_F5_Chq<)$; z2N379v0ChP?!lK%3v0yB<9EcONgi@{#5;vWM93%Ek$oo_TT6wGn^2*0T<@xhxoU yMX?@4xD5yT#9cW{tUmg*nL&_BrXYO)h5k;y@S|xFny4HKoBj;@(*FR%wR%hd delta 1509 zcmZvcO-xi*6vz9(axE|eL(9h~FjGDp7`_Gu2at*aVmcL+Mk+B;($VVlV*um?^^iFsaZi-11d2xpy~Ah*Jxlmu;iW26lTNQtpMXl#f%nqbdujeG`%h-5=I@{loQ5949JxelKh z&4w!1e2#pbD1AiqFfLliM?sB6kR(j303=`BTbu}Dnm2G|y#>C2T!KXta z)!C41lUo;22%p%|5gex8xR}RBf_vp@?p+t9uI94n*3%2>z#K%SWq~y^2|iD=uqleo ziV3)q=7P?+B#_c=FiGu4>DkapIlrgpumQ?h7Y`ub;sT^thoy|&rPvG64Xc(~IKC?h zzOrlVX9koJ!Sis;uHw%&-5J5O)t6(AW zeIf+6ZAYTE$ln!EmUDm!vRz^uuIB7#caSA>>3i^VZV~%) zhkz^gQs&jbXt5Wvzf_J!;fCG89_{c2QF-~h6b#}0JUjcF*d#*X$2=>0f@9?8p+7Ix z?8i~^OQazT;}H1(4u}q8DmY@FmiW{$pO`=%nK}(e z9r-HN*C;kc-z!uvIm%V4&lVS|RQDAZXjC((DzWQC##_wrYl%arHN)AcQ{3r1#RUDh z;P75)6V#Xz;7O@lAOB*%=fCe{Fm}``Z}8`PU?hD zRGioMbm@RcCp@F_W428D8WziNyYf6+C9fcU=A8@dnRZ^g4}L;dpdh{p*A9BvHhG2d z%BoKp*BF0T~ITH-Zy&X;PT-!Y)FGVxw?^!YLI_l-OQBdlO9np z)wtQD`r=N)*EQ8_M#FBrri{&Noafitm_j&l4xU;|+Oo>|3TYTOv0oTd1qQWt4UWZr KaC5s>iu?yw2+^>HcKF~JcJFQ0qF{wR)r?Se$ z1m4Q}7++*niZP62H6ck?A%?L}_dZ&5Wg>v9JVkWRivhUw2T`Xl=6`>qufr9dy0NLR zh0aha2=5r)!&!q@#L;K?2up@NVin%(PtlZpKrG|;>^7|Mw1}FVQ)tQAC*}~~DW3C+ zn8DuMV`$2)6d_#Bt;bkyg_wfgSO<@>LX4xs_%dKD5u+G1dNE~mi6LZ|yfB$u!jF1W zIT}q)(T^WYyYZ9BCVKIgsSqnBgXn@b&jNFvj;E@;%*tMpvPV{=DmRtBsNA}3$ZopE zXH51d@EZBpMWz3K!trQY@(`Pd9K!kHw6;;6=jApB=my_r<#+;bP=H-nP9-ovgY1Gb zl)yn6W;Y>c5;#mF?8f9AN#pi$k0Uh3t6{l#KOr7VOI~IZkjrQ;No!l>c|dM+f+qRq zlko&jQjlG@qyz?OdP`EbiqjOPID4OpVY)@D><6~lcJFeFW@(uh0TpNIHZ8IrR`E8? z(;WLT73XPzX4p@txIhsKu@CNGTS!J|iKciFR&j}?111 zXr27*qbjb`2K9S+5u^Sc+@QPE%VtBxyCk^;awl=vtbgV@DND*9cmC;w5>wWdwXITg znzPe=q(pfaKC?hQ@``dt8R?2iTEmZKGcKETB8Dlm8;jjmA?xY z^4~;HekmTZyn#H+Ry7V=j^H!PZm!0Ft(6>&KdrS$vUyUr-&K&t&~DS^ zezUb=!B&;>-}@ZK8M_`0_Bu4%U*nhz*w13q{wl}hbw?*YaU9^7OglR8q_c)&(%?LS zubfpJlaTWyl3n{bCWl<_qSf^x$E43yjiAfJG0_**z*$tzF*#jSjn9k9I41t0y_jIv zk8Jk~u)0e)CXMcLw7A{4DY`MVjBesmN+n>s`r|CZ@NS@^j>D9is-mY*v#hw#pKn#4&0UkQsQH+IO6wHU`eXQfi|(O>G!ns>*yh4&fZN z0h(4Eg{MI>s0qrTeLpVHzL(}K?a!zc5Tj0`+ugWA`%d_vzC!IYu2O4~oI z436MjYE6jL8sPrk97dna8~Hy_gc+UznL_-Y=q(r{|b*aX>5?P zm45+MSdw2K4Ez6evC6}nhZgE)FwoF?if(28PPQ%uSl;bL- z>hz-Ob6tX{>K&QjVZ{Ns7MZh+;|v*XL}t;S6g2CO6j2j2L!%+;tS{LtV`L&x;E)~2fRuNFh7zV7APwu=E{>2 FkN=IaY#0Cl diff --git a/Art/Districts/1200/Port_SW.PCX b/Art/Districts/1200/Port_SW.PCX index fd680ece52138bf8a481a9bd2f4def3f23398572..1623760f4438975685ac68b4c585e314b2188c89 100644 GIT binary patch delta 5428 zcmcgwdu&@*8Q--jV`b|$bxqqmo3v@yrfIkIm6S2ZYofMVS@#k~V

Y|A0YxZHxie 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 M{@_oSPkhq(Prw49qyPW_ delta 1436 zcmZ9LT~L%&7{}+~?bQjDGBjBBgU@w^-F0OF* zKcAqR$LNQzbaO{UKx!iJnL&zaoY2`{>Jlw7Ce9TXb6+$_(TM@wvIXZwtBmo)83L}u zo#3c^{EsSaSc7e0b@Q*;z#37W{Rnp9;4p0n|aApO5;dK-pV|b z?4mGEC)YD|DP9^yeaidHwv;vW5c<@2nEO*}Xc(=j8<`nt>*+rHY1^3((l*f$s?zr| ze^38_?!uCBfZ3R_g$6Loyh#}l-Ke$daKv0rw{X_HSGgrFBE-ttMXM-?Zx(&11jQxz zZAN5UD(D72vD7O!#1*7EjF_?%Q6F53Hz?P{Rru`-adL4jUB&d`cc~ZaGB?v@{F=Fn zdN3zzJ#}MiRylRyWY#+B#0yq0b)d$&kvn_RTFjjtv96|86xs@@84b1?(s0h^QJTf0 z=w2E2rL4SZxA4pcyPId8wx{t5*X&MSVcrrGuWVc5NbVF;j>|UD&T&*;6l2mKXLu@m zW(I9sZJMrMc1ORqiw=&%@=BDwVv;L2a@8rZ349A&b3#!5&EJM(r|9B1Ap4^XBH5A3 zy(n?Sa|grG%6G&j)`g`%(9oo{H)-QqNDCw;^~<~bLm%&cnPabf5aqB4%NVWQe^?vQ zLM8KV$ooR zx1ZO#Fr96s3y5_ZQQ&k&y6sHoUVZ7zqd(E;%wQkiWBn{9c-(^GoO15!x*Qw#|NERu zc5`b^Hf*_-?B=rEY<6=~?gnOaZUH@oJ8u;`du^VRJ^f4GdgdQ_2?*z$1F zEPwWd2yt~on453Hto(AC;87v{gQNMom}l}U=n>Yq4l>_%>G8G8s*LgDw7Cz`7?!$| z@u@pY2?+t!vyVb}(PPAFPo_G;ooL2g)K)E|@K)hYx`RW7UiCKTy{IhOLbtK4s7$@dc^@3b`{^b& z7W>r5tp|})vPbC`V+i_^u)k!9dY#n)I7&ZK`dB?sWyJ2%Of~8kn9H^+y{uBT0h`P0 zYUKKdp(}q+VIyLGfeCMvThyovF~6c#>05vKYQ)Kt2(I)g zZ9L22HREM(qT0&pQ~c&#!7n6?>>48mz45BXs>I>SlBjyTCKcaRE_kNa`j+zB@%s|7 z%NO_T|7o9@cS)(z&7P7o`y8uE=9ko1weZ Date: Mon, 1 Dec 2025 21:23:52 -0800 Subject: [PATCH 032/356] Update ports with commercial dock art --- Art/Districts/1200/Port_NE.PCX | Bin 18298 -> 19386 bytes Art/Districts/1200/Port_NW.PCX | Bin 18174 -> 20767 bytes Art/Districts/1200/Port_SE.PCX | Bin 18809 -> 20975 bytes Art/Districts/1200/Port_SW.PCX | Bin 21433 -> 21722 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index a6f926a418e378c0f5c06b799ca55fcb2b3b3355..4b74bc79aaad8c46257e27bf457b1d533d75605f 100644 GIT binary patch delta 2159 zcmZvdU1%It6vvwlAz;#?FH2Mq1#2)0VK;3)+ssa4X|451WT^E)B%;y>1riEmH?$Z5MoUSLA3cb}z0x%3Ip6K2>OwHeA12^?l2~R(0g3v99jF zt29q(tXaB)R?by9kUYy)hSJDY{?BxiE~-jKo{7coyWks^MmA;0)(qS>NYOfNAniI` z`%C^A>*ej{c{|Erq0)4re=~&@l|7N`vNPWI5U22zjs0wLbrDffvcKQeo*W{z;^$DA z?O&>1l+VTQmM_Hb?6!Sm@hB5TfJ<|V@QK~|J%w#6!=$>1R^^-V*n_sODVOrvm$cDO z>t6>E2(;1NAG@@Eq3_nG<+t&HK~5SL@OTexSevR$zn~)&UZCHQwS1ZimM>IIxwCcU zn-bjWF3QgMqFF|2$k^Yfjs5PUYlw%I6mp@B z2(nbrlt+~mW!qXiBQw6C!-0m@+ok@fPfMKh>2il=Ad`Y&oNm)ZEfp%Fj|!Dk1zG2& z14A?sI?`;2bo!1bEPtSx|x%8#RKaY+BO=T@TzXS?n9xf|tTJE6Sz`Qu3*N z&p(0a4&|}bUo{Ia0NNk#lcHdnl!@@H`>I5z`ZnpsPRC# zX%JE4X;q%;e`_4A=R2t|7@<}8k7Ub9V1yVJA6zuB$P2ViDW>AA4?XLL^M-t2;EiKZ zm^};S%1BYBWo&qvmQxU{fOPB3MV$(y(L*Kx7X#6N3y0aJr6u{}fO$keO*s>GMSLz6Q;qLK7)s0k20uKCI6;LJWGh-FGi3sc+n|61jcE~KTehZ!fnG{bM0H)2-wl0y zBmxWhoMe(#fpP-BfQOAG+u#TX3YUcLn4;P|$ztKGiHWixPYh2U&cnAN?iP`&7y@GB zj|L#93k~z}$m4hgr*6K^Xzaf=Wt`eEPttVe-Obg`4dG z6ly0Zg7XzccPLv4DnUv|77o1EYjL|7oR8_95P*HQkwWc^DY|v!2M6DOqyy7TF&%b` z<=^T!_xmtqQ)0+eVnOa5eK!WW^W-6d2NycT=(*ikh#p}{;k_eiRP;g|o=^(2GmmTD zZ<~W`mcXn)jH&OwB9CjLho!JB9<`y3w1_>MoZ|DQm?=k=v{&~gF%H=(tgc^a&^fS# zihMSiowpMckA{OHP?7flU_9@57x-%6ut1+;=o3$m!6>{Q8m30({F#j$7isODW9d4B9z2du1RbY%DV WNV^K+9r@CDycIhB@YVR$*8c$?Owzdk delta 356 zcmW;7PiRtc0KjqHl)%e`3^8$Glo}Pt&*%UDf8VQv&_y~`MrENHuzPD`Robdeo8M1Q~_f^y(Z551c~#CtvZTt07~N`Ka+R~aGc6osHJtTtUYjD6!k z|H-yELSHy6hVh<{LYRNR6AGGtrV*0ON4)v}H|(;IG_K1rn`R6rLv24dVgP#xg<&+n`BkW}6JOlNAl_mcKf&$9UHS5kUBao8yh}aeZ|W2q={3Hl{oKfO*vWV~o86?F^&sUQQ_H#eo_omYyo+b~ z`%Lprs)YwUFSyYQJ^hpJLh?JTR&L!HF%S8vQqHMHOwzy6_)E&l-IC!Jvc diff --git a/Art/Districts/1200/Port_NW.PCX b/Art/Districts/1200/Port_NW.PCX index da5a3156a4c6e77cb3531e2adc509dc971d5029b..8d223778ada12f67887920c516122eb49c1c56b1 100644 GIT binary patch delta 5883 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 a%>0%G1hD4!Ybr{OGUZ--Z||BXM*Saeue=rj delta 1093 zcmZvaTTI(!7>Dy85+d6Ig^HW>45dffQhGkO&;q4@#s&l;JA^R0?7|DZF(wP@m5XMX z$xmZq=7`fJ%qtSSSZ3V#Iu$m8I}{BrnE|>=fo0<`FwW%r{1`W0?B@ODeZJ?-`(At_ zmjrrF7#pY-wvn#Z3(xUIwLze`Rc$U}>?Vq3R%m1u=$EN@KaJ02MxMXoflSNuF645P zu!WQIMqv{_%H6^`B#IOQiVA$9=)f4y5@jbW%5t1l9>SP%KT1_S!V*4IH3@lG>t4sW zN&>061sb&%SJlnJDn8X!;h|cIUQG*zG+z9x=@y<~O)rI_rU5rGfkA@@TZSZxi~)RU zY{RJ04~NNzn8}Q5rZ}cdZir?d`pss{nq62l>(NnI-obM(v7y_bGXt zS??uTlBf91UW-Te*WO>IOLT)VdWY8;I-Ge*@|hK~Mwb408Caruy3OrrUZ;1idrJN) zCQF%>;=f-?@-#=EbAOW8+w>y8G<&mZ`+v=zs%?xk%F8I5qGV&33kxJy`V0$=stW$I z*e{%t*_}0|WOV~3hg!neB>QOx&y!p+r)xOl&~SO0Wp?QzDRBG*w;ZZ+#_ljaKTj4( zp2NpbId|Ya_F{e)uHap#cK;?D=Hen*=2`(+rwIjTIb1HEWNlZ@k{nqfg$!H76;~T( zT;(viyU^v9;HtYHId>&GJ$-o3BgL{OiP4A@Qg0Q`sSe;p7-e4MrU@PW=C$JTa;Zo>0u7_fHE1bspa5F-Y0UVD+Q5ii6 zS2T)3v>PQcC$7a#;ZZDz&zjP>(d5U`=0TiocB3|a3P<8z{1JZ}WeGn<5<|F~aGV#@B5YlqOFgoHXKc@)+iLy4w$9pk0Tt_CEaCZbV}$ uiDM}hE~H|ZNGZTl9w@|v=n(Z75+(RZ)Zl`+7vti=S7eN?kKYqJiv9yz*)JUc diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index 7c12613037564f0448fbbeb9331a8dfe07f4c62e..d01207c23483a9b971799de705f092700c20c856 100644 GIT binary patch delta 4176 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 IE3WDP1(%)0LjV8( delta 918 zcmchUX-t!M9LH1oMnoKn5!36Tr3aR$Ezp*uwFQbosdy0Y7-L90s&AZeju_F+BRUsj zoS(6HATK;$dPR*f9uMNJcx1!q7Vj%#PNQ)}FwcJv+Y3$hes4aX{JyV#$>;TM-G?o@ zgOd!Iy=uE?S4Svk%Y@p?0)ssKrK&tq9x9JJ)N?|4wW4k1q0>U`;iw@*r@^HAi!5U; z#YQvD#u@B4+UYY+#c8thm1#0ee9JsbG50KMFiAmnN-OzI z@{`P6#@^gQJl3yCT8rtnCXs9fY`4wly5{=4B+Ypq((Ox$+KcG2ujjdaC@x1XhaLO4 z<`~9sr;P*7?VNXJp?Asba=|HA2Jc)>R^_+QmY+dazKzC_E7&p8gVnv526rJ3-HRAp z;H9--CT9zrgyaUM%X!?Dr_d)$EcHxbt4AW!J096}^g?uS z!Y4^Wq{J1qpXb$!n%{}e&q>k1?DE^%@>=}M%j#9_>K0x9;Z^mzw!IMdK9SDC9J3Hl z#C>ghB5tU+G(QlJKatt>IodD3@J^gjJ`rU(H zsU6~s@<#ckJ=I_Jkv~X}f2b}+AP}P=FoKrAI8FqNh(Mg&poN9OB&|UQso)&kC3e0m zS%Oj`<1SrDW2u`9rE5tq3$mfCnM-Ab#LE}6s$51NYN8_KwU)>sX9VynrhuHbm} zTDq%a>>0g?&e37M95b7r#<-Dc8mX%Bb4PP;jfV}jb2(7!A`+j*thj@x@d@bbB$n4r uWJjHa^h69>!ouc+r2ELvi6Y)6vhj~KMEXQkrgBrc{6F>@bmY(RI{n|^De)oz diff --git a/Art/Districts/1200/Port_SW.PCX b/Art/Districts/1200/Port_SW.PCX index 1623760f4438975685ac68b4c585e314b2188c89..73dd62c5b1c78ca550ab316d009b6b692d0247f0 100644 GIT binary patch delta 479 zcmXYrJ!n&57=>vTC4*@+r3EJ;)o9Xm$kM8Vcj%@r4%Nxdikn-xDdIOcR^Krolr;WJ zO$`N8Zj$rf*4s!^LN`TG5hVZh_S#w^Y!O+GZyZjyi0I`U267SZKx2%AlMH~J~?T};4Q%Cqns+6{Vkaw}Am1=iR>363pj z>!41vrwW0F7`7~=9>HGMf;yd#6ay_W*JLhpY4WY0s#z~m2zA64 zPQ}wthJJ{0w7D`3Z$zQ`{6Q|CYtW4|snD(%8)D}5WYtjMsocut79EZ*9{nk*EWZ35 z12E3P#`X>^Mdy$2i`oz{^eCKF;OwF&b`L!fq1o68x*3}}cJRLgEnIndpVKcq+KSx^ z9`v`;tT7BLYb|?pZS4B+o}i;pcC=M-T3P7c(!X6=9lJ8*33vJtv#rnt`&9o&Fn)3H zx2R$go0=Aq3U-@j;urKVc9|zo>6bU}8gQGGjwgb@#0O6O#Ywm-Q>XXw#NfVeXa05F XB)-%rFrM&h*fLPYPVM#7`0el?MC3O+ delta 178 zcmcb$l5yv9#tj-O%>VzNpRBKP2S{I@EULN}NME1)K=m$=zCHPj+BqP7fATu@eL(v0 zGP)PfZa2^<-(S6+rs^Vktr~m*( CZ)Vg0 From f111e8b1e1e1f53b51232da7d5053138662c240d Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 8 Dec 2025 12:12:54 -0800 Subject: [PATCH 033/356] move abandoned district to separate property; Add maritime abandoned district and art --- .gitignore | 1 + Art/Districts/1200/Abandoned.PCX | Bin 0 -> 29061 bytes Art/Districts/1200/Neighborhood_Abandoned.PCX | Bin 20473 -> 0 bytes Art/Districts/1200/Port_NE.PCX | Bin 19386 -> 19372 bytes C3X.h | 52 +++--- injected_code.c | 149 +++++++++++------- 6 files changed, 125 insertions(+), 77 deletions(-) create mode 100644 Art/Districts/1200/Abandoned.PCX delete mode 100644 Art/Districts/1200/Neighborhood_Abandoned.PCX diff --git a/.gitignore b/.gitignore index 41cc1372..9a8d0561 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.exp *.lib ref/ +agents.md __pycache__ temp.exe Thumbs.db diff --git a/Art/Districts/1200/Abandoned.PCX b/Art/Districts/1200/Abandoned.PCX new file mode 100644 index 0000000000000000000000000000000000000000..8795f1bdf1e90bd6ac1b2ecf01bfa68374e9e497 GIT binary patch literal 29061 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 Y*s^YYSI+}?-4!6G>U{Rs4I26WznpXp?*IS* literal 0 HcmV?d00001 diff --git a/Art/Districts/1200/Neighborhood_Abandoned.PCX b/Art/Districts/1200/Neighborhood_Abandoned.PCX deleted file mode 100644 index 11ea0cb2c8f9e5c83f8ef3723230d4246c61c259..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20473 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 KKi8?b?|%RflbLz| diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index 4b74bc79aaad8c46257e27bf457b1d533d75605f..3be84f99d952c836af819b9e0d1446637f3cfa86 100644 GIT binary patch delta 1705 zcmZ{kO=w(I6vrnMQV~r{()iL3gI$OVOV!DwNoUgJ^-Zl5tl&bJg-fNl(`7byUKS~p zL8N%WRttl=C{_dm;>L}h`=(<8RukR`x{%JBAxxriUgkTqFxUTinIuhAhMDBOdp`fa zbMCh{d%nEcGd~dNThipM$-&6!NAX{ge8*gqiAdzcl1`FKCFQE5GRa%jTU%N?`oY%k z^3%wW{48>Gm`#c>fR~ws4Zbd|l;!x3T9r?ZK0ks0 zs%YTZGn3?zYkWr=^owd!O~n3?S4X2yFKe6@xeW3QEYA(vl*$mRJyS=aIf0w*}F{oi0-91o*vZRHLsSC&`C%&DKWgKW>zrqfX^`ksU2sY(k-*t$&>M~mzMS-X-#{#AK@ zEYa&{U3q+Lv*JhM1_PXq9(7Lef=`JD{^@JNgCWwm|CDy!rOL+*86%N59hQ6 zS-R>&+z!!xD0J(*L;5I*=9U@Sy#PR+*l8AfVS&?tVY|Di!j#sEYYj0D=w5bkl z^acW~3mg{6%kT_`P-$h(R$4fFQzK8s-%Eg+(o{~t0LH9cKy;U;FVGBS7^a%b1~~Wc zWLEwePY=Vdj6}auIRgQzh}Pttl{)ss;bS>1_EuEho%~R~b7AN-bo?JyfAh=;2(`1a za_0Q42`{C<27>-YiwapXtl>dgIbLD%1(qimDDvg2Q)$zryr60Z>}pQk&Ok3%{RwFk=cqB_HrwSH$f?O^T??VPLA6k0B0UTG!jpV7MyosWwE1teU`9gW~={o?E))C7UP9=P=Jp(EuP7%Q6vq>nBvejYMF~rR0#bw!4F|M#zMQ0)_4 z;4l&`M6$RdArS;8F7RgaK~IEkNASpFcdsc7i_x=-wIUp#EPLB)oCYCAKP{>|4#+a*RB>6X!8?2 zq?@`Hnn#a~SuwXC|D_(3qF_h}y~6JvE(3p5YV)?V}K%0!PNQMgF%f9hnsA2WlmeJmDMcD^O@5Bv9Wa zd)i5#CfZalB<(kRBXmGT@dfP+QspZS;YgqI^k<70l+Fof5pwRU>0~m_nVRQJsn7aP zKNopM>Rk(#_bvE6IU4^Bk%Ed8SVF}IUbYS!Li>iO3;kl^SA&Wn94Bk+sZ%XRRoEF~ zrIw?Owqu>KCF45q^1!E~HZAv0ockj#T%^(PopaT*R7R3*itt&|HKbgH5I8Ah)5^n6 zs>y+O(?fl67N-L?3+3y;aRH%HAO!tC~QakE3vbwU5vSw2`_pckwYmM!h%M-OCX75F*@Z0z?}yy+#|T z<0GH6&(l`cGs+(8ejPSG4Of%77P2uuOX}9hT>p|ma7Kd*;BL}&oujoJvwD(LW3(is ztNGE-Q%L3`R#KD$vTd)F&1Eq?YwRrRv)HIIi@=V(iHpH3H9dB1+^1V8otehqCU<6- z@gmxi_ytNn_8wu>984yU>H$-Ub8`;v0d%;9rpO~jfFdH=ZW8jba6kwpKo7TK5sloK zMai7iv*~NR!)48<9nDJTw9hA?w6j@Nx zV29JIbQgCM;KL>zp#ds9(~KYYB!yD_AU?HtMheZ=#cGGc6v5pd>*?gSwNN)o)9Rz~ zSsOb;k5z=|yTzx00|4L6!45udOR9e7%Go%s0?kDIR?qyO7?y*xw2_mktX3P&S~PXdCCF)FFt#9S{sP=tEUe*^;zL2s-g?EK{1^E`kJ`)M7S_q|r@ld~rw glKm|R$jfJxe0!#YuSl$Xf9(91?WfG%tHtp9KLN>Al>h($ diff --git a/C3X.h b/C3X.h index e93dbf0d..678c6923 100644 --- a/C3X.h +++ b/C3X.h @@ -622,15 +622,15 @@ struct wonder_location { }; const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYPES] = { - { - .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", - .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, - .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx", "Neighborhood_Abandoned.pcx"}, - .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, - .img_path_count = 6, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, - .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .defense_bonus_percent = 25 - - }, + { + .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", + .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, + .img_path_count = 5, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, + .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .defense_bonus_percent = 25 + + }, { .command = UCV_Build_WonderDistrict, .name = "Wonder District", .tooltip = "Build Wonder District", .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, @@ -1490,14 +1490,16 @@ struct injected_state { Sprite Mountain_Rivers_Images[16]; Sprite Territory_Images[8]; Sprite LM_Mountains_Images[16]; - Sprite LM_Forests_Large_Images[8]; - Sprite LM_Forests_Small_Images[10]; - Sprite LM_Forests_Pines_Images[12]; - Sprite LM_Hills_Images[16]; - Sprite District_Images[COUNT_DISTRICT_TYPES][10][4][6]; // [district][variant][era][building_stage] - struct wonder_district_image_set Wonder_District_Images[MAX_WONDER_DISTRICT_TYPES]; - struct natural_wonder_district_image_set Natural_Wonder_Images[MAX_NATURAL_WONDER_DISTRICT_TYPES]; - } day_night_cycle_imgs[24]; + Sprite LM_Forests_Large_Images[8]; + Sprite LM_Forests_Small_Images[10]; + Sprite LM_Forests_Pines_Images[12]; + Sprite LM_Hills_Images[16]; + Sprite District_Images[COUNT_DISTRICT_TYPES][10][4][6]; // [district][variant][era][building_stage] + Sprite Abandoned_District_Image; + Sprite Abandoned_Maritime_District_Image; + struct wonder_district_image_set Wonder_District_Images[MAX_WONDER_DISTRICT_TYPES]; + struct natural_wonder_district_image_set Natural_Wonder_Images[MAX_NATURAL_WONDER_DISTRICT_TYPES]; + } day_night_cycle_imgs[24]; // Districts enum init_state dc_img_state; @@ -1508,13 +1510,15 @@ struct injected_state { struct wonder_district_config wonder_district_configs[MAX_WONDER_DISTRICT_TYPES]; struct natural_wonder_district_config natural_wonder_configs[MAX_NATURAL_WONDER_DISTRICT_TYPES]; - struct district_image_set { - Sprite imgs[10][4][6]; // [variant][era][building_stage] - } district_img_sets[COUNT_DISTRICT_TYPES]; - - struct district_button_image_set { - Sprite imgs[4]; - } district_btn_img_sets[COUNT_DISTRICT_TYPES]; +struct district_image_set { + Sprite imgs[10][4][6]; // [variant][era][building_stage] +} district_img_sets[COUNT_DISTRICT_TYPES]; + Sprite abandoned_district_img; + Sprite abandoned_maritime_district_img; + +struct district_button_image_set { + Sprite imgs[4]; +} district_btn_img_sets[COUNT_DISTRICT_TYPES]; // Tech ID keys -> district ID. If a tech (aka advance) ID is present in the // table that means that tech enables a district. This also means one tech can enable at most one district. diff --git a/injected_code.c b/injected_code.c index 53445a8e..a1009d8a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -7223,15 +7223,20 @@ deinit_district_images (void) set->construct_img.vtable->destruct (&set->construct_img, __, 0); } - for (int ni = 0; ni < MAX_NATURAL_WONDER_DISTRICT_TYPES; ni++) { - Sprite * sprite = &is->natural_wonder_img_sets[ni].img; - if (sprite->vtable != NULL) - sprite->vtable->destruct (sprite, __, 0); - } - } - - is->dc_img_state = IS_UNINITED; -} + for (int ni = 0; ni < MAX_NATURAL_WONDER_DISTRICT_TYPES; ni++) { + Sprite * sprite = &is->natural_wonder_img_sets[ni].img; + if (sprite->vtable != NULL) + sprite->vtable->destruct (sprite, __, 0); + } + + if (is->abandoned_district_img.vtable != NULL) + is->abandoned_district_img.vtable->destruct (&is->abandoned_district_img, __, 0); + if (is->abandoned_maritime_district_img.vtable != NULL) + is->abandoned_maritime_district_img.vtable->destruct (&is->abandoned_maritime_district_img, __, 0); + } + + is->dc_img_state = IS_UNINITED; +} void clear_highlighted_worker_tiles_for_districts () @@ -10538,14 +10543,21 @@ bool load_day_night_hour_images(struct day_night_cycle_img_set *this, const char Sprite_slice_pcx (&this->District_Images[dc][variant_i][era][col], __, &img, tile_x, tile_y, 128, 64, 1, 1); } } - } - } - - // Load wonder district images (dynamically per wonder) for this hour - if (is->current_config.enable_wonder_districts) { - char const * last_img_path = NULL; - PCX_Image wpcx; - PCX_Image_construct (&wpcx); + } + } + + // Abandoned district art (land + maritime) for this hour + read_in_dir (&img, districts_hour_dir, "Abandoned.pcx", NULL); + if (img.JGL.Image == NULL) + return false; + Sprite_slice_pcx (&this->Abandoned_District_Image, __, &img, 0, 0, 128, 64, 1, 1); + Sprite_slice_pcx (&this->Abandoned_Maritime_District_Image, __, &img, 128, 0, 128, 64, 1, 1); + + // Load wonder district images (dynamically per wonder) for this hour + if (is->current_config.enable_wonder_districts) { + char const * last_img_path = NULL; + PCX_Image wpcx; + PCX_Image_construct (&wpcx); bool pcx_loaded = false; for (int wi = 0; wi < is->wonder_district_count; wi++) { @@ -10755,24 +10767,27 @@ build_sprite_proxies_24(Map_Renderer *mr) { int era_count = cfg->vary_img_by_era ? 4 : 1; int column_count = cfg->max_building_index + 1; - for (int variant_i = 0; variant_i < variant_count; variant_i++) { - if ((cfg->img_paths[variant_i] == NULL) || (cfg->img_paths[variant_i][0] == '\0')) - continue; - for (int era = 0; era < era_count; era++) { - for (int col = 0; col < column_count; col++) { - Sprite * base = &is->district_img_sets[dc].imgs[variant_i][era][col]; - Sprite * proxy = &is->day_night_cycle_imgs[h].District_Images[dc][variant_i][era][col]; - insert_sprite_proxy (base, proxy, h); - } - } - } - } - - // Wonder districts - if (is->current_config.enable_wonder_districts) { - for (int wi = 0; wi < is->wonder_district_count; wi++) { - Sprite * base_img = &is->wonder_district_img_sets[wi].img; - Sprite * proxy_img = &is->day_night_cycle_imgs[h].Wonder_District_Images[wi].img; + for (int variant_i = 0; variant_i < variant_count; variant_i++) { + if ((cfg->img_paths[variant_i] == NULL) || (cfg->img_paths[variant_i][0] == '\0')) + continue; + for (int era = 0; era < era_count; era++) { + for (int col = 0; col < column_count; col++) { + Sprite * base = &is->district_img_sets[dc].imgs[variant_i][era][col]; + Sprite * proxy = &is->day_night_cycle_imgs[h].District_Images[dc][variant_i][era][col]; + insert_sprite_proxy (base, proxy, h); + } + } + } + } + + insert_sprite_proxy (&is->abandoned_district_img, &is->day_night_cycle_imgs[h].Abandoned_District_Image, h); + insert_sprite_proxy (&is->abandoned_maritime_district_img, &is->day_night_cycle_imgs[h].Abandoned_Maritime_District_Image, h); + + // Wonder districts + if (is->current_config.enable_wonder_districts) { + for (int wi = 0; wi < is->wonder_district_count; wi++) { + Sprite * base_img = &is->wonder_district_img_sets[wi].img; + Sprite * proxy_img = &is->day_night_cycle_imgs[h].Wonder_District_Images[wi].img; insert_sprite_proxy (base_img, proxy_img, h); Sprite * base_construct = &is->wonder_district_img_sets[wi].construct_img; @@ -23595,10 +23610,37 @@ init_district_images () } pcx.vtable->clear_JGL (&pcx); - } - } - // Load wonder district images (dynamically per wonder) - if (is->current_config.enable_wonder_districts) { + } + } + // Load abandoned district images (land + maritime) + snprintf (art_dir, sizeof art_dir, "%s\\Art\\Districts\\1200\\Abandoned.pcx", is->mod_rel_dir); + PCX_Image_read_file (&pcx, __, art_dir, NULL, 0, 0x100, 2); + + if (pcx.JGL.Image == NULL) { + char ss[200]; + snprintf (ss, sizeof ss, "init_district_images: failed to load abandoned district images from %s", art_dir); + pop_up_in_game_error (ss); + for (int dc2 = 0; dc2 < COUNT_DISTRICT_TYPES; dc2++) + for (int variant_i2 = 0; variant_i2 < ARRAY_LEN (is->district_img_sets[dc2].imgs); variant_i2++) + for (int era_i = 0; era_i < 4; era_i++) { + for (int col = 0; col < ARRAY_LEN (is->district_img_sets[dc2].imgs[variant_i2][era_i]); col++) { + Sprite * sprite = &is->district_img_sets[dc2].imgs[variant_i2][era_i][col]; + if (sprite->vtable != NULL) + sprite->vtable->destruct (sprite, __, 0); + } + } + pcx.vtable->destruct (&pcx, __, 0); + return; + } + + Sprite_construct (&is->abandoned_district_img); + Sprite_slice_pcx (&is->abandoned_district_img, __, &pcx, 0, 0, 128, 64, 1, 1); + + Sprite_construct (&is->abandoned_maritime_district_img); + Sprite_slice_pcx (&is->abandoned_maritime_district_img, __, &pcx, 128, 0, 128, 64, 1, 1); + pcx.vtable->clear_JGL (&pcx); + // Load wonder district images (dynamically per wonder) + if (is->current_config.enable_wonder_districts) { char const * last_img_path = NULL; PCX_Image wpcx; PCX_Image_construct (&wpcx); @@ -24089,20 +24131,21 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par int buildings = 0; Sprite * district_sprite; - if (territory_owner_id > 0) { - Leader * leader = &leaders[territory_owner_id]; - culture = p_bic_data->Races[leader->RaceID].CultureGroupID; - if (cfg->vary_img_by_culture) - variant = culture; - if (cfg->vary_img_by_era) - era = leader->Era; - } else if (district_id != WONDER_DISTRICT_ID && district_id != NATURAL_WONDER_DISTRICT_ID) { - // Render abandoned district, special index 5 - variant = 5; - district_sprite = &is->district_img_sets[0].imgs[variant][era][buildings]; - patch_Sprite_draw_on_map (district_sprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); - return; - } + if (territory_owner_id > 0) { + Leader * leader = &leaders[territory_owner_id]; + culture = p_bic_data->Races[leader->RaceID].CultureGroupID; + if (cfg->vary_img_by_culture) + variant = culture; + if (cfg->vary_img_by_era) + era = leader->Era; + } else if (district_id != WONDER_DISTRICT_ID && district_id != NATURAL_WONDER_DISTRICT_ID) { + Sprite * abandoned_sprite = &is->abandoned_district_img; + if (tile->vtable->m35_Check_Is_Water (tile) && is->abandoned_maritime_district_img.vtable != NULL) + abandoned_sprite = &is->abandoned_maritime_district_img; + if (abandoned_sprite->vtable != NULL) + patch_Sprite_draw_on_map (abandoned_sprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + return; + } switch (district_id) { case NEIGHBORHOOD_DISTRICT_ID: From c99a83e7ca5df65f5321433b73eff274290fd724 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 10 Dec 2025 21:55:52 -0800 Subject: [PATCH 034/356] Add maritime alt images to c3x.h --- C3X.h | 65 +++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/C3X.h b/C3X.h index 678c6923..b4bfb2eb 100644 --- a/C3X.h +++ b/C3X.h @@ -579,8 +579,15 @@ struct wonder_district_config { img_row, img_column, img_construct_row, - img_construct_column; + img_construct_column, + + // Maritime wonders have a set of alternative images for facing east + maritime_alt_img_row, + maritime_alt_img_column, + maritime_alt_img_construct_row, + maritime_alt_img_construct_column; bool is_dynamic; + bool is_maritime; }; enum square_type_extras { @@ -622,15 +629,15 @@ struct wonder_location { }; const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYPES] = { - { - .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", - .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, - .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, - .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, - .img_path_count = 5, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, - .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .defense_bonus_percent = 25 - - }, + { + .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", + .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, + .img_path_count = 5, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, + .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .defense_bonus_percent = 25 + + }, { .command = UCV_Build_WonderDistrict, .name = "Wonder District", .tooltip = "Build Wonder District", .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, @@ -1490,16 +1497,16 @@ struct injected_state { Sprite Mountain_Rivers_Images[16]; Sprite Territory_Images[8]; Sprite LM_Mountains_Images[16]; - Sprite LM_Forests_Large_Images[8]; - Sprite LM_Forests_Small_Images[10]; - Sprite LM_Forests_Pines_Images[12]; - Sprite LM_Hills_Images[16]; - Sprite District_Images[COUNT_DISTRICT_TYPES][10][4][6]; // [district][variant][era][building_stage] - Sprite Abandoned_District_Image; - Sprite Abandoned_Maritime_District_Image; - struct wonder_district_image_set Wonder_District_Images[MAX_WONDER_DISTRICT_TYPES]; - struct natural_wonder_district_image_set Natural_Wonder_Images[MAX_NATURAL_WONDER_DISTRICT_TYPES]; - } day_night_cycle_imgs[24]; + Sprite LM_Forests_Large_Images[8]; + Sprite LM_Forests_Small_Images[10]; + Sprite LM_Forests_Pines_Images[12]; + Sprite LM_Hills_Images[16]; + Sprite District_Images[COUNT_DISTRICT_TYPES][10][4][6]; // [district][variant][era][building_stage] + Sprite Abandoned_District_Image; + Sprite Abandoned_Maritime_District_Image; + struct wonder_district_image_set Wonder_District_Images[MAX_WONDER_DISTRICT_TYPES]; + struct natural_wonder_district_image_set Natural_Wonder_Images[MAX_NATURAL_WONDER_DISTRICT_TYPES]; + } day_night_cycle_imgs[24]; // Districts enum init_state dc_img_state; @@ -1510,15 +1517,15 @@ struct injected_state { struct wonder_district_config wonder_district_configs[MAX_WONDER_DISTRICT_TYPES]; struct natural_wonder_district_config natural_wonder_configs[MAX_NATURAL_WONDER_DISTRICT_TYPES]; -struct district_image_set { - Sprite imgs[10][4][6]; // [variant][era][building_stage] -} district_img_sets[COUNT_DISTRICT_TYPES]; - Sprite abandoned_district_img; - Sprite abandoned_maritime_district_img; - -struct district_button_image_set { - Sprite imgs[4]; -} district_btn_img_sets[COUNT_DISTRICT_TYPES]; +struct district_image_set { + Sprite imgs[10][4][6]; // [variant][era][building_stage] +} district_img_sets[COUNT_DISTRICT_TYPES]; + Sprite abandoned_district_img; + Sprite abandoned_maritime_district_img; + +struct district_button_image_set { + Sprite imgs[4]; +} district_btn_img_sets[COUNT_DISTRICT_TYPES]; // Tech ID keys -> district ID. If a tech (aka advance) ID is present in the // table that means that tech enables a district. This also means one tech can enable at most one district. From e637387328c803b28b147eec9c9ac75dcb80727e Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 21 Nov 2025 16:33:47 -0800 Subject: [PATCH 035/356] Experimenting with art --- Art/Districts/1200/Port.PCX | Bin 0 -> 11053 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Art/Districts/1200/Port.PCX diff --git a/Art/Districts/1200/Port.PCX b/Art/Districts/1200/Port.PCX new file mode 100644 index 0000000000000000000000000000000000000000..985461602ab6bddc6054f745010f456633aab14c GIT binary patch literal 11053 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 literal 0 HcmV?d00001 From 32dec68e92cd2aabeaea47d12457ee76a26d8b16 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 09:28:34 -0800 Subject: [PATCH 036/356] Rebasing to master v26 --- C3X.h | 30 ++++- Civ3Conquests.h | 1 + civ_prog_objects.csv | 1 + default.c3x_config.ini | 1 + default.districts_config.txt | 12 ++ injected_code.c | 255 ++++++++++++++++++++++++++--------- 6 files changed, 230 insertions(+), 70 deletions(-) diff --git a/C3X.h b/C3X.h index 4b9d4743..e7e438bc 100644 --- a/C3X.h +++ b/C3X.h @@ -17,7 +17,7 @@ typedef unsigned char byte; #define MAX_BUILDING_PREREQS_FOR_UNIT 10 #define COUNT_SPECIAL_DISTRICT_TYPES 10 -#define USED_SPECIAL_DISTRICT_TYPES 5 +#define USED_SPECIAL_DISTRICT_TYPES 6 #define MAX_DYNAMIC_DISTRICT_TYPES 22 #define COUNT_DISTRICT_TYPES (COUNT_SPECIAL_DISTRICT_TYPES + MAX_DYNAMIC_DISTRICT_TYPES) #define MAX_WONDER_DISTRICT_TYPES 32 @@ -332,6 +332,7 @@ struct c3x_config { bool enable_wonder_districts; bool enable_distribution_hub_districts; bool enable_aerodrome_districts; + bool enable_port_districts; bool cities_with_mutual_district_receive_buildings; bool cities_with_mutual_district_receive_wonders; @@ -544,6 +545,10 @@ enum { MAX_DISTRICT_DEPENDENTS = 64 }; +enum { + DEFAULT_DISTRICT_BUILDABLE_MASK = (1 << SQ_Desert) | (1 << SQ_Plains) | (1 << SQ_Grassland) | (1 << SQ_Tundra) | (1 << SQ_FloodPlain) | (1 << SQ_Hills) +}; + struct district_config { enum Unit_Command_Values command; char const * name; @@ -551,10 +556,12 @@ struct district_config { char const * advance_prereq; char const * dependent_improvements[MAX_DISTRICT_DEPENDENTS]; char const * img_paths[10]; + unsigned short buildable_square_types_mask; bool allow_multiple; bool vary_img_by_era; bool vary_img_by_culture; bool is_dynamic; + bool is_maritime; int dependent_improvement_count; int img_path_count; int max_building_index; @@ -622,6 +629,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx", "Neighborhood_Abandoned.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 6, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .defense_bonus_percent = 25 @@ -630,6 +638,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_WonderDistrict, .name = "Wonder District", .tooltip = "Build Wonder District", .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"WonderDistrict.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 @@ -638,23 +647,34 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_DistributionHub, .name = "Distribution Hub", .tooltip = "Build Distribution Hub", .advance_prereq = "Construction", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"DistributionHub.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 2, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 }, { .command = UCV_Build_Aerodrome, .name = "Aerodrome", .tooltip = "Build Aerodrome", - .advance_prereq = "Flight", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereq = "Flight", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 1, .img_paths = {"Aerodrome.pcx"}, .dependent_improvements = {"Airport"}, - .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, + .img_path_count = 1, .max_building_index = 1, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 }, { - .command = -1, .name = "Natural Wonder", .tooltip = NULL, + .command = -1, .name = "Natural Wonder", .tooltip = NULL, .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {0}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 0, .max_building_index = 0, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 + }, + { + .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", + .advance_prereq = "Map Making", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, + .img_paths = {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, + .img_path_count = 8, .max_building_index = 2, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 2, .gold_bonus = 2, .shield_bonus = 0, .defense_bonus_percent = 0 } }; @@ -677,6 +697,7 @@ struct parsed_district_definition { int food_bonus; int gold_bonus; int shield_bonus; + unsigned short buildable_square_types_mask; bool has_name; bool has_tooltip; bool has_advance_prereq; @@ -693,6 +714,7 @@ struct parsed_district_definition { bool has_food_bonus; bool has_gold_bonus; bool has_shield_bonus; + bool has_buildable_on; }; struct parsed_wonder_definition { diff --git a/Civ3Conquests.h b/Civ3Conquests.h index 0ff12da2..fe5c1f02 100644 --- a/Civ3Conquests.h +++ b/Civ3Conquests.h @@ -772,6 +772,7 @@ enum Unit_Command_Values UCV_Build_WonderDistrict = -10000002, UCV_Build_DistributionHub = -10000003, UCV_Build_Aerodrome = -10000004, + UCV_Build_Port = -10000005, }; enum Unit_Mode_Actions diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 0921091c..356d2b0f 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -100,6 +100,7 @@ define, 0x5B2B20, 0x5C1440, 0x5B2830, "Unit_next_escorter_id", "int (__fast define, 0x5BC300, 0x5CAE50, 0x5BC010, "Unit_disband", "void (__fastcall *) (Unit * this)" inlead, 0x5C00A0, 0x5CEC20, 0x5BFDB0, "Unit_can_hurry_production", "bool (__fastcall *) (Unit * this, int edx, City * city, bool exclude_cheap_improvements)" inlead, 0x5B3AB0, 0x5C2400, 0x5B37C0, "Unit_can_pillage", "bool (__fastcall *) (Unit *this, int edx, int tile_x, int tile_y)" +inlead, 0x5CCBB0, 0x0, 0x0, "Unit_can_pass_between", "bool (__fastcall *) (Unit *this, int edx, int from_x, int from_y, int to_x, int to_y, byte param_5)" define, 0x5C0420, 0x5CEFC0, 0x5C0130, "Unit_hurry_production", "void (__fastcall *) (Unit * this)" define, 0x5C0300, 0x5CEE90, 0x5C0010, "Unit_ai_can_start_science_age", "bool (__fastcall *) (Unit * this)" define, 0x5C03B0, 0x5CEF50, 0x5C00C0, "Unit_start_science_age", "void (__fastcall *) (Unit * this)" diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 5ccc4967..a17b9da1 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -828,6 +828,7 @@ enable_neighborhood_districts = false enable_wonder_districts = false enable_distribution_hub_districts = false enable_aerodrome_districts = false +enable_port_districts = false ; When multiple cities share a district (i.e., the same district tile is within multiple cities' work radii), these options control whether those ; cities automatically share the benefits of buildings and wonders constructed in that district. For example, if Rome and Veii both have the same diff --git a/default.districts_config.txt b/default.districts_config.txt index e232cb6e..97938c38 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -149,4 +149,16 @@ culture_bonus = 0 science_bonus = 0 food_bonus = 0 gold_bonus = 0 +shield_bonus = 0 + +#District +name = Port +advance_prereq = Map Making +dependent_improvs = Harbor, "Commercial Dock" +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 2 +gold_bonus = 2 shield_bonus = 0 \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index aefa52f2..6649ac00 100644 --- a/injected_code.c +++ b/injected_code.c @@ -71,7 +71,7 @@ struct injected_state * is = ADDR_INJECTED_STATE; #define DISTRIBUTION_HUB_DISTRICT_ID 2 #define AERODROME_DISTRICT_ID 3 #define NATURAL_WONDER_DISTRICT_ID 4 - +#define PORT_DISTRICT_ID 5 char const * const hotseat_replay_save_path = "Saves\\Auto\\ai-move-replay-before-interturn.SAV"; char const * const hotseat_resume_save_path = "Saves\\Auto\\ai-move-replay-resume.SAV"; @@ -1722,6 +1722,40 @@ read_square_type_value (struct string_slice const * s, enum SquareTypes * out_ty return false; } +unsigned short +square_type_mask_bit (enum SquareTypes type) +{ + if ((int)type < 0 || type > SQ_RIVER) + return 0; + return (unsigned short)(1u << type); +} + +unsigned short +all_square_types_mask (void) +{ + return (unsigned short)((1u << (SQ_RIVER + 1)) - 1); +} + +unsigned short +district_default_buildable_mask (void) +{ + return (unsigned short)DEFAULT_DISTRICT_BUILDABLE_MASK; +} + +bool +district_is_buildable_on_square_type (struct district_config const * cfg, enum SquareTypes base_type) +{ + if (cfg == NULL) + return false; + + unsigned short mask = cfg->buildable_square_types_mask; + if (mask == 0) + mask = district_default_buildable_mask (); + + unsigned short bit = square_type_mask_bit (base_type); + return (bit != 0) && ((mask & bit) != 0); +} + bool read_natural_wonder_terrain_type (struct string_slice const * s, enum SquareTypes * out_type) { @@ -4741,6 +4775,7 @@ init_parsed_district_definition (struct parsed_district_definition * def) memset (def, 0, sizeof *def); def->img_path_count = -1; def->defense_bonus_percent = 100; + def->buildable_square_types_mask = district_default_buildable_mask (); } void @@ -4917,6 +4952,70 @@ parse_config_string_list (char * value_text, return true; } +bool +parse_buildable_square_type_mask (struct string_slice const * value, + unsigned short * out_mask, + struct error_line ** parse_errors, + int line_number) +{ + char * value_text = trim_and_extract_slice (value, 0); + unsigned short mask = 0; + int entry_count = 0; + + if (value_text != NULL) { + char * cursor = value_text; + while (1) { + while (is_space_char (*cursor)) + cursor++; + + char * item_start = cursor; + while ((*cursor != '\0') && (*cursor != ',')) + cursor++; + + char * item_end = cursor; + while ((item_end > item_start) && is_space_char (item_end[-1])) + item_end--; + + struct string_slice item_slice = { .str = item_start, .len = (int)(item_end - item_start) }; + if (item_slice.len > 0) { + enum SquareTypes parsed; + if (read_square_type_value (&item_slice, &parsed)) { + if (parsed == (enum SquareTypes)SQ_INVALID) + mask = all_square_types_mask (); + else + mask |= square_type_mask_bit (parsed); + entry_count += 1; + } else { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %.*s (invalid buildable_on entry)", line_number, item_slice.len, item_slice.str); + err->text[(sizeof err->text) - 1] = '\0'; + free (value_text); + return false; + } + } + + if (*cursor == ',') { + cursor++; + continue; + } + break; + } + } + + if (value_text != NULL) + free (value_text); + + if ((entry_count == 0) || (mask == 0)) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: buildable_on (expected at least one square type)", line_number); + err->text[(sizeof err->text) - 1] = '\0'; + return false; + } + + *out_mask = mask; + return true; +} + bool override_special_district_from_definition (struct parsed_district_definition * def, int section_start_line) { @@ -4970,6 +5069,8 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->gold_bonus = def->gold_bonus; if (def->has_shield_bonus) cfg->shield_bonus = def->shield_bonus; + if (def->has_buildable_on) + cfg->buildable_square_types_mask = def->buildable_square_types_mask; if (def->has_dependent_improvements) { for (int i = 0; i < ARRAY_LEN (cfg->dependent_improvements); i++) { @@ -5081,6 +5182,7 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.food_bonus = def->has_food_bonus ? def->food_bonus : 0; new_cfg.gold_bonus = def->has_gold_bonus ? def->gold_bonus : 0; new_cfg.shield_bonus = def->has_shield_bonus ? def->shield_bonus : 0; + new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); new_cfg.dependent_improvement_count = def->has_dependent_improvements ? def->dependent_improvement_count : 0; const int max_dependent_entries = ARRAY_LEN (is->district_configs[0].dependent_improvements); @@ -5208,7 +5310,7 @@ handle_district_definition_key (struct parsed_district_definition * def, &list_count, parse_errors, line_number, - "dependent_improvs")) { + "dependent_improvs")) { def->dependent_improvement_count = list_count; def->has_dependent_improvements = true; } else { @@ -5217,6 +5319,13 @@ handle_district_definition_key (struct parsed_district_definition * def, } free (value_text); + } else if (slice_matches_str (key, "buildable_on")) { + unsigned short mask; + if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number)) { + def->buildable_square_types_mask = mask; + def->has_buildable_on = true; + } + } else if (slice_matches_str (key, "allow_multiple")) { struct string_slice val_slice = *value; int ival; @@ -12124,8 +12233,7 @@ set_up_district_buttons (Main_GUI * this) Tile * tile = tile_at (selected_unit->Body.X, selected_unit->Body.Y); if ((tile == NULL) || (tile == p_null_tile) || (tile->CityID >= 0)) return; - int base_type = tile->vtable->m50_Get_Square_BaseType (tile); - if (base_type == SQ_Mountains || base_type == SQ_Forest || base_type == SQ_Jungle || base_type == SQ_Swamp || base_type == SQ_Volcano) return; + enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); if (tile->vtable->m21_Check_Crates (tile, __, 0)) return; if (tile->vtable->m20_Check_Pollution (tile, __, 0)) return; @@ -12207,6 +12315,8 @@ set_up_district_buttons (Main_GUI * this) int prereq_id = is->district_infos[dc].advance_prereq_id; if ((prereq_id >= 0) && !Leader_has_tech(&leaders[selected_unit->Body.CivID], __, prereq_id)) continue; + if (! district_is_buildable_on_square_type (&is->district_configs[dc], base_type)) + continue; // This district should be shown active_districts[active_count++] = dc; @@ -12833,11 +12943,14 @@ issue_district_worker_command (Unit * unit, int command) if (! is->current_config.enable_districts) return; + if (unit == NULL) + return; + int tile_x = unit->Body.X; int tile_y = unit->Body.Y; Tile * tile = tile_at (tile_x, tile_y); - int unit_type_id = unit->Body.UnitTypeID; - int unit_id = unit->Body.ID; + if ((tile == NULL) || (tile == p_null_tile)) + return; if (! is_worker(unit)) return; @@ -12860,80 +12973,77 @@ issue_district_worker_command (Unit * unit, int command) if (tile->vtable->m20_Check_Pollution (tile, __, 0)) return; enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType(tile); - if (base_type == SQ_Mountains || base_type == SQ_Forest || base_type == SQ_Jungle || base_type == SQ_Swamp || base_type == SQ_Volcano) { + if (base_type == SQ_Mountains || base_type == SQ_Forest || base_type == SQ_Jungle || base_type == SQ_Swamp) { return; } } if (tile != NULL && tile != p_null_tile) { - // If District will be replaced by another District - struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && district_is_complete(tile, inst->district_type)) { - int district_id = inst->district_type; - int inst_x, inst_y; - if (! district_instance_get_coords (inst, tile, &inst_x, &inst_y)) - return; + // If District will be replaced by another District + struct district_instance * inst = get_district_instance (tile); + if (inst != NULL && district_is_complete(tile, inst->district_type)) { + int existing_district_id = inst->district_type; + int inst_x, inst_y; + if (! district_instance_get_coords (inst, tile, &inst_x, &inst_y)) + return; - int civ_id = unit->Body.CivID; - bool redundant_district = district_instance_is_redundant (inst, tile); - bool would_lose_buildings = any_nearby_city_would_lose_district_benefits (district_id, civ_id, inst_x, inst_y); - if (redundant_district) - would_lose_buildings = false; + int civ_id = unit->Body.CivID; + bool redundant_district = district_instance_is_redundant (inst, tile); + bool would_lose_buildings = any_nearby_city_would_lose_district_benefits (existing_district_id, civ_id, inst_x, inst_y); + if (redundant_district) + would_lose_buildings = false; - bool remove_existing = false; - - PopupForm * popup = get_popup_form (); - set_popup_str_param (0, (char*)is->district_configs[district_id].name, -1, -1); - set_popup_str_param (1, (char*)is->district_configs[district_id].name, -1, -1); - popup->vtable->set_text_key_and_flags ( - popup, __, is->mod_script_path, - would_lose_buildings - ? "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT" - : "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT_SAFE", - -1, 0, 0, 0 - ); - - int sel = patch_show_popup (popup, __, 0, 0); - if (sel == 0) - remove_existing = true; - else - return; + bool remove_existing = false; + + PopupForm * popup = get_popup_form (); + set_popup_str_param (0, (char*)is->district_configs[existing_district_id].name, -1, -1); + set_popup_str_param (1, (char*)is->district_configs[existing_district_id].name, -1, -1); + popup->vtable->set_text_key_and_flags ( + popup, __, is->mod_script_path, + would_lose_buildings + ? "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT" + : "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT_SAFE", + -1, 0, 0, 0 + ); - if (remove_existing) { - remove_district_instance (tile); - tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); - tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, inst_x, inst_y); - handle_district_removed (tile, district_id, inst_x, inst_y, false); - } - } + int sel = patch_show_popup (popup, __, 0, 0); + if (sel == 0) + remove_existing = true; + else + return; - // If District will replace an improvement - if (itable_look_up (&is->command_id_to_district_id, command, &district_id)) { - unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); - unsigned int removable_flags = overlay_flags & 0xfc; + if (remove_existing) { + remove_district_instance (tile); + tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, inst_x, inst_y); + handle_district_removed (tile, existing_district_id, inst_x, inst_y, false); + } + } - if (removable_flags != 0) { - PopupForm * popup = get_popup_form (); - set_popup_str_param (0, (char*)is->district_configs[district_id].name, -1, -1); - popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_CONFIRM_BUILD_DISTRICT_OVER_IMPROVEMENT", -1, 0, 0, 0); - int sel = patch_show_popup (popup, __, 0, 0); - if (sel != 0) - return; - } + // If District will replace an improvement + unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); + unsigned int removable_flags = overlay_flags & 0xfc; - if (removable_flags != 0) - tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, removable_flags, tile_x, tile_y); - tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, tile_x, tile_y); - } + if (removable_flags != 0) { + PopupForm * popup = get_popup_form (); + set_popup_str_param (0, (char*)is->district_configs[district_id].name, -1, -1); + popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_CONFIRM_BUILD_DISTRICT_OVER_IMPROVEMENT", -1, 0, 0, 0); + int sel = patch_show_popup (popup, __, 0, 0); + if (sel != 0) + return; + } - inst = ensure_district_instance (tile, district_id, tile_x, tile_y); - if (inst != NULL) - inst->state = DS_UNDER_CONSTRUCTION; + if (removable_flags != 0) + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, removable_flags, tile_x, tile_y); + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, tile_x, tile_y); - Unit_set_state(unit, __, UnitState_Build_Mines); - unit->Body.Job_ID = WJ_Build_Mines; - } + inst = ensure_district_instance (tile, district_id, tile_x, tile_y); + if (inst != NULL) + inst->state = DS_UNDER_CONSTRUCTION; + + Unit_set_state (unit, __, UnitState_Build_Mines); + unit->Body.Job_ID = WJ_Build_Mines; } void @@ -23483,6 +23593,19 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i return false; } +int +get_port_district_variant_for_tile (Tile * tile) +{ + int variant = 0; + + int sheet_index = (tile->SquareParts >> 8) & 0xFF; + int sprite_index = tile->SquareParts & 0xFF; + + // TODO + + return variant; +} + void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { From 0284d38c10f0caaa995b170433c5eca4057083e3 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 09:33:49 -0800 Subject: [PATCH 037/356] Resolved merge issues --- C3X.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/C3X.h b/C3X.h index e7e438bc..1c150220 100644 --- a/C3X.h +++ b/C3X.h @@ -671,7 +671,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", .advance_prereq = "Map Making", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, - .img_paths = {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, + .img_paths = {0},// {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 8, .max_building_index = 2, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 2, .gold_bonus = 2, .shield_bonus = 0, .defense_bonus_percent = 0 From 9bc25646af50f252b985465d39cbd52a6aac8617 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 09:35:28 -0800 Subject: [PATCH 038/356] Resolved merge issues --- C3X.h | 10 ++++++ injected_code.c | 95 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 85 insertions(+), 20 deletions(-) diff --git a/C3X.h b/C3X.h index 1c150220..c4a78b8d 100644 --- a/C3X.h +++ b/C3X.h @@ -338,8 +338,11 @@ struct c3x_config { bool cities_with_mutual_district_receive_wonders; bool show_message_when_building_received_by_mutual_district; +<<<<<<< HEAD bool air_units_use_aerodrome_districts_not_cities; +======= +>>>>>>> 8e39ccd (Enable workers to walk on coast; Add granular settings for districts & buildable square types) int maximum_pop_before_neighborhood_needed; int per_neighborhood_pop_growth_enabled; int neighborhood_needed_message_frequency; @@ -671,7 +674,11 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", .advance_prereq = "Map Making", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, +<<<<<<< HEAD .img_paths = {0},// {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, +======= + .img_paths = {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, +>>>>>>> 8e39ccd (Enable workers to walk on coast; Add granular settings for districts & buildable square types) .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 8, .max_building_index = 2, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 2, .gold_bonus = 2, .shield_bonus = 0, .defense_bonus_percent = 0 @@ -715,6 +722,7 @@ struct parsed_district_definition { bool has_gold_bonus; bool has_shield_bonus; bool has_buildable_on; +<<<<<<< HEAD }; struct parsed_wonder_definition { @@ -757,6 +765,8 @@ struct parsed_natural_wonder_definition { bool has_food_bonus; bool has_gold_bonus; bool has_shield_bonus; +======= +>>>>>>> 8e39ccd (Enable workers to walk on coast; Add granular settings for districts & buildable square types) }; struct scenario_district_entry { diff --git a/injected_code.c b/injected_code.c index 6649ac00..4bc98879 100644 --- a/injected_code.c +++ b/injected_code.c @@ -12955,30 +12955,69 @@ issue_district_worker_command (Unit * unit, int command) if (! is_worker(unit)) return; + int district_id = -1; + if (! itable_look_up (&is->command_id_to_district_id, command, &district_id)) + return; + if ((district_id < 0) || (district_id >= is->district_count)) + return; + // Check tech prerequisite for the selected district, if any - int district_id; - if (itable_look_up (&is->command_id_to_district_id, command, &district_id)) { - if (district_id < 0 || district_id >= is->district_count) + int prereq_id = is->district_infos[district_id].advance_prereq_id; + // Only enforce if a prereq is configured + if ((prereq_id >= 0) && !Leader_has_tech (&leaders[unit->Body.CivID], __, prereq_id)) { + return; // Civ lacks required tech; do not issue command + } + + // Disallow placing districts on invalid terrain, pollution, or cratered tiles + if (tile->vtable->m21_Check_Crates (tile, __, 0)) + return; + if (tile->vtable->m20_Check_Pollution (tile, __, 0)) + return; + + enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); + if (! district_is_buildable_on_square_type (&is->district_configs[district_id], base_type)) + return; + + // If District will be replaced by another District + struct district_instance * inst = get_district_instance (tile); + if (inst != NULL && district_is_complete(tile, inst->district_type)) { + int existing_district_id = inst->district_type; + int inst_x, inst_y; + if (! district_instance_get_coords (inst, tile, &inst_x, &inst_y)) + return; + + int civ_id = unit->Body.CivID; + bool redundant_district = district_instance_is_redundant (inst, tile); + bool would_lose_buildings = any_nearby_city_would_lose_district_benefits (existing_district_id, civ_id, inst_x, inst_y); + if (redundant_district) + would_lose_buildings = false; + + bool remove_existing = false; + + PopupForm * popup = get_popup_form (); + set_popup_str_param (0, (char*)is->district_configs[existing_district_id].name, -1, -1); + set_popup_str_param (1, (char*)is->district_configs[existing_district_id].name, -1, -1); + popup->vtable->set_text_key_and_flags ( + popup, __, is->mod_script_path, + would_lose_buildings + ? "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT" + : "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT_SAFE", + -1, 0, 0, 0 + ); + + int sel = patch_show_popup (popup, __, 0, 0); + if (sel == 0) + remove_existing = true; + else return; - int prereq_id = is->district_infos[district_id].advance_prereq_id; - // Only enforce if a prereq is configured - if (prereq_id >= 0 && !Leader_has_tech (&leaders[unit->Body.CivID], __, prereq_id)) { - return; // Civ lacks required tech; do not issue command + + if (remove_existing) { + remove_district_instance (tile); + tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, inst_x, inst_y); + handle_district_removed (tile, existing_district_id, inst_x, inst_y, false); } } - // Disallow placing districts on invalid terrain, pollution, or cratered tiles - if (tile != NULL && tile != p_null_tile) { - if (tile->vtable->m21_Check_Crates (tile, __, 0)) - return; - if (tile->vtable->m20_Check_Pollution (tile, __, 0)) - return; - enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType(tile); - if (base_type == SQ_Mountains || base_type == SQ_Forest || base_type == SQ_Jungle || base_type == SQ_Swamp) { - return; - } - } - - if (tile != NULL && tile != p_null_tile) { // If District will be replaced by another District struct district_instance * inst = get_district_instance (tile); @@ -24783,5 +24822,21 @@ patch_Unit_disembark (Unit * this, int edx, int tile_x, int tile_y) } } +PassBetweenValidity __fastcall +patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int to_x, int to_y, int param_5) +{ + PassBetweenValidity base = Unit_can_pass_between (this, __, from_x, from_y, to_x, to_y, param_5); + + if (base != PBV_OK && is->current_config.enable_port_districts && is_worker(this)) { + Tile * dest = tile_at (to_x, to_y); + if ((dest != NULL) && + dest->vtable->m35_Check_Is_Water (dest) && + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) + return PBV_OK; // Let workers treat coast as passable when port districts are on + } + + return base; +} + // TCC requires a main function be defined even though it's never used. int main () { return 0; } From c50c28bd3047801dc1f385a4e208a510164df218 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 09:39:10 -0800 Subject: [PATCH 039/356] Resolve merge commits --- C3X.h | 10 ---------- injected_code.c | 41 ----------------------------------------- 2 files changed, 51 deletions(-) diff --git a/C3X.h b/C3X.h index c4a78b8d..e7e438bc 100644 --- a/C3X.h +++ b/C3X.h @@ -338,11 +338,8 @@ struct c3x_config { bool cities_with_mutual_district_receive_wonders; bool show_message_when_building_received_by_mutual_district; -<<<<<<< HEAD bool air_units_use_aerodrome_districts_not_cities; -======= ->>>>>>> 8e39ccd (Enable workers to walk on coast; Add granular settings for districts & buildable square types) int maximum_pop_before_neighborhood_needed; int per_neighborhood_pop_growth_enabled; int neighborhood_needed_message_frequency; @@ -674,11 +671,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", .advance_prereq = "Map Making", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, -<<<<<<< HEAD - .img_paths = {0},// {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, -======= .img_paths = {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, ->>>>>>> 8e39ccd (Enable workers to walk on coast; Add granular settings for districts & buildable square types) .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 8, .max_building_index = 2, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 2, .gold_bonus = 2, .shield_bonus = 0, .defense_bonus_percent = 0 @@ -722,7 +715,6 @@ struct parsed_district_definition { bool has_gold_bonus; bool has_shield_bonus; bool has_buildable_on; -<<<<<<< HEAD }; struct parsed_wonder_definition { @@ -765,8 +757,6 @@ struct parsed_natural_wonder_definition { bool has_food_bonus; bool has_gold_bonus; bool has_shield_bonus; -======= ->>>>>>> 8e39ccd (Enable workers to walk on coast; Add granular settings for districts & buildable square types) }; struct scenario_district_entry { diff --git a/injected_code.c b/injected_code.c index 4bc98879..0ae2a5fa 100644 --- a/injected_code.c +++ b/injected_code.c @@ -13019,47 +13019,6 @@ issue_district_worker_command (Unit * unit, int command) } } - // If District will be replaced by another District - struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && district_is_complete(tile, inst->district_type)) { - int existing_district_id = inst->district_type; - int inst_x, inst_y; - if (! district_instance_get_coords (inst, tile, &inst_x, &inst_y)) - return; - - int civ_id = unit->Body.CivID; - bool redundant_district = district_instance_is_redundant (inst, tile); - bool would_lose_buildings = any_nearby_city_would_lose_district_benefits (existing_district_id, civ_id, inst_x, inst_y); - if (redundant_district) - would_lose_buildings = false; - - bool remove_existing = false; - - PopupForm * popup = get_popup_form (); - set_popup_str_param (0, (char*)is->district_configs[existing_district_id].name, -1, -1); - set_popup_str_param (1, (char*)is->district_configs[existing_district_id].name, -1, -1); - popup->vtable->set_text_key_and_flags ( - popup, __, is->mod_script_path, - would_lose_buildings - ? "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT" - : "C3X_CONFIRM_REPLACE_DISTRICT_WITH_DIFFERENT_DISTRICT_SAFE", - -1, 0, 0, 0 - ); - - int sel = patch_show_popup (popup, __, 0, 0); - if (sel == 0) - remove_existing = true; - else - return; - - if (remove_existing) { - remove_district_instance (tile); - tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); - tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, inst_x, inst_y); - handle_district_removed (tile, existing_district_id, inst_x, inst_y, false); - } - } - // If District will replace an improvement unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); unsigned int removable_flags = overlay_flags & 0xfc; From 0aa285bff46846f9caba608559edbee41e53361d Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 09:44:53 -0800 Subject: [PATCH 040/356] Resolve merge commits --- Art/Districts/1200/Port.PCX | Bin 11053 -> 0 bytes Art/Districts/1200/Port_NE.PCX | Bin 0 -> 8885 bytes Art/Districts/1200/Port_NW.PCX | Bin 0 -> 8117 bytes Art/Districts/1200/Port_SE.PCX | Bin 0 -> 8885 bytes Art/Districts/1200/Port_SW.PCX | Bin 0 -> 8885 bytes Art/Districts/WorkerDistrictButtonsNorm.pcx | Bin 29281 -> 29858 bytes C3X.h | 12 +- civ_prog_objects.csv | 1 + default.c3x_config.ini | 2 + injected_code.c | 163 ++++++++++++++++---- 10 files changed, 146 insertions(+), 32 deletions(-) delete mode 100644 Art/Districts/1200/Port.PCX create mode 100644 Art/Districts/1200/Port_NE.PCX create mode 100644 Art/Districts/1200/Port_NW.PCX create mode 100644 Art/Districts/1200/Port_SE.PCX create mode 100644 Art/Districts/1200/Port_SW.PCX diff --git a/Art/Districts/1200/Port.PCX b/Art/Districts/1200/Port.PCX deleted file mode 100644 index 985461602ab6bddc6054f745010f456633aab14c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11053 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 diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX new file mode 100644 index 0000000000000000000000000000000000000000..d9d9d15be531146383906202fe4efbdbf4e6cffb GIT binary patch literal 8885 zcmeHMeNa@_6_;o;X_Iz_W~S}%l6liGI}V~|m#;;aUAhoZRza4qBB6kbK`jIXg&2)! zrjjg56u%G&0g@0QtfAIKLQpg5dka#&1qBpTM1qK*1h;6AcyG_UXlva)o#v1Jk(S-r z-z?m7@4N4w-#O=Z-u~<-4o}U17j>XBW;nq6hrS$UJn_RP{qrJ}h?J6tD6qc>!~5hk zj7&28p5Z;f`|zIz`2Sso-!Z%cI7uc*Gc^mRDY;Erz~FBTrSB79+)B+W&6JFib{P7W z;Vr4eb{hBpAmi4{hLbLN#YQLom2l10fN=?0VQ46gxN$rvnC z6)m%<)J`Glax>UmC6~eG8-~*NF}Q38ElG+siYjABiI{2@V7!-H1-!!WGTvbS#k^T9K&|NA-JdWRKb@Dox)f~lBg9EG|_t5hN^ln zYbCb<&oFEO93%te0u@Ufy%q>oVJtIBo0$|V(>NNNz^swn0c>K}060MU;e;|oYm|(W zKjkHiWR_@CRk8fc#AdLoBliIR%J4K`Kj|YEsR*kI)+dr=8cwc-}o6TJh*jld}h#~JpJ3uK7G{@R5x#e5%~zfP`k6m>EFezSN5w<@=V z-Uho?ppk@Q3@?B)N2pK}mdJ}bxsD6>;rLl$&RiU~$`F(Ix`i4V=q<233zU)YCc`e$ z0kd8|B*rBAi#k4;^Ldif$V5)#$0f({y2Q=lPp)pFH^8 z3$8d{l!b_6!id@!YgAac8lF?M&}(4Z1KyW#fZ3@oQCBKLZF?4idi zG88`YwVkvR<_yy=h8+wWNxgInVx=~;$t4C^5b zh9CEuMHGN*S#8qXTfMXeEUmPaVKc)zQVX*l^O61mN zXm^T*T&HqBMY`YAaf6qvqPSHGQ+pe#h13T(IHFK#)Nr=K$YDW*0~XFn%iFu(Iw@Y+ z9z>CgHi}kCYwVt6J^6;M42frCk5RI+=cPtS8CxOpf!`1A$sP?P?+n@{nB={Fy9}0i z&}%n3B%buxjXa5VU3OzxqUBk;*+HUutKDoPg+im<+$BX>o!xXOMWMxRa_wW;*)~)6 zvqW#3^aohEuq_>ftmfF(pCMMbY>U`1D}AKLdFy|rob`9jwyVMyI2Q-_Z)big7-FfKZN&Cc;AKh zYIr8XdqTXAl$!cs!ie$a$nJ)N z85{IHb#I61g36J+gIh z)AD#Hb4R&UW{5JyM;O{;twg5UGot9g#4%vL-Kny{@7z-7k2q>w=%} zE6h0AWeSe;mU}xFS8q@+;{!rH-29x9-U@S3{_3}L9V1>1e9vs^8vlH2?yFvYuC?dC zcxkcraD7fyZ}B`&m!L>(mU&~*sRMJoXD9Anvm$MIxw-t!j5k5%dxfZyOEWf}%-Pyd zm_K+Tr}s1Os6`*&u6w7WT$>nf+L85M;rkzzl>F)F(c_hsCr_QS)YP=Kw_oY&YiMdZ q-`#!f`t>Dx{X9>lQti9%K$SBAVMkIvCV9=}t1Obgi zVoFJ76ct~9Kv-jl%8X%M6UhWMmF?y+;q9P;3JOXTpD50%QDWaceTikV+vh`Sw`%u; zL-C`y-F^GsbI<*sfA{?2XKv4qfe&#bW5&3_&rkihjd|v$U-aZAL=*@SACss5iIk7T zMmRD;`5om$;3H^hhW7iE-%{QKj))J$c4&7|{)6%k@By6J3GKHiZ&MBd?~8ZEE@;0_ zd4uvA@GkUl7TO0XuTWkFy2N4eJhTr`enWW?I1D{sg!Vqle#-N}TjEXeGPL(l_EMe& z-h?||gZ6I9)0ADn>*7^$2-;6jo}}yqUWJj|f%Xo{2I4bpgtU-* zl=ms`Quc^v;BN=HL+PR%raS{P?kBu+(`_;Y4JRN#5^qv=i>DxDd_QxI*!adqat&I$ zAtVy7Ql5gqd_!y+rsN8-CD|P0GBo!<6eM1vJRv&83#3dSn^v+)CN-KTYjz9}Ymz3r z3eLX>=k!4oBwnC&LQDrpRY94+xYseynr#9}Uw5AHCv8b8!N3`+EDmxW&Kw|8ga(Kd zjRDdjwuzSrF9^S8>lnSjxIJfy78w2{-*4<8_IOpDhVzKvtTsy{ISUsI5-C)JL<-9w zX@ljuMC^QTrB0|+z$o=0f--$<$XIucrI%EuC7J|{DWX!yw0%vw;EL;{oAMN8i`XPy zA$(V5p5Th(6skZ*$ppF!Zh_3$)E<(r%SsYB6Bof5Y|W$-uDC@`P&z4_ASJF#19$Pm zyz3cWP$g-nCMS#&nCDXU>06Z1r{UnTthD@BxjIw4t(COHWiFVn#5PKY*Z>*HJNY{P zIPY2;x6Ggz&$4?`$WBVW302YeIgUEFtttrdekLCuQ&MCy=p<95EAUpu*$_ntijzfp88EVEoYN9e(x!9PHx>6wPjK6Ip?9@Pgh+eH##8)O3 z2Wz=R4~`e)r`;evAQk~~Xc&4M;%}$~r8ZcZp3LY|bEC&CSy@l``lwV!(5v;z=s@Mq zRq-Z8w%sC>waBi9LmZJp-UV~M!I=_GY@M>6QEFL%tlT9eq=pE3CS4ggPN|)+)nwU} zE1wlj(1|q7F8-!Zd_ZxwNl-&-3Q8vZ!}R6p5I`8tR6!*~*aX4SCYzfr;&+7S)9PKt zksRyBOjV{QOwCBnNKTEK$S4Ejk|J##vOA)U-umx+lavP4ky*v+kSIo-oSp!AWz?Ir zKG|szW$m(Cu>&qW^f-H}^AzgjgypQqnuKI#)(S)pDbtWtE1F z)-u7eu2ohOyTxzGrm|TE1v{UaP;B7`+GWM{44hV`PSOU$?$2+R73Utv)UuUF8k~dY7f0Fl7l2ej*O_C3icSe%Qk+(gPnUS|Nl2MVjE0Xh&cOjCkkT(~SQINL_ zQpF#w`=jK^lgf`DlRScakCG9QABAL8zFoRT7qd0rd61> zVOof3C#JQSHe*_jX+K^q@FQ4X*uAl_Vspb{fyEAsHMTa`T4HODu3Fj_E#L zDexm$U)a5|v0`(39E)zy)X_J-KYrKS1MQ=4e4oDSb-=rH>AmlN_OAD74Z&6ahc)Es zvxwZ&=hHg!@81JGtt0Ry75!##{55MvtvP1;6qU0)rD;ceM?>wQLeuWGMyomYNOAJj zfirt|-t3d{#Evhn7eJUXPb-jvJ5?r_oDRbb=k9TI&*Ga{F{00;-)XRef637lMl0v z;q0yr#$&c6;j_I;%=0#{`BSDz6&Ewx5UwdHOj~LSJFsW9e$KR*C{E+A%1PDjFNscx zW7p^SS@)GV8#Yby{C&mN1+8DiBrjw#(_T4To!{0_)ZbO{QF+E!hvHwEKe2HA^o{1s zgjgSAzlJR;@Q#WMIP!V!C;OMo3!6O6=Y^JrH+4ZT&G3J*raGp2 zZ}_&2L4lfy1?IUy+>2qM%$AKacbEE%JgD8VZC*urNUeST8%rkb+&g_MwXN}ps&3~q5 zN<^furNCHJ9+sUuY4^SvtgoWxOW%D5=WPGLZ+(GkcBp@K&7Th)d)1U2=g&QFF7lqr zG8;?gFcW|I?iOuOuy5}A*p9)~3sXF}u;0vG^!$>yrY&CO>Feixpw^PJ$=_TatB>`J zSvI+*z9eOZzS@x!96s55#;>+lYZ1bd{t8|CHyyFX4= z#JoPI%x3Ps``J51ulogiH=O=_)?D4*rb2t)?kUr~!eVt6+p51D+c_z~Gj+?-McE6j zHtXAYZ-dYGi_oO+%UgA{@SWz0(!nEzeV+!zg?@a;@%|~RE;ZV`!BST7!AF&qe?55c za81q8W5??1>z$pQ{TD7Yx3-=>d-n2`EAtG7DbxM^xuESki*DT>Hpax7-pB|I4U--s K@Xr_`{rnS}lF^R< literal 0 HcmV?d00001 diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX new file mode 100644 index 0000000000000000000000000000000000000000..d9d9d15be531146383906202fe4efbdbf4e6cffb GIT binary patch literal 8885 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 UNzY2&gUb>Kre{8WWu;d6eButton.vtable->m01_Show_Enabled ((Base_Form *)&free_button->Button, __, 0); } -bool __fastcall patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value); -bool __fastcall patch_Unit_can_pillage (Unit * this, int edx, int tile_x, int tile_y); - void init_district_command_buttons () { @@ -12220,6 +12219,56 @@ compute_highlighted_worker_tiles_for_districts () } } +bool +can_build_district_on_tile (Unit * unit, Tile * tile, int district_id, enum SquareTypes base_type) +{ + if ((! is->current_config.enable_districts) || + (unit == NULL) || + (tile == NULL) || (tile == p_null_tile) || + (! is_worker (unit)) || + (tile->CityID >= 0) || + tile->vtable->m21_Check_Crates (tile, __, 0) || + tile->vtable->m20_Check_Pollution (tile, __, 0) || + (district_id < 0) || (district_id >= is->district_count)) + return false; + + struct district_config const * cfg = &is->district_configs[district_id]; + if (cfg->command == -1) + return false; + + if ((cfg->command == UCV_Build_Neighborhood) && !is->current_config.enable_neighborhood_districts) return false; + if ((cfg->command == UCV_Build_WonderDistrict) && !is->current_config.enable_wonder_districts) return false; + if ((cfg->command == UCV_Build_DistributionHub) && !is->current_config.enable_distribution_hub_districts) return false; + if ((cfg->command == UCV_Build_Aerodrome) && !is->current_config.enable_aerodrome_districts) return false; + if ((cfg->command == UCV_Build_Port) && !is->current_config.enable_port_districts) return false; + + if (! district_is_buildable_on_square_type (cfg, base_type)) + return false; + + int prereq_id = is->district_infos[district_id].advance_prereq_id; + if ((prereq_id >= 0) && !Leader_has_tech (&leaders[unit->Body.CivID], __, prereq_id)) + return false; + + struct district_instance * existing_inst = get_district_instance (tile); + int existing_district_id = (existing_inst != NULL) ? existing_inst->district_type : -1; + bool district_completed = district_is_complete (tile, existing_district_id); + + if ((existing_district_id == district_id) && district_completed) + return false; + if ((existing_district_id >= 0) && (existing_district_id != district_id) && (! district_completed)) + return false; + + if (! cfg->allow_multiple) { + FOR_TILES_AROUND(tai, is->workable_tile_count, unit->Body.X, unit->Body.Y) { + struct district_instance * nearby_inst = get_district_instance (tai.tile); + if ((nearby_inst != NULL) && (nearby_inst->district_type == district_id)) + return false; + } + } + + return true; +} + void set_up_district_buttons (Main_GUI * this) { @@ -12271,12 +12320,16 @@ set_up_district_buttons (Main_GUI * this) for (int n = 0; n < 42; n++) { if (this->Unit_Command_Buttons[n].Command == UCV_Build_Mine) { Command_Button * mine_button = &this->Unit_Command_Buttons[n]; + if (base_type == SQ_Coast) { + mine_button->Button.vtable->m02_Show_Disabled ((Base_Form *)&mine_button->Button); + break; + } if ((mine_button->Button.Base_Data.Status2 & 1) == 0) { mine_button->Button.field_5FC[13] = 0; mine_button->Button.vtable->m01_Show_Enabled ((Base_Form *)&mine_button->Button, __, 0); } break; - } + } } } } @@ -12292,30 +12345,10 @@ set_up_district_buttons (Main_GUI * this) if (is->district_configs[dc].command == -1) continue; - if ((is->district_configs[dc].command == UCV_Build_Neighborhood) && !is->current_config.enable_neighborhood_districts) continue; - if ((is->district_configs[dc].command == UCV_Build_WonderDistrict) && !is->current_config.enable_wonder_districts) continue; - if ((is->district_configs[dc].command == UCV_Build_DistributionHub) && !is->current_config.enable_distribution_hub_districts) continue; - if ((is->district_configs[dc].command == UCV_Build_Aerodrome) && !is->current_config.enable_aerodrome_districts) continue; - if (existing_district_id == dc && district_completed) continue; if ((existing_district_id >= 0) && (existing_district_id != dc) && (! district_completed)) continue; - if (!is->district_configs[dc].allow_multiple) { - bool found_same_district_nearby = false; - FOR_TILES_AROUND(tai, is->workable_tile_count, selected_unit->Body.X, selected_unit->Body.Y) { - struct district_instance * nearby_inst = get_district_instance (tai.tile); - if (nearby_inst != NULL && nearby_inst->district_type == dc) { - found_same_district_nearby = true; - break; - } - } - if (found_same_district_nearby) - continue; - } - - int prereq_id = is->district_infos[dc].advance_prereq_id; - if ((prereq_id >= 0) && !Leader_has_tech(&leaders[selected_unit->Body.CivID], __, prereq_id)) continue; - if (! district_is_buildable_on_square_type (&is->district_configs[dc], base_type)) + if (! can_build_district_on_tile (selected_unit, tile, dc, base_type)) continue; // This district should be shown @@ -12755,6 +12788,8 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) { if (is->current_config.enable_districts || is->current_config.enable_natural_wonders) { Tile * tile = tile_at (this->Body.X, this->Body.Y); + enum SquareTypes base_type = (tile != NULL && tile != p_null_tile) ? tile->vtable->m50_Get_Square_BaseType (tile) : SQ_INVALID; + if ((tile != NULL) && (tile != p_null_tile) && is->current_config.enable_natural_wonders) { struct district_instance * inst = get_district_instance (tile); if ((inst != NULL) && @@ -12764,13 +12799,16 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) return false; } } - enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); if (is_district_command (unit_command_value)) { - return (base_type != SQ_Mountains && base_type != SQ_Forest && base_type != SQ_Jungle && base_type != SQ_Swamp && base_type != SQ_Volcano); + int district_id; + if (! itable_look_up (&is->command_id_to_district_id, unit_command_value, &district_id)) + return false; + + return can_build_district_on_tile (this, tile, district_id, base_type); } else if (unit_command_value == UCV_Build_Mine) { - bool has_district = (get_district_instance (tile) != NULL); + bool has_district = (tile != NULL) && (tile != p_null_tile) && (get_district_instance (tile) != NULL); if (has_district) { return (base_type != SQ_FloodPlain && base_type != SQ_Forest && base_type != SQ_Jungle && base_type != SQ_Volcano); @@ -13651,6 +13689,19 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, { AdjacentMoveValidity base_validity = Unit_can_move_to_adjacent_tile (this, __, neighbor_index, param_2); + // Let workers step onto coast tiles when the config flag is enabled (base logic treats this as an invalid sea move) + if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + is_worker (this) && + ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { + int nx, ny; + get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); + Tile * dest = tile_at (nx, ny); + if ((dest != NULL) && + dest->vtable->m35_Check_Is_Water (dest) && + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) + base_validity = AMV_OK; + } + // Apply unit count per tile limit enum UnitTypeClasses class = p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class; if ((base_validity == AMV_OK) && (is->current_config.limit_units_per_tile[class] > 0)) { @@ -19137,6 +19188,7 @@ patch_Trade_Net_get_move_cost_after_zoc (Trade_Net * this, int edx, int from_x, patch_Trade_Net_get_movement_cost (this, __, from_x, from_y, to_x, to_y, unit, civ_id, param_7, neighbor_index, dist_info) : -1; } + AdjacentMoveValidity __fastcall patch_Unit_can_move_after_zoc (Unit * this, int edx, int neighbor_index, int param_2) { @@ -19166,11 +19218,41 @@ patch_Unit_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, bool { is->moving_unit_to_adjacent_tile = true; + bool const allow_worker_coast = is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && is_worker (this); + bool coast_override_active = false; + enum UnitStateType prev_state = this->Body.UnitState; + int prev_container = this->Body.Container_Unit; + + if (allow_worker_coast) { + int nx, ny; + get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); + Tile * dest = tile_at (nx, ny); + if ((dest != NULL) && + dest->vtable->m35_Check_Is_Water (dest) && + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) { + coast_override_active = true; + is->coast_walk_unit = this; + is->coast_walk_transport_override = false; + is->coast_walk_prev_state = prev_state; + is->coast_walk_prev_container = prev_container; + } + } + is->zoc_interceptor = is->zoc_defender = NULL; int tr = Unit_move_to_adjacent_tile (this, __, neighbor_index, param_2, param_3, param_4); if ((this == is->zoc_defender) && check_life_after_zoc (this, is->zoc_interceptor)) tr = ! is_online_game (); // This is what the original method returns when the unit was destroyed in combat + if (coast_override_active) { + is->coast_walk_unit = NULL; + is->coast_walk_transport_override = false; + + if (this->Body.Container_Unit == this->Body.ID) { + this->Body.Container_Unit = is->coast_walk_prev_container; + Unit_set_state (this, __, is->coast_walk_prev_state); + } + } + is->temporarily_disallow_lethal_zoc = false; is->moving_unit_to_adjacent_tile = false; return tr; @@ -23607,6 +23689,7 @@ get_port_district_variant_for_tile (Tile * tile) void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -24786,16 +24869,36 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t { PassBetweenValidity base = Unit_can_pass_between (this, __, from_x, from_y, to_x, to_y, param_5); - if (base != PBV_OK && is->current_config.enable_port_districts && is_worker(this)) { + if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + base != PBV_OK && is_worker(this)) { Tile * dest = tile_at (to_x, to_y); if ((dest != NULL) && dest->vtable->m35_Check_Is_Water (dest) && (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) - return PBV_OK; // Let workers treat coast as passable when port districts are on + return PBV_OK; // Let workers treat coast as passable when allow_workers_to_enter_coast is on } return base; } +Unit * __fastcall +patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool do_show_popup) +{ + Unit * transport = Unit_select_transport (this, __, tile_x, tile_y, do_show_popup); + + if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + (transport == NULL) && (this == is->coast_walk_unit) && is_worker (this)) { + Tile * dest = tile_at (tile_x, tile_y); + if ((dest != NULL) && + dest->vtable->m35_Check_Is_Water (dest) && + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) { + is->coast_walk_transport_override = true; + return this; // Fake a transport so the move logic proceeds + } + } + + return transport; +} + // TCC requires a main function be defined even though it's never used. int main () { return 0; } From 8ba663539ed63b85b4077d29c6ca70b6d4b19b2a Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 09:45:50 -0800 Subject: [PATCH 041/356] Resolve merge commits --- Art/Districts/1200/Port_NE.PCX | Bin 8885 -> 8341 bytes Art/Districts/1200/Port_NW.PCX | Bin 8117 -> 8428 bytes Art/Districts/1200/Port_SE.PCX | Bin 8885 -> 8433 bytes Art/Districts/1200/Port_SW.PCX | Bin 8885 -> 8433 bytes C3X.h | 6 +- injected_code.c | 157 +++++++++++++++++++++++++++++++-- 6 files changed, 152 insertions(+), 11 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index d9d9d15be531146383906202fe4efbdbf4e6cffb..782ef2635f96ecfb9b89b0ab9e071d40ffe27953 100644 GIT binary patch delta 1549 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 HO}2jk2Elaa delta 2286 zcmeH{T}T{P6vx?WY=cHw%{X=Ml1niAP(o$K1W9mb%B;=HAl4OyP`j(Mfws8psx=Lz z)TDu>_*5*H+CV`XBqc87AvGnhJ$Folbv14<=A+h>G)+^TD#K9dL%FwiHf>3L=xZtX za1X=G`Ja#9xpQ3O%VXn(T?;%9Uw^b%zv~9i_vcCV^Yv$z_;i2k$%}c;HrNX1%qiJ* zVSi;FTjH>s?~l;97QZ;g*-z}AaCuy^kA(G@%Wm?kiHO~*g*vq9yzqIz)`ZW)lJ%cm zw#9DosZ5_mTdj^MI-Cdb{XTmj?Dvc~l~pyjdCnN=r<$d8Xz{#Fj9NK(n^(R7bYLWt zdsVZoME)8HR>^gDco}RR$qg6-)X?--I(x5PC&RUJ{T;62&tV1J(F`@{fS!m)My}2g zqe`y7%js0GrZ1ugtpYh;E6exzDD`?HNO`UWoZOT#+}elB(hFp`N)E4b zDiq*7lZog^i3@!n-kmnHAENF8Db~vS);JWwh9&UM^bW+WC<;&|P!_-xbtOMpAi;VG zf5(2{U%YdOdQ%xg2d`R*W-%&9__cFA!1MFurLc71TlOOds?p=`2gC^eKycQs-Z?F9$CE3hi2 z@k?{!$izvDDi~oJed7i>Und{j;DSUKR#Mrd;L&4RI#AI#wY|w>0GU~DqEk}?co5+t zF(j#Zfo<>-l;ofqdM}y6FTWL}?j(4VHis>6uE19?=hM5?IWiH;#*}3;h@^w_LiRT( zQ&k32dcrzPClAf>%YU)HG^$3GH&@73oqRx)^)$Nnp7u#_$F0RDQ(pZP3P)E7?2+-t zc6v4iF~PB-@40!O>_<}gnn=Elp5LM%Lw_S@L$bWF9h!9*HZS~6tZE5?5V!pUP_tjj zan6AZ@0G*rJgB6eg5gK?&lu-aI7kgPd(4%X`gZI|+k%cq>1obw5nc{G;-VBsZPhi= zBRCg;ibi4C7oQDDd$0TVu@sR~l(4AB`K;(ku6bC}iJ!t5xBPdoM+(l13w~PMTQTuJ z^KaEjGi%iqftjK3`vDTJY~R^AJ3D7*=lq{{PBA3!1NN_5rxov~? F{{WxjxyS$j diff --git a/Art/Districts/1200/Port_SW.PCX b/Art/Districts/1200/Port_SW.PCX index d9d9d15be531146383906202fe4efbdbf4e6cffb..8a88cd34da5b6f4825c76420d256413ebfac917b 100644 GIT binary patch delta 567 zcmWMhO=}ZD7{;ivvYYI*a|nVGbcpfLE}LjAXj%zGSuz1Bg_U@STYKwTJj{Rx1wY84 z7ZH461rhhK2dN+wVt+tp+geImlGraq6ssOQ>JP~49-nWXmFs`Y%SRK*gTdQ{r4BgS zsJ1s~|62&Av)1AU6cVFD`-25JdEfj5%i7q<{a6&`QezFiX~kFygeF%sR>=7tVj_unm_vkZpFkZN+o12UuEPwyhxSxb>smT&8lMguh2`CwGC9V88{Im zU!Z;LhPH`3k`D81asao}*Yplbu4JGHBYW^VeMQ^Gn=Y42G-5ljn;zG@@dOt>qYc&v z&9L-7a#>N55o&j#WQ=J$=qNXE?1*i{ype^8B++-`+NPXjcAjj(8{-_zCrP@8f#jj& z=NWl!cA#gBkuBuPuW(m{lr_9+2hgxaHc|Sakav`*QIj?9(hgYU0^L9%L*e>L6g`uX z#VqSUo#f~`2HbUg!TrFbbMf=q7Ca`oku{7Yj{?71@aSBliuf6(Ph>_`kEr}vGv=HBUQS*J_&_)Ci}AMGCrH!#clv}%NceJ#wU~6IaGlz zvgc6U{D6;zadUx81LI^FZl8^3%ozvH)snCOI|s=dB;~~!*yM$JCE6Js{5T;o;{X4H flP^k%Gavj1@{iKw8&X0<_-S&14BO^c{Op1Na}ApE diff --git a/C3X.h b/C3X.h index 5e2e6287..efdc587a 100644 --- a/C3X.h +++ b/C3X.h @@ -673,10 +673,10 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", .advance_prereq = "Map Making", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, - .img_paths = {"Port_NW.pcx", "Port_N.pcx", "Port_NE.pcx", "Port_E.pcx", "Port_SE.pcx", "Port_S.pcx", "Port_SW.pcx", "Port_W.pcx"}, + .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .buildable_square_types_mask = (1 << SQ_Coast), - .img_path_count = 8, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 2, .gold_bonus = 2, .shield_bonus = 0, .defense_bonus_percent = 0 + .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 } }; diff --git a/injected_code.c b/injected_code.c index c8749c7f..1ec9432c 100644 --- a/injected_code.c +++ b/injected_code.c @@ -13736,7 +13736,17 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr (unit == NULL) && ((flags == 0x1009) || (flags == 0x9))) // this call can be accelerated by TNX return is->get_move_cost_for_sea_trade (this, is->tnx_cache, from_x, from_y, to_x, to_y, civ_id, flags, neighbor_index, dist_info); - int const base_cost = Trade_Net_get_movement_cost (this, __, from_x, from_y, to_x, to_y, unit, civ_id, flags, neighbor_index, dist_info); + int base_cost = Trade_Net_get_movement_cost (this, __, from_x, from_y, to_x, to_y, unit, civ_id, flags, neighbor_index, dist_info); + + // Let the pathfinder consider coastal tiles reachable for workers when the config flag is on + if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + (base_cost < 0) && (unit != NULL) && is_worker (unit)) { + Tile * dest = tile_at (to_x, to_y); + if ((dest != NULL) && + dest->vtable->m35_Check_Is_Water (dest) && + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) + base_cost = Unit_get_max_move_points (unit); + } // Apply unit count per tile limit if ((unit != NULL) && ! is_below_stack_limit (tile_at (to_x, to_y), unit->Body.CivID, p_bic_data->UnitTypes[unit->Body.UnitTypeID].Unit_Class)) @@ -23673,23 +23683,146 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i return false; } -int -get_port_district_variant_for_tile (Tile * tile) +void +get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) { - int variant = 0; + int NW = 0; + int NE = 1; + int SE = 2; + int SW = 3; int sheet_index = (tile->SquareParts >> 8) & 0xFF; - int sprite_index = tile->SquareParts & 0xFF; + int sprite_index = tile->SquareParts & 0xFF; - // TODO + char ss[200]; + snprintf (ss, sizeof ss, "Port district has sheet index %d, sprite index %d\n", sheet_index, sprite_index); + (*p_OutputDebugStringA) (ss); - return variant; + if ((tile == NULL) || (tile == p_null_tile) || (out_variant == NULL)) + return; + + int owner_id = tile->Territory_OwnerID; + if (owner_id <= 0) + return; + + struct district_instance * inst = get_district_instance (tile); + if (inst == NULL) + return; + + int tile_x = inst->tile_x; + int tile_y = inst->tile_y; + Map * map = &p_bic_data->Map; + + City * closest_city = NULL; + int closest_dx = 0, closest_dy = 0; + int city_work_radius = is->current_config.city_work_radius; + + for (int ring = 1; (ring <= city_work_radius) && (closest_city == NULL); ring++) { + int start_ni = workable_tile_counts[ring - 1]; + int end_ni = workable_tile_counts[ring]; + + for (int ni = start_ni; ni < end_ni; ni++) { + int dx, dy; + patch_ni_to_diff_for_work_area (ni, &dx, &dy); + int tx = tile_x + dx; + int ty = tile_y + dy; + wrap_tile_coords (map, &tx, &ty); + + Tile * t = tile_at (tx, ty); + if ((t == NULL) || (t == p_null_tile)) + continue; + + int city_id = t->vtable->m45_Get_City_ID (t); + City * city = get_city_ptr (city_id); + if ((city != NULL) && (city->Body.CivID == owner_id)) { + int ndx = city->Body.X - tile_x; + int ndy = city->Body.Y - tile_y; + + if (map->Flags & 1) { + int half_width = map->Width / 2; + if (ndx > half_width) + ndx -= map->Width; + else if (ndx < -half_width) + ndx += map->Width; + } + if (map->Flags & 2) { + int half_height = map->Height / 2; + if (ndy > half_height) + ndy -= map->Height; + else if (ndy < -half_height) + ndy += map->Height; + } + + closest_city = city; + closest_dx = ndx; + closest_dy = ndy; + break; + } + } + } + + if (closest_city == NULL) + return; + + // Check the four diagonal neighbors explicitly + if ((closest_dx == 1) && (closest_dy == -1)) { + *out_variant = SW; + return; + } else if ((closest_dx == -1) && (closest_dy == -1)) { + *out_variant = SE; + return; + } else if ((closest_dx == 1) && (closest_dy == 1)) { + *out_variant = NW; + return; + } else if ((closest_dx == -1) && (closest_dy == 1)) { + *out_variant = NE; + return; + } + + // Otherwise, face roughly away from the city based on its relative position + int face_dx = -closest_dx; + int face_dy = -closest_dy; + int abs_face_dx = int_abs (face_dx); + int abs_face_dy = int_abs (face_dy); + + if ((abs_face_dx == 0) && (abs_face_dy == 0)) + return; + + if (abs_face_dx > abs_face_dy) { + if (face_dx > 0) + *out_variant = (face_dy >= 0) ? 2 : 1; // city is west; face east (toward +x), bias south if city is south, else bias north + else + *out_variant = (face_dy >= 0) ? 3 : 0; // city is east; face west (toward -x), bias south if city is south, else bias north + } else if (abs_face_dy > abs_face_dx) { + if (face_dy > 0) + *out_variant = (face_dx >= 0) ? 2 : 3; // city is north; face south (toward +y), bias east if city is east, else bias west + else + *out_variant = (face_dx >= 0) ? 1 : 0; // city is south; face north (toward -y), bias east if city is east, else bias west + } else { + if (face_dx >= 0) + *out_variant = (face_dy >= 0) ? 2 : 1; // city northwest/southwest-ish; pick the closer diagonal + else + *out_variant = (face_dy >= 0) ? 3 : 0; // city northeast/southeast-ish; pick the closer diagonal + } + +finalize: + + if (out_pixel_y != NULL) { + if ((*out_variant == 0) || (*out_variant == 1)) { + *out_pixel_y += 16; // Facing north-ish, nudge up + } else if ((*out_variant == 2) || (*out_variant == 3)) { + *out_pixel_y -= 16; // Facing south-ish, nudge down + if (*out_variant == 2) + *out_pixel_x += 8; // Facing southeast, nudge right + else + *out_pixel_x -= 8; // Facing southwest, nudge left + } + } } void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -23795,6 +23928,14 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } break; } + case PORT_DISTRICT_ID: + { + get_port_district_variant_for_tile (tile, &variant, &pixel_x, &pixel_y); + char ss[200]; + snprintf (ss, sizeof ss, "Port district at tile (%d,%d) using variant %d\n", tile_x, tile_y, variant); + (*p_OutputDebugStringA) (ss); + break; + } default: { struct district_infos * info = &is->district_infos[district_id]; From 98a046661883ade5e5a893ef63d71677a4d3e03a Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 09:47:06 -0800 Subject: [PATCH 042/356] Resolve merge commits --- C3X.h | 1 + default.c3x_config.ini | 1 + injected_code.c | 280 ++++++++++++++++++++++++++++------------- 3 files changed, 197 insertions(+), 85 deletions(-) diff --git a/C3X.h b/C3X.h index efdc587a..6109633f 100644 --- a/C3X.h +++ b/C3X.h @@ -339,6 +339,7 @@ struct c3x_config { bool show_message_when_building_received_by_mutual_district; bool air_units_use_aerodrome_districts_not_cities; + bool naval_units_use_port_districts_not_cities; int maximum_pop_before_neighborhood_needed; int per_neighborhood_pop_growth_enabled; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 93a19820..71da97d0 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -846,6 +846,7 @@ show_message_when_building_received_by_mutual_district = true ; limited to Aerodromes. This adds strategic depth by requiring infrastructure for air power and making air bases visible and vulnerable on the map. ; Only applies when enable_aerodrome_districts is also set to true. air_units_use_aerodrome_districts_not_cities = true +naval_units_use_port_districts_not_cities = true ; Neighborhood district limit population growth. Once a city reaches maximum_pop_before_neighborhood_needed, it cannot ; grow further until it has at least one Neighborhood district in its work radius. Each Neighborhood then enables additional population growth equal to diff --git a/injected_code.c b/injected_code.c index 1ec9432c..95e0a190 100644 --- a/injected_code.c +++ b/injected_code.c @@ -7344,6 +7344,56 @@ clear_city_district_request (City * city, int district_id) } } +bool +can_build_district_on_tile (Tile * tile, int district_id) +{ + if ((! is->current_config.enable_districts) || + (tile == NULL) || (tile == p_null_tile) || + (tile->CityID >= 0) || + tile->vtable->m21_Check_Crates (tile, __, 0) || + tile->vtable->m20_Check_Pollution (tile, __, 0) || + (district_id < 0) || (district_id >= is->district_count)) + return false; + + struct district_config const * cfg = &is->district_configs[district_id]; + if (cfg->command == -1) + return false; + + if ((cfg->command == UCV_Build_Neighborhood) && !is->current_config.enable_neighborhood_districts) return false; + if ((cfg->command == UCV_Build_WonderDistrict) && !is->current_config.enable_wonder_districts) return false; + if ((cfg->command == UCV_Build_DistributionHub) && !is->current_config.enable_distribution_hub_districts) return false; + if ((cfg->command == UCV_Build_Aerodrome) && !is->current_config.enable_aerodrome_districts) return false; + if ((cfg->command == UCV_Build_Port) && !is->current_config.enable_port_districts) return false; + + enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); + if (! district_is_buildable_on_square_type (cfg, base_type)) + return false; + + int prereq_id = is->district_infos[district_id].advance_prereq_id; + if ((prereq_id >= 0) && !Leader_has_tech (&leaders[tile->Territory_OwnerID], __, prereq_id)) + return false; + + struct district_instance * existing_inst = get_district_instance (tile); + int existing_district_id = (existing_inst != NULL) ? existing_inst->district_type : -1; + bool district_completed = district_is_complete (tile, existing_district_id); + + if ((existing_district_id == district_id) && district_completed) + return false; + if ((existing_district_id >= 0) && (existing_district_id != district_id) && (! district_completed)) + return false; + + if (! cfg->allow_multiple) { + int x, y; + tile_coords_from_ptr (&p_bic_data->Map, tile, &x, &y); + FOR_TILES_AROUND(tai, is->workable_tile_count, x, y) { + struct district_instance * nearby_inst = get_district_instance (tai.tile); + if ((nearby_inst != NULL) && (nearby_inst->district_type == district_id)) + return false; + } + } + + return true; +} bool tile_suitable_for_district (Tile * tile, int district_id, City * city, bool * out_has_resource) @@ -7354,17 +7404,11 @@ tile_suitable_for_district (Tile * tile, int district_id, City * city, bool * ou if (out_has_resource != NULL) *out_has_resource = has_resource; - if ((tile == NULL) || (tile == p_null_tile)) - return false; - if (tile->CityID >= 0) - return false; - if (tile->vtable->m38_Get_Territory_OwnerID (tile) != city->Body.CivID) - return false; - if (tile->vtable->m35_Check_Is_Water (tile)) - return false; - enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); - if ((base_type == SQ_Mountains) || (base_type == SQ_Volcano)) - return false; + if ((tile == NULL) || (tile == p_null_tile)) return false; + if (tile->CityID >= 0) return false; + if (tile->vtable->m38_Get_Territory_OwnerID (tile) != city->Body.CivID) return false; + if (! can_build_district_on_tile (tile, district_id)) return false; + struct district_instance * inst = get_district_instance (tile); if (inst != NULL) { if (inst->district_type != district_id) @@ -7836,19 +7880,14 @@ tile_has_friendly_aerodrome_district (Tile * tile, int civ_id, bool require_avai if (! is->current_config.enable_districts || ! is->current_config.enable_aerodrome_districts || (tile == NULL) || - (tile == p_null_tile) || - (civ_id < 0) || (civ_id >= 32)) - return false; - - int aerodrome_id = AERODROME_DISTRICT_ID; - if (aerodrome_id < 0) + (tile == p_null_tile)) return false; struct district_instance * inst = get_district_instance (tile); - if (inst == NULL || inst->district_type != aerodrome_id) + if (inst == NULL || inst->district_type != AERODROME_DISTRICT_ID) return false; - if (! district_is_complete (tile, aerodrome_id)) + if (! district_is_complete (tile, AERODROME_DISTRICT_ID)) return false; int territory_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); @@ -7865,6 +7904,25 @@ tile_has_friendly_aerodrome_district (Tile * tile, int civ_id, bool require_avai return true; } +bool +tile_has_friendly_port_district (Tile * tile, int civ_id) +{ + if (! is->current_config.enable_districts || + ! is->current_config.enable_port_districts || + ! is->current_config.naval_units_use_port_districts_not_cities || + (tile == NULL) || (tile == p_null_tile)) + return false; + + struct district_instance * inst = get_district_instance (tile); + if ((inst == NULL) || (inst->district_type != PORT_DISTRICT_ID)) + return false; + + if (! district_is_complete (tile, PORT_DISTRICT_ID)) + return false; + + return tile->vtable->m38_Get_Territory_OwnerID (tile) == civ_id; +} + bool city_has_required_district (City * city, int district_id) { @@ -10987,6 +11045,7 @@ patch_init_floating_point () {"cities_with_mutual_district_receive_wonders" , false, offsetof (struct c3x_config, cities_with_mutual_district_receive_wonders)}, {"show_message_when_building_received_by_mutual_district", false, offsetof (struct c3x_config, show_message_when_building_received_by_mutual_district)}, {"air_units_use_aerodrome_districts_not_cities" , false, offsetof (struct c3x_config, air_units_use_aerodrome_districts_not_cities)}, + {"naval_units_use_port_districts_not_cities" , false, offsetof (struct c3x_config, naval_units_use_port_districts_not_cities)}, {"show_natural_wonder_name_on_map" , false, offsetof (struct c3x_config, show_natural_wonder_name_on_map)}, {"ai_defends_districts" , false, offsetof (struct c3x_config, ai_defends_districts)}, {"enable_city_work_radii_highlights" , false, offsetof (struct c3x_config, enable_city_work_radii_highlights)}, @@ -12219,56 +12278,6 @@ compute_highlighted_worker_tiles_for_districts () } } -bool -can_build_district_on_tile (Unit * unit, Tile * tile, int district_id, enum SquareTypes base_type) -{ - if ((! is->current_config.enable_districts) || - (unit == NULL) || - (tile == NULL) || (tile == p_null_tile) || - (! is_worker (unit)) || - (tile->CityID >= 0) || - tile->vtable->m21_Check_Crates (tile, __, 0) || - tile->vtable->m20_Check_Pollution (tile, __, 0) || - (district_id < 0) || (district_id >= is->district_count)) - return false; - - struct district_config const * cfg = &is->district_configs[district_id]; - if (cfg->command == -1) - return false; - - if ((cfg->command == UCV_Build_Neighborhood) && !is->current_config.enable_neighborhood_districts) return false; - if ((cfg->command == UCV_Build_WonderDistrict) && !is->current_config.enable_wonder_districts) return false; - if ((cfg->command == UCV_Build_DistributionHub) && !is->current_config.enable_distribution_hub_districts) return false; - if ((cfg->command == UCV_Build_Aerodrome) && !is->current_config.enable_aerodrome_districts) return false; - if ((cfg->command == UCV_Build_Port) && !is->current_config.enable_port_districts) return false; - - if (! district_is_buildable_on_square_type (cfg, base_type)) - return false; - - int prereq_id = is->district_infos[district_id].advance_prereq_id; - if ((prereq_id >= 0) && !Leader_has_tech (&leaders[unit->Body.CivID], __, prereq_id)) - return false; - - struct district_instance * existing_inst = get_district_instance (tile); - int existing_district_id = (existing_inst != NULL) ? existing_inst->district_type : -1; - bool district_completed = district_is_complete (tile, existing_district_id); - - if ((existing_district_id == district_id) && district_completed) - return false; - if ((existing_district_id >= 0) && (existing_district_id != district_id) && (! district_completed)) - return false; - - if (! cfg->allow_multiple) { - FOR_TILES_AROUND(tai, is->workable_tile_count, unit->Body.X, unit->Body.Y) { - struct district_instance * nearby_inst = get_district_instance (tai.tile); - if ((nearby_inst != NULL) && (nearby_inst->district_type == district_id)) - return false; - } - } - - return true; -} - void set_up_district_buttons (Main_GUI * this) { @@ -12348,7 +12357,7 @@ set_up_district_buttons (Main_GUI * this) if (existing_district_id == dc && district_completed) continue; if ((existing_district_id >= 0) && (existing_district_id != dc) && (! district_completed)) continue; - if (! can_build_district_on_tile (selected_unit, tile, dc, base_type)) + if (! can_build_district_on_tile (tile, dc)) continue; // This district should be shown @@ -12805,7 +12814,7 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) if (! itable_look_up (&is->command_id_to_district_id, unit_command_value, &district_id)) return false; - return can_build_district_on_tile (this, tile, district_id, base_type); + return is_worker (this) && can_build_district_on_tile (tile, district_id); } else if (unit_command_value == UCV_Build_Mine) { bool has_district = (tile != NULL) && (tile != p_null_tile) && (get_district_instance (tile) != NULL); @@ -13702,6 +13711,18 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, base_validity = AMV_OK; } + if ((base_validity == AMV_OK) && + is->current_config.enable_districts && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea)) { + int nx, ny; + get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); + Tile * dest = tile_at (nx, ny); + if ((dest != NULL) && (dest != p_null_tile) && Tile_has_city (dest)) + return AMV_INVALID_SEA_MOVE; + } + // Apply unit count per tile limit enum UnitTypeClasses class = p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class; if ((base_validity == AMV_OK) && (is->current_config.limit_units_per_tile[class] > 0)) { @@ -13748,6 +13769,17 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr base_cost = Unit_get_max_move_points (unit); } + if ((unit != NULL) && + (base_cost >= 0) && + is->current_config.enable_districts && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities && + (p_bic_data->UnitTypes[unit->Body.UnitTypeID].Unit_Class == UTC_Sea)) { + Tile * dest = tile_at (to_x, to_y); + if ((dest != NULL) && (dest != p_null_tile) && Tile_has_city (dest)) + return -1; + } + // Apply unit count per tile limit if ((unit != NULL) && ! is_below_stack_limit (tile_at (to_x, to_y), unit->Body.CivID, p_bic_data->UnitTypes[unit->Body.UnitTypeID].Unit_Class)) return -1; @@ -14879,9 +14911,15 @@ patch_City_can_build_unit (City * this, int edx, int unit_type_id, bool exclude_ is->current_config.enable_aerodrome_districts && is->current_config.air_units_use_aerodrome_districts_not_cities) { UnitType * type = &p_bic_data->UnitTypes[unit_type_id]; - int aerodrome_id = AERODROME_DISTRICT_ID; - if ((type->Unit_Class == UTC_Air) && (aerodrome_id >= 0) && - ! city_has_required_district (this, aerodrome_id)) + if (type->Unit_Class == UTC_Air && ! city_has_required_district (this, AERODROME_DISTRICT_ID)) + return false; + } + + if (is->current_config.enable_districts && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities) { + UnitType * type = &p_bic_data->UnitTypes[unit_type_id]; + if (type->Unit_Class == UTC_Sea && ! city_has_required_district (this, PORT_DISTRICT_ID)) return false; } } @@ -14894,8 +14932,25 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply { // First defer to the base game's logic bool base = City_can_build_improvement (this, __, i_improv, apply_strict_rules); - if (! base) return false; - if (! is->current_config.enable_districts) return base; + + if (! is->current_config.enable_districts) + return base; + + if (! base) { + // Allow harbor-like improvements when port districts replace coastal adjacency + Improvement * improv = &p_bic_data->Improvements[i_improv]; + if (improv->ImprovementFlags & ITF_Allows_Water_Trade && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities) { + int tx, ty; + + // If the city has no coastal tiles within its workable area, disallow + if (find_tile_for_district (this, PORT_DISTRICT_ID, &tx, &ty) == NULL) + return false; + + // Else proceed with standard improvement checks + } + } // Different logic for human vs AI players bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; @@ -20182,15 +20237,31 @@ patch_Leader_spawn_unit (Leader * this, int edx, int type_id, int tile_x, int ti int spawn_x = tile_x, spawn_y = tile_y; - if (is->current_config.enable_districts && - is->current_config.air_units_use_aerodrome_districts_not_cities) { + if (is->current_config.enable_districts) { UnitType * type = &p_bic_data->UnitTypes[type_id]; - int aerodrome_id = AERODROME_DISTRICT_ID; - if ((type->Unit_Class == UTC_Air) && (aerodrome_id >= 0)) { + if (is->current_config.air_units_use_aerodrome_districts_not_cities) { + if (type->Unit_Class == UTC_Air) { + City * spawn_city = city_at (tile_x, tile_y); + if ((spawn_city != NULL) && (spawn_city->Body.CivID == this->ID)) { + int district_x, district_y; + Tile * district_tile = get_completed_district_tile_for_city (spawn_city, AERODROME_DISTRICT_ID, &district_x, &district_y); + if ((district_tile != NULL) && (district_tile != p_null_tile) && + (district_tile->Territory_OwnerID == this->ID) && + is_below_stack_limit (district_tile, this->ID, type->Unit_Class)) { + spawn_x = district_x; + spawn_y = district_y; + } + } + } + } + + if ((type->Unit_Class == UTC_Sea) && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities) { City * spawn_city = city_at (tile_x, tile_y); if ((spawn_city != NULL) && (spawn_city->Body.CivID == this->ID)) { int district_x, district_y; - Tile * district_tile = get_completed_district_tile_for_city (spawn_city, aerodrome_id, &district_x, &district_y); + Tile * district_tile = get_completed_district_tile_for_city (spawn_city, PORT_DISTRICT_ID, &district_x, &district_y); if ((district_tile != NULL) && (district_tile != p_null_tile) && (district_tile->Territory_OwnerID == this->ID) && is_below_stack_limit (district_tile, this->ID, type->Unit_Class)) { @@ -21698,6 +21769,46 @@ patch_Tile_m7_Check_Barbarian_Camp (Tile * this, int edx, int visible_to_civ) return Tile_m7_Check_Barbarian_Camp (this, __, visible_to_civ); } +bool __fastcall +patch_Unit_can_heal_at (Unit * this, int edx, int tile_x, int tile_y) +{ + if (is->current_config.enable_districts && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea)) { + Tile * tile = tile_at (tile_x, tile_y); + if (tile_has_friendly_port_district (tile, this->Body.CivID)) { + int occupier_id = get_tile_occupier_id (tile_x, tile_y, -1, true); + return (occupier_id == -1) || (occupier_id == this->Body.CivID); + } + } + + return Unit_can_heal_at (this, __, tile_x, tile_y); +} + +void __fastcall +patch_Unit_heal_at_start_of_turn (Unit * this) +{ + bool healed_in_port = false; + + if (is->current_config.enable_districts && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea)) { + Tile * tile = tile_at (this->Body.X, this->Body.Y); + if (tile_has_friendly_port_district (tile, this->Body.CivID)) + healed_in_port = true; + } + + Unit_heal_at_start_of_turn (this); + + if (healed_in_port && (this->Body.Damage > 0)) { + int heal_amt = Unit_get_max_hp (this) - 1; + int new_damage = this->Body.Damage - heal_amt; + this->Body.Damage = (new_damage < 0) ? 0 : new_damage; + } +} + bool __fastcall patch_Unit_can_airdrop (Unit * this) { @@ -22141,9 +22252,8 @@ patch_Unit_check_rebase_target (Unit * this, int edx, int tile_x, int tile_y) struct district_instance * inst = get_district_instance (tile); if (inst != NULL) { int district_id = inst->district_type; - int aerodrome_id = AERODROME_DISTRICT_ID; // Check if this is an aerodrome district owned by this unit's civ - if ((aerodrome_id >= 0) && (district_id == aerodrome_id) && (tile->Territory_OwnerID == this->Body.CivID)) { + if ((district_id == AERODROME_DISTRICT_ID) && (tile->Territory_OwnerID == this->Body.CivID)) { // Check if aerodrome is complete if (district_is_complete (tile, district_id)) { // Perform range check @@ -23823,6 +23933,7 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -23862,8 +23973,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par // Districts if (is->current_config.enable_districts) { - if (district_id < 0 || district_id >= is->district_count) - return; + if (district_id < 0 || district_id >= is->district_count) return; bool completed = district_is_complete (tile, district_id); if (! completed) From 01a09c14ebf75433f9c3a4733f71e4cf3652db70 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 09:47:42 -0800 Subject: [PATCH 043/356] Resolve merge commits --- C3X.h | 2 +- injected_code.c | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/C3X.h b/C3X.h index 6109633f..ddac0b21 100644 --- a/C3X.h +++ b/C3X.h @@ -674,7 +674,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", .advance_prereq = "Map Making", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, - .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, + .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .dependent_improvements = {"Harbor", "Commercial Dock"}, .buildable_square_types_mask = (1 << SQ_Coast), .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 diff --git a/injected_code.c b/injected_code.c index 95e0a190..c3b24949 100644 --- a/injected_code.c +++ b/injected_code.c @@ -14949,6 +14949,8 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply return false; // Else proceed with standard improvement checks + } else { + return base; } } @@ -16017,6 +16019,7 @@ patch_PCX_Image_draw_tile_info_terrain (PCX_Image * this, int edx, char * str, i if (is->current_config.enable_districts || is->current_config.enable_natural_wonders) { // Draw district name to the right of terrain name if tile has one struct district_instance * dist = get_district_instance (tile); + if (dist != NULL) { show_district_name = true; char const * display_name = is->district_configs[dist->district_type].name; @@ -16040,7 +16043,11 @@ patch_PCX_Image_draw_tile_info_terrain (PCX_Image * this, int edx, char * str, i } } - snprintf (s, sizeof s, "%s", display_name); + // Show sprites indexes for debugging + int sheet_index = (tile->SquareParts >> 8) & 0xFF; + int sprite_index = tile->SquareParts & 0xFF; + + snprintf (s, sizeof s, "%s, (%d, %d)", display_name, sheet_index, sprite_index); PCX_Image_draw_text (this, __, s, x + 68, y, strlen (s)); } } @@ -23933,7 +23940,7 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - *p_debug_mode_bits |= 0xC; + //*p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; From bf50ab51142eee3748fd1097047887f290fb5ef8 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 27 Nov 2025 08:43:57 -0800 Subject: [PATCH 044/356] Update get_port_district_variant_for_tile to include more detailed variables on surrounding tiles; Will use for more detailed offsets and variant selection --- injected_code.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/injected_code.c b/injected_code.c index c3b24949..a1a40411 100644 --- a/injected_code.c +++ b/injected_code.c @@ -23800,6 +23800,15 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i return false; } +bool +tile_offset_is_land (int adj_x, int adj_y) +{ + Map * map = &p_bic_data->Map; + wrap_tile_coords (map, &adj_x, &adj_y); + Tile * adj_tile = tile_at (adj_x, adj_y); + return (adj_tile != NULL) && (adj_tile != p_null_tile) && (! adj_tile->vtable->m35_Check_Is_Water (adj_tile)); +} + void get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) { @@ -23811,10 +23820,6 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi int sheet_index = (tile->SquareParts >> 8) & 0xFF; int sprite_index = tile->SquareParts & 0xFF; - char ss[200]; - snprintf (ss, sizeof ss, "Port district has sheet index %d, sprite index %d\n", sheet_index, sprite_index); - (*p_OutputDebugStringA) (ss); - if ((tile == NULL) || (tile == p_null_tile) || (out_variant == NULL)) return; @@ -23896,6 +23901,19 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi return; } + bool city_is_west_of_port = (closest_dx < 0); + bool city_is_north_of_port = (closest_dy < 0); + bool city_is_directly_above_port = (closest_dx == 0) && (closest_dy < 0); + bool city_is_directly_below_port = (closest_dx == 0) && (closest_dy > 0); + bool northwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y - 1); + bool north_tile_is_land = tile_offset_is_land (tile_x, tile_y - 1); + bool northeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y - 1); + bool east_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y); + bool southeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y + 1); + bool south_tile_is_land = tile_offset_is_land (tile_x, tile_y + 1); + bool southwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y + 1); + bool west_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y); + // Otherwise, face roughly away from the city based on its relative position int face_dx = -closest_dx; int face_dy = -closest_dy; From 2c76ae766bcdb4fa2ef458b56077a547559d8b87 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 09:48:37 -0800 Subject: [PATCH 045/356] Resolve merge commits --- Art/Districts/1200/Port_NE.PCX | Bin 8341 -> 8344 bytes Art/Districts/1200/Port_NW.PCX | Bin 8428 -> 8420 bytes Art/Districts/1200/Port_SE.PCX | Bin 8433 -> 8433 bytes Art/Districts/1200/Port_SW.PCX | Bin 8433 -> 8377 bytes C3X.h | 2 +- default.c3x_config.ini | 2 +- injected_code.c | 13 +++++++------ 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index 782ef2635f96ecfb9b89b0ab9e071d40ffe27953..43960159456ebf8b9ee0e89ff9a78364ec720fcc 100644 GIT binary patch delta 1086 zcmW+#T}T{P7-izRt47_~jMM8nZ3Z*lSOT5SZl6NN1%Dov8EG367t*woU4=e$aJ9_E zLi>TNbSg_J21*h8UhbVWiBYqzNFr@$wLfW-Y(y*ct@rlsz8vQJ&iQ`k z%r`d%76z`*u9VxtLd!P&YI13Z9-zfm`Q#SG|4jN9Rby@oRifaPDxHM=Q+kE|%NSj6 z;6?-Qar~23x!fBtolWFN!MQ5+J!op{lyY&t> z)Fg+eenZn?SzM*}m<@*s)I=%6*dJ(L0fLTbwH!-V_|ED!N{X_4NHLz|K!j zro^((XT(?V0>*KI{C0CJiE!&gS=1+S405&;(-5ZN3i%)VTp&J&r!a*h;P8|aN{D+9 zrkEgz(PgbvGbL(vdr##vgB4twCVI(A+Y8#W`9;9m;e@Ohp15FWrh zI^1@`4W<@C7=Z;cT=jW{_yF#~BeZgs-D`5K0pT({A;W~vG2%VA3ya8|-dsmvWrIO@ zM*67FVPfN5xC2XQ6`T$&lOV$-cu9sKp991@@E1Hq$Kp`ULzU~dj z>UEm5L_=tp?UbbT*8tJOoMYu3Yl{{-rjM}Es=APe7I#~%l${r~WX3EpU6eO2U{r|2 z$I)@~wzHqxZKAFvJH@fSYFV8}Z!x0B%ofv#m(X_dcFN^;G^1;wqpC)o2PLgU&3VQ4 zjLJ>JRCdKiF=wSZT-$PUy4XcMww2k%I$2%7gQs~i)51+PtHgpOEI3vn=bDj>*p)OV zS37lLsQ3zDahftzXpe#WUX0nPt`(kcBc^uG}pA$4&&K3J)e&Eci|ZbxUhm7-!!F z!vc_Rpj_0&BvgQoVsP{kg1p&0D1NzG}&wr10UrpMP9a6$$K6gbG3BN_+Cu@igf; zOZPraW4gk-J}2pa8^C;bJ|0dRv8<{D6!p{7yFEPjnjKQ)TGDFXZ{C+yRP%=(v!cZ~ z+V7QL(h#jk-V&8M`jbJo(Xu0;MwG6T%QSA%rfC=?ahstA+BKz9>Dryl9OU@@Zs`%t zr(8$LId8$63v(sDL&P1w?UnFcQ+j_oQc#Mj5|2FWtCvLz%}4jW z=Q?R_s0y7*eZVFpIaEZ?>n@_Zvne8|x^kZx_41m_xYKrxmiv9Ra&QQL{kG;FOV!JP XLEJWb!KgheTsT(Kx9Y-FwYUC%uo2Np diff --git a/Art/Districts/1200/Port_NW.PCX b/Art/Districts/1200/Port_NW.PCX index 12d0be1bba53605e30da3dfa20ea83651d8a95a6..d0d5aa10285402a249c7a76a535555366202943e 100644 GIT binary patch delta 1160 zcmZvaT}&KR6vwkJ-@CK}v%_QyW=d`=w9(1zK51B1A^Wh|*(kJSlSaE;mKT!^A9b$v z!6q8ANr_@&a+9g9iGx{_0xv%J;FBhE2bNN_fV4$JVEJgpT5J*0RAarz%j#oae)rsa zaz5_=oSlw49Us3>7N0#rzLno3pL)Kmm}j5&Pdp*7cqPvqv-kR~ZbcjUlZ@3%Ro7WR zZXZ-1lOeBEeT_Sfj!^OuS*Vw*3d}Na2W>&0_BAOoD%K-X|wx~qcD0g(5Ph2*l>yL@K&<89YtG&r-wM~9*lpOqe_{hR9&8= zFfjZpSXj?v{W&Cjq~3r6BYB_i`E9T%Jsfb97*$-=BWI{$-?Z0zmSNH^TnC++5j|z( zMU>3MzUN<&+%s&8WXhWAuK_mm20l-XxTvCJTAbwy$gWNDRhc3dz`%JUWl|%UQIRmB zpaNG+a?i01vR`&WoB~TvB}_v%EmiE}Q;1J-nPt~HdAaC)OJd;{I22e66cx zX$9)EbaF6o%cpc4tp$(G@Lc@zoa5;Q@u!n{Njolwi@AyIa7l;u`EyQ7g**9XWojq&rXR4_9OO~Ex;%C K*JCyDhJOJGLwoE1 delta 1177 zcmZvbUrZcD9LKXg{#?a!dtH|e(70(P72DW#_udT0MaVr|ZZ9Zpxuj9A$MJaKPu#IS zv}r0iQ=*tyCfS-Zabxa5;Gx#=VB(WIOKE{S{uhL!&_XrERzRAzu{1Ld8y|i7Wac-& z@BHTXXC5>@Zv6Uk*byep&wHgqY=sM0STTw#Ys zV=(a>y6zrX1`L7)w8~3LkwBb;d_*`qOBN_4k6b=>`6uI3vP=f?g|Hg@6r`uobzVAI zB&OA-oCSf?lVp`iHe5b%xz6}EDH1(t=3)`>LmpjngT!@c@&W$zIQfGqiY`}NE;Ihc zg1|5|azLm%dK)$K(%Z~{4S1|B3iwg7#WZs+=UvV+ZZeH-=|(1sLEQ*?otNHPCKzU{ zxC3}ho-xUU%e>2R#^3iP4vd3zgh$)HDkgwBh8E@l|C0GTgfNb{#Ekcu|9eRt4hjw+ z=vSXux&JENFoBQ&l-lUVtNSJ$hA{-Z5TVpT)1v5GA~ucVq!o`i%1O+5gLV;#6?K`j4%IPyUy@+<`I!k4ZZn(Vfa)wNiThxF# zBWYqIkX8WSD}y{8_%e&-5=Zu48N#%)XB+KQidun;QAT44B1T~hu$t|KAC8EbBPtnyfrFw~CmLsdbOZz|COv z=6H{C6RcVi_X6#Ck8pX^EK1@FTHBdWx>Mb$^eG_M20uu?KQzhuCW+7KDOpW*Ce&bO zYqTvCfE~@*0&BKdvq+v$znV&PX*CxUDLMSH-}rf8a3Jr_h#)SJ9r}*iqeNxFo`b}Xt XRyD&7FZufq4F6d33pae%SML88esh3r diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index d5ac5dbec945ad46e1b5282a2c9135fe4079e954..88698227cdb99a2d0f61246639c8d509d4f961b3 100644 GIT binary patch delta 852 zcmX9)O=uHA7-jq8Ut?XnSxeo@TE`ltW!Xd!#WWJ_A!Gx#sDU1gi3J5?{M$i65fyqU zh#-8h9t2m-K`Qi6DhQsO*;-Q@D^;wZqNpgfrqNid2VvIPdYH$@dvD(N_$tv#^y z&)ALJz-RWtby=A`=lEj*z4;G&%E-^0-L2~VAJ{9d&9Etsr3Linc{agvxZ}81WOtvl zknozTUzmr}3+T;r+(Ukv(Qv8=y0C7^1hY*XM&qzYXsdh5Fim4ICiN8Hf)Ml-*#JG) zsfW*?>|+=bws3=GXS5y^+EajOVLhB`@N>>cTlqu`-6_C^Dg--y9+xz*C5A-qJ5;q6 zZa4UO81BSo9!Y_g0^F=x1E+lcR!&kAw+7-!e9TDYe2zxxk;wk;R`Y&XN$gA9-L77n zg~ZHy?2A(r7zJN-3pA|q*A*GXeRk48;wU7lRbd8pG|QYd9Gpbim^cELs{_JU z=w9jtV1r_tib$j|S-nyC1{I&YtjKn!4Ll$@2%2g%p#&AhOTg4HEhME79Igp+>rm4| z1>8w75~Ub{2Q{0yIk>_zCu!PF#vDXpW$h+j;+FJ(T_5g1v4H46s3DfXhlJx?^R^G%1sF{#7lsYKA zbNi`#pHxVRlu4<5suBs7phc0}A2G+Zjdap74a?S~UVi1oC-)wCOQy(dX#Z9VXQ-|!Dpk}n zTw2|zj=Byu9NUTqvr=y={>3FEH!^2+bp!=fQ!0?Dml0uOu94ZUC+vJy3-hy0^TdZ_ zj2E99>@qEV*F2o`H!9<9NR3D_DJ#a%H+RUB`6~~eU-_Hsa+AZJxSAg6)x+AgS(i)_ lhrGQ`ireP-mZrtTW6HmHD|z^8{a#`CMDs;qxVi47{101crMUnA delta 917 zcmW-eUuYav6vmm|O6tt9`Ygt4vY^%N7)C>Lxoa7=#%$Ol4jF1G~FZ{X<}%_{;}KDq*|rwy`JpD{5ah2 zn|sbZ-%e&HGks^@U$Ul@Y{KCqq^pkAuW}vPXmPLpQ*Og)ty|lMSGD8XPw=7Et8Kxz znyqcZL+u5u&xD?fFUt#KGfE2C#GhANyor1uw_rR(k1k5SJLbQhNKO4GchPa9#bx9@ zbX*D@i&y0s>mRrNl}}J!YH<kbZFvjc4);WbWTMFQEIIoJ*fFBrf^Wi!jt6Q+d@`6ye?ZH-km)Ack8nSn zjNVh8U$V~9zLDRdhY+m}cH|VEIz_KCl%-!cVd4-8tx5POVja7y@(%O#K7FKtt!wJH z79X`(muOmu*wH(R4KlYvNwNo~jm~gQ24J>5-*HER9KGpE9wlT`oAs-eMvFFPY~zQO~(`2}Bkv92894LU`) zLTI6!TfsiDBV3`OSy#-fxa^#8hloWB=gBh8Xu$Nr2-#3BtGGo^xP4`d>Qf1N4IdUG z>&kbEdCzq`-y)-!#!waaV!*H&(brVKoWS>TcA1P8a#wM|F@04zrI%7eex+=saSfQJ r89T75`Vs>qOUind&S7w|XiZtEfzedj`g|{5j zQk6uJeBcs^Y9yzptk5cLuzG1D744zIU&~lo~Ov7FXbMZi>QnxJfDO)mUNt2mXQuIs>>= zR0AXxOI;mb0$j&dj8n+m$;J_-)%eqQpTC6*kVp>x-q7uLw6K>YH z!RSECE>qUGV=|)W@*l=*ZJ@Xa22l+z!JqmUjrsZ18g=8{xAPkLIdk+Ofo7IVB?;|k*wc+7mm;+J>{J7;r5CL{5IR6w6zgo3FBPe~mopP@-V|NcEn ztj1&S;3{_V{c=7x7>GA&B~a9Y)s>LG+JhbDTdHw^aT^}MIz3CO{Q)6#@E9)VEqN?5 zD7v$YkXDQW*TJ2vpm{&<+CX2w(44phMyH`$65rPP6rM4wpKF|B`~!Z6O^WBSjeo(_ z^Zg?!Eh@N3$mMp4MCi?2y2%Eh8-cc@#O)y05k9(2zy0)0Qnhk&99O-e=%^@o1o5LU z`Y;sdKrt0PpCnRS8Gsd>djX|P!@j)pw#Fvo6I&)CwfSL?PO*y@hwKtWr}^(*<2Am_dG+&Atnx zR>AlB4^01FuNWE{&9H0gOKR~B>`Z7MtW(=iY_pIIXZhhw(nc4Wo7hkrUi5PtRM8@# zF4M{eqiirgBF*cQFznIL@$kpTli|?mp*Wt!RYJ}^pdDhIPw-hTAyL$6-+L(Kxvx>L-FBIGTtY0(2@`$OysyCSh6buCNrpM{c|osvRH zpdgBWTAU75`<11Lwy{16t)Zr$Giy7xBiWW8vK!onq)%FrL=mC+D04e@J22(ROI@G0wy9c3tz6<|*>k;UvL6jMcDkJ&Z=dvc7AIR4F^u`QTS1<`HZ{fkw3 zw|0zwa(u=byi0bADGyDA6Srr9{m$<3kw1>{H;xTf=QN8G9y1s|H5{HV@|lO&-7DJn zppczR(_nhIKxc{s*B!EbA97JWMZ@Xva5zykF|o>yeXyPjZr_5;v@qyKf%AobjMG?$(9L>%c7c6UlI?XxTjU-jMLZ@5ky;~uBKqo34r zh3HIh($$<}m?A&sW2C*t%cIOU}4g^YUTBj?2uPIO8q_L-rh zkYci*@L|(wOBuUQOWrC&G=+qicQOHIkq+#=iuDF~nPS;zXjS?u{#g3EG zD|>97zBl9#T~jg-fzUwzksRNT`YZt&NjqKY2XCv*gZ|lhQYqoI{fIqpqnrlssiYou86XA14dFBnioxF7oTW5W4Vt%?~!cFJf4VZ zdR`He$~GQp>$N<`^>)c-@=$7Mh$2N0V_SHxO|txhT|KgO7shDH7>k+82Ht9ungIyT zw#%KnTsDWoO3W*iaaVh91b_l%z?XT9UXGA8 z(xCJPKf<-7mzP-?G@2jMQhGsDk{PuG+|ODd7zxKmSd_hN( zRF8{3C3#uo^EEDL`k{J zR>06wV|iW68$NN0`w}n14W6kQ-*om!^`=ekUIr6srjb_z8F4D90_<>qq@GRgvz89} z<3=Il3-NCR*ye`H_9f_ZMZ}nx5n>8Jg?lgAcbEx2e`LxZ@_h=h#e=f2@I-2ATKL-W Lanr){F4FxkEc{{} diff --git a/C3X.h b/C3X.h index ddac0b21..0548964e 100644 --- a/C3X.h +++ b/C3X.h @@ -352,7 +352,7 @@ struct c3x_config { int distribution_hub_shield_yield_divisor; int ai_ideal_distribution_hub_count_per_100_cities; - bool allow_workers_to_enter_coast; + bool workers_can_enter_coast; bool ai_defends_districts; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 71da97d0..1adfc302 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -877,7 +877,7 @@ distribution_hub_food_yield_divisor = 3 distribution_hub_shield_yield_divisor = 4 ai_ideal_distribution_hub_count_per_100_cities = 25 -allow_workers_to_enter_coast = true +workers_can_enter_coast = true ; When enabled, AI defensive units will actively seek out and defend districts within their territory, treating them as valuable assets like colonies. ; The AI prioritizes defending Wonder districts (if destructible wonders are enabled) over regular districts, searching within a 20-tile radius for diff --git a/injected_code.c b/injected_code.c index a1a40411..ddd56b0a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -11048,6 +11048,7 @@ patch_init_floating_point () {"naval_units_use_port_districts_not_cities" , false, offsetof (struct c3x_config, naval_units_use_port_districts_not_cities)}, {"show_natural_wonder_name_on_map" , false, offsetof (struct c3x_config, show_natural_wonder_name_on_map)}, {"ai_defends_districts" , false, offsetof (struct c3x_config, ai_defends_districts)}, + {"workers_can_enter_coast" , false, offsetof (struct c3x_config, workers_can_enter_coast)}, {"enable_city_work_radii_highlights" , false, offsetof (struct c3x_config, enable_city_work_radii_highlights)}, {"introduce_all_human_players_at_start_of_hotseat_game" , false, offsetof (struct c3x_config, introduce_all_human_players_at_start_of_hotseat_game)}, {"allow_unload_from_army" , false, offsetof (struct c3x_config, allow_unload_from_army)}, @@ -13699,7 +13700,7 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, AdjacentMoveValidity base_validity = Unit_can_move_to_adjacent_tile (this, __, neighbor_index, param_2); // Let workers step onto coast tiles when the config flag is enabled (base logic treats this as an invalid sea move) - if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + if (is->current_config.enable_districts && is->current_config.workers_can_enter_coast && is_worker (this) && ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { int nx, ny; @@ -13760,7 +13761,7 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr int base_cost = Trade_Net_get_movement_cost (this, __, from_x, from_y, to_x, to_y, unit, civ_id, flags, neighbor_index, dist_info); // Let the pathfinder consider coastal tiles reachable for workers when the config flag is on - if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + if (is->current_config.enable_districts && is->current_config.workers_can_enter_coast && (base_cost < 0) && (unit != NULL) && is_worker (unit)) { Tile * dest = tile_at (to_x, to_y); if ((dest != NULL) && @@ -19290,7 +19291,7 @@ patch_Unit_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, bool { is->moving_unit_to_adjacent_tile = true; - bool const allow_worker_coast = is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && is_worker (this); + bool const allow_worker_coast = is->current_config.enable_districts && is->current_config.workers_can_enter_coast && is_worker (this); bool coast_override_active = false; enum UnitStateType prev_state = this->Body.UnitState; int prev_container = this->Body.Container_Unit; @@ -25145,13 +25146,13 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t { PassBetweenValidity base = Unit_can_pass_between (this, __, from_x, from_y, to_x, to_y, param_5); - if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + if (is->current_config.enable_districts && is->current_config.workers_can_enter_coast && base != PBV_OK && is_worker(this)) { Tile * dest = tile_at (to_x, to_y); if ((dest != NULL) && dest->vtable->m35_Check_Is_Water (dest) && (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) - return PBV_OK; // Let workers treat coast as passable when allow_workers_to_enter_coast is on + return PBV_OK; // Let workers treat coast as passable when workers_can_enter_coast is on } return base; @@ -25162,7 +25163,7 @@ patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool { Unit * transport = Unit_select_transport (this, __, tile_x, tile_y, do_show_popup); - if (is->current_config.enable_districts && is->current_config.allow_workers_to_enter_coast && + if (is->current_config.enable_districts && is->current_config.workers_can_enter_coast && (transport == NULL) && (this == is->coast_walk_unit) && is_worker (this)) { Tile * dest = tile_at (tile_x, tile_y); if ((dest != NULL) && From 5c65d0f4b8d22bd28a0c93f3937afcd1858710d4 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 27 Nov 2025 15:02:37 -0800 Subject: [PATCH 046/356] Working through port angle & pixel offset logic --- injected_code.c | 169 +++++++++++++++++++++++++++--------------------- 1 file changed, 94 insertions(+), 75 deletions(-) diff --git a/injected_code.c b/injected_code.c index ddd56b0a..bd8a8e41 100644 --- a/injected_code.c +++ b/injected_code.c @@ -23804,23 +23804,29 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i bool tile_offset_is_land (int adj_x, int adj_y) { - Map * map = &p_bic_data->Map; - wrap_tile_coords (map, &adj_x, &adj_y); + wrap_tile_coords (&p_bic_data->Map, &adj_x, &adj_y); Tile * adj_tile = tile_at (adj_x, adj_y); return (adj_tile != NULL) && (adj_tile != p_null_tile) && (! adj_tile->vtable->m35_Check_Is_Water (adj_tile)); } void -get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) +get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * out_sprite_index) { - int NW = 0; - int NE = 1; - int SE = 2; - int SW = 3; + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); - int sheet_index = (tile->SquareParts >> 8) & 0xFF; - int sprite_index = tile->SquareParts & 0xFF; + if (tile != NULL & tile != p_null_tile) { + *out_sheet_index = (tile->SquareParts >> 8) & 0xFF; + *out_sprite_index = tile->SquareParts & 0xFF; + } else { + *out_sheet_index = -1; + *out_sprite_index = -1; + } +} +void +get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) +{ if ((tile == NULL) || (tile == p_null_tile) || (out_variant == NULL)) return; @@ -23887,72 +23893,85 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi if (closest_city == NULL) return; - // Check the four diagonal neighbors explicitly - if ((closest_dx == 1) && (closest_dy == -1)) { - *out_variant = SW; - return; - } else if ((closest_dx == -1) && (closest_dy == -1)) { - *out_variant = SE; - return; - } else if ((closest_dx == 1) && (closest_dy == 1)) { - *out_variant = NW; - return; - } else if ((closest_dx == -1) && (closest_dy == 1)) { - *out_variant = NE; - return; - } - - bool city_is_west_of_port = (closest_dx < 0); - bool city_is_north_of_port = (closest_dy < 0); - bool city_is_directly_above_port = (closest_dx == 0) && (closest_dy < 0); - bool city_is_directly_below_port = (closest_dx == 0) && (closest_dy > 0); - bool northwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y - 1); - bool north_tile_is_land = tile_offset_is_land (tile_x, tile_y - 1); - bool northeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y - 1); - bool east_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y); - bool southeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y + 1); - bool south_tile_is_land = tile_offset_is_land (tile_x, tile_y + 1); - bool southwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y + 1); - bool west_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y); - - // Otherwise, face roughly away from the city based on its relative position - int face_dx = -closest_dx; - int face_dy = -closest_dy; - int abs_face_dx = int_abs (face_dx); - int abs_face_dy = int_abs (face_dy); - - if ((abs_face_dx == 0) && (abs_face_dy == 0)) - return; - - if (abs_face_dx > abs_face_dy) { - if (face_dx > 0) - *out_variant = (face_dy >= 0) ? 2 : 1; // city is west; face east (toward +x), bias south if city is south, else bias north - else - *out_variant = (face_dy >= 0) ? 3 : 0; // city is east; face west (toward -x), bias south if city is south, else bias north - } else if (abs_face_dy > abs_face_dx) { - if (face_dy > 0) - *out_variant = (face_dx >= 0) ? 2 : 3; // city is north; face south (toward +y), bias east if city is east, else bias west - else - *out_variant = (face_dx >= 0) ? 1 : 0; // city is south; face north (toward -y), bias east if city is east, else bias west - } else { - if (face_dx >= 0) - *out_variant = (face_dy >= 0) ? 2 : 1; // city northwest/southwest-ish; pick the closer diagonal - else - *out_variant = (face_dy >= 0) ? 3 : 0; // city northeast/southeast-ish; pick the closer diagonal - } - -finalize: - - if (out_pixel_y != NULL) { - if ((*out_variant == 0) || (*out_variant == 1)) { - *out_pixel_y += 16; // Facing north-ish, nudge up - } else if ((*out_variant == 2) || (*out_variant == 3)) { - *out_pixel_y -= 16; // Facing south-ish, nudge down - if (*out_variant == 2) - *out_pixel_x += 8; // Facing southeast, nudge right - else - *out_pixel_x -= 8; // Facing southwest, nudge left - } + int sheet_index = -1, sprite_index = -1; + + bool city_is_directly_above_port = (closest_dx == 0) && (closest_dy < 0); + bool city_is_directly_below_port = (closest_dx == 0) && (closest_dy > 0); + bool city_is_directly_west_of_port = (closest_dx < 0) && (closest_dy == 0); + bool city_is_directly_east_of_port = (closest_dx > 0) && (closest_dy == 0); + bool city_is_directly_northeast_of_port = (closest_dx == 1) && (closest_dy == -1); + bool city_is_directly_southeast_of_port = (closest_dx == 1) && (closest_dy == 1); + bool city_is_directly_southwest_of_port = (closest_dx == -1) && (closest_dy == 1); + bool city_is_directly_northwest_of_port = (closest_dx == -1) && (closest_dy == -1); + bool city_is_west_of_port = (closest_dx < 0); + bool city_is_east_of_port = (closest_dx > 0); + bool city_is_north_of_port = (closest_dy < 0); + bool city_is_south_of_port = (closest_dy > 0); + bool northwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y - 1); + bool north_tile_is_land = tile_offset_is_land (tile_x, tile_y - 1); + bool northeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y - 1); + bool east_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y); + bool southeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y + 1); + bool south_tile_is_land = tile_offset_is_land (tile_x, tile_y + 1); + bool southwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y + 1); + bool west_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y); + + // Variant indices + int NONE = -1; + int NW = 0; + int NE = 1; + int SE = 2; + int SW = 3; + *out_variant = NONE; + + // Additional anchor indices + int W = 0; + int N = 1; + int E = 2; + int S = 3; + int anchor = NONE; + + // Direct diagonals + if (city_is_directly_northeast_of_port) { *out_variant = SW; anchor = NE; } + else if (city_is_directly_southeast_of_port) { *out_variant = NW; anchor = SE; } + else if (city_is_directly_southwest_of_port) { *out_variant = NE; anchor = SW; } + else if (city_is_directly_northwest_of_port) { *out_variant = SE; anchor = NW; } + + // Direct cardinals + else if (city_is_directly_above_port) { *out_variant = SW; anchor = NW; } + else if (city_is_directly_below_port) { *out_variant = NE; anchor = SE; } + else if (city_is_directly_west_of_port) { *out_variant = SE; anchor = SW; } + else if (city_is_directly_east_of_port) { *out_variant = SW; anchor = SE; } + + // City is not adjacent, check relative directions + else if (city_is_north_of_port && city_is_west_of_port) { + if (northwest_tile_is_land) { *out_variant = SE; anchor = NW; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = SW; } + else if (west_tile_is_land) { *out_variant = NE; anchor = W; } + } else if (city_is_north_of_port && city_is_east_of_port) { + if (northeast_tile_is_land) { *out_variant = SW; anchor = NE; } + else if (southeast_tile_is_land) { *out_variant = NW; anchor = SE; } + else if (east_tile_is_land) { *out_variant = SW; anchor = E; } + } else if (city_is_south_of_port && city_is_east_of_port) { + if (southeast_tile_is_land) { *out_variant = NW; anchor = SE; } + else if (northeast_tile_is_land) { *out_variant = SW; anchor = NE; } + else if (east_tile_is_land) { *out_variant = SW; anchor = E; } + } else if (city_is_south_of_port && city_is_west_of_port) { + if (southwest_tile_is_land) { *out_variant = NE; anchor = SW; } + else if (northwest_tile_is_land) { *out_variant = SE; anchor = NW; } + else if (west_tile_is_land) { *out_variant = NE; anchor = W; } + } + + // No ideal direction, pick based on any land tiles around port + if (*out_variant == NONE) { + if (north_tile_is_land) *out_variant = SW; + else if (east_tile_is_land) *out_variant = SW; + else if (south_tile_is_land) *out_variant = NE; + else if (west_tile_is_land) *out_variant = NE; + else if (northeast_tile_is_land) *out_variant = SW; + else if (southeast_tile_is_land) *out_variant = NW; + else if (southwest_tile_is_land) *out_variant = NE; + else if (northwest_tile_is_land) *out_variant = SE; } } From ac8707f88520f966538dfc7e1b25dfab126944ff Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 27 Nov 2025 23:52:31 -0800 Subject: [PATCH 047/356] Port direction & pixel offset system working --- injected_code.c | 99 ++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/injected_code.c b/injected_code.c index bd8a8e41..3f619fca 100644 --- a/injected_code.c +++ b/injected_code.c @@ -23802,11 +23802,12 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i } bool -tile_offset_is_land (int adj_x, int adj_y) +tile_offset_is_owner_land (int civ_id, int adj_x, int adj_y) { wrap_tile_coords (&p_bic_data->Map, &adj_x, &adj_y); Tile * adj_tile = tile_at (adj_x, adj_y); - return (adj_tile != NULL) && (adj_tile != p_null_tile) && (! adj_tile->vtable->m35_Check_Is_Water (adj_tile)); + return (adj_tile != NULL) && (adj_tile != p_null_tile) && + (adj_tile->Territory_OwnerID == civ_id) && (! adj_tile->vtable->m35_Check_Is_Water (adj_tile)); } void @@ -23907,16 +23908,16 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi bool city_is_east_of_port = (closest_dx > 0); bool city_is_north_of_port = (closest_dy < 0); bool city_is_south_of_port = (closest_dy > 0); - bool northwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y - 1); - bool north_tile_is_land = tile_offset_is_land (tile_x, tile_y - 1); - bool northeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y - 1); - bool east_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y); - bool southeast_tile_is_land = tile_offset_is_land (tile_x + 1, tile_y + 1); - bool south_tile_is_land = tile_offset_is_land (tile_x, tile_y + 1); - bool southwest_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y + 1); - bool west_tile_is_land = tile_offset_is_land (tile_x - 1, tile_y); - - // Variant indices + bool northwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y - 1); + bool north_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y - 1); + bool northeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y - 1); + bool east_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y); + bool southeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y + 1); + bool south_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y + 1); + bool southwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y + 1); + bool west_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y); + + // Variant indices; can't use direction enum as values are slightly different int NONE = -1; int NW = 0; int NE = 1; @@ -23924,55 +23925,56 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi int SW = 3; *out_variant = NONE; - // Additional anchor indices - int W = 0; - int N = 1; - int E = 2; - int S = 3; - int anchor = NONE; + enum direction anchor = NONE; // Direct diagonals - if (city_is_directly_northeast_of_port) { *out_variant = SW; anchor = NE; } - else if (city_is_directly_southeast_of_port) { *out_variant = NW; anchor = SE; } - else if (city_is_directly_southwest_of_port) { *out_variant = NE; anchor = SW; } - else if (city_is_directly_northwest_of_port) { *out_variant = SE; anchor = NW; } + if (city_is_directly_northeast_of_port) { *out_variant = SW; anchor = DIR_NE; } + else if (city_is_directly_southeast_of_port) { *out_variant = NW; anchor = DIR_SE; } + else if (city_is_directly_southwest_of_port) { *out_variant = NE; anchor = DIR_SW; } + else if (city_is_directly_northwest_of_port) { *out_variant = SE; anchor = DIR_NW; } // Direct cardinals - else if (city_is_directly_above_port) { *out_variant = SW; anchor = NW; } - else if (city_is_directly_below_port) { *out_variant = NE; anchor = SE; } - else if (city_is_directly_west_of_port) { *out_variant = SE; anchor = SW; } - else if (city_is_directly_east_of_port) { *out_variant = SW; anchor = SE; } + else if (city_is_directly_above_port) { *out_variant = SW; anchor = DIR_NW; } + else if (city_is_directly_below_port) { *out_variant = NE; anchor = DIR_SE; } + else if (city_is_directly_west_of_port) { *out_variant = SE; anchor = DIR_SW; } + else if (city_is_directly_east_of_port) { *out_variant = SW; anchor = DIR_SE; } // City is not adjacent, check relative directions else if (city_is_north_of_port && city_is_west_of_port) { - if (northwest_tile_is_land) { *out_variant = SE; anchor = NW; } - else if (southwest_tile_is_land) { *out_variant = NE; anchor = SW; } - else if (west_tile_is_land) { *out_variant = NE; anchor = W; } + if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else if (west_tile_is_land) { *out_variant = SE; anchor = DIR_W; } } else if (city_is_north_of_port && city_is_east_of_port) { - if (northeast_tile_is_land) { *out_variant = SW; anchor = NE; } - else if (southeast_tile_is_land) { *out_variant = NW; anchor = SE; } - else if (east_tile_is_land) { *out_variant = SW; anchor = E; } + if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } } else if (city_is_south_of_port && city_is_east_of_port) { - if (southeast_tile_is_land) { *out_variant = NW; anchor = SE; } - else if (northeast_tile_is_land) { *out_variant = SW; anchor = NE; } - else if (east_tile_is_land) { *out_variant = SW; anchor = E; } + if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } } else if (city_is_south_of_port && city_is_west_of_port) { - if (southwest_tile_is_land) { *out_variant = NE; anchor = SW; } - else if (northwest_tile_is_land) { *out_variant = SE; anchor = NW; } - else if (west_tile_is_land) { *out_variant = NE; anchor = W; } + if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } } // No ideal direction, pick based on any land tiles around port if (*out_variant == NONE) { - if (north_tile_is_land) *out_variant = SW; - else if (east_tile_is_land) *out_variant = SW; - else if (south_tile_is_land) *out_variant = NE; - else if (west_tile_is_land) *out_variant = NE; - else if (northeast_tile_is_land) *out_variant = SW; - else if (southeast_tile_is_land) *out_variant = NW; - else if (southwest_tile_is_land) *out_variant = NE; - else if (northwest_tile_is_land) *out_variant = SE; + if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (north_tile_is_land) { *out_variant = SW; anchor = DIR_N; } + else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } + else if (south_tile_is_land) { *out_variant = NE; anchor = DIR_S; } + else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } + else { *out_variant = SW; anchor = DIR_NW; } // Shouldn't happen but just in case } + + // Determine pixel offsets based on direction & anchor + if (*out_variant == SW && anchor == DIR_NE) { *out_pixel_x -= 0; *out_pixel_y += 6; } + else if (*out_variant == SE && anchor == DIR_NW) { *out_pixel_x -= 2; *out_pixel_y += 6; } + else if (*out_variant == SE && anchor == DIR_W) { *out_pixel_x -= 30; *out_pixel_y += 8; } } void __fastcall @@ -24086,10 +24088,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par case PORT_DISTRICT_ID: { get_port_district_variant_for_tile (tile, &variant, &pixel_x, &pixel_y); - char ss[200]; - snprintf (ss, sizeof ss, "Port district at tile (%d,%d) using variant %d\n", tile_x, tile_y, variant); - (*p_OutputDebugStringA) (ss); - break; + // Don't break, let fall through to default to count buildings } default: { From 64ffef898a8db43822ff41fcfa22e12371046b9e Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 28 Nov 2025 09:27:03 -0800 Subject: [PATCH 048/356] Added sheet index info temporarily to debug terrain info --- injected_code.c | 127 ++++++++++++++++++++++++++---------------------- 1 file changed, 69 insertions(+), 58 deletions(-) diff --git a/injected_code.c b/injected_code.c index 3f619fca..e8322875 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16044,14 +16044,17 @@ patch_PCX_Image_draw_tile_info_terrain (PCX_Image * this, int edx, char * str, i } } - // Show sprites indexes for debugging - int sheet_index = (tile->SquareParts >> 8) & 0xFF; - int sprite_index = tile->SquareParts & 0xFF; - - snprintf (s, sizeof s, "%s, (%d, %d)", display_name, sheet_index, sprite_index); + snprintf (s, sizeof s, "%s", display_name); PCX_Image_draw_text (this, __, s, x + 68, y, strlen (s)); } } + + // Show sprites indexes for port position debugging + int sheet_index = (tile->SquareParts >> 8) & 0xFF; + int sprite_index = tile->SquareParts & 0xFF; + snprintf (s, sizeof s, "(%d, %d)", sheet_index, sprite_index); + PCX_Image_draw_text (this, __, s, x, y - 18, strlen (s)); + // Draw tile coords on line below terrain name snprintf (s, sizeof s, "(%d, %d)", is->viewing_tile_info_x, is->viewing_tile_info_y); PCX_Image_draw_text (this, __, s, x, y + 14, strlen (s)); @@ -23826,7 +23829,7 @@ get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * ou } void -get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) +set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) { if ((tile == NULL) || (tile == p_null_tile) || (out_variant == NULL)) return; @@ -23894,8 +23897,6 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi if (closest_city == NULL) return; - int sheet_index = -1, sprite_index = -1; - bool city_is_directly_above_port = (closest_dx == 0) && (closest_dy < 0); bool city_is_directly_below_port = (closest_dx == 0) && (closest_dy > 0); bool city_is_directly_west_of_port = (closest_dx < 0) && (closest_dy == 0); @@ -23904,18 +23905,6 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi bool city_is_directly_southeast_of_port = (closest_dx == 1) && (closest_dy == 1); bool city_is_directly_southwest_of_port = (closest_dx == -1) && (closest_dy == 1); bool city_is_directly_northwest_of_port = (closest_dx == -1) && (closest_dy == -1); - bool city_is_west_of_port = (closest_dx < 0); - bool city_is_east_of_port = (closest_dx > 0); - bool city_is_north_of_port = (closest_dy < 0); - bool city_is_south_of_port = (closest_dy > 0); - bool northwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y - 1); - bool north_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y - 1); - bool northeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y - 1); - bool east_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y); - bool southeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y + 1); - bool south_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y + 1); - bool southwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y + 1); - bool west_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y); // Variant indices; can't use direction enum as values are slightly different int NONE = -1; @@ -23934,53 +23923,69 @@ get_port_district_variant_for_tile (Tile * tile, int * out_variant, int * out_pi else if (city_is_directly_northwest_of_port) { *out_variant = SE; anchor = DIR_NW; } // Direct cardinals - else if (city_is_directly_above_port) { *out_variant = SW; anchor = DIR_NW; } - else if (city_is_directly_below_port) { *out_variant = NE; anchor = DIR_SE; } - else if (city_is_directly_west_of_port) { *out_variant = SE; anchor = DIR_SW; } - else if (city_is_directly_east_of_port) { *out_variant = SW; anchor = DIR_SE; } - - // City is not adjacent, check relative directions - else if (city_is_north_of_port && city_is_west_of_port) { - if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } - else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } - else if (west_tile_is_land) { *out_variant = SE; anchor = DIR_W; } - } else if (city_is_north_of_port && city_is_east_of_port) { - if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } - else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } - else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } - } else if (city_is_south_of_port && city_is_east_of_port) { - if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } - else if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } - else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } - } else if (city_is_south_of_port && city_is_west_of_port) { - if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } - else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } - else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } - } - - // No ideal direction, pick based on any land tiles around port - if (*out_variant == NONE) { - if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } - else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } - else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } - else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } - else if (north_tile_is_land) { *out_variant = SW; anchor = DIR_N; } - else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } - else if (south_tile_is_land) { *out_variant = NE; anchor = DIR_S; } - else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } - else { *out_variant = SW; anchor = DIR_NW; } // Shouldn't happen but just in case + else if (city_is_directly_above_port) { *out_variant = SW; anchor = DIR_N; } + else if (city_is_directly_below_port) { *out_variant = NE; anchor = DIR_S; } + else if (city_is_directly_west_of_port) { *out_variant = NE; anchor = DIR_W; } + else if (city_is_directly_east_of_port) { *out_variant = SW; anchor = DIR_E; } + + // City not adjacent, check relative directions + else { + bool city_is_west_of_port = (closest_dx < 0); + bool city_is_east_of_port = (closest_dx > 0); + bool city_is_north_of_port = (closest_dy < 0); + bool city_is_south_of_port = (closest_dy > 0); + bool northwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y - 1); + bool north_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y - 2); + bool northeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y - 1); + bool east_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 2, tile_y); + bool southeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y + 1); + bool south_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y + 2); + bool southwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y + 1); + bool west_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 2, tile_y); + + if (city_is_north_of_port && city_is_west_of_port) { + if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (west_tile_is_land) { *out_variant = SE; anchor = DIR_W; } + } else if (city_is_north_of_port && city_is_east_of_port) { + if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } + } else if (city_is_south_of_port && city_is_east_of_port) { + if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } + } else if (city_is_south_of_port && city_is_west_of_port) { + if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } + } + + // No ideal direction, pick based on any owner land tiles around port + if (*out_variant == NONE) { + if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (north_tile_is_land) { *out_variant = SW; anchor = DIR_N; } + else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } + else if (south_tile_is_land) { *out_variant = NE; anchor = DIR_S; } + else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } + else { *out_variant = SW; anchor = DIR_NW; } // Shouldn't happen but just in case + } } // Determine pixel offsets based on direction & anchor if (*out_variant == SW && anchor == DIR_NE) { *out_pixel_x -= 0; *out_pixel_y += 6; } else if (*out_variant == SE && anchor == DIR_NW) { *out_pixel_x -= 2; *out_pixel_y += 6; } - else if (*out_variant == SE && anchor == DIR_W) { *out_pixel_x -= 30; *out_pixel_y += 8; } + else if (*out_variant == SE && anchor == DIR_W) { *out_pixel_x -= 30; *out_pixel_y += 12; } } void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - //*p_debug_mode_bits |= 0xC; + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -24003,6 +24008,12 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par if (is->dc_img_state != IS_OK) return; + /* + if (is->current_config.show_detailed_tile_info) { + tile = tile_at (is->viewing_tile_info_x, is->viewing_tile_info_y); + } + */ + // Natural Wonder if (district_id == NATURAL_WONDER_DISTRICT_ID) { if (! is->current_config.enable_natural_wonders) @@ -24087,7 +24098,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } case PORT_DISTRICT_ID: { - get_port_district_variant_for_tile (tile, &variant, &pixel_x, &pixel_y); + set_port_variant_and_pixel_offsets (tile, &variant, &pixel_x, &pixel_y); // Don't break, let fall through to default to count buildings } default: From 7999def1d5d3b866d29d17977576579ee8f97966 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 29 Nov 2025 09:14:38 -0800 Subject: [PATCH 049/356] Refine port placement logic --- injected_code.c | 74 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/injected_code.c b/injected_code.c index e8322875..c04d9fa1 100644 --- a/injected_code.c +++ b/injected_code.c @@ -23805,12 +23805,15 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i } bool -tile_offset_is_owner_land (int civ_id, int adj_x, int adj_y) +tile_offset_is_land (int civ_id, int tile_x, int tile_y, bool must_be_same_owner) { - wrap_tile_coords (&p_bic_data->Map, &adj_x, &adj_y); - Tile * adj_tile = tile_at (adj_x, adj_y); - return (adj_tile != NULL) && (adj_tile != p_null_tile) && - (adj_tile->Territory_OwnerID == civ_id) && (! adj_tile->vtable->m35_Check_Is_Water (adj_tile)); + if (must_be_same_owner && (civ_id <= 0)) + return false; + + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + return (tile != NULL) && (tile != p_null_tile) && (! tile->vtable->m35_Check_Is_Water (tile)) && + ((! must_be_same_owner) || (tile->Territory_OwnerID == civ_id)); } void @@ -23819,7 +23822,7 @@ get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * ou wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); Tile * tile = tile_at (tile_x, tile_y); - if (tile != NULL & tile != p_null_tile) { + if (tile != NULL && tile != p_null_tile) { *out_sheet_index = (tile->SquareParts >> 8) & 0xFF; *out_sprite_index = tile->SquareParts & 0xFF; } else { @@ -23934,14 +23937,14 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi bool city_is_east_of_port = (closest_dx > 0); bool city_is_north_of_port = (closest_dy < 0); bool city_is_south_of_port = (closest_dy > 0); - bool northwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y - 1); - bool north_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y - 2); - bool northeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y - 1); - bool east_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 2, tile_y); - bool southeast_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x + 1, tile_y + 1); - bool south_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x, tile_y + 2); - bool southwest_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 1, tile_y + 1); - bool west_tile_is_land = tile_offset_is_owner_land (owner_id, tile_x - 2, tile_y); + bool northwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y - 1, true); + bool north_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y - 2, true); + bool northeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y - 1, true); + bool east_tile_is_land = tile_offset_is_land (owner_id, tile_x + 2, tile_y, true); + bool southeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y + 1, true); + bool south_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y + 2, true); + bool southwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y + 1, true); + bool west_tile_is_land = tile_offset_is_land (owner_id, tile_x - 2, tile_y, true); if (city_is_north_of_port && city_is_west_of_port) { if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } @@ -23972,14 +23975,55 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } else if (south_tile_is_land) { *out_variant = NE; anchor = DIR_S; } else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } - else { *out_variant = SW; anchor = DIR_NW; } // Shouldn't happen but just in case + } + + // Same civ doesn't own any adjacent land tiles, pick based on *any* land tiles + if (*out_variant == NONE) { + bool northwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y - 1, false); + bool north_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y - 2, false); + bool northeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y - 1, false); + bool east_tile_is_land = tile_offset_is_land (owner_id, tile_x + 2, tile_y, false); + bool southeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y + 1, false); + bool south_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y + 2, false); + bool southwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y + 1, false); + bool west_tile_is_land = tile_offset_is_land (owner_id, tile_x - 2, tile_y, false); + if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (north_tile_is_land) { *out_variant = SW; anchor = DIR_N; } + else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } + else if (south_tile_is_land) { *out_variant = NE; anchor = DIR_S; } + else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } + else { *out_variant = SW; anchor = DIR_NE; } // Somehow no land tiles at all? Default to NE } } + int anchor_sheet_index, anchor_sprite_index; + switch (anchor) { + case DIR_N: get_tile_sprite_indices (tile_x, tile_y - 2, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_NE: get_tile_sprite_indices (tile_x + 1, tile_y - 1, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_E: get_tile_sprite_indices (tile_x + 2, tile_y, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_SE: get_tile_sprite_indices (tile_x + 1, tile_y + 1, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_S: get_tile_sprite_indices (tile_x, tile_y + 2, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_SW: get_tile_sprite_indices (tile_x - 1, tile_y + 1, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_W: get_tile_sprite_indices (tile_x - 2, tile_y, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_NW: get_tile_sprite_indices (tile_x - 1, tile_y - 1, &anchor_sheet_index, &anchor_sprite_index); break; + default: anchor_sheet_index = -1; anchor_sprite_index = -1; break; + } + + bool anchor_is_hill = anchor_sprite_index >= 50; + // Determine pixel offsets based on direction & anchor if (*out_variant == SW && anchor == DIR_NE) { *out_pixel_x -= 0; *out_pixel_y += 6; } else if (*out_variant == SE && anchor == DIR_NW) { *out_pixel_x -= 2; *out_pixel_y += 6; } else if (*out_variant == SE && anchor == DIR_W) { *out_pixel_x -= 30; *out_pixel_y += 12; } + + // 0 & 5 tend to be recessed compared to other sheets + if (! anchor_is_hill & (anchor_sheet_index == 0 || anchor_sheet_index == 5)) { + if (anchor == DIR_W || anchor == DIR_NW || anchor == DIR_SW) *out_pixel_x -= 6; + else if (anchor == DIR_E || anchor == DIR_NE || anchor == DIR_SE) *out_pixel_x += 6; + } } void __fastcall From e81c04f48571cd649a76bb045eab892ff507d06b Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 29 Nov 2025 15:52:30 -0800 Subject: [PATCH 050/356] More port positioning logic --- injected_code.c | 81 +++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/injected_code.c b/injected_code.c index c04d9fa1..115a081f 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16052,7 +16052,7 @@ patch_PCX_Image_draw_tile_info_terrain (PCX_Image * this, int edx, char * str, i // Show sprites indexes for port position debugging int sheet_index = (tile->SquareParts >> 8) & 0xFF; int sprite_index = tile->SquareParts & 0xFF; - snprintf (s, sizeof s, "(%d, %d)", sheet_index, sprite_index); + snprintf (s, sizeof s, "%d, %d", sheet_index, sprite_index); PCX_Image_draw_text (this, __, s, x, y - 18, strlen (s)); // Draw tile coords on line below terrain name @@ -23816,7 +23816,7 @@ tile_offset_is_land (int civ_id, int tile_x, int tile_y, bool must_be_same_owner ((! must_be_same_owner) || (tile->Territory_OwnerID == civ_id)); } -void +Tile * get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * out_sprite_index) { wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); @@ -23829,6 +23829,8 @@ get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * ou *out_sheet_index = -1; *out_sprite_index = -1; } + + return tile; } void @@ -23900,8 +23902,8 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi if (closest_city == NULL) return; - bool city_is_directly_above_port = (closest_dx == 0) && (closest_dy < 0); - bool city_is_directly_below_port = (closest_dx == 0) && (closest_dy > 0); + bool city_is_directly_above_port = (closest_dx == 0) && (closest_dy == -1); + bool city_is_directly_below_port = (closest_dx == 0) && (closest_dy == 1); bool city_is_directly_west_of_port = (closest_dx < 0) && (closest_dy == 0); bool city_is_directly_east_of_port = (closest_dx > 0) && (closest_dy == 0); bool city_is_directly_northeast_of_port = (closest_dx == 1) && (closest_dy == -1); @@ -23925,13 +23927,7 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi else if (city_is_directly_southwest_of_port) { *out_variant = NE; anchor = DIR_SW; } else if (city_is_directly_northwest_of_port) { *out_variant = SE; anchor = DIR_NW; } - // Direct cardinals - else if (city_is_directly_above_port) { *out_variant = SW; anchor = DIR_N; } - else if (city_is_directly_below_port) { *out_variant = NE; anchor = DIR_S; } - else if (city_is_directly_west_of_port) { *out_variant = NE; anchor = DIR_W; } - else if (city_is_directly_east_of_port) { *out_variant = SW; anchor = DIR_E; } - - // City not adjacent, check relative directions + // City either in a direct cardinal direction or not adjacent, check relative directions else { bool city_is_west_of_port = (closest_dx < 0); bool city_is_east_of_port = (closest_dx > 0); @@ -23946,7 +23942,23 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi bool southwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y + 1, true); bool west_tile_is_land = tile_offset_is_land (owner_id, tile_x - 2, tile_y, true); - if (city_is_north_of_port && city_is_west_of_port) { + if (city_is_directly_above_port) { + if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else { *out_variant = SW; anchor = DIR_N; } + } else if (city_is_directly_below_port) { + if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else { *out_variant = NE; anchor = DIR_S; } + } else if (city_is_directly_west_of_port) { + if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } + else { *out_variant = SE; anchor = DIR_W; } + } else if (city_is_directly_east_of_port) { + if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } + else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } + else { *out_variant = SW; anchor = DIR_E; } + } else if (city_is_north_of_port && city_is_west_of_port) { if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } else if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } @@ -23962,6 +23974,7 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi } else if (city_is_south_of_port && city_is_west_of_port) { if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } + else if (west_tile_is_land && ! north_tile_is_land) { *out_variant = NE; anchor = DIR_W; } else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } } @@ -23974,7 +23987,7 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi else if (north_tile_is_land) { *out_variant = SW; anchor = DIR_N; } else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } else if (south_tile_is_land) { *out_variant = NE; anchor = DIR_S; } - else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } + else if (west_tile_is_land) { *out_variant = SE; anchor = DIR_W; } } // Same civ doesn't own any adjacent land tiles, pick based on *any* land tiles @@ -23994,35 +24007,43 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi else if (north_tile_is_land) { *out_variant = SW; anchor = DIR_N; } else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } else if (south_tile_is_land) { *out_variant = NE; anchor = DIR_S; } - else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } + else if (west_tile_is_land) { *out_variant = SE; anchor = DIR_W; } else { *out_variant = SW; anchor = DIR_NE; } // Somehow no land tiles at all? Default to NE } } + Tile * anchor_tile; int anchor_sheet_index, anchor_sprite_index; switch (anchor) { - case DIR_N: get_tile_sprite_indices (tile_x, tile_y - 2, &anchor_sheet_index, &anchor_sprite_index); break; - case DIR_NE: get_tile_sprite_indices (tile_x + 1, tile_y - 1, &anchor_sheet_index, &anchor_sprite_index); break; - case DIR_E: get_tile_sprite_indices (tile_x + 2, tile_y, &anchor_sheet_index, &anchor_sprite_index); break; - case DIR_SE: get_tile_sprite_indices (tile_x + 1, tile_y + 1, &anchor_sheet_index, &anchor_sprite_index); break; - case DIR_S: get_tile_sprite_indices (tile_x, tile_y + 2, &anchor_sheet_index, &anchor_sprite_index); break; - case DIR_SW: get_tile_sprite_indices (tile_x - 1, tile_y + 1, &anchor_sheet_index, &anchor_sprite_index); break; - case DIR_W: get_tile_sprite_indices (tile_x - 2, tile_y, &anchor_sheet_index, &anchor_sprite_index); break; - case DIR_NW: get_tile_sprite_indices (tile_x - 1, tile_y - 1, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_N: anchor_tile = get_tile_sprite_indices (tile_x, tile_y - 2, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_NE: anchor_tile = get_tile_sprite_indices (tile_x + 1, tile_y - 1, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_E: anchor_tile = get_tile_sprite_indices (tile_x + 2, tile_y, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_SE: anchor_tile = get_tile_sprite_indices (tile_x + 1, tile_y + 1, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_S: anchor_tile = get_tile_sprite_indices (tile_x, tile_y + 2, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_SW: anchor_tile = get_tile_sprite_indices (tile_x - 1, tile_y + 1, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_W: anchor_tile = get_tile_sprite_indices (tile_x - 2, tile_y, &anchor_sheet_index, &anchor_sprite_index); break; + case DIR_NW: anchor_tile = get_tile_sprite_indices (tile_x - 1, tile_y - 1, &anchor_sheet_index, &anchor_sprite_index); break; default: anchor_sheet_index = -1; anchor_sprite_index = -1; break; } - bool anchor_is_hill = anchor_sprite_index >= 50; - - // Determine pixel offsets based on direction & anchor + // Determine general pixel offsets based on direction & anchor if (*out_variant == SW && anchor == DIR_NE) { *out_pixel_x -= 0; *out_pixel_y += 6; } else if (*out_variant == SE && anchor == DIR_NW) { *out_pixel_x -= 2; *out_pixel_y += 6; } - else if (*out_variant == SE && anchor == DIR_W) { *out_pixel_x -= 30; *out_pixel_y += 12; } + else if (*out_variant == NW && anchor == DIR_SE) { *out_pixel_x -= 0; *out_pixel_y -= 2; } + else if (*out_variant == SW && anchor == DIR_N) { *out_pixel_x -= 6; *out_pixel_y -= 10; } + else if (*out_variant == SW && anchor == DIR_E) { *out_pixel_x += 36; *out_pixel_y += 18; } + else if (*out_variant == NE && anchor == DIR_S) { *out_pixel_x += 20; *out_pixel_y += 20; } + + bool anchor_is_hill = anchor_tile != NULL && anchor_tile->vtable->m50_Get_Square_BaseType (anchor_tile) == SQ_Hills; + bool anchor_is_mountain = anchor_tile != NULL && anchor_tile->vtable->m50_Get_Square_BaseType (anchor_tile) == SQ_Mountains; + + // Handle edge cases. Annoying, but looks quite a bit better so worth it + if (! (anchor_is_hill | anchor_is_mountain)) { + if ((*out_variant == SW || *out_variant == NW) && anchor_sheet_index == 0) { *out_pixel_x += 24; } + else if ((*out_variant == SE || *out_variant == NE) && anchor_sheet_index == 0) { *out_pixel_x -= 24; } - // 0 & 5 tend to be recessed compared to other sheets - if (! anchor_is_hill & (anchor_sheet_index == 0 || anchor_sheet_index == 5)) { - if (anchor == DIR_W || anchor == DIR_NW || anchor == DIR_SW) *out_pixel_x -= 6; - else if (anchor == DIR_E || anchor == DIR_NE || anchor == DIR_SE) *out_pixel_x += 6; + if (*out_variant == SW && anchor == DIR_NE && anchor_sheet_index == 0 && anchor_sprite_index == 26) { *out_pixel_x -= 4; *out_pixel_y -= 6; } + else if (*out_variant == NE && anchor == DIR_SW && anchor_sheet_index == 0 && anchor_sprite_index == 20) { *out_pixel_x -= 2; *out_pixel_y += 4; } } } From f132c4cc9ff2f727c554928925ac367498db4257 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 09:02:10 -0800 Subject: [PATCH 051/356] Tentatively done with port placement algorithm --- injected_code.c | 107 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 79 insertions(+), 28 deletions(-) diff --git a/injected_code.c b/injected_code.c index 115a081f..d63febad 100644 --- a/injected_code.c +++ b/injected_code.c @@ -23834,7 +23834,7 @@ get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * ou } void -set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) +align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) { if ((tile == NULL) || (tile == p_null_tile) || (out_variant == NULL)) return; @@ -23920,12 +23920,13 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi *out_variant = NONE; enum direction anchor = NONE; + bool direct_diagonal = false; // Direct diagonals - if (city_is_directly_northeast_of_port) { *out_variant = SW; anchor = DIR_NE; } - else if (city_is_directly_southeast_of_port) { *out_variant = NW; anchor = DIR_SE; } - else if (city_is_directly_southwest_of_port) { *out_variant = NE; anchor = DIR_SW; } - else if (city_is_directly_northwest_of_port) { *out_variant = SE; anchor = DIR_NW; } + if (city_is_directly_northeast_of_port) { *out_variant = SW; anchor = DIR_NE; direct_diagonal = true; } + else if (city_is_directly_southeast_of_port) { *out_variant = NW; anchor = DIR_SE; direct_diagonal = true; } + else if (city_is_directly_southwest_of_port) { *out_variant = NE; anchor = DIR_SW; direct_diagonal = true; } + else if (city_is_directly_northwest_of_port) { *out_variant = SE; anchor = DIR_NW; direct_diagonal = true; } // City either in a direct cardinal direction or not adjacent, check relative directions else { @@ -23942,40 +23943,46 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi bool southwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y + 1, true); bool west_tile_is_land = tile_offset_is_land (owner_id, tile_x - 2, tile_y, true); - if (city_is_directly_above_port) { + if (city_is_directly_above_port) { if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } - else { *out_variant = SW; anchor = DIR_N; } + else { + *out_variant = SW; anchor = DIR_NE; + *out_pixel_x -= 4; *out_pixel_y -= 4; + } } else if (city_is_directly_below_port) { if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } - else { *out_variant = NE; anchor = DIR_S; } + else { + *out_variant = NE; anchor = DIR_SW; + *out_pixel_x += 4; *out_pixel_y += 4; + } } else if (city_is_directly_west_of_port) { if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } - else { *out_variant = SE; anchor = DIR_W; } + else { + *out_variant = SE; anchor = DIR_NW; + *out_pixel_x -= 50; *out_pixel_y += 14; + } } else if (city_is_directly_east_of_port) { if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } - else { *out_variant = SW; anchor = DIR_E; } + else { + *out_variant = SW; anchor = DIR_NE; + *out_pixel_x += 50; *out_pixel_y -= 14; + } } else if (city_is_north_of_port && city_is_west_of_port) { if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } - else if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } - else if (west_tile_is_land) { *out_variant = SE; anchor = DIR_W; } } else if (city_is_north_of_port && city_is_east_of_port) { if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } - else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } } else if (city_is_south_of_port && city_is_east_of_port) { if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } else if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } - else if (east_tile_is_land) { *out_variant = SW; anchor = DIR_E; } } else if (city_is_south_of_port && city_is_west_of_port) { if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } - else if (west_tile_is_land && ! north_tile_is_land) { *out_variant = NE; anchor = DIR_W; } - else if (west_tile_is_land) { *out_variant = NE; anchor = DIR_W; } } // No ideal direction, pick based on any owner land tiles around port @@ -24026,25 +24033,69 @@ set_port_variant_and_pixel_offsets (Tile * tile, int * out_variant, int * out_pi default: anchor_sheet_index = -1; anchor_sprite_index = -1; break; } + bool anchor_is_hill = anchor_tile != NULL && anchor_tile->vtable->m50_Get_Square_BaseType (anchor_tile) == SQ_Hills; + bool anchor_is_mountain = anchor_tile != NULL && anchor_tile->vtable->m50_Get_Square_BaseType (anchor_tile) == SQ_Mountains; + // Determine general pixel offsets based on direction & anchor if (*out_variant == SW && anchor == DIR_NE) { *out_pixel_x -= 0; *out_pixel_y += 6; } else if (*out_variant == SE && anchor == DIR_NW) { *out_pixel_x -= 2; *out_pixel_y += 6; } else if (*out_variant == NW && anchor == DIR_SE) { *out_pixel_x -= 0; *out_pixel_y -= 2; } - else if (*out_variant == SW && anchor == DIR_N) { *out_pixel_x -= 6; *out_pixel_y -= 10; } + else if (*out_variant == SW && anchor == DIR_N) { *out_pixel_x -= 56; *out_pixel_y -= 26; } else if (*out_variant == SW && anchor == DIR_E) { *out_pixel_x += 36; *out_pixel_y += 18; } - else if (*out_variant == NE && anchor == DIR_S) { *out_pixel_x += 20; *out_pixel_y += 20; } + else if (*out_variant == NE && anchor == DIR_S) { *out_pixel_x += 70; *out_pixel_y += 30; } + else if (*out_variant == SE && anchor == DIR_W) { *out_pixel_x -= 20; *out_pixel_y += 14; } - bool anchor_is_hill = anchor_tile != NULL && anchor_tile->vtable->m50_Get_Square_BaseType (anchor_tile) == SQ_Hills; - bool anchor_is_mountain = anchor_tile != NULL && anchor_tile->vtable->m50_Get_Square_BaseType (anchor_tile) == SQ_Mountains; + // Handle edge cases. Tedious, but looks quite a bit better so worth it + if (! (anchor_is_hill || anchor_is_mountain) && ! direct_diagonal) { + if (*out_variant == SE && anchor == DIR_W) { *out_pixel_x -= 20; *out_pixel_y += 20; } - // Handle edge cases. Annoying, but looks quite a bit better so worth it - if (! (anchor_is_hill | anchor_is_mountain)) { - if ((*out_variant == SW || *out_variant == NW) && anchor_sheet_index == 0) { *out_pixel_x += 24; } - else if ((*out_variant == SE || *out_variant == NE) && anchor_sheet_index == 0) { *out_pixel_x -= 24; } + // Sheet 0 + if (anchor_sheet_index == 0) { + if (*out_variant == SW || *out_variant == NW) { *out_pixel_x += 32; } + else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 32; } - if (*out_variant == SW && anchor == DIR_NE && anchor_sheet_index == 0 && anchor_sprite_index == 26) { *out_pixel_x -= 4; *out_pixel_y -= 6; } - else if (*out_variant == NE && anchor == DIR_SW && anchor_sheet_index == 0 && anchor_sprite_index == 20) { *out_pixel_x -= 2; *out_pixel_y += 4; } - } + if (*out_variant == SW && anchor == DIR_NE && anchor_sprite_index == 26) { *out_pixel_x -= 4; *out_pixel_y -= 6; } + else if (*out_variant == NE && anchor == DIR_SW && anchor_sprite_index == 20) { *out_pixel_x -= 2; *out_pixel_y += 4; } + + if (*out_variant == SW && (anchor_sprite_index == 0 || anchor_sprite_index == 4 || anchor_sprite_index == 10 || anchor_sprite_index == 1)) { *out_pixel_x +=2; *out_pixel_y -= 8; } + else if (*out_variant == SW && anchor == DIR_N && (anchor_sprite_index == 6)) { *out_pixel_x -= 6; *out_pixel_y -= 8; } + else if (*out_variant == SE && anchor == DIR_W && (anchor_sprite_index == 18)) { *out_pixel_x += 6; *out_pixel_y += 4; } + } + // Sheet 1 + else if (anchor_sheet_index == 1) { + if (*out_variant == SW || *out_variant == NW) { *out_pixel_x += 10; } + else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 10; } + + if (*out_variant == SW && (anchor_sprite_index == 7 || anchor_sprite_index == 17)) { *out_pixel_x +=2; *out_pixel_y -= 8; } + } + // Sheet 3 + else if (anchor_sheet_index == 2) { + if (*out_variant == SW && (anchor_sprite_index == 6)) { *out_pixel_x +=2; *out_pixel_y -= 8; } + } + // Sheet 3 + else if (anchor_sheet_index == 3) { + if (*out_variant == SW || *out_variant == NW) { *out_pixel_x += 16; } + else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 16; } + + if (*out_variant == SW && (anchor_sprite_index == 43 || anchor_sprite_index == 40)) { *out_pixel_x +=6; *out_pixel_y -= 12; } + if (*out_variant == SW && (anchor_sprite_index == 35 || anchor_sprite_index == 39)) { *out_pixel_x +=6; *out_pixel_y -= 12; } + } + // Sheet 4 + else if (anchor_sheet_index == 4) { + if (*out_variant == SW && (anchor_sprite_index == 71)) { *out_pixel_x +=2; *out_pixel_y -= 8; } + } + // Sheet 5 + else if (anchor_sheet_index == 5) { + if (*out_variant == SW || *out_variant == NW) { *out_pixel_x += 18; } + else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 18; } + + if ((*out_variant == SW || *out_variant == SE) && anchor_sprite_index == 18) { *out_pixel_y -= 10; } + else if (*out_variant == SW && anchor_sprite_index == 73) { *out_pixel_x -=4; *out_pixel_y -= 10; } + else if (*out_variant == NW && anchor == DIR_SE && anchor_sprite_index == 42) { *out_pixel_y -= 8; } + else if ((*out_variant == NW || *out_variant == SW) && anchor_sprite_index == 6) { *out_pixel_x += 8; } + else if ((*out_variant == SW) && anchor_sprite_index == 16) { *out_pixel_x -=8; *out_pixel_y -= 10; } + } + } else if (direct_diagonal && *out_variant == SW && anchor == DIR_NE) { *out_pixel_x -=8; *out_pixel_y -= 8; } } void __fastcall @@ -24163,7 +24214,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } case PORT_DISTRICT_ID: { - set_port_variant_and_pixel_offsets (tile, &variant, &pixel_x, &pixel_y); + align_variant_and_pixel_offsets_with_coastline (tile, &variant, &pixel_x, &pixel_y); // Don't break, let fall through to default to count buildings } default: From 242feea310d8c8af9d671a60ba6001c18b3f8276 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 09:12:02 -0800 Subject: [PATCH 052/356] Add find_tile_for_port_district function --- injected_code.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/injected_code.c b/injected_code.c index d63febad..26cdff1a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -7612,6 +7612,77 @@ find_tile_for_neighborhood_district (City * city, int * out_x, int * out_y) return NULL; } +Tile * +find_tile_for_port_district (City * city, int * out_x, int * out_y) +{ + if (city == NULL) + return NULL; + + int city_x = city->Body.X; + int city_y = city->Body.Y; + int city_work_radius = is->current_config.city_work_radius; + int threshold_for_body_of_water = 21; // Mimic vanilla behavior so AI doesn't build ports in small lakes + + // Search in order: ring 1, then rings 2..N + int ring_order[8]; + int ring_count = 0; + for (int r = 1; r <= city_work_radius; r++) + ring_order[ring_count++] = r; + + for (int r_idx = 0; r_idx < ring_count; r_idx++) { + int ring = ring_order[r_idx]; + int start_ni = workable_tile_counts[ring - 1]; + int end_ni = workable_tile_counts[ring]; + + Tile * best_tile = NULL; + int best_yield = INT_MAX; + + for (int ni = start_ni; ni < end_ni; ni++) { + int dx, dy; + patch_ni_to_diff_for_work_area (ni, &dx, &dy); + int tx = city_x + dx; + int ty = city_y + dy; + wrap_tile_coords (&p_bic_data->Map, &tx, &ty); + Tile * tile = tile_at (tx, ty); + + if ((tile == NULL) || (tile == p_null_tile)) + continue; + if (is->current_config.enable_distribution_hub_districts) { + int covered = itable_look_up_or (&is->distribution_hub_coverage_counts, (int)tile, 0); + if (covered > 0) + continue; + } + + if (! tile_suitable_for_district (tile, PORT_DISTRICT_ID, city, NULL)) + continue; + if (get_district_instance (tile) != NULL) + continue; + if (! tile->vtable->m35_Check_Is_Water (tile)) + continue; + + int continent_id = tile->vtable->m46_Get_ContinentID (tile); + if ((continent_id < 0) || (continent_id >= p_bic_data->Map.Continent_Count)) + continue; + Continent * continent = &p_bic_data->Map.Continents[continent_id]; + if (continent->Body.TileCount < threshold_for_body_of_water) + continue; + + int yield = compute_city_tile_yield_sum (city, tx, ty); + if (yield < best_yield) { + best_yield = yield; + best_tile = tile; + *out_x = tx; + *out_y = ty; + } + } + + if (best_tile != NULL) + return best_tile; + } + + return NULL; +} + Tile * find_tile_for_distribution_hub_district (City * city, int * out_x, int * out_y) { @@ -7766,6 +7837,8 @@ find_tile_for_district (City * city, int district_id, int * out_x, int * out_y) return find_tile_for_neighborhood_district (city, out_x, out_y); if (district_id == DISTRIBUTION_HUB_DISTRICT_ID) return find_tile_for_distribution_hub_district (city, out_x, out_y); + if (district_id == PORT_DISTRICT_ID) + return find_tile_for_port_district (city, out_x, out_y); int city_x = city->Body.X; int city_y = city->Body.Y; From 4d62ae01ea7d6d86120c6e0439dc759183b71213 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 11:52:34 -0800 Subject: [PATCH 053/356] Add middle ages port art; Separate harbor from non-harbor art; More placement logic --- Art/Districts/1200/Port_NE.PCX | Bin 8344 -> 10517 bytes Art/Districts/1200/Port_NW.PCX | Bin 8420 -> 10581 bytes Art/Districts/1200/Port_SE.PCX | Bin 8433 -> 10704 bytes Art/Districts/1200/Port_SW.PCX | Bin 8377 -> 10508 bytes injected_code.c | 12 ++++++------ 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index 43960159456ebf8b9ee0e89ff9a78364ec720fcc..d394d027ec9ef33d0db8708d39ba71fb0a42d19c 100644 GIT binary patch delta 4029 zcma)9YfxLq6&_@)N4Sy@sx2XbWF+5UAP5OC7LveRVmw5oc5o86DGf%>kGl3?w?Uo! zNaRa9_lKu5nW4Q?H#5vYU4|xh#z`m1#J@U|U12cB7-QpC{KAf3JZ+G%n>gLmbLFNn z7`OSMvsZh*J-d6(*>BIjC;VyC-+!O-%ov-7hpeM{|A^dVUAF20BeTt&^-k}e6KocM zJ(9=q`xd*w?!$f79Ie>p8Is$q%bvZ5;1(g&ILN+1=#ixCMfnxG${5VD&X#D%Js^K! zwWQr)QQ8zuuQk8cCr7kqc?+lJCfR=a_BwCqE%%PY@<$n&X%lR3XT=v9)%0V`O?F5`*JrJ#<(N3DK z47)6kW?J;Uu(7Ewy5Agk1B}W$^%lbsh|;>2y>4?Oc3qQCm@S5*P#0ZCqwaKbEx>iz zW3yBpgUz(5b8~IneHV&Te3diMhm$;n!$}!uii-d!JC0LI(F%8MZu%6E8J{NTKB9w{aw!lke*FDoHXnW+g}OYg?Q4!BRhmC{~kiG{C;YVY&0yF zFQ=}wD%*Mh{YiP4^|3Q>K-v3o*|N0IHiX>?7);7O*3U-ZKohFZ!AbeGrKQ=5>+gW# z-8*PufDOa`reO2~M2x^0`L9chS119UfYV7izy@(A2by?VUs3^=3 z{E^zR6rc}o%KuR3%TwwV_^$`XEq|uTS-r~>UXr?vZmpifG%q_uE_O)`g4Dc)uQWyMbf=T12KV@WQyuns zcWtdC{Ukx@?lC!`ap`m^sp_5HS@@^CM7v7%X>-y-;gB=M{)!z>w_K%NQ3L3OIQ%Oq zudq=jHf*mqalX`QdrE4GJ+zaC|J)Xtz++4vS1n0#Qv0UH|ES{fUTvO*pclI;`G_~P z(`iq&JAeM(s{{_*l)unECl6~g<-6KEnPHo)E07CxcKJo!bMnu0S@Ju&l_vDyWJP3) zHy#f4IPI0b5qY03PyR%=Tt2Q_x$GJvq?rI2B?*^P>rBx(J@#RFPUqI+xKT1$LS};A z=ZEFYXR`S?93<1G6S&|>OhI7dDCgJ%j8XXz{=Jj@kS{M!$nTogXH)jcMRpDQN0ah0 z$~$a|-NYX8OF5FVEcaVRrbyz$kI%4i>^zs0%J&3Q81ic-#mMxJSFM(}SeGM7N+KEQ zSYsJplq2Vu!Ry(meIZGbMKkOJLq(9>&9AE7b!<_hyu*UcMpscqUWMtIeuXXdQ>?*B zr6PmRQT^6``BFc@R7E~lT81vH%Y8(c<4iIL#s9o16A37q4ofrehOO#1bvY`Csd^{>EdK*paH9|&|?NTuW+SF zB}yxKjkanM$ZS!Dtzt}J%rFIg%oyO!Ip%RlBe3>O6Vq=(rB10=1`rI@CeSKCZG@GLi(DiWw$h zKJCTX1t>3V2e<^iNGk~HQ_-TVpvz=;_9~il@Zv8uYb{kKwJD=4uPb1R)tL}Ad-n;( zz64^Ryi`;vwy0ujmdv!N0^NopjH{bl@?H-l^&{Z65?-# zSi4GSZQz1-=i8=L^ML)}iEuU(AEfzdH4aHI^6_ahgIky%x;}OayaW_q1XR`lbrgyQ zEF0jsLhm>SZbB5Km|yVQw0zK^nU6K}LRlJD=1}KlY+G;9WIO&KbZN*mP27itci#~B z3S(rXy)D*8jRqCX$j&1D%85M5FcT(KBF?n7l^l~q-X^5gE&}d>4%~%>b2+lhf=1p} zq>o7*baoCF-lGFh80GaUjo3=FN>zG`hI84_tAluv(l(Na$0}b~UszVK2#A7>6J3G z-)8dACqYZeXubl`s=&&5=~&%&fXIjoc-gh7b8)F7u=-ZWx<&AxBE-2 zSv9Ip*VI`3T7V-sKnWG;+rGNeCj@e6$znhYXZP_omqsgisZo?nWcEJHF9}XyUhVD@ z>AuXE3(fV5-C5;ED@o++5e32OHzgCDCW!`; zNGu@V(@b$;cSor}21&m}ZK~4MT!T@Y&DkrW%^W7o@M88JwkgcnG(=& zLY*O|xiuK8%EHENY6sW6`NMnoc9;)&5;6ZBfcY@1itKB{wPHaP;H%VTzS7_ys#O!n-ZqY9 z!4E+_M4NqxRpcdhnf;{$QyVw`$ghcmNVNNSF5i)>3#xg%n;QKYx3N-N2wMy_zpA^6 zUMo@jipdhkZ19UJTBg=|xHmRoUfjaEhAxH>=RxB5bW?x~lNbxKbGs|i3*WC6`KnzN zEL8KR#^<%(uMpb%LqLx|4sT#yxsvgxQ+O-_qD>p#L3!(ESJB$Q0_EenWB33Cb`<3e zb_)Y43Y87rLI*iU``ermeYwWsyn=wCr>h=+&WUb*LKLh!3En{Wgs@MAq# d6`M_Tmh&kqxso)7-=FSY{&LEv@0!w#{|2x|4v_!= delta 848 zcmXAnT}YE*6vuhlben7Qb>H^7&F!_6z}VJBMQT_QV{cHYjk;(#Z^iYoH@XNaBtp9A z>rbnj8dMMlMYL|a*fY&)+NMPx7Nyb$CfSEj(8crg?Cs|Foc}q0&N=X$OWjvG@0}4^ zrs=i8kr#A{G5TEN|9G0m3$#r0zXr3r{Gs78zBjsCrs$yBeqe2h{^s_g#tDs2IX}@U zZclf%B_`nyxYtb4Y+7xTia?+l;{XK))i_yDi4fd ze^b4=Of%W!+P!k5r-02;MJs)o8{4Dutvs#<>O*B3SJaFc5qlV$W=nj^bM{^1H;u1o ziK<;{`i5+84lwot7bK^Zr$|~|)f|&W#$IAhuB4Q#l0Fq9ax-JEal-4glqgY8W#fg4 zX$Bu8w}mjJpvL8aD9^k@d(dqiWvW8dED>&Ba#j7x(OC1ec5=lApc+yA>8p3 zb+S{)qf@^U_Zl2LI;i*Y$c3BwM%ZK*X7o+y=aq4|s#-yLB~i5whl38Gh`Uu0M7%Bt z)nVrhANz&9)%Zr^BrRcgbp*4LLnz~Rbr>hPcd^o`Rq7kYQXA%h@8obA)gI@OcH)KidbLc$9=9()cEj->#-US z=DBA4&2YWZ?1AWa;HcS#&mNnh!0zOi>dI*MyHPZoa<2N*y4-B=fna8pS$s2o&ADvT S5djnaArx0^%k5k_X8i|qLAAdC diff --git a/Art/Districts/1200/Port_NW.PCX b/Art/Districts/1200/Port_NW.PCX index d0d5aa10285402a249c7a76a535555366202943e..eff55508dd0956c8b2b13c4df4c37c4c4c46a756 100644 GIT binary patch delta 4265 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 GxZppiKGuu? diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index 88698227cdb99a2d0f61246639c8d509d4f961b3..95aaf7ae13cd2001f986771d8b72592a149f2609 100644 GIT binary patch delta 3813 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 delta 716 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 In=Yf}|J_xJoB#j- delta 783 zcmZwD%TH556bA52TUs8$fG`45LTFJ4pjLb>pfM!wjE{<%urRiN0Ev;VSSSk@B)ULP z!pc|^H^9P8-LP|RQC?!fR%@Z9yo=aW+A7a*#?#Ryi~Gy{zB!Y7bDrHAZtD{ki90wU zC2o=XxQn0V7!EpH+yg_i_^NYI;uuU<@TL(I!6D}f&0%*8^nOE;?({gSZM_ml@C)7E zR3^!7`~wbF!F(@{bLeJA;3dnq37n;&b>$kF!4yX2leR9WW_Q`I+Z{te^mMsft%&n9 zxB?MY1%d2g(>m;jN*LuTvVOrDok#MsW@dISp(L zdTKa0a~k*x^?uq%)|4`gA@Jrju_bs=$)VVIi7!wel0TI{lwUCb4~-Z2Jf%4qSFS1p zI0FG=3y)BW(Z6S1RL-Cu&YLdsFr{S~Q%043oPwvOb38;TF6WflskofNNeG*4EC@F( z9IA60`3&{HWI*}veH=&Oer_XNHXno6xpjPk&bH;Ga$NZVeGs7uVJ>9?%z0L>QBV6) zivJ3@omYGEuoE9#kNgEIw2@AKMj=6zgIs`rL0@0eGX6&C*mLX;yB diff --git a/injected_code.c b/injected_code.c index 26cdff1a..8848500c 100644 --- a/injected_code.c +++ b/injected_code.c @@ -2402,8 +2402,6 @@ is_tile_earmarked_for_district (int tile_x, int tile_y) return false; int civ_id = tile->vtable->m38_Get_Territory_OwnerID (tile); - if ((civ_id < 0) || (civ_id >= 32)) - return false; FOR_TABLE_ENTRIES (tei, &is->city_pending_district_requests[civ_id]) { struct pending_district_request * req = (struct pending_district_request *)(long)tei.value; @@ -24127,7 +24125,7 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, if (*out_variant == SW || *out_variant == NW) { *out_pixel_x += 32; } else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 32; } - if (*out_variant == SW && anchor == DIR_NE && anchor_sprite_index == 26) { *out_pixel_x -= 4; *out_pixel_y -= 6; } + if ((*out_variant == NE || *out_variant == NW) && anchor == DIR_S && anchor_sprite_index == 26) { *out_pixel_x -= 4; *out_pixel_y += 30; } else if (*out_variant == NE && anchor == DIR_SW && anchor_sprite_index == 20) { *out_pixel_x -= 2; *out_pixel_y += 4; } if (*out_variant == SW && (anchor_sprite_index == 0 || anchor_sprite_index == 4 || anchor_sprite_index == 10 || anchor_sprite_index == 1)) { *out_pixel_x +=2; *out_pixel_y -= 8; } @@ -24139,7 +24137,7 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, if (*out_variant == SW || *out_variant == NW) { *out_pixel_x += 10; } else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 10; } - if (*out_variant == SW && (anchor_sprite_index == 7 || anchor_sprite_index == 17)) { *out_pixel_x +=2; *out_pixel_y -= 8; } + if (*out_variant == SW && (anchor_sprite_index == 7 || anchor_sprite_index == 17)) { *out_pixel_x +=10; *out_pixel_y -= 8; } } // Sheet 3 else if (anchor_sheet_index == 2) { @@ -24165,10 +24163,12 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, if ((*out_variant == SW || *out_variant == SE) && anchor_sprite_index == 18) { *out_pixel_y -= 10; } else if (*out_variant == SW && anchor_sprite_index == 73) { *out_pixel_x -=4; *out_pixel_y -= 10; } else if (*out_variant == NW && anchor == DIR_SE && anchor_sprite_index == 42) { *out_pixel_y -= 8; } - else if ((*out_variant == NW || *out_variant == SW) && anchor_sprite_index == 6) { *out_pixel_x += 8; } + else if ((*out_variant == NW || *out_variant == SW) && anchor_sprite_index == 6) { *out_pixel_x += 12; *out_pixel_y -= 6; } else if ((*out_variant == SW) && anchor_sprite_index == 16) { *out_pixel_x -=8; *out_pixel_y -= 10; } } - } else if (direct_diagonal && *out_variant == SW && anchor == DIR_NE) { *out_pixel_x -=8; *out_pixel_y -= 8; } + } + else if (direct_diagonal && *out_variant == SW && anchor == DIR_NE) { *out_pixel_x -=8; *out_pixel_y -= 8; } + else if (direct_diagonal && *out_variant == NW && anchor == DIR_SE) { *out_pixel_x +=6; *out_pixel_y += 6; } } void __fastcall From 8280249279c6dba2cf1878bc8b01164a3bcc1581 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 13:36:31 -0800 Subject: [PATCH 054/356] Add intial industrial port SE --- Art/Districts/1200/Port_SW.PCX | Bin 10508 -> 12094 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_SW.PCX b/Art/Districts/1200/Port_SW.PCX index 029e70e938066833a500f7c7ea30905bd6f4ed71..b95ee048e80d5cbaf15ae2d061a3de2e8fad92cd 100644 GIT binary patch delta 2424 zcmZ8j?@wFT84hMDC4&njBhbXH)+`-KE3KN0h+)#E`ypMWiD}vwcS7nPP^tUMr+cs4 z-7nK7Etx+eS+c12rL(rAQ8cAWn)h5|&jcC=+@K~(u%QV`;vca+LNM+7?l~uPn+EcG zF6chbdEV!J&VfJv`s%xX|NTqWA}c?fF$^L`6;7aOer+dx%5ltsN%+=e=b@^p>z0aFdw zB~~G8sOO9Z=M12Zy2IOi3*g6GlB^#JN6${h?Wp3^++QeIfYe2{)xc~06OfxVZ$9#{ zY(wd7z_#%0+m7<>@yvkb48*Z(dU`U)RCNBy%Ch^czFA>qUVebu<|f|*aFf@Bs=ggE zn>x{Wz#r3cY%o;MYJ3-$QM1jUV9o4kVO)MW1xc`5LD)O1|K zQK5!yBt9o>P-R|$k?bqyuUyDs07HFU2gcflVG;E{X5Rr@p1%@mrIcfPam=#rQd#*_ zK!qw*BIT=j{|a^9M;LNEJ~90>jIjx*r}Nv9O|CG5C*X(TXMAm)5v2UM(bdo`gGmKG zbx4JLJ%ey$`qY@3Z5X6Nt?)WX0%5KRj!VmIU3SqBuvypt%%vsZuev@3T1D*iL2vdq zHG}!a)OEg#H7^}lukm`@eQQh9gy%?DHLg^YmbH+j-`Uhe82&YR^7PmbX!o%J@0xlS znH0G$PeBEo=26LP2^>wGcGMPP;n}3-3>l*9r(@L2f{y-^golP>+tfS!CDt3#(&Ea1 z!<86Rmhh^mCO)x1MSY89{Qvzn`-$PNZJ_o>!M|r>|4g03lXn zOLtrMFbyrUkc?2lraVSVl^FI$h6(2K2jB7?xDR=&I7_;&@fTRv5;G{N zrm8xD?W7zy=O%G9$~4@-iNIFqAP%~SI{EY2!Og_kW5ZrU;1lFPR9Ux+ceFhH)THX1 zv9hk5jIy2i6dGTln_4~E!JMd_CYg&U_pP_bDiCuAan*H|Z(?1S4Spg?!(1`V0J8y^ zW-@IjK2CWrD^3=zr*TT#diu1C$>F?INvJR}ojf%*s3W(I=;?YB=Tu}STs-8zArVyY)Mco{!#$!#!dhqP6C zNc!pdXG6~&rjAp4S>S9M_enC2%UgUMBzL;2TkYO9BsxA@vW&AY@>p)^_V=yTvxLIu zR|(`Kshx2y6<-!ALMzTvfKM?HT#6<<0WK*@42DNe>qY^t;H+&JUAT|hnME=N!Xg%*H6!aot|zroK3tN);jPG+zg-s z5rDrMjtkbA^r%&l(A3G7kN2se*ZR6%Io$oz9QGC26TSDI;4d;(5rxau!#F{-p+{t< zjx|YDRmcAoUrA_uqVL7tANO=0d*Sd41pqY+;KbX#{qM9S1EUnx(LETn2zN4~Q;w68 ze<1yD9v|sF-20uw*Z&JNa80u(Ih{ECb-?wD0|Ru`nM6~!zD^{2M-Dv^Pazn{F}xr( zdUP*w?#Rd+Pb7q^B98pk?f%aHIa>g4Lsr1+S>e442jA@ak`C{`FwNj?Z_MBqO|NsA=yjDqkv8^Wa ZeDZp#l`g0~r2;AOK{&K+*sJ From c86449817f5ff04763d47fc4e736e69191eb301d Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 13:55:45 -0800 Subject: [PATCH 055/356] Add port SE & SW art --- Art/Districts/1200/Port_SE.PCX | Bin 10704 -> 12928 bytes Art/Districts/1200/Port_SW.PCX | Bin 12094 -> 12713 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index 95aaf7ae13cd2001f986771d8b72592a149f2609..462de498e0087544842b7203d36ce2b670663dfa 100644 GIT binary patch delta 3048 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 Z%M|rhc$>LK~OSQ2Z&d^BILMdFJ ziKvN0Bu`UO1NV_V`W3xk-pP^{)p6-;6LXz4d?yW^FtHWgk<;KQ9;10tlP3tdMNx}K tfO{NhGZwsDu)_mfcqfLBFYn0cqZm8D>&hIev3+9*6+bk_<*Rsd@gI1ij4}WK From 0393074fb17395632eee461766bc529479e701fc Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 14:10:25 -0800 Subject: [PATCH 056/356] Add port NE art --- Art/Districts/1200/Port_NE.PCX | Bin 10517 -> 11982 bytes Art/Districts/1200/Port_SE.PCX | Bin 12928 -> 12935 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index d394d027ec9ef33d0db8708d39ba71fb0a42d19c..d7b71d4516f0717b8f2a7010dd7748689d5348bd 100644 GIT binary patch delta 2221 zcmZ8iQA`}y6*c216dM*3cfA|Dpn`F+T{V%l78$6jYScQd6w%iGaVpx6eDo($l~2Cj z%(|Z6N}G=QWofGs+5V*4meh(WTTXo6ENs>WgYh&Zc3r^Kacp+kE^EMG_1&H~U@IzU z?tA;@-uuoy@7y=P|JjXS{`DK3FH9)0KxiC5-kCs^9U>bjJ5T zRnmxcG@qvWXWm;)@qJs$lI=&^2GXB;=Q`sjHkFK7t+Ce8P^~hn9Xp5lL-jzd@ndBg z>C^HhIXdT!BiouXW9ei4qq6^iH`{cm^*<^$y#Mu=P8=WIJMArX#@lL&)?>-eX4<5! zDet>IiMFbe>$E|e@(FES^EwW{+_v(Sl~25PdS3qaL!|9!ZQ9d6shF?xW@OhRdq;ha zafTnLQv<`_!^TDLWZyyW&&G@U@2gaL_;|k@mCEyD$yb-*a`v@-qd#Y!=M_ZFqY7>R zp>AQk!%K=D4<381{|Id{f9{PRjwcteM9=X(Vw$2=o5G6M-Wxx27scT9F8GH`C0dh< zL0I7h33M-P0#xt;ei zR}AeO)u};c?-#wvcP98kTH3GwsQ=%L=r!aB&mvujX&5ckZK~&Ks>-^s8sCg;S(Pw0 z_=YMml?R4%$NLXVuyj+m86_cZ2~Q|%NxEuMnA1OOT6Nr-_ik^(DRXRK`0pnU>@Bhp zGh>E7&w-6CYg83Ed;B9z0WeFeVzAEFvC$@%BRio2j8Az|U1L|XX%9E;m0OaHp;$_q znW2yoiTx$0kOBziX!~P~RH1Uga7ZQ_T=LGR;?8xpTp1A_KrI9c4KtONF>~nco4mth zWUJDlMf=iUt`O-wje>7wiLJJE)=-#f*Y1oGzzQP~G<}xEC(Q&_P^0Pi8Oxt$THeXs zMOBBY5zrD{rIOGTQRT8B2C1>-{q}{#4@xWy>v<}TvLjw}jg*-k%2?(#ooY!Z({&=c zrcXHd;{_jUq3!qr#Xj+7QVDB^R!m)P0aDu4QOA(sZ&A!9Du}lH+vsO?}d# zJ$RO;-*=>M`7~v0@(fsNuof@)SNbq(Jg0DOrv_TT)^f6Wuhyf?1Jsy1;#zHH{(N=< zNj0d8moEP9efs*Wo1_M^3R+E5#qhEprgYh?UHn*|+JI4n#-k zC!{$FzGD$t=Pn^oIF`^tXn1G35>Al`snF3-3uI?gMSveOo74MJZE{g-`n@5d%joP` zI7(`W!OZ&qwYToB1-=JnK)j$SeGCXk$CeFrQ7))G@5@DOviK)C_HN-rlKWgD4L~Lz zd(%e}L&UC-kU+(ux_H)+&`Q*a8sY9lh96uRzk-@vNn^U2HSZ5yiC1(x_)=pvcck&vgCM*ne&2z4SQnv$F$~xCj-7T(0 zc7FG=T|=!@cccXwuR;T0T3-d4Zv6zV{Sj@uG-ZG6+N)l*E71>d4dDZuRWPUl)q@>u zMs@2vc!lq@DP^YO{iH8(RI=k+BcL)vWiVHdQs-SvBF78hOg!>>x)Tnvh628p81doT z&@TES(UQ_RW&SLMU*fyP9Poc5yP%d7Cb*My2^Jvvjz#5MI!6^iq7r_fhn@?HQ4*n_m$NQHWNjF%0qz-%^FPuAv981a1b$zuQ00{un*M3 z=o@_t5n5H3ealIMlhxdS&QImLaQx~Ad__IN$~@cyOXR6^IhSeAXs6Gj7oWnesH*w` zD|d9svq*|Y2y`=Ow|}Ohcei1ys;2H?X+ir*AT8hh+tcoimcBRkwfyu@>^I$q-s?R) V`$}K6C-zPITlxx){`Ro`{~t%f?Lq(m delta 80 zcmX>XJ2hy7g4ARQ#7OF53ok$3V*QtOro&nWOKCR*d5|>nUWP9}=Xu{#iv8r~guYghqC(luJWVD=oPgM_W fG{2e>=Mj+jg@Y$QPj(QO+&o+73DahCO;1h$&(Jp_ delta 119 zcmV--0EqvGWq@U{{UHOxL4A_}B7y>~!jpy~b_UD;#6iKylLRAa0lSlKBVGZ+lg=Y$ z1i{C_@sl+qOa{Ti!O+3(lY}Hg1n2+3?32qROaj6|lNlvJ1MC09lVBx20qv8cB|!nD ZljS8Y13|&UlNKf-1jYZs!m~UkOAP+kHkAMX From 311a6a742153a642cf233ad1d0705db705f1a097 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 14:55:45 -0800 Subject: [PATCH 057/356] Finish industrial port art --- Art/Districts/1200/Port_NE.PCX | Bin 11982 -> 12005 bytes Art/Districts/1200/Port_NW.PCX | Bin 10581 -> 12176 bytes Art/Districts/1200/Port_SE.PCX | Bin 12935 -> 12974 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index d7b71d4516f0717b8f2a7010dd7748689d5348bd..7eb5ad96d87c1f7e0a9e84e5cafcce882926100b 100644 GIT binary patch delta 117 zcmX>X`!se#mK@{4$vJZIjEY9*8sd{(UGAWo(&zT|S)2 z@#|!Lg-Diz|64v!?o{vtF+NVd0c0Hg-|~L4x?%v!vHy;5Cs!y2upj+@sO8|vSCbcL Vicfx^sLOT&$ZvSDnNR5e4*)A+H$VUY delta 93 zcmaDFdoFfEmK@W;E0eS3;u&{OJ}no`xNx$%d<3K8N*z0MRlet^fc4 diff --git a/Art/Districts/1200/Port_NW.PCX b/Art/Districts/1200/Port_NW.PCX index eff55508dd0956c8b2b13c4df4c37c4c4c46a756..3c7f3584d35b9bb8b43db9f4dba4e07404a56c18 100644 GIT binary patch delta 2452 zcmZ8iU2GKB6~=29^Wz<3(6WoMX%aRV2dRJSm|dzi8dW5%TDMgq)l^o}_ey=>$xkz5 z+Ig-_=Mib8m9u?GQR)Om6%m1R$M$;38viYr;yB*50S9dFXm>GSSKQO@&e}AptvNG$ z@A>)8ch0%@_Pc|B_-8cK(0=N4yVDu^-FF)I{NhxjV~5^r5bfg4Pv=9ahTZq1!7}Su zt)V@0(rAgX9CLnC7uuhfMxeQc$}C;y)HSI!8bbl>z_Dp9SsLZC2eSt)<$|pn^ot$b|mOZu|ZGinRrf{W#|0SBSHGs ziswwe8s2w{<628o-L8&xXF)r%r>nc?EyP%LSPOkH&s5mpwpdSRhvL0BriOXFEc$!oIKreoHm8?-TH9f z0#n7!yYN7Gk{uE*_IA!pA{{RlcyJyKCH8Xbwk227kA`&<1MGTJ^T}tYzi2FToEMl#&y~fGgJT*H4pGKjf9avBBsD3Re2SR zme7~Z`Qu@$#6m=@mz)_y*FL_gN+B!&DJl*pdBhI1VSF@TfH^&PpEZimI8UOVs;s)I z${Cr(+v6Fzj@%4UV>6}l$!Dm@QH5sCfL>uVe4JgoI5AtiGh~ao9d1os{iSj z^L~4HhgYG4S2K0#@8qJB$P)&-iwZZH)@(hXFbx{cY(1#i@xi@xe{aK~QRia2;sR_cGSNTTk7%M-8GBSY zM>mE^d?NC_BMjFSDkKMN{j#w8n&Wyr4N%3g{~NWP!7ZU_z^_d?!!qEJsu7!}KKk30 zt5;AV9~MfiP5|-_s+#Gx=+h$uwl);UbUMLkII+e8jG{kN##K-`rNiafE1&%uO-dCt z$#9dzT&k`EF>2YPwPZp#i%9A*+b$;KvXL+XS1;&9`j5%Qc;tO^Xy$tb=1&wL6^v?E zDngZ26!U+l)f|4!a!G!}_80~-LPE!+WIiW4^C1BEW${^tBjpxV@*mI5_oBN?mL4tn zF4lcHC;yH2Bv<4^m)wNFODgiv}2Z&Fqy|EO{Sg6R$CVl-qGF$$E;AO%|t+1F_6PpYgiszt+F z85dzvhM^kG996e~HN%^}T#)KSby>CAY z7$;Qh++Q&I^pLaWglP6~0!H#Mnjrx7jLbtXk6?7@A` zzd&z7K9DQ^#X5=mLY?|l73o^UvZ=aGQB<*DS715ytt zoXqN!L5EP?dk=D5dSH^}qMYW-=w{(F0As%d2_PJ3 z23Vz_)V_xFWLbWV?`1iu?wXu6fHc(gy=M5>R5tg0jU~vktjc>>S%J1CaM)+xK}R5p zh_tf&#@~AgS(O{|4)j)`7oaShVq)~5T2RZ6AvfeRIfHLcxzgI4A2d}y{p%}3d!oPG gxA(V)wLc#_{NZayr@j|me^onSp5mRqU9A593+srb>;M1& delta 101 zcmbObe>G@>iYTMR+I&)NF^39J5D#GZ4}t($X*z8H diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index f08f0560a203a60a49954098b882f03bd3ad8877..1578f8c03335c51cff896692f28300a933b1812f 100644 GIT binary patch delta 119 zcmZoqU6;CnNt$Wl!O6_hfh>prJ06}K3S=DlUwd@&a%o?dqyKACTN Vc=F0*cKLWV5WC^p=5%=lVE~7|J0bu8 delta 80 zcmV-W0I&b9Wrt<30viFslLQ-40mqYB8&Ltuldc<00nL;D8(sm>lVKcg0n(Gx9CrcK mlTICZ0oRku9drTOlS3YF0o{|f9&7>MlPMox0pYWbA0QI~WFM>m From 3b3251e747591a55f8be65c6ed53535b25da7d91 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 15:22:57 -0800 Subject: [PATCH 058/356] Add port modern SE & SW --- Art/Districts/1200/Port_SE.PCX | Bin 12974 -> 15431 bytes Art/Districts/1200/Port_SW.PCX | Bin 12713 -> 17015 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index 1578f8c03335c51cff896692f28300a933b1812f..1c9053014e7fe25f93c9a64eeea058975502390e 100644 GIT binary patch delta 3285 zcma);yKiG#6~;%oitZ(1E~0UADL_J^0Ioy<%qR%SoZ=P~som00@ej}(?-bKer$R`U zMkoaZFo^ONu=a5t&SUbh^O$Fj?Icbd=Qzhs;^fZE-u%{aW;DzU4=HEa&RNfIeQWLg zmtQaZ_Q7wyZ&sb8Qaf|jTPFj#mMiaG@XI@w$yaWk?@Lbl=rxO{uI&FIXR>7n-d$(! zzCyloFI@$_1!q;SF0u4ZT;#>dgez@#gtZVM6@$nVju z{GG@LicrfvMjj&j?zx*wDim~Ay-OGWy~upe5j3$T?6?X*>z9cN+Q~sHEHH>Z3$PpH9U>niPmyzp^yhgb zku(R(08DYSRVwQOx3;JIQ(F={v2FaNlU|XKb!m=OK4c3V1-UkqIG~$#WNHT_vQ88q zY!GmeT^TUmv{y&weUicQQ&iHK#bFShl& zQv|31Y{Q=oqc!B{rn+}+qI^MW2Ab;D8RUvI>|8D@eq@JGEAA4xgghW%6qbW9N{sYJ zY^fN?M!sC1B4o*?K)LS|N6?tenKO`lUwznsDmtkK45p>_h14oOG>-_g=)OYEBbx-1 zm2e>lt(iu*B)vdtjI~mS7#;AhXb+Bj=XHI!S1oxsKG*QYdQ(lNkUITP!THi!AaUo5}kHmWRq`Zc7);k~H%<9!aM)yiE;KY9m%Dla^+K+kJmHL{)2Q z%Tc8J;zMRwY4mzj$nlXvq02AePmg|;Y&_?s$2i0+dGD85(kh?0>dls`<^h4VP%oCm z^lvhO%PDX(VAp#J;wgLvHNdk*y-y zvP}LDt65orY{d~B7+f`r$~Y~tpe+Zv!jWNRdcVU8Q@N!*O0x;;6N-!uv{jTad}?&4 zGOd()K1_y_)wXES|Fns`xyOh?cLs!P>VT3;Z_#l2<^iQe{3q-hl~wJJrx_fM)F01r zeq`*n6H26~!;TOd?zd^lhgCTOZ4gf04+FAG@YTzz)^N-l-}T@>Hmn@)_n3c`8*Yuu zlno<;O_!oclW%>SXaiRL<2O|^n(gv`(1iX8hl!mC!av@p1)EaKM;a8S7Nc0Y>|K6R z^`7pZCiAIs&8>1X10)zs-DvlXtSC#^iLX0P#q;m!tiTT@dU96JL>rfH3LQqF+^)oJ z=Fj^U+*i5kmbu5VV?)D+8+M+k8idnOYm+7?5AS)s=>uxHADACYx`?x>%%P%Z3vIuH z!xM<3a@ke5*%`FGf9yB8+??vku8eKvrV5MMs~X7$;jAnuuwp8JqhQcrT|+!&eoFNO z8hGEOB`Pc2Y`3y0c8~+JR z=tyt)8u0U@xh4sP^hD{GK%<-5G?^lLz3J{u{Jqb9sOtt|azkMY+3F#T`g1r5ZAx6h zGMsfzJJO#`XV!C{eZBzKITkaV$e|kL+uW=tqJW(@E7bx!M^>5Nj*-sHC*$A#u5u2_ zpOOw+MC_MYA6Y7;JL*;{-I-sEkAM2oD}Q9+%hL2WNbOhS<3D-{oVcb>5zl(}9poxm TUpCzf^}H^XzrXtYZ=?SL6ydWz delta 242 zcmX?Ju`YE(w&vs?JUSCMNlkt%DK`0xg@pdsf7ky1|M&XebubBHzXFPU{rCCbEg<{( zzuRCE#C`@8`8>HnQgU*Rg~a4QNr}leKw1$<^8o3O5)zZ|m`hA9;Fg%Y%3Nyl1Rz%8 zmYN&`q@B#AB(MJa|Nr{`S77orn0yH)uTJJQm!AAlTx#+yGpWf3fLNY`ZE^&c$mG>( zK(;E|-{y+JGm?W5fV+x{wPj(QO+}x=4glV&z HB_lHcaGr)- diff --git a/Art/Districts/1200/Port_SW.PCX b/Art/Districts/1200/Port_SW.PCX index 24a99a5261e94f3ac7be99e60ad32e261cd3f2aa..7e8b9cdea66626af8a9b3199d01bfdfa39391cf4 100644 GIT binary patch delta 5324 zcmb_gzi%7a5%$p#0_dUuX$T{A4hT}D%K=J5Y;i@3s~1R{E`7@7Q{n$0RSL z&cOWv$-Lzc(UL98TuCSUPFj+dC{ff>yDO3U5zpkCrD&WLiWcPnB$wKKGw++3Z@zid z*MA&7{^ZG@er29Bozn#kzh{am5MF01H}9?Cp72|)N`)yBKD+)_Wjmw|I{DW&QwsBT zQTX|kXRCPjmaq7!5Le(Q=BFn63b(s-1{mk~w3}?zTa!XcgjcSYPg_&Rnk&BG6`{z~ z;Q4s`wamGn(U8uceC>G7I?%&)LMpAYYpq{5gFge$Ww_IXpzIE9Jz=y)aaQ^(MzaP!A^5DI@@ctG1#{yUw0PUnBEX>}3`P?5cP zI-zA@JF~?rNm1`LDP!k)v}7YsGqr1O-17NvGyoBn%S`{a10R(5_3z7Ew0+vMU2NWw znJwP(j>O#Ob%gCac08oJZX=T`0fG_!Vd4Yq-+Q3U@fnfh4?3m6Lpre+coI{e_UEW$ zyK`?p%KFN1OZuQ|Lwv97cz$Fk&Dm1Xp>^TclWtcy_tS@SVBk>SG{`A6s$4?1H2nO3 zW;8Ldx#WF0<|Ez^TJ>sTu!`V0leQ54kq13WztMW4m}6uYG}#ad{v`UU>5uTH9zGUv`+oA&iKD~Xf4|*{Pk~VUa@Idys&9B9?$0#JABhTqI zaH?Zj;8Iv{ZNa)=SMY^!ZOUp7_z~&5)f6l_8TuGoHjwcESJP5WJ4!y*&&bUQUrHNt zx+8Q#L9-ho(R}r%`c^Bejpx5A0ikTDO69K6x6kO`tkb*+Z)nbH2p1RZDL*@~#A1KZ0Z~cM~Oj+UxL4y6I3Fz#Zok&?X+g|VJ12W z0rRyq@MB1%tfwODc>hF(bee*vfuCW{GOQ`ZebIe2f69%9ThS?{c}I33n#OY$eDDhi zPavo&r!91x>K_6axzJi6=N;;X_j5JD(+=?O$~v#%iXUCp_r3xKXa#1jZO|L+uk59s zM@d+p@;b zaD5I84s@kgbQL|dkWj^MyqdfMd<^?mC2LO=N6m2`I?@AuD6OfjxA?v>4n=AWI*llg z^b-W^9-byS+6Lr~Ji~8Tk(ZEWh&VPMKSzud8hfenUsP<{h;`WU)<+v!Hc* zlw~SPoZ1%RFe2gIeVGxCNzOI^`bbu|j_al%bx0HB(bg$ilb6^DV3i=a z;H6|h_LUH!l77jWXW}fpXJ&EOJkH3cM9+We`eGXYCr8?-77OC)z{2Fiyq?Q1Bf_!l-Opb0W3B{)5?@3t z$AU}*zEY}S^T30WwE$$KqfI@cVF0*3Fe=@i!gOYn(OwvIqdm>YgC;ysF7IL6k+>;Q z0v(u3Zv;>EEU(dH$ohqq@y;{bjkou){gqVN^x-@^ecU9XtML0Y9m^=zPt0)p_BWK|pM7n-vCOjPTL)$4u6Gb`&Avg#YkWxdd zmuQbkcgM_E4}IuFq|K-;c8P)dqd&$CyTZR!o!P`QgP<=UV&iXP1yYwC==(89ID|T9 S*KdzL`*ZHwfBfc?hyMi}XUrD> delta 280 zcmW;FJ8J?#6a`=wD??)CW8GFD3riE}nxJp1Xy3lTo>(xmQtOimnf zfWG6EN!_=Yg!GY1Q_r*M75D4VgGCsF_i*jn6udap$9Z=hdV>v|GP(z2@D2u9>|~vX zUluva%;cOl@kB)M%QcgF7Ev Date: Sun, 30 Nov 2025 15:48:42 -0800 Subject: [PATCH 059/356] Add port modern NW art --- Art/Districts/1200/Port_NW.PCX | Bin 12176 -> 14750 bytes Art/Districts/1200/Port_SE.PCX | Bin 15431 -> 15433 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_NW.PCX b/Art/Districts/1200/Port_NW.PCX index 3c7f3584d35b9bb8b43db9f4dba4e07404a56c18..690f7ce0c8c1cc77d0e36edee74318ea0aadbe67 100644 GIT binary patch delta 3642 zcmai1PfR1{6<>@{RT6fSRt$t?6{)JKD2lXdvR!3!=x7dms4A59Pus@{94dL9pv za&IJ`DkQ6@;-08d552VS8^DkZ!06j8u>Uay-c>wKbPy@~r>(3hHGysuzo57DVReDddnfn8M)7q%T z5xsPf9lt-+Hz`j_RO!KkDEo`lJAEp764IXV9Nr)7TVj}6qEL)I$le_u>|0?Yb@Alk zu=U9{Q#s}g7(?0z+rDPr;DoN}B7SZ1uHI(L_S2C_8|P`C_Oic!&l-k!i%MxK+i6ug zN=`3Qh3d4YvhQ{e{1yx$6@|e^Ak+9!rr=N*TVj~%o#Y)TsUX!tm-GUzHKbNfR%KPR zRazAx6xu>W+rgz1yHxL;&VF@o;Da5eyfMR~ss)}4IRF7>QF$*h9Qq)U{pEw9_bET& znc#>OMQc)$ToE5tNTuC`9Z6QT+3Z){0}m_gPPm?v5%ur`LaL~25rRK{dgN&(u?Zqn zqk4As!4Etzr~MGI5GIL9h$ti>C_=PU{*M+!v%UzBFn73M=#=(x-DGO+@U-4zm(-x+ z{$-rzDaee#V7Q6kB#xT6nAogCpm5=-7>Z0m1C^BAuR>s&>CmJfAQXeCvVtQRf5dVquIBKU?B$zVtbVFn?7-}C z$NPwyf`8SaH9GnS0#>w;RhV6ZH%r3rvqy(wf~2Tsjii^e_S^m5I?p%V=ZHX)oj%8% zK$0Vh9h0~d7F)EI+5=U7`VTRu_0ZiJe2yaO$yrC#_m4#L0iAlgD)SCpiu})3+rB0f1f6x2Lmj zeZSxH-ATvwn;ykDIHRIDGy*t54a$2HV|0p~&!CmS=8*oWPJKY&{hvvGP?k4DZFy+t zc@sw0_=TYhTB4R`ns@V#tGrQ)V1FB+3JScf4?V^$Q~YZ}z8ZHBvNI3m7FaOgm`=Ot zNS#Yp`WAUA&)jHa617P*v79^Oq*OH3&LIs`9O?shkX1F9db7L71Nu6q^g48!@+3*f z3mm#3Rl#99TsCyYv}I!OSvD0s=W|rC=SS2;gKFd^)H8*az0b}R_qnL;Q`H%#jsrhC zbnMZ&oG;nAE$m%L*Pv$;(@AJiuj$uJ=)>s3Hm($niar&2fL4&oLFJRWuCbd(qGD9! zIqjopj)7>zEN%TC(F16?95~(@=I$B40kEL^R4w1ms8cAD_Jxj_NZuAaf}SOg41J+Db;J)6+?MGT3Ibt)41g~| zmHB`c`3$(vjea~qQLM}C&MT5lQm0$@|TYGg1qA>*Z}W1&}wxwnMAff}C^ zx}(e3)c|4mB%WcsDhmJ&l_kK&(^nV?W7K>^jlbE9T~VSL04iAMD9Axw8PlVNv>MS1 zA@8~rX{gi+w2Vb9#FivEGx2LlFhyhaB0jIuPbgMjTm`;a3*E=!9k3oi;Pv<0KqqUVEgk7)%xGWMTM4ju7kz}L zeb1ph>(b?0`5?LZ)u8D4U0Dq`WFb|ob_+8_z#$- zZM3o7+SmrWBq1!#k_H=e(V$D`>&)mOxqRNdnc26y@4YuqUOs+Xxv_8naGx8d#>aes zpPccb#3{!rU2$Hx;9ZJtdPVRj{w3Kh@rV=tBXQOIKm*5@E46vtCY0keVUyg>%`q(#XqyL6jaw)!= z+pH@kXsLAjs()Yh2Z_wu=$N0!wlI}kEQh4lSyyl1*rd7nsk4BdL^$tBq-XQqq8f&; z7QE*`6}j|Q@!M>&?tcYr!aOzG+|eXg%yn~3?2KOyyCR!n!bf(2YUb!=O8JMZ6RFpD z*Csn+$~``jLf!mg?uk7LonYb{ccm6{mkQT?df*f$zH>`53AZSe%(PRWmFY|LFnIdz I^ diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index 1c9053014e7fe25f93c9a64eeea058975502390e..64c72d2a96987e251162219653d35041a79f104e 100644 GIT binary patch delta 37 vcmV+=0NVe@c*%IMEHMM|p_`L1F@yxc$w8r$xG{+b!T-VXp+TdwMKS^kVD%B; delta 35 tcmV+;0Nnq{c*l6KEHMPheZljSFENA!f5HEyleaO61jGM-gtJF70t!RO5byv1 From a0fd014b946dd58fb8a953889411e0f4c424ec37 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 16:04:03 -0800 Subject: [PATCH 060/356] Tentatively finish port district harbor art --- Art/Districts/1200/Port_NE.PCX | Bin 12005 -> 15118 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index 7eb5ad96d87c1f7e0a9e84e5cafcce882926100b..a8acb10b87210d637d3c2669a28ea4bb4937c35a 100644 GIT binary patch delta 4531 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 Date: Sun, 30 Nov 2025 16:20:07 -0800 Subject: [PATCH 061/356] Working on commerical hub art, add buildable_on prop to configs --- Art/Districts/1200/Port_SW.PCX | Bin 17015 -> 19424 bytes default.districts_config.txt | 16 ++++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Art/Districts/1200/Port_SW.PCX b/Art/Districts/1200/Port_SW.PCX index 7e8b9cdea66626af8a9b3199d01bfdfa39391cf4..fd680ece52138bf8a481a9bd2f4def3f23398572 100644 GIT binary patch delta 2436 zcmZ`){clrc6!vz80d9qDB3svWu(b?CgQRnGGb$GxU}PFYbSP0&e()n|G=A&{y|-Pw ze*pUapmE7HIBTM?AV>(o3^?Z&O2>e1Tp5aEcg!#1w%XP?SoFT0_qH4JD_P&0_P*yi z=RD7I&L%aQrSB27Qq zN=cC0J*>>L-gAOO#uf4g(H&}f0VYOl#kHUW(%g=-ZJzZt1`olpa)YBraG~(7i2M#r zST?%cC56r6T8wpi?pk`BAK9ns3XH`xmp8PtDcTf)sUcg&1%cuQ%7tviBm3iAISNzQ z58O~tlk2+{?2JC&4i=;aZFI{;?xHr%R#Z0E^>78Cd+%XQ#xRVbgBfjv0kq)4w|2-C zY{pV-`;)y^x#jj=t|48I!!d}7wK=6z&MlA=sVfu$?a zmPQS?$>mW&vgL=F zb}>^Ap0MG!#^r13YT?=y)>i%EcH$vNsNg|dPvJ0(X^_w%+oc>NtD1lsc52-)^^&$< zH*yA|tO-FEnP?y2ewCOs4XYK>F-ib4-ua3BT>WNPa6;h2lr8VmA8gLg@^9<``wuZ+=`QF+ zSqaO)-deIl>SsN*zUC8L)sCvrOUaI?8W>Iz`+MaJM<$-DIQ?APba!z zgpDkDdV3$INp;NQ6h$i1e4*7!eT^xQ4MT57oJSBta9VWM`;JE3m`IXM8@so;%hChh4CWfByNEIK3<7=>{dO>h`hWWWw5hYD#en!6(A zp@R;jThmsCah1d)Bha4%b(%;@1X10vscwj!Fth=T%C@d?6 z!T6hD$(opprs2j>Nbe$6Ce#+G0IywmuXcOscRY+rRJ5BVuCUybVAZu1cM5MR2miyF zVoeg!g4}EeDWRd`KZEupuxDzwY_+%x65)stNG=v#{@eH=$03l6|I3UyBf@y3y+e(X zWAc*}yIP3sQ0=BU-}A8Bn;<7w1%>_uFxz1=wdgnu)4;`XAoO`-ZqsfP3BB^6kM@W3mtz#n{9C``9yn-|B?eo;0?z9FgCi!VYnR{@X9F zswu*fK#}AtE4b$b`@p}BedhPwmY5M9tr_X_`|%`q)i)H)owvHMpmI*xoCF*6-^0fJ zJ}FKT*A$)N#j9!-&nugEW7#R@3#??T0=Gy)uWIQM+ZLW~Ik zN_Q`ovclmNgM5(UNBCK1s)qPw`ZmNz?6fc~^DQecP5-mpOYEV$7q3juE_9Gzp|}D5 ZHLW$y&+&2ROxYCc_Fnq(rDgNIe*rDQH2MGl delta 393 zcmWm8O(?^07zgmy{0`=&)X@GL^ESpdiz0EFm#Cd2Zj2mUCjL#vqaw|U!Cf^k zGOP$3)eA*AR#?u_gPep-Ux{|TU3~B?gaiGw_+bU$sF@Km>j;|K2|?teU-)V#SwZ^7 za@{QJgx)b!zsNeHR~Q>QtPnff{N=r;GlR;WhYY~fBAwwSQX-ES$!W`!D3 pYnjAnbjqK0^-e_Bi^asrU>AC;_ diff --git a/default.districts_config.txt b/default.districts_config.txt index 97938c38..2b0ebec3 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -8,6 +8,7 @@ vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereq = Warrior Code dependent_improvs = Barracks,"SAM Missile Battery" +buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 50 allow_multiple = 1 culture_bonus = 0 @@ -26,6 +27,7 @@ vary_img_by_era = 0 vary_img_by_culture = 1 advance_prereq = "Ceremonial Burial" dependent_improvs = Temple,Cathedral +buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 2 @@ -44,6 +46,7 @@ vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereq = Literature dependent_improvs = Library, University, "Research Lab" +buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 1 @@ -62,6 +65,7 @@ vary_img_by_era = 1 vary_img_by_culture = 1 advance_prereq = Construction dependent_improvs = Colosseum +buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 1 @@ -80,6 +84,7 @@ vary_img_by_era = 1 vary_img_by_culture = 1 advance_prereq = Currency dependent_improvs = Marketplace, Bank, "Stock Exchange" +buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 0 @@ -98,6 +103,7 @@ vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereq = Industrialization dependent_improvs = Factory, "Manufacturing Plant" +buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 0 @@ -108,6 +114,8 @@ shield_bonus = 4 #District name = Neighborhood +tooltip = Build Neighborhood +buildable_on = desert,plains,grassland,tundra,floodplain,hills advance_prereq = defense_bonus_percent = 25 allow_multiple = 1 @@ -119,6 +127,8 @@ shield_bonus = 0 #District name = Wonder District +tooltip = Build Wonder District +buildable_on = desert,plains,grassland,tundra,floodplain,hills advance_prereq = defense_bonus_percent = 0 allow_multiple = 1 @@ -130,6 +140,8 @@ shield_bonus = 0 #District name = Distribution Hub +tooltip = Build Distribution Hub +buildable_on = desert,plains,grassland,tundra,floodplain,hills advance_prereq = Construction defense_bonus_percent = 0 allow_multiple = 1 @@ -141,6 +153,8 @@ shield_bonus = 0 #District name = Aerodrome +tooltip = Build Aerodrome +buildable_on = desert,plains,grassland,tundra,floodplain,hills advance_prereq = Flight dependent_improvs = Airport defense_bonus_percent = 0 @@ -153,6 +167,8 @@ shield_bonus = 0 #District name = Port +tooltip = Build Port +buildable_on = coast advance_prereq = Map Making dependent_improvs = Harbor, "Commercial Dock" defense_bonus_percent = 0 From 2e5faf1cdc20597e2707496e06cc0c014b70ec91 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 30 Nov 2025 18:57:23 -0800 Subject: [PATCH 062/356] Preparing commercial docks --- Art/Districts/1200/Port_NE.PCX | Bin 15118 -> 18298 bytes Art/Districts/1200/Port_NW.PCX | Bin 14750 -> 18174 bytes Art/Districts/1200/Port_SE.PCX | Bin 15433 -> 18809 bytes Art/Districts/1200/Port_SW.PCX | Bin 19424 -> 21433 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index a8acb10b87210d637d3c2669a28ea4bb4937c35a..a6f926a418e378c0f5c06b799ca55fcb2b3b3355 100644 GIT binary patch delta 2385 zcmb7`X-rgC6vz8Cwgv$Q5EN!(n2lK(W)tcHBe+yc>ym0+ic%Z3iyv%K>5^gEgxDq3 zHk`(oH1%7X*4Wh#P1`ihePf};DwIK}AP9pnFcgLv1Z2>Adma-NMoHTboOjOuoOkb; z``-ESP8^M%;J!JT>ERipj0*NPtwlQ(XK$lYkNQNMouZTU66&LIb`mR_P!Gk~2|7kw zP#>IaC2S0rXv4&>)ZIPX34kRlqm-xa6k8|l#F8GgVO-hID0R2CBz5tN)k#iUOMJj+D@ucOfDW?>$|t)LlM^eO z;{!&0C=Jj6jj$WEV4iI)pUPJ@-4`+1fLeg|(s34X(1K)JuC|ibiW-D?cF5=j6npU} zM%f5WE?cxNRj;d@e-T9DC0rdC#F=0hr*d8>6HC*EC_hHV$pn9sOx9Dzyo|yTvAk$- zhW;Dsbu2nM z{I1-_4a1+xbVyW*&{L}Rz!sI6i@-6}E^Zu7s4}4uGvg4zI0_-v(v)fvt|HNDa`9ur zyWBw*g$?RwI0~E9VmPHvLhr147j&p~_+XPXTj5cS8Xxun%|o$tvIh&6Ln{{QJ&&7o)PaHyGP??$&JTWZ&cl&Pu zTQq}X(3!h5sg9B`X%5B+2DfsvMCU0PMXjeQ5W^QpbkYNN*=1DlSBB!52Cie5YH= zwL_yW%QHr+_z>ykgJgsRt{{|VqG?oUJVg2ykN_WC(26j+UzrK12tAGM`@9(~$fH@o zOhk=P5~;$vZqk!rZblr<6ohytq=vDWGz(26kijM>*Jt3H`-VOfs`UbFSU<%Tgo0iy_+u_93IqCV7}uxaXo(E+wCl7AZ}XD@yh)&( z04b+I0VRedIF{uGMS53kV;|`uLDDaPtQF~W3^ScVCM4|0C-5}HNOzF+!B+;=va5HR z?IXbiO!!I2TNGq|xMDCupFxg%NHpd{rcs6DC^hE67Gn-F<#nSA-ZlyV&dRFQuej_13OSeNq;L4wtW z#45Eu2S=?|B-Yo~gV1et;Gsxu2jB^t16lQ+Z9n{Av*S_TupI=Q-HN1o)?N-L?Run9 zoqaouqUQybV>6UFw8ArWJPltuG)SX1$2LfGD!2x)J2ydvQ-(M?<}8M9o#|XFTy*A( z1pc@^?m{x`>BP6C9TqIlMd~1w_+^4{2S9{*-DH@Ad9;^N@1d@wtFx}cKYDzp*?G5+ izlWok|9_xVA$*BU%=xu4*VoNlFnIuu&Mho>c>X_$7FEaq delta 1467 zcmZ9MTTIhe9LHD8a;k*svMdv_CGaql zE%Skd{ETld+ruJzSYond4`vTLE#eeK1_H`OfeN*@%GwGYkp0en{O!&*U%uyi&iS3o z@Av=bDTgZN0q(W|LX)+G1bq>jKRI!-=_0gQ^r z=uUDqdiCC(aq0;iQ*@LxfXlcpqUhGRimg3fs}9Cg26)A&g{VreUPZp9$;@Uv&Nr?> zSe^BPca5*}xc5)KaTRnLBiu9UO1Dsm%VI;(d7PzogwAzqQj%5AScjEvT+ubKm`rd= zlLpNuEqqVym!=dJqPb}igda>rEI@u%EJM&_XMXZ`#Uj+^RI$6{{bC-@E78U*BbMW$vuD0|3IGAS@4NAaR=HhBu4N$94Iuy{z4DiED1TKi4Z<1*CS(xK8; z!LAVk0I`D zkjHq0`}+boP9c9N`UqaD$k`FXDd?^+YXuRx{R~z}V;F&7D>Am9fq4^o*c&;*C;0L< z{ZG9F$gyWbhe`(r?WyYj(GPw0jH0K3kK#9cflv4hQ~%{1ez9l4O_d&!9I5bFrE`Xn zM=8$#?DIS{rj#jBEb@!E4V_OscoMV3?^DnyL7xR@9I3L}gN{7*58ZQ)T!n6e&ZlcTS%PS=?(L8i`}V#qYj$j3r>y1LzE(+YU)^CzuBdyRCCG)I`lFK9 z_4-4S*AG2SlGjO3o#eHw!6A9=YA9xj|0{gkkehlB?+|B8q!ElE7el?1u$X!Nr_Wwi G?f4s_``z;Z diff --git a/Art/Districts/1200/Port_NW.PCX b/Art/Districts/1200/Port_NW.PCX index 690f7ce0c8c1cc77d0e36edee74318ea0aadbe67..da5a3156a4c6e77cb3531e2adc509dc971d5029b 100644 GIT binary patch delta 2458 zcmb`JTTGNk6vsOnw57O$7%y~hunWuXBDcK=UW%Zgv_ed5#I|aiR%~LEwuwopv9kJL z`(S)%{%K><#26dXrfqCveP~~5=0g#og4`7pm)&w(uFF+KXL>$}1QvYj%YWzm&zUnb z-?#J2cj1^g$9`{I?@^=_QmFb&l#@<{=reLE$)5<(DKROk$sY;PNvfl!MA|G)MmeOoIe$1P`b8&Iw30T6 zlS@`c@gN;EB=U61e%e!j3Y$r5g|b8wwLF&x#e%3;^oRkft0%1y*~-fJ6>i7V(4dD7 zGE7x9q<4g($&S!OlspLyxZIs{ZxvKC5~KWl~VF}LDWI#e?o<~NGn8!#*t_# zQ7n;T3q!wX7lJCwNXvz$;MKfUHfzb!k%eKqctnL|QZ0`ylQ#t))2xeG&L{Zf@N{CH zgP&eAL=TaFOO^>`jF0mPb}#Mi?@s!v?7f8xMl zR-4?|W7>p6rX(itjmd=zrbJeTJEk<;GbONF2s5W4(wxAmu+^M`U1l?@!7;NFrDiLu z#}#uj{xX|bGyLXM44C6tD`Mi4XnzUuM(mDH#Qu0K^W#Lk(bFYrq(?b#6(um-@ZlA6Af~IDWMQk z3A*T;1jKtnQO$Pl<->$P7%ci73W2au@BzvUbDz?1LB*a=&IMw<;7!vLy-EYGlLW;O z2S+V2ktzf01n*OVMZBJO2%NPTJyX-f|=}bm023HRGYdTnbhQK3qz5?c5(eO0b(3 zV^gAj^$hOHsgBM<8wlsU!+0<8Iqz<$2`~4Dkx$nv(YShscgl?KNcW9<-J@@kf?^dJ_$F1r3${J7C@~Slh*R6I!Nzl3# z!kS8WaoM&Y$Cg6K`NFmhXKapXpylHq+ZIC8O8Xud?OBAYBldkbZr_F5_Chq<)$; z2N379v0ChP?!lK%3v0yB<9EcONgi@{#5;vWM93%Ek$oo_TT6wGn^2*0T<@xhxoU yMX?@4xD5yT#9cW{tUmg*nL&_BrXYO)h5k;y@S|xFny4HKoBj;@(*FR%wR%hd delta 1509 zcmZvcO-xi*6vz9(axE|eL(9h~FjGDp7`_Gu2at*aVmcL+Mk+B;($VVlV*um?^^iFsaZi-11d2xpy~Ah*Jxlmu;iW26lTNQtpMXl#f%nqbdujeG`%h-5=I@{loQ5949JxelKh z&4w!1e2#pbD1AiqFfLliM?sB6kR(j303=`BTbu}Dnm2G|y#>C2T!KXta z)!C41lUo;22%p%|5gex8xR}RBf_vp@?p+t9uI94n*3%2>z#K%SWq~y^2|iD=uqleo ziV3)q=7P?+B#_c=FiGu4>DkapIlrgpumQ?h7Y`ub;sT^thoy|&rPvG64Xc(~IKC?h zzOrlVX9koJ!Sis;uHw%&-5J5O)t6(AW zeIf+6ZAYTE$ln!EmUDm!vRz^uuIB7#caSA>>3i^VZV~%) zhkz^gQs&jbXt5Wvzf_J!;fCG89_{c2QF-~h6b#}0JUjcF*d#*X$2=>0f@9?8p+7Ix z?8i~^OQazT;}H1(4u}q8DmY@FmiW{$pO`=%nK}(e z9r-HN*C;kc-z!uvIm%V4&lVS|RQDAZXjC((DzWQC##_wrYl%arHN)AcQ{3r1#RUDh z;P75)6V#Xz;7O@lAOB*%=fCe{Fm}``Z}8`PU?hD zRGioMbm@RcCp@F_W428D8WziNyYf6+C9fcU=A8@dnRZ^g4}L;dpdh{p*A9BvHhG2d z%BoKp*BF0T~ITH-Zy&X;PT-!Y)FGVxw?^!YLI_l-OQBdlO9np z)wtQD`r=N)*EQ8_M#FBrri{&Noafitm_j&l4xU;|+Oo>|3TYTOv0oTd1qQWt4UWZr KaC5s>iu?yw2+^>HcKF~JcJFQ0qF{wR)r?Se$ z1m4Q}7++*niZP62H6ck?A%?L}_dZ&5Wg>v9JVkWRivhUw2T`Xl=6`>qufr9dy0NLR zh0aha2=5r)!&!q@#L;K?2up@NVin%(PtlZpKrG|;>^7|Mw1}FVQ)tQAC*}~~DW3C+ zn8DuMV`$2)6d_#Bt;bkyg_wfgSO<@>LX4xs_%dKD5u+G1dNE~mi6LZ|yfB$u!jF1W zIT}q)(T^WYyYZ9BCVKIgsSqnBgXn@b&jNFvj;E@;%*tMpvPV{=DmRtBsNA}3$ZopE zXH51d@EZBpMWz3K!trQY@(`Pd9K!kHw6;;6=jApB=my_r<#+;bP=H-nP9-ovgY1Gb zl)yn6W;Y>c5;#mF?8f9AN#pi$k0Uh3t6{l#KOr7VOI~IZkjrQ;No!l>c|dM+f+qRq zlko&jQjlG@qyz?OdP`EbiqjOPID4OpVY)@D><6~lcJFeFW@(uh0TpNIHZ8IrR`E8? z(;WLT73XPzX4p@txIhsKu@CNGTS!J|iKciFR&j}?111 zXr27*qbjb`2K9S+5u^Sc+@QPE%VtBxyCk^;awl=vtbgV@DND*9cmC;w5>wWdwXITg znzPe=q(pfaKC?hQ@``dt8R?2iTEmZKGcKETB8Dlm8;jjmA?xY z^4~;HekmTZyn#H+Ry7V=j^H!PZm!0Ft(6>&KdrS$vUyUr-&K&t&~DS^ zezUb=!B&;>-}@ZK8M_`0_Bu4%U*nhz*w13q{wl}hbw?*YaU9^7OglR8q_c)&(%?LS zubfpJlaTWyl3n{bCWl<_qSf^x$E43yjiAfJG0_**z*$tzF*#jSjn9k9I41t0y_jIv zk8Jk~u)0e)CXMcLw7A{4DY`MVjBesmN+n>s`r|CZ@NS@^j>D9is-mY*v#hw#pKn#4&0UkQsQH+IO6wHU`eXQfi|(O>G!ns>*yh4&fZN z0h(4Eg{MI>s0qrTeLpVHzL(}K?a!zc5Tj0`+ugWA`%d_vzC!IYu2O4~oI z436MjYE6jL8sPrk97dna8~Hy_gc+UznL_-Y=q(r{|b*aX>5?P zm45+MSdw2K4Ez6evC6}nhZgE)FwoF?if(28PPQ%uSl;bL- z>hz-Ob6tX{>K&QjVZ{Ns7MZh+;|v*XL}t;S6g2CO6j2j2L!%+;tS{LtV`L&x;E)~2fRuNFh7zV7APwu=E{>2 FkN=IaY#0Cl diff --git a/Art/Districts/1200/Port_SW.PCX b/Art/Districts/1200/Port_SW.PCX index fd680ece52138bf8a481a9bd2f4def3f23398572..1623760f4438975685ac68b4c585e314b2188c89 100644 GIT binary patch delta 5428 zcmcgwdu&@*8Q--jV`b|$bxqqmo3v@yrfIkIm6S2ZYofMVS@#k~V

Y|A0YxZHxie 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 M{@_oSPkhq(Prw49qyPW_ delta 1436 zcmZ9LT~L%&7{}+~?bQjDGBjBBgU@w^-F0OF* zKcAqR$LNQzbaO{UKx!iJnL&zaoY2`{>Jlw7Ce9TXb6+$_(TM@wvIXZwtBmo)83L}u zo#3c^{EsSaSc7e0b@Q*;z#37W{Rnp9;4p0n|aApO5;dK-pV|b z?4mGEC)YD|DP9^yeaidHwv;vW5c<@2nEO*}Xc(=j8<`nt>*+rHY1^3((l*f$s?zr| ze^38_?!uCBfZ3R_g$6Loyh#}l-Ke$daKv0rw{X_HSGgrFBE-ttMXM-?Zx(&11jQxz zZAN5UD(D72vD7O!#1*7EjF_?%Q6F53Hz?P{Rru`-adL4jUB&d`cc~ZaGB?v@{F=Fn zdN3zzJ#}MiRylRyWY#+B#0yq0b)d$&kvn_RTFjjtv96|86xs@@84b1?(s0h^QJTf0 z=w2E2rL4SZxA4pcyPId8wx{t5*X&MSVcrrGuWVc5NbVF;j>|UD&T&*;6l2mKXLu@m zW(I9sZJMrMc1ORqiw=&%@=BDwVv;L2a@8rZ349A&b3#!5&EJM(r|9B1Ap4^XBH5A3 zy(n?Sa|grG%6G&j)`g`%(9oo{H)-QqNDCw;^~<~bLm%&cnPabf5aqB4%NVWQe^?vQ zLM8KV$ooR zx1ZO#Fr96s3y5_ZQQ&k&y6sHoUVZ7zqd(E;%wQkiWBn{9c-(^GoO15!x*Qw#|NERu zc5`b^Hf*_-?B=rEY<6=~?gnOaZUH@oJ8u;`du^VRJ^f4GdgdQ_2?*z$1F zEPwWd2yt~on453Hto(AC;87v{gQNMom}l}U=n>Yq4l>_%>G8G8s*LgDw7Cz`7?!$| z@u@pY2?+t!vyVb}(PPAFPo_G;ooL2g)K)E|@K)hYx`RW7UiCKTy{IhOLbtK4s7$@dc^@3b`{^b& z7W>r5tp|})vPbC`V+i_^u)k!9dY#n)I7&ZK`dB?sWyJ2%Of~8kn9H^+y{uBT0h`P0 zYUKKdp(}q+VIyLGfeCMvThyovF~6c#>05vKYQ)Kt2(I)g zZ9L22HREM(qT0&pQ~c&#!7n6?>>48mz45BXs>I>SlBjyTCKcaRE_kNa`j+zB@%s|7 z%NO_T|7o9@cS)(z&7P7o`y8uE=9ko1weZ Date: Mon, 1 Dec 2025 21:23:52 -0800 Subject: [PATCH 063/356] Update ports with commercial dock art --- Art/Districts/1200/Port_NE.PCX | Bin 18298 -> 19386 bytes Art/Districts/1200/Port_NW.PCX | Bin 18174 -> 20767 bytes Art/Districts/1200/Port_SE.PCX | Bin 18809 -> 20975 bytes Art/Districts/1200/Port_SW.PCX | Bin 21433 -> 21722 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index a6f926a418e378c0f5c06b799ca55fcb2b3b3355..4b74bc79aaad8c46257e27bf457b1d533d75605f 100644 GIT binary patch delta 2159 zcmZvdU1%It6vvwlAz;#?FH2Mq1#2)0VK;3)+ssa4X|451WT^E)B%;y>1riEmH?$Z5MoUSLA3cb}z0x%3Ip6K2>OwHeA12^?l2~R(0g3v99jF zt29q(tXaB)R?by9kUYy)hSJDY{?BxiE~-jKo{7coyWks^MmA;0)(qS>NYOfNAniI` z`%C^A>*ej{c{|Erq0)4re=~&@l|7N`vNPWI5U22zjs0wLbrDffvcKQeo*W{z;^$DA z?O&>1l+VTQmM_Hb?6!Sm@hB5TfJ<|V@QK~|J%w#6!=$>1R^^-V*n_sODVOrvm$cDO z>t6>E2(;1NAG@@Eq3_nG<+t&HK~5SL@OTexSevR$zn~)&UZCHQwS1ZimM>IIxwCcU zn-bjWF3QgMqFF|2$k^Yfjs5PUYlw%I6mp@B z2(nbrlt+~mW!qXiBQw6C!-0m@+ok@fPfMKh>2il=Ad`Y&oNm)ZEfp%Fj|!Dk1zG2& z14A?sI?`;2bo!1bEPtSx|x%8#RKaY+BO=T@TzXS?n9xf|tTJE6Sz`Qu3*N z&p(0a4&|}bUo{Ia0NNk#lcHdnl!@@H`>I5z`ZnpsPRC# zX%JE4X;q%;e`_4A=R2t|7@<}8k7Ub9V1yVJA6zuB$P2ViDW>AA4?XLL^M-t2;EiKZ zm^};S%1BYBWo&qvmQxU{fOPB3MV$(y(L*Kx7X#6N3y0aJr6u{}fO$keO*s>GMSLz6Q;qLK7)s0k20uKCI6;LJWGh-FGi3sc+n|61jcE~KTehZ!fnG{bM0H)2-wl0y zBmxWhoMe(#fpP-BfQOAG+u#TX3YUcLn4;P|$ztKGiHWixPYh2U&cnAN?iP`&7y@GB zj|L#93k~z}$m4hgr*6K^Xzaf=Wt`eEPttVe-Obg`4dG z6ly0Zg7XzccPLv4DnUv|77o1EYjL|7oR8_95P*HQkwWc^DY|v!2M6DOqyy7TF&%b` z<=^T!_xmtqQ)0+eVnOa5eK!WW^W-6d2NycT=(*ikh#p}{;k_eiRP;g|o=^(2GmmTD zZ<~W`mcXn)jH&OwB9CjLho!JB9<`y3w1_>MoZ|DQm?=k=v{&~gF%H=(tgc^a&^fS# zihMSiowpMckA{OHP?7flU_9@57x-%6ut1+;=o3$m!6>{Q8m30({F#j$7isODW9d4B9z2du1RbY%DV WNV^K+9r@CDycIhB@YVR$*8c$?Owzdk delta 356 zcmW;7PiRtc0KjqHl)%e`3^8$Glo}Pt&*%UDf8VQv&_y~`MrENHuzPD`Robdeo8M1Q~_f^y(Z551c~#CtvZTt07~N`Ka+R~aGc6osHJtTtUYjD6!k z|H-yELSHy6hVh<{LYRNR6AGGtrV*0ON4)v}H|(;IG_K1rn`R6rLv24dVgP#xg<&+n`BkW}6JOlNAl_mcKf&$9UHS5kUBao8yh}aeZ|W2q={3Hl{oKfO*vWV~o86?F^&sUQQ_H#eo_omYyo+b~ z`%Lprs)YwUFSyYQJ^hpJLh?JTR&L!HF%S8vQqHMHOwzy6_)E&l-IC!Jvc diff --git a/Art/Districts/1200/Port_NW.PCX b/Art/Districts/1200/Port_NW.PCX index da5a3156a4c6e77cb3531e2adc509dc971d5029b..8d223778ada12f67887920c516122eb49c1c56b1 100644 GIT binary patch delta 5883 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 a%>0%G1hD4!Ybr{OGUZ--Z||BXM*Saeue=rj delta 1093 zcmZvaTTI(!7>Dy85+d6Ig^HW>45dffQhGkO&;q4@#s&l;JA^R0?7|DZF(wP@m5XMX z$xmZq=7`fJ%qtSSSZ3V#Iu$m8I}{BrnE|>=fo0<`FwW%r{1`W0?B@ODeZJ?-`(At_ zmjrrF7#pY-wvn#Z3(xUIwLze`Rc$U}>?Vq3R%m1u=$EN@KaJ02MxMXoflSNuF645P zu!WQIMqv{_%H6^`B#IOQiVA$9=)f4y5@jbW%5t1l9>SP%KT1_S!V*4IH3@lG>t4sW zN&>061sb&%SJlnJDn8X!;h|cIUQG*zG+z9x=@y<~O)rI_rU5rGfkA@@TZSZxi~)RU zY{RJ04~NNzn8}Q5rZ}cdZir?d`pss{nq62l>(NnI-obM(v7y_bGXt zS??uTlBf91UW-Te*WO>IOLT)VdWY8;I-Ge*@|hK~Mwb408Caruy3OrrUZ;1idrJN) zCQF%>;=f-?@-#=EbAOW8+w>y8G<&mZ`+v=zs%?xk%F8I5qGV&33kxJy`V0$=stW$I z*e{%t*_}0|WOV~3hg!neB>QOx&y!p+r)xOl&~SO0Wp?QzDRBG*w;ZZ+#_ljaKTj4( zp2NpbId|Ya_F{e)uHap#cK;?D=Hen*=2`(+rwIjTIb1HEWNlZ@k{nqfg$!H76;~T( zT;(viyU^v9;HtYHId>&GJ$-o3BgL{OiP4A@Qg0Q`sSe;p7-e4MrU@PW=C$JTa;Zo>0u7_fHE1bspa5F-Y0UVD+Q5ii6 zS2T)3v>PQcC$7a#;ZZDz&zjP>(d5U`=0TiocB3|a3P<8z{1JZ}WeGn<5<|F~aGV#@B5YlqOFgoHXKc@)+iLy4w$9pk0Tt_CEaCZbV}$ uiDM}hE~H|ZNGZTl9w@|v=n(Z75+(RZ)Zl`+7vti=S7eN?kKYqJiv9yz*)JUc diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index 7c12613037564f0448fbbeb9331a8dfe07f4c62e..d01207c23483a9b971799de705f092700c20c856 100644 GIT binary patch delta 4176 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 IE3WDP1(%)0LjV8( delta 918 zcmchUX-t!M9LH1oMnoKn5!36Tr3aR$Ezp*uwFQbosdy0Y7-L90s&AZeju_F+BRUsj zoS(6HATK;$dPR*f9uMNJcx1!q7Vj%#PNQ)}FwcJv+Y3$hes4aX{JyV#$>;TM-G?o@ zgOd!Iy=uE?S4Svk%Y@p?0)ssKrK&tq9x9JJ)N?|4wW4k1q0>U`;iw@*r@^HAi!5U; z#YQvD#u@B4+UYY+#c8thm1#0ee9JsbG50KMFiAmnN-OzI z@{`P6#@^gQJl3yCT8rtnCXs9fY`4wly5{=4B+Ypq((Ox$+KcG2ujjdaC@x1XhaLO4 z<`~9sr;P*7?VNXJp?Asba=|HA2Jc)>R^_+QmY+dazKzC_E7&p8gVnv526rJ3-HRAp z;H9--CT9zrgyaUM%X!?Dr_d)$EcHxbt4AW!J096}^g?uS z!Y4^Wq{J1qpXb$!n%{}e&q>k1?DE^%@>=}M%j#9_>K0x9;Z^mzw!IMdK9SDC9J3Hl z#C>ghB5tU+G(QlJKatt>IodD3@J^gjJ`rU(H zsU6~s@<#ckJ=I_Jkv~X}f2b}+AP}P=FoKrAI8FqNh(Mg&poN9OB&|UQso)&kC3e0m zS%Oj`<1SrDW2u`9rE5tq3$mfCnM-Ab#LE}6s$51NYN8_KwU)>sX9VynrhuHbm} zTDq%a>>0g?&e37M95b7r#<-Dc8mX%Bb4PP;jfV}jb2(7!A`+j*thj@x@d@bbB$n4r uWJjHa^h69>!ouc+r2ELvi6Y)6vhj~KMEXQkrgBrc{6F>@bmY(RI{n|^De)oz diff --git a/Art/Districts/1200/Port_SW.PCX b/Art/Districts/1200/Port_SW.PCX index 1623760f4438975685ac68b4c585e314b2188c89..73dd62c5b1c78ca550ab316d009b6b692d0247f0 100644 GIT binary patch delta 479 zcmXYrJ!n&57=>vTC4*@+r3EJ;)o9Xm$kM8Vcj%@r4%Nxdikn-xDdIOcR^Krolr;WJ zO$`N8Zj$rf*4s!^LN`TG5hVZh_S#w^Y!O+GZyZjyi0I`U267SZKx2%AlMH~J~?T};4Q%Cqns+6{Vkaw}Am1=iR>363pj z>!41vrwW0F7`7~=9>HGMf;yd#6ay_W*JLhpY4WY0s#z~m2zA64 zPQ}wthJJ{0w7D`3Z$zQ`{6Q|CYtW4|snD(%8)D}5WYtjMsocut79EZ*9{nk*EWZ35 z12E3P#`X>^Mdy$2i`oz{^eCKF;OwF&b`L!fq1o68x*3}}cJRLgEnIndpVKcq+KSx^ z9`v`;tT7BLYb|?pZS4B+o}i;pcC=M-T3P7c(!X6=9lJ8*33vJtv#rnt`&9o&Fn)3H zx2R$go0=Aq3U-@j;urKVc9|zo>6bU}8gQGGjwgb@#0O6O#Ywm-Q>XXw#NfVeXa05F XB)-%rFrM&h*fLPYPVM#7`0el?MC3O+ delta 178 zcmcb$l5yv9#tj-O%>VzNpRBKP2S{I@EULN}NME1)K=m$=zCHPj+BqP7fATu@eL(v0 zGP)PfZa2^<-(S6+rs^Vktr~m*( CZ)Vg0 From 6384500652969a90fa1ad17526f8b4339e625247 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 8 Dec 2025 12:12:54 -0800 Subject: [PATCH 064/356] move abandoned district to separate property; Add maritime abandoned district and art --- .gitignore | 1 + Art/Districts/1200/Abandoned.PCX | Bin 0 -> 29061 bytes Art/Districts/1200/Neighborhood_Abandoned.PCX | Bin 20473 -> 0 bytes Art/Districts/1200/Port_NE.PCX | Bin 19386 -> 19372 bytes C3X.h | 52 +++--- injected_code.c | 149 +++++++++++------- 6 files changed, 125 insertions(+), 77 deletions(-) create mode 100644 Art/Districts/1200/Abandoned.PCX delete mode 100644 Art/Districts/1200/Neighborhood_Abandoned.PCX diff --git a/.gitignore b/.gitignore index 41cc1372..9a8d0561 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.exp *.lib ref/ +agents.md __pycache__ temp.exe Thumbs.db diff --git a/Art/Districts/1200/Abandoned.PCX b/Art/Districts/1200/Abandoned.PCX new file mode 100644 index 0000000000000000000000000000000000000000..8795f1bdf1e90bd6ac1b2ecf01bfa68374e9e497 GIT binary patch literal 29061 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 Y*s^YYSI+}?-4!6G>U{Rs4I26WznpXp?*IS* literal 0 HcmV?d00001 diff --git a/Art/Districts/1200/Neighborhood_Abandoned.PCX b/Art/Districts/1200/Neighborhood_Abandoned.PCX deleted file mode 100644 index 11ea0cb2c8f9e5c83f8ef3723230d4246c61c259..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20473 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 KKi8?b?|%RflbLz| diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index 4b74bc79aaad8c46257e27bf457b1d533d75605f..3be84f99d952c836af819b9e0d1446637f3cfa86 100644 GIT binary patch delta 1705 zcmZ{kO=w(I6vrnMQV~r{()iL3gI$OVOV!DwNoUgJ^-Zl5tl&bJg-fNl(`7byUKS~p zL8N%WRttl=C{_dm;>L}h`=(<8RukR`x{%JBAxxriUgkTqFxUTinIuhAhMDBOdp`fa zbMCh{d%nEcGd~dNThipM$-&6!NAX{ge8*gqiAdzcl1`FKCFQE5GRa%jTU%N?`oY%k z^3%wW{48>Gm`#c>fR~ws4Zbd|l;!x3T9r?ZK0ks0 zs%YTZGn3?zYkWr=^owd!O~n3?S4X2yFKe6@xeW3QEYA(vl*$mRJyS=aIf0w*}F{oi0-91o*vZRHLsSC&`C%&DKWgKW>zrqfX^`ksU2sY(k-*t$&>M~mzMS-X-#{#AK@ zEYa&{U3q+Lv*JhM1_PXq9(7Lef=`JD{^@JNgCWwm|CDy!rOL+*86%N59hQ6 zS-R>&+z!!xD0J(*L;5I*=9U@Sy#PR+*l8AfVS&?tVY|Di!j#sEYYj0D=w5bkl z^acW~3mg{6%kT_`P-$h(R$4fFQzK8s-%Eg+(o{~t0LH9cKy;U;FVGBS7^a%b1~~Wc zWLEwePY=Vdj6}auIRgQzh}Pttl{)ss;bS>1_EuEho%~R~b7AN-bo?JyfAh=;2(`1a za_0Q42`{C<27>-YiwapXtl>dgIbLD%1(qimDDvg2Q)$zryr60Z>}pQk&Ok3%{RwFk=cqB_HrwSH$f?O^T??VPLA6k0B0UTG!jpV7MyosWwE1teU`9gW~={o?E))C7UP9=P=Jp(EuP7%Q6vq>nBvejYMF~rR0#bw!4F|M#zMQ0)_4 z;4l&`M6$RdArS;8F7RgaK~IEkNASpFcdsc7i_x=-wIUp#EPLB)oCYCAKP{>|4#+a*RB>6X!8?2 zq?@`Hnn#a~SuwXC|D_(3qF_h}y~6JvE(3p5YV)?V}K%0!PNQMgF%f9hnsA2WlmeJmDMcD^O@5Bv9Wa zd)i5#CfZalB<(kRBXmGT@dfP+QspZS;YgqI^k<70l+Fof5pwRU>0~m_nVRQJsn7aP zKNopM>Rk(#_bvE6IU4^Bk%Ed8SVF}IUbYS!Li>iO3;kl^SA&Wn94Bk+sZ%XRRoEF~ zrIw?Owqu>KCF45q^1!E~HZAv0ockj#T%^(PopaT*R7R3*itt&|HKbgH5I8Ah)5^n6 zs>y+O(?fl67N-L?3+3y;aRH%HAO!tC~QakE3vbwU5vSw2`_pckwYmM!h%M-OCX75F*@Z0z?}yy+#|T z<0GH6&(l`cGs+(8ejPSG4Of%77P2uuOX}9hT>p|ma7Kd*;BL}&oujoJvwD(LW3(is ztNGE-Q%L3`R#KD$vTd)F&1Eq?YwRrRv)HIIi@=V(iHpH3H9dB1+^1V8otehqCU<6- z@gmxi_ytNn_8wu>984yU>H$-Ub8`;v0d%;9rpO~jfFdH=ZW8jba6kwpKo7TK5sloK zMai7iv*~NR!)48<9nDJTw9hA?w6j@Nx zV29JIbQgCM;KL>zp#ds9(~KYYB!yD_AU?HtMheZ=#cGGc6v5pd>*?gSwNN)o)9Rz~ zSsOb;k5z=|yTzx00|4L6!45udOR9e7%Go%s0?kDIR?qyO7?y*xw2_mktX3P&S~PXdCCF)FFt#9S{sP=tEUe*^;zL2s-g?EK{1^E`kJ`)M7S_q|r@ld~rw glKm|R$jfJxe0!#YuSl$Xf9(91?WfG%tHtp9KLN>Al>h($ diff --git a/C3X.h b/C3X.h index 0548964e..838eb5b8 100644 --- a/C3X.h +++ b/C3X.h @@ -628,15 +628,15 @@ struct wonder_location { }; const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYPES] = { - { - .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", - .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, - .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx", "Neighborhood_Abandoned.pcx"}, - .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, - .img_path_count = 6, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, - .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .defense_bonus_percent = 25 - - }, + { + .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", + .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, + .img_path_count = 5, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, + .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .defense_bonus_percent = 25 + + }, { .command = UCV_Build_WonderDistrict, .name = "Wonder District", .tooltip = "Build Wonder District", .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, @@ -1496,14 +1496,16 @@ struct injected_state { Sprite Mountain_Rivers_Images[16]; Sprite Territory_Images[8]; Sprite LM_Mountains_Images[16]; - Sprite LM_Forests_Large_Images[8]; - Sprite LM_Forests_Small_Images[10]; - Sprite LM_Forests_Pines_Images[12]; - Sprite LM_Hills_Images[16]; - Sprite District_Images[COUNT_DISTRICT_TYPES][10][4][6]; // [district][variant][era][building_stage] - struct wonder_district_image_set Wonder_District_Images[MAX_WONDER_DISTRICT_TYPES]; - struct natural_wonder_district_image_set Natural_Wonder_Images[MAX_NATURAL_WONDER_DISTRICT_TYPES]; - } day_night_cycle_imgs[24]; + Sprite LM_Forests_Large_Images[8]; + Sprite LM_Forests_Small_Images[10]; + Sprite LM_Forests_Pines_Images[12]; + Sprite LM_Hills_Images[16]; + Sprite District_Images[COUNT_DISTRICT_TYPES][10][4][6]; // [district][variant][era][building_stage] + Sprite Abandoned_District_Image; + Sprite Abandoned_Maritime_District_Image; + struct wonder_district_image_set Wonder_District_Images[MAX_WONDER_DISTRICT_TYPES]; + struct natural_wonder_district_image_set Natural_Wonder_Images[MAX_NATURAL_WONDER_DISTRICT_TYPES]; + } day_night_cycle_imgs[24]; // Districts enum init_state dc_img_state; @@ -1514,13 +1516,15 @@ struct injected_state { struct wonder_district_config wonder_district_configs[MAX_WONDER_DISTRICT_TYPES]; struct natural_wonder_district_config natural_wonder_configs[MAX_NATURAL_WONDER_DISTRICT_TYPES]; - struct district_image_set { - Sprite imgs[10][4][6]; // [variant][era][building_stage] - } district_img_sets[COUNT_DISTRICT_TYPES]; - - struct district_button_image_set { - Sprite imgs[4]; - } district_btn_img_sets[COUNT_DISTRICT_TYPES]; +struct district_image_set { + Sprite imgs[10][4][6]; // [variant][era][building_stage] +} district_img_sets[COUNT_DISTRICT_TYPES]; + Sprite abandoned_district_img; + Sprite abandoned_maritime_district_img; + +struct district_button_image_set { + Sprite imgs[4]; +} district_btn_img_sets[COUNT_DISTRICT_TYPES]; // Tech ID keys -> district ID. If a tech (aka advance) ID is present in the // table that means that tech enables a district. This also means one tech can enable at most one district. diff --git a/injected_code.c b/injected_code.c index 8848500c..69eca404 100644 --- a/injected_code.c +++ b/injected_code.c @@ -7251,15 +7251,20 @@ deinit_district_images (void) set->construct_img.vtable->destruct (&set->construct_img, __, 0); } - for (int ni = 0; ni < MAX_NATURAL_WONDER_DISTRICT_TYPES; ni++) { - Sprite * sprite = &is->natural_wonder_img_sets[ni].img; - if (sprite->vtable != NULL) - sprite->vtable->destruct (sprite, __, 0); - } - } - - is->dc_img_state = IS_UNINITED; -} + for (int ni = 0; ni < MAX_NATURAL_WONDER_DISTRICT_TYPES; ni++) { + Sprite * sprite = &is->natural_wonder_img_sets[ni].img; + if (sprite->vtable != NULL) + sprite->vtable->destruct (sprite, __, 0); + } + + if (is->abandoned_district_img.vtable != NULL) + is->abandoned_district_img.vtable->destruct (&is->abandoned_district_img, __, 0); + if (is->abandoned_maritime_district_img.vtable != NULL) + is->abandoned_maritime_district_img.vtable->destruct (&is->abandoned_maritime_district_img, __, 0); + } + + is->dc_img_state = IS_UNINITED; +} void clear_highlighted_worker_tiles_for_districts () @@ -10568,14 +10573,21 @@ bool load_day_night_hour_images(struct day_night_cycle_img_set *this, const char Sprite_slice_pcx (&this->District_Images[dc][variant_i][era][col], __, &img, tile_x, tile_y, 128, 64, 1, 1); } } - } - } - - // Load wonder district images (dynamically per wonder) for this hour - if (is->current_config.enable_wonder_districts) { - char const * last_img_path = NULL; - PCX_Image wpcx; - PCX_Image_construct (&wpcx); + } + } + + // Abandoned district art (land + maritime) for this hour + read_in_dir (&img, districts_hour_dir, "Abandoned.pcx", NULL); + if (img.JGL.Image == NULL) + return false; + Sprite_slice_pcx (&this->Abandoned_District_Image, __, &img, 0, 0, 128, 64, 1, 1); + Sprite_slice_pcx (&this->Abandoned_Maritime_District_Image, __, &img, 128, 0, 128, 64, 1, 1); + + // Load wonder district images (dynamically per wonder) for this hour + if (is->current_config.enable_wonder_districts) { + char const * last_img_path = NULL; + PCX_Image wpcx; + PCX_Image_construct (&wpcx); bool pcx_loaded = false; for (int wi = 0; wi < is->wonder_district_count; wi++) { @@ -10785,24 +10797,27 @@ build_sprite_proxies_24(Map_Renderer *mr) { int era_count = cfg->vary_img_by_era ? 4 : 1; int column_count = cfg->max_building_index + 1; - for (int variant_i = 0; variant_i < variant_count; variant_i++) { - if ((cfg->img_paths[variant_i] == NULL) || (cfg->img_paths[variant_i][0] == '\0')) - continue; - for (int era = 0; era < era_count; era++) { - for (int col = 0; col < column_count; col++) { - Sprite * base = &is->district_img_sets[dc].imgs[variant_i][era][col]; - Sprite * proxy = &is->day_night_cycle_imgs[h].District_Images[dc][variant_i][era][col]; - insert_sprite_proxy (base, proxy, h); - } - } - } - } - - // Wonder districts - if (is->current_config.enable_wonder_districts) { - for (int wi = 0; wi < is->wonder_district_count; wi++) { - Sprite * base_img = &is->wonder_district_img_sets[wi].img; - Sprite * proxy_img = &is->day_night_cycle_imgs[h].Wonder_District_Images[wi].img; + for (int variant_i = 0; variant_i < variant_count; variant_i++) { + if ((cfg->img_paths[variant_i] == NULL) || (cfg->img_paths[variant_i][0] == '\0')) + continue; + for (int era = 0; era < era_count; era++) { + for (int col = 0; col < column_count; col++) { + Sprite * base = &is->district_img_sets[dc].imgs[variant_i][era][col]; + Sprite * proxy = &is->day_night_cycle_imgs[h].District_Images[dc][variant_i][era][col]; + insert_sprite_proxy (base, proxy, h); + } + } + } + } + + insert_sprite_proxy (&is->abandoned_district_img, &is->day_night_cycle_imgs[h].Abandoned_District_Image, h); + insert_sprite_proxy (&is->abandoned_maritime_district_img, &is->day_night_cycle_imgs[h].Abandoned_Maritime_District_Image, h); + + // Wonder districts + if (is->current_config.enable_wonder_districts) { + for (int wi = 0; wi < is->wonder_district_count; wi++) { + Sprite * base_img = &is->wonder_district_img_sets[wi].img; + Sprite * proxy_img = &is->day_night_cycle_imgs[h].Wonder_District_Images[wi].img; insert_sprite_proxy (base_img, proxy_img, h); Sprite * base_construct = &is->wonder_district_img_sets[wi].construct_img; @@ -23740,10 +23755,37 @@ init_district_images () } pcx.vtable->clear_JGL (&pcx); - } - } - // Load wonder district images (dynamically per wonder) - if (is->current_config.enable_wonder_districts) { + } + } + // Load abandoned district images (land + maritime) + snprintf (art_dir, sizeof art_dir, "%s\\Art\\Districts\\1200\\Abandoned.pcx", is->mod_rel_dir); + PCX_Image_read_file (&pcx, __, art_dir, NULL, 0, 0x100, 2); + + if (pcx.JGL.Image == NULL) { + char ss[200]; + snprintf (ss, sizeof ss, "init_district_images: failed to load abandoned district images from %s", art_dir); + pop_up_in_game_error (ss); + for (int dc2 = 0; dc2 < COUNT_DISTRICT_TYPES; dc2++) + for (int variant_i2 = 0; variant_i2 < ARRAY_LEN (is->district_img_sets[dc2].imgs); variant_i2++) + for (int era_i = 0; era_i < 4; era_i++) { + for (int col = 0; col < ARRAY_LEN (is->district_img_sets[dc2].imgs[variant_i2][era_i]); col++) { + Sprite * sprite = &is->district_img_sets[dc2].imgs[variant_i2][era_i][col]; + if (sprite->vtable != NULL) + sprite->vtable->destruct (sprite, __, 0); + } + } + pcx.vtable->destruct (&pcx, __, 0); + return; + } + + Sprite_construct (&is->abandoned_district_img); + Sprite_slice_pcx (&is->abandoned_district_img, __, &pcx, 0, 0, 128, 64, 1, 1); + + Sprite_construct (&is->abandoned_maritime_district_img); + Sprite_slice_pcx (&is->abandoned_maritime_district_img, __, &pcx, 128, 0, 128, 64, 1, 1); + pcx.vtable->clear_JGL (&pcx); + // Load wonder district images (dynamically per wonder) + if (is->current_config.enable_wonder_districts) { char const * last_img_path = NULL; PCX_Image wpcx; PCX_Image_construct (&wpcx); @@ -24234,20 +24276,21 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par int buildings = 0; Sprite * district_sprite; - if (territory_owner_id > 0) { - Leader * leader = &leaders[territory_owner_id]; - culture = p_bic_data->Races[leader->RaceID].CultureGroupID; - if (cfg->vary_img_by_culture) - variant = culture; - if (cfg->vary_img_by_era) - era = leader->Era; - } else if (district_id != WONDER_DISTRICT_ID && district_id != NATURAL_WONDER_DISTRICT_ID) { - // Render abandoned district, special index 5 - variant = 5; - district_sprite = &is->district_img_sets[0].imgs[variant][era][buildings]; - patch_Sprite_draw_on_map (district_sprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); - return; - } + if (territory_owner_id > 0) { + Leader * leader = &leaders[territory_owner_id]; + culture = p_bic_data->Races[leader->RaceID].CultureGroupID; + if (cfg->vary_img_by_culture) + variant = culture; + if (cfg->vary_img_by_era) + era = leader->Era; + } else if (district_id != WONDER_DISTRICT_ID && district_id != NATURAL_WONDER_DISTRICT_ID) { + Sprite * abandoned_sprite = &is->abandoned_district_img; + if (tile->vtable->m35_Check_Is_Water (tile) && is->abandoned_maritime_district_img.vtable != NULL) + abandoned_sprite = &is->abandoned_maritime_district_img; + if (abandoned_sprite->vtable != NULL) + patch_Sprite_draw_on_map (abandoned_sprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + return; + } switch (district_id) { case NEIGHBORHOOD_DISTRICT_ID: From 4c4dbc79e14fcf1a28e2a4d316ee2552552a1a38 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 10 Dec 2025 21:55:52 -0800 Subject: [PATCH 065/356] Add maritime alt images to c3x.h --- C3X.h | 65 +++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/C3X.h b/C3X.h index 838eb5b8..5cbec2f8 100644 --- a/C3X.h +++ b/C3X.h @@ -585,8 +585,15 @@ struct wonder_district_config { img_row, img_column, img_construct_row, - img_construct_column; + img_construct_column, + + // Maritime wonders have a set of alternative images for facing east + maritime_alt_img_row, + maritime_alt_img_column, + maritime_alt_img_construct_row, + maritime_alt_img_construct_column; bool is_dynamic; + bool is_maritime; }; enum square_type_extras { @@ -628,15 +635,15 @@ struct wonder_location { }; const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYPES] = { - { - .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", - .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, - .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, - .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, - .img_path_count = 5, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, - .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .defense_bonus_percent = 25 - - }, + { + .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", + .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, + .img_path_count = 5, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, + .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .defense_bonus_percent = 25 + + }, { .command = UCV_Build_WonderDistrict, .name = "Wonder District", .tooltip = "Build Wonder District", .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, @@ -1496,16 +1503,16 @@ struct injected_state { Sprite Mountain_Rivers_Images[16]; Sprite Territory_Images[8]; Sprite LM_Mountains_Images[16]; - Sprite LM_Forests_Large_Images[8]; - Sprite LM_Forests_Small_Images[10]; - Sprite LM_Forests_Pines_Images[12]; - Sprite LM_Hills_Images[16]; - Sprite District_Images[COUNT_DISTRICT_TYPES][10][4][6]; // [district][variant][era][building_stage] - Sprite Abandoned_District_Image; - Sprite Abandoned_Maritime_District_Image; - struct wonder_district_image_set Wonder_District_Images[MAX_WONDER_DISTRICT_TYPES]; - struct natural_wonder_district_image_set Natural_Wonder_Images[MAX_NATURAL_WONDER_DISTRICT_TYPES]; - } day_night_cycle_imgs[24]; + Sprite LM_Forests_Large_Images[8]; + Sprite LM_Forests_Small_Images[10]; + Sprite LM_Forests_Pines_Images[12]; + Sprite LM_Hills_Images[16]; + Sprite District_Images[COUNT_DISTRICT_TYPES][10][4][6]; // [district][variant][era][building_stage] + Sprite Abandoned_District_Image; + Sprite Abandoned_Maritime_District_Image; + struct wonder_district_image_set Wonder_District_Images[MAX_WONDER_DISTRICT_TYPES]; + struct natural_wonder_district_image_set Natural_Wonder_Images[MAX_NATURAL_WONDER_DISTRICT_TYPES]; + } day_night_cycle_imgs[24]; // Districts enum init_state dc_img_state; @@ -1516,15 +1523,15 @@ struct injected_state { struct wonder_district_config wonder_district_configs[MAX_WONDER_DISTRICT_TYPES]; struct natural_wonder_district_config natural_wonder_configs[MAX_NATURAL_WONDER_DISTRICT_TYPES]; -struct district_image_set { - Sprite imgs[10][4][6]; // [variant][era][building_stage] -} district_img_sets[COUNT_DISTRICT_TYPES]; - Sprite abandoned_district_img; - Sprite abandoned_maritime_district_img; - -struct district_button_image_set { - Sprite imgs[4]; -} district_btn_img_sets[COUNT_DISTRICT_TYPES]; +struct district_image_set { + Sprite imgs[10][4][6]; // [variant][era][building_stage] +} district_img_sets[COUNT_DISTRICT_TYPES]; + Sprite abandoned_district_img; + Sprite abandoned_maritime_district_img; + +struct district_button_image_set { + Sprite imgs[4]; +} district_btn_img_sets[COUNT_DISTRICT_TYPES]; // Tech ID keys -> district ID. If a tech (aka advance) ID is present in the // table that means that tech enables a district. This also means one tech can enable at most one district. From c4b1177f8eaae529fcca61fdca9c1f7af9916ed6 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 10:43:00 -0800 Subject: [PATCH 066/356] Add Wonders_3.pcx, right-facing colossus img --- Art/Districts/1200/Wonders_3.PCX | Bin 0 -> 7971 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Art/Districts/1200/Wonders_3.PCX diff --git a/Art/Districts/1200/Wonders_3.PCX b/Art/Districts/1200/Wonders_3.PCX new file mode 100644 index 0000000000000000000000000000000000000000..fca2f595353a9e3e2748dca3a54b7f0ead8b890d GIT binary patch literal 7971 zcmeI1Z%|a%6~Hw!JJZ3mGihehj@5CZ;{c*baAg;zlw!IdnoZP*D`nEu7)(-Qt47Ag zWP#MAtCEEEUkKSCCCSslWSiOq2x83K1zZ*v38;WTSP=#JBV-{;lz30ieV5C7Z#VlP z-}*tAIkWeiyYHQQ@44r94ut8Z{%i{Vz*LwrWh(yu)SszS?)m9oQeFbo1wenGWYm<- zKJhV^cj#%Ni9 zBdBSh7k~`mj{1v9lSD4n!6~-u4HL!@=%rWbP1KyBE#S23iH~#oL_GEr=qRqJ+37e9+0Cfu zRwlo}eFZoJH)$7~dd)t2CeE^^A0}0oaiuA z60egqvbQ9XrZ%+lIZR%}69G5}L$ntplglGj0U5+-Vc|{O9jND4CR^wQ9JN3{y@sc0 zmtG@7&RQVNKGbkMlNazr0otJlPufJ>*=2gk>ISI`6 z=MdLvkglMTo0x3G0|w}ZPI?_kJ)S2nd&rqnej2~lgIaE5vVqp)s2AGkFwT_hhaJQt z5myZ^qz{$c#AH222*3a|Q$M(J@K%ta#A5O_PINORH!*pJdT=xhP4qjEh?jVB$dH4$ z@MfPMLM1mb>A_e67=^PKf07mbW5-ZK`+`y!p*5GIi>ge}SnN2)askjED5gcJ8IS-(Zs~vs{i39V z2z|KD^nf{HUPnM)@nN*EY;_USxnXwfG<`yr&Y}x*=@QxkvX#hzEfC`}PJ6&%(i?R# zIy31by;&^*IZ$NQR`l~1kuHNSGASm4oYym=xhJwN%c~+&UxWyAL=5q_qW#Q7_GTGc zq#dYL{-6vHa1aVuxdh7BD@|E@{XojQCBXZK8Jcv zsP~F0CeTk&yoeRCLPlM)h#g?{i@Hn++_zBsEQ*o9a}mXdI6+=-D3yjdab933rA1(z zgo-N>k2i7u)p01|SyTi(ij}$mFA*keA(a|Hp*<(R!hD78obw9d6^wJFD==4Z&C#tu zTY)nNwSs5`$Q;fJmK6$fA}cIr!2NOfDwyTSRbZ=dl`~eMs{&FERRyOCIXOiYek!o! z;8c*QAd=%!0i{AlPDzE23J*CO6)FZ>1Pz0qCJ-3xf#6=S*8+FJD;1Cn-W>tA;2jlM z3qA!wwBVByAPYWi!LQ&55y%RTDuJosNEBoWj$T2c;D{IO3BDzQHR49`+toE$u?OD6 zQ1>>}Jr^xUU|zV(j~^okk$Yq+OEp1$ko)1=D3yMgk?VVzTsKiFeJ~@)cXCfWB9l%{ zkZdPpVc9s$3lH9&_%4Am)q(kmi+fZr= z(keHn%_y}5IWIS=O>(87j;ycc-t#O!CWs5A zmLP!FpbGbxNi9LfQRl|Gfk`bv#!z<#|3JW`mLQ|3Yrq|1QcI9w)Sbhw%geTCddv(XLcY5bf@$38LLmH9@qepe9H+`mUUunjqTKRue=UA!>qXqe@K> zZ6vA*qK#fPL9`Li*1)PJIv61X17HQ1@I-5OrQq6GWXyLggKG zrK`?U>dIK1*Tf(ESS78_gX#`Moi{_}S#@Qv&dcgfK%K`!<$WzL)T9V)3H7W{?+)c_ zsI`f`U|lKeKY(f((vBM5z1?Mq8yS6$2J^rKI$5$?oweNbm{OFGILd%W%w6?l;tS_xZo1x>Iehs) zw(Jd_FZX@*neCH*ZBI2vzq=>(fltgV}${M`9^ea)du z&4nL0wtRK`sV5(slea5ouWjp}(iWz!T(CSTd{c(8weHWi`!|;!U3Rn}evSFp2M_E? zOqe(BFIcxmAN}xe90%7VJvx_sk+I4gnU)&0?M36!!jyu2iLbvJeJD3-^~%^2WjjAP zx^YFy{6!0AoT+&+F=6&%_pu#^Pad2X8@}o7=hkgsTXL-A73(Wl;2R!~`|A^2Oy266q z6M4S>#-=BKG~)i}g_6WgYixUVzgPJF2Ooa;?@vGdytK6Pc*y~OI=-cUT Date: Sat, 13 Dec 2025 11:20:27 -0800 Subject: [PATCH 067/356] Add FOR_WORK_AREA_AROUND macro and replace repetitive loops --- injected_code.c | 355 +++++++++++++++++++----------------------------- 1 file changed, 137 insertions(+), 218 deletions(-) diff --git a/injected_code.c b/injected_code.c index b79700d2..b729ba47 100644 --- a/injected_code.c +++ b/injected_code.c @@ -3024,6 +3024,27 @@ uti_init (Tile * tile) #define FOR_UNITS_ON(uti_name, tile) for (struct unit_tile_iter uti_name = uti_init (tile); uti_name.id != -1; uti_next (&uti_name)) +bool +tile_has_enemy_unit (Tile * tile, int civ_id) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return false; + if ((civ_id < 0) || (civ_id >= 32)) + return false; + + FOR_UNITS_ON (uti, tile) { + Unit * unit = uti.unit; + if ((unit == NULL) || (unit->Body.Container_Unit >= 0)) + continue; + if (unit->Body.CivID == civ_id) + continue; + if (unit->vtable->is_enemy_of_civ (unit, __, civ_id, 0)) + return true; + } + + return false; +} + struct citizen_iter { int index; Citizens * list; @@ -3128,6 +3149,67 @@ tai_init (int num_tiles, int x, int y) #define FOR_TILES_AROUND(tai_name, _num_tiles, _x, _y) for (struct tiles_around_iter tai_name = tai_init (_num_tiles, _x, _y); (tai_name.n < tai_name.num_tiles); tai_next (&tai_name)) +struct work_area_iter { + int center_x, center_y; + int n, num_tiles; + int civ_id; + int dx, dy; + int tile_x, tile_y; + Tile * tile; +}; + +void +wai_next (struct work_area_iter * wai) +{ + wai->tile = p_null_tile; + while ((wai->n + 1) < wai->num_tiles) { + wai->n += 1; + patch_ni_to_diff_for_work_area (wai->n, &wai->dx, &wai->dy); + wai->tile_x = wai->center_x + wai->dx; + wai->tile_y = wai->center_y + wai->dy; + wrap_tile_coords (&p_bic_data->Map, &wai->tile_x, &wai->tile_y); + Tile * candidate = tile_at (wai->tile_x, wai->tile_y); + if ((candidate == NULL) || (candidate == p_null_tile)) + continue; + if ((wai->civ_id < 0) || (candidate->vtable->m38_Get_Territory_OwnerID (candidate) != wai->civ_id)) + continue; + if (candidate->CityID >= 0) + continue; + if (tile_has_enemy_unit (candidate, wai->civ_id)) + continue; + if (candidate->vtable->m20_Check_Pollution (candidate, __, 0)) + continue; + wai->tile = candidate; + break; + } + if (wai->tile == p_null_tile) + wai->n = wai->num_tiles; +} + +struct work_area_iter +wai_init (int x, int y) +{ + struct work_area_iter tr; + tr.center_x = x; + tr.center_y = y; + tr.n = -1; + tr.num_tiles = is->workable_tile_count; + Tile * center_tile = tile_at (x, y); + if ((center_tile == NULL) || (center_tile == p_null_tile)) + tr.civ_id = -1; + else + tr.civ_id = center_tile->vtable->m38_Get_Territory_OwnerID (center_tile); + tr.tile = p_null_tile; + tr.dx = 0; + tr.dy = 0; + tr.tile_x = 0; + tr.tile_y = 0; + wai_next (&tr); + return tr; +} + +#define FOR_WORK_AREA_AROUND(wai_name, _x, _y) for (struct work_area_iter wai_name = wai_init (_x, _y); (wai_name.n < wai_name.num_tiles); wai_next (&wai_name)) + void tai_get_coords (struct tiles_around_iter * tai, int * out_x, int * out_y) { @@ -4122,27 +4204,6 @@ city_radius_contains_tile (City * city, int tile_x, int tile_y) return ni >= 0; } -bool -tile_has_enemy_unit (Tile * tile, int civ_id) -{ - if ((tile == NULL) || (tile == p_null_tile)) - return false; - if ((civ_id < 0) || (civ_id >= 32)) - return false; - - FOR_UNITS_ON (uti, tile) { - Unit * unit = uti.unit; - if ((unit == NULL) || (unit->Body.Container_Unit >= 0)) - continue; - if (unit->Body.CivID == civ_id) - continue; - if (unit->vtable->is_enemy_of_civ (unit, __, civ_id, 0)) - return true; - } - - return false; -} - void recompute_distribution_hub_yields (struct distribution_hub_record * rec) { @@ -4477,16 +4538,9 @@ refresh_distribution_hubs_for_city (City * city) (city == NULL)) return; - int city_x = city->Body.X; - int city_y = city->Body.Y; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int tx = city_x + dx, ty = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &tx, &ty); - Tile * tile = tile_at (tx, ty); - if ((tile == NULL) || (tile == p_null_tile)) - continue; + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + int tx = wai.tile_x, ty = wai.tile_y; + Tile * tile = wai.tile; struct district_instance * inst = get_district_instance (tile); if (inst == NULL || inst->district_type != DISTRIBUTION_HUB_DISTRICT_ID) continue; @@ -7444,29 +7498,13 @@ calculate_city_center_district_bonus (City * city, int * out_food, int * out_shi int bonus_shields = 0; int bonus_gold = 0; - int city_civ_id = city->Body.CivID; - int city_x = city->Body.X; - int city_y = city->Body.Y; int utilized_neighborhoods = count_utilized_neighborhoods_in_city_radius (city); int neighborhoods_counted = 0; - for (int n = 0; n < is->workable_tile_count; n++) { - if (n == 0) - continue; - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int tile_x = city_x + dx; - int tile_y = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); - Tile * tile = tile_at (tile_x, tile_y); - if ((tile == NULL) || (tile == p_null_tile)) - continue; - if (tile_has_enemy_unit (tile, city_civ_id)) - continue; - if (tile->vtable->m20_Check_Pollution (tile, __, 0)) - continue; - if (tile->Territory_OwnerID != city_civ_id) + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + if ((wai.dx == 0) && (wai.dy == 0)) continue; + Tile * tile = wai.tile; struct district_instance * inst = get_district_instance (tile); if (inst == NULL) @@ -7706,8 +7744,6 @@ find_tile_for_distribution_hub_district (City * city, int * out_x, int * out_y) int best_distance = -1; int best_distance_to_capital = -1; bool best_has_resource = true; - int city_x = city->Body.X; - int city_y = city->Body.Y; int civ_id = city->Body.CivID; bool has_capital = false; @@ -7725,17 +7761,14 @@ find_tile_for_distribution_hub_district (City * city, int * out_x, int * out_y) capital_continent_id = capital_tile->vtable->m46_Get_ContinentID (capital_tile); } - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int tx = city_x + dx, ty = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &tx, &ty); - Tile * tile = tile_at (tx, ty); + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + int tx = wai.tile_x, ty = wai.tile_y; + Tile * tile = wai.tile; bool has_resource; if (! tile_suitable_for_district (tile, DISTRIBUTION_HUB_DISTRICT_ID, city, &has_resource)) continue; - int chebyshev = compute_wrapped_chebyshev_distance (tx, ty, city_x, city_y); + int chebyshev = compute_wrapped_chebyshev_distance (tx, ty, city->Body.X, city->Body.Y); if (chebyshev <= 1) continue; @@ -7765,7 +7798,7 @@ find_tile_for_distribution_hub_district (City * city, int * out_x, int * out_y) int raw_yield = compute_city_tile_yield_sum (city, tx, ty); int adjusted_yield = raw_yield - (has_resource ? resource_penalty : 0); - int distance = compute_wrapped_manhattan_distance (tx, ty, city_x, city_y); + int distance = compute_wrapped_manhattan_distance (tx, ty, city->Body.X, city->Body.Y); int distance_to_capital = has_capital ? compute_wrapped_manhattan_distance (tx, ty, capital_x, capital_y) : 0; @@ -8018,17 +8051,9 @@ city_has_wonder_district_with_no_completed_wonder (City * city) if (! is->current_config.enable_wonder_districts || (city == NULL)) return false; - int civ_id = city->Body.CivID; - int city_x = city->Body.X; - int city_y = city->Body.Y; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = city_x + dx, y = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * candidate = tile_at (x, y); - if (candidate == NULL || candidate == p_null_tile) continue; - if (candidate->vtable->m38_Get_Territory_OwnerID (candidate) != civ_id) continue; + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + int x = wai.tile_x, y = wai.tile_y; + Tile * candidate = wai.tile; struct district_instance * inst = get_district_instance (candidate); if (inst == NULL || inst->district_type != WONDER_DISTRICT_ID) continue; if (! district_is_complete (candidate, WONDER_DISTRICT_ID)) continue; @@ -8242,24 +8267,12 @@ calculate_district_culture_science_bonuses (City * city, int * culture_bonus, in int total_culture = 0; int total_science = 0; - int city_civ_id = city->Body.CivID; int utilized_neighborhoods = count_utilized_neighborhoods_in_city_radius (city); - int city_x = city->Body.X; - int city_y = city->Body.Y; - - for (int n = 0; n < is->workable_tile_count; n++) { - if (n == 0) + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + if ((wai.dx == 0) && (wai.dy == 0)) continue; - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = city_x + dx, y = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * tile = tile_at (x, y); - if ((tile == NULL) || (tile == p_null_tile)) continue; - if (tile_has_enemy_unit (tile, city_civ_id)) continue; - if (tile->vtable->m20_Check_Pollution (tile, __, 0)) continue; - if (tile->vtable->m38_Get_Territory_OwnerID (tile) != city_civ_id) continue; + Tile * tile = wai.tile; struct district_instance * inst = get_district_instance (tile); if (inst == NULL) continue; int district_id = inst->district_type; @@ -8383,23 +8396,12 @@ city_has_other_completed_district (City * city, int district_id, int removed_x, (city == NULL) || (district_id < 0) || (district_id >= is->district_count)) return false; - int civ_id = city->Body.CivID; - int city_x = city->Body.X; - int city_y = city->Body.Y; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = city_x + dx, y = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * candidate = tile_at (x, y); - if ((candidate == NULL) || (candidate == p_null_tile)) - continue; + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + int x = wai.tile_x, y = wai.tile_y; + Tile * candidate = wai.tile; if ((x == removed_x) && (y == removed_y)) continue; - if (candidate->vtable->m38_Get_Territory_OwnerID (candidate) != civ_id) - continue; - struct district_instance * inst = get_district_instance (candidate); if (inst == NULL || inst->district_type != district_id) continue; @@ -8798,18 +8800,10 @@ city_has_assigned_wonder_district (City * city, Tile * ignore_tile) if (! is->current_config.enable_wonder_districts || (city == NULL)) return false; - int civ_id = city->Body.CivID; - int city_x = city->Body.X; - int city_y = city->Body.Y; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = city_x + dx, y = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * candidate = tile_at (x, y); - if ((candidate == NULL) || (candidate == p_null_tile) || (candidate == ignore_tile)) - continue; - if (candidate->vtable->m38_Get_Territory_OwnerID (candidate) != civ_id) + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + int x = wai.tile_x, y = wai.tile_y; + Tile * candidate = wai.tile; + if (candidate == ignore_tile) continue; struct district_instance * inst = get_district_instance (candidate); @@ -8838,16 +8832,9 @@ free_wonder_district_for_city (City * city) if (city_needs_wonder_district (city)) return false; - int city_x = city->Body.X; - int city_y = city->Body.Y; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = city_x + dx, y = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * tile = tile_at (x, y); - if (tile == p_null_tile) - continue; + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + int x = wai.tile_x, y = wai.tile_y; + Tile * tile = wai.tile; struct district_instance * inst = get_district_instance (tile); if (inst == NULL || inst->district_type != WONDER_DISTRICT_ID) continue; @@ -8877,18 +8864,9 @@ reserve_wonder_district_for_city (City * city) if (city_has_assigned_wonder_district (city, NULL)) return true; - int civ_id = city->Body.CivID; - int city_x = city->Body.X; - int city_y = city->Body.Y; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = city_x + dx, y = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * candidate = tile_at (x, y); - if (candidate == NULL || candidate == p_null_tile) continue; - if (candidate->vtable->m38_Get_Territory_OwnerID (candidate) != civ_id) continue; - + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + int x = wai.tile_x, y = wai.tile_y; + Tile * candidate = wai.tile; struct district_instance * inst = get_district_instance (candidate); if (inst == NULL || inst->district_type != WONDER_DISTRICT_ID) continue; if (! district_is_complete (candidate, WONDER_DISTRICT_ID)) continue; @@ -8924,20 +8902,8 @@ release_wonder_district_reservation (City * city) return; // Find and remove any reservations for this city - int civ_id = city->Body.CivID; - int city_x = city->Body.X; - int city_y = city->Body.Y; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = city_x + dx, y = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * candidate = tile_at (x, y); - if (candidate == p_null_tile) - continue; - if (candidate->vtable->m38_Get_Territory_OwnerID (candidate) != civ_id) - continue; - + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + Tile * candidate = wai.tile; struct wonder_district_info * info = get_wonder_district_info (candidate); if ((info != NULL) && (info->state == WDS_UNDER_CONSTRUCTION) && @@ -13575,21 +13541,11 @@ patch_City_Form_draw (City_Form * this) // Calculate district gold and science bonuses by iterating workable tiles int district_gold = 0; - int city_x = city->Body.X; - int city_y = city->Body.Y; int city_civ_id = city->Body.CivID; - for (int n = 0; n < is->workable_tile_count; n++) { - if (n == 0) continue; - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = city_x + dx, y = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * tile = tile_at (x, y); - if ((tile == NULL) || (tile == p_null_tile)) continue; - if (tile->Territory_OwnerID != city_civ_id) continue; - if (tile_has_enemy_unit (tile, city_civ_id)) continue; - if (tile->vtable->m20_Check_Pollution (tile, __, 0)) continue; + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + if ((wai.dx == 0) && (wai.dy == 0)) continue; + Tile * tile = wai.tile; struct district_instance * inst = get_district_instance (tile); if (inst == NULL) continue; int district_id = inst->district_type; @@ -16614,15 +16570,9 @@ copy_building_with_cities_in_radius (City * source, int improv_id, int required_ } // Else there may be multiple district instances of this type, so check each tile around the city else { - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = source->Body.X + dx, y = source->Body.Y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * tile = tile_at (x, y); - if (tile == p_null_tile) continue; - if (tile->vtable->m38_Get_Territory_OwnerID (tile) != source->Body.CivID) continue; - + FOR_WORK_AREA_AROUND (wai, source->Body.X, source->Body.Y) { + int x = wai.tile_x, y = wai.tile_y; + Tile * tile = wai.tile; struct district_instance * inst = get_district_instance (tile); if (inst == NULL || inst->district_type != required_district_id) continue; if (! district_is_complete (tile, required_district_id)) continue; @@ -16682,17 +16632,10 @@ grant_existing_district_buildings_to_city (City * city) bool prev_flag = is->sharing_buildings_by_districts_in_progress; is->sharing_buildings_by_districts_in_progress = true; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int tile_x = city->Body.X + dx; - int tile_y = city->Body.Y + dy; - wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); - Tile * tile = tile_at (tile_x, tile_y); - if (tile == p_null_tile) - continue; - if (tile->vtable->m38_Get_Territory_OwnerID (tile) != civ_id) - continue; + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + int tile_x = wai.tile_x; + int tile_y = wai.tile_y; + Tile * tile = wai.tile; struct district_instance * inst = get_district_instance (tile); if (inst == NULL) @@ -17147,14 +17090,9 @@ handle_possible_duplicate_small_wonders(City * city, Leader * leader) if ((city == NULL) || (leader == NULL)) return; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = city->Body.X + dx, y = city->Body.Y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * tile = tile_at (x, y); - - if ((tile == NULL) || (tile == p_null_tile)) continue; + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + int x = wai.tile_x, y = wai.tile_y; + Tile * tile = wai.tile; // Make sure it's a completed wonder district struct district_instance * inst = get_district_instance (tile); @@ -17197,16 +17135,9 @@ grant_nearby_wonders_to_city (City * city) (city == NULL)) return; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = city->Body.X + dx, y = city->Body.Y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * tile = tile_at (x, y); - - // Check if Wonder tile owned by same civ - if ((tile == NULL) || (tile == p_null_tile)) continue; - if (tile->vtable->m38_Get_Territory_OwnerID (tile) != city->Body.CivID) continue; + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + int x = wai.tile_x, y = wai.tile_y; + Tile * tile = wai.tile; // Make sure Wonder is completed struct district_instance * inst = get_district_instance (tile); @@ -22866,23 +22797,11 @@ patch_City_Form_draw_yields_on_worked_tiles (City_Form * this) if (is->current_config.enable_districts && is->current_config.enable_neighborhood_districts) remaining_utilized_neighborhoods = count_utilized_neighborhoods_in_city_radius (city); - // Iterate through all neighbor tiles - for (int neighbor_index = 0; neighbor_index < is->workable_tile_count; neighbor_index++) { - int dx, dy; - patch_ni_to_diff_for_work_area (neighbor_index, &dx, &dy); - int tile_x = city_x + dx; - int tile_y = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); - - Tile * tile = tile_at (tile_x, tile_y); - if (tile == NULL || tile == p_null_tile) continue; - if (tile->vtable->m38_Get_Territory_OwnerID (tile) != civ_id) continue; - struct district_instance * inst = get_district_instance (tile); + FOR_WORK_AREA_AROUND (wai, city_x, city_y) { + struct district_instance * inst = get_district_instance (wai.tile); if (inst == NULL) continue; int district_id = inst->district_type; - if (!district_is_complete (tile, district_id)) continue; - if (tile_has_enemy_unit (tile, civ_id)) continue; - if (tile->vtable->m20_Check_Pollution (tile, __, 0)) continue; + if (!district_is_complete (wai.tile, district_id)) continue; bool is_distribution_hub = district_id == DISTRIBUTION_HUB_DISTRICT_ID; bool is_natural_wonder = district_id == NATURAL_WONDER_DISTRICT_ID; @@ -22910,14 +22829,14 @@ patch_City_Form_draw_yields_on_worked_tiles (City_Form * this) } // Calculate screen coordinates for this tile - int screen_x = center_screen_x + (dx * tile_half_width); - int screen_y = center_screen_y + (dy * tile_half_height); + int screen_x = center_screen_x + (wai.dx * tile_half_width); + int screen_y = center_screen_y + (wai.dy * tile_half_height); // Call the appropriate drawing function if (is_distribution_hub) { - draw_distribution_hub_yields (this, tile, tile_x, tile_y, screen_x, screen_y); + draw_distribution_hub_yields (this, wai.tile, wai.tile_x, wai.tile_y, screen_x, screen_y); } else { - draw_district_yields (this, tile, district_id, screen_x, screen_y); + draw_district_yields (this, wai.tile, district_id, screen_x, screen_y); } } } From f39bde7447934da3c08c50b65832005462648fd8 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 11:51:01 -0800 Subject: [PATCH 068/356] Add FOR_DISTRICTS_AROUND macro and replace repetitive loops --- injected_code.c | 182 +++++++++++++++++++++++------------------------- 1 file changed, 86 insertions(+), 96 deletions(-) diff --git a/injected_code.c b/injected_code.c index b729ba47..928bb9d2 100644 --- a/injected_code.c +++ b/injected_code.c @@ -3156,12 +3156,16 @@ struct work_area_iter { int dx, dy; int tile_x, tile_y; Tile * tile; + struct district_instance * district_inst; + bool districts_only; + bool completed_only; }; void wai_next (struct work_area_iter * wai) { wai->tile = p_null_tile; + wai->district_inst = NULL; while ((wai->n + 1) < wai->num_tiles) { wai->n += 1; patch_ni_to_diff_for_work_area (wai->n, &wai->dx, &wai->dy); @@ -3179,6 +3183,17 @@ wai_next (struct work_area_iter * wai) continue; if (candidate->vtable->m20_Check_Pollution (candidate, __, 0)) continue; + struct district_instance * inst = get_district_instance (candidate); + if (wai->districts_only) { + if (inst == NULL) + continue; + int district_id = inst->district_type; + if ((district_id < 0) || (district_id >= is->district_count)) + continue; + if (wai->completed_only && (! district_is_complete (candidate, district_id))) + continue; + } + wai->district_inst = inst; wai->tile = candidate; break; } @@ -3187,7 +3202,7 @@ wai_next (struct work_area_iter * wai) } struct work_area_iter -wai_init (int x, int y) +wai_init_common (int x, int y, bool districts_only, bool completed_only) { struct work_area_iter tr; tr.center_x = x; @@ -3200,15 +3215,31 @@ wai_init (int x, int y) else tr.civ_id = center_tile->vtable->m38_Get_Territory_OwnerID (center_tile); tr.tile = p_null_tile; + tr.district_inst = NULL; tr.dx = 0; tr.dy = 0; tr.tile_x = 0; tr.tile_y = 0; + tr.districts_only = districts_only; + tr.completed_only = completed_only; wai_next (&tr); return tr; } +struct work_area_iter +wai_init (int x, int y) +{ + return wai_init_common (x, y, false, false); +} + +struct work_area_iter +wai_init_districts (int x, int y, bool completed_only) +{ + return wai_init_common (x, y, true, completed_only); +} + #define FOR_WORK_AREA_AROUND(wai_name, _x, _y) for (struct work_area_iter wai_name = wai_init (_x, _y); (wai_name.n < wai_name.num_tiles); wai_next (&wai_name)) +#define FOR_DISTRICTS_AROUND(wai_name, _x, _y, _completed_only) for (struct work_area_iter wai_name = wai_init_districts (_x, _y, _completed_only); (wai_name.n < wai_name.num_tiles); wai_next (&wai_name)) void tai_get_coords (struct tiles_around_iter * tai, int * out_x, int * out_y) @@ -4538,11 +4569,11 @@ refresh_distribution_hubs_for_city (City * city) (city == NULL)) return; - FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { int tx = wai.tile_x, ty = wai.tile_y; Tile * tile = wai.tile; - struct district_instance * inst = get_district_instance (tile); - if (inst == NULL || inst->district_type != DISTRIBUTION_HUB_DISTRICT_ID) + struct district_instance * inst = wai.district_inst; + if (inst->district_type != DISTRIBUTION_HUB_DISTRICT_ID) continue; on_distribution_hub_completed (tile, tx, ty); } @@ -7442,9 +7473,8 @@ can_build_district_on_tile (Tile * tile, int district_id) if (! cfg->allow_multiple) { int x, y; tile_coords_from_ptr (&p_bic_data->Map, tile, &x, &y); - FOR_TILES_AROUND(tai, is->workable_tile_count, x, y) { - struct district_instance * nearby_inst = get_district_instance (tai.tile); - if ((nearby_inst != NULL) && (nearby_inst->district_type == district_id)) + FOR_DISTRICTS_AROUND (wai, x, y, false) { + if (wai.district_inst->district_type == district_id) return false; } } @@ -7501,19 +7531,13 @@ calculate_city_center_district_bonus (City * city, int * out_food, int * out_shi int utilized_neighborhoods = count_utilized_neighborhoods_in_city_radius (city); int neighborhoods_counted = 0; - FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { if ((wai.dx == 0) && (wai.dy == 0)) continue; Tile * tile = wai.tile; - struct district_instance * inst = get_district_instance (tile); - if (inst == NULL) - continue; + struct district_instance * inst = wai.district_inst; int district_id = inst->district_type; - if ((district_id < 0) || (district_id >= is->district_count)) - continue; - if (! district_is_complete (tile, district_id)) - continue; if (is->current_config.enable_neighborhood_districts && (district_id == NEIGHBORHOOD_DISTRICT_ID)) { @@ -8051,12 +8075,11 @@ city_has_wonder_district_with_no_completed_wonder (City * city) if (! is->current_config.enable_wonder_districts || (city == NULL)) return false; - FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { int x = wai.tile_x, y = wai.tile_y; Tile * candidate = wai.tile; - struct district_instance * inst = get_district_instance (candidate); - if (inst == NULL || inst->district_type != WONDER_DISTRICT_ID) continue; - if (! district_is_complete (candidate, WONDER_DISTRICT_ID)) continue; + struct district_instance * inst = wai.district_inst; + if (inst->district_type != WONDER_DISTRICT_ID) continue; // Get wonder district info struct wonder_district_info * info = get_wonder_district_info (candidate); @@ -8269,15 +8292,12 @@ calculate_district_culture_science_bonuses (City * city, int * culture_bonus, in int total_science = 0; int utilized_neighborhoods = count_utilized_neighborhoods_in_city_radius (city); - FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { if ((wai.dx == 0) && (wai.dy == 0)) continue; Tile * tile = wai.tile; - struct district_instance * inst = get_district_instance (tile); - if (inst == NULL) continue; + struct district_instance * inst = wai.district_inst; int district_id = inst->district_type; - if ((district_id < 0) || (district_id >= is->district_count)) continue; - if (! district_is_complete (tile, district_id)) continue; struct district_config const * cfg = &is->district_configs[district_id]; int district_culture_bonus = 0; @@ -8396,17 +8416,14 @@ city_has_other_completed_district (City * city, int district_id, int removed_x, (city == NULL) || (district_id < 0) || (district_id >= is->district_count)) return false; - FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { int x = wai.tile_x, y = wai.tile_y; Tile * candidate = wai.tile; if ((x == removed_x) && (y == removed_y)) continue; - struct district_instance * inst = get_district_instance (candidate); - if (inst == NULL || inst->district_type != district_id) - continue; - - if (! district_is_complete (candidate, district_id)) + struct district_instance * inst = wai.district_inst; + if (inst->district_type != district_id) continue; return true; @@ -8800,14 +8817,14 @@ city_has_assigned_wonder_district (City * city, Tile * ignore_tile) if (! is->current_config.enable_wonder_districts || (city == NULL)) return false; - FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, false) { int x = wai.tile_x, y = wai.tile_y; Tile * candidate = wai.tile; if (candidate == ignore_tile) continue; - struct district_instance * inst = get_district_instance (candidate); - if ((inst == NULL) || (inst->district_type != WONDER_DISTRICT_ID)) + struct district_instance * inst = wai.district_inst; + if (inst->district_type != WONDER_DISTRICT_ID) continue; struct wonder_district_info * info = inst ? &inst->wonder_info : NULL; @@ -8832,11 +8849,11 @@ free_wonder_district_for_city (City * city) if (city_needs_wonder_district (city)) return false; - FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, false) { int x = wai.tile_x, y = wai.tile_y; Tile * tile = wai.tile; - struct district_instance * inst = get_district_instance (tile); - if (inst == NULL || inst->district_type != WONDER_DISTRICT_ID) + struct district_instance * inst = wai.district_inst; + if (inst->district_type != WONDER_DISTRICT_ID) continue; struct wonder_district_info * info = get_wonder_district_info (tile); @@ -8864,12 +8881,11 @@ reserve_wonder_district_for_city (City * city) if (city_has_assigned_wonder_district (city, NULL)) return true; - FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { int x = wai.tile_x, y = wai.tile_y; Tile * candidate = wai.tile; - struct district_instance * inst = get_district_instance (candidate); - if (inst == NULL || inst->district_type != WONDER_DISTRICT_ID) continue; - if (! district_is_complete (candidate, WONDER_DISTRICT_ID)) continue; + struct district_instance * inst = wai.district_inst; + if (inst->district_type != WONDER_DISTRICT_ID) continue; struct wonder_district_info * info = &inst->wonder_info; if (info->state == WDS_COMPLETED) continue; @@ -8902,9 +8918,12 @@ release_wonder_district_reservation (City * city) return; // Find and remove any reservations for this city - FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { - Tile * candidate = wai.tile; - struct wonder_district_info * info = get_wonder_district_info (candidate); + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, false) { + struct district_instance * inst = wai.district_inst; + if (inst->district_type != WONDER_DISTRICT_ID) + continue; + + struct wonder_district_info * info = &inst->wonder_info; if ((info != NULL) && (info->state == WDS_UNDER_CONSTRUCTION) && (info->city_id == city->Body.ID)) { @@ -13543,14 +13562,11 @@ patch_City_Form_draw (City_Form * this) int district_gold = 0; int city_civ_id = city->Body.CivID; - FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { if ((wai.dx == 0) && (wai.dy == 0)) continue; Tile * tile = wai.tile; - struct district_instance * inst = get_district_instance (tile); - if (inst == NULL) continue; + struct district_instance * inst = wai.district_inst; int district_id = inst->district_type; - if (district_id < 0 || district_id >= is->district_count) continue; - if (! district_is_complete (tile, district_id)) continue; struct district_config const * cfg = &is->district_configs[district_id]; int gold_bonus = 0; @@ -16567,15 +16583,14 @@ copy_building_with_cities_in_radius (City * source, int improv_id, int required_ City_add_or_remove_improvement (city, __, improv_id, 1, false); } } - } + } // Else there may be multiple district instances of this type, so check each tile around the city else { - FOR_WORK_AREA_AROUND (wai, source->Body.X, source->Body.Y) { + FOR_DISTRICTS_AROUND (wai, source->Body.X, source->Body.Y, true) { int x = wai.tile_x, y = wai.tile_y; Tile * tile = wai.tile; - struct district_instance * inst = get_district_instance (tile); - if (inst == NULL || inst->district_type != required_district_id) continue; - if (! district_is_complete (tile, required_district_id)) continue; + struct district_instance * inst = wai.district_inst; + if (inst->district_type != required_district_id) continue; FOR_CITIES_OF (coi, source->Body.CivID) { City * city = coi.city; @@ -16632,19 +16647,13 @@ grant_existing_district_buildings_to_city (City * city) bool prev_flag = is->sharing_buildings_by_districts_in_progress; is->sharing_buildings_by_districts_in_progress = true; - FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { int tile_x = wai.tile_x; int tile_y = wai.tile_y; Tile * tile = wai.tile; - struct district_instance * inst = get_district_instance (tile); - if (inst == NULL) - continue; + struct district_instance * inst = wai.district_inst; int district_id = inst->district_type; - if ((district_id < 0) || (district_id >= is->district_count)) - continue; - if (! district_is_complete (tile, district_id)) - continue; struct district_infos * info = &is->district_infos[district_id]; if (info->dependent_building_count <= 0) @@ -17090,14 +17099,13 @@ handle_possible_duplicate_small_wonders(City * city, Leader * leader) if ((city == NULL) || (leader == NULL)) return; - FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { int x = wai.tile_x, y = wai.tile_y; Tile * tile = wai.tile; // Make sure it's a completed wonder district - struct district_instance * inst = get_district_instance (tile); - if ((inst == NULL) || (inst->district_type != WONDER_DISTRICT_ID)) continue; - if (! district_is_complete (tile, WONDER_DISTRICT_ID)) continue; + struct district_instance * inst = wai.district_inst; + if (inst->district_type != WONDER_DISTRICT_ID) continue; struct wonder_district_info * info = &inst->wonder_info; if ((info == NULL) || (info->state != WDS_COMPLETED)) continue; @@ -17135,14 +17143,13 @@ grant_nearby_wonders_to_city (City * city) (city == NULL)) return; - FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { int x = wai.tile_x, y = wai.tile_y; Tile * tile = wai.tile; // Make sure Wonder is completed - struct district_instance * inst = get_district_instance (tile); - if ((inst == NULL) || (inst->district_type != WONDER_DISTRICT_ID)) continue; - if (! district_is_complete (tile, WONDER_DISTRICT_ID)) continue; + struct district_instance * inst = wai.district_inst; + if (inst->district_type != WONDER_DISTRICT_ID) continue; struct wonder_district_info * info = &inst->wonder_info; if ((info == NULL) || (info->state != WDS_COMPLETED)) continue; @@ -22797,11 +22804,9 @@ patch_City_Form_draw_yields_on_worked_tiles (City_Form * this) if (is->current_config.enable_districts && is->current_config.enable_neighborhood_districts) remaining_utilized_neighborhoods = count_utilized_neighborhoods_in_city_radius (city); - FOR_WORK_AREA_AROUND (wai, city_x, city_y) { - struct district_instance * inst = get_district_instance (wai.tile); - if (inst == NULL) continue; + FOR_DISTRICTS_AROUND (wai, city_x, city_y, true) { + struct district_instance * inst = wai.district_inst; int district_id = inst->district_type; - if (!district_is_complete (wai.tile, district_id)) continue; bool is_distribution_hub = district_id == DISTRIBUTION_HUB_DISTRICT_ID; bool is_natural_wonder = district_id == NATURAL_WONDER_DISTRICT_ID; @@ -24412,14 +24417,11 @@ ai_move_district_worker (Unit * worker, struct district_worker_record * rec) // One final check: do we still need the district? Check for any dupes nearby if (req->district_id != DISTRIBUTION_HUB_DISTRICT_ID && req->district_id != NEIGHBORHOOD_DISTRICT_ID) { - int civ_id = worker->Body.CivID; - FOR_TILES_AROUND (tai, is->workable_tile_count, request_city->Body.X, request_city->Body.Y) { - Tile * nearby = tai.tile; - if (nearby == p_null_tile || nearby == tile) continue; - if (nearby->vtable->m38_Get_Territory_OwnerID (nearby) != civ_id) continue; - - struct district_instance * nearby_inst = get_district_instance (nearby); - if (nearby_inst != NULL && nearby_inst->district_type == req->district_id) { + FOR_DISTRICTS_AROUND (wai, request_city->Body.X, request_city->Body.Y, false) { + if (wai.tile == tile) + continue; + + if (wai.district_inst->district_type == req->district_id) { snprintf (ss, sizeof ss, "ai_move_district_worker: Found duplicate district ID %d near city at (%d,%d), cancelling request\n", req->district_id, request_city->Body.X, request_city->Body.Y); (*p_OutputDebugStringA) (ss); clear_city_district_request (request_city, req->district_id); @@ -24664,24 +24666,12 @@ patch_City_Form_draw_food_income_icons (City_Form * this) if (city == NULL) return; - int city_id = city->Body.ID; - int civ_id = city->Body.CivID; - // Calculate standard district food bonus int standard_district_food = 0; - FOR_TILES_AROUND (tai, is->workable_tile_count, city->Body.X, city->Body.Y) { - if (tai.tile == NULL || tai.tile == p_null_tile) continue; - if (tai.tile->Territory_OwnerID != civ_id) continue; - if (tile_has_enemy_unit (tai.tile, civ_id)) continue; - if (tai.tile->vtable->m20_Check_Pollution (tai.tile, __, 0)) continue; - struct district_instance * inst = get_district_instance (tai.tile); - if (inst == NULL) continue; - int district_id = inst->district_type; - if ((district_id < 0) || (district_id >= is->district_count)) continue; - if (! district_is_complete (tai.tile, district_id)) continue; - + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { + int district_id = wai.district_inst->district_type; int food_bonus = 0; - get_effective_district_yields (inst, &is->district_configs[district_id], &food_bonus, NULL, NULL, NULL, NULL); + get_effective_district_yields (wai.district_inst, &is->district_configs[district_id], &food_bonus, NULL, NULL, NULL, NULL); standard_district_food += food_bonus; } From e53de44ef3aa881322bcc9091b82320e47a1b768 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 13:39:44 -0800 Subject: [PATCH 069/356] Add distribution_hub_yield_division_mode --- C3X.h | 6 ++++++ default.c3x_config.ini | 11 ++++++----- injected_code.c | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/C3X.h b/C3X.h index 5cbec2f8..61f847bf 100644 --- a/C3X.h +++ b/C3X.h @@ -129,6 +129,11 @@ enum day_night_cycle_mode { DNCM_SPECIFIED }; +enum distribution_hub_yield_division_mode { + DHYDM_FLAT = 0, + DHYDM_SCALE_BY_CITY_COUNT +}; + enum perfume_kind { PK_PRODUCTION = 0, PK_TECHNOLOGY, @@ -348,6 +353,7 @@ struct c3x_config { bool completed_wonder_districts_can_be_destroyed; bool destroyed_wonders_can_be_built_again; + int distribution_hub_yield_division_mode; int distribution_hub_food_yield_divisor; int distribution_hub_shield_yield_divisor; int ai_ideal_distribution_hub_count_per_100_cities; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 1adfc302..57f7cf93 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -868,11 +868,12 @@ destroyed_wonders_can_be_built_again = true ; Distribution Hubs work as "breadbaskets" and mining areas far from urban centers, minimizing local city potential but benefiting the entire civilization. ; Distribution Hubs make surrounding tiles unworkable and instead distribute their raw food and shield yields to ALL connected cities in your civilization. -; The divisor settings prevent this from being overpowered by dividing the raw yields before distribution (e.g., with food_divisor = 2, a hub with 10 -; raw food provides 5 food to each connected city). Divisor results are rounded down. -; Distribution Hub yields are subject to corruption as with regular shields. The ai_ideal_distribution_hub_count_per_100_cities setting controls how many -; Distribution Hubs the AI tries to maintain per 100 cities (e.g., 25 means the AI aims for 1 hub per 4 cities). -; Only applies when enable_distribution_hub_districts is set to true. +; Yields are subject to corruption as with regular shields. +; The ai_ideal_distribution_hub_count_per_100_cities setting controls how many hubs the AI tries to maintain per 100 cities (e.g., 25 means the AI aims for 1 hub per 4 cities). +; Distribution hub yield division mode controls how a hub splits its collected food/shields across connected cities: +; flat: Divide raw yields by the configured divisors (distribution_hub_food_yield_divisor / distribution_hub_shield_yield_divisor) +; scale-by-city-count:Divide by (connected city count / divisor), letting hubs scale with network size (e.g., 12 food, 10 cities, divisor 2 => 12 / (10/2) = 2.4 -> 2) +distribution_hub_yield_division_mode = scale-by-city-count distribution_hub_food_yield_divisor = 3 distribution_hub_shield_yield_divisor = 4 ai_ideal_distribution_hub_count_per_100_cities = 25 diff --git a/injected_code.c b/injected_code.c index 928bb9d2..15a8369e 100644 --- a/injected_code.c +++ b/injected_code.c @@ -1682,6 +1682,16 @@ read_day_night_cycle_mode (struct string_slice const * s, int * out_val) return false; } +bool +read_distribution_hub_yield_division_mode (struct string_slice const * s, int * out_val) +{ + struct string_slice trimmed = trim_string_slice (s, 1); + if (slice_matches_str (&trimmed, "flat" )) { *out_val = DHYDM_FLAT; return true; } + else if (slice_matches_str (&trimmed, "scale-by-city-count" )) { *out_val = DHYDM_SCALE_BY_CITY_COUNT; return true; } + else + return false; +} + bool read_square_type_value (struct string_slice const * s, enum SquareTypes * out_type) { @@ -2114,6 +2124,9 @@ load_config (char const * file_path, int path_is_relative_to_mod_dir) } else if (slice_matches_str (&p.key, "day_night_cycle_mode")) { if (! read_day_night_cycle_mode (&value, (int *)&cfg->day_night_cycle_mode)) handle_config_error (&p, CPE_BAD_VALUE); + } else if (slice_matches_str (&p.key, "distribution_hub_yield_division_mode")) { + if (! read_distribution_hub_yield_division_mode (&value, (int *)&cfg->distribution_hub_yield_division_mode)) + handle_config_error (&p, CPE_BAD_VALUE); } else if (slice_matches_str (&p.key, "ptw_like_artillery_targeting")) { if (! read_ptw_arty_types (&value, &unrecognized_lines, @@ -4339,8 +4352,24 @@ recompute_distribution_hub_yields (struct distribution_hub_record * rec) if (shield_div <= 0) shield_div = 1; - rec->food_yield = food_sum / food_div; - rec->shield_yield = shield_sum / shield_div; + int connected_city_count = 0; + if (anchor_city != NULL) { + FOR_CITIES_OF (coi, rec->civ_id) { + City * other_city = coi.city; + if ((other_city != NULL) && distribution_hub_accessible_to_city (rec, other_city)) + connected_city_count++; + } + } + if (connected_city_count <= 0) + connected_city_count = 1; + + if (is->current_config.distribution_hub_yield_division_mode == DHYDM_SCALE_BY_CITY_COUNT) { + rec->food_yield = food_sum / (connected_city_count / food_div); + rec->shield_yield = shield_sum / (connected_city_count / shield_div); + } else { + rec->food_yield = food_sum / food_div; + rec->shield_yield = shield_sum / shield_div; + } } void @@ -11216,6 +11245,7 @@ patch_init_floating_point () base_config.double_minimap_size = MDM_HIGH_DEF; base_config.city_work_radius = 2; base_config.day_night_cycle_mode = DNCM_OFF; + base_config.distribution_hub_yield_division_mode = DHYDM_FLAT; 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; for (int n = 0; n < ARRAY_LEN (integer_config_options); n++) From dc546cf5c4f2fd006b0a9b64e2f59ecaafe66aee Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 13:48:51 -0800 Subject: [PATCH 070/356] On city screen, draw distro hub yields in separate, larger loop potentially outside of work radius --- injected_code.c | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/injected_code.c b/injected_code.c index 15a8369e..136a2c19 100644 --- a/injected_code.c +++ b/injected_code.c @@ -22848,13 +22848,14 @@ patch_City_Form_draw_yields_on_worked_tiles (City_Form * this) if (is_natural_wonder && (!is->current_config.enable_natural_wonders)) continue; - if (!is_distribution_hub && !is_natural_wonder && - (!is->current_config.enable_districts)) + if (is_distribution_hub) + continue; + + if (!is_natural_wonder && (!is->current_config.enable_districts)) continue; // For neighborhood districts, check if population is high enough to utilize them - if (!is_distribution_hub && - is->current_config.enable_districts && + if (is->current_config.enable_districts && is->current_config.enable_neighborhood_districts && district_id == NEIGHBORHOOD_DISTRICT_ID) { // Only draw yields if this neighborhood is utilized @@ -22868,10 +22869,32 @@ patch_City_Form_draw_yields_on_worked_tiles (City_Form * this) int screen_y = center_screen_y + (wai.dy * tile_half_height); // Call the appropriate drawing function - if (is_distribution_hub) { - draw_distribution_hub_yields (this, wai.tile, wai.tile_x, wai.tile_y, screen_x, screen_y); - } else { - draw_district_yields (this, wai.tile, district_id, screen_x, screen_y); + draw_district_yields (this, wai.tile, district_id, screen_x, screen_y); + } + + // Draw distribution hub yields around a larger radius so connected hubs outside the work area are shown + if (is->current_config.enable_districts && is->current_config.enable_distribution_hub_districts) { + int const max_tiles = workable_tile_counts[7]; + + for (int ni = 0; ni < max_tiles; ni++) { + int dx, dy; + patch_ni_to_diff_for_work_area (ni, &dx, &dy); + + int tile_x = city_x + dx; + int tile_y = city_y + dy; + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + + Tile * tile = tile_at (tile_x, tile_y); + if ((tile == NULL) || (tile == p_null_tile)) + continue; + + struct district_instance * inst = get_district_instance (tile); + if ((inst == NULL) || (inst->district_type != DISTRIBUTION_HUB_DISTRICT_ID)) + continue; + + int screen_x = center_screen_x + (dx * tile_half_width); + int screen_y = center_screen_y + (dy * tile_half_height); + draw_distribution_hub_yields (this, tile, tile_x, tile_y, screen_x, screen_y); } } } From 0e3f91214fb3e5235d9855ba477f907faea044ed Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 14:13:03 -0800 Subject: [PATCH 071/356] Add happiness bonus variable for districts and natural wonders --- C3X.h | 24 +++- default.districts_config.txt | 13 ++- default.districts_natural_wonders_config.txt | 13 ++- injected_code.c | 114 +++++++++++++++++++ 4 files changed, 156 insertions(+), 8 deletions(-) diff --git a/C3X.h b/C3X.h index 61f847bf..2286d5c7 100644 --- a/C3X.h +++ b/C3X.h @@ -581,6 +581,8 @@ struct district_config { int food_bonus; int gold_bonus; int shield_bonus; + int happy_bonus; + int unhappy_bonus; int defense_bonus_percent; }; @@ -621,6 +623,8 @@ struct natural_wonder_district_config { int food_bonus; int gold_bonus; int shield_bonus; + int happy_bonus; + int unhappy_bonus; bool is_dynamic; }; @@ -647,7 +651,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 5, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, - .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .defense_bonus_percent = 25 + .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .happy_bonus = 0, .unhappy_bonus = 0, .defense_bonus_percent = 25 }, { @@ -656,7 +660,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"WonderDistrict.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happy_bonus = 0, .unhappy_bonus = 0, .defense_bonus_percent = 0 }, { @@ -665,7 +669,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"DistributionHub.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 2, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happy_bonus = 0, .unhappy_bonus = 0, .defense_bonus_percent = 0 }, { @@ -674,7 +678,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"Aerodrome.pcx"}, .dependent_improvements = {"Airport"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 1, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happy_bonus = 0, .unhappy_bonus = 0, .defense_bonus_percent = 0 }, { .command = -1, .name = "Natural Wonder", .tooltip = NULL, @@ -682,7 +686,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {0}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 0, .max_building_index = 0, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happy_bonus = 0, .unhappy_bonus = 0, .defense_bonus_percent = 0 }, { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", @@ -690,7 +694,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .dependent_improvements = {"Harbor", "Commercial Dock"}, .buildable_square_types_mask = (1 << SQ_Coast), .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happy_bonus = 0, .unhappy_bonus = 0, .defense_bonus_percent = 0 } }; @@ -713,6 +717,8 @@ struct parsed_district_definition { int food_bonus; int gold_bonus; int shield_bonus; + int happy_bonus; + int unhappy_bonus; unsigned short buildable_square_types_mask; bool has_name; bool has_tooltip; @@ -730,6 +736,8 @@ struct parsed_district_definition { bool has_food_bonus; bool has_gold_bonus; bool has_shield_bonus; + bool has_happy_bonus; + bool has_unhappy_bonus; bool has_buildable_on; }; @@ -761,6 +769,8 @@ struct parsed_natural_wonder_definition { int food_bonus; int gold_bonus; int shield_bonus; + int happy_bonus; + int unhappy_bonus; bool has_name; bool has_img_path; bool has_img_row; @@ -773,6 +783,8 @@ struct parsed_natural_wonder_definition { bool has_food_bonus; bool has_gold_bonus; bool has_shield_bonus; + bool has_happy_bonus; + bool has_unhappy_bonus; }; struct scenario_district_entry { diff --git a/default.districts_config.txt b/default.districts_config.txt index 2b0ebec3..920283dd 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -16,6 +16,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #District name = Holy Site @@ -35,6 +36,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #District name = Campus @@ -54,6 +56,7 @@ science_bonus = 2 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #District name = Entertainment Complex @@ -73,6 +76,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 2 shield_bonus = 0 +happy_bonus = 0 #District name = Commercial Hub @@ -92,6 +96,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 2 shield_bonus = 0 +happy_bonus = 0 #District name = Industrial Zone @@ -111,6 +116,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 4 +happy_bonus = 0 #District name = Neighborhood @@ -124,6 +130,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 1 shield_bonus = 0 +happy_bonus = 0 #District name = Wonder District @@ -137,6 +144,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #District name = Distribution Hub @@ -150,6 +158,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #District name = Aerodrome @@ -164,6 +173,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #District name = Port @@ -177,4 +187,5 @@ culture_bonus = 0 science_bonus = 0 food_bonus = 2 gold_bonus = 2 -shield_bonus = 0 \ No newline at end of file +shield_bonus = 0 +happy_bonus = 0 \ No newline at end of file diff --git a/default.districts_natural_wonders_config.txt b/default.districts_natural_wonders_config.txt index caf8d322..4ce24d36 100644 --- a/default.districts_natural_wonders_config.txt +++ b/default.districts_natural_wonders_config.txt @@ -11,6 +11,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #Wonder name = Yosemite @@ -24,6 +25,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #Wonder name = Mount Fuji @@ -36,6 +38,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #Wonder name = Yellowstone @@ -49,6 +52,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #Wonder name = Mount Everest @@ -62,6 +66,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #Wonder name = Zhangjiajie Mountains @@ -75,6 +80,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #Wonder name = Mount Kilimanjaro @@ -87,6 +93,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #Wonder name = Great Barrier Reef @@ -100,6 +107,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #Wonder name = Matterhorn @@ -113,6 +121,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #Wonder name = Moraine Lake @@ -126,6 +135,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happy_bonus = 0 #Wonder name = Tropical Rainforest @@ -138,4 +148,5 @@ culture_bonus = 2 science_bonus = 1 food_bonus = 0 gold_bonus = 0 -shield_bonus = 0 \ No newline at end of file +shield_bonus = 0 +happy_bonus = 0 \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index 136a2c19..8a16de8e 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5183,6 +5183,10 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->gold_bonus = def->gold_bonus; if (def->has_shield_bonus) cfg->shield_bonus = def->shield_bonus; + if (def->has_happy_bonus) + cfg->happy_bonus = def->happy_bonus; + if (def->has_unhappy_bonus) + cfg->unhappy_bonus = def->unhappy_bonus; if (def->has_buildable_on) cfg->buildable_square_types_mask = def->buildable_square_types_mask; @@ -5296,6 +5300,10 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.food_bonus = def->has_food_bonus ? def->food_bonus : 0; new_cfg.gold_bonus = def->has_gold_bonus ? def->gold_bonus : 0; new_cfg.shield_bonus = def->has_shield_bonus ? def->shield_bonus : 0; + new_cfg.happy_bonus = def->has_happy_bonus ? def->happy_bonus : 0; + new_cfg.unhappy_bonus = def->has_unhappy_bonus ? def->unhappy_bonus : 0; + new_cfg.happy_bonus = def->has_happy_bonus ? def->happy_bonus : 0; + new_cfg.unhappy_bonus = def->has_unhappy_bonus ? def->unhappy_bonus : 0; new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); new_cfg.dependent_improvement_count = def->has_dependent_improvements ? def->dependent_improvement_count : 0; @@ -5539,6 +5547,24 @@ handle_district_definition_key (struct parsed_district_definition * def, } else add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + } else if (slice_matches_str (key, "happy_bonus")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->happy_bonus = ival; + def->has_happy_bonus = true; + } else + add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + + } else if (slice_matches_str (key, "unhappy_bonus")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->unhappy_bonus = ival; + def->has_unhappy_bonus = true; + } else + add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + } else add_unrecognized_key_error (unrecognized_keys, line_number, key); } @@ -6349,6 +6375,28 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); } + } else if (slice_matches_str (key, "happy_bonus")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->happy_bonus = ival; + def->has_happy_bonus = true; + } else { + def->has_happy_bonus = false; + add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + } + + } else if (slice_matches_str (key, "unhappy_bonus")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->unhappy_bonus = ival; + def->has_unhappy_bonus = true; + } else { + def->has_unhappy_bonus = false; + add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + } + } else add_unrecognized_key_error (unrecognized_keys, line_number, key); } @@ -8352,6 +8400,59 @@ calculate_district_culture_science_bonuses (City * city, int * culture_bonus, in *science_bonus = total_science; } +void +calculate_district_happiness_bonus (City * city, int * happy_bonus, int * unhappy_bonus) +{ + if (happy_bonus != NULL) + *happy_bonus = 0; + if (unhappy_bonus != NULL) + *unhappy_bonus = 0; + + if (! is->current_config.enable_districts || (city == NULL)) + return; + + int total_happy = 0; + int total_unhappy = 0; + int utilized_neighborhoods = count_utilized_neighborhoods_in_city_radius (city); + + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { + if ((wai.dx == 0) && (wai.dy == 0)) + continue; + + struct district_instance * inst = wai.district_inst; + int district_id = inst->district_type; + struct district_config const * cfg = &is->district_configs[district_id]; + + bool is_neighborhood = (cfg->command == UCV_Build_Neighborhood); + if (is_neighborhood && (utilized_neighborhoods <= 0)) + continue; + + if (is_neighborhood) + utilized_neighborhoods--; + + if (cfg->happy_bonus != 0) + total_happy += cfg->happy_bonus; + if (cfg->unhappy_bonus != 0) + total_unhappy += cfg->unhappy_bonus; + + if (is->current_config.enable_natural_wonders && district_id == NATURAL_WONDER_DISTRICT_ID) { + struct natural_wonder_district_config const * nwcfg = + get_natural_wonder_config_by_id (inst->natural_wonder_info.natural_wonder_id); + if (nwcfg != NULL) { + if (nwcfg->happy_bonus != 0) + total_happy += nwcfg->happy_bonus; + if (nwcfg->unhappy_bonus != 0) + total_unhappy += nwcfg->unhappy_bonus; + } + } + } + + if (happy_bonus != NULL) + *happy_bonus = total_happy; + if (unhappy_bonus != NULL) + *unhappy_bonus = total_unhappy; +} + int __fastcall patch_City_requires_improvement_to_grow (City * this) { @@ -23200,6 +23301,19 @@ patch_City_add_happiness_from_buildings (City * this, int edx, int * inout_happi City_add_happiness_from_buildings (this, __, inout_happiness, inout_unhappiness); + if (is->current_config.enable_districts) { + int district_happy = 0, district_unhappy = 0; + calculate_district_happiness_bonus (this, &district_happy, &district_unhappy); + + if (district_happy != 0) + *inout_happiness += district_happy; + + if (district_unhappy != 0) { + *inout_happiness -= district_unhappy; + this->Body.UnhappyThisCityImprovementsPercent += (char)district_unhappy; + } + } + if (restore_improv_counts) { for (int n = 0; n < p_bic_data->ImprovementsCount; n++) owner->Improvement_Counts[n] = is->saved_improv_counts[n]; From 4378b1e4e16d849312fcbae0150ae11a7b561a84 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 14:53:22 -0800 Subject: [PATCH 072/356] More refactoring --- C3X.h | 36 ++-- default.districts_config.txt | 22 +- default.districts_natural_wonders_config.txt | 22 +- injected_code.c | 203 ++++++++----------- 4 files changed, 125 insertions(+), 158 deletions(-) diff --git a/C3X.h b/C3X.h index 2286d5c7..16d77a0c 100644 --- a/C3X.h +++ b/C3X.h @@ -581,8 +581,8 @@ struct district_config { int food_bonus; int gold_bonus; int shield_bonus; - int happy_bonus; - int unhappy_bonus; + int happiness_bonus; + int unhappiness_bonus; int defense_bonus_percent; }; @@ -623,8 +623,8 @@ struct natural_wonder_district_config { int food_bonus; int gold_bonus; int shield_bonus; - int happy_bonus; - int unhappy_bonus; + int happiness_bonus; + int unhappiness_bonus; bool is_dynamic; }; @@ -651,7 +651,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 5, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, - .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .happy_bonus = 0, .unhappy_bonus = 0, .defense_bonus_percent = 25 + .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 25 }, { @@ -660,7 +660,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"WonderDistrict.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happy_bonus = 0, .unhappy_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 0 }, { @@ -669,7 +669,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"DistributionHub.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 2, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happy_bonus = 0, .unhappy_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 0 }, { @@ -678,7 +678,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"Aerodrome.pcx"}, .dependent_improvements = {"Airport"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 1, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happy_bonus = 0, .unhappy_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 0 }, { .command = -1, .name = "Natural Wonder", .tooltip = NULL, @@ -686,7 +686,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {0}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 0, .max_building_index = 0, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happy_bonus = 0, .unhappy_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 0 }, { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", @@ -694,7 +694,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .dependent_improvements = {"Harbor", "Commercial Dock"}, .buildable_square_types_mask = (1 << SQ_Coast), .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happy_bonus = 0, .unhappy_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 0 } }; @@ -717,8 +717,8 @@ struct parsed_district_definition { int food_bonus; int gold_bonus; int shield_bonus; - int happy_bonus; - int unhappy_bonus; + int happiness_bonus; + int unhappiness_bonus; unsigned short buildable_square_types_mask; bool has_name; bool has_tooltip; @@ -736,8 +736,8 @@ struct parsed_district_definition { bool has_food_bonus; bool has_gold_bonus; bool has_shield_bonus; - bool has_happy_bonus; - bool has_unhappy_bonus; + bool has_happiness_bonus; + bool has_unhappiness_bonus; bool has_buildable_on; }; @@ -769,8 +769,8 @@ struct parsed_natural_wonder_definition { int food_bonus; int gold_bonus; int shield_bonus; - int happy_bonus; - int unhappy_bonus; + int happiness_bonus; + int unhappiness_bonus; bool has_name; bool has_img_path; bool has_img_row; @@ -783,8 +783,8 @@ struct parsed_natural_wonder_definition { bool has_food_bonus; bool has_gold_bonus; bool has_shield_bonus; - bool has_happy_bonus; - bool has_unhappy_bonus; + bool has_happiness_bonus; + bool has_unhappiness_bonus; }; struct scenario_district_entry { diff --git a/default.districts_config.txt b/default.districts_config.txt index 920283dd..91fa46d3 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -16,7 +16,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #District name = Holy Site @@ -36,7 +36,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #District name = Campus @@ -56,7 +56,7 @@ science_bonus = 2 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #District name = Entertainment Complex @@ -76,7 +76,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 2 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #District name = Commercial Hub @@ -96,7 +96,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 2 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #District name = Industrial Zone @@ -116,7 +116,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 4 -happy_bonus = 0 +happiness_bonus = 0 #District name = Neighborhood @@ -130,7 +130,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 1 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #District name = Wonder District @@ -144,7 +144,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #District name = Distribution Hub @@ -158,7 +158,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #District name = Aerodrome @@ -173,7 +173,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #District name = Port @@ -188,4 +188,4 @@ science_bonus = 0 food_bonus = 2 gold_bonus = 2 shield_bonus = 0 -happy_bonus = 0 \ No newline at end of file +happiness_bonus = 0 \ No newline at end of file diff --git a/default.districts_natural_wonders_config.txt b/default.districts_natural_wonders_config.txt index 4ce24d36..0b36359a 100644 --- a/default.districts_natural_wonders_config.txt +++ b/default.districts_natural_wonders_config.txt @@ -11,7 +11,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #Wonder name = Yosemite @@ -25,7 +25,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #Wonder name = Mount Fuji @@ -38,7 +38,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #Wonder name = Yellowstone @@ -52,7 +52,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #Wonder name = Mount Everest @@ -66,7 +66,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #Wonder name = Zhangjiajie Mountains @@ -80,7 +80,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #Wonder name = Mount Kilimanjaro @@ -93,7 +93,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #Wonder name = Great Barrier Reef @@ -107,7 +107,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #Wonder name = Matterhorn @@ -121,7 +121,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #Wonder name = Moraine Lake @@ -135,7 +135,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 +happiness_bonus = 0 #Wonder name = Tropical Rainforest @@ -149,4 +149,4 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happy_bonus = 0 \ No newline at end of file +happiness_bonus = 0 \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index 8a16de8e..6f2fb5a1 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5183,10 +5183,10 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->gold_bonus = def->gold_bonus; if (def->has_shield_bonus) cfg->shield_bonus = def->shield_bonus; - if (def->has_happy_bonus) - cfg->happy_bonus = def->happy_bonus; - if (def->has_unhappy_bonus) - cfg->unhappy_bonus = def->unhappy_bonus; + if (def->has_happiness_bonus) + cfg->happiness_bonus = def->happiness_bonus; + if (def->has_unhappiness_bonus) + cfg->unhappiness_bonus = def->unhappiness_bonus; if (def->has_buildable_on) cfg->buildable_square_types_mask = def->buildable_square_types_mask; @@ -5300,10 +5300,10 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.food_bonus = def->has_food_bonus ? def->food_bonus : 0; new_cfg.gold_bonus = def->has_gold_bonus ? def->gold_bonus : 0; new_cfg.shield_bonus = def->has_shield_bonus ? def->shield_bonus : 0; - new_cfg.happy_bonus = def->has_happy_bonus ? def->happy_bonus : 0; - new_cfg.unhappy_bonus = def->has_unhappy_bonus ? def->unhappy_bonus : 0; - new_cfg.happy_bonus = def->has_happy_bonus ? def->happy_bonus : 0; - new_cfg.unhappy_bonus = def->has_unhappy_bonus ? def->unhappy_bonus : 0; + new_cfg.happiness_bonus = def->has_happiness_bonus ? def->happiness_bonus : 0; + new_cfg.unhappiness_bonus = def->has_unhappiness_bonus ? def->unhappiness_bonus : 0; + new_cfg.happiness_bonus = def->has_happiness_bonus ? def->happiness_bonus : 0; + new_cfg.unhappiness_bonus = def->has_unhappiness_bonus ? def->unhappiness_bonus : 0; new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); new_cfg.dependent_improvement_count = def->has_dependent_improvements ? def->dependent_improvement_count : 0; @@ -5547,21 +5547,21 @@ handle_district_definition_key (struct parsed_district_definition * def, } else add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); - } else if (slice_matches_str (key, "happy_bonus")) { + } else if (slice_matches_str (key, "happiness_bonus")) { struct string_slice val_slice = *value; int ival; if (read_int (&val_slice, &ival)) { - def->happy_bonus = ival; - def->has_happy_bonus = true; + def->happiness_bonus = ival; + def->has_happiness_bonus = true; } else add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); - } else if (slice_matches_str (key, "unhappy_bonus")) { + } else if (slice_matches_str (key, "unhappiness_bonus")) { struct string_slice val_slice = *value; int ival; if (read_int (&val_slice, &ival)) { - def->unhappy_bonus = ival; - def->has_unhappy_bonus = true; + def->unhappiness_bonus = ival; + def->has_unhappiness_bonus = true; } else add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); @@ -6375,25 +6375,25 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); } - } else if (slice_matches_str (key, "happy_bonus")) { + } else if (slice_matches_str (key, "happiness_bonus")) { struct string_slice val_slice = *value; int ival; if (read_int (&val_slice, &ival)) { - def->happy_bonus = ival; - def->has_happy_bonus = true; + def->happiness_bonus = ival; + def->has_happiness_bonus = true; } else { - def->has_happy_bonus = false; + def->has_happiness_bonus = false; add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); } - } else if (slice_matches_str (key, "unhappy_bonus")) { + } else if (slice_matches_str (key, "unhappiness_bonus")) { struct string_slice val_slice = *value; int ival; if (read_int (&val_slice, &ival)) { - def->unhappy_bonus = ival; - def->has_unhappy_bonus = true; + def->unhappiness_bonus = ival; + def->has_unhappiness_bonus = true; } else { - def->has_unhappy_bonus = false; + def->has_unhappiness_bonus = false; add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); } @@ -8040,48 +8040,36 @@ get_completed_district_tile_for_city (City * city, int district_id, int * out_x, if ((city == NULL) || (district_id < 0) || (district_id >= is->district_count)) return NULL; - int civ_id = city->Body.CivID; - int city_x = city->Body.X; - int city_y = city->Body.Y; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = city_x + dx, y = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * candidate = tile_at (x, y); - if ((candidate == NULL) || (candidate == p_null_tile)) - continue; - if (candidate->vtable->m38_Get_Territory_OwnerID (candidate) != civ_id) - continue; - - struct district_instance * inst = get_district_instance (candidate); - if (inst != NULL && - inst->district_type == district_id && - district_is_complete (candidate, district_id)) { + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { + int x = wai.tile_x, y = wai.tile_y; + Tile * candidate = wai.tile; + struct district_instance * inst = wai.district_inst; - // For wonder districts, filter based on wonder_district_state - if (district_id == WONDER_DISTRICT_ID) { - struct wonder_district_info * info = &inst->wonder_info; - // Must be either unused or under construction by this city - if (info->state == WDS_COMPLETED) - continue; - if (info->state == WDS_UNDER_CONSTRUCTION && info->city_id != city->Body.ID) - continue; - if (info->state == WDS_UNDER_CONSTRUCTION) { - info->city = city; - info->city_id = city->Body.ID; - } - } + if (inst->district_type != district_id) + continue; - if (out_x != NULL) - *out_x = x; - if (out_y != NULL) - *out_y = y; - return candidate; + // For wonder districts, filter based on wonder_district_state + if (district_id == WONDER_DISTRICT_ID) { + struct wonder_district_info * info = &inst->wonder_info; + // Must be either unused or under construction by this city + if (info->state == WDS_COMPLETED) + continue; + if (info->state == WDS_UNDER_CONSTRUCTION && info->city_id != city->Body.ID) + continue; + if (info->state == WDS_UNDER_CONSTRUCTION) { + info->city = city; + info->city_id = city->Body.ID; } } - return NULL; + if (out_x != NULL) + *out_x = x; + if (out_y != NULL) + *out_y = y; + return candidate; + } + + return NULL; } bool @@ -8248,24 +8236,11 @@ count_neighborhoods_in_city_radius (City * city) return 0; int count = 0; - int city_x = city->Body.X; - int city_y = city->Body.Y; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = city_x + dx, y = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * tile = tile_at (x, y); - if ((tile == NULL) || (tile == p_null_tile)) - continue; - if (! (tile->vtable->m38_Get_Territory_OwnerID (tile) == city->Body.CivID)) - continue; - - struct district_instance * inst = get_district_instance (tile); + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { + struct district_instance * inst = wai.district_inst; if (inst != NULL && inst->district_type >= 0 && inst->district_type < is->district_count && - inst->district_type == NEIGHBORHOOD_DISTRICT_ID && - district_is_complete (tile, inst->district_type)) + inst->district_type == NEIGHBORHOOD_DISTRICT_ID) count++; } return count; @@ -8401,12 +8376,12 @@ calculate_district_culture_science_bonuses (City * city, int * culture_bonus, in } void -calculate_district_happiness_bonus (City * city, int * happy_bonus, int * unhappy_bonus) +calculate_district_happiness_bonus (City * city, int * happiness_bonus, int * unhappiness_bonus) { - if (happy_bonus != NULL) - *happy_bonus = 0; - if (unhappy_bonus != NULL) - *unhappy_bonus = 0; + if (happiness_bonus != NULL) + *happiness_bonus = 0; + if (unhappiness_bonus != NULL) + *unhappiness_bonus = 0; if (! is->current_config.enable_districts || (city == NULL)) return; @@ -8430,27 +8405,27 @@ calculate_district_happiness_bonus (City * city, int * happy_bonus, int * unhapp if (is_neighborhood) utilized_neighborhoods--; - if (cfg->happy_bonus != 0) - total_happy += cfg->happy_bonus; - if (cfg->unhappy_bonus != 0) - total_unhappy += cfg->unhappy_bonus; + if (cfg->happiness_bonus != 0) + total_happy += cfg->happiness_bonus; + if (cfg->unhappiness_bonus != 0) + total_unhappy += cfg->unhappiness_bonus; if (is->current_config.enable_natural_wonders && district_id == NATURAL_WONDER_DISTRICT_ID) { struct natural_wonder_district_config const * nwcfg = get_natural_wonder_config_by_id (inst->natural_wonder_info.natural_wonder_id); if (nwcfg != NULL) { - if (nwcfg->happy_bonus != 0) - total_happy += nwcfg->happy_bonus; - if (nwcfg->unhappy_bonus != 0) - total_unhappy += nwcfg->unhappy_bonus; + if (nwcfg->happiness_bonus != 0) + total_happy += nwcfg->happiness_bonus; + if (nwcfg->unhappiness_bonus != 0) + total_unhappy += nwcfg->unhappiness_bonus; } } } - if (happy_bonus != NULL) - *happy_bonus = total_happy; - if (unhappy_bonus != NULL) - *unhappy_bonus = total_unhappy; + if (happiness_bonus != NULL) + *happiness_bonus = total_happy; + if (unhappiness_bonus != NULL) + *unhappiness_bonus = total_unhappy; } int __fastcall @@ -16898,33 +16873,25 @@ patch_City_add_or_remove_improvement (City * this, int edx, int improv_id, int a // as completed (which switches the art over and prevents the tile from being used again) int x, y; if (add && is->current_config.enable_districts && is->current_config.enable_wonder_districts) { - if (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { - int matched_windex = find_wonder_config_index_by_improvement_id (improv_id); + if (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { + int matched_windex = find_wonder_config_index_by_improvement_id (improv_id); - if (matched_windex >= 0) { - int city_x = this->Body.X; - int city_y = this->Body.Y; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - x = city_x + dx, y = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * t = tile_at (x, y); - if (t == p_null_tile) continue; - if (t->vtable->m38_Get_Territory_OwnerID (t) != this->Body.CivID) continue; - - struct district_instance * inst = get_district_instance (t); - if (inst == NULL || inst->district_type != WONDER_DISTRICT_ID) continue; - if (! district_is_complete (t, inst->district_type)) continue; - - struct wonder_district_info * info = &inst->wonder_info; - if (info->state != WDS_UNDER_CONSTRUCTION) continue; - if (info->city_id != this->Body.ID) continue; - - // Mark this wonder district as completed with the wonder - info->city = this; - info->city_id = this->Body.ID; - info->state = WDS_COMPLETED; + if (matched_windex >= 0) { + FOR_DISTRICTS_AROUND (wai, this->Body.X, this->Body.Y, true) { + x = wai.tile_x; + y = wai.tile_y; + Tile * t = wai.tile; + struct district_instance * inst = wai.district_inst; + if (inst->district_type != WONDER_DISTRICT_ID) continue; + + struct wonder_district_info * info = &inst->wonder_info; + if (info->state != WDS_UNDER_CONSTRUCTION) continue; + if (info->city_id != this->Body.ID) continue; + + // Mark this wonder district as completed with the wonder + info->city = this; + info->city_id = this->Body.ID; + info->state = WDS_COMPLETED; info->wonder_index = matched_windex; break; } From 77c22cb68be4fad8c494e0f9873704f29a48cd32 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 23:43:02 -0800 Subject: [PATCH 073/356] Add FOR_CITIES_AROUND macro, replace repetitive loops --- injected_code.c | 162 ++++++++++++++---------------------------------- 1 file changed, 46 insertions(+), 116 deletions(-) diff --git a/injected_code.c b/injected_code.c index 6f2fb5a1..b8c44edd 100644 --- a/injected_code.c +++ b/injected_code.c @@ -3162,6 +3162,12 @@ tai_init (int num_tiles, int x, int y) #define FOR_TILES_AROUND(tai_name, _num_tiles, _x, _y) for (struct tiles_around_iter tai_name = tai_init (_num_tiles, _x, _y); (tai_name.n < tai_name.num_tiles); tai_next (&tai_name)) +enum work_area_iter_output_type { + WAIO_ANY, + WAIO_DISTRICTS, + WAIO_CITIES, +}; + struct work_area_iter { int center_x, center_y; int n, num_tiles; @@ -3170,8 +3176,9 @@ struct work_area_iter { int tile_x, tile_y; Tile * tile; struct district_instance * district_inst; - bool districts_only; - bool completed_only; + City * city; + enum work_area_iter_output_type output_type; + bool completed_districts_only; }; void @@ -3190,22 +3197,29 @@ wai_next (struct work_area_iter * wai) continue; if ((wai->civ_id < 0) || (candidate->vtable->m38_Get_Territory_OwnerID (candidate) != wai->civ_id)) continue; - if (candidate->CityID >= 0) - continue; if (tile_has_enemy_unit (candidate, wai->civ_id)) continue; if (candidate->vtable->m20_Check_Pollution (candidate, __, 0)) continue; + City * city; + if (wai->output_type == WAIO_CITIES) { + if (candidate->CityID < 0) + continue; + city = get_city_ptr (candidate->vtable->m45_Get_City_ID (candidate)); + if (city == NULL || city->Body.CivID != wai->civ_id) + continue; + } struct district_instance * inst = get_district_instance (candidate); - if (wai->districts_only) { + if (wai->output_type == WAIO_DISTRICTS) { if (inst == NULL) continue; int district_id = inst->district_type; if ((district_id < 0) || (district_id >= is->district_count)) continue; - if (wai->completed_only && (! district_is_complete (candidate, district_id))) + if (wai->completed_districts_only && (! district_is_complete (candidate, district_id))) continue; } + wai->city = city; wai->district_inst = inst; wai->tile = candidate; break; @@ -3215,7 +3229,7 @@ wai_next (struct work_area_iter * wai) } struct work_area_iter -wai_init_common (int x, int y, bool districts_only, bool completed_only) +wai_init_common (int x, int y, enum work_area_iter_output_type output, bool completed_districts_only) { struct work_area_iter tr; tr.center_x = x; @@ -3233,8 +3247,8 @@ wai_init_common (int x, int y, bool districts_only, bool completed_only) tr.dy = 0; tr.tile_x = 0; tr.tile_y = 0; - tr.districts_only = districts_only; - tr.completed_only = completed_only; + tr.output_type = output; + tr.completed_districts_only = completed_districts_only; wai_next (&tr); return tr; } @@ -3248,11 +3262,19 @@ wai_init (int x, int y) struct work_area_iter wai_init_districts (int x, int y, bool completed_only) { - return wai_init_common (x, y, true, completed_only); + return wai_init_common (x, y, WAIO_DISTRICTS, completed_only); +} + +struct work_area_iter +wai_init_cities (int x, int y) +{ + struct work_area_iter tr = wai_init_common (x, y, WAIO_CITIES, false); + return tr; } #define FOR_WORK_AREA_AROUND(wai_name, _x, _y) for (struct work_area_iter wai_name = wai_init (_x, _y); (wai_name.n < wai_name.num_tiles); wai_next (&wai_name)) #define FOR_DISTRICTS_AROUND(wai_name, _x, _y, _completed_only) for (struct work_area_iter wai_name = wai_init_districts (_x, _y, _completed_only); (wai_name.n < wai_name.num_tiles); wai_next (&wai_name)) +#define FOR_CITIES_AROUND(wai_name, _x, _y) for (struct work_area_iter wai_name = wai_init_cities (_x, _y); (wai_name.n < wai_name.num_tiles); wai_next (&wai_name)) void tai_get_coords (struct tiles_around_iter * tai, int * out_x, int * out_y) @@ -7941,27 +7963,6 @@ find_tile_for_distribution_hub_district (City * city, int * out_x, int * out_y) return best_tile; } -int -count_cities_in_work_radius_of_tile (int tile_x, int tile_y, int civ_id) -{ - int count = 0; - - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = tile_x + dx, y = tile_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * tile = tile_at (x, y); - if ((tile != NULL) && (tile != p_null_tile) && (tile->CityID >= 0)) { - City * city = get_city_ptr (tile->vtable->m45_Get_City_ID (tile)); - if ((city != NULL) && (city->Body.CivID == civ_id)) - count += 1; - } - } - - return count; -} - Tile * find_tile_for_district (City * city, int district_id, int * out_x, int * out_y) { @@ -8565,22 +8566,8 @@ district_instance_is_redundant (struct district_instance * inst, Tile * tile) if (district_id == WONDER_DISTRICT_ID) return inst->wonder_info.state == WDS_UNUSED; - bool found_city = false; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = tile_x + dx, y = tile_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * candidate = tile_at (x, y); - if ((candidate == NULL) || (candidate == p_null_tile)) - continue; - - City * city = get_city_ptr (candidate->vtable->m45_Get_City_ID (candidate)); - if ((city == NULL) || (city->Body.CivID != civ_id)) - continue; - - found_city = true; - if (! city_has_other_completed_district (city, district_id, tile_x, tile_y)) + FOR_CITIES_AROUND (wai, tile_x, tile_y) { + if (! city_has_other_completed_district (wai.city, district_id, tile_x, tile_y)) return false; } @@ -8603,18 +8590,8 @@ any_nearby_city_would_lose_district_benefits (int district_id, int civ_id, int r return false; // Check all cities within work radius of the removed district - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = removed_x + dx, y = removed_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * tile = tile_at (x, y); - if ((tile == NULL) || (tile == p_null_tile)) - continue; - - City * city = get_city_ptr (tile->vtable->m45_Get_City_ID (tile)); - if (city == NULL || city->Body.CivID != civ_id) - continue; + FOR_CITIES_AROUND (wai, removed_x, removed_y) { + City * city = wai.city; // Check if this city has another completed district of the same type nearby (excluding the one being removed) if (city_has_other_completed_district (city, district_id, removed_x, removed_y)) @@ -8643,17 +8620,8 @@ remove_dependent_buildings_for_district (int district_id, int center_x, int cent (center_x >= p_bic_data->Map.Width) || (center_y >= p_bic_data->Map.Height)) return; - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = center_x + dx, y = center_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * tile = tile_at (x, y); - if ((tile == NULL) || (tile == p_null_tile)) - continue; - City * city = get_city_ptr (tile->vtable->m45_Get_City_ID (tile)); - if (city == NULL) - continue; + FOR_CITIES_AROUND (wai, center_x, center_y) { + City * city = wai.city; if (city_has_other_completed_district (city, district_id, center_x, center_y)) continue; @@ -8732,29 +8700,8 @@ handle_district_removed (Tile * tile, int district_id, int center_x, int center_ int tile_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); - // Check all tiles within city work radius to find cities that might now be able to work this tile - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = center_x + dx, y = center_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * nearby_tile = tile_at (x, y); - if ((nearby_tile == NULL) || (nearby_tile == p_null_tile)) - continue; - - int city_id = nearby_tile->vtable->m45_Get_City_ID (nearby_tile); - if (city_id < 0) - continue; - - City * city = get_city_ptr (city_id); - if (city == NULL) - continue; - - // Only recompute for cities of the same civ that owns this tile - if (city->Body.CivID != tile_owner) - continue; - - recompute_city_yields_with_districts (city); + FOR_CITIES_AROUND (wai, center_x, center_y) { + recompute_city_yields_with_districts (wai.city); } if (leave_ruins && (tile->vtable->m60_Set_Ruins != NULL)) { @@ -23947,29 +23894,13 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i { Tile * center = tile_at (tile_x, tile_y); - if ((center == NULL) || (center == p_null_tile)) - return false; - + if ((center == NULL) || (center == p_null_tile)) return false; int owner_id = center->Territory_OwnerID; - if (owner_id <= 0) - return false; - - // Loop over tiles in work radius around the center tile - for (int n = 0; n < is->workable_tile_count; n++) { - int dx, dy; - patch_ni_to_diff_for_work_area (n, &dx, &dy); - int x = tile_x + dx, y = tile_y + dy; - wrap_tile_coords (&p_bic_data->Map, &x, &y); - Tile * t = tile_at (x, y); - if ((t == NULL) || (t == p_null_tile)) - continue; + if (owner_id <= 0) return false; - // Only consider cities belonging to the same civ as the territory owner - City * city = get_city_ptr (t->vtable->m45_Get_City_ID (t)); - if ((city != NULL) && (city->Body.CivID == owner_id)) { - if (has_active_building (city, i_improv)) - return true; - } + FOR_CITIES_AROUND (wai, tile_x, tile_y) { + if (has_active_building (wai.city, i_improv)) + return true; } return false; @@ -24397,8 +24328,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par int completed_count = 0; for (int i = 0; i < info->dependent_building_count; i++) { int building_id = info->dependent_building_ids[i]; - if ((building_id >= 0) && - tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, district_id, building_id)) + if ((building_id >= 0) && tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, district_id, building_id)) completed_count++; } buildings = completed_count; From 9cf23bc082dd48a767e6614ce71599da748a884a Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 13 Dec 2025 23:47:58 -0800 Subject: [PATCH 074/356] Rename happy_bonus -> happiness_bonus --- default.districts_config.txt | 22 +++++++++++----------- injected_code.c | 1 + 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/default.districts_config.txt b/default.districts_config.txt index 91fa46d3..75516b6b 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -16,7 +16,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happiness_bonus = 0 +happiness_bonus = 0 #District name = Holy Site @@ -36,7 +36,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happiness_bonus = 0 +happiness_bonus = 0 #District name = Campus @@ -56,7 +56,7 @@ science_bonus = 2 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happiness_bonus = 0 +happiness_bonus = 0 #District name = Entertainment Complex @@ -76,7 +76,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 2 shield_bonus = 0 -happiness_bonus = 0 +happiness_bonus = 0 #District name = Commercial Hub @@ -96,7 +96,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 2 shield_bonus = 0 -happiness_bonus = 0 +happiness_bonus = 0 #District name = Industrial Zone @@ -116,7 +116,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 4 -happiness_bonus = 0 +happiness_bonus = 0 #District name = Neighborhood @@ -130,7 +130,7 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 1 shield_bonus = 0 -happiness_bonus = 0 +happiness_bonus = 0 #District name = Wonder District @@ -144,7 +144,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happiness_bonus = 0 +happiness_bonus = 0 #District name = Distribution Hub @@ -158,7 +158,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happiness_bonus = 0 +happiness_bonus = 0 #District name = Aerodrome @@ -173,7 +173,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happiness_bonus = 0 +happiness_bonus = 0 #District name = Port @@ -188,4 +188,4 @@ science_bonus = 0 food_bonus = 2 gold_bonus = 2 shield_bonus = 0 -happiness_bonus = 0 \ No newline at end of file +happiness_bonus = 0 \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index b8c44edd..56dc81fd 100644 --- a/injected_code.c +++ b/injected_code.c @@ -11162,6 +11162,7 @@ patch_init_floating_point () {"enable_natural_wonders" , false, offsetof (struct c3x_config, enable_natural_wonders)}, {"enable_distribution_hub_districts" , false, offsetof (struct c3x_config, enable_distribution_hub_districts)}, {"enable_aerodrome_districts" , false, offsetof (struct c3x_config, enable_aerodrome_districts)}, + {"enable_port_districts" , false, offsetof (struct c3x_config, enable_port_districts)}, {"completed_wonder_districts_can_be_destroyed" , false, offsetof (struct c3x_config, completed_wonder_districts_can_be_destroyed)}, {"destroyed_wonders_can_be_built_again" , false, offsetof (struct c3x_config, destroyed_wonders_can_be_built_again)}, {"cities_with_mutual_district_receive_buildings" , false, offsetof (struct c3x_config, cities_with_mutual_district_receive_buildings)}, From 4d986fd95b879571651b02c5743db9cb2c0b2c38 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 00:14:55 -0800 Subject: [PATCH 075/356] Add happiness sprite; Incorporate in district yield draw; Remove Unhappiness as possibility from districts --- Art/Districts/DistrictIncomeIcons.pcx | Bin 12422 -> 12498 bytes C3X.h | 3 +- injected_code.c | 104 ++++++++++---------------- 3 files changed, 42 insertions(+), 65 deletions(-) diff --git a/Art/Districts/DistrictIncomeIcons.pcx b/Art/Districts/DistrictIncomeIcons.pcx index 12646304281ce5ac6285aff2600159dad84102a1..e4475906d36105d237ceaec5a468a8fa3396d43d 100644 GIT binary patch delta 403 zcmYk3u}Z^G6o$!F>y3!khE$400;Lg$b|?;#MQ{=7R-d3-aE+T}brSL_CHDn9_u3|{ zTLw2LUm!z=5ORqp$t}2^@BZii&wtft+y{(a+JhW<;n#w6&LX1O-FQihUeQ?JTM^ zklPW?CB9#=%?TwYj$M!;<)NV5r;xG={kHoGcu!R;X=U5OhIR-r?4ga?5Fe zV-88L0XF0qI{Qp9c&cP5#w99uA25fZ5fJ{s(@J;O+nb delta 328 zcmXYty-LJD6or`)*NGT4k!2AV2`n42*k;*cNh63Lvv$70`UnPWB(0Ucg7^rM`viC5 zVrKm*QizqMrAF8ygbZ;e+J5K$oO812)%4zNkq_v!PRa6Xo$wbpU%4jsbIk!2R;YkK zfn!PH%nhpIzz;CP61ar#)i+|#Yrus+V*{)}!uy&cwyZgp{-Z{Ur|`MXs3U7sF%xmZ zM8q(M6qcNBgUgVKg~kO7QKc~2+MyeIx^#s%W5NpzJZtx9+dv;n?W%alwX?jLzw3;R z1hJ9wXmW`;7Jnapwuk0}@xt^*#`G=iXWTuYn~iJXdy^qM)EOn*?4D4!iKs9O2a!Ib eqf7Ymj;UqZNSY>&di(Uhr;C1%>gI0uM%FLZSh6Gl diff --git a/C3X.h b/C3X.h index 16d77a0c..efa49b77 100644 --- a/C3X.h +++ b/C3X.h @@ -1624,7 +1624,7 @@ struct district_button_image_set { int district_corruption_icons_remaining; int distribution_hub_corruption_icons_remaining; - // District UI sprites loaded from PCX files for rendering yield icons (science, commerce, shield, food, culture) + // District UI sprites loaded from PCX files for rendering yield icons (science, commerce, shield, food, culture, happiness) // Available in both regular and small sizes for different UI contexts in city interface Sprite district_science_icon; Sprite district_commerce_icon; @@ -1632,6 +1632,7 @@ struct district_button_image_set { Sprite district_corruption_icon; Sprite district_food_icon; Sprite district_food_eaten_icon; + Sprite district_happiness_icon; Sprite district_shield_icon_small; Sprite district_commerce_icon_small; Sprite district_food_icon_small; diff --git a/injected_code.c b/injected_code.c index 56dc81fd..fbce723e 100644 --- a/injected_code.c +++ b/injected_code.c @@ -213,6 +213,7 @@ bool has_active_building (City * city, int improv_id); void recompute_distribution_hub_totals (); void get_neighbor_coords (Map * map, int x, int y, int neighbor_index, int * out_x, int * out_y); void wrap_tile_coords (Map * map, int * x, int * y); +void init_district_icons (); int count_neighborhoods_in_city_radius (City * city); int count_utilized_neighborhoods_in_city_radius (City * city); @@ -2573,9 +2574,10 @@ get_effective_district_yields (struct district_instance * inst, int * out_shields, int * out_gold, int * out_science, - int * out_culture) + int * out_culture, + int * out_happiness) { - int food = 0, shields = 0, gold = 0, science = 0, culture = 0; + int food = 0, shields = 0, gold = 0, science = 0, culture = 0, happiness = 0; if (cfg != NULL && is->current_config.enable_districts) { food = cfg->food_bonus; @@ -2583,6 +2585,7 @@ get_effective_district_yields (struct district_instance * inst, gold = cfg->gold_bonus; science = cfg->science_bonus; culture = cfg->culture_bonus; + happiness = cfg->happiness_bonus; } if (inst != NULL && is->current_config.enable_natural_wonders && inst->district_type == NATURAL_WONDER_DISTRICT_ID) { @@ -2593,6 +2596,7 @@ get_effective_district_yields (struct district_instance * inst, gold += nwcfg->gold_bonus; science += nwcfg->science_bonus; culture += nwcfg->culture_bonus; + happiness += nwcfg->happiness_bonus; } } @@ -2606,6 +2610,8 @@ get_effective_district_yields (struct district_instance * inst, *out_science = science; if (out_culture != NULL) *out_culture = culture; + if (out_happiness != NULL) + *out_happiness = happiness; } int @@ -3175,8 +3181,8 @@ struct work_area_iter { int dx, dy; int tile_x, tile_y; Tile * tile; - struct district_instance * district_inst; City * city; + struct district_instance * district_inst; enum work_area_iter_output_type output_type; bool completed_districts_only; }; @@ -5207,8 +5213,6 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->shield_bonus = def->shield_bonus; if (def->has_happiness_bonus) cfg->happiness_bonus = def->happiness_bonus; - if (def->has_unhappiness_bonus) - cfg->unhappiness_bonus = def->unhappiness_bonus; if (def->has_buildable_on) cfg->buildable_square_types_mask = def->buildable_square_types_mask; @@ -5323,9 +5327,6 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.gold_bonus = def->has_gold_bonus ? def->gold_bonus : 0; new_cfg.shield_bonus = def->has_shield_bonus ? def->shield_bonus : 0; new_cfg.happiness_bonus = def->has_happiness_bonus ? def->happiness_bonus : 0; - new_cfg.unhappiness_bonus = def->has_unhappiness_bonus ? def->unhappiness_bonus : 0; - new_cfg.happiness_bonus = def->has_happiness_bonus ? def->happiness_bonus : 0; - new_cfg.unhappiness_bonus = def->has_unhappiness_bonus ? def->unhappiness_bonus : 0; new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); new_cfg.dependent_improvement_count = def->has_dependent_improvements ? def->dependent_improvement_count : 0; @@ -5578,15 +5579,6 @@ handle_district_definition_key (struct parsed_district_definition * def, } else add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); - } else if (slice_matches_str (key, "unhappiness_bonus")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->unhappiness_bonus = ival; - def->has_unhappiness_bonus = true; - } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); - } else add_unrecognized_key_error (unrecognized_keys, line_number, key); } @@ -6408,17 +6400,6 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); } - } else if (slice_matches_str (key, "unhappiness_bonus")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->unhappiness_bonus = ival; - def->has_unhappiness_bonus = true; - } else { - def->has_unhappiness_bonus = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); - } - } else add_unrecognized_key_error (unrecognized_keys, line_number, key); } @@ -7647,7 +7628,7 @@ calculate_city_center_district_bonus (City * city, int * out_food, int * out_shi struct district_config const * cfg = &is->district_configs[district_id]; int food_bonus = 0, shield_bonus = 0, gold_bonus = 0; - get_effective_district_yields (inst, cfg, &food_bonus, &shield_bonus, &gold_bonus, NULL, NULL); + get_effective_district_yields (inst, cfg, &food_bonus, &shield_bonus, &gold_bonus, NULL, NULL, NULL); bonus_food += food_bonus; bonus_shields += shield_bonus; bonus_gold += gold_bonus; @@ -8355,7 +8336,7 @@ calculate_district_culture_science_bonuses (City * city, int * culture_bonus, in struct district_config const * cfg = &is->district_configs[district_id]; int district_culture_bonus = 0; int district_science_bonus = 0; - get_effective_district_yields (inst, cfg, NULL, NULL, NULL, &district_science_bonus, &district_culture_bonus); + get_effective_district_yields (inst, cfg, NULL, NULL, NULL, &district_science_bonus, &district_culture_bonus, NULL); bool is_neighborhood = (cfg->command == UCV_Build_Neighborhood); if (is_neighborhood) { @@ -8377,18 +8358,15 @@ calculate_district_culture_science_bonuses (City * city, int * culture_bonus, in } void -calculate_district_happiness_bonus (City * city, int * happiness_bonus, int * unhappiness_bonus) +calculate_district_happiness_bonus (City * city, int * happiness_bonus) { if (happiness_bonus != NULL) *happiness_bonus = 0; - if (unhappiness_bonus != NULL) - *unhappiness_bonus = 0; if (! is->current_config.enable_districts || (city == NULL)) return; int total_happy = 0; - int total_unhappy = 0; int utilized_neighborhoods = count_utilized_neighborhoods_in_city_radius (city); FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { @@ -8399,7 +8377,7 @@ calculate_district_happiness_bonus (City * city, int * happiness_bonus, int * un int district_id = inst->district_type; struct district_config const * cfg = &is->district_configs[district_id]; - bool is_neighborhood = (cfg->command == UCV_Build_Neighborhood); + bool is_neighborhood = district_id == NEIGHBORHOOD_DISTRICT_ID; if (is_neighborhood && (utilized_neighborhoods <= 0)) continue; @@ -8408,8 +8386,6 @@ calculate_district_happiness_bonus (City * city, int * happiness_bonus, int * un if (cfg->happiness_bonus != 0) total_happy += cfg->happiness_bonus; - if (cfg->unhappiness_bonus != 0) - total_unhappy += cfg->unhappiness_bonus; if (is->current_config.enable_natural_wonders && district_id == NATURAL_WONDER_DISTRICT_ID) { struct natural_wonder_district_config const * nwcfg = @@ -8417,16 +8393,12 @@ calculate_district_happiness_bonus (City * city, int * happiness_bonus, int * un if (nwcfg != NULL) { if (nwcfg->happiness_bonus != 0) total_happy += nwcfg->happiness_bonus; - if (nwcfg->unhappiness_bonus != 0) - total_unhappy += nwcfg->unhappiness_bonus; } } } if (happiness_bonus != NULL) *happiness_bonus = total_happy; - if (unhappiness_bonus != NULL) - *unhappiness_bonus = total_unhappy; } int __fastcall @@ -13507,8 +13479,6 @@ patch_City_Form_open (City_Form * this, int edx, City * city, int param_2) } } -void init_district_icons (); - void __fastcall patch_City_Form_draw (City_Form * this) { @@ -13624,7 +13594,7 @@ patch_City_Form_draw (City_Form * this) struct district_config const * cfg = &is->district_configs[district_id]; int gold_bonus = 0; - get_effective_district_yields (inst, cfg, NULL, NULL, &gold_bonus, NULL, NULL); + get_effective_district_yields (inst, cfg, NULL, NULL, &gold_bonus, NULL, NULL, NULL); district_gold += gold_bonus; } @@ -22596,6 +22566,10 @@ init_district_icons () Sprite_construct (&is->district_food_eaten_icon); Sprite_slice_pcx (&is->district_food_eaten_icon, __, &pcx, 1 + 7*31, 1, 30, 30, 1, 1); + // Extract happiness icon (index 13: x = 1 + 13*31 = 404, width 30) + Sprite_construct (&is->district_happiness_icon); + Sprite_slice_pcx (&is->district_happiness_icon, __, &pcx, 1 + 13*31, 1, 30, 30, 1, 1); + // Extract small shield icon (index 13: x = 1 + 13*31 = 404, width 30) Sprite_construct (&is->district_shield_icon_small); Sprite_slice_pcx (&is->district_shield_icon_small, __, &pcx, 1 + 13*31, 1, 30, 30, 1, 1); @@ -22638,25 +22612,27 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s struct district_instance * inst = get_district_instance (tile); // Count total yields from bonuses - int food_bonus = 0, shield_bonus = 0, gold_bonus = 0, science_bonus = 0, culture_bonus = 0; - get_effective_district_yields (inst, config, &food_bonus, &shield_bonus, &gold_bonus, &science_bonus, &culture_bonus); + int food_bonus = 0, shield_bonus = 0, gold_bonus = 0, science_bonus = 0, culture_bonus = 0, happiness_bonus = 0; + get_effective_district_yields (inst, config, &food_bonus, &shield_bonus, &gold_bonus, &science_bonus, &culture_bonus, &happiness_bonus); int total_yield = 0; - if (food_bonus > 0) total_yield += food_bonus; - if (shield_bonus > 0) total_yield += shield_bonus; - if (gold_bonus > 0) total_yield += gold_bonus; - if (science_bonus > 0) total_yield += science_bonus; - if (culture_bonus > 0) total_yield += culture_bonus; + if (food_bonus > 0) total_yield += food_bonus; + if (shield_bonus > 0) total_yield += shield_bonus; + if (gold_bonus > 0) total_yield += gold_bonus; + if (science_bonus > 0) total_yield += science_bonus; + if (culture_bonus > 0) total_yield += culture_bonus; + if (happiness_bonus > 0) total_yield += happiness_bonus; if (total_yield <= 0) return; // Get sprites - Sprite * food_sprite = &is->district_food_icon_small; - Sprite * shield_sprite = &is->district_shield_icon_small; - Sprite * commerce_sprite = &is->district_commerce_icon_small; - Sprite * science_sprite = &is->district_science_icon_small; - Sprite * culture_sprite = &is->district_culture_icon_small; + Sprite * food_sprite = &is->district_food_icon_small; + Sprite * shield_sprite = &is->district_shield_icon_small; + Sprite * commerce_sprite = &is->district_commerce_icon_small; + Sprite * science_sprite = &is->district_science_icon_small; + Sprite * culture_sprite = &is->district_culture_icon_small; + Sprite * happiness_sprite = &is->district_happiness_icon; // Determine sprite dimensions int sprite_width = food_sprite->Width3; @@ -22712,6 +22688,11 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s Sprite_draw (culture_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); pixel_x += spacing; } + + for (int i = 0; i < happiness_bonus; i++) { + Sprite_draw (happiness_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); + pixel_x += spacing; + } } void @@ -23217,16 +23198,11 @@ patch_City_add_happiness_from_buildings (City * this, int edx, int * inout_happi City_add_happiness_from_buildings (this, __, inout_happiness, inout_unhappiness); if (is->current_config.enable_districts) { - int district_happy = 0, district_unhappy = 0; - calculate_district_happiness_bonus (this, &district_happy, &district_unhappy); + int district_happy = 0; + calculate_district_happiness_bonus (this, &district_happy); if (district_happy != 0) *inout_happiness += district_happy; - - if (district_unhappy != 0) { - *inout_happiness -= district_unhappy; - this->Body.UnhappyThisCityImprovementsPercent += (char)district_unhappy; - } } if (restore_improv_counts) { @@ -24736,7 +24712,7 @@ patch_City_Form_draw_food_income_icons (City_Form * this) FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { int district_id = wai.district_inst->district_type; int food_bonus = 0; - get_effective_district_yields (wai.district_inst, &is->district_configs[district_id], &food_bonus, NULL, NULL, NULL, NULL); + get_effective_district_yields (wai.district_inst, &is->district_configs[district_id], &food_bonus, NULL, NULL, NULL, NULL, NULL); standard_district_food += food_bonus; } From 4fe4b77634040edcffeccb78f89ab8973a477aca Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 13:57:19 -0800 Subject: [PATCH 076/356] Add support for resource_prereq and resource_prereq_on_tile; Move Colossus art to Wonder_2.pcx; Add +1 happiness_bonus to all natural wonders --- Art/Districts/1200/Wonders_2.PCX | Bin 30972 -> 32296 bytes C3X.h | 20 +- default.districts_natural_wonders_config.txt | 262 +++++++++---------- injected_code.c | 144 +++++++++- 4 files changed, 285 insertions(+), 141 deletions(-) diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index fe939a34b404f3ddda2c8734ad51b2af0e5a16f2..742c89b6ad746b4d2906953f631e8c0657d5b966 100644 GIT binary patch delta 2644 zcmY*bJ#QP=5=DY+LjfsW1ac8KqS7*?l@w1%9HnMuPtzUMORkDQxG{HT$;(7px!V`3tA4`dDA8`$K>U2~-f z>wW0E=Ax{)W}JLiKJo{C31rBtLe*?4O|=rt%g|{JxFl3dr?OOCl@B08ahu{RoIs~@}08W@D(=^dCnEFl`DCYUpy)Er0_`y9P?zEJ^6(s?~1Z3a)knR zk#ULwX;?^+*qJr4ifjqL4Ur>;wI&&xBvqi{nhZJML!=yY6&*T-Ecr9|ALoRRLf{SO z2-pny*#hdVN`TUjkkR6rpd;6(0c}#XDmD#;--Os97`Jgu70AFsNHtrNK40)VB<}IL zP>>=oOgth_e_8XoAHuufReuNgkxnNsMZvmUfQK^#ZE;0V>yS4bvfq_TRlW<@P<&!p(b^+)JNW+0`DektO0^)0AD{#U1~bi+K>)N8~|JA z*Frs9Xpp0ZqBm=@q6P6P1Yd$mnDjTmR4h?-K&xSZ3;Gn=ZP*H2c*%$0J!x|b`9+Mzfj*!` z-DJVd+f=WJ+Ca9(?!lqXt=n$CkOWmN@(-lN2S7c!q~LNC!x9 z=~)UVJ${m1TxrW5?;vDd2K*MqXI+(W(7hC>>QJ35lru+-U1(hg1sH_Tfimk3RSP)M za}usXL2qRT0u$Mg9-oRbU0*?rT?~evfAaPrVvSz#zSbuuA0t8CqA1rl)v0XpYv5aQ z#(x9PszvHK;Gdc;C{Y6)O;WRhwB8~<=>09PAx)7zc?Sub1OcP@2kS>#v_ z`2yPriUhqqC-Wx)hwpHU8au!Q(y%OlW>6pnTAj5ES?3ziQ`zM{GF5iTCSQ?@Ve>oS zUPC+Ffd!AN$hViWsHrbC%z((bB~1>*&4d>|m!nsDO3%R+Ftm?0FvIjJHy5hM%>?p- zeSm#w@TDj$5kEscn_TSH%^nEQP#u#?oA9_c^)jflsxHOH?1&8IC1y-v7 zgC;am<9}f3%0o1zuEpaLpc`4^_foLPPpMo<)Br_jua7}jCCTA9-zvP95=KnqBi8V( zXp-D&N&dRRA=+8uEBUJ=ZdDU|D@D9EB4UZl_gDq^W(!}g-N1%hpgX>S4POhNb?#w@ z31K`oYqwC6{QpPiOHy#VRMoMc3c-?4y46+rUH9`)%C-CsdeEn(uq$*mpZc`5cr#hx zwaam%m#|hPtI70i-RUPX7nPfF5Ir1>c^tr6701n}Mg7fB7p4_J9)kCShr6 z%iXb_yRvMp!r4dRqrrjx>(u!E4?LP@VT49g$iV>Q(;Q1ECdJMu%6im}P>zKV9n zBiWJYSKm#>s2KgBgC0jSe}V>|TggbmOwSrq>68D>yw%|oNlzKGX)_T?TF=Qlf$T?X z=2)B=!^%dC$V9}LJRUEYW0?$Hk*sCRILBaTwBy?H*o@Gb88sr)3$D=~@3pqL#=>;O zh?;a3F(0=vm3lfYy<89R!X?t?d^)}`ZKmmTYUyyiR}W=ricZt!^g=v6Z>AG8I0VU~ zt-deO*theu(b$w>K1xQv*~hOPz3)F@2YwSxJ~EA|Saf#&+gNlT`$O*${fg{hPbZ$t zOvGl>ma$+&Tw_-MGI@ALc4gKzB5jN7F73CJKNsWvwXPC%&Z^Cm;Re4 z&&e}oAWaeV=g1TCSlN_@x^pl`PS1@)vLO$ud3=!r@{@d!x@r+0WS{Jjb*cGkl<-c* zWS6W-RkeaQG9o)FjG^&;mAstl zXCD`&h3@O`##3zoFu)mU0S4+b<|L~M9FH)IUtjj$v+;=jXvpL=Cg#r6lj}`>qmg&@ zG5)a0%}DBC`Jb*QPs-nPY5f(9kC*j$@<=Anaq{fjL`~ioZrI#}71Nb;tC@7l7Bqbr IZLduK0B@wWMF0Q* diff --git a/C3X.h b/C3X.h index efa49b77..34149736 100644 --- a/C3X.h +++ b/C3X.h @@ -563,6 +563,8 @@ struct district_config { char const * name; char const * tooltip; char const * advance_prereq; + char const * resource_prereq; + char const * resource_prereq_on_tile; char const * dependent_improvements[MAX_DISTRICT_DEPENDENTS]; char const * img_paths[10]; unsigned short buildable_square_types_mask; @@ -647,7 +649,7 @@ struct wonder_location { const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYPES] = { { .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", - .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereq = NULL, .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 5, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, @@ -656,7 +658,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_WonderDistrict, .name = "Wonder District", .tooltip = "Build Wonder District", - .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereq = NULL, .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"WonderDistrict.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, @@ -665,7 +667,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_DistributionHub, .name = "Distribution Hub", .tooltip = "Build Distribution Hub", - .advance_prereq = "Construction", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereq = "Construction", .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"DistributionHub.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 2, .btn_tile_sheet_row = 0, @@ -674,7 +676,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_Aerodrome, .name = "Aerodrome", .tooltip = "Build Aerodrome", - .advance_prereq = "Flight", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 1, + .advance_prereq = "Flight", .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 1, .img_paths = {"Aerodrome.pcx"}, .dependent_improvements = {"Airport"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 1, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, @@ -682,7 +684,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = -1, .name = "Natural Wonder", .tooltip = NULL, - .advance_prereq = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereq = NULL, .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {0}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 0, .max_building_index = 0, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, @@ -690,7 +692,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", - .advance_prereq = "Map Making", .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, + .advance_prereq = "Map Making", .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .dependent_improvements = {"Harbor", "Commercial Dock"}, .buildable_square_types_mask = (1 << SQ_Coast), .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, @@ -702,6 +704,8 @@ struct parsed_district_definition { char * name; char * tooltip; char * advance_prereq; + char * resource_prereq; + char * resource_prereq_on_tile; char * dependent_improvements[5]; char * img_paths[5]; int dependent_improvement_count; @@ -739,6 +743,8 @@ struct parsed_district_definition { bool has_happiness_bonus; bool has_unhappiness_bonus; bool has_buildable_on; + bool has_resource_prereq; + bool has_resource_prereq_on_tile; }; struct parsed_wonder_definition { @@ -1589,6 +1595,8 @@ struct district_button_image_set { struct district_infos { int advance_prereq_id; // Tech ID that enables the district + int resource_prereq_id; + int resource_prereq_on_tile_id; int dependent_building_count; int dependent_building_ids[MAX_DISTRICT_DEPENDENTS]; // Building types the district enables } district_infos[COUNT_DISTRICT_TYPES]; diff --git a/default.districts_natural_wonders_config.txt b/default.districts_natural_wonders_config.txt index 0b36359a..8db672d3 100644 --- a/default.districts_natural_wonders_config.txt +++ b/default.districts_natural_wonders_config.txt @@ -1,152 +1,152 @@ #Wonder -name = Angel Falls -terrain_type = grassland -adjacent_to = river -adjacency_dir = southeast -img_path = NaturalWonders.pcx -img_row = 0 -img_column = 0 -culture_bonus = 2 -science_bonus = 1 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Angel Falls +terrain_type = grassland +adjacent_to = river +adjacency_dir = southeast +img_path = NaturalWonders.pcx +img_row = 0 +img_column = 0 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 1 #Wonder -name = Yosemite -terrain_type = grassland -adjacent_to = forest -img_path = NaturalWonders.pcx -img_row = 0 -img_column = 1 -culture_bonus = 2 -science_bonus = 1 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Yosemite +terrain_type = grassland +adjacent_to = forest +img_path = NaturalWonders.pcx +img_row = 0 +img_column = 1 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 1 #Wonder -name = Mount Fuji -terrain_type = grassland -img_path = NaturalWonders.pcx -img_row = 0 -img_column = 2 -culture_bonus = 2 -science_bonus = 1 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Mount Fuji +terrain_type = grassland +img_path = NaturalWonders.pcx +img_row = 0 +img_column = 2 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 1 #Wonder -name = Yellowstone -terrain_type = grassland -adjacent_to = forest -img_path = NaturalWonders.pcx -img_row = 0 -img_column = 3 -culture_bonus = 2 -science_bonus = 1 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Yellowstone +terrain_type = grassland +adjacent_to = forest +img_path = NaturalWonders.pcx +img_row = 0 +img_column = 3 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 1 #Wonder -name = Mount Everest -terrain_type = grassland -adjacent_to = mountains -img_path = NaturalWonders.pcx -img_row = 1 -img_column = 0 -culture_bonus = 2 -science_bonus = 1 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Mount Everest +terrain_type = grassland +adjacent_to = mountains +img_path = NaturalWonders.pcx +img_row = 1 +img_column = 0 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 1 #Wonder -name = Zhangjiajie Mountains -terrain_type = jungle -adjacent_to = jungle -img_path = NaturalWonders.pcx -img_row = 1 -img_column = 1 -culture_bonus = 2 -science_bonus = 1 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Zhangjiajie Mountains +terrain_type = jungle +adjacent_to = jungle +img_path = NaturalWonders.pcx +img_row = 1 +img_column = 1 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 1 #Wonder -name = Mount Kilimanjaro -terrain_type = grassland -img_path = NaturalWonders.pcx -img_row = 1 -img_column = 2 -culture_bonus = 2 -science_bonus = 1 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Mount Kilimanjaro +terrain_type = grassland +img_path = NaturalWonders.pcx +img_row = 1 +img_column = 2 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 1 #Wonder -name = Great Barrier Reef -terrain_type = sea -adjacent_to = coast -img_path = NaturalWonders.pcx -img_row = 1 -img_column = 3 -culture_bonus = 2 -science_bonus = 1 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Great Barrier Reef +terrain_type = sea +adjacent_to = coast +img_path = NaturalWonders.pcx +img_row = 1 +img_column = 3 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 1 #Wonder -name = Matterhorn -terrain_type = grassland -adjacent_to = mountains -img_path = NaturalWonders.pcx -img_row = 2 -img_column = 0 -culture_bonus = 2 -science_bonus = 1 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Matterhorn +terrain_type = grassland +adjacent_to = mountains +img_path = NaturalWonders.pcx +img_row = 2 +img_column = 0 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 1 #Wonder -name = Moraine Lake -terrain_type = grassland -adjacent_to = mountains -img_path = NaturalWonders.pcx -img_row = 2 -img_column = 1 -culture_bonus = 2 -science_bonus = 1 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Moraine Lake +terrain_type = grassland +adjacent_to = mountains +img_path = NaturalWonders.pcx +img_row = 2 +img_column = 1 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 1 #Wonder -name = Tropical Rainforest -terrain_type = jungle -adjacent_to = jungle -img_path = NaturalWonders.pcx -img_row = 2 -img_column = 2 -culture_bonus = 2 -science_bonus = 1 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 \ No newline at end of file +name = Tropical Rainforest +terrain_type = jungle +adjacent_to = jungle +img_path = NaturalWonders.pcx +img_row = 2 +img_column = 2 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 1 \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index fbce723e..7f91a8b2 100644 --- a/injected_code.c +++ b/injected_code.c @@ -199,6 +199,7 @@ void __fastcall patch_City_recompute_yields_and_happiness (City * this); void __fastcall patch_Map_build_trade_network (Map * this); bool __fastcall patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value); bool __fastcall patch_Unit_can_pillage (Unit * this, int edx, int tile_x, int tile_y); +bool __fastcall patch_City_has_resource (City * this, int edx, int resource_id); Tile * find_tile_for_district (City * city, int district_id, int * out_x, int * out_y); struct district_instance * get_district_instance (Tile * tile); bool city_has_required_district (City * city, int district_id); @@ -4742,6 +4743,14 @@ free_dynamic_district_config (struct district_config * cfg) free ((void *)cfg->advance_prereq); cfg->advance_prereq = NULL; } + if (cfg->resource_prereq != NULL) { + free ((void *)cfg->resource_prereq); + cfg->resource_prereq = NULL; + } + if (cfg->resource_prereq_on_tile != NULL) { + free ((void *)cfg->resource_prereq_on_tile); + cfg->resource_prereq_on_tile = NULL; + } for (int i = 0; i < 5; i++) { if (cfg->dependent_improvements[i] != NULL) { @@ -4829,6 +4838,14 @@ free_special_district_override_strings (struct district_config * cfg, struct dis free ((void *)cfg->advance_prereq); cfg->advance_prereq = NULL; } + if ((cfg->resource_prereq != NULL) && (cfg->resource_prereq != defaults->resource_prereq)) { + free ((void *)cfg->resource_prereq); + cfg->resource_prereq = NULL; + } + if ((cfg->resource_prereq_on_tile != NULL) && (cfg->resource_prereq_on_tile != defaults->resource_prereq_on_tile)) { + free ((void *)cfg->resource_prereq_on_tile); + cfg->resource_prereq_on_tile = NULL; + } for (int i = 0; i < ARRAY_LEN (cfg->dependent_improvements); i++) { char const * default_value = (i < defaults->dependent_improvement_count) ? defaults->dependent_improvements[i] : NULL; @@ -4938,6 +4955,14 @@ free_parsed_district_definition (struct parsed_district_definition * def) free (def->advance_prereq); def->advance_prereq = NULL; } + if (def->resource_prereq != NULL) { + free (def->resource_prereq); + def->resource_prereq = NULL; + } + if (def->resource_prereq_on_tile != NULL) { + free (def->resource_prereq_on_tile); + def->resource_prereq_on_tile = NULL; + } for (int i = 0; i < def->dependent_improvement_count; i++) { if (def->dependent_improvements[i] != NULL) { @@ -5188,6 +5213,18 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->advance_prereq = def->advance_prereq; def->advance_prereq = NULL; } + if (def->has_resource_prereq) { + if ((cfg->resource_prereq != NULL) && (cfg->resource_prereq != defaults->resource_prereq)) + free ((void *)cfg->resource_prereq); + cfg->resource_prereq = def->resource_prereq; + def->resource_prereq = NULL; + } + if (def->has_resource_prereq_on_tile) { + if ((cfg->resource_prereq_on_tile != NULL) && (cfg->resource_prereq_on_tile != defaults->resource_prereq_on_tile)) + free ((void *)cfg->resource_prereq_on_tile); + cfg->resource_prereq_on_tile = def->resource_prereq_on_tile; + def->resource_prereq_on_tile = NULL; + } if (def->has_allow_multiple) cfg->allow_multiple = def->allow_multiple; @@ -5314,6 +5351,14 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.advance_prereq = def->advance_prereq; def->advance_prereq = NULL; } + if (def->has_resource_prereq) { + new_cfg.resource_prereq = def->resource_prereq; + def->resource_prereq = NULL; + } + if (def->has_resource_prereq_on_tile) { + new_cfg.resource_prereq_on_tile = def->resource_prereq_on_tile; + def->resource_prereq_on_tile = NULL; + } new_cfg.allow_multiple = def->has_allow_multiple ? def->allow_multiple : false; new_cfg.vary_img_by_era = def->has_vary_img_by_era ? def->vary_img_by_era : false; @@ -5428,6 +5473,22 @@ handle_district_definition_key (struct parsed_district_definition * def, def->advance_prereq = copy_trimmed_string_or_null (value, 1); def->has_advance_prereq = true; + } else if (slice_matches_str (key, "resource_prereq")) { + if (def->resource_prereq != NULL) { + free (def->resource_prereq); + def->resource_prereq = NULL; + } + def->resource_prereq = copy_trimmed_string_or_null (value, 1); + def->has_resource_prereq = true; + + } else if (slice_matches_str (key, "resource_prereq_on_tile")) { + if (def->resource_prereq_on_tile != NULL) { + free (def->resource_prereq_on_tile); + def->resource_prereq_on_tile = NULL; + } + def->resource_prereq_on_tile = copy_trimmed_string_or_null (value, 1); + def->has_resource_prereq_on_tile = true; + } else if (slice_matches_str (key, "img_paths")) { char * value_text = trim_and_extract_slice (value, 0); int list_count = 0; @@ -6612,6 +6673,8 @@ void parse_building_and_tech_ids () if (is->district_configs[i].command != 0) itable_insert (&is->command_id_to_district_id, is->district_configs[i].command, i); is->district_infos[i].advance_prereq_id = -1; + is->district_infos[i].resource_prereq_id = -1; + is->district_infos[i].resource_prereq_on_tile_id = -1; // Map advance prereqs to districts if (is->district_configs[i].advance_prereq != NULL && is->district_configs[i].advance_prereq != "") { @@ -6626,6 +6689,28 @@ void parse_building_and_tech_ids () is->district_infos[i].advance_prereq_id = -1; } } + if (is->district_configs[i].resource_prereq != NULL && is->district_configs[i].resource_prereq != "") { + int res_id; + struct string_slice res_name = { .str = (char *)is->district_configs[i].resource_prereq, .len = (int)strlen (is->district_configs[i].resource_prereq) }; + if (find_game_object_id_by_name (GOK_RESOURCE, &res_name, 0, &res_id)) { + snprintf (ss, sizeof ss, "Found resource prereq \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, is->district_configs[i].resource_prereq, res_id); + (*p_OutputDebugStringA) (ss); + is->district_infos[i].resource_prereq_id = res_id; + } else { + is->district_infos[i].resource_prereq_id = -1; + } + } + if (is->district_configs[i].resource_prereq_on_tile != NULL && is->district_configs[i].resource_prereq_on_tile != "") { + int res_id; + struct string_slice res_name = { .str = (char *)is->district_configs[i].resource_prereq_on_tile, .len = (int)strlen (is->district_configs[i].resource_prereq_on_tile) }; + if (find_game_object_id_by_name (GOK_RESOURCE, &res_name, 0, &res_id)) { + snprintf (ss, sizeof ss, "Found on-tile resource prereq \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, is->district_configs[i].resource_prereq_on_tile, res_id); + (*p_OutputDebugStringA) (ss); + is->district_infos[i].resource_prereq_on_tile_id = res_id; + } else { + is->district_infos[i].resource_prereq_on_tile_id = -1; + } + } // Map improvement prereqs to districts int stored_count = 0; @@ -7472,6 +7557,8 @@ reset_district_state (bool reset_tile_map) for (int i = 0; i < COUNT_DISTRICT_TYPES; i++) { is->district_infos[i].advance_prereq_id = -1; + is->district_infos[i].resource_prereq_id = -1; + is->district_infos[i].resource_prereq_on_tile_id = -1; is->district_infos[i].dependent_building_count = 0; for (int j = 0; j < ARRAY_LEN (is->district_infos[i].dependent_building_ids); j++) is->district_infos[i].dependent_building_ids[j] = -1; @@ -7512,6 +7599,43 @@ clear_city_district_request (City * city, int district_id) } } +bool +district_resource_prereqs_met (Tile * tile, int tile_x, int tile_y, int district_id, City * city) +{ + if ((tile == NULL) || (tile == p_null_tile) || + (district_id < 0) || (district_id >= is->district_count)) + return false; + + struct district_infos * info = &is->district_infos[district_id]; + int on_tile_req = info->resource_prereq_on_tile_id; + if (on_tile_req >= 0) { + int res_here = tile->vtable->m39_Get_Resource_Type (tile); + if (res_here != on_tile_req) + return false; + } + + int resource_req = info->resource_prereq_id; + if (resource_req < 0) + return true; + + int owner = tile->vtable->m38_Get_Territory_OwnerID (tile); + if (owner < 0) + return false; + + if ((city != NULL) && + (city->Body.CivID == owner) && + city_radius_contains_tile (city, tile_x, tile_y) && + patch_City_has_resource (city, __, resource_req)) + return true; + + FOR_CITIES_AROUND (wai, tile_x, tile_y) { + if (patch_City_has_resource (wai.city, __, resource_req)) + return true; + } + + return false; +} + bool can_build_district_on_tile (Tile * tile, int district_id) { @@ -7537,10 +7661,16 @@ can_build_district_on_tile (Tile * tile, int district_id) if (! district_is_buildable_on_square_type (cfg, base_type)) return false; + int tile_x = 0, tile_y = 0; + tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y); + int prereq_id = is->district_infos[district_id].advance_prereq_id; if ((prereq_id >= 0) && !Leader_has_tech (&leaders[tile->Territory_OwnerID], __, prereq_id)) return false; + if (! district_resource_prereqs_met (tile, tile_x, tile_y, district_id, NULL)) + return false; + struct district_instance * existing_inst = get_district_instance (tile); int existing_district_id = (existing_inst != NULL) ? existing_inst->district_type : -1; bool district_completed = district_is_complete (tile, existing_district_id); @@ -7551,9 +7681,7 @@ can_build_district_on_tile (Tile * tile, int district_id) return false; if (! cfg->allow_multiple) { - int x, y; - tile_coords_from_ptr (&p_bic_data->Map, tile, &x, &y); - FOR_DISTRICTS_AROUND (wai, x, y, false) { + FOR_DISTRICTS_AROUND (wai, tile_x, tile_y, false) { if (wai.district_inst->district_type == district_id) return false; } @@ -7576,6 +7704,11 @@ tile_suitable_for_district (Tile * tile, int district_id, City * city, bool * ou if (tile->vtable->m38_Get_Territory_OwnerID (tile) != city->Body.CivID) return false; if (! can_build_district_on_tile (tile, district_id)) return false; + int tile_x = 0, tile_y = 0; + tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y); + if (! district_resource_prereqs_met (tile, tile_x, tile_y, district_id, city)) + return false; + struct district_instance * inst = get_district_instance (tile); if (inst != NULL) { if (inst->district_type != district_id) @@ -14497,11 +14630,14 @@ patch_Leader_can_do_worker_job (Leader * this, int edx, enum Worker_Jobs job, in ! tile->vtable->m35_Check_Is_Water (tile) && (tile->CityID < 0) && ! tile->vtable->m18_Check_Mines (tile, __, 0)) { + int tile_x = 0, tile_y = 0; + tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y); int owner_civ = tile->vtable->m38_Get_Territory_OwnerID (tile); if ((owner_civ == this->ID) || (owner_civ == 0)) { // Check if the leader has the tech prereq for this district int prereq_id = is->district_infos[inst->district_type].advance_prereq_id; - if (prereq_id < 0 || Leader_has_tech (this, __, prereq_id)) + if ((prereq_id < 0 || Leader_has_tech (this, __, prereq_id)) && + district_resource_prereqs_met (tile, tile_x, tile_y, inst->district_type, NULL)) tr = 1; } } From c512908172bd95484074c9ed4f180dd0843ec3be Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 14:26:20 -0800 Subject: [PATCH 077/356] Add support for buildable_on property for wonders, AI and functional-awareness & constraints for wonders only buildable on certain tile types --- C3X.h | 5 +- injected_code.c | 174 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 166 insertions(+), 13 deletions(-) diff --git a/C3X.h b/C3X.h index 34149736..49b86ec3 100644 --- a/C3X.h +++ b/C3X.h @@ -602,6 +602,7 @@ struct wonder_district_config { maritime_alt_img_column, maritime_alt_img_construct_row, maritime_alt_img_construct_column; + unsigned short buildable_square_types_mask; bool is_dynamic; bool is_maritime; }; @@ -660,7 +661,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_WonderDistrict, .name = "Wonder District", .tooltip = "Build Wonder District", .advance_prereq = NULL, .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"WonderDistrict.pcx"}, - .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, + .buildable_square_types_mask = (unsigned short)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Coast)), .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 0 @@ -754,12 +755,14 @@ struct parsed_wonder_definition { int img_column; int img_construct_row; int img_construct_column; + unsigned short buildable_square_types_mask; bool has_name; bool has_img_path; bool has_img_row; bool has_img_column; bool has_img_construct_row; bool has_img_construct_column; + bool has_buildable_on; }; struct parsed_natural_wonder_definition { diff --git a/injected_code.c b/injected_code.c index 7f91a8b2..48596020 100644 --- a/injected_code.c +++ b/injected_code.c @@ -3866,6 +3866,46 @@ find_wonder_config_index_by_improvement_id (int improv_id) void set_wonder_built_flag (int improv_id, bool is_built); +unsigned short +wonder_buildable_square_type_mask (struct wonder_district_config const * cfg) +{ + if (cfg == NULL) + return district_default_buildable_mask (); + + unsigned short mask = cfg->buildable_square_types_mask; + if (mask == 0) + mask = district_default_buildable_mask (); + return mask; +} + +unsigned short +wonder_buildable_mask_for_improvement (int improv_id) +{ + int windex = find_wonder_config_index_by_improvement_id (improv_id); + if (windex < 0) + return district_default_buildable_mask (); + return wonder_buildable_square_type_mask (&is->wonder_district_configs[windex]); +} + +bool +wonder_is_buildable_on_square_type (struct wonder_district_config const * cfg, enum SquareTypes base_type) +{ + unsigned short mask = wonder_buildable_square_type_mask (cfg); + unsigned short bit = square_type_mask_bit (base_type); + return (bit != 0) && ((mask & bit) != 0); +} + +bool +wonder_is_buildable_on_tile (Tile * tile, int wonder_improv_id) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return false; + + unsigned short mask = wonder_buildable_mask_for_improvement (wonder_improv_id); + unsigned short bit = square_type_mask_bit (tile->vtable->m50_Get_Square_BaseType (tile)); + return (bit != 0) && ((mask & bit) != 0); +} + int get_wonder_improvement_id_from_index (int windex) { @@ -5803,6 +5843,7 @@ void init_parsed_wonder_definition (struct parsed_wonder_definition * def) { memset (def, 0, sizeof *def); + def->buildable_square_types_mask = district_default_buildable_mask (); } void @@ -5847,6 +5888,7 @@ add_dynamic_wonder_from_definition (struct parsed_wonder_definition * def, int s new_cfg.img_column = def->img_column; new_cfg.img_construct_row = def->img_construct_row; new_cfg.img_construct_column = def->img_construct_column; + new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); if (existing_index >= 0) { struct wonder_district_config * cfg = &is->wonder_district_configs[existing_index]; @@ -6009,6 +6051,15 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); } + } else if (slice_matches_str (key, "buildable_on")) { + unsigned short mask; + if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number)) { + def->buildable_square_types_mask = mask; + def->has_buildable_on = true; + } else { + def->has_buildable_on = false; + } + } else add_unrecognized_key_error (unrecognized_keys, line_number, key); } @@ -7961,6 +8012,81 @@ find_tile_for_port_district (City * city, int * out_x, int * out_y) return NULL; } +Tile * +find_tile_for_wonder_district (City * city, int * out_x, int * out_y) +{ + if (city == NULL) + return NULL; + + int target_improv_id = -1; + if (lookup_pending_building_order (city, &target_improv_id)) { + if ((target_improv_id < 0) || + (target_improv_id >= p_bic_data->ImprovementsCount) || + ((p_bic_data->Improvements[target_improv_id].Characteristics & (ITC_Wonder | ITC_Small_Wonder)) == 0)) + target_improv_id = -1; + } + if (target_improv_id < 0) { + int order_id = city->Body.Order_ID; + if ((city->Body.Order_Type == COT_Improvement) && + (order_id >= 0) && + (order_id < p_bic_data->ImprovementsCount)) { + Improvement * order = &p_bic_data->Improvements[order_id]; + if (order->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) + target_improv_id = order_id; + } + } + + int city_x = city->Body.X; + int city_y = city->Body.Y; + int city_work_radius = is->current_config.city_work_radius; + + // Search in order: ring 2, then rings 3..N, then ring 1 as last resort + int ring_order[8]; + int ring_count = 0; + ring_order[ring_count++] = 2; + for (int r = 3; r <= city_work_radius; r++) + ring_order[ring_count++] = r; + ring_order[ring_count++] = 1; + + for (int r_idx = 0; r_idx < ring_count; r_idx++) { + int ring = ring_order[r_idx]; + int start_ni = workable_tile_counts[ring - 1]; + int end_ni = workable_tile_counts[ring]; + + Tile * best_tile = NULL; + int best_yield = INT_MAX; + + for (int ni = start_ni; ni < end_ni; ni++) { + int dx, dy; + patch_ni_to_diff_for_work_area (ni, &dx, &dy); + int tx = (city_x + dx) & 0xFF; + int ty = (city_y + dy) & 0xFF; + Tile * tile = tile_at (tx, ty); + + if ((tile == NULL) || (tile == p_null_tile)) + continue; + + if (! tile_suitable_for_district (tile, WONDER_DISTRICT_ID, city, NULL)) + continue; + if (! wonder_is_buildable_on_tile (tile, target_improv_id)) + continue; + + int yield = compute_city_tile_yield_sum (city, tx, ty); + if (yield < best_yield && ! is_tile_earmarked_for_district (tx, ty)) { + best_yield = yield; + best_tile = tile; + *out_x = tx; + *out_y = ty; + } + } + + if (best_tile != NULL) + return best_tile; + } + + return NULL; +} + Tile * find_tile_for_distribution_hub_district (City * city, int * out_x, int * out_y) { @@ -8091,6 +8217,8 @@ find_tile_for_district (City * city, int district_id, int * out_x, int * out_y) return find_tile_for_distribution_hub_district (city, out_x, out_y); if (district_id == PORT_DISTRICT_ID) return find_tile_for_port_district (city, out_x, out_y); + if (district_id == WONDER_DISTRICT_ID) + return find_tile_for_wonder_district (city, out_x, out_y); int city_x = city->Body.X; int city_y = city->Body.Y; @@ -8250,7 +8378,7 @@ city_has_required_district (City * city, int district_id) } bool -city_has_wonder_district_with_no_completed_wonder (City * city) +city_has_wonder_district_with_no_completed_wonder (City * city, int wonder_improv_id) { if (! is->current_config.enable_wonder_districts || (city == NULL)) return false; @@ -8261,8 +8389,19 @@ city_has_wonder_district_with_no_completed_wonder (City * city) struct district_instance * inst = wai.district_inst; if (inst->district_type != WONDER_DISTRICT_ID) continue; - // Get wonder district info struct wonder_district_info * info = get_wonder_district_info (candidate); + if ((wonder_improv_id >= 0) && ! wonder_is_buildable_on_tile (candidate, wonder_improv_id)) { + if ((info != NULL) && + (info->state == WDS_UNDER_CONSTRUCTION) && + (info->city_id == city->Body.ID)) { + info->state = WDS_UNUSED; + info->city = NULL; + info->city_id = -1; + info->wonder_index = -1; + } + continue; + } + // Get wonder district info if (info == NULL) return true; if (info->state == WDS_COMPLETED) continue; if (info->state == WDS_UNUSED) return true; // Unreserved and available @@ -8836,7 +8975,7 @@ city_requires_district_for_improvement (City * city, int improv_id, int * out_di return false; if (is->current_config.enable_wonder_districts) { if (district_id == WONDER_DISTRICT_ID) { - if (city_has_wonder_district_with_no_completed_wonder (city)) + if (city_has_wonder_district_with_no_completed_wonder (city, improv_id)) return false; if (out_district_id != NULL) *out_district_id = district_id; @@ -8969,7 +9108,7 @@ city_needs_wonder_district (City * city) } bool -city_has_assigned_wonder_district (City * city, Tile * ignore_tile) +city_has_assigned_wonder_district (City * city, Tile * ignore_tile, int wonder_improv_id) { if (! is->current_config.enable_wonder_districts || (city == NULL)) return false; @@ -8989,6 +9128,13 @@ city_has_assigned_wonder_district (City * city, Tile * ignore_tile) (info->state == WDS_UNDER_CONSTRUCTION) && (info->city_id == city->Body.ID)) { info->city = city; + if ((wonder_improv_id >= 0) && (! wonder_is_buildable_on_tile (candidate, wonder_improv_id))) { + info->state = WDS_UNUSED; + info->city = NULL; + info->city_id = -1; + info->wonder_index = -1; + continue; + } return true; } } @@ -9030,12 +9176,12 @@ free_wonder_district_for_city (City * city) } bool -reserve_wonder_district_for_city (City * city) +reserve_wonder_district_for_city (City * city, int wonder_improv_id) { if (! is->current_config.enable_wonder_districts || (city == NULL)) return false; - if (city_has_assigned_wonder_district (city, NULL)) + if (city_has_assigned_wonder_district (city, NULL, wonder_improv_id)) return true; FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { @@ -9043,6 +9189,8 @@ reserve_wonder_district_for_city (City * city) Tile * candidate = wai.tile; struct district_instance * inst = wai.district_inst; if (inst->district_type != WONDER_DISTRICT_ID) continue; + if ((wonder_improv_id >= 0) && (! wonder_is_buildable_on_tile (candidate, wonder_improv_id))) + continue; struct wonder_district_info * info = &inst->wonder_info; if (info->state == WDS_COMPLETED) continue; @@ -9054,7 +9202,7 @@ reserve_wonder_district_for_city (City * city) } continue; } - if (city_has_assigned_wonder_district (city, candidate)) + if (city_has_assigned_wonder_district (city, candidate, wonder_improv_id)) return true; // Reserve this Wonder district for this city @@ -15188,7 +15336,7 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply // Can only build wonders that need districts if an incomplete wonder district exists if (wonder_requires_district && - ! city_has_wonder_district_with_no_completed_wonder (this)) + ! city_has_wonder_district_with_no_completed_wonder (this, i_improv)) return !apply_strict_rules; } } @@ -15241,7 +15389,7 @@ ai_handle_district_production_requirements (City * city, City_Order * out) if (is->current_config.enable_wonder_districts) { Improvement * improv = &p_bic_data->Improvements[out->OrderID]; if ((improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) && - (! city_has_wonder_district_with_no_completed_wonder (city))) { + (! city_has_wonder_district_with_no_completed_wonder (city, out->OrderID))) { needs_wonder_district = true; if (required_district_id < 0) { required_district_id = WONDER_DISTRICT_ID; @@ -15274,7 +15422,7 @@ ai_handle_district_production_requirements (City * city, City_Order * out) if (fallback_is_feasible && is->current_config.enable_wonder_districts) { Improvement * fallback_improv = &p_bic_data->Improvements[stored->order.OrderID]; if ((fallback_improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) && - (! city_has_wonder_district_with_no_completed_wonder (city))) + (! city_has_wonder_district_with_no_completed_wonder (city, stored->order.OrderID))) fallback_is_feasible = false; } } else if (stored->order.OrderType == COT_Unit) { @@ -16937,6 +17085,8 @@ patch_City_add_or_remove_improvement (City * this, int edx, int improv_id, int a Tile * t = wai.tile; struct district_instance * inst = wai.district_inst; if (inst->district_type != WONDER_DISTRICT_ID) continue; + if (! wonder_is_buildable_on_tile (t, improv_id)) + continue; struct wonder_district_info * info = &inst->wonder_info; if (info->state != WDS_UNDER_CONSTRUCTION) continue; @@ -19153,7 +19303,7 @@ patch_Leader_do_production_phase (Leader * this) } if (wonder_requires_district) { - bool has_wonder_district = reserve_wonder_district_for_city (city); + bool has_wonder_district = reserve_wonder_district_for_city (city, i_improv); if (! has_wonder_district) { needs_halt = true; req_district_id = WONDER_DISTRICT_ID; @@ -25413,7 +25563,7 @@ patch_City_set_production (City * this, int edx, int order_type, int order_id, b if (order_type == COT_Improvement) { Improvement * improv = &p_bic_data->Improvements[order_id]; if (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { - if (reserve_wonder_district_for_city (this)) + if (reserve_wonder_district_for_city (this, order_id)) release_reservation = false; } } From 8b4f6ae135a7ddb4dd40983023ced6f1f917e5e6 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 14:54:13 -0800 Subject: [PATCH 078/356] Add support for snow mountains/volcano/forest in tile types for districts/wonders/natural wonders --- C3X.h | 15 ++-- injected_code.c | 180 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 137 insertions(+), 58 deletions(-) diff --git a/C3X.h b/C3X.h index 49b86ec3..0700e3cf 100644 --- a/C3X.h +++ b/C3X.h @@ -567,7 +567,7 @@ struct district_config { char const * resource_prereq_on_tile; char const * dependent_improvements[MAX_DISTRICT_DEPENDENTS]; char const * img_paths[10]; - unsigned short buildable_square_types_mask; + unsigned int buildable_square_types_mask; bool allow_multiple; bool vary_img_by_era; bool vary_img_by_culture; @@ -602,14 +602,17 @@ struct wonder_district_config { maritime_alt_img_column, maritime_alt_img_construct_row, maritime_alt_img_construct_column; - unsigned short buildable_square_types_mask; + unsigned int buildable_square_types_mask; bool is_dynamic; bool is_maritime; }; enum square_type_extras { SQ_INVALID = -1, - SQ_RIVER = SQ_Ocean + 1 + SQ_RIVER = SQ_Ocean + 1, + SQ_SNOW_VOLCANO, + SQ_SNOW_FOREST, + SQ_SNOW_MOUNTAIN }; struct natural_wonder_district_config { @@ -661,7 +664,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_WonderDistrict, .name = "Wonder District", .tooltip = "Build Wonder District", .advance_prereq = NULL, .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"WonderDistrict.pcx"}, - .buildable_square_types_mask = (unsigned short)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Coast)), + .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Coast)), .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 0 @@ -724,7 +727,7 @@ struct parsed_district_definition { int shield_bonus; int happiness_bonus; int unhappiness_bonus; - unsigned short buildable_square_types_mask; + unsigned int buildable_square_types_mask; bool has_name; bool has_tooltip; bool has_advance_prereq; @@ -755,7 +758,7 @@ struct parsed_wonder_definition { int img_column; int img_construct_row; int img_construct_column; - unsigned short buildable_square_types_mask; + unsigned int buildable_square_types_mask; bool has_name; bool has_img_path; bool has_img_row; diff --git a/injected_code.c b/injected_code.c index 48596020..4097972f 100644 --- a/injected_code.c +++ b/injected_code.c @@ -1708,22 +1708,25 @@ read_square_type_value (struct string_slice const * s, enum SquareTypes * out_ty char const * name; int value; } const entries[] = { - {"desert", SQ_Desert}, - {"plains", SQ_Plains}, - {"grassland", SQ_Grassland}, - {"tundra", SQ_Tundra}, - {"floodplain", SQ_FloodPlain}, - {"hills", SQ_Hills}, - {"mountains", SQ_Mountains}, - {"forest", SQ_Forest}, - {"jungle", SQ_Jungle}, - {"swamp", SQ_Swamp}, - {"volcano", SQ_Volcano}, - {"coast", SQ_Coast}, - {"sea", SQ_Sea}, - {"ocean", SQ_Ocean}, - {"river", SQ_RIVER}, - {"any", SQ_INVALID} + {"desert", SQ_Desert}, + {"plains", SQ_Plains}, + {"grassland", SQ_Grassland}, + {"tundra", SQ_Tundra}, + {"floodplain", SQ_FloodPlain}, + {"hills", SQ_Hills}, + {"mountains", SQ_Mountains}, + {"forest", SQ_Forest}, + {"jungle", SQ_Jungle}, + {"swamp", SQ_Swamp}, + {"volcano", SQ_Volcano}, + {"coast", SQ_Coast}, + {"sea", SQ_Sea}, + {"ocean", SQ_Ocean}, + {"river", SQ_RIVER}, + {"snow-volcano", SQ_SNOW_VOLCANO}, + {"snow-forest", SQ_SNOW_FOREST}, + {"snow-mountain", SQ_SNOW_MOUNTAIN}, + {"any", SQ_INVALID} }; for (int i = 0; i < (int)ARRAY_LEN (entries); i++) { @@ -1736,38 +1739,110 @@ read_square_type_value (struct string_slice const * s, enum SquareTypes * out_ty return false; } -unsigned short +unsigned int square_type_mask_bit (enum SquareTypes type) { - if ((int)type < 0 || type > SQ_RIVER) + if ((int)type < 0 || type > SQ_SNOW_MOUNTAIN) return 0; - return (unsigned short)(1u << type); + return (unsigned int)(1u << type); } -unsigned short +unsigned int all_square_types_mask (void) { - return (unsigned short)((1u << (SQ_RIVER + 1)) - 1); + return (unsigned int)((1u << (SQ_SNOW_MOUNTAIN + 1)) - 1); } -unsigned short +unsigned int district_default_buildable_mask (void) { - return (unsigned short)DEFAULT_DISTRICT_BUILDABLE_MASK; + return (unsigned int)DEFAULT_DISTRICT_BUILDABLE_MASK; } bool -district_is_buildable_on_square_type (struct district_config const * cfg, enum SquareTypes base_type) +tile_has_snow_mountain (Tile * tile) { - if (cfg == NULL) + return (tile != NULL) && (tile != p_null_tile) && tile->vtable->m29_Check_Mountain_Snowcap (tile); +} + +bool +tile_has_snow_volcano (Tile * tile) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return false; + + if (tile->vtable->m50_Get_Square_BaseType (tile) != SQ_Volcano) + return false; + + int overlays = tile->vtable->m43_Get_field_30 (tile); + return (overlays & 0x100000) != 0; +} + +bool +tile_has_snow_forest (Tile * tile) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return false; + + if (tile->vtable->m50_Get_Square_BaseType (tile) != SQ_Forest) + return false; + + return tile->vtable->m12_Check_Forest_Pines (tile) != 0; +} + +bool +tile_matches_square_type (Tile * tile, enum SquareTypes type) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return false; + + switch (type) { + case SQ_SNOW_MOUNTAIN: + return tile_has_snow_mountain (tile); + case SQ_SNOW_VOLCANO: + return tile_has_snow_volcano (tile); + case SQ_SNOW_FOREST: + return tile_has_snow_forest (tile); + case SQ_RIVER: + return tile->vtable->m37_Get_River_Code (tile) != 0; + default: + return tile->vtable->m50_Get_Square_BaseType (tile) == type; + } +} + +bool +tile_matches_square_type_mask (Tile * tile, unsigned int mask) +{ + if ((tile == NULL) || (tile == p_null_tile) || (mask == 0)) return false; - unsigned short mask = cfg->buildable_square_types_mask; + enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); + unsigned int base_bit = square_type_mask_bit (base_type); + if ((base_bit != 0) && ((mask & base_bit) != 0)) + return true; + + enum SquareTypes const special_types[] = {SQ_RIVER, SQ_SNOW_MOUNTAIN, SQ_SNOW_VOLCANO, SQ_SNOW_FOREST}; + for (int i = 0; i < (int)ARRAY_LEN (special_types); i++) { + enum SquareTypes stype = special_types[i]; + unsigned int bit = square_type_mask_bit (stype); + if ((mask & bit) && tile_matches_square_type (tile, stype)) + return true; + } + + return false; +} + +bool +district_is_buildable_on_square_type (struct district_config const * cfg, Tile * tile) +{ + if ((cfg == NULL) || (tile == NULL) || (tile == p_null_tile)) + return false; + + unsigned int mask = cfg->buildable_square_types_mask; if (mask == 0) mask = district_default_buildable_mask (); - unsigned short bit = square_type_mask_bit (base_type); - return (bit != 0) && ((mask & bit) != 0); + return tile_matches_square_type_mask (tile, mask); } bool @@ -1786,6 +1861,12 @@ read_natural_wonder_terrain_type (struct string_slice const * s, enum SquareType case SQ_FloodPlain: case SQ_Swamp: case SQ_Hills: + case SQ_Mountains: + case SQ_Forest: + case SQ_Volcano: + case SQ_SNOW_MOUNTAIN: + case SQ_SNOW_FOREST: + case SQ_SNOW_VOLCANO: case SQ_Coast: case SQ_Sea: case SQ_Ocean: @@ -3297,9 +3378,7 @@ tai_get_coords (struct tiles_around_iter * tai, int * out_x, int * out_y) bool tile_square_type_is (Tile * tile, enum SquareTypes type) { - if ((tile == NULL) || (tile == p_null_tile)) - return false; - return tile->vtable->m50_Get_Square_BaseType (tile) == type; + return tile_matches_square_type (tile, type); } bool @@ -3417,8 +3496,7 @@ natural_wonder_terrain_matches (struct natural_wonder_district_config const * cf if ((cfg == NULL) || (tile == NULL) || (tile == p_null_tile)) return false; - enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); - if (base_type != cfg->terrain_type) + if (! tile_matches_square_type (tile, cfg->terrain_type)) return false; if (natural_wonder_is_coastal_island (tile, tile_x, tile_y)) @@ -3866,19 +3944,19 @@ find_wonder_config_index_by_improvement_id (int improv_id) void set_wonder_built_flag (int improv_id, bool is_built); -unsigned short +unsigned int wonder_buildable_square_type_mask (struct wonder_district_config const * cfg) { if (cfg == NULL) return district_default_buildable_mask (); - unsigned short mask = cfg->buildable_square_types_mask; + unsigned int mask = cfg->buildable_square_types_mask; if (mask == 0) mask = district_default_buildable_mask (); return mask; } -unsigned short +unsigned int wonder_buildable_mask_for_improvement (int improv_id) { int windex = find_wonder_config_index_by_improvement_id (improv_id); @@ -3888,11 +3966,12 @@ wonder_buildable_mask_for_improvement (int improv_id) } bool -wonder_is_buildable_on_square_type (struct wonder_district_config const * cfg, enum SquareTypes base_type) +wonder_is_buildable_on_square_type (struct wonder_district_config const * cfg, Tile * tile) { - unsigned short mask = wonder_buildable_square_type_mask (cfg); - unsigned short bit = square_type_mask_bit (base_type); - return (bit != 0) && ((mask & bit) != 0); + if ((cfg == NULL) || (tile == NULL) || (tile == p_null_tile)) + return false; + + return tile_matches_square_type_mask (tile, wonder_buildable_square_type_mask (cfg)); } bool @@ -3901,9 +3980,8 @@ wonder_is_buildable_on_tile (Tile * tile, int wonder_improv_id) if ((tile == NULL) || (tile == p_null_tile)) return false; - unsigned short mask = wonder_buildable_mask_for_improvement (wonder_improv_id); - unsigned short bit = square_type_mask_bit (tile->vtable->m50_Get_Square_BaseType (tile)); - return (bit != 0) && ((mask & bit) != 0); + unsigned int mask = wonder_buildable_mask_for_improvement (wonder_improv_id); + return tile_matches_square_type_mask (tile, mask); } int @@ -5161,12 +5239,12 @@ parse_config_string_list (char * value_text, bool parse_buildable_square_type_mask (struct string_slice const * value, - unsigned short * out_mask, + unsigned int * out_mask, struct error_line ** parse_errors, int line_number) { char * value_text = trim_and_extract_slice (value, 0); - unsigned short mask = 0; + unsigned int mask = 0; int entry_count = 0; if (value_text != NULL) { @@ -5566,7 +5644,7 @@ handle_district_definition_key (struct parsed_district_definition * def, free (value_text); } else if (slice_matches_str (key, "buildable_on")) { - unsigned short mask; + unsigned int mask; if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number)) { def->buildable_square_types_mask = mask; def->has_buildable_on = true; @@ -6052,7 +6130,7 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, } } else if (slice_matches_str (key, "buildable_on")) { - unsigned short mask; + unsigned int mask; if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number)) { def->buildable_square_types_mask = mask; def->has_buildable_on = true; @@ -7708,8 +7786,7 @@ can_build_district_on_tile (Tile * tile, int district_id) if ((cfg->command == UCV_Build_Aerodrome) && !is->current_config.enable_aerodrome_districts) return false; if ((cfg->command == UCV_Build_Port) && !is->current_config.enable_port_districts) return false; - enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); - if (! district_is_buildable_on_square_type (cfg, base_type)) + if (! district_is_buildable_on_square_type (cfg, tile)) return false; int tile_x = 0, tile_y = 0; @@ -13199,7 +13276,7 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) bool has_district = (tile != NULL) && (tile != p_null_tile) && (get_district_instance (tile) != NULL); if (has_district) { - return (base_type != SQ_FloodPlain && base_type != SQ_Forest && base_type != SQ_Jungle && base_type != SQ_Volcano); + return Unit_can_perform_command (this, __, unit_command_value); } } else if (unit_command_value == UCV_Join_City) { @@ -13400,8 +13477,7 @@ issue_district_worker_command (Unit * unit, int command) if (tile->vtable->m20_Check_Pollution (tile, __, 0)) return; - enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); - if (! district_is_buildable_on_square_type (&is->district_configs[district_id], base_type)) + if (! district_is_buildable_on_square_type (&is->district_configs[district_id], tile)) return; // If District will be replaced by another District From d8c7ea90c278da2769f0dcb30153c1787fd0b236 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 15:05:37 -0800 Subject: [PATCH 079/356] Rename snow-mountain -> snow-mountains, set Everest and Matterhorn to be adjacent to snow mountains --- default.districts_natural_wonders_config.txt | 4 +-- injected_code.c | 38 ++++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/default.districts_natural_wonders_config.txt b/default.districts_natural_wonders_config.txt index 8db672d3..b5e7613a 100644 --- a/default.districts_natural_wonders_config.txt +++ b/default.districts_natural_wonders_config.txt @@ -57,7 +57,7 @@ happiness_bonus = 1 #Wonder name = Mount Everest terrain_type = grassland -adjacent_to = mountains +adjacent_to = snow-mountains img_path = NaturalWonders.pcx img_row = 1 img_column = 0 @@ -112,7 +112,7 @@ happiness_bonus = 1 #Wonder name = Matterhorn terrain_type = grassland -adjacent_to = mountains +adjacent_to = snow-mountains img_path = NaturalWonders.pcx img_row = 2 img_column = 0 diff --git a/injected_code.c b/injected_code.c index 4097972f..4919ef4a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -1708,25 +1708,25 @@ read_square_type_value (struct string_slice const * s, enum SquareTypes * out_ty char const * name; int value; } const entries[] = { - {"desert", SQ_Desert}, - {"plains", SQ_Plains}, - {"grassland", SQ_Grassland}, - {"tundra", SQ_Tundra}, - {"floodplain", SQ_FloodPlain}, - {"hills", SQ_Hills}, - {"mountains", SQ_Mountains}, - {"forest", SQ_Forest}, - {"jungle", SQ_Jungle}, - {"swamp", SQ_Swamp}, - {"volcano", SQ_Volcano}, - {"coast", SQ_Coast}, - {"sea", SQ_Sea}, - {"ocean", SQ_Ocean}, - {"river", SQ_RIVER}, - {"snow-volcano", SQ_SNOW_VOLCANO}, - {"snow-forest", SQ_SNOW_FOREST}, - {"snow-mountain", SQ_SNOW_MOUNTAIN}, - {"any", SQ_INVALID} + {"desert", SQ_Desert}, + {"plains", SQ_Plains}, + {"grassland", SQ_Grassland}, + {"tundra", SQ_Tundra}, + {"floodplain", SQ_FloodPlain}, + {"hills", SQ_Hills}, + {"mountains", SQ_Mountains}, + {"forest", SQ_Forest}, + {"jungle", SQ_Jungle}, + {"swamp", SQ_Swamp}, + {"volcano", SQ_Volcano}, + {"coast", SQ_Coast}, + {"sea", SQ_Sea}, + {"ocean", SQ_Ocean}, + {"river", SQ_RIVER}, + {"snow-volcano", SQ_SNOW_VOLCANO}, + {"snow-forest", SQ_SNOW_FOREST}, + {"snow-mountains", SQ_SNOW_MOUNTAIN}, + {"any", SQ_INVALID} }; for (int i = 0; i < (int)ARRAY_LEN (entries); i++) { From 672224b42f2d2084ded9d9ca63d40e5a5ad539ce Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 16:18:45 -0800 Subject: [PATCH 080/356] Add Great Lighthouse to Wonders_2.pcx, remove Wonders_3 --- Art/Districts/1200/Wonders_2.PCX | Bin 32296 -> 33306 bytes Art/Districts/1200/Wonders_3.PCX | Bin 7971 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Art/Districts/1200/Wonders_3.PCX diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index 742c89b6ad746b4d2906953f631e8c0657d5b966..e01640785d2c72ac48a4d51ec11e2906f63a379e 100644 GIT binary patch delta 1374 zcmX|BO=w(I6h`4S$xE;*%`H(&A#NrO*utpz=;Ep%>f(aM#a%f0X_DrDA{Z5`cooq_ zH$}V~b#bY}fPxFbjXOE_MV+bDs`IQ>Gt)_@&u3nu6HSR5_4?gJgn^m&&iT&y&Ud~u z@BMYd+ppgEVX3t5?|+^OsH(?Hhq}*jN*Ps2tJ0CK*a=z>R`mO&V%MimQJa0H8>Qo2 zpW}DJs(N7L!F@F+xIC{*BM)_d;iw)^dO^QCa-#c!Q_`mFOMQLh@$RoYAm$hKBcrnC zqGLRadGNJ9H#&CHH;y({7|=SU)cN*}v5B6#GgMoVLy4Xo8}I$jSrt?mwT3R~m9eS) z-{TpnIkhRH>SaANKGpXF-i0qxTBPhpeRh1N?hm_fTRqu>X?{7Fxbv&a@PFYir zot(Y3;Ypc@>YQ;@tDB}ie{!L};jF}{7$jhv1ymO%Z0h?a<_}(R)>ui2qznkil!es3 zr==?s<$*Ovi84x3O9GRjONXOb^{VclEFWwm8Y^pLz-VnRoKp3ger@temO?cnRsaPv z$`R#M{aODxx!9k9b*-r4gwi+zA4;hDi=Lh;-?r{Spsx(xt#ew0kJg*&#L1htx?7A4lR#ULEh zB$V&yb2HB$-gcHlHNXq_@)>Brd`3uVxT6ovo*vwCAe34zc*Law!4@2oSu9fo0&LOi zt=Z>>Hryyx?GR^vgPq6irVCI5V1xkeMo!QW@&pmtWnllpB;Ko>(iXTbY|540h>^KlbxC9*KD`zv^GrwHC z>LO_s4`oC(6WPSTzL1KeFiORKcWJ;)R5V_~JQzBY>X+u1j<$AjXc#hUu;@&B;eAhF z)dGWDfU}_!%f27n> zeAhxPiQBRazUgp@1>q9bcth70A01qEP&pD%5~;kR0(3`E_U?+?rnIRKmmj!&)t$g) z;g*pU6#`{BF4u5I^`@RIA7`u1q7IfA68qey4o*E#%(B@R2YW9{+InH}&Q6 Tk-moSi>3h^ZhjOj-FfrB?_?QO delta 387 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 Date: Sun, 14 Dec 2025 16:29:38 -0800 Subject: [PATCH 081/356] Add Colossus construction images --- Art/Districts/1200/Wonders_2.PCX | Bin 33306 -> 35620 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index e01640785d2c72ac48a4d51ec11e2906f63a379e..2f6531135cc7de5fecf387ea9bc763d0d453ecbe 100644 GIT binary patch delta 4344 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 N=dH7agTMN>{|CPGZ-f8< delta 1865 zcmY*ZOKekD5LH0#B6L|H$cI3wglU&<_?4=nl3`Jh+D%c#f;Clz)DPeCVn=+`O(j=P z>Hwn|I%wGk5OXGjr#U zUsqi`x9TtReFW+N`dp)XrZk8E3$ctNIz z_7ZLJTAlFKG_5B!Y*Ev=>V#XxbMQ=`>UAf$#+rvnr3#$%p# zo5U2ek!s2k#G^e(YXo|~M%>d#np`w4uu@X(K~>8-X8(L_UEP>{jo*8A=~yGb8;>{E zP1;`^-|*!mT`9Ez9)CX+KikNQm1hWyh1U3`RyUq2sD{ z@5z=rwck3`(gLKRdY8A7qRbsgIh}DC=!O|$1akJ7Q;nMd7t30Nl*Eg5@wh=Fg=9R3 zNP*QQYtNi&eh=uyqK@Ngd+gn2^46gaNjp`Z3&*>~iqXM7e0t+*px?fDdQ)vJoZigv zLn!GKOxW=Z^_^pd{gx7U6bsxnWbgm6IRXr0Mjukdm}!?%vKvw;QbQk~_LD1gvu8@} zCMNDf+|(g5cQ8ru!{SCdL&kDp#;KwYKVY85g=IpW5plya7}5a}McmU_HcWVx+%VD? z6(519T|#eS%a( zJj*1qFctLHP){o`StN`lUL^GdSxpmvhMX?Lzm${D;@V>d@xiP<#p5%6Jw=n`#WMIM`cLHo@H$vbnYEIR-1-I+zonUlQl`zDc2agE=O z8C>%3!&L0U(X|wA$^Iq!PHmOwiU8Mj5$(o_CMi_4!cwfLQlXVW(utaOKY~YiMQq0~ zZI{lY<<~lwX%TTb&uS9;O3k96Rj?{CGKB>U*RY#@g$o(PSdnUklD;Z@8769FVCGlq zjS-!P$-s)5GHX<8B8+jf!#um@P-HZ}o957!A-2H0Uv=m_WIzUYUE!#ksr9DcnlmsC zuI!RQ8JK5dtaSeelRAxkK}m<2b~Xg3UpN0Wf(<*HLXr;do8}#F@~mO|+q1rY!*=5A zx+5!3astU6fk>=AP`{==&@{2~7&+`F&{Tu!W0Am)t!Y!4AgHu z=mf?&R(+yO!U-JQS|138Wu`vdJkIO;ZQpCh&#h;h_i|#7p<|0owS~7HY;D^cZrdi6 zF-mopUjFnMMKJW?-cN#|ErIab1Ht!4UrwIg=hrjqw0-pD`uq9DP29p(8AhwKea+f< zIJ7;`8aN=+3_eUI!}<62g_gQy`{N6Z{C<6*ajQR3dvyfC1Ho1)-=dXg*Z5QR7Np!t Wc!V=!N-LL-ez(2u^5&oFR{sY!qnFA6 From 2df0224a35cf28866515f2c05cf49d4c569b5aff Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 16:44:35 -0800 Subject: [PATCH 082/356] Add align_to_coast prop for districts; Update shading on Colossus art --- Art/Districts/1200/Wonders_2.PCX | Bin 35620 -> 35640 bytes C3X.h | 5 ++++- injected_code.c | 23 ++++++++++++++++------- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index 2f6531135cc7de5fecf387ea9bc763d0d453ecbe..c8a5a8223f5e59a039dfa9c4822fc914ea41a230 100644 GIT binary patch delta 370 zcmWlUu}d3K9LGU>2#sW@aE8MOm7vdSk-~WiD)gcB-YNRDonDthT^zbPxSc{`{(%RM zH%aHhIf&c$loSiPG*xM}P1H6xh$uL8==+)OpU($An;RY58y%nK%AL`c(yz;^`*9{_ zffXv2;6Nwk8K*BuDchuLb3tf~VIM6STTEFcse39@1?DW5ou|`FZ*p4>=>0FcHBI6l zae+GaE$(wSkb0#uJ;=pd#bxT}3#uD6Q1*=O%}wQ ziAM~-d1Obz4Kit delta 350 zcmV-k0ipi5mI9=f0J{+|1-hV wC^beY1;REd4#D!1lZGr9HboA?DUh?mx?M*v*8l(h%Kq5@!SOXHv)6`U1lok3d;kCd diff --git a/C3X.h b/C3X.h index 0700e3cf..4901a014 100644 --- a/C3X.h +++ b/C3X.h @@ -573,6 +573,7 @@ struct district_config { bool vary_img_by_culture; bool is_dynamic; bool is_maritime; + bool align_to_coast; int dependent_improvement_count; int img_path_count; int max_building_index; @@ -699,7 +700,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereq = "Map Making", .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .dependent_improvements = {"Harbor", "Commercial Dock"}, .buildable_square_types_mask = (1 << SQ_Coast), - .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, + .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .align_to_coast = true, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 0 } }; @@ -717,6 +718,7 @@ struct parsed_district_definition { bool allow_multiple; bool vary_img_by_era; bool vary_img_by_culture; + bool align_to_coast; int btn_tile_sheet_column; int btn_tile_sheet_row; int defense_bonus_percent; @@ -736,6 +738,7 @@ struct parsed_district_definition { bool has_allow_multiple; bool has_vary_img_by_era; bool has_vary_img_by_culture; + bool has_align_to_coast; bool has_btn_tile_sheet_column; bool has_btn_tile_sheet_row; bool has_defense_bonus_percent; diff --git a/injected_code.c b/injected_code.c index 4919ef4a..2fa940ee 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5350,6 +5350,8 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->vary_img_by_era = def->vary_img_by_era; if (def->has_vary_img_by_culture) cfg->vary_img_by_culture = def->vary_img_by_culture; + if (def->has_align_to_coast) + cfg->align_to_coast = def->align_to_coast; if (def->has_btn_tile_sheet_column) cfg->btn_tile_sheet_column = def->btn_tile_sheet_column; if (def->has_btn_tile_sheet_row) @@ -5481,6 +5483,7 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.allow_multiple = def->has_allow_multiple ? def->allow_multiple : false; new_cfg.vary_img_by_era = def->has_vary_img_by_era ? def->vary_img_by_era : false; new_cfg.vary_img_by_culture = def->has_vary_img_by_culture ? def->vary_img_by_culture : false; + new_cfg.align_to_coast = def->has_align_to_coast ? def->align_to_coast : false; new_cfg.btn_tile_sheet_column = def->has_btn_tile_sheet_column ? def->btn_tile_sheet_column : 0; new_cfg.btn_tile_sheet_row = def->has_btn_tile_sheet_row ? def->btn_tile_sheet_row : 0; new_cfg.defense_bonus_percent = def->has_defense_bonus_percent ? def->defense_bonus_percent : 100; @@ -5677,6 +5680,15 @@ handle_district_definition_key (struct parsed_district_definition * def, } else add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + } else if (slice_matches_str (key, "align_to_coast")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->align_to_coast = (ival != 0); + def->has_align_to_coast = true; + } else + add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + } else if (slice_matches_str (key, "btn_tile_sheet_column")) { struct string_slice val_slice = *value; int ival; @@ -24611,6 +24623,8 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par variant = culture; if (cfg->vary_img_by_era) era = leader->Era; + if (cfg->align_to_coast) + align_variant_and_pixel_offsets_with_coastline (tile, &variant, &pixel_x, &pixel_y); } else if (district_id != WONDER_DISTRICT_ID && district_id != NATURAL_WONDER_DISTRICT_ID) { Sprite * abandoned_sprite = &is->abandoned_district_img; if (tile->vtable->m35_Check_Is_Water (tile) && is->abandoned_maritime_district_img.vtable != NULL) @@ -24654,13 +24668,8 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par patch_Sprite_draw_on_map (csprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); return; } - break; - } - case PORT_DISTRICT_ID: - { - align_variant_and_pixel_offsets_with_coastline (tile, &variant, &pixel_x, &pixel_y); - // Don't break, let fall through to default to count buildings - } + break; + } default: { struct district_infos * info = &is->district_infos[district_id]; From 9e2742be0bffd9fe731587e65335608f8a5b4eaa Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 18:22:33 -0800 Subject: [PATCH 083/356] Add Great Lighthouse construct art; Add wonder defs in config for lighthouse & colossus --- Art/Districts/1200/Wonders_2.PCX | Bin 35640 -> 36811 bytes default.districts_wonders_config.txt | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index c8a5a8223f5e59a039dfa9c4822fc914ea41a230..6348598429812afd75006b17377658f6ffd0ad31 100644 GIT binary patch delta 1470 zcmWktT}%{L6i!Tbfv^~Nn#{t&4l+8;vg<4}tX;ZUb|-CEc+fS{Cah^Gf`E+FA6QHu zS~C=ljn%|9xzQ#y8QV0pYHHG?P1^LS=i&=#RFKhHr7*03KSdEt`g(du?$f>B`ObH~ z^WEPrZu;}`rk`$gl}w>wO^DO%cGp1pG}c%ic=#V+WEx zoKO?~5{}?P5uOo*lQ;`-L{*}HvA=tcZ*?#f0raY&GD(SX>(UL;0EgH&?|s_@EWgM0 z9{5<=gA~egUdQa8OqA$GR zD#}`^E+nmNRD^||*zU4eb%Vk&FvmLjPRVm9Z`et0esH`|E8$5U9P1Oacc>b$CdM)3 zve~}TIv2fUgc4BNB%0= z8j))Uny;ui?pjwbR;CGG{*t{j_*L{@>?OHkavx9bDmOuPU~Pg&#PZ`2~?$cYElCMEdk*!%HR zkSnT(*aPHVg$b?^wGPDegg>0+#xlEm*lu(&K}y$?8(&{B)tzx3++2~!s8%il3s$y^ ziZy9>)&?i@2Dp^pMfTB=3)whDUSD`U8p}i1r4H zBON4692$hA4m2_n3c8-}O&^|@hnM%YYRc`2a@7cmS3Ct{;vK$FS`2U>(!?j`)s*K_i>p$rai> Date: Sun, 14 Dec 2025 19:23:26 -0800 Subject: [PATCH 084/356] Add support for config & rendering of alternate direction for wonders --- C3X.h | 45 ++--- default.districts_wonders_config.txt | 4 +- injected_code.c | 257 ++++++++++++++++++++++++--- 3 files changed, 262 insertions(+), 44 deletions(-) diff --git a/C3X.h b/C3X.h index 4901a014..7af906fe 100644 --- a/C3X.h +++ b/C3X.h @@ -572,7 +572,6 @@ struct district_config { bool vary_img_by_era; bool vary_img_by_culture; bool is_dynamic; - bool is_maritime; bool align_to_coast; int dependent_improvement_count; int img_path_count; @@ -585,7 +584,6 @@ struct district_config { int gold_bonus; int shield_bonus; int happiness_bonus; - int unhappiness_bonus; int defense_bonus_percent; }; @@ -597,15 +595,13 @@ struct wonder_district_config { img_column, img_construct_row, img_construct_column, - - // Maritime wonders have a set of alternative images for facing east - maritime_alt_img_row, - maritime_alt_img_column, - maritime_alt_img_construct_row, - maritime_alt_img_construct_column; + img_alt_dir_construct_row, + img_alt_dir_construct_column, + img_alt_dir_row, + img_alt_dir_column; unsigned int buildable_square_types_mask; + bool enable_img_alt_dir; bool is_dynamic; - bool is_maritime; }; enum square_type_extras { @@ -631,7 +627,6 @@ struct natural_wonder_district_config { int gold_bonus; int shield_bonus; int happiness_bonus; - int unhappiness_bonus; bool is_dynamic; }; @@ -658,7 +653,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 5, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, - .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 25 + .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 25 }, { @@ -667,7 +662,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"WonderDistrict.pcx"}, .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Coast)), .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 }, { @@ -676,7 +671,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"DistributionHub.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 2, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 }, { @@ -685,7 +680,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"Aerodrome.pcx"}, .dependent_improvements = {"Airport"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 1, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 }, { .command = -1, .name = "Natural Wonder", .tooltip = NULL, @@ -693,15 +688,15 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {0}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 0, .max_building_index = 0, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 }, { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", - .advance_prereq = "Map Making", .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .is_maritime = true, + .advance_prereq = "Map Making", .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .align_to_coast = true, .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .dependent_improvements = {"Harbor", "Commercial Dock"}, .buildable_square_types_mask = (1 << SQ_Coast), .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .align_to_coast = true, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .unhappiness_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 } }; @@ -728,7 +723,6 @@ struct parsed_district_definition { int gold_bonus; int shield_bonus; int happiness_bonus; - int unhappiness_bonus; unsigned int buildable_square_types_mask; bool has_name; bool has_tooltip; @@ -748,7 +742,6 @@ struct parsed_district_definition { bool has_gold_bonus; bool has_shield_bonus; bool has_happiness_bonus; - bool has_unhappiness_bonus; bool has_buildable_on; bool has_resource_prereq; bool has_resource_prereq_on_tile; @@ -761,6 +754,11 @@ struct parsed_wonder_definition { int img_column; int img_construct_row; int img_construct_column; + int img_alt_dir_construct_row; + int img_alt_dir_construct_column; + int img_alt_dir_row; + int img_alt_dir_column; + bool enable_img_alt_dir; unsigned int buildable_square_types_mask; bool has_name; bool has_img_path; @@ -768,6 +766,11 @@ struct parsed_wonder_definition { bool has_img_column; bool has_img_construct_row; bool has_img_construct_column; + bool has_img_alt_dir_construct_row; + bool has_img_alt_dir_construct_column; + bool has_img_alt_dir_row; + bool has_img_alt_dir_column; + bool has_enable_img_alt_dir; bool has_buildable_on; }; @@ -785,7 +788,6 @@ struct parsed_natural_wonder_definition { int gold_bonus; int shield_bonus; int happiness_bonus; - int unhappiness_bonus; bool has_name; bool has_img_path; bool has_img_row; @@ -799,7 +801,6 @@ struct parsed_natural_wonder_definition { bool has_gold_bonus; bool has_shield_bonus; bool has_happiness_bonus; - bool has_unhappiness_bonus; }; struct scenario_district_entry { @@ -1459,6 +1460,8 @@ struct injected_state { struct wonder_district_image_set { Sprite img; Sprite construct_img; + Sprite alt_dir_img; + Sprite alt_dir_construct_img; } wonder_district_img_sets[MAX_WONDER_DISTRICT_TYPES]; struct natural_wonder_district_image_set { diff --git a/default.districts_wonders_config.txt b/default.districts_wonders_config.txt index b797128b..d456dc94 100644 --- a/default.districts_wonders_config.txt +++ b/default.districts_wonders_config.txt @@ -104,6 +104,7 @@ img_column = 1 #Wonder name = The Great Lighthouse +buildable_on = coast img_path = Wonders_2.pcx img_construct_row = 2 img_construct_column = 2 @@ -112,6 +113,8 @@ img_column = 3 #Wonder name = The Colossus +buildable_on = coast +enable_img_alt_dir = 1 img_path = Wonders_2.pcx img_construct_row = 3 img_construct_column = 0 @@ -121,7 +124,6 @@ img_alt_dir_construct_row = 3 img_alt_dir_construct_column = 2 img_alt_dir_row = 3 img_alt_dir_column = 3 -check_direction = 1 #Wonder name = Apollo Program diff --git a/injected_code.c b/injected_code.c index 2fa940ee..55a23f3f 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5978,6 +5978,11 @@ add_dynamic_wonder_from_definition (struct parsed_wonder_definition * def, int s new_cfg.img_column = def->img_column; new_cfg.img_construct_row = def->img_construct_row; new_cfg.img_construct_column = def->img_construct_column; + new_cfg.img_alt_dir_construct_row = def->img_alt_dir_construct_row; + new_cfg.img_alt_dir_construct_column = def->img_alt_dir_construct_column; + new_cfg.img_alt_dir_row = def->img_alt_dir_row; + new_cfg.img_alt_dir_column = def->img_alt_dir_column; + new_cfg.enable_img_alt_dir = def->enable_img_alt_dir; new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); if (existing_index >= 0) { @@ -6042,6 +6047,40 @@ finalize_parsed_wonder_definition (struct parsed_wonder_definition * def, err->text[(sizeof err->text) - 1] = '\0'; } } + if (def->enable_img_alt_dir) { + if (! def->has_img_alt_dir_row) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: img_alt_dir_row (value is required when enable_img_alt_dir is set)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + if (! def->has_img_alt_dir_column) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: img_alt_dir_column (value is required when enable_img_alt_dir is set)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + if (! def->has_img_alt_dir_construct_row) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: img_alt_dir_construct_row (value is required when enable_img_alt_dir is set)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + if (! def->has_img_alt_dir_construct_column) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: img_alt_dir_construct_column (value is required when enable_img_alt_dir is set)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + } if (ok) add_dynamic_wonder_from_definition (def, section_start_line); @@ -6141,6 +6180,61 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); } + } else if (slice_matches_str (key, "img_alt_dir_construct_row")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_alt_dir_construct_row = ival; + def->has_img_alt_dir_construct_row = true; + } else { + def->has_img_alt_dir_construct_row = false; + add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + } + + } else if (slice_matches_str (key, "img_alt_dir_construct_column")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_alt_dir_construct_column = ival; + def->has_img_alt_dir_construct_column = true; + } else { + def->has_img_alt_dir_construct_column = false; + add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + } + + } else if (slice_matches_str (key, "img_alt_dir_row")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_alt_dir_row = ival; + def->has_img_alt_dir_row = true; + } else { + def->has_img_alt_dir_row = false; + add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + } + + } else if (slice_matches_str (key, "img_alt_dir_column")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_alt_dir_column = ival; + def->has_img_alt_dir_column = true; + } else { + def->has_img_alt_dir_column = false; + add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + } + + } else if (slice_matches_str (key, "enable_img_alt_dir")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->enable_img_alt_dir = (ival != 0); + def->has_enable_img_alt_dir = true; + } else { + def->has_enable_img_alt_dir = false; + add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + } + } else if (slice_matches_str (key, "buildable_on")) { unsigned int mask; if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number)) { @@ -7640,6 +7734,10 @@ deinit_district_images (void) set->img.vtable->destruct (&set->img, __, 0); if (set->construct_img.vtable != NULL) set->construct_img.vtable->destruct (&set->construct_img, __, 0); + if (set->alt_dir_img.vtable != NULL) + set->alt_dir_img.vtable->destruct (&set->alt_dir_img, __, 0); + if (set->alt_dir_construct_img.vtable != NULL) + set->alt_dir_construct_img.vtable->destruct (&set->alt_dir_construct_img, __, 0); } for (int ni = 0; ni < MAX_NATURAL_WONDER_DISTRICT_TYPES; ni++) { @@ -10970,7 +11068,8 @@ bool load_day_night_hour_images(struct day_night_cycle_img_set *this, const char bool pcx_loaded = false; for (int wi = 0; wi < is->wonder_district_count; wi++) { - char const * img_path = is->wonder_district_configs[wi].img_path; + struct wonder_district_config * cfg = &is->wonder_district_configs[wi]; + char const * img_path = cfg->img_path; if (img_path == NULL) img_path = "Wonders.pcx"; @@ -10993,15 +11092,29 @@ bool load_day_night_hour_images(struct day_night_cycle_img_set *this, const char if (! pcx_loaded) continue; - Sprite_construct (&this->Wonder_District_Images[wi].img); - int x = 128 * is->wonder_district_configs[wi].img_column; - int y = 64 * is->wonder_district_configs[wi].img_row; - Sprite_slice_pcx (&this->Wonder_District_Images[wi].img, __, &wpcx, x, y, 128, 64, 1, 1); + struct wonder_district_image_set * set = &this->Wonder_District_Images[wi]; + + Sprite_construct (&set->img); + int x = 128 * cfg->img_column; + int y = 64 * cfg->img_row; + Sprite_slice_pcx (&set->img, __, &wpcx, x, y, 128, 64, 1, 1); - Sprite_construct (&this->Wonder_District_Images[wi].construct_img); - int cx = 128 * is->wonder_district_configs[wi].img_construct_column; - int cy = 64 * is->wonder_district_configs[wi].img_construct_row; - Sprite_slice_pcx (&this->Wonder_District_Images[wi].construct_img, __, &wpcx, cx, cy, 128, 64, 1, 1); + Sprite_construct (&set->construct_img); + int cx = 128 * cfg->img_construct_column; + int cy = 64 * cfg->img_construct_row; + Sprite_slice_pcx (&set->construct_img, __, &wpcx, cx, cy, 128, 64, 1, 1); + + if (cfg->enable_img_alt_dir) { + Sprite_construct (&set->alt_dir_img); + int ax = 128 * cfg->img_alt_dir_column; + int ay = 64 * cfg->img_alt_dir_row; + Sprite_slice_pcx (&set->alt_dir_img, __, &wpcx, ax, ay, 128, 64, 1, 1); + + Sprite_construct (&set->alt_dir_construct_img); + int acx = 128 * cfg->img_alt_dir_construct_column; + int acy = 64 * cfg->img_alt_dir_construct_row; + Sprite_slice_pcx (&set->alt_dir_construct_img, __, &wpcx, acx, acy, 128, 64, 1, 1); + } } if (pcx_loaded) @@ -11202,6 +11315,18 @@ build_sprite_proxies_24(Map_Renderer *mr) { Sprite * base_construct = &is->wonder_district_img_sets[wi].construct_img; Sprite * proxy_construct = &is->day_night_cycle_imgs[h].Wonder_District_Images[wi].construct_img; insert_sprite_proxy (base_construct, proxy_construct, h); + + if (is->wonder_district_img_sets[wi].alt_dir_img.vtable != NULL) { + Sprite * base_alt = &is->wonder_district_img_sets[wi].alt_dir_img; + Sprite * proxy_alt = &is->day_night_cycle_imgs[h].Wonder_District_Images[wi].alt_dir_img; + insert_sprite_proxy (base_alt, proxy_alt, h); + } + + if (is->wonder_district_img_sets[wi].alt_dir_construct_img.vtable != NULL) { + Sprite * base_alt_construct = &is->wonder_district_img_sets[wi].alt_dir_construct_img; + Sprite * proxy_alt_construct = &is->day_night_cycle_imgs[h].Wonder_District_Images[wi].alt_dir_construct_img; + insert_sprite_proxy (base_alt_construct, proxy_alt_construct, h); + } } } } @@ -24148,7 +24273,8 @@ init_district_images () bool pcx_loaded = false; for (int wi = 0; wi < is->wonder_district_count; wi++) { - char const * img_path = is->wonder_district_configs[wi].img_path; + struct wonder_district_config * cfg = &is->wonder_district_configs[wi]; + char const * img_path = cfg->img_path; if (img_path == NULL) img_path = "Wonders.pcx"; @@ -24175,15 +24301,29 @@ init_district_images () if (! pcx_loaded) continue; - Sprite_construct (&is->wonder_district_img_sets[wi].img); - int x = 128 * is->wonder_district_configs[wi].img_column; - int y = 64 * is->wonder_district_configs[wi].img_row; - Sprite_slice_pcx (&is->wonder_district_img_sets[wi].img, __, &wpcx, x, y, 128, 64, 1, 1); + struct wonder_district_image_set * set = &is->wonder_district_img_sets[wi]; - Sprite_construct (&is->wonder_district_img_sets[wi].construct_img); - int cx = 128 * is->wonder_district_configs[wi].img_construct_column; - int cy = 64 * is->wonder_district_configs[wi].img_construct_row; - Sprite_slice_pcx (&is->wonder_district_img_sets[wi].construct_img, __, &wpcx, cx, cy, 128, 64, 1, 1); + Sprite_construct (&set->img); + int x = 128 * cfg->img_column; + int y = 64 * cfg->img_row; + Sprite_slice_pcx (&set->img, __, &wpcx, x, y, 128, 64, 1, 1); + + Sprite_construct (&set->construct_img); + int cx = 128 * cfg->img_construct_column; + int cy = 64 * cfg->img_construct_row; + Sprite_slice_pcx (&set->construct_img, __, &wpcx, cx, cy, 128, 64, 1, 1); + + if (cfg->enable_img_alt_dir) { + Sprite_construct (&set->alt_dir_img); + int ax = 128 * cfg->img_alt_dir_column; + int ay = 64 * cfg->img_alt_dir_row; + Sprite_slice_pcx (&set->alt_dir_img, __, &wpcx, ax, ay, 128, 64, 1, 1); + + Sprite_construct (&set->alt_dir_construct_img); + int acx = 128 * cfg->img_alt_dir_construct_column; + int acy = 64 * cfg->img_alt_dir_construct_row; + Sprite_slice_pcx (&set->alt_dir_construct_img, __, &wpcx, acx, acy, 128, 64, 1, 1); + } } if (pcx_loaded) @@ -24553,6 +24693,61 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, else if (direct_diagonal && *out_variant == NW && anchor == DIR_SE) { *out_pixel_x +=6; *out_pixel_y += 6; } } +bool +wonder_should_use_alt_dir_image (int tile_x, int tile_y, int owner_id) +{ + if (owner_id <= 0) + return false; + + // We only care about the nearest same-civ city in the work area around the tile. + // Assumes the base wonder art (img_row/column) faces west and the alt art faces east. + // To "face away" from the nearest city, we pick the alt art when that city lies to the west. + Tile * center = tile_at (tile_x, tile_y); + if ((center == NULL) || (center == p_null_tile)) + return false; + + Map * map = &p_bic_data->Map; + int best_dist = INT_MAX; + int best_dx = 0; + + FOR_CITIES_AROUND (wai, tile_x, tile_y) { + City * city = wai.city; + if ((city == NULL) || (city->Body.CivID != owner_id)) + continue; + + int dx = city->Body.X - tile_x; + int dy = city->Body.Y - tile_y; + + if (map->Flags & 1) { + int half_width = map->Width >> 1; + if (dx > half_width) + dx -= map->Width; + else if (dx < -half_width) + dx += map->Width; + } + if (map->Flags & 2) { + int half_height = map->Height >> 1; + if (dy > half_height) + dy -= map->Height; + else if (dy < -half_height) + dy += map->Height; + } + + int dist = int_abs (dx) + int_abs (dy); + // Pick the closest city; if tied, favor the one with a clearer east/west offset so we know which way to face. + if ((dist < best_dist) || + ((dist == best_dist) && ((int_abs (dx) < int_abs (best_dx)) || (best_dx == 0)))) { + best_dist = dist; + best_dx = dx; + } + } + + if ((best_dist == INT_MAX) || (best_dx == 0)) + return false; + + return best_dx < 0; +} + void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { @@ -24659,12 +24854,30 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par return; int construct_windex = -1; + + // Completed wonder if (info->state == WDS_COMPLETED) { - Sprite * wsprite = &is->wonder_district_img_sets[info->wonder_index].img; - patch_Sprite_draw_on_map (wsprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); - return; + int windex = info->wonder_index; + if ((windex < 0) || (windex >= is->wonder_district_count)) + return; + + struct wonder_district_config * wcfg = &is->wonder_district_configs[windex]; + struct wonder_district_image_set * set = &is->wonder_district_img_sets[windex]; + bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alt_dir_image (tile_x, tile_y, territory_owner_id); + Sprite * wsprite = (use_alt_dir && (set->alt_dir_img.vtable != NULL)) ? &set->alt_dir_img : &set->img; + + patch_Sprite_draw_on_map (wsprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + return; + + // Under construction } else if (wonder_district_tile_under_construction (tile, tile_x, tile_y, &construct_windex) && (construct_windex >= 0)) { - Sprite * csprite = &is->wonder_district_img_sets[construct_windex].construct_img; + if (construct_windex >= is->wonder_district_count) + return; + + struct wonder_district_config * wcfg = &is->wonder_district_configs[construct_windex]; + struct wonder_district_image_set * set = &is->wonder_district_img_sets[construct_windex]; + bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alt_dir_image (tile_x, tile_y, territory_owner_id); + Sprite * csprite = (use_alt_dir && (set->alt_dir_construct_img.vtable != NULL)) ? &set->alt_dir_construct_img : &set->construct_img; patch_Sprite_draw_on_map (csprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); return; } From 35833a743b6f0ed95e52e46be2cc35493520c968 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 19:38:23 -0800 Subject: [PATCH 085/356] Ensure AI workers dont unnecessarily remove forest & swamp/jungle if district buildable on those --- injected_code.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/injected_code.c b/injected_code.c index 55a23f3f..05dea412 100644 --- a/injected_code.c +++ b/injected_code.c @@ -25068,6 +25068,7 @@ ai_move_district_worker (Unit * worker, struct district_worker_record * rec) enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); unsigned int removable_flags = overlay_flags & 0xfc; + bool district_buildable_here = district_is_buildable_on_square_type (&is->district_configs[req->district_id], tile); // Remove any existing improvements tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); @@ -25092,11 +25093,11 @@ ai_move_district_worker (Unit * worker, struct district_worker_record * rec) (*p_OutputDebugStringA) (ss); // Clear any forest/wetlands - if (base_type == SQ_Forest) { + if (! district_buildable_here && base_type == SQ_Forest) { Unit_set_state(worker, __, UnitState_Clear_Forest); worker->Body.Job_ID = WJ_Clean_Forest; return true; - } else if ((base_type == SQ_Jungle) || (base_type == SQ_Swamp)) { + } else if (! district_buildable_here && ((base_type == SQ_Jungle) || (base_type == SQ_Swamp))) { Unit_set_state(worker, __, UnitState_Clear_Wetlands); worker->Body.Job_ID = WJ_Clear_Swamp; return true; From bfc1bc6737d3fa8e76af15cc18d7db4fedb483e0 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 19:56:10 -0800 Subject: [PATCH 086/356] Add FOR_TILE_RINGS_AROUND macro and refactor --- Art/Districts/1200/Wonders_2.PCX | Bin 36811 -> 36843 bytes injected_code.c | 413 ++++++++++++++++++------------- 2 files changed, 237 insertions(+), 176 deletions(-) diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index 6348598429812afd75006b17377658f6ffd0ad31..eadc78011b691e91b1bf86121c7d8d32cd06568d 100644 GIT binary patch delta 546 zcmWkrOH30{6iu7?P$?y93{6QfsU=p#5{(vR(uL91G)+cEiN(@VDjNJ@BsIp;5A;Jn z<3@8fx)658#DzxL>wBXcU8%!D7o-7|MB}2twa=N|x#ymH@45GWT(SOMvA!J1TI`Ur zXLzcw`Z`h1CTtR67$Pk1Y4~N_e)Iu#AE)&2nkV&f;$AlJ#K*-%3|Z zH$|K(U*?W$%!lXYY4Ij$8}4^vPi2z7PMJf(y-Hkk1}PgDR`9hl&Id(jmyAGlOrWk2 z%DVBOIw3?!n=&uj)r0}JDI3K3r5K-jYR-GH6z63_IRr;7Zi(Vr?V`;P5$5P8IzH57 zF-5k!jSze5V}e&HP-C@h{eoc7lm)R?j|pJbn~bf8aO18mJ?5!Zow5JTVw-onjxD)-bD`DiGBhds(Xj= zz82XeKgcHbs}XUyuWUGzkx}<`7%9C-w-h{9LxN0x(l2XhslCEyR`i7f_@^rTCq}<} KUZ1<4<^BT#hWs}G delta 514 zcmW-cO=}ZT6o#3bdnZNh6cr>)nl9P~q>EP3x=08(XiyPs3#HLCtxap~LL=6R$Rtgg zG?T9>Zah#|lEt7XxTw@0(0hl7AYBzdK>P(tL5eFE$GNV~%Q^2k&wGA$$fpk3j#4Qulv4<>@+#xp!wXO-Km4zB zI4GO+zK4UbyuQjzMfj(_uP?E!fx*jJxKx>ABYgv50WT{VB@d*Uqf^4in$<;8HQ;M? znGVYs7dP(Eilo2ZSPI(w(9f^IiQ2qOqUrFsmJaG$0>wxIzSM3qHy9GVaL%4q^@*57 zKDB36m;aXXz|N=!Ka_H^o?`YZfn)Hto@T;##^7ipt+;TvajVD2_>`y)9~(C$vyc1y zNOM|^g-UY!NpnW+$U_sb-%P2AJdyinTC=L&pOl7Ytx;+S%)y7&s1kxdt#Ou%a5P}7 zeU=QTV6lC^$A2Vha-4w|?FciYa=P12(rgz^(3}YAB8)rN0$qFqb!W)0Mc8(dB=HUY VI^n=)OhEtURA3+DJC8Ok`VVI(>Gl8s diff --git a/injected_code.c b/injected_code.c index 05dea412..fc5a0420 100644 --- a/injected_code.c +++ b/injected_code.c @@ -3364,6 +3364,77 @@ wai_init_cities (int x, int y) #define FOR_DISTRICTS_AROUND(wai_name, _x, _y, _completed_only) for (struct work_area_iter wai_name = wai_init_districts (_x, _y, _completed_only); (wai_name.n < wai_name.num_tiles); wai_next (&wai_name)) #define FOR_CITIES_AROUND(wai_name, _x, _y) for (struct work_area_iter wai_name = wai_init_cities (_x, _y); (wai_name.n < wai_name.num_tiles); wai_next (&wai_name)) +struct tile_rings_iter { + int center_x, center_y; + int const * rings; + int ring_count; + int r_idx; + int ni; + int tile_x, tile_y; + int current_ring; + Tile * tile; +}; + +void +tri_next (struct tile_rings_iter * tri) +{ + tri->tile = p_null_tile; + while (tri->r_idx < tri->ring_count) { + int ring_no = tri->rings[tri->r_idx]; + if ((ring_no <= 0) || (ring_no >= 8)) { + tri->r_idx += 1; + tri->ni = -1; + continue; + } + int start_ni = workable_tile_counts[ring_no - 1]; + int end_ni = workable_tile_counts[ring_no]; + if ((tri->ni + 1) < end_ni) + tri->ni += 1; + else { + tri->r_idx += 1; + tri->ni = -1; + continue; + } + tri->current_ring = ring_no; + int dx, dy; + patch_ni_to_diff_for_work_area (tri->ni, &dx, &dy); + int tx = tri->center_x + dx; + int ty = tri->center_y + dy; + wrap_tile_coords (&p_bic_data->Map, &tx, &ty); + tri->tile_x = tx; + tri->tile_y = ty; + Tile * candidate = tile_at (tx, ty); + if ((candidate == NULL) || (candidate == p_null_tile)) + continue; + tri->tile = candidate; + break; + } + if (tri->tile == p_null_tile) { + tri->r_idx = tri->ring_count; + tri->ni = -1; + } +} + +struct tile_rings_iter +tri_init (int x, int y, int const * rings, int ring_count) +{ + struct tile_rings_iter tr; + tr.center_x = x; + tr.center_y = y; + tr.rings = rings; + tr.ring_count = ring_count; + tr.r_idx = 0; + tr.ni = -1; + tr.tile_x = 0; + tr.tile_y = 0; + tr.current_ring = 0; + tr.tile = p_null_tile; + tri_next (&tr); + return tr; +} + +#define FOR_TILE_RINGS_AROUND(tri_name, _x, _y, _rings, _ring_count) for (struct tile_rings_iter tri_name = tri_init (_x, _y, _rings, _ring_count); (tri_name.r_idx < tri_name.ring_count); tri_next (&tri_name)) + void tai_get_coords (struct tiles_around_iter * tai, int * out_x, int * out_y) { @@ -8083,46 +8154,48 @@ find_tile_for_neighborhood_district (City * city, int * out_x, int * out_y) for (int r = 1; r <= city_work_radius; r++) ring_order[ring_count++] = r; - for (int r_idx = 0; r_idx < ring_count; r_idx++) { - int ring = ring_order[r_idx]; - int start_ni = workable_tile_counts[ring - 1]; - int end_ni = workable_tile_counts[ring]; - - Tile * best_tile = NULL; - int best_yield = INT_MAX; - - for (int ni = start_ni; ni < end_ni; ni++) { - int dx, dy; - patch_ni_to_diff_for_work_area (ni, &dx, &dy); - int tx = city_x + dx; - int ty = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &tx, &ty); - Tile * tile = tile_at (tx, ty); + Tile * best_tile = NULL; + int best_yield = INT_MAX; + int best_x = -1, best_y = -1; + int current_ring = -1; - if ((tile == NULL) || (tile == p_null_tile)) - continue; - if (is->current_config.enable_distribution_hub_districts) { - int covered = itable_look_up_or (&is->distribution_hub_coverage_counts, (int)tile, 0); - if (covered > 0) - continue; + FOR_TILE_RINGS_AROUND (tri, city_x, city_y, ring_order, ring_count) { + if (tri.current_ring != current_ring) { + if (best_tile != NULL) { + *out_x = best_x; + *out_y = best_y; + return best_tile; } + current_ring = tri.current_ring; + best_tile = NULL; + best_yield = INT_MAX; + } - if (! tile_suitable_for_district (tile, NEIGHBORHOOD_DISTRICT_ID, city, NULL)) - continue; - if (get_district_instance (tile) != NULL) + Tile * tile = tri.tile; + if (is->current_config.enable_distribution_hub_districts) { + int covered = itable_look_up_or (&is->distribution_hub_coverage_counts, (int)tile, 0); + if (covered > 0) continue; + } - int yield = compute_city_tile_yield_sum (city, tx, ty); - if (yield < best_yield) { - best_yield = yield; - best_tile = tile; - *out_x = tx; - *out_y = ty; - } + if (! tile_suitable_for_district (tile, NEIGHBORHOOD_DISTRICT_ID, city, NULL)) + continue; + if (get_district_instance (tile) != NULL) + continue; + + int yield = compute_city_tile_yield_sum (city, tri.tile_x, tri.tile_y); + if (yield < best_yield) { + best_yield = yield; + best_tile = tile; + best_x = tri.tile_x; + best_y = tri.tile_y; } + } - if (best_tile != NULL) - return best_tile; + if (best_tile != NULL) { + *out_x = best_x; + *out_y = best_y; + return best_tile; } return NULL; @@ -8145,55 +8218,57 @@ find_tile_for_port_district (City * city, int * out_x, int * out_y) for (int r = 1; r <= city_work_radius; r++) ring_order[ring_count++] = r; - for (int r_idx = 0; r_idx < ring_count; r_idx++) { - int ring = ring_order[r_idx]; - int start_ni = workable_tile_counts[ring - 1]; - int end_ni = workable_tile_counts[ring]; - - Tile * best_tile = NULL; - int best_yield = INT_MAX; - - for (int ni = start_ni; ni < end_ni; ni++) { - int dx, dy; - patch_ni_to_diff_for_work_area (ni, &dx, &dy); - int tx = city_x + dx; - int ty = city_y + dy; - wrap_tile_coords (&p_bic_data->Map, &tx, &ty); - Tile * tile = tile_at (tx, ty); + Tile * best_tile = NULL; + int best_yield = INT_MAX; + int best_x = -1, best_y = -1; + int current_ring = -1; - if ((tile == NULL) || (tile == p_null_tile)) - continue; - if (is->current_config.enable_distribution_hub_districts) { - int covered = itable_look_up_or (&is->distribution_hub_coverage_counts, (int)tile, 0); - if (covered > 0) - continue; + FOR_TILE_RINGS_AROUND (tri, city_x, city_y, ring_order, ring_count) { + if (tri.current_ring != current_ring) { + if (best_tile != NULL) { + *out_x = best_x; + *out_y = best_y; + return best_tile; } + current_ring = tri.current_ring; + best_tile = NULL; + best_yield = INT_MAX; + } - if (! tile_suitable_for_district (tile, PORT_DISTRICT_ID, city, NULL)) - continue; - if (get_district_instance (tile) != NULL) - continue; - if (! tile->vtable->m35_Check_Is_Water (tile)) + Tile * tile = tri.tile; + if (is->current_config.enable_distribution_hub_districts) { + int covered = itable_look_up_or (&is->distribution_hub_coverage_counts, (int)tile, 0); + if (covered > 0) continue; + } - int continent_id = tile->vtable->m46_Get_ContinentID (tile); - if ((continent_id < 0) || (continent_id >= p_bic_data->Map.Continent_Count)) - continue; - Continent * continent = &p_bic_data->Map.Continents[continent_id]; - if (continent->Body.TileCount < threshold_for_body_of_water) - continue; + if (! tile_suitable_for_district (tile, PORT_DISTRICT_ID, city, NULL)) + continue; + if (get_district_instance (tile) != NULL) + continue; + if (! tile->vtable->m35_Check_Is_Water (tile)) + continue; - int yield = compute_city_tile_yield_sum (city, tx, ty); - if (yield < best_yield) { - best_yield = yield; - best_tile = tile; - *out_x = tx; - *out_y = ty; - } + int continent_id = tile->vtable->m46_Get_ContinentID (tile); + if ((continent_id < 0) || (continent_id >= p_bic_data->Map.Continent_Count)) + continue; + Continent * continent = &p_bic_data->Map.Continents[continent_id]; + if (continent->Body.TileCount < threshold_for_body_of_water) + continue; + + int yield = compute_city_tile_yield_sum (city, tri.tile_x, tri.tile_y); + if (yield < best_yield) { + best_yield = yield; + best_tile = tile; + best_x = tri.tile_x; + best_y = tri.tile_y; } + } - if (best_tile != NULL) - return best_tile; + if (best_tile != NULL) { + *out_x = best_x; + *out_y = best_y; + return best_tile; } return NULL; @@ -8235,40 +8310,42 @@ find_tile_for_wonder_district (City * city, int * out_x, int * out_y) ring_order[ring_count++] = r; ring_order[ring_count++] = 1; - for (int r_idx = 0; r_idx < ring_count; r_idx++) { - int ring = ring_order[r_idx]; - int start_ni = workable_tile_counts[ring - 1]; - int end_ni = workable_tile_counts[ring]; - - Tile * best_tile = NULL; - int best_yield = INT_MAX; - - for (int ni = start_ni; ni < end_ni; ni++) { - int dx, dy; - patch_ni_to_diff_for_work_area (ni, &dx, &dy); - int tx = (city_x + dx) & 0xFF; - int ty = (city_y + dy) & 0xFF; - Tile * tile = tile_at (tx, ty); + Tile * best_tile = NULL; + int best_yield = INT_MAX; + int best_x = -1, best_y = -1; + int current_ring = -1; - if ((tile == NULL) || (tile == p_null_tile)) - continue; + FOR_TILE_RINGS_AROUND (tri, city_x, city_y, ring_order, ring_count) { + if (tri.current_ring != current_ring) { + if (best_tile != NULL) { + *out_x = best_x; + *out_y = best_y; + return best_tile; + } + current_ring = tri.current_ring; + best_tile = NULL; + best_yield = INT_MAX; + } - if (! tile_suitable_for_district (tile, WONDER_DISTRICT_ID, city, NULL)) - continue; - if (! wonder_is_buildable_on_tile (tile, target_improv_id)) - continue; + Tile * tile = tri.tile; + if (! tile_suitable_for_district (tile, WONDER_DISTRICT_ID, city, NULL)) + continue; + if (! wonder_is_buildable_on_tile (tile, target_improv_id)) + continue; - int yield = compute_city_tile_yield_sum (city, tx, ty); - if (yield < best_yield && ! is_tile_earmarked_for_district (tx, ty)) { - best_yield = yield; - best_tile = tile; - *out_x = tx; - *out_y = ty; - } + int yield = compute_city_tile_yield_sum (city, tri.tile_x, tri.tile_y); + if (yield < best_yield && (! is_tile_earmarked_for_district (tri.tile_x, tri.tile_y))) { + best_yield = yield; + best_tile = tile; + best_x = tri.tile_x; + best_y = tri.tile_y; } + } - if (best_tile != NULL) - return best_tile; + if (best_tile != NULL) { + *out_x = best_x; + *out_y = best_y; + return best_tile; } return NULL; @@ -8420,45 +8497,48 @@ find_tile_for_district (City * city, int district_id, int * out_x, int * out_y) ring_order[ring_count++] = r; ring_order[ring_count++] = 1; - for (int r_idx = 0; r_idx < ring_count; r_idx++) { - int ring = ring_order[r_idx]; - int start_ni = workable_tile_counts[ring - 1]; - int end_ni = workable_tile_counts[ring]; - - Tile * best_tile = NULL; - int best_yield = INT_MAX; - - for (int ni = start_ni; ni < end_ni; ni++) { - int dx, dy; - patch_ni_to_diff_for_work_area (ni, &dx, &dy); - int tx = (city_x + dx) & 0xFF; - int ty = (city_y + dy) & 0xFF; - Tile * tile = tile_at (tx, ty); + Tile * best_tile = NULL; + int best_yield = INT_MAX; + int best_x = -1, best_y = -1; + int current_ring = -1; - if ((tile == NULL) || (tile == p_null_tile)) - continue; - if (is->current_config.enable_distribution_hub_districts) { - int covered = itable_look_up_or (&is->distribution_hub_coverage_counts, (int)tile, 0); - if (covered > 0) - continue; + FOR_TILE_RINGS_AROUND (tri, city_x, city_y, ring_order, ring_count) { + if (tri.current_ring != current_ring) { + if (best_tile != NULL) { + *out_x = best_x; + *out_y = best_y; + return best_tile; } + current_ring = tri.current_ring; + best_tile = NULL; + best_yield = INT_MAX; + } - if (! tile_suitable_for_district (tile, district_id, city, NULL)) - continue; - if (get_district_instance (tile) != NULL) + Tile * tile = tri.tile; + if (is->current_config.enable_distribution_hub_districts) { + int covered = itable_look_up_or (&is->distribution_hub_coverage_counts, (int)tile, 0); + if (covered > 0) continue; + } - int yield = compute_city_tile_yield_sum (city, tx, ty); - if (yield < best_yield && ! is_tile_earmarked_for_district (tx, ty)) { - best_yield = yield; - best_tile = tile; - *out_x = tx; - *out_y = ty; - } + if (! tile_suitable_for_district (tile, district_id, city, NULL)) + continue; + if (get_district_instance (tile) != NULL) + continue; + + int yield = compute_city_tile_yield_sum (city, tri.tile_x, tri.tile_y); + if (yield < best_yield && (! is_tile_earmarked_for_district (tri.tile_x, tri.tile_y))) { + best_yield = yield; + best_tile = tile; + best_x = tri.tile_x; + best_y = tri.tile_y; } + } - if (best_tile != NULL) - return best_tile; + if (best_tile != NULL) { + *out_x = best_x; + *out_y = best_y; + return best_tile; } return NULL; @@ -24446,50 +24526,31 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, City * closest_city = NULL; int closest_dx = 0, closest_dy = 0; - int city_work_radius = is->current_config.city_work_radius; - - for (int ring = 1; (ring <= city_work_radius) && (closest_city == NULL); ring++) { - int start_ni = workable_tile_counts[ring - 1]; - int end_ni = workable_tile_counts[ring]; - - for (int ni = start_ni; ni < end_ni; ni++) { - int dx, dy; - patch_ni_to_diff_for_work_area (ni, &dx, &dy); - int tx = tile_x + dx; - int ty = tile_y + dy; - wrap_tile_coords (map, &tx, &ty); - - Tile * t = tile_at (tx, ty); - if ((t == NULL) || (t == p_null_tile)) - continue; - int city_id = t->vtable->m45_Get_City_ID (t); - City * city = get_city_ptr (city_id); - if ((city != NULL) && (city->Body.CivID == owner_id)) { - int ndx = city->Body.X - tile_x; - int ndy = city->Body.Y - tile_y; - - if (map->Flags & 1) { - int half_width = map->Width / 2; - if (ndx > half_width) - ndx -= map->Width; - else if (ndx < -half_width) - ndx += map->Width; - } - if (map->Flags & 2) { - int half_height = map->Height / 2; - if (ndy > half_height) - ndy -= map->Height; - else if (ndy < -half_height) - ndy += map->Height; - } + FOR_CITIES_AROUND (wai, tile_x, tile_y) { + City * city = wai.city; + int ndx = city->Body.X - tile_x; + int ndy = city->Body.Y - tile_y; - closest_city = city; - closest_dx = ndx; - closest_dy = ndy; - break; - } + if (map->Flags & 1) { + int half_width = map->Width / 2; + if (ndx > half_width) + ndx -= map->Width; + else if (ndx < -half_width) + ndx += map->Width; } + if (map->Flags & 2) { + int half_height = map->Height / 2; + if (ndy > half_height) + ndy -= map->Height; + else if (ndy < -half_height) + ndy += map->Height; + } + + closest_city = city; + closest_dx = ndx; + closest_dy = ndy; + break; } if (closest_city == NULL) From 3c9cac898eaa9842d21488af3bc8aef87d6ebaa1 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 20:11:28 -0800 Subject: [PATCH 087/356] Ensure districts rendered in detailed tile modal --- injected_code.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/injected_code.c b/injected_code.c index fc5a0420..9d8361f0 100644 --- a/injected_code.c +++ b/injected_code.c @@ -24828,6 +24828,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par return; } + PCX_Image * canvas = (PCX_Image *)map_renderer; int district_id = inst->district_type; if (is->dc_img_state == IS_UNINITED) init_district_images (); @@ -24835,12 +24836,6 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par if (is->dc_img_state != IS_OK) return; - /* - if (is->current_config.show_detailed_tile_info) { - tile = tile_at (is->viewing_tile_info_x, is->viewing_tile_info_y); - } - */ - // Natural Wonder if (district_id == NATURAL_WONDER_DISTRICT_ID) { if (! is->current_config.enable_natural_wonders) @@ -24851,7 +24846,12 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par Sprite * nsprite = &is->natural_wonder_img_sets[natural_id].img; int y_offset = 88 - 64; // Height of wonder img minus height of tile int draw_y = pixel_y - y_offset; - patch_Sprite_draw_on_map (nsprite, __, this, pixel_x, draw_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + + if (is->current_config.show_detailed_tile_info) { + Sprite_draw (nsprite, __, canvas, pixel_x, draw_y, NULL); + } else { + patch_Sprite_draw_on_map (nsprite, __, this, pixel_x, draw_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + } } return; } @@ -24885,8 +24885,13 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par Sprite * abandoned_sprite = &is->abandoned_district_img; if (tile->vtable->m35_Check_Is_Water (tile) && is->abandoned_maritime_district_img.vtable != NULL) abandoned_sprite = &is->abandoned_maritime_district_img; - if (abandoned_sprite->vtable != NULL) - patch_Sprite_draw_on_map (abandoned_sprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + if (abandoned_sprite->vtable != NULL) { + if (is->current_config.show_detailed_tile_info) { + Sprite_draw (abandoned_sprite, __, canvas, pixel_x, pixel_y, NULL); + } else { + patch_Sprite_draw_on_map (abandoned_sprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + } + } return; } @@ -24927,7 +24932,11 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alt_dir_image (tile_x, tile_y, territory_owner_id); Sprite * wsprite = (use_alt_dir && (set->alt_dir_img.vtable != NULL)) ? &set->alt_dir_img : &set->img; - patch_Sprite_draw_on_map (wsprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + if (is->current_config.show_detailed_tile_info) { + Sprite_draw (wsprite, __, canvas, pixel_x, pixel_y, NULL); + } else { + patch_Sprite_draw_on_map (wsprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + } return; // Under construction @@ -24939,7 +24948,12 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par struct wonder_district_image_set * set = &is->wonder_district_img_sets[construct_windex]; bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alt_dir_image (tile_x, tile_y, territory_owner_id); Sprite * csprite = (use_alt_dir && (set->alt_dir_construct_img.vtable != NULL)) ? &set->alt_dir_construct_img : &set->construct_img; - patch_Sprite_draw_on_map (csprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + + if (is->current_config.show_detailed_tile_info) { + Sprite_draw (csprite, __, canvas, pixel_x, pixel_y, NULL); + } else { + patch_Sprite_draw_on_map (csprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + } return; } break; @@ -24959,7 +24973,12 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; - patch_Sprite_draw_on_map (district_sprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + + if (is->current_config.show_detailed_tile_info) { + Sprite_draw (district_sprite, __, canvas, pixel_x, pixel_y, NULL); + } else { + patch_Sprite_draw_on_map (district_sprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + } return; } From 88aff8a0629499b7d7b457092dd8eb0df3f46b68 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 20:26:12 -0800 Subject: [PATCH 088/356] Add support for comments in config files --- default.districts_config.txt | 7 +++++++ default.districts_natural_wonders_config.txt | 8 ++++++++ injected_code.c | 14 ++++++++++---- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/default.districts_config.txt b/default.districts_config.txt index 75516b6b..118d511b 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -1,3 +1,10 @@ +[======================================================================= NOTE =======================================================================] +[Instead of editing this file, changes to the settings should be placed in either the scenario or user config files. The scenario config file must ] +[be named scenario.districts_config.txt and must be located in your scenario search folder. Of course it only applies if you are using a scenario. ] +[The user config file must be named user.districts_config.txt and located in the C3X folder, which is the folder where this file is. When creating ] +[scenario or user configs, note that all districts defined here will be removed and only your scenario or user-defined districts will be used ] +[====================================================================================================================================================] + #District name = Encampment tooltip = Build Encampment diff --git a/default.districts_natural_wonders_config.txt b/default.districts_natural_wonders_config.txt index b5e7613a..6f0aa2e2 100644 --- a/default.districts_natural_wonders_config.txt +++ b/default.districts_natural_wonders_config.txt @@ -1,3 +1,11 @@ +[======================================================================= NOTE =======================================================================] +[Instead of editing this file, changes to the settings should be placed in either the scenario or user config files. The scenario config file must ] +[be named scenario.districts_natural_wonders_config.txt and must be located in your scenario search folder. Of course it only applies if you are ] +[using a scenario. The user config file must be named user.districts_natural_wonders_config.txt and located in the C3X folder, which is the folder ] +[where this file is. When creating scenario or user configs, note that all natural wonders defined here will be removed and only your scenario or ] +[user-defined districts will be used. ] +[====================================================================================================================================================] + #Wonder name = Angel Falls terrain_type = grassland diff --git a/injected_code.c b/injected_code.c index 9d8361f0..0459b4de 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5845,6 +5845,12 @@ handle_district_definition_key (struct parsed_district_definition * def, add_unrecognized_key_error (unrecognized_keys, line_number, key); } +bool +line_is_empty_or_comment (struct string_slice const * trimmed) +{ + return ((trimmed == NULL) || (trimmed->len == 0) || (trimmed->str[0] == ';') || (trimmed->str[0] == '[')); +} + void load_dynamic_district_config_file (char const * file_path, int path_is_relative_to_mod_dir, @@ -5898,7 +5904,7 @@ load_dynamic_district_config_file (char const * file_path, struct string_slice line_slice = { .str = line_start, .len = line_len }; struct string_slice trimmed = trim_string_slice (&line_slice, 0); - if ((trimmed.len == 0) || (trimmed.str[0] == ';')) { + if (line_is_empty_or_comment (&trimmed)) { cursor = has_newline ? line_end + 1 : line_end; continue; } @@ -6372,7 +6378,7 @@ load_dynamic_wonder_config_file (char const * file_path, struct string_slice line_slice = { .str = line_start, .len = line_len }; struct string_slice trimmed = trim_string_slice (&line_slice, 0); - if ((trimmed.len == 0) || (trimmed.str[0] == ';')) { + if (line_is_empty_or_comment (&trimmed)) { cursor = has_newline ? line_end + 1 : line_end; continue; } @@ -6824,7 +6830,7 @@ load_natural_wonder_config_file (char const * file_path, struct string_slice line_slice = { .str = line_start, .len = line_len }; struct string_slice trimmed = trim_string_slice (&line_slice, 0); - if ((trimmed.len == 0) || (trimmed.str[0] == ';')) { + if (line_is_empty_or_comment (&trimmed)) { cursor = has_newline ? line_end + 1 : line_end; continue; } @@ -7689,7 +7695,7 @@ load_scenario_districts_from_file () struct string_slice line_slice = { .str = line_start, .len = line_len }; struct string_slice trimmed = trim_string_slice (&line_slice, 0); - if ((trimmed.len == 0) || (trimmed.str[0] == ';')) { + if (line_is_empty_or_comment (&trimmed)) { cursor = has_newline ? line_end + 1 : line_end; continue; } From bba77bb91a34a624bef3352a4eb00472e47b80f4 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 20:32:43 -0800 Subject: [PATCH 089/356] Add comments to one more config file --- default.districts_wonders_config.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/default.districts_wonders_config.txt b/default.districts_wonders_config.txt index d456dc94..87e3a13f 100644 --- a/default.districts_wonders_config.txt +++ b/default.districts_wonders_config.txt @@ -1,3 +1,11 @@ +[======================================================================= NOTE =======================================================================] +[Instead of editing this file, changes to the settings should be placed in either the scenario or user config files. The scenario config file must ] +[be named scenario.districts_wonders_config.txt and must be located in your scenario search folder. Of course it only applies if you are using a ] +[scenario. The user config file must be named user.districts_wonders_config.txt and located in the C3X folder, which is the folder where this file ] +[is. When creating scenario or user configs, note that all wonders defined here will be removed and only your scenario or user-defined districts will] +[be used. ] +[====================================================================================================================================================] + #Wonder name = The Pyramids img_path = Wonders.pcx From 706b1f2d227c8d05d8b17552b43511e07e610bfa Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 21:14:09 -0800 Subject: [PATCH 090/356] Add draft of AI naval attack logic for maritime districts, commented out --- injected_code.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 1 deletion(-) diff --git a/injected_code.c b/injected_code.c index 0459b4de..0f45b0b8 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5848,7 +5848,7 @@ handle_district_definition_key (struct parsed_district_definition * def, bool line_is_empty_or_comment (struct string_slice const * trimmed) { - return ((trimmed == NULL) || (trimmed->len == 0) || (trimmed->str[0] == ';') || (trimmed->str[0] == '[')); + return ((trimmed == NULL) || (trimmed->len == 0) || (trimmed->str[0] == ';')); } void @@ -26059,5 +26059,133 @@ patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool return transport; } +/* +// Returns true if the given tile is a water district owned by an enemy of the unit +bool +is_enemy_maritime_district_tile (Unit * unit, Tile * tile) +{ + if ((unit == NULL) || (tile == NULL) || (tile == p_null_tile)) + return false; + + if (! is->current_config.enable_districts) + return false; + + if (! tile->vtable->m35_Check_Is_Water (tile)) + return false; + + if (! tile->vtable->m18_Check_Mines (tile, __, 0)) + return false; + + struct district_instance * inst = get_district_instance (tile); + if (inst == NULL) + return false; + + int owner = tile->vtable->m38_Get_Territory_OwnerID (tile); + if ((owner <= 0) || (owner == unit->Body.CivID)) + return false; + + return unit->vtable->is_enemy_of_civ (unit, owner, false); +} + +// Boost pillage desirability for maritime districts +int __fastcall +patch_Unit_ai_eval_pillage_target (Unit * this, int edx, unsigned short tile_x, int tile_y) +{ + int base = Unit_ai_eval_pillage_target (this, __, tile_x, tile_y); + + Tile * tile = tile_at (tile_x, tile_y); + if ((base > 0) && is_enemy_maritime_district_tile (this, tile)) { + // Double the base score and add a flat kicker so districts outrank generic coast targets + base += base + 0x300; + } + + return base; +} + +// Light-weight hunt for nearby maritime districts; returns true if a path/command was issued +bool +try_path_to_maritime_district (Unit * unit) +{ + if ((unit == NULL) || (! is->current_config.enable_districts)) + return false; + + if (unit->Body.Container_Unit >= 0) // Don't redirect cargo + return false; + + if (unit->Body.Moves <= 0) // No moves left, let base AI handle healing/other states + return false; + + int sea_id = unit->vtable->get_sea_id (unit); + if (sea_id < 0) + return false; + + int best_score = 0, best_x = -1, best_y = -1, best_path_len = 0; + const int search_radius = 8; + for (int dy = -search_radius; dy <= search_radius; dy++) { + for (int dx = -search_radius; dx <= search_radius; dx++) { + int tx = unit->Body.X + dx, ty = unit->Body.Y + dy; + wrap_tile_coords (&p_bic_data->Map, &tx, &ty); + + Tile * tile = tile_at (tx, ty); + if ((tile == NULL) || (tile == p_null_tile)) + continue; + + if ((short)tile->vtable->m46_Get_ContinentID (tile) != sea_id) + continue; + + if (! is_enemy_maritime_district_tile (unit, tile)) + continue; + + int score = patch_Unit_ai_eval_pillage_target (unit, __, (unsigned short)tx, ty); + if (score <= 0) + continue; + + int path_len = 0; + int path_result = patch_Trade_Net_set_unit_path (is->trade_net, __, unit->Body.X, unit->Body.Y, tx, ty, unit, unit->Body.CivID, 0x81, &path_len); + if (path_result <= 0) + continue; + + if ((score > best_score) || ((score == best_score) && ((best_path_len == 0) || (path_len < best_path_len)))) { + best_score = score; + best_path_len = path_len; + best_x = tx; + best_y = ty; + } + } + } + + if (best_x >= 0) { + patch_Trade_Net_set_unit_path (is->trade_net, __, unit->Body.X, unit->Body.Y, best_x, best_y, unit, unit->Body.CivID, 0x81, NULL); + Unit_set_escortee (unit, __, -1); + Unit_set_state (unit, __, UnitState_Go_To); + unit->Body.path_dest_x = best_x; + unit->Body.path_dest_y = best_y; + return true; + } + + return false; +} + +void __fastcall +patch_Unit_ai_move_naval_power_unit (Unit * this) +{ + // If we're already sitting on an enemy maritime district, pillage it immediately + if (this->Body.Container_Unit < 0) { + Tile * here = tile_at (this->Body.X, this->Body.Y); + if (is_enemy_maritime_district_tile (this, here) && + can_pillage (this, (unsigned short)this->Body.X, this->Body.Y) && + (patch_Unit_ai_eval_pillage_target (this, __, (unsigned short)this->Body.X, this->Body.Y) > 0)) { + attack_tile (this, this->Body.X, this->Body.Y, 0); + return; + } + } + + if (try_path_to_maritime_district (this)) + return; + + Unit_ai_move_naval_power_unit (this); +} +*/ + // TCC requires a main function be defined even though it's never used. int main () { return 0; } From 23c93bd2300dff1d4d44fb30383e4a6a0f974b9f Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 14 Dec 2025 22:33:05 -0800 Subject: [PATCH 091/356] Refactor draw_buildings for simpler map vs canvas rendering --- injected_code.c | 49 +++++++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/injected_code.c b/injected_code.c index 0f45b0b8..16924453 100644 --- a/injected_code.c +++ b/injected_code.c @@ -24761,7 +24761,7 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, } bool -wonder_should_use_alt_dir_image (int tile_x, int tile_y, int owner_id) +wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner_id) { if (owner_id <= 0) return false; @@ -24815,6 +24815,17 @@ wonder_should_use_alt_dir_image (int tile_x, int tile_y, int owner_id) return best_dx < 0; } +void +draw_district_on_map_or_canvas(Sprite * sprite, Map_Renderer * map_renderer, int pixel_x, int pixel_y) +{ + if (is->current_config.show_detailed_tile_info) { + PCX_Image * canvas = (PCX_Image *)map_renderer; + Sprite_draw (sprite, __, canvas, pixel_x, pixel_y, NULL); + } else { + patch_Sprite_draw_on_map (sprite, __, map_renderer, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + } +} + void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { @@ -24834,7 +24845,6 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par return; } - PCX_Image * canvas = (PCX_Image *)map_renderer; int district_id = inst->district_type; if (is->dc_img_state == IS_UNINITED) init_district_images (); @@ -24853,11 +24863,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par int y_offset = 88 - 64; // Height of wonder img minus height of tile int draw_y = pixel_y - y_offset; - if (is->current_config.show_detailed_tile_info) { - Sprite_draw (nsprite, __, canvas, pixel_x, draw_y, NULL); - } else { - patch_Sprite_draw_on_map (nsprite, __, this, pixel_x, draw_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); - } + draw_district_on_map_or_canvas(nsprite, map_renderer, pixel_x, draw_y); } return; } @@ -24892,11 +24898,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par if (tile->vtable->m35_Check_Is_Water (tile) && is->abandoned_maritime_district_img.vtable != NULL) abandoned_sprite = &is->abandoned_maritime_district_img; if (abandoned_sprite->vtable != NULL) { - if (is->current_config.show_detailed_tile_info) { - Sprite_draw (abandoned_sprite, __, canvas, pixel_x, pixel_y, NULL); - } else { - patch_Sprite_draw_on_map (abandoned_sprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); - } + draw_district_on_map_or_canvas(abandoned_sprite, map_renderer, pixel_x, pixel_y); } return; } @@ -24935,14 +24937,10 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par struct wonder_district_config * wcfg = &is->wonder_district_configs[windex]; struct wonder_district_image_set * set = &is->wonder_district_img_sets[windex]; - bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alt_dir_image (tile_x, tile_y, territory_owner_id); + bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alternative_direction_image (tile_x, tile_y, territory_owner_id); Sprite * wsprite = (use_alt_dir && (set->alt_dir_img.vtable != NULL)) ? &set->alt_dir_img : &set->img; - if (is->current_config.show_detailed_tile_info) { - Sprite_draw (wsprite, __, canvas, pixel_x, pixel_y, NULL); - } else { - patch_Sprite_draw_on_map (wsprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); - } + draw_district_on_map_or_canvas(wsprite, map_renderer, pixel_x, pixel_y); return; // Under construction @@ -24952,14 +24950,10 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par struct wonder_district_config * wcfg = &is->wonder_district_configs[construct_windex]; struct wonder_district_image_set * set = &is->wonder_district_img_sets[construct_windex]; - bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alt_dir_image (tile_x, tile_y, territory_owner_id); + bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alternative_direction_image (tile_x, tile_y, territory_owner_id); Sprite * csprite = (use_alt_dir && (set->alt_dir_construct_img.vtable != NULL)) ? &set->alt_dir_construct_img : &set->construct_img; - if (is->current_config.show_detailed_tile_info) { - Sprite_draw (csprite, __, canvas, pixel_x, pixel_y, NULL); - } else { - patch_Sprite_draw_on_map (csprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); - } + draw_district_on_map_or_canvas(csprite, map_renderer, pixel_x, pixel_y); return; } break; @@ -24979,12 +24973,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; - - if (is->current_config.show_detailed_tile_info) { - Sprite_draw (district_sprite, __, canvas, pixel_x, pixel_y, NULL); - } else { - patch_Sprite_draw_on_map (district_sprite, __, this, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); - } + draw_district_on_map_or_canvas(district_sprite, map_renderer, pixel_x, pixel_y); return; } From 9fa7c15f01e4f4078838d6a944bd8d97eb6267bf Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 15 Dec 2025 06:11:52 -0800 Subject: [PATCH 092/356] Add Wadi Rum natural wonder --- Art/Districts/1200/NaturalWonders.pcx | Bin 64588 -> 70054 bytes default.districts_natural_wonders_config.txt | 14 ++++++++++++++ 2 files changed, 14 insertions(+) diff --git a/Art/Districts/1200/NaturalWonders.pcx b/Art/Districts/1200/NaturalWonders.pcx index 9aa359bd0e782cd14a54c109d4ad76bdf41eb2b8..1fbd3716eb15fbe62f22bc7fc26af7f2ba5b664b 100644 GIT binary patch delta 6414 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 K(fK#~$Nm@PC3oZi delta 756 zcmXw$KTH#06u|RwT+TsbB1sn$qGBA>#29tKXf#n1gD!>v}_CKzrv+<|dqaf0vm9|27|fxX+yh8#Y)R#3w58 z6NBcDPc55)%b#9Z7KMXd|E<*3zCDX|J1m=m@y{I^$*>$C0^b)0`?wt9om^jg*+->^ z_X_s(SnlUtjNX3sfE=Sdr24$p;Tt zBrQY8wW=27A}zsz!K|8>E3^O&Lq#OZj`-Nt31SyP;kAIxDmg$2|G4D{@h2V;oDQV`KD^(0m*z z8pabwjnF(C8FJ&+>=nWPIZuP=ieDFeSw(y~ciR-gM@ef!c;k27zoYYu*M#ckck^ib zQ)A9D=vzK#N(%3#E|{`}Vy)Vgd3>HeVoCyS*+Zrn*s{LYlnBOhHdDfQcw>(#L1^5} zFaz+qkYehf7Z;drNS3D6b^fFavRfe~FC~=Zh_WdQkfQ;mBy%s~qEC@BZP$h9P)QEU m6? Date: Mon, 15 Dec 2025 07:25:27 -0800 Subject: [PATCH 093/356] =?UTF-8?q?Add=20Eyjafjallaj=C3=B6kull=20natural?= =?UTF-8?q?=20wonder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Art/Districts/1200/NaturalWonders.pcx | Bin 70054 -> 74795 bytes default.districts_natural_wonders_config.txt | 14 ++++++++++++++ 2 files changed, 14 insertions(+) diff --git a/Art/Districts/1200/NaturalWonders.pcx b/Art/Districts/1200/NaturalWonders.pcx index 1fbd3716eb15fbe62f22bc7fc26af7f2ba5b664b..12f493abe15e6b242bf8715a4e7f49960a41bf24 100644 GIT binary patch delta 6559 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 L`A?mVEA{^aFgmY) delta 1478 zcmaKsK}!Nb6vr7VgrGW<1$7ANL-<8H^f9`2+CD)P9roBE=+ZUlY*Cg{Vv$zJLF1U2tcdJp7#f?dG7kyr1om>2&&uXkmRfPUj<4RS0`cnix zPG27AviNcRC6$~2^7wMs!(joRE*4di+b`jr)8h2xmd?Yg!CRgXsNmUPNs4qno(vXw zdZ30!gN0SOqHN&a;1xpyG*LE~mrJ@9ZVX;BY(U%HnODi>GY-4R89ZYQfu7r+RSRkG zcf!j!Gm Date: Mon, 15 Dec 2025 08:01:30 -0800 Subject: [PATCH 094/356] Add Ha Long Bay natural wonder --- Art/Districts/1200/NaturalWonders.pcx | Bin 74795 -> 78270 bytes default.districts_natural_wonders_config.txt | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Art/Districts/1200/NaturalWonders.pcx b/Art/Districts/1200/NaturalWonders.pcx index 12f493abe15e6b242bf8715a4e7f49960a41bf24..d29d5087f90e3f397a5c37b0035ddcc3442a4773 100644 GIT binary patch delta 4383 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 delta 870 zcmZ9~O-NKx6bEn)&Q)jp%q%M{O*0!mBQZmWjA&PpMFn9+WJXcB$u?yQX`9*w{#zTg zanmAb6VZDbYND1y8V;#AHX|Ae^J;X)Jh$T;&}?`NzjM#Mhj;Ef?|qKWUPsFtqWEUL z_G2B=rr#Ve{f5;W>9!32T1xOo|I%hh*MMmaMvVV*%i7W%hF>0I!0>;Oyt(0QSO;~7 zl(kEHBBr%oI7aIw48>Ds!unbWE+^&uv+x@yof~#ocqR+<6Ca#ZMl(op(Sf)h{7GwG z#9bFv#`$i9q zg&EVSbWVj?>n=2Hq6;b{>_KK4U5*P1`*jH2X;d3KFTs#T!)CgrX2l*9rqfLo5RPIx zo$e@~(2JK_s7Xx(@S=z(AklTj?7bGyCA!u=ieP*RIHo=I(j zW#nej6M@E37CpAnx6`h(jP_mjsYTq#rWQ$AsK~LgfJhEKl9f5U-fiy*pf1-wWxqx` z_UBQ(tgXXm|W+e3{G=|ZC_pYDk~8Z-ISs8$;eqJ1yj7Oq~zp#o`Mu17~OTp-QM zrD;aPKDw@?VeW##+71Jp-C7eKaF`ZH4LLbUX7PKg`U= Sin%(~9FC6Q+tyO5iuns5HXwKa diff --git a/default.districts_natural_wonders_config.txt b/default.districts_natural_wonders_config.txt index 2215cd41..8bad4a2f 100644 --- a/default.districts_natural_wonders_config.txt +++ b/default.districts_natural_wonders_config.txt @@ -174,7 +174,7 @@ shield_bonus = 0 happiness_bonus = 1 #Wonder -name = Eyjafjallajökull +name = Eyjafjallajokull terrain_type = tundra adjacent_to = tundra img_path = NaturalWonders.pcx @@ -185,4 +185,18 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happiness_bonus = 1 + +#Wonder +name = Ha Long Bay +terrain_type = sea +adjacent_to = coast +img_path = NaturalWonders.pcx +img_row = 3 +img_column = 1 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 happiness_bonus = 1 \ No newline at end of file From 64fdafbccba84d7f58feff8196f0985af8857b0b Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 15 Dec 2025 09:07:10 -0800 Subject: [PATCH 095/356] Update Ha Long Bay art --- Art/Districts/1200/NaturalWonders.pcx | Bin 78270 -> 77560 bytes default.districts_natural_wonders_config.txt | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Art/Districts/1200/NaturalWonders.pcx b/Art/Districts/1200/NaturalWonders.pcx index d29d5087f90e3f397a5c37b0035ddcc3442a4773..511edf6471b2450d08e6ff31dc74f51cc8ce21ee 100644 GIT binary patch delta 3638 zcmZ8ke{dVebHRm|x#Mdc3{KK^{BT6%9hsbn1omSliW&IW@$+RjdwjBQF5PLA@}jh`Ns<@ z`2O!qtnuIUGfLj9Tp=r0ALIXL`wd;8Z{qvxgRkKG*O;CBf$taeQ~D-@{Sx}dW3uvl z;2KIlw-=uP((2#$J+e73*zkWker50T{=tY9`!&`9{=@zkFWBt9>nQKoJO{Sc{mfDN zwf=;B7-HulrOvPI7dWt^{-*O*zTyx#XF^TV4qs-L6?>Tju_o%g>p%f1r`^U+ki{!= zCy}LGVfJ6xyH|s4A?n<4ENJ2jrhuS?phf1=W z+LjN*JE(Kk(S(x~^BpHKH%&}5hZik^n198@i}}lxNYF5$S#|lqeqs&SrBG+V@$A`o zg4fg{Deplh;x)^(C)R+T&D6n8UW)Y}pPM$&g2NNZgh!DE z(Q6uWJk6%dUZ$H-hB=vP?D9{!1!f) zlxQYAtyr35|CJv+_vCLKUN|WVGDPzhb_kW-T1d7`If3S(0u>>q&FXARngZEQO{Ccy zc`&&7H%=gA#WX0H7OGG;&%(wNZQ3aAF~-=?;i7tp0w^npt(L{Y{zo39w|(x^i!CrF z-ro|jJ0Ss>cwbLeUm4MJb1sP2| zt9iGFmr*349l`Gn6bE7&k+atEi`2tGKBNgD!9thqj|K2-=dYaJ?MGVH3u?|nvf^IL zxg;S(ViCEB&rje>T?&kNvR&Fmx>}G0iO8|YuzA(a*Ma9^D^ACDZ?Z8Q893JO=82p4 zx&%)EMPz)oh%2NR9-awAvI7z3yQN)lrC&eThh~;+X)QqA|K+sCTUFAA3dhC-4e+GK zwb>Jjw0K@)p6798NJFBU)g^d=)&|6YfN@_jg9Z*It*r>~vs!j-pY$(d(RKvzAoB2}osS11u$IN!m6F?cdvxIyg~1Hec=bSkuC zdn*UG#3v#}s2Og;)o`9YvQXQSeLGv6(BKi>BN>`$jpQXap@7J3G49}*OyZcTSLnC? z!cwla_Q&<$$TvT7&e7He7taW-AR}jJL*a^x2(f-{2R>gYK9A2o zV}|^k#KY-?f=t3=%1J`J#RGGR;cU8n{@h|7`=v@BO)Rfd(7A0&z3E6UViQ zV$$pPavBuEK2mu54SYUka{d~=-#TrX7BM|3-pw}z1Bt0bR^J#op`fOI%Ovo+{bD1? zf8!D*bwB_f*)rgX{t30u#o{JQPi^&+0QUA)HbF{Nzs(5WS4DW9T@MsO8ulE(71L-WE$m+6Nm`bmf9Loo@n4k zXNTVS9!rQvf~ag>H)6Nt;kSjBM7g}S(L&tC1xh8P@n>XlaI_MvFkzjmwAQvJ585$W@F^Cwh8;qxWBb+-y zWJnYOEdyC6%%iMAauc&+K@}TBUwe!jmZy)8W#9Ojos__jQt#5i-f(MO+#4iq1AQ@( zW3@6dY#Mx@$OYA2y+t5Ekp&iXIl&Tau)>do5SO0y>ApZP;FI=vU~GB@4$XgHf3+F> zxw=g8Qn|aW)!W+`ByoK_K*SIu*zj?bkov_Pz3JXoiIGwYd5C;MV+lb}gI1u+mmOCN zJ`(f=cFP_srV!K)^B>xuGt8&)L%f{t4K}934asm%yoKv(*Y*q@&#|f|x26Z<;TnnX zu%Gh~;g~!o@vJNp6dU05@np0ja*c940;`;1!TQj?@JHYk?K&;t+oc~I=x*(4<48O$ zws3*=SYLJk<;>w%C->(^>OiMk(wYQG)-uew2_}%t2*xn8VPCM(74yVf*v;ESxrFw< z{rY;4f94u3lcmzDqle=DsG9T=E+j^*>gKzqGr`X^GvyGKd(@jbPfz!s_S5VALBW+2o96qHifT z?amFLpe@pJi7bIc?%){;6G=Xd$SJ~bX5EPO8_?L>#K z+ke*r@~P_-pCe^bVtiQ2@85^-3}R zv*s&8Z8c>w@pL8GU9K2r@gy4>>b#tvYa8_IJwg3YgC2+^2d-AjTfO~cEBJ15nPu!D zOU!5Zmuv<)(&E?gy=+{6ZlSr>#ds=bCNjh`Fn4UWsHU2Cy|BMK+)(Euao=}*!STYn z@2mQ_e%Zd-3i7@0Rx`B3Fj~V6KOM*ahAXqFPSTtjkpf(7IGYSf;i=rvnPNKC`Ry{} zL8^gp@vx}-dg8iEkE`Q_i}sJc243D(UDK7+S~03-i|N9?R44DQ-z50A?oc;&Mb<+! zbaG~vq+WiD5iQ^C^2URH-8UH2*XeC(QaDfToni3OAK$17=RikWG>oqieYVuuT+h3= zcDbUytm+ewWTsD$Vj=mLhx06~Otq8sKyCBu{&-JequSVW3g0N7rS^j`c=_v0E%?r4 zcT^q3_w?ClK1Dj4M|^RA*jLa+F0HoX%9&X;TgYQJZCq$j>{d*P?} zvvP%gYQNqF{-l=?j_tfVTEO?B>S!6$eEp_wB6{O(dOFIHp59sYOySfdp>`GO0lqt# z-|y1{gS`t(KP>fxJ!N!7fU%c7EH9Nu4;AqF-q{k%9-Dl?(5gF(gS+QD}>F#x8<^7-l?17hl#Bqe*Y zL?+*|SxjO$te805B23@juZ308Vf2DYV z&BHL4%gj>73yaLSXy4cbGT!%Tsa(ChVIPvg$f}2|dDH$x2G6fzXD#QB{cQ!PtM0Mp zhxUR323LK+njhJlJHWH6ZnNem_Fs2^-K*YX&40J=cYqzMuCwMZ?DpuF$D9@CD!Z)y ae&IL{{w=AV(wr~;_Z0OHKa7H%HU9$)yjZ^g delta 4386 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 Date: Mon, 15 Dec 2025 09:53:33 -0800 Subject: [PATCH 096/356] Add Lofoten skerries; Set natural wonder placement algorithm to calculate adjacency based on all sides --- Art/Districts/1200/NaturalWonders.pcx | Bin 77560 -> 80126 bytes default.districts_natural_wonders_config.txt | 14 ++++++++++++++ injected_code.c | 13 +++++++++++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Art/Districts/1200/NaturalWonders.pcx b/Art/Districts/1200/NaturalWonders.pcx index 511edf6471b2450d08e6ff31dc74f51cc8ce21ee..812f5f27004be74ff7c6c8bee7b25ec3bf7e26a5 100644 GIT binary patch delta 3027 zcmX|DUx*!78Rtl_HGgc@W|Oui8h17q5qE`T(q$N$&>Vx-%%)+Qlv$^-u;ugyNR{7H&oV_3vMH(+=rH1y2sx)+Z1+o z=ALuD@B4kf@89|0on71S?Yee)OH|h%-?}M8@A|?{9^;b^ZDO{pgYVy55d# z-J`cZBc9y93Q2?%hHwI;;DywK2zAL?Z}N? zpCA0{yD3NOyT!8)E_++em)KqITkK8Qeo0^3Bl2S_9;-uJeIhvpN!EZPc3k!C$Zx+x zFYgiGpI-HBn-MsEV4aUOhJeFOEkS@jy;uC;r1Old$LAR(%;Kwt1k3N3$E)T;Q{|41 zk)>OOIB*0R>@v4UO&bp6xmC z#kR{4<8Czd%f>zIR~7wapICgHC3d)D$Z&v<*2WDB!@h!0)-}MP+B9mh^of$59d_x1 zed49Zx?avhw2uh&E&I<*K?B}6Q~@_ofIn&&AHpey(vW0&^x|j5&(17)l|cYIcm^N8 z`%q9h#gDO77u&$Y2N+dtBx3KzU0$f;725TH_{D{uH^DtcqkCkGorzt-K2)wQK~4!s zP&8mgg^W1C8p~B-$8i@~*4P1qAJVTM5Wl>%;tx#7yume0U2ZF-vlzf?<%SAMz>twp z0<}es|GlrfILZ_>!S7+8>zAg)0T-FNQNg$eP;Z}t7r zGK?~(jnZM{S|Q;abP^zeVLK>JhndbhGIE7J6h~=Z>LOQ3kT~z6IF&h|6b|`GenR~D z!n&V6HWQXnhGm+MT^P?~$~cufvKw|l3*j@Nq&(FHO$Mzrn!tzC$;7O(K6FWD$lx3R z00!qYUHzQsJaN-cx(U~k3(6TSqgEIvGXd#TE>K}NIvmB&X_*%kCV`6%NzjqX*`RHG zVjPO7DITwS^!$UOjBj}}Injz*5F?XCbCa`Bcp@d~B29%Z^upOlrWaGFRI!VKgOSeD z@1-(UZ_rl{2>bPa`01df<>>|Eim=7BWiv-(AUhvrwkjzr!;2+!<#SS}5%iRc`SAdK z3^7M}j_~y#dhdX!p6qk6Vznvh(w{TVw=>d(y{%f{UC-=6=mBOS1)=&XWiG5_=Hv_( z`7d8AC<plq+xkzWu!~GOOwP~w)I3bZ&u_75jJIF$1p_w&|Iyk?G8ql_0rTZQd<#+oSK-`(& z67r$3V9J~mZ5U7pwbh_w3;pRI1@DZ_o z_9n)aiqA0#O*wzAo+Q*c4~vm zVwha6Fs{(B;pF_`of&2r*BE*4WDrA*t>5)YK$vUPPO>&&4>xGyu&5)ZeI`pXP_T(< zc!IOWU{nA{&-U{U<)OlNz2%em#0wcWt+tCpwL$+rEN-1d6|!L*SoUfG=y>KRG({jT zbe1<=dCRN#M4STBhSSQd(_fB=8#8Zn@-+J$4tZnWl+=yrIE ze*Ohf&%VXimQftZ=UfNcV7J1v1`KWrjz9`AL*|sY>Wtr{OJ5Y-FaNX2Y79sxpQDZj z)J^8C+h6k)rVt&)Nj$WgI+9H%jH;N<`q5N%OQcQ5z>L=_q>jH4}H)P$Ce*fc+D>n|rQ7a{S6 zMrE?89VB3mD#Ve^irxsstEb-ds$_XwKp0dPliBuU%I-0Ar~434^V_n7$ozV~@+XM1z$)t6i1+57(w7@fZi delta 443 zcmW;DO(^7X9LMoKe?y+1cCCkHJS@8&*294_2knJgH3t{vp{6_(MQz&C9%Nniu)Bv( zIV>j^lp4|$G6rLfH8G)RO(ANq6N-(RuW$d`>-YYCe&HL-|G$=*+MwZdJL-Zvu&g+j zRvoEVb1rB2Ycj+ zNH{w_X_$@>IiUR&W)(;5NQYYyU|R-UIyx2FGqI}a zf`eIDR$TJiYZD=kXJcK<9bGx*w+EhC@kh%Ot$El{2pjX^GtpK6uX*ZvV{RVUSBPCL zcl=a@Ek%fZZ_U#syWe3=%LSW@v8gzvtpp2-06&ysQE|kEGR!CrSpOb#3P0=0F|Y8k ZrUFw6FLNu+jfYp2n9$;$AFD!Z^fQUa<17FG diff --git a/default.districts_natural_wonders_config.txt b/default.districts_natural_wonders_config.txt index 3d0731aa..63f8f1e8 100644 --- a/default.districts_natural_wonders_config.txt +++ b/default.districts_natural_wonders_config.txt @@ -199,4 +199,18 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happiness_bonus = 1 + +#Wonder +name = Lofoten Skerries +terrain_type = coast +adjacent_to = tundra +img_path = NaturalWonders.pcx +img_row = 3 +img_column = 2 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 happiness_bonus = 1 \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index 16924453..5aaa2f06 100644 --- a/injected_code.c +++ b/injected_code.c @@ -3570,6 +3570,15 @@ natural_wonder_terrain_matches (struct natural_wonder_district_config const * cf if (! tile_matches_square_type (tile, cfg->terrain_type)) return false; + if (cfg->terrain_type == SQ_Coast) { + int continent_id = tile->vtable->m46_Get_ContinentID (tile); + if ((continent_id < 0) || (continent_id >= p_bic_data->Map.Continent_Count)) + return false; + Continent * continent = &p_bic_data->Map.Continents[continent_id]; + if ((continent == NULL) || (continent->Body.TileCount <= 5)) + return false; + } + if (natural_wonder_is_coastal_island (tile, tile_x, tile_y)) return false; @@ -7245,8 +7254,8 @@ place_natural_wonders_on_map (void) (is->natural_wonder_configs[ni].adjacency_dir == DIR_ZERO); int adjacency_count = -1; if (adjacency_bonus_active) - adjacency_count = count_diagonal_adjacent_tiles_of_type (cand->x, cand->y, - is->natural_wonder_configs[ni].adjacent_to); + adjacency_count = count_adjacent_tiles_of_type (cand->x, cand->y, + is->natural_wonder_configs[ni].adjacent_to); int same_type_count = count_adjacent_tiles_of_type (cand->x, cand->y, is->natural_wonder_configs[ni].terrain_type); From aa819722239d198b7084283d13599d69fd198d04 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 15 Dec 2025 10:31:25 -0800 Subject: [PATCH 097/356] Tentatively finish commercial dock art --- Art/Districts/1200/Port_NE.PCX | Bin 19372 -> 21207 bytes Art/Districts/1200/Port_NW.PCX | Bin 20767 -> 20801 bytes Art/Districts/1200/Wonders_2.PCX | Bin 36843 -> 36851 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index 3be84f99d952c836af819b9e0d1446637f3cfa86..be0db0fb14cdcb582ffb9dc92221415cdc4a1c60 100644 GIT binary patch delta 7567 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 delta 2993 zcmZveeQZH8U0oN%$%u4Uj4!i4(fZ3#8D}Y-&~Ql5WyIx{a+B)7YeK z)%IRf;T4Up(#rXx>oU1>(^Rdxu6&hBseqjK+M#Hvmf^dK{k3an2yF^+orHWKZFkRm zuS1=55;#uod(L~#^Zd@S`L)HbzP9+}u_~&s2@_Sd(bWxMa;lo|c9d&7c7a`Xm?CRB z=v?gz`tBOzo;DtcDu?mZe^#LP*X&y^FPyTHl5Z&2V%MT@wHXntxOkCosViJm(&BN} zDd}g`2k!|>=JW6N^8}BoIN!+7&FW{O883wp$(MM5F9|N<-Wzy0NYB-D)5jZDRN9_k zPpMST8*Ha|h_A3?;e3u=MgOh&8a39gtYlBxUO3*@rDAmT(HrYuXb41>F)cAHfcl9HYEn5wFp50)&P%HIk1{4h{T6+5L!v`@DTU5`1LCsR zmmeCVJ746$Z?{(=n8o>1=TEbWvJ(1os@!;No3K6K925~ly$QaI>JXbQuu7=j#Cg7T z2x7Ar_A8*jY<#v+Hzbp`2g_oxKMDrMv}521v9_y}o#0!nLJ7WNdy457_Z@A7McQ}# zP=S>qEM&<_-6u4?Xz>sNFIWk-2dR0}kw);BQ1D5&IN#g>T?BkoZTykGZ0M-QkK&u6 zG`s2PdVwg{MA$rgrEgJ)RgWKJaWTt&I%~DmPbopIQVyOz2sl zjrHrOxBiI-gbg~=g)%>tCjx7HICvBz9xMP|lw(uF{a@dhcITOy_Db=fHaP9iTL1mu zUk``!J~(9-S~e;I3yj_i8JG4qJhj?aADGPZOgT`T(hc9KPVi6<>{wvWS%18+YW8k1 zt8ER<<@sLd0nx5Pp>tchE9Mjr!t^S_gPN^Ru7aRwPQ&Ck>HoxUbbRZRcO@J zrirbO-I2k2`tmmOb2yu4_srYd>h1_-P5QWL4?W&mK@T*?K0m{=JRQ*~>2UKy%hWXg zjX${K#}?QI9?M{W1sC$(%D zy{@LzwB+d?_%qFuaoj%=;cNJ(F`}z&-`XiGs01nJWKxEyWs$bo)d`3ud>#Jq8mr)Q{e1o%Iad*prLjdvxO{k zS$CV2ND~2JF8a(te~+Cb!VfJ*IeRqp8&nxvjVG0irW8%> z_+9g${@4XeK1xc2mdn->MRpFVjVgRnC;2+!XYe|e_QJxZKR@{L;xu)~Dpx(Mx7#>R zbV8eHU3kPC^mvF~{$fuvKCJ;m02Z?Zr_NnvXhFRsrt=0gokn}`CF8~Edqz*A%9m*4 zViixfbRlyCMljho>tAKj-_mtak+1}GUCX|cO$>ivI=Wn-3r7uRoa0p30Wy%bY z%6Q84HNgR88uX`!jy347Lq58}(g0!$DNB1$0~3ZB{>I)Db!u)q;InoiR_sAJZI#~I ztM{E)Keq!G;so_!jS(^M0rA&;zpM?W`!gtw_`RSbI{fhQs=(xOP>=*FLzX;MJaTj; jq`A$2DntyE-g@MPWv~)&IjhLn|LhV_apr^l&oBKS`EQu) diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index eadc78011b691e91b1bf86121c7d8d32cd06568d..1e9cb056fab18e4fc93e24e7ec34de8895b95022 100644 GIT binary patch delta 50 zcmaDopXu{_rVSy*%m<&|og7-c1xVkS%vG`kOs_9#Wj+KHH!m#&$!}g+>LdjKwk#VF delta 42 ycmex7pXv2{rVSy*jQ1vo7jI#_JDIO!36R`c(#m}B>AlIerG Date: Mon, 15 Dec 2025 10:38:44 -0800 Subject: [PATCH 098/356] Fix happiness bonus with natural wonders --- injected_code.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/injected_code.c b/injected_code.c index 5aaa2f06..4cdcad62 100644 --- a/injected_code.c +++ b/injected_code.c @@ -6552,6 +6552,7 @@ add_natural_wonder_from_definition (struct parsed_natural_wonder_definition * de new_cfg.food_bonus = def->has_food_bonus ? def->food_bonus : 0; new_cfg.gold_bonus = def->has_gold_bonus ? def->gold_bonus : 0; new_cfg.shield_bonus = def->has_shield_bonus ? def->shield_bonus : 0; + new_cfg.happiness_bonus = def->has_happiness_bonus ? def->happiness_bonus : 0; if (has_existing) { struct natural_wonder_district_config * cfg = &is->natural_wonder_configs[existing_index]; @@ -23160,9 +23161,9 @@ init_district_icons () Sprite_construct (&is->district_food_eaten_icon); Sprite_slice_pcx (&is->district_food_eaten_icon, __, &pcx, 1 + 7*31, 1, 30, 30, 1, 1); - // Extract happiness icon (index 13: x = 1 + 13*31 = 404, width 30) + // Extract happiness icon (index 12: x = 1 + 12*31 = 373, width 30) Sprite_construct (&is->district_happiness_icon); - Sprite_slice_pcx (&is->district_happiness_icon, __, &pcx, 1 + 13*31, 1, 30, 30, 1, 1); + Sprite_slice_pcx (&is->district_happiness_icon, __, &pcx, 1 + 12*31, 1, 30, 30, 1, 1); // Extract small shield icon (index 13: x = 1 + 13*31 = 404, width 30) Sprite_construct (&is->district_shield_icon_small); From fe853b1be5657f7cbb3c332845fca1553c067076 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 15 Dec 2025 15:16:49 -0800 Subject: [PATCH 099/356] Add Kalaallit Nunaat Cliffs natural wonder --- Art/Districts/1200/NaturalWonders.pcx | Bin 80126 -> 86017 bytes default.districts_natural_wonders_config.txt | 14 ++++++++++++++ injected_code.c | 6 ++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Art/Districts/1200/NaturalWonders.pcx b/Art/Districts/1200/NaturalWonders.pcx index 812f5f27004be74ff7c6c8bee7b25ec3bf7e26a5..80c668fd0771c191d4243464f0f04a1febc613f9 100644 GIT binary patch delta 8433 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 L=ia8hY4`sECLR36 delta 2274 zcmZ8jeT-CB70>DL*mesGON*4n!oqZC+X_otM_`-^wo}RiW3(%pMO{?t&}j7^V`?%@ z14e>r%+#o)$HZuxr%6MiZPU^;wQkaT-`mc9IJ0z7D6=geMIO3K*}?<1`xfr;ci%u0 z+}+%~d(Sz)d+z!8o&DMKjyX4D*M8dAz!Nhm&u=wQ%PJ91ro{CsDkz6?-H(=!MFoB= z(ZO{h{^5<((j#KzlITEJbfCii$Pd+ZYM14LXh7?*8ma%2O-m@Q3h<|J!8?#&f5jA^ zxrtg5A|Lbj8Yzj7OFY&{cjDtBKXG$>67q~$)b5DUBD`-FbvV=JZMni5X44vH(hSNp zUz|;OCp1-=;y0UUt21FnWRmyHq3zDNIRvO}bLlbXw5dtR9nJJ3=cG9*=eS`W?Q9V9 zzWG2Vcyb>75}Q#I$cG%>Lcg0;#cS~R!)8pz`O2-d58{L7to)Z-KSi%$Gi=VwX?|n@ z{SBKTb4gC|&u^o@V^cPfoa9#)(yO-wI_j&cNL8`}0P~EI6MVLXUPo6a zW&1=&I>a{gqG@9@c8r&Qn)Yrib!T-^Rf;+&!lRlF+#C!;74;UdmY>r3Oc7=QM7>-} ze**xbDxBeZ;6txE3AnPn&rjY?ziLiq3MFgZRV?Z5uB6WMqQ$guB^T~U=IptudHXrj z(XDCyle6r87E}=J8fqJR{Rp@VIFyEoe9k-Gg$NpIb`% zI@(sGy-YVNpTCsV>FkQ`qN|Gq&FX-v@z!)tI>&{sQZLU>rn)lC{L^LB+As!y!}2d) zl%$vD=w_YDZ7piIl*^>jSw51a_9aU4t}g3Pqau|Q%M!q3R1TVo!GX_zzJuOqN@Y@r zI=kJO)$=>j{CFGP)${@Mj>$V#uL8Y=DSB&0{D${ZbYIg^NKeXe9i_s1+v%TF;P2l_ z_s*?BbXqDX5nO-{d417cs#+JC(ATTDS4`tVnD_o>B;4iPF zLIOO%udO5nkZJLZLK`U`5vlRzd+4!*4hDF~z4YR(>iDo|fQYDA2?bi_Ki)?>5;`1U z=d<(&AXuRXHy}h2axzWdfw-S{r|H>Q^$YsYvO3`BK1bUjdYf-pMUN)bai4drqLt9G zPR0+d@ch;APlrA~xte|pk$q&p@de1wtf5C86k(|O{2KbnlB1%|=x|6_l~phz*CG}N zw1yO2$k4YPTeyn1e4d^Ln2onhgRbzlEEOnXKTF?DspAI(fvXOQk9JX!SJlgO^`KnK z+&_%%G}iai_6FQl(a|Wke}R4h#7}bWbK6?#nQ1%Pi+J~1+Ktd$t(OocHZ+BGw9WDG zfD_!mj(#({UU&zL4qbTUf04e4{Qa9-AE0LdjXE|XaJH|)3m>F!B~&%wCmy6tf~;KNOea0i7}J<}&pP0C(pOC=fVv8Ka3gg(9=vs>x6C475TWqKu9Eassy1UbS)fvOHq*K__D)O4 zm{~Eo*Vhlt8RLb1jA6UESaNM%z2dH3jZ}a_xwx5b&)OS?FdxJm`(&LkAKCZSZOh7c zZExb5O0kP~Y2P-P2Qd$Brk0%+OF#-o0b!?3eLZK+BN%q*T1{1UcLAej9f`w+0Ek@2cV`SJ3dEXWk+s(oix)3EqVyH zIv@IoRXtbm#6HnJ0A?pKuEp4)U0adjacent_to != SQ_Coast) && - (count_adjacent_tiles_of_type (tile_x, tile_y, SQ_Coast) > 0)) + if ((cfg->adjacent_to != SQ_Coast) && (count_adjacent_tiles_of_type (tile_x, tile_y, SQ_Coast) > 0)) + return false; + + if ((cfg->adjacent_to == SQ_Coast) && (count_adjacent_tiles_of_type (tile_x, tile_y, SQ_Coast) > 4)) return false; if (! natural_wonder_adjacent_requirement_met (cfg, tile, tile_x, tile_y)) From 0a30570f0d72be02b2f320728ceedc825b8431df Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 15 Dec 2025 16:29:47 -0800 Subject: [PATCH 100/356] Add logic for AI cities to build dynamic districts with no dependent buildings --- Art/Districts/1200/Park.PCX | Bin 0 -> 14901 bytes default.districts_natural_wonders_config.txt | 2 +- injected_code.c | 52 +++++++++++++++++-- 3 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 Art/Districts/1200/Park.PCX diff --git a/Art/Districts/1200/Park.PCX b/Art/Districts/1200/Park.PCX new file mode 100644 index 0000000000000000000000000000000000000000..05d9c3dd894f1682ff0e88bd7a44a790536dc0c5 GIT binary patch literal 14901 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 zot?+~`dWAIK6vcurrent_config.enable_districts) { assign_workers_for_pending_districts (this); + bool ai_player = ((*p_human_player_bits & (1 << this->ID)) == 0); + int auto_dynamic_district_ids[COUNT_DISTRICT_TYPES]; + int auto_dynamic_district_count = 0; + + // For dynamic districts, the AI will never be triggered to build them if they have no dependent buildings. + // Determine which dynamic districts the AI could build. In the city loop after, mark which cities need them + if (ai_player) { + for (int district_id = is->special_district_count; district_id < is->district_count; district_id++) { + struct district_config * cfg = &is->district_configs[district_id]; + struct district_infos * info = &is->district_infos[district_id]; + + if (! cfg->is_dynamic) continue; + if (info->dependent_building_count > 0) continue; + if (cfg->command == -1) continue; + + int prereq_id = info->advance_prereq_id; + if ((prereq_id >= 0) && ! Leader_has_tech (this, __, prereq_id)) + continue; + + if (auto_dynamic_district_count < ARRAY_LEN (auto_dynamic_district_ids)) + auto_dynamic_district_ids[auto_dynamic_district_count++] = district_id; + } + } + if (is->current_config.enable_distribution_hub_districts) { // Check if AI has the tech prereq for distribution hubs before updating goals int prereq_id = is->district_infos[DISTRIBUTION_HUB_DISTRICT_ID].advance_prereq_id; @@ -19576,11 +19600,31 @@ patch_Leader_do_production_phase (Leader * this) City * city = coi.city; if (city == NULL) continue; - bool is_human = (*p_human_player_bits & (1 << city->Body.CivID)) != 0; bool at_neighborhood_cap = is->current_config.enable_neighborhood_districts && city_is_at_neighborhood_cap (city); + // Mark any needed dynamic districts for AI players. This isn't the most intelligent approach (we're not weighing district benefits), + // but it's simple and works reasonably well + if (ai_player && (auto_dynamic_district_count > 0)) { + for (int i = 0; i < auto_dynamic_district_count; i++) { + int district_id = auto_dynamic_district_ids[i]; + struct district_infos * info = &is->district_infos[district_id]; + + if (city_has_required_district (city, district_id)) continue; + if (find_pending_district_request (city, district_id) != NULL) continue; + + if ((info->resource_prereq_id >= 0) && (! patch_City_has_resource (city, __, info->resource_prereq_id))) + continue; + + int target_x = 0, target_y = 0; + if (find_tile_for_district (city, district_id, &target_x, &target_y) == NULL) + continue; + + mark_city_needs_district (city, district_id); + } + } + if (at_neighborhood_cap) { - if (is_human) + if (! ai_player) maybe_show_neighborhood_growth_warning (city); else ensure_neighborhood_request_for_city (city); @@ -19631,7 +19675,7 @@ patch_Leader_do_production_phase (Leader * this) // If production needs to be halted, handle the reassignment and messaging if (needs_halt) { // Switch production to another option - if (! is_human) { + if (ai_player) { mark_city_needs_district (city, req_district_id); assign_ai_fallback_production (city, i_improv); } else { @@ -19642,7 +19686,7 @@ patch_Leader_do_production_phase (Leader * this) } // Show message to human player - if (is_human && (city->Body.CivID == p_main_screen_form->Player_CivID)) { + if (! ai_player && (city->Body.CivID == p_main_screen_form->Player_CivID)) { char msg[160]; char const * bname = p_bic_data->Improvements[i_improv].Name.S; snprintf (msg, sizeof msg, "%s %s %s", bname, is->c3x_labels[CL_CONSTRUCTION_HALTED_DUE_TO_MISSING_DISTRICT], district_description); From c992f7c5b72ec8d4b36afbc0553788f7d666a17d Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 15 Dec 2025 17:51:23 -0800 Subject: [PATCH 101/356] Shrink Greenland cliffs, looks better on coast and fits --- Art/Districts/1200/NaturalWonders.pcx | Bin 86017 -> 83708 bytes default.districts_natural_wonders_config.txt | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Art/Districts/1200/NaturalWonders.pcx b/Art/Districts/1200/NaturalWonders.pcx index 80c668fd0771c191d4243464f0f04a1febc613f9..f47e8f501f6286539b847fc588399a7984f1ae07 100644 GIT binary patch delta 4728 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 DJ3J+) diff --git a/default.districts_natural_wonders_config.txt b/default.districts_natural_wonders_config.txt index d8e8cf35..41585eab 100644 --- a/default.districts_natural_wonders_config.txt +++ b/default.districts_natural_wonders_config.txt @@ -218,7 +218,7 @@ happiness_bonus = 1 #Wonder name = Kalaallit Nunaat Cliffs terrain_type = tundra -adjacent_to = tundra +adjacent_to = coast img_path = NaturalWonders.pcx img_row = 3 img_column = 3 From f9c9321e4e11f715dc2c052ea9f8f71415c26bfa Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 15 Dec 2025 17:58:29 -0800 Subject: [PATCH 102/356] Enable option for AI to intelligently "auto"-decide how many ideal distro hubs it should have --- C3X.h | 6 +++ default.c3x_config.ini | 12 +++-- injected_code.c | 101 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/C3X.h b/C3X.h index 7af906fe..211e8fd5 100644 --- a/C3X.h +++ b/C3X.h @@ -134,6 +134,11 @@ enum distribution_hub_yield_division_mode { DHYDM_SCALE_BY_CITY_COUNT }; +enum ai_distribution_hub_build_strategy { + ADHBS_AUTO = 0, + ADHBS_BY_CITY_COUNT +}; + enum perfume_kind { PK_PRODUCTION = 0, PK_TECHNOLOGY, @@ -356,6 +361,7 @@ struct c3x_config { int distribution_hub_yield_division_mode; int distribution_hub_food_yield_divisor; int distribution_hub_shield_yield_divisor; + int ai_distribution_hub_build_strategy; int ai_ideal_distribution_hub_count_per_100_cities; bool workers_can_enter_coast; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 57f7cf93..b070bbb1 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -870,10 +870,16 @@ destroyed_wonders_can_be_built_again = true ; Distribution Hubs make surrounding tiles unworkable and instead distribute their raw food and shield yields to ALL connected cities in your civilization. ; Yields are subject to corruption as with regular shields. ; The ai_ideal_distribution_hub_count_per_100_cities setting controls how many hubs the AI tries to maintain per 100 cities (e.g., 25 means the AI aims for 1 hub per 4 cities). -; Distribution hub yield division mode controls how a hub splits its collected food/shields across connected cities: -; flat: Divide raw yields by the configured divisors (distribution_hub_food_yield_divisor / distribution_hub_shield_yield_divisor) -; scale-by-city-count:Divide by (connected city count / divisor), letting hubs scale with network size (e.g., 12 food, 10 cities, divisor 2 => 12 / (10/2) = 2.4 -> 2) +; +; distribution_hub_yield_division_mode controls how a hub splits its collected food/shields across connected cities: +; flat: Divide raw yields by the configured divisors (distribution_hub_food_yield_divisor / distribution_hub_shield_yield_divisor) +; scale-by-city-count: Divide by (connected city count / divisor), letting hubs scale with network size (e.g., 12 food, 10 cities, divisor 2 => 12 / (10/2) = 2.4 -> 2) +; +; ai_distribution_hub_build_strategy controls how the AI decides to build distribution hubs: +; by-city-count: AI builds hubs based on its ideal hub count per 100 cities +; auto: AI dynamically assesses need based on city growth and food/shield deficits, capped at max 1 hub for every 2 cities distribution_hub_yield_division_mode = scale-by-city-count +ai_distribution_hub_build_strategy = auto distribution_hub_food_yield_divisor = 3 distribution_hub_shield_yield_divisor = 4 ai_ideal_distribution_hub_count_per_100_cities = 25 diff --git a/injected_code.c b/injected_code.c index f12e8fe2..bee3ecd1 100644 --- a/injected_code.c +++ b/injected_code.c @@ -1694,6 +1694,16 @@ read_distribution_hub_yield_division_mode (struct string_slice const * s, int * return false; } +bool +read_ai_distribution_hub_build_strategy (struct string_slice const * s, int * out_val) +{ + struct string_slice trimmed = trim_string_slice (s, 1); + if (slice_matches_str (&trimmed, "auto" )) { *out_val = ADHBS_AUTO; return true; } + else if (slice_matches_str (&trimmed, "by-city-count" )) { *out_val = ADHBS_BY_CITY_COUNT; return true; } + else + return false; +} + bool read_square_type_value (struct string_slice const * s, enum SquareTypes * out_type) { @@ -2210,6 +2220,9 @@ load_config (char const * file_path, int path_is_relative_to_mod_dir) } else if (slice_matches_str (&p.key, "distribution_hub_yield_division_mode")) { if (! read_distribution_hub_yield_division_mode (&value, (int *)&cfg->distribution_hub_yield_division_mode)) handle_config_error (&p, CPE_BAD_VALUE); + } else if (slice_matches_str (&p.key, "ai_distribution_hub_build_strategy")) { + if (! read_ai_distribution_hub_build_strategy (&value, (int *)&cfg->ai_distribution_hub_build_strategy)) + handle_config_error (&p, CPE_BAD_VALUE); } else if (slice_matches_str (&p.key, "ptw_like_artillery_targeting")) { if (! read_ptw_arty_types (&value, &unrecognized_lines, @@ -11835,6 +11848,7 @@ patch_init_floating_point () base_config.city_work_radius = 2; base_config.day_night_cycle_mode = DNCM_OFF; base_config.distribution_hub_yield_division_mode = DHYDM_FLAT; + base_config.ai_distribution_hub_build_strategy = ADHBS_BY_CITY_COUNT; 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; for (int n = 0; n < ARRAY_LEN (integer_config_options); n++) @@ -19397,6 +19411,77 @@ patch_Map_wrap_vert_for_barb_ai (Map * this, int edx, int y) return Map_wrap_vert (this, __, is->current_config.patch_barbarian_diagonal_bug ? (y + is->barb_diag_patch_dy_fix) : y); } +int +count_workable_tiles_for_city (City * city) +{ + if (city == NULL) + return 0; + + int workable = 0; + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { + if ((wai.tile != NULL) && (wai.tile != p_null_tile)) + workable++; + } + return workable; +} + +int +compute_auto_distribution_hub_goal (Leader * leader, int city_count) +{ + int total_unused_tiles = 0; + int stagnating_cities = 0; + int slow_growth_cities = 0; + int very_low_production_cities = 0; + int low_production_cities = 0; + + FOR_CITIES_OF (coi, leader->ID) { + City * city = coi.city; + if (city == NULL) + continue; + + int pop_size = city->Body.Population.Size; + int workable_tiles = count_workable_tiles_for_city (city); + if ((workable_tiles > 0) && (pop_size < workable_tiles)) + total_unused_tiles += workable_tiles - pop_size; + + int net_food = city->Body.FoodIncome; + if (net_food <= 0) + stagnating_cities++; + else if (net_food <= 2) + slow_growth_cities++; + + int net_shields = city->Body.ProductionIncome + city->Body.ProductionLoss; + if (net_shields < 0) + net_shields = 0; + if (net_shields <= 3) + very_low_production_cities++; + else if (net_shields <= 6) + low_production_cities++; + } + + int base_desired = (city_count + 3) / 4; + + int tiles_per_chunk = is->workable_tile_count; + if (tiles_per_chunk <= 0) + tiles_per_chunk = 1; + int unused_bonus = (total_unused_tiles + tiles_per_chunk * 3 - 1) / (tiles_per_chunk * 3); + + int food_pressure = stagnating_cities * 2 + slow_growth_cities; + int food_bonus = (food_pressure + 2) / 3; + + int production_pressure = very_low_production_cities * 2 + low_production_cities; + int production_bonus = (production_pressure + 2) / 3; + + int desired = base_desired + unused_bonus + food_bonus + production_bonus; + int max_reasonable = (city_count + 1) / 2; + if (desired > max_reasonable) + desired = max_reasonable; + if (desired < 1) + desired = 1; + + return desired; +} + void ai_update_distribution_hub_goal_for_leader (Leader * leader) { @@ -19410,15 +19495,19 @@ ai_update_distribution_hub_goal_for_leader (Leader * leader) if ((1 << civ_id) & *p_human_player_bits) return; - int ideal_per_100 = is->current_config.ai_ideal_distribution_hub_count_per_100_cities; - if (ideal_per_100 <= 0) - return; - int city_count = leader->Cities_Count; if (city_count <= 0) return; - int desired = (city_count * ideal_per_100) / 100; + int desired = 0; + if (is->current_config.ai_distribution_hub_build_strategy == ADHBS_AUTO) + desired = compute_auto_distribution_hub_goal (leader, city_count); + else { + int ideal_per_100 = is->current_config.ai_ideal_distribution_hub_count_per_100_cities; + if (ideal_per_100 <= 0) + return; + desired = (city_count * ideal_per_100) / 100; + } if (desired <= 0) return; @@ -19480,7 +19569,7 @@ ai_update_distribution_hub_goal_for_leader (Leader * leader) continue; int tile_x, tile_y; - Tile * candidate = find_tile_for_distribution_hub_district (city, &tile_x, &tile_y); + Tile * candidate = find_tile_for_district (city, DISTRIBUTION_HUB_DISTRICT_ID, &tile_x, &tile_y); if (candidate == NULL) continue; From 4599c08a334033a1968da0ff4087cd28c30a1dd3 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 15 Dec 2025 18:00:39 -0800 Subject: [PATCH 103/356] Update default config comments --- default.c3x_config.ini | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/default.c3x_config.ini b/default.c3x_config.ini index b070bbb1..95e59e40 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -868,8 +868,10 @@ destroyed_wonders_can_be_built_again = true ; Distribution Hubs work as "breadbaskets" and mining areas far from urban centers, minimizing local city potential but benefiting the entire civilization. ; Distribution Hubs make surrounding tiles unworkable and instead distribute their raw food and shield yields to ALL connected cities in your civilization. -; Yields are subject to corruption as with regular shields. -; The ai_ideal_distribution_hub_count_per_100_cities setting controls how many hubs the AI tries to maintain per 100 cities (e.g., 25 means the AI aims for 1 hub per 4 cities). +; "Divisors" for food and shields divide the raw yield values before distribution to your cities. Yields are subject to corruption as with regular shields. +; +; ai_ideal_distribution_hub_count_per_100_cities setting controls how many hubs the AI tries to maintain per 100 cities if distribution_hub_yield_division_mode +; is set to "flat" (e.g., 25 means the AI aims for 1 hub per 4 cities). ; ; distribution_hub_yield_division_mode controls how a hub splits its collected food/shields across connected cities: ; flat: Divide raw yields by the configured divisors (distribution_hub_food_yield_divisor / distribution_hub_shield_yield_divisor) From 72188af738008cfd23f659a944b468369c9e2ac4 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 15 Dec 2025 18:39:01 -0800 Subject: [PATCH 104/356] Refine logic for determining workable tiles for distro hub auto AI --- injected_code.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/injected_code.c b/injected_code.c index bee3ecd1..37b7080c 100644 --- a/injected_code.c +++ b/injected_code.c @@ -19419,8 +19419,18 @@ count_workable_tiles_for_city (City * city) int workable = 0; FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { - if ((wai.tile != NULL) && (wai.tile != p_null_tile)) - workable++; + Tile * tile = wai.tile; + if ((tile == NULL) || (tile == p_null_tile)) + continue; + + if (tile->Body.CityAreaID != city->Body.ID) + continue; + + struct district_instance * inst = get_district_instance (tile); + if ((inst != NULL) && district_is_complete (tile, inst->district_type)) + continue; + + workable++; } return workable; } From 82d8d6ae71386d945db036e221f045d6f159c176 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 15 Dec 2025 21:24:27 -0800 Subject: [PATCH 105/356] Add flag and message for district buildings lost due to district destruction --- C3X.h | 6 ++++++ Text/c3x-labels.txt | 8 ++++++- default.c3x_config.ini | 1 + injected_code.c | 48 +++++++++++++++++++++++++++++++++++++----- 4 files changed, 57 insertions(+), 6 deletions(-) diff --git a/C3X.h b/C3X.h index 211e8fd5..460210db 100644 --- a/C3X.h +++ b/C3X.h @@ -347,6 +347,7 @@ struct c3x_config { bool cities_with_mutual_district_receive_buildings; bool cities_with_mutual_district_receive_wonders; bool show_message_when_building_received_by_mutual_district; + bool show_message_when_building_lost_to_destroyed_district; bool air_units_use_aerodrome_districts_not_cities; bool naval_units_use_port_districts_not_cities; @@ -486,6 +487,11 @@ enum c3x_label { CL_FROM_SHARED, CL_WITH, + CL_APOSTROPHE_S, + CL_AND, + CL_OTHER_BUILDINGS_HAVE_BEEN, + CL_LOST_DUE_TO_DESTROYED, + // Districts config mismatch checked on game load CL_DISTRICT_ID, CL_DISTRICT_IN_SAVE_BUT_MISSING_NOW, diff --git a/Text/c3x-labels.txt b/Text/c3x-labels.txt index ac0d77f0..de4b5e26 100644 --- a/Text/c3x-labels.txt +++ b/Text/c3x-labels.txt @@ -116,11 +116,17 @@ requires a Neighborhood to grow destroyed by volcanic eruption! construction halted due to missing -; Shown if City A receives a building from City B via shared district, as in " recieved from shared with ". +; Shown if City A receives a building from City B via shared district, as in " received from shared with ". received from shared with +; Shown if a city loses one or more buildings due to destroyed district, as in "'s and other buildings have been lost due to destroyed " +'s +and +other buildings have been +lost due to destroyed + ; Districts config mismatch checked on game load District ID is in the save file but missing in the current configuration. diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 95e59e40..80d06c97 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -840,6 +840,7 @@ enable_port_districts = false cities_with_mutual_district_receive_buildings = true cities_with_mutual_district_receive_wonders = true show_message_when_building_received_by_mutual_district = true +show_message_when_building_lost_to_destroyed_district = true ; When enabled, air units can only be built in cities with an Aerodrome district in their work radius and must be based at Aerodrome districts instead ; of cities. Air units will spawn on Aerodromes and can only land on Aerodromes, vanilla airfields, or carriers. Airlifts and airdrops are similarly diff --git a/injected_code.c b/injected_code.c index 37b7080c..68cc362f 100644 --- a/injected_code.c +++ b/injected_code.c @@ -9047,14 +9047,15 @@ patch_is_not_pop_capped_or_starving (City * city) return true; } -void +bool remove_building_if_no_district (City * city, int district_id, int building_id) { - if ((city == NULL) || (building_id < 0)) return; - if (! patch_City_has_improvement (city, __, building_id, false)) return; - if (city_has_required_district (city, district_id)) return; + if ((city == NULL) || (building_id < 0)) return false; + if (! patch_City_has_improvement (city, __, building_id, false)) return false; + if (city_has_required_district (city, district_id)) return false; patch_City_add_or_remove_improvement (city, __, building_id, 0, false); + return true; } bool @@ -9168,14 +9169,50 @@ remove_dependent_buildings_for_district (int district_id, int center_x, int cent if (city_has_other_completed_district (city, district_id, center_x, center_y)) continue; + int removed_count = 0; + int first_building_id = -1; + for (int i = 0; i < info->dependent_building_count; i++) { int building_id = info->dependent_building_ids[i]; if (building_id >= 0) { // This also loops through tiles around the city but is not redundant, as the city // may have multiple districts of the same type in its radius (eg outside radius of this particular district) - remove_building_if_no_district (city, district_id, building_id); + if (remove_building_if_no_district (city, district_id, building_id)) { + if (removed_count == 0) + first_building_id = building_id; + removed_count++; + } } } + + if ((removed_count > 0) && + is->current_config.show_message_when_building_lost_to_destroyed_district && + ((*p_human_player_bits & (1 << city->Body.CivID)) != 0)) { + char msg[200]; + char const * district_name = is->district_configs[district_id].name; + char const * building_name = (first_building_id >= 0) ? p_bic_data->Improvements[first_building_id].Name.S : ""; + + if (removed_count == 1) + snprintf (msg, sizeof msg, "%s%s %s %s %s", + city->Body.CityName, + is->c3x_labels[CL_APOSTROPHE_S], + building_name, + is->c3x_labels[CL_LOST_DUE_TO_DESTROYED], + district_name); + else + snprintf (msg, sizeof msg, "%s%s %s %s %d %s %s %s", + city->Body.CityName, + is->c3x_labels[CL_APOSTROPHE_S], + building_name, + is->c3x_labels[CL_AND], + removed_count - 1, + is->c3x_labels[CL_OTHER_BUILDINGS_HAVE_BEEN], + is->c3x_labels[CL_LOST_DUE_TO_DESTROYED], + district_name); + + msg[(sizeof msg) - 1] = '\0'; + show_map_specific_text (city->Body.X, city->Body.Y, msg, true); + } } } @@ -11746,6 +11783,7 @@ patch_init_floating_point () {"cities_with_mutual_district_receive_buildings" , false, offsetof (struct c3x_config, cities_with_mutual_district_receive_buildings)}, {"cities_with_mutual_district_receive_wonders" , false, offsetof (struct c3x_config, cities_with_mutual_district_receive_wonders)}, {"show_message_when_building_received_by_mutual_district", false, offsetof (struct c3x_config, show_message_when_building_received_by_mutual_district)}, + {"show_message_when_building_lost_to_destroyed_district" , false, offsetof (struct c3x_config, show_message_when_building_lost_to_destroyed_district)}, {"air_units_use_aerodrome_districts_not_cities" , false, offsetof (struct c3x_config, air_units_use_aerodrome_districts_not_cities)}, {"naval_units_use_port_districts_not_cities" , false, offsetof (struct c3x_config, naval_units_use_port_districts_not_cities)}, {"show_natural_wonder_name_on_map" , false, offsetof (struct c3x_config, show_natural_wonder_name_on_map)}, From 77d271032b5d0c3f12faba4388c826c5f5976670 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 15 Dec 2025 22:21:53 -0800 Subject: [PATCH 106/356] Add display_name field for districts, wire into tile detail view --- C3X.h | 9 +++++++++ injected_code.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/C3X.h b/C3X.h index 460210db..0001f24e 100644 --- a/C3X.h +++ b/C3X.h @@ -573,6 +573,7 @@ enum { struct district_config { enum Unit_Command_Values command; char const * name; + char const * display_name; char const * tooltip; char const * advance_prereq; char const * resource_prereq; @@ -661,6 +662,7 @@ struct wonder_location { const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYPES] = { { .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", + .display_name = "Neighborhood", .advance_prereq = NULL, .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, @@ -670,6 +672,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_WonderDistrict, .name = "Wonder District", .tooltip = "Build Wonder District", + .display_name = "Wonder District", .advance_prereq = NULL, .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"WonderDistrict.pcx"}, .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Coast)), @@ -679,6 +682,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_DistributionHub, .name = "Distribution Hub", .tooltip = "Build Distribution Hub", + .display_name = "Distribution Hub", .advance_prereq = "Construction", .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"DistributionHub.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, @@ -688,6 +692,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_Aerodrome, .name = "Aerodrome", .tooltip = "Build Aerodrome", + .display_name = "Aerodrome", .advance_prereq = "Flight", .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 1, .img_paths = {"Aerodrome.pcx"}, .dependent_improvements = {"Airport"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, @@ -696,6 +701,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = -1, .name = "Natural Wonder", .tooltip = NULL, + .display_name = "Natural Wonder", .advance_prereq = NULL, .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {0}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, @@ -704,6 +710,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", + .display_name = "Port", .advance_prereq = "Map Making", .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .align_to_coast = true, .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .dependent_improvements = {"Harbor", "Commercial Dock"}, .buildable_square_types_mask = (1 << SQ_Coast), @@ -714,6 +721,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP struct parsed_district_definition { char * name; + char * display_name; char * tooltip; char * advance_prereq; char * resource_prereq; @@ -740,6 +748,7 @@ struct parsed_district_definition { bool has_tooltip; bool has_advance_prereq; bool has_dependent_improvements; + bool has_display_name; bool has_img_paths; bool has_allow_multiple; bool has_vary_img_by_era; diff --git a/injected_code.c b/injected_code.c index 68cc362f..3a0e202e 100644 --- a/injected_code.c +++ b/injected_code.c @@ -4944,10 +4944,16 @@ free_dynamic_district_config (struct district_config * cfg) if (! cfg->is_dynamic) return; + char const * name_ptr = cfg->name; if (cfg->name != NULL) { free ((void *)cfg->name); cfg->name = NULL; } + if ((cfg->display_name != NULL) && + (cfg->display_name != name_ptr)) { + free ((void *)cfg->display_name); + cfg->display_name = NULL; + } if (cfg->tooltip != NULL) { free ((void *)cfg->tooltip); cfg->tooltip = NULL; @@ -5043,6 +5049,12 @@ free_special_district_override_strings (struct district_config * cfg, struct dis if (cfg == NULL || defaults == NULL) return; + if ((cfg->display_name != NULL) && + (cfg->display_name != cfg->name) && + (cfg->display_name != defaults->display_name)) { + free ((void *)cfg->display_name); + cfg->display_name = NULL; + } if ((cfg->tooltip != NULL) && (cfg->tooltip != defaults->tooltip)) { free ((void *)cfg->tooltip); cfg->tooltip = NULL; @@ -5156,6 +5168,10 @@ free_parsed_district_definition (struct parsed_district_definition * def) if (def == NULL) return; + if (def->display_name != NULL) { + free (def->display_name); + def->display_name = NULL; + } if (def->name != NULL) { free (def->name); def->name = NULL; @@ -5413,6 +5429,15 @@ override_special_district_from_definition (struct parsed_district_definition * d def->name = NULL; def->has_name = false; + if (def->has_display_name) { + if ((cfg->display_name != NULL) && + (cfg->display_name != cfg->name) && + (cfg->display_name != defaults->display_name)) + free ((void *)cfg->display_name); + cfg->display_name = def->display_name; + def->display_name = NULL; + } + if (def->has_tooltip) { if ((cfg->tooltip != NULL) && (cfg->tooltip != defaults->tooltip)) free ((void *)cfg->tooltip); @@ -5552,6 +5577,14 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.name = def->name; def->name = NULL; + if (def->has_display_name) { + new_cfg.display_name = def->display_name; + if (new_cfg.display_name == NULL) + new_cfg.display_name = new_cfg.name; + def->display_name = NULL; + } else { + new_cfg.display_name = new_cfg.name; + } if (def->has_tooltip) { new_cfg.tooltip = def->tooltip; @@ -5673,6 +5706,14 @@ handle_district_definition_key (struct parsed_district_definition * def, def->has_name = true; } + } else if (slice_matches_str (key, "display_name")) { + if (def->display_name != NULL) { + free (def->display_name); + def->display_name = NULL; + } + def->display_name = copy_trimmed_string_or_null (value, 1); + def->has_display_name = true; + } else if (slice_matches_str (key, "tooltip")) { if (def->tooltip != NULL) { free (def->tooltip); @@ -16752,7 +16793,9 @@ patch_PCX_Image_draw_tile_info_terrain (PCX_Image * this, int edx, char * str, i if (dist != NULL) { show_district_name = true; - char const * display_name = is->district_configs[dist->district_type].name; + char const * display_name = is->district_configs[dist->district_type].display_name; + if ((display_name == NULL) || (display_name[0] == '\0')) + display_name = is->district_configs[dist->district_type].name; // If it's a wonder district with a completed wonder, show the wonder name instead if ((dist->district_type == WONDER_DISTRICT_ID) && From 7ad8b2d8347f01d133b54fb4dfe48e83afc5d78c Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 15 Dec 2025 22:52:30 -0800 Subject: [PATCH 107/356] Add flag to prevent autozoom in city view of larger work radius --- C3X.h | 1 + default.c3x_config.ini | 8 +++++--- injected_code.c | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/C3X.h b/C3X.h index 0001f24e..2919183c 100644 --- a/C3X.h +++ b/C3X.h @@ -270,6 +270,7 @@ struct c3x_config { bool allow_sale_of_aqueducts_and_hospitals; bool no_cross_shore_detection; int city_work_radius; + bool auto_zoom_city_screen_for_large_work_areas; enum work_area_limit work_area_limit; struct work_area_improvement * work_area_improvements; int count_work_area_improvements; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 80d06c97..3f10545d 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -707,10 +707,12 @@ no_cross_shore_detection = false ; means cities would only be able to work tiles within their level 1 cultural borders, meaning the eight tiles neighboring the city's own ; tile. Setting to 2 gives you the game's standard rule that cities may work tiles within their level 2 cultural borders, which they attain after one ; expansion. Setting to 3 allows them to work within level 3 borders and so forth. -; Notes: (1) If the city can work tiles in the fourth ring or farther, the map view on the city screen will be zoomed out when you enter it. Otherwise -; it's not possible to show the entire work area. You can zoom back in by pressing Z. (2) The next option below can limit cities' workable areas to be -; smaller than the radius set here, based on their culture. (3) This option has a minimum value of 1 and a maximum of 7. +; Notes: (1) If the city can work tiles in the fourth ring or farther, the map view on the city screen will be zoomed out when you enter it if +; auto_zoom_city_screen_for_large_work_areas is true. Otherwise it's not possible to show the entire work area. You can zoom back in by pressing Z. +; (2) The next option below can limit cities' workable areas to be smaller than the radius set here, based on their culture. (3) This option has a +; minimum value of 1 and a maximum of 7. city_work_radius = 2 +auto_zoom_city_screen_for_large_work_areas = true ; This option can reduce the size of the area cities can work based on their cultural level. ; Possible values are: diff --git a/injected_code.c b/injected_code.c index 3a0e202e..5709fc6d 100644 --- a/injected_code.c +++ b/injected_code.c @@ -602,7 +602,7 @@ patch_Main_Screen_Form_bring_cnter_view_city_focus (Main_Screen_Form * this, int int effective_radius = not_above (get_work_ring_limit_total (p_city_form->CurrentCity), is->current_config.city_work_radius); if (is->current_config.work_area_limit == WAL_CULTURAL_OR_ADJACENT) effective_radius = not_above (is->current_config.city_work_radius, effective_radius + 1); - if (effective_radius >= 4) + if (effective_radius >= 4 && is->current_config.auto_zoom_city_screen_for_large_work_areas) p_bic_data->is_zoomed_out = true; Main_Screen_Form_bring_tile_into_view (this, __, x, get_city_screen_center_y (p_city_form->CurrentCity), param_3, always_update_tile_bounds, param_5); @@ -11809,6 +11809,7 @@ patch_init_floating_point () {"convert_to_landmark_after_planting_forest" , false, offsetof (struct c3x_config, convert_to_landmark_after_planting_forest)}, {"allow_sale_of_aqueducts_and_hospitals" , false, offsetof (struct c3x_config, allow_sale_of_aqueducts_and_hospitals)}, {"no_cross_shore_detection" , false, offsetof (struct c3x_config, no_cross_shore_detection)}, + {"auto_zoom_city_screen_for_large_work_areas" , true, offsetof (struct c3x_config, auto_zoom_city_screen_for_large_work_areas)}, {"limit_unit_loading_to_one_transport_per_turn" , false, offsetof (struct c3x_config, limit_unit_loading_to_one_transport_per_turn)}, {"prevent_old_units_from_upgrading_past_ability_block" , false, offsetof (struct c3x_config, prevent_old_units_from_upgrading_past_ability_block)}, {"draw_forests_over_roads_and_railroads" , false, offsetof (struct c3x_config, draw_forests_over_roads_and_railroads)}, From 5344d6f2292e2864f944c69b2d22d1aee8609087 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 16 Dec 2025 08:08:01 -0800 Subject: [PATCH 108/356] Add first try at central rail hub asian art --- Art/Districts/1200/CentralRailHub_ASIAN.PCX | Bin 0 -> 13708 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Art/Districts/1200/CentralRailHub_ASIAN.PCX diff --git a/Art/Districts/1200/CentralRailHub_ASIAN.PCX b/Art/Districts/1200/CentralRailHub_ASIAN.PCX new file mode 100644 index 0000000000000000000000000000000000000000..56bb189bbe77cf80599c0599865c69ea540e1d2f GIT binary patch literal 13708 zcmeHue{>V)nQk>RLRWImJ-w$rm)(?}y|>NYv|V=3&6X&{;%?XtBr(vFZ5N2cU~7Uc z#j>Qj;>6ZW0v7rkj>@?lmV>V3?3C{Zon}97-<6uC5En|L!5Rx`r zlGMbb`%du_Tl;%_@&xvObX)tW zep8>s{%g0jH*tg#_FuWJy`le3pThpx+uGmZ$QQ7G`nL8X{knb``_JCiUdI{V$Np2d zwb%5k`iIy*aa(&8SMo9TAHS`=qF>TK!Tyok+Do{?IqW}jTRW@I=sNZnZfj@sY3wdk zjP5^fr}gvLccd~`)t<*)RVpKuPpaCp`WfsyUirAHJ%c;0R3<7PR<)<}lh}8v@_to& z60K3GoUL50YA5t@?3=D!sA|X2QkBYVWvZ$@t{1WIN~KiQ7SW28%C*X5RXd^|#lDX! zC#u?`XyHobMrEw3J)#d`-=~#hRqYU3zf!qX8LnyzI$&R=LaJK$6F)H@&3pyst2W=M zxpmAfYJLjlCue@z<_IxIl{pg4(QA%)b1yOTD>FYd^IJ1NH{%5}9x>w`GoCWzH8UPG z<4rT3HREM79yjCt>Im_td(6f$o7ZfeX8SU;gPGmT>}+-}X6I;j?q)bK!<88h&2Vdm zb92uy_a<`>H1}F_Pd8@=b4D>|A#>(3XFGF-G-p+FCN^hpbH@LV_%Z$`vITR5n4`)Z ziRS1vN4&Y0nE92NADa2CnV*~Sf*FsP@s1f!nemz#51R3&8PA&WvKfz?@xEEK_*{ph zZ2_1C%#N#{`B>+6%R@Tc`Z>%$8)SVRsDS?XBNhF#s>0-BT_!&{tS8c%+T8}zjc%BJ zZYf-==$};8Dj)0JOrH7`QswzQ~hK8`CVI~vNy$RgcmfTK#7H-yvhU9e9#Z%`xX8Ds@msc z9p2gXEU9pE8t3xy0xyUH6xB|c6-dXr^)Oq}FIQDY(|RHb<>nWXqdHWchr`eA#ffYd zN-OY+1|?oeLrJqYxM7|bgB@@UE>^G_nZmvPp_)Pz=?;~!Fb!oTa#Sx=;2n7TOnN+O?1L($O?aEWf;{cx*-@BMN8^%3^qj)_7Bp)jN$0(}U_`f(Q&S)bS= zsG)ib&8GwqFic-vtWD`@g^$HI!+Qee=tqgDz|lfwFC5!-Q0Ab7 zJ1hEqEVq!;_>_p|B6y@}H3Xk^y2V4rkkxhGl&--XhkLMD!vuk)1SQ4C<55=jzV#+t zf{CacRmC7FGHd~d)o@G&r!5hIk455q0SB!v5^+!(Cc;(;PVk6{3Y?UMn1YK@QidH5 zsuYvqx>YEWG{FO(I5Du}ZUsTO z=U~XTPla)oa1^c(5k|?J6(=N8B$h~qJA+4X!0PJsr2gtVdYCYLT-wZ&O2W={KVjFz zxXAki-WH`7cEcF0*;K;E6~y^MJ5x|1O6Y+ugw6C(9J0EweOKS>fU-{-LL20gaAh#! zf=bGvvGq~vi6=E83Ivf;BpeHKPe75OZ0T4~BK)e*zK|fYq_Y*B|Jte%9K5vB4{T35pu!R6Oh8 z#t-XSJBl@{>~UT0fRexCjV8X%&Eq*&IGD51Du)vofD*Jcy*!FbuUAEwmAgbut+!F3 zdKj~_v?^)5BH--AvvHjjZmv9DvtA!VoZ--}rZy-`3K3O*&?ot93kerB96gO3Ar{hX zd(=s_uNS%m%`g~8$+m7Jr+S+#O7V_Enj6k-jc@E2uUYsPb)w?peN9ACk`k}>Jm-4A z9aaNSLer-Td|D{*1p&sbFv&GN1D|<#l0w|DQO>p-Cas7`m{WbRW*F<()YbYqv$8XNm=}p4 z`3&DI3b=DcOe+%KPXtzAo#-j;^=#gcuze#WA{X(KH3g1i`krLoe10kH%@mSUeJc zjm)>ejjbI4&K8RMaogzRF&oU4YU(e?^qz32p}npFh8tM8Ms3~L2-h0h9Bo`W0i$TF zWZG)CaUBh~voyxDw5X-Un8>CRViOW)@lukY+w9^DDk8y|sBy2u0j;aUt%w;`W9iJBrf z+c1m@3~|8R?&VGzfgy-~jeo5%@GQ)?J_z%c1yN@`%)5CK$;UA!t?%Lk_8AlH31b3W zch^TrM?Fs0*aDdCYr)aG4e33SrbW%RMNJEF!|p)VX}Ku*OvQ5xR!(YCt8-aC9+PK zusc#YWQNq?(WJ%@dwVz(P5a=+gJgYM2N$hxp)v5&i`pzS1BJ*SIuDdz2)HkssAPpo zv)jI$JJ7zolY1rp;47P!-|G@!SQ4dGoCQXl{Rlutz#}AyWFjqh+uE0}=xQ#sa89nf z3r1+%wgcv!7D`fre#0B4-#1a+Fq^mt=bL^B=lzc5(fWVtd^HQ_Hp0z#Y>+20t|8cD z^II0eTz}3WzI5UX*bK+44twXiRtx8B6glSsxLLqs#(b)-Uhx#Xb=OUVyrD{G)9Y~d zp8V@bcl)Dq<0CETatd6A(HOCCs!!1x@KjVwN=TuJ(l82_lQ5i2G3{N>1yQcm9*We% z&8VHTa4-&|S(;3HKQ_?@4_Dxpz2SjoIRDxfm|2k^4F(`D!<7U!!<;h;r97PB1)o%B z;~aKMbBQT|IgA^G^8Q9ATKT|_F^m!RSz?i+`!H!sVNeg&o(>~m1*Y#@j);sFczz%PQ*au&ma9WP4@Egd=>qhDL{1~4p6J)R7@k|$LiRxsNvs<; zNO2YfP1N<^-&I~6GKpCdIBv8+(a!1>61!lYLIA~PhoYweGOFP`y z_q>y`;#4XZZIIJ&A`0~xzQ7QY5Jj^%2EGt~CNmB)VnUBsey}4k( zPmDB9%168~<)AnIfOWR1Bb7srIvb4JI9if1v2)8Xw}5NNXniTaY}=!{aWyZ%)O$4< z7ZEodf)Sa42}W%S`baP)`uMyY;)q4{H9={t;&~2}GMHQyP~Ba!2J;e3TE4shilOH{ za7nseGio5NX1A*g$(+w0!;Faci*eb)HLQBJ#R1bN5aW$dwg-8a!iM~T9|zrb!gULW zKByV*4(o$(Dd58j2pOo1(2)ECjux(ad$-Fbo~%GQ2xSkNQp!~`!2N-aI( zQ=NiwKHHxYcrlAfZ7(nOi?yz3f2${ieki4(l29`gj+`K3j_^6rpA|Lh7bSc3{q2W( z`jTWdwXP^D&ksN+N9m|dS1Y0Ovo zaMpodmVt?EQq1x$qt@;Bz*rrYI}^N>!ZD_*w2bR|%o3%Ha$8gcC9e3wJg$SzW$|s1 zAA}hsN=!9)%-qIlwxXA+G>i}Qo>i78ab~QVD8+&#h9$a-Hx^>eiF_oFwNwhzb0w)H zGzQmjs_LHkJ>)o;4#ISIl+ZSA;R24rLPd#*DQhw&xIz?UYJP2 z=UCOiTt$Dk>VoRn&i85mFdmd6%#%oiIkF3)H?Za~TSc3KMBj;3Ix3kH=<3 z(Ea6=h$ z6h4#ESUbh1U`nDXtB4GK9zP}s?{6{?UaekC=_n?!1Vu3vn0PkXC&*dUQBWa6d|<{U ziFLTwL8eadQFIs!B_)rsY0fAR&%pU+1^;WtOmwChE*UybwU%*CPa)u4{kHMjdPrFL&>t%P<5(B zjWc?W;0hN|uTs$FrCc^=K%Q~KR1n5h3&|-G;uM`(8*uZvUbSFD6Kbdgj>mD1@)w)I zBQ48XA0^>IOdqMKY@E`2;51AL8AhP7x=WB`udz}AwL%zDNv2NnaTaS5Pga3Z-ofLF zNx}+q6pbZ(Ai~3$)f<9LA5)Owf)`F7ttoAs)YE?6qh>4=PLmb0y<`HBU2Ya=1DZ^R zW~>aEV-Vj&q~i&g2>KOKbJ$cz9Up66?L`yQbuYuL#|js{FkCy}oz!u3MG|C8YBLlA zCkhj8Z!@3aEI2e8r^u#ZBFB88>mg^L=}It~b3{--C`r6*5t`9lB`Yl-2uN}SCYqt_ zF4hz{PU?HCREBmTk_b-5z<4k(K?x_yP^j=RlH%wHLrJA^*_I3)D#WoG{aF)PJt!7>;K62v17><^g0XO9rRIIf`-2*Ik&#?cx1t zTA<0CQG}pasE{P8XBs{{>j?!K;R0s;xhyWq)*OFpyk?Rzq3@OLFk+NVI0zmrFL@<2 zQz4UMsI+9rCOi@P3%sAN}b0=#ie3&K8w0hirn3jzr!BEB$l|~&Dp>4%8qp2K2vwk#edXFaX zStXlPaAw>VDtcTfWo0&UeHT461Be-V=nWNJxb; z=Dm3s6I@<@U<1-c*<};_LH=~jEc^&6tf&77F{F!L ze;6hiR>;6u1TGFVqajmFhOuUm(>1Iw-B|GX`URIrvdAN`1a29|pa3&L9!?ku2^50^ zVcLP35AuSf%8^6(zs4iDf_5PaBNi^OjU0T_--d=bRkMg1($x?fk%TW|CK6vflAtu! zfUjW)9;3O7!R26dPi4gzq6{aUKrwy>#m5u)84ovrNA7JF>~0@^Vtpa37d8_wuD?hz z2~yy(QlF3R*mV#tz3GKn|LK}VKx4(W;ubG=9o2&MxVy`GR6q# zW;2qp{<$y~pJ|y5GNGM4-5e|Ud0fH*xViuz!l)1L;YzMxndsO7@4QKHEoW=01E3$S zSnV(r6|`7$21(y5S~9&jSrPqP;8PA<#YBQf?VLthA>&~Ln%HZlTQ?o1B#?J$%B z8l?r`Bphk*A+Z`>F(UI52aGs_85lO$rR1vEjWQ zMO!ri#wah$cw`1oxYSTD9oyJ6tvO;+^%!d0IywUmqE_t(7 zg7GQL@(xZVb8={>z^vLH;JaZ?z!Mo42Bm|%OgP&{ECI4-YkDf^O)%}KC$V5m}(QkbpE1OQbPQ71eY<>&NT^gFPc}hiRV*$8$_plcQ`m8w^01&2SkM z7O2g*x_M*=80u()2_daS4!y9Wo8WQGmum7>K@Y%DSHT{=lZzTRtHcv@cT>>}$70}O zL$ZdM%5cI;Xi5m9MA6*+L{mT}!%UymuI=#qkfHN2L})RkBnObDOVMp81|#zXSq>8r zjgRM_*@HY$>x+|z_0}NIhL(2(@<_XZ4!exzi=w_rI~@oM8)q0m%uyMEZHC!+2RdF> z$R%i@duMPJQY6~L#nHA9=lBA)MVRXBc5L%E=cPRuau|{xgv(EzsTrShfAKU;l-XM=mglUixAHbDg z#O;>_XT!5lMiCj)Yp2DUhT`d(5&Aez48wwK!&J(~{WDGB6`I{?MbEc#c9xXjG#SX| zk^2m1P1>mF~g87^Ru;4Mgl&h8zZQ77Jz5I-h)w)zg=g_^0<2_25- z2M*QAh{X&hw^SjcVm)Yqt3sF{^sKTwfP5N)sTh0M0@DFZC@@pc8c0?Qa0AH?| z3cjlw*Q{l~D${mZr8CY9as$Q!)NIi(mtc0_0j#~Uv=&k=P~f=(=oc)S^O6^TIG>?> z0e27=aB;e3LVHTbU>Fqal$F9n7K6+_f|aVP;9=z~E+4Mq-S3VV6q1`^*5!6-Fr6Q0 zAlyC~jU_amubGOU*7Jh{zTI}pM&?kk*hgiE7NCPLl*wsM3ujRqo`d6AC=3MMSOF@C zVqX+hig$RS>uk*{(h@E*@0IFQn;{KgB|)>A7sg(JAquhj9IEBcsLJ+WntC}BbZ-w! z=#o-+A6H+hS^1pPq3}$Xw?hsy6mnsaMfJMWbda?V;8Iqx+=3|kR&X!}g-F1?2a0Yl zTxcoPEYzlSxUv~O50DNRVjH-q4=!)^Ano`#lFcXXbsSyh&G zvKh``VZI*Y>y*bY_zHQL)e>p9gbVVkh$6Xe2j+$;xDqqIV_#kVUeIIBc|7t!+m4JD zM1j9?nqZ^=7>|DSR z)_Ar0TMn0X$*3^PFu$E!z+i>#=QlTRQ+=|}8-VFBF8ma}SN*MvD>@7h7}yQ$sJCRS zw1ksx&Tr@4YM|Mesrt(w@8cWI#On*;JrE{4Wwd(UFh%t@Pd?D|1No*MGDk6*ls|*d zOjn6SAL^0j2&yNXg{sqxCH~?2H2?PbclcWiaKR&OH&#>qD-Hj@eHH5t{4JC_?)d8B z#Y>hfS+;E1iZyH3GIua{Exv0_=Uq#dEncx^*_yR0mTma2Y~(KkKmEIgwm)BVcOBb# z?|t9UntK!n!wJf4sl%v3UI9B}*1{bgX@8&$nO6*1xpp{BVTHR4;h>f0}>PyH&nHL>gI zo8$4WCmTMy`O)`QEIB!Pa6Ivkm*0H4z489yt512@)z3WD=5K3x>!+*E|FUIw*Xn_t z%@5UoM~yrk^F2ad@@ISG+_uNx{@1?6-&_>)ukZi)i}x=6ANzZM)`N4bUbCjp>2Kr;{NDn_OW$6t!oZ#;Z`pGW+c$Md&^U=R@{vOaUVo~wysf$KY2Vr3jJ~>IdDq6Z`(Me$R^HQm&tGPDL^~h(`~S=^xpex` z6>C^e+rgnD$zSLHX=Br#Oz(qh)_i}{KWy-?Ubga{ Date: Tue, 16 Dec 2025 09:35:51 -0800 Subject: [PATCH 109/356] Add amer & euro central rail hub art --- Art/Districts/1200/CentralRailHub_AMER.PCX | Bin 0 -> 15131 bytes Art/Districts/1200/CentralRailHub_EURO.PCX | Bin 0 -> 13735 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Art/Districts/1200/CentralRailHub_AMER.PCX create mode 100644 Art/Districts/1200/CentralRailHub_EURO.PCX diff --git a/Art/Districts/1200/CentralRailHub_AMER.PCX b/Art/Districts/1200/CentralRailHub_AMER.PCX new file mode 100644 index 0000000000000000000000000000000000000000..2a1c81b95ee454ecedd2cb6fc617b3e46b727f66 GIT binary patch literal 15131 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 Re`f!<{r}DXp+@u1{{?rIXO;i} literal 0 HcmV?d00001 From 3facb69adc5012d3723d23a9cc7933a2f8343ae0 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 16 Dec 2025 10:57:07 -0800 Subject: [PATCH 110/356] Allow multiple resource prereqs for districts --- Art/Districts/1200/CentralRailHub_MIDEAST.PCX | Bin 0 -> 73004 bytes C3X.h | 31 +-- injected_code.c | 200 +++++++++++++----- 3 files changed, 169 insertions(+), 62 deletions(-) create mode 100644 Art/Districts/1200/CentralRailHub_MIDEAST.PCX diff --git a/Art/Districts/1200/CentralRailHub_MIDEAST.PCX b/Art/Districts/1200/CentralRailHub_MIDEAST.PCX new file mode 100644 index 0000000000000000000000000000000000000000..bc7ad2bc99019bba421fc80c391683e0fb904e0e GIT binary patch literal 73004 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 PXwCOU?)X4W{`Y?YZe3K5 literal 0 HcmV?d00001 diff --git a/C3X.h b/C3X.h index 2919183c..f5f7f988 100644 --- a/C3X.h +++ b/C3X.h @@ -577,7 +577,7 @@ struct district_config { char const * display_name; char const * tooltip; char const * advance_prereq; - char const * resource_prereq; + char const * resource_prereqs[MAX_DISTRICT_DEPENDENTS]; char const * resource_prereq_on_tile; char const * dependent_improvements[MAX_DISTRICT_DEPENDENTS]; char const * img_paths[10]; @@ -587,6 +587,7 @@ struct district_config { bool vary_img_by_culture; bool is_dynamic; bool align_to_coast; + int resource_prereq_count; int dependent_improvement_count; int img_path_count; int max_building_index; @@ -664,46 +665,46 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP { .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", .display_name = "Neighborhood", - .advance_prereq = NULL, .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereq = NULL, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 5, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 25 - + }, { .command = UCV_Build_WonderDistrict, .name = "Wonder District", .tooltip = "Build Wonder District", .display_name = "Wonder District", - .advance_prereq = NULL, .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereq = NULL, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"WonderDistrict.pcx"}, .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Coast)), .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 - + }, { .command = UCV_Build_DistributionHub, .name = "Distribution Hub", .tooltip = "Build Distribution Hub", .display_name = "Distribution Hub", - .advance_prereq = "Construction", .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereq = "Construction", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"DistributionHub.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 2, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 - + }, { .command = UCV_Build_Aerodrome, .name = "Aerodrome", .tooltip = "Build Aerodrome", .display_name = "Aerodrome", - .advance_prereq = "Flight", .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 1, + .advance_prereq = "Flight", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 1, .img_paths = {"Aerodrome.pcx"}, .dependent_improvements = {"Airport"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 1, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 }, { .command = -1, .name = "Natural Wonder", .tooltip = NULL, .display_name = "Natural Wonder", - .advance_prereq = NULL, .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereq = NULL, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {0}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 0, .max_building_index = 0, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, @@ -712,7 +713,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", .display_name = "Port", - .advance_prereq = "Map Making", .resource_prereq = NULL, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .dependent_improvement_count = 2, .align_to_coast = true, + .advance_prereq = "Map Making", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 2, .align_to_coast = true, .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .dependent_improvements = {"Harbor", "Commercial Dock"}, .buildable_square_types_mask = (1 << SQ_Coast), .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .align_to_coast = true, @@ -725,10 +726,11 @@ struct parsed_district_definition { char * display_name; char * tooltip; char * advance_prereq; - char * resource_prereq; + char * resource_prereqs[5]; char * resource_prereq_on_tile; char * dependent_improvements[5]; char * img_paths[5]; + int resource_prereq_count; int dependent_improvement_count; int img_path_count; bool allow_multiple; @@ -748,6 +750,7 @@ struct parsed_district_definition { bool has_name; bool has_tooltip; bool has_advance_prereq; + bool has_resource_prereqs; bool has_dependent_improvements; bool has_display_name; bool has_img_paths; @@ -765,7 +768,6 @@ struct parsed_district_definition { bool has_shield_bonus; bool has_happiness_bonus; bool has_buildable_on; - bool has_resource_prereq; bool has_resource_prereq_on_tile; }; @@ -1629,7 +1631,8 @@ struct district_button_image_set { struct district_infos { int advance_prereq_id; // Tech ID that enables the district - int resource_prereq_id; + int resource_prereq_ids[MAX_DISTRICT_DEPENDENTS]; + int resource_prereq_count; int resource_prereq_on_tile_id; int dependent_building_count; int dependent_building_ids[MAX_DISTRICT_DEPENDENTS]; // Building types the district enables diff --git a/injected_code.c b/injected_code.c index 5709fc6d..fed21406 100644 --- a/injected_code.c +++ b/injected_code.c @@ -200,6 +200,7 @@ void __fastcall patch_Map_build_trade_network (Map * this); bool __fastcall patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value); bool __fastcall patch_Unit_can_pillage (Unit * this, int edx, int tile_x, int tile_y); bool __fastcall patch_City_has_resource (City * this, int edx, int resource_id); +bool city_can_build_district (City * city, int district_id); Tile * find_tile_for_district (City * city, int district_id, int * out_x, int * out_y); struct district_instance * get_district_instance (Tile * tile); bool city_has_required_district (City * city, int district_id); @@ -4962,10 +4963,14 @@ free_dynamic_district_config (struct district_config * cfg) free ((void *)cfg->advance_prereq); cfg->advance_prereq = NULL; } - if (cfg->resource_prereq != NULL) { - free ((void *)cfg->resource_prereq); - cfg->resource_prereq = NULL; + + for (int i = 0; i < 5; i++) { + if (cfg->resource_prereqs[i] != NULL) { + free ((void *)cfg->resource_prereqs[i]); + cfg->resource_prereqs[i] = NULL; + } } + if (cfg->resource_prereq_on_tile != NULL) { free ((void *)cfg->resource_prereq_on_tile); cfg->resource_prereq_on_tile = NULL; @@ -5063,10 +5068,16 @@ free_special_district_override_strings (struct district_config * cfg, struct dis free ((void *)cfg->advance_prereq); cfg->advance_prereq = NULL; } - if ((cfg->resource_prereq != NULL) && (cfg->resource_prereq != defaults->resource_prereq)) { - free ((void *)cfg->resource_prereq); - cfg->resource_prereq = NULL; + + for (int i = 0; i < ARRAY_LEN (cfg->resource_prereqs); i++) { + char const * default_value = (i < defaults->resource_prereq_count) ? defaults->resource_prereqs[i] : NULL; + if ((cfg->resource_prereqs[i] != NULL) && + (cfg->resource_prereqs[i] != default_value)) + free ((void *)cfg->resource_prereqs[i]); + cfg->resource_prereqs[i] = NULL; } + cfg->resource_prereq_count = defaults->resource_prereq_count; + if ((cfg->resource_prereq_on_tile != NULL) && (cfg->resource_prereq_on_tile != defaults->resource_prereq_on_tile)) { free ((void *)cfg->resource_prereq_on_tile); cfg->resource_prereq_on_tile = NULL; @@ -5184,10 +5195,15 @@ free_parsed_district_definition (struct parsed_district_definition * def) free (def->advance_prereq); def->advance_prereq = NULL; } - if (def->resource_prereq != NULL) { - free (def->resource_prereq); - def->resource_prereq = NULL; + + for (int i = 0; i < def->resource_prereq_count; i++) { + if (def->resource_prereqs[i] != NULL) { + free (def->resource_prereqs[i]); + def->resource_prereqs[i] = NULL; + } } + def->resource_prereq_count = 0; + if (def->resource_prereq_on_tile != NULL) { free (def->resource_prereq_on_tile); def->resource_prereq_on_tile = NULL; @@ -5451,12 +5467,26 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->advance_prereq = def->advance_prereq; def->advance_prereq = NULL; } - if (def->has_resource_prereq) { - if ((cfg->resource_prereq != NULL) && (cfg->resource_prereq != defaults->resource_prereq)) - free ((void *)cfg->resource_prereq); - cfg->resource_prereq = def->resource_prereq; - def->resource_prereq = NULL; + + if (def->has_resource_prereqs) { + for (int i = 0; i < ARRAY_LEN (cfg->resource_prereqs); i++) { + char const * default_value = (i < defaults->resource_prereq_count) ? defaults->resource_prereqs[i] : NULL; + if ((cfg->resource_prereqs[i] != NULL) && + (cfg->resource_prereqs[i] != default_value)) + free ((void *)cfg->resource_prereqs[i]); + cfg->resource_prereqs[i] = NULL; + } + + cfg->resource_prereq_count = def->resource_prereq_count; + const int max_entries = ARRAY_LEN (cfg->resource_prereqs); + if (cfg->resource_prereq_count > max_entries) + cfg->resource_prereq_count = max_entries; + for (int i = 0; i < cfg->resource_prereq_count; i++) { + cfg->resource_prereqs[i] = def->resource_prereqs[i]; + def->resource_prereqs[i] = NULL; + } } + if (def->has_resource_prereq_on_tile) { if ((cfg->resource_prereq_on_tile != NULL) && (cfg->resource_prereq_on_tile != defaults->resource_prereq_on_tile)) free ((void *)cfg->resource_prereq_on_tile); @@ -5599,10 +5629,16 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.advance_prereq = def->advance_prereq; def->advance_prereq = NULL; } - if (def->has_resource_prereq) { - new_cfg.resource_prereq = def->resource_prereq; - def->resource_prereq = NULL; + + new_cfg.resource_prereq_count = def->has_resource_prereqs ? def->resource_prereq_count : 0; + const int max_resource_entries = ARRAY_LEN (new_cfg.resource_prereqs); + if (new_cfg.resource_prereq_count > max_resource_entries) + new_cfg.resource_prereq_count = max_resource_entries; + for (int i = 0; i < new_cfg.resource_prereq_count; i++) { + new_cfg.resource_prereqs[i] = def->resource_prereqs[i]; + def->resource_prereqs[i] = NULL; } + if (def->has_resource_prereq_on_tile) { new_cfg.resource_prereq_on_tile = def->resource_prereq_on_tile; def->resource_prereq_on_tile = NULL; @@ -5730,13 +5766,23 @@ handle_district_definition_key (struct parsed_district_definition * def, def->advance_prereq = copy_trimmed_string_or_null (value, 1); def->has_advance_prereq = true; - } else if (slice_matches_str (key, "resource_prereq")) { - if (def->resource_prereq != NULL) { - free (def->resource_prereq); - def->resource_prereq = NULL; + } else if (slice_matches_str (key, "resource_prereqs")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->resource_prereqs, + ARRAY_LEN (def->resource_prereqs), + &list_count, + parse_errors, + line_number, + "resource_prereqs")) { + def->resource_prereq_count = list_count; + def->has_resource_prereqs = true; + } else { + def->resource_prereq_count = 0; + def->has_resource_prereqs = false; } - def->resource_prereq = copy_trimmed_string_or_null (value, 1); - def->has_resource_prereq = true; + free (value_text); } else if (slice_matches_str (key, "resource_prereq_on_tile")) { if (def->resource_prereq_on_tile != NULL) { @@ -7051,7 +7097,9 @@ void parse_building_and_tech_ids () if (is->district_configs[i].command != 0) itable_insert (&is->command_id_to_district_id, is->district_configs[i].command, i); is->district_infos[i].advance_prereq_id = -1; - is->district_infos[i].resource_prereq_id = -1; + is->district_infos[i].resource_prereq_count = 0; + for (int j = 0; j < MAX_DISTRICT_DEPENDENTS; j++) + is->district_infos[i].resource_prereq_ids[j] = -1; is->district_infos[i].resource_prereq_on_tile_id = -1; // Map advance prereqs to districts @@ -7067,17 +7115,24 @@ void parse_building_and_tech_ids () is->district_infos[i].advance_prereq_id = -1; } } - if (is->district_configs[i].resource_prereq != NULL && is->district_configs[i].resource_prereq != "") { + + // Map resource prereqs to districts (multiple resources now supported) + int stored_res_count = 0; + for (int j = 0; j < is->district_configs[i].resource_prereq_count; j++) { + if (is->district_configs[i].resource_prereqs[j] == "" || is->district_configs[i].resource_prereqs[j] == NULL) + continue; int res_id; - struct string_slice res_name = { .str = (char *)is->district_configs[i].resource_prereq, .len = (int)strlen (is->district_configs[i].resource_prereq) }; + struct string_slice res_name = { .str = (char *)is->district_configs[i].resource_prereqs[j], .len = (int)strlen (is->district_configs[i].resource_prereqs[j]) }; if (find_game_object_id_by_name (GOK_RESOURCE, &res_name, 0, &res_id)) { - snprintf (ss, sizeof ss, "Found resource prereq \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, is->district_configs[i].resource_prereq, res_id); + snprintf (ss, sizeof ss, "Found resource prereq \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, is->district_configs[i].resource_prereqs[j], res_id); (*p_OutputDebugStringA) (ss); - is->district_infos[i].resource_prereq_id = res_id; - } else { - is->district_infos[i].resource_prereq_id = -1; + if (stored_res_count < ARRAY_LEN (is->district_infos[i].resource_prereq_ids)) { + is->district_infos[i].resource_prereq_ids[stored_res_count] = res_id; + stored_res_count++; + } } } + is->district_infos[i].resource_prereq_count = stored_res_count; if (is->district_configs[i].resource_prereq_on_tile != NULL && is->district_configs[i].resource_prereq_on_tile != "") { int res_id; struct string_slice res_name = { .str = (char *)is->district_configs[i].resource_prereq_on_tile, .len = (int)strlen (is->district_configs[i].resource_prereq_on_tile) }; @@ -7939,7 +7994,9 @@ reset_district_state (bool reset_tile_map) for (int i = 0; i < COUNT_DISTRICT_TYPES; i++) { is->district_infos[i].advance_prereq_id = -1; - is->district_infos[i].resource_prereq_id = -1; + is->district_infos[i].resource_prereq_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].resource_prereq_ids); j++) + is->district_infos[i].resource_prereq_ids[j] = -1; is->district_infos[i].resource_prereq_on_tile_id = -1; is->district_infos[i].dependent_building_count = 0; for (int j = 0; j < ARRAY_LEN (is->district_infos[i].dependent_building_ids); j++) @@ -7996,26 +8053,46 @@ district_resource_prereqs_met (Tile * tile, int tile_x, int tile_y, int district return false; } - int resource_req = info->resource_prereq_id; - if (resource_req < 0) + // If no resource prereqs, then the check passes + if (info->resource_prereq_count <= 0) return true; int owner = tile->vtable->m38_Get_Territory_OwnerID (tile); if (owner < 0) return false; - if ((city != NULL) && - (city->Body.CivID == owner) && - city_radius_contains_tile (city, tile_x, tile_y) && - patch_City_has_resource (city, __, resource_req)) - return true; + // Check all resource prereqs - ALL must be present (AND logic) + for (int i = 0; i < info->resource_prereq_count; i++) { + int resource_req = info->resource_prereq_ids[i]; + if (resource_req < 0) + continue; - FOR_CITIES_AROUND (wai, tile_x, tile_y) { - if (patch_City_has_resource (wai.city, __, resource_req)) - return true; + bool has_resource = false; + + // Check if the specified city has this resource + if ((city != NULL) && + (city->Body.CivID == owner) && + city_radius_contains_tile (city, tile_x, tile_y) && + patch_City_has_resource (city, __, resource_req)) { + has_resource = true; + } + + // Check cities around this tile for the resource + if (!has_resource) { + FOR_CITIES_AROUND (wai, tile_x, tile_y) { + if (patch_City_has_resource (wai.city, __, resource_req)) { + has_resource = true; + break; + } + } + } + + // If this required resource is not available, the check fails + if (!has_resource) + return false; } - return false; + return true; } bool @@ -8539,6 +8616,34 @@ find_tile_for_distribution_hub_district (City * city, int * out_x, int * out_y) return best_tile; } +bool +city_can_build_district (City * city, int district_id) +{ + if ((city == NULL) || (district_id < 0) || (district_id >= is->district_count)) + return false; + + struct district_config const * cfg = &is->district_configs[district_id]; + struct district_infos const * info = &is->district_infos[district_id]; + + // Check tech prerequisite + int prereq_id = info->advance_prereq_id; + if ((prereq_id >= 0) && ! Leader_has_tech (&leaders[city->Body.CivID], __, prereq_id)) + return false; + + // Check resource prerequisites (city connection) - ALL must be present + for (int i = 0; i < info->resource_prereq_count; i++) { + int resource_req = info->resource_prereq_ids[i]; + if ((resource_req >= 0) && ! patch_City_has_resource (city, __, resource_req)) + return false; + } + + // Check if district does not allow multiple and city already has one + if (! cfg->allow_multiple && city_has_required_district (city, district_id)) + return false; + + return true; +} + Tile * find_tile_for_district (City * city, int district_id, int * out_x, int * out_y) { @@ -8547,6 +8652,10 @@ find_tile_for_district (City * city, int district_id, int * out_x, int * out_y) if ((district_id < 0) || (district_id >= is->district_count)) return NULL; + // Check if city can build this district at all + if (! city_can_build_district (city, district_id)) + return NULL; + if (district_id == NEIGHBORHOOD_DISTRICT_ID) return find_tile_for_neighborhood_district (city, out_x, out_y); if (district_id == DISTRIBUTION_HUB_DISTRICT_ID) @@ -19757,7 +19866,6 @@ patch_Leader_do_production_phase (Leader * this) struct district_config * cfg = &is->district_configs[district_id]; struct district_infos * info = &is->district_infos[district_id]; - if (! cfg->is_dynamic) continue; if (info->dependent_building_count > 0) continue; if (cfg->command == -1) continue; @@ -19788,14 +19896,10 @@ patch_Leader_do_production_phase (Leader * this) if (ai_player && (auto_dynamic_district_count > 0)) { for (int i = 0; i < auto_dynamic_district_count; i++) { int district_id = auto_dynamic_district_ids[i]; - struct district_infos * info = &is->district_infos[district_id]; - if (city_has_required_district (city, district_id)) continue; + if (! city_can_build_district (city, district_id)) continue; if (find_pending_district_request (city, district_id) != NULL) continue; - if ((info->resource_prereq_id >= 0) && (! patch_City_has_resource (city, __, info->resource_prereq_id))) - continue; - int target_x = 0, target_y = 0; if (find_tile_for_district (city, district_id, &target_x, &target_y) == NULL) continue; From ef1c8b2a26796f343efb1b913b1edda1d97e822b Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 16 Dec 2025 12:25:41 -0800 Subject: [PATCH 111/356] Add mideast and roman central rail hub art --- Art/Districts/1200/CentralRailHub_ASIAN.PCX | Bin 13708 -> 13693 bytes Art/Districts/1200/CentralRailHub_MIDEAST.PCX | Bin 73004 -> 14086 bytes Art/Districts/1200/CentralRailHub_ROMAN.PCX | Bin 0 -> 15223 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Art/Districts/1200/CentralRailHub_ROMAN.PCX diff --git a/Art/Districts/1200/CentralRailHub_ASIAN.PCX b/Art/Districts/1200/CentralRailHub_ASIAN.PCX index 56bb189bbe77cf80599c0599865c69ea540e1d2f..fa121ee828d32cbf35c04bbd6af4644e17130c78 100644 GIT binary patch delta 98 zcmeCl{+qRdN1gG;W`1=87REc19krVoZ%^h|7oB`Z`#$5X$&M_dlP~EcGTxkgUrKbc zx2_N4&CRoPcQOK%@+*r@&eFF4Di>rGoxD^3J5bRA8PUln4E{6Tm~0>`y7`{rI%WV~ CktUP? delta 109 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 K&1V|s@Baf*mrT_i* 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 PXwCOU?)X4W{`Y?YZe3K5 diff --git a/Art/Districts/1200/CentralRailHub_ROMAN.PCX b/Art/Districts/1200/CentralRailHub_ROMAN.PCX new file mode 100644 index 0000000000000000000000000000000000000000..d2281b93613933a0f3d36d0ff957430f9e69ca2a GIT binary patch literal 15223 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 literal 0 HcmV?d00001 From afcd46bc9e783844249141d226f1368afd58f812 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 16 Dec 2025 12:42:49 -0800 Subject: [PATCH 112/356] Add central rail hub --- C3X.h | 16 ++++++++++++++-- Civ3Conquests.h | 1 + default.c3x_config.ini | 4 ++++ injected_code.c | 6 ++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/C3X.h b/C3X.h index f5f7f988..d985a30b 100644 --- a/C3X.h +++ b/C3X.h @@ -17,7 +17,7 @@ typedef unsigned char byte; #define MAX_BUILDING_PREREQS_FOR_UNIT 10 #define COUNT_SPECIAL_DISTRICT_TYPES 10 -#define USED_SPECIAL_DISTRICT_TYPES 6 +#define USED_SPECIAL_DISTRICT_TYPES 7 #define MAX_DYNAMIC_DISTRICT_TYPES 22 #define COUNT_DISTRICT_TYPES (COUNT_SPECIAL_DISTRICT_TYPES + MAX_DYNAMIC_DISTRICT_TYPES) #define MAX_WONDER_DISTRICT_TYPES 32 @@ -344,6 +344,7 @@ struct c3x_config { bool enable_distribution_hub_districts; bool enable_aerodrome_districts; bool enable_port_districts; + bool enable_central_rail_hub_districts; bool cities_with_mutual_district_receive_buildings; bool cities_with_mutual_district_receive_wonders; @@ -365,6 +366,8 @@ struct c3x_config { int distribution_hub_shield_yield_divisor; int ai_distribution_hub_build_strategy; int ai_ideal_distribution_hub_count_per_100_cities; + int central_rail_hub_distribution_food_bonus_percent; + int central_rail_hub_distribution_shield_bonus_percent; bool workers_can_enter_coast; @@ -715,9 +718,18 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .display_name = "Port", .advance_prereq = "Map Making", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 2, .align_to_coast = true, .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .dependent_improvements = {"Harbor", "Commercial Dock"}, - .buildable_square_types_mask = (1 << SQ_Coast), + .buildable_square_types_mask = (1 << SQ_Coast), .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .align_to_coast = true, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 + }, + { + .command = UCV_Build_CentralRailHub, .name = "Central Rail Hub", .tooltip = "Build Central Rail Hub", + .display_name = "Central Rail Hub", + .advance_prereq = "Steam Power", .resource_prereqs = {"Coal", "Iron"}, .resource_prereq_on_tile = NULL, .allow_multiple = false, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 2, .dependent_improvement_count = 0, + .img_paths = {"CentralRailHub_AMER.pcx", "CentralRailHub_EURO.pcx", "CentralRailHub_ROMAN.pcx", "CentralRailHub_MIDEAST.pcx", "CentralRailHub_ASIAN.pcx"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, + .img_path_count = 5, .max_building_index = 0, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 } }; diff --git a/Civ3Conquests.h b/Civ3Conquests.h index fe5c1f02..9797547c 100644 --- a/Civ3Conquests.h +++ b/Civ3Conquests.h @@ -773,6 +773,7 @@ enum Unit_Command_Values UCV_Build_DistributionHub = -10000003, UCV_Build_Aerodrome = -10000004, UCV_Build_Port = -10000005, + UCV_Build_CentralRailHub = -10000006, }; enum Unit_Mode_Actions diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 3f10545d..4ac11d06 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -831,6 +831,7 @@ enable_wonder_districts = false enable_distribution_hub_districts = false enable_aerodrome_districts = false enable_port_districts = false +enable_central_rail_hub_districts = false ; When multiple cities share a district (i.e., the same district tile is within multiple cities' work radii), these options control whether those ; cities automatically share the benefits of buildings and wonders constructed in that district. For example, if Rome and Veii both have the same @@ -889,6 +890,9 @@ distribution_hub_food_yield_divisor = 3 distribution_hub_shield_yield_divisor = 4 ai_ideal_distribution_hub_count_per_100_cities = 25 +central_rail_hub_distribution_food_bonus_percent = 25 +central_rail_hub_distribution_shield_bonus_percent = 25 + workers_can_enter_coast = true ; When enabled, AI defensive units will actively seek out and defend districts within their territory, treating them as valuable assets like colonies. diff --git a/injected_code.c b/injected_code.c index fed21406..8b06db50 100644 --- a/injected_code.c +++ b/injected_code.c @@ -72,6 +72,7 @@ struct injected_state * is = ADDR_INJECTED_STATE; #define AERODROME_DISTRICT_ID 3 #define NATURAL_WONDER_DISTRICT_ID 4 #define PORT_DISTRICT_ID 5 +#define CENTRAL_RAIL_HUB_DISTRICT_ID 6 char const * const hotseat_replay_save_path = "Saves\\Auto\\ai-move-replay-before-interturn.SAV"; char const * const hotseat_resume_save_path = "Saves\\Auto\\ai-move-replay-resume.SAV"; @@ -4404,6 +4405,11 @@ get_distribution_hub_yields_for_city (City * city, int * out_food, int * out_shi } } + if (city_has_required_district (city, CENTRAL_RAIL_HUB_DISTRICT_ID)) { + food += (food * is->current_config.central_rail_hub_distribution_food_bonus_percent) / 100; + shields += (shields * is->current_config.central_rail_hub_distribution_shield_bonus_percent) / 100; + } + if (out_food != NULL) *out_food = food; if (out_shields != NULL) From 0551d73a7b8b4fff5eb5942b2a7c0ff899b4e5e3 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 16 Dec 2025 13:56:55 -0800 Subject: [PATCH 113/356] Allow districts to generate resources, same as mills --- C3X.h | 8 ++ injected_code.c | 233 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 210 insertions(+), 31 deletions(-) diff --git a/C3X.h b/C3X.h index d985a30b..58f9f1fd 100644 --- a/C3X.h +++ b/C3X.h @@ -603,6 +603,9 @@ struct district_config { int shield_bonus; int happiness_bonus; int defense_bonus_percent; + char const * generated_resource; + int generated_resource_id; + short generated_resource_flags; }; struct wonder_district_config { @@ -781,6 +784,11 @@ struct parsed_district_definition { bool has_happiness_bonus; bool has_buildable_on; bool has_resource_prereq_on_tile; + char * generated_resource; + char * generated_resource_settings[5]; + int generated_resource_settings_count; + bool has_generated_resource; + bool has_generated_resource_settings; }; struct parsed_wonder_definition { diff --git a/injected_code.c b/injected_code.c index 8b06db50..beece9b8 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5231,6 +5231,19 @@ free_parsed_district_definition (struct parsed_district_definition * def) } def->img_path_count = 0; + if (def->generated_resource != NULL) { + free (def->generated_resource); + def->generated_resource = NULL; + } + + for (int i = 0; i < def->generated_resource_settings_count; i++) { + if (def->generated_resource_settings[i] != NULL) { + free (def->generated_resource_settings[i]); + def->generated_resource_settings[i] = NULL; + } + } + def->generated_resource_settings_count = 0; + init_parsed_district_definition (def); } @@ -5529,6 +5542,28 @@ override_special_district_from_definition (struct parsed_district_definition * d if (def->has_buildable_on) cfg->buildable_square_types_mask = def->buildable_square_types_mask; + if (def->has_generated_resource) { + if ((cfg->generated_resource != NULL) && (cfg->generated_resource != defaults->generated_resource)) + free ((void *)cfg->generated_resource); + cfg->generated_resource = def->generated_resource; + def->generated_resource = NULL; + cfg->generated_resource_flags = 0; + if (def->has_generated_resource_settings) { + for (int i = 0; i < def->generated_resource_settings_count; i++) { + char * setting = def->generated_resource_settings[i]; + if (strcmp (setting, "local") == 0) + cfg->generated_resource_flags |= MF_LOCAL; + else if (strcmp (setting, "no-tech-req") == 0) + cfg->generated_resource_flags |= MF_NO_TECH_REQ; + else if (strcmp (setting, "show-bonus") == 0) + cfg->generated_resource_flags |= MF_SHOW_BONUS; + else if (strcmp (setting, "hide-non-bonus") == 0) + cfg->generated_resource_flags |= MF_HIDE_NON_BONUS; + } + } + cfg->generated_resource_id = -1; + } + if (def->has_dependent_improvements) { for (int i = 0; i < ARRAY_LEN (cfg->dependent_improvements); i++) { char const * default_value = (i < defaults->dependent_improvement_count) ? defaults->dependent_improvements[i] : NULL; @@ -5665,6 +5700,30 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.happiness_bonus = def->has_happiness_bonus ? def->happiness_bonus : 0; new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); + if (def->has_generated_resource) { + new_cfg.generated_resource = def->generated_resource; + def->generated_resource = NULL; + new_cfg.generated_resource_flags = 0; + if (def->has_generated_resource_settings) { + for (int i = 0; i < def->generated_resource_settings_count; i++) { + char * setting = def->generated_resource_settings[i]; + if (strcmp (setting, "local") == 0) + new_cfg.generated_resource_flags |= MF_LOCAL; + else if (strcmp (setting, "no-tech-req") == 0) + new_cfg.generated_resource_flags |= MF_NO_TECH_REQ; + else if (strcmp (setting, "show-bonus") == 0) + new_cfg.generated_resource_flags |= MF_SHOW_BONUS; + else if (strcmp (setting, "hide-non-bonus") == 0) + new_cfg.generated_resource_flags |= MF_HIDE_NON_BONUS; + } + } + new_cfg.generated_resource_id = -1; + } else { + new_cfg.generated_resource = NULL; + new_cfg.generated_resource_id = -1; + new_cfg.generated_resource_flags = 0; + } + new_cfg.dependent_improvement_count = def->has_dependent_improvements ? def->dependent_improvement_count : 0; const int max_dependent_entries = ARRAY_LEN (is->district_configs[0].dependent_improvements); if (new_cfg.dependent_improvement_count > max_dependent_entries) @@ -5958,6 +6017,32 @@ handle_district_definition_key (struct parsed_district_definition * def, } else add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + } else if (slice_matches_str (key, "generated_resource")) { + if (def->generated_resource != NULL) { + free (def->generated_resource); + def->generated_resource = NULL; + } + def->generated_resource = copy_trimmed_string_or_null (value, 1); + def->has_generated_resource = true; + + } else if (slice_matches_str (key, "generated_resource_settings")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->generated_resource_settings, + ARRAY_LEN (def->generated_resource_settings), + &list_count, + parse_errors, + line_number, + "generated_resource_settings")) { + def->generated_resource_settings_count = list_count; + def->has_generated_resource_settings = true; + } else { + def->generated_resource_settings_count = 0; + def->has_generated_resource_settings = false; + } + free (value_text); + } else add_unrecognized_key_error (unrecognized_keys, line_number, key); } @@ -7151,6 +7236,19 @@ void parse_building_and_tech_ids () } } + // Resolve generated resource name to ID + if (is->district_configs[i].generated_resource != NULL && is->district_configs[i].generated_resource != "") { + int res_id; + struct string_slice res_name = { .str = (char *)is->district_configs[i].generated_resource, .len = (int)strlen (is->district_configs[i].generated_resource) }; + if (find_game_object_id_by_name (GOK_RESOURCE, &res_name, 0, &res_id)) { + snprintf (ss, sizeof ss, "Found generated resource \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, is->district_configs[i].generated_resource, res_id); + (*p_OutputDebugStringA) (ss); + is->district_configs[i].generated_resource_id = res_id; + } else { + is->district_configs[i].generated_resource_id = -1; + } + } + // Map improvement prereqs to districts int stored_count = 0; for (int j = 0; j < is->district_configs[i].dependent_improvement_count; j++) { @@ -9775,6 +9873,24 @@ can_generate_resource (int for_civ_id, struct mill * mill) return (req_tech_id < 0) || Leader_has_tech (&leaders[for_civ_id], __, req_tech_id); } +bool +district_can_generate_resource (int for_civ_id, struct district_config * dc) +{ + if (dc->generated_resource_id < 0) + return false; + int req_tech_id = (dc->generated_resource_flags & MF_NO_TECH_REQ) ? -1 : p_bic_data->ResourceTypes[dc->generated_resource_id].RequireID; + return (req_tech_id < 0) || Leader_has_tech (&leaders[for_civ_id], __, req_tech_id); +} + +bool +district_is_complete_and_owned_by_civ (struct district_instance * di, Tile * tile, int civ_id) +{ + if (di == NULL || di->state != DS_COMPLETED) + return false; + int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); + return owner_id == civ_id; +} + void init_unit_type_count (Leader * leader) { @@ -10018,6 +10134,24 @@ patch_City_has_resource (City * this, int edx, int resource_id) } } + // Check if access to this resource is provided by a district in the city's work radius + if ((! tr) && is->current_config.enable_districts) { + FOR_DISTRICTS_AROUND (wai, this->Body.X, this->Body.Y, true) { + struct district_instance * di = wai.district_inst; + int district_id = di->district_type; + if ((district_id < 0) || (district_id >= is->district_count)) + continue; + + struct district_config * dc = &is->district_configs[district_id]; + if ((dc->generated_resource_id == resource_id) && + (dc->generated_resource_flags & MF_LOCAL) && + district_can_generate_resource (this->Body.CivID, dc)) { + tr = true; + break; + } + } + } + return tr; } @@ -10247,9 +10381,7 @@ Tile * __fastcall patch_Map_get_tile_when_recomputing_resources_5 (Map * map, in int __fastcall patch_Tile_get_visible_resource_when_recomputing (Tile * tile, int edx, int civ_id) { - if (is->got_mill_tile == NULL) - return Tile_get_resource_visible_to (tile, __, civ_id); - else { + if (is->got_mill_tile != NULL) { struct mill_tile * mt = is->got_mill_tile; is->got_mill_tile = NULL; if (has_resources_required_by_building (mt->city, mt->mill->improv_id)) @@ -10257,6 +10389,23 @@ patch_Tile_get_visible_resource_when_recomputing (Tile * tile, int edx, int civ_ else return -1; } + + int base_resource = Tile_get_resource_visible_to (tile, __, civ_id); + + if ((base_resource < 0) && is->current_config.enable_districts) { + struct district_instance * inst = get_district_instance (tile); + if (inst != NULL && inst->state == DS_COMPLETED) { + int district_id = inst->district_type; + struct district_config * cfg = &is->district_configs[district_id]; + if ((cfg->generated_resource_id >= 0) && ((cfg->generated_resource_flags & MF_LOCAL) == 0)) { + int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); + if ((owner_id == civ_id) && district_can_generate_resource (civ_id, cfg)) + return cfg->generated_resource_id; + } + } + } + + return base_resource; } int WINAPI @@ -11935,6 +12084,7 @@ patch_init_floating_point () {"enable_distribution_hub_districts" , false, offsetof (struct c3x_config, enable_distribution_hub_districts)}, {"enable_aerodrome_districts" , false, offsetof (struct c3x_config, enable_aerodrome_districts)}, {"enable_port_districts" , false, offsetof (struct c3x_config, enable_port_districts)}, + {"enable_central_rail_hub_districts" , false, offsetof (struct c3x_config, enable_central_rail_hub_districts)}, {"completed_wonder_districts_can_be_destroyed" , false, offsetof (struct c3x_config, completed_wonder_districts_can_be_destroyed)}, {"destroyed_wonders_can_be_built_again" , false, offsetof (struct c3x_config, destroyed_wonders_can_be_built_again)}, {"cities_with_mutual_district_receive_buildings" , false, offsetof (struct c3x_config, cities_with_mutual_district_receive_buildings)}, @@ -11956,34 +12106,36 @@ patch_init_floating_point () int base_val; int offset; } integer_config_options[] = { - {"limit_railroad_movement" , 0, offsetof (struct c3x_config, limit_railroad_movement)}, - {"minimum_city_separation" , 1, offsetof (struct c3x_config, minimum_city_separation)}, - {"anarchy_length_percent" , 100, offsetof (struct c3x_config, anarchy_length_percent)}, - {"ai_multi_city_start" , 0, offsetof (struct c3x_config, ai_multi_city_start)}, - {"max_tries_to_place_fp_city" , 10000, offsetof (struct c3x_config, max_tries_to_place_fp_city)}, - {"ai_research_multiplier" , 100, offsetof (struct c3x_config, ai_research_multiplier)}, - {"ai_settler_perfume_on_founding_duration" , 0, offsetof (struct c3x_config, ai_settler_perfume_on_founding_duration)}, - {"extra_unit_maintenance_per_shields" , 0, offsetof (struct c3x_config, extra_unit_maintenance_per_shields)}, - {"ai_build_artillery_ratio" , 16, offsetof (struct c3x_config, ai_build_artillery_ratio)}, - {"ai_artillery_value_damage_percent" , 50, offsetof (struct c3x_config, ai_artillery_value_damage_percent)}, - {"ai_build_bomber_ratio" , 70, offsetof (struct c3x_config, ai_build_bomber_ratio)}, - {"max_ai_naval_escorts" , 3, offsetof (struct c3x_config, max_ai_naval_escorts)}, - {"ai_worker_requirement_percent" , 150, offsetof (struct c3x_config, ai_worker_requirement_percent)}, - {"chance_for_nukes_to_destroy_max_one_hp_units" , 100, offsetof (struct c3x_config, chance_for_nukes_to_destroy_max_one_hp_units)}, - {"rebase_range_multiplier" , 6, offsetof (struct c3x_config, rebase_range_multiplier)}, - {"elapsed_minutes_per_day_night_hour_transition" , 3, offsetof (struct c3x_config, elapsed_minutes_per_day_night_hour_transition)}, - {"fixed_hours_per_turn_for_day_night_cycle" , 1, offsetof (struct c3x_config, fixed_hours_per_turn_for_day_night_cycle)}, - {"pinned_hour_for_day_night_cycle" , 0, offsetof (struct c3x_config, pinned_hour_for_day_night_cycle)}, - {"years_to_double_building_culture" , 1000, offsetof (struct c3x_config, years_to_double_building_culture)}, - {"tourism_time_scale_percent" , 100, offsetof (struct c3x_config, tourism_time_scale_percent)}, - {"city_limit" , 2048, offsetof (struct c3x_config, city_limit)}, - {"maximum_pop_before_neighborhood_needed" , 8, offsetof (struct c3x_config, maximum_pop_before_neighborhood_needed)}, - {"per_neighborhood_pop_growth_enabled" , 2, offsetof (struct c3x_config, per_neighborhood_pop_growth_enabled)}, - {"minimum_natural_wonder_separation" , 10, offsetof (struct c3x_config, minimum_natural_wonder_separation)}, - {"distribution_hub_food_yield_divisor" , 1, offsetof (struct c3x_config, distribution_hub_food_yield_divisor)}, - {"distribution_hub_shield_yield_divisor" , 1, offsetof (struct c3x_config, distribution_hub_shield_yield_divisor)}, - {"ai_ideal_distribution_hub_count_per_100_cities", 1, offsetof (struct c3x_config, ai_ideal_distribution_hub_count_per_100_cities)}, - {"neighborhood_needed_message_frequency" , 4, offsetof (struct c3x_config, neighborhood_needed_message_frequency)}, + {"limit_railroad_movement" , 0, offsetof (struct c3x_config, limit_railroad_movement)}, + {"minimum_city_separation" , 1, offsetof (struct c3x_config, minimum_city_separation)}, + {"anarchy_length_percent" , 100, offsetof (struct c3x_config, anarchy_length_percent)}, + {"ai_multi_city_start" , 0, offsetof (struct c3x_config, ai_multi_city_start)}, + {"max_tries_to_place_fp_city" , 10000, offsetof (struct c3x_config, max_tries_to_place_fp_city)}, + {"ai_research_multiplier" , 100, offsetof (struct c3x_config, ai_research_multiplier)}, + {"ai_settler_perfume_on_founding_duration" , 0, offsetof (struct c3x_config, ai_settler_perfume_on_founding_duration)}, + {"extra_unit_maintenance_per_shields" , 0, offsetof (struct c3x_config, extra_unit_maintenance_per_shields)}, + {"ai_build_artillery_ratio" , 16, offsetof (struct c3x_config, ai_build_artillery_ratio)}, + {"ai_artillery_value_damage_percent" , 50, offsetof (struct c3x_config, ai_artillery_value_damage_percent)}, + {"ai_build_bomber_ratio" , 70, offsetof (struct c3x_config, ai_build_bomber_ratio)}, + {"max_ai_naval_escorts" , 3, offsetof (struct c3x_config, max_ai_naval_escorts)}, + {"ai_worker_requirement_percent" , 150, offsetof (struct c3x_config, ai_worker_requirement_percent)}, + {"chance_for_nukes_to_destroy_max_one_hp_units" , 100, offsetof (struct c3x_config, chance_for_nukes_to_destroy_max_one_hp_units)}, + {"rebase_range_multiplier" , 6, offsetof (struct c3x_config, rebase_range_multiplier)}, + {"elapsed_minutes_per_day_night_hour_transition" , 3, offsetof (struct c3x_config, elapsed_minutes_per_day_night_hour_transition)}, + {"fixed_hours_per_turn_for_day_night_cycle" , 1, offsetof (struct c3x_config, fixed_hours_per_turn_for_day_night_cycle)}, + {"pinned_hour_for_day_night_cycle" , 0, offsetof (struct c3x_config, pinned_hour_for_day_night_cycle)}, + {"years_to_double_building_culture" , 1000, offsetof (struct c3x_config, years_to_double_building_culture)}, + {"tourism_time_scale_percent" , 100, offsetof (struct c3x_config, tourism_time_scale_percent)}, + {"city_limit" , 2048, offsetof (struct c3x_config, city_limit)}, + {"maximum_pop_before_neighborhood_needed" , 8, offsetof (struct c3x_config, maximum_pop_before_neighborhood_needed)}, + {"per_neighborhood_pop_growth_enabled" , 2, offsetof (struct c3x_config, per_neighborhood_pop_growth_enabled)}, + {"minimum_natural_wonder_separation" , 10, offsetof (struct c3x_config, minimum_natural_wonder_separation)}, + {"distribution_hub_food_yield_divisor" , 1, offsetof (struct c3x_config, distribution_hub_food_yield_divisor)}, + {"distribution_hub_shield_yield_divisor" , 1, offsetof (struct c3x_config, distribution_hub_shield_yield_divisor)}, + {"ai_ideal_distribution_hub_count_per_100_cities" , 1, offsetof (struct c3x_config, ai_ideal_distribution_hub_count_per_100_cities)}, + {"central_rail_hub_distribution_food_bonus_percent" , 25, offsetof (struct c3x_config, central_rail_hub_distribution_food_bonus_percent)}, + {"central_rail_hub_distribution_shield_bonus_percent", 25, offsetof (struct c3x_config, central_rail_hub_distribution_shield_bonus_percent)}, + {"neighborhood_needed_message_frequency" , 4, offsetof (struct c3x_config, neighborhood_needed_message_frequency)}, }; is->kernel32 = (*p_GetModuleHandleA) ("kernel32.dll"); @@ -21133,6 +21285,25 @@ patch_Sprite_draw_improv_img_on_city_form (Sprite * this, int edx, PCX_Image * c generated_resources[generated_resource_count++] = mill->resource_id; } + if (is->current_config.enable_districts && (generated_resource_count < ARRAY_LEN (generated_resources))) { + FOR_DISTRICTS_AROUND (wai, p_city_form->CurrentCity->Body.X, p_city_form->CurrentCity->Body.Y, true) { + struct district_instance * di = wai.district_inst; + int district_id = di->district_type; + if ((district_id < 0) || (district_id >= is->district_count)) + continue; + + struct district_config * dc = &is->district_configs[district_id]; + if ((dc->generated_resource_id >= 0) && + ((dc->generated_resource_flags & MF_SHOW_BONUS) || (p_bic_data->ResourceTypes[dc->generated_resource_id].Class != RC_Bonus)) && + (! ((dc->generated_resource_flags & MF_HIDE_NON_BONUS) && (p_bic_data->ResourceTypes[dc->generated_resource_id].Class != RC_Bonus))) && + district_can_generate_resource (p_city_form->CurrentCity->Body.CivID, dc)) { + generated_resources[generated_resource_count++] = dc->generated_resource_id; + if (generated_resource_count >= ARRAY_LEN (generated_resources)) + break; + } + } + } + if ((generated_resource_count > 0) && (is->resources_sheet != NULL) && (is->resources_sheet->JGL.Image != NULL) && (TransparentBlt != NULL)) { JGL_Image * jgl_canvas = canvas->JGL.Image, * jgl_sheet = is->resources_sheet->JGL.Image; From 8c7cff7e61bd9180dc3049c3a155a9a4ad579e78 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 16 Dec 2025 14:35:32 -0800 Subject: [PATCH 114/356] Add support for river alignment with districts and wonders (Hoover Dam use case) --- injected_code.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/injected_code.c b/injected_code.c index beece9b8..6a945385 100644 --- a/injected_code.c +++ b/injected_code.c @@ -3035,6 +3035,29 @@ direction_to_neighbor_bit (enum direction dir) } } +bool +get_primary_river_direction (Tile * tile, enum direction * out_dir) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return false; + + char river_bits = tile->vtable->m37_Get_River_Code (tile); + if (river_bits == 0) + return false; + + enum direction dirs[] = {DIR_E, DIR_W, DIR_SE, DIR_NE, DIR_SW, DIR_NW, DIR_S, DIR_N}; + for (int i = 0; i < (int)ARRAY_LEN (dirs); i++) { + int bit = direction_to_neighbor_bit (dirs[i]); + if ((bit >= 0) && ((river_bits & (1 << bit)) != 0)) { + if (out_dir != NULL) + *out_dir = dirs[i]; + return true; + } + } + + return false; +} + void wrap_tile_coords (Map * map, int * x, int * y) { @@ -25030,6 +25053,37 @@ get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * ou return tile; } +void +align_district_with_river (Tile * tile, int * out_pixel_x, int * out_pixel_y, enum direction * out_dir) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return; + + enum direction dir = DIR_ZERO; + if (! get_primary_river_direction (tile, &dir)) + return; + + int dx = 0, dy = 0; + switch (dir) { + case DIR_N: dx = 0; dy = -32; break; + case DIR_NE: dx = 32; dy = -16; break; + case DIR_E: dx = 64; dy = 0; break; + case DIR_SE: dx = 32; dy = 16; break; + case DIR_S: dx = 0; dy = 32; break; + case DIR_SW: dx = -32; dy = 16; break; + case DIR_W: dx = -64; dy = 0; break; + case DIR_NW: dx = -32; dy = -16; break; + default: break; + } + + if (out_pixel_x != NULL) + *out_pixel_x += dx; + if (out_pixel_y != NULL) + *out_pixel_y += dy; + if (out_dir != NULL) + *out_dir = dir; +} + void align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, int * out_pixel_x, int * out_pixel_y) { @@ -25279,7 +25333,7 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, } bool -wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner_id) +wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner_id, struct wonder_district_config const * cfg) { if (owner_id <= 0) return false; @@ -25291,6 +25345,19 @@ wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner if ((center == NULL) || (center == p_null_tile)) return false; + // If on a river and the wonder allows river alignment, make sure we face the river instead + unsigned int mask = wonder_buildable_square_type_mask (cfg); + bool allow_river = (mask & square_type_mask_bit (SQ_RIVER)) != 0; + if (allow_river) { + enum direction river_dir = DIR_ZERO; + if (get_primary_river_direction (center, &river_dir)) { + int dx, dy; + if (direction_to_offset (river_dir, &dx, &dy)) + return dx > 0; + } + } + + // Else face away from the nearest same-civ city Map * map = &p_bic_data->Map; int best_dist = INT_MAX; int best_dx = 0; @@ -25402,6 +25469,15 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par int buildings = 0; Sprite * district_sprite; + // Handle river alignment, if district allows it + enum direction river_dir = DIR_ZERO; + unsigned int build_mask = cfg->buildable_square_types_mask; + if (build_mask == 0) + build_mask = district_default_buildable_mask (); + bool river_allowed = (build_mask & square_type_mask_bit (SQ_RIVER)) != 0; + if (river_allowed) + align_district_with_river (tile, &pixel_x, &pixel_y, &river_dir); + if (territory_owner_id > 0) { Leader * leader = &leaders[territory_owner_id]; culture = p_bic_data->Races[leader->RaceID].CultureGroupID; @@ -25455,7 +25531,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par struct wonder_district_config * wcfg = &is->wonder_district_configs[windex]; struct wonder_district_image_set * set = &is->wonder_district_img_sets[windex]; - bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alternative_direction_image (tile_x, tile_y, territory_owner_id); + bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alternative_direction_image (tile_x, tile_y, territory_owner_id, wcfg); Sprite * wsprite = (use_alt_dir && (set->alt_dir_img.vtable != NULL)) ? &set->alt_dir_img : &set->img; draw_district_on_map_or_canvas(wsprite, map_renderer, pixel_x, pixel_y); @@ -25468,7 +25544,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par struct wonder_district_config * wcfg = &is->wonder_district_configs[construct_windex]; struct wonder_district_image_set * set = &is->wonder_district_img_sets[construct_windex]; - bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alternative_direction_image (tile_x, tile_y, territory_owner_id); + bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alternative_direction_image (tile_x, tile_y, territory_owner_id, wcfg); Sprite * csprite = (use_alt_dir && (set->alt_dir_construct_img.vtable != NULL)) ? &set->alt_dir_construct_img : &set->construct_img; draw_district_on_map_or_canvas(csprite, map_renderer, pixel_x, pixel_y); From d27f62465568f24727c98a19487a436e13537a59 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 16 Dec 2025 14:49:32 -0800 Subject: [PATCH 115/356] Add Hoover Dam --- Art/Districts/1200/Wonders_3.PCX | Bin 0 -> 10210 bytes default.districts_wonders_config.txt | 14 ++++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 Art/Districts/1200/Wonders_3.PCX diff --git a/Art/Districts/1200/Wonders_3.PCX b/Art/Districts/1200/Wonders_3.PCX new file mode 100644 index 0000000000000000000000000000000000000000..eb6b4d9121a7f348c96f52326fddcb99158806e6 GIT binary patch literal 10210 zcmeHMeQ*?In%Aw85*e$^^s&PYOsDAH95dZalS$9?WSE3Zlpbut1mcnqJ`6+_dK9=5 zf%4SNkPO)k$|?arkewwQyF0Pacc~v_}{c2|3?)T$)p5O0zo}PQ|y7TTk;6?5vcieF&{Qa%3JMZ|>-+t5gix3kN zV%|iQ`DAAB&*ld(@?8f1CxhPt{u#d61K)q0!T-+SSAgG{-wFv&TFN zpHVmJO70?NAABO_Wxy}Ze)BM?!UXFh@m)iiC5Cy=4q~1%KPJe)tr{^em?z-cUci3y z2HXM5W6n7tF%6sES9UPg)hEr91a-+Sxca<#8m>MEc*DGI9w+kFvnGm z_Yw2B`3XVqFGq%3)7R_7JZ+wXdn~+eegB&FSO>0U4rU)P!pBO@L_9W`1hPI6DQDb0GM1BScIuK@P|UI15N zGJfQ4vUF%wyES1>t4$jwH6+S88H{)>=tY+xEOM>b?SNVJo$M@;m33(?>Sp3vl56F# z(#-OmF#e(WE#L=$=gqTl@{uk~%Bn_nkd^z$qk-)KL1q)m_)T)69tRAfi66q%2FxjP zIw!ShY*I*WSJ_sVcE8Kk+LmS_d*OoJrU{4v&ze0z7<73QKfe43vALM~mYC|c+faXE zd7YY2z~dt^ag5qSOlwl~KAWG`wW4HPcQLGP7vo|aC)Ku8B-sr!5@G=PEbM_e?;yF( zdBsPGV^F{u3k3zn?)pc6iz2u9nor15%~frUiSgvTZ_20Og6m3hMoP~)1QaJ^KTy}gkIiFn5_EM&K@=zu1+O{+_jHm8lM9A((_)^eN6^x>}vctr)&>-a&HBJVLhU{8~)hs6%r|DLtYwHc2;lCe8;igY{M{YTo#o zbUfz>ke-l3Kwt}xnMce{f^M|9ca!o_z~+7h%UCE2sil~>iv49{9>oD6=n+`a5H#H( zy92>Ax`F-C8Mgd_2M&|V<}uKgx+mg@po3{fX=S9O&hQ!!$7MA5uVM9I+pJtEISX~m zv&)VF?Fl&sjJ5EHc?hI4C?OI{V-9o+9d1z>-S2TVw5YNt$_B9zVKl?zU{HS`->qQF zp}u0Ss^BQ;1m?V>AMQv6qG9E5p@-Yyakdr_| z3lD*~9VRS;NO^t2EjvSD)z1tsR0E9cfQ@4T6f~mgnl51X5Y;ay+yOPmHYlg~I5`LM z(T2K3J~~Tfyb=VaV}Zy=0ug~{Fb{4{>MSQA!<#$zoyChz0znDs0Y+JPz}ydl?#e|Z z;RPcmWTn|Jc(%*2mY^cCb`Pu^fu)6rDKhlTsiZb%MHKID=XcTDz< zXfB-3I&cDYvQfc-JrT>jo=7la7gSbOxdy+Qno;B|7+QOhoG?!j)D31#mlvV#2%C%x zZ9L1w4IUf~dk93}4Oc!9W0A~p4gLt;EJ0p=ej7Yrlun{I0!K{bHc8}(cs`gmIJ>Ql$t}^cwIcoMmUQDB-7K7HJE?y5XQLHgI!bkX^ zCPZ{WhrMXYSQ2$FUa_X}owE?rgj|8pvT(2230Y{+__F03_A9br(;Ql!BCwDS;6LJV zfUj9klmm3Jwg7f8!9}5at9*lc$U*ZA*@U}Gks7s8(sF%TS5ycy!84L&1nddG+aavm#tL>U2BMrGDJ^O+GSXe#OR#y7 zR1ZsGqiyGV;97SwqG8aGsKL81q=}TOFZd^&uj5?`>TZxzQRxz_N8pA2g4Wu_RZet6 zX;H>Bs3G@!H!JAa1EK8-hyr7XZaert>WB);5G9H_WiAo)`0{Ml+Ly>e)V&GV-G;8Q zOkjbbi%DH$ah%ab*1s0md0z_c<{O*ywlqUT>RR^dhIy87OIMC9MAvZ0nO0)q!s4ih z0eT6#;DN*2BWy%~5^7|ji27y0FZx?JkovsZBFpA|#Mx{ZJ5e`~Nz&2#3>!2U7R0nc zXYXTp#wAGUhIhP5o7{l9XVqley&Yn`)?F}f?BwOBvmW(_R5{O!I)kj}5g16utX(ri zA)q;&!TOK@>uWX->uOP>g31iCg07dmJ6maKlOfK$dfX6s2J$l~c*NjQH_xWyQd^O3 zM_nsW2b676cEEK2%ZtmF6cmQ!AIaFyxl>9?a_k0f3nFGq><_wCN51Sxrvj{I_qRmH z49<5klFwq>r)2T$hBhnI<1CYFhcgEmPL$e^5fNFYQH1YjRr7IC+R}&@FGP4oHrqNO z4^=orWs^hqrs`5Z8RK6bnw>(OKGDMlEm_gQ_-F+>bHVIy9l7lia28HPbr7zZmQAv?w;^ifjhBf82fVQx#!cCAjC zlhc=_o>tZv#D&$-V<24J=mvO~8=~l}AO=9NJyA!Eimo-NsgXf+RTjd7UA>Sl&XO4^ z87K4Nv*I-jj*G0W1z3UGQq(9iTzbh9eOU=&KP(b9RSyiE=N~G9BjYE z5mX$8$a2BT1Uf7X3ySUrFU)!la?I4?am7pKaywZO=%z?D$#mYJB&r^uhhWCq z9Os}1M%V?L#CTX;RYn#DVs>;js=6*hQPM+d=RQ8CY+(Zr4vnJ@UTS0=Orx(is{%O! zr_RqSE|T)afWmp~B51PA2_wS8FG5w-LpD8LQJzxL3?y}?UaXr5Ver?b(=jN$qDfOIZu;B8DiyVDW5B3n>q<3(48n_c+&tY1`!jfJ zCwM8-XdcSotxe~ZOfP#ZgSYmzpMjj+Y?eRCydP+puLB8**^|LD860TBZ-6y!w(_55 z-VZeN{m`vg4-YK7n8AU^0$*kxCtS(k)eH_iTKGEiOyYV5KhNO6^9kz#2Z09<*-tlU z4@77WP;NeK8F=t=>mwKX2yHIf+O)mW&PKBZ&2}_f(|kkoCC&G=SfRxhEf#69do~m0 zKaBMrs6q_jiiISD-}l^BpZqO+*O5gvt4`kwyUFT%&yX{nt30L^#7oWwu;$3F!J>e%3G`2`~XJ2`$7Fl;Dur2CPH7F`2-IG zZ~1oMitop)O7iwIT9xhXcVSh&x8FJav{spZdkfL6YV{Vk_XOR0mW^-kajP%8eU#86 z%jzU=AC2?~w))=NM?IY>H@OAQ`%5+%cp66IB8|H=uG6%ErX@7(p=lLO+h|%y(@vV! z(zKbTi(?Q0 z1KruppL_@UxRdbxKsR^u=iSrr&A?B%xAuKB-_U$X^F1wAXt70$MOy6AVx3-L(W^3g zB}cFR=oKTq%A{AO^lFw~0n-(au99?RrK>Sr!Re}hYYxfoH~z0N`i%5l(05KhE!{$N ztI|C|_a5Ej^eCZ67Cjp25loMII#X!eqH&SNT^iSE+CbA1n)cAN>i>Tpv0dK$_>QJ& z0dL-c)V$6YAKu@-nt zsasNUvf~#IP0UX&RX;nh?E1O?Xj(qE^N(x(^mfx9{%vVx%=hZEmAhV_A5%T+n#!L4 zd1P^I-ppxgMJ&8-^&-7idt=j66QllV4;6=ldCL}*zP4`ayqUfg%aqNn>khR4N*LwX z&^)48>*1eH;#aQ-JlC|iW_m&86z|07s0H(a z$M?+n_VS{wuRZwM`r@gvVK2S-Y-x$`-Hr9tQ)Sp#f#?!0%MAceX-csS6`j0 zD4f%2`$c1D{rd3g)df#19Rh~K_xMl03Z|#~<`^d~-G1K(2DEV@qTNlmcht$6? zuH=6C=@rxW_dQiL&snV5AFO8P|EquQlcQusd}GJTWxonGrKV4uK5E)yqqpu}H}8pw zTRN8A9~&)(?p?c~?%iWevFU~Rh5W`HONuAws#E+0_d6HWKjaDic;x7uX+Mkp`t_z` zU%s7q`De=gV*BxT#y?n^?p(dCXQMaZ(WaNKeErFmT`viRqZYiNS1+yF{QBl+>Yjlz zUnvj&b8Fp`@2*bl*|5HE=jxvSESx#v_g{4U^2p}W1yh@zUHR(`zxnO&e)sQhzS*{Q z>$|&l8M}8MIC${P*|U4Px{jYX(R=Y?Sw)365DXTVtbOU_>!07KoHo5S{`iCmn$<+m KUw07e?|%UmJz{(S literal 0 HcmV?d00001 diff --git a/default.districts_wonders_config.txt b/default.districts_wonders_config.txt index 87e3a13f..ea0a9eb4 100644 --- a/default.districts_wonders_config.txt +++ b/default.districts_wonders_config.txt @@ -133,6 +133,20 @@ img_alt_dir_construct_column = 2 img_alt_dir_row = 3 img_alt_dir_column = 3 +#Wonder +name = Hoover Dam +buildable_on = river +enable_img_alt_dir = 1 +img_path = Wonders_3.pcx +img_construct_row = 0 +img_construct_column = 0 +img_row = 0 +img_column = 1 +img_alt_dir_construct_row = 0 +img_alt_dir_construct_column = 2 +img_alt_dir_row = 0 +img_alt_dir_column = 3 + #Wonder name = Apollo Program img_path = SmallWonders.pcx From 32b17c1aa140f21c60a6327fe4cd9bf0dd41b779 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 16 Dec 2025 17:57:32 -0800 Subject: [PATCH 116/356] Add extended_height property and handling for tall districts --- Art/Districts/1200/SkiResort.PCX | Bin 0 -> 2501 bytes Art/Districts/1200/Wonders_3.PCX | Bin 10210 -> 13169 bytes C3X.h | 3 +++ injected_code.c | 21 ++++++++++++++++++--- 4 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 Art/Districts/1200/SkiResort.PCX diff --git a/Art/Districts/1200/SkiResort.PCX b/Art/Districts/1200/SkiResort.PCX new file mode 100644 index 0000000000000000000000000000000000000000..a73ce846ffe306b2a56d37a41323d02a5bfcf3be GIT binary patch literal 2501 zcmb8wdrVVT90zb%vSiuZAI@Z3n1)P32o$EJ&;ni8f#XrI3P>p(aSpZUhHRoVOcXUk zSDCZCgj@9Ug% z@A=)=Uv+)W1vcU;c5!iq-^*^UF0Z`&N&nu2Ft-R{URa0!81xx)8}=-qf1~r@Gf0{s zpGE&dpMnd_9CHWqY4lHY3Y>!@J0N$WPteEUEHlG&K|YTDfsTPQaD`sT9q1!;1e|6j znTL?u(cjS_a1yRO1bII?fIbAB%mgz6c^`To?FA>`j$@E_qdjOBIL?eRk0HN{{)Tpd zqtKHn$lK6g(L11n8D^eB-i)@OP2e!}a2|3S`U`p+v@?SY19?5#fZhTJnSO}t2^-YI ze%5DbKl1=u8<|HJ&^h!0jEazE@)Vs#XV5<89<*&_3Y|tL(R(mrLhh2ss1uz)yO~aC zyU7?jj*g<8FdKyQkrC8^4x@LOc4+&_5NbyU(RP?ALhR%r%8t!W*m2lN8`BD{gY=^8 z2poj%t%J0}ED|zKx=^;C>iDlNPm0jZ#;dK(AE`g9n#hJF_=(drtl4$|r- zti&yw#PjdPf4>xq#!TBSYI&faXS+e!SiHW?YPqOQDc>hEsTJ3bZ5*}V zJ6o*MWF;i6=0%FUWfJaO=V8?bu1WWGM@{x{O`OdrwwTWuG|CM!p)7{CC0TSvvnDn= z=xFYSn#!yiqkN`S(c5$^XJ=fW?O=TL%5O8nC)!ocp`XLV>3$EwoaXpiJiGs`#b~ zUAy&=kJmfKqib8P$~PvuXQZ#YTz#O`S~}2WJZ0E^^Wyq-t39;2fd%S}&lLW$Sik59 zuQa8gr|I+gv2=4~RHa^&Abq!_I6qYEvoNVoO5kxo*L!pO89VHem zlSH~7DF`Xo`!CE{iVI_mhEdhh!K+6t5WaG@YX=(i@8m#2}H8YG$7KIIj`T;5{y z4PM2Ch@unxT#aXVq@ddT#l_lBQ#K|GL`&4AoDaC}1-b}#k2m%o`A8hb%g$A_Id&x~ z-9(bNqLP-ReiN`M%Ztb7oU`Pq4hhr-MYO_8{-v+EL8sgiU2Ri^NqsrNZx$JIu6L`Y zilCK29_5yuqP6bfu>t;JZt45uy#;T-@4HyOIpTPgx_jo*-qOwdFpjn7$|q5wXB)LO z{pEgv-V#Mbnl&#w%BPHyofWEm9_V*9|8fd@( literal 0 HcmV?d00001 diff --git a/Art/Districts/1200/Wonders_3.PCX b/Art/Districts/1200/Wonders_3.PCX index eb6b4d9121a7f348c96f52326fddcb99158806e6..ba6eb67334af749a77a1eb1390cdec119545d53d 100644 GIT binary patch delta 3795 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 diff --git a/C3X.h b/C3X.h index 58f9f1fd..47dfe924 100644 --- a/C3X.h +++ b/C3X.h @@ -590,6 +590,7 @@ struct district_config { bool vary_img_by_culture; bool is_dynamic; bool align_to_coast; + bool extended_height; int resource_prereq_count; int dependent_improvement_count; int img_path_count; @@ -752,6 +753,7 @@ struct parsed_district_definition { bool vary_img_by_era; bool vary_img_by_culture; bool align_to_coast; + bool extended_height; int btn_tile_sheet_column; int btn_tile_sheet_row; int defense_bonus_percent; @@ -773,6 +775,7 @@ struct parsed_district_definition { bool has_vary_img_by_era; bool has_vary_img_by_culture; bool has_align_to_coast; + bool has_extended_height; bool has_btn_tile_sheet_column; bool has_btn_tile_sheet_row; bool has_defense_bonus_percent; diff --git a/injected_code.c b/injected_code.c index 6a945385..cc634680 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5544,6 +5544,8 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->vary_img_by_culture = def->vary_img_by_culture; if (def->has_align_to_coast) cfg->align_to_coast = def->align_to_coast; + if (def->has_extended_height) + cfg->extended_height = def->extended_height; if (def->has_btn_tile_sheet_column) cfg->btn_tile_sheet_column = def->btn_tile_sheet_column; if (def->has_btn_tile_sheet_row) @@ -5712,6 +5714,7 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.vary_img_by_era = def->has_vary_img_by_era ? def->vary_img_by_era : false; new_cfg.vary_img_by_culture = def->has_vary_img_by_culture ? def->vary_img_by_culture : false; new_cfg.align_to_coast = def->has_align_to_coast ? def->align_to_coast : false; + new_cfg.extended_height = def->has_extended_height ? def->extended_height : false; new_cfg.btn_tile_sheet_column = def->has_btn_tile_sheet_column ? def->btn_tile_sheet_column : 0; new_cfg.btn_tile_sheet_row = def->has_btn_tile_sheet_row ? def->btn_tile_sheet_row : 0; new_cfg.defense_bonus_percent = def->has_defense_bonus_percent ? def->defense_bonus_percent : 100; @@ -5959,6 +5962,15 @@ handle_district_definition_key (struct parsed_district_definition * def, } else add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + } else if (slice_matches_str (key, "extended_height")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->extended_height = (ival != 0); + def->has_extended_height = true; + } else + add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + } else if (slice_matches_str (key, "btn_tile_sheet_column")) { struct string_slice val_slice = *value; int ival; @@ -24819,6 +24831,7 @@ init_district_images () int era_count = cfg->vary_img_by_era ? 4 : 1; int column_count = cfg->max_building_index + 1; + int sprite_height = cfg->extended_height ? 88 : 64; // For each cultural variant for (int variant_i = 0; variant_i < variant_count; variant_i++) { @@ -24857,8 +24870,8 @@ init_district_images () Sprite_construct (&is->district_img_sets[dc].imgs[variant_i][era_i][col_i]); int x = 128 * col_i, - y = 64 * era_i; - Sprite_slice_pcx (&is->district_img_sets[dc].imgs[variant_i][era_i][col_i], __, &pcx, x, y, 128, 64, 1, 1); + y = sprite_height * era_i; + Sprite_slice_pcx (&is->district_img_sets[dc].imgs[variant_i][era_i][col_i], __, &pcx, x, y, 128, sprite_height, 1, 1); } } @@ -25567,7 +25580,9 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; - draw_district_on_map_or_canvas(district_sprite, map_renderer, pixel_x, pixel_y); + int sprite_height = cfg->extended_height ? 88 : 64; + int draw_y = pixel_y - (sprite_height - 64); + draw_district_on_map_or_canvas(district_sprite, map_renderer, pixel_x, draw_y); return; } From 2ecf631be7cb3a931ea0f881834560414f29b471 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 16 Dec 2025 18:03:24 -0800 Subject: [PATCH 117/356] Add Ski Resort --- Art/Districts/1200/SkiResort.PCX | Bin 2501 -> 5310 bytes default.districts_config.txt | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/Art/Districts/1200/SkiResort.PCX b/Art/Districts/1200/SkiResort.PCX index a73ce846ffe306b2a56d37a41323d02a5bfcf3be..df9ada177a4876e146eecf6462edc954adf30d3d 100644 GIT binary patch literal 5310 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 delta 1128 zcmZ|OL2nX46bEoN&0s8yd~Pan_fv zuHU%1CL5}v6JIk59m!ja4C!w=fFoqm$oJ?kdJ2cqmo4PG^e64WKHkhBAJ8AP1$%NL zJIJp!7{1dwJi!!u7^u>3v;w=*mLl@U^ea7rZA^WHe2IRcMHom^D#-8C&vXwqr6G0X z3-r@vh8@_DnzS&Grypq!)=)~v#H0SoDMmA5eMi%d3w{>#8U))-HJog7{0> Date: Tue, 16 Dec 2025 18:17:02 -0800 Subject: [PATCH 118/356] Update Ski Resort config, art --- Art/Districts/1200/SkiResort.PCX | Bin 5310 -> 5303 bytes default.districts_config.txt | 1 + 2 files changed, 1 insertion(+) diff --git a/Art/Districts/1200/SkiResort.PCX b/Art/Districts/1200/SkiResort.PCX index df9ada177a4876e146eecf6462edc954adf30d3d..3ebb4a0c3efb41cd00317f920c3e38214dac48d3 100644 GIT binary patch delta 950 zcmZWnO=uHQ5GLE*G<_SQRvSy&P!q&8MWy~EF|BQgq^5!e18NVVR@CZALt;^^(Vhp(?&~db;sF&(BQ&;Z+7^;nVoOmQ{N-sy*(Db z*0P!2MqdRTSo6Hb3U(*8njF5uC4R!PvFn#D=_ERIJChQt*~_?ydVEOi>e(rxu}Iso0iPn$ zf+`FjgasXP|HxI9$sP!}Hb=WeQ%_Ui4C@o^2NXy{HUMcClx_t6(aqCjL*6Oo{Q9AD z@R^CC;`D|bFiV00WCp@w98&)Y3g(E(EDf2PU@<)b#coFPCPDH{9AWDLvv0zM*>UR~F3I9+* z$PwGyc|1go=9NB$#-fSWK(SW~Lq?~?Q9TbD-;9@}tq^WOV~LIxb>y^$_ec}e=qO0(-ljF;*$tj;d#)k_$+mv^`ks$4aiRaWgpkAdlhHDx8o*% zV)ci}!!KLy(lmL5n4}*!@lO>$81IZZ9GSpTpOa>uQxfAt-hS0w6l9#dz5b!)KA)@a zu;#HJQiFVhEo3cHZ`U9{Yzx-U(}jdo7az0jlYSwew)v$Ba=GD@G==;^!y9Q5c@J|- zEp~o_b;=zhcTe*{=H&O8SH5^ljPW-5m}jk0xRI-Bi=~%q>KL=kjN$V;TktHA1Z0*)AcU^BR|sDkZ38{YI;_zmRG?Rg>L7iH_s4E?RX2|eLmp%r>J3m*#zP?h;( zdpf7-6lT3RW+t|_Y%mcOtc%53y%qxne(V79~{bYmc9;fhqo`bDz7mc%c#at z8BQQzX%DwP7~UmQxQW{=UekeY(QkD047`?Ow~NBTZOtCq;U7w27JtlY0MW$vnd1T0d3o@pOb@S5L7Z)2Rw&S_m z+xt-(5)Io3iZZo}%#xXA@{@Y~dq)g@OA=uN)p$g0lB&RBsiO;JZoy&8EsI>uG6qdtrD z+CS6e6m>tfL;V`*i_OsZG^L%(ZNdpzhqs+m`=XhMa6Ub##-sa$7U`p&`Zn4>P>}hv z;j8SCsKCQCaI=YjnLo^*)UaHnkDMgOd|h~7z1?#>&fn>yDpXhO`_Ls-itV<18dK8F zS&KBOj@VgiiN@83_OsDC9escFw3@Q}tyMaxHtaV Date: Tue, 16 Dec 2025 23:18:37 -0800 Subject: [PATCH 119/356] Add Delicate Arch --- Art/Districts/1200/NaturalWonders.pcx | Bin 83708 -> 87294 bytes default.districts_natural_wonders_config.txt | 14 ++++++++++++++ 2 files changed, 14 insertions(+) diff --git a/Art/Districts/1200/NaturalWonders.pcx b/Art/Districts/1200/NaturalWonders.pcx index f47e8f501f6286539b847fc588399a7984f1ae07..56b1d3cfa8b11e1e11f079d4a08560e5186dbbfe 100644 GIT binary patch delta 3203 zcmZu!J#5^_6+R{KAvysJ)-c7&fPg))Fu*|&8wiLk43M}Y133jNDeX`|njox?IFKo~ z*djyrH2rJsLtY2;@g;YW>pH4=Ul{;BqhKi=%z9QlVoJN$U0J-hnx$U+If zN+c;I)d%=*Nog#CFutA31z5bhfIrBWlJSrN3O!Cik

Dja=67oqQq9nrWD1F><{y z78Ivhr-0EbdFKq1>TyTCmETG-$u@0^jGE>8K`O$m*@?TbdoDLLe1mTali5a%u(<7# z9eOO_K8Z4n6S<|~Yk4ZC%)0N^$uvgEuzYf(xS3Mi%#uZz9?4w|U%{bn)_&(Yb*Ek{ z4BNHELe^P=(Ifdl!x!=!dBe0!!N@M0x@puyH%xg(ow?=;EcfMshRmwCZjE}} z7jZN4$o$kXkgSkd%xluz8q7E4X@$x8AIC^SS51;>xq^rIWurE!n&H_@;1gwY**Z*D zW{oDGRqEztiv#Y5ZCl@S;TUg4v6zFzQ?o z^D>&d3)@Bcy@rqE5ZpqsnHSw|M)6YV;04TweWwr8LP`yXa$o)v4i@90DFSy{ndK`p zMV8KWwp3|e0CE2EQR2S54^ld#rp-gnxVxeZ(-NFTo!i)CTYV3!MGfzxTy2R4C0n5$~v%uwT`12gSXs&^>;f zHP;mO5wQqGoJQF|ox*%m!)@6I^Bs#^(Og$x6-7P{k9azJ>KV+pHSB{0_Y&1GEStr! zcS8}mVVp(HDC^u;Pv9`na8s_!9o4ECXne2%xg~B9i@1*s_tgO$bX*5t2FgJdc+_53 zHQOgt8?9>DqVrHafJ5@=?C~B~Wl!Em-r?e0DS~e%KR&o%XL^k6T@#fr#T7cU-4GWoLYT!sPL*5F$s-|p@Y_DKG z3ktNzzZAhsX_&+7P$gHd-9Ur|J9D#H$gxaNgn=N;u2KJ!Q=^OkoRm0mdE|!DFo$Re2Us`g=O8ip6~>p9cnbFh_M99ZK-^aKmC1UY5{g2LKHTx zj$K|>MY#k%E+P3~O0chyucG4$NPbu4yZH|FlQ>Lga5G`*22mOZ)6NZNVtnHAM4`qB z2eK^_%M~ScMAVGaJ&pZUKlIRMn+S-W=6!@UYz5Ss3Db<)l-|1G)W;l}7{4+`RMyEF40>l5ZgqwFFm6a>g{co#Is zgWr8W2@{<6{aEZ{+-|G9M~lVT*=e@*`IoK6^yDwy--H3Ve(7s-!6Rx4;@*ZfeXDuA zjS+vp%G@&qG@vzvGj*#zHQjInn&Gi;Q^^;Mee&G=X_D{uw3%Ps0z0@__NrxeXpMG| zuWozly3@GsFrNn)ULIjCVj<-`mJ-bHqiYjH9o<*$@H(i}odhG-%u+n+SMY;F z59Q~p3);0(v#;dq3nI9Z#jl~!H7WgTELy7TH}dVdB7ZN)D~-AvOfR- delta 38 scmeyjlJ!q7D?b-2BL@Qz{AY}3OkqfwC}_}Vztx^`kMs7wfsBmI0NED{8UO$Q diff --git a/default.districts_natural_wonders_config.txt b/default.districts_natural_wonders_config.txt index 41585eab..6c0f7dd2 100644 --- a/default.districts_natural_wonders_config.txt +++ b/default.districts_natural_wonders_config.txt @@ -227,4 +227,18 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happiness_bonus = 1 + +#Wonder +name = Delicate Arch +terrain_type = desert +adjacent_to = desert +img_path = NaturalWonders.pcx +img_row = 4 +img_column = 0 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 happiness_bonus = 1 \ No newline at end of file From b3ac4b31d1aeca6c3afdcd05371db5e9910161f2 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 17 Dec 2025 13:44:19 -0800 Subject: [PATCH 120/356] Add custom_height and width; Fix divide by zero bug with distro hubs; Add error outputs for more config parsing --- Art/Districts/1200/DataCenter.PCX | Bin 0 -> 7321 bytes Art/Districts/1200/NaturalWonders.pcx | Bin 87294 -> 87279 bytes Art/Districts/1200/PowerStation.PCX | Bin 0 -> 7321 bytes C3X.h | 9 +- default.districts_config.txt | 4 +- default.districts_wonders_config.txt | 2 + injected_code.c | 307 ++++++++++++++++++++------ 7 files changed, 248 insertions(+), 74 deletions(-) create mode 100644 Art/Districts/1200/DataCenter.PCX create mode 100644 Art/Districts/1200/PowerStation.PCX diff --git a/Art/Districts/1200/DataCenter.PCX b/Art/Districts/1200/DataCenter.PCX new file mode 100644 index 0000000000000000000000000000000000000000..01e2abc7b13073e2d1bce9eeff977669959badd7 GIT binary patch literal 7321 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.distribution_hub_yield_division_mode == DHYDM_SCALE_BY_CITY_COUNT) { - rec->food_yield = food_sum / (connected_city_count / food_div); - rec->shield_yield = shield_sum / (connected_city_count / shield_div); + int city_food_divisor = connected_city_count / food_div; + int city_shield_divisor = connected_city_count / shield_div; + if (city_food_divisor < 1) + city_food_divisor = 1; + if (city_shield_divisor < 1) + city_shield_divisor = 1; + rec->food_yield = food_sum / city_food_divisor; + rec->shield_yield = shield_sum / city_shield_divisor; } else { rec->food_yield = food_sum / food_div; rec->shield_yield = shield_sum / shield_div; @@ -4935,12 +4941,19 @@ void add_key_parse_error (struct error_line ** parse_errors, int line_number, struct string_slice const * key, + struct string_slice const * value, char const * message_suffix) { struct error_line * err = add_error_line (parse_errors); char * key_str = extract_slice (key); - snprintf (err->text, sizeof err->text, "^ Line %d: %s %s", line_number, key_str, message_suffix); + char * value_str = (value != NULL) ? extract_slice (value) : NULL; + if (value_str != NULL) + snprintf (err->text, sizeof err->text, "^ Line %d: %s \"%s\" %s", line_number, key_str, value_str, message_suffix); + else + snprintf (err->text, sizeof err->text, "^ Line %d: %s %s", line_number, key_str, message_suffix); err->text[(sizeof err->text) - 1] = '\0'; + if (value_str != NULL) + free (value_str); free (key_str); } @@ -5544,8 +5557,10 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->vary_img_by_culture = def->vary_img_by_culture; if (def->has_align_to_coast) cfg->align_to_coast = def->align_to_coast; - if (def->has_extended_height) - cfg->extended_height = def->extended_height; + if (def->has_custom_width) + cfg->custom_width = def->custom_width; + if (def->has_custom_height) + cfg->custom_height = def->custom_height; if (def->has_btn_tile_sheet_column) cfg->btn_tile_sheet_column = def->btn_tile_sheet_column; if (def->has_btn_tile_sheet_row) @@ -5714,7 +5729,8 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.vary_img_by_era = def->has_vary_img_by_era ? def->vary_img_by_era : false; new_cfg.vary_img_by_culture = def->has_vary_img_by_culture ? def->vary_img_by_culture : false; new_cfg.align_to_coast = def->has_align_to_coast ? def->align_to_coast : false; - new_cfg.extended_height = def->has_extended_height ? def->extended_height : false; + new_cfg.custom_width = def->has_custom_width ? def->custom_width : 0; + new_cfg.custom_height = def->has_custom_height ? def->custom_height : 0; new_cfg.btn_tile_sheet_column = def->has_btn_tile_sheet_column ? def->btn_tile_sheet_column : 0; new_cfg.btn_tile_sheet_row = def->has_btn_tile_sheet_row ? def->btn_tile_sheet_row : 0; new_cfg.defense_bonus_percent = def->has_defense_bonus_percent ? def->defense_bonus_percent : 100; @@ -5827,7 +5843,7 @@ handle_district_definition_key (struct parsed_district_definition * def, char * name_copy = copy_trimmed_string_or_null (value, 1); if (name_copy == NULL) { def->has_name = false; - add_key_parse_error (parse_errors, line_number, key, "(value is required)"); + add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); } else { def->name = name_copy; def->has_name = true; @@ -5933,7 +5949,7 @@ handle_district_definition_key (struct parsed_district_definition * def, def->allow_multiple = (ival != 0); def->has_allow_multiple = true; } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "vary_img_by_era")) { struct string_slice val_slice = *value; @@ -5942,7 +5958,7 @@ handle_district_definition_key (struct parsed_district_definition * def, def->vary_img_by_era = (ival != 0); def->has_vary_img_by_era = true; } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "vary_img_by_culture")) { struct string_slice val_slice = *value; @@ -5951,7 +5967,7 @@ handle_district_definition_key (struct parsed_district_definition * def, def->vary_img_by_culture = (ival != 0); def->has_vary_img_by_culture = true; } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "align_to_coast")) { struct string_slice val_slice = *value; @@ -5960,16 +5976,25 @@ handle_district_definition_key (struct parsed_district_definition * def, def->align_to_coast = (ival != 0); def->has_align_to_coast = true; } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + + } else if (slice_matches_str (key, "custom_width")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->custom_width = ival; + def->has_custom_width = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - } else if (slice_matches_str (key, "extended_height")) { + } else if (slice_matches_str (key, "custom_height")) { struct string_slice val_slice = *value; int ival; if (read_int (&val_slice, &ival)) { - def->extended_height = (ival != 0); - def->has_extended_height = true; + def->custom_height = ival; + def->has_custom_height = true; } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "btn_tile_sheet_column")) { struct string_slice val_slice = *value; @@ -5978,7 +6003,7 @@ handle_district_definition_key (struct parsed_district_definition * def, def->btn_tile_sheet_column = ival; def->has_btn_tile_sheet_column = true; } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "btn_tile_sheet_row")) { struct string_slice val_slice = *value; @@ -5987,7 +6012,7 @@ handle_district_definition_key (struct parsed_district_definition * def, def->btn_tile_sheet_row = ival; def->has_btn_tile_sheet_row = true; } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "defense_bonus_percent")) { struct string_slice val_slice = *value; @@ -5996,7 +6021,7 @@ handle_district_definition_key (struct parsed_district_definition * def, def->defense_bonus_percent = ival; def->has_defense_bonus_percent = true; } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "culture_bonus")) { struct string_slice val_slice = *value; @@ -6005,7 +6030,7 @@ handle_district_definition_key (struct parsed_district_definition * def, def->culture_bonus = ival; def->has_culture_bonus = true; } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "science_bonus")) { struct string_slice val_slice = *value; @@ -6014,7 +6039,7 @@ handle_district_definition_key (struct parsed_district_definition * def, def->science_bonus = ival; def->has_science_bonus = true; } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "food_bonus")) { struct string_slice val_slice = *value; @@ -6023,7 +6048,7 @@ handle_district_definition_key (struct parsed_district_definition * def, def->food_bonus = ival; def->has_food_bonus = true; } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "gold_bonus")) { struct string_slice val_slice = *value; @@ -6032,7 +6057,7 @@ handle_district_definition_key (struct parsed_district_definition * def, def->gold_bonus = ival; def->has_gold_bonus = true; } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "shield_bonus")) { struct string_slice val_slice = *value; @@ -6041,7 +6066,7 @@ handle_district_definition_key (struct parsed_district_definition * def, def->shield_bonus = ival; def->has_shield_bonus = true; } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "happiness_bonus")) { struct string_slice val_slice = *value; @@ -6050,7 +6075,7 @@ handle_district_definition_key (struct parsed_district_definition * def, def->happiness_bonus = ival; def->has_happiness_bonus = true; } else - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "generated_resource")) { if (def->generated_resource != NULL) { @@ -6070,8 +6095,34 @@ handle_district_definition_key (struct parsed_district_definition * def, parse_errors, line_number, "generated_resource_settings")) { - def->generated_resource_settings_count = list_count; - def->has_generated_resource_settings = true; + bool valid = true; + for (int i = 0; i < list_count; i++) { + char * setting = def->generated_resource_settings[i]; + if ((setting == NULL) || + ((strcmp (setting, "local") != 0) && + (strcmp (setting, "no-tech-req") != 0) && + (strcmp (setting, "show-bonus") != 0) && + (strcmp (setting, "hide-non-bonus") != 0))) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: generated_resource_settings (unrecognized value \"%s\")", line_number, (setting != NULL) ? setting : ""); + err->text[(sizeof err->text) - 1] = '\0'; + valid = false; + } + } + + if (valid) { + def->generated_resource_settings_count = list_count; + def->has_generated_resource_settings = true; + } else { + for (int i = 0; i < ARRAY_LEN (def->generated_resource_settings); i++) { + if (def->generated_resource_settings[i] != NULL) { + free (def->generated_resource_settings[i]); + def->generated_resource_settings[i] = NULL; + } + } + def->generated_resource_settings_count = 0; + def->has_generated_resource_settings = false; + } } else { def->generated_resource_settings_count = 0; def->has_generated_resource_settings = false; @@ -6419,12 +6470,12 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, struct string_slice unquoted = trim_string_slice (value, 1); if (unquoted.len == 0) { def->has_name = false; - add_key_parse_error (parse_errors, line_number, key, "(value is required)"); + add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); } else { char * name_copy = extract_slice (&unquoted); if (name_copy == NULL) { def->has_name = false; - add_key_parse_error (parse_errors, line_number, key, "(out of memory)"); + add_key_parse_error (parse_errors, line_number, key, value, "(out of memory)"); } else { def->name = name_copy; def->has_name = true; @@ -6458,7 +6509,7 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, def->has_img_row = true; } else { def->has_img_row = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "img_column")) { @@ -6469,7 +6520,7 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, def->has_img_column = true; } else { def->has_img_column = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "img_construct_row")) { @@ -6480,7 +6531,7 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, def->has_img_construct_row = true; } else { def->has_img_construct_row = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "img_construct_column")) { @@ -6491,7 +6542,7 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, def->has_img_construct_column = true; } else { def->has_img_construct_column = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "img_alt_dir_construct_row")) { @@ -6502,7 +6553,7 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, def->has_img_alt_dir_construct_row = true; } else { def->has_img_alt_dir_construct_row = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "img_alt_dir_construct_column")) { @@ -6513,7 +6564,7 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, def->has_img_alt_dir_construct_column = true; } else { def->has_img_alt_dir_construct_column = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "img_alt_dir_row")) { @@ -6524,7 +6575,7 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, def->has_img_alt_dir_row = true; } else { def->has_img_alt_dir_row = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "img_alt_dir_column")) { @@ -6535,7 +6586,7 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, def->has_img_alt_dir_column = true; } else { def->has_img_alt_dir_column = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "enable_img_alt_dir")) { @@ -6546,7 +6597,7 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, def->has_enable_img_alt_dir = true; } else { def->has_enable_img_alt_dir = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "buildable_on")) { @@ -6860,12 +6911,12 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * struct string_slice unquoted = trim_string_slice (value, 1); if (unquoted.len == 0) { def->has_name = false; - add_key_parse_error (parse_errors, line_number, key, "(value is required)"); + add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); } else { char * name_copy = extract_slice (&unquoted); if (name_copy == NULL) { def->has_name = false; - add_key_parse_error (parse_errors, line_number, key, "(out of memory)"); + add_key_parse_error (parse_errors, line_number, key, value, "(out of memory)"); } else { def->name = name_copy; def->has_name = true; @@ -6879,7 +6930,7 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * def->has_terrain_type = true; } else { def->has_terrain_type = false; - add_key_parse_error (parse_errors, line_number, key, "(unrecognized terrain type)"); + add_key_parse_error (parse_errors, line_number, key, value, "(unrecognized terrain type)"); } } else if (slice_matches_str (key, "adjacent_to")) { @@ -6890,7 +6941,7 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * } else { def->adjacent_to = (enum SquareTypes)SQ_INVALID; def->has_adjacent_to = false; - add_key_parse_error (parse_errors, line_number, key, "(unrecognized square type)"); + add_key_parse_error (parse_errors, line_number, key, value, "(unrecognized square type)"); } } else if (slice_matches_str (key, "adjacency_dir")) { @@ -6901,7 +6952,7 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * } else { def->adjacency_dir = DIR_ZERO; def->has_adjacency_dir = false; - add_key_parse_error (parse_errors, line_number, key, "(unrecognized direction)"); + add_key_parse_error (parse_errors, line_number, key, value, "(unrecognized direction)"); } } else if (slice_matches_str (key, "img_path")) { @@ -6931,7 +6982,7 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * def->has_img_row = true; } else { def->has_img_row = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "img_column")) { @@ -6942,7 +6993,7 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * def->has_img_column = true; } else { def->has_img_column = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "culture_bonus")) { @@ -6953,7 +7004,7 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * def->has_culture_bonus = true; } else { def->has_culture_bonus = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "science_bonus")) { @@ -6964,7 +7015,7 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * def->has_science_bonus = true; } else { def->has_science_bonus = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "food_bonus")) { @@ -6975,7 +7026,7 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * def->has_food_bonus = true; } else { def->has_food_bonus = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "gold_bonus")) { @@ -6986,7 +7037,7 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * def->has_gold_bonus = true; } else { def->has_gold_bonus = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "shield_bonus")) { @@ -6997,7 +7048,7 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * def->has_shield_bonus = true; } else { def->has_shield_bonus = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else if (slice_matches_str (key, "happiness_bonus")) { @@ -7008,7 +7059,7 @@ handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * def->has_happiness_bonus = true; } else { def->has_happiness_bonus = false; - add_key_parse_error (parse_errors, line_number, key, "(expected integer)"); + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } } else @@ -7219,7 +7270,10 @@ void parse_building_and_tech_ids () { struct c3x_config * cfg = &is->current_config; char ss[200]; + struct error_line * district_parse_errors = NULL; + struct error_line * wonder_parse_errors = NULL; for (int i = 0; i < is->district_count; i++) { + char const * district_name = (is->district_configs[i].name != NULL) ? is->district_configs[i].name : "District"; if (is->district_configs[i].command != 0) itable_insert (&is->command_id_to_district_id, is->district_configs[i].command, i); is->district_infos[i].advance_prereq_id = -1; @@ -7239,6 +7293,9 @@ void parse_building_and_tech_ids () itable_insert (&is->district_tech_prereqs, tech_id, i); } else { is->district_infos[i].advance_prereq_id = -1; + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": advance_prereq \"%.*s\" not found", district_name, tech_name.len, tech_name.str); + err->text[(sizeof err->text) - 1] = '\0'; } } @@ -7256,6 +7313,10 @@ void parse_building_and_tech_ids () is->district_infos[i].resource_prereq_ids[stored_res_count] = res_id; stored_res_count++; } + } else { + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": resource_prereq \"%.*s\" not found", district_name, res_name.len, res_name.str); + err->text[(sizeof err->text) - 1] = '\0'; } } is->district_infos[i].resource_prereq_count = stored_res_count; @@ -7268,6 +7329,9 @@ void parse_building_and_tech_ids () is->district_infos[i].resource_prereq_on_tile_id = res_id; } else { is->district_infos[i].resource_prereq_on_tile_id = -1; + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": resource_prereq_on_tile \"%.*s\" not found", district_name, res_name.len, res_name.str); + err->text[(sizeof err->text) - 1] = '\0'; } } @@ -7281,6 +7345,9 @@ void parse_building_and_tech_ids () is->district_configs[i].generated_resource_id = res_id; } else { is->district_configs[i].generated_resource_id = -1; + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": generated_resource \"%.*s\" not found", district_name, res_name.len, res_name.str); + err->text[(sizeof err->text) - 1] = '\0'; } } @@ -7307,6 +7374,9 @@ void parse_building_and_tech_ids () stable_insert (&is->building_name_to_id, improv_name.str, improv_id); } else { is->district_infos[i].dependent_building_ids[j] = -1; + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": dependent_improvs entry \"%.*s\" not found", district_name, improv_name.len, improv_name.str); + err->text[(sizeof err->text) - 1] = '\0'; } is->district_infos[i].dependent_building_count = stored_count; } @@ -7326,8 +7396,44 @@ void parse_building_and_tech_ids () } else { snprintf (ss, sizeof ss, "Could not find improvement prereq \"%.*s\" for wonder district \"%s\"\n", wonder_name.len, wonder_name.str, is->wonder_district_configs[wi].wonder_name); (*p_OutputDebugStringA) (ss); + struct error_line * err = add_error_line (&wonder_parse_errors); + snprintf (err->text, sizeof err->text, "^ Wonder district \"%s\": improvement \"%.*s\" not found", (is->wonder_district_configs[wi].wonder_name != NULL) ? is->wonder_district_configs[wi].wonder_name : "Wonder District", wonder_name.len, wonder_name.str); + err->text[(sizeof err->text) - 1] = '\0'; } } + + if ((district_parse_errors != NULL) || (wonder_parse_errors != NULL)) { + PopupForm * popup = get_popup_form (); + popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_WARNING", -1, 0, 0, 0); + + if (district_parse_errors != NULL) { + char header[256]; + if (is->current_districts_config_path[0] != '\0') + snprintf (header, sizeof header, "District Config errors in %s:", is->current_districts_config_path); + else + snprintf (header, sizeof header, "District Config lookup errors:"); + header[(sizeof header) - 1] = '\0'; + PopupForm_add_text (popup, __, header, false); + for (struct error_line * line = district_parse_errors; line != NULL; line = line->next) + PopupForm_add_text (popup, __, line->text, false); + } + + if ((district_parse_errors != NULL) && (wonder_parse_errors != NULL)) + PopupForm_add_text (popup, __, "", false); + + if (wonder_parse_errors != NULL) { + char header[256]; + snprintf (header, sizeof header, "Wonder District Config lookup errors:"); + header[(sizeof header) - 1] = '\0'; + PopupForm_add_text (popup, __, header, false); + for (struct error_line * line = wonder_parse_errors; line != NULL; line = line->next) + PopupForm_add_text (popup, __, line->text, false); + } + + patch_show_popup (popup, __, 0, 0); + free_error_lines (district_parse_errors); + free_error_lines (wonder_parse_errors); + } } void @@ -23746,9 +23852,17 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s return; // Get district configuration - struct district_config const * config = &is->district_configs[district_id]; + struct district_config * config = &is->district_configs[district_id]; struct district_instance * inst = get_district_instance (tile); + int generated_resource_id = -1; + if ((config->generated_resource_id >= 0) && + (city_form != NULL) && (city_form->CurrentCity != NULL) && + ((config->generated_resource_flags & MF_SHOW_BONUS) || (p_bic_data->ResourceTypes[config->generated_resource_id].Class != RC_Bonus)) && + (! ((config->generated_resource_flags & MF_HIDE_NON_BONUS) && (p_bic_data->ResourceTypes[config->generated_resource_id].Class != RC_Bonus))) && + district_can_generate_resource (city_form->CurrentCity->Body.CivID, config)) + generated_resource_id = config->generated_resource_id; + // Count total yields from bonuses int food_bonus = 0, shield_bonus = 0, gold_bonus = 0, science_bonus = 0, culture_bonus = 0, happiness_bonus = 0; get_effective_district_yields (inst, config, &food_bonus, &shield_bonus, &gold_bonus, &science_bonus, &culture_bonus, &happiness_bonus); @@ -23761,7 +23875,12 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s if (culture_bonus > 0) total_yield += culture_bonus; if (happiness_bonus > 0) total_yield += happiness_bonus; - if (total_yield <= 0) + JGL_Image * canvas_img = city_form->Base.Data.Canvas.JGL.Image; + JGL_Image * resource_sheet_img = (is->resources_sheet != NULL) ? is->resources_sheet->JGL.Image : NULL; + int resource_icon_count = (generated_resource_id >= 0 && canvas_img != NULL && resource_sheet_img != NULL && TransparentBlt != NULL) ? 1 : 0; + + int total_icons = total_yield + resource_icon_count; + if (total_icons <= 0) return; // Get sprites @@ -23777,7 +23896,7 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s int sprite_height = food_sprite->Height; // Calculate total width of all icons - int total_width = total_yield * sprite_width; + int total_width = total_icons * sprite_width; // Center the icons horizontally int half_width = total_width >> 1; @@ -23792,8 +23911,8 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s // Adjust spacing if icons would exceed tile width int spacing = sprite_width; if (total_width > tile_width - 10) { - if (total_yield > 1) { - spacing = (tile_width - 10 - sprite_width) / (total_yield - 1); + if (total_icons > 1) { + spacing = (tile_width - 10 - sprite_width) / (total_icons - 1); if (spacing < 1) spacing = 1; else if (spacing > sprite_width) @@ -23831,6 +23950,34 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s Sprite_draw (happiness_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); pixel_x += spacing; } + + if (resource_icon_count > 0) { + int icon_id = p_bic_data->ResourceTypes[generated_resource_id].IconID, + sheet_row = icon_id / 6, + sheet_col = icon_id % 6; + + int resource_height = 24, + resource_width = 24, + resource_pixel_y = pixel_y; + if (sprite_height > resource_height) + resource_pixel_y += (sprite_height - resource_height) >> 1; + + HDC canvas_dc = canvas_img->vtable->acquire_dc (canvas_img); + if (canvas_dc != NULL) { + HDC sheet_dc = resource_sheet_img->vtable->acquire_dc (resource_sheet_img); + if (sheet_dc != NULL) { + TransparentBlt (canvas_dc, + pixel_x, resource_pixel_y, resource_width, resource_height, + sheet_dc, + 9 + 50*sheet_col, 9 + 50*sheet_row, 33, 33, + 0xFF00FF); + resource_sheet_img->vtable->release_dc (resource_sheet_img, __, 1); + } + canvas_img->vtable->release_dc (canvas_img, __, 1); + } + + pixel_x += spacing; + } } void @@ -24831,7 +24978,8 @@ init_district_images () int era_count = cfg->vary_img_by_era ? 4 : 1; int column_count = cfg->max_building_index + 1; - int sprite_height = cfg->extended_height ? 88 : 64; + int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; + int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; // For each cultural variant for (int variant_i = 0; variant_i < variant_count; variant_i++) { @@ -24863,15 +25011,15 @@ init_district_images () } // For each era - for (int era_i = 0; era_i < era_count; era_i++) { + for (int era_i = 0; era_i < era_count; era_i++) { // For each column in the image (variations on the district image for that era) for (int col_i = 0; col_i < column_count; col_i++) { Sprite_construct (&is->district_img_sets[dc].imgs[variant_i][era_i][col_i]); - int x = 128 * col_i, + int x = sprite_width * col_i, y = sprite_height * era_i; - Sprite_slice_pcx (&is->district_img_sets[dc].imgs[variant_i][era_i][col_i], __, &pcx, x, y, 128, sprite_height, 1, 1); + Sprite_slice_pcx (&is->district_img_sets[dc].imgs[variant_i][era_i][col_i], __, &pcx, x, y, sprite_width, sprite_height, 1, 1); } } @@ -25408,9 +25556,9 @@ wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner } if ((best_dist == INT_MAX) || (best_dx == 0)) - return false; + return true; - return best_dx < 0; + return best_dx > 0; } void @@ -25424,6 +25572,24 @@ draw_district_on_map_or_canvas(Sprite * sprite, Map_Renderer * map_renderer, int } } +bool +district_allows_river (struct district_config const * cfg) +{ + unsigned int build_mask = cfg->buildable_square_types_mask; + if (build_mask == 0) + build_mask = district_default_buildable_mask (); + return (build_mask & square_type_mask_bit (SQ_RIVER)) != 0; +} + +bool +wonder_allows_river (struct wonder_district_config const * cfg) +{ + unsigned int build_mask = wonder_buildable_square_type_mask (cfg); + if (build_mask == 0) + build_mask = district_default_buildable_mask (); + return (build_mask & square_type_mask_bit (SQ_RIVER)) != 0; +} + void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { @@ -25484,11 +25650,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par // Handle river alignment, if district allows it enum direction river_dir = DIR_ZERO; - unsigned int build_mask = cfg->buildable_square_types_mask; - if (build_mask == 0) - build_mask = district_default_buildable_mask (); - bool river_allowed = (build_mask & square_type_mask_bit (SQ_RIVER)) != 0; - if (river_allowed) + if (district_allows_river(cfg)) align_district_with_river (tile, &pixel_x, &pixel_y, &river_dir); if (territory_owner_id > 0) { @@ -25544,6 +25706,10 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par struct wonder_district_config * wcfg = &is->wonder_district_configs[windex]; struct wonder_district_image_set * set = &is->wonder_district_img_sets[windex]; + + if (wonder_allows_river(wcfg)) + align_district_with_river (tile, &pixel_x, &pixel_y, &river_dir); + bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alternative_direction_image (tile_x, tile_y, territory_owner_id, wcfg); Sprite * wsprite = (use_alt_dir && (set->alt_dir_img.vtable != NULL)) ? &set->alt_dir_img : &set->img; @@ -25580,9 +25746,11 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; - int sprite_height = cfg->extended_height ? 88 : 64; + int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; + int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; + int draw_x = pixel_x - ((sprite_width - 128) / 2); int draw_y = pixel_y - (sprite_height - 64); - draw_district_on_map_or_canvas(district_sprite, map_renderer, pixel_x, draw_y); + draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); return; } @@ -25840,7 +26008,8 @@ patch_Unit_ai_move_terraformer (Unit * this) update_tracked_worker_for_unit (this); struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && inst->district_type != NATURAL_WONDER_DISTRICT_ID) { + if (inst != NULL && inst->district_type != NATURAL_WONDER_DISTRICT_ID && + tile->vtable->m50_Get_Square_BaseType (tile) != SQ_Coast) { // Roads should be made after district builds. The district is complete but // worker is still likely on the tile, so check here and build road if needed bool has_road = (*tile->vtable->m25_Check_Roads)(tile, __, 0); From 59b7d80fd01070c68860d48b5b811d5aa310b5de Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 17 Dec 2025 14:28:06 -0800 Subject: [PATCH 121/356] Update distro hub divisor formula; Fix wonder alt dir direction --- Art/Districts/1200/Wonders_2.PCX | Bin 36851 -> 36826 bytes default.c3x_config.ini | 13 +++++++------ injected_code.c | 11 +++++++---- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index 1e9cb056fab18e4fc93e24e7ec34de8895b95022..b29c1087b10f54a4f4a1cf59c870947db184ee71 100644 GIT binary patch delta 345 zcmex7pXt_orVU%#nLL#yZ)?|P^ql;tU5D2;O5gh6BQqrpw?htclchV9c|al>uB`6i zAOY_VRUSTlTYr7~Lz;2fadMMuI#f^v)^}(CwbUJox7O5>(-YR3`~;{1ML@h$lW(5R zAqgc*4efMob5=#U$pM|}yr?3rooc*+IulLn%#0OT&BfdfP2SO|!G|KEHTiR=Ht!(` z9gT_Mb*$lsSheIPYj^1i-~4~@BZ{c~t;soES_0x`vInokA2O5EG}rXh6qB30yh~Tm z6hriRmom@10BaprR!yxYabLN~65T5NH~u3FU7PINEyJqhD{gK&Ilo(u6hO(63o?^g4azir6lyYeR>k5uvx>#5-V6w@%@h)UNDR+%cXk`$k}5EE zHbDq%u&}#>AR<-{;@bGJ>XLM7zmv5;oWr@^tGw=2e*bAyVx+^y!aJn<#-hU~ogHn3 z%1H1GO*ZEoW7!!hh!gDaIc+pQeK=T!om~qo+kz?DZqASL?HZqQUuS>`)HVHTqBE^5 z{-+QuKgoe^hLqgr9v6HHGLSW;X*Bi6_$4$dGC|@i!+kHKDD|oyXqERPSGp+qmcFH* z9TY2(MB#yQHPS(f<8?k>5iZ|(hW~Ou;-^?sFI{lL*;QLe0|{8e?qq9e;<}Uu`B56z zL^WmSil4i**{Xfa>$dw-i8=(J-z0{9w3fyVKX4)Sa7WF?u9iU*&_!#m$}d$v55bQD z!pj)EGfS>70duHUS O%v`Cl+xgz!djB5}IiiIC diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 4ac11d06..bd89e5b4 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -871,23 +871,24 @@ completed_wonder_districts_can_be_destroyed = true destroyed_wonders_can_be_built_again = true ; Distribution Hubs work as "breadbaskets" and mining areas far from urban centers, minimizing local city potential but benefiting the entire civilization. -; Distribution Hubs make surrounding tiles unworkable and instead distribute their raw food and shield yields to ALL connected cities in your civilization. -; "Divisors" for food and shields divide the raw yield values before distribution to your cities. Yields are subject to corruption as with regular shields. +; Distribution Hubs make surrounding tiles unworkable and instead distribute their raw food and shield yields to ALL connected cities in your civilization. +; "Divisors" for food and shields multiply the sqrt-based divisor in scale-by-city-count mode, and are a straight divider in flat mode. Yields are subject +; to corruption as with regular shields. ; -; ai_ideal_distribution_hub_count_per_100_cities setting controls how many hubs the AI tries to maintain per 100 cities if distribution_hub_yield_division_mode +; ai_ideal_distribution_hub_count_per_100_cities controls how many hubs the AI tries to maintain per 100 cities if distribution_hub_yield_division_mode ; is set to "flat" (e.g., 25 means the AI aims for 1 hub per 4 cities). ; ; distribution_hub_yield_division_mode controls how a hub splits its collected food/shields across connected cities: ; flat: Divide raw yields by the configured divisors (distribution_hub_food_yield_divisor / distribution_hub_shield_yield_divisor) -; scale-by-city-count: Divide by (connected city count / divisor), letting hubs scale with network size (e.g., 12 food, 10 cities, divisor 2 => 12 / (10/2) = 2.4 -> 2) +; scale-by-city-count: More cities plugged into the hub, the thinner the bonus decays (floor(sqrt(connected city count)) * divisor) ; ; ai_distribution_hub_build_strategy controls how the AI decides to build distribution hubs: ; by-city-count: AI builds hubs based on its ideal hub count per 100 cities ; auto: AI dynamically assesses need based on city growth and food/shield deficits, capped at max 1 hub for every 2 cities distribution_hub_yield_division_mode = scale-by-city-count ai_distribution_hub_build_strategy = auto -distribution_hub_food_yield_divisor = 3 -distribution_hub_shield_yield_divisor = 4 +distribution_hub_food_yield_divisor = 1 +distribution_hub_shield_yield_divisor = 1 ai_ideal_distribution_hub_count_per_100_cities = 25 central_rail_hub_distribution_food_bonus_percent = 25 diff --git a/injected_code.c b/injected_code.c index b63d12dc..0b0fa5eb 100644 --- a/injected_code.c +++ b/injected_code.c @@ -4636,8 +4636,11 @@ recompute_distribution_hub_yields (struct distribution_hub_record * rec) connected_city_count = 1; if (is->current_config.distribution_hub_yield_division_mode == DHYDM_SCALE_BY_CITY_COUNT) { - int city_food_divisor = connected_city_count / food_div; - int city_shield_divisor = connected_city_count / shield_div; + int city_root = 1; + while ((city_root + 1) * (city_root + 1) <= connected_city_count) + city_root++; + int city_food_divisor = city_root * food_div; + int city_shield_divisor = city_root * shield_div; if (city_food_divisor < 1) city_food_divisor = 1; if (city_shield_divisor < 1) @@ -25501,7 +25504,7 @@ wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner // We only care about the nearest same-civ city in the work area around the tile. // Assumes the base wonder art (img_row/column) faces west and the alt art faces east. - // To "face away" from the nearest city, we pick the alt art when that city lies to the west. + // To "face away" from the nearest city, we pick the alt art when that city lies to the east. Tile * center = tile_at (tile_x, tile_y); if ((center == NULL) || (center == p_null_tile)) return false; @@ -25556,7 +25559,7 @@ wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner } if ((best_dist == INT_MAX) || (best_dx == 0)) - return true; + return false; return best_dx > 0; } From a6a63be2533ec29fcb3d91819744bf20d1d8bee1 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 17 Dec 2025 15:55:55 -0800 Subject: [PATCH 122/356] Allow hydro dams to be built if river within work radius; Prevent AI cities from building wonders they dont have terrain for --- injected_code.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/injected_code.c b/injected_code.c index 0b0fa5eb..bd694ff2 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16132,6 +16132,16 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply return false; // Else proceed with standard improvement checks + } else if (improv->ImprovementFlags & ITF_Must_Be_Near_River) { + bool has_river_in_radius = false; + FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { + if (wai.tile->vtable->m37_Get_River_Code (wai.tile)) { + has_river_in_radius = true; + break; + } + } + if (! has_river_in_radius) + return false; } else { return base; } @@ -16140,6 +16150,31 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply // Different logic for human vs AI players bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; + // For AI, only allow wonders if there's a valid tile in the city's work radius + if ((! is_human) && + is->current_config.enable_wonder_districts && + (i_improv >= 0) && (i_improv < p_bic_data->ImprovementsCount)) { + Improvement * improv = &p_bic_data->Improvements[i_improv]; + if (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { + bool has_buildable_tile = false; + FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { + Tile * tile = wai.tile; + if (! tile_suitable_for_district (tile, WONDER_DISTRICT_ID, this, NULL)) + continue; + if (! wonder_is_buildable_on_tile (tile, i_improv)) + continue; + if (is_tile_earmarked_for_district (wai.tile_x, wai.tile_y)) + continue; + + has_buildable_tile = true; + break; + } + + if (! has_buildable_tile) + return false; + } + } + // Check if this is a wonder and if wonder districts are enabled if (is_human && is->current_config.enable_wonder_districts && (i_improv >= 0) && (i_improv < p_bic_data->ImprovementsCount)) { From 603daaab2120c2f65c77700f838ae01e5d851a3b Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 17 Dec 2025 16:29:54 -0800 Subject: [PATCH 123/356] Ensure AI workers build roads, not needless cross coast --- injected_code.c | 77 +++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 50 deletions(-) diff --git a/injected_code.c b/injected_code.c index bd694ff2..b305fef2 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16120,6 +16120,9 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply return base; if (! base) { + if (patch_City_has_improvement (this, __, i_improv, false)) + return false; + // Allow harbor-like improvements when port districts replace coastal adjacency Improvement * improv = &p_bic_data->Improvements[i_improv]; if (improv->ImprovementFlags & ITF_Allows_Water_Trade && @@ -16132,7 +16135,8 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply return false; // Else proceed with standard improvement checks - } else if (improv->ImprovementFlags & ITF_Must_Be_Near_River) { + } /* TODO: Uncomment this when Power Stations are added + else if (improv->ImprovementFlags & ITF_Must_Be_Near_River) { bool has_river_in_radius = false; FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { if (wai.tile->vtable->m37_Get_River_Code (wai.tile)) { @@ -16142,7 +16146,7 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply } if (! has_river_in_radius) return false; - } else { + } */ else { return base; } } @@ -23890,17 +23894,9 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s return; // Get district configuration - struct district_config * config = &is->district_configs[district_id]; + struct district_config const * config = &is->district_configs[district_id]; struct district_instance * inst = get_district_instance (tile); - int generated_resource_id = -1; - if ((config->generated_resource_id >= 0) && - (city_form != NULL) && (city_form->CurrentCity != NULL) && - ((config->generated_resource_flags & MF_SHOW_BONUS) || (p_bic_data->ResourceTypes[config->generated_resource_id].Class != RC_Bonus)) && - (! ((config->generated_resource_flags & MF_HIDE_NON_BONUS) && (p_bic_data->ResourceTypes[config->generated_resource_id].Class != RC_Bonus))) && - district_can_generate_resource (city_form->CurrentCity->Body.CivID, config)) - generated_resource_id = config->generated_resource_id; - // Count total yields from bonuses int food_bonus = 0, shield_bonus = 0, gold_bonus = 0, science_bonus = 0, culture_bonus = 0, happiness_bonus = 0; get_effective_district_yields (inst, config, &food_bonus, &shield_bonus, &gold_bonus, &science_bonus, &culture_bonus, &happiness_bonus); @@ -23913,12 +23909,7 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s if (culture_bonus > 0) total_yield += culture_bonus; if (happiness_bonus > 0) total_yield += happiness_bonus; - JGL_Image * canvas_img = city_form->Base.Data.Canvas.JGL.Image; - JGL_Image * resource_sheet_img = (is->resources_sheet != NULL) ? is->resources_sheet->JGL.Image : NULL; - int resource_icon_count = (generated_resource_id >= 0 && canvas_img != NULL && resource_sheet_img != NULL && TransparentBlt != NULL) ? 1 : 0; - - int total_icons = total_yield + resource_icon_count; - if (total_icons <= 0) + if (total_yield <= 0) return; // Get sprites @@ -23934,7 +23925,7 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s int sprite_height = food_sprite->Height; // Calculate total width of all icons - int total_width = total_icons * sprite_width; + int total_width = total_yield * sprite_width; // Center the icons horizontally int half_width = total_width >> 1; @@ -23949,8 +23940,8 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s // Adjust spacing if icons would exceed tile width int spacing = sprite_width; if (total_width > tile_width - 10) { - if (total_icons > 1) { - spacing = (tile_width - 10 - sprite_width) / (total_icons - 1); + if (total_yield > 1) { + spacing = (tile_width - 10 - sprite_width) / (total_yield - 1); if (spacing < 1) spacing = 1; else if (spacing > sprite_width) @@ -23988,34 +23979,6 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s Sprite_draw (happiness_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); pixel_x += spacing; } - - if (resource_icon_count > 0) { - int icon_id = p_bic_data->ResourceTypes[generated_resource_id].IconID, - sheet_row = icon_id / 6, - sheet_col = icon_id % 6; - - int resource_height = 24, - resource_width = 24, - resource_pixel_y = pixel_y; - if (sprite_height > resource_height) - resource_pixel_y += (sprite_height - resource_height) >> 1; - - HDC canvas_dc = canvas_img->vtable->acquire_dc (canvas_img); - if (canvas_dc != NULL) { - HDC sheet_dc = resource_sheet_img->vtable->acquire_dc (resource_sheet_img); - if (sheet_dc != NULL) { - TransparentBlt (canvas_dc, - pixel_x, resource_pixel_y, resource_width, resource_height, - sheet_dc, - 9 + 50*sheet_col, 9 + 50*sheet_row, 33, 33, - 0xFF00FF); - resource_sheet_img->vtable->release_dc (resource_sheet_img, __, 1); - } - canvas_img->vtable->release_dc (canvas_img, __, 1); - } - - pixel_x += spacing; - } } void @@ -26838,8 +26801,22 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t Tile * dest = tile_at (to_x, to_y); if ((dest != NULL) && dest->vtable->m35_Check_Is_Water (dest) && - (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) - return PBV_OK; // Let workers treat coast as passable when workers_can_enter_coast is on + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) { + bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; + + // If human, okay to enter coast tile + if (is_human) + return PBV_OK; + + // If AI, only okay if moving to a wonder district. If not, they AI will + // sometimes not create roads to connect cities + struct district_worker_record * rec = get_tracked_worker_record (this); + struct pending_district_request * req = (rec != NULL) ? rec->pending_req : NULL; + if ((req != NULL) && + (req->district_id == WONDER_DISTRICT_ID) && + (req->target_x == to_x) && (req->target_y == to_y)) + return PBV_OK; + } } return base; From be2b4bc85659ffba4baf40e57661212aaf832da8 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 18 Dec 2025 15:58:56 -0800 Subject: [PATCH 124/356] Add feature for reduce pop if neighborhood destroyed; Better handling of naval unit production; first attempts at prevent wonder creation if city set to build it as fallback --- C3X.h | 28 ++++++--- Text/c3x-labels.txt | 4 +- civ_prog_objects.csv | 1 + injected_code.c | 135 +++++++++++++++++++++++++++++++++++++------ 4 files changed, 142 insertions(+), 26 deletions(-) diff --git a/C3X.h b/C3X.h index 9e068d2f..20a6d8f3 100644 --- a/C3X.h +++ b/C3X.h @@ -357,6 +357,7 @@ struct c3x_config { int maximum_pop_before_neighborhood_needed; int per_neighborhood_pop_growth_enabled; int neighborhood_needed_message_frequency; + bool destroying_neighborhood_reduces_pop; bool completed_wonder_districts_can_be_destroyed; bool destroyed_wonders_can_be_built_again; @@ -484,9 +485,12 @@ enum c3x_label { CL_BUILDING, // Districts-related texts - CL_REQUIRES_NEIGHBORHOOD_TO_GROW, + CL_REQUIRES, + CL_TO_GROW, CL_DISTRICT_DESTROYED_BY_VOLCANO, CL_CONSTRUCTION_HALTED_DUE_TO_MISSING_DISTRICT, + CL_LOST_POPULATION_DUE_TO_DESTROYED_NEIGHBORHOOD, + CL_RECEIVED, CL_FROM_SHARED, CL_WITH, @@ -677,7 +681,8 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 5, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, - .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 25 + .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 25, + .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, { @@ -687,7 +692,8 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"WonderDistrict.pcx"}, .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Coast)), .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, + .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, { @@ -697,7 +703,8 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"DistributionHub.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 2, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, + .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, { @@ -707,7 +714,8 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"Aerodrome.pcx"}, .dependent_improvements = {"Airport"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 1, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, + .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, { .command = -1, .name = "Natural Wonder", .tooltip = NULL, @@ -716,7 +724,8 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {0}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 0, .max_building_index = 0, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, + .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", @@ -725,7 +734,8 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .dependent_improvements = {"Harbor", "Commercial Dock"}, .buildable_square_types_mask = (1 << SQ_Coast), .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .align_to_coast = true, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, + .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, { .command = UCV_Build_CentralRailHub, .name = "Central Rail Hub", .tooltip = "Build Central Rail Hub", @@ -734,7 +744,8 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"CentralRailHub_AMER.pcx", "CentralRailHub_EURO.pcx", "CentralRailHub_ROMAN.pcx", "CentralRailHub_MIDEAST.pcx", "CentralRailHub_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 5, .max_building_index = 0, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, - .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0 + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, + .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 } }; @@ -1261,6 +1272,7 @@ struct injected_state { // first inside the loop over improvements then again inside a loop over unit types. The var is used by the intercept consideration functions // which run at the end of each loop iteration. City_Order ai_considering_order; + int handling_ai_district_fallback; // Used in the code that adds additional info to the tile info box int viewing_tile_info_x, viewing_tile_info_y; diff --git a/Text/c3x-labels.txt b/Text/c3x-labels.txt index de4b5e26..56fc4cda 100644 --- a/Text/c3x-labels.txt +++ b/Text/c3x-labels.txt @@ -112,9 +112,11 @@ Bombarding Building ; Districts-related texts -requires a Neighborhood to grow +requires a +to grow destroyed by volcanic eruption! construction halted due to missing +lost population due to destroyed ; Shown if City A receives a building from City B via shared district, as in " received from shared with ". received diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 71c1631b..8f18f26a 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -855,3 +855,4 @@ define, 0xCC2BB0, 0xCE54BC, 0xCC2B70, "p_got_leader_gender", "int *" ignore, 0x49D070, 0x4A3AF0, 0x49D100, "Advisor_GUI_open", "void (__fastcall *) (Advisor_GUI * this, int edx, AdvisorKind kind)" ignore, 0x4BF660, 0x4C6C10, 0x4BF6F0, "City_draw_citizens", "void (__fastcall *) (City * this, int edx, PCX_Image * canvas, RECT * rect, char param_3)" ignore, 0x4B9F60, 0x4C15D0, 0x4B9FF0, "City_add_population", "void (__fastcall *) (City * this, int edx, int num, int race_id)" +define, 0x4BA230, 0x0, 0x0, "City_remove_population", "void (__fastcall *) (City * this, int edx, int num_pops, int race_id, char param_3)" diff --git a/injected_code.c b/injected_code.c index b305fef2..1f6b9d2a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -9426,7 +9426,12 @@ maybe_show_neighborhood_growth_warning (City * city) char msg[160]; char const * city_name = city->Body.CityName; - snprintf (msg, sizeof msg, "%s %s", city_name, is->c3x_labels[CL_REQUIRES_NEIGHBORHOOD_TO_GROW]); + snprintf (msg, sizeof msg, "%s %s %s", + city_name, + is->c3x_labels[CL_REQUIRES], + is->district_configs[NEIGHBORHOOD_DISTRICT_ID].display_name, + is->c3x_labels[CL_TO_GROW] + ); msg[(sizeof msg) - 1] = '\0'; show_map_specific_text (city->Body.X, city->Body.Y, msg, true); } @@ -9456,6 +9461,50 @@ remove_building_if_no_district (City * city, int district_id, int building_id) return true; } +void +reduce_city_population_due_to_lost_neighborhood (City * city) +{ + if (city == NULL) + return; + + if (! (is->current_config.enable_districts && + is->current_config.enable_neighborhood_districts && + is->current_config.destroying_neighborhood_reduces_pop)) + return; + + int base_cap = is->current_config.maximum_pop_before_neighborhood_needed; + int per_neighborhood = is->current_config.per_neighborhood_pop_growth_enabled; + if ((base_cap <= 0) || (per_neighborhood <= 0)) + return; + + int neighborhoods = count_neighborhoods_in_city_radius (city); + int cap = base_cap + (per_neighborhood * neighborhoods); + if (cap < base_cap) + cap = base_cap; + + int pop = city->Body.Population.Size; + if (pop <= cap) + return; + + int to_remove = pop - cap; + int removed = 0; + while (to_remove-- > 0) { + City_remove_population (city, __, 1, -1, '\0'); + removed++; + } + + if ((removed > 0) && ((*p_human_player_bits & (1 << city->Body.CivID)) != 0)) { + char msg[160]; + snprintf (msg, sizeof msg, "%s %s %s", + city->Body.CityName, + is->c3x_labels[CL_LOST_POPULATION_DUE_TO_DESTROYED_NEIGHBORHOOD], + is->district_configs[NEIGHBORHOOD_DISTRICT_ID].display_name + ); + msg[(sizeof msg) - 1] = '\0'; + show_map_specific_text (city->Body.X, city->Body.Y, msg, true); + } +} + bool city_has_other_completed_district (City * city, int district_id, int removed_x, int removed_y) { @@ -9657,6 +9706,8 @@ handle_district_removed (Tile * tile, int district_id, int center_x, int center_ remove_district_instance (tile); + bool removed_neighborhood = actual_district_id == NEIGHBORHOOD_DISTRICT_ID; + if (is->current_config.enable_wonder_districts && (actual_district_id == WONDER_DISTRICT_ID) && (wonder_windex >= 0)) @@ -9678,6 +9729,8 @@ handle_district_removed (Tile * tile, int district_id, int center_x, int center_ int tile_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); FOR_CITIES_AROUND (wai, center_x, center_y) { + if (removed_neighborhood) + reduce_city_population_due_to_lost_neighborhood (wai.city); recompute_city_yields_with_districts (wai.city); } @@ -12235,6 +12288,7 @@ patch_init_floating_point () {"cities_with_mutual_district_receive_wonders" , false, offsetof (struct c3x_config, cities_with_mutual_district_receive_wonders)}, {"show_message_when_building_received_by_mutual_district", false, offsetof (struct c3x_config, show_message_when_building_received_by_mutual_district)}, {"show_message_when_building_lost_to_destroyed_district" , false, offsetof (struct c3x_config, show_message_when_building_lost_to_destroyed_district)}, + {"destroying_neighborhood_reduces_pop" , false, offsetof (struct c3x_config, destroying_neighborhood_reduces_pop)}, {"air_units_use_aerodrome_districts_not_cities" , false, offsetof (struct c3x_config, air_units_use_aerodrome_districts_not_cities)}, {"naval_units_use_port_districts_not_cities" , false, offsetof (struct c3x_config, naval_units_use_port_districts_not_cities)}, {"show_natural_wonder_name_on_map" , false, offsetof (struct c3x_config, show_natural_wonder_name_on_map)}, @@ -16089,21 +16143,26 @@ patch_City_can_build_unit (City * this, int edx, int unit_type_id, bool exclude_ int available; if (get_available_unit_count (&leaders[this->Body.CivID], unit_type_id, &available) && (available <= 0)) return false; + } - if (is->current_config.enable_districts && - is->current_config.enable_aerodrome_districts && - is->current_config.air_units_use_aerodrome_districts_not_cities) { - UnitType * type = &p_bic_data->UnitTypes[unit_type_id]; - if (type->Unit_Class == UTC_Air && ! city_has_required_district (this, AERODROME_DISTRICT_ID)) - return false; - } + if (is->current_config.enable_districts) { + UnitType * type = &p_bic_data->UnitTypes[unit_type_id]; - if (is->current_config.enable_districts && - is->current_config.enable_port_districts && - is->current_config.naval_units_use_port_districts_not_cities) { - UnitType * type = &p_bic_data->UnitTypes[unit_type_id]; - if (type->Unit_Class == UTC_Sea && ! city_has_required_district (this, PORT_DISTRICT_ID)) - return false; + // Bail if tech reqs are not met + int prereq_id = type->AdvReq; + if (prereq_id >= 0 && ! Leader_has_tech (&leaders[this->Body.CivID], __, prereq_id)) + return false; + + // Air units + if (type->Unit_Class == UTC_Air) { + if (is->current_config.enable_aerodrome_districts && is->current_config.air_units_use_aerodrome_districts_not_cities) { + return city_has_required_district (this, AERODROME_DISTRICT_ID); + } + // Naval units + } else if (type->Unit_Class == UTC_Sea) { + if (is->current_config.enable_port_districts && is->current_config.naval_units_use_port_districts_not_cities) { + return city_has_required_district (this, PORT_DISTRICT_ID); + } } } @@ -24688,7 +24747,25 @@ patch_City_add_building_if_done (City * this) // As in the base logic, if production gets switched, the game doesn't check if it might still complete on the same turn. return; + } + + // If production ended up on a wonder, make sure the city can actually build it; otherwise fall back to a defensive unit. + /* + int order_type = this->Body.Order_Type; + int order_id = this->Body.Order_ID; + if (is->current_config.enable_districts && order_type == COT_Improvement) { + Improvement * new_improv = &p_bic_data->Improvements[order_id]; // Improvement might have changed + if (new_improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { + if (! patch_City_can_build_improvement (this, __, order_id, true)) { + City_Order defensive_order = { .OrderID = -1, .OrderType = 0 }; + if (choose_defensive_unit_order (this, &defensive_order)) { + City_set_production (this, __, defensive_order.OrderType, defensive_order.OrderID, false); + return; + } + } + } } + */ City_add_building_if_done (this); } @@ -26704,6 +26781,12 @@ patch_City_set_production (City * this, int edx, int order_type, int order_id, b if (! is->current_config.enable_districts || ! is->current_config.enable_wonder_districts) return; + // If production is set to a wonder, make sure the city can legally build it; otherwise pick a defensive unit. + + int current_order_type = this->Body.Order_Type; + int current_order_id = this->Body.Order_ID; + bool wonder_is_valid = true; + // If the human player, we need to set/unset a wonder district for this city, depending // on what is being built. The human player wouldn't be able to choose a wonder if a wonder // district wasn't available, so we don't need to worry about feasibility here. @@ -26713,16 +26796,34 @@ patch_City_set_production (City * this, int edx, int order_type, int order_id, b return; bool release_reservation = true; - if (order_type == COT_Improvement) { - Improvement * improv = &p_bic_data->Improvements[order_id]; + if (wonder_is_valid && (current_order_type == COT_Improvement)) { + Improvement * improv = &p_bic_data->Improvements[current_order_id]; if (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { - if (reserve_wonder_district_for_city (this, order_id)) + if (reserve_wonder_district_for_city (this, current_order_id)) release_reservation = false; } } if (release_reservation) release_wonder_district_reservation (this); +/* + if ((current_order_type == COT_Improvement) && + (current_order_id >= 0) && (current_order_id < p_bic_data->ImprovementsCount)) { + Improvement * current_improv = &p_bic_data->Improvements[current_order_id]; + if (current_improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { + wonder_is_valid = patch_City_can_build_improvement (this, __, current_order_id, true); + if (! wonder_is_valid) { + City_Order defensive_order = { .OrderID = -1, .OrderType = 0 }; + if (choose_defensive_unit_order (this, &defensive_order)) { + City_set_production (this, __, defensive_order.OrderType, defensive_order.OrderID, false); + current_order_type = this->Body.Order_Type; + current_order_id = this->Body.Order_ID; + wonder_is_valid = true; + } + } + } + } + */ } int __fastcall From b9d0428c71137d44b8a6050a7b7f759975067883 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 18 Dec 2025 16:50:43 -0800 Subject: [PATCH 125/356] Mostly sorted out city build improvements --- injected_code.c | 76 +++++++++++++++++++------------------------------ 1 file changed, 29 insertions(+), 47 deletions(-) diff --git a/injected_code.c b/injected_code.c index 1f6b9d2a..4f5dba77 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16178,12 +16178,16 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply if (! is->current_config.enable_districts) return base; + Improvement * improv = &p_bic_data->Improvements[i_improv]; if (! base) { if (patch_City_has_improvement (this, __, i_improv, false)) return false; + // Ensure prereq tech for the improvement + if (improv->RequiredTechID >= 0 && ! Leader_has_tech (&leaders[this->Body.CivID], __, improv->RequiredTechID)) + return false; + // Allow harbor-like improvements when port districts replace coastal adjacency - Improvement * improv = &p_bic_data->Improvements[i_improv]; if (improv->ImprovementFlags & ITF_Allows_Water_Trade && is->current_config.enable_port_districts && is->current_config.naval_units_use_port_districts_not_cities) { @@ -16193,8 +16197,21 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply if (find_tile_for_district (this, PORT_DISTRICT_ID, &tx, &ty) == NULL) return false; - // Else proceed with standard improvement checks - } /* TODO: Uncomment this when Power Stations are added + // Allow improvements that must be near water + } else if (improv->ImprovementFlags & ITF_Must_Be_Near_Water || // For things like nuclear power plant + improv->Characteristics & 1) { // For coastal wonders - go figure + bool has_water_in_radius = false; + FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { + if (wai.tile->vtable->m35_Check_Is_Water (wai.tile)) { + has_water_in_radius = true; + break; + } + } + if (! has_water_in_radius) + return false; + } + + /* TODO: Uncomment this when Power Stations are added else if (improv->ImprovementFlags & ITF_Must_Be_Near_River) { bool has_river_in_radius = false; FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { @@ -16214,20 +16231,14 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; // For AI, only allow wonders if there's a valid tile in the city's work radius - if ((! is_human) && - is->current_config.enable_wonder_districts && - (i_improv >= 0) && (i_improv < p_bic_data->ImprovementsCount)) { - Improvement * improv = &p_bic_data->Improvements[i_improv]; + if ((! is_human) && is->current_config.enable_wonder_districts) { if (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { bool has_buildable_tile = false; FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { Tile * tile = wai.tile; - if (! tile_suitable_for_district (tile, WONDER_DISTRICT_ID, this, NULL)) - continue; - if (! wonder_is_buildable_on_tile (tile, i_improv)) - continue; - if (is_tile_earmarked_for_district (wai.tile_x, wai.tile_y)) - continue; + if (! tile_suitable_for_district (tile, WONDER_DISTRICT_ID, this, NULL)) continue; + if (! wonder_is_buildable_on_tile (tile, i_improv)) continue; + if (is_tile_earmarked_for_district (wai.tile_x, wai.tile_y)) continue; has_buildable_tile = true; break; @@ -16239,9 +16250,7 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply } // Check if this is a wonder and if wonder districts are enabled - if (is_human && is->current_config.enable_wonder_districts && - (i_improv >= 0) && (i_improv < p_bic_data->ImprovementsCount)) { - Improvement * improv = &p_bic_data->Improvements[i_improv]; + if (is_human && is->current_config.enable_wonder_districts) { if (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { bool wonder_requires_district = false; int required_id; @@ -16249,8 +16258,7 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply wonder_requires_district = true; // Can only build wonders that need districts if an incomplete wonder district exists - if (wonder_requires_district && - ! city_has_wonder_district_with_no_completed_wonder (this, i_improv)) + if (wonder_requires_district && ! city_has_wonder_district_with_no_completed_wonder (this, i_improv)) return !apply_strict_rules; } } @@ -24750,7 +24758,6 @@ patch_City_add_building_if_done (City * this) } // If production ended up on a wonder, make sure the city can actually build it; otherwise fall back to a defensive unit. - /* int order_type = this->Body.Order_Type; int order_id = this->Body.Order_ID; if (is->current_config.enable_districts && order_type == COT_Improvement) { @@ -24765,7 +24772,6 @@ patch_City_add_building_if_done (City * this) } } } - */ City_add_building_if_done (this); } @@ -26781,12 +26787,6 @@ patch_City_set_production (City * this, int edx, int order_type, int order_id, b if (! is->current_config.enable_districts || ! is->current_config.enable_wonder_districts) return; - // If production is set to a wonder, make sure the city can legally build it; otherwise pick a defensive unit. - - int current_order_type = this->Body.Order_Type; - int current_order_id = this->Body.Order_ID; - bool wonder_is_valid = true; - // If the human player, we need to set/unset a wonder district for this city, depending // on what is being built. The human player wouldn't be able to choose a wonder if a wonder // district wasn't available, so we don't need to worry about feasibility here. @@ -26796,34 +26796,16 @@ patch_City_set_production (City * this, int edx, int order_type, int order_id, b return; bool release_reservation = true; - if (wonder_is_valid && (current_order_type == COT_Improvement)) { - Improvement * improv = &p_bic_data->Improvements[current_order_id]; + if (this->Body.Order_Type == COT_Improvement) { + Improvement * improv = &p_bic_data->Improvements[this->Body.Order_ID]; if (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { - if (reserve_wonder_district_for_city (this, current_order_id)) + if (reserve_wonder_district_for_city (this, this->Body.Order_ID)) release_reservation = false; } } if (release_reservation) release_wonder_district_reservation (this); -/* - if ((current_order_type == COT_Improvement) && - (current_order_id >= 0) && (current_order_id < p_bic_data->ImprovementsCount)) { - Improvement * current_improv = &p_bic_data->Improvements[current_order_id]; - if (current_improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { - wonder_is_valid = patch_City_can_build_improvement (this, __, current_order_id, true); - if (! wonder_is_valid) { - City_Order defensive_order = { .OrderID = -1, .OrderType = 0 }; - if (choose_defensive_unit_order (this, &defensive_order)) { - City_set_production (this, __, defensive_order.OrderType, defensive_order.OrderID, false); - current_order_type = this->Body.Order_Type; - current_order_id = this->Body.Order_ID; - wonder_is_valid = true; - } - } - } - } - */ } int __fastcall From 88cf0b04fecc675bdc9db3c8e0bef2aacf06b8e9 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 18 Dec 2025 19:06:44 -0800 Subject: [PATCH 126/356] More refinements to patch_City_can_build_improvement, but not quite there yet --- injected_code.c | 113 ++++++++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/injected_code.c b/injected_code.c index 4f5dba77..e22b9145 100644 --- a/injected_code.c +++ b/injected_code.c @@ -9087,9 +9087,7 @@ city_has_wonder_district_with_no_completed_wonder (City * city, int wonder_impro struct wonder_district_info * info = get_wonder_district_info (candidate); if ((wonder_improv_id >= 0) && ! wonder_is_buildable_on_tile (candidate, wonder_improv_id)) { - if ((info != NULL) && - (info->state == WDS_UNDER_CONSTRUCTION) && - (info->city_id == city->Body.ID)) { + if (info != NULL && info->state == WDS_UNDER_CONSTRUCTION && info->city_id == city->Body.ID) { info->state = WDS_UNUSED; info->city = NULL; info->city_id = -1; @@ -16178,28 +16176,54 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply if (! is->current_config.enable_districts) return base; + // Ensure a district is actually needed + int required_district_id; + bool needs_district = city_requires_district_for_improvement (this, i_improv, &required_district_id); + if (! needs_district) + return base; + Improvement * improv = &p_bic_data->Improvements[i_improv]; + bool is_wonder = improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder); + + // Ensure prereq tech for the improvement + if (improv->RequiredTechID >= 0 && ! Leader_has_tech (&leaders[this->Body.CivID], __, improv->RequiredTechID)) + return false; + + // Different logic for human vs AI players + bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; + + // If disallowed by the base game, check if it was due to not being next to water, which can be + // circumvented with certain districts + bool checked_terrain = false; if (! base) { if (patch_City_has_improvement (this, __, i_improv, false)) return false; - // Ensure prereq tech for the improvement - if (improv->RequiredTechID >= 0 && ! Leader_has_tech (&leaders[this->Body.CivID], __, improv->RequiredTechID)) - return false; - // Allow harbor-like improvements when port districts replace coastal adjacency if (improv->ImprovementFlags & ITF_Allows_Water_Trade && is->current_config.enable_port_districts && is->current_config.naval_units_use_port_districts_not_cities) { int tx, ty; - // If the city has no coastal tiles within its workable area, disallow - if (find_tile_for_district (this, PORT_DISTRICT_ID, &tx, &ty) == NULL) + // Different checks for human vs AI. For human, look for any water. For AI, look for a tile suitable for a port district. + // The port district check is stricter and prevents the AI from trying to build harbors in small lakes, etc. + if (! is_human && find_tile_for_district (this, PORT_DISTRICT_ID, &tx, &ty) == NULL) return false; + else if (is_human) { + bool has_coastal_tile = false; + FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { + if (wai.tile->vtable->m35_Check_Is_Water (wai.tile)) { + has_coastal_tile = true; + break; + } + } + if (! has_coastal_tile) + return false; + } - // Allow improvements that must be near water - } else if (improv->ImprovementFlags & ITF_Must_Be_Near_Water || // For things like nuclear power plant - improv->Characteristics & 1) { // For coastal wonders - go figure + // Allow improvements that must be near water if water in work radius. + // ITF_Must_Be_Near_Water is Nuclear Power Plant, etc. Characteristics & 1 is Coastal Fortress, coastal Wonders, etc. + } else if (improv->ImprovementFlags & ITF_Must_Be_Near_Water || improv->Characteristics & 1) { bool has_water_in_radius = false; FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { if (wai.tile->vtable->m35_Check_Is_Water (wai.tile)) { @@ -16210,8 +16234,8 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply if (! has_water_in_radius) return false; } - /* TODO: Uncomment this when Power Stations are added + // Allow improvements that must be near river if river in work radius else if (improv->ImprovementFlags & ITF_Must_Be_Near_River) { bool has_river_in_radius = false; FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { @@ -16225,58 +16249,35 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply } */ else { return base; } + checked_terrain = true; } - // Different logic for human vs AI players - bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; - - // For AI, only allow wonders if there's a valid tile in the city's work radius - if ((! is_human) && is->current_config.enable_wonder_districts) { - if (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { - bool has_buildable_tile = false; - FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { - Tile * tile = wai.tile; - if (! tile_suitable_for_district (tile, WONDER_DISTRICT_ID, this, NULL)) continue; - if (! wonder_is_buildable_on_tile (tile, i_improv)) continue; - if (is_tile_earmarked_for_district (wai.tile_x, wai.tile_y)) continue; + // Ensure prereq tech for the district + int prereq_id = is->district_infos[required_district_id].advance_prereq_id; + if ((prereq_id >= 0) && ! Leader_has_tech (&leaders[this->Body.CivID], __, prereq_id)) { + return false; + } - has_buildable_tile = true; - break; - } + // Check that we have the necessary terrain + if (! checked_terrain) { + bool has_suitable_tile = false; + FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { + Tile * tile = wai.tile; + if (! tile_suitable_for_district (tile, required_district_id, this, NULL)) continue; + if (is_wonder && ! wonder_is_buildable_on_tile (tile, i_improv)) continue; - if (! has_buildable_tile) - return false; + has_suitable_tile = true; + break; } - } - // Check if this is a wonder and if wonder districts are enabled - if (is_human && is->current_config.enable_wonder_districts) { - if (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { - bool wonder_requires_district = false; - int required_id; - if (itable_look_up (&is->district_building_prereqs, i_improv, &required_id) && (required_id == WONDER_DISTRICT_ID)) - wonder_requires_district = true; - - // Can only build wonders that need districts if an incomplete wonder district exists - if (wonder_requires_district && ! city_has_wonder_district_with_no_completed_wonder (this, i_improv)) - return !apply_strict_rules; + if (! has_suitable_tile) { + return false; } } - // Check if the improvement requires a district and output the required district id when it does - int required_district_id; - bool needs_district = city_requires_district_for_improvement (this, i_improv, &required_district_id); - if (! needs_district) - return true; - - // Ensure prereq tech for the district - int prereq_id = is->district_infos[required_district_id].advance_prereq_id; - if ((prereq_id >= 0) && ! Leader_has_tech (&leaders[this->Body.CivID], __, prereq_id)) - return false; - - // Human doesn't have appropriate district but needs one; allow relaxed checks so UI can gray entry out - if (needs_district && is_human) { - return !apply_strict_rules; + // If human has everything needed except a built district, allow relaxed checks so UI can gray entry out + if (is_human) { + return ! apply_strict_rules; } // If AI already has a pending district request for this required district, return false From 24a1c1b6a79ba7ecab06f1458bf13bd9aed85a01 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 19 Dec 2025 10:00:29 -0800 Subject: [PATCH 127/356] Add wonder exists check in patch_City_can_build_improvement; Use display_name prop for c3x-script.txt district entries --- injected_code.c | 129 +++++++++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 57 deletions(-) diff --git a/injected_code.c b/injected_code.c index e22b9145..f679c1ae 100644 --- a/injected_code.c +++ b/injected_code.c @@ -9080,26 +9080,16 @@ city_has_wonder_district_with_no_completed_wonder (City * city, int wonder_impro return false; FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { - int x = wai.tile_x, y = wai.tile_y; Tile * candidate = wai.tile; struct district_instance * inst = wai.district_inst; if (inst->district_type != WONDER_DISTRICT_ID) continue; struct wonder_district_info * info = get_wonder_district_info (candidate); - if ((wonder_improv_id >= 0) && ! wonder_is_buildable_on_tile (candidate, wonder_improv_id)) { - if (info != NULL && info->state == WDS_UNDER_CONSTRUCTION && info->city_id == city->Body.ID) { - info->state = WDS_UNUSED; - info->city = NULL; - info->city_id = -1; - info->wonder_index = -1; - } - continue; - } - // Get wonder district info + bool buildable = wonder_is_buildable_on_tile (candidate, wonder_improv_id); if (info == NULL) return true; if (info->state == WDS_COMPLETED) continue; - if (info->state == WDS_UNUSED) return true; // Unreserved and available - if ((info->state == WDS_UNDER_CONSTRUCTION) && (info->city_id == city->Body.ID)) { + if (info->state == WDS_UNUSED && buildable) return true; + if (info->state == WDS_UNDER_CONSTRUCTION && buildable && info->city_id == city->Body.ID) { info->city = city; info->city_id = city->Body.ID; return true; // Reserved by this city @@ -13992,8 +13982,8 @@ handle_worker_command_that_may_replace_district (Unit * unit, int unit_command_v bool remove_existing = redundant_district; if (inst != NULL && district_id >= 0 && district_id < is->district_count) { PopupForm * popup = get_popup_form (); - set_popup_str_param (0, (char *)is->district_configs[district_id].name, -1, -1); - set_popup_str_param (1, (char *)is->district_configs[district_id].name, -1, -1); + set_popup_str_param (0, (char *)is->district_configs[district_id].display_name, -1, -1); + set_popup_str_param (1, (char *)is->district_configs[district_id].display_name, -1, -1); popup->vtable->set_text_key_and_flags ( popup, __, is->mod_script_path, would_lose_buildings @@ -14289,8 +14279,8 @@ issue_district_worker_command (Unit * unit, int command) bool remove_existing = false; PopupForm * popup = get_popup_form (); - set_popup_str_param (0, (char*)is->district_configs[existing_district_id].name, -1, -1); - set_popup_str_param (1, (char*)is->district_configs[existing_district_id].name, -1, -1); + set_popup_str_param (0, (char*)is->district_configs[existing_district_id].display_name, -1, -1); + set_popup_str_param (1, (char*)is->district_configs[existing_district_id].display_name, -1, -1); popup->vtable->set_text_key_and_flags ( popup, __, is->mod_script_path, would_lose_buildings @@ -14319,7 +14309,7 @@ issue_district_worker_command (Unit * unit, int command) if (removable_flags != 0) { PopupForm * popup = get_popup_form (); - set_popup_str_param (0, (char*)is->district_configs[district_id].name, -1, -1); + set_popup_str_param (0, (char*)is->district_configs[district_id].display_name, -1, -1); popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_CONFIRM_BUILD_DISTRICT_OVER_IMPROVEMENT", -1, 0, 0, 0); int sel = patch_show_popup (popup, __, 0, 0); if (sel != 0) @@ -16176,33 +16166,53 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply if (! is->current_config.enable_districts) return base; - // Ensure a district is actually needed + // Check if district is actually needed or city already has the improvement int required_district_id; - bool needs_district = city_requires_district_for_improvement (this, i_improv, &required_district_id); - if (! needs_district) + bool district_required = itable_look_up (&is->district_building_prereqs, i_improv, &required_district_id); + if (! district_required || patch_City_has_improvement (this, __, i_improv, false)) { return base; + } Improvement * improv = &p_bic_data->Improvements[i_improv]; - bool is_wonder = improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder); + bool is_wonder = is->current_config.enable_wonder_districts && improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder); + bool wonder_requires_district = is_wonder && required_district_id == WONDER_DISTRICT_ID; + + // Make sure wonder is not already built + if (is_wonder) { + if ((improv->Characteristics & ITC_Wonder) != 0) { + if (Game_get_wonder_city_id (p_game, __, i_improv) != -1) + return false; + } else { + Leader * leader = &leaders[this->Body.CivID]; + if ((leader->Small_Wonders != NULL) && (leader->Small_Wonders[i_improv] != -1)) + return false; + } + } // Ensure prereq tech for the improvement if (improv->RequiredTechID >= 0 && ! Leader_has_tech (&leaders[this->Body.CivID], __, improv->RequiredTechID)) return false; + // Ensure prereq tech for the district + int prereq_id = is->district_infos[required_district_id].advance_prereq_id; + if ((prereq_id >= 0) && ! Leader_has_tech (&leaders[this->Body.CivID], __, prereq_id)) { + return false; + } + + if (is_wonder && ! wonder_requires_district) { + return true; + } + // Different logic for human vs AI players bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; // If disallowed by the base game, check if it was due to not being next to water, which can be // circumvented with certain districts - bool checked_terrain = false; if (! base) { - if (patch_City_has_improvement (this, __, i_improv, false)) - return false; // Allow harbor-like improvements when port districts replace coastal adjacency if (improv->ImprovementFlags & ITF_Allows_Water_Trade && - is->current_config.enable_port_districts && - is->current_config.naval_units_use_port_districts_not_cities) { + is->current_config.enable_port_districts) { int tx, ty; // Different checks for human vs AI. For human, look for any water. For AI, look for a tile suitable for a port district. @@ -16222,8 +16232,11 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply } // Allow improvements that must be near water if water in work radius. - // ITF_Must_Be_Near_Water is Nuclear Power Plant, etc. Characteristics & 1 is Coastal Fortress, coastal Wonders, etc. - } else if (improv->ImprovementFlags & ITF_Must_Be_Near_Water || improv->Characteristics & 1) { + // ITF_*_Water is Nuclear Power Plant, Commercial Dock, etc. Characteristics & 1 is Coastal Fortress, coastal Wonders, etc. + } else if (improv->ImprovementFlags & ITF_Must_Be_Near_Water || + improv->ImprovementFlags & ITF_Increases_Trade_In_Water || + improv->ImprovementFlags & ITF_Increases_Shields_In_Water || + improv->Characteristics & 1) { bool has_water_in_radius = false; FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { if (wai.tile->vtable->m35_Check_Is_Water (wai.tile)) { @@ -16249,35 +16262,43 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply } */ else { return base; } - checked_terrain = true; - } - - // Ensure prereq tech for the district - int prereq_id = is->district_infos[required_district_id].advance_prereq_id; - if ((prereq_id >= 0) && ! Leader_has_tech (&leaders[this->Body.CivID], __, prereq_id)) { - return false; } // Check that we have the necessary terrain - if (! checked_terrain) { - bool has_suitable_tile = false; - FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { - Tile * tile = wai.tile; - if (! tile_suitable_for_district (tile, required_district_id, this, NULL)) continue; - if (is_wonder && ! wonder_is_buildable_on_tile (tile, i_improv)) continue; + bool has_terrain_for_district = false; + bool has_terrain_for_wonder = false; + FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { + Tile * tile = wai.tile; + if (! tile_suitable_for_district (tile, required_district_id, this, NULL)) + continue; + has_terrain_for_district = true; - has_suitable_tile = true; + if (! is_wonder) break; - } - if (! has_suitable_tile) { - return false; - } + if (is_wonder && wonder_is_buildable_on_tile (tile, i_improv)) { + has_terrain_for_wonder = true; + break; + } + } + + if (! has_terrain_for_district || (is_wonder && ! has_terrain_for_wonder)) { + return false; } - // If human has everything needed except a built district, allow relaxed checks so UI can gray entry out + // If human has everything needed except a built district (which is buildable), allow relaxed checks so UI can gray entry out if (is_human) { - return ! apply_strict_rules; + if (is_wonder) { + if (city_has_wonder_district_with_no_completed_wonder (this, i_improv)) + return true; + else + return ! apply_strict_rules; + } else { + if (city_has_required_district (this, required_district_id)) + return true; + else + return ! apply_strict_rules; + } } // If AI already has a pending district request for this required district, return false @@ -17881,8 +17902,6 @@ grant_existing_district_buildings_to_city (City * city) is->sharing_buildings_by_districts_in_progress = true; FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { - int tile_x = wai.tile_x; - int tile_y = wai.tile_y; Tile * tile = wai.tile; struct district_instance * inst = wai.district_inst; @@ -17892,16 +17911,12 @@ grant_existing_district_buildings_to_city (City * city) if (info->dependent_building_count <= 0) continue; - int tx, ty; - if (! district_instance_get_coords (inst, tile, &tx, &ty)) - continue; - FOR_CITIES_OF (coi, civ_id) { City * other = coi.city; if ((other == NULL) || (other == city)) continue; - if (! city_radius_contains_tile (other, tx, ty)) + if (! city_radius_contains_tile (other, wai.tile_x, wai.tile_y)) continue; if (tile->vtable->m38_Get_Territory_OwnerID (tile) != other->Body.CivID) @@ -26179,7 +26194,7 @@ patch_Unit_select (Unit * this) int district_id = inst->district_type; PopupForm * popup = get_popup_form (); int remaining_turns = get_worker_remaining_turns_to_complete (this, __, 0); - set_popup_str_param (0, (char*)is->district_configs[district_id].name, -1, -1); + set_popup_str_param (0, (char*)is->district_configs[district_id].display_name, -1, -1); set_popup_int_param (1, remaining_turns); popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_CONFIRM_CANCEL_BUILD_DISTRICT", -1, 0, 0, 0); From 27c0a995a8b050112b40efc8917ab714eaff8e2f Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 19 Dec 2025 10:58:27 -0800 Subject: [PATCH 128/356] Update great lighthouse art --- Art/Districts/1200/Wonders_2.PCX | Bin 36826 -> 37053 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index b29c1087b10f54a4f4a1cf59c870947db184ee71..deec8958d3f217bfa342c87fe3fc7200073e53d8 100644 GIT binary patch delta 2116 zcmW+%T}&L;6)us+F&&cDYh`u~?Ao(vvvX$)gpPLyipiv@XJ*}YT;r-)hw@`=i~(a~ z6Pv11b1OMge;%U7T&b!=HBla%H>ZtICEBWr?pUFZe z<<;kIaKA{iifW@*VCNStX303;6;=^T%4p{p8E^UV&g#gs#XFy^`4bFkDuLEda_ET81%SfDEclcpei&zt0uL$f%BOf*RrTVIb%aG%P!Nylk6 z?Ff{sg>!ejX-?N2dXr`u645kiYHQsWMvQB8T)HaMN|eWL!@1jDhPw{Kg5Q!e7`Tx8*NSQBxQ*)gdlhu&l+!eL&lHZ-^Aa9@C8@R*2 z@czvi4hPLz?d39m&~MpL+dcB)$yin*8xZv9JYl0{_{pC=!m>?D&N z`%3TeP46Lhp)I;uScPa3*+D%jJS3eR-SHBo05cE2w6n%nhs~Ho^X*kXEUqf6jxg@O z&mVhv&ay7UE;fX?C`7IJaGsQ$>LzA_=)lXjQ zmII&g8SgQ7FqG&jkU$qKTSoR{vfOokuqP~i%BQ^zz&M>v;8YT+01(nQ$d&Fs@p*$k-Xum(A89!g@_)w1y=`7-uYd%yWoE0Ey-gnVTq;^4-|SUN zKjR<6i4>j1%w<{TIpm%*r{&enq(Gq#w7pwUMkXqtyie??jP~c+XD%-+zR8R^x znQ93tN`~r~iIrdpi(P(1e%aSY{(8Rp+mB!b3%o9cHS9>DIaCM3+Hz@*j%$wV*f9oE zu~AZCnwbbIdJ-*>ADur--nyV_OEBFWTm^!spAtYa%pn)2_S2l^YKjcQLTVHyn$S#S zvnWy*NZEx`#OOyg3&5QRT!Xs_U|$Y9@syNyM1_vKPAUp*Mw6kWiX;$?Z0StRk=%ur zqIvS4{;EVCXnCL0@sz94O#mq8CIF}$Gvc~JC)}{Yke!r_Fp|D5A>e~0>Q-o;{INd; z4_tfWTY3(7KgW$uI-y--n=o}u(=JTMF_2!Ka13ZzmF=jlB15&9Zs~E!is*AB^~O)& zh1V~}>I&kT<%ipv!oA>>n{`mfHeGk=dO_-jWQk#4TD7CHU>PhGw-hTaUT%(v^>@hg z7r$ThfP8T=RGSA0&v2TD-0!!Q_9m`frQ=|7Yy&$5$@@^0**;`2JD4NtxeYR;WL@bkVdi!M%+<^eSV*|H;aOagl0W08LC?}@a} z-&h%GYX2?F^AH0Wk4}-nfk;J$OG0uOGH&g!j3ufnTB~BZ+HknOq22~%f_OKk$jCrN z8N8T*FUUH*@mAfHl8W7xaa*=SZ16I^P3{kbUjWRn1D$|KS6ID1c*r*BLYDA>vO;8o WW(;UOOLNW$7zoW}hRc5TQQ-e*3L3ls delta 1864 zcmX|CZEO@(6wR6p%7P}KJj+M7q(s@aK%z^*jlpL$)kHFhXv_=v=GM;;f*CF?`jD zTRhD^&y3Q+tu2X#_1ZLX4%uvrO??zM8s?45xnWgTAglG|+7VM*r%j4;V#s<-uxThD z=u~*tXvZ27?A4v4L#-{bGt}Dp;G&4eiSRt$P@}?S4mkTWiEXW`5=io_HuDo3n;ciX zXASXm)f)V^x;_ug@$dpL@z>Z-ad447-_}Nh+o~6y#gh)lqI8uw2f9+Er?+pXd$%X< zKX3jC$BpDXen&?Y4oWn(y)9K;&t}f*>bZ%B)@bL*u#j9>sLg%PKh?=*-Z}FpKoHMO zb6(=D%z3v&13NlM?xmX^$PSVmOF z(XO%tPu>ahIeUqIZSSUU+H010#t(fs!L^5x3N3O4QIfNl>HVFZ)Uq?Nc*;zM0c6Of zoJ@iJOaR_o;OsB7wxf%}or%OHGagPyNzk_A;%!<+rDnL|aJE9D9lPk`j+*Mr=0bSI zC|4ICHiFn!cVtD>RqF5TqYa%kRlk_C;WcBsN*AK2KyW1!IPvSWysMWkch)Ycn2~VC zI7nlo7Wx8f$q2bY*{&XXudBBDsyP|nG%4r5N)`6)S%m@&`4;Wj)kEudC6-(_x$sw$ zc2X$g!D{U50M<>$f1?H6{juvbwyS3O4f8#M6q?C7>~TRy{Xqf=9SU5zO}Xv?db@k& ziW`Ozs3qosgh~8~B2ijIk#Y9px9P3j{UmpTX{j8v;bCV24UgGa;DQAP z&+O@oU7Eha|OoWiVnWQJTkU`O)0*2fWF@I@S|7J zTVS*$GEyr$X@h#+5sru!F`}<69bo^H+DUT3R`WWp(1pFdF`r)F+pu;T@?A2nLV9dp z#&(CI&K>epY!c&1ERX-CFYnsaVi7hJ3=G{&M} z4k8no!?&cHPPvix6|3%$PXoPssH%5W>JsMf&Ei2IF0>0r1uB|g!~xi8P`9`Y?~6DZ zlnrHia$h@ny{l7Ga9GKtR8qS#Vip-AZ({^-QqmcYu&|32IR=URjB1!9v#&W;q)+$N zuk~Q#qT!svz6e*R6*eM3r#{d&&>E^m-Y;V(JuYjNR1?H~DmF&VeQO#guqU`9IrePn zIzuWVUVwzNXcUY@1Mxi1WRxwi0yFH7)2sVm0)FxSRgERA;u!Ad5CEJTQjiWG8QoVc z-3W_4_p!z>tFj#>VWA?eJMbLvqx%~kiPjzXT!e=OefAtV>sC+UFlYg5&PauEV@)S< zRf=@}z|&~`jRT2?CvZ@T2GdI0m2briT-!~pWA8|l zbfmoy_11HK0(YxuHsMU*a3MiDX*pWrJh>p6k6OLf-&i+cQdLdMK+wfR<_QnyuxK<2 z!$s+&NJfk&L3NyV46Lb{FzF*mZt3+N&MKP^p>NuDp)dYlJy{|>P(Ob>q7BZLXeiNw f!G~fYZ5~_^`-?sptc~5KQ-e!mw~mE_-&Oq!IT=Yh From 4a715862ac47abddbfa87a984bdb3b25091f8715 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 19 Dec 2025 18:23:29 -0800 Subject: [PATCH 129/356] Update great lighthouse art --- Art/Districts/1200/Wonders_2.PCX | Bin 37053 -> 37045 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index deec8958d3f217bfa342c87fe3fc7200073e53d8..a7d1ef95fee87f5e707103894badaa7b54befdf3 100644 GIT binary patch delta 23 fcmdnHkZJ2erVZ_ZjCVHo1e!52-rjsX=$kD7f5{5K delta 31 mcmdnGkZJEirVZ_ZtY`ioym@|eN1z!a>scW4?B@MJ-)sR2gb{22 From 834bc9ed244f50acd78677df085d8263e05698fc Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 23 Dec 2025 13:27:45 -0600 Subject: [PATCH 130/356] Add central rail hub to config; Update art; Add initial canal art --- Art/Districts/.gitignore | 24 -------------------- Art/Districts/1200/.gitignore | 1 - Art/Districts/1200/Canal.PCX | Bin 0 -> 29046 bytes Art/Districts/1200/CentralRailHub_ASIAN.PCX | Bin 13693 -> 13571 bytes default.districts_config.txt | 21 +++++++++++++++++ 5 files changed, 21 insertions(+), 25 deletions(-) delete mode 100644 Art/Districts/.gitignore delete mode 100644 Art/Districts/1200/.gitignore create mode 100644 Art/Districts/1200/Canal.PCX diff --git a/Art/Districts/.gitignore b/Art/Districts/.gitignore deleted file mode 100644 index 9bbba50d..00000000 --- a/Art/Districts/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -0100/ -0200/ -0300/ -0400/ -0500/ -0600/ -0700/ -0800/ -0900/ -1000/ -1100/ - -1300/ -1400/ -1500/ -1600/ -1700/ -1800/ -1900/ -2000/ -2100/ -2200/ -2300/ -2400/ \ No newline at end of file diff --git a/Art/Districts/1200/.gitignore b/Art/Districts/1200/.gitignore deleted file mode 100644 index 34050988..00000000 --- a/Art/Districts/1200/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*_lights.PCX \ No newline at end of file diff --git a/Art/Districts/1200/Canal.PCX b/Art/Districts/1200/Canal.PCX new file mode 100644 index 0000000000000000000000000000000000000000..17cf7963e8c79fd71534bb5cf0dbfa54948e18e1 GIT binary patch literal 29046 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 E2mAYy{{R30 literal 0 HcmV?d00001 diff --git a/Art/Districts/1200/CentralRailHub_ASIAN.PCX b/Art/Districts/1200/CentralRailHub_ASIAN.PCX index fa121ee828d32cbf35c04bbd6af4644e17130c78..0b00751114084a550dd3658151580bf602b5e741 100644 GIT binary patch delta 584 zcmY*VOK1~O6y?oKr&yUZW1R#O+eHO6&1c3UE-FG0LAvt?R)g)hXmQ#J(jasSU6g8T zG(nWRaH&FF3W4Y*pzvmDCAcW)LY$_V5CyeDOI?ZB_k81SHs`#1&*9v6@9yO7$x5e$ zTCmd_fj%jWOpyB`Ff8@(k8KLNloVgtl#*ZYNJ@x?|J;IAN#&UMTN>m|#+gu7obr`6 z%!JM`rTQjRL8u9Z@OciquoAw?e>39^;Dj#v-s3Sk9J9PgU-4;g=8{k2nGueK z;mkRo_N&Ly26P`%1edUKTvgeAa)%-`2=l&F6R{0S*zcSGdZg*gRLyd$6wYe>C)U}S z-jP26{6gB~Ej4eHmLY%7u8qvYZ1H|=G(YnMzt@G_>@ydh9#n*{eiYW?Y`2ycjXM;# z@a~c>3fMKKtitGg*(e$lCiYyYbSvsNW;aViXt{%5xP$)|YD?Bw*=2)2k<9@9B%A+N rI(GP|L$8&JIw!z_Z}dnzfZJq?Wm-Brpw_GKJEQ?|@p*M_5b^&2ti4dP delta 712 zcmY+BUr1AN6vw%Dck^F&y6R>WB}xfv)6I}5BarH$sE6#2X=B$5xf|0B+bF$aP^~!y zeF)C0;6o8T6zKMLh2-3Y7D1L!ZPV`N=JLTPq^R)E?{UvxXD65qoK#pqNkO#T(!&TBtFYthm9<6J-O; z7KM3F^p1IplcL6A%r46wgL3C9PUJ2LvYIHW0pdsQ?RI0jv`Hxnz_4pb zQUpweFas^N>-6~rSmL;yapJ?bC*vi9`-DMEV@C17ob58*m4e$GSN@uG`XyJOxyL7q znDN0GyT1%ok=GMml>HF3U!k8Q;r?OHG)GXy8K|l|T{EkNI+9wJXj-~}AIS$2#yn>H zFi_WS#5pdXZ(}eM_Cuupl0lx1LZZHr?q3AfQK{Eu5o#R{rf*zW z0E>|;S|k`B=oO*IVKs`Fy_o`M4OgrZTnXZ)52B9dVkL-48P!(!?Wn05O6gCTCF=#u zkt`YFWz37K*oqK1}!FI`sv{%eez91ZSeU6FJ{V6ZO<+2>e2wXa+*^_I7EKE#Ob`gM|4oCMAKQ%M!YlBWeNH R$Qr?{ClpD<+zrnR{{fbDllA}r diff --git a/default.districts_config.txt b/default.districts_config.txt index 78ba7265..4c7fc3cc 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -216,4 +216,25 @@ science_bonus = 0 food_bonus = 2 gold_bonus = 2 shield_bonus = 0 +happiness_bonus = 0 + +#District +name = Central Rail Hub +tooltip = Build Central Rail Hub +img_paths = CentralRailHub_AMER.pcx, CentralRailHub_EURO.pcx, CentralRailHub_ROMAN.pcx, CentralRailHub_MIDEAST.pcx, CentralRailHub_ASIAN.pcx +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 5 +vary_img_by_era = 1 +vary_img_by_culture = 1 +advance_prereq = Steam Power +resource_prereqs = Coal, Iron +dependent_improvs = Mass Transit System +buildable_on = desert,plains,grassland,tundra,floodplain,hills +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 1 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 2 +shield_bonus = 0 happiness_bonus = 0 \ No newline at end of file From 9afbf35dec0ebca568e34ab6e03394277cd9254a Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 2 Jan 2026 12:50:19 -0800 Subject: [PATCH 131/356] Add flag & implementation for show_ai_city_location_desirability_if_settler --- C3X.h | 1 + civ_prog_objects.csv | 2 +- default.c3x_config.ini | 2 ++ default.districts_config.txt | 20 +++++++++++++ injected_code.c | 56 ++++++++++++++++++++++++++++++++++-- 5 files changed, 78 insertions(+), 3 deletions(-) diff --git a/C3X.h b/C3X.h index 20a6d8f3..76fe7d86 100644 --- a/C3X.h +++ b/C3X.h @@ -193,6 +193,7 @@ struct c3x_config { bool warn_about_unrecognized_names; bool enable_ai_production_ranking; bool enable_ai_city_location_desirability_display; + bool show_ai_city_location_desirability_if_settler; bool zero_corruption_when_off; bool disallow_land_units_from_affecting_water_tiles; bool dont_end_units_turn_after_airdrop; diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 8f18f26a..2fb8393d 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -841,7 +841,7 @@ ignore, 0x558F70, 0x0, 0x0, "Leader_is_enemy_unit", "char (__fastcall *) ignore, 0x4BFD40, 0x0, 0x0, "City_get_turns_to_build", "int (__fastcall *) (City * this, int edx, int order_type, int order_id, char param_3)" ignore, 0x4E3D90, 0x0, 0x0, "Main_Screen_Form_is_unit_hidden_from_player", "bool (__fastcall *) (Main_Screen_Form * this, int edx, Unit * unit)" ignore, 0x4E8850, 0x4F14E0, 0x4E8910, "Main_Screen_Form_open_right_click_unit_menu", "void (__fastcall *) (Main_Screen_Form * this, int edx, int tile_x, int tile_y, int mouse_x, int mouse_y)" -ignore, 0x4DBA70, 0x0, 0x0, "Main_Screen_Form_set_selected_unit", "void (__fastcall *) (Main_Screen_Form * this, int edx, Unit * unit, bool param_2)" +inlead, 0x4DBA70, 0x0, 0x0, "Main_Screen_Form_set_selected_unit", "void (__fastcall *) (Main_Screen_Form * this, int edx, Unit * unit, bool param_2)" ignore, 0x5CCBB0, 0x0, 0x0, "Unit_can_pass_between", "PassBetweenValidity (__fastcall *) (Unit * this, int edx, int from_x, int from_y, int to_x, int to_y, byte param_5)" ignore, 0x452510, 0x454600, 0x452590, "ai_move_defensive_unit", "void (__fastcall *) (Unit * this)" ignore, 0x5E78E0, 0x5F7130, 0x0, "General_load", "void (__fastcall *) (General * this, int edx, byte ** buffer)" diff --git a/default.c3x_config.ini b/default.c3x_config.ini index bd89e5b4..d5350e54 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -417,7 +417,9 @@ enable_ai_production_ranking = true ; Shades/highlights each tile to show how desirable the AI considers it as a city location. To activate, press L while in game. The scale goes from ; white (least desirable) to red (most desirable) with yellow in between. Also the exact number the AI gives any tile can be seen on its info box. +; If show_ai_city_location_desirability_if_settler is true, the desirability overlay will automatically appear when a settler is selected. enable_ai_city_location_desirability_display = true +show_ai_city_location_desirability_if_settler = false ; Setting a government's corruption level to "OFF" will remove all corruption, as expected, instead of maximizing it zero_corruption_when_off = true diff --git a/default.districts_config.txt b/default.districts_config.txt index 78ba7265..d2a1d0be 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -125,6 +125,26 @@ gold_bonus = 0 shield_bonus = 4 happiness_bonus = 0 +#District +name = Central Rail Hub +tooltip = Build Central Rail Hub +img_paths = CentralRailHub_AMER.pcx, CentralRailHub_EURO.pcx, CentralRailHub_ROMAN.pcx, CentralRailHub_MIDEAST.pcx, CentralRailHub_ASIAN.pcx +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 7 +vary_img_by_era = 1 +vary_img_by_culture = 1 +advance_prereq = Steam Power +dependent_improvs = Mass Transit System +buildable_on = desert,plains,grassland,tundra,floodplain,hills +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 4 +happiness_bonus = 0 + #District name = Ski Resort tooltip = Build Ski Resort diff --git a/injected_code.c b/injected_code.c index f679c1ae..6e6fbfb7 100644 --- a/injected_code.c +++ b/injected_code.c @@ -12169,7 +12169,9 @@ patch_init_floating_point () {"show_detailed_tile_info" , true , offsetof (struct c3x_config, show_detailed_tile_info)}, {"warn_about_unrecognized_names" , true , offsetof (struct c3x_config, warn_about_unrecognized_names)}, {"enable_ai_production_ranking" , true , offsetof (struct c3x_config, enable_ai_production_ranking)}, - {"enable_ai_city_location_desirability_display" , true , offsetof (struct c3x_config, enable_ai_city_location_desirability_display)}, + {"enable_ai_city_location_desirability_display" , true, offsetof (struct c3x_config, enable_ai_city_location_desirability_display)}, + {"show_ai_city_location_desirability_if_settler" , false, offsetof (struct c3x_config, show_ai_city_location_desirability_if_settler)}, + {"auto_zoom_city_screen_for_large_work_areas" , true, offsetof (struct c3x_config, auto_zoom_city_screen_for_large_work_areas)}, {"zero_corruption_when_off" , true , offsetof (struct c3x_config, zero_corruption_when_off)}, {"disallow_land_units_from_affecting_water_tiles" , true , offsetof (struct c3x_config, disallow_land_units_from_affecting_water_tiles)}, {"dont_end_units_turn_after_airdrop" , false, offsetof (struct c3x_config, dont_end_units_turn_after_airdrop)}, @@ -13923,6 +13925,11 @@ intercept_end_of_turn () clear_highlighted_worker_tiles_and_redraw (); } + if (is->current_config.show_ai_city_location_desirability_if_settler && is->city_loc_display_perspective >= 0) { + is->city_loc_display_perspective = -1; + p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); // Trigger map redraw + } + // Clear things that don't apply across turns is->have_job_and_loc_to_skip = 0; } @@ -14441,6 +14448,11 @@ patch_Main_GUI_handle_button_press (Main_GUI * this, int edx, int button_id) clear_highlighted_worker_tiles_and_redraw (); } + if (is->current_config.show_ai_city_location_desirability_if_settler && is->city_loc_display_perspective >= 0) { + is->city_loc_display_perspective = -1; + p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); // Trigger map redraw + } + // If a district, run district build logic if (is->current_config.enable_districts && is_district_command (command)) { clear_something_1 (); @@ -19711,6 +19723,11 @@ patch_perform_interturn_in_main_loop () p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); } + if (is->current_config.show_ai_city_location_desirability_if_settler && is->city_loc_display_perspective >= 0) { + is->city_loc_display_perspective = -1; + p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); // Trigger map redraw + } + if (is->current_config.measure_turn_times) { long long ts_after; QueryPerformanceCounter ((LARGE_INTEGER *)&ts_after); @@ -25693,7 +25710,7 @@ wonder_allows_river (struct wonder_district_config const * cfg) void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - *p_debug_mode_bits |= 0xC; + //*p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -26222,6 +26239,17 @@ patch_Unit_select (Unit * this) } } + if (is->current_config.show_ai_city_location_desirability_if_settler) { + int unit_type_id = this->Body.UnitTypeID; + int worker_actions = p_bic_data->UnitTypes[unit_type_id].Worker_Actions; + int new_perspective = (worker_actions >= 1 && (worker_actions & (UCV_Build_City)) && !is_worker(this)) ? p_main_screen_form->Player_CivID : -1; + + if (new_perspective != is->city_loc_display_perspective) { + is->city_loc_display_perspective = new_perspective; + p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); // Trigger map redraw + } + } + // Sometimes clearing of highlighted tiles doesn't trigger when CTRL lifted, so double-check here if (is->current_config.enable_city_work_radii_highlights && is->highlight_city_radii) { is->highlight_city_radii = false; @@ -26231,6 +26259,30 @@ patch_Unit_select (Unit * this) Unit_select (this); } +void __fastcall +patch_Main_Screen_Form_set_selected_unit (Main_Screen_Form * this, int edx, Unit * unit, bool param_2) +{ + bool redraw = false; + if (is->current_config.show_ai_city_location_desirability_if_settler) { + int new_perspective = -1; + if (unit != NULL) { + int unit_type_id = unit->Body.UnitTypeID; + int worker_actions = p_bic_data->UnitTypes[unit_type_id].Worker_Actions; + new_perspective = (worker_actions >= 1 && (worker_actions & (UCV_Build_City)) && !is_worker(unit)) ? p_main_screen_form->Player_CivID : -1; + } + + if (new_perspective != is->city_loc_display_perspective) { + is->city_loc_display_perspective = new_perspective; + redraw = true; + } + } + + Main_Screen_Form_set_selected_unit (this, __, unit, param_2); + + if (redraw && ! this->is_now_loading_game) + p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); +} + void __fastcall patch_City_Form_draw_food_income_icons (City_Form * this) { From 4fd5ebcecb22c0a2e055b8df68c6e0570fb529c1 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 2 Jan 2026 14:36:44 -0800 Subject: [PATCH 132/356] Finish park art --- Art/Districts/1200/Park.PCX | Bin 14901 -> 19897 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Park.PCX b/Art/Districts/1200/Park.PCX index 05d9c3dd894f1682ff0e88bd7a44a790536dc0c5..d1e535a6e2f3fbea67cd1be7e009b38ba39fe3f2 100644 GIT binary patch literal 19897 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 NRKxi*6%msB{a-dw7@q(D literal 14901 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 Date: Fri, 2 Jan 2026 15:05:32 -0800 Subject: [PATCH 133/356] Complete Park config & code to remove forest upon completion --- Art/Districts/1200/Park.PCX | Bin 19897 -> 24080 bytes default.districts_config.txt | 41 +++++++++++++++++------------------ injected_code.c | 8 +++++++ 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/Art/Districts/1200/Park.PCX b/Art/Districts/1200/Park.PCX index d1e535a6e2f3fbea67cd1be7e009b38ba39fe3f2..78e2c1f3fd3df07ff32a85bc1653e296d37906b6 100644 GIT binary patch delta 58 zcmdlvn{mP(#t9k|6C_y}8JHL+2I@~t;F)Y|a&Ge^_QhP2r!Wglb{FcHJX_OrGn=qF O`(}1S78anCx*Gs9&Jrd7 delta 1132 zcmZ|P&rcIk5C`xsW+m}pW0qwvYK4;*5-Mjt=<>1i+%{zJ`{spEd z6EA>@fV5JCYAY>JVQp)>($dz_WXA6sOKk$%!{<$B%lol!hUvG)o0rD)<#G1;1dPCt z5eN(gjQF`L@k^(b;ha}bCPzbET4JWQ_(yEPE_Eo0d_%<@%SW_h5teDUW~;SGMhhB3u;_1d(qXArM(P4vX7n4xuy z87_+zu_UhJ#LPK$=b>$`A}XlW2lj9ZQIYfNUf2q)Aa*$`>Rq_3S9Ey=XJ@Xcr$Kvc z5s}AfQP1bGp3CD!ya(n6C)G^EGT$R^tF5SWin*(v-F2?%0-Hk?FF>5_`ENYwiT+(z zP2JFQ=(+6S|0~$Yd1~UOex~=OM^{yYj@ds=|BU*XKD8b*YT}j?@_Taw@7I)DP|@4Y zMZbIMU*_lq>~!F|k8ZnH9_s3d6+XFu{0gpDpvrh`g?lxO6$VS#ujYF_qGoL?mdD>5 x?;^jhYf5Jx@6t9&AB(@m8f+hRy7c>jSce_z|7S?tp9~s_sPXDd;>Gy$$X_V1x;+2@ diff --git a/default.districts_config.txt b/default.districts_config.txt index c9b948d4..2feb8882 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -145,6 +145,26 @@ gold_bonus = 0 shield_bonus = 4 happiness_bonus = 0 +#District +name = Park +tooltip = Build Park +img_paths = Park.pcx +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 7 +vary_img_by_era = 1 +vary_img_by_culture = 0 +advance_prereq = Engineering +dependent_improvs = +buildable_on = forest +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 2 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 2 + #District name = Ski Resort tooltip = Build Ski Resort @@ -236,25 +256,4 @@ science_bonus = 0 food_bonus = 2 gold_bonus = 2 shield_bonus = 0 -happiness_bonus = 0 - -#District -name = Central Rail Hub -tooltip = Build Central Rail Hub -img_paths = CentralRailHub_AMER.pcx, CentralRailHub_EURO.pcx, CentralRailHub_ROMAN.pcx, CentralRailHub_MIDEAST.pcx, CentralRailHub_ASIAN.pcx -btn_tile_sheet_row = 1 -btn_tile_sheet_column = 5 -vary_img_by_era = 1 -vary_img_by_culture = 1 -advance_prereq = Steam Power -resource_prereqs = Coal, Iron -dependent_improvs = Mass Transit System -buildable_on = desert,plains,grassland,tundra,floodplain,hills -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 1 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 2 -shield_bonus = 0 happiness_bonus = 0 \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index 6e6fbfb7..6fe7719b 100644 --- a/injected_code.c +++ b/injected_code.c @@ -4235,6 +4235,14 @@ district_is_complete(Tile * tile, int district_id) on_distribution_hub_completed (tile, tile_x, tile_y); } + // Remove forest/swamp if applicable + enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); + if ((base_type == SQ_Forest) || (base_type == SQ_Swamp)) { + int new_terrain_type = tile->vtable->m71_Check_Worker_Job (tile); + if (new_terrain_type >= 0) + Map_change_tile_terrain (&p_bic_data->Map, __, (enum SquareTypes)new_terrain_type, tile_x, tile_y); + } + char ss[200]; snprintf (ss, sizeof ss, "District %d completed at tile (%d,%d)\n", district_id, tile_x, tile_y); (*p_OutputDebugStringA) (ss); From 32d5090b3e4ea9c453f3bd2df99439faa88cb24e Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 2 Jan 2026 16:28:19 -0800 Subject: [PATCH 134/356] Add Offshore Extraction Zone art and config --- Art/Districts/1200/OffshoreExtractionZone.PCX | Bin 0 -> 4373 bytes default.districts_config.txt | 20 ++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 Art/Districts/1200/OffshoreExtractionZone.PCX diff --git a/Art/Districts/1200/OffshoreExtractionZone.PCX b/Art/Districts/1200/OffshoreExtractionZone.PCX new file mode 100644 index 0000000000000000000000000000000000000000..ebc4bae916056be771422968518c424226aa0ed8 GIT binary patch literal 4373 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 Date: Fri, 2 Jan 2026 21:58:45 -0800 Subject: [PATCH 135/356] Add Savanna art & config --- Art/Districts/1200/NaturalWonders.pcx | Bin 87279 -> 91898 bytes default.districts_natural_wonders_config.txt | 14 ++++++++++++++ 2 files changed, 14 insertions(+) diff --git a/Art/Districts/1200/NaturalWonders.pcx b/Art/Districts/1200/NaturalWonders.pcx index d1fe55207dd491448038053b5665c94b9fa8a962..4eb80614d8a2c1c2defcb97bc3d9ca7c0db40cc9 100644 GIT binary patch delta 5568 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 diff --git a/default.districts_natural_wonders_config.txt b/default.districts_natural_wonders_config.txt index 6c0f7dd2..2f8227b3 100644 --- a/default.districts_natural_wonders_config.txt +++ b/default.districts_natural_wonders_config.txt @@ -241,4 +241,18 @@ science_bonus = 1 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happiness_bonus = 1 + +#Wonder +name = Savanna +terrain_type = plains +adjacent_to = plains +img_path = NaturalWonders.pcx +img_row = 4 +img_column = 1 +culture_bonus = 2 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 happiness_bonus = 1 \ No newline at end of file From bbbc759811149e6be17ab2edb7cc4d6fa872a8ae Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 3 Jan 2026 09:00:23 -0800 Subject: [PATCH 136/356] Add Geirangerfjord art & config; Remove Greenland cliffs; Remove sea coloring around great barrier reef --- Art/Districts/1200/NaturalWonders.pcx | Bin 91898 -> 94567 bytes default.districts_natural_wonders_config.txt | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Art/Districts/1200/NaturalWonders.pcx b/Art/Districts/1200/NaturalWonders.pcx index 4eb80614d8a2c1c2defcb97bc3d9ca7c0db40cc9..7f27b79106cd0201f4a584f36e5e9c8e392be27f 100644 GIT binary patch delta 10759 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 delta 7375 zcmY*d4R9Q1dG2S`auOvDN_>$-l4AFAn+xe}3c27c7aDqN1#(&6j24GGOqk&4HmIbj z(gLE!J(mCm+wvTR2moUIfw&+aETo* z(Qco2<(8@C`}y|U@BMk+=l$9Hm&SFJ6bvRiV>$12L-M%guWzbq#BS$+YphKtiDDxZCHGwWXP zOXYFC=q&T|e7-zedcN|_zuUsDUG&SP!f-~IQ<0a&Y6}%BIIRGwF!$;^eFk`{2WDc^`Mh zdA`WcPLyEXtT@XL)=EbzcaKOccEmqSTJQ>PhCop&JSAMQI5mm)Wxm1>LRIj8uJXCh zNo?z3pTs(A*YNJH9iuC?gg3tsVRs$!XG;7a?0y5NlKl;cJcuKtp5EM2`A{kIv>^W` zrzc4>ZYf}Iv2yo6eu&+37(u}*5JJ|tya_QbUMu4TSR43_8=#*Mn1Dc05W@vA&)xA- zrjQW@w{)!Xy?^>^b~`9RdTnyfB}F8JT)-&@5kjPDC6O5}IHdyagZLAbRdXBLIuDOw zMrK5~_HI*53bQ~S1J<)HuESi2IzPo1gCLg5N1(q$jO7>g06V3lm6!87*=5fMoD`+8yT80#FSa~}#yW2e5{vlW+z5nWeUVsTOs?dD4pGo>ny9ON~=82Ii0>{<8+ z@2dL{P$c#BU-=uBSq^v%6(WORxYfxiXGxq#Y~2aBTo@K3B(6G6u#Y2pFsTgWmyt@# z;++7ig9y5CCy^xqJ#C`PV&!n*QTF~77zr+AM#eMy;NY*ECH^aZE=a?Jh(8d>l%5vY z8z0FO1iT#|`{WaS*Og4Z0zZ~11Q3dNSKZR_$`eogJ-h8R+3SK?!D&}oXQLkuD^!C7&EpW{pBa0wV;8}L&Q z3zK|pXBnoPgE8=!FHP>wfW_n4;t*dHpzBm-2C`q>_gQx9DL=>^03;xM+~#jU@G>A; zu0Bf&p?EFB5dVk2_MQ;DAIaQjIz=b~qz9jd+QrH@zBb72e-S*TkN}Zk6q72ii52d` z!ijSAN6*5#31k};+Q246omk{7O%Z6Yut2&>!P2;Yw?CgzzSM!mA| z^#|EU8zdj496FHgNDct8f~W`Vc#439X-}gB&$v^Q2-~v2=>QzG0^DBf?tKA31Q~(Y1nE#7hGLTn6U1Cfk-~U&tttd?tRQ(|F&IO&cvzr-%|P6Vr~284 zPWt5;if-`2?o5V~kP-}6KpC?NB*mmdnT#*QdBTITsXSkaKb>UCk`M65FEQ^R4wE7D z2R<%a)S-#E&N6?lND@+y_45JVH{KEumk1`)2lmWB!rA zN2=KK&LX^`>;zk?qBH_G0XZ!%ASW`zBPA-ALq>23mYnI9b4wQtvC6%v*v12E^u8qcmqFb&-swt;J#?j`P?<|6?-$mLGJU$5o`+|c0mwtoU?M5v#@d+>am1A2x}km z6ENQa|5Z^*lRs~1-tE@S3EL>x7KbXjeA`$ZrHm8U22fiF8K%(_i}G9 z$t2?(tS^be;WO5)UF^5HA|_--!r(1Km32Ef zPg-16-+9LR;+5Nf1 zw8NHm069enR14&~3H^X>B*KKEXeI(Yb)ocJ7eU-=uAvBvw;nxhy>d0%^BLc(c{}sE zA`>)$(E(O#P}b~hQMNUUPy&2z;3|Q+h8;l=;P?skKu$Nqy2x5`-pHk;o={IXDLeYm zb4oZp+Q0LZb^2ZGtAnIW4DC?j2qsRhF^GJJlkh66NS0;`0G`Kw4Qj$*Q9Fzi7RF z4f{dX_k_3INGg$#t{AGZ6G0YP-IQ!q4w<%Y09j~AsD=Z4gGe^4w3^zIRCL)=5H?p0 zcgF<$v~z}TrTXNg@>u`d!d1czsd0h={1_zwBF!Uk4N0J{K{2;`CAjE$B;5(j~1@UAN=Jhy8 z9Kdt8go2QaC`qkbPAR&Cvd(MJGHoIjuAqW|3x=Y{)xkXE>#A<6H=)*02DhOk6-th# zl1l%I^~iPXC-=9Z#8jn-WJz!nA;>FnVWvWIGTW`F`4rSOb)m+$%TN#$KZwt(q7hef zIuxm*B_ko5v+V;@+rvsSCvpIx_P0ZEnx?8jRa~es6JIE;nn09V9%Xg_yU3-VJLw(sHg$E1b)vIl-R0|?j;?EjHFpC$ z_Nm{%S!qfS$)+NuO{ECphjYnpNuO=UR0H;lmP2>~MfB>0H9yE4ylSQ$4@=>gtzi_f zi$X5~6KoiHRp0St-a2Vr^Jnb!J-_z$-lJs)cZ9cQ4b#q|w$`mMVkv3poI$cSQLKp| z_&vl7*bCP=H5k%MWvP}m@>tY_88Z_gLP!A1R92rdiiT>9TGJT&R!@599rIdO=bx;5 z*M<%zI@Y-zmv;*5tM6ei?KBP}(l(wl|wvIBUZ6bV?|C-UfTsJmw(eq`zdXY-VrnX?nunC8PmS3-`;)_F~p9 zLd#|iWJeJQYO$b+gBo&7Nh=s?*NP+c&Y-*qH8G8L2xslhaV^v%%&?hDrlK;mnR#%Z z+5(EujDNzbLtCtCeJ4WN!8%xHhxPG4XTR9v`Y|mm6^*!Tn8+2G*NTQMA(_(IBKZVH zqRz=j%@%&dx1 zlFvP^!wA~~Tr@nuiipy4WpDIKSru|b&gn`K-D|-=S`9^57ki-L)qJ6uYHCyosRwAd z0rf52)^HS6rdCvPMobt9-qh4-qdSr{lc7|6FqKnslA0TQJODe^Eh*vdq_i=Tgpz1# zP<5J%e!co_iCxKd9`ZF)Q%q|#fp q2$v#8001-Ew09MdRW+040LNq3Q2mhFQV+v z$q^;0a5onO(>1%p(a@DCX`?hGs}WP=)uLz}_vflNMA>ERfy1OAoa6Or3`0^frO3&l zQK4wUR^$Pp%LkI-6k?Ab1%+-Ik}@Rsp%kiepWLeq=BY;5W?V=cuZ#3 z+H#-hRh*&65vrOLPTUldA|VCMQKl}fQ=eL@-gYayju~@!I5E|@8Bny1*%7VC%G&86 zJz>hyjnW5)28Z@P9MSUPdi>k%^XdcO7NMS9(8&Vv-cQ6@AN=aSKqMEdpeaGY8f^Xijsk1{cr(@F% z-KLg^ZAJ(+v${uSoh)}04_l^csG5OFVWHd;cA#9^0M4$RjPym5z);;XIvB!*eA+15 zk|Os>5m`@0&QoVQ^SpE>H!tm^BCRM_= zmcV%AXT3r)va12kxUmYc!NHyB{2Hw8s@2@ZtS4ShboF9)39EDKU@9O2_J^gP=y{h zg_NaaZ=&`_`m#zil9PKujk2r419GkriLo0g2g~xgB(afH$y;{tlq8ccOP4V76;GO z300GXiEvBX26|K@f$9N$f$UZyZemJTi<&C*#3V$Z_XFx+A|TsZ(XqFNoc8{D+^XpDJpmrcc?h zyg&XpuiII|W)&)MmMB3nrL5v@auyTK+Kh+il$U(#%XhK2Z-3r5ZSbca^qGY59DJnN z2SQqOsa8a7#s@%zGHeuI_O0_DVsAvj!I}eX@a)T*Q?L-D0q$u{Pw^HA4u;Y1681}0 z|J|&0>uVkYPD2Wu!#jpn6Vd?;js-(XKuQCZ_*HBC!|d06zxS5?;AZOj3s4Wcc!(1tb^HUE_L^=<5j8$2u=|BUtT{mk0ndk)s;tRLLNp1`8! zwf&c^XFkII0gE~Bg8vKaiI1{JusG`d-v6a#+{+%o;)M5_|BCg26nhYhliqp%RcqaS zOvhryYx=*jp1hA~>(6*E`MuUjJ;>tFv%ui>|>-)L;>mRG%>`)_)` z^^RiH5Ov@Cg?HTdvHsupi`I9xvup2Yp-TS2YoQDK9+`UtpBMk{{cZ0}&k1&F-Yh=f NUVE?Jm1ehw{|Al&tvvt$ diff --git a/default.districts_natural_wonders_config.txt b/default.districts_natural_wonders_config.txt index 2f8227b3..70251996 100644 --- a/default.districts_natural_wonders_config.txt +++ b/default.districts_natural_wonders_config.txt @@ -216,9 +216,10 @@ shield_bonus = 0 happiness_bonus = 1 #Wonder -name = Kalaallit Nunaat Cliffs +name = Geirangerfjord terrain_type = tundra adjacent_to = coast +adjacency_dir = south img_path = NaturalWonders.pcx img_row = 3 img_column = 3 From c4e9b9bc4f4901c9b0f4b66a065ddf46775faf37 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 4 Jan 2026 09:39:32 -0800 Subject: [PATCH 137/356] Add industrial bridge angle 1 art --- Art/Districts/1200/Bridge.PCX | Bin 0 -> 9129 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Art/Districts/1200/Bridge.PCX diff --git a/Art/Districts/1200/Bridge.PCX b/Art/Districts/1200/Bridge.PCX new file mode 100644 index 0000000000000000000000000000000000000000..35401fcf3cb121c56c43ce8dccdf06332f09d329 GIT binary patch literal 9129 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 literal 0 HcmV?d00001 From 05ed88d53297f1aa82d600074a967f654eba4811 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 4 Jan 2026 10:09:05 -0800 Subject: [PATCH 138/356] Tentatively finish industrial bridge art --- Art/Districts/1200/Bridge.PCX | Bin 9129 -> 14624 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Bridge.PCX b/Art/Districts/1200/Bridge.PCX index 35401fcf3cb121c56c43ce8dccdf06332f09d329..2cbd35955f111ff742fdfcbae5e74f4ef5749f88 100644 GIT binary patch literal 14624 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 Ck}IPC literal 9129 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 From c91569d5da742001b9aa5a15bac8706c28c0b9bc Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 4 Jan 2026 11:15:24 -0800 Subject: [PATCH 139/356] Add data center --- Art/Districts/1200/DataCenter.PCX | Bin 7321 -> 29254 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/DataCenter.PCX b/Art/Districts/1200/DataCenter.PCX index 01e2abc7b13073e2d1bce9eeff977669959badd7..636957e1298e1c07ede3a92584253a1197b4814c 100644 GIT binary patch literal 29254 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 ltsd9E?qUA#-+K11dzkXKJCMsd#Ylf~W zY6pT?v17hLiZbW4B8o8x*zqO<2Pmk#O;DItWXO>D+@0q#GH3j=_+k4*oBVEi?$i5n z?m7S4d;9W>wlCSxi`g<88(Vrm@5|Qah3Eg$-<>ewB4fe`i}@#k^RaM=YKA!f;Cw`U zOqL3=4{-j@`H(my3<{UY-pBbH=L6y(Jy}O~E9ZUAd&B{uUuYnE59hC(-Nb%sp^5BW zoVPhUiG4z^aFgsEoWF3k6ML!UcCxo{wsPJiT7|npC)t}huX8pL?^2K5WN+lW%Gp5d z5$*{0$bN=w=+IWG}Agf>AS zdkJSL=S5oS-3`J4ST@Z z$Jxtyjb_Z)6?Tu)%6XTwQK+Y~k#%$SaNgmpCvPy;%sM%{IB#)Y5o)PyVeOn9oNb)7 z=xmWUPlZaEjyXVd9v2Sfx--rIpoj zilebIv1cnQCl52$$1Zb@ATQ*3#Fo)%d~-e+uxUE&nKKSVZ>B|wrB3(v0j&y%yj~L!Bp4=&L4QdPZjmC=RhJ1m1hkT9J2CXGp zdlV}uwoojh*hR69vjb-o&O)5IINPy8U{%3Ngw+cx9#%=LtXPe)f@9Txat|5aNs6Dj zi#%P2Tg0QlBgeDF9>QM5D}h%JuQ-kp99cLTaRlS2$C)ycTeS86^($@o_ZiAXl)EU` zkv1SLLE3|~3TYeCLZqEYYmqi1El1jqcMDvD+CqJ!v7)&lUm)KhU!%1_Yl+q##R`fo z6pJW!M`E3R=n7+Jk+HK#ADzygJVA}^Ac{;C99-_QOd5&}e=?Kytq*F-O zkPafGQ^D=ajLtNc|``rT_O?WNaUK_J0RRn~|0y?Z>;txNg&mLY>;()uA>^ zTfTciOLyJyjzj%!DDC!{2XN6wK3%AHgS$zc7?goP4Q=I3<V0^Ul*U1YDauW-T>i%j?Q^lV3t3sPCTf%~-f4SQK zZf%0K{d?WU#pUNyFPzjJ`*yWTDcQSCRg@Q_^moeC1@Bz7A*MTNW`KR(XSC?h9 zHW&|Pt+;Sv!TdS)`egU*y7-SYZmJO1pa92Mt?X*W$B(+>%(=634f1g1o9Vl^`T95y z^%^3>rIKlHWu=D)%yK9`qYYI$X;dyNm&;c6qm3N&$FkKj^)M#E2hqH zn^al0+}CHyOxa{}zB>PiGIhJpyWaLGy4gPR$%;U`UE95~4Q@k&g}YOOjajn_3+60~ zaZXS3a&d8tkJq}pPm{|fN~PD{y^B3Orc9hTZp-GG27|X=@3u02Qucv}HAynvHfg58 z>3XX^$E4nwD)aJoof{(Enxe|cQmjsL&d!-Bk&ZW?m*yM~NZap`oZ{dgD9bm0a-!(N z=tWU7`2<~t%R3Ug?U?~~_OEQ(^`4KPG$~nA)3qi-J5jE9ZFb~@m@j55PIQ!dxEw9q zny^Er%hCjC9MzvrHJ4^;R|e%<68w}?U1q+TW?X-+QK!^+PWQCWE?gy_XXhU>!_9AE z+@>%m+3Rml9j{&zurE*7*ne_E#u5)dm$Iv;KbY-%q+DOnlI`m5q|o?o%}e~IDBao9 zF?QF|$kh?1Jk#3sYv~rkW3KmSIqMV8={Hmu4P9sTEkAfh1s;B2`KsRJ8yl|MwsoIz z|G`6tzB_*Wl-Yc)sHmj0w6?CUwXLn9s_JS}Q|Im5!J(n9?lPI&CoMh0dcRkt) Date: Sun, 4 Jan 2026 11:37:38 -0800 Subject: [PATCH 140/356] Add Data Center art & config; Remove Research Lab building from Campus and move to Data Center --- Art/Districts/1200/Campus.PCX | Bin 35176 -> 30788 bytes Art/Districts/1200/DataCenter.PCX | Bin 29254 -> 15744 bytes default.districts_config.txt | 24 ++++++++++++++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Art/Districts/1200/Campus.PCX b/Art/Districts/1200/Campus.PCX index a2d0c770a1c3dc4a8e8869fd2725d1d07e51b35d..287743253276ac20fbfccf13c067b17a0f0e2772 100644 GIT binary patch delta 260 zcmV+f0sH>wk^;o=0kH8_vyC|93X{xcl9P-jOOp<0my>WNMw3@*m6N|kQ@R8h5-Xv3v>Vg delta 2923 zcmXw44Ny~87M}N$R;9HHLVrYyi1mlnkrq^>tyQEIs|I2rmKS4yz)}G*2nKdqv{WRN z6wx!SopyHx$t$Uy+15JCI=Gsi`-s-nM3VGR3Iai-9cs6tb$95RwO0VyHlOgy@!rl@B`UHVUYN6h`wj-v-i1%+ES`qy8|86Huu0W zbK(PC?V1c6hpdR0hqO$ewF&z(UeEZf8h$a)gLR9tp`_@KaHweU)UH1`na3u+%>avH zB6P{C@OFXWpD|AjdTbK9Eqv|9vOL2j-NK>WUYLI6;#0BhB$~!+y-eSA3$s3Q)%6AqR$Riu-2jbsC3X; zYjub=XzmprOPXGJ%N#-oR^k$BcLJj!v6}x3NDp z8~gDl3~r2tiRt;Uac2jV?_99{MhE(6n{?L3+nF2C&7=l&HyPR=qI-aZ3<6V~l&>euCCwkFi!XRaQUUh9y^j-LCdQL8mWNCGJ3UKmi3BIi` zL2dS2lFKEUm`wg<4lc~wSW{#D+c?bH6Mq zWOznq!`^l#3&+UEP6r0Hu79ZAMk0yna?HkV1%`IFgID*Mj;Xu$YD=bS&?oRXNgl<( zQ5>%9#-XCddK@Ia$mo*_ClE1CIV>(2m1vRC%WIsZ6Oi3-RPRwy!BKX)y9XZKs)qr~ zeAu=%W}TuYPuCY&ng;3!o_L{o-j z(+7;a_fikljnOq9^1Kxw%} zg$rAtL39coZP@D+8PO!}!U-JhRlk9O$_!|I<-wOOs@w7QCPNbXhUXesPs-Yh>H++Bs-ZknA`5!$ z%x1!YmyLL>h@dQIt#u|UQlPdFHB5BY)`Xezl6+pWv7cu_yauK{ar?eh52R;rDk%^N z#AAuDH{dWe-!PuHNk?tyu`-K|O>!hIp@jW{{&o{ZLvIJs?=L0sN6cp~sso|Hkg6(b z=zqUos{cP}Z_&LAs!_Z0`K3i7)udQV4K@><40VSoMEvni2jkczbpw6F0W>1tDb--F zAa5`I+_?A51(5g5TkobRfp@YCC|-aUnAiytvou)jmgW}px1rZ0YJ~f;@}iy6CcAZ# z3LoL%C&tf>RM02}-EeC~6!h(hUfZLZ)3+EZsL8^Ei_Rlo2Mt6|_`D+~nTEGmX*g2R zWCw2xdXDq*zH&BFioUXs_nK*xz%P3qO=BtsyI8+fRLHL!b)8r9A&pf=Q=jViQLW4WUY28pnIJI|TBJh(F>aFNnO%ymkjzI?-nV zIV3zUZ%21yb;X*b zYLH4X$O5&b0-dO*spK)>a%cT+AjFp8=$ULQnN-k(<7sN}SDlt7XMF`TNcK{4L|G0^ z=x&7lRnZHBq44`Dmhj*w1J)GfGOeyEH#w^;mG!+AhNrr&AuP|0o_iy-tV*A2MPKd5 zMR{hOQBm{OF>O^B!JY1|gp*aXX8#ysiAD@=$G#J0+NPXW%{o0zXZi#AxiK?Ho!^>J SagmkBT&K;aPYqWU3jYO{Cl<{B diff --git a/Art/Districts/1200/DataCenter.PCX b/Art/Districts/1200/DataCenter.PCX index 636957e1298e1c07ede3a92584253a1197b4814c..2f696b8173a63d9f7f4b04c2a78d020a694894ae 100644 GIT binary patch literal 15744 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Ξp!|5+il`w~xp5-JLX=>4Q^78nluP=Ccx?|)L zlUSYQYWfatF{=~c{=sz5oOrT!O^Ba>$u?SX&bE;jbXBc~@(u>(!Nm$ks2gv@A0_e= z@TDiwxQRT^v<1`6|E5#&r!}+^sW2i_Lh%0xS8rF%2uX^zx3=oIaN;FN2_u;iDBP(` zCf|CNZZ^D5RO$pAc__&mHfxOaaPePPKv@ZnMKGhMH1$!9d- zRg#$!R*WKnSMe$4Fxa!w!4;a?2yGvYHyW=yeo`2PL`7{kkLiX(7afu0CF%cxfkUU_Y%hi^DEo!HKtivJI~Vsh<2FPND+O?tUF z6HcS0g?VeI92lpT;;QXxUHoNcWU*J-fr!Z4D9E#l&o{JFLgKnbOHIk7V1Y=i9-tpE zQGk<6hvyDm7QyLzWPG74rVwj$pCmxrXGikTmsBZmc31l;2&H^gmp^Aw3l_}&ZZFTT zh*=UR3tSh`PfEh94hWQ|J4^?SU);TIhi_HT9^=(NFZO9y!jJy_-yasI=ymG9z&^4N= zGI_QQ5!qNdDxeed7ED!+Kx;wZOBOsSv|>ipg8L7D<*gSMcvv5zw{0(ZQ(3W73tghmKo*Wd%6xXR={=zFS zD7-McwZnI_?l2L$;T%IU$w=nEc8%Lu&4r`f4?%s@SH30sqw>?C#YIt8Du4efzQ-mG z9M3+t=)oL`7=G~YF8XH6aY`e;NGuDO=N~Z9z~zuUK;2YkP=qUGRi$UC;_#|weCZt5 zgD(;}FHG;N z@a^H-#NW||mXnO9nDB$6W=myeOGg;>*ML%4u4O zl*S1RCMpHuJxq`TS8|KW?02tX$Xx#OyD!RHnd3{t4+`9;jgiXu(Yi{3$S)6Tc)9YA z$pS`ajqIIQM(bh(_3vBH%L@nT;pZee>6Melk?~|Sf9P%boCOTq*VyJMM&o~N-Z|&x zH=dht4bsf_c0>3BevP*hnZ;iH++7ZDXF9w|uQbG**U8Aq#OcucuencoULwOh&V1%K z^l9JFLA5sepBDCpmL|$qh>5+QgPzKt<^zND?O`7a{&tk@4O1sfModgiL^0w&3#td{ zZ-;$)_}f9ZcV00xe#O#65F;@7V`Msr-w!h1GY);j_}d9MOz`IN+{XMaz8j>O?;nS- z@84fdqDwL*!y~3an)!Tk2p|3Xqf4sF*#6fDWDvigWR9W?eSP`cSvl&AF|~3LHa}vI zwheu<$$emMJbno}W|D{A#~GV@=vukVob}4=S;oe^IhHnBM%Higcs4e?mu+k(O|xESvCrAb$I7P zo{z-a%h{LmT>OrmyN`#r_u>-E;>GX0ypw_{jeJkCJxrPtkhod6 z>GZ02iFDKZTjNBjaUt8K%O$c6F^L;erQ)RcjR7fTsnU({30q|G$I_EL(~=Y8(zfnS z-YDLAWcN;+?8JkKX**-(2X=3heZ2jn+|)HY(>~t0BPnP52C4ka&a`8>JL9wE+Y3LA zOv+Xi?3U$au8R6sUC!Q}2lwpC%G`5s@75!GCGvgSkMG_1$^P9t_aEAyyW{A7-{Nff zhlg(+-M6)9|Ht_Ua=s{Z+g@CEGC#Li@$t#S+YTK){%KM6mqqe&Mf#^l3(Ak?=6tRs zU&K^>ewdVPKl$~iDuXK3UuT>yJ5pPAp#JO9@`}?XP*+uXIJ^4Wv!tws9Iqjz4K-Pf m)rT(Dd~u-`F4dm6Qd`>CP~F@Bmo8tqbFI3yrJ?)BZ~qSrci!Lt literal 29254 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 ltsd9E?qUA#-+K11dzk Date: Sun, 4 Jan 2026 22:27:03 -0800 Subject: [PATCH 141/356] Fix Data Center art magenta & green indexing --- Art/Districts/1200/DataCenter.PCX | Bin 15744 -> 15249 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/DataCenter.PCX b/Art/Districts/1200/DataCenter.PCX index 2f696b8173a63d9f7f4b04c2a78d020a694894ae..84c85db9a7f8cc874641012eaadb67410250d13e 100644 GIT binary patch literal 15249 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 Z#~W))n;NQH8sOrk#@kn`HE8DV{{fIHZTkQK literal 15744 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Ξp!|5+il`w~xp5-JLX=>4Q^78nluP=Ccx?|)L zlUSYQYWfatF{=~c{=sz5oOrT!O^Ba>$u?SX&bE;jbXBc~@(u>(!Nm$ks2gv@A0_e= z@TDiwxQRT^v<1`6|E5#&r!}+^sW2i_Lh%0xS8rF%2uX^zx3=oIaN;FN2_u;iDBP(` zCf|CNZZ^D5RO$pAc__&mHfxOaaPePPKv@ZnMKGhMH1$!9d- zRg#$!R*WKnSMe$4Fxa!w!4;a?2yGvYHyW=yeo`2PL`7{kkLiX(7afu0CF%cxfkUU_Y%hi^DEo!HKtivJI~Vsh<2FPND+O?tUF z6HcS0g?VeI92lpT;;QXxUHoNcWU*J-fr!Z4D9E#l&o{JFLgKnbOHIk7V1Y=i9-tpE zQGk<6hvyDm7QyLzWPG74rVwj$pCmxrXGikTmsBZmc31l;2&H^gmp^Aw3l_}&ZZFTT zh*=UR3tSh`PfEh94hWQ|J4^?SU);TIhi_HT9^=(NFZO9y!jJy_-yasI=ymG9z&^4N= zGI_QQ5!qNdDxeed7ED!+Kx;wZOBOsSv|>ipg8L7D<*gSMcvv5zw{0(ZQ(3W73tghmKo*Wd%6xXR={=zFS zD7-McwZnI_?l2L$;T%IU$w=nEc8%Lu&4r`f4?%s@SH30sqw>?C#YIt8Du4efzQ-mG z9M3+t=)oL`7=G~YF8XH6aY`e;NGuDO=N~Z9z~zuUK;2YkP=qUGRi$UC;_#|weCZt5 zgD(;}FHG;N z@a^H-#NW||mXnO9nDB$6W=myeOGg;>*ML%4u4O zl*S1RCMpHuJxq`TS8|KW?02tX$Xx#OyD!RHnd3{t4+`9;jgiXu(Yi{3$S)6Tc)9YA z$pS`ajqIIQM(bh(_3vBH%L@nT;pZee>6Melk?~|Sf9P%boCOTq*VyJMM&o~N-Z|&x zH=dht4bsf_c0>3BevP*hnZ;iH++7ZDXF9w|uQbG**U8Aq#OcucuencoULwOh&V1%K z^l9JFLA5sepBDCpmL|$qh>5+QgPzKt<^zND?O`7a{&tk@4O1sfModgiL^0w&3#td{ zZ-;$)_}f9ZcV00xe#O#65F;@7V`Msr-w!h1GY);j_}d9MOz`IN+{XMaz8j>O?;nS- z@84fdqDwL*!y~3an)!Tk2p|3Xqf4sF*#6fDWDvigWR9W?eSP`cSvl&AF|~3LHa}vI zwheu<$$emMJbno}W|D{A#~GV@=vukVob}4=S;oe^IhHnBM%Higcs4e?mu+k(O|xESvCrAb$I7P zo{z-a%h{LmT>OrmyN`#r_u>-E;>GX0ypw_{jeJkCJxrPtkhod6 z>GZ02iFDKZTjNBjaUt8K%O$c6F^L;erQ)RcjR7fTsnU({30q|G$I_EL(~=Y8(zfnS z-YDLAWcN;+?8JkKX**-(2X=3heZ2jn+|)HY(>~t0BPnP52C4ka&a`8>JL9wE+Y3LA zOv+Xi?3U$au8R6sUC!Q}2lwpC%G`5s@75!GCGvgSkMG_1$^P9t_aEAyyW{A7-{Nff zhlg(+-M6)9|Ht_Ua=s{Z+g@CEGC#Li@$t#S+YTK){%KM6mqqe&Mf#^l3(Ak?=6tRs zU&K^>ewdVPKl$~iDuXK3UuT>yJ5pPAp#JO9@`}?XP*+uXIJ^4Wv!tws9Iqjz4K-Pf m)rT(Dd~u-`F4dm6Qd`>CP~F@Bmo8tqbFI3yrJ?)BZ~qSrci!Lt From 7c659c5c3911b0cf31a53ac0525514aa7a23a9b0 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 5 Jan 2026 17:58:33 -0800 Subject: [PATCH 142/356] Initial attempt at adding Power Station art, config, rendering code --- Art/Districts/1200/PowerStation.PCX | Bin 7321 -> 100221 bytes Art/Districts/1200/_template.PCX | Bin 7321 -> 7297 bytes C3X.h | 34 ++++++++-------- Civ3Conquests.h | 1 + default.districts_config.txt | 61 +++++++++++++++++++--------- injected_code.c | 43 ++++++++++++++++++++ 6 files changed, 103 insertions(+), 36 deletions(-) diff --git a/Art/Districts/1200/PowerStation.PCX b/Art/Districts/1200/PowerStation.PCX index 01e2abc7b13073e2d1bce9eeff977669959badd7..d5fbfcd0e3c6b84c4829d9f1debf0be344f2db5e 100644 GIT binary patch literal 100221 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 literal 7321 zcmeI1ZA_C_6vtu7l4WxroXNI04Vi=xK&GWa5!NwqfCa07l+qFBP>XKJCMsd#Ylf~W zY6pT?v17hLiZbW4B8o8x*zqO<2Pmk#O;DItWXO>D+@0q#GH3j=_+k4*oBVEi?$i5n z?m7S4d;9W>wlCSxi`g<88(Vrm@5|Qah3Eg$-<>ewB4fe`i}@#k^RaM=YKA!f;Cw`U zOqL3=4{-j@`H(my3<{UY-pBbH=L6y(Jy}O~E9ZUAd&B{uUuYnE59hC(-Nb%sp^5BW zoVPhUiG4z^aFgsEoWF3k6ML!UcCxo{wsPJiT7|npC)t}huX8pL?^2K5WN+lW%Gp5d z5$*{0$bN=w=+IWG}Agf>AS zdkJSL=S5oS-3`J4ST@Z z$Jxtyjb_Z)6?Tu)%6XTwQK+Y~k#%$SaNgmpCvPy;%sM%{IB#)Y5o)PyVeOn9oNb)7 z=xmWUPlZaEjyXVd9v2Sfx--rIpoj zilebIv1cnQCl52$$1Zb@ATQ*3#Fo)%d~-e+uxUE&nKKSVZ>B|wrB3(v0j&y%yj~L!Bp4=&L4QdPZjmC=RhJ1m1hkT9J2CXGp zdlV}uwoojh*hR69vjb-o&O)5IINPy8U{%3Ngw+cx9#%=LtXPe)f@9Txat|5aNs6Dj zi#%P2Tg0QlBgeDF9>QM5D}h%JuQ-kp99cLTaRlS2$C)ycTeS86^($@o_ZiAXl)EU` zkv1SLLE3|~3TYeCLZqEYYmqi1El1jqcMDvD+CqJ!v7)&lUm)KhU!%1_Yl+q##R`fo z6pJW!M`E3R=n7+Jk+HK#ADzygJVA}^Ac{;C99-_QOd5&}e=?Kytq*F-O zkPafGQ^D=ajLtNc|``rT_O?WNaUK_J0RRn~|0y?Z>;txNg&mLY>;()uA>^ zTfTciOLyJyjzj%!DDC!{2XN6wK3%AHgS$zc7?goP4Q=I3<V0^Ul*U1YDauW-T>i%j?Q^lV3t3sPCTf%~-f4SQK zZf%0K{d?WU#pUNyFPzjJ`*yWTDcQSCRg@Q_^moeC1@Bz7A*MTNW`KR(XSC?h9 zHW&|Pt+;Sv!TdS)`egU*y7-SYZmJO1pa92Mt?X*W$B(+>%(=634f1g1o9Vl^`T95y z^%^3>rIKlHWu=D)%yK9`qYYI$X;dyNm&;c6qm3N&$FkKj^)M#E2hqH zn^al0+}CHyOxa{}zB>PiGIhJpyWaLGy4gPR$%;U`UE95~4Q@k&g}YOOjajn_3+60~ zaZXS3a&d8tkJq}pPm{|fN~PD{y^B3Orc9hTZp-GG27|X=@3u02Qucv}HAynvHfg58 z>3XX^$E4nwD)aJoof{(Enxe|cQmjsL&d!-Bk&ZW?m*yM~NZap`oZ{dgD9bm0a-!(N z=tWU7`2<~t%R3Ug?U?~~_OEQ(^`4KPG$~nA)3qi-J5jE9ZFb~@m@j55PIQ!dxEw9q zny^Er%hCjC9MzvrHJ4^;R|e%<68w}?U1q+TW?X-+QK!^+PWQCWE?gy_XXhU>!_9AE z+@>%m+3Rml9j{&zurE*7*ne_E#u5)dm$Iv;KbY-%q+DOnlI`m5q|o?o%}e~IDBao9 zF?QF|$kh?1Jk#3sYv~rkW3KmSIqMV8={Hmu4P9sTEkAfh1s;B2`KsRJ8yl|MwsoIz z|G`6tzB_*Wl-Yc)sHmj0w6?CUwXLn9s_JS}Q|Im5!J(n9?lPI&CoMh0dcRkt)district_infos[POWER_STATION_DISTRICT_ID]; + int coal_plant_id = info->dependent_building_ids[0]; + int hydro_plant_id = info->dependent_building_ids[1]; + int solar_plant_id = info->dependent_building_ids[2]; + int nuclear_plant_id = info->dependent_building_ids[3]; + bool coal = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, POWER_STATION_DISTRICT_ID, coal_plant_id); + bool hydro = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, POWER_STATION_DISTRICT_ID, hydro_plant_id); + bool solar = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, POWER_STATION_DISTRICT_ID, solar_plant_id); + bool nuclear = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, POWER_STATION_DISTRICT_ID, nuclear_plant_id); + + int index = 0; + + if (! coal && ! hydro && ! solar && ! nuclear) index = 0; // None + else if (coal && ! hydro && ! solar && ! nuclear) index = 1; // Coal + else if (! coal && hydro && ! solar && ! nuclear) index = 2; // Hydro + else if (coal && hydro && ! solar && ! nuclear) index = 3; // Coal, Hydro + else if (! coal && ! hydro && ! solar && nuclear) index = 4; // Nuclear + else if (! coal && ! hydro && solar && ! nuclear) index = 5; // Solar + else if (coal && hydro && solar && nuclear) index = 6; // All + else if (coal && hydro && ! solar && nuclear) index = 7; // Coal, Hydro, Nuclear + else if (! coal && hydro && solar && nuclear) index = 8; // Hydro, Solar, Nuclear + else if (! coal && hydro && ! solar && nuclear) index = 9; // Hydro, Nuclear + else if (! coal && hydro && solar && ! nuclear) index = 10; // Hydro, Solar + else if (! coal && ! hydro && solar && nuclear) index = 11; // Solar, Nuclear + else if (coal && ! hydro && ! solar && nuclear) index = 12; // Coal, Nuclear + else if (coal && ! hydro && solar && nuclear) index = 13; // Coal, Solar, Nuclear + else if (coal && ! hydro && solar && ! nuclear) index = 14; // Coal, Solar + else index = 0; + + return index; +} + void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { @@ -25856,6 +25892,13 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } break; } + case POWER_STATION_DISTRICT_ID: + { + // Power stations can have multiple buildings that - unlike most district buildings - don't have each other as prereq buildings, + // and thus have a combinatorial number of images based on which power plants are built in their radius. This returns the correct image index + buildings = get_power_station_img_index (tile_x, tile_y); + break; + } default: { struct district_infos * info = &is->district_infos[district_id]; From cfc1ab7209ef2ac46bb13d257dcab4aea402a766 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 6 Jan 2026 10:18:35 -0800 Subject: [PATCH 143/356] Fix AI not being able to build wonders across turns; Change Tile Highlights red->green; Fix Encampment ancient shadow; Clean up naval abandoned water art; Ensure air & naval units also only buildable by applicable leaders; Specify river for Power Station --- Art/Districts/1200/Abandoned.PCX | Bin 29061 -> 24937 bytes Art/Districts/1200/Encampment.PCX | Bin 40840 -> 40712 bytes Art/TileHighlights.pcx | Bin 67258 -> 67258 bytes C3X.h | 12 +-- civ_prog_objects.csv | 1 + default.districts_config.txt | 3 +- injected_code.c | 130 ++++++++++++++++++++---------- 7 files changed, 98 insertions(+), 48 deletions(-) diff --git a/Art/Districts/1200/Abandoned.PCX b/Art/Districts/1200/Abandoned.PCX index 8795f1bdf1e90bd6ac1b2ecf01bfa68374e9e497..3acbdc34901de5444154be91bdf56099f2a2ac28 100644 GIT binary patch delta 5345 zcmeHKO>9(E6wXYkrr1R|+(8OSzsbMN4YY|%?K<3lX}fJ2wrVdtHcFhC8R*n@J#mfDJQU!lyC>ia zt!d3Q5+(M?6cD8p+|gdG8csy%H_X2@wEPoNoScbLH$h4SMS^F-AUFaP0C42@|J z%1QeSL1LfNHVb3>LiSk#*3!Rop17%VY;ijJySke*G{VuX`0Es8rl%uK-~G@N!{!!C z={*09DhO8Z4{x2PtDLC{?s2NMm(%hCgB%+|=u$Bf^Y~Nn_5P^)CW%F{Bnlioe2?2w zQBkkc-;th(KSiOQ#zQ7@6I(6|WX)|piy+E+cQ44@xWqB`Xz!FfMMD?*!^}+6AdmS2 z{hEwgMdoz*Fptps%Eg8}iF-&^A!6X7D9(~7pdL3oIu^48wgVqMb(gv|QMyT}N72wl zF-&j9X`7ZI5YKP0 zjX>~VuaY8vBZiaQ9(zECyP7i8CoiPScT2>Kq^ys^D182QbaRRPL$zLP;687b#FCCo zX4+|S)+b6M(0)Q+w?MU16hLpA%V`cNH)NE293GxHvtyJj9z-|8OMHru+p*6gu6T1R zV01B5rb*{x@YTt4>L`g~1l`dY2~1$0DGH*D6;oNlu!@~+=o2`k{0MKII=6jTrr9$m zxubI{hy@-uu#}c*GlNd#3i<|z6p_CLYrD>ESs-MxHrOHE?g*4ISjM1I#$bL9zVGUO zZk~F}AQtR5#YEtk$fsy9+!;cyI@%3YNt7#M8ahvRZl5QXlviRFSA6I#1Nq_ep+Q+y zK206EBnzixElt9`T^;hL_}MVmk&B|35T$X#Vv=LPi%A7lMU+GySr*=Zr*+31$vTv& zdW4m{Eh1-jlOYeGPjxtP!oWltq8ko6Z&NTMGbM8)wZY<7P-%8Kk&IXhKJ0#N^&cc( zN9UrHg~&#qr0fXXX@7Nvy89iiwqsPBU3)eO%|Gj?7UN=UAO*XfFRsl{P*S^W9bB=FgUzL`m3J2toEh<^_<&@KYZQc2Su-xVo{;?3 z*?`t#>BA6pZP__NWrH@*Yw1a%<-5!Ca{WcFOUw6{CHt2@DH~H{Ak(iB}GNf0A X^nW*`SGvv0kp4d((!Xsyd$j6L55Az# literal 29061 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 Y*s^YYSI+}?-4!6G>U{Rs4I26WznpXp?*IS* diff --git a/Art/Districts/1200/Encampment.PCX b/Art/Districts/1200/Encampment.PCX index 94fffddbcb1e6b644c8f3d31ac9bf6250cb61afa..0c1c276e731b07fadbc0be928e50b610cd77d7b1 100644 GIT binary patch delta 561 zcmY+=O=uHQ5C?Gbb~hgvle90PTD2Rrp^<_gD70vUkfoPKq1jCfMZrVB9(od~P!C?J zE($8VveX`o5WI-D3Oy8*Y?8(p+5{^|QSi`%C$*I-Ey5dTx+hP+nfdcR82c@=->@eu zQ+fqJ{*hHE|Gb^tZ+7K-9Q~H%jS3DEHTWxcF@KzUZkt@MV)pC*c1Nz^a2pTQ;6YAX zfjzvi2IY@)Zn|J*EVG9351fcozCyeX#ca2mw$0>>9?R4*`X=ijUitdt4JbN3*NB-p zGkeBvV6-V4Ahvu1F)N*KV*gb(q2!cYBX8OJwx+QL`B|=lm}aT? z8ay+ce8oB4ZE!o*!sruzR4lU-2U~D_gum36oKmzf#_a{(#&lh_K&Z-DhFny-h-=CK zQB)3l91>rYUMl_SQ6i-p#0~Wrv7lZezN-m>YnO@ZS|2f|S!xU1L9wm%dqvWJg@XJ3 zlf<(BDsd!mju;P2sRjNHL^E)~E5gAUh6{WY93tAmyNU~gVj?t5F1`p&YOD<+63%(_ zh}rNx_5s9!NXDZ_+>G2()}bi2BE$Qhk3OYnBRWE)I_Bx+YR4$i)j7{_jjuaLk;M?d G^Y9<)Yb&V$ delta 680 zcmYk3UuaTM9LMqgaqhu?+ZCiqIAy+|$wB(O%B^eE<9ooOAd$1pk(J zQZrT&Qvaw`B>uWRP<{H*HL{2e1sjj!vpmPN)o*nbubZm)Wf47$(>1i!naGQ+yc9}v z;RM%iZQz+a_;hJ|4b2rJeNu?$2BuxM@DdJxsMv%#o!7pM{6#@0u{JKwMST1U4!^6| zgbiK5mJ20iYXC3*C8TS}db}a}cYdXd7&zUCfd{8?HD8!&gMq*1; zg8s>ig)*07gkmWjnX<9IIJt(K4Sd>m4yVXKS6c)F6L5!g?BYG}iri1vQE*QXI z{*o-FLTnIDQu{Q@P*J^%qS=fWUQ_)$_?bF`^K2mo9@?W(oKAGtk*H+EpFtgzY40xe zl|GMKC$eq`=nrc&slU*s%n&T=k7|VUR}CV9ag?G{~2cfXGr_cVEmtf;s5kktc(``;64ru delta 31 ncmdnh%d)GNrC|%>9ag^oXBhs^WcZ)P@ZXr>Kg0A_tc(``<2w!u diff --git a/C3X.h b/C3X.h index b06b223f..4cfa8eb9 100644 --- a/C3X.h +++ b/C3X.h @@ -734,7 +734,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_CentralRailHub, .name = "Central Rail Hub", .tooltip = "Build Central Rail Hub", .display_name = "Central Rail Hub", - .advance_prereq = "Steam Power", .resource_prereqs = {"Coal", "Iron"}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 2, .dependent_improvement_count = 0, + .advance_prereq = "Steam Power", .resource_prereqs = {"Iron", "Coal"}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 2, .dependent_improvement_count = 0, .img_paths = {"CentralRailHub_AMER.pcx", "CentralRailHub_EURO.pcx", "CentralRailHub_ROMAN.pcx", "CentralRailHub_MIDEAST.pcx", "CentralRailHub_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 5, .max_building_index = 0, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, @@ -745,7 +745,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_PowerStation, .name = "Power Station", .tooltip = "Build Power Station", .display_name = "Power Station", .advance_prereq = "Industrialization", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 4, .img_paths = {"PowerStation.pcx"}, .dependent_improvements = {"Coal Plant", "Hydro Plant", "Solar Plant", "Nuclear Plant"}, - .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .custom_height = 84, + .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_RIVER)), .custom_height = 84, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 2, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 @@ -914,10 +914,10 @@ struct district_worker_record { }; enum wonder_district_state { - WDS_UNUSED = 0, // Wonder district built, no wonder assigned - WDS_UNDER_CONSTRUCTION, // Reserved by a city for wonder construction - WDS_COMPLETED, // Wonder completed on this district - WDS_RUINED // (Future) Wonder was destroyed + WDS_UNUSED = 0, // Wonder district built, no wonder assigned + WDS_UNDER_CONSTRUCTION, // Reserved by a city for wonder construction + WDS_COMPLETED, // Wonder completed on this district + WDS_RUINED // (Future) Wonder was destroyed }; struct wonder_district_info { diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 2fb8393d..4bfd4d61 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -76,6 +76,7 @@ define, 0x4DAA70, 0x4E3430, 0x4DAB30, "Main_Screen_Form_issue_command", "cha define, 0x609D60, 0x6233C0, 0x609C80, "clear_something_1", "void (__stdcall *) ()" define, 0x6207B0, 0x644F10, 0x6206E0, "Timer_clear", "void (__fastcall *) (Timer * this)" inlead, 0x55EFA0, 0x56B030, 0x55EF50, "Leader_can_do_worker_job", "char (__fastcall *) (Leader * this, int edx, enum Worker_Jobs job, int tile_x, int tile_y, int ask_if_replacing)" +define, 0x56A7C0, 0x0, 0x0, "Leader_can_build_unit", "bool (__fastcall *) (Leader * this, int edx, int unit_type_id, int param_2, bool allow_kings)" define, 0x455288, 0x457458, 0x455308, "ADDR_CHECK_ARTILLERY_IN_CITY", "void *" inlead, 0x455140, 0x457310, 0x4551C0, "Unit_ai_move_artillery", "void (__fastcall *) (Unit * this)" define, 0x5E6E50, 0x5F66A0, 0x5E6D80, "neighbor_index_to_diff", "void (__cdecl *) (int neighbor_index, int * out_x, int * out_y)" diff --git a/default.districts_config.txt b/default.districts_config.txt index 46bb27dd..e72cad7f 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -287,6 +287,7 @@ btn_tile_sheet_column = 7 vary_img_by_era = 1 vary_img_by_culture = 1 advance_prereq = Steam Power +resource_prereqs = Iron, Coal dependent_improvs = "Mass Transit System" buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 @@ -309,7 +310,7 @@ vary_img_by_culture = 1 custom_height = 84 advance_prereq = Industrialization dependent_improvs = "Coal Plant", "Hydro Plant", "Solar Plant", "Nuclear Plant" -buildable_on = desert,plains,grassland,tundra,floodplain,hills +buildable_on = desert,plains,grassland,tundra,floodplain,hills,river defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 0 diff --git a/injected_code.c b/injected_code.c index 015565e4..393ffc02 100644 --- a/injected_code.c +++ b/injected_code.c @@ -9100,7 +9100,6 @@ city_has_wonder_district_with_no_completed_wonder (City * city, int wonder_impro if (info->state == WDS_UNUSED && buildable) return true; if (info->state == WDS_UNDER_CONSTRUCTION && buildable && info->city_id == city->Body.ID) { info->city = city; - info->city_id = city->Body.ID; return true; // Reserved by this city } } @@ -16162,6 +16161,9 @@ patch_City_can_build_unit (City * this, int edx, int unit_type_id, bool exclude_ if (prereq_id >= 0 && ! Leader_has_tech (&leaders[this->Body.CivID], __, prereq_id)) return false; + if (! Leader_can_build_unit (&leaders[this->Body.CivID], __, unit_type_id, 1, false)) + return false; + // Air units if (type->Unit_Class == UTC_Air) { if (is->current_config.enable_aerodrome_districts && is->current_config.air_units_use_aerodrome_districts_not_cities) { @@ -16187,13 +16189,8 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply if (! is->current_config.enable_districts) return base; - // Check if district is actually needed or city already has the improvement int required_district_id; bool district_required = itable_look_up (&is->district_building_prereqs, i_improv, &required_district_id); - if (! district_required || patch_City_has_improvement (this, __, i_improv, false)) { - return base; - } - Improvement * improv = &p_bic_data->Improvements[i_improv]; bool is_wonder = is->current_config.enable_wonder_districts && improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder); bool wonder_requires_district = is_wonder && required_district_id == WONDER_DISTRICT_ID; @@ -16210,6 +16207,16 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply } } + // Check if district is actually needed or city already has the improvement + if (! district_required || patch_City_has_improvement (this, __, i_improv, false)) { + if (! is_wonder) + return base; + + // If a Wonder and doesn't need a district, do non-strict check only to avoid inadvertently + // blocking Wonders that are buildable but grayed out in UI because already being built + return City_can_build_improvement (this, __, i_improv, false); + } + // Ensure prereq tech for the improvement if (improv->RequiredTechID >= 0 && ! Leader_has_tech (&leaders[this->Body.CivID], __, improv->RequiredTechID)) return false; @@ -16267,10 +16274,9 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply } if (! has_water_in_radius) return false; - } - /* TODO: Uncomment this when Power Stations are added + // Allow improvements that must be near river if river in work radius - else if (improv->ImprovementFlags & ITF_Must_Be_Near_River) { + } else if (improv->ImprovementFlags & ITF_Must_Be_Near_River) { bool has_river_in_radius = false; FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { if (wai.tile->vtable->m37_Get_River_Code (wai.tile)) { @@ -16280,7 +16286,10 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply } if (! has_river_in_radius) return false; - } */ else { + } else if (is_wonder && City_can_build_improvement (this, __, i_improv, false)) { + // Do nothing, game is only disallowing because the city is already building the Wonder + // (i.e., in UI, the Wonder is grayed out because it's already being built) + } else { return base; } } @@ -16310,15 +16319,13 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply // If human has everything needed except a built district (which is buildable), allow relaxed checks so UI can gray entry out if (is_human) { if (is_wonder) { - if (city_has_wonder_district_with_no_completed_wonder (this, i_improv)) - return true; - else - return ! apply_strict_rules; + return city_has_wonder_district_with_no_completed_wonder (this, i_improv) + ? true + : ! apply_strict_rules; } else { - if (city_has_required_district (this, required_district_id)) - return true; - else - return ! apply_strict_rules; + return city_has_required_district (this, required_district_id) + ? true + : ! apply_strict_rules; } } @@ -16355,6 +16362,9 @@ ai_handle_district_production_requirements (City * city, City_Order * out) Improvement * improv = &p_bic_data->Improvements[out->OrderID]; if ((improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) && (! city_has_wonder_district_with_no_completed_wonder (city, out->OrderID))) { + snprintf (ss, sizeof ss, "ai_handle_district_production_requirements: City %d (%s) needs wonder district to build wonder improvement %d\n", + city->Body.ID, city->Body.CityName, out->OrderID); + (*p_OutputDebugStringA) (ss); needs_wonder_district = true; if (required_district_id < 0) { required_district_id = WONDER_DISTRICT_ID; @@ -16426,6 +16436,11 @@ patch_City_ai_choose_production (City * this, int edx, City_Order * out) is->ai_considering_production_for_city = this; City_ai_choose_production (this, __, out); + char ss[200]; + snprintf (ss, sizeof ss, "patch_City_ai_choose_production: City %d (%s) chose order type %d id %d\n", + this->Body.ID, this->Body.CityName, out->OrderType, out->OrderID); + (*p_OutputDebugStringA) (ss); + if (is->current_config.enable_districts) { if (ai_handle_district_production_requirements (this, out)) { return; @@ -18036,27 +18051,27 @@ patch_City_add_or_remove_improvement (City * this, int edx, int improv_id, int a // as completed (which switches the art over and prevents the tile from being used again) int x, y; if (add && is->current_config.enable_districts && is->current_config.enable_wonder_districts) { - if (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { - int matched_windex = find_wonder_config_index_by_improvement_id (improv_id); - - if (matched_windex >= 0) { - FOR_DISTRICTS_AROUND (wai, this->Body.X, this->Body.Y, true) { - x = wai.tile_x; - y = wai.tile_y; - Tile * t = wai.tile; - struct district_instance * inst = wai.district_inst; - if (inst->district_type != WONDER_DISTRICT_ID) continue; - if (! wonder_is_buildable_on_tile (t, improv_id)) - continue; + if (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { + int matched_windex = find_wonder_config_index_by_improvement_id (improv_id); + + if (matched_windex >= 0) { + FOR_DISTRICTS_AROUND (wai, this->Body.X, this->Body.Y, true) { + x = wai.tile_x; + y = wai.tile_y; + Tile * t = wai.tile; + struct district_instance * inst = wai.district_inst; + if (inst->district_type != WONDER_DISTRICT_ID) continue; + if (! wonder_is_buildable_on_tile (t, improv_id)) + continue; - struct wonder_district_info * info = &inst->wonder_info; - if (info->state != WDS_UNDER_CONSTRUCTION) continue; - if (info->city_id != this->Body.ID) continue; + struct wonder_district_info * info = &inst->wonder_info; + if (info->state != WDS_UNDER_CONSTRUCTION) continue; + if (info->city_id != this->Body.ID) continue; - // Mark this wonder district as completed with the wonder - info->city = this; - info->city_id = this->Body.ID; - info->state = WDS_COMPLETED; + // Mark this wonder district as completed with the wonder + info->city = this; + info->city_id = this->Body.ID; + info->state = WDS_COMPLETED; info->wonder_index = matched_windex; break; } @@ -20382,8 +20397,15 @@ patch_Leader_do_production_phase (Leader * this) } // Wonders + char ss[200]; if (is->current_config.enable_wonder_districts) { + snprintf (ss, sizeof ss, "patch_Leader_do_production_phase: Checking wonder improv %d for city %d (%s)\n", + i_improv, city->Body.ID, city->Body.CityName); + (*p_OutputDebugStringA) (ss); if (city_is_currently_building_wonder (city)) { + snprintf (ss, sizeof ss, "patch_Leader_do_production_phase: City %d (%s) is building wonder improv %d\n", + city->Body.ID, city->Body.CityName, i_improv); + (*p_OutputDebugStringA) (ss); bool wonder_requires_district = false; if (i_improv >= 0) { int required_id; @@ -20395,12 +20417,18 @@ patch_Leader_do_production_phase (Leader * this) if (wonder_requires_district) { bool has_wonder_district = reserve_wonder_district_for_city (city, i_improv); if (! has_wonder_district) { + snprintf (ss, sizeof ss, "patch_Leader_do_production_phase: City %d (%s) lacks Wonder District for wonder improv %d\n", + city->Body.ID, city->Body.CityName, i_improv); + (*p_OutputDebugStringA) (ss); needs_halt = true; req_district_id = WONDER_DISTRICT_ID; district_description = "Wonder District"; } } else { release_wonder_district_reservation (city); + snprintf (ss, sizeof ss, "patch_Leader_do_production_phase: City %d (%s) wonder improv %d does not require Wonder District\n", + city->Body.ID, city->Body.CityName, i_improv); + (*p_OutputDebugStringA) (ss); } } else { release_wonder_district_reservation (city); @@ -20409,6 +20437,9 @@ patch_Leader_do_production_phase (Leader * this) // If production needs to be halted, handle the reassignment and messaging if (needs_halt) { + snprintf (ss, sizeof ss, "patch_Leader_do_production_phase: City %d (%s) halting improv %d due to missing district %d\n", + city->Body.ID, city->Body.CityName, i_improv, req_district_id); + (*p_OutputDebugStringA) (ss); // Switch production to another option if (ai_player) { mark_city_needs_district (city, req_district_id); @@ -20430,6 +20461,9 @@ patch_Leader_do_production_phase (Leader * this) } continue; } + snprintf (ss, sizeof ss, "patch_Leader_do_production_phase: City %d (%s) improv %d passed missing district check\n", + city->Body.ID, city->Body.CityName, i_improv); + (*p_OutputDebugStringA) (ss); } } @@ -24806,6 +24840,11 @@ patch_City_add_building_if_done (City * this) Improvement * new_improv = &p_bic_data->Improvements[order_id]; // Improvement might have changed if (new_improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { if (! patch_City_can_build_improvement (this, __, order_id, true)) { + char ss[256]; + snprintf (ss, sizeof ss, "patch_City_add_building_if_done: City '%s' cannot complete building of wonder '%s'; switching to defensive unit.\n", + this->Body.CityName, + new_improv->Name.S); + (*p_OutputDebugStringA) (ss); City_Order defensive_order = { .OrderID = -1, .OrderType = 0 }; if (choose_defensive_unit_order (this, &defensive_order)) { City_set_production (this, __, defensive_order.OrderType, defensive_order.OrderID, false); @@ -25754,7 +25793,7 @@ get_power_station_img_index (int tile_x, int tile_y) void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - //*p_debug_mode_bits |= 0xC; + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -25811,7 +25850,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par // Handle river alignment, if district allows it enum direction river_dir = DIR_ZERO; - if (district_allows_river(cfg)) + if (district_allows_river(cfg)) align_district_with_river (tile, &pixel_x, &pixel_y, &river_dir); if (territory_owner_id > 0) { @@ -26914,17 +26953,26 @@ patch_City_set_production (City * this, int edx, int order_type, int order_id, b if (! is_human) return; + char ss[256]; bool release_reservation = true; if (this->Body.Order_Type == COT_Improvement) { Improvement * improv = &p_bic_data->Improvements[this->Body.Order_ID]; if (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { - if (reserve_wonder_district_for_city (this, this->Body.Order_ID)) + if (reserve_wonder_district_for_city (this, this->Body.Order_ID)) { release_reservation = false; + snprintf (ss, sizeof(ss), "patch_City_set_production: City %s has reserved a wonder district for building wonder %s.\n", + this->Body.CityName, improv->Name); + (*p_OutputDebugStringA) (ss); + } } } - if (release_reservation) + if (release_reservation) { release_wonder_district_reservation (this); + snprintf (ss, sizeof(ss), "patch_City_set_production: City %s has released its wonder district reservation.\n", + this->Body.CityName); + (*p_OutputDebugStringA) (ss); + } } int __fastcall From 6c5c933c262cb4bc758ef1cce4fea6826b2a77fc Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 6 Jan 2026 11:52:09 -0800 Subject: [PATCH 144/356] Refine can build building checks; Update mideast holy site cathedral art --- Art/Districts/1200/HolySite_MIDEAST.PCX | Bin 13255 -> 13069 bytes injected_code.c | 48 +++++++++++++++++++----- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/Art/Districts/1200/HolySite_MIDEAST.PCX b/Art/Districts/1200/HolySite_MIDEAST.PCX index 9d09853f6281efaa92b0edcd9fd9eccbb5e63186..35a42d9b6e808f6d6372d0493ed1e29738e89bbc 100644 GIT binary patch delta 1314 zcmXw(e`phD7{_~Q7F*k1dTXv}8*`VYxwN&?n5$OTnCOdFabBy~+0rdeg|QX0ohVlr zxZ(E4yMf#M!JaK3Y=|5iQxOJ1%7m?Z-%FIC|4gNG&N-YLxPb#%+R2h!_C^>lynnp! z=lOo0_x--SS7wgA^5p^te{x=Uzzw-=CwLcR*QlF1kvp)_)dR;|s=MlR-G+OvcDUbWHS?jTd8GI2SWU5>R4gNKKDVKzmy>^TwkxuD2xSbNk0sq~k1SHe(^5Ka880ch>vm#n4HM zrZH;-@rd^(Nm(IfyOPF+Dxwb0MDAdlpXSI%)MsXI6 z`C4=F4xUq5V!UCpm6~drqS@Efj-;h#CsU@N`X!&4$;R7SVZK9xrcMgoyxK+#pvzq`mVL(Yx`QLOoRSr z|KNb%(pNKO6qta<6<;ZZDo>4!nK4;j7_r*$5vm^<4y?1cNb-O*Y-u$*6^uZ0@PU%A zCaKA@JS-cJV19^dR38m&p-LjTj+DJvXY^J$B=y17U_ZPneG{>(U70kZj7NNS%@Df; zeKfvSNzk2rq?ERpr|HdYm%Uu*y2DaIHe0e{Xyh5y#^J^-dE7*n@;GCXGW;m_LoyVC ztD!6M0t;Mtk?CUV5H^PxJS;Tl@n{V;S`xr_uY&vCw}Q88919O+Emuu)Sl}3?=9z7n zz^3p3EbF=1d6!vFWvPYMYc?Jdf_Z!-na8Ffz^2Fm6nffWBtkpxvESr{y@JUQHOStA zb(ro_Por7bqr~7r#0SqSm&MCLBFzk*yMIv$p{6^=*K6`fOhsC9@mF1MBjb4HF(l8tY|}N}E<`Vore1(+Jg`5WMf%QuC)8(MB(+)S1i5CT0d6dm`|gr_cAh8o@!u_$77L zCW%1-z24N~$Lbt8o>fAKyqG;LuFIS#WYs8@Mt2tGyv?v~-qGMAgv^W*b0RNi#Z8eD zQ!e*~qs}qOKXG6|Sqog8R|Vx|pS9doBd%Wlhs%PCivo@m#>YhQjIW*J?pWDM_@`{L z@y^p-r-YJo*;(XfoGIiB9$B;!nt)LG3OMsz12mR@6PbOsP5du*2T&|#B+7Au5pfSb zE`I~sD{`$rD0fu~<35XBkhAhTF@d<7CnsiQk&?K$3*!}=Vc-0G^KE4%hK1^rb9khi z+C4P?EDs)zw0022=dXYhmBrAEve=N-=i1R*!2bee9n}Jp5o4!{gq|J&U{IqEoDL6T&u*HJC%{&3WrJ?YcoZC@1r@LdQ_Dal$izhyINZlM zL#Vz99@O6tb?p&P5Mrk#o1`z2Si);?(ng7wuimNgq>+TTo)1Dz!$UoK*kKm4Gnn%< z#Svoa9%7i5!*x6+3B3)iaHZh|=+(ZCIK?+d5k}l$4z=+ykbp$7-`EoI0`(t;kB$QpO1< zi9mn4-!?hfFO6lewlNM~{X)}}8ag(xO=1Wvz-DI|_iiVifVBJ5rXH7uUI(j!E1@!Y zziCXR>E~29AZJpoQzcurrent_config.enable_wonder_districts && improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder); bool wonder_requires_district = is_wonder && required_district_id == WONDER_DISTRICT_ID; + // Ensure prereq tech for the improvement + if (improv->RequiredTechID >= 0 && ! Leader_has_tech (&leaders[this->Body.CivID], __, improv->RequiredTechID)) + return false; + + // Ensure prereq tech for the district + if (district_required) { + int prereq_id = is->district_infos[required_district_id].advance_prereq_id; + if ((prereq_id >= 0) && ! Leader_has_tech (&leaders[this->Body.CivID], __, prereq_id)) { + return false; + } + } + // Make sure wonder is not already built if (is_wonder) { if ((improv->Characteristics & ITC_Wonder) != 0) { if (Game_get_wonder_city_id (p_game, __, i_improv) != -1) return false; + FOR_CITIES_OF (coi, this->Body.CivID) { + City * other_city = coi.city; + if (other_city != this && other_city->Body.Order_Type == COT_Improvement && other_city->Body.Order_ID == i_improv) + return ! apply_strict_rules; + } } else { Leader * leader = &leaders[this->Body.CivID]; if ((leader->Small_Wonders != NULL) && (leader->Small_Wonders[i_improv] != -1)) @@ -16217,16 +16234,6 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply return City_can_build_improvement (this, __, i_improv, false); } - // Ensure prereq tech for the improvement - if (improv->RequiredTechID >= 0 && ! Leader_has_tech (&leaders[this->Body.CivID], __, improv->RequiredTechID)) - return false; - - // Ensure prereq tech for the district - int prereq_id = is->district_infos[required_district_id].advance_prereq_id; - if ((prereq_id >= 0) && ! Leader_has_tech (&leaders[this->Body.CivID], __, prereq_id)) { - return false; - } - if (is_wonder && ! wonder_requires_district) { return true; } @@ -25687,6 +25694,8 @@ wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner Map * map = &p_bic_data->Map; int best_dist = INT_MAX; int best_dx = 0; + int city_dx = 0; + int city_dy = 0; FOR_CITIES_AROUND (wai, tile_x, tile_y) { City * city = wai.city; @@ -25717,9 +25726,28 @@ wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner ((dist == best_dist) && ((int_abs (dx) < int_abs (best_dx)) || (best_dx == 0)))) { best_dist = dist; best_dx = dx; + city_dx = city->Body.X; + city_dy = city->Body.Y; } } + bool city_is_directly_above_port = city_dx == tile_x && city_dy == tile_y - 2; + bool city_is_directly_below_port = city_dx == tile_x && city_dy == tile_y + 2; + + if (city_is_directly_above_port || city_is_directly_below_port) { + bool northwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y - 1, true); + bool north_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y - 2, true); + bool northeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y - 1, true); + bool east_tile_is_land = tile_offset_is_land (owner_id, tile_x + 2, tile_y, true); + bool southeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y + 1, true); + bool south_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y + 2, true); + bool southwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y + 1, true); + bool west_tile_is_land = tile_offset_is_land (owner_id, tile_x - 2, tile_y, true); + + if (northeast_tile_is_land || east_tile_is_land || southeast_tile_is_land) + return true; + } + if ((best_dist == INT_MAX) || (best_dx == 0)) return false; From d79d0a05278e3412e631b1d20fa16a83f69f5de0 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 6 Jan 2026 12:16:57 -0800 Subject: [PATCH 145/356] Add config value for destroying neighborhood reduces population; Fix wonder direction correction algorithm --- default.c3x_config.ini | 1 + injected_code.c | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/default.c3x_config.ini b/default.c3x_config.ini index d5350e54..66e4de42 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -864,6 +864,7 @@ naval_units_use_port_districts_not_cities = true maximum_pop_before_neighborhood_needed = 6 per_neighborhood_pop_growth_enabled = 3 neighborhood_needed_message_frequency = 4 +destroying_neighborhood_reduces_pop = true ; These options control the vulnerability and rebuildability of completed Wonders in Wonder districts. When completed_wonder_districts_can_be_destroyed ; is enabled, completed Wonders (both Great and Small) can be pillaged or destroyed by enemies, making them valuable strategic targets that must be diff --git a/injected_code.c b/injected_code.c index fe447371..0452ce33 100644 --- a/injected_code.c +++ b/injected_code.c @@ -2415,9 +2415,6 @@ find_pending_district_request (City * city, int district_id) return NULL; int civ_id = city->Body.CivID; - if ((civ_id < 0) || (civ_id >= 32)) - return NULL; - int city_id = city->Body.ID; int key = get_pending_district_request_key (city_id, district_id); if (key < 0) @@ -25735,14 +25732,9 @@ wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner bool city_is_directly_below_port = city_dx == tile_x && city_dy == tile_y + 2; if (city_is_directly_above_port || city_is_directly_below_port) { - bool northwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y - 1, true); - bool north_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y - 2, true); bool northeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y - 1, true); bool east_tile_is_land = tile_offset_is_land (owner_id, tile_x + 2, tile_y, true); bool southeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y + 1, true); - bool south_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y + 2, true); - bool southwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y + 1, true); - bool west_tile_is_land = tile_offset_is_land (owner_id, tile_x - 2, tile_y, true); if (northeast_tile_is_land || east_tile_is_land || southeast_tile_is_land) return true; From 7b83656f589f8dfa4b22f50bdf975ba6ab22077c Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 6 Jan 2026 14:39:31 -0800 Subject: [PATCH 146/356] Update great lighthouse art --- Art/Districts/1200/Wonders_2.PCX | Bin 37045 -> 36703 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index a7d1ef95fee87f5e707103894badaa7b54befdf3..8567dd7aff6820280d7e82019bc2bd977dc0750f 100644 GIT binary patch delta 1309 zcmW+$ZA?>V6rLY+ZMXvEW~I9~w4>!_rO@kpd$+6Iy}e-9iyug#f7Db;TR&)v)QPQ< zWsykKxcQB5;(p+|;G)TrX_lB7=N9$87<5FG2yQw=U`m)XuILh%IMa7G{&|vf@|>LK zoaf~H&_91NJOBIEmiMmVeEwDbEtFR=@y-Co`Tz2R8oqvmukwR%e~WY85d5))UpR=P z*Wo`c-i18!-+)5cr@w)l*0_=U3)~l8uFtG7#(?Y|g1?4?g+n;f0%WwBCan;(h6-=u zOC3?sDgl2C!qyGMEv#Pc7W$E&tM(m_bS@gvVt%hWtk6_A0lOoa!aGZE=d4floIdE}q4LXC39SkyeYrnwW)0+71*y!IaXBLZWp$ zU@eI!uqJ+}_$f|`0-{8g2BgmS6b{9YEP93`7pP1nb&Zv!XE2rcvgB{fw+YJbK7jOSk}x94y~LFL|t6JPVE z`P0EvZ-$mw1E*u9P%;uF;;hD#Q(5~n?0HvE&Xf}EOBKG2_=qYeX~dF7T8afNq=6K* z6)f2L4f7XP?`Q-hWXns%NRkSsyrMyhb20)t)=si6PA{#5BU}3&f8wB7b5k8q!o1+H zxSG5vkBm48$Pyx35siSv5N>G}Wu|1J33Jq??n=>cDYSoK4Uy7{a+(AeN$ zyu`9}Ah}CWhtE{Ib%q!%`&S#73}8iiefLEZytw@f*8{AMo~&B&zB-moMnxtXZVLK; zQin@fh6CF&u2ud(x`B}wudOvzEgy%O^gb}IRs3wSSEvI^6-ErUhlsLfb;Qd_UL8pr zWA1YGc{&zUr%mcqeL@|*57|tLd4OvZq`el|vd02c^h8QDXk1wv6|A%%M#Dr8>VLxm)ovghFH|_{f6WHXecNR2u$55Y_ zi;2Kh*oX>D7U%D9X2-{+lem^OtvG|a9qL%T&Y3Vx!1X(q6isOA6Y)BGMZtOX@k4lN zr}OP8EoCuHLPHlbFAsxV_Ia=1(Jov0OFWaG&5vPJ@Id=>xAQMF9C?Gc@-NQ6?7Cw9 EA8}U|egFUf delta 1628 zcmW+$U2GLa6i$dMKU-qBg}t?UZ?ao&c4wM`&9Zl?*Sbd8oh7;z9?TL_S_;UI6y!fK z#)(0VB1kA>^r5=37)W>^F(D>ZAI!|H&_=X{yIAm#%Ws1eYuoT<7*BcF$?nOVbH49< z=i95vtZ({eecjRii0UGpt!vNC;ga$yMx4=hJf|!jN<1XksdjtzkJ5?YGHz&hsd_IWZnV;BN6Io&LI14?W(!w<&whexpQVWGp%FI++U>M zy`*`0eU`n|v0fdPTEtV@hE_s*-xBob7~20FXl`DV=} zfR{d#Ah!-*V%yifGk4IgP&WdcC>?c!PcW+Q-^0$t#qfYk@3FMWH}}$_H<;S2umM za7=noiKl)Nhk2b)q$*?VpG}7|cOXY312onf0}ZH?JM7@*Ba7|=UbsH<#0nOQNd&sf zp4#%+!h15J&^xi9lB)znN8}#+Y0IaX3E*B%i!=@q1|rCvV7s^WES!|~!_I=n3UNVU zmrSz9U+<}%mNP(SxFDhnf&((m23|j|P0N^%Qjv&MB$1A@X6Sn4K6`)L7rANa@ym`y zivSBS(AXpy;*tPqymFtB?mpvRsla<235O359Pu(O0-jLAVSu98@oa)P24born|{ z>xvFbYxovyw!4LWvZJr*rc7dBB&BTe;tTVS?R39s@YJ4zzbC}5TZg_f}WqG7oqV&=}zmsBAGwx;}09?kQ6 z6B;k&RXedQ9VU#h3F9x?J|e5*xB^$%sa;2#|B!qrPHa92l&eV*s8@yOxEB98F)V>dsCfp`=4l9|3sg7AvyHnC z+E=8viT`Y$7L%Y6<>dsX8+pimvKVDT&Z$P-u|iFudB^5w-+W_B zz;9e&xA)-!G)epUpay!=)rH;m_r-E1yQ-RG}JaZL??N1>;S#Uy7n7B zO!6#<4s!R1l6!0g@%Z&r-g+?hAeBP-L&^uk52oN8d3gQ;QRrFmU=As-M*Uz}Uag$@ Ja{mu?{{xaD>{I{% From d41d8481532e11d25b79702fce0dbe75a8651ef8 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 7 Jan 2026 08:39:35 -0800 Subject: [PATCH 147/356] Update bridge art; Update port placement logic --- Art/Districts/1200/Bridge.PCX | Bin 14624 -> 17013 bytes injected_code.c | 17 +++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Art/Districts/1200/Bridge.PCX b/Art/Districts/1200/Bridge.PCX index 2cbd35955f111ff742fdfcbae5e74f4ef5749f88..41096408a70cf285deb140504dea05bd6424d356 100644 GIT binary patch delta 5547 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 delta 2310 zcmZXVe{56N6~`4itCgMulz?qK9$j7uy$(bfecbz!xZ=r^u7kB*G*oTQf;#OFMp;*; zvQ-m*)DI_E8rxriJ58EiXDvg|+CNdN1x*o1NaxyMOk;i;mmi@CP?)CK2}#t_O4GZ$ zPBpDv|9J9~@BQ9$?m3_Dd7tFk{`|}LpKE{ax<47Pq<@pIZI5zHQ6M8Fcnw6OFm{6V|fQS{9)3Jk{VSholeW95Ie+FTF5=R$*~SSUs{Ojx)jcx!b$ zG$Vz10xEq&F47}L{G^Uk(l2wd`nwxGlCH>`!tK{nNCd4UHCij3YhLZRFU7PcOxF-8 zT!;+(taaFL)}+@zl)}w`$mXXoJ+N@JujZrAUTsp*TsAXUlBs7-z{^yUFCD}d5ESl2|eG}UzUcW8fyvRWXswUE%gfMi6 zIIu*O;yTDFJ83=P{_{O;jQ6bjo^?U8STJsm=wf&D`-fHCZb>Cw7CEz}HwXNvFHl7!-F!x7!W4lxm;DD|NvM{`G^>_Nkg8zQU|lt!^mJ87pKY{dDEjw3Hm zOZS`!jiAK>v0kPbdblnNd6>>6pduDsVIa+>g@6^X)5&f;OriyTWc?9Rm1cefD(MSK{FP+#Jm8?x3F>1Neciv425#9=)w zQW}ZYWLnkn8kEI=nf6>tsOYDfVAyDnM_bwwuWmSQU6X}KbBBAjWCEmN8 zuvWJGv+y5xsJ1O;yu7+og*p`JIF7GK#ic1EG?jEZ;e_%c<9bA(Xg{JixrqG! zkLdUVg=JK<2(3dSk8Dg-G3KGcuz76Q5JVXCHs2quzVeYuG!OH+Q~gjhu|Z)O8AxH+ zvXBD}b-=8{?65{*-cjV?;PA1H1~CLf_8tE7Z{GdRTE^`P>18dR7?kh(?-lCQZ=ac-k_Lcd@| zG~sGajC)u^#D<6P02XFXX)mV0Ca~!l)PbTRFByt`#iUQU_2R{@dveNY5vM|#YBNK4&t7CC)d%B$mVC-LZ@IAL zuAEX?R-n8pF-6P?Ca~CR#8V+<>i_!hDSxJ`+IdS^d>Lf+4P_*Uc_x$#C$&8hS~Q|7 zW_!w;U6n57(k0tz8Eax3rd=ozHLE@2BpyEC6cAN}Lj5y-bL;sRu1UvUf!j;uj;2}S zD9k6wX9Bu#Tr9u zxwyfC%d({0kuUQ?*7TPTmJj7pwO2p z)5R3SNsHO!a5O^2z&@f{_m)o z7X_$_t0iRvmHAbb(XxV1xj^l_rru`$#RJ=E_?};UFMe8$Rt1>?ZCU^P$lytwM0{^X&aa@&b__w&oWp=bZ$-<8sT WJo(zQz2BJJ9XQf9`L^Bm%zpp@FQEDW diff --git a/injected_code.c b/injected_code.c index 0452ce33..1ef1c990 100644 --- a/injected_code.c +++ b/injected_code.c @@ -25464,8 +25464,8 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, if (closest_city == NULL) return; - bool city_is_directly_above_port = (closest_dx == 0) && (closest_dy == -1); - bool city_is_directly_below_port = (closest_dx == 0) && (closest_dy == 1); + bool city_is_directly_above_port = (closest_dx == 0) && (closest_dy == -2); + bool city_is_directly_below_port = (closest_dx == 0) && (closest_dy == 2); bool city_is_directly_west_of_port = (closest_dx < 0) && (closest_dy == 0); bool city_is_directly_east_of_port = (closest_dx > 0) && (closest_dy == 0); bool city_is_directly_northeast_of_port = (closest_dx == 1) && (closest_dy == -1); @@ -25524,14 +25524,14 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } else { *out_variant = SE; anchor = DIR_NW; - *out_pixel_x -= 50; *out_pixel_y += 14; + *out_pixel_x -= 30; *out_pixel_y += 24; } } else if (city_is_directly_east_of_port) { if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } else { *out_variant = SW; anchor = DIR_NE; - *out_pixel_x += 50; *out_pixel_y -= 14; + *out_pixel_x += 30; *out_pixel_y -= 24; } } else if (city_is_north_of_port && city_is_west_of_port) { if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } @@ -25640,7 +25640,9 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 16; } if (*out_variant == SW && (anchor_sprite_index == 43 || anchor_sprite_index == 40)) { *out_pixel_x +=6; *out_pixel_y -= 12; } - if (*out_variant == SW && (anchor_sprite_index == 35 || anchor_sprite_index == 39)) { *out_pixel_x +=6; *out_pixel_y -= 12; } + else if (*out_variant == SW && (anchor_sprite_index == 35 || anchor_sprite_index == 39)) { *out_pixel_x +=6; *out_pixel_y -= 12; } + else if (*out_variant == SE && (anchor_sprite_index == 50)) { *out_pixel_x -=6; *out_pixel_y -= 12; } + else if (*out_variant == SE && (anchor_sprite_index == 26)) { *out_pixel_x += 50; *out_pixel_y -= 2; } } // Sheet 4 else if (anchor_sheet_index == 4) { @@ -26707,6 +26709,7 @@ district_tile_needs_defense (Tile * tile, int tile_x, int tile_y, struct distric if (inst == NULL) return false; int district_id = inst->district_type; + struct district_config const * cfg = &is->district_configs[district_id]; if (! district_is_complete (tile, district_id)) return false; if (tile->vtable->m38_Get_Territory_OwnerID (tile) != civ_id) return false; @@ -26714,6 +26717,7 @@ district_tile_needs_defense (Tile * tile, int tile_x, int tile_y, struct distric int defender_count = count_units_at (tile_x, tile_y, UF_DEFENDER_VIS_TO_A_OF_CLASS_B, civ_id, 0, -1); int max_defenders = (district_id == AERODROME_DISTRICT_ID || district_id == DISTRIBUTION_HUB_DISTRICT_ID || + (cfg->defense_bonus_percent > 0 && district_id != NEIGHBORHOOD_DISTRICT_ID) || (district_id == WONDER_DISTRICT_ID && is->current_config.completed_wonder_districts_can_be_destroyed)) ? 2 : 1; if (defender_count >= max_defenders) @@ -27078,12 +27082,9 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t if (is_human) return PBV_OK; - // If AI, only okay if moving to a wonder district. If not, they AI will - // sometimes not create roads to connect cities struct district_worker_record * rec = get_tracked_worker_record (this); struct pending_district_request * req = (rec != NULL) ? rec->pending_req : NULL; if ((req != NULL) && - (req->district_id == WONDER_DISTRICT_ID) && (req->target_x == to_x) && (req->target_y == to_y)) return PBV_OK; } From a7c7898e20dac68fe893fcbfdf8d5e8f1ba73384 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 7 Jan 2026 12:16:10 -0800 Subject: [PATCH 148/356] Updated can build building logic --- injected_code.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/injected_code.c b/injected_code.c index 1ef1c990..3eeef81a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -202,6 +202,7 @@ void __fastcall patch_Map_build_trade_network (Map * this); bool __fastcall patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value); bool __fastcall patch_Unit_can_pillage (Unit * this, int edx, int tile_x, int tile_y); bool __fastcall patch_City_has_resource (City * this, int edx, int resource_id); +bool __fastcall patch_Leader_can_build_city_improvement (Leader * this, int edx, int i_improv, bool param_2); bool city_can_build_district (City * city, int district_id); Tile * find_tile_for_district (City * city, int district_id, int * out_x, int * out_y); struct district_instance * get_district_instance (Tile * tile); @@ -2423,10 +2424,9 @@ find_pending_district_request (City * city, int district_id) int stored; if (itable_look_up (&is->city_pending_district_requests[civ_id], key, &stored)) { struct pending_district_request * req = (struct pending_district_request *)(long)stored; - if ((req != NULL) && (req->city_id == city_id) && (req->district_id == district_id)) { + if ((req != NULL) && (req->civ_id == civ_id) && (req->city_id == city_id) && (req->district_id == district_id)) { if (req->city != city) req->city = city; - req->civ_id = civ_id; return req; } } @@ -2566,7 +2566,7 @@ district_instance_set_coords (struct district_instance * inst, int tile_x, int t if (inst == NULL) return; - // Normalize coordinates to map bounds for consistency. + // Normalize coordinates to map bounds for consistency wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); inst->tile_x = tile_x; inst->tile_y = tile_y; @@ -20335,7 +20335,9 @@ patch_Leader_do_production_phase (Leader * this) struct district_config * cfg = &is->district_configs[district_id]; struct district_infos * info = &is->district_infos[district_id]; - if (info->dependent_building_count > 0) continue; + // Carve out an exception for AI to build Central Rail Hub, which is available in Industrial era but Mass Transit, + // the only building dependent on it, is in the Modern era. + if (info->dependent_building_count > 0 && district_id != CENTRAL_RAIL_HUB_DISTRICT_ID) continue; if (cfg->command == -1) continue; int prereq_id = info->advance_prereq_id; From 48200960a892783cca2c39b31fa62f70525daa45 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 7 Jan 2026 15:33:57 -0800 Subject: [PATCH 149/356] Fix crash when user chooses wonder to build --- injected_code.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/injected_code.c b/injected_code.c index 3eeef81a..e332bf48 100644 --- a/injected_code.c +++ b/injected_code.c @@ -9976,7 +9976,6 @@ reserve_wonder_district_for_city (City * city, int wonder_improv_id) if (info->state == WDS_UNDER_CONSTRUCTION) { if (info->city_id == city->Body.ID) { info->city = city; - info->city_id = city->Body.ID; return true; } continue; @@ -25817,7 +25816,7 @@ get_power_station_img_index (int tile_x, int tile_y) void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - *p_debug_mode_bits |= 0xC; + //*p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -26986,18 +26985,12 @@ patch_City_set_production (City * this, int edx, int order_type, int order_id, b if (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { if (reserve_wonder_district_for_city (this, this->Body.Order_ID)) { release_reservation = false; - snprintf (ss, sizeof(ss), "patch_City_set_production: City %s has reserved a wonder district for building wonder %s.\n", - this->Body.CityName, improv->Name); - (*p_OutputDebugStringA) (ss); } } } if (release_reservation) { release_wonder_district_reservation (this); - snprintf (ss, sizeof(ss), "patch_City_set_production: City %s has released its wonder district reservation.\n", - this->Body.CityName); - (*p_OutputDebugStringA) (ss); } } From 94fadf880f4162ca90e63cdfe26939339978164b Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 7 Jan 2026 16:54:48 -0800 Subject: [PATCH 150/356] Fix bug with building natural wonder on group worker move; Fix mine not shown if worker on district bug --- injected_code.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/injected_code.c b/injected_code.c index e332bf48..b1e274c7 100644 --- a/injected_code.c +++ b/injected_code.c @@ -14037,8 +14037,14 @@ bool __fastcall bool is_district_command (int unit_command_value) { - int dummy; - return itable_look_up (&is->command_id_to_district_id, unit_command_value, &dummy); + int district_id; + bool maybe_district_command = itable_look_up (&is->command_id_to_district_id, unit_command_value, &district_id); + + // Natural wonder command IDs are -1 (unbuildable), which can be a false positive + if (district_id == NATURAL_WONDER_DISTRICT_ID) + return false; + + return maybe_district_command; } bool __fastcall @@ -14069,7 +14075,11 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) bool has_district = (tile != NULL) && (tile != p_null_tile) && (get_district_instance (tile) != NULL); if (has_district) { - return Unit_can_perform_command (this, __, unit_command_value); + return base_type == SQ_Hills || + base_type == SQ_Mountains || + base_type == SQ_Desert || + base_type == SQ_Plains || + base_type == SQ_Tundra; } } else if (unit_command_value == UCV_Join_City) { @@ -14467,6 +14477,7 @@ patch_Main_GUI_handle_button_press (Main_GUI * this, int edx, int button_id) // Check if command is a worker build command (not a district) and a district exists on the tile if (is->current_config.enable_districts && p_main_screen_form->Current_Unit != NULL) { + bool removed_existing = false; if (! handle_worker_command_that_may_replace_district (p_main_screen_form->Current_Unit, command, &removed_existing)) return; @@ -14517,6 +14528,7 @@ patch_Main_Screen_Form_issue_command (Main_Screen_Form * this, int edx, int comm Unit * target_unit = unit; if (target_unit == NULL) target_unit = this->Current_Unit; + pop_up_in_game_error("patch_Main_Screen_Form_issue_command"); if (is->current_config.enable_districts) { bool removed_existing = false; @@ -25629,7 +25641,7 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, if (*out_variant == SW || *out_variant == NW) { *out_pixel_x += 10; } else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 10; } - if (*out_variant == SW && (anchor_sprite_index == 7 || anchor_sprite_index == 17)) { *out_pixel_x +=10; *out_pixel_y -= 8; } + if (*out_variant == SW && (anchor_sprite_index == 7 || anchor_sprite_index == 17 || anchor_sprite_index == 16)) { *out_pixel_x +=10; *out_pixel_y -= 8; } } // Sheet 3 else if (anchor_sheet_index == 2) { @@ -26319,7 +26331,7 @@ patch_Unit_select (Unit * this) Tile * tile = tile_at (this->Body.X, this->Body.Y); struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && inst->district_type >= 0 && inst->district_type <= is->district_count && + if (inst != NULL && is_worker(this) && inst->district_type >= 0 && inst->district_type <= is->district_count && ! district_is_complete (tile, inst->district_type)) { int district_id = inst->district_type; PopupForm * popup = get_popup_form (); From 9449689a7cfbee59236a105c664e7597edb3bea8 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 8 Jan 2026 11:38:48 -0800 Subject: [PATCH 151/356] Add expand_water_tile_checks_to_city_work_area flag and refactor patch_City_can_build_improvement to simplify; Push water tile check logic into repl calls to original functions --- .../1200/{PowerStation.PCX => EnergyGrid.PCX} | Bin C3X.h | 7 +- Civ3Conquests.h | 2 +- civ_prog_objects.csv | 9 + default.c3x_config.ini | 1 + default.districts_config.txt | 6 +- injected_code.c | 217 ++++++++---------- 7 files changed, 114 insertions(+), 128 deletions(-) rename Art/Districts/1200/{PowerStation.PCX => EnergyGrid.PCX} (100%) diff --git a/Art/Districts/1200/PowerStation.PCX b/Art/Districts/1200/EnergyGrid.PCX similarity index 100% rename from Art/Districts/1200/PowerStation.PCX rename to Art/Districts/1200/EnergyGrid.PCX diff --git a/C3X.h b/C3X.h index 4cfa8eb9..560f12e9 100644 --- a/C3X.h +++ b/C3X.h @@ -371,6 +371,7 @@ struct c3x_config { int central_rail_hub_distribution_food_bonus_percent; int central_rail_hub_distribution_shield_bonus_percent; + bool expand_water_tile_checks_to_city_work_area; bool workers_can_enter_coast; bool ai_defends_districts; @@ -742,10 +743,10 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, { - .command = UCV_Build_PowerStation, .name = "Power Station", .tooltip = "Build Power Station", .display_name = "Power Station", + .command = UCV_Build_EnergyGrid, .name = "Energy Grid", .tooltip = "Build Energy Grid", .display_name = "Energy Grid", .advance_prereq = "Industrialization", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 4, - .img_paths = {"PowerStation.pcx"}, .dependent_improvements = {"Coal Plant", "Hydro Plant", "Solar Plant", "Nuclear Plant"}, - .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_RIVER)), .custom_height = 84, + .img_paths = {"EnergyGrid.pcx"}, .dependent_improvements = {"Coal Plant", "Hydro Plant", "Solar Plant", "Nuclear Plant"}, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .custom_height = 84, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 2, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 diff --git a/Civ3Conquests.h b/Civ3Conquests.h index 30b443d2..8e2ea1d5 100644 --- a/Civ3Conquests.h +++ b/Civ3Conquests.h @@ -774,7 +774,7 @@ enum Unit_Command_Values UCV_Build_Aerodrome = -10000004, UCV_Build_Port = -10000005, UCV_Build_CentralRailHub = -10000006, - UCV_Build_PowerStation = -10000007, + UCV_Build_EnergyGrid = -10000007, }; enum Unit_Mode_Actions diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 4bfd4d61..9ce66db5 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -857,3 +857,12 @@ ignore, 0x49D070, 0x4A3AF0, 0x49D100, "Advisor_GUI_open", "void (__fastcal ignore, 0x4BF660, 0x4C6C10, 0x4BF6F0, "City_draw_citizens", "void (__fastcall *) (City * this, int edx, PCX_Image * canvas, RECT * rect, char param_3)" ignore, 0x4B9F60, 0x4C15D0, 0x4B9FF0, "City_add_population", "void (__fastcall *) (City * this, int edx, int num, int race_id)" define, 0x4BA230, 0x0, 0x0, "City_remove_population", "void (__fastcall *) (City * this, int edx, int num_pops, int race_id, char param_3)" +define, 0x4AE280, 0x0, 0x0, "City_get_largest_adjacent_sea", "int (__fastcall *) (City * this)" +define, 0x5F39E0, 0x0, 0x0, "Map_impl_has_fresh_water", "bool (__fastcall *) (Map * this, int edx, int tile_x, int tile_y)" +define, 0x5F37F0, 0x0, 0x0, "Map_impl_is_near_river", "bool (__fastcall *) (Map * this, int edx, int x, int y, int num_tiles)" +define, 0x5F38C0, 0x0, 0x0, "Map_impl_is_near_lake", "bool (__fastcall *) (Map * this, int edx, int x, int y, int num_tiles)" +repl call, 0x4C008E, 0x0, 0x0, "City_get_largest_adjacent_sea_within_work_area", "" +repl call, 0x4C010F, 0x0, 0x0, "Map_impl_has_fresh_water_within_work_area", "" +repl call, 0x4C0173, 0x0, 0x0, "Map_impl_is_near_river_within_work_area", "" +repl call, 0x4C01AF, 0x0, 0x0, "Map_impl_is_near_river_within_work_area", "" +repl call, 0x4C01CF, 0x0, 0x0, "Map_impl_is_near_lake_within_work_area", "" diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 66e4de42..4d0dbe6a 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -897,6 +897,7 @@ ai_ideal_distribution_hub_count_per_100_cities = 25 central_rail_hub_distribution_food_bonus_percent = 25 central_rail_hub_distribution_shield_bonus_percent = 25 +expand_water_tile_checks_to_city_work_area = true workers_can_enter_coast = true ; When enabled, AI defensive units will actively seek out and defend districts within their territory, treating them as valuable assets like colonies. diff --git a/default.districts_config.txt b/default.districts_config.txt index e72cad7f..5ee1d876 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -300,9 +300,9 @@ shield_bonus = 4 happiness_bonus = 0 #District -name = Power Station -tooltip = Build Power Station -img_paths = PowerStation.pcx +name = Energy Grid +tooltip = Build Energy Grid +img_paths = EnergyGrid.pcx btn_tile_sheet_row = 1 btn_tile_sheet_column = 7 vary_img_by_era = 1 diff --git a/injected_code.c b/injected_code.c index b1e274c7..6f465960 100644 --- a/injected_code.c +++ b/injected_code.c @@ -73,7 +73,7 @@ struct injected_state * is = ADDR_INJECTED_STATE; #define NATURAL_WONDER_DISTRICT_ID 4 #define PORT_DISTRICT_ID 5 #define CENTRAL_RAIL_HUB_DISTRICT_ID 6 -#define POWER_STATION_DISTRICT_ID 7 +#define ENERGY_GRID_DISTRICT_ID 7 char const * const hotseat_replay_save_path = "Saves\\Auto\\ai-move-replay-before-interturn.SAV"; char const * const hotseat_resume_save_path = "Saves\\Auto\\ai-move-replay-resume.SAV"; @@ -12287,6 +12287,7 @@ patch_init_floating_point () {"naval_units_use_port_districts_not_cities" , false, offsetof (struct c3x_config, naval_units_use_port_districts_not_cities)}, {"show_natural_wonder_name_on_map" , false, offsetof (struct c3x_config, show_natural_wonder_name_on_map)}, {"ai_defends_districts" , false, offsetof (struct c3x_config, ai_defends_districts)}, + {"expand_water_tile_checks_to_city_work_area" , false, offsetof (struct c3x_config, expand_water_tile_checks_to_city_work_area)}, {"workers_can_enter_coast" , false, offsetof (struct c3x_config, workers_can_enter_coast)}, {"enable_city_work_radii_highlights" , false, offsetof (struct c3x_config, enable_city_work_radii_highlights)}, {"introduce_all_human_players_at_start_of_hotseat_game" , false, offsetof (struct c3x_config, introduce_all_human_players_at_start_of_hotseat_game)}, @@ -14079,6 +14080,7 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) base_type == SQ_Mountains || base_type == SQ_Desert || base_type == SQ_Plains || + base_type == SQ_Grassland || base_type == SQ_Tundra; } } @@ -16188,126 +16190,107 @@ patch_City_can_build_unit (City * this, int edx, int unit_type_id, bool exclude_ return base; } +int __fastcall +patch_City_get_largest_adjacent_sea_within_work_area (City * this) +{ + if (is->current_config.enable_districts && is->current_config.expand_water_tile_checks_to_city_work_area) { + int lake_size_threshold = 21; + int largest_size = 0; + int largest_continent_id = -1; + FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { + if (wai.tile->vtable->m35_Check_Is_Water (wai.tile)) { + int continent_id = wai.tile->vtable->m46_Get_ContinentID (wai.tile); + if ((continent_id < 0) || (continent_id >= p_bic_data->Map.Continent_Count)) + continue; + Continent * continent = &p_bic_data->Map.Continents[continent_id]; + if (continent->Body.TileCount <= lake_size_threshold) + continue; + if (p_bic_data->Map.Continents[continent_id].Body.TileCount > largest_size) { + largest_size = p_bic_data->Map.Continents[continent_id].Body.TileCount; + largest_continent_id = continent_id; + } + } + } + return largest_continent_id; + } + return City_get_largest_adjacent_sea (this); +} + bool __fastcall -patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply_strict_rules) +patch_Map_impl_is_near_river_within_work_area (Map * this, int edx, int x, int y, int num_tiles) { - // First defer to the base game's logic - bool base = City_can_build_improvement (this, __, i_improv, apply_strict_rules); - - if (! is->current_config.enable_districts) - return base; - - int required_district_id; - bool district_required = itable_look_up (&is->district_building_prereqs, i_improv, &required_district_id); - Improvement * improv = &p_bic_data->Improvements[i_improv]; - bool is_wonder = is->current_config.enable_wonder_districts && improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder); - bool wonder_requires_district = is_wonder && required_district_id == WONDER_DISTRICT_ID; - - // Ensure prereq tech for the improvement - if (improv->RequiredTechID >= 0 && ! Leader_has_tech (&leaders[this->Body.CivID], __, improv->RequiredTechID)) - return false; - - // Ensure prereq tech for the district - if (district_required) { - int prereq_id = is->district_infos[required_district_id].advance_prereq_id; - if ((prereq_id >= 0) && ! Leader_has_tech (&leaders[this->Body.CivID], __, prereq_id)) { - return false; + if (is->current_config.enable_districts && is->current_config.expand_water_tile_checks_to_city_work_area) { + FOR_WORK_AREA_AROUND (wai, x, y) { + if (wai.tile->vtable->m37_Get_River_Code (wai.tile) != 0) { + return true; + } } + return false; } - // Make sure wonder is not already built - if (is_wonder) { - if ((improv->Characteristics & ITC_Wonder) != 0) { - if (Game_get_wonder_city_id (p_game, __, i_improv) != -1) - return false; - FOR_CITIES_OF (coi, this->Body.CivID) { - City * other_city = coi.city; - if (other_city != this && other_city->Body.Order_Type == COT_Improvement && other_city->Body.Order_ID == i_improv) - return ! apply_strict_rules; + return Map_impl_is_near_river (this, __, x, y, num_tiles); +} + +bool __fastcall +patch_Map_impl_is_near_lake_within_work_area (Map * this, int edx, int x, int y, int num_tiles) +{ + if (is->current_config.enable_districts && is->current_config.expand_water_tile_checks_to_city_work_area) { + int lake_size_threshold = 21; + FOR_WORK_AREA_AROUND (wai, x, y) { + if (wai.tile->vtable->m35_Check_Is_Water (wai.tile)) { + int continent_id = wai.tile->vtable->m46_Get_ContinentID (wai.tile); + if ((continent_id < 0) || (continent_id >= p_bic_data->Map.Continent_Count)) + continue; + Continent * continent = &p_bic_data->Map.Continents[continent_id]; + if (continent->Body.TileCount <= lake_size_threshold) + return true; } - } else { - Leader * leader = &leaders[this->Body.CivID]; - if ((leader->Small_Wonders != NULL) && (leader->Small_Wonders[i_improv] != -1)) - return false; } + return false; } - // Check if district is actually needed or city already has the improvement - if (! district_required || patch_City_has_improvement (this, __, i_improv, false)) { - if (! is_wonder) - return base; + return Map_impl_is_near_lake (this, __, x, y, num_tiles); +} - // If a Wonder and doesn't need a district, do non-strict check only to avoid inadvertently - // blocking Wonders that are buildable but grayed out in UI because already being built - return City_can_build_improvement (this, __, i_improv, false); +bool __fastcall +patch_Map_impl_has_fresh_water_within_work_area (Map * this, int edx, int tile_x, int tile_y) +{ + if (is->current_config.enable_districts && is->current_config.expand_water_tile_checks_to_city_work_area) { + if (patch_Map_impl_is_near_river_within_work_area (this, __, tile_x, tile_y, 1)) + return true; + if (patch_Map_impl_is_near_lake_within_work_area (this, __, tile_x, tile_y, 1)) + return true; + return false; } - if (is_wonder && ! wonder_requires_district) { - return true; - } + return Map_impl_has_fresh_water (this, __, tile_x, tile_y); +} - // Different logic for human vs AI players - bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; - // If disallowed by the base game, check if it was due to not being next to water, which can be - // circumvented with certain districts - if (! base) { +bool __fastcall +patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply_strict_rules) +{ + // First defer to the base game's logic + bool base = City_can_build_improvement (this, __, i_improv, apply_strict_rules); + if (! base) return false; + if (! is->current_config.enable_districts) return base; - // Allow harbor-like improvements when port districts replace coastal adjacency - if (improv->ImprovementFlags & ITF_Allows_Water_Trade && - is->current_config.enable_port_districts) { - int tx, ty; + // Different logic for human vs AI players + bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; - // Different checks for human vs AI. For human, look for any water. For AI, look for a tile suitable for a port district. - // The port district check is stricter and prevents the AI from trying to build harbors in small lakes, etc. - if (! is_human && find_tile_for_district (this, PORT_DISTRICT_ID, &tx, &ty) == NULL) - return false; - else if (is_human) { - bool has_coastal_tile = false; - FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { - if (wai.tile->vtable->m35_Check_Is_Water (wai.tile)) { - has_coastal_tile = true; - break; - } - } - if (! has_coastal_tile) - return false; - } + // Check if the improvement requires a district and output the required district id when it does + int required_district_id; + bool needs_district = city_requires_district_for_improvement (this, i_improv, &required_district_id); + if (! needs_district) + return true; - // Allow improvements that must be near water if water in work radius. - // ITF_*_Water is Nuclear Power Plant, Commercial Dock, etc. Characteristics & 1 is Coastal Fortress, coastal Wonders, etc. - } else if (improv->ImprovementFlags & ITF_Must_Be_Near_Water || - improv->ImprovementFlags & ITF_Increases_Trade_In_Water || - improv->ImprovementFlags & ITF_Increases_Shields_In_Water || - improv->Characteristics & 1) { - bool has_water_in_radius = false; - FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { - if (wai.tile->vtable->m35_Check_Is_Water (wai.tile)) { - has_water_in_radius = true; - break; - } - } - if (! has_water_in_radius) - return false; + // Ensure prereq tech for the district + int prereq_id = is->district_infos[required_district_id].advance_prereq_id; + if ((prereq_id >= 0) && ! Leader_has_tech (&leaders[this->Body.CivID], __, prereq_id)) + return false; - // Allow improvements that must be near river if river in work radius - } else if (improv->ImprovementFlags & ITF_Must_Be_Near_River) { - bool has_river_in_radius = false; - FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { - if (wai.tile->vtable->m37_Get_River_Code (wai.tile)) { - has_river_in_radius = true; - break; - } - } - if (! has_river_in_radius) - return false; - } else if (is_wonder && City_can_build_improvement (this, __, i_improv, false)) { - // Do nothing, game is only disallowing because the city is already building the Wonder - // (i.e., in UI, the Wonder is grayed out because it's already being built) - } else { - return base; - } - } + Improvement * improv = &p_bic_data->Improvements[i_improv]; + bool is_wonder = is->current_config.enable_wonder_districts && improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder); // Check that we have the necessary terrain bool has_terrain_for_district = false; @@ -16333,15 +16316,7 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply // If human has everything needed except a built district (which is buildable), allow relaxed checks so UI can gray entry out if (is_human) { - if (is_wonder) { - return city_has_wonder_district_with_no_completed_wonder (this, i_improv) - ? true - : ! apply_strict_rules; - } else { - return city_has_required_district (this, required_district_id) - ? true - : ! apply_strict_rules; - } + return ! apply_strict_rules; } // If AI already has a pending district request for this required district, return false @@ -25791,17 +25766,17 @@ wonder_allows_river (struct wonder_district_config const * cfg) } int -get_power_station_img_index (int tile_x, int tile_y) +get_energy_grid_img_index (int tile_x, int tile_y) { - struct district_infos * info = &is->district_infos[POWER_STATION_DISTRICT_ID]; + struct district_infos * info = &is->district_infos[ENERGY_GRID_DISTRICT_ID]; int coal_plant_id = info->dependent_building_ids[0]; int hydro_plant_id = info->dependent_building_ids[1]; int solar_plant_id = info->dependent_building_ids[2]; int nuclear_plant_id = info->dependent_building_ids[3]; - bool coal = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, POWER_STATION_DISTRICT_ID, coal_plant_id); - bool hydro = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, POWER_STATION_DISTRICT_ID, hydro_plant_id); - bool solar = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, POWER_STATION_DISTRICT_ID, solar_plant_id); - bool nuclear = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, POWER_STATION_DISTRICT_ID, nuclear_plant_id); + bool coal = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, ENERGY_GRID_DISTRICT_ID, coal_plant_id); + bool hydro = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, ENERGY_GRID_DISTRICT_ID, hydro_plant_id); + bool solar = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, ENERGY_GRID_DISTRICT_ID, solar_plant_id); + bool nuclear = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, ENERGY_GRID_DISTRICT_ID, nuclear_plant_id); int index = 0; @@ -25966,11 +25941,11 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } break; } - case POWER_STATION_DISTRICT_ID: + case ENERGY_GRID_DISTRICT_ID: { - // Power stations can have multiple buildings that - unlike most district buildings - don't have each other as prereq buildings, + // Energy grids can have multiple buildings that - unlike most district buildings - don't have each other as prereq buildings, // and thus have a combinatorial number of images based on which power plants are built in their radius. This returns the correct image index - buildings = get_power_station_img_index (tile_x, tile_y); + buildings = get_energy_grid_img_index (tile_x, tile_y); break; } default: From 9cfbbb62d09e0b9fed67b19e6cb15c1960c32f26 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 8 Jan 2026 14:13:40 -0800 Subject: [PATCH 152/356] Update port art to have middle ages and ancient be the same --- Art/Districts/1200/Port_NE.PCX | Bin 21207 -> 20837 bytes Art/Districts/1200/Port_NW.PCX | Bin 20801 -> 21246 bytes Art/Districts/1200/Port_SE.PCX | Bin 20975 -> 20756 bytes Art/Districts/1200/Port_SW.PCX | Bin 21722 -> 21565 bytes injected_code.c | 2 ++ 5 files changed, 2 insertions(+) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index be0db0fb14cdcb582ffb9dc92221415cdc4a1c60..e5c40db90f67665f94bcfffc1c1ef32283d9b893 100644 GIT binary patch delta 52 zcmcb`qXnb{Bh`ws*MCnvDz!PuK8vm9Zce85<5@+^Oz%@-uuEdj#m B8bAO5 delta 2812 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 V_zVHhAqaZfYe+8HV+)>W$6v-s@ksyx delta 3118 zcmaJ@Z){W76_+2yPL}8A*%^C@V>@8-8WPvBP5$863C*K}70{NYlVDvY)=HDc{$y*V zN}JR_#FU#4P}-u^1C>` zf^wYGEG@kI7vPi_mM!}(ax+15-`LUoDbJzF7|I1c$Cq(DigJXT2Aw#*OMOsOji8+4 zAMq8O9!7bdE8&LDlKte8<*W7&_@9*aIQ>t3$DW_BkR{6&`&l0Eari^+b?weC6^LC@ z?Pqv*H8Vp>#JA@cdGd@@X=hvtMf_#1(gUTnOXOQpr9ru==&erG+Lhw~!*H)Cr}&5X z9ff6-BM@RHa~yi*`AkulFUk)wK!9-&t1d!~sd(M4@bGn|7wziHt_%~J5O(BAp@NXJsP)>rvVil2KLpkwn zavN7cGRzvyR??A#X`G&g8$~(FFW`3$uJIXYZw+)5z@Bi#wDN7`LoijG5wXp|1q2mf z5hl^D(H*Y(URRZr2gQ+4S4WA!h*Y!#VzM zFr9w6+$&e@4jwsb^~pVl-d%)PVle6{>-3RNHdXxS4w|s6U1q^O;kFW=jk!DH@;uN2 zO-U@|iTj;Z)g$N<-~kA?48Tz42aVk zzt$7SB)zX&j#pK`?%a{aEDFFyChsFk0%|uj>x=XaU&lXN?NU+%H0UK6TEx%0#YkkebdAA7>sE0{BR3eR5Y^sc=XQ9Yi*D!PDZ(*naI>fs*0tR6UG96+}vVfM%ci|FVI zJ}*LE#$x?3{EdPiDUmh?JgtrjTBz?e8?}Sx5Z3THM7kHN0U}^Jk!^$8%l1}Vo8}Gd z@;-9O68aq{hrePsn9ZtM8_=uX=xFTl9Hs@+A&8eq;_HF1PS8AJkzu*MNE~x#E7>2g zJ=(m0K1boR=Vc0CKam1|ncCsBIuX5Q*_k-#LyhY8Au)L2> zKY!J3;ET&-k&%F0ugXr@rAi_D0L}Ku3_dQxfM_lo`eThApuZISwKfn9`%10 zeFh@m+!bqV)19W?ZqQ}Z!};Uq039UZ)1sW_w~;j-(;@q8zJOO#y2IZIITD8MPU6ci z3i#vZk{o*vVZz}ypT&oy;bqzF)Z(nZY_HGM9a=Jn=pV#Ro$Kg6)#mlRp`WzT94iP|fS>!qyz#Js=u?E*3clZS{5l-RLtuIKd0XNR%^k8Q z%;RjCqRHr!CNHk*e-`*vaLayV4(3?3sSgH4vzzlJ?Q&Y*>E zS^z%tuzF29%WvYs8I*ZmK=(Aya@}~xd&FtyHY(=%HB{e0S>X5hsAw?Fynb#LMbyH2vJ{w*9~he|%F|09luVTGIt{PrnvJISlh{2m8MAhkh5;y}&@e_1L> zouhQvVCVnnuOCY?i|?qKh2&Ny8r+H diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index d01207c23483a9b971799de705f092700c20c856..15a83233461790736c67c812ce2d2efa0392ac29 100644 GIT binary patch delta 47 zcmaFAm~qM?#tkaG8#mqIn0!%8V)7DBdG%PVOf^M*fq1ci!-dL`_KY)(QfF&oNU1s zufPTj%GEZ$>`k5bFu*{DG&3-uFkCEq*z3JY>^SjHnmBfwv~CmsC9bVlR+n`tcJ2=4 zWVJKDd?dy1oO5~4_noUhzE}0|Le&QceB?1);~Xa8Zx1RrU%^#;e1!Nld<8Rje-+4T z<>pJ6#>Xwhelp}qGjBN_L2yN-3#hWE#9>r~$Pm|(N|>X{RhTePFU90MUMvy2eR zFwK-1jK-5@$L;{k{t>Jp&LMt*^>#+|SP9*0gpV;%YIs{U-*SGzig&QLLaarv305Dk zJ~#$L`D~u~Wq}2w5jsKC-fpkz+m7%X-`|elFbhSjaRc!I*4Q0mirqb;q|_K!hXoJa zLbB_qM~GJumofdW07VKgsm?lMr)iOl;F>0)%X||KIC4}C#`li_EzyagR3a4V7B04T zhf>=CzwZ|{sk^4H>3fWa-4a*eG2$5FBKG>(p!Q6wE-_!$rt6fSUE@XY_c0;-W3d?v zU`;X{N&7>DmK)DHtuMLk*Da|@^Dx!w$4}{fXVewzB>+T*co%E5J`LNBl7BZ4IuncF z5GXo_ihLb1wl5ZLIMOu+qsX*d{jFV**JwFvxXe?Q8fw@2y}{}MuO^*-_5CHxjQ%h1 z|6q**{|seoSUcmoHAnGfM_3Wp%js5$uK}tv)M7T*UWQR4spzVZXpdd|7|SsBNgFFiK1*Sf$;P&-3a9` zFexptr08vy-hKr)NAN7+kmCF%&Jh>q;U+MC(*CJ!8cFkqrY}qdTKA)81Hb-5Z!F0lg^M}nW7OOZ+^!cuPFK+P7}`O(eEzaf)C*mw=Xcq z*1f81$C;lv0g9fz!aoOD$kpk=_(94G&%B@Haz1Afx^a#<8P$9IC(H=_eOu0pXmrl+ zpdVel2{SOx&lAU{8%|&K2`AvA8^q~wJGcOu>_{q7b3z-z`n%sxbGx%|*gi)m*+jnn zN4~U2sYhR;oR`stoPUN!aq&Z#!UL6-9kyYA*9o{9?xSVSr}%Bxe&Yg*MJzo)!u9O` zSl2n4xybO;WI3BjAw|;Z>=Q|^piekoL@~QK1K038rX^PFLl2(je3sAQAxKMkAu}w; za31cLzV#?9AZnR9@G?K?iBJ8ikI_MRF;cVIKF0Zak!Xro|f!h&gb|n zu3mQBp)VLm#Flu=5zPJ#33jeevcmPh4jfwIm9v8u za54W=mn*LOgD>w>Ox$D)!Xm_WvMD>IdJRkS#8BcC6~3qclRl$FS(z!E)a}) zA%`gq)vh_#VrEzO5WqSg$8Y(XU#?&QC|=PMC@_|%q@|uTZFz8^bd0$IX{1;b3>Ma4}nRvg+a+b^mDpFWdlAUH||9 diff --git a/Art/Districts/1200/Port_SW.PCX b/Art/Districts/1200/Port_SW.PCX index 73dd62c5b1c78ca550ab316d009b6b692d0247f0..02ccf89fbdca34fdfb70f2af3c79409ae35c00c2 100644 GIT binary patch delta 35 tcmV+;0NnrDsR6yH0kF*sv7Vs_lkyc9lT`^LlQ9+;vyKTR1G9q}S34B24R-(l delta 2532 zcmai0-ESMm5l61Tkt&aOI11@eN9qL8IyjQRuZX5jq$KOyDsk*KhWd~e5MVS&fw~EV z6m5V$6qjXz`vX+urGNkdg&-hBV6EV*B^*9S+=d6Rz|TP6EEy;ei#a6@*S;b75D15}DCE#Yd;nVn zmiQ@PrCdBpbL90_%E*c*d-t8ZaS{#;8x2I|m#ygmsmb1p8-)_Mz zBwIne4L9-4Q&yXku&76?WHvsaM5)*FrrT$Xa5(=YEkX6aKuBj3p-9V-mi_mkj+9G? zH{k|c;Z~nK&D=p`wEL94k@!!jm+eRuVnTq!+_&l%z?JO*RTIbVK@&L_5O2VBxXNjB zb~7_#CO{5U*pgZ*a$T zm}^_3l8_E|n*BW?jinC6PmH~RrAznHlSc+s=yK(DJ(AgX7ak(%8N_RF6>f5y)@)j( zo~yJ=p?v{OiVY`^pAZdc;-z=s|LCirj58tj3o%s(Kvsr$6|P`@X)_(shlrLjN5oe1 zP-ajx0!%+vl(ru!ckAAVf*nrBItNGYAX^zHgpXIS>l*ymDhrA%Qxsba)6kNUkzl07 zj0Qyt?I-RbXNdBHyKKl~MrFWWjAoOm)>}wh!SUeZCG5(3ob~kUsX&L3P1~l_W9)A! ziY61K<5r{zclk{0#mti}${-N8%M1iZK10T{*!n)M!AG#g$>E{a*uIQie09>bh`{n< zFeSb6V_nGI#gGPV6?Z++qzqw4DVVLDts6*r9y{B|k6;zHdFLyNdDQ(4tJ5+z#5s3L zC?-WyJ^uY*riLCT^!He5Fm{+=5Z_|TKvo=C>S*2nDRN)HruA_ZKEPE$-kKwqusT=` zo1~xzkG#gr=-(;71)E$@d&N{QGsL3yCu#UucW-mDV^2X)gt%_Tj;tf`D)yd_AHW4% zPjS{1?BVfQdzzkO)ya;R!vg`Wqs%ujj353oa8MgjMKzM|o++r#QI&donlmm7rM5&Q zv;Pz1T*G$p@dA)Ve5i82ym1KC84hYzvY8asqy$|)&DZfIsrR}xCiQ5NDn&HK4$sos zgmmgjdl)Y`C#`tsm~k1YuVA(NxQHW&^978B&gWcWOUXoJj$a0{$;Y%rx?SoqvoH1+ z`tZbEdx+4c8?SuLxXjVRionwGVnU5|tRdSqtW6)!<1NVf5_S}AFweHc1%46Tj=PFv z1dQHxQy$O!G_5$q*5|)6LPE4BlR{xuPUD!`Cw=FStH^T$3&_WLyeK(eK_{f_B46?I z@Uo<mnbL z@`l(QIq{qpXgA0o?3n=O++*m@DyMgH{$1(V14PevwH6Pl-Ag+={bSn1Pk{=q9$!Fj zrqKgDB%?iFR7^b`cHC*!#Cx$pJ@R|onG)>MxKxaF{7!v_Vv782ewsX9fz!Bd@i`>X ziIiYcfEi2^HOWp3w2n7@-F@@p9U^pv*sf#QmL4>VMj%thQ2f1qI9vQQlyTk0kB%m{ zXY8bzPyQ?!j?wDaOxR@}#gH!f$94y=d0IbM!ZtK@J9IdkbWWoi{?@$j%Qi3LbmsVD zIBLqSoe1i(onZ=bRoYzVY?69qXZVHZlKI}`8)jIlyfyK3+s{3I5{Eslx#KOzTs2H< z#2xyS=cdR>`{ZoN($y3*d*4S+pBbI`&-%#cd+3_WYW5FY6{mzZPQB|pLDR~s2ZpnD tp|fvHuDrGQw3hWy&g$csmx(XFk%0f5N5hX3Q$I-?_SnOB+rHN|{BMRcbshi! diff --git a/injected_code.c b/injected_code.c index 6f465960..c6f1996e 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16281,6 +16281,8 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply // Check if the improvement requires a district and output the required district id when it does int required_district_id; bool needs_district = city_requires_district_for_improvement (this, i_improv, &required_district_id); + + // District is either not needed or already built if (! needs_district) return true; From 422c8b6622c6f7227aac67fd8a4abdea8d6c7b20 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 8 Jan 2026 15:35:31 -0800 Subject: [PATCH 153/356] Enable pillaging for naval units --- injected_code.c | 66 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/injected_code.c b/injected_code.c index c6f1996e..5a524905 100644 --- a/injected_code.c +++ b/injected_code.c @@ -13714,6 +13714,32 @@ patch_Main_GUI_set_up_unit_command_buttons (Main_GUI * this) if (is->current_config.enable_districts) { set_up_district_buttons (this); + + if (p_main_screen_form->Current_Unit != NULL) { + Unit_Body * selected_unit = &p_main_screen_form->Current_Unit->Body; + + enum UnitTypeClasses class = p_bic_data->UnitTypes[selected_unit->UnitTypeID].Unit_Class; + Tile * tile = tile_at (selected_unit->X, selected_unit->Y); + struct district_instance * existing_inst = get_district_instance (tile); + if (class == UTC_Sea && tile->vtable->m35_Check_Is_Water (tile) && existing_inst != NULL) { + + // Really hate this but can't figure out a cleaner way to do it. + // If looping through command buttons, the command ID is always zero if disallowed + Command_Button * pillage_button = &this->Unit_Command_Buttons[3]; + // Special actions sprites: UCV_Pillage is index 3 (0x10000008). + Sprite * base_img = &this->Images[0x80 + 3]; + pillage_button->Command = UCV_Pillage; + pillage_button->field_6D8 = 0x10; + pillage_button->Button.vtable->m02_Show_Disabled ((Base_Form *)&pillage_button->Button); + pillage_button->Button.Images[0] = base_img - 0x3b; + pillage_button->Button.Images[1] = base_img; + pillage_button->Button.Images[2] = base_img + 0x3b; + pillage_button->Button.Images[3] = &this->Images[2]; + pillage_button->Button.field_664 = 0; + pillage_button->Button.field_5FC[13] = 0; + pillage_button->Button.vtable->m01_Show_Enabled ((Base_Form *)&pillage_button->Button, __, 0); + } + } } // If the minimum city separation is increased, then gray out the found city button if we're too close to another city. @@ -14083,8 +14109,9 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) base_type == SQ_Grassland || base_type == SQ_Tundra; } - } - else if (unit_command_value == UCV_Join_City) { + } else if (unit_command_value == UCV_Pillage) { + return patch_Unit_can_pillage (this, __, this->Body.X, this->Body.Y); + } else if (unit_command_value == UCV_Join_City) { bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; if (!is_human) { int type_id = this->Body.UnitTypeID; @@ -14112,11 +14139,6 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) return ((class != UTC_Land) || (! tile->vtable->m35_Check_Is_Water (tile))) && Unit_can_perform_command (this, __, unit_command_value); } - - if ((unit_command_value == UCV_Pillage) && - ! patch_Unit_can_pillage (this, __, this->Body.X, this->Body.Y)) { - return false; - } return Unit_can_perform_command (this, __, unit_command_value); } @@ -14125,40 +14147,37 @@ bool __fastcall patch_Unit_can_pillage (Unit * this, int edx, int tile_x, int tile_y) { bool base = Unit_can_pillage (this, __, tile_x, tile_y); - if (! base) - return false; if (! is->current_config.enable_districts || ! is->current_config.enable_wonder_districts || is->current_config.completed_wonder_districts_can_be_destroyed) - return true; + return base; Tile * tile = tile_at (tile_x, tile_y); if ((tile == NULL) || (tile == p_null_tile)) - return true; + return base; struct district_instance * inst = get_district_instance (tile); if (inst == NULL) - return true; + return base; + int district_id = inst->district_type; if (is->current_config.enable_natural_wonders && - (inst->district_type == NATURAL_WONDER_DISTRICT_ID) && + (district_id == NATURAL_WONDER_DISTRICT_ID) && (inst->natural_wonder_info.natural_wonder_id >= 0)) return false; - int district_id = inst->district_type; - if (district_id != WONDER_DISTRICT_ID) - return true; - if (! district_is_complete (tile, district_id)) return true; - // Check if this wonder district has a completed wonder on it - struct wonder_district_info * info = get_wonder_district_info (tile); - if (info == NULL || info->state != WDS_COMPLETED) + if (district_id == WONDER_DISTRICT_ID) { + struct wonder_district_info * info = get_wonder_district_info (tile); + if (info == NULL || info->state != WDS_COMPLETED) + return true; + return is->current_config.completed_wonder_districts_can_be_destroyed; + } else { return true; - - return false; + } } bool __fastcall @@ -21054,7 +21073,8 @@ patch_Unit_attack_tile (Unit * this, int edx, int x, int y, int bombarding) // If the district existed before but not after, or the tile no longer has a mine, the district was destroyed if (!has_district_after || !(target_tile->vtable->m42_Get_Overlays (target_tile, __, 0) & TILE_FLAG_MINE)) { - handle_district_destroyed_by_attack (target_tile, tile_x, tile_y, true); + bool is_water_tile = target_tile != NULL && target_tile->vtable->m35_Check_Is_Water (target_tile); + handle_district_destroyed_by_attack (target_tile, tile_x, tile_y, ! is_water_tile); } } From ba0f3f60a00e10700b0a5afb497162a41767fb83 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 8 Jan 2026 16:14:14 -0800 Subject: [PATCH 154/356] Intercept key event and show distrit-specific confirmation popup for replacing districts with improvements --- injected_code.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/injected_code.c b/injected_code.c index 5a524905..16c58dc2 100644 --- a/injected_code.c +++ b/injected_code.c @@ -9730,8 +9730,8 @@ handle_district_removed (Tile * tile, int district_id, int center_x, int center_ if (leave_ruins && (tile->vtable->m60_Set_Ruins != NULL)) { tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, center_x, center_y); tile->vtable->m60_Set_Ruins (tile, __, 1); - p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); } + p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); } bool @@ -14549,7 +14549,6 @@ patch_Main_Screen_Form_issue_command (Main_Screen_Form * this, int edx, int comm Unit * target_unit = unit; if (target_unit == NULL) target_unit = this->Current_Unit; - pop_up_in_game_error("patch_Main_Screen_Form_issue_command"); if (is->current_config.enable_districts) { bool removed_existing = false; @@ -17629,6 +17628,22 @@ patch_Main_Screen_Form_m82_handle_key_event (Main_Screen_Form * this, int edx, i } } this->vtable->m73_call_m22_Draw ((Base_Form *)this); // Trigger map redraw + + // Worker keyboard shortcuts that would normally go to patch_Main_Screen_Form_issue_command + // unfortunately get short-circuited if a district is on a tile and the default popup to replace a "mine" is shown. + // The only way to catch these beforehand I've found it is to intercept the key event here. + } else if (is->current_config.enable_districts && + p_main_screen_form->Current_Unit != NULL && + is_worker (p_main_screen_form->Current_Unit)) { + int command = -1; + bool removed_existing = false; + if (virtual_key_code == VK_M && is_command_button_active (&this->GUI, UCV_Build_Mine)) command = UCV_Build_Mine; + else if (virtual_key_code == VK_I && is_command_button_active (&this->GUI, UCV_Irrigate)) command = UCV_Irrigate; + else if (virtual_key_code == VK_N && is_command_button_active (&this->GUI, UCV_Plant_Forest)) command = UCV_Plant_Forest; + + if (handle_worker_command_that_may_replace_district (p_main_screen_form->Current_Unit, command, &removed_existing)) { + Main_Screen_Form_issue_command (this, __, command, p_main_screen_form->Current_Unit); + } } Main_Screen_Form_m82_handle_key_event (this, __, virtual_key_code, is_down); From ccf3eef663f1241dd7b552a576e6a1c5fbcae353 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 8 Jan 2026 17:05:09 -0800 Subject: [PATCH 155/356] Update port defaults; Fix naval pillaging bug --- default.c3x_config.ini | 8 ++++---- default.districts_config.txt | 2 +- injected_code.c | 15 +++++++-------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 4d0dbe6a..94bc1fe8 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -882,16 +882,16 @@ destroyed_wonders_can_be_built_again = true ; is set to "flat" (e.g., 25 means the AI aims for 1 hub per 4 cities). ; ; distribution_hub_yield_division_mode controls how a hub splits its collected food/shields across connected cities: -; flat: Divide raw yields by the configured divisors (distribution_hub_food_yield_divisor / distribution_hub_shield_yield_divisor) -; scale-by-city-count: More cities plugged into the hub, the thinner the bonus decays (floor(sqrt(connected city count)) * divisor) +; flat: Divide raw yields by the configured divisors (distribution_hub_food_yield_divisor & distribution_hub_shield_yield_divisor) +; scale-by-city-count: Bonuses gradually reduce as more cities plugged into the hub (formula: floor(sqrt(connected_city_count)) * divisor) ; ; ai_distribution_hub_build_strategy controls how the AI decides to build distribution hubs: ; by-city-count: AI builds hubs based on its ideal hub count per 100 cities ; auto: AI dynamically assesses need based on city growth and food/shield deficits, capped at max 1 hub for every 2 cities distribution_hub_yield_division_mode = scale-by-city-count ai_distribution_hub_build_strategy = auto -distribution_hub_food_yield_divisor = 1 -distribution_hub_shield_yield_divisor = 1 +distribution_hub_food_yield_divisor = 2 +distribution_hub_shield_yield_divisor = 2 ai_ideal_distribution_hub_count_per_100_cities = 25 central_rail_hub_distribution_food_bonus_percent = 25 diff --git a/default.districts_config.txt b/default.districts_config.txt index 5ee1d876..cc28e9d1 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -273,7 +273,7 @@ defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 0 science_bonus = 0 -food_bonus = 2 +food_bonus = 0 gold_bonus = 2 shield_bonus = 0 happiness_bonus = 0 diff --git a/injected_code.c b/injected_code.c index 16c58dc2..a6284883 100644 --- a/injected_code.c +++ b/injected_code.c @@ -4623,7 +4623,7 @@ recompute_distribution_hub_yields (struct distribution_hub_record * rec) rec->raw_food_yield = food_sum; rec->raw_shield_yield = shield_sum; - int food_div = is->current_config.distribution_hub_food_yield_divisor; + int food_div = is->current_config.distribution_hub_food_yield_divisor; int shield_div = is->current_config.distribution_hub_shield_yield_divisor; if (food_div <= 0) food_div = 1; @@ -4645,16 +4645,14 @@ recompute_distribution_hub_yields (struct distribution_hub_record * rec) int city_root = 1; while ((city_root + 1) * (city_root + 1) <= connected_city_count) city_root++; - int city_food_divisor = city_root * food_div; + int city_food_divisor = city_root * food_div; int city_shield_divisor = city_root * shield_div; - if (city_food_divisor < 1) - city_food_divisor = 1; - if (city_shield_divisor < 1) - city_shield_divisor = 1; + if (city_food_divisor < 1) city_food_divisor = 1; + if (city_shield_divisor < 1) city_shield_divisor = 1; rec->food_yield = food_sum / city_food_divisor; rec->shield_yield = shield_sum / city_shield_divisor; } else { - rec->food_yield = food_sum / food_div; + rec->food_yield = food_sum / food_div; rec->shield_yield = shield_sum / shield_div; } } @@ -13721,7 +13719,8 @@ patch_Main_GUI_set_up_unit_command_buttons (Main_GUI * this) enum UnitTypeClasses class = p_bic_data->UnitTypes[selected_unit->UnitTypeID].Unit_Class; Tile * tile = tile_at (selected_unit->X, selected_unit->Y); struct district_instance * existing_inst = get_district_instance (tile); - if (class == UTC_Sea && tile->vtable->m35_Check_Is_Water (tile) && existing_inst != NULL) { + if (class == UTC_Sea && tile->vtable->m35_Check_Is_Water (tile) && existing_inst != NULL && + patch_Unit_can_perform_command (p_main_screen_form->Current_Unit, __, UCV_Pillage)) { // Really hate this but can't figure out a cleaner way to do it. // If looping through command buttons, the command ID is always zero if disallowed From 2ca314c9f2ec2c67fedb3db27640dea13746a5ad Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 9 Jan 2026 16:43:32 -0800 Subject: [PATCH 156/356] Refactor districts check to build improvements --- Art/Districts/1200/Wonders_3.PCX | Bin 13169 -> 13421 bytes injected_code.c | 47 +++++++++++++++++-------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/Art/Districts/1200/Wonders_3.PCX b/Art/Districts/1200/Wonders_3.PCX index ba6eb67334af749a77a1eb1390cdec119545d53d..e796b9dfaafd4103fabcdc2d052c583f701303d1 100644 GIT binary patch delta 431 zcmX9)ze~el5XB@-np(B$KVg+(JGgXFa4RlO4&vaTC<=8DCr4)oZx99l2f@GKVwbuU zjGFIjA=rGzLC_+&$9L&++>dwf-n-xZ?}PigHUFCN2dAt#W_7~@B!*V3tx7oV$B&vL zG$XpSnzNj-3N)hO9u|zEu2r3T&hjeZ7_*2KYx=WVUPl2EDq6Ldg)PJ&29ppdIotgkl zAdHHKh!OdP-AP~-B&H(xs8AC%DUWigVmPGR!k!O^Y^D0jT`EIHC$}SJYVM&gT5%=O zbnP}uZ`t@M7(Th}DHH>o8$nm9sOdG!cu|6mP)2m-9s5W`kLi=~)*W$33&m48Do;!M bbXVM%F!WWtfE@r6rncV`;P7Fs)TsUg&O#Y? delta 180 zcmaEx@iA?K0Tb)3|1%H&pX|&O&UEMhoj1MP&WzuB2{eQ9nvlrvN$&JkVAkG2i zKp;nfMT_ay|H%<7(TsN|?_x0kaelGH0y%N4x**PM)ge{huttS;BaK@current_config.enable_districts) return base; - // Different logic for human vs AI players - bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; + bool is_human = (*p_human_player_bits & (1 << city->Body.CivID)) != 0; // Check if the improvement requires a district and output the required district id when it does int required_district_id; - bool needs_district = city_requires_district_for_improvement (this, i_improv, &required_district_id); + bool needs_district = city_requires_district_for_improvement (city, i_improv, &required_district_id); // District is either not needed or already built if (! needs_district) @@ -16305,7 +16299,7 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply // Ensure prereq tech for the district int prereq_id = is->district_infos[required_district_id].advance_prereq_id; - if ((prereq_id >= 0) && ! Leader_has_tech (&leaders[this->Body.CivID], __, prereq_id)) + if ((prereq_id >= 0) && ! Leader_has_tech (&leaders[city->Body.CivID], __, prereq_id)) return false; Improvement * improv = &p_bic_data->Improvements[i_improv]; @@ -16314,9 +16308,9 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply // Check that we have the necessary terrain bool has_terrain_for_district = false; bool has_terrain_for_wonder = false; - FOR_WORK_AREA_AROUND (wai, this->Body.X, this->Body.Y) { + FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { Tile * tile = wai.tile; - if (! tile_suitable_for_district (tile, required_district_id, this, NULL)) + if (! tile_suitable_for_district (tile, required_district_id, city, NULL)) continue; has_terrain_for_district = true; @@ -16340,7 +16334,7 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply // If AI already has a pending district request for this required district, return false // to prevent wasting a turn trying to choose this improvement - if (find_pending_district_request (this, required_district_id) != NULL) { + if (find_pending_district_request (city, required_district_id) != NULL) { return false; } @@ -16350,6 +16344,18 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply return true; } + +bool __fastcall +patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply_strict_rules) +{ + // First defer to the base game's logic + bool base = City_can_build_improvement (this, __, i_improv, apply_strict_rules); + if (! base) return false; + if (! is->current_config.enable_districts) return base; + + return city_meets_district_prereqs_to_build_improvement (this, i_improv, apply_strict_rules); +} + bool ai_handle_district_production_requirements (City * city, City_Order * out) { @@ -24867,7 +24873,7 @@ patch_City_add_building_if_done (City * this) if (is->current_config.enable_districts && order_type == COT_Improvement) { Improvement * new_improv = &p_bic_data->Improvements[order_id]; // Improvement might have changed if (new_improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) { - if (! patch_City_can_build_improvement (this, __, order_id, true)) { + if (! (City_can_build_improvement (this, __, order_id, false) && city_meets_district_prereqs_to_build_improvement (this, order_id, true))) { char ss[256]; snprintf (ss, sizeof ss, "patch_City_add_building_if_done: City '%s' cannot complete building of wonder '%s'; switching to defensive unit.\n", this->Body.CityName, @@ -25802,7 +25808,7 @@ wonder_allows_river (struct wonder_district_config const * cfg) } int -get_energy_grid_img_index (int tile_x, int tile_y) +get_energy_grid_image_index (int tile_x, int tile_y) { struct district_infos * info = &is->district_infos[ENERGY_GRID_DISTRICT_ID]; int coal_plant_id = info->dependent_building_ids[0]; @@ -25831,7 +25837,6 @@ get_energy_grid_img_index (int tile_x, int tile_y) else if (coal && ! hydro && ! solar && nuclear) index = 12; // Coal, Nuclear else if (coal && ! hydro && solar && nuclear) index = 13; // Coal, Solar, Nuclear else if (coal && ! hydro && solar && ! nuclear) index = 14; // Coal, Solar - else index = 0; return index; } @@ -25896,8 +25901,8 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par // Handle river alignment, if district allows it enum direction river_dir = DIR_ZERO; - if (district_allows_river(cfg)) - align_district_with_river (tile, &pixel_x, &pixel_y, &river_dir); + //if (district_allows_river(cfg)) + //align_district_with_river (tile, &pixel_x, &pixel_y, &river_dir); if (territory_owner_id > 0) { Leader * leader = &leaders[territory_owner_id]; @@ -25979,9 +25984,9 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } case ENERGY_GRID_DISTRICT_ID: { - // Energy grids can have multiple buildings that - unlike most district buildings - don't have each other as prereq buildings, + // Energy grids can have multiple buildings that - unlike most district buildings - don't have each other as prereqs (e.g., Coal Plant & Nuclear Plant), // and thus have a combinatorial number of images based on which power plants are built in their radius. This returns the correct image index - buildings = get_energy_grid_img_index (tile_x, tile_y); + buildings = get_energy_grid_image_index (tile_x, tile_y); break; } default: From 69d14ff8fda3c27506b481c9490e266dfc9c6cac Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 10 Jan 2026 17:34:39 -0800 Subject: [PATCH 157/356] Ensure AI workers dont join cities if districts to be built; Fix logic for AI building central rail hubs --- civ_prog_objects.csv | 2 +- default.c3x_config.ini | 2 +- injected_code.c | 98 +++++++++++++++++++++++++++++++++--------- 3 files changed, 80 insertions(+), 22 deletions(-) diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 9ce66db5..339c2870 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -143,7 +143,7 @@ inlead, 0x4B1DC0, 0x4B8D50, 0x4B1E50, "City_requires_improvement_to_grow", " inlead, 0x4DD530, 0x4E5F00, 0x4DD5F0, "maybe_show_improvement_needed_for_growth_dialog", "void (__fastcall *) (void * this, int edx, City * city, int * param_2)" repl call, 0x5213E3, 0x52B3F3, 0x521483, "City_requires_improvement_to_grow_besides_neighborhood", "" inlead, 0x5C1AD0, 0x5D0670, 0x5C17E0, "Unit_can_perform_command", "bool (__fastcall *) (Unit * this, int edx, int unit_command_value)" -define, 0x5B39D0, 0x5C2320, 0x5B36E0, "Unit_join_city", "void (__fastcall *) (Unit * this, int edx, City * city)" +inlead, 0x5B39D0, 0x5C2320, 0x5B36E0, "Unit_join_city", "void (__fastcall *) (Unit * this, int edx, City * city)" define, 0x5C0740, 0x5CF2E0, 0x5C0450, "Unit_upgrade", "Unit * (__fastcall *) (Unit * this, int edx, bool ignore_cost)" define, 0x5C04D0, 0x5CF070, 0x5C01E0, "Unit_get_upgrade_cost", "int (__fastcall *) (Unit * this)" define, 0xCADC18, 0xCD0510, 0xCADBD8, "script_dot_txt_file_path", "char *" diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 94bc1fe8..3e1fa2f5 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -883,7 +883,7 @@ destroyed_wonders_can_be_built_again = true ; ; distribution_hub_yield_division_mode controls how a hub splits its collected food/shields across connected cities: ; flat: Divide raw yields by the configured divisors (distribution_hub_food_yield_divisor & distribution_hub_shield_yield_divisor) -; scale-by-city-count: Bonuses gradually reduce as more cities plugged into the hub (formula: floor(sqrt(connected_city_count)) * divisor) +; scale-by-city-count: Bonuses gradually reduce as more cities plugged into the hub. Formula: floor(raw_yield / (sqrt(connected_city_count) * divisor)) ; ; ai_distribution_hub_build_strategy controls how the AI decides to build distribution hubs: ; by-city-count: AI builds hubs based on its ideal hub count per 100 cities diff --git a/injected_code.c b/injected_code.c index 97f4db45..b2731d64 100644 --- a/injected_code.c +++ b/injected_code.c @@ -14110,21 +14110,6 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) } } else if (unit_command_value == UCV_Pillage) { return patch_Unit_can_pillage (this, __, this->Body.X, this->Body.Y); - } else if (unit_command_value == UCV_Join_City) { - bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; - if (!is_human) { - int type_id = this->Body.UnitTypeID; - if ((type_id >= 0) && (type_id < p_bic_data->UnitTypeCount)) { - int worker_actions = p_bic_data->UnitTypes[type_id].Worker_Actions; - if (worker_actions != 0 && (worker_actions & (UCV_Automate))) { - int civ_id = this->Body.CivID; - if (civ_id >= 0 && civ_id < 32) { - return is->city_pending_district_requests[civ_id].len == 0; - } - return true; - } - } - } } } if (is->current_config.disable_worker_automation && @@ -14142,6 +14127,74 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) return Unit_can_perform_command (this, __, unit_command_value); } +void __fastcall +patch_Unit_join_city (Unit * this, int edx, City * city) +{ + if (is->current_config.enable_districts && is_worker (this)) { + int civ_id = this->Body.CivID; + bool is_human = (*p_human_player_bits & (1 << civ_id)) != 0; + if (! is_human) { + struct district_worker_record * rec = get_tracked_worker_record (this); + if ((rec != NULL) && (rec->pending_req != NULL)) { + ai_move_district_worker (this, rec); + return; + } + + Tile * worker_tile = tile_at (this->Body.X, this->Body.Y); + if ((worker_tile != NULL) && (worker_tile != p_null_tile)) { + int worker_continent_id = worker_tile->vtable->m46_Get_ContinentID (worker_tile); + + if ((civ_id >= 0) && (civ_id < 32)) { + struct pending_district_request * same_city_req = NULL; + struct pending_district_request * same_continent_req = NULL; + + FOR_TABLE_ENTRIES (tei, &is->city_pending_district_requests[civ_id]) { + struct pending_district_request * req = (struct pending_district_request *)(long)tei.value; + if ((req == NULL) || (req->assigned_worker_id >= 0)) + continue; + if ((req->civ_id != civ_id) || (req->city_id < 0)) + continue; + + City * req_city = get_city_ptr (req->city_id); + if (req_city == NULL) + continue; + + if (city != NULL && req_city->Body.ID == city->Body.ID) { + same_city_req = req; + break; + } + + Tile * req_city_tile = tile_at (req_city->Body.X, req_city->Body.Y); + if ((req_city_tile != NULL) && (req_city_tile != p_null_tile)) { + int req_continent_id = req_city_tile->vtable->m46_Get_ContinentID (req_city_tile); + if (req_continent_id == worker_continent_id) { + same_continent_req = req; + } + } + } + + struct pending_district_request * chosen_req = (same_city_req != NULL) ? same_city_req : same_continent_req; + if (chosen_req != NULL) { + City * req_city = get_city_ptr (chosen_req->city_id); + if (req_city != NULL) { + int target_x = 0; + int target_y = 0; + Tile * target_tile = find_tile_for_district (req_city, chosen_req->district_id, &target_x, &target_y); + if ((target_tile != NULL) && (target_tile != p_null_tile)) { + wrap_tile_coords (&p_bic_data->Map, &target_x, &target_y); + assign_worker_to_district (chosen_req, this, req_city, chosen_req->district_id, target_x, target_y); + return; + } + } + } + } + } + } + } + + Unit_join_city (this, __, city); +} + bool __fastcall patch_Unit_can_pillage (Unit * this, int edx, int tile_x, int tile_y) { @@ -20362,9 +20415,7 @@ patch_Leader_do_production_phase (Leader * this) struct district_config * cfg = &is->district_configs[district_id]; struct district_infos * info = &is->district_infos[district_id]; - // Carve out an exception for AI to build Central Rail Hub, which is available in Industrial era but Mass Transit, - // the only building dependent on it, is in the Modern era. - if (info->dependent_building_count > 0 && district_id != CENTRAL_RAIL_HUB_DISTRICT_ID) continue; + if (info->dependent_building_count > 0) continue; if (cfg->command == -1) continue; int prereq_id = info->advance_prereq_id; @@ -20376,8 +20427,15 @@ patch_Leader_do_production_phase (Leader * this) } } + // Special exception for AI to build Central Rail Hub, which is available in Industrial era but Mass Transit, + // the only building dependent on it, is in the Modern era. Without this the AI wouldn't build in Industrial era. + if (is->current_config.enable_central_rail_hub_districts) { + int prereq_id = is->district_infos[CENTRAL_RAIL_HUB_DISTRICT_ID].advance_prereq_id; + if ((prereq_id < 0) || Leader_has_tech (this, __, prereq_id)) + auto_dynamic_district_ids[auto_dynamic_district_count++] = CENTRAL_RAIL_HUB_DISTRICT_ID; + } + if (is->current_config.enable_distribution_hub_districts) { - // Check if AI has the tech prereq for distribution hubs before updating goals int prereq_id = is->district_infos[DISTRIBUTION_HUB_DISTRICT_ID].advance_prereq_id; if ((prereq_id < 0) || Leader_has_tech (this, __, prereq_id)) ai_update_distribution_hub_goal_for_leader (this); @@ -25844,7 +25902,7 @@ get_energy_grid_image_index (int tile_x, int tile_y) void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - //*p_debug_mode_bits |= 0xC; + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; From eba0d343957d7323f0e37e7b899c33d4f0ce6afc Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 11 Jan 2026 09:59:12 -0800 Subject: [PATCH 158/356] Add logic for port healing and attack --- C3X.h | 3 ++- civ_prog_objects.csv | 2 ++ default.c3x_config.ini | 3 +++ injected_code.c | 38 ++++++++++++++++++++++++++++++-------- 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/C3X.h b/C3X.h index 560f12e9..48b537ce 100644 --- a/C3X.h +++ b/C3X.h @@ -336,6 +336,7 @@ struct c3x_config { int pinned_hour_for_day_night_cycle; bool enable_natural_wonders; + bool add_natural_wonders_to_scenarios_if_none; bool show_natural_wonder_name_on_map; int minimum_natural_wonder_separation; @@ -371,8 +372,8 @@ struct c3x_config { int central_rail_hub_distribution_food_bonus_percent; int central_rail_hub_distribution_shield_bonus_percent; - bool expand_water_tile_checks_to_city_work_area; bool workers_can_enter_coast; + bool expand_water_tile_checks_to_city_work_area; bool ai_defends_districts; diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 339c2870..18619eb5 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -866,3 +866,5 @@ repl call, 0x4C010F, 0x0, 0x0, "Map_impl_has_fresh_water_within repl call, 0x4C0173, 0x0, 0x0, "Map_impl_is_near_river_within_work_area", "" repl call, 0x4C01AF, 0x0, 0x0, "Map_impl_is_near_river_within_work_area", "" repl call, 0x4C01CF, 0x0, 0x0, "Map_impl_is_near_lake_within_work_area", "" +inlead, 0x458120, 0x0, 0x0, "Unit_ai_move_naval_power_unit", "void (__fastcall *) (Unit * this)" +inlead, 0x44C130, 0x0, 0x0, "Unit_ai_eval_pillage_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" \ No newline at end of file diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 3e1fa2f5..63bf0ff7 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -804,6 +804,9 @@ pinned_hour_for_day_night_cycle = 0 ; Show or hide natural wonders on the map. When disabled, natural wonders will not appear on the map. enable_natural_wonders = true +; If a new scenario is loaded which has no natural wonders defined, add natural wonders. +add_natural_wonders_to_scenarios_if_none = true + ; Show the names of natural wonders on the map below their image. show_natural_wonder_name_on_map = true diff --git a/injected_code.c b/injected_code.c index b2731d64..1b68007f 100644 --- a/injected_code.c +++ b/injected_code.c @@ -12270,6 +12270,7 @@ patch_init_floating_point () {"enable_neighborhood_districts" , false, offsetof (struct c3x_config, enable_neighborhood_districts)}, {"enable_wonder_districts" , false, offsetof (struct c3x_config, enable_wonder_districts)}, {"enable_natural_wonders" , false, offsetof (struct c3x_config, enable_natural_wonders)}, + {"add_natural_wonders_to_scenarios_if_none" , false, offsetof (struct c3x_config, add_natural_wonders_to_scenarios_if_none)}, {"enable_distribution_hub_districts" , false, offsetof (struct c3x_config, enable_distribution_hub_districts)}, {"enable_aerodrome_districts" , false, offsetof (struct c3x_config, enable_aerodrome_districts)}, {"enable_port_districts" , false, offsetof (struct c3x_config, enable_port_districts)}, @@ -20453,6 +20454,7 @@ patch_Leader_do_production_phase (Leader * this) for (int i = 0; i < auto_dynamic_district_count; i++) { int district_id = auto_dynamic_district_ids[i]; + if (city_has_required_district (city, district_id)) continue; if (! city_can_build_district (city, district_id)) continue; if (find_pending_district_request (city, district_id) != NULL) continue; @@ -22297,6 +22299,22 @@ patch_Map_place_scenario_things (Map * this) if (is->current_config.enable_districts) load_scenario_districts_from_file (); + if (is->current_config.add_natural_wonders_to_scenarios_if_none && + is->current_config.enable_natural_wonders) { + bool any_natural_wonders = false; + FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { + struct district_instance * inst = (struct district_instance *)(long)tei.value; + if ((inst != NULL) && + (inst->district_type == NATURAL_WONDER_DISTRICT_ID) && + (inst->natural_wonder_info.natural_wonder_id >= 0)) { + any_natural_wonders = true; + break; + } + } + if (! any_natural_wonders) + place_natural_wonders_on_map (); + } + is->is_placing_scenario_things = false; } @@ -25749,7 +25767,9 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, } } else if (direct_diagonal && *out_variant == SW && anchor == DIR_NE) { *out_pixel_x -=8; *out_pixel_y -= 8; } + else if (direct_diagonal && *out_variant == SE && anchor == DIR_NW) { *out_pixel_x -=8; *out_pixel_y -= 8; } else if (direct_diagonal && *out_variant == NW && anchor == DIR_SE) { *out_pixel_x +=6; *out_pixel_y += 6; } + else if (direct_diagonal && *out_variant == NE && anchor == DIR_SW) { *out_pixel_x +=6; *out_pixel_y += 6; } } bool @@ -27193,7 +27213,6 @@ patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool return transport; } -/* // Returns true if the given tile is a water district owned by an enemy of the unit bool is_enemy_maritime_district_tile (Unit * unit, Tile * tile) @@ -27218,15 +27237,19 @@ is_enemy_maritime_district_tile (Unit * unit, Tile * tile) if ((owner <= 0) || (owner == unit->Body.CivID)) return false; - return unit->vtable->is_enemy_of_civ (unit, owner, false); + Leader * leader = &leaders[unit->Body.CivID]; + return leader->At_War[owner]; } // Boost pillage desirability for maritime districts int __fastcall -patch_Unit_ai_eval_pillage_target (Unit * this, int edx, unsigned short tile_x, int tile_y) +patch_Unit_ai_eval_pillage_target (Unit * this, int edx, int tile_x, int tile_y) { int base = Unit_ai_eval_pillage_target (this, __, tile_x, tile_y); + if (! is->current_config.enable_districts || ! is->current_config.enable_port_districts) + return base; + Tile * tile = tile_at (tile_x, tile_y); if ((base > 0) && is_enemy_maritime_district_tile (this, tile)) { // Double the base score and add a flat kicker so districts outrank generic coast targets @@ -27304,12 +27327,12 @@ void __fastcall patch_Unit_ai_move_naval_power_unit (Unit * this) { // If we're already sitting on an enemy maritime district, pillage it immediately - if (this->Body.Container_Unit < 0) { + if (is->current_config.enable_districts && is->current_config.enable_port_districts && this->Body.Container_Unit < 0) { Tile * here = tile_at (this->Body.X, this->Body.Y); if (is_enemy_maritime_district_tile (this, here) && - can_pillage (this, (unsigned short)this->Body.X, this->Body.Y) && - (patch_Unit_ai_eval_pillage_target (this, __, (unsigned short)this->Body.X, this->Body.Y) > 0)) { - attack_tile (this, this->Body.X, this->Body.Y, 0); + patch_Unit_can_pillage (this, __, this->Body.X, this->Body.Y) && + (patch_Unit_ai_eval_pillage_target (this, __, this->Body.X, this->Body.Y) > 0)) { + Unit_attack_tile (this, __, this->Body.X, this->Body.Y, false); return; } } @@ -27319,7 +27342,6 @@ patch_Unit_ai_move_naval_power_unit (Unit * this) Unit_ai_move_naval_power_unit (this); } -*/ // TCC requires a main function be defined even though it's never used. int main () { return 0; } From e84b07e9dbbb783e78c39dafa822c097d473ebd6 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 11 Jan 2026 10:33:03 -0800 Subject: [PATCH 159/356] AI trying to build air/naval units triggers districts to be queued --- injected_code.c | 103 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 30 deletions(-) diff --git a/injected_code.c b/injected_code.c index 1b68007f..f042f0ac 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16234,25 +16234,28 @@ patch_City_can_build_unit (City * this, int edx, int unit_type_id, bool exclude_ return false; } - if (is->current_config.enable_districts) { + if (is->current_config.enable_districts && base) { + bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; UnitType * type = &p_bic_data->UnitTypes[unit_type_id]; - // Bail if tech reqs are not met - int prereq_id = type->AdvReq; - if (prereq_id >= 0 && ! Leader_has_tech (&leaders[this->Body.CivID], __, prereq_id)) - return false; - - if (! Leader_can_build_unit (&leaders[this->Body.CivID], __, unit_type_id, 1, false)) - return false; + // Superficially allow the AI to choose the unit for scoring and production. + // If a disallowed air/naval unit is chosen in ai_choose_production, we'll swap it out for a feasible fallback later + // after prioritizing the aerodrome/port to be built + if (! is_human && (type->Unit_Class == UTC_Air || type->Unit_Class == UTC_Sea)) + return base; // Air units if (type->Unit_Class == UTC_Air) { if (is->current_config.enable_aerodrome_districts && is->current_config.air_units_use_aerodrome_districts_not_cities) { + if (! city_can_build_district (this, AERODROME_DISTRICT_ID)) + return false; return city_has_required_district (this, AERODROME_DISTRICT_ID); } // Naval units } else if (type->Unit_Class == UTC_Sea) { if (is->current_config.enable_port_districts && is->current_config.naval_units_use_port_districts_not_cities) { + if (! city_can_build_district (this, PORT_DISTRICT_ID)) + return false; return city_has_required_district (this, PORT_DISTRICT_ID); } } @@ -16417,31 +16420,55 @@ ai_handle_district_production_requirements (City * city, City_Order * out) bool swapped_to_fallback = false; City_Order fallback_order = {0}; int required_district_id = -1; + bool should_mark_district = false; char ss[200]; if (is->current_config.enable_districts && - (out->OrderType == COT_Improvement) && - (out->OrderID >= 0) && (out->OrderID < p_bic_data->ImprovementsCount)) { - - // Check if AI is trying to build a wonder without an incomplete wonder district + (out->OrderID >= 0)) { bool needs_wonder_district = false; - bool requires_district = city_requires_district_for_improvement (city, out->OrderID, &required_district_id); - if (is->current_config.enable_wonder_districts) { - Improvement * improv = &p_bic_data->Improvements[out->OrderID]; - if ((improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) && - (! city_has_wonder_district_with_no_completed_wonder (city, out->OrderID))) { - snprintf (ss, sizeof ss, "ai_handle_district_production_requirements: City %d (%s) needs wonder district to build wonder improvement %d\n", - city->Body.ID, city->Body.CityName, out->OrderID); - (*p_OutputDebugStringA) (ss); - needs_wonder_district = true; - if (required_district_id < 0) { - required_district_id = WONDER_DISTRICT_ID; + bool requires_district = false; + bool needs_district = false; + + if ((out->OrderType == COT_Unit) && + (out->OrderID < p_bic_data->UnitTypeCount)) { + UnitType * type = &p_bic_data->UnitTypes[out->OrderID]; + if ((type->Unit_Class == UTC_Air) && + is->current_config.enable_aerodrome_districts && + is->current_config.air_units_use_aerodrome_districts_not_cities && + (! city_has_required_district (city, AERODROME_DISTRICT_ID))) { + required_district_id = AERODROME_DISTRICT_ID; + needs_district = true; + should_mark_district = true; + } else if ((type->Unit_Class == UTC_Sea) && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities && + (! city_has_required_district (city, PORT_DISTRICT_ID))) { + required_district_id = PORT_DISTRICT_ID; + needs_district = true; + should_mark_district = true; + } + } else if ((out->OrderType == COT_Improvement) && + (out->OrderID < p_bic_data->ImprovementsCount)) { + // Check if AI is trying to build a wonder without an incomplete wonder district + requires_district = city_requires_district_for_improvement (city, out->OrderID, &required_district_id); + if (is->current_config.enable_wonder_districts) { + Improvement * improv = &p_bic_data->Improvements[out->OrderID]; + if ((improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) && + (! city_has_wonder_district_with_no_completed_wonder (city, out->OrderID))) { + snprintf (ss, sizeof ss, "ai_handle_district_production_requirements: City %d (%s) needs wonder district to build wonder improvement %d\n", + city->Body.ID, city->Body.CityName, out->OrderID); + (*p_OutputDebugStringA) (ss); + needs_wonder_district = true; + if (required_district_id < 0) { + required_district_id = WONDER_DISTRICT_ID; + } } } + needs_district = needs_wonder_district || requires_district; } - if (needs_wonder_district || requires_district) { + if (needs_district) { struct ai_best_feasible_order * stored = get_best_feasible_order (city); if (stored != NULL) { bool fallback_is_feasible = true; @@ -16473,15 +16500,30 @@ ai_handle_district_production_requirements (City * city, City_Order * out) if ((stored->order.OrderID < 0) || (stored->order.OrderID >= p_bic_data->UnitTypeCount)) fallback_is_feasible = false; + if (fallback_is_feasible) { + UnitType * type = &p_bic_data->UnitTypes[stored->order.OrderID]; + if ((type->Unit_Class == UTC_Air) && + is->current_config.enable_aerodrome_districts && + is->current_config.air_units_use_aerodrome_districts_not_cities && + (! city_has_required_district (city, AERODROME_DISTRICT_ID))) + fallback_is_feasible = false; + if ((type->Unit_Class == UTC_Sea) && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities && + (! city_has_required_district (city, PORT_DISTRICT_ID))) + fallback_is_feasible = false; + } } else fallback_is_feasible = false; if (fallback_is_feasible) { - // Remember pending building order for any improvement that requires a district - snprintf (ss, sizeof ss, "ai_handle_district_production_requirements: Remembering fallback pending building order for city %d (%s): order type %d id %d\n", - city->Body.ID, city->Body.CityName, out->OrderType, out->OrderID); - (*p_OutputDebugStringA) (ss); - remember_pending_building_order (city, out->OrderID); + if (out->OrderType == COT_Improvement) { + // Remember pending building order for any improvement that requires a district + snprintf (ss, sizeof ss, "ai_handle_district_production_requirements: Remembering fallback pending building order for city %d (%s): order type %d id %d\n", + city->Body.ID, city->Body.CityName, out->OrderType, out->OrderID); + (*p_OutputDebugStringA) (ss); + remember_pending_building_order (city, out->OrderID); + } fallback_order = stored->order; swapped_to_fallback = true; @@ -16492,8 +16534,9 @@ ai_handle_district_production_requirements (City * city, City_Order * out) if (swapped_to_fallback) { *out = fallback_order; - mark_city_needs_district (city, required_district_id); } + if ((swapped_to_fallback || should_mark_district) && (required_district_id >= 0)) + mark_city_needs_district (city, required_district_id); clear_best_feasible_order (city); return swapped_to_fallback; From 793fee569047bc2e420f1252ac987be16892afb2 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 11 Jan 2026 11:54:50 -0800 Subject: [PATCH 160/356] Add patches for AI aerodrome usage; Fix tech check on patch_City_can_build_unit --- civ_prog_objects.csv | 7 +- injected_code.c | 237 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 241 insertions(+), 3 deletions(-) diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 18619eb5..ed442b76 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -867,4 +867,9 @@ repl call, 0x4C0173, 0x0, 0x0, "Map_impl_is_near_river_within_w repl call, 0x4C01AF, 0x0, 0x0, "Map_impl_is_near_river_within_work_area", "" repl call, 0x4C01CF, 0x0, 0x0, "Map_impl_is_near_lake_within_work_area", "" inlead, 0x458120, 0x0, 0x0, "Unit_ai_move_naval_power_unit", "void (__fastcall *) (Unit * this)" -inlead, 0x44C130, 0x0, 0x0, "Unit_ai_eval_pillage_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" \ No newline at end of file +inlead, 0x44C130, 0x0, 0x0, "Unit_ai_eval_pillage_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" +inlead, 0x456840, 0x0, 0x0, "Unit_ai_move_air_bombard_unit", "void (__fastcall *) (Unit * this)" +inlead, 0x4579E0, 0x0, 0x0, "Unit_ai_move_air_defense_unit", "void (__fastcall *) (Unit * this)" +inlead, 0x459CE0, 0x0, 0x0, "Unit_ai_move_air_transport", "void (__fastcall *) (Unit * this)" +define, 0x44C340, 0x0, 0x0, "Unit_ai_eval_bombard_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y, int param_3)" +define, 0x5C1920, 0x0, 0x0, "Unit_airdrop", "void (__fastcall *) (Unit * this, int edx, int x, int y)" \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index f042f0ac..6d76c45c 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16234,14 +16234,25 @@ patch_City_can_build_unit (City * this, int edx, int unit_type_id, bool exclude_ return false; } - if (is->current_config.enable_districts && base) { + if (is->current_config.enable_districts) { bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; UnitType * type = &p_bic_data->UnitTypes[unit_type_id]; + // Bail if tech reqs are not met + int prereq_id = type->AdvReq; + if (prereq_id >= 0 && ! Leader_has_tech (&leaders[this->Body.CivID], __, prereq_id)) + return false; + + if (! Leader_can_build_unit (&leaders[this->Body.CivID], __, unit_type_id, 1, false)) + return false; + // Superficially allow the AI to choose the unit for scoring and production. // If a disallowed air/naval unit is chosen in ai_choose_production, we'll swap it out for a feasible fallback later // after prioritizing the aerodrome/port to be built - if (! is_human && (type->Unit_Class == UTC_Air || type->Unit_Class == UTC_Sea)) + if (! is_human && ( + (city_can_build_district (this, AERODROME_DISTRICT_ID) && type->Unit_Class == UTC_Air) || + (city_can_build_district (this, PORT_DISTRICT_ID) && type->Unit_Class == UTC_Sea)) + ) return base; // Air units @@ -27386,5 +27397,227 @@ patch_Unit_ai_move_naval_power_unit (Unit * this) Unit_ai_move_naval_power_unit (this); } +void __fastcall +patch_Unit_ai_move_air_bombard_unit (Unit * this) +{ + if (! (is->current_config.enable_districts && + is->current_config.enable_aerodrome_districts && + is->current_config.air_units_use_aerodrome_districts_not_cities)) { + Unit_ai_move_air_bombard_unit (this); + return; + } + + if (this->Body.Damage > 0) { + Unit_set_escortee (this, __, -1); + Unit_set_state (this, __, UnitState_Fortifying); + return; + } + + int best_target = 0; + int target_x = -1, target_y = -1; + int op_range = p_bic_data->UnitTypes[this->Body.UnitTypeID].OperationalRange; + int grid = op_range * 2 + 1; + if (1 < grid * grid) { + for (int n = 1; n < grid * grid; n++) { + int dx, dy; + neighbor_index_to_diff (n, &dx, &dy); + int x = Map_wrap_horiz (&p_bic_data->Map, __, this->Body.X + dx); + int y = Map_wrap_vert (&p_bic_data->Map, __, this->Body.Y + dy); + if (Map_in_range (&p_bic_data->Map, __, x, y)) { + int score = Unit_ai_eval_bombard_target (this, __, x, y, 1); + if (score > best_target) { + best_target = score; + target_x = x; + target_y = y; + } + } + } + } + + int best_base_score = 0x7fffffff; + int base_x = -1, base_y = -1; + for (int y = 0; y < p_bic_data->Map.Height; y++) { + for (int x = 0; x < p_bic_data->Map.Width; x++) { + Tile * tile = tile_at (x, y); + if (! tile_has_friendly_aerodrome_district (tile, this->Body.CivID, false)) + continue; + if (! patch_Unit_is_in_rebase_range (this, __, x, y)) + continue; + if (! is_below_stack_limit (tile, this->Body.CivID, + p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class)) + continue; + + int count = count_units_at (x, y, UF_AI_STRAT_A_VIS_TO_B, 6, -1, -1); + int x_dist = Map_get_x_dist (&p_bic_data->Map, __, x, this->Body.X); + int y_dist = Map_get_y_dist (&p_bic_data->Map, __, y, this->Body.Y); + int score = (count * 10) + ((x_dist + y_dist) >> 1); + if ((this->Body.X == x) && (this->Body.Y == y)) + score -= 20; + + if (score < best_base_score) { + best_base_score = score; + base_x = x; + base_y = y; + } + } + } + + if ((base_x >= 0) && ((this->Body.X != base_x) || (this->Body.Y != base_y))) { + Unit_set_escortee (this, __, -1); + patch_Unit_move (this, __, base_x, base_y); + return; + } + + if ((target_x >= 0) && (target_y >= 0)) { + Unit_set_escortee (this, __, -1); + patch_Unit_bombard_tile (this, __, target_x, target_y); + return; + } + + Unit_set_escortee (this, __, -1); + Unit_set_state (this, __, UnitState_Fortifying); +} + +void __fastcall +patch_Unit_ai_move_air_defense_unit (Unit * this) +{ + if (! (is->current_config.enable_districts && + is->current_config.enable_aerodrome_districts && + is->current_config.air_units_use_aerodrome_districts_not_cities)) { + Unit_ai_move_air_defense_unit (this); + return; + } + + Unit_set_state (this, __, 0); + if (this->Body.Damage > 0) { + Unit_set_escortee (this, __, -1); + Unit_set_state (this, __, UnitState_Fortifying); + return; + } + + int best_base_score = 0x7fffffff; + int base_x = -1, base_y = -1; + for (int y = 0; y < p_bic_data->Map.Height; y++) { + for (int x = 0; x < p_bic_data->Map.Width; x++) { + Tile * tile = tile_at (x, y); + if (! tile_has_friendly_aerodrome_district (tile, this->Body.CivID, false)) + continue; + if (! patch_Unit_is_in_rebase_range (this, __, x, y)) + continue; + if (! is_below_stack_limit (tile, this->Body.CivID, + p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class)) + continue; + + int count = count_units_at (x, y, UF_AI_STRAT_A_VIS_TO_B, 7, -1, -1); + int x_dist = Map_get_x_dist (&p_bic_data->Map, __, x, this->Body.X); + int y_dist = Map_get_y_dist (&p_bic_data->Map, __, y, this->Body.Y); + int score = (count * 10) + ((x_dist + y_dist) >> 1); + if ((this->Body.X == x) && (this->Body.Y == y)) + score -= 20; + + if (score < best_base_score) { + best_base_score = score; + base_x = x; + base_y = y; + } + } + } + + if ((base_x >= 0) && ((this->Body.X != base_x) || (this->Body.Y != base_y))) { + Unit_set_escortee (this, __, -1); + patch_Unit_move (this, __, base_x, base_y); + return; + } + + Unit_set_escortee (this, __, -1); + Unit_set_state (this, __, UnitState_Intercept); +} + +void __fastcall +patch_Unit_ai_move_air_transport (Unit * this) +{ + if (! (is->current_config.enable_districts && + is->current_config.enable_aerodrome_districts && + is->current_config.air_units_use_aerodrome_districts_not_cities)) { + Unit_ai_move_air_transport (this); + return; + } + + if (this->Body.Damage < 1) { + if (Unit_can_airdrop (this)) { + int best_score = 0; + int best_x = -1, best_y = -1; + int op_range = p_bic_data->UnitTypes[this->Body.UnitTypeID].OperationalRange; + int grid = op_range * 2 + 1; + if (1 < grid * grid) { + for (int n = 1; n < grid * grid; n++) { + int dx, dy; + neighbor_index_to_diff (n, &dx, &dy); + int x = Map_wrap_horiz (&p_bic_data->Map, __, this->Body.X + dx); + int y = Map_wrap_vert (&p_bic_data->Map, __, this->Body.Y + dy); + if (Map_in_range (&p_bic_data->Map, __, x, y)) { + int score = Unit_ai_eval_airdrop_target (this, __, x, y); + if (score > best_score) { + best_score = score; + best_x = x; + best_y = y; + } + } + } + if ((best_x >= 0) && (best_y >= 0)) { + Unit_set_escortee (this, __, -1); + Unit_airdrop (this, __, best_x, best_y); + return; + } + } + } + + int best_score = -1; + int base_x = -1, base_y = -1; + for (int y = 0; y < p_bic_data->Map.Height; y++) { + for (int x = 0; x < p_bic_data->Map.Width; x++) { + Tile * tile = tile_at (x, y); + if (! tile_has_friendly_aerodrome_district (tile, this->Body.CivID, false)) + continue; + if (! patch_Unit_is_in_rebase_range (this, __, x, y)) + continue; + if (! is_below_stack_limit (tile, this->Body.CivID, + p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class)) + continue; + + int score = count_units_at (x, y, UF_AI_STRAT_A_VIS_TO_B, 0, -1, -1) + + count_units_at (x, y, UF_AI_STRAT_A_VIS_TO_B, 1, -1, -1) + 1; + if (count_units_at (x, y, UF_AI_STRAT_A_VIS_TO_B, 9, -1, -1) == 0) + score *= 2; + int cont_id = tile->vtable->m46_Get_ContinentID (tile); + if ((cont_id >= 0) && (cont_id < p_bic_data->Map.Continent_Count) && + (p_bic_data->Map.Continents[cont_id].Body.TileCount > 0x15)) + score *= 2; + + if (score > best_score) { + best_score = score; + base_x = x; + base_y = y; + } + } + } + + if ((base_x >= 0) && ((this->Body.X != base_x) || (this->Body.Y != base_y))) { + Unit_set_escortee (this, __, -1); + patch_Unit_move (this, __, base_x, base_y); + if (Unit_count_contained_units (this) > 0) { + patch_Unit_disembark_passengers (this, __, this->Body.X, this->Body.Y); + } + return; + } + } + + if (Unit_count_contained_units (this) > 0) + patch_Unit_disembark_passengers (this, __, this->Body.X, this->Body.Y); + + Unit_set_escortee (this, __, -1); + Unit_set_state (this, __, UnitState_Fortifying); +} + // TCC requires a main function be defined even though it's never used. int main () { return 0; } From ef62b2bfd5bbd29ba15b912d67e8b89f982f2648 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 11 Jan 2026 13:48:13 -0800 Subject: [PATCH 161/356] Add buildable_by_civs, buildable_by_civ_traits, buildable_by_civ_govs, buildable_by_civ_cultures --- C3X.h | 30 +++ injected_code.c | 516 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 521 insertions(+), 25 deletions(-) diff --git a/C3X.h b/C3X.h index 48b537ce..aedcaed5 100644 --- a/C3X.h +++ b/C3X.h @@ -615,6 +615,18 @@ struct district_config { char const * generated_resource; int generated_resource_id; short generated_resource_flags; + char const * buildable_by_civs[32]; + int buildable_by_civ_count; + bool has_buildable_by_civs; + int buildable_by_civ_traits_ids[8]; + int buildable_by_civ_traits_id_count; + bool has_buildable_by_civ_traits; + int buildable_by_civ_govs_ids[5]; + int buildable_by_civ_govs_id_count; + bool has_buildable_by_civ_govs; + int buildable_by_civ_cultures_ids[5]; + int buildable_by_civ_cultures_id_count; + bool has_buildable_by_civ_cultures; }; struct wonder_district_config { @@ -782,6 +794,8 @@ struct parsed_district_definition { int shield_bonus; int happiness_bonus; unsigned int buildable_square_types_mask; + char * buildable_by_civs[32]; + int buildable_by_civ_count; bool has_name; bool has_tooltip; bool has_advance_prereq; @@ -806,11 +820,27 @@ struct parsed_district_definition { bool has_happiness_bonus; bool has_buildable_on; bool has_resource_prereq_on_tile; + bool has_buildable_by_civs; char * generated_resource; char * generated_resource_settings[5]; int generated_resource_settings_count; bool has_generated_resource; bool has_generated_resource_settings; + char * buildable_by_civ_traits[32]; + int buildable_by_civ_traits_count; + int buildable_by_civ_traits_ids[32]; + int buildable_by_civ_traits_id_count; + bool has_buildable_by_civ_traits; + char * buildable_by_civ_govs[32]; + int buildable_by_civ_govs_count; + int buildable_by_civ_govs_ids[32]; + int buildable_by_civ_govs_id_count; + bool has_buildable_by_civ_govs; + char * buildable_by_civ_cultures[32]; + int buildable_by_civ_cultures_count; + int buildable_by_civ_cultures_ids[32]; + int buildable_by_civ_cultures_id_count; + bool has_buildable_by_civ_cultures; }; struct parsed_wonder_definition { diff --git a/injected_code.c b/injected_code.c index 6d76c45c..e8e3f284 100644 --- a/injected_code.c +++ b/injected_code.c @@ -204,6 +204,9 @@ bool __fastcall patch_Unit_can_pillage (Unit * this, int edx, int tile_x, int ti bool __fastcall patch_City_has_resource (City * this, int edx, int resource_id); bool __fastcall patch_Leader_can_build_city_improvement (Leader * this, int edx, int i_improv, bool param_2); bool city_can_build_district (City * city, int district_id); +bool leader_can_build_district (Leader * leader, int district_id); +bool find_civ_trait_id_by_name (struct string_slice const * name, int * out_id); +bool find_civ_culture_id_by_name (struct string_slice const * name, int * out_id); Tile * find_tile_for_district (City * city, int district_id, int * out_x, int * out_y); struct district_instance * get_district_instance (Tile * tile); bool city_has_required_district (City * city, int district_id); @@ -5025,6 +5028,27 @@ free_dynamic_district_config (struct district_config * cfg) cfg->resource_prereq_on_tile = NULL; } + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { + if (cfg->buildable_by_civs[i] != NULL) { + free ((void *)cfg->buildable_by_civs[i]); + cfg->buildable_by_civs[i] = NULL; + } + } + cfg->buildable_by_civ_count = 0; + cfg->has_buildable_by_civs = false; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_traits_ids); i++) + cfg->buildable_by_civ_traits_ids[i] = -1; + cfg->buildable_by_civ_traits_id_count = 0; + cfg->has_buildable_by_civ_traits = false; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_govs_ids); i++) + cfg->buildable_by_civ_govs_ids[i] = -1; + cfg->buildable_by_civ_govs_id_count = 0; + cfg->has_buildable_by_civ_govs = false; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_cultures_ids); i++) + cfg->buildable_by_civ_cultures_ids[i] = -1; + cfg->buildable_by_civ_cultures_id_count = 0; + cfg->has_buildable_by_civ_cultures = false; + for (int i = 0; i < 5; i++) { if (cfg->dependent_improvements[i] != NULL) { free ((void *)cfg->dependent_improvements[i]); @@ -5132,6 +5156,29 @@ free_special_district_override_strings (struct district_config * cfg, struct dis cfg->resource_prereq_on_tile = NULL; } + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { + char const * default_value = (i < defaults->buildable_by_civ_count) ? defaults->buildable_by_civs[i] : NULL; + if ((cfg->buildable_by_civs[i] != NULL) && + (cfg->buildable_by_civs[i] != default_value)) { + free ((void *)cfg->buildable_by_civs[i]); + } + cfg->buildable_by_civs[i] = NULL; + } + cfg->buildable_by_civ_count = defaults->buildable_by_civ_count; + cfg->has_buildable_by_civs = defaults->has_buildable_by_civs; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_traits_ids); i++) + cfg->buildable_by_civ_traits_ids[i] = -1; + cfg->buildable_by_civ_traits_id_count = defaults->buildable_by_civ_traits_id_count; + cfg->has_buildable_by_civ_traits = defaults->has_buildable_by_civ_traits; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_govs_ids); i++) + cfg->buildable_by_civ_govs_ids[i] = -1; + cfg->buildable_by_civ_govs_id_count = defaults->buildable_by_civ_govs_id_count; + cfg->has_buildable_by_civ_govs = defaults->has_buildable_by_civ_govs; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_cultures_ids); i++) + cfg->buildable_by_civ_cultures_ids[i] = -1; + cfg->buildable_by_civ_cultures_id_count = defaults->buildable_by_civ_cultures_id_count; + cfg->has_buildable_by_civ_cultures = defaults->has_buildable_by_civ_cultures; + for (int i = 0; i < ARRAY_LEN (cfg->dependent_improvements); i++) { char const * default_value = (i < defaults->dependent_improvement_count) ? defaults->dependent_improvements[i] : NULL; if ((cfg->dependent_improvements[i] != NULL) && @@ -5258,6 +5305,38 @@ free_parsed_district_definition (struct parsed_district_definition * def) def->resource_prereq_on_tile = NULL; } + for (int i = 0; i < def->buildable_by_civ_count; i++) { + if (def->buildable_by_civs[i] != NULL) { + free (def->buildable_by_civs[i]); + def->buildable_by_civs[i] = NULL; + } + } + def->buildable_by_civ_count = 0; + for (int i = 0; i < def->buildable_by_civ_traits_count; i++) { + if (def->buildable_by_civ_traits[i] != NULL) { + free (def->buildable_by_civ_traits[i]); + def->buildable_by_civ_traits[i] = NULL; + } + } + def->buildable_by_civ_traits_count = 0; + def->buildable_by_civ_traits_id_count = 0; + for (int i = 0; i < def->buildable_by_civ_govs_count; i++) { + if (def->buildable_by_civ_govs[i] != NULL) { + free (def->buildable_by_civ_govs[i]); + def->buildable_by_civ_govs[i] = NULL; + } + } + def->buildable_by_civ_govs_count = 0; + def->buildable_by_civ_govs_id_count = 0; + for (int i = 0; i < def->buildable_by_civ_cultures_count; i++) { + if (def->buildable_by_civ_cultures[i] != NULL) { + free (def->buildable_by_civ_cultures[i]); + def->buildable_by_civ_cultures[i] = NULL; + } + } + def->buildable_by_civ_cultures_count = 0; + def->buildable_by_civ_cultures_id_count = 0; + for (int i = 0; i < def->dependent_improvement_count; i++) { if (def->dependent_improvements[i] != NULL) { free (def->dependent_improvements[i]); @@ -5556,6 +5635,55 @@ override_special_district_from_definition (struct parsed_district_definition * d def->resource_prereq_on_tile = NULL; } + if (def->has_buildable_by_civs) { + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { + char const * default_value = (i < defaults->buildable_by_civ_count) ? defaults->buildable_by_civs[i] : NULL; + if ((cfg->buildable_by_civs[i] != NULL) && + (cfg->buildable_by_civs[i] != default_value)) + free ((void *)cfg->buildable_by_civs[i]); + cfg->buildable_by_civs[i] = NULL; + } + cfg->buildable_by_civ_count = def->buildable_by_civ_count; + const int max_civ_names = ARRAY_LEN (cfg->buildable_by_civs); + if (cfg->buildable_by_civ_count > max_civ_names) + cfg->buildable_by_civ_count = max_civ_names; + for (int i = 0; i < cfg->buildable_by_civ_count; i++) { + cfg->buildable_by_civs[i] = def->buildable_by_civs[i]; + def->buildable_by_civs[i] = NULL; + } + cfg->has_buildable_by_civs = true; + } + + if (def->has_buildable_by_civ_traits) { + cfg->buildable_by_civ_traits_id_count = def->buildable_by_civ_traits_id_count; + const int max_entries = ARRAY_LEN (cfg->buildable_by_civ_traits_ids); + if (cfg->buildable_by_civ_traits_id_count > max_entries) + cfg->buildable_by_civ_traits_id_count = max_entries; + for (int i = 0; i < cfg->buildable_by_civ_traits_id_count; i++) + cfg->buildable_by_civ_traits_ids[i] = def->buildable_by_civ_traits_ids[i]; + cfg->has_buildable_by_civ_traits = true; + } + + if (def->has_buildable_by_civ_govs) { + cfg->buildable_by_civ_govs_id_count = def->buildable_by_civ_govs_id_count; + const int max_entries = ARRAY_LEN (cfg->buildable_by_civ_govs_ids); + if (cfg->buildable_by_civ_govs_id_count > max_entries) + cfg->buildable_by_civ_govs_id_count = max_entries; + for (int i = 0; i < cfg->buildable_by_civ_govs_id_count; i++) + cfg->buildable_by_civ_govs_ids[i] = def->buildable_by_civ_govs_ids[i]; + cfg->has_buildable_by_civ_govs = true; + } + + if (def->has_buildable_by_civ_cultures) { + cfg->buildable_by_civ_cultures_id_count = def->buildable_by_civ_cultures_id_count; + const int max_entries = ARRAY_LEN (cfg->buildable_by_civ_cultures_ids); + if (cfg->buildable_by_civ_cultures_id_count > max_entries) + cfg->buildable_by_civ_cultures_id_count = max_entries; + for (int i = 0; i < cfg->buildable_by_civ_cultures_id_count; i++) + cfg->buildable_by_civ_cultures_ids[i] = def->buildable_by_civ_cultures_ids[i]; + cfg->has_buildable_by_civ_cultures = true; + } + if (def->has_allow_multiple) cfg->allow_multiple = def->allow_multiple; if (def->has_vary_img_by_era) @@ -5732,6 +5860,41 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i def->resource_prereq_on_tile = NULL; } + new_cfg.buildable_by_civ_count = def->has_buildable_by_civs ? def->buildable_by_civ_count : 0; + const int max_civ_names = ARRAY_LEN (new_cfg.buildable_by_civs); + if (new_cfg.buildable_by_civ_count > max_civ_names) + new_cfg.buildable_by_civ_count = max_civ_names; + for (int i = 0; i < new_cfg.buildable_by_civ_count; i++) { + new_cfg.buildable_by_civs[i] = def->buildable_by_civs[i]; + def->buildable_by_civs[i] = NULL; + } + + new_cfg.has_buildable_by_civs = def->has_buildable_by_civs; + + new_cfg.buildable_by_civ_traits_id_count = def->has_buildable_by_civ_traits ? def->buildable_by_civ_traits_id_count : 0; + const int max_buildable_traits = ARRAY_LEN (new_cfg.buildable_by_civ_traits_ids); + if (new_cfg.buildable_by_civ_traits_id_count > max_buildable_traits) + new_cfg.buildable_by_civ_traits_id_count = max_buildable_traits; + for (int i = 0; i < new_cfg.buildable_by_civ_traits_id_count; i++) + new_cfg.buildable_by_civ_traits_ids[i] = def->buildable_by_civ_traits_ids[i]; + new_cfg.has_buildable_by_civ_traits = def->has_buildable_by_civ_traits; + + new_cfg.buildable_by_civ_govs_id_count = def->has_buildable_by_civ_govs ? def->buildable_by_civ_govs_id_count : 0; + const int max_buildable_govs = ARRAY_LEN (new_cfg.buildable_by_civ_govs_ids); + if (new_cfg.buildable_by_civ_govs_id_count > max_buildable_govs) + new_cfg.buildable_by_civ_govs_id_count = max_buildable_govs; + for (int i = 0; i < new_cfg.buildable_by_civ_govs_id_count; i++) + new_cfg.buildable_by_civ_govs_ids[i] = def->buildable_by_civ_govs_ids[i]; + new_cfg.has_buildable_by_civ_govs = def->has_buildable_by_civ_govs; + + new_cfg.buildable_by_civ_cultures_id_count = def->has_buildable_by_civ_cultures ? def->buildable_by_civ_cultures_id_count : 0; + const int max_buildable_cultures = ARRAY_LEN (new_cfg.buildable_by_civ_cultures_ids); + if (new_cfg.buildable_by_civ_cultures_id_count > max_buildable_cultures) + new_cfg.buildable_by_civ_cultures_id_count = max_buildable_cultures; + for (int i = 0; i < new_cfg.buildable_by_civ_cultures_id_count; i++) + new_cfg.buildable_by_civ_cultures_ids[i] = def->buildable_by_civ_cultures_ids[i]; + new_cfg.has_buildable_by_civ_cultures = def->has_buildable_by_civ_cultures; + new_cfg.allow_multiple = def->has_allow_multiple ? def->allow_multiple : false; new_cfg.vary_img_by_era = def->has_vary_img_by_era ? def->vary_img_by_era : false; new_cfg.vary_img_by_culture = def->has_vary_img_by_culture ? def->vary_img_by_culture : false; @@ -5906,6 +6069,174 @@ handle_district_definition_key (struct parsed_district_definition * def, def->resource_prereq_on_tile = copy_trimmed_string_or_null (value, 1); def->has_resource_prereq_on_tile = true; + } else if (slice_matches_str (key, "buildable_by_civs")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->buildable_by_civs, + ARRAY_LEN (def->buildable_by_civs), + &list_count, + parse_errors, + line_number, + "buildable_by_civs")) { + def->buildable_by_civ_count = list_count; + def->has_buildable_by_civs = true; + } else { + def->buildable_by_civ_count = 0; + def->has_buildable_by_civs = false; + } + free (value_text); + + } else if (slice_matches_str (key, "buildable_by_civ_traits")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->buildable_by_civ_traits, + ARRAY_LEN (def->buildable_by_civ_traits), + &list_count, + parse_errors, + line_number, + "buildable_by_civ_traits")) { + def->buildable_by_civ_traits_count = list_count; + def->buildable_by_civ_traits_id_count = 0; + for (int i = 0; i < def->buildable_by_civ_traits_count; i++) { + char const * trait_name = def->buildable_by_civ_traits[i]; + if ((trait_name == NULL) || (trait_name[0] == '\0')) + continue; + int trait_id = -1; + struct string_slice trait_slice = { .str = (char *)trait_name, .len = (int)strlen (trait_name) }; + if (find_civ_trait_id_by_name (&trait_slice, &trait_id)) { + bool already_listed = false; + for (int k = 0; k < def->buildable_by_civ_traits_id_count; k++) { + if (def->buildable_by_civ_traits_ids[k] == trait_id) { + already_listed = true; + break; + } + } + if ((! already_listed) && (def->buildable_by_civ_traits_id_count < ARRAY_LEN (def->buildable_by_civ_traits_ids))) + def->buildable_by_civ_traits_ids[def->buildable_by_civ_traits_id_count++] = trait_id; + } else { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: buildable_by_civ_traits entry \"%.*s\" not found", line_number, trait_slice.len, trait_slice.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + + for (int i = 0; i < def->buildable_by_civ_traits_count; i++) { + if (def->buildable_by_civ_traits[i] != NULL) { + free (def->buildable_by_civ_traits[i]); + def->buildable_by_civ_traits[i] = NULL; + } + } + def->buildable_by_civ_traits_count = 0; + def->has_buildable_by_civ_traits = true; + } else { + def->buildable_by_civ_traits_count = 0; + def->buildable_by_civ_traits_id_count = 0; + def->has_buildable_by_civ_traits = false; + } + free (value_text); + + } else if (slice_matches_str (key, "buildable_by_civ_govs")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->buildable_by_civ_govs, + ARRAY_LEN (def->buildable_by_civ_govs), + &list_count, + parse_errors, + line_number, + "buildable_by_civ_govs")) { + def->buildable_by_civ_govs_count = list_count; + def->buildable_by_civ_govs_id_count = 0; + for (int i = 0; i < def->buildable_by_civ_govs_count; i++) { + char const * gov_name = def->buildable_by_civ_govs[i]; + if ((gov_name == NULL) || (gov_name[0] == '\0')) + continue; + int gov_id = -1; + struct string_slice gov_slice = { .str = (char *)gov_name, .len = (int)strlen (gov_name) }; + if (find_game_object_id_by_name (GOK_GOVERNMENT, &gov_slice, 0, &gov_id)) { + bool already_listed = false; + for (int k = 0; k < def->buildable_by_civ_govs_id_count; k++) { + if (def->buildable_by_civ_govs_ids[k] == gov_id) { + already_listed = true; + break; + } + } + if ((! already_listed) && (def->buildable_by_civ_govs_id_count < ARRAY_LEN (def->buildable_by_civ_govs_ids))) + def->buildable_by_civ_govs_ids[def->buildable_by_civ_govs_id_count++] = gov_id; + } else { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: buildable_by_civ_govs entry \"%.*s\" not found", line_number, gov_slice.len, gov_slice.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + + for (int i = 0; i < def->buildable_by_civ_govs_count; i++) { + if (def->buildable_by_civ_govs[i] != NULL) { + free (def->buildable_by_civ_govs[i]); + def->buildable_by_civ_govs[i] = NULL; + } + } + def->buildable_by_civ_govs_count = 0; + def->has_buildable_by_civ_govs = true; + } else { + def->buildable_by_civ_govs_count = 0; + def->buildable_by_civ_govs_id_count = 0; + def->has_buildable_by_civ_govs = false; + } + free (value_text); + + } else if (slice_matches_str (key, "buildable_by_civ_cultures")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->buildable_by_civ_cultures, + ARRAY_LEN (def->buildable_by_civ_cultures), + &list_count, + parse_errors, + line_number, + "buildable_by_civ_cultures")) { + def->buildable_by_civ_cultures_count = list_count; + def->buildable_by_civ_cultures_id_count = 0; + for (int i = 0; i < def->buildable_by_civ_cultures_count; i++) { + char const * culture_name = def->buildable_by_civ_cultures[i]; + if ((culture_name == NULL) || (culture_name[0] == '\0')) + continue; + int culture_id = -1; + struct string_slice culture_slice = { .str = (char *)culture_name, .len = (int)strlen (culture_name) }; + if (find_civ_culture_id_by_name (&culture_slice, &culture_id)) { + bool already_listed = false; + for (int k = 0; k < def->buildable_by_civ_cultures_id_count; k++) { + if (def->buildable_by_civ_cultures_ids[k] == culture_id) { + already_listed = true; + break; + } + } + if ((! already_listed) && (def->buildable_by_civ_cultures_id_count < ARRAY_LEN (def->buildable_by_civ_cultures_ids))) + def->buildable_by_civ_cultures_ids[def->buildable_by_civ_cultures_id_count++] = culture_id; + } else { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: buildable_by_civ_cultures entry \"%.*s\" not found", line_number, culture_slice.len, culture_slice.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + + for (int i = 0; i < def->buildable_by_civ_cultures_count; i++) { + if (def->buildable_by_civ_cultures[i] != NULL) { + free (def->buildable_by_civ_cultures[i]); + def->buildable_by_civ_cultures[i] = NULL; + } + } + def->buildable_by_civ_cultures_count = 0; + def->has_buildable_by_civ_cultures = true; + } else { + def->buildable_by_civ_cultures_count = 0; + def->buildable_by_civ_cultures_id_count = 0; + def->has_buildable_by_civ_cultures = false; + } + free (value_text); + } else if (slice_matches_str (key, "img_paths")) { char * value_text = trim_and_extract_slice (value, 0); int list_count = 0; @@ -7240,6 +7571,60 @@ district_config_has_dependent_improvement (struct district_config * cfg, char co return false; } +bool +find_civ_trait_id_by_name (struct string_slice const * name, int * out_id) +{ + if ((name == NULL) || (name->len <= 0) || (out_id == NULL)) + return false; + + struct trait_entry { char const * name; int id; } traits[] = { + {"Agricultural", 6}, + {"Commercial", 1}, + {"Expansionist", 2}, + {"Industrious", 5}, + {"Militaristic", 0}, + {"Religious", 4}, + {"Scientific", 3}, + {"Seafaring", 7} + }; + + for (int i = 0; i < ARRAY_LEN (traits); i++) { + if (slice_matches_str (name, traits[i].name)) { + *out_id = traits[i].id; + return true; + } + } + + return false; +} + +bool +find_civ_culture_id_by_name (struct string_slice const * name, int * out_id) +{ + if ((name == NULL) || (name->len <= 0) || (out_id == NULL)) + return false; + + struct culture_entry { char const * name; int id; } cultures[] = { + {"American", 0}, + {"European", 1}, + {"Roman", 2}, + {"Mid East", 3}, + {"Mideast", 3}, + {"Middle Eastern", 3}, + {"MiddleEastern", 3}, + {"Asian", 4} + }; + + for (int i = 0; i < ARRAY_LEN (cultures); i++) { + if (slice_matches_str (name, cultures[i].name)) { + *out_id = cultures[i].id; + return true; + } + } + + return false; +} + void set_wonders_dependent_on_wonder_district (void) { @@ -8374,8 +8759,10 @@ can_build_district_on_tile (Tile * tile, int district_id) int tile_x = 0, tile_y = 0; tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y); - int prereq_id = is->district_infos[district_id].advance_prereq_id; - if ((prereq_id >= 0) && !Leader_has_tech (&leaders[tile->Territory_OwnerID], __, prereq_id)) + int owner_id = tile->Territory_OwnerID; + if ((owner_id < 0) || (owner_id >= 32)) + return false; + if (! leader_can_build_district (&leaders[owner_id], district_id)) return false; if (! district_resource_prereqs_met (tile, tile_x, tile_y, district_id, NULL)) @@ -8877,9 +9264,7 @@ city_can_build_district (City * city, int district_id) struct district_config const * cfg = &is->district_configs[district_id]; struct district_infos const * info = &is->district_infos[district_id]; - // Check tech prerequisite - int prereq_id = info->advance_prereq_id; - if ((prereq_id >= 0) && ! Leader_has_tech (&leaders[city->Body.CivID], __, prereq_id)) + if (! leader_can_build_district (&leaders[city->Body.CivID], district_id)) return false; // Check resource prerequisites (city connection) - ALL must be present @@ -8896,6 +9281,98 @@ city_can_build_district (City * city, int district_id) return true; } +bool +leader_can_build_district (Leader * leader, int district_id) +{ + if ((leader == NULL) || (district_id < 0) || (district_id >= is->district_count)) + return false; + + struct district_config const * cfg = &is->district_configs[district_id]; + struct district_infos const * info = &is->district_infos[district_id]; + + int prereq_id = info->advance_prereq_id; + if ((prereq_id >= 0) && ! Leader_has_tech (leader, __, prereq_id)) + return false; + + if (cfg->has_buildable_by_civs) { + bool civ_match = false; + if (cfg->buildable_by_civ_count > 0) { + Race * race = (leader->RaceID >= 0 && leader->RaceID < p_bic_data->RacesCount) + ? &p_bic_data->Races[leader->RaceID] + : NULL; + if (race == NULL) + return false; + for (int i = 0; i < cfg->buildable_by_civ_count; i++) { + char const * civ_name = cfg->buildable_by_civs[i]; + if ((civ_name == NULL) || (civ_name[0] == '\0')) continue; + if ((race->CountryName != NULL) && (strcmp (civ_name, race->CountryName) == 0)) civ_match = true; break; + if ((race->SingularName != NULL) && (strcmp (civ_name, race->SingularName) == 0)) civ_match = true; break; + if ((race->AdjectiveName != NULL) && (strcmp (civ_name, race->AdjectiveName) == 0)) civ_match = true; break; + if ((race->LeaderName != NULL) && (strcmp (civ_name, race->LeaderName) == 0)) civ_match = true; break; + } + } + if (! civ_match) + return false; + } + + if (cfg->has_buildable_by_civ_traits) { + if (cfg->buildable_by_civ_traits_id_count <= 0) + return false; + Race * race = (leader->RaceID >= 0 && leader->RaceID < p_bic_data->RacesCount) + ? &p_bic_data->Races[leader->RaceID] + : NULL; + if (race == NULL || race->vtable == NULL) + return false; + bool trait_match = false; + for (int i = 0; i < cfg->buildable_by_civ_traits_id_count; i++) { + int trait_id = cfg->buildable_by_civ_traits_ids[i]; + if (trait_id >= 0 && race->vtable->CheckBonus (race, __, trait_id)) { + trait_match = true; + break; + } + } + if (! trait_match) + return false; + } + + if (cfg->has_buildable_by_civ_govs) { + if (cfg->buildable_by_civ_govs_id_count <= 0) + return false; + int gov_id = leader->GovernmentType; + bool gov_match = false; + for (int i = 0; i < cfg->buildable_by_civ_govs_id_count; i++) { + if (cfg->buildable_by_civ_govs_ids[i] == gov_id) { + gov_match = true; + break; + } + } + if (! gov_match) + return false; + } + + if (cfg->has_buildable_by_civ_cultures) { + if (cfg->buildable_by_civ_cultures_id_count <= 0) + return false; + Race * race = (leader->RaceID >= 0 && leader->RaceID < p_bic_data->RacesCount) + ? &p_bic_data->Races[leader->RaceID] + : NULL; + if (race == NULL) + return false; + int culture_id = race->CultureGroupID; + bool culture_match = false; + for (int i = 0; i < cfg->buildable_by_civ_cultures_id_count; i++) { + if (cfg->buildable_by_civ_cultures_ids[i] == culture_id) { + culture_match = true; + break; + } + } + if (! culture_match) + return false; + } + + return true; +} + Tile * find_tile_for_district (City * city, int district_id, int * out_x, int * out_y) { @@ -9264,8 +9741,8 @@ ensure_neighborhood_request_for_city (City * city) (city == NULL)) return; - int prereq = is->district_infos[NEIGHBORHOOD_DISTRICT_ID].advance_prereq_id; - if ((prereq >= 0) && ! Leader_has_tech (&leaders[city->Body.CivID], __, prereq)) return; + if (! leader_can_build_district (&leaders[city->Body.CivID], NEIGHBORHOOD_DISTRICT_ID)) + return; mark_city_needs_district (city, NEIGHBORHOOD_DISTRICT_ID); } @@ -14341,12 +14818,8 @@ issue_district_worker_command (Unit * unit, int command) if ((district_id < 0) || (district_id >= is->district_count)) return; - // Check tech prerequisite for the selected district, if any - int prereq_id = is->district_infos[district_id].advance_prereq_id; - // Only enforce if a prereq is configured - if ((prereq_id >= 0) && !Leader_has_tech (&leaders[unit->Body.CivID], __, prereq_id)) { - return; // Civ lacks required tech; do not issue command - } + if (! leader_can_build_district (&leaders[unit->Body.CivID], district_id)) + return; // Disallow placing districts on invalid terrain, pollution, or cratered tiles if (tile->vtable->m21_Check_Crates (tile, __, 0)) @@ -15741,9 +16214,7 @@ patch_Leader_can_do_worker_job (Leader * this, int edx, enum Worker_Jobs job, in tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y); int owner_civ = tile->vtable->m38_Get_Territory_OwnerID (tile); if ((owner_civ == this->ID) || (owner_civ == 0)) { - // Check if the leader has the tech prereq for this district - int prereq_id = is->district_infos[inst->district_type].advance_prereq_id; - if ((prereq_id < 0 || Leader_has_tech (this, __, prereq_id)) && + if (leader_can_build_district (this, inst->district_type) && district_resource_prereqs_met (tile, tile_x, tile_y, inst->district_type, NULL)) tr = 1; } @@ -16365,9 +16836,7 @@ city_meets_district_prereqs_to_build_improvement (City * city, int i_improv, boo if (! needs_district) return true; - // Ensure prereq tech for the district - int prereq_id = is->district_infos[required_district_id].advance_prereq_id; - if ((prereq_id >= 0) && ! Leader_has_tech (&leaders[city->Body.CivID], __, prereq_id)) + if (! leader_can_build_district (&leaders[city->Body.CivID], required_district_id)) return false; Improvement * improv = &p_bic_data->Improvements[i_improv]; @@ -20473,8 +20942,7 @@ patch_Leader_do_production_phase (Leader * this) if (info->dependent_building_count > 0) continue; if (cfg->command == -1) continue; - int prereq_id = info->advance_prereq_id; - if ((prereq_id >= 0) && ! Leader_has_tech (this, __, prereq_id)) + if (! leader_can_build_district (this, district_id)) continue; if (auto_dynamic_district_count < ARRAY_LEN (auto_dynamic_district_ids)) @@ -20485,14 +20953,12 @@ patch_Leader_do_production_phase (Leader * this) // Special exception for AI to build Central Rail Hub, which is available in Industrial era but Mass Transit, // the only building dependent on it, is in the Modern era. Without this the AI wouldn't build in Industrial era. if (is->current_config.enable_central_rail_hub_districts) { - int prereq_id = is->district_infos[CENTRAL_RAIL_HUB_DISTRICT_ID].advance_prereq_id; - if ((prereq_id < 0) || Leader_has_tech (this, __, prereq_id)) + if (leader_can_build_district (this, CENTRAL_RAIL_HUB_DISTRICT_ID)) auto_dynamic_district_ids[auto_dynamic_district_count++] = CENTRAL_RAIL_HUB_DISTRICT_ID; } if (is->current_config.enable_distribution_hub_districts) { - int prereq_id = is->district_infos[DISTRIBUTION_HUB_DISTRICT_ID].advance_prereq_id; - if ((prereq_id < 0) || Leader_has_tech (this, __, prereq_id)) + if (leader_can_build_district (this, DISTRIBUTION_HUB_DISTRICT_ID)) ai_update_distribution_hub_goal_for_leader (this); } From b719bbfbc9860952d7111fb2db4d7b7fc3236a01 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 11 Jan 2026 14:48:39 -0800 Subject: [PATCH 162/356] Finish 1st version of bridge art --- Art/Districts/1200/Bridge.PCX | Bin 17013 -> 24561 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Bridge.PCX b/Art/Districts/1200/Bridge.PCX index 41096408a70cf285deb140504dea05bd6424d356..8ca4c8c51cbcc01f457461f02a1f761fa3cd063c 100644 GIT binary patch delta 15315 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�*=>)wepwg-scrb^JRx+J}>C?Dp) zYcI`722JR+nzG_HOfP#4J&$D`-~p#iNW+i{jyW0wa1{JsAF(ElG&aBt^2Lc-;Q>&Z zx$zf$FT0bD(OXBUu>$$fdb}{Uj_oZ=<`-|HJ-mSKZ<@!lZu67E!M^-FQLxvG$(81f zxe-!98~0-b_{WW;6(w048Ope%vJNLInv-jdcsr`Nno+vRpNjyleU1!m^&$ci_y@HH z9}IUd)A(I>yNlm=#0U>L>8)!Z*R;iJAIzu>^@>H9qMJ=uMr(0H*>GCgw{N0{@)rMCU zvnxOKL9Yo#C4l7aYSOJxrj;#7lZ#PUEiCCt-Uj}Q zIjF`|tH7HQ?75xrIPO#RDbWe;In8jZovZWHj3#T>I-y9lLYf@oj}MwM3ZWrLwwiD+xoTLJ`N!EqM8ne-0gbVJDvk`^eN$Cqs``5=kh!B*Mmko1cv z^xhmjL&v(p%$T61a9PVyV#()v&rFRfKH!HA62{3{_qWiISxgOO_p+9+nv=Ldc_<&Tk6%+058c?D8}Kf2@y320u(B+ zvA|MmQ9WNw_uujEs@$QCO=gN5^kis3&!533rVx@*;Azj{$01me`- zb;wFmT`xM56x4u~wtCcaHQ%8kjTQSWqnHit!>|c!pGpsRuyttD{}-^Vvbv`ImSw&b zc*=EIMxfMwLzdl*6=~H)(?#2rQ!K1`12-O3Zu!t>{O<*6lro3g=-H#m3P>SBqh6~g`ls}!tk&}>#1W4pG?^@TL zr*vmj6EBt)3JaRuWXXs*7@(Ac0u!Tj&VEM7j?tn_s`b)<&p-p_o4vQlC(c!e@v03y zRDo!I5;7G4*0e96H9cjKnhCs7(8Tdxy`gwlYjN+1XAhdQfS=6BQ+-XbgtwHpBFmfD zXU+tEGfLNFfR2`S`{-a#+*|5Xl&Y27oNj89>d(c(p{OQcx^7Bit&KUXrck|J!@R@) zr=+j}BsALU)eRD_!6+?`If@IKR+@;~p}*IVZjS({w5+&|5bMwjn>u}kgG`NtV>gB>PS(?OaqZRn;W@R^VOKfQCR*`#;i0WNtr zPIclM1`;JcX_i4cK(5c+Lg^$(rgvd)k+zjeq1(&(3!ZJIIGE83mseemF<4O>mjN+) zNmj=f^j`H!z+- zSV4Fzm)_^FO79U*6H6}13g`Xl;Go`@O^|<(yK7d>O;@bKyIQOR@gerY6p|lu8BXss z=Rja&#%#Sj8+VB<-mNWd4&?NTvkv)UQ>za2N_AATB8|y_1C1|^2#arj>s{uK z{FnP5e1w%4kuU>8&HT+o=AQg|>u()2+;5XLj`Ck;zP$g<6?LW;e}Cb4DovfnZ1`jc PyItpt;s4;f|4;ru4yjXo delta 7149 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 Bvqt~` From 6532df0dafe997350e1051909319f502653ee1f8 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 11 Jan 2026 14:53:12 -0800 Subject: [PATCH 163/356] Begin bridge implementation --- C3X.h | 11 ++++++++++- Civ3Conquests.h | 1 + default.districts_config.txt | 20 ++++++++++++++++++++ injected_code.c | 7 ++++++- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/C3X.h b/C3X.h index aedcaed5..cccf160b 100644 --- a/C3X.h +++ b/C3X.h @@ -17,7 +17,7 @@ typedef unsigned char byte; #define MAX_BUILDING_PREREQS_FOR_UNIT 10 #define COUNT_SPECIAL_DISTRICT_TYPES 10 -#define USED_SPECIAL_DISTRICT_TYPES 8 +#define USED_SPECIAL_DISTRICT_TYPES 9 #define MAX_DYNAMIC_DISTRICT_TYPES 22 #define COUNT_DISTRICT_TYPES (COUNT_SPECIAL_DISTRICT_TYPES + MAX_DYNAMIC_DISTRICT_TYPES) #define MAX_WONDER_DISTRICT_TYPES 32 @@ -763,6 +763,15 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 2, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 + }, + { + .command = UCV_Build_Bridge, .name = "Bridge", .tooltip = "Build Bridge", .display_name = "Bridge", + .advance_prereq = "Industrialization", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, + .img_paths = {"Bridge.pcx"}, .dependent_improvements = {0}, + .buildable_square_types_mask = (1 << SQ_Coast), + .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, + .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 } }; diff --git a/Civ3Conquests.h b/Civ3Conquests.h index 8e2ea1d5..9313514a 100644 --- a/Civ3Conquests.h +++ b/Civ3Conquests.h @@ -775,6 +775,7 @@ enum Unit_Command_Values UCV_Build_Port = -10000005, UCV_Build_CentralRailHub = -10000006, UCV_Build_EnergyGrid = -10000007, + UCV_Build_Bridge = -10000008, }; enum Unit_Mode_Actions diff --git a/default.districts_config.txt b/default.districts_config.txt index cc28e9d1..65317cf9 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -318,4 +318,24 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 2 +happiness_bonus = 0 + +#District +name = Bridge +tooltip = Build Bridge +img_paths = Bridge.pcx +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 7 +vary_img_by_era = 1 +vary_img_by_culture = 0 +advance_prereq = Industrialization +dependent_improvs = +buildable_on = coast +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 happiness_bonus = 0 \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index e8e3f284..04aebda1 100644 --- a/injected_code.c +++ b/injected_code.c @@ -73,7 +73,8 @@ struct injected_state * is = ADDR_INJECTED_STATE; #define NATURAL_WONDER_DISTRICT_ID 4 #define PORT_DISTRICT_ID 5 #define CENTRAL_RAIL_HUB_DISTRICT_ID 6 -#define ENERGY_GRID_DISTRICT_ID 7 +#define ENERGY_GRID_DISTRICT_ID 7 +#define BRIDGE_DISTRICT_ID 8 char const * const hotseat_replay_save_path = "Saves\\Auto\\ai-move-replay-before-interturn.SAV"; char const * const hotseat_resume_save_path = "Saves\\Auto\\ai-move-replay-resume.SAV"; @@ -26587,6 +26588,10 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par buildings = get_energy_grid_image_index (tile_x, tile_y); break; } + case BRIDGE_DISTRICT_ID: + { + buildings = get_bridge_orientation (tile); + } default: { struct district_infos * info = &is->district_infos[district_id]; From 4428fca69e52583451c83a2cc38cad4bf5989001 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 11 Jan 2026 15:19:02 -0800 Subject: [PATCH 164/356] Initial implementation attempt with bridges --- C3X.h | 2 + injected_code.c | 228 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 217 insertions(+), 13 deletions(-) diff --git a/C3X.h b/C3X.h index cccf160b..a19f0193 100644 --- a/C3X.h +++ b/C3X.h @@ -346,6 +346,7 @@ struct c3x_config { bool enable_distribution_hub_districts; bool enable_aerodrome_districts; bool enable_port_districts; + bool enable_bridge_districts; bool enable_central_rail_hub_districts; bool cities_with_mutual_district_receive_buildings; @@ -374,6 +375,7 @@ struct c3x_config { bool workers_can_enter_coast; bool expand_water_tile_checks_to_city_work_area; + int max_contiguous_bridge_districts; bool ai_defends_districts; diff --git a/injected_code.c b/injected_code.c index 04aebda1..f16160ab 100644 --- a/injected_code.c +++ b/injected_code.c @@ -8733,6 +8733,39 @@ district_resource_prereqs_met (Tile * tile, int tile_x, int tile_y, int district return true; } +bool +tile_has_bridge_district_at (int tile_x, int tile_y) +{ + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + if ((tile == NULL) || (tile == p_null_tile)) + return false; + + struct district_instance * inst = get_district_instance (tile); + return (inst != NULL) && (inst->district_type == BRIDGE_DISTRICT_ID); +} + +int +count_contiguous_bridge_districts (int tile_x, int tile_y, int dx, int dy) +{ + Map * map = &p_bic_data->Map; + int nx = tile_x + dx; + int ny = tile_y + dy; + int count = 0; + int max_steps = map->Width + map->Height; + + for (int step = 0; step < max_steps; step++) { + wrap_tile_coords (map, &nx, &ny); + if (! tile_has_bridge_district_at (nx, ny)) + break; + count++; + nx += dx; + ny += dy; + } + + return count; +} + bool can_build_district_on_tile (Tile * tile, int district_id) { @@ -8753,6 +8786,7 @@ can_build_district_on_tile (Tile * tile, int district_id) if ((cfg->command == UCV_Build_DistributionHub) && !is->current_config.enable_distribution_hub_districts) return false; if ((cfg->command == UCV_Build_Aerodrome) && !is->current_config.enable_aerodrome_districts) return false; if ((cfg->command == UCV_Build_Port) && !is->current_config.enable_port_districts) return false; + if ((cfg->command == UCV_Build_Bridge) && !is->current_config.enable_bridge_districts) return false; if (! district_is_buildable_on_square_type (cfg, tile)) return false; @@ -8769,6 +8803,30 @@ can_build_district_on_tile (Tile * tile, int district_id) if (! district_resource_prereqs_met (tile, tile_x, tile_y, district_id, NULL)) return false; + if (district_id == BRIDGE_DISTRICT_ID && is->current_config.max_contiguous_bridge_districts > 0) { + int max_bridges = is->current_config.max_contiguous_bridge_districts; + + int ns_count = count_contiguous_bridge_districts (tile_x, tile_y, 0, -2) + + count_contiguous_bridge_districts (tile_x, tile_y, 0, 2); + if (ns_count > max_bridges) + return false; + + int we_count = count_contiguous_bridge_districts (tile_x, tile_y, -2, 0) + + count_contiguous_bridge_districts (tile_x, tile_y, 2, 0); + if (we_count > max_bridges) + return false; + + int swne_count = count_contiguous_bridge_districts (tile_x, tile_y, -1, 1) + + count_contiguous_bridge_districts (tile_x, tile_y, 1, -1); + if (swne_count > max_bridges) + return false; + + int nwse_count = count_contiguous_bridge_districts (tile_x, tile_y, -1, -1) + + count_contiguous_bridge_districts (tile_x, tile_y, 1, 1); + if (nwse_count > max_bridges) + return false; + } + struct district_instance * existing_inst = get_district_instance (tile); int existing_district_id = (existing_inst != NULL) ? existing_inst->district_type : -1; bool district_completed = district_is_complete (tile, existing_district_id); @@ -12752,6 +12810,7 @@ patch_init_floating_point () {"enable_distribution_hub_districts" , false, offsetof (struct c3x_config, enable_distribution_hub_districts)}, {"enable_aerodrome_districts" , false, offsetof (struct c3x_config, enable_aerodrome_districts)}, {"enable_port_districts" , false, offsetof (struct c3x_config, enable_port_districts)}, + {"enable_bridge_districts" , false, offsetof (struct c3x_config, enable_bridge_districts)}, {"enable_central_rail_hub_districts" , false, offsetof (struct c3x_config, enable_central_rail_hub_districts)}, {"completed_wonder_districts_can_be_destroyed" , false, offsetof (struct c3x_config, completed_wonder_districts_can_be_destroyed)}, {"destroyed_wonders_can_be_built_again" , false, offsetof (struct c3x_config, destroyed_wonders_can_be_built_again)}, @@ -12806,6 +12865,7 @@ patch_init_floating_point () {"central_rail_hub_distribution_food_bonus_percent" , 25, offsetof (struct c3x_config, central_rail_hub_distribution_food_bonus_percent)}, {"central_rail_hub_distribution_shield_bonus_percent", 25, offsetof (struct c3x_config, central_rail_hub_distribution_shield_bonus_percent)}, {"neighborhood_needed_message_frequency" , 4, offsetof (struct c3x_config, neighborhood_needed_message_frequency)}, + {"max_contiguous_bridge_districts" , 0, offsetof (struct c3x_config, max_contiguous_bridge_districts)}, }; is->kernel32 = (*p_GetModuleHandleA) ("kernel32.dll"); @@ -14075,6 +14135,8 @@ set_up_district_buttons (Main_GUI * this) if (is->district_configs[dc].command == -1) continue; + if ((dc == BRIDGE_DISTRICT_ID) && ! is->current_config.enable_bridge_districts) + continue; if (existing_district_id == dc && district_completed) continue; if ((existing_district_id >= 0) && (existing_district_id != dc) && (! district_completed)) continue; @@ -15508,6 +15570,23 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, base_validity = AMV_OK; } + // Allow land units to enter bridge tiles + if (is->current_config.enable_districts && + is->current_config.enable_bridge_districts && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Land) && + ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { + int nx, ny; + get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); + Tile * dest = tile_at (nx, ny); + if ((dest != NULL) && (dest != p_null_tile)) { + struct district_instance * inst = get_district_instance (dest); + if ((inst != NULL) && + (inst->district_type == BRIDGE_DISTRICT_ID) && + district_is_complete (dest, inst->district_type)) + base_validity = AMV_OK; + } + } + if ((base_validity == AMV_OK) && is->current_config.enable_districts && is->current_config.enable_port_districts && @@ -15566,6 +15645,21 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr base_cost = Unit_get_max_move_points (unit); } + // Let the pathfinder consider bridge tiles reachable for land units + if (is->current_config.enable_districts && + is->current_config.enable_bridge_districts && + (base_cost < 0) && (unit != NULL) && + (p_bic_data->UnitTypes[unit->Body.UnitTypeID].Unit_Class == UTC_Land)) { + Tile * dest = tile_at (to_x, to_y); + if ((dest != NULL) && (dest != p_null_tile)) { + struct district_instance * inst = get_district_instance (dest); + if ((inst != NULL) && + (inst->district_type == BRIDGE_DISTRICT_ID) && + district_is_complete (dest, inst->district_type)) + base_cost = Unit_get_max_move_points (unit); + } + } + if ((unit != NULL) && (base_cost >= 0) && is->current_config.enable_districts && @@ -21359,22 +21453,39 @@ patch_Unit_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, bool is->moving_unit_to_adjacent_tile = true; bool const allow_worker_coast = is->current_config.enable_districts && is->current_config.workers_can_enter_coast && is_worker (this); + bool const allow_bridge_walk = is->current_config.enable_districts && + is->current_config.enable_bridge_districts && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Land); bool coast_override_active = false; enum UnitStateType prev_state = this->Body.UnitState; int prev_container = this->Body.Container_Unit; - if (allow_worker_coast) { + if (allow_worker_coast || allow_bridge_walk) { int nx, ny; get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); Tile * dest = tile_at (nx, ny); - if ((dest != NULL) && - dest->vtable->m35_Check_Is_Water (dest) && - (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) { + if (dest != NULL) { + bool should_override = false; + if (allow_worker_coast && + dest->vtable->m35_Check_Is_Water (dest) && + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) + should_override = true; + + if (! should_override && allow_bridge_walk) { + struct district_instance * inst = get_district_instance (dest); + if ((inst != NULL) && + (inst->district_type == BRIDGE_DISTRICT_ID) && + district_is_complete (dest, inst->district_type)) + should_override = true; + } + + if (should_override) { coast_override_active = true; is->coast_walk_unit = this; is->coast_walk_transport_override = false; is->coast_walk_prev_state = prev_state; is->coast_walk_prev_container = prev_container; + } } } @@ -26440,6 +26551,66 @@ get_energy_grid_image_index (int tile_x, int tile_y) return index; } +int +get_bridge_image_index (Tile * tile) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return 0; + + int tile_x = 0; + int tile_y = 0; + struct district_instance * inst = get_district_instance (tile); + if (inst != NULL) { + tile_x = inst->tile_x; + tile_y = inst->tile_y; + } else { + tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y); + } + + bool bridge_n = tile_has_bridge_district_at (tile_x, tile_y - 2); + bool bridge_s = tile_has_bridge_district_at (tile_x, tile_y + 2); + bool bridge_w = tile_has_bridge_district_at (tile_x - 2, tile_y); + bool bridge_e = tile_has_bridge_district_at (tile_x + 2, tile_y); + bool bridge_ne = tile_has_bridge_district_at (tile_x + 1, tile_y - 1); + bool bridge_nw = tile_has_bridge_district_at (tile_x - 1, tile_y - 1); + bool bridge_se = tile_has_bridge_district_at (tile_x + 1, tile_y + 1); + bool bridge_sw = tile_has_bridge_district_at (tile_x - 1, tile_y + 1); + + if (bridge_n || bridge_s || bridge_w || bridge_e || bridge_ne || bridge_nw || bridge_se || bridge_sw) { + int ns_count = (bridge_n ? 1 : 0) + (bridge_s ? 1 : 0); + int we_count = (bridge_w ? 1 : 0) + (bridge_e ? 1 : 0); + int swne_count = (bridge_sw ? 1 : 0) + (bridge_ne ? 1 : 0); + int nwse_count = (bridge_nw ? 1 : 0) + (bridge_se ? 1 : 0); + + if (ns_count == 2) return 2; + if (we_count == 2) return 3; + if (swne_count == 2) return 0; + if (nwse_count == 2) return 1; + + if (ns_count == 1) return 2; + if (we_count == 1) return 3; + if (swne_count == 1) return 0; + if (nwse_count == 1) return 1; + } + + int owner_id = tile->Territory_OwnerID; + bool north_land = tile_offset_is_land (owner_id, tile_x, tile_y - 2, false); + bool south_land = tile_offset_is_land (owner_id, tile_x, tile_y + 2, false); + bool west_land = tile_offset_is_land (owner_id, tile_x - 2, tile_y, false); + bool east_land = tile_offset_is_land (owner_id, tile_x + 2, tile_y, false); + bool ne_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y - 1, false); + bool nw_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y - 1, false); + bool se_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y + 1, false); + bool sw_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y + 1, false); + + if (north_land || south_land) return 2; + if (west_land || east_land) return 3; + if (ne_land || sw_land) return 0; + if (nw_land || se_land) return 1; + + return 0; +} + void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { @@ -26512,7 +26683,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par era = leader->Era; if (cfg->align_to_coast) align_variant_and_pixel_offsets_with_coastline (tile, &variant, &pixel_x, &pixel_y); - } else if (district_id != WONDER_DISTRICT_ID && district_id != NATURAL_WONDER_DISTRICT_ID) { + } else if (district_id != WONDER_DISTRICT_ID && district_id != NATURAL_WONDER_DISTRICT_ID && district_id != BRIDGE_DISTRICT_ID) { Sprite * abandoned_sprite = &is->abandoned_district_img; if (tile->vtable->m35_Check_Is_Water (tile) && is->abandoned_maritime_district_img.vtable != NULL) abandoned_sprite = &is->abandoned_maritime_district_img; @@ -26590,7 +26761,8 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } case BRIDGE_DISTRICT_ID: { - buildings = get_bridge_orientation (tile); + buildings = get_bridge_image_index (tile); + break; } default: { @@ -27716,6 +27888,20 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t } } + if (is->current_config.enable_districts && + is->current_config.enable_bridge_districts && + base != PBV_OK && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Land)) { + Tile * dest = tile_at (to_x, to_y); + if ((dest != NULL) && (dest != p_null_tile)) { + struct district_instance * inst = get_district_instance (dest); + if ((inst != NULL) && + (inst->district_type == BRIDGE_DISTRICT_ID) && + district_is_complete (dest, inst->district_type)) + return PBV_OK; + } + } + return base; } @@ -27724,14 +27910,30 @@ patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool { Unit * transport = Unit_select_transport (this, __, tile_x, tile_y, do_show_popup); - if (is->current_config.enable_districts && is->current_config.workers_can_enter_coast && - (transport == NULL) && (this == is->coast_walk_unit) && is_worker (this)) { + if (is->current_config.enable_districts && + (transport == NULL) && (this == is->coast_walk_unit)) { Tile * dest = tile_at (tile_x, tile_y); - if ((dest != NULL) && - dest->vtable->m35_Check_Is_Water (dest) && - (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) { - is->coast_walk_transport_override = true; - return this; // Fake a transport so the move logic proceeds + if (dest != NULL) { + bool allow_move = false; + if (is->current_config.workers_can_enter_coast && is_worker (this) && + dest->vtable->m35_Check_Is_Water (dest) && + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) + allow_move = true; + + if (! allow_move && + is->current_config.enable_bridge_districts && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Land)) { + struct district_instance * inst = get_district_instance (dest); + if ((inst != NULL) && + (inst->district_type == BRIDGE_DISTRICT_ID) && + district_is_complete (dest, inst->district_type)) + allow_move = true; + } + + if (allow_move) { + is->coast_walk_transport_override = true; + return this; // Fake a transport so the move logic proceeds + } } } From 9799bcdba0cbcf0a2a6a377f675344193441b562 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 11 Jan 2026 16:15:37 -0800 Subject: [PATCH 165/356] Fix bridge rendering bug --- Art/Districts/1200/Bridge.PCX | Bin 24561 -> 31588 bytes Art/Districts/1200/OffshoreExtractionZone.PCX | Bin 4373 -> 4363 bytes C3X.h | 6 +++--- default.c3x_config.ini | 1 + default.districts_config.txt | 3 --- injected_code.c | 5 +++-- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Art/Districts/1200/Bridge.PCX b/Art/Districts/1200/Bridge.PCX index 8ca4c8c51cbcc01f457461f02a1f761fa3cd063c..cb62b9219d8fae47e8d075b9ea7ec7f9ebd77ed3 100644 GIT binary patch delta 25 hcmeykpYh2z#tAK(Ie0fKPMp9o`2ZWw<{NC1kpP}`3eErk delta 78 zcmaFzjq&4t#tAKxRh2|Gdn^57l0WqCKM+93gZ~a9@xWr6GgPNDvI0f_9p22MUd}i< NUxNpTIe?laBLN^xF&_W` diff --git a/Art/Districts/1200/OffshoreExtractionZone.PCX b/Art/Districts/1200/OffshoreExtractionZone.PCX index ebc4bae916056be771422968518c424226aa0ed8..dc4e966c3d343f01f51cf8abeee42d59b4a22887 100644 GIT binary patch delta 27 jcmbQL)UC9^myz-D=0L_9jEwgt>oJEh-rd~6+{+69ive0gI&&BckacTw9dj=)04+=p(EtDd diff --git a/C3X.h b/C3X.h index a19f0193..a6f9c805 100644 --- a/C3X.h +++ b/C3X.h @@ -753,7 +753,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereq = "Steam Power", .resource_prereqs = {"Iron", "Coal"}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 2, .dependent_improvement_count = 0, .img_paths = {"CentralRailHub_AMER.pcx", "CentralRailHub_EURO.pcx", "CentralRailHub_ROMAN.pcx", "CentralRailHub_MIDEAST.pcx", "CentralRailHub_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, - .img_path_count = 5, .max_building_index = 0, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, + .img_path_count = 5, .max_building_index = 1, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, @@ -762,7 +762,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereq = "Industrialization", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 4, .img_paths = {"EnergyGrid.pcx"}, .dependent_improvements = {"Coal Plant", "Hydro Plant", "Solar Plant", "Nuclear Plant"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .custom_height = 84, - .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, + .img_path_count = 1, .max_building_index = 14, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 2, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, @@ -771,7 +771,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereq = "Industrialization", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"Bridge.pcx"}, .dependent_improvements = {0}, .buildable_square_types_mask = (1 << SQ_Coast), - .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, + .img_path_count = 1, .max_building_index = 3, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 } diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 63bf0ff7..5cdc0321 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -836,6 +836,7 @@ enable_wonder_districts = false enable_distribution_hub_districts = false enable_aerodrome_districts = false enable_port_districts = false +enable_bridge_districts = false enable_central_rail_hub_districts = false ; When multiple cities share a district (i.e., the same district tile is within multiple cities' work radii), these options control whether those diff --git a/default.districts_config.txt b/default.districts_config.txt index 65317cf9..3f435dea 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -281,7 +281,6 @@ happiness_bonus = 0 #District name = Central Rail Hub tooltip = Build Central Rail Hub -img_paths = CentralRailHub_AMER.pcx, CentralRailHub_EURO.pcx, CentralRailHub_ROMAN.pcx, CentralRailHub_MIDEAST.pcx, CentralRailHub_ASIAN.pcx btn_tile_sheet_row = 1 btn_tile_sheet_column = 7 vary_img_by_era = 1 @@ -302,7 +301,6 @@ happiness_bonus = 0 #District name = Energy Grid tooltip = Build Energy Grid -img_paths = EnergyGrid.pcx btn_tile_sheet_row = 1 btn_tile_sheet_column = 7 vary_img_by_era = 1 @@ -323,7 +321,6 @@ happiness_bonus = 0 #District name = Bridge tooltip = Build Bridge -img_paths = Bridge.pcx btn_tile_sheet_row = 1 btn_tile_sheet_column = 7 vary_img_by_era = 1 diff --git a/injected_code.c b/injected_code.c index f16160ab..286a0084 100644 --- a/injected_code.c +++ b/injected_code.c @@ -8787,6 +8787,7 @@ can_build_district_on_tile (Tile * tile, int district_id) if ((cfg->command == UCV_Build_Aerodrome) && !is->current_config.enable_aerodrome_districts) return false; if ((cfg->command == UCV_Build_Port) && !is->current_config.enable_port_districts) return false; if ((cfg->command == UCV_Build_Bridge) && !is->current_config.enable_bridge_districts) return false; + if ((cfg->command == UCV_Build_CentralRailHub) && !is->current_config.enable_central_rail_hub_districts) return false; if (! district_is_buildable_on_square_type (cfg, tile)) return false; @@ -26778,8 +26779,8 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par } } - district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; - int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; + district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; + int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; int draw_x = pixel_x - ((sprite_width - 128) / 2); int draw_y = pixel_y - (sprite_height - 64); From 39ee666f7c6d0728ade25e862a75bd9151c0da12 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 11 Jan 2026 16:28:29 -0800 Subject: [PATCH 166/356] Add support for buildable_on irrigation and mines --- injected_code.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/injected_code.c b/injected_code.c index 286a0084..605844a2 100644 --- a/injected_code.c +++ b/injected_code.c @@ -1765,6 +1765,18 @@ square_type_mask_bit (enum SquareTypes type) return (unsigned int)(1u << type); } +unsigned int +district_buildable_mine_mask_bit (void) +{ + return (unsigned int)(1u << (SQ_SNOW_MOUNTAIN + 1)); +} + +unsigned int +district_buildable_irrigation_mask_bit (void) +{ + return (unsigned int)(1u << (SQ_SNOW_MOUNTAIN + 2)); +} + unsigned int all_square_types_mask (void) { @@ -1847,6 +1859,14 @@ tile_matches_square_type_mask (Tile * tile, unsigned int mask) return true; } + unsigned int mine_bit = district_buildable_mine_mask_bit (); + if ((mask & mine_bit) && tile->vtable->m18_Check_Mines (tile, __, 0)) + return true; + + unsigned int irrigation_bit = district_buildable_irrigation_mask_bit (); + if ((mask & irrigation_bit) && tile->vtable->m17_Check_Irrigation (tile, __, 0)) + return true; + return false; } @@ -5532,6 +5552,13 @@ parse_buildable_square_type_mask (struct string_slice const * value, struct string_slice item_slice = { .str = item_start, .len = (int)(item_end - item_start) }; if (item_slice.len > 0) { + if (slice_matches_str (&item_slice, "mine")) { + mask |= district_buildable_mine_mask_bit (); + entry_count += 1; + } else if (slice_matches_str (&item_slice, "irrigation")) { + mask |= district_buildable_irrigation_mask_bit (); + entry_count += 1; + } else { enum SquareTypes parsed; if (read_square_type_value (&item_slice, &parsed)) { if (parsed == (enum SquareTypes)SQ_INVALID) @@ -5546,6 +5573,7 @@ parse_buildable_square_type_mask (struct string_slice const * value, free (value_text); return false; } + } } if (*cursor == ',') { From 07c1d3d18c013dfd83b255917a0189ee0356f462 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 11 Jan 2026 16:35:40 -0800 Subject: [PATCH 167/356] Add contiguous bridge direction check --- injected_code.c | 84 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/injected_code.c b/injected_code.c index 605844a2..2a2f56d3 100644 --- a/injected_code.c +++ b/injected_code.c @@ -8794,6 +8794,65 @@ count_contiguous_bridge_districts (int tile_x, int tile_y, int dx, int dy) return count; } +bool +bridge_district_tile_is_valid (int tile_x, int tile_y) +{ + if (is->current_config.max_contiguous_bridge_districts > 0) { + int max_bridges = is->current_config.max_contiguous_bridge_districts; + + int ns_count = count_contiguous_bridge_districts (tile_x, tile_y, 0, -2) + + count_contiguous_bridge_districts (tile_x, tile_y, 0, 2); + if (ns_count > max_bridges) + return false; + + int we_count = count_contiguous_bridge_districts (tile_x, tile_y, -2, 0) + + count_contiguous_bridge_districts (tile_x, tile_y, 2, 0); + if (we_count > max_bridges) + return false; + + int swne_count = count_contiguous_bridge_districts (tile_x, tile_y, -1, 1) + + count_contiguous_bridge_districts (tile_x, tile_y, 1, -1); + if (swne_count > max_bridges) + return false; + + int nwse_count = count_contiguous_bridge_districts (tile_x, tile_y, -1, -1) + + count_contiguous_bridge_districts (tile_x, tile_y, 1, 1); + if (nwse_count > max_bridges) + return false; + } + + Map * map = &p_bic_data->Map; + int const adj_dx[8] = { 0, 0, -2, 2, -1, 1, -1, 1 }; + int const adj_dy[8] = { -2, 2, 0, 0, 1, -1, -1, 1 }; + + for (int i = 0; i < 8; i++) { + int nx = tile_x + adj_dx[i]; + int ny = tile_y + adj_dy[i]; + wrap_tile_coords (map, &nx, &ny); + if (! tile_has_bridge_district_at (nx, ny)) + continue; + + int cand_dx = -adj_dx[i]; + int cand_dy = -adj_dy[i]; + + for (int j = 0; j < 8; j++) { + int ox = nx + adj_dx[j]; + int oy = ny + adj_dy[j]; + wrap_tile_coords (map, &ox, &oy); + if ((ox == tile_x) && (oy == tile_y)) + continue; + if (! tile_has_bridge_district_at (ox, oy)) + continue; + + if (! ((adj_dx[j] == cand_dx) && (adj_dy[j] == cand_dy)) && + ! ((adj_dx[j] == -cand_dx) && (adj_dy[j] == -cand_dy))) + return false; + } + } + + return true; +} + bool can_build_district_on_tile (Tile * tile, int district_id) { @@ -8832,29 +8891,8 @@ can_build_district_on_tile (Tile * tile, int district_id) if (! district_resource_prereqs_met (tile, tile_x, tile_y, district_id, NULL)) return false; - if (district_id == BRIDGE_DISTRICT_ID && is->current_config.max_contiguous_bridge_districts > 0) { - int max_bridges = is->current_config.max_contiguous_bridge_districts; - - int ns_count = count_contiguous_bridge_districts (tile_x, tile_y, 0, -2) + - count_contiguous_bridge_districts (tile_x, tile_y, 0, 2); - if (ns_count > max_bridges) - return false; - - int we_count = count_contiguous_bridge_districts (tile_x, tile_y, -2, 0) + - count_contiguous_bridge_districts (tile_x, tile_y, 2, 0); - if (we_count > max_bridges) - return false; - - int swne_count = count_contiguous_bridge_districts (tile_x, tile_y, -1, 1) + - count_contiguous_bridge_districts (tile_x, tile_y, 1, -1); - if (swne_count > max_bridges) - return false; - - int nwse_count = count_contiguous_bridge_districts (tile_x, tile_y, -1, -1) + - count_contiguous_bridge_districts (tile_x, tile_y, 1, 1); - if (nwse_count > max_bridges) - return false; - } + if ((district_id == BRIDGE_DISTRICT_ID) && (! bridge_district_tile_is_valid (tile_x, tile_y))) + return false; struct district_instance * existing_inst = get_district_instance (tile); int existing_district_id = (existing_inst != NULL) ? existing_inst->district_type : -1; From 48561f0087a3429f0273c410eb2e54c631fd5424 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 13 Jan 2026 13:31:28 -0800 Subject: [PATCH 168/356] Add support for flexible tile- & improvement-based extra bonuses for districts; Have leader_can_build_district take civ_id as an arg instead of basing off tile owner, allowing bridges, etc on non-owner tiles --- C3X.h | 44 +++- default.districts_config.txt | 1 - injected_code.c | 450 +++++++++++++++++++++++++++++------ 3 files changed, 417 insertions(+), 78 deletions(-) diff --git a/C3X.h b/C3X.h index a6f9c805..0a6d721b 100644 --- a/C3X.h +++ b/C3X.h @@ -583,6 +583,28 @@ enum { DEFAULT_DISTRICT_BUILDABLE_MASK = (1 << SQ_Desert) | (1 << SQ_Plains) | (1 << SQ_Grassland) | (1 << SQ_Tundra) | (1 << SQ_FloodPlain) | (1 << SQ_Hills) }; +enum { + MAX_DISTRICT_BONUS_ENTRIES = 16 +}; + +enum district_bonus_entry_type { + DBET_TILE = 0, + DBET_BUILDING = 1 +}; + +struct district_bonus_entry { + enum district_bonus_entry_type type; + int bonus; + enum SquareTypes tile_type; + int building_id; + char const * building_name; +}; + +struct district_bonus_list { + int count; + struct district_bonus_entry entries[MAX_DISTRICT_BONUS_ENTRIES]; +}; + struct district_config { enum Unit_Command_Values command; char const * name; @@ -613,6 +635,12 @@ struct district_config { int gold_bonus; int shield_bonus; int happiness_bonus; + struct district_bonus_list culture_bonus_extras; + struct district_bonus_list science_bonus_extras; + struct district_bonus_list food_bonus_extras; + struct district_bonus_list gold_bonus_extras; + struct district_bonus_list shield_bonus_extras; + struct district_bonus_list happiness_bonus_extras; int defense_bonus_percent; char const * generated_resource; int generated_resource_id; @@ -804,6 +832,12 @@ struct parsed_district_definition { int gold_bonus; int shield_bonus; int happiness_bonus; + struct district_bonus_list culture_bonus_extras; + struct district_bonus_list science_bonus_extras; + struct district_bonus_list food_bonus_extras; + struct district_bonus_list gold_bonus_extras; + struct district_bonus_list shield_bonus_extras; + struct district_bonus_list happiness_bonus_extras; unsigned int buildable_square_types_mask; char * buildable_by_civs[32]; int buildable_by_civ_count; @@ -837,19 +871,19 @@ struct parsed_district_definition { int generated_resource_settings_count; bool has_generated_resource; bool has_generated_resource_settings; - char * buildable_by_civ_traits[32]; + char * buildable_by_civ_traits[10]; int buildable_by_civ_traits_count; - int buildable_by_civ_traits_ids[32]; + int buildable_by_civ_traits_ids[10]; int buildable_by_civ_traits_id_count; bool has_buildable_by_civ_traits; - char * buildable_by_civ_govs[32]; + char * buildable_by_civ_govs[10]; int buildable_by_civ_govs_count; int buildable_by_civ_govs_ids[32]; int buildable_by_civ_govs_id_count; bool has_buildable_by_civ_govs; - char * buildable_by_civ_cultures[32]; + char * buildable_by_civ_cultures[5]; int buildable_by_civ_cultures_count; - int buildable_by_civ_cultures_ids[32]; + int buildable_by_civ_cultures_ids[5]; int buildable_by_civ_cultures_id_count; bool has_buildable_by_civ_cultures; }; diff --git a/default.districts_config.txt b/default.districts_config.txt index 3f435dea..c3b53946 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -326,7 +326,6 @@ btn_tile_sheet_column = 7 vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereq = Industrialization -dependent_improvs = buildable_on = coast defense_bonus_percent = 0 allow_multiple = 1 diff --git a/injected_code.c b/injected_code.c index 2a2f56d3..38bbadfd 100644 --- a/injected_code.c +++ b/injected_code.c @@ -219,6 +219,7 @@ bool city_radius_contains_tile (City * city, int tile_x, int tile_y); void on_distribution_hub_completed (Tile * tile, int tile_x, int tile_y); bool ai_move_district_worker (Unit * worker, struct district_worker_record * rec); bool has_active_building (City * city, int improv_id); +bool tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, int district_id, int i_improv); void recompute_distribution_hub_totals (); void get_neighbor_coords (Map * map, int x, int y, int neighbor_index, int * out_x, int * out_y); void wrap_tile_coords (Map * map, int * x, int * y); @@ -2686,6 +2687,36 @@ assign_natural_wonder_to_tile (Tile * tile, int tile_x, int tile_y, int natural_ set_tile_unworkable_for_all_cities (tile, tile_x, tile_y); } +int +apply_district_bonus_entries (struct district_instance * inst, + struct district_bonus_list const * extras, + int district_id) +{ + if ((inst == NULL) || (extras == NULL) || (extras->count <= 0)) + return 0; + + int tile_x = inst->tile_x; + int tile_y = inst->tile_y; + Tile * tile = tile_at (tile_x, tile_y); + if ((tile == NULL) || (tile == p_null_tile)) + return 0; + + int bonus = 0; + for (int i = 0; i < extras->count; i++) { + struct district_bonus_entry const * entry = &extras->entries[i]; + if (entry->type == DBET_TILE) { + if (tile_matches_square_type (tile, entry->tile_type)) + bonus += entry->bonus; + } else if (entry->type == DBET_BUILDING) { + if (entry->building_id >= 0 && + tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, district_id, entry->building_id)) + bonus += entry->bonus; + } + } + + return bonus; +} + void get_effective_district_yields (struct district_instance * inst, struct district_config const * cfg, @@ -2699,22 +2730,30 @@ get_effective_district_yields (struct district_instance * inst, int food = 0, shields = 0, gold = 0, science = 0, culture = 0, happiness = 0; if (cfg != NULL && is->current_config.enable_districts) { - food = cfg->food_bonus; - shields = cfg->shield_bonus; - gold = cfg->gold_bonus; - science = cfg->science_bonus; - culture = cfg->culture_bonus; + food = cfg->food_bonus; + shields = cfg->shield_bonus; + gold = cfg->gold_bonus; + science = cfg->science_bonus; + culture = cfg->culture_bonus; happiness = cfg->happiness_bonus; + + int district_id = (inst != NULL) ? inst->district_type : -1; + food += apply_district_bonus_entries (inst, &cfg->food_bonus_extras, district_id); + shields += apply_district_bonus_entries (inst, &cfg->shield_bonus_extras, district_id); + gold += apply_district_bonus_entries (inst, &cfg->gold_bonus_extras, district_id); + science += apply_district_bonus_entries (inst, &cfg->science_bonus_extras, district_id); + culture += apply_district_bonus_entries (inst, &cfg->culture_bonus_extras, district_id); + happiness += apply_district_bonus_entries (inst, &cfg->happiness_bonus_extras, district_id); } if (inst != NULL && is->current_config.enable_natural_wonders && inst->district_type == NATURAL_WONDER_DISTRICT_ID) { struct natural_wonder_district_config const * nwcfg = get_natural_wonder_config_by_id (inst->natural_wonder_info.natural_wonder_id); if (nwcfg != NULL) { - food += nwcfg->food_bonus; - shields += nwcfg->shield_bonus; - gold += nwcfg->gold_bonus; - science += nwcfg->science_bonus; - culture += nwcfg->culture_bonus; + food += nwcfg->food_bonus; + shields += nwcfg->shield_bonus; + gold += nwcfg->gold_bonus; + science += nwcfg->science_bonus; + culture += nwcfg->culture_bonus; happiness += nwcfg->happiness_bonus; } } @@ -5009,6 +5048,58 @@ copy_trimmed_string_or_null (struct string_slice const * slice, int remove_quote return extract_slice (&trimmed); } +void +free_bonus_entry_list (struct district_bonus_list * list) +{ + if (list == NULL) + return; + + for (int i = 0; i < list->count; i++) { + if (list->entries[i].type == DBET_BUILDING && + list->entries[i].building_name != NULL) { + free ((void *)list->entries[i].building_name); + list->entries[i].building_name = NULL; + } + } + list->count = 0; +} + +void +free_bonus_entry_list_override (struct district_bonus_list * list, + struct district_bonus_list const * defaults) +{ + if (list == NULL) + return; + + for (int i = 0; i < list->count; i++) { + if (list->entries[i].type == DBET_BUILDING && + list->entries[i].building_name != NULL) { + char const * default_name = NULL; + if ((defaults != NULL) && (i < defaults->count)) + default_name = defaults->entries[i].building_name; + if (list->entries[i].building_name != default_name) + free ((void *)list->entries[i].building_name); + list->entries[i].building_name = NULL; + } + } + list->count = (defaults != NULL) ? defaults->count : 0; +} + +void +move_bonus_entry_list (struct district_bonus_list * dest, + struct district_bonus_list * src) +{ + if ((dest == NULL) || (src == NULL)) + return; + + dest->count = src->count; + for (int i = 0; i < src->count; i++) { + dest->entries[i] = src->entries[i]; + src->entries[i].building_name = NULL; + } + src->count = 0; +} + void free_dynamic_district_config (struct district_config * cfg) { @@ -5084,6 +5175,13 @@ free_dynamic_district_config (struct district_config * cfg) } } + free_bonus_entry_list (&cfg->culture_bonus_extras); + free_bonus_entry_list (&cfg->science_bonus_extras); + free_bonus_entry_list (&cfg->food_bonus_extras); + free_bonus_entry_list (&cfg->gold_bonus_extras); + free_bonus_entry_list (&cfg->shield_bonus_extras); + free_bonus_entry_list (&cfg->happiness_bonus_extras); + memset (cfg, 0, sizeof *cfg); } @@ -5219,6 +5317,13 @@ free_special_district_override_strings (struct district_config * cfg, struct dis cfg->img_paths[i] = NULL; } cfg->img_path_count = defaults->img_path_count; + + free_bonus_entry_list_override (&cfg->culture_bonus_extras, &defaults->culture_bonus_extras); + free_bonus_entry_list_override (&cfg->science_bonus_extras, &defaults->science_bonus_extras); + free_bonus_entry_list_override (&cfg->food_bonus_extras, &defaults->food_bonus_extras); + free_bonus_entry_list_override (&cfg->gold_bonus_extras, &defaults->gold_bonus_extras); + free_bonus_entry_list_override (&cfg->shield_bonus_extras, &defaults->shield_bonus_extras); + free_bonus_entry_list_override (&cfg->happiness_bonus_extras, &defaults->happiness_bonus_extras); } void @@ -5387,6 +5492,13 @@ free_parsed_district_definition (struct parsed_district_definition * def) } def->generated_resource_settings_count = 0; + free_bonus_entry_list (&def->culture_bonus_extras); + free_bonus_entry_list (&def->science_bonus_extras); + free_bonus_entry_list (&def->food_bonus_extras); + free_bonus_entry_list (&def->gold_bonus_extras); + free_bonus_entry_list (&def->shield_bonus_extras); + free_bonus_entry_list (&def->happiness_bonus_extras); + init_parsed_district_definition (def); } @@ -5598,6 +5710,151 @@ parse_buildable_square_type_mask (struct string_slice const * value, return true; } +bool +parse_district_bonus_entries (struct string_slice const * value, + int * out_base_bonus, + struct district_bonus_list * out_extras, + struct error_line ** parse_errors, + int line_number, + struct string_slice const * key) +{ + if ((out_base_bonus == NULL) || (out_extras == NULL)) { + add_key_parse_error (parse_errors, line_number, key, value, "(invalid bonus target)"); + return false; + } + + char * value_text = trim_and_extract_slice (value, 0); + free_bonus_entry_list (out_extras); + *out_base_bonus = 0; + + if ((value_text == NULL) || (*value_text == '\0')) { + add_key_parse_error (parse_errors, line_number, key, value, "(expected base bonus)"); + free (value_text); + return false; + } + + bool got_base = false; + int base_bonus = 0; + + char * cursor = value_text; + while (1) { + while (is_space_char (*cursor)) + cursor++; + + if (*cursor == '\0') + break; + + char * item_start = cursor; + bool in_quotes = false; + while (*cursor != '\0') { + if (*cursor == '"') + in_quotes = ! in_quotes; + if ((! in_quotes) && (*cursor == ',')) + break; + cursor++; + } + + char * item_end = cursor; + while ((item_end > item_start) && is_space_char (item_end[-1])) + item_end--; + while ((item_start < item_end) && is_space_char (*item_start)) + item_start++; + + if (item_end > item_start) { + struct string_slice item = { .str = item_start, .len = (int)(item_end - item_start) }; + + if (! got_base) { + struct string_slice base_slice = trim_string_slice (&item, 0); + if (! read_int (&base_slice, &base_bonus)) { + add_key_parse_error (parse_errors, line_number, key, value, "(expected base bonus)"); + free_bonus_entry_list (out_extras); + free (value_text); + return false; + } + got_base = true; + } else { + char * colon = NULL; + in_quotes = false; + for (char * p = item_start; p < item_end; p++) { + if (*p == '"') + in_quotes = ! in_quotes; + if ((! in_quotes) && (*p == ':')) { + colon = p; + break; + } + } + + if (colon == NULL) { + add_key_parse_error (parse_errors, line_number, key, value, "(expected \"name: bonus\" entry)"); + free_bonus_entry_list (out_extras); + free (value_text); + return false; + } + + struct string_slice name_slice = { .str = item_start, .len = (int)(colon - item_start) }; + struct string_slice bonus_slice = { .str = colon + 1, .len = (int)(item_end - (colon + 1)) }; + struct string_slice trimmed_name = trim_string_slice (&name_slice, 1); + struct string_slice trimmed_bonus = trim_string_slice (&bonus_slice, 0); + + if (trimmed_name.len <= 0) { + add_key_parse_error (parse_errors, line_number, key, value, "(expected bonus name)"); + free_bonus_entry_list (out_extras); + free (value_text); + return false; + } + + int bonus_value = 0; + if (! read_int (&trimmed_bonus, &bonus_value)) { + add_key_parse_error (parse_errors, line_number, key, value, "(expected bonus value)"); + free_bonus_entry_list (out_extras); + free (value_text); + return false; + } + + if (out_extras->count >= MAX_DISTRICT_BONUS_ENTRIES) { + add_key_parse_error (parse_errors, line_number, key, value, "(too many bonus entries)"); + free_bonus_entry_list (out_extras); + free (value_text); + return false; + } + + struct district_bonus_entry * entry = &out_extras->entries[out_extras->count++]; + entry->bonus = bonus_value; + entry->building_id = -1; + entry->building_name = NULL; + + enum SquareTypes parsed_type; + if (read_square_type_value (&trimmed_name, &parsed_type)) { + entry->type = DBET_TILE; + entry->tile_type = parsed_type; + } else { + entry->type = DBET_BUILDING; + entry->tile_type = (enum SquareTypes)SQ_INVALID; + entry->building_name = extract_slice (&trimmed_name); + } + } + } + + if (*cursor == ',') { + cursor++; + continue; + } + if (*cursor == '\0') + break; + } + + free (value_text); + + if (! got_base) { + add_key_parse_error (parse_errors, line_number, key, value, "(expected base bonus)"); + free_bonus_entry_list (out_extras); + return false; + } + + *out_base_bonus = base_bonus; + return true; +} + bool override_special_district_from_definition (struct parsed_district_definition * def, int section_start_line) { @@ -5731,18 +5988,36 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->btn_tile_sheet_row = def->btn_tile_sheet_row; if (def->has_defense_bonus_percent) cfg->defense_bonus_percent = def->defense_bonus_percent; - if (def->has_culture_bonus) + if (def->has_culture_bonus) { cfg->culture_bonus = def->culture_bonus; - if (def->has_science_bonus) + free_bonus_entry_list_override (&cfg->culture_bonus_extras, &defaults->culture_bonus_extras); + move_bonus_entry_list (&cfg->culture_bonus_extras, &def->culture_bonus_extras); + } + if (def->has_science_bonus) { cfg->science_bonus = def->science_bonus; - if (def->has_food_bonus) + free_bonus_entry_list_override (&cfg->science_bonus_extras, &defaults->science_bonus_extras); + move_bonus_entry_list (&cfg->science_bonus_extras, &def->science_bonus_extras); + } + if (def->has_food_bonus) { cfg->food_bonus = def->food_bonus; - if (def->has_gold_bonus) + free_bonus_entry_list_override (&cfg->food_bonus_extras, &defaults->food_bonus_extras); + move_bonus_entry_list (&cfg->food_bonus_extras, &def->food_bonus_extras); + } + if (def->has_gold_bonus) { cfg->gold_bonus = def->gold_bonus; - if (def->has_shield_bonus) + free_bonus_entry_list_override (&cfg->gold_bonus_extras, &defaults->gold_bonus_extras); + move_bonus_entry_list (&cfg->gold_bonus_extras, &def->gold_bonus_extras); + } + if (def->has_shield_bonus) { cfg->shield_bonus = def->shield_bonus; - if (def->has_happiness_bonus) + free_bonus_entry_list_override (&cfg->shield_bonus_extras, &defaults->shield_bonus_extras); + move_bonus_entry_list (&cfg->shield_bonus_extras, &def->shield_bonus_extras); + } + if (def->has_happiness_bonus) { cfg->happiness_bonus = def->happiness_bonus; + free_bonus_entry_list_override (&cfg->happiness_bonus_extras, &defaults->happiness_bonus_extras); + move_bonus_entry_list (&cfg->happiness_bonus_extras, &def->happiness_bonus_extras); + } if (def->has_buildable_on) cfg->buildable_square_types_mask = def->buildable_square_types_mask; @@ -5941,6 +6216,19 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.happiness_bonus = def->has_happiness_bonus ? def->happiness_bonus : 0; new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); + if (def->has_culture_bonus) + move_bonus_entry_list (&new_cfg.culture_bonus_extras, &def->culture_bonus_extras); + if (def->has_science_bonus) + move_bonus_entry_list (&new_cfg.science_bonus_extras, &def->science_bonus_extras); + if (def->has_food_bonus) + move_bonus_entry_list (&new_cfg.food_bonus_extras, &def->food_bonus_extras); + if (def->has_gold_bonus) + move_bonus_entry_list (&new_cfg.gold_bonus_extras, &def->gold_bonus_extras); + if (def->has_shield_bonus) + move_bonus_entry_list (&new_cfg.shield_bonus_extras, &def->shield_bonus_extras); + if (def->has_happiness_bonus) + move_bonus_entry_list (&new_cfg.happiness_bonus_extras, &def->happiness_bonus_extras); + if (def->has_generated_resource) { new_cfg.generated_resource = def->generated_resource; def->generated_resource = NULL; @@ -6391,58 +6679,46 @@ handle_district_definition_key (struct parsed_district_definition * def, add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "culture_bonus")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->culture_bonus = ival; + if (parse_district_bonus_entries (value, &def->culture_bonus, &def->culture_bonus_extras, parse_errors, line_number, key)) { def->has_culture_bonus = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else { + def->has_culture_bonus = false; + } } else if (slice_matches_str (key, "science_bonus")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->science_bonus = ival; + if (parse_district_bonus_entries (value, &def->science_bonus, &def->science_bonus_extras, parse_errors, line_number, key)) { def->has_science_bonus = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else { + def->has_science_bonus = false; + } } else if (slice_matches_str (key, "food_bonus")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->food_bonus = ival; + if (parse_district_bonus_entries (value, &def->food_bonus, &def->food_bonus_extras, parse_errors, line_number, key)) { def->has_food_bonus = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else { + def->has_food_bonus = false; + } } else if (slice_matches_str (key, "gold_bonus")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->gold_bonus = ival; + if (parse_district_bonus_entries (value, &def->gold_bonus, &def->gold_bonus_extras, parse_errors, line_number, key)) { def->has_gold_bonus = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else { + def->has_gold_bonus = false; + } } else if (slice_matches_str (key, "shield_bonus")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->shield_bonus = ival; + if (parse_district_bonus_entries (value, &def->shield_bonus, &def->shield_bonus_extras, parse_errors, line_number, key)) { def->has_shield_bonus = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else { + def->has_shield_bonus = false; + } } else if (slice_matches_str (key, "happiness_bonus")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->happiness_bonus = ival; + if (parse_district_bonus_entries (value, &def->happiness_bonus, &def->happiness_bonus_extras, parse_errors, line_number, key)) { def->has_happiness_bonus = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else { + def->has_happiness_bonus = false; + } } else if (slice_matches_str (key, "generated_resource")) { if (def->generated_resource != NULL) { @@ -7687,6 +7963,39 @@ set_wonders_dependent_on_wonder_district (void) cfg->max_building_index = cfg->dependent_improvement_count; } +void +resolve_district_bonus_building_entries (struct district_bonus_list * list, + char const * district_name, + char const * bonus_name, + struct error_line ** parse_errors) +{ + if (list == NULL) + return; + + for (int i = 0; i < list->count; i++) { + struct district_bonus_entry * entry = &list->entries[i]; + if (entry->type != DBET_BUILDING) + continue; + if ((entry->building_name == NULL) || (entry->building_name[0] == '\0')) { + entry->building_id = -1; + continue; + } + + int improv_id; + struct string_slice improv_name = { .str = (char *)entry->building_name, .len = (int)strlen (entry->building_name) }; + if (find_game_object_id_by_name (GOK_BUILDING, &improv_name, 0, &improv_id)) { + entry->building_id = improv_id; + stable_insert (&is->building_name_to_id, improv_name.str, improv_id); + } else { + entry->building_id = -1; + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": %s entry \"%.*s\" not found", + district_name, bonus_name, improv_name.len, improv_name.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + } +} + void parse_building_and_tech_ids () { struct c3x_config * cfg = &is->current_config; @@ -7801,6 +8110,13 @@ void parse_building_and_tech_ids () } is->district_infos[i].dependent_building_count = stored_count; } + + resolve_district_bonus_building_entries (&is->district_configs[i].culture_bonus_extras, district_name, "culture_bonus", &district_parse_errors); + resolve_district_bonus_building_entries (&is->district_configs[i].science_bonus_extras, district_name, "science_bonus", &district_parse_errors); + resolve_district_bonus_building_entries (&is->district_configs[i].food_bonus_extras, district_name, "food_bonus", &district_parse_errors); + resolve_district_bonus_building_entries (&is->district_configs[i].gold_bonus_extras, district_name, "gold_bonus", &district_parse_errors); + resolve_district_bonus_building_entries (&is->district_configs[i].shield_bonus_extras, district_name, "shield_bonus", &district_parse_errors); + resolve_district_bonus_building_entries (&is->district_configs[i].happiness_bonus_extras, district_name, "happiness_bonus", &district_parse_errors); } // Map wonder names to their improvement IDs for rendering under-construction wonders @@ -8854,7 +9170,7 @@ bridge_district_tile_is_valid (int tile_x, int tile_y) } bool -can_build_district_on_tile (Tile * tile, int district_id) +can_build_district_on_tile (Tile * tile, int district_id, int civ_id) { if ((! is->current_config.enable_districts) || (tile == NULL) || (tile == p_null_tile) || @@ -8882,10 +9198,7 @@ can_build_district_on_tile (Tile * tile, int district_id) int tile_x = 0, tile_y = 0; tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y); - int owner_id = tile->Territory_OwnerID; - if ((owner_id < 0) || (owner_id >= 32)) - return false; - if (! leader_can_build_district (&leaders[owner_id], district_id)) + if (! leader_can_build_district (&leaders[civ_id], district_id)) return false; if (! district_resource_prereqs_met (tile, tile_x, tile_y, district_id, NULL)) @@ -8925,7 +9238,7 @@ tile_suitable_for_district (Tile * tile, int district_id, City * city, bool * ou if ((tile == NULL) || (tile == p_null_tile)) return false; if (tile->CityID >= 0) return false; if (tile->vtable->m38_Get_Territory_OwnerID (tile) != city->Body.CivID) return false; - if (! can_build_district_on_tile (tile, district_id)) return false; + if (! can_build_district_on_tile (tile, district_id, city->Body.CivID)) return false; int tile_x = 0, tile_y = 0; tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y); @@ -9946,17 +10259,10 @@ calculate_district_happiness_bonus (City * city, int * happiness_bonus) if (is_neighborhood) utilized_neighborhoods--; - if (cfg->happiness_bonus != 0) - total_happy += cfg->happiness_bonus; - - if (is->current_config.enable_natural_wonders && district_id == NATURAL_WONDER_DISTRICT_ID) { - struct natural_wonder_district_config const * nwcfg = - get_natural_wonder_config_by_id (inst->natural_wonder_info.natural_wonder_id); - if (nwcfg != NULL) { - if (nwcfg->happiness_bonus != 0) - total_happy += nwcfg->happiness_bonus; - } - } + int district_happy = 0; + get_effective_district_yields (inst, cfg, NULL, NULL, NULL, NULL, NULL, &district_happy); + if (district_happy != 0) + total_happy += district_happy; } if (happiness_bonus != NULL) @@ -14208,7 +14514,7 @@ set_up_district_buttons (Main_GUI * this) if (existing_district_id == dc && district_completed) continue; if ((existing_district_id >= 0) && (existing_district_id != dc) && (! district_completed)) continue; - if (! can_build_district_on_tile (tile, dc)) + if (! can_build_district_on_tile (tile, dc, selected_unit->Body.CivID)) continue; // This district should be shown @@ -14703,7 +15009,7 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) if (! itable_look_up (&is->command_id_to_district_id, unit_command_value, &district_id)) return false; - return is_worker (this) && can_build_district_on_tile (tile, district_id); + return is_worker (this) && can_build_district_on_tile (tile, district_id, this->Body.CivID); } else if (unit_command_value == UCV_Build_Mine) { bool has_district = (tile != NULL) && (tile != p_null_tile) && (get_district_instance (tile) != NULL); @@ -26681,7 +26987,7 @@ get_bridge_image_index (Tile * tile) void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - *p_debug_mode_bits |= 0xC; + //*p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; From 69d9fb3bd85ff932b2b4ab1519f55c3190fc095c Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 13 Jan 2026 14:17:30 -0800 Subject: [PATCH 169/356] Have district-generated resources appear on map --- civ_prog_objects.csv | 3 +- injected_code.c | 80 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index ed442b76..62b455ed 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -809,7 +809,8 @@ define, 0x50FDB3, 0x51A057, 0x50FE53, "ADDR_MAX_TRADABLE_CITY_ID", "byte *" define, 0x505B21, 0x50F8EB, 0x505BC1, "ADDR_TRADABLE_CITIES_SIZE_TO_CLEAR", "byte *" define, 0x51000C, 0x51A2AC, 0x5100AC, "ADDR_MAX_TRADABLE_UNIT_ID", "byte *" define, 0x505B67, 0x50F931, 0x505C07, "ADDR_TRADABLE_UNITS_SIZE_TO_CLEAR", "byte *" -repl vptr, 0x66A538, 0x687644, 0x66A538, "Map_Renderer_m12_Draw_Tile_Buildings", "void (__fastcall *) (Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y)" +repl vptr, 0x66A538, 0x687644, 0x66A538, "Map_Renderer_m12_Draw_Tile_Buildings", "void (__fastcall *) (Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y)" +repl vptr, 0x66A52C, 0x0, 0x0, "Map_Renderer_m09_Draw_Tile_Resources", "void (__fastcall *) (Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y)" repl call, 0x5F5580, 0x6053D2, 0x5F54B0, "Tile_has_city_or_district", "" repl call, 0x5F5816, 0x605639, 0x5F5746, "Tile_has_city_or_district", "" repl call, 0x5F5ABB, 0x6058AF, 0x5F59EB, "Tile_has_city_or_district", "" diff --git a/injected_code.c b/injected_code.c index 38bbadfd..e8a326bb 100644 --- a/injected_code.c +++ b/injected_code.c @@ -26985,11 +26985,11 @@ get_bridge_image_index (Tile * tile) } void __fastcall -patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int param_1, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) +patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { //*p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { - Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); + Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; } @@ -26999,7 +26999,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par struct district_instance * inst = get_district_instance (tile); if (inst == NULL) { - Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); + Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; } @@ -27160,7 +27160,79 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int par return; } - Map_Renderer_m12_Draw_Tile_Buildings(this, __, param_1, tile_x, tile_y, map_renderer, pixel_x, pixel_y); + Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); +} + +void __fastcall +patch_Map_Renderer_m09_Draw_Tile_Resources(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) +{ + if (! is->current_config.enable_districts) { + Map_Renderer_m09_Draw_Tile_Resources(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); + return; + } + + Tile * tile = tile_at (tile_x, tile_y); + if ((tile == NULL) || (tile == p_null_tile)) { + Map_Renderer_m09_Draw_Tile_Resources(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); + return; + } + + int base_resource = Tile_get_resource_visible_to (tile, __, visible_to_civ_id); + int district_resource = -1; + + struct district_instance * inst = get_district_instance (tile); + if ((inst != NULL) && (inst->state == DS_COMPLETED)) { + int district_id = inst->district_type; + if ((district_id >= 0) && (district_id < is->district_count)) { + struct district_config * cfg = &is->district_configs[district_id]; + if (cfg->generated_resource_id >= 0) { + int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); + if ((owner_id >= 0) && district_can_generate_resource (owner_id, cfg) && + ((visible_to_civ_id < 0) || (owner_id == visible_to_civ_id))) { + int res_id = cfg->generated_resource_id; + bool show = true; + if (show) { + if (visible_to_civ_id >= 0) { + int req_tech_id = (cfg->generated_resource_flags & MF_NO_TECH_REQ) ? -1 : p_bic_data->ResourceTypes[res_id].RequireID; + if ((req_tech_id < 0) || Leader_has_tech (&leaders[visible_to_civ_id], __, req_tech_id)) + district_resource = res_id; + } else + district_resource = res_id; + } + } + } + } + } + + if (district_resource < 0) { + Map_Renderer_m09_Draw_Tile_Resources(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); + return; + } + + int tile_width = p_bic_data->is_zoomed_out ? 64 : 128; + int offset = tile_width >> 2; + int left_x = pixel_x - (offset >> 1); + int right_x = pixel_x + (offset >> 1); + + if (base_resource >= 0) + Map_Renderer_m09_Draw_Tile_Resources(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, left_x, pixel_y); + + int icon_id = p_bic_data->ResourceTypes[district_resource].IconID; + if (icon_id >= 0 && icon_id < 36 && map_renderer != NULL && map_renderer->Resources != NULL) { + Sprite * sprite = &map_renderer->Resources[icon_id]; + int tile_height = tile_width >> 1; + int sprite_width = sprite->Width; + int sprite_height = sprite->Height; + if (sprite_width <= 0) sprite_width = sprite->Width3; + if (sprite_height <= 0) sprite_height = sprite->Height3; + int center_x = (tile_width - sprite_width) >> 1; + int center_y = (tile_height - sprite_height) >> 1; + int draw_x = (base_resource >= 0) ? right_x : pixel_x; + draw_x += center_x; + int draw_y = pixel_y + center_y; + int draw_scale = (p_bic_data->is_zoomed_out != false) + 1; + patch_Sprite_draw_on_map (sprite, __, map_renderer, draw_x, draw_y, 1, 1, draw_scale, 0); + } } bool __fastcall From 31c514b64c8d91045aa6dfb68f5217f2ab9b8640 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 13 Jan 2026 14:19:16 -0800 Subject: [PATCH 170/356] Add district todos to source control --- Notes/district_todos.md | 94 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 Notes/district_todos.md diff --git a/Notes/district_todos.md b/Notes/district_todos.md new file mode 100644 index 00000000..8b9acc7e --- /dev/null +++ b/Notes/district_todos.md @@ -0,0 +1,94 @@ + - Allow workers over water only if in radius of city that can build water district/wonder + - Show generated resource icon over districts + - Add cap on count of distribution hubs, destroy extra if conquered + - AI navies target maritime districts + - Keep irrigation/mine on tile if specified + + - Hoover Dam (use alt dir, special positioning b/c on river) + - Districts: + - Central Rail Hub (+25% distro hub yields) + - Mass Transit System + - Energy Grid + - Coal Plant + - Solar Plant + - Hydro Plant + - Nuclear Plant + - Canal (2 opposite sides have coast "isthmus") + - Bridge (2 opposite sides have land "strait" or bridges) + - Light annotations + - Buttons + - Add commented instructions on fields in config files + - Double check PCX third column alignment + + +## Maritime Districts + - ~~Choose terrain type~~ + - ~~Resources generated by districts appear on map~~ + - ~~Flexible config for extra district bonuses based on tile type and nearby buildings~~ + - ~~Put abandoned district art in proper separate field~~ + - ~~Put workable tile loop in macro~~ + - ~~Distro hub yield divisors can scale by connected city count~~ + - ~~Distro hub yields in city screen show even if outside workable ring~~ + - ~~District can add happiness~~ + - ~~Add happiness yield icons to city screen~~ + - ~~Limit district building by resources (multiple, and both on tile and connected?)~~ + - ~~Need to be able to specify buildable_on for specific wonders as well, carry over to wonder district construction and tile choosing for AI~~ + - ~~Add support for districts/wonders on forest, jungle, swamp, snow-mountain, snow-forest, snow-volcano (?)~~ + - ~~align_to_coast setting, parsable~~ + - ~~is_maritime & left/right direction img properties, parsable~~ + - ~~Make sure AI doesn't remove forests and jungle/swamp if district can be built on them~~ + - ~~Replacing ring tile loops with FOR_TILE_RINGS_AROUND ?~~ + - ~~Show district in terrain info modal~~ + - ~~Allow comments in config files~~ + - ~~images for colossus, great lighthouse~~ + - ~~Natural Wonders~~ + - ~~Art for ports~~ + - ~~AI builds non-special districts with no dependent buildings~~ + - ~~Add "auto" intelligent option for AI distro hub building strategy~~ + - ~~Add message if buildings lost due to destroyed district~~ + - ~~Add "display_name"~~ + - ~~Add option to not zoom city screen even if word radius over something~~ + - ~~Districts can provide resources (mills)~~ + - ~~Districts and Wonders can be aligned with rivers~~ + - ~~Add extended_height property for tall district art~~ + - ~~Make sure tech/resource/building parse errors show on load screen~~ + - ~~Auto-show city founding location if settler selected~~ + - ~~Make Tile Highlights green, not red~~ + - ~~Tune ai_defends_districts logic~~ + - ~~Move all units of same type, "build" natural wonder bug~~ + - ~~Fix mideast cathedral colors~~ + - ~~Make terrain highlights green~~ + - ~~Refine logic for air units using aerodrome~~ + - ~~Add buildable_by_civs, buildable_by_civ_traits, buildable_by_civ_govs, buildable_by_civ_cultures~~ + - ~~Support for buildable_on "irrigation", "mine"~~ + - ~~Other districts:~~ + - ~~Ski Resort~~ + - ~~Park~~ + - ~~Offshore Extraction Zone~~ + - ~~Data Center~~ + + + + + + + + + + + + + +## Seasons + - Splice nice terrain onto 1-2 pcx tiles, use for prompt for remainder + +## Great Wall + - Also Civilopedia? At least district buttons in tech tree + - Water Park (2 happiness, 2 gold) + - Special district, special rendering (return early, don't do usually render flow, loop directionally and render multiple times, once per direction if connected) + - Building automatically covers borders, allows workers to build until metallurgy + - Maybe do one-time tile-by-tile modal confirmation if would destroy district/improvement? + - Add wonder_prereq, natural_wonder_prereq, made_obsolete_by to districts config + - Enemy units can't pass over, but can destroy (recommend using PTW targeting?) + - Need to figure out how AI will manage + - +1 culture & +1 gold per wall \ No newline at end of file From b6f87a5047e2778f55ea4ffe41cbf9e80d3089bc Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 13 Jan 2026 17:01:59 -0800 Subject: [PATCH 171/356] Expand Bridge art with side paddings; Add y_offset and x_offset config settings --- Art/Districts/1200/Bridge.PCX | Bin 31588 -> 33609 bytes C3X.h | 8 ++++- default.districts_config.txt | 6 +++- injected_code.c | 53 +++++++++++++++++++++++++++------- 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/Art/Districts/1200/Bridge.PCX b/Art/Districts/1200/Bridge.PCX index cb62b9219d8fae47e8d075b9ea7ec7f9ebd77ed3..8ac5ac4ab1bc7295c48fed51af2fbddb710b0458 100644 GIT binary patch delta 16995 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 zlnnCI>custom_width = def->custom_width; if (def->has_custom_height) cfg->custom_height = def->custom_height; + if (def->has_x_offset) + cfg->x_offset = def->x_offset; + if (def->has_y_offset) + cfg->y_offset = def->y_offset; if (def->has_btn_tile_sheet_column) cfg->btn_tile_sheet_column = def->btn_tile_sheet_column; if (def->has_btn_tile_sheet_row) @@ -6205,6 +6209,8 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.align_to_coast = def->has_align_to_coast ? def->align_to_coast : false; new_cfg.custom_width = def->has_custom_width ? def->custom_width : 0; new_cfg.custom_height = def->has_custom_height ? def->custom_height : 0; + new_cfg.x_offset = def->has_x_offset ? def->x_offset : 0; + new_cfg.y_offset = def->has_y_offset ? def->y_offset : 0; new_cfg.btn_tile_sheet_column = def->has_btn_tile_sheet_column ? def->btn_tile_sheet_column : 0; new_cfg.btn_tile_sheet_row = def->has_btn_tile_sheet_row ? def->btn_tile_sheet_row : 0; new_cfg.defense_bonus_percent = def->has_defense_bonus_percent ? def->defense_bonus_percent : 100; @@ -6651,6 +6657,24 @@ handle_district_definition_key (struct parsed_district_definition * def, } else add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "x_offset")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->x_offset = ival; + def->has_x_offset = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + + } else if (slice_matches_str (key, "y_offset")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->y_offset = ival; + def->has_y_offset = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "btn_tile_sheet_column")) { struct string_slice val_slice = *value; int ival; @@ -26987,7 +27011,7 @@ get_bridge_image_index (Tile * tile) void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - //*p_debug_mode_bits |= 0xC; + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -27034,12 +27058,14 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis if (! completed) return; - struct district_config const * cfg = &is->district_configs[district_id]; + struct district_config const * cfg = &is->district_configs[district_id]; int territory_owner_id = tile->Territory_OwnerID; int variant = 0; int era = 0; int culture = 0; int buildings = 0; + int draw_pixel_x = pixel_x; + int draw_pixel_y = pixel_y; Sprite * district_sprite; // Handle river alignment, if district allows it @@ -27055,13 +27081,13 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis if (cfg->vary_img_by_era) era = leader->Era; if (cfg->align_to_coast) - align_variant_and_pixel_offsets_with_coastline (tile, &variant, &pixel_x, &pixel_y); + align_variant_and_pixel_offsets_with_coastline (tile, &variant, &draw_pixel_x, &draw_pixel_y); } else if (district_id != WONDER_DISTRICT_ID && district_id != NATURAL_WONDER_DISTRICT_ID && district_id != BRIDGE_DISTRICT_ID) { Sprite * abandoned_sprite = &is->abandoned_district_img; if (tile->vtable->m35_Check_Is_Water (tile) && is->abandoned_maritime_district_img.vtable != NULL) abandoned_sprite = &is->abandoned_maritime_district_img; if (abandoned_sprite->vtable != NULL) { - draw_district_on_map_or_canvas(abandoned_sprite, map_renderer, pixel_x, pixel_y); + draw_district_on_map_or_canvas(abandoned_sprite, map_renderer, draw_pixel_x + cfg->x_offset, draw_pixel_y + cfg->y_offset); } return; } @@ -27102,12 +27128,14 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis struct wonder_district_image_set * set = &is->wonder_district_img_sets[windex]; if (wonder_allows_river(wcfg)) - align_district_with_river (tile, &pixel_x, &pixel_y, &river_dir); + align_district_with_river (tile, &draw_pixel_x, &draw_pixel_y, &river_dir); bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alternative_direction_image (tile_x, tile_y, territory_owner_id, wcfg); Sprite * wsprite = (use_alt_dir && (set->alt_dir_img.vtable != NULL)) ? &set->alt_dir_img : &set->img; + int offset_x = draw_pixel_x + cfg->x_offset; + int offset_y = draw_pixel_y + cfg->y_offset; - draw_district_on_map_or_canvas(wsprite, map_renderer, pixel_x, pixel_y); + draw_district_on_map_or_canvas(wsprite, map_renderer, offset_x, offset_y); return; // Under construction @@ -27119,8 +27147,10 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis struct wonder_district_image_set * set = &is->wonder_district_img_sets[construct_windex]; bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alternative_direction_image (tile_x, tile_y, territory_owner_id, wcfg); Sprite * csprite = (use_alt_dir && (set->alt_dir_construct_img.vtable != NULL)) ? &set->alt_dir_construct_img : &set->construct_img; + int offset_x = draw_pixel_x + cfg->x_offset; + int offset_y = draw_pixel_y + cfg->y_offset; - draw_district_on_map_or_canvas(csprite, map_renderer, pixel_x, pixel_y); + draw_district_on_map_or_canvas(csprite, map_renderer, offset_x, offset_y); return; } break; @@ -27128,13 +27158,14 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis case ENERGY_GRID_DISTRICT_ID: { // Energy grids can have multiple buildings that - unlike most district buildings - don't have each other as prereqs (e.g., Coal Plant & Nuclear Plant), - // and thus have a combinatorial number of images based on which power plants are built in their radius. This returns the correct image index + // and thus have a combinatorial number of images based on which power plants are built in their radius. buildings = get_energy_grid_image_index (tile_x, tile_y); break; } case BRIDGE_DISTRICT_ID: { buildings = get_bridge_image_index (tile); + era = 2; break; } default: @@ -27154,8 +27185,10 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; - int draw_x = pixel_x - ((sprite_width - 128) / 2); - int draw_y = pixel_y - (sprite_height - 64); + int offset_x = draw_pixel_x + cfg->x_offset; + int offset_y = draw_pixel_y + cfg->y_offset; + int draw_x = offset_x - ((sprite_width - 128) / 2); + int draw_y = offset_y - (sprite_height - 64); draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); return; } From b7b9da4dc5c68e8fafc8da52065a9c4824d3e925 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 13 Jan 2026 17:24:06 -0800 Subject: [PATCH 172/356] Begin implementation of canal district --- Art/Districts/1200/Canal.PCX | Bin 29046 -> 15233 bytes C3X.h | 11 ++++++++++- Civ3Conquests.h | 1 + default.districts_config.txt | 22 ++++++++++++++++++++++ injected_code.c | 14 +++++++++++++- 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/Art/Districts/1200/Canal.PCX b/Art/Districts/1200/Canal.PCX index 17cf7963e8c79fd71534bb5cf0dbfa54948e18e1..9a9f8cc1086943e86e3d7cc4df519fb3b2ab0798 100644 GIT binary patch literal 15233 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 E2mAYy{{R30 diff --git a/C3X.h b/C3X.h index 4ed8a755..630ceed3 100644 --- a/C3X.h +++ b/C3X.h @@ -17,7 +17,7 @@ typedef unsigned char byte; #define MAX_BUILDING_PREREQS_FOR_UNIT 10 #define COUNT_SPECIAL_DISTRICT_TYPES 10 -#define USED_SPECIAL_DISTRICT_TYPES 9 +#define USED_SPECIAL_DISTRICT_TYPES 10 #define MAX_DYNAMIC_DISTRICT_TYPES 22 #define COUNT_DISTRICT_TYPES (COUNT_SPECIAL_DISTRICT_TYPES + MAX_DYNAMIC_DISTRICT_TYPES) #define MAX_WONDER_DISTRICT_TYPES 32 @@ -804,6 +804,15 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_path_count = 1, .max_building_index = 3, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 + }, + { + .command = UCV_Build_Canal, .name = "Canal", .tooltip = "Build Canal", .display_name = "Canal", + .advance_prereq = "Industrialization", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, + .img_paths = {"Canal.pcx"}, .dependent_improvements = {0}, .custom_width = 176, .custom_height = 112, .y_offset = 24, .x_offset = 0, + .buildable_square_types_mask = (1 << SQ_Desert) | (1 << SQ_Plains) | (1 << SQ_Grassland) | (1 << SQ_Tundra) | (1 << SQ_FloodPlain), + .img_path_count = 1, .max_building_index = 3, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, + .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 } }; diff --git a/Civ3Conquests.h b/Civ3Conquests.h index 9313514a..2244ef41 100644 --- a/Civ3Conquests.h +++ b/Civ3Conquests.h @@ -776,6 +776,7 @@ enum Unit_Command_Values UCV_Build_CentralRailHub = -10000006, UCV_Build_EnergyGrid = -10000007, UCV_Build_Bridge = -10000008, + UCV_Build_Canal = -10000009, }; enum Unit_Mode_Actions diff --git a/default.districts_config.txt b/default.districts_config.txt index 0b5c4a2e..38b79f61 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -338,4 +338,26 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happiness_bonus = 0 + +#District +name = Canal +tooltip = Build Canal +custom_height = 176 +custom_width = 112 +y_offset = 24 +x_offset = 0 +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 7 +vary_img_by_era = 1 +vary_img_by_culture = 0 +advance_prereq = Industrialization +buildable_on = desert,plains,grassland,tundra,floodplain +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 happiness_bonus = 0 \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index 95332c03..3164ea57 100644 --- a/injected_code.c +++ b/injected_code.c @@ -75,6 +75,7 @@ struct injected_state * is = ADDR_INJECTED_STATE; #define CENTRAL_RAIL_HUB_DISTRICT_ID 6 #define ENERGY_GRID_DISTRICT_ID 7 #define BRIDGE_DISTRICT_ID 8 +#define CANAL_DISTRICT_ID 9 char const * const hotseat_replay_save_path = "Saves\\Auto\\ai-move-replay-before-interturn.SAV"; char const * const hotseat_resume_save_path = "Saves\\Auto\\ai-move-replay-resume.SAV"; @@ -27008,6 +27009,12 @@ get_bridge_image_index (Tile * tile) return 0; } +void +draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int era) +{ + // TODO +} + void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { @@ -27165,9 +27172,14 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis case BRIDGE_DISTRICT_ID: { buildings = get_bridge_image_index (tile); - era = 2; + era = 2; // DEBUG break; } + case CANAL_DISTRICT_ID: + { + draw_canal_district (tile, tile_x, tile_y, map_renderer, pixel_x, pixel_y, era); + return; + } default: { struct district_infos * info = &is->district_infos[district_id]; From 72ad5262573e7647beac246aabb3e6de168cc4dd Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 13 Jan 2026 17:34:06 -0800 Subject: [PATCH 173/356] Attempt canal movement implementation --- C3X.h | 1 + injected_code.c | 120 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 84 insertions(+), 37 deletions(-) diff --git a/C3X.h b/C3X.h index 630ceed3..e85b03b7 100644 --- a/C3X.h +++ b/C3X.h @@ -347,6 +347,7 @@ struct c3x_config { bool enable_aerodrome_districts; bool enable_port_districts; bool enable_bridge_districts; + bool enable_canal_districts; bool enable_central_rail_hub_districts; bool cities_with_mutual_district_receive_buildings; diff --git a/injected_code.c b/injected_code.c index 3164ea57..5e97becb 100644 --- a/injected_code.c +++ b/injected_code.c @@ -13209,6 +13209,7 @@ patch_init_floating_point () {"enable_aerodrome_districts" , false, offsetof (struct c3x_config, enable_aerodrome_districts)}, {"enable_port_districts" , false, offsetof (struct c3x_config, enable_port_districts)}, {"enable_bridge_districts" , false, offsetof (struct c3x_config, enable_bridge_districts)}, + {"enable_canal_districts" , false, offsetof (struct c3x_config, enable_canal_districts)}, {"enable_central_rail_hub_districts" , false, offsetof (struct c3x_config, enable_central_rail_hub_districts)}, {"completed_wonder_districts_can_be_destroyed" , false, offsetof (struct c3x_config, completed_wonder_districts_can_be_destroyed)}, {"destroyed_wonders_can_be_built_again" , false, offsetof (struct c3x_config, destroyed_wonders_can_be_built_again)}, @@ -15955,46 +15956,62 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, { AdjacentMoveValidity base_validity = Unit_can_move_to_adjacent_tile (this, __, neighbor_index, param_2); - // Let workers step onto coast tiles when the config flag is enabled (base logic treats this as an invalid sea move) - if (is->current_config.enable_districts && is->current_config.workers_can_enter_coast && - is_worker (this) && - ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { - int nx, ny; - get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); - Tile * dest = tile_at (nx, ny); - if ((dest != NULL) && - dest->vtable->m35_Check_Is_Water (dest) && - (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) - base_validity = AMV_OK; - } + if (is->current_config.enable_districts) { - // Allow land units to enter bridge tiles - if (is->current_config.enable_districts && - is->current_config.enable_bridge_districts && - (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Land) && - ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { - int nx, ny; - get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); - Tile * dest = tile_at (nx, ny); - if ((dest != NULL) && (dest != p_null_tile)) { - struct district_instance * inst = get_district_instance (dest); - if ((inst != NULL) && - (inst->district_type == BRIDGE_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) + // Let workers step onto coast tiles when the config flag is enabled (base logic treats this as an invalid sea move) + if (is->current_config.workers_can_enter_coast && is_worker (this) && + ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { + int nx, ny; + get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); + Tile * dest = tile_at (nx, ny); + if ((dest != NULL) && + dest->vtable->m35_Check_Is_Water (dest) && + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) base_validity = AMV_OK; } - } - if ((base_validity == AMV_OK) && - is->current_config.enable_districts && - is->current_config.enable_port_districts && - is->current_config.naval_units_use_port_districts_not_cities && - (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea)) { - int nx, ny; - get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); - Tile * dest = tile_at (nx, ny); - if ((dest != NULL) && (dest != p_null_tile) && Tile_has_city (dest)) - return AMV_INVALID_SEA_MOVE; + // Allow land units to enter bridge tiles + if (is->current_config.enable_bridge_districts && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Land) && + ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { + int nx, ny; + get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); + Tile * dest = tile_at (nx, ny); + if ((dest != NULL) && (dest != p_null_tile)) { + struct district_instance * inst = get_district_instance (dest); + if ((inst != NULL) && + (inst->district_type == BRIDGE_DISTRICT_ID) && + district_is_complete (dest, inst->district_type)) + base_validity = AMV_OK; + } + } + + // Allow naval units to enter completed canal tiles + if (is->current_config.enable_canal_districts && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea) && + ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { + int nx, ny; + get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); + Tile * dest = tile_at (nx, ny); + if ((dest != NULL) && (dest != p_null_tile)) { + struct district_instance * inst = get_district_instance (dest); + if ((inst != NULL) && + (inst->district_type == CANAL_DISTRICT_ID) && + district_is_complete (dest, inst->district_type)) + base_validity = AMV_OK; + } + } + + if ((base_validity == AMV_OK) && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea)) { + int nx, ny; + get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); + Tile * dest = tile_at (nx, ny); + if ((dest != NULL) && (dest != p_null_tile) && Tile_has_city (dest)) + return AMV_INVALID_SEA_MOVE; + } } // Apply unit count per tile limit @@ -16058,6 +16075,21 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr } } + // Let the pathfinder consider canal tiles reachable for naval units + if (is->current_config.enable_districts && + is->current_config.enable_canal_districts && + (base_cost < 0) && (unit != NULL) && + (p_bic_data->UnitTypes[unit->Body.UnitTypeID].Unit_Class == UTC_Sea)) { + Tile * dest = tile_at (to_x, to_y); + if ((dest != NULL) && (dest != p_null_tile)) { + struct district_instance * inst = get_district_instance (dest); + if ((inst != NULL) && + (inst->district_type == CANAL_DISTRICT_ID) && + district_is_complete (dest, inst->district_type)) + base_cost = Unit_get_max_move_points (unit); + } + } + if ((unit != NULL) && (base_cost >= 0) && is->current_config.enable_districts && @@ -27089,7 +27121,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis era = leader->Era; if (cfg->align_to_coast) align_variant_and_pixel_offsets_with_coastline (tile, &variant, &draw_pixel_x, &draw_pixel_y); - } else if (district_id != WONDER_DISTRICT_ID && district_id != NATURAL_WONDER_DISTRICT_ID && district_id != BRIDGE_DISTRICT_ID) { + } else if (district_id != WONDER_DISTRICT_ID && district_id != NATURAL_WONDER_DISTRICT_ID && district_id != BRIDGE_DISTRICT_ID && district_id != CANAL_DISTRICT_ID) { Sprite * abandoned_sprite = &is->abandoned_district_img; if (tile->vtable->m35_Check_Is_Water (tile) && is->abandoned_maritime_district_img.vtable != NULL) abandoned_sprite = &is->abandoned_maritime_district_img; @@ -28392,6 +28424,20 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t } } + if (is->current_config.enable_districts && + is->current_config.enable_canal_districts && + base != PBV_OK && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea)) { + Tile * dest = tile_at (to_x, to_y); + if ((dest != NULL) && (dest != p_null_tile)) { + struct district_instance * inst = get_district_instance (dest); + if ((inst != NULL) && + (inst->district_type == CANAL_DISTRICT_ID) && + district_is_complete (dest, inst->district_type)) + return PBV_OK; + } + } + return base; } From 9c6c39fdf506d9ad00343f85e21fc957276f8810 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 13 Jan 2026 18:16:05 -0800 Subject: [PATCH 174/356] Working on canal art --- Art/Districts/1200/Canal.PCX | Bin 15233 -> 59506 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Canal.PCX b/Art/Districts/1200/Canal.PCX index 9a9f8cc1086943e86e3d7cc4df519fb3b2ab0798..d8253e57772e3102b1969991eac0c0b65bca78ec 100644 GIT binary patch literal 59506 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 Date: Tue, 13 Jan 2026 22:13:57 -0800 Subject: [PATCH 175/356] Begin logic for canal render --- Art/Districts/1200/Canal.PCX | Bin 59506 -> 59544 bytes C3X.h | 2 +- injected_code.c | 130 +++++++++++++++++++---------------- 3 files changed, 73 insertions(+), 59 deletions(-) diff --git a/Art/Districts/1200/Canal.PCX b/Art/Districts/1200/Canal.PCX index d8253e57772e3102b1969991eac0c0b65bca78ec..e5365359873872b35558214422e233fdfaf752a5 100644 GIT binary patch delta 757 zcmXw%T}YEr7{@tI!LK4na|NPE#h|GT11XI}Uh2Z3p_74R(dlK?mJ2FtzN^>Ll2V0KH4Pi*GXY- z5GC-6a8ED_ibw@sDS#bVtc3+QfW>N1;NvrBGyx>53umN^eF z!9GJ*Y35@m7WMUvk5O_uJ*U1=V3M9%B4MV*R_NIynzygeg&i$rfuYt+^RTlDiw-?j zj4;kqJ#G>&s>@BH)eb*OYivm^d*tWv0!n3Eex{VoNHPrA%b7J7bIt1&>Z-4#>NF8M ztgrzGIq;X3b?uTrqu){L7E?C@L60R}lT%l$V96+eSKxSd;rL39XJba~9X)JYrfXq( zo5a5lpp^f^#4NS5tfChI3+uu4!Suo`!R^7{v(TeE0S)cJ*}>s% z!QP+2<-zT<7Nw*C3Bqv)Zkxg1!R@oerj-Q(O%juFR~WN%ti}P8uvQrgl)>o1;=$j+ z^yafzuIK@iPFonWV6cz@2lc`2!Nj3klk{96vmLUO0R`tQxN#(tfL)kO2YileJ+a1Ht|8vxB#$0Rixn3cMl%o;vWeR=KAElP9%!S})IxO?lfEWPjn1?nup%D9siXd?*0?46%%XF}++ zOTXa(1?MbHd$^OWX&ne|CVOW)eShV%TEP_p2IMTALT7A~PHQCwIzmooedV*B!_@%< ze|-TK=(91<3;_k=EWyO*laUl9vt`jU7kVne?EnA( diff --git a/C3X.h b/C3X.h index e85b03b7..91640047 100644 --- a/C3X.h +++ b/C3X.h @@ -811,7 +811,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereq = "Industrialization", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"Canal.pcx"}, .dependent_improvements = {0}, .custom_width = 176, .custom_height = 112, .y_offset = 24, .x_offset = 0, .buildable_square_types_mask = (1 << SQ_Desert) | (1 << SQ_Plains) | (1 << SQ_Grassland) | (1 << SQ_Tundra) | (1 << SQ_FloodPlain), - .img_path_count = 1, .max_building_index = 3, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, + .img_path_count = 1, .max_building_index = 8, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 } diff --git a/injected_code.c b/injected_code.c index 5e97becb..f90afa6d 100644 --- a/injected_code.c +++ b/injected_code.c @@ -9103,7 +9103,7 @@ district_resource_prereqs_met (Tile * tile, int tile_x, int tile_y, int district } bool -tile_has_bridge_district_at (int tile_x, int tile_y) +tile_has_district_at (int tile_x, int tile_y, int district_id) { wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); Tile * tile = tile_at (tile_x, tile_y); @@ -9111,7 +9111,7 @@ tile_has_bridge_district_at (int tile_x, int tile_y) return false; struct district_instance * inst = get_district_instance (tile); - return (inst != NULL) && (inst->district_type == BRIDGE_DISTRICT_ID); + return (inst != NULL) && (inst->district_type == district_id); } int @@ -9125,7 +9125,7 @@ count_contiguous_bridge_districts (int tile_x, int tile_y, int dx, int dy) for (int step = 0; step < max_steps; step++) { wrap_tile_coords (map, &nx, &ny); - if (! tile_has_bridge_district_at (nx, ny)) + if (! tile_has_district_at (nx, ny, BRIDGE_DISTRICT_ID)) break; count++; nx += dx; @@ -9170,7 +9170,7 @@ bridge_district_tile_is_valid (int tile_x, int tile_y) int nx = tile_x + adj_dx[i]; int ny = tile_y + adj_dy[i]; wrap_tile_coords (map, &nx, &ny); - if (! tile_has_bridge_district_at (nx, ny)) + if (! tile_has_district_at (nx, ny, BRIDGE_DISTRICT_ID)) continue; int cand_dx = -adj_dx[i]; @@ -9182,7 +9182,7 @@ bridge_district_tile_is_valid (int tile_x, int tile_y) wrap_tile_coords (map, &ox, &oy); if ((ox == tile_x) && (oy == tile_y)) continue; - if (! tile_has_bridge_district_at (ox, oy)) + if (! tile_has_district_at (ox, oy, BRIDGE_DISTRICT_ID)) continue; if (! ((adj_dx[j] == cand_dx) && (adj_dy[j] == cand_dy)) && @@ -26523,7 +26523,7 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i } bool -tile_offset_is_land (int civ_id, int tile_x, int tile_y, bool must_be_same_owner) +tile_is_land (int civ_id, int tile_x, int tile_y, bool must_be_same_owner) { if (must_be_same_owner && (civ_id <= 0)) return false; @@ -26534,6 +26534,14 @@ tile_offset_is_land (int civ_id, int tile_x, int tile_y, bool must_be_same_owner ((! must_be_same_owner) || (tile->Territory_OwnerID == civ_id)); } +bool +tile_is_water (int tile_x, int tile_y) +{ + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + return (tile != NULL) && (tile != p_null_tile) && (tile->vtable->m35_Check_Is_Water (tile)); +} + Tile * get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * out_sprite_index) { @@ -26664,14 +26672,14 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, bool city_is_east_of_port = (closest_dx > 0); bool city_is_north_of_port = (closest_dy < 0); bool city_is_south_of_port = (closest_dy > 0); - bool northwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y - 1, true); - bool north_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y - 2, true); - bool northeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y - 1, true); - bool east_tile_is_land = tile_offset_is_land (owner_id, tile_x + 2, tile_y, true); - bool southeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y + 1, true); - bool south_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y + 2, true); - bool southwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y + 1, true); - bool west_tile_is_land = tile_offset_is_land (owner_id, tile_x - 2, tile_y, true); + bool northwest_tile_is_land = tile_is_land (owner_id, tile_x - 1, tile_y - 1, true); + bool north_tile_is_land = tile_is_land (owner_id, tile_x, tile_y - 2, true); + bool northeast_tile_is_land = tile_is_land (owner_id, tile_x + 1, tile_y - 1, true); + bool east_tile_is_land = tile_is_land (owner_id, tile_x + 2, tile_y, true); + bool southeast_tile_is_land = tile_is_land (owner_id, tile_x + 1, tile_y + 1, true); + bool south_tile_is_land = tile_is_land (owner_id, tile_x, tile_y + 2, true); + bool southwest_tile_is_land = tile_is_land (owner_id, tile_x - 1, tile_y + 1, true); + bool west_tile_is_land = tile_is_land (owner_id, tile_x - 2, tile_y, true); if (city_is_directly_above_port) { if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } @@ -26729,14 +26737,14 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, // Same civ doesn't own any adjacent land tiles, pick based on *any* land tiles if (*out_variant == NONE) { - bool northwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y - 1, false); - bool north_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y - 2, false); - bool northeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y - 1, false); - bool east_tile_is_land = tile_offset_is_land (owner_id, tile_x + 2, tile_y, false); - bool southeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y + 1, false); - bool south_tile_is_land = tile_offset_is_land (owner_id, tile_x, tile_y + 2, false); - bool southwest_tile_is_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y + 1, false); - bool west_tile_is_land = tile_offset_is_land (owner_id, tile_x - 2, tile_y, false); + bool northwest_tile_is_land = tile_is_land (owner_id, tile_x - 1, tile_y - 1, false); + bool north_tile_is_land = tile_is_land (owner_id, tile_x, tile_y - 2, false); + bool northeast_tile_is_land = tile_is_land (owner_id, tile_x + 1, tile_y - 1, false); + bool east_tile_is_land = tile_is_land (owner_id, tile_x + 2, tile_y, false); + bool southeast_tile_is_land = tile_is_land (owner_id, tile_x + 1, tile_y + 1, false); + bool south_tile_is_land = tile_is_land (owner_id, tile_x, tile_y + 2, false); + bool southwest_tile_is_land = tile_is_land (owner_id, tile_x - 1, tile_y + 1, false); + bool west_tile_is_land = tile_is_land (owner_id, tile_x - 2, tile_y, false); if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } else if (southeast_tile_is_land) { *out_variant = NW; anchor = DIR_SE; } else if (southwest_tile_is_land) { *out_variant = NE; anchor = DIR_SW; } @@ -26904,9 +26912,9 @@ wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner bool city_is_directly_below_port = city_dx == tile_x && city_dy == tile_y + 2; if (city_is_directly_above_port || city_is_directly_below_port) { - bool northeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y - 1, true); - bool east_tile_is_land = tile_offset_is_land (owner_id, tile_x + 2, tile_y, true); - bool southeast_tile_is_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y + 1, true); + bool northeast_tile_is_land = tile_is_land (owner_id, tile_x + 1, tile_y - 1, true); + bool east_tile_is_land = tile_is_land (owner_id, tile_x + 2, tile_y, true); + bool southeast_tile_is_land = tile_is_land (owner_id, tile_x + 1, tile_y + 1, true); if (northeast_tile_is_land || east_tile_is_land || southeast_tile_is_land) return true; @@ -26982,29 +26990,16 @@ get_energy_grid_image_index (int tile_x, int tile_y) } int -get_bridge_image_index (Tile * tile) +get_bridge_image_index (Tile * tile, int tile_x, int tile_y) { - if ((tile == NULL) || (tile == p_null_tile)) - return 0; - - int tile_x = 0; - int tile_y = 0; - struct district_instance * inst = get_district_instance (tile); - if (inst != NULL) { - tile_x = inst->tile_x; - tile_y = inst->tile_y; - } else { - tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y); - } - - bool bridge_n = tile_has_bridge_district_at (tile_x, tile_y - 2); - bool bridge_s = tile_has_bridge_district_at (tile_x, tile_y + 2); - bool bridge_w = tile_has_bridge_district_at (tile_x - 2, tile_y); - bool bridge_e = tile_has_bridge_district_at (tile_x + 2, tile_y); - bool bridge_ne = tile_has_bridge_district_at (tile_x + 1, tile_y - 1); - bool bridge_nw = tile_has_bridge_district_at (tile_x - 1, tile_y - 1); - bool bridge_se = tile_has_bridge_district_at (tile_x + 1, tile_y + 1); - bool bridge_sw = tile_has_bridge_district_at (tile_x - 1, tile_y + 1); + bool bridge_n = tile_has_district_at (tile_x, tile_y - 2, BRIDGE_DISTRICT_ID); + bool bridge_s = tile_has_district_at (tile_x, tile_y + 2, BRIDGE_DISTRICT_ID); + bool bridge_w = tile_has_district_at (tile_x - 2, tile_y, BRIDGE_DISTRICT_ID); + bool bridge_e = tile_has_district_at (tile_x + 2, tile_y, BRIDGE_DISTRICT_ID); + bool bridge_ne = tile_has_district_at (tile_x + 1, tile_y - 1, BRIDGE_DISTRICT_ID); + bool bridge_nw = tile_has_district_at (tile_x - 1, tile_y - 1, BRIDGE_DISTRICT_ID); + bool bridge_se = tile_has_district_at (tile_x + 1, tile_y + 1, BRIDGE_DISTRICT_ID); + bool bridge_sw = tile_has_district_at (tile_x - 1, tile_y + 1, BRIDGE_DISTRICT_ID); if (bridge_n || bridge_s || bridge_w || bridge_e || bridge_ne || bridge_nw || bridge_se || bridge_sw) { int ns_count = (bridge_n ? 1 : 0) + (bridge_s ? 1 : 0); @@ -27024,14 +27019,14 @@ get_bridge_image_index (Tile * tile) } int owner_id = tile->Territory_OwnerID; - bool north_land = tile_offset_is_land (owner_id, tile_x, tile_y - 2, false); - bool south_land = tile_offset_is_land (owner_id, tile_x, tile_y + 2, false); - bool west_land = tile_offset_is_land (owner_id, tile_x - 2, tile_y, false); - bool east_land = tile_offset_is_land (owner_id, tile_x + 2, tile_y, false); - bool ne_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y - 1, false); - bool nw_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y - 1, false); - bool se_land = tile_offset_is_land (owner_id, tile_x + 1, tile_y + 1, false); - bool sw_land = tile_offset_is_land (owner_id, tile_x - 1, tile_y + 1, false); + bool north_land = tile_is_land (owner_id, tile_x, tile_y - 2, false); + bool south_land = tile_is_land (owner_id, tile_x, tile_y + 2, false); + bool west_land = tile_is_land (owner_id, tile_x - 2, tile_y, false); + bool east_land = tile_is_land (owner_id, tile_x + 2, tile_y, false); + bool ne_land = tile_is_land (owner_id, tile_x + 1, tile_y - 1, false); + bool nw_land = tile_is_land (owner_id, tile_x - 1, tile_y - 1, false); + bool se_land = tile_is_land (owner_id, tile_x + 1, tile_y + 1, false); + bool sw_land = tile_is_land (owner_id, tile_x - 1, tile_y + 1, false); if (north_land || south_land) return 2; if (west_land || east_land) return 3; @@ -27044,13 +27039,32 @@ get_bridge_image_index (Tile * tile) void draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int era) { - // TODO + bool canal_or_water_n = tile_has_district_at (tile_x, tile_y - 2, CANAL_DISTRICT_ID || tile_is_water (tile_x, tile_y - 2, false)); + bool canal_or_water_s = tile_has_district_at (tile_x, tile_y + 2, CANAL_DISTRICT_ID) || tile_is_water (tile_x, tile_y + 2, false); + bool canal_or_water_w = tile_has_district_at (tile_x - 2, tile_y, CANAL_DISTRICT_ID) || tile_is_water (tile_x - 2, tile_y, false); + bool canal_or_water_e = tile_has_district_at (tile_x + 2, tile_y, CANAL_DISTRICT_ID) || tile_is_water (tile_x + 2, tile_y, false); + bool canal_or_water_ne = tile_has_district_at (tile_x + 1, tile_y - 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x + 1, tile_y - 1, false); + bool canal_or_water_nw = tile_has_district_at (tile_x - 1, tile_y - 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x - 1, tile_y - 1, false); + bool canal_or_water_se = tile_has_district_at (tile_x + 1, tile_y + 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x + 1, tile_y + 1, false); + bool canal_or_water_sw = tile_has_district_at (tile_x - 1, tile_y + 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x - 1, tile_y + 1, false); + + Sprite * canal_centroid = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][0]; + Sprite * canal_n = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][1]; + Sprite * canal_ne = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][2]; + Sprite * canal_e = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][3]; + Sprite * canal_se = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][4]; + Sprite * canal_s = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][5]; + Sprite * canal_sw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][6]; + Sprite * canal_w = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][7]; + Sprite * canal_nw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][8]; + + } void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - *p_debug_mode_bits |= 0xC; + //*p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -27203,7 +27217,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis } case BRIDGE_DISTRICT_ID: { - buildings = get_bridge_image_index (tile); + buildings = get_bridge_image_index (tile, tile_x, tile_y); era = 2; // DEBUG break; } From 0dd88095b6162b414e13fc31bd9c2a4c1aab8199 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 14 Jan 2026 07:03:02 -0800 Subject: [PATCH 176/356] Add logic for building and rendering canals --- Notes/district_todos.md | 4 +- injected_code.c | 177 ++++++++++++++++++++++++++++++++-------- 2 files changed, 145 insertions(+), 36 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 8b9acc7e..4a2f5ea3 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,8 +1,9 @@ - Allow workers over water only if in radius of city that can build water district/wonder - - Show generated resource icon over districts - Add cap on count of distribution hubs, destroy extra if conquered - AI navies target maritime districts - Keep irrigation/mine on tile if specified + - Firm up logic for river district rendering + - - Hoover Dam (use alt dir, special positioning b/c on river) - Districts: @@ -23,6 +24,7 @@ ## Maritime Districts - ~~Choose terrain type~~ + - ~~Show generated resource icon over districts~~ - ~~Resources generated by districts appear on map~~ - ~~Flexible config for extra district bonuses based on tile type and nearby buildings~~ - ~~Put abandoned district art in proper separate field~~ diff --git a/injected_code.c b/injected_code.c index f90afa6d..7a1f65f0 100644 --- a/injected_code.c +++ b/injected_code.c @@ -9194,6 +9194,34 @@ bridge_district_tile_is_valid (int tile_x, int tile_y) return true; } +bool +canal_district_tile_is_valid (int tile_x, int tile_y) +{ + Map * map = &p_bic_data->Map; + int const adj_dx[8] = { 0, 0, -2, 2, 1, 1, -1, -1 }; + int const adj_dy[8] = { -2, 2, 0, 0, -1, 1, -1, 1 }; + + for (int i = 0; i < 8; i++) { + int nx = tile_x + adj_dx[i]; + int ny = tile_y + adj_dy[i]; + wrap_tile_coords (map, &nx, &ny); + Tile * tile = tile_at (nx, ny); + if ((tile == NULL) || (tile == p_null_tile)) + continue; + if (tile->vtable->m35_Check_Is_Water (tile)) + return true; + + if (tile_has_district_at (nx, ny, CANAL_DISTRICT_ID)) { + struct district_instance * inst = get_district_instance (tile); + if ((inst != NULL) && (inst->district_type == CANAL_DISTRICT_ID) && + district_is_complete (tile, CANAL_DISTRICT_ID)) + return true; + } + } + + return false; +} + bool can_build_district_on_tile (Tile * tile, int district_id, int civ_id) { @@ -9229,6 +9257,9 @@ can_build_district_on_tile (Tile * tile, int district_id, int civ_id) if (! district_resource_prereqs_met (tile, tile_x, tile_y, district_id, NULL)) return false; + if ((district_id == CANAL_DISTRICT_ID) && (! canal_district_tile_is_valid (tile_x, tile_y))) + return false; + if ((district_id == BRIDGE_DISTRICT_ID) && (! bridge_district_tile_is_valid (tile_x, tile_y))) return false; @@ -26992,6 +27023,11 @@ get_energy_grid_image_index (int tile_x, int tile_y) int get_bridge_image_index (Tile * tile, int tile_x, int tile_y) { + int SW_NE = 0; + int NW_SE = 1; + int N_S = 2; + int W_E = 3; + bool bridge_n = tile_has_district_at (tile_x, tile_y - 2, BRIDGE_DISTRICT_ID); bool bridge_s = tile_has_district_at (tile_x, tile_y + 2, BRIDGE_DISTRICT_ID); bool bridge_w = tile_has_district_at (tile_x - 2, tile_y, BRIDGE_DISTRICT_ID); @@ -27002,23 +27038,23 @@ get_bridge_image_index (Tile * tile, int tile_x, int tile_y) bool bridge_sw = tile_has_district_at (tile_x - 1, tile_y + 1, BRIDGE_DISTRICT_ID); if (bridge_n || bridge_s || bridge_w || bridge_e || bridge_ne || bridge_nw || bridge_se || bridge_sw) { - int ns_count = (bridge_n ? 1 : 0) + (bridge_s ? 1 : 0); - int we_count = (bridge_w ? 1 : 0) + (bridge_e ? 1 : 0); + int ns_count = (bridge_n ? 1 : 0) + (bridge_s ? 1 : 0); + int we_count = (bridge_w ? 1 : 0) + (bridge_e ? 1 : 0); int swne_count = (bridge_sw ? 1 : 0) + (bridge_ne ? 1 : 0); int nwse_count = (bridge_nw ? 1 : 0) + (bridge_se ? 1 : 0); - if (ns_count == 2) return 2; - if (we_count == 2) return 3; - if (swne_count == 2) return 0; - if (nwse_count == 2) return 1; + if (ns_count == 2) return N_S; + if (we_count == 2) return W_E; + if (swne_count == 2) return SW_NE; + if (nwse_count == 2) return NW_SE; - if (ns_count == 1) return 2; - if (we_count == 1) return 3; - if (swne_count == 1) return 0; - if (nwse_count == 1) return 1; + if (ns_count == 1) return N_S; + if (we_count == 1) return W_E; + if (swne_count == 1) return SW_NE; + if (nwse_count == 1) return NW_SE; } - int owner_id = tile->Territory_OwnerID; + int owner_id = tile->Territory_OwnerID; bool north_land = tile_is_land (owner_id, tile_x, tile_y - 2, false); bool south_land = tile_is_land (owner_id, tile_x, tile_y + 2, false); bool west_land = tile_is_land (owner_id, tile_x - 2, tile_y, false); @@ -27028,10 +27064,10 @@ get_bridge_image_index (Tile * tile, int tile_x, int tile_y) bool se_land = tile_is_land (owner_id, tile_x + 1, tile_y + 1, false); bool sw_land = tile_is_land (owner_id, tile_x - 1, tile_y + 1, false); - if (north_land || south_land) return 2; - if (west_land || east_land) return 3; - if (ne_land || sw_land) return 0; - if (nw_land || se_land) return 1; + if (north_land || south_land) return N_S; + if (west_land || east_land) return W_E; + if (ne_land || sw_land) return SW_NE; + if (nw_land || se_land) return NW_SE; return 0; } @@ -27039,26 +27075,96 @@ get_bridge_image_index (Tile * tile, int tile_x, int tile_y) void draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int era) { - bool canal_or_water_n = tile_has_district_at (tile_x, tile_y - 2, CANAL_DISTRICT_ID || tile_is_water (tile_x, tile_y - 2, false)); - bool canal_or_water_s = tile_has_district_at (tile_x, tile_y + 2, CANAL_DISTRICT_ID) || tile_is_water (tile_x, tile_y + 2, false); - bool canal_or_water_w = tile_has_district_at (tile_x - 2, tile_y, CANAL_DISTRICT_ID) || tile_is_water (tile_x - 2, tile_y, false); - bool canal_or_water_e = tile_has_district_at (tile_x + 2, tile_y, CANAL_DISTRICT_ID) || tile_is_water (tile_x + 2, tile_y, false); - bool canal_or_water_ne = tile_has_district_at (tile_x + 1, tile_y - 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x + 1, tile_y - 1, false); - bool canal_or_water_nw = tile_has_district_at (tile_x - 1, tile_y - 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x - 1, tile_y - 1, false); - bool canal_or_water_se = tile_has_district_at (tile_x + 1, tile_y + 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x + 1, tile_y + 1, false); - bool canal_or_water_sw = tile_has_district_at (tile_x - 1, tile_y + 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x - 1, tile_y + 1, false); - - Sprite * canal_centroid = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][0]; - Sprite * canal_n = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][1]; - Sprite * canal_ne = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][2]; - Sprite * canal_e = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][3]; - Sprite * canal_se = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][4]; - Sprite * canal_s = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][5]; - Sprite * canal_sw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][6]; - Sprite * canal_w = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][7]; - Sprite * canal_nw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][8]; + int CENTROID = 0; + int N = 1; + int NE = 2; + int E = 3; + int SE = 4; + int S = 5; + int SW = 6; + int W = 7; + int NW = 8; + + bool canal_or_water_n = tile_has_district_at (tile_x, tile_y - 2, CANAL_DISTRICT_ID) || tile_is_water (tile_x, tile_y - 2); + bool canal_or_water_s = tile_has_district_at (tile_x, tile_y + 2, CANAL_DISTRICT_ID) || tile_is_water (tile_x, tile_y + 2); + bool canal_or_water_w = tile_has_district_at (tile_x - 2, tile_y, CANAL_DISTRICT_ID) || tile_is_water (tile_x - 2, tile_y); + bool canal_or_water_e = tile_has_district_at (tile_x + 2, tile_y, CANAL_DISTRICT_ID) || tile_is_water (tile_x + 2, tile_y); + bool canal_or_water_ne = tile_has_district_at (tile_x + 1, tile_y - 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x + 1, tile_y - 1); + bool canal_or_water_nw = tile_has_district_at (tile_x - 1, tile_y - 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x - 1, tile_y - 1); + bool canal_or_water_se = tile_has_district_at (tile_x + 1, tile_y + 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x + 1, tile_y + 1); + bool canal_or_water_sw = tile_has_district_at (tile_x - 1, tile_y + 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x - 1, tile_y + 1); + + Sprite * canal_centroid = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][CENTROID]; + Sprite * canal_n = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][N]; + Sprite * canal_ne = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][NE]; + Sprite * canal_e = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][E]; + Sprite * canal_se = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][SE]; + Sprite * canal_s = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][S]; + Sprite * canal_sw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][SW]; + Sprite * canal_w = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][W]; + Sprite * canal_nw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][NW]; + + bool available_dirs[9] = { + false, canal_or_water_n, canal_or_water_ne, canal_or_water_e, canal_or_water_se, + canal_or_water_s, canal_or_water_sw, canal_or_water_w, canal_or_water_nw + }; - + Sprite * dir_sprites[9] = { + NULL, canal_n, canal_ne, canal_e, canal_se, + canal_s, canal_sw, canal_w, canal_nw + }; + + int dir1 = -1; + int dir2 = -1; + + // Prefer straight lines. + if (available_dirs[N] && available_dirs[S]) { dir1 = N; dir2 = S; } + else if (available_dirs[E] && available_dirs[W]) { dir1 = E; dir2 = W; } + else if (available_dirs[NE] && available_dirs[SW]) { dir1 = NE; dir2 = SW; } + else if (available_dirs[NW] && available_dirs[SE]) { dir1 = NW; dir2 = SE; } + else { + for (int i = N; i <= NW && dir1 == -1; i++) { + if (! available_dirs[i]) + continue; + int i_prev = (i == N) ? NW : (i - 1); + int i_next = (i == NW) ? N : (i + 1); + for (int j = N; j <= NW; j++) { + if (! available_dirs[j] || j == i) + continue; + if (j == i_next || j == i_prev) + continue; + dir1 = i; + dir2 = j; + break; + } + } + if (dir1 == -1) { + for (int i = N; i <= NW; i++) { + if (available_dirs[i]) { + dir1 = i; + break; + } + } + } + } + + bool draw_centroid = true; + if ((dir1 >= 0) && (dir2 >= 0)) { + int opposite_dir1 = ((dir1 + 3) > NW) ? (dir1 - 5) : (dir1 + 4); + draw_centroid = (dir2 != opposite_dir1); + } + + struct district_config const * cfg = &is->district_configs[CANAL_DISTRICT_ID]; + int draw_x = pixel_x + cfg->x_offset; + int draw_y = pixel_y + cfg->y_offset; + + if (draw_centroid) + draw_district_on_map_or_canvas(canal_centroid, map_renderer, draw_x, draw_y); + + if (dir1 >= 0) + draw_district_on_map_or_canvas(dir_sprites[dir1], map_renderer, draw_x, draw_y); + if (dir2 >= 0) + draw_district_on_map_or_canvas(dir_sprites[dir2], map_renderer, draw_x, draw_y); } void __fastcall @@ -27218,12 +27324,13 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis case BRIDGE_DISTRICT_ID: { buildings = get_bridge_image_index (tile, tile_x, tile_y); - era = 2; // DEBUG + era = clamp(2, 3, era); // DEBUG break; } case CANAL_DISTRICT_ID: { draw_canal_district (tile, tile_x, tile_y, map_renderer, pixel_x, pixel_y, era); + era = clamp(3, 3, era); // DEBUG return; } default: From 16c7dfc4651970f034d258d525436b12fb3bb911 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 14 Jan 2026 07:42:52 -0800 Subject: [PATCH 177/356] Working through Canal rendering --- Art/Districts/1200/Canal.PCX | Bin 59544 -> 75371 bytes default.c3x_config.ini | 1 + injected_code.c | 12 ++++++++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Art/Districts/1200/Canal.PCX b/Art/Districts/1200/Canal.PCX index e5365359873872b35558214422e233fdfaf752a5..7cb547d0b248c12f0519c831b1765132bab84eef 100644 GIT binary patch literal 75371 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

#rAYmu6FX4 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 literal 59544 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 AsQ>@~ diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 5cdc0321..0a9032e0 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -837,6 +837,7 @@ enable_distribution_hub_districts = false enable_aerodrome_districts = false enable_port_districts = false enable_bridge_districts = false +enable_canal_districts = false enable_central_rail_hub_districts = false ; When multiple cities share a district (i.e., the same district tile is within multiple cities' work radii), these options control whether those diff --git a/injected_code.c b/injected_code.c index 7a1f65f0..6434316b 100644 --- a/injected_code.c +++ b/injected_code.c @@ -27155,8 +27155,12 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren } struct district_config const * cfg = &is->district_configs[CANAL_DISTRICT_ID]; - int draw_x = pixel_x + cfg->x_offset; - int draw_y = pixel_y + cfg->y_offset; + int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; + int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; + int offset_x = pixel_x + cfg->x_offset; + int offset_y = pixel_y + cfg->y_offset; + int draw_x = offset_x - ((sprite_width - 128) / 2); + int draw_y = offset_y - (sprite_height - 64); if (draw_centroid) draw_district_on_map_or_canvas(canal_centroid, map_renderer, draw_x, draw_y); @@ -27170,7 +27174,7 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - //*p_debug_mode_bits |= 0xC; + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -27329,8 +27333,8 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis } case CANAL_DISTRICT_ID: { + era = 0; // DEBUG draw_canal_district (tile, tile_x, tile_y, map_renderer, pixel_x, pixel_y, era); - era = clamp(3, 3, era); // DEBUG return; } default: From 73797fa4b02d977aef356c51bd2b717fdf33aa94 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 14 Jan 2026 08:29:13 -0800 Subject: [PATCH 178/356] Mostly sorted out canal rendering issues --- C3X.h | 2 +- default.districts_config.txt | 16 ---------------- injected_code.c | 23 +++++++++++++++++------ 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/C3X.h b/C3X.h index 91640047..2cb3a82f 100644 --- a/C3X.h +++ b/C3X.h @@ -1718,7 +1718,7 @@ struct injected_state { struct natural_wonder_district_config natural_wonder_configs[MAX_NATURAL_WONDER_DISTRICT_TYPES]; struct district_image_set { - Sprite imgs[10][4][6]; // [variant][era][building_stage] + Sprite imgs[10][4][20]; // [variant][era][building_stage] } district_img_sets[COUNT_DISTRICT_TYPES]; Sprite abandoned_district_img; Sprite abandoned_maritime_district_img; diff --git a/default.districts_config.txt b/default.districts_config.txt index 38b79f61..31a10891 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -321,14 +321,6 @@ happiness_bonus = 0 #District name = Bridge tooltip = Build Bridge -custom_height = 176 -custom_width = 112 -y_offset = 24 -x_offset = 0 -btn_tile_sheet_row = 1 -btn_tile_sheet_column = 7 -vary_img_by_era = 1 -vary_img_by_culture = 0 advance_prereq = Industrialization buildable_on = coast defense_bonus_percent = 0 @@ -343,14 +335,6 @@ happiness_bonus = 0 #District name = Canal tooltip = Build Canal -custom_height = 176 -custom_width = 112 -y_offset = 24 -x_offset = 0 -btn_tile_sheet_row = 1 -btn_tile_sheet_column = 7 -vary_img_by_era = 1 -vary_img_by_culture = 0 advance_prereq = Industrialization buildable_on = desert,plains,grassland,tundra,floodplain defense_bonus_percent = 0 diff --git a/injected_code.c b/injected_code.c index 6434316b..c1c01980 100644 --- a/injected_code.c +++ b/injected_code.c @@ -6066,8 +6066,8 @@ override_special_district_from_definition (struct parsed_district_definition * d def->dependent_improvements[i] = NULL; } cfg->max_building_index = cfg->dependent_improvement_count; - if (cfg->max_building_index > 5) - cfg->max_building_index = 5; + if (cfg->max_building_index > 20) + cfg->max_building_index = 20; } if (def->has_img_paths) { @@ -6284,8 +6284,8 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i } new_cfg.max_building_index = new_cfg.dependent_improvement_count; - if (new_cfg.max_building_index > 5) - new_cfg.max_building_index = 5; + if (new_cfg.max_building_index > 20) + new_cfg.max_building_index = 20; if (reusing_existing) new_cfg.command = preserved_command; @@ -26345,8 +26345,8 @@ init_district_images () continue; int era_count = cfg->vary_img_by_era ? 4 : 1; - int column_count = cfg->max_building_index + 1; - int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; + int column_count = cfg->max_building_index + 1; + int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; // For each cultural variant @@ -26421,6 +26421,7 @@ init_district_images () Sprite_construct (&is->abandoned_maritime_district_img); Sprite_slice_pcx (&is->abandoned_maritime_district_img, __, &pcx, 128, 0, 128, 64, 1, 1); pcx.vtable->clear_JGL (&pcx); + // Load wonder district images (dynamically per wonder) if (is->current_config.enable_wonder_districts) { char const * last_img_path = NULL; @@ -27094,6 +27095,13 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren bool canal_or_water_se = tile_has_district_at (tile_x + 1, tile_y + 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x + 1, tile_y + 1); bool canal_or_water_sw = tile_has_district_at (tile_x - 1, tile_y + 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x - 1, tile_y + 1); + char ss[200]; + snprintf (ss, sizeof(ss), "Canal at (%d,%d) dirs N:%d NE:%d E:%d SE:%d S:%d SW:%d W:%d NW:%d\n", + tile_x, tile_y, + canal_or_water_n, canal_or_water_ne, canal_or_water_e, canal_or_water_se, + canal_or_water_s, canal_or_water_sw, canal_or_water_w, canal_or_water_nw); + (*p_OutputDebugStringA)(ss); + Sprite * canal_centroid = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][CENTROID]; Sprite * canal_n = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][N]; Sprite * canal_ne = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][NE]; @@ -27162,6 +27170,9 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren int draw_x = offset_x - ((sprite_width - 128) / 2); int draw_y = offset_y - (sprite_height - 64); + snprintf (ss, sizeof(ss), "Drawing canal at (%d,%d) dirs:%d,%d centroid:%d\n", tile_x, tile_y, dir1, dir2, draw_centroid); + (*p_OutputDebugStringA)(ss); + if (draw_centroid) draw_district_on_map_or_canvas(canal_centroid, map_renderer, draw_x, draw_y); From fbb0780e33a0204a8733dbdd7bd5673c75a4ee8e Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 14 Jan 2026 10:08:56 -0800 Subject: [PATCH 179/356] Canals *almost* rendering perfectly --- Art/Districts/1200/Canal.PCX | Bin 75371 -> 76573 bytes injected_code.c | 204 +++++++++++++++++++++++++++++------ 2 files changed, 170 insertions(+), 34 deletions(-) diff --git a/Art/Districts/1200/Canal.PCX b/Art/Districts/1200/Canal.PCX index 7cb547d0b248c12f0519c831b1765132bab84eef..5e8a60fbaced776b2eca72c9b1f30fdd9d3f1447 100644 GIT binary patch literal 76573 zcmeFaeO#4Q);I3U*a*!?YTWA2TZWMNoWVZDOX5*12WM?-ucER{r1LBt|c$@k>^zTfLQM^M4Hr=CBa&*cYJWuR z0Y&yI@P80Lf&q6wkszwY{sb^-eeQsVhu=FJ&9$?VJUp?FI0Pm+!V&;k=|$pD?_97* zy@5aBy&o2Shbfhi!)4=IApu!s#&2-Wp2-9J^0zc`Nt{dI&jUB<4`xg{OojZ;tr83e za65j$?;(ZXn-t*Ri9^XieP=j-W;`hRozPkNoqixuokDHW1l<#yeh5^e5NHZO$@Pi+ zaejF4k?O}6P=F#$C{X~Kto|<)gR4|=Oq>hnPZ7aiHNCZ5>_hk6ckjZknAEqHzhyFA zG{4$m$|f8i6n0K2?gV=6k7#F-45A0c?J+ zP#Q21>yqo#7}kyN0y-yqM4E5wc8?(cX(I=E&A z7|>183yab|Bx)9@_xsz>@nKpK$~_0D0hzAS;e}cSA7XZ+V>b?BmZOx4pPlIY>@_DK z5sv_~29S_g?k>ioT?(&f5;1QIQ3psHwPF z72qgs`mH^vAVk!zb?nDM)GE}1&k3VAp%4wC1IS_U#Z}YxRJ3$U8u`4Cc=5YE9ma`9 zhk%N#(%HF0$O7wre;Yb-XP0JG1Q;ER6Avim6O$_)wK#~0fkhc)BLw9M4izL52PRzc zoM(FqB_s{1luTs!X-$V{l=azipaX_QJSbjClq^8+_qU;AWwAorW(PSiTh@!leF{26 z>{7>J97IgiRElG>@m`}c3=_# z&I0*3a3r=DIvQ}C$01Fq$T`titnj8q(4P>yHv?-D6yzoEDdKKA zSq}hB=ap|8Orj>#Thz(gatMOKhUG;YG3u!!b(8o;(H{Zni1}r;Kuf{h|2@>P9>elC z;JDpF%H7H^E&Y>=mKz2`V3o^SQRf{hYIZ32vnD@woWgM)#|~2#b)|A(cpZ2vSfhjQ z1q_Ai3X2?4WM%?ocd|5p*kG#iPmKiO_ls81UY_YNFj?%AhZOREC%$K^p%Eyu$Yu?| z3YDr=%CwYy@4x#y_vHdt|DA6~VD` zKXkO_nt=FGMeQb!q8h0B5i<9Ff@v{)W&W_iBtsKQpekPlIWLNugn?1sE`I|`R65=mxb}XoriUWv_yX1*3ca-NpWUQS8q`=Rfzq-dYbf`Ce&Z z^hCAeLyRB#;=M1{zUSf{H826)@gfj}T~U%CLr(#D*8gnBIUMJ4>^7CKJ(D+}$L$4v z6mm2OzT3f3p4Z3CV+AN6T6UQ~YcOR-rF|%B%Qm4yW+r$)=@q>inpL~@4wX@EIW6iOs*)-75g@$3~~|&Q|gT<5jy1e62t3z z?p{xNCuE`vZz>bDAErfRo&v_K@|li{IL_nPYbsTw@(om(IxRsp86h@trVfA&Y099) zF;dj+Hh%_Azf^1h*TP~ z&8`8}VsYsd$|D_4cU;DC9>;#uIJ^Qod?()FxS>d42vhQ?LKNlqXt^>DVw6taYyPs) zRG9$f8iJ%mv?Y}UIM}$jzX#S=d*TU&BTpD9(g^IhnHog;NWW)CK@*=c6NEm%BnP=@cPDhNJtv@vSGdS@(*`_2yCYuXf4kyXhJ!x{K<|E z9OrS=nm!~`zAi7J^6a~WMRWBK`ZFb$Lg6Y@9Ygmelw5bv{AHslHxmrhiqBg_TVaB? ztIgP9teH_0$KeD+LgU$X(>Ty)V8;eRwicaJ#NOu3X zx}&rReHcnO^tebsXe-?gK|o#k~sGyEjTxb;d$XX2L^HjM{6`8{9lJ~=g=1$@+MdH4H9xNHd6Ld2u7~R-^>@gB+t4P0gbT{KC5J6+8a!?&6g+Iu(ki z0n}|3Ei1*&CX`O{90z%-@p~S}VN(LCeU3WtbyPq#aa6?)Gy+VH2H!=H%FX~(U-`B9 zLoiMy$a7KAL~1i5`*}Nzcr{<_0CH?OGg3HHd52<&x(xg%iaa6x4*wGX_E(7JRM%p( zQHvpR`tk~c7s+Hn(P&yDYCeLXGHns7SJ4AOBLx`>a-~y)as)>Imdb&uw+r_+1Hdti0If-dNwQhCRk@Cn zr4eIEN(+wCG*HxKuMxF{j}<`Et1Zc%a`M*0&euB6PMM};mq0dD7d%!dYS(0ox`Cq0 z2h|;8BV_3~*DBd2n^7#7>>6-r6XGkDiY0IjUTnSAe(L#LsY& z{Cx7(3!Sfao}DtOZ{h9Qf;@b6?DJI72D)0Ho`+fdkEFpPW#;JeLQtsI5Rp(0mV$WR ztPE?a_*KOqd%xm91zT7sjQT9{3XQOlQK&d&hO#n~g9szL=;rBd_`iC`uzBPZ? zXnHpnh+nHL6=%~jFe;*;L}_1M=2x6uh*KSNB}-2+CLCi<-yD^+|?P6CH&b49Oh+NRW91+?j24?(N*;`C{ydG>U8$pDZu z6L={KZel<(de}8_ZX;A7RllG_qBP=PDo5<4haprT^>?B_tZ;uB18ZiQI9pnY4{LMZ zZ31Yr&o^M7Blta!qu~Kf%zE?tji&hq*-nNR&1p$}qe8u07C}SdFrW&z?~N*>fU|M- zFC4Tr53d(Lfk`mR?zJLP* zvZWoALH`6*zubb$IuLgkfYsq`3LhxW$}81#3(DZ;#XXvGbfE6SZ_5@1mpZY%fPCS5Z!FDo}yb6@3^pU_b{`M^&k)RWnf8k2}qxW=p|b zRPDTIr)rC_BdbKzq>0w~*{_H%dw#M~RBt(>G|Rq@|C#@6L{eb_z}FOr3x@0*l#CHh z{Is6z0HIBXB4$?b+oYI+WM4aSI0~BChJWREu>90(GBO-4Fw;(s%t@FH zsx&K2+MJc-kTrdxIH;m^)+FDR3yX3#BxL92j0`KPoa~lss9d=)AUSvND>+5UVTsU} zcV3w>ZlUl>Je z;)3|O9<{h+dbinJnNE6iRhus1DVX7Be9&4BX4q8b12_YRuUwcQd-Zn?vvbS!Ss6Al zwKy;L|64xW2O}_Ax3)s;&s{0LEf+2Cmd%}2A=(Nm#jbLhTU?GotFB^UcQ@ri`R-W- zmjfN*;Kb=!4oO)c43!SB(d>krk!ZuSVhUX0U~OG zU--lbpW{FE0p6!1JOp#+DE>;aJM~}cgm{w*7o7u&U4?h(mgC){@(>I-JR*Wk{{*Io z{)zGL(E2xfZE#F3>)(5_Fv^t$9Euc?e@Wb|2UeHha$}!qHe=XHzq|FpOLzx-aucJ6 zk)ljoDUd6LadPkeWyy{kdW~NX?2!sFoY85?F4s+>zMMl1Y;f*?Am_X4RS zFoo1{hWx|h5PoqedNItnVJ@!THc|fY)5CdHl}U1tp$>qdn(%ubNAttEdQO=?J7L-& zE^gpJU`WVxbr|8|{u*>@R#){ha=w_2ZqZxJXL!naSXGW3#bmemIW5vJ52CnD*2xIR zfN_rfcbSX$!6d%NkV&JGX5@ENd~uDbG^GQnlcI`gLHTJm@hQPD%+}yt{5`rI)HsR; z+%5#0172R>1jZuwV;nq=qAr=bbh&18*`4&cTh-lu2`_T18a6!XL{8e*@uNu`+3~|goSnzf^3V>mX7iUPOqgYYA9h2|pPzM2a(Ve}vt$4R z?I06b904Q?^@uT6KT)=4-`ot^~Bn{wIP7x@20@O9Uqw+o4UiH6GtyFkj#nKE<6%Ohz zzv?W>{DfuyXvT7v>8~Rl{UeHP45gwb5_7S|q9y>!&bf{2L45+}0UrfA96rZ!ocBO1 z-pAy^`j=gjT%Y}O&ISmli!Fdko}gKb8X@w;_(QvLwWp)?Vf=?J=8sOmNd&#HtsG*e ztt9FXBOFFh$k5S24p4Pll?O;patZBDHtu(@IN|O5Cm8-TG%p9&q%Dv!H3{Rq`z8C2 z^*?AX;R4tKqcHohWejRCHkpMEYpgN|8;CVNq|{#_Dp=+OzWkAmVeXE&qgvu*C%jEn ziC_Il1@AaYyky$E_Py8c=t-x|sF+OOo6ovY=3fOb(0_hH9=U|KAbh~Xlz1rm8-+`b zl3Sst?V*gs#KiYNf_%cMiXVX}IttNJI9oZR@*#h?U@mj72~Eb!uc3}aP-OvCB(V9(Ac_hD9QaIS6$%nC zMl*DV_SDFv>@BIIy%UVC4&x?%Z7u@Q9;}t3AK@=*PL3cjT(MK(9IkuZQ>sWQO6q*W z)|X0j3kN^ajo+B*M(S}Agor?3f_s!{IrUn3yXiQ}zhr`(P-rG`@?O{eCF@U{5j2^8 zG@sqA$B*B-neBoWQ%aHzm+-1toP*Id!FzP-mh7ZR&a+<;&D+7hDuJoO z{0pjL6I7ht98#4*4}BXN;@S(S4mqqElT|>IV&H^?Bm_~Le!|`JINBc4EB&4MJMdOL zkU>NVgvHsM1n+*{BZAjc&MM`wF{vDrr$|Ia5aJ2RrE(n=T!~TahPav>>0JW7)4ojt5LUbw7aI6X{p&)M-Xq3o`SOYaU5urHe(Vn0RuE3ULHJ;%( zrJ!{bkRdP>G0^qtiQ_1r!<*7cIZ1XHGrE-h(1zE(Apa1R>VV+6+DfomBUw!)p2>8@ ze0H}v@%QcKkxiB3O8TZ3cdjrhm}JSY|2x# zwIa^R1$=Y2(!e-}>+Ue1t31oG6=DaR-0giM#o-Z49gdo2I3&6D9SB!JGuj54a*1m; z#0ldczD98{QBai1d}`-m&*!lhn$$JahHPi-Xg4XGNwG}I)9z$VIEL}{6PNJLdF6Px zDUoyL?^;b!zr}3Z3c#c#ruJu|MHUAGnrW;`H@2aCPaKdJL~~UVwS)GO@`SX4=Ob{Tx~Z1F1?vlj(-} z>`wFJ)xUqC404nY5TsOqp;9Vx52Fo)gwf16UWvU(b@D1T%2%{LoV{}Qg#DK>KqeFw-0|0vfh%%JDV()r)Sb+(@ zO)7>|rc)d<4VYe0aAz2?de)5%>VzVDz&5-(}u{ z5h;c_*GkJsd(MOvCnTs!JbHO(f?39}Q)2IH$)fr707vg6NWpf{v#l`FAtBUd_A4#s z5;3&i#DuIok2x?*VV+0v2&}_7W8J=$KOx4VrR&2BxNUw2v2+--WfZ4T*0DUl-c=>wv1}v+7*+3T zw-Gox>QS`IMRBeG^MkF$hpsQe`u{7}Gi@>0d&lw1Q|{U(tq!yo&3K_{Frk zD?gC3vb^v$PhC$yXds2oVWEYG`h5x8UWj%*wMi{t!Fhgzx3Dj2N@(U8l(nXsh z$r8#sj0%hO0^;BmiU|;?0q46n{~Z&%&_rg-*9{2{hRU&N;jUx7LT9&_HT)~4DRZ8h z`kUX6dNEIY*?Y-!pE_mEi+}9X>se5h`U7c<*fg{Qe}CSFi8I`*p*4g_sm>1n)>@!x7S&46SGkn^ zoSsCQD(vad2EJIaHrU0rUsU88G!+3ObnckB3h2&1gGIRP3-ty&LOwAbjejvFJk4fQ z7nE7Zbl8OG18_JXS&Th**r05ZTRIUZiD<4m)1nYUX+1CoZz@atb zDkgHb0XIe(azdW5LMnM$pY7G>k1x)d687#knFy;#fOAry&z;>)}jNB#acQ@gqK)SM|6t~q6z9qKjKH9T!>rNJS=AYaEsFnLZj zV5&K^DS8N`G-T{$vr*p0rCaC-X&~Vl(hruYxl1duv?)TK`^Oq_slwuo0lC$1OO-AZ z#26o;IU?@9HPH}I8jyv$Hy6GbCY75>(7?>=_ z?>^oEDb6Z`XdME1w#cgTEryacVpoaS_hFShxZ^C|s$YYT8C22!IW1Q%WnxgnImD%` z8R$1W6QJ1FF&~Y)KcgQWEUcOX%GYffT@Pf=nC|MFS!*GaC=@vkLi;x==OB`zPNDS~ z2P-#!A>Z_Jkx}Cinsg1tqCG9Xqc+oUXy)p&K-#FP_CsP*n$fC)S{Te%)MbYjQr4O< z(q9&Jl~}u$ouHV16psWzwo{|>6z1b_r7)fIaP(E zv6&nfK$^4SVCHbYfiO`npt~coL$s|C$I3)=CFSELQC*G&E3NXPtSOhfpb{39YyyM> zVqeuJ^qFJgA~ZNQWZjzZ9DIc~soBCO^-62jF@~fCEiHexIDSrID$LJJgFxYn{Dwzn-M1(5zIAtV9g(>_<6e!(3;Mtx3zzU%Xiw7Ef zsZ3v!aO?lJQCybg<3_m1z5^xdpZ1uybbq1648Gm83A0)|Axan$Q!DBv!eqAK;zAk5 zje1F@L|`xu#E{}SLWgm66$j_S_|3&~s8Gdt%n_R0u%G+@de&}&c4@?f7Z&`SE60)` zxh8LbRj<@hWwYkGjmlfNtxUB3X|kVa{h%JmoHE_jW5L=7*KSs4*Y`+FkaFsh`ld49 z1Mx(aR8dUVzV-(%n{iboXbr@zsY|R zt!3L5=8bZjJF9F|YA>q+MO(zNVlZA#Hcm>U0l`W+35f=fv8Dv%J(gog6m=NgH?I9g3rkbe7TA)5R)p-ZGq@J ztHdqVA*c>hYwRcBLH?x7Kp z0m)o}poEB7Cg@txKM@z52<%!EQZht^vr4oS6a^8zv}v6B4qkM&kYl$AxS!)_D4W+r zM)h-5mtW8Wpl~PRQ)p-3G9Lj@u6^yz9?eH(((S4^m%8QmIinLNzdsd3cz@RDUcCpV zfXwRKtwf#Ate;lRGg_D8v&^_r16Y8#bx|LVL`YNE*MBieX#|yfgyA1l{%1{ zpC}UI3S!#mS!BFki>%GDOx>L_jeprOU?AB}oqOAd=nl&~bmtkEs7;&?c&-wX1S|xR z{9E#n!}R%_x8>aZkEX;;pNs0sHHJ+U63FL>od{(vgHDrMAX*A?vNx1(f_6S{!@?p% zxleBPnu;|!yl3qGjQiK|$0A)aQHOvv!v* z-r3%mW|=MaEw@bdx0o_PRr7N2e;FY@aIk-Id~0yzGj-j$CGwyek*1(L#A|uNI2k2f zg0WD{VjZxHvTKs#6o~{+I6y(`!$|#R&(<5K`9bF!2#K8~F1#O}C{Azs{j5=r1M_-W z`6U*}%91sPR8p#-yrgnXLOH_Bc5X6cZYeYr7FU*F#V=#_58|N8acoQBmMR9r*3t&K z2IccbPujS) z+!ps@G(9s95sqb|`h&9H{Kt!bIfWQ`Nm1p{OJxP*Kc7! zlV0L#<#PcrpWEJE5A{z~{pcoFoZ%RC_pIFxI&uV7Z0GInPmXXMXNV97p(RaE&IKjl zkHrH0{U5WxG8B-}&E|Fe^qNq+VL*@SR3jAt{5ig3&N`q*10F<>B4oiu(OQxm5E&8b z{wCHXqUA_DmC0$;DgF@q{q1t~O7~zJq{%T|nXV0H~5&E>R=S{u4Nyq9X}wDOE$sJc>Yc&`U%wFlg^CHE4IMW-0ghB85l$)0NL2>wvi?(f zgSipZ_}Tp(NLDeM6N}m25|MA~Q6kMjiYgq6aM+DIq?zhR0$4EW9@`$YMKx@;lP>BR z>DGO;^EuOev9m>qGVc1qNt5+37wagMEek2;#X*1PDVw0(-uOaMS~-WwQJ3v{OUwdm zUK3kB+wnG{y}Tnb2Nz|@iv~wTRe@K)M6-8;VvJ3%g3QNC9A-wKU{pD{fr}YQ1?BhD zNcA14ewk{J`reLLN}<3vFHZ=tfsCaumn5?ERnXZ?K7u>0^lFvm_y&UOKJH%mQRC%1 zaEV6ey_2R5exYPU|1C-QdwES-crH42+sE{4H7L?epl6cHKp&hD zr5}MVrwI0LUg17As-Jg+IE#EX(DwIdMF#kjR9pq(2t`Us-9R>=Ta2TdLL$Z1P{{Q( z-%&G3sH;F^$H#!g$rm{Ja+9ZvI+(2yhHTeTdpTcy?lO}L3ugQGy zA@?}stIJe6X`r5wZk=mo?ss+)9%MPcFtjjpK6+m}3NHPvr2y{obTyrYui_oo~)AL0b&% z$d+_7JgV!SI#*ArXQY>91z6|OuAa_Lnv#v7nf@-`Hp*~Nmov{N57fH25%Xr8kWyG| zv!d%$8Xwt?yNUx9zkJHcER~Alg|P)y72@EBt_}koM?UvoyFfCo@XDmJpys=<^h^?p zl>|hP4|7Jrw8t{(BdFvexgSllA>C+h)&w{jBV3049qp#hb&=h9r~-|l zC_Ez=rZ5^9yGg~o2EGXgZ>!qZXX@hReYW6`P4Jg&$1Y?bDd#NXXvL1UXg!r zwJ}tFDB3F%3w%&rBm{fw@9R-M2hF{h3I>@dMX<2gy&YWIk{h1329BA~q%HophC_AzXy-~&^^Ejt zgyga;zrFVvG>~V#zg>XP9Dl$zry-Y>hoTP-Se$ScS>^FEg zGiO}IXp40;v%R2c=fmhntKc&V1qexo{?Im4KtT`LydG@WCax9Dm5Gss=$P{}A#kKJ z{HaTj@1y}~bpPeiCrvL)*HCGQD69gzqTBmn1(do1iE|)~eXN5^LP->YU(SV*L=u7n z>lgP@er(PfU9r5>vG0CRg^R>9*_KLH5GKb^^eHfqD(vB)dkLMaS5b={_DAs zjsqQBKNJTmstU%s2Qwa9T|ud`3_BMo0gYNEF0RyW2*z2v5m>h8`OM3K5|4r2#@od| z(-@lE0?>CZ4<|+Lzjhwd$sn`*v2^8N6<{L(u6TAiY|^Wh$rFpj#?TiB8 zaQD($g@ag8OLa*W%9|tXOlX1l>l{8OaY-%N62=!5R4bF%0FRxV961r&s)k=DnS`2F12+;wHr!OL~D-7iuZ%tpZ%1Y;g zT;pHRTprmjk3vV{82fcH>;v}e8c>j7N416oIl}8%*!5}>i>~P+NQC<*)!@LsQO@_D z9btIpukx9{LK&jhp=O-352dRj(kwH45D^&2_paamH7d`{AqlrKhcFt46}HT6@QUL& zD{;iEAaaRni<&>g*wNRqba}<-EPQ}6Ff^-;&DVL>gh_Vs8rYpPZd_WWc;^k-eyI6b zFl}LkG=xgmms^0wovA}u@6L=aJ*84Xz0XvX&W^}@M!xdb-+d(76J>v^-rJEu2E|(z zH8W-oSu@^SeHf)MJ4`au_vWYiM;;0Lpk{AHE_{aZ~+)3pk$TxYtTo|5CRa+uvn zo*_T_>+ha3Z-Z>8q{bm@p~I9a&mArD7T8Sob;-weVy}c)HC;@t-eK-n79c{11p+| zJS?(B4dniQ2MF*M+VL1xQ)PMRhfUm01P>|c&rz;WTfq2j&G>zCC79J6ZO3iD9o zhp32=upB2iWr4F>VIES+sI{j<9Ho;}8DQ}Or19?FRhZZnf?Q0@d-deBD-7jy5yJR5OVy5+%l3s{?%n>mnCDBCrwwC zHy{>X2jg6omy6j`D?a+2{OiQu^=Mc?c$v& z8;QpKvPoK=w|8`PJHvwkD&U~efldl8`k{;KnJ66Ti2Czy1F}MqJFH$wKY3>cOO}4W zESr|+?HyfQsSFULLx%!^B7*U1;FT^8RhD0ePMakfPbgVA@67ATvU^H8dguN2jxK&k z+BFoBrQY)&rgb{qbxJo?B_KmEn8R8fsL^>lU3PcF zJ9YQ1+wSb{>fvHXQ{}%ZY2jN$W1PDjI|6mXk44v=QN z&Eyfsl?27ADgJ2SZzZTy^%}F0g;wAYMd^RYtBVc$C}ni92+_2(a@C^n7jsfaUg)$4 z9|d|BOADR~Wmr}FI(whe1H0H-k5WLV8KbfeeT%9rGaDjZ446lO-)Sk(yr#r5w}HZ- z)TNucS_uzdL=9$-nR)9HJX6={-3g-{CUmmXc4!kf5Q2A|I-$69H~5ox-@5HijVY0y zb?ATRj5or}ZA;^TS?|fGhZL_S;wpFC5Z7+|RJnKn_~AZJOCQ(1Jr7oAuDr29T+hWIrFaR^ZTC>Jk?>H13y`*K15{~L@lgs$gl25q zKyLs@bS@0~ff;ed$E3Ey5ok_G)-Rc`|GI2^lw!gWhmly03F^GbpbMRVtU&|1F&i2I z#5sB4sKhy$S0t(*zJOgOujhJM^)dExHeh5sP76=z1!@mC;?}EwuRgu4282C4V-2ga5*McRLDu6&F z{h=H;`Gi~Iye`npr}qQ83xxNx8{jwKkBr|Zlc+-?gquPf;qRAY=WLbx@Q`Z&dGzo; z3B^$EIJu&F7^(9py_XYan_sj0W7L2@7Ct=iyTAB9zvW4HIP`Sy-&@lByM4$T|K@}L z8)@+1rf2s?q{X;=7z5g3NO%ADVH^P#hd}p#3CZHQ_}}|WNDtz(;T{tAevgoD_XpkS zA9~PhLV6G{+}(~bb?b%B`-F76hwo00--BKg()}H;UEQAawRU$+NcVR~cXj&G>)hQn zA>H3&-_;dPmwb2EgmnLjjjpo=oV4ifnvm{4HPn6n_1nKAr2Egbb)A;#Da$z_-GAn? z>nvYSS;7hF{Wb;hqw9*8sXZ*qhlZb9$|MMp57U#{-|e!y$3j2(rEpXXM|k? z*p{?QzvLNVR|B>s>&`EEM%X(6+p==+mpmhEBVb!HX#A3Age?Hul11T{Jo~j_|C%)U zHQVynTJKJuK8=~$hT|ccg8zD=ry_Gm$NQ@Ig96ZYwFHWwgmTc15y zr9Iszr0y-^o-E~_?h{h?R(em?dr$WX>6h~~{BqupU(O?QZ|~3jyhE1fq|^VN$I233 zLrC3w$LpeGoa;u*MxL$Z=%Jkc)!2*3t^!yd*{D@`?3FT^kd)I%J;wi zHy`|)56C6|=7UG4!7i@gKs^3MH8yA7UM3sak5B=`p(Jj(e;@4n#%Wwsn_gs+EkuP& zY!wu?*Hnol9Ec|}UfI<3)%%|}sla@!{G6?@s5UHrRYppFtcNZne$iH}Kk~848D5xN zd4D)v-!Q`z^~Rm3F49DW7Kq*Wi; zyHA)lW3>$OSf|S@86_AkwCc9x@QjLd`E$rM)AzewZ$=%(&P0cRw?u2*z$q z1ZSU{wnXcpkseE&CV4m+gTus)T$v0qx7_{Zz3!+`vmNCY*6 zePoc0@g5-_Ygfxy(DdmLr=jCOJbtjV(-+i zz(>wN1sKs!yZ`}EXdQJQ2MG&|M zh&bZdagmeqs4i{i~u9FfZ2!66#W5kj_I+it}*t$k0h!r)R`9H^U6O zQldiJe^R%O^OoazPnHZfuLwZb#ds{k9S`=dcOo6zR4Ec6l+#o`9Ykbx5g2Z-G0SsP7ENW5O zhs4L?@M!7Con4w$5nyyMPBeDAT&eS2%Qxy@6_L_#MLsurF8){tl1z%zefGCEHMalB zqNa1fz(sj$W!8?4wf&@xV?Y&ni|ICYW>u)`t<=R^EgdV1mHW-uK06z${2N8L%ewB2 zUa?%O2#w2SUQlcyKOsoJdhx1dDNb75-?a9&;KUgdvDS0Pt@Uii4piA8pdzbub}sR- z_@!DpL~C*psMRtUC!-U zt-a$0aRZ|-o?QG2-CT8*VKs82tk34owe2EVZ2@lLignasiBKt0^yn-ZhNhc z+apu`s~XHey9@g1gr-6zo@XIZBS5M~>L%A?bsllcrUk`$#5s9r?LEf1ZBU2p+P!#@ zeqJO*uL<==`c0&dyj(cgu)HYQ-`*D7pv_2^DSD=du{e+a)ln?=%@hYdUg9fbQYNIQ ze3GKo#A`h~X9l}F4DZ+1I}$0$+qjOsy2RUA)Haxl=q|{p6RHZGn4iH~JfuH^2@_zHO?iN#(e`+L1wI9T4FVA!sh`c)nFO7bw*2&%z zRMn>Y>SI!TxftK#Nk7~x874$Q z?nIefQJh<;HTr5jeKlH7?GR62&!sbU>(DS>PkJY0g6}sWnd66PQJD?hBm?LKXaTJL zWur7c!K77J0bkQWLu3<6LXbv(RFaq1Y)v%{q)k z0SN=6yj`Swu=}e{<4Iy)Pn|vx%#*(Otx|}RTBJ+?om-es#4-VcI@Lu~P)36f&PFBm z(`YmFo<@zTm90E#`L>n_;wu`!i%0_eH4<3?s&{XK`rlj(YiCwjNT1rsdrztw=VejM^ zwi4`z8tp%f$Tdcwf%z^KlZY_ffo`ny~`*k&HFPPY9iFp_h9k{L(Gc(^mB z$^@j0fMCU5S4kxS4mK|CGxK9Iw7xo{R-^NjsHHX<`1%I-dv*jwNlP+Xi*@Xr8$Hlb z3S$-gvL9MNBq-Ka8m!T$>r?bbRMFSdSGOuf3kD6+uUa&I*jViEoj{KfvnnC6WM*!o z8J9w z%XSF&mtMhJmxO3hNAy5r3K&o8I|i5Oftq0>b_}dSO|Fz?v96tS%;<(Y4a-XngNq%I zl&e`@-1wSyj20t8Y=%)w-o=ln?~;@y`JXMteiTWgQ=yD-0X9;EU{mKtGcJ!3K6n_* zZg1pHN2^}S_%tYP(&9;RpLs0O#n}%H6*nu3VO> zDSH}RXVAm}gdnUDH6MX#u=RQS=YJ9HXA$}4WvEy_J(60^{$kfuY{|=R^RaJAMoNYr z+#Zt>>#I+RMPJs*p!juSNB*lL9K8EQsyHfpepiAEy3Zl@AkmLUO@7i1$-*uLeWLSN zEp?L?qhEWBcY02=%yToiO>97P1LGjiAaTn|VA(1f4bYKNM<-#Ur=_3eqrH4Ri7EKp zsLhClhJ;pqZKFgQ6+@as@nA#ga%iY~kTw7R5ofd4u)jwWhvUB93j6B_Aj@-p`B_Is(IUplD0|95krSD*%Hs89Gv- zwIU$rA4!8p%FNN_g{XP0Ap*JGDN$SA2B8)N>c<#k&?d1mL!YuLJ~n98>iiYKUZD{- zG72fBDN3#u&1*DQ6YXj$B1NiEAGNIF4T`Dh{v*g5?a z-eZW)8B;N>Rz(p)66naBY;PNvVI)+3yss_;jR`hq!+Pr3$}!r%;pJm!`Bt!iY?f_R zuH$5B#MnK6l(=TJ-@v4(9ECG3Y!MeeXQu75bHvF__~Z(fC!Q8pFs`1&RvFv!K9J{e zw_05KT%6xlCXex*Gq^sDzZbS)Yi?|SvQ3Pg>Q7cLhVq%=Vec-VnrNs@wrqrc#x$=8RcHZE9h6Zv5uskeEAm&b z3W|+im7^1D(C(*_@cb50BKjRg>z4ipG93ND*5G{?2@v%#Q~e<;)QBI z!%Ckre1^R$>{O9}eD#S1e3c;Jt(csMWOdD3MD-K0oPvSEa;AeN5~pt5RYq8ssOjQXebx zw+6d-g+}$o9%jubUfFT}(W0-}ga?p|b^t71SeoL@CvsKFrzvsMjB(4H>~#|tifW&7 zw<1fH<};>ub3vhNm8IfrS|$d+m)%z^&Hu~@`5|_!lHjuxU&t@LjPVSNS-cTr-ZgPC zIUo_=K%$A7Ot7j9@H&8oOf`&l=v4sXhFK*}m9xr_wf#@;E_4fX4xc!EpbSf%F?U|S z+?9#*2K&q{*kEv*@m${A!NZfE3v+ww-GyOut$gO?&huGm=O)8)C+CG(x!Fw)lkduf z0m-?817z;Vu*8*<-Db@#dv~GL#FcaVjhr{qr+2td*gU)3h5hGE3|r}w>t+|`++X%e zSh#Zdz=cy6`al+37)31x+iGx&dd%h>oH#wp0cq(tnZ>kUHM0|PMnV9b4e{0c!bb9q zkr|(4$W=KH5ocy?>s8p$Muy254b8!KEq+^wH?f5YkVH&(nl$a;kwCavFib!%UnmsYHM+i9y@u{B?A z4IUagFwA-BFUGk*mhzY+Y>3)aEz)>~f<-ug!Hc z8Dbrbk!PLzF#HA&>b1c!xvYQh$-*dC7C>}<3DIdpjc+yqMeG^vI3hG6nQ6qEE9Pfa zvg;#luJ~zV>;%sk=*1Z_Hbp|X2?DnnGPnb#fa8dtkm_2X>`GxoC z9ai+o>R1`~DSR_BXc8Rj9*zb(tHg-|zQZfrbm5HY9l|*a+1iuK@C#&LCdjxp| zt^RcJXF>M10qm{xpIyhEbn4#7ttK`(*t^A$km>3$!o@v!(af)O`k+{y5hELf6gVa& z2zeHZ#m{MxetA$}+hm=La10pd*#DqXS~_O0LgGDwofgHcSh94e?~(~I5;Wer$jN^E zQcehs*Vr$8XX`ubWW4>%_ysZAcLv!G3VCgTXG-AGkQJWxi}E#|f!h2K`;ZuD8tb%= z&s;at$r>%A*UZ$c^NiPQP1j`PzavAeb=J1eYoLzI)Y%WQ_Ou=%_0bbT7Q|~j7QCZh zmv8T}be(?4uoZIMf>)b?+0h<%v4=*FVUf}Zoqd%)K1P=jFZE0EmulQc+_@3<2{WZ0 zZXpg^Vkeep`sIcp&P1I16d@So?VvbJLd4SvBd%8%DwlK9XdbO5OeetCxFP4y&$=eL zy!`4+FoiFeBST@6Ri9?WLUMj0WA(nu2wsbjkz&yTHL0f5U{f67?>O?WUn_7lgC;DA zUAI(PKW`uM4ghF1o@1oFZ;X?5{vv&P^t${(z8dR6_#3TX5+9<~1f?&~=i7TOvGpC2 z0xL=D5$vmtUm`WO@lKxFSX-^m!&knuz;?_a>p|Kf+Qqsd8e7de-v#m={UB>6t-Y@% zRueyH!H^7lYmHVj0WB84BxXszX2H6cb@}6;57dM#@C<1NX7$}}^Zqet0zK-Jj8$j| z*bu%tcop)|D==;SbG-piZ(?qqj*BNfrqX2trUqD z@fJ5yZsksx*bJwfV{l2-AHWgrnx*+mq&_}F7Za}srqstIv}2h2A44`F1F6BmHE9d_ zMoq#v?|#YtWBpks8da~;1Yv$>&=}1iF!_W@+Q6;rq8CKVB|7~OPpJt^e@8}-u^wcr zrRAj2>G9g2C4n+JMk}3`EC|thz>BLl2HIlsVn{md2Hdxwut28A1o>w8%Jh^aDfUh= zp3!=Hk1-R{gEYSD(gWjtCj`Nj9kU=Mf6175t;X7Z3~YV9Mn56Y-g@TJW*~N=rybGX zrjH~e7T&rbNU9jEuk@5U89!4leg5uklP&u_KipC;Hiy8HW4o~81e)q{nJ#Gcq)(?w zT~J266J}0tgjHgaXU0!>i<^@w&Cz(<#-#R3^U*Q#;k}?|hYf;J8@d!!i;povT*2g1 zWK#B))Y0AvMpuV%6YCW)D|FHN_yy5RCXCVQxB5;9gx(dBGG>COPQv4-4+8bI5-dE# zR~IbNGUIB*(bO?=cOw0l&{g~(_z6*wE#%LyZY8On14ouhTxv_EcJ@yDc zuo&iH23(1Hxk?X~%~%q@B;Ge(FZ1DPyK|@E6J}yDEJ7vQgPd8K0_IrbJZEubW!&Pp zNpaJ3tK~8{A8ef-pDZp-gfs8bL`!49Q+)TA4~GXZhByte)@#Sa>EYLzpat|Xm{%Ag2ia=_F=hxTFg@&~jj1SodWLU& zMt%_N_ZUxoK3XGIpC5nsKD?|RIrl-Dpz}S8m>T}Oi43s2{u!F$vpA1wpTz~)$3g>y zfpL9CZqag!n1UU0*JhGSWxgIf0#1<`7RFhHp-#WJ$6X^GW-wuEt4Y6L!h#r0>=54+ zkZg?JGtkp_LA>5m6QAyx9_XpjPSB&R^7Yn3Y&F{SnCN%(OQO+qwrIb1JoSOvB_8^a zccSHje0^ZFy=DxYSP*xs)_H2tm6p5{9jK8QETWAImaNlhphMZhZ5$o%n;sKr3sp^8 zYoxt4dO~1~Mhn?DW<=%fID{uWH7`-@e0Oo&v`;53GcJZhQD-~;xs^o|b7v%5hHo_aojWS7h|kx@qAl|E zo^a;H1m$PM`(iQ`gSt4-?dFLM%J7ZxU?6nGcI zy&0#A(>WpH1+h*e5xJl!xT4;o;bJ#nIO@_o2dS{>jWIyo4YKDG*w*nO2r$xqJ7Lj%GE zUIs_z1)Q7_?VGmY?`gFEmuS}uBj3jtbRlvN;8uD;uZfe2XLHIzc@o*om}mYPd{Vb< z`TWN+c|o3*cvW1Olb3_f0SqOXw{ku?3w|(lPv?{2c;~dZIR~Wz)@fYbYa3@O%`H7| zB@A^Lii9R6Jp~>MC=^l~FE#P{bsB8EcH91>haOpFrUjXawz`5TWzm@8@#$(OIHf&S z$4&CE#q>XB-DVUaveRNnVnf|KxAl8|MSir@YsTnhI*j;qs7fgrF=N1rJ?`e$08&GDrR~%xVgD;rx%Xm2*bTf#2){IY)f?`_ZGEr^+cSd)V!G)SWvU&l;6F=ZOd2 z*|-U)w{)}pF3>+XMndz~!iI+M$$$$mHf9MXU=bJbyyo`(I}bdvsY#zp^27noqH$&H zvc;HOHtH73Y0`eY+sdaVhRdfcE|)`hZmH+4S*IuO5Fah#UsBQ``C%KcN^$RL54{G( zb>l3mlZKCu;y%7VgU6YT@@$o8&McfIPI6C5(Y_7ot@!{lQ()g;cwzHI?dP2DZO6-3 zOK`hgT+2xnXLA7Jv|BiK&r8I%pY5A)c{4{`&PJz3o9ASQi_=pl!z+yq9Pivi7_M#X zWnaU2kq^S1!`b&lHDK0ZEdaJdhu5f9oGg*p(-w0B)!Ddn0b99%omx}xw^wiz>e^3FN!RCm=F)8q6cEJ;oAaOw+LS*LTq|UOECE{e+ z`%|Cj)BCC6;Z~VBWr;rjkpr@ye_xJzqPLavD67e%j2~EG`X>=92(m{z!;}0z`r|As zJ1d_l1FZ5sC@JexlA5=s4DDANo|BrF6Q1Z?(!-{?ba1wGyF=whnr?v@=d=r;^`=Do z2104VRP&1GZ{Lry`jI`GoCyF&u}#3djVVE^K4Wl$2jn6GRQ^+(-!QR4TraoG2JC^0 z9AXT<1a&_@>291k0U;?0w?Gy^UrP6!fT%9t9=6VIfO8x(0STjr_Zq%%rCXl_=K(Ly zMK}5VpPrCb1Kga${hXib9aa!t(8nq-EYB{yz%JaaPw#@fvQbaXoho||m=o6fu|E!X z_L+)Awc^54R`0sWUVSFZr-mnv4imKw;fdjLxbtt^tO`Z-tQUWiw-6~!M~C$ZA0D3A z+b^#y&&@7z@{0pxqH}mzpE)mfzr`%40$NPUMyGe~KpBLp!IFUTi5V;xy86t(Iy)8YY&fb>kiLcB^cAICJU#m9Vwlkph z^D7XAfH0as-)LwYDGRh?^y%OpZTA}iHeenKQ}~$2nj)%um-);sn`Ji_vuk-%h4D{e zPdqnw;qZk8@Wsy^H@N@8xnX7VX3Q;HIXP@$SkY7SIA6I^PMz|^;NHX6Ob!=cK2hdY zwgGc*1Ny+i+#dm+1$y4 z`}wR~m^;@80K?oyE;Qa_7gtW3we-BprA&zl%*co7oWB6U2l1+ojQ3 z6cubta1?OyHp3-F)HQ(twGhitKrYD&s9Yp*{As9oD_KEw(^A*!G=XpP}LAZ`8u!d-7>cs=@a?T(X zVKAGn@$pV$>3O7NeQ*Mg$>hcQX85YiW?ZHmkk_~;y7w5{e-e6d1M-+N^+X}5#SAk{ zoZ5n-S1cy7T<-1b<%_KoOaxx;{xC)0)`>&)G#@%ww2-y(D0;|V0tfX_HT;(0G6?HS z{1VveVVi^#y!}z$ul$CY}o+DLi)H>E2@35FyF!+!UGz`MJ3!7tq*n)g~ zl`#5v`FbzMEgYxRTUQw8+7+F3blxskEG3{+czdxCa500|4z`hzY0*AuncfObfx=wQ zN$LoDAt%%kHSn=d@az=_U&t(ZViXM4DjZ?S)xomy*haydZd{Jm@2aZ9LaM;|hte=a zuv?~KZ-qHbZhyXT`1Ey8!PRFWCt#h4KNM_D7V=4YM<^o`;Z6htAU2uhLDBAtVpfsH zYH*7;$sc(H{a`PUh5DhE&uj-nOu#1Q=Q1`+o?3?E_tZedl8s(4aUAw1?Vo9V?W#J= zrTtSBXy84a<^yxMcDyidux)rNcG4!pG|69`iLcxhO9>)n?p~}iQW=4L7}*xcxXWUQ zG<29}mJY0-44 zDbl;59SSyuR@s#RrKaG=k`VY0JqC>ZwR;(8Wo zeP=5>jMMg|#AvV>z#*YG#+!yG5tEh0O+aQLMPHqX?<*dn785nX?RjS)cC+ZzC{&~Q|zU% zA?v9}RdB5LPQ*l?iruP2UOnRw0^F{xq)OCe;x6c(hAomB(K2bOn7;ZE42A&*#(@ab zCW;Cn!_f6GEKGjLh*B()MZp^#qdd8t0^z5;a^6(8W>?ndT57k)0ug_GFCy^LfV*QocL2)@aqN>db_;3lxS?cftYytJjFfGIK>6l6SF#?+P5cWrmndj8|t8 zcz2}3B%5F$oFK?oTnyv0{wk>GF_a>d>v0JLhRD=`vMd~=vu;zFpK{o7&kWUuFV=MM z&1ZwmX2Ua0*>}u1XJCbj{Nv4sFB-qE)oWVbCjs=FDp7f>T*_>2Q# z3ECjf+76uC2b!e8J7Ok`^Mp}qK#Hoc4d_c5} zn?%$+7M4sEMZjL_P%#yTS@4dT33ElvnwSERLRkzWT`m1C&M6##%w&J|!8zb=1XIeR z_-6r5+IRWEa->zG-Y5m$2{SRj7qt2QC}_wq(c11oLM?`CDMC1tbfIme|!~<}5VybF&h%9h7f^5-f3Rc7P z)i(RGHtpR7yCT1hBf_f@tf(C+x9<1X{baHlVKh%fA|Wrcn+q9xH~zUg%~yo0hapgKSz^saRbr zWV8zUt&_EMS~khmrPJz`8nq)c&>ttCZnDR^No3KoMYetxtyc&6sO5oKJMaLPyA5*2 zY_Oa;%bIMuoGWyhvlb<(*hTUk%K}5x&yVcmVli))l*prx@@AFcaji~`R`2kZr^tfc ze{U&YmXt(FfhOe3D)WLH=8h26qO3)!fy)A?TFR0oCDPjG=05wC;?{YJo4Nk9_TI03 z{cB&-{1E5CFJzqPG6nF4Fy$U{W64vbD-6INb)p>NI>`=V|LFue zg#4!y=Me6no;Zh5|8z1P%ls4N{^BU&pO}>M7tSHPKb<%-(|4Wrr;|4mTbr)4{_YZ& zf=8=y+->1%|Ali1@ekhEMgTRAyAJb_xZk_oA;AE=Eo=_YJtCvE*s)!#GbN{8;OTL+ zITg3Fw>zYvfushNKVbq#BT$7%AtG*UwpB=uTnf=xTg8<$8K(T;VyLz0L{+EDQdJt`WhA&QWCtuseWJI_`c@0OQI#e z7Lak@caDg53T@I=Xq`KUHg%eq)`(iDak_KZlbjT$@+3;rN$vOc)X&2FHBEswx_*!R*vibfaK;dNpHCJl`-LzIH z+we#l;EG)UqEQE4M0_b6t^QYNva6_2=Vz?@Nq!^Uyt0H${}`l-6(k}ZhVOG4oWJ&B#= zn$dc)8lL_CZQ58XYB9&~Go5B_D0zcJm>R;wl8*_0NXg;`tJ;U7Fh*^u6(U}XkIR#K$F3$?wj|-fBJj^r$D@cJAKjg-O zWeBX`VR)qR$iWB9&1?kc-b3t&mPvRYfx?6-)CvK)5Ol*Id98qCbp!&9MvVseEC$$s zQf3A- zU*BbZ$!Dqtf>BwzZb5&KFfat)>NWFfEu%p`ji^o?^KA4; zo;MjhYtRS--XulU5drVAG-OyT+Ta+N+EJnA+nfUkXjdVuoztWJH3`yn3F{jyllj_E z7-Rep*I~;(jPc%5KY$2(_glAQF6;D^M_7N)Jim2eb_u)omQeYTE-~4t2{e!to~w^eV@@m5cklUNQD{v1 z>g6lo)36OP zFr)}-0ROBKH{G8$u?XIr*zKv(gqL?Y^#R(jpzrWv{2%P>>OOet{NMuTk-;Av3Z8!m zAV23(Q-ePMKCwU(L$t&v`_A99Jmrqi$YTybz0p9?*nUlDNiB;_N0|aoO6(dSc^2&k@_0DgmXJ3$h*rqguzDP|LvR^!eFx*y`_kNZTLXx0>>2N>d`!rltxKH9yaPC0v zOZ|vDxhFF;awc5QG}K|YYr8AXvPaG11TC zkBC5t1^^BiIB@0VvaHb5n9*UI<3D6-c5Q7jftTR2bN~?U8$s>^&LJ4Ls}P6LvPLd! zHq#PYXI&e<+s4fYj0^7l0Yr&^4M& z_l!gC(LTk100yxx-p(?B9G~{SFQ{?BF6dlzJHAF^SAiIKtgWkZ^5T)+?1PZ#G?hNo zGBhwIGzH`H0RPS;Y|g!Ef)(P2uWJCgZv~+bIY*G+NX2%b0jdx6M(>1A2GzgCVZr(EIK0yFI`WJ)m=&DEs>k-69 zFBgCw{pAYpp9O9HZ6C4jKE6hSSl-b}VNWnw70nr%5cUBOW#DP)0Wkv$LsDbF!(>hc z%-DR*gx7^JbO?7&%K_`)96*b^3MDa$HjxsW%{2&4CU#Q|+aRcoOOJ7O16~j@rv5=* z#NCYcO4B3)tm^Nh^7W5aW)%Aa>*?zy2l&n>QsxFqB%1+Vq$(0zE??liA`waUi=fkY zwh`~BIE^HO`f1k!K4w7v4A1%Ls%V8iPxB2$XlhD$kAvB9<2GM60pTIE>-Ls~Pa;DH z5>a#pR)>jzJqoe)7|s!lIjjpYiZ*i=k<&}4UgRuxZDvySZCsFdhB5+3Hy_|1++CCW z(;~crWJ!_bz(DvW0^3;-p=5y&@N)Nwico?xnGq4KiAYkqf|UqtUUZ~yP_$Ci=X;xg zc^t>0W^p_T6TLOZGi*!xB2{#n0gf{`G&+W8oO9#)XPXXZ3-{KONGW1B>W)Z@;wUas5l@V*wz^wK5DF&;?U6~}_+8eE~{Ae<0@94C^ z`g9x9v^m^kz_`W1g(}Ll^awl}f*Zj0_`Fq2>w$y=ri-f<&Q97M@gs?v`H=hXAn{ru z8&4=CP+2i%AFSk6ivw|iF-{bi)Uij0h81%Z5;TTNw_#DZ^Her@`r5& zWDbc_oci!YPBth#0)$R*Izj7<2=W5C6R1>IFEI2#z|B|_;qJ=%Cb@gNy7h5)m5U3u zMl*Oo8hh=!?9s+(+ku3z__;fSRjeN6G*A<>ct!Z$gqdu=1k-I1=+?I5`v@&b&KwW8 z{~=4S6%~T)NWS__xCgW@9P{${_nD@I;Wm>jY3oR8U*n{(ytE&qk{29-q@>=$BnCE+ z4gszm#DeQq?p&mpy2aFNJqS!1js*br1k&^V7qOTy0DEdlpM)E|2^J#ET=zHNZTkH% zQ=6L~i?24Y_kj()Rr)6!Lfk}|Ti}w?!NM=%4z1CcJ4h2ny9?v39j%|`6e0VMIr3`O zlugJ<2G7GGBSud#HFppR-3A^YWlx|45%)O9q5E1PuffbkfZny8k$FK-%sLIYMJRRF z0lZ7<3an?U7(gV!lYNLAK?_Em{wKU8B_(b7i&OVMjg6f&dBYb2d&MRXoX2jM^hH1^ z8#`&<JbfTsI|rtvO}(V zd+-!X9M*-?lH*Dfn3{mW(^r{V@>)1)!k}okyvsQb3D^p=T!Zoa&B3_}TpeuYW}Xpj zgUFy^c9!e&g|7%@Pj(f8NxgBE8wSiMb3gWA5%1SQ!%asJ_uv3?W<|e%-rJJ-;YYSK z@I|wkN}M$AWkEA-K*3hJQGh&JlbnkS9v3~3q_}H9P}P1+9?Y71NV0~b3<9FIun$EG zXrq>(NF%Q`Y0~M!+fzo57!tm8#DN@R7%^%-7dNq2_c847QKlw%5X7yZ48_BU|8Ne5 z4z|L0-k>*)d$Ye8{D;?BWHGiC%y~jE<=X@v7TZzPRmdg@y2@>2S^_4%!nBlru_S+> z=Yn~q1CyP4^vvJt5f`4DpPWlzQjgMY`N>%`>mQjc*@(eikK!tB=JPnb3pG|Bb!Z*##%T_phNv* zvP3&*^=Q)gtlumDXocS5~*+Dp{cPcpZ* zV(0OYxp!dbcs$Int?mS9lU$KVV@dMw_kz+)*O-~q9`yf1=4YfPmaOx8{JDt>*wV1>V?Cbg<{A6Sv&?sEk?4=92;0PN z=(YB#{$WgIY5qj<++5qCGzjO>B!a}6fPtHNPYQi-CQ?!vgBi^^feiWW@7Uk;fi1FT ze9^S=xHXfPR29?O8FnX~-JK_Mf;8Kg7^624473vmgV(WoLt5;-glR~r$efv;^ZJlK z{BF4YAWmxD4Q|fZ-#=CgMm0#V3*uip2D@6VLElg}R-+piPcy;!ik?}b=TSAKYI#_M z?tow1Th4>~IydniN&4QHK@Y16dbK>c+Q{bk7772OO) z7rKk+DgeO`P6^o6#QW6gyX7@F1pi5WbI~Ra?zg-K6%M6&nzl;Ybm|_h)2OjmXbZQP zsc7PzKlKZcvnac`XiCwF#kksvX0mY$He&&NhdFH*1eCTFH8wGCNk6hrI0ETu+8nKa zEA@hHEKc%5ZsAf4T_$=q@q$cSNbRo29kyJEQ?ULMe3ZIE0X>1X)Z2itJ;y6k(Pf-xlZ8f6 zqot!p;K0KUnZo8wFFJ^A3rNWU$byP2I9Q@l-c9n$sIdtDq$sHdM3hPcYtNt@8ELvi z^lajV7`BO8DIN2JE7^Yb=NV{xS(-n@(0^^vd2-7 z@&en(^lak4WvDZ?LORwFt&pUDeglnKrFpT(P>5VZZDx8l@n101k+hbMIVG*F(m($V zP1(G7zL=q6q^fk_ zbOUFW{`nnhy&(;CWl@M0+t#R_EY`hL%1{^5&X&;ga#I3m9faMoh2Bcx>le+VJ&$64 zKpz{bKuSr4=+ozTRQ&MC_~|gfk9+N@p6r(IA?c1SkXEX)#+2-NoR97|#srWyQuG4) z94Km$0otfl#5#(Vl&Cf0dYJ#heo2S*JjWv+ zjG+-!uO)hg@%wi=Y(c70we?!4=Qlj^!WNHP zlLyQgt_=FKN~xSa+1u~UprGJ!vs50TAzssh{Dx1T;v5tAOw61KQ^p5P8PhK&LKZn| zkb3Tz*^{4(ThQ~pq$v?$Ztu>IoTXeGu6XBfKF`H3di!tw>e(Y_Mkw7D&W()tYvfzA z7tR`yI#a%E_K`8s@oX^^i4%Ym)<3 zCC?7dSvGRb@=*n==PplPk*=Qn;p(9OTRB^^Xym4CK1Cb+iq<5qPAJHJZ)M)Q8&<}y z&5hoi9aES$BX_m>lkAzx^Yhh3tLARppxVBB+O};aA5L1gZcORwu<<*KXMda@v~x4F zM)fa^de8cpj|)@&v0=`R4eynG^#1O`e}1U`=l*y07XE!#e)z%t z%og>*?a>hj%NFk6^-)>w_FZWomwa-hIPp-)(!FKLi@w@&emNyGftNL{nU)f{LFIVKB*}13k#%oo}>QAk2JC)LM@}s)b|Jc%au(0W? z=JVS(wfw8)8_l+s%Ddlu*#7Ur8?`(B^X-mHji2AHUvr~w@9p}cdri9^HW%N&y5;`0 Qk_Xog|9pMF6GwmlA9Q}MJ^%m! literal 75371 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

#rAYmu6FX4 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 diff --git a/injected_code.c b/injected_code.c index c1c01980..8b1764c9 100644 --- a/injected_code.c +++ b/injected_code.c @@ -27086,14 +27086,30 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren int W = 7; int NW = 8; - bool canal_or_water_n = tile_has_district_at (tile_x, tile_y - 2, CANAL_DISTRICT_ID) || tile_is_water (tile_x, tile_y - 2); - bool canal_or_water_s = tile_has_district_at (tile_x, tile_y + 2, CANAL_DISTRICT_ID) || tile_is_water (tile_x, tile_y + 2); - bool canal_or_water_w = tile_has_district_at (tile_x - 2, tile_y, CANAL_DISTRICT_ID) || tile_is_water (tile_x - 2, tile_y); - bool canal_or_water_e = tile_has_district_at (tile_x + 2, tile_y, CANAL_DISTRICT_ID) || tile_is_water (tile_x + 2, tile_y); - bool canal_or_water_ne = tile_has_district_at (tile_x + 1, tile_y - 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x + 1, tile_y - 1); - bool canal_or_water_nw = tile_has_district_at (tile_x - 1, tile_y - 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x - 1, tile_y - 1); - bool canal_or_water_se = tile_has_district_at (tile_x + 1, tile_y + 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x + 1, tile_y + 1); - bool canal_or_water_sw = tile_has_district_at (tile_x - 1, tile_y + 1, CANAL_DISTRICT_ID) || tile_is_water (tile_x - 1, tile_y + 1); + bool canal_at_n = tile_has_district_at (tile_x, tile_y - 2, CANAL_DISTRICT_ID); + bool canal_at_s = tile_has_district_at (tile_x, tile_y + 2, CANAL_DISTRICT_ID); + bool canal_at_w = tile_has_district_at (tile_x - 2, tile_y, CANAL_DISTRICT_ID); + bool canal_at_e = tile_has_district_at (tile_x + 2, tile_y, CANAL_DISTRICT_ID); + bool canal_at_ne = tile_has_district_at (tile_x + 1, tile_y - 1, CANAL_DISTRICT_ID); + bool canal_at_nw = tile_has_district_at (tile_x - 1, tile_y - 1, CANAL_DISTRICT_ID); + bool canal_at_se = tile_has_district_at (tile_x + 1, tile_y + 1, CANAL_DISTRICT_ID); + bool canal_at_sw = tile_has_district_at (tile_x - 1, tile_y + 1, CANAL_DISTRICT_ID); + bool water_n = tile_is_water (tile_x, tile_y - 2); + bool water_s = tile_is_water (tile_x, tile_y + 2); + bool water_w = tile_is_water (tile_x - 2, tile_y); + bool water_e = tile_is_water (tile_x + 2, tile_y); + bool water_ne = tile_is_water (tile_x + 1, tile_y - 1); + bool water_nw = tile_is_water (tile_x - 1, tile_y - 1); + bool water_se = tile_is_water (tile_x + 1, tile_y + 1); + bool water_sw = tile_is_water (tile_x - 1, tile_y + 1); + bool canal_or_water_n = canal_at_n || water_n; + bool canal_or_water_s = canal_at_s || water_s; + bool canal_or_water_w = canal_at_w || water_w; + bool canal_or_water_e = canal_at_e || water_e; + bool canal_or_water_ne = canal_at_ne || water_ne; + bool canal_or_water_nw = canal_at_nw || water_nw; + bool canal_or_water_se = canal_at_se || water_se; + bool canal_or_water_sw = canal_at_sw || water_sw; char ss[200]; snprintf (ss, sizeof(ss), "Canal at (%d,%d) dirs N:%d NE:%d E:%d SE:%d S:%d SW:%d W:%d NW:%d\n", @@ -27112,7 +27128,11 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren Sprite * canal_w = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][W]; Sprite * canal_nw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][NW]; - bool available_dirs[9] = { + bool canal_dirs[9] = { + false, canal_at_n, canal_at_ne, canal_at_e, canal_at_se, + canal_at_s, canal_at_sw, canal_at_w, canal_at_nw + }; + bool available_dirs[9] = { false, canal_or_water_n, canal_or_water_ne, canal_or_water_e, canal_or_water_se, canal_or_water_s, canal_or_water_sw, canal_or_water_w, canal_or_water_nw }; @@ -27125,34 +27145,137 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren int dir1 = -1; int dir2 = -1; - // Prefer straight lines. - if (available_dirs[N] && available_dirs[S]) { dir1 = N; dir2 = S; } - else if (available_dirs[E] && available_dirs[W]) { dir1 = E; dir2 = W; } - else if (available_dirs[NE] && available_dirs[SW]) { dir1 = NE; dir2 = SW; } - else if (available_dirs[NW] && available_dirs[SE]) { dir1 = NW; dir2 = SE; } - else { - for (int i = N; i <= NW && dir1 == -1; i++) { - if (! available_dirs[i]) - continue; - int i_prev = (i == N) ? NW : (i - 1); - int i_next = (i == NW) ? N : (i + 1); - for (int j = N; j <= NW; j++) { - if (! available_dirs[j] || j == i) - continue; - if (j == i_next || j == i_prev) + bool has_canal_dir = canal_dirs[N] || canal_dirs[NE] || canal_dirs[E] || canal_dirs[SE] || + canal_dirs[S] || canal_dirs[SW] || canal_dirs[W] || canal_dirs[NW]; + if (has_canal_dir) { + // Prefer diagonals using only canal neighbors first. + if (canal_dirs[NE] && canal_dirs[SW]) { dir1 = NE; dir2 = SW; } + else if (canal_dirs[NW] && canal_dirs[SE]) { dir1 = NW; dir2 = SE; } + else if (canal_dirs[N] && canal_dirs[S]) { dir1 = N; dir2 = S; } + else if (canal_dirs[E] && canal_dirs[W]) { dir1 = E; dir2 = W; } + else { + for (int i = N; i <= NW && dir1 == -1; i++) { + if (! canal_dirs[i]) continue; - dir1 = i; - dir2 = j; - break; + int i_prev = (i == N) ? NW : (i - 1); + int i_next = (i == NW) ? N : (i + 1); + for (int j = N; j <= NW; j++) { + if (! canal_dirs[j] || j == i) + continue; + if (j == i_next || j == i_prev) + continue; + dir1 = i; + dir2 = j; + break; + } + } + if (dir1 == -1) { + for (int i = N; i <= NW; i++) { + if (canal_dirs[i]) { + dir1 = i; + break; + } + } } } if (dir1 == -1) { - for (int i = N; i <= NW; i++) { - if (available_dirs[i]) { + // No canal direction found; fall back to water/land adjacency. + if (available_dirs[NE] && available_dirs[SW]) { dir1 = NE; dir2 = SW; } + else if (available_dirs[NW] && available_dirs[SE]) { dir1 = NW; dir2 = SE; } + else if (available_dirs[N] && available_dirs[S]) { dir1 = N; dir2 = S; } + else if (available_dirs[E] && available_dirs[W]) { dir1 = E; dir2 = W; } + else { + for (int i = N; i <= NW && dir1 == -1; i++) { + if (! available_dirs[i]) + continue; + int i_prev = (i == N) ? NW : (i - 1); + int i_next = (i == NW) ? N : (i + 1); + for (int j = N; j <= NW; j++) { + if (! available_dirs[j] || j == i) + continue; + if (j == i_next || j == i_prev) + continue; + dir1 = i; + dir2 = j; + break; + } + } + if (dir1 == -1) { + for (int i = N; i <= NW; i++) { + if (available_dirs[i]) { + dir1 = i; + break; + } + } + } + } + } else if (dir2 == -1) { + int canal_dir = dir1; + int a1 = -1; + int a2 = -1; + if (available_dirs[NE] && available_dirs[SW]) { a1 = NE; a2 = SW; } + else if (available_dirs[NW] && available_dirs[SE]) { a1 = NW; a2 = SE; } + else if (available_dirs[N] && available_dirs[S]) { a1 = N; a2 = S; } + else if (available_dirs[E] && available_dirs[W]) { a1 = E; a2 = W; } + else { + for (int i = N; i <= NW && a1 == -1; i++) { + if (! available_dirs[i]) + continue; + int i_prev = (i == N) ? NW : (i - 1); + int i_next = (i == NW) ? N : (i + 1); + for (int j = N; j <= NW; j++) { + if (! available_dirs[j] || j == i) + continue; + if (j == i_next || j == i_prev) + continue; + a1 = i; + a2 = j; + break; + } + } + if (a1 == -1) { + for (int i = N; i <= NW; i++) { + if (available_dirs[i]) { + a1 = i; + break; + } + } + } + } + if (a1 == canal_dir) { dir1 = a1; dir2 = a2; } + else if (a2 == canal_dir) { dir1 = a2; dir2 = a1; } + else { dir1 = canal_dir; dir2 = a1; } + } + } else { + // Prefer diagonals. + if (available_dirs[NE] && available_dirs[SW]) { dir1 = NE; dir2 = SW; } + else if (available_dirs[NW] && available_dirs[SE]) { dir1 = NW; dir2 = SE; } + else if (available_dirs[N] && available_dirs[S]) { dir1 = N; dir2 = S; } + else if (available_dirs[E] && available_dirs[W]) { dir1 = E; dir2 = W; } + else { + for (int i = N; i <= NW && dir1 == -1; i++) { + if (! available_dirs[i]) + continue; + int i_prev = (i == N) ? NW : (i - 1); + int i_next = (i == NW) ? N : (i + 1); + for (int j = N; j <= NW; j++) { + if (! available_dirs[j] || j == i) + continue; + if (j == i_next || j == i_prev) + continue; dir1 = i; + dir2 = j; break; } } + if (dir1 == -1) { + for (int i = N; i <= NW; i++) { + if (available_dirs[i]) { + dir1 = i; + break; + } + } + } } } @@ -27173,13 +27296,26 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren snprintf (ss, sizeof(ss), "Drawing canal at (%d,%d) dirs:%d,%d centroid:%d\n", tile_x, tile_y, dir1, dir2, draw_centroid); (*p_OutputDebugStringA)(ss); + int draw_dir1 = dir1; + int draw_dir2 = dir2; + if ((draw_dir1 >= 0) && (draw_dir2 >= 0)) { + bool dir1_diagonal = (draw_dir1 == NE) || (draw_dir1 == SE) || (draw_dir1 == SW) || (draw_dir1 == NW); + bool dir2_diagonal = (draw_dir2 == NE) || (draw_dir2 == SE) || (draw_dir2 == SW) || (draw_dir2 == NW); + if (dir1_diagonal != dir2_diagonal) { + if (dir1_diagonal) { + int tmp = draw_dir1; + draw_dir1 = draw_dir2; + draw_dir2 = tmp; + } + } + } + + if (draw_dir1 >= 0) + draw_district_on_map_or_canvas(dir_sprites[draw_dir1], map_renderer, draw_x, draw_y); + if (draw_dir2 >= 0) + draw_district_on_map_or_canvas(dir_sprites[draw_dir2], map_renderer, draw_x, draw_y); if (draw_centroid) draw_district_on_map_or_canvas(canal_centroid, map_renderer, draw_x, draw_y); - - if (dir1 >= 0) - draw_district_on_map_or_canvas(dir_sprites[dir1], map_renderer, draw_x, draw_y); - if (dir2 >= 0) - draw_district_on_map_or_canvas(dir_sprites[dir2], map_renderer, draw_x, draw_y); } void __fastcall From bb682f915e5394d467fa735b90c1f75884e17890 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 14 Jan 2026 10:55:23 -0800 Subject: [PATCH 180/356] Canal art updates --- Art/Districts/1200/Canal.PCX | Bin 76573 -> 78778 bytes injected_code.c | 33 ++++++++++++++++++++------------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Art/Districts/1200/Canal.PCX b/Art/Districts/1200/Canal.PCX index 5e8a60fbaced776b2eca72c9b1f30fdd9d3f1447..2950a7a46d88c65c2cf21ef4fc8fa2cbcccd9dc0 100644 GIT binary patch delta 5847 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 delta 4141 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 0) && (draw_dir2 >= 0)) { - bool dir1_diagonal = (draw_dir1 == NE) || (draw_dir1 == SE) || (draw_dir1 == SW) || (draw_dir1 == NW); - bool dir2_diagonal = (draw_dir2 == NE) || (draw_dir2 == SE) || (draw_dir2 == SW) || (draw_dir2 == NW); - if (dir1_diagonal != dir2_diagonal) { - if (dir1_diagonal) { - int tmp = draw_dir1; - draw_dir1 = draw_dir2; - draw_dir2 = tmp; - } + int weight1 = 0; + int weight2 = 0; + + if (draw_dir1 == S) weight1 = 2; + else if ((draw_dir1 == SE) || (draw_dir1 == SW)) weight1 = 1; + else if ((draw_dir1 == NE) || (draw_dir1 == NW) || (draw_dir1 == N)) weight1 = -1; + + if (draw_dir2 == S) weight2 = 2; + else if ((draw_dir2 == SE) || (draw_dir2 == SW)) weight2 = 1; + else if ((draw_dir2 == NE) || (draw_dir2 == NW) || (draw_dir2 == N)) weight2 = -1; + + if (weight1 > weight2) { + int tmp = draw_dir1; + draw_dir1 = draw_dir2; + draw_dir2 = tmp; } } + snprintf (ss, sizeof(ss), "Drawing canal at (%d,%d) dirs:%d,%d centroid:%d\n", tile_x, tile_y, draw_dir1, draw_dir2, draw_centroid); + (*p_OutputDebugStringA)(ss); + if (draw_dir1 >= 0) draw_district_on_map_or_canvas(dir_sprites[draw_dir1], map_renderer, draw_x, draw_y); if (draw_dir2 >= 0) draw_district_on_map_or_canvas(dir_sprites[draw_dir2], map_renderer, draw_x, draw_y); - if (draw_centroid) - draw_district_on_map_or_canvas(canal_centroid, map_renderer, draw_x, draw_y); + //if (draw_centroid) + // draw_district_on_map_or_canvas(canal_centroid, map_renderer, draw_x, draw_y); } void __fastcall From 6d6dbab13b3358b19a9921657c47f691b633eae5 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 14 Jan 2026 13:32:14 -0800 Subject: [PATCH 181/356] Better rendering for Canal --- Art/Districts/1200/Canal.PCX | Bin 78778 -> 78558 bytes injected_code.c | 165 +++++++++++++++++++++++++++++++---- 2 files changed, 146 insertions(+), 19 deletions(-) diff --git a/Art/Districts/1200/Canal.PCX b/Art/Districts/1200/Canal.PCX index 2950a7a46d88c65c2cf21ef4fc8fa2cbcccd9dc0..b9a06964169bd469f0c37282271f99e9a46b50a7 100644 GIT binary patch delta 4058 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 bh+BAG=$(fytPV4=zV?uI#7KYfz`%b2_Iv9% delta 4048 zcmcgveNa@_73W1wG%=YV5!4k_+))#Y8X}D~errui{EDeojm9K1ZA49>lQH`ef^#H&`C3G{>&fu zy?f6+_nhDPxaXZ0zS{rTzxyBAlQ_h!mXYU>GmBIQ^_+Mu zEaxHelqvFs#8Hxg)cCM^>EUTLo&p7BeV|lWe zyk!Lxh$ng4uncJwPDa#_cCs`71O>^v%L2wux=(lL{w48NZt}1zs=fl%YuSa};E8IIFSL^4ok$|!5#|iw325V#Xc?AOs5|0h|4{R^6HcnX|CBcc2$XUQM$R%xi2p>*PO7~RFb=+er*;d6H-w^*n$T+)K^K zFWqH+|I1Aa5GCmL9;LJ9ebj8Hwmr6-jYp}|;-p%;VlK5)GX;+3=|u7C+llhJ(IchN z)J_rdQ&fpy7Ntha8x=n_;jQ2q5n{}ABUqxx=n6IdA?~m-*EUdEL@s5Yg?bUp8gkKX zdeAKBvm`MnH36{R{bt0<*DC0u62_Ts>YxVlk#h1Fg&~xhOSTlFo^zjiWhzjA_ue-e~+l!`=j`O%PMBL>VxPgP04L zbzv~23(uyB>jv+8zhoL{Q^FLI{@#8-R zk_e+yqHgF1?kkKL?{2!{0+R9xKLL`<6B1^y0GA(*Y@z#lV02|jnVwuMHe+N5y!qJTTgt@IA3SyotZURDYPN!%9nR?% z)!he51RNoDGtv(!!4yrb<_Y);bf$erp=_DBO`53L%8X5L^Ff9+*p4Vfhv5>;U2L?TJQ1kl$oYYb z>HkN4H`L$2IIpgr)9-~`%eE}LRZg4qThu_-E^L#>CInRvAXX4TAkrL@BGEQ^*}EYX z2530+B;>52b_9-%HL+dd(R7$xbGSk-$4yl@d>L58P{*j)rgspB;SY{(Iank6h zfCD@ThUR15=NJMt5|FPaqh$2(K4yq6(_LardV>7&D`sgd4 zMln!+F*9Ybf&|8%mhs~F%(O8VsZKZ6$C-+o$G03gj60=o$OC8n4v~F;W_=ZqiFi{sa2 z$DIAr6CatyFXkC%H6x|ERk}ys9rNtyXFOk&11WL^UT0%jEGuZz-uSBG9%xsNd?!V@ zsGRo~h+aE?e7`-%6?y0U(bDj#=zf~1waS5Zp-E^XcyL=JEWE>J2@ieLWpkQCbc zAYK~%rrnCpEvN)wV`mu!ZPe;Sqjp!O8aoA)m~u<;*p*spah6zRU^+)#NVlr! z8pUJ^l&vx06rEvcflYA~J3uW8Izk6P-?X87Y*E;zHtzeNr421vFWR(DSlkJgTNnYl zWp|Lbl;ecuJ&dzpivgNfCa=TgDJ8F+uG`T|J(>-Q6F1*TK-=}t*GG&iudIe?YWWP2&Kn6eh)NuHya7H022Xj&`<*^J1uH4dS=>+&4r zP}W%OY>A6IXHOOP@^sySuwGNx83E-ygm4VmLHxW)o?@A;x7)y43kuxGu4sYBTa<6j z9a7xXjr%t~zj4X_McGqL&pPs1ZL?ZG^WjzX%Xg-%+_b)!MefWzmkhDu5ATEnqKau}2IyAqbIKpd>eF^{ZngwynoZg)wrnnPD`tsQLueid7 zHN%KMK)?tV%-X@+jYIsAYN(WFzWt13r~m>8Ke{o>C2Br#xJt*Bf$M1district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][CENTROID]; Sprite * canal_n = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][N]; Sprite * canal_ne = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][NE]; Sprite * canal_e = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][E]; @@ -27132,10 +27164,16 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren false, canal_at_n, canal_at_ne, canal_at_e, canal_at_se, canal_at_s, canal_at_sw, canal_at_w, canal_at_nw }; + bool water_dirs[9] = { + false, water_n, water_ne, water_e, water_se, + water_s, water_sw, water_w, water_nw + }; bool available_dirs[9] = { false, canal_or_water_n, canal_or_water_ne, canal_or_water_e, canal_or_water_se, canal_or_water_s, canal_or_water_sw, canal_or_water_w, canal_or_water_nw }; + bool any_canal_diagonal = canal_dirs[NE] || canal_dirs[NW] || canal_dirs[SE] || canal_dirs[SW]; + bool any_available_diagonal = available_dirs[NE] || available_dirs[NW] || available_dirs[SE] || available_dirs[SW]; Sprite * dir_sprites[9] = { NULL, canal_n, canal_ne, canal_e, canal_se, @@ -27151,8 +27189,6 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren // Prefer diagonals using only canal neighbors first. if (canal_dirs[NE] && canal_dirs[SW]) { dir1 = NE; dir2 = SW; } else if (canal_dirs[NW] && canal_dirs[SE]) { dir1 = NW; dir2 = SE; } - else if (canal_dirs[N] && canal_dirs[S]) { dir1 = N; dir2 = S; } - else if (canal_dirs[E] && canal_dirs[W]) { dir1 = E; dir2 = W; } else { for (int i = N; i <= NW && dir1 == -1; i++) { if (! canal_dirs[i]) @@ -27164,11 +27200,32 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren continue; if (j == i_next || j == i_prev) continue; + // If a diagonal is available, skip straight lines to favor angled segments. + if (any_canal_diagonal) { + if ((i == N && j == S) || (i == S && j == N) || + (i == E && j == W) || (i == W && j == E)) + continue; + } + bool pair_is_too_close = false; + for (int k = 0; k < disallowed_pair_count; k++) { + if ((i == disallowed_pairs[k][0] && j == disallowed_pairs[k][1]) || + (i == disallowed_pairs[k][1] && j == disallowed_pairs[k][0])) { + pair_is_too_close = true; + break; + } + } + if (pair_is_too_close) + continue; dir1 = i; dir2 = j; break; } } + if (dir1 == -1) { + // Fall back to straight lines only if no diagonal/mixed pair is viable. + if (canal_dirs[N] && canal_dirs[S]) { dir1 = N; dir2 = S; } + else if (canal_dirs[E] && canal_dirs[W]) { dir1 = E; dir2 = W; } + } if (dir1 == -1) { for (int i = N; i <= NW; i++) { if (canal_dirs[i]) { @@ -27182,8 +27239,6 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren // No canal direction found; fall back to water/land adjacency. if (available_dirs[NE] && available_dirs[SW]) { dir1 = NE; dir2 = SW; } else if (available_dirs[NW] && available_dirs[SE]) { dir1 = NW; dir2 = SE; } - else if (available_dirs[N] && available_dirs[S]) { dir1 = N; dir2 = S; } - else if (available_dirs[E] && available_dirs[W]) { dir1 = E; dir2 = W; } else { for (int i = N; i <= NW && dir1 == -1; i++) { if (! available_dirs[i]) @@ -27195,11 +27250,32 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren continue; if (j == i_next || j == i_prev) continue; + // If a diagonal is available, skip straight lines to favor angled segments. + if (any_available_diagonal) { + if ((i == N && j == S) || (i == S && j == N) || + (i == E && j == W) || (i == W && j == E)) + continue; + } + bool pair_is_too_close = false; + for (int k = 0; k < disallowed_pair_count; k++) { + if ((i == disallowed_pairs[k][0] && j == disallowed_pairs[k][1]) || + (i == disallowed_pairs[k][1] && j == disallowed_pairs[k][0])) { + pair_is_too_close = true; + break; + } + } + if (pair_is_too_close) + continue; dir1 = i; dir2 = j; break; } } + if (dir1 == -1) { + // Fall back to straight lines only if no diagonal/mixed pair is viable. + if (available_dirs[N] && available_dirs[S]) { dir1 = N; dir2 = S; } + else if (available_dirs[E] && available_dirs[W]) { dir1 = E; dir2 = W; } + } if (dir1 == -1) { for (int i = N; i <= NW; i++) { if (available_dirs[i]) { @@ -27215,8 +27291,6 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren int a2 = -1; if (available_dirs[NE] && available_dirs[SW]) { a1 = NE; a2 = SW; } else if (available_dirs[NW] && available_dirs[SE]) { a1 = NW; a2 = SE; } - else if (available_dirs[N] && available_dirs[S]) { a1 = N; a2 = S; } - else if (available_dirs[E] && available_dirs[W]) { a1 = E; a2 = W; } else { for (int i = N; i <= NW && a1 == -1; i++) { if (! available_dirs[i]) @@ -27228,11 +27302,32 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren continue; if (j == i_next || j == i_prev) continue; + // If a diagonal is available, skip straight lines to favor angled segments. + if (any_available_diagonal) { + if ((i == N && j == S) || (i == S && j == N) || + (i == E && j == W) || (i == W && j == E)) + continue; + } + bool pair_is_too_close = false; + for (int k = 0; k < disallowed_pair_count; k++) { + if ((i == disallowed_pairs[k][0] && j == disallowed_pairs[k][1]) || + (i == disallowed_pairs[k][1] && j == disallowed_pairs[k][0])) { + pair_is_too_close = true; + break; + } + } + if (pair_is_too_close) + continue; a1 = i; a2 = j; break; } } + if (a1 == -1) { + // Fall back to straight lines only if no diagonal/mixed pair is viable. + if (available_dirs[N] && available_dirs[S]) { a1 = N; a2 = S; } + else if (available_dirs[E] && available_dirs[W]) { a1 = E; a2 = W; } + } if (a1 == -1) { for (int i = N; i <= NW; i++) { if (available_dirs[i]) { @@ -27250,8 +27345,6 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren // Prefer diagonals. if (available_dirs[NE] && available_dirs[SW]) { dir1 = NE; dir2 = SW; } else if (available_dirs[NW] && available_dirs[SE]) { dir1 = NW; dir2 = SE; } - else if (available_dirs[N] && available_dirs[S]) { dir1 = N; dir2 = S; } - else if (available_dirs[E] && available_dirs[W]) { dir1 = E; dir2 = W; } else { for (int i = N; i <= NW && dir1 == -1; i++) { if (! available_dirs[i]) @@ -27263,11 +27356,32 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren continue; if (j == i_next || j == i_prev) continue; + // If a diagonal is available, skip straight lines to favor angled segments. + if (any_available_diagonal) { + if ((i == N && j == S) || (i == S && j == N) || + (i == E && j == W) || (i == W && j == E)) + continue; + } + bool pair_is_too_close = false; + for (int k = 0; k < disallowed_pair_count; k++) { + if ((i == disallowed_pairs[k][0] && j == disallowed_pairs[k][1]) || + (i == disallowed_pairs[k][1] && j == disallowed_pairs[k][0])) { + pair_is_too_close = true; + break; + } + } + if (pair_is_too_close) + continue; dir1 = i; dir2 = j; break; } } + if (dir1 == -1) { + // Fall back to straight lines only if no diagonal/mixed pair is viable. + if (available_dirs[N] && available_dirs[S]) { dir1 = N; dir2 = S; } + else if (available_dirs[E] && available_dirs[W]) { dir1 = E; dir2 = W; } + } if (dir1 == -1) { for (int i = N; i <= NW; i++) { if (available_dirs[i]) { @@ -27278,11 +27392,26 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren } } } - - bool draw_centroid = true; if ((dir1 >= 0) && (dir2 >= 0)) { - int opposite_dir1 = ((dir1 + 3) > NW) ? (dir1 - 5) : (dir1 + 4); - draw_centroid = (dir2 != opposite_dir1); + bool pair_is_too_close = false; + for (int k = 0; k < disallowed_pair_count; k++) { + if ((dir1 == disallowed_pairs[k][0] && dir2 == disallowed_pairs[k][1]) || + (dir1 == disallowed_pairs[k][1] && dir2 == disallowed_pairs[k][0])) { + pair_is_too_close = true; + break; + } + } + if (pair_is_too_close) { + if (available_dirs[N] && available_dirs[S]) { dir1 = N; dir2 = S; } + else if (available_dirs[E] && available_dirs[W]) { dir1 = E; dir2 = W; } + else { + if (available_dirs[N]) { dir1 = N; } + else if (available_dirs[S]) { dir1 = S; } + else if (available_dirs[E]) { dir1 = E; } + else if (available_dirs[W]) { dir1 = W; } + dir2 = -1; + } + } } struct district_config const * cfg = &is->district_configs[CANAL_DISTRICT_ID]; @@ -27314,15 +27443,13 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren } } - snprintf (ss, sizeof(ss), "Drawing canal at (%d,%d) dirs:%d,%d centroid:%d\n", tile_x, tile_y, draw_dir1, draw_dir2, draw_centroid); + snprintf (ss, sizeof(ss), "Drawing canal at (%d,%d) dirs:%d,%d\n", tile_x, tile_y, draw_dir1, draw_dir2); (*p_OutputDebugStringA)(ss); if (draw_dir1 >= 0) - draw_district_on_map_or_canvas(dir_sprites[draw_dir1], map_renderer, draw_x, draw_y); + draw_canal_on_map_or_canvas(dir_sprites[draw_dir1], draw_dir1, water_dirs, map_renderer, draw_x, draw_y); if (draw_dir2 >= 0) - draw_district_on_map_or_canvas(dir_sprites[draw_dir2], map_renderer, draw_x, draw_y); - //if (draw_centroid) - // draw_district_on_map_or_canvas(canal_centroid, map_renderer, draw_x, draw_y); + draw_canal_on_map_or_canvas(dir_sprites[draw_dir2], draw_dir2, water_dirs, map_renderer, draw_x, draw_y); } void __fastcall From 286d71df676614cb328a1baf6b4bfad4d1090eae Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 14 Jan 2026 15:07:38 -0800 Subject: [PATCH 182/356] Canals finally looking good; Canal art fragement indices aligned with DIR_* enum, much cleaner --- Art/Districts/1200/Canal.PCX | Bin 78558 -> 78816 bytes injected_code.c | 387 +++++++++-------------------------- 2 files changed, 93 insertions(+), 294 deletions(-) diff --git a/Art/Districts/1200/Canal.PCX b/Art/Districts/1200/Canal.PCX index b9a06964169bd469f0c37282271f99e9a46b50a7..e2fba6124f61eacd77ce8d5bf44d7115d5278451 100644 GIT binary patch delta 2788 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 Q>4PPfmphTw>-8o72mX~yFaQ7m diff --git a/injected_code.c b/injected_code.c index e6cf4ddd..259932b6 100644 --- a/injected_code.c +++ b/injected_code.c @@ -27074,67 +27074,50 @@ get_bridge_image_index (Tile * tile, int tile_x, int tile_y) } void -draw_canal_on_map_or_canvas(Sprite * sprite, int dir, bool water_dirs[9], Map_Renderer * map_renderer, int draw_x, int draw_y) +draw_canal_on_map_or_canvas(Sprite * sprite, int tile_x, int tile_y, int dir, bool water_dirs[9], Map_Renderer * map_renderer, int draw_x, int draw_y) { - int N = 1; - int NE = 2; - int E = 3; - int SE = 4; - int S = 5; - int SW = 6; - int W = 7; - int NW = 8; - int y_offset = 9; int x_offset = y_offset * 2; draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y); // Add an additional draw if adjacent to water - if (dir == N && water_dirs[N]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y - y_offset); - else if (dir == NE && water_dirs[NE]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y - y_offset); - else if (dir == E && water_dirs[E]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y); - else if (dir == SE && water_dirs[SE]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y + y_offset); - else if (dir == S && water_dirs[S]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y + y_offset); - else if (dir == SW && water_dirs[SW]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y + y_offset); - else if (dir == W && water_dirs[W]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y); - else if (dir == NW && water_dirs[NW]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y - y_offset); + if (dir == DIR_N && water_dirs[DIR_N]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y - y_offset); + else if (dir == DIR_NE && water_dirs[DIR_NE]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y - y_offset); + else if (dir == DIR_E && water_dirs[DIR_E]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y); + else if (dir == DIR_SE && water_dirs[DIR_SE] && ! (tile_is_water (tile_x - 2, tile_y))) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y + y_offset); + else if (dir == DIR_S && water_dirs[DIR_S]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y + y_offset); + else if (dir == DIR_SW && water_dirs[DIR_SW] && ! (tile_is_water (tile_x + 2, tile_y))) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y + y_offset); + else if (dir == DIR_W && water_dirs[DIR_W]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y); + else if (dir == DIR_NW && water_dirs[DIR_NW]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y - y_offset); } void draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int era) { - int N = 1; - int NE = 2; - int E = 3; - int SE = 4; - int S = 5; - int SW = 6; - int W = 7; - int NW = 8; - // Avoid narrow angles that look cramped; fall back to straight lines instead. + // Avoid acute angles (adjacent directions) that look cramped. int disallowed_pairs[][2] = { - { N, NE }, { N, E }, { N, NW }, { N, W }, - { S, NE }, { S, NW } + { DIR_N, DIR_NE }, { DIR_NE, DIR_E }, { DIR_E, DIR_SE }, { DIR_SE, DIR_S }, + { DIR_S, DIR_SW }, { DIR_SW, DIR_W }, { DIR_W, DIR_NW }, { DIR_NW, DIR_N } }; int disallowed_pair_count = sizeof(disallowed_pairs) / sizeof(disallowed_pairs[0]); - bool canal_at_n = tile_has_district_at (tile_x, tile_y - 2, CANAL_DISTRICT_ID); - bool canal_at_s = tile_has_district_at (tile_x, tile_y + 2, CANAL_DISTRICT_ID); - bool canal_at_w = tile_has_district_at (tile_x - 2, tile_y, CANAL_DISTRICT_ID); - bool canal_at_e = tile_has_district_at (tile_x + 2, tile_y, CANAL_DISTRICT_ID); - bool canal_at_ne = tile_has_district_at (tile_x + 1, tile_y - 1, CANAL_DISTRICT_ID); - bool canal_at_nw = tile_has_district_at (tile_x - 1, tile_y - 1, CANAL_DISTRICT_ID); - bool canal_at_se = tile_has_district_at (tile_x + 1, tile_y + 1, CANAL_DISTRICT_ID); - bool canal_at_sw = tile_has_district_at (tile_x - 1, tile_y + 1, CANAL_DISTRICT_ID); - bool water_n = tile_is_water (tile_x, tile_y - 2); - bool water_s = tile_is_water (tile_x, tile_y + 2); - bool water_w = tile_is_water (tile_x - 2, tile_y); - bool water_e = tile_is_water (tile_x + 2, tile_y); - bool water_ne = tile_is_water (tile_x + 1, tile_y - 1); - bool water_nw = tile_is_water (tile_x - 1, tile_y - 1); - bool water_se = tile_is_water (tile_x + 1, tile_y + 1); - bool water_sw = tile_is_water (tile_x - 1, tile_y + 1); + bool canal_at_n = tile_has_district_at (tile_x, tile_y - 2, CANAL_DISTRICT_ID); + bool canal_at_s = tile_has_district_at (tile_x, tile_y + 2, CANAL_DISTRICT_ID); + bool canal_at_w = tile_has_district_at (tile_x - 2, tile_y, CANAL_DISTRICT_ID); + bool canal_at_e = tile_has_district_at (tile_x + 2, tile_y, CANAL_DISTRICT_ID); + bool canal_at_ne = tile_has_district_at (tile_x + 1, tile_y - 1, CANAL_DISTRICT_ID); + bool canal_at_nw = tile_has_district_at (tile_x - 1, tile_y - 1, CANAL_DISTRICT_ID); + bool canal_at_se = tile_has_district_at (tile_x + 1, tile_y + 1, CANAL_DISTRICT_ID); + bool canal_at_sw = tile_has_district_at (tile_x - 1, tile_y + 1, CANAL_DISTRICT_ID); + bool water_n = tile_is_water (tile_x, tile_y - 2); + bool water_s = tile_is_water (tile_x, tile_y + 2); + bool water_w = tile_is_water (tile_x - 2, tile_y); + bool water_e = tile_is_water (tile_x + 2, tile_y); + bool water_ne = tile_is_water (tile_x + 1, tile_y - 1); + bool water_nw = tile_is_water (tile_x - 1, tile_y - 1); + bool water_se = tile_is_water (tile_x + 1, tile_y + 1); + bool water_sw = tile_is_water (tile_x - 1, tile_y + 1); bool canal_or_water_n = canal_at_n || water_n; bool canal_or_water_s = canal_at_s || water_s; bool canal_or_water_w = canal_at_w || water_w; @@ -27143,275 +27126,86 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren bool canal_or_water_nw = canal_at_nw || water_nw; bool canal_or_water_se = canal_at_se || water_se; bool canal_or_water_sw = canal_at_sw || water_sw; + bool no_other_canals = !canal_at_n && !canal_at_s && !canal_at_w && !canal_at_e && + !canal_at_ne && !canal_at_nw && !canal_at_se && !canal_at_sw; char ss[200]; - snprintf (ss, sizeof(ss), "Canal at (%d,%d) dirs N:%d NE:%d E:%d SE:%d S:%d SW:%d W:%d NW:%d\n", + snprintf (ss, sizeof(ss), "Canal at (%d,%d), canal tiles: N:%d NE:%d E:%d SE:%d S:%d SW:%d W:%d NW:%d; water tiles: N:%d NE:%d E:%d SE:%d S:%d SW:%d W:%d NW:%d\n", tile_x, tile_y, - canal_or_water_n, canal_or_water_ne, canal_or_water_e, canal_or_water_se, - canal_or_water_s, canal_or_water_sw, canal_or_water_w, canal_or_water_nw); + canal_at_n, canal_at_ne, canal_at_e, canal_at_se, + canal_at_s, canal_at_sw, canal_at_w, canal_at_nw, + water_n, water_ne, water_e, water_se, + water_s, water_sw, water_w, water_nw); (*p_OutputDebugStringA)(ss); - Sprite * canal_n = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][N]; - Sprite * canal_ne = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][NE]; - Sprite * canal_e = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][E]; - Sprite * canal_se = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][SE]; - Sprite * canal_s = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][S]; - Sprite * canal_sw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][SW]; - Sprite * canal_w = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][W]; - Sprite * canal_nw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][NW]; + Sprite * canal_n = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_N]; + Sprite * canal_ne = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_NE]; + Sprite * canal_e = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_E]; + Sprite * canal_se = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_SE]; + Sprite * canal_s = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_S]; + Sprite * canal_sw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_SW]; + Sprite * canal_w = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_W]; + Sprite * canal_nw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_NW]; bool canal_dirs[9] = { - false, canal_at_n, canal_at_ne, canal_at_e, canal_at_se, - canal_at_s, canal_at_sw, canal_at_w, canal_at_nw + false, canal_at_ne, canal_at_e, canal_at_se, + canal_at_s, canal_at_sw, canal_at_w, canal_at_nw, canal_at_n }; bool water_dirs[9] = { - false, water_n, water_ne, water_e, water_se, - water_s, water_sw, water_w, water_nw + false, water_ne, water_e, water_se, + water_s, water_sw, water_w, water_nw, water_n }; bool available_dirs[9] = { - false, canal_or_water_n, canal_or_water_ne, canal_or_water_e, canal_or_water_se, - canal_or_water_s, canal_or_water_sw, canal_or_water_w, canal_or_water_nw + false, canal_or_water_ne, canal_or_water_e, canal_or_water_se, + canal_or_water_s, canal_or_water_sw, canal_or_water_w, canal_or_water_nw, canal_or_water_n }; - bool any_canal_diagonal = canal_dirs[NE] || canal_dirs[NW] || canal_dirs[SE] || canal_dirs[SW]; - bool any_available_diagonal = available_dirs[NE] || available_dirs[NW] || available_dirs[SE] || available_dirs[SW]; - Sprite * dir_sprites[9] = { - NULL, canal_n, canal_ne, canal_e, canal_se, - canal_s, canal_sw, canal_w, canal_nw + NULL, canal_ne, canal_e, canal_se, + canal_s, canal_sw, canal_w, canal_nw, canal_n }; int dir1 = -1; int dir2 = -1; - bool has_canal_dir = canal_dirs[N] || canal_dirs[NE] || canal_dirs[E] || canal_dirs[SE] || - canal_dirs[S] || canal_dirs[SW] || canal_dirs[W] || canal_dirs[NW]; + bool has_canal_dir = canal_dirs[DIR_N] || canal_dirs[DIR_NE] || canal_dirs[DIR_E] || canal_dirs[DIR_SE] || + canal_dirs[DIR_S] || canal_dirs[DIR_SW] || canal_dirs[DIR_W] || canal_dirs[DIR_NW]; + int pref_dirs[8] = { DIR_NE, DIR_NW, DIR_SE, DIR_SW, DIR_N, DIR_E, DIR_S, DIR_W }; + if (has_canal_dir) { - // Prefer diagonals using only canal neighbors first. - if (canal_dirs[NE] && canal_dirs[SW]) { dir1 = NE; dir2 = SW; } - else if (canal_dirs[NW] && canal_dirs[SE]) { dir1 = NW; dir2 = SE; } - else { - for (int i = N; i <= NW && dir1 == -1; i++) { - if (! canal_dirs[i]) - continue; - int i_prev = (i == N) ? NW : (i - 1); - int i_next = (i == NW) ? N : (i + 1); - for (int j = N; j <= NW; j++) { - if (! canal_dirs[j] || j == i) - continue; - if (j == i_next || j == i_prev) - continue; - // If a diagonal is available, skip straight lines to favor angled segments. - if (any_canal_diagonal) { - if ((i == N && j == S) || (i == S && j == N) || - (i == E && j == W) || (i == W && j == E)) - continue; - } - bool pair_is_too_close = false; - for (int k = 0; k < disallowed_pair_count; k++) { - if ((i == disallowed_pairs[k][0] && j == disallowed_pairs[k][1]) || - (i == disallowed_pairs[k][1] && j == disallowed_pairs[k][0])) { - pair_is_too_close = true; - break; - } - } - if (pair_is_too_close) - continue; - dir1 = i; - dir2 = j; - break; - } - } - if (dir1 == -1) { - // Fall back to straight lines only if no diagonal/mixed pair is viable. - if (canal_dirs[N] && canal_dirs[S]) { dir1 = N; dir2 = S; } - else if (canal_dirs[E] && canal_dirs[W]) { dir1 = E; dir2 = W; } - } - if (dir1 == -1) { - for (int i = N; i <= NW; i++) { - if (canal_dirs[i]) { - dir1 = i; - break; - } - } - } - } - if (dir1 == -1) { - // No canal direction found; fall back to water/land adjacency. - if (available_dirs[NE] && available_dirs[SW]) { dir1 = NE; dir2 = SW; } - else if (available_dirs[NW] && available_dirs[SE]) { dir1 = NW; dir2 = SE; } - else { - for (int i = N; i <= NW && dir1 == -1; i++) { - if (! available_dirs[i]) - continue; - int i_prev = (i == N) ? NW : (i - 1); - int i_next = (i == NW) ? N : (i + 1); - for (int j = N; j <= NW; j++) { - if (! available_dirs[j] || j == i) - continue; - if (j == i_next || j == i_prev) - continue; - // If a diagonal is available, skip straight lines to favor angled segments. - if (any_available_diagonal) { - if ((i == N && j == S) || (i == S && j == N) || - (i == E && j == W) || (i == W && j == E)) - continue; - } - bool pair_is_too_close = false; - for (int k = 0; k < disallowed_pair_count; k++) { - if ((i == disallowed_pairs[k][0] && j == disallowed_pairs[k][1]) || - (i == disallowed_pairs[k][1] && j == disallowed_pairs[k][0])) { - pair_is_too_close = true; - break; - } - } - if (pair_is_too_close) - continue; - dir1 = i; - dir2 = j; - break; - } - } - if (dir1 == -1) { - // Fall back to straight lines only if no diagonal/mixed pair is viable. - if (available_dirs[N] && available_dirs[S]) { dir1 = N; dir2 = S; } - else if (available_dirs[E] && available_dirs[W]) { dir1 = E; dir2 = W; } - } - if (dir1 == -1) { - for (int i = N; i <= NW; i++) { - if (available_dirs[i]) { - dir1 = i; - break; - } - } - } - } - } else if (dir2 == -1) { - int canal_dir = dir1; - int a1 = -1; - int a2 = -1; - if (available_dirs[NE] && available_dirs[SW]) { a1 = NE; a2 = SW; } - else if (available_dirs[NW] && available_dirs[SE]) { a1 = NW; a2 = SE; } - else { - for (int i = N; i <= NW && a1 == -1; i++) { - if (! available_dirs[i]) - continue; - int i_prev = (i == N) ? NW : (i - 1); - int i_next = (i == NW) ? N : (i + 1); - for (int j = N; j <= NW; j++) { - if (! available_dirs[j] || j == i) - continue; - if (j == i_next || j == i_prev) - continue; - // If a diagonal is available, skip straight lines to favor angled segments. - if (any_available_diagonal) { - if ((i == N && j == S) || (i == S && j == N) || - (i == E && j == W) || (i == W && j == E)) - continue; - } - bool pair_is_too_close = false; - for (int k = 0; k < disallowed_pair_count; k++) { - if ((i == disallowed_pairs[k][0] && j == disallowed_pairs[k][1]) || - (i == disallowed_pairs[k][1] && j == disallowed_pairs[k][0])) { - pair_is_too_close = true; - break; - } - } - if (pair_is_too_close) - continue; - a1 = i; - a2 = j; - break; - } - } - if (a1 == -1) { - // Fall back to straight lines only if no diagonal/mixed pair is viable. - if (available_dirs[N] && available_dirs[S]) { a1 = N; a2 = S; } - else if (available_dirs[E] && available_dirs[W]) { a1 = E; a2 = W; } - } - if (a1 == -1) { - for (int i = N; i <= NW; i++) { - if (available_dirs[i]) { - a1 = i; - break; - } - } - } - } - if (a1 == canal_dir) { dir1 = a1; dir2 = a2; } - else if (a2 == canal_dir) { dir1 = a2; dir2 = a1; } - else { dir1 = canal_dir; dir2 = a1; } + for (int i = 0; i < 8 && dir1 == -1; i++) { + int d = pref_dirs[i]; + if (canal_dirs[d]) + dir1 = d; } } else { - // Prefer diagonals. - if (available_dirs[NE] && available_dirs[SW]) { dir1 = NE; dir2 = SW; } - else if (available_dirs[NW] && available_dirs[SE]) { dir1 = NW; dir2 = SE; } - else { - for (int i = N; i <= NW && dir1 == -1; i++) { - if (! available_dirs[i]) + for (int i = 0; i < 8 && dir1 == -1; i++) { + int d = pref_dirs[i]; + if (available_dirs[d]) + dir1 = d; + } + } + + if (dir1 >= 0) { + for (int pass = 0; pass < 2 && dir2 == -1; pass++) { + bool * dirs = (pass == 0) ? canal_dirs : available_dirs; + for (int i = 0; i < 8; i++) { + int d = pref_dirs[i]; + if (d == dir1 || ! dirs[d]) continue; - int i_prev = (i == N) ? NW : (i - 1); - int i_next = (i == NW) ? N : (i + 1); - for (int j = N; j <= NW; j++) { - if (! available_dirs[j] || j == i) - continue; - if (j == i_next || j == i_prev) - continue; - // If a diagonal is available, skip straight lines to favor angled segments. - if (any_available_diagonal) { - if ((i == N && j == S) || (i == S && j == N) || - (i == E && j == W) || (i == W && j == E)) - continue; - } - bool pair_is_too_close = false; - for (int k = 0; k < disallowed_pair_count; k++) { - if ((i == disallowed_pairs[k][0] && j == disallowed_pairs[k][1]) || - (i == disallowed_pairs[k][1] && j == disallowed_pairs[k][0])) { - pair_is_too_close = true; - break; - } - } - if (pair_is_too_close) - continue; - dir1 = i; - dir2 = j; - break; - } - } - if (dir1 == -1) { - // Fall back to straight lines only if no diagonal/mixed pair is viable. - if (available_dirs[N] && available_dirs[S]) { dir1 = N; dir2 = S; } - else if (available_dirs[E] && available_dirs[W]) { dir1 = E; dir2 = W; } - } - if (dir1 == -1) { - for (int i = N; i <= NW; i++) { - if (available_dirs[i]) { - dir1 = i; + bool pair_is_too_close = false; + for (int k = 0; k < disallowed_pair_count; k++) { + if ((dir1 == disallowed_pairs[k][0] && d == disallowed_pairs[k][1]) || + (dir1 == disallowed_pairs[k][1] && d == disallowed_pairs[k][0])) { + pair_is_too_close = true; break; } } - } - } - } - if ((dir1 >= 0) && (dir2 >= 0)) { - bool pair_is_too_close = false; - for (int k = 0; k < disallowed_pair_count; k++) { - if ((dir1 == disallowed_pairs[k][0] && dir2 == disallowed_pairs[k][1]) || - (dir1 == disallowed_pairs[k][1] && dir2 == disallowed_pairs[k][0])) { - pair_is_too_close = true; + if (pair_is_too_close) + continue; + dir2 = d; break; } } - if (pair_is_too_close) { - if (available_dirs[N] && available_dirs[S]) { dir1 = N; dir2 = S; } - else if (available_dirs[E] && available_dirs[W]) { dir1 = E; dir2 = W; } - else { - if (available_dirs[N]) { dir1 = N; } - else if (available_dirs[S]) { dir1 = S; } - else if (available_dirs[E]) { dir1 = E; } - else if (available_dirs[W]) { dir1 = W; } - dir2 = -1; - } - } } struct district_config const * cfg = &is->district_configs[CANAL_DISTRICT_ID]; @@ -27428,13 +27222,13 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren int weight1 = 0; int weight2 = 0; - if (draw_dir1 == S) weight1 = 2; - else if ((draw_dir1 == SE) || (draw_dir1 == SW)) weight1 = 1; - else if ((draw_dir1 == NE) || (draw_dir1 == NW) || (draw_dir1 == N)) weight1 = -1; + if (draw_dir1 == DIR_S) weight1 = 2; + else if ((draw_dir1 == DIR_SE) || (draw_dir1 == DIR_SW)) weight1 = 1; + else if ((draw_dir1 == DIR_NE) || (draw_dir1 == DIR_NW) || (draw_dir1 == DIR_N)) weight1 = -1; - if (draw_dir2 == S) weight2 = 2; - else if ((draw_dir2 == SE) || (draw_dir2 == SW)) weight2 = 1; - else if ((draw_dir2 == NE) || (draw_dir2 == NW) || (draw_dir2 == N)) weight2 = -1; + if (draw_dir2 == DIR_S) weight2 = 2; + else if ((draw_dir2 == DIR_SE) || (draw_dir2 == DIR_SW)) weight2 = 1; + else if ((draw_dir2 == DIR_NE) || (draw_dir2 == DIR_NW) || (draw_dir2 == DIR_N)) weight2 = -1; if (weight1 > weight2) { int tmp = draw_dir1; @@ -27443,13 +27237,18 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren } } + // Manual overrides - overall algorithm works pretty well, but handle corner cases + if (no_other_canals && water_ne && water_sw) { draw_dir1 = DIR_NE; draw_dir2 = DIR_SW; } + else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_S && water_n && water_ne) { draw_dir1 = DIR_N; draw_dir2 = DIR_S; } + else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_SE && water_se && water_sw) { draw_dir2 = DIR_SW; } + snprintf (ss, sizeof(ss), "Drawing canal at (%d,%d) dirs:%d,%d\n", tile_x, tile_y, draw_dir1, draw_dir2); (*p_OutputDebugStringA)(ss); if (draw_dir1 >= 0) - draw_canal_on_map_or_canvas(dir_sprites[draw_dir1], draw_dir1, water_dirs, map_renderer, draw_x, draw_y); + draw_canal_on_map_or_canvas(dir_sprites[draw_dir1], tile_x, tile_y, draw_dir1, water_dirs, map_renderer, draw_x, draw_y); if (draw_dir2 >= 0) - draw_canal_on_map_or_canvas(dir_sprites[draw_dir2], draw_dir2, water_dirs, map_renderer, draw_x, draw_y); + draw_canal_on_map_or_canvas(dir_sprites[draw_dir2], tile_x, tile_y, draw_dir2, water_dirs, map_renderer, draw_x, draw_y); } void __fastcall From f5cb97d824be5584ca70531b9d7490e74e3023d5 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 14 Jan 2026 15:23:27 -0800 Subject: [PATCH 183/356] Updated canal logic --- injected_code.c | 81 ++++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 34 deletions(-) diff --git a/injected_code.c b/injected_code.c index 259932b6..ce65ec2e 100644 --- a/injected_code.c +++ b/injected_code.c @@ -27093,15 +27093,8 @@ draw_canal_on_map_or_canvas(Sprite * sprite, int tile_x, int tile_y, int dir, bo } void -draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int era) +get_canal_directions (int tile_x, int tile_y, bool * out_water_dirs[9], int * out_dir1, int * out_dir2) { - // Avoid acute angles (adjacent directions) that look cramped. - int disallowed_pairs[][2] = { - { DIR_N, DIR_NE }, { DIR_NE, DIR_E }, { DIR_E, DIR_SE }, { DIR_SE, DIR_S }, - { DIR_S, DIR_SW }, { DIR_SW, DIR_W }, { DIR_W, DIR_NW }, { DIR_NW, DIR_N } - }; - int disallowed_pair_count = sizeof(disallowed_pairs) / sizeof(disallowed_pairs[0]); - bool canal_at_n = tile_has_district_at (tile_x, tile_y - 2, CANAL_DISTRICT_ID); bool canal_at_s = tile_has_district_at (tile_x, tile_y + 2, CANAL_DISTRICT_ID); bool canal_at_w = tile_has_district_at (tile_x - 2, tile_y, CANAL_DISTRICT_ID); @@ -27126,8 +27119,6 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren bool canal_or_water_nw = canal_at_nw || water_nw; bool canal_or_water_se = canal_at_se || water_se; bool canal_or_water_sw = canal_at_sw || water_sw; - bool no_other_canals = !canal_at_n && !canal_at_s && !canal_at_w && !canal_at_e && - !canal_at_ne && !canal_at_nw && !canal_at_se && !canal_at_sw; char ss[200]; snprintf (ss, sizeof(ss), "Canal at (%d,%d), canal tiles: N:%d NE:%d E:%d SE:%d S:%d SW:%d W:%d NW:%d; water tiles: N:%d NE:%d E:%d SE:%d S:%d SW:%d W:%d NW:%d\n", @@ -27138,15 +27129,6 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren water_s, water_sw, water_w, water_nw); (*p_OutputDebugStringA)(ss); - Sprite * canal_n = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_N]; - Sprite * canal_ne = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_NE]; - Sprite * canal_e = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_E]; - Sprite * canal_se = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_SE]; - Sprite * canal_s = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_S]; - Sprite * canal_sw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_SW]; - Sprite * canal_w = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_W]; - Sprite * canal_nw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_NW]; - bool canal_dirs[9] = { false, canal_at_ne, canal_at_e, canal_at_se, canal_at_s, canal_at_sw, canal_at_w, canal_at_nw, canal_at_n @@ -27159,17 +27141,20 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren false, canal_or_water_ne, canal_or_water_e, canal_or_water_se, canal_or_water_s, canal_or_water_sw, canal_or_water_w, canal_or_water_nw, canal_or_water_n }; - Sprite * dir_sprites[9] = { - NULL, canal_ne, canal_e, canal_se, - canal_s, canal_sw, canal_w, canal_nw, canal_n + + // Avoid acute angles (adjacent directions) that look cramped. + int disallowed_pairs[][2] = { + { DIR_N, DIR_NE }, { DIR_NE, DIR_E }, { DIR_E, DIR_SE }, { DIR_SE, DIR_S }, + { DIR_S, DIR_SW }, { DIR_SW, DIR_W }, { DIR_W, DIR_NW }, { DIR_NW, DIR_N } }; + int disallowed_pair_count = sizeof(disallowed_pairs) / sizeof(disallowed_pairs[0]); + int pref_dirs[8] = { DIR_NE, DIR_NW, DIR_SE, DIR_SW, DIR_N, DIR_E, DIR_S, DIR_W }; int dir1 = -1; int dir2 = -1; bool has_canal_dir = canal_dirs[DIR_N] || canal_dirs[DIR_NE] || canal_dirs[DIR_E] || canal_dirs[DIR_SE] || canal_dirs[DIR_S] || canal_dirs[DIR_SW] || canal_dirs[DIR_W] || canal_dirs[DIR_NW]; - int pref_dirs[8] = { DIR_NE, DIR_NW, DIR_SE, DIR_SW, DIR_N, DIR_E, DIR_S, DIR_W }; if (has_canal_dir) { for (int i = 0; i < 8 && dir1 == -1; i++) { @@ -27208,14 +27193,6 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren } } - struct district_config const * cfg = &is->district_configs[CANAL_DISTRICT_ID]; - int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; - int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; - int offset_x = pixel_x + cfg->x_offset; - int offset_y = pixel_y + cfg->y_offset; - int draw_x = offset_x - ((sprite_width - 128) / 2); - int draw_y = offset_y - (sprite_height - 64); - int draw_dir1 = dir1; int draw_dir2 = dir2; if ((draw_dir1 >= 0) && (draw_dir2 >= 0)) { @@ -27238,13 +27215,49 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren } // Manual overrides - overall algorithm works pretty well, but handle corner cases - if (no_other_canals && water_ne && water_sw) { draw_dir1 = DIR_NE; draw_dir2 = DIR_SW; } - else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_S && water_n && water_ne) { draw_dir1 = DIR_N; draw_dir2 = DIR_S; } - else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_SE && water_se && water_sw) { draw_dir2 = DIR_SW; } + if (!has_canal_dir && water_dirs[DIR_NE] && water_dirs[DIR_SW]) { draw_dir1 = DIR_NE; draw_dir2 = DIR_SW; } + else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_S && water_dirs[DIR_N] && water_dirs[DIR_NE]) { draw_dir1 = DIR_N; draw_dir2 = DIR_S; } + else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_SE && water_dirs[DIR_SE] && water_dirs[DIR_SW]) { draw_dir2 = DIR_SW; } + else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_SE && water_dirs[DIR_NE] && water_dirs[DIR_N]) { draw_dir1 = DIR_N; } snprintf (ss, sizeof(ss), "Drawing canal at (%d,%d) dirs:%d,%d\n", tile_x, tile_y, draw_dir1, draw_dir2); (*p_OutputDebugStringA)(ss); + *out_dir1 = draw_dir1; + *out_dir2 = draw_dir2; + *out_water_dirs = water_dirs; +} + +void +draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int era) +{ + Sprite * canal_n = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_N]; + Sprite * canal_ne = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_NE]; + Sprite * canal_e = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_E]; + Sprite * canal_se = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_SE]; + Sprite * canal_s = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_S]; + Sprite * canal_sw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_SW]; + Sprite * canal_w = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_W]; + Sprite * canal_nw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_NW]; + + Sprite * dir_sprites[9] = { + NULL, canal_ne, canal_e, canal_se, + canal_s, canal_sw, canal_w, canal_nw, canal_n + }; + + struct district_config const * cfg = &is->district_configs[CANAL_DISTRICT_ID]; + int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; + int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; + int offset_x = pixel_x + cfg->x_offset; + int offset_y = pixel_y + cfg->y_offset; + int draw_x = offset_x - ((sprite_width - 128) / 2); + int draw_y = offset_y - (sprite_height - 64); + + int draw_dir1 = -1; + int draw_dir2 = -1; + bool water_dirs[9] = { false, false, false, false, false, false, false, false, false }; + get_canal_directions (tile_x, tile_y, &water_dirs, &draw_dir1, &draw_dir2); + if (draw_dir1 >= 0) draw_canal_on_map_or_canvas(dir_sprites[draw_dir1], tile_x, tile_y, draw_dir1, water_dirs, map_renderer, draw_x, draw_y); if (draw_dir2 >= 0) From 10941b186208a83e6af5c2044917dc4444bb9b36 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 14 Jan 2026 15:40:01 -0800 Subject: [PATCH 184/356] Refactor bridges to always know which direction facing, like canals, by separate func without rendering --- injected_code.c | 57 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/injected_code.c b/injected_code.c index ce65ec2e..6a79cb7e 100644 --- a/injected_code.c +++ b/injected_code.c @@ -27044,15 +27044,15 @@ get_bridge_image_index (Tile * tile, int tile_x, int tile_y) int swne_count = (bridge_sw ? 1 : 0) + (bridge_ne ? 1 : 0); int nwse_count = (bridge_nw ? 1 : 0) + (bridge_se ? 1 : 0); - if (ns_count == 2) return N_S; - if (we_count == 2) return W_E; if (swne_count == 2) return SW_NE; if (nwse_count == 2) return NW_SE; + if (ns_count == 2) return N_S; + if (we_count == 2) return W_E; - if (ns_count == 1) return N_S; - if (we_count == 1) return W_E; - if (swne_count == 1) return SW_NE; - if (nwse_count == 1) return NW_SE; + if (bridge_ne || bridge_sw) return SW_NE; + if (bridge_nw || bridge_se) return NW_SE; + if (bridge_n || bridge_s) return N_S; + if (bridge_w || bridge_e) return W_E; } int owner_id = tile->Territory_OwnerID; @@ -27065,14 +27065,47 @@ get_bridge_image_index (Tile * tile, int tile_x, int tile_y) bool se_land = tile_is_land (owner_id, tile_x + 1, tile_y + 1, false); bool sw_land = tile_is_land (owner_id, tile_x - 1, tile_y + 1, false); - if (north_land || south_land) return N_S; - if (west_land || east_land) return W_E; - if (ne_land || sw_land) return SW_NE; - if (nw_land || se_land) return NW_SE; + bool north_link = north_land || bridge_n; + bool south_link = south_land || bridge_s; + bool west_link = west_land || bridge_w; + bool east_link = east_land || bridge_e; + bool ne_link = ne_land || bridge_ne; + bool nw_link = nw_land || bridge_nw; + bool se_link = se_land || bridge_se; + bool sw_link = sw_land || bridge_sw; + + if (sw_link && ne_link) return SW_NE; + if (nw_link && se_link) return NW_SE; + if (north_link && south_link) return N_S; + if (west_link && east_link) return W_E; + + if (ne_link || sw_link) return SW_NE; + if (nw_link || se_link) return NW_SE; + if (north_link || south_link) return N_S; + if (west_link || east_link) return W_E; return 0; } +void +get_bridge_directions (Tile * tile, int tile_x, int tile_y, int * out_dir1, int * out_dir2) +{ + int dir1 = -1; + int dir2 = -1; + int index = get_bridge_image_index (tile, tile_x, tile_y); + + switch (index) { + case 0: dir1 = DIR_SW; dir2 = DIR_NE; break; + case 1: dir1 = DIR_NW; dir2 = DIR_SE; break; + case 2: dir1 = DIR_N; dir2 = DIR_S; break; + case 3: dir1 = DIR_W; dir2 = DIR_E; break; + default: break; + } + + *out_dir1 = dir1; + *out_dir2 = dir2; +} + void draw_canal_on_map_or_canvas(Sprite * sprite, int tile_x, int tile_y, int dir, bool water_dirs[9], Map_Renderer * map_renderer, int draw_x, int draw_y) { @@ -27093,7 +27126,7 @@ draw_canal_on_map_or_canvas(Sprite * sprite, int tile_x, int tile_y, int dir, bo } void -get_canal_directions (int tile_x, int tile_y, bool * out_water_dirs[9], int * out_dir1, int * out_dir2) +get_canal_directions (Tile * tile, int tile_x, int tile_y, bool * out_water_dirs[9], int * out_dir1, int * out_dir2) { bool canal_at_n = tile_has_district_at (tile_x, tile_y - 2, CANAL_DISTRICT_ID); bool canal_at_s = tile_has_district_at (tile_x, tile_y + 2, CANAL_DISTRICT_ID); @@ -27256,7 +27289,7 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren int draw_dir1 = -1; int draw_dir2 = -1; bool water_dirs[9] = { false, false, false, false, false, false, false, false, false }; - get_canal_directions (tile_x, tile_y, &water_dirs, &draw_dir1, &draw_dir2); + get_canal_directions (tile, tile_x, tile_y, &water_dirs, &draw_dir1, &draw_dir2); if (draw_dir1 >= 0) draw_canal_on_map_or_canvas(dir_sprites[draw_dir1], tile_x, tile_y, draw_dir1, water_dirs, map_renderer, draw_x, draw_y); From f3d4fdf8a7d50edeca5d310431407e9df8994e0d Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 14 Jan 2026 16:01:11 -0800 Subject: [PATCH 185/356] Add flags for enabling/disabled units from entering bridges and canals from any direction --- Art/Districts/1200/Canal.PCX | Bin 78816 -> 93317 bytes C3X.h | 2 + default.c3x_config.ini | 3 + injected_code.c | 133 +++++++++++++++++++++++++++++++---- 4 files changed, 124 insertions(+), 14 deletions(-) diff --git a/Art/Districts/1200/Canal.PCX b/Art/Districts/1200/Canal.PCX index e2fba6124f61eacd77ce8d5bf44d7115d5278451..91d4e20fcf654b7738f14aa3c942c05fa3d56cd2 100644 GIT binary patch delta 33 ocmaFxoTc?9>xPKF$pVE5o9h_==S()Z{S3%>p0oYF0V5+b03FE>ssI20 delta 30081 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 == BRIDGE_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) - base_validity = AMV_OK; + district_is_complete (dest, inst->district_type)) { + bool allow_move = true; + if (! is->current_config.allow_enter_bridge_from_any_direction) { + int move_dx = 0, move_dy = 0; + int dir1 = -1, dir2 = -1; + neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); + get_bridge_directions (dest, nx, ny, &dir1, &dir2); + allow_move = move_matches_directions (move_dx, move_dy, dir1, dir2); + } + if (allow_move) + base_validity = AMV_OK; + } } } @@ -16028,8 +16043,18 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, struct district_instance * inst = get_district_instance (dest); if ((inst != NULL) && (inst->district_type == CANAL_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) - base_validity = AMV_OK; + district_is_complete (dest, inst->district_type)) { + bool allow_move = true; + if (! is->current_config.allow_enter_canal_from_any_direction) { + int move_dx = 0, move_dy = 0; + int dir1 = -1, dir2 = -1; + neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); + get_canal_directions (dest, nx, ny, NULL, &dir1, &dir2); + allow_move = move_matches_directions (move_dx, move_dy, dir1, dir2); + } + if (allow_move) + base_validity = AMV_OK; + } } } @@ -16101,8 +16126,18 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr struct district_instance * inst = get_district_instance (dest); if ((inst != NULL) && (inst->district_type == BRIDGE_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) - base_cost = Unit_get_max_move_points (unit); + district_is_complete (dest, inst->district_type)) { + bool allow_move = true; + if (! is->current_config.allow_enter_bridge_from_any_direction) { + int move_dx = 0, move_dy = 0; + int dir1 = -1, dir2 = -1; + neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); + get_bridge_directions (dest, to_x, to_y, &dir1, &dir2); + allow_move = move_matches_directions (move_dx, move_dy, dir1, dir2); + } + if (allow_move) + base_cost = Unit_get_max_move_points (unit); + } } } @@ -16116,8 +16151,19 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr struct district_instance * inst = get_district_instance (dest); if ((inst != NULL) && (inst->district_type == CANAL_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) - base_cost = Unit_get_max_move_points (unit); + district_is_complete (dest, inst->district_type)) { + bool allow_move = true; + if (! is->current_config.allow_enter_canal_from_any_direction) { + int move_dx = 0, move_dy = 0; + int dir1 = -1, dir2 = -1; + bool water_dirs[9] = { false, false, false, false, false, false, false, false, false }; + neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); + get_canal_directions (dest, to_x, to_y, &water_dirs, &dir1, &dir2); + allow_move = move_matches_directions (move_dx, move_dy, dir1, dir2); + } + if (allow_move) + base_cost = Unit_get_max_move_points (unit); + } } } @@ -27258,7 +27304,9 @@ get_canal_directions (Tile * tile, int tile_x, int tile_y, bool * out_water_dirs *out_dir1 = draw_dir1; *out_dir2 = draw_dir2; - *out_water_dirs = water_dirs; + + if (out_water_dirs != NULL) + *out_water_dirs = water_dirs; } void @@ -28670,8 +28718,22 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t struct district_instance * inst = get_district_instance (dest); if ((inst != NULL) && (inst->district_type == BRIDGE_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) - return PBV_OK; + district_is_complete (dest, inst->district_type)) { + bool allow_move = true; + if (! is->current_config.allow_enter_bridge_from_any_direction) { + int move_dx = 0, move_dy = 0; + int dir1 = -1, dir2 = -1; + int neighbor_index = Map_compute_neighbor_index (&p_bic_data->Map, __, from_x, from_y, to_x, to_y, 1000); + if (neighbor_index >= 0) { + neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); + get_bridge_directions (dest, to_x, to_y, &dir1, &dir2); + allow_move = move_matches_directions (move_dx, move_dy, dir1, dir2); + } else + allow_move = false; + } + if (allow_move) + return PBV_OK; + } } } @@ -28684,8 +28746,23 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t struct district_instance * inst = get_district_instance (dest); if ((inst != NULL) && (inst->district_type == CANAL_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) - return PBV_OK; + district_is_complete (dest, inst->district_type)) { + bool allow_move = true; + if (!is->current_config.allow_enter_canal_from_any_direction) { + int move_dx = 0, move_dy = 0; + int dir1 = -1, dir2 = -1; + bool water_dirs[9] = { false, false, false, false, false, false, false, false, false }; + int neighbor_index = Map_compute_neighbor_index (&p_bic_data->Map, __, from_x, from_y, to_x, to_y, 1000); + if (neighbor_index >= 0) { + neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); + get_canal_directions (dest, to_x, to_y, &water_dirs, &dir1, &dir2); + allow_move = move_matches_directions (move_dx, move_dy, dir1, dir2); + } else + allow_move = false; + } + if (allow_move) + return PBV_OK; + } } } @@ -28713,8 +28790,21 @@ patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool struct district_instance * inst = get_district_instance (dest); if ((inst != NULL) && (inst->district_type == BRIDGE_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) + district_is_complete (dest, inst->district_type)) { allow_move = true; + if (! is->current_config.allow_enter_bridge_from_any_direction) { + int move_dx = 0, move_dy = 0; + int dir1 = -1, dir2 = -1; + int neighbor_index = Map_compute_neighbor_index (&p_bic_data->Map, __, + this->Body.X, this->Body.Y, tile_x, tile_y, 1000); + if (neighbor_index >= 0) { + neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); + get_bridge_directions (dest, tile_x, tile_y, &dir1, &dir2); + allow_move = move_matches_directions (move_dx, move_dy, dir1, dir2); + } else + allow_move = false; + } + } } if (allow_move) { @@ -28727,6 +28817,21 @@ patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool return transport; } +bool +move_matches_directions (int move_dx, int move_dy, int dir1, int dir2) +{ + int dx = 0, dy = 0; + if ((dir1 >= 0) && + direction_to_offset ((enum direction)dir1, &dx, &dy) && + (dx == move_dx) && (dy == move_dy)) + return true; + if ((dir2 >= 0) && + direction_to_offset ((enum direction)dir2, &dx, &dy) && + (dx == move_dx) && (dy == move_dy)) + return true; + return false; +} + // Returns true if the given tile is a water district owned by an enemy of the unit bool is_enemy_maritime_district_tile (Unit * unit, Tile * tile) From 97ad024160cdb600cf1c5f1c63bc867b8053c7fe Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 14 Jan 2026 16:06:50 -0800 Subject: [PATCH 186/356] Add flag for max_contiguous_canal_districts --- C3X.h | 1 + default.c3x_config.ini | 1 + injected_code.c | 83 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) diff --git a/C3X.h b/C3X.h index fa43762e..24555c59 100644 --- a/C3X.h +++ b/C3X.h @@ -377,6 +377,7 @@ struct c3x_config { bool workers_can_enter_coast; bool expand_water_tile_checks_to_city_work_area; int max_contiguous_bridge_districts; + int max_contiguous_canal_districts; bool allow_enter_bridge_from_any_direction; bool allow_enter_canal_from_any_direction; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index fbbf9369..c8993c2c 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -907,6 +907,7 @@ workers_can_enter_coast = true allow_enter_bridge_from_any_direction = false allow_enter_canal_from_any_direction = false max_contiguous_bridge_districts = 3 +max_contiguous_canal_districts = 5 ; When enabled, AI defensive units will actively seek out and defend districts within their territory, treating them as valuable assets like colonies. ; The AI prioritizes defending Wonder districts (if destructible wonders are enabled) over regular districts, searching within a 20-tile radius for diff --git a/injected_code.c b/injected_code.c index 1caaaefb..f2f67047 100644 --- a/injected_code.c +++ b/injected_code.c @@ -230,6 +230,7 @@ void init_district_icons (); int count_neighborhoods_in_city_radius (City * city); int count_utilized_neighborhoods_in_city_radius (City * city); bool move_matches_directions (int move_dx, int move_dy, int dir1, int dir2); +int count_contiguous_canal_districts (int tile_x, int tile_y, int max_count); struct pause_for_popup { bool done; // Set to true to exit for loop @@ -9138,6 +9139,81 @@ count_contiguous_bridge_districts (int tile_x, int tile_y, int dx, int dy) return count; } +int +count_contiguous_canal_districts (int tile_x, int tile_y, int max_count) +{ + if (max_count <= 0) + return 0; + + Map * map = &p_bic_data->Map; + int limit = max_count + 1; + int capacity = limit; + if (capacity < 1) + capacity = 1; + + int * xs = malloc (sizeof (*xs) * capacity); + int * ys = malloc (sizeof (*ys) * capacity); + if ((xs == NULL) || (ys == NULL)) { + if (xs != NULL) + free (xs); + if (ys != NULL) + free (ys); + return limit; + } + + int const adj_dx[8] = { 0, 0, -2, 2, 1, 1, -1, -1 }; + int const adj_dy[8] = { -2, 2, 0, 0, -1, 1, -1, 1 }; + int head = 0; + int tail = 0; + int count = 0; + + xs[tail] = tile_x; + ys[tail] = tile_y; + tail++; + + while (head < tail) { + int cx = xs[head]; + int cy = ys[head]; + head++; + count++; + if (count >= limit) + break; + + for (int i = 0; i < 8; i++) { + int nx = cx + adj_dx[i]; + int ny = cy + adj_dy[i]; + wrap_tile_coords (map, &nx, &ny); + if (! tile_has_district_at (nx, ny, CANAL_DISTRICT_ID)) + continue; + + bool seen = false; + for (int j = 0; j < tail; j++) { + if ((xs[j] == nx) && (ys[j] == ny)) { + seen = true; + break; + } + } + if (seen) + continue; + + if (tail < capacity) { + xs[tail] = nx; + ys[tail] = ny; + tail++; + } else { + count = limit; + head = tail; + break; + } + } + } + + free (xs); + free (ys); + + return count; +} + bool bridge_district_tile_is_valid (int tile_x, int tile_y) { @@ -9200,6 +9276,12 @@ bridge_district_tile_is_valid (int tile_x, int tile_y) bool canal_district_tile_is_valid (int tile_x, int tile_y) { + if (is->current_config.max_contiguous_canal_districts > 0) { + int count = count_contiguous_canal_districts (tile_x, tile_y, is->current_config.max_contiguous_canal_districts); + if (count > is->current_config.max_contiguous_canal_districts) + return false; + } + Map * map = &p_bic_data->Map; int const adj_dx[8] = { 0, 0, -2, 2, 1, 1, -1, -1 }; int const adj_dy[8] = { -2, 2, 0, 0, -1, 1, -1, 1 }; @@ -13301,6 +13383,7 @@ patch_init_floating_point () {"central_rail_hub_distribution_shield_bonus_percent", 25, offsetof (struct c3x_config, central_rail_hub_distribution_shield_bonus_percent)}, {"neighborhood_needed_message_frequency" , 4, offsetof (struct c3x_config, neighborhood_needed_message_frequency)}, {"max_contiguous_bridge_districts" , 0, offsetof (struct c3x_config, max_contiguous_bridge_districts)}, + {"max_contiguous_canal_districts" , 0, offsetof (struct c3x_config, max_contiguous_canal_districts)}, }; is->kernel32 = (*p_GetModuleHandleA) ("kernel32.dll"); From 3f3937ad4bdb2b57d70cfb4bd042441384ff2e8a Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 14 Jan 2026 17:31:04 -0800 Subject: [PATCH 187/356] Experimenting with NS canal width --- Art/Districts/1200/Canal.PCX | Bin 93317 -> 93947 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Canal.PCX b/Art/Districts/1200/Canal.PCX index 91d4e20fcf654b7738f14aa3c942c05fa3d56cd2..81424f4a1b11429ffbf674b8391bc1bea82c239e 100644 GIT binary patch delta 2747 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 PlRUV*oN6m;etYwOS+I_q delta 136 zcmV;30C)fU-35i&1+abrvo4YUY!lS~!b!jmvkAd?btG8WYT z!b>i}(82w|_gcaC!O~u~!Ro>7lVEXD64d|0OSz674tT-Yr@_m??ZNAl#BoCq)&Igv qMjj4E4kfn1%E9ZxlOJ+Q5!V00OEen-X`#W@!OX$f!Q-=la(n! Date: Thu, 15 Jan 2026 13:58:15 -0800 Subject: [PATCH 188/356] Update logic for naval & air movement, AI naval & air district building --- civ_prog_objects.csv | 2 + injected_code.c | 356 +++++++++++++++++++++++++++++++------------ 2 files changed, 264 insertions(+), 94 deletions(-) diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 62b455ed..7018bcb5 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -868,6 +868,8 @@ repl call, 0x4C0173, 0x0, 0x0, "Map_impl_is_near_river_within_w repl call, 0x4C01AF, 0x0, 0x0, "Map_impl_is_near_river_within_work_area", "" repl call, 0x4C01CF, 0x0, 0x0, "Map_impl_is_near_lake_within_work_area", "" inlead, 0x458120, 0x0, 0x0, "Unit_ai_move_naval_power_unit", "void (__fastcall *) (Unit * this)" +inlead, 0x45A170, 0x0, 0x0, "Unit_ai_move_naval_transport", "void (__fastcall *) (Unit * this)" +inlead, 0x460620, 0x0, 0x0, "Unit_ai_move_naval_missile_transport", "void (__fastcall *) (Unit * this)" inlead, 0x44C130, 0x0, 0x0, "Unit_ai_eval_pillage_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" inlead, 0x456840, 0x0, 0x0, "Unit_ai_move_air_bombard_unit", "void (__fastcall *) (Unit * this)" inlead, 0x4579E0, 0x0, 0x0, "Unit_ai_move_air_defense_unit", "void (__fastcall *) (Unit * this)" diff --git a/injected_code.c b/injected_code.c index f2f67047..1004d8cd 100644 --- a/injected_code.c +++ b/injected_code.c @@ -3467,6 +3467,23 @@ wai_init_cities (int x, int y) #define FOR_WORK_AREA_AROUND(wai_name, _x, _y) for (struct work_area_iter wai_name = wai_init (_x, _y); (wai_name.n < wai_name.num_tiles); wai_next (&wai_name)) #define FOR_DISTRICTS_AROUND(wai_name, _x, _y, _completed_only) for (struct work_area_iter wai_name = wai_init_districts (_x, _y, _completed_only); (wai_name.n < wai_name.num_tiles); wai_next (&wai_name)) #define FOR_CITIES_AROUND(wai_name, _x, _y) for (struct work_area_iter wai_name = wai_init_cities (_x, _y); (wai_name.n < wai_name.num_tiles); wai_next (&wai_name)) +#define FOR_AERODROMES_AROUND(unit_ptr) \ + for (struct table_entry_iter _tei = tei_init (&is->district_tile_map); \ + _tei.index < _tei.capacity; \ + tei_next (&_tei)) \ + for (Tile * aerodrome_tile = (Tile *)_tei.key; \ + (aerodrome_tile != NULL) && (aerodrome_tile != p_null_tile); \ + aerodrome_tile = NULL) \ + for (struct district_instance * aerodrome_inst = (struct district_instance *)(long)_tei.value; \ + (aerodrome_inst != NULL) && \ + (aerodrome_inst->district_type == AERODROME_DISTRICT_ID) && \ + district_is_complete (aerodrome_tile, AERODROME_DISTRICT_ID); \ + aerodrome_inst = NULL) \ + for (int aerodrome_x = 0, aerodrome_y = 0; \ + district_instance_get_coords (aerodrome_inst, aerodrome_tile, &aerodrome_x, &aerodrome_y) && \ + (aerodrome_tile->vtable->m38_Get_Territory_OwnerID (aerodrome_tile) == (unit_ptr)->Body.CivID) && \ + patch_Unit_is_in_rebase_range ((unit_ptr), __, aerodrome_x, aerodrome_y); \ + aerodrome_x = 0, aerodrome_y = 0) struct tile_rings_iter { int center_x, center_y; @@ -9621,7 +9638,7 @@ find_tile_for_port_district (City * city, int * out_x, int * out_y) if ((continent_id < 0) || (continent_id >= p_bic_data->Map.Continent_Count)) continue; Continent * continent = &p_bic_data->Map.Continents[continent_id]; - if (continent->Body.TileCount < threshold_for_body_of_water) + if (continent->Body.TileCount < threshold_for_body_of_water && ! is->current_config.enable_canal_districts) continue; int yield = compute_city_tile_yield_sum (city, tri.tile_x, tri.tile_y); @@ -10107,7 +10124,6 @@ tile_has_friendly_port_district (Tile * tile, int civ_id) { if (! is->current_config.enable_districts || ! is->current_config.enable_port_districts || - ! is->current_config.naval_units_use_port_districts_not_cities || (tile == NULL) || (tile == p_null_tile)) return false; @@ -13375,15 +13391,15 @@ patch_init_floating_point () {"city_limit" , 2048, offsetof (struct c3x_config, city_limit)}, {"maximum_pop_before_neighborhood_needed" , 8, offsetof (struct c3x_config, maximum_pop_before_neighborhood_needed)}, {"per_neighborhood_pop_growth_enabled" , 2, offsetof (struct c3x_config, per_neighborhood_pop_growth_enabled)}, - {"minimum_natural_wonder_separation" , 10, offsetof (struct c3x_config, minimum_natural_wonder_separation)}, + {"minimum_natural_wonder_separation" , 10, offsetof (struct c3x_config, minimum_natural_wonder_separation)}, {"distribution_hub_food_yield_divisor" , 1, offsetof (struct c3x_config, distribution_hub_food_yield_divisor)}, {"distribution_hub_shield_yield_divisor" , 1, offsetof (struct c3x_config, distribution_hub_shield_yield_divisor)}, {"ai_ideal_distribution_hub_count_per_100_cities" , 1, offsetof (struct c3x_config, ai_ideal_distribution_hub_count_per_100_cities)}, {"central_rail_hub_distribution_food_bonus_percent" , 25, offsetof (struct c3x_config, central_rail_hub_distribution_food_bonus_percent)}, {"central_rail_hub_distribution_shield_bonus_percent", 25, offsetof (struct c3x_config, central_rail_hub_distribution_shield_bonus_percent)}, {"neighborhood_needed_message_frequency" , 4, offsetof (struct c3x_config, neighborhood_needed_message_frequency)}, - {"max_contiguous_bridge_districts" , 0, offsetof (struct c3x_config, max_contiguous_bridge_districts)}, - {"max_contiguous_canal_districts" , 0, offsetof (struct c3x_config, max_contiguous_canal_districts)}, + {"max_contiguous_bridge_districts" , 3, offsetof (struct c3x_config, max_contiguous_bridge_districts)}, + {"max_contiguous_canal_districts" , 5, offsetof (struct c3x_config, max_contiguous_canal_districts)}, }; is->kernel32 = (*p_GetModuleHandleA) ("kernel32.dll"); @@ -17406,8 +17422,8 @@ patch_City_can_build_unit (City * this, int edx, int unit_type_id, bool exclude_ // If a disallowed air/naval unit is chosen in ai_choose_production, we'll swap it out for a feasible fallback later // after prioritizing the aerodrome/port to be built if (! is_human && ( - (city_can_build_district (this, AERODROME_DISTRICT_ID) && type->Unit_Class == UTC_Air) || - (city_can_build_district (this, PORT_DISTRICT_ID) && type->Unit_Class == UTC_Sea)) + (type->Unit_Class == UTC_Air || city_can_build_district (this, AERODROME_DISTRICT_ID)) || + (type->Unit_Class == UTC_Sea || city_can_build_district (this, PORT_DISTRICT_ID))) ) return base; @@ -17444,7 +17460,7 @@ patch_City_get_largest_adjacent_sea_within_work_area (City * this) if ((continent_id < 0) || (continent_id >= p_bic_data->Map.Continent_Count)) continue; Continent * continent = &p_bic_data->Map.Continents[continent_id]; - if (continent->Body.TileCount <= lake_size_threshold) + if (continent->Body.TileCount <= lake_size_threshold && ! is->current_config.enable_canal_districts) continue; if (p_bic_data->Map.Continents[continent_id].Body.TileCount > largest_size) { largest_size = p_bic_data->Map.Continents[continent_id].Body.TileCount; @@ -17667,6 +17683,8 @@ ai_handle_district_production_requirements (City * city, City_Order * out) fallback_is_feasible = false; if (fallback_is_feasible) { UnitType * type = &p_bic_data->UnitTypes[stored->order.OrderID]; + if (type->Unit_Class != UTC_Land) + fallback_is_feasible = false; if ((type->Unit_Class == UTC_Air) && is->current_config.enable_aerodrome_districts && is->current_config.air_units_use_aerodrome_districts_not_cities && @@ -19191,7 +19209,9 @@ copy_building_with_cities_in_radius (City * source, int improv_id, int required_ if (current_improv_id == improv_id) { City_Order defensive_order = { .OrderID = -1, .OrderType = 0 }; if (choose_defensive_unit_order (city, &defensive_order)) { - City_set_production (city, __, defensive_order.OrderType, defensive_order.OrderID, false); + UnitType * def_type = &p_bic_data->UnitTypes[defensive_order.OrderID]; + if (def_type->Unit_Class == UTC_Land) + City_set_production (city, __, defensive_order.OrderType, defensive_order.OrderID, false); } } @@ -19274,7 +19294,9 @@ grant_existing_district_buildings_to_city (City * city) if (current_improv_id == building_id) { City_Order defensive_order = { .OrderID = -1, .OrderType = 0 }; if (choose_defensive_unit_order (city, &defensive_order)) { - City_set_production (city, __, defensive_order.OrderType, defensive_order.OrderID, false); + UnitType * def_type = &p_bic_data->UnitTypes[defensive_order.OrderID]; + if (def_type->Unit_Class == UTC_Land) + City_set_production (city, __, defensive_order.OrderType, defensive_order.OrderID, false); } } @@ -21581,6 +21603,7 @@ assign_ai_fallback_production (City * city, int disallowed_improvement_id) } else if (new_order.OrderType == COT_Unit) { if ((new_order.OrderID >= 0) && (new_order.OrderID < p_bic_data->UnitTypeCount) && + (p_bic_data->UnitTypes[new_order.OrderID].Unit_Class == UTC_Land) && patch_City_can_build_unit (city, __, new_order.OrderID, 1, 0, 0)) order_ok = true; } @@ -21598,8 +21621,11 @@ assign_ai_fallback_production (City * city, int disallowed_improvement_id) City_Order defensive_order = { .OrderID = -1, .OrderType = 0 }; if (choose_defensive_unit_order (city, &defensive_order)) { - City_set_production (city, __, defensive_order.OrderType, defensive_order.OrderID, false); - return true; + UnitType * def_type = &p_bic_data->UnitTypes[defensive_order.OrderID]; + if (def_type->Unit_Class == UTC_Land) { + City_set_production (city, __, defensive_order.OrderType, defensive_order.OrderID, false); + return true; + } } return false; @@ -21678,6 +21704,48 @@ patch_Leader_do_production_phase (Leader * this) ensure_neighborhood_request_for_city (city); } + if (city->Body.Order_Type == COT_Unit) { + int unit_id = city->Body.Order_ID; + int req_district_id = -1; + bool needs_halt = false; + + if ((unit_id >= 0) && (unit_id < p_bic_data->UnitTypeCount)) { + UnitType * type = &p_bic_data->UnitTypes[unit_id]; + if ((type->Unit_Class == UTC_Air) && + is->current_config.enable_aerodrome_districts && + is->current_config.air_units_use_aerodrome_districts_not_cities && + (! city_has_required_district (city, AERODROME_DISTRICT_ID))) { + req_district_id = AERODROME_DISTRICT_ID; + needs_halt = true; + } else if ((type->Unit_Class == UTC_Sea) && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities && + (! city_has_required_district (city, PORT_DISTRICT_ID))) { + req_district_id = PORT_DISTRICT_ID; + needs_halt = true; + } + } + + if (needs_halt) { + char ss[200]; + snprintf (ss, sizeof ss, "patch_Leader_do_production_phase: City %d (%s) halting unit %d due to missing district %d\n", + city->Body.ID, city->Body.CityName, unit_id, req_district_id); + (*p_OutputDebugStringA) (ss); + + if (ai_player) + mark_city_needs_district (city, req_district_id); + + City_Order defensive_order = { .OrderID = -1, .OrderType = 0 }; + if (choose_defensive_unit_order (city, &defensive_order)) { + UnitType * def_type = &p_bic_data->UnitTypes[defensive_order.OrderID]; + if (def_type->Unit_Class == UTC_Land) + City_set_production (city, __, defensive_order.OrderType, defensive_order.OrderID, false); + } + + continue; + } + } + if (city->Body.Order_Type != COT_Improvement) continue; int i_improv = city->Body.Order_ID; @@ -21745,7 +21813,9 @@ patch_Leader_do_production_phase (Leader * this) } else { City_Order defensive_order = { .OrderID = -1, .OrderType = 0 }; if (choose_defensive_unit_order (city, &defensive_order)) { - City_set_production (city, __, defensive_order.OrderType, defensive_order.OrderID, false); + UnitType * def_type = &p_bic_data->UnitTypes[defensive_order.OrderID]; + if (def_type->Unit_Class == UTC_Land) + City_set_production (city, __, defensive_order.OrderType, defensive_order.OrderID, false); } } @@ -24586,7 +24656,6 @@ patch_Unit_can_heal_at (Unit * this, int edx, int tile_x, int tile_y) { if (is->current_config.enable_districts && is->current_config.enable_port_districts && - is->current_config.naval_units_use_port_districts_not_cities && (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea)) { Tile * tile = tile_at (tile_x, tile_y); if (tile_has_friendly_port_district (tile, this->Body.CivID)) { @@ -24605,7 +24674,6 @@ patch_Unit_heal_at_start_of_turn (Unit * this) if (is->current_config.enable_districts && is->current_config.enable_port_districts && - is->current_config.naval_units_use_port_districts_not_cities && (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea)) { Tile * tile = tile_at (this->Body.X, this->Body.Y); if (tile_has_friendly_port_district (tile, this->Body.CivID)) @@ -26179,8 +26247,11 @@ patch_City_add_building_if_done (City * this) (*p_OutputDebugStringA) (ss); City_Order defensive_order = { .OrderID = -1, .OrderType = 0 }; if (choose_defensive_unit_order (this, &defensive_order)) { - City_set_production (this, __, defensive_order.OrderType, defensive_order.OrderID, false); - return; + UnitType * def_type = &p_bic_data->UnitTypes[defensive_order.OrderID]; + if (def_type->Unit_Class == UTC_Land) { + City_set_production (this, __, defensive_order.OrderType, defensive_order.OrderID, false); + return; + } } } } @@ -27243,15 +27314,23 @@ draw_canal_on_map_or_canvas(Sprite * sprite, int tile_x, int tile_y, int dir, bo draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y); - // Add an additional draw if adjacent to water - if (dir == DIR_N && water_dirs[DIR_N]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y - y_offset); - else if (dir == DIR_NE && water_dirs[DIR_NE]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y - y_offset); - else if (dir == DIR_E && water_dirs[DIR_E]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y); - else if (dir == DIR_SE && water_dirs[DIR_SE] && ! (tile_is_water (tile_x - 2, tile_y))) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y + y_offset); - else if (dir == DIR_S && water_dirs[DIR_S]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y + y_offset); - else if (dir == DIR_SW && water_dirs[DIR_SW] && ! (tile_is_water (tile_x + 2, tile_y))) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y + y_offset); - else if (dir == DIR_W && water_dirs[DIR_W]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y); - else if (dir == DIR_NW && water_dirs[DIR_NW]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y - y_offset); + // in certain cases, add an additional draw if adjacent to water so that the canal appears to extend far enough + if (dir == DIR_N && water_dirs[DIR_N]) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y - y_offset); + else if (dir == DIR_NE && water_dirs[DIR_NE]) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y - y_offset); + else if (dir == DIR_E && water_dirs[DIR_E]) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y); + else if (dir == DIR_SE && water_dirs[DIR_SE] && ! (tile_is_water (tile_x - 2, tile_y))) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y + y_offset); + else if (dir == DIR_S && water_dirs[DIR_S]) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y + y_offset); + else if (dir == DIR_SW && water_dirs[DIR_SW] && ! (tile_is_water (tile_x + 2, tile_y))) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y + y_offset); + else if (dir == DIR_W && water_dirs[DIR_W]) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y); + else if (dir == DIR_NW && water_dirs[DIR_NW]) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y - y_offset); } void @@ -29025,6 +29104,82 @@ try_path_to_maritime_district (Unit * unit) return false; } +// Light-weight hunt for nearby friendly ports; returns true if a path/command was issued +bool +try_path_to_friendly_port_district (Unit * unit) +{ + if ((unit == NULL) || + ! is->current_config.enable_districts || + ! is->current_config.enable_port_districts) + return false; + + if (unit->Body.Container_Unit >= 0) // Don't redirect cargo + return false; + + if (unit->Body.Moves <= 0) + return false; + + if (unit->Body.Damage <= 0) + return false; + + if (p_bic_data->UnitTypes[unit->Body.UnitTypeID].Unit_Class != UTC_Sea) + return false; + + int sea_id = unit->vtable->get_sea_id (unit); + if (sea_id < 0) + return false; + + int best_x = -1, best_y = -1, best_path_len = INT_MAX; + const int search_radius = 10; + for (int dy = -search_radius; dy <= search_radius; dy++) { + for (int dx = -search_radius; dx <= search_radius; dx++) { + int tx = unit->Body.X + dx, ty = unit->Body.Y + dy; + wrap_tile_coords (&p_bic_data->Map, &tx, &ty); + + Tile * tile = tile_at (tx, ty); + if ((tile == NULL) || (tile == p_null_tile)) + continue; + + if ((short)tile->vtable->m46_Get_ContinentID (tile) != sea_id) + continue; + + if (! tile_has_friendly_port_district (tile, unit->Body.CivID)) + continue; + + int occupier_id = get_tile_occupier_id (tx, ty, -1, true); + if ((occupier_id != -1) && (occupier_id != unit->Body.CivID)) + continue; + + if (! is_below_stack_limit (tile, unit->Body.CivID, UTC_Sea)) + continue; + + int path_len = 0; + int path_result = patch_Trade_Net_set_unit_path (is->trade_net, __, unit->Body.X, unit->Body.Y, + tx, ty, unit, unit->Body.CivID, 0x81, &path_len); + if (path_result <= 0) + continue; + + if (path_len < best_path_len) { + best_path_len = path_len; + best_x = tx; + best_y = ty; + } + } + } + + if (best_x >= 0) { + patch_Trade_Net_set_unit_path (is->trade_net, __, unit->Body.X, unit->Body.Y, best_x, best_y, + unit, unit->Body.CivID, 0x81, NULL); + Unit_set_escortee (unit, __, -1); + Unit_set_state (unit, __, UnitState_Go_To); + unit->Body.path_dest_x = best_x; + unit->Body.path_dest_y = best_y; + return true; + } + + return false; +} + void __fastcall patch_Unit_ai_move_naval_power_unit (Unit * this) { @@ -29039,22 +29194,50 @@ patch_Unit_ai_move_naval_power_unit (Unit * this) } } + if ((this->Body.Damage > 0) && + ! patch_Unit_can_heal_at (this, __, this->Body.X, this->Body.Y) && + try_path_to_friendly_port_district (this)) + return; + if (try_path_to_maritime_district (this)) return; Unit_ai_move_naval_power_unit (this); } +void __fastcall +patch_Unit_ai_move_naval_transport (Unit * this) +{ + if ((this->Body.Damage > 0) && + ! patch_Unit_can_heal_at (this, __, this->Body.X, this->Body.Y) && + try_path_to_friendly_port_district (this)) + return; + + Unit_ai_move_naval_transport (this); +} + +void __fastcall +patch_Unit_ai_move_naval_missile_transport (Unit * this) +{ + if ((this->Body.Damage > 0) && + ! patch_Unit_can_heal_at (this, __, this->Body.X, this->Body.Y) && + try_path_to_friendly_port_district (this)) + return; + + Unit_ai_move_naval_missile_transport (this); +} + void __fastcall patch_Unit_ai_move_air_bombard_unit (Unit * this) { if (! (is->current_config.enable_districts && is->current_config.enable_aerodrome_districts && is->current_config.air_units_use_aerodrome_districts_not_cities)) { - Unit_ai_move_air_bombard_unit (this); - return; } + Unit_ai_move_air_bombard_unit (this); + return; + if (this->Body.Damage > 0) { Unit_set_escortee (this, __, -1); Unit_set_state (this, __, UnitState_Fortifying); @@ -29084,29 +29267,22 @@ patch_Unit_ai_move_air_bombard_unit (Unit * this) int best_base_score = 0x7fffffff; int base_x = -1, base_y = -1; - for (int y = 0; y < p_bic_data->Map.Height; y++) { - for (int x = 0; x < p_bic_data->Map.Width; x++) { - Tile * tile = tile_at (x, y); - if (! tile_has_friendly_aerodrome_district (tile, this->Body.CivID, false)) - continue; - if (! patch_Unit_is_in_rebase_range (this, __, x, y)) - continue; - if (! is_below_stack_limit (tile, this->Body.CivID, - p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class)) - continue; + FOR_AERODROMES_AROUND (this) { + if (! is_below_stack_limit (aerodrome_tile, this->Body.CivID, + p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class)) + continue; - int count = count_units_at (x, y, UF_AI_STRAT_A_VIS_TO_B, 6, -1, -1); - int x_dist = Map_get_x_dist (&p_bic_data->Map, __, x, this->Body.X); - int y_dist = Map_get_y_dist (&p_bic_data->Map, __, y, this->Body.Y); - int score = (count * 10) + ((x_dist + y_dist) >> 1); - if ((this->Body.X == x) && (this->Body.Y == y)) - score -= 20; + int count = count_units_at (aerodrome_x, aerodrome_y, UF_AI_STRAT_A_VIS_TO_B, 6, -1, -1); + int x_dist = Map_get_x_dist (&p_bic_data->Map, __, aerodrome_x, this->Body.X); + int y_dist = Map_get_y_dist (&p_bic_data->Map, __, aerodrome_y, this->Body.Y); + int score = (count * 10) + ((x_dist + y_dist) >> 1); + if ((this->Body.X == aerodrome_x) && (this->Body.Y == aerodrome_y)) + score -= 20; - if (score < best_base_score) { - best_base_score = score; - base_x = x; - base_y = y; - } + if (score < best_base_score) { + best_base_score = score; + base_x = aerodrome_x; + base_y = aerodrome_y; } } @@ -29136,6 +29312,9 @@ patch_Unit_ai_move_air_defense_unit (Unit * this) return; } + Unit_ai_move_air_defense_unit (this); + return; + Unit_set_state (this, __, 0); if (this->Body.Damage > 0) { Unit_set_escortee (this, __, -1); @@ -29145,29 +29324,22 @@ patch_Unit_ai_move_air_defense_unit (Unit * this) int best_base_score = 0x7fffffff; int base_x = -1, base_y = -1; - for (int y = 0; y < p_bic_data->Map.Height; y++) { - for (int x = 0; x < p_bic_data->Map.Width; x++) { - Tile * tile = tile_at (x, y); - if (! tile_has_friendly_aerodrome_district (tile, this->Body.CivID, false)) - continue; - if (! patch_Unit_is_in_rebase_range (this, __, x, y)) - continue; - if (! is_below_stack_limit (tile, this->Body.CivID, - p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class)) - continue; + FOR_AERODROMES_AROUND (this) { + if (! is_below_stack_limit (aerodrome_tile, this->Body.CivID, + p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class)) + continue; - int count = count_units_at (x, y, UF_AI_STRAT_A_VIS_TO_B, 7, -1, -1); - int x_dist = Map_get_x_dist (&p_bic_data->Map, __, x, this->Body.X); - int y_dist = Map_get_y_dist (&p_bic_data->Map, __, y, this->Body.Y); - int score = (count * 10) + ((x_dist + y_dist) >> 1); - if ((this->Body.X == x) && (this->Body.Y == y)) - score -= 20; + int count = count_units_at (aerodrome_x, aerodrome_y, UF_AI_STRAT_A_VIS_TO_B, 7, -1, -1); + int x_dist = Map_get_x_dist (&p_bic_data->Map, __, aerodrome_x, this->Body.X); + int y_dist = Map_get_y_dist (&p_bic_data->Map, __, aerodrome_y, this->Body.Y); + int score = (count * 10) + ((x_dist + y_dist) >> 1); + if ((this->Body.X == aerodrome_x) && (this->Body.Y == aerodrome_y)) + score -= 20; - if (score < best_base_score) { - best_base_score = score; - base_x = x; - base_y = y; - } + if (score < best_base_score) { + best_base_score = score; + base_x = aerodrome_x; + base_y = aerodrome_y; } } @@ -29191,6 +29363,9 @@ patch_Unit_ai_move_air_transport (Unit * this) return; } + Unit_ai_move_air_transport (this); + return; + if (this->Body.Damage < 1) { if (Unit_can_airdrop (this)) { int best_score = 0; @@ -29222,31 +29397,24 @@ patch_Unit_ai_move_air_transport (Unit * this) int best_score = -1; int base_x = -1, base_y = -1; - for (int y = 0; y < p_bic_data->Map.Height; y++) { - for (int x = 0; x < p_bic_data->Map.Width; x++) { - Tile * tile = tile_at (x, y); - if (! tile_has_friendly_aerodrome_district (tile, this->Body.CivID, false)) - continue; - if (! patch_Unit_is_in_rebase_range (this, __, x, y)) - continue; - if (! is_below_stack_limit (tile, this->Body.CivID, - p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class)) - continue; + FOR_AERODROMES_AROUND (this) { + if (! is_below_stack_limit (aerodrome_tile, this->Body.CivID, + p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class)) + continue; - int score = count_units_at (x, y, UF_AI_STRAT_A_VIS_TO_B, 0, -1, -1) + - count_units_at (x, y, UF_AI_STRAT_A_VIS_TO_B, 1, -1, -1) + 1; - if (count_units_at (x, y, UF_AI_STRAT_A_VIS_TO_B, 9, -1, -1) == 0) - score *= 2; - int cont_id = tile->vtable->m46_Get_ContinentID (tile); - if ((cont_id >= 0) && (cont_id < p_bic_data->Map.Continent_Count) && - (p_bic_data->Map.Continents[cont_id].Body.TileCount > 0x15)) - score *= 2; - - if (score > best_score) { - best_score = score; - base_x = x; - base_y = y; - } + int score = count_units_at (aerodrome_x, aerodrome_y, UF_AI_STRAT_A_VIS_TO_B, 0, -1, -1) + + count_units_at (aerodrome_x, aerodrome_y, UF_AI_STRAT_A_VIS_TO_B, 1, -1, -1) + 1; + if (count_units_at (aerodrome_x, aerodrome_y, UF_AI_STRAT_A_VIS_TO_B, 9, -1, -1) == 0) + score *= 2; + int cont_id = aerodrome_tile->vtable->m46_Get_ContinentID (aerodrome_tile); + if ((cont_id >= 0) && (cont_id < p_bic_data->Map.Continent_Count) && + (p_bic_data->Map.Continents[cont_id].Body.TileCount > 0x15)) + score *= 2; + + if (score > best_score) { + best_score = score; + base_x = aerodrome_x; + base_y = aerodrome_y; } } From 710d6c634ab9aaae9f1daa455e085fe63f0fb2ba Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 15 Jan 2026 16:11:10 -0800 Subject: [PATCH 189/356] Refine port direction code --- injected_code.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/injected_code.c b/injected_code.c index 1004d8cd..2c958127 100644 --- a/injected_code.c +++ b/injected_code.c @@ -26914,9 +26914,11 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, bool west_tile_is_land = tile_is_land (owner_id, tile_x - 2, tile_y, true); if (city_is_directly_above_port) { - if (northeast_tile_is_land) { *out_variant = SW; anchor = DIR_NE; } - else if (northwest_tile_is_land) { *out_variant = SE; anchor = DIR_NW; } - else { + if (northwest_tile_is_land && ! tile_is_land (owner_id, tile_x + 1, tile_y + 1, true)) { + *out_variant = SE; anchor = DIR_NW; + } else if (northeast_tile_is_land && ! tile_is_land (owner_id, tile_x - 1, tile_y + 1, true)) { + *out_variant = SW; anchor = DIR_NE; + } else { *out_variant = SW; anchor = DIR_NE; *out_pixel_x -= 4; *out_pixel_y -= 4; } From 9e2dacbda2a52ba27ed8eab854292069a4d40cd4 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 15 Jan 2026 16:46:08 -0800 Subject: [PATCH 190/356] Initial experimentation with Great Wall --- Art/Districts/1200/GreatWall.pcx | Bin 0 -> 18905 bytes C3X.h | 25 ++- Civ3Conquests.h | 1 + default.districts_config.txt | 13 ++ injected_code.c | 257 +++++++++++++++++++++++++++++++ 5 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 Art/Districts/1200/GreatWall.pcx diff --git a/Art/Districts/1200/GreatWall.pcx b/Art/Districts/1200/GreatWall.pcx new file mode 100644 index 0000000000000000000000000000000000000000..e520527c7337137c74ea7626b593dc9dce22e9e6 GIT binary patch literal 18905 zcmeHucUV?ew)dGkGxyGF}}-O{Ix| zMvW4~HH>kLpE2dj&CES_9eJKFcMj)e^uO-XCVH?&uxOh4*u^p_AiDYV2pq9^V=-{ETG>c_Rl)^ zn~v?%kKe5Fe~IKbd-=^?{+BxJ`}Mc|<)20Jo4x#hYcCl7CgOVQmcZY{UvJ&M`v?@b{FtGr?fj*Q{hls z7JS=s7#y!q_OhFdE%Ow|=H{+#`L3 zy&@h1{vAqtpXg)uqFd;5bo;u$p0=7-q8q}8f2Q=>qkBbgg5nb#kXc$7Dks2yTFFx8 zvMVPHFR`zHm2FD9g`oxfG5Qkyh`vHkfQLKi=CyKP3vJ`T-i{wBo%hIJVlRoWfSqkh zyG5|jGQNZ!pc`lrpI~O?I_oQHyVlnINa?so`jX&;C@dxEYGZa5o)BYfD1gBYA1ldw zQZhN86Q1YTH^ALJO1qA!d!S|D@D3>9m+0;oS_6D5=ng(w9@PavXJ1h$_QrUQJtw{a z{_auQbwbu+e2_(I;!N`hnjM+w?6~Svj&An8qV(LOeNJ$~#l;Vc=6@y8H8vwU-qqV! z$~w)H6P9P#x4_#5rCq~R9$AsjNpXk%&8Mx9`PB$KK6kLW7Q(eGlr#CO2d8l_z! zjP#7;mBZVZy+s}1GzQVF>3VC>g!LDco_n;r1Sd>_xDfYgHJ`|En}DK#h^ujqN3wD` zVR?%E2z)J3+9eFvH{_F3whqsS)!GJgdyZCXr_da$dgM8!?;iD2>?!dhaJ58fmk2FQ zJ$@N^W6yXtm>0Mp*5#I-#;lg-l#Y9(PYF(d&hTISkoyF`l2J}-c7DPoH-D!~E}Wq3 zVE+Y7EmGQ>n5mwUg32Xz6QI&|`#0qODy@&pp~F*c0NvfumVUyGR&VS{SJ~ zo9TTfXja!++cb%0dR)-lE~VcdtmXY;QK0^y-Y2)5mj8=69 zYY=R83EgW%51(?N*d{n5JA3+q^oO5_h@Iv>?W~;OpPo_STE`jFBO2Wybz&AXHaE05 zAt`IDpzMrhm=JEPWV)f*espt(1IZ)o5sglhYC22k8)zAGOGxUVwEHh>%y&&{Nfx(NUKa3Q8*~7?mH!m!ELx{gB`Yi5C|X z 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 F{0{<&6N>-< literal 0 HcmV?d00001 diff --git a/C3X.h b/C3X.h index 24555c59..2f46da00 100644 --- a/C3X.h +++ b/C3X.h @@ -17,7 +17,7 @@ typedef unsigned char byte; #define MAX_BUILDING_PREREQS_FOR_UNIT 10 #define COUNT_SPECIAL_DISTRICT_TYPES 10 -#define USED_SPECIAL_DISTRICT_TYPES 10 +#define USED_SPECIAL_DISTRICT_TYPES 11 #define MAX_DYNAMIC_DISTRICT_TYPES 22 #define COUNT_DISTRICT_TYPES (COUNT_SPECIAL_DISTRICT_TYPES + MAX_DYNAMIC_DISTRICT_TYPES) #define MAX_WONDER_DISTRICT_TYPES 32 @@ -618,6 +618,8 @@ struct district_config { char const * resource_prereqs[MAX_DISTRICT_DEPENDENTS]; char const * resource_prereq_on_tile; char const * dependent_improvements[MAX_DISTRICT_DEPENDENTS]; + char const * wonder_prereqs[MAX_DISTRICT_DEPENDENTS]; + char const * natural_wonder_prereqs[MAX_DISTRICT_DEPENDENTS]; char const * img_paths[10]; unsigned int buildable_square_types_mask; bool allow_multiple; @@ -631,6 +633,8 @@ struct district_config { int y_offset; int resource_prereq_count; int dependent_improvement_count; + int wonder_prereq_count; + int natural_wonder_prereq_count; int img_path_count; int max_building_index; int btn_tile_sheet_column; @@ -817,6 +821,15 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_path_count = 1, .max_building_index = 8, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 + }, + { + .command = UCV_Build_GreatWall, .name = "Great Wall", .tooltip = "Build Great Wall", .display_name = "Great Wall", + .advance_prereq = NULL, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, + .img_paths = {"GreatWall.pcx"}, .dependent_improvements = {0}, .custom_height = 112, .wonder_prereqs = {"The Great Wall"}, .wonder_prereq_count = 1, + .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Mountains)), + .img_path_count = 1, .max_building_index = 10, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, + .culture_bonus = 2, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 2, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 50, + .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 } }; @@ -828,9 +841,13 @@ struct parsed_district_definition { char * resource_prereqs[5]; char * resource_prereq_on_tile; char * dependent_improvements[5]; + char * wonder_prereqs[5]; + char * natural_wonder_prereqs[5]; char * img_paths[5]; int resource_prereq_count; int dependent_improvement_count; + int wonder_prereq_count; + int natural_wonder_prereq_count; int img_path_count; bool allow_multiple; bool vary_img_by_era; @@ -863,6 +880,8 @@ struct parsed_district_definition { bool has_advance_prereq; bool has_resource_prereqs; bool has_dependent_improvements; + bool has_wonder_prereqs; + bool has_natural_wonder_prereqs; bool has_display_name; bool has_img_paths; bool has_allow_multiple; @@ -1771,6 +1790,10 @@ struct district_button_image_set { int resource_prereq_ids[MAX_DISTRICT_DEPENDENTS]; int resource_prereq_count; int resource_prereq_on_tile_id; + int wonder_prereq_ids[MAX_DISTRICT_DEPENDENTS]; + int wonder_prereq_count; + int natural_wonder_prereq_ids[MAX_DISTRICT_DEPENDENTS]; + int natural_wonder_prereq_count; int dependent_building_count; int dependent_building_ids[MAX_DISTRICT_DEPENDENTS]; // Building types the district enables } district_infos[COUNT_DISTRICT_TYPES]; diff --git a/Civ3Conquests.h b/Civ3Conquests.h index 2244ef41..3f926549 100644 --- a/Civ3Conquests.h +++ b/Civ3Conquests.h @@ -777,6 +777,7 @@ enum Unit_Command_Values UCV_Build_EnergyGrid = -10000007, UCV_Build_Bridge = -10000008, UCV_Build_Canal = -10000009, + UCV_Build_GreatWall = -10000010, }; enum Unit_Mode_Actions diff --git a/default.districts_config.txt b/default.districts_config.txt index 31a10891..eba30c43 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -344,4 +344,17 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 +happiness_bonus = 0 + +#District +name = Great Wall +tooltip = Build Great Wall +buildable_on = desert,plains,grassland,tundra,floodplain,mountains +defense_bonus_percent = 50 +allow_multiple = 1 +culture_bonus = 2 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 2 +shield_bonus = 0 happiness_bonus = 0 \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index 2c958127..f6df8c12 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5162,6 +5162,22 @@ free_dynamic_district_config (struct district_config * cfg) cfg->resource_prereq_on_tile = NULL; } + for (int i = 0; i < ARRAY_LEN (cfg->wonder_prereqs); i++) { + if (cfg->wonder_prereqs[i] != NULL) { + free ((void *)cfg->wonder_prereqs[i]); + cfg->wonder_prereqs[i] = NULL; + } + } + cfg->wonder_prereq_count = 0; + + for (int i = 0; i < ARRAY_LEN (cfg->natural_wonder_prereqs); i++) { + if (cfg->natural_wonder_prereqs[i] != NULL) { + free ((void *)cfg->natural_wonder_prereqs[i]); + cfg->natural_wonder_prereqs[i] = NULL; + } + } + cfg->natural_wonder_prereq_count = 0; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { if (cfg->buildable_by_civs[i] != NULL) { free ((void *)cfg->buildable_by_civs[i]); @@ -5297,6 +5313,24 @@ free_special_district_override_strings (struct district_config * cfg, struct dis cfg->resource_prereq_on_tile = NULL; } + for (int i = 0; i < ARRAY_LEN (cfg->wonder_prereqs); i++) { + char const * default_value = (i < defaults->wonder_prereq_count) ? defaults->wonder_prereqs[i] : NULL; + if ((cfg->wonder_prereqs[i] != NULL) && + (cfg->wonder_prereqs[i] != default_value)) + free ((void *)cfg->wonder_prereqs[i]); + cfg->wonder_prereqs[i] = NULL; + } + cfg->wonder_prereq_count = defaults->wonder_prereq_count; + + for (int i = 0; i < ARRAY_LEN (cfg->natural_wonder_prereqs); i++) { + char const * default_value = (i < defaults->natural_wonder_prereq_count) ? defaults->natural_wonder_prereqs[i] : NULL; + if ((cfg->natural_wonder_prereqs[i] != NULL) && + (cfg->natural_wonder_prereqs[i] != default_value)) + free ((void *)cfg->natural_wonder_prereqs[i]); + cfg->natural_wonder_prereqs[i] = NULL; + } + cfg->natural_wonder_prereq_count = defaults->natural_wonder_prereq_count; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { char const * default_value = (i < defaults->buildable_by_civ_count) ? defaults->buildable_by_civs[i] : NULL; if ((cfg->buildable_by_civs[i] != NULL) && @@ -5453,6 +5487,22 @@ free_parsed_district_definition (struct parsed_district_definition * def) def->resource_prereq_on_tile = NULL; } + for (int i = 0; i < def->wonder_prereq_count; i++) { + if (def->wonder_prereqs[i] != NULL) { + free (def->wonder_prereqs[i]); + def->wonder_prereqs[i] = NULL; + } + } + def->wonder_prereq_count = 0; + + for (int i = 0; i < def->natural_wonder_prereq_count; i++) { + if (def->natural_wonder_prereqs[i] != NULL) { + free (def->natural_wonder_prereqs[i]); + def->natural_wonder_prereqs[i] = NULL; + } + } + def->natural_wonder_prereq_count = 0; + for (int i = 0; i < def->buildable_by_civ_count; i++) { if (def->buildable_by_civs[i] != NULL) { free (def->buildable_by_civs[i]); @@ -5943,6 +5993,44 @@ override_special_district_from_definition (struct parsed_district_definition * d def->resource_prereq_on_tile = NULL; } + if (def->has_wonder_prereqs) { + for (int i = 0; i < ARRAY_LEN (cfg->wonder_prereqs); i++) { + char const * default_value = (i < defaults->wonder_prereq_count) ? defaults->wonder_prereqs[i] : NULL; + if ((cfg->wonder_prereqs[i] != NULL) && + (cfg->wonder_prereqs[i] != default_value)) + free ((void *)cfg->wonder_prereqs[i]); + cfg->wonder_prereqs[i] = NULL; + } + + cfg->wonder_prereq_count = def->wonder_prereq_count; + const int max_entries = ARRAY_LEN (cfg->wonder_prereqs); + if (cfg->wonder_prereq_count > max_entries) + cfg->wonder_prereq_count = max_entries; + for (int i = 0; i < cfg->wonder_prereq_count; i++) { + cfg->wonder_prereqs[i] = def->wonder_prereqs[i]; + def->wonder_prereqs[i] = NULL; + } + } + + if (def->has_natural_wonder_prereqs) { + for (int i = 0; i < ARRAY_LEN (cfg->natural_wonder_prereqs); i++) { + char const * default_value = (i < defaults->natural_wonder_prereq_count) ? defaults->natural_wonder_prereqs[i] : NULL; + if ((cfg->natural_wonder_prereqs[i] != NULL) && + (cfg->natural_wonder_prereqs[i] != default_value)) + free ((void *)cfg->natural_wonder_prereqs[i]); + cfg->natural_wonder_prereqs[i] = NULL; + } + + cfg->natural_wonder_prereq_count = def->natural_wonder_prereq_count; + const int max_entries = ARRAY_LEN (cfg->natural_wonder_prereqs); + if (cfg->natural_wonder_prereq_count > max_entries) + cfg->natural_wonder_prereq_count = max_entries; + for (int i = 0; i < cfg->natural_wonder_prereq_count; i++) { + cfg->natural_wonder_prereqs[i] = def->natural_wonder_prereqs[i]; + def->natural_wonder_prereqs[i] = NULL; + } + } + if (def->has_buildable_by_civs) { for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { char const * default_value = (i < defaults->buildable_by_civ_count) ? defaults->buildable_by_civs[i] : NULL; @@ -6190,6 +6278,24 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i def->resource_prereq_on_tile = NULL; } + new_cfg.wonder_prereq_count = def->has_wonder_prereqs ? def->wonder_prereq_count : 0; + const int max_required_wonders = ARRAY_LEN (new_cfg.wonder_prereqs); + if (new_cfg.wonder_prereq_count > max_required_wonders) + new_cfg.wonder_prereq_count = max_required_wonders; + for (int i = 0; i < new_cfg.wonder_prereq_count; i++) { + new_cfg.wonder_prereqs[i] = def->wonder_prereqs[i]; + def->wonder_prereqs[i] = NULL; + } + + new_cfg.natural_wonder_prereq_count = def->has_natural_wonder_prereqs ? def->natural_wonder_prereq_count : 0; + const int max_required_natural_wonders = ARRAY_LEN (new_cfg.natural_wonder_prereqs); + if (new_cfg.natural_wonder_prereq_count > max_required_natural_wonders) + new_cfg.natural_wonder_prereq_count = max_required_natural_wonders; + for (int i = 0; i < new_cfg.natural_wonder_prereq_count; i++) { + new_cfg.natural_wonder_prereqs[i] = def->natural_wonder_prereqs[i]; + def->natural_wonder_prereqs[i] = NULL; + } + new_cfg.buildable_by_civ_count = def->has_buildable_by_civs ? def->buildable_by_civ_count : 0; const int max_civ_names = ARRAY_LEN (new_cfg.buildable_by_civs); if (new_cfg.buildable_by_civ_count > max_civ_names) @@ -6414,6 +6520,42 @@ handle_district_definition_key (struct parsed_district_definition * def, def->resource_prereq_on_tile = copy_trimmed_string_or_null (value, 1); def->has_resource_prereq_on_tile = true; + } else if (slice_matches_str (key, "wonder_prereqs")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->wonder_prereqs, + ARRAY_LEN (def->wonder_prereqs), + &list_count, + parse_errors, + line_number, + "wonder_prereqs")) { + def->wonder_prereq_count = list_count; + def->has_wonder_prereqs = true; + } else { + def->wonder_prereq_count = 0; + def->has_wonder_prereqs = false; + } + free (value_text); + + } else if (slice_matches_str (key, "natural_wonder_prereqs")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->natural_wonder_prereqs, + ARRAY_LEN (def->natural_wonder_prereqs), + &list_count, + parse_errors, + line_number, + "natural_wonder_prereqs")) { + def->natural_wonder_prereq_count = list_count; + def->has_natural_wonder_prereqs = true; + } else { + def->natural_wonder_prereq_count = 0; + def->has_natural_wonder_prereqs = false; + } + free (value_text); + } else if (slice_matches_str (key, "buildable_by_civs")) { char * value_text = trim_and_extract_slice (value, 0); int list_count = 0; @@ -8057,6 +8199,12 @@ void parse_building_and_tech_ids () for (int j = 0; j < MAX_DISTRICT_DEPENDENTS; j++) is->district_infos[i].resource_prereq_ids[j] = -1; is->district_infos[i].resource_prereq_on_tile_id = -1; + is->district_infos[i].wonder_prereq_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].wonder_prereq_ids); j++) + is->district_infos[i].wonder_prereq_ids[j] = -1; + is->district_infos[i].natural_wonder_prereq_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].natural_wonder_prereq_ids); j++) + is->district_infos[i].natural_wonder_prereq_ids[j] = -1; // Map advance prereqs to districts if (is->district_configs[i].advance_prereq != NULL && is->district_configs[i].advance_prereq != "") { @@ -8111,6 +8259,45 @@ void parse_building_and_tech_ids () } } + int stored_wonder_count = 0; + for (int j = 0; j < is->district_configs[i].wonder_prereq_count; j++) { + if (is->district_configs[i].wonder_prereqs[j] == "" || is->district_configs[i].wonder_prereqs[j] == NULL) + continue; + int improv_id; + struct string_slice wonder_name = { .str = (char *)is->district_configs[i].wonder_prereqs[j], .len = (int)strlen (is->district_configs[i].wonder_prereqs[j]) }; + if (find_game_object_id_by_name (GOK_BUILDING, &wonder_name, 0, &improv_id)) { + if (stored_wonder_count < ARRAY_LEN (is->district_infos[i].wonder_prereq_ids)) { + is->district_infos[i].wonder_prereq_ids[stored_wonder_count] = improv_id; + stored_wonder_count += 1; + } + stable_insert (&is->building_name_to_id, wonder_name.str, improv_id); + } else { + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": wonder_prereqs entry \"%.*s\" not found", district_name, wonder_name.len, wonder_name.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + is->district_infos[i].wonder_prereq_count = stored_wonder_count; + + int stored_natural_wonder_count = 0; + for (int j = 0; j < is->district_configs[i].natural_wonder_prereq_count; j++) { + if (is->district_configs[i].natural_wonder_prereqs[j] == "" || is->district_configs[i].natural_wonder_prereqs[j] == NULL) + continue; + int natural_wonder_id = -1; + char const * name = is->district_configs[i].natural_wonder_prereqs[j]; + if (stable_look_up (&is->natural_wonder_name_to_id, (char *)name, &natural_wonder_id)) { + if (stored_natural_wonder_count < ARRAY_LEN (is->district_infos[i].natural_wonder_prereq_ids)) { + is->district_infos[i].natural_wonder_prereq_ids[stored_natural_wonder_count] = natural_wonder_id; + stored_natural_wonder_count += 1; + } + } else { + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": natural_wonder_prereqs entry \"%s\" not found", district_name, name); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + is->district_infos[i].natural_wonder_prereq_count = stored_natural_wonder_count; + // Resolve generated resource name to ID if (is->district_configs[i].generated_resource != NULL && is->district_configs[i].generated_resource != "") { int res_id; @@ -9026,6 +9213,12 @@ reset_district_state (bool reset_tile_map) for (int j = 0; j < ARRAY_LEN (is->district_infos[i].resource_prereq_ids); j++) is->district_infos[i].resource_prereq_ids[j] = -1; is->district_infos[i].resource_prereq_on_tile_id = -1; + is->district_infos[i].wonder_prereq_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].wonder_prereq_ids); j++) + is->district_infos[i].wonder_prereq_ids[j] = -1; + is->district_infos[i].natural_wonder_prereq_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].natural_wonder_prereq_ids); j++) + is->district_infos[i].natural_wonder_prereq_ids[j] = -1; is->district_infos[i].dependent_building_count = 0; for (int j = 0; j < ARRAY_LEN (is->district_infos[i].dependent_building_ids); j++) is->district_infos[i].dependent_building_ids[j] = -1; @@ -9878,6 +10071,64 @@ city_can_build_district (City * city, int district_id) return true; } +bool +leader_has_wonder_prereq (Leader * leader, struct district_infos const * info) +{ + if (info == NULL) + return false; + if (info->wonder_prereq_count <= 0) + return true; + if ((leader == NULL) || (leader->Improvement_Counts == NULL)) + return false; + + for (int i = 0; i < info->wonder_prereq_count; i++) { + int improv_id = info->wonder_prereq_ids[i]; + if (improv_id >= 0 && leader->Improvement_Counts[improv_id] > 0) + return true; + } + + return false; +} + +bool +leader_has_natural_wonder_prereq_in_territory (int civ_id, struct district_infos const * info) +{ + if (info == NULL) + return false; + if (info->natural_wonder_prereq_count <= 0) + return true; + if (! is->current_config.enable_natural_wonders) + return false; + + FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { + struct district_instance * inst = (struct district_instance *)(long)tei.value; + if ((inst == NULL) || (inst->district_type != NATURAL_WONDER_DISTRICT_ID)) + continue; + + int wonder_id = inst->natural_wonder_info.natural_wonder_id; + if (wonder_id < 0) + continue; + + bool matches = false; + for (int i = 0; i < info->natural_wonder_prereq_count; i++) { + if (info->natural_wonder_prereq_ids[i] == wonder_id) { + matches = true; + break; + } + } + if (! matches) + continue; + + Tile * tile = (Tile *)tei.key; + if ((tile == NULL) || (tile == p_null_tile)) + continue; + + return tile->vtable->m38_Get_Territory_OwnerID (tile) == civ_id; + } + + return false; +} + bool leader_can_build_district (Leader * leader, int district_id) { @@ -9891,6 +10142,12 @@ leader_can_build_district (Leader * leader, int district_id) if ((prereq_id >= 0) && ! Leader_has_tech (leader, __, prereq_id)) return false; + if (! leader_has_wonder_prereq (leader, info)) + return false; + + if (! leader_has_natural_wonder_prereq_in_territory (leader->ID, info)) + return false; + if (cfg->has_buildable_by_civs) { bool civ_match = false; if (cfg->buildable_by_civ_count > 0) { From af9c437db1beec2cea377536568b4c2e0d3cd2b5 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 15 Jan 2026 16:53:22 -0800 Subject: [PATCH 191/356] Touch up Great Wall art --- Art/Districts/1200/GreatWall.pcx | Bin 18905 -> 19250 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/GreatWall.pcx b/Art/Districts/1200/GreatWall.pcx index e520527c7337137c74ea7626b593dc9dce22e9e6..49cbca13eaf2c28c475c35944de9ec9cb08715a4 100644 GIT binary patch delta 5124 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 delta 4963 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 Date: Thu, 15 Jan 2026 17:01:08 -0800 Subject: [PATCH 192/356] Add obsoleted_by flag and logic --- C3X.h | 6 +++- default.districts_config.txt | 1 + injected_code.c | 53 ++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/C3X.h b/C3X.h index 2f46da00..a04d89b8 100644 --- a/C3X.h +++ b/C3X.h @@ -615,6 +615,7 @@ struct district_config { char const * display_name; char const * tooltip; char const * advance_prereq; + char const * obsoleted_by; char const * resource_prereqs[MAX_DISTRICT_DEPENDENTS]; char const * resource_prereq_on_tile; char const * dependent_improvements[MAX_DISTRICT_DEPENDENTS]; @@ -824,7 +825,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_GreatWall, .name = "Great Wall", .tooltip = "Build Great Wall", .display_name = "Great Wall", - .advance_prereq = NULL, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, + .advance_prereq = NULL, .obsoleted_by = "Metallurgy", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"GreatWall.pcx"}, .dependent_improvements = {0}, .custom_height = 112, .wonder_prereqs = {"The Great Wall"}, .wonder_prereq_count = 1, .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Mountains)), .img_path_count = 1, .max_building_index = 10, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, @@ -838,6 +839,7 @@ struct parsed_district_definition { char * display_name; char * tooltip; char * advance_prereq; + char * obsoleted_by; char * resource_prereqs[5]; char * resource_prereq_on_tile; char * dependent_improvements[5]; @@ -878,6 +880,7 @@ struct parsed_district_definition { bool has_name; bool has_tooltip; bool has_advance_prereq; + bool has_obsoleted_by; bool has_resource_prereqs; bool has_dependent_improvements; bool has_wonder_prereqs; @@ -1787,6 +1790,7 @@ struct district_button_image_set { struct district_infos { int advance_prereq_id; // Tech ID that enables the district + int obsoleted_by_id; int resource_prereq_ids[MAX_DISTRICT_DEPENDENTS]; int resource_prereq_count; int resource_prereq_on_tile_id; diff --git a/default.districts_config.txt b/default.districts_config.txt index eba30c43..eee80bdc 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -349,6 +349,7 @@ happiness_bonus = 0 #District name = Great Wall tooltip = Build Great Wall +obsoleted_by = Metallurgy buildable_on = desert,plains,grassland,tundra,floodplain,mountains defense_bonus_percent = 50 allow_multiple = 1 diff --git a/injected_code.c b/injected_code.c index f6df8c12..9fc126ca 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5149,6 +5149,10 @@ free_dynamic_district_config (struct district_config * cfg) free ((void *)cfg->advance_prereq); cfg->advance_prereq = NULL; } + if (cfg->obsoleted_by != NULL) { + free ((void *)cfg->obsoleted_by); + cfg->obsoleted_by = NULL; + } for (int i = 0; i < 5; i++) { if (cfg->resource_prereqs[i] != NULL) { @@ -5298,6 +5302,10 @@ free_special_district_override_strings (struct district_config * cfg, struct dis free ((void *)cfg->advance_prereq); cfg->advance_prereq = NULL; } + if ((cfg->obsoleted_by != NULL) && (cfg->obsoleted_by != defaults->obsoleted_by)) { + free ((void *)cfg->obsoleted_by); + cfg->obsoleted_by = NULL; + } for (int i = 0; i < ARRAY_LEN (cfg->resource_prereqs); i++) { char const * default_value = (i < defaults->resource_prereq_count) ? defaults->resource_prereqs[i] : NULL; @@ -5473,6 +5481,10 @@ free_parsed_district_definition (struct parsed_district_definition * def) free (def->advance_prereq); def->advance_prereq = NULL; } + if (def->obsoleted_by != NULL) { + free (def->obsoleted_by); + def->obsoleted_by = NULL; + } for (int i = 0; i < def->resource_prereq_count; i++) { if (def->resource_prereqs[i] != NULL) { @@ -5967,6 +5979,13 @@ override_special_district_from_definition (struct parsed_district_definition * d def->advance_prereq = NULL; } + if (def->has_obsoleted_by) { + if ((cfg->obsoleted_by != NULL) && (cfg->obsoleted_by != defaults->obsoleted_by)) + free ((void *)cfg->obsoleted_by); + cfg->obsoleted_by = def->obsoleted_by; + def->obsoleted_by = NULL; + } + if (def->has_resource_prereqs) { for (int i = 0; i < ARRAY_LEN (cfg->resource_prereqs); i++) { char const * default_value = (i < defaults->resource_prereq_count) ? defaults->resource_prereqs[i] : NULL; @@ -6264,6 +6283,11 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i def->advance_prereq = NULL; } + if (def->has_obsoleted_by) { + new_cfg.obsoleted_by = def->obsoleted_by; + def->obsoleted_by = NULL; + } + new_cfg.resource_prereq_count = def->has_resource_prereqs ? def->resource_prereq_count : 0; const int max_resource_entries = ARRAY_LEN (new_cfg.resource_prereqs); if (new_cfg.resource_prereq_count > max_resource_entries) @@ -6494,6 +6518,14 @@ handle_district_definition_key (struct parsed_district_definition * def, def->advance_prereq = copy_trimmed_string_or_null (value, 1); def->has_advance_prereq = true; + } else if (slice_matches_str (key, "obsoleted_by")) { + if (def->obsoleted_by != NULL) { + free (def->obsoleted_by); + def->obsoleted_by = NULL; + } + def->obsoleted_by = copy_trimmed_string_or_null (value, 1); + def->has_obsoleted_by = true; + } else if (slice_matches_str (key, "resource_prereqs")) { char * value_text = trim_and_extract_slice (value, 0); int list_count = 0; @@ -8195,6 +8227,7 @@ void parse_building_and_tech_ids () if (is->district_configs[i].command != 0) itable_insert (&is->command_id_to_district_id, is->district_configs[i].command, i); is->district_infos[i].advance_prereq_id = -1; + is->district_infos[i].obsoleted_by_id = -1; is->district_infos[i].resource_prereq_count = 0; for (int j = 0; j < MAX_DISTRICT_DEPENDENTS; j++) is->district_infos[i].resource_prereq_ids[j] = -1; @@ -8223,6 +8256,22 @@ void parse_building_and_tech_ids () } } + // Map obsoleted_by to tech ID + if (is->district_configs[i].obsoleted_by != NULL && is->district_configs[i].obsoleted_by != "") { + int tech_id; + struct string_slice tech_name = { .str = (char *)is->district_configs[i].obsoleted_by, .len = (int)strlen (is->district_configs[i].obsoleted_by) }; + if (find_game_object_id_by_name (GOK_TECHNOLOGY, &tech_name, 0, &tech_id)) { + snprintf (ss, sizeof ss, "Found tech obsoleted_by \"%.*s\" for district \"%s\", ID %d\n", tech_name.len, tech_name.str, is->district_configs[i].obsoleted_by, tech_id); + (*p_OutputDebugStringA) (ss); + is->district_infos[i].obsoleted_by_id = tech_id; + } else { + is->district_infos[i].obsoleted_by_id = -1; + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": obsoleted_by \"%.*s\" not found", district_name, tech_name.len, tech_name.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + // Map resource prereqs to districts (multiple resources now supported) int stored_res_count = 0; for (int j = 0; j < is->district_configs[i].resource_prereq_count; j++) { @@ -9209,6 +9258,7 @@ reset_district_state (bool reset_tile_map) for (int i = 0; i < COUNT_DISTRICT_TYPES; i++) { is->district_infos[i].advance_prereq_id = -1; + is->district_infos[i].obsoleted_by_id = -1; is->district_infos[i].resource_prereq_count = 0; for (int j = 0; j < ARRAY_LEN (is->district_infos[i].resource_prereq_ids); j++) is->district_infos[i].resource_prereq_ids[j] = -1; @@ -10141,6 +10191,9 @@ leader_can_build_district (Leader * leader, int district_id) int prereq_id = info->advance_prereq_id; if ((prereq_id >= 0) && ! Leader_has_tech (leader, __, prereq_id)) return false; + int obsolete_id = info->obsoleted_by_id; + if ((obsolete_id >= 0) && Leader_has_tech (leader, __, obsolete_id)) + return false; if (! leader_has_wonder_prereq (leader, info)) return false; From 7b41a9339504bd2c5f9a0275ec97c8ff231bddfd Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 15 Jan 2026 17:08:13 -0800 Subject: [PATCH 193/356] Add flags and basic logic for Great Wall impl --- C3X.h | 2 ++ default.c3x_config.ini | 3 +++ injected_code.c | 15 +++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/C3X.h b/C3X.h index a04d89b8..a7248876 100644 --- a/C3X.h +++ b/C3X.h @@ -349,6 +349,7 @@ struct c3x_config { bool enable_bridge_districts; bool enable_canal_districts; bool enable_central_rail_hub_districts; + bool enable_great_wall_districts; bool cities_with_mutual_district_receive_buildings; bool cities_with_mutual_district_receive_wonders; @@ -382,6 +383,7 @@ struct c3x_config { bool allow_enter_canal_from_any_direction; bool ai_defends_districts; + bool great_wall_districts_impassible_by_others; bool enable_city_work_radii_highlights; }; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index c8993c2c..ab2bca36 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -839,6 +839,7 @@ enable_port_districts = false enable_bridge_districts = false enable_canal_districts = false enable_central_rail_hub_districts = false +enable_great_wall_districts = false ; When multiple cities share a district (i.e., the same district tile is within multiple cities' work radii), these options control whether those ; cities automatically share the benefits of buildings and wonders constructed in that district. For example, if Rome and Veii both have the same @@ -915,6 +916,8 @@ max_contiguous_canal_districts = 5 ; infrastructure. Only applies when enable_districts is set to true. ai_defends_districts = true +great_wall_districts_impassible_by_others = true + ; When enabled, holding down the Control key while a worker is selected will highlight all tiles within the work radii of nearby cities. City centers ; are highlighted more brightly, while tiles that fall within multiple cities' work radii are highlighted more intensely. This visual aid helps you ; strategically place districts and improvements by showing which cities will be affected. The highlights update dynamically as you move the worker diff --git a/injected_code.c b/injected_code.c index 9fc126ca..c97a10da 100644 --- a/injected_code.c +++ b/injected_code.c @@ -76,6 +76,7 @@ struct injected_state * is = ADDR_INJECTED_STATE; #define ENERGY_GRID_DISTRICT_ID 7 #define BRIDGE_DISTRICT_ID 8 #define CANAL_DISTRICT_ID 9 +#define GREAT_WALL_DISTRICT_ID 10 char const * const hotseat_replay_save_path = "Saves\\Auto\\ai-move-replay-before-interturn.SAV"; char const * const hotseat_resume_save_path = "Saves\\Auto\\ai-move-replay-resume.SAV"; @@ -9589,6 +9590,7 @@ can_build_district_on_tile (Tile * tile, int district_id, int civ_id) if ((cfg->command == UCV_Build_Port) && !is->current_config.enable_port_districts) return false; if ((cfg->command == UCV_Build_Bridge) && !is->current_config.enable_bridge_districts) return false; if ((cfg->command == UCV_Build_CentralRailHub) && !is->current_config.enable_central_rail_hub_districts) return false; + if ((cfg->command == UCV_Build_GreatWall) && !is->current_config.enable_great_wall_districts) return false; if (! district_is_buildable_on_square_type (cfg, tile)) return false; @@ -13653,6 +13655,7 @@ patch_init_floating_point () {"enable_bridge_districts" , false, offsetof (struct c3x_config, enable_bridge_districts)}, {"enable_canal_districts" , false, offsetof (struct c3x_config, enable_canal_districts)}, {"enable_central_rail_hub_districts" , false, offsetof (struct c3x_config, enable_central_rail_hub_districts)}, + {"enable_great_wall_districts" , false, offsetof (struct c3x_config, enable_great_wall_districts)}, {"completed_wonder_districts_can_be_destroyed" , false, offsetof (struct c3x_config, completed_wonder_districts_can_be_destroyed)}, {"destroyed_wonders_can_be_built_again" , false, offsetof (struct c3x_config, destroyed_wonders_can_be_built_again)}, {"cities_with_mutual_district_receive_buildings" , false, offsetof (struct c3x_config, cities_with_mutual_district_receive_buildings)}, @@ -13664,6 +13667,7 @@ patch_init_floating_point () {"naval_units_use_port_districts_not_cities" , false, offsetof (struct c3x_config, naval_units_use_port_districts_not_cities)}, {"show_natural_wonder_name_on_map" , false, offsetof (struct c3x_config, show_natural_wonder_name_on_map)}, {"ai_defends_districts" , false, offsetof (struct c3x_config, ai_defends_districts)}, + {"great_wall_districts_impassible_by_others" , false, offsetof (struct c3x_config, great_wall_districts_impassible_by_others)}, {"expand_water_tile_checks_to_city_work_area" , false, offsetof (struct c3x_config, expand_water_tile_checks_to_city_work_area)}, {"workers_can_enter_coast" , false, offsetof (struct c3x_config, workers_can_enter_coast)}, {"allow_enter_bridge_from_any_direction" , false, offsetof (struct c3x_config, allow_enter_bridge_from_any_direction)}, @@ -27819,6 +27823,12 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren draw_canal_on_map_or_canvas(dir_sprites[draw_dir2], tile_x, tile_y, draw_dir2, water_dirs, map_renderer, draw_x, draw_y); } +void +draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) +{ + // TODO +} + void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { @@ -27985,6 +27995,11 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis draw_canal_district (tile, tile_x, tile_y, map_renderer, pixel_x, pixel_y, era); return; } + case GREAT_WALL_DISTRICT_ID: + { + draw_great_wall_district (tile, tile_x, tile_y, map_renderer, pixel_x, pixel_y); + return; + } default: { struct district_infos * info = &is->district_infos[district_id]; From 1c7c5bd32d550a9025a7c2581fca491d60ffbcb4 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 15 Jan 2026 17:24:43 -0800 Subject: [PATCH 194/356] Added flag & logic for disabling great wall vanilla defense bonus effect --- Art/Districts/1200/GreatWall.pcx | Bin 19250 -> 18930 bytes C3X.h | 2 ++ civ_prog_objects.csv | 11 ++++++----- default.c3x_config.ini | 1 + injected_code.c | 11 +++++++++++ 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Art/Districts/1200/GreatWall.pcx b/Art/Districts/1200/GreatWall.pcx index 49cbca13eaf2c28c475c35944de9ec9cb08715a4..86a245e83b9da33b3397e52521e47233cd6833ae 100644 GIT binary patch literal 18930 zcmeHOd0bUTvOcrDH- zK|r7nn+l2uxS=31#u#H%h;fY1=~w4oZ{VDJ?lt*;%rEV=PoL_l>gwwHs_T64r{DdV zfIs*T76kEo``=N&f772o5P$sbFD=Y~-}C1h#)Rnqt`YAcfq}+*{`_Yax`O_rF}ltD z$HoNg<3GD{kxB&j3&L`it>ue`aDNU9cr#+NX-yv8*f23H;L;HOOgdyg3pqxY2AV}b zp#MW}PB9+f%K}rYp-FTfMn65mod2@G{A=?Iii)*aXwtggVokm6P3Qr-*a?zZ;XF^nU)BYF z+^9hJ&=WLQNgKw(*T0BdSM6U`30>?-L4D{7x|{hv4*!KUr9S5_*|8(0F;QooS!SYj z{e&C*ThNT}yTt>oUCeDbzaf=2h=s$Sh&s;-blvgYI-8hYesj_GxR-r^2^#@LlcKrC+$Lmw6QFV{z&9RmY@N26V!Q~ zyUwj0zANMOn%>9ik9gMxi|C9qDcsfe_LBRn-HJmjv(dWdaFl}+nnusjJZ!NEboG~2 zFNm=JL?!efZ8QtJbA*eLHM)dup&N*7Z{{!kNhi~P%DFqekLNkOR$MxxBqNISH< zb|4bi=bFQomNR=x+Opoq^fSC{gBoc~t~4_a4Ugek#sB#1mbVW&z%nhZ>nT3piF(mv zu#9JDBuvc<4S~){_@8GUMd)kvpd9|uC*T#Cp5Q4#Ha3CQXVI-zUlo_8^JSe}J4r@k z=<4Z?y7yZ4Q@q#GDy1+xO!YI3&=7}UuEAKv9ya#z-a%uUr#@QO6Wm;r3Rd$7&7nKw zFdD4OD(wQnX1PWK3j%!A!~Vb7MthA%iFO`8A!-YgK;Y|O?Ss_~EroH(g{9~c*L*-Z zBpS);K>bzkTaqVuucLWdN>+@EtG}!9ZqhD+Ye`YT$DATg_^>cR>w1hk!0_kLtLAL* z!y{-EM7xJ3I%~m+4Mb$Vv#P-wM8<(^ll8QhS%i8_oCD5h(Kpp89b{V%>g_2_tEzQB z&b6=RjB|cyxaaKqM0$+(9W1O4+7l7%9}#lU!PLc1&Wvkjhs@lSP6s=&F+p;V3wk?4 zputvHsqaC+*%pLO6{2BSnv39K?gqM^;neVrF1Q#?l%sLf7l;Wu35r#24<@+>485Jf zO{HLTx4>id7dJ&DHPuzsp%G831eyGI#^{=adehKAgBxZg0FtmyI70+^XXa!bck)u+ z>FMU=oT$8E@85j6Cc4EW((3tF<9Fp@OHh89q4vr zNr?E`ByX^_U#xZ#e9CyKKO)=M>AWT0;z3Zv8%8^YJ_8CNq~9(h`wp?GYA9|rwo1&%=PNA*fZNIs!mjQB-bInwO?v0F7tpP z!|bHq5N|LQguP<4H!;}Q12kE5K-0CMv$+9X?hXn(x~U*PvY;$7o3{rVN>UPSTHDwO zyus`f-XxMsG7fB7rF7^>fP2i5?Ha}(IPEua_fU7>?h8*hbJO)!@o%VrBWvk z_dxFrbhV?!``sRj4^68%Nw&Xckl+!0jVbxkPbI{&%XTI?C)sm<$X@3FU(b}7rGNTx zpYGB0j07@RfxDk}86I#m4h|D0#4Ad7Bj}2^%Tf62Zdb_wsO}kYoz{*L*0!y{^d->Q zZTNcz^@A&%Mq}j`85M~#p74)rpu=8J>TN2@dAHZ<+go5$_BOV?V$kEw@QQc^8fG_blG5EvVv}BUl$|NNgksA*qJ8|2ghl$g_=Gxsyu;G;Z_?hr9!~Cu zxBGJU;~pOu4|f|6VY0lW6nPKSa~Bjdn&)n)>$D>o^!2D8RQZIsL2HMJn5gQC$n#)_ zGiVChp`MsnZ`Fb>^ajlYSCrH@o=C0-+nYeVoqE5EI!#Obl3p?h@kqZUUQ)J5cEdsp zoKJStQZiLJ+=xc|L6=YP4O%;lLtHY`T??BL+0#^8=ND+NkX8krbI>ItF)PP6ETg59 zyM@s}X;){OQ_M@o`#g#-F(q$MNGKU(tFZcW=_UF;G3`>?M0JF8bgvmBf3}D#0ePC8oU}a?KXSWU^vqz(Rfp2VqOi{`}N{(^4&(i(OOws zdNyO8L5fFwo|wl21iD&v0S&ZWC`&)?D8F%CD(dUFK&kiTK@)OHjCQh-q3&5_O9L;G5m3zl9trQK*mK zf8`X+wdkd*;!B+a;~Plz76#r zYEI|+i|n+N!l3Xu1`!_F7nssU)^RexIUq2?!qL*zV#x>V?A%hlUDhfC zq2?}hsWrO>_QgBL(%n!CtFV|L|MxTE83aU|bW&=oI?<(SvZ%GYCOto`um@aCUm@zH zj=mZE)uQ%3QL;Gt?`f=yJ+U@)M{5td+!dKrdxG@MJXe)d+*lf$-NSP-Rrz_a3X5s* ze?P;NLc~_s26==WcXzT62tHu6&&(k*B-Hu@n-VnmfFaD?&(Qe4%|7u9I(b;z)O&}D zl8K9#g^j!2PWx!CyK%O0fJNX-xmcagh^NFwtYt&$wNt?Y8tg)SbzLo>$9rh97EE{y zqKTi-4-|HrBEDJF-p6QM5Od5Uv~hjuChZDvvQJ=Lt~6xTA@V|eb4_86M_x-cxS=uh z&il;K=<#VkC7$Aogu1o1YI#k2ZDCbrNq##T>`bd}f%9b+-KG*mQ}~-j?R}z)4pycc zBvzkYr<6{P_OzxS&aA9BRFIu|%KB7Ec3b;9Cp1T+z^C>UQ`%S~FZNlWAGmN&D;JxC z;Q_|(KGC^n*)$NcfNwJV|M-FDUp}%p5gT^UO3%*CUD49f+E`56&K95#&l3rg_X)(d zW(I0<`u4?Wthw|Y81zLr0%nVvk5;yTGrvOxz&DBS7PT|DKAIg9^=y(V>qJ-D8^B+m zZ3nx(1T+&Wy_+*qz^jy@QIhM1=4j;jq@NH^AP6?pR*_TNliHn^nA%hiXV9g%EVtBL z*guF0lW*eh7PT`(jEewvs*zfrUfo%eU0)uTQ(o7Y8Ia*r9DBH=DzPnx>xJfMB>3c> zU`i1(D_49bzR@nq(bzn|JK>nN{T2(Gj3aCs*eXH8RzBpP_j(4|TKl@_Sh{(;7;~(o zz4Vo=osGiwa=nl+i60Xq*mk>|mad+@O9U9_GXR+{!2Sms+Q8tboGs)sMnL!D#1D(w zXu?U(6`;42z7&zpDxIc0pE5RGBa_98?r8Rl~uMxC7%KJGe;xEC;OOqj7Nw) z`tllDq_%@!4}{GVXtJp`1Hu>ZajoY@K=I@Fheho)an{b03E862cuhqWM31GBrxUzt z8>-TMJqnJO6`oAZbq|jQ|1(FU!Ke2aQ>x%dEnUA>YPF(vOiFyRx7!}au#_}^H#YhA zuUSV&b;=2W2lv|_a@5&vAi3Y`z>bZwP8!yGGz~1)t7w7;5+?K`Kpc8I^i;R$=xMIe zrmTOk6s&FvO_mjL5$yy8a7Kx_MeQ`M>TVW_03l%i0b z37&!G!yZx1rP1kgGe*+G~{3>namkVH5SHoI?FwofPO9#dC|= zX(Fq(2o5a)oLGY$WrT+HmK2MjO2ZIk88Ab+$vgyF|fFl!0dYuaM^UyT3(l~fP*Zxasc_~Rt(#BX? zLVBMuM^l*04~c7dr;)z7fw`6vM?Rn#UF!z7F#*BMbr6ig)>o-Z+hO9TMePi>g?L9K z)Pz?r0-0;5_dJA2nO((x>4n~jRi(u_U5U|I`KUK;jz*GC{vq)YUnBH)>6_{pYiv`H zcP(fwZMuLadeF7zmL}?ca2WrzsGT8ntlw;0FL2?Txf{h|7>#Y8*2(at?Ms<$P{dP`Sk^)JuWX zA>!vn?JO1zR7)GkYBbOV&L}xLFCeF~zN77Q)|nnS;hXd3Xq5O=9}o`!Q|>iXGgvOI zsiDjkyk&P?{4EA6-$_ zTylDjMu1Q30j5--Dz#GPuVNb{)`+hO+YuF;7-q03Fv^WhbT$NQY-46}z|z`IU1rzX zot6&!jJH{9yLg5=?+_;KEHQy~loi!J{`5-&ZG!-CD7~G9q1NC#uAxz9)IR~*?j!!a zsC|G%Aldp1)LYwA6MF#-wsdCal+>1FMxQ<37Il1%MuSgpmYBs8gr*Wl^`A>+^fdH6 z(B*Tbr(AUr>bzjqJONtn!~b5?J|KiZ+05wbthy}klBTnz$Kz9SQ;v8imlXSk%+cQF zQ<}w;)RJPWr2g{v73-y>wd0-kZI~&Y=?deCJVVR?swXv+6*q6(tgN&}HJ;j0V`o9nbzRFg z++eKDLqZ6atxGA4JLn&d2HNw|LW0BXb1E{9MS~Mznh0-sXD}r-Wy#BzE*D!Rwqp0r z)F_+JV;v*^_@SV{Y+%;Qc(1k+seIILw~;!jw%*p>+*nIjxRCdVTX>^^w*J;#`r7jA zIT0Y^EE=n^LgUTo@=?@xi?Qtgh+rQE4$JTN~>eiUBe)H$QLh z-XreeTZA+iu)Kkm+U9k#9)O)LE9@x7_2e6Sa(WW3nVydpWXPcs?f|{N?oH0~fG1X#CPN3Ol)O&|u>{M1Y<|wMlDe2l@pbpW2Vx11i@tm!^ z!#Fn|6RgNHucER%v$>@;@MwNsNm_UWSF7}6oOk9?@D ze}o1^+jb;>T;ZQ?H8qr6pDNn2kxCNNXQ67mL1)hm38&!>i{eb)#S%zP^(Fg(s(_$iQD)tu}e`- zbE~?Js=Dm<0>~dia5vaZ)?GNCbC(ejJ|PerkSr>VP9<}T^K*-vb54eyr0kR)oAP>Y zV@k|Tt3H?e+${_!R#p_`XM{Aym6e|iitT5-w}3xx z5;q~0q@u{#1u+^&Nq%pAE7^bsd)mrU&LK3-060&H3KmdECd9_ax)es_Wrn*3^)p^s z09-dQCE-em719uqtz5ortGAbr>(Q_PCliktLBSkYfS{6^j-iQ+y@HLg{7P9-GEEZq zv5tbej<}4L?tu+^>%lt4>b=9u;qPe($olUykn1xn9PgpQ`jn!~j@Gp3z}kz9_ZDFE zBr%Ea6I$}h>JV~i+pRatt>`Fl_DX}?OHcKE21Xu9R-SB$*N3oHVxkzh&fr;D<)(nlDtEu?_PiE&7#X>ydb zmaW*NsIIy__IOc2j3??%Mnn1TUolclF9=p#R(84~KRhk^P}~S({6f-e98+RRkgHaI z_|eL~RTBEz4i;*1QF=jp)(gtSg2HT^jC}SgnW&g)ijrlF_!=8++p4Xmy>&fj+fJ{u zkih)5%D5$^yEyM_M$&Mey2=uQuh6BA=5oJ`xND4e7Sfkv#2EgXFyzRqDXDK+C##^l zA8t%1bJYt%%HofI&Bztb6KpW5xx667nGC+h7`%{P9K)16Ur30Fuaa2u@jsWW+G6Q> z)N+aQ7BfvPL21}fQ>2HT(VAVdqQn@5C9_pl)lpMW*K<(PcYv7kZhP@g$Q*oy=3BDA zVI;g?Vs=>6l@^qrH^CUZkaQmJ%(vWo6AxelV z#JAX3X`80JhPu4b8V;nVy3kiGh7i=;N7J3{-!iUdUSW1fb2$F^EymD=i=ivT75pu+ zlT=iZS5cPLUM=HaSA<3@)gW6ig!+myzhzuHy&~A~i(f>@EylowtFJ4V5=2H~<%(tE ztCp&M$%&7xeF#)Sr1>=2ZJ4XsEjazsfm3|`bkSy5R@j+D35H-nhuA%uS4)){m+ zmcjXY?K{Rj;cF}mmE2{FT)3kgCWhfu*uG7HRFsvI->thH4(k3EG*PNvlXI%E`8&qt z=WBu;fZSz_TeyE6#*`RRtCufZwdC_<xQODV1-!pF5-w*=$VU{su;hO&vrWoHSxq6k9*ry+@*}TEq!@|YzQ(wD~ zeY*si76c2TSRR5HMu($hsBf;NrpvLYLl>z_i?7k69)JpHGV=#U&BI$PfCwHkhAh-t z3=u;RjcF-xbhV8&NL9Jrx%GKX5bBMW=br0{{ee-p@s?l){|^}>7D`8kFva+4@#W-N zSyFtd*q8e}&E4aYoXrnJ=nJyUjPyluJU~3hnu-b>h^N#Ib{L-OK##~aG}KZDY4#iG z&lwdr7&Aio#{vWRMLGj;U-_I+=WLaMut`-@M=P`xjTYorXQfj2htba&M6dg`m?r|{Ql^Uun$gedmMi<3yAD|0OMP&s@HR-5w zEH41)8AdldAw-{nAGgp*?i`~Q3o6a<;$Zx#z;k}}Rv*y^cuAe3BnOEkO-0$=m1wx3 zv)}^itM6`&pJUXJLB$!dIP!if@SI|0qzN*H0`&%`XI=^2_oN24=sMT)Bc2`~0t; z{Bzls17-DHgmQauU86%Q*i)YZLh9CSA>fHOC&rm zTKX^(9c6hLg>Cxl6n*L{8uL;T8-)nZ4|(TBi@sfE&1zDN6qo$RXItI&A9OS`@)IID zKfV>gVILHhpdZ||v{g7N`d@B#Jzouu;4;V!S3Om12K6qCofm2O^Tglm)ijh8l-0kG zaVSmC&u&U=Y(JUGX7=2OHF#d6!BZ^CNv#HvSAP2OmwS!(SqB*Tg&3N!@y82KMX@&k zBHLK#s;TI!sm8&r)_qE1boFd!(+dILMunjB0=3E8%$yhKUF_lJvGgX%HIge=FW-JJW}h_~pygyG$Pg7yiehL8g!A2@v-6#3XfVIK zCAI50;{1?i{SJmIXXrOOklF}^ZCqU<^2ta?lMMKB8;>F3ux=B(QQu@1=N z3D9h-gCHA{ilW%K1j5M5%WCAKn@tGS_MCezG&Z>o2_|~&9u=*?;%wfs?KJAmug$GW zdoDBpxegg4dd(pflFZ|e{{D%W^&t~y6FdFiHuW$#X+>H2n8u2htmi_*O?qJ*6^2p) z=p3Q2WwU>NT0z8fp>ZU=nvaSvsmO8;|L@;F(y-WRCHvdvyaI3PAE^#{E)+n}>&qx$ zK|zW+LL(q&|8t?3;0~yjUK&TCggN|~rj^8Rn`;!7msxBjs)xdc3m|U#SNx`co+ygb z0#8SYG3cY$9`Wmj*!wAfzW~MAYJZqdDY(u~OV~_fP!zF{is4t$i0YxzH48}a#tMpO@#Ok<}dY^F&l;aNzZ@{2C+V)XW< z$Rz7R9+Y1va+mnSOOeUa-#*Q4V2v$hEh#h=2Pm@ zgjuG*uPI>fr|vzNRD@CIU(DQtH|Uq({5x#+e(HXV{Vt1t?aq8k-Epzg5;oI4sE}m8 z{p4T!GM`enqU<-9{Of7v(;2wYWKt1Eo!37@%rY)lnKbEhQ5WK(Dbk-(SJ`w5@1y^K zaUIFjJbgu|atL7>XX~eG80Zwz!j1#BeyReJ|E{RRhd<^C6z~3;Ba}aeDy!%8&`R_7=7qeDFU)CA{!1s~f{b(g}CC;ch)l^j(f zDja6gpHd&Y(J6?Y>ov>AkUs$!6Z|}q*TeQ@<4*wH1iy9U^{{=gD4M?DN1H!GmRj(; zRbCIh#C|^<=}@ zw~(bC1LX(5iwc6@{{6RFk3|0x&TCxww)W?*=#xJoa?&StY2r6zwt$It=#wbiYhI)9 zZTLY?^*OIroaenD+IO8gw3Q~tQPl$YO1JDd*1D0V$I+*r4Ad56qW=qVm3~1)1!e|W z&QUSZKp<)K4Kal8<4ZZp#GUy1NP9-pztKhy26`6AGWdq>#bbSmqQx~A`x@dJ{*82e zjgfv3XTdi_+?s4lj%!G1qRK>~WzhL=WFE-mJSWcJF?Tr$qiP^GS{VHYWP=#VSQ5|%lJ8wU65Ccb`|>A+4B7t z3pb@|d3An4yVXpyX9S+4p5SNrXZ#ud6p(M>D5-+n}Ht_an3-i=*>j zs7S@Md`!rfk%#T_VsVPfTxEKHFga&9B%Jv z-Mh2?d&E~UkQC%RBFxR$oaL1@1=$JtYooH0GLvJ26j}hCf1oHOlmCE-4iEE|Z61cx zOdY<6zZk`naDjJDW`WwCzd#0THmXsYa39cN!C}4u#fJuuH*|x64B)FHt#P1{n=g>e z(HI?Mzl{c!Cd>zfJ>|gV%d$&~m*h3%t^S}Qk+F6Ai$)Gyzrw`y9zlgHvhfLsE3Sv5 zJ%xMx7SA+a#N$IBo&Gy&3jhmHusHW6WCe$PhYnRx3j<`*0JoH`(axiYI)$$d<9mNY8^sLNpkOiY5R?ta zl;WG4F@zbblS*S&uc}>hK#QRJg8l}z%bDC;L}bFUNdKhhU~l`SC(aFC#CP!%OqJlf zU!y8BCb}gj*|+G>#W7*xAtHR<)n?*4JUg5|0!dWhZb$o@t>&M@HiTHB3b2*E9 zgASDhMse*zB7B9ak7|Yn;AXGk`)&B+FOf{bK!tJ^^9EtTxAxLAa(3l&`N;umbE|9i zrM74>biP1UGA3_^2#;PA!}jnF_X|kImj)qpOxLF2v3`8zIdZp#3m3qJ%S@OvbV#Tw zoa^opAB%k(yG*{q9H*)OE}!iw0q3)mKd>Lpu#t`6a4Nv9zT=ckEi>dAVaA;^D@D5smQeF z^Qep!bbv{9^61LR@Ap?7Uf&YbntB;gNh4=;U1h@JO_WhM6IKrjP zJVx#kCQ6lX_!G1QtnwLiBy6F}`yK-(I>O zFdyDR<|-yy>CdvCB@S%pf)sln%BHv=`P%|d75hnJ*N>cS#}mOHbmQ?`=q2pn z`#D^=({P6+)%!kd)T+N=rPurvvo)a<$0Mk+bnyyiT>2KR1l^m&EIO%T=+4kJDE>XZ ziI<;BxgLCM8m#`4_#W`=%Qt|4iSCP7x-&!t+W#KUz};J>EX3DNp29a5eRyW@2C4!A zfy-M+&)>vmhIX+Pxa{lNx0x!^sy~X$oA<7V6NAgM7S$Bxlyfpxm(5{r0+vQs6P+2P zH&0-!6bE@#;>j~0`DuDT#EOX-wA+=fGmQ+@44?_0$6Fu~sXViLoBHw1$o*#rW?+S> zJ55gmorYrjJhr|SU%+tBO3K;D%q%U}0C3SQwFwaQp~-pn#{L1Zkr;zRje$acY#SbE zy^d}I$Ti{`YF9IKr->e9IG=Ao`Nb?cG};9||5e@oJzKBCA`sEnfSBPpO~)n0FKul9 zXswtftE@;X*Xnoa){RlC3Kp62?QL@_SCsBkCxivP2ET$%`T{uJiFVGnGf+7zRRef( zxO>mVKDfE7u*lTCLR>*QewrP0p$GG=^%af^Wz&&nRCRdYhkaLJiHin&H3yL){`)+A z3uB7AEIlc7(aNOQ!q*5>=w+a>{!g?f7(*=i>NbsUqons;48@WV*;b7E@TKF2F5tUI zF9RV{cZ!%oRUXFke&uJRSFBEp$u9DW)n64UOkES3E81DUoM&e(@LL|29UGm!AyF#` zeedy>1x^1#UnSoBgneGe(*@a8@aOS`u4xnsbxoum*F@&vr8zJ?`GE>9vnw?-drU2GGhEh zmsM0!Wr^OF92aKLs+uV})7lRGrf;Ed=3rnIp0Kp6$|X9xOe2)-28yF03%vRHGY#H7 z2AlgBD&d=F51~9iC+7}rbh$T%LQPFk)&h>cT;eI)Pdz-3Zzmk3uAvt~wkw9s(~x@J z{QQ}~GeR(`Q=r3}WZ=o(g_zKxKFbupXE!fzV zCNO~f$HdGa^$zBrS!{8Y%uPHAcqhqqt(R&6~dTg8dHlf9<<5#fLRu;p4IL7}M z;78QJ5TIEzbF{^PZ>XT0o!0fkJ%Mp0pBcnAG^b$v*(soRAvu4E7(|Z^+ztI4?c56X zpF4?fM0d2Ff3;=S|Fh*EQ7cLQ#jFY`3~lr{%y}G1Y-w(pAS-dbMwpJ)V4wvtW?G^s zff<@K6c`1F0z4NTfcQF&AC#Uug=e+q;qsDn8a8os&IC>smIYxC1j~w6Ix~F{< z%w?<{UqTi}&I`PK{k&wg{YUWSjE?5f-z|jolIEEumgQB{<}ObyDbI>Xj!oN8QJL@o zsxz|FXIQd%+|c}tN{5uCTeL>Pn9#V_|8mWLpeB|j`prmaD0Gvn3eFsXER0WH)=+93 zk@@G2_z&d3<{}x?nDxet_uV}t@|3RKEeB5=xU6wMhd&WRTMK!t(xS@TjQpk9%T})| ziOOA7-Fy%|H?}j-Bh9S5^P>}&<;4Fk7`#v$<8;159F7@EG!f3D!h%)l?H^LT*WZk3 zyzhumWq_AtF&b&vg--Q@v0vG?e+6m=OULMA6UTWWp@FiPhU&fhuD=d&tnX-7z8Op4 zl(IQJDz2np`xa%Y$C73HHlZ+v-8|BQYwoZ_5x4ZU98F9&hW4n5Yk_Vuyxtk7muB`h zLuUIum>ZjZODql&$`q6`t!PwJ{o=c)PV8$RgxdA)@DY?i+P^bSL@)Vh*Ns>4-H7!q z?PJ5Xx3#hKRn|1t?JiicIDKv7p5hhgCTR;I>>LsUUyINrBV(O!5ZjcG;+dYhQ}o?8 za+xvr8$uDL771mP(pt2)uXnqqSh>_#kH;^ZN1rimA^hKcBdr;;zM*Yg9L?G0RyKNM za(;H$>hd)ap&QC7Q9R4~4RB4iHP-nG9WWChe_Jm4H_HfCAO4=JHEYg%HtM%!pmpp8 zAm-s4dD@uy6>aNiBee4r@XWaZDQmK*yp6@RW2?|(w&iOa)GacB*2zr3*NH*(b}Y1x z132_2uPPDJ^a~;>R1$3JBA5C5gbwc>0e^ac#~Uin;)%l|^fgBSQa^d4EHMH!b91K7 zTPTSOYp;+*{-oLGw94OCVoW**D}zfQvR^viUYP*#{RW=Rz~ksS*IH*1CID=x#YoQQ zgk0n!HetI4%7XIn)gy=MbJQ50zZg4+vU%n}xXGXWssuA)eNJ2P%x&H?FtfLDQPwTb z3~?d1tgPT~Ku>x6SC}{bNu5v8Zx%e1V`>G@G`)XBz}rw}YM!sP2*O~6#NEy_4`R&4*1!vGr%Q*12hI=owDq=DAK8^ywKKVL=k~qZ zd)Adw+m4n`!bE`$^)>1Bls2`uabmjI2uNdwSNYbg820*@63NEm6{`!=mt`zfuZS^M zMBDlVMWzHUmrBCb8J>TLrvq5L7^!@*pRe3o5||4e)WA&$>@Qf?4S%0N1R3vZ=s4R7iC`RG+q|b{$M8V^ z$BjuuhN=#hu6AGcf6r4kuVjtVwbFM98<(?Dx^60UB!VRajU$~c=SG@(`})uJ^$x)lvvpe&oZ*)-m`1RXKt}37N9e>d?l@G9 zsOHYr=FY(bJ1Rj%?qe|0fXY-xI^CunEqDTR3#PFN)7&FIzbc-Y;SnoWBqaKWr)B4) zq^A|c2KvPO0lNucZ=!f-o_=pnTkF1xfo+Goca*hEqTMoxklMvd&Rl(J`_{Ux+3o9F zn%3o%PQpxs4dWit`X=peF=oy(cktj@xK?k7i6lR$+#c;ElBhzXT~oc6MG5&Sb5I6y z(s`DS68Xu6GN~}a+NTCjUBI6Wg@SiI!%xni!IS-s9Zlzk_f(gT(fFTZFrZ+=go}g@ z^jjCMe&?==9ko5(+bYw?2p2F3FzZ6?Vz?`F!IIE`#o>9$Vo&}btZlG5jgGsT&d1ad zjL+=s_-OwW%pTb2hMcsTCPMtYDPK=7VWc_)nyB$1eCLq#_<{O?!NWUyTG#EVUNZ#) z4R)h~G@qtzEKRxQOb(ZFv|3k`T9?WxjZv>GS{|_~NfaFrJBR9~Aax+&xpCjcSI!M~ z7wwyZSp)c<_OZJ=^PR!&w#JO|DO3bc8x$n}8tr6dX>P+K4XxcB3i6g@hmwUU70KZn z@=_wK!{@j!DM-sJw8}>m8Wg9HsC}07;mevi70dxgHAA|)O&9i*wNAlc0m3?9ULh3T zK5Au@j1qfAHutwc1B`EXHy;_@dUVT*k|~%^fFUVKohzutgZbB$GOM&X-bf|MpQ7cy z-ag>g(%?m*_2=+c)M-35)CWV`dwVz5O~L$tjWz-A6pHlZ=oYP?f`J2=h?3+^(Emwnhoy| zNR+masVR!#TdJC5V>-mu+LGc?0xKid!WEgT(_K}~Y-i(G{=z$YNc**)j% zF;Ldk9?0}}4Wi{!&NRv_pGaKRUH*;LHvLWe{ALnq=Y^UN)A1 z&nJzYlcJInN+OCXLZj!n1%V`I*%GxRAV?zK2KC`H4L+VZc>Yu;O}u#ZB8OyBF$&6G z>hHVmEWQr-r)%4r>uLJMs~0&W>EdQ(ZN;}V4Ov~2oIsT*mM;x`&E6kKG71-|!d~Iy z$<~n}bb%&*c=hgyG|o^f)N{}Sb-k_Y57YDyuU82OrnN)WdPeX4hnAHb$fW82pznk%c-LpB_aA~KUuy9N G-Twd^+x6uD diff --git a/C3X.h b/C3X.h index a7248876..326ca152 100644 --- a/C3X.h +++ b/C3X.h @@ -383,6 +383,8 @@ struct c3x_config { bool allow_enter_canal_from_any_direction; bool ai_defends_districts; + + bool disable_great_wall_city_defense_bonus; bool great_wall_districts_impassible_by_others; bool enable_city_work_radii_highlights; diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 7018bcb5..e3d8aee9 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -870,9 +870,10 @@ repl call, 0x4C01CF, 0x0, 0x0, "Map_impl_is_near_lake_within_wo inlead, 0x458120, 0x0, 0x0, "Unit_ai_move_naval_power_unit", "void (__fastcall *) (Unit * this)" inlead, 0x45A170, 0x0, 0x0, "Unit_ai_move_naval_transport", "void (__fastcall *) (Unit * this)" inlead, 0x460620, 0x0, 0x0, "Unit_ai_move_naval_missile_transport", "void (__fastcall *) (Unit * this)" -inlead, 0x44C130, 0x0, 0x0, "Unit_ai_eval_pillage_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" -inlead, 0x456840, 0x0, 0x0, "Unit_ai_move_air_bombard_unit", "void (__fastcall *) (Unit * this)" -inlead, 0x4579E0, 0x0, 0x0, "Unit_ai_move_air_defense_unit", "void (__fastcall *) (Unit * this)" -inlead, 0x459CE0, 0x0, 0x0, "Unit_ai_move_air_transport", "void (__fastcall *) (Unit * this)" +inlead, 0x44C130, 0x0, 0x0, "Unit_ai_eval_pillage_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" +inlead, 0x456840, 0x0, 0x0, "Unit_ai_move_air_bombard_unit", "void (__fastcall *) (Unit * this)" +inlead, 0x4579E0, 0x0, 0x0, "Unit_ai_move_air_defense_unit", "void (__fastcall *) (Unit * this)" +inlead, 0x459CE0, 0x0, 0x0, "Unit_ai_move_air_transport", "void (__fastcall *) (Unit * this)" define, 0x44C340, 0x0, 0x0, "Unit_ai_eval_bombard_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y, int param_3)" -define, 0x5C1920, 0x0, 0x0, "Unit_airdrop", "void (__fastcall *) (Unit * this, int edx, int x, int y)" \ No newline at end of file +define, 0x5C1920, 0x0, 0x0, "Unit_airdrop", "void (__fastcall *) (Unit * this, int edx, int x, int y)" +repl call, 0x4C0D87, 0x0, 0x0, "Leader_count_wonders_with_flag_ignore_great_wall", "" \ No newline at end of file diff --git a/default.c3x_config.ini b/default.c3x_config.ini index ab2bca36..bbeb82a9 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -916,6 +916,7 @@ max_contiguous_canal_districts = 5 ; infrastructure. Only applies when enable_districts is set to true. ai_defends_districts = true +disable_great_wall_city_defense_bonus = false great_wall_districts_impassible_by_others = true ; When enabled, holding down the Control key while a worker is selected will highlight all tiles within the work radii of nearby cities. City centers diff --git a/injected_code.c b/injected_code.c index c97a10da..476cd2cf 100644 --- a/injected_code.c +++ b/injected_code.c @@ -13668,6 +13668,7 @@ patch_init_floating_point () {"show_natural_wonder_name_on_map" , false, offsetof (struct c3x_config, show_natural_wonder_name_on_map)}, {"ai_defends_districts" , false, offsetof (struct c3x_config, ai_defends_districts)}, {"great_wall_districts_impassible_by_others" , false, offsetof (struct c3x_config, great_wall_districts_impassible_by_others)}, + {"disable_great_wall_city_defense_bonus" , false, offsetof (struct c3x_config, disable_great_wall_city_defense_bonus)}, {"expand_water_tile_checks_to_city_work_area" , false, offsetof (struct c3x_config, expand_water_tile_checks_to_city_work_area)}, {"workers_can_enter_coast" , false, offsetof (struct c3x_config, workers_can_enter_coast)}, {"allow_enter_bridge_from_any_direction" , false, offsetof (struct c3x_config, allow_enter_bridge_from_any_direction)}, @@ -26185,6 +26186,16 @@ patch_Leader_count_any_shared_wonders_with_flag (Leader * this, int edx, enum Im return tr; } +int __fastcall +patch_Leader_count_wonders_with_flag_ignore_great_wall (Leader * this, int edx, enum ImprovementTypeWonderFeatures flag, City * only_in_city) +{ + if (is->current_config.enable_districts && + is->current_config.disable_great_wall_city_defense_bonus) + return 0; + + return patch_Leader_count_any_shared_wonders_with_flag (this, __, flag, only_in_city); +} + int const shared_small_wonder_flags = ITSW_Increases_Chance_of_Leader_Appearance | ITSW_Build_Larger_Armies | From a2ad97ca55e30313dfe8bf628238a7b8d6d02389 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 15 Jan 2026 17:38:01 -0800 Subject: [PATCH 195/356] Add logic for making great wall impassible --- injected_code.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/injected_code.c b/injected_code.c index 476cd2cf..9134531d 100644 --- a/injected_code.c +++ b/injected_code.c @@ -232,6 +232,7 @@ int count_neighborhoods_in_city_radius (City * city); int count_utilized_neighborhoods_in_city_radius (City * city); bool move_matches_directions (int move_dx, int move_dy, int dir1, int dir2); int count_contiguous_canal_districts (int tile_x, int tile_y, int max_count); +bool great_wall_blocks_civ (Tile * tile, int civ_id); struct pause_for_popup { bool done; // Set to true to exit for loop @@ -16520,6 +16521,9 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr int base_cost = Trade_Net_get_movement_cost (this, __, from_x, from_y, to_x, to_y, unit, civ_id, flags, neighbor_index, dist_info); + if ((unit != NULL) && great_wall_blocks_civ (tile_at (to_x, to_y), unit->Body.CivID)) + return -1; + // Let the pathfinder consider coastal tiles reachable for workers when the config flag is on if (is->current_config.enable_districts && is->current_config.workers_can_enter_coast && (base_cost < 0) && (unit != NULL) && is_worker (unit)) { @@ -29189,6 +29193,9 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t { PassBetweenValidity base = Unit_can_pass_between (this, __, from_x, from_y, to_x, to_y, param_5); + if (great_wall_blocks_civ (tile_at (to_x, to_y), this->Body.CivID)) + return PBV_GENERIC_INVALID_MOVE; + if (is->current_config.enable_districts && is->current_config.workers_can_enter_coast && base != PBV_OK && is_worker(this)) { Tile * dest = tile_at (to_x, to_y); @@ -29317,6 +29324,37 @@ patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool return transport; } +bool +great_wall_blocks_civ (Tile * tile, int civ_id) +{ + if (! is->current_config.enable_districts || + ! is->current_config.enable_great_wall_districts || + ! is->current_config.great_wall_districts_impassible_by_others) + return false; + + if ((tile == NULL) || (tile == p_null_tile)) + return false; + + int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); + if (owner_id <= 0) + return false; + if (owner_id == civ_id) + return false; + + struct district_instance * inst = get_district_instance (tile); + if ((inst == NULL) || (inst->district_type != GREAT_WALL_DISTRICT_ID)) + return false; + + if (! district_is_complete (tile, GREAT_WALL_DISTRICT_ID)) + return false; + + int obsolete_id = is->district_infos[GREAT_WALL_DISTRICT_ID].obsoleted_by_id; + if ((obsolete_id >= 0) && Leader_has_tech (&leaders[civ_id], __, obsolete_id)) + return false; + + return true; +} + bool move_matches_directions (int move_dx, int move_dy, int dir1, int dir2) { From 1ff2558b2f70c3a04a43d4cf965abe1c3d904e2a Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 15 Jan 2026 17:42:59 -0800 Subject: [PATCH 196/356] Refine angles in Great Wall art --- Art/Districts/1200/GreatWall.pcx | Bin 18930 -> 23413 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/GreatWall.pcx b/Art/Districts/1200/GreatWall.pcx index 86a245e83b9da33b3397e52521e47233cd6833ae..82333f8fcc50fcd6ec0d6f5e17ebf20b125fe8db 100644 GIT binary patch delta 4021 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 delta 4010 zcmai1du&tJ9ljaPy*I{j;>31r$FUv1?Ig}SkdT)lK;AF%AQYxfRi&z(fK8S9kT&uU zYfD+0@U`oz!U}X!tF;mBSV7gwMhXgnXru+HbcI3-8yO%74Uo_PIs4rk$BFGfdZTl5 z&iDJi@BYrYzjJbP?@-(%;y!AQN9z)0+z?vgb&roS#zp>c`RfMvL_&-k!t@U$q;Awqr^d&EFYPIqxZjLqpTbP3LwoF-A9kQd`HiH*4# zct3&W8?`=@IGI4%MP$L)#4cs5$pnJR>==UywB#G{rcz$Y2%DSuSHAxzm**zB3v8Lu z1sk2_P1G#Sib?YZdY6{QO!fv&7rJqKY3yWgK%>fuak+*TRaK13HMABxe=@IY7%jGH zBBo7)5mY~x22Wxmwk0OuWTFZ$C)Qx)V>*1FSQhsPTh%(`t84Mh1_K7vRk)y5VQ_;A z|5KMCkfg#3)XI_?aJ7<0*{_9m$>eBv}h@j|wM~>(JVx5RS7y z{QlJ)!}x_J6?vLUyr@ZsA*jL|nl4Oh45&>}At_}w9#2V=@#j*iaV*7w=et$7n$m(` zsx6+e8|*7=NNvKwRI?lzOI?W!trsJCDy-JFVZYXn`&}v=*RDmH&MM<2x*GgWmxHfs zR5+;XhFOLRg#`(R}nO}V7Jj8j(=)wK(fgv zTqpiH?mCK08?oEu7OoTj9J`JqrdAl!?csPydL#PMbA(&OKg-?1q4f1InLWZS;-6)= zP;PF+TW0%0+}wnu48Jf*{4s74MH!p0C&ML75`TCu!C(fu03Q_U_sbZcdS+v6h}dQB7_L!+k#?|D3AK`)Xr z8pEik0@dvbIJ~w+ylQVb2D~|hP(hILEdK5d!sv68l1he@XHo7Wj!$}ch(pYq3_owp z#MeGO1izF&)gQ^vA>a?<=sE@7^kuU4bXJ2+4W{Udpx7qeEO$++)ym7%>#k%c6(5lGQl-hOEY6$t%Kd^1Ng- z&V55Rgo@dT4z_0WDWK72xDW&_5G^b7j4(01{JE%Z>fm9_oxUb0O z23!UCXf04+dx2EI5u$%~>hM{?oakWzO@U3YGvqmejKW;>7D|(v;LecE1ojnXV^%`K z9BWYyo-LAI^Bs1YRCn-pQ8wa=r7F3@ohH>CIE%CJ+hR#I&v}QCyTyKCnn~YQ(=e9! z(OcpZra9>gY#M(q@!@C7+~K%mnGeq|3kVO`F!3K?AC13IE1+8L6drKHqbjp(wO_4Gz@<&^K|Cehuid`SWk@&RjXCh zc#KegTbD>%ftR;K*6M}W4(e~|6lqH-agX}F2DQBDde$u9pHjciNH}eK-&z}5J495i zRicmj^Y|_5e^<+gnusTAE$B@r4IL}PwUIGToi_dg8+vpPU$pD+YMn`Ofm?D9EZvCP zb^1l2x_T#Ww~0uyDDiQ<4XZ@!b)HWX8MrRn`-e^Nh zlM3%Nin!WnL7z=6o%$6`mN5J}^;?~^=5iA;N#k$rDOl4?^Ie4RX*T1MU5hFDz1glu zeTzvRztv(!zfDDr$ITWCSc{4pkIGg{7=MlWQfhNe#CWTj{x&EnO#3e~=hi};qX1Ie_1;Z7j9JQds2DNE_Jl+Vl=b^=>T zdr_^V4{&i2N5sELGfQ7MZ7WjgWJ{1f7%EG}SR#4RKj6~2xxf|w52z5| Al>h($ From 4654182efe0a1def539f66e62dc0608257205dc5 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 15 Jan 2026 22:42:46 -0800 Subject: [PATCH 197/356] Add draw_great_wall_district --- injected_code.c | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/injected_code.c b/injected_code.c index 9134531d..1357f778 100644 --- a/injected_code.c +++ b/injected_code.c @@ -27805,20 +27805,6 @@ get_canal_directions (Tile * tile, int tile_x, int tile_y, bool * out_water_dirs void draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int era) { - Sprite * canal_n = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_N]; - Sprite * canal_ne = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_NE]; - Sprite * canal_e = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_E]; - Sprite * canal_se = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_SE]; - Sprite * canal_s = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_S]; - Sprite * canal_sw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_SW]; - Sprite * canal_w = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_W]; - Sprite * canal_nw = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][DIR_NW]; - - Sprite * dir_sprites[9] = { - NULL, canal_ne, canal_e, canal_se, - canal_s, canal_sw, canal_w, canal_nw, canal_n - }; - struct district_config const * cfg = &is->district_configs[CANAL_DISTRICT_ID]; int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; @@ -27832,16 +27818,41 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren bool water_dirs[9] = { false, false, false, false, false, false, false, false, false }; get_canal_directions (tile, tile_x, tile_y, &water_dirs, &draw_dir1, &draw_dir2); - if (draw_dir1 >= 0) - draw_canal_on_map_or_canvas(dir_sprites[draw_dir1], tile_x, tile_y, draw_dir1, water_dirs, map_renderer, draw_x, draw_y); - if (draw_dir2 >= 0) - draw_canal_on_map_or_canvas(dir_sprites[draw_dir2], tile_x, tile_y, draw_dir2, water_dirs, map_renderer, draw_x, draw_y); + if (draw_dir1 >= 0) { + Sprite * sprite1 = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][draw_dir1]; + draw_canal_on_map_or_canvas(sprite1, tile_x, tile_y, draw_dir1, water_dirs, map_renderer, draw_x, draw_y); + } + if (draw_dir2 >= 0) { + Sprite * sprite2 = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][draw_dir2]; + draw_canal_on_map_or_canvas(sprite2, tile_x, tile_y, draw_dir2, water_dirs, map_renderer, draw_x, draw_y); + } } void draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - // TODO + bool wall_nw = tile_has_district_at (tile_x - 1, tile_y - 1, GREAT_WALL_DISTRICT_ID); + bool wall_n = tile_has_district_at (tile_x, tile_y - 2, GREAT_WALL_DISTRICT_ID); + bool wall_ne = tile_has_district_at (tile_x + 1, tile_y - 1, GREAT_WALL_DISTRICT_ID); + bool wall_e = tile_has_district_at (tile_x + 2, tile_y, GREAT_WALL_DISTRICT_ID); + bool wall_se = tile_has_district_at (tile_x + 1, tile_y + 1, GREAT_WALL_DISTRICT_ID); + bool wall_s = tile_has_district_at (tile_x, tile_y + 2, GREAT_WALL_DISTRICT_ID); + bool wall_sw = tile_has_district_at (tile_x - 1, tile_y + 1, GREAT_WALL_DISTRICT_ID); + bool wall_w = tile_has_district_at (tile_x - 2, tile_y, GREAT_WALL_DISTRICT_ID); + + Sprite * sprites = is->district_img_sets[GREAT_WALL_DISTRICT_ID].imgs[0][0]; + Sprite * base = &sprites[0]; + + // Rotate around clockwise NW -> W to get the perspective right + if (wall_nw) draw_district_on_map_or_canvas(&sprites[DIR_NW], map_renderer, pixel_x, pixel_y); + if (wall_n) draw_district_on_map_or_canvas(&sprites[DIR_N], map_renderer, pixel_x, pixel_y); + if (wall_ne) draw_district_on_map_or_canvas(&sprites[DIR_NE], map_renderer, pixel_x, pixel_y); + if (wall_e) draw_district_on_map_or_canvas(&sprites[DIR_E], map_renderer, pixel_x, pixel_y); + draw_district_on_map_or_canvas(base, map_renderer, pixel_x, pixel_y); + if (wall_se) draw_district_on_map_or_canvas(&sprites[DIR_SE], map_renderer, pixel_x, pixel_y); + if (wall_s) draw_district_on_map_or_canvas(&sprites[DIR_S], map_renderer, pixel_x, pixel_y); + if (wall_sw) draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); + if (wall_w) draw_district_on_map_or_canvas(&sprites[DIR_W], map_renderer, pixel_x, pixel_y); } void __fastcall From 4eeec5caac409b649653285bfbbeee505ecb9bc2 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 16 Jan 2026 09:20:36 -0800 Subject: [PATCH 198/356] Add process for auto-building great wall around territory if wonder completed --- Art/Districts/1200/GreatWall.pcx | Bin 23413 -> 26014 bytes C3X.h | 4 + Text/c3x-script.txt | 21 ++++ injected_code.c | 173 ++++++++++++++++++++++++++++++- 4 files changed, 197 insertions(+), 1 deletion(-) diff --git a/Art/Districts/1200/GreatWall.pcx b/Art/Districts/1200/GreatWall.pcx index 82333f8fcc50fcd6ec0d6f5e17ebf20b125fe8db..758c7298a5afb50c3b49527f2c4924835272f747 100644 GIT binary patch delta 6823 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 delta 4939 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 ky~H5gwMbg%Bccity_pending_district_requests[civ_id]); } table_deinit (&is->city_pending_building_orders); + + is->great_wall_auto_build_is_done = false; } void @@ -13669,6 +13671,7 @@ patch_init_floating_point () {"show_natural_wonder_name_on_map" , false, offsetof (struct c3x_config, show_natural_wonder_name_on_map)}, {"ai_defends_districts" , false, offsetof (struct c3x_config, ai_defends_districts)}, {"great_wall_districts_impassible_by_others" , false, offsetof (struct c3x_config, great_wall_districts_impassible_by_others)}, + {"auto_build_great_wall_around_territory" , false, offsetof (struct c3x_config, auto_build_great_wall_around_territory)}, {"disable_great_wall_city_defense_bonus" , false, offsetof (struct c3x_config, disable_great_wall_city_defense_bonus)}, {"expand_water_tile_checks_to_city_work_area" , false, offsetof (struct c3x_config, expand_water_tile_checks_to_city_work_area)}, {"workers_can_enter_coast" , false, offsetof (struct c3x_config, workers_can_enter_coast)}, @@ -19640,6 +19643,153 @@ grant_existing_district_buildings_to_city (City * city) is->sharing_buildings_by_districts_in_progress = prev_flag; } +void +auto_build_great_wall_districts_for_civ (City * city) +{ + if ((! is->current_config.enable_districts) || + (! is->current_config.enable_great_wall_districts) || + (! is->current_config.auto_build_great_wall_around_territory) || + is->great_wall_auto_build_is_done || + (city == NULL) || + is->is_placing_scenario_things) + return; + + int civ_id = city->Body.CivID; + bool is_human = (*p_human_player_bits & (1 << civ_id)) != 0; + + if ((GREAT_WALL_DISTRICT_ID < 0) || (GREAT_WALL_DISTRICT_ID >= is->district_count)) { + is->great_wall_auto_build_is_done = true; + return; + } + + struct district_config const * cfg = &is->district_configs[GREAT_WALL_DISTRICT_ID]; + if ((cfg->command == -1) || (! leader_can_build_district (&leaders[civ_id], GREAT_WALL_DISTRICT_ID))) { + is->great_wall_auto_build_is_done = true; + return; + } + + unsigned int const irrigation_flag = 0x8; + unsigned int const replaceable_flags = TILE_FLAG_MINE | irrigation_flag; + + for (int y = 0; y < p_bic_data->Map.Height; y++) { + for (int x = 0; x < p_bic_data->Map.Width; x++) { + Tile * tile = tile_at (x, y); + if ((tile == NULL) || (tile == p_null_tile)) + continue; + if (tile->CityID >= 0) + continue; + if (tile->vtable->m35_Check_Is_Water (tile)) + continue; + if (tile->vtable->m38_Get_Territory_OwnerID (tile) != civ_id) + continue; + + bool has_border = false; + for (int ni = 1; ni < 9; ni++) { + int nx, ny; + get_neighbor_coords (&p_bic_data->Map, x, y, ni, &nx, &ny); + wrap_tile_coords (&p_bic_data->Map, &nx, &ny); + Tile * neighbor = tile_at (nx, ny); + if ((neighbor == NULL) || (neighbor == p_null_tile)) + continue; + if (neighbor->vtable->m35_Check_Is_Water (neighbor)) + continue; + if (neighbor->vtable->m38_Get_Territory_OwnerID (neighbor) != civ_id) { + has_border = true; + break; + } + } + if (! has_border) + continue; + + if (! district_is_buildable_on_square_type (cfg, tile)) + continue; + if (! district_resource_prereqs_met (tile, x, y, GREAT_WALL_DISTRICT_ID, NULL)) + continue; + + struct district_instance * inst = get_district_instance (tile); + if ((inst != NULL) && (inst->district_type == GREAT_WALL_DISTRICT_ID)) { + if (! district_is_complete (tile, GREAT_WALL_DISTRICT_ID)) { + inst->state = DS_COMPLETED; + if (! tile->vtable->m18_Check_Mines (tile, __, 0)) + tile->vtable->m56_Set_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, x, y); + set_tile_unworkable_for_all_cities (tile, x, y); + } + continue; + } + + unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); + unsigned int replace_flags = overlay_flags & replaceable_flags; + bool has_district = (inst != NULL); + if (has_district || (replace_flags != 0)) { + if (is_human) { + Main_Screen_Form_bring_tile_into_view (p_main_screen_form, __, x, y, 0, true, false); + + PopupForm * popup = get_popup_form (); + set_popup_str_param (0, (char *)is->district_configs[GREAT_WALL_DISTRICT_ID].display_name, -1, -1); + + if (has_district) { + int existing_district_id = inst->district_type; + bool redundant_district = district_instance_is_redundant (inst, tile); + bool would_lose_buildings = any_nearby_city_would_lose_district_benefits (existing_district_id, civ_id, x, y); + if (redundant_district) + would_lose_buildings = false; + set_popup_str_param (0, (char *)is->district_configs[GREAT_WALL_DISTRICT_ID].display_name, -1, -1); + if ((existing_district_id >= 0) && (existing_district_id < is->district_count)) { + set_popup_str_param (1, (char *)is->district_configs[existing_district_id].display_name, -1, -1); + } + + popup->vtable->set_text_key_and_flags ( + popup, __, is->mod_script_path, + would_lose_buildings + ? "C3X_CONFIRM_BUILD_GREAT_WALL_OVER_DISTRICT" + : "C3X_CONFIRM_BUILD_GREAT_WALL_OVER_DISTRICT_SAFE", + -1, 0, 0, 0); + } else { + popup->vtable->set_text_key_and_flags ( + popup, __, is->mod_script_path, + "C3X_CONFIRM_BUILD_GREAT_WALL_OVER_IMPROVEMENT", + -1, 0, 0, 0); + } + + int sel = patch_show_popup (popup, __, 0, 0); + if (sel != 0) + continue; + } + + if (has_district) { + int existing_district_id = inst->district_type; + int inst_x = x, inst_y = y; + if (! district_instance_get_coords (inst, tile, &inst_x, &inst_y)) + continue; + remove_district_instance (tile); + tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, inst_x, inst_y); + handle_district_removed (tile, existing_district_id, inst_x, inst_y, false); + } + + if (replace_flags != 0) + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, replace_flags, x, y); + } + + if (get_district_instance (tile) != NULL) + continue; + + inst = ensure_district_instance (tile, GREAT_WALL_DISTRICT_ID, x, y); + if (inst == NULL) + continue; + + inst->district_type = GREAT_WALL_DISTRICT_ID; + district_instance_set_coords (inst, x, y); + inst->state = DS_COMPLETED; + if (! tile->vtable->m18_Check_Mines (tile, __, 0)) + tile->vtable->m56_Set_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, x, y); + set_tile_unworkable_for_all_cities (tile, x, y); + } + } + + is->great_wall_auto_build_is_done = true; +} + //We need to forwards-declare this void __fastcall patch_City_manage_by_governor (City * this, int edx, bool manage_professions); @@ -19712,6 +19862,9 @@ patch_City_add_or_remove_improvement (City * this, int edx, int improv_id, int a } } } + + if (add && is->current_config.enable_districts && (improv->WonderFlags & ITW_Doubles_City_Defenses)) + auto_build_great_wall_districts_for_civ (this); //Calculate if work_area has shrunk, and if so, redistribute citizens. int post_work_area_radius = get_work_ring_limit_total(this); @@ -24142,6 +24295,10 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi serialize_aligned_text ("current_day_night_cycle", &mod_data); int_to_bytes (buffer_allocate (&mod_data, sizeof is->current_day_night_cycle), is->current_day_night_cycle); } + if (is->great_wall_auto_build_is_done) { + serialize_aligned_text ("great_wall_auto_build_is_done", &mod_data); + *(int *)buffer_allocate (&mod_data, sizeof(int)) = 1; + } if (is->current_config.enable_districts && (is->district_count > 0)) { serialize_aligned_text ("district_config_names", &mod_data); @@ -24541,6 +24698,9 @@ patch_move_game_data (byte * buffer, bool save_else_load) // doesn't get restarted. is->day_night_cycle_unstarted = false; + } else if (match_save_chunk_name (&cursor, "great_wall_auto_build_is_done")) { + is->great_wall_auto_build_is_done = (*((int *)cursor)++ != 0); + } else if (match_save_chunk_name (&cursor, "district_pending_requests")) { bool success = false; int remaining_bytes = (seg + seg_size) - cursor; @@ -26194,7 +26354,8 @@ int __fastcall patch_Leader_count_wonders_with_flag_ignore_great_wall (Leader * this, int edx, enum ImprovementTypeWonderFeatures flag, City * only_in_city) { if (is->current_config.enable_districts && - is->current_config.disable_great_wall_city_defense_bonus) + is->current_config.disable_great_wall_city_defense_bonus && + flag == ITW_Doubles_City_Defenses) return 0; return patch_Leader_count_any_shared_wonders_with_flag (this, __, flag, only_in_city); @@ -27839,10 +28000,20 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma bool wall_s = tile_has_district_at (tile_x, tile_y + 2, GREAT_WALL_DISTRICT_ID); bool wall_sw = tile_has_district_at (tile_x - 1, tile_y + 1, GREAT_WALL_DISTRICT_ID); bool wall_w = tile_has_district_at (tile_x - 2, tile_y, GREAT_WALL_DISTRICT_ID); + + bool none = !wall_nw && !wall_n && !wall_ne && !wall_e && !wall_se && !wall_s && !wall_sw && !wall_w; Sprite * sprites = is->district_img_sets[GREAT_WALL_DISTRICT_ID].imgs[0][0]; Sprite * base = &sprites[0]; + // If no surrounding walls, draw NE, base, SW so tile doesn't look empty + if (none) { + draw_district_on_map_or_canvas(&sprites[DIR_NE], map_renderer, pixel_x, pixel_y); + draw_district_on_map_or_canvas(base, map_renderer, pixel_x, pixel_y); + draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); + return; + } + // Rotate around clockwise NW -> W to get the perspective right if (wall_nw) draw_district_on_map_or_canvas(&sprites[DIR_NW], map_renderer, pixel_x, pixel_y); if (wall_n) draw_district_on_map_or_canvas(&sprites[DIR_N], map_renderer, pixel_x, pixel_y); From 5b8e9fe2298766d85a3b65b98d7d2914ffa5ee63 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 16 Jan 2026 12:29:44 -0800 Subject: [PATCH 199/356] Refine great wall art --- Art/Districts/1200/GreatWall.pcx | Bin 26014 -> 35968 bytes default.c3x_config.ini | 1 + injected_code.c | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Art/Districts/1200/GreatWall.pcx b/Art/Districts/1200/GreatWall.pcx index 758c7298a5afb50c3b49527f2c4924835272f747..016143ef9f3b89cf85871273782716ba6851218d 100644 GIT binary patch literal 35968 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 delta 6091 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 dLHBA~*4?a diff --git a/default.c3x_config.ini b/default.c3x_config.ini index bbeb82a9..98513aa4 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -918,6 +918,7 @@ ai_defends_districts = true disable_great_wall_city_defense_bonus = false great_wall_districts_impassible_by_others = true +auto_build_great_wall_around_territory = true ; When enabled, holding down the Control key while a worker is selected will highlight all tiles within the work radii of nearby cities. City centers ; are highlighted more brightly, while tiles that fall within multiple cities' work radii are highlighted more intensely. This visual aid helps you diff --git a/injected_code.c b/injected_code.c index 674fa506..93f5afa8 100644 --- a/injected_code.c +++ b/injected_code.c @@ -28029,7 +28029,7 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - *p_debug_mode_bits |= 0xC; + //*p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; From c83a8ee7a123a63de156cd0226397ce99426b9bb Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 16 Jan 2026 13:02:58 -0800 Subject: [PATCH 200/356] Done with initial art draft of Great Wall --- Art/Districts/1200/GreatWall.pcx | Bin 35968 -> 20053 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/GreatWall.pcx b/Art/Districts/1200/GreatWall.pcx index 016143ef9f3b89cf85871273782716ba6851218d..c4f86382e722c88682f2ac44a7c8cbf7ffc6b11e 100644 GIT binary patch literal 20053 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 literal 35968 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 From 6433312ace8c8e3b01a44e4de5e7ec8d759bd752 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 16 Jan 2026 14:50:22 -0800 Subject: [PATCH 201/356] Great Wall finally looking good --- Art/Districts/1200/GreatWall.pcx | Bin 20053 -> 20279 bytes C3X.h | 2 +- Text/c3x-script.txt | 2 +- civ_prog_objects.csv | 3 ++- injected_code.c | 33 ++++++++++++++++++------------- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Art/Districts/1200/GreatWall.pcx b/Art/Districts/1200/GreatWall.pcx index c4f86382e722c88682f2ac44a7c8cbf7ffc6b11e..fb696341fcac01c61c03924c3d58589e61676cf6 100644 GIT binary patch delta 5767 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 z zs&M{tPh9|9Q!^q1I0!hE63>I_)eqaJ=0ax()-{r{@D$l%%Wh2XO>#Ofc{}M`!6#w; z-on@%U-QOw;TT5lm;3=~%E&@lgo`a0T`ui=^T%-i)_bWu7=V{k=7%|r>I9m(xYm;| z2n0S)i4BV9VIYOq5W%U^sSS7>9wgXH{6Hv}rC_i;LcbGY-*A->X(^tD)7!=#sr3wDf0jL|C0bSA}3PatUe zf{1C64xJ_;t+L#cnJWlQP&_?W8gH?}hUrFz2znKx)T@-S=4X$R#b(}ORjCm(#>gTHMi*-P-t8XJUQ1f52Cy{+;RlF$;x`fo0!|jrD|FIUY~0^V z4MwqX>912BlXhYLp3zW_#qezX?gqMJ%LqixTqwUPxHO0on$#GZnHs4e3_{J!0(kKW z7H-dEjX7(vn9dWifN0MOK3gm-Ec?@W140K(O^ZtX7ldaF*xV;_{!y+`{u8{KRy5

b>&IFAM4!T;NWo8#@NMyHOq!JA4WSY1y1U1)_%3xoP- z;{#6f5U2B!<^~_?`M{-hiat^XgFC8*bXAcf4QmdIyG9T$-~a!U;L!1Am&RygtvC*N zh;SJ|pFU098A!eh^u?bq9uS%*9>F~#Bkmz%p)J34U+M!5ZO9$23d@Wt*q=THByy)Hx-p5zH0DoxdnN%=fNkl4Dk2aQzzc1Bk+xh)r}#&;>xVbI^W?vt2dO3 zB&zHdsN8d4>uepYaoc2f`2|$OL?g9`3MS2Ke7B&}Jy*wT>3B@b3JG(jNFQ(pUC^dm zuw~BND2qt&A16^JxHu4G-3K?zd$e`Y@^Vk;>v z>w+P?eko^}eq7K)%U(PQeK{|T14F8M;OX48A`*BQ1|Ul6&3z`~k@Z`40e(BbH2mSk zhj8JO`O8#~EttH1n4GsbLj3gJz^Aw)2j!9A&6}&ZKe3+~hQH=T!bo1a;#Pph8l16u m*RwbF&sso&m>CO(PaoZQvFQ(AA6tL^=<69p12Cx|>;D1ae<~#a delta 5485 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 W54QgL(kHK7J9}WySPtAR%KbmaW94B0 diff --git a/C3X.h b/C3X.h index b9b99cbe..d483f407 100644 --- a/C3X.h +++ b/C3X.h @@ -831,7 +831,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP { .command = UCV_Build_GreatWall, .name = "Great Wall", .tooltip = "Build Great Wall", .display_name = "Great Wall", .advance_prereq = NULL, .obsoleted_by = "Metallurgy", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, - .img_paths = {"GreatWall.pcx"}, .dependent_improvements = {0}, .custom_height = 112, .wonder_prereqs = {"The Great Wall"}, .wonder_prereq_count = 1, + .img_paths = {"GreatWall.pcx"}, .dependent_improvements = {0}, .custom_height = 88, .wonder_prereqs = {"The Great Wall"}, .wonder_prereq_count = 1, .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Mountains)), .img_path_count = 1, .max_building_index = 10, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .culture_bonus = 2, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 2, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 50, diff --git a/Text/c3x-script.txt b/Text/c3x-script.txt index c73fe94d..7df9bf1c 100644 --- a/Text/c3x-script.txt +++ b/Text/c3x-script.txt @@ -191,7 +191,7 @@ No, nevermind. #C3X_CONFIRM_BUILD_GREAT_WALL_OVER_IMPROVEMENT #advisor Military -As we expand our $DISTRICT0 to protect our cities, we've found an already exists here. Should we replace the $IMPROVEMENT0 with a $DISTRICT0? +As we expand our $DISTRICT0 to protect our cities, we've found an improvement already exists here. Should we replace the $IMPROVEMENT0 with a $DISTRICT0? #itemlist Yes. We must protect our cities. No, leave it alone. diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index e3d8aee9..19490961 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -809,8 +809,9 @@ define, 0x50FDB3, 0x51A057, 0x50FE53, "ADDR_MAX_TRADABLE_CITY_ID", "byte *" define, 0x505B21, 0x50F8EB, 0x505BC1, "ADDR_TRADABLE_CITIES_SIZE_TO_CLEAR", "byte *" define, 0x51000C, 0x51A2AC, 0x5100AC, "ADDR_MAX_TRADABLE_UNIT_ID", "byte *" define, 0x505B67, 0x50F931, 0x505C07, "ADDR_TRADABLE_UNITS_SIZE_TO_CLEAR", "byte *" -repl vptr, 0x66A538, 0x687644, 0x66A538, "Map_Renderer_m12_Draw_Tile_Buildings", "void (__fastcall *) (Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y)" repl vptr, 0x66A52C, 0x0, 0x0, "Map_Renderer_m09_Draw_Tile_Resources", "void (__fastcall *) (Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y)" +define, 0x66A530, 0x0, 0x0, "Map_Renderer_m10_Draw_Tile_Mountains_Hills_Volcano", "void (__fastcall *) (Map_Renderer * this, int edx, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int flags)" +repl vptr, 0x66A538, 0x687644, 0x66A538, "Map_Renderer_m12_Draw_Tile_Buildings", "void (__fastcall *) (Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y)" repl call, 0x5F5580, 0x6053D2, 0x5F54B0, "Tile_has_city_or_district", "" repl call, 0x5F5816, 0x605639, 0x5F5746, "Tile_has_city_or_district", "" repl call, 0x5F5ABB, 0x6058AF, 0x5F59EB, "Tile_has_city_or_district", "" diff --git a/injected_code.c b/injected_code.c index 93f5afa8..fbf70b1c 100644 --- a/injected_code.c +++ b/injected_code.c @@ -19863,7 +19863,10 @@ patch_City_add_or_remove_improvement (City * this, int edx, int improv_id, int a } } - if (add && is->current_config.enable_districts && (improv->WonderFlags & ITW_Doubles_City_Defenses)) + // Doesn't seem to actually be true for Great Wall?? Check ITW_Double_Combat_Strength_vs_Barbarians instead + if (add && is->current_config.enable_districts && + is->current_config.auto_build_great_wall_around_territory && + Improvement_has_wonder_flag(improv, __, ITW_Double_Combat_Strength_vs_Barbarians)) auto_build_great_wall_districts_for_civ (this); //Calculate if work_area has shrunk, and if so, redistribute citizens. @@ -21715,6 +21718,8 @@ patch_City_can_trade_via_air (City * this) int __fastcall patch_City_get_building_defense_bonus (City * this) { + bool cancel_great_wall_boost = is->current_config.enable_districts && is->current_config.disable_great_wall_city_defense_bonus; + if (is->current_config.optimize_improvement_loops) { int tr = 0; int is_size_level_1 = (this->Body.Population.Size <= p_bic_data->General.MaximumSize_City) && @@ -21725,6 +21730,7 @@ patch_City_get_building_defense_bonus (City * this) if ((is_size_level_1 || (improv->Combat_Bombard == 0)) && has_active_building (this, improv_id)) { int multiplier; if ((improv->Combat_Bombard > 0) && + (! cancel_great_wall_boost) && (patch_Leader_count_any_shared_wonders_with_flag (&leaders[(this->Body).CivID], __, ITW_Doubles_City_Defenses, NULL) > 0)) multiplier = 2; else @@ -26353,6 +26359,8 @@ patch_Leader_count_any_shared_wonders_with_flag (Leader * this, int edx, enum Im int __fastcall patch_Leader_count_wonders_with_flag_ignore_great_wall (Leader * this, int edx, enum ImprovementTypeWonderFeatures flag, City * only_in_city) { + return patch_Leader_count_any_shared_wonders_with_flag (this, __, flag, only_in_city); + if (is->current_config.enable_districts && is->current_config.disable_great_wall_city_defense_bonus && flag == ITW_Doubles_City_Defenses) @@ -27043,6 +27051,7 @@ init_district_images () for (int variant_i = 0; variant_i < variant_count; variant_i++) { if (cfg->img_paths[variant_i] == NULL) continue; + // Read PCX file snprintf (art_dir, sizeof art_dir, "%s\\Art\\Districts\\1200\\%s", is->mod_rel_dir, cfg->img_paths[variant_i]); @@ -27993,18 +28002,16 @@ void draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { bool wall_nw = tile_has_district_at (tile_x - 1, tile_y - 1, GREAT_WALL_DISTRICT_ID); - bool wall_n = tile_has_district_at (tile_x, tile_y - 2, GREAT_WALL_DISTRICT_ID); bool wall_ne = tile_has_district_at (tile_x + 1, tile_y - 1, GREAT_WALL_DISTRICT_ID); - bool wall_e = tile_has_district_at (tile_x + 2, tile_y, GREAT_WALL_DISTRICT_ID); bool wall_se = tile_has_district_at (tile_x + 1, tile_y + 1, GREAT_WALL_DISTRICT_ID); - bool wall_s = tile_has_district_at (tile_x, tile_y + 2, GREAT_WALL_DISTRICT_ID); bool wall_sw = tile_has_district_at (tile_x - 1, tile_y + 1, GREAT_WALL_DISTRICT_ID); - bool wall_w = tile_has_district_at (tile_x - 2, tile_y, GREAT_WALL_DISTRICT_ID); - bool none = !wall_nw && !wall_n && !wall_ne && !wall_e && !wall_se && !wall_s && !wall_sw && !wall_w; + bool none = !wall_nw && !wall_ne && !wall_se && !wall_sw; Sprite * sprites = is->district_img_sets[GREAT_WALL_DISTRICT_ID].imgs[0][0]; Sprite * base = &sprites[0]; + int base_pixel_x = pixel_x; + int base_pixel_y = pixel_y; // If no surrounding walls, draw NE, base, SW so tile doesn't look empty if (none) { @@ -28013,17 +28020,15 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); return; } - // Rotate around clockwise NW -> W to get the perspective right if (wall_nw) draw_district_on_map_or_canvas(&sprites[DIR_NW], map_renderer, pixel_x, pixel_y); - if (wall_n) draw_district_on_map_or_canvas(&sprites[DIR_N], map_renderer, pixel_x, pixel_y); if (wall_ne) draw_district_on_map_or_canvas(&sprites[DIR_NE], map_renderer, pixel_x, pixel_y); - if (wall_e) draw_district_on_map_or_canvas(&sprites[DIR_E], map_renderer, pixel_x, pixel_y); - draw_district_on_map_or_canvas(base, map_renderer, pixel_x, pixel_y); - if (wall_se) draw_district_on_map_or_canvas(&sprites[DIR_SE], map_renderer, pixel_x, pixel_y); - if (wall_s) draw_district_on_map_or_canvas(&sprites[DIR_S], map_renderer, pixel_x, pixel_y); + + // Base pillar + draw_district_on_map_or_canvas(base, map_renderer, base_pixel_x, base_pixel_y); + if (wall_sw) draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); - if (wall_w) draw_district_on_map_or_canvas(&sprites[DIR_W], map_renderer, pixel_x, pixel_y); + if (wall_se) draw_district_on_map_or_canvas(&sprites[DIR_SE], map_renderer, pixel_x, pixel_y); } void __fastcall @@ -28226,7 +28231,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis } void __fastcall -patch_Map_Renderer_m09_Draw_Tile_Resources(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) +patch_Map_Renderer_m09_Draw_Tile_Resources (Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { if (! is->current_config.enable_districts) { Map_Renderer_m09_Draw_Tile_Resources(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); From 44f0992073c3ac16cca91124ecde899df2f98f35 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 16 Jan 2026 15:47:49 -0800 Subject: [PATCH 202/356] Great Wall placement routine almost working --- C3X.h | 9 ++- Text/c3x-script.txt | 24 ++++++-- injected_code.c | 132 +++++++++++++++++++++++++++----------------- 3 files changed, 106 insertions(+), 59 deletions(-) diff --git a/C3X.h b/C3X.h index d483f407..b8411e66 100644 --- a/C3X.h +++ b/C3X.h @@ -601,6 +601,12 @@ enum district_bonus_entry_type { DBET_BUILDING = 1 }; +enum great_wall_auto_build_state { + GWABS_NOT_STARTED = 0, + GWABS_RUNNING, + GWABS_DONE +}; + struct district_bonus_entry { enum district_bonus_entry_type type; int bonus; @@ -1877,7 +1883,8 @@ struct district_button_image_set { int current_tile_x, current_tile_y; // Set to true once the auto-build process for the Great Wall is complete to avoid running it again - bool great_wall_auto_build_is_done; + enum great_wall_auto_build_state great_wall_auto_build; + Tile * great_wall_focus_tile; // ========== // } diff --git a/Text/c3x-script.txt b/Text/c3x-script.txt index 7df9bf1c..53e78ad2 100644 --- a/Text/c3x-script.txt +++ b/Text/c3x-script.txt @@ -189,25 +189,37 @@ A $DISTRICT0 exists here, but either it has no dependent buildings or another $D Yes. There are better uses for this area. No, nevermind. +#C3X_BEGIN_GREAT_WALL_AUTO_BUILD +#advisor Military Happy +#text +Our $DISTRICT0 is now ready to protect our cities! Let's review each area to determine where to place it. + +#C3X_CONFIRM_BUILD_GREAT_WALL +#advisor Military +Shall we extend our $DISTRICT0 to this area? +#itemlist +Yes, we must protect our cities. +No, don't build it here. + #C3X_CONFIRM_BUILD_GREAT_WALL_OVER_IMPROVEMENT #advisor Military -As we expand our $DISTRICT0 to protect our cities, we've found an improvement already exists here. Should we replace the $IMPROVEMENT0 with a $DISTRICT0? +We've found an improvement already exists here. Should we replace it with a $DISTRICT0? #itemlist -Yes. We must protect our cities. +Yes, we must protect our cities. No, leave it alone. #C3X_CONFIRM_BUILD_GREAT_WALL_OVER_DISTRICT #advisor Military -As we expand our $DISTRICT0 to protect our cities, we've found a $DISTRICT1 already exists here with dependent buildings. Should we replace the $DISTRICT1 with a $DISTRICT0? Doing so will remove any dependent buildings. +We've found a $DISTRICT1 already exists here with dependent buildings. Should we replace the $DISTRICT1 with a $DISTRICT0? Doing so will remove any dependent buildings. #itemlist -Yes. We must protect our cities. +Yes, we must protect our cities. No, leave it alone. #C3X_CONFIRM_BUILD_GREAT_WALL_OVER_DISTRICT_SAFE #advisor Military -As we expand our $DISTRICT0 to protect our cities, we've found a $DISTRICT1 already exists here but has no dependent buildings. Should we replace the $DISTRICT1 with a $DISTRICT0? +We've found a $DISTRICT1 already exists here but has no dependent buildings. Should we replace the $DISTRICT1 with a $DISTRICT0? #itemlist -Yes. We must protect our cities. +Yes, we must protect our cities. No, leave it alone. # ; This line must remain at end of file \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index fbf70b1c..0dd274ed 100644 --- a/injected_code.c +++ b/injected_code.c @@ -9286,7 +9286,7 @@ reset_district_state (bool reset_tile_map) } table_deinit (&is->city_pending_building_orders); - is->great_wall_auto_build_is_done = false; + is->great_wall_auto_build = GWABS_NOT_STARTED; } void @@ -19090,6 +19090,7 @@ patch_Map_Renderer_m19_Draw_Tile_by_XY_and_Flags (Map_Renderer * this, int edx, Map_Renderer_m19_Draw_Tile_by_XY_and_Flags (this, __, param_1, pixel_x, pixel_y, map_renderer, param_5, tile_x, tile_y, param_8); Map * map = &p_bic_data->Map; + Tile * tile = tile_at (tile_x, tile_y); if ((is->city_loc_display_perspective >= 0) && (! map->vtable->m10_Get_Map_Zoom (map)) && // Turn off display when zoomed out. Need another set of highlight images for that. ((1 << is->city_loc_display_perspective) & *p_player_bits) && @@ -19108,14 +19109,15 @@ patch_Map_Renderer_m19_Draw_Tile_by_XY_and_Flags (Map_Renderer * this, int edx, } } - // Draw city work radius highlights for selected worker - if (is->current_config.enable_districts && - is->current_config.enable_city_work_radii_highlights && - is->highlight_city_radii && - (((tile_x + tile_y) % 2) == 0)) { // Replicate a check from the base game code. Without this we'd be drawing additional tiles half-way off the grid. + // Districts-related highlights + if (is->current_config.enable_districts && is->tile_highlight_state == IS_OK && + (((tile_x + tile_y) % 2) == 0)) { + init_tile_highlights (); + + // Draw city work radius highlights for selected worker + if (is->current_config.enable_city_work_radii_highlights && + is->highlight_city_radii) { // Replicate a check from the base game code. Without this we'd be drawing additional tiles half-way off the grid. - if (is->tile_highlight_state == IS_OK) { - Tile * tile = tile_at (tile_x, tile_y); if ((tile != NULL) && (tile != p_null_tile)) { int stored_ptr; if (itable_look_up (&is->highlighted_city_radius_tile_pointers, (int)tile, &stored_ptr)) { @@ -19124,6 +19126,13 @@ patch_Map_Renderer_m19_Draw_Tile_by_XY_and_Flags (Map_Renderer * this, int edx, } } } + + // If focusing on a tile after Great Wall completed, highlight the tile while getting user confirmation + if (is->current_config.auto_build_great_wall_around_territory && + is->great_wall_auto_build == GWABS_RUNNING && + is->great_wall_focus_tile == tile) { + Sprite_draw_on_map (&is->tile_highlights[10], __, this, pixel_x, pixel_y, 1, 1, 1, 0); + } } } @@ -19644,30 +19653,38 @@ grant_existing_district_buildings_to_city (City * city) } void -auto_build_great_wall_districts_for_civ (City * city) +auto_build_great_wall_districts_for_civ (int civ_id) { if ((! is->current_config.enable_districts) || (! is->current_config.enable_great_wall_districts) || (! is->current_config.auto_build_great_wall_around_territory) || - is->great_wall_auto_build_is_done || - (city == NULL) || + (is->great_wall_auto_build == GWABS_DONE) || + (civ_id < 0) || is->is_placing_scenario_things) return; - int civ_id = city->Body.CivID; bool is_human = (*p_human_player_bits & (1 << civ_id)) != 0; if ((GREAT_WALL_DISTRICT_ID < 0) || (GREAT_WALL_DISTRICT_ID >= is->district_count)) { - is->great_wall_auto_build_is_done = true; + is->great_wall_auto_build = GWABS_DONE; return; } + init_tile_highlights (); + struct district_config const * cfg = &is->district_configs[GREAT_WALL_DISTRICT_ID]; if ((cfg->command == -1) || (! leader_can_build_district (&leaders[civ_id], GREAT_WALL_DISTRICT_ID))) { - is->great_wall_auto_build_is_done = true; + is->great_wall_auto_build = GWABS_DONE; return; } + PopupForm * popup = get_popup_form (); + set_popup_str_param (0, (char *)is->district_configs[GREAT_WALL_DISTRICT_ID].display_name, -1, -1); + popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_BEGIN_GREAT_WALL_AUTO_BUILD", -1, 0, 0, 0); + patch_show_popup (popup, __, 0, 0); + + is->great_wall_auto_build = GWABS_RUNNING; + unsigned int const irrigation_flag = 0x8; unsigned int const replaceable_flags = TILE_FLAG_MINE | irrigation_flag; @@ -19720,44 +19737,46 @@ auto_build_great_wall_districts_for_civ (City * city) unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); unsigned int replace_flags = overlay_flags & replaceable_flags; bool has_district = (inst != NULL); - if (has_district || (replace_flags != 0)) { - if (is_human) { - Main_Screen_Form_bring_tile_into_view (p_main_screen_form, __, x, y, 0, true, false); + int existing_district_id = -1; + if (has_district) + existing_district_id = inst->district_type; - PopupForm * popup = get_popup_form (); - set_popup_str_param (0, (char *)is->district_configs[GREAT_WALL_DISTRICT_ID].display_name, -1, -1); - - if (has_district) { - int existing_district_id = inst->district_type; - bool redundant_district = district_instance_is_redundant (inst, tile); - bool would_lose_buildings = any_nearby_city_would_lose_district_benefits (existing_district_id, civ_id, x, y); - if (redundant_district) - would_lose_buildings = false; - set_popup_str_param (0, (char *)is->district_configs[GREAT_WALL_DISTRICT_ID].display_name, -1, -1); - if ((existing_district_id >= 0) && (existing_district_id < is->district_count)) { - set_popup_str_param (1, (char *)is->district_configs[existing_district_id].display_name, -1, -1); - } + if (is_human) { + is->great_wall_focus_tile = tile; + Main_Screen_Form_bring_tile_into_view (p_main_screen_form, __, x, y, 0, true, false); - popup->vtable->set_text_key_and_flags ( - popup, __, is->mod_script_path, - would_lose_buildings - ? "C3X_CONFIRM_BUILD_GREAT_WALL_OVER_DISTRICT" - : "C3X_CONFIRM_BUILD_GREAT_WALL_OVER_DISTRICT_SAFE", - -1, 0, 0, 0); - } else { - popup->vtable->set_text_key_and_flags ( - popup, __, is->mod_script_path, - "C3X_CONFIRM_BUILD_GREAT_WALL_OVER_IMPROVEMENT", - -1, 0, 0, 0); - } + char * popup_key = "C3X_CONFIRM_BUILD_GREAT_WALL"; + set_popup_str_param (0, (char *)is->district_configs[GREAT_WALL_DISTRICT_ID].display_name, -1, -1); - int sel = patch_show_popup (popup, __, 0, 0); - if (sel != 0) - continue; + if (has_district) { + bool redundant_district = district_instance_is_redundant (inst, tile); + bool would_lose_buildings; + would_lose_buildings = any_nearby_city_would_lose_district_benefits (existing_district_id, civ_id, x, y); + if (redundant_district) + would_lose_buildings = false; + if ((existing_district_id >= 0) && (existing_district_id < is->district_count)) { + set_popup_str_param (1, (char *)is->district_configs[existing_district_id].display_name, -1, -1); + } + popup_key = would_lose_buildings + ? "C3X_CONFIRM_BUILD_GREAT_WALL_OVER_DISTRICT" + : "C3X_CONFIRM_BUILD_GREAT_WALL_OVER_DISTRICT_SAFE"; + } else if (replace_flags != 0) { + popup_key = "C3X_CONFIRM_BUILD_GREAT_WALL_OVER_IMPROVEMENT"; } + popup->vtable->set_text_key_and_flags ( + popup, __, is->mod_script_path, + popup_key, + -1, 0, 0, 0); + + int sel = patch_show_popup (popup, __, 0, 0); + p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); // Trigger map redraw + if (sel != 0) + continue; + } + + if (has_district || (replace_flags != 0)) { if (has_district) { - int existing_district_id = inst->district_type; int inst_x = x, inst_y = y; if (! district_instance_get_coords (inst, tile, &inst_x, &inst_y)) continue; @@ -19787,7 +19806,8 @@ auto_build_great_wall_districts_for_civ (City * city) } } - is->great_wall_auto_build_is_done = true; + is->great_wall_auto_build = GWABS_DONE; + is->great_wall_focus_tile = NULL; } //We need to forwards-declare this @@ -19867,7 +19887,7 @@ patch_City_add_or_remove_improvement (City * this, int edx, int improv_id, int a if (add && is->current_config.enable_districts && is->current_config.auto_build_great_wall_around_territory && Improvement_has_wonder_flag(improv, __, ITW_Double_Combat_Strength_vs_Barbarians)) - auto_build_great_wall_districts_for_civ (this); + auto_build_great_wall_districts_for_civ (this->Body.CivID); //Calculate if work_area has shrunk, and if so, redistribute citizens. int post_work_area_radius = get_work_ring_limit_total(this); @@ -24301,9 +24321,9 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi serialize_aligned_text ("current_day_night_cycle", &mod_data); int_to_bytes (buffer_allocate (&mod_data, sizeof is->current_day_night_cycle), is->current_day_night_cycle); } - if (is->great_wall_auto_build_is_done) { - serialize_aligned_text ("great_wall_auto_build_is_done", &mod_data); - *(int *)buffer_allocate (&mod_data, sizeof(int)) = 1; + if (is->great_wall_auto_build != GWABS_NOT_STARTED) { + serialize_aligned_text ("great_wall_auto_build_state", &mod_data); + *(int *)buffer_allocate (&mod_data, sizeof(int)) = (int)is->great_wall_auto_build; } if (is->current_config.enable_districts && (is->district_count > 0)) { @@ -24704,8 +24724,16 @@ patch_move_game_data (byte * buffer, bool save_else_load) // doesn't get restarted. is->day_night_cycle_unstarted = false; + } else if (match_save_chunk_name (&cursor, "great_wall_auto_build_state")) { + int state = *((int *)cursor)++; + if ((state >= GWABS_NOT_STARTED) && (state <= GWABS_DONE)) + is->great_wall_auto_build = (enum great_wall_auto_build_state)state; + else + is->great_wall_auto_build = GWABS_NOT_STARTED; + } else if (match_save_chunk_name (&cursor, "great_wall_auto_build_is_done")) { - is->great_wall_auto_build_is_done = (*((int *)cursor)++ != 0); + bool was_done = (*((int *)cursor)++ != 0); + is->great_wall_auto_build = was_done ? GWABS_DONE : GWABS_NOT_STARTED; } else if (match_save_chunk_name (&cursor, "district_pending_requests")) { bool success = false; From 111c7fc91afdae230943ef4361aa5a556fb56e10 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 16 Jan 2026 16:33:42 -0800 Subject: [PATCH 203/356] Demo-ready --- C3X.h | 2 +- Text/c3x-script.txt | 2 +- default.districts_config.txt | 2 +- injected_code.c | 6 ++++++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/C3X.h b/C3X.h index b8411e66..13247595 100644 --- a/C3X.h +++ b/C3X.h @@ -838,7 +838,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_GreatWall, .name = "Great Wall", .tooltip = "Build Great Wall", .display_name = "Great Wall", .advance_prereq = NULL, .obsoleted_by = "Metallurgy", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"GreatWall.pcx"}, .dependent_improvements = {0}, .custom_height = 88, .wonder_prereqs = {"The Great Wall"}, .wonder_prereq_count = 1, - .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Mountains)), + .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Mountains) | (1 << SQ_Forest) | (1 << SQ_Swamp) | (1 << SQ_Jungle)), .img_path_count = 1, .max_building_index = 10, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .culture_bonus = 2, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 2, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 50, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 diff --git a/Text/c3x-script.txt b/Text/c3x-script.txt index 53e78ad2..a4ae0162 100644 --- a/Text/c3x-script.txt +++ b/Text/c3x-script.txt @@ -192,7 +192,7 @@ No, nevermind. #C3X_BEGIN_GREAT_WALL_AUTO_BUILD #advisor Military Happy #text -Our $DISTRICT0 is now ready to protect our cities! Let's review each area to determine where to place it. +Our $DISTRICT0 is now ready to protect our cities! Let's review each area near our borders to determine where to place it. #C3X_CONFIRM_BUILD_GREAT_WALL #advisor Military diff --git a/default.districts_config.txt b/default.districts_config.txt index eee80bdc..a9f0345d 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -350,7 +350,7 @@ happiness_bonus = 0 name = Great Wall tooltip = Build Great Wall obsoleted_by = Metallurgy -buildable_on = desert,plains,grassland,tundra,floodplain,mountains +buildable_on = desert,plains,grassland,tundra,floodplain,mountains,forest,swamp,jungle defense_bonus_percent = 50 allow_multiple = 1 culture_bonus = 2 diff --git a/injected_code.c b/injected_code.c index 0dd274ed..77e62545 100644 --- a/injected_code.c +++ b/injected_code.c @@ -28057,6 +28057,12 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma if (wall_sw) draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); if (wall_se) draw_district_on_map_or_canvas(&sprites[DIR_SE], map_renderer, pixel_x, pixel_y); + + // Extras for tiles near water + if (!wall_se && tile_is_water (tile_x + 1, tile_y - 1)) + draw_district_on_map_or_canvas(&sprites[DIR_NE], map_renderer, pixel_x, pixel_y); + else if (!wall_sw && !wall_nw && tile_is_water (tile_x - 2, tile_y)) + draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); } void __fastcall From 7b25b5429371862015f0be8f32e60a907b518fea Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 16 Jan 2026 20:49:30 -0800 Subject: [PATCH 204/356] Add support for buildable_on_districts --- C3X.h | 8 ++ Notes/district_todos.md | 4 +- default.districts_config.txt | 1 + injected_code.c | 257 ++++++++++++++++++++++++++--------- 4 files changed, 202 insertions(+), 68 deletions(-) diff --git a/C3X.h b/C3X.h index 13247595..9dcb3528 100644 --- a/C3X.h +++ b/C3X.h @@ -632,6 +632,7 @@ struct district_config { char const * dependent_improvements[MAX_DISTRICT_DEPENDENTS]; char const * wonder_prereqs[MAX_DISTRICT_DEPENDENTS]; char const * natural_wonder_prereqs[MAX_DISTRICT_DEPENDENTS]; + char const * buildable_on_districts[MAX_DISTRICT_DEPENDENTS]; char const * img_paths[10]; unsigned int buildable_square_types_mask; bool allow_multiple; @@ -647,6 +648,7 @@ struct district_config { int dependent_improvement_count; int wonder_prereq_count; int natural_wonder_prereq_count; + int buildable_on_district_count; int img_path_count; int max_building_index; int btn_tile_sheet_column; @@ -667,6 +669,9 @@ struct district_config { char const * generated_resource; int generated_resource_id; short generated_resource_flags; + int buildable_on_district_ids[MAX_DISTRICT_DEPENDENTS]; + int buildable_on_district_id_count; + bool has_buildable_on_districts; char const * buildable_by_civs[32]; int buildable_by_civ_count; bool has_buildable_by_civs; @@ -856,11 +861,13 @@ struct parsed_district_definition { char * dependent_improvements[5]; char * wonder_prereqs[5]; char * natural_wonder_prereqs[5]; + char * buildable_on_districts[5]; char * img_paths[5]; int resource_prereq_count; int dependent_improvement_count; int wonder_prereq_count; int natural_wonder_prereq_count; + int buildable_on_district_count; int img_path_count; bool allow_multiple; bool vary_img_by_era; @@ -938,6 +945,7 @@ struct parsed_district_definition { int buildable_by_civ_cultures_ids[5]; int buildable_by_civ_cultures_id_count; bool has_buildable_by_civ_cultures; + bool has_buildable_on_districts; }; struct parsed_wonder_definition { diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 4a2f5ea3..0525b13f 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -3,7 +3,8 @@ - AI navies target maritime districts - Keep irrigation/mine on tile if specified - Firm up logic for river district rendering - - + - Bump great wall bombard eval scores + - Make sure AI workers remove great walls after they have metallury - Hoover Dam (use alt dir, special positioning b/c on river) - Districts: @@ -24,6 +25,7 @@ ## Maritime Districts - ~~Choose terrain type~~ + - ~~Add support for buildable_on_districts~~ - ~~Show generated resource icon over districts~~ - ~~Resources generated by districts appear on map~~ - ~~Flexible config for extra district bonuses based on tile type and nearby buildings~~ diff --git a/default.districts_config.txt b/default.districts_config.txt index a9f0345d..adb81b21 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -350,6 +350,7 @@ happiness_bonus = 0 name = Great Wall tooltip = Build Great Wall obsoleted_by = Metallurgy +wonder_prereqs = "The Great Wall" buildable_on = desert,plains,grassland,tundra,floodplain,mountains,forest,swamp,jungle defense_bonus_percent = 50 allow_multiple = 1 diff --git a/injected_code.c b/injected_code.c index 77e62545..f65275e4 100644 --- a/injected_code.c +++ b/injected_code.c @@ -1888,7 +1888,30 @@ district_is_buildable_on_square_type (struct district_config const * cfg, Tile * if (mask == 0) mask = district_default_buildable_mask (); - return tile_matches_square_type_mask (tile, mask); + if (! tile_matches_square_type_mask (tile, mask)) + return false; + + if (cfg->has_buildable_on_districts) { + struct district_instance * inst = get_district_instance (tile); + if (inst == NULL) + return false; + + int existing_district_id = inst->district_type; + if (! district_is_complete (tile, existing_district_id)) + return false; + + bool matches = false; + for (int i = 0; i < cfg->buildable_on_district_id_count; i++) { + if (cfg->buildable_on_district_ids[i] == existing_district_id) { + matches = true; + break; + } + } + if (! matches) + return false; + } + + return true; } bool @@ -5184,6 +5207,16 @@ free_dynamic_district_config (struct district_config * cfg) } cfg->natural_wonder_prereq_count = 0; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_on_districts); i++) { + if (cfg->buildable_on_districts[i] != NULL) { + free ((void *)cfg->buildable_on_districts[i]); + cfg->buildable_on_districts[i] = NULL; + } + } + cfg->buildable_on_district_count = 0; + cfg->buildable_on_district_id_count = 0; + cfg->has_buildable_on_districts = false; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { if (cfg->buildable_by_civs[i] != NULL) { free ((void *)cfg->buildable_by_civs[i]); @@ -5341,6 +5374,18 @@ free_special_district_override_strings (struct district_config * cfg, struct dis } cfg->natural_wonder_prereq_count = defaults->natural_wonder_prereq_count; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_on_districts); i++) { + char const * default_value = (i < defaults->buildable_on_district_count) ? defaults->buildable_on_districts[i] : NULL; + if ((cfg->buildable_on_districts[i] != NULL) && + (cfg->buildable_on_districts[i] != default_value)) { + free ((void *)cfg->buildable_on_districts[i]); + } + cfg->buildable_on_districts[i] = NULL; + } + cfg->buildable_on_district_count = defaults->buildable_on_district_count; + cfg->buildable_on_district_id_count = defaults->buildable_on_district_id_count; + cfg->has_buildable_on_districts = defaults->has_buildable_on_districts; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { char const * default_value = (i < defaults->buildable_by_civ_count) ? defaults->buildable_by_civs[i] : NULL; if ((cfg->buildable_by_civs[i] != NULL) && @@ -5483,6 +5528,12 @@ free_parsed_district_definition (struct parsed_district_definition * def) free (def->advance_prereq); def->advance_prereq = NULL; } + for (int i = 0; i < ARRAY_LEN (def->buildable_on_districts); i++) { + if (def->buildable_on_districts[i] != NULL) { + free (def->buildable_on_districts[i]); + def->buildable_on_districts[i] = NULL; + } + } if (def->obsoleted_by != NULL) { free (def->obsoleted_by); def->obsoleted_by = NULL; @@ -6052,6 +6103,27 @@ override_special_district_from_definition (struct parsed_district_definition * d } } + if (def->has_buildable_on_districts) { + for (int i = 0; i < ARRAY_LEN (cfg->buildable_on_districts); i++) { + char const * default_value = (i < defaults->buildable_on_district_count) ? defaults->buildable_on_districts[i] : NULL; + if ((cfg->buildable_on_districts[i] != NULL) && + (cfg->buildable_on_districts[i] != default_value)) + free ((void *)cfg->buildable_on_districts[i]); + cfg->buildable_on_districts[i] = NULL; + } + + cfg->buildable_on_district_count = def->buildable_on_district_count; + const int max_entries = ARRAY_LEN (cfg->buildable_on_districts); + if (cfg->buildable_on_district_count > max_entries) + cfg->buildable_on_district_count = max_entries; + for (int i = 0; i < cfg->buildable_on_district_count; i++) { + cfg->buildable_on_districts[i] = def->buildable_on_districts[i]; + def->buildable_on_districts[i] = NULL; + } + cfg->buildable_on_district_id_count = 0; + cfg->has_buildable_on_districts = true; + } + if (def->has_buildable_by_civs) { for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { char const * default_value = (i < defaults->buildable_by_civ_count) ? defaults->buildable_by_civs[i] : NULL; @@ -6322,6 +6394,17 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i def->natural_wonder_prereqs[i] = NULL; } + new_cfg.buildable_on_district_count = def->has_buildable_on_districts ? def->buildable_on_district_count : 0; + const int max_buildable_on_districts = ARRAY_LEN (new_cfg.buildable_on_districts); + if (new_cfg.buildable_on_district_count > max_buildable_on_districts) + new_cfg.buildable_on_district_count = max_buildable_on_districts; + for (int i = 0; i < new_cfg.buildable_on_district_count; i++) { + new_cfg.buildable_on_districts[i] = def->buildable_on_districts[i]; + def->buildable_on_districts[i] = NULL; + } + new_cfg.buildable_on_district_id_count = 0; + new_cfg.has_buildable_on_districts = def->has_buildable_on_districts; + new_cfg.buildable_by_civ_count = def->has_buildable_by_civs ? def->buildable_by_civ_count : 0; const int max_civ_names = ARRAY_LEN (new_cfg.buildable_by_civs); if (new_cfg.buildable_by_civ_count > max_civ_names) @@ -6590,6 +6673,24 @@ handle_district_definition_key (struct parsed_district_definition * def, } free (value_text); + } else if (slice_matches_str (key, "buildable_on_districts")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->buildable_on_districts, + ARRAY_LEN (def->buildable_on_districts), + &list_count, + parse_errors, + line_number, + "buildable_on_districts")) { + def->buildable_on_district_count = list_count; + def->has_buildable_on_districts = true; + } else { + def->buildable_on_district_count = 0; + def->has_buildable_on_districts = false; + } + free (value_text); + } else if (slice_matches_str (key, "buildable_by_civs")) { char * value_text = trim_and_extract_slice (value, 0); int list_count = 0; @@ -8152,6 +8253,66 @@ find_civ_culture_id_by_name (struct string_slice const * name, int * out_id) return false; } +int +find_district_index_by_name (char const * name) +{ + if ((name == NULL) || (name[0] == '\0')) + return -1; + + for (int i = 0; i < is->district_count; i++) { + char const * existing = is->district_configs[i].name; + if ((existing != NULL) && (strcmp (existing, name) == 0)) + return i; + } + + return -1; +} + +int +find_wonder_district_index_by_name (char const * name) +{ + if ((name == NULL) || (name[0] == '\0')) + return -1; + + int improv_id; + if (! stable_look_up (&is->building_name_to_id, (char *)name, &improv_id)) + return -1; + + return find_wonder_config_index_by_improvement_id (improv_id); +} + +int +find_natural_wonder_index_by_name (char const * name) +{ + if ((name == NULL) || (name[0] == '\0') || (is == NULL)) + return -1; + + for (int i = 0; i < is->natural_wonder_count; i++) { + char const * existing = is->natural_wonder_configs[i].name; + if ((existing != NULL) && (strcmp (existing, name) == 0)) + return i; + } + return -1; +} + +City * +find_city_by_name (char const * name) +{ + if ((name == NULL) || (name[0] == '\0') || (p_cities == NULL) || (p_cities->Cities == NULL)) + return NULL; + + for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { + City * city = get_city_ptr (city_index); + if ((city != NULL) && (city->Body.CityName != NULL)) { + (*p_OutputDebugStringA) (city->Body.CityName); + } + if ((city != NULL) && (city->Body.CityName != NULL) && (strcmp (city->Body.CityName, name) == 0)) + return city; + } + + return NULL; +} + void set_wonders_dependent_on_wonder_district (void) { @@ -8349,6 +8510,31 @@ void parse_building_and_tech_ids () } is->district_infos[i].natural_wonder_prereq_count = stored_natural_wonder_count; + for (int j = 0; j < ARRAY_LEN (is->district_configs[i].buildable_on_district_ids); j++) + is->district_configs[i].buildable_on_district_ids[j] = -1; + is->district_configs[i].buildable_on_district_id_count = 0; + + if (is->district_configs[i].has_buildable_on_districts) { + int stored_buildable_on_count = 0; + for (int j = 0; j < is->district_configs[i].buildable_on_district_count; j++) { + char const * name = is->district_configs[i].buildable_on_districts[j]; + if (name == NULL || name[0] == '\0') + continue; + int other_district_id = find_district_index_by_name (name); + if (other_district_id >= 0) { + if (stored_buildable_on_count < ARRAY_LEN (is->district_configs[i].buildable_on_district_ids)) { + is->district_configs[i].buildable_on_district_ids[stored_buildable_on_count] = other_district_id; + stored_buildable_on_count += 1; + } + } else { + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": buildable_on_districts entry \"%s\" not found", district_name, name); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + is->district_configs[i].buildable_on_district_id_count = stored_buildable_on_count; + } + // Resolve generated resource name to ID if (is->district_configs[i].generated_resource != NULL && is->district_configs[i].generated_resource != "") { int res_id; @@ -8727,66 +8913,6 @@ place_natural_wonders_on_map (void) free (placements); } -int -find_district_index_by_name (char const * name) -{ - if ((name == NULL) || (name[0] == '\0')) - return -1; - - for (int i = 0; i < is->district_count; i++) { - char const * existing = is->district_configs[i].name; - if ((existing != NULL) && (strcmp (existing, name) == 0)) - return i; - } - - return -1; -} - -int -find_wonder_district_index_by_name (char const * name) -{ - if ((name == NULL) || (name[0] == '\0')) - return -1; - - int improv_id; - if (! stable_look_up (&is->building_name_to_id, (char *)name, &improv_id)) - return -1; - - return find_wonder_config_index_by_improvement_id (improv_id); -} - -int -find_natural_wonder_index_by_name (char const * name) -{ - if ((name == NULL) || (name[0] == '\0') || (is == NULL)) - return -1; - - for (int i = 0; i < is->natural_wonder_count; i++) { - char const * existing = is->natural_wonder_configs[i].name; - if ((existing != NULL) && (strcmp (existing, name) == 0)) - return i; - } - return -1; -} - -City * -find_city_by_name (char const * name) -{ - if ((name == NULL) || (name[0] == '\0') || (p_cities == NULL) || (p_cities->Cities == NULL)) - return NULL; - - for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { - City * city = get_city_ptr (city_index); - if ((city != NULL) && (city->Body.CityName != NULL)) { - (*p_OutputDebugStringA) (city->Body.CityName); - } - if ((city != NULL) && (city->Body.CityName != NULL) && (strcmp (city->Body.CityName, name) == 0)) - return city; - } - - return NULL; -} - void init_scenario_district_entry (struct scenario_district_entry * entry) { @@ -19701,13 +19827,10 @@ auto_build_great_wall_districts_for_civ (int civ_id) continue; bool has_border = false; - for (int ni = 1; ni < 9; ni++) { - int nx, ny; - get_neighbor_coords (&p_bic_data->Map, x, y, ni, &nx, &ny); - wrap_tile_coords (&p_bic_data->Map, &nx, &ny); - Tile * neighbor = tile_at (nx, ny); - if ((neighbor == NULL) || (neighbor == p_null_tile)) + FOR_TILES_AROUND (tai, 9, x, y) { + if (tai.n == 0) continue; + Tile * neighbor = tai.tile; if (neighbor->vtable->m35_Check_Is_Water (neighbor)) continue; if (neighbor->vtable->m38_Get_Territory_OwnerID (neighbor) != civ_id) { From 3ffb5c3737e9717ba336575f273628a750d6f629 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 16 Jan 2026 23:45:33 -0800 Subject: [PATCH 205/356] Fix Great Wall tile loop bug; Refine art; Shorten text prompts; Handle water cases better --- Art/Districts/1200/GreatWall.pcx | Bin 20279 -> 20747 bytes Notes/district_todos.md | 1 + Text/c3x-script.txt | 4 +- injected_code.c | 63 ++++++++++++++++++------------- 4 files changed, 39 insertions(+), 29 deletions(-) diff --git a/Art/Districts/1200/GreatWall.pcx b/Art/Districts/1200/GreatWall.pcx index fb696341fcac01c61c03924c3d58589e61676cf6..59516d53860f6a86237ed803182fa0e6d91acb50 100644 GIT binary patch delta 4338 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 Of0MIX7XK)E*1rK{)MP9G delta 3708 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 += 1; int tx, ty; get_neighbor_coords (&p_bic_data->Map, tai->center_x, tai->center_y, tai->n, &tx, &ty); - if ((tx >= 0) && (tx < p_bic_data->Map.Width) && (ty >= 0) && (ty < p_bic_data->Map.Height)) + if ((tx >= 0) && (tx < p_bic_data->Map.Width) && (ty >= 0) && (ty < p_bic_data->Map.Height)) { tai->tile = tile_at (tx, ty); + tai->tile_x = tx; + tai->tile_y = ty; + } } } @@ -9779,14 +9783,23 @@ tile_suitable_for_district (Tile * tile, int district_id, City * city, bool * ou struct district_instance * inst = get_district_instance (tile); if (inst != NULL) { - if (inst->district_type != district_id) - return false; + struct district_infos const * info = &is->district_infos[district_id]; + // Unused wonder districts can be repurposed, completed cannot if (district_id == WONDER_DISTRICT_ID && inst->state == DS_COMPLETED) { - struct wonder_district_info * info = &inst->wonder_info; - if (info->state == WDS_COMPLETED) + struct wonder_district_info * winfo = &inst->wonder_info; + if (winfo->state == WDS_COMPLETED) return false; } + + // Great Wall districts can be replaced if obsolete + if (district_id == GREAT_WALL_DISTRICT_ID) { + int obsolete_id = info->obsoleted_by_id; + if (obsolete_id >= 0 && Leader_has_tech (&leaders[city->Body.CivID], __, obsolete_id)) + return true; + } + + return false; } return true; @@ -19814,11 +19827,12 @@ auto_build_great_wall_districts_for_civ (int civ_id) unsigned int const irrigation_flag = 0x8; unsigned int const replaceable_flags = TILE_FLAG_MINE | irrigation_flag; - for (int y = 0; y < p_bic_data->Map.Height; y++) { - for (int x = 0; x < p_bic_data->Map.Width; x++) { - Tile * tile = tile_at (x, y); - if ((tile == NULL) || (tile == p_null_tile)) - continue; + for (int index = 0; index < p_bic_data->Map.TileCount; index++) { + int x, y; + tile_index_to_coords (&p_bic_data->Map, index, &x, &y); + Tile * tile = tile_at (x, y); + if ((tile == NULL) || (tile == p_null_tile)) + continue; if (tile->CityID >= 0) continue; if (tile->vtable->m35_Check_Is_Water (tile)) @@ -19893,7 +19907,6 @@ auto_build_great_wall_districts_for_civ (int civ_id) -1, 0, 0, 0); int sel = patch_show_popup (popup, __, 0, 0); - p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); // Trigger map redraw if (sel != 0) continue; } @@ -19926,7 +19939,6 @@ auto_build_great_wall_districts_for_civ (int civ_id) if (! tile->vtable->m18_Check_Mines (tile, __, 0)) tile->vtable->m56_Set_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, x, y); set_tile_unworkable_for_all_cities (tile, x, y); - } } is->great_wall_auto_build = GWABS_DONE; @@ -28156,36 +28168,33 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma bool wall_ne = tile_has_district_at (tile_x + 1, tile_y - 1, GREAT_WALL_DISTRICT_ID); bool wall_se = tile_has_district_at (tile_x + 1, tile_y + 1, GREAT_WALL_DISTRICT_ID); bool wall_sw = tile_has_district_at (tile_x - 1, tile_y + 1, GREAT_WALL_DISTRICT_ID); - - bool none = !wall_nw && !wall_ne && !wall_se && !wall_sw; + bool wall_s = tile_has_district_at (tile_x, tile_y + 2, GREAT_WALL_DISTRICT_ID); + bool wall_n = tile_has_district_at (tile_x, tile_y - 2, GREAT_WALL_DISTRICT_ID); Sprite * sprites = is->district_img_sets[GREAT_WALL_DISTRICT_ID].imgs[0][0]; Sprite * base = &sprites[0]; - int base_pixel_x = pixel_x; - int base_pixel_y = pixel_y; - // If no surrounding walls, draw NE, base, SW so tile doesn't look empty - if (none) { - draw_district_on_map_or_canvas(&sprites[DIR_NE], map_renderer, pixel_x, pixel_y); - draw_district_on_map_or_canvas(base, map_renderer, pixel_x, pixel_y); - draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); - return; - } - // Rotate around clockwise NW -> W to get the perspective right + // Rotate around clockwise NW -> SW to get the perspective right if (wall_nw) draw_district_on_map_or_canvas(&sprites[DIR_NW], map_renderer, pixel_x, pixel_y); if (wall_ne) draw_district_on_map_or_canvas(&sprites[DIR_NE], map_renderer, pixel_x, pixel_y); + if (!wall_nw && !wall_ne && wall_n) + draw_district_on_map_or_canvas(&sprites[DIR_N], map_renderer, pixel_x, pixel_y); + // Base pillar - draw_district_on_map_or_canvas(base, map_renderer, base_pixel_x, base_pixel_y); + draw_district_on_map_or_canvas(base, map_renderer, pixel_x, pixel_y); if (wall_sw) draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); if (wall_se) draw_district_on_map_or_canvas(&sprites[DIR_SE], map_renderer, pixel_x, pixel_y); // Extras for tiles near water - if (!wall_se && tile_is_water (tile_x + 1, tile_y - 1)) + if (!wall_se && !wall_s && tile_is_water (tile_x + 1, tile_y - 1)) draw_district_on_map_or_canvas(&sprites[DIR_NE], map_renderer, pixel_x, pixel_y); - else if (!wall_sw && !wall_nw && tile_is_water (tile_x - 2, tile_y)) + else if (!wall_sw && !wall_nw && !wall_s && tile_is_water (tile_x - 2, tile_y) && !tile_is_water (tile_x, tile_y + 2)) draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); + + if (!wall_sw && !wall_se && wall_s) + draw_district_on_map_or_canvas(&sprites[DIR_S], map_renderer, pixel_x, pixel_y); } void __fastcall From 56c8f0420b70f4a9ce6bb665357f5a2d93d2087d Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 16 Jan 2026 23:54:42 -0800 Subject: [PATCH 206/356] Refine Great Wall near water placement logic --- injected_code.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/injected_code.c b/injected_code.c index 9949b888..2a53c6cd 100644 --- a/injected_code.c +++ b/injected_code.c @@ -28178,7 +28178,7 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma if (wall_nw) draw_district_on_map_or_canvas(&sprites[DIR_NW], map_renderer, pixel_x, pixel_y); if (wall_ne) draw_district_on_map_or_canvas(&sprites[DIR_NE], map_renderer, pixel_x, pixel_y); - if (!wall_nw && !wall_ne && wall_n) + if (!wall_nw && !wall_ne && wall_n && tile_is_water (tile_x - 1, tile_y - 1)) draw_district_on_map_or_canvas(&sprites[DIR_N], map_renderer, pixel_x, pixel_y); // Base pillar @@ -28193,7 +28193,7 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma else if (!wall_sw && !wall_nw && !wall_s && tile_is_water (tile_x - 2, tile_y) && !tile_is_water (tile_x, tile_y + 2)) draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); - if (!wall_sw && !wall_se && wall_s) + if (!wall_sw && !wall_se && wall_s && tile_is_water (tile_x - 1, tile_y + 1)) draw_district_on_map_or_canvas(&sprites[DIR_S], map_renderer, pixel_x, pixel_y); } From 1220cc0de339b1ae0467f485f8eb8d435cbdc459 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 17 Jan 2026 23:30:24 -0800 Subject: [PATCH 207/356] Add flag for allowing extraterritorial colonies, as well as check to prevent removal if inside a territory --- C3X.h | 2 ++ Text/c3x-script.txt | 8 ++++---- civ_prog_objects.csv | 12 +++++++----- default.c3x_config.ini | 2 ++ injected_code.c | 31 ++++++++++++++++++------------- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/C3X.h b/C3X.h index 9dcb3528..0f9fe2aa 100644 --- a/C3X.h +++ b/C3X.h @@ -328,6 +328,8 @@ struct c3x_config { bool prevent_autorazing; bool prevent_razing_by_players; + bool allow_extraterritorial_colonies; + bool draw_forests_over_roads_and_railroads; int day_night_cycle_mode; diff --git a/Text/c3x-script.txt b/Text/c3x-script.txt index 996b19f5..66b1dbdd 100644 --- a/Text/c3x-script.txt +++ b/Text/c3x-script.txt @@ -198,28 +198,28 @@ Our $DISTRICT0 is ready! Let's review our borders to determine where to place it #advisor Military Shall we extend our $DISTRICT0 here? #itemlist -Yes, we must protect our cities. +Yes, we must protect our people. No, don't build it here. #C3X_CONFIRM_BUILD_GREAT_WALL_OVER_IMPROVEMENT #advisor Military We've found an improvement already exists here. Should we replace it with a $DISTRICT0? #itemlist -Yes, we must protect our cities. +Yes, we must protect our people. No, leave it alone. #C3X_CONFIRM_BUILD_GREAT_WALL_OVER_DISTRICT #advisor Military We've found a $DISTRICT1 already exists here with dependent buildings. Should we replace the $DISTRICT1 with a $DISTRICT0? Doing so will remove any dependent buildings. #itemlist -Yes, we must protect our cities. +Yes, we must protect our people. No, leave it alone. #C3X_CONFIRM_BUILD_GREAT_WALL_OVER_DISTRICT_SAFE #advisor Military We've found a $DISTRICT1 already exists here but has no dependent buildings. Should we replace the $DISTRICT1 with a $DISTRICT0? #itemlist -Yes, we must protect our cities. +Yes, we must protect our people. No, leave it alone. # ; This line must remain at end of file \ No newline at end of file diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 19490961..298933f4 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -30,6 +30,7 @@ inlead, 0x5C1410, 0x5CFFA0, 0x5C1120, "Unit_bombard_tile", "void (__fastca define, 0x5BE820, 0x5CD420, 0x5BE530, "Unit_get_defense_strength", "int (__fastcall *) (Unit * this)" inlead, 0x5BB650, 0x5CA190, 0x5BB360, "Unit_is_visible_to_civ", "char (__fastcall *) (Unit * this, int edx, int civ_id, int param_2)" define, 0x5EA6C0, 0x5F9F10, 0x5EA5F0, "Tile_has_city", "char (__fastcall *) (Tile * this)" +define, 0x5EA6E0, 0x0, 0x0, "Tile_has_colony", "bool (__fastcall *) (Tile * this)" define, 0x5BE5B0, 0x5CD180, 0x5BE2C0, "Unit_get_max_hp", "int (__fastcall *) (Unit * this)" define, 0x5E4EF0, 0x5F4750, 0x5E4E20, "UnitType_has_ability", "bool (__fastcall *) (UnitType * this, int edx, enum UnitTypeAbilities a)" define, 0x5CDDF0, 0x5DCEB0, 0x5CDD10, "get_max_move_points", "int (__cdecl *) (UnitType * unit_type, int civ_id)" @@ -871,10 +872,11 @@ repl call, 0x4C01CF, 0x0, 0x0, "Map_impl_is_near_lake_within_wo inlead, 0x458120, 0x0, 0x0, "Unit_ai_move_naval_power_unit", "void (__fastcall *) (Unit * this)" inlead, 0x45A170, 0x0, 0x0, "Unit_ai_move_naval_transport", "void (__fastcall *) (Unit * this)" inlead, 0x460620, 0x0, 0x0, "Unit_ai_move_naval_missile_transport", "void (__fastcall *) (Unit * this)" -inlead, 0x44C130, 0x0, 0x0, "Unit_ai_eval_pillage_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" -inlead, 0x456840, 0x0, 0x0, "Unit_ai_move_air_bombard_unit", "void (__fastcall *) (Unit * this)" -inlead, 0x4579E0, 0x0, 0x0, "Unit_ai_move_air_defense_unit", "void (__fastcall *) (Unit * this)" -inlead, 0x459CE0, 0x0, 0x0, "Unit_ai_move_air_transport", "void (__fastcall *) (Unit * this)" +inlead, 0x44C130, 0x0, 0x0, "Unit_ai_eval_pillage_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" +inlead, 0x456840, 0x0, 0x0, "Unit_ai_move_air_bombard_unit", "void (__fastcall *) (Unit * this)" +inlead, 0x4579E0, 0x0, 0x0, "Unit_ai_move_air_defense_unit", "void (__fastcall *) (Unit * this)" +inlead, 0x459CE0, 0x0, 0x0, "Unit_ai_move_air_transport", "void (__fastcall *) (Unit * this)" define, 0x44C340, 0x0, 0x0, "Unit_ai_eval_bombard_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y, int param_3)" define, 0x5C1920, 0x0, 0x0, "Unit_airdrop", "void (__fastcall *) (Unit * this, int edx, int x, int y)" -repl call, 0x4C0D87, 0x0, 0x0, "Leader_count_wonders_with_flag_ignore_great_wall", "" \ No newline at end of file +repl call, 0x4C0D87, 0x0, 0x0, "Leader_count_wonders_with_flag_ignore_great_wall", "" +repl call, 0x5D3D67, 0x0, 0x0, "Tile_has_colony_ignore_extraterritorial", "" \ No newline at end of file diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 98513aa4..ee2c211a 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -764,6 +764,8 @@ introduce_all_human_players_at_start_of_hotseat_game = false ; Allows units to be unloaded from an army. The army type must have the "unload" ability set in the editor. allow_unload_from_army = false +allow_extraterritorial_colonies = false + [==================] [=== AESTHETICS ===] [==================] diff --git a/injected_code.c b/injected_code.c index 2a53c6cd..d5bac19b 100644 --- a/injected_code.c +++ b/injected_code.c @@ -13785,6 +13785,7 @@ patch_init_floating_point () {"auto_zoom_city_screen_for_large_work_areas" , true, offsetof (struct c3x_config, auto_zoom_city_screen_for_large_work_areas)}, {"limit_unit_loading_to_one_transport_per_turn" , false, offsetof (struct c3x_config, limit_unit_loading_to_one_transport_per_turn)}, {"prevent_old_units_from_upgrading_past_ability_block" , false, offsetof (struct c3x_config, prevent_old_units_from_upgrading_past_ability_block)}, + {"allow_extraterritorial_colonies" , false, offsetof (struct c3x_config, allow_extraterritorial_colonies)}, {"draw_forests_over_roads_and_railroads" , false, offsetof (struct c3x_config, draw_forests_over_roads_and_railroads)}, {"enable_districts" , false, offsetof (struct c3x_config, enable_districts)}, {"enable_neighborhood_districts" , false, offsetof (struct c3x_config, enable_neighborhood_districts)}, @@ -28180,6 +28181,8 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma if (!wall_nw && !wall_ne && wall_n && tile_is_water (tile_x - 1, tile_y - 1)) draw_district_on_map_or_canvas(&sprites[DIR_N], map_renderer, pixel_x, pixel_y); + if (!wall_se && !wall_s && tile_is_water (tile_x + 1, tile_y - 1)) + draw_district_on_map_or_canvas(&sprites[DIR_NE], map_renderer, pixel_x, pixel_y); // Base pillar draw_district_on_map_or_canvas(base, map_renderer, pixel_x, pixel_y); @@ -28187,12 +28190,8 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma if (wall_sw) draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); if (wall_se) draw_district_on_map_or_canvas(&sprites[DIR_SE], map_renderer, pixel_x, pixel_y); - // Extras for tiles near water - if (!wall_se && !wall_s && tile_is_water (tile_x + 1, tile_y - 1)) - draw_district_on_map_or_canvas(&sprites[DIR_NE], map_renderer, pixel_x, pixel_y); - else if (!wall_sw && !wall_nw && !wall_s && tile_is_water (tile_x - 2, tile_y) && !tile_is_water (tile_x, tile_y + 2)) + if (!wall_sw && !wall_nw && !wall_s && tile_is_water (tile_x - 2, tile_y) && !tile_is_water (tile_x, tile_y + 2)) draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); - if (!wall_sw && !wall_se && wall_s && tile_is_water (tile_x - 1, tile_y + 1)) draw_district_on_map_or_canvas(&sprites[DIR_S], map_renderer, pixel_x, pixel_y); } @@ -28423,15 +28422,12 @@ patch_Map_Renderer_m09_Draw_Tile_Resources (Map_Renderer * this, int edx, int vi if ((owner_id >= 0) && district_can_generate_resource (owner_id, cfg) && ((visible_to_civ_id < 0) || (owner_id == visible_to_civ_id))) { int res_id = cfg->generated_resource_id; - bool show = true; - if (show) { - if (visible_to_civ_id >= 0) { - int req_tech_id = (cfg->generated_resource_flags & MF_NO_TECH_REQ) ? -1 : p_bic_data->ResourceTypes[res_id].RequireID; - if ((req_tech_id < 0) || Leader_has_tech (&leaders[visible_to_civ_id], __, req_tech_id)) - district_resource = res_id; - } else + if (visible_to_civ_id >= 0) { + int req_tech_id = (cfg->generated_resource_flags & MF_NO_TECH_REQ) ? -1 : p_bic_data->ResourceTypes[res_id].RequireID; + if ((req_tech_id < 0) || Leader_has_tech (&leaders[visible_to_civ_id], __, req_tech_id)) district_resource = res_id; - } + } else + district_resource = res_id; } } } @@ -30164,5 +30160,14 @@ patch_Unit_ai_move_air_transport (Unit * this) Unit_set_state (this, __, UnitState_Fortifying); } +bool __fastcall +patch_Tile_has_colony_ignore_extraterritorial (Tile * tile) +{ + if (is->current_config.allow_extraterritorial_colonies) + return false; + + return Tile_has_colony (tile); +} + // TCC requires a main function be defined even though it's never used. int main () { return 0; } From 0259a7d2a1688cafa817d66562b1940dc291b6a9 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 18 Jan 2026 09:22:24 -0800 Subject: [PATCH 208/356] Implement attitude penalty for extraterritorial colonies --- C3X.h | 1 + civ_prog_objects.csv | 3 ++- default.c3x_config.ini | 1 + injected_code.c | 32 +++++++++++++++++++++++++++++++- 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/C3X.h b/C3X.h index 0f9fe2aa..e48a2c48 100644 --- a/C3X.h +++ b/C3X.h @@ -329,6 +329,7 @@ struct c3x_config { bool prevent_razing_by_players; bool allow_extraterritorial_colonies; + int per_extraterritorial_colony_relation_penalty; bool draw_forests_over_roads_and_railroads; diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 298933f4..0ec2a19a 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -879,4 +879,5 @@ inlead, 0x459CE0, 0x0, 0x0, "Unit_ai_move_air_transport", define, 0x44C340, 0x0, 0x0, "Unit_ai_eval_bombard_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y, int param_3)" define, 0x5C1920, 0x0, 0x0, "Unit_airdrop", "void (__fastcall *) (Unit * this, int edx, int x, int y)" repl call, 0x4C0D87, 0x0, 0x0, "Leader_count_wonders_with_flag_ignore_great_wall", "" -repl call, 0x5D3D67, 0x0, 0x0, "Tile_has_colony_ignore_extraterritorial", "" \ No newline at end of file +repl call, 0x5D3D67, 0x0, 0x0, "Tile_has_colony_ignore_extraterritorial", "" +inlead, 0x440100, 0x0, 0x0, "Leader_get_attitude_toward", "int (__fastcall *) (Leader * this, int edx, int civ_id, int param_2)" \ No newline at end of file diff --git a/default.c3x_config.ini b/default.c3x_config.ini index ee2c211a..9208bae7 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -765,6 +765,7 @@ introduce_all_human_players_at_start_of_hotseat_game = false allow_unload_from_army = false allow_extraterritorial_colonies = false +per_extraterritorial_colony_relation_penalty = 3 [==================] [=== AESTHETICS ===] diff --git a/injected_code.c b/injected_code.c index d5bac19b..7f603e30 100644 --- a/injected_code.c +++ b/injected_code.c @@ -13859,6 +13859,7 @@ patch_init_floating_point () {"neighborhood_needed_message_frequency" , 4, offsetof (struct c3x_config, neighborhood_needed_message_frequency)}, {"max_contiguous_bridge_districts" , 3, offsetof (struct c3x_config, max_contiguous_bridge_districts)}, {"max_contiguous_canal_districts" , 5, offsetof (struct c3x_config, max_contiguous_canal_districts)}, + {"per_extraterritorial_colony_relation_penalty" , 0, offsetof (struct c3x_config, per_extraterritorial_colony_relation_penalty)}, }; is->kernel32 = (*p_GetModuleHandleA) ("kernel32.dll"); @@ -28199,7 +28200,7 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - //*p_debug_mode_bits |= 0xC; + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -30169,5 +30170,34 @@ patch_Tile_has_colony_ignore_extraterritorial (Tile * tile) return Tile_has_colony (tile); } +int __fastcall +patch_Leader_get_attitude_toward (Leader * this, int edx, int civ_id, int param_2) +{ + int score = Leader_get_attitude_toward (this, __, civ_id, param_2); + if (!is->current_config.allow_extraterritorial_colonies) + return score; + + int penalty = is->current_config.per_extraterritorial_colony_relation_penalty; + if (penalty != 0) { + int this_civ_id = this->ID; + Map * map = &p_bic_data->Map; + for (int index = 0; index < map->TileCount; index++) { + int x, y; + tile_index_to_coords (map, index, &x, &y); + Tile * tile = tile_at (x, y); + if ((tile == NULL) || (tile == p_null_tile)) + continue; + if (tile->vtable->m38_Get_Territory_OwnerID (tile) != this_civ_id) + continue; + if (! Tile_has_colony (tile)) + continue; + if (tile->vtable->m70_Get_Tile_Building_OwnerID (tile) != civ_id) + continue; + score -= penalty; + } + } + return score; +} + // TCC requires a main function be defined even though it's never used. int main () { return 0; } From b9205832ec992cfa1ad187f09940409eee2a4e4d Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 18 Jan 2026 15:11:40 -0800 Subject: [PATCH 209/356] Add alternative render strategy (building by building, multiple draw passes) for cases where buildings are not dependent on one another, and also can be rendered in the same position each time' --- C3X.h | 8 +++++ injected_code.c | 78 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/C3X.h b/C3X.h index e48a2c48..18f89f15 100644 --- a/C3X.h +++ b/C3X.h @@ -623,6 +623,11 @@ struct district_bonus_list { struct district_bonus_entry entries[MAX_DISTRICT_BONUS_ENTRIES]; }; +enum district_render_strategy { + DRS_BY_COUNT = 0, + DRS_BY_BUILDING = 1 +}; + struct district_config { enum Unit_Command_Values command; char const * name; @@ -641,6 +646,7 @@ struct district_config { bool allow_multiple; bool vary_img_by_era; bool vary_img_by_culture; + enum district_render_strategy render_strategy; bool is_dynamic; bool align_to_coast; int custom_width; @@ -875,6 +881,7 @@ struct parsed_district_definition { bool allow_multiple; bool vary_img_by_era; bool vary_img_by_culture; + enum district_render_strategy render_strategy; bool align_to_coast; int custom_width; int custom_height; @@ -911,6 +918,7 @@ struct parsed_district_definition { bool has_allow_multiple; bool has_vary_img_by_era; bool has_vary_img_by_culture; + bool has_render_strategy; bool has_align_to_coast; bool has_custom_width; bool has_custom_height; diff --git a/injected_code.c b/injected_code.c index 7f603e30..90df6e7c 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5507,6 +5507,7 @@ init_parsed_district_definition (struct parsed_district_definition * def) memset (def, 0, sizeof *def); def->img_path_count = -1; def->defense_bonus_percent = 100; + def->render_strategy = DRS_BY_COUNT; def->buildable_square_types_mask = district_default_buildable_mask (); } @@ -6183,6 +6184,8 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->vary_img_by_era = def->vary_img_by_era; if (def->has_vary_img_by_culture) cfg->vary_img_by_culture = def->vary_img_by_culture; + if (def->has_render_strategy) + cfg->render_strategy = def->render_strategy; if (def->has_align_to_coast) cfg->align_to_coast = def->align_to_coast; if (def->has_custom_width) @@ -6447,6 +6450,7 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.allow_multiple = def->has_allow_multiple ? def->allow_multiple : false; new_cfg.vary_img_by_era = def->has_vary_img_by_era ? def->vary_img_by_era : false; new_cfg.vary_img_by_culture = def->has_vary_img_by_culture ? def->vary_img_by_culture : false; + new_cfg.render_strategy = def->has_render_strategy ? def->render_strategy : DRS_BY_COUNT; new_cfg.align_to_coast = def->has_align_to_coast ? def->align_to_coast : false; new_cfg.custom_width = def->has_custom_width ? def->custom_width : 0; new_cfg.custom_height = def->has_custom_height ? def->custom_height : 0; @@ -6933,6 +6937,24 @@ handle_district_definition_key (struct parsed_district_definition * def, } else add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "render_strategy")) { + char * strategy = copy_trimmed_string_or_null (value, 1); + if (strategy == NULL) { + def->has_render_strategy = false; + add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); + } else if (strcmp (strategy, "by-count") == 0) { + def->render_strategy = DRS_BY_COUNT; + def->has_render_strategy = true; + } else if (strcmp (strategy, "by-building") == 0) { + def->render_strategy = DRS_BY_BUILDING; + def->has_render_strategy = true; + } else { + def->has_render_strategy = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected \"by-count\" or \"by-building\")"); + } + if (strategy != NULL) + free (strategy); + } else if (slice_matches_str (key, "align_to_coast")) { struct string_slice val_slice = *value; int ival; @@ -28247,10 +28269,11 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis if (! completed) return; - struct district_config const * cfg = &is->district_configs[district_id]; + struct district_config const * cfg = &is->district_configs[district_id]; + struct district_infos * district_info = &is->district_infos[district_id]; int territory_owner_id = tile->Territory_OwnerID; int variant = 0; - int era = 0; + int era = 0; int culture = 0; int buildings = 0; int draw_pixel_x = pixel_x; @@ -28370,27 +28393,54 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis } default: { - struct district_infos * info = &is->district_infos[district_id]; - int completed_count = 0; - for (int i = 0; i < info->dependent_building_count; i++) { - int building_id = info->dependent_building_ids[i]; - if ((building_id >= 0) && tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, district_id, building_id)) - completed_count++; - } - buildings = completed_count; - break; + // No-op } } - district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; int offset_x = draw_pixel_x + cfg->x_offset; int offset_y = draw_pixel_y + cfg->y_offset; int draw_x = offset_x - ((sprite_width - 128) / 2); int draw_y = offset_y - (sprite_height - 64); - draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); - return; + + // Render building by building, assuming one image per building stage + if (cfg->render_strategy == DRS_BY_BUILDING) { + int max_stage = cfg->max_building_index; + int stage_limit = ARRAY_LEN (is->district_img_sets[0].imgs[0][0]) - 1; + + if (max_stage > stage_limit) + max_stage = stage_limit; + + // Render base district sprite, index zero + district_sprite = &is->district_img_sets[district_id].imgs[variant][era][0]; + draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); + + for (int i = 0; i < district_info->dependent_building_count; i++) { + int building_id = district_info->dependent_building_ids[i]; + int stage = i + 1; + if (stage > max_stage) + break; + if ((building_id >= 0) && tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, district_id, building_id)) { + district_sprite = &is->district_img_sets[district_id].imgs[variant][era][stage]; + draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); + } + } + return; + + // Render by count of completed buildings, assuming one images contains all buildings up to that count + } else if (cfg->render_strategy == DRS_BY_COUNT) { + int completed_count = 0; + for (int i = 0; i < district_info->dependent_building_count; i++) { + int building_id = district_info->dependent_building_ids[i]; + if ((building_id >= 0) && tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, district_id, building_id)) + completed_count++; + } + buildings = completed_count; + district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; + draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); + return; + } } Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); From ce07d75a9eb053afc8d43b98b21f6ce44c8fb9f3 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 18 Jan 2026 15:20:32 -0800 Subject: [PATCH 210/356] Fix render logic --- Notes/district_todos.md | 1 + injected_code.c | 31 +++++++++++++++---------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 7d2b7d42..f8ff00fe 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -26,6 +26,7 @@ ## Maritime Districts - ~~Choose terrain type~~ + - ~~Add support for alternative render strategy (Municipal District use case)~~ - ~~Add support for buildable_on_districts~~ - ~~Show generated resource icon over districts~~ - ~~Resources generated by districts appear on map~~ diff --git a/injected_code.c b/injected_code.c index 90df6e7c..9d819b22 100644 --- a/injected_code.c +++ b/injected_code.c @@ -28393,7 +28393,17 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis } default: { - // No-op + if (cfg->render_strategy == DRS_BY_BUILDING) + break; + + int completed_count = 0; + for (int i = 0; i < district_info->dependent_building_count; i++) { + int building_id = district_info->dependent_building_ids[i]; + if ((building_id >= 0) && tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, district_id, building_id)) + completed_count++; + } + buildings = completed_count; + break; } } @@ -28404,7 +28414,6 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis int draw_x = offset_x - ((sprite_width - 128) / 2); int draw_y = offset_y - (sprite_height - 64); - // Render building by building, assuming one image per building stage if (cfg->render_strategy == DRS_BY_BUILDING) { int max_stage = cfg->max_building_index; int stage_limit = ARRAY_LEN (is->district_img_sets[0].imgs[0][0]) - 1; @@ -28412,7 +28421,6 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis if (max_stage > stage_limit) max_stage = stage_limit; - // Render base district sprite, index zero district_sprite = &is->district_img_sets[district_id].imgs[variant][era][0]; draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); @@ -28427,20 +28435,11 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis } } return; - - // Render by count of completed buildings, assuming one images contains all buildings up to that count - } else if (cfg->render_strategy == DRS_BY_COUNT) { - int completed_count = 0; - for (int i = 0; i < district_info->dependent_building_count; i++) { - int building_id = district_info->dependent_building_ids[i]; - if ((building_id >= 0) && tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, district_id, building_id)) - completed_count++; - } - buildings = completed_count; - district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; - draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); - return; } + + district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; + draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); + return; } Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); From 811c9a274d75044dcf0b053c127b1b71d44eadd9 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 18 Jan 2026 19:53:00 -0800 Subject: [PATCH 211/356] Add patch for allowing extraterritorial colonies --- Notes/district_todos.md | 1 + civ_prog_objects.csv | 4 +++- injected_code.c | 39 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index f8ff00fe..b990fc85 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,4 +1,5 @@ - Allow workers over water only if in radius of city that can build water district/wonder + - Add ai_build_strategy option and AI worker handling - Add cap on count of distribution hubs, destroy extra if conquered - AI navies target maritime districts - Keep irrigation/mine on tile if specified diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 0ec2a19a..e74077e9 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -4,6 +4,7 @@ define, 0x9C3508, 0x9E5D08, 0x9C34C8, "p_bic_data", "BIC *" define, 0xA52E80, 0xA75680, 0xA52E40, "p_units", "Units *" define, 0xA52DD4, 0xA755D0, 0xA52D94, "p_tile_units", "TileUnits *" define, 0xA52E68, 0xA75668, 0xA52E28, "p_cities", "Cities *" +define, 0xA52E60, 0x0, 0x0, "p_colonies", "Colonies *" define, 0xA52E98, 0xA75698, 0xA52E58, "leaders", "Leader *" define, 0xB3CEA0, 0xB5F6A0, 0xB3CE60, "city_sprites", "Sprite *" define, 0xB3E7D0, 0xB60FD0, 0xB3E790, "destroyed_city_sprites", "Sprite *" @@ -880,4 +881,5 @@ define, 0x44C340, 0x0, 0x0, "Unit_ai_eval_bombard_target", define, 0x5C1920, 0x0, 0x0, "Unit_airdrop", "void (__fastcall *) (Unit * this, int edx, int x, int y)" repl call, 0x4C0D87, 0x0, 0x0, "Leader_count_wonders_with_flag_ignore_great_wall", "" repl call, 0x5D3D67, 0x0, 0x0, "Tile_has_colony_ignore_extraterritorial", "" -inlead, 0x440100, 0x0, 0x0, "Leader_get_attitude_toward", "int (__fastcall *) (Leader * this, int edx, int civ_id, int param_2)" \ No newline at end of file +inlead, 0x440100, 0x0, 0x0, "Leader_get_attitude_toward", "int (__fastcall *) (Leader * this, int edx, int civ_id, int param_2)" +inlead, 0x5D7080, 0x0, 0x0, "Map_check_colony_location", "int (__fastcall *) (Map * this, int edx, int tile_x, int tile_y, int civ_id)" \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index 9d819b22..f1c419d9 100644 --- a/injected_code.c +++ b/injected_code.c @@ -15630,6 +15630,35 @@ is_district_command (int unit_command_value) return maybe_district_command; } +int __fastcall +patch_Map_check_colony_location (Map * this, int edx, int tile_x, int tile_y, int civ_id) +{ + int base = Map_check_colony_location (this, __, tile_x, tile_y, civ_id); + Tile * tile = tile_at (tile_x, tile_y); + + if ((tile == NULL) || (tile == p_null_tile) || ! is->current_config.allow_extraterritorial_colonies) + return base; + + if (tile->vtable->m35_Check_Is_Water (tile)) return base; + if (Tile_has_city (tile) || Tile_has_colony (tile)) return base; + + int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); + if ((owner_id < 0) || (owner_id == civ_id)) return base; + + int resource_type = Tile_get_resource_visible_to (tile, __, civ_id); + if ((resource_type < 0) || (resource_type >= p_bic_data->ResourceTypeCount)) return base; + + int req_tech = p_bic_data->ResourceTypes[resource_type].RequireID; + if ((req_tech >= 0) && (! Leader_has_tech (&leaders[civ_id], __, req_tech))) return base; + + int res_class = p_bic_data->ResourceTypes[resource_type].Class; + if ((res_class != RC_Strategic) && (res_class != RC_Luxury)) return base; + if (tile->vtable->m26_Check_Tile_Building (tile)) + return 6; + + return 0; +} + bool __fastcall patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) { @@ -15654,6 +15683,11 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) return is_worker (this) && can_build_district_on_tile (tile, district_id, this->Body.CivID); } + else if (unit_command_value == UCV_Build_Colony || unit_command_value == UCV_Build_Remote_Colony) { + if (is->current_config.allow_extraterritorial_colonies && is_worker (this)) { + return patch_Map_check_colony_location (&p_bic_data->Map, __, this->Body.X, this->Body.Y, this->Body.CivID) == 0; + } + } else if (unit_command_value == UCV_Build_Mine) { bool has_district = (tile != NULL) && (tile != p_null_tile) && (get_district_instance (tile) != NULL); @@ -28222,7 +28256,7 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - *p_debug_mode_bits |= 0xC; + //*p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -30226,6 +30260,9 @@ patch_Leader_get_attitude_toward (Leader * this, int edx, int civ_id, int param_ if (!is->current_config.allow_extraterritorial_colonies) return score; + // Note, ideally we'd loop over p_colonies like in vanilla, but it's not clear what the + // structure is of it, so for now just loop over all tiles + int penalty = is->current_config.per_extraterritorial_colony_relation_penalty; if (penalty != 0) { int this_civ_id = this->ID; From 4b1b09198185b1c8e852dba60733b22a607e04a5 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 18 Jan 2026 22:59:07 -0800 Subject: [PATCH 212/356] Add support for draw_over_resources to districts config; Update Great Wall art with subtle sandy terrain underneath --- Art/Districts/1200/GreatWall.pcx | Bin 20747 -> 20996 bytes C3X.h | 5 +- Notes/district_todos.md | 1 + default.districts_config.txt | 1 + injected_code.c | 160 +++++++++++++++++++------------ 5 files changed, 105 insertions(+), 62 deletions(-) diff --git a/Art/Districts/1200/GreatWall.pcx b/Art/Districts/1200/GreatWall.pcx index 59516d53860f6a86237ed803182fa0e6d91acb50..8f9405d39e47aeca82c8f4bdfa9006e1d196cf89 100644 GIT binary patch literal 20996 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%0AwSvgGAcfR;9YU}T}u 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 literal 20747 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 = def->render_strategy; if (def->has_align_to_coast) cfg->align_to_coast = def->align_to_coast; + if (def->has_draw_over_resources) + cfg->draw_over_resources = def->draw_over_resources; if (def->has_custom_width) cfg->custom_width = def->custom_width; if (def->has_custom_height) @@ -6452,6 +6454,7 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.vary_img_by_culture = def->has_vary_img_by_culture ? def->vary_img_by_culture : false; new_cfg.render_strategy = def->has_render_strategy ? def->render_strategy : DRS_BY_COUNT; new_cfg.align_to_coast = def->has_align_to_coast ? def->align_to_coast : false; + new_cfg.draw_over_resources = def->has_draw_over_resources ? def->draw_over_resources : false; new_cfg.custom_width = def->has_custom_width ? def->custom_width : 0; new_cfg.custom_height = def->has_custom_height ? def->custom_height : 0; new_cfg.x_offset = def->has_x_offset ? def->x_offset : 0; @@ -6964,6 +6967,15 @@ handle_district_definition_key (struct parsed_district_definition * def, } else add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "draw_over_resources")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->draw_over_resources = (ival != 0); + def->has_draw_over_resources = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "custom_width")) { struct string_slice val_slice = *value; int ival; @@ -15647,10 +15659,10 @@ patch_Map_check_colony_location (Map * this, int edx, int tile_x, int tile_y, in int resource_type = Tile_get_resource_visible_to (tile, __, civ_id); if ((resource_type < 0) || (resource_type >= p_bic_data->ResourceTypeCount)) return base; - + int req_tech = p_bic_data->ResourceTypes[resource_type].RequireID; if ((req_tech >= 0) && (! Leader_has_tech (&leaders[civ_id], __, req_tech))) return base; - + int res_class = p_bic_data->ResourceTypes[resource_type].Class; if ((res_class != RC_Strategic) && (res_class != RC_Luxury)) return base; if (tile->vtable->m26_Check_Tile_Building (tile)) @@ -28253,25 +28265,67 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma draw_district_on_map_or_canvas(&sprites[DIR_S], map_renderer, pixel_x, pixel_y); } -void __fastcall -patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) +void +draw_district_generated_resource_on_tile (Map_Renderer * this, Tile * tile, struct district_instance * inst, + int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int visible_to_civ_id) { - //*p_debug_mode_bits |= 0xC; - if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { - Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); - return; + int base_resource = Tile_get_resource_visible_to (tile, __, visible_to_civ_id); + int district_resource = -1; + + if (inst->state == DS_COMPLETED) { + int district_id = inst->district_type; + if ((district_id >= 0) && (district_id < is->district_count)) { + struct district_config * cfg = &is->district_configs[district_id]; + if (cfg->generated_resource_id >= 0) { + int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); + if ((owner_id >= 0) && district_can_generate_resource (owner_id, cfg) && + ((visible_to_civ_id < 0) || (owner_id == visible_to_civ_id))) { + int res_id = cfg->generated_resource_id; + if (visible_to_civ_id >= 0) { + int req_tech_id = (cfg->generated_resource_flags & MF_NO_TECH_REQ) ? -1 : p_bic_data->ResourceTypes[res_id].RequireID; + if ((req_tech_id < 0) || Leader_has_tech (&leaders[visible_to_civ_id], __, req_tech_id)) + district_resource = res_id; + } else + district_resource = res_id; + } + } + } } - Tile * tile = tile_at (tile_x, tile_y); - if ((tile == NULL) || (tile == p_null_tile)) + if (district_resource < 0) { + Map_Renderer_m09_Draw_Tile_Resources(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; + } - struct district_instance * inst = get_district_instance (tile); - if (inst == NULL) { - Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); - return; + int tile_width = p_bic_data->is_zoomed_out ? 64 : 128; + int offset = tile_width >> 2; + int left_x = pixel_x - (offset >> 1); + int right_x = pixel_x + (offset >> 1); + + if (base_resource >= 0) + Map_Renderer_m09_Draw_Tile_Resources(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, left_x, pixel_y); + + int icon_id = p_bic_data->ResourceTypes[district_resource].IconID; + if (icon_id >= 0 && icon_id < 36 && map_renderer != NULL && map_renderer->Resources != NULL) { + Sprite * sprite = &map_renderer->Resources[icon_id]; + int tile_height = tile_width >> 1; + int sprite_width = sprite->Width; + int sprite_height = sprite->Height; + if (sprite_width <= 0) sprite_width = sprite->Width3; + if (sprite_height <= 0) sprite_height = sprite->Height3; + int center_x = (tile_width - sprite_width) >> 1; + int center_y = (tile_height - sprite_height) >> 1; + int draw_x = (base_resource >= 0) ? right_x : pixel_x; + draw_x += center_x; + int draw_y = pixel_y + center_y; + int draw_scale = (p_bic_data->is_zoomed_out != false) + 1; + patch_Sprite_draw_on_map (sprite, __, map_renderer, draw_x, draw_y, 1, 1, draw_scale, 0); } +} +void +draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instance * inst, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int visible_to_civ_id) +{ int district_id = inst->district_type; if (is->dc_img_state == IS_UNINITED) init_district_images (); @@ -28479,6 +28533,32 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); } +void __fastcall +patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) +{ + //*p_debug_mode_bits |= 0xC; + if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { + Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); + return; + } + + Tile * tile = tile_at (tile_x, tile_y); + if ((tile == NULL) || (tile == p_null_tile)) + return; + + struct district_instance * inst = get_district_instance (tile); + if (inst == NULL) { + Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); + return; + } + + // Draw resources first if needed + if (is->district_configs[inst->district_type].draw_over_resources) + draw_district_generated_resource_on_tile (this, tile, inst, tile_x, tile_y, map_renderer, pixel_x, pixel_y, visible_to_civ_id); + + draw_district_on_tile (this, tile, inst, tile_x, tile_y, map_renderer, pixel_x, pixel_y, visible_to_civ_id); +} + void __fastcall patch_Map_Renderer_m09_Draw_Tile_Resources (Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { @@ -28493,59 +28573,17 @@ patch_Map_Renderer_m09_Draw_Tile_Resources (Map_Renderer * this, int edx, int vi return; } - int base_resource = Tile_get_resource_visible_to (tile, __, visible_to_civ_id); - int district_resource = -1; - struct district_instance * inst = get_district_instance (tile); - if ((inst != NULL) && (inst->state == DS_COMPLETED)) { - int district_id = inst->district_type; - if ((district_id >= 0) && (district_id < is->district_count)) { - struct district_config * cfg = &is->district_configs[district_id]; - if (cfg->generated_resource_id >= 0) { - int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); - if ((owner_id >= 0) && district_can_generate_resource (owner_id, cfg) && - ((visible_to_civ_id < 0) || (owner_id == visible_to_civ_id))) { - int res_id = cfg->generated_resource_id; - if (visible_to_civ_id >= 0) { - int req_tech_id = (cfg->generated_resource_flags & MF_NO_TECH_REQ) ? -1 : p_bic_data->ResourceTypes[res_id].RequireID; - if ((req_tech_id < 0) || Leader_has_tech (&leaders[visible_to_civ_id], __, req_tech_id)) - district_resource = res_id; - } else - district_resource = res_id; - } - } - } - } - - if (district_resource < 0) { + if (inst == NULL) { Map_Renderer_m09_Draw_Tile_Resources(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; } - int tile_width = p_bic_data->is_zoomed_out ? 64 : 128; - int offset = tile_width >> 2; - int left_x = pixel_x - (offset >> 1); - int right_x = pixel_x + (offset >> 1); - - if (base_resource >= 0) - Map_Renderer_m09_Draw_Tile_Resources(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, left_x, pixel_y); + // Resources that should be drawn below district are already drawn, skip in that case + if (is->district_configs[inst->district_type].draw_over_resources) + return; - int icon_id = p_bic_data->ResourceTypes[district_resource].IconID; - if (icon_id >= 0 && icon_id < 36 && map_renderer != NULL && map_renderer->Resources != NULL) { - Sprite * sprite = &map_renderer->Resources[icon_id]; - int tile_height = tile_width >> 1; - int sprite_width = sprite->Width; - int sprite_height = sprite->Height; - if (sprite_width <= 0) sprite_width = sprite->Width3; - if (sprite_height <= 0) sprite_height = sprite->Height3; - int center_x = (tile_width - sprite_width) >> 1; - int center_y = (tile_height - sprite_height) >> 1; - int draw_x = (base_resource >= 0) ? right_x : pixel_x; - draw_x += center_x; - int draw_y = pixel_y + center_y; - int draw_scale = (p_bic_data->is_zoomed_out != false) + 1; - patch_Sprite_draw_on_map (sprite, __, map_renderer, draw_x, draw_y, 1, 1, draw_scale, 0); - } + draw_district_generated_resource_on_tile (this, tile, inst, tile_x, tile_y, map_renderer, pixel_x, pixel_y, visible_to_civ_id); } bool __fastcall From ad495ab0cc2bf4098b56146be77cd263c31564d3 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 18 Jan 2026 23:09:59 -0800 Subject: [PATCH 213/356] Align Great Wall base pillar slats to be symmetrical --- Art/Districts/1200/GreatWall.pcx | Bin 20996 -> 20994 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/GreatWall.pcx b/Art/Districts/1200/GreatWall.pcx index 8f9405d39e47aeca82c8f4bdfa9006e1d196cf89..348dcca777337951a32554bf1efc4c3a462c0c38 100644 GIT binary patch delta 221 zcmZo!!q~Kgal<1~M!U^VL~|I0Sh<8G4hd=;Jlu2e+`-oee>qRsoGhNk$i&FJ`Lx7o zCMMa#n`cUgFfvOX65RYjCYDh^go~R?MpDNA;IkcTTHOv_-y9?Nm5GU!Yjdz-Iau2n zrAds8W}6dKN*P&X4@rt_zNxx{QQ(k>;2|MN3wGnh+Yg>y9JqG#LiIXEJ|1pCDG|9t z#cLKPbR4|8Sy%Hn*eFe%R1^@s6 delta 223 zcmV<503iQ@qydDa0kGl~0>SvR;}vWH6ATm*7{U=K!NZKf&cW-!`oZFPvtbuz0RjsO zv&k6C0s}G}GP9N&Q~?7e1q!q699;nq5&;7W92p!>!RENEg+?Q@S|0TR0uUv$Q6YB$ z0>Tlq$|8~h0>RX?T_kk@1ScFa7_-(TxB(Cp7zGLv88`cL4$n3bWBRF&k5HOAi15 From 7aaa162fc0d67dbd9531c347cd0701d6935fa8d6 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 19 Jan 2026 07:26:19 -0800 Subject: [PATCH 214/356] Limit AI workers to only enter coast if assigned a maritime district job --- Notes/district_todos.md | 2 +- injected_code.c | 57 +++++++++++++++++++++++++++++------------ 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index d6caea50..a4ef2940 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,4 +1,4 @@ - - Allow workers over water only if in radius of city that can build water district/wonder + - [x] Allow workers over water only if in radius of city that can build water district/wonder - Add ai_build_strategy option and AI worker handling - Add cap on count of distribution hubs, destroy extra if conquered - AI navies target maritime districts diff --git a/injected_code.c b/injected_code.c index 88c6be05..d602dee7 100644 --- a/injected_code.c +++ b/injected_code.c @@ -22831,8 +22831,27 @@ patch_Unit_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, bool bool should_override = false; if (allow_worker_coast && dest->vtable->m35_Check_Is_Water (dest) && - (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) - should_override = true; + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) { + bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; + if (is_human) { + should_override = true; + } else { + struct district_worker_record * rec = get_tracked_worker_record (this); + struct pending_district_request * req = (rec != NULL) ? rec->pending_req : NULL; + if (req != NULL) { + City * req_city = (req->city != NULL) ? req->city : get_city_ptr (req->city_id); + if ((req->district_id >= 0) && + (req->district_id < is->district_count) && + (req_city != NULL) && + city_radius_contains_tile (req_city, nx, ny) && + (req->target_x == nx) && (req->target_y == ny)) { + struct district_config const * cfg = &is->district_configs[req->district_id]; + if ((cfg->buildable_square_types_mask & (1 << SQ_Coast)) != 0) + should_override = true; + } + } + } + } if (! should_override && allow_bridge_walk) { struct district_instance * inst = get_district_instance (dest); @@ -22843,11 +22862,11 @@ patch_Unit_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, bool } if (should_override) { - coast_override_active = true; - is->coast_walk_unit = this; - is->coast_walk_transport_override = false; - is->coast_walk_prev_state = prev_state; - is->coast_walk_prev_container = prev_container; + coast_override_active = true; + is->coast_walk_unit = this; + is->coast_walk_transport_override = false; + is->coast_walk_prev_state = prev_state; + is->coast_walk_prev_container = prev_container; } } } @@ -29681,9 +29700,18 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t struct district_worker_record * rec = get_tracked_worker_record (this); struct pending_district_request * req = (rec != NULL) ? rec->pending_req : NULL; - if ((req != NULL) && - (req->target_x == to_x) && (req->target_y == to_y)) - return PBV_OK; + if (req != NULL) { + City * req_city = (req->city != NULL) ? req->city : get_city_ptr (req->city_id); + if ((req->district_id >= 0) && + (req->district_id < is->district_count) && + (req_city != NULL) && + city_radius_contains_tile (req_city, to_x, to_y) && + (req->target_x == to_x) && (req->target_y == to_y)) { + struct district_config const * cfg = &is->district_configs[req->district_id]; + if ((cfg->buildable_square_types_mask & (1 << SQ_Coast)) != 0) + return PBV_OK; + } + } } } @@ -29762,19 +29790,16 @@ patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) allow_move = true; - if (! allow_move && - is->current_config.enable_bridge_districts && + if (! allow_move && is->current_config.enable_bridge_districts && (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Land)) { struct district_instance * inst = get_district_instance (dest); - if ((inst != NULL) && - (inst->district_type == BRIDGE_DISTRICT_ID) && + if (inst != NULL && inst->district_type == BRIDGE_DISTRICT_ID && district_is_complete (dest, inst->district_type)) { allow_move = true; if (! is->current_config.allow_enter_bridge_from_any_direction) { int move_dx = 0, move_dy = 0; int dir1 = -1, dir2 = -1; - int neighbor_index = Map_compute_neighbor_index (&p_bic_data->Map, __, - this->Body.X, this->Body.Y, tile_x, tile_y, 1000); + int neighbor_index = Map_compute_neighbor_index (&p_bic_data->Map, __, this->Body.X, this->Body.Y, tile_x, tile_y, 1000); if (neighbor_index >= 0) { neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); get_bridge_directions (dest, tile_x, tile_y, &dir1, &dir2); From 3bf1a2f43a7e8fdad0cfad61203c54508f38d18a Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 19 Jan 2026 08:14:32 -0800 Subject: [PATCH 215/356] Add ai_build_strategy flag and tile improvement AI worker logic --- .../WorkerDistrictButtonsHighlighted.pcx | Bin 19431 -> 20055 bytes C3X.h | 8 + Notes/district_todos.md | 1 + injected_code.c | 249 ++++++++++++++++++ 4 files changed, 258 insertions(+) diff --git a/Art/Districts/WorkerDistrictButtonsHighlighted.pcx b/Art/Districts/WorkerDistrictButtonsHighlighted.pcx index d756610de8d881165d2c0dd1c9f228097829dedb..4dde6f4648c21bf48727967b94dd6674b047b83a 100644 GIT binary patch delta 956 zcmXYwxo#9e5Qe9^cc)wnz1{6Lcx|r_L_&y!Lrd5hhL8XWgnS?p0}-#_2LM(A0s~X;^HVrg8asx&9n@3x557JB zv~4FaQDt`$ES{uagNv{=KZddDa$r9R4#)ZT9oV`sf~bI1=|q_ZXGU3h4ZF)j`CYKW z;GM66XEzG2GtBZ2*zMO)iRLVO4xSjvxM6TqXTzqtzg`KsB}A7T7Kt4jR|jvg%AS{M zzh8j@=gCH~kTxP3N#h&CUv-!aM(8sLVI9Q$|Egl?4AbO=sT^uOANM>_~h)gLQa`fBQy*_9osujG^(kbDm3PPC- zFWL_mp&XK7iMFW8pNK>`_NcwL2q>D3E^97n=dyOzK3E1ErIKcvv@VKlvb zkqqWDfMW2ww=nqC>zyAdz%weFyV>hpT|E(sJV4__lH@CcVp^znRkmBx!NJl-n8GmK RPa)lvDdpSzP<1Zv{0Cm61mXYy delta 336 zcmcaUhw=Gz#tCOw|1j^CJQhpfLTW;_b?|xSzIjHVAhezlUcH$ED6>qFzfK-Zq{fhi-|22%Bo|FhqAt~ zB|=&G?72|ZFZN_8tCj;|+u_N7IC7w3?VJ#M4^QUc3I>ZEnOwu=1!f(c{FuuF%5vw1 z`107~MNro9$-lUb!LlbNJMd_*g976uBg5oGX_3hjc*F!j{8Nk!rx;k+j&mGhXJei` WQCbD8{>fL%8mdETZ*Ir diff --git a/C3X.h b/C3X.h index ece99686..953be355 100644 --- a/C3X.h +++ b/C3X.h @@ -628,6 +628,11 @@ enum district_render_strategy { DRS_BY_BUILDING = 1 }; +enum district_ai_build_strategy { + DABS_DISTRICT = 0, + DABS_TILE_IMPROVEMENT = 1 +}; + struct district_config { enum Unit_Command_Values command; char const * name; @@ -647,6 +652,7 @@ struct district_config { bool vary_img_by_era; bool vary_img_by_culture; enum district_render_strategy render_strategy; + enum district_ai_build_strategy ai_build_strategy; bool is_dynamic; bool align_to_coast; bool draw_over_resources; @@ -883,6 +889,7 @@ struct parsed_district_definition { bool vary_img_by_era; bool vary_img_by_culture; enum district_render_strategy render_strategy; + enum district_ai_build_strategy ai_build_strategy; bool align_to_coast; bool draw_over_resources; int custom_width; @@ -921,6 +928,7 @@ struct parsed_district_definition { bool has_vary_img_by_era; bool has_vary_img_by_culture; bool has_render_strategy; + bool has_ai_build_strategy; bool has_align_to_coast; bool has_draw_over_resources; bool has_custom_width; diff --git a/Notes/district_todos.md b/Notes/district_todos.md index a4ef2940..882d70c0 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,5 +1,6 @@ - [x] Allow workers over water only if in radius of city that can build water district/wonder - Add ai_build_strategy option and AI worker handling + - Add logic for determining if canal connects bodies of water for AI port-building - Add cap on count of distribution hubs, destroy extra if conquered - AI navies target maritime districts - Keep irrigation/mine on tile if specified diff --git a/injected_code.c b/injected_code.c index d602dee7..01bbd3cf 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5508,6 +5508,7 @@ init_parsed_district_definition (struct parsed_district_definition * def) def->img_path_count = -1; def->defense_bonus_percent = 100; def->render_strategy = DRS_BY_COUNT; + def->ai_build_strategy = DABS_DISTRICT; def->buildable_square_types_mask = district_default_buildable_mask (); } @@ -6186,6 +6187,8 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->vary_img_by_culture = def->vary_img_by_culture; if (def->has_render_strategy) cfg->render_strategy = def->render_strategy; + if (def->has_ai_build_strategy) + cfg->ai_build_strategy = def->ai_build_strategy; if (def->has_align_to_coast) cfg->align_to_coast = def->align_to_coast; if (def->has_draw_over_resources) @@ -6453,6 +6456,7 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.vary_img_by_era = def->has_vary_img_by_era ? def->vary_img_by_era : false; new_cfg.vary_img_by_culture = def->has_vary_img_by_culture ? def->vary_img_by_culture : false; new_cfg.render_strategy = def->has_render_strategy ? def->render_strategy : DRS_BY_COUNT; + new_cfg.ai_build_strategy = def->has_ai_build_strategy ? def->ai_build_strategy : DABS_DISTRICT; new_cfg.align_to_coast = def->has_align_to_coast ? def->align_to_coast : false; new_cfg.draw_over_resources = def->has_draw_over_resources ? def->draw_over_resources : false; new_cfg.custom_width = def->has_custom_width ? def->custom_width : 0; @@ -6958,6 +6962,24 @@ handle_district_definition_key (struct parsed_district_definition * def, if (strategy != NULL) free (strategy); + } else if (slice_matches_str (key, "ai_build_strategy")) { + char * strategy = copy_trimmed_string_or_null (value, 1); + if (strategy == NULL) { + def->has_ai_build_strategy = false; + add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); + } else if (strcmp (strategy, "district") == 0) { + def->ai_build_strategy = DABS_DISTRICT; + def->has_ai_build_strategy = true; + } else if (strcmp (strategy, "tile-improvement") == 0) { + def->ai_build_strategy = DABS_TILE_IMPROVEMENT; + def->has_ai_build_strategy = true; + } else { + def->has_ai_build_strategy = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected \"district\" or \"tile-improvement\")"); + } + if (strategy != NULL) + free (strategy); + } else if (slice_matches_str (key, "align_to_coast")) { struct string_slice val_slice = *value; int ival; @@ -28842,6 +28864,228 @@ ai_move_district_worker (Unit * worker, struct district_worker_record * rec) return false; } +bool +ai_worker_try_tile_improvement_district (Unit * worker) +{ + if (! is->current_config.enable_districts || worker == NULL) return false; + if (! is_worker (worker)) return false; + if (worker->Body.Auto_CityID < 0) return false; + + City * city = get_city_ptr (worker->Body.Auto_CityID); + if (city == NULL) return false; + if (city->Body.CivID != worker->Body.CivID) return false; + + int civ_id = worker->Body.CivID; + int tile_x = worker->Body.X; + int tile_y = worker->Body.Y; + Tile * tile = tile_at (tile_x, tile_y); + if ((tile == NULL) || (tile == p_null_tile)) return false; + if (tile->CityID >= 0) return false; + if (tile->vtable->m38_Get_Territory_OwnerID (tile) != civ_id) return false; + if (! city_radius_contains_tile (city, tile_x, tile_y)) return false; + if (get_district_instance (tile) != NULL) return false; + + // Check if a canal district could be built here, making sure there isn't already one nearby and + // that there are at least two different adjacent water bodies owned by the civ + if (is->current_config.enable_canal_districts && + can_build_district_on_tile (tile, CANAL_DISTRICT_ID, civ_id)) { + bool has_adjacent_canal = false; + FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { + if (tai.n == 0) + continue; + Tile * adj = tai.tile; + if ((adj == NULL) || (adj == p_null_tile)) + continue; + struct district_instance * adj_inst = get_district_instance (adj); + if ((adj_inst != NULL) && (adj_inst->district_type == CANAL_DISTRICT_ID)) { + has_adjacent_canal = true; + break; + } + } + + if (! has_adjacent_canal) { + int water_ids[2] = { -1, -1 }; + int water_count = 0; + FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { + if (tai.n == 0) + continue; + if (water_count >= 2) + break; + Tile * adj = tai.tile; + if ((adj == NULL) || (adj == p_null_tile)) + continue; + if (! adj->vtable->m35_Check_Is_Water (adj)) + continue; + if (adj->vtable->m38_Get_Territory_OwnerID (adj) != civ_id) + continue; + + int sea_id = adj->vtable->m46_Get_ContinentID (adj); + if (water_count == 0 || sea_id != water_ids[0]) { + water_ids[water_count] = sea_id; + water_count += 1; + } + } + + if (water_count >= 2) { + unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); + unsigned int removable_flags = overlay_flags & 0xfc; + tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); + if (removable_flags != 0) + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, removable_flags, tile_x, tile_y); + ensure_district_instance (tile, CANAL_DISTRICT_ID, tile_x, tile_y); + Unit_set_state (worker, __, UnitState_Build_Mines); + worker->Body.Job_ID = WJ_Build_Mines; + return true; + } + } + } + + // Check if a bridge district could be built here, making sure there isn't already one nearby and + // that there are at least two different adjacent land tiles owned by the civ + if (is->current_config.enable_bridge_districts && + can_build_district_on_tile (tile, BRIDGE_DISTRICT_ID, civ_id)) { + bool has_adjacent_bridge = false; + FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { + if (tai.n == 0) + continue; + Tile * adj = tai.tile; + if ((adj == NULL) || (adj == p_null_tile)) + continue; + struct district_instance * adj_inst = get_district_instance (adj); + if ((adj_inst != NULL) && (adj_inst->district_type == BRIDGE_DISTRICT_ID)) { + has_adjacent_bridge = true; + break; + } + } + + if (! has_adjacent_bridge) { + bool north_land = false; + bool south_land = false; + bool west_land = false; + bool east_land = false; + bool ne_land = false; + bool nw_land = false; + bool se_land = false; + bool sw_land = false; + + FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { + if (tai.n == 0) + continue; + int dx = 0, dy = 0; + neighbor_index_to_diff (tai.n, &dx, &dy); + bool is_land = tile_is_land (civ_id, tile_x + dx, tile_y + dy, false); + if (dx == 0 && dy == -2) north_land = is_land; + else if (dx == 0 && dy == 2) south_land = is_land; + else if (dx == -2 && dy == 0) west_land = is_land; + else if (dx == 2 && dy == 0) east_land = is_land; + else if (dx == 1 && dy == -1) ne_land = is_land; + else if (dx == -1 && dy == -1) nw_land = is_land; + else if (dx == 1 && dy == 1) se_land = is_land; + else if (dx == -1 && dy == 1) sw_land = is_land; + } + + bool has_land_pair = (north_land && south_land) || + (west_land && east_land) || + (ne_land && sw_land) || + (nw_land && se_land); + if (has_land_pair) { + unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); + unsigned int removable_flags = overlay_flags & 0xfc; + tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); + if (removable_flags != 0) + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, removable_flags, tile_x, tile_y); + ensure_district_instance (tile, BRIDGE_DISTRICT_ID, tile_x, tile_y); + Unit_set_state (worker, __, UnitState_Build_Mines); + worker->Body.Job_ID = WJ_Build_Mines; + return true; + } + } + } + + // Evaluate whether the best improvement here is irrigation, mines, or a district, using heuristics based on city needs + int food_weight = (city->Body.FoodIncome < city->Body.FoodRequired) ? 3 : 1; + int shield_weight = (city->Body.ProductionIncome < city->Body.Population.Size) ? 2 : 1; + int unhappy_percent = + city->Body.UnhappyNoReasonPercent + + city->Body.UnhappyCrowdedPercent + + city->Body.UnhappyWarWearinessPercent + + city->Body.UnhappyAgresssionPercent + + city->Body.UnhappyPropagandaPercent + + city->Body.UnhappyDraftPercent + + city->Body.UnhappyOppressionPercent + + city->Body.UnhappyThisCityImprovementsPercent + + city->Body.UnhappyOtherCityImprovementsPercent; + int happiness_weight = (unhappy_percent > 0) ? 2 : 1; + int gold_weight = 1; + int resource_boost = 5 * (food_weight + shield_weight + gold_weight + happiness_weight); + + int irrigation_score = INT_MIN; + if (Leader_can_do_worker_job (&leaders[civ_id], __, WJ_Irrigate, tile_x, tile_y, 0) && + ! tile->vtable->m17_Check_Irrigation (tile, __, 0)) { + int base_type = tile->vtable->m50_Get_Square_BaseType (tile); + int bonus = p_bic_data->TileTypes[base_type].IrrigationBonus; + if (bonus < 0) + bonus = 0; + irrigation_score = bonus * food_weight; + } + + int mine_score = INT_MIN; + if (Leader_can_do_worker_job (&leaders[civ_id], __, WJ_Build_Mines, tile_x, tile_y, 0) && + ! tile->vtable->m18_Check_Mines (tile, __, 0)) { + int base_type = tile->vtable->m50_Get_Square_BaseType (tile); + int bonus = p_bic_data->TileTypes[base_type].MiningBonus; + if (bonus < 0) + bonus = 0; + mine_score = bonus * shield_weight; + } + + int best_score = INT_MIN; + int best_district_id = -1; + for (int i = 0; i < is->district_count; i++) { + struct district_config const * cfg = &is->district_configs[i]; + if (cfg->ai_build_strategy != DABS_TILE_IMPROVEMENT) + continue; + if (! can_build_district_on_tile (tile, i, civ_id)) + continue; + + struct district_instance temp = {0}; + temp.district_type = i; + temp.tile_x = (short)tile_x; + temp.tile_y = (short)tile_y; + + int food = 0, shields = 0, gold = 0, science = 0, culture = 0, happiness = 0; + get_effective_district_yields (&temp, cfg, &food, &shields, &gold, &science, &culture, &happiness); + + int score = food * food_weight + shields * shield_weight + gold * gold_weight; + score += (science + culture) / 2; + score += happiness * happiness_weight; + if ((cfg->generated_resource_id >= 0) && + ! patch_City_has_resource (city, __, cfg->generated_resource_id)) + score += resource_boost; + + if (score > best_score) { + best_score = score; + best_district_id = i; + } + } + + if ((best_district_id >= 0) && + (best_score >= irrigation_score) && + (best_score >= mine_score)) { + unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); + unsigned int removable_flags = overlay_flags & 0xfc; + tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); + if (removable_flags != 0) + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, removable_flags, tile_x, tile_y); + ensure_district_instance (tile, best_district_id, tile_x, tile_y); + Unit_set_state (worker, __, UnitState_Build_Mines); + worker->Body.Job_ID = WJ_Build_Mines; + return true; + } + + return false; +} + void __fastcall patch_Unit_ai_move_terraformer (Unit * this) { @@ -28882,6 +29126,11 @@ patch_Unit_ai_move_terraformer (Unit * this) if (ai_move_district_worker (this, rec)) return; } + + if ((this->Body.Auto_CityID >= 0) && + ai_worker_try_tile_improvement_district (this)) + return; + } bool pop_else_caravan; From 1db5b82d7e00df4f800f6cb269ba65627c819b22 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 19 Jan 2026 08:44:27 -0800 Subject: [PATCH 216/356] Add algorithm for determining bodies of water connected by canals; Incorporate into port-tile identification logic for AI --- Notes/district_todos.md | 6 +- injected_code.c | 196 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 198 insertions(+), 4 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 882d70c0..4d75db04 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,12 +1,12 @@ - [x] Allow workers over water only if in radius of city that can build water district/wonder - - Add ai_build_strategy option and AI worker handling - - Add logic for determining if canal connects bodies of water for AI port-building + - [x] Add ai_build_strategy option and AI worker handling + - [x] Add logic for determining if canal connects bodies of water for AI port-building - Add cap on count of distribution hubs, destroy extra if conquered - AI navies target maritime districts - Keep irrigation/mine on tile if specified - Firm up logic for river district rendering - Bump great wall bombard eval scores - - Prevent AI building districts on resources + - [x] Prevent AI building districts on resources - Make sure AI workers remove great walls after they have metallury - Hoover Dam (use alt dir, special positioning b/c on river) diff --git a/injected_code.c b/injected_code.c index 01bbd3cf..f98ca9a7 100644 --- a/injected_code.c +++ b/injected_code.c @@ -9965,6 +9965,175 @@ compute_city_tile_yield_sum (City * city, int tile_x, int tile_y) return food + shields + commerce; } +int * +get_water_continent_ids_connected_via_canal (Tile * tile, int * out_count) +{ + if (out_count != NULL) + *out_count = 0; + if (! is->current_config.enable_canal_districts) + return NULL; + if ((tile == NULL) || (tile == p_null_tile)) + return NULL; + if (! tile->vtable->m35_Check_Is_Water (tile)) + return NULL; + + int origin_continent_id = tile->vtable->m46_Get_ContinentID (tile); + int continent_count = p_bic_data->Map.Continent_Count; + if ((origin_continent_id < 0) || (origin_continent_id >= continent_count)) + return NULL; + + Map * map = &p_bic_data->Map; + int tile_count = map->TileCount; + if (tile_count <= 0) + return NULL; + + // Use a BFS over completed canal tiles, starting from those touching the origin water body. + int * queue_xs = malloc (sizeof (*queue_xs) * tile_count); + int * queue_ys = malloc (sizeof (*queue_ys) * tile_count); + int * ids = malloc (sizeof (*ids) * continent_count); + bool * seen = calloc (continent_count, sizeof (*seen)); + if ((queue_xs == NULL) || (queue_ys == NULL) || (ids == NULL) || (seen == NULL)) { + if (queue_xs != NULL) + free (queue_xs); + if (queue_ys != NULL) + free (queue_ys); + if (ids != NULL) + free (ids); + if (seen != NULL) + free (seen); + return NULL; + } + + seen[origin_continent_id] = true; + + int const adj_dx[8] = { 0, 0, -2, 2, 1, 1, -1, -1 }; + int const adj_dy[8] = { -2, 2, 0, 0, -1, 1, -1, 1 }; + int head = 0; + int tail = 0; + + // Seed queue with completed canal districts adjacent to the origin water body. + for (int index = 0; index < tile_count; index++) { + Tile * cand = Map_get_tile (map, __, index); + if ((cand == NULL) || (cand == p_null_tile)) + continue; + + int cx = 0, cy = 0; + tile_index_to_coords (map, index, &cx, &cy); + if (! tile_has_district_at (cx, cy, CANAL_DISTRICT_ID)) + continue; + + struct district_instance * inst = get_district_instance (cand); + if ((inst == NULL) || (inst->district_type != CANAL_DISTRICT_ID) || + (! district_is_complete (cand, CANAL_DISTRICT_ID))) + continue; + + bool adjacent_to_origin = false; + for (int i = 0; i < 8; i++) { + int nx = cx + adj_dx[i]; + int ny = cy + adj_dy[i]; + wrap_tile_coords (map, &nx, &ny); + Tile * adj = tile_at (nx, ny); + if ((adj == NULL) || (adj == p_null_tile)) + continue; + if (! adj->vtable->m35_Check_Is_Water (adj)) + continue; + int adj_continent_id = adj->vtable->m46_Get_ContinentID (adj); + if (adj_continent_id == origin_continent_id) { + adjacent_to_origin = true; + break; + } + } + if (! adjacent_to_origin) + continue; + + bool seen_canal = false; + for (int j = 0; j < tail; j++) { + if ((queue_xs[j] == cx) && (queue_ys[j] == cy)) { + seen_canal = true; + break; + } + } + if (! seen_canal && (tail < tile_count)) { + queue_xs[tail] = cx; + queue_ys[tail] = cy; + tail++; + } + } + + int id_count = 0; + + while (head < tail) { + int cx = queue_xs[head]; + int cy = queue_ys[head]; + head++; + + // Record all water bodies adjacent to this canal tile. + for (int i = 0; i < 8; i++) { + int nx = cx + adj_dx[i]; + int ny = cy + adj_dy[i]; + wrap_tile_coords (map, &nx, &ny); + Tile * adj = tile_at (nx, ny); + if ((adj == NULL) || (adj == p_null_tile)) + continue; + if (! adj->vtable->m35_Check_Is_Water (adj)) + continue; + + int adj_continent_id = adj->vtable->m46_Get_ContinentID (adj); + if ((adj_continent_id < 0) || (adj_continent_id >= continent_count)) + continue; + if (! seen[adj_continent_id]) { + seen[adj_continent_id] = true; + ids[id_count] = adj_continent_id; + id_count++; + } + } + + // Traverse to neighboring completed canal districts. + for (int i = 0; i < 8; i++) { + int nx = cx + adj_dx[i]; + int ny = cy + adj_dy[i]; + wrap_tile_coords (map, &nx, &ny); + if (! tile_has_district_at (nx, ny, CANAL_DISTRICT_ID)) + continue; + + Tile * adj = tile_at (nx, ny); + if ((adj == NULL) || (adj == p_null_tile)) + continue; + + struct district_instance * inst = get_district_instance (adj); + if ((inst == NULL) || (inst->district_type != CANAL_DISTRICT_ID) || + (! district_is_complete (adj, CANAL_DISTRICT_ID))) + continue; + + bool seen_canal = false; + for (int j = 0; j < tail; j++) { + if ((queue_xs[j] == nx) && (queue_ys[j] == ny)) { + seen_canal = true; + break; + } + } + if (! seen_canal && (tail < tile_count)) { + queue_xs[tail] = nx; + queue_ys[tail] = ny; + tail++; + } + } + } + + free (queue_xs); + free (queue_ys); + free (seen); + + if (out_count != NULL) + *out_count = id_count; + if (id_count <= 0) { + free (ids); + return NULL; + } + + return ids; +} + Tile * find_tile_for_neighborhood_district (City * city, int * out_x, int * out_y) { @@ -10008,6 +10177,8 @@ find_tile_for_neighborhood_district (City * city, int * out_x, int * out_y) if (! tile_suitable_for_district (tile, NEIGHBORHOOD_DISTRICT_ID, city, NULL)) continue; + if (tile_has_resource (tile)) + continue; if (get_district_instance (tile) != NULL) continue; @@ -10072,6 +10243,8 @@ find_tile_for_port_district (City * city, int * out_x, int * out_y) if (! tile_suitable_for_district (tile, PORT_DISTRICT_ID, city, NULL)) continue; + if (tile_has_resource (tile)) + continue; if (get_district_instance (tile) != NULL) continue; if (! tile->vtable->m35_Check_Is_Water (tile)) @@ -10081,7 +10254,24 @@ find_tile_for_port_district (City * city, int * out_x, int * out_y) if ((continent_id < 0) || (continent_id >= p_bic_data->Map.Continent_Count)) continue; Continent * continent = &p_bic_data->Map.Continents[continent_id]; - if (continent->Body.TileCount < threshold_for_body_of_water && ! is->current_config.enable_canal_districts) + bool large_enough_body = (continent->Body.TileCount >= threshold_for_body_of_water); + if (! large_enough_body) { + int connected_count = 0; + int * connected_ids = get_water_continent_ids_connected_via_canal (tile, &connected_count); + for (int i = 0; i < connected_count; i++) { + int connected_id = connected_ids[i]; + if ((connected_id < 0) || (connected_id >= p_bic_data->Map.Continent_Count)) + continue; + Continent * connected_continent = &p_bic_data->Map.Continents[connected_id]; + if (connected_continent->Body.TileCount >= threshold_for_body_of_water) { + large_enough_body = true; + break; + } + } + if (connected_ids != NULL) + free (connected_ids); + } + if (! large_enough_body) continue; int yield = compute_city_tile_yield_sum (city, tri.tile_x, tri.tile_y); @@ -10158,6 +10348,8 @@ find_tile_for_wonder_district (City * city, int * out_x, int * out_y) Tile * tile = tri.tile; if (! tile_suitable_for_district (tile, WONDER_DISTRICT_ID, city, NULL)) continue; + if (tile_has_resource (tile)) + continue; if (! wonder_is_buildable_on_tile (tile, target_improv_id)) continue; @@ -10222,6 +10414,8 @@ find_tile_for_distribution_hub_district (City * city, int * out_x, int * out_y) bool has_resource; if (! tile_suitable_for_district (tile, DISTRIBUTION_HUB_DISTRICT_ID, city, &has_resource)) continue; + if (has_resource) + continue; int chebyshev = compute_wrapped_chebyshev_distance (tx, ty, city->Body.X, city->Body.Y); if (chebyshev <= 1) From 9ce4aa61da951e552ffa910dafc9488829e5666d Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 19 Jan 2026 08:56:23 -0800 Subject: [PATCH 217/356] Add flag and logic for capping # of distro hubs per civ --- C3X.h | 1 + Notes/district_todos.md | 4 ++-- default.c3x_config.ini | 3 ++- injected_code.c | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/C3X.h b/C3X.h index 953be355..a3c0000c 100644 --- a/C3X.h +++ b/C3X.h @@ -375,6 +375,7 @@ struct c3x_config { int distribution_hub_shield_yield_divisor; int ai_distribution_hub_build_strategy; int ai_ideal_distribution_hub_count_per_100_cities; + int max_distribution_hub_count_per_100_cities; int central_rail_hub_distribution_food_bonus_percent; int central_rail_hub_distribution_shield_bonus_percent; diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 4d75db04..4225905c 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,8 +1,8 @@ - [x] Allow workers over water only if in radius of city that can build water district/wonder - [x] Add ai_build_strategy option and AI worker handling - [x] Add logic for determining if canal connects bodies of water for AI port-building - - Add cap on count of distribution hubs, destroy extra if conquered - - AI navies target maritime districts + - [x] Add cap on count of distribution hubs, destroy extra if conquered + - [x] AI navies target maritime districts - Keep irrigation/mine on tile if specified - Firm up logic for river district rendering - Bump great wall bombard eval scores diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 9208bae7..0942ee1c 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -901,7 +901,8 @@ distribution_hub_yield_division_mode = scale-by-city-count ai_distribution_hub_build_strategy = auto distribution_hub_food_yield_divisor = 2 distribution_hub_shield_yield_divisor = 2 -ai_ideal_distribution_hub_count_per_100_cities = 25 +ai_ideal_distribution_hub_count_per_100_cities = 50 +max_distribution_hub_count_per_100_cities = 50 central_rail_hub_distribution_food_bonus_percent = 25 central_rail_hub_distribution_shield_bonus_percent = 25 diff --git a/injected_code.c b/injected_code.c index f98ca9a7..25d5ad7a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -10573,6 +10573,18 @@ leader_has_natural_wonder_prereq_in_territory (int civ_id, struct district_infos return false; } +int +count_distribution_hubs_for_civ (int civ_id) +{ + int current = 0; + FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { + struct distribution_hub_record * rec = (struct distribution_hub_record *)(long)tei.value; + if ((rec != NULL) && (rec->civ_id == civ_id)) + current++; + } + return current; +} + bool leader_can_build_district (Leader * leader, int district_id) { @@ -10595,6 +10607,20 @@ leader_can_build_district (Leader * leader, int district_id) if (! leader_has_natural_wonder_prereq_in_territory (leader->ID, info)) return false; + if (district_id == DISTRIBUTION_HUB_DISTRICT_ID) { + int max_per_100 = is->current_config.max_distribution_hub_count_per_100_cities; + if (max_per_100 > 0) { + int city_count = leader->Cities_Count; + int max_allowed = (city_count * max_per_100 + 99) / 100; + if (max_allowed <= 0) + return false; + + int current = count_distribution_hubs_for_civ (leader->ID); + if (current >= max_allowed) + return false; + } + } + if (cfg->has_buildable_by_civs) { bool civ_match = false; if (cfg->buildable_by_civ_count > 0) { @@ -14104,6 +14130,7 @@ patch_init_floating_point () {"distribution_hub_food_yield_divisor" , 1, offsetof (struct c3x_config, distribution_hub_food_yield_divisor)}, {"distribution_hub_shield_yield_divisor" , 1, offsetof (struct c3x_config, distribution_hub_shield_yield_divisor)}, {"ai_ideal_distribution_hub_count_per_100_cities" , 1, offsetof (struct c3x_config, ai_ideal_distribution_hub_count_per_100_cities)}, + {"max_distribution_hub_count_per_100_cities" , 50, offsetof (struct c3x_config, max_distribution_hub_count_per_100_cities)}, {"central_rail_hub_distribution_food_bonus_percent" , 25, offsetof (struct c3x_config, central_rail_hub_distribution_food_bonus_percent)}, {"central_rail_hub_distribution_shield_bonus_percent", 25, offsetof (struct c3x_config, central_rail_hub_distribution_shield_bonus_percent)}, {"neighborhood_needed_message_frequency" , 4, offsetof (struct c3x_config, neighborhood_needed_message_frequency)}, @@ -22374,6 +22401,12 @@ compute_auto_distribution_hub_goal (Leader * leader, int city_count) int desired = base_desired + unused_bonus + food_bonus + production_bonus; int max_reasonable = (city_count + 1) / 2; + int max_per_100 = is->current_config.max_distribution_hub_count_per_100_cities; + if (max_per_100 > 0) { + int capped = (city_count * max_per_100 + 99) / 100; + if (capped >= 0) + max_reasonable = capped; + } if (desired > max_reasonable) desired = max_reasonable; if (desired < 1) From 6bbef111fef76a954c6170e62bd2155822783a59 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 19 Jan 2026 09:38:42 -0800 Subject: [PATCH 218/356] Add button for central rail hub --- .../WorkerDistrictButtonsHighlighted.pcx | Bin 20055 -> 20544 bytes Art/Districts/WorkerDistrictButtonsNorm.pcx | Bin 29858 -> 30830 bytes .../WorkerDistrictButtonsRollover.pcx | Bin 26662 -> 28478 bytes C3X.h | 2 +- civ_prog_objects.csv | 1 + 5 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Art/Districts/WorkerDistrictButtonsHighlighted.pcx b/Art/Districts/WorkerDistrictButtonsHighlighted.pcx index 4dde6f4648c21bf48727967b94dd6674b047b83a..aad5e01cd82e55c304a574efedb0f87d1384e6ab 100644 GIT binary patch delta 852 zcmYk5ziSjh6vyYsW#-=A&Cbk?-X)%3VFl&8U& zn&c_J(HRLAg$~~No#3(|$!EkZH%86b=y!vI0g11~!^K)O(CSJboh_ZOgSWaO=;Z1# zF1Yhx6NB8?nA3rTU&^)PTo?}~TJIuY+%mXmhZvwFf7W=`XUdl*I&Ug4Il5hrIOs^c zXgpoy;2=_fXec#3Ho?V(dp&N^6|I3tvLh!aD>8fqdKLMwW6j#Q;fz=R1U*P(vWo4ZWZ6r*y0`DMA^=<2#9pX|kxrg2Av?YhF<$NScCTC!*PTO>$ea?>sM@{d7(X@ED z%(weTimmpEr70ff7+BGsayDTNRX3wnve@pQ>`(YWFl#X{;Pfd)V^R` j*7F~Mvl-jvGq%I`*_3}~GtT%2e47aNMGW6vHO=L}eKquM delta 348 zcmX@Gfbseq#tDO(07|$|X2I0*X{2Yz|(i@JZ diff --git a/Art/Districts/WorkerDistrictButtonsNorm.pcx b/Art/Districts/WorkerDistrictButtonsNorm.pcx index 032679eba6cf3d8a140f19aa2b60e0e801c951b5..a05e28a1d9cf22df9550b83c2eb30ed6f059f51b 100644 GIT binary patch delta 1110 zcmXX`YfM{p6mMa3o5*vCaYUD(?h%qjmwhyTGVz-+snNy{rWrOFbt#B8VYCXx6xK1W z-13KB;BqN!(9-(n(Ja8G#Bk0nEey#r-j69;V@!t`PM1-?)$=cYxcTSY^Lw0g{*N1v zjeAdx^0u$HC^y3weZSSSnd5oBKl3PBU>0Mo@gCbb{xU`LY}44sPUIA@$Hqd6=a@5R z#F${;=Dbc(f|ciX8|T@%+*c`zvv}?dd*@L8$q>qr*7so@)*uNx@C5#V)WmH_!`~S` z%dQ=oI;(RNubd8RofRl|_#!@7nuIhi!2@`vr(kyi7U6+8!(;5d!(-(FJRH*0j-HTg z^FVrvl%K&P_#O5!4NozJyEg`5Pr#oa{`nko_@^s~s)BOZA0hEcSjvj;)4GN!swXuO zgiT0fxXvbDm^hEz_W)s;8s3Htd_t}6lg%zBiL+7*4=XbqS@}`8VD47Xjarr@0}$mGSjA0} zKOl)U2^$BrB+Lum-qGpv=FcRzq@jwP=n02l7GmVL3R?tCX5BV%LwLi=AZ-35SR==# z-ZDGg4wN)W^?Ph`aO8j50(s8Sp#Vv zt>|`;uG&9pjgCgp=IOXyr%+9(kB@|5Au2@eFLj^d_6B#oB%rQ{JnHmf#McIE!`4u6 zyml-+1j|ss|Ch}$%iHd3i1I<5N_4Z=KHte&8Drzfa7KC-NS2y#eGUNjRhdV}! z%^%qN0$=pIu;11$hh=Q{-x(ID3kBCn&fN4RjajOxy4=+#n)joEkvd;kLB9+-`RWV` zzI!dk;ors6%-z zur``goGlmCy^`Q&-Eh@)i7HNz5giakBopkz;;&E6^Ge+?P*u}_?rag+yv0iSIc|ut z=$RAjQSrMB&y>*Joo8hwpA_HcXlUwl)y&pcy8?Fc7C1{jdSjJ3G1%lZL`#vE3c=9{;tzh~J$4(&q zX!2gpgJ60i*B&rEj~k@#(c~7M4PZKkcP*IK=UV`#zwpfj)0_CG1L?<;a|L?Aw6tJ1 zm_8-g1f-u#&J^kZ(?Y_HK>F$AwZhdv`q^YBkxC%_eDY%6BPb`4G7-ayhiL$A^>c7n#%wH diff --git a/Art/Districts/WorkerDistrictButtonsRollover.pcx b/Art/Districts/WorkerDistrictButtonsRollover.pcx index cd3e482b55d33ad8eeddc85fadf2bb7b9261d9e7..e08c33e87acb50065c524c21fa1e6c9d0720e9f0 100644 GIT binary patch delta 2174 zcmZWrO^6&-5S}&WKXDN+UILmx^x#P)9*iPlm=Q;&O$S^JErJj*crye%M55`~FA*=( zCt)B>L(8y-ZFh3%lTh_$dSyP}pW8|gjjBe6(x=)MzH$Jb^&GdgO!>sF$j+{I+mC+w`n-=IU%~6MD zN#p6$Bf3YoW*_g((_EzWx_+X<>gSxD06oaS_@Dj};&6IlZtw&P=qjto?H_xkjg@q9sff#17`_-27AL(oy zNhJX#!We}%7*iI)Ra)7|nJJ+=ZQvFt38B$%a6-$?bP@@#gYY4Mb0Aq37hCS5Ek7Ks zQbseB`c}Yx9TsL}ob<%9u@We=Pw8s~u&h?nUBiyNZh-Y{#r zyS839J{YgFV{JNtGKg$LQ5Rm^1@_!gjU`D@* z7Qlsk|F$2GRm0{6v0tl{Y*r=|fq4xU0!XuU{Xsegy5@gxb4E{mjV#O6!smXOfw}T@3$tLUd6M9CuhG)cPP6Q@_*!LnyHQXVbQ)p zFW>X1eSW~a^b5OhZ@+lVj?pQJ_CwR~d?Az>(2s|nf$vdfh;oK>_WVY~8>l|`MEDX0 z3TYs+EodR0+5UYI#7>}-dSDtgC_+qAreQ8WL1rzc%=TL75_Cwy7?uuWpwS>AvahAD z0dkM-I9F7$W#aI}4^*XwHtw)YFe2(;TLh2gS)R5~vmSd+=wMdB6XRG(N9zu_0CdwG z-yKmc%QQ>}Y=J7707^dXu;xd^4WM#amIv#-aaPn*H8{KMg^~g8GYr0ls!w;GIbwsU zWalLcR1s7pR~?6@O-WSu!3FDVEmYc7u_rAAgfAF(Y}YsM8D8qiK)z%ifdjiWXQy| zk;skm=AM(U2|+GGm8-2mwP>5@-=H3iODF`itQd9i9Bs*%PmN35_?|CcmpIB4_TfOW z$~aiTS08=eZDFrqo8DDRI~J_W7v+L{e(#A_FyNdSqFDkF1bVr&doeZ&hM5mPe9o`` zb9+BK*aA96hPW^Q4S?)qx=?{HSJBOIL!fPUaqq`3~rpE>?w8(?Klqhai;Agn525a*dS18>@_K<@JX1MWZjpOfd{RDotBFH{9O zBx_aqJ>x<}vDrc@3PIDEd-1?N_ooB9-4{lm-G>9@wK<>}BUTl+hd--^-BNZ%{&zSu T`o?w|T*(SIvddqOULN}wMSj{Z delta 347 zcmdmYk8#-r#tj^d><9n+|NZ~>!O8lJS#b7w#xyuPov8uNHefD+vp+JIu^&7K(YuMI z70&KpZG*ED*y`YHbM{U)sB&qJ7FLkPLzBOAbTj?`J^31EC);d@QTw^NV9dGPO(4lM zo@Nkbz}pC-zVX(As2zM@s+7MHYLbyaEtK_IpbW~ICzuRn*$QPrS+|5Tp{#P@G$@Nx zBo4~z6$yv3ctoQ)!EXG`aPar!^`dGZ%mxveT&pEHSwKtytn~0?UomsGtA7rkJN0LC Ix7g+g0L35G8UO$Q diff --git a/C3X.h b/C3X.h index a3c0000c..07e816b5 100644 --- a/C3X.h +++ b/C3X.h @@ -825,7 +825,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereq = "Steam Power", .resource_prereqs = {"Iron", "Coal"}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 2, .dependent_improvement_count = 0, .img_paths = {"CentralRailHub_AMER.pcx", "CentralRailHub_EURO.pcx", "CentralRailHub_ROMAN.pcx", "CentralRailHub_MIDEAST.pcx", "CentralRailHub_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, - .img_path_count = 5, .max_building_index = 1, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, + .img_path_count = 5, .max_building_index = 1, .btn_tile_sheet_column = 5, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index e74077e9..6acfab4a 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -814,6 +814,7 @@ define, 0x505B67, 0x50F931, 0x505C07, "ADDR_TRADABLE_UNITS_SIZE_TO_CLEAR", " repl vptr, 0x66A52C, 0x0, 0x0, "Map_Renderer_m09_Draw_Tile_Resources", "void (__fastcall *) (Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y)" define, 0x66A530, 0x0, 0x0, "Map_Renderer_m10_Draw_Tile_Mountains_Hills_Volcano", "void (__fastcall *) (Map_Renderer * this, int edx, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int flags)" repl vptr, 0x66A538, 0x687644, 0x66A538, "Map_Renderer_m12_Draw_Tile_Buildings", "void (__fastcall *) (Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y)" +define, 0x4C4060, 0x0, 0x0, "Map_Renderer_m38_Draw_Irrigation", "void (__fastcall *) (Map_Renderer * this, int edx, int terrain_index, int sprite_index, Map_Renderer * map_renderer, int pixel_x, int pixel_y)" repl call, 0x5F5580, 0x6053D2, 0x5F54B0, "Tile_has_city_or_district", "" repl call, 0x5F5816, 0x605639, 0x5F5746, "Tile_has_city_or_district", "" repl call, 0x5F5ABB, 0x6058AF, 0x5F59EB, "Tile_has_city_or_district", "" From f5aa648c54a064574788f2933f354ad739b5cc10 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 19 Jan 2026 10:17:42 -0800 Subject: [PATCH 219/356] Add inleads for right click menu functions --- civ_prog_objects.csv | 2 ++ injected_code.c | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 6acfab4a..1f487ecc 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -24,6 +24,8 @@ define, 0x4CD0B1, 0x4D5151, 0x4CD171, "ADDR_PEDIA_TEXTURE_BUG_PATCH", "void define, 0x5640AC, 0x570385, 0x56405C, "ADDR_AUTORAZE_BYPASS", "void *" repl vptr, 0x66CB50, 0x689C3C, 0x66CB50, "Leader_impl_would_raze_city", "bool (__fastcall *) (Leader * this, int edx, City * city)" repl vptr, 0x66B44C, 0x68854C, 0x66B44C, "Main_Screen_Form_handle_left_click_on_map_1", "void (__fastcall *) (Main_Screen_Form * this, int edx, int param_1, int param_2)" +inlead, 0x4EA210, 0x0, 0x0, "Main_Screen_Form_handle_right_click_on_tile", "void (__fastcall *) (Main_Screen_Form * this, int edx, int tile_x, int tile_y, int mouse_x, int mouse_y)" +inlead, 0x4E8850, 0x0, 0x0, "Main_Screen_Form_open_right_click_menu", "void (__fastcall *) (Main_Screen_Form * this, int edx, int tile_x, int tile_y, int mouse_x, int mouse_y)" define, 0x499FE0, 0x49F9F0, 0x49A070, "is_online_game", "char (__stdcall *) (void)" define, 0x437A70, 0x439620, 0x437AF0, "tile_at", "Tile * (__cdecl *) (int x, int y)" define, 0x426C80, 0x4283C0, 0x426D00, "TileUnits_TileUnitID_to_UnitID", "int (__fastcall *) (TileUnits * this, int edx, int tile_unit_id, int * out_UnitItem_field_0)" diff --git a/injected_code.c b/injected_code.c index 25d5ad7a..0ab6a0ea 100644 --- a/injected_code.c +++ b/injected_code.c @@ -20488,6 +20488,18 @@ patch_Map_Renderer_m71_Draw_Tiles (Map_Renderer * this, int edx, int param_1, in Map_Renderer_m71_Draw_Tiles (this, __, param_1, param_2, param_3); } +void __fastcall +patch_Main_Screen_Form_handle_right_click_on_tile (Main_Screen_Form * this, int edx, int tile_x, int tile_y, int mouse_x, int mouse_y) +{ + Main_Screen_Form_handle_right_click_on_tile (this, __, tile_x, tile_y, mouse_x, mouse_y); +} + +void __fastcall +patch_Main_Screen_Form_open_right_click_menu (Main_Screen_Form * this, int edx, int tile_x, int tile_y, int mouse_x, int mouse_y) +{ + Main_Screen_Form_open_right_click_menu (this, __, tile_x, tile_y, mouse_x, mouse_y); +} + void __fastcall patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Image * canvas) { From 20fb71a1ac4ff12be0af0ca696ac17a916a42de1 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 19 Jan 2026 11:36:20 -0800 Subject: [PATCH 220/356] Add support for named tiles --- C3X.h | 14 +- civ_prog_objects.csv | 1 + default.c3x_config.ini | 3 + injected_code.c | 389 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 371 insertions(+), 36 deletions(-) diff --git a/C3X.h b/C3X.h index 07e816b5..1de821c1 100644 --- a/C3X.h +++ b/C3X.h @@ -332,6 +332,8 @@ struct c3x_config { int per_extraterritorial_colony_relation_penalty; bool draw_forests_over_roads_and_railroads; + + bool enable_named_tiles; int day_night_cycle_mode; int elapsed_minutes_per_day_night_hour_transition; @@ -825,7 +827,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereq = "Steam Power", .resource_prereqs = {"Iron", "Coal"}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 2, .dependent_improvement_count = 0, .img_paths = {"CentralRailHub_AMER.pcx", "CentralRailHub_EURO.pcx", "CentralRailHub_ROMAN.pcx", "CentralRailHub_MIDEAST.pcx", "CentralRailHub_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, - .img_path_count = 5, .max_building_index = 1, .btn_tile_sheet_column = 5, .btn_tile_sheet_row = 0, + .img_path_count = 5, .max_building_index = 1, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, @@ -1105,6 +1107,12 @@ struct district_instance { struct natural_wonder_district_info natural_wonder_info; // Only used if district_type is a natural wonder district }; +struct named_tile_entry { + int tile_x; + int tile_y; + char name[24]; +}; + struct highlighted_city_radius_tile_info { int highlight_level; }; @@ -1248,6 +1256,9 @@ struct injected_state { struct table saved_code_areas; int * unit_menu_duplicates; // NULL initialized, allocated to an array of 0x100 ints when needed + bool named_tile_menu_active; + int named_tile_menu_tile_x; + int named_tile_menu_tile_y; // List of temporary ints. Initializes to NULL/0/0, used with functions "memoize" and "clear_memo" int * memo; @@ -1807,6 +1818,7 @@ struct district_button_image_set { // For wonder districts, the wonder_info field tracks wonder-specific state (unused, reserved, completed, ruined), // which city reserved/completed the wonder, and which wonder index is on this district. struct table district_tile_map; + struct table named_tile_map; // Tracks per-turn airlift usage for aerodrome districts (tile pointer -> civ bitmask). struct table aerodrome_airlift_usage; diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 1f487ecc..0d4a63d5 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -10,6 +10,7 @@ define, 0xB3CEA0, 0xB5F6A0, 0xB3CE60, "city_sprites", "Sprite *" define, 0xB3E7D0, 0xB60FD0, 0xB3E790, "destroyed_city_sprites", "Sprite *" define, 0x9F8700, 0xA1AF00, 0x9F86C0, "p_main_screen_form", "Main_Screen_Form *" define, 0xA52658, 0xA74E50, 0xA52618, "p_game", "Game *" +define, 0xCB8B38, 0x0, 0x0, "temp_ui_strs", "char[5][4096]" define, 0x665188, 0x68219C, 0x665188, "ADDR_ADDR_OUTPUTDEBUGSTRINGA", "void *" define, 0x665280, 0x6822E0, 0x665280, "ADDR_ADDR_GETASYNCKEYSTATE", "void *" define, 0x665168, 0x682130, 0x665168, "ADDR_ADDR_GETPROCADDRESS", "void *" diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 0942ee1c..8ab9e571 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -929,3 +929,6 @@ auto_build_great_wall_around_territory = true ; strategically place districts and improvements by showing which cities will be affected. The highlights update dynamically as you move the worker ; around the map. Only applies when enable_districts is set to true. enable_city_work_radii_highlights = true + + +enable_named_tiles = true \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index 0ab6a0ea..ff65d699 100644 --- a/injected_code.c +++ b/injected_code.c @@ -78,6 +78,8 @@ struct injected_state * is = ADDR_INJECTED_STATE; #define CANAL_DISTRICT_ID 9 #define GREAT_WALL_DISTRICT_ID 10 +enum { NAMED_TILE_MENU_ID = 0x90 }; + char const * const hotseat_replay_save_path = "Saves\\Auto\\ai-move-replay-before-interturn.SAV"; char const * const hotseat_resume_save_path = "Saves\\Auto\\ai-move-replay-resume.SAV"; @@ -214,6 +216,9 @@ void get_bridge_directions (Tile * tile, int tile_x, int tile_y, int * out_dir1, void get_canal_directions (Tile * tile, int tile_x, int tile_y, bool * out_water_dirs[9], int * out_dir1, int * out_dir2); Tile * find_tile_for_district (City * city, int district_id, int * out_x, int * out_y); struct district_instance * get_district_instance (Tile * tile); +bool tile_can_be_named (Tile * tile, int tile_x, int tile_y); +struct named_tile_entry * get_named_tile_entry (Tile * tile); +void handle_named_tile_menu_selection (void); bool city_has_required_district (City * city, int district_id); bool district_is_complete (Tile * tile, int district_id); bool city_requires_district_for_improvement (City * city, int improv_id, int * out_district_id); @@ -9435,6 +9440,12 @@ reset_district_state (bool reset_tile_map) free (inst); } table_deinit (&is->district_tile_map); + FOR_TABLE_ENTRIES (tei, &is->named_tile_map) { + struct named_tile_entry * entry = (struct named_tile_entry *)(long)tei.value; + if (entry != NULL) + free (entry); + } + table_deinit (&is->named_tile_map); } clear_distribution_hub_tables (); @@ -14068,6 +14079,7 @@ patch_init_floating_point () {"enable_wonder_districts" , false, offsetof (struct c3x_config, enable_wonder_districts)}, {"enable_natural_wonders" , false, offsetof (struct c3x_config, enable_natural_wonders)}, {"add_natural_wonders_to_scenarios_if_none" , false, offsetof (struct c3x_config, add_natural_wonders_to_scenarios_if_none)}, + {"enable_named_tiles" , false, offsetof (struct c3x_config, enable_named_tiles)}, {"enable_distribution_hub_districts" , false, offsetof (struct c3x_config, enable_distribution_hub_districts)}, {"enable_aerodrome_districts" , false, offsetof (struct c3x_config, enable_aerodrome_districts)}, {"enable_port_districts" , false, offsetof (struct c3x_config, enable_port_districts)}, @@ -19080,6 +19092,22 @@ patch_Context_Menu_open (Context_Menu * this, int edx, int x, int y, int param_3 int * p_stack = (int *)&x; int ret_addr = p_stack[-1]; + if (is->current_config.enable_named_tiles && + is->named_tile_menu_active) { + int tile_x = is->named_tile_menu_tile_x; + int tile_y = is->named_tile_menu_tile_y; + Tile * tile = tile_at (tile_x, tile_y); + if (tile_can_be_named (tile, tile_x, tile_y)) { + struct named_tile_entry * entry = get_named_tile_entry (tile); + char menu_text[64]; + if ((entry != NULL) && (entry->name[0] != '\0')) + snprintf (menu_text, sizeof menu_text, "Rename %s", entry->name); + else + strcpy (menu_text, "Name Tile"); + Context_Menu_add_item (this, __, NAMED_TILE_MENU_ID, menu_text, false, (Sprite *)0x0); + } + } + if (is->current_config.group_units_on_right_click_menu && (ret_addr == ADDR_OPEN_UNIT_MENU_RETURN) && (is->unit_menu_duplicates != NULL)) { @@ -20488,16 +20516,147 @@ patch_Map_Renderer_m71_Draw_Tiles (Map_Renderer * this, int edx, int param_1, in Map_Renderer_m71_Draw_Tiles (this, __, param_1, param_2, param_3); } +bool +tile_can_be_named (Tile * tile, int tile_x, int tile_y) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return false; + if (city_at (tile_x, tile_y) != NULL) + return false; + struct district_instance * inst = get_district_instance (tile); + if ((inst != NULL) && (inst->district_type == NATURAL_WONDER_DISTRICT_ID)) + return false; + return true; +} + +struct named_tile_entry * +get_named_tile_entry (Tile * tile) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return NULL; + int stored_ptr = 0; + if (! itable_look_up (&is->named_tile_map, (int)tile, &stored_ptr)) + return NULL; + return (struct named_tile_entry *)(long)stored_ptr; +} + +void +remove_named_tile_entry (Tile * tile) +{ + struct named_tile_entry * entry = get_named_tile_entry (tile); + if (entry == NULL) + return; + itable_remove (&is->named_tile_map, (int)tile); + free (entry); +} + +void +set_named_tile_entry (Tile * tile, int tile_x, int tile_y, char const * name) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return; + if ((name == NULL) || (name[0] == '\0')) { + remove_named_tile_entry (tile); + return; + } + + struct named_tile_entry * entry = get_named_tile_entry (tile); + if (entry == NULL) { + entry = calloc (1, sizeof *entry); + if (entry == NULL) + return; + itable_insert (&is->named_tile_map, (int)tile, (int)(long)entry); + } + entry->tile_x = tile_x; + entry->tile_y = tile_y; + strncpy (entry->name, name, sizeof entry->name); + entry->name[(sizeof entry->name) - 1] = '\0'; +} + +bool +prompt_for_named_tile (char const * seed_name, char * out_name, int out_len) +{ + if ((out_name == NULL) || (out_len <= 0)) + return false; + out_name[0] = '\0'; + + PopupForm * popup = get_popup_form (); + if (popup == NULL) + return false; + + char seed_buf[24]; + if (seed_name == NULL) + seed_name = ""; + strncpy (seed_buf, seed_name, sizeof seed_buf); + seed_buf[(sizeof seed_buf) - 1] = '\0'; + + set_popup_str_param (0, seed_buf, -1, -1); + popup->vtable->set_text_key_and_flags (popup, __, script_dot_txt_file_path, "RENAME_CITY", 0x17, (int)seed_buf, 0x44, 0); + int sel = patch_show_popup (popup, __, 0, 0); + if (sel != 0) + return false; + + if (temp_ui_strs[0][0] == '\0') + return true; + + strncpy (out_name, temp_ui_strs[0], out_len); + out_name[out_len - 1] = '\0'; + return true; +} + +void +handle_named_tile_menu_selection (void) +{ + int tile_x = is->named_tile_menu_tile_x; + int tile_y = is->named_tile_menu_tile_y; + Tile * tile = tile_at (tile_x, tile_y); + if (! tile_can_be_named (tile, tile_x, tile_y)) + return; + + struct named_tile_entry * entry = get_named_tile_entry (tile); + char const * current_name = (entry != NULL) ? entry->name : ""; + char new_name[24]; + if (! prompt_for_named_tile (current_name, new_name, sizeof new_name)) + return; + + set_named_tile_entry (tile, tile_x, tile_y, new_name); + p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); +} + void __fastcall patch_Main_Screen_Form_handle_right_click_on_tile (Main_Screen_Form * this, int edx, int tile_x, int tile_y, int mouse_x, int mouse_y) { + if (is->current_config.enable_named_tiles) { + Tile * tile = tile_at (tile_x, tile_y); + if (tile_can_be_named (tile, tile_x, tile_y)) { + is->named_tile_menu_active = true; + is->named_tile_menu_tile_x = tile_x; + is->named_tile_menu_tile_y = tile_y; + Main_Screen_Form_open_right_click_menu (this, __, tile_x, tile_y, mouse_x, mouse_y); + is->named_tile_menu_active = false; + return; + } + } + Main_Screen_Form_handle_right_click_on_tile (this, __, tile_x, tile_y, mouse_x, mouse_y); } void __fastcall patch_Main_Screen_Form_open_right_click_menu (Main_Screen_Form * this, int edx, int tile_x, int tile_y, int mouse_x, int mouse_y) { + bool set_active = false; + if (!is->named_tile_menu_active && is->current_config.enable_named_tiles) { + Tile * tile = tile_at (tile_x, tile_y); + if (tile_can_be_named (tile, tile_x, tile_y)) { + is->named_tile_menu_active = true; + is->named_tile_menu_tile_x = tile_x; + is->named_tile_menu_tile_y = tile_y; + set_active = true; + } + } Main_Screen_Form_open_right_click_menu (this, __, tile_x, tile_y, mouse_x, mouse_y); + if (set_active) + is->named_tile_menu_active = false; } void __fastcall @@ -20505,8 +20664,10 @@ patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Imag { Main_Screen_Form_draw_city_hud (this, __, canvas); - if (!is->current_config.enable_natural_wonders || - !is->current_config.show_natural_wonder_name_on_map) + bool draw_natural_wonders = is->current_config.enable_natural_wonders && + is->current_config.show_natural_wonder_name_on_map; + bool draw_named_tiles = is->current_config.enable_named_tiles; + if (!draw_natural_wonders && !draw_named_tiles) return; if (canvas == NULL) @@ -20515,47 +20676,99 @@ patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Imag if ((canvas == NULL) || (canvas->JGL.Image == NULL)) return; - FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { - struct district_instance * inst = (struct district_instance *)(long)tei.value; - if ((inst == NULL) || (inst->district_type != NATURAL_WONDER_DISTRICT_ID)) - continue; + if (draw_natural_wonders) { + FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { + struct district_instance * inst = (struct district_instance *)(long)tei.value; + if ((inst == NULL) || (inst->district_type != NATURAL_WONDER_DISTRICT_ID)) + continue; - int natural_id = inst->natural_wonder_info.natural_wonder_id; - if ((natural_id < 0) || (natural_id >= is->natural_wonder_count)) - continue; + int natural_id = inst->natural_wonder_info.natural_wonder_id; + if ((natural_id < 0) || (natural_id >= is->natural_wonder_count)) + continue; - struct natural_wonder_district_config const * nw_cfg = &is->natural_wonder_configs[natural_id]; - if ((nw_cfg == NULL) || (nw_cfg->name == NULL) || (nw_cfg->name[0] == '\0')) - continue; + struct natural_wonder_district_config const * nw_cfg = &is->natural_wonder_configs[natural_id]; + if ((nw_cfg == NULL) || (nw_cfg->name == NULL) || (nw_cfg->name[0] == '\0')) + continue; - Tile * tile = (Tile *)(long)tei.key; - if ((tile == NULL) || (tile == p_null_tile)) - continue; + Tile * tile = (Tile *)(long)tei.key; + if ((tile == NULL) || (tile == p_null_tile)) + continue; - int tile_x, tile_y; - if (!tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y)) - continue; + int tile_x, tile_y; + if (!tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y)) + continue; - int screen_x, screen_y; - Main_Screen_Form_tile_to_screen_coords (this, __, tile_x, tile_y, &screen_x, &screen_y); + int screen_x, screen_y; + Main_Screen_Form_tile_to_screen_coords (this, __, tile_x, tile_y, &screen_x, &screen_y); - int is_zoomed_out = (p_bic_data->is_zoomed_out != false); - int scale = is_zoomed_out ? 2 : 1; - int screen_width = 128 / scale; - int screen_height = 88 / scale; - int text_width = screen_width - (is_zoomed_out ? 4 : 8); - if (text_width < 12) - text_width = screen_width; + int is_zoomed_out = (p_bic_data->is_zoomed_out != false); + int scale = is_zoomed_out ? 2 : 1; + int screen_width = 128 / scale; + int screen_height = 88 / scale; + int text_width = screen_width - (is_zoomed_out ? 4 : 8); + if (text_width < 12) + text_width = screen_width; - int text_left = screen_x + (screen_width - text_width) / 2; - int y_offset = 88 - 64; - int draw_y = screen_y - y_offset; - int text_top = draw_y + screen_height + (is_zoomed_out ? 2 : 4); + int text_left = screen_x + (screen_width - text_width) / 2; + int y_offset = 88 - 64; + int draw_y = screen_y - y_offset; + int text_top = draw_y + screen_height + (is_zoomed_out ? 2 : 4); - Object_66C3FC * font = get_font (10, FSF_NONE); - if (font != NULL) { - PCX_Image_set_text_effects (canvas, __, 0x80FFFFFF, 0x80000000, 1, 1); - PCX_Image_draw_centered_text (canvas, __, font, (char *)nw_cfg->name, text_left, text_top - 10, text_width, strlen (nw_cfg->name)); + Object_66C3FC * font = get_font (10, FSF_NONE); + if (font != NULL) { + PCX_Image_set_text_effects (canvas, __, 0x80FFFFFF, 0x80000000, 1, 1); + PCX_Image_draw_centered_text (canvas, __, font, (char *)nw_cfg->name, text_left, text_top - 10, text_width, strlen (nw_cfg->name)); + } + } + } + + if (draw_named_tiles) { + FOR_TABLE_ENTRIES (tei, &is->named_tile_map) { + struct named_tile_entry * entry = (struct named_tile_entry *)(long)tei.value; + if ((entry == NULL) || (entry->name[0] == '\0')) + continue; + + Tile * tile = (Tile *)(long)tei.key; + if ((tile == NULL) || (tile == p_null_tile)) + continue; + + int tile_x, tile_y; + if (!tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y)) { + tile_x = entry->tile_x; + tile_y = entry->tile_y; + tile = tile_at (tile_x, tile_y); + if ((tile == NULL) || (tile == p_null_tile)) + continue; + } + + if (city_at (tile_x, tile_y) != NULL) + continue; + + struct district_instance * inst = get_district_instance (tile); + if ((inst != NULL) && (inst->district_type == NATURAL_WONDER_DISTRICT_ID)) + continue; + + int screen_x, screen_y; + Main_Screen_Form_tile_to_screen_coords (this, __, tile_x, tile_y, &screen_x, &screen_y); + + int is_zoomed_out = (p_bic_data->is_zoomed_out != false); + int scale = is_zoomed_out ? 2 : 1; + int screen_width = 128 / scale; + int screen_height = 88 / scale; + int text_width = screen_width - (is_zoomed_out ? 4 : 8); + if (text_width < 12) + text_width = screen_width; + + int text_left = screen_x + (screen_width - text_width) / 2; + int y_offset = 88 - 64; + int draw_y = screen_y - y_offset; + int text_top = draw_y + screen_height + (is_zoomed_out ? 2 : 4); + + Object_66C3FC * font = get_font (10, FSF_NONE); + if (font != NULL) { + PCX_Image_set_text_effects (canvas, __, 0x80FFFFFF, 0x80000000, 1, 1); + PCX_Image_draw_centered_text (canvas, __, font, entry->name, text_left, text_top - 10, text_width, strlen (entry->name)); + } } } } @@ -24620,6 +24833,13 @@ patch_Context_Menu_get_selected_item_on_unit_rcm (Context_Menu * this) // click unit items which have been disabled by the mod so they can interrupt the queued actions of units that have no moves left. int index = this->Selected_Item; if (index >= 0) { + if (is->current_config.enable_named_tiles && is->named_tile_menu_active) { + Context_Menu_Item * item = &this->Items[index]; + if (item->Menu_Item_ID == NAMED_TILE_MENU_ID) { + handle_named_tile_menu_selection (); + return -1; + } + } bool is_enabled = (this->Items[index].Status & 2) == 0; bool is_unit_item = (this->Items[index].Menu_Item_ID - (0x13 + p_bic_data->UnitTypeCount)) >= 0; return (is_enabled || is_unit_item) ? index : -1; @@ -25018,6 +25238,38 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi mod_data.length -= trimmed_bytes; } } + + if (is->current_config.enable_named_tiles && (is->named_tile_map.len > 0)) { + serialize_aligned_text ("named_tiles", &mod_data); + int entry_capacity = is->named_tile_map.len; + int bytes_per_entry = (int)sizeof(int) * 2 + (int)sizeof(((struct named_tile_entry *)0)->name); + byte * chunk = (byte *)buffer_allocate (&mod_data, (int)sizeof(int) + bytes_per_entry * entry_capacity); + int * count = (int *)chunk; + byte * out = (byte *)(count + 1); + int written = 0; + FOR_TABLE_ENTRIES (tei, &is->named_tile_map) { + struct named_tile_entry * entry = (struct named_tile_entry *)(long)tei.value; + if ((entry == NULL) || (entry->name[0] == '\0')) + continue; + Tile * tile = (Tile *)(long)tei.key; + int tile_x = entry->tile_x; + int tile_y = entry->tile_y; + if ((tile != NULL) && (tile != p_null_tile)) + tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y); + ((int *)out)[0] = tile_x; + ((int *)out)[1] = tile_y; + out += sizeof(int) * 2; + memcpy (out, entry->name, sizeof entry->name); + out += sizeof entry->name; + written++; + } + *count = written; + int unused_entries = entry_capacity - written; + if (unused_entries > 0) { + int trimmed_bytes = unused_entries * bytes_per_entry; + mod_data.length -= trimmed_bytes; + } + } } int metadata_size = (mod_data.length > 0) ? 12 : 0; // Two four-byte bookends plus one four-byte size, only written if there's any mod data @@ -25077,6 +25329,12 @@ patch_move_game_data (byte * buffer, bool save_else_load) free (inst); } table_deinit (&is->district_tile_map); + FOR_TABLE_ENTRIES (tei, &is->named_tile_map) { + struct named_tile_entry * entry = (struct named_tile_entry *)(long)tei.value; + if (entry != NULL) + free (entry); + } + table_deinit (&is->named_tile_map); clear_all_tracked_workers (); } @@ -25492,6 +25750,67 @@ patch_move_game_data (byte * buffer, bool save_else_load) break; } + } else if (match_save_chunk_name (&cursor, "named_tiles")) { + bool success = false; + int remaining_bytes = (seg + seg_size) - cursor; + if (remaining_bytes >= (int)sizeof(int)) { + int * ints = (int *)cursor; + int entry_count = *ints++; + cursor = (byte *)ints; + remaining_bytes -= (int)sizeof(int); + int bytes_per_entry = (int)sizeof(int) * 2 + (int)sizeof(((struct named_tile_entry *)0)->name); + if ((entry_count >= 0) && (remaining_bytes >= entry_count * bytes_per_entry)) { + table_deinit (&is->named_tile_map); + success = true; + for (int n = 0; n < entry_count; n++) { + if (remaining_bytes < bytes_per_entry) { + success = false; + break; + } + int tile_x = *ints++; + int tile_y = *ints++; + cursor = (byte *)ints; + remaining_bytes -= (int)sizeof(int) * 2; + + char name_buf[24]; + memcpy (name_buf, cursor, sizeof name_buf); + name_buf[(sizeof name_buf) - 1] = '\0'; + cursor += sizeof name_buf; + remaining_bytes -= sizeof name_buf; + ints = (int *)cursor; + + if (name_buf[0] == '\0') + continue; + Tile * tile = tile_at (tile_x, tile_y); + if ((tile == NULL) || (tile == p_null_tile)) + continue; + + struct named_tile_entry * entry = calloc (1, sizeof *entry); + if (entry == NULL) { + success = false; + break; + } + entry->tile_x = tile_x; + entry->tile_y = tile_y; + strncpy (entry->name, name_buf, sizeof entry->name); + entry->name[(sizeof entry->name) - 1] = '\0'; + itable_insert (&is->named_tile_map, (int)tile, (int)(long)entry); + } + if (! success) { + FOR_TABLE_ENTRIES (tei, &is->named_tile_map) { + struct named_tile_entry * entry = (struct named_tile_entry *)(long)tei.value; + if (entry != NULL) + free (entry); + } + table_deinit (&is->named_tile_map); + } + } + } + if (! success) { + error_chunk_name = "named_tiles"; + break; + } + } else if (match_save_chunk_name (&cursor, "district_config_names")) { bool success = false; bool mismatch_found = false; From cbc69c3f58d8d957f766d63326d57853cb2fa7ad Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 19 Jan 2026 12:33:50 -0800 Subject: [PATCH 221/356] Update named tile header --- C3X.h | 4 ++++ Notes/district_todos.md | 1 + Text/c3x-labels.txt | 4 ++++ injected_code.c | 12 +++++++----- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/C3X.h b/C3X.h index 1de821c1..e2889ede 100644 --- a/C3X.h +++ b/C3X.h @@ -531,6 +531,10 @@ enum c3x_label { CL_DISTRICTS_IN_SAVE_FILE, CL_CURRENTLY_CONFIGURED_DISTRICTS, + // Tile naming + CL_NAME_TILE, + CL_RENAME_TILE, + // "Action" for passenger units CL_TRANSPORTED, diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 4225905c..b1bf85b1 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -3,6 +3,7 @@ - [x] Add logic for determining if canal connects bodies of water for AI port-building - [x] Add cap on count of distribution hubs, destroy extra if conquered - [x] AI navies target maritime districts + - [x] Named tiles (still need to allow for scenarios) - Keep irrigation/mine on tile if specified - Firm up logic for river district rendering - Bump great wall bombard eval scores diff --git a/Text/c3x-labels.txt b/Text/c3x-labels.txt index 56fc4cda..dbb7fb63 100644 --- a/Text/c3x-labels.txt +++ b/Text/c3x-labels.txt @@ -140,6 +140,10 @@ There may be other errors as well. Districts in save file Currently configured districts from +; For naming tiles +Name Tile +Rename + ; This appears instead of the above actions on units that have been loaded into another (except an army). Transported diff --git a/injected_code.c b/injected_code.c index ff65d699..cf21814b 100644 --- a/injected_code.c +++ b/injected_code.c @@ -14079,7 +14079,7 @@ patch_init_floating_point () {"enable_wonder_districts" , false, offsetof (struct c3x_config, enable_wonder_districts)}, {"enable_natural_wonders" , false, offsetof (struct c3x_config, enable_natural_wonders)}, {"add_natural_wonders_to_scenarios_if_none" , false, offsetof (struct c3x_config, add_natural_wonders_to_scenarios_if_none)}, - {"enable_named_tiles" , false, offsetof (struct c3x_config, enable_named_tiles)}, + {"enable_named_tiles" , false, offsetof (struct c3x_config, enable_named_tiles)}, {"enable_distribution_hub_districts" , false, offsetof (struct c3x_config, enable_distribution_hub_districts)}, {"enable_aerodrome_districts" , false, offsetof (struct c3x_config, enable_aerodrome_districts)}, {"enable_port_districts" , false, offsetof (struct c3x_config, enable_port_districts)}, @@ -19101,9 +19101,9 @@ patch_Context_Menu_open (Context_Menu * this, int edx, int x, int y, int param_3 struct named_tile_entry * entry = get_named_tile_entry (tile); char menu_text[64]; if ((entry != NULL) && (entry->name[0] != '\0')) - snprintf (menu_text, sizeof menu_text, "Rename %s", entry->name); + snprintf (menu_text, sizeof menu_text, "%s %s", is->c3x_labels[CL_RENAME_TILE], entry->name); else - strcpy (menu_text, "Name Tile"); + strcpy (menu_text,is->c3x_labels[CL_NAME_TILE]); Context_Menu_add_item (this, __, NAMED_TILE_MENU_ID, menu_text, false, (Sprite *)0x0); } } @@ -20589,6 +20589,8 @@ prompt_for_named_tile (char const * seed_name, char * out_name, int out_len) seed_name = ""; strncpy (seed_buf, seed_name, sizeof seed_buf); seed_buf[(sizeof seed_buf) - 1] = '\0'; + if (seed_buf[0] == '\0') + strncpy (seed_buf, "Tile", sizeof seed_buf); set_popup_str_param (0, seed_buf, -1, -1); popup->vtable->set_text_key_and_flags (popup, __, script_dot_txt_file_path, "RENAME_CITY", 0x17, (int)seed_buf, 0x44, 0); @@ -20754,13 +20756,13 @@ patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Imag int is_zoomed_out = (p_bic_data->is_zoomed_out != false); int scale = is_zoomed_out ? 2 : 1; int screen_width = 128 / scale; - int screen_height = 88 / scale; + int screen_height = 64 / scale; int text_width = screen_width - (is_zoomed_out ? 4 : 8); if (text_width < 12) text_width = screen_width; int text_left = screen_x + (screen_width - text_width) / 2; - int y_offset = 88 - 64; + int y_offset = 32; int draw_y = screen_y - y_offset; int text_top = draw_y + screen_height + (is_zoomed_out ? 2 : 4); From 1f4ede5100639c7a2b68ce223b4245f7146d753b Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 19 Jan 2026 12:37:13 -0800 Subject: [PATCH 222/356] Refactor map tile text drawing --- injected_code.c | 60 ++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/injected_code.c b/injected_code.c index cf21814b..b22f3d1c 100644 --- a/injected_code.c +++ b/injected_code.c @@ -20661,6 +20661,28 @@ patch_Main_Screen_Form_open_right_click_menu (Main_Screen_Form * this, int edx, is->named_tile_menu_active = false; } +void +draw_map_tile_text (Main_Screen_Form * this, PCX_Image * canvas, char * text, int screen_x, int screen_y, int base_screen_height, int y_offset) +{ + int is_zoomed_out = (p_bic_data->is_zoomed_out != false); + int scale = is_zoomed_out ? 2 : 1; + int screen_width = 128 / scale; + int screen_height = base_screen_height / scale; + int text_width = screen_width - (is_zoomed_out ? 4 : 8); + if (text_width < 12) + text_width = screen_width; + + int text_left = screen_x + (screen_width - text_width) / 2; + int draw_y = screen_y - y_offset; + int text_top = draw_y + screen_height + (is_zoomed_out ? 2 : 4); + + Object_66C3FC * font = get_font (10, FSF_NONE); + if (font != NULL) { + PCX_Image_set_text_effects (canvas, __, 0x80FFFFFF, 0x80000000, 1, 1); + PCX_Image_draw_centered_text (canvas, __, font, text, text_left, text_top - 10, text_width, strlen (text)); + } +} + void __fastcall patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Image * canvas) { @@ -20703,24 +20725,7 @@ patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Imag int screen_x, screen_y; Main_Screen_Form_tile_to_screen_coords (this, __, tile_x, tile_y, &screen_x, &screen_y); - int is_zoomed_out = (p_bic_data->is_zoomed_out != false); - int scale = is_zoomed_out ? 2 : 1; - int screen_width = 128 / scale; - int screen_height = 88 / scale; - int text_width = screen_width - (is_zoomed_out ? 4 : 8); - if (text_width < 12) - text_width = screen_width; - - int text_left = screen_x + (screen_width - text_width) / 2; - int y_offset = 88 - 64; - int draw_y = screen_y - y_offset; - int text_top = draw_y + screen_height + (is_zoomed_out ? 2 : 4); - - Object_66C3FC * font = get_font (10, FSF_NONE); - if (font != NULL) { - PCX_Image_set_text_effects (canvas, __, 0x80FFFFFF, 0x80000000, 1, 1); - PCX_Image_draw_centered_text (canvas, __, font, (char *)nw_cfg->name, text_left, text_top - 10, text_width, strlen (nw_cfg->name)); - } + draw_map_tile_text (this, canvas, (char *)nw_cfg->name, screen_x, screen_y, 88, 88 - 64); } } @@ -20753,24 +20758,7 @@ patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Imag int screen_x, screen_y; Main_Screen_Form_tile_to_screen_coords (this, __, tile_x, tile_y, &screen_x, &screen_y); - int is_zoomed_out = (p_bic_data->is_zoomed_out != false); - int scale = is_zoomed_out ? 2 : 1; - int screen_width = 128 / scale; - int screen_height = 64 / scale; - int text_width = screen_width - (is_zoomed_out ? 4 : 8); - if (text_width < 12) - text_width = screen_width; - - int text_left = screen_x + (screen_width - text_width) / 2; - int y_offset = 32; - int draw_y = screen_y - y_offset; - int text_top = draw_y + screen_height + (is_zoomed_out ? 2 : 4); - - Object_66C3FC * font = get_font (10, FSF_NONE); - if (font != NULL) { - PCX_Image_set_text_effects (canvas, __, 0x80FFFFFF, 0x80000000, 1, 1); - PCX_Image_draw_centered_text (canvas, __, font, entry->name, text_left, text_top - 10, text_width, strlen (entry->name)); - } + draw_map_tile_text (this, canvas, entry->name, screen_x, screen_y, 64, 32); } } } From f100710ea21e960410870db17af3075b838ce03b Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 19 Jan 2026 17:33:41 -0800 Subject: [PATCH 223/356] Allow naming of city tiles --- C3X.h | 4 ++-- injected_code.c | 27 +++++++++++++-------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/C3X.h b/C3X.h index e2889ede..1a701ebc 100644 --- a/C3X.h +++ b/C3X.h @@ -1114,7 +1114,7 @@ struct district_instance { struct named_tile_entry { int tile_x; int tile_y; - char name[24]; + char name[100]; }; struct highlighted_city_radius_tile_info { @@ -1931,7 +1931,7 @@ struct district_button_image_set { // Set to true once the auto-build process for the Great Wall is complete to avoid running it again enum great_wall_auto_build_state great_wall_auto_build; - Tile * great_wall_focus_tile; + Tile * focused_tile; // ========== // } diff --git a/injected_code.c b/injected_code.c index b22f3d1c..a0d75479 100644 --- a/injected_code.c +++ b/injected_code.c @@ -19610,7 +19610,7 @@ patch_Map_Renderer_m19_Draw_Tile_by_XY_and_Flags (Map_Renderer * this, int edx, // If focusing on a tile after Great Wall completed, highlight the tile while getting user confirmation if (is->current_config.auto_build_great_wall_around_territory && is->great_wall_auto_build == GWABS_RUNNING && - is->great_wall_focus_tile == tile) { + is->focused_tile == tile) { Sprite_draw_on_map (&is->tile_highlights[10], __, this, pixel_x, pixel_y, 1, 1, 1, 0); } } @@ -20220,7 +20220,7 @@ auto_build_great_wall_districts_for_civ (int civ_id) existing_district_id = inst->district_type; if (is_human) { - is->great_wall_focus_tile = tile; + is->focused_tile = tile; Main_Screen_Form_bring_tile_into_view (p_main_screen_form, __, x, y, 0, true, false); char * popup_key = "C3X_CONFIRM_BUILD_GREAT_WALL"; @@ -20283,7 +20283,7 @@ auto_build_great_wall_districts_for_civ (int civ_id) } is->great_wall_auto_build = GWABS_DONE; - is->great_wall_focus_tile = NULL; + is->focused_tile = NULL; } //We need to forwards-declare this @@ -20521,8 +20521,6 @@ tile_can_be_named (Tile * tile, int tile_x, int tile_y) { if ((tile == NULL) || (tile == p_null_tile)) return false; - if (city_at (tile_x, tile_y) != NULL) - return false; struct district_instance * inst = get_district_instance (tile); if ((inst != NULL) && (inst->district_type == NATURAL_WONDER_DISTRICT_ID)) return false; @@ -20584,7 +20582,7 @@ prompt_for_named_tile (char const * seed_name, char * out_name, int out_len) if (popup == NULL) return false; - char seed_buf[24]; + char seed_buf[101]; if (seed_name == NULL) seed_name = ""; strncpy (seed_buf, seed_name, sizeof seed_buf); @@ -20593,7 +20591,7 @@ prompt_for_named_tile (char const * seed_name, char * out_name, int out_len) strncpy (seed_buf, "Tile", sizeof seed_buf); set_popup_str_param (0, seed_buf, -1, -1); - popup->vtable->set_text_key_and_flags (popup, __, script_dot_txt_file_path, "RENAME_CITY", 0x17, (int)seed_buf, 0x44, 0); + popup->vtable->set_text_key_and_flags (popup, __, script_dot_txt_file_path, "RENAME_CITY", 0x64, (int)seed_buf, 0x44, 0); int sel = patch_show_popup (popup, __, 0, 0); if (sel != 0) return false; @@ -20617,8 +20615,12 @@ handle_named_tile_menu_selection (void) struct named_tile_entry * entry = get_named_tile_entry (tile); char const * current_name = (entry != NULL) ? entry->name : ""; - char new_name[24]; - if (! prompt_for_named_tile (current_name, new_name, sizeof new_name)) + char new_name[100]; + is->focused_tile = tile; + p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); + bool got_name = prompt_for_named_tile (current_name, new_name, sizeof new_name); + is->focused_tile = NULL; + if (! got_name) return; set_named_tile_entry (tile, tile_x, tile_y, new_name); @@ -20748,9 +20750,6 @@ patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Imag continue; } - if (city_at (tile_x, tile_y) != NULL) - continue; - struct district_instance * inst = get_district_instance (tile); if ((inst != NULL) && (inst->district_type == NATURAL_WONDER_DISTRICT_ID)) continue; @@ -20758,7 +20757,7 @@ patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Imag int screen_x, screen_y; Main_Screen_Form_tile_to_screen_coords (this, __, tile_x, tile_y, &screen_x, &screen_y); - draw_map_tile_text (this, canvas, entry->name, screen_x, screen_y, 64, 32); + draw_map_tile_text (this, canvas, entry->name, screen_x, screen_y, 64, 4); } } } @@ -25762,7 +25761,7 @@ patch_move_game_data (byte * buffer, bool save_else_load) cursor = (byte *)ints; remaining_bytes -= (int)sizeof(int) * 2; - char name_buf[24]; + char name_buf[101]; memcpy (name_buf, cursor, sizeof name_buf); name_buf[(sizeof name_buf) - 1] = '\0'; cursor += sizeof name_buf; From 28f7c9d4118399368cf601bf4709f20609f6066e Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 19 Jan 2026 20:02:43 -0800 Subject: [PATCH 224/356] Add separator bar before "Name Tile", tile highlighting --- civ_prog_objects.csv | 1 + injected_code.c | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 0d4a63d5..f32c8859 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -143,6 +143,7 @@ inlead, 0x61A480, 0x63B9F0, 0x61A3B0, "Context_Menu_open", "int (__fastcal define, 0x4E993C, 0x4F273A, 0x4E99FC, "ADDR_OPEN_UNIT_MENU_RETURN", "int" define, 0x61B0C0, 0x63C940, 0x61AFF0, "Context_Menu_widen_for_text", "void (__fastcall *) (Context_Menu * this, int edx, char * text)" inlead, 0x61A110, 0x63B420, 0x61A040, "Context_Menu_add_item_and_set_color", "int (__fastcall *) (Context_Menu * this, int edx, int item_id, char * text, int red)" +define, 0x61A160, 0x0, 0x0, "Context_Menu_add_separator", "int (__fastcall *) (Context_Menu * this, int edx, int item_id)" inlead, 0x45C750, 0x45EA70, 0x45C7D0, "Unit_ai_move_terraformer", "void (__fastcall *) (Unit * this)" inlead, 0x4B1DC0, 0x4B8D50, 0x4B1E50, "City_requires_improvement_to_grow", "int (__fastcall *) (City * this)" inlead, 0x4DD530, 0x4E5F00, 0x4DD5F0, "maybe_show_improvement_needed_for_growth_dialog", "void (__fastcall *) (void * this, int edx, City * city, int * param_2)" diff --git a/injected_code.c b/injected_code.c index a0d75479..2dae707e 100644 --- a/injected_code.c +++ b/injected_code.c @@ -19104,6 +19104,8 @@ patch_Context_Menu_open (Context_Menu * this, int edx, int x, int y, int param_3 snprintf (menu_text, sizeof menu_text, "%s %s", is->c3x_labels[CL_RENAME_TILE], entry->name); else strcpy (menu_text,is->c3x_labels[CL_NAME_TILE]); + if (this->Item_Count > 0) + Context_Menu_add_separator (this, __, 0); Context_Menu_add_item (this, __, NAMED_TILE_MENU_ID, menu_text, false, (Sprite *)0x0); } } @@ -19608,10 +19610,8 @@ patch_Map_Renderer_m19_Draw_Tile_by_XY_and_Flags (Map_Renderer * this, int edx, } // If focusing on a tile after Great Wall completed, highlight the tile while getting user confirmation - if (is->current_config.auto_build_great_wall_around_territory && - is->great_wall_auto_build == GWABS_RUNNING && - is->focused_tile == tile) { - Sprite_draw_on_map (&is->tile_highlights[10], __, this, pixel_x, pixel_y, 1, 1, 1, 0); + if (is->focused_tile != NULL && is->focused_tile == tile) { + Sprite_draw_on_map (&is->tile_highlights[10], __, this, pixel_x, pixel_y, 1, 1, 1, 0); } } } @@ -20535,7 +20535,7 @@ get_named_tile_entry (Tile * tile) int stored_ptr = 0; if (! itable_look_up (&is->named_tile_map, (int)tile, &stored_ptr)) return NULL; - return (struct named_tile_entry *)(long)stored_ptr; + return (struct named_tile_entry *)stored_ptr; } void @@ -20593,6 +20593,7 @@ prompt_for_named_tile (char const * seed_name, char * out_name, int out_len) set_popup_str_param (0, seed_buf, -1, -1); popup->vtable->set_text_key_and_flags (popup, __, script_dot_txt_file_path, "RENAME_CITY", 0x64, (int)seed_buf, 0x44, 0); int sel = patch_show_popup (popup, __, 0, 0); + is->focused_tile = NULL; if (sel != 0) return false; @@ -20613,6 +20614,8 @@ handle_named_tile_menu_selection (void) if (! tile_can_be_named (tile, tile_x, tile_y)) return; + init_tile_highlights (); + struct named_tile_entry * entry = get_named_tile_entry (tile); char const * current_name = (entry != NULL) ? entry->name : ""; char new_name[100]; @@ -20620,6 +20623,7 @@ handle_named_tile_menu_selection (void) p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); bool got_name = prompt_for_named_tile (current_name, new_name, sizeof new_name); is->focused_tile = NULL; + p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); if (! got_name) return; @@ -20757,7 +20761,7 @@ patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Imag int screen_x, screen_y; Main_Screen_Form_tile_to_screen_coords (this, __, tile_x, tile_y, &screen_x, &screen_y); - draw_map_tile_text (this, canvas, entry->name, screen_x, screen_y, 64, 4); + draw_map_tile_text (this, canvas, entry->name, screen_x, screen_y, 64, 3); } } } From d12183098559b5937e73399f4b607b323fac7f92 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 19 Jan 2026 20:20:09 -0800 Subject: [PATCH 225/356] Add support for parsing named tiles from scenario files; Ensure scenario files can be loaded with districts/natural wonders/named tiles alone and parsed accordingly --- C3X.h | 8 +++ injected_code.c | 172 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 168 insertions(+), 12 deletions(-) diff --git a/C3X.h b/C3X.h index 1a701ebc..747fc356 100644 --- a/C3X.h +++ b/C3X.h @@ -1045,6 +1045,14 @@ struct scenario_district_entry { int has_wonder_name; }; +struct scenario_named_tile_entry { + int tile_x; + int tile_y; + int has_coordinates; + char * name; + int has_name; +}; + struct distribution_hub_record { Tile * tile; int tile_x; diff --git a/injected_code.c b/injected_code.c index 2dae707e..e57a626e 100644 --- a/injected_code.c +++ b/injected_code.c @@ -218,7 +218,6 @@ Tile * find_tile_for_district (City * city, int district_id, int * out_x, int * struct district_instance * get_district_instance (Tile * tile); bool tile_can_be_named (Tile * tile, int tile_x, int tile_y); struct named_tile_entry * get_named_tile_entry (Tile * tile); -void handle_named_tile_menu_selection (void); bool city_has_required_district (City * city, int district_id); bool district_is_complete (Tile * tile, int district_id); bool city_requires_district_for_improvement (City * city, int improv_id, int * out_district_id); @@ -236,8 +235,8 @@ void init_district_icons (); int count_neighborhoods_in_city_radius (City * city); int count_utilized_neighborhoods_in_city_radius (City * city); bool move_matches_directions (int move_dx, int move_dy, int dir1, int dir2); -int count_contiguous_canal_districts (int tile_x, int tile_y, int max_count); bool great_wall_blocks_civ (Tile * tile, int civ_id); +void set_named_tile_entry (Tile * tile, int tile_x, int tile_y, char const * name); struct pause_for_popup { bool done; // Set to true to exit for loop @@ -8987,6 +8986,15 @@ init_scenario_district_entry (struct scenario_district_entry * entry) memset (entry, 0, sizeof *entry); } +void +init_scenario_named_tile_entry (struct scenario_named_tile_entry * entry) +{ + if (entry == NULL) + return; + + memset (entry, 0, sizeof *entry); +} + void free_scenario_district_entry (struct scenario_district_entry * entry) { @@ -9011,6 +9019,20 @@ free_scenario_district_entry (struct scenario_district_entry * entry) entry->has_wonder_name = 0; } +void +free_scenario_named_tile_entry (struct scenario_named_tile_entry * entry) +{ + if (entry == NULL) + return; + + if (entry->name != NULL) { + free (entry->name); + entry->name = NULL; + } + entry->has_coordinates = 0; + entry->has_name = 0; +} + void add_scenario_district_error (struct error_line ** parse_errors, int line_number, @@ -9061,6 +9083,12 @@ finalize_scenario_district_entry (struct scenario_district_entry * entry, if ((entry == NULL) || (parse_errors == NULL)) return 0; + if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { + free_scenario_district_entry (entry); + init_scenario_district_entry (entry); + return 1; + } + if (! entry->has_coordinates) add_scenario_district_error (parse_errors, section_start_line, "coordinates (value is required)"); if ((! entry->has_district_name) || (entry->district_name == NULL) || (entry->district_name[0] == '\0')) @@ -9086,6 +9114,16 @@ finalize_scenario_district_entry (struct scenario_district_entry * entry, add_scenario_district_error (parse_errors, section_start_line, msg); success = 0; } else { + if ((district_id == NATURAL_WONDER_DISTRICT_ID) && ! is->current_config.enable_natural_wonders) { + free_scenario_district_entry (entry); + init_scenario_district_entry (entry); + return 1; + } + if ((district_id != NATURAL_WONDER_DISTRICT_ID) && ! is->current_config.enable_districts) { + free_scenario_district_entry (entry); + init_scenario_district_entry (entry); + return 1; + } struct district_instance * inst = ensure_district_instance (tile, district_id, map_x, map_y); if (inst == NULL) { add_scenario_district_error (parse_errors, section_start_line, "Failed to create district instance"); @@ -9159,6 +9197,51 @@ finalize_scenario_district_entry (struct scenario_district_entry * entry, return success; } +int +finalize_scenario_named_tile_entry (struct scenario_named_tile_entry * entry, + int section_start_line, + struct error_line ** parse_errors) +{ + int success = 1; + if ((entry == NULL) || (parse_errors == NULL)) + return 0; + + if (! is->current_config.enable_named_tiles) { + free_scenario_named_tile_entry (entry); + init_scenario_named_tile_entry (entry); + return 1; + } + + if (! entry->has_coordinates) + add_scenario_district_error (parse_errors, section_start_line, "coordinates (value is required)"); + if ((! entry->has_name) || (entry->name == NULL) || (entry->name[0] == '\0')) + add_scenario_district_error (parse_errors, section_start_line, "name (value is required)"); + + if ((! entry->has_coordinates) || (! entry->has_name) || + (entry->name == NULL) || (entry->name[0] == '\0')) + success = 0; + + int map_x = entry->tile_x; + int map_y = entry->tile_y; + if (success) { + wrap_tile_coords (&p_bic_data->Map, &map_x, &map_y); + Tile * tile = tile_at (map_x, map_y); + if ((tile == NULL) || (tile == p_null_tile)) { + add_scenario_district_error (parse_errors, section_start_line, "Invalid coordinates (tile not found)"); + success = 0; + } else if (! tile_can_be_named (tile, map_x, map_y)) { + add_scenario_district_error (parse_errors, section_start_line, "Invalid coordinates (tile cannot be named)"); + success = 0; + } else { + set_named_tile_entry (tile, map_x, map_y, entry->name); + } + } + + free_scenario_named_tile_entry (entry); + init_scenario_named_tile_entry (entry); + return success; +} + void handle_scenario_district_key (struct scenario_district_entry * entry, struct string_slice const * key, @@ -9209,6 +9292,40 @@ handle_scenario_district_key (struct scenario_district_entry * entry, add_unrecognized_key_error (unrecognized_keys, line_number, key); } +void +handle_scenario_named_tile_key (struct scenario_named_tile_entry * entry, + struct string_slice const * key, + struct string_slice const * value, + int line_number, + struct error_line ** parse_errors, + struct error_line ** unrecognized_keys) +{ + if ((entry == NULL) || (key == NULL) || (value == NULL)) + return; + + if (slice_matches_str (key, "coordinates")) { + int x, y; + if (parse_scenario_district_coordinates (value, &x, &y)) { + entry->tile_x = x; + entry->tile_y = y; + entry->has_coordinates = 1; + } else + add_scenario_district_error (parse_errors, line_number, "coordinates (expected format: x,y)"); + + } else if (slice_matches_str (key, "name")) { + if (entry->name != NULL) { + free (entry->name); + entry->name = NULL; + } + entry->name = copy_trimmed_string_or_null (value, 1); + entry->has_name = (entry->name != NULL); + if (! entry->has_name) + add_scenario_district_error (parse_errors, line_number, "name (value is required)"); + + } else + add_unrecognized_key_error (unrecognized_keys, line_number, key); +} + // Parses a .c3x.txt file corresponding to the given scenario file path, loading district instances as specified. // Attempts the scenario_path first, then scenario_config_path; if neither yields a readable file, no action is taken. // @@ -9231,6 +9348,10 @@ handle_scenario_district_key (struct scenario_district_entry * entry, // coordinates = 10,30 // district = Natural Wonder // wonder_name = Mount Everest +// +// #NamedTile +// coordinates = 41,23 +// name = Tiber River // ``` // // Details at https://github.com/instafluff0/Civ3_Editor_Fork_for_C3X_Districts @@ -9249,9 +9370,12 @@ load_scenario_districts_from_file () return; struct scenario_district_entry entry; + struct scenario_named_tile_entry named_entry; init_scenario_district_entry (&entry); + init_scenario_named_tile_entry (&named_entry); int in_section = 0; int section_start_line = 0; + int section_type = 0; int line_number = 0; int header_seen = 0; struct error_line * unrecognized_keys = NULL; @@ -9278,15 +9402,12 @@ load_scenario_districts_from_file () continue; } + // Keep support for legacy header, technically not needed if (! header_seen) { if (slice_matches_str (&trimmed, "DISTRICTS")) { header_seen = 1; cursor = has_newline ? line_end + 1 : line_end; continue; - } else { - add_scenario_district_error (&parse_errors, line_number, "Expected \"DISTRICTS\" header"); - header_seen = 1; - // Fall through to allow parsing of entries even if header missing. } } @@ -9296,12 +9417,29 @@ load_scenario_districts_from_file () directive.len -= 1; directive = trim_string_slice (&directive, 0); if (slice_matches_str (&directive, "District")) { - if (in_section) - finalize_scenario_district_entry (&entry, section_start_line, &parse_errors); + if (in_section) { + if (section_type == 1) + finalize_scenario_district_entry (&entry, section_start_line, &parse_errors); + else if (section_type == 2) + finalize_scenario_named_tile_entry (&named_entry, section_start_line, &parse_errors); + } in_section = 1; + section_type = 1; section_start_line = line_number; free_scenario_district_entry (&entry); init_scenario_district_entry (&entry); + } else if (slice_matches_str (&directive, "NamedTile")) { + if (in_section) { + if (section_type == 1) + finalize_scenario_district_entry (&entry, section_start_line, &parse_errors); + else if (section_type == 2) + finalize_scenario_named_tile_entry (&named_entry, section_start_line, &parse_errors); + } + in_section = 1; + section_type = 2; + section_start_line = line_number; + free_scenario_named_tile_entry (&named_entry); + init_scenario_named_tile_entry (&named_entry); } cursor = has_newline ? line_end + 1 : line_end; continue; @@ -9322,17 +9460,25 @@ load_scenario_districts_from_file () add_scenario_district_error (&parse_errors, line_number, "(missing key)"); break; case KVP_SUCCESS: - handle_scenario_district_key (&entry, &key_slice, &value_slice, line_number, &parse_errors, &unrecognized_keys); + if (section_type == 1) + handle_scenario_district_key (&entry, &key_slice, &value_slice, line_number, &parse_errors, &unrecognized_keys); + else if (section_type == 2) + handle_scenario_named_tile_key (&named_entry, &key_slice, &value_slice, line_number, &parse_errors, &unrecognized_keys); break; } cursor = has_newline ? line_end + 1 : line_end; } - if (in_section) - finalize_scenario_district_entry (&entry, section_start_line, &parse_errors); + if (in_section) { + if (section_type == 1) + finalize_scenario_district_entry (&entry, section_start_line, &parse_errors); + else if (section_type == 2) + finalize_scenario_named_tile_entry (&named_entry, section_start_line, &parse_errors); + } free_scenario_district_entry (&entry); + free_scenario_named_tile_entry (&named_entry); free (text); // Append to loaded config names list @@ -24777,7 +24923,9 @@ patch_Map_place_scenario_things (Map * this) patch_City_recompute_yields_and_happiness (city); } - if (is->current_config.enable_districts) + if (is->current_config.enable_districts || + is->current_config.enable_natural_wonders || + is->current_config.enable_named_tiles) load_scenario_districts_from_file (); if (is->current_config.add_natural_wonders_to_scenarios_if_none && From 664a54b8bb547a85cfc7efb07141967ed33e519d Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 20 Jan 2026 11:16:57 -0800 Subject: [PATCH 226/356] Add support for allow_irrigation_from flag; Refactor and simplify forests over roads drawing logic --- C3X.h | 11 +++++- Notes/district_todos.md | 10 ++--- civ_prog_objects.csv | 3 +- injected_code.c | 85 +++++++++++++++++++++++++++++++++-------- 4 files changed, 86 insertions(+), 23 deletions(-) diff --git a/C3X.h b/C3X.h index 747fc356..c76bed6f 100644 --- a/C3X.h +++ b/C3X.h @@ -663,6 +663,7 @@ struct district_config { bool is_dynamic; bool align_to_coast; bool draw_over_resources; + bool allow_irrigation_from; int custom_width; int custom_height; int x_offset; @@ -899,6 +900,7 @@ struct parsed_district_definition { enum district_ai_build_strategy ai_build_strategy; bool align_to_coast; bool draw_over_resources; + bool allow_irrigation_from; int custom_width; int custom_height; int x_offset; @@ -975,6 +977,7 @@ struct parsed_district_definition { int buildable_by_civ_cultures_id_count; bool has_buildable_by_civ_cultures; bool has_buildable_on_districts; + bool has_allow_irrigation_from; }; struct parsed_wonder_definition { @@ -1934,8 +1937,12 @@ struct district_button_image_set { // Stores the parameters to Unit::can_load while it's running, NULL otherwise. Unit * can_load_transport, * can_load_passenger; - // Used in patch_Map_Renderer_m08_Draw_Tile_Forests_Jungle_Swamp. Tracks the current tile coordinates being rendered, then for drawing forests over roads/railroad - int current_tile_x, current_tile_y; + // Current tile being rendered in Map_Renderer_m19_Draw_Tile_by_XY_and_Flags. For use within various Map_Renderer::impl_m*_Draw_* functions. Nulled out after the render function is complete + Tile * current_render_tile; + int current_render_tile_x, current_render_tile_y; + + // Used in patch_Map_Renderer_m08_Draw_Tile_Forests_Jungle_Swamp and so on for flagging whether to draw forests over roads on the tile being rendered + bool draw_forests_over_roads_on_tile; // Set to true once the auto-build process for the Great Wall is complete to avoid running it again enum great_wall_auto_build_state great_wall_auto_build; diff --git a/Notes/district_todos.md b/Notes/district_todos.md index b1bf85b1..65677a0a 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,13 +1,8 @@ - [x] Allow workers over water only if in radius of city that can build water district/wonder - [x] Add ai_build_strategy option and AI worker handling - - [x] Add logic for determining if canal connects bodies of water for AI port-building - - [x] Add cap on count of distribution hubs, destroy extra if conquered - [x] AI navies target maritime districts - - [x] Named tiles (still need to allow for scenarios) - - Keep irrigation/mine on tile if specified - Firm up logic for river district rendering - Bump great wall bombard eval scores - - [x] Prevent AI building districts on resources - Make sure AI workers remove great walls after they have metallury - Hoover Dam (use alt dir, special positioning b/c on river) @@ -29,6 +24,11 @@ ## Maritime Districts - ~~Choose terrain type~~ + - ~~Named tiles~~ + - ~~Prevent AI building districts on resources~~ + - ~~Add allow_irrigation_from flag~~ + - ~~Add logic for determining if canal connects bodies of water for AI port-building~~ + - ~~Add cap on count of distribution hubs, destroy extra if conquered~~ - ~~Add districts flag for drawing resources under districts~~ - ~~Add support for alternative render strategy (Municipal District use case)~~ - ~~Add support for buildable_on_districts~~ diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index f32c8859..23bc9eff 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -214,6 +214,7 @@ inlead, 0x4ACF40, 0x4B3F20, 0x4ACFD0, "City_add_or_remove_improvement", "voi define, 0x57E943, 0x58B68E, 0x57E6A3, "ADDR_RESOURCE_TILE_COUNT_MASK", "void *" define, 0x57E5EB, 0x58B328, 0x57E34B, "ADDR_RESOURCE_TILE_COUNT_ZERO_COMPARE", "void *" define, 0x5DA1A0, 0x5E9710, 0x5DA0D0, "Tile_get_resource_visible_to", "int (__fastcall *) (Tile * this, int edx, int civ_id)" +inlead, 0x5EA7f0, 0x0, 0x0, "Tile_m17_Check_Irrigation", "bool (__fastcall *) (Tile * this, int edx, int visible_to_civ_id)" define, 0x5D16A0, 0x5E0900, 0x5D15D0, "Map_get_tile", "Tile * (__fastcall *) (Map * this, int edx, int index)" repl call, 0x57E602, 0x58B345, 0x57E362, "Map_get_tile_when_recomputing_resources_1", "" repl call, 0x57E629, 0x58B36C, 0x57E389, "Map_get_tile_when_recomputing_resources_2", "" @@ -818,7 +819,7 @@ define, 0x505B67, 0x50F931, 0x505C07, "ADDR_TRADABLE_UNITS_SIZE_TO_CLEAR", " repl vptr, 0x66A52C, 0x0, 0x0, "Map_Renderer_m09_Draw_Tile_Resources", "void (__fastcall *) (Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y)" define, 0x66A530, 0x0, 0x0, "Map_Renderer_m10_Draw_Tile_Mountains_Hills_Volcano", "void (__fastcall *) (Map_Renderer * this, int edx, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int flags)" repl vptr, 0x66A538, 0x687644, 0x66A538, "Map_Renderer_m12_Draw_Tile_Buildings", "void (__fastcall *) (Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y)" -define, 0x4C4060, 0x0, 0x0, "Map_Renderer_m38_Draw_Irrigation", "void (__fastcall *) (Map_Renderer * this, int edx, int terrain_index, int sprite_index, Map_Renderer * map_renderer, int pixel_x, int pixel_y)" +inlead, 0x5F61A0, 0x0, 0x0, "Map_Renderer_m11_Draw_Tile_Irrigation", "void (__fastcall *) (Map_Renderer *this, int edx, int visible_to_civ, int tile_x, int tile_y, int param_4, int param_5, int param_6)" repl call, 0x5F5580, 0x6053D2, 0x5F54B0, "Tile_has_city_or_district", "" repl call, 0x5F5816, 0x605639, 0x5F5746, "Tile_has_city_or_district", "" repl call, 0x5F5ABB, 0x6058AF, 0x5F59EB, "Tile_has_city_or_district", "" diff --git a/injected_code.c b/injected_code.c index e57a626e..d674f981 100644 --- a/injected_code.c +++ b/injected_code.c @@ -6197,6 +6197,8 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->align_to_coast = def->align_to_coast; if (def->has_draw_over_resources) cfg->draw_over_resources = def->draw_over_resources; + if (def->has_allow_irrigation_from) + cfg->allow_irrigation_from = def->allow_irrigation_from; if (def->has_custom_width) cfg->custom_width = def->custom_width; if (def->has_custom_height) @@ -6463,6 +6465,7 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.ai_build_strategy = def->has_ai_build_strategy ? def->ai_build_strategy : DABS_DISTRICT; new_cfg.align_to_coast = def->has_align_to_coast ? def->align_to_coast : false; new_cfg.draw_over_resources = def->has_draw_over_resources ? def->draw_over_resources : false; + new_cfg.allow_irrigation_from = def->has_allow_irrigation_from ? def->allow_irrigation_from : false; new_cfg.custom_width = def->has_custom_width ? def->custom_width : 0; new_cfg.custom_height = def->has_custom_height ? def->custom_height : 0; new_cfg.x_offset = def->has_x_offset ? def->x_offset : 0; @@ -7002,6 +7005,15 @@ handle_district_definition_key (struct parsed_district_definition * def, } else add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "allow_irrigation_from")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->allow_irrigation_from = (ival != 0); + def->has_allow_irrigation_from = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "custom_width")) { struct string_slice val_slice = *value; int ival; @@ -19715,10 +19727,18 @@ patch_Sprite_draw_on_map (Sprite * this, int edx, Map_Renderer * map_renderer, i void __fastcall patch_Map_Renderer_m19_Draw_Tile_by_XY_and_Flags (Map_Renderer * this, int edx, int param_1, int pixel_x, int pixel_y, Map_Renderer * map_renderer, int param_5, int tile_x, int tile_y, int param_8) { - Map_Renderer_m19_Draw_Tile_by_XY_and_Flags (this, __, param_1, pixel_x, pixel_y, map_renderer, param_5, tile_x, tile_y, param_8); - Map * map = &p_bic_data->Map; Tile * tile = tile_at (tile_x, tile_y); + is->current_render_tile = tile; + is->current_render_tile_x = tile_x; + is->current_render_tile_y = tile_y; + + Map_Renderer_m19_Draw_Tile_by_XY_and_Flags (this, __, param_1, pixel_x, pixel_y, map_renderer, param_5, tile_x, tile_y, param_8); + + is->current_render_tile = NULL; + is->current_render_tile_x = -1; + is->current_render_tile_y = -1; + if ((is->city_loc_display_perspective >= 0) && (! map->vtable->m10_Get_Map_Zoom (map)) && // Turn off display when zoomed out. Need another set of highlight images for that. ((1 << is->city_loc_display_perspective) & *p_player_bits) && @@ -19774,12 +19794,9 @@ patch_Map_Renderer_m08_Draw_Tile_Forests_Jungle_Swamp (Map_Renderer * this, int if ((tile == NULL) || (tile == p_null_tile)) return; - is->current_tile_x = -1; - is->current_tile_y = -1; if ((tile->vtable->m50_Get_Square_BaseType (tile) == SQ_Forest) && (*tile->vtable->m25_Check_Roads)(tile, __, 0)) { - is->current_tile_x = tile_x; - is->current_tile_y = tile_y; + is->draw_forests_over_roads_on_tile = true; return; } @@ -19791,13 +19808,10 @@ patch_Map_Renderer_m52_Draw_Roads (Map_Renderer * this, int edx, int image_index { Map_Renderer_m52_Draw_Roads (this, __, image_index, map_renderer, pixel_x, pixel_y); - if (! is->current_config.draw_forests_over_roads_and_railroads || - is->current_tile_x == -1 || is->current_tile_y == -1) + if (! is->current_config.draw_forests_over_roads_and_railroads || ! is->draw_forests_over_roads_on_tile) return; - // Current tile x & y will only have coordinates if we have a forest (per check in patch_Map_Renderer_m08_Draw_Tile_Forests_Jungle_Swamp), - // so go ahead and render the forest on top of the road here. - Map_Renderer_m08_Draw_Tile_Forests_Jungle_Swamp (this, __, is->current_tile_x, is->current_tile_y, map_renderer, pixel_x, pixel_y); + Map_Renderer_m08_Draw_Tile_Forests_Jungle_Swamp (this, __, is->current_render_tile_x, is->current_render_tile_y, map_renderer, pixel_x, pixel_y); } void __fastcall @@ -19805,12 +19819,10 @@ patch_Map_Renderer_m52_Draw_Railroads (Map_Renderer * this, int edx, int image_i { Map_Renderer_m52_Draw_Railroads (this, __, image_index, map_renderer, pixel_x, pixel_y); - if (! is->current_config.draw_forests_over_roads_and_railroads - || is->current_tile_x == -1 || is->current_tile_y == -1) + if (! is->current_config.draw_forests_over_roads_and_railroads || ! is->draw_forests_over_roads_on_tile) return; - // patch_Map_Renderer_m08_Draw_Tile_Forests_Jungle_Swamp sets x & y only if we have a forest, so render on top of railroad - Map_Renderer_m08_Draw_Tile_Forests_Jungle_Swamp (this, __, is->current_tile_x, is->current_tile_y, map_renderer, pixel_x, pixel_y); + Map_Renderer_m08_Draw_Tile_Forests_Jungle_Swamp (this, __, is->current_render_tile_x, is->current_render_tile_y, map_renderer, pixel_x, pixel_y); } void __fastcall @@ -29326,6 +29338,28 @@ patch_Map_Renderer_m09_Draw_Tile_Resources (Map_Renderer * this, int edx, int vi draw_district_generated_resource_on_tile (this, tile, inst, tile_x, tile_y, map_renderer, pixel_x, pixel_y, visible_to_civ_id); } +void __fastcall +patch_Map_Renderer_m11_Draw_Tile_Irrigation (Map_Renderer *this, int edx, int visible_to_civ, int tile_x, int tile_y, int param_4, int param_5, int param_6) +{ + if (! is->current_config.enable_districts) { + Map_Renderer_m11_Draw_Tile_Irrigation (this, __, visible_to_civ, tile_x, tile_y, param_4, param_5, param_6); + return; + } + + struct district_instance * inst = get_district_instance (is->current_render_tile); + if (inst == NULL || inst->district_type < 0 || inst->district_type >= is->district_count) { + Map_Renderer_m11_Draw_Tile_Irrigation (this, __, visible_to_civ, tile_x, tile_y, param_4, param_5, param_6); + return; + } + + // If it has a completed district that serves as pseudo-irrigation source, suppress drawing irrigation + if (is->district_configs[inst->district_type].allow_irrigation_from + && district_is_complete (is->current_render_tile, inst->district_type)) + return; + + Map_Renderer_m11_Draw_Tile_Irrigation (this, __, visible_to_civ, tile_x, tile_y, param_4, param_5, param_6); +} + bool __fastcall patch_Tile_has_city_or_district (Tile * this) { @@ -31296,5 +31330,26 @@ patch_Leader_get_attitude_toward (Leader * this, int edx, int civ_id, int param_ return score; } +bool __fastcall +patch_Tile_m17_Check_Irrigation (Tile * this, int edx, int visible_to_civ_id) +{ + bool base = Tile_m17_Check_Irrigation (this, __, visible_to_civ_id); + if (base) + return true; + + if (! is->current_config.enable_districts) + return base; + + struct district_instance * inst = get_district_instance (this); + if (inst == NULL || inst->district_type < 0 || inst->district_type >= is->district_count) + return base; + + if (is->district_configs[inst->district_type].allow_irrigation_from && + district_is_complete (this, inst->district_type)) + return true; + + return base; +} + // TCC requires a main function be defined even though it's never used. int main () { return 0; } From 496f111d7f3c728f8e2c027233d49e4c3ac25bd2 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 20 Jan 2026 12:51:40 -0800 Subject: [PATCH 227/356] Update config for central rail hub button --- .../WorkerDistrictButtonsHighlighted.pcx | Bin 20544 -> 20634 bytes C3X.h | 2 +- Notes/district_todos.md | 2 +- default.districts_config.txt | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Art/Districts/WorkerDistrictButtonsHighlighted.pcx b/Art/Districts/WorkerDistrictButtonsHighlighted.pcx index aad5e01cd82e55c304a574efedb0f87d1384e6ab..0cd325d06319a5640e57269b147cda50f63f7772 100644 GIT binary patch delta 440 zcmXYtKT1PE5QoW*$v8=t+4V&{g6GgqJc6Be7FI$0Q>YM#RtC1S(hG>-1!VR;0}3{t zK(Mp2uoZu6Ui096-^_1j_UX2BbJuxzbyCO5LJV=9-rQth!D1D?2Gg`dS22iri$1G2 zlv&K_o2Dxo#EZwr)Q3uQ$Z5KiqJpxaLt&-R_3kWW@mXq=8e$AEcTW^$K$KZ?tb?Vk z-dSLDim`AbT1U0?-n$qz!daL!0C5Q^VvkQD$iy>wV-;5R4a66pR}iH`42t}MA1}{V z+a&O!NHW%pX-zKU!r&>8Ptt(i%cRY527pP~3)bA|Un9q`JqDEd2&!?brH}qp>(bF5Qn*o$!1I@vrE7S@G&ebwD%#r&ca$e@DChBD-Q-NthBJO6KpJHcC!fz z3S#98*bA0|lN1&UvmYP7-^|s`o5RTD%mRXM!GE362)Duk3ZAH5`_>%0olm2?6??#?Z^Ljc1i(6 zk^DQ2nPVIn3^7B~T7FV>fM5gydO}5a`Eo>AFdno)(GHx^XrY(@!y1vlU_sA%nMzb^ zq^koTtzNiZvywCs

y@<@KVedhm%iEs)hqi64st8^J)Py{)c;;5=`a`*{nUQ5U%_ V_p*0D4d+eVO<$FThg Date: Tue, 20 Jan 2026 12:56:59 -0800 Subject: [PATCH 228/356] Add button art and config for energy grid --- .../WorkerDistrictButtonsHighlighted.pcx | Bin 20634 -> 21140 bytes Art/Districts/WorkerDistrictButtonsNorm.pcx | Bin 30830 -> 31663 bytes C3X.h | 2 +- default.districts_config.txt | 4 ++-- 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Art/Districts/WorkerDistrictButtonsHighlighted.pcx b/Art/Districts/WorkerDistrictButtonsHighlighted.pcx index 0cd325d06319a5640e57269b147cda50f63f7772..5994ad226302f1fd34333a4c0d9d417f4e381655 100644 GIT binary patch delta 803 zcmXAny=zoK6vf}nY~Iby=I+cmiJKLIwIB%o0~$dr(up<}skD()f~B2BEFvy1F+sGD zU>OY9Tg-d=na#(dvCtx0*x1-vSm~Lhc|7iK&YXMZ-Z$^n#s~Fklm6+bCn_uU?Dh7+ zLi@X@N&DB``WKxy2Eo!$Pl_ArUa_VCiY@iP54DOqcOm*waMA<+qgH)iXHiq|&L0Lh z(?#`>eqskGIUD^^aNI%dhhBF*U4zc~T>=E~n>Xf6 z9}QR%_}N}4&pUiRv(roMQk)mVptvFoFb#UioMxNYtmr=lY9gsm+ih z8Dt>a@ljmJlX7|XS*y|+4RGlhWW0EyXLA_MK1~$1q)o{LoPNAq@%P;D~+3OpcP7V#6nm)Z0yIz z`Z>Y0Voqf~YPp@rl=|B2-+syy~ delta 350 zcmbQTlyTNV#tBo|9y0xBWMr6pQBY#?c|ne;mYF_xZiqKn?|ZH9%fwvY$NH`wT#aKY0 XH922Un*AEnF~+kDmp4xkWN`!l^9Oma diff --git a/Art/Districts/WorkerDistrictButtonsNorm.pcx b/Art/Districts/WorkerDistrictButtonsNorm.pcx index a05e28a1d9cf22df9550b83c2eb30ed6f059f51b..8d6e63058d90e709fae7c0964295504e618ea4fd 100644 GIT binary patch delta 1657 zcmYjRT})GF7%u4M&zy?C%eJdsc;S+Hv1FMs*@BrGX&?hHdNWtWUU&+nu$j&F7;U#A z)Sg2Re4KXc;k4kO?P-nyRK~!nyzc=-nK4A1|8WIH(h2I^=I-3v>cx`gq{;Jq&-c9V z^PDsPq@eqGf%FF(TDP*`QS*E&+qixyD_y^AVOBSdvD@o+vj+tquxP=eA{dT$8UuLz z`)_*s;2}JnW$Oyhu!CF-=Ef0xl-yL#BsRYpCkz~LL#py(`@7G{aa*-SNM?R3t>c& z0N^=FY4S36yc6bgBFTiRgk-6EV6dml6-@W#6fm1Zr`du<7m8aMue8@Vc_O)dwq^;_ zFoW4RO??4%`16N2D(I3WjCZwEiH7Yo+{-P4oXqy+NmFco(b=t-B9&Uv6~>Sp2_;)t zkaV(uKfyfe70r?dRcnjJVkx`NnQjiEEeUsU79JDlW}Y?0CKsREfofxW0F_X<+<|R& zrd4VNP4gHgzlGoTTPlL;SE^`jkur{M`a&OK=TI7L!}BQ7q>oSKvdVV?=SVHj%CP7g=e9Wl zf@0sPv_<7qEYhS5M@Z&3xYY>=Ll(E^0E%jLSmv5I8|Um#R;7csAYGFsYtv-vB`?E# z%YOb$bod3;TjIl`ieqQUZIg{ikeY*Wm^`I7iUK)OLbjBwDK#md3$DXhupcH!Xaa7Y ze@V-*FWx-4T@>840Wn5O9lrWfrJPeJLl;~D?1FI^(i@K%qAIG2+$vR8rIMVe!ENf< zINUhj2UqbWE5kM|j~5GW{!k!hR#EwqYEZ^eb;7pp6B>;7>TUr&v~g5rNt3)OX5}QR z^)1r+6%mH>qzwCOdF)fc(eCue%znx*j7ll58VV&Cc0c zaD;Yu4aNrsyYrk3JGmmdCE%`c)_F_BGBRGSl(y5o3MaETTu1#VkGb##8YyBevy+M| zK6t_y?2&T#6mzU(o5g_Ji+r?%=M2^AEAszKN3z4HZJC z8Mi99Ibb+S%L;{ z5c-eNP`ReCVDG42fhLFZ0JWKq#+(76+UMkJ1<^{_1GaUIZ^MWfz&t%1?7g5{3{wOD zb?)@BYJom80=-cL%&@C#0&H&0;-VwD$i!NTkr1n`_aWQ6wu-gCzjTRbRP-EnrgqP= zD{E~lCUi8_NBO#u!%jZt$g+>t?RoDS4PN1BY%^kQ?)pc%>5^``t5;ZF=r45CP4{%u KKd1C{xc5J}^5(q& delta 921 zcmXYwO-vI}5XW0UP!t6e0SPt`4;YDeZk{#9#EbESU=OaSiN*v`6EWE+pli20Xpwa( zD7184z9ir`VP*?OD$xM)B}Q$GsTdGNDaM#Oul2B*&CLJ({xg&Jy29qAWwZP>F`|#@ zrcq`$XE52E%yyd3Q7WLDMwlfomlfsgV@Yv)<0JYDb};TKl}2_Gf&wlN`{ zN`AI8p@kJBWYGUG`;jo3Cwsgelp$=a!Zb`l5Z=Qge1=fpBM8Iyh(5&rt{b$JqFiU! z>@eOBVH}5fSTRDd&mbh#& z$-fI_uR|kNKa59Ns}!d&M6RJeH_U=Rq8sej`scgr8%v#5xedl}iqh|+t??$mL5RKu z%HchZHQ3J$1AEkBrM}T>Z-ZBWFhP^Ng&AC=8CHqBfI*T^mcxe=5gpm@VnW#)!yzV+sGmg6JHwS%XdTqm0^VY5&xdu6P3JZ|UpQ(J3uRq5=RvW6x;mjj)s zRoJ~wbjVJ3Ge zifg+7j`K2PRHsWmRT@?eMXeNzz0XScU9H$Irl~`cYDk7!DHSst%{$BrGsRRt!K6yb z$uoZ4WU&2N7Pgit#NLUIz22M699f5=&u1;mN-@C7vX5<#Jv!a=PCraHoi?MJBKmai IVm6li1AfXxdjJ3c diff --git a/C3X.h b/C3X.h index ec29d020..8ebd2337 100644 --- a/C3X.h +++ b/C3X.h @@ -841,7 +841,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereq = "Industrialization", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 4, .img_paths = {"EnergyGrid.pcx"}, .dependent_improvements = {"Coal Plant", "Hydro Plant", "Solar Plant", "Nuclear Plant"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .custom_height = 84, - .img_path_count = 1, .max_building_index = 14, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, + .img_path_count = 1, .max_building_index = 14, .btn_tile_sheet_column = 6, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 2, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, diff --git a/default.districts_config.txt b/default.districts_config.txt index 57edf9af..b1c9b74c 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -301,8 +301,8 @@ happiness_bonus = 0 #District name = Energy Grid tooltip = Build Energy Grid -btn_tile_sheet_row = 1 -btn_tile_sheet_column = 7 +btn_tile_sheet_row = 0 +btn_tile_sheet_column = 6 vary_img_by_era = 1 vary_img_by_culture = 1 custom_height = 84 From b9bf0441c3ceb3e4e2a297e888fdffcee063d78f Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 20 Jan 2026 14:06:23 -0800 Subject: [PATCH 229/356] Add bridge button art and config --- .../WorkerDistrictButtonsHighlighted.pcx | Bin 21140 -> 21593 bytes Art/Districts/WorkerDistrictButtonsNorm.pcx | Bin 31663 -> 32604 bytes .../WorkerDistrictButtonsRollover.pcx | Bin 28478 -> 30226 bytes C3X.h | 2 +- default.districts_config.txt | 2 ++ 5 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Art/Districts/WorkerDistrictButtonsHighlighted.pcx b/Art/Districts/WorkerDistrictButtonsHighlighted.pcx index 5994ad226302f1fd34333a4c0d9d417f4e381655..60474d97978b16353e3e4fef3e97a0e7b5363b9c 100644 GIT binary patch delta 756 zcmW+!y=xRf6yJGsx8u9b+?%=aH0Hxgw6lnfrHFq)h+uD_rGkQuLJHe7emq5#fEH>L zv5z|PEzMtlapS!=e zc>jB$$AbLw=lG18DGnfS4+EKzU^hBv`L0^zr>fA~$<^udNL^qHj(c#6*Pw-E@T1^b z1K@R7bwe=0QS`xo4i5Xk^ZE(Z& zdYjZMnZ$uS?@fdynW#j_}ZZtGp0hD9)nSc zWREULfdV+-#e?$>TNWlVs!VkwxEzW2n`HJK#W|9n&^=9kMVY7{;D5Nox z(oP5i+$&zq>r#jkqfSZ4p~gCrI#!~Vrt4zsh#sOgWnyKn(z{Y(NnuRA?dnuA>b{(8 zuo8(4Hu92PZL+QOCgn(QbOZJh=u9~y%GZr`5j zLk8w6dWW*Z>vYu)Kt<38vPL88|3Mdv9nkyfcOW-tr8mdRy%t|`C)-0%a=e*Hjd1DSCw PUHEzU1DlWh-}C3JpWSE>UY&7wVGRqktXX2komQz4_ay(-k%Lx$U6k|HeaS+3i zsgmUwh;f#wmgOjj5yd={#D?yYO&oU6j%L_K;Ht%Yt`;3!k^UYxaId-xu|0I^fAjT{H zE(oJlpbFv&ZowRgyIKT8SV1mjn9QiCHkngMnHA&#hRFd!B9r}u3?csN5DI|!^r?^s Z*z1$6g=IOeF&$$(%W#%)bCmFZ2LNDBYQ6vf diff --git a/Art/Districts/WorkerDistrictButtonsNorm.pcx b/Art/Districts/WorkerDistrictButtonsNorm.pcx index 8d6e63058d90e709fae7c0964295504e618ea4fd..824474fc04bc4a8cb6cbb13f039e3987d4e5fc08 100644 GIT binary patch delta 1162 zcmX|AZA@EL80MA&6B+A}jgKh|!;~1^|NZliG4U&bKm0J1Oq+}Xr8FV3+b*SxWfs=U zz4WB5_Hv=ypipuaAdS&&jqtv=mjY!X12+m=q9d6|!WO1vkY$SpH0I{qC+D2!dEV!| zIcMw{cmE|Pd2|}>z1-T{XVA~nj5*TF9U~H#OUgJ8!&k{qrAPF0G-HO8>)s$|PP|WY zbRUo@?vMc^>W!<{#=rU+*5D7=o*|#=R!FlhpU66cK1nl>JkNZSe4KfQjeNpq7I4#~ zOutk)OD6y%e>5`Q4$H6tY21Po!-ud1X-MJC_$@8=RG-U{lB~RP)L;o#agC8S;1MHj zGJFDSH-Cg@u$>?;vR=HW(i++jk1GD?IB2i{$P%rxvBJm?VLh?Ia0TY!Hr%gO$@A>( zs(r=Cup)B~Tg{R*}P{z;~?PEBK8Y1&g z!+IS4Vp+CWt_?O#MA$EhWarS!caYZfN-o(Sibh0i6b~fJur9(~R^dN7;5B%RvA;Bt(;V;J(g+C1AN7ert&B<>qmf^t+%-kOLvP(bUr@N~$J; zW)|bDO=(!f)piztHkO^2&AmnI1TQMG(GhCuaqyA}7NjAcRf2mWi@J!LkRF8et=Qqi zIz(H!tNP#R{YKF}Oe9Da>Y!a6*FC)Zi5DPqXqtE3)PP;83Z=C(UX&=fUG zLkkw!whBrhY;W|Ds7OR#3o>H@nm7(P#OPB*XLwXW z?cNTr)!ihxTkQSeQS+!d+%&+WD8-}k>rqHekUtF%DyXHy)p4<>FMK&%ZNY#)Bt^wO zULw5P#7Bo<={!juT{(&LGlxsDb}51quC}X23`u-nK#fYoPa<#+Nw^?Yf%M8inZ0yjpGG`}{_tZF-3UCITU^MbW2tOzKpL0%2YK`9`CdLGG{ z!pYYY6m~CmqasWzC>v#C$RG0avR{_QMV0IoCQBJ%r_<_8D99?Jl9Ux$@Qnd{sS-H$ zL!riQ81>kknxYD`iclYopG^?{cvqIU8@&R_E;>~*O>2-^tZhs!D98#4>u=t8+$hIjy60Gi0~eSk=*{I<1ZY z%Ob^$&VGkYJDB}UYhhQV|1VscBTXfif;+4W>@n>$SUi2|falUD^RaZv-LwAyn%<}y delta 326 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 diff --git a/C3X.h b/C3X.h index 8ebd2337..677d49b1 100644 --- a/C3X.h +++ b/C3X.h @@ -850,7 +850,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereq = "Industrialization", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"Bridge.pcx"}, .dependent_improvements = {0}, .custom_width = 176, .custom_height = 112, .y_offset = 24, .x_offset = 0, .buildable_square_types_mask = (1 << SQ_Coast), - .img_path_count = 1, .max_building_index = 3, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, + .img_path_count = 1, .max_building_index = 3, .btn_tile_sheet_column = 7, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, diff --git a/default.districts_config.txt b/default.districts_config.txt index b1c9b74c..c71c9c0d 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -321,6 +321,8 @@ happiness_bonus = 0 #District name = Bridge tooltip = Build Bridge +btn_tile_sheet_row = 0 +btn_tile_sheet_column = 7 advance_prereq = Industrialization buildable_on = coast defense_bonus_percent = 0 From efedbf180bc0a71341f1e0908491b9f5b053ad8c Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 20 Jan 2026 14:16:25 -0800 Subject: [PATCH 230/356] Add canal button and config; Extend button pcx files --- .../WorkerDistrictButtonsHighlighted.pcx | Bin 21593 -> 22537 bytes Art/Districts/WorkerDistrictButtonsNorm.pcx | Bin 32604 -> 33902 bytes .../WorkerDistrictButtonsRollover.pcx | Bin 30226 -> 31576 bytes C3X.h | 2 +- default.districts_config.txt | 2 ++ 5 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Art/Districts/WorkerDistrictButtonsHighlighted.pcx b/Art/Districts/WorkerDistrictButtonsHighlighted.pcx index 60474d97978b16353e3e4fef3e97a0e7b5363b9c..be42604fc00d1ba42dfc3d1340cea2f050f2d974 100644 GIT binary patch delta 1839 zcmZvcYfx2H6vyvA2kvHb?`EHU4Ku~u!ms}Yt))a&2&g} zn!(-EW(1lVwVKkLaTq7m&bc7hNR+0gn#wMqqGQ?grD-Z@owe~>z@6V&d;Qnid;QP8 ze1C}SK0-u%EQ#~MPZ??TRr)GbdqY7CSwILp%xT#^Hg?1qG<@nQyP&0aTFZ?9x!&^k zT2)Kp$ThW^tXG{RK!#O>Y!YV;;uwC*c4V(@@hhs#cvbWmKF6T8wB$M4iodLSjlDbq z3zl$XpKY;AYK3_);fxUj6_z}2OF@SeiRs|PAeLmH>bK>P>WDcFD_k-zspMhCsj!(tbq5|*o$=>WM$-baj)dK*w7e5Q@!r76G%pJkrhrqz92Pta%+si< z3WifnFHcR%!qkBSz&?W8E)Edned(iVhnX&gqf;0Vbg+*j2hFkR--E+`B$*RxV^%l3oW;{D{X;iG5Z+zfgO_;akVuxxlWSH*q_ zN%Ji0=3xOIN;_sy?X-5uF2iR)%~|f1NsA4sc)7*u_U?@t@zn4N=q2^CT*baL%nPs` zy{w}0-h_xj;4HKcOuPb@u|AZjmWF1<^%;boWBy^)9C{$wXZSo#1g5#BhC=u7e(&!2 zh}lgY^SUarGtCj>Pqo5c4m8veyO9EYBXHB#kP@|lWXARoFS9Xm4J!H**CA()*@!$m zW;e1RNg=zE-bAh(dk|?PA3}bZtaXJcdob=yc^%o1`Wo_L>Kn-C(%O)T>F*(rr|b3V zjJGi!%+UI0GGD~_w^2SZ?gfl5je8VXll3t2)2u~)J<-1G>1spqyBO>W#Nl`_ zuxiWBRjt_*Z{=@_#?D>P6;H?nM4NS?52<(`GE5vLDwkPSNqU+q9{Zrs}@mO#KY= z&9@&zmP|W^{A=0~Wg*YR zDEQ?44iKMb!_`7Pe(4M?Yn|~l%06XQ+AA0u;S9!GvR^8|9=tO#=D zY;8w&iMHYAk}q(*b&kGE<#To2KXXsw`iasWqMDmZ`oJ4{?2@D@45xL zK5bzauK%)7+xW^NeYdg}zl7`UiyuMK@*3o_a&5~;<>mip!@uRZD9fsthg@AT8~Ij6 z5wf?U3>mD{tf|ztwN+}{jH>mRZ>rMv{aCdDiTW9`l~ur+l1@&OLf1Fr4M18Qnwnpx$Z&aw{@$Kh4uQM!Jhh3jL+2P TA%CwgMow&)?(x|22E+Uh9t%&& delta 813 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 delta 991 zcmXw&eN0nV7{>b+D2QSem`oIG3xXmdUloBhBHg4Q1rrc{4Y67eMaKy!+!OHy=w=*J>$F@mVT&n+p?sa+2bRjY2x zuXXuGyJ1XgKu4Mdx^x>3 zrEd*j0nYzLs!^j?BVVsqja2*5teJ?Nn$c+2$5R)4`gCT<>|LzhF}sbOWMv$oUidN| zv*Taqyk_?oGT(6Gr?b>@@K>_KhMocQ18 zjW3x$ed%S^edS~3&40p7%DcM%gm%m|e@cJI<9FNIYA?UG!O50tw^FC=zSS0}1u3o}+yAUofp1hQaax zny;TAsNzoyR}A9bS`SXIyNB)TuVYT-DLkm$3u{#sd{t(|SC_(GJwJpMy^;!LNy98@ zu-ZT#L|ZFZ@owv4>cj8WN_Jwb*~6~fHQzGLwcoMN`?dSobL}?=IRyW@Q_S;qXE>aa z`XA(=>JKq@+75Dn5gQ!L_6;q}^o={1mp9s(S)1gFbCX=j-Q3LIyEZrRJZFoIDMlFA sVC6X)YM9hm&a^b Date: Tue, 20 Jan 2026 14:27:15 -0800 Subject: [PATCH 231/356] Add Great Wall button art and config --- Art/Districts/WorkerDistrictButtonsAlpha.pcx | Bin 14410 -> 17960 bytes .../WorkerDistrictButtonsHighlighted.pcx | Bin 22537 -> 23109 bytes Art/Districts/WorkerDistrictButtonsNorm.pcx | Bin 33902 -> 34819 bytes .../WorkerDistrictButtonsRollover.pcx | Bin 31576 -> 32462 bytes C3X.h | 2 +- default.districts_config.txt | 2 ++ 6 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Art/Districts/WorkerDistrictButtonsAlpha.pcx b/Art/Districts/WorkerDistrictButtonsAlpha.pcx index cafd34354d266d33e3a5e283a589a5a6c63c88a1..cb2a6c0d247cf29b279439e1230f296b838525f5 100644 GIT binary patch delta 2355 zcmc(gU1%It7=~vuyW7chJ3HImWM?;TWEw8H$h#xX3(sla(hnn$#57)G6?&@xPgZ+@*_50ITh zDX|6jB+S5^BhBLLx6!bs5Vkz?jIvR8Fc+St??|3^jge9v{mhfxnUffbh^Sk@k+=Z{A?iXK<-hju`dl4zR zyV;;_$7C)Zpqixx56a3iJ~x+{t(i`36m#diSjDH&=57CI&gJE2Oc$bJYc5yj#~93 zj+t9vo9>UiYz_#~V-$-?kzzSHEX41Fq)72csy&LCRFz_hycfTx$$RlrEs^4tlt}T` zrd<_^Z#9ki@e8J_6weeAmH4GK7CGnH&!lJOkuUBfo|?yV52eo{8ULDPfQH}&ob56Z zZ~2e|J=W4=tOSbBTi6us;CWru{B`1GMJ1M_G-2uWxTI((WwTATV_&mp zmnD17{+;|ERWIR@>;R5unPkwuj(3Qho7NAr#QLMQoZD49YO`KouYc4|cYQ;Q zd;iY&m5oyfZR%1ktoD3me6az`>dHv3 w1U_wfk-lUd@*{nB;h;a5&lC0+^F8=q3n{l}JW5hQ1vNQP9j?J?27YfFYq5uE@ delta 1409 zcmb`HOH30{6hJ#Ot+ZwMO!*r=1qy|vpCQ2rr4;#8AfnbE;1}E&5;fAD?qJ6P4b6>P zt)E2QVOSZX(uxkyR59ubO^iEN!p^vPzI$JZ;0B{i)10|)-kscga^F0hmX^o!?H0*u z!b`es>f=B75!NjxNtUFTrpKJwu?egEn#HcqC@HoKQRzI4OS$}h5v06RpvyV(5`&rY zQQ2UiWw#(Im3=$~qDn-0#a_aC{$XW-C1J9)0?wLy4<58lz=^p7W)MP)t$${*{Lc^yu~~9SL76fkLoRTk!@VOoyR#>mGplrsDsny7A#?2JBsi+ zsQ7X(wX*IATaqln9>GYP7ava*P~V0?#=={luHvqQp{$yegaOP_1osN-G2v4oy{NmW z52L$9GK}B6R%{Mo;zMB8o}Gi>851 zB5+EMzdmeA9a!YN3dipz8J5*J-^DjE9 z52Ax@U7@QBm*};+NF$Y-zSKV#n$PrIx}oc;NB8tP9q3v$#jqWHtxHw2W(OLgcN})3 z$C5V8mh0)6=&es-7=wd?uFVv_&`K*G&q-`^ zz{yQKA~2+q?ql}pG5&^opuxyVjDqzo}cMXS}XOWN2% zkmLY83xSqJA}>*7w2X&~IxEqDWt0pkCRSi&+lbVz{b*{zKq5-$qXp|(n4FGl-`oG0 zlSUR(hwP>jOZ)lJ;%ZDOB_YY=PC8{ZSwaq#1Jg}6S`rB)1Z^-GNf*h<8f>FwTJ|5>Nt=YijZ?C* zV*H8HWPQw-{`z-k3SANBLX72+1Qu30L?E~JHoR(yz=!~PW70}1x)5g)t;|1nrLJh$ z6PPi0Ea-Utk?U$DgAGycgdLRhFs|euwb_%igRcB}Pdeu)HZIWRMs9jiA9l{tsFTx! j#`)^5Rxre@PaBO@`qRkOHevG?U2o2*oi}%yFR%Oq=~T!< delta 226 zcmX@Qg|Tx3;|6v{<~K|Xlhq{bCI>TWF~0(`dnK$UA7Zp*egS0vmk^yS$CS(XeDW-& znT*dSyE4yUd^-6R^A^S@lh?8w0g{cZXBZz(j%T~Y_-L{Z`&A%m$#ItP;baxgYd}(* z>pqYa;JyPS8F_93$sar~faEXU8$j|U-(4Vim;V(|*HeMJK=QueejvF=XgAQz$-=9E zWQfRApe;W{+JW}=iKa3?1G?&Kz5^;$mDu4d&U=&kBOx-zM5<)p2+xS J^K$WdP5``WUTOdU diff --git a/Art/Districts/WorkerDistrictButtonsNorm.pcx b/Art/Districts/WorkerDistrictButtonsNorm.pcx index 837b0912b4f40328273736745df2aec4310574a3..5f78a9293d04bfd0f2b61fd817edb2893eeac4da 100644 GIT binary patch delta 1002 zcmXAoZ)hAv7{;A=t)VTZtv2>wl1sEUf>2TLb3O{1gG+^ckZ{y2nBC-DOM~g|NxQ*q zJd$i~*SlURc4j4bu0WyCgL!Rdc2$wYIbfwP1>h*bfz*D9pgX{GR81 z=Fhv4ta{(9Djp=G&Q7vS>&Q>^`KJBkYxKxPv{+S57K3l9b`v`NXnknGe+21l z=!Kv*6e9GMP!0Vhw2RCK2ev##8ZY}Z0A@O$E53UkDy#%c@B`e2UttZF@WR~1a`4jD z4)R>dZ%4lqu9#zH9{Zq-$SQFCKlC2lh5K;*!Yup+zm+fvR6FCIDSS>u-SCZn*LqHt}xFyaYQQ0aqPUBQvZ z-ynzlk1fp@_L7M-j!UvBCj_`E3SUktq6Js`Hp$y?IT)(h^z1M+L-Qh%g}Njs+(fsi z3n@!+mCZv|CSdJiaBF9jH2SL6tMy6)Sv~7ADS@6*(uJ&Ga%z2oU*DzR#oaBW$xj-? zLxwakqRTFKv=mP{t0NU$!7Kvipb`Wpo@n89lY<5h_D3>3SxN2|6tqxDTW7_jnC9Fh ze~nAQ7f&{mu&)klIFxNw6KXQcq=GIe0xAVnw9L=)urw3g*wetN_-V}<%ASiSqNt8! zU7^1qvQb%;lyrU)R!#9OB*m81Y{3;J z6m`V}b`t54{SO*SK0N3jbXaRtjYr+?m@SS-1+NgzxVBkLG1`3KVVE<2o{>*U__lh@pKtmY+LTqFd^2*3SEF z=*Q(=W5F?)WAkmAnkmzt>%QBw;I}jXgTz|~I*WXwVWno~>1_SC^q=}_sy5VYc43L8 vUfxG@4I14V-bohe;qVyk3Gd>a`&Bqa?}w|mb;Xkz<|YSTTMibm4ayo!0OYTHQ#z&hE@&tn@E4~m$ z#)q3X@<)RxLqRZag%D7~!_876Kn)K!*NB3t`(hd(p1OoQh)R=`0BN5u#SNlPOY;D! z$vd4<`d@@->;XdQj$+C)<7@tjMQM$+g-e6P_p1eW1 Tf$`O3CzXE2H=7@;C?o>_pTJqG diff --git a/Art/Districts/WorkerDistrictButtonsRollover.pcx b/Art/Districts/WorkerDistrictButtonsRollover.pcx index 230d0cb3ac8f8d6a94af6eba7dee19f917f070a0..f5f45e9d9317cd9391a703f1a428a11523dfba86 100644 GIT binary patch delta 1096 zcmXYw&x;&I6vtDEMq)I}WaE!)e(acRHU{*h7e!FKxR`}*sEY(DC&@YgLZn00$-~r1 z7)UYDWLP1(+Xwp~$a^)vc9TgjHp7Sxt1Dy|6Oj-|6nYJQYa;Y7sy^@geec!lpPq=j z|B7!v7xp7@^uV2JpZ(#DD|RTZi_&h269+e{eYDrNSNDHnZ|t|nH!J#;O7hG;C!aIt zG@(Cfdyu_zU_mTwR1L1^HyY>M&I6b80Ivz%r!uELjcKQ!efHd2;_RTJJ)k4%x2b3o zgr*NJ{Z>H7^j9yt{ye~SKsgm@%fqYmL2e6jf733NSQ*o(o2?zZDBkQ>D{v@MDq1k- zfe*d`eL|1%oaDq*&~_(#`a&!gJ5_^ljav!<9^QVy2=Kf77y(=4p>?m5MTeHf1s_=I zwUuVT3>aEDg@9c@@u&?r(*vJ1U%V{NqpZCZVy$#1MJkZax#S72(l69sr2)`g`kAxp za4Z%W+CwUoG%Quh?P+5tT&`;Ab(;n`J>YElM53X(Y(9 zP5(3~yLseOah|FhsfQ#umI#uX`cBh$O}jKS9PEACrtHzoa?njWO2#r}=Mkb9WRYs4 zLyhG$*%4(Q);<>V&(O#?Ha)32E6#2<43gIZrYuQb}B9A4kF^A&I3JNf&uF1FDyw;4L6 zS~&V)$DTa3Uh9RwE|x#1y%`ywY`(-10HQp&d>9!YZ{Eut$H@3-vlA~+@X_XNd{K;y4>y|%c!8*8 zf0`&1F9@KA!9; z_Yz3@$UkCyGTBMtB;(V`GK$L?pG`imxSH|#hk^?S0OrSxQdOWw&G Smi&_iR6H5qY|c Date: Tue, 20 Jan 2026 14:37:24 -0800 Subject: [PATCH 232/356] Add data center button art and config --- .../WorkerDistrictButtonsHighlighted.pcx | Bin 23109 -> 23682 bytes Art/Districts/WorkerDistrictButtonsNorm.pcx | Bin 34819 -> 35673 bytes .../WorkerDistrictButtonsRollover.pcx | Bin 32462 -> 33211 bytes default.districts_config.txt | 14 +++++++------- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Art/Districts/WorkerDistrictButtonsHighlighted.pcx b/Art/Districts/WorkerDistrictButtonsHighlighted.pcx index dbd13dd5bcc98f5a7e134816605f3e8d828fa14e..ee8975250f0e2287ee85ba9477c93ca574dd2f6b 100644 GIT binary patch delta 1068 zcmZ8gJ8Km|6yBK$dzk2CW*pu7zRA6LbXHatBK`p%jUYl4d$AI<@Ii=0Ks51|B0{X} z6fI0Uv%B~1&CLrWt=QS5)l$!HU?#}yu(039cfLL6JK0yVvPC|9tPMWb5`IYkl0&jh zqrs$hBN>wkWiT!5%rvlxhZgI1%#e&}JX076WB>hS{Z^8bg3efDbzu~YJakmQ-N;Bv ziKVeZbYhekdh~kZZaXE8MwWunN;{*+Mos6Q@U3yb;Yfz4l2#kNV-(LkdcL_@wuTpeg z^eGeQ0!iq{0hf4=X|bo>?7r^M*zr^_bc|A|u7tQ}9Cg=P6n(OkGMX|A6@JR&PiNPg z70w;Yfg31&2oEQ7Z#xwXECnvmg02`rU~BH(aRG-8tVm^rgI8GYt#>Iy1Nbu*9Xbul z8K+J7i{HILr=kE_a9BZN0I(~eX&e1D6oH&GU?Xe=2#g7iM+8cL{&9n{oNqG+<%ESY zaX>fcAGI-i5lR##fm?}$6O-*PJZzQ(wufy*qnH&l6$bx);al&Xf_}K delta 513 zcmZqL$#`@N9-Dk_jBgpqxokxg1Af5^Yi~97kXrCFwqn z!!XWy>4^wVi_9X9LogW^*~JJBlUxf2D@QY&%puX{F3kFV25$$DNI1f zcqq<>%S=+53ujN>p)>(uSeo)=gf(I+od^f)RjGshSFg%DR3yKL(77!BwVlg&oNGoV)Pj=AY24O@XH)<$PZqQKSI0f_C k1)!=AKvh7j3X3Gh3k-}b=Ya7Fid$rS0jO)UiKd@107Ry!mjD0& diff --git a/Art/Districts/WorkerDistrictButtonsNorm.pcx b/Art/Districts/WorkerDistrictButtonsNorm.pcx index 5f78a9293d04bfd0f2b61fd817edb2893eeac4da..d94f74fa8a9d5b595f36cf7cdba4096918657fc3 100644 GIT binary patch delta 1172 zcmXYx?@wD*7{?0}_07V3GvbdeCPRiRsGH9H@()0^62qImT45c@a7$N6>KN_pTD9%v z$APjVp!afmFN{%`C$TzB+*~rB=eB@>852=qlg%-w3{#mB;}R3=b6Z~A`{JC>_j%6q z^WAvy!a#9qt^Dzv%*lCao8|wNyZXeY4LOZorbc3fc4uEW;|?yz&M71Ape^J>suDf2Ycn z^|JZugy>H;4R#3)Rd+Il8B*RReg)=X7Oulj&Yb*(1mimvcUXe)q|j7%!cdrwhF8_p zcF6P+y$nk*H!=!;6mVw$w;Cn<^}!fwF4gTf{6oaIc68x;kZFe%T!sZmkI`=`C+}Jz z#;fkxQP%gxLz)X!kE7NRMGyLiP$$Vc4|v{kkmuj7zG;{By@F{fx}qn9W~%5;nVB~s zOV7z#=3%Z4es0dmCCh%}3oF&~!@&@W_fc~M{Dz{RG*qwJkuu;erRNT%3oXSGe4Ur@ z`WNTz@-PbttsS+&dRRA9#dWGJ zes19M_&WI6?vJ)fXQU-$imuu^tW7)WkSzPDF9!$WV3PBE-H*LGyp1~RVYt&29bI)X z55}UVWmgNT{aQTLn{PnmoA$_OiTgX^4Vv`K&?I%ZV5qTk9|Ig+hthlcpSIdmDy_kc znAA*{srecvt{%xx3GyR*JD;MaMCtMR*dzf$3pMawN*N6u^0mD&k~>Wq3mju8rl^nt z*`Re#Givg8_Pw>$l+C6bKG|yBVE$u1*3d)+btQU;){M1M8V_utjk9nm Icp!4 delta 469 zcmcaPjj4G8(}vF~-1pZo{9p4OM4X-2r$2G7J_nEm;+~#tz-T%745R2|e?}*u;AxQH z$;t9+y+F=M5a;ORlWH4)oD(3<;mLE=PXjr}L7Zcg`!u!!ImbYpLzB}q?*TbSA)I`z zhd|B|2&YE-E`rmn^Bc%H43arKdA06S1ZTJ2Z6N0mMCPFWD;Q_ZO=uit7{|BqlA{U{3PsQ)O}nOPYCn>SCqcT<>TG$zOPQ;Oo&*nGL=Rb*Ie2Nu z4=8ONwhYWh=(ekr1*eDSd3SeqTeEZ6hBdmFwq_G;Z4n_!mp3~8-f|T)jOVKKINOCBvECb>y_2@5p z5UW2m>U^^QR<@C;sf(<`bgNvcat9#_PH!QYy?nyqJ~q8;$d)UQxf z38-211BOm+XhD~r#%d$M`&0gOEmPNMi=uLo!oUw|I`{^HZ{xoMLtR>q)p{bhbl`^> z2|J5O8K4s%rBrrJi#@*rR|qhG zY%9stABrZjyP{IXv^?JywrNY@Szf715%5hIMBgZ+$FW*VGCj$z9zr78S)`D4{DLU3 z&7$sBOSHwCNTK-+`XyGsCyJFfn%TtK5=EvZy}XfQgY#UFHoK`rN}V9ZuTQsTbRiLI z*gz4|V?U5p7roj_`h|A*^o-iEdYEKbIP~*jQs0)PS!T-+1)&Xj!gB+G0tU2(F(#Tv zx?`#4!hU(MaVMz(BT4DE57RWMLTDOi_|#XqGr9oeKywE0 zG(hwo1dJtfPRHE#1BiAW>}uRm4(1M9CI|hg3jFBxtAL_awAhaL>u*0b}rAIAH%h8OO?~9-I6}hZ-VpU{Kqdf zae{nJSQnSC;caoI$M-uo=HD4>z>8FdEgCKh+elw1XJ`Jsu@EPT(>C_QaFUm;?wmb& O+M^-ku zN_`&hF%%IejTO8{;UYjcO%~JK!g~ZoAIkOpZ5?%4OHZg z&IvS;bGj#aP?Q|iyTOeRm^Ar}{wY)i`wb4FC|F~72!%h(Xd4Q@%6JtD-_K+<_aAVO zfE;ybvV!SSB>tqyFHC2l@Rys->U07fWwlK=n! diff --git a/default.districts_config.txt b/default.districts_config.txt index 08b49fcc..946a5509 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -10,7 +10,7 @@ name = Encampment tooltip = Build Encampment img_paths = Encampment.pcx btn_tile_sheet_row = 1 -btn_tile_sheet_column = 2 +btn_tile_sheet_column = 0 vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereq = Warrior Code @@ -30,7 +30,7 @@ name = Holy Site tooltip = Build Holy Site img_paths = HolySite_AMER.pcx, HolySite_EURO.pcx, HolySite_ROMAN.pcx, HolySite_MIDEAST.pcx, HolySite_ASIAN.pcx btn_tile_sheet_row = 1 -btn_tile_sheet_column = 3 +btn_tile_sheet_column = 1 vary_img_by_era = 0 vary_img_by_culture = 1 advance_prereq = "Ceremonial Burial" @@ -50,7 +50,7 @@ name = Campus tooltip = Build Campus img_paths = Campus.pcx btn_tile_sheet_row = 1 -btn_tile_sheet_column = 4 +btn_tile_sheet_column = 2 vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereq = Literature @@ -70,7 +70,7 @@ name = Entertainment Complex tooltip = Build Entertainment Complex img_paths = EntertainmentComplex_AMER.pcx, EntertainmentComplex_EURO.pcx, EntertainmentComplex_ROMAN.pcx, EntertainmentComplex_MIDEAST.pcx, EntertainmentComplex_ASIAN.pcx btn_tile_sheet_row = 1 -btn_tile_sheet_column = 5 +btn_tile_sheet_column = 3 vary_img_by_era = 1 vary_img_by_culture = 1 advance_prereq = Construction @@ -90,7 +90,7 @@ name = Commercial Hub tooltip = Build Commercial Hub img_paths = CommercialHub_AMER.pcx, CommercialHub_EURO.pcx, CommercialHub_ROMAN.pcx, CommercialHub_MIDEAST.pcx, CommercialHub_ASIAN.pcx btn_tile_sheet_row = 1 -btn_tile_sheet_column = 6 +btn_tile_sheet_column = 4 vary_img_by_era = 1 vary_img_by_culture = 1 advance_prereq = Currency @@ -110,7 +110,7 @@ name = Industrial Zone tooltip = Build Industrial Zone img_paths = IndustrialZone.pcx btn_tile_sheet_row = 1 -btn_tile_sheet_column = 7 +btn_tile_sheet_column = 5 vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereq = Industrialization @@ -130,7 +130,7 @@ name = Data Center tooltip = Build Data Center img_paths = DataCenter.pcx btn_tile_sheet_row = 1 -btn_tile_sheet_column = 4 +btn_tile_sheet_column = 6 vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereq = Computers From 589f2f08f8dc83eead7d8b4289b5043ddcba529c Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 20 Jan 2026 14:44:07 -0800 Subject: [PATCH 233/356] Add offshore extraction zone button art and config; touch up image --- Art/Districts/1200/OffshoreExtractionZone.PCX | Bin 4363 -> 4353 bytes .../WorkerDistrictButtonsHighlighted.pcx | Bin 23682 -> 24183 bytes Art/Districts/WorkerDistrictButtonsNorm.pcx | Bin 35673 -> 36607 bytes .../WorkerDistrictButtonsRollover.pcx | Bin 33211 -> 34110 bytes default.districts_config.txt | 4 ++-- 5 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Art/Districts/1200/OffshoreExtractionZone.PCX b/Art/Districts/1200/OffshoreExtractionZone.PCX index dc4e966c3d343f01f51cf8abeee42d59b4a22887..bb1d750c9301f65b55aa24bd39f169f49229dfe4 100644 GIT binary patch delta 95 zcmeBHYE+uAi1F0MWrB>1?<&*on<=*MPGpze4PF)7lFUAFmiCk&$5HWn>i8H9oZDaz%K`5z%2s%K*YYaWot+_iBfr~(>hmhGz zK|$hZuM%5~F6ZX&j_14p2{bBwY}*qAG#mzx^KJh2C>%r=-JoZUk?5dE8#@%=?$W|F z)8Yj#KT%s@mQ9)B;ZCIZd+?B?RyV*GIT%%s;bFqUFVkacl zZGcVHx}=)A?~Un7k25qNckv$B%mJQiF1h7q313>?tk;HL@NBvkpm3XFURDNZVKF9bF=%_23)wKhR0TK_IYybcN diff --git a/Art/Districts/WorkerDistrictButtonsNorm.pcx b/Art/Districts/WorkerDistrictButtonsNorm.pcx index d94f74fa8a9d5b595f36cf7cdba4096918657fc3..9e3f826a472b4462e9c83fd975d539747827df7f 100644 GIT binary patch delta 1062 zcmXYwUu+ar6vi{HB}60D7-Ant)Vj7PQj7l)%Cm`fH`{q=d|(>}3T>n_OK7sDleD{9 zBB`CS?al7$jJE_SyB%{0OZLHvjdITHADU@n2upPrG*VugO{CGpz}tFe^_)^Sh3?K z)0%g%fo4y0gPENV*hej`?ERLfX|ST_ZQFOHvym)Rux3IHeuWMA6Dr{1wdEh` zqL@4O@;0d}FZxj&g|&DxZYvR}Vgb_!5NQgMqrHD{9~ z1dw&d;Pr_lrf0%OaRrGE>GH*ZE&x8^}c)SZo3$n zh!%LSKS&{iei;=r5Ti*9qheozTc3DX^6m9@6n;JGaLXW)wh{fZW6JY-Bw;Ho4ZqMn zFzp@pITRPpj}|x*q!JYLAT^_ybII=)J*MYf_eB)_XTnjgIzhdWEBG;Gn~dR%nM%gN zAxnonu^(Q@3wRWp;)cUxG;=v*$q5rrq8Un9@$5M|TkPlkE6)@4&lzpJz^COHw?YYh zK51geGUl!T9Q7dUy~}L{I)CEqsd1O1g(0+z0ekrfoqqPgfeUR#(T8G08#Ri&U=hFv z=FZW0ifrq^`L>eicf{xV2?KeFeij-j?_-J09*ncw?awwYtFPzNXIu{)nh%fPFrwqy gw}-t;ZTC#DZl8C7UAE`YXY6|8|61UQCovh1fGx-do=wyG!7`FdwUaw&|F?k-7?Bq1HcA)gj$?WPE zf%J>XKh@s>>F1NVG_L{aXPa5H*cgF~r<*Obxj>93n~QW$=&Cm3BAq+(W zW)S1i<^V%cFk|sFBZ$CWV^%O@@=KHNAQh8Y%zl7r1@n6l+TsqFma_!e`*^aL)oBnt z$r?;eerJ6U$bB;Tg3U=Vz0-CrkbXM3&Tb))em2?Mem;6I`yE)uxg%1E(M358! diff --git a/Art/Districts/WorkerDistrictButtonsRollover.pcx b/Art/Districts/WorkerDistrictButtonsRollover.pcx index 50b929f63c22f1f8fcb396c17a9b9d30755c53f1..ea7a58b4e06b6ae52a2177ef76726733fa52db13 100644 GIT binary patch delta 1154 zcmXw(&ubl36vs^}Xjj^74E|`UHci@UqSe->25}<>H*erBTwQ4C&Oe}~&{ZA07nkos zL1=ZDGB5=3mC0tHDx7oQ%$qbmhdR_J(qBU3qc709Xr6^z7|$6Z%xdOy&i8x1_uRSm z<>b#_PhOpyH6Kq*Jzu-Ndj{OPIW>FQoSZ#ky0d%tRSxdKUD$*X+=9YdxC2jM3;wL# z2E#q`?c}HC^5lv+ci?4na@X{(ni^a@aC`rC*G8@#q?n{=Q!JpO79*%q>NbGCYgd>v zbJ=|1;3<(eVKad;0fZur0Wk#&mA%9G03Oz)^wIncv;5rd8ASRHLmWpiCOaee7lvS| zU;sC3mz$}BcNcnYl~l?Olp)c#a7fsFxxx$6JpMBQ@_sDN$h*i#TOEh$M~61F*ypiS_r(PjoZ2Q=5-8SSu|2M9#RiU4{- z5w4*hk~O4?oZ0x@t-auM)(k69uZuvTR!t-o?+TiWM{d7DNRUb2YU>k%x&uW*9-9_t z-n%hF)^h2!ycYTz3Cn~gSG|y!#?oi+m&ABI+46ky9HQ5e(4APhM2`$vDALARoFiJq?nQGP=uafF?eJy~<**iUa**Agg& z^#xf%?FUu|WSt=DRgk1Lr$6}q^)4-kpy4P@jfxUOiIIv_BUsW7nW^JfUt_8zf?R4r zQAI*`BCBL4NmrT3j(_t~Mg+wqf(@A~q{AFQpV#%&(pS?t-$_ptGE*G1WCzWa87+OW zXM?`5_D5RE;7F kQ0Ugq4`RbiEbnhGAKUXAedi0%_Hx}a^siZ4TE6hfe+YvCU;qFB delta 268 zcmdnj#k9MbX@j~d^Z(y(C)=p*0Mf4~^Q)}`(l009P`eAHUrfHKeiTSQpL|T?B9MMI zdB5g;ApLam8LgLK`iVA(ezN(i4ksg!@p!U^-g_|Zr2hp-KiZsPzygwZGFEa4cjar{e1Fb+ZrJKVzRzn3y^*}Sw~rIvZOu#q8= PKKAiI`t9aD_D!w;KPR30 diff --git a/default.districts_config.txt b/default.districts_config.txt index 946a5509..bf560ecb 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -170,7 +170,7 @@ name = Park tooltip = Build Park img_paths = Park.pcx btn_tile_sheet_row = 1 -btn_tile_sheet_column = 7 +btn_tile_sheet_column = 8 vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereq = Engineering @@ -190,7 +190,7 @@ name = Ski Resort tooltip = Build Ski Resort img_paths = SkiResort.pcx btn_tile_sheet_row = 1 -btn_tile_sheet_column = 7 +btn_tile_sheet_column = 9 vary_img_by_era = 0 vary_img_by_culture = 0 advance_prereq = Electricity From 8339dcf00aad1c710b92a3965ac70cddf9e001b4 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 20 Jan 2026 14:47:20 -0800 Subject: [PATCH 234/356] Add park button art and config --- .../WorkerDistrictButtonsHighlighted.pcx | Bin 24183 -> 24697 bytes Art/Districts/WorkerDistrictButtonsNorm.pcx | Bin 36607 -> 37478 bytes .../WorkerDistrictButtonsRollover.pcx | Bin 34110 -> 34935 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/WorkerDistrictButtonsHighlighted.pcx b/Art/Districts/WorkerDistrictButtonsHighlighted.pcx index 5bae20dc7882c109c4622d60a7199c2c00ef98c9..fd6425b7a08eb2ac3288c8f7b4f483302e92d9ce 100644 GIT binary patch delta 718 zcmXAny^9nv7{)ir-Nv}aB$wOS5AXEuR&T#J3oY#}#nMJB1iQt`!a}qV6bnHEb`~oZ zi2^m zYHW`3O!I~tkwfew>KCYgkQyscZ?$f@>+~qgQ9o5|n_ClHO|3DnWCcXdLEHbNyPvC2zbSQE%_Eo~AijgTO*m0rZsWbnm+Ki3YL zm8yxQiUUi9mnyZ!au1lUf36du0TW|Y2A|5%8RW_F*AY=Zd>tcN0Tf&W=)#G&O(p}; zMth_yPzP8AJiM{nVAVuOuXJn#&xI6$39$Lbbd~W`#O1UzQJQK14>sS_8HXwKvbanR ziv&5pRlpKR!G)C~jBJBNck(CzFImQYK&eeJ70|a%+-AYz5h5|t8{xqawM_W*WKn0S zU^6a^NK9CI++pTxr>133$z`TE!i!u8wNtgSZZ4h`u_GrCA7l7;CIl< z6~EaXx1g~CqeDdoyoEA$DeT93@2*K{g`&|MJ7u&8Tll94}?+KH~;_u delta 307 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 S%H(-YE Date: Tue, 20 Jan 2026 14:52:03 -0800 Subject: [PATCH 235/356] Add ski resort button art and config --- .../WorkerDistrictButtonsHighlighted.pcx | Bin 24697 -> 25091 bytes .../WorkerDistrictButtonsRollover.pcx | Bin 34935 -> 35809 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/WorkerDistrictButtonsHighlighted.pcx b/Art/Districts/WorkerDistrictButtonsHighlighted.pcx index fd6425b7a08eb2ac3288c8f7b4f483302e92d9ce..1adbd30573c8e6e9405d4505ddf9cccbb602c292 100644 GIT binary patch delta 947 zcmZXTL1-OS6o%)_#CN@>H+OFD%X`jClb7TrZIbt5g;EHbMyn}}w3RMIM2KQGx^Q7Z zD2U)93Zc+edp079=tgv7OZQstotZQyFE!XMEL5`f-IEIQl(~_Tro##1CR0N4!x#n{3k7Cp&H4Psh1MEq;xf z{1F}F-N#31gZF5V|H79u*-J@DIlmK6^N(3d5#PyPPYUw9)EMJe8U|tU^<3j^P`@>v z;6EFWiLOm$fil&hRgj+!&6@SN&!#HPP1hPNa%kP5i{$v#1J6_`rqoECn{{iPDy<3f zmj^zEe0cceAjQf^)svl)kag(b{o${Ocz9$niz>z|?UC)3frEC|SmD6DIr23a**un! zOr=x0Fy0#FrPW^LRtM&EbCn+9Tg}Ccv? z-g>Go3WaPUMeDPa&yJf^QhyC#h6&cQW| zb@|X@EfL5K4x#|zw6ZDy-yAv*U}S2(?&BnvK9RjTH_=pTD}h*?cHrQ{Vvpt(+e5YWW8sAnRw(&ZERn-|6iuxz-k?xX&WqBEV{D9tW7Gz?z}a# zjAblmj_faEa&gj?IYg&L*hy+HP{Gf(n**D1vMnJfElLRI+D~#?+XvV0+k?DZ>qpG- zZad>^wJQHmv;0R*@tMv9Vt`8ipfkv`^?pQ^U#<`E)lP>t`Cey|FV**rZ^hBi5%q1; RPIT*k(fbXrw`X5G@*h%G79;=w delta 677 zcmZ9KJ!n%=6vyAWPm`wwX;bpjd^ImGP4n?SFmx$ezZET5b<@R)gPTJsg$g1T;?PAK z{ktd*+C{o0h`XlmH8G}1LoEdtK?fHH11?VSYTs?a;ScxsKlj{^b57Br-|wir64JJ$ zCVi4RB+)uwHJJ2;Nj=tM+iZ(IGKo+RU6C8qeB9BLrtl_*rsWznFijz)AD2ENDCbEBoZ^#aWIMck>}VPnGl#wzkr)QDljyh@GNsq;IGUg?qzqMv7MdA<=li)6<=J=%_EW@S5}2_D?f+N`Qv*=!MG_`d1Wd_ z6lLSkUUk~WbzVcu9P@iM#9dPd%s4rymk$^z zAnvQ`U-3rQQBl?X|JsE2Rsk(bfeOee}f#{{sn~`zQ|7 z(jRtN7+3?%ty#G2>p8P~e`s#DxLhtsj7Er|Ne~Jqk*-3&v%=0U%=w(}eCM3ATeo_a z?)3ci?VuhNpNKjA-QfEEyr5_Fl$L3g7RXXwu0?uHYxMYmK6~itzP}5^vCnK{M4QYp z&~;jBMtg_)h5r4$1Mjz}OD`JHxx*g|J$R&dM@*ZX+eOM_1YXvoZ;tE{1FeEOHOfrD z91?SrwrJCC;JrdQG`rMEqaTJ(iQz^uO)bi*lm!+fj$DDgOL&I zQ8Anpx2d3c>ITvvgBdm#%A93Zw8a~=h@BQKCsFt4NujT8+t%lVaUTfRSozR|S$b98 zpJ~lTA04|YhPa?N)3B--rjd}wlv@?jYRDO^G~sQ99&_~l=Of|}!$q4i2{?s^IILP9 zdKp(0OgCNWXpN&^j$al-1U6%b297oC%a9tZsz_J_mxt)UJVnn>cw&%>KdOl|HpD}j z^*JyAH(lA@p{uk+(V0^t4xLJ6-GqugpE7)Ww8|ClU0e|0Rc?~aD!a7C47e_@u6_r$~6r>4RNfWcX3R-J{<@AE0?95&@+)Q!Uc zS~C>=ceY3LaWUzo5{PFcIM12{C1NaC>#~0P)yq9xT&X0|Ry*rqY zw#9^RrJa(>vy}&iF>}@V6M8P%7{7Vxe>xtH*#H0l delta 234 zcmaDjo$31orVZYzjBh5#slH)*^_$_~@8gr_F}Y8k%j7Znx0*NOi_Hn@ij0iUH!s!T zWn_G|`MRbABjeM}LfXcRj88V_>o|a@o4Q7fjE^^)>W48hKH9v=zyd@G7`ZbtKHOYq z91NnqnE(YJZVomBs(iS4rMV7R&_WVK8CkJ|s7PxL5Y=GA1k^rxq3t^$x!mptklbef zobmDG%?=NM Date: Tue, 20 Jan 2026 14:58:04 -0800 Subject: [PATCH 236/356] Touch up great wall button art --- .../WorkerDistrictButtonsHighlighted.pcx | Bin 25091 -> 25087 bytes Art/Districts/WorkerDistrictButtonsNorm.pcx | Bin 37478 -> 38364 bytes .../WorkerDistrictButtonsRollover.pcx | Bin 35809 -> 35834 bytes Notes/district_todos.md | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Art/Districts/WorkerDistrictButtonsHighlighted.pcx b/Art/Districts/WorkerDistrictButtonsHighlighted.pcx index 1adbd30573c8e6e9405d4505ddf9cccbb602c292..5e02e90c1ad90cfda279b98239c96fd022de215b 100644 GIT binary patch delta 85 zcmZoZ!ubC%41c9Na*5c{FY5BT1*o@8eOvaay|qXI#8+hlf; zg=XQF%AxJNmm8wccEv*|IhxFlWIWHg7bY;{0&{@-U}mxqlCfxF^ouVCOwKtU{^$2R z|NnWpGhA@%>w?^mrG@>01fMTGNVZ|e-~dKPB7?{ueRw}{3}D0xwgyLdOZi?>gx+8@ z=#5~oTPpF2DzeQRlv+e;BOEKMi37de8wy{0q=amVMxtj&B7DKrA6>O)D!gAVL`w#UM-cLP+C1jen?0S~arp?>8io8FUhgBNg zT*i}66%!iU;wVe!{5iDXF06_bxh}yh<|i)OJhi)-?6z1Vvg2?qmWX9l^yk3BCHNIq z;rggtR^T4|oWBHr!fzHQdla%uum;vXku~F4RQ;F(gyr>uZvt&_7gt~r=HMFKY83qa zXWk^W>-5EpY3S>8PT11?HUI`ZvhU)za0eE?8iqB&vwJ@#PGs==g)|z%@ESXh#>?M4hclk7qd+@P3jlc_DhAy`9x;<woSx ze9xbL;izobs!33>bu|$3HN`Rk^jwkHbqOLY48zi8e(J@yNev5zPikm(NwO1Nv5eB} z*+AxD8Rh|Cd06t3TrbnUs!BS6mGk&2g|1rTOeAU>X)U-}F))sb-P$ zLNkRCQaVB|MfHS^rTu8jRseG;z?AfuGy3mb^2SY7F^@BYIH$PLeDVRUa1Ft+l9!K_0C~TRX=yoZl zBwa#RF{4sB(r^=pdR`^v$i5srDNNyZp%E2sTenobFY(<)cEwE&mUToH%2d+LUQ2h7$HV}`>p#sgLGl0q delta 317 zcmcb!n(5gRrVSUwn5RykviY)@BO~YGtW_;3l_`fRH_J(wF>?0Jo1fCSD)aEf%`uXC zjNFHF=T}axT$Po%AZ7D3DP=~ks`)9?TIQ_E%x~FzSXzaVBdcJ)V^5C9%P8I0}qgz9O?KMXnw8}GtipJ-Oe`{ wpG>ZDImP&Na)j$C#%Gf)-1aj*pUm&RnDNEr!$9)Y*+ zG5>Ml^Y9ng1c&IFNP7aAeYj_i9fl@!!6nqoU<b8NH~hcO^D6WWR0h0OJA z|NeP)D2!r905u%7d$!hho0VH(6uOvf(hPcJ^aHlxH)P4m8{6r>$tuu>_1NSbz+8u9 zhucK!fumn83|wbZORbS3Hmr#&p*NHYnpJq*6N>qqFwlT{dlm_G(JINXD)MDRy9esiCxtA&cP` zwC&m9YiwNS3?77mmdi%M`b%k|WujO!a)Xt0jYnvB;Iu$OJCW2f51rZJBUjjjN(HZK zgW7Roq=wKi4}+BR(db213N)#6QDm@_qd7z?vz_8RD{3AHq-mgxh@nihtDGD=&B|o0 zt4bpk*G^GzY!D$_iS3>78CLXA&;{$B_FBqQjX-f3#r8>Qid+d%RnI8&oT93!hW6}Q zd4g5ygaztqReM@J#AzJ%_X~UvdGH$+>j*Inxpw4oeg(}qBs4ysh>xMasQ>f zp?E_#Jeq0fU#ff`2}H_K2dP<=Mo0l2|MIb~2ZU*nMmT!X*uOB{9r6Mc?~EW%C!{|% z^L#)fjkie){pQT8QE0oJIyb`PZ6qRneed|wK4@#RCIbJ|Y&3}4tcn12_5F_jaiQdY Qn|(kyrFGigJ5j&?7rkrAkpKVy delta 744 zcmW-eJ!lj`6vvs1B!}WhVvfs)mv~0dLXm(V5ezm$K;c%NprB)6BWY}fK)QvQ`nE`l z1&4!`MUZ%o#UeKE&3StuaRXbV@VHCn6go}V8RseOCssu)XS%wi-;4mQf&z?O#>#Bt1` zmZJo7S9lDpH}X)_Q@+KlMR7tsm$UzXtNg(IqE>>0neCPDiJ2yDwefs807MzBwLTY*zC_7eN@*dsAZ63ElwA{D!=O&M-uH`KsH9^#NX zn1%u%;ATK!Y7}n2RwhEuCb#ROcf}-l5Rq}rNn$yRlvYL0xhXHY}cUrORHo@l_E!&)|i3&LBH$3AzPpe3UU_gOqJ5$qQ zc1d$A{;dZ4Hhfl%4h`#`HY!T6YyVAGI21-mhq~eUKni?58gRw*NpPX|bAqI6knj2I zrHqCULdIDQ7G}PWf=t0Usdy}D__GmQoBcMjRZJ;c!IjhbhzFbeb7fGiZGe&*S5Q^N-d!QxyL?A(|P{?4roHNCD~J-hn&KRJ$_0{{R3 diff --git a/Notes/district_todos.md b/Notes/district_todos.md index b32d3b29..6d35fd63 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -17,13 +17,13 @@ - Canal (2 opposite sides have coast "isthmus") - Bridge (2 opposite sides have land "strait" or bridges) - Light annotations - - Buttons - Add commented instructions on fields in config files - Double check PCX third column alignment ## Maritime Districts - ~~Choose terrain type~~ + - ~~Buttons~~ - ~~Named tiles~~ - ~~Prevent AI building districts on resources~~ - ~~Add allow_irrigation_from flag~~ From 0fd472276bdcaf91dc223ccdf36a90f5a7cebb72 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 20 Jan 2026 15:03:45 -0800 Subject: [PATCH 237/356] Ensure great wall, bridges, canals, only connect to completed tiles --- injected_code.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/injected_code.c b/injected_code.c index d674f981..0676a69f 100644 --- a/injected_code.c +++ b/injected_code.c @@ -9734,7 +9734,7 @@ tile_has_district_at (int tile_x, int tile_y, int district_id) return false; struct district_instance * inst = get_district_instance (tile); - return (inst != NULL) && (inst->district_type == district_id); + return (inst != NULL) && (inst->district_type == district_id) && (district_is_complete (tile, district_id)); } int From 2b1a01b8559e6b806142683139d2cf8ab2e7d60c Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 20 Jan 2026 15:28:30 -0800 Subject: [PATCH 238/356] Boost AI chances of bombarding great wall --- Notes/district_todos.md | 2 +- civ_prog_objects.csv | 3 +-- injected_code.c | 54 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 6d35fd63..dd72d6dc 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -2,7 +2,6 @@ - [x] Add ai_build_strategy option and AI worker handling - [x] AI navies target maritime districts - Bump great wall bombard eval scores - - Make sure AI workers remove great walls after they have metallury - Firm up logic for river district rendering - Hoover Dam (use alt dir, special positioning b/c on river) @@ -25,6 +24,7 @@ - ~~Choose terrain type~~ - ~~Buttons~~ - ~~Named tiles~~ + - ~~- Make sure AI workers remove great walls after they have metallury~~ - ~~Prevent AI building districts on resources~~ - ~~Add allow_irrigation_from flag~~ - ~~Add logic for determining if canal connects bodies of water for AI port-building~~ diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 23bc9eff..53617136 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -847,7 +847,7 @@ ignore, 0x5FCC50, 0x0, 0x0, "PCX_Image_draw_region_to_location", "void (__ ignore, 0x600050, 0x0, 0x0, "PCX_Image_fill", "void (__fastcall *) (PCX_Image * this, int edx, int color)" ignore, 0x5FFF10, 0x0, 0x0, "PCX_Image_set_color_table", "int (__fastcall *) (PCX_Image * this, int edx, PCX_Color_Table * color_table)" ignore, 0x4507B0, 0x452860, 0x0, "Unit_ai_move_offensive_unit", "void (__fastcall *) (Unit * this)" -ignore, 0x44C340, 0x0, 0x0, "Unit_ai_eval_bombard_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y, int param_3)" +inlead, 0x44C340, 0x0, 0x0, "Unit_ai_eval_bombard_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y, int param_3)" ignore, 0x558F70, 0x0, 0x0, "Leader_is_enemy_unit", "char (__fastcall *) (Leader * this, int edx, Unit * unit)" ignore, 0x4BFD40, 0x0, 0x0, "City_get_turns_to_build", "int (__fastcall *) (City * this, int edx, int order_type, int order_id, char param_3)" ignore, 0x4E3D90, 0x0, 0x0, "Main_Screen_Form_is_unit_hidden_from_player", "bool (__fastcall *) (Main_Screen_Form * this, int edx, Unit * unit)" @@ -883,7 +883,6 @@ inlead, 0x44C130, 0x0, 0x0, "Unit_ai_eval_pillage_target", inlead, 0x456840, 0x0, 0x0, "Unit_ai_move_air_bombard_unit", "void (__fastcall *) (Unit * this)" inlead, 0x4579E0, 0x0, 0x0, "Unit_ai_move_air_defense_unit", "void (__fastcall *) (Unit * this)" inlead, 0x459CE0, 0x0, 0x0, "Unit_ai_move_air_transport", "void (__fastcall *) (Unit * this)" -define, 0x44C340, 0x0, 0x0, "Unit_ai_eval_bombard_target", "int (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y, int param_3)" define, 0x5C1920, 0x0, 0x0, "Unit_airdrop", "void (__fastcall *) (Unit * this, int edx, int x, int y)" repl call, 0x4C0D87, 0x0, 0x0, "Leader_count_wonders_with_flag_ignore_great_wall", "" repl call, 0x5D3D67, 0x0, 0x0, "Tile_has_colony_ignore_extraterritorial", "" diff --git a/injected_code.c b/injected_code.c index 0676a69f..887e051e 100644 --- a/injected_code.c +++ b/injected_code.c @@ -31351,5 +31351,59 @@ patch_Tile_m17_Check_Irrigation (Tile * this, int edx, int visible_to_civ_id) return base; } +int __fastcall +patch_Unit_ai_eval_bombard_target (Unit * this, int edx, int tile_x, int tile_y, int param_3) +{ + int score = Unit_ai_eval_bombard_target (this, __, tile_x, tile_y, param_3); + + if (! (is->current_config.enable_districts && + is->current_config.enable_great_wall_districts)) + return score; + + Tile * tile = tile_at (tile_x, tile_y); + if ((tile == NULL) || (tile == p_null_tile)) + return score; + + struct district_instance * inst = get_district_instance (tile); + if ((inst == NULL) || (inst->district_type != GREAT_WALL_DISTRICT_ID) || (! district_is_complete (tile, GREAT_WALL_DISTRICT_ID))) + return score; + + int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); + if ((owner_id <= 0) || (owner_id == this->Body.CivID)) + return score; + if (! this->vtable->is_enemy_of_civ (this, __, owner_id, false)) + return score; + + bool has_unit_on_tile = false; + bool has_enemy_on_tile = false; + Leader * me = &leaders[this->Body.CivID]; + FOR_UNITS_ON (uti, tile) { + UnitType const * unit_type = &p_bic_data->UnitTypes[uti.unit->Body.UnitTypeID]; + if (patch_Unit_is_visible_to_civ (uti.unit, __, me->ID, 0)) { + has_unit_on_tile = true; + if (me->At_War[uti.unit->Body.CivID]) { + if ((unit_type->Defence > 0) || (unit_type->Attack > 0)) { + has_enemy_on_tile = true; + break; + } + } else + break; + } + } + + if (has_unit_on_tile && ! has_enemy_on_tile) + return score; + + // Boost score to prioritize Great Wall targets + if (score < 0x6000) + score = 0x6000; + + // Additional boost if there is no unit on it, more likely to destroy it + if (! has_enemy_on_tile) + score += 0x200; + + return score; +} + // TCC requires a main function be defined even though it's never used. int main () { return 0; } From 8e48e700b467fac729c9ce39cae2ff9c00151d9c Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 20 Jan 2026 17:50:42 -0800 Subject: [PATCH 239/356] Relax AI worker rules for entering coast; Nerf Great Wall culture & gold bonuses; Add logic for better handling improvements which require rivers --- C3X.h | 2 +- Notes/district_todos.md | 2 +- default.districts_config.txt | 4 +- injected_code.c | 89 +++++++++++++++++++++++++----------- 4 files changed, 67 insertions(+), 30 deletions(-) diff --git a/C3X.h b/C3X.h index b51261b7..efba6271 100644 --- a/C3X.h +++ b/C3X.h @@ -869,7 +869,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .img_paths = {"GreatWall.pcx"}, .dependent_improvements = {0}, .custom_height = 88, .wonder_prereqs = {"The Great Wall"}, .wonder_prereq_count = 1, .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Mountains) | (1 << SQ_Forest) | (1 << SQ_Swamp) | (1 << SQ_Jungle)), .draw_over_resources = true, .img_path_count = 1, .max_building_index = 10, .btn_tile_sheet_column = 9, .btn_tile_sheet_row = 0, - .culture_bonus = 2, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 2, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 50, + .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 50, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 } }; diff --git a/Notes/district_todos.md b/Notes/district_todos.md index dd72d6dc..db897858 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,7 +1,6 @@ - [x] Allow workers over water only if in radius of city that can build water district/wonder - [x] Add ai_build_strategy option and AI worker handling - [x] AI navies target maritime districts - - Bump great wall bombard eval scores - Firm up logic for river district rendering - Hoover Dam (use alt dir, special positioning b/c on river) @@ -24,6 +23,7 @@ - ~~Choose terrain type~~ - ~~Buttons~~ - ~~Named tiles~~ + - ~~Bump great wall bombard eval scores~~ - ~~- Make sure AI workers remove great walls after they have metallury~~ - ~~Prevent AI building districts on resources~~ - ~~Add allow_irrigation_from flag~~ diff --git a/default.districts_config.txt b/default.districts_config.txt index bf560ecb..bc305673 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -361,9 +361,9 @@ buildable_on = desert,plains,grassland,tundra,floodplain,mountains,fore draw_over_resources = 1 defense_bonus_percent = 50 allow_multiple = 1 -culture_bonus = 2 +culture_bonus = 0 science_bonus = 0 food_bonus = 0 -gold_bonus = 2 +gold_bonus = 0 shield_bonus = 0 happiness_bonus = 0 \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index 887e051e..b780bb30 100644 --- a/injected_code.c +++ b/injected_code.c @@ -11715,16 +11715,55 @@ city_requires_district_for_improvement (City * city, int improv_id, int * out_di int district_id; if (! itable_look_up (&is->district_building_prereqs, improv_id, &district_id)) return false; + Improvement * improv = &p_bic_data->Improvements[improv_id]; + + // Special logic for handling rivers + bool requires_river = (improv->ImprovementFlags & ITF_Must_Be_Near_River) != 0; if (is->current_config.enable_wonder_districts) { if (district_id == WONDER_DISTRICT_ID) { - if (city_has_wonder_district_with_no_completed_wonder (city, improv_id)) + if (requires_river) { + bool has_river_wonder_district = false; + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { + if (wai.district_inst->district_type != WONDER_DISTRICT_ID) + continue; + if (wai.tile->vtable->m37_Get_River_Code (wai.tile) == 0) + continue; + if (! wonder_is_buildable_on_tile (wai.tile, improv_id)) + continue; + struct wonder_district_info * info = get_wonder_district_info (wai.tile); + if (info == NULL) { + has_river_wonder_district = true; + break; + } + if (info->state == WDS_COMPLETED) + continue; + if (info->state == WDS_UNDER_CONSTRUCTION && info->city_id != city->Body.ID) + continue; + has_river_wonder_district = true; + break; + } + if (has_river_wonder_district) + return false; + } else if (city_has_wonder_district_with_no_completed_wonder (city, improv_id)) return false; if (out_district_id != NULL) *out_district_id = district_id; return true; } } - if (city_has_required_district (city, district_id)) + if (requires_river) { + bool has_river_district = false; + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { + if (wai.district_inst->district_type != district_id) + continue; + if (wai.tile->vtable->m37_Get_River_Code (wai.tile) != 0) { + has_river_district = true; + break; + } + } + if (has_river_district) + return false; + } else if (city_has_required_district (city, district_id)) return false; if (out_district_id != NULL) *out_district_id = district_id; @@ -18473,8 +18512,11 @@ city_meets_district_prereqs_to_build_improvement (City * city, int i_improv, boo // Different logic for human vs AI players bool is_human = (*p_human_player_bits & (1 << city->Body.CivID)) != 0; + Improvement * improv = &p_bic_data->Improvements[i_improv]; + bool requires_river = (improv->ImprovementFlags & ITF_Must_Be_Near_River) != 0; + // Check if the improvement requires a district and output the required district id when it does - int required_district_id; + int required_district_id = -1; bool needs_district = city_requires_district_for_improvement (city, i_improv, &required_district_id); // District is either not needed or already built @@ -18484,28 +18526,33 @@ city_meets_district_prereqs_to_build_improvement (City * city, int i_improv, boo if (! leader_can_build_district (&leaders[city->Body.CivID], required_district_id)) return false; - Improvement * improv = &p_bic_data->Improvements[i_improv]; bool is_wonder = is->current_config.enable_wonder_districts && improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder); // Check that we have the necessary terrain bool has_terrain_for_district = false; bool has_terrain_for_wonder = false; + bool has_river_terrain_for_district = false; FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { Tile * tile = wai.tile; if (! tile_suitable_for_district (tile, required_district_id, city, NULL)) continue; has_terrain_for_district = true; + if (requires_river && tile->vtable->m37_Get_River_Code (tile) != 0) + has_river_terrain_for_district = true; if (! is_wonder) - break; + continue; - if (is_wonder && wonder_is_buildable_on_tile (tile, i_improv)) { - has_terrain_for_wonder = true; - break; + if (wonder_is_buildable_on_tile (tile, i_improv)) { + if (! requires_river || tile->vtable->m37_Get_River_Code (tile) != 0) { + has_terrain_for_wonder = true; + break; + } } } - if (! has_terrain_for_district || (is_wonder && ! has_terrain_for_wonder)) { + if (! has_terrain_for_district || (requires_river && ! has_river_terrain_for_district) || + (is_wonder && ! has_terrain_for_wonder)) { return false; } @@ -18526,7 +18573,6 @@ city_meets_district_prereqs_to_build_improvement (City * city, int i_improv, boo return true; } - bool __fastcall patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply_strict_rules) { @@ -20319,7 +20365,8 @@ auto_build_great_wall_districts_for_civ (int civ_id) PopupForm * popup = get_popup_form (); set_popup_str_param (0, (char *)is->district_configs[GREAT_WALL_DISTRICT_ID].display_name, -1, -1); popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_BEGIN_GREAT_WALL_AUTO_BUILD", -1, 0, 0, 0); - patch_show_popup (popup, __, 0, 0); + if (civ_id == p_main_screen_form->Player_CivID) + patch_show_popup (popup, __, 0, 0); is->great_wall_auto_build = GWABS_RUNNING; @@ -20377,7 +20424,7 @@ auto_build_great_wall_districts_for_civ (int civ_id) if (has_district) existing_district_id = inst->district_type; - if (is_human) { + if (is_human && civ_id == p_main_screen_form->Player_CivID) { is->focused_tile = tile; Main_Screen_Form_bring_tile_into_view (p_main_screen_form, __, x, y, 0, true, false); @@ -29277,7 +29324,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc return; } - district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; + district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); return; } @@ -29288,7 +29335,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - //*p_debug_mode_bits |= 0xC; + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -30682,18 +30729,8 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t struct district_worker_record * rec = get_tracked_worker_record (this); struct pending_district_request * req = (rec != NULL) ? rec->pending_req : NULL; - if (req != NULL) { - City * req_city = (req->city != NULL) ? req->city : get_city_ptr (req->city_id); - if ((req->district_id >= 0) && - (req->district_id < is->district_count) && - (req_city != NULL) && - city_radius_contains_tile (req_city, to_x, to_y) && - (req->target_x == to_x) && (req->target_y == to_y)) { - struct district_config const * cfg = &is->district_configs[req->district_id]; - if ((cfg->buildable_square_types_mask & (1 << SQ_Coast)) != 0) - return PBV_OK; - } - } + if (req != NULL) + return PBV_OK; } } From ff5e5204fee470222999f8229b7a5373b186859c Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 20 Jan 2026 20:15:35 -0800 Subject: [PATCH 240/356] Make ai city district build wait turns a config option, not hardcoded --- C3X.h | 1 + default.c3x_config.ini | 2 ++ injected_code.c | 5 +++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/C3X.h b/C3X.h index efba6271..94ed9823 100644 --- a/C3X.h +++ b/C3X.h @@ -389,6 +389,7 @@ struct c3x_config { bool allow_enter_canal_from_any_direction; bool ai_defends_districts; + int ai_city_district_max_build_wait_turns; bool disable_great_wall_city_defense_bonus; bool great_wall_districts_impassible_by_others; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 8ab9e571..40e85edf 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -920,6 +920,8 @@ max_contiguous_canal_districts = 5 ; infrastructure. Only applies when enable_districts is set to true. ai_defends_districts = true +ai_city_district_max_build_wait_turns = 20 + disable_great_wall_city_defense_bonus = false great_wall_districts_impassible_by_others = true auto_build_great_wall_around_territory = true diff --git a/injected_code.c b/injected_code.c index b780bb30..6af482e9 100644 --- a/injected_code.c +++ b/injected_code.c @@ -4062,9 +4062,9 @@ process_pending_district_request (Leader * leader, struct pending_district_reque if (req->assigned_worker_id >= 0) { Unit * assigned = get_unit_ptr (req->assigned_worker_id); if (assigned != NULL) { - // Check if more than 5 turns have elapsed since assignment and worker is not at target tile + // Check if more than allowed turns have elapsed since assignment and worker is not at target tile bool worker_at_target = (assigned->Body.X == req->target_x) && (assigned->Body.Y == req->target_y); - if ((*p_current_turn_no - req->worker_assigned_turn) > 5 && !worker_at_target) { + if ((*p_current_turn_no - req->worker_assigned_turn) > is->current_config.ai_city_district_max_build_wait_turns && !worker_at_target) { clear_tracked_worker_assignment_by_id (civ_id, req->assigned_worker_id); req->assigned_worker_id = -1; req->worker_assigned_turn = *p_current_turn_no; @@ -14345,6 +14345,7 @@ patch_init_floating_point () {"neighborhood_needed_message_frequency" , 4, offsetof (struct c3x_config, neighborhood_needed_message_frequency)}, {"max_contiguous_bridge_districts" , 3, offsetof (struct c3x_config, max_contiguous_bridge_districts)}, {"max_contiguous_canal_districts" , 5, offsetof (struct c3x_config, max_contiguous_canal_districts)}, + {"ai_city_district_max_build_wait_turns" , 20, offsetof (struct c3x_config, ai_city_district_max_build_wait_turns)}, {"per_extraterritorial_colony_relation_penalty" , 0, offsetof (struct c3x_config, per_extraterritorial_colony_relation_penalty)}, }; From 9ddc81261a46e8207297f8145ff85a994288e16c Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 21 Jan 2026 09:41:50 -0800 Subject: [PATCH 241/356] Refine naval unit healing logic --- civ_prog_objects.csv | 4 +- injected_code.c | 87 +++++++++++++++++++++++++------------------- 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 53617136..2a20193b 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -594,7 +594,7 @@ define, 0x5FF0E0, 0x614190, 0x5FEFC0, "PCX_Image_do_draw_centered_text", "in repl call, 0x41AF59, 0x41C067, 0x41AFD9, "PCX_Image_do_draw_cntd_text_for_strat_res", "" repl call, 0x41AE9F, 0x41BFA9, 0x41AF1F, "Sprite_draw_strat_res_on_city_screen", "" define, 0x41AE1B, 0x41BF23, 0x41AE9B, "ADDR_MOST_STRAT_RES_ON_CITY_SCREEN", "void *" -define, 0x5BEB60, 0x5CD780, 0x5BE870, "Unit_can_heal_at", "bool (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" +inlead, 0x5BEB60, 0x5CD780, 0x5BE870, "Unit_can_heal_at", "bool (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" define, 0x5BECE0, 0x5CD900, 0x5BE9F0, "Unit_heal_at_start_of_turn", "void (__fastcall *) (Unit * this)" define, 0x469590, 0x46BD40, 0x469610, "check_online_skip_flag", "bool (__stdcall *) (int unit_id)" inlead, 0x448BF0, 0x44AB10, 0x448C70, "Leader_ai_eval_technology", "int (__fastcall *) (Leader * this, int edx, int id, bool param_2, bool param_3)" @@ -887,4 +887,4 @@ define, 0x5C1920, 0x0, 0x0, "Unit_airdrop", repl call, 0x4C0D87, 0x0, 0x0, "Leader_count_wonders_with_flag_ignore_great_wall", "" repl call, 0x5D3D67, 0x0, 0x0, "Tile_has_colony_ignore_extraterritorial", "" inlead, 0x440100, 0x0, 0x0, "Leader_get_attitude_toward", "int (__fastcall *) (Leader * this, int edx, int civ_id, int param_2)" -inlead, 0x5D7080, 0x0, 0x0, "Map_check_colony_location", "int (__fastcall *) (Map * this, int edx, int tile_x, int tile_y, int civ_id)" \ No newline at end of file +inlead, 0x5D7080, 0x0, 0x0, "Map_check_colony_location", "int (__fastcall *) (Map * this, int edx, int tile_x, int tile_y, int civ_id)" diff --git a/injected_code.c b/injected_code.c index 6af482e9..79ce0b16 100644 --- a/injected_code.c +++ b/injected_code.c @@ -10270,8 +10270,7 @@ get_water_continent_ids_connected_via_canal (Tile * tile, int * out_count) continue; struct district_instance * inst = get_district_instance (adj); - if ((inst == NULL) || (inst->district_type != CANAL_DISTRICT_ID) || - (! district_is_complete (adj, CANAL_DISTRICT_ID))) + if ((inst == NULL) || (inst->district_type != CANAL_DISTRICT_ID) || (! district_is_complete (adj, CANAL_DISTRICT_ID))) continue; bool seen_canal = false; @@ -12583,12 +12582,11 @@ patch_Tile_get_visible_resource_when_recomputing (Tile * tile, int edx, int civ_ int base_resource = Tile_get_resource_visible_to (tile, __, civ_id); - if ((base_resource < 0) && is->current_config.enable_districts) { + if (is->current_config.enable_districts) { struct district_instance * inst = get_district_instance (tile); if (inst != NULL && inst->state == DS_COMPLETED) { - int district_id = inst->district_type; - struct district_config * cfg = &is->district_configs[district_id]; - if ((cfg->generated_resource_id >= 0) && ((cfg->generated_resource_flags & MF_LOCAL) == 0)) { + struct district_config * cfg = &is->district_configs[inst->district_type]; + if (cfg->generated_resource_id >= 0) { int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); if ((owner_id == civ_id) && district_can_generate_resource (civ_id, cfg)) return cfg->generated_resource_id; @@ -18414,6 +18412,8 @@ patch_City_can_build_unit (City * this, int edx, int unit_type_id, bool exclude_ // Air units if (type->Unit_Class == UTC_Air) { if (is->current_config.enable_aerodrome_districts && is->current_config.air_units_use_aerodrome_districts_not_cities) { + if (find_pending_district_request (this, AERODROME_DISTRICT_ID) != NULL) + return false; if (! city_can_build_district (this, AERODROME_DISTRICT_ID)) return false; return city_has_required_district (this, AERODROME_DISTRICT_ID); @@ -18421,6 +18421,8 @@ patch_City_can_build_unit (City * this, int edx, int unit_type_id, bool exclude_ // Naval units } else if (type->Unit_Class == UTC_Sea) { if (is->current_config.enable_port_districts && is->current_config.naval_units_use_port_districts_not_cities) { + if (find_pending_district_request (this, PORT_DISTRICT_ID) != NULL) + return false; if (! city_can_build_district (this, PORT_DISTRICT_ID)) return false; return city_has_required_district (this, PORT_DISTRICT_ID); @@ -26185,28 +26187,6 @@ patch_Unit_can_heal_at (Unit * this, int edx, int tile_x, int tile_y) return Unit_can_heal_at (this, __, tile_x, tile_y); } -void __fastcall -patch_Unit_heal_at_start_of_turn (Unit * this) -{ - bool healed_in_port = false; - - if (is->current_config.enable_districts && - is->current_config.enable_port_districts && - (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea)) { - Tile * tile = tile_at (this->Body.X, this->Body.Y); - if (tile_has_friendly_port_district (tile, this->Body.CivID)) - healed_in_port = true; - } - - Unit_heal_at_start_of_turn (this); - - if (healed_in_port && (this->Body.Damage > 0)) { - int heal_amt = Unit_get_max_hp (this) - 1; - int new_damage = this->Body.Damage - heal_amt; - this->Body.Damage = (new_damage < 0) ? 0 : new_damage; - } -} - bool __fastcall patch_Unit_can_airdrop (Unit * this) { @@ -28990,10 +28970,11 @@ get_canal_directions (Tile * tile, int tile_x, int tile_y, bool * out_water_dirs } // Manual overrides - overall algorithm works pretty well, but handle corner cases - if (!has_canal_dir && water_dirs[DIR_NE] && water_dirs[DIR_SW]) { draw_dir1 = DIR_NE; draw_dir2 = DIR_SW; } + if (!has_canal_dir && water_dirs[DIR_NE] && water_dirs[DIR_SW]) { draw_dir1 = DIR_NE; draw_dir2 = DIR_SW; } else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_S && water_dirs[DIR_N] && water_dirs[DIR_NE]) { draw_dir1 = DIR_N; draw_dir2 = DIR_S; } else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_SE && water_dirs[DIR_SE] && water_dirs[DIR_SW]) { draw_dir2 = DIR_SW; } else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_SE && water_dirs[DIR_NE] && water_dirs[DIR_N]) { draw_dir1 = DIR_N; } + else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_NW && water_dirs[DIR_S]) { draw_dir2 = DIR_S; } snprintf (ss, sizeof(ss), "Drawing canal at (%d,%d) dirs:%d,%d\n", tile_x, tile_y, draw_dir1, draw_dir2); (*p_OutputDebugStringA)(ss); @@ -30925,7 +30906,7 @@ patch_Unit_ai_eval_pillage_target (Unit * this, int edx, int tile_x, int tile_y) Tile * tile = tile_at (tile_x, tile_y); if ((base > 0) && is_enemy_maritime_district_tile (this, tile)) { - // Double the base score and add a flat kicker so districts outrank generic coast targets + // Double the base score so districts outrank generic coast targets base += base + 0x300; } @@ -31059,7 +31040,12 @@ try_path_to_friendly_port_district (Unit * unit) } } - if (best_x >= 0) { + if (unit->Body.X == best_x && unit->Body.Y == best_y) { + Unit_set_escortee (unit, __, -1); + Unit_set_state (unit, __, UnitState_Fortifying); + return true; + } + else if (best_x >= 0) { patch_Trade_Net_set_unit_path (is->trade_net, __, unit->Body.X, unit->Body.Y, best_x, best_y, unit, unit->Body.CivID, 0x81, NULL); Unit_set_escortee (unit, __, -1); @@ -31081,13 +31067,20 @@ patch_Unit_ai_move_naval_power_unit (Unit * this) if (is_enemy_maritime_district_tile (this, here) && patch_Unit_can_pillage (this, __, this->Body.X, this->Body.Y) && (patch_Unit_ai_eval_pillage_target (this, __, this->Body.X, this->Body.Y) > 0)) { - Unit_attack_tile (this, __, this->Body.X, this->Body.Y, false); + patch_Unit_attack_tile (this, __, this->Body.X, this->Body.Y, false); return; } } + // If damaged and CAN heal at current location (e.g. port district), fortify to heal + if (this->Body.Damage > 0 && patch_Unit_can_heal_at (this, __, this->Body.X, this->Body.Y)) { + Unit_set_escortee (this, __, -1); + Unit_set_state (this, __, UnitState_Fortifying); + return; + } + + // If damaged and cannot heal here, try to path to a friendly port district if ((this->Body.Damage > 0) && - ! patch_Unit_can_heal_at (this, __, this->Body.X, this->Body.Y) && try_path_to_friendly_port_district (this)) return; @@ -31100,8 +31093,16 @@ patch_Unit_ai_move_naval_power_unit (Unit * this) void __fastcall patch_Unit_ai_move_naval_transport (Unit * this) { + // If damaged and CAN heal at current location (e.g. port district), fortify to heal + if ((this->Body.Damage > 0) && + patch_Unit_can_heal_at (this, __, this->Body.X, this->Body.Y)) { + Unit_set_escortee (this, __, -1); + Unit_set_state (this, __, UnitState_Fortifying); + return; + } + + // If damaged and cannot heal here, try to path to a friendly port district if ((this->Body.Damage > 0) && - ! patch_Unit_can_heal_at (this, __, this->Body.X, this->Body.Y) && try_path_to_friendly_port_district (this)) return; @@ -31111,8 +31112,16 @@ patch_Unit_ai_move_naval_transport (Unit * this) void __fastcall patch_Unit_ai_move_naval_missile_transport (Unit * this) { + // If damaged and CAN heal at current location (e.g. port district), fortify to heal + if ((this->Body.Damage > 0) && + patch_Unit_can_heal_at (this, __, this->Body.X, this->Body.Y)) { + Unit_set_escortee (this, __, -1); + Unit_set_state (this, __, UnitState_Fortifying); + return; + } + + // If damaged and cannot heal here, try to path to a friendly port district if ((this->Body.Damage > 0) && - ! patch_Unit_can_heal_at (this, __, this->Body.X, this->Body.Y) && try_path_to_friendly_port_district (this)) return; @@ -31382,8 +31391,7 @@ patch_Tile_m17_Check_Irrigation (Tile * this, int edx, int visible_to_civ_id) if (inst == NULL || inst->district_type < 0 || inst->district_type >= is->district_count) return base; - if (is->district_configs[inst->district_type].allow_irrigation_from && - district_is_complete (this, inst->district_type)) + if (is->district_configs[inst->district_type].allow_irrigation_from && district_is_complete (this, inst->district_type)) return true; return base; @@ -31406,6 +31414,10 @@ patch_Unit_ai_eval_bombard_target (Unit * this, int edx, int tile_x, int tile_y, if ((inst == NULL) || (inst->district_type != GREAT_WALL_DISTRICT_ID) || (! district_is_complete (tile, GREAT_WALL_DISTRICT_ID))) return score; + int obsolete_id = is->district_infos[GREAT_WALL_DISTRICT_ID].obsoleted_by_id; + if ((obsolete_id >= 0) && Leader_has_tech (&leaders[this->Body.CivID], __, obsolete_id)) + return score; + int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); if ((owner_id <= 0) || (owner_id == this->Body.CivID)) return score; @@ -31443,5 +31455,6 @@ patch_Unit_ai_eval_bombard_target (Unit * this, int edx, int tile_x, int tile_y, return score; } + // TCC requires a main function be defined even though it's never used. int main () { return 0; } From 2b3ff25ab23305e17ecc718eca3ae5dc075a3f03 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 22 Jan 2026 07:24:40 -0800 Subject: [PATCH 242/356] Identified bug in try_path_to_maritime_district, comment out for now --- injected_code.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/injected_code.c b/injected_code.c index 79ce0b16..b2f27598 100644 --- a/injected_code.c +++ b/injected_code.c @@ -31061,8 +31061,14 @@ try_path_to_friendly_port_district (Unit * unit) void __fastcall patch_Unit_ai_move_naval_power_unit (Unit * this) { + if (! is->current_config.enable_districts || + ! is->current_config.enable_port_districts || + ! is->current_config.naval_units_use_port_districts_not_cities) { + return; + } + // If we're already sitting on an enemy maritime district, pillage it immediately - if (is->current_config.enable_districts && is->current_config.enable_port_districts && this->Body.Container_Unit < 0) { + if (this->Body.Container_Unit < 0) { Tile * here = tile_at (this->Body.X, this->Body.Y); if (is_enemy_maritime_district_tile (this, here) && patch_Unit_can_pillage (this, __, this->Body.X, this->Body.Y) && @@ -31080,14 +31086,18 @@ patch_Unit_ai_move_naval_power_unit (Unit * this) } // If damaged and cannot heal here, try to path to a friendly port district - if ((this->Body.Damage > 0) && - try_path_to_friendly_port_district (this)) + if ((this->Body.Damage > 0) && try_path_to_friendly_port_district (this)) return; + Unit_ai_move_naval_power_unit (this); + return; + + /* if (try_path_to_maritime_district (this)) return; Unit_ai_move_naval_power_unit (this); + */ } void __fastcall From d45f90d871ffd8c409f10758bf3b92ec1b882482 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 22 Jan 2026 10:51:20 -0800 Subject: [PATCH 243/356] Add heal_units_in_one_turn option --- C3X.h | 3 ++ civ_prog_objects.csv | 2 ++ injected_code.c | 69 ++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/C3X.h b/C3X.h index 94ed9823..5c82d367 100644 --- a/C3X.h +++ b/C3X.h @@ -691,6 +691,7 @@ struct district_config { struct district_bonus_list shield_bonus_extras; struct district_bonus_list happiness_bonus_extras; int defense_bonus_percent; + bool heal_units_in_one_turn; char const * generated_resource; int generated_resource_id; short generated_resource_flags; @@ -909,6 +910,7 @@ struct parsed_district_definition { int btn_tile_sheet_column; int btn_tile_sheet_row; int defense_bonus_percent; + bool heal_units_in_one_turn; int culture_bonus; int science_bonus; int food_bonus; @@ -948,6 +950,7 @@ struct parsed_district_definition { bool has_btn_tile_sheet_column; bool has_btn_tile_sheet_row; bool has_defense_bonus_percent; + bool has_heal_units_in_one_turn; bool has_culture_bonus; bool has_science_bonus; bool has_food_bonus; diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 2a20193b..22ec572e 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -318,6 +318,7 @@ repl call, 0x4E67f4, 0x4EF255, 0x4E68B4, "City_shows_harbor_icon", "" inlead, 0x4ADF90, 0x4B4F70, 0x4AE020, "City_can_trade_via_air", "bool (__fastcall *) (City * this)" repl call, 0x4E6842, 0x4EF2A7, 0x4E6902, "City_shows_airport_icon", "" define, 0x4B1F90, 0x4B8F10, 0x4B2020, "City_count_improvements_with_flag", "int (__fastcall *) (City * this, int edx, enum ImprovementTypeFlags flag)" +repl call, 0x5BEDAB, 0x0, 0x0, "City_count_improvements_with_flag_in_Unit_heal_at_start_of_turn", "" repl call, 0x4A12EC, 0x4A7E9C, 0x4A137C, "Unit_check_king_for_defense_priority", "" repl call, 0x4A131A, 0x4A7ECA, 0x4A13AA, "Unit_check_king_for_defense_priority", "" repl call, 0x4A133D, 0x4A7EED, 0x4A13CD, "Unit_check_king_for_defense_priority", "" @@ -450,6 +451,7 @@ repl call, 0x52BED0, 0x536310, 0x52BF60, "Map_get_tile_to_check_visibility", "" repl call, 0x5B555E, 0x5C3EBF, 0x5B526E, "Unit_get_max_moves_after_barricade_attack", "" define, 0x56D2C0, 0x579C40, 0x56D230, "city_at", "City * (__cdecl *) (int x, int y)" repl call, 0x4A2018, 0x4A8C28, 0x4A20A8, "city_at_in_find_bombard_defender", "" +repl call, 0x5BEd6D, 0x0, 0x0, "city_at_in_Unit_heal_at_start_of_turn", "" inlead, 0x5C3510, 0x5D2140, 0x5C3220, "Unit_check_bombard_target", "bool (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" inlead, 0x5C5160, 0x5D3E40, 0x5C4E70, "Unit_can_disembark_anything", "bool (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" repl call, 0x52C912, 0x536D92, 0x52C9A2, "Unit_get_defense_for_bombardable_unit_check", "" diff --git a/injected_code.c b/injected_code.c index b2f27598..62a5ab58 100644 --- a/injected_code.c +++ b/injected_code.c @@ -2590,7 +2590,12 @@ get_district_instance (Tile * tile) if (! itable_look_up (&is->district_tile_map, (int)tile, &stored_ptr)) return NULL; - return (struct district_instance *)(long)stored_ptr; + struct district_instance * inst = (struct district_instance *)(long)stored_ptr; + + if ((inst == NULL) || (inst->district_type < 0) || (inst->district_type >= is->district_count)) + return NULL; + + return inst; } struct wonder_district_info * @@ -3439,8 +3444,6 @@ wai_next (struct work_area_iter * wai) if (inst == NULL) continue; int district_id = inst->district_type; - if ((district_id < 0) || (district_id >= is->district_count)) - continue; if (wai->completed_districts_only && (! district_is_complete (candidate, district_id))) continue; } @@ -6213,6 +6216,8 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->btn_tile_sheet_row = def->btn_tile_sheet_row; if (def->has_defense_bonus_percent) cfg->defense_bonus_percent = def->defense_bonus_percent; + if (def->has_heal_units_in_one_turn) + cfg->heal_units_in_one_turn = def->heal_units_in_one_turn; if (def->has_culture_bonus) { cfg->culture_bonus = def->culture_bonus; free_bonus_entry_list_override (&cfg->culture_bonus_extras, &defaults->culture_bonus_extras); @@ -6473,6 +6478,7 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.btn_tile_sheet_column = def->has_btn_tile_sheet_column ? def->btn_tile_sheet_column : 0; new_cfg.btn_tile_sheet_row = def->has_btn_tile_sheet_row ? def->btn_tile_sheet_row : 0; new_cfg.defense_bonus_percent = def->has_defense_bonus_percent ? def->defense_bonus_percent : 100; + new_cfg.heal_units_in_one_turn = def->has_heal_units_in_one_turn ? def->heal_units_in_one_turn : false; new_cfg.culture_bonus = def->has_culture_bonus ? def->culture_bonus : 0; new_cfg.science_bonus = def->has_science_bonus ? def->science_bonus : 0; new_cfg.food_bonus = def->has_food_bonus ? def->food_bonus : 0; @@ -7077,6 +7083,15 @@ handle_district_definition_key (struct parsed_district_definition * def, } else add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "heal_units_in_one_turn")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->heal_units_in_one_turn = (ival != 0); + def->has_heal_units_in_one_turn = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "culture_bonus")) { if (parse_district_bonus_entries (value, &def->culture_bonus, &def->culture_bonus_extras, parse_errors, line_number, key)) { def->has_culture_bonus = true; @@ -29376,7 +29391,7 @@ patch_Map_Renderer_m11_Draw_Tile_Irrigation (Map_Renderer *this, int edx, int vi } struct district_instance * inst = get_district_instance (is->current_render_tile); - if (inst == NULL || inst->district_type < 0 || inst->district_type >= is->district_count) { + if (inst == NULL) { Map_Renderer_m11_Draw_Tile_Irrigation (this, __, visible_to_civ, tile_x, tile_y, param_4, param_5, param_6); return; } @@ -31398,7 +31413,7 @@ patch_Tile_m17_Check_Irrigation (Tile * this, int edx, int visible_to_civ_id) return base; struct district_instance * inst = get_district_instance (this); - if (inst == NULL || inst->district_type < 0 || inst->district_type >= is->district_count) + if (inst == NULL) return base; if (is->district_configs[inst->district_type].allow_irrigation_from && district_is_complete (this, inst->district_type)) @@ -31465,6 +31480,50 @@ patch_Unit_ai_eval_bombard_target (Unit * this, int edx, int tile_x, int tile_y, return score; } +// Called to determine if unit is in a city (which if has a barracks, would heal in one turn). +// We add a districts check as well +City * __cdecl +patch_city_at_in_Unit_heal_at_start_of_turn (int tile_x, int tile_y) +{ + City * city = city_at (tile_x, tile_y); + if (! is->current_config.enable_districts) + return city; + + Tile * tile = tile_at (tile_x, tile_y); + if ((city == NULL) || (tile == NULL) || (tile == p_null_tile)) + return NULL; + + struct district_instance * inst = get_district_instance (tile); + if ((inst == NULL) || (inst->district_type < 0) || (inst->district_type >= is->district_count)) + return NULL; + + if (! district_is_complete (tile, inst->district_type)) + return NULL; + + if (! is->district_configs[inst->district_type].heal_units_in_one_turn) + return NULL; + + int territory_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); + if (territory_owner <= 0) + return NULL; + + return (City *)0x1; // City sentinel value +} + +// If a district that heals in one turn is present, and the city pointer is the sentinel value, return true +int __fastcall +patch_City_count_improvements_with_flag_in_Unit_heal_at_start_of_turn (City * this, int edx, int flag) +{ + if (! is->current_config.enable_districts) + return City_count_improvements_with_flag (this, __, flag); + + // If sentinel value for fully healing district, this will return true to heal in one turn + if (this == (City *)0x1) + return 1; + + return 0; +} + // TCC requires a main function be defined even though it's never used. int main () { return 0; } From 370624d95f62c843777e1b699e8873d9ef19d4b8 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 22 Jan 2026 14:44:00 -0800 Subject: [PATCH 244/356] Update patch_City_spawn_unit_if_done to ensure air units and naval units cant be spawned without appropriate districts --- Notes/district_todos.md | 7 +- Text/c3x-script.txt | 2 +- civ_prog_objects.csv | 4 + default.districts_config.txt | 648 ++++++++++++++++++----------------- injected_code.c | 156 ++++++++- 5 files changed, 479 insertions(+), 338 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index db897858..3a71db00 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,6 +1,4 @@ - - [x] Allow workers over water only if in radius of city that can build water district/wonder - - [x] Add ai_build_strategy option and AI worker handling - - [x] AI navies target maritime districts + - Firm up logic for river district rendering - Hoover Dam (use alt dir, special positioning b/c on river) @@ -23,6 +21,9 @@ - ~~Choose terrain type~~ - ~~Buttons~~ - ~~Named tiles~~ + - ~~Allow workers over water only if in radius of city that can build water district/wonder~~ + - ~~Add ai_build_strategy option and AI worker handling~~ + - ~~AI navies target maritime districts~~ - ~~Bump great wall bombard eval scores~~ - ~~- Make sure AI workers remove great walls after they have metallury~~ - ~~Prevent AI building districts on resources~~ diff --git a/Text/c3x-script.txt b/Text/c3x-script.txt index 66b1dbdd..96e00120 100644 --- a/Text/c3x-script.txt +++ b/Text/c3x-script.txt @@ -104,7 +104,7 @@ Razing cities is prohibited by the rules of this scenario. #C3X_CANT_PILLAGE #caption Can't do that, chief! #text -Pillaging Districts is prohibited by the rules of this scenario. +Pillaging Wonder Districts is prohibited by the rules of this scenario. #C3X_LIMITED_UNIT_CHANGE #map_center 0 diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 22ec572e..ce653f60 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -256,6 +256,7 @@ define, 0x53A960, 0x544C80, 0x53A9E0, "get_unit_support_info", "void (__std define, 0x55D2A0, 0x569330, 0x55D250, "Leader_get_free_unit_count", "int (__fastcall *) (Leader * this, int edx, int govt_id)" inlead, 0x55D030, 0x5690C0, 0x55CFE0, "Leader_count_maintenance_free_units", "int (__fastcall *) (Leader * this)" define, 0x56D7D0, 0x57A1B0, 0x56D740, "get_tile_occupier_id", "int (__cdecl *) (int x, int y, int pov_civ_id, bool respect_unit_invisibility)" +repl call, 0x4585F4, 0x0, 0x0, "get_tile_occupier_id_in_Unit_ai_move_naval_power_unit", "" inlead, 0x4ED220, 0x4F6140, 0x4ED2E0, "Main_Screen_Form_show_map_message", "void (__fastcall *) (Main_Screen_Form * this, int edx, int tile_x, int tile_y, char * text_key, bool pause)" repl call, 0x4BE664, 0x4C5C66, 0x4BE6F4, "Main_Screen_Form_show_wltk_message", "" repl call, 0x4BE71B, 0x4C5D1D, 0x4BE7AB, "Main_Screen_Form_show_wltk_ended_message", "" @@ -280,6 +281,8 @@ inlead, 0x561220, 0x56D350, 0x5611D0, "Leader_begin_unit_turns", "void (__f define, 0x4A1FA0, 0x4A8BB0, 0x4A2030, "Fighter_find_defender_against_bombardment", "Unit * (__fastcall *) (Fighter * this, int edx, Unit * bombarder, int tile_x, int tile_y, int bombarder_civ_id, bool land_lethal, bool sea_lethal)" repl call, 0x4A3ED9, 0x4AAB85, 0x4A3F69, "Fighter_find_actual_bombard_defender", "" repl call, 0x4A2BAA, 0x4A97F6, 0x4A2C3A, "Fighter_find_actual_bombard_defender", "" +define, 0x4A7280, 0x0, 0x0, "Fighter_eval_tile_vulnerability", "int (__fastcall *) (Fighter * this, int edx, Unit * unit, int tile_x, int tile_y)" +repl call, 0x45861F, 0x0, 0x0, "Fighter_eval_tile_vulnerability_in_Unit_ai_move_naval_power_unit", "" inlead, 0x5C6290, 0x5D50E0, 0x5C5FA0, "Unit_select", "void (__fastcall *) (Unit *this)" inlead, 0x5B6820, 0x5C5180, 0x5B6530, "Unit_select_stealth_attack_target", "bool (__fastcall *) (Unit * this, int edx, int target_civ_id, int x, int y, bool allow_popup, Unit ** out_selected_target)" repl call, 0x5C73FF, 0x5D629C, 0x5C710F, "Unit_try_flying_for_precision_strike", "" @@ -469,6 +472,7 @@ define, 0x55B1A0, 0x567100, 0x55B150, "Leader_reveal_tile", "void (__fastc define, 0x5FCB10, 0x610790, 0x5FC9F0, "PCX_Image_draw_onto", "int (__fastcall *) (PCX_Image * this, int edx, PCX_Image * canvas, int pixel_x, int pixel_y)" define, 0x5DBF60, 0x5EB4B0, 0x5DBE90, "Tile_get_terrain_move_cost", "int (__fastcall *) (Tile * this)" define, 0x56D340, 0x579CC0, 0x56D2B0, "get_combat_occupier", "int (__cdecl *) (int tile_x, int tile_y, int civ_id, byte ignore_visibility)" +repl call, 0x45871C, 0x0, 0x0, "get_combat_occupier_in_Unit_ai_move_naval_power_unit", "" define, 0x561480, 0x56D5E0, 0x561430, "Leader_has_tech_with_flag", "bool (__fastcall *) (Leader * this, int edx, enum AdvanceTypeFlags flag)" inlead, 0x57D980, 0x58A680, 0x57D6E0, "Trade_Net_recompute_city_connections", "void (__fastcall *) (Trade_Net * this, int edx, int civ_id, bool redo_road_network, byte param_3, int redo_roads_for_city_id)" inlead, 0x62B1A0, 0x64E220, 0x6274B0, "OpenGLRenderer_initialize", "int (__fastcall *) (OpenGLRenderer * this, int edx, PCX_Image * texture)" diff --git a/default.districts_config.txt b/default.districts_config.txt index bc305673..d6d75b13 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -6,364 +6,366 @@ [====================================================================================================================================================] #District -name = Encampment -tooltip = Build Encampment -img_paths = Encampment.pcx -btn_tile_sheet_row = 1 -btn_tile_sheet_column = 0 -vary_img_by_era = 1 -vary_img_by_culture = 0 -advance_prereq = Warrior Code -dependent_improvs = Barracks,"SAM Missile Battery" -buildable_on = desert,plains,grassland,tundra,floodplain,hills -defense_bonus_percent = 50 -allow_multiple = 1 -culture_bonus = 0 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Encampment +tooltip = Build Encampment +img_paths = Encampment.pcx +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 0 +vary_img_by_era = 1 +vary_img_by_culture = 0 +advance_prereq = Warrior Code +dependent_improvs = Barracks,"SAM Missile Battery" +buildable_on = desert,plains,grassland,tundra,floodplain,hills +heal_units_in_one_turn = 1 +defense_bonus_percent = 50 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 0 #District -name = Holy Site -tooltip = Build Holy Site -img_paths = HolySite_AMER.pcx, HolySite_EURO.pcx, HolySite_ROMAN.pcx, HolySite_MIDEAST.pcx, HolySite_ASIAN.pcx -btn_tile_sheet_row = 1 -btn_tile_sheet_column = 1 -vary_img_by_era = 0 -vary_img_by_culture = 1 -advance_prereq = "Ceremonial Burial" -dependent_improvs = Temple,Cathedral -buildable_on = desert,plains,grassland,tundra,floodplain,hills -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 2 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Holy Site +tooltip = Build Holy Site +img_paths = HolySite_AMER.pcx, HolySite_EURO.pcx, HolySite_ROMAN.pcx, HolySite_MIDEAST.pcx, HolySite_ASIAN.pcx +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 1 +vary_img_by_era = 0 +vary_img_by_culture = 1 +advance_prereq = "Ceremonial Burial" +dependent_improvs = Temple,Cathedral +buildable_on = desert,plains,grassland,tundra,floodplain,hills +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 2 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 0 #District -name = Campus -tooltip = Build Campus -img_paths = Campus.pcx -btn_tile_sheet_row = 1 -btn_tile_sheet_column = 2 -vary_img_by_era = 1 -vary_img_by_culture = 0 -advance_prereq = Literature -dependent_improvs = Library, University -buildable_on = desert,plains,grassland,tundra,floodplain,hills -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 1 -science_bonus = 2 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Campus +tooltip = Build Campus +img_paths = Campus.pcx +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 2 +vary_img_by_era = 1 +vary_img_by_culture = 0 +advance_prereq = Literature +dependent_improvs = Library, University +buildable_on = desert,plains,grassland,tundra,floodplain,hills +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 1 +science_bonus = 2 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 0 #District -name = Entertainment Complex -tooltip = Build Entertainment Complex -img_paths = EntertainmentComplex_AMER.pcx, EntertainmentComplex_EURO.pcx, EntertainmentComplex_ROMAN.pcx, EntertainmentComplex_MIDEAST.pcx, EntertainmentComplex_ASIAN.pcx -btn_tile_sheet_row = 1 -btn_tile_sheet_column = 3 -vary_img_by_era = 1 -vary_img_by_culture = 1 -advance_prereq = Construction -dependent_improvs = Colosseum -buildable_on = desert,plains,grassland,tundra,floodplain,hills -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 1 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 2 -shield_bonus = 0 -happiness_bonus = 0 +name = Entertainment Complex +tooltip = Build Entertainment Complex +img_paths = EntertainmentComplex_AMER.pcx, EntertainmentComplex_EURO.pcx, EntertainmentComplex_ROMAN.pcx, EntertainmentComplex_MIDEAST.pcx, EntertainmentComplex_ASIAN.pcx +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 3 +vary_img_by_era = 1 +vary_img_by_culture = 1 +advance_prereq = Construction +dependent_improvs = Colosseum +buildable_on = desert,plains,grassland,tundra,floodplain,hills +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 1 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 2 +shield_bonus = 0 +happiness_bonus = 0 #District -name = Commercial Hub -tooltip = Build Commercial Hub -img_paths = CommercialHub_AMER.pcx, CommercialHub_EURO.pcx, CommercialHub_ROMAN.pcx, CommercialHub_MIDEAST.pcx, CommercialHub_ASIAN.pcx -btn_tile_sheet_row = 1 -btn_tile_sheet_column = 4 -vary_img_by_era = 1 -vary_img_by_culture = 1 -advance_prereq = Currency -dependent_improvs = Marketplace, Bank, "Stock Exchange" -buildable_on = desert,plains,grassland,tundra,floodplain,hills -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 0 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 2 -shield_bonus = 0 -happiness_bonus = 0 +name = Commercial Hub +tooltip = Build Commercial Hub +img_paths = CommercialHub_AMER.pcx, CommercialHub_EURO.pcx, CommercialHub_ROMAN.pcx, CommercialHub_MIDEAST.pcx, CommercialHub_ASIAN.pcx +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 4 +vary_img_by_era = 1 +vary_img_by_culture = 1 +advance_prereq = Currency +dependent_improvs = Marketplace, Bank, "Stock Exchange" +buildable_on = desert,plains,grassland,tundra,floodplain,hills +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 2 +shield_bonus = 0 +happiness_bonus = 0 #District -name = Industrial Zone -tooltip = Build Industrial Zone -img_paths = IndustrialZone.pcx -btn_tile_sheet_row = 1 -btn_tile_sheet_column = 5 -vary_img_by_era = 1 -vary_img_by_culture = 0 -advance_prereq = Industrialization -dependent_improvs = Factory, "Manufacturing Plant" -buildable_on = desert,plains,grassland,tundra,floodplain,hills -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 0 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 4 -happiness_bonus = 0 +name = Industrial Zone +tooltip = Build Industrial Zone +img_paths = IndustrialZone.pcx +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 5 +vary_img_by_era = 1 +vary_img_by_culture = 0 +advance_prereq = Industrialization +dependent_improvs = Factory, "Manufacturing Plant" +buildable_on = desert,plains,grassland,tundra,floodplain,hills +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 4 +happiness_bonus = 0 #District -name = Data Center -tooltip = Build Data Center -img_paths = DataCenter.pcx -btn_tile_sheet_row = 1 -btn_tile_sheet_column = 6 -vary_img_by_era = 1 -vary_img_by_culture = 0 -advance_prereq = Computers -dependent_improvs = "Research Lab" -buildable_on = desert,plains,grassland,tundra,floodplain,hills -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 0 -science_bonus = 4 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Data Center +tooltip = Build Data Center +img_paths = DataCenter.pcx +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 6 +vary_img_by_era = 1 +vary_img_by_culture = 0 +advance_prereq = Computers +dependent_improvs = "Research Lab" +buildable_on = desert,plains,grassland,tundra,floodplain,hills +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 4 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 0 #District -name = Offshore Extraction Zone -tooltip = Build Offshore Extraction Zone -img_paths = OffshoreExtractionZone.pcx -btn_tile_sheet_row = 1 -btn_tile_sheet_column = 7 -vary_img_by_era = 0 -vary_img_by_culture = 0 -advance_prereq = Miniaturization -dependent_improvs = Offshore Platform -buildable_on = coast -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 0 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 2 -happiness_bonus = 0 +name = Offshore Extraction Zone +tooltip = Build Offshore Extraction Zone +img_paths = OffshoreExtractionZone.pcx +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 7 +vary_img_by_era = 0 +vary_img_by_culture = 0 +advance_prereq = Miniaturization +dependent_improvs = Offshore Platform +buildable_on = coast +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 2 +happiness_bonus = 0 #District -name = Park -tooltip = Build Park -img_paths = Park.pcx -btn_tile_sheet_row = 1 -btn_tile_sheet_column = 8 -vary_img_by_era = 1 -vary_img_by_culture = 0 -advance_prereq = Engineering -dependent_improvs = -buildable_on = forest -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 2 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 2 +name = Park +tooltip = Build Park +img_paths = Park.pcx +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 8 +vary_img_by_era = 1 +vary_img_by_culture = 0 +advance_prereq = Engineering +dependent_improvs = +buildable_on = forest +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 2 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 2 #District -name = Ski Resort -tooltip = Build Ski Resort -img_paths = SkiResort.pcx -btn_tile_sheet_row = 1 -btn_tile_sheet_column = 9 -vary_img_by_era = 0 -vary_img_by_culture = 0 -advance_prereq = Electricity -dependent_improvs = -buildable_on = snow-mountains -custom_height = 88 -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 2 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 4 -shield_bonus = 0 -happiness_bonus = 0 +name = Ski Resort +tooltip = Build Ski Resort +img_paths = SkiResort.pcx +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 9 +vary_img_by_era = 0 +vary_img_by_culture = 0 +advance_prereq = Electricity +dependent_improvs = +buildable_on = snow-mountains +custom_height = 88 +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 2 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 4 +shield_bonus = 0 +happiness_bonus = 0 #District -name = Neighborhood -tooltip = Build Neighborhood -buildable_on = desert,plains,grassland,tundra,floodplain,hills -advance_prereq = -defense_bonus_percent = 25 -allow_multiple = 1 -culture_bonus = 1 -science_bonus = 1 -food_bonus = 0 -gold_bonus = 1 -shield_bonus = 0 -happiness_bonus = 0 +name = Neighborhood +tooltip = Build Neighborhood +buildable_on = desert,plains,grassland,tundra,floodplain,hills +advance_prereq = +defense_bonus_percent = 25 +allow_multiple = 1 +culture_bonus = 1 +science_bonus = 1 +food_bonus = 0 +gold_bonus = 1 +shield_bonus = 0 +happiness_bonus = 0 #District -name = Wonder District -tooltip = Build Wonder District -buildable_on = desert,plains,grassland,tundra,floodplain,hills,coast -advance_prereq = -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 0 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Wonder District +tooltip = Build Wonder District +buildable_on = desert,plains,grassland,tundra,floodplain,hills,coast +advance_prereq = +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 0 #District -name = Distribution Hub -tooltip = Build Distribution Hub -buildable_on = desert,plains,grassland,tundra,floodplain,hills -advance_prereq = Construction -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 0 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Distribution Hub +tooltip = Build Distribution Hub +buildable_on = desert,plains,grassland,tundra,floodplain,hills +advance_prereq = Construction +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 0 #District -name = Aerodrome -tooltip = Build Aerodrome -buildable_on = desert,plains,grassland,tundra,floodplain,hills -advance_prereq = Flight -dependent_improvs = Airport -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 0 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Aerodrome +tooltip = Build Aerodrome +buildable_on = desert,plains,grassland,tundra,floodplain,hills +advance_prereq = Flight +dependent_improvs = Airport +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 0 #District -name = Port -tooltip = Build Port -buildable_on = coast -advance_prereq = Map Making -dependent_improvs = Harbor, "Commercial Dock" -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 0 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 2 -shield_bonus = 0 -happiness_bonus = 0 +name = Port +tooltip = Build Port +buildable_on = coast +advance_prereq = Map Making +dependent_improvs = Harbor, "Commercial Dock" +heal_units_in_one_turn = 1 +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 2 +shield_bonus = 0 +happiness_bonus = 0 #District -name = Central Rail Hub -tooltip = Build Central Rail Hub -btn_tile_sheet_row = 0 -btn_tile_sheet_column = 5 -vary_img_by_era = 1 -vary_img_by_culture = 1 -advance_prereq = Steam Power -resource_prereqs = Iron, Coal -dependent_improvs = "Mass Transit System" -buildable_on = desert,plains,grassland,tundra,floodplain,hills -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 0 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 4 -happiness_bonus = 0 +name = Central Rail Hub +tooltip = Build Central Rail Hub +btn_tile_sheet_row = 0 +btn_tile_sheet_column = 5 +vary_img_by_era = 1 +vary_img_by_culture = 1 +advance_prereq = Steam Power +resource_prereqs = Iron, Coal +dependent_improvs = "Mass Transit System" +buildable_on = desert,plains,grassland,tundra,floodplain,hills +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 4 +happiness_bonus = 0 #District -name = Energy Grid -tooltip = Build Energy Grid -btn_tile_sheet_row = 0 -btn_tile_sheet_column = 6 -vary_img_by_era = 1 -vary_img_by_culture = 1 -custom_height = 84 -advance_prereq = Industrialization -dependent_improvs = "Coal Plant", "Hydro Plant", "Solar Plant", "Nuclear Plant" -buildable_on = desert,plains,grassland,tundra,floodplain,hills,river -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 0 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 2 -happiness_bonus = 0 +name = Energy Grid +tooltip = Build Energy Grid +btn_tile_sheet_row = 0 +btn_tile_sheet_column = 6 +vary_img_by_era = 1 +vary_img_by_culture = 1 +custom_height = 84 +advance_prereq = Industrialization +dependent_improvs = "Coal Plant", "Hydro Plant", "Solar Plant", "Nuclear Plant" +buildable_on = desert,plains,grassland,tundra,floodplain,hills,river +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 2 +happiness_bonus = 0 #District -name = Bridge -tooltip = Build Bridge -btn_tile_sheet_row = 0 -btn_tile_sheet_column = 7 -advance_prereq = Industrialization -buildable_on = coast -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 0 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Bridge +tooltip = Build Bridge +btn_tile_sheet_row = 0 +btn_tile_sheet_column = 7 +advance_prereq = Industrialization +buildable_on = coast +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 0 #District -name = Canal -tooltip = Build Canal -btn_tile_sheet_row = 0 -btn_tile_sheet_column = 8 -advance_prereq = Industrialization -buildable_on = desert,plains,grassland,tundra,floodplain -defense_bonus_percent = 0 -allow_multiple = 1 -culture_bonus = 0 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 +name = Canal +tooltip = Build Canal +btn_tile_sheet_row = 0 +btn_tile_sheet_column = 8 +advance_prereq = Industrialization +buildable_on = desert,plains,grassland,tundra,floodplain +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 0 #District -name = Great Wall -btn_tile_sheet_row = 0 -btn_tile_sheet_column = 9 -tooltip = Build Great Wall -obsoleted_by = Metallurgy -wonder_prereqs = "The Great Wall" -buildable_on = desert,plains,grassland,tundra,floodplain,mountains,forest,swamp,jungle -draw_over_resources = 1 -defense_bonus_percent = 50 -allow_multiple = 1 -culture_bonus = 0 -science_bonus = 0 -food_bonus = 0 -gold_bonus = 0 -shield_bonus = 0 -happiness_bonus = 0 \ No newline at end of file +name = Great Wall +btn_tile_sheet_row = 0 +btn_tile_sheet_column = 9 +tooltip = Build Great Wall +obsoleted_by = Metallurgy +wonder_prereqs = "The Great Wall" +buildable_on = desert,plains,grassland,tundra,floodplain,mountains,forest,swamp,jungle +draw_over_resources = 1 +defense_bonus_percent = 50 +allow_multiple = 1 +culture_bonus = 0 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 0 \ No newline at end of file diff --git a/injected_code.c b/injected_code.c index 62a5ab58..122eea44 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16269,9 +16269,7 @@ patch_Unit_can_pillage (Unit * this, int edx, int tile_x, int tile_y) { bool base = Unit_can_pillage (this, __, tile_x, tile_y); - if (! is->current_config.enable_districts || - ! is->current_config.enable_wonder_districts || - is->current_config.completed_wonder_districts_can_be_destroyed) + if (! is->current_config.enable_districts || ! is->current_config.enable_wonder_districts) return base; Tile * tile = tile_at (tile_x, tile_y); @@ -16296,9 +16294,9 @@ patch_Unit_can_pillage (Unit * this, int edx, int tile_x, int tile_y) if (info == NULL || info->state != WDS_COMPLETED) return true; return is->current_config.completed_wonder_districts_can_be_destroyed; - } else { - return true; } + + return true; } bool __fastcall @@ -24662,6 +24660,59 @@ patch_City_spawn_unit_if_done (City * this) skip_spawn = true; } + // Check district requirements for air and naval units + if ((! skip_spawn) && (this->Body.Order_Type == COT_Unit) && is->current_config.enable_districts) { + int unit_id = this->Body.Order_ID; + UnitType * type = &p_bic_data->UnitTypes[unit_id]; + bool needs_district = false; + int required_district_id = -1; + + // Air units require aerodrome + if ((type->Unit_Class == UTC_Air) && + is->current_config.enable_aerodrome_districts && + is->current_config.air_units_use_aerodrome_districts_not_cities && + (! city_has_required_district (this, AERODROME_DISTRICT_ID))) { + needs_district = true; + required_district_id = AERODROME_DISTRICT_ID; + } + // Naval units require port + else if ((type->Unit_Class == UTC_Sea) && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities && + (! city_has_required_district (this, PORT_DISTRICT_ID))) { + needs_district = true; + required_district_id = PORT_DISTRICT_ID; + } + + if (needs_district) { + bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; + + // Mark district needed for AI + if (! is_human) + mark_city_needs_district (this, required_district_id); + + // Find fallback land unit + City_Order defensive_order = { .OrderID = -1, .OrderType = 0 }; + if (choose_defensive_unit_order (this, &defensive_order)) { + UnitType * def_type = &p_bic_data->UnitTypes[defensive_order.OrderID]; + if (def_type->Unit_Class == UTC_Land) + City_set_production (this, __, defensive_order.OrderType, defensive_order.OrderID, false); + } + + // Show message to human player + if (is_human && (this->Body.CivID == p_main_screen_form->Player_CivID)) { + char msg[160]; + char const * unit_name = type->Name; + char const * district_name = is->district_configs[required_district_id].name; + snprintf (msg, sizeof msg, "%s %s %s", unit_name, is->c3x_labels[CL_CONSTRUCTION_HALTED_DUE_TO_MISSING_DISTRICT], district_name); + msg[(sizeof msg) - 1] = '\0'; + show_map_specific_text (this->Body.X, this->Body.Y, msg, true); + } + + skip_spawn = true; + } + } + if (! skip_spawn) City_spawn_unit_if_done (this); } @@ -28557,8 +28608,10 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, else if (*out_variant == NE && anchor == DIR_SW && anchor_sprite_index == 20) { *out_pixel_x -= 2; *out_pixel_y += 4; } if (*out_variant == SW && (anchor_sprite_index == 0 || anchor_sprite_index == 4 || anchor_sprite_index == 10 || anchor_sprite_index == 1)) { *out_pixel_x +=2; *out_pixel_y -= 8; } - else if (*out_variant == SW && anchor == DIR_N && (anchor_sprite_index == 6)) { *out_pixel_x -= 6; *out_pixel_y -= 8; } - else if (*out_variant == SE && anchor == DIR_W && (anchor_sprite_index == 18)) { *out_pixel_x += 6; *out_pixel_y += 4; } + else if (*out_variant == SW && anchor == DIR_N && (anchor_sprite_index == 6)) { *out_pixel_x -= 6; *out_pixel_y -= 8; } + else if (*out_variant == SE && anchor == DIR_W && (anchor_sprite_index == 18)) { *out_pixel_x += 6; *out_pixel_y += 4; } + else if (*out_variant == NE && anchor == DIR_SW && (anchor_sprite_index == 51)) { *out_pixel_x += 20; *out_pixel_y -= 20; } + else if (*out_variant == SW && anchor == DIR_NE && (anchor_sprite_index == 0)) { *out_pixel_x -= 4; *out_pixel_y += 4; } } // Sheet 1 else if (anchor_sheet_index == 1) { @@ -30895,9 +30948,6 @@ is_enemy_maritime_district_tile (Unit * unit, Tile * tile) if (! tile->vtable->m35_Check_Is_Water (tile)) return false; - if (! tile->vtable->m18_Check_Mines (tile, __, 0)) - return false; - struct district_instance * inst = get_district_instance (tile); if (inst == NULL) return false; @@ -30920,7 +30970,7 @@ patch_Unit_ai_eval_pillage_target (Unit * this, int edx, int tile_x, int tile_y) return base; Tile * tile = tile_at (tile_x, tile_y); - if ((base > 0) && is_enemy_maritime_district_tile (this, tile)) { + if (is_enemy_maritime_district_tile (this, tile)) { // Double the base score so districts outrank generic coast targets base += base + 0x300; } @@ -31524,6 +31574,90 @@ patch_City_count_improvements_with_flag_in_Unit_heal_at_start_of_turn (City * th return 0; } +// Makes naval AI treat enemy port districts as valid targets to path toward. +int __cdecl +patch_get_tile_occupier_id_in_Unit_ai_move_naval_power_unit (int x, int y, int pov_civ_id, bool respect_unit_invisibility) +{ + int base = get_tile_occupier_id (x, y, pov_civ_id, respect_unit_invisibility); + if (base != -1) + return base; + + if (! is->current_config.enable_districts || ! is->current_config.enable_port_districts) + return -1; + + Tile * tile = tile_at (x, y); + if (tile == NULL || tile == p_null_tile) + return -1; + + struct district_instance * inst = get_district_instance (tile); + if (inst == NULL || inst->district_type != PORT_DISTRICT_ID) + return -1; + + if (! district_is_complete (tile, PORT_DISTRICT_ID)) + return -1; + + return (*tile->vtable->m38_Get_Territory_OwnerID) (tile); +} + +// Returns a non-zero score for enemy port district tiles so they pass the threshold check. +int __fastcall +patch_Fighter_eval_tile_vulnerability_in_Unit_ai_move_naval_power_unit (Fighter * this, int edx, Unit * unit, int tile_x, int tile_y) +{ + int base = Fighter_eval_tile_vulnerability (this, __, unit, tile_x, tile_y); + if (base > 0) + return base; + + if (! is->current_config.enable_districts || ! is->current_config.enable_port_districts) + return base; + + Tile * tile = tile_at (tile_x, tile_y); + if (tile == NULL || tile == p_null_tile) + return base; + + struct district_instance * inst = get_district_instance (tile); + if (inst == NULL) + return base; + + int owner = (*tile->vtable->m38_Get_Territory_OwnerID) (tile); + if (owner <= 0 || owner == unit->Body.CivID) + return base; + + if (! leaders[unit->Body.CivID].At_War[owner]) + return base; + + // Return a score high enough to pass threshold (iVar13 * 8 + 0x100) + // 0x300 should be sufficient for most cases + return 0x300; +} + +// Returns the territory owner for enemy port districts so the score isn't reduced. +int __cdecl +patch_get_combat_occupier_in_Unit_ai_move_naval_power_unit (int tile_x, int tile_y, int civ_id, byte ignore_visibility) +{ + int base = get_combat_occupier (tile_x, tile_y, civ_id, ignore_visibility); + if (base != -1) + return base; + + if (! is->current_config.enable_districts || ! is->current_config.enable_port_districts) + return base; + + Tile * tile = tile_at (tile_x, tile_y); + if (tile == NULL || tile == p_null_tile) + return -1; + + struct district_instance * inst = get_district_instance (tile); + if (inst == NULL) + return -1; + + int owner = (*tile->vtable->m38_Get_Territory_OwnerID) (tile); + if (owner <= 0 || owner == civ_id) + return -1; + + if (! leaders[civ_id].At_War[owner]) + return -1; + + return owner; +} // TCC requires a main function be defined even though it's never used. int main () { return 0; } From 4886f84d1d10990797a6d0b1223d20be297be1f7 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 22 Jan 2026 15:09:25 -0800 Subject: [PATCH 245/356] Add building-level bonus boosts to default config --- Notes/district_todos.md | 2 +- default.districts_config.txt | 22 +-- injected_code.c | 258 +++++++++++++++++++++-------------- 3 files changed, 171 insertions(+), 111 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 3a71db00..ed756e41 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,4 +1,4 @@ - + - Better patch for heal units in one turn - Firm up logic for river district rendering - Hoover Dam (use alt dir, special positioning b/c on river) diff --git a/default.districts_config.txt b/default.districts_config.txt index d6d75b13..676b76d4 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -19,11 +19,11 @@ buildable_on = desert,plains,grassland,tundra,floodplain,hills heal_units_in_one_turn = 1 defense_bonus_percent = 50 allow_multiple = 1 -culture_bonus = 0 +culture_bonus = 2 science_bonus = 0 food_bonus = 0 gold_bonus = 0 -shield_bonus = 0 +shield_bonus = 1, Barracks: 1 happiness_bonus = 0 #District @@ -39,7 +39,7 @@ dependent_improvs = Temple,Cathedral buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 -culture_bonus = 2 +culture_bonus = 2, Temple: 2, Cathedral: 2 science_bonus = 0 food_bonus = 0 gold_bonus = 0 @@ -59,8 +59,8 @@ dependent_improvs = Library, University buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 -culture_bonus = 1 -science_bonus = 2 +culture_bonus = 1, Library: 2, University: 2 +science_bonus = 1, Library: 2, University: 2 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 @@ -79,10 +79,10 @@ dependent_improvs = Colosseum buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 -culture_bonus = 1 +culture_bonus = 1, Colosseum: 1 science_bonus = 0 food_bonus = 0 -gold_bonus = 2 +gold_bonus = 2, Colosseum: 1 shield_bonus = 0 happiness_bonus = 0 @@ -99,10 +99,10 @@ dependent_improvs = Marketplace, Bank, "Stock Exchange" buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 -culture_bonus = 0 +culture_bonus = 0, Marketplace: 1 science_bonus = 0 food_bonus = 0 -gold_bonus = 2 +gold_bonus = 2, Marketplace: 1, Bank: 1, "Stock Exchange": 1 shield_bonus = 0 happiness_bonus = 0 @@ -123,7 +123,7 @@ culture_bonus = 0 science_bonus = 0 food_bonus = 0 gold_bonus = 0 -shield_bonus = 4 +shield_bonus = 2, Factory: 2, "Manufacturing Plant": 2 happiness_bonus = 0 #District @@ -140,7 +140,7 @@ buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 0 -science_bonus = 4 +science_bonus = 4, "Research Lab": 4 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 diff --git a/injected_code.c b/injected_code.c index 122eea44..a15c7a8c 100644 --- a/injected_code.c +++ b/injected_code.c @@ -29694,6 +29694,160 @@ ai_move_district_worker (Unit * worker, struct district_worker_record * rec) return false; } +bool +canal_has_different_adjacent_seas (int tile_x, int tile_y, int civ_id) +{ + int water_ids[2] = { -1, -1 }; + int water_count = 0; + FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { + if (tai.n == 0) + continue; + if (water_count >= 2) + break; + Tile * adj = tai.tile; + if ((adj == NULL) || (adj == p_null_tile)) + continue; + if (! adj->vtable->m35_Check_Is_Water (adj)) + continue; + if (adj->vtable->m38_Get_Territory_OwnerID (adj) != civ_id) + continue; + int sea_id = adj->vtable->m46_Get_ContinentID (adj); + if (water_count == 0 || sea_id != water_ids[0]) { + water_ids[water_count] = sea_id; + water_count += 1; + } + } + return water_count >= 2; +} + +// Check if two water tiles can reach each other via water within a radius, excluding a blocked tile +bool +water_tiles_connected_within_radius (int start_x, int start_y, int target_x, int target_y, int block_x, int block_y, int radius) +{ + // Simple BFS using a fixed-size visited array for tiles within radius + // workable_tile_counts[6] = 137 tiles for radius 6 + int max_tiles = 137; + int visited_x[137]; + int visited_y[137]; + int visited_count = 0; + int queue_x[137]; + int queue_y[137]; + int queue_head = 0; + int queue_tail = 0; + + queue_x[queue_tail] = start_x; + queue_y[queue_tail] = start_y; + queue_tail++; + visited_x[visited_count] = start_x; + visited_y[visited_count] = start_y; + visited_count++; + + while (queue_head < queue_tail) { + int cx = queue_x[queue_head]; + int cy = queue_y[queue_head]; + queue_head++; + + // Check 8 adjacent tiles + FOR_TILES_AROUND (tai, 9, cx, cy) { + if (tai.n == 0) + continue; + int nx = tai.tile_x; + int ny = tai.tile_y; + + // Found target + if (nx == target_x && ny == target_y) + return true; + + // Skip blocked tile (the isthmus) + if (nx == block_x && ny == block_y) + continue; + + // Check if within radius of original start + int dx = nx - start_x; + int dy = ny - start_y; + if (dx < 0) dx = -dx; + if (dy < 0) dy = -dy; + // Use Chebyshev distance approximation for hex grid + int dist = (dx + dy + ((dx > dy) ? dx : dy)) / 2; + if (dist > radius) + continue; + + Tile * adj = tai.tile; + if ((adj == NULL) || (adj == p_null_tile)) + continue; + if (! adj->vtable->m35_Check_Is_Water (adj)) + continue; + + // Check if already visited + bool already_visited = false; + for (int i = 0; i < visited_count; i++) { + if (visited_x[i] == nx && visited_y[i] == ny) { + already_visited = true; + break; + } + } + if (already_visited) + continue; + + // Add to queue and visited + if (visited_count < max_tiles && queue_tail < max_tiles) { + visited_x[visited_count] = nx; + visited_y[visited_count] = ny; + visited_count++; + queue_x[queue_tail] = nx; + queue_y[queue_tail] = ny; + queue_tail++; + } + } + } + return false; +} + +// Helper: Check if tile has two adjacent water tiles of the same sea that are not connected within radius +bool +canal_has_same_sea_isthmus (int tile_x, int tile_y, int civ_id, int check_radius) +{ + // Collect all adjacent water tiles owned by civ, grouped by sea ID + int adj_water_x[8]; + int adj_water_y[8]; + int adj_water_sea[8]; + int adj_water_count = 0; + + FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { + if (tai.n == 0) + continue; + if (adj_water_count >= 8) + break; + Tile * adj = tai.tile; + if ((adj == NULL) || (adj == p_null_tile)) + continue; + if (! adj->vtable->m35_Check_Is_Water (adj)) + continue; + if (adj->vtable->m38_Get_Territory_OwnerID (adj) != civ_id) + continue; + adj_water_x[adj_water_count] = tai.tile_x; + adj_water_y[adj_water_count] = tai.tile_y; + adj_water_sea[adj_water_count] = adj->vtable->m46_Get_ContinentID (adj); + adj_water_count++; + } + + // Check pairs of water tiles with the same sea ID + for (int i = 0; i < adj_water_count; i++) { + for (int j = i + 1; j < adj_water_count; j++) { + if (adj_water_sea[i] != adj_water_sea[j]) + continue; + // Same sea - check if they can reach each other without crossing the isthmus + if (! water_tiles_connected_within_radius ( + adj_water_x[i], adj_water_y[i], + adj_water_x[j], adj_water_y[j], + tile_x, tile_y, check_radius)) { + return true; + } + } + } + return false; +} + bool ai_worker_try_tile_improvement_district (Unit * worker) { @@ -29716,7 +29870,7 @@ ai_worker_try_tile_improvement_district (Unit * worker) if (get_district_instance (tile) != NULL) return false; // Check if a canal district could be built here, making sure there isn't already one nearby and - // that there are at least two different adjacent water bodies owned by the civ + // that either: (1) two different seas are adjacent, or (2) same sea tiles separated by isthmus if (is->current_config.enable_canal_districts && can_build_district_on_tile (tile, CANAL_DISTRICT_ID, civ_id)) { bool has_adjacent_canal = false; @@ -29734,29 +29888,9 @@ ai_worker_try_tile_improvement_district (Unit * worker) } if (! has_adjacent_canal) { - int water_ids[2] = { -1, -1 }; - int water_count = 0; - FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { - if (tai.n == 0) - continue; - if (water_count >= 2) - break; - Tile * adj = tai.tile; - if ((adj == NULL) || (adj == p_null_tile)) - continue; - if (! adj->vtable->m35_Check_Is_Water (adj)) - continue; - if (adj->vtable->m38_Get_Territory_OwnerID (adj) != civ_id) - continue; - - int sea_id = adj->vtable->m46_Get_ContinentID (adj); - if (water_count == 0 || sea_id != water_ids[0]) { - water_ids[water_count] = sea_id; - water_count += 1; - } - } - - if (water_count >= 2) { + bool should_build = canal_has_different_adjacent_seas (tile_x, tile_y, civ_id) || + canal_has_same_sea_isthmus (tile_x, tile_y, civ_id, 6); + if (should_build) { unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); unsigned int removable_flags = overlay_flags & 0xfc; tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); @@ -30978,70 +31112,6 @@ patch_Unit_ai_eval_pillage_target (Unit * this, int edx, int tile_x, int tile_y) return base; } -// Light-weight hunt for nearby maritime districts; returns true if a path/command was issued -bool -try_path_to_maritime_district (Unit * unit) -{ - if ((unit == NULL) || (! is->current_config.enable_districts)) - return false; - - if (unit->Body.Container_Unit >= 0) // Don't redirect cargo - return false; - - if (unit->Body.Moves <= 0) // No moves left, let base AI handle healing/other states - return false; - - int sea_id = unit->vtable->get_sea_id (unit); - if (sea_id < 0) - return false; - - int best_score = 0, best_x = -1, best_y = -1, best_path_len = 0; - const int search_radius = 8; - for (int dy = -search_radius; dy <= search_radius; dy++) { - for (int dx = -search_radius; dx <= search_radius; dx++) { - int tx = unit->Body.X + dx, ty = unit->Body.Y + dy; - wrap_tile_coords (&p_bic_data->Map, &tx, &ty); - - Tile * tile = tile_at (tx, ty); - if ((tile == NULL) || (tile == p_null_tile)) - continue; - - if ((short)tile->vtable->m46_Get_ContinentID (tile) != sea_id) - continue; - - if (! is_enemy_maritime_district_tile (unit, tile)) - continue; - - int score = patch_Unit_ai_eval_pillage_target (unit, __, (unsigned short)tx, ty); - if (score <= 0) - continue; - - int path_len = 0; - int path_result = patch_Trade_Net_set_unit_path (is->trade_net, __, unit->Body.X, unit->Body.Y, tx, ty, unit, unit->Body.CivID, 0x81, &path_len); - if (path_result <= 0) - continue; - - if ((score > best_score) || ((score == best_score) && ((best_path_len == 0) || (path_len < best_path_len)))) { - best_score = score; - best_path_len = path_len; - best_x = tx; - best_y = ty; - } - } - } - - if (best_x >= 0) { - patch_Trade_Net_set_unit_path (is->trade_net, __, unit->Body.X, unit->Body.Y, best_x, best_y, unit, unit->Body.CivID, 0x81, NULL); - Unit_set_escortee (unit, __, -1); - Unit_set_state (unit, __, UnitState_Go_To); - unit->Body.path_dest_x = best_x; - unit->Body.path_dest_y = best_y; - return true; - } - - return false; -} - // Light-weight hunt for nearby friendly ports; returns true if a path/command was issued bool try_path_to_friendly_port_district (Unit * unit) @@ -31156,13 +31226,6 @@ patch_Unit_ai_move_naval_power_unit (Unit * this) Unit_ai_move_naval_power_unit (this); return; - - /* - if (try_path_to_maritime_district (this)) - return; - - Unit_ai_move_naval_power_unit (this); - */ } void __fastcall @@ -31590,10 +31653,7 @@ patch_get_tile_occupier_id_in_Unit_ai_move_naval_power_unit (int x, int y, int p return -1; struct district_instance * inst = get_district_instance (tile); - if (inst == NULL || inst->district_type != PORT_DISTRICT_ID) - return -1; - - if (! district_is_complete (tile, PORT_DISTRICT_ID)) + if (inst == NULL) return -1; return (*tile->vtable->m38_Get_Territory_OwnerID) (tile); From 2fef37596a8df6bae608f86bb9ece61800442b6f Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 22 Jan 2026 15:36:56 -0800 Subject: [PATCH 246/356] Simplify AI canal building logic; Fix heal in one turn logic; Remove bridge & canal entry restrictions for performance reasons --- C3X.h | 2 - Notes/district_todos.md | 2 +- civ_prog_objects.csv | 4 +- default.c3x_config.ini | 2 - injected_code.c | 178 ++++++++++------------------------------ 5 files changed, 45 insertions(+), 143 deletions(-) diff --git a/C3X.h b/C3X.h index 5c82d367..6f9b8009 100644 --- a/C3X.h +++ b/C3X.h @@ -385,8 +385,6 @@ struct c3x_config { bool expand_water_tile_checks_to_city_work_area; int max_contiguous_bridge_districts; int max_contiguous_canal_districts; - bool allow_enter_bridge_from_any_direction; - bool allow_enter_canal_from_any_direction; bool ai_defends_districts; int ai_city_district_max_build_wait_turns; diff --git a/Notes/district_todos.md b/Notes/district_todos.md index ed756e41..de29011d 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -14,7 +14,7 @@ - Bridge (2 opposite sides have land "strait" or bridges) - Light annotations - Add commented instructions on fields in config files - - Double check PCX third column alignment + - Double check PCX third column alignment, clean up ground in industrial zone ## Maritime Districts diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index ce653f60..7ac94970 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -321,7 +321,6 @@ repl call, 0x4E67f4, 0x4EF255, 0x4E68B4, "City_shows_harbor_icon", "" inlead, 0x4ADF90, 0x4B4F70, 0x4AE020, "City_can_trade_via_air", "bool (__fastcall *) (City * this)" repl call, 0x4E6842, 0x4EF2A7, 0x4E6902, "City_shows_airport_icon", "" define, 0x4B1F90, 0x4B8F10, 0x4B2020, "City_count_improvements_with_flag", "int (__fastcall *) (City * this, int edx, enum ImprovementTypeFlags flag)" -repl call, 0x5BEDAB, 0x0, 0x0, "City_count_improvements_with_flag_in_Unit_heal_at_start_of_turn", "" repl call, 0x4A12EC, 0x4A7E9C, 0x4A137C, "Unit_check_king_for_defense_priority", "" repl call, 0x4A131A, 0x4A7ECA, 0x4A13AA, "Unit_check_king_for_defense_priority", "" repl call, 0x4A133D, 0x4A7EED, 0x4A13CD, "Unit_check_king_for_defense_priority", "" @@ -454,7 +453,6 @@ repl call, 0x52BED0, 0x536310, 0x52BF60, "Map_get_tile_to_check_visibility", "" repl call, 0x5B555E, 0x5C3EBF, 0x5B526E, "Unit_get_max_moves_after_barricade_attack", "" define, 0x56D2C0, 0x579C40, 0x56D230, "city_at", "City * (__cdecl *) (int x, int y)" repl call, 0x4A2018, 0x4A8C28, 0x4A20A8, "city_at_in_find_bombard_defender", "" -repl call, 0x5BEd6D, 0x0, 0x0, "city_at_in_Unit_heal_at_start_of_turn", "" inlead, 0x5C3510, 0x5D2140, 0x5C3220, "Unit_check_bombard_target", "bool (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" inlead, 0x5C5160, 0x5D3E40, 0x5C4E70, "Unit_can_disembark_anything", "bool (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" repl call, 0x52C912, 0x536D92, 0x52C9A2, "Unit_get_defense_for_bombardable_unit_check", "" @@ -601,7 +599,7 @@ repl call, 0x41AF59, 0x41C067, 0x41AFD9, "PCX_Image_do_draw_cntd_text_for_strat_ repl call, 0x41AE9F, 0x41BFA9, 0x41AF1F, "Sprite_draw_strat_res_on_city_screen", "" define, 0x41AE1B, 0x41BF23, 0x41AE9B, "ADDR_MOST_STRAT_RES_ON_CITY_SCREEN", "void *" inlead, 0x5BEB60, 0x5CD780, 0x5BE870, "Unit_can_heal_at", "bool (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" -define, 0x5BECE0, 0x5CD900, 0x5BE9F0, "Unit_heal_at_start_of_turn", "void (__fastcall *) (Unit * this)" +inlead, 0x5BECE0, 0x5CD900, 0x5BE9F0, "Unit_heal_at_start_of_turn", "void (__fastcall *) (Unit * this)" define, 0x469590, 0x46BD40, 0x469610, "check_online_skip_flag", "bool (__stdcall *) (int unit_id)" inlead, 0x448BF0, 0x44AB10, 0x448C70, "Leader_ai_eval_technology", "int (__fastcall *) (Leader * this, int edx, int id, bool param_2, bool param_3)" inlead, 0x4446C0, 0x4464C0, 0x444740, "Leader_ai_eval_government", "int (__fastcall *) (Leader * this, int edx, int id)" diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 40e85edf..da48ff5e 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -909,8 +909,6 @@ central_rail_hub_distribution_shield_bonus_percent = 25 expand_water_tile_checks_to_city_work_area = true workers_can_enter_coast = true -allow_enter_bridge_from_any_direction = false -allow_enter_canal_from_any_direction = false max_contiguous_bridge_districts = 3 max_contiguous_canal_districts = 5 diff --git a/injected_code.c b/injected_code.c index a15c7a8c..80ec74cf 100644 --- a/injected_code.c +++ b/injected_code.c @@ -14313,8 +14313,6 @@ patch_init_floating_point () {"disable_great_wall_city_defense_bonus" , false, offsetof (struct c3x_config, disable_great_wall_city_defense_bonus)}, {"expand_water_tile_checks_to_city_work_area" , false, offsetof (struct c3x_config, expand_water_tile_checks_to_city_work_area)}, {"workers_can_enter_coast" , false, offsetof (struct c3x_config, workers_can_enter_coast)}, - {"allow_enter_bridge_from_any_direction" , false, offsetof (struct c3x_config, allow_enter_bridge_from_any_direction)}, - {"allow_enter_canal_from_any_direction" , false, offsetof (struct c3x_config, allow_enter_canal_from_any_direction)}, {"enable_city_work_radii_highlights" , false, offsetof (struct c3x_config, enable_city_work_radii_highlights)}, {"introduce_all_human_players_at_start_of_hotseat_game" , false, offsetof (struct c3x_config, introduce_all_human_players_at_start_of_hotseat_game)}, {"allow_unload_from_army" , false, offsetof (struct c3x_config, allow_unload_from_army)}, @@ -17109,16 +17107,7 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, if ((inst != NULL) && (inst->district_type == BRIDGE_DISTRICT_ID) && district_is_complete (dest, inst->district_type)) { - bool allow_move = true; - if (! is->current_config.allow_enter_bridge_from_any_direction) { - int move_dx = 0, move_dy = 0; - int dir1 = -1, dir2 = -1; - neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); - get_bridge_directions (dest, nx, ny, &dir1, &dir2); - allow_move = move_matches_directions (move_dx, move_dy, dir1, dir2); - } - if (allow_move) - base_validity = AMV_OK; + base_validity = AMV_OK; } } } @@ -17135,16 +17124,7 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, if ((inst != NULL) && (inst->district_type == CANAL_DISTRICT_ID) && district_is_complete (dest, inst->district_type)) { - bool allow_move = true; - if (! is->current_config.allow_enter_canal_from_any_direction) { - int move_dx = 0, move_dy = 0; - int dir1 = -1, dir2 = -1; - neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); - get_canal_directions (dest, nx, ny, NULL, &dir1, &dir2); - allow_move = move_matches_directions (move_dx, move_dy, dir1, dir2); - } - if (allow_move) - base_validity = AMV_OK; + base_validity = AMV_OK; } } } @@ -17221,16 +17201,7 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr if ((inst != NULL) && (inst->district_type == BRIDGE_DISTRICT_ID) && district_is_complete (dest, inst->district_type)) { - bool allow_move = true; - if (! is->current_config.allow_enter_bridge_from_any_direction) { - int move_dx = 0, move_dy = 0; - int dir1 = -1, dir2 = -1; - neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); - get_bridge_directions (dest, to_x, to_y, &dir1, &dir2); - allow_move = move_matches_directions (move_dx, move_dy, dir1, dir2); - } - if (allow_move) - base_cost = Unit_get_max_move_points (unit); + base_cost = Unit_get_max_move_points (unit); } } } @@ -17246,17 +17217,7 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr if ((inst != NULL) && (inst->district_type == CANAL_DISTRICT_ID) && district_is_complete (dest, inst->district_type)) { - bool allow_move = true; - if (! is->current_config.allow_enter_canal_from_any_direction) { - int move_dx = 0, move_dy = 0; - int dir1 = -1, dir2 = -1; - bool water_dirs[9] = { false, false, false, false, false, false, false, false, false }; - neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); - get_canal_directions (dest, to_x, to_y, &water_dirs, &dir1, &dir2); - allow_move = move_matches_directions (move_dx, move_dy, dir1, dir2); - } - if (allow_move) - base_cost = Unit_get_max_move_points (unit); + base_cost = Unit_get_max_move_points (unit); } } } @@ -29803,14 +29764,28 @@ water_tiles_connected_within_radius (int start_x, int start_y, int target_x, int return false; } -// Helper: Check if tile has two adjacent water tiles of the same sea that are not connected within radius +// Check if tile separates adjacent water tiles that are not connected within a small radius bool canal_has_same_sea_isthmus (int tile_x, int tile_y, int civ_id, int check_radius) { - // Collect all adjacent water tiles owned by civ, grouped by sea ID + (void) civ_id; + (void) check_radius; + + // If another canal exists nearby, this isn't a unique isthmus target. + FOR_TILES_AROUND (tai, workable_tile_counts[2], tile_x, tile_y) { + if (tai.n == 0) + continue; + Tile * adj = tai.tile; + if ((adj == NULL) || (adj == p_null_tile)) + continue; + struct district_instance * adj_inst = get_district_instance (adj); + if ((adj_inst != NULL) && (adj_inst->district_type == CANAL_DISTRICT_ID)) + return false; + } + + // Collect all adjacent water tiles int adj_water_x[8]; int adj_water_y[8]; - int adj_water_sea[8]; int adj_water_count = 0; FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { @@ -29823,24 +29798,18 @@ canal_has_same_sea_isthmus (int tile_x, int tile_y, int civ_id, int check_radius continue; if (! adj->vtable->m35_Check_Is_Water (adj)) continue; - if (adj->vtable->m38_Get_Territory_OwnerID (adj) != civ_id) - continue; adj_water_x[adj_water_count] = tai.tile_x; adj_water_y[adj_water_count] = tai.tile_y; - adj_water_sea[adj_water_count] = adj->vtable->m46_Get_ContinentID (adj); adj_water_count++; } - // Check pairs of water tiles with the same sea ID + // Check pairs of adjacent water tiles that are not connected within radius 2 for (int i = 0; i < adj_water_count; i++) { for (int j = i + 1; j < adj_water_count; j++) { - if (adj_water_sea[i] != adj_water_sea[j]) - continue; - // Same sea - check if they can reach each other without crossing the isthmus if (! water_tiles_connected_within_radius ( adj_water_x[i], adj_water_y[i], adj_water_x[j], adj_water_y[j], - tile_x, tile_y, check_radius)) { + tile_x, tile_y, 2)) { return true; } } @@ -29889,7 +29858,7 @@ ai_worker_try_tile_improvement_district (Unit * worker) if (! has_adjacent_canal) { bool should_build = canal_has_different_adjacent_seas (tile_x, tile_y, civ_id) || - canal_has_same_sea_isthmus (tile_x, tile_y, civ_id, 6); + canal_has_same_sea_isthmus (tile_x, tile_y, civ_id, 2); if (should_build) { unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); unsigned int removable_flags = overlay_flags & 0xfc; @@ -30928,20 +30897,7 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t if ((inst != NULL) && (inst->district_type == BRIDGE_DISTRICT_ID) && district_is_complete (dest, inst->district_type)) { - bool allow_move = true; - if (! is->current_config.allow_enter_bridge_from_any_direction) { - int move_dx = 0, move_dy = 0; - int dir1 = -1, dir2 = -1; - int neighbor_index = Map_compute_neighbor_index (&p_bic_data->Map, __, from_x, from_y, to_x, to_y, 1000); - if (neighbor_index >= 0) { - neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); - get_bridge_directions (dest, to_x, to_y, &dir1, &dir2); - allow_move = move_matches_directions (move_dx, move_dy, dir1, dir2); - } else - allow_move = false; - } - if (allow_move) - return PBV_OK; + return PBV_OK; } } } @@ -30956,21 +30912,7 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t if ((inst != NULL) && (inst->district_type == CANAL_DISTRICT_ID) && district_is_complete (dest, inst->district_type)) { - bool allow_move = true; - if (!is->current_config.allow_enter_canal_from_any_direction) { - int move_dx = 0, move_dy = 0; - int dir1 = -1, dir2 = -1; - bool water_dirs[9] = { false, false, false, false, false, false, false, false, false }; - int neighbor_index = Map_compute_neighbor_index (&p_bic_data->Map, __, from_x, from_y, to_x, to_y, 1000); - if (neighbor_index >= 0) { - neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); - get_canal_directions (dest, to_x, to_y, &water_dirs, &dir1, &dir2); - allow_move = move_matches_directions (move_dx, move_dy, dir1, dir2); - } else - allow_move = false; - } - if (allow_move) - return PBV_OK; + return PBV_OK; } } } @@ -30999,17 +30941,6 @@ patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool if (inst != NULL && inst->district_type == BRIDGE_DISTRICT_ID && district_is_complete (dest, inst->district_type)) { allow_move = true; - if (! is->current_config.allow_enter_bridge_from_any_direction) { - int move_dx = 0, move_dy = 0; - int dir1 = -1, dir2 = -1; - int neighbor_index = Map_compute_neighbor_index (&p_bic_data->Map, __, this->Body.X, this->Body.Y, tile_x, tile_y, 1000); - if (neighbor_index >= 0) { - neighbor_index_to_diff (neighbor_index, &move_dx, &move_dy); - get_bridge_directions (dest, tile_x, tile_y, &dir1, &dir2); - allow_move = move_matches_directions (move_dx, move_dy, dir1, dir2); - } else - allow_move = false; - } } } @@ -31593,48 +31524,25 @@ patch_Unit_ai_eval_bombard_target (Unit * this, int edx, int tile_x, int tile_y, return score; } -// Called to determine if unit is in a city (which if has a barracks, would heal in one turn). -// We add a districts check as well -City * __cdecl -patch_city_at_in_Unit_heal_at_start_of_turn (int tile_x, int tile_y) -{ - City * city = city_at (tile_x, tile_y); - if (! is->current_config.enable_districts) - return city; - - Tile * tile = tile_at (tile_x, tile_y); - if ((city == NULL) || (tile == NULL) || (tile == p_null_tile)) - return NULL; - - struct district_instance * inst = get_district_instance (tile); - if ((inst == NULL) || (inst->district_type < 0) || (inst->district_type >= is->district_count)) - return NULL; - - if (! district_is_complete (tile, inst->district_type)) - return NULL; - - if (! is->district_configs[inst->district_type].heal_units_in_one_turn) - return NULL; - - int territory_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); - if (territory_owner <= 0) - return NULL; - - return (City *)0x1; // City sentinel value -} - -// If a district that heals in one turn is present, and the city pointer is the sentinel value, return true -int __fastcall -patch_City_count_improvements_with_flag_in_Unit_heal_at_start_of_turn (City * this, int edx, int flag) +void __fastcall +patch_Unit_heal_at_start_of_turn (Unit * this) { - if (! is->current_config.enable_districts) - return City_count_improvements_with_flag (this, __, flag); - - // If sentinel value for fully healing district, this will return true to heal in one turn - if (this == (City *)0x1) - return 1; + if (is->current_config.enable_districts) { + Tile * tile = tile_at ((this->Body).X, (this->Body).Y); + if ((tile != NULL) && (tile != p_null_tile)) { + struct district_instance * inst = get_district_instance (tile); + if (inst != NULL && district_is_complete (tile, inst->district_type) && + is->district_configs[inst->district_type].heal_units_in_one_turn) { + int territory_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); + if (territory_owner == this->Body.CivID) { + (this->Body).Damage = 0; + return; + } + } + } + } - return 0; + Unit_heal_at_start_of_turn (this); } // Makes naval AI treat enemy port districts as valid targets to path toward. From 289258d8a97753cd93f6b3a654fa643c92400fc4 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 22 Jan 2026 15:53:16 -0800 Subject: [PATCH 247/356] Add logic for AI protecting ports --- injected_code.c | 57 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/injected_code.c b/injected_code.c index 80ec74cf..a4a55e92 100644 --- a/injected_code.c +++ b/injected_code.c @@ -31045,7 +31045,7 @@ patch_Unit_ai_eval_pillage_target (Unit * this, int edx, int tile_x, int tile_y) // Light-weight hunt for nearby friendly ports; returns true if a path/command was issued bool -try_path_to_friendly_port_district (Unit * unit) +try_path_to_friendly_port_district (Unit * unit, bool require_damaged, bool require_undefended) { if ((unit == NULL) || ! is->current_config.enable_districts || @@ -31058,7 +31058,7 @@ try_path_to_friendly_port_district (Unit * unit) if (unit->Body.Moves <= 0) return false; - if (unit->Body.Damage <= 0) + if (require_damaged && (unit->Body.Damage <= 0)) return false; if (p_bic_data->UnitTypes[unit->Body.UnitTypeID].Unit_Class != UTC_Sea) @@ -31089,6 +31089,25 @@ try_path_to_friendly_port_district (Unit * unit) if ((occupier_id != -1) && (occupier_id != unit->Body.CivID)) continue; + if (require_undefended) { + bool has_friendly_sea_unit = false; + FOR_UNITS_ON (uti, tile) { + Unit * on_tile = uti.unit; + if ((on_tile == NULL) || (on_tile->Body.Container_Unit >= 0)) + continue; + if (on_tile->Body.CivID != unit->Body.CivID) + continue; + if (p_bic_data->UnitTypes[on_tile->Body.UnitTypeID].Unit_Class != UTC_Sea) + continue; + if (on_tile->Body.ID == unit->Body.ID) + continue; + has_friendly_sea_unit = true; + break; + } + if (has_friendly_sea_unit) + continue; + } + if (! is_below_stack_limit (tile, unit->Body.CivID, UTC_Sea)) continue; @@ -31130,7 +31149,8 @@ patch_Unit_ai_move_naval_power_unit (Unit * this) if (! is->current_config.enable_districts || ! is->current_config.enable_port_districts || ! is->current_config.naval_units_use_port_districts_not_cities) { - return; + Unit_ai_move_naval_power_unit (this); + return; } // If we're already sitting on an enemy maritime district, pillage it immediately @@ -31152,7 +31172,11 @@ patch_Unit_ai_move_naval_power_unit (Unit * this) } // If damaged and cannot heal here, try to path to a friendly port district - if ((this->Body.Damage > 0) && try_path_to_friendly_port_district (this)) + if ((this->Body.Damage > 0) && try_path_to_friendly_port_district (this, true, false)) + return; + + // If configured, try to keep at least one ship defending each friendly port district + if (is->current_config.ai_defends_districts && try_path_to_friendly_port_district (this, false, true)) return; Unit_ai_move_naval_power_unit (this); @@ -31162,6 +31186,13 @@ patch_Unit_ai_move_naval_power_unit (Unit * this) void __fastcall patch_Unit_ai_move_naval_transport (Unit * this) { + if (! is->current_config.enable_districts || + ! is->current_config.enable_port_districts || + ! is->current_config.naval_units_use_port_districts_not_cities) { + Unit_ai_move_naval_transport (this); + return; + } + // If damaged and CAN heal at current location (e.g. port district), fortify to heal if ((this->Body.Damage > 0) && patch_Unit_can_heal_at (this, __, this->Body.X, this->Body.Y)) { @@ -31171,8 +31202,7 @@ patch_Unit_ai_move_naval_transport (Unit * this) } // If damaged and cannot heal here, try to path to a friendly port district - if ((this->Body.Damage > 0) && - try_path_to_friendly_port_district (this)) + if ((this->Body.Damage > 0) && try_path_to_friendly_port_district (this, true, false)) return; Unit_ai_move_naval_transport (this); @@ -31181,6 +31211,13 @@ patch_Unit_ai_move_naval_transport (Unit * this) void __fastcall patch_Unit_ai_move_naval_missile_transport (Unit * this) { + if (! is->current_config.enable_districts || + ! is->current_config.enable_port_districts || + ! is->current_config.naval_units_use_port_districts_not_cities) { + patch_Unit_ai_move_naval_missile_transport (this); + return; + } + // If damaged and CAN heal at current location (e.g. port district), fortify to heal if ((this->Body.Damage > 0) && patch_Unit_can_heal_at (this, __, this->Body.X, this->Body.Y)) { @@ -31190,8 +31227,7 @@ patch_Unit_ai_move_naval_missile_transport (Unit * this) } // If damaged and cannot heal here, try to path to a friendly port district - if ((this->Body.Damage > 0) && - try_path_to_friendly_port_district (this)) + if ((this->Body.Damage > 0) && try_path_to_friendly_port_district (this, true, false)) return; Unit_ai_move_naval_missile_transport (this); @@ -31203,11 +31239,10 @@ patch_Unit_ai_move_air_bombard_unit (Unit * this) if (! (is->current_config.enable_districts && is->current_config.enable_aerodrome_districts && is->current_config.air_units_use_aerodrome_districts_not_cities)) { + Unit_ai_move_air_bombard_unit (this); + return; } - Unit_ai_move_air_bombard_unit (this); - return; - if (this->Body.Damage > 0) { Unit_set_escortee (this, __, -1); Unit_set_state (this, __, UnitState_Fortifying); From 13ebce0eff23d271f09a1454ee25c1fd1a253e58 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 22 Jan 2026 17:04:59 -0800 Subject: [PATCH 248/356] Add additive bonuses for defense_bonus_percent --- C3X.h | 2 ++ Notes/district_todos.md | 3 +-- default.districts_config.txt | 2 +- injected_code.c | 32 +++++++++++++++++++++----------- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/C3X.h b/C3X.h index 6f9b8009..3f57b148 100644 --- a/C3X.h +++ b/C3X.h @@ -688,6 +688,7 @@ struct district_config { struct district_bonus_list gold_bonus_extras; struct district_bonus_list shield_bonus_extras; struct district_bonus_list happiness_bonus_extras; + struct district_bonus_list defense_bonus_extras; int defense_bonus_percent; bool heal_units_in_one_turn; char const * generated_resource; @@ -921,6 +922,7 @@ struct parsed_district_definition { struct district_bonus_list gold_bonus_extras; struct district_bonus_list shield_bonus_extras; struct district_bonus_list happiness_bonus_extras; + struct district_bonus_list defense_bonus_extras; unsigned int buildable_square_types_mask; char * buildable_by_civs[32]; int buildable_by_civ_count; diff --git a/Notes/district_todos.md b/Notes/district_todos.md index de29011d..4766d09d 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,4 +1,3 @@ - - Better patch for heal units in one turn - Firm up logic for river district rendering - Hoover Dam (use alt dir, special positioning b/c on river) @@ -14,7 +13,7 @@ - Bridge (2 opposite sides have land "strait" or bridges) - Light annotations - Add commented instructions on fields in config files - - Double check PCX third column alignment, clean up ground in industrial zone + - Double check PCX third column alignment, clean up ground in industrial zone, Newton's University, Grand Cathedral? ## Maritime Districts diff --git a/default.districts_config.txt b/default.districts_config.txt index 676b76d4..a83f89bb 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -17,7 +17,7 @@ advance_prereq = Warrior Code dependent_improvs = Barracks,"SAM Missile Battery" buildable_on = desert,plains,grassland,tundra,floodplain,hills heal_units_in_one_turn = 1 -defense_bonus_percent = 50 +defense_bonus_percent = 50, Barracks: 25, hills: 25 allow_multiple = 1 culture_bonus = 2 science_bonus = 0 diff --git a/injected_code.c b/injected_code.c index a4a55e92..5972c8cf 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5269,6 +5269,7 @@ free_dynamic_district_config (struct district_config * cfg) free_bonus_entry_list (&cfg->gold_bonus_extras); free_bonus_entry_list (&cfg->shield_bonus_extras); free_bonus_entry_list (&cfg->happiness_bonus_extras); + free_bonus_entry_list (&cfg->defense_bonus_extras); memset (cfg, 0, sizeof *cfg); } @@ -5446,6 +5447,7 @@ free_special_district_override_strings (struct district_config * cfg, struct dis free_bonus_entry_list_override (&cfg->gold_bonus_extras, &defaults->gold_bonus_extras); free_bonus_entry_list_override (&cfg->shield_bonus_extras, &defaults->shield_bonus_extras); free_bonus_entry_list_override (&cfg->happiness_bonus_extras, &defaults->happiness_bonus_extras); + free_bonus_entry_list_override (&cfg->defense_bonus_extras, &defaults->defense_bonus_extras); } void @@ -5513,7 +5515,7 @@ init_parsed_district_definition (struct parsed_district_definition * def) { memset (def, 0, sizeof *def); def->img_path_count = -1; - def->defense_bonus_percent = 100; + def->defense_bonus_percent = 0; def->render_strategy = DRS_BY_COUNT; def->ai_build_strategy = DABS_DISTRICT; def->buildable_square_types_mask = district_default_buildable_mask (); @@ -5648,6 +5650,7 @@ free_parsed_district_definition (struct parsed_district_definition * def) free_bonus_entry_list (&def->gold_bonus_extras); free_bonus_entry_list (&def->shield_bonus_extras); free_bonus_entry_list (&def->happiness_bonus_extras); + free_bonus_entry_list (&def->defense_bonus_extras); init_parsed_district_definition (def); } @@ -6214,8 +6217,11 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->btn_tile_sheet_column = def->btn_tile_sheet_column; if (def->has_btn_tile_sheet_row) cfg->btn_tile_sheet_row = def->btn_tile_sheet_row; - if (def->has_defense_bonus_percent) + if (def->has_defense_bonus_percent) { cfg->defense_bonus_percent = def->defense_bonus_percent; + free_bonus_entry_list_override (&cfg->defense_bonus_extras, &defaults->defense_bonus_extras); + move_bonus_entry_list (&cfg->defense_bonus_extras, &def->defense_bonus_extras); + } if (def->has_heal_units_in_one_turn) cfg->heal_units_in_one_turn = def->heal_units_in_one_turn; if (def->has_culture_bonus) { @@ -6477,7 +6483,7 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.y_offset = def->has_y_offset ? def->y_offset : 0; new_cfg.btn_tile_sheet_column = def->has_btn_tile_sheet_column ? def->btn_tile_sheet_column : 0; new_cfg.btn_tile_sheet_row = def->has_btn_tile_sheet_row ? def->btn_tile_sheet_row : 0; - new_cfg.defense_bonus_percent = def->has_defense_bonus_percent ? def->defense_bonus_percent : 100; + new_cfg.defense_bonus_percent = def->has_defense_bonus_percent ? def->defense_bonus_percent : 0; new_cfg.heal_units_in_one_turn = def->has_heal_units_in_one_turn ? def->heal_units_in_one_turn : false; new_cfg.culture_bonus = def->has_culture_bonus ? def->culture_bonus : 0; new_cfg.science_bonus = def->has_science_bonus ? def->science_bonus : 0; @@ -6499,6 +6505,8 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i move_bonus_entry_list (&new_cfg.shield_bonus_extras, &def->shield_bonus_extras); if (def->has_happiness_bonus) move_bonus_entry_list (&new_cfg.happiness_bonus_extras, &def->happiness_bonus_extras); + if (def->has_defense_bonus_percent) + move_bonus_entry_list (&new_cfg.defense_bonus_extras, &def->defense_bonus_extras); if (def->has_generated_resource) { new_cfg.generated_resource = def->generated_resource; @@ -7075,13 +7083,11 @@ handle_district_definition_key (struct parsed_district_definition * def, add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } else if (slice_matches_str (key, "defense_bonus_percent")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->defense_bonus_percent = ival; + if (parse_district_bonus_entries (value, &def->defense_bonus_percent, &def->defense_bonus_extras, parse_errors, line_number, key)) { def->has_defense_bonus_percent = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else { + def->has_defense_bonus_percent = false; + } } else if (slice_matches_str (key, "heal_units_in_one_turn")) { struct string_slice val_slice = *value; @@ -8678,6 +8684,7 @@ void parse_building_and_tech_ids () resolve_district_bonus_building_entries (&is->district_configs[i].gold_bonus_extras, district_name, "gold_bonus", &district_parse_errors); resolve_district_bonus_building_entries (&is->district_configs[i].shield_bonus_extras, district_name, "shield_bonus", &district_parse_errors); resolve_district_bonus_building_entries (&is->district_configs[i].happiness_bonus_extras, district_name, "happiness_bonus", &district_parse_errors); + resolve_district_bonus_building_entries (&is->district_configs[i].defense_bonus_extras, district_name, "defense_bonus_percent", &district_parse_errors); } // Map wonder names to their improvement IDs for rendering under-construction wonders @@ -30104,9 +30111,12 @@ patch_get_building_defense_bonus_at (int x, int y, int param_3) if ((tile == NULL) || (tile == p_null_tile)) return base; - struct district_instance * inst = get_district_instance (tile); + struct district_instance * inst = get_district_instance (tile); if (inst != NULL) { - return is->district_configs[inst->district_type].defense_bonus_percent; + struct district_config const * cfg = &is->district_configs[inst->district_type]; + int bonus = cfg->defense_bonus_percent; + bonus += apply_district_bonus_entries (inst, &cfg->defense_bonus_extras, inst->district_type); + return bonus; } return base; From 79c86dee9f92fadccf530e3e817323f179c100e2 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 22 Jan 2026 17:15:01 -0800 Subject: [PATCH 249/356] Refine logic for defending ports --- injected_code.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/injected_code.c b/injected_code.c index 5972c8cf..2f29f0eb 100644 --- a/injected_code.c +++ b/injected_code.c @@ -31163,9 +31163,23 @@ patch_Unit_ai_move_naval_power_unit (Unit * this) return; } + Tile * here = tile_at (this->Body.X, this->Body.Y); + if (here == NULL) { + Unit_ai_move_naval_power_unit (this); + return; + } + + // If we're on a port and the sole unit, fortify + if (is->current_config.ai_defends_districts && + tile_has_friendly_port_district (here, this->Body.CivID) && + count_units_at (this->Body.X, this->Body.Y, UF_0, this->Body.CivID, 0, -1) == 1) { + Unit_set_escortee (this, __, -1); + Unit_set_state (this, __, UnitState_Fortifying); + return; + } + // If we're already sitting on an enemy maritime district, pillage it immediately if (this->Body.Container_Unit < 0) { - Tile * here = tile_at (this->Body.X, this->Body.Y); if (is_enemy_maritime_district_tile (this, here) && patch_Unit_can_pillage (this, __, this->Body.X, this->Body.Y) && (patch_Unit_ai_eval_pillage_target (this, __, this->Body.X, this->Body.Y) > 0)) { @@ -31185,10 +31199,6 @@ patch_Unit_ai_move_naval_power_unit (Unit * this) if ((this->Body.Damage > 0) && try_path_to_friendly_port_district (this, true, false)) return; - // If configured, try to keep at least one ship defending each friendly port district - if (is->current_config.ai_defends_districts && try_path_to_friendly_port_district (this, false, true)) - return; - Unit_ai_move_naval_power_unit (this); return; } From 3e30415a40c36fa641bae3ea0b90c606f9de871e Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 22 Jan 2026 17:30:10 -0800 Subject: [PATCH 250/356] Streamline logic and remove repetitive code in patch_Unit_can_move_to_adjacent_tile --- injected_code.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/injected_code.c b/injected_code.c index 2f29f0eb..dca2b242 100644 --- a/injected_code.c +++ b/injected_code.c @@ -17089,13 +17089,13 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, AdjacentMoveValidity base_validity = Unit_can_move_to_adjacent_tile (this, __, neighbor_index, param_2); if (is->current_config.enable_districts) { + int nx, ny; + get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); + Tile * dest = tile_at (nx, ny); // Let workers step onto coast tiles when the config flag is enabled (base logic treats this as an invalid sea move) if (is->current_config.workers_can_enter_coast && is_worker (this) && ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { - int nx, ny; - get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); - Tile * dest = tile_at (nx, ny); if ((dest != NULL) && dest->vtable->m35_Check_Is_Water (dest) && (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) @@ -17106,14 +17106,9 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, if (is->current_config.enable_bridge_districts && (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Land) && ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { - int nx, ny; - get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); - Tile * dest = tile_at (nx, ny); if ((dest != NULL) && (dest != p_null_tile)) { struct district_instance * inst = get_district_instance (dest); - if ((inst != NULL) && - (inst->district_type == BRIDGE_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) { + if ((inst != NULL) && (inst->district_type == BRIDGE_DISTRICT_ID) && district_is_complete (dest, inst->district_type)) { base_validity = AMV_OK; } } @@ -17123,14 +17118,9 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, if (is->current_config.enable_canal_districts && (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea) && ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { - int nx, ny; - get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); - Tile * dest = tile_at (nx, ny); if ((dest != NULL) && (dest != p_null_tile)) { struct district_instance * inst = get_district_instance (dest); - if ((inst != NULL) && - (inst->district_type == CANAL_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) { + if ((inst != NULL) && (inst->district_type == CANAL_DISTRICT_ID) && district_is_complete (dest, inst->district_type)) { base_validity = AMV_OK; } } @@ -17140,9 +17130,6 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, is->current_config.enable_port_districts && is->current_config.naval_units_use_port_districts_not_cities && (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea)) { - int nx, ny; - get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); - Tile * dest = tile_at (nx, ny); if ((dest != NULL) && (dest != p_null_tile) && Tile_has_city (dest)) return AMV_INVALID_SEA_MOVE; } From 6329ea8c36d4981685dff1fc68219bb5a7952c72 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 22 Jan 2026 18:56:47 -0800 Subject: [PATCH 251/356] Add district-generated resources using same approach is mill-based, for one-tile-two-resources problem --- C3X.h | 34 +++++++--- Notes/district_todos.md | 2 + injected_code.c | 146 +++++++++++++++++++++++++++------------- 3 files changed, 128 insertions(+), 54 deletions(-) diff --git a/C3X.h b/C3X.h index 3f57b148..4a6f0d47 100644 --- a/C3X.h +++ b/C3X.h @@ -1126,6 +1126,26 @@ struct district_instance { struct natural_wonder_district_info natural_wonder_info; // Only used if district_type is a natural wonder district }; +enum extra_resource_tile_type { + ERT_MILL_RESOURCE = 0, + ERT_DISTRICT_RESOURCE +}; + +struct extra_resource_tile { + Tile * tile; + enum extra_resource_tile_type type; + union { + struct { + City * city; + struct mill * mill; + } mill_info; + struct { + struct district_instance * inst; + struct district_config * cfg; + } district_info; + }; +}; + struct named_tile_entry { int tile_x; int tile_y; @@ -1481,15 +1501,11 @@ struct injected_state { int count_ai_prod_valuations; int ai_prod_valuations_capacity; - // Used for generating resources from buildings - struct mill_tile { - Tile * tile; - City * city; - struct mill * mill; - } * mill_tiles; - int count_mill_tiles; - int mill_tiles_capacity; - struct mill_tile * got_mill_tile; + // Used for generating resources from buildings and districts + struct extra_resource_tile * resource_tiles; + int count_resource_tiles; + int resource_tiles_capacity; + struct extra_resource_tile * got_resource_tile; int saved_tile_count; // Stores the actual tile count in case p_bic_data->Map.TileCount was temporarily overwritten. Set to -1 when empty. byte * mill_input_resource_bits; // Array of bits, one for each resource. Stores whether or not each one is an input to any mill. diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 4766d09d..e6ae7b48 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,3 +1,5 @@ + - Figure out multi-resource single-tile generation problem + - Validate resource generation - Firm up logic for river district rendering - Hoover Dam (use alt dir, special positioning b/c on river) diff --git a/injected_code.c b/injected_code.c index dca2b242..db39c921 100644 --- a/injected_code.c +++ b/injected_code.c @@ -12521,10 +12521,23 @@ recompute_mill_yields_after_resource_change (Leader * leader_or_null) int -compare_mill_tiles (void const * vp_a, void const * vp_b) +resource_tile_resource_id (struct extra_resource_tile const * rt) { - struct mill_tile const * a = vp_a, * b = vp_b; - return a->mill->resource_id - b->mill->resource_id; + if (rt == NULL) + return -1; + if (rt->type == ERT_MILL_RESOURCE) { + return (rt->mill_info.mill != NULL) ? rt->mill_info.mill->resource_id : -1; + } else if (rt->district_info.cfg != NULL) { + return rt->district_info.cfg->generated_resource_id; + } + return -1; +} + +int +compare_resource_tiles (void const * vp_a, void const * vp_b) +{ + struct extra_resource_tile const * a = vp_a, * b = vp_b; + return resource_tile_resource_id (a) - resource_tile_resource_id (b); } void __fastcall @@ -12535,7 +12548,7 @@ patch_Trade_Net_recompute_resources (Trade_Net * this, int edx, bool skip_popups memset (is->extra_available_resources, 0, is->extra_available_resources_capacity * ints_per_city * sizeof (unsigned)); // Assemble list of mill tiles - is->count_mill_tiles = 0; + is->count_resource_tiles = 0; if (p_cities->Cities != NULL) for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { City * city = get_city_ptr (city_index); @@ -12545,23 +12558,64 @@ patch_Trade_Net_recompute_resources (Trade_Net * this, int edx, bool skip_popups if (((mill->flags & MF_LOCAL) == 0) && has_active_building (city, mill->improv_id) && can_generate_resource (city->Body.CivID, mill)) { - reserve (sizeof is->mill_tiles[0], - (void **)&is->mill_tiles, - &is->mill_tiles_capacity, - is->count_mill_tiles); - is->mill_tiles[is->count_mill_tiles++] = (struct mill_tile) { - .tile = tile_at (city->Body.X, city->Body.Y), - .city = city, - .mill = mill + Tile * resource_tile = tile_at (city->Body.X, city->Body.Y); + if ((resource_tile == NULL) || (resource_tile == p_null_tile)) + continue; + reserve (sizeof is->resource_tiles[0], + (void **)&is->resource_tiles, + &is->resource_tiles_capacity, + is->count_resource_tiles); + is->resource_tiles[is->count_resource_tiles++] = (struct extra_resource_tile) { + .tile = resource_tile, + .type = ERT_MILL_RESOURCE, + .mill_info = { + .city = city, + .mill = mill + } }; } } } - qsort (is->mill_tiles, is->count_mill_tiles, sizeof is->mill_tiles[0], compare_mill_tiles); + if (is->current_config.enable_districts) { + FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { + Tile * district_tile = (Tile *)(long)tei.key; + struct district_instance * inst = (struct district_instance *)(long)tei.value; + if ((district_tile == NULL) || (district_tile == p_null_tile) || (inst == NULL) || (inst->state != DS_COMPLETED)) + continue; + + int district_id = inst->district_type; + if ((district_id < 0) || (district_id >= is->district_count)) + continue; - is->got_mill_tile = NULL; + struct district_config * cfg = &is->district_configs[district_id]; + if ((cfg == NULL) || (cfg->generated_resource_id < 0)) + continue; + + int owner_id = district_tile->vtable->m38_Get_Territory_OwnerID (district_tile); + if ((owner_id < 0) || (owner_id >= 32)) + continue; + if (! district_can_generate_resource (owner_id, cfg)) + continue; + + reserve (sizeof is->resource_tiles[0], + (void **)&is->resource_tiles, + &is->resource_tiles_capacity, + is->count_resource_tiles); + is->resource_tiles[is->count_resource_tiles++] = (struct extra_resource_tile) { + .tile = district_tile, + .type = ERT_DISTRICT_RESOURCE, + .district_info = { + .inst = inst, + .cfg = cfg + } + }; + } + } + qsort (is->resource_tiles, is->count_resource_tiles, sizeof is->resource_tiles[0], compare_resource_tiles); + + is->got_resource_tile = NULL; is->saved_tile_count = p_bic_data->Map.TileCount; - p_bic_data->Map.TileCount += is->count_mill_tiles; + p_bic_data->Map.TileCount += is->count_resource_tiles; Trade_Net_recompute_resources (this, __, skip_popups); // Restore the tile count if necessary. It may have already been restored by patch_Map_Renderer_m71_Draw_Tiles. This happens when the call to @@ -12577,45 +12631,46 @@ patch_Trade_Net_recompute_resources (Trade_Net * this, int edx, bool skip_popups } Tile * -get_mill_tile (int index) +get_resource_tile (int index) { - struct mill_tile * mt = &is->mill_tiles[index]; - is->got_mill_tile = mt; - return mt->tile; + struct extra_resource_tile * rt = &is->resource_tiles[index]; + is->got_resource_tile = rt; + return rt->tile; } -Tile * __fastcall patch_Map_get_tile_when_recomputing_resources_1 (Map * map, int edx, int index) { return (index < is->saved_tile_count) ? Map_get_tile (map, __, index) : get_mill_tile (index - is->saved_tile_count); } -Tile * __fastcall patch_Map_get_tile_when_recomputing_resources_2 (Map * map, int edx, int index) { return (index < is->saved_tile_count) ? Map_get_tile (map, __, index) : get_mill_tile (index - is->saved_tile_count); } -Tile * __fastcall patch_Map_get_tile_when_recomputing_resources_3 (Map * map, int edx, int index) { return (index < is->saved_tile_count) ? Map_get_tile (map, __, index) : get_mill_tile (index - is->saved_tile_count); } -Tile * __fastcall patch_Map_get_tile_when_recomputing_resources_4 (Map * map, int edx, int index) { return (index < is->saved_tile_count) ? Map_get_tile (map, __, index) : get_mill_tile (index - is->saved_tile_count); } -Tile * __fastcall patch_Map_get_tile_when_recomputing_resources_5 (Map * map, int edx, int index) { return (index < is->saved_tile_count) ? Map_get_tile (map, __, index) : get_mill_tile (index - is->saved_tile_count); } +Tile * __fastcall patch_Map_get_tile_when_recomputing_resources_1 (Map * map, int edx, int index) { return (index < is->saved_tile_count) ? Map_get_tile (map, __, index) : get_resource_tile (index - is->saved_tile_count); } +Tile * __fastcall patch_Map_get_tile_when_recomputing_resources_2 (Map * map, int edx, int index) { return (index < is->saved_tile_count) ? Map_get_tile (map, __, index) : get_resource_tile (index - is->saved_tile_count); } +Tile * __fastcall patch_Map_get_tile_when_recomputing_resources_3 (Map * map, int edx, int index) { return (index < is->saved_tile_count) ? Map_get_tile (map, __, index) : get_resource_tile (index - is->saved_tile_count); } +Tile * __fastcall patch_Map_get_tile_when_recomputing_resources_4 (Map * map, int edx, int index) { return (index < is->saved_tile_count) ? Map_get_tile (map, __, index) : get_resource_tile (index - is->saved_tile_count); } +Tile * __fastcall patch_Map_get_tile_when_recomputing_resources_5 (Map * map, int edx, int index) { return (index < is->saved_tile_count) ? Map_get_tile (map, __, index) : get_resource_tile (index - is->saved_tile_count); } int __fastcall patch_Tile_get_visible_resource_when_recomputing (Tile * tile, int edx, int civ_id) { - if (is->got_mill_tile != NULL) { - struct mill_tile * mt = is->got_mill_tile; - is->got_mill_tile = NULL; - if (has_resources_required_by_building (mt->city, mt->mill->improv_id)) - return mt->mill->resource_id; - else - return -1; - } - - int base_resource = Tile_get_resource_visible_to (tile, __, civ_id); - - if (is->current_config.enable_districts) { - struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && inst->state == DS_COMPLETED) { - struct district_config * cfg = &is->district_configs[inst->district_type]; - if (cfg->generated_resource_id >= 0) { - int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); - if ((owner_id == civ_id) && district_can_generate_resource (civ_id, cfg)) + if (is->got_resource_tile != NULL) { + struct extra_resource_tile * rt = is->got_resource_tile; + is->got_resource_tile = NULL; + if (rt->type == ERT_MILL_RESOURCE) { + if ((rt->mill_info.city != NULL) && (rt->mill_info.mill != NULL) && + has_resources_required_by_building (rt->mill_info.city, rt->mill_info.mill->improv_id)) + return rt->mill_info.mill->resource_id; + else + return -1; + } else { + Tile * district_tile = rt->tile; + struct district_instance * inst = rt->district_info.inst; + struct district_config * cfg = rt->district_info.cfg; + if ((district_tile != NULL) && (district_tile != p_null_tile) && (inst != NULL) && + (cfg != NULL) && (inst->state == DS_COMPLETED)) { + int owner_id = district_tile->vtable->m38_Get_Territory_OwnerID (district_tile); + if ((owner_id == civ_id) && district_can_generate_resource (owner_id, cfg)) return cfg->generated_resource_id; } + return -1; } } + int base_resource = Tile_get_resource_visible_to (tile, __, civ_id); return base_resource; } @@ -14546,9 +14601,10 @@ patch_init_floating_point () is->count_ai_prod_valuations = 0; is->ai_prod_valuations_capacity = 0; - is->mill_tiles = NULL; - is->count_mill_tiles = 0; - is->mill_tiles_capacity = 0; + is->resource_tiles = NULL; + is->count_resource_tiles = 0; + is->resource_tiles_capacity = 0; + is->got_resource_tile = NULL; is->saved_tile_count = -1; is->mill_input_resource_bits = NULL; From 5bacf42461960a57d7ef671c653920224694a7c0 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 22 Jan 2026 19:21:21 -0800 Subject: [PATCH 252/356] Add logic for bridge connecting two continents --- injected_code.c | 86 +++++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 35 deletions(-) diff --git a/injected_code.c b/injected_code.c index db39c921..cab295fa 100644 --- a/injected_code.c +++ b/injected_code.c @@ -9914,6 +9914,50 @@ bridge_district_tile_is_valid (int tile_x, int tile_y) return true; } +bool +bridge_tile_connects_two_continents (int tile_x, int tile_y, int civ_id) +{ + struct bridge_pair { + int dx1, dy1; + int dx2, dy2; + }; + + Map * map = &p_bic_data->Map; + const struct bridge_pair pairs[] = { + {0, -2, 0, 2}, + {-2, 0, 2, 0}, + {-1, -1, 1, 1}, + {-1, 1, 1, -1}, + }; + + for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { + int ax = tile_x + pairs[i].dx1; + int ay = tile_y + pairs[i].dy1; + wrap_tile_coords (map, &ax, &ay); + if (! tile_is_land (civ_id, ax, ay, true)) + continue; + Tile * first = tile_at (ax, ay); + if ((first == NULL) || (first == p_null_tile)) + continue; + + int bx = tile_x + pairs[i].dx2; + int by = tile_y + pairs[i].dy2; + wrap_tile_coords (map, &bx, &by); + if (! tile_is_land (civ_id, bx, by, true)) + continue; + Tile * second = tile_at (bx, by); + if ((second == NULL) || (second == p_null_tile)) + continue; + + int cont_a = first->vtable->m46_Get_ContinentID (first); + int cont_b = second->vtable->m46_Get_ContinentID (second); + if ((cont_a >= 0) && (cont_b >= 0) && (cont_a != cont_b)) + return true; + } + + return false; +} + bool canal_district_tile_is_valid (int tile_x, int tile_y) { @@ -29941,37 +29985,9 @@ ai_worker_try_tile_improvement_district (Unit * worker) } } - if (! has_adjacent_bridge) { - bool north_land = false; - bool south_land = false; - bool west_land = false; - bool east_land = false; - bool ne_land = false; - bool nw_land = false; - bool se_land = false; - bool sw_land = false; - - FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { - if (tai.n == 0) - continue; - int dx = 0, dy = 0; - neighbor_index_to_diff (tai.n, &dx, &dy); - bool is_land = tile_is_land (civ_id, tile_x + dx, tile_y + dy, false); - if (dx == 0 && dy == -2) north_land = is_land; - else if (dx == 0 && dy == 2) south_land = is_land; - else if (dx == -2 && dy == 0) west_land = is_land; - else if (dx == 2 && dy == 0) east_land = is_land; - else if (dx == 1 && dy == -1) ne_land = is_land; - else if (dx == -1 && dy == -1) nw_land = is_land; - else if (dx == 1 && dy == 1) se_land = is_land; - else if (dx == -1 && dy == 1) sw_land = is_land; - } - - bool has_land_pair = (north_land && south_land) || - (west_land && east_land) || - (ne_land && sw_land) || - (nw_land && se_land); - if (has_land_pair) { + // Only build if no bridges are within the immediate 9-tile radius and this tile links two civ-owned continents spotted inside the 21 tiles (radius 2) neighborhood. + if (! has_adjacent_bridge && + bridge_tile_connects_two_continents (tile_x, tile_y, civ_id)) { unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); unsigned int removable_flags = overlay_flags & 0xfc; tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); @@ -31709,18 +31725,18 @@ patch_get_combat_occupier_in_Unit_ai_move_naval_power_unit (int tile_x, int tile Tile * tile = tile_at (tile_x, tile_y); if (tile == NULL || tile == p_null_tile) - return -1; + return base; struct district_instance * inst = get_district_instance (tile); if (inst == NULL) - return -1; + return base; int owner = (*tile->vtable->m38_Get_Territory_OwnerID) (tile); if (owner <= 0 || owner == civ_id) - return -1; + return base; if (! leaders[civ_id].At_War[owner]) - return -1; + return base; return owner; } From 2557f849243ef12a77ec2c85180d017f9e0d52a6 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 22 Jan 2026 23:04:42 -0800 Subject: [PATCH 253/356] Add auto_add_road, auto_add_railroad --- C3X.h | 6 ++ Notes/district_todos.md | 4 +- default.districts_config.txt | 2 +- injected_code.c | 107 ++++++++++++++++++++++++----------- 4 files changed, 84 insertions(+), 35 deletions(-) diff --git a/C3X.h b/C3X.h index 4a6f0d47..0d1f5fd3 100644 --- a/C3X.h +++ b/C3X.h @@ -663,6 +663,8 @@ struct district_config { bool align_to_coast; bool draw_over_resources; bool allow_irrigation_from; + bool auto_add_road; + bool auto_add_railroad; int custom_width; int custom_height; int x_offset; @@ -902,6 +904,8 @@ struct parsed_district_definition { bool align_to_coast; bool draw_over_resources; bool allow_irrigation_from; + bool auto_add_road; + bool auto_add_railroad; int custom_width; int custom_height; int x_offset; @@ -982,6 +986,8 @@ struct parsed_district_definition { bool has_buildable_by_civ_cultures; bool has_buildable_on_districts; bool has_allow_irrigation_from; + bool has_auto_add_road; + bool has_auto_add_railroad; }; struct parsed_wonder_definition { diff --git a/Notes/district_todos.md b/Notes/district_todos.md index e6ae7b48..d86b0494 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,4 +1,6 @@ - - Figure out multi-resource single-tile generation problem + - Auto add road/railrood + - Upfront bridge/canal algorithm + - AI great wall build strategy setting - Validate resource generation - Firm up logic for river district rendering diff --git a/default.districts_config.txt b/default.districts_config.txt index a83f89bb..14c6c7b3 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -17,7 +17,7 @@ advance_prereq = Warrior Code dependent_improvs = Barracks,"SAM Missile Battery" buildable_on = desert,plains,grassland,tundra,floodplain,hills heal_units_in_one_turn = 1 -defense_bonus_percent = 50, Barracks: 25, hills: 25 +defense_bonus_percent = 50, Barracks: 25, hills: 25 allow_multiple = 1 culture_bonus = 2 science_bonus = 0 diff --git a/injected_code.c b/injected_code.c index cab295fa..f0f590ee 100644 --- a/injected_code.c +++ b/injected_code.c @@ -64,6 +64,8 @@ struct injected_state * is = ADDR_INJECTED_STATE; #define PEDIA_MULTIPAGE_EFFECTS_BUTTON_ID 0x222004 #define PEDIA_MULTIPAGE_PREV_BUTTON_ID 0x222005 +#define TILE_FLAG_ROAD 0x1 +#define TILE_FLAG_RAILROAD 0x2 #define TILE_FLAG_MINE 0x4 #define NEIGHBORHOOD_DISTRICT_ID 0 @@ -4310,6 +4312,7 @@ district_is_complete(Tile * tile, int district_id) bool is_natural_wonder = (district_id == NATURAL_WONDER_DISTRICT_ID); bool districts_disabled = ! is->current_config.enable_districts; bool natural_wonders_disabled = ! is->current_config.enable_natural_wonders; + struct district_config const * cfg = &is->district_configs[district_id]; if (districts_disabled && (!is_natural_wonder || natural_wonders_disabled)) return false; @@ -4348,6 +4351,22 @@ district_is_complete(Tile * tile, int district_id) if (district_instance_get_coords (inst, tile, &tile_x, &tile_y)) { set_tile_unworkable_for_all_cities (tile, tile_x, tile_y); + if (cfg->auto_add_road) { + bool has_road = tile->vtable->m25_Check_Roads (tile, __, 0) != 0; + if (! has_road) + tile->vtable->m56_Set_Tile_Flags (tile, __, 0, TILE_FLAG_ROAD, tile_x, tile_y); + } + + if (cfg->auto_add_railroad) { + bool has_railroad = tile->vtable->m23_Check_Railroads (tile, __, 0) != 0; + if (! has_railroad) { + int territory_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); + if ((territory_owner >= 0) && Leader_can_do_worker_job (&leaders[territory_owner], __, WJ_Build_Railroad, tile_x, tile_y, 0)) { + tile->vtable->m56_Set_Tile_Flags (tile, __, 0, TILE_FLAG_RAILROAD, tile_x, tile_y); + } + } + } + // Activate distribution hub if applicable if (is->current_config.enable_distribution_hub_districts && (district_id == DISTRIBUTION_HUB_DISTRICT_ID)) { @@ -6205,6 +6224,10 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->draw_over_resources = def->draw_over_resources; if (def->has_allow_irrigation_from) cfg->allow_irrigation_from = def->allow_irrigation_from; + if (def->has_auto_add_road) + cfg->auto_add_road = def->auto_add_road; + if (def->has_auto_add_railroad) + cfg->auto_add_railroad = def->auto_add_railroad; if (def->has_custom_width) cfg->custom_width = def->custom_width; if (def->has_custom_height) @@ -6477,6 +6500,8 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.align_to_coast = def->has_align_to_coast ? def->align_to_coast : false; new_cfg.draw_over_resources = def->has_draw_over_resources ? def->draw_over_resources : false; new_cfg.allow_irrigation_from = def->has_allow_irrigation_from ? def->allow_irrigation_from : false; + new_cfg.auto_add_road = def->has_auto_add_road ? def->auto_add_road : false; + new_cfg.auto_add_railroad = def->has_auto_add_railroad ? def->auto_add_railroad : false; new_cfg.custom_width = def->has_custom_width ? def->custom_width : 0; new_cfg.custom_height = def->has_custom_height ? def->custom_height : 0; new_cfg.x_offset = def->has_x_offset ? def->x_offset : 0; @@ -7028,6 +7053,24 @@ handle_district_definition_key (struct parsed_district_definition * def, } else add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "auto_add_road")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->auto_add_road = (ival != 0); + def->has_auto_add_road = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + + } else if (slice_matches_str (key, "auto_add_railroad")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->auto_add_railroad = (ival != 0); + def->has_auto_add_railroad = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "custom_width")) { struct string_slice val_slice = *value; int ival; @@ -9759,6 +9802,26 @@ tile_has_district_at (int tile_x, int tile_y, int district_id) return (inst != NULL) && (inst->district_type == district_id) && (district_is_complete (tile, district_id)); } +bool +tile_is_land (int civ_id, int tile_x, int tile_y, bool must_be_same_owner) +{ + if (must_be_same_owner && (civ_id <= 0)) + return false; + + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + return (tile != NULL) && (tile != p_null_tile) && (! tile->vtable->m35_Check_Is_Water (tile)) && + ((! must_be_same_owner) || (tile->Territory_OwnerID == civ_id)); +} + +bool +tile_is_water (int tile_x, int tile_y) +{ + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + return (tile != NULL) && (tile != p_null_tile) && (tile->vtable->m35_Check_Is_Water (tile)); +} + int count_contiguous_bridge_districts (int tile_x, int tile_y, int dx, int dy) { @@ -28387,26 +28450,6 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i return false; } -bool -tile_is_land (int civ_id, int tile_x, int tile_y, bool must_be_same_owner) -{ - if (must_be_same_owner && (civ_id <= 0)) - return false; - - wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); - Tile * tile = tile_at (tile_x, tile_y); - return (tile != NULL) && (tile != p_null_tile) && (! tile->vtable->m35_Check_Is_Water (tile)) && - ((! must_be_same_owner) || (tile->Territory_OwnerID == civ_id)); -} - -bool -tile_is_water (int tile_x, int tile_y) -{ - wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); - Tile * tile = tile_at (tile_x, tile_y); - return (tile != NULL) && (tile != p_null_tile) && (tile->vtable->m35_Check_Is_Water (tile)); -} - Tile * get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * out_sprite_index) { @@ -29440,7 +29483,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - *p_debug_mode_bits |= 0xC; + //*p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -29986,18 +30029,16 @@ ai_worker_try_tile_improvement_district (Unit * worker) } // Only build if no bridges are within the immediate 9-tile radius and this tile links two civ-owned continents spotted inside the 21 tiles (radius 2) neighborhood. - if (! has_adjacent_bridge && - bridge_tile_connects_two_continents (tile_x, tile_y, civ_id)) { - unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); - unsigned int removable_flags = overlay_flags & 0xfc; - tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); - if (removable_flags != 0) - tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, removable_flags, tile_x, tile_y); - ensure_district_instance (tile, BRIDGE_DISTRICT_ID, tile_x, tile_y); - Unit_set_state (worker, __, UnitState_Build_Mines); - worker->Body.Job_ID = WJ_Build_Mines; - return true; - } + if (! has_adjacent_bridge && bridge_tile_connects_two_continents (tile_x, tile_y, civ_id)) { + unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); + unsigned int removable_flags = overlay_flags & 0xfc; + tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); + if (removable_flags != 0) + tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, removable_flags, tile_x, tile_y); + ensure_district_instance (tile, BRIDGE_DISTRICT_ID, tile_x, tile_y); + Unit_set_state (worker, __, UnitState_Build_Mines); + worker->Body.Job_ID = WJ_Build_Mines; + return true; } } From 44ca94ea760cfe25a0c84909cc4e64f672a07e5f Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 23 Jan 2026 13:26:11 -0800 Subject: [PATCH 254/356] Ensure AI doesnt build roads and railroads not needed if they will be auto-added --- C3X.h | 4 ++-- Notes/district_todos.md | 2 +- default.districts_config.txt | 2 ++ injected_code.c | 8 +++++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/C3X.h b/C3X.h index 0d1f5fd3..a973e156 100644 --- a/C3X.h +++ b/C3X.h @@ -834,7 +834,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_CentralRailHub, .name = "Central Rail Hub", .tooltip = "Build Central Rail Hub", .display_name = "Central Rail Hub", .advance_prereq = "Steam Power", .resource_prereqs = {"Iron", "Coal"}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 2, .dependent_improvement_count = 0, .img_paths = {"CentralRailHub_AMER.pcx", "CentralRailHub_EURO.pcx", "CentralRailHub_ROMAN.pcx", "CentralRailHub_MIDEAST.pcx", "CentralRailHub_ASIAN.pcx"}, - .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, + .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .auto_add_road = true, .auto_add_railroad = true, .img_path_count = 5, .max_building_index = 1, .btn_tile_sheet_column = 5, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 @@ -852,7 +852,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_Bridge, .name = "Bridge", .tooltip = "Build Bridge", .display_name = "Bridge", .advance_prereq = "Industrialization", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"Bridge.pcx"}, .dependent_improvements = {0}, .custom_width = 176, .custom_height = 112, .y_offset = 24, .x_offset = 0, - .buildable_square_types_mask = (1 << SQ_Coast), + .buildable_square_types_mask = (1 << SQ_Coast), .auto_add_road = true, .img_path_count = 1, .max_building_index = 3, .btn_tile_sheet_column = 7, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 diff --git a/Notes/district_todos.md b/Notes/district_todos.md index d86b0494..089fc261 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,4 +1,3 @@ - - Auto add road/railrood - Upfront bridge/canal algorithm - AI great wall build strategy setting - Validate resource generation @@ -23,6 +22,7 @@ ## Maritime Districts - ~~Choose terrain type~~ - ~~Buttons~~ + - ~~Auto add road/railrood~~ - ~~Named tiles~~ - ~~Allow workers over water only if in radius of city that can build water district/wonder~~ - ~~Add ai_build_strategy option and AI worker handling~~ diff --git a/default.districts_config.txt b/default.districts_config.txt index 14c6c7b3..02ea739f 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -212,6 +212,7 @@ name = Neighborhood tooltip = Build Neighborhood buildable_on = desert,plains,grassland,tundra,floodplain,hills advance_prereq = +auto_add_road = 1 defense_bonus_percent = 25 allow_multiple = 1 culture_bonus = 1 @@ -291,6 +292,7 @@ advance_prereq = Steam Power resource_prereqs = Iron, Coal dependent_improvs = "Mass Transit System" buildable_on = desert,plains,grassland,tundra,floodplain,hills +auto_add_railroad = 1 defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 0 diff --git a/injected_code.c b/injected_code.c index f0f590ee..bccc9c91 100644 --- a/injected_code.c +++ b/injected_code.c @@ -29652,6 +29652,7 @@ ai_move_district_worker (Unit * worker, struct district_worker_record * rec) return false; } req->city = request_city; + struct district_config * cfg = &is->district_configs[district_id]; // If the worker has arrived if ((worker->Body.X == req->target_x) && (worker->Body.Y == req->target_y)) { @@ -29708,7 +29709,7 @@ ai_move_district_worker (Unit * worker, struct district_worker_record * rec) // Need to make sure distro hubs get roads. If this will be one, build the road first to be sure if (req->district_id == DISTRIBUTION_HUB_DISTRICT_ID) { bool has_road = (*tile->vtable->m25_Check_Roads)(tile, __, 0); - if (! has_road) { + if (! has_road && ! cfg->auto_add_road) { Unit_set_state(worker, __, UnitState_Build_Road); worker->Body.Job_ID = WJ_Build_Road; return true; @@ -30144,8 +30145,9 @@ patch_Unit_ai_move_terraformer (Unit * this) tile->vtable->m50_Get_Square_BaseType (tile) != SQ_Coast) { // Roads should be made after district builds. The district is complete but // worker is still likely on the tile, so check here and build road if needed + struct district_config * cfg = &is->district_configs[inst->district_type]; bool has_road = (*tile->vtable->m25_Check_Roads)(tile, __, 0); - if (! has_road) { + if (! has_road && ! cfg->auto_add_road) { Unit_set_state(this, __, UnitState_Build_Road); this->Body.Job_ID = WJ_Build_Road; return; @@ -30154,7 +30156,7 @@ patch_Unit_ai_move_terraformer (Unit * this) // Same check for railroads bool can_build_railroad = Leader_can_do_worker_job (&leaders[this->Body.CivID], __, WJ_Build_Railroad, this->Body.X, this->Body.Y, 0); bool has_railroad = (*tile->vtable->m23_Check_Railroads)(tile, __, 0); - if (can_build_railroad && !has_railroad) { + if (can_build_railroad && !has_railroad && ! cfg->auto_add_railroad) { Unit_set_state(this, __, UnitState_Build_Railroad); this->Body.Job_ID = WJ_Build_Railroad; return; From 79bb4e558c7e0234213e42858ed682416c656b80 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 24 Jan 2026 14:31:44 -0800 Subject: [PATCH 255/356] AI actually building reasonable canals and bridges - hallelujah --- Art/Districts/1200/Canal.PCX | Bin 93947 -> 72779 bytes C3X.h | 21 + Notes/district_todos.md | 1 + injected_code.c | 1966 ++++++++++++++++++++++++++++------ 4 files changed, 1631 insertions(+), 357 deletions(-) diff --git a/Art/Districts/1200/Canal.PCX b/Art/Districts/1200/Canal.PCX index 81424f4a1b11429ffbf674b8391bc1bea82c239e..2ba4760c6bba6040da4c9ed81974d1357f28b4c0 100644 GIT binary patch delta 159 pcmex;m-X}xmJM}`6DP1tUjHwJGRkc8gtB>3l$il_0?4TB9{_<cKiq?tYxaUZh>@C*OrA;O;bi3lC=n>5syw2YT2|UNOVe7 zMu#-1N*XfS-=xb_H4kVz-1uIrU>E#BisBU=1jSH3{2|SFV!i|^c-hHfd9}Gk6=kxgXiJvsCPV*YI{1u0@n%T=f4x9|(-NSxS z*;t4)FE!dkq;VC#9y@3gg$LIP{Ryi$dT^aRYSi-pUvY3ZpXNSYI_={ik8)5KKmBk$ zMa63$+C**qYXV<$wM}VJ;}E;~bUBCF#Xi{h04CTXc51(&uf?cl6^ruzN)b`flrCzU zUbRJHcDTR%HX^u_F{BOB5RV@E8x+qRS|~bC*u})5my}3MEY@wJXpLR$J6r+bvLj|< zv16@S6d!q0itAe};#{#^tXQ>2{#$!GP0{_Lv&16ig=5qzYL6`xT}2UU6YpP17oQxzrF0mg z?NGYbd7_A@Q)K9Nt!T|6g}Ox9S|HMPtbIhG9#Pj~)y@@PBRVf?PFsLP>n!mS^-Y4X zYJ=-~i7qCQq-&?!7b|o*3CE_HJFN;0CNZUJ+kQJFiJ1Uq)2cTGi9VI+wAY`1Mj`*C zn`qxkg+7~fQ+8>!LRTl3MP}ZY7Y->RVXAg)dv5xf$U+UipO{y z3%>e3^pF<&w}a;Sjhu(3r6{gx znN{4SW4>MDFZ(TMne)aF-kD=oZDU0mnSDl1eu_Oe%aN5btyC@>WbbG7xCN#0Ydde& z(T_cboIc;F-mnOj2*&cXp-o9Wd~MXj{_5XKT~_{L4sd4oq#lCxE^E|TKd@4h2-L>Xl1F+RGz)xa+V?_-Z1 z&_@40k1Sor{a9baRk~cS9!Sq1DBy_|GYYo)8OG|zWjSs3p#M#hsUp`&Y&BX z4x{4s22A({JXkN={pEr-LvB)UJzisR;=@?A-b+_(t&o6RP3YseRF6OL+Ng79(nZrL zxc!`m9Wrn~IO9V{18Q_8oie+Pha~rN;j)9z0EyhwK@ocMn8}4ivGU6sfb&A!;mtb(JF{Zw-nes^5CY9FFOL)GZ6>=3A1}l27UbKcIiij>IbT zaFwLNNr^;l$B(8+jISVAc&q*uev3PD1=R9cv@I_Z&VgDf0-N^HFtW-kwv@^wi4l05M)gM#XoIaFxrMX4`yI2Y0|cOfKl zK~?)jXitH?7e$#oqB5UV|5 zk5=BAOGOAhg_!MeTE)wyuAfJR3wm!8vtOnfC*3hf5OoS6+YnPyT4D;Qi|12*Jf+FA zd>(Qs4e5S$`+O=i$(*LJQqD?%RnL4{Jx_|J(h~K3bOEhJz$tV@4zk^eN{CPX=|q}S zC@BZzzl>Im1thC*o0bVIL(ss&5`W%Tkk&hemvKJ8x1`<)6FqJ3`Ns3*#Ax63Sgkfe zc<009gy=3;rwAdx@xZKs?VYd3)APFTbQe~cyY6}hoM~Nhpa1XvR~=S*P-MPmc`UB* z-4_1qJ1nAcziqj6@m)8*^pUaGc3;>3-}y<^rQP3h?I-P)^?cjKpET5b3s2soJvXDq Krftve82k^nx~gIT diff --git a/C3X.h b/C3X.h index a973e156..346e5343 100644 --- a/C3X.h +++ b/C3X.h @@ -385,6 +385,9 @@ struct c3x_config { bool expand_water_tile_checks_to_city_work_area; int max_contiguous_bridge_districts; int max_contiguous_canal_districts; + int min_canal_bisected_land_tiles; + int ai_bridge_canal_subset_size; + int ai_bridge_lake_tile_threshold; bool ai_defends_districts; int ai_city_district_max_build_wait_turns; @@ -1093,6 +1096,19 @@ struct pending_district_request { int worker_assigned_turn; }; +struct ai_candidate_bridge_or_canal_entry { + int district_id; + short owner_civ_id; + short * tile_x; + short * tile_y; + short tile_count; + short assigned_tile_index; + int assigned_worker_id; + bool completed; + struct pending_district_request pending_req; + int tile_capacity; +}; + struct district_worker_record { Unit * worker; int unit_id; @@ -1952,6 +1968,11 @@ struct district_button_image_set { // Natural Wonder labels: table mapping natural wonder name strings to their IDs, count of defined natural wonders, struct table natural_wonder_name_to_id; + struct ai_candidate_bridge_or_canal_entry * ai_candidate_bridge_or_canals; + int ai_candidate_bridge_or_canals_count; + int ai_candidate_bridge_or_canals_capacity; + bool ai_candidate_bridge_or_canals_initialized; + // City work radius highlighting: flag to enable/disable, table mapping tile pointers to highlight_level for visual feedback bool highlight_city_radii; struct table highlighted_city_radius_tile_pointers; diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 089fc261..e27cb0f4 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,6 +1,7 @@ - Upfront bridge/canal algorithm - AI great wall build strategy setting - Validate resource generation + - Fix scenario district art mod folder check - Firm up logic for river district rendering - Hoover Dam (use alt dir, special positioning b/c on river) diff --git a/injected_code.c b/injected_code.c index bccc9c91..115ad17b 100644 --- a/injected_code.c +++ b/injected_code.c @@ -210,6 +210,7 @@ bool __fastcall patch_Unit_can_perform_command (Unit * this, int edx, int unit_c bool __fastcall patch_Unit_can_pillage (Unit * this, int edx, int tile_x, int tile_y); bool __fastcall patch_City_has_resource (City * this, int edx, int resource_id); bool __fastcall patch_Leader_can_build_city_improvement (Leader * this, int edx, int i_improv, bool param_2); +bool can_build_district_on_tile (Tile * tile, int district_id, int civ_id); bool city_can_build_district (City * city, int district_id); bool leader_can_build_district (Leader * leader, int district_id); bool find_civ_trait_id_by_name (struct string_slice const * name, int * out_id); @@ -310,6 +311,7 @@ memoize (int val) is->memo[is->memo_len++] = val; } } + } void @@ -3190,6 +3192,21 @@ tile_index_to_coords (Map * map, int index, int * out_x, int * out_y) *out_x = *out_y = -1; } +int +tile_coords_to_index (Map * map, int x, int y) +{ + if ((map == NULL) || (x < 0) || (y < 0) || (x >= map->Width) || (y >= map->Height)) + return -1; + + int width = map->Width; + int row = y / 2; + if ((y & 1) == 0) { + return row * width + (x / 2); + } else { + return row * width + (width / 2) + (x / 2); + } +} + Tile * tile_at_index (Map * map, int i) { @@ -4108,6 +4125,8 @@ process_pending_district_request (Leader * leader, struct pending_district_reque assign_worker_to_district (req, worker, city, district_id, target_x, target_y); } +void assign_workers_for_ai_candidate_bridge_or_canals (Leader * leader); + void assign_workers_for_pending_districts (Leader * leader) { @@ -4144,6 +4163,174 @@ assign_workers_for_pending_districts (Leader * leader) process_pending_district_request (leader, req); } + if (is->current_config.enable_canal_districts || is->current_config.enable_bridge_districts) + assign_workers_for_ai_candidate_bridge_or_canals (leader); +} + +City * +find_city_for_tile (int civ_id, int tile_x, int tile_y) +{ + City * best_city = NULL; + int best_dist = INT_MAX; + + FOR_CITIES_OF (coi, civ_id) { + City * city = coi.city; + if (city == NULL) + continue; + + int dist = compute_wrapped_chebyshev_distance (city->Body.X, city->Body.Y, tile_x, tile_y); + if (dist < best_dist) { + best_dist = dist; + best_city = city; + } + } + + return best_city; +} + +bool +ai_candidate_bridge_or_canal_is_buildable_for_civ (struct ai_candidate_bridge_or_canal_entry * entry, int civ_id, int * out_tile_index) +{ + if ((entry == NULL) || (entry->tile_count <= 0)) + return false; + + int pending_index = -1; + for (int ti = 0; ti < entry->tile_count; ti++) { + int tx = entry->tile_x[ti]; + int ty = entry->tile_y[ti]; + wrap_tile_coords (&p_bic_data->Map, &tx, &ty); + Tile * tile = tile_at (tx, ty); + if ((tile == NULL) || (tile == p_null_tile)) + return false; + int owner = tile->vtable->m38_Get_Territory_OwnerID (tile); + if (owner != civ_id) + return false; + + struct district_instance * inst = get_district_instance (tile); + if (inst != NULL) { + if ((inst->district_type == entry->district_id) && + district_is_complete (tile, entry->district_id)) + continue; + if ((inst->district_type != entry->district_id) && + district_is_complete (tile, inst->district_type)) { + entry->completed = true; + return false; + } + if ((inst->district_type != entry->district_id) && + ! district_is_complete (tile, inst->district_type)) + return false; + } + + if (pending_index < 0) + pending_index = ti; + } + + if (pending_index < 0) { + entry->completed = true; + return false; + } + + if (out_tile_index != NULL) + *out_tile_index = pending_index; + + return true; +} + +void +release_ai_candidate_bridge_or_canal_worker (struct ai_candidate_bridge_or_canal_entry * entry) +{ + if ((entry == NULL) || (entry->assigned_worker_id < 0)) + return; + + int civ_id = entry->pending_req.civ_id; + if ((civ_id >= 0) && (civ_id < 32)) + clear_tracked_worker_assignment_by_id (civ_id, entry->assigned_worker_id); + + entry->assigned_worker_id = -1; + entry->assigned_tile_index = -1; + entry->pending_req.city = NULL; + entry->pending_req.city_id = -1; + entry->pending_req.civ_id = -1; + entry->pending_req.district_id = -1; + entry->pending_req.assigned_worker_id = -1; + entry->pending_req.target_x = -1; + entry->pending_req.target_y = -1; + entry->pending_req.worker_assigned_turn = 0; +} + +void +assign_workers_for_ai_candidate_bridge_or_canals (Leader * leader) +{ + if ((leader == NULL) || (is->ai_candidate_bridge_or_canals_count <= 0)) + return; + + int civ_id = leader->ID; + if ((civ_id < 0) || (civ_id >= 32)) + return; + if ((*p_human_player_bits & (1 << civ_id)) != 0) + return; + + for (int ei = 0; ei < is->ai_candidate_bridge_or_canals_count; ei++) { + struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[ei]; + if (entry->completed) + continue; + + int district_id = entry->district_id; + if ((district_id == CANAL_DISTRICT_ID) && (! is->current_config.enable_canal_districts)) + continue; + if ((district_id == BRIDGE_DISTRICT_ID) && (! is->current_config.enable_bridge_districts)) + continue; + if (! leader_can_build_district (leader, district_id)) + continue; + + if (entry->assigned_worker_id >= 0) { + Unit * worker = get_unit_ptr (entry->assigned_worker_id); + if (worker == NULL) { + release_ai_candidate_bridge_or_canal_worker (entry); + } else if ((entry->assigned_tile_index >= 0) && (entry->assigned_tile_index < entry->tile_count)) { + int tx = entry->tile_x[entry->assigned_tile_index]; + int ty = entry->tile_y[entry->assigned_tile_index]; + wrap_tile_coords (&p_bic_data->Map, &tx, &ty); + Tile * tile = tile_at (tx, ty); + if ((tile != NULL) && (tile != p_null_tile)) { + struct district_instance * inst = get_district_instance (tile); + if ((inst != NULL) && + (inst->district_type == district_id) && + district_is_complete (tile, district_id)) { + release_ai_candidate_bridge_or_canal_worker (entry); + } + } + } + if (entry->assigned_worker_id >= 0) + continue; + } + + int target_idx = -1; + if (! ai_candidate_bridge_or_canal_is_buildable_for_civ (entry, civ_id, &target_idx)) { + release_ai_candidate_bridge_or_canal_worker (entry); + continue; + } + + if (target_idx < 0) + continue; + + City * city = find_city_for_tile (civ_id, entry->tile_x[target_idx], entry->tile_y[target_idx]); + if (city == NULL) + continue; + + Unit * worker = find_best_worker_for_district (leader, city, district_id, entry->tile_x[target_idx], entry->tile_y[target_idx]); + if (worker == NULL) + continue; + + memset (&entry->pending_req, 0, sizeof entry->pending_req); + entry->pending_req.district_id = district_id; + entry->pending_req.civ_id = civ_id; + if (! assign_worker_to_district (&entry->pending_req, worker, city, district_id, entry->tile_x[target_idx], entry->tile_y[target_idx])) + continue; + + entry->assigned_worker_id = worker->Body.ID; + entry->assigned_tile_index = target_idx; + } } void @@ -5521,12 +5708,32 @@ reset_natural_wonder_configs (void) is->natural_wonder_count = 0; } +void +reset_ai_candidate_bridge_or_canals (void) +{ + if (is->ai_candidate_bridge_or_canals != NULL) { + for (int i = 0; i < is->ai_candidate_bridge_or_canals_capacity; i++) { + struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[i]; + if (entry->tile_x != NULL) + free (entry->tile_x); + if (entry->tile_y != NULL) + free (entry->tile_y); + } + free (is->ai_candidate_bridge_or_canals); + is->ai_candidate_bridge_or_canals = NULL; + } + is->ai_candidate_bridge_or_canals_capacity = 0; + is->ai_candidate_bridge_or_canals_count = 0; + is->ai_candidate_bridge_or_canals_initialized = false; +} + void clear_dynamic_district_definitions (void) { reset_regular_district_configs (); reset_wonder_district_configs (); reset_natural_wonder_configs (); + reset_ai_candidate_bridge_or_canals (); } void @@ -9822,129 +10029,1345 @@ tile_is_water (int tile_x, int tile_y) return (tile != NULL) && (tile != p_null_tile) && (tile->vtable->m35_Check_Is_Water (tile)); } -int -count_contiguous_bridge_districts (int tile_x, int tile_y, int dx, int dy) +bool +ensure_ai_candidate_bridge_or_canals_capacity (int required) +{ + if (required <= 0) + return true; + if (required <= is->ai_candidate_bridge_or_canals_capacity) + return true; + int new_capacity = (is->ai_candidate_bridge_or_canals_capacity > 0) ? is->ai_candidate_bridge_or_canals_capacity * 2 : 4; + if (new_capacity < required) + new_capacity = required; + struct ai_candidate_bridge_or_canal_entry * larger = (struct ai_candidate_bridge_or_canal_entry *)realloc ( + is->ai_candidate_bridge_or_canals, new_capacity * sizeof *larger); + if (larger == NULL) + return false; + for (int i = is->ai_candidate_bridge_or_canals_capacity; i < new_capacity; i++) { + struct ai_candidate_bridge_or_canal_entry * entry = &larger[i]; + entry->tile_x = NULL; + entry->tile_y = NULL; + entry->tile_capacity = 0; + entry->district_id = -1; + entry->owner_civ_id = -1; + entry->tile_count = 0; + entry->assigned_tile_index = -1; + entry->assigned_worker_id = -1; + entry->completed = false; + struct pending_district_request * req = &entry->pending_req; + req->city = NULL; + req->city_id = -1; + req->civ_id = -1; + req->district_id = -1; + req->assigned_worker_id = -1; + req->target_x = -1; + req->target_y = -1; + req->worker_assigned_turn = 0; + } + is->ai_candidate_bridge_or_canals = larger; + is->ai_candidate_bridge_or_canals_capacity = new_capacity; + return true; +} + +bool +canal_has_different_adjacent_seas (int tile_x, int tile_y, int civ_id) { + struct water_pair { + int dx1, dy1; + int dx2, dy2; + }; + + const struct water_pair pairs[] = { + { 1, -1, -1, 1 }, // NE + SW + { 1, 1, -1, -1 }, // SE + NW + }; + Map * map = &p_bic_data->Map; - int nx = tile_x + dx; - int ny = tile_y + dy; - int count = 0; - int max_steps = map->Width + map->Height; + bool require_owner = (civ_id >= 0); - for (int step = 0; step < max_steps; step++) { - wrap_tile_coords (map, &nx, &ny); - if (! tile_has_district_at (nx, ny, BRIDGE_DISTRICT_ID)) - break; - count++; - nx += dx; - ny += dy; + for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { + int ax = tile_x + pairs[i].dx1; + int ay = tile_y + pairs[i].dy1; + wrap_tile_coords (map, &ax, &ay); + Tile * first = tile_at (ax, ay); + if ((first == NULL) || (first == p_null_tile)) + continue; + if (! first->vtable->m35_Check_Is_Water (first)) + continue; + if (require_owner && (first->vtable->m38_Get_Territory_OwnerID (first) != civ_id)) + continue; + + int bx = tile_x + pairs[i].dx2; + int by = tile_y + pairs[i].dy2; + wrap_tile_coords (map, &bx, &by); + Tile * second = tile_at (bx, by); + if ((second == NULL) || (second == p_null_tile)) + continue; + if (! second->vtable->m35_Check_Is_Water (second)) + continue; + if (require_owner && (second->vtable->m38_Get_Territory_OwnerID (second) != civ_id)) + continue; + + int sea_a = first->vtable->m46_Get_ContinentID (first); + int sea_b = second->vtable->m46_Get_ContinentID (second); + if ((sea_a >= 0) && (sea_b >= 0) && (sea_a != sea_b)) + return true; } - return count; + return false; } -int -count_contiguous_canal_districts (int tile_x, int tile_y, int max_count) +// Check if two water tiles can reach each other via water within a radius, excluding a blocked tile +bool +water_tiles_connected_within_radius (int start_x, int start_y, int target_x, int target_y, int block_x, int block_y, int radius) { - if (max_count <= 0) - return 0; + // Simple BFS using a fixed-size visited array for tiles within radius + // workable_tile_counts[6] = 137 tiles for radius 6 + int max_tiles = 137; + int visited_x[137]; + int visited_y[137]; + int visited_count = 0; + int queue_x[137]; + int queue_y[137]; + int queue_head = 0; + int queue_tail = 0; - Map * map = &p_bic_data->Map; - int limit = max_count + 1; - int capacity = limit; - if (capacity < 1) - capacity = 1; + queue_x[queue_tail] = start_x; + queue_y[queue_tail] = start_y; + queue_tail++; + visited_x[visited_count] = start_x; + visited_y[visited_count] = start_y; + visited_count++; - int * xs = malloc (sizeof (*xs) * capacity); - int * ys = malloc (sizeof (*ys) * capacity); - if ((xs == NULL) || (ys == NULL)) { - if (xs != NULL) - free (xs); - if (ys != NULL) - free (ys); - return limit; - } + while (queue_head < queue_tail) { + int cx = queue_x[queue_head]; + int cy = queue_y[queue_head]; + queue_head++; - int const adj_dx[8] = { 0, 0, -2, 2, 1, 1, -1, -1 }; - int const adj_dy[8] = { -2, 2, 0, 0, -1, 1, -1, 1 }; - int head = 0; - int tail = 0; - int count = 0; + // Check 8 adjacent tiles + FOR_TILES_AROUND (tai, 9, cx, cy) { + if (tai.n == 0) + continue; + int nx = tai.tile_x; + int ny = tai.tile_y; - xs[tail] = tile_x; - ys[tail] = tile_y; - tail++; + // Found target + if (nx == target_x && ny == target_y) + return true; - while (head < tail) { - int cx = xs[head]; - int cy = ys[head]; - head++; - count++; - if (count >= limit) - break; + // Skip blocked tile (the isthmus) + if (nx == block_x && ny == block_y) + continue; - for (int i = 0; i < 8; i++) { - int nx = cx + adj_dx[i]; - int ny = cy + adj_dy[i]; - wrap_tile_coords (map, &nx, &ny); - if (! tile_has_district_at (nx, ny, CANAL_DISTRICT_ID)) + // Check if within radius of original start + int dx = nx - start_x; + int dy = ny - start_y; + if (dx < 0) dx = -dx; + if (dy < 0) dy = -dy; + // Use Chebyshev distance approximation for hex grid + int dist = (dx + dy + ((dx > dy) ? dx : dy)) / 2; + if (dist > radius) continue; - bool seen = false; - for (int j = 0; j < tail; j++) { - if ((xs[j] == nx) && (ys[j] == ny)) { - seen = true; + Tile * adj = tai.tile; + if ((adj == NULL) || (adj == p_null_tile)) + continue; + if (! adj->vtable->m35_Check_Is_Water (adj)) + continue; + + // Check if already visited + bool already_visited = false; + for (int i = 0; i < visited_count; i++) { + if (visited_x[i] == nx && visited_y[i] == ny) { + already_visited = true; break; } } - if (seen) + if (already_visited) continue; - if (tail < capacity) { - xs[tail] = nx; - ys[tail] = ny; - tail++; - } else { - count = limit; - head = tail; - break; + // Add to queue and visited + if (visited_count < max_tiles && queue_tail < max_tiles) { + visited_x[visited_count] = nx; + visited_y[visited_count] = ny; + visited_count++; + queue_x[queue_tail] = nx; + queue_y[queue_tail] = ny; + queue_tail++; + } + } + } + return false; +} + +// Check if tile separates adjacent water tiles that are not connected within a small radius +bool +canal_has_same_sea_isthmus (int tile_x, int tile_y, int civ_id, int check_radius) +{ + (void) civ_id; + (void) check_radius; + + // If another canal exists nearby, this isn't a unique isthmus target. + FOR_TILES_AROUND (tai, workable_tile_counts[2], tile_x, tile_y) { + if (tai.n == 0) + continue; + Tile * adj = tai.tile; + if ((adj == NULL) || (adj == p_null_tile)) + continue; + struct district_instance * adj_inst = get_district_instance (adj); + if ((adj_inst != NULL) && (adj_inst->district_type == CANAL_DISTRICT_ID)) + return false; + } + + // Check opposite diagonal water tiles that are not connected within radius 2 + struct water_pair { + int dx1, dy1; + int dx2, dy2; + }; + + const struct water_pair pairs[] = { + { 1, -1, -1, 1 }, // NE + SW + { 1, 1, -1, -1 }, // SE + NW + }; + + for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { + int ax = tile_x + pairs[i].dx1; + int ay = tile_y + pairs[i].dy1; + wrap_tile_coords (&p_bic_data->Map, &ax, &ay); + Tile * first = tile_at (ax, ay); + if ((first == NULL) || (first == p_null_tile)) + continue; + if (! first->vtable->m35_Check_Is_Water (first)) + continue; + + int bx = tile_x + pairs[i].dx2; + int by = tile_y + pairs[i].dy2; + wrap_tile_coords (&p_bic_data->Map, &bx, &by); + Tile * second = tile_at (bx, by); + if ((second == NULL) || (second == p_null_tile)) + continue; + if (! second->vtable->m35_Check_Is_Water (second)) + continue; + + if (! water_tiles_connected_within_radius ( + ax, ay, + bx, by, + tile_x, tile_y, 2)) { + return true; + } + } + return false; +} + +bool +bridge_tile_connects_two_continents (int tile_x, int tile_y, int civ_id) +{ + struct bridge_pair { + int dx1, dy1; + int dx2, dy2; + }; + + Map * map = &p_bic_data->Map; + const struct bridge_pair pairs[] = { + {0, -2, 0, 2}, + {-2, 0, 2, 0}, + {-1, -1, 1, 1}, + {-1, 1, 1, -1}, + }; + + bool require_owner = (civ_id >= 0); + + for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { + int ax = tile_x + pairs[i].dx1; + int ay = tile_y + pairs[i].dy1; + wrap_tile_coords (map, &ax, &ay); + if (! tile_is_land (civ_id, ax, ay, require_owner)) + continue; + Tile * first = tile_at (ax, ay); + if ((first == NULL) || (first == p_null_tile)) + continue; + + int bx = tile_x + pairs[i].dx2; + int by = tile_y + pairs[i].dy2; + wrap_tile_coords (map, &bx, &by); + if (! tile_is_land (civ_id, bx, by, require_owner)) + continue; + Tile * second = tile_at (bx, by); + if ((second == NULL) || (second == p_null_tile)) + continue; + + int cont_a = first->vtable->m46_Get_ContinentID (first); + int cont_b = second->vtable->m46_Get_ContinentID (second); + if ((cont_a >= 0) && (cont_b >= 0) && (cont_a != cont_b)) + return true; + } + + return false; +} + +enum { + AI_CANDIDATE_MAX_TILES = 5 // hard limit on how many tiles a candidate can contain +}; + +bool +tile_point_in_block (int tile_x, int tile_y, int block_x0, int block_y0, int block_x1, int block_y1) +{ + return (tile_x >= block_x0) && (tile_x < block_x1) && (tile_y >= block_y0) && (tile_y < block_y1); +} + +bool +tile_is_coastal_water (int tile_x, int tile_y) +{ + Map * map = &p_bic_data->Map; + wrap_tile_coords (map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + if ((tile == NULL) || (tile == p_null_tile)) + return false; + if (! tile->vtable->m35_Check_Is_Water (tile)) + return false; + enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); + return base_type == SQ_Coast; +} + +bool +tile_exists_at (int tile_x, int tile_y) +{ + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + return (tile != NULL) && (tile != p_null_tile); +} + +bool +tile_is_reserved_in_district_tile_map (int tile_x, int tile_y) +{ + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + if ((tile == NULL) || (tile == p_null_tile)) + return false; + int existing = 0; + return itable_look_up (&is->district_tile_map, (int)tile, &existing); +} + +bool +tile_has_diagonal_water (int tile_x, int tile_y) +{ + Map * map = &p_bic_data->Map; + int const adj_dx[4] = { 1, 1, -1, -1 }; + int const adj_dy[4] = { -1, 1, -1, 1 }; + + for (int i = 0; i < 4; i++) { + int nx = tile_x + adj_dx[i]; + int ny = tile_y + adj_dy[i]; + wrap_tile_coords (map, &nx, &ny); + Tile * tile = tile_at (nx, ny); + if ((tile == NULL) || (tile == p_null_tile)) + continue; + if (tile->vtable->m35_Check_Is_Water (tile)) + return true; + } + return false; +} + +bool +bridge_tile_has_land_on_both_sides (int tile_x, int tile_y) +{ + struct bridge_pair { + int dx1, dy1; + int dx2, dy2; + }; + + const struct bridge_pair pairs[] = { + { 0, -2, 0, 2 }, + { -2, 0, 2, 0 }, + { -1, -1, 1, 1 }, + { -1, 1, 1, -1 }, + }; + + for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { + int ax = tile_x + pairs[i].dx1; + int ay = tile_y + pairs[i].dy1; + if (! tile_exists_at (ax, ay)) + continue; + if (! tile_is_land (-1, ax, ay, false)) + continue; + int bx = tile_x + pairs[i].dx2; + int by = tile_y + pairs[i].dy2; + if (! tile_exists_at (bx, by)) + continue; + if (! tile_is_land (-1, bx, by, false)) + continue; + return true; + } + + return false; +} + +bool +tile_part_of_existing_candidate (int tile_x, int tile_y) +{ + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + for (int ei = 0; ei < is->ai_candidate_bridge_or_canals_count; ei++) { + struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[ei]; + if (entry->tile_count <= 0) + continue; + for (int ti = 0; ti < entry->tile_count; ti++) { + if ((entry->tile_x[ti] == tile_x) && (entry->tile_y[ti] == tile_y)) + return true; + } + } + return false; +} + +bool +add_ai_candidate_entry (int district_id, short owner_civ_id, short * xs, short * ys, int count) +{ + if (count <= 0) + return false; + if (! ensure_ai_candidate_bridge_or_canals_capacity (is->ai_candidate_bridge_or_canals_count + 1)) + return false; + + struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[is->ai_candidate_bridge_or_canals_count]; + entry->tile_x = (short *)malloc (sizeof *entry->tile_x * count); + entry->tile_y = (short *)malloc (sizeof *entry->tile_y * count); + if ((entry->tile_x == NULL) || (entry->tile_y == NULL)) { + if (entry->tile_x != NULL) + free (entry->tile_x); + if (entry->tile_y != NULL) + free (entry->tile_y); + return false; + } + + entry->district_id = district_id; + entry->owner_civ_id = owner_civ_id; + entry->tile_count = (short)count; + entry->tile_capacity = count; + entry->assigned_tile_index = -1; + entry->assigned_worker_id = -1; + entry->completed = false; + for (int ti = 0; ti < count; ti++) { + entry->tile_x[ti] = xs[ti]; + entry->tile_y[ti] = ys[ti]; + } + + struct pending_district_request * req = &entry->pending_req; + req->city = NULL; + req->city_id = -1; + req->civ_id = owner_civ_id; + req->district_id = district_id; + req->assigned_worker_id = -1; + req->target_x = -1; + req->target_y = -1; + req->worker_assigned_turn = 0; + + is->ai_candidate_bridge_or_canals_count++; + return true; +} + +int +gather_bridge_line (int start_x, int start_y, int dx, int dy, int limit, + int block_x0, int block_y0, int block_x1, int block_y1, + short * out_x, short * out_y) +{ + int effective_limit = limit; + if (effective_limit <= 0) + effective_limit = 1; + if (effective_limit > AI_CANDIDATE_MAX_TILES) + effective_limit = AI_CANDIDATE_MAX_TILES; + + if (! tile_is_coastal_water (start_x, start_y)) + return 0; + if (! bridge_tile_has_land_on_both_sides (start_x, start_y)) + return 0; + + short back_x[AI_CANDIDATE_MAX_TILES]; + short back_y[AI_CANDIDATE_MAX_TILES]; + short forward_x[AI_CANDIDATE_MAX_TILES]; + short forward_y[AI_CANDIDATE_MAX_TILES]; + int back_count = 0; + int forward_count = 0; + int remaining = effective_limit - 1; + Map * map = &p_bic_data->Map; + + int cx = start_x; + int cy = start_y; + while ((remaining > 0) && (back_count < effective_limit)) { + int nx = cx - dx; + int ny = cy - dy; + wrap_tile_coords (map, &nx, &ny); + if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) + break; + if (! tile_is_coastal_water (nx, ny)) + break; + if (! bridge_tile_has_land_on_both_sides (nx, ny)) + break; + if (tile_part_of_existing_candidate (nx, ny)) + break; + back_x[back_count] = (short)nx; + back_y[back_count] = (short)ny; + back_count++; + remaining--; + cx = nx; + cy = ny; + } + + cx = start_x; + cy = start_y; + while ((remaining > 0) && (forward_count < effective_limit)) { + int nx = cx + dx; + int ny = cy + dy; + wrap_tile_coords (map, &nx, &ny); + if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) + break; + if (! tile_is_coastal_water (nx, ny)) + break; + if (! bridge_tile_has_land_on_both_sides (nx, ny)) + break; + if (tile_part_of_existing_candidate (nx, ny)) + break; + forward_x[forward_count] = (short)nx; + forward_y[forward_count] = (short)ny; + forward_count++; + remaining--; + cx = nx; + cy = ny; + } + + int write = 0; + for (int bi = back_count - 1; bi >= 0; bi--) { + out_x[write] = back_x[bi]; + out_y[write] = back_y[bi]; + write++; + } + out_x[write] = (short)start_x; + out_y[write] = (short)start_y; + write++; + for (int fi = 0; fi < forward_count; fi++) { + out_x[write] = forward_x[fi]; + out_y[write] = forward_y[fi]; + write++; + } + + return write; +} + +bool +bridge_line_connects_two_continents (short * xs, short * ys, int count) +{ + for (int i = 0; i < count; i++) { + if (bridge_tile_connects_two_continents (xs[i], ys[i], -1)) + return true; + } + return false; +} + +int +find_bridge_candidate_in_block (Map * map, int block_x0, int block_y0, int block_x1, int block_y1, + int contiguous_limit, int candidate_capacity, + short * out_x, short * out_y, short * out_owner) +{ + const int dirs[4][2] = { + { 0, -2 }, { -2, 0 }, { -1, -1 }, { -1, 1 } + }; + + int max_len = contiguous_limit; + if (max_len <= 0) + max_len = 1; + if (max_len > AI_CANDIDATE_MAX_TILES) + max_len = AI_CANDIDATE_MAX_TILES; + if (candidate_capacity > 0 && max_len > candidate_capacity) + max_len = candidate_capacity; + + for (int length = 1; length <= max_len; length++) { + for (int ti = 0; ti < map->TileCount; ti++) { + int tx = -1; + int ty = -1; + tile_index_to_coords (map, ti, &tx, &ty); + if (! tile_point_in_block (tx, ty, block_x0, block_y0, block_x1, block_y1)) + continue; + Tile * tile = tile_at (tx, ty); + if ((tile == NULL) || (tile == p_null_tile)) + continue; + if (tile_has_resource (tile)) + continue; + if (! district_is_buildable_on_square_type (&is->district_configs[BRIDGE_DISTRICT_ID], tile)) + continue; + if (tile_is_reserved_in_district_tile_map (tx, ty)) + continue; + short owner = tile->vtable->m38_Get_Territory_OwnerID (tile); + + for (int di = 0; di < (int)(sizeof (dirs) / sizeof (dirs[0])); di++) { + int dx = dirs[di][0]; + int dy = dirs[di][1]; + int end_x = tx + dx * (length - 1); + int end_y = ty + dy * (length - 1); + wrap_tile_coords (map, &end_x, &end_y); + if (! tile_point_in_block (end_x, end_y, block_x0, block_y0, block_x1, block_y1)) + continue; + + bool ok = true; + for (int step = 0; step < length; step++) { + int cx = tx + dx * step; + int cy = ty + dy * step; + wrap_tile_coords (map, &cx, &cy); + if (! tile_point_in_block (cx, cy, block_x0, block_y0, block_x1, block_y1)) { + ok = false; + break; + } + if (! tile_exists_at (cx, cy)) { + ok = false; + break; + } + if (! tile_is_coastal_water (cx, cy)) { + ok = false; + break; + } + Tile * bridge_tile = tile_at (cx, cy); + if ((bridge_tile == NULL) || (bridge_tile == p_null_tile)) { + ok = false; + break; + } + if (tile_has_resource (bridge_tile)) { + ok = false; + break; + } + if (! district_is_buildable_on_square_type (&is->district_configs[BRIDGE_DISTRICT_ID], bridge_tile)) { + ok = false; + break; + } + if (tile_is_reserved_in_district_tile_map (cx, cy)) { + ok = false; + break; + } + if (tile_part_of_existing_candidate (cx, cy)) { + ok = false; + break; + } + } + if (! ok) + continue; + + int land_ax = tx - dx; + int land_ay = ty - dy; + wrap_tile_coords (map, &land_ax, &land_ay); + if (! tile_point_in_block (land_ax, land_ay, block_x0, block_y0, block_x1, block_y1)) + continue; + if (! tile_exists_at (land_ax, land_ay)) + continue; + if (! tile_is_land (-1, land_ax, land_ay, false)) + continue; + Tile * land_a = tile_at (land_ax, land_ay); + if ((land_a == NULL) || (land_a == p_null_tile)) + continue; + + int land_bx = tx + dx * length; + int land_by = ty + dy * length; + wrap_tile_coords (map, &land_bx, &land_by); + if (! tile_point_in_block (land_bx, land_by, block_x0, block_y0, block_x1, block_y1)) + continue; + if (! tile_exists_at (land_bx, land_by)) + continue; + if (! tile_is_land (-1, land_bx, land_by, false)) + continue; + Tile * land_b = tile_at (land_bx, land_by); + if ((land_b == NULL) || (land_b == p_null_tile)) + continue; + + int cont_a = land_a->vtable->m46_Get_ContinentID (land_a); + int cont_b = land_b->vtable->m46_Get_ContinentID (land_b); + if ((cont_a < 0) || (cont_b < 0) || (cont_a == cont_b)) + continue; + + for (int step = 0; step < length; step++) { + int cx = tx + dx * step; + int cy = ty + dy * step; + wrap_tile_coords (map, &cx, &cy); + out_x[step] = (short)cx; + out_y[step] = (short)cy; + } + *out_owner = owner; + return length; + } + } + } + return 0; +} + +int +gather_canal_line (int start_x, int start_y, int dx, int dy, int limit, + int block_x0, int block_y0, int block_x1, int block_y1, + short * out_x, short * out_y) +{ + int effective_limit = limit; + if (effective_limit <= 0) + effective_limit = 1; + if (effective_limit > AI_CANDIDATE_MAX_TILES) + effective_limit = AI_CANDIDATE_MAX_TILES; + + if (! tile_is_land (-1, start_x, start_y, false)) + return 0; + if (tile_part_of_existing_candidate (start_x, start_y)) + return 0; + + short back_x[AI_CANDIDATE_MAX_TILES]; + short back_y[AI_CANDIDATE_MAX_TILES]; + short forward_x[AI_CANDIDATE_MAX_TILES]; + short forward_y[AI_CANDIDATE_MAX_TILES]; + int back_count = 0; + int forward_count = 0; + int remaining = effective_limit - 1; + Map * map = &p_bic_data->Map; + + int cx = start_x; + int cy = start_y; + while ((remaining > 0) && (back_count < effective_limit)) { + int nx = cx - dx; + int ny = cy - dy; + wrap_tile_coords (map, &nx, &ny); + if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) + break; + if (! tile_is_land (-1, nx, ny, false)) + break; + if (tile_part_of_existing_candidate (nx, ny)) + break; + back_x[back_count] = (short)nx; + back_y[back_count] = (short)ny; + back_count++; + remaining--; + cx = nx; + cy = ny; + } + + cx = start_x; + cy = start_y; + while ((remaining > 0) && (forward_count < effective_limit)) { + int nx = cx + dx; + int ny = cy + dy; + wrap_tile_coords (map, &nx, &ny); + if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) + break; + if (! tile_is_land (-1, nx, ny, false)) + break; + if (tile_part_of_existing_candidate (nx, ny)) + break; + forward_x[forward_count] = (short)nx; + forward_y[forward_count] = (short)ny; + forward_count++; + remaining--; + cx = nx; + cy = ny; + } + + int write = 0; + for (int bi = back_count - 1; bi >= 0; bi--) { + out_x[write] = back_x[bi]; + out_y[write] = back_y[bi]; + write++; + } + out_x[write] = (short)start_x; + out_y[write] = (short)start_y; + write++; + for (int fi = 0; fi < forward_count; fi++) { + out_x[write] = forward_x[fi]; + out_y[write] = forward_y[fi]; + write++; + } + + return write; +} + +bool +cluster_connects_two_seas_or_isthmus (short * xs, short * ys, int count) +{ + for (int i = 0; i < count; i++) { + if (canal_has_different_adjacent_seas (xs[i], ys[i], -1)) + return true; + if (canal_has_same_sea_isthmus (xs[i], ys[i], -1, 2)) + return true; + } + return false; +} + +int +find_canal_candidate_in_block (Map * map, int block_x0, int block_y0, int block_x1, int block_y1, + int contiguous_limit, int candidate_capacity, + short * out_x, short * out_y, short * out_owner) +{ + const int dir_dx[8] = { 0, 1, 2, 1, 0, -1, -2, -1 }; + const int dir_dy[8] = { -2, -1, 0, 1, 2, 1, 0, -1 }; + + int max_len = contiguous_limit; + if (max_len <= 0) + max_len = 1; + if (max_len > AI_CANDIDATE_MAX_TILES) + max_len = AI_CANDIDATE_MAX_TILES; + if (candidate_capacity > 0 && max_len > candidate_capacity) + max_len = candidate_capacity; + + int tile_count = map->TileCount; + int * visit = (int *)malloc (sizeof (*visit) * tile_count); + int * queue_x = (int *)malloc (sizeof (*queue_x) * tile_count); + int * queue_y = (int *)malloc (sizeof (*queue_y) * tile_count); + if ((visit == NULL) || (queue_x == NULL) || (queue_y == NULL)) { + if (visit != NULL) + free (visit); + if (queue_x != NULL) + free (queue_x); + if (queue_y != NULL) + free (queue_y); + return 0; + } + for (int i = 0; i < tile_count; i++) + visit[i] = 0; + + int visit_mark = 1; + + for (int length = 1; length <= max_len; length++) { + for (int ti = 0; ti < map->TileCount; ti++) { + int start_x = -1; + int start_y = -1; + tile_index_to_coords (map, ti, &start_x, &start_y); + if (! tile_point_in_block (start_x, start_y, block_x0, block_y0, block_x1, block_y1)) + continue; + if (! tile_is_land (-1, start_x, start_y, false)) + continue; + if (tile_part_of_existing_candidate (start_x, start_y)) + continue; + if (tile_is_reserved_in_district_tile_map (start_x, start_y)) + continue; + Tile * start_tile = tile_at (start_x, start_y); + if ((start_tile == NULL) || (start_tile == p_null_tile)) + continue; + if (tile_has_resource (start_tile)) + continue; + if (! district_is_buildable_on_square_type (&is->district_configs[CANAL_DISTRICT_ID], start_tile)) + continue; + int continent_id = start_tile->vtable->m46_Get_ContinentID (start_tile); + if (continent_id < 0) + continue; + short owner = start_tile->vtable->m38_Get_Territory_OwnerID (start_tile); + + int stack_len = 1; + int dir_stack[AI_CANDIDATE_MAX_TILES]; + int path_dir[AI_CANDIDATE_MAX_TILES]; + out_x[0] = (short)start_x; + out_y[0] = (short)start_y; + dir_stack[0] = -1; + path_dir[0] = -1; + + while (stack_len > 0) { + int depth = stack_len - 1; + int cx = out_x[depth]; + int cy = out_y[depth]; + + if (depth + 1 == length) { + bool endpoints_ok = false; + if (length == 1) { + int ex = out_x[0]; + int ey = out_y[0]; + bool pair_ok = false; + if (tile_is_water (ex, ey - 2) && tile_is_water (ex, ey + 2)) pair_ok = true; + if (tile_is_water (ex - 2, ey) && tile_is_water (ex + 2, ey)) pair_ok = true; + if (tile_is_water (ex - 1, ey - 1) && tile_is_water (ex + 1, ey + 1)) pair_ok = true; + if (tile_is_water (ex - 1, ey + 1) && tile_is_water (ex + 1, ey - 1)) pair_ok = true; + if (pair_ok && tile_has_diagonal_water (ex, ey)) + endpoints_ok = true; + } else { + int first_dir = path_dir[1]; + int last_dir = path_dir[length - 1]; + int sx = out_x[0]; + int sy = out_y[0]; + int ex = out_x[length - 1]; + int ey = out_y[length - 1]; + bool start_water = tile_is_water (sx - dir_dx[first_dir], sy - dir_dy[first_dir]); + bool end_water = tile_is_water (ex + dir_dx[last_dir], ey + dir_dy[last_dir]); + if (start_water && end_water && + tile_has_diagonal_water (sx, sy) && + tile_has_diagonal_water (ex, ey)) + endpoints_ok = true; + } + + bool buildable = true; + if (endpoints_ok) { + for (int pi = 0; pi < length; pi++) { + Tile * path_tile = tile_at (out_x[pi], out_y[pi]); + if ((path_tile == NULL) || (path_tile == p_null_tile) || + tile_has_resource (path_tile) || + (! district_is_buildable_on_square_type (&is->district_configs[CANAL_DISTRICT_ID], path_tile)) || + tile_is_reserved_in_district_tile_map (out_x[pi], out_y[pi])) { + buildable = false; + break; + } + } + } else { + buildable = false; + } + + if (buildable) { + // Collect adjacent land tiles for component checks + int adj_x[AI_CANDIDATE_MAX_TILES * 8]; + int adj_y[AI_CANDIDATE_MAX_TILES * 8]; + int adj_count = 0; + for (int pi = 0; pi < length; pi++) { + int px = out_x[pi]; + int py = out_y[pi]; + for (int di = 0; di < 8; di++) { + int nx = px + dir_dx[di]; + int ny = py + dir_dy[di]; + wrap_tile_coords (map, &nx, &ny); + if (! tile_is_land (-1, nx, ny, false)) + continue; + Tile * adj = tile_at (nx, ny); + if ((adj == NULL) || (adj == p_null_tile)) + continue; + if (adj->vtable->m46_Get_ContinentID (adj) != continent_id) + continue; + bool in_path = false; + for (int pj = 0; pj < length; pj++) { + if ((out_x[pj] == nx) && (out_y[pj] == ny)) { + in_path = true; + break; + } + } + if (in_path) + continue; + bool seen = false; + for (int aj = 0; aj < adj_count; aj++) { + if ((adj_x[aj] == nx) && (adj_y[aj] == ny)) { + seen = true; + break; + } + } + if (! seen && (adj_count < (int)(sizeof (adj_x) / sizeof (adj_x[0])))) { + adj_x[adj_count] = nx; + adj_y[adj_count] = ny; + adj_count++; + } + } + } + + if (adj_count >= 2) { + int best1 = 0; + int best2 = 0; + int min_land = is->current_config.min_canal_bisected_land_tiles; + if (min_land < 1) + min_land = 1; + visit_mark++; + if (visit_mark == 0) { + visit_mark = 1; + for (int i = 0; i < tile_count; i++) + visit[i] = 0; + } + + for (int ai = 0; ai < adj_count; ai++) { + int aidx = tile_coords_to_index (map, adj_x[ai], adj_y[ai]); + if ((aidx < 0) || (visit[aidx] == visit_mark)) + continue; + + int comp_size = 0; + int head = 0; + int tail = 0; + visit[aidx] = visit_mark; + queue_x[tail] = adj_x[ai]; + queue_y[tail] = adj_y[ai]; + tail++; + + while (head < tail) { + int qx = queue_x[head]; + int qy = queue_y[head]; + head++; + comp_size++; + for (int di = 0; di < 8; di++) { + int nx = qx + dir_dx[di]; + int ny = qy + dir_dy[di]; + wrap_tile_coords (map, &nx, &ny); + if (! tile_is_land (-1, nx, ny, false)) + continue; + Tile * adj = tile_at (nx, ny); + if ((adj == NULL) || (adj == p_null_tile)) + continue; + if (adj->vtable->m46_Get_ContinentID (adj) != continent_id) + continue; + bool blocked = false; + for (int pj = 0; pj < length; pj++) { + if ((out_x[pj] == nx) && (out_y[pj] == ny)) { + blocked = true; + break; + } + } + if (blocked) + continue; + int nidx = tile_coords_to_index (map, nx, ny); + if ((nidx < 0) || (visit[nidx] == visit_mark)) + continue; + visit[nidx] = visit_mark; + queue_x[tail] = nx; + queue_y[tail] = ny; + tail++; + } + } + + if (comp_size > best1) { + best2 = best1; + best1 = comp_size; + } else if (comp_size > best2) { + best2 = comp_size; + } + } + + if ((best1 >= min_land) && (best2 >= min_land)) { + *out_owner = owner; + free (visit); + free (queue_x); + free (queue_y); + return length; + } + } + } + dir_stack[depth] = -1; + stack_len--; + continue; + } + + int next_dir = dir_stack[depth] + 1; + bool advanced = false; + while (next_dir < 8) { + int ndx = dir_dx[next_dir]; + int ndy = dir_dy[next_dir]; + int nx = cx + ndx; + int ny = cy + ndy; + wrap_tile_coords (map, &nx, &ny); + dir_stack[depth] = next_dir; + + if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) { + next_dir++; + continue; + } + if (! tile_is_land (-1, nx, ny, false)) { + next_dir++; + continue; + } + if (tile_part_of_existing_candidate (nx, ny)) { + next_dir++; + continue; + } + if (tile_is_reserved_in_district_tile_map (nx, ny)) { + next_dir++; + continue; + } + Tile * next_tile = tile_at (nx, ny); + if ((next_tile == NULL) || (next_tile == p_null_tile)) { + next_dir++; + continue; + } + if (tile_has_resource (next_tile)) { + next_dir++; + continue; + } + if (! district_is_buildable_on_square_type (&is->district_configs[CANAL_DISTRICT_ID], next_tile)) { + next_dir++; + continue; + } + if (next_tile->vtable->m46_Get_ContinentID (next_tile) != continent_id) { + next_dir++; + continue; + } + bool dup = false; + for (int pi = 0; pi < depth + 1; pi++) { + if ((out_x[pi] == nx) && (out_y[pi] == ny)) { + dup = true; + break; + } + } + if (dup) { + next_dir++; + continue; + } + + if (depth >= 1) { + int prev_dir = dir_stack[depth - 1]; + int diff = next_dir - prev_dir; + if (diff < 0) + diff += 8; + if (diff > 4) + diff = 8 - diff; + if (diff > 1) { + next_dir++; + continue; + } + } + + out_x[depth + 1] = (short)nx; + out_y[depth + 1] = (short)ny; + dir_stack[depth + 1] = -1; + path_dir[depth + 1] = next_dir; + stack_len++; + advanced = true; + break; + } + + if (! advanced) { + dir_stack[depth] = -1; + stack_len--; + } + } + } + } + + free (visit); + free (queue_x); + free (queue_y); + return 0; +} + +void +plan_bridge_candidates_in_subsets (Map * map, int subset_size, int contiguous_limit) +{ + if ((map == NULL) || (subset_size <= 0)) + return; + + int width = map->Width; + int height = map->Height; + if ((width <= 0) || (height <= 0)) + return; + + int block_size = subset_size; + if (block_size < 1) + block_size = 1; + + int candidate_capacity = contiguous_limit; + if (candidate_capacity <= 0) + candidate_capacity = 1; + if (candidate_capacity > AI_CANDIDATE_MAX_TILES) + candidate_capacity = AI_CANDIDATE_MAX_TILES; + + short * candidate_x = (short *)malloc (sizeof *candidate_x * candidate_capacity); + short * candidate_y = (short *)malloc (sizeof *candidate_y * candidate_capacity); + if ((candidate_x == NULL) || (candidate_y == NULL)) { + if (candidate_x != NULL) + free (candidate_x); + if (candidate_y != NULL) + free (candidate_y); + return; + } + + for (int base_y = 0; base_y < height; base_y += block_size) { + int block_y1 = base_y + block_size; + if (block_y1 > height) + block_y1 = height; + for (int base_x = 0; base_x < width; base_x += block_size) { + int block_x1 = base_x + block_size; + if (block_x1 > width) + block_x1 = width; + short owner = -1; + int count = find_bridge_candidate_in_block ( + map, base_x, base_y, block_x1, block_y1, + contiguous_limit, candidate_capacity, + candidate_x, candidate_y, &owner); + if (count <= 0) + continue; + if (! add_ai_candidate_entry (BRIDGE_DISTRICT_ID, owner, candidate_x, candidate_y, count)) { + free (candidate_x); + free (candidate_y); + return; } } } - free (xs); - free (ys); + free (candidate_x); + free (candidate_y); +} + +void +plan_canal_candidates_in_subsets (Map * map, int subset_size, int contiguous_limit) +{ + if ((map == NULL) || (subset_size <= 0)) + return; + + int width = map->Width; + int height = map->Height; + if ((width <= 0) || (height <= 0)) + return; + + int block_size = subset_size; + if (block_size < 1) + block_size = 1; + + int candidate_capacity = contiguous_limit; + if (candidate_capacity <= 0) + candidate_capacity = 1; + if (candidate_capacity > AI_CANDIDATE_MAX_TILES) + candidate_capacity = AI_CANDIDATE_MAX_TILES; + + short * candidate_x = (short *)malloc (sizeof *candidate_x * candidate_capacity); + short * candidate_y = (short *)malloc (sizeof *candidate_y * candidate_capacity); + if ((candidate_x == NULL) || (candidate_y == NULL)) { + if (candidate_x != NULL) + free (candidate_x); + if (candidate_y != NULL) + free (candidate_y); + return; + } + + for (int base_y = 0; base_y < height; base_y += block_size) { + int block_y1 = base_y + block_size; + if (block_y1 > height) + block_y1 = height; + for (int base_x = 0; base_x < width; base_x += block_size) { + int block_x1 = base_x + block_size; + if (block_x1 > width) + block_x1 = width; + short owner = -1; + int count = find_canal_candidate_in_block ( + map, base_x, base_y, block_x1, block_y1, + contiguous_limit, candidate_capacity, + candidate_x, candidate_y, &owner); + if (count <= 0) + continue; + if (! add_ai_candidate_entry (CANAL_DISTRICT_ID, owner, candidate_x, candidate_y, count)) { + free (candidate_x); + free (candidate_y); + return; + } + } + } + + free (candidate_x); + free (candidate_y); +} + +void +plan_canal_and_bridge_targets (void) +{ + if (is->ai_candidate_bridge_or_canals_initialized) + return; + if ((! is->current_config.enable_canal_districts) && (! is->current_config.enable_bridge_districts)) + return; + + Map * map = &p_bic_data->Map; + int width = map->Width; + int height = map->Height; + if ((width <= 0) || (height <= 0)) + return; + + int subset_size = is->current_config.ai_bridge_canal_subset_size; + if (subset_size <= 0) + subset_size = 10; + + if (is->current_config.enable_canal_districts) { + plan_canal_candidates_in_subsets ( + map, + subset_size, + is->current_config.max_contiguous_canal_districts); + } + + if (is->current_config.enable_bridge_districts) { + plan_bridge_candidates_in_subsets ( + map, + subset_size, + is->current_config.max_contiguous_bridge_districts); + } + + is->ai_candidate_bridge_or_canals_initialized = true; +} +int +count_contiguous_bridge_districts (int tile_x, int tile_y, int dx, int dy) +{ + Map * map = &p_bic_data->Map; + int nx = tile_x + dx; + int ny = tile_y + dy; + int count = 0; + int max_steps = map->Width + map->Height; + + for (int step = 0; step < max_steps; step++) { + wrap_tile_coords (map, &nx, &ny); + if (! tile_has_district_at (nx, ny, BRIDGE_DISTRICT_ID)) + break; + count++; + nx += dx; + ny += dy; + } return count; } -bool -bridge_district_tile_is_valid (int tile_x, int tile_y) +int +count_contiguous_canal_districts (int tile_x, int tile_y, int max_count) { - if (is->current_config.max_contiguous_bridge_districts > 0) { - int max_bridges = is->current_config.max_contiguous_bridge_districts; + if (max_count <= 0) + return 0; - int ns_count = count_contiguous_bridge_districts (tile_x, tile_y, 0, -2) + - count_contiguous_bridge_districts (tile_x, tile_y, 0, 2); - if (ns_count > max_bridges) - return false; + Map * map = &p_bic_data->Map; + int limit = max_count + 1; + int capacity = limit; + if (capacity < 1) + capacity = 1; - int we_count = count_contiguous_bridge_districts (tile_x, tile_y, -2, 0) + - count_contiguous_bridge_districts (tile_x, tile_y, 2, 0); - if (we_count > max_bridges) - return false; + int * xs = malloc (sizeof (*xs) * capacity); + int * ys = malloc (sizeof (*ys) * capacity); + if ((xs == NULL) || (ys == NULL)) { + if (xs != NULL) + free (xs); + if (ys != NULL) + free (ys); + return limit; + } - int swne_count = count_contiguous_bridge_districts (tile_x, tile_y, -1, 1) + - count_contiguous_bridge_districts (tile_x, tile_y, 1, -1); - if (swne_count > max_bridges) - return false; + int const adj_dx[8] = { 0, 0, -2, 2, 1, 1, -1, -1 }; + int const adj_dy[8] = { -2, 2, 0, 0, -1, 1, -1, 1 }; + int head = 0; + int tail = 0; + int count = 0; - int nwse_count = count_contiguous_bridge_districts (tile_x, tile_y, -1, -1) + - count_contiguous_bridge_districts (tile_x, tile_y, 1, 1); - if (nwse_count > max_bridges) - return false; + xs[tail] = tile_x; + ys[tail] = tile_y; + tail++; + + while (head < tail) { + int cx = xs[head]; + int cy = ys[head]; + head++; + count++; + if (count >= limit) + break; + + for (int i = 0; i < 8; i++) { + int nx = cx + adj_dx[i]; + int ny = cy + adj_dy[i]; + wrap_tile_coords (map, &nx, &ny); + if (! tile_has_district_at (nx, ny, CANAL_DISTRICT_ID)) + continue; + + bool seen = false; + for (int j = 0; j < tail; j++) { + if ((xs[j] == nx) && (ys[j] == ny)) { + seen = true; + break; + } + } + if (seen) + continue; + + if (tail < capacity) { + xs[tail] = nx; + ys[tail] = ny; + tail++; + } else { + count = limit; + head = tail; + break; + } + } } + free (xs); + free (ys); + + return count; +} + +bool +district_line_is_straight (int tile_x, int tile_y, int district_id) +{ Map * map = &p_bic_data->Map; int const adj_dx[8] = { 0, 0, -2, 2, -1, 1, -1, 1 }; int const adj_dy[8] = { -2, 2, 0, 0, 1, -1, -1, 1 }; @@ -9953,7 +11376,7 @@ bridge_district_tile_is_valid (int tile_x, int tile_y) int nx = tile_x + adj_dx[i]; int ny = tile_y + adj_dy[i]; wrap_tile_coords (map, &nx, &ny); - if (! tile_has_district_at (nx, ny, BRIDGE_DISTRICT_ID)) + if (! tile_has_district_at (nx, ny, district_id)) continue; int cand_dx = -adj_dx[i]; @@ -9965,7 +11388,7 @@ bridge_district_tile_is_valid (int tile_x, int tile_y) wrap_tile_coords (map, &ox, &oy); if ((ox == tile_x) && (oy == tile_y)) continue; - if (! tile_has_district_at (ox, oy, BRIDGE_DISTRICT_ID)) + if (! tile_has_district_at (ox, oy, district_id)) continue; if (! ((adj_dx[j] == cand_dx) && (adj_dy[j] == cand_dy)) && @@ -9978,58 +11401,55 @@ bridge_district_tile_is_valid (int tile_x, int tile_y) } bool -bridge_tile_connects_two_continents (int tile_x, int tile_y, int civ_id) +bridge_district_tile_is_valid (int tile_x, int tile_y) { - struct bridge_pair { - int dx1, dy1; - int dx2, dy2; - }; + if (! tile_is_coastal_water (tile_x, tile_y)) + return false; + if (! bridge_tile_has_land_on_both_sides (tile_x, tile_y)) + return false; - Map * map = &p_bic_data->Map; - const struct bridge_pair pairs[] = { - {0, -2, 0, 2}, - {-2, 0, 2, 0}, - {-1, -1, 1, 1}, - {-1, 1, 1, -1}, - }; + if (is->current_config.max_contiguous_bridge_districts > 0) { + int max_bridges = is->current_config.max_contiguous_bridge_districts; - for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { - int ax = tile_x + pairs[i].dx1; - int ay = tile_y + pairs[i].dy1; - wrap_tile_coords (map, &ax, &ay); - if (! tile_is_land (civ_id, ax, ay, true)) - continue; - Tile * first = tile_at (ax, ay); - if ((first == NULL) || (first == p_null_tile)) - continue; + int ns_count = count_contiguous_bridge_districts (tile_x, tile_y, 0, -2) + + count_contiguous_bridge_districts (tile_x, tile_y, 0, 2); + if (ns_count > max_bridges) + return false; - int bx = tile_x + pairs[i].dx2; - int by = tile_y + pairs[i].dy2; - wrap_tile_coords (map, &bx, &by); - if (! tile_is_land (civ_id, bx, by, true)) - continue; - Tile * second = tile_at (bx, by); - if ((second == NULL) || (second == p_null_tile)) - continue; + int we_count = count_contiguous_bridge_districts (tile_x, tile_y, -2, 0) + + count_contiguous_bridge_districts (tile_x, tile_y, 2, 0); + if (we_count > max_bridges) + return false; - int cont_a = first->vtable->m46_Get_ContinentID (first); - int cont_b = second->vtable->m46_Get_ContinentID (second); - if ((cont_a >= 0) && (cont_b >= 0) && (cont_a != cont_b)) - return true; + int swne_count = count_contiguous_bridge_districts (tile_x, tile_y, -1, 1) + + count_contiguous_bridge_districts (tile_x, tile_y, 1, -1); + if (swne_count > max_bridges) + return false; + + int nwse_count = count_contiguous_bridge_districts (tile_x, tile_y, -1, -1) + + count_contiguous_bridge_districts (tile_x, tile_y, 1, 1); + if (nwse_count > max_bridges) + return false; } - return false; + return district_line_is_straight (tile_x, tile_y, BRIDGE_DISTRICT_ID); } bool canal_district_tile_is_valid (int tile_x, int tile_y) { + if (tile_is_water (tile_x, tile_y)) + return false; + if (is->current_config.max_contiguous_canal_districts > 0) { int count = count_contiguous_canal_districts (tile_x, tile_y, is->current_config.max_contiguous_canal_districts); if (count > is->current_config.max_contiguous_canal_districts) return false; } + if (! district_line_is_straight (tile_x, tile_y, CANAL_DISTRICT_ID)) + return false; + Map * map = &p_bic_data->Map; int const adj_dx[8] = { 0, 0, -2, 2, 1, 1, -1, -1 }; int const adj_dy[8] = { -2, 2, 0, 0, -1, 1, -1, 1 }; @@ -14525,6 +15945,9 @@ patch_init_floating_point () {"neighborhood_needed_message_frequency" , 4, offsetof (struct c3x_config, neighborhood_needed_message_frequency)}, {"max_contiguous_bridge_districts" , 3, offsetof (struct c3x_config, max_contiguous_bridge_districts)}, {"max_contiguous_canal_districts" , 5, offsetof (struct c3x_config, max_contiguous_canal_districts)}, + {"min_canal_bisected_land_tiles" , 10, offsetof (struct c3x_config, min_canal_bisected_land_tiles)}, + {"ai_bridge_canal_subset_size" , 20, offsetof (struct c3x_config, ai_bridge_canal_subset_size)}, + {"ai_bridge_lake_tile_threshold" , 6, offsetof (struct c3x_config, ai_bridge_lake_tile_threshold)}, {"ai_city_district_max_build_wait_turns" , 20, offsetof (struct c3x_config, ai_city_district_max_build_wait_turns)}, {"per_extraterritorial_colony_relation_penalty" , 0, offsetof (struct c3x_config, per_extraterritorial_colony_relation_penalty)}, }; @@ -19962,6 +21385,62 @@ patch_Map_Renderer_m19_Draw_Tile_by_XY_and_Flags (Map_Renderer * this, int edx, } } +void +insert_ai_candidate_bridge_or_canals_into_district_tile_map () +{ + if ((is->ai_candidate_bridge_or_canals_count <= 0) || (! is->ai_candidate_bridge_or_canals_initialized)) + return; + + for (int ei = 0; ei < is->ai_candidate_bridge_or_canals_count; ei++) { + struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[ei]; + if (entry == NULL) + continue; + + char ss[256]; + snprintf (ss, sizeof ss, "Entry %d: district %d owner %d tiles %d completed %d\n", + ei, entry->district_id, entry->owner_civ_id, entry->tile_count, entry->completed ? 1 : 0); + (*p_OutputDebugStringA)(ss); + + for (int ti = 0; ti < entry->tile_count; ti++) { + int tx = entry->tile_x[ti]; + int ty = entry->tile_y[ti]; + wrap_tile_coords (&p_bic_data->Map, &tx, &ty); + snprintf (ss, sizeof ss, " (%d,%d)%s\n", tx, ty, (ti == entry->assigned_tile_index) ? " [assigned]" : ""); + (*p_OutputDebugStringA)(ss); + } + } + + for (int ei = 0; ei < is->ai_candidate_bridge_or_canals_count; ei++) { + struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[ei]; + if ((entry == NULL) || (entry->completed)) + continue; + + for (int ti = 0; ti < entry->tile_count; ti++) { + int tx = entry->tile_x[ti]; + int ty = entry->tile_y[ti]; + wrap_tile_coords (&p_bic_data->Map, &tx, &ty); + Tile * tile = tile_at (tx, ty); + if ((tile == NULL) || (tile == p_null_tile)) + continue; + + int key = (int)tile; + int existing; + if (itable_look_up (&is->district_tile_map, key, &existing)) + continue; + + struct district_instance * inst = (struct district_instance *)calloc (1, sizeof *inst); + if (inst == NULL) + continue; + inst->state = DS_COMPLETED; + inst->district_type = entry->district_id; + inst->tile_x = tx; + inst->tile_y = ty; + + itable_insert (&is->district_tile_map, key, (int)(long)inst); + } + } +} + void __fastcall patch_Map_Renderer_m08_Draw_Tile_Forests_Jungle_Swamp (Map_Renderer * this, int edx, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { @@ -21587,6 +23066,10 @@ patch_Map_impl_generate (Map * this, int edx, int seed, bool is_multiplayer_game { Map_impl_generate (this, __, seed, is_multiplayer_game, num_seafaring_civs); + reset_ai_candidate_bridge_or_canals (); + plan_canal_and_bridge_targets (); + insert_ai_candidate_bridge_or_canals_into_district_tile_map (); + if (is->current_config.enable_natural_wonders) place_natural_wonders_on_map (); } @@ -29417,7 +30900,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc } case CANAL_DISTRICT_ID: { - era = 0; // DEBUG + era = clamp(2, 3, era); // DEBUG draw_canal_district (tile, tile_x, tile_y, map_renderer, pixel_x, pixel_y, era); return; } @@ -29483,7 +30966,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - //*p_debug_mode_bits |= 0xC; + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -29793,168 +31276,6 @@ ai_move_district_worker (Unit * worker, struct district_worker_record * rec) return false; } -bool -canal_has_different_adjacent_seas (int tile_x, int tile_y, int civ_id) -{ - int water_ids[2] = { -1, -1 }; - int water_count = 0; - FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { - if (tai.n == 0) - continue; - if (water_count >= 2) - break; - Tile * adj = tai.tile; - if ((adj == NULL) || (adj == p_null_tile)) - continue; - if (! adj->vtable->m35_Check_Is_Water (adj)) - continue; - if (adj->vtable->m38_Get_Territory_OwnerID (adj) != civ_id) - continue; - int sea_id = adj->vtable->m46_Get_ContinentID (adj); - if (water_count == 0 || sea_id != water_ids[0]) { - water_ids[water_count] = sea_id; - water_count += 1; - } - } - return water_count >= 2; -} - -// Check if two water tiles can reach each other via water within a radius, excluding a blocked tile -bool -water_tiles_connected_within_radius (int start_x, int start_y, int target_x, int target_y, int block_x, int block_y, int radius) -{ - // Simple BFS using a fixed-size visited array for tiles within radius - // workable_tile_counts[6] = 137 tiles for radius 6 - int max_tiles = 137; - int visited_x[137]; - int visited_y[137]; - int visited_count = 0; - int queue_x[137]; - int queue_y[137]; - int queue_head = 0; - int queue_tail = 0; - - queue_x[queue_tail] = start_x; - queue_y[queue_tail] = start_y; - queue_tail++; - visited_x[visited_count] = start_x; - visited_y[visited_count] = start_y; - visited_count++; - - while (queue_head < queue_tail) { - int cx = queue_x[queue_head]; - int cy = queue_y[queue_head]; - queue_head++; - - // Check 8 adjacent tiles - FOR_TILES_AROUND (tai, 9, cx, cy) { - if (tai.n == 0) - continue; - int nx = tai.tile_x; - int ny = tai.tile_y; - - // Found target - if (nx == target_x && ny == target_y) - return true; - - // Skip blocked tile (the isthmus) - if (nx == block_x && ny == block_y) - continue; - - // Check if within radius of original start - int dx = nx - start_x; - int dy = ny - start_y; - if (dx < 0) dx = -dx; - if (dy < 0) dy = -dy; - // Use Chebyshev distance approximation for hex grid - int dist = (dx + dy + ((dx > dy) ? dx : dy)) / 2; - if (dist > radius) - continue; - - Tile * adj = tai.tile; - if ((adj == NULL) || (adj == p_null_tile)) - continue; - if (! adj->vtable->m35_Check_Is_Water (adj)) - continue; - - // Check if already visited - bool already_visited = false; - for (int i = 0; i < visited_count; i++) { - if (visited_x[i] == nx && visited_y[i] == ny) { - already_visited = true; - break; - } - } - if (already_visited) - continue; - - // Add to queue and visited - if (visited_count < max_tiles && queue_tail < max_tiles) { - visited_x[visited_count] = nx; - visited_y[visited_count] = ny; - visited_count++; - queue_x[queue_tail] = nx; - queue_y[queue_tail] = ny; - queue_tail++; - } - } - } - return false; -} - -// Check if tile separates adjacent water tiles that are not connected within a small radius -bool -canal_has_same_sea_isthmus (int tile_x, int tile_y, int civ_id, int check_radius) -{ - (void) civ_id; - (void) check_radius; - - // If another canal exists nearby, this isn't a unique isthmus target. - FOR_TILES_AROUND (tai, workable_tile_counts[2], tile_x, tile_y) { - if (tai.n == 0) - continue; - Tile * adj = tai.tile; - if ((adj == NULL) || (adj == p_null_tile)) - continue; - struct district_instance * adj_inst = get_district_instance (adj); - if ((adj_inst != NULL) && (adj_inst->district_type == CANAL_DISTRICT_ID)) - return false; - } - - // Collect all adjacent water tiles - int adj_water_x[8]; - int adj_water_y[8]; - int adj_water_count = 0; - - FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { - if (tai.n == 0) - continue; - if (adj_water_count >= 8) - break; - Tile * adj = tai.tile; - if ((adj == NULL) || (adj == p_null_tile)) - continue; - if (! adj->vtable->m35_Check_Is_Water (adj)) - continue; - adj_water_x[adj_water_count] = tai.tile_x; - adj_water_y[adj_water_count] = tai.tile_y; - adj_water_count++; - } - - // Check pairs of adjacent water tiles that are not connected within radius 2 - for (int i = 0; i < adj_water_count; i++) { - for (int j = i + 1; j < adj_water_count; j++) { - if (! water_tiles_connected_within_radius ( - adj_water_x[i], adj_water_y[i], - adj_water_x[j], adj_water_y[j], - tile_x, tile_y, 2)) { - return true; - } - } - } - return false; -} - bool ai_worker_try_tile_improvement_district (Unit * worker) { @@ -29976,73 +31297,6 @@ ai_worker_try_tile_improvement_district (Unit * worker) if (! city_radius_contains_tile (city, tile_x, tile_y)) return false; if (get_district_instance (tile) != NULL) return false; - // Check if a canal district could be built here, making sure there isn't already one nearby and - // that either: (1) two different seas are adjacent, or (2) same sea tiles separated by isthmus - if (is->current_config.enable_canal_districts && - can_build_district_on_tile (tile, CANAL_DISTRICT_ID, civ_id)) { - bool has_adjacent_canal = false; - FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { - if (tai.n == 0) - continue; - Tile * adj = tai.tile; - if ((adj == NULL) || (adj == p_null_tile)) - continue; - struct district_instance * adj_inst = get_district_instance (adj); - if ((adj_inst != NULL) && (adj_inst->district_type == CANAL_DISTRICT_ID)) { - has_adjacent_canal = true; - break; - } - } - - if (! has_adjacent_canal) { - bool should_build = canal_has_different_adjacent_seas (tile_x, tile_y, civ_id) || - canal_has_same_sea_isthmus (tile_x, tile_y, civ_id, 2); - if (should_build) { - unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); - unsigned int removable_flags = overlay_flags & 0xfc; - tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); - if (removable_flags != 0) - tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, removable_flags, tile_x, tile_y); - ensure_district_instance (tile, CANAL_DISTRICT_ID, tile_x, tile_y); - Unit_set_state (worker, __, UnitState_Build_Mines); - worker->Body.Job_ID = WJ_Build_Mines; - return true; - } - } - } - - // Check if a bridge district could be built here, making sure there isn't already one nearby and - // that there are at least two different adjacent land tiles owned by the civ - if (is->current_config.enable_bridge_districts && - can_build_district_on_tile (tile, BRIDGE_DISTRICT_ID, civ_id)) { - bool has_adjacent_bridge = false; - FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { - if (tai.n == 0) - continue; - Tile * adj = tai.tile; - if ((adj == NULL) || (adj == p_null_tile)) - continue; - struct district_instance * adj_inst = get_district_instance (adj); - if ((adj_inst != NULL) && (adj_inst->district_type == BRIDGE_DISTRICT_ID)) { - has_adjacent_bridge = true; - break; - } - } - - // Only build if no bridges are within the immediate 9-tile radius and this tile links two civ-owned continents spotted inside the 21 tiles (radius 2) neighborhood. - if (! has_adjacent_bridge && bridge_tile_connects_two_continents (tile_x, tile_y, civ_id)) { - unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); - unsigned int removable_flags = overlay_flags & 0xfc; - tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); - if (removable_flags != 0) - tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, removable_flags, tile_x, tile_y); - ensure_district_instance (tile, BRIDGE_DISTRICT_ID, tile_x, tile_y); - Unit_set_state (worker, __, UnitState_Build_Mines); - worker->Body.Job_ID = WJ_Build_Mines; - return true; - } - } - // Evaluate whether the best improvement here is irrigation, mines, or a district, using heuristics based on city needs int food_weight = (city->Body.FoodIncome < city->Body.FoodRequired) ? 3 : 1; int shield_weight = (city->Body.ProductionIncome < city->Body.Population.Size) ? 2 : 1; @@ -30169,10 +31423,8 @@ patch_Unit_ai_move_terraformer (Unit * this) return; } - if ((this->Body.Auto_CityID >= 0) && - ai_worker_try_tile_improvement_district (this)) + if ((this->Body.Auto_CityID >= 0) && ai_worker_try_tile_improvement_district (this)) return; - } bool pop_else_caravan; From 76e627f8b0951c57ccfcb788a7b21073356c5366 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 24 Jan 2026 14:57:58 -0800 Subject: [PATCH 256/356] Add AI great wall auto build strategy --- C3X.h | 12 +++++++++--- Notes/district_todos.md | 4 ++-- default.c3x_config.ini | 3 +++ injected_code.c | 36 +++++++++++++++++++++++++++++------- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/C3X.h b/C3X.h index 346e5343..5f47a711 100644 --- a/C3X.h +++ b/C3X.h @@ -139,6 +139,11 @@ enum ai_distribution_hub_build_strategy { ADHBS_BY_CITY_COUNT }; +enum ai_auto_build_great_wall_strategy { + AAGWS_ALL_BORDERS = 0, + AAGWS_OTHER_CIV_BORDERED_ONLY +}; + enum perfume_kind { PK_PRODUCTION = 0, PK_TECHNOLOGY, @@ -385,9 +390,9 @@ struct c3x_config { bool expand_water_tile_checks_to_city_work_area; int max_contiguous_bridge_districts; int max_contiguous_canal_districts; - int min_canal_bisected_land_tiles; - int ai_bridge_canal_subset_size; - int ai_bridge_lake_tile_threshold; + int ai_min_canal_bisected_land_tiles; + int ai_bridge_canal_eval_subset_size; + int ai_bridge_eval_lake_tile_threshold; bool ai_defends_districts; int ai_city_district_max_build_wait_turns; @@ -395,6 +400,7 @@ struct c3x_config { bool disable_great_wall_city_defense_bonus; bool great_wall_districts_impassible_by_others; bool auto_build_great_wall_around_territory; + int ai_auto_build_great_wall_strategy; bool enable_city_work_radii_highlights; }; diff --git a/Notes/district_todos.md b/Notes/district_todos.md index e27cb0f4..01159d5f 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,5 +1,4 @@ - - Upfront bridge/canal algorithm - - AI great wall build strategy setting + - Validate and review upfront bridge/canal algorithm - Validate resource generation - Fix scenario district art mod folder check - Firm up logic for river district rendering @@ -25,6 +24,7 @@ - ~~Buttons~~ - ~~Auto add road/railrood~~ - ~~Named tiles~~ + - ~~AI great wall build strategy setting~~ - ~~Allow workers over water only if in radius of city that can build water district/wonder~~ - ~~Add ai_build_strategy option and AI worker handling~~ - ~~AI navies target maritime districts~~ diff --git a/default.c3x_config.ini b/default.c3x_config.ini index da48ff5e..6b0f3c97 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -911,6 +911,9 @@ expand_water_tile_checks_to_city_work_area = true workers_can_enter_coast = true max_contiguous_bridge_districts = 3 max_contiguous_canal_districts = 5 +ai_min_canal_bisected_land_tiles = 20 +ai_bridge_canal_eval_subset_size = 20 +ai_bridge_eval_lake_tile_threshold = 5 ; When enabled, AI defensive units will actively seek out and defend districts within their territory, treating them as valuable assets like colonies. ; The AI prioritizes defending Wonder districts (if destructible wonders are enabled) over regular districts, searching within a 20-tile radius for diff --git a/injected_code.c b/injected_code.c index 115ad17b..07b16f00 100644 --- a/injected_code.c +++ b/injected_code.c @@ -1728,6 +1728,16 @@ read_ai_distribution_hub_build_strategy (struct string_slice const * s, int * ou return false; } +bool +read_ai_auto_build_great_wall_strategy (struct string_slice const * s, int * out_val) +{ + struct string_slice trimmed = trim_string_slice (s, 1); + if (slice_matches_str (&trimmed, "all-borders" )) { *out_val = AAGWS_ALL_BORDERS; return true; } + else if (slice_matches_str (&trimmed, "other-civ-bordered-only")) { *out_val = AAGWS_OTHER_CIV_BORDERED_ONLY; return true; } + else + return false; +} + bool read_square_type_value (struct string_slice const * s, enum SquareTypes * out_type) { @@ -2290,6 +2300,9 @@ load_config (char const * file_path, int path_is_relative_to_mod_dir) } else if (slice_matches_str (&p.key, "ai_distribution_hub_build_strategy")) { if (! read_ai_distribution_hub_build_strategy (&value, (int *)&cfg->ai_distribution_hub_build_strategy)) handle_config_error (&p, CPE_BAD_VALUE); + } else if (slice_matches_str (&p.key, "ai_auto_build_great_wall_strategy")) { + if (! read_ai_auto_build_great_wall_strategy (&value, (int *)&cfg->ai_auto_build_great_wall_strategy)) + handle_config_error (&p, CPE_BAD_VALUE); } else if (slice_matches_str (&p.key, "ptw_like_artillery_targeting")) { if (! read_ptw_arty_types (&value, &unrecognized_lines, @@ -10948,7 +10961,7 @@ find_canal_candidate_in_block (Map * map, int block_x0, int block_y0, int block_ if (adj_count >= 2) { int best1 = 0; int best2 = 0; - int min_land = is->current_config.min_canal_bisected_land_tiles; + int min_land = is->current_config.ai_min_canal_bisected_land_tiles; if (min_land < 1) min_land = 1; visit_mark++; @@ -11249,7 +11262,7 @@ plan_canal_and_bridge_targets (void) if ((width <= 0) || (height <= 0)) return; - int subset_size = is->current_config.ai_bridge_canal_subset_size; + int subset_size = is->current_config.ai_bridge_canal_eval_subset_size; if (subset_size <= 0) subset_size = 10; @@ -15945,9 +15958,9 @@ patch_init_floating_point () {"neighborhood_needed_message_frequency" , 4, offsetof (struct c3x_config, neighborhood_needed_message_frequency)}, {"max_contiguous_bridge_districts" , 3, offsetof (struct c3x_config, max_contiguous_bridge_districts)}, {"max_contiguous_canal_districts" , 5, offsetof (struct c3x_config, max_contiguous_canal_districts)}, - {"min_canal_bisected_land_tiles" , 10, offsetof (struct c3x_config, min_canal_bisected_land_tiles)}, - {"ai_bridge_canal_subset_size" , 20, offsetof (struct c3x_config, ai_bridge_canal_subset_size)}, - {"ai_bridge_lake_tile_threshold" , 6, offsetof (struct c3x_config, ai_bridge_lake_tile_threshold)}, + {"ai_min_canal_bisected_land_tiles" , 10, offsetof (struct c3x_config, ai_min_canal_bisected_land_tiles)}, + {"ai_bridge_canal_eval_subset_size" , 20, offsetof (struct c3x_config, ai_bridge_canal_eval_subset_size)}, + {"ai_bridge_eval_lake_tile_threshold" , 6, offsetof (struct c3x_config, ai_bridge_eval_lake_tile_threshold)}, {"ai_city_district_max_build_wait_turns" , 20, offsetof (struct c3x_config, ai_city_district_max_build_wait_turns)}, {"per_extraterritorial_colony_relation_penalty" , 0, offsetof (struct c3x_config, per_extraterritorial_colony_relation_penalty)}, }; @@ -16010,6 +16023,7 @@ patch_init_floating_point () base_config.day_night_cycle_mode = DNCM_OFF; 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; 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; for (int n = 0; n < ARRAY_LEN (integer_config_options); n++) @@ -21985,6 +21999,7 @@ auto_build_great_wall_districts_for_civ (int civ_id) unsigned int const irrigation_flag = 0x8; unsigned int const replaceable_flags = TILE_FLAG_MINE | irrigation_flag; + bool require_other_civ_border = (! is_human) && is->current_config.ai_auto_build_great_wall_strategy == AAGWS_OTHER_CIV_BORDERED_ONLY; for (int index = 0; index < p_bic_data->Map.TileCount; index++) { int x, y; @@ -22000,19 +22015,26 @@ auto_build_great_wall_districts_for_civ (int civ_id) continue; bool has_border = false; + bool has_other_civ_border = false; FOR_TILES_AROUND (tai, 9, x, y) { if (tai.n == 0) continue; Tile * neighbor = tai.tile; if (neighbor->vtable->m35_Check_Is_Water (neighbor)) continue; - if (neighbor->vtable->m38_Get_Territory_OwnerID (neighbor) != civ_id) { + int owner_id = neighbor->vtable->m38_Get_Territory_OwnerID (neighbor); + if (owner_id != civ_id) { has_border = true; - break; + if (owner_id >= 0) { + has_other_civ_border = true; + break; + } } } if (! has_border) continue; + if (require_other_civ_border && (! has_other_civ_border)) + continue; if (! district_is_buildable_on_square_type (cfg, tile)) continue; From 54dbc870ba7680a4434735967804d1e9ea8b48b0 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 24 Jan 2026 21:50:53 -0800 Subject: [PATCH 257/356] Add flag for buildable_by_war_allies --- C3X.h | 7 +- default.c3x_config.ini | 4 +- injected_code.c | 455 ++++++++++++++++++++++------------------- 3 files changed, 256 insertions(+), 210 deletions(-) diff --git a/C3X.h b/C3X.h index 5f47a711..a48138c7 100644 --- a/C3X.h +++ b/C3X.h @@ -390,8 +390,8 @@ struct c3x_config { bool expand_water_tile_checks_to_city_work_area; int max_contiguous_bridge_districts; int max_contiguous_canal_districts; - int ai_min_canal_bisected_land_tiles; - int ai_bridge_canal_eval_subset_size; + int ai_canal_eval_min_bisected_land_tiles; + int ai_bridge_canal_eval_block_size; int ai_bridge_eval_lake_tile_threshold; bool ai_defends_districts; @@ -720,6 +720,7 @@ struct district_config { int buildable_by_civ_cultures_ids[5]; int buildable_by_civ_cultures_id_count; bool has_buildable_by_civ_cultures; + bool buildable_by_war_allies; }; struct wonder_district_config { @@ -939,6 +940,7 @@ struct parsed_district_definition { unsigned int buildable_square_types_mask; char * buildable_by_civs[32]; int buildable_by_civ_count; + bool buildable_by_war_allies; bool has_name; bool has_tooltip; bool has_advance_prereq; @@ -973,6 +975,7 @@ struct parsed_district_definition { bool has_buildable_on; bool has_resource_prereq_on_tile; bool has_buildable_by_civs; + bool has_buildable_by_war_allies; char * generated_resource; char * generated_resource_settings[5]; int generated_resource_settings_count; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 6b0f3c97..a4f3cca9 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -911,8 +911,8 @@ expand_water_tile_checks_to_city_work_area = true workers_can_enter_coast = true max_contiguous_bridge_districts = 3 max_contiguous_canal_districts = 5 -ai_min_canal_bisected_land_tiles = 20 -ai_bridge_canal_eval_subset_size = 20 +ai_canal_eval_min_bisected_land_tiles = 20 +ai_bridge_canal_eval_block_size = 10 ai_bridge_eval_lake_tile_threshold = 5 ; When enabled, AI defensive units will actively seek out and defend districts within their territory, treating them as valuable assets like colonies. diff --git a/injected_code.c b/injected_code.c index 07b16f00..2d5fe7c0 100644 --- a/injected_code.c +++ b/injected_code.c @@ -80,6 +80,10 @@ struct injected_state * is = ADDR_INJECTED_STATE; #define CANAL_DISTRICT_ID 9 #define GREAT_WALL_DISTRICT_ID 10 +// Max grid of tiles that an AI will evaluate a candidate bridge or canal for, +// used to limit computational complexity +#define AI_CANDIDATE_MAX_TILES 10 + enum { NAMED_TILE_MENU_ID = 0x90 }; char const * const hotseat_replay_save_path = "Saves\\Auto\\ai-move-replay-before-interturn.SAV"; @@ -215,11 +219,8 @@ bool city_can_build_district (City * city, int district_id); bool leader_can_build_district (Leader * leader, int district_id); bool find_civ_trait_id_by_name (struct string_slice const * name, int * out_id); bool find_civ_culture_id_by_name (struct string_slice const * name, int * out_id); -void get_bridge_directions (Tile * tile, int tile_x, int tile_y, int * out_dir1, int * out_dir2); -void get_canal_directions (Tile * tile, int tile_x, int tile_y, bool * out_water_dirs[9], int * out_dir1, int * out_dir2); Tile * find_tile_for_district (City * city, int district_id, int * out_x, int * out_y); struct district_instance * get_district_instance (Tile * tile); -bool tile_can_be_named (Tile * tile, int tile_x, int tile_y); struct named_tile_entry * get_named_tile_entry (Tile * tile); bool city_has_required_district (City * city, int district_id); bool district_is_complete (Tile * tile, int district_id); @@ -234,12 +235,9 @@ bool tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile void recompute_distribution_hub_totals (); void get_neighbor_coords (Map * map, int x, int y, int neighbor_index, int * out_x, int * out_y); void wrap_tile_coords (Map * map, int * x, int * y); -void init_district_icons (); int count_neighborhoods_in_city_radius (City * city); int count_utilized_neighborhoods_in_city_radius (City * city); -bool move_matches_directions (int move_dx, int move_dy, int dir1, int dir2); -bool great_wall_blocks_civ (Tile * tile, int civ_id); -void set_named_tile_entry (Tile * tile, int tile_x, int tile_y, char const * name); +void assign_workers_for_ai_candidate_bridge_or_canals (Leader * leader); struct pause_for_popup { bool done; // Set to true to exit for loop @@ -4138,8 +4136,6 @@ process_pending_district_request (Leader * leader, struct pending_district_reque assign_worker_to_district (req, worker, city, district_id, target_x, target_y); } -void assign_workers_for_ai_candidate_bridge_or_canals (Leader * leader); - void assign_workers_for_pending_districts (Leader * leader) { @@ -5639,6 +5635,7 @@ free_special_district_override_strings (struct district_config * cfg, struct dis cfg->buildable_by_civ_cultures_ids[i] = -1; cfg->buildable_by_civ_cultures_id_count = defaults->buildable_by_civ_cultures_id_count; cfg->has_buildable_by_civ_cultures = defaults->has_buildable_by_civ_cultures; + cfg->buildable_by_war_allies = defaults->buildable_by_war_allies; for (int i = 0; i < ARRAY_LEN (cfg->dependent_improvements); i++) { char const * default_value = (i < defaults->dependent_improvement_count) ? defaults->dependent_improvements[i] : NULL; @@ -6428,6 +6425,9 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->has_buildable_by_civ_cultures = true; } + if (def->has_buildable_by_war_allies) + cfg->buildable_by_war_allies = def->buildable_by_war_allies; + if (def->has_allow_multiple) cfg->allow_multiple = def->allow_multiple; if (def->has_vary_img_by_era) @@ -6712,6 +6712,8 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.buildable_by_civ_cultures_ids[i] = def->buildable_by_civ_cultures_ids[i]; new_cfg.has_buildable_by_civ_cultures = def->has_buildable_by_civ_cultures; + new_cfg.buildable_by_war_allies = def->has_buildable_by_war_allies ? def->buildable_by_war_allies : false; + new_cfg.allow_multiple = def->has_allow_multiple ? def->allow_multiple : false; new_cfg.vary_img_by_era = def->has_vary_img_by_era ? def->vary_img_by_era : false; new_cfg.vary_img_by_culture = def->has_vary_img_by_culture ? def->vary_img_by_culture : false; @@ -7140,6 +7142,15 @@ handle_district_definition_key (struct parsed_district_definition * def, } free (value_text); + } else if (slice_matches_str (key, "buildable_by_war_allies")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->buildable_by_war_allies = (ival != 0); + def->has_buildable_by_war_allies = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "img_paths")) { char * value_text = trim_and_extract_slice (value, 0); int list_count = 0; @@ -9494,6 +9505,50 @@ finalize_scenario_district_entry (struct scenario_district_entry * entry, return success; } +bool +tile_can_be_named (Tile * tile, int tile_x, int tile_y) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return false; + struct district_instance * inst = get_district_instance (tile); + if ((inst != NULL) && (inst->district_type == NATURAL_WONDER_DISTRICT_ID)) + return false; + return true; +} + +void +remove_named_tile_entry (Tile * tile) +{ + struct named_tile_entry * entry = get_named_tile_entry (tile); + if (entry == NULL) + return; + itable_remove (&is->named_tile_map, (int)tile); + free (entry); +} + +void +set_named_tile_entry (Tile * tile, int tile_x, int tile_y, char const * name) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return; + if ((name == NULL) || (name[0] == '\0')) { + remove_named_tile_entry (tile); + return; + } + + struct named_tile_entry * entry = get_named_tile_entry (tile); + if (entry == NULL) { + entry = calloc (1, sizeof *entry); + if (entry == NULL) + return; + itable_insert (&is->named_tile_map, (int)tile, (int)(long)entry); + } + entry->tile_x = tile_x; + entry->tile_y = tile_y; + strncpy (entry->name, name, sizeof entry->name); + entry->name[(sizeof entry->name) - 1] = '\0'; +} + int finalize_scenario_named_tile_entry (struct scenario_named_tile_entry * entry, int section_start_line, @@ -10318,10 +10373,6 @@ bridge_tile_connects_two_continents (int tile_x, int tile_y, int civ_id) return false; } -enum { - AI_CANDIDATE_MAX_TILES = 5 // hard limit on how many tiles a candidate can contain -}; - bool tile_point_in_block (int tile_x, int tile_y, int block_x0, int block_y0, int block_x1, int block_y1) { @@ -10961,7 +11012,7 @@ find_canal_candidate_in_block (Map * map, int block_x0, int block_y0, int block_ if (adj_count >= 2) { int best1 = 0; int best2 = 0; - int min_land = is->current_config.ai_min_canal_bisected_land_tiles; + int min_land = is->current_config.ai_canal_eval_min_bisected_land_tiles; if (min_land < 1) min_land = 1; visit_mark++; @@ -11133,9 +11184,9 @@ find_canal_candidate_in_block (Map * map, int block_x0, int block_y0, int block_ } void -plan_bridge_candidates_in_subsets (Map * map, int subset_size, int contiguous_limit) +generate_ai_bridge_candidates_by_block (Map * map, int block_size, int contiguous_limit) { - if ((map == NULL) || (subset_size <= 0)) + if ((map == NULL) || (block_size <= 0)) return; int width = map->Width; @@ -11143,7 +11194,6 @@ plan_bridge_candidates_in_subsets (Map * map, int subset_size, int contiguous_li if ((width <= 0) || (height <= 0)) return; - int block_size = subset_size; if (block_size < 1) block_size = 1; @@ -11191,9 +11241,9 @@ plan_bridge_candidates_in_subsets (Map * map, int subset_size, int contiguous_li } void -plan_canal_candidates_in_subsets (Map * map, int subset_size, int contiguous_limit) +generate_ai_canal_candidates_by_block (Map * map, int block_size, int contiguous_limit) { - if ((map == NULL) || (subset_size <= 0)) + if ((map == NULL) || (block_size <= 0)) return; int width = map->Width; @@ -11201,7 +11251,6 @@ plan_canal_candidates_in_subsets (Map * map, int subset_size, int contiguous_lim if ((width <= 0) || (height <= 0)) return; - int block_size = subset_size; if (block_size < 1) block_size = 1; @@ -11249,7 +11298,7 @@ plan_canal_candidates_in_subsets (Map * map, int subset_size, int contiguous_lim } void -plan_canal_and_bridge_targets (void) +generate_ai_canal_and_bridge_targets () { if (is->ai_candidate_bridge_or_canals_initialized) return; @@ -11262,21 +11311,21 @@ plan_canal_and_bridge_targets (void) if ((width <= 0) || (height <= 0)) return; - int subset_size = is->current_config.ai_bridge_canal_eval_subset_size; - if (subset_size <= 0) - subset_size = 10; + int block_size = is->current_config.ai_bridge_canal_eval_block_size; + if (block_size <= 0) + block_size = 10; if (is->current_config.enable_canal_districts) { - plan_canal_candidates_in_subsets ( + generate_ai_canal_candidates_by_block ( map, - subset_size, + block_size, is->current_config.max_contiguous_canal_districts); } if (is->current_config.enable_bridge_districts) { - plan_bridge_candidates_in_subsets ( + generate_ai_bridge_candidates_by_block ( map, - subset_size, + block_size, is->current_config.max_contiguous_bridge_districts); } @@ -11531,6 +11580,7 @@ can_build_district_on_tile (Tile * tile, int district_id, int civ_id) return false; struct district_instance * existing_inst = get_district_instance (tile); + struct district_infos const * info = &is->district_infos[district_id]; int existing_district_id = (existing_inst != NULL) ? existing_inst->district_type : -1; bool district_completed = district_is_complete (tile, existing_district_id); @@ -11538,7 +11588,7 @@ can_build_district_on_tile (Tile * tile, int district_id, int civ_id) return false; if ((existing_district_id >= 0) && (existing_district_id != district_id) && (! district_completed)) return false; - + if (! cfg->allow_multiple) { FOR_DISTRICTS_AROUND (wai, tile_x, tile_y, false) { if (wai.district_inst->district_type == district_id) @@ -12316,7 +12366,7 @@ count_distribution_hubs_for_civ (int civ_id) } bool -leader_can_build_district (Leader * leader, int district_id) +leader_can_natively_build_district (Leader * leader, int district_id) { if ((leader == NULL) || (district_id < 0) || (district_id >= is->district_count)) return false; @@ -12430,6 +12480,39 @@ leader_can_build_district (Leader * leader, int district_id) return true; } +bool +leader_has_war_ally_district_access (Leader * leader, int district_id) +{ + if (leader == NULL) + return false; + + int self_id = leader->ID; + for (int civ_id = 0; civ_id < 32; civ_id++) { + if (civ_id == self_id) + continue; + Leader * other = &leaders[civ_id]; + if (other->Military_Allies[self_id] != 0 && leader_can_natively_build_district (other, district_id)) + return true; + } + + return false; +} + +bool +leader_can_build_district (Leader * leader, int district_id) +{ + bool can_natively_build = leader_can_natively_build_district (leader, district_id); + + if (can_natively_build) + return true; + + struct district_config const * cfg = &is->district_configs[district_id]; + if (cfg->buildable_by_war_allies && leader_has_war_ally_district_access (leader, district_id)) + return true; + + return false; +} + Tile * find_tile_for_district (City * city, int district_id, int * out_x, int * out_y) { @@ -15958,9 +16041,9 @@ patch_init_floating_point () {"neighborhood_needed_message_frequency" , 4, offsetof (struct c3x_config, neighborhood_needed_message_frequency)}, {"max_contiguous_bridge_districts" , 3, offsetof (struct c3x_config, max_contiguous_bridge_districts)}, {"max_contiguous_canal_districts" , 5, offsetof (struct c3x_config, max_contiguous_canal_districts)}, - {"ai_min_canal_bisected_land_tiles" , 10, offsetof (struct c3x_config, ai_min_canal_bisected_land_tiles)}, - {"ai_bridge_canal_eval_subset_size" , 20, offsetof (struct c3x_config, ai_bridge_canal_eval_subset_size)}, - {"ai_bridge_eval_lake_tile_threshold" , 6, offsetof (struct c3x_config, ai_bridge_eval_lake_tile_threshold)}, + {"ai_canal_eval_min_bisected_land_tiles" , 10, offsetof (struct c3x_config, ai_canal_eval_min_bisected_land_tiles)}, + {"ai_bridge_canal_eval_block_size" , 20, offsetof (struct c3x_config, ai_bridge_canal_eval_block_size)}, + {"ai_bridge_eval_lake_tile_threshold" , 6, offsetof (struct c3x_config, ai_bridge_eval_lake_tile_threshold)}, {"ai_city_district_max_build_wait_turns" , 20, offsetof (struct c3x_config, ai_city_district_max_build_wait_turns)}, {"per_extraterritorial_colony_relation_penalty" , 0, offsetof (struct c3x_config, per_extraterritorial_colony_relation_penalty)}, }; @@ -18386,6 +18469,84 @@ patch_City_Form_open (City_Form * this, int edx, City * city, int param_2) } } +void +init_district_icons () +{ + if (is->dc_icons_img_state != IS_UNINITED) + return; + + char ss[200]; + snprintf (ss, sizeof ss, "[C3X] init_district_icons: state=%d\n", is->dc_icons_img_state); + (*p_OutputDebugStringA) (ss); + + PCX_Image pcx; + PCX_Image_construct (&pcx); + + char temp_path[2*MAX_PATH]; + get_mod_art_path ("Districts/DistrictIncomeIcons.pcx", temp_path, sizeof temp_path); + + PCX_Image_read_file (&pcx, __, temp_path, NULL, 0, 0x100, 2); + if ((pcx.JGL.Image == NULL) || + (pcx.JGL.Image->vtable->m54_Get_Width (pcx.JGL.Image) < 776) || + (pcx.JGL.Image->vtable->m55_Get_Height (pcx.JGL.Image) < 32)) { + (*p_OutputDebugStringA) ("[C3X] PCX file for district icons failed to load or is too small.\n"); + is->dc_icons_img_state = IS_INIT_FAILED; + goto cleanup; + } + + // Extract science icon (index 1: x = 1 + 1*31 = 32, width 30) + Sprite_construct (&is->district_science_icon); + Sprite_slice_pcx (&is->district_science_icon, __, &pcx, 1 + 1*31, 1, 30, 30, 1, 1); + + // Extract commerce icon (index 2: x = 1 + 2*31 = 63, width 30) + Sprite_construct (&is->district_commerce_icon); + Sprite_slice_pcx (&is->district_commerce_icon, __, &pcx, 1 + 2*31, 1, 30, 30, 1, 1); + + // Extract shield icon (index 4: x = 1 + 4*31 = 125, width 30) + Sprite_construct (&is->district_shield_icon); + Sprite_slice_pcx (&is->district_shield_icon, __, &pcx, 1 + 4*31, 1, 30, 30, 1, 1); + + // Extract corruption icon (index 5: x = 1 + 5*31 = 156, width 30) + Sprite_construct (&is->district_corruption_icon); + Sprite_slice_pcx (&is->district_corruption_icon, __, &pcx, 1 + 5*31, 1, 30, 30, 1, 1); + + // Extract food icon (index 6: x = 1 + 6*31 = 187, width 30) + Sprite_construct (&is->district_food_icon); + Sprite_slice_pcx (&is->district_food_icon, __, &pcx, 1 + 6*31, 1, 30, 30, 1, 1); + + // Extract food eaten icon (index 7: x = 1 + 7*31 = 218, width 30) + Sprite_construct (&is->district_food_eaten_icon); + Sprite_slice_pcx (&is->district_food_eaten_icon, __, &pcx, 1 + 7*31, 1, 30, 30, 1, 1); + + // Extract happiness icon (index 12: x = 1 + 12*31 = 373, width 30) + Sprite_construct (&is->district_happiness_icon); + Sprite_slice_pcx (&is->district_happiness_icon, __, &pcx, 1 + 12*31, 1, 30, 30, 1, 1); + + // Extract small shield icon (index 13: x = 1 + 13*31 = 404, width 30) + Sprite_construct (&is->district_shield_icon_small); + Sprite_slice_pcx (&is->district_shield_icon_small, __, &pcx, 1 + 13*31, 1, 30, 30, 1, 1); + + // Extract small commerce icon (index 14: x = 1 + 14*31 = 435, width 30) + Sprite_construct (&is->district_commerce_icon_small); + Sprite_slice_pcx (&is->district_commerce_icon_small, __, &pcx, 1 + 14*31, 1, 30, 30, 1, 1); + + // Extract small food icon (index 15: x = 1 + 15*31 = 466, width 30) + Sprite_construct (&is->district_food_icon_small); + Sprite_slice_pcx (&is->district_food_icon_small, __, &pcx, 1 + 15*31, 1, 30, 30, 1, 1); + + // Extract small science icon (index 16: x = 1 + 16*31 = 497, width 30) + Sprite_construct (&is->district_science_icon_small); + Sprite_slice_pcx (&is->district_science_icon_small, __, &pcx, 1 + 16*31, 1, 30, 30, 1, 1); + + // Extract small culture icon (index 18: x = 1 + 18*31 = 559, width 30) + Sprite_construct (&is->district_culture_icon_small); + Sprite_slice_pcx (&is->district_culture_icon_small, __, &pcx, 1 + 18*31, 1, 30, 30, 1, 1); + + is->dc_icons_img_state = IS_OK; +cleanup: + pcx.vtable->destruct (&pcx, __, 0); +} + void __fastcall patch_City_Form_draw (City_Form * this) { @@ -18761,6 +18922,37 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, return base_validity; } +bool +great_wall_blocks_civ (Tile * tile, int civ_id) +{ + if (! is->current_config.enable_districts || + ! is->current_config.enable_great_wall_districts || + ! is->current_config.great_wall_districts_impassible_by_others) + return false; + + if ((tile == NULL) || (tile == p_null_tile)) + return false; + + int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); + if (owner_id <= 0) + return false; + if (owner_id == civ_id) + return false; + + struct district_instance * inst = get_district_instance (tile); + if ((inst == NULL) || (inst->district_type != GREAT_WALL_DISTRICT_ID)) + return false; + + if (! district_is_complete (tile, GREAT_WALL_DISTRICT_ID)) + return false; + + int obsolete_id = is->district_infos[GREAT_WALL_DISTRICT_ID].obsoleted_by_id; + if ((obsolete_id >= 0) && Leader_has_tech (&leaders[civ_id], __, obsolete_id)) + return false; + + return true; +} + int __fastcall patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int from_y, int to_x, int to_y, Unit * unit, int civ_id, unsigned flags, int neighbor_index, Trade_Net_Distance_Info * dist_info) { @@ -22356,17 +22548,6 @@ patch_Map_Renderer_m71_Draw_Tiles (Map_Renderer * this, int edx, int param_1, in Map_Renderer_m71_Draw_Tiles (this, __, param_1, param_2, param_3); } -bool -tile_can_be_named (Tile * tile, int tile_x, int tile_y) -{ - if ((tile == NULL) || (tile == p_null_tile)) - return false; - struct district_instance * inst = get_district_instance (tile); - if ((inst != NULL) && (inst->district_type == NATURAL_WONDER_DISTRICT_ID)) - return false; - return true; -} - struct named_tile_entry * get_named_tile_entry (Tile * tile) { @@ -22378,39 +22559,6 @@ get_named_tile_entry (Tile * tile) return (struct named_tile_entry *)stored_ptr; } -void -remove_named_tile_entry (Tile * tile) -{ - struct named_tile_entry * entry = get_named_tile_entry (tile); - if (entry == NULL) - return; - itable_remove (&is->named_tile_map, (int)tile); - free (entry); -} - -void -set_named_tile_entry (Tile * tile, int tile_x, int tile_y, char const * name) -{ - if ((tile == NULL) || (tile == p_null_tile)) - return; - if ((name == NULL) || (name[0] == '\0')) { - remove_named_tile_entry (tile); - return; - } - - struct named_tile_entry * entry = get_named_tile_entry (tile); - if (entry == NULL) { - entry = calloc (1, sizeof *entry); - if (entry == NULL) - return; - itable_insert (&is->named_tile_map, (int)tile, (int)(long)entry); - } - entry->tile_x = tile_x; - entry->tile_y = tile_y; - strncpy (entry->name, name, sizeof entry->name); - entry->name[(sizeof entry->name) - 1] = '\0'; -} - bool prompt_for_named_tile (char const * seed_name, char * out_name, int out_len) { @@ -23088,9 +23236,12 @@ patch_Map_impl_generate (Map * this, int edx, int seed, bool is_multiplayer_game { Map_impl_generate (this, __, seed, is_multiplayer_game, num_seafaring_civs); - reset_ai_candidate_bridge_or_canals (); - plan_canal_and_bridge_targets (); - insert_ai_candidate_bridge_or_canals_into_district_tile_map (); + if (is->current_config.enable_districts && + (is->current_config.enable_bridge_districts || is->current_config.enable_canal_districts)) { + reset_ai_candidate_bridge_or_canals (); + generate_ai_canal_and_bridge_targets (); + insert_ai_candidate_bridge_or_canals_into_district_tile_map (); + } if (is->current_config.enable_natural_wonders) place_natural_wonders_on_map (); @@ -26679,8 +26830,8 @@ patch_Map_place_scenario_things (Map * this) is->current_config.enable_named_tiles) load_scenario_districts_from_file (); - if (is->current_config.add_natural_wonders_to_scenarios_if_none && - is->current_config.enable_natural_wonders) { + if (is->current_config.enable_natural_wonders && + is->current_config.add_natural_wonders_to_scenarios_if_none) { bool any_natural_wonders = false; FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { struct district_instance * inst = (struct district_instance *)(long)tei.value; @@ -26695,6 +26846,13 @@ patch_Map_place_scenario_things (Map * this) place_natural_wonders_on_map (); } + if (is->current_config.enable_districts && + (is->current_config.enable_bridge_districts || is->current_config.enable_canal_districts)) { + reset_ai_candidate_bridge_or_canals (); + generate_ai_canal_and_bridge_targets (); + //insert_ai_candidate_bridge_or_canals_into_district_tile_map (); + } + is->is_placing_scenario_things = false; } @@ -28533,84 +28691,6 @@ init_distribution_hub_icons () pcx.vtable->destruct (&pcx, __, 0); } -void -init_district_icons () -{ - if (is->dc_icons_img_state != IS_UNINITED) - return; - - char ss[200]; - snprintf (ss, sizeof ss, "[C3X] init_district_icons: state=%d\n", is->dc_icons_img_state); - (*p_OutputDebugStringA) (ss); - - PCX_Image pcx; - PCX_Image_construct (&pcx); - - char temp_path[2*MAX_PATH]; - get_mod_art_path ("Districts/DistrictIncomeIcons.pcx", temp_path, sizeof temp_path); - - PCX_Image_read_file (&pcx, __, temp_path, NULL, 0, 0x100, 2); - if ((pcx.JGL.Image == NULL) || - (pcx.JGL.Image->vtable->m54_Get_Width (pcx.JGL.Image) < 776) || - (pcx.JGL.Image->vtable->m55_Get_Height (pcx.JGL.Image) < 32)) { - (*p_OutputDebugStringA) ("[C3X] PCX file for district icons failed to load or is too small.\n"); - is->dc_icons_img_state = IS_INIT_FAILED; - goto cleanup; - } - - // Extract science icon (index 1: x = 1 + 1*31 = 32, width 30) - Sprite_construct (&is->district_science_icon); - Sprite_slice_pcx (&is->district_science_icon, __, &pcx, 1 + 1*31, 1, 30, 30, 1, 1); - - // Extract commerce icon (index 2: x = 1 + 2*31 = 63, width 30) - Sprite_construct (&is->district_commerce_icon); - Sprite_slice_pcx (&is->district_commerce_icon, __, &pcx, 1 + 2*31, 1, 30, 30, 1, 1); - - // Extract shield icon (index 4: x = 1 + 4*31 = 125, width 30) - Sprite_construct (&is->district_shield_icon); - Sprite_slice_pcx (&is->district_shield_icon, __, &pcx, 1 + 4*31, 1, 30, 30, 1, 1); - - // Extract corruption icon (index 5: x = 1 + 5*31 = 156, width 30) - Sprite_construct (&is->district_corruption_icon); - Sprite_slice_pcx (&is->district_corruption_icon, __, &pcx, 1 + 5*31, 1, 30, 30, 1, 1); - - // Extract food icon (index 6: x = 1 + 6*31 = 187, width 30) - Sprite_construct (&is->district_food_icon); - Sprite_slice_pcx (&is->district_food_icon, __, &pcx, 1 + 6*31, 1, 30, 30, 1, 1); - - // Extract food eaten icon (index 7: x = 1 + 7*31 = 218, width 30) - Sprite_construct (&is->district_food_eaten_icon); - Sprite_slice_pcx (&is->district_food_eaten_icon, __, &pcx, 1 + 7*31, 1, 30, 30, 1, 1); - - // Extract happiness icon (index 12: x = 1 + 12*31 = 373, width 30) - Sprite_construct (&is->district_happiness_icon); - Sprite_slice_pcx (&is->district_happiness_icon, __, &pcx, 1 + 12*31, 1, 30, 30, 1, 1); - - // Extract small shield icon (index 13: x = 1 + 13*31 = 404, width 30) - Sprite_construct (&is->district_shield_icon_small); - Sprite_slice_pcx (&is->district_shield_icon_small, __, &pcx, 1 + 13*31, 1, 30, 30, 1, 1); - - // Extract small commerce icon (index 14: x = 1 + 14*31 = 435, width 30) - Sprite_construct (&is->district_commerce_icon_small); - Sprite_slice_pcx (&is->district_commerce_icon_small, __, &pcx, 1 + 14*31, 1, 30, 30, 1, 1); - - // Extract small food icon (index 15: x = 1 + 15*31 = 466, width 30) - Sprite_construct (&is->district_food_icon_small); - Sprite_slice_pcx (&is->district_food_icon_small, __, &pcx, 1 + 15*31, 1, 30, 30, 1, 1); - - // Extract small science icon (index 16: x = 1 + 16*31 = 497, width 30) - Sprite_construct (&is->district_science_icon_small); - Sprite_slice_pcx (&is->district_science_icon_small, __, &pcx, 1 + 16*31, 1, 30, 30, 1, 1); - - // Extract small culture icon (index 18: x = 1 + 18*31 = 559, width 30) - Sprite_construct (&is->district_culture_icon_small); - Sprite_slice_pcx (&is->district_culture_icon_small, __, &pcx, 1 + 18*31, 1, 30, 30, 1, 1); - - is->dc_icons_img_state = IS_OK; -cleanup: - pcx.vtable->destruct (&pcx, __, 0); -} - void draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int screen_x, int screen_y) { @@ -31182,10 +31262,19 @@ ai_move_district_worker (Unit * worker, struct district_worker_record * rec) return false; } + // Allow replacement of unused wonder districts if (existing_district_id == WONDER_DISTRICT_ID) { struct wonder_district_info * info = &inst->wonder_info; if (info->state == WDS_UNUSED) do_replacement = true; + // Allow replace of Great Wall if the civ has the tech to make it obsolete + } else if (existing_district_id == GREAT_WALL_DISTRICT_ID) { + + } else { + snprintf (ss, sizeof ss, "ai_move_district_worker: Worker ID %d cannot replace existing district ID %d at (%d,%d), cancelling request\n", worker->Body.ID, existing_district_id, worker->Body.X, worker->Body.Y); + (*p_OutputDebugStringA) (ss); + clear_city_district_request (request_city, req->district_id); + return false; } if (!do_replacement) { @@ -32340,52 +32429,6 @@ patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool return transport; } -bool -great_wall_blocks_civ (Tile * tile, int civ_id) -{ - if (! is->current_config.enable_districts || - ! is->current_config.enable_great_wall_districts || - ! is->current_config.great_wall_districts_impassible_by_others) - return false; - - if ((tile == NULL) || (tile == p_null_tile)) - return false; - - int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); - if (owner_id <= 0) - return false; - if (owner_id == civ_id) - return false; - - struct district_instance * inst = get_district_instance (tile); - if ((inst == NULL) || (inst->district_type != GREAT_WALL_DISTRICT_ID)) - return false; - - if (! district_is_complete (tile, GREAT_WALL_DISTRICT_ID)) - return false; - - int obsolete_id = is->district_infos[GREAT_WALL_DISTRICT_ID].obsoleted_by_id; - if ((obsolete_id >= 0) && Leader_has_tech (&leaders[civ_id], __, obsolete_id)) - return false; - - return true; -} - -bool -move_matches_directions (int move_dx, int move_dy, int dir1, int dir2) -{ - int dx = 0, dy = 0; - if ((dir1 >= 0) && - direction_to_offset ((enum direction)dir1, &dx, &dy) && - (dx == move_dx) && (dy == move_dy)) - return true; - if ((dir2 >= 0) && - direction_to_offset ((enum direction)dir2, &dx, &dy) && - (dx == move_dx) && (dy == move_dy)) - return true; - return false; -} - // Returns true if the given tile is a water district owned by an enemy of the unit bool is_enemy_maritime_district_tile (Unit * unit, Tile * tile) From 6965c9d2a814fff328ec5e16332034f489cbd3bf Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 24 Jan 2026 22:00:20 -0800 Subject: [PATCH 258/356] Add flag for buildable_by_pact_allies --- C3X.h | 3 +++ injected_code.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/C3X.h b/C3X.h index a48138c7..7d6ea669 100644 --- a/C3X.h +++ b/C3X.h @@ -721,6 +721,7 @@ struct district_config { int buildable_by_civ_cultures_id_count; bool has_buildable_by_civ_cultures; bool buildable_by_war_allies; + bool buildable_by_pact_allies; }; struct wonder_district_config { @@ -941,6 +942,7 @@ struct parsed_district_definition { char * buildable_by_civs[32]; int buildable_by_civ_count; bool buildable_by_war_allies; + bool buildable_by_pact_allies; bool has_name; bool has_tooltip; bool has_advance_prereq; @@ -976,6 +978,7 @@ struct parsed_district_definition { bool has_resource_prereq_on_tile; bool has_buildable_by_civs; bool has_buildable_by_war_allies; + bool has_buildable_by_pact_allies; char * generated_resource; char * generated_resource_settings[5]; int generated_resource_settings_count; diff --git a/injected_code.c b/injected_code.c index 2d5fe7c0..bf471f46 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5636,6 +5636,7 @@ free_special_district_override_strings (struct district_config * cfg, struct dis cfg->buildable_by_civ_cultures_id_count = defaults->buildable_by_civ_cultures_id_count; cfg->has_buildable_by_civ_cultures = defaults->has_buildable_by_civ_cultures; cfg->buildable_by_war_allies = defaults->buildable_by_war_allies; + cfg->buildable_by_pact_allies = defaults->buildable_by_pact_allies; for (int i = 0; i < ARRAY_LEN (cfg->dependent_improvements); i++) { char const * default_value = (i < defaults->dependent_improvement_count) ? defaults->dependent_improvements[i] : NULL; @@ -6427,6 +6428,8 @@ override_special_district_from_definition (struct parsed_district_definition * d if (def->has_buildable_by_war_allies) cfg->buildable_by_war_allies = def->buildable_by_war_allies; + if (def->has_buildable_by_pact_allies) + cfg->buildable_by_pact_allies = def->buildable_by_pact_allies; if (def->has_allow_multiple) cfg->allow_multiple = def->allow_multiple; @@ -6713,6 +6716,7 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.has_buildable_by_civ_cultures = def->has_buildable_by_civ_cultures; new_cfg.buildable_by_war_allies = def->has_buildable_by_war_allies ? def->buildable_by_war_allies : false; + new_cfg.buildable_by_pact_allies = def->has_buildable_by_pact_allies ? def->buildable_by_pact_allies : false; new_cfg.allow_multiple = def->has_allow_multiple ? def->allow_multiple : false; new_cfg.vary_img_by_era = def->has_vary_img_by_era ? def->vary_img_by_era : false; @@ -7151,6 +7155,15 @@ handle_district_definition_key (struct parsed_district_definition * def, } else add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "buildable_by_pact_allies")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->buildable_by_pact_allies = (ival != 0); + def->has_buildable_by_pact_allies = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "img_paths")) { char * value_text = trim_and_extract_slice (value, 0); int list_count = 0; @@ -12498,6 +12511,27 @@ leader_has_war_ally_district_access (Leader * leader, int district_id) return false; } +bool +leader_has_pact_ally_district_access (Leader * leader, int district_id) +{ + if (leader == NULL) + return false; + + int self_id = leader->ID; + for (int civ_id = 0; civ_id < 32; civ_id++) { + if (civ_id == self_id) + continue; + Leader * other = &leaders[civ_id]; + if (((leader->Relation_Treaties[civ_id] & 1) != 0 || + (leader->Relation_Treaties[civ_id] & 4) != 0) && + leader_can_natively_build_district (other, district_id)) { + return true; + } + } + + return false; +} + bool leader_can_build_district (Leader * leader, int district_id) { @@ -12509,6 +12543,8 @@ leader_can_build_district (Leader * leader, int district_id) struct district_config const * cfg = &is->district_configs[district_id]; if (cfg->buildable_by_war_allies && leader_has_war_ally_district_access (leader, district_id)) return true; + if (cfg->buildable_by_pact_allies && leader_has_pact_ally_district_access (leader, district_id)) + return true; return false; } From 624824f9db6f9fa07002e8058d4aea09e1551018 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 24 Jan 2026 22:15:25 -0800 Subject: [PATCH 259/356] Fix scenario art from search folder not loading bug --- Notes/district_todos.md | 4 ++- injected_code.c | 65 +++++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 01159d5f..3886011a 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,5 +1,5 @@ - Validate and review upfront bridge/canal algorithm - - Validate resource generation + - Validate resource generation (add yield?) - Fix scenario district art mod folder check - Firm up logic for river district rendering @@ -20,8 +20,10 @@ ## Maritime Districts + - ~~Fix scenario art not loading bug~~ - ~~Choose terrain type~~ - ~~Buttons~~ + - ~~Add buildable_by_war_allies, buildable_by_pact_allies~~ - ~~Auto add road/railrood~~ - ~~Named tiles~~ - ~~AI great wall build strategy setting~~ diff --git a/injected_code.c b/injected_code.c index bf471f46..d24f43b7 100644 --- a/injected_code.c +++ b/injected_code.c @@ -15057,6 +15057,22 @@ apply_machine_code_edits (struct c3x_config const * cfg, bool at_program_start) } } +void +get_mod_art_path (char const * file_name, char * out_path, int path_buf_size) +{ + char s[1000]; + snprintf (s, sizeof s, "Art\\%s", file_name); + s[(sizeof s) - 1] = '\0'; + + char * scenario_path = BIC_get_asset_path (p_bic_data, __, s, false); + if (0 != strcmp (scenario_path, s)) // get_asset_path returns its input when the file is not found + snprintf (out_path, path_buf_size, "%s", scenario_path); + else + snprintf (out_path, path_buf_size, "%s\\Art\\%s", is->mod_rel_dir, file_name); + out_path[path_buf_size - 1] = '\0'; +} + + Sprite* SpriteList_at(SpriteList *list, int i) { return &list->field_0[i]; @@ -15736,8 +15752,10 @@ init_day_night_images() for (int i = 0; i < 24; i++) { char art_dir[200]; - snprintf(art_dir, sizeof art_dir, "%s\\Art\\DayNight\\%s", is->mod_rel_dir, hour_strs[i]); - bool success = load_day_night_hour_images(&is->day_night_cycle_imgs[i], art_dir, hour_strs[i]); + char temp_path[2*MAX_PATH]; + snprintf (art_dir, sizeof art_dir, "DayNight/%s", hour_strs[i]); + get_mod_art_path (art_dir, temp_path, sizeof temp_path); + bool success = load_day_night_hour_images (&is->day_night_cycle_imgs[i], temp_path, hour_strs[i]); if (!success) { char ss[200]; @@ -16368,21 +16386,6 @@ patch_init_floating_point () apply_machine_code_edits (&is->current_config, true); } -void -get_mod_art_path (char const * file_name, char * out_path, int path_buf_size) -{ - char s[1000]; - snprintf (s, sizeof s, "Art\\%s", file_name); - s[(sizeof s) - 1] = '\0'; - - char * scenario_path = BIC_get_asset_path (p_bic_data, __, s, false); - if (0 != strcmp (scenario_path, s)) // get_asset_path returns its input when the file is not found - snprintf (out_path, path_buf_size, "%s", scenario_path); - else - snprintf (out_path, path_buf_size, "%s\\Art\\%s", is->mod_rel_dir, file_name); - out_path[path_buf_size - 1] = '\0'; -} - void init_stackable_command_buttons () { @@ -29847,6 +29850,7 @@ init_district_images () return; char art_dir[200]; + char temp_path[2*MAX_PATH]; is->dc_img_state = IS_INIT_FAILED; @@ -29871,14 +29875,15 @@ init_district_images () continue; // Read PCX file - snprintf (art_dir, sizeof art_dir, "%s\\Art\\Districts\\1200\\%s", is->mod_rel_dir, cfg->img_paths[variant_i]); + snprintf (art_dir, sizeof art_dir, "Districts/1200/%s", cfg->img_paths[variant_i]); + get_mod_art_path (art_dir, temp_path, sizeof temp_path); - PCX_Image_read_file (&pcx, __, art_dir, NULL, 0, 0x100, 2); + PCX_Image_read_file (&pcx, __, temp_path, NULL, 0, 0x100, 2); if (pcx.JGL.Image == NULL) { char ss[200]; - snprintf (ss, sizeof ss, "init_district_images: failed to load district images from %s", art_dir); + snprintf (ss, sizeof ss, "init_district_images: failed to load district images from %s", temp_path); pop_up_in_game_error (ss); (*p_OutputDebugStringA) ("[C3X] Failed to load districts sprite sheet.\n"); @@ -29912,12 +29917,12 @@ init_district_images () } } // Load abandoned district images (land + maritime) - snprintf (art_dir, sizeof art_dir, "%s\\Art\\Districts\\1200\\Abandoned.pcx", is->mod_rel_dir); - PCX_Image_read_file (&pcx, __, art_dir, NULL, 0, 0x100, 2); + get_mod_art_path ("Districts/1200/Abandoned.pcx", temp_path, sizeof temp_path); + PCX_Image_read_file (&pcx, __, temp_path, NULL, 0, 0x100, 2); if (pcx.JGL.Image == NULL) { char ss[200]; - snprintf (ss, sizeof ss, "init_district_images: failed to load abandoned district images from %s", art_dir); + snprintf (ss, sizeof ss, "init_district_images: failed to load abandoned district images from %s", temp_path); pop_up_in_game_error (ss); for (int dc2 = 0; dc2 < COUNT_DISTRICT_TYPES; dc2++) for (int variant_i2 = 0; variant_i2 < ARRAY_LEN (is->district_img_sets[dc2].imgs); variant_i2++) @@ -29957,12 +29962,13 @@ init_district_images () if (pcx_loaded) wpcx.vtable->clear_JGL (&wpcx); - snprintf(art_dir, sizeof art_dir, "%s\\Art\\Districts\\1200\\%s", is->mod_rel_dir, img_path); - PCX_Image_read_file (&wpcx, __, art_dir, NULL, 0, 0x100, 2); + snprintf (art_dir, sizeof art_dir, "Districts/1200/%s", img_path); + get_mod_art_path (art_dir, temp_path, sizeof temp_path); + PCX_Image_read_file (&wpcx, __, temp_path, NULL, 0, 0x100, 2); if (wpcx.JGL.Image == NULL) { char ss[200]; - snprintf (ss, sizeof ss, "init_district_images: failed to load wonder district images from %s", art_dir); + snprintf (ss, sizeof ss, "init_district_images: failed to load wonder district images from %s", temp_path); pop_up_in_game_error (ss); pcx_loaded = false; continue; @@ -30021,12 +30027,13 @@ init_district_images () if (pcx_loaded) nwpcx.vtable->clear_JGL (&nwpcx); - snprintf (art_dir, sizeof art_dir, "%s\\Art\\Districts\\1200\\%s", is->mod_rel_dir, img_path); - PCX_Image_read_file (&nwpcx, __, art_dir, NULL, 0, 0x100, 2); + snprintf (art_dir, sizeof art_dir, "Districts/1200/%s", img_path); + get_mod_art_path (art_dir, temp_path, sizeof temp_path); + PCX_Image_read_file (&nwpcx, __, temp_path, NULL, 0, 0x100, 2); if (nwpcx.JGL.Image == NULL) { char ss[200]; - snprintf (ss, sizeof ss, "init_district_images: failed to load natural wonder images from %s", art_dir); + snprintf (ss, sizeof ss, "init_district_images: failed to load natural wonder images from %s", temp_path); pop_up_in_game_error (ss); pcx_loaded = false; continue; From ea5ce1936cb90764741ea30a00b0f2fa0f99af9f Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 24 Jan 2026 22:34:13 -0800 Subject: [PATCH 260/356] Clean up bridge & canal AI logic --- injected_code.c | 65 +++++++++++++++---------------------------------- 1 file changed, 19 insertions(+), 46 deletions(-) diff --git a/injected_code.c b/injected_code.c index d24f43b7..0e43687b 100644 --- a/injected_code.c +++ b/injected_code.c @@ -82,7 +82,7 @@ struct injected_state * is = ADDR_INJECTED_STATE; // Max grid of tiles that an AI will evaluate a candidate bridge or canal for, // used to limit computational complexity -#define AI_CANDIDATE_MAX_TILES 10 +#define AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES 10 enum { NAMED_TILE_MENU_ID = 0x90 }; @@ -10545,21 +10545,17 @@ gather_bridge_line (int start_x, int start_y, int dx, int dy, int limit, int block_x0, int block_y0, int block_x1, int block_y1, short * out_x, short * out_y) { - int effective_limit = limit; - if (effective_limit <= 0) - effective_limit = 1; - if (effective_limit > AI_CANDIDATE_MAX_TILES) - effective_limit = AI_CANDIDATE_MAX_TILES; + int effective_limit = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, limit); if (! tile_is_coastal_water (start_x, start_y)) return 0; if (! bridge_tile_has_land_on_both_sides (start_x, start_y)) return 0; - short back_x[AI_CANDIDATE_MAX_TILES]; - short back_y[AI_CANDIDATE_MAX_TILES]; - short forward_x[AI_CANDIDATE_MAX_TILES]; - short forward_y[AI_CANDIDATE_MAX_TILES]; + short back_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + short back_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + short forward_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + short forward_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; int back_count = 0; int forward_count = 0; int remaining = effective_limit - 1; @@ -10646,11 +10642,7 @@ find_bridge_candidate_in_block (Map * map, int block_x0, int block_y0, int block { 0, -2 }, { -2, 0 }, { -1, -1 }, { -1, 1 } }; - int max_len = contiguous_limit; - if (max_len <= 0) - max_len = 1; - if (max_len > AI_CANDIDATE_MAX_TILES) - max_len = AI_CANDIDATE_MAX_TILES; + int max_len = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, contiguous_limit); if (candidate_capacity > 0 && max_len > candidate_capacity) max_len = candidate_capacity; @@ -10774,21 +10766,17 @@ gather_canal_line (int start_x, int start_y, int dx, int dy, int limit, int block_x0, int block_y0, int block_x1, int block_y1, short * out_x, short * out_y) { - int effective_limit = limit; - if (effective_limit <= 0) - effective_limit = 1; - if (effective_limit > AI_CANDIDATE_MAX_TILES) - effective_limit = AI_CANDIDATE_MAX_TILES; + int effective_limit = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, limit); if (! tile_is_land (-1, start_x, start_y, false)) return 0; if (tile_part_of_existing_candidate (start_x, start_y)) return 0; - short back_x[AI_CANDIDATE_MAX_TILES]; - short back_y[AI_CANDIDATE_MAX_TILES]; - short forward_x[AI_CANDIDATE_MAX_TILES]; - short forward_y[AI_CANDIDATE_MAX_TILES]; + short back_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + short back_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + short forward_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + short forward_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; int back_count = 0; int forward_count = 0; int remaining = effective_limit - 1; @@ -10872,11 +10860,7 @@ find_canal_candidate_in_block (Map * map, int block_x0, int block_y0, int block_ const int dir_dx[8] = { 0, 1, 2, 1, 0, -1, -2, -1 }; const int dir_dy[8] = { -2, -1, 0, 1, 2, 1, 0, -1 }; - int max_len = contiguous_limit; - if (max_len <= 0) - max_len = 1; - if (max_len > AI_CANDIDATE_MAX_TILES) - max_len = AI_CANDIDATE_MAX_TILES; + int max_len = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, contiguous_limit); if (candidate_capacity > 0 && max_len > candidate_capacity) max_len = candidate_capacity; @@ -10924,8 +10908,8 @@ find_canal_candidate_in_block (Map * map, int block_x0, int block_y0, int block_ short owner = start_tile->vtable->m38_Get_Territory_OwnerID (start_tile); int stack_len = 1; - int dir_stack[AI_CANDIDATE_MAX_TILES]; - int path_dir[AI_CANDIDATE_MAX_TILES]; + int dir_stack[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + int path_dir[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; out_x[0] = (short)start_x; out_y[0] = (short)start_y; dir_stack[0] = -1; @@ -10981,8 +10965,8 @@ find_canal_candidate_in_block (Map * map, int block_x0, int block_y0, int block_ if (buildable) { // Collect adjacent land tiles for component checks - int adj_x[AI_CANDIDATE_MAX_TILES * 8]; - int adj_y[AI_CANDIDATE_MAX_TILES * 8]; + int adj_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES * 8]; + int adj_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES * 8]; int adj_count = 0; for (int pi = 0; pi < length; pi++) { int px = out_x[pi]; @@ -11210,11 +11194,7 @@ generate_ai_bridge_candidates_by_block (Map * map, int block_size, int contiguou if (block_size < 1) block_size = 1; - int candidate_capacity = contiguous_limit; - if (candidate_capacity <= 0) - candidate_capacity = 1; - if (candidate_capacity > AI_CANDIDATE_MAX_TILES) - candidate_capacity = AI_CANDIDATE_MAX_TILES; + int candidate_capacity = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, contiguous_limit); short * candidate_x = (short *)malloc (sizeof *candidate_x * candidate_capacity); short * candidate_y = (short *)malloc (sizeof *candidate_y * candidate_capacity); @@ -11267,11 +11247,7 @@ generate_ai_canal_candidates_by_block (Map * map, int block_size, int contiguous if (block_size < 1) block_size = 1; - int candidate_capacity = contiguous_limit; - if (candidate_capacity <= 0) - candidate_capacity = 1; - if (candidate_capacity > AI_CANDIDATE_MAX_TILES) - candidate_capacity = AI_CANDIDATE_MAX_TILES; + int candidate_capacity = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, contiguous_limit); short * candidate_x = (short *)malloc (sizeof *candidate_x * candidate_capacity); short * candidate_y = (short *)malloc (sizeof *candidate_y * candidate_capacity); @@ -11522,9 +11498,6 @@ canal_district_tile_is_valid (int tile_x, int tile_y) return false; } - if (! district_line_is_straight (tile_x, tile_y, CANAL_DISTRICT_ID)) - return false; - Map * map = &p_bic_data->Map; int const adj_dx[8] = { 0, 0, -2, 2, 1, 1, -1, -1 }; int const adj_dy[8] = { -2, 2, 0, 0, -1, 1, -1, 1 }; From c71aaa31b2ea04027c49fad907055383afd8b683 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 25 Jan 2026 09:46:56 -0800 Subject: [PATCH 261/356] Add ser/deser processes for great wall state, AI candidate bridges and canals; Fixed water_dir not getting carried over bug in canal sprite drawing --- C3X.h | 8 +- injected_code.c | 574 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 389 insertions(+), 193 deletions(-) diff --git a/C3X.h b/C3X.h index 7d6ea669..f80b5166 100644 --- a/C3X.h +++ b/C3X.h @@ -1153,11 +1153,13 @@ enum district_state { struct district_instance { enum district_state state; - int district_type; // Index into district_configs array + int district_id; // Index into district_configs array int tile_x; int tile_y; - struct wonder_district_info wonder_info; // Only used if district_type is a wonder district - struct natural_wonder_district_info natural_wonder_info; // Only used if district_type is a natural wonder district + int built_by_civ_id; + int completed_turn; + struct wonder_district_info wonder_info; // Only used if district_id is a wonder district + struct natural_wonder_district_info natural_wonder_info; // Only used if district_id is a natural wonder district }; enum extra_resource_tile_type { diff --git a/injected_code.c b/injected_code.c index 0e43687b..60497f3b 100644 --- a/injected_code.c +++ b/injected_code.c @@ -545,7 +545,7 @@ patch_City_controls_tile (City * this, int edx, int neighbor_index, bool conside if (is->current_config.enable_districts || is->current_config.enable_natural_wonders) { // Check if the tile itself is a completed district (includes natural wonders) struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && district_is_complete (tile, inst->district_type)) + if (inst != NULL && district_is_complete (tile, inst->district_id)) return false; // Check if the tile is covered by a distribution hub @@ -1912,7 +1912,7 @@ district_is_buildable_on_square_type (struct district_config const * cfg, Tile * if (inst == NULL) return false; - int existing_district_id = inst->district_type; + int existing_district_id = inst->district_id; if (! district_is_complete (tile, existing_district_id)) return false; @@ -2607,7 +2607,7 @@ get_district_instance (Tile * tile) struct district_instance * inst = (struct district_instance *)(long)stored_ptr; - if ((inst == NULL) || (inst->district_type < 0) || (inst->district_type >= is->district_count)) + if ((inst == NULL) || (inst->district_id < 0) || (inst->district_id >= is->district_count)) return NULL; return inst; @@ -2652,7 +2652,7 @@ district_instance_set_coords (struct district_instance * inst, int tile_x, int t } struct district_instance * -ensure_district_instance (Tile * tile, int district_type, int tile_x, int tile_y) +ensure_district_instance (Tile * tile, int district_id, int tile_x, int tile_y) { if (tile == NULL || tile == p_null_tile) return NULL; @@ -2667,7 +2667,9 @@ ensure_district_instance (Tile * tile, int district_type, int tile_x, int tile_y return NULL; inst->state = DS_UNDER_CONSTRUCTION; - inst->district_type = district_type; + inst->district_id = district_id; + inst->built_by_civ_id = -1; + inst->completed_turn = -1; // Initialize wonder_info (only relevant for wonder districts) inst->wonder_info.state = WDS_UNUSED; @@ -2735,7 +2737,7 @@ assign_natural_wonder_to_tile (Tile * tile, int tile_x, int tile_y, int natural_ if (inst == NULL) return; - inst->district_type = NATURAL_WONDER_DISTRICT_ID; + inst->district_id = NATURAL_WONDER_DISTRICT_ID; inst->state = DS_COMPLETED; inst->natural_wonder_info.natural_wonder_id = natural_wonder_id; set_tile_unworkable_for_all_cities (tile, tile_x, tile_y); @@ -2791,7 +2793,7 @@ get_effective_district_yields (struct district_instance * inst, culture = cfg->culture_bonus; happiness = cfg->happiness_bonus; - int district_id = (inst != NULL) ? inst->district_type : -1; + int district_id = (inst != NULL) ? inst->district_id : -1; food += apply_district_bonus_entries (inst, &cfg->food_bonus_extras, district_id); shields += apply_district_bonus_entries (inst, &cfg->shield_bonus_extras, district_id); gold += apply_district_bonus_entries (inst, &cfg->gold_bonus_extras, district_id); @@ -2800,7 +2802,7 @@ get_effective_district_yields (struct district_instance * inst, happiness += apply_district_bonus_entries (inst, &cfg->happiness_bonus_extras, district_id); } - if (inst != NULL && is->current_config.enable_natural_wonders && inst->district_type == NATURAL_WONDER_DISTRICT_ID) { + if (inst != NULL && is->current_config.enable_natural_wonders && inst->district_id == NATURAL_WONDER_DISTRICT_ID) { struct natural_wonder_district_config const * nwcfg = get_natural_wonder_config_by_id (inst->natural_wonder_info.natural_wonder_id); if (nwcfg != NULL) { food += nwcfg->food_bonus; @@ -2922,7 +2924,7 @@ natural_wonder_exists_within_distance (int tile_x, int tile_y, int min_distance) Tile * here = tile_at (tile_x, tile_y); if ((here != NULL) && (here != p_null_tile)) { struct district_instance * inst = get_district_instance (here); - if ((inst != NULL) && (inst->district_type == NATURAL_WONDER_DISTRICT_ID)) + if ((inst != NULL) && (inst->district_id == NATURAL_WONDER_DISTRICT_ID)) return true; } @@ -2951,7 +2953,7 @@ natural_wonder_exists_within_distance (int tile_x, int tile_y, int min_distance) if ((tile == NULL) || (tile == p_null_tile)) continue; struct district_instance * inst = get_district_instance (tile); - if ((inst != NULL) && (inst->district_type == NATURAL_WONDER_DISTRICT_ID)) + if ((inst != NULL) && (inst->district_id == NATURAL_WONDER_DISTRICT_ID)) return true; } } @@ -3004,7 +3006,7 @@ remove_pending_district_request (struct pending_district_request * req) if ((tile != NULL) && (tile != p_null_tile)) { struct district_instance * inst = get_district_instance (tile); if ((inst != NULL) && - (inst->district_type == req->district_id) && + (inst->district_id == req->district_id) && (inst->state != DS_COMPLETED)) remove_district_instance (tile); } @@ -3473,7 +3475,7 @@ wai_next (struct work_area_iter * wai) if (wai->output_type == WAIO_DISTRICTS) { if (inst == NULL) continue; - int district_id = inst->district_type; + int district_id = inst->district_id; if (wai->completed_districts_only && (! district_is_complete (candidate, district_id))) continue; } @@ -3542,7 +3544,7 @@ wai_init_cities (int x, int y) aerodrome_tile = NULL) \ for (struct district_instance * aerodrome_inst = (struct district_instance *)(long)_tei.value; \ (aerodrome_inst != NULL) && \ - (aerodrome_inst->district_type == AERODROME_DISTRICT_ID) && \ + (aerodrome_inst->district_id == AERODROME_DISTRICT_ID) && \ district_is_complete (aerodrome_tile, AERODROME_DISTRICT_ID); \ aerodrome_inst = NULL) \ for (int aerodrome_x = 0, aerodrome_y = 0; \ @@ -4172,8 +4174,6 @@ assign_workers_for_pending_districts (Leader * leader) process_pending_district_request (leader, req); } - if (is->current_config.enable_canal_districts || is->current_config.enable_bridge_districts) - assign_workers_for_ai_candidate_bridge_or_canals (leader); } City * @@ -4214,19 +4214,18 @@ ai_candidate_bridge_or_canal_is_buildable_for_civ (struct ai_candidate_bridge_or int owner = tile->vtable->m38_Get_Territory_OwnerID (tile); if (owner != civ_id) return false; + if (tile->CityID >= 0) + return false; struct district_instance * inst = get_district_instance (tile); if (inst != NULL) { - if ((inst->district_type == entry->district_id) && - district_is_complete (tile, entry->district_id)) + if (inst->district_id == entry->district_id && district_is_complete (tile, entry->district_id)) continue; - if ((inst->district_type != entry->district_id) && - district_is_complete (tile, inst->district_type)) { + if (inst->district_id != entry->district_id && district_is_complete (tile, inst->district_id)) { entry->completed = true; return false; } - if ((inst->district_type != entry->district_id) && - ! district_is_complete (tile, inst->district_type)) + if (inst->district_id != entry->district_id && ! district_is_complete (tile, inst->district_id)) return false; } @@ -4274,8 +4273,6 @@ assign_workers_for_ai_candidate_bridge_or_canals (Leader * leader) return; int civ_id = leader->ID; - if ((civ_id < 0) || (civ_id >= 32)) - return; if ((*p_human_player_bits & (1 << civ_id)) != 0) return; @@ -4285,12 +4282,9 @@ assign_workers_for_ai_candidate_bridge_or_canals (Leader * leader) continue; int district_id = entry->district_id; - if ((district_id == CANAL_DISTRICT_ID) && (! is->current_config.enable_canal_districts)) - continue; - if ((district_id == BRIDGE_DISTRICT_ID) && (! is->current_config.enable_bridge_districts)) - continue; - if (! leader_can_build_district (leader, district_id)) - continue; + if ((district_id == CANAL_DISTRICT_ID) && (! is->current_config.enable_canal_districts)) continue; + if ((district_id == BRIDGE_DISTRICT_ID) && (! is->current_config.enable_bridge_districts)) continue; + if (! leader_can_build_district (leader, district_id)) continue; if (entry->assigned_worker_id >= 0) { Unit * worker = get_unit_ptr (entry->assigned_worker_id); @@ -4303,9 +4297,7 @@ assign_workers_for_ai_candidate_bridge_or_canals (Leader * leader) Tile * tile = tile_at (tx, ty); if ((tile != NULL) && (tile != p_null_tile)) { struct district_instance * inst = get_district_instance (tile); - if ((inst != NULL) && - (inst->district_type == district_id) && - district_is_complete (tile, district_id)) { + if (inst != NULL && inst->district_id == district_id && district_is_complete (tile, district_id)) { release_ai_candidate_bridge_or_canal_worker (entry); } } @@ -4514,7 +4506,7 @@ district_is_complete(Tile * tile, int district_id) return false; struct district_instance * inst = get_district_instance (tile); - if (inst == NULL || inst->district_type != district_id) + if (inst == NULL || inst->district_id != district_id) return false; // If already marked COMPLETED, just return true @@ -4543,9 +4535,12 @@ district_is_complete(Tile * tile, int district_id) // Mark as completed and run one-time side effects inst->state = DS_COMPLETED; + inst->completed_turn = *p_current_turn_no; + int tile_x, tile_y; if (district_instance_get_coords (inst, tile, &tile_x, &tile_y)) { set_tile_unworkable_for_all_cities (tile, tile_x, tile_y); + int territory_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); if (cfg->auto_add_road) { bool has_road = tile->vtable->m25_Check_Roads (tile, __, 0) != 0; @@ -4556,7 +4551,6 @@ district_is_complete(Tile * tile, int district_id) if (cfg->auto_add_railroad) { bool has_railroad = tile->vtable->m23_Check_Railroads (tile, __, 0) != 0; if (! has_railroad) { - int territory_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); if ((territory_owner >= 0) && Leader_can_do_worker_job (&leaders[territory_owner], __, WJ_Build_Railroad, tile_x, tile_y, 0)) { tile->vtable->m56_Set_Tile_Flags (tile, __, 0, TILE_FLAG_RAILROAD, tile_x, tile_y); } @@ -5223,7 +5217,7 @@ refresh_distribution_hubs_for_city (City * city) int tx = wai.tile_x, ty = wai.tile_y; Tile * tile = wai.tile; struct district_instance * inst = wai.district_inst; - if (inst->district_type != DISTRIBUTION_HUB_DISTRICT_ID) + if (inst->district_id != DISTRIBUTION_HUB_DISTRICT_ID) continue; on_distribution_hub_completed (tile, tx, ty); } @@ -9068,7 +9062,7 @@ place_natural_wonders_on_map (void) // Record existing natural wonders FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { struct district_instance * inst = (struct district_instance *)(long)tei.value; - if ((inst == NULL) || (inst->district_type != NATURAL_WONDER_DISTRICT_ID)) + if ((inst == NULL) || (inst->district_id != NATURAL_WONDER_DISTRICT_ID)) continue; int wonder_id = inst->natural_wonder_info.natural_wonder_id; @@ -9450,7 +9444,7 @@ finalize_scenario_district_entry (struct scenario_district_entry * entry, add_scenario_district_error (parse_errors, section_start_line, "Failed to create district instance"); success = 0; } else { - inst->district_type = district_id; + inst->district_id = district_id; district_instance_set_coords (inst, map_x, map_y); inst->state = DS_COMPLETED; inst->wonder_info.state = WDS_UNUSED; @@ -9524,7 +9518,7 @@ tile_can_be_named (Tile * tile, int tile_x, int tile_y) if ((tile == NULL) || (tile == p_null_tile)) return false; struct district_instance * inst = get_district_instance (tile); - if ((inst != NULL) && (inst->district_type == NATURAL_WONDER_DISTRICT_ID)) + if ((inst != NULL) && (inst->district_id == NATURAL_WONDER_DISTRICT_ID)) return false; return true; } @@ -10087,7 +10081,7 @@ tile_has_district_at (int tile_x, int tile_y, int district_id) return false; struct district_instance * inst = get_district_instance (tile); - return (inst != NULL) && (inst->district_type == district_id) && (district_is_complete (tile, district_id)); + return (inst != NULL) && (inst->district_id == district_id) && (district_is_complete (tile, district_id)); } bool @@ -10296,7 +10290,7 @@ canal_has_same_sea_isthmus (int tile_x, int tile_y, int civ_id, int check_radius if ((adj == NULL) || (adj == p_null_tile)) continue; struct district_instance * adj_inst = get_district_instance (adj); - if ((adj_inst != NULL) && (adj_inst->district_type == CANAL_DISTRICT_ID)) + if ((adj_inst != NULL) && (adj_inst->district_id == CANAL_DISTRICT_ID)) return false; } @@ -11514,7 +11508,7 @@ canal_district_tile_is_valid (int tile_x, int tile_y) if (tile_has_district_at (nx, ny, CANAL_DISTRICT_ID)) { struct district_instance * inst = get_district_instance (tile); - if ((inst != NULL) && (inst->district_type == CANAL_DISTRICT_ID) && + if ((inst != NULL) && (inst->district_id == CANAL_DISTRICT_ID) && district_is_complete (tile, CANAL_DISTRICT_ID)) return true; } @@ -11567,7 +11561,7 @@ can_build_district_on_tile (Tile * tile, int district_id, int civ_id) struct district_instance * existing_inst = get_district_instance (tile); struct district_infos const * info = &is->district_infos[district_id]; - int existing_district_id = (existing_inst != NULL) ? existing_inst->district_type : -1; + int existing_district_id = (existing_inst != NULL) ? existing_inst->district_id : -1; bool district_completed = district_is_complete (tile, existing_district_id); if ((existing_district_id == district_id) && district_completed) @@ -11577,7 +11571,7 @@ can_build_district_on_tile (Tile * tile, int district_id, int civ_id) if (! cfg->allow_multiple) { FOR_DISTRICTS_AROUND (wai, tile_x, tile_y, false) { - if (wai.district_inst->district_type == district_id) + if (wai.district_inst->district_id == district_id) return false; } } @@ -11654,7 +11648,7 @@ calculate_city_center_district_bonus (City * city, int * out_food, int * out_shi Tile * tile = wai.tile; struct district_instance * inst = wai.district_inst; - int district_id = inst->district_type; + int district_id = inst->district_id; if (is->current_config.enable_neighborhood_districts && (district_id == NEIGHBORHOOD_DISTRICT_ID)) { @@ -11696,7 +11690,7 @@ patch_Map_calc_food_yield_at (Map * this, int edx, int tile_x, int tile_y, int t Tile * tile = tile_at (tile_x, tile_y); if ((tile != NULL) && (tile != p_null_tile)) { struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && district_is_complete (tile, inst->district_type)) { + if (inst != NULL && district_is_complete (tile, inst->district_id)) { return 0; } } @@ -11713,7 +11707,7 @@ patch_Map_calc_shield_yield_at (Map * this, int edx, int tile_x, int tile_y, int Tile * tile = tile_at (tile_x, tile_y); if ((tile != NULL) && (tile != p_null_tile)) { struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && district_is_complete (tile, inst->district_type)) { + if (inst != NULL && district_is_complete (tile, inst->district_id)) { return 0; } } @@ -11790,7 +11784,7 @@ get_water_continent_ids_connected_via_canal (Tile * tile, int * out_count) continue; struct district_instance * inst = get_district_instance (cand); - if ((inst == NULL) || (inst->district_type != CANAL_DISTRICT_ID) || + if ((inst == NULL) || (inst->district_id != CANAL_DISTRICT_ID) || (! district_is_complete (cand, CANAL_DISTRICT_ID))) continue; @@ -11868,7 +11862,7 @@ get_water_continent_ids_connected_via_canal (Tile * tile, int * out_count) continue; struct district_instance * inst = get_district_instance (adj); - if ((inst == NULL) || (inst->district_type != CANAL_DISTRICT_ID) || (! district_is_complete (adj, CANAL_DISTRICT_ID))) + if ((inst == NULL) || (inst->district_id != CANAL_DISTRICT_ID) || (! district_is_complete (adj, CANAL_DISTRICT_ID))) continue; bool seen_canal = false; @@ -12197,7 +12191,7 @@ find_tile_for_distribution_hub_district (City * city, int * out_x, int * out_y) if ((nearby_tile != NULL) && (nearby_tile != p_null_tile)) { struct district_instance * nearby_inst = get_district_instance (nearby_tile); if ((nearby_inst != NULL) && - (nearby_inst->district_type == DISTRIBUTION_HUB_DISTRICT_ID) && + (nearby_inst->district_id == DISTRIBUTION_HUB_DISTRICT_ID) && district_is_complete (nearby_tile, DISTRIBUTION_HUB_DISTRICT_ID)) { too_close_to_existing_hub_or_city = true; break; @@ -12312,7 +12306,7 @@ leader_has_natural_wonder_prereq_in_territory (int civ_id, struct district_infos FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { struct district_instance * inst = (struct district_instance *)(long)tei.value; - if ((inst == NULL) || (inst->district_type != NATURAL_WONDER_DISTRICT_ID)) + if ((inst == NULL) || (inst->district_id != NATURAL_WONDER_DISTRICT_ID)) continue; int wonder_id = inst->natural_wonder_info.natural_wonder_id; @@ -12614,7 +12608,7 @@ get_completed_district_tile_for_city (City * city, int district_id, int * out_x, Tile * candidate = wai.tile; struct district_instance * inst = wai.district_inst; - if (inst->district_type != district_id) + if (inst->district_id != district_id) continue; // For wonder districts, filter based on wonder_district_state @@ -12651,7 +12645,7 @@ tile_has_friendly_aerodrome_district (Tile * tile, int civ_id, bool require_avai return false; struct district_instance * inst = get_district_instance (tile); - if (inst == NULL || inst->district_type != AERODROME_DISTRICT_ID) + if (inst == NULL || inst->district_id != AERODROME_DISTRICT_ID) return false; if (! district_is_complete (tile, AERODROME_DISTRICT_ID)) @@ -12680,7 +12674,7 @@ tile_has_friendly_port_district (Tile * tile, int civ_id) return false; struct district_instance * inst = get_district_instance (tile); - if ((inst == NULL) || (inst->district_type != PORT_DISTRICT_ID)) + if ((inst == NULL) || (inst->district_id != PORT_DISTRICT_ID)) return false; if (! district_is_complete (tile, PORT_DISTRICT_ID)) @@ -12711,7 +12705,7 @@ city_has_wonder_district_with_no_completed_wonder (City * city, int wonder_impro FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { Tile * candidate = wai.tile; struct district_instance * inst = wai.district_inst; - if (inst->district_type != WONDER_DISTRICT_ID) continue; + if (inst->district_id != WONDER_DISTRICT_ID) continue; struct wonder_district_info * info = get_wonder_district_info (candidate); bool buildable = wonder_is_buildable_on_tile (candidate, wonder_improv_id); @@ -12805,8 +12799,8 @@ count_neighborhoods_in_city_radius (City * city) FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { struct district_instance * inst = wai.district_inst; if (inst != NULL && - inst->district_type >= 0 && inst->district_type < is->district_count && - inst->district_type == NEIGHBORHOOD_DISTRICT_ID) + inst->district_id >= 0 && inst->district_id < is->district_count && + inst->district_id == NEIGHBORHOOD_DISTRICT_ID) count++; } return count; @@ -12915,7 +12909,7 @@ calculate_district_culture_science_bonuses (City * city, int * culture_bonus, in continue; Tile * tile = wai.tile; struct district_instance * inst = wai.district_inst; - int district_id = inst->district_type; + int district_id = inst->district_id; struct district_config const * cfg = &is->district_configs[district_id]; int district_culture_bonus = 0; @@ -12958,7 +12952,7 @@ calculate_district_happiness_bonus (City * city, int * happiness_bonus) continue; struct district_instance * inst = wai.district_inst; - int district_id = inst->district_type; + int district_id = inst->district_id; struct district_config const * cfg = &is->district_configs[district_id]; bool is_neighborhood = district_id == NEIGHBORHOOD_DISTRICT_ID; @@ -13128,7 +13122,7 @@ city_has_other_completed_district (City * city, int district_id, int removed_x, continue; struct district_instance * inst = wai.district_inst; - if (inst->district_type != district_id) + if (inst->district_id != district_id) continue; return true; @@ -13145,7 +13139,7 @@ district_instance_is_redundant (struct district_instance * inst, Tile * tile) (tile == NULL) || (tile == p_null_tile)) return false; - int district_id = inst->district_type; + int district_id = inst->district_id; if ((district_id < 0) || (district_id >= is->district_count)) return false; @@ -13310,7 +13304,7 @@ handle_district_removed (Tile * tile, int district_id, int center_x, int center_ if (actual_district_id < 0) { struct district_instance * inst = get_district_instance (tile); if (inst != NULL) - actual_district_id = inst->district_type; + actual_district_id = inst->district_id; } remove_district_instance (tile); @@ -13377,7 +13371,7 @@ city_requires_district_for_improvement (City * city, int improv_id, int * out_di if (requires_river) { bool has_river_wonder_district = false; FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { - if (wai.district_inst->district_type != WONDER_DISTRICT_ID) + if (wai.district_inst->district_id != WONDER_DISTRICT_ID) continue; if (wai.tile->vtable->m37_Get_River_Code (wai.tile) == 0) continue; @@ -13407,7 +13401,7 @@ city_requires_district_for_improvement (City * city, int improv_id, int * out_di if (requires_river) { bool has_river_district = false; FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { - if (wai.district_inst->district_type != district_id) + if (wai.district_inst->district_id != district_id) continue; if (wai.tile->vtable->m37_Get_River_Code (wai.tile) != 0) { has_river_district = true; @@ -13488,7 +13482,7 @@ wonder_district_tile_under_construction (Tile * tile, int tile_x, int tile_y, in return false; struct district_instance * inst = get_district_instance (tile); - if (inst == NULL || inst->district_type != WONDER_DISTRICT_ID) + if (inst == NULL || inst->district_id != WONDER_DISTRICT_ID) return false; struct wonder_district_info * info = get_wonder_district_info (tile); @@ -13554,7 +13548,7 @@ city_has_assigned_wonder_district (City * city, Tile * ignore_tile, int wonder_i continue; struct district_instance * inst = wai.district_inst; - if (inst->district_type != WONDER_DISTRICT_ID) + if (inst->district_id != WONDER_DISTRICT_ID) continue; struct wonder_district_info * info = inst ? &inst->wonder_info : NULL; @@ -13590,7 +13584,7 @@ free_wonder_district_for_city (City * city) int x = wai.tile_x, y = wai.tile_y; Tile * tile = wai.tile; struct district_instance * inst = wai.district_inst; - if (inst->district_type != WONDER_DISTRICT_ID) + if (inst->district_id != WONDER_DISTRICT_ID) continue; struct wonder_district_info * info = get_wonder_district_info (tile); @@ -13622,7 +13616,7 @@ reserve_wonder_district_for_city (City * city, int wonder_improv_id) int x = wai.tile_x, y = wai.tile_y; Tile * candidate = wai.tile; struct district_instance * inst = wai.district_inst; - if (inst->district_type != WONDER_DISTRICT_ID) continue; + if (inst->district_id != WONDER_DISTRICT_ID) continue; if ((wonder_improv_id >= 0) && (! wonder_is_buildable_on_tile (candidate, wonder_improv_id))) continue; @@ -13658,7 +13652,7 @@ release_wonder_district_reservation (City * city) // Find and remove any reservations for this city FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, false) { struct district_instance * inst = wai.district_inst; - if (inst->district_type != WONDER_DISTRICT_ID) + if (inst->district_id != WONDER_DISTRICT_ID) continue; struct wonder_district_info * info = &inst->wonder_info; @@ -13682,7 +13676,7 @@ handle_district_destroyed_by_attack (Tile * tile, int tile_x, int tile_y, bool l struct district_instance * inst = get_district_instance (tile); if (inst != NULL) { - int district_id = inst->district_type; + int district_id = inst->district_id; // If this is a Wonder District with a completed wonder and wonders can't be destroyed, restore overlay and keep district if (is->current_config.enable_wonder_districts) { @@ -13982,7 +13976,7 @@ patch_City_has_resource (City * this, int edx, int resource_id) if ((! tr) && is->current_config.enable_districts) { FOR_DISTRICTS_AROUND (wai, this->Body.X, this->Body.Y, true) { struct district_instance * di = wai.district_inst; - int district_id = di->district_type; + int district_id = di->district_id; if ((district_id < 0) || (district_id >= is->district_count)) continue; @@ -14215,7 +14209,7 @@ patch_Trade_Net_recompute_resources (Trade_Net * this, int edx, bool skip_popups if ((district_tile == NULL) || (district_tile == p_null_tile) || (inst == NULL) || (inst->state != DS_COMPLETED)) continue; - int district_id = inst->district_type; + int district_id = inst->district_id; if ((district_id < 0) || (district_id >= is->district_count)) continue; @@ -16795,7 +16789,7 @@ patch_Unit_bombard_tile (Unit * this, int edx, int x, int y) is->bombard_stealth_target = NULL; is->bombarding_unit = NULL; - if (had_district_before && target_tile != NULL && target_tile != p_null_tile && inst->district_type != NATURAL_WONDER_DISTRICT_ID) { + if (had_district_before && target_tile != NULL && target_tile != p_null_tile && inst->district_id != NATURAL_WONDER_DISTRICT_ID) { unsigned int overlays = target_tile->vtable->m42_Get_Overlays (target_tile, __, 0); if ((overlays & TILE_FLAG_MINE) == 0) handle_district_destroyed_by_attack (target_tile, tile_x, tile_y, false); @@ -17297,8 +17291,8 @@ set_up_district_buttons (Main_GUI * this) int existing_district_id = -1; struct district_instance * existing_inst = get_district_instance (tile); if (existing_inst != NULL) { - existing_district_id = existing_inst->district_type; - if (is->current_config.enable_natural_wonders && existing_inst->district_type == NATURAL_WONDER_DISTRICT_ID) { + existing_district_id = existing_inst->district_id; + if (is->current_config.enable_natural_wonders && existing_inst->district_id == NATURAL_WONDER_DISTRICT_ID) { return; } if (patch_Unit_can_perform_command(selected_unit, __, UCV_Build_Mine)) { @@ -17737,14 +17731,14 @@ handle_worker_command_that_may_replace_district (Unit * unit, int unit_command_v return true; struct district_instance * inst = get_district_instance (tile); - if ((inst == NULL) || (! district_is_complete (tile, inst->district_type))) + if ((inst == NULL) || (! district_is_complete (tile, inst->district_id))) return true; int tile_x, tile_y; if (! district_instance_get_coords (inst, tile, &tile_x, &tile_y)) return false; - int district_id = inst->district_type; + int district_id = inst->district_id; int civ_id = unit->Body.CivID; bool redundant_district = district_instance_is_redundant (inst, tile); bool would_lose_buildings = any_nearby_city_would_lose_district_benefits (district_id, civ_id, tile_x, tile_y); @@ -17847,7 +17841,7 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) if ((tile != NULL) && (tile != p_null_tile) && is->current_config.enable_natural_wonders) { struct district_instance * inst = get_district_instance (tile); if ((inst != NULL) && - (inst->district_type == NATURAL_WONDER_DISTRICT_ID) && + (inst->district_id == NATURAL_WONDER_DISTRICT_ID) && (inst->natural_wonder_info.natural_wonder_id >= 0)) { if (is_worker_or_settler_command (unit_command_value) || is_district_command (unit_command_value)) return false; @@ -17980,7 +17974,7 @@ patch_Unit_can_pillage (Unit * this, int edx, int tile_x, int tile_y) if (inst == NULL) return base; - int district_id = inst->district_type; + int district_id = inst->district_id; if (is->current_config.enable_natural_wonders && (district_id == NATURAL_WONDER_DISTRICT_ID) && (inst->natural_wonder_info.natural_wonder_id >= 0)) @@ -18121,8 +18115,8 @@ issue_district_worker_command (Unit * unit, int command) // If District will be replaced by another District struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && district_is_complete(tile, inst->district_type)) { - int existing_district_id = inst->district_type; + if (inst != NULL && district_is_complete(tile, inst->district_id)) { + int existing_district_id = inst->district_id; int inst_x, inst_y; if (! district_instance_get_coords (inst, tile, &inst_x, &inst_y)) return; @@ -18178,6 +18172,7 @@ issue_district_worker_command (Unit * unit, int command) tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, tile_x, tile_y); inst = ensure_district_instance (tile, district_id, tile_x, tile_y); + inst->built_by_civ_id = unit->Body.CivID; if (inst != NULL) inst->state = DS_UNDER_CONSTRUCTION; @@ -18670,7 +18665,7 @@ patch_City_Form_draw (City_Form * this) if ((wai.dx == 0) && (wai.dy == 0)) continue; Tile * tile = wai.tile; struct district_instance * inst = wai.district_inst; - int district_id = inst->district_type; + int district_id = inst->district_id; struct district_config const * cfg = &is->district_configs[district_id]; int gold_bonus = 0; @@ -18881,7 +18876,7 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { if ((dest != NULL) && (dest != p_null_tile)) { struct district_instance * inst = get_district_instance (dest); - if ((inst != NULL) && (inst->district_type == BRIDGE_DISTRICT_ID) && district_is_complete (dest, inst->district_type)) { + if ((inst != NULL) && (inst->district_id == BRIDGE_DISTRICT_ID) && district_is_complete (dest, inst->district_id)) { base_validity = AMV_OK; } } @@ -18893,7 +18888,7 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, ((base_validity == AMV_INVALID_SEA_MOVE) || (base_validity == AMV_CANNOT_EMBARK))) { if ((dest != NULL) && (dest != p_null_tile)) { struct district_instance * inst = get_district_instance (dest); - if ((inst != NULL) && (inst->district_type == CANAL_DISTRICT_ID) && district_is_complete (dest, inst->district_type)) { + if ((inst != NULL) && (inst->district_id == CANAL_DISTRICT_ID) && district_is_complete (dest, inst->district_id)) { base_validity = AMV_OK; } } @@ -18952,7 +18947,7 @@ great_wall_blocks_civ (Tile * tile, int civ_id) return false; struct district_instance * inst = get_district_instance (tile); - if ((inst == NULL) || (inst->district_type != GREAT_WALL_DISTRICT_ID)) + if ((inst == NULL) || (inst->district_id != GREAT_WALL_DISTRICT_ID)) return false; if (! district_is_complete (tile, GREAT_WALL_DISTRICT_ID)) @@ -18997,8 +18992,8 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr if ((dest != NULL) && (dest != p_null_tile)) { struct district_instance * inst = get_district_instance (dest); if ((inst != NULL) && - (inst->district_type == BRIDGE_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) { + (inst->district_id == BRIDGE_DISTRICT_ID) && + district_is_complete (dest, inst->district_id)) { base_cost = Unit_get_max_move_points (unit); } } @@ -19013,8 +19008,8 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr if ((dest != NULL) && (dest != p_null_tile)) { struct district_instance * inst = get_district_instance (dest); if ((inst != NULL) && - (inst->district_type == CANAL_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) { + (inst->district_id == CANAL_DISTRICT_ID) && + district_is_complete (dest, inst->district_id)) { base_cost = Unit_get_max_move_points (unit); } } @@ -19618,8 +19613,8 @@ patch_Leader_can_do_worker_job (Leader * this, int edx, enum Worker_Jobs job, in Tile * tile = tile_at (tile_x, tile_y); if ((tile != NULL) && (tile != p_null_tile)) { struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && inst->district_type >= 0 && inst->district_type < is->district_count) { - int district_id = inst->district_type; + if (inst != NULL && inst->district_id >= 0 && inst->district_id < is->district_count) { + int district_id = inst->district_id; // For Wonder Districts: check if unused (can be replaced) if (is->current_config.enable_wonder_districts && (district_id == WONDER_DISTRICT_ID)) { @@ -19661,7 +19656,7 @@ patch_Leader_can_do_worker_job (Leader * this, int edx, enum Worker_Jobs job, in if ((tile != NULL) && (tile != p_null_tile)) { struct district_instance * inst = get_district_instance (tile); if (inst != NULL && - inst->district_type >= 0 && inst->district_type < is->district_count && + inst->district_id >= 0 && inst->district_id < is->district_count && ! tile->vtable->m35_Check_Is_Water (tile) && (tile->CityID < 0) && ! tile->vtable->m18_Check_Mines (tile, __, 0)) { @@ -19669,8 +19664,8 @@ patch_Leader_can_do_worker_job (Leader * this, int edx, enum Worker_Jobs job, in tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y); int owner_civ = tile->vtable->m38_Get_Territory_OwnerID (tile); if ((owner_civ == this->ID) || (owner_civ == 0)) { - if (leader_can_build_district (this, inst->district_type) && - district_resource_prereqs_met (tile, tile_x, tile_y, inst->district_type, NULL)) + if (leader_can_build_district (this, inst->district_id) && + district_resource_prereqs_met (tile, tile_x, tile_y, inst->district_id, NULL)) tr = 1; } } @@ -20824,7 +20819,7 @@ patch_Map_check_city_location (Map *this, int edx, int tile_x, int tile_y, int c Tile * tile = tile_at (tile_x, tile_y); if ((tile != NULL) && (tile != p_null_tile)) { struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && (inst->district_type == NATURAL_WONDER_DISTRICT_ID)) { + if (inst != NULL && (inst->district_id == NATURAL_WONDER_DISTRICT_ID)) { return CLV_BLOCKED; } } @@ -21447,12 +21442,12 @@ patch_PCX_Image_draw_tile_info_terrain (PCX_Image * this, int edx, char * str, i if (dist != NULL) { show_district_name = true; - char const * display_name = is->district_configs[dist->district_type].display_name; + char const * display_name = is->district_configs[dist->district_id].display_name; if ((display_name == NULL) || (display_name[0] == '\0')) - display_name = is->district_configs[dist->district_type].name; + display_name = is->district_configs[dist->district_id].name; // If it's a wonder district with a completed wonder, show the wonder name instead - if ((dist->district_type == WONDER_DISTRICT_ID) && + if ((dist->district_id == WONDER_DISTRICT_ID) && (dist->wonder_info.state == WDS_COMPLETED) && (dist->wonder_info.wonder_index >= 0) && (dist->wonder_info.wonder_index < is->wonder_district_count)) { @@ -21460,7 +21455,7 @@ patch_PCX_Image_draw_tile_info_terrain (PCX_Image * this, int edx, char * str, i if ((wonder_name != NULL) && (wonder_name[0] != '\0')) { display_name = wonder_name; } - } else if ((dist->district_type == NATURAL_WONDER_DISTRICT_ID) && + } else if ((dist->district_id == NATURAL_WONDER_DISTRICT_ID) && (dist->natural_wonder_info.natural_wonder_id >= 0) && (dist->natural_wonder_info.natural_wonder_id < is->natural_wonder_count)) { int natural_id = dist->natural_wonder_info.natural_wonder_id; @@ -21650,7 +21645,7 @@ insert_ai_candidate_bridge_or_canals_into_district_tile_map () if (inst == NULL) continue; inst->state = DS_COMPLETED; - inst->district_type = entry->district_id; + inst->district_id = entry->district_id; inst->tile_x = tx; inst->tile_y = ty; @@ -22012,7 +22007,7 @@ copy_building_with_cities_in_radius (City * source, int improv_id, int required_ if (tile->vtable->m38_Get_Territory_OwnerID (tile) != source->Body.CivID) return; struct district_instance * inst = get_district_instance (tile); - if (inst == NULL || inst->district_type != required_district_id) return; + if (inst == NULL || inst->district_id != required_district_id) return; if (! district_is_complete (tile, required_district_id)) return; FOR_CITIES_OF (coi, source->Body.CivID) { @@ -22037,7 +22032,7 @@ copy_building_with_cities_in_radius (City * source, int improv_id, int required_ int x = wai.tile_x, y = wai.tile_y; Tile * tile = wai.tile; struct district_instance * inst = wai.district_inst; - if (inst->district_type != required_district_id) continue; + if (inst->district_id != required_district_id) continue; FOR_CITIES_OF (coi, source->Body.CivID) { City * city = coi.city; @@ -22100,7 +22095,7 @@ grant_existing_district_buildings_to_city (City * city) Tile * tile = wai.tile; struct district_instance * inst = wai.district_inst; - int district_id = inst->district_type; + int district_id = inst->district_id; struct district_infos * info = &is->district_infos[district_id]; if (info->dependent_building_count <= 0) @@ -22246,7 +22241,7 @@ auto_build_great_wall_districts_for_civ (int civ_id) continue; struct district_instance * inst = get_district_instance (tile); - if ((inst != NULL) && (inst->district_type == GREAT_WALL_DISTRICT_ID)) { + if ((inst != NULL) && (inst->district_id == GREAT_WALL_DISTRICT_ID)) { if (! district_is_complete (tile, GREAT_WALL_DISTRICT_ID)) { inst->state = DS_COMPLETED; if (! tile->vtable->m18_Check_Mines (tile, __, 0)) @@ -22261,7 +22256,7 @@ auto_build_great_wall_districts_for_civ (int civ_id) bool has_district = (inst != NULL); int existing_district_id = -1; if (has_district) - existing_district_id = inst->district_type; + existing_district_id = inst->district_id; if (is_human && civ_id == p_main_screen_form->Player_CivID) { is->focused_tile = tile; @@ -22318,7 +22313,7 @@ auto_build_great_wall_districts_for_civ (int civ_id) if (inst == NULL) continue; - inst->district_type = GREAT_WALL_DISTRICT_ID; + inst->district_id = GREAT_WALL_DISTRICT_ID; district_instance_set_coords (inst, x, y); inst->state = DS_COMPLETED; if (! tile->vtable->m18_Check_Mines (tile, __, 0)) @@ -22384,7 +22379,7 @@ patch_City_add_or_remove_improvement (City * this, int edx, int improv_id, int a y = wai.tile_y; Tile * t = wai.tile; struct district_instance * inst = wai.district_inst; - if (inst->district_type != WONDER_DISTRICT_ID) continue; + if (inst->district_id != WONDER_DISTRICT_ID) continue; if (! wonder_is_buildable_on_tile (t, improv_id)) continue; @@ -22709,7 +22704,7 @@ patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Imag if (draw_natural_wonders) { FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { struct district_instance * inst = (struct district_instance *)(long)tei.value; - if ((inst == NULL) || (inst->district_type != NATURAL_WONDER_DISTRICT_ID)) + if ((inst == NULL) || (inst->district_id != NATURAL_WONDER_DISTRICT_ID)) continue; int natural_id = inst->natural_wonder_info.natural_wonder_id; @@ -22755,7 +22750,7 @@ patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Imag } struct district_instance * inst = get_district_instance (tile); - if ((inst != NULL) && (inst->district_type == NATURAL_WONDER_DISTRICT_ID)) + if ((inst != NULL) && (inst->district_id == NATURAL_WONDER_DISTRICT_ID)) continue; int screen_x, screen_y; @@ -22859,7 +22854,7 @@ handle_possible_duplicate_small_wonders(City * city, Leader * leader) // Make sure it's a completed wonder district struct district_instance * inst = wai.district_inst; - if (inst->district_type != WONDER_DISTRICT_ID) continue; + if (inst->district_id != WONDER_DISTRICT_ID) continue; struct wonder_district_info * info = &inst->wonder_info; if ((info == NULL) || (info->state != WDS_COMPLETED)) continue; @@ -22903,7 +22898,7 @@ grant_nearby_wonders_to_city (City * city) // Make sure Wonder is completed struct district_instance * inst = wai.district_inst; - if (inst->district_type != WONDER_DISTRICT_ID) continue; + if (inst->district_id != WONDER_DISTRICT_ID) continue; struct wonder_district_info * info = &inst->wonder_info; if ((info == NULL) || (info->state != WDS_COMPLETED)) continue; @@ -22961,7 +22956,7 @@ on_gain_city (Leader * leader, City * city, enum city_gain_reason reason) if ((city_tile != NULL) && (city_tile != p_null_tile)) { struct district_instance * inst = get_district_instance (city_tile); if (inst != NULL) - handle_district_removed (city_tile, inst->district_type, city->Body.X, city->Body.Y, false); + handle_district_removed (city_tile, inst->district_id, city->Body.X, city->Body.Y, false); } } @@ -24569,7 +24564,7 @@ count_workable_tiles_for_city (City * city) continue; struct district_instance * inst = get_district_instance (tile); - if ((inst != NULL) && district_is_complete (tile, inst->district_type)) + if ((inst != NULL) && district_is_complete (tile, inst->district_id)) continue; workable++; @@ -24816,6 +24811,9 @@ patch_Leader_do_production_phase (Leader * this) if (is->current_config.enable_districts) { assign_workers_for_pending_districts (this); + if (is->current_config.enable_canal_districts || is->current_config.enable_bridge_districts) + assign_workers_for_ai_candidate_bridge_or_canals (this); + bool ai_player = ((*p_human_player_bits & (1 << this->ID)) == 0); int auto_dynamic_district_ids[COUNT_DISTRICT_TYPES]; int auto_dynamic_district_count = 0; @@ -25330,8 +25328,8 @@ patch_Unit_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, bool if (! should_override && allow_bridge_walk) { struct district_instance * inst = get_district_instance (dest); if ((inst != NULL) && - (inst->district_type == BRIDGE_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) + (inst->district_id == BRIDGE_DISTRICT_ID) && + district_is_complete (dest, inst->district_id)) should_override = true; } @@ -25621,7 +25619,7 @@ patch_Unit_attack_tile (Unit * this, int edx, int x, int y, int bombarding) struct district_instance * inst = get_district_instance (target_tile); if (inst != NULL) { had_district_before = true; - district_id_before = inst->district_type; + district_id_before = inst->district_id; } } } @@ -25637,7 +25635,7 @@ patch_Unit_attack_tile (Unit * this, int edx, int x, int y, int bombarding) if (had_district_before && (target_tile != NULL) && (target_tile != p_null_tile) && district_id_before != NATURAL_WONDER_DISTRICT_ID) { struct district_instance * inst_after = get_district_instance (target_tile); bool has_district_after = (inst_after != NULL); - int district_id_after = (inst_after != NULL) ? inst_after->district_type : -1; + int district_id_after = (inst_after != NULL) ? inst_after->district_id : -1; // If the district existed before but not after, or the tile no longer has a mine, the district was destroyed if (!has_district_after || !(target_tile->vtable->m42_Get_Overlays (target_tile, __, 0) & TILE_FLAG_MINE)) { @@ -26196,7 +26194,7 @@ patch_Sprite_draw_improv_img_on_city_form (Sprite * this, int edx, PCX_Image * c if (is->current_config.enable_districts && (generated_resource_count < ARRAY_LEN (generated_resources))) { FOR_DISTRICTS_AROUND (wai, p_city_form->CurrentCity->Body.X, p_city_form->CurrentCity->Body.Y, true) { struct district_instance * di = wai.district_inst; - int district_id = di->district_type; + int district_id = di->district_id; if ((district_id < 0) || (district_id >= is->district_count)) continue; @@ -26776,7 +26774,7 @@ get_menu_verb_for_unit (Unit * unit, char * out_str, int str_capacity) Tile * tile = tile_at (unit->Body.X, unit->Body.Y); struct district_instance * inst = get_district_instance (tile); if ((tile != NULL) && (tile != p_null_tile) && inst != NULL) { - char const * district_name = is->district_configs[inst->district_type].name; + char const * district_name = is->district_configs[inst->district_id].name; snprintf (out_str, str_capacity, "%s %s", is->c3x_labels[CL_BUILDING], district_name); out_str[str_capacity - 1] = '\0'; return true; @@ -26848,7 +26846,7 @@ patch_Map_place_scenario_things (Map * this) FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { struct district_instance * inst = (struct district_instance *)(long)tei.value; if ((inst != NULL) && - (inst->district_type == NATURAL_WONDER_DISTRICT_ID) && + (inst->district_id == NATURAL_WONDER_DISTRICT_ID) && (inst->natural_wonder_info.natural_wonder_id >= 0)) { any_natural_wonders = true; break; @@ -26926,9 +26924,9 @@ patch_Tile_set_flag_for_eruption_damage (Tile * this, int edx, int param_1, int { if (is->current_config.enable_districts) { struct district_instance * inst = get_district_instance (this); - if (inst != NULL && inst->district_type >= 0 && inst->district_type < is->district_count) { + if (inst != NULL && inst->district_id >= 0 && inst->district_id < is->district_count) { // District found - handle removal - int district_id = inst->district_type; + int district_id = inst->district_id; // Notify human player if this tile is in their territory int territory_owner = this->vtable->m38_Get_Territory_OwnerID (this); @@ -27088,11 +27086,6 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi serialize_aligned_text ("current_day_night_cycle", &mod_data); int_to_bytes (buffer_allocate (&mod_data, sizeof is->current_day_night_cycle), is->current_day_night_cycle); } - if (is->great_wall_auto_build != GWABS_NOT_STARTED) { - serialize_aligned_text ("great_wall_auto_build_state", &mod_data); - *(int *)buffer_allocate (&mod_data, sizeof(int)) = (int)is->great_wall_auto_build; - } - if (is->current_config.enable_districts && (is->district_count > 0)) { serialize_aligned_text ("district_config_names", &mod_data); int * entry_count = (int *)buffer_allocate (&mod_data, sizeof(int)); @@ -27165,7 +27158,7 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi if (is->current_config.enable_districts && (is->district_tile_map.len > 0)) { serialize_aligned_text ("district_tile_map", &mod_data); int entry_capacity = is->district_tile_map.len; - int * chunk = (int *)buffer_allocate (&mod_data, sizeof(int) * (1 + 7 * entry_capacity)); + int * chunk = (int *)buffer_allocate (&mod_data, sizeof(int) * (1 + 9 * entry_capacity)); int * out = chunk + 1; int written = 0; FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { @@ -27186,18 +27179,20 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi inst->wonder_info.city = NULL; out[0] = x; out[1] = y; - out[2] = inst->district_type; + out[2] = inst->district_id; out[3] = (int)inst->state; - out[4] = (int)inst->wonder_info.state; - out[5] = wonder_city_id; - out[6] = inst->wonder_info.wonder_index; - out += 7; + out[4] = inst->built_by_civ_id; + out[5] = inst->completed_turn; + out[6] = (int)inst->wonder_info.state; + out[7] = wonder_city_id; + out[8] = inst->wonder_info.wonder_index; + out += 9; written++; } chunk[0] = written; int unused_entries = entry_capacity - written; if (unused_entries > 0) { - int trimmed_bytes = unused_entries * 7 * (int)sizeof(int); + int trimmed_bytes = unused_entries * 9 * (int)sizeof(int); mod_data.length -= trimmed_bytes; } } @@ -27206,7 +27201,7 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi int entry_capacity = 0; FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { struct district_instance * inst = (struct district_instance *)(long)tei.value; - if ((inst == NULL) || (inst->district_type != NATURAL_WONDER_DISTRICT_ID)) + if ((inst == NULL) || (inst->district_id != NATURAL_WONDER_DISTRICT_ID)) continue; if (inst->natural_wonder_info.natural_wonder_id < 0) continue; @@ -27221,7 +27216,7 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi Tile * tile = (Tile *)tei.key; struct district_instance * inst = (struct district_instance *)(long)tei.value; if ((inst == NULL) || - (inst->district_type != NATURAL_WONDER_DISTRICT_ID) || + (inst->district_id != NATURAL_WONDER_DISTRICT_ID) || (inst->natural_wonder_info.natural_wonder_id < 0)) continue; int x, y; @@ -27332,6 +27327,53 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi mod_data.length -= trimmed_bytes; } } + + if (is->great_wall_auto_build != GWABS_NOT_STARTED) { + serialize_aligned_text ("great_wall_auto_build_state", &mod_data); + *(int *)buffer_allocate (&mod_data, sizeof(int)) = (int)is->great_wall_auto_build; + } + + if (is->ai_candidate_bridge_or_canals_initialized || (is->ai_candidate_bridge_or_canals_count > 0)) { + serialize_aligned_text ("ai_candidate_bridge_or_canals", &mod_data); + int * header = (int *)buffer_allocate (&mod_data, sizeof(int) * 3); + header[0] = is->ai_candidate_bridge_or_canals_initialized ? 1 : 0; + header[1] = is->ai_candidate_bridge_or_canals_count; + header[2] = is->ai_candidate_bridge_or_canals_capacity; + for (int ei = 0; ei < is->ai_candidate_bridge_or_canals_count; ei++) { + struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[ei]; + int tile_count = (int)entry->tile_count; + if ((tile_count <= 0) || (entry->tile_x == NULL) || (entry->tile_y == NULL)) + tile_count = 0; + + int field_count = 14; + int * chunk = (int *)buffer_allocate (&mod_data, sizeof(int) * (field_count + tile_count * 2)); + int pending_city_id = entry->pending_req.city_id; + if ((pending_city_id < 0) && (entry->pending_req.city != NULL)) + pending_city_id = entry->pending_req.city->Body.ID; + + chunk[0] = entry->district_id; + chunk[1] = (int)entry->owner_civ_id; + chunk[2] = tile_count; + chunk[3] = (entry->tile_capacity > 0) ? entry->tile_capacity : tile_count; + chunk[4] = entry->assigned_tile_index; + chunk[5] = entry->assigned_worker_id; + chunk[6] = entry->completed ? 1 : 0; + chunk[7] = pending_city_id; + chunk[8] = entry->pending_req.civ_id; + chunk[9] = entry->pending_req.district_id; + chunk[10] = entry->pending_req.assigned_worker_id; + chunk[11] = entry->pending_req.target_x; + chunk[12] = entry->pending_req.target_y; + chunk[13] = entry->pending_req.worker_assigned_turn; + + int * out = chunk + field_count; + for (int ti = 0; ti < tile_count; ti++) { + out[0] = entry->tile_x[ti]; + out[1] = entry->tile_y[ti]; + out += 2; + } + } + } } int metadata_size = (mod_data.length > 0) ? 12 : 0; // Two four-byte bookends plus one four-byte size, only written if there's any mod data @@ -27398,6 +27440,7 @@ patch_move_game_data (byte * buffer, bool save_else_load) } table_deinit (&is->named_tile_map); clear_all_tracked_workers (); + reset_ai_candidate_bridge_or_canals (); } // Check for a mod save data section and load it if present @@ -27638,9 +27681,10 @@ patch_move_game_data (byte * buffer, bool save_else_load) cursor = (byte *)ints; remaining_bytes -= (int)sizeof(int); if (entry_count >= 0) { - int ints_per_entry = 7; + int ints_per_entry = 9; + success = true; int required_bytes = entry_count * ints_per_entry * (int)sizeof(int); - if (remaining_bytes >= required_bytes) { + if (success && remaining_bytes >= required_bytes) { FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { struct district_instance * inst = (struct district_instance *)(long)tei.value; if (inst != NULL) @@ -27657,6 +27701,8 @@ patch_move_game_data (byte * buffer, bool save_else_load) int y = *ints++; int district_id = *ints++; int state_val = *ints++; + int built_by_civ_id = *ints++; + int completed_turn = *ints++; int wonder_state = *ints++; int wonder_city_id = *ints++; int wonder_index = *ints++; @@ -27681,6 +27727,8 @@ patch_move_game_data (byte * buffer, bool save_else_load) break; } inst->state = new_state; + inst->built_by_civ_id = built_by_civ_id; + inst->completed_turn = completed_turn; inst->wonder_info.state = (enum wonder_district_state)wonder_state; inst->wonder_info.city_id = (wonder_city_id >= 0) ? wonder_city_id : -1; @@ -27694,6 +27742,8 @@ patch_move_game_data (byte * buffer, bool save_else_load) } } } + else + success = false; } } if (! success) { @@ -27728,7 +27778,7 @@ patch_move_game_data (byte * buffer, bool save_else_load) struct district_instance * inst = ensure_district_instance (tile, NATURAL_WONDER_DISTRICT_ID, x, y); if (inst == NULL) continue; - inst->district_type = NATURAL_WONDER_DISTRICT_ID; + inst->district_id = NATURAL_WONDER_DISTRICT_ID; inst->state = DS_COMPLETED; inst->natural_wonder_info.natural_wonder_id = natural_id; set_tile_unworkable_for_all_cities (tile, x, y); @@ -27873,6 +27923,142 @@ patch_move_game_data (byte * buffer, bool save_else_load) break; } + } else if (match_save_chunk_name (&cursor, "ai_candidate_bridge_or_canals")) { + bool success = false; + int remaining_bytes = (seg + seg_size) - cursor; + if (remaining_bytes >= (int)sizeof(int) * 3) { + int * ints = (int *)cursor; + int saved_initialized = *ints++; + int saved_count = *ints++; + int saved_capacity = *ints++; + cursor = (byte *)ints; + remaining_bytes -= (int)sizeof(int) * 3; + + if ((saved_count >= 0) && (saved_capacity >= 0)) { + reset_ai_candidate_bridge_or_canals (); + success = true; + + int alloc_capacity = saved_capacity; + if (alloc_capacity < saved_count) + alloc_capacity = saved_count; + if (alloc_capacity > 0) { + is->ai_candidate_bridge_or_canals = (struct ai_candidate_bridge_or_canal_entry *)calloc (alloc_capacity, sizeof *is->ai_candidate_bridge_or_canals); + if (is->ai_candidate_bridge_or_canals == NULL) { + success = false; + alloc_capacity = 0; + } else + is->ai_candidate_bridge_or_canals_capacity = alloc_capacity; + } + + is->ai_candidate_bridge_or_canals_initialized = (saved_initialized != 0); + int loaded_count = 0; + for (int ei = 0; ei < saved_count; ei++) { + if (remaining_bytes < (int)sizeof(int) * 14) { + success = false; + break; + } + int district_id = *ints++; + int owner_civ_id = *ints++; + int tile_count = *ints++; + int tile_capacity = *ints++; + int assigned_tile_index = *ints++; + int assigned_worker_id = *ints++; + int completed = *ints++; + int pending_city_id = *ints++; + int pending_civ_id = *ints++; + int pending_district_id = *ints++; + int pending_assigned_worker_id = *ints++; + int pending_target_x = *ints++; + int pending_target_y = *ints++; + int pending_worker_assigned_turn = *ints++; + cursor = (byte *)ints; + remaining_bytes -= (int)sizeof(int) * 14; + + if (tile_count < 0) { + success = false; + break; + } + if (remaining_bytes < tile_count * 2 * (int)sizeof(int)) { + success = false; + break; + } + + int stored_tile_count = tile_count; + if (tile_count > AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES) + tile_count = AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES; + if (tile_capacity < tile_count) + tile_capacity = tile_count; + + struct ai_candidate_bridge_or_canal_entry * entry = NULL; + if ((alloc_capacity > 0) && (loaded_count < alloc_capacity)) + entry = &is->ai_candidate_bridge_or_canals[loaded_count]; + + if (entry != NULL) { + entry->district_id = district_id; + entry->owner_civ_id = (short)owner_civ_id; + entry->tile_count = (short)tile_count; + entry->tile_capacity = tile_capacity; + entry->assigned_tile_index = assigned_tile_index; + entry->assigned_worker_id = assigned_worker_id; + entry->completed = (completed != 0); + + entry->tile_x = (short *)calloc (sizeof *entry->tile_x, tile_count); + entry->tile_y = (short *)calloc (sizeof *entry->tile_y, tile_count); + if ((tile_count > 0) && ((entry->tile_x == NULL) || (entry->tile_y == NULL))) { + if (entry->tile_x != NULL) + free (entry->tile_x); + if (entry->tile_y != NULL) + free (entry->tile_y); + entry->tile_x = NULL; + entry->tile_y = NULL; + entry->tile_count = 0; + entry->tile_capacity = 0; + } + + for (int ti = 0; ti < stored_tile_count; ti++) { + int tx = *ints++; + int ty = *ints++; + if ((entry->tile_x != NULL) && (ti < tile_count)) { + entry->tile_x[ti] = (short)tx; + entry->tile_y[ti] = (short)ty; + } + } + + entry->pending_req.city = (pending_city_id >= 0) ? get_city_ptr (pending_city_id) : NULL; + entry->pending_req.city_id = (entry->pending_req.city != NULL) ? pending_city_id : -1; + entry->pending_req.civ_id = pending_civ_id; + entry->pending_req.district_id = pending_district_id; + entry->pending_req.assigned_worker_id = pending_assigned_worker_id; + entry->pending_req.target_x = pending_target_x; + entry->pending_req.target_y = pending_target_y; + entry->pending_req.worker_assigned_turn = pending_worker_assigned_turn; + + if ((entry->assigned_worker_id >= 0) && (get_unit_ptr (entry->assigned_worker_id) == NULL)) + entry->assigned_worker_id = -1; + if ((entry->pending_req.assigned_worker_id >= 0) && (get_unit_ptr (entry->pending_req.assigned_worker_id) == NULL)) + entry->pending_req.assigned_worker_id = -1; + if ((entry->assigned_tile_index < 0) || (entry->assigned_tile_index >= entry->tile_count)) + entry->assigned_tile_index = -1; + + loaded_count++; + } else { + for (int ti = 0; ti < stored_tile_count; ti++) { + ints += 2; + } + } + + cursor = (byte *)ints; + remaining_bytes -= stored_tile_count * 2 * (int)sizeof(int); + } + if (success) + is->ai_candidate_bridge_or_canals_count = loaded_count; + } + } + if (! success) { + error_chunk_name = "ai_candidate_bridge_or_canals"; + break; + } + } else if (match_save_chunk_name (&cursor, "district_config_names")) { bool success = false; bool mismatch_found = false; @@ -28139,7 +28325,7 @@ patch_set_worker_animation (void * this, int edx, Unit * unit, int job_id) Tile * tile = tile_at (unit->Body.X, unit->Body.Y); struct district_instance * inst = get_district_instance (tile); if (inst != NULL && - ! district_is_complete (tile, inst->district_type) && job_id == WJ_Build_Mines) { + ! district_is_complete (tile, inst->district_id) && job_id == WJ_Build_Mines) { // Override and ensure build animation is used job_id = AT_BUILD; @@ -28162,8 +28348,8 @@ patch_Unit_work_simple_job (Unit * this, int edx, int job_id) if (tile != NULL && tile != p_null_tile) { // Check if there's a completed district on this tile struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && district_is_complete(tile, inst->district_type)) { - int district_id = inst->district_type; + if (inst != NULL && district_is_complete(tile, inst->district_id)) { + int district_id = inst->district_id; bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; // AI players only (human removal is handled via issue_district_worker_command) @@ -28488,7 +28674,7 @@ patch_Unit_check_rebase_target (Unit * this, int edx, int tile_x, int tile_y) // Check if tile has a district struct district_instance * inst = get_district_instance (tile); if (inst != NULL) { - int district_id = inst->district_type; + int district_id = inst->district_id; // Check if this is an aerodrome district owned by this unit's civ if ((district_id == AERODROME_DISTRICT_ID) && (tile->Territory_OwnerID == this->Body.CivID)) { // Check if aerodrome is complete @@ -28941,7 +29127,7 @@ patch_City_Form_draw_yields_on_worked_tiles (City_Form * this) FOR_DISTRICTS_AROUND (wai, city_x, city_y, true) { struct district_instance * inst = wai.district_inst; - int district_id = inst->district_type; + int district_id = inst->district_id; bool is_distribution_hub = district_id == DISTRIBUTION_HUB_DISTRICT_ID; bool is_natural_wonder = district_id == NATURAL_WONDER_DISTRICT_ID; @@ -28994,7 +29180,7 @@ patch_City_Form_draw_yields_on_worked_tiles (City_Form * this) continue; struct district_instance * inst = get_district_instance (tile); - if ((inst == NULL) || (inst->district_type != DISTRIBUTION_HUB_DISTRICT_ID)) + if ((inst == NULL) || (inst->district_id != DISTRIBUTION_HUB_DISTRICT_ID)) continue; int screen_x = center_screen_x + (dx * tile_half_width); @@ -30595,18 +30781,22 @@ draw_canal_on_map_or_canvas(Sprite * sprite, int tile_x, int tile_y, int dir, bo draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y); - // in certain cases, add an additional draw if adjacent to water so that the canal appears to extend far enough + char ss[200]; + snprintf (ss, sizeof(ss), "Drawing canal dir = %d, DIR_SE = %d, water dirs[SE] = %d\n", dir, DIR_SE, water_dirs[DIR_SE]); + (*p_OutputDebugStringA)(ss); + + // In certain cases, add an additional draw if adjacent to water so that the canal appears to extend far enough if (dir == DIR_N && water_dirs[DIR_N]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y - y_offset); else if (dir == DIR_NE && water_dirs[DIR_NE]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y - y_offset); else if (dir == DIR_E && water_dirs[DIR_E]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y); - else if (dir == DIR_SE && water_dirs[DIR_SE] && ! (tile_is_water (tile_x - 2, tile_y))) + else if (dir == DIR_SE && water_dirs[DIR_SE] && (! (tile_is_water (tile_x - 2, tile_y)))) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y + y_offset); else if (dir == DIR_S && water_dirs[DIR_S]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y + y_offset); - else if (dir == DIR_SW && water_dirs[DIR_SW] && ! (tile_is_water (tile_x + 2, tile_y))) + else if (dir == DIR_SW && water_dirs[DIR_SW] && (! (tile_is_water (tile_x + 2, tile_y)))) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y + y_offset); else if (dir == DIR_W && water_dirs[DIR_W]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y); @@ -30615,7 +30805,7 @@ draw_canal_on_map_or_canvas(Sprite * sprite, int tile_x, int tile_y, int dir, bo } void -get_canal_directions (Tile * tile, int tile_x, int tile_y, bool * out_water_dirs[9], int * out_dir1, int * out_dir2) +get_canal_directions (Tile * tile, int tile_x, int tile_y, bool out_water_dirs[9], int * out_dir1, int * out_dir2) { bool canal_at_n = tile_has_district_at (tile_x, tile_y - 2, CANAL_DISTRICT_ID); bool canal_at_s = tile_has_district_at (tile_x, tile_y + 2, CANAL_DISTRICT_ID); @@ -30737,20 +30927,23 @@ get_canal_directions (Tile * tile, int tile_x, int tile_y, bool * out_water_dirs } // Manual overrides - overall algorithm works pretty well, but handle corner cases - if (!has_canal_dir && water_dirs[DIR_NE] && water_dirs[DIR_SW]) { draw_dir1 = DIR_NE; draw_dir2 = DIR_SW; } - else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_S && water_dirs[DIR_N] && water_dirs[DIR_NE]) { draw_dir1 = DIR_N; draw_dir2 = DIR_S; } - else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_SE && water_dirs[DIR_SE] && water_dirs[DIR_SW]) { draw_dir2 = DIR_SW; } - else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_SE && water_dirs[DIR_NE] && water_dirs[DIR_N]) { draw_dir1 = DIR_N; } - else if (draw_dir1 == DIR_NE && draw_dir2 == DIR_NW && water_dirs[DIR_S]) { draw_dir2 = DIR_S; } + if (!has_canal_dir && water_dirs[DIR_NE] && water_dirs[DIR_SW]) { draw_dir1 = DIR_NE; draw_dir2 = DIR_SW; } + if (draw_dir1 == DIR_NE && draw_dir2 == DIR_S && water_dirs[DIR_N] && water_dirs[DIR_NE]) { draw_dir1 = DIR_N; draw_dir2 = DIR_S; } + if (draw_dir1 == DIR_NE && draw_dir2 == DIR_SE && water_dirs[DIR_SE] && water_dirs[DIR_SW]) { draw_dir2 = DIR_SW; } + if (draw_dir1 == DIR_NE && draw_dir2 == DIR_SE && water_dirs[DIR_NE] && water_dirs[DIR_N]) { draw_dir1 = DIR_N; } + if (draw_dir1 == DIR_NE && draw_dir2 == DIR_NW && water_dirs[DIR_S]) { draw_dir2 = DIR_S; } + if (draw_dir1 == DIR_NE && draw_dir2 == DIR_NW && canal_dirs[DIR_NE] && water_dirs[DIR_SW]) { draw_dir2 = DIR_SW; } + if (draw_dir1 == DIR_NE && draw_dir2 == DIR_S && canal_dirs[DIR_NE] && water_dirs[DIR_S]) { draw_dir2 = DIR_SW; } + if (draw_dir1 == DIR_NW && draw_dir2 == DIR_NE && canal_dirs[DIR_NW] && water_dirs[DIR_SE]) { draw_dir2 = DIR_SE; } + if (draw_dir1 == DIR_NW && draw_dir2 == DIR_S && canal_dirs[DIR_NW] && water_dirs[DIR_S]) { draw_dir2 = DIR_SE; } snprintf (ss, sizeof(ss), "Drawing canal at (%d,%d) dirs:%d,%d\n", tile_x, tile_y, draw_dir1, draw_dir2); (*p_OutputDebugStringA)(ss); *out_dir1 = draw_dir1; *out_dir2 = draw_dir2; - - if (out_water_dirs != NULL) - *out_water_dirs = water_dirs; + for (int i = 0; i < 9; i++) + out_water_dirs[i] = water_dirs[i]; } void @@ -30767,7 +30960,7 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren int draw_dir1 = -1; int draw_dir2 = -1; bool water_dirs[9] = { false, false, false, false, false, false, false, false, false }; - get_canal_directions (tile, tile_x, tile_y, &water_dirs, &draw_dir1, &draw_dir2); + get_canal_directions (tile, tile_x, tile_y, water_dirs, &draw_dir1, &draw_dir2); if (draw_dir1 >= 0) { Sprite * sprite1 = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][draw_dir1]; @@ -30821,7 +31014,7 @@ draw_district_generated_resource_on_tile (Map_Renderer * this, Tile * tile, stru int district_resource = -1; if (inst->state == DS_COMPLETED) { - int district_id = inst->district_type; + int district_id = inst->district_id; if ((district_id >= 0) && (district_id < is->district_count)) { struct district_config * cfg = &is->district_configs[district_id]; if (cfg->generated_resource_id >= 0) { @@ -30874,7 +31067,7 @@ draw_district_generated_resource_on_tile (Map_Renderer * this, Tile * tile, stru void draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instance * inst, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int visible_to_civ_id) { - int district_id = inst->district_type; + int district_id = inst->district_id; if (is->dc_img_state == IS_UNINITED) init_district_images (); @@ -31101,7 +31294,7 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis } // Draw resources first if needed - if (is->district_configs[inst->district_type].draw_over_resources) + if (is->district_configs[inst->district_id].draw_over_resources) draw_district_generated_resource_on_tile (this, tile, inst, tile_x, tile_y, map_renderer, pixel_x, pixel_y, visible_to_civ_id); draw_district_on_tile (this, tile, inst, tile_x, tile_y, map_renderer, pixel_x, pixel_y, visible_to_civ_id); @@ -31128,7 +31321,7 @@ patch_Map_Renderer_m09_Draw_Tile_Resources (Map_Renderer * this, int edx, int vi } // Resources that should be drawn below district are already drawn, skip in that case - if (is->district_configs[inst->district_type].draw_over_resources) + if (is->district_configs[inst->district_id].draw_over_resources) return; draw_district_generated_resource_on_tile (this, tile, inst, tile_x, tile_y, map_renderer, pixel_x, pixel_y, visible_to_civ_id); @@ -31149,8 +31342,8 @@ patch_Map_Renderer_m11_Draw_Tile_Irrigation (Map_Renderer *this, int edx, int vi } // If it has a completed district that serves as pseudo-irrigation source, suppress drawing irrigation - if (is->district_configs[inst->district_type].allow_irrigation_from - && district_is_complete (is->current_render_tile, inst->district_type)) + if (is->district_configs[inst->district_id].allow_irrigation_from + && district_is_complete (is->current_render_tile, inst->district_id)) return; Map_Renderer_m11_Draw_Tile_Irrigation (this, __, visible_to_civ, tile_x, tile_y, param_4, param_5, param_6); @@ -31266,8 +31459,8 @@ ai_move_district_worker (Unit * worker, struct district_worker_record * rec) bool do_replacement = false; // If there is a completed district here already - if ((inst != NULL) && district_is_complete (tile, inst->district_type)) { - int existing_district_id = inst->district_type; + if ((inst != NULL) && district_is_complete (tile, inst->district_id)) { + int existing_district_id = inst->district_id; snprintf (ss, sizeof ss, "ai_move_district_worker: Worker ID %d found existing district ID %d at (%d,%d)\n", worker->Body.ID, existing_district_id, worker->Body.X, worker->Body.Y); (*p_OutputDebugStringA) (ss); @@ -31307,7 +31500,7 @@ ai_move_district_worker (Unit * worker, struct district_worker_record * rec) if (wai.tile == tile) continue; - if (wai.district_inst->district_type == req->district_id) { + if (wai.district_inst->district_id == req->district_id) { snprintf (ss, sizeof ss, "ai_move_district_worker: Found duplicate district ID %d near city at (%d,%d), cancelling request\n", req->district_id, request_city->Body.X, request_city->Body.Y); (*p_OutputDebugStringA) (ss); clear_city_district_request (request_city, req->district_id); @@ -31373,6 +31566,7 @@ ai_move_district_worker (Unit * worker, struct district_worker_record * rec) // Start construction of district inst = ensure_district_instance (tile, req->district_id, req->target_x, req->target_y); + inst->built_by_civ_id = worker->Body.CivID; Unit_set_state(worker, __, UnitState_Build_Mines); worker->Body.Job_ID = WJ_Build_Mines; // Build district return true; @@ -31471,7 +31665,7 @@ ai_worker_try_tile_improvement_district (Unit * worker) continue; struct district_instance temp = {0}; - temp.district_type = i; + temp.district_id = i; temp.tile_x = (short)tile_x; temp.tile_y = (short)tile_y; @@ -31522,11 +31716,11 @@ patch_Unit_ai_move_terraformer (Unit * this) update_tracked_worker_for_unit (this); struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && inst->district_type != NATURAL_WONDER_DISTRICT_ID && + if (inst != NULL && inst->district_id != NATURAL_WONDER_DISTRICT_ID && tile->vtable->m50_Get_Square_BaseType (tile) != SQ_Coast) { // Roads should be made after district builds. The district is complete but // worker is still likely on the tile, so check here and build road if needed - struct district_config * cfg = &is->district_configs[inst->district_type]; + struct district_config * cfg = &is->district_configs[inst->district_id]; bool has_road = (*tile->vtable->m25_Check_Roads)(tile, __, 0); if (! has_road && ! cfg->auto_add_road) { Unit_set_state(this, __, UnitState_Build_Road); @@ -31594,9 +31788,9 @@ patch_get_building_defense_bonus_at (int x, int y, int param_3) struct district_instance * inst = get_district_instance (tile); if (inst != NULL) { - struct district_config const * cfg = &is->district_configs[inst->district_type]; + struct district_config const * cfg = &is->district_configs[inst->district_id]; int bonus = cfg->defense_bonus_percent; - bonus += apply_district_bonus_entries (inst, &cfg->defense_bonus_extras, inst->district_type); + bonus += apply_district_bonus_entries (inst, &cfg->defense_bonus_extras, inst->district_id); return bonus; } @@ -31610,9 +31804,9 @@ patch_Unit_select (Unit * this) Tile * tile = tile_at (this->Body.X, this->Body.Y); struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && is_worker(this) && inst->district_type >= 0 && inst->district_type <= is->district_count && - ! district_is_complete (tile, inst->district_type)) { - int district_id = inst->district_type; + if (inst != NULL && is_worker(this) && inst->district_id >= 0 && inst->district_id <= is->district_count && + ! district_is_complete (tile, inst->district_id)) { + int district_id = inst->district_id; PopupForm * popup = get_popup_form (); int remaining_turns = get_worker_remaining_turns_to_complete (this, __, 0); set_popup_str_param (0, (char*)is->district_configs[district_id].display_name, -1, -1); @@ -31704,7 +31898,7 @@ patch_City_Form_draw_food_income_icons (City_Form * this) // Calculate standard district food bonus int standard_district_food = 0; FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { - int district_id = wai.district_inst->district_type; + int district_id = wai.district_inst->district_id; int food_bonus = 0; get_effective_district_yields (wai.district_inst, &is->district_configs[district_id], &food_bonus, NULL, NULL, NULL, NULL, NULL); standard_district_food += food_bonus; @@ -32000,7 +32194,7 @@ district_tile_needs_defense (Tile * tile, int tile_x, int tile_y, struct distric if ((tile == NULL) || (tile == p_null_tile)) return false; if (inst == NULL) return false; - int district_id = inst->district_type; + int district_id = inst->district_id; struct district_config const * cfg = &is->district_configs[district_id]; if (! district_is_complete (tile, district_id)) return false; if (tile->vtable->m38_Get_Territory_OwnerID (tile) != civ_id) return false; @@ -32175,7 +32369,7 @@ patch_Unit_seek_colony (Unit * this, int edx, bool for_own_defense, int max_dist continue; struct district_instance * inst = get_district_instance (district_tile); - if ((inst == NULL) || (inst->district_type == WONDER_DISTRICT_ID)) + if ((inst == NULL) || (inst->district_id == WONDER_DISTRICT_ID)) continue; int tile_x, tile_y; @@ -32212,7 +32406,7 @@ patch_Tile_has_district_or_colony (Tile * this) is->current_config.ai_defends_districts) { struct district_instance * inst = get_district_instance (this); - return (inst != NULL) && district_is_complete (this, inst->district_type); + return (inst != NULL) && district_is_complete (this, inst->district_id); } // Fallback to original has_colony logic @@ -32291,7 +32485,7 @@ patch_Tile_m71_Check_Worker_Job (Tile * this) if (is->current_config.enable_natural_wonders) { struct district_instance * inst = get_district_instance (this); if ((inst != NULL) && - (inst->district_type == NATURAL_WONDER_DISTRICT_ID) && + (inst->district_id == NATURAL_WONDER_DISTRICT_ID) && (inst->natural_wonder_info.natural_wonder_id >= 0)) { return -1; // No worker job allowed on natural wonders } @@ -32304,7 +32498,7 @@ patch_Tile_get_road_bonus (Tile * this) { if (is->current_config.enable_natural_wonders) { struct district_instance * inst = get_district_instance (this); - if ((inst != NULL) && (inst->district_type == NATURAL_WONDER_DISTRICT_ID)) { + if ((inst != NULL) && (inst->district_id == NATURAL_WONDER_DISTRICT_ID)) { return 0; } } @@ -32386,8 +32580,8 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t if ((dest != NULL) && (dest != p_null_tile)) { struct district_instance * inst = get_district_instance (dest); if ((inst != NULL) && - (inst->district_type == BRIDGE_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) { + (inst->district_id == BRIDGE_DISTRICT_ID) && + district_is_complete (dest, inst->district_id)) { return PBV_OK; } } @@ -32401,8 +32595,8 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t if ((dest != NULL) && (dest != p_null_tile)) { struct district_instance * inst = get_district_instance (dest); if ((inst != NULL) && - (inst->district_type == CANAL_DISTRICT_ID) && - district_is_complete (dest, inst->district_type)) { + (inst->district_id == CANAL_DISTRICT_ID) && + district_is_complete (dest, inst->district_id)) { return PBV_OK; } } @@ -32429,8 +32623,8 @@ patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool if (! allow_move && is->current_config.enable_bridge_districts && (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Land)) { struct district_instance * inst = get_district_instance (dest); - if (inst != NULL && inst->district_type == BRIDGE_DISTRICT_ID && - district_is_complete (dest, inst->district_type)) { + if (inst != NULL && inst->district_id == BRIDGE_DISTRICT_ID && + district_is_complete (dest, inst->district_id)) { allow_move = true; } } @@ -32950,7 +33144,7 @@ patch_Tile_m17_Check_Irrigation (Tile * this, int edx, int visible_to_civ_id) if (inst == NULL) return base; - if (is->district_configs[inst->district_type].allow_irrigation_from && district_is_complete (this, inst->district_type)) + if (is->district_configs[inst->district_id].allow_irrigation_from && district_is_complete (this, inst->district_id)) return true; return base; @@ -32970,7 +33164,7 @@ patch_Unit_ai_eval_bombard_target (Unit * this, int edx, int tile_x, int tile_y, return score; struct district_instance * inst = get_district_instance (tile); - if ((inst == NULL) || (inst->district_type != GREAT_WALL_DISTRICT_ID) || (! district_is_complete (tile, GREAT_WALL_DISTRICT_ID))) + if ((inst == NULL) || (inst->district_id != GREAT_WALL_DISTRICT_ID) || (! district_is_complete (tile, GREAT_WALL_DISTRICT_ID))) return score; int obsolete_id = is->district_infos[GREAT_WALL_DISTRICT_ID].obsoleted_by_id; @@ -33021,8 +33215,8 @@ patch_Unit_heal_at_start_of_turn (Unit * this) Tile * tile = tile_at ((this->Body).X, (this->Body).Y); if ((tile != NULL) && (tile != p_null_tile)) { struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && district_is_complete (tile, inst->district_type) && - is->district_configs[inst->district_type].heal_units_in_one_turn) { + if (inst != NULL && district_is_complete (tile, inst->district_id) && + is->district_configs[inst->district_id].heal_units_in_one_turn) { int territory_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); if (territory_owner == this->Body.CivID) { (this->Body).Damage = 0; From e9954e77a0fcf835b3ae56b13e2118a364c114c4 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 25 Jan 2026 10:03:47 -0800 Subject: [PATCH 262/356] Ensure AI doesnt build bridges/canals immediately next to each other; Add flag to prevent AI from replacing existing districts with canals, if false --- C3X.h | 1 + Notes/district_todos.md | 2 +- default.c3x_config.ini | 1 + injected_code.c | 50 +++++++++++++++++++++++++++++++++-------- 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/C3X.h b/C3X.h index f80b5166..317f4579 100644 --- a/C3X.h +++ b/C3X.h @@ -393,6 +393,7 @@ struct c3x_config { int ai_canal_eval_min_bisected_land_tiles; int ai_bridge_canal_eval_block_size; int ai_bridge_eval_lake_tile_threshold; + bool ai_can_replace_existing_districts_with_canals; bool ai_defends_districts; int ai_city_district_max_build_wait_turns; diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 3886011a..907df21d 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,6 +1,5 @@ - Validate and review upfront bridge/canal algorithm - Validate resource generation (add yield?) - - Fix scenario district art mod folder check - Firm up logic for river district rendering - Hoover Dam (use alt dir, special positioning b/c on river) @@ -23,6 +22,7 @@ - ~~Fix scenario art not loading bug~~ - ~~Choose terrain type~~ - ~~Buttons~~ + - ~~Fix scenario district art mod folder check~~ - ~~Add buildable_by_war_allies, buildable_by_pact_allies~~ - ~~Auto add road/railrood~~ - ~~Named tiles~~ diff --git a/default.c3x_config.ini b/default.c3x_config.ini index a4f3cca9..b9476497 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -914,6 +914,7 @@ max_contiguous_canal_districts = 5 ai_canal_eval_min_bisected_land_tiles = 20 ai_bridge_canal_eval_block_size = 10 ai_bridge_eval_lake_tile_threshold = 5 +ai_can_replace_existing_districts_with_canals = true ; When enabled, AI defensive units will actively seek out and defend districts within their territory, treating them as valuable assets like colonies. ; The AI prioritizes defending Wonder districts (if destructible wonders are enabled) over regular districts, searching within a 20-tile radius for diff --git a/injected_code.c b/injected_code.c index 60497f3b..a2cf43d5 100644 --- a/injected_code.c +++ b/injected_code.c @@ -237,7 +237,6 @@ void get_neighbor_coords (Map * map, int x, int y, int neighbor_index, int * out void wrap_tile_coords (Map * map, int * x, int * y); int count_neighborhoods_in_city_radius (City * city); int count_utilized_neighborhoods_in_city_radius (City * city); -void assign_workers_for_ai_candidate_bridge_or_canals (Leader * leader); struct pause_for_popup { bool done; // Set to true to exit for loop @@ -4219,14 +4218,13 @@ ai_candidate_bridge_or_canal_is_buildable_for_civ (struct ai_candidate_bridge_or struct district_instance * inst = get_district_instance (tile); if (inst != NULL) { - if (inst->district_id == entry->district_id && district_is_complete (tile, entry->district_id)) + if (inst->district_id == entry->district_id) continue; - if (inst->district_id != entry->district_id && district_is_complete (tile, inst->district_id)) { - entry->completed = true; + if (inst->district_id == WONDER_DISTRICT_ID) return false; - } - if (inst->district_id != entry->district_id && ! district_is_complete (tile, inst->district_id)) + if (! is->current_config.ai_can_replace_existing_districts_with_canals) { return false; + } } if (pending_index < 0) @@ -10489,6 +10487,25 @@ tile_part_of_existing_candidate (int tile_x, int tile_y) return false; } +bool +tile_has_bridge_or_canal_nearby (int tile_x, int tile_y) +{ + FOR_TILES_AROUND (tai, workable_tile_counts[1], tile_x, tile_y) { + Tile * adj = tai.tile; + if ((adj == NULL) || (adj == p_null_tile)) + continue; + struct district_instance * inst = get_district_instance (adj); + if ((inst != NULL) && + ((inst->district_id == BRIDGE_DISTRICT_ID) || (inst->district_id == CANAL_DISTRICT_ID))) + return true; + int nx = (tai.n == 0) ? tile_x : tai.tile_x; + int ny = (tai.n == 0) ? tile_y : tai.tile_y; + if (tile_part_of_existing_candidate (nx, ny)) + return true; + } + return false; +} + bool add_ai_candidate_entry (int district_id, short owner_civ_id, short * xs, short * ys, int count) { @@ -10697,6 +10714,10 @@ find_bridge_candidate_in_block (Map * map, int block_x0, int block_y0, int block ok = false; break; } + if (tile_has_bridge_or_canal_nearby (cx, cy)) { + ok = false; + break; + } if (tile_is_reserved_in_district_tile_map (cx, cy)) { ok = false; break; @@ -10887,6 +10908,8 @@ find_canal_candidate_in_block (Map * map, int block_x0, int block_y0, int block_ continue; if (tile_part_of_existing_candidate (start_x, start_y)) continue; + if (tile_has_bridge_or_canal_nearby (start_x, start_y)) + continue; if (tile_is_reserved_in_district_tile_map (start_x, start_y)) continue; Tile * start_tile = tile_at (start_x, start_y); @@ -10952,6 +10975,10 @@ find_canal_candidate_in_block (Map * map, int block_x0, int block_y0, int block_ buildable = false; break; } + if (tile_has_bridge_or_canal_nearby (out_x[pi], out_y[pi])) { + buildable = false; + break; + } } } else { buildable = false; @@ -11122,6 +11149,10 @@ find_canal_candidate_in_block (Map * map, int block_x0, int block_y0, int block_ next_dir++; continue; } + if (tile_has_bridge_or_canal_nearby (nx, ny)) { + next_dir++; + continue; + } if (next_tile->vtable->m46_Get_ContinentID (next_tile) != continent_id) { next_dir++; continue; @@ -16017,7 +16048,8 @@ patch_init_floating_point () {"great_wall_districts_impassible_by_others" , false, offsetof (struct c3x_config, great_wall_districts_impassible_by_others)}, {"auto_build_great_wall_around_territory" , false, offsetof (struct c3x_config, auto_build_great_wall_around_territory)}, {"disable_great_wall_city_defense_bonus" , false, offsetof (struct c3x_config, disable_great_wall_city_defense_bonus)}, - {"expand_water_tile_checks_to_city_work_area" , false, offsetof (struct c3x_config, expand_water_tile_checks_to_city_work_area)}, + {"ai_can_replace_existing_districts_with_canals" , false, offsetof (struct c3x_config, ai_can_replace_existing_districts_with_canals)}, + {"workers_can_enter_coast" , false, offsetof (struct c3x_config, workers_can_enter_coast)}, {"workers_can_enter_coast" , false, offsetof (struct c3x_config, workers_can_enter_coast)}, {"enable_city_work_radii_highlights" , false, offsetof (struct c3x_config, enable_city_work_radii_highlights)}, {"introduce_all_human_players_at_start_of_hotseat_game" , false, offsetof (struct c3x_config, introduce_all_human_players_at_start_of_hotseat_game)}, @@ -30792,11 +30824,11 @@ draw_canal_on_map_or_canvas(Sprite * sprite, int tile_x, int tile_y, int dir, bo draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y - y_offset); else if (dir == DIR_E && water_dirs[DIR_E]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y); - else if (dir == DIR_SE && water_dirs[DIR_SE] && (! (tile_is_water (tile_x - 2, tile_y)))) + else if (dir == DIR_SE && water_dirs[DIR_SE]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y + y_offset); else if (dir == DIR_S && water_dirs[DIR_S]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y + y_offset); - else if (dir == DIR_SW && water_dirs[DIR_SW] && (! (tile_is_water (tile_x + 2, tile_y)))) + else if (dir == DIR_SW && water_dirs[DIR_SW]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y + y_offset); else if (dir == DIR_W && water_dirs[DIR_W]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y); From 42864b4764a1e31c552ea2eac32eac69061048eb Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sun, 25 Jan 2026 10:09:15 -0800 Subject: [PATCH 263/356] Add comments, remove bridge/canal debugging function calls --- injected_code.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/injected_code.c b/injected_code.c index a2cf43d5..d6b7c3e6 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16048,6 +16048,7 @@ patch_init_floating_point () {"great_wall_districts_impassible_by_others" , false, offsetof (struct c3x_config, great_wall_districts_impassible_by_others)}, {"auto_build_great_wall_around_territory" , false, offsetof (struct c3x_config, auto_build_great_wall_around_territory)}, {"disable_great_wall_city_defense_bonus" , false, offsetof (struct c3x_config, disable_great_wall_city_defense_bonus)}, + {"expand_water_tile_checks_to_city_work_area" , false, offsetof (struct c3x_config, expand_water_tile_checks_to_city_work_area)}, {"ai_can_replace_existing_districts_with_canals" , false, offsetof (struct c3x_config, ai_can_replace_existing_districts_with_canals)}, {"workers_can_enter_coast" , false, offsetof (struct c3x_config, workers_can_enter_coast)}, {"workers_can_enter_coast" , false, offsetof (struct c3x_config, workers_can_enter_coast)}, @@ -21630,6 +21631,12 @@ patch_Map_Renderer_m19_Draw_Tile_by_XY_and_Flags (Map_Renderer * this, int edx, } } +// We determine at the start of the game where *any* AI player might want to build canals and bridges. +// This is done up front in a single pass, as re-assessing every single turn per AI is wasteful and the +// geography of the map doesn't change. This function adds all the candidates as districts, effectively +// drawing them directly on the map. We don't use it in a normal game, but this is extremely useful for +// debugging so good to keep it around. For debugging, just call it immediately after +// generate_ai_canal_and_bridge_targets () void insert_ai_candidate_bridge_or_canals_into_district_tile_map () { @@ -23279,7 +23286,6 @@ patch_Map_impl_generate (Map * this, int edx, int seed, bool is_multiplayer_game (is->current_config.enable_bridge_districts || is->current_config.enable_canal_districts)) { reset_ai_candidate_bridge_or_canals (); generate_ai_canal_and_bridge_targets (); - insert_ai_candidate_bridge_or_canals_into_district_tile_map (); } if (is->current_config.enable_natural_wonders) @@ -26892,7 +26898,6 @@ patch_Map_place_scenario_things (Map * this) (is->current_config.enable_bridge_districts || is->current_config.enable_canal_districts)) { reset_ai_candidate_bridge_or_canals (); generate_ai_canal_and_bridge_targets (); - //insert_ai_candidate_bridge_or_canals_into_district_tile_map (); } is->is_placing_scenario_things = false; From d01aaa9a67892b48d88f0e43ce3cb90790b52be5 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 26 Jan 2026 07:29:14 -0800 Subject: [PATCH 264/356] Add advance_prereqs (plural); Allow OR logic for buildings with alternative district dependencies --- C3X.h | 38 ++-- Notes/district_todos.md | 5 +- default.c3x_config.ini | 5 +- default.districts_config.txt | 38 ++-- injected_code.c | 427 ++++++++++++++++++++++++++--------- 5 files changed, 362 insertions(+), 151 deletions(-) diff --git a/C3X.h b/C3X.h index 317f4579..7ff1f47c 100644 --- a/C3X.h +++ b/C3X.h @@ -654,7 +654,8 @@ struct district_config { char const * name; char const * display_name; char const * tooltip; - char const * advance_prereq; + char const * advance_prereqs[MAX_DISTRICT_DEPENDENTS]; + int advance_prereq_count; char const * obsoleted_by; char const * resource_prereqs[MAX_DISTRICT_DEPENDENTS]; char const * resource_prereq_on_tile; @@ -787,7 +788,7 @@ struct wonder_location { const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYPES] = { { .command = UCV_Build_Neighborhood, .name = "Neighborhood", .tooltip = "Build Neighborhood", .display_name = "Neighborhood", - .advance_prereq = NULL, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereqs = {0}, .advance_prereq_count = 0, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 5, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, @@ -797,7 +798,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_WonderDistrict, .name = "Wonder District", .tooltip = "Build Wonder District", .display_name = "Wonder District", - .advance_prereq = NULL, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereqs = {0}, .advance_prereq_count = 0, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"WonderDistrict.pcx"}, .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Coast)), .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, @@ -807,7 +808,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_DistributionHub, .name = "Distribution Hub", .tooltip = "Build Distribution Hub", .display_name = "Distribution Hub", - .advance_prereq = "Construction", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereqs = {"Construction"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"DistributionHub.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 2, .btn_tile_sheet_row = 0, @@ -817,7 +818,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_Aerodrome, .name = "Aerodrome", .tooltip = "Build Aerodrome", .display_name = "Aerodrome", - .advance_prereq = "Flight", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 1, + .advance_prereqs = {"Flight"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 1, .img_paths = {"Aerodrome.pcx"}, .dependent_improvements = {"Airport"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 1, .max_building_index = 1, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, @@ -826,7 +827,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = -1, .name = "Natural Wonder", .tooltip = NULL, .display_name = "Natural Wonder", - .advance_prereq = NULL, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, + .advance_prereqs = {0}, .advance_prereq_count = 0, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {0}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .img_path_count = 0, .max_building_index = 0, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, @@ -835,7 +836,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_Port, .name = "Port", .tooltip = "Build Port", .display_name = "Port", - .advance_prereq = "Map Making", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 2, .align_to_coast = true, + .advance_prereqs = {"Map Making"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 2, .align_to_coast = true, .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .dependent_improvements = {"Harbor", "Commercial Dock"}, .buildable_square_types_mask = (1 << SQ_Coast), .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .align_to_coast = true, @@ -844,7 +845,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_CentralRailHub, .name = "Central Rail Hub", .tooltip = "Build Central Rail Hub", .display_name = "Central Rail Hub", - .advance_prereq = "Steam Power", .resource_prereqs = {"Iron", "Coal"}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 2, .dependent_improvement_count = 0, + .advance_prereqs = {"Steam Power"}, .advance_prereq_count = 1, .resource_prereqs = {"Iron", "Coal"}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 2, .dependent_improvement_count = 0, .img_paths = {"CentralRailHub_AMER.pcx", "CentralRailHub_EURO.pcx", "CentralRailHub_ROMAN.pcx", "CentralRailHub_MIDEAST.pcx", "CentralRailHub_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .auto_add_road = true, .auto_add_railroad = true, .img_path_count = 5, .max_building_index = 1, .btn_tile_sheet_column = 5, .btn_tile_sheet_row = 0, @@ -853,7 +854,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_EnergyGrid, .name = "Energy Grid", .tooltip = "Build Energy Grid", .display_name = "Energy Grid", - .advance_prereq = "Industrialization", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 4, + .advance_prereqs = {"Industrialization"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 4, .img_paths = {"EnergyGrid.pcx"}, .dependent_improvements = {"Coal Plant", "Hydro Plant", "Solar Plant", "Nuclear Plant"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .custom_height = 84, .img_path_count = 1, .max_building_index = 14, .btn_tile_sheet_column = 6, .btn_tile_sheet_row = 0, @@ -862,7 +863,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_Bridge, .name = "Bridge", .tooltip = "Build Bridge", .display_name = "Bridge", - .advance_prereq = "Industrialization", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, + .advance_prereqs = {"Industrialization"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"Bridge.pcx"}, .dependent_improvements = {0}, .custom_width = 176, .custom_height = 112, .y_offset = 24, .x_offset = 0, .buildable_square_types_mask = (1 << SQ_Coast), .auto_add_road = true, .img_path_count = 1, .max_building_index = 3, .btn_tile_sheet_column = 7, .btn_tile_sheet_row = 0, @@ -871,7 +872,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_Canal, .name = "Canal", .tooltip = "Build Canal", .display_name = "Canal", - .advance_prereq = "Industrialization", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, + .advance_prereqs = {"Industrialization"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"Canal.pcx"}, .dependent_improvements = {0}, .custom_width = 176, .custom_height = 112, .y_offset = 24, .x_offset = 0, .buildable_square_types_mask = (1 << SQ_Desert) | (1 << SQ_Plains) | (1 << SQ_Grassland) | (1 << SQ_Tundra) | (1 << SQ_FloodPlain), .img_path_count = 1, .max_building_index = 8, .btn_tile_sheet_column = 8, .btn_tile_sheet_row = 0, @@ -880,7 +881,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP }, { .command = UCV_Build_GreatWall, .name = "Great Wall", .tooltip = "Build Great Wall", .display_name = "Great Wall", - .advance_prereq = NULL, .obsoleted_by = "Metallurgy", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, + .advance_prereqs = {0}, .advance_prereq_count = 0, .obsoleted_by = "Metallurgy", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"GreatWall.pcx"}, .dependent_improvements = {0}, .custom_height = 88, .wonder_prereqs = {"The Great Wall"}, .wonder_prereq_count = 1, .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Mountains) | (1 << SQ_Forest) | (1 << SQ_Swamp) | (1 << SQ_Jungle)), .draw_over_resources = true, .img_path_count = 1, .max_building_index = 10, .btn_tile_sheet_column = 9, .btn_tile_sheet_row = 0, @@ -893,7 +894,8 @@ struct parsed_district_definition { char * name; char * display_name; char * tooltip; - char * advance_prereq; + char * advance_prereqs[5]; + int advance_prereq_count; char * obsoleted_by; char * resource_prereqs[5]; char * resource_prereq_on_tile; @@ -946,7 +948,7 @@ struct parsed_district_definition { bool buildable_by_pact_allies; bool has_name; bool has_tooltip; - bool has_advance_prereq; + bool has_advance_prereqs; bool has_obsoleted_by; bool has_resource_prereqs; bool has_dependent_improvements; @@ -1098,6 +1100,11 @@ struct ai_best_feasible_order { int value; }; +struct district_building_prereq_list { + int count; + int district_ids[MAX_DISTRICT_DEPENDENTS]; +}; + struct pending_district_request { City * city; int city_id; @@ -1915,7 +1922,8 @@ struct district_button_image_set { struct table building_name_to_id; struct district_infos { - int advance_prereq_id; // Tech ID that enables the district + int advance_prereq_ids[MAX_DISTRICT_DEPENDENTS]; // Tech IDs that enable the district (all required) + int advance_prereq_count; int obsoleted_by_id; int resource_prereq_ids[MAX_DISTRICT_DEPENDENTS]; int resource_prereq_count; diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 907df21d..d59675a2 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,5 +1,6 @@ - - Validate and review upfront bridge/canal algorithm + - Validate and upfront bridge/canal algorithm - Validate resource generation (add yield?) + - Negative bonuses - Firm up logic for river district rendering - Hoover Dam (use alt dir, special positioning b/c on river) @@ -20,6 +21,8 @@ ## Maritime Districts - ~~Fix scenario art not loading bug~~ + - ~~Add advance_prereqs~~ + - ~~Allow district OR logic for buildings~~ - ~~Choose terrain type~~ - ~~Buttons~~ - ~~Fix scenario district art mod folder check~~ diff --git a/default.c3x_config.ini b/default.c3x_config.ini index b9476497..419b7816 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -892,11 +892,11 @@ destroyed_wonders_can_be_built_again = true ; ; distribution_hub_yield_division_mode controls how a hub splits its collected food/shields across connected cities: ; flat: Divide raw yields by the configured divisors (distribution_hub_food_yield_divisor & distribution_hub_shield_yield_divisor) -; scale-by-city-count: Bonuses gradually reduce as more cities plugged into the hub. Formula: floor(raw_yield / (sqrt(connected_city_count) * divisor)) +; scale-by-city-count: Bonuses gradually reduce as more cities plug into the hub. Formula: floor(raw_yield / (sqrt(connected_city_count) * divisor)) ; ; ai_distribution_hub_build_strategy controls how the AI decides to build distribution hubs: ; by-city-count: AI builds hubs based on its ideal hub count per 100 cities -; auto: AI dynamically assesses need based on city growth and food/shield deficits, capped at max 1 hub for every 2 cities +; auto: AI dynamically assesses need based on city growth and food/shield deficits across civ distribution_hub_yield_division_mode = scale-by-city-count ai_distribution_hub_build_strategy = auto distribution_hub_food_yield_divisor = 2 @@ -934,5 +934,4 @@ auto_build_great_wall_around_territory = true ; around the map. Only applies when enable_districts is set to true. enable_city_work_radii_highlights = true - enable_named_tiles = true \ No newline at end of file diff --git a/default.districts_config.txt b/default.districts_config.txt index 02ea739f..079e6d67 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -13,7 +13,7 @@ btn_tile_sheet_row = 1 btn_tile_sheet_column = 0 vary_img_by_era = 1 vary_img_by_culture = 0 -advance_prereq = Warrior Code +advance_prereqs = Warrior Code dependent_improvs = Barracks,"SAM Missile Battery" buildable_on = desert,plains,grassland,tundra,floodplain,hills heal_units_in_one_turn = 1 @@ -34,7 +34,7 @@ btn_tile_sheet_row = 1 btn_tile_sheet_column = 1 vary_img_by_era = 0 vary_img_by_culture = 1 -advance_prereq = "Ceremonial Burial" +advance_prereqs = "Ceremonial Burial" dependent_improvs = Temple,Cathedral buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 @@ -54,7 +54,7 @@ btn_tile_sheet_row = 1 btn_tile_sheet_column = 2 vary_img_by_era = 1 vary_img_by_culture = 0 -advance_prereq = Literature +advance_prereqs = Literature dependent_improvs = Library, University buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 @@ -74,7 +74,7 @@ btn_tile_sheet_row = 1 btn_tile_sheet_column = 3 vary_img_by_era = 1 vary_img_by_culture = 1 -advance_prereq = Construction +advance_prereqs = Construction dependent_improvs = Colosseum buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 @@ -94,7 +94,7 @@ btn_tile_sheet_row = 1 btn_tile_sheet_column = 4 vary_img_by_era = 1 vary_img_by_culture = 1 -advance_prereq = Currency +advance_prereqs = Currency dependent_improvs = Marketplace, Bank, "Stock Exchange" buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 @@ -114,7 +114,7 @@ btn_tile_sheet_row = 1 btn_tile_sheet_column = 5 vary_img_by_era = 1 vary_img_by_culture = 0 -advance_prereq = Industrialization +advance_prereqs = Industrialization dependent_improvs = Factory, "Manufacturing Plant" buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 @@ -134,7 +134,7 @@ btn_tile_sheet_row = 1 btn_tile_sheet_column = 6 vary_img_by_era = 1 vary_img_by_culture = 0 -advance_prereq = Computers +advance_prereqs = Computers dependent_improvs = "Research Lab" buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 @@ -154,7 +154,7 @@ btn_tile_sheet_row = 1 btn_tile_sheet_column = 7 vary_img_by_era = 0 vary_img_by_culture = 0 -advance_prereq = Miniaturization +advance_prereqs = Miniaturization dependent_improvs = Offshore Platform buildable_on = coast defense_bonus_percent = 0 @@ -174,7 +174,7 @@ btn_tile_sheet_row = 1 btn_tile_sheet_column = 8 vary_img_by_era = 1 vary_img_by_culture = 0 -advance_prereq = Engineering +advance_prereqs = Engineering dependent_improvs = buildable_on = forest defense_bonus_percent = 0 @@ -194,7 +194,7 @@ btn_tile_sheet_row = 1 btn_tile_sheet_column = 9 vary_img_by_era = 0 vary_img_by_culture = 0 -advance_prereq = Electricity +advance_prereqs = Electricity dependent_improvs = buildable_on = snow-mountains custom_height = 88 @@ -211,7 +211,7 @@ happiness_bonus = 0 name = Neighborhood tooltip = Build Neighborhood buildable_on = desert,plains,grassland,tundra,floodplain,hills -advance_prereq = +advance_prereqs = auto_add_road = 1 defense_bonus_percent = 25 allow_multiple = 1 @@ -226,7 +226,7 @@ happiness_bonus = 0 name = Wonder District tooltip = Build Wonder District buildable_on = desert,plains,grassland,tundra,floodplain,hills,coast -advance_prereq = +advance_prereqs = defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 0 @@ -240,7 +240,7 @@ happiness_bonus = 0 name = Distribution Hub tooltip = Build Distribution Hub buildable_on = desert,plains,grassland,tundra,floodplain,hills -advance_prereq = Construction +advance_prereqs = Construction defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 0 @@ -254,7 +254,7 @@ happiness_bonus = 0 name = Aerodrome tooltip = Build Aerodrome buildable_on = desert,plains,grassland,tundra,floodplain,hills -advance_prereq = Flight +advance_prereqs = Flight dependent_improvs = Airport defense_bonus_percent = 0 allow_multiple = 1 @@ -269,7 +269,7 @@ happiness_bonus = 0 name = Port tooltip = Build Port buildable_on = coast -advance_prereq = Map Making +advance_prereqs = Map Making dependent_improvs = Harbor, "Commercial Dock" heal_units_in_one_turn = 1 defense_bonus_percent = 0 @@ -288,7 +288,7 @@ btn_tile_sheet_row = 0 btn_tile_sheet_column = 5 vary_img_by_era = 1 vary_img_by_culture = 1 -advance_prereq = Steam Power +advance_prereqs = Steam Power resource_prereqs = Iron, Coal dependent_improvs = "Mass Transit System" buildable_on = desert,plains,grassland,tundra,floodplain,hills @@ -310,7 +310,7 @@ btn_tile_sheet_column = 6 vary_img_by_era = 1 vary_img_by_culture = 1 custom_height = 84 -advance_prereq = Industrialization +advance_prereqs = Industrialization dependent_improvs = "Coal Plant", "Hydro Plant", "Solar Plant", "Nuclear Plant" buildable_on = desert,plains,grassland,tundra,floodplain,hills,river defense_bonus_percent = 0 @@ -327,7 +327,7 @@ name = Bridge tooltip = Build Bridge btn_tile_sheet_row = 0 btn_tile_sheet_column = 7 -advance_prereq = Industrialization +advance_prereqs = Industrialization buildable_on = coast defense_bonus_percent = 0 allow_multiple = 1 @@ -343,7 +343,7 @@ name = Canal tooltip = Build Canal btn_tile_sheet_row = 0 btn_tile_sheet_column = 8 -advance_prereq = Industrialization +advance_prereqs = Industrialization buildable_on = desert,plains,grassland,tundra,floodplain defense_bonus_percent = 0 allow_multiple = 1 diff --git a/injected_code.c b/injected_code.c index d6b7c3e6..357eb276 100644 --- a/injected_code.c +++ b/injected_code.c @@ -4470,6 +4470,7 @@ is_wonder_or_small_wonder_already_being_built (City * city, int improv_id) return false; Improvement * improv = &p_bic_data->Improvements[improv_id]; + bool is_wonder = (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) != 0; if ((improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) == 0) return false; @@ -4487,6 +4488,112 @@ is_wonder_or_small_wonder_already_being_built (City * city, int improv_id) return false; } +struct district_building_prereq_list * +get_district_building_prereq_list (int improv_id) +{ + if (improv_id < 0) + return NULL; + + int stored = 0; + if (! itable_look_up (&is->district_building_prereqs, improv_id, &stored)) + return NULL; + return (struct district_building_prereq_list *)(long)stored; +} + +bool +district_building_prereq_list_contains (struct district_building_prereq_list * list, int district_id) +{ + if ((list == NULL) || (district_id < 0)) + return false; + for (int i = 0; i < list->count; i++) { + if (list->district_ids[i] == district_id) + return true; + } + return false; +} + +void +add_district_building_prereq (int improv_id, int district_id) +{ + if ((improv_id < 0) || (district_id < 0)) + return; + + struct district_building_prereq_list * list = get_district_building_prereq_list (improv_id); + if (list == NULL) { + list = calloc (1, sizeof *list); + if (list == NULL) + return; + list->count = 0; + itable_insert (&is->district_building_prereqs, improv_id, (int)(long)list); + } + + if (district_building_prereq_list_contains (list, district_id)) + return; + + if (list->count >= ARRAY_LEN (list->district_ids)) + return; + + list->district_ids[list->count++] = district_id; +} + +bool +city_has_river_district (City * city, int district_id) +{ + if (city == NULL) + return false; + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { + if (wai.district_inst->district_id != district_id) + continue; + if (wai.tile->vtable->m37_Get_River_Code (wai.tile) != 0) + return true; + } + return false; +} + +bool +city_has_any_prereq_district_for_improvement (City * city, + struct district_building_prereq_list * list, + bool requires_river, + bool allow_wonder_district) +{ + if ((city == NULL) || (list == NULL)) + return false; + + for (int i = 0; i < list->count; i++) { + int district_id = list->district_ids[i]; + if (district_id < 0) + continue; + if (! allow_wonder_district && district_id == WONDER_DISTRICT_ID) + continue; + if (requires_river) { + if (city_has_river_district (city, district_id)) + return true; + } else if (city_has_required_district (city, district_id)) { + return true; + } + } + return false; +} + +int +pick_missing_district_for_improvement (City * city, struct district_building_prereq_list * list) +{ + if ((list == NULL) || (list->count <= 0)) + return -1; + + int fallback = -1; + for (int i = 0; i < list->count; i++) { + int district_id = list->district_ids[i]; + if (district_id < 0) + continue; + if (fallback < 0) + fallback = district_id; + if ((city != NULL) && leader_can_build_district (&leaders[city->Body.CivID], district_id)) + return district_id; + } + return fallback; +} + bool district_is_complete(Tile * tile, int district_id) @@ -4586,9 +4693,8 @@ district_is_complete(Tile * tile, int district_id) // Check if city has pending building order that depends on this district int pending_improv_id; if (lookup_pending_building_order (requesting_city, &pending_improv_id)) { - int prereq_district_id; - if (itable_look_up (&is->district_building_prereqs, pending_improv_id, &prereq_district_id)) { - if (prereq_district_id == district_id) { + struct district_building_prereq_list * prereq_list = get_district_building_prereq_list (pending_improv_id); + if (district_building_prereq_list_contains (prereq_list, district_id)) { snprintf (ss, sizeof ss, "City %s setting production to improvement %d\n", requesting_city->Body.CityName, pending_improv_id); (*p_OutputDebugStringA) (ss); @@ -4612,7 +4718,6 @@ district_is_complete(Tile * tile, int district_id) // Clear the pending building order forget_pending_building_order (requesting_city); - } } } @@ -5388,10 +5493,13 @@ free_dynamic_district_config (struct district_config * cfg) free ((void *)cfg->tooltip); cfg->tooltip = NULL; } - if (cfg->advance_prereq != NULL) { - free ((void *)cfg->advance_prereq); - cfg->advance_prereq = NULL; + for (int i = 0; i < ARRAY_LEN (cfg->advance_prereqs); i++) { + if (cfg->advance_prereqs[i] != NULL) { + free ((void *)cfg->advance_prereqs[i]); + cfg->advance_prereqs[i] = NULL; + } } + cfg->advance_prereq_count = 0; if (cfg->obsoleted_by != NULL) { free ((void *)cfg->obsoleted_by); cfg->obsoleted_by = NULL; @@ -5552,10 +5660,17 @@ free_special_district_override_strings (struct district_config * cfg, struct dis free ((void *)cfg->tooltip); cfg->tooltip = NULL; } - if ((cfg->advance_prereq != NULL) && (cfg->advance_prereq != defaults->advance_prereq)) { - free ((void *)cfg->advance_prereq); - cfg->advance_prereq = NULL; + for (int i = 0; i < ARRAY_LEN (cfg->advance_prereqs); i++) { + char const * default_value = (defaults != NULL && i < defaults->advance_prereq_count) + ? defaults->advance_prereqs[i] + : NULL; + if ((cfg->advance_prereqs[i] != NULL) && + (cfg->advance_prereqs[i] != default_value)) { + free ((void *)cfg->advance_prereqs[i]); + } + cfg->advance_prereqs[i] = NULL; } + cfg->advance_prereq_count = 0; if ((cfg->obsoleted_by != NULL) && (cfg->obsoleted_by != defaults->obsoleted_by)) { free ((void *)cfg->obsoleted_by); cfg->obsoleted_by = NULL; @@ -5768,10 +5883,13 @@ free_parsed_district_definition (struct parsed_district_definition * def) free (def->tooltip); def->tooltip = NULL; } - if (def->advance_prereq != NULL) { - free (def->advance_prereq); - def->advance_prereq = NULL; + for (int i = 0; i < def->advance_prereq_count; i++) { + if (def->advance_prereqs[i] != NULL) { + free (def->advance_prereqs[i]); + def->advance_prereqs[i] = NULL; + } } + def->advance_prereq_count = 0; for (int i = 0; i < ARRAY_LEN (def->buildable_on_districts); i++) { if (def->buildable_on_districts[i] != NULL) { free (def->buildable_on_districts[i]); @@ -6270,11 +6388,24 @@ override_special_district_from_definition (struct parsed_district_definition * d def->tooltip = NULL; } - if (def->has_advance_prereq) { - if ((cfg->advance_prereq != NULL) && (cfg->advance_prereq != defaults->advance_prereq)) - free ((void *)cfg->advance_prereq); - cfg->advance_prereq = def->advance_prereq; - def->advance_prereq = NULL; + if (def->has_advance_prereqs) { + for (int i = 0; i < ARRAY_LEN (cfg->advance_prereqs); i++) { + char const * default_value = (i < defaults->advance_prereq_count) ? defaults->advance_prereqs[i] : NULL; + if ((cfg->advance_prereqs[i] != NULL) && + (cfg->advance_prereqs[i] != default_value)) + free ((void *)cfg->advance_prereqs[i]); + cfg->advance_prereqs[i] = NULL; + } + + cfg->advance_prereq_count = def->advance_prereq_count; + const int max_entries = ARRAY_LEN (cfg->advance_prereqs); + if (cfg->advance_prereq_count > max_entries) + cfg->advance_prereq_count = max_entries; + for (int i = 0; i < cfg->advance_prereq_count; i++) { + cfg->advance_prereqs[i] = def->advance_prereqs[i]; + def->advance_prereqs[i] = NULL; + } + def->advance_prereq_count = 0; } if (def->has_obsoleted_by) { @@ -6619,9 +6750,16 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.tooltip = strdup (buffer); } - if (def->has_advance_prereq) { - new_cfg.advance_prereq = def->advance_prereq; - def->advance_prereq = NULL; + if (def->has_advance_prereqs) { + new_cfg.advance_prereq_count = def->advance_prereq_count; + const int max_entries = ARRAY_LEN (new_cfg.advance_prereqs); + if (new_cfg.advance_prereq_count > max_entries) + new_cfg.advance_prereq_count = max_entries; + for (int i = 0; i < new_cfg.advance_prereq_count; i++) { + new_cfg.advance_prereqs[i] = def->advance_prereqs[i]; + def->advance_prereqs[i] = NULL; + } + def->advance_prereq_count = 0; } if (def->has_obsoleted_by) { @@ -6874,13 +7012,30 @@ handle_district_definition_key (struct parsed_district_definition * def, def->tooltip = copy_trimmed_string_or_null (value, 1); def->has_tooltip = true; - } else if (slice_matches_str (key, "advance_prereq")) { - if (def->advance_prereq != NULL) { - free (def->advance_prereq); - def->advance_prereq = NULL; + } else if (slice_matches_str (key, "advance_prereqs") || slice_matches_str (key, "advance_prereq")) { + for (int i = 0; i < def->advance_prereq_count; i++) { + if (def->advance_prereqs[i] != NULL) { + free (def->advance_prereqs[i]); + def->advance_prereqs[i] = NULL; + } + } + def->advance_prereq_count = 0; + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->advance_prereqs, + ARRAY_LEN (def->advance_prereqs), + &list_count, + parse_errors, + line_number, + "advance_prereqs")) { + def->advance_prereq_count = list_count; + def->has_advance_prereqs = true; + } else { + def->advance_prereq_count = 0; + def->has_advance_prereqs = false; } - def->advance_prereq = copy_trimmed_string_or_null (value, 1); - def->has_advance_prereq = true; + free (value_text); } else if (slice_matches_str (key, "obsoleted_by")) { if (def->obsoleted_by != NULL) { @@ -8765,7 +8920,9 @@ void parse_building_and_tech_ids () char const * district_name = (is->district_configs[i].name != NULL) ? is->district_configs[i].name : "District"; if (is->district_configs[i].command != 0) itable_insert (&is->command_id_to_district_id, is->district_configs[i].command, i); - is->district_infos[i].advance_prereq_id = -1; + is->district_infos[i].advance_prereq_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].advance_prereq_ids); j++) + is->district_infos[i].advance_prereq_ids[j] = -1; is->district_infos[i].obsoleted_by_id = -1; is->district_infos[i].resource_prereq_count = 0; for (int j = 0; j < MAX_DISTRICT_DEPENDENTS; j++) @@ -8779,21 +8936,28 @@ void parse_building_and_tech_ids () is->district_infos[i].natural_wonder_prereq_ids[j] = -1; // Map advance prereqs to districts - if (is->district_configs[i].advance_prereq != NULL && is->district_configs[i].advance_prereq != "") { + int stored_tech_count = 0; + for (int j = 0; j < is->district_configs[i].advance_prereq_count; j++) { + char const * prereq = is->district_configs[i].advance_prereqs[j]; + if (prereq == NULL || prereq[0] == '\0') + continue; int tech_id; - struct string_slice tech_name = { .str = (char *)is->district_configs[i].advance_prereq, .len = (int)strlen (is->district_configs[i].advance_prereq) }; + struct string_slice tech_name = { .str = (char *)prereq, .len = (int)strlen (prereq) }; if (find_game_object_id_by_name (GOK_TECHNOLOGY, &tech_name, 0, &tech_id)) { - snprintf (ss, sizeof ss, "Found tech prereq \"%.*s\" for district \"%s\", ID %d\n", tech_name.len, tech_name.str, is->district_configs[i].advance_prereq, tech_id); + snprintf (ss, sizeof ss, "Found tech prereq \"%.*s\" for district \"%s\", ID %d\n", tech_name.len, tech_name.str, district_name, tech_id); (*p_OutputDebugStringA) (ss); - is->district_infos[i].advance_prereq_id = tech_id; + if (stored_tech_count < ARRAY_LEN (is->district_infos[i].advance_prereq_ids)) { + is->district_infos[i].advance_prereq_ids[stored_tech_count] = tech_id; + stored_tech_count++; + } itable_insert (&is->district_tech_prereqs, tech_id, i); } else { - is->district_infos[i].advance_prereq_id = -1; struct error_line * err = add_error_line (&district_parse_errors); - snprintf (err->text, sizeof err->text, "^ District \"%s\": advance_prereq \"%.*s\" not found", district_name, tech_name.len, tech_name.str); + snprintf (err->text, sizeof err->text, "^ District \"%s\": advance_prereqs entry \"%.*s\" not found", district_name, tech_name.len, tech_name.str); err->text[(sizeof err->text) - 1] = '\0'; } } + is->district_infos[i].advance_prereq_count = stored_tech_count; // Map obsoleted_by to tech ID if (is->district_configs[i].obsoleted_by != NULL && is->district_configs[i].obsoleted_by != "") { @@ -8946,7 +9110,7 @@ void parse_building_and_tech_ids () is->district_infos[i].dependent_building_ids[stored_count] = improv_id; stored_count += 1; } - itable_insert (&is->district_building_prereqs, improv_id, i); + add_district_building_prereq (improv_id, i); stable_insert (&is->building_name_to_id, improv_name.str, improv_id); } else { is->district_infos[i].dependent_building_ids[j] = -1; @@ -9933,6 +10097,11 @@ reset_district_state (bool reset_tile_map) clear_highlighted_worker_tiles_for_districts (); table_deinit (&is->district_tech_prereqs); + FOR_TABLE_ENTRIES (tei, &is->district_building_prereqs) { + struct district_building_prereq_list * list = (struct district_building_prereq_list *)(long)tei.value; + if (list != NULL) + free (list); + } table_deinit (&is->district_building_prereqs); table_deinit (&is->command_id_to_district_id); stable_deinit (&is->building_name_to_id); @@ -9959,7 +10128,9 @@ reset_district_state (bool reset_tile_map) is->district_count = is->special_district_count; for (int i = 0; i < COUNT_DISTRICT_TYPES; i++) { - is->district_infos[i].advance_prereq_id = -1; + is->district_infos[i].advance_prereq_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].advance_prereq_ids); j++) + is->district_infos[i].advance_prereq_ids[j] = -1; is->district_infos[i].obsoleted_by_id = -1; is->district_infos[i].resource_prereq_count = 0; for (int j = 0; j < ARRAY_LEN (is->district_infos[i].resource_prereq_ids); j++) @@ -12385,9 +12556,11 @@ leader_can_natively_build_district (Leader * leader, int district_id) struct district_config const * cfg = &is->district_configs[district_id]; struct district_infos const * info = &is->district_infos[district_id]; - int prereq_id = info->advance_prereq_id; - if ((prereq_id >= 0) && ! Leader_has_tech (leader, __, prereq_id)) - return false; + for (int i = 0; i < info->advance_prereq_count; i++) { + int prereq_id = info->advance_prereq_ids[i]; + if ((prereq_id >= 0) && ! Leader_has_tech (leader, __, prereq_id)) + return false; + } int obsolete_id = info->obsoleted_by_id; if ((obsolete_id >= 0) && Leader_has_tech (leader, __, obsolete_id)) return false; @@ -12544,7 +12717,7 @@ leader_can_build_district (Leader * leader, int district_id) if (cfg->buildable_by_pact_allies && leader_has_pact_ally_district_access (leader, district_id)) return true; - return false; + return can_natively_build; } Tile * @@ -13390,61 +13563,53 @@ city_has_active_wonder_for_district (City * city) bool city_requires_district_for_improvement (City * city, int improv_id, int * out_district_id) { - int district_id; - if (! itable_look_up (&is->district_building_prereqs, improv_id, &district_id)) + struct district_building_prereq_list * prereq_list = get_district_building_prereq_list (improv_id); + if ((prereq_list == NULL) || (prereq_list->count <= 0)) return false; Improvement * improv = &p_bic_data->Improvements[improv_id]; // Special logic for handling rivers bool requires_river = (improv->ImprovementFlags & ITF_Must_Be_Near_River) != 0; - if (is->current_config.enable_wonder_districts) { - if (district_id == WONDER_DISTRICT_ID) { - if (requires_river) { - bool has_river_wonder_district = false; - FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { - if (wai.district_inst->district_id != WONDER_DISTRICT_ID) - continue; - if (wai.tile->vtable->m37_Get_River_Code (wai.tile) == 0) - continue; - if (! wonder_is_buildable_on_tile (wai.tile, improv_id)) - continue; - struct wonder_district_info * info = get_wonder_district_info (wai.tile); - if (info == NULL) { - has_river_wonder_district = true; - break; - } - if (info->state == WDS_COMPLETED) - continue; - if (info->state == WDS_UNDER_CONSTRUCTION && info->city_id != city->Body.ID) - continue; + bool has_prereq = false; + + if (is->current_config.enable_wonder_districts && + district_building_prereq_list_contains (prereq_list, WONDER_DISTRICT_ID)) { + if (requires_river) { + bool has_river_wonder_district = false; + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { + if (wai.district_inst->district_id != WONDER_DISTRICT_ID) + continue; + if (wai.tile->vtable->m37_Get_River_Code (wai.tile) == 0) + continue; + if (! wonder_is_buildable_on_tile (wai.tile, improv_id)) + continue; + struct wonder_district_info * info = get_wonder_district_info (wai.tile); + if (info == NULL) { has_river_wonder_district = true; break; } - if (has_river_wonder_district) - return false; - } else if (city_has_wonder_district_with_no_completed_wonder (city, improv_id)) - return false; - if (out_district_id != NULL) - *out_district_id = district_id; - return true; - } - } - if (requires_river) { - bool has_river_district = false; - FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { - if (wai.district_inst->district_id != district_id) - continue; - if (wai.tile->vtable->m37_Get_River_Code (wai.tile) != 0) { - has_river_district = true; + if (info->state == WDS_COMPLETED) + continue; + if (info->state == WDS_UNDER_CONSTRUCTION && info->city_id != city->Body.ID) + continue; + has_river_wonder_district = true; break; } + if (has_river_wonder_district) + has_prereq = true; + } else if (city_has_wonder_district_with_no_completed_wonder (city, improv_id)) { + has_prereq = true; } - if (has_river_district) - return false; - } else if (city_has_required_district (city, district_id)) + } + + if (! has_prereq && city_has_any_prereq_district_for_improvement (city, prereq_list, requires_river, !is_wonder)) + has_prereq = true; + + if (has_prereq) return false; + if (out_district_id != NULL) - *out_district_id = district_id; + *out_district_id = pick_missing_district_for_improvement (city, prereq_list); return true; } @@ -20318,15 +20483,33 @@ city_meets_district_prereqs_to_build_improvement (City * city, int i_improv, boo Improvement * improv = &p_bic_data->Improvements[i_improv]; bool requires_river = (improv->ImprovementFlags & ITF_Must_Be_Near_River) != 0; - // Check if the improvement requires a district and output the required district id when it does - int required_district_id = -1; - bool needs_district = city_requires_district_for_improvement (city, i_improv, &required_district_id); + // Check if the improvement requires a district + bool needs_district = city_requires_district_for_improvement (city, i_improv, NULL); + struct district_building_prereq_list * prereq_list = get_district_building_prereq_list (i_improv); // District is either not needed or already built if (! needs_district) return true; - if (! leader_can_build_district (&leaders[city->Body.CivID], required_district_id)) + if (prereq_list == NULL) + return false; + + bool has_buildable_candidate = false; + bool has_wonder_candidate = false; + bool has_non_wonder_candidate = false; + for (int i = 0; i < prereq_list->count; i++) { + int district_id = prereq_list->district_ids[i]; + if (district_id < 0) + continue; + if (! leader_can_build_district (&leaders[city->Body.CivID], district_id)) + continue; + has_buildable_candidate = true; + if (district_id == WONDER_DISTRICT_ID) + has_wonder_candidate = true; + else + has_non_wonder_candidate = true; + } + if (! has_buildable_candidate) return false; bool is_wonder = is->current_config.enable_wonder_districts && improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder); @@ -20337,25 +20520,36 @@ city_meets_district_prereqs_to_build_improvement (City * city, int i_improv, boo bool has_river_terrain_for_district = false; FOR_WORK_AREA_AROUND (wai, city->Body.X, city->Body.Y) { Tile * tile = wai.tile; - if (! tile_suitable_for_district (tile, required_district_id, city, NULL)) - continue; - has_terrain_for_district = true; - if (requires_river && tile->vtable->m37_Get_River_Code (tile) != 0) - has_river_terrain_for_district = true; + for (int i = 0; i < prereq_list->count; i++) { + int district_id = prereq_list->district_ids[i]; + if (district_id < 0) + continue; + if (! leader_can_build_district (&leaders[city->Body.CivID], district_id)) + continue; + if (! tile_suitable_for_district (tile, district_id, city, NULL)) + continue; - if (! is_wonder) - continue; + if (is_wonder && district_id == WONDER_DISTRICT_ID) { + if (! wonder_is_buildable_on_tile (tile, i_improv)) + continue; + } - if (wonder_is_buildable_on_tile (tile, i_improv)) { - if (! requires_river || tile->vtable->m37_Get_River_Code (tile) != 0) { - has_terrain_for_wonder = true; - break; + has_terrain_for_district = true; + if (requires_river && tile->vtable->m37_Get_River_Code (tile) != 0) + has_river_terrain_for_district = true; + + if (is_wonder && district_id == WONDER_DISTRICT_ID) { + if (! requires_river || tile->vtable->m37_Get_River_Code (tile) != 0) { + has_terrain_for_wonder = true; + } } - } + } } - if (! has_terrain_for_district || (requires_river && ! has_river_terrain_for_district) || - (is_wonder && ! has_terrain_for_wonder)) { + bool requires_wonder_terrain = is_wonder && has_wonder_candidate && ! has_non_wonder_candidate; + if (! has_terrain_for_district || + (requires_river && ! has_river_terrain_for_district) || + (requires_wonder_terrain && ! has_terrain_for_wonder)) { return false; } @@ -20366,8 +20560,12 @@ city_meets_district_prereqs_to_build_improvement (City * city, int i_improv, boo // If AI already has a pending district request for this required district, return false // to prevent wasting a turn trying to choose this improvement - if (find_pending_district_request (city, required_district_id) != NULL) { - return false; + for (int i = 0; i < prereq_list->count; i++) { + int district_id = prereq_list->district_ids[i]; + if (district_id < 0) + continue; + if (find_pending_district_request (city, district_id) != NULL) + return false; } // Superficially allow the AI to choose the improvement for scoring and production. @@ -22518,12 +22716,17 @@ patch_City_add_or_remove_improvement (City * this, int edx, int improv_id, int a if ((! is->is_placing_scenario_things) && add && is->current_config.enable_districts && (allow_building_sharing || allow_wonder_sharing) && (! is->sharing_buildings_by_districts_in_progress)) { - int required_district_id; - if (itable_look_up (&is->district_building_prereqs, improv_id, &required_district_id)) { + struct district_building_prereq_list * prereq_list = get_district_building_prereq_list (improv_id); + if (prereq_list != NULL) { bool is_wonder = (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) != 0; if ((! is_wonder && allow_building_sharing) || (is_wonder && allow_wonder_sharing)) { is->sharing_buildings_by_districts_in_progress = true; - copy_building_with_cities_in_radius (this, improv_id, required_district_id, x, y); + for (int i = 0; i < prereq_list->count; i++) { + int district_id = prereq_list->district_ids[i]; + if (district_id < 0) + continue; + copy_building_with_cities_in_radius (this, improv_id, district_id, x, y); + } is->sharing_buildings_by_districts_in_progress = false; } } @@ -24968,8 +25171,8 @@ patch_Leader_do_production_phase (Leader * this) bool needs_halt = false; // Check buildings & wonders dependent on districts - if (itable_look_up (&is->district_building_prereqs, i_improv, &req_district_id)) { - if (! city_has_required_district (city, req_district_id)) { + if (city_requires_district_for_improvement (city, i_improv, &req_district_id)) { + if (req_district_id >= 0) { needs_halt = true; district_description = is->district_configs[req_district_id].name; } @@ -24987,9 +25190,8 @@ patch_Leader_do_production_phase (Leader * this) (*p_OutputDebugStringA) (ss); bool wonder_requires_district = false; if (i_improv >= 0) { - int required_id; - if (itable_look_up (&is->district_building_prereqs, i_improv, &required_id) && - (required_id == WONDER_DISTRICT_ID)) + struct district_building_prereq_list * prereq_list = get_district_building_prereq_list (i_improv); + if (district_building_prereq_list_contains (prereq_list, WONDER_DISTRICT_ID)) wonder_requires_district = true; } @@ -30073,7 +30275,6 @@ init_district_images () // Read PCX file snprintf (art_dir, sizeof art_dir, "Districts/1200/%s", cfg->img_paths[variant_i]); get_mod_art_path (art_dir, temp_path, sizeof temp_path); - PCX_Image_read_file (&pcx, __, temp_path, NULL, 0, 0x100, 2); if (pcx.JGL.Image == NULL) { @@ -31314,7 +31515,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - *p_debug_mode_bits |= 0xC; + //*p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -31776,7 +31977,7 @@ patch_Unit_ai_move_terraformer (Unit * this) } struct district_worker_record * rec = get_tracked_worker_record (this); - if (rec->pending_req != NULL) { + if (rec != NULL && rec->pending_req != NULL) { if (ai_move_district_worker (this, rec)) return; } From 23c905e3f1442c7d57e138598805cd2fdedbf037 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 26 Jan 2026 10:30:01 -0800 Subject: [PATCH 265/356] Add negative district yield icon art & pcx loading --- Art/Districts/DistrictIncomeIcons.pcx | Bin 12498 -> 12847 bytes C3X.h | 9 ++++- injected_code.c | 56 +++++++++++++++++++------- 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/Art/Districts/DistrictIncomeIcons.pcx b/Art/Districts/DistrictIncomeIcons.pcx index e4475906d36105d237ceaec5a468a8fa3396d43d..8468deae2a046748bf8a19a49c32dfc1216a8521 100644 GIT binary patch delta 2597 zcmZvePly{;9LGt!wH7I*)!l7FyJ9VCYe^~WMT0qck*f5vr3e3@66Dc(@#sMoY%zEm zC4s1v@g{|~C>_CrGTYyK$!3!5R$NDBOd+T>hhjqKkkYc*bo~C_n`GD3Lw1vS^Sf&rnH?dD}+K3^_SYm?Gx8l05k z0u5%25_=4sezuy)(z=s42pm!LLbtdTfv3XhF@=CRH5rgcHGK20%fO|{+89k-rf#0P zP;EF@cYm6iWRIw)d^=y5mwAo?Rw%x@=K#BhjjUy5W;Qb`GskF{IL<84?!cFh4p4{y zu5JZm4{lJH%&GGnPx`L1T?!WIu1uI3I&4+y!qq`)Rv=2IX6 zN_PNl%xh>n5Z4^CA;J_?j?>+HDK*863VhgE=;r1*o7uxlkMB2pYuSwIXHZ-Z5Ygcl zzL0ZEF<+Yk4DHBR_O03Y38ieWVxB>V!wdN10xg=|9SVM`Ev`)3E(7i?H!&+7$ zA!jpk_CKuBu(m$R^n+Yu72{KddvAM{aq@(lw>Iuf&wThDpcm? zneo~|T`#((5p1}Jn8U!tYZE!{#cgz0q&Rs=O!cN^Bz+lziI1N#Y{^!x4ewl=i+W7T z_6?u_t(+|Iu8ov){E$jbfKe2qnW+U+3K|(*u(Ey{D6Bndfiinap3i(p!;LN|S^7^F zMl^9~EMf|qknCIfd^#jD+D}IF;iwH?OBmW<6q9I?2_^fP#KyV!q?cM?M(Vg&!gtH; zBK7i>xiPSqq5Eo`20xy}#u7XD5E)+2A3KkihofVJa~>xTrc(q~b3kfwlp~IhKwC31 z6sM+XJ4byk9aPaKPX+x#_l&8kzybOkndIDl$~Cn4GJ=CD#ddK8h?EYxaYmew1Juy1 z3o~$LSqD!d`y4fN_{imSGCjzvBO8?iD{gIQZKUkUctR^bjjg2RbPR{ zyr<)Pcaa;R^w79*?k%Fd-1(PhcM#&;_o%9(uc&$jYu22zvsUUQh_Q~KE{YH*N(Da> z9M1a{kTqj-O&Z<6Fl98Pi)Dp#skrjY0q2#Y=TgscnKY9d)>7QCZbE`DKKvQ|<|0n+ r2_AZz$G4~A-^bRr*W#7p@6S%|9^Gad5NH(l9uGgeG2sl32NVAQVX_{n delta 2403 zcmYk8e`p)$8OJRxn`Ev`wd_%)uB@u-JT9jT4ng|kCjW5~^SSTy>;9hj|8kH@5&mnNfkdtw^U1C z%ma>88SeV!dGLB--WW9-oi961SJ~>)>Jm+?!V=$^13ZtS6!+e51$a`!0^ zzzebdm~ip2-*N_0U)xB)(nMl)iPUjMlVgnR4nT6A?Dn-$O!7dnWQFXR9HhdgUaG`9 zXZidp>jjVCp@O?j5|ze53eQLa1fp%8!gyCG1qSG8&>>yY)2cqHk2%0}e*aHRjBUEL zmROpA1a}Cx!Sxs;Uq6#1jC@S)w)|0LBc!2#+%NZoERy|^N)u=FT6~Hxs-g!XUKGV8 zxR2O$%QI4-%MyY^mmp(nQwVnlrn5~F72`9ix+PTsRX+-5(c z{vYZ7KyrZG?`y>$THQY@!&xMH;iODdHWm z@0Uo>P-J*OWq61l+P48Rf3V^Q_VLyIY450)x;@}J*3v}^RE5DnD_#nrXlOW#Vkq2d zL@Wm*!_#11?RlT;0o#}?TjU5BrB?)7w1^x~N-}%+dJ|`7jfd>&(gsR%e2|+jGvqlu zKSuWEd4;oy%(DtfNx3$Q|_8KRFiJSt=f%;JcL=HUyd&zAVbT~SkEwrfQstQsxZ|Bsx z5QP1qtOj01Q$SNRL=rV+Qs-P_eU{@%T*eFT7GCdg#TsOL5Kys1dF9yCKfscVB?goe za8iC1i>+;X_VSz+U${KVBNo?}HE-dBJ4PB)PUvttoIK`GB&n+?1S}zN;IoiFFDAs3I1}3gN3OBAFJRWqy*10_t+1;e==He0>bFI< zVTZOon@b*rT@3Tv^Ldg_ z14VW6)S^L|x%uNiI)05sX#+dMJ-WlIGygKQUqQp|pMmQvvz*~Ay2^3{W8Zw|FYoZN z8Y^aCIkOCzJZ)u^sSgFB;k2%ST;%4Y2&#UyXkHe763)(_8oFV| Icount <= 0)) return false; Improvement * improv = &p_bic_data->Improvements[improv_id]; + bool is_wonder = false; // Special logic for handling rivers bool requires_river = (improv->ImprovementFlags & ITF_Must_Be_Near_River) != 0; @@ -13574,6 +13575,7 @@ city_requires_district_for_improvement (City * city, int improv_id, int * out_di if (is->current_config.enable_wonder_districts && district_building_prereq_list_contains (prereq_list, WONDER_DISTRICT_ID)) { + is_wonder = true; if (requires_river) { bool has_river_wonder_district = false; FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { @@ -18699,39 +18701,39 @@ init_district_icons () goto cleanup; } - // Extract science icon (index 1: x = 1 + 1*31 = 32, width 30) + // Extract science icon (index 1) Sprite_construct (&is->district_science_icon); Sprite_slice_pcx (&is->district_science_icon, __, &pcx, 1 + 1*31, 1, 30, 30, 1, 1); - // Extract commerce icon (index 2: x = 1 + 2*31 = 63, width 30) + // Extract commerce icon (index 2) Sprite_construct (&is->district_commerce_icon); Sprite_slice_pcx (&is->district_commerce_icon, __, &pcx, 1 + 2*31, 1, 30, 30, 1, 1); - // Extract shield icon (index 4: x = 1 + 4*31 = 125, width 30) + // Extract shield icon (index 4) Sprite_construct (&is->district_shield_icon); Sprite_slice_pcx (&is->district_shield_icon, __, &pcx, 1 + 4*31, 1, 30, 30, 1, 1); - // Extract corruption icon (index 5: x = 1 + 5*31 = 156, width 30) + // Extract corruption icon (index 5) Sprite_construct (&is->district_corruption_icon); Sprite_slice_pcx (&is->district_corruption_icon, __, &pcx, 1 + 5*31, 1, 30, 30, 1, 1); - // Extract food icon (index 6: x = 1 + 6*31 = 187, width 30) + // Extract food icon (index 6) Sprite_construct (&is->district_food_icon); Sprite_slice_pcx (&is->district_food_icon, __, &pcx, 1 + 6*31, 1, 30, 30, 1, 1); - // Extract food eaten icon (index 7: x = 1 + 7*31 = 218, width 30) + // Extract food eaten icon (index 7) Sprite_construct (&is->district_food_eaten_icon); Sprite_slice_pcx (&is->district_food_eaten_icon, __, &pcx, 1 + 7*31, 1, 30, 30, 1, 1); - // Extract happiness icon (index 12: x = 1 + 12*31 = 373, width 30) - Sprite_construct (&is->district_happiness_icon); - Sprite_slice_pcx (&is->district_happiness_icon, __, &pcx, 1 + 12*31, 1, 30, 30, 1, 1); + // Extract small happiness icon (index 12) + Sprite_construct (&is->district_happiness_icon_small); + Sprite_slice_pcx (&is->district_happiness_icon_small, __, &pcx, 1 + 12*31, 1, 30, 30, 1, 1); - // Extract small shield icon (index 13: x = 1 + 13*31 = 404, width 30) + // Extract small shield icon (index 13) Sprite_construct (&is->district_shield_icon_small); Sprite_slice_pcx (&is->district_shield_icon_small, __, &pcx, 1 + 13*31, 1, 30, 30, 1, 1); - // Extract small commerce icon (index 14: x = 1 + 14*31 = 435, width 30) + // Extract small commerce icon (index 14) Sprite_construct (&is->district_commerce_icon_small); Sprite_slice_pcx (&is->district_commerce_icon_small, __, &pcx, 1 + 14*31, 1, 30, 30, 1, 1); @@ -18739,14 +18741,40 @@ init_district_icons () Sprite_construct (&is->district_food_icon_small); Sprite_slice_pcx (&is->district_food_icon_small, __, &pcx, 1 + 15*31, 1, 30, 30, 1, 1); - // Extract small science icon (index 16: x = 1 + 16*31 = 497, width 30) + // Extract small science icon (index 16) Sprite_construct (&is->district_science_icon_small); Sprite_slice_pcx (&is->district_science_icon_small, __, &pcx, 1 + 16*31, 1, 30, 30, 1, 1); - // Extract small culture icon (index 18: x = 1 + 18*31 = 559, width 30) + // Extract small culture icon (index 18) Sprite_construct (&is->district_culture_icon_small); Sprite_slice_pcx (&is->district_culture_icon_small, __, &pcx, 1 + 18*31, 1, 30, 30, 1, 1); + // Load Negatives (mostly red) from here + + // Extract negative small commerce icon (index 17) + Sprite_construct (&is->district_negative_commerce_icon_small); + Sprite_slice_pcx (&is->district_negative_commerce_icon_small, __, &pcx, 1 + 17*31, 1, 30, 30, 1, 1); + + // Extract small unhappiness icon (index 19) + Sprite_construct (&is->district_unhappiness_icon_small); + Sprite_slice_pcx (&is->district_unhappiness_icon_small, __, &pcx, 1 + 19*31, 1, 30, 30, 1, 1); + + // Extract negative small shield icon (index 20) + Sprite_construct (&is->district_shield_icon_small); + Sprite_slice_pcx (&is->district_shield_icon_small, __, &pcx, 1 + 20*31, 1, 30, 30, 1, 1); + + // Extract negative small culture icon (index 21) + Sprite_construct (&is->district_negative_culture_icon_small); + Sprite_slice_pcx (&is->district_negative_culture_icon_small, __, &pcx, 1 + 21*31, 1, 30, 30, 1, 1); + + // Extract negative small culture icon (index 22) + Sprite_construct (&is->district_negative_food_icon_small); + Sprite_slice_pcx (&is->district_negative_food_icon_small, __, &pcx, 1 + 22*31, 1, 30, 30, 1, 1); + + // Extract negative small science icon (index 23) + Sprite_construct (&is->district_negative_science_icon_small); + Sprite_slice_pcx (&is->district_negative_science_icon_small, __, &pcx, 1 + 23*31, 1, 30, 30, 1, 1); + is->dc_icons_img_state = IS_OK; cleanup: pcx.vtable->destruct (&pcx, __, 0); @@ -29165,7 +29193,7 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s Sprite * commerce_sprite = &is->district_commerce_icon_small; Sprite * science_sprite = &is->district_science_icon_small; Sprite * culture_sprite = &is->district_culture_icon_small; - Sprite * happiness_sprite = &is->district_happiness_icon; + Sprite * happiness_sprite = &is->district_happiness_icon_small; // Determine sprite dimensions int sprite_width = food_sprite->Width3; From d8b919963a55db0e2dc70c4f4415cb70ce837676 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 26 Jan 2026 10:35:28 -0800 Subject: [PATCH 266/356] Update read_int() to allow for negative numbers --- common.c | 73 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/common.c b/common.c index 32ceb8cf..8b1bf260 100644 --- a/common.c +++ b/common.c @@ -549,38 +549,47 @@ extract_slice (struct string_slice const * s) int read_int (struct string_slice const * s, int * out_val) { - struct string_slice trimmed = trim_string_slice (s, 1); - char * str = trimmed.str; - int len = trimmed.len; - - if ((len > 0) && (*str == '-') || ((*str >= '0') && (*str <= '9'))) { - char * end; - int base = 10; - if ((str[0] == '0') && ((str[1] == 'x') || (str[1] == 'X'))) { - base = 16; - str += 2; - len -= 2; - } - int res = strtol (str, &end, base); - if (end == str + len) { - *out_val = res; - return 1; - } else - return 0; - } else if ((len == 4) && - ((0 == strncmp (str, "true", 4)) || - (0 == strncmp (str, "True", 4)) || - (0 == strncmp (str, "TRUE", 4)))) { - *out_val = 1; - return 1; - } else if ((len == 5) && - ((0 == strncmp (str, "false", 5)) || - (0 == strncmp (str, "False", 5)) || - (0 == strncmp (str, "FALSE", 5)))) { - *out_val = 0; - return 1; - } else - return 0; + struct string_slice trimmed = trim_string_slice (s, 1); + char *str = trimmed.str; + int len = trimmed.len; + + if (len > 0 && (*str == '-' || (*str >= '0' && *str <= '9'))) { + char *end; + int base = 10; + + char *p = str; + if (*p == '-') { + p++; + } + + if (len >= 3 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { + base = 16; + } + + int res = strtol(str, &end, base); + + if (end == str + len) { + *out_val = res; + return 1; + } + return 0; + } + else if (len == 4 && + (!strncmp(str, "true", 4) || + !strncmp(str, "True", 4) || + !strncmp(str, "TRUE", 4))) { + *out_val = 1; + return 1; + } + else if (len == 5 && + (!strncmp(str, "false", 5) || + !strncmp(str, "False", 5) || + !strncmp(str, "FALSE", 5))) { + *out_val = 0; + return 1; + } + + return 0; } int From 001cff7ce5fbf5f0d72081858a8d552453738f63 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 26 Jan 2026 11:49:52 -0800 Subject: [PATCH 267/356] Add logic and UI handling for negative bonus values --- injected_code.c | 83 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/injected_code.c b/injected_code.c index 9f3cf6a6..cc0677a1 100644 --- a/injected_code.c +++ b/injected_code.c @@ -18760,8 +18760,8 @@ init_district_icons () Sprite_slice_pcx (&is->district_unhappiness_icon_small, __, &pcx, 1 + 19*31, 1, 30, 30, 1, 1); // Extract negative small shield icon (index 20) - Sprite_construct (&is->district_shield_icon_small); - Sprite_slice_pcx (&is->district_shield_icon_small, __, &pcx, 1 + 20*31, 1, 30, 30, 1, 1); + Sprite_construct (&is->district_negative_shield_icon_small); + Sprite_slice_pcx (&is->district_negative_shield_icon_small, __, &pcx, 1 + 20*31, 1, 30, 30, 1, 1); // Extract negative small culture icon (index 21) Sprite_construct (&is->district_negative_culture_icon_small); @@ -29176,24 +29176,43 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s int food_bonus = 0, shield_bonus = 0, gold_bonus = 0, science_bonus = 0, culture_bonus = 0, happiness_bonus = 0; get_effective_district_yields (inst, config, &food_bonus, &shield_bonus, &gold_bonus, &science_bonus, &culture_bonus, &happiness_bonus); + int food_pos = food_bonus > 0 ? food_bonus : 0; + int food_neg = food_bonus < 0 ? -food_bonus : 0; + int shield_pos = shield_bonus > 0 ? shield_bonus : 0; + int shield_neg = shield_bonus < 0 ? -shield_bonus : 0; + int gold_pos = gold_bonus > 0 ? gold_bonus : 0; + int gold_neg = gold_bonus < 0 ? -gold_bonus : 0; + int science_pos = science_bonus > 0 ? science_bonus : 0; + int science_neg = science_bonus < 0 ? -science_bonus : 0; + int culture_pos = culture_bonus > 0 ? culture_bonus : 0; + int culture_neg = culture_bonus < 0 ? -culture_bonus : 0; + int happiness_pos = happiness_bonus > 0 ? happiness_bonus : 0; + int happiness_neg = happiness_bonus < 0 ? -happiness_bonus : 0; + int total_yield = 0; - if (food_bonus > 0) total_yield += food_bonus; - if (shield_bonus > 0) total_yield += shield_bonus; - if (gold_bonus > 0) total_yield += gold_bonus; - if (science_bonus > 0) total_yield += science_bonus; - if (culture_bonus > 0) total_yield += culture_bonus; - if (happiness_bonus > 0) total_yield += happiness_bonus; + total_yield += food_pos + food_neg; + total_yield += shield_pos + shield_neg; + total_yield += gold_pos + gold_neg; + total_yield += science_pos + science_neg; + total_yield += culture_pos + culture_neg; + total_yield += happiness_pos + happiness_neg; if (total_yield <= 0) return; // Get sprites - Sprite * food_sprite = &is->district_food_icon_small; - Sprite * shield_sprite = &is->district_shield_icon_small; - Sprite * commerce_sprite = &is->district_commerce_icon_small; - Sprite * science_sprite = &is->district_science_icon_small; - Sprite * culture_sprite = &is->district_culture_icon_small; - Sprite * happiness_sprite = &is->district_happiness_icon_small; + Sprite * food_sprite = &is->district_food_icon_small; + Sprite * shield_sprite = &is->district_shield_icon_small; + Sprite * commerce_sprite = &is->district_commerce_icon_small; + Sprite * science_sprite = &is->district_science_icon_small; + Sprite * culture_sprite = &is->district_culture_icon_small; + Sprite * happiness_sprite = &is->district_happiness_icon_small; + Sprite * food_negative_sprite = &is->district_negative_food_icon_small; + Sprite * shield_negative_sprite = &is->district_negative_shield_icon_small; + Sprite * commerce_negative_sprite = &is->district_negative_commerce_icon_small; + Sprite * science_negative_sprite = &is->district_negative_science_icon_small; + Sprite * culture_negative_sprite = &is->district_negative_culture_icon_small; + Sprite * happiness_negative_sprite = &is->district_unhappiness_icon_small; // Determine sprite dimensions int sprite_width = food_sprite->Width3; @@ -29225,35 +29244,59 @@ draw_district_yields (City_Form * city_form, Tile * tile, int district_id, int s } // Draw icons in order: shields, food, science, commerce, culture - for (int i = 0; i < shield_bonus; i++) { + for (int i = 0; i < shield_pos; i++) { Sprite_draw (shield_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); pixel_x += spacing; } + for (int i = 0; i < shield_neg; i++) { + Sprite_draw (shield_negative_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); + pixel_x += spacing; + } - for (int i = 0; i < food_bonus; i++) { + for (int i = 0; i < food_pos; i++) { Sprite_draw (food_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); pixel_x += spacing; } + for (int i = 0; i < food_neg; i++) { + Sprite_draw (food_negative_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); + pixel_x += spacing; + } - for (int i = 0; i < science_bonus; i++) { + for (int i = 0; i < science_pos; i++) { Sprite_draw (science_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); pixel_x += spacing; } + for (int i = 0; i < science_neg; i++) { + Sprite_draw (science_negative_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); + pixel_x += spacing; + } - for (int i = 0; i < gold_bonus; i++) { + for (int i = 0; i < gold_pos; i++) { Sprite_draw (commerce_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); pixel_x += spacing; } + for (int i = 0; i < gold_neg; i++) { + Sprite_draw (commerce_negative_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); + pixel_x += spacing; + } - for (int i = 0; i < culture_bonus; i++) { + for (int i = 0; i < culture_pos; i++) { Sprite_draw (culture_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); pixel_x += spacing; } + for (int i = 0; i < culture_neg; i++) { + Sprite_draw (culture_negative_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); + pixel_x += spacing; + } - for (int i = 0; i < happiness_bonus; i++) { + for (int i = 0; i < happiness_pos; i++) { Sprite_draw (happiness_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); pixel_x += spacing; } + for (int i = 0; i < happiness_neg; i++) { + Sprite_draw (happiness_negative_sprite, __, &city_form->Base.Data.Canvas, pixel_x, pixel_y, NULL); + pixel_x += spacing; + } } void From a76a6042f7e0052c4ca52ff3a94effa582b55fa5 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 26 Jan 2026 13:21:59 -0800 Subject: [PATCH 268/356] Allow for bonus resources to be prereqs as well --- C3X.h | 1 - Notes/district_todos.md | 2 +- injected_code.c | 20 ++++++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/C3X.h b/C3X.h index 130408e9..972d552d 100644 --- a/C3X.h +++ b/C3X.h @@ -1981,7 +1981,6 @@ struct district_button_image_set { Sprite district_food_icon_small; Sprite district_science_icon_small; Sprite district_culture_icon_small; - Sprite district_unhappiness_icon_small; Sprite district_negative_shield_icon_small; Sprite district_negative_commerce_icon_small; diff --git a/Notes/district_todos.md b/Notes/district_todos.md index d59675a2..61a5f2d1 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,6 +1,5 @@ - Validate and upfront bridge/canal algorithm - Validate resource generation (add yield?) - - Negative bonuses - Firm up logic for river district rendering - Hoover Dam (use alt dir, special positioning b/c on river) @@ -20,6 +19,7 @@ ## Maritime Districts + - ~~Negative bonuses~~ - ~~Fix scenario art not loading bug~~ - ~~Add advance_prereqs~~ - ~~Allow district OR logic for buildings~~ diff --git a/injected_code.c b/injected_code.c index cc0677a1..f7f34ed0 100644 --- a/injected_code.c +++ b/injected_code.c @@ -14186,6 +14186,26 @@ patch_City_has_resource (City * this, int edx, int resource_id) break; } } + + // A district may require a bonus resource as well. If that's the case, + // check if one is in the work radius + if (! tr) { + int res_class = p_bic_data->ResourceTypes[resource_id].Class; + if ((res_class != RC_Strategic) && (res_class != RC_Luxury)) { + int civ_id = this->Body.CivID; + FOR_TILES_AROUND (tai, is->workable_tile_count, this->Body.X, this->Body.Y) { + Tile * tile = tai.tile; + if ((tile == NULL) || (tile == p_null_tile)) + continue; + if (tile->vtable->m38_Get_Territory_OwnerID (tile) != civ_id) + continue; + if (Tile_get_resource_visible_to (tile, __, civ_id) == resource_id) { + tr = true; + break; + } + } + } + } } return tr; From b5f493e398431c695782a25b584537a616d3056b Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 26 Jan 2026 14:27:57 -0800 Subject: [PATCH 269/356] Working through district resource updates --- C3X.h | 14 ++--- injected_code.c | 151 +++++++++++++++++------------------------------- 2 files changed, 58 insertions(+), 107 deletions(-) diff --git a/C3X.h b/C3X.h index 972d552d..998781dd 100644 --- a/C3X.h +++ b/C3X.h @@ -980,14 +980,12 @@ struct parsed_district_definition { bool has_buildable_on; bool has_resource_prereq_on_tile; bool has_buildable_by_civs; - bool has_buildable_by_war_allies; - bool has_buildable_by_pact_allies; - char * generated_resource; - char * generated_resource_settings[5]; - int generated_resource_settings_count; - bool has_generated_resource; - bool has_generated_resource_settings; - char * buildable_by_civ_traits[10]; + bool has_buildable_by_war_allies; + bool has_buildable_by_pact_allies; + char * generated_resource; + short generated_resource_flags; + bool has_generated_resource; + char * buildable_by_civ_traits[10]; int buildable_by_civ_traits_count; int buildable_by_civ_traits_ids[10]; int buildable_by_civ_traits_id_count; diff --git a/injected_code.c b/injected_code.c index f7f34ed0..4d445f3a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5983,14 +5983,6 @@ free_parsed_district_definition (struct parsed_district_definition * def) def->generated_resource = NULL; } - for (int i = 0; i < def->generated_resource_settings_count; i++) { - if (def->generated_resource_settings[i] != NULL) { - free (def->generated_resource_settings[i]); - def->generated_resource_settings[i] = NULL; - } - } - def->generated_resource_settings_count = 0; - free_bonus_entry_list (&def->culture_bonus_extras); free_bonus_entry_list (&def->science_bonus_extras); free_bonus_entry_list (&def->food_bonus_extras); @@ -6631,20 +6623,7 @@ override_special_district_from_definition (struct parsed_district_definition * d free ((void *)cfg->generated_resource); cfg->generated_resource = def->generated_resource; def->generated_resource = NULL; - cfg->generated_resource_flags = 0; - if (def->has_generated_resource_settings) { - for (int i = 0; i < def->generated_resource_settings_count; i++) { - char * setting = def->generated_resource_settings[i]; - if (strcmp (setting, "local") == 0) - cfg->generated_resource_flags |= MF_LOCAL; - else if (strcmp (setting, "no-tech-req") == 0) - cfg->generated_resource_flags |= MF_NO_TECH_REQ; - else if (strcmp (setting, "show-bonus") == 0) - cfg->generated_resource_flags |= MF_SHOW_BONUS; - else if (strcmp (setting, "hide-non-bonus") == 0) - cfg->generated_resource_flags |= MF_HIDE_NON_BONUS; - } - } + cfg->generated_resource_flags = def->generated_resource_flags; cfg->generated_resource_id = -1; } @@ -6892,20 +6871,7 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i if (def->has_generated_resource) { new_cfg.generated_resource = def->generated_resource; def->generated_resource = NULL; - new_cfg.generated_resource_flags = 0; - if (def->has_generated_resource_settings) { - for (int i = 0; i < def->generated_resource_settings_count; i++) { - char * setting = def->generated_resource_settings[i]; - if (strcmp (setting, "local") == 0) - new_cfg.generated_resource_flags |= MF_LOCAL; - else if (strcmp (setting, "no-tech-req") == 0) - new_cfg.generated_resource_flags |= MF_NO_TECH_REQ; - else if (strcmp (setting, "show-bonus") == 0) - new_cfg.generated_resource_flags |= MF_SHOW_BONUS; - else if (strcmp (setting, "hide-non-bonus") == 0) - new_cfg.generated_resource_flags |= MF_HIDE_NON_BONUS; - } - } + new_cfg.generated_resource_flags = def->generated_resource_flags; new_cfg.generated_resource_id = -1; } else { new_cfg.generated_resource = NULL; @@ -7579,50 +7545,42 @@ handle_district_definition_key (struct parsed_district_definition * def, free (def->generated_resource); def->generated_resource = NULL; } - def->generated_resource = copy_trimmed_string_or_null (value, 1); - def->has_generated_resource = true; + def->generated_resource_flags = 0; - } else if (slice_matches_str (key, "generated_resource_settings")) { char * value_text = trim_and_extract_slice (value, 0); - int list_count = 0; - if (parse_config_string_list (value_text, - def->generated_resource_settings, - ARRAY_LEN (def->generated_resource_settings), - &list_count, - parse_errors, - line_number, - "generated_resource_settings")) { - bool valid = true; - for (int i = 0; i < list_count; i++) { - char * setting = def->generated_resource_settings[i]; - if ((setting == NULL) || - ((strcmp (setting, "local") != 0) && - (strcmp (setting, "no-tech-req") != 0) && - (strcmp (setting, "show-bonus") != 0) && - (strcmp (setting, "hide-non-bonus") != 0))) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: generated_resource_settings (unrecognized value \"%s\")", line_number, (setting != NULL) ? setting : ""); - err->text[(sizeof err->text) - 1] = '\0'; - valid = false; + if ((value_text == NULL) || (*value_text == '\0')) { + def->generated_resource = NULL; + def->has_generated_resource = true; + } else { + char * cursor = value_text; + struct string_slice resource_name = {0}; + struct string_slice token; + bool ok = true; + while (skip_white_space (&cursor) && parse_string (&cursor, &token)) { + if (slice_matches_str (&token, "local")) + def->generated_resource_flags |= MF_LOCAL; + else if (slice_matches_str (&token, "no-tech-req")) + def->generated_resource_flags |= MF_NO_TECH_REQ; + else if (slice_matches_str (&token, "yields")) + def->generated_resource_flags |= MF_YIELDS; + else if (resource_name.str == NULL) + resource_name = token; + else { + ok = false; + break; } } - if (valid) { - def->generated_resource_settings_count = list_count; - def->has_generated_resource_settings = true; + if (! ok || (resource_name.str == NULL)) { + def->generated_resource = NULL; + def->has_generated_resource = false; + def->generated_resource_flags = 0; + add_key_parse_error (parse_errors, line_number, key, value, + "(expected resource name plus optional flags: local, yields, no-tech-req)"); } else { - for (int i = 0; i < ARRAY_LEN (def->generated_resource_settings); i++) { - if (def->generated_resource_settings[i] != NULL) { - free (def->generated_resource_settings[i]); - def->generated_resource_settings[i] = NULL; - } - } - def->generated_resource_settings_count = 0; - def->has_generated_resource_settings = false; + def->generated_resource = extract_slice (&resource_name); + def->has_generated_resource = true; } - } else { - def->generated_resource_settings_count = 0; - def->has_generated_resource_settings = false; } free (value_text); @@ -8964,7 +8922,7 @@ void parse_building_and_tech_ids () int tech_id; struct string_slice tech_name = { .str = (char *)is->district_configs[i].obsoleted_by, .len = (int)strlen (is->district_configs[i].obsoleted_by) }; if (find_game_object_id_by_name (GOK_TECHNOLOGY, &tech_name, 0, &tech_id)) { - snprintf (ss, sizeof ss, "Found tech obsoleted_by \"%.*s\" for district \"%s\", ID %d\n", tech_name.len, tech_name.str, is->district_configs[i].obsoleted_by, tech_id); + snprintf (ss, sizeof ss, "Found tech obsoleted_by \"%.*s\" for district \"%s\", ID %d\n", tech_name.len, tech_name.str, district_name, tech_id); (*p_OutputDebugStringA) (ss); is->district_infos[i].obsoleted_by_id = tech_id; } else { @@ -8983,7 +8941,7 @@ void parse_building_and_tech_ids () int res_id; struct string_slice res_name = { .str = (char *)is->district_configs[i].resource_prereqs[j], .len = (int)strlen (is->district_configs[i].resource_prereqs[j]) }; if (find_game_object_id_by_name (GOK_RESOURCE, &res_name, 0, &res_id)) { - snprintf (ss, sizeof ss, "Found resource prereq \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, is->district_configs[i].resource_prereqs[j], res_id); + snprintf (ss, sizeof ss, "Found resource prereq \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, district_name, res_id); (*p_OutputDebugStringA) (ss); if (stored_res_count < ARRAY_LEN (is->district_infos[i].resource_prereq_ids)) { is->district_infos[i].resource_prereq_ids[stored_res_count] = res_id; @@ -9000,7 +8958,7 @@ void parse_building_and_tech_ids () int res_id; struct string_slice res_name = { .str = (char *)is->district_configs[i].resource_prereq_on_tile, .len = (int)strlen (is->district_configs[i].resource_prereq_on_tile) }; if (find_game_object_id_by_name (GOK_RESOURCE, &res_name, 0, &res_id)) { - snprintf (ss, sizeof ss, "Found on-tile resource prereq \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, is->district_configs[i].resource_prereq_on_tile, res_id); + snprintf (ss, sizeof ss, "Found on-tile resource prereq \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, district_name, res_id); (*p_OutputDebugStringA) (ss); is->district_infos[i].resource_prereq_on_tile_id = res_id; } else { @@ -9080,7 +9038,7 @@ void parse_building_and_tech_ids () int res_id; struct string_slice res_name = { .str = (char *)is->district_configs[i].generated_resource, .len = (int)strlen (is->district_configs[i].generated_resource) }; if (find_game_object_id_by_name (GOK_RESOURCE, &res_name, 0, &res_id)) { - snprintf (ss, sizeof ss, "Found generated resource \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, is->district_configs[i].generated_resource, res_id); + snprintf (ss, sizeof ss, "Found generated resource \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, district_name, res_id); (*p_OutputDebugStringA) (ss); is->district_configs[i].generated_resource_id = res_id; } else { @@ -9104,7 +9062,7 @@ void parse_building_and_tech_ids () struct string_slice improv_name = { .str = (char *)is->district_configs[i].dependent_improvements[j], .len = (int)strlen (is->district_configs[i].dependent_improvements[j]) }; if (find_game_object_id_by_name (GOK_BUILDING, &improv_name, 0, &improv_id)) { - snprintf (ss, sizeof ss, "Found improvement prereq \"%.*s\" for district \"%s\", ID %d\n", improv_name.len, improv_name.str, is->district_configs[i].dependent_improvements[j], improv_id); + snprintf (ss, sizeof ss, "Found improvement prereq \"%.*s\" for district \"%s\", ID %d\n", improv_name.len, improv_name.str, district_name, improv_id); (*p_OutputDebugStringA) (ss); if (stored_count < ARRAY_LEN (is->district_infos[i].dependent_building_ids)) { is->district_infos[i].dependent_building_ids[stored_count] = improv_id; @@ -10207,7 +10165,7 @@ district_resource_prereqs_met (Tile * tile, int tile_x, int tile_y, int district if (owner < 0) return false; - // Check all resource prereqs - ALL must be present (AND logic) + // Check all resource prereqs - ALL must be present for (int i = 0; i < info->resource_prereq_count; i++) { int resource_req = info->resource_prereq_ids[i]; if (resource_req < 0) @@ -11865,6 +11823,18 @@ calculate_city_center_district_bonus (City * city, int * out_food, int * out_shi bonus_food += food_bonus; bonus_shields += shield_bonus; bonus_gold += gold_bonus; + + if ((cfg->generated_resource_id >= 0) && + (cfg->generated_resource_flags & MF_YIELDS)) { + int res_id = cfg->generated_resource_id; + int req_tech_id = (cfg->generated_resource_flags & MF_NO_TECH_REQ) ? -1 : p_bic_data->ResourceTypes[res_id].RequireID; + if ((req_tech_id < 0) || Leader_has_tech (&leaders[city->Body.CivID], __, req_tech_id)) { + Resource_Type * res = &p_bic_data->ResourceTypes[res_id]; + bonus_food += res->Food; + bonus_shields += res->Shield; + bonus_gold += res->Commerce; + } + } } if (is->current_config.enable_districts && is->current_config.enable_distribution_hub_districts) { @@ -14191,7 +14161,7 @@ patch_City_has_resource (City * this, int edx, int resource_id) // check if one is in the work radius if (! tr) { int res_class = p_bic_data->ResourceTypes[resource_id].Class; - if ((res_class != RC_Strategic) && (res_class != RC_Luxury)) { + if (res_class == RC_Bonus) { int civ_id = this->Body.CivID; FOR_TILES_AROUND (tai, is->workable_tile_count, this->Body.X, this->Body.Y) { Tile * tile = tai.tile; @@ -14440,6 +14410,8 @@ patch_Trade_Net_recompute_resources (Trade_Net * this, int edx, bool skip_popups continue; if (! district_can_generate_resource (owner_id, cfg)) continue; + if ((cfg->generated_resource_flags & MF_LOCAL) != 0) + continue; reserve (sizeof is->resource_tiles[0], (void **)&is->resource_tiles, @@ -26479,25 +26451,6 @@ patch_Sprite_draw_improv_img_on_city_form (Sprite * this, int edx, PCX_Image * c generated_resources[generated_resource_count++] = mill->resource_id; } - if (is->current_config.enable_districts && (generated_resource_count < ARRAY_LEN (generated_resources))) { - FOR_DISTRICTS_AROUND (wai, p_city_form->CurrentCity->Body.X, p_city_form->CurrentCity->Body.Y, true) { - struct district_instance * di = wai.district_inst; - int district_id = di->district_id; - if ((district_id < 0) || (district_id >= is->district_count)) - continue; - - struct district_config * dc = &is->district_configs[district_id]; - if ((dc->generated_resource_id >= 0) && - ((dc->generated_resource_flags & MF_SHOW_BONUS) || (p_bic_data->ResourceTypes[dc->generated_resource_id].Class != RC_Bonus)) && - (! ((dc->generated_resource_flags & MF_HIDE_NON_BONUS) && (p_bic_data->ResourceTypes[dc->generated_resource_id].Class != RC_Bonus))) && - district_can_generate_resource (p_city_form->CurrentCity->Body.CivID, dc)) { - generated_resources[generated_resource_count++] = dc->generated_resource_id; - if (generated_resource_count >= ARRAY_LEN (generated_resources)) - break; - } - } - } - if ((generated_resource_count > 0) && (is->resources_sheet != NULL) && (is->resources_sheet->JGL.Image != NULL) && (TransparentBlt != NULL)) { JGL_Image * jgl_canvas = canvas->JGL.Image, * jgl_sheet = is->resources_sheet->JGL.Image; From 8313cdfef37ecf1cfb0674724f6b27727a2dd1ae Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 26 Jan 2026 15:24:29 -0800 Subject: [PATCH 270/356] Centralize and reuse generated resource tech check --- injected_code.c | 65 ++++++++++++++++++++----------------------------- 1 file changed, 26 insertions(+), 39 deletions(-) diff --git a/injected_code.c b/injected_code.c index 4d445f3a..4b2f872a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -11782,6 +11782,22 @@ tile_suitable_for_district (Tile * tile, int district_id, City * city, bool * ou return true; } +bool +can_generate_resource (int for_civ_id, struct mill * mill) +{ + int req_tech_id = (mill->flags & MF_NO_TECH_REQ) ? -1 : p_bic_data->ResourceTypes[mill->resource_id].RequireID; + return (req_tech_id < 0) || Leader_has_tech (&leaders[for_civ_id], __, req_tech_id); +} + +bool +district_can_generate_resource (int for_civ_id, struct district_config * dc) +{ + if (dc->generated_resource_id < 0) + return false; + int req_tech_id = (dc->generated_resource_flags & MF_NO_TECH_REQ) ? -1 : p_bic_data->ResourceTypes[dc->generated_resource_id].RequireID; + return (req_tech_id < 0) || Leader_has_tech (&leaders[for_civ_id], __, req_tech_id); +} + void calculate_city_center_district_bonus (City * city, int * out_food, int * out_shields, int * out_gold) { @@ -11817,18 +11833,16 @@ calculate_city_center_district_bonus (City * city, int * out_food, int * out_shi neighborhoods_counted++; } - struct district_config const * cfg = &is->district_configs[district_id]; + struct district_config * cfg = &is->district_configs[district_id]; int food_bonus = 0, shield_bonus = 0, gold_bonus = 0; get_effective_district_yields (inst, cfg, &food_bonus, &shield_bonus, &gold_bonus, NULL, NULL, NULL); bonus_food += food_bonus; bonus_shields += shield_bonus; bonus_gold += gold_bonus; - if ((cfg->generated_resource_id >= 0) && - (cfg->generated_resource_flags & MF_YIELDS)) { + if ((cfg->generated_resource_id >= 0) && (cfg->generated_resource_flags & MF_YIELDS)) { int res_id = cfg->generated_resource_id; - int req_tech_id = (cfg->generated_resource_flags & MF_NO_TECH_REQ) ? -1 : p_bic_data->ResourceTypes[res_id].RequireID; - if ((req_tech_id < 0) || Leader_has_tech (&leaders[city->Body.CivID], __, req_tech_id)) { + if (district_can_generate_resource (city->Body.CivID, cfg)) { Resource_Type * res = &p_bic_data->ResourceTypes[res_id]; bonus_food += res->Food; bonus_shields += res->Shield; @@ -13872,31 +13886,6 @@ has_active_building (City * city, int improv_id) ((improv->GovernmentID < 0) || (improv->GovernmentID == owner->GovernmentType)); // building is not restricted to a different govt } -bool -can_generate_resource (int for_civ_id, struct mill * mill) -{ - int req_tech_id = (mill->flags & MF_NO_TECH_REQ) ? -1 : p_bic_data->ResourceTypes[mill->resource_id].RequireID; - return (req_tech_id < 0) || Leader_has_tech (&leaders[for_civ_id], __, req_tech_id); -} - -bool -district_can_generate_resource (int for_civ_id, struct district_config * dc) -{ - if (dc->generated_resource_id < 0) - return false; - int req_tech_id = (dc->generated_resource_flags & MF_NO_TECH_REQ) ? -1 : p_bic_data->ResourceTypes[dc->generated_resource_id].RequireID; - return (req_tech_id < 0) || Leader_has_tech (&leaders[for_civ_id], __, req_tech_id); -} - -bool -district_is_complete_and_owned_by_civ (struct district_instance * di, Tile * tile, int civ_id) -{ - if (di == NULL || di->state != DS_COMPLETED) - return false; - int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); - return owner_id == civ_id; -} - void init_unit_type_count (Leader * leader) { @@ -14157,8 +14146,8 @@ patch_City_has_resource (City * this, int edx, int resource_id) } } - // A district may require a bonus resource as well. If that's the case, - // check if one is in the work radius + // A district may alternatively require a bonus resource, which would be missed in above checks. + // If that's the case, check if one is in the work radius if (! tr) { int res_class = p_bic_data->ResourceTypes[resource_id].Class; if (res_class == RC_Bonus) { @@ -14392,8 +14381,8 @@ patch_Trade_Net_recompute_resources (Trade_Net * this, int edx, bool skip_popups } if (is->current_config.enable_districts) { FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { - Tile * district_tile = (Tile *)(long)tei.key; - struct district_instance * inst = (struct district_instance *)(long)tei.value; + Tile * district_tile = (Tile *)tei.key; + struct district_instance * inst = (struct district_instance *)tei.value; if ((district_tile == NULL) || (district_tile == p_null_tile) || (inst == NULL) || (inst->state != DS_COMPLETED)) continue; @@ -14406,7 +14395,7 @@ patch_Trade_Net_recompute_resources (Trade_Net * this, int edx, bool skip_popups continue; int owner_id = district_tile->vtable->m38_Get_Territory_OwnerID (district_tile); - if ((owner_id < 0) || (owner_id >= 32)) + if (owner_id < 0) continue; if (! district_can_generate_resource (owner_id, cfg)) continue; @@ -31301,12 +31290,10 @@ draw_district_generated_resource_on_tile (Map_Renderer * this, Tile * tile, stru struct district_config * cfg = &is->district_configs[district_id]; if (cfg->generated_resource_id >= 0) { int owner_id = tile->vtable->m38_Get_Territory_OwnerID (tile); - if ((owner_id >= 0) && district_can_generate_resource (owner_id, cfg) && - ((visible_to_civ_id < 0) || (owner_id == visible_to_civ_id))) { + if ((owner_id >= 0) && ((visible_to_civ_id < 0) || (owner_id == visible_to_civ_id))) { int res_id = cfg->generated_resource_id; if (visible_to_civ_id >= 0) { - int req_tech_id = (cfg->generated_resource_flags & MF_NO_TECH_REQ) ? -1 : p_bic_data->ResourceTypes[res_id].RequireID; - if ((req_tech_id < 0) || Leader_has_tech (&leaders[visible_to_civ_id], __, req_tech_id)) + if (district_can_generate_resource (visible_to_civ_id, cfg)) district_resource = res_id; } else district_resource = res_id; From 268ecb067776d24e0ecf32be26dc22b142d9285f Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 26 Jan 2026 15:26:06 -0800 Subject: [PATCH 271/356] Remove unnecessary (long) conversions --- Notes/district_todos.md | 2 +- injected_code.c | 144 ++++++++++++++++++++-------------------- 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 61a5f2d1..b7725b90 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,5 +1,4 @@ - Validate and upfront bridge/canal algorithm - - Validate resource generation (add yield?) - Firm up logic for river district rendering - Hoover Dam (use alt dir, special positioning b/c on river) @@ -20,6 +19,7 @@ ## Maritime Districts - ~~Negative bonuses~~ + - ~~Validate resource generation~~ - ~~Fix scenario art not loading bug~~ - ~~Add advance_prereqs~~ - ~~Allow district OR logic for buildings~~ diff --git a/injected_code.c b/injected_code.c index 4b2f872a..ffe8a6ff 100644 --- a/injected_code.c +++ b/injected_code.c @@ -2496,7 +2496,7 @@ find_pending_district_request (City * city, int district_id) int stored; if (itable_look_up (&is->city_pending_district_requests[civ_id], key, &stored)) { - struct pending_district_request * req = (struct pending_district_request *)(long)stored; + struct pending_district_request * req = (struct pending_district_request *)stored; if ((req != NULL) && (req->civ_id == civ_id) && (req->city_id == city_id) && (req->district_id == district_id)) { if (req->city != city) req->city = city; @@ -2544,7 +2544,7 @@ create_pending_district_request (City * city, int district_id) city->Body.CityName, city->Body.ID, district_id, key); (*p_OutputDebugStringA) (ss); - itable_insert (&is->city_pending_district_requests[civ_id], key, (int)(long)req); + itable_insert (&is->city_pending_district_requests[civ_id], key, (int)req); return req; } @@ -2560,7 +2560,7 @@ find_pending_district_request_by_coords (City * city_or_null, int tile_x, int ti return NULL; FOR_TABLE_ENTRIES (tei, &is->city_pending_district_requests[civ_id]) { - struct pending_district_request * req = (struct pending_district_request *)(long)tei.value; + struct pending_district_request * req = (struct pending_district_request *)tei.value; if (req == NULL) continue; if (req->district_id != district_id) continue; if (city_or_null != NULL) { @@ -2586,7 +2586,7 @@ is_tile_earmarked_for_district (int tile_x, int tile_y) int civ_id = tile->vtable->m38_Get_Territory_OwnerID (tile); FOR_TABLE_ENTRIES (tei, &is->city_pending_district_requests[civ_id]) { - struct pending_district_request * req = (struct pending_district_request *)(long)tei.value; + struct pending_district_request * req = (struct pending_district_request *)tei.value; if (req == NULL) continue; if ((req->target_x == tile_x) && (req->target_y == tile_y)) return true; @@ -2604,7 +2604,7 @@ get_district_instance (Tile * tile) if (! itable_look_up (&is->district_tile_map, (int)tile, &stored_ptr)) return NULL; - struct district_instance * inst = (struct district_instance *)(long)stored_ptr; + struct district_instance * inst = (struct district_instance *)stored_ptr; if ((inst == NULL) || (inst->district_id < 0) || (inst->district_id >= is->district_count)) return NULL; @@ -2678,7 +2678,7 @@ ensure_district_instance (Tile * tile, int district_id, int tile_x, int tile_y) inst->natural_wonder_info.natural_wonder_id = -1; district_instance_set_coords (inst, tile_x, tile_y); - itable_insert (&is->district_tile_map, (int)tile, (int)(long)inst); + itable_insert (&is->district_tile_map, (int)tile, (int)inst); return inst; } @@ -2971,7 +2971,7 @@ detach_workers_from_request (struct pending_district_request * req) if ((civ_id < 0) || (civ_id >= 32)) { for (int civ = 0; civ < 32; civ++) { FOR_TABLE_ENTRIES (tei, &is->district_worker_tables[civ]) { - struct district_worker_record * rec = (struct district_worker_record *)(long)tei.value; + struct district_worker_record * rec = (struct district_worker_record *)tei.value; if ((rec != NULL) && (rec->pending_req == req)) rec->pending_req = NULL; } @@ -2980,7 +2980,7 @@ detach_workers_from_request (struct pending_district_request * req) } FOR_TABLE_ENTRIES (tei, &is->district_worker_tables[civ_id]) { - struct district_worker_record * rec = (struct district_worker_record *)(long)tei.value; + struct district_worker_record * rec = (struct district_worker_record *)tei.value; if ((rec != NULL) && (rec->pending_req == req)) rec->pending_req = NULL; } @@ -3541,7 +3541,7 @@ wai_init_cities (int x, int y) for (Tile * aerodrome_tile = (Tile *)_tei.key; \ (aerodrome_tile != NULL) && (aerodrome_tile != p_null_tile); \ aerodrome_tile = NULL) \ - for (struct district_instance * aerodrome_inst = (struct district_instance *)(long)_tei.value; \ + for (struct district_instance * aerodrome_inst = (struct district_instance *)_tei.value; \ (aerodrome_inst != NULL) && \ (aerodrome_inst->district_id == AERODROME_DISTRICT_ID) && \ district_is_complete (aerodrome_tile, AERODROME_DISTRICT_ID); \ @@ -3791,7 +3791,7 @@ get_tracked_worker_record (Unit * worker) int value; if (itable_look_up (&is->district_worker_tables[civ_id], worker->Body.ID, &value)) - return (struct district_worker_record *)(long)value; + return (struct district_worker_record *)value; return NULL; } @@ -3814,7 +3814,7 @@ ensure_tracked_worker_record (Unit * worker) rec->continent_id = ((tile != NULL) && (tile != p_null_tile)) ? tile->vtable->m46_Get_ContinentID (tile) : -1; rec->pending_req = NULL; - itable_insert (&is->district_worker_tables[civ_id], worker->Body.ID, (int)(long)rec); + itable_insert (&is->district_worker_tables[civ_id], worker->Body.ID, (int)rec); return rec; } @@ -3828,7 +3828,7 @@ remove_tracked_worker_record (int civ_id, int unit_id) if (! itable_look_up (&is->district_worker_tables[civ_id], unit_id, &value)) return; - struct district_worker_record * rec = (struct district_worker_record *)(long)value; + struct district_worker_record * rec = (struct district_worker_record *)value; if (rec->pending_req != NULL) { rec->pending_req->assigned_worker_id = -1; rec->pending_req = NULL; @@ -3861,7 +3861,7 @@ clear_tracked_worker_assignment_by_id (int civ_id, int unit_id) if (! itable_look_up (&is->district_worker_tables[civ_id], unit_id, &value)) return; - struct district_worker_record * rec = (struct district_worker_record *)(long)value; + struct district_worker_record * rec = (struct district_worker_record *)value; if ((rec->pending_req != NULL) && (rec->pending_req->assigned_worker_id == unit_id)) rec->pending_req->assigned_worker_id = -1; rec->pending_req = NULL; @@ -3874,7 +3874,7 @@ clear_all_tracked_workers (void) { for (int civ = 0; civ < 32; civ++) { FOR_TABLE_ENTRIES (tei, &is->district_worker_tables[civ]) { - struct district_worker_record * rec = (struct district_worker_record *)(long)tei.value; + struct district_worker_record * rec = (struct district_worker_record *)tei.value; if (rec == NULL) continue; if ((rec->pending_req != NULL) && (rec->pending_req->assigned_worker_id == rec->unit_id)) @@ -4018,7 +4018,7 @@ find_best_worker_for_district (Leader * leader, City * city, int district_id, in (*p_OutputDebugStringA) (ss); FOR_TABLE_ENTRIES (tei, &is->district_worker_tables[civ_id]) { - struct district_worker_record * rec = (struct district_worker_record *)(long)tei.value; + struct district_worker_record * rec = (struct district_worker_record *)tei.value; if (rec == NULL) { continue; @@ -4155,7 +4155,7 @@ assign_workers_for_pending_districts (Leader * leader) return; FOR_TABLE_ENTRIES (tei, &is->city_pending_district_requests[civ_id]) { - struct pending_district_request * req = (struct pending_district_request *)(long)tei.value; + struct pending_district_request * req = (struct pending_district_request *)tei.value; if (req == NULL) continue; @@ -4439,7 +4439,7 @@ remember_pending_building_order (City * city, int improvement_id) if ((*p_human_player_bits & (1 << city->Body.CivID)) != 0) return; - itable_insert (&is->city_pending_building_orders, (int)(long)city, improvement_id); + itable_insert (&is->city_pending_building_orders, (int)city, improvement_id); } bool @@ -4450,7 +4450,7 @@ lookup_pending_building_order (City * city, int * out_improv_id) (out_improv_id == NULL)) return false; - return itable_look_up (&is->city_pending_building_orders, (int)(long)city, out_improv_id); + return itable_look_up (&is->city_pending_building_orders, (int)city, out_improv_id); } void @@ -4460,7 +4460,7 @@ forget_pending_building_order (City * city) (city == NULL)) return; - itable_remove (&is->city_pending_building_orders, (int)(long)city); + itable_remove (&is->city_pending_building_orders, (int)city); } bool @@ -4497,7 +4497,7 @@ get_district_building_prereq_list (int improv_id) int stored = 0; if (! itable_look_up (&is->district_building_prereqs, improv_id, &stored)) return NULL; - return (struct district_building_prereq_list *)(long)stored; + return (struct district_building_prereq_list *)stored; } bool @@ -4524,7 +4524,7 @@ add_district_building_prereq (int improv_id, int district_id) if (list == NULL) return; list->count = 0; - itable_insert (&is->district_building_prereqs, improv_id, (int)(long)list); + itable_insert (&is->district_building_prereqs, improv_id, (int)list); } if (district_building_prereq_list_contains (list, district_id)) @@ -4800,7 +4800,7 @@ get_distribution_hub_record (Tile * tile) int stored; if (itable_look_up (&is->distribution_hub_records, (int)tile, &stored)) - return (struct distribution_hub_record *)(long)stored; + return (struct distribution_hub_record *)stored; else return NULL; } @@ -4859,7 +4859,7 @@ get_distribution_hub_yields_for_city (City * city, int * out_food, int * out_shi recompute_distribution_hub_totals (); FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { - struct distribution_hub_record * rec = (struct distribution_hub_record *)(long)tei.value; + struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; if (distribution_hub_accessible_to_city (rec, city)) { food += rec->food_yield; shields += rec->shield_yield; @@ -4941,7 +4941,7 @@ void clear_distribution_hub_tables (void) { FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { - struct distribution_hub_record * rec = (struct distribution_hub_record *)(long)tei.value; + struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; free (rec); } table_deinit (&is->distribution_hub_records); @@ -5022,7 +5022,7 @@ recompute_distribution_hub_yields (struct distribution_hub_record * rec) bool tile_belongs_to_me = true; FOR_TABLE_ENTRIES (other_tei, &is->distribution_hub_records) { - struct distribution_hub_record * other_rec = (struct distribution_hub_record *)(long)other_tei.value; + struct distribution_hub_record * other_rec = (struct distribution_hub_record *)other_tei.value; if ((other_rec == NULL) || (other_rec == rec)) continue; if (other_rec->civ_id != rec->civ_id) @@ -5131,7 +5131,7 @@ recompute_distribution_hub_totals () FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { Tile * tile = (Tile *)tei.key; - struct distribution_hub_record * rec = (struct distribution_hub_record *)(long)tei.value; + struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; if (rec == NULL) continue; @@ -5202,7 +5202,7 @@ recompute_distribution_hub_totals () clear_memo (); FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { - struct distribution_hub_record * rec = (struct distribution_hub_record *)(long)tei.value; + struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; if (rec == NULL) continue; @@ -5223,7 +5223,7 @@ recompute_distribution_hub_totals () memset (&new_coverage_counts, 0, sizeof new_coverage_counts); FOR_TABLE_ENTRIES (tei, &newly_covered_tiles) { - Tile * covered_tile = (Tile *)(long)tei.key; + Tile * covered_tile = (Tile *)tei.key; if ((covered_tile == NULL) || (covered_tile == p_null_tile)) continue; int tx, ty; @@ -5291,7 +5291,7 @@ on_distribution_hub_completed (Tile * tile, int tile_x, int tile_y) rec->shield_yield = 0; rec->raw_food_yield = 0; rec->raw_shield_yield = 0; - itable_insert (&is->distribution_hub_records, (int)tile, (int)(long)rec); + itable_insert (&is->distribution_hub_records, (int)tile, (int)rec); adjust_distribution_hub_coverage (rec); is->distribution_hub_totals_dirty = true; @@ -9181,7 +9181,7 @@ place_natural_wonders_on_map (void) // Record existing natural wonders FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { - struct district_instance * inst = (struct district_instance *)(long)tei.value; + struct district_instance * inst = (struct district_instance *)tei.value; if ((inst == NULL) || (inst->district_id != NATURAL_WONDER_DISTRICT_ID)) continue; @@ -9668,7 +9668,7 @@ set_named_tile_entry (Tile * tile, int tile_x, int tile_y, char const * name) entry = calloc (1, sizeof *entry); if (entry == NULL) return; - itable_insert (&is->named_tile_map, (int)tile, (int)(long)entry); + itable_insert (&is->named_tile_map, (int)tile, (int)entry); } entry->tile_x = tile_x; entry->tile_y = tile_y; @@ -10039,7 +10039,7 @@ void clear_highlighted_worker_tiles_for_districts () { FOR_TABLE_ENTRIES (tei, &is->highlighted_city_radius_tile_pointers) { - struct highlighted_city_radius_tile_info * info = (struct highlighted_city_radius_tile_info *)(long)tei.value; + struct highlighted_city_radius_tile_info * info = (struct highlighted_city_radius_tile_info *)tei.value; if (info != NULL) free (info); } @@ -10056,7 +10056,7 @@ reset_district_state (bool reset_tile_map) table_deinit (&is->district_tech_prereqs); FOR_TABLE_ENTRIES (tei, &is->district_building_prereqs) { - struct district_building_prereq_list * list = (struct district_building_prereq_list *)(long)tei.value; + struct district_building_prereq_list * list = (struct district_building_prereq_list *)tei.value; if (list != NULL) free (list); } @@ -10065,13 +10065,13 @@ reset_district_state (bool reset_tile_map) stable_deinit (&is->building_name_to_id); if (reset_tile_map) { FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { - struct district_instance * inst = (struct district_instance *)(long)tei.value; + struct district_instance * inst = (struct district_instance *)tei.value; if (inst != NULL) free (inst); } table_deinit (&is->district_tile_map); FOR_TABLE_ENTRIES (tei, &is->named_tile_map) { - struct named_tile_entry * entry = (struct named_tile_entry *)(long)tei.value; + struct named_tile_entry * entry = (struct named_tile_entry *)tei.value; if (entry != NULL) free (entry); } @@ -10107,7 +10107,7 @@ reset_district_state (bool reset_tile_map) for (int civ_id = 0; civ_id < 32; civ_id++) { FOR_TABLE_ENTRIES (tei, &is->city_pending_district_requests[civ_id]) { - struct pending_district_request * req = (struct pending_district_request *)(long)tei.value; + struct pending_district_request * req = (struct pending_district_request *)tei.value; if (req != NULL) free (req); } @@ -12491,7 +12491,7 @@ leader_has_natural_wonder_prereq_in_territory (int civ_id, struct district_infos return false; FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { - struct district_instance * inst = (struct district_instance *)(long)tei.value; + struct district_instance * inst = (struct district_instance *)tei.value; if ((inst == NULL) || (inst->district_id != NATURAL_WONDER_DISTRICT_ID)) continue; @@ -12524,7 +12524,7 @@ count_distribution_hubs_for_civ (int civ_id) { int current = 0; FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { - struct distribution_hub_record * rec = (struct distribution_hub_record *)(long)tei.value; + struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; if ((rec != NULL) && (rec->civ_id == civ_id)) current++; } @@ -13602,10 +13602,10 @@ city_requires_district_for_improvement (City * city, int improv_id, int * out_di void clear_best_feasible_order (City * city) { - int key = (int)(long)city; + int key = (int)city; int stored_int; if (itable_look_up (&is->ai_best_feasible_orders, key, &stored_int)) { - struct ai_best_feasible_order * stored = (struct ai_best_feasible_order *)(long)stored_int; + struct ai_best_feasible_order * stored = (struct ai_best_feasible_order *)stored_int; free (stored); itable_remove (&is->ai_best_feasible_orders, key); } @@ -13614,18 +13614,18 @@ clear_best_feasible_order (City * city) void record_best_feasible_order (City * city, City_Order const * order, int value) { - int key = (int)(long)city; + int key = (int)city; int stored_int; struct ai_best_feasible_order * stored; if (itable_look_up (&is->ai_best_feasible_orders, key, &stored_int)) - stored = (struct ai_best_feasible_order *)(long)stored_int; + stored = (struct ai_best_feasible_order *)stored_int; else { stored = malloc (sizeof *stored); if (stored == NULL) return; stored->order = *order; stored->value = value; - itable_insert (&is->ai_best_feasible_orders, key, (int)(long)stored); + itable_insert (&is->ai_best_feasible_orders, key, (int)stored); return; } @@ -13639,8 +13639,8 @@ struct ai_best_feasible_order * get_best_feasible_order (City * city) { int stored_int; - if (itable_look_up (&is->ai_best_feasible_orders, (int)(long)city, &stored_int)) - return (struct ai_best_feasible_order *)(long)stored_int; + if (itable_look_up (&is->ai_best_feasible_orders, (int)city, &stored_int)) + return (struct ai_best_feasible_order *)stored_int; else return NULL; } @@ -18093,7 +18093,7 @@ patch_Unit_join_city (Unit * this, int edx, City * city) struct pending_district_request * same_continent_req = NULL; FOR_TABLE_ENTRIES (tei, &is->city_pending_district_requests[civ_id]) { - struct pending_district_request * req = (struct pending_district_request *)(long)tei.value; + struct pending_district_request * req = (struct pending_district_request *)tei.value; if ((req == NULL) || (req->assigned_worker_id >= 0)) continue; if ((req->civ_id != civ_id) || (req->city_id < 0)) @@ -21895,7 +21895,7 @@ insert_ai_candidate_bridge_or_canals_into_district_tile_map () inst->tile_x = tx; inst->tile_y = ty; - itable_insert (&is->district_tile_map, key, (int)(long)inst); + itable_insert (&is->district_tile_map, key, (int)inst); } } } @@ -22954,7 +22954,7 @@ patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Imag if (draw_natural_wonders) { FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { - struct district_instance * inst = (struct district_instance *)(long)tei.value; + struct district_instance * inst = (struct district_instance *)tei.value; if ((inst == NULL) || (inst->district_id != NATURAL_WONDER_DISTRICT_ID)) continue; @@ -22966,7 +22966,7 @@ patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Imag if ((nw_cfg == NULL) || (nw_cfg->name == NULL) || (nw_cfg->name[0] == '\0')) continue; - Tile * tile = (Tile *)(long)tei.key; + Tile * tile = (Tile *)tei.key; if ((tile == NULL) || (tile == p_null_tile)) continue; @@ -22983,11 +22983,11 @@ patch_Main_Screen_Form_draw_city_hud (Main_Screen_Form * this, int edx, PCX_Imag if (draw_named_tiles) { FOR_TABLE_ENTRIES (tei, &is->named_tile_map) { - struct named_tile_entry * entry = (struct named_tile_entry *)(long)tei.value; + struct named_tile_entry * entry = (struct named_tile_entry *)tei.value; if ((entry == NULL) || (entry->name[0] == '\0')) continue; - Tile * tile = (Tile *)(long)tei.key; + Tile * tile = (Tile *)tei.key; if ((tile == NULL) || (tile == p_null_tile)) continue; @@ -24916,14 +24916,14 @@ ai_update_distribution_hub_goal_for_leader (Leader * leader) int current = 0; FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { - struct distribution_hub_record * rec = (struct distribution_hub_record *)(long)tei.value; + struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; if ((rec != NULL) && (rec->civ_id == civ_id)) current++; } int in_progress = 0; FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { - Tile * tile = (Tile *)(long)tei.key; + Tile * tile = (Tile *)tei.key; int mapped_district_id = tei.value; if ((tile != NULL) && (mapped_district_id == DISTRIBUTION_HUB_DISTRICT_ID) && @@ -24936,7 +24936,7 @@ ai_update_distribution_hub_goal_for_leader (Leader * leader) int pending = 0; FOR_TABLE_ENTRIES (tei, &is->city_pending_district_requests[civ_id]) { - struct pending_district_request * req = (struct pending_district_request *)(long)tei.value; + struct pending_district_request * req = (struct pending_district_request *)tei.value; if ((req != NULL) && (req->district_id == DISTRIBUTION_HUB_DISTRICT_ID)) pending++; } @@ -27074,7 +27074,7 @@ patch_Map_place_scenario_things (Map * this) is->current_config.add_natural_wonders_to_scenarios_if_none) { bool any_natural_wonders = false; FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { - struct district_instance * inst = (struct district_instance *)(long)tei.value; + struct district_instance * inst = (struct district_instance *)tei.value; if ((inst != NULL) && (inst->district_id == NATURAL_WONDER_DISTRICT_ID) && (inst->natural_wonder_info.natural_wonder_id >= 0)) { @@ -27332,7 +27332,7 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi int entry_count = 0; for (int civ_id = 0; civ_id < 32; civ_id++) { FOR_TABLE_ENTRIES (tei, &is->city_pending_district_requests[civ_id]) { - struct pending_district_request * req = (struct pending_district_request *)(long)tei.value; + struct pending_district_request * req = (struct pending_district_request *)tei.value; if ((req != NULL) && (req->city_id >= 0)) entry_count++; } @@ -27344,7 +27344,7 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi chunk[0] = entry_count; for (int civ_id = 0; civ_id < 32; civ_id++) { FOR_TABLE_ENTRIES (tei, &is->city_pending_district_requests[civ_id]) { - struct pending_district_request * req = (struct pending_district_request *)(long)tei.value; + struct pending_district_request * req = (struct pending_district_request *)tei.value; if ((req == NULL) || (req->city_id < 0)) continue; out[0] = req->city_id; @@ -27362,7 +27362,7 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi (is->city_pending_building_orders.len > 0)) { int entry_count = 0; FOR_TABLE_ENTRIES (tei, &is->city_pending_building_orders) { - City * city = (City *)(long)tei.key; + City * city = (City *)tei.key; int improv_id = tei.value; if ((city != NULL) && (improv_id >= 0)) entry_count++; @@ -27373,7 +27373,7 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi int * out = chunk + 1; chunk[0] = entry_count; FOR_TABLE_ENTRIES (tei, &is->city_pending_building_orders) { - City * city = (City *)(long)tei.key; + City * city = (City *)tei.key; int improv_id = tei.value; if ((city == NULL) || (improv_id < 0)) continue; @@ -27392,7 +27392,7 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi int written = 0; FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { Tile * tile = (Tile *)tei.key; - struct district_instance * inst = (struct district_instance *)(long)tei.value; + struct district_instance * inst = (struct district_instance *)tei.value; if (inst == NULL) continue; int x, y; @@ -27429,7 +27429,7 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi if (is->current_config.enable_natural_wonders && (is->district_tile_map.len > 0)) { int entry_capacity = 0; FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { - struct district_instance * inst = (struct district_instance *)(long)tei.value; + struct district_instance * inst = (struct district_instance *)tei.value; if ((inst == NULL) || (inst->district_id != NATURAL_WONDER_DISTRICT_ID)) continue; if (inst->natural_wonder_info.natural_wonder_id < 0) @@ -27443,7 +27443,7 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi int written = 0; FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { Tile * tile = (Tile *)tei.key; - struct district_instance * inst = (struct district_instance *)(long)tei.value; + struct district_instance * inst = (struct district_instance *)tei.value; if ((inst == NULL) || (inst->district_id != NATURAL_WONDER_DISTRICT_ID) || (inst->natural_wonder_info.natural_wonder_id < 0)) @@ -27475,7 +27475,7 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi int * out = chunk + 1; int written = 0; FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { - struct distribution_hub_record * rec = (struct distribution_hub_record *)(long)tei.value; + struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; if (rec == NULL) continue; out[0] = rec->tile_x; @@ -27534,10 +27534,10 @@ patch_MappedFile_create_file_to_save_game (MappedFile * this, int edx, LPCSTR fi byte * out = (byte *)(count + 1); int written = 0; FOR_TABLE_ENTRIES (tei, &is->named_tile_map) { - struct named_tile_entry * entry = (struct named_tile_entry *)(long)tei.value; + struct named_tile_entry * entry = (struct named_tile_entry *)tei.value; if ((entry == NULL) || (entry->name[0] == '\0')) continue; - Tile * tile = (Tile *)(long)tei.key; + Tile * tile = (Tile *)tei.key; int tile_x = entry->tile_x; int tile_y = entry->tile_y; if ((tile != NULL) && (tile != p_null_tile)) @@ -27657,13 +27657,13 @@ patch_move_game_data (byte * buffer, bool save_else_load) if (! save_else_load) { // Free all district_instance structs first FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { - struct district_instance * inst = (struct district_instance *)(long)tei.value; + struct district_instance * inst = (struct district_instance *)tei.value; if (inst != NULL) free (inst); } table_deinit (&is->district_tile_map); FOR_TABLE_ENTRIES (tei, &is->named_tile_map) { - struct named_tile_entry * entry = (struct named_tile_entry *)(long)tei.value; + struct named_tile_entry * entry = (struct named_tile_entry *)tei.value; if (entry != NULL) free (entry); } @@ -27824,7 +27824,7 @@ patch_move_game_data (byte * buffer, bool save_else_load) (remaining_bytes >= entry_count * 5 * (int)sizeof(int))) { for (int civ_id = 0; civ_id < 32; civ_id++) { FOR_TABLE_ENTRIES (tei, &is->city_pending_district_requests[civ_id]) { - struct pending_district_request * req = (struct pending_district_request *)(long)tei.value; + struct pending_district_request * req = (struct pending_district_request *)tei.value; if (req != NULL) free (req); } @@ -27891,7 +27891,7 @@ patch_move_game_data (byte * buffer, bool save_else_load) City * city = get_city_ptr (city_id); if (city == NULL) continue; - itable_insert (&is->city_pending_building_orders, (int)(long)city, improv_id); + itable_insert (&is->city_pending_building_orders, (int)city, improv_id); } if (! success) table_deinit (&is->city_pending_building_orders); @@ -27915,7 +27915,7 @@ patch_move_game_data (byte * buffer, bool save_else_load) int required_bytes = entry_count * ints_per_entry * (int)sizeof(int); if (success && remaining_bytes >= required_bytes) { FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { - struct district_instance * inst = (struct district_instance *)(long)tei.value; + struct district_instance * inst = (struct district_instance *)tei.value; if (inst != NULL) free (inst); } @@ -28135,11 +28135,11 @@ patch_move_game_data (byte * buffer, bool save_else_load) entry->tile_y = tile_y; strncpy (entry->name, name_buf, sizeof entry->name); entry->name[(sizeof entry->name) - 1] = '\0'; - itable_insert (&is->named_tile_map, (int)tile, (int)(long)entry); + itable_insert (&is->named_tile_map, (int)tile, (int)entry); } if (! success) { FOR_TABLE_ENTRIES (tei, &is->named_tile_map) { - struct named_tile_entry * entry = (struct named_tile_entry *)(long)tei.value; + struct named_tile_entry * entry = (struct named_tile_entry *)tei.value; if (entry != NULL) free (entry); } From 07a8d26f8e351a2f89303d83a3c7ad0e6412e7af Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 26 Jan 2026 17:07:12 -0800 Subject: [PATCH 272/356] Refine great wall draw logic; Change district default bonuses --- C3X.h | 12 ++++++------ default.districts_config.txt | 10 +++++----- injected_code.c | 25 ++++++++++++++++++++----- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/C3X.h b/C3X.h index 998781dd..70ec21e5 100644 --- a/C3X.h +++ b/C3X.h @@ -980,12 +980,12 @@ struct parsed_district_definition { bool has_buildable_on; bool has_resource_prereq_on_tile; bool has_buildable_by_civs; - bool has_buildable_by_war_allies; - bool has_buildable_by_pact_allies; - char * generated_resource; - short generated_resource_flags; - bool has_generated_resource; - char * buildable_by_civ_traits[10]; + bool has_buildable_by_war_allies; + bool has_buildable_by_pact_allies; + char * generated_resource; + short generated_resource_flags; + bool has_generated_resource; + char * buildable_by_civ_traits[10]; int buildable_by_civ_traits_count; int buildable_by_civ_traits_ids[10]; int buildable_by_civ_traits_id_count; diff --git a/default.districts_config.txt b/default.districts_config.txt index 079e6d67..e765aca0 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -102,7 +102,7 @@ allow_multiple = 1 culture_bonus = 0, Marketplace: 1 science_bonus = 0 food_bonus = 0 -gold_bonus = 2, Marketplace: 1, Bank: 1, "Stock Exchange": 1 +gold_bonus = 2, Marketplace: 1, Bank: 1, "Stock Exchange": 1, river: 2 shield_bonus = 0 happiness_bonus = 0 @@ -123,7 +123,7 @@ culture_bonus = 0 science_bonus = 0 food_bonus = 0 gold_bonus = 0 -shield_bonus = 2, Factory: 2, "Manufacturing Plant": 2 +shield_bonus = 2, Factory: 2, "Manufacturing Plant": 2, river: 2 happiness_bonus = 0 #District @@ -144,7 +144,7 @@ science_bonus = 4, "Research Lab": 4 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happiness_bonus = 0 +happiness_bonus = -2 #District name = Offshore Extraction Zone @@ -205,7 +205,7 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 4 shield_bonus = 0 -happiness_bonus = 0 +happiness_bonus = 2 #District name = Neighborhood @@ -361,7 +361,7 @@ btn_tile_sheet_column = 9 tooltip = Build Great Wall obsoleted_by = Metallurgy wonder_prereqs = "The Great Wall" -buildable_on = desert,plains,grassland,tundra,floodplain,mountains,forest,swamp,jungle +buildable_on = desert,plains,grassland,tundra,floodplain,mountains,forest,swamp,jungle,hills draw_over_resources = 1 defense_bonus_percent = 50 allow_multiple = 1 diff --git a/injected_code.c b/injected_code.c index ffe8a6ff..5e54c8b2 100644 --- a/injected_code.c +++ b/injected_code.c @@ -31252,6 +31252,13 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma bool wall_sw = tile_has_district_at (tile_x - 1, tile_y + 1, GREAT_WALL_DISTRICT_ID); bool wall_s = tile_has_district_at (tile_x, tile_y + 2, GREAT_WALL_DISTRICT_ID); bool wall_n = tile_has_district_at (tile_x, tile_y - 2, GREAT_WALL_DISTRICT_ID); + + bool water_ne = tile_is_water (tile_x - 1, tile_y - 1); + bool water_nw = tile_is_water (tile_x + 1, tile_y - 1); + bool water_w = tile_is_water (tile_x - 2, tile_y); + bool water_n = tile_is_water (tile_x, tile_y + 2); + bool water_sw = tile_is_water (tile_x - 1, tile_y + 1); + bool water_se = tile_is_water (tile_x + 1, tile_y + 1); Sprite * sprites = is->district_img_sets[GREAT_WALL_DISTRICT_ID].imgs[0][0]; Sprite * base = &sprites[0]; @@ -31260,10 +31267,16 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma if (wall_nw) draw_district_on_map_or_canvas(&sprites[DIR_NW], map_renderer, pixel_x, pixel_y); if (wall_ne) draw_district_on_map_or_canvas(&sprites[DIR_NE], map_renderer, pixel_x, pixel_y); - if (!wall_nw && !wall_ne && wall_n && tile_is_water (tile_x - 1, tile_y - 1)) + if (!wall_nw && !wall_ne && wall_n && water_ne) draw_district_on_map_or_canvas(&sprites[DIR_N], map_renderer, pixel_x, pixel_y); - if (!wall_se && !wall_s && tile_is_water (tile_x + 1, tile_y - 1)) + if (!wall_se && !wall_s && water_nw) draw_district_on_map_or_canvas(&sprites[DIR_NE], map_renderer, pixel_x, pixel_y); + if (!wall_ne && wall_n && water_nw) + draw_district_on_map_or_canvas(&sprites[DIR_N], map_renderer, pixel_x, pixel_y); + if (water_sw && water_nw && !water_w) + draw_district_on_map_or_canvas(&sprites[DIR_W], map_renderer, pixel_x, pixel_y); + if (water_n && !wall_nw && !wall_ne) + draw_district_on_map_or_canvas(&sprites[DIR_N], map_renderer, pixel_x, pixel_y); // Base pillar draw_district_on_map_or_canvas(base, map_renderer, pixel_x, pixel_y); @@ -31271,9 +31284,11 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma if (wall_sw) draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); if (wall_se) draw_district_on_map_or_canvas(&sprites[DIR_SE], map_renderer, pixel_x, pixel_y); - if (!wall_sw && !wall_nw && !wall_s && tile_is_water (tile_x - 2, tile_y) && !tile_is_water (tile_x, tile_y + 2)) + if (!wall_sw && !wall_nw && !wall_s && water_w && !water_n) draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); - if (!wall_sw && !wall_se && wall_s && tile_is_water (tile_x - 1, tile_y + 1)) + if (!wall_sw && !wall_se && wall_s && water_sw) + draw_district_on_map_or_canvas(&sprites[DIR_S], map_renderer, pixel_x, pixel_y); + if (!wall_se && wall_s && water_se) draw_district_on_map_or_canvas(&sprites[DIR_S], map_renderer, pixel_x, pixel_y); } @@ -31546,7 +31561,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - //*p_debug_mode_bits |= 0xC; + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; From 130938f8ebbc2ae01c5867fba5b1d481e68739ed Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 26 Jan 2026 17:17:56 -0800 Subject: [PATCH 273/356] Allow AI workers to replace obsolete districts --- injected_code.c | 59 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/injected_code.c b/injected_code.c index 5e54c8b2..3e483cbe 100644 --- a/injected_code.c +++ b/injected_code.c @@ -11739,6 +11739,39 @@ can_build_district_on_tile (Tile * tile, int district_id, int civ_id) return true; } +bool +district_is_obsolete_for_civ (int district_id, int civ_id) +{ + if ((district_id < 0) || (district_id >= is->district_count)) + return false; + + int obsolete_id = is->district_infos[district_id].obsoleted_by_id; + if (obsolete_id < 0) + return false; + + return Leader_has_tech (&leaders[civ_id], __, obsolete_id); +} + +bool +tile_has_obsolete_district_for_civ (Tile * tile, int civ_id) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return false; + + struct district_instance * inst = get_district_instance (tile); + if (inst == NULL) + return false; + + int district_id = inst->district_id; + if ((district_id < 0) || (district_id >= is->district_count)) + return false; + + if (! district_is_complete (tile, district_id)) + return false; + + return district_is_obsolete_for_civ (district_id, civ_id); +} + bool tile_suitable_for_district (Tile * tile, int district_id, City * city, bool * out_has_resource) { @@ -11760,7 +11793,8 @@ tile_suitable_for_district (Tile * tile, int district_id, City * city, bool * ou struct district_instance * inst = get_district_instance (tile); if (inst != NULL) { - struct district_infos const * info = &is->district_infos[district_id]; + if (tile_has_obsolete_district_for_civ (tile, city->Body.CivID)) + return true; // Unused wonder districts can be repurposed, completed cannot if (district_id == WONDER_DISTRICT_ID && inst->state == DS_COMPLETED) { @@ -11769,13 +11803,6 @@ tile_suitable_for_district (Tile * tile, int district_id, City * city, bool * ou return false; } - // Great Wall districts can be replaced if obsolete - if (district_id == GREAT_WALL_DISTRICT_ID) { - int obsolete_id = info->obsoleted_by_id; - if (obsolete_id >= 0 && Leader_has_tech (&leaders[city->Body.CivID], __, obsolete_id)) - return true; - } - return false; } @@ -12125,7 +12152,8 @@ find_tile_for_neighborhood_district (City * city, int * out_x, int * out_y) continue; if (tile_has_resource (tile)) continue; - if (get_district_instance (tile) != NULL) + if (get_district_instance (tile) != NULL && + ! tile_has_obsolete_district_for_civ (tile, city->Body.CivID)) continue; int yield = compute_city_tile_yield_sum (city, tri.tile_x, tri.tile_y); @@ -12191,7 +12219,8 @@ find_tile_for_port_district (City * city, int * out_x, int * out_y) continue; if (tile_has_resource (tile)) continue; - if (get_district_instance (tile) != NULL) + if (get_district_instance (tile) != NULL && + ! tile_has_obsolete_district_for_civ (tile, city->Body.CivID)) continue; if (! tile->vtable->m35_Check_Is_Water (tile)) continue; @@ -12677,8 +12706,8 @@ leader_has_pact_ally_district_access (Leader * leader, int district_id) if (civ_id == self_id) continue; Leader * other = &leaders[civ_id]; - if (((leader->Relation_Treaties[civ_id] & 1) != 0 || - (leader->Relation_Treaties[civ_id] & 4) != 0) && + if (((leader->Relation_Treaties[civ_id] & 1) != 0 || // 1 = peace treaty + (leader->Relation_Treaties[civ_id] & 4) != 0) && // 4 = mutual protection pact leader_can_natively_build_district (other, district_id)) { return true; } @@ -12764,7 +12793,8 @@ find_tile_for_district (City * city, int district_id, int * out_x, int * out_y) if (! tile_suitable_for_district (tile, district_id, city, NULL)) continue; - if (get_district_instance (tile) != NULL) + if (get_district_instance (tile) != NULL && + ! tile_has_obsolete_district_for_civ (tile, city->Body.CivID)) continue; int yield = compute_city_tile_yield_sum (city, tri.tile_x, tri.tile_y); @@ -19160,8 +19190,7 @@ great_wall_blocks_civ (Tile * tile, int civ_id) if (! district_is_complete (tile, GREAT_WALL_DISTRICT_ID)) return false; - int obsolete_id = is->district_infos[GREAT_WALL_DISTRICT_ID].obsoleted_by_id; - if ((obsolete_id >= 0) && Leader_has_tech (&leaders[civ_id], __, obsolete_id)) + if (district_is_obsolete_for_civ (GREAT_WALL_DISTRICT_ID, civ_id)) return false; return true; From fe952fb42e5e29a4604c1c6af63feb2ccfe27df2 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 26 Jan 2026 18:57:32 -0800 Subject: [PATCH 274/356] Switch great wall to be a config value with wonder name, instead of checking by improv characteristics --- C3X.h | 1 + default.c3x_config.ini | 1 + injected_code.c | 16 ++++++++++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/C3X.h b/C3X.h index 70ec21e5..a64fd076 100644 --- a/C3X.h +++ b/C3X.h @@ -401,6 +401,7 @@ struct c3x_config { bool disable_great_wall_city_defense_bonus; bool great_wall_districts_impassible_by_others; bool auto_build_great_wall_around_territory; + int great_wall_auto_build_wonder_improv_id; int ai_auto_build_great_wall_strategy; bool enable_city_work_radii_highlights; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 419b7816..3aed2c87 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -927,6 +927,7 @@ ai_city_district_max_build_wait_turns = 20 disable_great_wall_city_defense_bonus = false great_wall_districts_impassible_by_others = true auto_build_great_wall_around_territory = true +great_wall_auto_build_wonder_name = "The Great Wall" ; When enabled, holding down the Control key while a worker is selected will highlight all tiles within the work radii of nearby cities. City centers ; are highlighted more brightly, while tiles that fall within multiple cities' work radii are highlighted more intensely. This visual aid helps you diff --git a/injected_code.c b/injected_code.c index 3e483cbe..2bfe87a9 100644 --- a/injected_code.c +++ b/injected_code.c @@ -2300,6 +2300,17 @@ load_config (char const * file_path, int path_is_relative_to_mod_dir) } else if (slice_matches_str (&p.key, "ai_auto_build_great_wall_strategy")) { if (! read_ai_auto_build_great_wall_strategy (&value, (int *)&cfg->ai_auto_build_great_wall_strategy)) handle_config_error (&p, CPE_BAD_VALUE); + } else if (slice_matches_str (&p.key, "great_wall_auto_build_wonder_name")) { + struct string_slice trimmed = trim_string_slice (&value, 1); + if (trimmed.len <= 0) { + cfg->great_wall_auto_build_wonder_improv_id = -1; + } else { + int improv_id; + if (find_improv_id_by_name (&trimmed, &improv_id)) + cfg->great_wall_auto_build_wonder_improv_id = improv_id; + else + handle_config_error (&p, CPE_BAD_VALUE); + } } else if (slice_matches_str (&p.key, "ptw_like_artillery_targeting")) { if (! read_ptw_arty_types (&value, &unrecognized_lines, @@ -16339,6 +16350,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.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; for (int n = 0; n < ARRAY_LEN (integer_config_options); n++) @@ -22673,10 +22685,10 @@ patch_City_add_or_remove_improvement (City * this, int edx, int improv_id, int a } } - // Doesn't seem to actually be true for Great Wall?? Check ITW_Double_Combat_Strength_vs_Barbarians instead + int gw_auto_build_improv_id = is->current_config.great_wall_auto_build_wonder_improv_id; if (add && is->current_config.enable_districts && is->current_config.auto_build_great_wall_around_territory && - Improvement_has_wonder_flag(improv, __, ITW_Double_Combat_Strength_vs_Barbarians)) + (gw_auto_build_improv_id >= 0) && (improv_id == gw_auto_build_improv_id)) auto_build_great_wall_districts_for_civ (this->Body.CivID); //Calculate if work_area has shrunk, and if so, redistribute citizens. From b748f1c8a47599b1370ca4ea4222beb9a0e40918 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 27 Jan 2026 16:17:30 -0800 Subject: [PATCH 275/356] Rework Hoover Dam art, tighten river alignment code --- Art/Districts/1200/CentralRailHub_ASIAN.PCX | Bin 13571 -> 12224 bytes Art/Districts/1200/CentralRailHub_ASIAN_.PCX | Bin 0 -> 15380 bytes Art/Districts/1200/CentralRailHub_MIDEAST.PCX | Bin 14086 -> 14794 bytes Art/Districts/1200/CentralRailHub_ROMAN.PCX | Bin 15223 -> 16806 bytes Art/Districts/1200/Wonders_3.PCX | Bin 13421 -> 14889 bytes injected_code.c | 12309 ++++++++-------- 6 files changed, 6158 insertions(+), 6151 deletions(-) create mode 100644 Art/Districts/1200/CentralRailHub_ASIAN_.PCX diff --git a/Art/Districts/1200/CentralRailHub_ASIAN.PCX b/Art/Districts/1200/CentralRailHub_ASIAN.PCX index 0b00751114084a550dd3658151580bf602b5e741..c5387dbeae527858597dbb9a8cce9608d570ea93 100644 GIT binary patch literal 12224 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 diff --git a/Art/Districts/1200/CentralRailHub_ASIAN_.PCX b/Art/Districts/1200/CentralRailHub_ASIAN_.PCX new file mode 100644 index 0000000000000000000000000000000000000000..5138f204b19e940899feeef5d0be0b5dbb1ae867 GIT binary patch literal 15380 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 literal 0 HcmV?d00001 diff --git a/Art/Districts/1200/CentralRailHub_MIDEAST.PCX b/Art/Districts/1200/CentralRailHub_MIDEAST.PCX index 8467140a2cca746844a1192eee9f7fa78d8fe0f9..5634c3fc391caedfc564dc997b796f001bbe85a7 100644 GIT binary patch delta 5227 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 HY4!gCS_jqv delta 4485 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 diff --git a/Art/Districts/1200/CentralRailHub_ROMAN.PCX b/Art/Districts/1200/CentralRailHub_ROMAN.PCX index d2281b93613933a0f3d36d0ff957430f9e69ca2a..5da251fe039526c5eb28fc9c3f28f3b2581a86e5 100644 GIT binary patch delta 6523 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 QwGZ{3?y+zGec{jk4`YzC!T*SC3ukZVQzoHy& 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 sAN~8AubBV4tp^Pao&0OXH-l7;Q)YAHyD4p#T5? literal 13421 zcmeHsdvqIRmM6_Q&B{V5jVa0Z-s;tNlCAcaSrX0)=+X*4TFi9zp z?2-q~G>m0QWHxD-Zk#lP&S(&u=_zyQ9(Qjq2E=2S{ysS|O6&xZ5Zih96_OChql1@b zX}0UzTgq~FX3o@{Gym-FInz#ZBTK6K?(=tl_tq^pee2sdzz_Krx#5Oy!JmKa&$n*4 z@n3(@|2c%%m=OCLiAulOCHZ^%0DSUwN&e50{2j>O!#fAz{Vz-Mf0X2JL4Iw2WgjNE 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 <= 0)) - return; - - int civ_id = leader->ID; - if ((*p_human_player_bits & (1 << civ_id)) != 0) - return; - - for (int ei = 0; ei < is->ai_candidate_bridge_or_canals_count; ei++) { - struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[ei]; - if (entry->completed) - continue; - - int district_id = entry->district_id; - if ((district_id == CANAL_DISTRICT_ID) && (! is->current_config.enable_canal_districts)) continue; - if ((district_id == BRIDGE_DISTRICT_ID) && (! is->current_config.enable_bridge_districts)) continue; - if (! leader_can_build_district (leader, district_id)) continue; - - if (entry->assigned_worker_id >= 0) { - Unit * worker = get_unit_ptr (entry->assigned_worker_id); - if (worker == NULL) { - release_ai_candidate_bridge_or_canal_worker (entry); - } else if ((entry->assigned_tile_index >= 0) && (entry->assigned_tile_index < entry->tile_count)) { - int tx = entry->tile_x[entry->assigned_tile_index]; - int ty = entry->tile_y[entry->assigned_tile_index]; - wrap_tile_coords (&p_bic_data->Map, &tx, &ty); - Tile * tile = tile_at (tx, ty); - if ((tile != NULL) && (tile != p_null_tile)) { - struct district_instance * inst = get_district_instance (tile); - if (inst != NULL && inst->district_id == district_id && district_is_complete (tile, district_id)) { - release_ai_candidate_bridge_or_canal_worker (entry); - } - } - } - if (entry->assigned_worker_id >= 0) - continue; - } - - int target_idx = -1; - if (! ai_candidate_bridge_or_canal_is_buildable_for_civ (entry, civ_id, &target_idx)) { - release_ai_candidate_bridge_or_canal_worker (entry); - continue; + if (is->ai_candidate_bridge_or_canals != NULL) { + for (int i = 0; i < is->ai_candidate_bridge_or_canals_capacity; i++) { + struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[i]; + if (entry->tile_x != NULL) + free (entry->tile_x); + if (entry->tile_y != NULL) + free (entry->tile_y); } - - if (target_idx < 0) - continue; - - City * city = find_city_for_tile (civ_id, entry->tile_x[target_idx], entry->tile_y[target_idx]); - if (city == NULL) - continue; - - Unit * worker = find_best_worker_for_district (leader, city, district_id, entry->tile_x[target_idx], entry->tile_y[target_idx]); - if (worker == NULL) - continue; - - memset (&entry->pending_req, 0, sizeof entry->pending_req); - entry->pending_req.district_id = district_id; - entry->pending_req.civ_id = civ_id; - if (! assign_worker_to_district (&entry->pending_req, worker, city, district_id, entry->tile_x[target_idx], entry->tile_y[target_idx])) - continue; - - entry->assigned_worker_id = worker->Body.ID; - entry->assigned_tile_index = target_idx; + free (is->ai_candidate_bridge_or_canals); + is->ai_candidate_bridge_or_canals = NULL; } + is->ai_candidate_bridge_or_canals_capacity = 0; + is->ai_candidate_bridge_or_canals_count = 0; + is->ai_candidate_bridge_or_canals_initialized = false; } -void -recompute_city_yields_with_districts (City * city) -{ - if (city == NULL) - return; - - bool prev_flag = is->distribution_hub_refresh_in_progress; - is->distribution_hub_refresh_in_progress = true; - patch_City_recompute_yields_and_happiness (city); - is->distribution_hub_refresh_in_progress = prev_flag; -} - -enum UnitStateType __fastcall -patch_City_instruct_worker (City * this, int edx, int tile_x, int tile_y, bool param_3, Unit * worker) -{ - return City_instruct_worker (this, __, tile_x, tile_y, param_3, worker); -} - -int -find_wonder_config_index_by_improvement_id (int improv_id) +bool +tile_has_district_at (int tile_x, int tile_y, int district_id) { - if (improv_id < 0) - return -1; - - char ss[200]; - - for (int wi = 0; wi < is->wonder_district_count; wi++) { - int bid = -1; - if (stable_look_up (&is->building_name_to_id, is->wonder_district_configs[wi].wonder_name, &bid) && - (bid == improv_id)) { - return wi; - } - } + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + if ((tile == NULL) || (tile == p_null_tile)) + return false; - return -1; + struct district_instance * inst = get_district_instance (tile); + return (inst != NULL) && (inst->district_id == district_id) && (district_is_complete (tile, district_id)); } -void set_wonder_built_flag (int improv_id, bool is_built); - -unsigned int -wonder_buildable_square_type_mask (struct wonder_district_config const * cfg) +bool +tile_is_land (int civ_id, int tile_x, int tile_y, bool must_be_same_owner) { - if (cfg == NULL) - return district_default_buildable_mask (); + if (must_be_same_owner && (civ_id <= 0)) + return false; - unsigned int mask = cfg->buildable_square_types_mask; - if (mask == 0) - mask = district_default_buildable_mask (); - return mask; + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + return (tile != NULL) && (tile != p_null_tile) && (! tile->vtable->m35_Check_Is_Water (tile)) && + ((! must_be_same_owner) || (tile->Territory_OwnerID == civ_id)); } -unsigned int -wonder_buildable_mask_for_improvement (int improv_id) +bool +tile_is_water (int tile_x, int tile_y) { - int windex = find_wonder_config_index_by_improvement_id (improv_id); - if (windex < 0) - return district_default_buildable_mask (); - return wonder_buildable_square_type_mask (&is->wonder_district_configs[windex]); + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + return (tile != NULL) && (tile != p_null_tile) && (tile->vtable->m35_Check_Is_Water (tile)); } bool -wonder_is_buildable_on_square_type (struct wonder_district_config const * cfg, Tile * tile) +ensure_ai_candidate_bridge_or_canals_capacity (int required) { - if ((cfg == NULL) || (tile == NULL) || (tile == p_null_tile)) + if (required <= 0) + return true; + if (required <= is->ai_candidate_bridge_or_canals_capacity) + return true; + int new_capacity = (is->ai_candidate_bridge_or_canals_capacity > 0) ? is->ai_candidate_bridge_or_canals_capacity * 2 : 4; + if (new_capacity < required) + new_capacity = required; + struct ai_candidate_bridge_or_canal_entry * larger = (struct ai_candidate_bridge_or_canal_entry *)realloc ( + is->ai_candidate_bridge_or_canals, new_capacity * sizeof *larger); + if (larger == NULL) return false; - - return tile_matches_square_type_mask (tile, wonder_buildable_square_type_mask (cfg)); + for (int i = is->ai_candidate_bridge_or_canals_capacity; i < new_capacity; i++) { + struct ai_candidate_bridge_or_canal_entry * entry = &larger[i]; + entry->tile_x = NULL; + entry->tile_y = NULL; + entry->tile_capacity = 0; + entry->district_id = -1; + entry->owner_civ_id = -1; + entry->tile_count = 0; + entry->assigned_tile_index = -1; + entry->assigned_worker_id = -1; + entry->completed = false; + struct pending_district_request * req = &entry->pending_req; + req->city = NULL; + req->city_id = -1; + req->civ_id = -1; + req->district_id = -1; + req->assigned_worker_id = -1; + req->target_x = -1; + req->target_y = -1; + req->worker_assigned_turn = 0; + } + is->ai_candidate_bridge_or_canals = larger; + is->ai_candidate_bridge_or_canals_capacity = new_capacity; + return true; } bool -wonder_is_buildable_on_tile (Tile * tile, int wonder_improv_id) +canal_has_different_adjacent_seas (int tile_x, int tile_y, int civ_id) { - if ((tile == NULL) || (tile == p_null_tile)) - return false; - - unsigned int mask = wonder_buildable_mask_for_improvement (wonder_improv_id); - return tile_matches_square_type_mask (tile, mask); -} + struct water_pair { + int dx1, dy1; + int dx2, dy2; + }; -int -get_wonder_improvement_id_from_index (int windex) -{ - if ((windex < 0) || (windex >= is->wonder_district_count)) - return -1; + const struct water_pair pairs[] = { + { 1, -1, -1, 1 }, // NE + SW + { 1, 1, -1, -1 }, // SE + NW + }; - char const * wonder_name = is->wonder_district_configs[windex].wonder_name; - if ((wonder_name == NULL) || (wonder_name[0] == '\0')) - return -1; + Map * map = &p_bic_data->Map; + bool require_owner = (civ_id >= 0); - int improv_id; - if (stable_look_up (&is->building_name_to_id, (char *)wonder_name, &improv_id)) - return improv_id; - else - return -1; -} + for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { + int ax = tile_x + pairs[i].dx1; + int ay = tile_y + pairs[i].dy1; + wrap_tile_coords (map, &ax, &ay); + Tile * first = tile_at (ax, ay); + if ((first == NULL) || (first == p_null_tile)) + continue; + if (! first->vtable->m35_Check_Is_Water (first)) + continue; + if (require_owner && (first->vtable->m38_Get_Territory_OwnerID (first) != civ_id)) + continue; -void -remember_pending_building_order (City * city, int improvement_id) -{ - if (! is->current_config.enable_districts || - (city == NULL) || - (improvement_id < 0)) - return; + int bx = tile_x + pairs[i].dx2; + int by = tile_y + pairs[i].dy2; + wrap_tile_coords (map, &bx, &by); + Tile * second = tile_at (bx, by); + if ((second == NULL) || (second == p_null_tile)) + continue; + if (! second->vtable->m35_Check_Is_Water (second)) + continue; + if (require_owner && (second->vtable->m38_Get_Territory_OwnerID (second) != civ_id)) + continue; - if ((*p_human_player_bits & (1 << city->Body.CivID)) != 0) - return; + int sea_a = first->vtable->m46_Get_ContinentID (first); + int sea_b = second->vtable->m46_Get_ContinentID (second); + if ((sea_a >= 0) && (sea_b >= 0) && (sea_a != sea_b)) + return true; + } - itable_insert (&is->city_pending_building_orders, (int)city, improvement_id); + return false; } +// Check if two water tiles can reach each other via water within a radius, excluding a blocked tile bool -lookup_pending_building_order (City * city, int * out_improv_id) +water_tiles_connected_within_radius (int start_x, int start_y, int target_x, int target_y, int block_x, int block_y, int radius) { - if (! is->current_config.enable_districts || - (city == NULL) || - (out_improv_id == NULL)) - return false; - - return itable_look_up (&is->city_pending_building_orders, (int)city, out_improv_id); -} - -void -forget_pending_building_order (City * city) -{ - if (! is->current_config.enable_districts || - (city == NULL)) - return; + // Simple BFS using a fixed-size visited array for tiles within radius + // workable_tile_counts[6] = 137 tiles for radius 6 + int max_tiles = 137; + int visited_x[137]; + int visited_y[137]; + int visited_count = 0; + int queue_x[137]; + int queue_y[137]; + int queue_head = 0; + int queue_tail = 0; - itable_remove (&is->city_pending_building_orders, (int)city); -} + queue_x[queue_tail] = start_x; + queue_y[queue_tail] = start_y; + queue_tail++; + visited_x[visited_count] = start_x; + visited_y[visited_count] = start_y; + visited_count++; -bool -is_wonder_or_small_wonder_already_being_built (City * city, int improv_id) -{ - if ((city == NULL) || (improv_id < 0) || (improv_id >= p_bic_data->ImprovementsCount)) - return false; + while (queue_head < queue_tail) { + int cx = queue_x[queue_head]; + int cy = queue_y[queue_head]; + queue_head++; - Improvement * improv = &p_bic_data->Improvements[improv_id]; - bool is_wonder = (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) != 0; - if ((improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) == 0) - return false; + // Check 8 adjacent tiles + FOR_TILES_AROUND (tai, 9, cx, cy) { + if (tai.n == 0) + continue; + int nx = tai.tile_x; + int ny = tai.tile_y; - int civ_id = city->Body.CivID; - FOR_CITIES_OF (coi, civ_id) { - City * other_city = coi.city; - if ((other_city == NULL) || (other_city == city)) - continue; + // Found target + if (nx == target_x && ny == target_y) + return true; - if ((other_city->Body.Order_Type == COT_Improvement) && - (other_city->Body.Order_ID == improv_id)) - return true; - } + // Skip blocked tile (the isthmus) + if (nx == block_x && ny == block_y) + continue; - return false; -} + // Check if within radius of original start + int dx = nx - start_x; + int dy = ny - start_y; + if (dx < 0) dx = -dx; + if (dy < 0) dy = -dy; + // Use Chebyshev distance approximation for hex grid + int dist = (dx + dy + ((dx > dy) ? dx : dy)) / 2; + if (dist > radius) + continue; -struct district_building_prereq_list * -get_district_building_prereq_list (int improv_id) -{ - if (improv_id < 0) - return NULL; + Tile * adj = tai.tile; + if ((adj == NULL) || (adj == p_null_tile)) + continue; + if (! adj->vtable->m35_Check_Is_Water (adj)) + continue; - int stored = 0; - if (! itable_look_up (&is->district_building_prereqs, improv_id, &stored)) - return NULL; - return (struct district_building_prereq_list *)stored; -} + // Check if already visited + bool already_visited = false; + for (int i = 0; i < visited_count; i++) { + if (visited_x[i] == nx && visited_y[i] == ny) { + already_visited = true; + break; + } + } + if (already_visited) + continue; -bool -district_building_prereq_list_contains (struct district_building_prereq_list * list, int district_id) -{ - if ((list == NULL) || (district_id < 0)) - return false; - for (int i = 0; i < list->count; i++) { - if (list->district_ids[i] == district_id) - return true; + // Add to queue and visited + if (visited_count < max_tiles && queue_tail < max_tiles) { + visited_x[visited_count] = nx; + visited_y[visited_count] = ny; + visited_count++; + queue_x[queue_tail] = nx; + queue_y[queue_tail] = ny; + queue_tail++; + } + } } return false; } -void -add_district_building_prereq (int improv_id, int district_id) +// Check if tile separates adjacent water tiles that are not connected within a small radius +bool +canal_has_same_sea_isthmus (int tile_x, int tile_y, int civ_id, int check_radius) { - if ((improv_id < 0) || (district_id < 0)) - return; + (void) civ_id; + (void) check_radius; - struct district_building_prereq_list * list = get_district_building_prereq_list (improv_id); - if (list == NULL) { - list = calloc (1, sizeof *list); - if (list == NULL) - return; - list->count = 0; - itable_insert (&is->district_building_prereqs, improv_id, (int)list); + // If another canal exists nearby, this isn't a unique isthmus target. + FOR_TILES_AROUND (tai, workable_tile_counts[2], tile_x, tile_y) { + if (tai.n == 0) + continue; + Tile * adj = tai.tile; + if ((adj == NULL) || (adj == p_null_tile)) + continue; + struct district_instance * adj_inst = get_district_instance (adj); + if ((adj_inst != NULL) && (adj_inst->district_id == CANAL_DISTRICT_ID)) + return false; } - if (district_building_prereq_list_contains (list, district_id)) - return; + // Check opposite diagonal water tiles that are not connected within radius 2 + struct water_pair { + int dx1, dy1; + int dx2, dy2; + }; - if (list->count >= ARRAY_LEN (list->district_ids)) - return; + const struct water_pair pairs[] = { + { 1, -1, -1, 1 }, // NE + SW + { 1, 1, -1, -1 }, // SE + NW + }; - list->district_ids[list->count++] = district_id; -} + for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { + int ax = tile_x + pairs[i].dx1; + int ay = tile_y + pairs[i].dy1; + wrap_tile_coords (&p_bic_data->Map, &ax, &ay); + Tile * first = tile_at (ax, ay); + if ((first == NULL) || (first == p_null_tile)) + continue; + if (! first->vtable->m35_Check_Is_Water (first)) + continue; -bool -city_has_river_district (City * city, int district_id) -{ - if (city == NULL) - return false; - FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { - if (wai.district_inst->district_id != district_id) + int bx = tile_x + pairs[i].dx2; + int by = tile_y + pairs[i].dy2; + wrap_tile_coords (&p_bic_data->Map, &bx, &by); + Tile * second = tile_at (bx, by); + if ((second == NULL) || (second == p_null_tile)) continue; - if (wai.tile->vtable->m37_Get_River_Code (wai.tile) != 0) + if (! second->vtable->m35_Check_Is_Water (second)) + continue; + + if (! water_tiles_connected_within_radius ( + ax, ay, + bx, by, + tile_x, tile_y, 2)) { return true; + } } return false; } bool -city_has_any_prereq_district_for_improvement (City * city, - struct district_building_prereq_list * list, - bool requires_river, - bool allow_wonder_district) +bridge_tile_connects_two_continents (int tile_x, int tile_y, int civ_id) { - if ((city == NULL) || (list == NULL)) - return false; + struct bridge_pair { + int dx1, dy1; + int dx2, dy2; + }; - for (int i = 0; i < list->count; i++) { - int district_id = list->district_ids[i]; - if (district_id < 0) + Map * map = &p_bic_data->Map; + const struct bridge_pair pairs[] = { + {0, -2, 0, 2}, + {-2, 0, 2, 0}, + {-1, -1, 1, 1}, + {-1, 1, 1, -1}, + }; + + bool require_owner = (civ_id >= 0); + + for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { + int ax = tile_x + pairs[i].dx1; + int ay = tile_y + pairs[i].dy1; + wrap_tile_coords (map, &ax, &ay); + if (! tile_is_land (civ_id, ax, ay, require_owner)) continue; - if (! allow_wonder_district && district_id == WONDER_DISTRICT_ID) + Tile * first = tile_at (ax, ay); + if ((first == NULL) || (first == p_null_tile)) continue; - if (requires_river) { - if (city_has_river_district (city, district_id)) - return true; - } else if (city_has_required_district (city, district_id)) { + + int bx = tile_x + pairs[i].dx2; + int by = tile_y + pairs[i].dy2; + wrap_tile_coords (map, &bx, &by); + if (! tile_is_land (civ_id, bx, by, require_owner)) + continue; + Tile * second = tile_at (bx, by); + if ((second == NULL) || (second == p_null_tile)) + continue; + + int cont_a = first->vtable->m46_Get_ContinentID (first); + int cont_b = second->vtable->m46_Get_ContinentID (second); + if ((cont_a >= 0) && (cont_b >= 0) && (cont_a != cont_b)) return true; - } } + return false; } -int -pick_missing_district_for_improvement (City * city, struct district_building_prereq_list * list) +bool +tile_point_in_block (int tile_x, int tile_y, int block_x0, int block_y0, int block_x1, int block_y1) { - if ((list == NULL) || (list->count <= 0)) - return -1; + return (tile_x >= block_x0) && (tile_x < block_x1) && (tile_y >= block_y0) && (tile_y < block_y1); +} - int fallback = -1; - for (int i = 0; i < list->count; i++) { - int district_id = list->district_ids[i]; - if (district_id < 0) - continue; - if (fallback < 0) - fallback = district_id; - if ((city != NULL) && leader_can_build_district (&leaders[city->Body.CivID], district_id)) - return district_id; - } - return fallback; +bool +tile_is_coastal_water (int tile_x, int tile_y) +{ + Map * map = &p_bic_data->Map; + wrap_tile_coords (map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + if ((tile == NULL) || (tile == p_null_tile)) + return false; + if (! tile->vtable->m35_Check_Is_Water (tile)) + return false; + enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); + return base_type == SQ_Coast; } +bool +tile_exists_at (int tile_x, int tile_y) +{ + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + return (tile != NULL) && (tile != p_null_tile); +} bool -district_is_complete(Tile * tile, int district_id) +tile_is_reserved_in_district_tile_map (int tile_x, int tile_y) { - if ((tile == NULL) || (tile == p_null_tile) || - (district_id < 0) || (district_id >= is->district_count)) + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + Tile * tile = tile_at (tile_x, tile_y); + if ((tile == NULL) || (tile == p_null_tile)) return false; + int existing = 0; + return itable_look_up (&is->district_tile_map, (int)tile, &existing); +} - bool is_natural_wonder = (district_id == NATURAL_WONDER_DISTRICT_ID); - bool districts_disabled = ! is->current_config.enable_districts; - bool natural_wonders_disabled = ! is->current_config.enable_natural_wonders; - struct district_config const * cfg = &is->district_configs[district_id]; +bool +tile_has_diagonal_water (int tile_x, int tile_y) +{ + Map * map = &p_bic_data->Map; + int const adj_dx[4] = { 1, 1, -1, -1 }; + int const adj_dy[4] = { -1, 1, -1, 1 }; - if (districts_disabled && (!is_natural_wonder || natural_wonders_disabled)) - return false; + for (int i = 0; i < 4; i++) { + int nx = tile_x + adj_dx[i]; + int ny = tile_y + adj_dy[i]; + wrap_tile_coords (map, &nx, &ny); + Tile * tile = tile_at (nx, ny); + if ((tile == NULL) || (tile == p_null_tile)) + continue; + if (tile->vtable->m35_Check_Is_Water (tile)) + return true; + } + return false; +} - struct district_instance * inst = get_district_instance (tile); - if (inst == NULL || inst->district_id != district_id) - return false; +bool +bridge_tile_has_land_on_both_sides (int tile_x, int tile_y) +{ + struct bridge_pair { + int dx1, dy1; + int dx2, dy2; + }; - // If already marked COMPLETED, just return true - if (inst->state == DS_COMPLETED) + const struct bridge_pair pairs[] = { + { 0, -2, 0, 2 }, + { -2, 0, 2, 0 }, + { -1, -1, 1, 1 }, + { -1, 1, 1, -1 }, + }; + + for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { + int ax = tile_x + pairs[i].dx1; + int ay = tile_y + pairs[i].dy1; + if (! tile_exists_at (ax, ay)) + continue; + if (! tile_is_land (-1, ax, ay, false)) + continue; + int bx = tile_x + pairs[i].dx2; + int by = tile_y + pairs[i].dy2; + if (! tile_exists_at (bx, by)) + continue; + if (! tile_is_land (-1, bx, by, false)) + continue; return true; + } - // State is UNDER_CONSTRUCTION - check if tile has mines now - bool has_mines = tile->vtable->m18_Check_Mines (tile, __, 0) != 0; + return false; +} - if (! has_mines) { - // Still under construction - check if we should clean it up - bool worker_present = false; - FOR_UNITS_ON (uti, tile) { - Unit * unit = uti.unit; - if ((unit != NULL) && - (p_bic_data->UnitTypes[unit->Body.UnitTypeID].Worker_Actions != 0)) { - worker_present = true; - break; - } - } - if (! worker_present) { - remove_district_instance (tile); +bool +tile_part_of_existing_candidate (int tile_x, int tile_y) +{ + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + for (int ei = 0; ei < is->ai_candidate_bridge_or_canals_count; ei++) { + struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[ei]; + if (entry->tile_count <= 0) + continue; + for (int ti = 0; ti < entry->tile_count; ti++) { + if ((entry->tile_x[ti] == tile_x) && (entry->tile_y[ti] == tile_y)) + return true; } - return false; } + return false; +} - // Mark as completed and run one-time side effects - inst->state = DS_COMPLETED; - inst->completed_turn = *p_current_turn_no; +bool +tile_has_bridge_or_canal_nearby (int tile_x, int tile_y) +{ + FOR_TILES_AROUND (tai, workable_tile_counts[1], tile_x, tile_y) { + Tile * adj = tai.tile; + if ((adj == NULL) || (adj == p_null_tile)) + continue; + struct district_instance * inst = get_district_instance (adj); + if ((inst != NULL) && + ((inst->district_id == BRIDGE_DISTRICT_ID) || (inst->district_id == CANAL_DISTRICT_ID))) + return true; + int nx = (tai.n == 0) ? tile_x : tai.tile_x; + int ny = (tai.n == 0) ? tile_y : tai.tile_y; + if (tile_part_of_existing_candidate (nx, ny)) + return true; + } + return false; +} - int tile_x, tile_y; - if (district_instance_get_coords (inst, tile, &tile_x, &tile_y)) { - set_tile_unworkable_for_all_cities (tile, tile_x, tile_y); - int territory_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); +bool +add_ai_candidate_entry (int district_id, short owner_civ_id, short * xs, short * ys, int count) +{ + if (count <= 0) + return false; + if (! ensure_ai_candidate_bridge_or_canals_capacity (is->ai_candidate_bridge_or_canals_count + 1)) + return false; - if (cfg->auto_add_road) { - bool has_road = tile->vtable->m25_Check_Roads (tile, __, 0) != 0; - if (! has_road) - tile->vtable->m56_Set_Tile_Flags (tile, __, 0, TILE_FLAG_ROAD, tile_x, tile_y); - } + struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[is->ai_candidate_bridge_or_canals_count]; + entry->tile_x = (short *)malloc (sizeof *entry->tile_x * count); + entry->tile_y = (short *)malloc (sizeof *entry->tile_y * count); + if ((entry->tile_x == NULL) || (entry->tile_y == NULL)) { + if (entry->tile_x != NULL) + free (entry->tile_x); + if (entry->tile_y != NULL) + free (entry->tile_y); + return false; + } - if (cfg->auto_add_railroad) { - bool has_railroad = tile->vtable->m23_Check_Railroads (tile, __, 0) != 0; - if (! has_railroad) { - if ((territory_owner >= 0) && Leader_can_do_worker_job (&leaders[territory_owner], __, WJ_Build_Railroad, tile_x, tile_y, 0)) { - tile->vtable->m56_Set_Tile_Flags (tile, __, 0, TILE_FLAG_RAILROAD, tile_x, tile_y); - } - } - } + entry->district_id = district_id; + entry->owner_civ_id = owner_civ_id; + entry->tile_count = (short)count; + entry->tile_capacity = count; + entry->assigned_tile_index = -1; + entry->assigned_worker_id = -1; + entry->completed = false; + for (int ti = 0; ti < count; ti++) { + entry->tile_x[ti] = xs[ti]; + entry->tile_y[ti] = ys[ti]; + } - // Activate distribution hub if applicable - if (is->current_config.enable_distribution_hub_districts && - (district_id == DISTRIBUTION_HUB_DISTRICT_ID)) { - on_distribution_hub_completed (tile, tile_x, tile_y); - } + struct pending_district_request * req = &entry->pending_req; + req->city = NULL; + req->city_id = -1; + req->civ_id = owner_civ_id; + req->district_id = district_id; + req->assigned_worker_id = -1; + req->target_x = -1; + req->target_y = -1; + req->worker_assigned_turn = 0; - // Remove forest/swamp if applicable - enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); - if ((base_type == SQ_Forest) || (base_type == SQ_Swamp)) { - int new_terrain_type = tile->vtable->m71_Check_Worker_Job (tile); - if (new_terrain_type >= 0) - Map_change_tile_terrain (&p_bic_data->Map, __, (enum SquareTypes)new_terrain_type, tile_x, tile_y); - } + is->ai_candidate_bridge_or_canals_count++; + return true; +} - char ss[200]; - snprintf (ss, sizeof ss, "District %d completed at tile (%d,%d)\n", district_id, tile_x, tile_y); - (*p_OutputDebugStringA) (ss); +int +gather_bridge_line (int start_x, int start_y, int dx, int dy, int limit, + int block_x0, int block_y0, int block_x1, int block_y1, + short * out_x, short * out_y) +{ + int effective_limit = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, limit); - // Check if this was an AI-requested district - struct pending_district_request * req = find_pending_district_request_by_coords (NULL, tile_x, tile_y, district_id); - if (req != NULL) { - City * requesting_city = get_city_ptr (req->city_id); - if (requesting_city != NULL) { - req->city = requesting_city; - snprintf (ss, sizeof ss, "District %d at tile (%d,%d) was ordered by city %s\n", - district_id, tile_x, tile_y, requesting_city->Body.CityName); - (*p_OutputDebugStringA) (ss); + if (! tile_is_coastal_water (start_x, start_y)) + return 0; + if (! bridge_tile_has_land_on_both_sides (start_x, start_y)) + return 0; - // Check if city has pending building order that depends on this district - int pending_improv_id; - if (lookup_pending_building_order (requesting_city, &pending_improv_id)) { - struct district_building_prereq_list * prereq_list = get_district_building_prereq_list (pending_improv_id); - if (district_building_prereq_list_contains (prereq_list, district_id)) { - snprintf (ss, sizeof ss, "City %s setting production to improvement %d\n", - requesting_city->Body.CityName, pending_improv_id); - (*p_OutputDebugStringA) (ss); + short back_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + short back_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + short forward_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + short forward_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + int back_count = 0; + int forward_count = 0; + int remaining = effective_limit - 1; + Map * map = &p_bic_data->Map; - // Check if another city is already building this wonder/small wonder - bool can_set_production = true; - if (is_wonder_or_small_wonder_already_being_built (requesting_city, pending_improv_id)) { - snprintf (ss, sizeof ss, "City %s cannot build improvement %d - already being built elsewhere\n", - requesting_city->Body.CityName, pending_improv_id); - (*p_OutputDebugStringA) (ss); - can_set_production = false; - } - - // Set city production to the pending improvement - if (can_set_production && City_can_build_improvement (requesting_city, __, pending_improv_id, false)) { - snprintf (ss, sizeof ss, "City %s can now build improvement %d\n", - requesting_city->Body.CityName, pending_improv_id); - (*p_OutputDebugStringA) (ss); - City_set_production (requesting_city, __, COT_Improvement, pending_improv_id, false); - } - - // Clear the pending building order - forget_pending_building_order (requesting_city); - } - } + int cx = start_x; + int cy = start_y; + while ((remaining > 0) && (back_count < effective_limit)) { + int nx = cx - dx; + int ny = cy - dy; + wrap_tile_coords (map, &nx, &ny); + if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) + break; + if (! tile_is_coastal_water (nx, ny)) + break; + if (! bridge_tile_has_land_on_both_sides (nx, ny)) + break; + if (tile_part_of_existing_candidate (nx, ny)) + break; + back_x[back_count] = (short)nx; + back_y[back_count] = (short)ny; + back_count++; + remaining--; + cx = nx; + cy = ny; + } - // Clear worker assignment so worker is freed up for other tasks - if (req->assigned_worker_id >= 0) { - snprintf (ss, sizeof ss, "Clearing worker assignment for unit %d\n", req->assigned_worker_id); - (*p_OutputDebugStringA) (ss); - int civ_id = req->civ_id; - clear_tracked_worker_assignment_by_id (civ_id, req->assigned_worker_id); - } - } + cx = start_x; + cy = start_y; + while ((remaining > 0) && (forward_count < effective_limit)) { + int nx = cx + dx; + int ny = cy + dy; + wrap_tile_coords (map, &nx, &ny); + if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) + break; + if (! tile_is_coastal_water (nx, ny)) + break; + if (! bridge_tile_has_land_on_both_sides (nx, ny)) + break; + if (tile_part_of_existing_candidate (nx, ny)) + break; + forward_x[forward_count] = (short)nx; + forward_y[forward_count] = (short)ny; + forward_count++; + remaining--; + cx = nx; + cy = ny; + } - // Remove the pending district request - remove_pending_district_request (req); - } + int write = 0; + for (int bi = back_count - 1; bi >= 0; bi--) { + out_x[write] = back_x[bi]; + out_y[write] = back_y[bi]; + write++; + } + out_x[write] = (short)start_x; + out_y[write] = (short)start_y; + write++; + for (int fi = 0; fi < forward_count; fi++) { + out_x[write] = forward_x[fi]; + out_y[write] = forward_y[fi]; + write++; } - return true; + return write; } -void -mark_city_needs_district (City * city, int district_id) +bool +bridge_line_connects_two_continents (short * xs, short * ys, int count) { - if (! is->current_config.enable_districts || - (city == NULL) || - (district_id < 0) || (district_id >= is->district_count)) - return; - - create_pending_district_request (city, district_id); + for (int i = 0; i < count; i++) { + if (bridge_tile_connects_two_continents (xs[i], ys[i], -1)) + return true; + } + return false; } -void -set_tile_unworkable_for_all_cities (Tile * tile, int tile_x, int tile_y) +int +find_bridge_candidate_in_block (Map * map, int block_x0, int block_y0, int block_x1, int block_y1, + int contiguous_limit, int candidate_capacity, + short * out_x, short * out_y, short * out_owner) { - if ((tile == NULL) || (tile == p_null_tile)) - return; - - wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); - - City * assigned_city = NULL; - int assigned_city_id = tile->Body.CityAreaID; - if (assigned_city_id >= 0) - assigned_city = get_city_ptr (assigned_city_id); + const int dirs[4][2] = { + { 0, -2 }, { -2, 0 }, { -1, -1 }, { -1, 1 } + }; - if (assigned_city != NULL) { - int neighbor_index = Map_compute_neighbor_index (&p_bic_data->Map, __, - assigned_city->Body.X, assigned_city->Body.Y, tile_x, tile_y, 1000); - bool removed_assignment = false; - if ((neighbor_index > 0) && (neighbor_index < ARRAY_LEN (is->ni_to_work_radius))) - removed_assignment = City_stop_working_tile (assigned_city, __, neighbor_index); - if (! removed_assignment) - tile->Body.CityAreaID = -1; - if (! removed_assignment) - recompute_city_yields_with_districts (assigned_city); - } else - tile->Body.CityAreaID = -1; + int max_len = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, contiguous_limit); + if (candidate_capacity > 0 && max_len > candidate_capacity) + max_len = candidate_capacity; - if (p_cities->Cities != NULL) { - for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { - City * city = get_city_ptr (city_index); - if ((city == NULL) || (city == assigned_city)) + for (int length = 1; length <= max_len; length++) { + for (int ti = 0; ti < map->TileCount; ti++) { + int tx = -1; + int ty = -1; + tile_index_to_coords (map, ti, &tx, &ty); + if (! tile_point_in_block (tx, ty, block_x0, block_y0, block_x1, block_y1)) continue; - int neighbor_index = Map_compute_neighbor_index (&p_bic_data->Map, __, - city->Body.X, city->Body.Y, tile_x, tile_y, 1000); - if ((neighbor_index <= 0) || (neighbor_index >= ARRAY_LEN (is->ni_to_work_radius))) + Tile * tile = tile_at (tx, ty); + if ((tile == NULL) || (tile == p_null_tile)) continue; - int work_radius = is->ni_to_work_radius[neighbor_index]; - if ((work_radius < 0) || (work_radius > is->current_config.city_work_radius)) + if (tile_has_resource (tile)) continue; - recompute_city_yields_with_districts (city); - } - } -} + if (! district_is_buildable_on_square_type (&is->district_configs[BRIDGE_DISTRICT_ID], tile)) + continue; + if (tile_is_reserved_in_district_tile_map (tx, ty)) + continue; + short owner = tile->vtable->m38_Get_Territory_OwnerID (tile); -struct distribution_hub_record * -get_distribution_hub_record (Tile * tile) -{ - if ((tile == NULL) || (tile == p_null_tile)) - return NULL; + for (int di = 0; di < (int)(sizeof (dirs) / sizeof (dirs[0])); di++) { + int dx = dirs[di][0]; + int dy = dirs[di][1]; + int end_x = tx + dx * (length - 1); + int end_y = ty + dy * (length - 1); + wrap_tile_coords (map, &end_x, &end_y); + if (! tile_point_in_block (end_x, end_y, block_x0, block_y0, block_x1, block_y1)) + continue; - int stored; - if (itable_look_up (&is->distribution_hub_records, (int)tile, &stored)) - return (struct distribution_hub_record *)stored; - else - return NULL; -} + bool ok = true; + for (int step = 0; step < length; step++) { + int cx = tx + dx * step; + int cy = ty + dy * step; + wrap_tile_coords (map, &cx, &cy); + if (! tile_point_in_block (cx, cy, block_x0, block_y0, block_x1, block_y1)) { + ok = false; + break; + } + if (! tile_exists_at (cx, cy)) { + ok = false; + break; + } + if (! tile_is_coastal_water (cx, cy)) { + ok = false; + break; + } + Tile * bridge_tile = tile_at (cx, cy); + if ((bridge_tile == NULL) || (bridge_tile == p_null_tile)) { + ok = false; + break; + } + if (tile_has_resource (bridge_tile)) { + ok = false; + break; + } + if (! district_is_buildable_on_square_type (&is->district_configs[BRIDGE_DISTRICT_ID], bridge_tile)) { + ok = false; + break; + } + if (tile_has_bridge_or_canal_nearby (cx, cy)) { + ok = false; + break; + } + if (tile_is_reserved_in_district_tile_map (cx, cy)) { + ok = false; + break; + } + if (tile_part_of_existing_candidate (cx, cy)) { + ok = false; + break; + } + } + if (! ok) + continue; -City * -get_connected_city_for_distribution_hub (struct distribution_hub_record * rec) -{ - if (rec == NULL) - return NULL; + int land_ax = tx - dx; + int land_ay = ty - dy; + wrap_tile_coords (map, &land_ax, &land_ay); + if (! tile_point_in_block (land_ax, land_ay, block_x0, block_y0, block_x1, block_y1)) + continue; + if (! tile_exists_at (land_ax, land_ay)) + continue; + if (! tile_is_land (-1, land_ax, land_ay, false)) + continue; + Tile * land_a = tile_at (land_ax, land_ay); + if ((land_a == NULL) || (land_a == p_null_tile)) + continue; - Tile * tile = rec->tile; - if ((tile == NULL) || (tile == p_null_tile)) - tile = tile_at (rec->tile_x, rec->tile_y); - if ((tile == NULL) || (tile == p_null_tile)) - return NULL; + int land_bx = tx + dx * length; + int land_by = ty + dy * length; + wrap_tile_coords (map, &land_bx, &land_by); + if (! tile_point_in_block (land_bx, land_by, block_x0, block_y0, block_x1, block_y1)) + continue; + if (! tile_exists_at (land_bx, land_by)) + continue; + if (! tile_is_land (-1, land_bx, land_by, false)) + continue; + Tile * land_b = tile_at (land_bx, land_by); + if ((land_b == NULL) || (land_b == p_null_tile)) + continue; - int city_id = tile->Body.connected_city_ids[rec->civ_id]; - if (city_id < 0) - return NULL; - - City * city = get_city_ptr (city_id); - - return city; -} - -bool -distribution_hub_accessible_to_city (struct distribution_hub_record * rec, City * city) -{ - if ((rec == NULL) || (city == NULL)) - return false; - - if (city->Body.CivID != rec->civ_id) - return false; - - City * anchor_city = get_connected_city_for_distribution_hub (rec); - if (anchor_city == NULL) - return false; - - if (anchor_city == city) - return true; - - return Trade_Net_have_trade_connection (is->trade_net, __, anchor_city, city, rec->civ_id); -} - -void -get_distribution_hub_yields_for_city (City * city, int * out_food, int * out_shields) -{ - int food = 0; - int shields = 0; - - if ((city != NULL) && - is->current_config.enable_districts && - is->current_config.enable_distribution_hub_districts) { - if (is->distribution_hub_totals_dirty && - ! is->distribution_hub_refresh_in_progress) - recompute_distribution_hub_totals (); + int cont_a = land_a->vtable->m46_Get_ContinentID (land_a); + int cont_b = land_b->vtable->m46_Get_ContinentID (land_b); + if ((cont_a < 0) || (cont_b < 0) || (cont_a == cont_b)) + continue; - FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { - struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; - if (distribution_hub_accessible_to_city (rec, city)) { - food += rec->food_yield; - shields += rec->shield_yield; + for (int step = 0; step < length; step++) { + int cx = tx + dx * step; + int cy = ty + dy * step; + wrap_tile_coords (map, &cx, &cy); + out_x[step] = (short)cx; + out_y[step] = (short)cy; + } + *out_owner = owner; + return length; } } } - - if (city_has_required_district (city, CENTRAL_RAIL_HUB_DISTRICT_ID)) { - food += (food * is->current_config.central_rail_hub_distribution_food_bonus_percent) / 100; - shields += (shields * is->current_config.central_rail_hub_distribution_shield_bonus_percent) / 100; - } - - if (out_food != NULL) - *out_food = food; - if (out_shields != NULL) - *out_shields = shields; + return 0; } -void -adjust_distribution_hub_coverage (struct distribution_hub_record * rec) +int +gather_canal_line (int start_x, int start_y, int dx, int dy, int limit, + int block_x0, int block_y0, int block_x1, int block_y1, + short * out_x, short * out_y) { - if (rec == NULL) - return; - - FOR_TILES_AROUND (tai, workable_tile_counts[1], rec->tile_x, rec->tile_y) { - Tile * area_tile = tai.tile; - if ((area_tile == NULL) || (area_tile == p_null_tile)) - continue; - - int tx, ty; - tai_get_coords (&tai, &tx, &ty); - - if (area_tile->vtable->m38_Get_Territory_OwnerID (area_tile) != rec->civ_id) - continue; - if (Tile_has_city (area_tile)) - continue; - if (get_district_instance (area_tile) != NULL) - continue; + int effective_limit = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, limit); - struct wonder_district_info * area_info = get_wonder_district_info (area_tile); - if ((area_info != NULL) && (area_info->state == WDS_COMPLETED)) - continue; + if (! tile_is_land (-1, start_x, start_y, false)) + return 0; + if (tile_part_of_existing_candidate (start_x, start_y)) + return 0; - int key = (int)area_tile; - int prev = itable_look_up_or (&is->distribution_hub_coverage_counts, key, 0); - itable_insert (&is->distribution_hub_coverage_counts, key, prev + 1); + short back_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + short back_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + short forward_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + short forward_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + int back_count = 0; + int forward_count = 0; + int remaining = effective_limit - 1; + Map * map = &p_bic_data->Map; - if (area_tile->Body.CityAreaID >= 0) { - set_tile_unworkable_for_all_cities (area_tile, tx, ty); - area_tile->Body.CityAreaID = -1; - } + int cx = start_x; + int cy = start_y; + while ((remaining > 0) && (back_count < effective_limit)) { + int nx = cx - dx; + int ny = cy - dy; + wrap_tile_coords (map, &nx, &ny); + if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) + break; + if (! tile_is_land (-1, nx, ny, false)) + break; + if (tile_part_of_existing_candidate (nx, ny)) + break; + back_x[back_count] = (short)nx; + back_y[back_count] = (short)ny; + back_count++; + remaining--; + cx = nx; + cy = ny; } -} - -void -release_distribution_hub_coverage (struct distribution_hub_record * rec) -{ - if (rec == NULL) - return; - - FOR_TILES_AROUND (tai, workable_tile_counts[1], rec->tile_x, rec->tile_y) { - Tile * area_tile = tai.tile; - if ((area_tile == NULL) || (area_tile == p_null_tile)) - continue; - - int key = (int)area_tile; - int prev = itable_look_up_or (&is->distribution_hub_coverage_counts, key, 0); - if (prev <= 0) - continue; - if (prev == 1) - itable_remove (&is->distribution_hub_coverage_counts, key); - else - itable_insert (&is->distribution_hub_coverage_counts, key, prev - 1); + cx = start_x; + cy = start_y; + while ((remaining > 0) && (forward_count < effective_limit)) { + int nx = cx + dx; + int ny = cy + dy; + wrap_tile_coords (map, &nx, &ny); + if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) + break; + if (! tile_is_land (-1, nx, ny, false)) + break; + if (tile_part_of_existing_candidate (nx, ny)) + break; + forward_x[forward_count] = (short)nx; + forward_y[forward_count] = (short)ny; + forward_count++; + remaining--; + cx = nx; + cy = ny; } -} -void -clear_distribution_hub_tables (void) -{ - FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { - struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; - free (rec); + int write = 0; + for (int bi = back_count - 1; bi >= 0; bi--) { + out_x[write] = back_x[bi]; + out_y[write] = back_y[bi]; + write++; } - table_deinit (&is->distribution_hub_records); - table_deinit (&is->distribution_hub_coverage_counts); - is->distribution_hub_totals_dirty = true; + out_x[write] = (short)start_x; + out_y[write] = (short)start_y; + write++; + for (int fi = 0; fi < forward_count; fi++) { + out_x[write] = forward_x[fi]; + out_y[write] = forward_y[fi]; + write++; + } + + return write; } bool -city_radius_contains_tile (City * city, int tile_x, int tile_y) +cluster_connects_two_seas_or_isthmus (short * xs, short * ys, int count) { - if (city == NULL) - return false; - - int ni = patch_Map_compute_ni_for_work_area (&p_bic_data->Map, __, city->Body.X, city->Body.Y, tile_x, tile_y, is->workable_tile_count); - return ni >= 0; + for (int i = 0; i < count; i++) { + if (canal_has_different_adjacent_seas (xs[i], ys[i], -1)) + return true; + if (canal_has_same_sea_isthmus (xs[i], ys[i], -1, 2)) + return true; + } + return false; } -void -recompute_distribution_hub_yields (struct distribution_hub_record * rec) +int +find_canal_candidate_in_block (Map * map, int block_x0, int block_y0, int block_x1, int block_y1, + int contiguous_limit, int candidate_capacity, + short * out_x, short * out_y, short * out_owner) { - if (rec == NULL) - return; + const int dir_dx[8] = { 0, 1, 2, 1, 0, -1, -2, -1 }; + const int dir_dy[8] = { -2, -1, 0, 1, 2, 1, 0, -1 }; - Tile * tile = tile_at (rec->tile_x, rec->tile_y); - rec->tile = tile; + int max_len = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, contiguous_limit); + if (candidate_capacity > 0 && max_len > candidate_capacity) + max_len = candidate_capacity; - if ((tile == NULL) || - (tile == p_null_tile) || - ! district_is_complete (tile, DISTRIBUTION_HUB_DISTRICT_ID) || - tile->vtable->m20_Check_Pollution (tile, __, 0) || - tile_has_enemy_unit (tile, rec->civ_id)) { - rec->food_yield = 0; - rec->shield_yield = 0; - rec->raw_food_yield = 0; - rec->raw_shield_yield = 0; - return; + int tile_count = map->TileCount; + int * visit = (int *)malloc (sizeof (*visit) * tile_count); + int * queue_x = (int *)malloc (sizeof (*queue_x) * tile_count); + int * queue_y = (int *)malloc (sizeof (*queue_y) * tile_count); + if ((visit == NULL) || (queue_x == NULL) || (queue_y == NULL)) { + if (visit != NULL) + free (visit); + if (queue_x != NULL) + free (queue_x); + if (queue_y != NULL) + free (queue_y); + return 0; } + for (int i = 0; i < tile_count; i++) + visit[i] = 0; - int food_sum = 0; - int shield_sum = 0; - City * anchor_city = get_connected_city_for_distribution_hub (rec); - FOR_TILES_AROUND (tai, workable_tile_counts[1], rec->tile_x, rec->tile_y) { - Tile * area_tile = tai.tile; - if (area_tile == p_null_tile) - continue; - - int tx, ty; - tai_get_coords (&tai, &tx, &ty); - - // Only include tiles that belong to the distribution hub owner - if (area_tile->vtable->m38_Get_Territory_OwnerID (area_tile) != rec->civ_id) - continue; - - // Skip city tiles - if (Tile_has_city (area_tile)) - continue; - - // Skip tiles with enemy units - if (tile_has_enemy_unit (area_tile, rec->civ_id)) - continue; - - // Skip tiles with pollution - if (area_tile->vtable->m20_Check_Pollution (area_tile, __, 0)) - continue; - - // Skip tiles that are other districts (but not this hub itself) - struct district_instance * area_district = get_district_instance (area_tile); - if ((area_district != NULL) && ((tx != rec->tile_x) || (ty != rec->tile_y))) - continue; - - // Skip tiles with completed wonders - struct wonder_district_info * area_info = get_wonder_district_info (area_tile); - if ((area_info != NULL) && (area_info->state == WDS_COMPLETED)) - continue; - - // Check if another hub of the same civ is closer to this tile - int my_distance = compute_wrapped_manhattan_distance (rec->tile_x, rec->tile_y, tx, ty); - bool tile_belongs_to_me = true; + int visit_mark = 1; - FOR_TABLE_ENTRIES (other_tei, &is->distribution_hub_records) { - struct distribution_hub_record * other_rec = (struct distribution_hub_record *)other_tei.value; - if ((other_rec == NULL) || (other_rec == rec)) + for (int length = 1; length <= max_len; length++) { + for (int ti = 0; ti < map->TileCount; ti++) { + int start_x = -1; + int start_y = -1; + tile_index_to_coords (map, ti, &start_x, &start_y); + if (! tile_point_in_block (start_x, start_y, block_x0, block_y0, block_x1, block_y1)) continue; - if (other_rec->civ_id != rec->civ_id) + if (! tile_is_land (-1, start_x, start_y, false)) continue; - - int other_distance = compute_wrapped_manhattan_distance (other_rec->tile_x, other_rec->tile_y, tx, ty); - if (other_distance < my_distance) { - tile_belongs_to_me = false; - break; - } - if (other_distance == my_distance) { - // Tie-breaking: prefer hub with lower Y, then lower X - if (other_rec->tile_y < rec->tile_y) { - tile_belongs_to_me = false; - break; - } - if ((other_rec->tile_y == rec->tile_y) && (other_rec->tile_x < rec->tile_x)) { - tile_belongs_to_me = false; - break; - } - } - } - - if (! tile_belongs_to_me) - continue; - - food_sum += City_calc_tile_yield_at (anchor_city, __, 0, tx, ty); - shield_sum += City_calc_tile_yield_at (anchor_city, __, 1, tx, ty); - } - - rec->raw_food_yield = food_sum; - rec->raw_shield_yield = shield_sum; - - int food_div = is->current_config.distribution_hub_food_yield_divisor; - int shield_div = is->current_config.distribution_hub_shield_yield_divisor; - if (food_div <= 0) - food_div = 1; - if (shield_div <= 0) - shield_div = 1; - - int connected_city_count = 0; - if (anchor_city != NULL) { - FOR_CITIES_OF (coi, rec->civ_id) { - City * other_city = coi.city; - if ((other_city != NULL) && distribution_hub_accessible_to_city (rec, other_city)) - connected_city_count++; - } - } - if (connected_city_count <= 0) - connected_city_count = 1; - - if (is->current_config.distribution_hub_yield_division_mode == DHYDM_SCALE_BY_CITY_COUNT) { - int city_root = 1; - while ((city_root + 1) * (city_root + 1) <= connected_city_count) - city_root++; - int city_food_divisor = city_root * food_div; - int city_shield_divisor = city_root * shield_div; - if (city_food_divisor < 1) city_food_divisor = 1; - if (city_shield_divisor < 1) city_shield_divisor = 1; - rec->food_yield = food_sum / city_food_divisor; - rec->shield_yield = shield_sum / city_shield_divisor; - } else { - rec->food_yield = food_sum / food_div; - rec->shield_yield = shield_sum / shield_div; - } -} - -void -remove_distribution_hub_record (Tile * tile) -{ - struct distribution_hub_record * rec = get_distribution_hub_record (tile); - if (rec == NULL) - return; - - int affected_civ_id = rec->civ_id; - release_distribution_hub_coverage (rec); - itable_remove (&is->distribution_hub_records, (int)tile); - free (rec); - is->distribution_hub_totals_dirty = true; - recompute_distribution_hub_totals (); - - // Recalculate yields for all cities of this civ - if ((affected_civ_id >= 0) && (p_cities->Cities != NULL)) { - for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { - City * target_city = get_city_ptr (city_index); - if ((target_city != NULL) && (target_city->Body.CivID == affected_civ_id)) - recompute_city_yields_with_districts (target_city); - } - } -} - -void -recompute_distribution_hub_totals () -{ - if (! is->current_config.enable_districts || - ! is->current_config.enable_distribution_hub_districts) { - is->distribution_hub_totals_dirty = false; - return; - } - - struct table new_coverage_counts = {0}; - struct table newly_covered_tiles = {0}; - - clear_memo (); - int civs_needing_recalc[32] = {0}; - - FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { - Tile * tile = (Tile *)tei.key; - struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; - if (rec == NULL) - continue; - - Tile * current_tile = tile_at (rec->tile_x, rec->tile_y); - if ((current_tile == NULL) || - (current_tile == p_null_tile) || - ! district_is_complete (current_tile, DISTRIBUTION_HUB_DISTRICT_ID)) { - memoize (tei.key); - continue; - } - - rec->tile = current_tile; - rec->food_yield = 0; - rec->shield_yield = 0; - rec->raw_food_yield = 0; - rec->raw_shield_yield = 0; - - int old_civ_id = rec->civ_id; - rec->civ_id = current_tile->vtable->m38_Get_Territory_OwnerID (current_tile); - - if (old_civ_id != rec->civ_id) - civs_needing_recalc[old_civ_id] = 1; - civs_needing_recalc[rec->civ_id] = 1; - - City * anchor = get_connected_city_for_distribution_hub (rec); - - if ((anchor == NULL) || - current_tile->vtable->m20_Check_Pollution (current_tile, __, 0) || - tile_has_enemy_unit (current_tile, rec->civ_id)) - continue; - - FOR_TILES_AROUND (tai, workable_tile_counts[1], rec->tile_x, rec->tile_y) { - Tile * area_tile = tai.tile; - if (area_tile == p_null_tile) + if (tile_part_of_existing_candidate (start_x, start_y)) continue; - - int tx, ty; - tai_get_coords (&tai, &tx, &ty); - - if (area_tile->vtable->m38_Get_Territory_OwnerID (area_tile) != rec->civ_id) + if (tile_has_bridge_or_canal_nearby (start_x, start_y)) continue; - if (Tile_has_city (area_tile)) + if (tile_is_reserved_in_district_tile_map (start_x, start_y)) continue; - if (tile_has_enemy_unit (area_tile, rec->civ_id)) + Tile * start_tile = tile_at (start_x, start_y); + if ((start_tile == NULL) || (start_tile == p_null_tile)) continue; - if (area_tile->vtable->m20_Check_Pollution (area_tile, __, 0)) + if (tile_has_resource (start_tile)) continue; - - struct district_instance * area_district = get_district_instance (area_tile); - if ((area_district != NULL) && ((tx != rec->tile_x) || (ty != rec->tile_y))) + if (! district_is_buildable_on_square_type (&is->district_configs[CANAL_DISTRICT_ID], start_tile)) continue; - - struct wonder_district_info * area_info = get_wonder_district_info (area_tile); - if ((area_info != NULL) && (area_info->state == WDS_COMPLETED)) + int continent_id = start_tile->vtable->m46_Get_ContinentID (start_tile); + if (continent_id < 0) continue; + short owner = start_tile->vtable->m38_Get_Territory_OwnerID (start_tile); - int key = (int)area_tile; - int prev_cover_pass = itable_look_up_or (&new_coverage_counts, key, 0); - int prev_cover_old = itable_look_up_or (&is->distribution_hub_coverage_counts, key, 0); - itable_insert (&new_coverage_counts, key, prev_cover_pass + 1); - if ((prev_cover_pass == 0) && (prev_cover_old <= 0)) - itable_insert (&newly_covered_tiles, key, 1); - } - } - - for (int i = 0; i < is->memo_len; i++) - remove_distribution_hub_record ((Tile *)is->memo[i]); - clear_memo (); - - FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { - struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; - if (rec == NULL) - continue; - - City * anchor = get_connected_city_for_distribution_hub (rec); - if (anchor == NULL) { - rec->food_yield = 0; - rec->shield_yield = 0; - rec->raw_food_yield = 0; - rec->raw_shield_yield = 0; - continue; - } - - recompute_distribution_hub_yields (rec); - } + int stack_len = 1; + int dir_stack[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + int path_dir[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; + out_x[0] = (short)start_x; + out_y[0] = (short)start_y; + dir_stack[0] = -1; + path_dir[0] = -1; - table_deinit (&is->distribution_hub_coverage_counts); - is->distribution_hub_coverage_counts = new_coverage_counts; - memset (&new_coverage_counts, 0, sizeof new_coverage_counts); + while (stack_len > 0) { + int depth = stack_len - 1; + int cx = out_x[depth]; + int cy = out_y[depth]; - FOR_TABLE_ENTRIES (tei, &newly_covered_tiles) { - Tile * covered_tile = (Tile *)tei.key; - if ((covered_tile == NULL) || (covered_tile == p_null_tile)) - continue; - int tx, ty; - if (! tile_coords_from_ptr (&p_bic_data->Map, covered_tile, &tx, &ty)) - continue; - set_tile_unworkable_for_all_cities (covered_tile, tx, ty); - covered_tile->Body.CityAreaID = -1; - } - table_deinit (&newly_covered_tiles); - - // Recalculate yields for cities of civs whose distribution hub ownership changed - for (int civ_id = 0; civ_id < 32; civ_id++) { - if (civs_needing_recalc[civ_id] && (p_cities->Cities != NULL)) { - for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { - City * city = get_city_ptr (city_index); - if ((city != NULL) && (city->Body.CivID == civ_id)) - recompute_city_yields_with_districts (city); - } - } - } - - is->distribution_hub_totals_dirty = false; -} + if (depth + 1 == length) { + bool endpoints_ok = false; + if (length == 1) { + int ex = out_x[0]; + int ey = out_y[0]; + bool pair_ok = false; + if (tile_is_water (ex, ey - 2) && tile_is_water (ex, ey + 2)) pair_ok = true; + if (tile_is_water (ex - 2, ey) && tile_is_water (ex + 2, ey)) pair_ok = true; + if (tile_is_water (ex - 1, ey - 1) && tile_is_water (ex + 1, ey + 1)) pair_ok = true; + if (tile_is_water (ex - 1, ey + 1) && tile_is_water (ex + 1, ey - 1)) pair_ok = true; + if (pair_ok && tile_has_diagonal_water (ex, ey)) + endpoints_ok = true; + } else { + int first_dir = path_dir[1]; + int last_dir = path_dir[length - 1]; + int sx = out_x[0]; + int sy = out_y[0]; + int ex = out_x[length - 1]; + int ey = out_y[length - 1]; + bool start_water = tile_is_water (sx - dir_dx[first_dir], sy - dir_dy[first_dir]); + bool end_water = tile_is_water (ex + dir_dx[last_dir], ey + dir_dy[last_dir]); + if (start_water && end_water && + tile_has_diagonal_water (sx, sy) && + tile_has_diagonal_water (ex, ey)) + endpoints_ok = true; + } -void -on_distribution_hub_completed (Tile * tile, int tile_x, int tile_y) -{ - if (! is->current_config.enable_districts || ! is->current_config.enable_distribution_hub_districts) - return; + bool buildable = true; + if (endpoints_ok) { + for (int pi = 0; pi < length; pi++) { + Tile * path_tile = tile_at (out_x[pi], out_y[pi]); + if ((path_tile == NULL) || (path_tile == p_null_tile) || + tile_has_resource (path_tile) || + (! district_is_buildable_on_square_type (&is->district_configs[CANAL_DISTRICT_ID], path_tile)) || + tile_is_reserved_in_district_tile_map (out_x[pi], out_y[pi])) { + buildable = false; + break; + } + if (tile_has_bridge_or_canal_nearby (out_x[pi], out_y[pi])) { + buildable = false; + break; + } + } + } else { + buildable = false; + } - int tile_owner = -1; - if ((tile != NULL) && (tile != p_null_tile)) - tile_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); + if (buildable) { + // Collect adjacent land tiles for component checks + int adj_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES * 8]; + int adj_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES * 8]; + int adj_count = 0; + for (int pi = 0; pi < length; pi++) { + int px = out_x[pi]; + int py = out_y[pi]; + for (int di = 0; di < 8; di++) { + int nx = px + dir_dx[di]; + int ny = py + dir_dy[di]; + wrap_tile_coords (map, &nx, &ny); + if (! tile_is_land (-1, nx, ny, false)) + continue; + Tile * adj = tile_at (nx, ny); + if ((adj == NULL) || (adj == p_null_tile)) + continue; + if (adj->vtable->m46_Get_ContinentID (adj) != continent_id) + continue; + bool in_path = false; + for (int pj = 0; pj < length; pj++) { + if ((out_x[pj] == nx) && (out_y[pj] == ny)) { + in_path = true; + break; + } + } + if (in_path) + continue; + bool seen = false; + for (int aj = 0; aj < adj_count; aj++) { + if ((adj_x[aj] == nx) && (adj_y[aj] == ny)) { + seen = true; + break; + } + } + if (! seen && (adj_count < (int)(sizeof (adj_x) / sizeof (adj_x[0])))) { + adj_x[adj_count] = nx; + adj_y[adj_count] = ny; + adj_count++; + } + } + } - struct distribution_hub_record * rec = get_distribution_hub_record (tile); - if (rec != NULL) { - int old_civ_id = rec->civ_id; - rec->tile = tile; - rec->tile_x = tile_x; - rec->tile_y = tile_y; + if (adj_count >= 2) { + int best1 = 0; + int best2 = 0; + int min_land = is->current_config.ai_canal_eval_min_bisected_land_tiles; + if (min_land < 1) + min_land = 1; + visit_mark++; + if (visit_mark == 0) { + visit_mark = 1; + for (int i = 0; i < tile_count; i++) + visit[i] = 0; + } - release_distribution_hub_coverage (rec); - rec->civ_id = tile_owner; - is->distribution_hub_totals_dirty = true; - recompute_distribution_hub_totals (); + for (int ai = 0; ai < adj_count; ai++) { + int aidx = tile_coords_to_index (map, adj_x[ai], adj_y[ai]); + if ((aidx < 0) || (visit[aidx] == visit_mark)) + continue; - if (old_civ_id != tile_owner) { - // Recompute for old civ - for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { - City * target_city = get_city_ptr (city_index); - if ((target_city != NULL) && (target_city->Body.CivID == old_civ_id)) - recompute_city_yields_with_districts (target_city); - } - } - } + int comp_size = 0; + int head = 0; + int tail = 0; + visit[aidx] = visit_mark; + queue_x[tail] = adj_x[ai]; + queue_y[tail] = adj_y[ai]; + tail++; - rec = malloc (sizeof *rec); - if (rec == NULL) - return; - rec->tile = tile; - rec->tile_x = tile_x; - rec->tile_y = tile_y; - rec->civ_id = tile_owner; - rec->food_yield = 0; - rec->shield_yield = 0; - rec->raw_food_yield = 0; - rec->raw_shield_yield = 0; - itable_insert (&is->distribution_hub_records, (int)tile, (int)rec); - adjust_distribution_hub_coverage (rec); + while (head < tail) { + int qx = queue_x[head]; + int qy = queue_y[head]; + head++; + comp_size++; + for (int di = 0; di < 8; di++) { + int nx = qx + dir_dx[di]; + int ny = qy + dir_dy[di]; + wrap_tile_coords (map, &nx, &ny); + if (! tile_is_land (-1, nx, ny, false)) + continue; + Tile * adj = tile_at (nx, ny); + if ((adj == NULL) || (adj == p_null_tile)) + continue; + if (adj->vtable->m46_Get_ContinentID (adj) != continent_id) + continue; + bool blocked = false; + for (int pj = 0; pj < length; pj++) { + if ((out_x[pj] == nx) && (out_y[pj] == ny)) { + blocked = true; + break; + } + } + if (blocked) + continue; + int nidx = tile_coords_to_index (map, nx, ny); + if ((nidx < 0) || (visit[nidx] == visit_mark)) + continue; + visit[nidx] = visit_mark; + queue_x[tail] = nx; + queue_y[tail] = ny; + tail++; + } + } - is->distribution_hub_totals_dirty = true; - recompute_distribution_hub_totals (); + if (comp_size > best1) { + best2 = best1; + best1 = comp_size; + } else if (comp_size > best2) { + best2 = comp_size; + } + } - // Recalculate yields for all cities of this civ - int affected_civ_id = rec->civ_id; - if ((affected_civ_id >= 0) && (p_cities->Cities != NULL)) { - for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { - City * target_city = get_city_ptr (city_index); - if ((target_city != NULL) && (target_city->Body.CivID == affected_civ_id)) - recompute_city_yields_with_districts (target_city); - } - } -} + if ((best1 >= min_land) && (best2 >= min_land)) { + *out_owner = owner; + free (visit); + free (queue_x); + free (queue_y); + return length; + } + } + } + dir_stack[depth] = -1; + stack_len--; + continue; + } -void -refresh_distribution_hubs_for_city (City * city) -{ - if (! is->current_config.enable_districts || - ! is->current_config.enable_distribution_hub_districts || - (city == NULL)) - return; + int next_dir = dir_stack[depth] + 1; + bool advanced = false; + while (next_dir < 8) { + int ndx = dir_dx[next_dir]; + int ndy = dir_dy[next_dir]; + int nx = cx + ndx; + int ny = cy + ndy; + wrap_tile_coords (map, &nx, &ny); + dir_stack[depth] = next_dir; - FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { - int tx = wai.tile_x, ty = wai.tile_y; - Tile * tile = wai.tile; - struct district_instance * inst = wai.district_inst; - if (inst->district_id != DISTRIBUTION_HUB_DISTRICT_ID) - continue; - on_distribution_hub_completed (tile, tx, ty); - } -} - -bool -is_space_char (char c) -{ - switch (c) { - case ' ': - case '\t': - case '\n': - case '\r': - case '\f': - case '\v': - return true; - default: - return false; - } -} - -enum key_value_parse_status { - KVP_SUCCESS, - KVP_NO_EQUALS, - KVP_EMPTY_KEY -}; - -enum key_value_parse_status -parse_trimmed_key_value (struct string_slice const * trimmed, - struct string_slice * out_key, - struct string_slice * out_value) -{ - if ((trimmed == NULL) || (trimmed->len <= 0)) - return KVP_NO_EQUALS; - - char * equals = NULL; - for (int i = 0; i < trimmed->len; i++) { - if (trimmed->str[i] == '=') { - equals = trimmed->str + i; - break; - } - } - if (equals == NULL) - return KVP_NO_EQUALS; - - struct string_slice key = { .str = trimmed->str, .len = (int)(equals - trimmed->str) }; - key = trim_string_slice (&key, 0); - if (key.len == 0) - return KVP_EMPTY_KEY; - - struct string_slice value = { .str = equals + 1, .len = (int)((trimmed->str + trimmed->len) - (equals + 1)) }; - *out_key = key; - *out_value = trim_string_slice (&value, 0); - return KVP_SUCCESS; -} - -void -add_key_parse_error (struct error_line ** parse_errors, - int line_number, - struct string_slice const * key, - struct string_slice const * value, - char const * message_suffix) -{ - struct error_line * err = add_error_line (parse_errors); - char * key_str = extract_slice (key); - char * value_str = (value != NULL) ? extract_slice (value) : NULL; - if (value_str != NULL) - snprintf (err->text, sizeof err->text, "^ Line %d: %s \"%s\" %s", line_number, key_str, value_str, message_suffix); - else - snprintf (err->text, sizeof err->text, "^ Line %d: %s %s", line_number, key_str, message_suffix); - err->text[(sizeof err->text) - 1] = '\0'; - if (value_str != NULL) - free (value_str); - free (key_str); -} - -void -add_unrecognized_key_error (struct error_line ** unrecognized_keys, - int line_number, - struct string_slice const * key) -{ - struct error_line * err_line = add_error_line (unrecognized_keys); - char * key_str = extract_slice (key); - snprintf (err_line->text, sizeof err_line->text, "^ Line %d: %s", line_number, key_str); - err_line->text[(sizeof err_line->text) - 1] = '\0'; - free (key_str); -} - -char * -copy_trimmed_string_or_null (struct string_slice const * slice, int remove_quotes) -{ - struct string_slice trimmed = trim_string_slice (slice, remove_quotes); - if (trimmed.len == 0) - return NULL; - return extract_slice (&trimmed); -} - -void -free_bonus_entry_list (struct district_bonus_list * list) -{ - if (list == NULL) - return; - - for (int i = 0; i < list->count; i++) { - if (list->entries[i].type == DBET_BUILDING && - list->entries[i].building_name != NULL) { - free ((void *)list->entries[i].building_name); - list->entries[i].building_name = NULL; - } - } - list->count = 0; -} - -void -free_bonus_entry_list_override (struct district_bonus_list * list, - struct district_bonus_list const * defaults) -{ - if (list == NULL) - return; - - for (int i = 0; i < list->count; i++) { - if (list->entries[i].type == DBET_BUILDING && - list->entries[i].building_name != NULL) { - char const * default_name = NULL; - if ((defaults != NULL) && (i < defaults->count)) - default_name = defaults->entries[i].building_name; - if (list->entries[i].building_name != default_name) - free ((void *)list->entries[i].building_name); - list->entries[i].building_name = NULL; - } - } - list->count = (defaults != NULL) ? defaults->count : 0; -} - -void -move_bonus_entry_list (struct district_bonus_list * dest, - struct district_bonus_list * src) -{ - if ((dest == NULL) || (src == NULL)) - return; - - dest->count = src->count; - for (int i = 0; i < src->count; i++) { - dest->entries[i] = src->entries[i]; - src->entries[i].building_name = NULL; - } - src->count = 0; -} - -void -free_dynamic_district_config (struct district_config * cfg) -{ - if (cfg == NULL) - return; - - if (! cfg->is_dynamic) - return; - - char const * name_ptr = cfg->name; - if (cfg->name != NULL) { - free ((void *)cfg->name); - cfg->name = NULL; - } - if ((cfg->display_name != NULL) && - (cfg->display_name != name_ptr)) { - free ((void *)cfg->display_name); - cfg->display_name = NULL; - } - if (cfg->tooltip != NULL) { - free ((void *)cfg->tooltip); - cfg->tooltip = NULL; - } - for (int i = 0; i < ARRAY_LEN (cfg->advance_prereqs); i++) { - if (cfg->advance_prereqs[i] != NULL) { - free ((void *)cfg->advance_prereqs[i]); - cfg->advance_prereqs[i] = NULL; - } - } - cfg->advance_prereq_count = 0; - if (cfg->obsoleted_by != NULL) { - free ((void *)cfg->obsoleted_by); - cfg->obsoleted_by = NULL; - } - - for (int i = 0; i < 5; i++) { - if (cfg->resource_prereqs[i] != NULL) { - free ((void *)cfg->resource_prereqs[i]); - cfg->resource_prereqs[i] = NULL; - } - } - - if (cfg->resource_prereq_on_tile != NULL) { - free ((void *)cfg->resource_prereq_on_tile); - cfg->resource_prereq_on_tile = NULL; - } - - for (int i = 0; i < ARRAY_LEN (cfg->wonder_prereqs); i++) { - if (cfg->wonder_prereqs[i] != NULL) { - free ((void *)cfg->wonder_prereqs[i]); - cfg->wonder_prereqs[i] = NULL; - } - } - cfg->wonder_prereq_count = 0; - - for (int i = 0; i < ARRAY_LEN (cfg->natural_wonder_prereqs); i++) { - if (cfg->natural_wonder_prereqs[i] != NULL) { - free ((void *)cfg->natural_wonder_prereqs[i]); - cfg->natural_wonder_prereqs[i] = NULL; - } - } - cfg->natural_wonder_prereq_count = 0; - - for (int i = 0; i < ARRAY_LEN (cfg->buildable_on_districts); i++) { - if (cfg->buildable_on_districts[i] != NULL) { - free ((void *)cfg->buildable_on_districts[i]); - cfg->buildable_on_districts[i] = NULL; - } - } - cfg->buildable_on_district_count = 0; - cfg->buildable_on_district_id_count = 0; - cfg->has_buildable_on_districts = false; - - for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { - if (cfg->buildable_by_civs[i] != NULL) { - free ((void *)cfg->buildable_by_civs[i]); - cfg->buildable_by_civs[i] = NULL; - } - } - cfg->buildable_by_civ_count = 0; - cfg->has_buildable_by_civs = false; - for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_traits_ids); i++) - cfg->buildable_by_civ_traits_ids[i] = -1; - cfg->buildable_by_civ_traits_id_count = 0; - cfg->has_buildable_by_civ_traits = false; - for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_govs_ids); i++) - cfg->buildable_by_civ_govs_ids[i] = -1; - cfg->buildable_by_civ_govs_id_count = 0; - cfg->has_buildable_by_civ_govs = false; - for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_cultures_ids); i++) - cfg->buildable_by_civ_cultures_ids[i] = -1; - cfg->buildable_by_civ_cultures_id_count = 0; - cfg->has_buildable_by_civ_cultures = false; - - for (int i = 0; i < 5; i++) { - if (cfg->dependent_improvements[i] != NULL) { - free ((void *)cfg->dependent_improvements[i]); - cfg->dependent_improvements[i] = NULL; - } - } - - for (int i = 0; i < 10; i++) { - if (cfg->img_paths[i] != NULL) { - free ((void *)cfg->img_paths[i]); - cfg->img_paths[i] = NULL; - } - } - - free_bonus_entry_list (&cfg->culture_bonus_extras); - free_bonus_entry_list (&cfg->science_bonus_extras); - free_bonus_entry_list (&cfg->food_bonus_extras); - free_bonus_entry_list (&cfg->gold_bonus_extras); - free_bonus_entry_list (&cfg->shield_bonus_extras); - free_bonus_entry_list (&cfg->happiness_bonus_extras); - free_bonus_entry_list (&cfg->defense_bonus_extras); - - memset (cfg, 0, sizeof *cfg); -} - -void -free_dynamic_wonder_config (struct wonder_district_config * cfg) -{ - if (cfg == NULL) - return; - - if (! cfg->is_dynamic) - return; - - if (cfg->wonder_name != NULL) { - free ((void *)cfg->wonder_name); - cfg->wonder_name = NULL; - } - - if (cfg->img_path != NULL) { - free ((void *)cfg->img_path); - cfg->img_path = NULL; - } - - memset (cfg, 0, sizeof *cfg); -} - -void -free_dynamic_natural_wonder_config (struct natural_wonder_district_config * cfg) -{ - if (cfg == NULL) - return; - - if (! cfg->is_dynamic) - return; - - if (cfg->name != NULL) { - free ((void *)cfg->name); - cfg->name = NULL; - } - - if (cfg->img_path != NULL) { - free ((void *)cfg->img_path); - cfg->img_path = NULL; - } - - memset (cfg, 0, sizeof *cfg); - cfg->adjacent_to = (enum SquareTypes)SQ_INVALID; - cfg->adjacency_dir = DIR_ZERO; -} - -enum Unit_Command_Values -allocate_dynamic_district_command (char const * name) -{ - int offset = is->next_custom_dynamic_command_index; - is->next_custom_dynamic_command_index += 1; - int value = C3X_DISTRICT_COMMAND_BASE - (offset + 1); - return (enum Unit_Command_Values)value; -} - -void -free_special_district_override_strings (struct district_config * cfg, struct district_config const * defaults) -{ - if (cfg == NULL || defaults == NULL) - return; - - if ((cfg->display_name != NULL) && - (cfg->display_name != cfg->name) && - (cfg->display_name != defaults->display_name)) { - free ((void *)cfg->display_name); - cfg->display_name = NULL; - } - if ((cfg->tooltip != NULL) && (cfg->tooltip != defaults->tooltip)) { - free ((void *)cfg->tooltip); - cfg->tooltip = NULL; - } - for (int i = 0; i < ARRAY_LEN (cfg->advance_prereqs); i++) { - char const * default_value = (defaults != NULL && i < defaults->advance_prereq_count) - ? defaults->advance_prereqs[i] - : NULL; - if ((cfg->advance_prereqs[i] != NULL) && - (cfg->advance_prereqs[i] != default_value)) { - free ((void *)cfg->advance_prereqs[i]); - } - cfg->advance_prereqs[i] = NULL; - } - cfg->advance_prereq_count = 0; - if ((cfg->obsoleted_by != NULL) && (cfg->obsoleted_by != defaults->obsoleted_by)) { - free ((void *)cfg->obsoleted_by); - cfg->obsoleted_by = NULL; - } - - for (int i = 0; i < ARRAY_LEN (cfg->resource_prereqs); i++) { - char const * default_value = (i < defaults->resource_prereq_count) ? defaults->resource_prereqs[i] : NULL; - if ((cfg->resource_prereqs[i] != NULL) && - (cfg->resource_prereqs[i] != default_value)) - free ((void *)cfg->resource_prereqs[i]); - cfg->resource_prereqs[i] = NULL; - } - cfg->resource_prereq_count = defaults->resource_prereq_count; - - if ((cfg->resource_prereq_on_tile != NULL) && (cfg->resource_prereq_on_tile != defaults->resource_prereq_on_tile)) { - free ((void *)cfg->resource_prereq_on_tile); - cfg->resource_prereq_on_tile = NULL; - } - - for (int i = 0; i < ARRAY_LEN (cfg->wonder_prereqs); i++) { - char const * default_value = (i < defaults->wonder_prereq_count) ? defaults->wonder_prereqs[i] : NULL; - if ((cfg->wonder_prereqs[i] != NULL) && - (cfg->wonder_prereqs[i] != default_value)) - free ((void *)cfg->wonder_prereqs[i]); - cfg->wonder_prereqs[i] = NULL; - } - cfg->wonder_prereq_count = defaults->wonder_prereq_count; - - for (int i = 0; i < ARRAY_LEN (cfg->natural_wonder_prereqs); i++) { - char const * default_value = (i < defaults->natural_wonder_prereq_count) ? defaults->natural_wonder_prereqs[i] : NULL; - if ((cfg->natural_wonder_prereqs[i] != NULL) && - (cfg->natural_wonder_prereqs[i] != default_value)) - free ((void *)cfg->natural_wonder_prereqs[i]); - cfg->natural_wonder_prereqs[i] = NULL; - } - cfg->natural_wonder_prereq_count = defaults->natural_wonder_prereq_count; - - for (int i = 0; i < ARRAY_LEN (cfg->buildable_on_districts); i++) { - char const * default_value = (i < defaults->buildable_on_district_count) ? defaults->buildable_on_districts[i] : NULL; - if ((cfg->buildable_on_districts[i] != NULL) && - (cfg->buildable_on_districts[i] != default_value)) { - free ((void *)cfg->buildable_on_districts[i]); - } - cfg->buildable_on_districts[i] = NULL; - } - cfg->buildable_on_district_count = defaults->buildable_on_district_count; - cfg->buildable_on_district_id_count = defaults->buildable_on_district_id_count; - cfg->has_buildable_on_districts = defaults->has_buildable_on_districts; - - for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { - char const * default_value = (i < defaults->buildable_by_civ_count) ? defaults->buildable_by_civs[i] : NULL; - if ((cfg->buildable_by_civs[i] != NULL) && - (cfg->buildable_by_civs[i] != default_value)) { - free ((void *)cfg->buildable_by_civs[i]); - } - cfg->buildable_by_civs[i] = NULL; - } - cfg->buildable_by_civ_count = defaults->buildable_by_civ_count; - cfg->has_buildable_by_civs = defaults->has_buildable_by_civs; - for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_traits_ids); i++) - cfg->buildable_by_civ_traits_ids[i] = -1; - cfg->buildable_by_civ_traits_id_count = defaults->buildable_by_civ_traits_id_count; - cfg->has_buildable_by_civ_traits = defaults->has_buildable_by_civ_traits; - for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_govs_ids); i++) - cfg->buildable_by_civ_govs_ids[i] = -1; - cfg->buildable_by_civ_govs_id_count = defaults->buildable_by_civ_govs_id_count; - cfg->has_buildable_by_civ_govs = defaults->has_buildable_by_civ_govs; - for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_cultures_ids); i++) - cfg->buildable_by_civ_cultures_ids[i] = -1; - cfg->buildable_by_civ_cultures_id_count = defaults->buildable_by_civ_cultures_id_count; - cfg->has_buildable_by_civ_cultures = defaults->has_buildable_by_civ_cultures; - cfg->buildable_by_war_allies = defaults->buildable_by_war_allies; - cfg->buildable_by_pact_allies = defaults->buildable_by_pact_allies; - - for (int i = 0; i < ARRAY_LEN (cfg->dependent_improvements); i++) { - char const * default_value = (i < defaults->dependent_improvement_count) ? defaults->dependent_improvements[i] : NULL; - if ((cfg->dependent_improvements[i] != NULL) && - (cfg->dependent_improvements[i] != default_value)) { - free ((void *)cfg->dependent_improvements[i]); - } - cfg->dependent_improvements[i] = NULL; - } - cfg->dependent_improvement_count = defaults->dependent_improvement_count; - - for (int i = 0; i < ARRAY_LEN (cfg->img_paths); i++) { - char const * default_value = (i < defaults->img_path_count) ? defaults->img_paths[i] : NULL; - if ((cfg->img_paths[i] != NULL) && - (cfg->img_paths[i] != default_value)) { - free ((void *)cfg->img_paths[i]); - } - cfg->img_paths[i] = NULL; - } - cfg->img_path_count = defaults->img_path_count; - - free_bonus_entry_list_override (&cfg->culture_bonus_extras, &defaults->culture_bonus_extras); - free_bonus_entry_list_override (&cfg->science_bonus_extras, &defaults->science_bonus_extras); - free_bonus_entry_list_override (&cfg->food_bonus_extras, &defaults->food_bonus_extras); - free_bonus_entry_list_override (&cfg->gold_bonus_extras, &defaults->gold_bonus_extras); - free_bonus_entry_list_override (&cfg->shield_bonus_extras, &defaults->shield_bonus_extras); - free_bonus_entry_list_override (&cfg->happiness_bonus_extras, &defaults->happiness_bonus_extras); - free_bonus_entry_list_override (&cfg->defense_bonus_extras, &defaults->defense_bonus_extras); -} + if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) { + next_dir++; + continue; + } + if (! tile_is_land (-1, nx, ny, false)) { + next_dir++; + continue; + } + if (tile_part_of_existing_candidate (nx, ny)) { + next_dir++; + continue; + } + if (tile_is_reserved_in_district_tile_map (nx, ny)) { + next_dir++; + continue; + } + Tile * next_tile = tile_at (nx, ny); + if ((next_tile == NULL) || (next_tile == p_null_tile)) { + next_dir++; + continue; + } + if (tile_has_resource (next_tile)) { + next_dir++; + continue; + } + if (! district_is_buildable_on_square_type (&is->district_configs[CANAL_DISTRICT_ID], next_tile)) { + next_dir++; + continue; + } + if (tile_has_bridge_or_canal_nearby (nx, ny)) { + next_dir++; + continue; + } + if (next_tile->vtable->m46_Get_ContinentID (next_tile) != continent_id) { + next_dir++; + continue; + } + bool dup = false; + for (int pi = 0; pi < depth + 1; pi++) { + if ((out_x[pi] == nx) && (out_y[pi] == ny)) { + dup = true; + break; + } + } + if (dup) { + next_dir++; + continue; + } -void -reset_regular_district_configs (void) -{ - for (int i = USED_SPECIAL_DISTRICT_TYPES; i < COUNT_DISTRICT_TYPES; i++) { - if (is->district_configs[i].is_dynamic) - free_dynamic_district_config (&is->district_configs[i]); - } + if (depth >= 1) { + int prev_dir = dir_stack[depth - 1]; + int diff = next_dir - prev_dir; + if (diff < 0) + diff += 8; + if (diff > 4) + diff = 8 - diff; + if (diff > 1) { + next_dir++; + continue; + } + } - for (int i = 0; i < USED_SPECIAL_DISTRICT_TYPES; i++) - free_special_district_override_strings (&is->district_configs[i], &special_district_defaults[i]); + out_x[depth + 1] = (short)nx; + out_y[depth + 1] = (short)ny; + dir_stack[depth + 1] = -1; + path_dir[depth + 1] = next_dir; + stack_len++; + advanced = true; + break; + } - memset (is->district_configs, 0, sizeof is->district_configs); - for (int i = 0; i < USED_SPECIAL_DISTRICT_TYPES; i++) - is->district_configs[i] = special_district_defaults[i]; + if (! advanced) { + dir_stack[depth] = -1; + stack_len--; + } + } + } + } - is->special_district_count = USED_SPECIAL_DISTRICT_TYPES; - is->dynamic_district_count = 0; - is->district_count = is->special_district_count; - is->next_custom_dynamic_command_index = 0; + free (visit); + free (queue_x); + free (queue_y); + return 0; } void -reset_wonder_district_configs (void) +generate_ai_bridge_candidates_by_block (Map * map, int block_size, int contiguous_limit) { - for (int i = 0; i < MAX_WONDER_DISTRICT_TYPES; i++) { - if (is->wonder_district_configs[i].is_dynamic) - free_dynamic_wonder_config (&is->wonder_district_configs[i]); - } + if ((map == NULL) || (block_size <= 0)) + return; - memset (is->wonder_district_configs, 0, sizeof is->wonder_district_configs); - is->wonder_district_count = 0; -} + int width = map->Width; + int height = map->Height; + if ((width <= 0) || (height <= 0)) + return; -void -reset_natural_wonder_configs (void) -{ - for (int i = 0; i < MAX_NATURAL_WONDER_DISTRICT_TYPES; i++) { - if (is->natural_wonder_configs[i].is_dynamic) - free_dynamic_natural_wonder_config (&is->natural_wonder_configs[i]); - } + if (block_size < 1) + block_size = 1; - memset (is->natural_wonder_configs, 0, sizeof is->natural_wonder_configs); - for (int i = 0; i < MAX_NATURAL_WONDER_DISTRICT_TYPES; i++) { - is->natural_wonder_configs[i].adjacent_to = (enum SquareTypes)SQ_INVALID; - is->natural_wonder_configs[i].adjacency_dir = DIR_ZERO; + int candidate_capacity = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, contiguous_limit); + + short * candidate_x = (short *)malloc (sizeof *candidate_x * candidate_capacity); + short * candidate_y = (short *)malloc (sizeof *candidate_y * candidate_capacity); + if ((candidate_x == NULL) || (candidate_y == NULL)) { + if (candidate_x != NULL) + free (candidate_x); + if (candidate_y != NULL) + free (candidate_y); + return; } - for (int i = 0; i < MAX_NATURAL_WONDER_DISTRICT_TYPES; i++) - is->natural_wonder_img_sets[i].img.vtable = NULL; - stable_deinit (&is->natural_wonder_name_to_id); - is->natural_wonder_count = 0; -} -void -reset_ai_candidate_bridge_or_canals (void) -{ - if (is->ai_candidate_bridge_or_canals != NULL) { - for (int i = 0; i < is->ai_candidate_bridge_or_canals_capacity; i++) { - struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[i]; - if (entry->tile_x != NULL) - free (entry->tile_x); - if (entry->tile_y != NULL) - free (entry->tile_y); + for (int base_y = 0; base_y < height; base_y += block_size) { + int block_y1 = base_y + block_size; + if (block_y1 > height) + block_y1 = height; + for (int base_x = 0; base_x < width; base_x += block_size) { + int block_x1 = base_x + block_size; + if (block_x1 > width) + block_x1 = width; + short owner = -1; + int count = find_bridge_candidate_in_block ( + map, base_x, base_y, block_x1, block_y1, + contiguous_limit, candidate_capacity, + candidate_x, candidate_y, &owner); + if (count <= 0) + continue; + if (! add_ai_candidate_entry (BRIDGE_DISTRICT_ID, owner, candidate_x, candidate_y, count)) { + free (candidate_x); + free (candidate_y); + return; + } } - free (is->ai_candidate_bridge_or_canals); - is->ai_candidate_bridge_or_canals = NULL; } - is->ai_candidate_bridge_or_canals_capacity = 0; - is->ai_candidate_bridge_or_canals_count = 0; - is->ai_candidate_bridge_or_canals_initialized = false; -} - -void -clear_dynamic_district_definitions (void) -{ - reset_regular_district_configs (); - reset_wonder_district_configs (); - reset_natural_wonder_configs (); - reset_ai_candidate_bridge_or_canals (); -} -void -init_parsed_district_definition (struct parsed_district_definition * def) -{ - memset (def, 0, sizeof *def); - def->img_path_count = -1; - def->defense_bonus_percent = 0; - def->render_strategy = DRS_BY_COUNT; - def->ai_build_strategy = DABS_DISTRICT; - def->buildable_square_types_mask = district_default_buildable_mask (); + free (candidate_x); + free (candidate_y); } void -free_parsed_district_definition (struct parsed_district_definition * def) +generate_ai_canal_candidates_by_block (Map * map, int block_size, int contiguous_limit) { - if (def == NULL) + if ((map == NULL) || (block_size <= 0)) return; - if (def->display_name != NULL) { - free (def->display_name); - def->display_name = NULL; - } - if (def->name != NULL) { - free (def->name); - def->name = NULL; - } - if (def->tooltip != NULL) { - free (def->tooltip); - def->tooltip = NULL; - } - for (int i = 0; i < def->advance_prereq_count; i++) { - if (def->advance_prereqs[i] != NULL) { - free (def->advance_prereqs[i]); - def->advance_prereqs[i] = NULL; - } - } - def->advance_prereq_count = 0; - for (int i = 0; i < ARRAY_LEN (def->buildable_on_districts); i++) { - if (def->buildable_on_districts[i] != NULL) { - free (def->buildable_on_districts[i]); - def->buildable_on_districts[i] = NULL; - } - } - if (def->obsoleted_by != NULL) { - free (def->obsoleted_by); - def->obsoleted_by = NULL; - } - - for (int i = 0; i < def->resource_prereq_count; i++) { - if (def->resource_prereqs[i] != NULL) { - free (def->resource_prereqs[i]); - def->resource_prereqs[i] = NULL; - } - } - def->resource_prereq_count = 0; - - if (def->resource_prereq_on_tile != NULL) { - free (def->resource_prereq_on_tile); - def->resource_prereq_on_tile = NULL; - } - - for (int i = 0; i < def->wonder_prereq_count; i++) { - if (def->wonder_prereqs[i] != NULL) { - free (def->wonder_prereqs[i]); - def->wonder_prereqs[i] = NULL; - } - } - def->wonder_prereq_count = 0; - - for (int i = 0; i < def->natural_wonder_prereq_count; i++) { - if (def->natural_wonder_prereqs[i] != NULL) { - free (def->natural_wonder_prereqs[i]); - def->natural_wonder_prereqs[i] = NULL; - } - } - def->natural_wonder_prereq_count = 0; - - for (int i = 0; i < def->buildable_by_civ_count; i++) { - if (def->buildable_by_civs[i] != NULL) { - free (def->buildable_by_civs[i]); - def->buildable_by_civs[i] = NULL; - } - } - def->buildable_by_civ_count = 0; - for (int i = 0; i < def->buildable_by_civ_traits_count; i++) { - if (def->buildable_by_civ_traits[i] != NULL) { - free (def->buildable_by_civ_traits[i]); - def->buildable_by_civ_traits[i] = NULL; - } - } - def->buildable_by_civ_traits_count = 0; - def->buildable_by_civ_traits_id_count = 0; - for (int i = 0; i < def->buildable_by_civ_govs_count; i++) { - if (def->buildable_by_civ_govs[i] != NULL) { - free (def->buildable_by_civ_govs[i]); - def->buildable_by_civ_govs[i] = NULL; - } - } - def->buildable_by_civ_govs_count = 0; - def->buildable_by_civ_govs_id_count = 0; - for (int i = 0; i < def->buildable_by_civ_cultures_count; i++) { - if (def->buildable_by_civ_cultures[i] != NULL) { - free (def->buildable_by_civ_cultures[i]); - def->buildable_by_civ_cultures[i] = NULL; - } - } - def->buildable_by_civ_cultures_count = 0; - def->buildable_by_civ_cultures_id_count = 0; - - for (int i = 0; i < def->dependent_improvement_count; i++) { - if (def->dependent_improvements[i] != NULL) { - free (def->dependent_improvements[i]); - def->dependent_improvements[i] = NULL; - } - } - def->dependent_improvement_count = 0; + int width = map->Width; + int height = map->Height; + if ((width <= 0) || (height <= 0)) + return; - for (int i = 0; i < def->img_path_count; i++) { - if (def->img_paths[i] != NULL) { - free (def->img_paths[i]); - def->img_paths[i] = NULL; - } - } - def->img_path_count = 0; + if (block_size < 1) + block_size = 1; - if (def->generated_resource != NULL) { - free (def->generated_resource); - def->generated_resource = NULL; + int candidate_capacity = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, contiguous_limit); + + short * candidate_x = (short *)malloc (sizeof *candidate_x * candidate_capacity); + short * candidate_y = (short *)malloc (sizeof *candidate_y * candidate_capacity); + if ((candidate_x == NULL) || (candidate_y == NULL)) { + if (candidate_x != NULL) + free (candidate_x); + if (candidate_y != NULL) + free (candidate_y); + return; } - free_bonus_entry_list (&def->culture_bonus_extras); - free_bonus_entry_list (&def->science_bonus_extras); - free_bonus_entry_list (&def->food_bonus_extras); - free_bonus_entry_list (&def->gold_bonus_extras); - free_bonus_entry_list (&def->shield_bonus_extras); - free_bonus_entry_list (&def->happiness_bonus_extras); - free_bonus_entry_list (&def->defense_bonus_extras); + for (int base_y = 0; base_y < height; base_y += block_size) { + int block_y1 = base_y + block_size; + if (block_y1 > height) + block_y1 = height; + for (int base_x = 0; base_x < width; base_x += block_size) { + int block_x1 = base_x + block_size; + if (block_x1 > width) + block_x1 = width; + short owner = -1; + int count = find_canal_candidate_in_block ( + map, base_x, base_y, block_x1, block_y1, + contiguous_limit, candidate_capacity, + candidate_x, candidate_y, &owner); + if (count <= 0) + continue; + if (! add_ai_candidate_entry (CANAL_DISTRICT_ID, owner, candidate_x, candidate_y, count)) { + free (candidate_x); + free (candidate_y); + return; + } + } + } - init_parsed_district_definition (def); + free (candidate_x); + free (candidate_y); } -int -find_special_district_index_by_name (char const * name) +void +generate_ai_canal_and_bridge_targets () { - if (name == NULL) - return -1; + if (is->ai_candidate_bridge_or_canals_initialized) + return; + if ((! is->current_config.enable_canal_districts) && (! is->current_config.enable_bridge_districts)) + return; - for (int i = 0; i < is->special_district_count; i++) { - if ((is->district_configs[i].name != NULL) && - (strcmp (is->district_configs[i].name, name) == 0)) - return i; - } - return -1; -} + Map * map = &p_bic_data->Map; + int width = map->Width; + int height = map->Height; + if ((width <= 0) || (height <= 0)) + return; -bool -ensure_culture_variant_art (struct district_config * cfg, int section_start_line) -{ - if ((cfg == NULL) || (! cfg->vary_img_by_culture)) - return true; + int block_size = is->current_config.ai_bridge_canal_eval_block_size; + if (block_size <= 0) + block_size = 10; - const int required_variants = 5; - const int max_img_paths = ARRAY_LEN (is->district_configs[0].img_paths); - if (cfg->img_path_count <= 0) { - char ss[256]; - snprintf (ss, sizeof ss, "[C3X] load_dynamic_district_configs: district \"%s\" requires culture-specific art but none provided (line %d)\n", cfg->name, section_start_line); - (*p_OutputDebugStringA) (ss); - return false; + if (is->current_config.enable_canal_districts) { + generate_ai_canal_candidates_by_block ( + map, + block_size, + is->current_config.max_contiguous_canal_districts); } - while ((cfg->img_path_count < required_variants) && - (cfg->img_path_count < max_img_paths)) { - cfg->img_paths[cfg->img_path_count] = strdup (cfg->img_paths[0]); - cfg->img_path_count += 1; + if (is->current_config.enable_bridge_districts) { + generate_ai_bridge_candidates_by_block ( + map, + block_size, + is->current_config.max_contiguous_bridge_districts); } - return true; + is->ai_candidate_bridge_or_canals_initialized = true; } -bool -parse_config_string_list (char * value_text, - char ** dest, - int capacity, - int * out_count, - struct error_line ** parse_errors, - int line_number, - char const * key) +void +assign_workers_for_ai_candidate_bridge_or_canals (Leader * leader) { - for (int i = 0; i < capacity; i++) { - if (dest[i] != NULL) { - free (dest[i]); - dest[i] = NULL; - } - } - *out_count = 0; + if ((leader == NULL) || (is->ai_candidate_bridge_or_canals_count <= 0)) + return; - if (value_text == NULL || *value_text == '\0') - return true; + int civ_id = leader->ID; + if ((*p_human_player_bits & (1 << civ_id)) != 0) + return; - char * cursor = value_text; - while (1) { - while (is_space_char (*cursor)) - cursor++; + if (! leader_can_build_district (leader, CANAL_DISTRICT_ID) && ! leader_can_build_district (leader, BRIDGE_DISTRICT_ID)) + return; - if (*cursor == '\0') - break; + if (! is->ai_candidate_bridge_or_canals_initialized) { + reset_ai_candidate_bridge_or_canals (); + generate_ai_canal_and_bridge_targets (); + } - char * item_start; - char * item_end; - if (*cursor == '"') { - cursor++; - item_start = cursor; - while ((*cursor != '\0') && (*cursor != '"')) - cursor++; - if (*cursor != '"') { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: %s (missing closing quote)", line_number, key); - err->text[(sizeof err->text) - 1] = '\0'; - for (int j = 0; j < capacity; j++) { - if (dest[j] != NULL) { - free (dest[j]); - dest[j] = NULL; - } - } - *out_count = 0; - return false; - } - item_end = cursor; - cursor++; - } else { - item_start = cursor; - while ((*cursor != '\0') && (*cursor != ',')) - cursor++; - item_end = cursor; - } + for (int ei = 0; ei < is->ai_candidate_bridge_or_canals_count; ei++) { + struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[ei]; + if (entry->completed) + continue; - while ((item_end > item_start) && is_space_char (item_end[-1])) - item_end--; + int district_id = entry->district_id; + if ((district_id == CANAL_DISTRICT_ID) && (! is->current_config.enable_canal_districts)) continue; + if ((district_id == BRIDGE_DISTRICT_ID) && (! is->current_config.enable_bridge_districts)) continue; + if (! leader_can_build_district (leader, district_id)) continue; - int item_len = item_end - item_start; - if (item_len > 0) { - if (*out_count < capacity) { - char * copy = malloc (item_len + 1); - if (copy == NULL) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: %s (out of memory)", line_number, key); - err->text[(sizeof err->text) - 1] = '\0'; - for (int j = 0; j < capacity; j++) { - if (dest[j] != NULL) { - free (dest[j]); - dest[j] = NULL; - } + if (entry->assigned_worker_id >= 0) { + Unit * worker = get_unit_ptr (entry->assigned_worker_id); + if (worker == NULL) { + release_ai_candidate_bridge_or_canal_worker (entry); + } else if ((entry->assigned_tile_index >= 0) && (entry->assigned_tile_index < entry->tile_count)) { + int tx = entry->tile_x[entry->assigned_tile_index]; + int ty = entry->tile_y[entry->assigned_tile_index]; + wrap_tile_coords (&p_bic_data->Map, &tx, &ty); + Tile * tile = tile_at (tx, ty); + if ((tile != NULL) && (tile != p_null_tile)) { + struct district_instance * inst = get_district_instance (tile); + if (inst != NULL && inst->district_id == district_id && district_is_complete (tile, district_id)) { + release_ai_candidate_bridge_or_canal_worker (entry); } - *out_count = 0; - return false; } - memcpy (copy, item_start, item_len); - copy[item_len] = '\0'; - dest[*out_count] = copy; - *out_count += 1; } + if (entry->assigned_worker_id >= 0) + continue; } - while (is_space_char (*cursor)) - cursor++; - - if (*cursor == ',') { - cursor++; + int target_idx = -1; + if (! ai_candidate_bridge_or_canal_is_buildable_for_civ (entry, civ_id, &target_idx)) { + release_ai_candidate_bridge_or_canal_worker (entry); continue; } - if (*cursor == '\0') - break; - } - return true; + if (target_idx < 0) + continue; + + City * city = find_city_for_tile (civ_id, entry->tile_x[target_idx], entry->tile_y[target_idx]); + if (city == NULL) + continue; + + Unit * worker = find_best_worker_for_district (leader, city, district_id, entry->tile_x[target_idx], entry->tile_y[target_idx]); + if (worker == NULL) + continue; + + memset (&entry->pending_req, 0, sizeof entry->pending_req); + entry->pending_req.district_id = district_id; + entry->pending_req.civ_id = civ_id; + if (! assign_worker_to_district (&entry->pending_req, worker, city, district_id, entry->tile_x[target_idx], entry->tile_y[target_idx])) + continue; + + entry->assigned_worker_id = worker->Body.ID; + entry->assigned_tile_index = target_idx; + } } -bool -parse_buildable_square_type_mask (struct string_slice const * value, - unsigned int * out_mask, - struct error_line ** parse_errors, - int line_number) +void +recompute_city_yields_with_districts (City * city) { - char * value_text = trim_and_extract_slice (value, 0); - unsigned int mask = 0; - int entry_count = 0; + if (city == NULL) + return; - if (value_text != NULL) { - char * cursor = value_text; - while (1) { - while (is_space_char (*cursor)) - cursor++; + bool prev_flag = is->distribution_hub_refresh_in_progress; + is->distribution_hub_refresh_in_progress = true; + patch_City_recompute_yields_and_happiness (city); + is->distribution_hub_refresh_in_progress = prev_flag; +} - char * item_start = cursor; - while ((*cursor != '\0') && (*cursor != ',')) - cursor++; +enum UnitStateType __fastcall +patch_City_instruct_worker (City * this, int edx, int tile_x, int tile_y, bool param_3, Unit * worker) +{ + return City_instruct_worker (this, __, tile_x, tile_y, param_3, worker); +} - char * item_end = cursor; - while ((item_end > item_start) && is_space_char (item_end[-1])) - item_end--; +int +find_wonder_config_index_by_improvement_id (int improv_id) +{ + if (improv_id < 0) + return -1; - struct string_slice item_slice = { .str = item_start, .len = (int)(item_end - item_start) }; - if (item_slice.len > 0) { - if (slice_matches_str (&item_slice, "mine")) { - mask |= district_buildable_mine_mask_bit (); - entry_count += 1; - } else if (slice_matches_str (&item_slice, "irrigation")) { - mask |= district_buildable_irrigation_mask_bit (); - entry_count += 1; - } else { - enum SquareTypes parsed; - if (read_square_type_value (&item_slice, &parsed)) { - if (parsed == (enum SquareTypes)SQ_INVALID) - mask = all_square_types_mask (); - else - mask |= square_type_mask_bit (parsed); - entry_count += 1; - } else { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: %.*s (invalid buildable_on entry)", line_number, item_slice.len, item_slice.str); - err->text[(sizeof err->text) - 1] = '\0'; - free (value_text); - return false; - } - } - } + char ss[200]; - if (*cursor == ',') { - cursor++; - continue; - } - break; + for (int wi = 0; wi < is->wonder_district_count; wi++) { + int bid = -1; + if (stable_look_up (&is->building_name_to_id, is->wonder_district_configs[wi].wonder_name, &bid) && + (bid == improv_id)) { + return wi; } } - if (value_text != NULL) - free (value_text); + return -1; +} - if ((entry_count == 0) || (mask == 0)) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: buildable_on (expected at least one square type)", line_number); - err->text[(sizeof err->text) - 1] = '\0'; - return false; - } +void set_wonder_built_flag (int improv_id, bool is_built); - *out_mask = mask; - return true; +unsigned int +wonder_buildable_square_type_mask (struct wonder_district_config const * cfg) +{ + if (cfg == NULL) + return district_default_buildable_mask (); + + unsigned int mask = cfg->buildable_square_types_mask; + if (mask == 0) + mask = district_default_buildable_mask (); + return mask; +} + +unsigned int +wonder_buildable_mask_for_improvement (int improv_id) +{ + int windex = find_wonder_config_index_by_improvement_id (improv_id); + if (windex < 0) + return district_default_buildable_mask (); + return wonder_buildable_square_type_mask (&is->wonder_district_configs[windex]); } bool -parse_district_bonus_entries (struct string_slice const * value, - int * out_base_bonus, - struct district_bonus_list * out_extras, - struct error_line ** parse_errors, - int line_number, - struct string_slice const * key) +wonder_is_buildable_on_square_type (struct wonder_district_config const * cfg, Tile * tile) { - if ((out_base_bonus == NULL) || (out_extras == NULL)) { - add_key_parse_error (parse_errors, line_number, key, value, "(invalid bonus target)"); + if ((cfg == NULL) || (tile == NULL) || (tile == p_null_tile)) return false; - } - char * value_text = trim_and_extract_slice (value, 0); - free_bonus_entry_list (out_extras); - *out_base_bonus = 0; + return tile_matches_square_type_mask (tile, wonder_buildable_square_type_mask (cfg)); +} - if ((value_text == NULL) || (*value_text == '\0')) { - add_key_parse_error (parse_errors, line_number, key, value, "(expected base bonus)"); - free (value_text); +bool +wonder_is_buildable_on_tile (Tile * tile, int wonder_improv_id) +{ + if ((tile == NULL) || (tile == p_null_tile)) return false; - } - - bool got_base = false; - int base_bonus = 0; - - char * cursor = value_text; - while (1) { - while (is_space_char (*cursor)) - cursor++; - if (*cursor == '\0') - break; + unsigned int mask = wonder_buildable_mask_for_improvement (wonder_improv_id); + return tile_matches_square_type_mask (tile, mask); +} - char * item_start = cursor; - bool in_quotes = false; - while (*cursor != '\0') { - if (*cursor == '"') - in_quotes = ! in_quotes; - if ((! in_quotes) && (*cursor == ',')) - break; - cursor++; - } +int +get_wonder_improvement_id_from_index (int windex) +{ + if ((windex < 0) || (windex >= is->wonder_district_count)) + return -1; - char * item_end = cursor; - while ((item_end > item_start) && is_space_char (item_end[-1])) - item_end--; - while ((item_start < item_end) && is_space_char (*item_start)) - item_start++; + char const * wonder_name = is->wonder_district_configs[windex].wonder_name; + if ((wonder_name == NULL) || (wonder_name[0] == '\0')) + return -1; - if (item_end > item_start) { - struct string_slice item = { .str = item_start, .len = (int)(item_end - item_start) }; + int improv_id; + if (stable_look_up (&is->building_name_to_id, (char *)wonder_name, &improv_id)) + return improv_id; + else + return -1; +} - if (! got_base) { - struct string_slice base_slice = trim_string_slice (&item, 0); - if (! read_int (&base_slice, &base_bonus)) { - add_key_parse_error (parse_errors, line_number, key, value, "(expected base bonus)"); - free_bonus_entry_list (out_extras); - free (value_text); - return false; - } - got_base = true; - } else { - char * colon = NULL; - in_quotes = false; - for (char * p = item_start; p < item_end; p++) { - if (*p == '"') - in_quotes = ! in_quotes; - if ((! in_quotes) && (*p == ':')) { - colon = p; - break; - } - } +void +remember_pending_building_order (City * city, int improvement_id) +{ + if (! is->current_config.enable_districts || + (city == NULL) || + (improvement_id < 0)) + return; - if (colon == NULL) { - add_key_parse_error (parse_errors, line_number, key, value, "(expected \"name: bonus\" entry)"); - free_bonus_entry_list (out_extras); - free (value_text); - return false; - } + if ((*p_human_player_bits & (1 << city->Body.CivID)) != 0) + return; - struct string_slice name_slice = { .str = item_start, .len = (int)(colon - item_start) }; - struct string_slice bonus_slice = { .str = colon + 1, .len = (int)(item_end - (colon + 1)) }; - struct string_slice trimmed_name = trim_string_slice (&name_slice, 1); - struct string_slice trimmed_bonus = trim_string_slice (&bonus_slice, 0); + itable_insert (&is->city_pending_building_orders, (int)city, improvement_id); +} - if (trimmed_name.len <= 0) { - add_key_parse_error (parse_errors, line_number, key, value, "(expected bonus name)"); - free_bonus_entry_list (out_extras); - free (value_text); - return false; - } +bool +lookup_pending_building_order (City * city, int * out_improv_id) +{ + if (! is->current_config.enable_districts || + (city == NULL) || + (out_improv_id == NULL)) + return false; - int bonus_value = 0; - if (! read_int (&trimmed_bonus, &bonus_value)) { - add_key_parse_error (parse_errors, line_number, key, value, "(expected bonus value)"); - free_bonus_entry_list (out_extras); - free (value_text); - return false; - } + return itable_look_up (&is->city_pending_building_orders, (int)city, out_improv_id); +} - if (out_extras->count >= MAX_DISTRICT_BONUS_ENTRIES) { - add_key_parse_error (parse_errors, line_number, key, value, "(too many bonus entries)"); - free_bonus_entry_list (out_extras); - free (value_text); - return false; - } +void +forget_pending_building_order (City * city) +{ + if (! is->current_config.enable_districts || + (city == NULL)) + return; - struct district_bonus_entry * entry = &out_extras->entries[out_extras->count++]; - entry->bonus = bonus_value; - entry->building_id = -1; - entry->building_name = NULL; + itable_remove (&is->city_pending_building_orders, (int)city); +} - enum SquareTypes parsed_type; - if (read_square_type_value (&trimmed_name, &parsed_type)) { - entry->type = DBET_TILE; - entry->tile_type = parsed_type; - } else { - entry->type = DBET_BUILDING; - entry->tile_type = (enum SquareTypes)SQ_INVALID; - entry->building_name = extract_slice (&trimmed_name); - } - } - } +bool +is_wonder_or_small_wonder_already_being_built (City * city, int improv_id) +{ + if ((city == NULL) || (improv_id < 0) || (improv_id >= p_bic_data->ImprovementsCount)) + return false; - if (*cursor == ',') { - cursor++; + Improvement * improv = &p_bic_data->Improvements[improv_id]; + bool is_wonder = (improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) != 0; + if ((improv->Characteristics & (ITC_Wonder | ITC_Small_Wonder)) == 0) + return false; + + int civ_id = city->Body.CivID; + FOR_CITIES_OF (coi, civ_id) { + City * other_city = coi.city; + if ((other_city == NULL) || (other_city == city)) continue; - } - if (*cursor == '\0') - break; + + if ((other_city->Body.Order_Type == COT_Improvement) && + (other_city->Body.Order_ID == improv_id)) + return true; } - free (value_text); + return false; +} - if (! got_base) { - add_key_parse_error (parse_errors, line_number, key, value, "(expected base bonus)"); - free_bonus_entry_list (out_extras); - return false; - } +struct district_building_prereq_list * +get_district_building_prereq_list (int improv_id) +{ + if (improv_id < 0) + return NULL; - *out_base_bonus = base_bonus; - return true; + int stored = 0; + if (! itable_look_up (&is->district_building_prereqs, improv_id, &stored)) + return NULL; + return (struct district_building_prereq_list *)stored; } bool -override_special_district_from_definition (struct parsed_district_definition * def, int section_start_line) +district_building_prereq_list_contains (struct district_building_prereq_list * list, int district_id) { - if ((! def->has_name) || (def->name == NULL)) + if ((list == NULL) || (district_id < 0)) return false; + for (int i = 0; i < list->count; i++) { + if (list->district_ids[i] == district_id) + return true; + } + return false; +} - int index = find_special_district_index_by_name (def->name); - if (index < 0) - return false; +void +add_district_building_prereq (int improv_id, int district_id) +{ + if ((improv_id < 0) || (district_id < 0)) + return; - struct district_config * cfg = &is->district_configs[index]; - struct district_config const * defaults = &special_district_defaults[index]; + struct district_building_prereq_list * list = get_district_building_prereq_list (improv_id); + if (list == NULL) { + list = calloc (1, sizeof *list); + if (list == NULL) + return; + list->count = 0; + itable_insert (&is->district_building_prereqs, improv_id, (int)list); + } - free (def->name); - def->name = NULL; - def->has_name = false; + if (district_building_prereq_list_contains (list, district_id)) + return; - if (def->has_display_name) { - if ((cfg->display_name != NULL) && - (cfg->display_name != cfg->name) && - (cfg->display_name != defaults->display_name)) - free ((void *)cfg->display_name); - cfg->display_name = def->display_name; - def->display_name = NULL; - } + if (list->count >= ARRAY_LEN (list->district_ids)) + return; - if (def->has_tooltip) { - if ((cfg->tooltip != NULL) && (cfg->tooltip != defaults->tooltip)) - free ((void *)cfg->tooltip); - cfg->tooltip = def->tooltip; - def->tooltip = NULL; + list->district_ids[list->count++] = district_id; +} + +bool +city_has_river_district (City * city, int district_id) +{ + if (city == NULL) + return false; + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { + if (wai.district_inst->district_id != district_id) + continue; + if (wai.tile->vtable->m37_Get_River_Code (wai.tile) != 0) + return true; } + return false; +} - if (def->has_advance_prereqs) { - for (int i = 0; i < ARRAY_LEN (cfg->advance_prereqs); i++) { - char const * default_value = (i < defaults->advance_prereq_count) ? defaults->advance_prereqs[i] : NULL; - if ((cfg->advance_prereqs[i] != NULL) && - (cfg->advance_prereqs[i] != default_value)) - free ((void *)cfg->advance_prereqs[i]); - cfg->advance_prereqs[i] = NULL; - } +bool +city_has_any_prereq_district_for_improvement (City * city, + struct district_building_prereq_list * list, + bool requires_river, + bool allow_wonder_district) +{ + if ((city == NULL) || (list == NULL)) + return false; - cfg->advance_prereq_count = def->advance_prereq_count; - const int max_entries = ARRAY_LEN (cfg->advance_prereqs); - if (cfg->advance_prereq_count > max_entries) - cfg->advance_prereq_count = max_entries; - for (int i = 0; i < cfg->advance_prereq_count; i++) { - cfg->advance_prereqs[i] = def->advance_prereqs[i]; - def->advance_prereqs[i] = NULL; + for (int i = 0; i < list->count; i++) { + int district_id = list->district_ids[i]; + if (district_id < 0) + continue; + if (! allow_wonder_district && district_id == WONDER_DISTRICT_ID) + continue; + if (requires_river) { + if (city_has_river_district (city, district_id)) + return true; + } else if (city_has_required_district (city, district_id)) { + return true; } - def->advance_prereq_count = 0; } + return false; +} - if (def->has_obsoleted_by) { - if ((cfg->obsoleted_by != NULL) && (cfg->obsoleted_by != defaults->obsoleted_by)) - free ((void *)cfg->obsoleted_by); - cfg->obsoleted_by = def->obsoleted_by; - def->obsoleted_by = NULL; +int +pick_missing_district_for_improvement (City * city, struct district_building_prereq_list * list) +{ + if ((list == NULL) || (list->count <= 0)) + return -1; + + int fallback = -1; + for (int i = 0; i < list->count; i++) { + int district_id = list->district_ids[i]; + if (district_id < 0) + continue; + if (fallback < 0) + fallback = district_id; + if ((city != NULL) && leader_can_build_district (&leaders[city->Body.CivID], district_id)) + return district_id; } + return fallback; +} - if (def->has_resource_prereqs) { - for (int i = 0; i < ARRAY_LEN (cfg->resource_prereqs); i++) { - char const * default_value = (i < defaults->resource_prereq_count) ? defaults->resource_prereqs[i] : NULL; - if ((cfg->resource_prereqs[i] != NULL) && - (cfg->resource_prereqs[i] != default_value)) - free ((void *)cfg->resource_prereqs[i]); - cfg->resource_prereqs[i] = NULL; - } - cfg->resource_prereq_count = def->resource_prereq_count; - const int max_entries = ARRAY_LEN (cfg->resource_prereqs); - if (cfg->resource_prereq_count > max_entries) - cfg->resource_prereq_count = max_entries; - for (int i = 0; i < cfg->resource_prereq_count; i++) { - cfg->resource_prereqs[i] = def->resource_prereqs[i]; - def->resource_prereqs[i] = NULL; - } - } +bool +district_is_complete(Tile * tile, int district_id) +{ + if ((tile == NULL) || (tile == p_null_tile) || + (district_id < 0) || (district_id >= is->district_count)) + return false; - if (def->has_resource_prereq_on_tile) { - if ((cfg->resource_prereq_on_tile != NULL) && (cfg->resource_prereq_on_tile != defaults->resource_prereq_on_tile)) - free ((void *)cfg->resource_prereq_on_tile); - cfg->resource_prereq_on_tile = def->resource_prereq_on_tile; - def->resource_prereq_on_tile = NULL; - } + bool is_natural_wonder = (district_id == NATURAL_WONDER_DISTRICT_ID); + bool districts_disabled = ! is->current_config.enable_districts; + bool natural_wonders_disabled = ! is->current_config.enable_natural_wonders; + struct district_config const * cfg = &is->district_configs[district_id]; - if (def->has_wonder_prereqs) { - for (int i = 0; i < ARRAY_LEN (cfg->wonder_prereqs); i++) { - char const * default_value = (i < defaults->wonder_prereq_count) ? defaults->wonder_prereqs[i] : NULL; - if ((cfg->wonder_prereqs[i] != NULL) && - (cfg->wonder_prereqs[i] != default_value)) - free ((void *)cfg->wonder_prereqs[i]); - cfg->wonder_prereqs[i] = NULL; - } + if (districts_disabled && (!is_natural_wonder || natural_wonders_disabled)) + return false; - cfg->wonder_prereq_count = def->wonder_prereq_count; - const int max_entries = ARRAY_LEN (cfg->wonder_prereqs); - if (cfg->wonder_prereq_count > max_entries) - cfg->wonder_prereq_count = max_entries; - for (int i = 0; i < cfg->wonder_prereq_count; i++) { - cfg->wonder_prereqs[i] = def->wonder_prereqs[i]; - def->wonder_prereqs[i] = NULL; + struct district_instance * inst = get_district_instance (tile); + if (inst == NULL || inst->district_id != district_id) + return false; + + // If already marked COMPLETED, just return true + if (inst->state == DS_COMPLETED) + return true; + + // State is UNDER_CONSTRUCTION - check if tile has mines now + bool has_mines = tile->vtable->m18_Check_Mines (tile, __, 0) != 0; + + if (! has_mines) { + // Still under construction - check if we should clean it up + bool worker_present = false; + FOR_UNITS_ON (uti, tile) { + Unit * unit = uti.unit; + if ((unit != NULL) && + (p_bic_data->UnitTypes[unit->Body.UnitTypeID].Worker_Actions != 0)) { + worker_present = true; + break; + } + } + if (! worker_present) { + remove_district_instance (tile); } + return false; } - if (def->has_natural_wonder_prereqs) { - for (int i = 0; i < ARRAY_LEN (cfg->natural_wonder_prereqs); i++) { - char const * default_value = (i < defaults->natural_wonder_prereq_count) ? defaults->natural_wonder_prereqs[i] : NULL; - if ((cfg->natural_wonder_prereqs[i] != NULL) && - (cfg->natural_wonder_prereqs[i] != default_value)) - free ((void *)cfg->natural_wonder_prereqs[i]); - cfg->natural_wonder_prereqs[i] = NULL; + // Mark as completed and run one-time side effects + inst->state = DS_COMPLETED; + inst->completed_turn = *p_current_turn_no; + + int tile_x, tile_y; + if (district_instance_get_coords (inst, tile, &tile_x, &tile_y)) { + set_tile_unworkable_for_all_cities (tile, tile_x, tile_y); + int territory_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); + + if (cfg->auto_add_road) { + bool has_road = tile->vtable->m25_Check_Roads (tile, __, 0) != 0; + if (! has_road) + tile->vtable->m56_Set_Tile_Flags (tile, __, 0, TILE_FLAG_ROAD, tile_x, tile_y); } - cfg->natural_wonder_prereq_count = def->natural_wonder_prereq_count; - const int max_entries = ARRAY_LEN (cfg->natural_wonder_prereqs); - if (cfg->natural_wonder_prereq_count > max_entries) - cfg->natural_wonder_prereq_count = max_entries; - for (int i = 0; i < cfg->natural_wonder_prereq_count; i++) { - cfg->natural_wonder_prereqs[i] = def->natural_wonder_prereqs[i]; - def->natural_wonder_prereqs[i] = NULL; + if (cfg->auto_add_railroad) { + bool has_railroad = tile->vtable->m23_Check_Railroads (tile, __, 0) != 0; + if (! has_railroad) { + if ((territory_owner >= 0) && Leader_can_do_worker_job (&leaders[territory_owner], __, WJ_Build_Railroad, tile_x, tile_y, 0)) { + tile->vtable->m56_Set_Tile_Flags (tile, __, 0, TILE_FLAG_RAILROAD, tile_x, tile_y); + } + } } - } - if (def->has_buildable_on_districts) { - for (int i = 0; i < ARRAY_LEN (cfg->buildable_on_districts); i++) { - char const * default_value = (i < defaults->buildable_on_district_count) ? defaults->buildable_on_districts[i] : NULL; - if ((cfg->buildable_on_districts[i] != NULL) && - (cfg->buildable_on_districts[i] != default_value)) - free ((void *)cfg->buildable_on_districts[i]); - cfg->buildable_on_districts[i] = NULL; + // Activate distribution hub if applicable + if (is->current_config.enable_distribution_hub_districts && + (district_id == DISTRIBUTION_HUB_DISTRICT_ID)) { + on_distribution_hub_completed (tile, tile_x, tile_y); } - cfg->buildable_on_district_count = def->buildable_on_district_count; - const int max_entries = ARRAY_LEN (cfg->buildable_on_districts); - if (cfg->buildable_on_district_count > max_entries) - cfg->buildable_on_district_count = max_entries; - for (int i = 0; i < cfg->buildable_on_district_count; i++) { - cfg->buildable_on_districts[i] = def->buildable_on_districts[i]; - def->buildable_on_districts[i] = NULL; + // Remove forest/swamp if applicable + enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); + if ((base_type == SQ_Forest) || (base_type == SQ_Swamp)) { + int new_terrain_type = tile->vtable->m71_Check_Worker_Job (tile); + if (new_terrain_type >= 0) + Map_change_tile_terrain (&p_bic_data->Map, __, (enum SquareTypes)new_terrain_type, tile_x, tile_y); } - cfg->buildable_on_district_id_count = 0; - cfg->has_buildable_on_districts = true; - } - if (def->has_buildable_by_civs) { - for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { - char const * default_value = (i < defaults->buildable_by_civ_count) ? defaults->buildable_by_civs[i] : NULL; - if ((cfg->buildable_by_civs[i] != NULL) && - (cfg->buildable_by_civs[i] != default_value)) - free ((void *)cfg->buildable_by_civs[i]); - cfg->buildable_by_civs[i] = NULL; - } - cfg->buildable_by_civ_count = def->buildable_by_civ_count; - const int max_civ_names = ARRAY_LEN (cfg->buildable_by_civs); - if (cfg->buildable_by_civ_count > max_civ_names) - cfg->buildable_by_civ_count = max_civ_names; - for (int i = 0; i < cfg->buildable_by_civ_count; i++) { - cfg->buildable_by_civs[i] = def->buildable_by_civs[i]; - def->buildable_by_civs[i] = NULL; + char ss[200]; + snprintf (ss, sizeof ss, "District %d completed at tile (%d,%d)\n", district_id, tile_x, tile_y); + (*p_OutputDebugStringA) (ss); + + // Check if this was an AI-requested district + struct pending_district_request * req = find_pending_district_request_by_coords (NULL, tile_x, tile_y, district_id); + if (req != NULL) { + City * requesting_city = get_city_ptr (req->city_id); + if (requesting_city != NULL) { + req->city = requesting_city; + snprintf (ss, sizeof ss, "District %d at tile (%d,%d) was ordered by city %s\n", + district_id, tile_x, tile_y, requesting_city->Body.CityName); + (*p_OutputDebugStringA) (ss); + + // Check if city has pending building order that depends on this district + int pending_improv_id; + if (lookup_pending_building_order (requesting_city, &pending_improv_id)) { + struct district_building_prereq_list * prereq_list = get_district_building_prereq_list (pending_improv_id); + if (district_building_prereq_list_contains (prereq_list, district_id)) { + snprintf (ss, sizeof ss, "City %s setting production to improvement %d\n", + requesting_city->Body.CityName, pending_improv_id); + (*p_OutputDebugStringA) (ss); + + // Check if another city is already building this wonder/small wonder + bool can_set_production = true; + if (is_wonder_or_small_wonder_already_being_built (requesting_city, pending_improv_id)) { + snprintf (ss, sizeof ss, "City %s cannot build improvement %d - already being built elsewhere\n", + requesting_city->Body.CityName, pending_improv_id); + (*p_OutputDebugStringA) (ss); + can_set_production = false; + } + + // Set city production to the pending improvement + if (can_set_production && City_can_build_improvement (requesting_city, __, pending_improv_id, false)) { + snprintf (ss, sizeof ss, "City %s can now build improvement %d\n", + requesting_city->Body.CityName, pending_improv_id); + (*p_OutputDebugStringA) (ss); + City_set_production (requesting_city, __, COT_Improvement, pending_improv_id, false); + } + + // Clear the pending building order + forget_pending_building_order (requesting_city); + } + } + + // Clear worker assignment so worker is freed up for other tasks + if (req->assigned_worker_id >= 0) { + snprintf (ss, sizeof ss, "Clearing worker assignment for unit %d\n", req->assigned_worker_id); + (*p_OutputDebugStringA) (ss); + int civ_id = req->civ_id; + clear_tracked_worker_assignment_by_id (civ_id, req->assigned_worker_id); + } + } + + // Remove the pending district request + remove_pending_district_request (req); } - cfg->has_buildable_by_civs = true; } - if (def->has_buildable_by_civ_traits) { - cfg->buildable_by_civ_traits_id_count = def->buildable_by_civ_traits_id_count; - const int max_entries = ARRAY_LEN (cfg->buildable_by_civ_traits_ids); - if (cfg->buildable_by_civ_traits_id_count > max_entries) - cfg->buildable_by_civ_traits_id_count = max_entries; - for (int i = 0; i < cfg->buildable_by_civ_traits_id_count; i++) - cfg->buildable_by_civ_traits_ids[i] = def->buildable_by_civ_traits_ids[i]; - cfg->has_buildable_by_civ_traits = true; - } + return true; +} - if (def->has_buildable_by_civ_govs) { - cfg->buildable_by_civ_govs_id_count = def->buildable_by_civ_govs_id_count; - const int max_entries = ARRAY_LEN (cfg->buildable_by_civ_govs_ids); - if (cfg->buildable_by_civ_govs_id_count > max_entries) - cfg->buildable_by_civ_govs_id_count = max_entries; - for (int i = 0; i < cfg->buildable_by_civ_govs_id_count; i++) - cfg->buildable_by_civ_govs_ids[i] = def->buildable_by_civ_govs_ids[i]; - cfg->has_buildable_by_civ_govs = true; - } +void +mark_city_needs_district (City * city, int district_id) +{ + if (! is->current_config.enable_districts || + (city == NULL) || + (district_id < 0) || (district_id >= is->district_count)) + return; - if (def->has_buildable_by_civ_cultures) { - cfg->buildable_by_civ_cultures_id_count = def->buildable_by_civ_cultures_id_count; - const int max_entries = ARRAY_LEN (cfg->buildable_by_civ_cultures_ids); - if (cfg->buildable_by_civ_cultures_id_count > max_entries) - cfg->buildable_by_civ_cultures_id_count = max_entries; - for (int i = 0; i < cfg->buildable_by_civ_cultures_id_count; i++) - cfg->buildable_by_civ_cultures_ids[i] = def->buildable_by_civ_cultures_ids[i]; - cfg->has_buildable_by_civ_cultures = true; - } + create_pending_district_request (city, district_id); +} - if (def->has_buildable_by_war_allies) - cfg->buildable_by_war_allies = def->buildable_by_war_allies; - if (def->has_buildable_by_pact_allies) - cfg->buildable_by_pact_allies = def->buildable_by_pact_allies; +void +set_tile_unworkable_for_all_cities (Tile * tile, int tile_x, int tile_y) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return; - if (def->has_allow_multiple) - cfg->allow_multiple = def->allow_multiple; - if (def->has_vary_img_by_era) - cfg->vary_img_by_era = def->vary_img_by_era; - if (def->has_vary_img_by_culture) - cfg->vary_img_by_culture = def->vary_img_by_culture; - if (def->has_render_strategy) - cfg->render_strategy = def->render_strategy; - if (def->has_ai_build_strategy) - cfg->ai_build_strategy = def->ai_build_strategy; - if (def->has_align_to_coast) - cfg->align_to_coast = def->align_to_coast; - if (def->has_draw_over_resources) - cfg->draw_over_resources = def->draw_over_resources; - if (def->has_allow_irrigation_from) - cfg->allow_irrigation_from = def->allow_irrigation_from; - if (def->has_auto_add_road) - cfg->auto_add_road = def->auto_add_road; - if (def->has_auto_add_railroad) - cfg->auto_add_railroad = def->auto_add_railroad; - if (def->has_custom_width) - cfg->custom_width = def->custom_width; - if (def->has_custom_height) - cfg->custom_height = def->custom_height; - if (def->has_x_offset) - cfg->x_offset = def->x_offset; - if (def->has_y_offset) - cfg->y_offset = def->y_offset; - if (def->has_btn_tile_sheet_column) - cfg->btn_tile_sheet_column = def->btn_tile_sheet_column; - if (def->has_btn_tile_sheet_row) - cfg->btn_tile_sheet_row = def->btn_tile_sheet_row; - if (def->has_defense_bonus_percent) { - cfg->defense_bonus_percent = def->defense_bonus_percent; - free_bonus_entry_list_override (&cfg->defense_bonus_extras, &defaults->defense_bonus_extras); - move_bonus_entry_list (&cfg->defense_bonus_extras, &def->defense_bonus_extras); - } - if (def->has_heal_units_in_one_turn) - cfg->heal_units_in_one_turn = def->heal_units_in_one_turn; - if (def->has_culture_bonus) { - cfg->culture_bonus = def->culture_bonus; - free_bonus_entry_list_override (&cfg->culture_bonus_extras, &defaults->culture_bonus_extras); - move_bonus_entry_list (&cfg->culture_bonus_extras, &def->culture_bonus_extras); - } - if (def->has_science_bonus) { - cfg->science_bonus = def->science_bonus; - free_bonus_entry_list_override (&cfg->science_bonus_extras, &defaults->science_bonus_extras); - move_bonus_entry_list (&cfg->science_bonus_extras, &def->science_bonus_extras); - } - if (def->has_food_bonus) { - cfg->food_bonus = def->food_bonus; - free_bonus_entry_list_override (&cfg->food_bonus_extras, &defaults->food_bonus_extras); - move_bonus_entry_list (&cfg->food_bonus_extras, &def->food_bonus_extras); - } - if (def->has_gold_bonus) { - cfg->gold_bonus = def->gold_bonus; - free_bonus_entry_list_override (&cfg->gold_bonus_extras, &defaults->gold_bonus_extras); - move_bonus_entry_list (&cfg->gold_bonus_extras, &def->gold_bonus_extras); - } - if (def->has_shield_bonus) { - cfg->shield_bonus = def->shield_bonus; - free_bonus_entry_list_override (&cfg->shield_bonus_extras, &defaults->shield_bonus_extras); - move_bonus_entry_list (&cfg->shield_bonus_extras, &def->shield_bonus_extras); - } - if (def->has_happiness_bonus) { - cfg->happiness_bonus = def->happiness_bonus; - free_bonus_entry_list_override (&cfg->happiness_bonus_extras, &defaults->happiness_bonus_extras); - move_bonus_entry_list (&cfg->happiness_bonus_extras, &def->happiness_bonus_extras); + wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); + + City * assigned_city = NULL; + int assigned_city_id = tile->Body.CityAreaID; + if (assigned_city_id >= 0) + assigned_city = get_city_ptr (assigned_city_id); + + if (assigned_city != NULL) { + int neighbor_index = Map_compute_neighbor_index (&p_bic_data->Map, __, + assigned_city->Body.X, assigned_city->Body.Y, tile_x, tile_y, 1000); + bool removed_assignment = false; + if ((neighbor_index > 0) && (neighbor_index < ARRAY_LEN (is->ni_to_work_radius))) + removed_assignment = City_stop_working_tile (assigned_city, __, neighbor_index); + if (! removed_assignment) + tile->Body.CityAreaID = -1; + if (! removed_assignment) + recompute_city_yields_with_districts (assigned_city); + } else + tile->Body.CityAreaID = -1; + + if (p_cities->Cities != NULL) { + for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { + City * city = get_city_ptr (city_index); + if ((city == NULL) || (city == assigned_city)) + continue; + int neighbor_index = Map_compute_neighbor_index (&p_bic_data->Map, __, + city->Body.X, city->Body.Y, tile_x, tile_y, 1000); + if ((neighbor_index <= 0) || (neighbor_index >= ARRAY_LEN (is->ni_to_work_radius))) + continue; + int work_radius = is->ni_to_work_radius[neighbor_index]; + if ((work_radius < 0) || (work_radius > is->current_config.city_work_radius)) + continue; + recompute_city_yields_with_districts (city); + } } - if (def->has_buildable_on) - cfg->buildable_square_types_mask = def->buildable_square_types_mask; +} - if (def->has_generated_resource) { - if ((cfg->generated_resource != NULL) && (cfg->generated_resource != defaults->generated_resource)) - free ((void *)cfg->generated_resource); - cfg->generated_resource = def->generated_resource; - def->generated_resource = NULL; - cfg->generated_resource_flags = def->generated_resource_flags; - cfg->generated_resource_id = -1; - } +struct distribution_hub_record * +get_distribution_hub_record (Tile * tile) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return NULL; - if (def->has_dependent_improvements) { - for (int i = 0; i < ARRAY_LEN (cfg->dependent_improvements); i++) { - char const * default_value = (i < defaults->dependent_improvement_count) ? defaults->dependent_improvements[i] : NULL; - if ((cfg->dependent_improvements[i] != NULL) && - (cfg->dependent_improvements[i] != default_value)) - free ((void *)cfg->dependent_improvements[i]); - cfg->dependent_improvements[i] = NULL; - } + int stored; + if (itable_look_up (&is->distribution_hub_records, (int)tile, &stored)) + return (struct distribution_hub_record *)stored; + else + return NULL; +} - cfg->dependent_improvement_count = def->dependent_improvement_count; - const int max_entries = ARRAY_LEN (cfg->dependent_improvements); - if (cfg->dependent_improvement_count > max_entries) - cfg->dependent_improvement_count = max_entries; - for (int i = 0; i < cfg->dependent_improvement_count; i++) { - cfg->dependent_improvements[i] = def->dependent_improvements[i]; - def->dependent_improvements[i] = NULL; - } - cfg->max_building_index = cfg->dependent_improvement_count; - if (cfg->max_building_index > 20) - cfg->max_building_index = 20; - } +City * +get_connected_city_for_distribution_hub (struct distribution_hub_record * rec) +{ + if (rec == NULL) + return NULL; - if (def->has_img_paths) { - for (int i = 0; i < ARRAY_LEN (cfg->img_paths); i++) { - char const * default_value = (i < defaults->img_path_count) ? defaults->img_paths[i] : NULL; - if ((cfg->img_paths[i] != NULL) && - (cfg->img_paths[i] != default_value)) - free ((void *)cfg->img_paths[i]); - cfg->img_paths[i] = NULL; - } + Tile * tile = rec->tile; + if ((tile == NULL) || (tile == p_null_tile)) + tile = tile_at (rec->tile_x, rec->tile_y); + if ((tile == NULL) || (tile == p_null_tile)) + return NULL; - cfg->img_path_count = def->img_path_count; - const int max_img_paths = ARRAY_LEN (cfg->img_paths); - if (cfg->img_path_count > max_img_paths) - cfg->img_path_count = max_img_paths; - for (int i = 0; i < cfg->img_path_count; i++) { - cfg->img_paths[i] = def->img_paths[i]; - def->img_paths[i] = NULL; - } - } + int city_id = tile->Body.connected_city_ids[rec->civ_id]; + if (city_id < 0) + return NULL; - if (! ensure_culture_variant_art (cfg, section_start_line)) { - free_special_district_override_strings (cfg, defaults); - *cfg = *defaults; - return false; - } + City * city = get_city_ptr (city_id); - return true; + return city; } bool -add_dynamic_district_from_definition (struct parsed_district_definition * def, int section_start_line) +distribution_hub_accessible_to_city (struct distribution_hub_record * rec, City * city) { - if ((! def->has_name) || (def->name == NULL)) + if ((rec == NULL) || (city == NULL)) return false; - if ((! def->has_img_paths) || (def->img_path_count <= 0)) + if (city->Body.CivID != rec->civ_id) return false; - int existing_index = -1; - for (int i = is->special_district_count; i < is->district_count; i++) { - if ((is->district_configs[i].name != NULL) && - (strcmp (is->district_configs[i].name, def->name) == 0)) { - existing_index = i; - break; - } - } + City * anchor_city = get_connected_city_for_distribution_hub (rec); + if (anchor_city == NULL) + return false; - bool reusing_existing = existing_index >= 0; - int dest_index = reusing_existing ? existing_index : (is->special_district_count + is->dynamic_district_count); + if (anchor_city == city) + return true; - if ((! reusing_existing) && (dest_index >= COUNT_DISTRICT_TYPES)) - return false; + return Trade_Net_have_trade_connection (is->trade_net, __, anchor_city, city, rec->civ_id); +} - enum Unit_Command_Values preserved_command = 0; - if (reusing_existing) - preserved_command = is->district_configs[dest_index].command; +void +get_distribution_hub_yields_for_city (City * city, int * out_food, int * out_shields) +{ + int food = 0; + int shields = 0; - struct district_config new_cfg; - memset (&new_cfg, 0, sizeof new_cfg); - new_cfg.is_dynamic = true; + if ((city != NULL) && + is->current_config.enable_districts && + is->current_config.enable_distribution_hub_districts) { + if (is->distribution_hub_totals_dirty && + ! is->distribution_hub_refresh_in_progress) + recompute_distribution_hub_totals (); - new_cfg.name = def->name; - def->name = NULL; - if (def->has_display_name) { - new_cfg.display_name = def->display_name; - if (new_cfg.display_name == NULL) - new_cfg.display_name = new_cfg.name; - def->display_name = NULL; - } else { - new_cfg.display_name = new_cfg.name; + FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { + struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; + if (distribution_hub_accessible_to_city (rec, city)) { + food += rec->food_yield; + shields += rec->shield_yield; + } + } } - if (def->has_tooltip) { - new_cfg.tooltip = def->tooltip; - def->tooltip = NULL; - } else if (new_cfg.name != NULL) { - char buffer[128]; - snprintf (buffer, sizeof buffer, "Build %s", new_cfg.name); - new_cfg.tooltip = strdup (buffer); + if (city_has_required_district (city, CENTRAL_RAIL_HUB_DISTRICT_ID)) { + food += (food * is->current_config.central_rail_hub_distribution_food_bonus_percent) / 100; + shields += (shields * is->current_config.central_rail_hub_distribution_shield_bonus_percent) / 100; } - if (def->has_advance_prereqs) { - new_cfg.advance_prereq_count = def->advance_prereq_count; - const int max_entries = ARRAY_LEN (new_cfg.advance_prereqs); - if (new_cfg.advance_prereq_count > max_entries) - new_cfg.advance_prereq_count = max_entries; - for (int i = 0; i < new_cfg.advance_prereq_count; i++) { - new_cfg.advance_prereqs[i] = def->advance_prereqs[i]; - def->advance_prereqs[i] = NULL; + if (out_food != NULL) + *out_food = food; + if (out_shields != NULL) + *out_shields = shields; +} + +void +adjust_distribution_hub_coverage (struct distribution_hub_record * rec) +{ + if (rec == NULL) + return; + + FOR_TILES_AROUND (tai, workable_tile_counts[1], rec->tile_x, rec->tile_y) { + Tile * area_tile = tai.tile; + if ((area_tile == NULL) || (area_tile == p_null_tile)) + continue; + + int tx, ty; + tai_get_coords (&tai, &tx, &ty); + + if (area_tile->vtable->m38_Get_Territory_OwnerID (area_tile) != rec->civ_id) + continue; + if (Tile_has_city (area_tile)) + continue; + if (get_district_instance (area_tile) != NULL) + continue; + + struct wonder_district_info * area_info = get_wonder_district_info (area_tile); + if ((area_info != NULL) && (area_info->state == WDS_COMPLETED)) + continue; + + int key = (int)area_tile; + int prev = itable_look_up_or (&is->distribution_hub_coverage_counts, key, 0); + itable_insert (&is->distribution_hub_coverage_counts, key, prev + 1); + + if (area_tile->Body.CityAreaID >= 0) { + set_tile_unworkable_for_all_cities (area_tile, tx, ty); + area_tile->Body.CityAreaID = -1; } - def->advance_prereq_count = 0; } +} - if (def->has_obsoleted_by) { - new_cfg.obsoleted_by = def->obsoleted_by; - def->obsoleted_by = NULL; +void +release_distribution_hub_coverage (struct distribution_hub_record * rec) +{ + if (rec == NULL) + return; + + FOR_TILES_AROUND (tai, workable_tile_counts[1], rec->tile_x, rec->tile_y) { + Tile * area_tile = tai.tile; + if ((area_tile == NULL) || (area_tile == p_null_tile)) + continue; + + int key = (int)area_tile; + int prev = itable_look_up_or (&is->distribution_hub_coverage_counts, key, 0); + if (prev <= 0) + continue; + + if (prev == 1) + itable_remove (&is->distribution_hub_coverage_counts, key); + else + itable_insert (&is->distribution_hub_coverage_counts, key, prev - 1); } +} - new_cfg.resource_prereq_count = def->has_resource_prereqs ? def->resource_prereq_count : 0; - const int max_resource_entries = ARRAY_LEN (new_cfg.resource_prereqs); - if (new_cfg.resource_prereq_count > max_resource_entries) - new_cfg.resource_prereq_count = max_resource_entries; - for (int i = 0; i < new_cfg.resource_prereq_count; i++) { - new_cfg.resource_prereqs[i] = def->resource_prereqs[i]; - def->resource_prereqs[i] = NULL; +void +clear_distribution_hub_tables (void) +{ + FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { + struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; + free (rec); } + table_deinit (&is->distribution_hub_records); + table_deinit (&is->distribution_hub_coverage_counts); + is->distribution_hub_totals_dirty = true; +} + +bool +city_radius_contains_tile (City * city, int tile_x, int tile_y) +{ + if (city == NULL) + return false; + + int ni = patch_Map_compute_ni_for_work_area (&p_bic_data->Map, __, city->Body.X, city->Body.Y, tile_x, tile_y, is->workable_tile_count); + return ni >= 0; +} + +void +recompute_distribution_hub_yields (struct distribution_hub_record * rec) +{ + if (rec == NULL) + return; - if (def->has_resource_prereq_on_tile) { - new_cfg.resource_prereq_on_tile = def->resource_prereq_on_tile; - def->resource_prereq_on_tile = NULL; - } + Tile * tile = tile_at (rec->tile_x, rec->tile_y); + rec->tile = tile; - new_cfg.wonder_prereq_count = def->has_wonder_prereqs ? def->wonder_prereq_count : 0; - const int max_required_wonders = ARRAY_LEN (new_cfg.wonder_prereqs); - if (new_cfg.wonder_prereq_count > max_required_wonders) - new_cfg.wonder_prereq_count = max_required_wonders; - for (int i = 0; i < new_cfg.wonder_prereq_count; i++) { - new_cfg.wonder_prereqs[i] = def->wonder_prereqs[i]; - def->wonder_prereqs[i] = NULL; + if ((tile == NULL) || + (tile == p_null_tile) || + ! district_is_complete (tile, DISTRIBUTION_HUB_DISTRICT_ID) || + tile->vtable->m20_Check_Pollution (tile, __, 0) || + tile_has_enemy_unit (tile, rec->civ_id)) { + rec->food_yield = 0; + rec->shield_yield = 0; + rec->raw_food_yield = 0; + rec->raw_shield_yield = 0; + return; } - new_cfg.natural_wonder_prereq_count = def->has_natural_wonder_prereqs ? def->natural_wonder_prereq_count : 0; - const int max_required_natural_wonders = ARRAY_LEN (new_cfg.natural_wonder_prereqs); - if (new_cfg.natural_wonder_prereq_count > max_required_natural_wonders) - new_cfg.natural_wonder_prereq_count = max_required_natural_wonders; - for (int i = 0; i < new_cfg.natural_wonder_prereq_count; i++) { - new_cfg.natural_wonder_prereqs[i] = def->natural_wonder_prereqs[i]; - def->natural_wonder_prereqs[i] = NULL; - } + int food_sum = 0; + int shield_sum = 0; + City * anchor_city = get_connected_city_for_distribution_hub (rec); + FOR_TILES_AROUND (tai, workable_tile_counts[1], rec->tile_x, rec->tile_y) { + Tile * area_tile = tai.tile; + if (area_tile == p_null_tile) + continue; - new_cfg.buildable_on_district_count = def->has_buildable_on_districts ? def->buildable_on_district_count : 0; - const int max_buildable_on_districts = ARRAY_LEN (new_cfg.buildable_on_districts); - if (new_cfg.buildable_on_district_count > max_buildable_on_districts) - new_cfg.buildable_on_district_count = max_buildable_on_districts; - for (int i = 0; i < new_cfg.buildable_on_district_count; i++) { - new_cfg.buildable_on_districts[i] = def->buildable_on_districts[i]; - def->buildable_on_districts[i] = NULL; - } - new_cfg.buildable_on_district_id_count = 0; - new_cfg.has_buildable_on_districts = def->has_buildable_on_districts; + int tx, ty; + tai_get_coords (&tai, &tx, &ty); - new_cfg.buildable_by_civ_count = def->has_buildable_by_civs ? def->buildable_by_civ_count : 0; - const int max_civ_names = ARRAY_LEN (new_cfg.buildable_by_civs); - if (new_cfg.buildable_by_civ_count > max_civ_names) - new_cfg.buildable_by_civ_count = max_civ_names; - for (int i = 0; i < new_cfg.buildable_by_civ_count; i++) { - new_cfg.buildable_by_civs[i] = def->buildable_by_civs[i]; - def->buildable_by_civs[i] = NULL; - } + // Only include tiles that belong to the distribution hub owner + if (area_tile->vtable->m38_Get_Territory_OwnerID (area_tile) != rec->civ_id) + continue; - new_cfg.has_buildable_by_civs = def->has_buildable_by_civs; + // Skip city tiles + if (Tile_has_city (area_tile)) + continue; - new_cfg.buildable_by_civ_traits_id_count = def->has_buildable_by_civ_traits ? def->buildable_by_civ_traits_id_count : 0; - const int max_buildable_traits = ARRAY_LEN (new_cfg.buildable_by_civ_traits_ids); - if (new_cfg.buildable_by_civ_traits_id_count > max_buildable_traits) - new_cfg.buildable_by_civ_traits_id_count = max_buildable_traits; - for (int i = 0; i < new_cfg.buildable_by_civ_traits_id_count; i++) - new_cfg.buildable_by_civ_traits_ids[i] = def->buildable_by_civ_traits_ids[i]; - new_cfg.has_buildable_by_civ_traits = def->has_buildable_by_civ_traits; + // Skip tiles with enemy units + if (tile_has_enemy_unit (area_tile, rec->civ_id)) + continue; - new_cfg.buildable_by_civ_govs_id_count = def->has_buildable_by_civ_govs ? def->buildable_by_civ_govs_id_count : 0; - const int max_buildable_govs = ARRAY_LEN (new_cfg.buildable_by_civ_govs_ids); - if (new_cfg.buildable_by_civ_govs_id_count > max_buildable_govs) - new_cfg.buildable_by_civ_govs_id_count = max_buildable_govs; - for (int i = 0; i < new_cfg.buildable_by_civ_govs_id_count; i++) - new_cfg.buildable_by_civ_govs_ids[i] = def->buildable_by_civ_govs_ids[i]; - new_cfg.has_buildable_by_civ_govs = def->has_buildable_by_civ_govs; + // Skip tiles with pollution + if (area_tile->vtable->m20_Check_Pollution (area_tile, __, 0)) + continue; - new_cfg.buildable_by_civ_cultures_id_count = def->has_buildable_by_civ_cultures ? def->buildable_by_civ_cultures_id_count : 0; - const int max_buildable_cultures = ARRAY_LEN (new_cfg.buildable_by_civ_cultures_ids); - if (new_cfg.buildable_by_civ_cultures_id_count > max_buildable_cultures) - new_cfg.buildable_by_civ_cultures_id_count = max_buildable_cultures; - for (int i = 0; i < new_cfg.buildable_by_civ_cultures_id_count; i++) - new_cfg.buildable_by_civ_cultures_ids[i] = def->buildable_by_civ_cultures_ids[i]; - new_cfg.has_buildable_by_civ_cultures = def->has_buildable_by_civ_cultures; + // Skip tiles that are other districts (but not this hub itself) + struct district_instance * area_district = get_district_instance (area_tile); + if ((area_district != NULL) && ((tx != rec->tile_x) || (ty != rec->tile_y))) + continue; - new_cfg.buildable_by_war_allies = def->has_buildable_by_war_allies ? def->buildable_by_war_allies : false; - new_cfg.buildable_by_pact_allies = def->has_buildable_by_pact_allies ? def->buildable_by_pact_allies : false; + // Skip tiles with completed wonders + struct wonder_district_info * area_info = get_wonder_district_info (area_tile); + if ((area_info != NULL) && (area_info->state == WDS_COMPLETED)) + continue; - new_cfg.allow_multiple = def->has_allow_multiple ? def->allow_multiple : false; - new_cfg.vary_img_by_era = def->has_vary_img_by_era ? def->vary_img_by_era : false; - new_cfg.vary_img_by_culture = def->has_vary_img_by_culture ? def->vary_img_by_culture : false; - new_cfg.render_strategy = def->has_render_strategy ? def->render_strategy : DRS_BY_COUNT; - new_cfg.ai_build_strategy = def->has_ai_build_strategy ? def->ai_build_strategy : DABS_DISTRICT; - new_cfg.align_to_coast = def->has_align_to_coast ? def->align_to_coast : false; - new_cfg.draw_over_resources = def->has_draw_over_resources ? def->draw_over_resources : false; - new_cfg.allow_irrigation_from = def->has_allow_irrigation_from ? def->allow_irrigation_from : false; - new_cfg.auto_add_road = def->has_auto_add_road ? def->auto_add_road : false; - new_cfg.auto_add_railroad = def->has_auto_add_railroad ? def->auto_add_railroad : false; - new_cfg.custom_width = def->has_custom_width ? def->custom_width : 0; - new_cfg.custom_height = def->has_custom_height ? def->custom_height : 0; - new_cfg.x_offset = def->has_x_offset ? def->x_offset : 0; - new_cfg.y_offset = def->has_y_offset ? def->y_offset : 0; - new_cfg.btn_tile_sheet_column = def->has_btn_tile_sheet_column ? def->btn_tile_sheet_column : 0; - new_cfg.btn_tile_sheet_row = def->has_btn_tile_sheet_row ? def->btn_tile_sheet_row : 0; - new_cfg.defense_bonus_percent = def->has_defense_bonus_percent ? def->defense_bonus_percent : 0; - new_cfg.heal_units_in_one_turn = def->has_heal_units_in_one_turn ? def->heal_units_in_one_turn : false; - new_cfg.culture_bonus = def->has_culture_bonus ? def->culture_bonus : 0; - new_cfg.science_bonus = def->has_science_bonus ? def->science_bonus : 0; - new_cfg.food_bonus = def->has_food_bonus ? def->food_bonus : 0; - new_cfg.gold_bonus = def->has_gold_bonus ? def->gold_bonus : 0; - new_cfg.shield_bonus = def->has_shield_bonus ? def->shield_bonus : 0; - new_cfg.happiness_bonus = def->has_happiness_bonus ? def->happiness_bonus : 0; - new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); + // Check if another hub of the same civ is closer to this tile + int my_distance = compute_wrapped_manhattan_distance (rec->tile_x, rec->tile_y, tx, ty); + bool tile_belongs_to_me = true; - if (def->has_culture_bonus) - move_bonus_entry_list (&new_cfg.culture_bonus_extras, &def->culture_bonus_extras); - if (def->has_science_bonus) - move_bonus_entry_list (&new_cfg.science_bonus_extras, &def->science_bonus_extras); - if (def->has_food_bonus) - move_bonus_entry_list (&new_cfg.food_bonus_extras, &def->food_bonus_extras); - if (def->has_gold_bonus) - move_bonus_entry_list (&new_cfg.gold_bonus_extras, &def->gold_bonus_extras); - if (def->has_shield_bonus) - move_bonus_entry_list (&new_cfg.shield_bonus_extras, &def->shield_bonus_extras); - if (def->has_happiness_bonus) - move_bonus_entry_list (&new_cfg.happiness_bonus_extras, &def->happiness_bonus_extras); - if (def->has_defense_bonus_percent) - move_bonus_entry_list (&new_cfg.defense_bonus_extras, &def->defense_bonus_extras); + FOR_TABLE_ENTRIES (other_tei, &is->distribution_hub_records) { + struct distribution_hub_record * other_rec = (struct distribution_hub_record *)other_tei.value; + if ((other_rec == NULL) || (other_rec == rec)) + continue; + if (other_rec->civ_id != rec->civ_id) + continue; - if (def->has_generated_resource) { - new_cfg.generated_resource = def->generated_resource; - def->generated_resource = NULL; - new_cfg.generated_resource_flags = def->generated_resource_flags; - new_cfg.generated_resource_id = -1; - } else { - new_cfg.generated_resource = NULL; - new_cfg.generated_resource_id = -1; - new_cfg.generated_resource_flags = 0; - } + int other_distance = compute_wrapped_manhattan_distance (other_rec->tile_x, other_rec->tile_y, tx, ty); + if (other_distance < my_distance) { + tile_belongs_to_me = false; + break; + } + if (other_distance == my_distance) { + // Tie-breaking: prefer hub with lower Y, then lower X + if (other_rec->tile_y < rec->tile_y) { + tile_belongs_to_me = false; + break; + } + if ((other_rec->tile_y == rec->tile_y) && (other_rec->tile_x < rec->tile_x)) { + tile_belongs_to_me = false; + break; + } + } + } - new_cfg.dependent_improvement_count = def->has_dependent_improvements ? def->dependent_improvement_count : 0; - const int max_dependent_entries = ARRAY_LEN (is->district_configs[0].dependent_improvements); - if (new_cfg.dependent_improvement_count > max_dependent_entries) - new_cfg.dependent_improvement_count = max_dependent_entries; - for (int i = 0; i < new_cfg.dependent_improvement_count; i++) { - new_cfg.dependent_improvements[i] = def->dependent_improvements[i]; - def->dependent_improvements[i] = NULL; - } + if (! tile_belongs_to_me) + continue; - new_cfg.img_path_count = def->img_path_count; - const int max_img_paths = ARRAY_LEN (is->district_configs[0].img_paths); - if (new_cfg.img_path_count > max_img_paths) - new_cfg.img_path_count = max_img_paths; - for (int i = 0; i < new_cfg.img_path_count; i++) { - new_cfg.img_paths[i] = def->img_paths[i]; - def->img_paths[i] = NULL; + food_sum += City_calc_tile_yield_at (anchor_city, __, 0, tx, ty); + shield_sum += City_calc_tile_yield_at (anchor_city, __, 1, tx, ty); } - if (! ensure_culture_variant_art (&new_cfg, section_start_line)) { - free_dynamic_district_config (&new_cfg); - return false; - } + rec->raw_food_yield = food_sum; + rec->raw_shield_yield = shield_sum; - new_cfg.max_building_index = new_cfg.dependent_improvement_count; - if (new_cfg.max_building_index > 20) - new_cfg.max_building_index = 20; + int food_div = is->current_config.distribution_hub_food_yield_divisor; + int shield_div = is->current_config.distribution_hub_shield_yield_divisor; + if (food_div <= 0) + food_div = 1; + if (shield_div <= 0) + shield_div = 1; - if (reusing_existing) - new_cfg.command = preserved_command; - else - new_cfg.command = allocate_dynamic_district_command (new_cfg.name); + int connected_city_count = 0; + if (anchor_city != NULL) { + FOR_CITIES_OF (coi, rec->civ_id) { + City * other_city = coi.city; + if ((other_city != NULL) && distribution_hub_accessible_to_city (rec, other_city)) + connected_city_count++; + } + } + if (connected_city_count <= 0) + connected_city_count = 1; - struct district_config * dest_cfg = &is->district_configs[dest_index]; - if (reusing_existing) { - enum Unit_Command_Values saved_command = preserved_command; - free_dynamic_district_config (dest_cfg); - *dest_cfg = new_cfg; - dest_cfg->command = saved_command; + if (is->current_config.distribution_hub_yield_division_mode == DHYDM_SCALE_BY_CITY_COUNT) { + int city_root = 1; + while ((city_root + 1) * (city_root + 1) <= connected_city_count) + city_root++; + int city_food_divisor = city_root * food_div; + int city_shield_divisor = city_root * shield_div; + if (city_food_divisor < 1) city_food_divisor = 1; + if (city_shield_divisor < 1) city_shield_divisor = 1; + rec->food_yield = food_sum / city_food_divisor; + rec->shield_yield = shield_sum / city_shield_divisor; } else { - free_dynamic_district_config (dest_cfg); - *dest_cfg = new_cfg; - is->dynamic_district_count += 1; - is->district_count = is->special_district_count + is->dynamic_district_count; + rec->food_yield = food_sum / food_div; + rec->shield_yield = shield_sum / shield_div; } - - return true; } void -finalize_parsed_district_definition (struct parsed_district_definition * def, int section_start_line) +remove_distribution_hub_record (Tile * tile) { - if ((! def->has_name) || (def->name == NULL)) + struct distribution_hub_record * rec = get_distribution_hub_record (tile); + if (rec == NULL) return; - if (! override_special_district_from_definition (def, section_start_line)) - add_dynamic_district_from_definition (def, section_start_line); + int affected_civ_id = rec->civ_id; + release_distribution_hub_coverage (rec); + itable_remove (&is->distribution_hub_records, (int)tile); + free (rec); + is->distribution_hub_totals_dirty = true; + recompute_distribution_hub_totals (); - free_parsed_district_definition (def); + // Recalculate yields for all cities of this civ + if ((affected_civ_id >= 0) && (p_cities->Cities != NULL)) { + for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { + City * target_city = get_city_ptr (city_index); + if ((target_city != NULL) && (target_city->Body.CivID == affected_civ_id)) + recompute_city_yields_with_districts (target_city); + } + } } void -handle_district_definition_key (struct parsed_district_definition * def, - struct string_slice const * key, - struct string_slice const * value, - int line_number, - struct error_line ** parse_errors, - struct error_line ** unrecognized_keys) +recompute_distribution_hub_totals () { - if (slice_matches_str (key, "name")) { - if (def->name != NULL) { - free (def->name); - def->name = NULL; - } + if (! is->current_config.enable_districts || + ! is->current_config.enable_distribution_hub_districts) { + is->distribution_hub_totals_dirty = false; + return; + } - char * name_copy = copy_trimmed_string_or_null (value, 1); - if (name_copy == NULL) { - def->has_name = false; - add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); - } else { - def->name = name_copy; - def->has_name = true; - } + struct table new_coverage_counts = {0}; + struct table newly_covered_tiles = {0}; - } else if (slice_matches_str (key, "display_name")) { - if (def->display_name != NULL) { - free (def->display_name); - def->display_name = NULL; - } - def->display_name = copy_trimmed_string_or_null (value, 1); - def->has_display_name = true; + clear_memo (); + int civs_needing_recalc[32] = {0}; - } else if (slice_matches_str (key, "tooltip")) { - if (def->tooltip != NULL) { - free (def->tooltip); - def->tooltip = NULL; - } - def->tooltip = copy_trimmed_string_or_null (value, 1); - def->has_tooltip = true; + FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { + Tile * tile = (Tile *)tei.key; + struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; + if (rec == NULL) + continue; - } else if (slice_matches_str (key, "advance_prereqs") || slice_matches_str (key, "advance_prereq")) { - for (int i = 0; i < def->advance_prereq_count; i++) { - if (def->advance_prereqs[i] != NULL) { - free (def->advance_prereqs[i]); - def->advance_prereqs[i] = NULL; - } - } - def->advance_prereq_count = 0; - char * value_text = trim_and_extract_slice (value, 0); - int list_count = 0; - if (parse_config_string_list (value_text, - def->advance_prereqs, - ARRAY_LEN (def->advance_prereqs), - &list_count, - parse_errors, - line_number, - "advance_prereqs")) { - def->advance_prereq_count = list_count; - def->has_advance_prereqs = true; - } else { - def->advance_prereq_count = 0; - def->has_advance_prereqs = false; + Tile * current_tile = tile_at (rec->tile_x, rec->tile_y); + if ((current_tile == NULL) || + (current_tile == p_null_tile) || + ! district_is_complete (current_tile, DISTRIBUTION_HUB_DISTRICT_ID)) { + memoize (tei.key); + continue; } - free (value_text); - } else if (slice_matches_str (key, "obsoleted_by")) { - if (def->obsoleted_by != NULL) { - free (def->obsoleted_by); - def->obsoleted_by = NULL; - } - def->obsoleted_by = copy_trimmed_string_or_null (value, 1); - def->has_obsoleted_by = true; + rec->tile = current_tile; + rec->food_yield = 0; + rec->shield_yield = 0; + rec->raw_food_yield = 0; + rec->raw_shield_yield = 0; - } else if (slice_matches_str (key, "resource_prereqs")) { - char * value_text = trim_and_extract_slice (value, 0); - int list_count = 0; - if (parse_config_string_list (value_text, - def->resource_prereqs, - ARRAY_LEN (def->resource_prereqs), - &list_count, - parse_errors, - line_number, - "resource_prereqs")) { - def->resource_prereq_count = list_count; - def->has_resource_prereqs = true; - } else { - def->resource_prereq_count = 0; - def->has_resource_prereqs = false; - } - free (value_text); + int old_civ_id = rec->civ_id; + rec->civ_id = current_tile->vtable->m38_Get_Territory_OwnerID (current_tile); - } else if (slice_matches_str (key, "resource_prereq_on_tile")) { - if (def->resource_prereq_on_tile != NULL) { - free (def->resource_prereq_on_tile); - def->resource_prereq_on_tile = NULL; - } - def->resource_prereq_on_tile = copy_trimmed_string_or_null (value, 1); - def->has_resource_prereq_on_tile = true; + if (old_civ_id != rec->civ_id) + civs_needing_recalc[old_civ_id] = 1; + civs_needing_recalc[rec->civ_id] = 1; - } else if (slice_matches_str (key, "wonder_prereqs")) { - char * value_text = trim_and_extract_slice (value, 0); - int list_count = 0; - if (parse_config_string_list (value_text, - def->wonder_prereqs, - ARRAY_LEN (def->wonder_prereqs), - &list_count, - parse_errors, - line_number, - "wonder_prereqs")) { - def->wonder_prereq_count = list_count; - def->has_wonder_prereqs = true; - } else { - def->wonder_prereq_count = 0; - def->has_wonder_prereqs = false; - } - free (value_text); + City * anchor = get_connected_city_for_distribution_hub (rec); - } else if (slice_matches_str (key, "natural_wonder_prereqs")) { - char * value_text = trim_and_extract_slice (value, 0); - int list_count = 0; - if (parse_config_string_list (value_text, - def->natural_wonder_prereqs, - ARRAY_LEN (def->natural_wonder_prereqs), - &list_count, - parse_errors, - line_number, - "natural_wonder_prereqs")) { - def->natural_wonder_prereq_count = list_count; - def->has_natural_wonder_prereqs = true; - } else { - def->natural_wonder_prereq_count = 0; - def->has_natural_wonder_prereqs = false; - } - free (value_text); + if ((anchor == NULL) || + current_tile->vtable->m20_Check_Pollution (current_tile, __, 0) || + tile_has_enemy_unit (current_tile, rec->civ_id)) + continue; - } else if (slice_matches_str (key, "buildable_on_districts")) { - char * value_text = trim_and_extract_slice (value, 0); - int list_count = 0; - if (parse_config_string_list (value_text, - def->buildable_on_districts, - ARRAY_LEN (def->buildable_on_districts), - &list_count, - parse_errors, - line_number, - "buildable_on_districts")) { - def->buildable_on_district_count = list_count; - def->has_buildable_on_districts = true; - } else { - def->buildable_on_district_count = 0; - def->has_buildable_on_districts = false; - } - free (value_text); + FOR_TILES_AROUND (tai, workable_tile_counts[1], rec->tile_x, rec->tile_y) { + Tile * area_tile = tai.tile; + if (area_tile == p_null_tile) + continue; - } else if (slice_matches_str (key, "buildable_by_civs")) { - char * value_text = trim_and_extract_slice (value, 0); - int list_count = 0; - if (parse_config_string_list (value_text, - def->buildable_by_civs, - ARRAY_LEN (def->buildable_by_civs), - &list_count, - parse_errors, - line_number, - "buildable_by_civs")) { - def->buildable_by_civ_count = list_count; - def->has_buildable_by_civs = true; - } else { - def->buildable_by_civ_count = 0; - def->has_buildable_by_civs = false; - } - free (value_text); + int tx, ty; + tai_get_coords (&tai, &tx, &ty); - } else if (slice_matches_str (key, "buildable_by_civ_traits")) { - char * value_text = trim_and_extract_slice (value, 0); - int list_count = 0; - if (parse_config_string_list (value_text, - def->buildable_by_civ_traits, - ARRAY_LEN (def->buildable_by_civ_traits), - &list_count, - parse_errors, - line_number, - "buildable_by_civ_traits")) { - def->buildable_by_civ_traits_count = list_count; - def->buildable_by_civ_traits_id_count = 0; - for (int i = 0; i < def->buildable_by_civ_traits_count; i++) { - char const * trait_name = def->buildable_by_civ_traits[i]; - if ((trait_name == NULL) || (trait_name[0] == '\0')) - continue; - int trait_id = -1; - struct string_slice trait_slice = { .str = (char *)trait_name, .len = (int)strlen (trait_name) }; - if (find_civ_trait_id_by_name (&trait_slice, &trait_id)) { - bool already_listed = false; - for (int k = 0; k < def->buildable_by_civ_traits_id_count; k++) { - if (def->buildable_by_civ_traits_ids[k] == trait_id) { - already_listed = true; - break; - } - } - if ((! already_listed) && (def->buildable_by_civ_traits_id_count < ARRAY_LEN (def->buildable_by_civ_traits_ids))) - def->buildable_by_civ_traits_ids[def->buildable_by_civ_traits_id_count++] = trait_id; - } else { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: buildable_by_civ_traits entry \"%.*s\" not found", line_number, trait_slice.len, trait_slice.str); - err->text[(sizeof err->text) - 1] = '\0'; - } - } + if (area_tile->vtable->m38_Get_Territory_OwnerID (area_tile) != rec->civ_id) + continue; + if (Tile_has_city (area_tile)) + continue; + if (tile_has_enemy_unit (area_tile, rec->civ_id)) + continue; + if (area_tile->vtable->m20_Check_Pollution (area_tile, __, 0)) + continue; - for (int i = 0; i < def->buildable_by_civ_traits_count; i++) { - if (def->buildable_by_civ_traits[i] != NULL) { - free (def->buildable_by_civ_traits[i]); - def->buildable_by_civ_traits[i] = NULL; - } - } - def->buildable_by_civ_traits_count = 0; - def->has_buildable_by_civ_traits = true; - } else { - def->buildable_by_civ_traits_count = 0; - def->buildable_by_civ_traits_id_count = 0; - def->has_buildable_by_civ_traits = false; + struct district_instance * area_district = get_district_instance (area_tile); + if ((area_district != NULL) && ((tx != rec->tile_x) || (ty != rec->tile_y))) + continue; + + struct wonder_district_info * area_info = get_wonder_district_info (area_tile); + if ((area_info != NULL) && (area_info->state == WDS_COMPLETED)) + continue; + + int key = (int)area_tile; + int prev_cover_pass = itable_look_up_or (&new_coverage_counts, key, 0); + int prev_cover_old = itable_look_up_or (&is->distribution_hub_coverage_counts, key, 0); + itable_insert (&new_coverage_counts, key, prev_cover_pass + 1); + if ((prev_cover_pass == 0) && (prev_cover_old <= 0)) + itable_insert (&newly_covered_tiles, key, 1); } - free (value_text); + } - } else if (slice_matches_str (key, "buildable_by_civ_govs")) { - char * value_text = trim_and_extract_slice (value, 0); - int list_count = 0; - if (parse_config_string_list (value_text, - def->buildable_by_civ_govs, - ARRAY_LEN (def->buildable_by_civ_govs), - &list_count, - parse_errors, - line_number, - "buildable_by_civ_govs")) { - def->buildable_by_civ_govs_count = list_count; - def->buildable_by_civ_govs_id_count = 0; - for (int i = 0; i < def->buildable_by_civ_govs_count; i++) { - char const * gov_name = def->buildable_by_civ_govs[i]; - if ((gov_name == NULL) || (gov_name[0] == '\0')) - continue; - int gov_id = -1; - struct string_slice gov_slice = { .str = (char *)gov_name, .len = (int)strlen (gov_name) }; - if (find_game_object_id_by_name (GOK_GOVERNMENT, &gov_slice, 0, &gov_id)) { - bool already_listed = false; - for (int k = 0; k < def->buildable_by_civ_govs_id_count; k++) { - if (def->buildable_by_civ_govs_ids[k] == gov_id) { - already_listed = true; - break; - } - } - if ((! already_listed) && (def->buildable_by_civ_govs_id_count < ARRAY_LEN (def->buildable_by_civ_govs_ids))) - def->buildable_by_civ_govs_ids[def->buildable_by_civ_govs_id_count++] = gov_id; - } else { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: buildable_by_civ_govs entry \"%.*s\" not found", line_number, gov_slice.len, gov_slice.str); - err->text[(sizeof err->text) - 1] = '\0'; - } - } + for (int i = 0; i < is->memo_len; i++) + remove_distribution_hub_record ((Tile *)is->memo[i]); + clear_memo (); - for (int i = 0; i < def->buildable_by_civ_govs_count; i++) { - if (def->buildable_by_civ_govs[i] != NULL) { - free (def->buildable_by_civ_govs[i]); - def->buildable_by_civ_govs[i] = NULL; - } - } - def->buildable_by_civ_govs_count = 0; - def->has_buildable_by_civ_govs = true; - } else { - def->buildable_by_civ_govs_count = 0; - def->buildable_by_civ_govs_id_count = 0; - def->has_buildable_by_civ_govs = false; + FOR_TABLE_ENTRIES (tei, &is->distribution_hub_records) { + struct distribution_hub_record * rec = (struct distribution_hub_record *)tei.value; + if (rec == NULL) + continue; + + City * anchor = get_connected_city_for_distribution_hub (rec); + if (anchor == NULL) { + rec->food_yield = 0; + rec->shield_yield = 0; + rec->raw_food_yield = 0; + rec->raw_shield_yield = 0; + continue; } - free (value_text); - } else if (slice_matches_str (key, "buildable_by_civ_cultures")) { - char * value_text = trim_and_extract_slice (value, 0); - int list_count = 0; - if (parse_config_string_list (value_text, - def->buildable_by_civ_cultures, - ARRAY_LEN (def->buildable_by_civ_cultures), - &list_count, - parse_errors, - line_number, - "buildable_by_civ_cultures")) { - def->buildable_by_civ_cultures_count = list_count; - def->buildable_by_civ_cultures_id_count = 0; - for (int i = 0; i < def->buildable_by_civ_cultures_count; i++) { - char const * culture_name = def->buildable_by_civ_cultures[i]; - if ((culture_name == NULL) || (culture_name[0] == '\0')) - continue; - int culture_id = -1; - struct string_slice culture_slice = { .str = (char *)culture_name, .len = (int)strlen (culture_name) }; - if (find_civ_culture_id_by_name (&culture_slice, &culture_id)) { - bool already_listed = false; - for (int k = 0; k < def->buildable_by_civ_cultures_id_count; k++) { - if (def->buildable_by_civ_cultures_ids[k] == culture_id) { - already_listed = true; - break; - } - } - if ((! already_listed) && (def->buildable_by_civ_cultures_id_count < ARRAY_LEN (def->buildable_by_civ_cultures_ids))) - def->buildable_by_civ_cultures_ids[def->buildable_by_civ_cultures_id_count++] = culture_id; - } else { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: buildable_by_civ_cultures entry \"%.*s\" not found", line_number, culture_slice.len, culture_slice.str); - err->text[(sizeof err->text) - 1] = '\0'; - } - } + recompute_distribution_hub_yields (rec); + } - for (int i = 0; i < def->buildable_by_civ_cultures_count; i++) { - if (def->buildable_by_civ_cultures[i] != NULL) { - free (def->buildable_by_civ_cultures[i]); - def->buildable_by_civ_cultures[i] = NULL; - } + table_deinit (&is->distribution_hub_coverage_counts); + is->distribution_hub_coverage_counts = new_coverage_counts; + memset (&new_coverage_counts, 0, sizeof new_coverage_counts); + + FOR_TABLE_ENTRIES (tei, &newly_covered_tiles) { + Tile * covered_tile = (Tile *)tei.key; + if ((covered_tile == NULL) || (covered_tile == p_null_tile)) + continue; + int tx, ty; + if (! tile_coords_from_ptr (&p_bic_data->Map, covered_tile, &tx, &ty)) + continue; + set_tile_unworkable_for_all_cities (covered_tile, tx, ty); + covered_tile->Body.CityAreaID = -1; + } + table_deinit (&newly_covered_tiles); + + // Recalculate yields for cities of civs whose distribution hub ownership changed + for (int civ_id = 0; civ_id < 32; civ_id++) { + if (civs_needing_recalc[civ_id] && (p_cities->Cities != NULL)) { + for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { + City * city = get_city_ptr (city_index); + if ((city != NULL) && (city->Body.CivID == civ_id)) + recompute_city_yields_with_districts (city); } - def->buildable_by_civ_cultures_count = 0; - def->has_buildable_by_civ_cultures = true; - } else { - def->buildable_by_civ_cultures_count = 0; - def->buildable_by_civ_cultures_id_count = 0; - def->has_buildable_by_civ_cultures = false; } - free (value_text); + } - } else if (slice_matches_str (key, "buildable_by_war_allies")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->buildable_by_war_allies = (ival != 0); - def->has_buildable_by_war_allies = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + is->distribution_hub_totals_dirty = false; +} - } else if (slice_matches_str (key, "buildable_by_pact_allies")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->buildable_by_pact_allies = (ival != 0); - def->has_buildable_by_pact_allies = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); +void +on_distribution_hub_completed (Tile * tile, int tile_x, int tile_y) +{ + if (! is->current_config.enable_districts || ! is->current_config.enable_distribution_hub_districts) + return; - } else if (slice_matches_str (key, "img_paths")) { - char * value_text = trim_and_extract_slice (value, 0); - int list_count = 0; - if (parse_config_string_list (value_text, - def->img_paths, - ARRAY_LEN (def->img_paths), - &list_count, - parse_errors, - line_number, - "img_paths")) { - def->img_path_count = list_count; - def->has_img_paths = true; - } else { - def->img_path_count = 0; - def->has_img_paths = false; - } - free (value_text); + int tile_owner = -1; + if ((tile != NULL) && (tile != p_null_tile)) + tile_owner = tile->vtable->m38_Get_Territory_OwnerID (tile); - } else if (slice_matches_str (key, "dependent_improvs")) { - char * value_text = trim_and_extract_slice (value, 0); - int list_count = 0; - if (parse_config_string_list (value_text, - def->dependent_improvements, - ARRAY_LEN (def->dependent_improvements), - &list_count, - parse_errors, - line_number, - "dependent_improvs")) { - def->dependent_improvement_count = list_count; - def->has_dependent_improvements = true; - } else { - def->dependent_improvement_count = 0; - def->has_dependent_improvements = false; + struct distribution_hub_record * rec = get_distribution_hub_record (tile); + if (rec != NULL) { + int old_civ_id = rec->civ_id; + rec->tile = tile; + rec->tile_x = tile_x; + rec->tile_y = tile_y; + + release_distribution_hub_coverage (rec); + rec->civ_id = tile_owner; + is->distribution_hub_totals_dirty = true; + recompute_distribution_hub_totals (); + + if (old_civ_id != tile_owner) { + // Recompute for old civ + for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { + City * target_city = get_city_ptr (city_index); + if ((target_city != NULL) && (target_city->Body.CivID == old_civ_id)) + recompute_city_yields_with_districts (target_city); + } } - free (value_text); + } + + rec = malloc (sizeof *rec); + if (rec == NULL) + return; + rec->tile = tile; + rec->tile_x = tile_x; + rec->tile_y = tile_y; + rec->civ_id = tile_owner; + rec->food_yield = 0; + rec->shield_yield = 0; + rec->raw_food_yield = 0; + rec->raw_shield_yield = 0; + itable_insert (&is->distribution_hub_records, (int)tile, (int)rec); + adjust_distribution_hub_coverage (rec); - } else if (slice_matches_str (key, "buildable_on")) { - unsigned int mask; - if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number)) { - def->buildable_square_types_mask = mask; - def->has_buildable_on = true; + is->distribution_hub_totals_dirty = true; + recompute_distribution_hub_totals (); + + // Recalculate yields for all cities of this civ + int affected_civ_id = rec->civ_id; + if ((affected_civ_id >= 0) && (p_cities->Cities != NULL)) { + for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { + City * target_city = get_city_ptr (city_index); + if ((target_city != NULL) && (target_city->Body.CivID == affected_civ_id)) + recompute_city_yields_with_districts (target_city); } + } +} - } else if (slice_matches_str (key, "allow_multiple")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->allow_multiple = (ival != 0); - def->has_allow_multiple = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); +void +refresh_distribution_hubs_for_city (City * city) +{ + if (! is->current_config.enable_districts || + ! is->current_config.enable_distribution_hub_districts || + (city == NULL)) + return; - } else if (slice_matches_str (key, "vary_img_by_era")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->vary_img_by_era = (ival != 0); - def->has_vary_img_by_era = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + FOR_DISTRICTS_AROUND (wai, city->Body.X, city->Body.Y, true) { + int tx = wai.tile_x, ty = wai.tile_y; + Tile * tile = wai.tile; + struct district_instance * inst = wai.district_inst; + if (inst->district_id != DISTRIBUTION_HUB_DISTRICT_ID) + continue; + on_distribution_hub_completed (tile, tx, ty); + } +} - } else if (slice_matches_str (key, "vary_img_by_culture")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->vary_img_by_culture = (ival != 0); - def->has_vary_img_by_culture = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); +bool +is_space_char (char c) +{ + switch (c) { + case ' ': + case '\t': + case '\n': + case '\r': + case '\f': + case '\v': + return true; + default: + return false; + } +} - } else if (slice_matches_str (key, "render_strategy")) { - char * strategy = copy_trimmed_string_or_null (value, 1); - if (strategy == NULL) { - def->has_render_strategy = false; - add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); - } else if (strcmp (strategy, "by-count") == 0) { - def->render_strategy = DRS_BY_COUNT; - def->has_render_strategy = true; - } else if (strcmp (strategy, "by-building") == 0) { - def->render_strategy = DRS_BY_BUILDING; - def->has_render_strategy = true; - } else { - def->has_render_strategy = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected \"by-count\" or \"by-building\")"); - } - if (strategy != NULL) - free (strategy); +enum key_value_parse_status { + KVP_SUCCESS, + KVP_NO_EQUALS, + KVP_EMPTY_KEY +}; - } else if (slice_matches_str (key, "ai_build_strategy")) { - char * strategy = copy_trimmed_string_or_null (value, 1); - if (strategy == NULL) { - def->has_ai_build_strategy = false; - add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); - } else if (strcmp (strategy, "district") == 0) { - def->ai_build_strategy = DABS_DISTRICT; - def->has_ai_build_strategy = true; - } else if (strcmp (strategy, "tile-improvement") == 0) { - def->ai_build_strategy = DABS_TILE_IMPROVEMENT; - def->has_ai_build_strategy = true; - } else { - def->has_ai_build_strategy = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected \"district\" or \"tile-improvement\")"); +enum key_value_parse_status +parse_trimmed_key_value (struct string_slice const * trimmed, + struct string_slice * out_key, + struct string_slice * out_value) +{ + if ((trimmed == NULL) || (trimmed->len <= 0)) + return KVP_NO_EQUALS; + + char * equals = NULL; + for (int i = 0; i < trimmed->len; i++) { + if (trimmed->str[i] == '=') { + equals = trimmed->str + i; + break; } - if (strategy != NULL) - free (strategy); + } + if (equals == NULL) + return KVP_NO_EQUALS; - } else if (slice_matches_str (key, "align_to_coast")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->align_to_coast = (ival != 0); - def->has_align_to_coast = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + struct string_slice key = { .str = trimmed->str, .len = (int)(equals - trimmed->str) }; + key = trim_string_slice (&key, 0); + if (key.len == 0) + return KVP_EMPTY_KEY; - } else if (slice_matches_str (key, "draw_over_resources")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->draw_over_resources = (ival != 0); - def->has_draw_over_resources = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + struct string_slice value = { .str = equals + 1, .len = (int)((trimmed->str + trimmed->len) - (equals + 1)) }; + *out_key = key; + *out_value = trim_string_slice (&value, 0); + return KVP_SUCCESS; +} - } else if (slice_matches_str (key, "allow_irrigation_from")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->allow_irrigation_from = (ival != 0); - def->has_allow_irrigation_from = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); +void +add_key_parse_error (struct error_line ** parse_errors, + int line_number, + struct string_slice const * key, + struct string_slice const * value, + char const * message_suffix) +{ + struct error_line * err = add_error_line (parse_errors); + char * key_str = extract_slice (key); + char * value_str = (value != NULL) ? extract_slice (value) : NULL; + if (value_str != NULL) + snprintf (err->text, sizeof err->text, "^ Line %d: %s \"%s\" %s", line_number, key_str, value_str, message_suffix); + else + snprintf (err->text, sizeof err->text, "^ Line %d: %s %s", line_number, key_str, message_suffix); + err->text[(sizeof err->text) - 1] = '\0'; + if (value_str != NULL) + free (value_str); + free (key_str); +} - } else if (slice_matches_str (key, "auto_add_road")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->auto_add_road = (ival != 0); - def->has_auto_add_road = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); +void +add_unrecognized_key_error (struct error_line ** unrecognized_keys, + int line_number, + struct string_slice const * key) +{ + struct error_line * err_line = add_error_line (unrecognized_keys); + char * key_str = extract_slice (key); + snprintf (err_line->text, sizeof err_line->text, "^ Line %d: %s", line_number, key_str); + err_line->text[(sizeof err_line->text) - 1] = '\0'; + free (key_str); +} - } else if (slice_matches_str (key, "auto_add_railroad")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->auto_add_railroad = (ival != 0); - def->has_auto_add_railroad = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); +char * +copy_trimmed_string_or_null (struct string_slice const * slice, int remove_quotes) +{ + struct string_slice trimmed = trim_string_slice (slice, remove_quotes); + if (trimmed.len == 0) + return NULL; + return extract_slice (&trimmed); +} - } else if (slice_matches_str (key, "custom_width")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->custom_width = ival; - def->has_custom_width = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); +void +free_bonus_entry_list (struct district_bonus_list * list) +{ + if (list == NULL) + return; + + for (int i = 0; i < list->count; i++) { + if (list->entries[i].type == DBET_BUILDING && + list->entries[i].building_name != NULL) { + free ((void *)list->entries[i].building_name); + list->entries[i].building_name = NULL; + } + } + list->count = 0; +} + +void +free_bonus_entry_list_override (struct district_bonus_list * list, + struct district_bonus_list const * defaults) +{ + if (list == NULL) + return; + + for (int i = 0; i < list->count; i++) { + if (list->entries[i].type == DBET_BUILDING && + list->entries[i].building_name != NULL) { + char const * default_name = NULL; + if ((defaults != NULL) && (i < defaults->count)) + default_name = defaults->entries[i].building_name; + if (list->entries[i].building_name != default_name) + free ((void *)list->entries[i].building_name); + list->entries[i].building_name = NULL; + } + } + list->count = (defaults != NULL) ? defaults->count : 0; +} - } else if (slice_matches_str (key, "custom_height")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->custom_height = ival; - def->has_custom_height = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); +void +move_bonus_entry_list (struct district_bonus_list * dest, + struct district_bonus_list * src) +{ + if ((dest == NULL) || (src == NULL)) + return; - } else if (slice_matches_str (key, "x_offset")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->x_offset = ival; - def->has_x_offset = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + dest->count = src->count; + for (int i = 0; i < src->count; i++) { + dest->entries[i] = src->entries[i]; + src->entries[i].building_name = NULL; + } + src->count = 0; +} - } else if (slice_matches_str (key, "y_offset")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->y_offset = ival; - def->has_y_offset = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); +void +free_dynamic_district_config (struct district_config * cfg) +{ + if (cfg == NULL) + return; - } else if (slice_matches_str (key, "btn_tile_sheet_column")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->btn_tile_sheet_column = ival; - def->has_btn_tile_sheet_column = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + if (! cfg->is_dynamic) + return; - } else if (slice_matches_str (key, "btn_tile_sheet_row")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->btn_tile_sheet_row = ival; - def->has_btn_tile_sheet_row = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + char const * name_ptr = cfg->name; + if (cfg->name != NULL) { + free ((void *)cfg->name); + cfg->name = NULL; + } + if ((cfg->display_name != NULL) && + (cfg->display_name != name_ptr)) { + free ((void *)cfg->display_name); + cfg->display_name = NULL; + } + if (cfg->tooltip != NULL) { + free ((void *)cfg->tooltip); + cfg->tooltip = NULL; + } + for (int i = 0; i < ARRAY_LEN (cfg->advance_prereqs); i++) { + if (cfg->advance_prereqs[i] != NULL) { + free ((void *)cfg->advance_prereqs[i]); + cfg->advance_prereqs[i] = NULL; + } + } + cfg->advance_prereq_count = 0; + if (cfg->obsoleted_by != NULL) { + free ((void *)cfg->obsoleted_by); + cfg->obsoleted_by = NULL; + } - } else if (slice_matches_str (key, "defense_bonus_percent")) { - if (parse_district_bonus_entries (value, &def->defense_bonus_percent, &def->defense_bonus_extras, parse_errors, line_number, key)) { - def->has_defense_bonus_percent = true; - } else { - def->has_defense_bonus_percent = false; + for (int i = 0; i < 5; i++) { + if (cfg->resource_prereqs[i] != NULL) { + free ((void *)cfg->resource_prereqs[i]); + cfg->resource_prereqs[i] = NULL; } + } - } else if (slice_matches_str (key, "heal_units_in_one_turn")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->heal_units_in_one_turn = (ival != 0); - def->has_heal_units_in_one_turn = true; - } else - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + if (cfg->resource_prereq_on_tile != NULL) { + free ((void *)cfg->resource_prereq_on_tile); + cfg->resource_prereq_on_tile = NULL; + } - } else if (slice_matches_str (key, "culture_bonus")) { - if (parse_district_bonus_entries (value, &def->culture_bonus, &def->culture_bonus_extras, parse_errors, line_number, key)) { - def->has_culture_bonus = true; - } else { - def->has_culture_bonus = false; + for (int i = 0; i < ARRAY_LEN (cfg->wonder_prereqs); i++) { + if (cfg->wonder_prereqs[i] != NULL) { + free ((void *)cfg->wonder_prereqs[i]); + cfg->wonder_prereqs[i] = NULL; } + } + cfg->wonder_prereq_count = 0; - } else if (slice_matches_str (key, "science_bonus")) { - if (parse_district_bonus_entries (value, &def->science_bonus, &def->science_bonus_extras, parse_errors, line_number, key)) { - def->has_science_bonus = true; - } else { - def->has_science_bonus = false; + for (int i = 0; i < ARRAY_LEN (cfg->natural_wonder_prereqs); i++) { + if (cfg->natural_wonder_prereqs[i] != NULL) { + free ((void *)cfg->natural_wonder_prereqs[i]); + cfg->natural_wonder_prereqs[i] = NULL; } + } + cfg->natural_wonder_prereq_count = 0; - } else if (slice_matches_str (key, "food_bonus")) { - if (parse_district_bonus_entries (value, &def->food_bonus, &def->food_bonus_extras, parse_errors, line_number, key)) { - def->has_food_bonus = true; - } else { - def->has_food_bonus = false; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_on_districts); i++) { + if (cfg->buildable_on_districts[i] != NULL) { + free ((void *)cfg->buildable_on_districts[i]); + cfg->buildable_on_districts[i] = NULL; } + } + cfg->buildable_on_district_count = 0; + cfg->buildable_on_district_id_count = 0; + cfg->has_buildable_on_districts = false; - } else if (slice_matches_str (key, "gold_bonus")) { - if (parse_district_bonus_entries (value, &def->gold_bonus, &def->gold_bonus_extras, parse_errors, line_number, key)) { - def->has_gold_bonus = true; - } else { - def->has_gold_bonus = false; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { + if (cfg->buildable_by_civs[i] != NULL) { + free ((void *)cfg->buildable_by_civs[i]); + cfg->buildable_by_civs[i] = NULL; } + } + cfg->buildable_by_civ_count = 0; + cfg->has_buildable_by_civs = false; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_traits_ids); i++) + cfg->buildable_by_civ_traits_ids[i] = -1; + cfg->buildable_by_civ_traits_id_count = 0; + cfg->has_buildable_by_civ_traits = false; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_govs_ids); i++) + cfg->buildable_by_civ_govs_ids[i] = -1; + cfg->buildable_by_civ_govs_id_count = 0; + cfg->has_buildable_by_civ_govs = false; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_cultures_ids); i++) + cfg->buildable_by_civ_cultures_ids[i] = -1; + cfg->buildable_by_civ_cultures_id_count = 0; + cfg->has_buildable_by_civ_cultures = false; - } else if (slice_matches_str (key, "shield_bonus")) { - if (parse_district_bonus_entries (value, &def->shield_bonus, &def->shield_bonus_extras, parse_errors, line_number, key)) { - def->has_shield_bonus = true; - } else { - def->has_shield_bonus = false; + for (int i = 0; i < 5; i++) { + if (cfg->dependent_improvements[i] != NULL) { + free ((void *)cfg->dependent_improvements[i]); + cfg->dependent_improvements[i] = NULL; } + } - } else if (slice_matches_str (key, "happiness_bonus")) { - if (parse_district_bonus_entries (value, &def->happiness_bonus, &def->happiness_bonus_extras, parse_errors, line_number, key)) { - def->has_happiness_bonus = true; - } else { - def->has_happiness_bonus = false; + for (int i = 0; i < 10; i++) { + if (cfg->img_paths[i] != NULL) { + free ((void *)cfg->img_paths[i]); + cfg->img_paths[i] = NULL; } + } - } else if (slice_matches_str (key, "generated_resource")) { - if (def->generated_resource != NULL) { - free (def->generated_resource); - def->generated_resource = NULL; - } - def->generated_resource_flags = 0; + free_bonus_entry_list (&cfg->culture_bonus_extras); + free_bonus_entry_list (&cfg->science_bonus_extras); + free_bonus_entry_list (&cfg->food_bonus_extras); + free_bonus_entry_list (&cfg->gold_bonus_extras); + free_bonus_entry_list (&cfg->shield_bonus_extras); + free_bonus_entry_list (&cfg->happiness_bonus_extras); + free_bonus_entry_list (&cfg->defense_bonus_extras); - char * value_text = trim_and_extract_slice (value, 0); - if ((value_text == NULL) || (*value_text == '\0')) { - def->generated_resource = NULL; - def->has_generated_resource = true; - } else { - char * cursor = value_text; - struct string_slice resource_name = {0}; - struct string_slice token; - bool ok = true; - while (skip_white_space (&cursor) && parse_string (&cursor, &token)) { - if (slice_matches_str (&token, "local")) - def->generated_resource_flags |= MF_LOCAL; - else if (slice_matches_str (&token, "no-tech-req")) - def->generated_resource_flags |= MF_NO_TECH_REQ; - else if (slice_matches_str (&token, "yields")) - def->generated_resource_flags |= MF_YIELDS; - else if (resource_name.str == NULL) - resource_name = token; - else { - ok = false; - break; - } - } + memset (cfg, 0, sizeof *cfg); +} + +void +free_dynamic_wonder_config (struct wonder_district_config * cfg) +{ + if (cfg == NULL) + return; + + if (! cfg->is_dynamic) + return; + + if (cfg->wonder_name != NULL) { + free ((void *)cfg->wonder_name); + cfg->wonder_name = NULL; + } + + if (cfg->img_path != NULL) { + free ((void *)cfg->img_path); + cfg->img_path = NULL; + } + + memset (cfg, 0, sizeof *cfg); +} + +void +free_dynamic_natural_wonder_config (struct natural_wonder_district_config * cfg) +{ + if (cfg == NULL) + return; + + if (! cfg->is_dynamic) + return; - if (! ok || (resource_name.str == NULL)) { - def->generated_resource = NULL; - def->has_generated_resource = false; - def->generated_resource_flags = 0; - add_key_parse_error (parse_errors, line_number, key, value, - "(expected resource name plus optional flags: local, yields, no-tech-req)"); - } else { - def->generated_resource = extract_slice (&resource_name); - def->has_generated_resource = true; - } - } - free (value_text); + if (cfg->name != NULL) { + free ((void *)cfg->name); + cfg->name = NULL; + } - } else - add_unrecognized_key_error (unrecognized_keys, line_number, key); + if (cfg->img_path != NULL) { + free ((void *)cfg->img_path); + cfg->img_path = NULL; + } + + memset (cfg, 0, sizeof *cfg); + cfg->adjacent_to = (enum SquareTypes)SQ_INVALID; + cfg->adjacency_dir = DIR_ZERO; } -bool -line_is_empty_or_comment (struct string_slice const * trimmed) +enum Unit_Command_Values +allocate_dynamic_district_command (char const * name) { - return ((trimmed == NULL) || (trimmed->len == 0) || (trimmed->str[0] == ';')); + int offset = is->next_custom_dynamic_command_index; + is->next_custom_dynamic_command_index += 1; + int value = C3X_DISTRICT_COMMAND_BASE - (offset + 1); + return (enum Unit_Command_Values)value; } void -load_dynamic_district_config_file (char const * file_path, - int path_is_relative_to_mod_dir, - int log_missing, - int drop_existing_configs) +free_special_district_override_strings (struct district_config * cfg, struct district_config const * defaults) { - char path[MAX_PATH]; - if (path_is_relative_to_mod_dir) { - if (is->mod_rel_dir == NULL) - return; - snprintf (path, sizeof path, "%s\\%s", is->mod_rel_dir, file_path); - } else { - strncpy (path, file_path, sizeof path); - } - path[(sizeof path) - 1] = '\0'; + if (cfg == NULL || defaults == NULL) + return; - char * text = file_to_string (path); - if (text == NULL) { - if (log_missing) { - char ss[256]; - snprintf (ss, sizeof ss, "[C3X] Districts config file not found: %s", path); - (*p_OutputDebugStringA) (ss); + if ((cfg->display_name != NULL) && + (cfg->display_name != cfg->name) && + (cfg->display_name != defaults->display_name)) { + free ((void *)cfg->display_name); + cfg->display_name = NULL; + } + if ((cfg->tooltip != NULL) && (cfg->tooltip != defaults->tooltip)) { + free ((void *)cfg->tooltip); + cfg->tooltip = NULL; + } + for (int i = 0; i < ARRAY_LEN (cfg->advance_prereqs); i++) { + char const * default_value = (defaults != NULL && i < defaults->advance_prereq_count) + ? defaults->advance_prereqs[i] + : NULL; + if ((cfg->advance_prereqs[i] != NULL) && + (cfg->advance_prereqs[i] != default_value)) { + free ((void *)cfg->advance_prereqs[i]); } - return; + cfg->advance_prereqs[i] = NULL; + } + cfg->advance_prereq_count = 0; + if ((cfg->obsoleted_by != NULL) && (cfg->obsoleted_by != defaults->obsoleted_by)) { + free ((void *)cfg->obsoleted_by); + cfg->obsoleted_by = NULL; } - if (drop_existing_configs) - reset_regular_district_configs (); - - struct parsed_district_definition def; - init_parsed_district_definition (&def); - bool in_section = false; - int section_start_line = 0; - int line_number = 0; - struct error_line * unrecognized_keys = NULL; - struct error_line * parse_errors = NULL; + for (int i = 0; i < ARRAY_LEN (cfg->resource_prereqs); i++) { + char const * default_value = (i < defaults->resource_prereq_count) ? defaults->resource_prereqs[i] : NULL; + if ((cfg->resource_prereqs[i] != NULL) && + (cfg->resource_prereqs[i] != default_value)) + free ((void *)cfg->resource_prereqs[i]); + cfg->resource_prereqs[i] = NULL; + } + cfg->resource_prereq_count = defaults->resource_prereq_count; - char * cursor = text; - while (*cursor != '\0') { - line_number += 1; + if ((cfg->resource_prereq_on_tile != NULL) && (cfg->resource_prereq_on_tile != defaults->resource_prereq_on_tile)) { + free ((void *)cfg->resource_prereq_on_tile); + cfg->resource_prereq_on_tile = NULL; + } - char * line_start = cursor; - char * line_end = cursor; - while ((*line_end != '\0') && (*line_end != '\n')) - line_end++; + for (int i = 0; i < ARRAY_LEN (cfg->wonder_prereqs); i++) { + char const * default_value = (i < defaults->wonder_prereq_count) ? defaults->wonder_prereqs[i] : NULL; + if ((cfg->wonder_prereqs[i] != NULL) && + (cfg->wonder_prereqs[i] != default_value)) + free ((void *)cfg->wonder_prereqs[i]); + cfg->wonder_prereqs[i] = NULL; + } + cfg->wonder_prereq_count = defaults->wonder_prereq_count; - int line_len = line_end - line_start; - bool has_newline = (*line_end == '\n'); - if (has_newline) - *line_end = '\0'; + for (int i = 0; i < ARRAY_LEN (cfg->natural_wonder_prereqs); i++) { + char const * default_value = (i < defaults->natural_wonder_prereq_count) ? defaults->natural_wonder_prereqs[i] : NULL; + if ((cfg->natural_wonder_prereqs[i] != NULL) && + (cfg->natural_wonder_prereqs[i] != default_value)) + free ((void *)cfg->natural_wonder_prereqs[i]); + cfg->natural_wonder_prereqs[i] = NULL; + } + cfg->natural_wonder_prereq_count = defaults->natural_wonder_prereq_count; - struct string_slice line_slice = { .str = line_start, .len = line_len }; - struct string_slice trimmed = trim_string_slice (&line_slice, 0); - if (line_is_empty_or_comment (&trimmed)) { - cursor = has_newline ? line_end + 1 : line_end; - continue; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_on_districts); i++) { + char const * default_value = (i < defaults->buildable_on_district_count) ? defaults->buildable_on_districts[i] : NULL; + if ((cfg->buildable_on_districts[i] != NULL) && + (cfg->buildable_on_districts[i] != default_value)) { + free ((void *)cfg->buildable_on_districts[i]); } + cfg->buildable_on_districts[i] = NULL; + } + cfg->buildable_on_district_count = defaults->buildable_on_district_count; + cfg->buildable_on_district_id_count = defaults->buildable_on_district_id_count; + cfg->has_buildable_on_districts = defaults->has_buildable_on_districts; - if (trimmed.str[0] == '#') { - struct string_slice directive = trimmed; - directive.str += 1; - directive.len -= 1; - directive = trim_string_slice (&directive, 0); - if ((directive.len > 0) && slice_matches_str (&directive, "District")) { - if (in_section) - finalize_parsed_district_definition (&def, section_start_line); - in_section = true; - section_start_line = line_number; - } - cursor = has_newline ? line_end + 1 : line_end; - continue; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { + char const * default_value = (i < defaults->buildable_by_civ_count) ? defaults->buildable_by_civs[i] : NULL; + if ((cfg->buildable_by_civs[i] != NULL) && + (cfg->buildable_by_civs[i] != default_value)) { + free ((void *)cfg->buildable_by_civs[i]); } + cfg->buildable_by_civs[i] = NULL; + } + cfg->buildable_by_civ_count = defaults->buildable_by_civ_count; + cfg->has_buildable_by_civs = defaults->has_buildable_by_civs; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_traits_ids); i++) + cfg->buildable_by_civ_traits_ids[i] = -1; + cfg->buildable_by_civ_traits_id_count = defaults->buildable_by_civ_traits_id_count; + cfg->has_buildable_by_civ_traits = defaults->has_buildable_by_civ_traits; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_govs_ids); i++) + cfg->buildable_by_civ_govs_ids[i] = -1; + cfg->buildable_by_civ_govs_id_count = defaults->buildable_by_civ_govs_id_count; + cfg->has_buildable_by_civ_govs = defaults->has_buildable_by_civ_govs; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civ_cultures_ids); i++) + cfg->buildable_by_civ_cultures_ids[i] = -1; + cfg->buildable_by_civ_cultures_id_count = defaults->buildable_by_civ_cultures_id_count; + cfg->has_buildable_by_civ_cultures = defaults->has_buildable_by_civ_cultures; + cfg->buildable_by_war_allies = defaults->buildable_by_war_allies; + cfg->buildable_by_pact_allies = defaults->buildable_by_pact_allies; - if (! in_section) { - cursor = has_newline ? line_end + 1 : line_end; - continue; + for (int i = 0; i < ARRAY_LEN (cfg->dependent_improvements); i++) { + char const * default_value = (i < defaults->dependent_improvement_count) ? defaults->dependent_improvements[i] : NULL; + if ((cfg->dependent_improvements[i] != NULL) && + (cfg->dependent_improvements[i] != default_value)) { + free ((void *)cfg->dependent_improvements[i]); } + cfg->dependent_improvements[i] = NULL; + } + cfg->dependent_improvement_count = defaults->dependent_improvement_count; - struct string_slice key_slice = {0}; - struct string_slice value_slice = {0}; - enum key_value_parse_status status = parse_trimmed_key_value (&trimmed, &key_slice, &value_slice); - if (status == KVP_NO_EQUALS) { - char * line_text = extract_slice (&trimmed); - struct error_line * err = add_error_line (&parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: %s (expected '=')", line_number, line_text); - err->text[(sizeof err->text) - 1] = '\0'; - free (line_text); - cursor = has_newline ? line_end + 1 : line_end; - continue; - } else if (status == KVP_EMPTY_KEY) { - struct error_line * err = add_error_line (&parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: (missing key)", line_number); - err->text[(sizeof err->text) - 1] = '\0'; - cursor = has_newline ? line_end + 1 : line_end; - continue; + for (int i = 0; i < ARRAY_LEN (cfg->img_paths); i++) { + char const * default_value = (i < defaults->img_path_count) ? defaults->img_paths[i] : NULL; + if ((cfg->img_paths[i] != NULL) && + (cfg->img_paths[i] != default_value)) { + free ((void *)cfg->img_paths[i]); } + cfg->img_paths[i] = NULL; + } + cfg->img_path_count = defaults->img_path_count; - handle_district_definition_key (&def, &key_slice, &value_slice, line_number, &parse_errors, &unrecognized_keys); - cursor = has_newline ? line_end + 1 : line_end; + free_bonus_entry_list_override (&cfg->culture_bonus_extras, &defaults->culture_bonus_extras); + free_bonus_entry_list_override (&cfg->science_bonus_extras, &defaults->science_bonus_extras); + free_bonus_entry_list_override (&cfg->food_bonus_extras, &defaults->food_bonus_extras); + free_bonus_entry_list_override (&cfg->gold_bonus_extras, &defaults->gold_bonus_extras); + free_bonus_entry_list_override (&cfg->shield_bonus_extras, &defaults->shield_bonus_extras); + free_bonus_entry_list_override (&cfg->happiness_bonus_extras, &defaults->happiness_bonus_extras); + free_bonus_entry_list_override (&cfg->defense_bonus_extras, &defaults->defense_bonus_extras); +} + +void +reset_regular_district_configs (void) +{ + for (int i = USED_SPECIAL_DISTRICT_TYPES; i < COUNT_DISTRICT_TYPES; i++) { + if (is->district_configs[i].is_dynamic) + free_dynamic_district_config (&is->district_configs[i]); } - if (in_section) - finalize_parsed_district_definition (&def, section_start_line); + for (int i = 0; i < USED_SPECIAL_DISTRICT_TYPES; i++) + free_special_district_override_strings (&is->district_configs[i], &special_district_defaults[i]); - free_parsed_district_definition (&def); - free (text); + memset (is->district_configs, 0, sizeof is->district_configs); + for (int i = 0; i < USED_SPECIAL_DISTRICT_TYPES; i++) + is->district_configs[i] = special_district_defaults[i]; - // Append to loaded config names list - struct loaded_config_name * top_lcn = is->loaded_config_names; - while (top_lcn->next != NULL) - top_lcn = top_lcn->next; + is->special_district_count = USED_SPECIAL_DISTRICT_TYPES; + is->dynamic_district_count = 0; + is->district_count = is->special_district_count; + is->next_custom_dynamic_command_index = 0; +} + +void +reset_wonder_district_configs (void) +{ + for (int i = 0; i < MAX_WONDER_DISTRICT_TYPES; i++) { + if (is->wonder_district_configs[i].is_dynamic) + free_dynamic_wonder_config (&is->wonder_district_configs[i]); + } - struct loaded_config_name * new_lcn = malloc (sizeof *new_lcn); - new_lcn->name = strdup (path); - new_lcn->next = NULL; + memset (is->wonder_district_configs, 0, sizeof is->wonder_district_configs); + is->wonder_district_count = 0; +} - top_lcn->next = new_lcn; - snprintf (is->current_districts_config_path, sizeof is->current_districts_config_path, path); +void +reset_natural_wonder_configs (void) +{ + for (int i = 0; i < MAX_NATURAL_WONDER_DISTRICT_TYPES; i++) { + if (is->natural_wonder_configs[i].is_dynamic) + free_dynamic_natural_wonder_config (&is->natural_wonder_configs[i]); + } - if (parse_errors != NULL || unrecognized_keys != NULL) { - PopupForm * popup = get_popup_form (); - popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_WARNING", -1, 0, 0, 0); - char s[200]; - snprintf (s, sizeof s, "District Config errors in %s:", path); - s[(sizeof s) - 1] = '\0'; - PopupForm_add_text (popup, __, s, false); - if (parse_errors != NULL) { - for (struct error_line * line = parse_errors; line != NULL; line = line->next) - PopupForm_add_text (popup, __, line->text, false); - } - if (unrecognized_keys != NULL) { - PopupForm_add_text (popup, __, "", false); - PopupForm_add_text (popup, __, "Unrecognized keys:", false); - for (struct error_line * line = unrecognized_keys; line != NULL; line = line->next) - PopupForm_add_text (popup, __, line->text, false); - } - patch_show_popup (popup, __, 0, 0); - free_error_lines (parse_errors); - free_error_lines (unrecognized_keys); + memset (is->natural_wonder_configs, 0, sizeof is->natural_wonder_configs); + for (int i = 0; i < MAX_NATURAL_WONDER_DISTRICT_TYPES; i++) { + is->natural_wonder_configs[i].adjacent_to = (enum SquareTypes)SQ_INVALID; + is->natural_wonder_configs[i].adjacency_dir = DIR_ZERO; } + for (int i = 0; i < MAX_NATURAL_WONDER_DISTRICT_TYPES; i++) + is->natural_wonder_img_sets[i].img.vtable = NULL; + stable_deinit (&is->natural_wonder_name_to_id); + is->natural_wonder_count = 0; } void -load_dynamic_district_configs () +clear_dynamic_district_definitions (void) { - load_dynamic_district_config_file ("default.districts_config.txt", 1, 1, 1); - load_dynamic_district_config_file ("user.districts_config.txt", 1, 0, 1); - - - char * scenario_filename = "scenario.districts_config.txt"; - char * scenario_district_config_path = BIC_get_asset_path (p_bic_data, __, scenario_filename, false); - if ((scenario_district_config_path != NULL) && (0 != strcmp (scenario_filename, scenario_district_config_path))) - load_dynamic_district_config_file (scenario_district_config_path, 0, 0, 1); + reset_regular_district_configs (); + reset_wonder_district_configs (); + reset_natural_wonder_configs (); + reset_ai_candidate_bridge_or_canals (); } void -init_parsed_wonder_definition (struct parsed_wonder_definition * def) +init_parsed_district_definition (struct parsed_district_definition * def) { memset (def, 0, sizeof *def); + def->img_path_count = -1; + def->defense_bonus_percent = 0; + def->render_strategy = DRS_BY_COUNT; + def->ai_build_strategy = DABS_DISTRICT; def->buildable_square_types_mask = district_default_buildable_mask (); } void -free_parsed_wonder_definition (struct parsed_wonder_definition * def) +free_parsed_district_definition (struct parsed_district_definition * def) { + if (def == NULL) + return; + + if (def->display_name != NULL) { + free (def->display_name); + def->display_name = NULL; + } if (def->name != NULL) { free (def->name); def->name = NULL; } + if (def->tooltip != NULL) { + free (def->tooltip); + def->tooltip = NULL; + } + for (int i = 0; i < def->advance_prereq_count; i++) { + if (def->advance_prereqs[i] != NULL) { + free (def->advance_prereqs[i]); + def->advance_prereqs[i] = NULL; + } + } + def->advance_prereq_count = 0; + for (int i = 0; i < ARRAY_LEN (def->buildable_on_districts); i++) { + if (def->buildable_on_districts[i] != NULL) { + free (def->buildable_on_districts[i]); + def->buildable_on_districts[i] = NULL; + } + } + if (def->obsoleted_by != NULL) { + free (def->obsoleted_by); + def->obsoleted_by = NULL; + } - if (def->img_path != NULL) { - free (def->img_path); - def->img_path = NULL; + for (int i = 0; i < def->resource_prereq_count; i++) { + if (def->resource_prereqs[i] != NULL) { + free (def->resource_prereqs[i]); + def->resource_prereqs[i] = NULL; + } } + def->resource_prereq_count = 0; - init_parsed_wonder_definition (def); + if (def->resource_prereq_on_tile != NULL) { + free (def->resource_prereq_on_tile); + def->resource_prereq_on_tile = NULL; + } + + for (int i = 0; i < def->wonder_prereq_count; i++) { + if (def->wonder_prereqs[i] != NULL) { + free (def->wonder_prereqs[i]); + def->wonder_prereqs[i] = NULL; + } + } + def->wonder_prereq_count = 0; + + for (int i = 0; i < def->natural_wonder_prereq_count; i++) { + if (def->natural_wonder_prereqs[i] != NULL) { + free (def->natural_wonder_prereqs[i]); + def->natural_wonder_prereqs[i] = NULL; + } + } + def->natural_wonder_prereq_count = 0; + + for (int i = 0; i < def->buildable_by_civ_count; i++) { + if (def->buildable_by_civs[i] != NULL) { + free (def->buildable_by_civs[i]); + def->buildable_by_civs[i] = NULL; + } + } + def->buildable_by_civ_count = 0; + for (int i = 0; i < def->buildable_by_civ_traits_count; i++) { + if (def->buildable_by_civ_traits[i] != NULL) { + free (def->buildable_by_civ_traits[i]); + def->buildable_by_civ_traits[i] = NULL; + } + } + def->buildable_by_civ_traits_count = 0; + def->buildable_by_civ_traits_id_count = 0; + for (int i = 0; i < def->buildable_by_civ_govs_count; i++) { + if (def->buildable_by_civ_govs[i] != NULL) { + free (def->buildable_by_civ_govs[i]); + def->buildable_by_civ_govs[i] = NULL; + } + } + def->buildable_by_civ_govs_count = 0; + def->buildable_by_civ_govs_id_count = 0; + for (int i = 0; i < def->buildable_by_civ_cultures_count; i++) { + if (def->buildable_by_civ_cultures[i] != NULL) { + free (def->buildable_by_civ_cultures[i]); + def->buildable_by_civ_cultures[i] = NULL; + } + } + def->buildable_by_civ_cultures_count = 0; + def->buildable_by_civ_cultures_id_count = 0; + + for (int i = 0; i < def->dependent_improvement_count; i++) { + if (def->dependent_improvements[i] != NULL) { + free (def->dependent_improvements[i]); + def->dependent_improvements[i] = NULL; + } + } + def->dependent_improvement_count = 0; + + for (int i = 0; i < def->img_path_count; i++) { + if (def->img_paths[i] != NULL) { + free (def->img_paths[i]); + def->img_paths[i] = NULL; + } + } + def->img_path_count = 0; + + if (def->generated_resource != NULL) { + free (def->generated_resource); + def->generated_resource = NULL; + } + + free_bonus_entry_list (&def->culture_bonus_extras); + free_bonus_entry_list (&def->science_bonus_extras); + free_bonus_entry_list (&def->food_bonus_extras); + free_bonus_entry_list (&def->gold_bonus_extras); + free_bonus_entry_list (&def->shield_bonus_extras); + free_bonus_entry_list (&def->happiness_bonus_extras); + free_bonus_entry_list (&def->defense_bonus_extras); + + init_parsed_district_definition (def); } -bool -add_dynamic_wonder_from_definition (struct parsed_wonder_definition * def, int section_start_line) +int +find_special_district_index_by_name (char const * name) { - int existing_index = -1; - for (int i = 0; i < is->wonder_district_count; i++) { - if ((is->wonder_district_configs[i].wonder_name != NULL) && - (strcmp (is->wonder_district_configs[i].wonder_name, def->name) == 0)) { - existing_index = i; - break; - } + if (name == NULL) + return -1; + + for (int i = 0; i < is->special_district_count; i++) { + if ((is->district_configs[i].name != NULL) && + (strcmp (is->district_configs[i].name, name) == 0)) + return i; } + return -1; +} - int dest = (existing_index >= 0) ? existing_index : is->wonder_district_count; - if ((dest < 0) || (dest >= MAX_WONDER_DISTRICT_TYPES)) - return false; +bool +ensure_culture_variant_art (struct district_config * cfg, int section_start_line) +{ + if ((cfg == NULL) || (! cfg->vary_img_by_culture)) + return true; - struct wonder_district_config new_cfg; - memset (&new_cfg, 0, sizeof new_cfg); - new_cfg.index = dest; - new_cfg.is_dynamic = true; - new_cfg.wonder_name = strdup (def->name); - new_cfg.img_path = (def->img_path != NULL) ? strdup (def->img_path) : strdup ("Wonders.pcx"); - new_cfg.img_row = def->img_row; - new_cfg.img_column = def->img_column; - new_cfg.img_construct_row = def->img_construct_row; - new_cfg.img_construct_column = def->img_construct_column; - new_cfg.img_alt_dir_construct_row = def->img_alt_dir_construct_row; - new_cfg.img_alt_dir_construct_column = def->img_alt_dir_construct_column; - new_cfg.img_alt_dir_row = def->img_alt_dir_row; - new_cfg.img_alt_dir_column = def->img_alt_dir_column; - new_cfg.enable_img_alt_dir = def->enable_img_alt_dir; - new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); + const int required_variants = 5; + const int max_img_paths = ARRAY_LEN (is->district_configs[0].img_paths); + if (cfg->img_path_count <= 0) { + char ss[256]; + snprintf (ss, sizeof ss, "[C3X] load_dynamic_district_configs: district \"%s\" requires culture-specific art but none provided (line %d)\n", cfg->name, section_start_line); + (*p_OutputDebugStringA) (ss); + return false; + } - if (existing_index >= 0) { - struct wonder_district_config * cfg = &is->wonder_district_configs[existing_index]; - free_dynamic_wonder_config (cfg); - *cfg = new_cfg; - cfg->index = existing_index; - } else { - struct wonder_district_config * cfg = &is->wonder_district_configs[dest]; - free_dynamic_wonder_config (cfg); - *cfg = new_cfg; - is->wonder_district_count += 1; + while ((cfg->img_path_count < required_variants) && + (cfg->img_path_count < max_img_paths)) { + cfg->img_paths[cfg->img_path_count] = strdup (cfg->img_paths[0]); + cfg->img_path_count += 1; } return true; } -void -finalize_parsed_wonder_definition (struct parsed_wonder_definition * def, - int section_start_line, - struct error_line ** parse_errors) +bool +parse_config_string_list (char * value_text, + char ** dest, + int capacity, + int * out_count, + struct error_line ** parse_errors, + int line_number, + char const * key) { - bool ok = true; - - if ((! def->has_name) || (def->name == NULL)) { - ok = false; - if (parse_errors != NULL) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: name (value is required)", section_start_line); - err->text[(sizeof err->text) - 1] = '\0'; - } - } - if (! def->has_img_row) { - ok = false; - if (parse_errors != NULL) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: img_row (value is required)", section_start_line); - err->text[(sizeof err->text) - 1] = '\0'; - } - } - if (! def->has_img_column) { - ok = false; - if (parse_errors != NULL) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: img_column (value is required)", section_start_line); - err->text[(sizeof err->text) - 1] = '\0'; - } - } - if (! def->has_img_construct_row) { - ok = false; - if (parse_errors != NULL) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: img_construct_row (value is required)", section_start_line); - err->text[(sizeof err->text) - 1] = '\0'; - } - } - if (! def->has_img_construct_column) { - ok = false; - if (parse_errors != NULL) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: img_construct_column (value is required)", section_start_line); - err->text[(sizeof err->text) - 1] = '\0'; - } - } - if (def->enable_img_alt_dir) { - if (! def->has_img_alt_dir_row) { - ok = false; - if (parse_errors != NULL) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: img_alt_dir_row (value is required when enable_img_alt_dir is set)", section_start_line); - err->text[(sizeof err->text) - 1] = '\0'; - } - } - if (! def->has_img_alt_dir_column) { - ok = false; - if (parse_errors != NULL) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: img_alt_dir_column (value is required when enable_img_alt_dir is set)", section_start_line); - err->text[(sizeof err->text) - 1] = '\0'; - } - } - if (! def->has_img_alt_dir_construct_row) { - ok = false; - if (parse_errors != NULL) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: img_alt_dir_construct_row (value is required when enable_img_alt_dir is set)", section_start_line); - err->text[(sizeof err->text) - 1] = '\0'; - } - } - if (! def->has_img_alt_dir_construct_column) { - ok = false; - if (parse_errors != NULL) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: img_alt_dir_construct_column (value is required when enable_img_alt_dir is set)", section_start_line); - err->text[(sizeof err->text) - 1] = '\0'; - } + for (int i = 0; i < capacity; i++) { + if (dest[i] != NULL) { + free (dest[i]); + dest[i] = NULL; } } + *out_count = 0; - if (ok) - add_dynamic_wonder_from_definition (def, section_start_line); + if (value_text == NULL || *value_text == '\0') + return true; - free_parsed_wonder_definition (def); -} + char * cursor = value_text; + while (1) { + while (is_space_char (*cursor)) + cursor++; -void -handle_wonder_definition_key (struct parsed_wonder_definition * def, - struct string_slice const * key, - struct string_slice const * value, - int line_number, - struct error_line ** parse_errors, - struct error_line ** unrecognized_keys) -{ - if (slice_matches_str (key, "name")) { - if (def->name != NULL) { - free (def->name); - def->name = NULL; - } + if (*cursor == '\0') + break; - struct string_slice unquoted = trim_string_slice (value, 1); - if (unquoted.len == 0) { - def->has_name = false; - add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); - } else { - char * name_copy = extract_slice (&unquoted); - if (name_copy == NULL) { - def->has_name = false; - add_key_parse_error (parse_errors, line_number, key, value, "(out of memory)"); - } else { - def->name = name_copy; - def->has_name = true; + char * item_start; + char * item_end; + if (*cursor == '"') { + cursor++; + item_start = cursor; + while ((*cursor != '\0') && (*cursor != '"')) + cursor++; + if (*cursor != '"') { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %s (missing closing quote)", line_number, key); + err->text[(sizeof err->text) - 1] = '\0'; + for (int j = 0; j < capacity; j++) { + if (dest[j] != NULL) { + free (dest[j]); + dest[j] = NULL; + } + } + *out_count = 0; + return false; } + item_end = cursor; + cursor++; + } else { + item_start = cursor; + while ((*cursor != '\0') && (*cursor != ',')) + cursor++; + item_end = cursor; } - } else if (slice_matches_str (key, "img_path")) { - if (def->img_path != NULL) { - free (def->img_path); - def->img_path = NULL; - } + while ((item_end > item_start) && is_space_char (item_end[-1])) + item_end--; - struct string_slice unquoted = trim_string_slice (value, 1); - if (unquoted.len == 0) { - def->has_img_path = false; - } else { - char * path_copy = extract_slice (&unquoted); - if (path_copy == NULL) { - def->has_img_path = false; - } else { - def->img_path = path_copy; - def->has_img_path = true; + int item_len = item_end - item_start; + if (item_len > 0) { + if (*out_count < capacity) { + char * copy = malloc (item_len + 1); + if (copy == NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %s (out of memory)", line_number, key); + err->text[(sizeof err->text) - 1] = '\0'; + for (int j = 0; j < capacity; j++) { + if (dest[j] != NULL) { + free (dest[j]); + dest[j] = NULL; + } + } + *out_count = 0; + return false; + } + memcpy (copy, item_start, item_len); + copy[item_len] = '\0'; + dest[*out_count] = copy; + *out_count += 1; } } - } else if (slice_matches_str (key, "img_row")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->img_row = ival; - def->has_img_row = true; - } else { - def->has_img_row = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - } + while (is_space_char (*cursor)) + cursor++; - } else if (slice_matches_str (key, "img_column")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->img_column = ival; - def->has_img_column = true; - } else { - def->has_img_column = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + if (*cursor == ',') { + cursor++; + continue; } + if (*cursor == '\0') + break; + } - } else if (slice_matches_str (key, "img_construct_row")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->img_construct_row = ival; - def->has_img_construct_row = true; - } else { - def->has_img_construct_row = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - } + return true; +} - } else if (slice_matches_str (key, "img_construct_column")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->img_construct_column = ival; - def->has_img_construct_column = true; - } else { - def->has_img_construct_column = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - } +bool +parse_buildable_square_type_mask (struct string_slice const * value, + unsigned int * out_mask, + struct error_line ** parse_errors, + int line_number) +{ + char * value_text = trim_and_extract_slice (value, 0); + unsigned int mask = 0; + int entry_count = 0; + + if (value_text != NULL) { + char * cursor = value_text; + while (1) { + while (is_space_char (*cursor)) + cursor++; - } else if (slice_matches_str (key, "img_alt_dir_construct_row")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->img_alt_dir_construct_row = ival; - def->has_img_alt_dir_construct_row = true; - } else { - def->has_img_alt_dir_construct_row = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - } + char * item_start = cursor; + while ((*cursor != '\0') && (*cursor != ',')) + cursor++; - } else if (slice_matches_str (key, "img_alt_dir_construct_column")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->img_alt_dir_construct_column = ival; - def->has_img_alt_dir_construct_column = true; - } else { - def->has_img_alt_dir_construct_column = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - } + char * item_end = cursor; + while ((item_end > item_start) && is_space_char (item_end[-1])) + item_end--; - } else if (slice_matches_str (key, "img_alt_dir_row")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->img_alt_dir_row = ival; - def->has_img_alt_dir_row = true; - } else { - def->has_img_alt_dir_row = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - } + struct string_slice item_slice = { .str = item_start, .len = (int)(item_end - item_start) }; + if (item_slice.len > 0) { + if (slice_matches_str (&item_slice, "mine")) { + mask |= district_buildable_mine_mask_bit (); + entry_count += 1; + } else if (slice_matches_str (&item_slice, "irrigation")) { + mask |= district_buildable_irrigation_mask_bit (); + entry_count += 1; + } else { + enum SquareTypes parsed; + if (read_square_type_value (&item_slice, &parsed)) { + if (parsed == (enum SquareTypes)SQ_INVALID) + mask = all_square_types_mask (); + else + mask |= square_type_mask_bit (parsed); + entry_count += 1; + } else { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %.*s (invalid buildable_on entry)", line_number, item_slice.len, item_slice.str); + err->text[(sizeof err->text) - 1] = '\0'; + free (value_text); + return false; + } + } + } - } else if (slice_matches_str (key, "img_alt_dir_column")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->img_alt_dir_column = ival; - def->has_img_alt_dir_column = true; - } else { - def->has_img_alt_dir_column = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + if (*cursor == ',') { + cursor++; + continue; + } + break; } + } - } else if (slice_matches_str (key, "enable_img_alt_dir")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->enable_img_alt_dir = (ival != 0); - def->has_enable_img_alt_dir = true; - } else { - def->has_enable_img_alt_dir = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - } + if (value_text != NULL) + free (value_text); - } else if (slice_matches_str (key, "buildable_on")) { - unsigned int mask; - if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number)) { - def->buildable_square_types_mask = mask; - def->has_buildable_on = true; - } else { - def->has_buildable_on = false; - } + if ((entry_count == 0) || (mask == 0)) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: buildable_on (expected at least one square type)", line_number); + err->text[(sizeof err->text) - 1] = '\0'; + return false; + } - } else - add_unrecognized_key_error (unrecognized_keys, line_number, key); + *out_mask = mask; + return true; } -void -load_dynamic_wonder_config_file (char const * file_path, - int path_is_relative_to_mod_dir, - int log_missing, - int drop_existing_configs) +bool +parse_district_bonus_entries (struct string_slice const * value, + int * out_base_bonus, + struct district_bonus_list * out_extras, + struct error_line ** parse_errors, + int line_number, + struct string_slice const * key) { - char path[MAX_PATH]; - if (path_is_relative_to_mod_dir) { - if (is->mod_rel_dir == NULL) - return; - snprintf (path, sizeof path, "%s\\%s", is->mod_rel_dir, file_path); - } else { - strncpy (path, file_path, sizeof path); - } - path[(sizeof path) - 1] = '\0'; - - char * text = file_to_string (path); - if (text == NULL) { - if (log_missing) { - char ss[256]; - snprintf (ss, sizeof ss, "[C3X] Wonders config file not found: %s", path); - (*p_OutputDebugStringA) (ss); - } - return; + if ((out_base_bonus == NULL) || (out_extras == NULL)) { + add_key_parse_error (parse_errors, line_number, key, value, "(invalid bonus target)"); + return false; } - if (drop_existing_configs) - reset_wonder_district_configs (); + char * value_text = trim_and_extract_slice (value, 0); + free_bonus_entry_list (out_extras); + *out_base_bonus = 0; - struct parsed_wonder_definition def; - init_parsed_wonder_definition (&def); - bool in_section = false; - int section_start_line = 0; - int line_number = 0; - struct error_line * unrecognized_keys = NULL; - struct error_line * parse_errors = NULL; + if ((value_text == NULL) || (*value_text == '\0')) { + add_key_parse_error (parse_errors, line_number, key, value, "(expected base bonus)"); + free (value_text); + return false; + } - char * cursor = text; - while (*cursor != '\0') { - line_number += 1; + bool got_base = false; + int base_bonus = 0; - char * line_start = cursor; - char * line_end = cursor; - while ((*line_end != '\0') && (*line_end != '\n')) - line_end++; + char * cursor = value_text; + while (1) { + while (is_space_char (*cursor)) + cursor++; - int line_len = line_end - line_start; - bool has_newline = (*line_end == '\n'); - if (has_newline) - *line_end = '\0'; + if (*cursor == '\0') + break; - struct string_slice line_slice = { .str = line_start, .len = line_len }; - struct string_slice trimmed = trim_string_slice (&line_slice, 0); - if (line_is_empty_or_comment (&trimmed)) { - cursor = has_newline ? line_end + 1 : line_end; - continue; + char * item_start = cursor; + bool in_quotes = false; + while (*cursor != '\0') { + if (*cursor == '"') + in_quotes = ! in_quotes; + if ((! in_quotes) && (*cursor == ',')) + break; + cursor++; } - if (trimmed.str[0] == '#') { - struct string_slice directive = trimmed; - directive.str += 1; - directive.len -= 1; - directive = trim_string_slice (&directive, 0); - if ((directive.len > 0) && slice_matches_str (&directive, "Wonder")) { - if (in_section) - finalize_parsed_wonder_definition (&def, section_start_line, &parse_errors); - in_section = true; - section_start_line = line_number; - } - cursor = has_newline ? line_end + 1 : line_end; - continue; - } + char * item_end = cursor; + while ((item_end > item_start) && is_space_char (item_end[-1])) + item_end--; + while ((item_start < item_end) && is_space_char (*item_start)) + item_start++; - if (! in_section) { - cursor = has_newline ? line_end + 1 : line_end; - continue; - } + if (item_end > item_start) { + struct string_slice item = { .str = item_start, .len = (int)(item_end - item_start) }; - struct string_slice key_slice = {0}; - struct string_slice value_slice = {0}; - enum key_value_parse_status status = parse_trimmed_key_value (&trimmed, &key_slice, &value_slice); - if (status == KVP_NO_EQUALS) { - char * line_text = extract_slice (&trimmed); - struct error_line * err = add_error_line (&parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: %s (expected '=')", line_number, line_text); - err->text[(sizeof err->text) - 1] = '\0'; - free (line_text); - cursor = has_newline ? line_end + 1 : line_end; - continue; - } else if (status == KVP_EMPTY_KEY) { - struct error_line * err = add_error_line (&parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: (missing key)", line_number); - err->text[(sizeof err->text) - 1] = '\0'; - cursor = has_newline ? line_end + 1 : line_end; - continue; - } + if (! got_base) { + struct string_slice base_slice = trim_string_slice (&item, 0); + if (! read_int (&base_slice, &base_bonus)) { + add_key_parse_error (parse_errors, line_number, key, value, "(expected base bonus)"); + free_bonus_entry_list (out_extras); + free (value_text); + return false; + } + got_base = true; + } else { + char * colon = NULL; + in_quotes = false; + for (char * p = item_start; p < item_end; p++) { + if (*p == '"') + in_quotes = ! in_quotes; + if ((! in_quotes) && (*p == ':')) { + colon = p; + break; + } + } - handle_wonder_definition_key (&def, &key_slice, &value_slice, line_number, &parse_errors, &unrecognized_keys); - cursor = has_newline ? line_end + 1 : line_end; - } + if (colon == NULL) { + add_key_parse_error (parse_errors, line_number, key, value, "(expected \"name: bonus\" entry)"); + free_bonus_entry_list (out_extras); + free (value_text); + return false; + } - if (in_section) - finalize_parsed_wonder_definition (&def, section_start_line, &parse_errors); + struct string_slice name_slice = { .str = item_start, .len = (int)(colon - item_start) }; + struct string_slice bonus_slice = { .str = colon + 1, .len = (int)(item_end - (colon + 1)) }; + struct string_slice trimmed_name = trim_string_slice (&name_slice, 1); + struct string_slice trimmed_bonus = trim_string_slice (&bonus_slice, 0); - free_parsed_wonder_definition (&def); - free (text); + if (trimmed_name.len <= 0) { + add_key_parse_error (parse_errors, line_number, key, value, "(expected bonus name)"); + free_bonus_entry_list (out_extras); + free (value_text); + return false; + } - // Append to loaded config names list - struct loaded_config_name * top_lcn = is->loaded_config_names; - while (top_lcn->next != NULL) - top_lcn = top_lcn->next; + int bonus_value = 0; + if (! read_int (&trimmed_bonus, &bonus_value)) { + add_key_parse_error (parse_errors, line_number, key, value, "(expected bonus value)"); + free_bonus_entry_list (out_extras); + free (value_text); + return false; + } - struct loaded_config_name * new_lcn = malloc (sizeof *new_lcn); - new_lcn->name = strdup (path); - new_lcn->next = NULL; + if (out_extras->count >= MAX_DISTRICT_BONUS_ENTRIES) { + add_key_parse_error (parse_errors, line_number, key, value, "(too many bonus entries)"); + free_bonus_entry_list (out_extras); + free (value_text); + return false; + } - top_lcn->next = new_lcn; + struct district_bonus_entry * entry = &out_extras->entries[out_extras->count++]; + entry->bonus = bonus_value; + entry->building_id = -1; + entry->building_name = NULL; - if (parse_errors != NULL || unrecognized_keys != NULL) { - PopupForm * popup = get_popup_form (); - popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_WARNING", -1, 0, 0, 0); - char s[200]; - snprintf (s, sizeof s, "Wonder District Config errors in %s:", path); - s[(sizeof s) - 1] = '\0'; - PopupForm_add_text (popup, __, s, false); - if (parse_errors != NULL) { - for (struct error_line * line = parse_errors; line != NULL; line = line->next) - PopupForm_add_text (popup, __, line->text, false); + enum SquareTypes parsed_type; + if (read_square_type_value (&trimmed_name, &parsed_type)) { + entry->type = DBET_TILE; + entry->tile_type = parsed_type; + } else { + entry->type = DBET_BUILDING; + entry->tile_type = (enum SquareTypes)SQ_INVALID; + entry->building_name = extract_slice (&trimmed_name); + } + } } - if (unrecognized_keys != NULL) { - PopupForm_add_text (popup, __, "", false); - PopupForm_add_text (popup, __, "Unrecognized keys:", false); - for (struct error_line * line = unrecognized_keys; line != NULL; line = line->next) - PopupForm_add_text (popup, __, line->text, false); + + if (*cursor == ',') { + cursor++; + continue; } - patch_show_popup (popup, __, 0, 0); - free_error_lines (parse_errors); - free_error_lines (unrecognized_keys); + if (*cursor == '\0') + break; } -} -void -load_dynamic_wonder_configs () -{ - load_dynamic_wonder_config_file ("default.districts_wonders_config.txt", 1, 1, 1); - load_dynamic_wonder_config_file ("user.districts_wonders_config.txt", 1, 0, 1); - - char * scenario_filename = "scenario.districts_wonders_config.txt"; - char * scenario_wonder_config_path = BIC_get_asset_path (p_bic_data, __, scenario_filename, false); - if ((scenario_wonder_config_path != NULL) && (0 != strcmp (scenario_filename, scenario_wonder_config_path))) - load_dynamic_wonder_config_file (scenario_wonder_config_path, 0, 0, 1); -} - -void -init_parsed_natural_wonder_definition (struct parsed_natural_wonder_definition * def) -{ - memset (def, 0, sizeof *def); - def->terrain_type = SQ_Grassland; - def->adjacent_to = (enum SquareTypes)SQ_INVALID; - def->adjacency_dir = DIR_ZERO; -} + free (value_text); -void -free_parsed_natural_wonder_definition (struct parsed_natural_wonder_definition * def) -{ - if (def->name != NULL) { - free (def->name); - def->name = NULL; - } - if (def->img_path != NULL) { - free (def->img_path); - def->img_path = NULL; + if (! got_base) { + add_key_parse_error (parse_errors, line_number, key, value, "(expected base bonus)"); + free_bonus_entry_list (out_extras); + return false; } - init_parsed_natural_wonder_definition (def); + + *out_base_bonus = base_bonus; + return true; } bool -add_natural_wonder_from_definition (struct parsed_natural_wonder_definition * def, int section_start_line) +override_special_district_from_definition (struct parsed_district_definition * def, int section_start_line) { - if ((def == NULL) || (def->name == NULL)) + if ((! def->has_name) || (def->name == NULL)) return false; - int existing_index; - bool has_existing = stable_look_up (&is->natural_wonder_name_to_id, def->name, &existing_index); - - int dest = has_existing ? existing_index : is->natural_wonder_count; - if ((dest < 0) || (dest >= MAX_NATURAL_WONDER_DISTRICT_TYPES)) + int index = find_special_district_index_by_name (def->name); + if (index < 0) return false; - struct natural_wonder_district_config new_cfg; - memset (&new_cfg, 0, sizeof new_cfg); - new_cfg.index = dest; - new_cfg.is_dynamic = true; - new_cfg.adjacent_to = (enum SquareTypes)SQ_INVALID; - new_cfg.adjacency_dir = DIR_ZERO; + struct district_config * cfg = &is->district_configs[index]; + struct district_config const * defaults = &special_district_defaults[index]; - char * name_copy = strdup (def->name); - if (name_copy == NULL) - return false; - new_cfg.name = name_copy; + free (def->name); + def->name = NULL; + def->has_name = false; - char const * img_path_src = def->img_path; - char * img_copy = strdup (img_path_src); - if (img_copy == NULL) { - free (name_copy); - return false; + if (def->has_display_name) { + if ((cfg->display_name != NULL) && + (cfg->display_name != cfg->name) && + (cfg->display_name != defaults->display_name)) + free ((void *)cfg->display_name); + cfg->display_name = def->display_name; + def->display_name = NULL; } - new_cfg.img_path = img_copy; - new_cfg.img_row = def->img_row; - new_cfg.img_column = def->img_column; - new_cfg.terrain_type = def->terrain_type; - new_cfg.adjacent_to = def->adjacent_to; - new_cfg.adjacency_dir = def->adjacency_dir; - new_cfg.culture_bonus = def->has_culture_bonus ? def->culture_bonus : 0; - new_cfg.science_bonus = def->has_science_bonus ? def->science_bonus : 0; - new_cfg.food_bonus = def->has_food_bonus ? def->food_bonus : 0; - new_cfg.gold_bonus = def->has_gold_bonus ? def->gold_bonus : 0; - new_cfg.shield_bonus = def->has_shield_bonus ? def->shield_bonus : 0; - new_cfg.happiness_bonus = def->has_happiness_bonus ? def->happiness_bonus : 0; - if (has_existing) { - struct natural_wonder_district_config * cfg = &is->natural_wonder_configs[existing_index]; - free_dynamic_natural_wonder_config (cfg); - *cfg = new_cfg; - } else { - struct natural_wonder_district_config * cfg = &is->natural_wonder_configs[dest]; - free_dynamic_natural_wonder_config (cfg); - *cfg = new_cfg; - is->natural_wonder_count = dest + 1; - stable_insert (&is->natural_wonder_name_to_id, new_cfg.name, dest); + if (def->has_tooltip) { + if ((cfg->tooltip != NULL) && (cfg->tooltip != defaults->tooltip)) + free ((void *)cfg->tooltip); + cfg->tooltip = def->tooltip; + def->tooltip = NULL; } - return true; -} - -void -finalize_parsed_natural_wonder_definition (struct parsed_natural_wonder_definition * def, - int section_start_line, - struct error_line ** parse_errors) -{ - bool ok = true; + if (def->has_advance_prereqs) { + for (int i = 0; i < ARRAY_LEN (cfg->advance_prereqs); i++) { + char const * default_value = (i < defaults->advance_prereq_count) ? defaults->advance_prereqs[i] : NULL; + if ((cfg->advance_prereqs[i] != NULL) && + (cfg->advance_prereqs[i] != default_value)) + free ((void *)cfg->advance_prereqs[i]); + cfg->advance_prereqs[i] = NULL; + } - if ((! def->has_name) || (def->name == NULL)) { - ok = false; - if (parse_errors != NULL) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: name (value is required)", section_start_line); - err->text[(sizeof err->text) - 1] = '\0'; + cfg->advance_prereq_count = def->advance_prereq_count; + const int max_entries = ARRAY_LEN (cfg->advance_prereqs); + if (cfg->advance_prereq_count > max_entries) + cfg->advance_prereq_count = max_entries; + for (int i = 0; i < cfg->advance_prereq_count; i++) { + cfg->advance_prereqs[i] = def->advance_prereqs[i]; + def->advance_prereqs[i] = NULL; } + def->advance_prereq_count = 0; } - if (! def->has_img_row) { - ok = false; - if (parse_errors != NULL) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: img_row (value is required)", section_start_line); - err->text[(sizeof err->text) - 1] = '\0'; - } + + if (def->has_obsoleted_by) { + if ((cfg->obsoleted_by != NULL) && (cfg->obsoleted_by != defaults->obsoleted_by)) + free ((void *)cfg->obsoleted_by); + cfg->obsoleted_by = def->obsoleted_by; + def->obsoleted_by = NULL; } - if (! def->has_img_column) { - ok = false; - if (parse_errors != NULL) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: img_column (value is required)", section_start_line); - err->text[(sizeof err->text) - 1] = '\0'; + + if (def->has_resource_prereqs) { + for (int i = 0; i < ARRAY_LEN (cfg->resource_prereqs); i++) { + char const * default_value = (i < defaults->resource_prereq_count) ? defaults->resource_prereqs[i] : NULL; + if ((cfg->resource_prereqs[i] != NULL) && + (cfg->resource_prereqs[i] != default_value)) + free ((void *)cfg->resource_prereqs[i]); + cfg->resource_prereqs[i] = NULL; } - } - if (! def->has_terrain_type) { - ok = false; - if (parse_errors != NULL) { - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: terrain_type (value is required)", section_start_line); - err->text[(sizeof err->text) - 1] = '\0'; + + cfg->resource_prereq_count = def->resource_prereq_count; + const int max_entries = ARRAY_LEN (cfg->resource_prereqs); + if (cfg->resource_prereq_count > max_entries) + cfg->resource_prereq_count = max_entries; + for (int i = 0; i < cfg->resource_prereq_count; i++) { + cfg->resource_prereqs[i] = def->resource_prereqs[i]; + def->resource_prereqs[i] = NULL; } } - if (ok) - add_natural_wonder_from_definition (def, section_start_line); + if (def->has_resource_prereq_on_tile) { + if ((cfg->resource_prereq_on_tile != NULL) && (cfg->resource_prereq_on_tile != defaults->resource_prereq_on_tile)) + free ((void *)cfg->resource_prereq_on_tile); + cfg->resource_prereq_on_tile = def->resource_prereq_on_tile; + def->resource_prereq_on_tile = NULL; + } - free_parsed_natural_wonder_definition (def); -} + if (def->has_wonder_prereqs) { + for (int i = 0; i < ARRAY_LEN (cfg->wonder_prereqs); i++) { + char const * default_value = (i < defaults->wonder_prereq_count) ? defaults->wonder_prereqs[i] : NULL; + if ((cfg->wonder_prereqs[i] != NULL) && + (cfg->wonder_prereqs[i] != default_value)) + free ((void *)cfg->wonder_prereqs[i]); + cfg->wonder_prereqs[i] = NULL; + } -void -handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * def, - struct string_slice const * key, - struct string_slice const * value, - int line_number, - struct error_line ** parse_errors, - struct error_line ** unrecognized_keys) -{ - if (slice_matches_str (key, "name")) { - if (def->name != NULL) { - free (def->name); - def->name = NULL; + cfg->wonder_prereq_count = def->wonder_prereq_count; + const int max_entries = ARRAY_LEN (cfg->wonder_prereqs); + if (cfg->wonder_prereq_count > max_entries) + cfg->wonder_prereq_count = max_entries; + for (int i = 0; i < cfg->wonder_prereq_count; i++) { + cfg->wonder_prereqs[i] = def->wonder_prereqs[i]; + def->wonder_prereqs[i] = NULL; } + } - struct string_slice unquoted = trim_string_slice (value, 1); - if (unquoted.len == 0) { - def->has_name = false; - add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); - } else { - char * name_copy = extract_slice (&unquoted); - if (name_copy == NULL) { - def->has_name = false; - add_key_parse_error (parse_errors, line_number, key, value, "(out of memory)"); - } else { - def->name = name_copy; - def->has_name = true; - } + if (def->has_natural_wonder_prereqs) { + for (int i = 0; i < ARRAY_LEN (cfg->natural_wonder_prereqs); i++) { + char const * default_value = (i < defaults->natural_wonder_prereq_count) ? defaults->natural_wonder_prereqs[i] : NULL; + if ((cfg->natural_wonder_prereqs[i] != NULL) && + (cfg->natural_wonder_prereqs[i] != default_value)) + free ((void *)cfg->natural_wonder_prereqs[i]); + cfg->natural_wonder_prereqs[i] = NULL; } - } else if (slice_matches_str (key, "terrain_type")) { - enum SquareTypes terrain; - if (read_natural_wonder_terrain_type (value, &terrain)) { - def->terrain_type = terrain; - def->has_terrain_type = true; - } else { - def->has_terrain_type = false; - add_key_parse_error (parse_errors, line_number, key, value, "(unrecognized terrain type)"); + cfg->natural_wonder_prereq_count = def->natural_wonder_prereq_count; + const int max_entries = ARRAY_LEN (cfg->natural_wonder_prereqs); + if (cfg->natural_wonder_prereq_count > max_entries) + cfg->natural_wonder_prereq_count = max_entries; + for (int i = 0; i < cfg->natural_wonder_prereq_count; i++) { + cfg->natural_wonder_prereqs[i] = def->natural_wonder_prereqs[i]; + def->natural_wonder_prereqs[i] = NULL; } + } - } else if (slice_matches_str (key, "adjacent_to")) { - enum SquareTypes adj; - if (read_square_type_value (value, &adj)) { - def->adjacent_to = adj; - def->has_adjacent_to = true; - } else { - def->adjacent_to = (enum SquareTypes)SQ_INVALID; - def->has_adjacent_to = false; - add_key_parse_error (parse_errors, line_number, key, value, "(unrecognized square type)"); + if (def->has_buildable_on_districts) { + for (int i = 0; i < ARRAY_LEN (cfg->buildable_on_districts); i++) { + char const * default_value = (i < defaults->buildable_on_district_count) ? defaults->buildable_on_districts[i] : NULL; + if ((cfg->buildable_on_districts[i] != NULL) && + (cfg->buildable_on_districts[i] != default_value)) + free ((void *)cfg->buildable_on_districts[i]); + cfg->buildable_on_districts[i] = NULL; } - } else if (slice_matches_str (key, "adjacency_dir")) { - enum direction dir; - if (read_direction_value (value, &dir)) { - def->adjacency_dir = dir; - def->has_adjacency_dir = true; - } else { - def->adjacency_dir = DIR_ZERO; - def->has_adjacency_dir = false; - add_key_parse_error (parse_errors, line_number, key, value, "(unrecognized direction)"); + cfg->buildable_on_district_count = def->buildable_on_district_count; + const int max_entries = ARRAY_LEN (cfg->buildable_on_districts); + if (cfg->buildable_on_district_count > max_entries) + cfg->buildable_on_district_count = max_entries; + for (int i = 0; i < cfg->buildable_on_district_count; i++) { + cfg->buildable_on_districts[i] = def->buildable_on_districts[i]; + def->buildable_on_districts[i] = NULL; } + cfg->buildable_on_district_id_count = 0; + cfg->has_buildable_on_districts = true; + } - } else if (slice_matches_str (key, "img_path")) { - if (def->img_path != NULL) { - free (def->img_path); - def->img_path = NULL; + if (def->has_buildable_by_civs) { + for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { + char const * default_value = (i < defaults->buildable_by_civ_count) ? defaults->buildable_by_civs[i] : NULL; + if ((cfg->buildable_by_civs[i] != NULL) && + (cfg->buildable_by_civs[i] != default_value)) + free ((void *)cfg->buildable_by_civs[i]); + cfg->buildable_by_civs[i] = NULL; + } + cfg->buildable_by_civ_count = def->buildable_by_civ_count; + const int max_civ_names = ARRAY_LEN (cfg->buildable_by_civs); + if (cfg->buildable_by_civ_count > max_civ_names) + cfg->buildable_by_civ_count = max_civ_names; + for (int i = 0; i < cfg->buildable_by_civ_count; i++) { + cfg->buildable_by_civs[i] = def->buildable_by_civs[i]; + def->buildable_by_civs[i] = NULL; + } + cfg->has_buildable_by_civs = true; + } + + if (def->has_buildable_by_civ_traits) { + cfg->buildable_by_civ_traits_id_count = def->buildable_by_civ_traits_id_count; + const int max_entries = ARRAY_LEN (cfg->buildable_by_civ_traits_ids); + if (cfg->buildable_by_civ_traits_id_count > max_entries) + cfg->buildable_by_civ_traits_id_count = max_entries; + for (int i = 0; i < cfg->buildable_by_civ_traits_id_count; i++) + cfg->buildable_by_civ_traits_ids[i] = def->buildable_by_civ_traits_ids[i]; + cfg->has_buildable_by_civ_traits = true; + } + + if (def->has_buildable_by_civ_govs) { + cfg->buildable_by_civ_govs_id_count = def->buildable_by_civ_govs_id_count; + const int max_entries = ARRAY_LEN (cfg->buildable_by_civ_govs_ids); + if (cfg->buildable_by_civ_govs_id_count > max_entries) + cfg->buildable_by_civ_govs_id_count = max_entries; + for (int i = 0; i < cfg->buildable_by_civ_govs_id_count; i++) + cfg->buildable_by_civ_govs_ids[i] = def->buildable_by_civ_govs_ids[i]; + cfg->has_buildable_by_civ_govs = true; + } + + if (def->has_buildable_by_civ_cultures) { + cfg->buildable_by_civ_cultures_id_count = def->buildable_by_civ_cultures_id_count; + const int max_entries = ARRAY_LEN (cfg->buildable_by_civ_cultures_ids); + if (cfg->buildable_by_civ_cultures_id_count > max_entries) + cfg->buildable_by_civ_cultures_id_count = max_entries; + for (int i = 0; i < cfg->buildable_by_civ_cultures_id_count; i++) + cfg->buildable_by_civ_cultures_ids[i] = def->buildable_by_civ_cultures_ids[i]; + cfg->has_buildable_by_civ_cultures = true; + } + + if (def->has_buildable_by_war_allies) + cfg->buildable_by_war_allies = def->buildable_by_war_allies; + if (def->has_buildable_by_pact_allies) + cfg->buildable_by_pact_allies = def->buildable_by_pact_allies; + + if (def->has_allow_multiple) + cfg->allow_multiple = def->allow_multiple; + if (def->has_vary_img_by_era) + cfg->vary_img_by_era = def->vary_img_by_era; + if (def->has_vary_img_by_culture) + cfg->vary_img_by_culture = def->vary_img_by_culture; + if (def->has_render_strategy) + cfg->render_strategy = def->render_strategy; + if (def->has_ai_build_strategy) + cfg->ai_build_strategy = def->ai_build_strategy; + if (def->has_align_to_coast) + cfg->align_to_coast = def->align_to_coast; + if (def->has_draw_over_resources) + cfg->draw_over_resources = def->draw_over_resources; + if (def->has_allow_irrigation_from) + cfg->allow_irrigation_from = def->allow_irrigation_from; + if (def->has_auto_add_road) + cfg->auto_add_road = def->auto_add_road; + if (def->has_auto_add_railroad) + cfg->auto_add_railroad = def->auto_add_railroad; + if (def->has_custom_width) + cfg->custom_width = def->custom_width; + if (def->has_custom_height) + cfg->custom_height = def->custom_height; + if (def->has_x_offset) + cfg->x_offset = def->x_offset; + if (def->has_y_offset) + cfg->y_offset = def->y_offset; + if (def->has_btn_tile_sheet_column) + cfg->btn_tile_sheet_column = def->btn_tile_sheet_column; + if (def->has_btn_tile_sheet_row) + cfg->btn_tile_sheet_row = def->btn_tile_sheet_row; + if (def->has_defense_bonus_percent) { + cfg->defense_bonus_percent = def->defense_bonus_percent; + free_bonus_entry_list_override (&cfg->defense_bonus_extras, &defaults->defense_bonus_extras); + move_bonus_entry_list (&cfg->defense_bonus_extras, &def->defense_bonus_extras); + } + if (def->has_heal_units_in_one_turn) + cfg->heal_units_in_one_turn = def->heal_units_in_one_turn; + if (def->has_culture_bonus) { + cfg->culture_bonus = def->culture_bonus; + free_bonus_entry_list_override (&cfg->culture_bonus_extras, &defaults->culture_bonus_extras); + move_bonus_entry_list (&cfg->culture_bonus_extras, &def->culture_bonus_extras); + } + if (def->has_science_bonus) { + cfg->science_bonus = def->science_bonus; + free_bonus_entry_list_override (&cfg->science_bonus_extras, &defaults->science_bonus_extras); + move_bonus_entry_list (&cfg->science_bonus_extras, &def->science_bonus_extras); + } + if (def->has_food_bonus) { + cfg->food_bonus = def->food_bonus; + free_bonus_entry_list_override (&cfg->food_bonus_extras, &defaults->food_bonus_extras); + move_bonus_entry_list (&cfg->food_bonus_extras, &def->food_bonus_extras); + } + if (def->has_gold_bonus) { + cfg->gold_bonus = def->gold_bonus; + free_bonus_entry_list_override (&cfg->gold_bonus_extras, &defaults->gold_bonus_extras); + move_bonus_entry_list (&cfg->gold_bonus_extras, &def->gold_bonus_extras); + } + if (def->has_shield_bonus) { + cfg->shield_bonus = def->shield_bonus; + free_bonus_entry_list_override (&cfg->shield_bonus_extras, &defaults->shield_bonus_extras); + move_bonus_entry_list (&cfg->shield_bonus_extras, &def->shield_bonus_extras); + } + if (def->has_happiness_bonus) { + cfg->happiness_bonus = def->happiness_bonus; + free_bonus_entry_list_override (&cfg->happiness_bonus_extras, &defaults->happiness_bonus_extras); + move_bonus_entry_list (&cfg->happiness_bonus_extras, &def->happiness_bonus_extras); + } + if (def->has_buildable_on) + cfg->buildable_square_types_mask = def->buildable_square_types_mask; + + if (def->has_generated_resource) { + if ((cfg->generated_resource != NULL) && (cfg->generated_resource != defaults->generated_resource)) + free ((void *)cfg->generated_resource); + cfg->generated_resource = def->generated_resource; + def->generated_resource = NULL; + cfg->generated_resource_flags = def->generated_resource_flags; + cfg->generated_resource_id = -1; + } + + if (def->has_dependent_improvements) { + for (int i = 0; i < ARRAY_LEN (cfg->dependent_improvements); i++) { + char const * default_value = (i < defaults->dependent_improvement_count) ? defaults->dependent_improvements[i] : NULL; + if ((cfg->dependent_improvements[i] != NULL) && + (cfg->dependent_improvements[i] != default_value)) + free ((void *)cfg->dependent_improvements[i]); + cfg->dependent_improvements[i] = NULL; } - struct string_slice unquoted = trim_string_slice (value, 1); - if (unquoted.len == 0) { - def->has_img_path = false; - } else { - char * path_copy = extract_slice (&unquoted); - if (path_copy == NULL) { - def->has_img_path = false; - } else { - def->img_path = path_copy; - def->has_img_path = true; - } + cfg->dependent_improvement_count = def->dependent_improvement_count; + const int max_entries = ARRAY_LEN (cfg->dependent_improvements); + if (cfg->dependent_improvement_count > max_entries) + cfg->dependent_improvement_count = max_entries; + for (int i = 0; i < cfg->dependent_improvement_count; i++) { + cfg->dependent_improvements[i] = def->dependent_improvements[i]; + def->dependent_improvements[i] = NULL; } + cfg->max_building_index = cfg->dependent_improvement_count; + if (cfg->max_building_index > 20) + cfg->max_building_index = 20; + } - } else if (slice_matches_str (key, "img_row")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->img_row = ival; - def->has_img_row = true; - } else { - def->has_img_row = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + if (def->has_img_paths) { + for (int i = 0; i < ARRAY_LEN (cfg->img_paths); i++) { + char const * default_value = (i < defaults->img_path_count) ? defaults->img_paths[i] : NULL; + if ((cfg->img_paths[i] != NULL) && + (cfg->img_paths[i] != default_value)) + free ((void *)cfg->img_paths[i]); + cfg->img_paths[i] = NULL; } - } else if (slice_matches_str (key, "img_column")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->img_column = ival; - def->has_img_column = true; - } else { - def->has_img_column = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + cfg->img_path_count = def->img_path_count; + const int max_img_paths = ARRAY_LEN (cfg->img_paths); + if (cfg->img_path_count > max_img_paths) + cfg->img_path_count = max_img_paths; + for (int i = 0; i < cfg->img_path_count; i++) { + cfg->img_paths[i] = def->img_paths[i]; + def->img_paths[i] = NULL; } + } - } else if (slice_matches_str (key, "culture_bonus")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->culture_bonus = ival; - def->has_culture_bonus = true; - } else { - def->has_culture_bonus = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - } + if (! ensure_culture_variant_art (cfg, section_start_line)) { + free_special_district_override_strings (cfg, defaults); + *cfg = *defaults; + return false; + } - } else if (slice_matches_str (key, "science_bonus")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->science_bonus = ival; - def->has_science_bonus = true; - } else { - def->has_science_bonus = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - } + return true; +} - } else if (slice_matches_str (key, "food_bonus")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->food_bonus = ival; - def->has_food_bonus = true; - } else { - def->has_food_bonus = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - } +bool +add_dynamic_district_from_definition (struct parsed_district_definition * def, int section_start_line) +{ + if ((! def->has_name) || (def->name == NULL)) + return false; - } else if (slice_matches_str (key, "gold_bonus")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->gold_bonus = ival; - def->has_gold_bonus = true; - } else { - def->has_gold_bonus = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - } + if ((! def->has_img_paths) || (def->img_path_count <= 0)) + return false; - } else if (slice_matches_str (key, "shield_bonus")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->shield_bonus = ival; - def->has_shield_bonus = true; - } else { - def->has_shield_bonus = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + int existing_index = -1; + for (int i = is->special_district_count; i < is->district_count; i++) { + if ((is->district_configs[i].name != NULL) && + (strcmp (is->district_configs[i].name, def->name) == 0)) { + existing_index = i; + break; } + } - } else if (slice_matches_str (key, "happiness_bonus")) { - struct string_slice val_slice = *value; - int ival; - if (read_int (&val_slice, &ival)) { - def->happiness_bonus = ival; - def->has_happiness_bonus = true; - } else { - def->has_happiness_bonus = false; - add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - } + bool reusing_existing = existing_index >= 0; + int dest_index = reusing_existing ? existing_index : (is->special_district_count + is->dynamic_district_count); - } else - add_unrecognized_key_error (unrecognized_keys, line_number, key); -} + if ((! reusing_existing) && (dest_index >= COUNT_DISTRICT_TYPES)) + return false; -void -load_natural_wonder_config_file (char const * file_path, - int path_is_relative_to_mod_dir, - int log_missing, - int drop_existing_configs) -{ - char path[MAX_PATH]; - if (path_is_relative_to_mod_dir) { - if (is->mod_rel_dir == NULL) - return; - snprintf (path, sizeof path, "%s\\%s", is->mod_rel_dir, file_path); + enum Unit_Command_Values preserved_command = 0; + if (reusing_existing) + preserved_command = is->district_configs[dest_index].command; + + struct district_config new_cfg; + memset (&new_cfg, 0, sizeof new_cfg); + new_cfg.is_dynamic = true; + + new_cfg.name = def->name; + def->name = NULL; + if (def->has_display_name) { + new_cfg.display_name = def->display_name; + if (new_cfg.display_name == NULL) + new_cfg.display_name = new_cfg.name; + def->display_name = NULL; } else { - strncpy (path, file_path, sizeof path); + new_cfg.display_name = new_cfg.name; } - path[(sizeof path) - 1] = '\0'; - char * text = file_to_string (path); - if (text == NULL) { - if (log_missing) { - char ss[256]; - snprintf (ss, sizeof ss, "[C3X] Natural wonders config file not found: %s", path); - (*p_OutputDebugStringA) (ss); + if (def->has_tooltip) { + new_cfg.tooltip = def->tooltip; + def->tooltip = NULL; + } else if (new_cfg.name != NULL) { + char buffer[128]; + snprintf (buffer, sizeof buffer, "Build %s", new_cfg.name); + new_cfg.tooltip = strdup (buffer); + } + + if (def->has_advance_prereqs) { + new_cfg.advance_prereq_count = def->advance_prereq_count; + const int max_entries = ARRAY_LEN (new_cfg.advance_prereqs); + if (new_cfg.advance_prereq_count > max_entries) + new_cfg.advance_prereq_count = max_entries; + for (int i = 0; i < new_cfg.advance_prereq_count; i++) { + new_cfg.advance_prereqs[i] = def->advance_prereqs[i]; + def->advance_prereqs[i] = NULL; } - return; + def->advance_prereq_count = 0; } - if (drop_existing_configs) - reset_natural_wonder_configs (); + if (def->has_obsoleted_by) { + new_cfg.obsoleted_by = def->obsoleted_by; + def->obsoleted_by = NULL; + } - struct parsed_natural_wonder_definition def; - init_parsed_natural_wonder_definition (&def); - bool in_section = false; - int section_start_line = 0; - int line_number = 0; - struct error_line * unrecognized_keys = NULL; - struct error_line * parse_errors = NULL; + new_cfg.resource_prereq_count = def->has_resource_prereqs ? def->resource_prereq_count : 0; + const int max_resource_entries = ARRAY_LEN (new_cfg.resource_prereqs); + if (new_cfg.resource_prereq_count > max_resource_entries) + new_cfg.resource_prereq_count = max_resource_entries; + for (int i = 0; i < new_cfg.resource_prereq_count; i++) { + new_cfg.resource_prereqs[i] = def->resource_prereqs[i]; + def->resource_prereqs[i] = NULL; + } + + if (def->has_resource_prereq_on_tile) { + new_cfg.resource_prereq_on_tile = def->resource_prereq_on_tile; + def->resource_prereq_on_tile = NULL; + } + + new_cfg.wonder_prereq_count = def->has_wonder_prereqs ? def->wonder_prereq_count : 0; + const int max_required_wonders = ARRAY_LEN (new_cfg.wonder_prereqs); + if (new_cfg.wonder_prereq_count > max_required_wonders) + new_cfg.wonder_prereq_count = max_required_wonders; + for (int i = 0; i < new_cfg.wonder_prereq_count; i++) { + new_cfg.wonder_prereqs[i] = def->wonder_prereqs[i]; + def->wonder_prereqs[i] = NULL; + } + + new_cfg.natural_wonder_prereq_count = def->has_natural_wonder_prereqs ? def->natural_wonder_prereq_count : 0; + const int max_required_natural_wonders = ARRAY_LEN (new_cfg.natural_wonder_prereqs); + if (new_cfg.natural_wonder_prereq_count > max_required_natural_wonders) + new_cfg.natural_wonder_prereq_count = max_required_natural_wonders; + for (int i = 0; i < new_cfg.natural_wonder_prereq_count; i++) { + new_cfg.natural_wonder_prereqs[i] = def->natural_wonder_prereqs[i]; + def->natural_wonder_prereqs[i] = NULL; + } + + new_cfg.buildable_on_district_count = def->has_buildable_on_districts ? def->buildable_on_district_count : 0; + const int max_buildable_on_districts = ARRAY_LEN (new_cfg.buildable_on_districts); + if (new_cfg.buildable_on_district_count > max_buildable_on_districts) + new_cfg.buildable_on_district_count = max_buildable_on_districts; + for (int i = 0; i < new_cfg.buildable_on_district_count; i++) { + new_cfg.buildable_on_districts[i] = def->buildable_on_districts[i]; + def->buildable_on_districts[i] = NULL; + } + new_cfg.buildable_on_district_id_count = 0; + new_cfg.has_buildable_on_districts = def->has_buildable_on_districts; + + new_cfg.buildable_by_civ_count = def->has_buildable_by_civs ? def->buildable_by_civ_count : 0; + const int max_civ_names = ARRAY_LEN (new_cfg.buildable_by_civs); + if (new_cfg.buildable_by_civ_count > max_civ_names) + new_cfg.buildable_by_civ_count = max_civ_names; + for (int i = 0; i < new_cfg.buildable_by_civ_count; i++) { + new_cfg.buildable_by_civs[i] = def->buildable_by_civs[i]; + def->buildable_by_civs[i] = NULL; + } - char * cursor = text; - while (*cursor != '\0') { - line_number += 1; + new_cfg.has_buildable_by_civs = def->has_buildable_by_civs; - char * line_start = cursor; - char * line_end = cursor; - while ((*line_end != '\0') && (*line_end != '\n')) - line_end++; + new_cfg.buildable_by_civ_traits_id_count = def->has_buildable_by_civ_traits ? def->buildable_by_civ_traits_id_count : 0; + const int max_buildable_traits = ARRAY_LEN (new_cfg.buildable_by_civ_traits_ids); + if (new_cfg.buildable_by_civ_traits_id_count > max_buildable_traits) + new_cfg.buildable_by_civ_traits_id_count = max_buildable_traits; + for (int i = 0; i < new_cfg.buildable_by_civ_traits_id_count; i++) + new_cfg.buildable_by_civ_traits_ids[i] = def->buildable_by_civ_traits_ids[i]; + new_cfg.has_buildable_by_civ_traits = def->has_buildable_by_civ_traits; - int line_len = line_end - line_start; - bool has_newline = (*line_end == '\n'); - if (has_newline) - *line_end = '\0'; + new_cfg.buildable_by_civ_govs_id_count = def->has_buildable_by_civ_govs ? def->buildable_by_civ_govs_id_count : 0; + const int max_buildable_govs = ARRAY_LEN (new_cfg.buildable_by_civ_govs_ids); + if (new_cfg.buildable_by_civ_govs_id_count > max_buildable_govs) + new_cfg.buildable_by_civ_govs_id_count = max_buildable_govs; + for (int i = 0; i < new_cfg.buildable_by_civ_govs_id_count; i++) + new_cfg.buildable_by_civ_govs_ids[i] = def->buildable_by_civ_govs_ids[i]; + new_cfg.has_buildable_by_civ_govs = def->has_buildable_by_civ_govs; - struct string_slice line_slice = { .str = line_start, .len = line_len }; - struct string_slice trimmed = trim_string_slice (&line_slice, 0); - if (line_is_empty_or_comment (&trimmed)) { - cursor = has_newline ? line_end + 1 : line_end; - continue; - } + new_cfg.buildable_by_civ_cultures_id_count = def->has_buildable_by_civ_cultures ? def->buildable_by_civ_cultures_id_count : 0; + const int max_buildable_cultures = ARRAY_LEN (new_cfg.buildable_by_civ_cultures_ids); + if (new_cfg.buildable_by_civ_cultures_id_count > max_buildable_cultures) + new_cfg.buildable_by_civ_cultures_id_count = max_buildable_cultures; + for (int i = 0; i < new_cfg.buildable_by_civ_cultures_id_count; i++) + new_cfg.buildable_by_civ_cultures_ids[i] = def->buildable_by_civ_cultures_ids[i]; + new_cfg.has_buildable_by_civ_cultures = def->has_buildable_by_civ_cultures; - if (trimmed.str[0] == '#') { - struct string_slice directive = trimmed; - directive.str += 1; - directive.len -= 1; - directive = trim_string_slice (&directive, 0); - if ((directive.len > 0) && slice_matches_str (&directive, "Wonder")) { - if (in_section) - finalize_parsed_natural_wonder_definition (&def, section_start_line, &parse_errors); - in_section = true; - section_start_line = line_number; - } - cursor = has_newline ? line_end + 1 : line_end; - continue; - } + new_cfg.buildable_by_war_allies = def->has_buildable_by_war_allies ? def->buildable_by_war_allies : false; + new_cfg.buildable_by_pact_allies = def->has_buildable_by_pact_allies ? def->buildable_by_pact_allies : false; - if (! in_section) { - cursor = has_newline ? line_end + 1 : line_end; - continue; - } + new_cfg.allow_multiple = def->has_allow_multiple ? def->allow_multiple : false; + new_cfg.vary_img_by_era = def->has_vary_img_by_era ? def->vary_img_by_era : false; + new_cfg.vary_img_by_culture = def->has_vary_img_by_culture ? def->vary_img_by_culture : false; + new_cfg.render_strategy = def->has_render_strategy ? def->render_strategy : DRS_BY_COUNT; + new_cfg.ai_build_strategy = def->has_ai_build_strategy ? def->ai_build_strategy : DABS_DISTRICT; + new_cfg.align_to_coast = def->has_align_to_coast ? def->align_to_coast : false; + new_cfg.draw_over_resources = def->has_draw_over_resources ? def->draw_over_resources : false; + new_cfg.allow_irrigation_from = def->has_allow_irrigation_from ? def->allow_irrigation_from : false; + new_cfg.auto_add_road = def->has_auto_add_road ? def->auto_add_road : false; + new_cfg.auto_add_railroad = def->has_auto_add_railroad ? def->auto_add_railroad : false; + new_cfg.custom_width = def->has_custom_width ? def->custom_width : 0; + new_cfg.custom_height = def->has_custom_height ? def->custom_height : 0; + new_cfg.x_offset = def->has_x_offset ? def->x_offset : 0; + new_cfg.y_offset = def->has_y_offset ? def->y_offset : 0; + new_cfg.btn_tile_sheet_column = def->has_btn_tile_sheet_column ? def->btn_tile_sheet_column : 0; + new_cfg.btn_tile_sheet_row = def->has_btn_tile_sheet_row ? def->btn_tile_sheet_row : 0; + new_cfg.defense_bonus_percent = def->has_defense_bonus_percent ? def->defense_bonus_percent : 0; + new_cfg.heal_units_in_one_turn = def->has_heal_units_in_one_turn ? def->heal_units_in_one_turn : false; + new_cfg.culture_bonus = def->has_culture_bonus ? def->culture_bonus : 0; + new_cfg.science_bonus = def->has_science_bonus ? def->science_bonus : 0; + new_cfg.food_bonus = def->has_food_bonus ? def->food_bonus : 0; + new_cfg.gold_bonus = def->has_gold_bonus ? def->gold_bonus : 0; + new_cfg.shield_bonus = def->has_shield_bonus ? def->shield_bonus : 0; + new_cfg.happiness_bonus = def->has_happiness_bonus ? def->happiness_bonus : 0; + new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); - struct string_slice key_slice = {0}; - struct string_slice value_slice = {0}; - enum key_value_parse_status status = parse_trimmed_key_value (&trimmed, &key_slice, &value_slice); - if (status == KVP_NO_EQUALS) { - char * line_text = extract_slice (&trimmed); - struct error_line * err = add_error_line (&parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: %s (expected '=')", line_number, line_text); - err->text[(sizeof err->text) - 1] = '\0'; - free (line_text); - cursor = has_newline ? line_end + 1 : line_end; - continue; - } else if (status == KVP_EMPTY_KEY) { - struct error_line * err = add_error_line (&parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: (missing key)", line_number); - err->text[(sizeof err->text) - 1] = '\0'; - cursor = has_newline ? line_end + 1 : line_end; - continue; - } + if (def->has_culture_bonus) + move_bonus_entry_list (&new_cfg.culture_bonus_extras, &def->culture_bonus_extras); + if (def->has_science_bonus) + move_bonus_entry_list (&new_cfg.science_bonus_extras, &def->science_bonus_extras); + if (def->has_food_bonus) + move_bonus_entry_list (&new_cfg.food_bonus_extras, &def->food_bonus_extras); + if (def->has_gold_bonus) + move_bonus_entry_list (&new_cfg.gold_bonus_extras, &def->gold_bonus_extras); + if (def->has_shield_bonus) + move_bonus_entry_list (&new_cfg.shield_bonus_extras, &def->shield_bonus_extras); + if (def->has_happiness_bonus) + move_bonus_entry_list (&new_cfg.happiness_bonus_extras, &def->happiness_bonus_extras); + if (def->has_defense_bonus_percent) + move_bonus_entry_list (&new_cfg.defense_bonus_extras, &def->defense_bonus_extras); - handle_natural_wonder_definition_key (&def, &key_slice, &value_slice, line_number, &parse_errors, &unrecognized_keys); - cursor = has_newline ? line_end + 1 : line_end; + if (def->has_generated_resource) { + new_cfg.generated_resource = def->generated_resource; + def->generated_resource = NULL; + new_cfg.generated_resource_flags = def->generated_resource_flags; + new_cfg.generated_resource_id = -1; + } else { + new_cfg.generated_resource = NULL; + new_cfg.generated_resource_id = -1; + new_cfg.generated_resource_flags = 0; } - if (in_section) - finalize_parsed_natural_wonder_definition (&def, section_start_line, &parse_errors); + new_cfg.dependent_improvement_count = def->has_dependent_improvements ? def->dependent_improvement_count : 0; + const int max_dependent_entries = ARRAY_LEN (is->district_configs[0].dependent_improvements); + if (new_cfg.dependent_improvement_count > max_dependent_entries) + new_cfg.dependent_improvement_count = max_dependent_entries; + for (int i = 0; i < new_cfg.dependent_improvement_count; i++) { + new_cfg.dependent_improvements[i] = def->dependent_improvements[i]; + def->dependent_improvements[i] = NULL; + } - free_parsed_natural_wonder_definition (&def); - free (text); + new_cfg.img_path_count = def->img_path_count; + const int max_img_paths = ARRAY_LEN (is->district_configs[0].img_paths); + if (new_cfg.img_path_count > max_img_paths) + new_cfg.img_path_count = max_img_paths; + for (int i = 0; i < new_cfg.img_path_count; i++) { + new_cfg.img_paths[i] = def->img_paths[i]; + def->img_paths[i] = NULL; + } - // Append to loaded config names list - struct loaded_config_name * top_lcn = is->loaded_config_names; - while (top_lcn->next != NULL) - top_lcn = top_lcn->next; + if (! ensure_culture_variant_art (&new_cfg, section_start_line)) { + free_dynamic_district_config (&new_cfg); + return false; + } - struct loaded_config_name * new_lcn = malloc (sizeof *new_lcn); - new_lcn->name = strdup (path); - new_lcn->next = NULL; + new_cfg.max_building_index = new_cfg.dependent_improvement_count; + if (new_cfg.max_building_index > 20) + new_cfg.max_building_index = 20; - top_lcn->next = new_lcn; + if (reusing_existing) + new_cfg.command = preserved_command; + else + new_cfg.command = allocate_dynamic_district_command (new_cfg.name); - if (parse_errors != NULL || unrecognized_keys != NULL) { - PopupForm * popup = get_popup_form (); - popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_WARNING", -1, 0, 0, 0); - char s[200]; - snprintf (s, sizeof s, "Natural Wonder Config errors in %s:", path); - s[(sizeof s) - 1] = '\0'; - PopupForm_add_text (popup, __, s, false); - if (parse_errors != NULL) { - for (struct error_line * line = parse_errors; line != NULL; line = line->next) - PopupForm_add_text (popup, __, line->text, false); - } - if (unrecognized_keys != NULL) { - PopupForm_add_text (popup, __, "", false); - PopupForm_add_text (popup, __, "Unrecognized keys:", false); - for (struct error_line * line = unrecognized_keys; line != NULL; line = line->next) - PopupForm_add_text (popup, __, line->text, false); - } - patch_show_popup (popup, __, 0, 0); - free_error_lines (parse_errors); - free_error_lines (unrecognized_keys); + struct district_config * dest_cfg = &is->district_configs[dest_index]; + if (reusing_existing) { + enum Unit_Command_Values saved_command = preserved_command; + free_dynamic_district_config (dest_cfg); + *dest_cfg = new_cfg; + dest_cfg->command = saved_command; + } else { + free_dynamic_district_config (dest_cfg); + *dest_cfg = new_cfg; + is->dynamic_district_count += 1; + is->district_count = is->special_district_count + is->dynamic_district_count; } + + return true; } void -load_natural_wonder_configs () +finalize_parsed_district_definition (struct parsed_district_definition * def, int section_start_line) { - load_natural_wonder_config_file ("default.districts_natural_wonders_config.txt", 1, 1, 1); - load_natural_wonder_config_file ("user.districts_natural_wonders_config.txt", 1, 0, 1); - - char * scenario_filename = "scenario.districts_natural_wonders_config.txt"; - char * scenario_natural_wonder_config_path = BIC_get_asset_path (p_bic_data, __, scenario_filename, false); - if ((scenario_natural_wonder_config_path != NULL) && (0 != strcmp (scenario_filename, scenario_natural_wonder_config_path))) - load_natural_wonder_config_file (scenario_natural_wonder_config_path, 0, 0, 1); -} + if ((! def->has_name) || (def->name == NULL)) + return; -bool -district_config_has_dependent_improvement (struct district_config * cfg, char const * name) -{ - if ((cfg == NULL) || (name == NULL) || (name[0] == '\0')) - return false; + if (! override_special_district_from_definition (def, section_start_line)) + add_dynamic_district_from_definition (def, section_start_line); - for (int i = 0; i < cfg->dependent_improvement_count; i++) { - char const * existing = cfg->dependent_improvements[i]; - if ((existing != NULL) && (strcmp (existing, name) == 0)) - return true; - } - return false; + free_parsed_district_definition (def); } -bool -find_civ_trait_id_by_name (struct string_slice const * name, int * out_id) +void +handle_district_definition_key (struct parsed_district_definition * def, + struct string_slice const * key, + struct string_slice const * value, + int line_number, + struct error_line ** parse_errors, + struct error_line ** unrecognized_keys) { - if ((name == NULL) || (name->len <= 0) || (out_id == NULL)) - return false; - - struct trait_entry { char const * name; int id; } traits[] = { - {"Agricultural", 6}, - {"Commercial", 1}, - {"Expansionist", 2}, - {"Industrious", 5}, - {"Militaristic", 0}, - {"Religious", 4}, - {"Scientific", 3}, - {"Seafaring", 7} - }; + if (slice_matches_str (key, "name")) { + if (def->name != NULL) { + free (def->name); + def->name = NULL; + } - for (int i = 0; i < ARRAY_LEN (traits); i++) { - if (slice_matches_str (name, traits[i].name)) { - *out_id = traits[i].id; - return true; + char * name_copy = copy_trimmed_string_or_null (value, 1); + if (name_copy == NULL) { + def->has_name = false; + add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); + } else { + def->name = name_copy; + def->has_name = true; } - } - return false; -} + } else if (slice_matches_str (key, "display_name")) { + if (def->display_name != NULL) { + free (def->display_name); + def->display_name = NULL; + } + def->display_name = copy_trimmed_string_or_null (value, 1); + def->has_display_name = true; -bool -find_civ_culture_id_by_name (struct string_slice const * name, int * out_id) -{ - if ((name == NULL) || (name->len <= 0) || (out_id == NULL)) - return false; + } else if (slice_matches_str (key, "tooltip")) { + if (def->tooltip != NULL) { + free (def->tooltip); + def->tooltip = NULL; + } + def->tooltip = copy_trimmed_string_or_null (value, 1); + def->has_tooltip = true; - struct culture_entry { char const * name; int id; } cultures[] = { - {"American", 0}, - {"European", 1}, - {"Roman", 2}, - {"Mid East", 3}, - {"Mideast", 3}, - {"Middle Eastern", 3}, - {"MiddleEastern", 3}, - {"Asian", 4} - }; + } else if (slice_matches_str (key, "advance_prereqs") || slice_matches_str (key, "advance_prereq")) { + for (int i = 0; i < def->advance_prereq_count; i++) { + if (def->advance_prereqs[i] != NULL) { + free (def->advance_prereqs[i]); + def->advance_prereqs[i] = NULL; + } + } + def->advance_prereq_count = 0; + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->advance_prereqs, + ARRAY_LEN (def->advance_prereqs), + &list_count, + parse_errors, + line_number, + "advance_prereqs")) { + def->advance_prereq_count = list_count; + def->has_advance_prereqs = true; + } else { + def->advance_prereq_count = 0; + def->has_advance_prereqs = false; + } + free (value_text); - for (int i = 0; i < ARRAY_LEN (cultures); i++) { - if (slice_matches_str (name, cultures[i].name)) { - *out_id = cultures[i].id; - return true; + } else if (slice_matches_str (key, "obsoleted_by")) { + if (def->obsoleted_by != NULL) { + free (def->obsoleted_by); + def->obsoleted_by = NULL; } - } + def->obsoleted_by = copy_trimmed_string_or_null (value, 1); + def->has_obsoleted_by = true; - return false; -} + } else if (slice_matches_str (key, "resource_prereqs")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->resource_prereqs, + ARRAY_LEN (def->resource_prereqs), + &list_count, + parse_errors, + line_number, + "resource_prereqs")) { + def->resource_prereq_count = list_count; + def->has_resource_prereqs = true; + } else { + def->resource_prereq_count = 0; + def->has_resource_prereqs = false; + } + free (value_text); -int -find_district_index_by_name (char const * name) -{ - if ((name == NULL) || (name[0] == '\0')) - return -1; + } else if (slice_matches_str (key, "resource_prereq_on_tile")) { + if (def->resource_prereq_on_tile != NULL) { + free (def->resource_prereq_on_tile); + def->resource_prereq_on_tile = NULL; + } + def->resource_prereq_on_tile = copy_trimmed_string_or_null (value, 1); + def->has_resource_prereq_on_tile = true; - for (int i = 0; i < is->district_count; i++) { - char const * existing = is->district_configs[i].name; - if ((existing != NULL) && (strcmp (existing, name) == 0)) - return i; - } + } else if (slice_matches_str (key, "wonder_prereqs")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->wonder_prereqs, + ARRAY_LEN (def->wonder_prereqs), + &list_count, + parse_errors, + line_number, + "wonder_prereqs")) { + def->wonder_prereq_count = list_count; + def->has_wonder_prereqs = true; + } else { + def->wonder_prereq_count = 0; + def->has_wonder_prereqs = false; + } + free (value_text); - return -1; -} + } else if (slice_matches_str (key, "natural_wonder_prereqs")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->natural_wonder_prereqs, + ARRAY_LEN (def->natural_wonder_prereqs), + &list_count, + parse_errors, + line_number, + "natural_wonder_prereqs")) { + def->natural_wonder_prereq_count = list_count; + def->has_natural_wonder_prereqs = true; + } else { + def->natural_wonder_prereq_count = 0; + def->has_natural_wonder_prereqs = false; + } + free (value_text); -int -find_wonder_district_index_by_name (char const * name) -{ - if ((name == NULL) || (name[0] == '\0')) - return -1; + } else if (slice_matches_str (key, "buildable_on_districts")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->buildable_on_districts, + ARRAY_LEN (def->buildable_on_districts), + &list_count, + parse_errors, + line_number, + "buildable_on_districts")) { + def->buildable_on_district_count = list_count; + def->has_buildable_on_districts = true; + } else { + def->buildable_on_district_count = 0; + def->has_buildable_on_districts = false; + } + free (value_text); - int improv_id; - if (! stable_look_up (&is->building_name_to_id, (char *)name, &improv_id)) - return -1; + } else if (slice_matches_str (key, "buildable_by_civs")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->buildable_by_civs, + ARRAY_LEN (def->buildable_by_civs), + &list_count, + parse_errors, + line_number, + "buildable_by_civs")) { + def->buildable_by_civ_count = list_count; + def->has_buildable_by_civs = true; + } else { + def->buildable_by_civ_count = 0; + def->has_buildable_by_civs = false; + } + free (value_text); - return find_wonder_config_index_by_improvement_id (improv_id); -} + } else if (slice_matches_str (key, "buildable_by_civ_traits")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->buildable_by_civ_traits, + ARRAY_LEN (def->buildable_by_civ_traits), + &list_count, + parse_errors, + line_number, + "buildable_by_civ_traits")) { + def->buildable_by_civ_traits_count = list_count; + def->buildable_by_civ_traits_id_count = 0; + for (int i = 0; i < def->buildable_by_civ_traits_count; i++) { + char const * trait_name = def->buildable_by_civ_traits[i]; + if ((trait_name == NULL) || (trait_name[0] == '\0')) + continue; + int trait_id = -1; + struct string_slice trait_slice = { .str = (char *)trait_name, .len = (int)strlen (trait_name) }; + if (find_civ_trait_id_by_name (&trait_slice, &trait_id)) { + bool already_listed = false; + for (int k = 0; k < def->buildable_by_civ_traits_id_count; k++) { + if (def->buildable_by_civ_traits_ids[k] == trait_id) { + already_listed = true; + break; + } + } + if ((! already_listed) && (def->buildable_by_civ_traits_id_count < ARRAY_LEN (def->buildable_by_civ_traits_ids))) + def->buildable_by_civ_traits_ids[def->buildable_by_civ_traits_id_count++] = trait_id; + } else { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: buildable_by_civ_traits entry \"%.*s\" not found", line_number, trait_slice.len, trait_slice.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + } -int -find_natural_wonder_index_by_name (char const * name) -{ - if ((name == NULL) || (name[0] == '\0') || (is == NULL)) - return -1; + for (int i = 0; i < def->buildable_by_civ_traits_count; i++) { + if (def->buildable_by_civ_traits[i] != NULL) { + free (def->buildable_by_civ_traits[i]); + def->buildable_by_civ_traits[i] = NULL; + } + } + def->buildable_by_civ_traits_count = 0; + def->has_buildable_by_civ_traits = true; + } else { + def->buildable_by_civ_traits_count = 0; + def->buildable_by_civ_traits_id_count = 0; + def->has_buildable_by_civ_traits = false; + } + free (value_text); + + } else if (slice_matches_str (key, "buildable_by_civ_govs")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->buildable_by_civ_govs, + ARRAY_LEN (def->buildable_by_civ_govs), + &list_count, + parse_errors, + line_number, + "buildable_by_civ_govs")) { + def->buildable_by_civ_govs_count = list_count; + def->buildable_by_civ_govs_id_count = 0; + for (int i = 0; i < def->buildable_by_civ_govs_count; i++) { + char const * gov_name = def->buildable_by_civ_govs[i]; + if ((gov_name == NULL) || (gov_name[0] == '\0')) + continue; + int gov_id = -1; + struct string_slice gov_slice = { .str = (char *)gov_name, .len = (int)strlen (gov_name) }; + if (find_game_object_id_by_name (GOK_GOVERNMENT, &gov_slice, 0, &gov_id)) { + bool already_listed = false; + for (int k = 0; k < def->buildable_by_civ_govs_id_count; k++) { + if (def->buildable_by_civ_govs_ids[k] == gov_id) { + already_listed = true; + break; + } + } + if ((! already_listed) && (def->buildable_by_civ_govs_id_count < ARRAY_LEN (def->buildable_by_civ_govs_ids))) + def->buildable_by_civ_govs_ids[def->buildable_by_civ_govs_id_count++] = gov_id; + } else { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: buildable_by_civ_govs entry \"%.*s\" not found", line_number, gov_slice.len, gov_slice.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + } - for (int i = 0; i < is->natural_wonder_count; i++) { - char const * existing = is->natural_wonder_configs[i].name; - if ((existing != NULL) && (strcmp (existing, name) == 0)) - return i; - } - return -1; -} + for (int i = 0; i < def->buildable_by_civ_govs_count; i++) { + if (def->buildable_by_civ_govs[i] != NULL) { + free (def->buildable_by_civ_govs[i]); + def->buildable_by_civ_govs[i] = NULL; + } + } + def->buildable_by_civ_govs_count = 0; + def->has_buildable_by_civ_govs = true; + } else { + def->buildable_by_civ_govs_count = 0; + def->buildable_by_civ_govs_id_count = 0; + def->has_buildable_by_civ_govs = false; + } + free (value_text); -City * -find_city_by_name (char const * name) -{ - if ((name == NULL) || (name[0] == '\0') || (p_cities == NULL) || (p_cities->Cities == NULL)) - return NULL; + } else if (slice_matches_str (key, "buildable_by_civ_cultures")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->buildable_by_civ_cultures, + ARRAY_LEN (def->buildable_by_civ_cultures), + &list_count, + parse_errors, + line_number, + "buildable_by_civ_cultures")) { + def->buildable_by_civ_cultures_count = list_count; + def->buildable_by_civ_cultures_id_count = 0; + for (int i = 0; i < def->buildable_by_civ_cultures_count; i++) { + char const * culture_name = def->buildable_by_civ_cultures[i]; + if ((culture_name == NULL) || (culture_name[0] == '\0')) + continue; + int culture_id = -1; + struct string_slice culture_slice = { .str = (char *)culture_name, .len = (int)strlen (culture_name) }; + if (find_civ_culture_id_by_name (&culture_slice, &culture_id)) { + bool already_listed = false; + for (int k = 0; k < def->buildable_by_civ_cultures_id_count; k++) { + if (def->buildable_by_civ_cultures_ids[k] == culture_id) { + already_listed = true; + break; + } + } + if ((! already_listed) && (def->buildable_by_civ_cultures_id_count < ARRAY_LEN (def->buildable_by_civ_cultures_ids))) + def->buildable_by_civ_cultures_ids[def->buildable_by_civ_cultures_id_count++] = culture_id; + } else { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: buildable_by_civ_cultures entry \"%.*s\" not found", line_number, culture_slice.len, culture_slice.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + } - for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { - City * city = get_city_ptr (city_index); - if ((city != NULL) && (city->Body.CityName != NULL)) { - (*p_OutputDebugStringA) (city->Body.CityName); + for (int i = 0; i < def->buildable_by_civ_cultures_count; i++) { + if (def->buildable_by_civ_cultures[i] != NULL) { + free (def->buildable_by_civ_cultures[i]); + def->buildable_by_civ_cultures[i] = NULL; + } + } + def->buildable_by_civ_cultures_count = 0; + def->has_buildable_by_civ_cultures = true; + } else { + def->buildable_by_civ_cultures_count = 0; + def->buildable_by_civ_cultures_id_count = 0; + def->has_buildable_by_civ_cultures = false; } - if ((city != NULL) && (city->Body.CityName != NULL) && (strcmp (city->Body.CityName, name) == 0)) - return city; - } + free (value_text); - return NULL; -} + } else if (slice_matches_str (key, "buildable_by_war_allies")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->buildable_by_war_allies = (ival != 0); + def->has_buildable_by_war_allies = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); -void -set_wonders_dependent_on_wonder_district (void) -{ - if (! is->current_config.enable_districts || - ! is->current_config.enable_wonder_districts) - return; + } else if (slice_matches_str (key, "buildable_by_pact_allies")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->buildable_by_pact_allies = (ival != 0); + def->has_buildable_by_pact_allies = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - struct district_config * cfg = &is->district_configs[WONDER_DISTRICT_ID]; - for (int wi = 0; wi < is->wonder_district_count; wi++) { - char const * wonder_name = is->wonder_district_configs[wi].wonder_name; - if ((wonder_name == NULL) || (wonder_name[0] == '\0')) - continue; - if (district_config_has_dependent_improvement (cfg, wonder_name)) - continue; + } else if (slice_matches_str (key, "img_paths")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->img_paths, + ARRAY_LEN (def->img_paths), + &list_count, + parse_errors, + line_number, + "img_paths")) { + def->img_path_count = list_count; + def->has_img_paths = true; + } else { + def->img_path_count = 0; + def->has_img_paths = false; + } + free (value_text); - int dest = cfg->dependent_improvement_count; - if (dest >= ARRAY_LEN (cfg->dependent_improvements)) { - continue; + } else if (slice_matches_str (key, "dependent_improvs")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->dependent_improvements, + ARRAY_LEN (def->dependent_improvements), + &list_count, + parse_errors, + line_number, + "dependent_improvs")) { + def->dependent_improvement_count = list_count; + def->has_dependent_improvements = true; + } else { + def->dependent_improvement_count = 0; + def->has_dependent_improvements = false; } + free (value_text); - char * copy = strdup (wonder_name); - if (copy == NULL) { - continue; + } else if (slice_matches_str (key, "buildable_on")) { + unsigned int mask; + if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number)) { + def->buildable_square_types_mask = mask; + def->has_buildable_on = true; } - cfg->dependent_improvements[dest] = copy; - cfg->dependent_improvement_count = dest + 1; - } + } else if (slice_matches_str (key, "allow_multiple")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->allow_multiple = (ival != 0); + def->has_allow_multiple = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - if (cfg->max_building_index < cfg->dependent_improvement_count) - cfg->max_building_index = cfg->dependent_improvement_count; -} + } else if (slice_matches_str (key, "vary_img_by_era")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->vary_img_by_era = (ival != 0); + def->has_vary_img_by_era = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); -void -resolve_district_bonus_building_entries (struct district_bonus_list * list, - char const * district_name, - char const * bonus_name, - struct error_line ** parse_errors) -{ - if (list == NULL) - return; + } else if (slice_matches_str (key, "vary_img_by_culture")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->vary_img_by_culture = (ival != 0); + def->has_vary_img_by_culture = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - for (int i = 0; i < list->count; i++) { - struct district_bonus_entry * entry = &list->entries[i]; - if (entry->type != DBET_BUILDING) - continue; - if ((entry->building_name == NULL) || (entry->building_name[0] == '\0')) { - entry->building_id = -1; - continue; + } else if (slice_matches_str (key, "render_strategy")) { + char * strategy = copy_trimmed_string_or_null (value, 1); + if (strategy == NULL) { + def->has_render_strategy = false; + add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); + } else if (strcmp (strategy, "by-count") == 0) { + def->render_strategy = DRS_BY_COUNT; + def->has_render_strategy = true; + } else if (strcmp (strategy, "by-building") == 0) { + def->render_strategy = DRS_BY_BUILDING; + def->has_render_strategy = true; + } else { + def->has_render_strategy = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected \"by-count\" or \"by-building\")"); } + if (strategy != NULL) + free (strategy); - int improv_id; - struct string_slice improv_name = { .str = (char *)entry->building_name, .len = (int)strlen (entry->building_name) }; - if (find_game_object_id_by_name (GOK_BUILDING, &improv_name, 0, &improv_id)) { - entry->building_id = improv_id; - stable_insert (&is->building_name_to_id, improv_name.str, improv_id); + } else if (slice_matches_str (key, "ai_build_strategy")) { + char * strategy = copy_trimmed_string_or_null (value, 1); + if (strategy == NULL) { + def->has_ai_build_strategy = false; + add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); + } else if (strcmp (strategy, "district") == 0) { + def->ai_build_strategy = DABS_DISTRICT; + def->has_ai_build_strategy = true; + } else if (strcmp (strategy, "tile-improvement") == 0) { + def->ai_build_strategy = DABS_TILE_IMPROVEMENT; + def->has_ai_build_strategy = true; } else { - entry->building_id = -1; - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ District \"%s\": %s entry \"%.*s\" not found", - district_name, bonus_name, improv_name.len, improv_name.str); - err->text[(sizeof err->text) - 1] = '\0'; + def->has_ai_build_strategy = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected \"district\" or \"tile-improvement\")"); } - } -} + if (strategy != NULL) + free (strategy); -void parse_building_and_tech_ids () -{ - struct c3x_config * cfg = &is->current_config; - char ss[200]; - struct error_line * district_parse_errors = NULL; - struct error_line * wonder_parse_errors = NULL; - for (int i = 0; i < is->district_count; i++) { - char const * district_name = (is->district_configs[i].name != NULL) ? is->district_configs[i].name : "District"; - if (is->district_configs[i].command != 0) - itable_insert (&is->command_id_to_district_id, is->district_configs[i].command, i); - is->district_infos[i].advance_prereq_count = 0; - for (int j = 0; j < ARRAY_LEN (is->district_infos[i].advance_prereq_ids); j++) - is->district_infos[i].advance_prereq_ids[j] = -1; - is->district_infos[i].obsoleted_by_id = -1; - is->district_infos[i].resource_prereq_count = 0; - for (int j = 0; j < MAX_DISTRICT_DEPENDENTS; j++) - is->district_infos[i].resource_prereq_ids[j] = -1; - is->district_infos[i].resource_prereq_on_tile_id = -1; - is->district_infos[i].wonder_prereq_count = 0; - for (int j = 0; j < ARRAY_LEN (is->district_infos[i].wonder_prereq_ids); j++) - is->district_infos[i].wonder_prereq_ids[j] = -1; - is->district_infos[i].natural_wonder_prereq_count = 0; - for (int j = 0; j < ARRAY_LEN (is->district_infos[i].natural_wonder_prereq_ids); j++) - is->district_infos[i].natural_wonder_prereq_ids[j] = -1; + } else if (slice_matches_str (key, "align_to_coast")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->align_to_coast = (ival != 0); + def->has_align_to_coast = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - // Map advance prereqs to districts - int stored_tech_count = 0; - for (int j = 0; j < is->district_configs[i].advance_prereq_count; j++) { - char const * prereq = is->district_configs[i].advance_prereqs[j]; - if (prereq == NULL || prereq[0] == '\0') - continue; - int tech_id; - struct string_slice tech_name = { .str = (char *)prereq, .len = (int)strlen (prereq) }; - if (find_game_object_id_by_name (GOK_TECHNOLOGY, &tech_name, 0, &tech_id)) { - snprintf (ss, sizeof ss, "Found tech prereq \"%.*s\" for district \"%s\", ID %d\n", tech_name.len, tech_name.str, district_name, tech_id); - (*p_OutputDebugStringA) (ss); - if (stored_tech_count < ARRAY_LEN (is->district_infos[i].advance_prereq_ids)) { - is->district_infos[i].advance_prereq_ids[stored_tech_count] = tech_id; - stored_tech_count++; - } - itable_insert (&is->district_tech_prereqs, tech_id, i); - } else { - struct error_line * err = add_error_line (&district_parse_errors); - snprintf (err->text, sizeof err->text, "^ District \"%s\": advance_prereqs entry \"%.*s\" not found", district_name, tech_name.len, tech_name.str); - err->text[(sizeof err->text) - 1] = '\0'; - } - } - is->district_infos[i].advance_prereq_count = stored_tech_count; + } else if (slice_matches_str (key, "draw_over_resources")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->draw_over_resources = (ival != 0); + def->has_draw_over_resources = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - // Map obsoleted_by to tech ID - if (is->district_configs[i].obsoleted_by != NULL && is->district_configs[i].obsoleted_by != "") { - int tech_id; - struct string_slice tech_name = { .str = (char *)is->district_configs[i].obsoleted_by, .len = (int)strlen (is->district_configs[i].obsoleted_by) }; - if (find_game_object_id_by_name (GOK_TECHNOLOGY, &tech_name, 0, &tech_id)) { - snprintf (ss, sizeof ss, "Found tech obsoleted_by \"%.*s\" for district \"%s\", ID %d\n", tech_name.len, tech_name.str, district_name, tech_id); - (*p_OutputDebugStringA) (ss); - is->district_infos[i].obsoleted_by_id = tech_id; - } else { - is->district_infos[i].obsoleted_by_id = -1; - struct error_line * err = add_error_line (&district_parse_errors); - snprintf (err->text, sizeof err->text, "^ District \"%s\": obsoleted_by \"%.*s\" not found", district_name, tech_name.len, tech_name.str); - err->text[(sizeof err->text) - 1] = '\0'; - } - } + } else if (slice_matches_str (key, "allow_irrigation_from")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->allow_irrigation_from = (ival != 0); + def->has_allow_irrigation_from = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - // Map resource prereqs to districts (multiple resources now supported) - int stored_res_count = 0; - for (int j = 0; j < is->district_configs[i].resource_prereq_count; j++) { - if (is->district_configs[i].resource_prereqs[j] == "" || is->district_configs[i].resource_prereqs[j] == NULL) - continue; - int res_id; - struct string_slice res_name = { .str = (char *)is->district_configs[i].resource_prereqs[j], .len = (int)strlen (is->district_configs[i].resource_prereqs[j]) }; - if (find_game_object_id_by_name (GOK_RESOURCE, &res_name, 0, &res_id)) { - snprintf (ss, sizeof ss, "Found resource prereq \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, district_name, res_id); - (*p_OutputDebugStringA) (ss); - if (stored_res_count < ARRAY_LEN (is->district_infos[i].resource_prereq_ids)) { - is->district_infos[i].resource_prereq_ids[stored_res_count] = res_id; - stored_res_count++; - } - } else { - struct error_line * err = add_error_line (&district_parse_errors); - snprintf (err->text, sizeof err->text, "^ District \"%s\": resource_prereq \"%.*s\" not found", district_name, res_name.len, res_name.str); - err->text[(sizeof err->text) - 1] = '\0'; - } - } - is->district_infos[i].resource_prereq_count = stored_res_count; - if (is->district_configs[i].resource_prereq_on_tile != NULL && is->district_configs[i].resource_prereq_on_tile != "") { - int res_id; - struct string_slice res_name = { .str = (char *)is->district_configs[i].resource_prereq_on_tile, .len = (int)strlen (is->district_configs[i].resource_prereq_on_tile) }; - if (find_game_object_id_by_name (GOK_RESOURCE, &res_name, 0, &res_id)) { - snprintf (ss, sizeof ss, "Found on-tile resource prereq \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, district_name, res_id); - (*p_OutputDebugStringA) (ss); - is->district_infos[i].resource_prereq_on_tile_id = res_id; - } else { - is->district_infos[i].resource_prereq_on_tile_id = -1; - struct error_line * err = add_error_line (&district_parse_errors); - snprintf (err->text, sizeof err->text, "^ District \"%s\": resource_prereq_on_tile \"%.*s\" not found", district_name, res_name.len, res_name.str); - err->text[(sizeof err->text) - 1] = '\0'; - } - } + } else if (slice_matches_str (key, "auto_add_road")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->auto_add_road = (ival != 0); + def->has_auto_add_road = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - int stored_wonder_count = 0; - for (int j = 0; j < is->district_configs[i].wonder_prereq_count; j++) { - if (is->district_configs[i].wonder_prereqs[j] == "" || is->district_configs[i].wonder_prereqs[j] == NULL) - continue; - int improv_id; - struct string_slice wonder_name = { .str = (char *)is->district_configs[i].wonder_prereqs[j], .len = (int)strlen (is->district_configs[i].wonder_prereqs[j]) }; - if (find_game_object_id_by_name (GOK_BUILDING, &wonder_name, 0, &improv_id)) { - if (stored_wonder_count < ARRAY_LEN (is->district_infos[i].wonder_prereq_ids)) { - is->district_infos[i].wonder_prereq_ids[stored_wonder_count] = improv_id; - stored_wonder_count += 1; - } - stable_insert (&is->building_name_to_id, wonder_name.str, improv_id); - } else { - struct error_line * err = add_error_line (&district_parse_errors); - snprintf (err->text, sizeof err->text, "^ District \"%s\": wonder_prereqs entry \"%.*s\" not found", district_name, wonder_name.len, wonder_name.str); - err->text[(sizeof err->text) - 1] = '\0'; - } - } - is->district_infos[i].wonder_prereq_count = stored_wonder_count; + } else if (slice_matches_str (key, "auto_add_railroad")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->auto_add_railroad = (ival != 0); + def->has_auto_add_railroad = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - int stored_natural_wonder_count = 0; - for (int j = 0; j < is->district_configs[i].natural_wonder_prereq_count; j++) { - if (is->district_configs[i].natural_wonder_prereqs[j] == "" || is->district_configs[i].natural_wonder_prereqs[j] == NULL) - continue; - int natural_wonder_id = -1; - char const * name = is->district_configs[i].natural_wonder_prereqs[j]; - if (stable_look_up (&is->natural_wonder_name_to_id, (char *)name, &natural_wonder_id)) { - if (stored_natural_wonder_count < ARRAY_LEN (is->district_infos[i].natural_wonder_prereq_ids)) { - is->district_infos[i].natural_wonder_prereq_ids[stored_natural_wonder_count] = natural_wonder_id; - stored_natural_wonder_count += 1; - } - } else { - struct error_line * err = add_error_line (&district_parse_errors); - snprintf (err->text, sizeof err->text, "^ District \"%s\": natural_wonder_prereqs entry \"%s\" not found", district_name, name); - err->text[(sizeof err->text) - 1] = '\0'; - } - } - is->district_infos[i].natural_wonder_prereq_count = stored_natural_wonder_count; + } else if (slice_matches_str (key, "custom_width")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->custom_width = ival; + def->has_custom_width = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - for (int j = 0; j < ARRAY_LEN (is->district_configs[i].buildable_on_district_ids); j++) - is->district_configs[i].buildable_on_district_ids[j] = -1; - is->district_configs[i].buildable_on_district_id_count = 0; + } else if (slice_matches_str (key, "custom_height")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->custom_height = ival; + def->has_custom_height = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - if (is->district_configs[i].has_buildable_on_districts) { - int stored_buildable_on_count = 0; - for (int j = 0; j < is->district_configs[i].buildable_on_district_count; j++) { - char const * name = is->district_configs[i].buildable_on_districts[j]; - if (name == NULL || name[0] == '\0') - continue; - int other_district_id = find_district_index_by_name (name); - if (other_district_id >= 0) { - if (stored_buildable_on_count < ARRAY_LEN (is->district_configs[i].buildable_on_district_ids)) { - is->district_configs[i].buildable_on_district_ids[stored_buildable_on_count] = other_district_id; - stored_buildable_on_count += 1; - } - } else { - struct error_line * err = add_error_line (&district_parse_errors); - snprintf (err->text, sizeof err->text, "^ District \"%s\": buildable_on_districts entry \"%s\" not found", district_name, name); - err->text[(sizeof err->text) - 1] = '\0'; - } - } - is->district_configs[i].buildable_on_district_id_count = stored_buildable_on_count; - } + } else if (slice_matches_str (key, "x_offset")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->x_offset = ival; + def->has_x_offset = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + + } else if (slice_matches_str (key, "y_offset")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->y_offset = ival; + def->has_y_offset = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + + } else if (slice_matches_str (key, "btn_tile_sheet_column")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->btn_tile_sheet_column = ival; + def->has_btn_tile_sheet_column = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + + } else if (slice_matches_str (key, "btn_tile_sheet_row")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->btn_tile_sheet_row = ival; + def->has_btn_tile_sheet_row = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - // Resolve generated resource name to ID - if (is->district_configs[i].generated_resource != NULL && is->district_configs[i].generated_resource != "") { - int res_id; - struct string_slice res_name = { .str = (char *)is->district_configs[i].generated_resource, .len = (int)strlen (is->district_configs[i].generated_resource) }; - if (find_game_object_id_by_name (GOK_RESOURCE, &res_name, 0, &res_id)) { - snprintf (ss, sizeof ss, "Found generated resource \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, district_name, res_id); - (*p_OutputDebugStringA) (ss); - is->district_configs[i].generated_resource_id = res_id; - } else { - is->district_configs[i].generated_resource_id = -1; - struct error_line * err = add_error_line (&district_parse_errors); - snprintf (err->text, sizeof err->text, "^ District \"%s\": generated_resource \"%.*s\" not found", district_name, res_name.len, res_name.str); - err->text[(sizeof err->text) - 1] = '\0'; - } + } else if (slice_matches_str (key, "defense_bonus_percent")) { + if (parse_district_bonus_entries (value, &def->defense_bonus_percent, &def->defense_bonus_extras, parse_errors, line_number, key)) { + def->has_defense_bonus_percent = true; + } else { + def->has_defense_bonus_percent = false; } - // Map improvement prereqs to districts - int stored_count = 0; - for (int j = 0; j < is->district_configs[i].dependent_improvement_count; j++) { - int improv_id; - if (is->district_configs[i].dependent_improvements[j] == "" || is->district_configs[i].dependent_improvements[j] == NULL) - continue; + } else if (slice_matches_str (key, "heal_units_in_one_turn")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->heal_units_in_one_turn = (ival != 0); + def->has_heal_units_in_one_turn = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); - // Gate wonder district prereqs behind enable_wonder_districts - if ((is->district_configs[i].command == UCV_Build_WonderDistrict) && (! cfg->enable_wonder_districts)) - continue; + } else if (slice_matches_str (key, "culture_bonus")) { + if (parse_district_bonus_entries (value, &def->culture_bonus, &def->culture_bonus_extras, parse_errors, line_number, key)) { + def->has_culture_bonus = true; + } else { + def->has_culture_bonus = false; + } - struct string_slice improv_name = { .str = (char *)is->district_configs[i].dependent_improvements[j], .len = (int)strlen (is->district_configs[i].dependent_improvements[j]) }; - if (find_game_object_id_by_name (GOK_BUILDING, &improv_name, 0, &improv_id)) { - snprintf (ss, sizeof ss, "Found improvement prereq \"%.*s\" for district \"%s\", ID %d\n", improv_name.len, improv_name.str, district_name, improv_id); - (*p_OutputDebugStringA) (ss); - if (stored_count < ARRAY_LEN (is->district_infos[i].dependent_building_ids)) { - is->district_infos[i].dependent_building_ids[stored_count] = improv_id; - stored_count += 1; - } - add_district_building_prereq (improv_id, i); - stable_insert (&is->building_name_to_id, improv_name.str, improv_id); - } else { - is->district_infos[i].dependent_building_ids[j] = -1; - struct error_line * err = add_error_line (&district_parse_errors); - snprintf (err->text, sizeof err->text, "^ District \"%s\": dependent_improvs entry \"%.*s\" not found", district_name, improv_name.len, improv_name.str); - err->text[(sizeof err->text) - 1] = '\0'; - } - is->district_infos[i].dependent_building_count = stored_count; + } else if (slice_matches_str (key, "science_bonus")) { + if (parse_district_bonus_entries (value, &def->science_bonus, &def->science_bonus_extras, parse_errors, line_number, key)) { + def->has_science_bonus = true; + } else { + def->has_science_bonus = false; } - resolve_district_bonus_building_entries (&is->district_configs[i].culture_bonus_extras, district_name, "culture_bonus", &district_parse_errors); - resolve_district_bonus_building_entries (&is->district_configs[i].science_bonus_extras, district_name, "science_bonus", &district_parse_errors); - resolve_district_bonus_building_entries (&is->district_configs[i].food_bonus_extras, district_name, "food_bonus", &district_parse_errors); - resolve_district_bonus_building_entries (&is->district_configs[i].gold_bonus_extras, district_name, "gold_bonus", &district_parse_errors); - resolve_district_bonus_building_entries (&is->district_configs[i].shield_bonus_extras, district_name, "shield_bonus", &district_parse_errors); - resolve_district_bonus_building_entries (&is->district_configs[i].happiness_bonus_extras, district_name, "happiness_bonus", &district_parse_errors); - resolve_district_bonus_building_entries (&is->district_configs[i].defense_bonus_extras, district_name, "defense_bonus_percent", &district_parse_errors); - } + } else if (slice_matches_str (key, "food_bonus")) { + if (parse_district_bonus_entries (value, &def->food_bonus, &def->food_bonus_extras, parse_errors, line_number, key)) { + def->has_food_bonus = true; + } else { + def->has_food_bonus = false; + } - // Map wonder names to their improvement IDs for rendering under-construction wonders - for (int wi = 0; wi < is->wonder_district_count; wi++) { - if (is->wonder_district_configs[wi].wonder_name == NULL || is->wonder_district_configs[wi].wonder_name[0] == '\0') - continue; + } else if (slice_matches_str (key, "gold_bonus")) { + if (parse_district_bonus_entries (value, &def->gold_bonus, &def->gold_bonus_extras, parse_errors, line_number, key)) { + def->has_gold_bonus = true; + } else { + def->has_gold_bonus = false; + } - int improv_id; - struct string_slice wonder_name = { .str = (char *)is->wonder_district_configs[wi].wonder_name, .len = (int)strlen (is->wonder_district_configs[wi].wonder_name) }; - if (find_game_object_id_by_name (GOK_BUILDING, &wonder_name, 0, &improv_id)) { - snprintf (ss, sizeof ss, "Found improvement prereq \"%.*s\" for wonder district \"%s\", ID %d\n", wonder_name.len, wonder_name.str, is->wonder_district_configs[wi].wonder_name, improv_id); - (*p_OutputDebugStringA) (ss); - stable_insert (&is->building_name_to_id, wonder_name.str, improv_id); + } else if (slice_matches_str (key, "shield_bonus")) { + if (parse_district_bonus_entries (value, &def->shield_bonus, &def->shield_bonus_extras, parse_errors, line_number, key)) { + def->has_shield_bonus = true; } else { - snprintf (ss, sizeof ss, "Could not find improvement prereq \"%.*s\" for wonder district \"%s\"\n", wonder_name.len, wonder_name.str, is->wonder_district_configs[wi].wonder_name); - (*p_OutputDebugStringA) (ss); - struct error_line * err = add_error_line (&wonder_parse_errors); - snprintf (err->text, sizeof err->text, "^ Wonder district \"%s\": improvement \"%.*s\" not found", (is->wonder_district_configs[wi].wonder_name != NULL) ? is->wonder_district_configs[wi].wonder_name : "Wonder District", wonder_name.len, wonder_name.str); - err->text[(sizeof err->text) - 1] = '\0'; + def->has_shield_bonus = false; } - } - if ((district_parse_errors != NULL) || (wonder_parse_errors != NULL)) { - PopupForm * popup = get_popup_form (); - popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_WARNING", -1, 0, 0, 0); + } else if (slice_matches_str (key, "happiness_bonus")) { + if (parse_district_bonus_entries (value, &def->happiness_bonus, &def->happiness_bonus_extras, parse_errors, line_number, key)) { + def->has_happiness_bonus = true; + } else { + def->has_happiness_bonus = false; + } - if (district_parse_errors != NULL) { - char header[256]; - if (is->current_districts_config_path[0] != '\0') - snprintf (header, sizeof header, "District Config errors in %s:", is->current_districts_config_path); - else - snprintf (header, sizeof header, "District Config lookup errors:"); - header[(sizeof header) - 1] = '\0'; - PopupForm_add_text (popup, __, header, false); - for (struct error_line * line = district_parse_errors; line != NULL; line = line->next) - PopupForm_add_text (popup, __, line->text, false); + } else if (slice_matches_str (key, "generated_resource")) { + if (def->generated_resource != NULL) { + free (def->generated_resource); + def->generated_resource = NULL; } + def->generated_resource_flags = 0; - if ((district_parse_errors != NULL) && (wonder_parse_errors != NULL)) - PopupForm_add_text (popup, __, "", false); + char * value_text = trim_and_extract_slice (value, 0); + if ((value_text == NULL) || (*value_text == '\0')) { + def->generated_resource = NULL; + def->has_generated_resource = true; + } else { + char * cursor = value_text; + struct string_slice resource_name = {0}; + struct string_slice token; + bool ok = true; + while (skip_white_space (&cursor) && parse_string (&cursor, &token)) { + if (slice_matches_str (&token, "local")) + def->generated_resource_flags |= MF_LOCAL; + else if (slice_matches_str (&token, "no-tech-req")) + def->generated_resource_flags |= MF_NO_TECH_REQ; + else if (slice_matches_str (&token, "yields")) + def->generated_resource_flags |= MF_YIELDS; + else if (resource_name.str == NULL) + resource_name = token; + else { + ok = false; + break; + } + } - if (wonder_parse_errors != NULL) { - char header[256]; - snprintf (header, sizeof header, "Wonder District Config lookup errors:"); - header[(sizeof header) - 1] = '\0'; - PopupForm_add_text (popup, __, header, false); - for (struct error_line * line = wonder_parse_errors; line != NULL; line = line->next) - PopupForm_add_text (popup, __, line->text, false); + if (! ok || (resource_name.str == NULL)) { + def->generated_resource = NULL; + def->has_generated_resource = false; + def->generated_resource_flags = 0; + add_key_parse_error (parse_errors, line_number, key, value, + "(expected resource name plus optional flags: local, yields, no-tech-req)"); + } else { + def->generated_resource = extract_slice (&resource_name); + def->has_generated_resource = true; + } } + free (value_text); - patch_show_popup (popup, __, 0, 0); - free_error_lines (district_parse_errors); - free_error_lines (wonder_parse_errors); - } + } else + add_unrecognized_key_error (unrecognized_keys, line_number, key); } -void -load_districts_config () +bool +line_is_empty_or_comment (struct string_slice const * trimmed) { - clear_dynamic_district_definitions (); - load_dynamic_district_configs (); - load_dynamic_wonder_configs (); - load_natural_wonder_configs (); - is->district_count = is->special_district_count + is->dynamic_district_count; - - set_wonders_dependent_on_wonder_district (); - parse_building_and_tech_ids (); + return ((trimmed == NULL) || (trimmed->len == 0) || (trimmed->str[0] == ';')); } void -place_natural_wonders_on_map (void) +load_dynamic_district_config_file (char const * file_path, + int path_is_relative_to_mod_dir, + int log_missing, + int drop_existing_configs) { - if (! is->current_config.enable_natural_wonders) - return; + char path[MAX_PATH]; + if (path_is_relative_to_mod_dir) { + if (is->mod_rel_dir == NULL) + return; + snprintf (path, sizeof path, "%s\\%s", is->mod_rel_dir, file_path); + } else { + strncpy (path, file_path, sizeof path); + } + path[(sizeof path) - 1] = '\0'; - int wonder_count = is->natural_wonder_count; - if (wonder_count <= 0) + char * text = file_to_string (path); + if (text == NULL) { + if (log_missing) { + char ss[256]; + snprintf (ss, sizeof ss, "[C3X] Districts config file not found: %s", path); + (*p_OutputDebugStringA) (ss); + } return; + } - struct natural_wonder_candidate_list * candidate_lists = (struct natural_wonder_candidate_list *)calloc (wonder_count, sizeof *candidate_lists); - bool * already_placed = (bool *)calloc (wonder_count, sizeof *already_placed); + if (drop_existing_configs) + reset_regular_district_configs (); - if ((candidate_lists == NULL) || (already_placed == NULL)) { - if (candidate_lists != NULL) free (candidate_lists); - if (already_placed != NULL) free (already_placed); - return; - } + struct parsed_district_definition def; + init_parsed_district_definition (&def); + bool in_section = false; + int section_start_line = 0; + int line_number = 0; + struct error_line * unrecognized_keys = NULL; + struct error_line * parse_errors = NULL; - struct wonder_location * placements = NULL; - int placement_count = 0; - int placement_capacity = 0; - int existing_count = 0; + char * cursor = text; + while (*cursor != '\0') { + line_number += 1; - // Record existing natural wonders - FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { - struct district_instance * inst = (struct district_instance *)tei.value; - if ((inst == NULL) || (inst->district_id != NATURAL_WONDER_DISTRICT_ID)) - continue; + char * line_start = cursor; + char * line_end = cursor; + while ((*line_end != '\0') && (*line_end != '\n')) + line_end++; + + int line_len = line_end - line_start; + bool has_newline = (*line_end == '\n'); + if (has_newline) + *line_end = '\0'; - int wonder_id = inst->natural_wonder_info.natural_wonder_id; - if ((wonder_id < 0) || (wonder_id >= wonder_count)) + struct string_slice line_slice = { .str = line_start, .len = line_len }; + struct string_slice trimmed = trim_string_slice (&line_slice, 0); + if (line_is_empty_or_comment (&trimmed)) { + cursor = has_newline ? line_end + 1 : line_end; continue; + } - already_placed[wonder_id] = true; - - Tile * tile = (Tile *)tei.key; - int tile_x, tile_y; - if (! district_instance_get_coords (inst, tile, &tile_x, &tile_y)) + if (trimmed.str[0] == '#') { + struct string_slice directive = trimmed; + directive.str += 1; + directive.len -= 1; + directive = trim_string_slice (&directive, 0); + if ((directive.len > 0) && slice_matches_str (&directive, "District")) { + if (in_section) + finalize_parsed_district_definition (&def, section_start_line); + in_section = true; + section_start_line = line_number; + } + cursor = has_newline ? line_end + 1 : line_end; continue; + } - if (placement_count >= placement_capacity) { - int new_capacity = (placement_capacity > 0) ? placement_capacity * 2 : 8; - struct wonder_location * grown = - (struct wonder_location *)realloc (placements, new_capacity * sizeof *grown); - if (grown == NULL) - continue; - placements = grown; - placement_capacity = new_capacity; + if (! in_section) { + cursor = has_newline ? line_end + 1 : line_end; + continue; } - if (placements != NULL) { - placements[placement_count++] = (struct wonder_location){ - .x = (short)tile_x, - .y = (short)tile_y - }; - existing_count += 1; + struct string_slice key_slice = {0}; + struct string_slice value_slice = {0}; + enum key_value_parse_status status = parse_trimmed_key_value (&trimmed, &key_slice, &value_slice); + if (status == KVP_NO_EQUALS) { + char * line_text = extract_slice (&trimmed); + struct error_line * err = add_error_line (&parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %s (expected '=')", line_number, line_text); + err->text[(sizeof err->text) - 1] = '\0'; + free (line_text); + cursor = has_newline ? line_end + 1 : line_end; + continue; + } else if (status == KVP_EMPTY_KEY) { + struct error_line * err = add_error_line (&parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: (missing key)", line_number); + err->text[(sizeof err->text) - 1] = '\0'; + cursor = has_newline ? line_end + 1 : line_end; + continue; } - } - // Build candidate lists - int map_width = p_bic_data->Map.Width; - int map_height = p_bic_data->Map.Height; - int minimum_separation = is->current_config.minimum_natural_wonder_separation; - - for (int y = 0; y < map_height; y++) { - for (int x = 0; x < map_width; x++) { - Tile * tile = tile_at (x, y); - if ((tile == NULL) || (tile == p_null_tile)) - continue; + handle_district_definition_key (&def, &key_slice, &value_slice, line_number, &parse_errors, &unrecognized_keys); + cursor = has_newline ? line_end + 1 : line_end; + } - if (! natural_wonder_tile_is_clear (tile, x, y)) continue; + if (in_section) + finalize_parsed_district_definition (&def, section_start_line); - if (natural_wonder_exists_within_distance (x, y, minimum_separation)) - continue; + free_parsed_district_definition (&def); + free (text); - for (int ni = 0; ni < wonder_count; ni++) { - if (already_placed[ni]) - continue; + // Append to loaded config names list + struct loaded_config_name * top_lcn = is->loaded_config_names; + while (top_lcn->next != NULL) + top_lcn = top_lcn->next; - struct natural_wonder_district_config const * cfg = &is->natural_wonder_configs[ni]; - if (cfg->name == NULL) - continue; + struct loaded_config_name * new_lcn = malloc (sizeof *new_lcn); + new_lcn->name = strdup (path); + new_lcn->next = NULL; - if (! natural_wonder_terrain_matches (cfg, tile, x, y)) - continue; + top_lcn->next = new_lcn; + snprintf (is->current_districts_config_path, sizeof is->current_districts_config_path, path); - natural_wonder_candidate_list_push (&candidate_lists[ni], tile, x, y); - } + if (parse_errors != NULL || unrecognized_keys != NULL) { + PopupForm * popup = get_popup_form (); + popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_WARNING", -1, 0, 0, 0); + char s[200]; + snprintf (s, sizeof s, "District Config errors in %s:", path); + s[(sizeof s) - 1] = '\0'; + PopupForm_add_text (popup, __, s, false); + if (parse_errors != NULL) { + for (struct error_line * line = parse_errors; line != NULL; line = line->next) + PopupForm_add_text (popup, __, line->text, false); } - } - - bool wraps_horiz = (p_bic_data->Map.Flags & 1) != 0; - int newly_placed = 0; - int * wonder_order = (int *)malloc (wonder_count * sizeof *wonder_order); - if (wonder_order != NULL) { - for (int i = 0; i < wonder_count; i++) - wonder_order[i] = i; - for (int i = wonder_count - 1; i > 0; i--) { - int swap_index = rand_int (p_rand_object, __, i + 1); - int temp = wonder_order[i]; - wonder_order[i] = wonder_order[swap_index]; - wonder_order[swap_index] = temp; + if (unrecognized_keys != NULL) { + PopupForm_add_text (popup, __, "", false); + PopupForm_add_text (popup, __, "Unrecognized keys:", false); + for (struct error_line * line = unrecognized_keys; line != NULL; line = line->next) + PopupForm_add_text (popup, __, line->text, false); } + patch_show_popup (popup, __, 0, 0); + free_error_lines (parse_errors); + free_error_lines (unrecognized_keys); } +} - for (int order_index = 0; order_index < wonder_count; order_index++) { - int ni = (wonder_order != NULL) ? wonder_order[order_index] : order_index; - if (already_placed[ni]) - continue; +void +load_dynamic_district_configs () +{ + load_dynamic_district_config_file ("default.districts_config.txt", 1, 1, 1); + load_dynamic_district_config_file ("user.districts_config.txt", 1, 0, 1); - struct natural_wonder_candidate_list * list = &candidate_lists[ni]; - if (list->count == 0) { - char msg[256]; - snprintf (msg, sizeof msg, "[C3X] No valid tiles to place natural wonder \"%s\".\n", - (is->natural_wonder_configs[ni].name != NULL) ? is->natural_wonder_configs[ni].name : "Natural Wonder"); - (*p_OutputDebugStringA) (msg); - continue; - } + + char * scenario_filename = "scenario.districts_config.txt"; + char * scenario_district_config_path = BIC_get_asset_path (p_bic_data, __, scenario_filename, false); + if ((scenario_district_config_path != NULL) && (0 != strcmp (scenario_filename, scenario_district_config_path))) + load_dynamic_district_config_file (scenario_district_config_path, 0, 0, 1); +} - int best_index = -1; - int best_dist = -1; - int best_adjacent_count = -1; - int best_target_diff = INT_MAX; - int best_rand = INT_MAX; - int best_same_type_count = -1; - int best_continent_priority = INT_MAX; - int target_x = (wonder_count > 0) - ? (int)(((long long)(2 * ni + 1) * map_width) / (2 * wonder_count)) - : (map_width >> 1); +void +init_parsed_wonder_definition (struct parsed_wonder_definition * def) +{ + memset (def, 0, sizeof *def); + def->buildable_square_types_mask = district_default_buildable_mask (); +} - for (int ci = 0; ci < list->count; ci++) { - struct natural_wonder_candidate * cand = &list->entries[ci]; - Tile * tile = cand->tile; - if ((tile == NULL) || (tile == p_null_tile)) continue; - if (get_district_instance (tile) != NULL) continue; - if (! natural_wonder_tile_is_clear (tile, cand->x, cand->y)) continue; - if (! natural_wonder_terrain_matches (&is->natural_wonder_configs[ni], tile, cand->x, cand->y)) continue; - if (natural_wonder_exists_within_distance (cand->x, cand->y, minimum_separation)) continue; +void +free_parsed_wonder_definition (struct parsed_wonder_definition * def) +{ + if (def->name != NULL) { + free (def->name); + def->name = NULL; + } - int min_dist_sq = natural_wonder_min_distance_sq (cand->x, cand->y, placements, placement_count); - if (min_dist_sq == INT_MAX) { - int span = (map_width * map_width) + (map_height * map_height); - if (span <= 0) - span = INT_MAX; - min_dist_sq = span; - } + if (def->img_path != NULL) { + free (def->img_path); + def->img_path = NULL; + } - int dx_raw = int_abs (cand->x - target_x); - int dx_adjusted = compute_wrapped_component (dx_raw, map_width, wraps_horiz); - int rand_val = rand_int (p_rand_object, __, 0x7FFF); + init_parsed_wonder_definition (def); +} - int continent_id = tile->vtable->m46_Get_ContinentID (tile); - int continent_priority = 1; - if ((continent_id >= 0) && ! continent_has_natural_wonder (continent_id, placements, placement_count)) - continent_priority = 0; +bool +add_dynamic_wonder_from_definition (struct parsed_wonder_definition * def, int section_start_line) +{ + int existing_index = -1; + for (int i = 0; i < is->wonder_district_count; i++) { + if ((is->wonder_district_configs[i].wonder_name != NULL) && + (strcmp (is->wonder_district_configs[i].wonder_name, def->name) == 0)) { + existing_index = i; + break; + } + } - bool adjacency_bonus_active = - (is->natural_wonder_configs[ni].adjacent_to != (enum SquareTypes)SQ_INVALID) && - (is->natural_wonder_configs[ni].adjacency_dir == DIR_ZERO); - int adjacency_count = -1; - if (adjacency_bonus_active) - adjacency_count = count_adjacent_tiles_of_type (cand->x, cand->y, - is->natural_wonder_configs[ni].adjacent_to); + int dest = (existing_index >= 0) ? existing_index : is->wonder_district_count; + if ((dest < 0) || (dest >= MAX_WONDER_DISTRICT_TYPES)) + return false; - int same_type_count = count_adjacent_tiles_of_type (cand->x, cand->y, - is->natural_wonder_configs[ni].terrain_type); + struct wonder_district_config new_cfg; + memset (&new_cfg, 0, sizeof new_cfg); + new_cfg.index = dest; + new_cfg.is_dynamic = true; + new_cfg.wonder_name = strdup (def->name); + new_cfg.img_path = (def->img_path != NULL) ? strdup (def->img_path) : strdup ("Wonders.pcx"); + new_cfg.img_row = def->img_row; + new_cfg.img_column = def->img_column; + new_cfg.img_construct_row = def->img_construct_row; + new_cfg.img_construct_column = def->img_construct_column; + new_cfg.img_alt_dir_construct_row = def->img_alt_dir_construct_row; + new_cfg.img_alt_dir_construct_column = def->img_alt_dir_construct_column; + new_cfg.img_alt_dir_row = def->img_alt_dir_row; + new_cfg.img_alt_dir_column = def->img_alt_dir_column; + new_cfg.enable_img_alt_dir = def->enable_img_alt_dir; + new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); - bool better = false; - if (continent_priority < best_continent_priority) - better = true; - else if (continent_priority > best_continent_priority) - continue; + if (existing_index >= 0) { + struct wonder_district_config * cfg = &is->wonder_district_configs[existing_index]; + free_dynamic_wonder_config (cfg); + *cfg = new_cfg; + cfg->index = existing_index; + } else { + struct wonder_district_config * cfg = &is->wonder_district_configs[dest]; + free_dynamic_wonder_config (cfg); + *cfg = new_cfg; + is->wonder_district_count += 1; + } - if (! better && adjacency_bonus_active) { - if (adjacency_count > best_adjacent_count) - better = true; - else if (adjacency_count < best_adjacent_count) - continue; - } + return true; +} - if (! better) { - if (same_type_count > best_same_type_count) - better = true; - else if (same_type_count < best_same_type_count) - continue; - } +void +finalize_parsed_wonder_definition (struct parsed_wonder_definition * def, + int section_start_line, + struct error_line ** parse_errors) +{ + bool ok = true; - if (! better) { - if ((min_dist_sq > best_dist) || - ((min_dist_sq == best_dist) && (dx_adjusted < best_target_diff)) || - ((min_dist_sq == best_dist) && (dx_adjusted == best_target_diff) && (rand_val < best_rand))) - better = true; - else - continue; + if ((! def->has_name) || (def->name == NULL)) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: name (value is required)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + if (! def->has_img_row) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: img_row (value is required)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + if (! def->has_img_column) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: img_column (value is required)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + if (! def->has_img_construct_row) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: img_construct_row (value is required)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + if (! def->has_img_construct_column) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: img_construct_column (value is required)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + if (def->enable_img_alt_dir) { + if (! def->has_img_alt_dir_row) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: img_alt_dir_row (value is required when enable_img_alt_dir is set)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; } - - best_dist = min_dist_sq; - best_target_diff = dx_adjusted; - best_rand = rand_val; - best_index = ci; - best_continent_priority = continent_priority; - if (adjacency_bonus_active) - best_adjacent_count = adjacency_count; - best_same_type_count = same_type_count; } - - if (best_index < 0) { - char msg[256]; - snprintf (msg, sizeof msg, "[C3X] Could not find a suitable tile for natural wonder \"%s\" after filtering.\n", - (is->natural_wonder_configs[ni].name != NULL) ? is->natural_wonder_configs[ni].name : "Natural Wonder"); - (*p_OutputDebugStringA) (msg); - continue; + if (! def->has_img_alt_dir_column) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: img_alt_dir_column (value is required when enable_img_alt_dir is set)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; + } } - - struct natural_wonder_candidate * chosen = &list->entries[best_index]; - assign_natural_wonder_to_tile (chosen->tile, chosen->x, chosen->y, ni); - - if (placement_count >= placement_capacity) { - int new_capacity = (placement_capacity > 0) ? placement_capacity * 2 : 8; - struct wonder_location * grown = - (struct wonder_location *)realloc (placements, new_capacity * sizeof *grown); - if (grown != NULL) { - placements = grown; - placement_capacity = new_capacity; + if (! def->has_img_alt_dir_construct_row) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: img_alt_dir_construct_row (value is required when enable_img_alt_dir is set)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; } } - - if ((placements != NULL) && (placement_count < placement_capacity)) { - placements[placement_count++] = (struct wonder_location){ - .x = chosen->x, - .y = chosen->y - }; + if (! def->has_img_alt_dir_construct_column) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: img_alt_dir_construct_column (value is required when enable_img_alt_dir is set)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; + } } - - newly_placed += 1; - - char msg[256]; - snprintf (msg, sizeof msg, "[C3X] Placed natural wonder \"%s\" at (%d,%d).\n", - (is->natural_wonder_configs[ni].name != NULL) ? is->natural_wonder_configs[ni].name : "Natural Wonder", - chosen->x, chosen->y); - (*p_OutputDebugStringA) (msg); } - char summary[256]; - snprintf (summary, sizeof summary, "[C3X] Natural wonder placement complete. Newly placed: %d, already present: %d.\n", - newly_placed, existing_count); - (*p_OutputDebugStringA) (summary); + if (ok) + add_dynamic_wonder_from_definition (def, section_start_line); - for (int ni = 0; ni < wonder_count; ni++) - free (candidate_lists[ni].entries); - free (wonder_order); - free (candidate_lists); - free (already_placed); - free (placements); + free_parsed_wonder_definition (def); } void -init_scenario_district_entry (struct scenario_district_entry * entry) +handle_wonder_definition_key (struct parsed_wonder_definition * def, + struct string_slice const * key, + struct string_slice const * value, + int line_number, + struct error_line ** parse_errors, + struct error_line ** unrecognized_keys) { - if (entry == NULL) - return; + if (slice_matches_str (key, "name")) { + if (def->name != NULL) { + free (def->name); + def->name = NULL; + } - memset (entry, 0, sizeof *entry); -} + struct string_slice unquoted = trim_string_slice (value, 1); + if (unquoted.len == 0) { + def->has_name = false; + add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); + } else { + char * name_copy = extract_slice (&unquoted); + if (name_copy == NULL) { + def->has_name = false; + add_key_parse_error (parse_errors, line_number, key, value, "(out of memory)"); + } else { + def->name = name_copy; + def->has_name = true; + } + } -void -init_scenario_named_tile_entry (struct scenario_named_tile_entry * entry) -{ - if (entry == NULL) - return; + } else if (slice_matches_str (key, "img_path")) { + if (def->img_path != NULL) { + free (def->img_path); + def->img_path = NULL; + } - memset (entry, 0, sizeof *entry); -} + struct string_slice unquoted = trim_string_slice (value, 1); + if (unquoted.len == 0) { + def->has_img_path = false; + } else { + char * path_copy = extract_slice (&unquoted); + if (path_copy == NULL) { + def->has_img_path = false; + } else { + def->img_path = path_copy; + def->has_img_path = true; + } + } -void -free_scenario_district_entry (struct scenario_district_entry * entry) -{ - if (entry == NULL) - return; + } else if (slice_matches_str (key, "img_row")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_row = ival; + def->has_img_row = true; + } else { + def->has_img_row = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } - if (entry->district_name != NULL) { - free (entry->district_name); - entry->district_name = NULL; - } - if (entry->wonder_city_name != NULL) { - free (entry->wonder_city_name); - entry->wonder_city_name = NULL; - } - if (entry->wonder_name != NULL) { - free (entry->wonder_name); - entry->wonder_name = NULL; - } - entry->has_coordinates = 0; - entry->has_district_name = 0; - entry->has_wonder_city = 0; - entry->has_wonder_name = 0; -} + } else if (slice_matches_str (key, "img_column")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_column = ival; + def->has_img_column = true; + } else { + def->has_img_column = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } + + } else if (slice_matches_str (key, "img_construct_row")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_construct_row = ival; + def->has_img_construct_row = true; + } else { + def->has_img_construct_row = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } -void -free_scenario_named_tile_entry (struct scenario_named_tile_entry * entry) -{ - if (entry == NULL) - return; + } else if (slice_matches_str (key, "img_construct_column")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_construct_column = ival; + def->has_img_construct_column = true; + } else { + def->has_img_construct_column = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } - if (entry->name != NULL) { - free (entry->name); - entry->name = NULL; - } - entry->has_coordinates = 0; - entry->has_name = 0; -} + } else if (slice_matches_str (key, "img_alt_dir_construct_row")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_alt_dir_construct_row = ival; + def->has_img_alt_dir_construct_row = true; + } else { + def->has_img_alt_dir_construct_row = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } -void -add_scenario_district_error (struct error_line ** parse_errors, - int line_number, - char const * message) -{ - if (message == NULL) - return; + } else if (slice_matches_str (key, "img_alt_dir_construct_column")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_alt_dir_construct_column = ival; + def->has_img_alt_dir_construct_column = true; + } else { + def->has_img_alt_dir_construct_column = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } - struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: %s", line_number, message); - err->text[(sizeof err->text) - 1] = '\0'; -} + } else if (slice_matches_str (key, "img_alt_dir_row")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_alt_dir_row = ival; + def->has_img_alt_dir_row = true; + } else { + def->has_img_alt_dir_row = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } -int -parse_scenario_district_coordinates (struct string_slice const * value, int * out_x, int * out_y) -{ - if ((value == NULL) || (out_x == NULL) || (out_y == NULL)) - return 0; + } else if (slice_matches_str (key, "img_alt_dir_column")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_alt_dir_column = ival; + def->has_img_alt_dir_column = true; + } else { + def->has_img_alt_dir_column = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } - char * text = trim_and_extract_slice (value, 0); - if (text == NULL) - return 0; + } else if (slice_matches_str (key, "enable_img_alt_dir")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->enable_img_alt_dir = (ival != 0); + def->has_enable_img_alt_dir = true; + } else { + def->has_enable_img_alt_dir = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } - char * cursor = text; - int success = 0; - int x, y; - if (parse_int (&cursor, &x) && - skip_punctuation (&cursor, ',') && - parse_int (&cursor, &y)) { - skip_horiz_space (&cursor); - success = (*cursor == '\0'); - if (success) { - *out_x = x; - *out_y = y; + } else if (slice_matches_str (key, "buildable_on")) { + unsigned int mask; + if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number)) { + def->buildable_square_types_mask = mask; + def->has_buildable_on = true; + } else { + def->has_buildable_on = false; } - } - free (text); - return success; + } else + add_unrecognized_key_error (unrecognized_keys, line_number, key); } -int -finalize_scenario_district_entry (struct scenario_district_entry * entry, - int section_start_line, - struct error_line ** parse_errors) +void +load_dynamic_wonder_config_file (char const * file_path, + int path_is_relative_to_mod_dir, + int log_missing, + int drop_existing_configs) { - int success = 1; - if ((entry == NULL) || (parse_errors == NULL)) - return 0; + char path[MAX_PATH]; + if (path_is_relative_to_mod_dir) { + if (is->mod_rel_dir == NULL) + return; + snprintf (path, sizeof path, "%s\\%s", is->mod_rel_dir, file_path); + } else { + strncpy (path, file_path, sizeof path); + } + path[(sizeof path) - 1] = '\0'; - if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { - free_scenario_district_entry (entry); - init_scenario_district_entry (entry); - return 1; + char * text = file_to_string (path); + if (text == NULL) { + if (log_missing) { + char ss[256]; + snprintf (ss, sizeof ss, "[C3X] Wonders config file not found: %s", path); + (*p_OutputDebugStringA) (ss); + } + return; } - if (! entry->has_coordinates) - add_scenario_district_error (parse_errors, section_start_line, "coordinates (value is required)"); - if ((! entry->has_district_name) || (entry->district_name == NULL) || (entry->district_name[0] == '\0')) - add_scenario_district_error (parse_errors, section_start_line, "district (value is required)"); + if (drop_existing_configs) + reset_wonder_district_configs (); - if ((! entry->has_coordinates) || (! entry->has_district_name) || - (entry->district_name == NULL) || (entry->district_name[0] == '\0')) - success = 0; + struct parsed_wonder_definition def; + init_parsed_wonder_definition (&def); + bool in_section = false; + int section_start_line = 0; + int line_number = 0; + struct error_line * unrecognized_keys = NULL; + struct error_line * parse_errors = NULL; - int map_x = entry->tile_x; - int map_y = entry->tile_y; - if (success) { - wrap_tile_coords (&p_bic_data->Map, &map_x, &map_y); - Tile * tile = tile_at (map_x, map_y); - if ((tile == NULL) || (tile == p_null_tile)) { - add_scenario_district_error (parse_errors, section_start_line, "Invalid coordinates (tile not found)"); - success = 0; - } else { - int district_id = find_district_index_by_name (entry->district_name); - if ((district_id < 0) || (district_id >= is->district_count)) { - char msg[200]; - snprintf (msg, sizeof msg, "district (unrecognized name: \"%s\")", entry->district_name); - add_scenario_district_error (parse_errors, section_start_line, msg); - success = 0; - } else { - if ((district_id == NATURAL_WONDER_DISTRICT_ID) && ! is->current_config.enable_natural_wonders) { - free_scenario_district_entry (entry); - init_scenario_district_entry (entry); - return 1; - } - if ((district_id != NATURAL_WONDER_DISTRICT_ID) && ! is->current_config.enable_districts) { - free_scenario_district_entry (entry); - init_scenario_district_entry (entry); - return 1; - } - struct district_instance * inst = ensure_district_instance (tile, district_id, map_x, map_y); - if (inst == NULL) { - add_scenario_district_error (parse_errors, section_start_line, "Failed to create district instance"); - success = 0; - } else { - inst->district_id = district_id; - district_instance_set_coords (inst, map_x, map_y); - inst->state = DS_COMPLETED; - inst->wonder_info.state = WDS_UNUSED; - inst->wonder_info.city = NULL; - inst->wonder_info.city_id = -1; - inst->wonder_info.wonder_index = -1; - inst->natural_wonder_info.natural_wonder_id = -1; + char * cursor = text; + while (*cursor != '\0') { + line_number += 1; - if (district_id == WONDER_DISTRICT_ID) { - int has_city = entry->has_wonder_city && - (entry->wonder_city_name != NULL) && (entry->wonder_city_name[0] != '\0'); - int has_wonder = entry->has_wonder_name && - (entry->wonder_name != NULL) && (entry->wonder_name[0] != '\0'); - if (! has_city || ! has_wonder) { - add_scenario_district_error (parse_errors, section_start_line, "Wonder district requires both wonder_city and wonder_name"); - success = 0; - } else { - int wonder_index = find_wonder_district_index_by_name (entry->wonder_name); - if (wonder_index < 0) { - char msg[200]; - snprintf (msg, sizeof msg, "wonder_name (unrecognized wonder: \"%s\")", entry->wonder_name); - add_scenario_district_error (parse_errors, section_start_line, msg); - success = 0; - } else { - inst->wonder_info.city = NULL; - inst->wonder_info.city_id = -1; - inst->wonder_info.state = WDS_COMPLETED; - inst->wonder_info.wonder_index = wonder_index; - } - } - } else if (district_id == NATURAL_WONDER_DISTRICT_ID) { - int has_name = entry->has_wonder_name && - (entry->wonder_name != NULL) && (entry->wonder_name[0] != '\0'); - if (! has_name) { - add_scenario_district_error (parse_errors, section_start_line, "Natural Wonder district requires wonder_name"); - success = 0; - } else { - int natural_index = find_natural_wonder_index_by_name (entry->wonder_name); - if (natural_index < 0) { - char msg[200]; - snprintf (msg, sizeof msg, "wonder_name (unrecognized natural wonder: \"%s\")", entry->wonder_name); - add_scenario_district_error (parse_errors, section_start_line, msg); - success = 0; - } else - inst->natural_wonder_info.natural_wonder_id = natural_index; - } - if (entry->has_wonder_city) - add_scenario_district_error (parse_errors, section_start_line, "wonder_city ignored for Natural Wonder district entries"); - } else if (entry->has_wonder_city || entry->has_wonder_name) { - add_scenario_district_error (parse_errors, section_start_line, "wonder_* fields only valid for Wonder or Natural Wonder district entries"); - } + char * line_start = cursor; + char * line_end = cursor; + while ((*line_end != '\0') && (*line_end != '\n')) + line_end++; - if (success) { - if (district_id != NATURAL_WONDER_DISTRICT_ID && !tile->vtable->m18_Check_Mines (tile, __, 0)) - tile->vtable->m56_Set_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, map_x, map_y); - set_tile_unworkable_for_all_cities (tile, map_x, map_y); - } - } + int line_len = line_end - line_start; + bool has_newline = (*line_end == '\n'); + if (has_newline) + *line_end = '\0'; + + struct string_slice line_slice = { .str = line_start, .len = line_len }; + struct string_slice trimmed = trim_string_slice (&line_slice, 0); + if (line_is_empty_or_comment (&trimmed)) { + cursor = has_newline ? line_end + 1 : line_end; + continue; + } + + if (trimmed.str[0] == '#') { + struct string_slice directive = trimmed; + directive.str += 1; + directive.len -= 1; + directive = trim_string_slice (&directive, 0); + if ((directive.len > 0) && slice_matches_str (&directive, "Wonder")) { + if (in_section) + finalize_parsed_wonder_definition (&def, section_start_line, &parse_errors); + in_section = true; + section_start_line = line_number; } + cursor = has_newline ? line_end + 1 : line_end; + continue; + } + + if (! in_section) { + cursor = has_newline ? line_end + 1 : line_end; + continue; + } + + struct string_slice key_slice = {0}; + struct string_slice value_slice = {0}; + enum key_value_parse_status status = parse_trimmed_key_value (&trimmed, &key_slice, &value_slice); + if (status == KVP_NO_EQUALS) { + char * line_text = extract_slice (&trimmed); + struct error_line * err = add_error_line (&parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %s (expected '=')", line_number, line_text); + err->text[(sizeof err->text) - 1] = '\0'; + free (line_text); + cursor = has_newline ? line_end + 1 : line_end; + continue; + } else if (status == KVP_EMPTY_KEY) { + struct error_line * err = add_error_line (&parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: (missing key)", line_number); + err->text[(sizeof err->text) - 1] = '\0'; + cursor = has_newline ? line_end + 1 : line_end; + continue; } + + handle_wonder_definition_key (&def, &key_slice, &value_slice, line_number, &parse_errors, &unrecognized_keys); + cursor = has_newline ? line_end + 1 : line_end; } - free_scenario_district_entry (entry); - init_scenario_district_entry (entry); - return success; + if (in_section) + finalize_parsed_wonder_definition (&def, section_start_line, &parse_errors); + + free_parsed_wonder_definition (&def); + free (text); + + // Append to loaded config names list + struct loaded_config_name * top_lcn = is->loaded_config_names; + while (top_lcn->next != NULL) + top_lcn = top_lcn->next; + + struct loaded_config_name * new_lcn = malloc (sizeof *new_lcn); + new_lcn->name = strdup (path); + new_lcn->next = NULL; + + top_lcn->next = new_lcn; + + if (parse_errors != NULL || unrecognized_keys != NULL) { + PopupForm * popup = get_popup_form (); + popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_WARNING", -1, 0, 0, 0); + char s[200]; + snprintf (s, sizeof s, "Wonder District Config errors in %s:", path); + s[(sizeof s) - 1] = '\0'; + PopupForm_add_text (popup, __, s, false); + if (parse_errors != NULL) { + for (struct error_line * line = parse_errors; line != NULL; line = line->next) + PopupForm_add_text (popup, __, line->text, false); + } + if (unrecognized_keys != NULL) { + PopupForm_add_text (popup, __, "", false); + PopupForm_add_text (popup, __, "Unrecognized keys:", false); + for (struct error_line * line = unrecognized_keys; line != NULL; line = line->next) + PopupForm_add_text (popup, __, line->text, false); + } + patch_show_popup (popup, __, 0, 0); + free_error_lines (parse_errors); + free_error_lines (unrecognized_keys); + } } -bool -tile_can_be_named (Tile * tile, int tile_x, int tile_y) +void +load_dynamic_wonder_configs () { - if ((tile == NULL) || (tile == p_null_tile)) - return false; - struct district_instance * inst = get_district_instance (tile); - if ((inst != NULL) && (inst->district_id == NATURAL_WONDER_DISTRICT_ID)) - return false; - return true; + load_dynamic_wonder_config_file ("default.districts_wonders_config.txt", 1, 1, 1); + load_dynamic_wonder_config_file ("user.districts_wonders_config.txt", 1, 0, 1); + + char * scenario_filename = "scenario.districts_wonders_config.txt"; + char * scenario_wonder_config_path = BIC_get_asset_path (p_bic_data, __, scenario_filename, false); + if ((scenario_wonder_config_path != NULL) && (0 != strcmp (scenario_filename, scenario_wonder_config_path))) + load_dynamic_wonder_config_file (scenario_wonder_config_path, 0, 0, 1); } void -remove_named_tile_entry (Tile * tile) +init_parsed_natural_wonder_definition (struct parsed_natural_wonder_definition * def) { - struct named_tile_entry * entry = get_named_tile_entry (tile); - if (entry == NULL) - return; - itable_remove (&is->named_tile_map, (int)tile); - free (entry); + memset (def, 0, sizeof *def); + def->terrain_type = SQ_Grassland; + def->adjacent_to = (enum SquareTypes)SQ_INVALID; + def->adjacency_dir = DIR_ZERO; } void -set_named_tile_entry (Tile * tile, int tile_x, int tile_y, char const * name) +free_parsed_natural_wonder_definition (struct parsed_natural_wonder_definition * def) { - if ((tile == NULL) || (tile == p_null_tile)) - return; - if ((name == NULL) || (name[0] == '\0')) { - remove_named_tile_entry (tile); - return; + if (def->name != NULL) { + free (def->name); + def->name = NULL; } - - struct named_tile_entry * entry = get_named_tile_entry (tile); - if (entry == NULL) { - entry = calloc (1, sizeof *entry); - if (entry == NULL) - return; - itable_insert (&is->named_tile_map, (int)tile, (int)entry); + if (def->img_path != NULL) { + free (def->img_path); + def->img_path = NULL; } - entry->tile_x = tile_x; - entry->tile_y = tile_y; - strncpy (entry->name, name, sizeof entry->name); - entry->name[(sizeof entry->name) - 1] = '\0'; + init_parsed_natural_wonder_definition (def); } -int -finalize_scenario_named_tile_entry (struct scenario_named_tile_entry * entry, - int section_start_line, - struct error_line ** parse_errors) +bool +add_natural_wonder_from_definition (struct parsed_natural_wonder_definition * def, int section_start_line) { - int success = 1; - if ((entry == NULL) || (parse_errors == NULL)) - return 0; + if ((def == NULL) || (def->name == NULL)) + return false; - if (! is->current_config.enable_named_tiles) { - free_scenario_named_tile_entry (entry); - init_scenario_named_tile_entry (entry); - return 1; + int existing_index; + bool has_existing = stable_look_up (&is->natural_wonder_name_to_id, def->name, &existing_index); + + int dest = has_existing ? existing_index : is->natural_wonder_count; + if ((dest < 0) || (dest >= MAX_NATURAL_WONDER_DISTRICT_TYPES)) + return false; + + struct natural_wonder_district_config new_cfg; + memset (&new_cfg, 0, sizeof new_cfg); + new_cfg.index = dest; + new_cfg.is_dynamic = true; + new_cfg.adjacent_to = (enum SquareTypes)SQ_INVALID; + new_cfg.adjacency_dir = DIR_ZERO; + + char * name_copy = strdup (def->name); + if (name_copy == NULL) + return false; + new_cfg.name = name_copy; + + char const * img_path_src = def->img_path; + char * img_copy = strdup (img_path_src); + if (img_copy == NULL) { + free (name_copy); + return false; + } + new_cfg.img_path = img_copy; + new_cfg.img_row = def->img_row; + new_cfg.img_column = def->img_column; + new_cfg.terrain_type = def->terrain_type; + new_cfg.adjacent_to = def->adjacent_to; + new_cfg.adjacency_dir = def->adjacency_dir; + new_cfg.culture_bonus = def->has_culture_bonus ? def->culture_bonus : 0; + new_cfg.science_bonus = def->has_science_bonus ? def->science_bonus : 0; + new_cfg.food_bonus = def->has_food_bonus ? def->food_bonus : 0; + new_cfg.gold_bonus = def->has_gold_bonus ? def->gold_bonus : 0; + new_cfg.shield_bonus = def->has_shield_bonus ? def->shield_bonus : 0; + new_cfg.happiness_bonus = def->has_happiness_bonus ? def->happiness_bonus : 0; + + if (has_existing) { + struct natural_wonder_district_config * cfg = &is->natural_wonder_configs[existing_index]; + free_dynamic_natural_wonder_config (cfg); + *cfg = new_cfg; + } else { + struct natural_wonder_district_config * cfg = &is->natural_wonder_configs[dest]; + free_dynamic_natural_wonder_config (cfg); + *cfg = new_cfg; + is->natural_wonder_count = dest + 1; + stable_insert (&is->natural_wonder_name_to_id, new_cfg.name, dest); } - if (! entry->has_coordinates) - add_scenario_district_error (parse_errors, section_start_line, "coordinates (value is required)"); - if ((! entry->has_name) || (entry->name == NULL) || (entry->name[0] == '\0')) - add_scenario_district_error (parse_errors, section_start_line, "name (value is required)"); + return true; +} - if ((! entry->has_coordinates) || (! entry->has_name) || - (entry->name == NULL) || (entry->name[0] == '\0')) - success = 0; +void +finalize_parsed_natural_wonder_definition (struct parsed_natural_wonder_definition * def, + int section_start_line, + struct error_line ** parse_errors) +{ + bool ok = true; - int map_x = entry->tile_x; - int map_y = entry->tile_y; - if (success) { - wrap_tile_coords (&p_bic_data->Map, &map_x, &map_y); - Tile * tile = tile_at (map_x, map_y); - if ((tile == NULL) || (tile == p_null_tile)) { - add_scenario_district_error (parse_errors, section_start_line, "Invalid coordinates (tile not found)"); - success = 0; - } else if (! tile_can_be_named (tile, map_x, map_y)) { - add_scenario_district_error (parse_errors, section_start_line, "Invalid coordinates (tile cannot be named)"); - success = 0; - } else { - set_named_tile_entry (tile, map_x, map_y, entry->name); + if ((! def->has_name) || (def->name == NULL)) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: name (value is required)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + if (! def->has_img_row) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: img_row (value is required)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + if (! def->has_img_column) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: img_column (value is required)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + if (! def->has_terrain_type) { + ok = false; + if (parse_errors != NULL) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: terrain_type (value is required)", section_start_line); + err->text[(sizeof err->text) - 1] = '\0'; } } - free_scenario_named_tile_entry (entry); - init_scenario_named_tile_entry (entry); - return success; + if (ok) + add_natural_wonder_from_definition (def, section_start_line); + + free_parsed_natural_wonder_definition (def); } void -handle_scenario_district_key (struct scenario_district_entry * entry, - struct string_slice const * key, - struct string_slice const * value, - int line_number, - struct error_line ** parse_errors, - struct error_line ** unrecognized_keys) +handle_natural_wonder_definition_key (struct parsed_natural_wonder_definition * def, + struct string_slice const * key, + struct string_slice const * value, + int line_number, + struct error_line ** parse_errors, + struct error_line ** unrecognized_keys) { - if ((entry == NULL) || (key == NULL) || (value == NULL)) - return; + if (slice_matches_str (key, "name")) { + if (def->name != NULL) { + free (def->name); + def->name = NULL; + } - if (slice_matches_str (key, "coordinates")) { - int x, y; - if (parse_scenario_district_coordinates (value, &x, &y)) { - entry->tile_x = x; - entry->tile_y = y; - entry->has_coordinates = 1; - } else - add_scenario_district_error (parse_errors, line_number, "coordinates (expected format: x,y)"); + struct string_slice unquoted = trim_string_slice (value, 1); + if (unquoted.len == 0) { + def->has_name = false; + add_key_parse_error (parse_errors, line_number, key, value, "(value is required)"); + } else { + char * name_copy = extract_slice (&unquoted); + if (name_copy == NULL) { + def->has_name = false; + add_key_parse_error (parse_errors, line_number, key, value, "(out of memory)"); + } else { + def->name = name_copy; + def->has_name = true; + } + } - } else if (slice_matches_str (key, "district")) { - if (entry->district_name != NULL) { - free (entry->district_name); - entry->district_name = NULL; + } else if (slice_matches_str (key, "terrain_type")) { + enum SquareTypes terrain; + if (read_natural_wonder_terrain_type (value, &terrain)) { + def->terrain_type = terrain; + def->has_terrain_type = true; + } else { + def->has_terrain_type = false; + add_key_parse_error (parse_errors, line_number, key, value, "(unrecognized terrain type)"); } - entry->district_name = copy_trimmed_string_or_null (value, 1); - entry->has_district_name = (entry->district_name != NULL); - if (! entry->has_district_name) - add_scenario_district_error (parse_errors, line_number, "district (value is required)"); - } else if (slice_matches_str (key, "wonder_city")) { - if (entry->wonder_city_name != NULL) { - free (entry->wonder_city_name); - entry->wonder_city_name = NULL; + } else if (slice_matches_str (key, "adjacent_to")) { + enum SquareTypes adj; + if (read_square_type_value (value, &adj)) { + def->adjacent_to = adj; + def->has_adjacent_to = true; + } else { + def->adjacent_to = (enum SquareTypes)SQ_INVALID; + def->has_adjacent_to = false; + add_key_parse_error (parse_errors, line_number, key, value, "(unrecognized square type)"); } - entry->wonder_city_name = copy_trimmed_string_or_null (value, 1); - entry->has_wonder_city = (entry->wonder_city_name != NULL); - } else if (slice_matches_str (key, "wonder_name")) { - if (entry->wonder_name != NULL) { - free (entry->wonder_name); - entry->wonder_name = NULL; + } else if (slice_matches_str (key, "adjacency_dir")) { + enum direction dir; + if (read_direction_value (value, &dir)) { + def->adjacency_dir = dir; + def->has_adjacency_dir = true; + } else { + def->adjacency_dir = DIR_ZERO; + def->has_adjacency_dir = false; + add_key_parse_error (parse_errors, line_number, key, value, "(unrecognized direction)"); } - entry->wonder_name = copy_trimmed_string_or_null (value, 1); - entry->has_wonder_name = (entry->wonder_name != NULL); - } else - add_unrecognized_key_error (unrecognized_keys, line_number, key); -} + } else if (slice_matches_str (key, "img_path")) { + if (def->img_path != NULL) { + free (def->img_path); + def->img_path = NULL; + } -void -handle_scenario_named_tile_key (struct scenario_named_tile_entry * entry, - struct string_slice const * key, - struct string_slice const * value, - int line_number, - struct error_line ** parse_errors, - struct error_line ** unrecognized_keys) -{ - if ((entry == NULL) || (key == NULL) || (value == NULL)) - return; + struct string_slice unquoted = trim_string_slice (value, 1); + if (unquoted.len == 0) { + def->has_img_path = false; + } else { + char * path_copy = extract_slice (&unquoted); + if (path_copy == NULL) { + def->has_img_path = false; + } else { + def->img_path = path_copy; + def->has_img_path = true; + } + } - if (slice_matches_str (key, "coordinates")) { - int x, y; - if (parse_scenario_district_coordinates (value, &x, &y)) { - entry->tile_x = x; - entry->tile_y = y; - entry->has_coordinates = 1; - } else - add_scenario_district_error (parse_errors, line_number, "coordinates (expected format: x,y)"); + } else if (slice_matches_str (key, "img_row")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_row = ival; + def->has_img_row = true; + } else { + def->has_img_row = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } - } else if (slice_matches_str (key, "name")) { - if (entry->name != NULL) { - free (entry->name); - entry->name = NULL; + } else if (slice_matches_str (key, "img_column")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_column = ival; + def->has_img_column = true; + } else { + def->has_img_column = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); } - entry->name = copy_trimmed_string_or_null (value, 1); - entry->has_name = (entry->name != NULL); - if (! entry->has_name) - add_scenario_district_error (parse_errors, line_number, "name (value is required)"); - } else - add_unrecognized_key_error (unrecognized_keys, line_number, key); -} + } else if (slice_matches_str (key, "culture_bonus")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->culture_bonus = ival; + def->has_culture_bonus = true; + } else { + def->has_culture_bonus = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } + + } else if (slice_matches_str (key, "science_bonus")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->science_bonus = ival; + def->has_science_bonus = true; + } else { + def->has_science_bonus = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } + + } else if (slice_matches_str (key, "food_bonus")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->food_bonus = ival; + def->has_food_bonus = true; + } else { + def->has_food_bonus = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } + + } else if (slice_matches_str (key, "gold_bonus")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->gold_bonus = ival; + def->has_gold_bonus = true; + } else { + def->has_gold_bonus = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } + + } else if (slice_matches_str (key, "shield_bonus")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->shield_bonus = ival; + def->has_shield_bonus = true; + } else { + def->has_shield_bonus = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } + + } else if (slice_matches_str (key, "happiness_bonus")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->happiness_bonus = ival; + def->has_happiness_bonus = true; + } else { + def->has_happiness_bonus = false; + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } + + } else + add_unrecognized_key_error (unrecognized_keys, line_number, key); +} -// Parses a .c3x.txt file corresponding to the given scenario file path, loading district instances as specified. -// Attempts the scenario_path first, then scenario_config_path; if neither yields a readable file, no action is taken. -// -// The expected file format itself is very simple. Example: -// -// ``` -// DISTRICTS -// -// #District -// coordinates = 12,28 -// district = Entertainment Complex -// -// #District -// coordinates = 9,23 -// district = Wonder District -// wonder_city = Rome -// wonder_name = The Pyramids -// -// #District -// coordinates = 10,30 -// district = Natural Wonder -// wonder_name = Mount Everest -// -// #NamedTile -// coordinates = 41,23 -// name = Tiber River -// ``` -// -// Details at https://github.com/instafluff0/Civ3_Editor_Fork_for_C3X_Districts void -load_scenario_districts_from_file () +load_natural_wonder_config_file (char const * file_path, + int path_is_relative_to_mod_dir, + int log_missing, + int drop_existing_configs) { - char * scenario_filename = "scenario.districts.txt"; - char * scenario_districts_path = BIC_get_asset_path (p_bic_data, __, scenario_filename, false); + char path[MAX_PATH]; + if (path_is_relative_to_mod_dir) { + if (is->mod_rel_dir == NULL) + return; + snprintf (path, sizeof path, "%s\\%s", is->mod_rel_dir, file_path); + } else { + strncpy (path, file_path, sizeof path); + } + path[(sizeof path) - 1] = '\0'; - // BIC_get_asset_path returns the file name when it can't find the file - if ((scenario_districts_path == NULL) || (0 == strcmp (scenario_filename, scenario_districts_path))) + char * text = file_to_string (path); + if (text == NULL) { + if (log_missing) { + char ss[256]; + snprintf (ss, sizeof ss, "[C3X] Natural wonders config file not found: %s", path); + (*p_OutputDebugStringA) (ss); + } return; + } - char * text = file_to_string (scenario_districts_path); - if (text == NULL) - return; + if (drop_existing_configs) + reset_natural_wonder_configs (); - struct scenario_district_entry entry; - struct scenario_named_tile_entry named_entry; - init_scenario_district_entry (&entry); - init_scenario_named_tile_entry (&named_entry); - int in_section = 0; + struct parsed_natural_wonder_definition def; + init_parsed_natural_wonder_definition (&def); + bool in_section = false; int section_start_line = 0; - int section_type = 0; int line_number = 0; - int header_seen = 0; struct error_line * unrecognized_keys = NULL; struct error_line * parse_errors = NULL; @@ -9881,7 +9863,7 @@ load_scenario_districts_from_file () line_end++; int line_len = line_end - line_start; - int has_newline = (*line_end == '\n'); + bool has_newline = (*line_end == '\n'); if (has_newline) *line_end = '\0'; @@ -9892,44 +9874,16 @@ load_scenario_districts_from_file () continue; } - // Keep support for legacy header, technically not needed - if (! header_seen) { - if (slice_matches_str (&trimmed, "DISTRICTS")) { - header_seen = 1; - cursor = has_newline ? line_end + 1 : line_end; - continue; - } - } - if (trimmed.str[0] == '#') { struct string_slice directive = trimmed; directive.str += 1; directive.len -= 1; directive = trim_string_slice (&directive, 0); - if (slice_matches_str (&directive, "District")) { - if (in_section) { - if (section_type == 1) - finalize_scenario_district_entry (&entry, section_start_line, &parse_errors); - else if (section_type == 2) - finalize_scenario_named_tile_entry (&named_entry, section_start_line, &parse_errors); - } - in_section = 1; - section_type = 1; - section_start_line = line_number; - free_scenario_district_entry (&entry); - init_scenario_district_entry (&entry); - } else if (slice_matches_str (&directive, "NamedTile")) { - if (in_section) { - if (section_type == 1) - finalize_scenario_district_entry (&entry, section_start_line, &parse_errors); - else if (section_type == 2) - finalize_scenario_named_tile_entry (&named_entry, section_start_line, &parse_errors); - } - in_section = 1; - section_type = 2; + if ((directive.len > 0) && slice_matches_str (&directive, "Wonder")) { + if (in_section) + finalize_parsed_natural_wonder_definition (&def, section_start_line, &parse_errors); + in_section = true; section_start_line = line_number; - free_scenario_named_tile_entry (&named_entry); - init_scenario_named_tile_entry (&named_entry); } cursor = has_newline ? line_end + 1 : line_end; continue; @@ -9942,1549 +9896,1604 @@ load_scenario_districts_from_file () struct string_slice key_slice = {0}; struct string_slice value_slice = {0}; - switch (parse_trimmed_key_value (&trimmed, &key_slice, &value_slice)) { - case KVP_NO_EQUALS: - add_scenario_district_error (&parse_errors, line_number, "(expected '=')"); - break; - case KVP_EMPTY_KEY: - add_scenario_district_error (&parse_errors, line_number, "(missing key)"); - break; - case KVP_SUCCESS: - if (section_type == 1) - handle_scenario_district_key (&entry, &key_slice, &value_slice, line_number, &parse_errors, &unrecognized_keys); - else if (section_type == 2) - handle_scenario_named_tile_key (&named_entry, &key_slice, &value_slice, line_number, &parse_errors, &unrecognized_keys); - break; + enum key_value_parse_status status = parse_trimmed_key_value (&trimmed, &key_slice, &value_slice); + if (status == KVP_NO_EQUALS) { + char * line_text = extract_slice (&trimmed); + struct error_line * err = add_error_line (&parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %s (expected '=')", line_number, line_text); + err->text[(sizeof err->text) - 1] = '\0'; + free (line_text); + cursor = has_newline ? line_end + 1 : line_end; + continue; + } else if (status == KVP_EMPTY_KEY) { + struct error_line * err = add_error_line (&parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: (missing key)", line_number); + err->text[(sizeof err->text) - 1] = '\0'; + cursor = has_newline ? line_end + 1 : line_end; + continue; } - cursor = has_newline ? line_end + 1 : line_end; + handle_natural_wonder_definition_key (&def, &key_slice, &value_slice, line_number, &parse_errors, &unrecognized_keys); + cursor = has_newline ? line_end + 1 : line_end; + } + + if (in_section) + finalize_parsed_natural_wonder_definition (&def, section_start_line, &parse_errors); + + free_parsed_natural_wonder_definition (&def); + free (text); + + // Append to loaded config names list + struct loaded_config_name * top_lcn = is->loaded_config_names; + while (top_lcn->next != NULL) + top_lcn = top_lcn->next; + + struct loaded_config_name * new_lcn = malloc (sizeof *new_lcn); + new_lcn->name = strdup (path); + new_lcn->next = NULL; + + top_lcn->next = new_lcn; + + if (parse_errors != NULL || unrecognized_keys != NULL) { + PopupForm * popup = get_popup_form (); + popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_WARNING", -1, 0, 0, 0); + char s[200]; + snprintf (s, sizeof s, "Natural Wonder Config errors in %s:", path); + s[(sizeof s) - 1] = '\0'; + PopupForm_add_text (popup, __, s, false); + if (parse_errors != NULL) { + for (struct error_line * line = parse_errors; line != NULL; line = line->next) + PopupForm_add_text (popup, __, line->text, false); + } + if (unrecognized_keys != NULL) { + PopupForm_add_text (popup, __, "", false); + PopupForm_add_text (popup, __, "Unrecognized keys:", false); + for (struct error_line * line = unrecognized_keys; line != NULL; line = line->next) + PopupForm_add_text (popup, __, line->text, false); + } + patch_show_popup (popup, __, 0, 0); + free_error_lines (parse_errors); + free_error_lines (unrecognized_keys); + } +} + +void +load_natural_wonder_configs () +{ + load_natural_wonder_config_file ("default.districts_natural_wonders_config.txt", 1, 1, 1); + load_natural_wonder_config_file ("user.districts_natural_wonders_config.txt", 1, 0, 1); + + char * scenario_filename = "scenario.districts_natural_wonders_config.txt"; + char * scenario_natural_wonder_config_path = BIC_get_asset_path (p_bic_data, __, scenario_filename, false); + if ((scenario_natural_wonder_config_path != NULL) && (0 != strcmp (scenario_filename, scenario_natural_wonder_config_path))) + load_natural_wonder_config_file (scenario_natural_wonder_config_path, 0, 0, 1); +} + +bool +district_config_has_dependent_improvement (struct district_config * cfg, char const * name) +{ + if ((cfg == NULL) || (name == NULL) || (name[0] == '\0')) + return false; + + for (int i = 0; i < cfg->dependent_improvement_count; i++) { + char const * existing = cfg->dependent_improvements[i]; + if ((existing != NULL) && (strcmp (existing, name) == 0)) + return true; + } + return false; +} + +bool +find_civ_trait_id_by_name (struct string_slice const * name, int * out_id) +{ + if ((name == NULL) || (name->len <= 0) || (out_id == NULL)) + return false; + + struct trait_entry { char const * name; int id; } traits[] = { + {"Agricultural", 6}, + {"Commercial", 1}, + {"Expansionist", 2}, + {"Industrious", 5}, + {"Militaristic", 0}, + {"Religious", 4}, + {"Scientific", 3}, + {"Seafaring", 7} + }; + + for (int i = 0; i < ARRAY_LEN (traits); i++) { + if (slice_matches_str (name, traits[i].name)) { + *out_id = traits[i].id; + return true; + } + } + + return false; +} + +bool +find_civ_culture_id_by_name (struct string_slice const * name, int * out_id) +{ + if ((name == NULL) || (name->len <= 0) || (out_id == NULL)) + return false; + + struct culture_entry { char const * name; int id; } cultures[] = { + {"American", 0}, + {"European", 1}, + {"Roman", 2}, + {"Mid East", 3}, + {"Mideast", 3}, + {"Middle Eastern", 3}, + {"MiddleEastern", 3}, + {"Asian", 4} + }; + + for (int i = 0; i < ARRAY_LEN (cultures); i++) { + if (slice_matches_str (name, cultures[i].name)) { + *out_id = cultures[i].id; + return true; + } + } + + return false; +} + +int +find_district_index_by_name (char const * name) +{ + if ((name == NULL) || (name[0] == '\0')) + return -1; + + for (int i = 0; i < is->district_count; i++) { + char const * existing = is->district_configs[i].name; + if ((existing != NULL) && (strcmp (existing, name) == 0)) + return i; } - if (in_section) { - if (section_type == 1) - finalize_scenario_district_entry (&entry, section_start_line, &parse_errors); - else if (section_type == 2) - finalize_scenario_named_tile_entry (&named_entry, section_start_line, &parse_errors); - } + return -1; +} - free_scenario_district_entry (&entry); - free_scenario_named_tile_entry (&named_entry); - free (text); +int +find_wonder_district_index_by_name (char const * name) +{ + if ((name == NULL) || (name[0] == '\0')) + return -1; - // Append to loaded config names list - struct loaded_config_name * top_lcn = is->loaded_config_names; - while (top_lcn->next != NULL) - top_lcn = top_lcn->next; + int improv_id; + if (! stable_look_up (&is->building_name_to_id, (char *)name, &improv_id)) + return -1; - struct loaded_config_name * new_lcn = malloc (sizeof *new_lcn); - new_lcn->name = strdup (scenario_districts_path); - new_lcn->next = NULL; + return find_wonder_config_index_by_improvement_id (improv_id); +} - top_lcn->next = new_lcn; +int +find_natural_wonder_index_by_name (char const * name) +{ + if ((name == NULL) || (name[0] == '\0') || (is == NULL)) + return -1; - if ((parse_errors != NULL) || (unrecognized_keys != NULL)) { - PopupForm * popup = get_popup_form (); - popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_WARNING", -1, 0, 0, 0); - char header[256]; - snprintf (header, sizeof header, "District scenario file issues in %s:", scenario_districts_path); - header[(sizeof header) - 1] = '\0'; - PopupForm_add_text (popup, __, header, 0); - if (parse_errors != NULL) - for (struct error_line * line = parse_errors; line != NULL; line = line->next) - PopupForm_add_text (popup, __, line->text, 0); - if (unrecognized_keys != NULL) { - PopupForm_add_text (popup, __, "", 0); - PopupForm_add_text (popup, __, "Unrecognized keys:", 0); - for (struct error_line * line = unrecognized_keys; line != NULL; line = line->next) - PopupForm_add_text (popup, __, line->text, 0); + for (int i = 0; i < is->natural_wonder_count; i++) { + char const * existing = is->natural_wonder_configs[i].name; + if ((existing != NULL) && (strcmp (existing, name) == 0)) + return i; + } + return -1; +} + +City * +find_city_by_name (char const * name) +{ + if ((name == NULL) || (name[0] == '\0') || (p_cities == NULL) || (p_cities->Cities == NULL)) + return NULL; + + for (int city_index = 0; city_index <= p_cities->LastIndex; city_index++) { + City * city = get_city_ptr (city_index); + if ((city != NULL) && (city->Body.CityName != NULL)) { + (*p_OutputDebugStringA) (city->Body.CityName); } - patch_show_popup (popup, __, 0, 0); + if ((city != NULL) && (city->Body.CityName != NULL) && (strcmp (city->Body.CityName, name) == 0)) + return city; } - free_error_lines (parse_errors); - free_error_lines (unrecognized_keys); + return NULL; } void -deinit_district_images (void) +set_wonders_dependent_on_wonder_district (void) { - if (is->dc_img_state == IS_OK) { - for (int dc = 0; dc < COUNT_DISTRICT_TYPES; dc++) { - for (int variant = 0; variant < ARRAY_LEN (is->district_img_sets[dc].imgs); variant++) - for (int era = 0; era < 4; era++) - for (int col = 0; col < ARRAY_LEN (is->district_img_sets[dc].imgs[variant][era]); col++) { - Sprite * sprite = &is->district_img_sets[dc].imgs[variant][era][col]; - if (sprite->vtable != NULL) - sprite->vtable->destruct (sprite, __, 0); - } - } + if (! is->current_config.enable_districts || + ! is->current_config.enable_wonder_districts) + return; - for (int wi = 0; wi < MAX_WONDER_DISTRICT_TYPES; wi++) { - struct wonder_district_image_set * set = &is->wonder_district_img_sets[wi]; - if (set->img.vtable != NULL) - set->img.vtable->destruct (&set->img, __, 0); - if (set->construct_img.vtable != NULL) - set->construct_img.vtable->destruct (&set->construct_img, __, 0); - if (set->alt_dir_img.vtable != NULL) - set->alt_dir_img.vtable->destruct (&set->alt_dir_img, __, 0); - if (set->alt_dir_construct_img.vtable != NULL) - set->alt_dir_construct_img.vtable->destruct (&set->alt_dir_construct_img, __, 0); + struct district_config * cfg = &is->district_configs[WONDER_DISTRICT_ID]; + for (int wi = 0; wi < is->wonder_district_count; wi++) { + char const * wonder_name = is->wonder_district_configs[wi].wonder_name; + if ((wonder_name == NULL) || (wonder_name[0] == '\0')) + continue; + if (district_config_has_dependent_improvement (cfg, wonder_name)) + continue; + + int dest = cfg->dependent_improvement_count; + if (dest >= ARRAY_LEN (cfg->dependent_improvements)) { + continue; } - for (int ni = 0; ni < MAX_NATURAL_WONDER_DISTRICT_TYPES; ni++) { - Sprite * sprite = &is->natural_wonder_img_sets[ni].img; - if (sprite->vtable != NULL) - sprite->vtable->destruct (sprite, __, 0); + char * copy = strdup (wonder_name); + if (copy == NULL) { + continue; } - if (is->abandoned_district_img.vtable != NULL) - is->abandoned_district_img.vtable->destruct (&is->abandoned_district_img, __, 0); - if (is->abandoned_maritime_district_img.vtable != NULL) - is->abandoned_maritime_district_img.vtable->destruct (&is->abandoned_maritime_district_img, __, 0); + cfg->dependent_improvements[dest] = copy; + cfg->dependent_improvement_count = dest + 1; } - is->dc_img_state = IS_UNINITED; + if (cfg->max_building_index < cfg->dependent_improvement_count) + cfg->max_building_index = cfg->dependent_improvement_count; } void -clear_highlighted_worker_tiles_for_districts () +resolve_district_bonus_building_entries (struct district_bonus_list * list, + char const * district_name, + char const * bonus_name, + struct error_line ** parse_errors) { - FOR_TABLE_ENTRIES (tei, &is->highlighted_city_radius_tile_pointers) { - struct highlighted_city_radius_tile_info * info = (struct highlighted_city_radius_tile_info *)tei.value; - if (info != NULL) - free (info); + if (list == NULL) + return; + + for (int i = 0; i < list->count; i++) { + struct district_bonus_entry * entry = &list->entries[i]; + if (entry->type != DBET_BUILDING) + continue; + if ((entry->building_name == NULL) || (entry->building_name[0] == '\0')) { + entry->building_id = -1; + continue; + } + + int improv_id; + struct string_slice improv_name = { .str = (char *)entry->building_name, .len = (int)strlen (entry->building_name) }; + if (find_game_object_id_by_name (GOK_BUILDING, &improv_name, 0, &improv_id)) { + entry->building_id = improv_id; + stable_insert (&is->building_name_to_id, improv_name.str, improv_id); + } else { + entry->building_id = -1; + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": %s entry \"%.*s\" not found", + district_name, bonus_name, improv_name.len, improv_name.str); + err->text[(sizeof err->text) - 1] = '\0'; + } } - table_deinit (&is->highlighted_city_radius_tile_pointers); } - -void -reset_district_state (bool reset_tile_map) +void parse_building_and_tech_ids () { - clear_all_tracked_workers (); - deinit_district_images (); - clear_highlighted_worker_tiles_for_districts (); + struct c3x_config * cfg = &is->current_config; + char ss[200]; + struct error_line * district_parse_errors = NULL; + struct error_line * wonder_parse_errors = NULL; + for (int i = 0; i < is->district_count; i++) { + char const * district_name = (is->district_configs[i].name != NULL) ? is->district_configs[i].name : "District"; + if (is->district_configs[i].command != 0) + itable_insert (&is->command_id_to_district_id, is->district_configs[i].command, i); + is->district_infos[i].advance_prereq_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].advance_prereq_ids); j++) + is->district_infos[i].advance_prereq_ids[j] = -1; + is->district_infos[i].obsoleted_by_id = -1; + is->district_infos[i].resource_prereq_count = 0; + for (int j = 0; j < MAX_DISTRICT_DEPENDENTS; j++) + is->district_infos[i].resource_prereq_ids[j] = -1; + is->district_infos[i].resource_prereq_on_tile_id = -1; + is->district_infos[i].wonder_prereq_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].wonder_prereq_ids); j++) + is->district_infos[i].wonder_prereq_ids[j] = -1; + is->district_infos[i].natural_wonder_prereq_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].natural_wonder_prereq_ids); j++) + is->district_infos[i].natural_wonder_prereq_ids[j] = -1; - table_deinit (&is->district_tech_prereqs); - FOR_TABLE_ENTRIES (tei, &is->district_building_prereqs) { - struct district_building_prereq_list * list = (struct district_building_prereq_list *)tei.value; - if (list != NULL) - free (list); - } - table_deinit (&is->district_building_prereqs); - table_deinit (&is->command_id_to_district_id); - stable_deinit (&is->building_name_to_id); - if (reset_tile_map) { - FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { - struct district_instance * inst = (struct district_instance *)tei.value; - if (inst != NULL) - free (inst); + // Map advance prereqs to districts + int stored_tech_count = 0; + for (int j = 0; j < is->district_configs[i].advance_prereq_count; j++) { + char const * prereq = is->district_configs[i].advance_prereqs[j]; + if (prereq == NULL || prereq[0] == '\0') + continue; + int tech_id; + struct string_slice tech_name = { .str = (char *)prereq, .len = (int)strlen (prereq) }; + if (find_game_object_id_by_name (GOK_TECHNOLOGY, &tech_name, 0, &tech_id)) { + snprintf (ss, sizeof ss, "Found tech prereq \"%.*s\" for district \"%s\", ID %d\n", tech_name.len, tech_name.str, district_name, tech_id); + (*p_OutputDebugStringA) (ss); + if (stored_tech_count < ARRAY_LEN (is->district_infos[i].advance_prereq_ids)) { + is->district_infos[i].advance_prereq_ids[stored_tech_count] = tech_id; + stored_tech_count++; + } + itable_insert (&is->district_tech_prereqs, tech_id, i); + } else { + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": advance_prereqs entry \"%.*s\" not found", district_name, tech_name.len, tech_name.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + is->district_infos[i].advance_prereq_count = stored_tech_count; + + // Map obsoleted_by to tech ID + if (is->district_configs[i].obsoleted_by != NULL && is->district_configs[i].obsoleted_by != "") { + int tech_id; + struct string_slice tech_name = { .str = (char *)is->district_configs[i].obsoleted_by, .len = (int)strlen (is->district_configs[i].obsoleted_by) }; + if (find_game_object_id_by_name (GOK_TECHNOLOGY, &tech_name, 0, &tech_id)) { + snprintf (ss, sizeof ss, "Found tech obsoleted_by \"%.*s\" for district \"%s\", ID %d\n", tech_name.len, tech_name.str, district_name, tech_id); + (*p_OutputDebugStringA) (ss); + is->district_infos[i].obsoleted_by_id = tech_id; + } else { + is->district_infos[i].obsoleted_by_id = -1; + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": obsoleted_by \"%.*s\" not found", district_name, tech_name.len, tech_name.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + + // Map resource prereqs to districts (multiple resources now supported) + int stored_res_count = 0; + for (int j = 0; j < is->district_configs[i].resource_prereq_count; j++) { + if (is->district_configs[i].resource_prereqs[j] == "" || is->district_configs[i].resource_prereqs[j] == NULL) + continue; + int res_id; + struct string_slice res_name = { .str = (char *)is->district_configs[i].resource_prereqs[j], .len = (int)strlen (is->district_configs[i].resource_prereqs[j]) }; + if (find_game_object_id_by_name (GOK_RESOURCE, &res_name, 0, &res_id)) { + snprintf (ss, sizeof ss, "Found resource prereq \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, district_name, res_id); + (*p_OutputDebugStringA) (ss); + if (stored_res_count < ARRAY_LEN (is->district_infos[i].resource_prereq_ids)) { + is->district_infos[i].resource_prereq_ids[stored_res_count] = res_id; + stored_res_count++; + } + } else { + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": resource_prereq \"%.*s\" not found", district_name, res_name.len, res_name.str); + err->text[(sizeof err->text) - 1] = '\0'; + } } - table_deinit (&is->district_tile_map); - FOR_TABLE_ENTRIES (tei, &is->named_tile_map) { - struct named_tile_entry * entry = (struct named_tile_entry *)tei.value; - if (entry != NULL) - free (entry); + is->district_infos[i].resource_prereq_count = stored_res_count; + if (is->district_configs[i].resource_prereq_on_tile != NULL && is->district_configs[i].resource_prereq_on_tile != "") { + int res_id; + struct string_slice res_name = { .str = (char *)is->district_configs[i].resource_prereq_on_tile, .len = (int)strlen (is->district_configs[i].resource_prereq_on_tile) }; + if (find_game_object_id_by_name (GOK_RESOURCE, &res_name, 0, &res_id)) { + snprintf (ss, sizeof ss, "Found on-tile resource prereq \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, district_name, res_id); + (*p_OutputDebugStringA) (ss); + is->district_infos[i].resource_prereq_on_tile_id = res_id; + } else { + is->district_infos[i].resource_prereq_on_tile_id = -1; + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": resource_prereq_on_tile \"%.*s\" not found", district_name, res_name.len, res_name.str); + err->text[(sizeof err->text) - 1] = '\0'; + } } - table_deinit (&is->named_tile_map); - } - - clear_distribution_hub_tables (); - is->distribution_hub_totals_dirty = true; + int stored_wonder_count = 0; + for (int j = 0; j < is->district_configs[i].wonder_prereq_count; j++) { + if (is->district_configs[i].wonder_prereqs[j] == "" || is->district_configs[i].wonder_prereqs[j] == NULL) + continue; + int improv_id; + struct string_slice wonder_name = { .str = (char *)is->district_configs[i].wonder_prereqs[j], .len = (int)strlen (is->district_configs[i].wonder_prereqs[j]) }; + if (find_game_object_id_by_name (GOK_BUILDING, &wonder_name, 0, &improv_id)) { + if (stored_wonder_count < ARRAY_LEN (is->district_infos[i].wonder_prereq_ids)) { + is->district_infos[i].wonder_prereq_ids[stored_wonder_count] = improv_id; + stored_wonder_count += 1; + } + stable_insert (&is->building_name_to_id, wonder_name.str, improv_id); + } else { + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": wonder_prereqs entry \"%.*s\" not found", district_name, wonder_name.len, wonder_name.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + is->district_infos[i].wonder_prereq_count = stored_wonder_count; - clear_dynamic_district_definitions (); - is->district_count = is->special_district_count; + int stored_natural_wonder_count = 0; + for (int j = 0; j < is->district_configs[i].natural_wonder_prereq_count; j++) { + if (is->district_configs[i].natural_wonder_prereqs[j] == "" || is->district_configs[i].natural_wonder_prereqs[j] == NULL) + continue; + int natural_wonder_id = -1; + char const * name = is->district_configs[i].natural_wonder_prereqs[j]; + if (stable_look_up (&is->natural_wonder_name_to_id, (char *)name, &natural_wonder_id)) { + if (stored_natural_wonder_count < ARRAY_LEN (is->district_infos[i].natural_wonder_prereq_ids)) { + is->district_infos[i].natural_wonder_prereq_ids[stored_natural_wonder_count] = natural_wonder_id; + stored_natural_wonder_count += 1; + } + } else { + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": natural_wonder_prereqs entry \"%s\" not found", district_name, name); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + is->district_infos[i].natural_wonder_prereq_count = stored_natural_wonder_count; - for (int i = 0; i < COUNT_DISTRICT_TYPES; i++) { - is->district_infos[i].advance_prereq_count = 0; - for (int j = 0; j < ARRAY_LEN (is->district_infos[i].advance_prereq_ids); j++) - is->district_infos[i].advance_prereq_ids[j] = -1; - is->district_infos[i].obsoleted_by_id = -1; - is->district_infos[i].resource_prereq_count = 0; - for (int j = 0; j < ARRAY_LEN (is->district_infos[i].resource_prereq_ids); j++) - is->district_infos[i].resource_prereq_ids[j] = -1; - is->district_infos[i].resource_prereq_on_tile_id = -1; - is->district_infos[i].wonder_prereq_count = 0; - for (int j = 0; j < ARRAY_LEN (is->district_infos[i].wonder_prereq_ids); j++) - is->district_infos[i].wonder_prereq_ids[j] = -1; - is->district_infos[i].natural_wonder_prereq_count = 0; - for (int j = 0; j < ARRAY_LEN (is->district_infos[i].natural_wonder_prereq_ids); j++) - is->district_infos[i].natural_wonder_prereq_ids[j] = -1; - is->district_infos[i].dependent_building_count = 0; - for (int j = 0; j < ARRAY_LEN (is->district_infos[i].dependent_building_ids); j++) - is->district_infos[i].dependent_building_ids[j] = -1; - } + for (int j = 0; j < ARRAY_LEN (is->district_configs[i].buildable_on_district_ids); j++) + is->district_configs[i].buildable_on_district_ids[j] = -1; + is->district_configs[i].buildable_on_district_id_count = 0; - for (int civ_id = 0; civ_id < 32; civ_id++) { - FOR_TABLE_ENTRIES (tei, &is->city_pending_district_requests[civ_id]) { - struct pending_district_request * req = (struct pending_district_request *)tei.value; - if (req != NULL) - free (req); + if (is->district_configs[i].has_buildable_on_districts) { + int stored_buildable_on_count = 0; + for (int j = 0; j < is->district_configs[i].buildable_on_district_count; j++) { + char const * name = is->district_configs[i].buildable_on_districts[j]; + if (name == NULL || name[0] == '\0') + continue; + int other_district_id = find_district_index_by_name (name); + if (other_district_id >= 0) { + if (stored_buildable_on_count < ARRAY_LEN (is->district_configs[i].buildable_on_district_ids)) { + is->district_configs[i].buildable_on_district_ids[stored_buildable_on_count] = other_district_id; + stored_buildable_on_count += 1; + } + } else { + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": buildable_on_districts entry \"%s\" not found", district_name, name); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + is->district_configs[i].buildable_on_district_id_count = stored_buildable_on_count; } - table_deinit (&is->city_pending_district_requests[civ_id]); - } - table_deinit (&is->city_pending_building_orders); - is->great_wall_auto_build = GWABS_NOT_STARTED; -} - -void -clear_city_district_request (City * city, int district_id) -{ - if (! is->current_config.enable_districts || - (city == NULL) || - (district_id < 0) || (district_id >= is->district_count)) - return; + // Resolve generated resource name to ID + if (is->district_configs[i].generated_resource != NULL && is->district_configs[i].generated_resource != "") { + int res_id; + struct string_slice res_name = { .str = (char *)is->district_configs[i].generated_resource, .len = (int)strlen (is->district_configs[i].generated_resource) }; + if (find_game_object_id_by_name (GOK_RESOURCE, &res_name, 0, &res_id)) { + snprintf (ss, sizeof ss, "Found generated resource \"%.*s\" for district \"%s\", ID %d\n", res_name.len, res_name.str, district_name, res_id); + (*p_OutputDebugStringA) (ss); + is->district_configs[i].generated_resource_id = res_id; + } else { + is->district_configs[i].generated_resource_id = -1; + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": generated_resource \"%.*s\" not found", district_name, res_name.len, res_name.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + } - struct pending_district_request * req = find_pending_district_request (city, district_id); - if (req == NULL) - return; + // Map improvement prereqs to districts + int stored_count = 0; + for (int j = 0; j < is->district_configs[i].dependent_improvement_count; j++) { + int improv_id; + if (is->district_configs[i].dependent_improvements[j] == "" || is->district_configs[i].dependent_improvements[j] == NULL) + continue; - remove_pending_district_request (req); + // Gate wonder district prereqs behind enable_wonder_districts + if ((is->district_configs[i].command == UCV_Build_WonderDistrict) && (! cfg->enable_wonder_districts)) + continue; - int pending_improv_id; - if (lookup_pending_building_order (city, &pending_improv_id)) { - int required_district_id; - if (city_requires_district_for_improvement (city, pending_improv_id, &required_district_id)) { - if (required_district_id == district_id) - forget_pending_building_order (city); + struct string_slice improv_name = { .str = (char *)is->district_configs[i].dependent_improvements[j], .len = (int)strlen (is->district_configs[i].dependent_improvements[j]) }; + if (find_game_object_id_by_name (GOK_BUILDING, &improv_name, 0, &improv_id)) { + snprintf (ss, sizeof ss, "Found improvement prereq \"%.*s\" for district \"%s\", ID %d\n", improv_name.len, improv_name.str, district_name, improv_id); + (*p_OutputDebugStringA) (ss); + if (stored_count < ARRAY_LEN (is->district_infos[i].dependent_building_ids)) { + is->district_infos[i].dependent_building_ids[stored_count] = improv_id; + stored_count += 1; + } + add_district_building_prereq (improv_id, i); + stable_insert (&is->building_name_to_id, improv_name.str, improv_id); + } else { + is->district_infos[i].dependent_building_ids[j] = -1; + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": dependent_improvs entry \"%.*s\" not found", district_name, improv_name.len, improv_name.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + is->district_infos[i].dependent_building_count = stored_count; } - } -} - -bool -district_resource_prereqs_met (Tile * tile, int tile_x, int tile_y, int district_id, City * city) -{ - if ((tile == NULL) || (tile == p_null_tile) || - (district_id < 0) || (district_id >= is->district_count)) - return false; - struct district_infos * info = &is->district_infos[district_id]; - int on_tile_req = info->resource_prereq_on_tile_id; - if (on_tile_req >= 0) { - int res_here = tile->vtable->m39_Get_Resource_Type (tile); - if (res_here != on_tile_req) - return false; + resolve_district_bonus_building_entries (&is->district_configs[i].culture_bonus_extras, district_name, "culture_bonus", &district_parse_errors); + resolve_district_bonus_building_entries (&is->district_configs[i].science_bonus_extras, district_name, "science_bonus", &district_parse_errors); + resolve_district_bonus_building_entries (&is->district_configs[i].food_bonus_extras, district_name, "food_bonus", &district_parse_errors); + resolve_district_bonus_building_entries (&is->district_configs[i].gold_bonus_extras, district_name, "gold_bonus", &district_parse_errors); + resolve_district_bonus_building_entries (&is->district_configs[i].shield_bonus_extras, district_name, "shield_bonus", &district_parse_errors); + resolve_district_bonus_building_entries (&is->district_configs[i].happiness_bonus_extras, district_name, "happiness_bonus", &district_parse_errors); + resolve_district_bonus_building_entries (&is->district_configs[i].defense_bonus_extras, district_name, "defense_bonus_percent", &district_parse_errors); } - // If no resource prereqs, then the check passes - if (info->resource_prereq_count <= 0) - return true; - - int owner = tile->vtable->m38_Get_Territory_OwnerID (tile); - if (owner < 0) - return false; - - // Check all resource prereqs - ALL must be present - for (int i = 0; i < info->resource_prereq_count; i++) { - int resource_req = info->resource_prereq_ids[i]; - if (resource_req < 0) + // Map wonder names to their improvement IDs for rendering under-construction wonders + for (int wi = 0; wi < is->wonder_district_count; wi++) { + if (is->wonder_district_configs[wi].wonder_name == NULL || is->wonder_district_configs[wi].wonder_name[0] == '\0') continue; - bool has_resource = false; + int improv_id; + struct string_slice wonder_name = { .str = (char *)is->wonder_district_configs[wi].wonder_name, .len = (int)strlen (is->wonder_district_configs[wi].wonder_name) }; + if (find_game_object_id_by_name (GOK_BUILDING, &wonder_name, 0, &improv_id)) { + snprintf (ss, sizeof ss, "Found improvement prereq \"%.*s\" for wonder district \"%s\", ID %d\n", wonder_name.len, wonder_name.str, is->wonder_district_configs[wi].wonder_name, improv_id); + (*p_OutputDebugStringA) (ss); + stable_insert (&is->building_name_to_id, wonder_name.str, improv_id); + } else { + snprintf (ss, sizeof ss, "Could not find improvement prereq \"%.*s\" for wonder district \"%s\"\n", wonder_name.len, wonder_name.str, is->wonder_district_configs[wi].wonder_name); + (*p_OutputDebugStringA) (ss); + struct error_line * err = add_error_line (&wonder_parse_errors); + snprintf (err->text, sizeof err->text, "^ Wonder district \"%s\": improvement \"%.*s\" not found", (is->wonder_district_configs[wi].wonder_name != NULL) ? is->wonder_district_configs[wi].wonder_name : "Wonder District", wonder_name.len, wonder_name.str); + err->text[(sizeof err->text) - 1] = '\0'; + } + } - // Check if the specified city has this resource - if ((city != NULL) && - (city->Body.CivID == owner) && - city_radius_contains_tile (city, tile_x, tile_y) && - patch_City_has_resource (city, __, resource_req)) { - has_resource = true; + if ((district_parse_errors != NULL) || (wonder_parse_errors != NULL)) { + PopupForm * popup = get_popup_form (); + popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_WARNING", -1, 0, 0, 0); + + if (district_parse_errors != NULL) { + char header[256]; + if (is->current_districts_config_path[0] != '\0') + snprintf (header, sizeof header, "District Config errors in %s:", is->current_districts_config_path); + else + snprintf (header, sizeof header, "District Config lookup errors:"); + header[(sizeof header) - 1] = '\0'; + PopupForm_add_text (popup, __, header, false); + for (struct error_line * line = district_parse_errors; line != NULL; line = line->next) + PopupForm_add_text (popup, __, line->text, false); } - // Check cities around this tile for the resource - if (!has_resource) { - FOR_CITIES_AROUND (wai, tile_x, tile_y) { - if (patch_City_has_resource (wai.city, __, resource_req)) { - has_resource = true; - break; - } - } + if ((district_parse_errors != NULL) && (wonder_parse_errors != NULL)) + PopupForm_add_text (popup, __, "", false); + + if (wonder_parse_errors != NULL) { + char header[256]; + snprintf (header, sizeof header, "Wonder District Config lookup errors:"); + header[(sizeof header) - 1] = '\0'; + PopupForm_add_text (popup, __, header, false); + for (struct error_line * line = wonder_parse_errors; line != NULL; line = line->next) + PopupForm_add_text (popup, __, line->text, false); } - // If this required resource is not available, the check fails - if (!has_resource) - return false; + patch_show_popup (popup, __, 0, 0); + free_error_lines (district_parse_errors); + free_error_lines (wonder_parse_errors); } - - return true; } -bool -tile_has_district_at (int tile_x, int tile_y, int district_id) +void +load_districts_config () { - wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); - Tile * tile = tile_at (tile_x, tile_y); - if ((tile == NULL) || (tile == p_null_tile)) - return false; + clear_dynamic_district_definitions (); + load_dynamic_district_configs (); + load_dynamic_wonder_configs (); + load_natural_wonder_configs (); + is->district_count = is->special_district_count + is->dynamic_district_count; - struct district_instance * inst = get_district_instance (tile); - return (inst != NULL) && (inst->district_id == district_id) && (district_is_complete (tile, district_id)); + set_wonders_dependent_on_wonder_district (); + parse_building_and_tech_ids (); } -bool -tile_is_land (int civ_id, int tile_x, int tile_y, bool must_be_same_owner) +void +place_natural_wonders_on_map (void) { - if (must_be_same_owner && (civ_id <= 0)) - return false; + if (! is->current_config.enable_natural_wonders) + return; - wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); - Tile * tile = tile_at (tile_x, tile_y); - return (tile != NULL) && (tile != p_null_tile) && (! tile->vtable->m35_Check_Is_Water (tile)) && - ((! must_be_same_owner) || (tile->Territory_OwnerID == civ_id)); -} + int wonder_count = is->natural_wonder_count; + if (wonder_count <= 0) + return; -bool -tile_is_water (int tile_x, int tile_y) -{ - wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); - Tile * tile = tile_at (tile_x, tile_y); - return (tile != NULL) && (tile != p_null_tile) && (tile->vtable->m35_Check_Is_Water (tile)); -} + struct natural_wonder_candidate_list * candidate_lists = (struct natural_wonder_candidate_list *)calloc (wonder_count, sizeof *candidate_lists); + bool * already_placed = (bool *)calloc (wonder_count, sizeof *already_placed); -bool -ensure_ai_candidate_bridge_or_canals_capacity (int required) -{ - if (required <= 0) - return true; - if (required <= is->ai_candidate_bridge_or_canals_capacity) - return true; - int new_capacity = (is->ai_candidate_bridge_or_canals_capacity > 0) ? is->ai_candidate_bridge_or_canals_capacity * 2 : 4; - if (new_capacity < required) - new_capacity = required; - struct ai_candidate_bridge_or_canal_entry * larger = (struct ai_candidate_bridge_or_canal_entry *)realloc ( - is->ai_candidate_bridge_or_canals, new_capacity * sizeof *larger); - if (larger == NULL) - return false; - for (int i = is->ai_candidate_bridge_or_canals_capacity; i < new_capacity; i++) { - struct ai_candidate_bridge_or_canal_entry * entry = &larger[i]; - entry->tile_x = NULL; - entry->tile_y = NULL; - entry->tile_capacity = 0; - entry->district_id = -1; - entry->owner_civ_id = -1; - entry->tile_count = 0; - entry->assigned_tile_index = -1; - entry->assigned_worker_id = -1; - entry->completed = false; - struct pending_district_request * req = &entry->pending_req; - req->city = NULL; - req->city_id = -1; - req->civ_id = -1; - req->district_id = -1; - req->assigned_worker_id = -1; - req->target_x = -1; - req->target_y = -1; - req->worker_assigned_turn = 0; + if ((candidate_lists == NULL) || (already_placed == NULL)) { + if (candidate_lists != NULL) free (candidate_lists); + if (already_placed != NULL) free (already_placed); + return; } - is->ai_candidate_bridge_or_canals = larger; - is->ai_candidate_bridge_or_canals_capacity = new_capacity; - return true; -} - -bool -canal_has_different_adjacent_seas (int tile_x, int tile_y, int civ_id) -{ - struct water_pair { - int dx1, dy1; - int dx2, dy2; - }; - - const struct water_pair pairs[] = { - { 1, -1, -1, 1 }, // NE + SW - { 1, 1, -1, -1 }, // SE + NW - }; - Map * map = &p_bic_data->Map; - bool require_owner = (civ_id >= 0); + struct wonder_location * placements = NULL; + int placement_count = 0; + int placement_capacity = 0; + int existing_count = 0; - for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { - int ax = tile_x + pairs[i].dx1; - int ay = tile_y + pairs[i].dy1; - wrap_tile_coords (map, &ax, &ay); - Tile * first = tile_at (ax, ay); - if ((first == NULL) || (first == p_null_tile)) - continue; - if (! first->vtable->m35_Check_Is_Water (first)) - continue; - if (require_owner && (first->vtable->m38_Get_Territory_OwnerID (first) != civ_id)) + // Record existing natural wonders + FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { + struct district_instance * inst = (struct district_instance *)tei.value; + if ((inst == NULL) || (inst->district_id != NATURAL_WONDER_DISTRICT_ID)) continue; - int bx = tile_x + pairs[i].dx2; - int by = tile_y + pairs[i].dy2; - wrap_tile_coords (map, &bx, &by); - Tile * second = tile_at (bx, by); - if ((second == NULL) || (second == p_null_tile)) - continue; - if (! second->vtable->m35_Check_Is_Water (second)) - continue; - if (require_owner && (second->vtable->m38_Get_Territory_OwnerID (second) != civ_id)) + int wonder_id = inst->natural_wonder_info.natural_wonder_id; + if ((wonder_id < 0) || (wonder_id >= wonder_count)) continue; - int sea_a = first->vtable->m46_Get_ContinentID (first); - int sea_b = second->vtable->m46_Get_ContinentID (second); - if ((sea_a >= 0) && (sea_b >= 0) && (sea_a != sea_b)) - return true; - } - - return false; -} - -// Check if two water tiles can reach each other via water within a radius, excluding a blocked tile -bool -water_tiles_connected_within_radius (int start_x, int start_y, int target_x, int target_y, int block_x, int block_y, int radius) -{ - // Simple BFS using a fixed-size visited array for tiles within radius - // workable_tile_counts[6] = 137 tiles for radius 6 - int max_tiles = 137; - int visited_x[137]; - int visited_y[137]; - int visited_count = 0; - int queue_x[137]; - int queue_y[137]; - int queue_head = 0; - int queue_tail = 0; - - queue_x[queue_tail] = start_x; - queue_y[queue_tail] = start_y; - queue_tail++; - visited_x[visited_count] = start_x; - visited_y[visited_count] = start_y; - visited_count++; + already_placed[wonder_id] = true; - while (queue_head < queue_tail) { - int cx = queue_x[queue_head]; - int cy = queue_y[queue_head]; - queue_head++; + Tile * tile = (Tile *)tei.key; + int tile_x, tile_y; + if (! district_instance_get_coords (inst, tile, &tile_x, &tile_y)) + continue; - // Check 8 adjacent tiles - FOR_TILES_AROUND (tai, 9, cx, cy) { - if (tai.n == 0) + if (placement_count >= placement_capacity) { + int new_capacity = (placement_capacity > 0) ? placement_capacity * 2 : 8; + struct wonder_location * grown = + (struct wonder_location *)realloc (placements, new_capacity * sizeof *grown); + if (grown == NULL) continue; - int nx = tai.tile_x; - int ny = tai.tile_y; + placements = grown; + placement_capacity = new_capacity; + } - // Found target - if (nx == target_x && ny == target_y) - return true; + if (placements != NULL) { + placements[placement_count++] = (struct wonder_location){ + .x = (short)tile_x, + .y = (short)tile_y + }; + existing_count += 1; + } + } - // Skip blocked tile (the isthmus) - if (nx == block_x && ny == block_y) + // Build candidate lists + int map_width = p_bic_data->Map.Width; + int map_height = p_bic_data->Map.Height; + int minimum_separation = is->current_config.minimum_natural_wonder_separation; + + for (int y = 0; y < map_height; y++) { + for (int x = 0; x < map_width; x++) { + Tile * tile = tile_at (x, y); + if ((tile == NULL) || (tile == p_null_tile)) continue; - // Check if within radius of original start - int dx = nx - start_x; - int dy = ny - start_y; - if (dx < 0) dx = -dx; - if (dy < 0) dy = -dy; - // Use Chebyshev distance approximation for hex grid - int dist = (dx + dy + ((dx > dy) ? dx : dy)) / 2; - if (dist > radius) - continue; + if (! natural_wonder_tile_is_clear (tile, x, y)) continue; - Tile * adj = tai.tile; - if ((adj == NULL) || (adj == p_null_tile)) - continue; - if (! adj->vtable->m35_Check_Is_Water (adj)) + if (natural_wonder_exists_within_distance (x, y, minimum_separation)) continue; - // Check if already visited - bool already_visited = false; - for (int i = 0; i < visited_count; i++) { - if (visited_x[i] == nx && visited_y[i] == ny) { - already_visited = true; - break; - } - } - if (already_visited) - continue; + for (int ni = 0; ni < wonder_count; ni++) { + if (already_placed[ni]) + continue; - // Add to queue and visited - if (visited_count < max_tiles && queue_tail < max_tiles) { - visited_x[visited_count] = nx; - visited_y[visited_count] = ny; - visited_count++; - queue_x[queue_tail] = nx; - queue_y[queue_tail] = ny; - queue_tail++; + struct natural_wonder_district_config const * cfg = &is->natural_wonder_configs[ni]; + if (cfg->name == NULL) + continue; + + if (! natural_wonder_terrain_matches (cfg, tile, x, y)) + continue; + + natural_wonder_candidate_list_push (&candidate_lists[ni], tile, x, y); } } } - return false; -} - -// Check if tile separates adjacent water tiles that are not connected within a small radius -bool -canal_has_same_sea_isthmus (int tile_x, int tile_y, int civ_id, int check_radius) -{ - (void) civ_id; - (void) check_radius; - // If another canal exists nearby, this isn't a unique isthmus target. - FOR_TILES_AROUND (tai, workable_tile_counts[2], tile_x, tile_y) { - if (tai.n == 0) - continue; - Tile * adj = tai.tile; - if ((adj == NULL) || (adj == p_null_tile)) - continue; - struct district_instance * adj_inst = get_district_instance (adj); - if ((adj_inst != NULL) && (adj_inst->district_id == CANAL_DISTRICT_ID)) - return false; + bool wraps_horiz = (p_bic_data->Map.Flags & 1) != 0; + int newly_placed = 0; + int * wonder_order = (int *)malloc (wonder_count * sizeof *wonder_order); + if (wonder_order != NULL) { + for (int i = 0; i < wonder_count; i++) + wonder_order[i] = i; + for (int i = wonder_count - 1; i > 0; i--) { + int swap_index = rand_int (p_rand_object, __, i + 1); + int temp = wonder_order[i]; + wonder_order[i] = wonder_order[swap_index]; + wonder_order[swap_index] = temp; + } } - // Check opposite diagonal water tiles that are not connected within radius 2 - struct water_pair { - int dx1, dy1; - int dx2, dy2; - }; - - const struct water_pair pairs[] = { - { 1, -1, -1, 1 }, // NE + SW - { 1, 1, -1, -1 }, // SE + NW - }; - - for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { - int ax = tile_x + pairs[i].dx1; - int ay = tile_y + pairs[i].dy1; - wrap_tile_coords (&p_bic_data->Map, &ax, &ay); - Tile * first = tile_at (ax, ay); - if ((first == NULL) || (first == p_null_tile)) - continue; - if (! first->vtable->m35_Check_Is_Water (first)) + for (int order_index = 0; order_index < wonder_count; order_index++) { + int ni = (wonder_order != NULL) ? wonder_order[order_index] : order_index; + if (already_placed[ni]) continue; - int bx = tile_x + pairs[i].dx2; - int by = tile_y + pairs[i].dy2; - wrap_tile_coords (&p_bic_data->Map, &bx, &by); - Tile * second = tile_at (bx, by); - if ((second == NULL) || (second == p_null_tile)) - continue; - if (! second->vtable->m35_Check_Is_Water (second)) + struct natural_wonder_candidate_list * list = &candidate_lists[ni]; + if (list->count == 0) { + char msg[256]; + snprintf (msg, sizeof msg, "[C3X] No valid tiles to place natural wonder \"%s\".\n", + (is->natural_wonder_configs[ni].name != NULL) ? is->natural_wonder_configs[ni].name : "Natural Wonder"); + (*p_OutputDebugStringA) (msg); continue; - - if (! water_tiles_connected_within_radius ( - ax, ay, - bx, by, - tile_x, tile_y, 2)) { - return true; } - } - return false; -} -bool -bridge_tile_connects_two_continents (int tile_x, int tile_y, int civ_id) -{ - struct bridge_pair { - int dx1, dy1; - int dx2, dy2; - }; + int best_index = -1; + int best_dist = -1; + int best_adjacent_count = -1; + int best_target_diff = INT_MAX; + int best_rand = INT_MAX; + int best_same_type_count = -1; + int best_continent_priority = INT_MAX; + int target_x = (wonder_count > 0) + ? (int)(((long long)(2 * ni + 1) * map_width) / (2 * wonder_count)) + : (map_width >> 1); - Map * map = &p_bic_data->Map; - const struct bridge_pair pairs[] = { - {0, -2, 0, 2}, - {-2, 0, 2, 0}, - {-1, -1, 1, 1}, - {-1, 1, 1, -1}, - }; + for (int ci = 0; ci < list->count; ci++) { + struct natural_wonder_candidate * cand = &list->entries[ci]; + Tile * tile = cand->tile; + if ((tile == NULL) || (tile == p_null_tile)) continue; + if (get_district_instance (tile) != NULL) continue; + if (! natural_wonder_tile_is_clear (tile, cand->x, cand->y)) continue; + if (! natural_wonder_terrain_matches (&is->natural_wonder_configs[ni], tile, cand->x, cand->y)) continue; + if (natural_wonder_exists_within_distance (cand->x, cand->y, minimum_separation)) continue; - bool require_owner = (civ_id >= 0); + int min_dist_sq = natural_wonder_min_distance_sq (cand->x, cand->y, placements, placement_count); + if (min_dist_sq == INT_MAX) { + int span = (map_width * map_width) + (map_height * map_height); + if (span <= 0) + span = INT_MAX; + min_dist_sq = span; + } - for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { - int ax = tile_x + pairs[i].dx1; - int ay = tile_y + pairs[i].dy1; - wrap_tile_coords (map, &ax, &ay); - if (! tile_is_land (civ_id, ax, ay, require_owner)) - continue; - Tile * first = tile_at (ax, ay); - if ((first == NULL) || (first == p_null_tile)) - continue; + int dx_raw = int_abs (cand->x - target_x); + int dx_adjusted = compute_wrapped_component (dx_raw, map_width, wraps_horiz); + int rand_val = rand_int (p_rand_object, __, 0x7FFF); - int bx = tile_x + pairs[i].dx2; - int by = tile_y + pairs[i].dy2; - wrap_tile_coords (map, &bx, &by); - if (! tile_is_land (civ_id, bx, by, require_owner)) - continue; - Tile * second = tile_at (bx, by); - if ((second == NULL) || (second == p_null_tile)) - continue; + int continent_id = tile->vtable->m46_Get_ContinentID (tile); + int continent_priority = 1; + if ((continent_id >= 0) && ! continent_has_natural_wonder (continent_id, placements, placement_count)) + continent_priority = 0; - int cont_a = first->vtable->m46_Get_ContinentID (first); - int cont_b = second->vtable->m46_Get_ContinentID (second); - if ((cont_a >= 0) && (cont_b >= 0) && (cont_a != cont_b)) - return true; - } + bool adjacency_bonus_active = + (is->natural_wonder_configs[ni].adjacent_to != (enum SquareTypes)SQ_INVALID) && + (is->natural_wonder_configs[ni].adjacency_dir == DIR_ZERO); + int adjacency_count = -1; + if (adjacency_bonus_active) + adjacency_count = count_adjacent_tiles_of_type (cand->x, cand->y, + is->natural_wonder_configs[ni].adjacent_to); - return false; -} + int same_type_count = count_adjacent_tiles_of_type (cand->x, cand->y, + is->natural_wonder_configs[ni].terrain_type); -bool -tile_point_in_block (int tile_x, int tile_y, int block_x0, int block_y0, int block_x1, int block_y1) -{ - return (tile_x >= block_x0) && (tile_x < block_x1) && (tile_y >= block_y0) && (tile_y < block_y1); -} + bool better = false; + if (continent_priority < best_continent_priority) + better = true; + else if (continent_priority > best_continent_priority) + continue; -bool -tile_is_coastal_water (int tile_x, int tile_y) -{ - Map * map = &p_bic_data->Map; - wrap_tile_coords (map, &tile_x, &tile_y); - Tile * tile = tile_at (tile_x, tile_y); - if ((tile == NULL) || (tile == p_null_tile)) - return false; - if (! tile->vtable->m35_Check_Is_Water (tile)) - return false; - enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); - return base_type == SQ_Coast; -} + if (! better && adjacency_bonus_active) { + if (adjacency_count > best_adjacent_count) + better = true; + else if (adjacency_count < best_adjacent_count) + continue; + } -bool -tile_exists_at (int tile_x, int tile_y) -{ - wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); - Tile * tile = tile_at (tile_x, tile_y); - return (tile != NULL) && (tile != p_null_tile); -} + if (! better) { + if (same_type_count > best_same_type_count) + better = true; + else if (same_type_count < best_same_type_count) + continue; + } -bool -tile_is_reserved_in_district_tile_map (int tile_x, int tile_y) -{ - wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); - Tile * tile = tile_at (tile_x, tile_y); - if ((tile == NULL) || (tile == p_null_tile)) - return false; - int existing = 0; - return itable_look_up (&is->district_tile_map, (int)tile, &existing); -} + if (! better) { + if ((min_dist_sq > best_dist) || + ((min_dist_sq == best_dist) && (dx_adjusted < best_target_diff)) || + ((min_dist_sq == best_dist) && (dx_adjusted == best_target_diff) && (rand_val < best_rand))) + better = true; + else + continue; + } -bool -tile_has_diagonal_water (int tile_x, int tile_y) -{ - Map * map = &p_bic_data->Map; - int const adj_dx[4] = { 1, 1, -1, -1 }; - int const adj_dy[4] = { -1, 1, -1, 1 }; + best_dist = min_dist_sq; + best_target_diff = dx_adjusted; + best_rand = rand_val; + best_index = ci; + best_continent_priority = continent_priority; + if (adjacency_bonus_active) + best_adjacent_count = adjacency_count; + best_same_type_count = same_type_count; + } - for (int i = 0; i < 4; i++) { - int nx = tile_x + adj_dx[i]; - int ny = tile_y + adj_dy[i]; - wrap_tile_coords (map, &nx, &ny); - Tile * tile = tile_at (nx, ny); - if ((tile == NULL) || (tile == p_null_tile)) + if (best_index < 0) { + char msg[256]; + snprintf (msg, sizeof msg, "[C3X] Could not find a suitable tile for natural wonder \"%s\" after filtering.\n", + (is->natural_wonder_configs[ni].name != NULL) ? is->natural_wonder_configs[ni].name : "Natural Wonder"); + (*p_OutputDebugStringA) (msg); continue; - if (tile->vtable->m35_Check_Is_Water (tile)) - return true; - } - return false; -} + } -bool -bridge_tile_has_land_on_both_sides (int tile_x, int tile_y) -{ - struct bridge_pair { - int dx1, dy1; - int dx2, dy2; - }; + struct natural_wonder_candidate * chosen = &list->entries[best_index]; + assign_natural_wonder_to_tile (chosen->tile, chosen->x, chosen->y, ni); - const struct bridge_pair pairs[] = { - { 0, -2, 0, 2 }, - { -2, 0, 2, 0 }, - { -1, -1, 1, 1 }, - { -1, 1, 1, -1 }, - }; + if (placement_count >= placement_capacity) { + int new_capacity = (placement_capacity > 0) ? placement_capacity * 2 : 8; + struct wonder_location * grown = + (struct wonder_location *)realloc (placements, new_capacity * sizeof *grown); + if (grown != NULL) { + placements = grown; + placement_capacity = new_capacity; + } + } - for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { - int ax = tile_x + pairs[i].dx1; - int ay = tile_y + pairs[i].dy1; - if (! tile_exists_at (ax, ay)) - continue; - if (! tile_is_land (-1, ax, ay, false)) - continue; - int bx = tile_x + pairs[i].dx2; - int by = tile_y + pairs[i].dy2; - if (! tile_exists_at (bx, by)) - continue; - if (! tile_is_land (-1, bx, by, false)) - continue; - return true; + if ((placements != NULL) && (placement_count < placement_capacity)) { + placements[placement_count++] = (struct wonder_location){ + .x = chosen->x, + .y = chosen->y + }; + } + + newly_placed += 1; + + char msg[256]; + snprintf (msg, sizeof msg, "[C3X] Placed natural wonder \"%s\" at (%d,%d).\n", + (is->natural_wonder_configs[ni].name != NULL) ? is->natural_wonder_configs[ni].name : "Natural Wonder", + chosen->x, chosen->y); + (*p_OutputDebugStringA) (msg); } - return false; + char summary[256]; + snprintf (summary, sizeof summary, "[C3X] Natural wonder placement complete. Newly placed: %d, already present: %d.\n", + newly_placed, existing_count); + (*p_OutputDebugStringA) (summary); + + for (int ni = 0; ni < wonder_count; ni++) + free (candidate_lists[ni].entries); + free (wonder_order); + free (candidate_lists); + free (already_placed); + free (placements); } -bool -tile_part_of_existing_candidate (int tile_x, int tile_y) +void +init_scenario_district_entry (struct scenario_district_entry * entry) { - wrap_tile_coords (&p_bic_data->Map, &tile_x, &tile_y); - for (int ei = 0; ei < is->ai_candidate_bridge_or_canals_count; ei++) { - struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[ei]; - if (entry->tile_count <= 0) - continue; - for (int ti = 0; ti < entry->tile_count; ti++) { - if ((entry->tile_x[ti] == tile_x) && (entry->tile_y[ti] == tile_y)) - return true; - } - } - return false; + if (entry == NULL) + return; + + memset (entry, 0, sizeof *entry); } -bool -tile_has_bridge_or_canal_nearby (int tile_x, int tile_y) +void +init_scenario_named_tile_entry (struct scenario_named_tile_entry * entry) { - FOR_TILES_AROUND (tai, workable_tile_counts[1], tile_x, tile_y) { - Tile * adj = tai.tile; - if ((adj == NULL) || (adj == p_null_tile)) - continue; - struct district_instance * inst = get_district_instance (adj); - if ((inst != NULL) && - ((inst->district_id == BRIDGE_DISTRICT_ID) || (inst->district_id == CANAL_DISTRICT_ID))) - return true; - int nx = (tai.n == 0) ? tile_x : tai.tile_x; - int ny = (tai.n == 0) ? tile_y : tai.tile_y; - if (tile_part_of_existing_candidate (nx, ny)) - return true; - } - return false; + if (entry == NULL) + return; + + memset (entry, 0, sizeof *entry); } -bool -add_ai_candidate_entry (int district_id, short owner_civ_id, short * xs, short * ys, int count) +void +free_scenario_district_entry (struct scenario_district_entry * entry) { - if (count <= 0) - return false; - if (! ensure_ai_candidate_bridge_or_canals_capacity (is->ai_candidate_bridge_or_canals_count + 1)) - return false; + if (entry == NULL) + return; - struct ai_candidate_bridge_or_canal_entry * entry = &is->ai_candidate_bridge_or_canals[is->ai_candidate_bridge_or_canals_count]; - entry->tile_x = (short *)malloc (sizeof *entry->tile_x * count); - entry->tile_y = (short *)malloc (sizeof *entry->tile_y * count); - if ((entry->tile_x == NULL) || (entry->tile_y == NULL)) { - if (entry->tile_x != NULL) - free (entry->tile_x); - if (entry->tile_y != NULL) - free (entry->tile_y); - return false; + if (entry->district_name != NULL) { + free (entry->district_name); + entry->district_name = NULL; } - - entry->district_id = district_id; - entry->owner_civ_id = owner_civ_id; - entry->tile_count = (short)count; - entry->tile_capacity = count; - entry->assigned_tile_index = -1; - entry->assigned_worker_id = -1; - entry->completed = false; - for (int ti = 0; ti < count; ti++) { - entry->tile_x[ti] = xs[ti]; - entry->tile_y[ti] = ys[ti]; + if (entry->wonder_city_name != NULL) { + free (entry->wonder_city_name); + entry->wonder_city_name = NULL; } - - struct pending_district_request * req = &entry->pending_req; - req->city = NULL; - req->city_id = -1; - req->civ_id = owner_civ_id; - req->district_id = district_id; - req->assigned_worker_id = -1; - req->target_x = -1; - req->target_y = -1; - req->worker_assigned_turn = 0; - - is->ai_candidate_bridge_or_canals_count++; - return true; + if (entry->wonder_name != NULL) { + free (entry->wonder_name); + entry->wonder_name = NULL; + } + entry->has_coordinates = 0; + entry->has_district_name = 0; + entry->has_wonder_city = 0; + entry->has_wonder_name = 0; } -int -gather_bridge_line (int start_x, int start_y, int dx, int dy, int limit, - int block_x0, int block_y0, int block_x1, int block_y1, - short * out_x, short * out_y) +void +free_scenario_named_tile_entry (struct scenario_named_tile_entry * entry) { - int effective_limit = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, limit); - - if (! tile_is_coastal_water (start_x, start_y)) - return 0; - if (! bridge_tile_has_land_on_both_sides (start_x, start_y)) - return 0; - - short back_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; - short back_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; - short forward_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; - short forward_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; - int back_count = 0; - int forward_count = 0; - int remaining = effective_limit - 1; - Map * map = &p_bic_data->Map; - - int cx = start_x; - int cy = start_y; - while ((remaining > 0) && (back_count < effective_limit)) { - int nx = cx - dx; - int ny = cy - dy; - wrap_tile_coords (map, &nx, &ny); - if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) - break; - if (! tile_is_coastal_water (nx, ny)) - break; - if (! bridge_tile_has_land_on_both_sides (nx, ny)) - break; - if (tile_part_of_existing_candidate (nx, ny)) - break; - back_x[back_count] = (short)nx; - back_y[back_count] = (short)ny; - back_count++; - remaining--; - cx = nx; - cy = ny; - } + if (entry == NULL) + return; - cx = start_x; - cy = start_y; - while ((remaining > 0) && (forward_count < effective_limit)) { - int nx = cx + dx; - int ny = cy + dy; - wrap_tile_coords (map, &nx, &ny); - if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) - break; - if (! tile_is_coastal_water (nx, ny)) - break; - if (! bridge_tile_has_land_on_both_sides (nx, ny)) - break; - if (tile_part_of_existing_candidate (nx, ny)) - break; - forward_x[forward_count] = (short)nx; - forward_y[forward_count] = (short)ny; - forward_count++; - remaining--; - cx = nx; - cy = ny; + if (entry->name != NULL) { + free (entry->name); + entry->name = NULL; } + entry->has_coordinates = 0; + entry->has_name = 0; +} - int write = 0; - for (int bi = back_count - 1; bi >= 0; bi--) { - out_x[write] = back_x[bi]; - out_y[write] = back_y[bi]; - write++; - } - out_x[write] = (short)start_x; - out_y[write] = (short)start_y; - write++; - for (int fi = 0; fi < forward_count; fi++) { - out_x[write] = forward_x[fi]; - out_y[write] = forward_y[fi]; - write++; - } +void +add_scenario_district_error (struct error_line ** parse_errors, + int line_number, + char const * message) +{ + if (message == NULL) + return; - return write; + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %s", line_number, message); + err->text[(sizeof err->text) - 1] = '\0'; } -bool -bridge_line_connects_two_continents (short * xs, short * ys, int count) +int +parse_scenario_district_coordinates (struct string_slice const * value, int * out_x, int * out_y) { - for (int i = 0; i < count; i++) { - if (bridge_tile_connects_two_continents (xs[i], ys[i], -1)) - return true; + if ((value == NULL) || (out_x == NULL) || (out_y == NULL)) + return 0; + + char * text = trim_and_extract_slice (value, 0); + if (text == NULL) + return 0; + + char * cursor = text; + int success = 0; + int x, y; + if (parse_int (&cursor, &x) && + skip_punctuation (&cursor, ',') && + parse_int (&cursor, &y)) { + skip_horiz_space (&cursor); + success = (*cursor == '\0'); + if (success) { + *out_x = x; + *out_y = y; + } } - return false; + + free (text); + return success; } int -find_bridge_candidate_in_block (Map * map, int block_x0, int block_y0, int block_x1, int block_y1, - int contiguous_limit, int candidate_capacity, - short * out_x, short * out_y, short * out_owner) +finalize_scenario_district_entry (struct scenario_district_entry * entry, + int section_start_line, + struct error_line ** parse_errors) { - const int dirs[4][2] = { - { 0, -2 }, { -2, 0 }, { -1, -1 }, { -1, 1 } - }; + int success = 1; + if ((entry == NULL) || (parse_errors == NULL)) + return 0; - int max_len = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, contiguous_limit); - if (candidate_capacity > 0 && max_len > candidate_capacity) - max_len = candidate_capacity; + if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { + free_scenario_district_entry (entry); + init_scenario_district_entry (entry); + return 1; + } - for (int length = 1; length <= max_len; length++) { - for (int ti = 0; ti < map->TileCount; ti++) { - int tx = -1; - int ty = -1; - tile_index_to_coords (map, ti, &tx, &ty); - if (! tile_point_in_block (tx, ty, block_x0, block_y0, block_x1, block_y1)) - continue; - Tile * tile = tile_at (tx, ty); - if ((tile == NULL) || (tile == p_null_tile)) - continue; - if (tile_has_resource (tile)) - continue; - if (! district_is_buildable_on_square_type (&is->district_configs[BRIDGE_DISTRICT_ID], tile)) - continue; - if (tile_is_reserved_in_district_tile_map (tx, ty)) - continue; - short owner = tile->vtable->m38_Get_Territory_OwnerID (tile); + if (! entry->has_coordinates) + add_scenario_district_error (parse_errors, section_start_line, "coordinates (value is required)"); + if ((! entry->has_district_name) || (entry->district_name == NULL) || (entry->district_name[0] == '\0')) + add_scenario_district_error (parse_errors, section_start_line, "district (value is required)"); - for (int di = 0; di < (int)(sizeof (dirs) / sizeof (dirs[0])); di++) { - int dx = dirs[di][0]; - int dy = dirs[di][1]; - int end_x = tx + dx * (length - 1); - int end_y = ty + dy * (length - 1); - wrap_tile_coords (map, &end_x, &end_y); - if (! tile_point_in_block (end_x, end_y, block_x0, block_y0, block_x1, block_y1)) - continue; + if ((! entry->has_coordinates) || (! entry->has_district_name) || + (entry->district_name == NULL) || (entry->district_name[0] == '\0')) + success = 0; - bool ok = true; - for (int step = 0; step < length; step++) { - int cx = tx + dx * step; - int cy = ty + dy * step; - wrap_tile_coords (map, &cx, &cy); - if (! tile_point_in_block (cx, cy, block_x0, block_y0, block_x1, block_y1)) { - ok = false; - break; - } - if (! tile_exists_at (cx, cy)) { - ok = false; - break; - } - if (! tile_is_coastal_water (cx, cy)) { - ok = false; - break; - } - Tile * bridge_tile = tile_at (cx, cy); - if ((bridge_tile == NULL) || (bridge_tile == p_null_tile)) { - ok = false; - break; - } - if (tile_has_resource (bridge_tile)) { - ok = false; - break; - } - if (! district_is_buildable_on_square_type (&is->district_configs[BRIDGE_DISTRICT_ID], bridge_tile)) { - ok = false; - break; - } - if (tile_has_bridge_or_canal_nearby (cx, cy)) { - ok = false; - break; - } - if (tile_is_reserved_in_district_tile_map (cx, cy)) { - ok = false; - break; - } - if (tile_part_of_existing_candidate (cx, cy)) { - ok = false; - break; - } + int map_x = entry->tile_x; + int map_y = entry->tile_y; + if (success) { + wrap_tile_coords (&p_bic_data->Map, &map_x, &map_y); + Tile * tile = tile_at (map_x, map_y); + if ((tile == NULL) || (tile == p_null_tile)) { + add_scenario_district_error (parse_errors, section_start_line, "Invalid coordinates (tile not found)"); + success = 0; + } else { + int district_id = find_district_index_by_name (entry->district_name); + if ((district_id < 0) || (district_id >= is->district_count)) { + char msg[200]; + snprintf (msg, sizeof msg, "district (unrecognized name: \"%s\")", entry->district_name); + add_scenario_district_error (parse_errors, section_start_line, msg); + success = 0; + } else { + if ((district_id == NATURAL_WONDER_DISTRICT_ID) && ! is->current_config.enable_natural_wonders) { + free_scenario_district_entry (entry); + init_scenario_district_entry (entry); + return 1; } - if (! ok) - continue; - - int land_ax = tx - dx; - int land_ay = ty - dy; - wrap_tile_coords (map, &land_ax, &land_ay); - if (! tile_point_in_block (land_ax, land_ay, block_x0, block_y0, block_x1, block_y1)) - continue; - if (! tile_exists_at (land_ax, land_ay)) - continue; - if (! tile_is_land (-1, land_ax, land_ay, false)) - continue; - Tile * land_a = tile_at (land_ax, land_ay); - if ((land_a == NULL) || (land_a == p_null_tile)) - continue; - - int land_bx = tx + dx * length; - int land_by = ty + dy * length; - wrap_tile_coords (map, &land_bx, &land_by); - if (! tile_point_in_block (land_bx, land_by, block_x0, block_y0, block_x1, block_y1)) - continue; - if (! tile_exists_at (land_bx, land_by)) - continue; - if (! tile_is_land (-1, land_bx, land_by, false)) - continue; - Tile * land_b = tile_at (land_bx, land_by); - if ((land_b == NULL) || (land_b == p_null_tile)) - continue; + if ((district_id != NATURAL_WONDER_DISTRICT_ID) && ! is->current_config.enable_districts) { + free_scenario_district_entry (entry); + init_scenario_district_entry (entry); + return 1; + } + struct district_instance * inst = ensure_district_instance (tile, district_id, map_x, map_y); + if (inst == NULL) { + add_scenario_district_error (parse_errors, section_start_line, "Failed to create district instance"); + success = 0; + } else { + inst->district_id = district_id; + district_instance_set_coords (inst, map_x, map_y); + inst->state = DS_COMPLETED; + inst->wonder_info.state = WDS_UNUSED; + inst->wonder_info.city = NULL; + inst->wonder_info.city_id = -1; + inst->wonder_info.wonder_index = -1; + inst->natural_wonder_info.natural_wonder_id = -1; - int cont_a = land_a->vtable->m46_Get_ContinentID (land_a); - int cont_b = land_b->vtable->m46_Get_ContinentID (land_b); - if ((cont_a < 0) || (cont_b < 0) || (cont_a == cont_b)) - continue; + if (district_id == WONDER_DISTRICT_ID) { + int has_city = entry->has_wonder_city && + (entry->wonder_city_name != NULL) && (entry->wonder_city_name[0] != '\0'); + int has_wonder = entry->has_wonder_name && + (entry->wonder_name != NULL) && (entry->wonder_name[0] != '\0'); + if (! has_city || ! has_wonder) { + add_scenario_district_error (parse_errors, section_start_line, "Wonder district requires both wonder_city and wonder_name"); + success = 0; + } else { + int wonder_index = find_wonder_district_index_by_name (entry->wonder_name); + if (wonder_index < 0) { + char msg[200]; + snprintf (msg, sizeof msg, "wonder_name (unrecognized wonder: \"%s\")", entry->wonder_name); + add_scenario_district_error (parse_errors, section_start_line, msg); + success = 0; + } else { + inst->wonder_info.city = NULL; + inst->wonder_info.city_id = -1; + inst->wonder_info.state = WDS_COMPLETED; + inst->wonder_info.wonder_index = wonder_index; + } + } + } else if (district_id == NATURAL_WONDER_DISTRICT_ID) { + int has_name = entry->has_wonder_name && + (entry->wonder_name != NULL) && (entry->wonder_name[0] != '\0'); + if (! has_name) { + add_scenario_district_error (parse_errors, section_start_line, "Natural Wonder district requires wonder_name"); + success = 0; + } else { + int natural_index = find_natural_wonder_index_by_name (entry->wonder_name); + if (natural_index < 0) { + char msg[200]; + snprintf (msg, sizeof msg, "wonder_name (unrecognized natural wonder: \"%s\")", entry->wonder_name); + add_scenario_district_error (parse_errors, section_start_line, msg); + success = 0; + } else + inst->natural_wonder_info.natural_wonder_id = natural_index; + } + if (entry->has_wonder_city) + add_scenario_district_error (parse_errors, section_start_line, "wonder_city ignored for Natural Wonder district entries"); + } else if (entry->has_wonder_city || entry->has_wonder_name) { + add_scenario_district_error (parse_errors, section_start_line, "wonder_* fields only valid for Wonder or Natural Wonder district entries"); + } - for (int step = 0; step < length; step++) { - int cx = tx + dx * step; - int cy = ty + dy * step; - wrap_tile_coords (map, &cx, &cy); - out_x[step] = (short)cx; - out_y[step] = (short)cy; + if (success) { + if (district_id != NATURAL_WONDER_DISTRICT_ID && !tile->vtable->m18_Check_Mines (tile, __, 0)) + tile->vtable->m56_Set_Tile_Flags (tile, __, 0, TILE_FLAG_MINE, map_x, map_y); + set_tile_unworkable_for_all_cities (tile, map_x, map_y); + } } - *out_owner = owner; - return length; } } } - return 0; + + free_scenario_district_entry (entry); + init_scenario_district_entry (entry); + return success; } -int -gather_canal_line (int start_x, int start_y, int dx, int dy, int limit, - int block_x0, int block_y0, int block_x1, int block_y1, - short * out_x, short * out_y) +bool +tile_can_be_named (Tile * tile, int tile_x, int tile_y) { - int effective_limit = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, limit); - - if (! tile_is_land (-1, start_x, start_y, false)) - return 0; - if (tile_part_of_existing_candidate (start_x, start_y)) - return 0; + if ((tile == NULL) || (tile == p_null_tile)) + return false; + struct district_instance * inst = get_district_instance (tile); + if ((inst != NULL) && (inst->district_id == NATURAL_WONDER_DISTRICT_ID)) + return false; + return true; +} - short back_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; - short back_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; - short forward_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; - short forward_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; - int back_count = 0; - int forward_count = 0; - int remaining = effective_limit - 1; - Map * map = &p_bic_data->Map; +void +remove_named_tile_entry (Tile * tile) +{ + struct named_tile_entry * entry = get_named_tile_entry (tile); + if (entry == NULL) + return; + itable_remove (&is->named_tile_map, (int)tile); + free (entry); +} - int cx = start_x; - int cy = start_y; - while ((remaining > 0) && (back_count < effective_limit)) { - int nx = cx - dx; - int ny = cy - dy; - wrap_tile_coords (map, &nx, &ny); - if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) - break; - if (! tile_is_land (-1, nx, ny, false)) - break; - if (tile_part_of_existing_candidate (nx, ny)) - break; - back_x[back_count] = (short)nx; - back_y[back_count] = (short)ny; - back_count++; - remaining--; - cx = nx; - cy = ny; +void +set_named_tile_entry (Tile * tile, int tile_x, int tile_y, char const * name) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return; + if ((name == NULL) || (name[0] == '\0')) { + remove_named_tile_entry (tile); + return; } - cx = start_x; - cy = start_y; - while ((remaining > 0) && (forward_count < effective_limit)) { - int nx = cx + dx; - int ny = cy + dy; - wrap_tile_coords (map, &nx, &ny); - if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) - break; - if (! tile_is_land (-1, nx, ny, false)) - break; - if (tile_part_of_existing_candidate (nx, ny)) - break; - forward_x[forward_count] = (short)nx; - forward_y[forward_count] = (short)ny; - forward_count++; - remaining--; - cx = nx; - cy = ny; + struct named_tile_entry * entry = get_named_tile_entry (tile); + if (entry == NULL) { + entry = calloc (1, sizeof *entry); + if (entry == NULL) + return; + itable_insert (&is->named_tile_map, (int)tile, (int)entry); } + entry->tile_x = tile_x; + entry->tile_y = tile_y; + strncpy (entry->name, name, sizeof entry->name); + entry->name[(sizeof entry->name) - 1] = '\0'; +} - int write = 0; - for (int bi = back_count - 1; bi >= 0; bi--) { - out_x[write] = back_x[bi]; - out_y[write] = back_y[bi]; - write++; +int +finalize_scenario_named_tile_entry (struct scenario_named_tile_entry * entry, + int section_start_line, + struct error_line ** parse_errors) +{ + int success = 1; + if ((entry == NULL) || (parse_errors == NULL)) + return 0; + + if (! is->current_config.enable_named_tiles) { + free_scenario_named_tile_entry (entry); + init_scenario_named_tile_entry (entry); + return 1; } - out_x[write] = (short)start_x; - out_y[write] = (short)start_y; - write++; - for (int fi = 0; fi < forward_count; fi++) { - out_x[write] = forward_x[fi]; - out_y[write] = forward_y[fi]; - write++; + + if (! entry->has_coordinates) + add_scenario_district_error (parse_errors, section_start_line, "coordinates (value is required)"); + if ((! entry->has_name) || (entry->name == NULL) || (entry->name[0] == '\0')) + add_scenario_district_error (parse_errors, section_start_line, "name (value is required)"); + + if ((! entry->has_coordinates) || (! entry->has_name) || + (entry->name == NULL) || (entry->name[0] == '\0')) + success = 0; + + int map_x = entry->tile_x; + int map_y = entry->tile_y; + if (success) { + wrap_tile_coords (&p_bic_data->Map, &map_x, &map_y); + Tile * tile = tile_at (map_x, map_y); + if ((tile == NULL) || (tile == p_null_tile)) { + add_scenario_district_error (parse_errors, section_start_line, "Invalid coordinates (tile not found)"); + success = 0; + } else if (! tile_can_be_named (tile, map_x, map_y)) { + add_scenario_district_error (parse_errors, section_start_line, "Invalid coordinates (tile cannot be named)"); + success = 0; + } else { + set_named_tile_entry (tile, map_x, map_y, entry->name); + } } - return write; + free_scenario_named_tile_entry (entry); + init_scenario_named_tile_entry (entry); + return success; } -bool -cluster_connects_two_seas_or_isthmus (short * xs, short * ys, int count) +void +handle_scenario_district_key (struct scenario_district_entry * entry, + struct string_slice const * key, + struct string_slice const * value, + int line_number, + struct error_line ** parse_errors, + struct error_line ** unrecognized_keys) +{ + if ((entry == NULL) || (key == NULL) || (value == NULL)) + return; + + if (slice_matches_str (key, "coordinates")) { + int x, y; + if (parse_scenario_district_coordinates (value, &x, &y)) { + entry->tile_x = x; + entry->tile_y = y; + entry->has_coordinates = 1; + } else + add_scenario_district_error (parse_errors, line_number, "coordinates (expected format: x,y)"); + + } else if (slice_matches_str (key, "district")) { + if (entry->district_name != NULL) { + free (entry->district_name); + entry->district_name = NULL; + } + entry->district_name = copy_trimmed_string_or_null (value, 1); + entry->has_district_name = (entry->district_name != NULL); + if (! entry->has_district_name) + add_scenario_district_error (parse_errors, line_number, "district (value is required)"); + + } else if (slice_matches_str (key, "wonder_city")) { + if (entry->wonder_city_name != NULL) { + free (entry->wonder_city_name); + entry->wonder_city_name = NULL; + } + entry->wonder_city_name = copy_trimmed_string_or_null (value, 1); + entry->has_wonder_city = (entry->wonder_city_name != NULL); + + } else if (slice_matches_str (key, "wonder_name")) { + if (entry->wonder_name != NULL) { + free (entry->wonder_name); + entry->wonder_name = NULL; + } + entry->wonder_name = copy_trimmed_string_or_null (value, 1); + entry->has_wonder_name = (entry->wonder_name != NULL); + + } else + add_unrecognized_key_error (unrecognized_keys, line_number, key); +} + +void +handle_scenario_named_tile_key (struct scenario_named_tile_entry * entry, + struct string_slice const * key, + struct string_slice const * value, + int line_number, + struct error_line ** parse_errors, + struct error_line ** unrecognized_keys) +{ + if ((entry == NULL) || (key == NULL) || (value == NULL)) + return; + + if (slice_matches_str (key, "coordinates")) { + int x, y; + if (parse_scenario_district_coordinates (value, &x, &y)) { + entry->tile_x = x; + entry->tile_y = y; + entry->has_coordinates = 1; + } else + add_scenario_district_error (parse_errors, line_number, "coordinates (expected format: x,y)"); + + } else if (slice_matches_str (key, "name")) { + if (entry->name != NULL) { + free (entry->name); + entry->name = NULL; + } + entry->name = copy_trimmed_string_or_null (value, 1); + entry->has_name = (entry->name != NULL); + if (! entry->has_name) + add_scenario_district_error (parse_errors, line_number, "name (value is required)"); + + } else + add_unrecognized_key_error (unrecognized_keys, line_number, key); +} + +// Parses a .c3x.txt file corresponding to the given scenario file path, loading district instances as specified. +// Attempts the scenario_path first, then scenario_config_path; if neither yields a readable file, no action is taken. +// +// The expected file format itself is very simple. Example: +// +// ``` +// DISTRICTS +// +// #District +// coordinates = 12,28 +// district = Entertainment Complex +// +// #District +// coordinates = 9,23 +// district = Wonder District +// wonder_city = Rome +// wonder_name = The Pyramids +// +// #District +// coordinates = 10,30 +// district = Natural Wonder +// wonder_name = Mount Everest +// +// #NamedTile +// coordinates = 41,23 +// name = Tiber River +// ``` +// +// Details at https://github.com/instafluff0/Civ3_Editor_Fork_for_C3X_Districts +void +load_scenario_districts_from_file () { - for (int i = 0; i < count; i++) { - if (canal_has_different_adjacent_seas (xs[i], ys[i], -1)) - return true; - if (canal_has_same_sea_isthmus (xs[i], ys[i], -1, 2)) - return true; - } - return false; -} + char * scenario_filename = "scenario.districts.txt"; + char * scenario_districts_path = BIC_get_asset_path (p_bic_data, __, scenario_filename, false); -int -find_canal_candidate_in_block (Map * map, int block_x0, int block_y0, int block_x1, int block_y1, - int contiguous_limit, int candidate_capacity, - short * out_x, short * out_y, short * out_owner) -{ - const int dir_dx[8] = { 0, 1, 2, 1, 0, -1, -2, -1 }; - const int dir_dy[8] = { -2, -1, 0, 1, 2, 1, 0, -1 }; + // BIC_get_asset_path returns the file name when it can't find the file + if ((scenario_districts_path == NULL) || (0 == strcmp (scenario_filename, scenario_districts_path))) + return; - int max_len = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, contiguous_limit); - if (candidate_capacity > 0 && max_len > candidate_capacity) - max_len = candidate_capacity; + char * text = file_to_string (scenario_districts_path); + if (text == NULL) + return; - int tile_count = map->TileCount; - int * visit = (int *)malloc (sizeof (*visit) * tile_count); - int * queue_x = (int *)malloc (sizeof (*queue_x) * tile_count); - int * queue_y = (int *)malloc (sizeof (*queue_y) * tile_count); - if ((visit == NULL) || (queue_x == NULL) || (queue_y == NULL)) { - if (visit != NULL) - free (visit); - if (queue_x != NULL) - free (queue_x); - if (queue_y != NULL) - free (queue_y); - return 0; - } - for (int i = 0; i < tile_count; i++) - visit[i] = 0; + struct scenario_district_entry entry; + struct scenario_named_tile_entry named_entry; + init_scenario_district_entry (&entry); + init_scenario_named_tile_entry (&named_entry); + int in_section = 0; + int section_start_line = 0; + int section_type = 0; + int line_number = 0; + int header_seen = 0; + struct error_line * unrecognized_keys = NULL; + struct error_line * parse_errors = NULL; - int visit_mark = 1; + char * cursor = text; + while (*cursor != '\0') { + line_number += 1; - for (int length = 1; length <= max_len; length++) { - for (int ti = 0; ti < map->TileCount; ti++) { - int start_x = -1; - int start_y = -1; - tile_index_to_coords (map, ti, &start_x, &start_y); - if (! tile_point_in_block (start_x, start_y, block_x0, block_y0, block_x1, block_y1)) - continue; - if (! tile_is_land (-1, start_x, start_y, false)) - continue; - if (tile_part_of_existing_candidate (start_x, start_y)) - continue; - if (tile_has_bridge_or_canal_nearby (start_x, start_y)) - continue; - if (tile_is_reserved_in_district_tile_map (start_x, start_y)) - continue; - Tile * start_tile = tile_at (start_x, start_y); - if ((start_tile == NULL) || (start_tile == p_null_tile)) - continue; - if (tile_has_resource (start_tile)) - continue; - if (! district_is_buildable_on_square_type (&is->district_configs[CANAL_DISTRICT_ID], start_tile)) - continue; - int continent_id = start_tile->vtable->m46_Get_ContinentID (start_tile); - if (continent_id < 0) - continue; - short owner = start_tile->vtable->m38_Get_Territory_OwnerID (start_tile); + char * line_start = cursor; + char * line_end = cursor; + while ((*line_end != '\0') && (*line_end != '\n')) + line_end++; - int stack_len = 1; - int dir_stack[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; - int path_dir[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; - out_x[0] = (short)start_x; - out_y[0] = (short)start_y; - dir_stack[0] = -1; - path_dir[0] = -1; + int line_len = line_end - line_start; + int has_newline = (*line_end == '\n'); + if (has_newline) + *line_end = '\0'; - while (stack_len > 0) { - int depth = stack_len - 1; - int cx = out_x[depth]; - int cy = out_y[depth]; + struct string_slice line_slice = { .str = line_start, .len = line_len }; + struct string_slice trimmed = trim_string_slice (&line_slice, 0); + if (line_is_empty_or_comment (&trimmed)) { + cursor = has_newline ? line_end + 1 : line_end; + continue; + } - if (depth + 1 == length) { - bool endpoints_ok = false; - if (length == 1) { - int ex = out_x[0]; - int ey = out_y[0]; - bool pair_ok = false; - if (tile_is_water (ex, ey - 2) && tile_is_water (ex, ey + 2)) pair_ok = true; - if (tile_is_water (ex - 2, ey) && tile_is_water (ex + 2, ey)) pair_ok = true; - if (tile_is_water (ex - 1, ey - 1) && tile_is_water (ex + 1, ey + 1)) pair_ok = true; - if (tile_is_water (ex - 1, ey + 1) && tile_is_water (ex + 1, ey - 1)) pair_ok = true; - if (pair_ok && tile_has_diagonal_water (ex, ey)) - endpoints_ok = true; - } else { - int first_dir = path_dir[1]; - int last_dir = path_dir[length - 1]; - int sx = out_x[0]; - int sy = out_y[0]; - int ex = out_x[length - 1]; - int ey = out_y[length - 1]; - bool start_water = tile_is_water (sx - dir_dx[first_dir], sy - dir_dy[first_dir]); - bool end_water = tile_is_water (ex + dir_dx[last_dir], ey + dir_dy[last_dir]); - if (start_water && end_water && - tile_has_diagonal_water (sx, sy) && - tile_has_diagonal_water (ex, ey)) - endpoints_ok = true; - } + // Keep support for legacy header, technically not needed + if (! header_seen) { + if (slice_matches_str (&trimmed, "DISTRICTS")) { + header_seen = 1; + cursor = has_newline ? line_end + 1 : line_end; + continue; + } + } - bool buildable = true; - if (endpoints_ok) { - for (int pi = 0; pi < length; pi++) { - Tile * path_tile = tile_at (out_x[pi], out_y[pi]); - if ((path_tile == NULL) || (path_tile == p_null_tile) || - tile_has_resource (path_tile) || - (! district_is_buildable_on_square_type (&is->district_configs[CANAL_DISTRICT_ID], path_tile)) || - tile_is_reserved_in_district_tile_map (out_x[pi], out_y[pi])) { - buildable = false; - break; - } - if (tile_has_bridge_or_canal_nearby (out_x[pi], out_y[pi])) { - buildable = false; - break; - } - } - } else { - buildable = false; - } + if (trimmed.str[0] == '#') { + struct string_slice directive = trimmed; + directive.str += 1; + directive.len -= 1; + directive = trim_string_slice (&directive, 0); + if (slice_matches_str (&directive, "District")) { + if (in_section) { + if (section_type == 1) + finalize_scenario_district_entry (&entry, section_start_line, &parse_errors); + else if (section_type == 2) + finalize_scenario_named_tile_entry (&named_entry, section_start_line, &parse_errors); + } + in_section = 1; + section_type = 1; + section_start_line = line_number; + free_scenario_district_entry (&entry); + init_scenario_district_entry (&entry); + } else if (slice_matches_str (&directive, "NamedTile")) { + if (in_section) { + if (section_type == 1) + finalize_scenario_district_entry (&entry, section_start_line, &parse_errors); + else if (section_type == 2) + finalize_scenario_named_tile_entry (&named_entry, section_start_line, &parse_errors); + } + in_section = 1; + section_type = 2; + section_start_line = line_number; + free_scenario_named_tile_entry (&named_entry); + init_scenario_named_tile_entry (&named_entry); + } + cursor = has_newline ? line_end + 1 : line_end; + continue; + } - if (buildable) { - // Collect adjacent land tiles for component checks - int adj_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES * 8]; - int adj_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES * 8]; - int adj_count = 0; - for (int pi = 0; pi < length; pi++) { - int px = out_x[pi]; - int py = out_y[pi]; - for (int di = 0; di < 8; di++) { - int nx = px + dir_dx[di]; - int ny = py + dir_dy[di]; - wrap_tile_coords (map, &nx, &ny); - if (! tile_is_land (-1, nx, ny, false)) - continue; - Tile * adj = tile_at (nx, ny); - if ((adj == NULL) || (adj == p_null_tile)) - continue; - if (adj->vtable->m46_Get_ContinentID (adj) != continent_id) - continue; - bool in_path = false; - for (int pj = 0; pj < length; pj++) { - if ((out_x[pj] == nx) && (out_y[pj] == ny)) { - in_path = true; - break; - } - } - if (in_path) - continue; - bool seen = false; - for (int aj = 0; aj < adj_count; aj++) { - if ((adj_x[aj] == nx) && (adj_y[aj] == ny)) { - seen = true; - break; - } - } - if (! seen && (adj_count < (int)(sizeof (adj_x) / sizeof (adj_x[0])))) { - adj_x[adj_count] = nx; - adj_y[adj_count] = ny; - adj_count++; - } - } - } + if (! in_section) { + cursor = has_newline ? line_end + 1 : line_end; + continue; + } + + struct string_slice key_slice = {0}; + struct string_slice value_slice = {0}; + switch (parse_trimmed_key_value (&trimmed, &key_slice, &value_slice)) { + case KVP_NO_EQUALS: + add_scenario_district_error (&parse_errors, line_number, "(expected '=')"); + break; + case KVP_EMPTY_KEY: + add_scenario_district_error (&parse_errors, line_number, "(missing key)"); + break; + case KVP_SUCCESS: + if (section_type == 1) + handle_scenario_district_key (&entry, &key_slice, &value_slice, line_number, &parse_errors, &unrecognized_keys); + else if (section_type == 2) + handle_scenario_named_tile_key (&named_entry, &key_slice, &value_slice, line_number, &parse_errors, &unrecognized_keys); + break; + } - if (adj_count >= 2) { - int best1 = 0; - int best2 = 0; - int min_land = is->current_config.ai_canal_eval_min_bisected_land_tiles; - if (min_land < 1) - min_land = 1; - visit_mark++; - if (visit_mark == 0) { - visit_mark = 1; - for (int i = 0; i < tile_count; i++) - visit[i] = 0; - } + cursor = has_newline ? line_end + 1 : line_end; + } - for (int ai = 0; ai < adj_count; ai++) { - int aidx = tile_coords_to_index (map, adj_x[ai], adj_y[ai]); - if ((aidx < 0) || (visit[aidx] == visit_mark)) - continue; + if (in_section) { + if (section_type == 1) + finalize_scenario_district_entry (&entry, section_start_line, &parse_errors); + else if (section_type == 2) + finalize_scenario_named_tile_entry (&named_entry, section_start_line, &parse_errors); + } - int comp_size = 0; - int head = 0; - int tail = 0; - visit[aidx] = visit_mark; - queue_x[tail] = adj_x[ai]; - queue_y[tail] = adj_y[ai]; - tail++; + free_scenario_district_entry (&entry); + free_scenario_named_tile_entry (&named_entry); + free (text); - while (head < tail) { - int qx = queue_x[head]; - int qy = queue_y[head]; - head++; - comp_size++; - for (int di = 0; di < 8; di++) { - int nx = qx + dir_dx[di]; - int ny = qy + dir_dy[di]; - wrap_tile_coords (map, &nx, &ny); - if (! tile_is_land (-1, nx, ny, false)) - continue; - Tile * adj = tile_at (nx, ny); - if ((adj == NULL) || (adj == p_null_tile)) - continue; - if (adj->vtable->m46_Get_ContinentID (adj) != continent_id) - continue; - bool blocked = false; - for (int pj = 0; pj < length; pj++) { - if ((out_x[pj] == nx) && (out_y[pj] == ny)) { - blocked = true; - break; - } - } - if (blocked) - continue; - int nidx = tile_coords_to_index (map, nx, ny); - if ((nidx < 0) || (visit[nidx] == visit_mark)) - continue; - visit[nidx] = visit_mark; - queue_x[tail] = nx; - queue_y[tail] = ny; - tail++; - } - } + // Append to loaded config names list + struct loaded_config_name * top_lcn = is->loaded_config_names; + while (top_lcn->next != NULL) + top_lcn = top_lcn->next; - if (comp_size > best1) { - best2 = best1; - best1 = comp_size; - } else if (comp_size > best2) { - best2 = comp_size; - } - } + struct loaded_config_name * new_lcn = malloc (sizeof *new_lcn); + new_lcn->name = strdup (scenario_districts_path); + new_lcn->next = NULL; - if ((best1 >= min_land) && (best2 >= min_land)) { - *out_owner = owner; - free (visit); - free (queue_x); - free (queue_y); - return length; - } - } - } - dir_stack[depth] = -1; - stack_len--; - continue; + top_lcn->next = new_lcn; + + if ((parse_errors != NULL) || (unrecognized_keys != NULL)) { + PopupForm * popup = get_popup_form (); + popup->vtable->set_text_key_and_flags (popup, __, is->mod_script_path, "C3X_WARNING", -1, 0, 0, 0); + char header[256]; + snprintf (header, sizeof header, "District scenario file issues in %s:", scenario_districts_path); + header[(sizeof header) - 1] = '\0'; + PopupForm_add_text (popup, __, header, 0); + if (parse_errors != NULL) + for (struct error_line * line = parse_errors; line != NULL; line = line->next) + PopupForm_add_text (popup, __, line->text, 0); + if (unrecognized_keys != NULL) { + PopupForm_add_text (popup, __, "", 0); + PopupForm_add_text (popup, __, "Unrecognized keys:", 0); + for (struct error_line * line = unrecognized_keys; line != NULL; line = line->next) + PopupForm_add_text (popup, __, line->text, 0); + } + patch_show_popup (popup, __, 0, 0); + } + + free_error_lines (parse_errors); + free_error_lines (unrecognized_keys); +} + +void +deinit_district_images (void) +{ + if (is->dc_img_state == IS_OK) { + for (int dc = 0; dc < COUNT_DISTRICT_TYPES; dc++) { + for (int variant = 0; variant < ARRAY_LEN (is->district_img_sets[dc].imgs); variant++) + for (int era = 0; era < 4; era++) + for (int col = 0; col < ARRAY_LEN (is->district_img_sets[dc].imgs[variant][era]); col++) { + Sprite * sprite = &is->district_img_sets[dc].imgs[variant][era][col]; + if (sprite->vtable != NULL) + sprite->vtable->destruct (sprite, __, 0); } + } - int next_dir = dir_stack[depth] + 1; - bool advanced = false; - while (next_dir < 8) { - int ndx = dir_dx[next_dir]; - int ndy = dir_dy[next_dir]; - int nx = cx + ndx; - int ny = cy + ndy; - wrap_tile_coords (map, &nx, &ny); - dir_stack[depth] = next_dir; + for (int wi = 0; wi < MAX_WONDER_DISTRICT_TYPES; wi++) { + struct wonder_district_image_set * set = &is->wonder_district_img_sets[wi]; + if (set->img.vtable != NULL) + set->img.vtable->destruct (&set->img, __, 0); + if (set->construct_img.vtable != NULL) + set->construct_img.vtable->destruct (&set->construct_img, __, 0); + if (set->alt_dir_img.vtable != NULL) + set->alt_dir_img.vtable->destruct (&set->alt_dir_img, __, 0); + if (set->alt_dir_construct_img.vtable != NULL) + set->alt_dir_construct_img.vtable->destruct (&set->alt_dir_construct_img, __, 0); + } - if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) { - next_dir++; - continue; - } - if (! tile_is_land (-1, nx, ny, false)) { - next_dir++; - continue; - } - if (tile_part_of_existing_candidate (nx, ny)) { - next_dir++; - continue; - } - if (tile_is_reserved_in_district_tile_map (nx, ny)) { - next_dir++; - continue; - } - Tile * next_tile = tile_at (nx, ny); - if ((next_tile == NULL) || (next_tile == p_null_tile)) { - next_dir++; - continue; - } - if (tile_has_resource (next_tile)) { - next_dir++; - continue; - } - if (! district_is_buildable_on_square_type (&is->district_configs[CANAL_DISTRICT_ID], next_tile)) { - next_dir++; - continue; - } - if (tile_has_bridge_or_canal_nearby (nx, ny)) { - next_dir++; - continue; - } - if (next_tile->vtable->m46_Get_ContinentID (next_tile) != continent_id) { - next_dir++; - continue; - } - bool dup = false; - for (int pi = 0; pi < depth + 1; pi++) { - if ((out_x[pi] == nx) && (out_y[pi] == ny)) { - dup = true; - break; - } - } - if (dup) { - next_dir++; - continue; - } + for (int ni = 0; ni < MAX_NATURAL_WONDER_DISTRICT_TYPES; ni++) { + Sprite * sprite = &is->natural_wonder_img_sets[ni].img; + if (sprite->vtable != NULL) + sprite->vtable->destruct (sprite, __, 0); + } - if (depth >= 1) { - int prev_dir = dir_stack[depth - 1]; - int diff = next_dir - prev_dir; - if (diff < 0) - diff += 8; - if (diff > 4) - diff = 8 - diff; - if (diff > 1) { - next_dir++; - continue; - } - } + if (is->abandoned_district_img.vtable != NULL) + is->abandoned_district_img.vtable->destruct (&is->abandoned_district_img, __, 0); + if (is->abandoned_maritime_district_img.vtable != NULL) + is->abandoned_maritime_district_img.vtable->destruct (&is->abandoned_maritime_district_img, __, 0); + } - out_x[depth + 1] = (short)nx; - out_y[depth + 1] = (short)ny; - dir_stack[depth + 1] = -1; - path_dir[depth + 1] = next_dir; - stack_len++; - advanced = true; - break; - } + is->dc_img_state = IS_UNINITED; +} - if (! advanced) { - dir_stack[depth] = -1; - stack_len--; - } - } - } +void +clear_highlighted_worker_tiles_for_districts () +{ + FOR_TABLE_ENTRIES (tei, &is->highlighted_city_radius_tile_pointers) { + struct highlighted_city_radius_tile_info * info = (struct highlighted_city_radius_tile_info *)tei.value; + if (info != NULL) + free (info); } - - free (visit); - free (queue_x); - free (queue_y); - return 0; + table_deinit (&is->highlighted_city_radius_tile_pointers); } + void -generate_ai_bridge_candidates_by_block (Map * map, int block_size, int contiguous_limit) +reset_district_state (bool reset_tile_map) { - if ((map == NULL) || (block_size <= 0)) - return; + clear_all_tracked_workers (); + deinit_district_images (); + clear_highlighted_worker_tiles_for_districts (); - int width = map->Width; - int height = map->Height; - if ((width <= 0) || (height <= 0)) - return; + table_deinit (&is->district_tech_prereqs); + FOR_TABLE_ENTRIES (tei, &is->district_building_prereqs) { + struct district_building_prereq_list * list = (struct district_building_prereq_list *)tei.value; + if (list != NULL) + free (list); + } + table_deinit (&is->district_building_prereqs); + table_deinit (&is->command_id_to_district_id); + stable_deinit (&is->building_name_to_id); + if (reset_tile_map) { + FOR_TABLE_ENTRIES (tei, &is->district_tile_map) { + struct district_instance * inst = (struct district_instance *)tei.value; + if (inst != NULL) + free (inst); + } + table_deinit (&is->district_tile_map); + FOR_TABLE_ENTRIES (tei, &is->named_tile_map) { + struct named_tile_entry * entry = (struct named_tile_entry *)tei.value; + if (entry != NULL) + free (entry); + } + table_deinit (&is->named_tile_map); + } - if (block_size < 1) - block_size = 1; + clear_distribution_hub_tables (); - int candidate_capacity = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, contiguous_limit); + is->distribution_hub_totals_dirty = true; - short * candidate_x = (short *)malloc (sizeof *candidate_x * candidate_capacity); - short * candidate_y = (short *)malloc (sizeof *candidate_y * candidate_capacity); - if ((candidate_x == NULL) || (candidate_y == NULL)) { - if (candidate_x != NULL) - free (candidate_x); - if (candidate_y != NULL) - free (candidate_y); - return; + clear_dynamic_district_definitions (); + is->district_count = is->special_district_count; + + for (int i = 0; i < COUNT_DISTRICT_TYPES; i++) { + is->district_infos[i].advance_prereq_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].advance_prereq_ids); j++) + is->district_infos[i].advance_prereq_ids[j] = -1; + is->district_infos[i].obsoleted_by_id = -1; + is->district_infos[i].resource_prereq_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].resource_prereq_ids); j++) + is->district_infos[i].resource_prereq_ids[j] = -1; + is->district_infos[i].resource_prereq_on_tile_id = -1; + is->district_infos[i].wonder_prereq_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].wonder_prereq_ids); j++) + is->district_infos[i].wonder_prereq_ids[j] = -1; + is->district_infos[i].natural_wonder_prereq_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].natural_wonder_prereq_ids); j++) + is->district_infos[i].natural_wonder_prereq_ids[j] = -1; + is->district_infos[i].dependent_building_count = 0; + for (int j = 0; j < ARRAY_LEN (is->district_infos[i].dependent_building_ids); j++) + is->district_infos[i].dependent_building_ids[j] = -1; } - for (int base_y = 0; base_y < height; base_y += block_size) { - int block_y1 = base_y + block_size; - if (block_y1 > height) - block_y1 = height; - for (int base_x = 0; base_x < width; base_x += block_size) { - int block_x1 = base_x + block_size; - if (block_x1 > width) - block_x1 = width; - short owner = -1; - int count = find_bridge_candidate_in_block ( - map, base_x, base_y, block_x1, block_y1, - contiguous_limit, candidate_capacity, - candidate_x, candidate_y, &owner); - if (count <= 0) - continue; - if (! add_ai_candidate_entry (BRIDGE_DISTRICT_ID, owner, candidate_x, candidate_y, count)) { - free (candidate_x); - free (candidate_y); - return; - } + for (int civ_id = 0; civ_id < 32; civ_id++) { + FOR_TABLE_ENTRIES (tei, &is->city_pending_district_requests[civ_id]) { + struct pending_district_request * req = (struct pending_district_request *)tei.value; + if (req != NULL) + free (req); } + table_deinit (&is->city_pending_district_requests[civ_id]); } + table_deinit (&is->city_pending_building_orders); - free (candidate_x); - free (candidate_y); + is->great_wall_auto_build = GWABS_NOT_STARTED; } void -generate_ai_canal_candidates_by_block (Map * map, int block_size, int contiguous_limit) +clear_city_district_request (City * city, int district_id) { - if ((map == NULL) || (block_size <= 0)) + if (! is->current_config.enable_districts || + (city == NULL) || + (district_id < 0) || (district_id >= is->district_count)) return; - int width = map->Width; - int height = map->Height; - if ((width <= 0) || (height <= 0)) + struct pending_district_request * req = find_pending_district_request (city, district_id); + if (req == NULL) return; - if (block_size < 1) - block_size = 1; - - int candidate_capacity = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, contiguous_limit); - - short * candidate_x = (short *)malloc (sizeof *candidate_x * candidate_capacity); - short * candidate_y = (short *)malloc (sizeof *candidate_y * candidate_capacity); - if ((candidate_x == NULL) || (candidate_y == NULL)) { - if (candidate_x != NULL) - free (candidate_x); - if (candidate_y != NULL) - free (candidate_y); - return; - } + remove_pending_district_request (req); - for (int base_y = 0; base_y < height; base_y += block_size) { - int block_y1 = base_y + block_size; - if (block_y1 > height) - block_y1 = height; - for (int base_x = 0; base_x < width; base_x += block_size) { - int block_x1 = base_x + block_size; - if (block_x1 > width) - block_x1 = width; - short owner = -1; - int count = find_canal_candidate_in_block ( - map, base_x, base_y, block_x1, block_y1, - contiguous_limit, candidate_capacity, - candidate_x, candidate_y, &owner); - if (count <= 0) - continue; - if (! add_ai_candidate_entry (CANAL_DISTRICT_ID, owner, candidate_x, candidate_y, count)) { - free (candidate_x); - free (candidate_y); - return; - } + int pending_improv_id; + if (lookup_pending_building_order (city, &pending_improv_id)) { + int required_district_id; + if (city_requires_district_for_improvement (city, pending_improv_id, &required_district_id)) { + if (required_district_id == district_id) + forget_pending_building_order (city); } } - - free (candidate_x); - free (candidate_y); } -void -generate_ai_canal_and_bridge_targets () +bool +district_resource_prereqs_met (Tile * tile, int tile_x, int tile_y, int district_id, City * city) { - if (is->ai_candidate_bridge_or_canals_initialized) - return; - if ((! is->current_config.enable_canal_districts) && (! is->current_config.enable_bridge_districts)) - return; + if ((tile == NULL) || (tile == p_null_tile) || + (district_id < 0) || (district_id >= is->district_count)) + return false; - Map * map = &p_bic_data->Map; - int width = map->Width; - int height = map->Height; - if ((width <= 0) || (height <= 0)) - return; + struct district_infos * info = &is->district_infos[district_id]; + int on_tile_req = info->resource_prereq_on_tile_id; + if (on_tile_req >= 0) { + int res_here = tile->vtable->m39_Get_Resource_Type (tile); + if (res_here != on_tile_req) + return false; + } - int block_size = is->current_config.ai_bridge_canal_eval_block_size; - if (block_size <= 0) - block_size = 10; + // If no resource prereqs, then the check passes + if (info->resource_prereq_count <= 0) + return true; - if (is->current_config.enable_canal_districts) { - generate_ai_canal_candidates_by_block ( - map, - block_size, - is->current_config.max_contiguous_canal_districts); - } + int owner = tile->vtable->m38_Get_Territory_OwnerID (tile); + if (owner < 0) + return false; - if (is->current_config.enable_bridge_districts) { - generate_ai_bridge_candidates_by_block ( - map, - block_size, - is->current_config.max_contiguous_bridge_districts); + // Check all resource prereqs - ALL must be present + for (int i = 0; i < info->resource_prereq_count; i++) { + int resource_req = info->resource_prereq_ids[i]; + if (resource_req < 0) + continue; + + bool has_resource = false; + + // Check if the specified city has this resource + if ((city != NULL) && + (city->Body.CivID == owner) && + city_radius_contains_tile (city, tile_x, tile_y) && + patch_City_has_resource (city, __, resource_req)) { + has_resource = true; + } + + // Check cities around this tile for the resource + if (!has_resource) { + FOR_CITIES_AROUND (wai, tile_x, tile_y) { + if (patch_City_has_resource (wai.city, __, resource_req)) { + has_resource = true; + break; + } + } + } + + // If this required resource is not available, the check fails + if (!has_resource) + return false; } - is->ai_candidate_bridge_or_canals_initialized = true; + return true; } + int count_contiguous_bridge_districts (int tile_x, int tile_y, int dx, int dy) { @@ -23535,12 +23544,6 @@ patch_Map_impl_generate (Map * this, int edx, int seed, bool is_multiplayer_game { Map_impl_generate (this, __, seed, is_multiplayer_game, num_seafaring_civs); - if (is->current_config.enable_districts && - (is->current_config.enable_bridge_districts || is->current_config.enable_canal_districts)) { - reset_ai_candidate_bridge_or_canals (); - generate_ai_canal_and_bridge_targets (); - } - if (is->current_config.enable_natural_wonders) place_natural_wonders_on_map (); } @@ -27126,13 +27129,6 @@ patch_Map_place_scenario_things (Map * this) if (! any_natural_wonders) place_natural_wonders_on_map (); } - - if (is->current_config.enable_districts && - (is->current_config.enable_bridge_districts || is->current_config.enable_canal_districts)) { - reset_ai_candidate_bridge_or_canals (); - generate_ai_canal_and_bridge_targets (); - } - is->is_placing_scenario_things = false; } @@ -30576,16 +30572,24 @@ align_district_with_river (Tile * tile, int * out_pixel_x, int * out_pixel_y, en if (! get_primary_river_direction (tile, &dir)) return; - int dx = 0, dy = 0; + int dx, dy; + bool under = false; + direction_to_offset (dir, &dx, &dy); + if (dy < 0) + under = true; + + int offset = 16*2; + + dx = 0, dy = 0; switch (dir) { - case DIR_N: dx = 0; dy = -32; break; - case DIR_NE: dx = 32; dy = -16; break; - case DIR_E: dx = 64; dy = 0; break; - case DIR_SE: dx = 32; dy = 16; break; - case DIR_S: dx = 0; dy = 32; break; - case DIR_SW: dx = -32; dy = 16; break; - case DIR_W: dx = -64; dy = 0; break; - case DIR_NW: dx = -32; dy = -16; break; + case DIR_N: /* dx = 0; */ dy = -offset; break; + case DIR_NE: /* dx = offset; */ dy = -offset/2; break; + case DIR_E: /* dx = offset*2; */ dy = 0; break; + case DIR_SE: /* dx = offset; */ dy = offset/2; break; + case DIR_S: /* dx = 0; */ dy = offset; break; + case DIR_SW: /* dx = -offset; */ dy = offset/2; break; + case DIR_W: /* dx = -offset*2; */ dy = 0; break; + case DIR_NW: /* dx = -offset; */ dy = -offset/2; break; default: break; } @@ -30873,8 +30877,15 @@ wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner enum direction river_dir = DIR_ZERO; if (get_primary_river_direction (center, &river_dir)) { int dx, dy; - if (direction_to_offset (river_dir, &dx, &dy)) + + if (direction_to_offset (river_dir, &dx, &dy)) { + // I'm not completely sure of the logic here, but this seems to match the vanilla behavior + // in terms of having the wonder face the general direction of the river flow + if (dx == 2) + return false; + return dx > 0; + } } } @@ -31310,8 +31321,6 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma if (!wall_nw && !wall_ne && wall_n && water_ne) draw_district_on_map_or_canvas(&sprites[DIR_N], map_renderer, pixel_x, pixel_y); - if (!wall_se && !wall_s && water_nw) - draw_district_on_map_or_canvas(&sprites[DIR_NE], map_renderer, pixel_x, pixel_y); if (!wall_ne && wall_n && water_nw) draw_district_on_map_or_canvas(&sprites[DIR_N], map_renderer, pixel_x, pixel_y); if (water_sw && water_nw && !water_w) @@ -31325,8 +31334,6 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma if (wall_sw) draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); if (wall_se) draw_district_on_map_or_canvas(&sprites[DIR_SE], map_renderer, pixel_x, pixel_y); - if (!wall_sw && !wall_nw && !wall_s && water_w && !water_n) - draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); if (!wall_sw && !wall_se && wall_s && water_sw) draw_district_on_map_or_canvas(&sprites[DIR_S], map_renderer, pixel_x, pixel_y); if (!wall_se && wall_s && water_se) From 9840a6dd06f45d4f79be18ec80ea156b3e86b564 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 27 Jan 2026 16:30:16 -0800 Subject: [PATCH 276/356] Finish Hoover Dam construction art --- Art/Districts/1200/Wonders_3.PCX | Bin 14889 -> 23194 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Wonders_3.PCX b/Art/Districts/1200/Wonders_3.PCX index d953ca9fe1e7617e4766b00118d6ce05275d587d..5bc8a7f15af118709b379789568a4c3d15133d12 100644 GIT binary patch delta 9598 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 Date: Tue, 27 Jan 2026 16:34:58 -0800 Subject: [PATCH 277/356] Update todos --- Notes/district_todos.md | 2 +- injected_code.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index b7725b90..e013aabf 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,7 +1,6 @@ - Validate and upfront bridge/canal algorithm - Firm up logic for river district rendering - - Hoover Dam (use alt dir, special positioning b/c on river) - Districts: - Central Rail Hub (+25% distro hub yields) - Mass Transit System @@ -81,6 +80,7 @@ - ~~Refine logic for air units using aerodrome~~ - ~~Add buildable_by_civs, buildable_by_civ_traits, buildable_by_civ_govs, buildable_by_civ_cultures~~ - ~~Support for buildable_on "irrigation", "mine"~~ + - ~~Hoover Dam (use alt dir, special positioning b/c on river)~~ - ~~Other districts:~~ - ~~Ski Resort~~ - ~~Park~~ diff --git a/injected_code.c b/injected_code.c index 7c4e850e..52c2f6cf 100644 --- a/injected_code.c +++ b/injected_code.c @@ -30578,7 +30578,7 @@ align_district_with_river (Tile * tile, int * out_pixel_x, int * out_pixel_y, en if (dy < 0) under = true; - int offset = 16*2; + int offset = 36; dx = 0, dy = 0; switch (dir) { From f75e3ac727c6146fe6616cee63d0294907bb3781 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 27 Jan 2026 21:46:26 -0800 Subject: [PATCH 278/356] Add buildable_adjacent_to and buildable_adjacent_to_districts, as well as "city" special token --- C3X.h | 14 +++ injected_code.c | 290 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 257 insertions(+), 47 deletions(-) diff --git a/C3X.h b/C3X.h index a64fd076..7b61ffd2 100644 --- a/C3X.h +++ b/C3X.h @@ -664,8 +664,11 @@ struct district_config { char const * wonder_prereqs[MAX_DISTRICT_DEPENDENTS]; char const * natural_wonder_prereqs[MAX_DISTRICT_DEPENDENTS]; char const * buildable_on_districts[MAX_DISTRICT_DEPENDENTS]; + char const * buildable_adjacent_to_districts[MAX_DISTRICT_DEPENDENTS]; char const * img_paths[10]; unsigned int buildable_square_types_mask; + unsigned int buildable_adjacent_to_square_types_mask; + bool buildable_adjacent_to_allows_city; bool allow_multiple; bool vary_img_by_era; bool vary_img_by_culture; @@ -686,6 +689,7 @@ struct district_config { int wonder_prereq_count; int natural_wonder_prereq_count; int buildable_on_district_count; + int buildable_adjacent_to_district_count; int img_path_count; int max_building_index; int btn_tile_sheet_column; @@ -711,6 +715,10 @@ struct district_config { int buildable_on_district_ids[MAX_DISTRICT_DEPENDENTS]; int buildable_on_district_id_count; bool has_buildable_on_districts; + int buildable_adjacent_to_district_ids[MAX_DISTRICT_DEPENDENTS]; + int buildable_adjacent_to_district_id_count; + bool has_buildable_adjacent_to; + bool has_buildable_adjacent_to_districts; char const * buildable_by_civs[32]; int buildable_by_civ_count; bool has_buildable_by_civs; @@ -904,12 +912,14 @@ struct parsed_district_definition { char * wonder_prereqs[5]; char * natural_wonder_prereqs[5]; char * buildable_on_districts[5]; + char * buildable_adjacent_to_districts[5]; char * img_paths[5]; int resource_prereq_count; int dependent_improvement_count; int wonder_prereq_count; int natural_wonder_prereq_count; int buildable_on_district_count; + int buildable_adjacent_to_district_count; int img_path_count; bool allow_multiple; bool vary_img_by_era; @@ -943,6 +953,8 @@ struct parsed_district_definition { struct district_bonus_list happiness_bonus_extras; struct district_bonus_list defense_bonus_extras; unsigned int buildable_square_types_mask; + unsigned int buildable_adjacent_to_square_types_mask; + bool buildable_adjacent_to_allows_city; char * buildable_by_civs[32]; int buildable_by_civ_count; bool buildable_by_war_allies; @@ -979,6 +991,7 @@ struct parsed_district_definition { bool has_shield_bonus; bool has_happiness_bonus; bool has_buildable_on; + bool has_buildable_adjacent_to; bool has_resource_prereq_on_tile; bool has_buildable_by_civs; bool has_buildable_by_war_allies; @@ -1002,6 +1015,7 @@ struct parsed_district_definition { int buildable_by_civ_cultures_id_count; bool has_buildable_by_civ_cultures; bool has_buildable_on_districts; + bool has_buildable_adjacent_to_districts; bool has_allow_irrigation_from; bool has_auto_add_road; bool has_auto_add_railroad; diff --git a/injected_code.c b/injected_code.c index 52c2f6cf..3cfcb949 100644 --- a/injected_code.c +++ b/injected_code.c @@ -1893,42 +1893,6 @@ tile_matches_square_type_mask (Tile * tile, unsigned int mask) return false; } -bool -district_is_buildable_on_square_type (struct district_config const * cfg, Tile * tile) -{ - if ((cfg == NULL) || (tile == NULL) || (tile == p_null_tile)) - return false; - - unsigned int mask = cfg->buildable_square_types_mask; - if (mask == 0) - mask = district_default_buildable_mask (); - - if (! tile_matches_square_type_mask (tile, mask)) - return false; - - if (cfg->has_buildable_on_districts) { - struct district_instance * inst = get_district_instance (tile); - if (inst == NULL) - return false; - - int existing_district_id = inst->district_id; - if (! district_is_complete (tile, existing_district_id)) - return false; - - bool matches = false; - for (int i = 0; i < cfg->buildable_on_district_id_count; i++) { - if (cfg->buildable_on_district_ids[i] == existing_district_id) { - matches = true; - break; - } - } - if (! matches) - return false; - } - - return true; -} - bool read_natural_wonder_terrain_type (struct string_slice const * s, enum SquareTypes * out_type) { @@ -3651,6 +3615,94 @@ tile_square_type_is (Tile * tile, enum SquareTypes type) return tile_matches_square_type (tile, type); } +bool +district_is_buildable_on_square_type (struct district_config const * cfg, Tile * tile) +{ + if ((cfg == NULL) || (tile == NULL) || (tile == p_null_tile)) + return false; + + unsigned int mask = cfg->buildable_square_types_mask; + if (mask == 0) + mask = district_default_buildable_mask (); + + if (! tile_matches_square_type_mask (tile, mask)) + return false; + + if (cfg->has_buildable_on_districts) { + struct district_instance * inst = get_district_instance (tile); + if (inst == NULL) + return false; + + int existing_district_id = inst->district_id; + if (! district_is_complete (tile, existing_district_id)) + return false; + + bool matches = false; + for (int i = 0; i < cfg->buildable_on_district_id_count; i++) { + if (cfg->buildable_on_district_ids[i] == existing_district_id) { + matches = true; + break; + } + } + if (! matches) + return false; + } + + if (cfg->has_buildable_adjacent_to || cfg->has_buildable_adjacent_to_districts) { + int tile_x, tile_y; + if (! tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y)) + return false; + + if (cfg->has_buildable_adjacent_to) { + bool matches = false; + bool city_adjacent = false; + FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { + if (tai.n == 0) + continue; + if (Tile_has_city (tai.tile)) { + city_adjacent = true; + if (cfg->buildable_adjacent_to_allows_city) + matches = true; + } + if (tile_matches_square_type_mask (tai.tile, cfg->buildable_adjacent_to_square_types_mask)) + matches = true; + if (matches && (cfg->buildable_adjacent_to_allows_city || ! city_adjacent)) + break; + } + if (city_adjacent && ! cfg->buildable_adjacent_to_allows_city) + return false; + if (! matches) + return false; + } + + if (cfg->has_buildable_adjacent_to_districts) { + bool matches = false; + FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { + if (tai.n == 0) + continue; + struct district_instance * adj_inst = get_district_instance (tai.tile); + if (adj_inst == NULL) + continue; + int adj_district_id = adj_inst->district_id; + if (! district_is_complete (tai.tile, adj_district_id)) + continue; + for (int i = 0; i < cfg->buildable_adjacent_to_district_id_count; i++) { + if (cfg->buildable_adjacent_to_district_ids[i] == adj_district_id) { + matches = true; + break; + } + } + if (matches) + break; + } + if (! matches) + return false; + } + } + + return true; +} + bool natural_wonder_is_coastal_island (Tile * tile, int tile_x, int tile_y) { @@ -6856,6 +6908,16 @@ free_dynamic_district_config (struct district_config * cfg) cfg->buildable_on_district_count = 0; cfg->buildable_on_district_id_count = 0; cfg->has_buildable_on_districts = false; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_adjacent_to_districts); i++) { + if (cfg->buildable_adjacent_to_districts[i] != NULL) { + free ((void *)cfg->buildable_adjacent_to_districts[i]); + cfg->buildable_adjacent_to_districts[i] = NULL; + } + } + cfg->buildable_adjacent_to_district_count = 0; + cfg->buildable_adjacent_to_district_id_count = 0; + cfg->has_buildable_adjacent_to = false; + cfg->has_buildable_adjacent_to_districts = false; for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { if (cfg->buildable_by_civs[i] != NULL) { @@ -7033,6 +7095,20 @@ free_special_district_override_strings (struct district_config * cfg, struct dis cfg->buildable_on_district_count = defaults->buildable_on_district_count; cfg->buildable_on_district_id_count = defaults->buildable_on_district_id_count; cfg->has_buildable_on_districts = defaults->has_buildable_on_districts; + for (int i = 0; i < ARRAY_LEN (cfg->buildable_adjacent_to_districts); i++) { + char const * default_value = (i < defaults->buildable_adjacent_to_district_count) + ? defaults->buildable_adjacent_to_districts[i] + : NULL; + if ((cfg->buildable_adjacent_to_districts[i] != NULL) && + (cfg->buildable_adjacent_to_districts[i] != default_value)) { + free ((void *)cfg->buildable_adjacent_to_districts[i]); + } + cfg->buildable_adjacent_to_districts[i] = NULL; + } + cfg->buildable_adjacent_to_district_count = defaults->buildable_adjacent_to_district_count; + cfg->buildable_adjacent_to_district_id_count = defaults->buildable_adjacent_to_district_id_count; + cfg->has_buildable_adjacent_to = defaults->has_buildable_adjacent_to; + cfg->has_buildable_adjacent_to_districts = defaults->has_buildable_adjacent_to_districts; for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { char const * default_value = (i < defaults->buildable_by_civ_count) ? defaults->buildable_by_civs[i] : NULL; @@ -7158,6 +7234,8 @@ init_parsed_district_definition (struct parsed_district_definition * def) def->render_strategy = DRS_BY_COUNT; def->ai_build_strategy = DABS_DISTRICT; def->buildable_square_types_mask = district_default_buildable_mask (); + def->buildable_adjacent_to_square_types_mask = 0; + def->buildable_adjacent_to_allows_city = false; } void @@ -7191,6 +7269,12 @@ free_parsed_district_definition (struct parsed_district_definition * def) def->buildable_on_districts[i] = NULL; } } + for (int i = 0; i < ARRAY_LEN (def->buildable_adjacent_to_districts); i++) { + if (def->buildable_adjacent_to_districts[i] != NULL) { + free (def->buildable_adjacent_to_districts[i]); + def->buildable_adjacent_to_districts[i] = NULL; + } + } if (def->obsoleted_by != NULL) { free (def->obsoleted_by); def->obsoleted_by = NULL; @@ -7224,6 +7308,7 @@ free_parsed_district_definition (struct parsed_district_definition * def) } } def->natural_wonder_prereq_count = 0; + def->buildable_adjacent_to_district_count = 0; for (int i = 0; i < def->buildable_by_civ_count; i++) { if (def->buildable_by_civs[i] != NULL) { @@ -7429,11 +7514,17 @@ bool parse_buildable_square_type_mask (struct string_slice const * value, unsigned int * out_mask, struct error_line ** parse_errors, - int line_number) + int line_number, + char const * key_name, + bool * out_allow_city) { char * value_text = trim_and_extract_slice (value, 0); unsigned int mask = 0; int entry_count = 0; + bool allow_city = false; + bool allow_city_token = (key_name != NULL) && (strcmp (key_name, "buildable_adjacent_to") == 0); + if (key_name == NULL) + key_name = "buildable_on"; if (value_text != NULL) { char * cursor = value_text; @@ -7451,7 +7542,17 @@ parse_buildable_square_type_mask (struct string_slice const * value, struct string_slice item_slice = { .str = item_start, .len = (int)(item_end - item_start) }; if (item_slice.len > 0) { - if (slice_matches_str (&item_slice, "mine")) { + if (slice_matches_str (&item_slice, "city")) { + if (! allow_city_token) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %.*s (invalid %s entry)", line_number, item_slice.len, item_slice.str, key_name); + err->text[(sizeof err->text) - 1] = '\0'; + free (value_text); + return false; + } + allow_city = true; + entry_count += 1; + } else if (slice_matches_str (&item_slice, "mine")) { mask |= district_buildable_mine_mask_bit (); entry_count += 1; } else if (slice_matches_str (&item_slice, "irrigation")) { @@ -7467,7 +7568,7 @@ parse_buildable_square_type_mask (struct string_slice const * value, entry_count += 1; } else { struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: %.*s (invalid buildable_on entry)", line_number, item_slice.len, item_slice.str); + snprintf (err->text, sizeof err->text, "^ Line %d: %.*s (invalid %s entry)", line_number, item_slice.len, item_slice.str, key_name); err->text[(sizeof err->text) - 1] = '\0'; free (value_text); return false; @@ -7486,14 +7587,16 @@ parse_buildable_square_type_mask (struct string_slice const * value, if (value_text != NULL) free (value_text); - if ((entry_count == 0) || (mask == 0)) { + if ((entry_count == 0) || ((mask == 0) && ! allow_city)) { struct error_line * err = add_error_line (parse_errors); - snprintf (err->text, sizeof err->text, "^ Line %d: buildable_on (expected at least one square type)", line_number); + snprintf (err->text, sizeof err->text, "^ Line %d: %s (expected at least one square type)", line_number, key_name); err->text[(sizeof err->text) - 1] = '\0'; return false; } *out_mask = mask; + if (out_allow_city != NULL) + *out_allow_city = allow_city; return true; } @@ -7787,6 +7890,29 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->has_buildable_on_districts = true; } + if (def->has_buildable_adjacent_to_districts) { + for (int i = 0; i < ARRAY_LEN (cfg->buildable_adjacent_to_districts); i++) { + char const * default_value = (i < defaults->buildable_adjacent_to_district_count) + ? defaults->buildable_adjacent_to_districts[i] + : NULL; + if ((cfg->buildable_adjacent_to_districts[i] != NULL) && + (cfg->buildable_adjacent_to_districts[i] != default_value)) + free ((void *)cfg->buildable_adjacent_to_districts[i]); + cfg->buildable_adjacent_to_districts[i] = NULL; + } + + cfg->buildable_adjacent_to_district_count = def->buildable_adjacent_to_district_count; + const int max_entries = ARRAY_LEN (cfg->buildable_adjacent_to_districts); + if (cfg->buildable_adjacent_to_district_count > max_entries) + cfg->buildable_adjacent_to_district_count = max_entries; + for (int i = 0; i < cfg->buildable_adjacent_to_district_count; i++) { + cfg->buildable_adjacent_to_districts[i] = def->buildable_adjacent_to_districts[i]; + def->buildable_adjacent_to_districts[i] = NULL; + } + cfg->buildable_adjacent_to_district_id_count = 0; + cfg->has_buildable_adjacent_to_districts = true; + } + if (def->has_buildable_by_civs) { for (int i = 0; i < ARRAY_LEN (cfg->buildable_by_civs); i++) { char const * default_value = (i < defaults->buildable_by_civ_count) ? defaults->buildable_by_civs[i] : NULL; @@ -7912,6 +8038,11 @@ override_special_district_from_definition (struct parsed_district_definition * d } if (def->has_buildable_on) cfg->buildable_square_types_mask = def->buildable_square_types_mask; + if (def->has_buildable_adjacent_to) { + cfg->buildable_adjacent_to_square_types_mask = def->buildable_adjacent_to_square_types_mask; + cfg->has_buildable_adjacent_to = true; + cfg->buildable_adjacent_to_allows_city = def->buildable_adjacent_to_allows_city; + } if (def->has_generated_resource) { if ((cfg->generated_resource != NULL) && (cfg->generated_resource != defaults->generated_resource)) @@ -8084,6 +8215,17 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.buildable_on_district_id_count = 0; new_cfg.has_buildable_on_districts = def->has_buildable_on_districts; + new_cfg.buildable_adjacent_to_district_count = def->has_buildable_adjacent_to_districts ? def->buildable_adjacent_to_district_count : 0; + const int max_adjacent_to_districts = ARRAY_LEN (new_cfg.buildable_adjacent_to_districts); + if (new_cfg.buildable_adjacent_to_district_count > max_adjacent_to_districts) + new_cfg.buildable_adjacent_to_district_count = max_adjacent_to_districts; + for (int i = 0; i < new_cfg.buildable_adjacent_to_district_count; i++) { + new_cfg.buildable_adjacent_to_districts[i] = def->buildable_adjacent_to_districts[i]; + def->buildable_adjacent_to_districts[i] = NULL; + } + new_cfg.buildable_adjacent_to_district_id_count = 0; + new_cfg.has_buildable_adjacent_to_districts = def->has_buildable_adjacent_to_districts; + new_cfg.buildable_by_civ_count = def->has_buildable_by_civs ? def->buildable_by_civ_count : 0; const int max_civ_names = ARRAY_LEN (new_cfg.buildable_by_civs); if (new_cfg.buildable_by_civ_count > max_civ_names) @@ -8147,6 +8289,9 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.shield_bonus = def->has_shield_bonus ? def->shield_bonus : 0; new_cfg.happiness_bonus = def->has_happiness_bonus ? def->happiness_bonus : 0; new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); + new_cfg.buildable_adjacent_to_square_types_mask = def->has_buildable_adjacent_to ? def->buildable_adjacent_to_square_types_mask : 0; + new_cfg.has_buildable_adjacent_to = def->has_buildable_adjacent_to; + new_cfg.buildable_adjacent_to_allows_city = def->has_buildable_adjacent_to ? def->buildable_adjacent_to_allows_city : false; if (def->has_culture_bonus) move_bonus_entry_list (&new_cfg.culture_bonus_extras, &def->culture_bonus_extras); @@ -8386,6 +8531,24 @@ handle_district_definition_key (struct parsed_district_definition * def, } free (value_text); + } else if (slice_matches_str (key, "buildable_adjacent_to_districts")) { + char * value_text = trim_and_extract_slice (value, 0); + int list_count = 0; + if (parse_config_string_list (value_text, + def->buildable_adjacent_to_districts, + ARRAY_LEN (def->buildable_adjacent_to_districts), + &list_count, + parse_errors, + line_number, + "buildable_adjacent_to_districts")) { + def->buildable_adjacent_to_district_count = list_count; + def->has_buildable_adjacent_to_districts = true; + } else { + def->buildable_adjacent_to_district_count = 0; + def->has_buildable_adjacent_to_districts = false; + } + free (value_text); + } else if (slice_matches_str (key, "buildable_by_civs")) { char * value_text = trim_and_extract_slice (value, 0); int list_count = 0; @@ -8610,11 +8773,19 @@ handle_district_definition_key (struct parsed_district_definition * def, } else if (slice_matches_str (key, "buildable_on")) { unsigned int mask; - if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number)) { + if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number, "buildable_on", NULL)) { def->buildable_square_types_mask = mask; def->has_buildable_on = true; } + } else if (slice_matches_str (key, "buildable_adjacent_to")) { + unsigned int mask; + def->buildable_adjacent_to_allows_city = false; + if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number, "buildable_adjacent_to", &def->buildable_adjacent_to_allows_city)) { + def->buildable_adjacent_to_square_types_mask = mask; + def->has_buildable_adjacent_to = true; + } + } else if (slice_matches_str (key, "allow_multiple")) { struct string_slice val_slice = *value; int ival; @@ -9352,7 +9523,7 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, } else if (slice_matches_str (key, "buildable_on")) { unsigned int mask; - if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number)) { + if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number, "buildable_on", NULL)) { def->buildable_square_types_mask = mask; def->has_buildable_on = true; } else { @@ -10328,6 +10499,31 @@ void parse_building_and_tech_ids () is->district_configs[i].buildable_on_district_id_count = stored_buildable_on_count; } + for (int j = 0; j < ARRAY_LEN (is->district_configs[i].buildable_adjacent_to_district_ids); j++) + is->district_configs[i].buildable_adjacent_to_district_ids[j] = -1; + is->district_configs[i].buildable_adjacent_to_district_id_count = 0; + + if (is->district_configs[i].has_buildable_adjacent_to_districts) { + int stored_adjacent_count = 0; + for (int j = 0; j < is->district_configs[i].buildable_adjacent_to_district_count; j++) { + char const * name = is->district_configs[i].buildable_adjacent_to_districts[j]; + if (name == NULL || name[0] == '\0') + continue; + int other_district_id = find_district_index_by_name (name); + if (other_district_id >= 0) { + if (stored_adjacent_count < ARRAY_LEN (is->district_configs[i].buildable_adjacent_to_district_ids)) { + is->district_configs[i].buildable_adjacent_to_district_ids[stored_adjacent_count] = other_district_id; + stored_adjacent_count += 1; + } + } else { + struct error_line * err = add_error_line (&district_parse_errors); + snprintf (err->text, sizeof err->text, "^ District \"%s\": buildable_adjacent_to_districts entry \"%s\" not found", district_name, name); + err->text[(sizeof err->text) - 1] = '\0'; + } + } + is->district_configs[i].buildable_adjacent_to_district_id_count = stored_adjacent_count; + } + // Resolve generated resource name to ID if (is->district_configs[i].generated_resource != NULL && is->district_configs[i].generated_resource != "") { int res_id; @@ -16249,7 +16445,6 @@ patch_init_floating_point () {"expand_water_tile_checks_to_city_work_area" , false, offsetof (struct c3x_config, expand_water_tile_checks_to_city_work_area)}, {"ai_can_replace_existing_districts_with_canals" , false, offsetof (struct c3x_config, ai_can_replace_existing_districts_with_canals)}, {"workers_can_enter_coast" , false, offsetof (struct c3x_config, workers_can_enter_coast)}, - {"workers_can_enter_coast" , false, offsetof (struct c3x_config, workers_can_enter_coast)}, {"enable_city_work_radii_highlights" , false, offsetof (struct c3x_config, enable_city_work_radii_highlights)}, {"introduce_all_human_players_at_start_of_hotseat_game" , false, offsetof (struct c3x_config, introduce_all_human_players_at_start_of_hotseat_game)}, {"allow_unload_from_army" , false, offsetof (struct c3x_config, allow_unload_from_army)}, @@ -31609,7 +31804,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - *p_debug_mode_bits |= 0xC; + //*p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -33583,7 +33778,7 @@ patch_get_tile_occupier_id_in_Unit_ai_move_naval_power_unit (int x, int y, int p return (*tile->vtable->m38_Get_Territory_OwnerID) (tile); } -// Returns a non-zero score for enemy port district tiles so they pass the threshold check. +// Returns a non-zero score for enemy port district tiles so they pass the threshold check and are bombard targets int __fastcall patch_Fighter_eval_tile_vulnerability_in_Unit_ai_move_naval_power_unit (Fighter * this, int edx, Unit * unit, int tile_x, int tile_y) { @@ -33614,7 +33809,8 @@ patch_Fighter_eval_tile_vulnerability_in_Unit_ai_move_naval_power_unit (Fighter return 0x300; } -// Returns the territory owner for enemy port districts so the score isn't reduced. +// Returns the territory owner for enemy port districts so the score isn't reduced and naval units move to enemy ports +// (to subsequently pillage them) int __cdecl patch_get_combat_occupier_in_Unit_ai_move_naval_power_unit (int tile_x, int tile_y, int civ_id, byte ignore_visibility) { From 3c2c855476f1748f3dda4909dd0ce471e5939d00 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 27 Jan 2026 22:46:04 -0800 Subject: [PATCH 279/356] Add commented details of district config fields --- Notes/district_todos.md | 2 +- default.districts_config.txt | 62 +++++++++++++++++++++++++++++++++++- injected_code.c | 13 +++++--- 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index e013aabf..08138ed7 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -12,7 +12,7 @@ - Canal (2 opposite sides have coast "isthmus") - Bridge (2 opposite sides have land "strait" or bridges) - Light annotations - - Add commented instructions on fields in config files + - Add commented instructions on fields in Wonder, Natural Wonder config files - Double check PCX third column alignment, clean up ground in industrial zone, Newton's University, Grand Cathedral? diff --git a/default.districts_config.txt b/default.districts_config.txt index e765aca0..693c8f1a 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -5,6 +5,62 @@ [scenario or user configs, note that all districts defined here will be removed and only your scenario or user-defined districts will be used ] [====================================================================================================================================================] +[ + ; District config fields (each District block begins with "#District") + ; - name : Text (required). Internal district name; must be unique. + ; - display_name : Text. Name shown to user; defaults to name. + ; - tooltip : Text. Shown when hovering over build action button. + ; - img_paths : Comma-separated PCX filenames under Art/Districts/1200/. + 1 for single image, 5 for culture variants (AMER, EURO, ROMAN, MIDEAST, ASIAN). Order matters. + ; - render_strategy : "by-count" | "by-building". Whether the PCX files show all buildings together, or one building per column. (default: "by-count") + ; - vary_img_by_era : 0 or 1. If 1, each PCX image must have 4 rows, 1 for each era. + ; - vary_img_by_culture : 0 or 1. If 1, img_paths should list 5 files (AMER, EURO, ROMAN, MIDEAST, ASIAN). + ; - btn_tile_sheet_row : Number. Row index in the district button tile sheet (0-based). + ; - btn_tile_sheet_column : Number. Column index in the district button tile sheet (0-based). + ; - dependent_improvs : Comma-separated city improvement names. Cities can't build these improvements until they have this district. + ; - generated_resource : Resource name plus optional flags: local, yields, no-tech-req. District generates resource if tile connected by road. + ; - advance_prereqs : Comma-separated tech names. District cannot be built unless all techs are discovered. + ; - obsoleted_by : Tech name that obsoletes this district. District cannot built after discovery. + ; - resource_prereqs : Comma-separated resource names. District cannot be built unless a nearby city has all required resources. + ; - resource_prereq_on_tile : Resource name required on the district tile. District cannot be built unless resource is on same tile. + ; - wonder_prereqs : Comma-separated wonder names. District cannot be built unless all Wonders are built by same civ. + ; - natural_wonder_prereqs : Comma-separated natural wonder names. District cannot be built unless all Natural Wonders are within civ's territory. + ; - buildable_on : Comma-separated square types: desert, plains, grassland, tundra, floodplain, hills, mountains, forest, jungle, swamp, volcano, + coast, sea, ocean, river, snow-forest, snow-mountains, snow-volcano, mine, irrigation. + ; - buildable_adjacent_to : Same as buildable_on, plus "city". + ; - buildable_on_districts : Comma-separated district names (tile must already have a completed district of any of these types, which it would replace). + ; - buildable_adjacent_to_districts : Comma-separated district names (adjacent tile must have a completed district of any of these types). + ; - buildable_by_civs : Comma-separated civ names (e.g., Romans, Egyptians). + ; - buildable_by_civ_traits : Comma-separated trait names (e.g., Commercial, Militaristic). + ; - buildable_by_civ_govs : Comma-separated government names (e.g., Republic, Monarchy). + ; - buildable_by_civ_cultures : Comma-separated culture names (e.g., EURO, ASIAN). + ; - buildable_by_war_allies : 0 or 1. Can build if war allied with a civ that can build it. + ; - buildable_by_pact_allies : 0 or 1. Can build if mutual defense pact allied with a civ that can build it. + ; - ai_build_strategy : "district" | "tile-improvement". If "tile_improvement", the AI may build many. (default: "district") + ; - defense_bonus_percent : Number, with optional "Name: bonus" entries (Name = building or tile type, each added to base bonus if true. Negative values allowed). + ; - culture_bonus : Number, with optional "Name: bonus" entries (same pattern as defense_bonus_percent). + ; - science_bonus : Number, with optional "Name: bonus" entries. + ; - food_bonus : Number, with optional "Name: bonus" entries. + ; - gold_bonus : Number, with optional "Name: bonus" entries. + ; - shield_bonus : Number, with optional "Name: bonus" entries. + ; - happiness_bonus : Number, with optional "Name: bonus" entries. + ; - custom_width : Number (pixels). Override sprite width. (default: 128) + ; - custom_height : Number (pixels). Override sprite height. (default: 64) + ; - x_offset : Number (pixels). Push the sprite farther to the right (or left, if negative). (default: 0) + ; - y_offset : Number (pixels). Push the sprite farther down (or up, if negative). (default: 0) + ; - align_to_coast : 0 or 1. Aligns art to coastline, slightly adjusting x & y pixels. (default: 0) + ; - draw_over_resources : 0 or 1. If a resource is also on the tile, draw the district on top. (default: 0) + ; - auto_add_road : 0 or 1. Auto-add road on completion. (default: 0) + ; - auto_add_railroad : 0 or 1. Auto-add railroad on completion. (default: 0) + ; - allow_irrigation_from : 0 or 1. District can act as an irrigation source. (default: 0) + ; - allow_multiple : 0 or 1. If 1, multiple copies can exist per city. (default: 0) + ; - heal_units_in_one_turn : 0 or 1. Friendly units fully heal when ending turn here. (default: 0) +] + +[=========================================================================] +[=========================STANDARD DISTRICTS==============================] +[=========================================================================] + #District name = Encampment tooltip = Build Encampment @@ -207,6 +263,10 @@ gold_bonus = 4 shield_bonus = 0 happiness_bonus = 2 +[========================================================================] +[=========================SPECIAL DISTRICTS==============================] +[========================================================================] + #District name = Neighborhood tooltip = Build Neighborhood @@ -370,4 +430,4 @@ science_bonus = 0 food_bonus = 0 gold_bonus = 0 shield_bonus = 0 -happiness_bonus = 0 \ No newline at end of file +happiness_bonus = 0 diff --git a/injected_code.c b/injected_code.c index 3cfcb949..08f60f25 100644 --- a/injected_code.c +++ b/injected_code.c @@ -9057,7 +9057,7 @@ handle_district_definition_key (struct parsed_district_definition * def, bool line_is_empty_or_comment (struct string_slice const * trimmed) { - return ((trimmed == NULL) || (trimmed->len == 0) || (trimmed->str[0] == ';')); + return ((trimmed == NULL) || (trimmed->len == 0) || (trimmed->str[0] == ';') || (trimmed->str[0] == '[')); } void @@ -10189,13 +10189,18 @@ find_civ_culture_id_by_name (struct string_slice const * name, int * out_id) struct culture_entry { char const * name; int id; } cultures[] = { {"American", 0}, + {"AMERICAN", 0}, + {"AMER", 0}, {"European", 1}, + {"EUROPEAN", 1}, + {"EURO", 1}, {"Roman", 2}, + {"ROMAN", 2}, {"Mid East", 3}, {"Mideast", 3}, - {"Middle Eastern", 3}, - {"MiddleEastern", 3}, - {"Asian", 4} + {"MIDEAST", 3}, + {"Asian", 4}, + {"ASIAN", 4} }; for (int i = 0; i < ARRAY_LEN (cultures); i++) { From 1d6c6135ee0736132e11c70522df42abd04abae0 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 27 Jan 2026 23:01:40 -0800 Subject: [PATCH 280/356] Add Water Park --- Art/Districts/1200/WaterPark.pcx | Bin 0 -> 8535 bytes default.districts_config.txt | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 Art/Districts/1200/WaterPark.pcx diff --git a/Art/Districts/1200/WaterPark.pcx b/Art/Districts/1200/WaterPark.pcx new file mode 100644 index 0000000000000000000000000000000000000000..cb54442877b998f72bb1245ef5950f72cf2470b1 GIT binary patch literal 8535 zcmb_hd012D(np0}5n2_n0@naiK#1T)DG0Nx^DrlcOf7uK%mQNote-`yc%qf&Sp{=MUb~^#9){FKOr^eQ-tff<9=U z2-QWuprLIaJ3N*o?T(d0=Z6bA=zr0L@*r!aF|j80Y%?9`oT#B47bt}G#>{pdrnQds zY+3ch@0Ij(Isn=gII|7vH8I(*a13!?vuo;t`IN%U8rpdD0m|-W&lgzBh6401eNSH=!GD!ZY~2lMb-P zy%7#isu#xONcdbuIiDvbd;ymyxI$M={OwH|?w%-!h$)g+XBVJ?-<5PGv3_qtsc

*o{}xr6&$syR*^nOm-My^VGPB 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 Date: Wed, 28 Jan 2026 07:24:44 -0800 Subject: [PATCH 281/356] Add flags for AI building bridges/canals; refine great wall render logic; Fix bug to make sure AI workers dont get stuck on coastal water --- C3X.h | 2 ++ Notes/district_todos.md | 18 ------------------ default.c3x_config.ini | 2 ++ default.districts_config.txt | 2 +- injected_code.c | 30 +++++++++++++++++------------- 5 files changed, 22 insertions(+), 32 deletions(-) diff --git a/C3X.h b/C3X.h index 7b61ffd2..926752e5 100644 --- a/C3X.h +++ b/C3X.h @@ -394,6 +394,8 @@ struct c3x_config { int ai_bridge_canal_eval_block_size; int ai_bridge_eval_lake_tile_threshold; bool ai_can_replace_existing_districts_with_canals; + bool ai_builds_bridges; + bool ai_builds_canals; bool ai_defends_districts; int ai_city_district_max_build_wait_turns; diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 08138ed7..15a7e929 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -92,23 +92,5 @@ - - - - - - - ## Seasons - Splice nice terrain onto 1-2 pcx tiles, use for prompt for remainder - -## Great Wall - - Also Civilopedia? At least district buttons in tech tree - - Water Park (2 happiness, 2 gold) - - Special district, special rendering (return early, don't do usually render flow, loop directionally and render multiple times, once per direction if connected) - - Building automatically covers borders, allows workers to build until metallurgy - - Maybe do one-time tile-by-tile modal confirmation if would destroy district/improvement? - - Add wonder_prereq, natural_wonder_prereq, made_obsolete_by to districts config - - Enemy units can't pass over, but can destroy (recommend using PTW targeting?) - - Need to figure out how AI will manage - - +1 culture & +1 gold per wall \ No newline at end of file diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 3aed2c87..93958fae 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -915,6 +915,8 @@ ai_canal_eval_min_bisected_land_tiles = 20 ai_bridge_canal_eval_block_size = 10 ai_bridge_eval_lake_tile_threshold = 5 ai_can_replace_existing_districts_with_canals = true +ai_builds_bridges = true +ai_builds_canals = true ; When enabled, AI defensive units will actively seek out and defend districts within their territory, treating them as valuable assets like colonies. ; The AI prioritizes defending Wonder districts (if destructible wonders are enabled) over regular districts, searching within a 20-tile radius for diff --git a/default.districts_config.txt b/default.districts_config.txt index d291d03c..22a2bd83 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -75,7 +75,7 @@ buildable_on = desert,plains,grassland,tundra,floodplain,hills heal_units_in_one_turn = 1 defense_bonus_percent = 50, Barracks: 25, hills: 25 allow_multiple = 1 -culture_bonus = 2 +culture_bonus = 0 science_bonus = 0 food_bonus = 0 gold_bonus = 0 diff --git a/injected_code.c b/injected_code.c index 08f60f25..c125fe61 100644 --- a/injected_code.c +++ b/injected_code.c @@ -5605,18 +5605,18 @@ generate_ai_canal_and_bridge_targets () if (block_size <= 0) block_size = 10; - if (is->current_config.enable_canal_districts) { - generate_ai_canal_candidates_by_block ( + if (is->current_config.enable_bridge_districts && is->current_config.ai_builds_bridges) { + generate_ai_bridge_candidates_by_block ( map, block_size, - is->current_config.max_contiguous_canal_districts); + is->current_config.max_contiguous_bridge_districts); } - if (is->current_config.enable_bridge_districts) { - generate_ai_bridge_candidates_by_block ( + if (is->current_config.enable_canal_districts && is->current_config.ai_builds_canals) { + generate_ai_canal_candidates_by_block ( map, block_size, - is->current_config.max_contiguous_bridge_districts); + is->current_config.max_contiguous_canal_districts); } is->ai_candidate_bridge_or_canals_initialized = true; @@ -16449,6 +16449,8 @@ patch_init_floating_point () {"disable_great_wall_city_defense_bonus" , false, offsetof (struct c3x_config, disable_great_wall_city_defense_bonus)}, {"expand_water_tile_checks_to_city_work_area" , false, offsetof (struct c3x_config, expand_water_tile_checks_to_city_work_area)}, {"ai_can_replace_existing_districts_with_canals" , false, offsetof (struct c3x_config, ai_can_replace_existing_districts_with_canals)}, + {"ai_builds_bridges" , false, offsetof (struct c3x_config, ai_builds_bridges)}, + {"ai_builds_canals" , false, offsetof (struct c3x_config, ai_builds_canals)}, {"workers_can_enter_coast" , false, offsetof (struct c3x_config, workers_can_enter_coast)}, {"enable_city_work_radii_highlights" , false, offsetof (struct c3x_config, enable_city_work_radii_highlights)}, {"introduce_all_human_players_at_start_of_hotseat_game" , false, offsetof (struct c3x_config, introduce_all_human_players_at_start_of_hotseat_game)}, @@ -25305,7 +25307,8 @@ patch_Leader_do_production_phase (Leader * this) if (is->current_config.enable_districts) { assign_workers_for_pending_districts (this); - if (is->current_config.enable_canal_districts || is->current_config.enable_bridge_districts) + if ((is->current_config.enable_canal_districts || is->current_config.enable_bridge_districts) && + (is->current_config.ai_builds_bridges || is->current_config.ai_builds_canals)) assign_workers_for_ai_candidate_bridge_or_canals (this); bool ai_player = ((*p_human_player_bits & (1 << this->ID)) == 0); @@ -31533,11 +31536,6 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma if (wall_sw) draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); if (wall_se) draw_district_on_map_or_canvas(&sprites[DIR_SE], map_renderer, pixel_x, pixel_y); - - if (!wall_sw && !wall_se && wall_s && water_sw) - draw_district_on_map_or_canvas(&sprites[DIR_S], map_renderer, pixel_x, pixel_y); - if (!wall_se && wall_s && water_se) - draw_district_on_map_or_canvas(&sprites[DIR_S], map_renderer, pixel_x, pixel_y); } void @@ -31809,7 +31807,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - //*p_debug_mode_bits |= 0xC; + *p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -33087,6 +33085,12 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t if (is->current_config.enable_districts && is->current_config.workers_can_enter_coast && base != PBV_OK && is_worker(this)) { + Tile * source = tile_at (from_x, from_y); + if (source != NULL && + source->vtable->m35_Check_Is_Water (source) && + (source->vtable->m50_Get_Square_BaseType (source) == SQ_Coast)) + return true; + Tile * dest = tile_at (to_x, to_y); if ((dest != NULL) && dest->vtable->m35_Check_Is_Water (dest) && From 754bce13643c3cb458c73c66d604cb3059e4d2d7 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 08:20:49 -0800 Subject: [PATCH 282/356] Touch up Energy Grid art; Abandoned maritime art --- Art/Districts/1200/Abandoned.PCX | Bin 24937 -> 23609 bytes Art/Districts/1200/EnergyGrid.PCX | Bin 100221 -> 104481 bytes Notes/district_todos.md | 5 ++--- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Art/Districts/1200/Abandoned.PCX b/Art/Districts/1200/Abandoned.PCX index 3acbdc34901de5444154be91bdf56099f2a2ac28..8575052ec4761819215bb8b5dc04a0589dd40106 100644 GIT binary patch delta 3981 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 diff --git a/Art/Districts/1200/EnergyGrid.PCX b/Art/Districts/1200/EnergyGrid.PCX index d5fbfcd0e3c6b84c4829d9f1debf0be344f2db5e..363e44d19c09c9f1e710025621e7d8066be4138c 100644 GIT binary patch literal 104481 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 rNA?^$az!H16fCJ literal 100221 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 Date: Wed, 28 Jan 2026 10:04:41 -0800 Subject: [PATCH 283/356] Update holy site art to use river water color --- Art/Districts/1200/HolySite_AMER.PCX | Bin 12850 -> 15796 bytes Art/Districts/1200/HolySite_ASIAN.PCX | Bin 16139 -> 16433 bytes Art/Districts/1200/HolySite_EURO.PCX | Bin 13504 -> 16545 bytes Art/Districts/1200/HolySite_MIDEAST.PCX | Bin 13069 -> 16001 bytes Art/Districts/1200/HolySite_ROMAN.PCX | Bin 12774 -> 15685 bytes Notes/district_todos.md | 11 ++++++----- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Art/Districts/1200/HolySite_AMER.PCX b/Art/Districts/1200/HolySite_AMER.PCX index 27f6fd7b8d6712a89458f9fea7b7db9387e5ef2e..763a098917505ac9f94adec0a8f53fc3137f4893 100644 GIT binary patch literal 15796 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 literal 12850 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 literal 16139 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 F{~sThKU)9* diff --git a/Art/Districts/1200/HolySite_EURO.PCX b/Art/Districts/1200/HolySite_EURO.PCX index b9f3ac96e483b260e7050db0f942c1d22e600e39..1d6f15998f9702a2eba33945a77d9e0e972bdfdd 100644 GIT binary patch literal 16545 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 literal 13069 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

t7yQKo9MMjq?6I4iN+IQD>x0J!G)c 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 YZM*zU=H-j;e0$-eOBXwwc=q@I0nxUR0{{R3 literal 46313 zcmdtL3tZIKwJ+Wl8Avm^exT6D9Mk+y{yja)(B57jy@@jLXf(!mz|QY}_n^ikK0q;= zs{9;&vwv&tz1Ci9uf5jV>-S^FA3Fa4f5ab(KgjxF)(`pT5&ik$4}LWAhrYrf+L$QX zzJ2|NZ5Qqje&hNN+-b)BE8n;d+P<_M!2K_-Ut96{f~{5j5-r>C`Rw)UA$AejZmB-5qX zaHZPM{@{^2-tWV+M{RxL8I0wy_@6|;$O4zq<=Lh3c`0;ho?J~k(>U_>dwzcktvYR| z#M8D<(YAmnZdPDSV9cx_5QnLiljw5FJcX8aa%hK!(c&!Hwb{;!pMrviMRqZ6HXy{j zM3)PK$_m_=RIPOj3ZzSZ3UrZ^e*Z9Aowv6=FZSDdZS5jmOo^QfYK6-&^tnN4bWYGp zE$D1aV0=Ni#jSvY-aGt;m+j3ji@o4vyXg1KIJ(3x&jJaQ3RRq)1a|02%K}+UJTP*4 zbBZ*Lu=RlbnE|l}6RAV=b7urvYg+ehLvZdyRM0KS^8Fn3kK zbP#7((o2AjA!z5Zq*n3#u-l2^ezfM-Bap6kadPV96mXd?kr*_O&NCpgD6~?oTB~rE z$Jx2DEHHi=C>1E5n6&JGc=rAt62&$=%CEqRX|u%L5?-*JE_)gCshqox3b)S*koR>5zEpzMm7tPCU)8?a%?8Vp`lhc|lOQDh=a`si)rO z$8B&^s2OnMFI`cD?@F5G_B)K3&M8us9TxlU>lslziYNKC&2|v8YzYmhpb`UEmVRL<4)AHC!6FI49=R_i|G10H2S6)vx zWq&MofN`Z6EH9z3#JGUH1|A*~MD`%?MDaAB^6OsP9tizdX~PpzW^q#}BNVU(E@O>N z&NC6gcq=&pT$yxDaD^y^6Y|nv{N_Pw91s> z7fT1%ZSNObZTm&q8D3)a&R~`eDky|Clhf`8j3{0JFn(>aHQIVa-B{@pS{($;O%a|l zHwa8R$1c&iY1ELA;+qVfU{c2hl&mslRIuPYQ)qDVNwL{>P{gvmN+)MJWeuIJ)kV{Z z(2TkF2TK&M03yFOV#(|i=Z%H2fu+f!z@nOhf3ZQabElXxnEzj>sbWb*o-x~)y{2M2 zGcGkv)nuF$cfokHFs*(AT?|)7(?@hRy2g{*6K9)VyboBSI0%URD%i;OsrXZOx3OwB z3v(l4upju`ev$f3$r;Ii`Bk}VIi1j~t4LdsXIim}c`r3;;!@Bfh8{8P21_c~D2)Pz zgqr`NvmQ#1*yQc^2T#NwhF*yxp;xEGdC$n7)wx-;;b~<-v9man{NsHivnPeilo90` zQ%0~RyTs(ZRPbK4)Rdgik7o~wQEY>LgQb*68)eR{*@kX;Y|uh1F2!nIA33~RdnABP zbT0j;<9_oDou5b-UYtVxB`ldGOrKRaDJ()pCrhVRWanw}jDq)4{LKzol@NIXWIiBz zF^g1dsYq4oWz>~vu8G!J=mKWo<=0D7C*B9dTiMr$ZkeF%4pBe&!ifYLC7b5X*zO;s ziU?kpmu-s7UJAD1FGQVBsh54bF&SIMGKyKCu)lUTHWi zdL6`7YN3m-vrB(`UAROSHXeij*15*z%q97~HuDWgIB(|}jTS1V{> zU3w`quBga51{KyKmeGaMbc?VP=EzPh)Oxr8y3jc;7#1m=p^n~djh(3LEUdVEl%A) z=f?_CtxV}|VM_cfPhFQ$vB;!ZCzw8jEzpA9ULV7bU}rdfn1zoXXgN;mG;67q_E2BS z-28jNxz(~b2$d3-`V1^vozeobD{xIE3N~p>B~T#)RPReu}w5b@S zg;nWwyIL^37TULd_MINQ)vj#kiiw_4NY(BjrF5}WQPE&3F%^CQdv~wM;*?ygBcT>d;1-aMCg1+?Tek&AFqF`AKjzM)i=8W&$)6fKu~*qE#{H2l za8~FxFi50MtF#O;gIe*Sv1NU7z_*{eb)Rz-Ob8!Wp2<`d8tU)F?y6Y~iYuj^8$cFx zoawMgfJK;Q)VjOHFQe}3t#93tbzzC(J3#cRluuHwt!Sq_T*u_v4N`45A8iAEaFh0x za@CxVOCvLNT;Dd{*gPHwAiiPUC#u4kh%PX!q&htoRXpAdb==2hMSggFM|)#m+s!|~ zhG-Szv{Eh{S}E<6YMC^gwQNA|FH+yp>)UMJjBmDkY(rc77FbtW=>}am?=MV7`^cqo zEJZty;E|^5&A11k*EZyJ0B@B7r;1bu#$tD*qYKYcA9q{rz21a7Ci-ndE|6xitd#Av zPX#>FfCrf_t{si{UvI#DB%eYTb3e>p5k3+$8|nrNSJFA&lsjmKuAjn%G~@yTv*;XG z0%%`aMwbqw1$PV$T~EVJHRN^z$aAF_4}O;OHrx?4bUgzH*bTRqXyeYUq3h>xqaDL% z?j*bQ`URYDH(YH)4!5D}1vu?)IO>Mnbwk%naPr-7>$kWZTIDnEAw9N z%da^$hwH`smY;K^4$a%TKJU-Jc_)A9=y*KuSGkL}mK6Mj8w zya;{qaJ;F*^3OKd#!r~e-NMjNaZ|<*Le)KN@9XeAyA!6*oW=J;mxG{_lLN0pZ9hDh z?XVodOr%4ra?zpK!1&m*poCd*bScSq{6+ifa~zJd(RcL@59al5%}$C*DVsYRZnbz{ zA!+9H@t@mQ-iPTV9-i|V-o?gQbZ%byB$hBcc6Hh$`1WIGPfucjFR{Sio<=Vpo|F0B zj%&Yoc(TyDG#-n(;y%eYes)kgosaVkd?}D!PI_Mc>%a;9=;On2Q{T(&U62%*^2e#+ zu)G9?JDo%1BRC^)W=zB7m>F`doc;C$diGtrxJTwp_rs1B6fXE*2#H;R&smKq#q^{Y zb~zs84TNVuZFbCWr;q;_19>z)^O5`L=OL7k6qq&`xRWrP_`n8?2Y#g~ z6UKA<`MdSPkIdVjHqm|3tfZG_&gQe8#{zVW2i0PDz$h>V{xLXia=l-kz#YVogJ1ta zA_D~~cZ+VGFMA0gtDSBNi#>dpB)pUa*ER;aJYl;gGI;z480q5|H5io;L|udyE(mu! z3BXvA&kn@!u;|3`pM z$1E?*O$gP5PNAJMri?#~aX*d`kkO2&q>bZ29R-4p3O9H8BwtMZ>6rLI0l6=Go7D7q zX6VG=@ogZ);}&iiwdf6t3{F?M^SQvok6N|LqHv!S$cd0Pw{U)#Ce$M$&U?g(1}F9^q`VpJHP4U1+Rw0CTRRJn72PYPJydY34#@vo_|)U){a$< z)iL+X^u#rE!USc{#htFuiHL&#X`&O0GK>q2-ag*KV;CqKwZL0x9QC(mhHewT%MxXydco4T_iJ-h-UjSW7l8XvQ;+^B{5G_e8mw{kP@9T&P1K_5d5sGCkG z=yOH7S3#a8F>QhOtHuWJmvb||qQPSn2prAOV#bQlC4bFSsWaDjgm{Jcjmut2-7{Vb z^UGQw_&^Sfk<*ENB@B;M*l~12nXiS=0s1&osq+E>R?J`F73UucX)t=TIo=?JF*gRk zANWcwKJq~4sKre)5;ACj`V=l$4d}#*g>&XF$z8J|a)EcFk9T%VNg=zr96sWB>S%^& zLo*_M^X3MpM{IEA(jPXTP6RE;3+1}shk0kS!a8;{ujLWOZbvN^%zPsgeB)Sj)#5Q=;p@VcYSMEEEXpcf<+;`^mSxurT za_01zbEhWK8BMO~xk%)#+)PS*D3S8}2(dSs5@sgs6@|!{Dche92>v%t@*7E<4=vb! zzmfWx3BIjjU47)t5(Jsi(Hl9g4=-T<0P+5rz6poK9DmJ?G~iK<_m4bFATqoCW+wE5 zW(W*FpTGpHH?q4QD3@Tw2?+Jshi_)C4?Fo_SlJ23ab^HclHuef3?p%21St!{$Y~g6 zHp4wq9fpFT0_KLd?dh_+q$;%m1KBMs&Rp6I%H-Ditd0lG`H zMljXoaS1+wr1x9zwIRLcBT-W~e-9qy$5FodvDuag0In`d-o;2$9lCkF(YL-iWIJWc zr~dq$h4mg@?c%CznB1r9HRCww!z2Et$u`cc+6>(0L=*Mao3c%LCe6Sdz}&X+0i-Tb z&zz-mii(SPrL@^=Ic% z@8x^JzSD#Ik)t)o$NTT~MTVlkv3zD&=v~(;l90O+=~ovwQlrlt41v1qw@(Vw@Z(_n z9%6IBrj$jPH=8rft}fA$)NA6rSV+Cmh6^LXdS@`L7%c`=lwXw3z$og;$={-x}rm%i(?7Hw4>8z(7E6E?ia&HdRI6t zNJb;EjukOO5z8+UATNBp9tNZL9ubL_I~T3m#bP!m)Twezr88!0u?L?*_~7HMnJ>7>_T^3hTakd>BI+xp zPFt4F3G+-0qy9NdeU`qKmrcDDppodiWCrGwv=T=xHsH7oPjI4vRn)MtbR`oGp z+ZhrXw`K`Th!39iuao&a!g&;&-%vcEF2zyI-ALp=|0hUZ2Ru5D@_8&w^tmAYkrb>p zgSCZ565$XS3aS3hUF9uT6|2Dx&k4vofhg_QJVE0j>B(HHsRE1@^)%c z&C8_$cQ8X*3N;4#q`Y|l1p9HddjevndjdKNxBh6rF$p4dDx4Kkt^SF{(kCi6m6X!C z+?7GO2w?hjjSxj?y3RiTzCPr;VZbp?;7~c4UEI}RrM^<4%nGA{kZ}eY#HxKBamBcb zP!m7RKbs{?Nthn*7ZDJNG=%b05MEg4A3w!6!8D5>=ig^9dBDDVV9CA-zIW=`i>x!s zNvn1@^YiH_*6?}dUtu9#+-S^Q#iQijG{TPGz_mRiHsG67xHm3do&eN#HM=^gWX`By z1Cl#&N;hM*Z4OI!0if6(a0_tVE2S%N63`$rQ_7pfXOcg+`fYzuI6&NnXW)HmR2kZg04HS7w5&5T4$-V ziy;iP2&m7HMJK}7&ktj89c;N?aDpWi`m2;7B(T7wKP*V|nn)+M{?(WHmvj%EJHNF; z$D@Im?0@?{`e$KcsUqAf6(}yUF;2mp=>*bHjH{V3sI;)@{<=SeEFOZkeTpyQ<}dqr z=WX}B7b;#%qa8I>RA&Cy9gDR@tyi1biZvlbeAzs&z&ZA!e4&TFq+|Bt8Z04b7Bb7% zm!Q%O3hlf|l<1rL-c}Ry{{(4;`@da*RNQb2!?UPHw%f`ah zxhPmrvgZLtl7NXnCBt`nK%r@u&(gdABp%)i880C6f%r3`r_Am!U$Bd@s-~0>XOUKqUFaZZKP&6k#I4qghrfBHyuGTRA*Tb>%1`%)u{bKw$c=tJsc z`?m|peD^?#J)E_!D@?B;uZjB0)v3nS%Sk}_z?BP1j6VBD+)MuiSjF34O!iHjniU-R zvNwC#dujG-d6D-(!wb6{hkY6~(M?;aA6uzchNZhpoh7p9eAa`>>OljBdg`gqo@CZa z^}OT;)<~3}Y5+Y0V`h(|?jeHSt`yFr2e;Lue6h1uP(|2NNGnrIh)Q!;mSGd)<2^*; zk@lNYc}lW>f^VutIWI0a&)Zm%v6|(MyE`U=is*)9yECpC3}Mu&mN4uIojh1kF*F1Z zqIeWeDpygTd~Q<1ODKoOk7vzdnytN%tb=!`>+=X zgtOXD!74WumX*M|rpqn&-*JX5T|jce?^s2mA9kfo8MJCO?XtU-HQt&#p=*SV2O=L# znC?2Cy31YQ4#HlGNjzDPhm$!v)<2;zNbQp8Wl=pJ2TzYiS zG+t&WY!75~#zk*UMYskPKDcg(B)0cHyA9gL$1Zt#U2v)kszX}TrN$Dvyt>3l{n(7& z5mys-EyqR8N;-h@RUUNO6_G5M5!6mS`E=52l8|6pQ=wHWnTuzt{C~r6o1QU8@Tm)_ z=eranx!k32)T0DPy-PN`NGz@q?ow^J(pjA-_$9#XO+5i6=v+xY$d`c#-+yFVA>0}(kD;{yrkhgKcqOE#2odF4Ty_? zRn97(kO@vM@DRhB;j=Kk&YQ>&6|_D?WN28myiDnj2x5-^_PVX^TCZ{NU?)WU7W2 za+>d_ujWk8`5HQDcFhcN!FMfHNI5yJz(@*wLoB+rK+l`BNA?JaDir%J8oPwMN#< zsi`azLpl)}jMO$7EO&8`DYUBKlES6lb3!7Du>O9sl==&A06&O8PCtZpCH1}Is(MDH zctWpqQdf>qM{6dA7~?`Ta{|%=IuNVbJ*;wX8S$D$-wh<_nO+Uc@DBx%*rDbT^)MBp zWcE^7oRx{&>t0=#O3kiJra(o{1|P2Ssehqn`4H4Lga$z#6qk!op&}$s-#W|SK(x}y ztnjR*0cpWY*^FNwM*Od7xMk@~k25g8h)lsORXDp)HxzVldO35FA@^bzOn9Sd-Nx7! zBsTJ_b$$r%idwoDhSFFFmuFUb)~hTlxbD}#<@KYR?o~1`()6eS%qagW%f#BRmLcxl zT^~u^%+;xm=PUVmga6D}!8QgrqY5@pf7hv;)iSAmvD%AzX9O63_xy}$ZO9xzJb4&} z`%+M+G4+uGnBi5PX%VCr7)YG48KIu83mc#Z`m<@ban+wTR;=5NGUG?N&OA3MQ6<+) z$9e{^w36+Ikx|k#+{%H;32>i3vd5QX%Cw8MSfTZ5)NrKJMJxFFK>glEVp*}qRY4Ui zsdpD@wReG^<3fIqDtn%xziUT6QPaH(7p5dfnkI)#O@-_3*ZPR2jAWB0m}%VsZ53 zREyfeDmLyCzdBz5y>h#%mArCc8&^6b{<29ltntF8M_jiHFAup_f|+;u;;Vxk5YMkw&*YUIk8`0N*4?ivWCH)q>Y5xz z5AV@k?>rt=G#~VE2(!^CemsG_{|TtpVE^sV9e=xu%AT0iZFH4R8Tf+-Y;}g5`uC4R z+HSeqI_$fbQ9=|oo=)W>ZETwWfGv2mh;`(!IZqztRaC$Frg_BQT*R>z06$h7A#tb= z-9|^~)Ha+)*=?&s@AHf5VNjmkQM4uO1CHpMt#*xCtMfP;1M&>g*=n({fkTjbe6V)PC$aL|xrsFgU@7H!K% z7texN4YvPL#a(AbhWvV#zat;{7scF7cDHBPck)c5?O)#w>&GdfJz^9nc8bnYS8=`r zoKo~@bgfJR?jB{;+vu2m5UfgQ&)Kms2XUBa!vQe-Ue90<-@!A2Z9KzN%g{H}3x6cl#LG z-rrJOl(R5@hp89!kim{42C@r4ag;+dOw|>Z1dJ4xEyxsb>dme4xQT4W9vh| zile~Aqt%>cZH6dAu@|b$i%i02ZVhPB1+p+ltE8HaI`T^m<1`WY-1yS=W1$O7uVo8y z7K6HKtBP?Z<^cd+d29h1sLfI0S+v8zc5rQ7L|=Y_!%?+#DoRpIo$PEhwMF6o4ns|` zfl2h#Rb88j{~ILiCAwo?1<7ctH7z`2$4Gm@$14w1gVO) zeSM~GyJ-8dBq}6I$c$bHZQo1nRnd*30QUT243=4CQCQPh6jrvj8V1RG@?+3a23d3f zAy3iKT89QnR52G@yq6|Z!mN6Uz7Z;{+EL=brIe0-bJp&_A*m(^fTWV=*%i{lvvp*7R~2d4YgGxMWT}nHhzwNX#0f!2BV|a5$$BG zqnbKojgm}uwx*a)RoUfQ56Om-Y!{AJ*L-W%)6vtmEnBI*I4nA|Dr_@#IbnyEW5~%j zL{V=sW_df-6eMIk7>-ZBhd8WyN083}S^y?c4W?)nUKRv0oZ7+Z-i_5`OEI6%QB2Py zEDG}yij{xXJHCO>NAO*zV}nF5NLV$KH~;{bjBm?I)JZHQQMI0+HIjq|@usA%%z7+( zgRIw3C5eizLiOy?Le=+uMekw{&XJIcC zLkb)u>MBog3>FfdmFOjovTD{=9c68@i-UszcHBm|_*B0xT%XwfhZA*di>ivEc38UG zB-pW?p~0Mm@Y%9KZu{cM>nNeUMW#Rgr$P%Fb43r$WmjKEt6-jSHO8R5I^hsyXR)FV z8uXrqEmd60VCRIGcn23A8048u?+5|pD{xzrL|=pTLUvt{?k1k#+=eLX!ppQ^@o{Kp zVL|W7G%VcY87&M-q8-lDdSqp+5ojzQ*8kuLGx_}{L%?86N#QsQ35vM19Zw0 zJR5|i*(h<`^adMXZD3bTH9N(1rz3=pGYd-(U*l_TBfC3}+nyw$78o3bs@mup{?eYR zO@^p2Hdy5_ifFpNuW0nwr3bnS5+sAp;i7RWzoCYZTyeyPlQ!32Vme9$$0|Bf#P>|t zD{|U1*j~IXyd>0yjeIp7sjjgvN8k{H7}?FPyGZA8+mET4RXJd%+-1LZ)clF|7>c7d zW2s}&n;#nDu75_v61$aP-Ce`LsJ2}-f_NBi;6|}ZZ>@&b!}6rBW=9zIv27BZWtLbS zsf%%|3wDHnjfnl89&>=35}b9%?j)nU{M(b%g3T^=ytP;(q525hk>V;E42uTT(MQR} z>z@H3-``mwY1IN&8YZcQly!vsggY5{C?3vico?vpAVt>79IFKbcSM|p!PkhLbq735 ztOF0&WsLJj;s8Feqjwfzf!F={0`FS$s$~GXEEnt5v1KKOuqc@jjV1!YOjvYgw4>2x!+zR5v&j#&< z(gfutfc&-WN&3f=&;InOUyX;!+=D~M@D=kF2{fy5fD6Bxa|R*<&Tx?fYijH}SD1L1 z^-yt+jn&_j#a%T`sE74#Rc(|*47LA#**0v)u0nQd`1%x09m3#)V`%)IVQ8qPE-P1A z4K=mgrk1$;_-E{?C+UmysXu+?xdLMRMcz1Az-PEsUt^Wj*j=D-eRH7@BvyQY7)9Yf z3E#xRz;?hQ0V`vm+M((zSz~bO2sL|Pag^sKMMS|Q$Xm9Wx@xG!8+@#BfB{GCJLuC< zLe%uVM}*}8bG_QWfKa$uu;<3VGM?FAWWFCl0hwS{I9c>y1W0M; z1Q!d~(b%=v*#k&?fjL`jKoa7hA*$+Y;#La@fI(Ng7Y0RIi6^z-#Vl3AvMRWVK3xq? z)l_X^T|53hikS9%zcDp}oY=tpg8lL*a`sF1)U)G%_QI^^#!q{p!TvJ$gJ4R7RZ@$6 zYL!r1RjUVw1kNQ+<{{nB`9a;AWl`MN?uFZ7Fbtcen_?(%!-2s!(Spo!A8)V3swC=q zZCMFtNy}<{(JtGHWxs17of zt954nM5iGi4pT@cWaKEM2v$M|k8E;(T-$yefXUp%83o~eZFL=Mr`A&JOeGF; zAcb8!=#$aMx$mC{CquB0OBm*NyNm7_clkv6$Fpqw&tHLz{hs}VF3s2@@&nwR;2k)_ ztnk!<5il6=Qf)mq46)X0J>&Ka*g0PD`YY33(7r(X_lf)j z`B6yTm z5jqQY;#NCyLIg2Nl?f+MYYDYRA;eb;o!shzSjHvl+K98ieD-u9<>Dcr$D4>o3%w(| z#%J>g#{O`T=Uc}JAa;9{6I~O^B~D`$s#!5*mT6MDq{C@3yabZ2;$>t*jMWQPJvckG z9M;rU)j;reA!=yXuI&nE`W&kz)GV$z;l7QV{)QaMbu|FKHb|L(2Ad|KHlwHz1uYr5hoVEojn; zDmB+EbWwq=#TochP%EXYh{4DI5c)8r`9`x3n<1zYVfa+|j(Q-29_0+E!a<4?kk7sd z73-5luuEm2`eha)T$)9naYa020>NLxtZ>V7PZi(wwbxqp4cz(2Sq&(NiXjYRh(bH? zTZR=hzqmR_V%G__{wS-$Kc z;obkhc%$BtIC+I`<<{bG2mA1l@{GEUS};;ibe)lq&DYCrauWKuhi^^xpcv5&l42d@ zjH|LMWBXPUGNbpjZ&x6lj|ztr&BKvVR(2T^Z*Wt zK3|YtP>M>Ob{%h_W(B5DODeSfK<#LwYke?u4%nk{?iswGd7_3fyoZDjMvDY->GmpD zx+ZtlTTnyJD`Htp%3SUd+ldsaK{;Ixolqyh3%8G-uDA+nUS%*)t9Xhhe-8o79S((? zI~)-BSmjtArI49&SZ}5?LOMAJ$Q9+AWEf>sXjQQR{rTw zWo9eX6HB4XWr{HQmM}YSU~pn#*v}8^G^IUF18NxmPzO~zOh{NeRRlI*?XpW1c!RBk z%p$SOkB0En2ZSZ6B~|&BlM-hC>$iTbkT-XU7*slSRjrGw#EezKJjQj51<{t2 zFv%AAmUqDg&F^^mhbHkYhFZ0J+R|XYA=3c5;ogGYQNFBI$!Y+?`q*%2X!Xh1G(X&2P%Isxf?qPJE$B+pE5wIVL zGw}XieS@XH!S^o+w$1n9yrpxBDRoqns-zq_ndm?YuHsTcF2Rbr&aUE2SXdgFKiQn9}a9bEZEFh zZB|Is1*oS*JDv3sc?4rDIVB+VuKgYz*eHwJV9ME{NlVAesom&2>(lG3a(DuA!`$oY zCzv$vzj6S{vUC-bl$v#L*&sx~w*u8T8Og(rk=b=DAhX<}2rx<1==|nHXRiDg=3rBj zZ8GO%=TOu91~GI!7XfTfY7E8LMGj~L6e$^fsB|x=0rN9;3YA*H^a>rk4g=*gV4eA= z73kh5y<=A&7-#^tTKq(FLme)siq@8b&hBmzX1xwJ(9A7$6SA2pn9d!veQ3iBFGZCc!NgXY z=rfbvS#MQ?oUk%G4cV|7JL*jUutzinFUiC{+A|F=y8g%L0J<}nP^K|q=XOo!taG;L zol(ECEP}5rbZ#1*e}S%ky+aQI2XxD!9#m1dTAz=l>gq>LZ^@*!Vg;&hf+@qd=hUsMFS$?pg3YT z4+aDvZVNy&f34RBi^^H2u&5O}sf8|}_%@}=8Ekr>zNH)Y2Z8nE&{gcxdl+Q3UXwQd z{ZLHaFo@`36!zFYA4Myzm@%xwMBTM8w3ud>$ccjG9Znm}Gi*bOhccTCtoIN|(KzC# zcma^xKSkpFwGpX0mE2jUwrKhI6LeZ8e*uc&!V(O>_W>T=frt$oA5dq?4%Pf_Zt{dq z9Mmg&T0ce)eQv;E((YiCW7kj<#)+Asm*mqRj}8ehXk7`;LS8P`T3L&QC0|&-FXM%=>;=BAr=;b-&mHSuhdr!E`f&)I z1w2hn0ei&&Om;s#KLiXmb6Uj zbBgO!CQnAE3-Y<<89~!eQEec23A8RsF4V8hgfWNC@`XPq8|UUtp)j~i9QR>+tCl;q z;ji8tYO`A`30<4^^d^+?yQWZss=Aca6edqWZPGF*qo(0E zg;@-t?p{e(IZAucmpe7!uRrCbg8VV>*QTnVa|8wNO_?0Z2R@}dTqK|FR|vs2%-~9f*JONyzo%-ZN6!+f72l5W9i7>$kI~(&ywJ-Y(SQDWg+4zj0K&()){lYSa*I>0A0vFz#O6Oe`Nu9QAfSr@ zX%kHBDz80q=kG3{!VOL^Tzw0|J2Z;Z0Da?k-uKghn`2sn*PU&C#*YQe$7)v3Vc!1! zB^2)An)~zXS2#bud|%MuZK$EysPAt7=9~Afppp<*92c%%8-OoV1EX)~feWC{EjPYI zAtNpYSFT@Sxb^UUUO_+kr$O7D-kg1>_ialrubSuK>hX&wf1y-*kC93}Cr|$WfAuSG zzn45G|GU)9QJp5!tbE~L{{2Z+{LjOd{M*0(UYf9M!pk!~{eqs0%O5xC#h)ltvc;>P zTTt|`8LP*KY2?pO{r!?6A5F&ZU(cSFkTE_W>|d8xOe?Ag4vTp@!zf?A=K1QaiR<1{ zFE5u@yg9XcYe-Q^>WqM2%n6^RS@18lTV^!xPyO>-k?ENe;xzwmT>e}}=5v^|M&pEv zwa>p@>041Ty>R*T$&<$^mA}$z{nxGWc>mp3uU%b!{#?z(lQ1jXBHLyVcK z|E`SR{a(haLDSrxlvWgmzFiUbVRJIE*Oe8#yCLQiVcD7`GrlaENfo9)$HOD2OOrld$2f z)T*tEk00;+%U?EbdoOlZTpIezw8LaqwoNwWb-zWIZ{LfVzcGSGr^e$UxT3fZQa_;Q#^u)B~ zi;aK#^Z(uQw=G4+)s~7k6XH`}_V%lJuc~NGVQ#_fWyXZTfiDDBwY2Xd|5f#taY@=1i#c}2OD3c72S4~hnPEzRZervgpD}0rHeV}m{Lh)4@6MI~ e-o2whzqj@OuFli0F)p60^bAUW`UfKa`~Lu<&!dO{ diff --git a/Art/Districts/1200/CommercialHub_ROMAN.PCX b/Art/Districts/1200/CommercialHub_ROMAN.PCX index a8fbfdd40add470a7c54f73c556fe849c3659422..eeb9e5a46e8b0a396abbab441e7102f4991bc812 100644 GIT binary patch literal 46117 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 bCt9vvuDpJ^?%EaG^=rGXUpwW(vw#0TmA6+4 literal 48896 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

QD6&-V;zJfjlbE)4|jTt%F(!7Yt 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 From 566bb60ca8dad7da07c27b8cd594620cb75a30a7 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 15:35:58 -0800 Subject: [PATCH 290/356] Major touchup of bridges and canals, looking much better; Refine rendering code; Simplify tile render pass logic --- Art/Districts/1200/Bridge.PCX | Bin 36760 -> 35619 bytes Art/Districts/1200/Canal.PCX | Bin 72779 -> 73965 bytes C3X.h | 1 + Notes/district_todos.md | 7 +- injected_code.c | 118 +++++++++++++++++++++------------- 5 files changed, 79 insertions(+), 47 deletions(-) diff --git a/Art/Districts/1200/Bridge.PCX b/Art/Districts/1200/Bridge.PCX index 9211d945176cc7bbc9c49b7a99f574000fc911da..331b45e79690626b608712afbe1c7d662611b332 100644 GIT binary patch delta 1088 zcmXX_U2IfE6wcl4E@hjRl9(pE7>N*wKP6F#(HMN-NlehtT2K+81VT$H0)iI2P${&< z5QsSm;Xz{wV${T7xMm@|=**>T+r91nblEM?Xj5vtbW7V^f~09Y$8WZM`hI54IWyAdf^*d{ASsZBM337(M&dZVT;@Br)qg=@A2vS3+SSGzMX4mM8d+WO!aSd!_` zdk0HZEDaH7dZzAdFa_GrG*ssVlc0S};my6lIH%|2+Nr{rjcLTM*43Of8BP z16}(3!_h?v@Hi`K+&WXLy#XkcYW&EE+Yl+H-P`^x)1DGjIn6FLd_21tzFF%MXYcl) zRj#~lF`)z8Ez0TGQD%iaF_{zWiSu*)vsNfBrfQ7i;*9L9w#r>Eg%g`or^Izu?%HUT zYj04Zu%bLD+G*Q;(FzSo4~!lj66ei5R}p0oO9W1YhY=NObga;*biwH2QE?J`p~u99 zc6m&k^G)5BZ(N-EeHSgC77Ffv0pP6Nf5j@%wkwo9Q0;e7YuUjt9Is0#{n-ltEjYeI z-x?<(^yA^x{%$y#!*5&5lou6U(D{*tbB&13>p~QlUjM~%(}%*s4GN;Dxq0I%a~n^z zt`=*5YTF&`$LfgOkp~P0=Qs6`bx~s7L#2Wui^y(4}*TYCETdU0gXnkvw z6`|{`%l$o2|FrI_aFM?FDS$5C=*gy~7!XfM@%I2DtZC-tCvc-YAvqi&2aLi!eyX7& zh8OV=-a}xFsidv3Vt_|v8rC#$fay%z)++5~_%F%B%mXv5JzM3E(I2%{^mO~W%6oiQ z9%1nj@E&WMT24JhSGFvsZ`xn4biKbhEswGM7&y$7ZQrouJ}2b~tS7)x{IhpDURyHG q{W1%07MO+C+3_-hbC~uV+&SPR(`3gg;<8G5>GX4!I6F?i8vGxDQU3V= delta 2197 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 diff --git a/C3X.h b/C3X.h index 926752e5..7088ca7d 100644 --- a/C3X.h +++ b/C3X.h @@ -2030,6 +2030,7 @@ struct district_button_image_set { // Current tile being rendered in Map_Renderer_m19_Draw_Tile_by_XY_and_Flags. For use within various Map_Renderer::impl_m*_Draw_* functions. Nulled out after the render function is complete Tile * current_render_tile; + struct district_instance * current_render_tile_district; int current_render_tile_x, current_render_tile_y; // Used in patch_Map_Renderer_m08_Draw_Tile_Forests_Jungle_Swamp and so on for flagging whether to draw forests over roads on the tile being rendered diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 6af4b3d2..8bee23c6 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,10 +1,8 @@ - Districts: - - Central Rail Hub (+25% distro hub yields) - - Mass Transit System + - Municipal District (ZergMazter doing art) + - Central Rail Hub (ZergMazter doing art) - Canal (2 opposite sides have coast "isthmus") - - Bridge (2 opposite sides have land "strait" or bridges) - Light annotations - - Double check PCX third column alignment ## To ask Flintlock - Enabling pillage button for naval units @@ -86,6 +84,7 @@ - ~~Offshore Extraction Zone~~ - ~~Data Center~~ - ~~Energy Grid~~ + - ~~Bridge (2 opposite sides have land "strait" or bridges)~~ diff --git a/injected_code.c b/injected_code.c index c125fe61..89a7df9e 100644 --- a/injected_code.c +++ b/injected_code.c @@ -22040,12 +22040,14 @@ patch_Map_Renderer_m19_Draw_Tile_by_XY_and_Flags (Map_Renderer * this, int edx, is->current_render_tile = tile; is->current_render_tile_x = tile_x; is->current_render_tile_y = tile_y; + is->current_render_tile_district = get_district_instance (tile); Map_Renderer_m19_Draw_Tile_by_XY_and_Flags (this, __, param_1, pixel_x, pixel_y, map_renderer, param_5, tile_x, tile_y, param_8); is->current_render_tile = NULL; is->current_render_tile_x = -1; is->current_render_tile_y = -1; + is->current_render_tile_district = NULL; if ((is->city_loc_display_perspective >= 0) && (! map->vtable->m10_Get_Map_Zoom (map)) && // Turn off display when zoomed out. Need another set of highlight images for that. @@ -22160,7 +22162,7 @@ patch_Map_Renderer_m08_Draw_Tile_Forests_Jungle_Swamp (Map_Renderer * this, int return; } - Tile * tile = tile_at (tile_x, tile_y); + Tile * tile = is->current_render_tile; if ((tile == NULL) || (tile == p_null_tile)) return; @@ -22176,6 +22178,17 @@ patch_Map_Renderer_m08_Draw_Tile_Forests_Jungle_Swamp (Map_Renderer * this, int void __fastcall patch_Map_Renderer_m52_Draw_Roads (Map_Renderer * this, int edx, int image_index, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { + // Don't draw roads if a bridge is here + if (is->current_config.enable_districts && is->current_config.enable_bridge_districts) { + Tile * tile = is->current_render_tile; + if ((tile != NULL) && (tile != p_null_tile)) { + struct district_instance * dist = get_district_instance (tile); + if ((dist != NULL) && (dist->district_id == BRIDGE_DISTRICT_ID)) { + return; + } + } + } + Map_Renderer_m52_Draw_Roads (this, __, image_index, map_renderer, pixel_x, pixel_y); if (! is->current_config.draw_forests_over_roads_and_railroads || ! is->draw_forests_over_roads_on_tile) @@ -22187,6 +22200,17 @@ patch_Map_Renderer_m52_Draw_Roads (Map_Renderer * this, int edx, int image_index void __fastcall patch_Map_Renderer_m52_Draw_Railroads (Map_Renderer * this, int edx, int image_index, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { + // Don't draw railroads if a bridge is here + if (is->current_config.enable_districts && is->current_config.enable_bridge_districts) { + Tile * tile = is->current_render_tile; + if ((tile != NULL) && (tile != p_null_tile)) { + struct district_instance * dist = get_district_instance (tile); + if ((dist != NULL) && (dist->district_id == BRIDGE_DISTRICT_ID)) { + return; + } + } + } + Map_Renderer_m52_Draw_Railroads (this, __, image_index, map_renderer, pixel_x, pixel_y); if (! is->current_config.draw_forests_over_roads_and_railroads || ! is->draw_forests_over_roads_on_tile) @@ -31299,37 +31323,6 @@ get_bridge_directions (Tile * tile, int tile_x, int tile_y, int * out_dir1, int *out_dir2 = dir2; } -void -draw_canal_on_map_or_canvas(Sprite * sprite, int tile_x, int tile_y, int dir, bool water_dirs[9], Map_Renderer * map_renderer, int draw_x, int draw_y) -{ - int y_offset = 9; - int x_offset = y_offset * 2; - - draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y); - - char ss[200]; - snprintf (ss, sizeof(ss), "Drawing canal dir = %d, DIR_SE = %d, water dirs[SE] = %d\n", dir, DIR_SE, water_dirs[DIR_SE]); - (*p_OutputDebugStringA)(ss); - - // In certain cases, add an additional draw if adjacent to water so that the canal appears to extend far enough - if (dir == DIR_N && water_dirs[DIR_N]) - draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y - y_offset); - else if (dir == DIR_NE && water_dirs[DIR_NE]) - draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y - y_offset); - else if (dir == DIR_E && water_dirs[DIR_E]) - draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y); - else if (dir == DIR_SE && water_dirs[DIR_SE]) - draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y + y_offset); - else if (dir == DIR_S && water_dirs[DIR_S]) - draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y + y_offset); - else if (dir == DIR_SW && water_dirs[DIR_SW]) - draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y + y_offset); - else if (dir == DIR_W && water_dirs[DIR_W]) - draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y); - else if (dir == DIR_NW && water_dirs[DIR_NW]) - draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y - y_offset); -} - void get_canal_directions (Tile * tile, int tile_x, int tile_y, bool out_water_dirs[9], int * out_dir1, int * out_dir2) { @@ -31472,6 +31465,37 @@ get_canal_directions (Tile * tile, int tile_x, int tile_y, bool out_water_dirs[9 out_water_dirs[i] = water_dirs[i]; } +void +draw_canal_on_map_or_canvas(Sprite * sprite, int tile_x, int tile_y, int dir, bool water_dirs[9], Map_Renderer * map_renderer, int draw_x, int draw_y) +{ + int y_offset = 9; + int x_offset = y_offset * 2; + + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y); + + char ss[200]; + snprintf (ss, sizeof(ss), "Drawing canal dir = %d, DIR_SE = %d, water dirs[SE] = %d\n", dir, DIR_SE, water_dirs[DIR_SE]); + (*p_OutputDebugStringA)(ss); + + // In certain cases, add an additional draw if adjacent to water so that the canal appears to extend far enough + if (dir == DIR_N && water_dirs[DIR_N]) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y - y_offset); + else if (dir == DIR_NE && water_dirs[DIR_NE]) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y - y_offset); + else if (dir == DIR_E && water_dirs[DIR_E]) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y); + else if (dir == DIR_SE && water_dirs[DIR_SE]) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x + x_offset, draw_y + y_offset); + else if (dir == DIR_S && water_dirs[DIR_S]) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y + y_offset); + else if (dir == DIR_SW && water_dirs[DIR_SW]) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y + y_offset); + else if (dir == DIR_W && water_dirs[DIR_W]) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y); + else if (dir == DIR_NW && water_dirs[DIR_NW]) + draw_district_on_map_or_canvas(sprite, map_renderer, draw_x - x_offset, draw_y - y_offset); +} + void draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int era) { @@ -31480,21 +31504,29 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; int offset_x = pixel_x + cfg->x_offset; int offset_y = pixel_y + cfg->y_offset; - int draw_x = offset_x - ((sprite_width - 128) / 2); - int draw_y = offset_y - (sprite_height - 64); + int dir1_draw_x = offset_x - ((sprite_width - 128) / 2); + int dir1_draw_y = offset_y - (sprite_height - 64); + int dir2_draw_x = dir1_draw_x; + int dir2_draw_y = dir1_draw_y; int draw_dir1 = -1; int draw_dir2 = -1; bool water_dirs[9] = { false, false, false, false, false, false, false, false, false }; get_canal_directions (tile, tile_x, tile_y, water_dirs, &draw_dir1, &draw_dir2); + // Set offsets based on directions for (literal) edge cases + if (draw_dir1 == DIR_NE && draw_dir2 == DIR_S) { dir1_draw_x += 7; dir1_draw_y -= 2; } + else if (draw_dir1 == DIR_N && draw_dir2 == DIR_W) { dir2_draw_x -= 12; } + else if (draw_dir1 == DIR_N && draw_dir2 == DIR_SE) { dir2_draw_x += 4; } + if (draw_dir1 >= 0) { Sprite * sprite1 = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][draw_dir1]; - draw_canal_on_map_or_canvas(sprite1, tile_x, tile_y, draw_dir1, water_dirs, map_renderer, draw_x, draw_y); + draw_canal_on_map_or_canvas(sprite1, tile_x, tile_y, draw_dir1, water_dirs, map_renderer, dir1_draw_x, dir1_draw_y); } + if (draw_dir2 >= 0) { Sprite * sprite2 = &is->district_img_sets[CANAL_DISTRICT_ID].imgs[0][era][draw_dir2]; - draw_canal_on_map_or_canvas(sprite2, tile_x, tile_y, draw_dir2, water_dirs, map_renderer, draw_x, draw_y); + draw_canal_on_map_or_canvas(sprite2, tile_x, tile_y, draw_dir2, water_dirs, map_renderer, dir2_draw_x, dir2_draw_y); } } @@ -31736,12 +31768,12 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc case BRIDGE_DISTRICT_ID: { buildings = get_bridge_image_index (tile, tile_x, tile_y); - era = clamp(2, 3, era); // DEBUG + era = 3; // DEBUG break; } case CANAL_DISTRICT_ID: { - era = clamp(2, 3, era); // DEBUG + era = 3; // DEBUG draw_canal_district (tile, tile_x, tile_y, map_renderer, pixel_x, pixel_y, era); return; } @@ -31813,11 +31845,11 @@ patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int vis return; } - Tile * tile = tile_at (tile_x, tile_y); + Tile * tile = is->current_render_tile; if ((tile == NULL) || (tile == p_null_tile)) return; - struct district_instance * inst = get_district_instance (tile); + struct district_instance * inst = is->current_render_tile_district; if (inst == NULL) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -31838,13 +31870,13 @@ patch_Map_Renderer_m09_Draw_Tile_Resources (Map_Renderer * this, int edx, int vi return; } - Tile * tile = tile_at (tile_x, tile_y); + Tile * tile = is->current_render_tile; if ((tile == NULL) || (tile == p_null_tile)) { Map_Renderer_m09_Draw_Tile_Resources(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; } - struct district_instance * inst = get_district_instance (tile); + struct district_instance * inst = is->current_render_tile_district; if (inst == NULL) { Map_Renderer_m09_Draw_Tile_Resources(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; @@ -31865,7 +31897,7 @@ patch_Map_Renderer_m11_Draw_Tile_Irrigation (Map_Renderer *this, int edx, int vi return; } - struct district_instance * inst = get_district_instance (is->current_render_tile); + struct district_instance * inst = is->current_render_tile_district; if (inst == NULL) { Map_Renderer_m11_Draw_Tile_Irrigation (this, __, visible_to_civ, tile_x, tile_y, param_4, param_5, param_6); return; From 5e423f6c31b34c62bcbfe2a7e20619b3ccba2cf3 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 15:58:29 -0800 Subject: [PATCH 291/356] Add railroad render handling and art for bridges --- Art/Districts/1200/Bridge.PCX | Bin 35619 -> 69983 bytes Art/Districts/1200/Canal.PCX | Bin 73965 -> 73963 bytes Notes/district_todos.md | 1 - injected_code.c | 7 +++++++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Art/Districts/1200/Bridge.PCX b/Art/Districts/1200/Bridge.PCX index 331b45e79690626b608712afbe1c7d662611b332..74137287eb9dc9320ae2666a326ecb23006a3437 100644 GIT binary patch delta 9733 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 B?79E| delta 2174 zcmeH`YfMvT7{`6;fl|uq=?ySh80$(kmAOTo3Zcu~|{`H_<)U5IgceK`>7mm4~M?Np;Bu+^$Lk_Ya zmGLm=n8;uY(itCI!5Ya#}sB4^N2~2?c6V+ zLbmZdI}-JFc$llqaDyEMOcygkBVmv+ivO1db<90xsL6t*%mL;J6RnnzXpZ9dv*0Kr zgo6m+48D=IxIL;C<3`tD>zKWGkW`Nu z;~H@)xdFr8*#oz$3KzyVA^F{7IGuU`iIdvVF}WQ%@14VoQ!k-2y#t#veij+i)g-cJ zMv>SxdnAe24?QGO=T0S|nm>+uAL%}in_0`zH|wnGNViDJcJ_FW7iMDE!ei`V$l@YY zS|0_Q;xzBY-#qalM)0S|!w#4upoqJR0 zvmfTa)H;kWjl_o1ez;I-WegbXGa`AbjAcF(M?H@E!tvDCkF^br+w|DE)f|X-IL4OQ zkjY+tnGv1kdYs{yvON+nm1~hw{z{;(vfRYEn1Fs`n~CfD1!~(^`+LH-8@R^`YN?s_>x3xTMG$Sdn1WM z-?fq$aJHF*`}{Ey>aI=_i!R1{}Ik59`gn$h*~uTetTi z{cb&)@9jYF{TfX9y$}l?l)(G&3$#4);r`=oc=Zo2Tu)bGWcTut!L&lL5iL>FD#}r= zsA#@Y*PAkxwGvHN#stv>T8yrSv|E82xVPs_IpKcnR+&@i6s@{JB2cT<^ diff --git a/Art/Districts/1200/Canal.PCX b/Art/Districts/1200/Canal.PCX index 686baa368bb88ebbb8ab96e400a0e5f2cb79d30e..6cf25f8a6d88687e533627edda4769c775c5412a 100644 GIT binary patch delta 99 zcmaERkmdD3mJN)l?BD)h{CDgBk;(a2syFYv`i7C|(ErWLuUBy~9sIxfZDFb)*nRpA8~_qBvtable->m42_Get_Overlays (tile, __, 0) & TILE_FLAG_RAILROAD) != 0) { + SW_NE += 4; + NW_SE += 4; + N_S += 4; + W_E += 4; + } + bool bridge_n = tile_has_district_at (tile_x, tile_y - 2, BRIDGE_DISTRICT_ID); bool bridge_s = tile_has_district_at (tile_x, tile_y + 2, BRIDGE_DISTRICT_ID); bool bridge_w = tile_has_district_at (tile_x - 2, tile_y, BRIDGE_DISTRICT_ID); From 7a275412e7cbd16daeb0f0623be23ddf623dba76 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 16:28:03 -0800 Subject: [PATCH 292/356] Add Municipal District art placeholder; Validate by-building render strategy; Fix bridge max_buildings (for raildroads; Add buttons and update configs for Water Park & Municipal District --- Art/Districts/1200/MunicipalDistrict.PCX | Bin 0 -> 7818 bytes Art/Districts/WorkerDistrictButtonsAlpha.pcx | Bin 17960 -> 19148 bytes .../WorkerDistrictButtonsHighlighted.pcx | Bin 25087 -> 26517 bytes Art/Districts/WorkerDistrictButtonsNorm.pcx | Bin 38364 -> 40565 bytes .../WorkerDistrictButtonsRollover.pcx | Bin 35834 -> 37981 bytes C3X.h | 2 +- 6 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 Art/Districts/1200/MunicipalDistrict.PCX diff --git a/Art/Districts/1200/MunicipalDistrict.PCX b/Art/Districts/1200/MunicipalDistrict.PCX new file mode 100644 index 0000000000000000000000000000000000000000..ebeefb36f2c4a0d22bb7b5eba1382f850d07fff2 GIT binary patch literal 7818 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 UC+~$FDJiM^rzhplJmP=<1AJSo3d; zVEHvMx&SxExKJYtABkYa_&`viu4EzZTxp3bS9RKRry(XLx?qyY{pQ?r@67qiVF_|!FGP9}KWbb2Lc2td{lWRi}aT@gxOUr^ONa)QBJJPoa+_7+O9!Z;=tJ@{jz{|WY%1O ze}N5s_H#(v+u&MS2&d456>0$|)p1O%lVEhjWx23H>_U&mj#2T%fYu7Xwh+OhDB86W z?l~p+yxTA%^Nf~9x~K*IVgZfC5p?OyxL6#*vtpfmZypGlAZvtHw;5Zd5C+`>dZb;s z^l2#$!6IR!C5A*;!eprtclAa*w6r6ww`E_(ucmB!$VTNvQ))sA#PG}o-*b-%ewrJ5XC(oR{;))aE>nZ{D7yIzmpohaYfNrA>&kC+!%UF&6<6(?SI=qm3u`4;zWb$L);PGCwEJ>(Df$$kUVnm!2Hj5&xeS_Uf#hbbAUR?iU9 z2NBmt;5LNF7bV+Waz)7$!j6Q479`8Iv0YxoA4SlB?@kkTt4@)`gg|vb;WGq`|s)j*T%W#o0L)oZhQ|&5=3u%1S{4s8f{})`nqk8exdaa z9!o{|)Y?shuOs&2fuS$V*(H8oi;s@f5vU^8opNg3woewbY`@EaC$6r2_FmyV76k^F z3va0SUzF2r4EHFsy_v!@>Uet?P3|!)x`(o52Rin|8y$ySU#7-FC;y(oImf> BTlxS1 diff --git a/Art/Districts/WorkerDistrictButtonsHighlighted.pcx b/Art/Districts/WorkerDistrictButtonsHighlighted.pcx index 5e02e90c1ad90cfda279b98239c96fd022de215b..acbed31490e8fb1ad89a43bacc5b68a393b93b45 100644 GIT binary patch delta 2028 zcmZ{kX>1i$6vt<2-|cOCof!vON?C2G1=_Need`7kgtC#ohmVVj1F-1 z$B}KYQ*S^)3HQCU~@w>Q5?7F9$7A{&Lwcx>JTSa>Cjq^3} z(|gY$eQ0z~5*PJ(6B&l?K2697ywJA}`)I*FZscUakdzX%qN=OhRCqe2*rFa=wYX4A zf@C2RKFH04pHqr3whhLo)@9;M4DFoOuAkG+=$hVvcddr@t)bmEe7(m|Ums*@1LXAE z3w62;;eOA#}$~)Wzx_>jxQ_^^GWV<7c=^UdNOp5qCo08qSLjcp4om{+>O zfgM9{3=4%Zv&C$VJ93LFF*nG`F>eI3N)d)f@4KAAFiI$)N-dliDw7NrR>NSH;4H>{ zSdj5U7Hi>xafVwljCP`^69xtgEQpH;7A0Ji=4IZ=Vo}sF+#gpQZaa}6r_y@tKk1-h z-NQ)gGa8GHC83fT=tLcp`0PC9VN#LRon+6*F;=4*q1X+VXqFE_B`Bm20XdFjx&rjTYYpTUx>usK=&F)t-sHTVdwe(2;#A z*E38U8diq8;4@KyYIe38q7~+$+*IQWV*&8W@Rkw2q2Vr6A&wR^Dz&A&J`XE3J_2mm z^cYl)INb*q%r@RCxaGKU%82oH$Q*e94vlp2z7^!iw+BWH9V?9SDDE#U_OB^%$ijzp zIrY$%AO0<_Otm#~dZ@9DAHtI4(v^%?7`(7}ygh zy-W6`NkLwi{1MruQ$8mCJw=^~5>8HSCT^Lg&PoZX)88O|GJOwm{)}D3i!-(nYiCvy zkIgJ2vVw<+n+huWV!WY!ul4Badr8;Su_NJ3K`?>r!a(_A;T9FKyJ%AoSo9RJtLQmm zeewIm!6nCtUzMC7HqB}$uAUtXWW=0c5Lf5;GMMV35ZbzeaY&l4pq*mTVvvFI`C7y)+oi z^`&$FXArZ>vq-tIypVXPd>Zk`@=3%Y%ZiC>dwFnKFu1>#1%q2tQAg{Y6*NF7s;npb z!^(}sfy;weZ2R&WvcK!KXRioexx*`h`2JN}Y5nCYir-zmp3L>tn~7gm2kDAdK1BAx om8*%YW({#e&4a{PO%*Ytwt%>{Hiy_)JDC`*%~x#xv(_~K0prf_1poj5 delta 784 zcmZXQeM}5s9LMjutB{hyU19Q4iIVFTiRwBjZ#gerb<*8+6-(3Hye}`y`yaM^m|-H< zhAYgDwq|YT5=9n^v6+|j2U{p(Q)bKKZ@*{1&z?V?=llEYccx1Ww~7E?Z!uCKD@|Nd zWGS+EZj190Iw9m`Va4&7=kUt;|T z)Rc+G?A26=PWm)c%JbO+76 zd%T?2LEZc=>#MujZ*Zeq`6m7Z_P?D#2x{V8#YJ=QdR0S6I%T!cuw*3(446O5?^d)I}8>DcZqsQ6<~Vn|N#9 pN{*$R7K@EAtA%4$D_^Zm=qd1L$;%Gu`3ozLdT&HX*)nCgjRvrKV diff --git a/Art/Districts/WorkerDistrictButtonsNorm.pcx b/Art/Districts/WorkerDistrictButtonsNorm.pcx index 8cc43113db7ec01c18edc43845ced44e14ad7a6b..c2eaeb8937e4f8ba3295640918403a3bf051d1bb 100644 GIT binary patch delta 2640 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 aKiH4lj(z-s;rL%|d*j4A+}x{E3;zeulDP2z delta 1082 zcmXAp4@{JG7{~89@iK@`6f6l8ghP!q;ii__WTzw`2S`9v{E-k0g&QITk;ET4K|O=x zp`kX{Sctc|W${e6Sj}Dp0r^v|GRjy3r?gg68%yIf`~I%?_I^IU_qX5O`+L66d+&rx zKijT1de5bKIvg?du`W&*$9c|ZFR~C}u+dp-HuSo6+Jj+UYD#8rvdtY)mpp0O#W$#f zu5hFNZyJ=^IWH3p$dFeG(ParTBoXyX$zywo`sDh&x0PP;E+@JqS9}_XdS!<%u6kwg z$r>U}66TjE_4t)4ty-9;@4rG&gW09zI*^@mDR{!SpB#> zJb^}73tw%xk3!v^x}9Sy*k($QUrdh^2QU9ewylUIxw3(Bw-K6f3TYaY77--zRyMr{&c&i^= zQse@Mif)13dmfVZNjIJ>P0kmi6ir4;LY3~?B9sD4O-h!1F-rEbNF~|7O6f*kvG9swij z_JVD7Dd2S78(?gG`~pptblaV%YP$P>O`Y{IG{)2Q1*kgtU>g2CT?dPh#)5_>By*~v z77VdFkkvW66S-|U)Q%)44xI+=jV_gNQ!CitbR67yxE)=XJ=_L5k2HeM9IXLQ9kqid zhnhL(P%}}->aqU9u_}zsAC!SE4}+VFF?KYUfDf8;!JL+@;OUkq@TZm+!2qYl3$K7D P@H)8a4Dpia)=2$-3Z>HT diff --git a/C3X.h b/C3X.h index 7088ca7d..71d6bcb5 100644 --- a/C3X.h +++ b/C3X.h @@ -877,7 +877,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {"Industrialization"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"Bridge.pcx"}, .dependent_improvements = {0}, .custom_width = 176, .custom_height = 112, .y_offset = 24, .x_offset = 0, .buildable_square_types_mask = (1 << SQ_Coast), .auto_add_road = true, - .img_path_count = 1, .max_building_index = 3, .btn_tile_sheet_column = 7, .btn_tile_sheet_row = 0, + .img_path_count = 1, .max_building_index = 7, .btn_tile_sheet_column = 7, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, From 681ed28e3a6ff472f48d9893e85cc29ae57f8df4 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 16:28:54 -0800 Subject: [PATCH 293/356] Update default config for water park and municipal district --- default.districts_config.txt | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/default.districts_config.txt b/default.districts_config.txt index 22a2bd83..34f8a3c3 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -268,7 +268,7 @@ name = Water Park tooltip = Build Water Park img_paths = WaterPark.pcx btn_tile_sheet_row = 1 -btn_tile_sheet_column = 9 +btn_tile_sheet_column = 10 vary_img_by_era = 0 vary_img_by_culture = 0 advance_prereqs = Miniaturization @@ -284,6 +284,27 @@ gold_bonus = 4 shield_bonus = -2 happiness_bonus = 2 +#District +name = Municipal District +tooltip = Build Municipal District +img_paths = MunicipalDistrict.pcx +render_strategy = by-building +btn_tile_sheet_row = 1 +btn_tile_sheet_column = 11 +vary_img_by_era = 0 +vary_img_by_culture = 0 +advance_prereqs = Code of Laws +dependent_improvs = Courthouse, Police Station, Hospital +buildable_on = desert,plains,grassland,tundra,floodplain,hills +defense_bonus_percent = 0 +allow_multiple = 1 +culture_bonus = 2 +science_bonus = 0 +food_bonus = 0 +gold_bonus = 0 +shield_bonus = 0 +happiness_bonus = 0 + [========================================================================] [=========================SPECIAL DISTRICTS==============================] [========================================================================] From 598d5b1bb37cdb28ffc51b7eaf1ef6a1cd7148be Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 17:56:14 -0800 Subject: [PATCH 294/356] Refactor and simplify draw_district_on_tile --- Text/c3x-labels.txt | 4 +- injected_code.c | 153 +++++++++++++++++++++++++------------------- 2 files changed, 89 insertions(+), 68 deletions(-) diff --git a/Text/c3x-labels.txt b/Text/c3x-labels.txt index dbb7fb63..49ece465 100644 --- a/Text/c3x-labels.txt +++ b/Text/c3x-labels.txt @@ -134,9 +134,9 @@ District ID is in the save file but missing in the current configuration. in the save file in the current configuration is called The save file had -total district types but current configuration has only +total district types but current configuration has Warning! This save file was created with a different districts configuration. The game may not function correctly. Error: -There may be other errors as well. +. There may be other errors as well. Districts in save file Currently configured districts from diff --git a/injected_code.c b/injected_code.c index 60faf8d7..35d9a796 100644 --- a/injected_code.c +++ b/injected_code.c @@ -31633,6 +31633,45 @@ draw_district_generated_resource_on_tile (Map_Renderer * this, Tile * tile, stru } } +int +count_completed_buildings_in_district_radius (int tile_x, int tile_y, int district_id) +{ + struct district_config const * cfg = &is->district_configs[district_id]; + struct district_infos * district_info = &is->district_infos[district_id]; + int completed_count = 0; + for (int i = 0; i < district_info->dependent_building_count; i++) { + int building_id = district_info->dependent_building_ids[i]; + if ((building_id >= 0) && tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, district_id, building_id)) + completed_count++; + } + return completed_count; +} + +void +draw_district_on_map_or_canvas_by_buildings (Sprite * base_sprite, Map_Renderer * map_renderer, int district_id, int variant, int era, int tile_x, int tile_y, int draw_x, int draw_y) +{ + struct district_config const * cfg = &is->district_configs[district_id]; + struct district_infos * district_info = &is->district_infos[district_id]; + int max_stage = cfg->max_building_index; + int stage_limit = ARRAY_LEN (is->district_img_sets[0].imgs[0][0]) - 1; + + if (max_stage > stage_limit) + max_stage = stage_limit; + + draw_district_on_map_or_canvas(base_sprite, map_renderer, draw_x, draw_y); + + for (int i = 0; i < district_info->dependent_building_count; i++) { + int building_id = district_info->dependent_building_ids[i]; + int stage = i + 1; + if (stage > max_stage) + break; + if ((building_id >= 0) && tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, district_id, building_id)) { + Sprite * district_sprite = &is->district_img_sets[district_id].imgs[variant][era][stage]; + draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); + } + } +} + void draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instance * inst, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int visible_to_civ_id) { @@ -31677,11 +31716,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc int draw_pixel_x = pixel_x; int draw_pixel_y = pixel_y; Sprite * district_sprite; - - // Handle river alignment, if district allows it enum direction river_dir = DIR_ZERO; - //if (district_allows_river(cfg)) - //align_district_with_river (tile, &pixel_x, &pixel_y, &river_dir); if (territory_owner_id > 0) { Leader * leader = &leaders[territory_owner_id]; @@ -31702,21 +31737,22 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc return; } + // If out of a territory but builder is known, use builder's era + if (territory_owner_id < 0 && inst->built_by_civ_id >= 0) { + Leader * builder = &leaders[inst->built_by_civ_id]; + culture = p_bic_data->Races[builder->RaceID].CultureGroupID; + if (cfg->vary_img_by_era) + era = builder->Era; + } + + int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; + int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; + int offset_x = draw_pixel_x + cfg->x_offset; + int offset_y = draw_pixel_y + cfg->y_offset; + int draw_x = offset_x - ((sprite_width - 128) / 2); + int draw_y = offset_y - (sprite_height - 64); + switch (district_id) { - case NEIGHBORHOOD_DISTRICT_ID: - { - unsigned v = (unsigned)tile_x * 0x9E3779B1u + (unsigned)tile_y * 0x85EBCA6Bu; - v ^= v >> 16; - v *= 0x7FEB352Du; - v ^= v >> 15; - v *= 0x846CA68Bu; - v ^= v >> 16; - buildings = clamp(0, 3, (int)(v & 3u)); /* final 0..3 */ - variant = culture; - break; - } - case DISTRIBUTION_HUB_DISTRICT_ID: - break; case WONDER_DISTRICT_ID: { if (! is->current_config.enable_wonder_districts) @@ -31765,22 +31801,39 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc } break; } + case NEIGHBORHOOD_DISTRICT_ID: + { + unsigned v = (unsigned)tile_x * 0x9E3779B1u + (unsigned)tile_y * 0x85EBCA6Bu; + v ^= v >> 16; + v *= 0x7FEB352Du; + v ^= v >> 15; + v *= 0x846CA68Bu; + v ^= v >> 16; + buildings = clamp(0, 3, (int)(v & 3u)); /* final 0..3 */ + variant = culture; + district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; + draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); + return; + } + case DISTRIBUTION_HUB_DISTRICT_ID: + draw_district_on_map_or_canvas(&is->district_img_sets[district_id].imgs[variant][era][buildings], map_renderer, draw_x, draw_y); + return; case ENERGY_GRID_DISTRICT_ID: { // Energy grids can have multiple buildings that - unlike most district buildings - don't have each other as prereqs (e.g., Coal Plant & Nuclear Plant), // and thus have a combinatorial number of images based on which power plants are built in their radius. buildings = get_energy_grid_image_index (tile_x, tile_y); - break; + draw_district_on_map_or_canvas(&is->district_img_sets[district_id].imgs[variant][era][buildings], map_renderer, draw_x, draw_y); + return; } case BRIDGE_DISTRICT_ID: { buildings = get_bridge_image_index (tile, tile_x, tile_y); - era = 3; // DEBUG - break; + draw_district_on_map_or_canvas(&is->district_img_sets[district_id].imgs[variant][era][buildings], map_renderer, draw_x, draw_y); + return; } case CANAL_DISTRICT_ID: { - era = 3; // DEBUG draw_canal_district (tile, tile_x, tile_y, map_renderer, pixel_x, pixel_y, era); return; } @@ -31791,53 +31844,21 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc } default: { - if (cfg->render_strategy == DRS_BY_BUILDING) - break; - - int completed_count = 0; - for (int i = 0; i < district_info->dependent_building_count; i++) { - int building_id = district_info->dependent_building_ids[i]; - if ((building_id >= 0) && tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, district_id, building_id)) - completed_count++; - } - buildings = completed_count; - break; - } - } - - int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; - int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; - int offset_x = draw_pixel_x + cfg->x_offset; - int offset_y = draw_pixel_y + cfg->y_offset; - int draw_x = offset_x - ((sprite_width - 128) / 2); - int draw_y = offset_y - (sprite_height - 64); - - if (cfg->render_strategy == DRS_BY_BUILDING) { - int max_stage = cfg->max_building_index; - int stage_limit = ARRAY_LEN (is->district_img_sets[0].imgs[0][0]) - 1; - - if (max_stage > stage_limit) - max_stage = stage_limit; - - district_sprite = &is->district_img_sets[district_id].imgs[variant][era][0]; - draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); - - for (int i = 0; i < district_info->dependent_building_count; i++) { - int building_id = district_info->dependent_building_ids[i]; - int stage = i + 1; - if (stage > max_stage) - break; - if ((building_id >= 0) && tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, district_id, building_id)) { - district_sprite = &is->district_img_sets[district_id].imgs[variant][era][stage]; + // Draw by counting number of completed buildings in radius + if (cfg->render_strategy == DRS_BY_COUNT) { + buildings = count_completed_buildings_in_district_radius (tile_x, tile_y, district_id); + district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); + return; } - } - return; + // Draw by checking each building individually and layering images + else if (cfg->render_strategy == DRS_BY_BUILDING) { + Sprite * base_sprite = &is->district_img_sets[district_id].imgs[variant][era][0]; + draw_district_on_map_or_canvas_by_buildings(base_sprite, map_renderer, district_id, variant, era, tile_x, tile_y, draw_x, draw_y); + return; + } + } } - - district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; - draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); - return; } Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); From 973d14df2aed1611753e3f3ce6c96a796c309a15 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 17:58:26 -0800 Subject: [PATCH 295/356] Fix error message --- Text/c3x-labels.txt | 2 +- injected_code.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Text/c3x-labels.txt b/Text/c3x-labels.txt index 49ece465..7ea058ee 100644 --- a/Text/c3x-labels.txt +++ b/Text/c3x-labels.txt @@ -136,7 +136,7 @@ in the save file in the current configuration is called The save file had total district types but current configuration has Warning! This save file was created with a different districts configuration. The game may not function correctly. Error: -. There may be other errors as well. +There may be other errors as well. Districts in save file Currently configured districts from diff --git a/injected_code.c b/injected_code.c index 35d9a796..20e7a301 100644 --- a/injected_code.c +++ b/injected_code.c @@ -28628,7 +28628,7 @@ patch_move_game_data (byte * buffer, bool save_else_load) char s[1000]; snprintf (s, sizeof s, "%s %s", is->c3x_labels[CL_WARNING_DISTRICTS_CONFIG_MISMATCH], first_mismatch); - snprintf (s, sizeof s, "%s %s", s, is->c3x_labels[CL_MAY_BE_OTHER_ERRORS_AS_WELL]); + snprintf (s, sizeof s, "%s. %s", s, is->c3x_labels[CL_MAY_BE_OTHER_ERRORS_AS_WELL]); s[(sizeof s) - 1] = '\0'; PopupForm_add_text (popup, __, s, 0); From 7bfc1ee0f8ed85f4d2021522367385d1f4c18bc2 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 22:37:58 -0800 Subject: [PATCH 296/356] Resolve more merge issues --- civ_prog_objects.csv | 1 - injected_code.c | 44 ++++++++++++++++++++------------------------ 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index d33458e7..8947f1f7 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -895,7 +895,6 @@ ignore, 0x558F70, 0x0, 0x0, "Leader_is_enemy_unit", "char (__fastcall *) ignore, 0x4BFD40, 0x0, 0x0, "City_get_turns_to_build", "int (__fastcall *) (City * this, int edx, int order_type, int order_id, char param_3)" ignore, 0x4E3D90, 0x0, 0x0, "Main_Screen_Form_is_unit_hidden_from_player", "bool (__fastcall *) (Main_Screen_Form * this, int edx, Unit * unit)" ignore, 0x4E8850, 0x4F14E0, 0x4E8910, "Main_Screen_Form_open_right_click_unit_menu", "void (__fastcall *) (Main_Screen_Form * this, int edx, int tile_x, int tile_y, int mouse_x, int mouse_y)" -inlead, 0x4DBA70, 0x0, 0x0, "Main_Screen_Form_set_selected_unit", "void (__fastcall *) (Main_Screen_Form * this, int edx, Unit * unit, bool param_2)" ignore, 0x5CCBB0, 0x0, 0x0, "Unit_can_pass_between", "PassBetweenValidity (__fastcall *) (Unit * this, int edx, int from_x, int from_y, int to_x, int to_y, byte param_5)" ignore, 0x452510, 0x454600, 0x452590, "ai_move_defensive_unit", "void (__fastcall *) (Unit * this)" ignore, 0x5E78E0, 0x5F7130, 0x0, "General_load", "void (__fastcall *) (General * this, int edx, byte ** buffer)" diff --git a/injected_code.c b/injected_code.c index edd32f8a..988e6120 100644 --- a/injected_code.c +++ b/injected_code.c @@ -32920,30 +32920,6 @@ patch_Unit_select (Unit * this) Unit_select (this); } -void __fastcall -patch_Main_Screen_Form_set_selected_unit (Main_Screen_Form * this, int edx, Unit * unit, bool param_2) -{ - bool redraw = false; - if (is->current_config.show_ai_city_location_desirability_if_settler) { - int new_perspective = -1; - if (unit != NULL) { - int unit_type_id = unit->Body.UnitTypeID; - int worker_actions = p_bic_data->UnitTypes[unit_type_id].Worker_Actions; - new_perspective = (worker_actions >= 1 && (worker_actions & (UCV_Build_City)) && !is_worker(unit)) ? p_main_screen_form->Player_CivID : -1; - } - - if (new_perspective != is->city_loc_display_perspective) { - is->city_loc_display_perspective = new_perspective; - redraw = true; - } - } - - Main_Screen_Form_set_selected_unit (this, __, unit, param_2); - - if (redraw && ! this->is_now_loading_game) - p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); -} - void __fastcall patch_City_Form_draw_food_income_icons (City_Form * this) { @@ -33802,6 +33778,8 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t return PBV_OK; } } + } + if (is->current_config.enable_districts && is->current_config.enable_canal_districts && base != PBV_OK && @@ -34644,6 +34622,21 @@ patch_Main_Screen_Form_set_selected_unit (Main_Screen_Form * this, int edx, Unit if (is->current_config.unit_cycle_search_criteria != UCSC_STANDARD) clear_selectable_units_list (this, false); + bool redraw = false; + if (is->current_config.show_ai_city_location_desirability_if_settler) { + int new_perspective = -1; + if (unit != NULL) { + int unit_type_id = unit->Body.UnitTypeID; + int worker_actions = p_bic_data->UnitTypes[unit_type_id].Worker_Actions; + new_perspective = (worker_actions >= 1 && (worker_actions & (UCV_Build_City)) && !is_worker(unit)) ? p_main_screen_form->Player_CivID : -1; + } + + if (new_perspective != is->city_loc_display_perspective) { + is->city_loc_display_perspective = new_perspective; + redraw = true; + } + } + Main_Screen_Form_set_selected_unit (this, __, unit, param_2); // If selecting a new unit, must insert it into the list to ensure it's actually selected. @@ -34651,6 +34644,9 @@ patch_Main_Screen_Form_set_selected_unit (Main_Screen_Form * this, int edx, Unit UnitIDList_insert_before (&this->selectable_units, __, unit->Body.ID, NULL); this->unit_cycle_cursor = this->selectable_units.first; } + + if (redraw && ! this->is_now_loading_game) + p_main_screen_form->vtable->m73_call_m22_Draw ((Base_Form *)p_main_screen_form); } Unit * __fastcall From d854b8692a68a6e886a3d3acfd7bb3a88273f230 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 22:54:17 -0800 Subject: [PATCH 297/356] Add enable_energy_grid_districts flag --- C3X.h | 1 + default.c3x_config.ini | 1 + injected_code.c | 75 ++++++++++++++++++++++-------------------- 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/C3X.h b/C3X.h index eaa92f84..88aa8303 100644 --- a/C3X.h +++ b/C3X.h @@ -394,6 +394,7 @@ struct c3x_config { bool enable_bridge_districts; bool enable_canal_districts; bool enable_central_rail_hub_districts; + bool enable_energy_grid_districts; bool enable_great_wall_districts; bool cities_with_mutual_district_receive_buildings; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 5e1eac9e..102d3c69 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -917,6 +917,7 @@ enable_port_districts = false enable_bridge_districts = false enable_canal_districts = false enable_central_rail_hub_districts = false +enable_energy_grid_districts = false enable_great_wall_districts = false ; When multiple cities share a district (i.e., the same district tile is within multiple cities' work radii), these options control whether those diff --git a/injected_code.c b/injected_code.c index 988e6120..b9c186a2 100644 --- a/injected_code.c +++ b/injected_code.c @@ -12037,6 +12037,7 @@ can_build_district_on_tile (Tile * tile, int district_id, int civ_id) if ((cfg->command == UCV_Build_Port) && !is->current_config.enable_port_districts) return false; if ((cfg->command == UCV_Build_Bridge) && !is->current_config.enable_bridge_districts) return false; if ((cfg->command == UCV_Build_CentralRailHub) && !is->current_config.enable_central_rail_hub_districts) return false; + if ((cfg->command == UCV_Build_EnergyGrid) && !is->current_config.enable_energy_grid_districts) return false; if ((cfg->command == UCV_Build_GreatWall) && !is->current_config.enable_great_wall_districts) return false; if (! district_is_buildable_on_square_type (cfg, tile)) @@ -16620,6 +16621,7 @@ patch_init_floating_point () {"enable_bridge_districts" , false, offsetof (struct c3x_config, enable_bridge_districts)}, {"enable_canal_districts" , false, offsetof (struct c3x_config, enable_canal_districts)}, {"enable_central_rail_hub_districts" , false, offsetof (struct c3x_config, enable_central_rail_hub_districts)}, + {"enable_energy_grid_districts" , false, offsetof (struct c3x_config, enable_energy_grid_districts)}, {"enable_great_wall_districts" , false, offsetof (struct c3x_config, enable_great_wall_districts)}, {"completed_wonder_districts_can_be_destroyed" , false, offsetof (struct c3x_config, completed_wonder_districts_can_be_destroyed)}, {"destroyed_wonders_can_be_built_again" , false, offsetof (struct c3x_config, destroyed_wonders_can_be_built_again)}, @@ -31273,23 +31275,19 @@ align_district_with_river (Tile * tile, int * out_pixel_x, int * out_pixel_y, en return; int dx, dy; - bool under = false; - direction_to_offset (dir, &dx, &dy); - if (dy < 0) - under = true; - int offset = 36; + direction_to_offset (dir, &dx, &dy); - dx = 0, dy = 0; + dy = 0; switch (dir) { - case DIR_N: /* dx = 0; */ dy = -offset; break; - case DIR_NE: /* dx = offset; */ dy = -offset/2; break; - case DIR_E: /* dx = offset*2; */ dy = 0; break; - case DIR_SE: /* dx = offset; */ dy = offset/2; break; - case DIR_S: /* dx = 0; */ dy = offset; break; - case DIR_SW: /* dx = -offset; */ dy = offset/2; break; - case DIR_W: /* dx = -offset*2; */ dy = 0; break; - case DIR_NW: /* dx = -offset; */ dy = -offset/2; break; + case DIR_N: dy = -offset; break; + case DIR_NE: dy = -offset/2; break; + case DIR_E: dy = 0; break; + case DIR_SE: dy = offset/2; break; + case DIR_S: dy = offset; break; + case DIR_SW: dy = offset/2; break; + case DIR_W: dy = 0; break; + case DIR_NW: dy = -offset/2; break; default: break; } @@ -31566,7 +31564,7 @@ wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner // We only care about the nearest same-civ city in the work area around the tile. // Assumes the base wonder art (img_row/column) faces west and the alt art faces east. // To "face away" from the nearest city, we pick the alt art when that city lies to the east. - Tile * center = tile_at (tile_x, tile_y); + Tile * center = is->current_render_tile; if ((center == NULL) || (center == p_null_tile)) return false; @@ -31771,8 +31769,8 @@ get_bridge_image_index (Tile * tile, int tile_x, int tile_y) bool se_link = se_land || bridge_se; bool sw_link = sw_land || bridge_sw; - if (sw_link && ne_link) return SW_NE; - if (nw_link && se_link) return NW_SE; + if (sw_link && ne_link) return SW_NE; + if (nw_link && se_link) return NW_SE; if (north_link && south_link) return N_S; if (west_link && east_link) return W_E; @@ -31781,7 +31779,7 @@ get_bridge_image_index (Tile * tile, int tile_x, int tile_y) if (north_link || south_link) return N_S; if (west_link || east_link) return W_E; - return 0; + return SW_NE; } void @@ -31806,10 +31804,10 @@ get_bridge_directions (Tile * tile, int tile_x, int tile_y, int * out_dir1, int void get_canal_directions (Tile * tile, int tile_x, int tile_y, bool out_water_dirs[9], int * out_dir1, int * out_dir2) { - bool canal_at_n = tile_has_district_at (tile_x, tile_y - 2, CANAL_DISTRICT_ID); - bool canal_at_s = tile_has_district_at (tile_x, tile_y + 2, CANAL_DISTRICT_ID); - bool canal_at_w = tile_has_district_at (tile_x - 2, tile_y, CANAL_DISTRICT_ID); - bool canal_at_e = tile_has_district_at (tile_x + 2, tile_y, CANAL_DISTRICT_ID); + bool canal_at_n = tile_has_district_at (tile_x, tile_y - 2, CANAL_DISTRICT_ID); + bool canal_at_s = tile_has_district_at (tile_x, tile_y + 2, CANAL_DISTRICT_ID); + bool canal_at_w = tile_has_district_at (tile_x - 2, tile_y, CANAL_DISTRICT_ID); + bool canal_at_e = tile_has_district_at (tile_x + 2, tile_y, CANAL_DISTRICT_ID); bool canal_at_ne = tile_has_district_at (tile_x + 1, tile_y - 1, CANAL_DISTRICT_ID); bool canal_at_nw = tile_has_district_at (tile_x - 1, tile_y - 1, CANAL_DISTRICT_ID); bool canal_at_se = tile_has_district_at (tile_x + 1, tile_y + 1, CANAL_DISTRICT_ID); @@ -31831,15 +31829,6 @@ get_canal_directions (Tile * tile, int tile_x, int tile_y, bool out_water_dirs[9 bool canal_or_water_se = canal_at_se || water_se; bool canal_or_water_sw = canal_at_sw || water_sw; - char ss[200]; - snprintf (ss, sizeof(ss), "Canal at (%d,%d), canal tiles: N:%d NE:%d E:%d SE:%d S:%d SW:%d W:%d NW:%d; water tiles: N:%d NE:%d E:%d SE:%d S:%d SW:%d W:%d NW:%d\n", - tile_x, tile_y, - canal_at_n, canal_at_ne, canal_at_e, canal_at_se, - canal_at_s, canal_at_sw, canal_at_w, canal_at_nw, - water_n, water_ne, water_e, water_se, - water_s, water_sw, water_w, water_nw); - (*p_OutputDebugStringA)(ss); - bool canal_dirs[9] = { false, canal_at_ne, canal_at_e, canal_at_se, canal_at_s, canal_at_sw, canal_at_w, canal_at_nw, canal_at_n @@ -31936,9 +31925,6 @@ get_canal_directions (Tile * tile, int tile_x, int tile_y, bool out_water_dirs[9 if (draw_dir1 == DIR_NW && draw_dir2 == DIR_NE && canal_dirs[DIR_NW] && water_dirs[DIR_SE]) { draw_dir2 = DIR_SE; } if (draw_dir1 == DIR_NW && draw_dir2 == DIR_S && canal_dirs[DIR_NW] && water_dirs[DIR_S]) { draw_dir2 = DIR_SE; } - snprintf (ss, sizeof(ss), "Drawing canal at (%d,%d) dirs:%d,%d\n", tile_x, tile_y, draw_dir1, draw_dir2); - (*p_OutputDebugStringA)(ss); - *out_dir1 = draw_dir1; *out_dir2 = draw_dir2; for (int i = 0; i < 9; i++) @@ -32276,6 +32262,9 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc } case NEIGHBORHOOD_DISTRICT_ID: { + if (! is->current_config.enable_neighborhood_districts) + return; + unsigned v = (unsigned)tile_x * 0x9E3779B1u + (unsigned)tile_y * 0x85EBCA6Bu; v ^= v >> 16; v *= 0x7FEB352Du; @@ -32284,15 +32273,20 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc v ^= v >> 16; buildings = clamp(0, 3, (int)(v & 3u)); /* final 0..3 */ variant = culture; - district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; - draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); + draw_district_on_map_or_canvas(&is->district_img_sets[district_id].imgs[variant][era][buildings], map_renderer, draw_x, draw_y); return; } case DISTRIBUTION_HUB_DISTRICT_ID: + if (! is->current_config.enable_distribution_hub_districts) + return; + draw_district_on_map_or_canvas(&is->district_img_sets[district_id].imgs[variant][era][buildings], map_renderer, draw_x, draw_y); return; case ENERGY_GRID_DISTRICT_ID: { + if (! is->current_config.enable_energy_grid_districts) + return; + // Energy grids can have multiple buildings that - unlike most district buildings - don't have each other as prereqs (e.g., Coal Plant & Nuclear Plant), // and thus have a combinatorial number of images based on which power plants are built in their radius. buildings = get_energy_grid_image_index (tile_x, tile_y); @@ -32301,17 +32295,26 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc } case BRIDGE_DISTRICT_ID: { + if (! is->current_config.enable_bridge_districts) + return; + buildings = get_bridge_image_index (tile, tile_x, tile_y); draw_district_on_map_or_canvas(&is->district_img_sets[district_id].imgs[variant][era][buildings], map_renderer, draw_x, draw_y); return; } case CANAL_DISTRICT_ID: { + if (! is->current_config.enable_canal_districts) + return; + draw_canal_district (tile, tile_x, tile_y, map_renderer, pixel_x, pixel_y, era); return; } case GREAT_WALL_DISTRICT_ID: { + if (! is->current_config.enable_great_wall_districts) + return; + draw_great_wall_district (tile, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; } From bd5dc4b5577abfc77c4b519182e804ff42220115 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 23:02:44 -0800 Subject: [PATCH 298/356] Correct snow forest detection func --- injected_code.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/injected_code.c b/injected_code.c index b9c186a2..422fc77d 100644 --- a/injected_code.c +++ b/injected_code.c @@ -1884,7 +1884,8 @@ tile_has_snow_forest (Tile * tile) if (tile->vtable->m50_Get_Square_BaseType (tile) != SQ_Forest) return false; - return tile->vtable->m12_Check_Forest_Pines (tile) != 0; + int overlays = tile->vtable->m43_Get_field_30 (tile); + return (overlays & 0x100000) != 0; } bool From 767357fbffbc5986510919f114550fc21cbda0d0 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 29 Jan 2026 07:39:31 -0800 Subject: [PATCH 299/356] Update Water Park art --- Art/Districts/1200/Canal.PCX | Bin 73963 -> 115795 bytes Art/Districts/1200/WaterPark.pcx | Bin 8535 -> 8225 bytes injected_code.c | 59 ++++++++++++++++++------------- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/Art/Districts/1200/Canal.PCX b/Art/Districts/1200/Canal.PCX index 6cf25f8a6d88687e533627edda4769c775c5412a..4de393f8971c9fd1c0b276da7133f24167a9e889 100644 GIT binary patch delta 76 zcmaETkmYg*`-VEk&2@HPEH*EDuBgHM{@dAtzEnQe6=2A=>SCY-79gZfx!L5Zs5~@BVhqJzvf}|9+pH z=#7$xVPK0Lmgc@!TOAYN z*<~wCpK-y^6&Ln}NoqJ~#jC?q9Sl=@b=85s02A^0ed@9At#yONTCK3$;s9x_3}UAR zh+UIl(fd$@#t*=i+Zj{4;sQu!z2&3v%(-B)&pkF*xGd z5z_unUdfDPy}|;@xcE4UuaZ~(B9e18=Hf3&{0Di(;D~F#h^w;tlO75W4UD+>7%8gc zm7I~ByIQX{e{sFm8D+HoG<{4Ai-BX28e6WNIi0dmGt0$ut)(oBQ_gGca`CKNsDRzF zVf(3a^0A7;1)Z?80W!{HIQg7w6m{C;D!wn=U3{%!g?7e23Txyr6UF&t7WbYEW-ID z*6Puqi#Hu@JbL(ZtId^Uq%>H1QqSCaa8;Q}V*Gb#)}x`eg`>=!hIQ_A=-aCW!xSfS zMD(WPBn)*XQL{QppZ#5q(9);kaJEPq&ehx`Ws{VPI-OFyZIUu&DIyhljF#mT_9%<-lu6g|4!7kkU4aj&M*#H0l diff --git a/Art/Districts/1200/WaterPark.pcx b/Art/Districts/1200/WaterPark.pcx index cb54442877b998f72bb1245ef5950f72cf2470b1..81e2358a29845843eefc0d97f5f1e04b8173381c 100644 GIT binary patch literal 8225 zcmeHMc|4Wr+b>N@WSguhO9yGJsT>?eH0g|(_*oCxl8{Pd97=;CE#$SHQYKm?2hpM? z)O8$LBiFc|6a3eedtJ-uFFg`qY_I@Iy~^ zn=(bjcdGFEzxqs_BJ$tA^p^&D(x-9KZ<_x9JAbF7k?yDUH~#$&O+UhWl4Ae7H_Po` z?#*>;rX42Uzezu*C1))@(@f9$=Re3?hsc!cw7!AThKc`drk~MSu1brY(_yfY)-q7a z=S_T|iGD_LIa1IFgV0~xKwT0eN|E23Bov`(ivEXuO8ICK4vceI@DbYUsUpX@N|75< z>ha+Z)I>Q>by7MG*C`tgmo4bT{QI5)5ni2*&l1j(m##}d`l;VKe zOSmz!+;DCmoAITW)ZGd4ExtO-;f#QC2TIaG*-*+|GSbZ*>LnwAnk5EQEX$h-4KM{^(pic)>x+4FIqaAm7`lz`J*6H_ zkfZpvmfPZu=|D|Q@gYi6nL5y^0o^uG=dZkY)<$PB4N7K=1v+whNSQ0<;Jn65Io*a60Nq+J zOz6Uco^U@~9lC1iXp8LX)LL3VL0t&kg%3~*A8G0?)rZ4B`XYyfU^QVpL)BP$qZ<*M zN|fF9K(|>58rgX1?0JQEX z3@82^tVSX{)$G5o-gjBSWSWVefUZIGqo=mOM}jKg$>DSG494pzWVjZCf_j0vTEEEW zI3u{FR+FJ6)TJ~<%Gkhd2bwN3Koq*uVa$jZ4&!D7@4zbM0iL#%T0l(E48=yjiDN1vd z+>SYxKts4h8MK!qlyJ*z2+mFtx`F-~?wy`CBOB<`4Yb@o0iC=c5T`T_!>HpK`3O(9 zRH0i8!gv@r?=Xy?I}RVls5hvv>5F_qJ)lhCe!PoeCUi?`MW(o#xzAN{a5mTsA2QiE ztq;&q0uRdILjXG*8f+>nf}IM;fz!|h4+$L)bT!Zw=5=&V8q}u+E!egI21&au8g~?4 z2Is-}@xypEhEzX$OO2uChA*-gO-kOuLv@StfbG0+E+0n90?LT-;*@k4B>`sa0G_C9 zCX5qKxX>rq&|9@=GhC}ZN4De@q{6S!$%IaZUP32n*jO*KR^f6KLvXhBY#-!#grHr= zVbt~wMWI%~7x^1?pNfaRweT*%#V|m_a64N9x-~cfGE5G1z=#ayga#g4s4!eYal1@r z0Nx+Lk%kluRRrs|og#sGfno62z5?iU;yq>c{HXbwd%cr5-YS;bR+TVbpvao-dcNY9 zSEyD%3)TK9yV0#`;9VxPR-v!;xyWS(5Dhl64j%}UtH^+E1|RCShigDviG-pCk88#$ z!$|GmxK5jq>arxBbol{TSRKEJ8RKE{d>O{-nG09US4?tE+rLcZ(DVxLpUq|MrpJ0^ zC6A!i272so@>dMiZ1m7KWoV6O@FCAhG+fi{KqiN6rokkd-Z;aQq?_CGlAhF)j09{H5V?j;g$F5g`42!k=bA6lCZoW61FIm!SZG$ z6qi8j5UOsWg%tcLA7BQ72Ui0Z6u|qdTQCHLF+YYlMuV6oM&Is-Mos2+DJ~D*pxw&? zFf+G_ZmqpW&K;jW_l)uxc;Opy6`le)ek$ze{L-`<=&C;G3yrH+OecSARjCcLTsCi} z6~-Gs3mNV}-Z0{x_alH-`o-U5J9U$SrfplcVeWemZ^;(&{v5`|O$5@)=IJhi{u5@J z+a=9-Q11h6+clUl#9Il348t|S6CjR6DgVqBxOOf#55WwRr(w>-Ez(Zfq6!*UNKgMC z_yg(|%@%yUFmZ2yC6EH>#NyaNMxd_ZIihH%g^2!?_o-$I?pMR(LzpGTjvq!WpT2UT zUNJ3W8I0$xWNueAD}%P3=zv5Mqu~g7dpu~-a@KswI7g`?=g=m@@Fe#Taay`iOnHr% zLxxF2Mt~1DC#wwVa#-Fmu8GB!%L3t93HtE3(2vp0=|05pXo76TTm+zg&<{iRBaDY^ zv&77d{CvQqvBfGLO@8m?HPUwOwAIp&5mxsE zc?V$?if5bH1Jr2jJhb2wCZx)8skCW)d13ZZXo^>4Yif`(^w+5UGF5U10)b?eUMVFP z^i{WRhMT+;Jv9Y&3zKQ$>Jj3O`Ki$n=)8Pr%t|0xB!-d2P{g3?MjTkCu+G&%hdgxD z(_ILqcYE^bnJ#8g9*Fwt?b;tet7lcKZj zouDa~96y)4E$?bBH1ElGo0U=#p&%~0^8obiAo&W!B+ZW>&hz&wjp3r={>`&72?;5A zhKaWc0d+!0BmEGOJe?qKA`%6W2tNzYc+6mpI{`db*nYJ?WQMr9pS?-7x-dTN6^RRu zhWWE3UB|-2B6X2%5D>vrc!FNrlxyXjLh{8hzhCc7P|2bx((3`Opr~~b!y+*RncnV@!8TY;t>E$#hLqm_AIO>5rw3> zFrv^@kX@bz_p;@qiGsRza`90R91U5p%rY-9)L3$=<2pA7Hv_jw11$+d^%>?h*%8@= z(dLXa2H$q}Rv67A>klD>p<;9&TD&^C+#*`tFWW~|$DSxG(LSGW0&D8am<8XGYk7hG9;^#2&QX?tPg@#1~mFZ{Vn6rYwIVXscAMXd#Y{EtM0p*;~_6;YV&r+(16+D~mhdlmv(cZ+*q)?kopS>(ZTV?ioe{uJrEbzLatbC9_R@aYF0zNYAKU;M)WTW8PQI)T}mpdv;&dxn_07#-hU(y);!+}JmTyd)234$;|a7Z@5X(FC2 zoxRB_Fwe@LpOr{Fl9Pyk7BnUmLt_A!Lt?U$e6d?2G(kx81PKVKnbz}EGo9iYWgG88 zM5d?lY-|mUd@U;ZWDgn6hQ|tO3v=P!WZX}zUS~~i!c%=a61!GP(pccO zZ#Q4GBD`i{xKHJdD{z-TEu%bHRb=mOTUmD_jLauFS6EZ{ z&4|3O)R?kDQ31%l56S;Wn-ta3&Wv)l^kNYfZti zf*e_BwUxt;^tq&wN3zBWQg#H$34$GEb6_q(Xz(Er>+F;N19{1Q&0M#X{4-wS>d_W5 zM@cRAFHSQ|#LWsfEftc+)vS^#lYlo!U2Dyr6n8E^izUw>Mp>F=Fx2=jZr^}w=|VZR zgMLI>j%m9}ZRQo|+yhN%(4C9ZA9jok@kekE3S8hl>4mnMcyZAKTouelWc~6WcuH^; zl553WPM)lXK6n8G(37SdDLA7=riq(E-TbeEmoH>)Cu%dI<$Y}Yv!;+7G*BasgYay0 z92w~e7j)c3Sb9L~N%=#gFfb?Qh(>Ckxv5|3!E8^pY1-;0W||D7u^WlJ1~-pmVjlGf z*&e<+bSIj$fwWa#v=CjsPF5w5UcJcIPVy( zL|4M9B7+$|s!~8PyoKO+Hxb?BD$__^+NI!$T>XaG2(k;ag-iAanb4LA?e0-knc8M0 zM@!KjccE8LWhwlYbOL%(e%g^w*4V|`DW`aqy2ljcSz1GVuFwP9pfB#c6p{RvvFFO_ zb*n>HY@Qi79sO6{)Sk!_mOnaOU5*Ul`n!@iR~|IRu%JB+tL>YbR8u)k7Iv5H76duD zFkh$4zy1WV6&i4XP!0FVMKthl*nOQ4k@1k3t{uO8&!?R5N^j*(^+YuP0J>VsNjrV) zo?a6>V_2(c9JE+{QnL$?-;FarJ}ndZ8nW&tUP< z8Ag1BC4F+=cb)PuPk>fI7h3y0qCmS>;@1$gY7hzQOsORbCUF=s^}>`&RA+33m&hVw zj4Kk@8KbDAC&rZ}?=4I}IhsZcc~(}8zxh3cH@REBLG4QYgsTga=lj~X7&GwTqK{c( zI<%LIlNm)wTR-#)I=QMIXrm_R+bz&1vBYkT@)`rjQ(^m49KH0UgF@!vZcGdE3a=}4 zRf889q`2cuJK=5(sq$x(kq%WMz9>Z2fmtSv2d}ssS9cae-c_D6$7Agrd8VRAJHq>O zLGgKUabMWX|4O8ybKd!tVc|w3$eY0t{Nke7%Y_?(3$05gUGI9 zMBIgLwg&2Wh4J{hm^{T4L!kXat(`ux6U4?rAMt{|B$MM6}hDAo>XM3NG1 z4?%AwG=^FtwohUHF44=#mfZbG2Zs~RXo$2yhX>!rC;_L~mXlajR#SpnI_G#?B0uVS z{3!PX0Zv}U-=j_=WA`#N$@yKXTn-M@DMVLx9$NHFJ$dLp?a-ROUbxjI-0vE~Nwi?x z7Qs7=gBFbAmXpxS*4`9VAhQS&VEQ5N5{YWk<`s^l6d&)0POh#ikvGbMUOu9!L;pdT z{2ZbyTt-jc&}c_1UQYJd`^)#fGE5c`E--O~H0UqefY9DR<9tTk>7WAC{0wPn`NRWe zI(ShOyPLF}y|~FK9v)j09bpUnM#vNHMfKv8dU2Nxb+^EcQXgK9(HtXH3``a_QjIZL z{2ZaR{@l>{yt&g&bx}V|GZzWxXAz-?ck-Q?@`_}(?3!aUtRIC+kiVU$Qc6UI;oR z_9!Qj)B!=yg_Cf7H@rI_ZBxO=>iP=mt8k%>#;}UJ06l`=$)ii;5&TZ$T5a;aV;{8- zl|T0|CoXc@wx2wvAIH7}o#-#f)+dAm`cjKNS@xCC;Tqu;hABE-#fcqf&XV8BB|u{S z+CBYz{gir+4H^7SaPny<{4W<);s^*Ti-R2Zp@I953j8hHsTmQx!Xk^m8ceZx;fRlF zam2s&e?MP8#}fhpU4e=0BY9X=qLIT!5JEV(tB_NMR|qK_`gt&t=Hio4v+EN1{gQB; zzxJ#@Uk^}2*vdlvdGlhq;s8y;;|7%EaCrefzRXhi`I)ZyHIyybqa0Jq`HyKeze+pc?23atRG^sRbYC{l}@o=%ho7&&L>})ayw{ zA)gONUgMF@#1od!XD)B4_md87J|E+}$0MVOM?jwsg+5T@la7);A1;mm%X!r26DkQY zX{8zR26A#z(kjw6QnJQ!q6_9pC@v5;R92N>{4jfgr^Yv8OLXRXsf*7wTD`!?#Kcf$ zse#VQZ&aOhWYwAay2~{5ml+zeWUQ7gRAO=Gu|nptc5PcadxsIjnPtIRX>4K3uwpAX z*chDtW`^x|)^^{SEM^C;w^CbYy<{cZG0IZwhZWm&ovrnpT(z7-be;CP+w0mme!uWP zImS-0oFC?ge=pCoRI+!I@o=$J8;zAk@#+GJKW!=3UX=e*Pi4%R@@$#+fm<9B~cWo9d&T=^nUj z^R>|6W$S|dzYi_&503ageBIU!9^o6c9k*<=`0>ZEpn#Cj)oZsp1aH;Y6y_bjVb_i= z&ezu}Rj*h1F|6?72HmUcmKSfd{4VZZMnvH8Z3aiS*}d=;zqw&aex&){81L9!hQ9{M z{bzTbd3xNDD6c)SAyG-ockSGEaHspmgbJqvc^L^y4#n&Y*n2c7X;s4RaOdUNBi#OJwJIlQ0>sOe)QMlesjm>`eL@e&s2b zs^c4jFXyM7-Lvo0ff_J8Dg59e*V!cRF}ar9cr zu1lr4>>GpJs^KT6{eCTu8ZNO8?J$pNcyz1$LI&LHJbS4AT1I_sXhXfnotmsV$ZM%N z)^;tTr0H1V{iYTu<+tp+b|d)_*`I&=_N~VKef7?bH!fs9sQCyRs&C#L6GXqOOFY+6 z-E-?~R?kTL-J&NgCrci^cz-i*taJanUiMH|_4{A*2YZ^Y4Gi`CerxD?cKhJa;84l? Wfzq-5D=+^Lj1Auur>D|WsQ(3}uz5@X literal 8535 zcmb_hd012D(np0}5n2_n0@naiK#1T)DG0Nx^DrlcOf7uK%mQNote-`yc%qf&Sp{=MUb~^#9){FKOr^eQ-tff<9=U z2-QWuprLIaJ3N*o?T(d0=Z6bA=zr0L@*r!aF|j80Y%?9`oT#B47bt}G#>{pdrnQds zY+3ch@0Ij(Isn=gII|7vH8I(*a13!?vuo;t`IN%U8rpdD0m|-W&lgzBh6401eNSH=!GD!ZY~2lMb-P zy%7#isu#xONcdbuIiDvbd;ymyxI$M={OwH|?w%-!h$)g+XBVJ?-<5PGv3_qtsc

*o{}xr6&$syR*^nOm-My^VGPB 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 >> 8) & 0xFF; - int sprite_index = tile->SquareParts & 0xFF; - snprintf (s, sizeof s, "%d, %d", sheet_index, sprite_index); - PCX_Image_draw_text (this, __, s, x, y - 18, strlen (s)); + // Show sprites & sheet indexes if in debug mode + int is_debug_mode = (*p_debug_mode_bits & 4) != 0; + if (is_debug_mode) { + int sheet_index = (tile->SquareParts >> 8) & 0xFF; + int sprite_index = tile->SquareParts & 0xFF; + snprintf (s, sizeof s, "%d, %d", sheet_index, sprite_index); + PCX_Image_draw_text (this, __, s, x, y - 18, strlen (s)); + } // Draw tile coords on line below terrain name snprintf (s, sizeof s, "(%d, %d)", is->viewing_tile_info_x, is->viewing_tile_info_y); @@ -31529,10 +31532,10 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, if (*out_variant == SW || *out_variant == NW) { *out_pixel_x += 16; } else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 16; } - if (*out_variant == SW && (anchor_sprite_index == 43 || anchor_sprite_index == 40)) { *out_pixel_x +=6; *out_pixel_y -= 12; } - else if (*out_variant == SW && (anchor_sprite_index == 35 || anchor_sprite_index == 39)) { *out_pixel_x +=6; *out_pixel_y -= 12; } - else if (*out_variant == SE && (anchor_sprite_index == 50)) { *out_pixel_x -=6; *out_pixel_y -= 12; } - else if (*out_variant == SE && (anchor_sprite_index == 26)) { *out_pixel_x += 50; *out_pixel_y -= 2; } + if (*out_variant == SW && (anchor_sprite_index == 43 || anchor_sprite_index == 40)) { *out_pixel_x +=6; *out_pixel_y -= 12; } + else if (*out_variant == SW && (anchor_sprite_index == 35 || anchor_sprite_index == 39)) { *out_pixel_x +=6; *out_pixel_y -= 12; } + else if (*out_variant == SE && (anchor_sprite_index == 50)) { *out_pixel_x -=6; *out_pixel_y -= 12; } + else if (*out_variant == SE && (anchor_sprite_index == 26)) { *out_pixel_x += 50; *out_pixel_y -= 2; } } // Sheet 4 else if (anchor_sheet_index == 4) { @@ -31544,10 +31547,10 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, else if (*out_variant == SE || *out_variant == NE) { *out_pixel_x -= 18; } if ((*out_variant == SW || *out_variant == SE) && anchor_sprite_index == 18) { *out_pixel_y -= 10; } - else if (*out_variant == SW && anchor_sprite_index == 73) { *out_pixel_x -=4; *out_pixel_y -= 10; } + else if (*out_variant == SW && anchor_sprite_index == 73) { *out_pixel_x -=4; *out_pixel_y -= 10; } else if (*out_variant == NW && anchor == DIR_SE && anchor_sprite_index == 42) { *out_pixel_y -= 8; } else if ((*out_variant == NW || *out_variant == SW) && anchor_sprite_index == 6) { *out_pixel_x += 12; *out_pixel_y -= 6; } - else if ((*out_variant == SW) && anchor_sprite_index == 16) { *out_pixel_x -=8; *out_pixel_y -= 10; } + else if ((*out_variant == SW) && anchor_sprite_index == 16) { *out_pixel_x -=8; *out_pixel_y -= 10; } } } else if (direct_diagonal && *out_variant == SW && anchor == DIR_NE) { *out_pixel_x -=8; *out_pixel_y -= 8; } @@ -32175,9 +32178,9 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc int buildings = 0; int draw_pixel_x = pixel_x; int draw_pixel_y = pixel_y; - Sprite * district_sprite; enum direction river_dir = DIR_ZERO; + // If in a territory, use owner's culture/era if (territory_owner_id > 0) { Leader * leader = &leaders[territory_owner_id]; culture = p_bic_data->Races[leader->RaceID].CultureGroupID; @@ -32187,6 +32190,8 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc era = leader->Era; if (cfg->align_to_coast) align_variant_and_pixel_offsets_with_coastline (tile, &variant, &draw_pixel_x, &draw_pixel_y); + + // Else render abandoned if not a Wonder, Natural Wonder, Bridge, or Canal } else if (district_id != WONDER_DISTRICT_ID && district_id != NATURAL_WONDER_DISTRICT_ID && district_id != BRIDGE_DISTRICT_ID && district_id != CANAL_DISTRICT_ID) { Sprite * abandoned_sprite = &is->abandoned_district_img; if (tile->vtable->m35_Check_Is_Water (tile) && is->abandoned_maritime_district_img.vtable != NULL) @@ -32197,10 +32202,12 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc return; } - // If out of a territory but builder is known, use builder's era + // If out of a territory (and not abandoned) but builder is known, use builder's era & culture if (territory_owner_id < 0 && inst->built_by_civ_id >= 0) { Leader * builder = &leaders[inst->built_by_civ_id]; culture = p_bic_data->Races[builder->RaceID].CultureGroupID; + if (cfg->vary_img_by_culture) + variant = culture; if (cfg->vary_img_by_era) era = builder->Era; } @@ -32211,7 +32218,9 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc int offset_y = draw_pixel_y + cfg->y_offset; int draw_x = offset_x - ((sprite_width - 128) / 2); int draw_y = offset_y - (sprite_height - 64); + Sprite (*sprites)[4][20] = is->district_img_sets[district_id].imgs; + // Render switch (district_id) { case WONDER_DISTRICT_ID: { @@ -32223,6 +32232,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc return; int construct_windex = -1; + Sprite * wsprite = NULL; // Completed wonder if (info->state == WDS_COMPLETED) { @@ -32237,9 +32247,9 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc align_district_with_river (tile, &draw_pixel_x, &draw_pixel_y, &river_dir); bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alternative_direction_image (tile_x, tile_y, territory_owner_id, wcfg); - Sprite * wsprite = (use_alt_dir && (set->alt_dir_img.vtable != NULL)) ? &set->alt_dir_img : &set->img; int offset_x = draw_pixel_x + cfg->x_offset; int offset_y = draw_pixel_y + cfg->y_offset; + wsprite = (use_alt_dir && (set->alt_dir_img.vtable != NULL)) ? &set->alt_dir_img : &set->img; draw_district_on_map_or_canvas(wsprite, map_renderer, offset_x, offset_y); return; @@ -32249,14 +32259,14 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc if (construct_windex >= is->wonder_district_count) return; - struct wonder_district_config * wcfg = &is->wonder_district_configs[construct_windex]; + struct wonder_district_config * wcfg = &is->wonder_district_configs[construct_windex]; struct wonder_district_image_set * set = &is->wonder_district_img_sets[construct_windex]; - bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alternative_direction_image (tile_x, tile_y, territory_owner_id, wcfg); - Sprite * csprite = (use_alt_dir && (set->alt_dir_construct_img.vtable != NULL)) ? &set->alt_dir_construct_img : &set->construct_img; int offset_x = draw_pixel_x + cfg->x_offset; int offset_y = draw_pixel_y + cfg->y_offset; + bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alternative_direction_image (tile_x, tile_y, territory_owner_id, wcfg); + wsprite = (use_alt_dir && (set->alt_dir_construct_img.vtable != NULL)) ? &set->alt_dir_construct_img : &set->construct_img; - draw_district_on_map_or_canvas(csprite, map_renderer, offset_x, offset_y); + draw_district_on_map_or_canvas(wsprite, map_renderer, offset_x, offset_y); return; } break; @@ -32274,14 +32284,14 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc v ^= v >> 16; buildings = clamp(0, 3, (int)(v & 3u)); /* final 0..3 */ variant = culture; - draw_district_on_map_or_canvas(&is->district_img_sets[district_id].imgs[variant][era][buildings], map_renderer, draw_x, draw_y); + draw_district_on_map_or_canvas(&sprites[variant][era][buildings], map_renderer, draw_x, draw_y); return; } case DISTRIBUTION_HUB_DISTRICT_ID: if (! is->current_config.enable_distribution_hub_districts) return; - draw_district_on_map_or_canvas(&is->district_img_sets[district_id].imgs[variant][era][buildings], map_renderer, draw_x, draw_y); + draw_district_on_map_or_canvas(&sprites[variant][era][buildings], map_renderer, draw_x, draw_y); return; case ENERGY_GRID_DISTRICT_ID: { @@ -32291,7 +32301,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc // Energy grids can have multiple buildings that - unlike most district buildings - don't have each other as prereqs (e.g., Coal Plant & Nuclear Plant), // and thus have a combinatorial number of images based on which power plants are built in their radius. buildings = get_energy_grid_image_index (tile_x, tile_y); - draw_district_on_map_or_canvas(&is->district_img_sets[district_id].imgs[variant][era][buildings], map_renderer, draw_x, draw_y); + draw_district_on_map_or_canvas(&sprites[variant][era][buildings], map_renderer, draw_x, draw_y); return; } case BRIDGE_DISTRICT_ID: @@ -32300,7 +32310,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc return; buildings = get_bridge_image_index (tile, tile_x, tile_y); - draw_district_on_map_or_canvas(&is->district_img_sets[district_id].imgs[variant][era][buildings], map_renderer, draw_x, draw_y); + draw_district_on_map_or_canvas(&sprites[variant][era][buildings], map_renderer, draw_x, draw_y); return; } case CANAL_DISTRICT_ID: @@ -32324,8 +32334,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc // Draw by counting number of completed buildings in radius if (cfg->render_strategy == DRS_BY_COUNT) { buildings = count_completed_buildings_in_district_radius (tile_x, tile_y, district_id); - district_sprite = &is->district_img_sets[district_id].imgs[variant][era][buildings]; - draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); + draw_district_on_map_or_canvas(&sprites[variant][era][buildings], map_renderer, draw_x, draw_y); return; } // Draw by checking each building individually and layering images @@ -32344,7 +32353,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - *p_debug_mode_bits |= 0xC; + //*p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; From 7ca3b1a5e8b95d3e281c68d8c78d287359b0686b Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 29 Jan 2026 07:42:20 -0800 Subject: [PATCH 300/356] Update bridge art to reuse for ancient/middle ages --- Art/Districts/1200/Bridge.PCX | Bin 69983 -> 88323 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Bridge.PCX b/Art/Districts/1200/Bridge.PCX index 74137287eb9dc9320ae2666a326ecb23006a3437..a61bd5c0e11e0cd7fb414a7ce0ed403a34a5f1b4 100644 GIT binary patch delta 1921 zcmeH`-Afc<6vi2y9d~tGC%;{mA!@OuBuzp$bio`g&UxoOXWp~WOVUXp{JgEtk>B9sVkziedL;J;0g9H;2tm}gi79A*5o{ARp?n|&NK}3ugvPm#1 z0UaxbOcFR^ceXb9kUP!CMq!>-gO$mVVTeJvOpRGXE_wpgaK>FgKC#vVmH0;V;&>av z88xyFHo)btMmHnJxrA;Q30M2m=t+ExMMi^d9t?ga^&!$j1Cy~Ic0Ud79_Eqcr*WTI zpFw|eBI1(Euu9^mO)n5B*Wlxk5!ih-h)Nm6K4MFKX*g3KV$Da3cm;!nv=J04v>Wx_ znRS@Vh)0o+4pW)Wv9?uj|(nX1e{l_F z&VNST79kRLve$O?U!itv;5mhh67HITce~MxDZ)*$&ZuEfm3PpC! zJ-A5QjkR4ECSw_@>ww%-xMPOhigY`TR|*~8*{fk8cI=iNtRjSrn8tMdRYd!88>1h; zZ9$cs!=pPkj1vtF7X0$yP|{cdlOKnyrpu@zeoS*I#)%);V!;x_kzY^aM@ueD{@lis zc;Yw#3(?QC=3t&^Tw6MpWE|eNr64DOgB3~WCRWjIKq%3bfh=6@A(*2%qz0-$ImO+H zbBa&+rZZevg%6VA+aW4>5l77hrQ9Kz&m-cC)7cfCTo(JHzbm%=^C2vwOF_ccbBJ zee!fRPi%To_ryJ2yvs)MIm-zh%2XVD%E0uD56={d7YZkI1aomjaxpxo*rJ$I<0z;x z{K!lE%yUA={j|ixG$#aZvoIbo)rE!|sRVAOjOwfjZI%;ZWGV^FRSdkJG_gL(34y8> zMqN`~XsDG9)XSU@@Rm(*8gm-|=2o@9`WHuMVjORm1;O@ox4L*apTzMA=0HOuXrXnQ zIpA0m60ffb8(KS~U@b)Zg@lbntw<>rg^k3eC5hGnx_1DM+JeNBW5R}^xj^qaE73e? zqIp%vmvaVIuLv84`cWT(V-}+41tD>7QDQwQY#1IdD#%6?C|qC;I1U|_*oZL~luIdr zHznqPaxPhpd2emE=QU)amSZ1=TbH@}Oy2Gm_KzL0k31|TXUbH%!IEsZ=HgPsRk~g& zHB^fMN11grLIFn`Xf6y_t9(ORs-Zn^IojTnz`yeN#gl#QkchMaX;n#PeaQ`%rGG%W z(-o^H=N*L$Tu)g&(eGjkKklT>zsC8NH*G@+WK`;aYM_xLrREImqZpz1R55L1ibo&j zJRIXQ8o5>{RLB*MkJ*6j=m3^i{2lFN+q>=fk?n_QWMuoD&&SC2_8;Xxvi)~Y=hf<| GuYUl<_Uya> From 3512dc855fd4554d2c1537e344dbdea9df318516 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 29 Jan 2026 08:57:53 -0800 Subject: [PATCH 301/356] Add comments to default c3x config file for new settings --- default.c3x_config.ini | 50 +++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 102d3c69..04d39ec1 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -793,7 +793,7 @@ introduce_all_human_players_at_start_of_hotseat_game = false ; Allows units to be unloaded from an army. The army type must have the "unload" ability set in the editor. allow_unload_from_army = false -; TODO: add documentation +; Allow colonies to be built on tiles inside another civ's territory (for strategic/luxury resources); applies a relation penalty with the other civ, per each colony. allow_extraterritorial_colonies = false per_extraterritorial_colony_relation_penalty = 3 @@ -846,7 +846,7 @@ draw_forests_over_roads_and_railroads = true ; set to "none". aircraft_victory_animation = none -; TODO: add documentation +; Enables naming tiles via the right-click menu and displays those names on the map. enable_named_tiles = true [=======================] @@ -901,13 +901,19 @@ minimum_natural_wonder_separation = 10 ; their work radius. If multiple cities share a district, they can optionally share its buildings and wonders (see options below). Districts with ; pollution or enemy units yield no bonuses. Destroyed districts remove dependent buildings from affected cities unless they have another district of ; that type in range. The five district types below can be enabled independently: -; enable_districts: Enables standard configurable districts (by default: Encampment, Campus, Holy Site, Commercial Hub, Entertainment Complex, Industrial Zone). -; Configured is done in default.districts_config.txt, user.districts_config.txt, and scenario.districts_config.txt -; enable_neighborhood_districts: Enables population growth past a configurable limit (see maximum_pop_before_neighborhood_needed below) -; enable_wonder_districts: Wonders require a dedicated district tile, making them visible on the map and optionally destructible. -; Configured in default.districts_wonders_config.txt, user.districts_wonders_config.txt, and scenario.districts_wonders_config.txt +; enable_districts: Enables standard configurable districts (by default: Encampment, Campus, Holy Site, Commercial Hub, Entertainment Complex, Industrial Zone). +; Configured is done in default.districts_config.txt, user.districts_config.txt, and scenario.districts_config.txt +; enable_neighborhood_districts: Enables population growth past a configurable limit (see maximum_pop_before_neighborhood_needed below) +; enable_wonder_districts: Wonders require a dedicated district tile, making them visible on the map and optionally destructible. +; Configured in default.districts_wonders_config.txt, user.districts_wonders_config.txt, and scenario.districts_wonders_config.txt ; enable_distribution_hub_districts: Special districts that distribute food/shields from surrounding tiles to all connected cities -; enable_aerodrome_districts: Air units can only be built/based at Aerodrome districts instead of cities (if air_units_use_aerodrome_districts_not_cities is true) +; enable_aerodrome_districts: Air units can only be built/based at Aerodrome districts instead of cities (if air_units_use_aerodrome_districts_not_cities is true) +; enable_port_districts: Enables Port districts, which serve as coastal district tiles for naval production/basing when naval_units_use_port_districts_not_cities is on. +; enable_bridge_districts: Enables Bridge districts on coastal water; completed bridges let land units cross water tiles. +; enable_canal_districts: Enables Canal districts on land; completed canals let naval units traverse land tiles between water bodies. +; enable_central_rail_hub_districts: Enables Central Rail Hub districts used for rail-era infrastructure (e.g., Mass Transit). +; enable_energy_grid_districts: Enables Energy Grid districts associated with power plant infrastructure. +; enable_great_wall_districts: Enables Great Wall districts (wall-like tiles that can block others if great_wall_districts_impassible_by_others is on). enable_districts = false enable_neighborhood_districts = false enable_wonder_districts = false @@ -946,6 +952,7 @@ naval_units_use_port_districts_not_cities = true ; culture, by default) only apply if the city actually needs them based on its population. Periodic popups will remind you when a Neighborhood is needed. Only ; applies when enable_neighborhood_districts is set to true. neighborhood_needed_message_frequency sets how often (in turns) the reminder message appears; ; set to 0 to disable. +; If destroying_neighborhood_reduces_pop is true, losing a Neighborhood district reduces the city's population down to the new cap. maximum_pop_before_neighborhood_needed = 6 per_neighborhood_pop_growth_enabled = 3 neighborhood_needed_message_frequency = 4 @@ -980,11 +987,23 @@ distribution_hub_shield_yield_divisor = 2 ai_ideal_distribution_hub_count_per_100_cities = 50 max_distribution_hub_count_per_100_cities = 50 -; TODO: add documentation +; Bonus percent applied to Distribution Hub food and shield yields for cities that have a Central Rail Hub district in their work radius. central_rail_hub_distribution_food_bonus_percent = 25 central_rail_hub_distribution_shield_bonus_percent = 25 -; TODO: add documentation +; District placement and AI bridge/canal behavior: +; expand_water_tile_checks_to_city_work_area: Use the full city work radius (not just adjacent tiles) for river/lake/sea checks (e.g. to build a port if a +; city has coast within its work area, even if not adjacent to the sea). +; Note that this does not affect Aqueducts, which still require adjacency to a fresh water source. +; workers_can_enter_coast: Allow workers to move onto coast tiles without embarking. +; max_contiguous_bridge_districts: Maximum number of contiguous bridge district tiles allowed in a line (0 = no limit). +; max_contiguous_canal_districts: Maximum number of contiguous canal district tiles allowed in a line (0 = no limit). +; ai_canal_eval_min_bisected_land_tiles: Minimum land tiles required on each side for the AI to consider a canal candidate. +; ai_bridge_canal_eval_block_size: Map block size used when scanning for AI bridge/canal candidates. +; ai_bridge_eval_lake_tile_threshold: Water bodies at or below this size are treated as lakes for AI bridge evaluation. +; ai_can_replace_existing_districts_with_canals: Allow the AI to build canal/bridge districts over existing non-wonder districts. +; ai_builds_bridges: Allow the AI to construct bridge districts. +; ai_builds_canals: Allow the AI to construct canal districts. expand_water_tile_checks_to_city_work_area = true workers_can_enter_coast = true max_contiguous_bridge_districts = 3 @@ -1002,10 +1021,15 @@ ai_builds_canals = true ; infrastructure. Only applies when enable_districts is set to true. ai_defends_districts = true -; TODO: add documentation +; As AI cities queue up districts needed, the max number of turns to wait for a worker to build it before the request is dropped +; until the AI city is triggered to build the district again (e.g., by trying to build a building that depends on it) ai_city_district_max_build_wait_turns = 20 -; TODO: add documentation +; Great Wall behavior: +; disable_great_wall_city_defense_bonus: Disables the Great Wall wonder's city defense doubling effect. +; great_wall_districts_impassible_by_others: Great Wall district tiles block movement by other civs (unless obsolete). +; auto_build_great_wall_around_territory: Auto-place Great Wall districts along your borders when the specified wonder is built. +; great_wall_auto_build_wonder_name: Name of the wonder that triggers auto-building (empty disables auto-build). disable_great_wall_city_defense_bonus = false great_wall_districts_impassible_by_others = true auto_build_great_wall_around_territory = true @@ -1015,4 +1039,4 @@ great_wall_auto_build_wonder_name = "The Great Wall" ; are highlighted more brightly, while tiles that fall within multiple cities' work radii are highlighted more intensely. This visual aid helps you ; strategically place districts and improvements by showing which cities will be affected. The highlights update dynamically as you move the worker ; around the map. Only applies when enable_districts is set to true. -enable_city_work_radii_highlights = true \ No newline at end of file +enable_city_work_radii_highlights = true From d22291178c898bceaf82af12093dac7fa365e4ca Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 29 Jan 2026 09:00:04 -0800 Subject: [PATCH 302/356] Add comments for port districts --- default.c3x_config.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 04d39ec1..cb2050bf 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -943,6 +943,9 @@ show_message_when_building_lost_to_destroyed_district = true ; limited to Aerodromes. This adds strategic depth by requiring infrastructure for air power and making air bases visible and vulnerable on the map. ; Only applies when enable_aerodrome_districts is also set to true. air_units_use_aerodrome_districts_not_cities = true + +; When enabled, naval units can only be built in cities with a Port district in their work radius and must be based at Port districts instead of cities. +; If true, a city cannot build naval units until it has a Port district in range. Only applies when enable_port_districts is also set to true. naval_units_use_port_districts_not_cities = true ; Neighborhood district limit population growth. Once a city reaches maximum_pop_before_neighborhood_needed, it cannot From 58d31dc491f051ac666e7c674adfceb09909b8ac Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 29 Jan 2026 09:02:23 -0800 Subject: [PATCH 303/356] More port district comments --- default.c3x_config.ini | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/default.c3x_config.ini b/default.c3x_config.ini index cb2050bf..8935f823 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -944,8 +944,9 @@ show_message_when_building_lost_to_destroyed_district = true ; Only applies when enable_aerodrome_districts is also set to true. air_units_use_aerodrome_districts_not_cities = true -; When enabled, naval units can only be built in cities with a Port district in their work radius and must be based at Port districts instead of cities. -; If true, a city cannot build naval units until it has a Port district in range. Only applies when enable_port_districts is also set to true. +; When enabled, naval units can only be built in cities with a Port district in their work radius and cannot enter city tiles directly. Naval unit +; healing must be done at Port districts, and travel between continents, lakes and so on can instead only be done via Canal districts (via enable_canal_districts). +; Additionally, a city cannot build naval units until it has a Port district in range. Only applies when enable_port_districts is set to true. naval_units_use_port_districts_not_cities = true ; Neighborhood district limit population growth. Once a city reaches maximum_pop_before_neighborhood_needed, it cannot From 4c637a1b2d936daf06199b6967ab39d162e33138 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 29 Jan 2026 09:03:09 -0800 Subject: [PATCH 304/356] More port comments --- default.c3x_config.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 8935f823..e672e83a 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -945,8 +945,8 @@ show_message_when_building_lost_to_destroyed_district = true air_units_use_aerodrome_districts_not_cities = true ; When enabled, naval units can only be built in cities with a Port district in their work radius and cannot enter city tiles directly. Naval unit -; healing must be done at Port districts, and travel between continents, lakes and so on can instead only be done via Canal districts (via enable_canal_districts). -; Additionally, a city cannot build naval units until it has a Port district in range. Only applies when enable_port_districts is set to true. +; healing must be done at Port districts, and travel between continents, lakes and so on can instead only be done via Canal districts (via enable_canal_districts), +; rather than cities on an isthmus. Additionally, a city cannot build naval units until it has a Port district in range. Only applies when enable_port_districts is set to true. naval_units_use_port_districts_not_cities = true ; Neighborhood district limit population growth. Once a city reaches maximum_pop_before_neighborhood_needed, it cannot From bfcda2b81d7990bc288d77de2175315cb6293c30 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 29 Jan 2026 09:08:54 -0800 Subject: [PATCH 305/356] Add comments for AI auto build great wall strategy --- default.c3x_config.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/default.c3x_config.ini b/default.c3x_config.ini index e672e83a..33c74245 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -1034,10 +1034,12 @@ ai_city_district_max_build_wait_turns = 20 ; great_wall_districts_impassible_by_others: Great Wall district tiles block movement by other civs (unless obsolete). ; auto_build_great_wall_around_territory: Auto-place Great Wall districts along your borders when the specified wonder is built. ; great_wall_auto_build_wonder_name: Name of the wonder that triggers auto-building (empty disables auto-build). +; ai_auto_build_great_wall_strategy: How AI uses auto-built Great Wall: all-borders (any border tiles) or other-civ-bordered-only (only borders touching another civ). disable_great_wall_city_defense_bonus = false great_wall_districts_impassible_by_others = true auto_build_great_wall_around_territory = true great_wall_auto_build_wonder_name = "The Great Wall" +ai_auto_build_great_wall_strategy = all-borders ; When enabled, holding down the Control key while a worker is selected will highlight all tiles within the work radii of nearby cities. City centers ; are highlighted more brightly, while tiles that fall within multiple cities' work radii are highlighted more intensely. This visual aid helps you From d980af8ffa45a5566c2426b986d10d2f08028ca0 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 29 Jan 2026 12:50:02 -0800 Subject: [PATCH 306/356] Fix unused Wonder districts not rendering --- injected_code.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/injected_code.c b/injected_code.c index 92284db6..bef432a7 100644 --- a/injected_code.c +++ b/injected_code.c @@ -32268,7 +32268,12 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc draw_district_on_map_or_canvas(wsprite, map_renderer, offset_x, offset_y); return; - } + + // Unused + } else { + draw_district_on_map_or_canvas(&sprites[variant][era][buildings], map_renderer, draw_x, draw_y); + return; + } break; } case NEIGHBORHOOD_DISTRICT_ID: From c7d2fc48888160168348724abdc287150c7a02ce Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 29 Jan 2026 14:54:44 -0800 Subject: [PATCH 307/356] Add extra logic for handling aqueducts --- C3X.h | 3 +++ injected_code.c | 35 +++++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/C3X.h b/C3X.h index 88aa8303..37227bed 100644 --- a/C3X.h +++ b/C3X.h @@ -2080,6 +2080,9 @@ struct district_button_image_set { enum great_wall_auto_build_state great_wall_auto_build; Tile * focused_tile; + // Stores the improve ID currently being evaluated inside patch_City_can_build_improvement. + int current_evaluating_improve_id; + // Stores the index in the list of target civs currently being drawn on the espionage form. Used to replace the civ name with its era-specific alias. int espionage_form_drawing_target_index; diff --git a/injected_code.c b/injected_code.c index bef432a7..bd3d0e80 100644 --- a/injected_code.c +++ b/injected_code.c @@ -11949,8 +11949,8 @@ bridge_district_tile_is_valid (int tile_x, int tile_y) { if (! tile_is_coastal_water (tile_x, tile_y)) return false; - if (! bridge_tile_has_land_on_both_sides (tile_x, tile_y)) - return false; + //if (! bridge_tile_has_land_on_both_sides (tile_x, tile_y)) + // return false; if (is->current_config.max_contiguous_bridge_districts > 0) { int max_bridges = is->current_config.max_contiguous_bridge_districts; @@ -20954,6 +20954,13 @@ bool __fastcall patch_Map_impl_has_fresh_water_within_work_area (Map * this, int edx, int tile_x, int tile_y) { if (is->current_config.enable_districts && is->current_config.expand_water_tile_checks_to_city_work_area) { + int improv_id = is->current_evaluating_improve_id; + if ((improv_id >= 0) && (improv_id < p_bic_data->ImprovementsCount)) { + + // If an Aqueduct, default to original logic + if ((p_bic_data->Improvements[improv_id].ImprovementFlags & ITF_Allows_City_Level_2) != 0) + return Map_impl_has_fresh_water (this, __, tile_x, tile_y); + } if (patch_Map_impl_is_near_river_within_work_area (this, __, tile_x, tile_y, 1)) return true; if (patch_Map_impl_is_near_lake_within_work_area (this, __, tile_x, tile_y, 1)) @@ -21066,13 +21073,18 @@ city_meets_district_prereqs_to_build_improvement (City * city, int i_improv, boo bool __fastcall patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply_strict_rules) -{ +{ + is->current_evaluating_improve_id = i_improv; + // First defer to the base game's logic bool base = City_can_build_improvement (this, __, i_improv, apply_strict_rules); if (! base) return false; if (! is->current_config.enable_districts) return base; - return city_meets_district_prereqs_to_build_improvement (this, i_improv, apply_strict_rules); + bool can_build = city_meets_district_prereqs_to_build_improvement (this, i_improv, apply_strict_rules); + is->current_evaluating_improve_id = -1; + + return can_build; } bool @@ -26219,11 +26231,14 @@ patch_Unit_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, bool bool const allow_bridge_walk = is->current_config.enable_districts && is->current_config.enable_bridge_districts && (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Land); + bool const allow_canal_sail = is->current_config.enable_districts && + is->current_config.enable_canal_districts && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Sea); bool coast_override_active = false; enum UnitStateType prev_state = this->Body.UnitState; int prev_container = this->Body.Container_Unit; - if (allow_worker_coast || allow_bridge_walk) { + if (allow_worker_coast || allow_bridge_walk || allow_canal_sail) { int nx, ny; get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); Tile * dest = tile_at (nx, ny); @@ -26261,6 +26276,14 @@ patch_Unit_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, bool should_override = true; } + if (! should_override && allow_canal_sail) { + struct district_instance * inst = get_district_instance (dest); + if ((inst != NULL) && + (inst->district_id == CANAL_DISTRICT_ID) && + district_is_complete (dest, inst->district_id)) + should_override = true; + } + if (should_override) { coast_override_active = true; is->coast_walk_unit = this; @@ -33764,7 +33787,7 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t if (source != NULL && source->vtable->m35_Check_Is_Water (source) && (source->vtable->m50_Get_Square_BaseType (source) == SQ_Coast)) - return true; + return PBV_OK; Tile * dest = tile_at (to_x, to_y); if ((dest != NULL) && From 667eb0dbbf3883474f1c36b2cd73776f0658bfe0 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 29 Jan 2026 15:12:17 -0800 Subject: [PATCH 308/356] Fix bug with worker taking instruction from previous worker on keydown event --- Notes/district_todos.md | 1 + injected_code.c | 149 +++++----------------------------------- 2 files changed, 20 insertions(+), 130 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 5d27a863..be9aacba 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -4,6 +4,7 @@ - Light annotations ## To ask Flintlock + - naval units can't enter canals - Enabling pillage button for naval units - Worker on coast wake up issue - Colony structure and loop for relation penalty diff --git a/injected_code.c b/injected_code.c index bd3d0e80..5191fe42 100644 --- a/injected_code.c +++ b/injected_code.c @@ -4831,40 +4831,6 @@ tile_has_diagonal_water (int tile_x, int tile_y) return false; } -bool -bridge_tile_has_land_on_both_sides (int tile_x, int tile_y) -{ - struct bridge_pair { - int dx1, dy1; - int dx2, dy2; - }; - - const struct bridge_pair pairs[] = { - { 0, -2, 0, 2 }, - { -2, 0, 2, 0 }, - { -1, -1, 1, 1 }, - { -1, 1, 1, -1 }, - }; - - for (int i = 0; i < (int)(sizeof (pairs) / sizeof (pairs[0])); i++) { - int ax = tile_x + pairs[i].dx1; - int ay = tile_y + pairs[i].dy1; - if (! tile_exists_at (ax, ay)) - continue; - if (! tile_is_land (-1, ax, ay, false)) - continue; - int bx = tile_x + pairs[i].dx2; - int by = tile_y + pairs[i].dy2; - if (! tile_exists_at (bx, by)) - continue; - if (! tile_is_land (-1, bx, by, false)) - continue; - return true; - } - - return false; -} - bool tile_part_of_existing_candidate (int tile_x, int tile_y) { @@ -4945,99 +4911,6 @@ add_ai_candidate_entry (int district_id, short owner_civ_id, short * xs, short * return true; } -int -gather_bridge_line (int start_x, int start_y, int dx, int dy, int limit, - int block_x0, int block_y0, int block_x1, int block_y1, - short * out_x, short * out_y) -{ - int effective_limit = clamp (1, AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES, limit); - - if (! tile_is_coastal_water (start_x, start_y)) - return 0; - if (! bridge_tile_has_land_on_both_sides (start_x, start_y)) - return 0; - - short back_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; - short back_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; - short forward_x[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; - short forward_y[AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES]; - int back_count = 0; - int forward_count = 0; - int remaining = effective_limit - 1; - Map * map = &p_bic_data->Map; - - int cx = start_x; - int cy = start_y; - while ((remaining > 0) && (back_count < effective_limit)) { - int nx = cx - dx; - int ny = cy - dy; - wrap_tile_coords (map, &nx, &ny); - if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) - break; - if (! tile_is_coastal_water (nx, ny)) - break; - if (! bridge_tile_has_land_on_both_sides (nx, ny)) - break; - if (tile_part_of_existing_candidate (nx, ny)) - break; - back_x[back_count] = (short)nx; - back_y[back_count] = (short)ny; - back_count++; - remaining--; - cx = nx; - cy = ny; - } - - cx = start_x; - cy = start_y; - while ((remaining > 0) && (forward_count < effective_limit)) { - int nx = cx + dx; - int ny = cy + dy; - wrap_tile_coords (map, &nx, &ny); - if (! tile_point_in_block (nx, ny, block_x0, block_y0, block_x1, block_y1)) - break; - if (! tile_is_coastal_water (nx, ny)) - break; - if (! bridge_tile_has_land_on_both_sides (nx, ny)) - break; - if (tile_part_of_existing_candidate (nx, ny)) - break; - forward_x[forward_count] = (short)nx; - forward_y[forward_count] = (short)ny; - forward_count++; - remaining--; - cx = nx; - cy = ny; - } - - int write = 0; - for (int bi = back_count - 1; bi >= 0; bi--) { - out_x[write] = back_x[bi]; - out_y[write] = back_y[bi]; - write++; - } - out_x[write] = (short)start_x; - out_y[write] = (short)start_y; - write++; - for (int fi = 0; fi < forward_count; fi++) { - out_x[write] = forward_x[fi]; - out_y[write] = forward_y[fi]; - write++; - } - - return write; -} - -bool -bridge_line_connects_two_continents (short * xs, short * ys, int count) -{ - for (int i = 0; i < count; i++) { - if (bridge_tile_connects_two_continents (xs[i], ys[i], -1)) - return true; - } - return false; -} - int find_bridge_candidate_in_block (Map * map, int block_x0, int block_y0, int block_x1, int block_y1, int contiguous_limit, int candidate_capacity, @@ -11949,8 +11822,23 @@ bridge_district_tile_is_valid (int tile_x, int tile_y) { if (! tile_is_coastal_water (tile_x, tile_y)) return false; - //if (! bridge_tile_has_land_on_both_sides (tile_x, tile_y)) - // return false; + + bool has_adjacent_land_or_bridge = false; + Map * map = &p_bic_data->Map; + int const adj_dx[8] = { 0, 0, -2, 2, -1, 1, -1, 1 }; + int const adj_dy[8] = { -2, 2, 0, 0, 1, -1, -1, 1 }; + + for (int i = 0; i < 8; i++) { + int nx = tile_x + adj_dx[i]; + int ny = tile_y + adj_dy[i]; + wrap_tile_coords (map, &nx, &ny); + if (tile_is_land (-1, nx, ny, false) || tile_has_district_at (nx, ny, BRIDGE_DISTRICT_ID)) { + has_adjacent_land_or_bridge = true; + break; + } + } + if (! has_adjacent_land_or_bridge) + return false; if (is->current_config.max_contiguous_bridge_districts > 0) { int max_bridges = is->current_config.max_contiguous_bridge_districts; @@ -20956,7 +20844,7 @@ patch_Map_impl_has_fresh_water_within_work_area (Map * this, int edx, int tile_x if (is->current_config.enable_districts && is->current_config.expand_water_tile_checks_to_city_work_area) { int improv_id = is->current_evaluating_improve_id; if ((improv_id >= 0) && (improv_id < p_bic_data->ImprovementsCount)) { - + // If an Aqueduct, default to original logic if ((p_bic_data->Improvements[improv_id].ImprovementFlags & ITF_Allows_City_Level_2) != 0) return Map_impl_has_fresh_water (this, __, tile_x, tile_y); @@ -22537,6 +22425,7 @@ patch_Main_Screen_Form_m82_handle_key_event (Main_Screen_Form * this, int edx, i // The only way to catch these beforehand I've found it is to intercept the key event here. } else if (is->current_config.enable_districts && p_main_screen_form->Current_Unit != NULL && + is_down && is_worker (p_main_screen_form->Current_Unit)) { int command = -1; bool removed_existing = false; From 283b8cfbe4758f42e04bf0a15bd10cf278351a41 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 29 Jan 2026 16:23:28 -0800 Subject: [PATCH 309/356] Working through building rail/road on bridges issues --- Notes/district_todos.md | 1 + default.districts_config.txt | 1 + injected_code.c | 103 +++++++++++++++++++++++++++++++++-- 3 files changed, 99 insertions(+), 6 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index be9aacba..14429a74 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,3 +1,4 @@ + - Make sure workers can build roads and railroads on bridges - Districts: - Municipal District (ZergMazter doing art) - Central Rail Hub (ZergMazter doing art) diff --git a/default.districts_config.txt b/default.districts_config.txt index 34f8a3c3..413ef6f4 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -431,6 +431,7 @@ btn_tile_sheet_row = 0 btn_tile_sheet_column = 7 advance_prereqs = Industrialization buildable_on = coast +auto_add_road = 1 defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 0 diff --git a/injected_code.c b/injected_code.c index 5191fe42..bbd504c7 100644 --- a/injected_code.c +++ b/injected_code.c @@ -215,6 +215,7 @@ bool __fastcall patch_Unit_can_perform_command (Unit * this, int edx, int unit_c bool __fastcall patch_Unit_can_pillage (Unit * this, int edx, int tile_x, int tile_y); bool __fastcall patch_City_has_resource (City * this, int edx, int resource_id); bool __fastcall patch_Leader_can_build_city_improvement (Leader * this, int edx, int i_improv, bool param_2); +char __fastcall patch_Leader_can_do_worker_job (Leader * this, int edx, enum Worker_Jobs job, int tile_x, int tile_y, int ask_if_replacing); bool can_build_district_on_tile (Tile * tile, int district_id, int civ_id); bool city_can_build_district (City * city, int district_id); bool leader_can_build_district (Leader * leader, int district_id); @@ -6013,7 +6014,7 @@ district_is_complete(Tile * tile, int district_id) if (cfg->auto_add_railroad) { bool has_railroad = tile->vtable->m23_Check_Railroads (tile, __, 0) != 0; if (! has_railroad) { - if ((territory_owner >= 0) && Leader_can_do_worker_job (&leaders[territory_owner], __, WJ_Build_Railroad, tile_x, tile_y, 0)) { + if ((territory_owner >= 0) && patch_Leader_can_do_worker_job (&leaders[territory_owner], __, WJ_Build_Railroad, tile_x, tile_y, 0)) { tile->vtable->m56_Set_Tile_Flags (tile, __, 0, TILE_FLAG_RAILROAD, tile_x, tile_y); } } @@ -18406,6 +18407,12 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) return patch_Map_check_colony_location (&p_bic_data->Map, __, this->Body.X, this->Body.Y, this->Body.CivID) == 0; } } + else if (unit_command_value == UCV_Build_Road) { + struct district_instance * inst = get_district_instance (tile); + bool has_road = tile->vtable->m25_Check_Roads (tile, __, 0) != 0; + if (!has_road && (inst != NULL) && tile_has_district_at (this->Body.X, this->Body.Y, BRIDGE_DISTRICT_ID)) + return true; + } else if (unit_command_value == UCV_Build_Mine) { bool has_district = (tile != NULL) && (tile != p_null_tile) && (get_district_instance (tile) != NULL); @@ -18833,6 +18840,26 @@ patch_Main_GUI_handle_button_press (Main_GUI * this, int edx, int button_id) int command = this->Unit_Command_Buttons[button_id].Command; + // Bypass normal command handling for bridge road/rail if the job is allowed + if (is->current_config.enable_districts && is->current_config.enable_bridge_districts && + (command == UCV_Build_Road || command == UCV_Build_Railroad) && + (p_main_screen_form->Current_Unit != NULL) && + is_worker (p_main_screen_form->Current_Unit)) { + Unit * unit = p_main_screen_form->Current_Unit; + Tile * tile = tile_at (unit->Body.X, unit->Body.Y); + if ((tile != NULL) && (tile != p_null_tile)) { + struct district_instance * inst = get_district_instance (tile); + if ((inst != NULL) && (inst->district_id == BRIDGE_DISTRICT_ID)) { + enum Worker_Jobs job = (command == UCV_Build_Road) ? WJ_Build_Road : WJ_Build_Railroad; + if (patch_Leader_can_do_worker_job (&leaders[unit->Body.CivID], __, job, unit->Body.X, unit->Body.Y, 1)) { + Unit_set_state (unit, __, (job == WJ_Build_Road) ? UnitState_Build_Road : UnitState_Build_Railroad); + unit->Body.Job_ID = job; + return; + } + } + } + } + // Clear any highlighted tiles if (is->current_config.enable_city_work_radii_highlights && is->highlight_city_radii) { is->highlight_city_radii = false; @@ -20183,17 +20210,28 @@ patch_Leader_can_do_worker_job (Leader * this, int edx, enum Worker_Jobs job, in bool skip_replacement_logic = (p_main_screen_form->Player_CivID == this->ID) && is->current_config.skip_repeated_tile_improv_replacement_asks; + Tile * tile = tile_at (tile_x, tile_y); + // Check if AI is trying to change a district tile (before calling vanilla logic) if ((is->current_config.enable_districts || is->current_config.enable_natural_wonders) && ((*p_human_player_bits & (1 << this->ID)) == 0)) { - Tile * tile = tile_at (tile_x, tile_y); if ((tile != NULL) && (tile != p_null_tile)) { struct district_instance * inst = get_district_instance (tile); if (inst != NULL && inst->district_id >= 0 && inst->district_id < is->district_count) { int district_id = inst->district_id; + bool allow_ai_change = false; + + // Allow AI to modify obsolete districts + if (district_is_obsolete_for_civ (district_id, this->ID)) + allow_ai_change = true; + + // Allow AI to build roads/rails on bridge districts + if (! allow_ai_change && (district_id == BRIDGE_DISTRICT_ID) && + (job == WJ_Build_Road || job == WJ_Build_Railroad)) + allow_ai_change = true; // For Wonder Districts: check if unused (can be replaced) - if (is->current_config.enable_wonder_districts && (district_id == WONDER_DISTRICT_ID)) { + if (! allow_ai_change && is->current_config.enable_wonder_districts && (district_id == WONDER_DISTRICT_ID)) { struct wonder_district_info * info = get_wonder_district_info (tile); // If there's a reservation (wonder being built) or completed wonder, block replacement @@ -20202,10 +20240,10 @@ patch_Leader_can_do_worker_job (Leader * this, int edx, enum Worker_Jobs job, in // Wonder district is unused - fall through to normal tech checks } - else if (is->current_config.enable_natural_wonders && (district_id == NATURAL_WONDER_DISTRICT_ID)) { + else if (! allow_ai_change && is->current_config.enable_natural_wonders && (district_id == NATURAL_WONDER_DISTRICT_ID)) { return 0; } - else { + else if (! allow_ai_change) { // For all other district types: AI should not change them return 0; } @@ -20228,7 +20266,6 @@ patch_Leader_can_do_worker_job (Leader * this, int edx, enum Worker_Jobs job, in } if (! tr && is->current_config.enable_districts && (job == WJ_Build_Mines)) { - Tile * tile = tile_at (tile_x, tile_y); if ((tile != NULL) && (tile != p_null_tile)) { struct district_instance * inst = get_district_instance (tile); if (inst != NULL && @@ -20248,6 +20285,29 @@ patch_Leader_can_do_worker_job (Leader * this, int edx, enum Worker_Jobs job, in } } + if (! tr && is->current_config.enable_districts && is->current_config.enable_bridge_districts && + (job == WJ_Build_Road || job == WJ_Build_Railroad)) { + if ((tile != NULL) && (tile != p_null_tile)) { + struct district_instance * inst = get_district_instance (tile); + if ((inst != NULL) && (inst->district_id == BRIDGE_DISTRICT_ID)) { + bool has_road = tile->vtable->m25_Check_Roads (tile, __, 0) != 0; + if (job == WJ_Build_Road) { + if (! has_road) + tr = 1; + } else { + bool has_rail = tile->vtable->m23_Check_Railroads (tile, __, 0) != 0; + if (has_road && ! has_rail) { + int req_tech = p_bic_data->WorkerJobs[job].RequireID; + if ((req_tech < 0) || + ((req_tech != p_bic_data->AdvanceCount) && + Leader_has_tech (&leaders[this->ID], __, req_tech))) + tr = 1; + } + } + } + } + } + return tr; } @@ -22432,12 +22492,37 @@ patch_Main_Screen_Form_m82_handle_key_event (Main_Screen_Form * this, int edx, i if (virtual_key_code == VK_M && is_command_button_active (&this->GUI, UCV_Build_Mine)) command = UCV_Build_Mine; else if (virtual_key_code == VK_I && is_command_button_active (&this->GUI, UCV_Irrigate)) command = UCV_Irrigate; else if (virtual_key_code == VK_N && is_command_button_active (&this->GUI, UCV_Plant_Forest)) command = UCV_Plant_Forest; + else if (virtual_key_code == VK_R) { + int shift_down = (*p_GetAsyncKeyState) (VK_SHIFT) >> 8; + if (shift_down && is_command_button_active (&this->GUI, UCV_Build_Railroad)) + command = UCV_Build_Railroad; + else if (is_command_button_active (&this->GUI, UCV_Build_Road)) + command = UCV_Build_Road; + } + + if ((command == UCV_Build_Road || command == UCV_Build_Railroad) && + is->current_config.enable_bridge_districts) { + Unit * unit = p_main_screen_form->Current_Unit; + Tile * tile = tile_at (unit->Body.X, unit->Body.Y); + if ((tile != NULL) && (tile != p_null_tile)) { + struct district_instance * inst = get_district_instance (tile); + if ((inst != NULL) && (inst->district_id == BRIDGE_DISTRICT_ID)) { + enum Worker_Jobs job = (command == UCV_Build_Road) ? WJ_Build_Road : WJ_Build_Railroad; + if (patch_Leader_can_do_worker_job (&leaders[unit->Body.CivID], __, job, unit->Body.X, unit->Body.Y, 1)) { + Unit_set_state (unit, __, (job == WJ_Build_Road) ? UnitState_Build_Road : UnitState_Build_Railroad); + unit->Body.Job_ID = job; + goto after_district_key_handling; + } + } + } + } if (handle_worker_command_that_may_replace_district (p_main_screen_form->Current_Unit, command, &removed_existing)) { Main_Screen_Form_issue_command (this, __, command, p_main_screen_form->Current_Unit); } } +after_district_key_handling: Main_Screen_Form_m82_handle_key_event (this, __, virtual_key_code, is_down); } @@ -33471,6 +33556,12 @@ patch_Tile_get_road_bonus (Tile * this) return 0; } } + if (is->current_config.enable_districts && is->current_config.enable_bridge_districts) { + struct district_instance * inst = get_district_instance (this); + if ((inst != NULL) && (inst->district_id == BRIDGE_DISTRICT_ID)) { + return 1; + } + } return Tile_get_road_bonus (this); } From 377bfe32b35a163e3067002fbbb213f9491ca737 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 29 Jan 2026 16:41:32 -0800 Subject: [PATCH 310/356] Fix movement on bridges w/ roads & rails bug --- injected_code.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/injected_code.c b/injected_code.c index bbd504c7..9975db93 100644 --- a/injected_code.c +++ b/injected_code.c @@ -19613,6 +19613,35 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr } } + // Treat roads/rails on bridge districts like land roads/rails for movement cost. + if ((unit != NULL) && + is->current_config.enable_districts && + is->current_config.enable_bridge_districts && + (p_bic_data->UnitTypes[unit->Body.UnitTypeID].Unit_Class == UTC_Land)) { + Tile * from = tile_at (from_x, from_y); + Tile * to = tile_at (to_x, to_y); + if ((from != NULL) && (from != p_null_tile) && (to != NULL) && (to != p_null_tile)) { + struct district_instance * from_inst = get_district_instance (from); + struct district_instance * to_inst = get_district_instance (to); + bool from_bridge = (from_inst != NULL) && + (from_inst->district_id == BRIDGE_DISTRICT_ID) && + district_is_complete (from, from_inst->district_id); + bool to_bridge = (to_inst != NULL) && + (to_inst->district_id == BRIDGE_DISTRICT_ID) && + district_is_complete (to, to_inst->district_id); + if (from_bridge || to_bridge) { + bool from_rail = from->vtable->m23_Check_Railroads (from, __, 0) != 0; + bool to_rail = to->vtable->m23_Check_Railroads (to, __, 0) != 0; + bool from_road = from->vtable->m25_Check_Roads (from, __, 0) != 0; + bool to_road = to->vtable->m25_Check_Roads (to, __, 0) != 0; + if (from_rail && to_rail) + base_cost = 0; + else if (from_road && to_road) + base_cost = 1; + } + } + } + if ((unit != NULL) && (base_cost >= 0) && is->current_config.enable_districts && From 63edbb685fef7f35410b3e0687d52548300344ac Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 29 Jan 2026 16:52:55 -0800 Subject: [PATCH 311/356] Clean up logic for allowing bridge roads --- injected_code.c | 61 ++++++++++++++----------------------------------- 1 file changed, 17 insertions(+), 44 deletions(-) diff --git a/injected_code.c b/injected_code.c index 9975db93..c15c8133 100644 --- a/injected_code.c +++ b/injected_code.c @@ -18385,6 +18385,7 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) Tile * tile = tile_at (this->Body.X, this->Body.Y); enum SquareTypes base_type = (tile != NULL && tile != p_null_tile) ? tile->vtable->m50_Get_Square_BaseType (tile) : SQ_INVALID; + // No worker or settler commands allowed on natural wonders if ((tile != NULL) && (tile != p_null_tile) && is->current_config.enable_natural_wonders) { struct district_instance * inst = get_district_instance (tile); if ((inst != NULL) && @@ -18402,17 +18403,33 @@ patch_Unit_can_perform_command (Unit * this, int edx, int unit_command_value) return is_worker (this) && can_build_district_on_tile (tile, district_id, this->Body.CivID); } + // Extra check for colony founding if extraterritorial colonies allowed else if (unit_command_value == UCV_Build_Colony || unit_command_value == UCV_Build_Remote_Colony) { if (is->current_config.allow_extraterritorial_colonies && is_worker (this)) { return patch_Map_check_colony_location (&p_bic_data->Map, __, this->Body.X, this->Body.Y, this->Body.CivID) == 0; } } + // Extra check for road building if bridge districts allowed else if (unit_command_value == UCV_Build_Road) { struct district_instance * inst = get_district_instance (tile); bool has_road = tile->vtable->m25_Check_Roads (tile, __, 0) != 0; if (!has_road && (inst != NULL) && tile_has_district_at (this->Body.X, this->Body.Y, BRIDGE_DISTRICT_ID)) return true; } + // Extra check for railroad building if bridge districts allowed + else if (unit_command_value == UCV_Build_Railroad) { + struct district_instance * inst = get_district_instance (tile); + bool has_road = tile->vtable->m25_Check_Roads (tile, __, 0) != 0; + bool has_rail = tile->vtable->m23_Check_Railroads (tile, __, 0) != 0; + if ((inst != NULL) && is_worker (this) && has_road && + ! has_rail && tile_has_district_at (this->Body.X, this->Body.Y, BRIDGE_DISTRICT_ID)) { + int req_tech = p_bic_data->WorkerJobs[WJ_Build_Railroad].RequireID; + if ((req_tech < 0) || + ((req_tech != p_bic_data->AdvanceCount) && + Leader_has_tech (&leaders[this->Body.CivID], __, req_tech))) + return true; + } + } else if (unit_command_value == UCV_Build_Mine) { bool has_district = (tile != NULL) && (tile != p_null_tile) && (get_district_instance (tile) != NULL); @@ -18840,26 +18857,6 @@ patch_Main_GUI_handle_button_press (Main_GUI * this, int edx, int button_id) int command = this->Unit_Command_Buttons[button_id].Command; - // Bypass normal command handling for bridge road/rail if the job is allowed - if (is->current_config.enable_districts && is->current_config.enable_bridge_districts && - (command == UCV_Build_Road || command == UCV_Build_Railroad) && - (p_main_screen_form->Current_Unit != NULL) && - is_worker (p_main_screen_form->Current_Unit)) { - Unit * unit = p_main_screen_form->Current_Unit; - Tile * tile = tile_at (unit->Body.X, unit->Body.Y); - if ((tile != NULL) && (tile != p_null_tile)) { - struct district_instance * inst = get_district_instance (tile); - if ((inst != NULL) && (inst->district_id == BRIDGE_DISTRICT_ID)) { - enum Worker_Jobs job = (command == UCV_Build_Road) ? WJ_Build_Road : WJ_Build_Railroad; - if (patch_Leader_can_do_worker_job (&leaders[unit->Body.CivID], __, job, unit->Body.X, unit->Body.Y, 1)) { - Unit_set_state (unit, __, (job == WJ_Build_Road) ? UnitState_Build_Road : UnitState_Build_Railroad); - unit->Body.Job_ID = job; - return; - } - } - } - } - // Clear any highlighted tiles if (is->current_config.enable_city_work_radii_highlights && is->highlight_city_radii) { is->highlight_city_radii = false; @@ -22521,30 +22518,6 @@ patch_Main_Screen_Form_m82_handle_key_event (Main_Screen_Form * this, int edx, i if (virtual_key_code == VK_M && is_command_button_active (&this->GUI, UCV_Build_Mine)) command = UCV_Build_Mine; else if (virtual_key_code == VK_I && is_command_button_active (&this->GUI, UCV_Irrigate)) command = UCV_Irrigate; else if (virtual_key_code == VK_N && is_command_button_active (&this->GUI, UCV_Plant_Forest)) command = UCV_Plant_Forest; - else if (virtual_key_code == VK_R) { - int shift_down = (*p_GetAsyncKeyState) (VK_SHIFT) >> 8; - if (shift_down && is_command_button_active (&this->GUI, UCV_Build_Railroad)) - command = UCV_Build_Railroad; - else if (is_command_button_active (&this->GUI, UCV_Build_Road)) - command = UCV_Build_Road; - } - - if ((command == UCV_Build_Road || command == UCV_Build_Railroad) && - is->current_config.enable_bridge_districts) { - Unit * unit = p_main_screen_form->Current_Unit; - Tile * tile = tile_at (unit->Body.X, unit->Body.Y); - if ((tile != NULL) && (tile != p_null_tile)) { - struct district_instance * inst = get_district_instance (tile); - if ((inst != NULL) && (inst->district_id == BRIDGE_DISTRICT_ID)) { - enum Worker_Jobs job = (command == UCV_Build_Road) ? WJ_Build_Road : WJ_Build_Railroad; - if (patch_Leader_can_do_worker_job (&leaders[unit->Body.CivID], __, job, unit->Body.X, unit->Body.Y, 1)) { - Unit_set_state (unit, __, (job == WJ_Build_Road) ? UnitState_Build_Road : UnitState_Build_Railroad); - unit->Body.Job_ID = job; - goto after_district_key_handling; - } - } - } - } if (handle_worker_command_that_may_replace_district (p_main_screen_form->Current_Unit, command, &removed_existing)) { Main_Screen_Form_issue_command (this, __, command, p_main_screen_form->Current_Unit); From cb77f8b320e9e5891e0b79c12cdfc5a5721f6198 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Thu, 29 Jan 2026 19:16:42 -0800 Subject: [PATCH 312/356] Add "lake" option --- Notes/district_todos.md | 5 +++-- default.districts_config.txt | 2 +- injected_code.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 14429a74..8fc03dff 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -5,9 +5,10 @@ - Light annotations ## To ask Flintlock - - naval units can't enter canals + - Naval units can't enter canals + - Unit movement from bridge->land (even when both have roads) still takes 1 turn - Enabling pillage button for naval units - - Worker on coast wake up issue + - Worker on coast and land units on bridge movement wake up issue - Colony structure and loop for relation penalty diff --git a/default.districts_config.txt b/default.districts_config.txt index 413ef6f4..dc6b72e2 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -26,7 +26,7 @@ ; - wonder_prereqs : Comma-separated wonder names. District cannot be built unless all Wonders are built by same civ. ; - natural_wonder_prereqs : Comma-separated natural wonder names. District cannot be built unless all Natural Wonders are within civ's territory. ; - buildable_on : Comma-separated square types: desert, plains, grassland, tundra, floodplain, hills, mountains, forest, jungle, swamp, volcano, - coast, sea, ocean, river, snow-forest, snow-mountains, snow-volcano, mine, irrigation. + coast, sea, ocean, river, snow-forest, snow-mountains, snow-volcano, mine, irrigation, lake. ; - buildable_adjacent_to : Same as buildable_on, plus "city". ; - buildable_on_districts : Comma-separated district names (tile must already have a completed district of any of these types, which it would replace). ; - buildable_adjacent_to_districts : Comma-separated district names (adjacent tile must have a completed district of any of these types). diff --git a/injected_code.c b/injected_code.c index c15c8133..88e37d54 100644 --- a/injected_code.c +++ b/injected_code.c @@ -1845,6 +1845,12 @@ district_buildable_irrigation_mask_bit (void) return (unsigned int)(1u << (SQ_SNOW_MOUNTAIN + 2)); } +unsigned int +district_buildable_lake_mask_bit (void) +{ + return (unsigned int)(1u << (SQ_SNOW_MOUNTAIN + 3)); +} + unsigned int all_square_types_mask (void) { @@ -1863,6 +1869,24 @@ tile_has_snow_mountain (Tile * tile) return (tile != NULL) && (tile != p_null_tile) && tile->vtable->m29_Check_Mountain_Snowcap (tile); } +bool +tile_is_lake (Tile * tile) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return false; + + if (! tile->vtable->m35_Check_Is_Water (tile)) + return false; + + int continent_id = tile->vtable->m46_Get_ContinentID (tile); + if ((continent_id < 0) || (continent_id >= p_bic_data->Map.Continent_Count)) + return false; + + int lake_size_threshold = 21; + Continent * continent = &p_bic_data->Map.Continents[continent_id]; + return continent->Body.TileCount <= lake_size_threshold; +} + bool tile_has_snow_volcano (Tile * tile) { @@ -1936,6 +1960,10 @@ tile_matches_square_type_mask (Tile * tile, unsigned int mask) if ((mask & irrigation_bit) && tile->vtable->m17_Check_Irrigation (tile, __, 0)) return true; + unsigned int lake_bit = district_buildable_lake_mask_bit (); + if ((mask & lake_bit) && tile_is_lake (tile)) + return true; + return false; } @@ -7550,6 +7578,9 @@ parse_buildable_square_type_mask (struct string_slice const * value, } else if (slice_matches_str (&item_slice, "irrigation")) { mask |= district_buildable_irrigation_mask_bit (); entry_count += 1; + } else if (slice_matches_str (&item_slice, "lake")) { + mask |= district_buildable_lake_mask_bit (); + entry_count += 1; } else { enum SquareTypes parsed; if (read_square_type_value (&item_slice, &parsed)) { From 5ad8c6d59404ed092b5f6fa5fdb41e80c2cc652d Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 30 Jan 2026 09:57:48 -0800 Subject: [PATCH 313/356] Set maritime pillage button tooltip text --- C3X.h | 3 +++ Text/c3x-labels.txt | 3 +++ default.districts_config.txt | 2 +- injected_code.c | 1 + 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/C3X.h b/C3X.h index 37227bed..b4966ff7 100644 --- a/C3X.h +++ b/C3X.h @@ -579,6 +579,9 @@ enum c3x_label { CL_DISTRICTS_IN_SAVE_FILE, CL_CURRENTLY_CONFIGURED_DISTRICTS, + // Maritime district pillaging button tooltip + CL_PILLAGE, + // Tile naming CL_NAME_TILE, CL_RENAME_TILE, diff --git a/Text/c3x-labels.txt b/Text/c3x-labels.txt index 7ea058ee..9b345902 100644 --- a/Text/c3x-labels.txt +++ b/Text/c3x-labels.txt @@ -140,6 +140,9 @@ There may be other errors as well. Districts in save file Currently configured districts from +; Maritime district pillage button tooltip +Pillage + ; For naming tiles Name Tile Rename diff --git a/default.districts_config.txt b/default.districts_config.txt index dc6b72e2..cfdb804b 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -13,6 +13,7 @@ ; - img_paths : Comma-separated PCX filenames under Art/Districts/1200/. 1 for single image, 5 for culture variants (AMER, EURO, ROMAN, MIDEAST, ASIAN). Order matters. ; - render_strategy : "by-count" | "by-building". Whether the PCX files show all buildings together, or one building per column. (default: "by-count") + ; - draw_over_resources : 0 or 1. If a resource is also on the tile, draw the district on top. (default: 0) ; - vary_img_by_era : 0 or 1. If 1, each PCX image must have 4 rows, 1 for each era. ; - vary_img_by_culture : 0 or 1. If 1, img_paths should list 5 files (AMER, EURO, ROMAN, MIDEAST, ASIAN). ; - btn_tile_sheet_row : Number. Row index in the district button tile sheet (0-based). @@ -49,7 +50,6 @@ ; - x_offset : Number (pixels). Push the sprite farther to the right (or left, if negative). (default: 0) ; - y_offset : Number (pixels). Push the sprite farther down (or up, if negative). (default: 0) ; - align_to_coast : 0 or 1. Aligns art to coastline, slightly adjusting x & y pixels. (default: 0) - ; - draw_over_resources : 0 or 1. If a resource is also on the tile, draw the district on top. (default: 0) ; - auto_add_road : 0 or 1. Auto-add road on completion. (default: 0) ; - auto_add_railroad : 0 or 1. Auto-add railroad on completion. (default: 0) ; - allow_irrigation_from : 0 or 1. District can act as an irrigation source. (default: 0) diff --git a/injected_code.c b/injected_code.c index 88e37d54..2f7a645a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -18044,6 +18044,7 @@ patch_Main_GUI_set_up_unit_command_buttons (Main_GUI * this) pillage_button->Button.field_664 = 0; pillage_button->Button.field_5FC[13] = 0; pillage_button->Button.vtable->m01_Show_Enabled ((Base_Form *)&pillage_button->Button, __, 0); + Button_set_tooltip (&pillage_button->Button, __, is->c3x_labels[CL_PILLAGE]); } } } From c047278aec15fe7097d50430b7e36a671fbe40dc Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 30 Jan 2026 12:04:56 -0800 Subject: [PATCH 314/356] Fix coastal fortress check - city still needs to be directly adjacent --- default.c3x_config.ini | 2 +- injected_code.c | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 33c74245..bd7a16be 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -998,7 +998,7 @@ central_rail_hub_distribution_shield_bonus_percent = 25 ; District placement and AI bridge/canal behavior: ; expand_water_tile_checks_to_city_work_area: Use the full city work radius (not just adjacent tiles) for river/lake/sea checks (e.g. to build a port if a ; city has coast within its work area, even if not adjacent to the sea). -; Note that this does not affect Aqueducts, which still require adjacency to a fresh water source. +; Note that this does not affect Aqueducts or Coastal Fortresses, which still require direct city adjacency. ; workers_can_enter_coast: Allow workers to move onto coast tiles without embarking. ; max_contiguous_bridge_districts: Maximum number of contiguous bridge district tiles allowed in a line (0 = no limit). ; max_contiguous_canal_districts: Maximum number of contiguous canal district tiles allowed in a line (0 = no limit). diff --git a/injected_code.c b/injected_code.c index 2f7a645a..8add187a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -20898,6 +20898,13 @@ int __fastcall patch_City_get_largest_adjacent_sea_within_work_area (City * this) { if (is->current_config.enable_districts && is->current_config.expand_water_tile_checks_to_city_work_area) { + int improv_id = is->current_evaluating_improve_id; + if ((improv_id >= 0) && (improv_id < p_bic_data->ImprovementsCount)) { + + // If Coastal Fortress, default to original logic (city must be next to coast) + if (p_bic_data->Improvements[improv_id].Naval_Bombard_Defence > 0) + return City_get_largest_adjacent_sea (this); + } int lake_size_threshold = 21; int largest_size = 0; int largest_continent_id = -1; @@ -20963,9 +20970,9 @@ patch_Map_impl_has_fresh_water_within_work_area (Map * this, int edx, int tile_x int improv_id = is->current_evaluating_improve_id; if ((improv_id >= 0) && (improv_id < p_bic_data->ImprovementsCount)) { - // If an Aqueduct, default to original logic + // If an Aqueduct, default to original logic (city must be next to coast) if ((p_bic_data->Improvements[improv_id].ImprovementFlags & ITF_Allows_City_Level_2) != 0) - return Map_impl_has_fresh_water (this, __, tile_x, tile_y); + return Map_impl_has_fresh_water(this, __, tile_x, tile_y); } if (patch_Map_impl_is_near_river_within_work_area (this, __, tile_x, tile_y, 1)) return true; @@ -21082,6 +21089,11 @@ patch_City_can_build_improvement (City * this, int edx, int i_improv, bool apply { is->current_evaluating_improve_id = i_improv; + char ss[200]; + snprintf (ss, sizeof ss, "patch_City_can_build_improvement:evaluating improvement %s (%d)\n", + p_bic_data->Improvements[i_improv].Name.S, i_improv); + (*p_OutputDebugStringA) (ss); + // First defer to the base game's logic bool base = City_can_build_improvement (this, __, i_improv, apply_strict_rules); if (! base) return false; From 3ffe97414fcad40e9e0e6a1651f300ac1d3e5b31 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 30 Jan 2026 13:00:52 -0800 Subject: [PATCH 315/356] Add much better art for newtons university --- Art/Districts/1200/Wonders_2.PCX | Bin 40012 -> 39585 bytes Text/c3x-labels.txt | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index 82137a44338d9bfbcd51a225929c3d9d7f57a8d6..601a9ee9b48adf913052fe7df3b87c5399f09483 100644 GIT binary patch delta 3317 zcmXw5ZEPFYl{Mc??6{A0M21$=ajBLG1-ip(yo@ZCRU0ka8CZ;|0wGM+!cO6l8@J}h z75te|od!*dqS$P^ z*>nT+pJp$M?4KbqbMKjZ&pqeP+kOjOyMgK-Jh8c#%zgaYpTByL?xo{Qh-d$7eZ})N z?;TeYCCnry%zR{B_9%x2ZY~|b@xIU(rO5wSzxKTN^p5-eg?TJUeL)f-kcp8GtvPgR ze8**{;sQ=F%mhhbQV@Rdht|I#{h5KA5_pKkk}$fmAmSq#ZQ;MI=gcHkxgNRSZT zgd`kD%2;C=j=F!c9&*;_2JW|SN(@Uk1$d>83utjE%IoyK5_A8{dX2Zgc-!T^U!(5J zhx>?^cmNQBd;n)990+{ib_sFBf~ydijZ7?6w=hXAw?Q_b}rb!UDrWa5B7a zN!GYb!}Ka99T@-aZtg8aP)b7DW9!uZKODd9rg18cq2)A!EFMDx-o*KUj3A!n5;onO zquBlUF3nCDVPk z9L8vV)TSixyO#Cj-dBT>5mEq)f++My9E34LOh8Pw7d4gCHHB_oo`m}XMq>-852--z zP00DB_2}To-?;6fl!$>h5ylaea4;~#I5ZKSM#6k&=~%%`7?d>>=ko0MUwdMBenc|% zF=#X(3g}V$vzYUI9p(vh6+{LTh35j#0+}(yxI+iN&@9iJigQ5)7Sz)v8aeAv!=Kjl$moWeqXgd>PSA&85ZzZ9Ke$RCSv>rA6}BDAGRl+`m`qnoC+9_;4z3(C@!M&`(>#?hs$G! zLdlcO7^X*>{zxiZuTI0(il5Yd=1I?~2QKGZ>uBCE5=3r#iEek3{(`-zD!_rE8ALgRg%}!n z2t$p36$!(^DC5g0bAnT$j$tykPKP0)Q-L^?yu~#mubwVH0 z&7JnM!qc9U58Z+inw(KN*dgbPYC^5i%?m}v$rqHO(K8qGu`~~o2ElI$(Q06l{(RJ(bV*d*-SY1fZD<9X?ks~49hXVWKr3IBnxN-E{Bv$8r%N@dk9R@M5* zIWQ(4$O_CWAa2oLJfajGlM`nACQu2WudB>v1SVk{!}h`8VNdpvOO}7IC_fjTNk>DX z>NoAYp*SmR3Nsj~;;tG28HwPN&5`K@JSC%ZN0m&cXDB%G;xPlrCu;_=t4k^!ZWzuR z_1dsqo_NZ0=8?NJzof~UoRC=xuupo8O2be{*&wEphv@O9p93lp23?O9MG%NBPM=0B zLv+=sU8|NZ9IMg6hEC`vQR(KJ*VLXvw>z!Iwff!mA3q=RXz#ccR$A0zbHYqa&df&y zF&j3W8)e`SHgD;Qx{(tYN+8}h+62&sZ@*GRT2RhVX68&{s?B<%!;A_H&NkkP!Afd$ z=r-%Mnr}4Pcdd&d+q2J_c=jK(yYAOlN>`R*dbXUNOB`cr_=S`>OKc#Nlu}AvUNKq( z6rX131bnM7pG$J7u}hp>ZDv(gpo4rt?K%)&vsWkVpLG?xLw9?0tJU3LZ#0JOkDh(T z^X)q>X7j7XvZj~yxmiE{5;Q0#U|Z8k`SB#M=6g!$wE!~1-5=g_n-W411a zO{46k?>1@$Aj>pr>*R)FYpcD!L$|t(D;vv0>+i#FLNCJPk~VP1=HscwOi|7%(;*H_ zt#XTW%U+^Pb+j_6nFe8%!;y@m9zNtn!oE~8v;Q0&H0M;iMphazu<>faMqISEPIup& zC3M%W(=FO(8$asShwa@--t%`mZUxWBC0Wl-CHykDC5`=Nu_LKAeV-Jvx~X5RAa!C~ z?-j;T3TH&M156R~LYOqxR7Zy;X>eFEYs8?tbo)BnqHN>Jk0=}dIN zIT}{IQ*Sdw1D2?l$$2OJmT0?}ggP zvD>hs*+%!u@?GoIqkn-luF6NHnZ!693cY%QGt%qMD+`*_V-C4=Iwy?B#N34752+ox zRnl!@iz?~nRfqKGW-qT06UA~px?N|$2W0?y?~5Ot9Jp#AIq6I^SqT|X>MZl(40&Et zC-td$^tX@bSgVRnvN9gwJSrbHR-)Ms80ObR^{bNFJEjC(> z+SR*O>PvUN0UayiN-2+wr0tlj)zo9*n_11DQ)3CgkxGP3x(z;G9SvH|PteWf8y))C zc@^-s?AqHk=bm+Ts+%0RrThw4NW$4nWi3!K%gTK%UX^&pLn1j_o&FZezSV%2Zo{4j zH8z@SXRqJ0e6dSk8n}jb0?rxdFB-bcatm@!pI$mE&f2hY6CgQA+N!gy?i=;SeT&8Z zDSXE*O2MN+S!Yn z-lLB@py`q0HkR2Mxo3U+d;+{ZH68uj(4CUv^IExXR17(L(I6F&4!hPUEv?#gH|dl14-EN(Qq4=g+rePZY;FF&vrW}b`x*8OnrqrLZBckg$fTzA~hpQgN_~g3n(#Jd25LD;~zQ=)6m&X&D+?C)3C8UsV`-^#ikIpcFCr$A}~hw(n%yO*F!Kg zU?2ht65CvW2EDu+jv9;Q)vP}xt!M11-*l`gA zL0j}qw`Vp%Uj#`!_xya{cfWJD_vUW?!`$Hg2i`pS%fG*1TR;DsUz~k-|3vFr2IC%B z|2Z^UpZo9+Wqo2Pn&oqFY|oG1AAY0t!bg56HAnFnXJ9bj>sr8mHau#5{>*JjXf0J6 zEDntO+_vnl!n~gie|PSsGsluB4fTn|01s-+BbriC_TI3Vd+oR1lG?)jfus)eou1_} zOW{^q36(hm>hQhc>HEISo<@MNa@R7e&0&$eSl^s|^?0cZLi1|MU;>Y_kMdkb>9n zf*=WvqO+bsVcj!%2ah>*(-S=vQElXOR+RuVu#R(k_|*qaY|soq9IP1G<1w(1bbyhL zf>9>D+P6)Y*rws|#ooF`7*Nb|R9yBW1Z(_^Na3&?BL-)1*3^P)2 ze9u|(SmB>)lQ|kvlEc^sg;<1(7|dzg^_}{-3LwrID74!)>AG*}C3PCN+JSY)AGaOhQ)1|d zibFj9a$MM++0oQ?;@qs3Klotli>mf38XT|jZ9rWXj6_i6ZagkSq7kF*nmvygIQt5A zt0{Hk&v^(lSCA|Q7IR`=0HcI?bBZs*n^pN6A8aw4(sTUSiVKHzbn0uQuB*DHBIcs- zLY7;^u&7CUnS&cc?+ z3dD(WSbg!s545}=NN|XP{Tiq++U8}EF2LqeWb!@9K2XFZN(gkP%Wg9YG9A%XgwL%HKJQ^+ItMdVKt}S-}*ATkkO^O=6Ipv zj!U6>ap}8kv6u7uc)Tmh`i{tODz$AIZ4C%@z{3VTjuXTd(N%>SCe`R>u43a{kN392 z#V8d0!|LTn9&UMeW;Vi%88>bMaYo}Zf?gQsBHhMu*Me+4!t{i;K`}IHVe<`VK5$D% zIT)m)qs0)QYKn?I#oQ=Qw#xx_|BoMip!MY+NkgASbqzgso7jO~AkwnFUN<7GkLyV_ z$O1NiE^RBbtBr;nB7~+2 zojE%X!G!?|YA<;e3*7k*)<{xFigKKE+Zxdvn=GL%@z}FY0h=S^YBM&}E*QE>)sB%u zFB**Ua?IEBs&1O<>mU0_>nm^Lh5$`tSt8US^#C9BB|aII1H8U zF~9OOBH}hu1);5U4P<~&MHMOEtJXMO;-H)a$RR4zT8$28{6@02MTVqQO9jxdkkhp(GKTGudG ziRxo9uag_qv-%^g)pz7_v}9P0P8`)l?5`JBvt%^L7R&l>73AZ~BcXJRMzd@bsN;9E zxs^>z<>SKWp&%JGSSPnN#{xYM$}vKliS=Z0ul~81UwCeLzuF`pYF&6oUK#}nP(w8o zOSMH*5k>hYSc*dw^1P3m(sA?pE?#XaqMj`VP}b9Q4Qi1MvgvT+u{T;5htEC!^;do* z7evx`moL@wG`brub2|e!6r*MwyjtJg^G3J{Z8Qo-ks@ZdO&He`&b}7s1RX}KL%BBM zRIR78?x?A&s5Qn#mR~M5L99VGtva7L)4Fgh&w~iEWErFzVn|2T6|>KHJX$NZx3Yfy z?=ih0RmbXRVQk`MH!O6y++i@Dxy_~@z;-r>i+U0h*6C7J7aXqdZf#V(Aj!mZ_!pln zai%AqxUhO8&$|M8$?G>H>K|~U8>++8a(k2wqS-uHeie;@c1Vnt28OfRi_3~Fx+zZ! zC`WN}%BBJj(!djE-b>#J|d`Bf1O#PWe8CK)6O5YVjYG^lX0NSt%r5X6qlmN7S}rjBZapcT*tsFIDBlQO(chYW>rZ~C zkMDt;;+Bq^(S(EVL4e`0|eovr@lK&2Qw5@108jj1G+bHVaJ#oAO_c(TYVFdH&q8s z*TNZ&mB`R!MGnVV$jgabn1>_~ur(ci;py+au#e*H5QD0t3L8VYMmdhC8#EhT6^+yt z8#O?*V2}od*c{i_tB_r(Cz-&$&R$A-;p|d4-8ateOGQ_xdJWU4L}w{p0E4(<=Z0J` zHG9@jCra?nW}?csu$wDWdG^u<1ij_y(D>}eAIxr%tzN@{8p8O_zC;l%(5F>f?--gA z=0#a$V((u4iF|545woKpFYXWD{cMU)PJ@!ViLp2kceHybUwrym)x)yn(J0~RMkcQ0 zTL<#|!u+K~xWVhwVexx^{!}9=-fx7E_EK?(6P<%+vEHK^)?+vt!gX$GQH1E6)8S{H zdF@jNv-}~hJ2Sc|7cBZHLX&W~vVo~DEj*9!`ry)ZIDY2$(Io!onI}K7k57--#NKpt zu|s+GMLHi|xj2!}FW9R|6D*&f3_nNNnSGg}u8}s)6hB7QKkqIE$r>X2ZTh7%2goO? z6PI};(BI~tPlB~xx-lK9=8LDNNERwU507S|Ud6{jZ*!J4G#9j~1Zv{5D|$;VoKF@- zw%B`fxMM!~(J6`?53vX#K0EIwVG?X&zJE18`P%Hvy-yeC7wje6M8RN${r=+QmnV0n zJo%4b|2&c(f9bDY7~H(~um3-j{GEVkOHAc2PyXxVU+~+zzy5hs*6$qM`} Date: Fri, 30 Jan 2026 13:08:00 -0800 Subject: [PATCH 316/356] Add better newtons university construction art --- Art/Districts/1200/Wonders_2.PCX | Bin 39585 -> 39216 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index 601a9ee9b48adf913052fe7df3b87c5399f09483..dfc692063297631922840f3964c1ed9cec2de14f 100644 GIT binary patch delta 3658 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 WAGcPSHt`Gh)AIXw?mzj#i~kR>EU}RQ delta 4119 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 Date: Fri, 30 Jan 2026 13:36:14 -0800 Subject: [PATCH 317/356] Touch up industrial zone art with lighter ground surrounding --- Art/Districts/1200/IndustrialZone.PCX | Bin 24779 -> 25026 bytes default.districts_config.txt | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Art/Districts/1200/IndustrialZone.PCX b/Art/Districts/1200/IndustrialZone.PCX index d997d01492d757e64325b83134e7a474d9bd1ed3..f463ddaa13265f9292f44819b4a5796c739d706e 100644 GIT binary patch delta 1223 zcmZ8gZD?C%6sBo*Q#WPMHYvSr`rT4wj1EQJkIuRjlTM_JAv1Hrm7!LsqAP*ngis@+9L)OjylHN3K8#C~HZ+xGYi&xqe&Ce3DxTxHZN_AO9^UtSJm;L} zJ#PxCWVFiIa648=Y`L#GR_WRm&t=B}NWrZM@mS+k`?T_g2seJI+$94_9^qGyROLlT zJ?G9-5{dq*U<{*pj}D37)T34NqQ2?8XMs%i|5L#z0`3xI#m7xY9BTz}z3DZ_PC>Yv z2b@rZk~rIZz8caC;<|0P4Qt|l^Xu(U%s)aB<08sP;GMw{1=b)b7Q9_fh(lR?;nk|4 zG$;P`HacY-=EUWeZYPu=Ee2YSADDq9SQj6)bhSbJ24%RfKPni<1m2||m64$U+=tYJ zXlw1Rsn5_Hn&c9jEl)WoJg_XFrL!8!84+lG;UJ{bw1Be&me5;2tA+m2Q2*#xb&yR` z5v6TD*7kXQeKo{A+*%ea$OkRDiR_qbJL^;-pA=8FKVQq;SyF|Vs#$ak?LhHf`zs2^ zXN{DYZ9jexOy}XBX%$(Ys*ac1!Q5jrA!j71%tI_yFo*NFOdMr&^eC=0t)o{=b)0I3 zG?*#c;K)J+=dpk{=?VCCMC^3*Dy{@=qbj_eL#>dt&qTQj7H|;@r1+kG2FkGdlbGo2 zt$|#een%*Z?>k>~7;*7u=Shba6VG;CacNPy$1@1C>ysKy(k^;=oYUQtF0eGT9gE!q z4Q?RfJ>e>QcpFzRZR_G0Rz^@scc|5zlNJz00DFRVxOkWZPK)?Xev=9edYGqx%& zyOR_z(>|`^8d{`=WZ#rNJ*fDU;l74CRkGd&B-D%gSm;zZqWdG`(}4!qoC)}9rAnCO ze)&aq`(${K8*@b;VQ?Bi8st=IJ!E)5@Mh5N;1}M#eO$wJ-Z%|ndf&I9urKhb9KOKs zonV{#{@6EzJjJBc$00L%@ofb*p-7nF@)!KWyj>=N`7lp)niQ1(%X*M>h%krST+qgK zyu$_0faTXCZ$(~hWL=E}X;D?y&NsQ4r%hA7|U>mh0aT=s& zEm+>q;HN3z2A8n$4sLM{BO_6$ga#VgF+Mxy_hl-y68qK%Q(?L zx+%k}wq-;hRMyowE9d8|rmeIBXC_XIu*g2(|L;Az3kkaU@x0ITKJV8#YCt!x=pMw3 zT$CQcs_`cGk&*^`OlS3+`;i;AnQwCEg&OdiZ}H#gOe<7d3iM53=(5xso5D1PMC)-r z#{jMqELjTmPp83@+F+`BI!(iD#-BI?sNipci>U?t*C#zQ@G$knrV!GO>O0*~m)5{_ zx|z?0k^z3Fo#Z4#80yn4Fq>}EyW5~Y{ZyvA?d3oF2Jt%;A5@Qt*V>0v$jZpmw@4sm zl;}4{T#GaX_cJVfduM9}?8&qy_SH<%D-06+%-F}pO^%sRn^|U<&>B;Z}C7E(Gh@CHxEegm5UQ z+N24hxj4f(gYz^bz?~c$KS?o!S2WeElQj`Qw&VuqaRDa@uGk6_HXKlGhm#d1gY2Viasd}{oWQs<|J*ZXjiSrn>nxRI zUt&^#R9W?TZ{I8Rh)%J?AE0uf%kGu^vd3ZQ^~p6-k*FSaJl1;S_#@6mT*6WEsPMZeFPqO3l4I-0bjPN4G3u6iqitWdV4G4j8{6t1zF#wA?F z5t8k9Jzj_Ns_JSSBPhxjOdD~d=o;OUu?w=Okwszr66Z3mu)Zk!oBhYl)8y(B+XR#L wHWBt0m2Udeq(rv{v5h!JEBKjBQbJ<)JLb%3>vT(RNaYkrPg)vG*)?$BFCXzA@Bjb+ diff --git a/default.districts_config.txt b/default.districts_config.txt index cfdb804b..107edfb7 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -414,7 +414,7 @@ vary_img_by_culture = 1 custom_height = 84 advance_prereqs = Industrialization dependent_improvs = "Coal Plant", "Hydro Plant", "Solar Plant", "Nuclear Plant" -buildable_on = desert,plains,grassland,tundra,floodplain,hills,river +buildable_on = desert,plains,grassland,tundra,floodplain,hills defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 0 From f0855e613923bc94bc097d6770a75b0499cdfc29 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 30 Jan 2026 14:03:27 -0800 Subject: [PATCH 318/356] Add missing interpolation sign in "Needs neighborhood to grow" message popup --- injected_code.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/injected_code.c b/injected_code.c index 8add187a..e8aa64a8 100644 --- a/injected_code.c +++ b/injected_code.c @@ -13507,7 +13507,7 @@ maybe_show_neighborhood_growth_warning (City * city) char msg[160]; char const * city_name = city->Body.CityName; - snprintf (msg, sizeof msg, "%s %s %s", + snprintf (msg, sizeof msg, "%s %s %s %s", city_name, is->c3x_labels[CL_REQUIRES], is->district_configs[NEIGHBORHOOD_DISTRICT_ID].display_name, From 19f93d23bb7c3a5695650fe0a7b3c08f05769fc6 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 30 Jan 2026 14:51:42 -0800 Subject: [PATCH 319/356] Touch up hoover dam mountains to blend better --- Art/Districts/1200/Wonders_3.PCX | Bin 23179 -> 21460 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Wonders_3.PCX b/Art/Districts/1200/Wonders_3.PCX index b399d7950640dd96973f0e3b2c0969ec272fa7e4..5f646a1704db3ca4520d436d5e6f3cc363fe37b2 100644 GIT binary patch literal 21460 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 S4}CO_&wu>U$7i%C^83FWWAsA+ literal 23179 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 iOpL$TF*7=F<=@VIfKSa%{r>XUwd Date: Fri, 30 Jan 2026 15:03:51 -0800 Subject: [PATCH 320/356] Draw extra south great wall --- injected_code.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/injected_code.c b/injected_code.c index e8aa64a8..9e9f258c 100644 --- a/injected_code.c +++ b/injected_code.c @@ -32081,6 +32081,9 @@ draw_great_wall_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * ma if (wall_sw) draw_district_on_map_or_canvas(&sprites[DIR_SW], map_renderer, pixel_x, pixel_y); if (wall_se) draw_district_on_map_or_canvas(&sprites[DIR_SE], map_renderer, pixel_x, pixel_y); + + if (wall_s && !wall_sw && !wall_se) + draw_district_on_map_or_canvas(&sprites[DIR_S], map_renderer, pixel_x, pixel_y); } void From 8182b9b1f200cf8d963a86e4aa8fcd3880acce18 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 30 Jan 2026 15:14:57 -0800 Subject: [PATCH 321/356] Resolving merge issues --- C3X.h | 1 + injected_code.c | 74 ++++++++++++++++++++++++++++++++----------------- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/C3X.h b/C3X.h index b4966ff7..4bf5fc8e 100644 --- a/C3X.h +++ b/C3X.h @@ -182,6 +182,7 @@ struct c3x_config { bool limited_railroads_work_like_fast_roads; int limit_units_per_tile[3]; // Limits for land, sea, and air units respectively bool exclude_cities_from_units_per_tile_limit; + struct table exclude_types_from_units_per_tile_limit; bool enable_free_buildings_from_small_wonders; bool enable_stack_unit_commands; bool skip_repeated_tile_improv_replacement_asks; diff --git a/injected_code.c b/injected_code.c index 9e9f258c..46708886 100644 --- a/injected_code.c +++ b/injected_code.c @@ -665,6 +665,8 @@ reset_to_base_config () for (int n = 0; n < ARRAY_LEN (cc->limit_units_per_tile); n++) cc->limit_units_per_tile[n] = 0; + table_deinit (&cc->exclude_types_from_units_per_tile_limit); + for (int n = 0; n < COUNT_PERFUME_KINDS; n++) stable_deinit (&cc->perfume_specs[n]); @@ -2371,6 +2373,9 @@ load_config (char const * file_path, int path_is_relative_to_mod_dir) else handle_config_error (&p, CPE_BAD_VALUE); } + } else if (slice_matches_str (&p.key, "exclude_types_from_units_per_tile_limit")) { + if (! read_unit_type_list (&value, &unrecognized_lines, &cfg->exclude_types_from_units_per_tile_limit)) + handle_config_error (&p, CPE_BAD_VALUE); } else if (slice_matches_str (&p.key, "limit_defensive_retreat_on_water_to_types")) { if (! read_unit_type_list (&value, &unrecognized_lines, &cfg->limit_defensive_retreat_on_water_to_types)) handle_config_error (&p, CPE_BAD_VALUE); @@ -19425,8 +19430,10 @@ patch_City_get_turns_to_build (City * this, int edx, enum City_Order_Types order } bool -is_below_stack_limit (Tile * tile, int civ_id, enum UnitTypeClasses class) +is_below_stack_limit (Tile * tile, int civ_id, int type_id) { + enum UnitTypeClasses class = p_bic_data->UnitTypes[type_id].Unit_Class; + int stack_limit = is->current_config.limit_units_per_tile[class]; if (stack_limit <= 0) return true; @@ -19435,14 +19442,19 @@ is_below_stack_limit (Tile * tile, int civ_id, enum UnitTypeClasses class) get_city_ptr (tile->CityID) != NULL) return true; + if (itable_look_up_or (&is->current_config.exclude_types_from_units_per_tile_limit, type_id, 0)) + return true; + FOR_UNITS_ON (uti, tile) { // If there is a foreign unit on the tile then consider it as being below the stack limit. This ensures that the stack limit doesn't // block combat between players. if (uti.unit->Body.CivID != civ_id) return true; + int uti_type_id = uti.unit->Body.UnitTypeID; if ((uti.unit->Body.Container_Unit < 0) && - (class == p_bic_data->UnitTypes[uti.unit->Body.UnitTypeID].Unit_Class)) { + (class == p_bic_data->UnitTypes[uti_type_id].Unit_Class) && + ! itable_look_up_or (&is->current_config.exclude_types_from_units_per_tile_limit, uti_type_id, 0)) { stack_limit -= 1; if (stack_limit <= 0) return false; @@ -19532,11 +19544,11 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, } // Apply unit count per tile limit - enum UnitTypeClasses class = p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class; - if ((base_validity == AMV_OK) && (is->current_config.limit_units_per_tile[class] > 0)) { + int type_id = this->Body.UnitTypeID; + if ((base_validity == AMV_OK) && (is->current_config.limit_units_per_tile[p_bic_data->UnitTypes[type_id].Unit_Class] > 0)) { int nx, ny; get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, neighbor_index, &nx, &ny); - if (! is_below_stack_limit (tile_at (nx, ny), this->Body.CivID, class)) + if (! is_below_stack_limit (tile_at (nx, ny), this->Body.CivID, type_id)) return AMV_CANNOT_PASS_BETWEEN; } @@ -19683,7 +19695,7 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr } // Apply unit count per tile limit - if ((unit != NULL) && ! is_below_stack_limit (tile_at (to_x, to_y), unit->Body.CivID, p_bic_data->UnitTypes[unit->Body.UnitTypeID].Unit_Class)) + if ((unit != NULL) && ! is_below_stack_limit (tile_at (to_x, to_y), unit->Body.CivID, unit->Body.UnitTypeID)) return -1; // Apply trespassing restriction @@ -26715,18 +26727,27 @@ bool __fastcall patch_Unit_can_disembark_anything (Unit * this, int edx, int tile_x, int tile_y) { Tile * this_tile = tile_at (this->Body.X, this->Body.Y); + Tile * target_tile = tile_at (tile_x, tile_y); bool base = Unit_can_disembark_anything (this, __, tile_x, tile_y); // Apply units per tile limit - if (base && ! is_below_stack_limit (tile_at (tile_x, tile_y), this->Body.CivID, UTC_Land)) - return false; + if (base) { + bool stack_limited_for_all = true; + FOR_UNITS_ON (uti, this_tile) + if ((uti.unit->Body.Container_Unit == this->Body.ID) && is_below_stack_limit (target_tile, this->Body.CivID, uti.unit->Body.UnitTypeID)) { + stack_limited_for_all = false; + break; + } + if (stack_limited_for_all) + return false; + } // Apply trespassing restriction. First check if this civ may move into (tile_x, tile_y) without trespassing. If it would be trespassing, then // we can only disembark anything if this transport has a passenger that can ignore the restriction. Without this check, the game can enter an // infinite loop under rare circumstances. - else if (base && - is->current_config.disallow_trespassing && - check_trespassing (this->Body.CivID, this_tile, tile_at (tile_x, tile_y))) { + if (base && + is->current_config.disallow_trespassing && + check_trespassing (this->Body.CivID, this_tile, target_tile)) { bool any_exempt_passengers = false; FOR_UNITS_ON (uti, this_tile) if ((uti.unit->Body.Container_Unit == this->Body.ID) && is_allowed_to_trespass (uti.unit)) { @@ -27063,7 +27084,7 @@ patch_Tile_check_water_for_retreat_on_defense (Tile * this) // Check stack limit if ((! retreat_blocked) && (defender != NULL) && - ! is_below_stack_limit (this, defender->Body.CivID, p_bic_data->UnitTypes[defender->Body.UnitTypeID].Unit_Class)) + ! is_below_stack_limit (this, defender->Body.CivID, defender->Body.UnitTypeID)) retreat_blocked = true; // The return from this call is only used to filter the given tile as a possible retreat destination based on whether it's water or not @@ -27281,7 +27302,7 @@ patch_Leader_spawn_unit (Leader * this, int edx, int type_id, int tile_x, int ti Tile * district_tile = get_completed_district_tile_for_city (spawn_city, AERODROME_DISTRICT_ID, &district_x, &district_y); if ((district_tile != NULL) && (district_tile != p_null_tile) && (district_tile->Territory_OwnerID == this->ID) && - is_below_stack_limit (district_tile, this->ID, type->Unit_Class)) { + is_below_stack_limit (district_tile, this->ID, type_id)) { spawn_x = district_x; spawn_y = district_y; } @@ -27298,7 +27319,7 @@ patch_Leader_spawn_unit (Leader * this, int edx, int type_id, int tile_x, int ti Tile * district_tile = get_completed_district_tile_for_city (spawn_city, PORT_DISTRICT_ID, &district_x, &district_y); if ((district_tile != NULL) && (district_tile != p_null_tile) && (district_tile->Territory_OwnerID == this->ID) && - is_below_stack_limit (district_tile, this->ID, type->Unit_Class)) { + is_below_stack_limit (district_tile, this->ID, type_id)) { spawn_x = district_x; spawn_y = district_y; } @@ -29514,7 +29535,7 @@ bool __fastcall patch_Unit_check_airdrop_target (Unit * this, int edx, int tile_x, int tile_y) { return Unit_check_airdrop_target (this, __, tile_x, tile_y) && - is_below_stack_limit (tile_at (tile_x, tile_y), this->Body.CivID, p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class) && + is_below_stack_limit (tile_at (tile_x, tile_y), this->Body.CivID, this->Body.UnitTypeID) && ! is_airdrop_trespassing (this, tile_x, tile_y); } @@ -29573,7 +29594,7 @@ patch_Unit_check_airlift_target (Unit * this, int edx, int tile_x, int tile_y) if (! allowed) return false; - return is_below_stack_limit (tile, this->Body.CivID, p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class); + return is_below_stack_limit (tile, this->Body.CivID, this->Body.UnitTypeID); } void __fastcall @@ -29602,11 +29623,14 @@ patch_Unit_airlift (Unit * this, int edx, int tile_x, int tile_y) int __fastcall patch_City_count_airports_for_ai_airlift_target (City * this, int edx, enum ImprovementTypeFlags airport_flag) { + // When this function is called, the AI unit being moved is stored in register ESI. + Unit * unit; + __asm__ __volatile__("mov %%esi, %0" : "=r" (unit)); + int tr = City_count_improvements_with_flag (this, __, airport_flag); - // Check the stack limit here. If the city's tile is at the limit, return that it has no airport so the AI can't airlift there. The two calls - // to this patch function come from the off. & def. unit AI, so only for land units. - if ((tr > 0) && ! is_below_stack_limit (tile_at (this->Body.X, this->Body.Y), this->Body.CivID, UTC_Land)) + // Check the stack limit here. If the city's tile is at the limit, return that it has no airport so the AI can't airlift there. + if ((tr > 0) && ! is_below_stack_limit (tile_at (this->Body.X, this->Body.Y), this->Body.CivID, unit->Body.UnitTypeID)) return 0; else @@ -29620,7 +29644,7 @@ patch_Unit_ai_eval_airdrop_target (Unit * this, int edx, int tile_x, int tile_y) // Prevent the AI from airdropping onto tiles in violation of the stack limit or trespassing restriction if ((tr > 0) && - ((! is_below_stack_limit (tile_at (tile_x, tile_y), this->Body.CivID, p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class)) || + ((! is_below_stack_limit (tile_at (tile_x, tile_y), this->Body.CivID, this->Body.UnitTypeID)) || is_airdrop_trespassing (this, tile_x, tile_y))) tr = 0; @@ -29630,7 +29654,7 @@ patch_Unit_ai_eval_airdrop_target (Unit * this, int edx, int tile_x, int tile_y) bool __fastcall patch_Unit_find_telepad_on_tile (Unit * this, int edx, int x, int y, bool show_selection_popup, Unit ** out_unit_telepad) { - if (! is_below_stack_limit (tile_at (x, y), this->Body.CivID, p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class)) { + if (! is_below_stack_limit (tile_at (x, y), this->Body.CivID, this->Body.UnitTypeID)) { *out_unit_telepad = NULL; return false; } else @@ -29645,7 +29669,7 @@ patch_Unit_ai_go_to_capital (Unit * this) // Block going to capital if the capital's tile is at the stack limit. This stops the AI from airlifting there (would violate the limit) and // saves it from trying to pathfind there. if ((capital != NULL) && - ! is_below_stack_limit (tile_at (capital->Body.X, capital->Body.Y), this->Body.CivID, p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class)) + ! is_below_stack_limit (tile_at (capital->Body.X, capital->Body.Y), this->Body.CivID, this->Body.UnitTypeID)) return false; return Unit_ai_go_to_capital (this); @@ -29671,7 +29695,7 @@ patch_Unit_is_in_rebase_range (Unit * this, int edx, int tile_x, int tile_y) in_range = ((x_dist + y_dist) >> 1) <= (op_range * is->current_config.rebase_range_multiplier); } - return in_range && is_below_stack_limit (tile_at (tile_x, tile_y), this->Body.CivID, p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class); + return in_range && is_below_stack_limit (tile_at (tile_x, tile_y), this->Body.CivID, this->Body.UnitTypeID); } bool __fastcall @@ -29697,7 +29721,7 @@ patch_Unit_check_rebase_target (Unit * this, int edx, int tile_x, int tile_y) // Perform range check bool in_range = patch_Unit_is_in_rebase_range (this, __, tile_x, tile_y); if (in_range) { - return is_below_stack_limit (tile, this->Body.CivID, p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class); + return is_below_stack_limit (tile, this->Body.CivID, this->Body.UnitTypeID); } } } @@ -29716,7 +29740,7 @@ patch_Unit_check_rebase_target (Unit * this, int edx, int tile_x, int tile_y) // 6 is the game's standard value, so fall back on the base game logic in that case if (is->current_config.rebase_range_multiplier == 6) { - return Unit_check_rebase_target (this, __, tile_x, tile_y) && is_below_stack_limit (tile_at (tile_x, tile_y), this->Body.CivID, p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class); + return Unit_check_rebase_target (this, __, tile_x, tile_y) && is_below_stack_limit (tile_at (tile_x, tile_y), this->Body.CivID, this->Body.UnitTypeID); // Otherwise, we have to redo the range check. Unlike Unit::is_in_rebase_range, the base method here does more than just check the range so we // want to make sure to call it even if we determine that the target is in range. In that case, set the range to unlimited temporarily so the From 5d47b1f23bcff5ea12434710153e9ba7f825e57d Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 30 Jan 2026 17:00:37 -0800 Subject: [PATCH 322/356] Remove unnecessary debug line --- Notes/district_todos.md | 2 +- injected_code.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 8fc03dff..49be57ac 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,4 +1,4 @@ - - Make sure workers can build roads and railroads on bridges + - Add buildable_on_overlay - Districts: - Municipal District (ZergMazter doing art) - Central Rail Hub (ZergMazter doing art) diff --git a/injected_code.c b/injected_code.c index 46708886..83ef5a71 100644 --- a/injected_code.c +++ b/injected_code.c @@ -32304,7 +32304,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc int construct_windex = -1; Sprite * wsprite = NULL; - // Completed wonder + // Completed if (info->state == WDS_COMPLETED) { int windex = info->wonder_index; if ((windex < 0) || (windex >= is->wonder_district_count)) @@ -32428,7 +32428,6 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc void __fastcall patch_Map_Renderer_m12_Draw_Tile_Buildings(Map_Renderer * this, int edx, int visible_to_civ_id, int tile_x, int tile_y, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - //*p_debug_mode_bits |= 0xC; if (! is->current_config.enable_districts && ! is->current_config.enable_natural_wonders) { Map_Renderer_m12_Draw_Tile_Buildings(this, __, visible_to_civ_id, tile_x, tile_y, map_renderer, pixel_x, pixel_y); return; From d435fa89865baa9a200220f6ac1724560af61c65 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Fri, 30 Jan 2026 17:11:08 -0800 Subject: [PATCH 323/356] Remove redundant rendering pixel offset code --- injected_code.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/injected_code.c b/injected_code.c index 83ef5a71..acc47d63 100644 --- a/injected_code.c +++ b/injected_code.c @@ -32317,8 +32317,6 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc align_district_with_river (tile, &draw_pixel_x, &draw_pixel_y, &river_dir); bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alternative_direction_image (tile_x, tile_y, territory_owner_id, wcfg); - int offset_x = draw_pixel_x + cfg->x_offset; - int offset_y = draw_pixel_y + cfg->y_offset; wsprite = (use_alt_dir && (set->alt_dir_img.vtable != NULL)) ? &set->alt_dir_img : &set->img; draw_district_on_map_or_canvas(wsprite, map_renderer, offset_x, offset_y); @@ -32331,8 +32329,6 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc struct wonder_district_config * wcfg = &is->wonder_district_configs[construct_windex]; struct wonder_district_image_set * set = &is->wonder_district_img_sets[construct_windex]; - int offset_x = draw_pixel_x + cfg->x_offset; - int offset_y = draw_pixel_y + cfg->y_offset; bool use_alt_dir = wcfg->enable_img_alt_dir && wonder_should_use_alternative_direction_image (tile_x, tile_y, territory_owner_id, wcfg); wsprite = (use_alt_dir && (set->alt_dir_construct_img.vtable != NULL)) ? &set->alt_dir_construct_img : &set->construct_img; From a83c7aa75edb1c46c5422749b44612f0ccac8868 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 31 Jan 2026 09:17:11 -0800 Subject: [PATCH 324/356] Refactor and add separate distrit config support for overlays vs tile types --- C3X.h | 31 ++- default.districts_config.txt | 14 +- default.districts_wonders_config.txt | 29 +-- injected_code.c | 288 ++++++++++++++++++++++++--- 4 files changed, 319 insertions(+), 43 deletions(-) diff --git a/C3X.h b/C3X.h index 4bf5fc8e..b6ae221a 100644 --- a/C3X.h +++ b/C3X.h @@ -710,6 +710,8 @@ struct district_config { char const * img_paths[10]; unsigned int buildable_square_types_mask; unsigned int buildable_adjacent_to_square_types_mask; + unsigned int buildable_on_overlays_mask; + unsigned int buildable_adjacent_to_overlays_mask; bool buildable_adjacent_to_allows_city; bool allow_multiple; bool vary_img_by_era; @@ -761,6 +763,8 @@ struct district_config { int buildable_adjacent_to_district_id_count; bool has_buildable_adjacent_to; bool has_buildable_adjacent_to_districts; + bool has_buildable_on_overlays; + bool has_buildable_adjacent_to_overlays; char const * buildable_by_civs[32]; int buildable_by_civ_count; bool has_buildable_by_civs; @@ -790,8 +794,12 @@ struct wonder_district_config { img_alt_dir_row, img_alt_dir_column; unsigned int buildable_square_types_mask; + unsigned int buildable_on_overlays_mask; + unsigned int buildable_adjacent_to_overlays_mask; bool enable_img_alt_dir; bool is_dynamic; + bool has_buildable_on_overlays; + bool has_buildable_adjacent_to_overlays; }; enum square_type_extras { @@ -802,6 +810,19 @@ enum square_type_extras { SQ_SNOW_MOUNTAIN }; +enum district_overlay_mask_bits { + DOM_MINE = 1u << 0, + DOM_IRRIGATION = 1u << 1, + DOM_FORTRESS = 1u << 2, + DOM_BARRICADE = 1u << 3, + DOM_OUTPOST = 1u << 4, + DOM_RADAR_TOWER = 1u << 5, + DOM_JUNGLE = 1u << 6, + DOM_FOREST = 1u << 7, + DOM_SWAMP = 1u << 8, + DOM_RIVER = 1u << 9 +}; + struct natural_wonder_district_config { char const * name; char const * img_path; @@ -851,7 +872,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_WonderDistrict, .name = "Wonder District", .tooltip = "Build Wonder District", .display_name = "Wonder District", .advance_prereqs = {0}, .advance_prereq_count = 0, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"WonderDistrict.pcx"}, - .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Coast)), + .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Coast) | (1 << SQ_Mountains)), .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 @@ -996,6 +1017,8 @@ struct parsed_district_definition { struct district_bonus_list defense_bonus_extras; unsigned int buildable_square_types_mask; unsigned int buildable_adjacent_to_square_types_mask; + unsigned int buildable_on_overlays_mask; + unsigned int buildable_adjacent_to_overlays_mask; bool buildable_adjacent_to_allows_city; char * buildable_by_civs[32]; int buildable_by_civ_count; @@ -1034,6 +1057,8 @@ struct parsed_district_definition { bool has_happiness_bonus; bool has_buildable_on; bool has_buildable_adjacent_to; + bool has_buildable_on_overlays; + bool has_buildable_adjacent_to_overlays; bool has_resource_prereq_on_tile; bool has_buildable_by_civs; bool has_buildable_by_war_allies; @@ -1076,6 +1101,8 @@ struct parsed_wonder_definition { int img_alt_dir_column; bool enable_img_alt_dir; unsigned int buildable_square_types_mask; + unsigned int buildable_on_overlays_mask; + unsigned int buildable_adjacent_to_overlays_mask; bool has_name; bool has_img_path; bool has_img_row; @@ -1088,6 +1115,8 @@ struct parsed_wonder_definition { bool has_img_alt_dir_column; bool has_enable_img_alt_dir; bool has_buildable_on; + bool has_buildable_on_overlays; + bool has_buildable_adjacent_to_overlays; }; struct parsed_natural_wonder_definition { diff --git a/default.districts_config.txt b/default.districts_config.txt index 107edfb7..2de61571 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -26,9 +26,11 @@ ; - resource_prereq_on_tile : Resource name required on the district tile. District cannot be built unless resource is on same tile. ; - wonder_prereqs : Comma-separated wonder names. District cannot be built unless all Wonders are built by same civ. ; - natural_wonder_prereqs : Comma-separated natural wonder names. District cannot be built unless all Natural Wonders are within civ's territory. - ; - buildable_on : Comma-separated square types: desert, plains, grassland, tundra, floodplain, hills, mountains, forest, jungle, swamp, volcano, - coast, sea, ocean, river, snow-forest, snow-mountains, snow-volcano, mine, irrigation, lake. + ; - buildable_on : Comma-separated square types: desert, plains, grassland, tundra, floodplain, hills, mountains, volcano, + coast, sea, ocean, snow-forest, snow-mountains, snow-volcano, lake, any. + ; - buildable_on_overlays : Comma-separated overlays: irrigation, mine, fortress, barricade, outpost, radar-tower, jungle, forest, swamp, river. ; - buildable_adjacent_to : Same as buildable_on, plus "city". + ; - buildable_adjacent_to_overlays : Same as buildable_on_overlays. ; - buildable_on_districts : Comma-separated district names (tile must already have a completed district of any of these types, which it would replace). ; - buildable_adjacent_to_districts : Comma-separated district names (adjacent tile must have a completed district of any of these types). ; - buildable_by_civs : Comma-separated civ names (e.g., Romans, Egyptians). @@ -232,7 +234,8 @@ vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereqs = Engineering dependent_improvs = -buildable_on = forest +buildable_on = any +buildable_on_overlays = forest defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 2 @@ -327,7 +330,7 @@ happiness_bonus = 0 #District name = Wonder District tooltip = Build Wonder District -buildable_on = desert,plains,grassland,tundra,floodplain,hills,coast +buildable_on = desert,plains,grassland,tundra,floodplain,hills,coast,mountains advance_prereqs = defense_bonus_percent = 0 allow_multiple = 1 @@ -464,7 +467,8 @@ btn_tile_sheet_column = 9 tooltip = Build Great Wall obsoleted_by = Metallurgy wonder_prereqs = "The Great Wall" -buildable_on = desert,plains,grassland,tundra,floodplain,mountains,forest,swamp,jungle,hills +buildable_on = desert,plains,grassland,tundra,floodplain,mountains,hills +buildable_on_overlays = forest,swamp,jungle draw_over_resources = 1 defense_bonus_percent = 50 allow_multiple = 1 diff --git a/default.districts_wonders_config.txt b/default.districts_wonders_config.txt index 01e8db71..3a1100bb 100644 --- a/default.districts_wonders_config.txt +++ b/default.districts_wonders_config.txt @@ -9,8 +9,10 @@ [ ; Wonder config fields (each Wonder block begins with "#Wonder") ; - name : Text (required). Must match the in-game Wonder improvement name. - ; - buildable_on : Comma-separated square types: desert, plains, grassland, tundra, floodplain, hills, mountains, forest, jungle, swamp, volcano, - coast, sea, ocean, river, snow-forest, snow-mountains, snow-volcano, mine, irrigation, any. + ; - buildable_on : Comma-separated square types: desert, plains, grassland, tundra, floodplain, hills, mountains, volcano, + coast, sea, ocean, snow-forest, snow-mountains, snow-volcano, any. + ; - buildable_on_overlays : Comma-separated overlays: irrigation, mine, fortress, barricade, outpost, radar-tower, jungle, forest, swamp, river. + ; - buildable_adjacent_to_overlays : Same as buildable_on_overlays. ; - img_path : Text. PCX filename (under Art/Districts/1200/). Defaults to Wonders.pcx if omitted. ; - img_construct_row : Number (required). Row index in the PCX for the "under construction" wonder art (0-based). ; - img_construct_column : Number (required). Column index in the PCX for the "under construction" wonder art (0-based). @@ -153,18 +155,19 @@ img_alt_dir_row = 3 img_alt_dir_column = 3 #Wonder -name = Hoover Dam -buildable_on = river -enable_img_alt_dir = 1 -img_path = Wonders_3.pcx -img_construct_row = 0 -img_construct_column = 0 -img_row = 0 -img_column = 1 +name = Hoover Dam +buildable_on = mountains +buildable_on_overlays = river +enable_img_alt_dir = 1 +img_path = Wonders_3.pcx +img_construct_row = 0 +img_construct_column = 0 +img_row = 0 +img_column = 1 img_alt_dir_construct_row = 0 img_alt_dir_construct_column = 2 -img_alt_dir_row = 0 -img_alt_dir_column = 3 +img_alt_dir_row = 0 +img_alt_dir_column = 3 #Wonder name = Apollo Program @@ -220,4 +223,4 @@ img_path = SmallWonders.pcx img_construct_row = 3 img_construct_column = 0 img_row = 3 -img_column = 1 \ No newline at end of file +img_column = 1 diff --git a/injected_code.c b/injected_code.c index acc47d63..97d2fc58 100644 --- a/injected_code.c +++ b/injected_code.c @@ -1969,6 +1969,37 @@ tile_matches_square_type_mask (Tile * tile, unsigned int mask) return false; } +bool +tile_matches_overlay_mask (Tile * tile, unsigned int mask) +{ + if ((tile == NULL) || (tile == p_null_tile) || (mask == 0)) + return false; + + unsigned int overlays = tile->vtable->m42_Get_Overlays (tile, __, 0); + if ((mask & DOM_MINE) && ((overlays & TILE_FLAG_MINE) != 0)) + return true; + if ((mask & DOM_IRRIGATION) && ((overlays & 0x00000008) != 0)) + return true; + if ((mask & DOM_FORTRESS) && ((overlays & 0x00000010) != 0)) + return true; + if ((mask & DOM_BARRICADE) && ((overlays & 0x10000000) != 0)) + return true; + if ((mask & DOM_OUTPOST) && ((overlays & 0x80000000) != 0)) + return true; + if ((mask & DOM_RADAR_TOWER) && ((overlays & 0x40000000) != 0)) + return true; + if ((mask & DOM_JUNGLE) && tile_matches_square_type (tile, SQ_Jungle)) + return true; + if ((mask & DOM_FOREST) && tile_matches_square_type (tile, SQ_Forest)) + return true; + if ((mask & DOM_SWAMP) && tile_matches_square_type (tile, SQ_Swamp)) + return true; + if ((mask & DOM_RIVER) && tile_matches_square_type (tile, SQ_RIVER)) + return true; + + return false; +} + bool read_natural_wonder_terrain_type (struct string_slice const * s, enum SquareTypes * out_type) { @@ -3780,6 +3811,11 @@ district_is_buildable_on_square_type (struct district_config const * cfg, Tile * if (! tile_matches_square_type_mask (tile, mask)) return false; + if (cfg->has_buildable_on_overlays) { + if (! tile_matches_overlay_mask (tile, cfg->buildable_on_overlays_mask)) + return false; + } + if (cfg->has_buildable_on_districts) { struct district_instance * inst = get_district_instance (tile); if (inst == NULL) @@ -3800,7 +3836,7 @@ district_is_buildable_on_square_type (struct district_config const * cfg, Tile * return false; } - if (cfg->has_buildable_adjacent_to || cfg->has_buildable_adjacent_to_districts) { + if (cfg->has_buildable_adjacent_to || cfg->has_buildable_adjacent_to_districts || cfg->has_buildable_adjacent_to_overlays) { int tile_x, tile_y; if (! tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y)) return false; @@ -3827,6 +3863,20 @@ district_is_buildable_on_square_type (struct district_config const * cfg, Tile * return false; } + if (cfg->has_buildable_adjacent_to_overlays) { + bool matches = false; + FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { + if (tai.n == 0) + continue; + if (tile_matches_overlay_mask (tai.tile, cfg->buildable_adjacent_to_overlays_mask)) { + matches = true; + break; + } + } + if (! matches) + return false; + } + if (cfg->has_buildable_adjacent_to_districts) { bool matches = false; FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { @@ -5789,7 +5839,33 @@ wonder_is_buildable_on_square_type (struct wonder_district_config const * cfg, T if ((cfg == NULL) || (tile == NULL) || (tile == p_null_tile)) return false; - return tile_matches_square_type_mask (tile, wonder_buildable_square_type_mask (cfg)); + if (! tile_matches_square_type_mask (tile, wonder_buildable_square_type_mask (cfg))) + return false; + + if (cfg->has_buildable_on_overlays) { + if (! tile_matches_overlay_mask (tile, cfg->buildable_on_overlays_mask)) + return false; + } + + if (cfg->has_buildable_adjacent_to_overlays) { + int tile_x, tile_y; + if (! tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y)) + return false; + + bool matches = false; + FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { + if (tai.n == 0) + continue; + if (tile_matches_overlay_mask (tai.tile, cfg->buildable_adjacent_to_overlays_mask)) { + matches = true; + break; + } + } + if (! matches) + return false; + } + + return true; } bool @@ -5798,8 +5874,11 @@ wonder_is_buildable_on_tile (Tile * tile, int wonder_improv_id) if ((tile == NULL) || (tile == p_null_tile)) return false; - unsigned int mask = wonder_buildable_mask_for_improvement (wonder_improv_id); - return tile_matches_square_type_mask (tile, mask); + int windex = find_wonder_config_index_by_improvement_id (wonder_improv_id); + if (windex < 0) + return tile_matches_square_type_mask (tile, district_default_buildable_mask ()); + + return wonder_is_buildable_on_square_type (&is->wonder_district_configs[windex], tile); } int @@ -7260,6 +7339,8 @@ init_parsed_district_definition (struct parsed_district_definition * def) def->ai_build_strategy = DABS_DISTRICT; def->buildable_square_types_mask = district_default_buildable_mask (); def->buildable_adjacent_to_square_types_mask = 0; + def->buildable_on_overlays_mask = 0; + def->buildable_adjacent_to_overlays_mask = 0; def->buildable_adjacent_to_allows_city = false; } @@ -7577,21 +7658,28 @@ parse_buildable_square_type_mask (struct string_slice const * value, } allow_city = true; entry_count += 1; - } else if (slice_matches_str (&item_slice, "mine")) { - mask |= district_buildable_mine_mask_bit (); - entry_count += 1; - } else if (slice_matches_str (&item_slice, "irrigation")) { - mask |= district_buildable_irrigation_mask_bit (); - entry_count += 1; } else if (slice_matches_str (&item_slice, "lake")) { mask |= district_buildable_lake_mask_bit (); entry_count += 1; } else { enum SquareTypes parsed; if (read_square_type_value (&item_slice, &parsed)) { - if (parsed == (enum SquareTypes)SQ_INVALID) + if ((parsed == SQ_RIVER) || + (parsed == SQ_Forest) || + (parsed == SQ_Jungle) || + (parsed == SQ_Swamp)) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %.*s (invalid %s entry)", line_number, item_slice.len, item_slice.str, key_name); + err->text[(sizeof err->text) - 1] = '\0'; + free (value_text); + return false; + } + if (parsed == (enum SquareTypes)SQ_INVALID) { mask = all_square_types_mask (); - else + mask &= ~(square_type_mask_bit (SQ_Forest) | + square_type_mask_bit (SQ_Jungle) | + square_type_mask_bit (SQ_Swamp)); + } else mask |= square_type_mask_bit (parsed); entry_count += 1; } else { @@ -7628,6 +7716,97 @@ parse_buildable_square_type_mask (struct string_slice const * value, return true; } +bool +parse_buildable_overlay_mask (struct string_slice const * value, + unsigned int * out_mask, + struct error_line ** parse_errors, + int line_number, + char const * key_name) +{ + char * value_text = trim_and_extract_slice (value, 0); + unsigned int mask = 0; + int entry_count = 0; + + if (key_name == NULL) + key_name = "buildable_on_overlays"; + + if (value_text != NULL) { + char * cursor = value_text; + while (1) { + while (is_space_char (*cursor)) + cursor++; + + char * item_start = cursor; + while ((*cursor != '\0') && (*cursor != ',')) + cursor++; + + char * item_end = cursor; + while ((item_end > item_start) && is_space_char (item_end[-1])) + item_end--; + + struct string_slice item_slice = { .str = item_start, .len = (int)(item_end - item_start) }; + if (item_slice.len > 0) { + if (slice_matches_str (&item_slice, "mine")) { + mask |= DOM_MINE; + entry_count += 1; + } else if (slice_matches_str (&item_slice, "irrigation")) { + mask |= DOM_IRRIGATION; + entry_count += 1; + } else if (slice_matches_str (&item_slice, "fortress")) { + mask |= DOM_FORTRESS; + entry_count += 1; + } else if (slice_matches_str (&item_slice, "barricade")) { + mask |= DOM_BARRICADE; + entry_count += 1; + } else if (slice_matches_str (&item_slice, "outpost")) { + mask |= DOM_OUTPOST; + entry_count += 1; + } else if (slice_matches_str (&item_slice, "radar-tower")) { + mask |= DOM_RADAR_TOWER; + entry_count += 1; + } else if (slice_matches_str (&item_slice, "jungle")) { + mask |= DOM_JUNGLE; + entry_count += 1; + } else if (slice_matches_str (&item_slice, "forest")) { + mask |= DOM_FOREST; + entry_count += 1; + } else if (slice_matches_str (&item_slice, "swamp")) { + mask |= DOM_SWAMP; + entry_count += 1; + } else if (slice_matches_str (&item_slice, "river")) { + mask |= DOM_RIVER; + entry_count += 1; + } else { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %.*s (invalid %s entry)", line_number, item_slice.len, item_slice.str, key_name); + err->text[(sizeof err->text) - 1] = '\0'; + free (value_text); + return false; + } + } + + if (*cursor == ',') { + cursor++; + continue; + } + break; + } + } + + if (value_text != NULL) + free (value_text); + + if ((entry_count == 0) || (mask == 0)) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %s (expected at least one overlay)", line_number, key_name); + err->text[(sizeof err->text) - 1] = '\0'; + return false; + } + + *out_mask = mask; + return true; +} + bool parse_district_bonus_entries (struct string_slice const * value, int * out_base_bonus, @@ -8071,6 +8250,14 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->has_buildable_adjacent_to = true; cfg->buildable_adjacent_to_allows_city = def->buildable_adjacent_to_allows_city; } + if (def->has_buildable_on_overlays) { + cfg->buildable_on_overlays_mask = def->buildable_on_overlays_mask; + cfg->has_buildable_on_overlays = true; + } + if (def->has_buildable_adjacent_to_overlays) { + cfg->buildable_adjacent_to_overlays_mask = def->buildable_adjacent_to_overlays_mask; + cfg->has_buildable_adjacent_to_overlays = true; + } if (def->has_generated_resource) { if ((cfg->generated_resource != NULL) && (cfg->generated_resource != defaults->generated_resource)) @@ -8320,6 +8507,10 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.buildable_adjacent_to_square_types_mask = def->has_buildable_adjacent_to ? def->buildable_adjacent_to_square_types_mask : 0; new_cfg.has_buildable_adjacent_to = def->has_buildable_adjacent_to; new_cfg.buildable_adjacent_to_allows_city = def->has_buildable_adjacent_to ? def->buildable_adjacent_to_allows_city : false; + new_cfg.buildable_on_overlays_mask = def->has_buildable_on_overlays ? def->buildable_on_overlays_mask : 0; + new_cfg.has_buildable_on_overlays = def->has_buildable_on_overlays; + new_cfg.buildable_adjacent_to_overlays_mask = def->has_buildable_adjacent_to_overlays ? def->buildable_adjacent_to_overlays_mask : 0; + new_cfg.has_buildable_adjacent_to_overlays = def->has_buildable_adjacent_to_overlays; if (def->has_culture_bonus) move_bonus_entry_list (&new_cfg.culture_bonus_extras, &def->culture_bonus_extras); @@ -8806,6 +8997,15 @@ handle_district_definition_key (struct parsed_district_definition * def, def->has_buildable_on = true; } + } else if (slice_matches_str (key, "buildable_on_overlays")) { + unsigned int mask; + if (parse_buildable_overlay_mask (value, &mask, parse_errors, line_number, "buildable_on_overlays")) { + def->buildable_on_overlays_mask = mask; + def->has_buildable_on_overlays = true; + } else { + def->has_buildable_on_overlays = false; + } + } else if (slice_matches_str (key, "buildable_adjacent_to")) { unsigned int mask; def->buildable_adjacent_to_allows_city = false; @@ -8814,6 +9014,15 @@ handle_district_definition_key (struct parsed_district_definition * def, def->has_buildable_adjacent_to = true; } + } else if (slice_matches_str (key, "buildable_adjacent_to_overlays")) { + unsigned int mask; + if (parse_buildable_overlay_mask (value, &mask, parse_errors, line_number, "buildable_adjacent_to_overlays")) { + def->buildable_adjacent_to_overlays_mask = mask; + def->has_buildable_adjacent_to_overlays = true; + } else { + def->has_buildable_adjacent_to_overlays = false; + } + } else if (slice_matches_str (key, "allow_multiple")) { struct string_slice val_slice = *value; int ival; @@ -9248,6 +9457,8 @@ init_parsed_wonder_definition (struct parsed_wonder_definition * def) { memset (def, 0, sizeof *def); def->buildable_square_types_mask = district_default_buildable_mask (); + def->buildable_on_overlays_mask = 0; + def->buildable_adjacent_to_overlays_mask = 0; } void @@ -9298,6 +9509,10 @@ add_dynamic_wonder_from_definition (struct parsed_wonder_definition * def, int s new_cfg.img_alt_dir_column = def->img_alt_dir_column; new_cfg.enable_img_alt_dir = def->enable_img_alt_dir; new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); + new_cfg.buildable_on_overlays_mask = def->has_buildable_on_overlays ? def->buildable_on_overlays_mask : 0; + new_cfg.buildable_adjacent_to_overlays_mask = def->has_buildable_adjacent_to_overlays ? def->buildable_adjacent_to_overlays_mask : 0; + new_cfg.has_buildable_on_overlays = def->has_buildable_on_overlays; + new_cfg.has_buildable_adjacent_to_overlays = def->has_buildable_adjacent_to_overlays; if (existing_index >= 0) { struct wonder_district_config * cfg = &is->wonder_district_configs[existing_index]; @@ -9558,6 +9773,24 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, def->has_buildable_on = false; } + } else if (slice_matches_str (key, "buildable_on_overlays")) { + unsigned int mask; + if (parse_buildable_overlay_mask (value, &mask, parse_errors, line_number, "buildable_on_overlays")) { + def->buildable_on_overlays_mask = mask; + def->has_buildable_on_overlays = true; + } else { + def->has_buildable_on_overlays = false; + } + + } else if (slice_matches_str (key, "buildable_adjacent_to_overlays")) { + unsigned int mask; + if (parse_buildable_overlay_mask (value, &mask, parse_errors, line_number, "buildable_adjacent_to_overlays")) { + def->buildable_adjacent_to_overlays_mask = mask; + def->has_buildable_adjacent_to_overlays = true; + } else { + def->has_buildable_adjacent_to_overlays = false; + } + } else add_unrecognized_key_error (unrecognized_keys, line_number, key); } @@ -31318,6 +31551,19 @@ tile_coords_has_city_with_building_in_district_radius (int tile_x, int tile_y, i return false; } +bool +wonder_allows_river (struct wonder_district_config const * cfg) +{ + unsigned int build_mask = wonder_buildable_square_type_mask (cfg); + if (build_mask == 0) + build_mask = district_default_buildable_mask (); + if ((build_mask & square_type_mask_bit (SQ_RIVER)) != 0) + return true; + if (cfg->has_buildable_on_overlays && ((cfg->buildable_on_overlays_mask & DOM_RIVER) != 0)) + return true; + return false; +} + Tile * get_tile_sprite_indices (int tile_x, int tile_y, int * out_sheet_index, int * out_sprite_index) { @@ -31640,8 +31886,7 @@ wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner return false; // If on a river and the wonder allows river alignment, make sure we face the river instead - unsigned int mask = wonder_buildable_square_type_mask (cfg); - bool allow_river = (mask & square_type_mask_bit (SQ_RIVER)) != 0; + bool allow_river = wonder_allows_river (cfg); if (allow_river) { enum direction river_dir = DIR_ZERO; if (get_primary_river_direction (center, &river_dir)) { @@ -31734,16 +31979,11 @@ district_allows_river (struct district_config const * cfg) unsigned int build_mask = cfg->buildable_square_types_mask; if (build_mask == 0) build_mask = district_default_buildable_mask (); - return (build_mask & square_type_mask_bit (SQ_RIVER)) != 0; -} - -bool -wonder_allows_river (struct wonder_district_config const * cfg) -{ - unsigned int build_mask = wonder_buildable_square_type_mask (cfg); - if (build_mask == 0) - build_mask = district_default_buildable_mask (); - return (build_mask & square_type_mask_bit (SQ_RIVER)) != 0; + if ((build_mask & square_type_mask_bit (SQ_RIVER)) != 0) + return true; + if (cfg->has_buildable_on_overlays && ((cfg->buildable_on_overlays_mask & DOM_RIVER) != 0)) + return true; + return false; } int From 9d006b8888d1fc0c3b697310a938ef20b9cd0be5 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 31 Jan 2026 09:34:36 -0800 Subject: [PATCH 325/356] Rename max_building_index -> img_column_count, allow setting via config --- C3X.h | 27 ++++++++++--------- default.districts_config.txt | 6 ++--- injected_code.c | 51 +++++++++++++++++++++++++++--------- 3 files changed, 57 insertions(+), 27 deletions(-) diff --git a/C3X.h b/C3X.h index b6ae221a..3e96c038 100644 --- a/C3X.h +++ b/C3X.h @@ -735,7 +735,8 @@ struct district_config { int buildable_on_district_count; int buildable_adjacent_to_district_count; int img_path_count; - int max_building_index; + int img_column_count; + bool has_img_column_count_override; int btn_tile_sheet_column; int btn_tile_sheet_row; int culture_bonus; @@ -863,7 +864,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {0}, .advance_prereq_count = 0, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = true, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"Neighborhood_AMER.pcx", "Neighborhood_EURO.pcx", "Neighborhood_ROMAN.pcx", "Neighborhood_MIDEAST.pcx", "Neighborhood_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, - .img_path_count = 5, .max_building_index = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, + .img_path_count = 5, .img_column_count = 3, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, .culture_bonus = 1, .science_bonus = 1, .food_bonus = 0, .gold_bonus = 1, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 25, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 @@ -873,7 +874,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {0}, .advance_prereq_count = 0, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"WonderDistrict.pcx"}, .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Coast) | (1 << SQ_Mountains)), - .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, + .img_path_count = 1, .img_column_count = 0, .btn_tile_sheet_column = 1, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 @@ -883,7 +884,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {"Construction"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {"DistributionHub.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, - .img_path_count = 1, .max_building_index = 0, .btn_tile_sheet_column = 2, .btn_tile_sheet_row = 0, + .img_path_count = 1, .img_column_count = 0, .btn_tile_sheet_column = 2, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 @@ -893,7 +894,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {"Flight"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 1, .img_paths = {"Aerodrome.pcx"}, .dependent_improvements = {"Airport"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, - .img_path_count = 1, .max_building_index = 1, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, + .img_path_count = 1, .img_column_count = 1, .btn_tile_sheet_column = 3, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, @@ -902,7 +903,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {0}, .advance_prereq_count = 0, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .dependent_improvements = {0}, .img_paths = {0}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, - .img_path_count = 0, .max_building_index = 0, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, + .img_path_count = 0, .img_column_count = 0, .btn_tile_sheet_column = 0, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, @@ -911,7 +912,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {"Map Making"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 2, .align_to_coast = true, .img_paths = {"Port_NW.pcx", "Port_NE.pcx", "Port_SE.pcx", "Port_SW.pcx"}, .dependent_improvements = {"Harbor", "Commercial Dock"}, .buildable_square_types_mask = (1 << SQ_Coast), - .img_path_count = 4, .max_building_index = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .align_to_coast = true, + .img_path_count = 4, .img_column_count = 2, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .align_to_coast = true, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, @@ -920,7 +921,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {"Steam Power"}, .advance_prereq_count = 1, .resource_prereqs = {"Iron", "Coal"}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 2, .dependent_improvement_count = 0, .img_paths = {"CentralRailHub_AMER.pcx", "CentralRailHub_EURO.pcx", "CentralRailHub_ROMAN.pcx", "CentralRailHub_MIDEAST.pcx", "CentralRailHub_ASIAN.pcx"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .auto_add_road = true, .auto_add_railroad = true, - .img_path_count = 5, .max_building_index = 1, .btn_tile_sheet_column = 5, .btn_tile_sheet_row = 0, + .img_path_count = 5, .img_column_count = 1, .btn_tile_sheet_column = 5, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, @@ -929,7 +930,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {"Industrialization"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 4, .img_paths = {"EnergyGrid.pcx"}, .dependent_improvements = {"Coal Plant", "Hydro Plant", "Solar Plant", "Nuclear Plant"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .custom_height = 84, - .img_path_count = 1, .max_building_index = 14, .btn_tile_sheet_column = 6, .btn_tile_sheet_row = 0, + .img_path_count = 1, .img_column_count = 14, .btn_tile_sheet_column = 6, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 2, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, @@ -938,7 +939,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {"Industrialization"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"Bridge.pcx"}, .dependent_improvements = {0}, .custom_width = 176, .custom_height = 112, .y_offset = 24, .x_offset = 0, .buildable_square_types_mask = (1 << SQ_Coast), .auto_add_road = true, - .img_path_count = 1, .max_building_index = 7, .btn_tile_sheet_column = 7, .btn_tile_sheet_row = 0, + .img_path_count = 1, .img_column_count = 7, .btn_tile_sheet_column = 7, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, @@ -947,7 +948,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {"Industrialization"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"Canal.pcx"}, .dependent_improvements = {0}, .custom_width = 176, .custom_height = 112, .y_offset = 24, .x_offset = 0, .buildable_square_types_mask = (1 << SQ_Desert) | (1 << SQ_Plains) | (1 << SQ_Grassland) | (1 << SQ_Tundra) | (1 << SQ_FloodPlain), - .img_path_count = 1, .max_building_index = 8, .btn_tile_sheet_column = 8, .btn_tile_sheet_row = 0, + .img_path_count = 1, .img_column_count = 8, .btn_tile_sheet_column = 8, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, @@ -956,7 +957,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {0}, .advance_prereq_count = 0, .obsoleted_by = "Metallurgy", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"GreatWall.pcx"}, .dependent_improvements = {0}, .custom_height = 88, .wonder_prereqs = {"The Great Wall"}, .wonder_prereq_count = 1, .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Mountains) | (1 << SQ_Forest) | (1 << SQ_Swamp) | (1 << SQ_Jungle)), .draw_over_resources = true, - .img_path_count = 1, .max_building_index = 10, .btn_tile_sheet_column = 9, .btn_tile_sheet_row = 0, + .img_path_count = 1, .img_column_count = 10, .btn_tile_sheet_column = 9, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 50, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 } @@ -984,6 +985,7 @@ struct parsed_district_definition { int buildable_on_district_count; int buildable_adjacent_to_district_count; int img_path_count; + int img_column_count; bool allow_multiple; bool vary_img_by_era; bool vary_img_by_culture; @@ -1034,6 +1036,7 @@ struct parsed_district_definition { bool has_natural_wonder_prereqs; bool has_display_name; bool has_img_paths; + bool has_img_column_count; bool has_allow_multiple; bool has_vary_img_by_era; bool has_vary_img_by_culture; diff --git a/default.districts_config.txt b/default.districts_config.txt index 2de61571..97331007 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -12,6 +12,7 @@ ; - tooltip : Text. Shown when hovering over build action button. ; - img_paths : Comma-separated PCX filenames under Art/Districts/1200/. 1 for single image, 5 for culture variants (AMER, EURO, ROMAN, MIDEAST, ASIAN). Order matters. + ; - img_column_count : Number. Overrides the number of sprite columns per image (derived from dependent_improvs if omitted). ; - render_strategy : "by-count" | "by-building". Whether the PCX files show all buildings together, or one building per column. (default: "by-count") ; - draw_over_resources : 0 or 1. If a resource is also on the tile, draw the district on top. (default: 0) ; - vary_img_by_era : 0 or 1. If 1, each PCX image must have 4 rows, 1 for each era. @@ -27,7 +28,7 @@ ; - wonder_prereqs : Comma-separated wonder names. District cannot be built unless all Wonders are built by same civ. ; - natural_wonder_prereqs : Comma-separated natural wonder names. District cannot be built unless all Natural Wonders are within civ's territory. ; - buildable_on : Comma-separated square types: desert, plains, grassland, tundra, floodplain, hills, mountains, volcano, - coast, sea, ocean, snow-forest, snow-mountains, snow-volcano, lake, any. + coast, sea, ocean, snow-forest, snow-mountains, snow-volcano, lake. ; - buildable_on_overlays : Comma-separated overlays: irrigation, mine, fortress, barricade, outpost, radar-tower, jungle, forest, swamp, river. ; - buildable_adjacent_to : Same as buildable_on, plus "city". ; - buildable_adjacent_to_overlays : Same as buildable_on_overlays. @@ -234,7 +235,6 @@ vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereqs = Engineering dependent_improvs = -buildable_on = any buildable_on_overlays = forest defense_bonus_percent = 0 allow_multiple = 1 @@ -330,7 +330,7 @@ happiness_bonus = 0 #District name = Wonder District tooltip = Build Wonder District -buildable_on = desert,plains,grassland,tundra,floodplain,hills,coast,mountains +buildable_on = desert,plains,grassland,tundra,floodplain,hills,coast advance_prereqs = defense_bonus_percent = 0 allow_multiple = 1 diff --git a/injected_code.c b/injected_code.c index 97d2fc58..c25c960c 100644 --- a/injected_code.c +++ b/injected_code.c @@ -8285,9 +8285,14 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->dependent_improvements[i] = def->dependent_improvements[i]; def->dependent_improvements[i] = NULL; } - cfg->max_building_index = cfg->dependent_improvement_count; - if (cfg->max_building_index > 20) - cfg->max_building_index = 20; + if (! def->has_img_column_count) { + cfg->img_column_count = cfg->dependent_improvement_count; + if (cfg->img_column_count > 20) + cfg->img_column_count = 20; + if (cfg->img_column_count < 0) + cfg->img_column_count = 0; + cfg->has_img_column_count_override = false; + } } if (def->has_img_paths) { @@ -8309,6 +8314,15 @@ override_special_district_from_definition (struct parsed_district_definition * d } } + if (def->has_img_column_count) { + cfg->img_column_count = def->img_column_count; + if (cfg->img_column_count > 20) + cfg->img_column_count = 20; + if (cfg->img_column_count < 0) + cfg->img_column_count = 0; + cfg->has_img_column_count_override = true; + } + if (! ensure_culture_variant_art (cfg, section_start_line)) { free_special_district_override_strings (cfg, defaults); *cfg = *defaults; @@ -8561,9 +8575,12 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i return false; } - new_cfg.max_building_index = new_cfg.dependent_improvement_count; - if (new_cfg.max_building_index > 20) - new_cfg.max_building_index = 20; + new_cfg.img_column_count = def->has_img_column_count ? def->img_column_count : new_cfg.dependent_improvement_count; + if (new_cfg.img_column_count > 20) + new_cfg.img_column_count = 20; + if (new_cfg.img_column_count < 0) + new_cfg.img_column_count = 0; + new_cfg.has_img_column_count_override = def->has_img_column_count; if (reusing_existing) new_cfg.command = preserved_command; @@ -8972,6 +8989,15 @@ handle_district_definition_key (struct parsed_district_definition * def, } free (value_text); + } else if (slice_matches_str (key, "img_column_count")) { + struct string_slice val_slice = *value; + int ival; + if (read_int (&val_slice, &ival)) { + def->img_column_count = ival; + def->has_img_column_count = true; + } else + add_key_parse_error (parse_errors, line_number, key, value, "(expected integer)"); + } else if (slice_matches_str (key, "dependent_improvs")) { char * value_text = trim_and_extract_slice (value, 0); int list_count = 0; @@ -10563,8 +10589,9 @@ set_wonders_dependent_on_wonder_district (void) cfg->dependent_improvement_count = dest + 1; } - if (cfg->max_building_index < cfg->dependent_improvement_count) - cfg->max_building_index = cfg->dependent_improvement_count; + if (! cfg->has_img_column_count_override && + (cfg->img_column_count < cfg->dependent_improvement_count)) + cfg->img_column_count = cfg->dependent_improvement_count; } void @@ -16169,7 +16196,7 @@ bool load_day_night_hour_images(struct day_night_cycle_img_set *this, const char variant_count = variant_capacity; int era_count = cfg->vary_img_by_era ? 4 : 1; - int column_count = cfg->max_building_index + 1; + int column_count = cfg->img_column_count + 1; for (int variant_i = 0; variant_i < variant_count; variant_i++) { const char * img_path = cfg->img_paths[variant_i]; @@ -16424,7 +16451,7 @@ build_sprite_proxies_24(Map_Renderer *mr) { variant_count = variant_capacity; int era_count = cfg->vary_img_by_era ? 4 : 1; - int column_count = cfg->max_building_index + 1; + int column_count = cfg->img_column_count + 1; for (int variant_i = 0; variant_i < variant_count; variant_i++) { if ((cfg->img_paths[variant_i] == NULL) || (cfg->img_paths[variant_i][0] == '\0')) @@ -31339,7 +31366,7 @@ init_district_images () continue; int era_count = cfg->vary_img_by_era ? 4 : 1; - int column_count = cfg->max_building_index + 1; + int column_count = cfg->img_column_count + 1; int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; @@ -32425,7 +32452,7 @@ draw_district_on_map_or_canvas_by_buildings (Sprite * base_sprite, Map_Renderer { struct district_config const * cfg = &is->district_configs[district_id]; struct district_infos * district_info = &is->district_infos[district_id]; - int max_stage = cfg->max_building_index; + int max_stage = cfg->img_column_count; int stage_limit = ARRAY_LEN (is->district_img_sets[0].imgs[0][0]) - 1; if (max_stage > stage_limit) From bfddb73bdda160a65f6e716e6422b1fb4b8f9761 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 31 Jan 2026 10:28:23 -0800 Subject: [PATCH 326/356] Update water park art --- Art/Districts/1200/WaterPark.pcx | Bin 8225 -> 7979 bytes Notes/district_todos.md | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Art/Districts/1200/WaterPark.pcx b/Art/Districts/1200/WaterPark.pcx index 81e2358a29845843eefc0d97f5f1e04b8173381c..edcca3e31a9215a15f117abe63d2bbe82eb24a2b 100644 GIT binary patch literal 7979 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 literal 8225 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 Wfzq-5D=+^Lj1Auur>D|WsQ(3}uz5@X diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 49be57ac..219d401b 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,4 +1,3 @@ - - Add buildable_on_overlay - Districts: - Municipal District (ZergMazter doing art) - Central Rail Hub (ZergMazter doing art) @@ -14,6 +13,7 @@ ## Maritime Districts - ~~Negative bonuses~~ + - ~~Add buildable_on_overlay~~ - ~~Add commented instructions on fields in Wonder, Natural Wonder config files~~ - ~~Firm up logic for river district rendering~~ - ~~Validate and upfront bridge/canal algorithm~~ From 1a1f4447986de16d760ca92b203401a7e2b521fc Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 31 Jan 2026 11:15:43 -0800 Subject: [PATCH 327/356] Ensure civ-built overlays are removed when a district is built over top --- injected_code.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/injected_code.c b/injected_code.c index c25c960c..5ee3d5e6 100644 --- a/injected_code.c +++ b/injected_code.c @@ -19023,7 +19023,7 @@ issue_district_worker_command (Unit * unit, int command) // If District will replace an improvement unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); - unsigned int removable_flags = overlay_flags & 0xfc; + unsigned int removable_flags = overlay_flags & (destructible_overlays & ~(TILE_FLAG_ROAD | TILE_FLAG_RAILROAD)); if (removable_flags != 0) { PopupForm * popup = get_popup_form (); @@ -32934,7 +32934,7 @@ ai_move_district_worker (Unit * worker, struct district_worker_record * rec) enum SquareTypes base_type = tile->vtable->m50_Get_Square_BaseType (tile); unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); - unsigned int removable_flags = overlay_flags & 0xfc; + unsigned int removable_flags = overlay_flags & (destructible_overlays & ~(TILE_FLAG_ROAD | TILE_FLAG_RAILROAD)); bool district_buildable_here = district_is_buildable_on_square_type (&is->district_configs[req->district_id], tile); // Remove any existing improvements @@ -33102,7 +33102,7 @@ ai_worker_try_tile_improvement_district (Unit * worker) (best_score >= irrigation_score) && (best_score >= mine_score)) { unsigned int overlay_flags = tile->vtable->m42_Get_Overlays (tile, __, 0); - unsigned int removable_flags = overlay_flags & 0xfc; + unsigned int removable_flags = overlay_flags & (destructible_overlays & ~(TILE_FLAG_ROAD | TILE_FLAG_RAILROAD)); tile->vtable->m62_Set_Tile_BuildingID (tile, __, -1); if (removable_flags != 0) tile->vtable->m51_Unset_Tile_Flags (tile, __, 0, removable_flags, tile_x, tile_y); From 23807e0e110e9910ba87506821fd1e4ba31ce606 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 31 Jan 2026 13:14:36 -0800 Subject: [PATCH 328/356] Add patch to prevent empty army combat divide by zero bug --- C3X.h | 1 + civ_prog_objects.csv | 1 + default.c3x_config.ini | 3 +++ injected_code.c | 21 +++++++++++++++++++++ 4 files changed, 26 insertions(+) diff --git a/C3X.h b/C3X.h index 3e96c038..68596bb5 100644 --- a/C3X.h +++ b/C3X.h @@ -355,6 +355,7 @@ struct c3x_config { bool patch_disease_stopping_tech_flag_bug; bool patch_division_by_zero_in_ai_alliance_eval; bool patch_empty_army_movement; + bool patch_empty_army_combat; bool delete_off_map_ai_units; bool fix_overlapping_specialist_yield_icons; bool patch_premature_truncation_of_found_paths; diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 8947f1f7..72251797 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -931,3 +931,4 @@ inlead, 0x440100, 0x0, 0x0, "Leader_get_attitude_toward", inlead, 0x5D7080, 0x0, 0x0, "Map_check_colony_location", "int (__fastcall *) (Map * this, int edx, int tile_x, int tile_y, int civ_id)" ignore, 0x670234, 0x68D2E0, 0x670234, "Tile_m27_Check_Shield_Bonus", "bool (__fastcall *) (Tile * this)" ignore, 0x5f3448, 0x6032DF, 0x5F3378, "CHECK_SHIELD_BONUS_TO_CAN_SPAWN_RES_RETURN", "int" +inlead, 0x5BCE60, 0x0, 0x0, "Unit_select_army_member_for_combat", "Unit * (__fastcall *) (Unit * this, int edx, int param_1, char param_2)" diff --git a/default.c3x_config.ini b/default.c3x_config.ini index f89ed841..dadcc36d 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -312,6 +312,8 @@ ai_worker_requirement_percent = 150 ; the search fails to find anything, there will be no default option, the dropdown selector will not be initialized, and the game will crash when it ; tries to open the popup. To fix this, the mod detects when the game has failed to find a new build option and fills in Wealth instead or, failing ; that, any unit or improvement that the city can build. +; PREVENT EMPTY ARMY FROM COMBAT -- Guards a crash in the army combat selection routine by returning the army unit itself if it has no contained +; units (prevents divide-by-zero in the vanilla selection loop). patch_submarine_bug = true patch_science_age_bug = true @@ -325,6 +327,7 @@ patch_barbarian_diagonal_bug = true patch_disease_stopping_tech_flag_bug = false patch_division_by_zero_in_ai_alliance_eval = true patch_empty_army_movement = true +patch_empty_army_combat = true patch_premature_truncation_of_found_paths = true patch_zero_production_crash = true patch_ai_can_sacrifice_without_special_ability = true diff --git a/injected_code.c b/injected_code.c index 865a1bdd..854dc42a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16733,6 +16733,7 @@ patch_init_floating_point () {"patch_disease_stopping_tech_flag_bug" , false, offsetof (struct c3x_config, patch_disease_stopping_tech_flag_bug)}, {"patch_division_by_zero_in_ai_alliance_eval" , true , offsetof (struct c3x_config, patch_division_by_zero_in_ai_alliance_eval)}, {"patch_empty_army_movement" , true , offsetof (struct c3x_config, patch_empty_army_movement)}, + {"patch_empty_army_combat" , true , offsetof (struct c3x_config, patch_empty_army_combat)}, {"patch_premature_truncation_of_found_paths" , true , offsetof (struct c3x_config, patch_premature_truncation_of_found_paths)}, {"patch_zero_production_crash" , true , offsetof (struct c3x_config, patch_zero_production_crash)}, {"patch_ai_can_form_army_without_special_ability" , true , offsetof (struct c3x_config, patch_ai_can_form_army_without_special_ability)}, @@ -35088,5 +35089,25 @@ patch_City_m22 (City * this, int edx, bool param_1) } } +Unit * __fastcall +patch_Unit_select_army_member_for_combat (Unit * this, int edx, int param_1, char param_2) +{ + if (is->current_config.patch_empty_army_combat) { + int unit_count = 0; + Tile * tile = tile_at (this->Body.X, this->Body.Y); + if (tile != NULL && tile != p_null_tile) { + FOR_UNITS_ON (uti, tile) { + Unit * unit = uti.unit; + if ((unit != NULL) && (unit->Body.Container_Unit == this->Body.ID)) + unit_count += Unit_count_contained_units (unit) + 1; + } + } + if (unit_count == 0) + return this; + } + + return Unit_select_army_member_for_combat (this, __, param_1, param_2); +} + // TCC requires a main function be defined even though it's never used. int main () { return 0; } From 012671caf8e3246d600c954e3cba313dca447e50 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 31 Jan 2026 22:31:11 -0800 Subject: [PATCH 329/356] Renamed patch_empty_army_combat to patch_empty_army_combat_crash and update changelog --- C3X.h | 2 +- changelog.txt | 1 + default.c3x_config.ini | 4 ++-- injected_code.c | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/C3X.h b/C3X.h index 68596bb5..bdd9ad6e 100644 --- a/C3X.h +++ b/C3X.h @@ -355,7 +355,7 @@ struct c3x_config { bool patch_disease_stopping_tech_flag_bug; bool patch_division_by_zero_in_ai_alliance_eval; bool patch_empty_army_movement; - bool patch_empty_army_combat; + bool patch_empty_army_combat_crash; bool delete_off_map_ai_units; bool fix_overlapping_specialist_yield_icons; bool patch_premature_truncation_of_found_paths; diff --git a/changelog.txt b/changelog.txt index 8e16f9fc..90d1a99e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -12,6 +12,7 @@ - aggressively_penalize_bankruptcy now alternates between selling buildings or disbanding units first for bankrupt AI players and every third turn cuts their research spending - Fix crash caused by failure to find default option for popup asking player to select new build in city that completed its previous one - Except nuclear weapons from disallow_useless_bombard_vs_airfields +- Fix crash caused by empty armies entering combat despite having zero attack and defense strength * RELEASE 26 - Add some options to make land transports viable, controlled by land_transport_rules setting diff --git a/default.c3x_config.ini b/default.c3x_config.ini index dadcc36d..41e7e8e5 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -312,7 +312,7 @@ ai_worker_requirement_percent = 150 ; the search fails to find anything, there will be no default option, the dropdown selector will not be initialized, and the game will crash when it ; tries to open the popup. To fix this, the mod detects when the game has failed to find a new build option and fills in Wealth instead or, failing ; that, any unit or improvement that the city can build. -; PREVENT EMPTY ARMY FROM COMBAT -- Guards a crash in the army combat selection routine by returning the army unit itself if it has no contained +; PATCH EMPTY ARMY COMBAT CRASH -- Guards a crash in the army combat selection routine by returning the army unit itself if it has no contained ; units (prevents divide-by-zero in the vanilla selection loop). patch_submarine_bug = true @@ -327,7 +327,7 @@ patch_barbarian_diagonal_bug = true patch_disease_stopping_tech_flag_bug = false patch_division_by_zero_in_ai_alliance_eval = true patch_empty_army_movement = true -patch_empty_army_combat = true +patch_empty_army_combat_crash = true patch_premature_truncation_of_found_paths = true patch_zero_production_crash = true patch_ai_can_sacrifice_without_special_ability = true diff --git a/injected_code.c b/injected_code.c index 854dc42a..e4d0a446 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16733,7 +16733,7 @@ patch_init_floating_point () {"patch_disease_stopping_tech_flag_bug" , false, offsetof (struct c3x_config, patch_disease_stopping_tech_flag_bug)}, {"patch_division_by_zero_in_ai_alliance_eval" , true , offsetof (struct c3x_config, patch_division_by_zero_in_ai_alliance_eval)}, {"patch_empty_army_movement" , true , offsetof (struct c3x_config, patch_empty_army_movement)}, - {"patch_empty_army_combat" , true , offsetof (struct c3x_config, patch_empty_army_combat)}, + {"patch_empty_army_combat_crash" , true , offsetof (struct c3x_config, patch_empty_army_combat_crash)}, {"patch_premature_truncation_of_found_paths" , true , offsetof (struct c3x_config, patch_premature_truncation_of_found_paths)}, {"patch_zero_production_crash" , true , offsetof (struct c3x_config, patch_zero_production_crash)}, {"patch_ai_can_form_army_without_special_ability" , true , offsetof (struct c3x_config, patch_ai_can_form_army_without_special_ability)}, @@ -35092,7 +35092,7 @@ patch_City_m22 (City * this, int edx, bool param_1) Unit * __fastcall patch_Unit_select_army_member_for_combat (Unit * this, int edx, int param_1, char param_2) { - if (is->current_config.patch_empty_army_combat) { + if (is->current_config.patch_empty_army_combat_crash) { int unit_count = 0; Tile * tile = tile_at (this->Body.X, this->Body.Y); if (tile != NULL && tile != p_null_tile) { From b6dcb408ef9dad09e109ef0923e9bd817c2310b1 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 31 Jan 2026 22:32:48 -0800 Subject: [PATCH 330/356] Minor alignment fix --- injected_code.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/injected_code.c b/injected_code.c index e4d0a446..76653589 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16733,7 +16733,7 @@ patch_init_floating_point () {"patch_disease_stopping_tech_flag_bug" , false, offsetof (struct c3x_config, patch_disease_stopping_tech_flag_bug)}, {"patch_division_by_zero_in_ai_alliance_eval" , true , offsetof (struct c3x_config, patch_division_by_zero_in_ai_alliance_eval)}, {"patch_empty_army_movement" , true , offsetof (struct c3x_config, patch_empty_army_movement)}, - {"patch_empty_army_combat_crash" , true , offsetof (struct c3x_config, patch_empty_army_combat_crash)}, + {"patch_empty_army_combat_crash" , true , offsetof (struct c3x_config, patch_empty_army_combat_crash)}, {"patch_premature_truncation_of_found_paths" , true , offsetof (struct c3x_config, patch_premature_truncation_of_found_paths)}, {"patch_zero_production_crash" , true , offsetof (struct c3x_config, patch_zero_production_crash)}, {"patch_ai_can_form_army_without_special_ability" , true , offsetof (struct c3x_config, patch_ai_can_form_army_without_special_ability)}, From cb70639cd1234b2cafdcb194b93b22c88d05209c Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Sat, 31 Jan 2026 23:34:55 -0800 Subject: [PATCH 331/356] Add wonders_2 light annotations --- Art/.gitignore | 3 ++- Art/Districts/1200/Wonders_2.PCX | Bin 39216 -> 39166 bytes .../Annotations/Wonders_2_lights.PCX | Bin 31355 -> 37610 bytes .../Annotations/Wonders_3_lights.PCX | Bin 0 -> 21460 bytes 4 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 Art/Districts/Annotations/Wonders_3_lights.PCX diff --git a/Art/.gitignore b/Art/.gitignore index c85a63f9..6918d97e 100644 --- a/Art/.gitignore +++ b/Art/.gitignore @@ -1 +1,2 @@ -Seasons/ \ No newline at end of file +Seasons/ +2400/ \ No newline at end of file diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index dfc692063297631922840f3964c1ed9cec2de14f..295a8829afa76f180c44be338b19263597b3f655 100644 GIT binary patch delta 625 zcmWlV&rj2E7>9*LHj_CADB0f{+apXCEF3;zAW3>J&wPpu*v!sNp z;27lE!lq1JMtC?VJChPGCxa?<4n21E9!TWmqyeR&DQ7qs(<@90P7i0DVXvk~c?GtH zUpm=tj?yUsUR{0RjH`2nw5GzPYdH@mgh{c4H8X3GZ36C*C*CEjB`jP)+eGq~R2RS) zdG2XgtY+o%Rs4g?@;ey0o^_XnDJ#v01rp^oI5;}HC(QVLL6KC7usS;176>xKmTYp& znHKMKU_d{b(Q{~L&Rb<%f{hz9`?MfaJlxfnk&}L6V+|gS<-JmvjfH%yp4LLxAT|%D zZoc-gk&6M!s#;7jkBtO4#>;yN9Sozb6`vLta1$2C=iOz0fW>;j;<1r}_^prb6TQ)y zN+4tv(3XH=Kf5X4m3|`5U;{}4j@|y+!O>!XNpgqy3f3#<@|vMS`^2|)DR5HC(LU-~ zhU;s}+9lYUSm~sApDYPIw5AFSZX#)_z{Z`mE?JyD9@B&#K96>B44X%gG$*{f?n&Z$ zgd<6$-YX#!=*XfgkybcaOV7cP$$6JLnO8YE6^GxG9~?2o0G%(mcqNW@ mDwcqOD7b1;Lb15G1%IOt9BN1aL0D_~WnK`!cem}@-r+yOKT=iz delta 675 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 delta 11713 zcmb7~e{fsZmEZj%&$J$B8i=}234M9QcLhCdwAF&(Vbf!CV@1uTDKP>@}EX%eiQY1uD zF+q`vn|SkXKj+bQ#cs4SJ0vdNFWh&}_k7RyoO|D!K77wd3wOWydeQjCk7%R(P^a-f z^0hJkZ~UWX#E%$%jt%4gy|2uAegCQztqre*jQ>jx{)Q`07>^(N`cWm9{(^nu-||D+ zZ&c>K&FhtUP0c&*_H2f6<`)pZum_yYX-OTN;PW>ycGI zK58D6`<^ks@;~%XnTBCA$kkkFbVT{>#-H@hbECT7yV)}jG%Sl8Gpf>H{0}(%q5I9d zSGHc@^bPE2xks7y`?yi&IG6ByuA25Qm|O1O-u+PLJ6v_$A2vPrHLUbZuV>Q3T&1~6 zac0tg%N){!k-;No-u>J?vB%!$;59C9y#L>FK}yYe#?p3=x@xIOWz^{K$3GnN&zeX3 zb)-=CLPF`R|%Tp||L+f@PZsRi&}LNgo=7SbFgDr_H#3 z)jwkP{d`yEhc7+TzS7Oy|5)*~DcJeVC(MvG)}UwpV>0vOBWC|_b-#>C`I|L?Oh7i= zf6Q>JyLB&YY0{>U$G5hwISeTc2MX&|g(IqLsqx-Mf#6xsczhAszJJN=?{3s3V7SS+ zs%lhKGA&Iowg0||z;V_6%Cdfj0<$?csmAjA!A0?3OLzY1kFy(6S=Teis%MPvKS!UT zEhFYnzSn!q9M-l|NaN!vBCCKOas^n8*V)L}o;jpUGyZB=)&BPk;-Nu1#W}{Y2oGNZ zZl*6HtZ(vg&QWt{Q1=gNbH)9sJy1MjoSsa|Q4`wfIA+$yZAV>IWmU^DFg1_8RSTG_ zkf6%4F0$> zz3s;WiYl}DSBx4-NvJ8Tx~9hX)EWPDfZ;q?&VuM@!i%))!9EYx>cK5y%pqL)o_j7d zP<+cc8MV-(o&3gubpsm{jgKp{$Id&TJfTOi=323!uB%B813au*V2oI_{EYdKSZe7d zrb|ZyLe8lgG$fLoG@ed460HRb{D^}bF)*6*DtX8HpKupxgOs-rj$TWH6ig%(#W8{v zMqogp6j1brnoTu67;6a(81y22$WK3%9X6NU@B{tD^JZgSEhN=wPbR7py5azcO$0tl{=YRgbXZ{j@Onc&oiAB}&EZ7Tk zaRcDb>0BWvqb+!Nra+3z{?!rlC+m)wIaJHS3uAo7F`&$M4Kt?njH>EcRY~fmKKTQW zSW`=;2@%C{{ezJPTh))6OYWDNGR2D7zGw_CNyU0}#zDxe&VhpU1A3c*MAzwi0^f}C zP0!p#jLA6pjJEfy>Nzv(A2UxNir5cWzU-<81(6k;GLx#neI_AujROPV;TMiAP+I>yPT0ti=XbhYh zjC9%zsD9jBbl-R|Q>>c34$(?LCDlSpqAL?~v~8`-3|F}fR3-?A39JxzFTkq|iNeX` zL(XRzYc>9snej)=qn;rzsaZ`DdZPcaLx_-rdxT~XL)2BY6-@0!DWMA`wsKC3lXldX zMkB*!N5c+P?p;4(ru-|WRUBe3`UU%eujY*b|UM zYs&nsHg+dIUEgrhOau8G{MwVmdG#3nzZ&H#T1tQJBa zE5nQvFOx`4o6Q(h`Ro(4vqz1(MnPdaglWD|!cS0;FJwYInAdCzDZSDLRf{-9gf4=g z3_3F1oqr^kh6D4(X&S;f;$^c294na>I9kUx zil6g(6kR0$OH7lkaU!wja3W1vV%Z_~fFX_Q1r)BDD-=1ql-cz+?UV+?hGM9uA#cjb z!&XwuTwr)AS3nn&Y7TT@@Ep+06|HvsT`p{4@UwPW?^xk%-g4HwM=A2NeLvE#=*Z6> zA*G2G;$1t@SJ-%PWfH&6qf+!BB)*2JnAjK5b==L!Z*UKRVR8|MDGpr=OnJ+k^-q&a z_GLD|8EtNC>2>u#d#w1LiEUCNnf$Jno|f$l4n5U0K#Q(balIJ7$qoTSU0qF!?P7k; z%7q*?W5+L=%M@kLeOu%Ril4N)rOwLQZP{iu0U>buqU!}|Vg)pk;#6n2q;Hgx#YUvp zDYQeA#&tW;>z4L&<`PBIrqqv~c&a6&EvW`1kjvNwgZCa3f~j(vol$QdLrH(|VpN#n z!HE(GKmo3v$Lx8~lH3+RGs6jy(<&Nq-kkPN`j_31v$c4|=68kxa zAic$HN5%JQAz?>1rmbxqYzBDbRV@#77w~i(k6$zw@mfEkOs*8kvaP0VZK}TfwZB0i z%hbpuNOTL{kW+F6DdqCr+R>$m?G<-Kn2L0You9R>9vwMvs<1WL{M64wR=OpVcY?~I zB1G7GP~@nh^O$>8tm8xWF9aB{fsBg$O+u+_!O0uAjURBH+#>v#0Zn`hr|?4jygBK+ z&#d@D@v4FUu}z?z$gD`D)Ga{3=T$oo<8e7%RPj_ar>%|v153>$EG-yt)=F6MaQ2cZ zos`;qxyzr2!ugWD=IiIE$umlR`1y@zAKuF*{Z51gaNyDY}I|@#r z0cX!zA!`LRdDN*Ay>A+j*QnEKESD!Y zPiTw7b9_c2LRyv4aCEa+<`$Gx>zsm0}1h=TN^~K4W*YjrZYP)yh*)N7ceGcva}O8V|?c zgn?Wp6g++z*w!Dzcf@3G(LXh}n;U{XVRsFd>RIP*~lZ4~h@&L<(ZdMfFv7W5E|X0~HiR;w2a*#6m|4hi6D$OmY4&SJrZ@*vxO>0~j!G)!-cpxV zk-JN7A-b(NWt=Xx@FQo_;b^K$7cxD0dmB8cTOnr!s1O&q?nICPgd%C72s4C?RkE{; zc5;+ep!dxGZdT^iQhwF;Zjq_Zbk1JW8Ak`0(n%2rt0e}7CKbzbwlO*hkp^P1Xl>C} z`ibY-?R>;pt!@ojmsoTdMJKR6NlX;T;G?MHoX6n3X}77G+)FFJ00uE6pbrl(Y|86YpPGDAs(DaK zNPQ|GS!CiXbohI(t7+&hblArPpN zRJ8zdmC9_?!fOPCT})$3!OPhq1*P>EluK{1ocw)Gh`DKt(;8l_rWRp=^JvEiA}asn zf&d$eCaf1cWJ)%r7iX6;!yIFqo3KUFB)wMIuRs&Y!Sl%02)w`XId!eXo|G5_N>GTD zZD$A}bXy#&-- z5(G@NyXB=dg&27h;1EB=mJ&k%;zyz%QVoHliJ(j~1;$4{E=jG2hTTA`RU; zA}}l8tj4$D4>Ux=MKlCl3Ia+A+T7&!V=Na1V-O+0tN@~GFd|S4#wO02CMiTQT_Q+o zka-}uQKA5aOYBUmdRUwcX{1uEb%e8Ty9fSxKeC!gwe)Bh69CiWWJr>A8$bXdof)<` zgL0q)l05N1nFOM%tK-9AHTgbz`ZlO4FSSyQtV!-os`*lqIcte>1yG1GkJu4;kVwDE z!90P-d>Z;RIlsquHPVcamtp|jB#99HVnw@UFI$r%$Rg3Ba5TSsilN&{5DFxhL zV`2~##S%n-E=Ad5Qe|p@$>T))21gFDtig2nW+6ahfsR09k?}et^%t6yjb|^os zuR<)9=pxf(dhpspAs27S!Kl14%n@Lq6FyD?Ai#&06n~b1#^Z-r18( zQOJ`Db6IsB<@hO4lLiw>w}6=zDau0t!fN_4WJ>2Z5KYC9Co|Z_98>hldU#RkSG5&Z zezT>nZ3(wpQYK!aEEp-h8Xy87qb2phtBkQg)Lqllj7F4`FiMw%yyNgqYNb)W7pOfK zpv?_6u32c3FcZPyAZoyLWky8^9Gea(M|9neOC@q&+wT=2=FLK^r7P9bl?oCsL6Xd; z90p(22__dwPyvaBD62`ToU^S)%(Zl@4i9OO*Y_yJDy9@|dPdzyGRaFmm1mi5RE_6I zv7O*s6Me<>3Z8`RnCq0F5a#6$dqqKM7BJTFlL`|t;YwmN!k&)wTj9If&QX(HV2mxy zJ#PZ~O){}8&@d=uh5(WTBUyL80P7H@Yxxr9&r2gSwK))@Bo`_`99%qA(q%p7m4E_a z`JI3E;7f&k%xOyI=MYb%p#^0HD{G{KBz?UY3<}t37Bw`9;6`Y+GTLGOFOMDqq~mx{Kg3g7{Px;V73Ne+@9k_-6jk z0tH5~S^(Wqm2mccNZtxvxaosrpDdi~vE}5sy#s^I2j={^70q}f881JuCzwf)eBl9ol_g`~Cdmlb94l*O)pWSf zO^GHaaG7Uh)%#Ta;uXh41)+*y!Eje(8vFJ$U24Wodf5lH=|GVMxW9x zJKf{)M~W9r^E2O$CcfQ+;3E!+mQ;x}fuEZO(X&50lx;nU3YA%Y>CuOqVg9E7?p6{Hv%rc(@A_X| zNq7YdqIu}W%q68Ju_TkS3N}lR;cR#)>xFp=cZDUo6Hq6$t7R`_O=h~u2mdA0R?=Bv zKBl9}8CGE&E8eyi`i_%a0=o&5gsOG|uau;eT@lZERDENtQHqz<)(FdWCr!_$ouXG# ze3eqq<-Zu)_5GJH{Uub&9(eslMmWv1t^V5HgDfz~Y7QBNy0hrqW7*ye>Ng!&E(b|6 zI*uE#+!}HJu46N%W}-XaJRB|cMkRNm^H@n>)DyiF6=88O?Dnit7Fv%RrzP5xDFx-< z)qHGksa4Izvo?_)782WGUF&%M>5UtPS-jw>j!l$afBcBmkYA~4rCzyYFivvOcUi|- zYaKC#t}ixqHUCiNU`hOk+Q(A6U+>ze77hgx9McC1Yqk#@+3g}3QnKc zBbE6ufgIO(BTiI}xPQ^v%W^-<8OfJ^_$Ka%X1bB`N{?gf`hlz-IzrA@lUIW!@YK#| zs!^7EMsy+8OAOu2zQNFuw=9E2>73Wrk!$6N1vdxX31RHD*S^}>t%jX7YK$FK`ESCi zpxnH5SL<=s#DD*|DKz&m%Zs8vQW{bZ_dM5h@ad-XcAoll?@_}Cw-2n`yB%#JjBK(~ zP#0aJFpaZ7UmxC&LGj@s>xBEqYkDw8jW&1f^Dw6;CacPmdOxd1I7Usjde+t8@dV4T zY9y*|9$2TwdZge=QPs6=MN_tBN$XY{iZi~km$iII8T^wo!b9qowYB4g=a+Z1ZD9Q& z`xis;ux>rUlZxY}dEen!9>+Y6c!>^=rJ@J-K4v8;v-jp=nVfZ?1=WXAK|FbN8y3c& z-?O$O{Dzm+@u9FM{>VMNK@6xg?|yMpv^m8}Kx5kxD$xlZ4Go(@bSLWqgd5A4BK6=k zs&00^rb&Xco!WZb*#q`d+k1B;#R7ESBF%;PQJ04%vxpv!D-SI};8_Et_Ctd52 zM;AxU!mK>SS$TgOY6RZbcq->{n-`lUCyypk!%C)Pu~b6I!ptn=uu>!3pIO^|;AzQq zlbUCc)K%6I)4DoLSlzN(34sybGdN;WyLU!gWOUc~d?2G_suWZ|vz@>$LBae1fM5Rd zo{fC%&syu@bu9|M!`T{=>XHSFUXeQ>fG54*t&AW~>hElYPhY~WK%5P*jD3{L5#7{!4W`*AfYoWcohjA1N;)26T zk_)HzuiPlHif&P&%XGB5UuTCz+$Q;F5vS|nZ6|$oksIhP9c}JO4`eoOW4bL#T!glk z^?)YJN`moE0BUBJZjSa0tO~7Ko`pVo{~pob_z?5kpLE63XM?_)cRllWspW0mYdW%F zbt`@>oNc?ydYc~32s#oL06efJ@km=-4`qp~;G?a#-HEQ%-Q}PU7ODyzcP$T!_iU?f zTi&+ZYE=``%Xw(V45p*A1HQn-Y#W+!$?aO#QhXcpt=2Z~*LsC3FUXZJe1>;wCfUCn zwC{2h<&z8E;lMBa+2Ey;D?vN@@e3wn=;<7@VS{HD-&m1$jO{spo^?m#*MjzX&`#3M z`?uVOcYonCyeDwneR=nahdi_VD8VJK0Lkk>i|_wP`)eG!eAxX@yI0(Q%U`$rsJ~qQ z_3rgAtzE6ahiCag`)mI$dC8#hcjO(ECBJs-yMMP~K6pv!m+t4EePo%u<0CKroN@ov zv&m)h`cCi?kNY2<-L!0mNlEasj(g?V&C5O=t-JQQ?aQtOmSL0o@1A>N*{7DfbI<+3 zvUdZhz{h>&EBR$_`_uGt!Jl?7e`VLQivIz7=lu`dr@lJ0>@EK$duRNc?$xh8v+R`r zOZHCqzjR;z+U{k?{a>?p)c>{nnXi9k*|6`kr+wf3>DRx)>rcjbL@KSD{Ki*9AF*`| z4WV^Ue&ZXV57|122GY8Fp8r~Co~^T>PwU>hXHRIBtuo%Db$_iG6e^%`_3PJJv7eN6(Wt+-S^GsLs!@urx2xeAAI5Y&}Fu2RO+pDxiGE!=P$h+I?2|DtQXL_PyUAO3Z;BoZ3Us{6Kf&EV`!L#u|{}Q|p9!M|ohku}ZiNEsSdt`|}|Ll9Q zCH_A1y;W;IWkaBT=S-||`&zwTs!)l{V*ztM=8wDLk5)B&%q;iuKl)tpQ_fMTP6cc% z2kocKga1Nlc4s zwWw2f(zQG3;8OAYe~8_VFw*<6L;&%fTv7|9;%v~~?!t*cG)@NXrvgIgjt3-ILIsYD zlcb3v-wWE0X;ftAsxL9^p`g;@j8F81ye@Hm%=|iNZ&MoLU*i6x`DM`Fra3GJh!&hR zH-q*z^+6*<8_MQ`pk36VVjx63^Ay3jXr_bqHc3KAL_jW^$)LSWoLH6;QMqEqg7!9< zLTp52#?AXdE5>~+SWINIQ42kmWohH8mg z&6_hpdz-Sc+AIq8p*az>w`m;8CW`hEFZD@VyiM*{zYk=Nx7b`T&fFJD z{q}hIEg=>}*quVMc&8Y?Q_SBp5W+5EZGlw%Tf~&svw(*0Jq0_k9Ou=1rqPJ7fQP1#=`f_x{fBeCONF z76aX<^tIrJdWzE0dJ2Aj>(5hK+Q0pw|Eq*jVTw{cSpK3KgzG=6mA}B16L9^nYUR(W zA5|ye`dzj1N4WD0T)(YW{#EsZ>La**L#_M)5XPy-RlyRzVhBFJruJ@_68-t@caMxu zfAr5ccU9j)y}@)XBT=GEF$^DGdEosB#dLogeTFW|1cc}n)vqpm3k*+DM*3R1$+$Xu ziC;D5qmh2N?~Vb^b~Zi2kFqE81XmM+#db(cUL z+5AK~I^KvzlvH#D`ifGWSDl{i<8!J#=|M^gjWoK`v6(U6gmqLkB4I=~mJ! z=QZAPnz{+HMNB|C_?DsTLn(icSX&E)Q9LNB^70g^-Sl5GP4tk6?`#wiByJ$xeRTiPW%nJNU2W! z`k~QB-9M>HVCvCa-gf866=;}~oM5F(V`j4fv{No~ivnr?^x+`YEP6N8C>XmlMI2Ja ztdETT>)l_euBuorB82fbZ*%y#Gnt<#HT-^>P9G zMf2w*jp!$IoiFFe68XuU5^ToA2CisW4y_!Y?O8ns^B_rMt;Iv0yb6XTcB8u|qhV;p zH|V%%@3B_w*V%}Uqm%p4y#ssqt(%AL?bc*;79ad&%5yNeMsflbqzYn2EOeE|*s zf_S8nhvQVs6s>5iptC+9-R;pJqu|`K8A`q7f=(HzWNAGb7Sws0W+$QnIzEbq*}Kub zdF%G=J%H}*Lnpx+JF#DDlbHdHPD_vzt1gugCYnL&*$iE`^o07(0yNgDkX1hf;EL+9 z3NtO|u|Txw3nCLm2L^7xe2VD|@P?|%c`{YuA(Iat&R7y(ZK37lQit~1-JOPB#Dd)Jeld{qA zKBhv-5h(BpbP{NdWT5K@&_Hoe#>sHhS1ldR}2K~CmaSKkbw-HGTpz67E|yXF0qYF4N% zHMPG1X@Cl4&efwaIdX(bJJE20K#n_Qp-FskO@&9*;Z1APUfSYWjK1eNu6a84)h)}C z5lXaXuq?1lBEuMjgxdrN7(E21(}ixOhErR!Wj2^gs?R_-O^vj)%^9An_(VzwH#WC| zhP;6{D$|tf)~rm!>(}CyYj?i1|NROVGwkldV(85^^V`d>&Oumt5zCGYP(RIzf=l*4 zF3cchWk$yaW*l^>dapo%a45S@As1M=RJ_0crJaNaUZ1vdjdGnb4R3s7w?`=c+_G#`0P;hDH#o z*|-g;xSCSBQkq1-dj$efJ0` zyms5KkSu3G8m8EWG$z(_lY^*G=`m@7E1T;{7kR8)Kd||&P{6xH&FHU&WXXS zpwLbbkws{-6k}Oqn_v*=JOqTv^`u$;3S=9GR%*gHuQ~^!h5(_BX=W_UcEu~uMTp~< z`q~BAscToJ1p$TrZ6)x#9u00=50YN_v@C!5pPs3~?TUBNXJXHJR^W^K6w2XY>elR7 zC3sK`3O}tGYJ_spqhW?FEdf$x^x3-#++Op{pO)v#o?Z!BzJ41TTu*rQZv!a>rLA0> zn$7WKuptaVI*C`fveUT;D;gDGeMv*WOcNYdhr@#(jL7(^wJA1O`WL z#Rok>L{pBG?m2`@%pfh<)(_URQkk}L<9>8{e}Ld{zQc1hM-!5=6OSVB~ zXNF4-?J0fRWjbred@DJJkscvJ$7^M6xpK)cP}2mqzvtIr1G587sU`u+h^DPK-<-`^ z5=!I9mhf>k^w9u1E-rn2eaVOD`aa?SH=-$Yrwm;{_Z8iebaWYgU4d?((W=1A3Y^Fn zxH7eM6G7a>zkYp83PKz?L4EZI>}@4za2orS{XOUkn!=>Kg}y|ekas4JC&?vf50U`Nvx};ly?xPE3=nk5KD1}S*q3a)(tbe^!%w-sgW!3y8 zG7c@&t*%i}N=A+s|4jHk6tf4Keg!yv3EZv#M|jt>|CMP>GzbbmE9Ru?G@u&kYU#~2 zF!#ivE@q(|ksL#M-g)#`U)`3pZIJZrC`sFoK1TN!pb39;)gG7O0Q=NB-U3V4Zm3mR zo#>b3?xH)_Z!ZbG;Q6Z2@Ub!K^6bDE4D!EcfX|_CK^9j`y3wuf_lb;!?JNg(E zOw%)>nYo5S;&oBRPV!5vMoRQ$x2xp>UR|m^4!~vh=&C=OSODlB?@ue)0cqv7v@Lag z!3LI{iF|o=a;TY0L`5ftq2=*pIcF3&YedBpz|KTSg#11sY63*>1Mzdy(EkmpGCTBX z)dYYubhWjxo;Hmk5Hs9em>iizAeGgjf!5mgSBVcmC!m5mpe9!wfxBaG3Yq|AR-&&e zQugesF$v{?3rJiNnv~8oVg)6FyivBikz@`dTRm#?~wGpmG- z!Yaqr-Bo3QvjdM)xtL)_Gcd{)LB1y!BqW6{Nrax}g__jt+LHov8o==~nn>w&1O;&f z(TqYXZb57AnU%cSURzM?k|Y(B!hB1pJ3}mKlqoSErcwlBKw2YqU|Ci7YR74gna15v z#V10L_gCn&@)bNg=mZRE$EX%-rMB)|T`fbFJY!J;Q=gICs7NZUZ!L(fZru1X_*;)M zpnImW7hNq2D02)D8tWvbdzq}TLMJ;D=jn$gnCZ{6NQCH_-I=36BNNmoj{+s){T3Sj zi3hqOtja`Faqnje196OWJirUTys@!5x}deLG)d7YO5}$oEXq)DB}xpsjJ}cAw~YZ` zO|aaBartPKt}3I1*cIJ-Ok;O?q=aMgBDIb2mK?8yX4Z;x{n&jx*pDqzqs z(~O>k`Cxs5P$ta`U84TyD#e(&oRCFldhr=_yLTVBK;pEMu#>3%h!kCH zbgyj`u&om$5mJ!1A25fUueN0zcs~T3PQb;Eaoa)SnLWZvTm{y>9ar^KK@{=FchHrJ z$H5+j*@2mmTEbA1w;B*Lj%4kTz{Dr`LT3dPpg@Ki+ z#ySo>OFc7=_!!sIfMyDlpROW5DLO8xlZ75Z!;Pn@GmipK2b-R#AW`-V=nDiH>Ya*v5QW_p%92OZ=zLBbCLuyQC<8V z`xLqby0=&uF;y zGmN)Qf|~I3$=?*C$1| z8vIQsmUm|4o&@?2LD$yu1oT+i0#J9HFT|BqJ(VOinP!fAx_c|j9t(U(H2_|0v+w+V zZC$0Fw$>su_uvp~Lu@d|tdq|x{IK+xK&b(F3BZ*9cQQ$j|7j)1RwJ{Ci&kLFx=@ha0*xE$AQ@694z8Z zXGNc+FqxL6XB3wTTTE#Xpe33~*rS zxgX*1JA(zDE$tw8_n;gss^;goa2q9Zsbup6aDNy&*3wN7nZ{c<#DV1F@-aw#MrW#K z+VDC!oyUS7fLW~5RLml6tf!?-GiT^&8!mFakZ$EBdv{fbO;1R*$1g% z|5Z@Y-c(_#eSp1=eY#hDx|yQRfu;))V(y%HPrW^%r8&7d$;V_ON#|K=@Nuw-xYvOA zf%%u@Z?IqRHp0p{;r2`(+XC}u!#_*0K;yA4D>tV!bdO%JIKe##i!4kX>J(<_^f z64F>m)#3zxd`ojwNHjNyEv**tX)Fu2RL z!xm}l(j4kA&w|a%+%Al(6arP^Z%B6jC6Rs4n#V#{st|PSqQ!HxLEjC{=gc)X49>|2 zF#tL0)7jN>nSg(^2K(zo!_cP;UjjCD8St={D|8}byDLD!?1F<8L` zl5AI=gE{81%(17wp7?0zQ(7ke=rYeZ726Y)|2y$se6BuB^V&N0x{W=iF|VF)Qba&(vf0tm9}D$yO#9Rzyc z3yQy5SeXK0*4~)cj&8^x!Z@JI&YNT0BTTI=*@7dFoKcV5JEW6i~Snrk>qwFMg*(F|F^(Y47(1TLoL%n0|G&Ccku z1B7rHy3x)vwuiu*QYl;wMgZMGS|UVuD#4(m7hrz|izy(J$I>L36nf+Hf{f^7i7OUy z%$*k*YP$yqvD`^-XsQS6&1Jdq7CFzyao{ZTt1$2hL1%`{G~?mb*v6Y` zX*;CgWgbGHoKKQaCA4C?Mc?$4*9e%doezd81SiY^v15G^Ui~$N0D; zrzyA_JQO<74Jj$H5NL*SH4Ze^@yEss7a(O)g}(>791~U0*5SK|B^6+4)5}hnU0mCV zo7&L$NeFL`QwOARU!tjgXo3>Vy{D=d=cneU2F_m=n__BU$z#o0!AS~@hZq>@V%l7q zQ9%LZAV){R1CW8;a`!2lnWMOV}D5G0+p+0lnNSiqZ|6;dUUs^ks(6>qb(Tl zNRwckc&socLm+KTU}NuPc1GBVuJ3&cnlVc)ES{x-X-e#}`GG(h=l51Y8%R4yd!Su+ z9}e11SjWA*r1&qyI!7I9Dh!QsGosO0?p)8#P%O!5ZNnMu^^Le~YsVO*c#p*2MGOK)Vbd=q zSVblDL|lNQvxl=Mj)x?(VpjYys>9b1Gnl5>+9)FjOF}zYb92#HbVja`VMUF@QLMAj zKgGc~P{+P<^)!8G1-exiPb}lW9yX0hgL&>mS!oi#8iqB&zA?=>hqXv)x6~4|%zY=I z1+&yp0Vz6;_XMhr0ddd|kU2pM{@xm59J?N)h9MZAoup#SEbW}lEe+}JxwvC9o-^N2 z*PNXoNrY)jnLvV$*TIbC4s@fp8%(>uS6KOMs=fbm9yYc&sVL>?EI7i#86mlC87xVt zqz~t`b+qL6H5D|Yadh?sjB_3XKZQ8gLCBMcH9#NW)Hn}k2cB1~H;>Dn9sLA_&UWO6 zzEwHOC7AuLo)2VGYWDVMahaZgRjuSAbpB>z5SNC*!2T{7GDo)w6 zU5HZyJ)Gk=<5=(b8rCehC#a6x=BOCo+_^!uieyn^PCDmDkXL zhK8r7s%C*r=Rx0HRf1O>nt}9>hft9l?J_ImFolLX8p0NZKnB$3$`#4#Gi(>xIb(C{ z_?i^A`AhMFGBEeb?fqmpipgASH@c1P3t48aY-o>PcAa^!*n6>0*dpJU!lK*|AV?+w zXMuHz&Mn65c>ihGA&@ChhM@&-c1VyZOui^1qne9i8oI*Dm-!T~3h`~P4a!lJ3YuEE z>3A13|Etwd+)_dY*xR!mvOq^?Yr30D5Uv$tYjbD3%sW0*)+lC0wc(uB=AwfQg@emRiV$a^02Okn4;z=Wh9xr{tdAG)6V&km=mzLF-C=l zMHR)^<{d1;i#yO*kaf&fG}K~SP%EylZH?FShgq#pl*DqCFk;JuG|peoA%YbGQ}T7` zo4xIPY!+DuH#QYu+rs8t+=>QpLs$RwI>GOQ+fQ}j1~ibN8@XOp>uha_t-_nDu;TT*f-9-=zSdmd zn9a6XVO!Bib5~Jw8!pVvz}!L*W3U%*4+!@Pu)WirS_$NPUCFre>z>1Q-;J z6)rAE;~nLT7qx`h5E*Rn@r@Y=;=dpEBrGP6QNA`^Hn=>TXmufE2a{ckZDH?Kj-)m> z)?-gQ8pR|7AQKNHKPJ=qUv`&)=+T#6tl|*!jON1NjP~00B4Aw9QAA{Z3LgZVfer-A zx4$zyqB=ly<6RA1SgMt7E@fD8_Ye12Aa`XbPb)#D-1)YHiYMa@XwNN zy!rdWf1@H2Luc{9JfHA`VTF0<6tFnkj0TQzgL8|;Ojf+N#ur+6dinIyGHsuXCuP6!< z?5?aZJOm9IYhA@lNjQSP#P{DPdAl+&6Pv8|=tlQ54VbP;Y~N_uhY3vwi8q9G93;L9 zhc|SQw%`6eKtayXy&#!z;uH>cgh2_FO}>1rG1`}%UBFuy}!$9ol(e3XR;S%A}jz>WHAl34%QQ%8(UbwOd zr25^HhrFc%c`A#-F{o@&)YkH8d5+_ec3=_V=HKmsftaI5rqBdq9+t{*CM?gvYEuu@1H2zEftAX z%I4_`3UOOGJ_X%8(&QPz)Q#YkLAGiiPOe4HJb3k<9)6lhsLi9N_upKVIM&uu#Fy8kn*msRaa@wI|(C38> z#CgD9BRQU45vFeb9+{5z3!hcq+yn{#FA#bcI)VbOSdzRdx&XJMp|0|GvLH1Y>+r#g zv5g&fnELSv<6VNy)x%We2AeEjP$Berc^J+LS5CI%RHNfdR>)EUjbQ}!GyVbn4gHLM zcxh7)8jUq|OR8{*Z))uT;i1U}B1CwdQg;09$wA#yDLcSgr=h1Sxz^Fz6z@`zt%o{fFYxC@Vg zeuD0OjvJ^ah;?5D1Axx~cL-|0UCn)so^;nFFY7hvBVcf0ru$}?0lOc_x&kL$MyQk|;;8a`B5__*^K}(!ek9 z|Ig@0c-=(bq4Vf28uen+wM|R=+PXjiU`T+5D448#A~3K{HbgRTbhfLluhdkV&h~;x zbr-7r4yvD_aSJFB8Qp?iYQ;`V3R*hKyKr8l55xjAl9Vz60vdV}FjwKk)i70+To7dw z?vvM$RbJG*D#Izc0)28A*nmLr43BAEhk)c@$)VS45?FQ-b;n?G4}VUnPW-0;11b#O zulZPAgdHoP=Ji9cdlB6|^!MI?-kGNGBl_+z`lKS+DPvW0QF&Gaq=7b31<6%JRZje8 z0M}I4p$YN1KH-Zu;BZI=z&jSjIOSwife7vbkDK^sOnlFe_$EG&cOFI;HswE?k49S1 z7^OP-A3Qq(p0ovx0N5sU;V|9_=r^Z@PRy75br*eCm678VQv{+0_lCnaEDra{jVFyh z`ENga9Zob3Q)`xDn`Lus$nXh9Ber{YqfcSM<0E_l?D7ZvBZl6`cMpA_eDeeJ@oQ-v z^h|4tL>$nF>fnpf4Bx^1;sZ7qe@#NXzoVm&W>JK@=dh{Tq+ngma(6 zRQZ-yi(sr32{~Cyt!(ACL(0G4H+LSwAEWci3;6Uq=oC3y`FQtDs?e!-@M+}*bRK_v zXeWMC`Ikf6R?2EyAYY0^XAc&=x}}`ZJ^xtu-G-B`LlhhdDW8s&kbf7qpz(L!dt>KI z&%e3z%@6QSP@!p&_ml8WsXqN}w@;}^C`J7v`w;f?p;X7T_y z#^+yoVM90^UCkW>7N2YW@GhJyK0(EdQm{Py9_Y{{1p!L!0vw>4g#DuLAsAN2kYlq~ ze|_Nxjc1lMopIK5CR)=O>IY9-&wfP{j;4Aw@%-&)u|EOvTvPqJB&V$}!u2a^B{@TV z8LnSaE6M5Sui*LxwelwT%CF)2IkoZz#Ee^TeNwHw4otp->l13_HR!;5aD7y*Bxg~7 zg6l(SMpFNs+VM+9!32Xmb=wUvX6|Seum8z z>LuCfM8OY!pd|ZnG<~8OO)~?{tTne(vvf2|RC6zA?m5l9tyx1fYn5hA)U3UlHD0ro z{KKxGmsI$l)q$is6N|PRH(%XONIk=;L zi6Vc*k89K)@#8K<_WY>F$eE}~_N1uC)tN|3_E4zD;+g17_5rBJ@0o~BR{f0p&f_~SP9$N1w0^+)_rQvZMA$9F0N;u;?ufPAkqAi4O1 zGg$Xj1|)fZFofJ!84z#%;{)I8_mj$ixc8q`>dyafDg$!qmj}KxP^B`M2_uF!riQK- z^PQeHv|_QG98J9K7P>nu@^my=wUiO$Z0hf77O{N63b)0ku0h&vsb%Up5pG z=!H=ciz6c}S4PCdhj@oXgapMdUmIr1e#UZr$cmU~=d1{)ym0G{k)E5Q?4DW8&UnU& z7w46jurwywA|=tPB+BNw_>kDdwcDefUXv7GxZ2h>GXuUFv1qlWAbxp%qQfSReevpL zob~3b)0S+7Z!}1^-MIcwg0+?#dG={(D|RPFb2HXQW#y%(trMjB$8O?md(OKy*`X}O zXV+S{UC%jfU+`9bZuD?N5quipMr?u(-RLW5UDc5V3)QKfYfah^!( z-5^LPe|}?c&Prjicz+c~MP zz1_V%cIi8>9xRPMvNPmRrO}5wLPw>s!`mYdRAldX{h2>k#jI&*Zmy3W*cpGIhJE}M z+P<3n*7{X%HvHGnZl^bEw^zLpSk(M{{j0UFH?3-T!>?}7F8==gMf>Wr+Z(ulez&P{ zN6A|UuI&_0R;NwXa8A6Icwm3zwOY0n;WAsV; z#({&SH`_M7{(-#bz1EQ<#UH%)=3f-Al@Ip4GI;o0Z~CF*M}O=y0X;c4*7e@h$j_(e S4}CO_&wu>U$7i%C^83FWWAsA+ literal 0 HcmV?d00001 From a36b1b8cd3ba8507b8e16f8d0e256939baa731a0 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 2 Feb 2026 08:23:01 -0800 Subject: [PATCH 332/356] Fix day-night cycle render issues --- Art/.gitignore | 23 +++++++++++ Art/Districts/1200/.gitignore | 1 + Art/Districts/1200/CentralRailHub_ASIAN_.PCX | Bin 15380 -> 0 bytes Art/Districts/1200/GreatWall.pcx | Bin 20994 -> 20994 bytes Art/Districts/Annotations/Bridge_lights.PCX | Bin 0 -> 88323 bytes Art/Districts/Annotations/Canal_lights.PCX | Bin 0 -> 115795 bytes .../CentralRailHub_AMER_lights.PCX | Bin 0 -> 15131 bytes .../CentralRailHub_ASIAN_lights.PCX | Bin 0 -> 12224 bytes .../CentralRailHub_EURO_lights.PCX | Bin 0 -> 13735 bytes .../CentralRailHub_MIDEAST_lights.PCX | Bin 0 -> 14794 bytes .../CentralRailHub_ROMAN_lights.PCX | Bin 0 -> 16806 bytes .../Annotations/DataCenter_lights.PCX | Bin 0 -> 15249 bytes .../Annotations/EnergyGrid_lights.PCX | Bin 0 -> 104481 bytes .../Annotations/GreatWall_lights.pcx | Bin 0 -> 20994 bytes .../Annotations/MunicipalDistrict_lights.PCX | Bin 0 -> 7818 bytes .../OffshoreExtractionZone_lights.PCX | Bin 0 -> 4353 bytes Art/Districts/Annotations/Park_lights.PCX | Bin 0 -> 24063 bytes Art/Districts/Annotations/Port_NE_lights.PCX | Bin 0 -> 20827 bytes Art/Districts/Annotations/Port_NW_lights.PCX | Bin 0 -> 21246 bytes Art/Districts/Annotations/Port_SE_lights.PCX | Bin 0 -> 20756 bytes Art/Districts/Annotations/Port_SW_lights.PCX | Bin 0 -> 21565 bytes .../Annotations/SkiResort_lights.PCX | Bin 0 -> 5303 bytes .../Annotations/WaterPark_lights.pcx | Bin 0 -> 7979 bytes C3X.h | 7 ++-- civ_prog_objects.csv | 4 +- injected_code.c | 38 +++++++++++------- 26 files changed, 53 insertions(+), 20 deletions(-) create mode 100644 Art/Districts/1200/.gitignore delete mode 100644 Art/Districts/1200/CentralRailHub_ASIAN_.PCX create mode 100644 Art/Districts/Annotations/Bridge_lights.PCX create mode 100644 Art/Districts/Annotations/Canal_lights.PCX create mode 100644 Art/Districts/Annotations/CentralRailHub_AMER_lights.PCX create mode 100644 Art/Districts/Annotations/CentralRailHub_ASIAN_lights.PCX create mode 100644 Art/Districts/Annotations/CentralRailHub_EURO_lights.PCX create mode 100644 Art/Districts/Annotations/CentralRailHub_MIDEAST_lights.PCX create mode 100644 Art/Districts/Annotations/CentralRailHub_ROMAN_lights.PCX create mode 100644 Art/Districts/Annotations/DataCenter_lights.PCX create mode 100644 Art/Districts/Annotations/EnergyGrid_lights.PCX create mode 100644 Art/Districts/Annotations/GreatWall_lights.pcx create mode 100644 Art/Districts/Annotations/MunicipalDistrict_lights.PCX create mode 100644 Art/Districts/Annotations/OffshoreExtractionZone_lights.PCX create mode 100644 Art/Districts/Annotations/Park_lights.PCX create mode 100644 Art/Districts/Annotations/Port_NE_lights.PCX create mode 100644 Art/Districts/Annotations/Port_NW_lights.PCX create mode 100644 Art/Districts/Annotations/Port_SE_lights.PCX create mode 100644 Art/Districts/Annotations/Port_SW_lights.PCX create mode 100644 Art/Districts/Annotations/SkiResort_lights.PCX create mode 100644 Art/Districts/Annotations/WaterPark_lights.pcx diff --git a/Art/.gitignore b/Art/.gitignore index 6918d97e..5ab70449 100644 --- a/Art/.gitignore +++ b/Art/.gitignore @@ -1,2 +1,25 @@ Seasons/ +0100/ +0200/ +0300/ +0400/ +0500/ +0600/ +0700/ +0800/ +0900/ +1000/ +1100/ + +1300/ +1400/ +1500/ +1600/ +1700/ +1800/ +1900/ +2000/ +2100/ +2200/ +2300/ 2400/ \ No newline at end of file diff --git a/Art/Districts/1200/.gitignore b/Art/Districts/1200/.gitignore new file mode 100644 index 00000000..3a7f50b6 --- /dev/null +++ b/Art/Districts/1200/.gitignore @@ -0,0 +1 @@ +*_lights.pcx \ No newline at end of file diff --git a/Art/Districts/1200/CentralRailHub_ASIAN_.PCX b/Art/Districts/1200/CentralRailHub_ASIAN_.PCX deleted file mode 100644 index 5138f204b19e940899feeef5d0be0b5dbb1ae867..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15380 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 diff --git a/Art/Districts/1200/GreatWall.pcx b/Art/Districts/1200/GreatWall.pcx index 348dcca777337951a32554bf1efc4c3a462c0c38..2dd668f14b55f2b725da0253aca50aaf3ccf6465 100644 GIT binary patch delta 16 VcmZo#!q~Kgal@ZLHirKo2mm{j2TuS1 delta 16 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 z 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 literal 0 HcmV?d00001 diff --git a/Art/Districts/Annotations/CentralRailHub_AMER_lights.PCX b/Art/Districts/Annotations/CentralRailHub_AMER_lights.PCX new file mode 100644 index 0000000000000000000000000000000000000000..2a1c81b95ee454ecedd2cb6fc617b3e46b727f66 GIT binary patch literal 15131 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 Re`f!<{r}DXp+@u1{{?rIXO;i} literal 0 HcmV?d00001 diff --git a/Art/Districts/Annotations/CentralRailHub_MIDEAST_lights.PCX b/Art/Districts/Annotations/CentralRailHub_MIDEAST_lights.PCX new file mode 100644 index 0000000000000000000000000000000000000000..5634c3fc391caedfc564dc997b796f001bbe85a7 GIT binary patch literal 14794 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 KC0~)1fBzd9n^z?O literal 0 HcmV?d00001 diff --git a/Art/Districts/Annotations/CentralRailHub_ROMAN_lights.PCX b/Art/Districts/Annotations/CentralRailHub_ROMAN_lights.PCX new file mode 100644 index 0000000000000000000000000000000000000000..5da251fe039526c5eb28fc9c3f28f3b2581a86e5 GIT binary patch literal 16806 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 aUfZ#z#@}{zj-NeS=W;c?<^4vC|NXzhVo@Cc literal 0 HcmV?d00001 diff --git a/Art/Districts/Annotations/DataCenter_lights.PCX b/Art/Districts/Annotations/DataCenter_lights.PCX new file mode 100644 index 0000000000000000000000000000000000000000..84c85db9a7f8cc874641012eaadb67410250d13e GIT binary patch literal 15249 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 Z#~W))n;NQH8sOrk#@kn`HE8DV{{fIHZTkQK literal 0 HcmV?d00001 diff --git a/Art/Districts/Annotations/EnergyGrid_lights.PCX b/Art/Districts/Annotations/EnergyGrid_lights.PCX new file mode 100644 index 0000000000000000000000000000000000000000..363e44d19c09c9f1e710025621e7d8066be4138c GIT binary patch literal 104481 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 rNA?^$az!H16fCJ literal 0 HcmV?d00001 diff --git a/Art/Districts/Annotations/GreatWall_lights.pcx b/Art/Districts/Annotations/GreatWall_lights.pcx new file mode 100644 index 0000000000000000000000000000000000000000..348dcca777337951a32554bf1efc4c3a462c0c38 GIT binary patch literal 20994 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 d<$>m8R{Z(mc~Y5%Ra=J%(!@qZrp{V&z<7FqxR literal 0 HcmV?d00001 diff --git a/Art/Districts/Annotations/Park_lights.PCX b/Art/Districts/Annotations/Park_lights.PCX new file mode 100644 index 0000000000000000000000000000000000000000..121b267cc1bd460fb7f1ecc5b157af816615fe7a GIT binary patch literal 24063 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 Z`%g)yP8|OH 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 Bmq`Es literal 0 HcmV?d00001 diff --git a/Art/Districts/Annotations/Port_NW_lights.PCX b/Art/Districts/Annotations/Port_NW_lights.PCX new file mode 100644 index 0000000000000000000000000000000000000000..3afef1e24d1d80724138620681fb0ba8e0f5680e GIT binary patch literal 21246 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 MWntx?5rY2yC%ux@%>V!Z literal 0 HcmV?d00001 diff --git a/Art/Districts/Annotations/Port_SE_lights.PCX b/Art/Districts/Annotations/Port_SE_lights.PCX new file mode 100644 index 0000000000000000000000000000000000000000..15a83233461790736c67c812ce2d2efa0392ac29 GIT binary patch literal 20756 zcmeHvdwkQ!l`oh)nk+cD{w&MIboFVxUP5T7jfo!^qx*x9fQ?h2fWmTlm~7JBxR6jg zHkGz&rwy9b+(lL>Ax}lHc6#nbG4X zA!GKxdp{o_jHI7t&YU^#Ip-*M-a7glqwq(J7NbUu#^3+$pV6b@{`-rN0QueabqAI3rbl70;Dxy}YQlrJ%*ZuQE~7xbfe+Kus&en#KdqR$w80uPwJp(OXo z=?&p0T{=BKi_uRL*X=u`r%j)oH!tsm&;hepo4p3JjgN)ip`XT@c8os#w7z#;PRSqk z)#=Q&w8+HsQuC6Q&2Gaf%Zp29C)W@2wa`6!JAjI_oZLwd=kDFNmFXLQ zxADo1JZ}w9oX%4o=Drgk-d_IG2U2HmPA;k*dA88o^lpGZitz$auGp})kmnRWT){W( z+qh=>Q|q5vU$ivYlr(!4>tX(P*r`+EvcA2%aQe#R8Q-tDk?BHr>wVav1>q%vA1EQ-$83 zUk2nljGct}6WSA8%boMR#j6e^xC~aydaTcuIru;iIGtaf zUu@6g$x{x-n*6IDrzM$+mND_OmoEUU5Ki&69M1qa$sFskS7@7Z)|z1-4dK& zttlxrKj-IKnFQTvy9D;;J4;q17cCg?ikdikRUza2t~949kDEr{v2__YF*oxpKM!>f zq8|2w#*_Lnecn=&LF#5Of5j#VzYkph5^OHX=OyKQeDZjH%~an3cB(?-6$v0t-aMYP zam>6ix8!WS03#zr6U+vU$MqvR6A7v7*GS=8Z-VTBKDERV)&~(}?ytf2@2$%(*_-H0 zjxm*EI%h{KtlYmUHz8%VDakbZ7Cw5mX-rWMq)dobNCl0@pjlj`&P{uIE=lA198ytM zYvn!6XCt0@Aw2oT+mjR5V4v{3IfM=8Hm3m12rYm74^!67nZAC_ZQOK6QQpSiBrUrD z84=nx%*hTjzqVo3l7z8ox6Dl%J!Z6tCoNsGqX*0t;v^VAW2@c_pCakd!+Fx`LPg2 zY}T9fT8Sr4(epwf8z4F`z;A+>l;qn>a+4QK8LtklrW_Kic0S3v7Hi`RZoqhxzUamiX< zyeM&c@|yLDr^Qw-4iLj3vpp$1Y0liFjq6Q$Mb9j|?eQOXgF$K;jkS6;KKaEV$bE51 zMe(}rym&^@^yKkdP03!t&ak66R!?r>+QKwvlFO7dC#?s>p%smCtk$dWIwz1QB2mThmyjJMnr|9E8j`OY)uN z+w;(56t44rDW2AqfGdK|f(kSE@bZcJeKb>+k`qB>R zs;lK_blaUWGN_lMF-qu?u}G5~jd96&83VP-(HIY1kkQvMIT|CeUK#YAl%p{K?3V%N z895q*O~kA?k&0nIm+w_1>z9$Riigh`qT)+3lvV+Ck0GocltH)(#lJ8FdeF1U9#j+uUpUQ%&Q(CNz`j~*xvptWp=F|JuW^gK5h?+7xt}9!>Bg&v+Z?n(m2Qo@D}njiLtg@|US7(@2XB4M zm~{`W4^Bc#dLS-qaypOCn)L*Zh(s#Q1-N!K+l9$=6&%uix{bMQ%*#DCWYB0L_GIpQ zOyD|?i%+q z9;!dl4RuieLT-}C+L zn&vp&RwUyj2tuwbyPy&~TC0sS?Hb7$=Iy^L-g*~JDdqj#qfm9TX4;po@P1U5AfEMd zb2_M#=!2rg5oY_$ggeceGLsdjcd$mp!1@l3PAQOO=lm-V9(#T7< zh%fc5`1E_GZ01wO*I%;^NWJsHJ1@P-4%;U(KQ^|qzVw4at2Zud)48=WAo9kkw7H@& zcDM6>o5aCW13W2L(vLDrPClN#|IwZCRB;g zrqoX_Mx!c{v||#P9Uv^_9g1Xw?9Jrq6OThK*`@f2lWu2&YzU=GgAyB8I_anY)WV!O z8e`heKZx7UA&~Z_2mr0XoK#iqsFW2ZYfpGpMJ1JeO0hp^P8{&UDk-cibOg$3T>#}n z`mF6CTm#z^{y45JJH2uy^FPkoPeQ)=C3cw&@-Nv?{V>b02>bbdKZ~&sp<#!C0%!gj zVQ-FD-V8xe78rOLwb&h6xr^~qJ8Q6(5)W_!5C!;1(gYZxK9R6f;_f_-BF)1KvtS++ z_Te@7++&6Gy!S}_8R&)aAj2@sg;FS(RS;;OO!me4lU7M+ljpPAqrv?42rxG&RhpNy- z=I(&jWW^_p9X)DF{dJaLhd+P(_Qwz;J7*O&0w<|;hnF_$0joofP?-}rQMsEnyo~k1 zF7t_+wL$|wTUaw#?6JAP6)eMB!k%)ioum*wb}del)S6XBbaA>JtglQ~Dp0`bg4WE; zC@{mwKn0@bRskds+Q6=SZ08kr#tGrFqv16TdXy1zdR%VI3$K8~8pkzOYiE6x5I?^S zUfc#tA24<#7lbaQj5~mCB}iRiUuD?Gdco8wd~;pQ zsR7um#U2Mazv8rW-Vt8YphsFDY1iNgDsnV}e~dXk`FlH?Ah015b8XW89h@u$vNn!WWSf#KuiRWC<27K0{-MJWM;jfP-Le zE98E%o%fWo+VpJZ9)vcqi!5X?5wsGl7}G)9(2kXM@JXHt=1)U}VL3Yt93E+#81@YT zf|7@!#7W#ez#Mi%SyYYn*d#WPGT=n=1}1|5s$6ptmomtmmka?BL9)|r;T}sIxh9yP zQr57G^<9Mq;eZ!|KEZWEt3oJX3!gkHC$pwV3^I3)2-RTF6e1ENBETTPN@AHjR0wJn zB-QHFtgyr6PZUk$(GYGS;3%sNRI!fZCI)ms5ya>qft(34I8eFIE~R2RhZ_DocvB8$n^xhQ>fGle#0AK=cO_&_|lC zn2N{-9O;2Pz`E9;D_~bTw>`LE3jFzXCnE|N2SI|Bi0xfEM*tOJeChIVeQ+eWOu#OE zDE$Kn;();iVQQNKRb1-EaME?Op|mU@+cMN)=*ByQ#spTIQQ(+L%@VQZG!yKJ*YQ1_ z!+?P4h_JwH09V-&U{wpE9IO7&QFd2ZYoI<&y&lz~6gLOrA`S?ACJE7uZi@Jfg1_85 zeoR&Lx2+(`h<>N@S^(q(BTQ_Zv~V8c>4@;iKL9m2J10f9#{z|SY9y%_afq~KkfV(g z!Yz*Kb~669g3)96&_Uz3M{mT-bjAVz-YOp(Vdta)hnGdS%*fu0gbjzNJsD^xpiY%0 zQ5rv4h~wx-fLE5r+=&3ch8~|B(PwmCyDaR^lxd=X#&Gqhy#bi24INzuG>^JU6yM0X3ZHc$_8*P*ina8N_n?^Enwb1}%sUTD$s1f77 zd-vbnk$A@~iT+57;>Tu&Z(DGN5seb5y}>9E2(?|Xv>=3wXaS}7gOD9JVkK$}3lyos z4Nueu)-j?6Ukx!l+zUk=XzhS}UELZA$*-|fs)e>3Ug!r!P$7*?R7dsChnL7pFtF5V zRS)AG?yfo^5sWQ2{*cKuc3qvuCI*-05BUUR16Dg>63HdinbnNQB;1X47N-nyO7&;8 zA`lN(M^tB)8jm1*6Yj}Uompx;hA!Q3PnPP;QsZ%4cf&ndD%1}K{KlRv)tRNn&-61` z6YR-Sompx;qZ@m&RA-jDq8fX$RA-jDY#V#BRA-jT85n!ARA-jTgcy6WRA-h-^B8-w zRA-jTWEp$1RA-h-(iwZQRA-jTIU0MiRA-jTnHqbtRA-h-?izctRA-h-HXD1gRA-jT zej9tTRA-h-${YKl)bBA=Wx&`+rGCFbBRb$@c|(n;9-L}0d{$2zb&C7Gz;gtzNG`H3 zsb)dXygIw$>Z`*yxTgfZ8ItSOZm{;$F56-2)m`Ef_60k_2Prucc#7QomC}QKXc5&} z8#{zbiCz_+HYm~i3JsdHM7Ao!ZLxt1u-AySFEO}6jAtUhJ*C(8vD%N|+1gk~^hEWW z6eLPp%}%`$a9yYl0P+B0cDDo9O?nOmwQ76+1n`6Laa5g%m~=v~LLWDfrbu#twHoG9 zO-b=!fJ#V*-d2(V)c>`c|`IgSSTAZbONPQzasDVw?Uk?ngL}9}{gr zE;yA+FiHx1L^+pvf`yW0X8H*YZdE=DYh|4t`4yjl93tZur}z`dH8=}R(~Q!yp=rl; z7dENxlhbe?Lo}skB6j_p(d|W7zqtXtK6Lpuv9{-&W^BgV&7g8S`@C6vrK8c8F2!Ef zc_-LhgL#3uJK2{R;1X{69I18-4tji)IRk05x@%WaeqTd=(%B~${SGlzoP#i;C1&2Q zaha-8zw9{;Mj(;XmPyF4exQRxBc%Y$3Yx`L-G=f*C+`L}T@8V$3-3jUGQ=;V^rWgc zH6Y35U8n-pM=og7omQ771x!jcBEwI)!=^E{fO#$4Yn7Y1sXC(+8#SN4((1k{B6}0i z$#i+myd5OL*)71)(u$H9w4tA!y_I$G?pvW9#s#f>hbSg zz`#xX=dxL|-6*)wRlU_Yq0TTJV&oUr-#TV2N*-EMc*!w9F9l{s{Y3xw zARnN)7A)lE0*zkfEm`JDl^KoDE8heNI}Sly<*guMKZHjnjPA!d`NX40C9wg@taw?+ zowY`l3@!!XEo`V1#2f@+ycm6a5Xj?J4V0TB03anpWwZj;5uwj=C6I@SkSjqWM9d_` zR#46ZfkgJkOXO7^g2b29)#=yJgHP1;I)MgWln6kOaY59^wTX+s9-klycd`qxZBfhm zq0S@*t?VL_bmyg+lji22u(n1nZqWLB5@%wKAYBP&Z)RMKlnQ!J$n zlbf{?<&`fF8V;CdBy*(EXmJtUkz>m&0KRSrO}qF)A>Yo<1;|du#<<1_)p5f~krew9Xwj^$K`Rs|-QE|3>x-~y)yA_oCM=@Nd2 zyon0Iq*YKfiaL{E?r>KGtW)G83E9oh(!4#VSSoy=cW zN?ApCm>OX{83_P(6bKotCx}e02>wYKM0_Ua;4)hkB+6$(q-4oSCK0V>Cs;u_YxGsH zeo_=r5L*T7B5iu9wML8MKG%TNrmSWYR05v47imC}#&&jIvLtdIcI{v=n&g?f=l~vA z!^_N#Lwpvo5b>0I0$~ih7dEqiJ7|Wo!FJ+2bf&>Lvlf8l0D}b^P#0Aze$(Y~fg_lO ze?T8B!nu)q3I^22bas4#8Hps#jypxE6YKd+J_wU0L-2z3L1h$1r4SZ0zw>*WyQJIMNq8~K$%BgqW$m# z!BUqS+yg;^l?1m7A6aW%sDy%?vy3uY&0r~H#*4xpqC|OY*gzi@qgmgO_?(VR;@`>d zluhS-1(9)^pW zj|+JzO6dp6VyN0cXh|F4{Lw!{UaGw=R6J?X`7s=(gMALG%gqxcsRx)J-6CK(<|2hj ziv2Z`zmnJO>@4fP6G6{N%Y1uw$qFO+N-O=cu>*d`U+O3Y`f!uyqXB~a(IBCr(Y_-9 zlZXhQaUr0<6hNyCnGVn~O>_6fvd$R~U|y(`7V z1=(SQ>7>EnxIFkst5Dt{6Np9*>1qM0!$zioC@Ref5VM?%f{Hr;P=uukV*?osc^nYZ z9RSKDRCKBZfg*4sQ$|oBQUjrUO&^{Hj2B*IC&@tBA!CgNKQJbPi)}%IP7-^NQ~h&8 z+-u9o7~o17uwT3j@;>4kvY>mk3Zt9$g7NrSE6fd?^Z5vfa3M|$gxr7u;%6$lN)JWA z;4MSYAjwu7OM)|ceDFPxZgL>bmL@G&G&>xsxF4Fo&{S(kwS_MDG~hKn;GLz7+b@=glB;lVt%paT=^ zatLZW#Du`2@y8Gnc#zOl$y(H2TP+a&P}QsjdL)GU1uj(&oz#B=dJ>uN&d>Wuh8vL$ z3-S?usM|HnOxWV6YM?uoCkGcJ;X#sMR^UeHB8PjTylF^pW-&M)18jInn*{<+WQ&Xv z$bb#RP6oT(p?qBzh<#&-RUF(H#ZzEq}jR~(hG6#Apl!2{9uV^XI5aI+9x#(%5 zYa?05t_cN*z3D4As9{Wn$9BFKqL#w}1p;H-he(eh=MkO*`}8A#xdG6!o`4*>CzNWg z){kJN5T}r_WtVvC@84P9tq zRYHv@@G>!+Ds2;4FM@sPp>|zB^}s@!Z=FE7@?a?;rfcd)x#QYp$ZG{8-cboMw3A!0nYnz7cnbE+tWz10#;K0-_(@dUkk6;*+JNsQJ=X1tcB$NJNy%t^ujY zJ1LoF)!1>3ha$j{B`2sJvb+#aq92T@(;O-RUSt1h^|kZyqydDJL7=M^3L16A#P`Ga zlQ1^HiWKG&E|3m$FGw7_+(O(lOsp4h+t@;taR16;dJ$5H>%ohxWqoBV-1I}@+Q?5x zh7WIYYoZq^F%4F^zKXPQ{m||c0(^QrrbVo|cDB;gVD0NF9S2sYA6DlCC>(ESwTo>6 zgQYuKq5kI&ktMO?H()UaLzX$RHgoegB(zbA$IKzs2IzBy`fuDWFhW&fX%(3 z%j3;*+-YQX{@*Xhsq&i|$9C_1>isR2`|nF>-t}1R%d4BJ-hX>rVR`BDy<1j(uzmgc zj^mXtKmGjXWsfXMe<*kA@`Y(bM}GbE+O)kpo@_brOy_|m)w|8_zx(E{=O0_UJZE`+ z`VUs+{{H!g7w2WY@a)qH~0J` z@4=atrMWqoDNh$^Z|qq1*s8Q=pU&9(`i|zRU)?+9?{>fNy@tP7)~_&a+VK5%_S+k) zUiBQ^{hzy@`sD4^-(Qrx?b-W(xn(!Jr`Hx|6HGk00R z#8+N^PBY(o)wlDJW$9_(P1^O!vIpm-u)jREq+sgG<4dMmu5|yHf7VM$L_!XySce(1qHKSd+pJTjJqdI z8vAed?43Jvwr!j7qfL{`_pJEIGnreSOW(0`>M75**Y{d}{bJ^<*)8q zSn|xh<*#R_r6;`mar*0TJ^0dpWIX$P%KV2i_rLqEZ-4Yph3i*k<|b}=b=vea(=T^C zXiEN@vfs=x&rN^k*_BOQKUwkEq}+wyT<}QZ`Ds#)Om3b?t zSf2RKyRIFN{U~pL_0w|;zB4U*+)KNyA0FFMurez(E4lpr;@t0<<}aQ(WA3C4WlN`K zPWbkB5-k7x;BWVBIdK&!0XcHm6;efLjYxKi7?Z?og0SMJT4 zQuLc2JW{e^@4mf1wf+jSe%Td5`%vToDP z45_oLZMM~x|C#1%BIQY(Y!Xf_cwqjK7c`dGXwDf!r(9pqRGAcJ@@yUncoZp z+1-Eok0x`0-|yaY&pq#Z&Vf5`8~TkQ_$P?~5XvNWrBPp^>Sd zgc`pTRd{n#jbEto2F5S(jI&b>v zlqlJ-Uy2$$lA;@9pXe3!(k+9D%xba4cc8u3VP@IxXlxn7B z<*9zpJlM9-I%_qkgLhI~z!($(aaOvlg==|se)eP8i?H%U%oC3&Ei9UqCdD}{YQY!~ zU6}IC35>5V+~E4#Y%PBkmL2c+tU(mpl=N>*WgV_E>!z7&Qil~tab$ou4Pset(aw>RM}9vj zx&h}1#*2`u7RiAK3npwBH#;SL5ztzbdPhOd#^0nYAqgAg^-)pua8Y4dTGIIM1Vj%& z9>jQFw2IFqt795F#)8iEt9bsbl%YI*=&+%wJZ16fT~bt;C#+n*C}zSDaIdgv-1wZd zkzL{jz*b>wg*3OxxBoG7#;W_)KakIYiRrU8rlx!?CH3!KS;_>&K)vO`vJKYR{oWoF zxuv;fqy_JTxPAN{!$}uF_>%+Q`cQt3j=4Y=-)DNl*X_%bi(U7QQelV-(L>{m`7RrDmi& z{_+j|Z9NNQOq!MRP)_=6^V=Gcs}(IvE0{O34bZBg+ccgPr$tb*&o~fqE?@PdCl{rs zer<7D%DR-tm#!}O2Td&P21A*9aX~82NzX}HGAkwKy(Wrsc~L1JnKtqwKpukr(s){& z6ut6A)&Xogm_LgJw>&v}98Z}&CuQTh)SQB6mwf%nD**iuukh6c5AmGTjVZIfo)SAl z6W?2#TeLq(OFIv!N1>`To)k^u3t7SfJm?$^B6eiwugTBQQrxL28GgXr_`8iyZ3JO} z@+3a)F`Ugj0|@in!kuYJtJ7Kmt^q1YW0PpaS0>-dR2T=LkDZfOW$i4==jo{_seINC zm~02|l#Qz=J-zPfbp?ylQd4HF#NmSPuru*yGm%^LFfU3QnKaJ_&`r=U8XH9ezE(;O zM$TnygFCo$VRpvQ)TdHXHF-eoNHjfyc*RVb7SA{=)oGTUM0&*J&evJ?RvnwL%3D}W;#5Bzg5#~4>CbWX+ z5|q_&vr_iTgINDil&DN5iY)g^tteM3-I9egx=H|~#Ur<_BFliQ~w+b853flCh)C9v<3D162rq9WEW{%Ed$XSqc$3;0g zb#~Icq=J$2(rPf5X6o~qX&uPakuh_2Hh9Q7${7bEnWs%oN}v6B`m9O24#q4DAq+#M zP~<#iRXz`X8G~1dD6m(65BJltBmc{ovquQ;Aj5$V-*z*GxFPNmt4LM5AnZEZh>n*b zJTx|`F(eVhn2Qin8c(Y+BzDBOR%iu{q^A)fC#cO(NE%z!7*Z|T^)ze&jbv0Kx=s5v z!4A<#z9C{%2%iS&S7VnNLw1cYIV!u=7*u0GjVEFI$yE2Ku~&`P)OZ@6hP=ZKHQrQX z$Y0PIG)eAGgU2&_C&He5AdQ;IAGRuYTdl?eCf9pjdG|wVtTlQ1i;zqS^Fre>HR?e{ zn+jAK)aWqY@^;nsEG)^*{}d0kMUBd*~8 zj|)TW(py)Ca5~`DmEqQvVc?4ay*VhjYb<5_jc?r|i+whlt~MOHsNUUFcMiHmREvbf zti*(Ul;w~7#KnKV4bJp{18_CcYSqYtbOLm(;0cMN zZl6oQky?>NInA_S3%llxnxa=mJksq-7@M_YM)ArlDyc+%wq|j$Z_lets{|B z+=!_lhX!&OG&Yu&&nRX$n$0ux;>mFoK5`_xWqeS~%#W+NH-t;QL zj@b_go@gDOIDE52ex_O)T#%XpU`P3no?~9tb=Evhud_5E8{@Jj5HeuH4k^wE)1xeV zR@CLMt`u1bnREHK_+|m->kM*Pi9uWuqkKAR;$C**lxdz`o;i-B zD68S+I8~WzrOWzV2vG$UP-0(CVe4@^&d0B1gd zGsnKVE6bWyUM1`wLQIZXU5w9t=e@_=A~Elqk9_Oi37dye9>4`J*Uh|&(Fz{7J2#cv z6be4>U~bmrF|W`IMJHvML-ROziPgndTJM68mazurrE*jBtI@@-MD?mtBz*Xsl`-o~ z)D7yv@oR~r$K1|(S>JVW3|M%OCia$)G!Vqm<&cOnJS0lch!1UmQk!X=UT!*#Y^jyi zqHKUU9-!P|hcskaK_s7A)xx?ce-nS+SQnCS;dxvj^9X5V)gLi7$|JTUd}ZayTN#Zixg^XX5NJHd^1(NO9)ZW90KTauom7$Y3*)GF&BqL zTR}C9PYK|4EAyiS7(WnS9S0*Y^=e|Lgs}>6D{v2%06unsx&d7^=-(2A8$!+ORy4%{ z)l9d;^{Io^0^|R7xR^%dwN1No^NJ2~YmiNv1r9?{6~JKe7$Dpk5$!V9AGmI&ba)OW z5Z#K#2O_{I4@~vRfOJ-no;k}oj)(JrBKY7Nh(%owvbIoSJkn3Bz6(GVF|(tX^DK6l zNL#xFRs^={>hyc2jZb5J?Z8#n6xbp|xRlacLB9a+uulQbGA!S&w{`er)tCX@3&RE^ z7-YO8GYWL53VK!YfEBnq?d1>=VuhjzWQo30DjWy`e;`Z9^AfiQh*R;4Ds0^_e^$o# zr~|-JxXNuT;KmVH*pgi$5z(cYL`1O<#-tOPfbv^~{XMt@lPRJ?UU>`x44k%3xJLbkdW`)5v;jK&tk({)Sqvjc4 zpJo+Wbr{88LbZa`l2TZSe&m1@r+hXbL*(^3I&exlxtAbfhGB=n1Dfi0LKpSbnXJaj zs!763tC@H%bj%?t1|$;VggB3n%n&|42hf0J?ntPT(Q2l4_2@fAY%0Y8y5$B>bS$Da z@r43BsxHyBO;e2A2gw2la6%IL20|XJDsD%)pKKlM44H0DilABT1I)&8GWk7^ZXVeJ zy9!XsOevDX06+>L7vNf_9nBT2c2Bi5YoiOItl)=?F2xdb5VGI2n%wA(F#J~7Z9SNm z+)hPPDrZ&UDBxDvJ_uJmt0R3<9774NoT12t#)h!5*FlC2Gpzj{rD%RW7>qrc-0{e~ z@m@$IGzNG9Kb+FAHef~#xqd(Es6pTwqpXS$%CFE)9J4~P2z&taP!}!*>M(d-6WZ#6 z5yf&z-lsiWe7CzHl?bL-kc=BU3{@aGaiiLI#q1}Dmk~!{FP_4x-Ex4%Fb_3T^GYU+ zA9)Vf1t;^9c(vdILDaY{BAgj+oNW%8qjALTHmq za$h)5Mson&cG`@({}GAUkAJk!>dXYK{UzuXA}dveI_H%rw*tg)M8m5bjK7M>#Ho3I zx_9KTGYID^Ks2I1f@7U`j_Il)0}QJ^5C&ZmQdy5R1YCW8e)!1`VHMM^LVY$iZq+aANX19%7m9co1QmmuM2#3uWOo1mFw+&PXQ* zg&@&jHshnm)C>rb48Q%oIA)(BbT}8K$$OxTF7yI2V}Hc>Ofv6)IZTRpnsq^3-`U+J?G@u#FYjI z*>$b=cK#P`q>=s?E30)oAIAVk%)Fh&2Y*$%YNXi*b(y+Zt_*1Pr#o4<52>VkzJ2#_ zK5E?Xp?{S$Y<3kIUN$07Dh#hYg}(Y7g`WcHO`Yw?O00ihi1KQ%?7wmiK}j z@xa}Gc}LP6UrRcq@EXiaLm;6p6sZhoJP$>~yG{Zbj^96dn-B|S@S!|$Z0Q$@eevE5 zY?^dqcMdDBN#=*ag)Ew{o&m5bqytxCE#6ItcfW@Nm=VJoHDgIoazAZkj)6&PLw!hB z%vR|Jls@VK)(sYQHe>!x8FjzwgdT_bmw;~K*qh2|;v2vkAnG$|b-lPw7qqC0*kxEU zbK}!-(L1QBHN#=_;#lU(5Semwi_|HwU@*~cMk`ehFbUs3nD-L=o4J?j9B4b36rM#a z0uUVt#8}rQxO#KD6?H+z6P{+H9ji{otf3YxYRZgpfai?vtOgaQhdQ#_G125@&Kiwc zjp$d6G-vq-|}gu~nG+v-B=48qW)Jf0o{*MdL+b?$6RoE;P0w(@$;c zdVf}Aye!Q9S$dZijh*P6qV9UVKPxhJp|$@Up6mTtkuiXdsz`s9-laujQ2qWvs6R{Z z(xUMiI;|r8S$dZijlII$pQU$c(b%Vcr6SayrFUu3cvF~rs`OvPpz+IaXO_83EBgB* zf>C7Edqz-%D4`nq&+r?9`8$lK$Oq!Qv`ZzPN}Nak4yndo*2u1WdQ!k22Usg|fG9)g z&oqBq#x1@NyIy1bxKOS7bV=@}ZhI7cOm8*+g_!qx4dxJR zsIwYCJB9HrsyT_sEMNcQek332fHJ^#BZ&7P1z3&J2=|-bGk2$ zm||teslXHUfQzY?A?#$8_#L}8m0jOmP5hvnijSSYlU-p4s2Ecv-PqBp`PE#+o^-dX z#|m9mPQ_cJS7wf?l{Ll+Ql;o8n%INDxQ`t`g6cEgqd^Yto$nTulsM1ZvHwLO;M(j~ z)REkNW3Dp+Ahak)L8k0rw!WmqVPzhRT4EAa{324+I=yz&fYokVi}Yy% zr3>8h~zg! z7U)$Q+)9jPxDX;Oign;L)sEu7fp#8aL}x+*MNN!XovaT+f~=$$GSYe{nJOn`d)(Fp z#QAEv9rJwpY^X$ESZj_&t<#G)$7PAret_1xyX_7OguIE0NO2y67eQqz+MrxlF%e9% zC07wz6q;PPlU?qm5)IwgYn&i6GIAPaMajHG)-YSLS-D{F%nIS!qcHWd*4u#SH6UsO$9xtVP*9;=cmNs1 z$SUMDu!{Mj6|{!%4MWjOGbbs6p^_9{1&TwU?94MARBEZTy-8Miz)1KBYp_7p zdO+2nBN;oxDe#^!)-CK3&<;{1qygDgFYdh-)|G%jHiUT8N;?2SD|~UZ+aN;-pkBN= zjo;V1%5%pmh;th56l9o0~GdI3#{wi5l|GXEvn3R8OyxhiBC z$ynnm9JNIQD2{V!-bx$KgWJ@;VzM3laT5u=3WW{_5(q(d9$B4abug8sQU~i&Okyo0 zS`D^Jiw#Gh%&77pYTHBC@XG&=(3Z^YypueS>`j$m=Djoa3tM~9f;k4qKHe9ibod^ zQc%&N6Oe>u2mBnc;CQe<<;0@E zFi!%((Wpv9A{%3E`)$zx8nI3zdb^}bPS+$ahG-J10jjq^;4B(WpqN681m!G#WWXX0 zM#DoYm|+g`aWJsX5~oHXacc>D56LA^qN9;3HV_`Q&MTPCE#MCECik%!)70ntTrld2ul4Lgt`?wcQbzi1Q!TkDGWBLCb)*9wJM<`an!8{2_y#x zFVOLbZJ;b4jmiw3n?VA20-cmLBbx^m4cSl>cN#b>Qk=b-z&{FJ&0%PVQHXN7?d2o} zZk&}qIH)ffkh?G7sA1PcyaVzm?-4e9bOPiNqS=kmrW;5Q&nP#DFhZ?W95~M}`V8G8 z4qB4INElW65Fcq3WvTs6B)e#%2sGH#=&gFF$g8Zfm-)!1sv=##%}(|YZT&_K%1uEh zg60bBNihmkje15XT}bRClH$3lM-hr)w*{O@o)kx%cIyZYLJ#hC!{C$OAp?o?gdu>nlc;3fEq)p>7dyz_3Eel#xLF6i%VT&br;IT7}a`J2((PcW{0bW3V3A z1{P2N)J)i!2ne0;@FneMQIo?_h(>5aN@nLl0DV4bu!nS|SdqZAnGRN&Wl|#S1_{Ie zQN~^A3#ka9Y~By)U;WB|6k$O)mhU!-TIh%5Rv6V6Njh2%_+w^*S-Dh-Y^0HXdoWftMCDjP_2un8%0T=*nIaP5^69;ly{hae$1D|yZt zy9cgarc2&YltKGl5Uv`6nOq3OWtW z(#jD5!m?CisgD5Bdv&-0U|@H{4O$|08%70Q0N$L?z}fo4ZjeYAN#p7(DP06vGhKR` zL$MG-1@aNxVzbm4wKzJpze&{{u(!ccJ(lb5I?;N|N4pU~0~KXMyx=2(3ams9s|rxg zgTs8ZI>%SQ(%lncx3HB4-SseyZXN?|&`8yF{oX8%#1?iV%D^pnO@R@abeJ_z+Gjx| zX)5)Q0uc`f@tq@@Ai8deYPjA=2D*7Iw+CY&%X z^d>5|hpyK2Co_!1*`=^#;6VDlhs*#OLvN!*fQPKRMA0lTsC)F$-3P&iO(XY;coxkP zU$KN(Ie_mXLFfnVB$e1hIXCJvzz>wpQ8~8@k=RFhTq-!L=m&xoeS|?>+~>q_x^+|Z z1pBC2s9va!KIl568wYVnU{@TdC#AJYs*Ih)O#vSxV|U&V0fV`UE1`&nP7|suqU9)_ z?*c#R02JN<`6}v6iZ@MBe;|(nC69su*@1V^vwDOm*#P}I41x{B#N!a2P(9K=fX}%O z#UAKg)mPf;Gg#dyakBtugB7sT z2$3SWIQ{b3tk^k#>uxmFJJCX-+wmK4UEv>yPhVv`&tO5FOu**-?uKW-l8B9_`lw&o z*ggaa`geLrKqnr=qMgR{qw46Sl9AjQ5I;Q##TrdD)lOKxUW8-x70NP57=8w`4(pr2 z<%hCa*y`l}f=-QbYOvUBP|ZGrDEpAwum?J--)cCjq`gqZueSD{IECA|>UaiMnHsfC zIvgZBQHow2ucJ1(rQwMk23m2WP{-?z`>AxtN z+gbNaSIZx_Iv=n3aM!Vf4O)3U+&%Uz1lz7 z)-6ljwBh^jRJaaTzU)1|_uuzC{pnk)zP})C`<4fPwRO|#6_b}Pyg%oW@dZyzJ8^LB zjrI-izBcc*-Pud>M!)piUs^5q-VE$sz9ci_fs{QjEqUajaqLe|EXtd(V(G-6{CL`H zd*|X#>>x7T)Z&r!}p&3!)s6G&cAQU4@->tA!l;^$hE6c2z zY0qz+XUYEd{6|xN^Q#%9yC>i5`{1`Phz4vQSW||`RbdGy!dZZw>&>??xWKx-u=;AAOCIs zx|P$ileWG*aZ*O=uXa6>n)c1&-^{km$$WOpilY~Qy6lND+4H|OZ+X&_|2XCG&Eqqt zPJH8oZO{D6w5@wq_0=$J@v7f8(+0={%E`7;NIOAKiuB@ zcGk*Af8SmG&yD-71xvR6a@)V|{kMNFEBntk-+brYcR%|0W6kZZtFQO^d}AdC!FMUSW0 z1kV_Y%Cy!CD7E03vDKrk)6;ewYwN68`wJ2AMlr-|TM4(2fD)j#RZsVxXMZ!DR^9gvkCerZ;A;2wk`nU_4MWel%45WxK7YrX6 zC4Lfk$ftb!_=$VWU$7>h^x(rC38SZkC5C;3Z)JDc@W%6y$M3UI%2~ohGVGWo*g^4; zfFfL<1o!$#7wIK~64(Nxqf}yyGD_rEDkI2zJ*#9fzec-p4kMv`7^rE69taDp139t(G{*(-4sPv@Ij;LWiC8KxfIdH#` z@hjj1`Xz<^7~W4t^s+|+qDoCV7TG68k^%fnL8k~6F*-mmf%>J4Bfxu{dR9~^BT2w1 zssy6|Hw{gLV+HO8*~31W*-tNn8IhXJ$$S&|R!mbUrO~;ei;gjRjdo#zP8q)d4$<2%Uo%f2Lp&ZjwcQMr z1mQe|CduZQOnN_H^a||*=`$H`0B^(d9%fW2tX!(Z9m5yO08J8=f>QE|R@;+|cF+J= z*JQi`9H4%Bl|`tzc6wy_jf7&QL>P&XMZ_l?xCGtw4zNSU%RoQ%(GK=c)tev;Q8iIA zL}58WB_U2}PZ^p)X`>^+Pi4FW^wC~K=O4;f!yYFXc4UZ}FQ|;zo_3sz_B{OxctOT< zz+T!-ud(kn&y=E|z&52|lPDxeq9S3P`Zx+YZK3ypXJtGC?51AC^E<&4R%#Lv3^+!# z>k)>|OmNN7^Izv`cTx&$mQezF=`H#h!pJ2bx~W!T*e~(wRq#?^67e=oLQHxe51n{{ zaf%aMj^E07i+)bMY-=;yu2vw`yh3%;%4l;4FAtD)mr5 zd;bjEj;*<^Fv3pmMBoKt7L{cl7QnEp&=N<#j2>vl&&;Ibg%+lge56;QQjor|7=+m< z3Tauc@6~gj^?`B|+ThqLqnozV0r-9f=xKrq5$h8b4%>n>64Z%;WORL}p^K~wgj)zS zN3V?Sklk$@BS^NhObwsANtZ(4E{R0!1|mgCRff#|u7b9)FF@;Nw`BZW#x{D9-eVCG zDrBGVxFGq&WL{J?%bWixrgnA%Lw)R~jMrtn2<3jmcBR=7^HJmh=_Va|)ZC^FDL9D) z(_XA~elI9Dy8_YxyDFnc#`Cll>!P&uG{sMBQep;Pp!=i%BGs2FhD;N)pE|>~US*dt ze2BSaY?rYW;(x$UVg@5a>|{7Xse;zJVRav7XeA>;oKvBRJ;QcfXO}=2WoH_XL} zqRz1CL}hW=%uvEUxWy!lM2gACS&Wf?u;Y)GASB~C&(wgUyM#p2z%UY6(lkHm22ZhrWJK;`i$ron^|U946h)3 zw?hgsg9Hr_HV&jTQ*o7NTuE>_qJjlPDFP`Iv`LSE~rZ(Jf9s>hZ8XtOP5t$ z9#@nX`7~}4FpD9GCyGW=PNW>DUZr5I77i^iJJFH^m2WQF(>88ew9zkF4J;nASB6I+ zn~{bv1mYeN!lS}5RVQhM&R`wF&g)QVS^0%=^Rk@|v)!bl3A-P5g#QRLp`zkEh0?mD zqGUlGr8J2~%!6=x@M4n6&y8Cn+;oU18az@N9u}v;H2rdSC=*dbi$bvzpJ-C?$#Bw$ z%Iy7T$1PPJUTwH%yDgga{G28}H3?3EIkJ(k2g>Ab3-BPI`jE3K$y8o-X56}Y6?L=W zEjR>{OxNV_l~<)(MB+ge#fC7IA3x!}GKx<}&I)M}rK{&Ok6X8|qtt%{_d?iMy_a-b z(J~R=#QHcDKEZb=FkDX9o7$pHMX@Y>v$5Pc?y7z%UcaQgAN zPwJ6<$OD*LQ7;)BQg~W%RXG{==;81p)#l5QBS0l)S0Q0 zMXPgESj>*p<@t=Bd!#KKK;H^|5x+iWsg-ou!{Wni^@moa#KxspBDRBJQw`H6SElc+ zW%SY`o#<`a&eG6h^(gcT;ttj2kWq_6GSpW{;pEf$rpl>5QLousSH;j9jBR~~=%=8P zfbm0qrRgR;(bLn(Xl1pfgbZro1zknX?AkhMKW}2k_S(a=jg=zFePw8f5H3YRbUkU+ zPEFCq%`=+Pr0rl`gT@|i@kepB*{>FsHI(YJq#o%J!{% z^?UHhH4y5>4Bz1oFPEW#qiaspSQF;Pv>zyG-Sew9*Qxh+EW1gzj_!N$^2w^+lX)$T`co&~IZ#{jd_i(SPJC%m%3HP1 zEY4o|`j+QU9@%)Z(R%k{;f-_kw#vMlEnA<|uPD~*3Ru;#Dt|LPaH zb9Qb?*io~#xKLZLcy4y4s=Ore+PN3*2P#e+F&$}0$v02myJx#WuX!-sup~b|?#Y=4 z_U32)JcfK!l4n*I7R0XmP2!QptcG0%$IEebwXx4FUik5!tKL1bGAHYa1=?BXUB5Bt zqmvRNPaL-%f7`rkhyJPLIkhz=eM)4OarSFF79DKRKDc*k&#s)t13y1?GPi7{X7BDr zv9YSPYfBOmo=i!JGn*If-@ja^i=HtfbgLt&p&@zaPVKt2vkv~=@={e|&Gz_x4eHN2 zcOE`!eRWsjqU5>HERNq+TTpi(YkigG;NhgW`0x{di9h^a=I%FjTWVu6jEToj{QCXV z|F(2lQDRC&O{G2*bp?o6Tx+3$9V>MUrerT^-p?f;kb?wjTCd1pUJ5Sy^I5$C^RcP3D zY-7{uy_$up^4Ch2thXFJc65_%6IwdmGs&wD+cti((|)e8!T0gbTOTYeGQKs^@`~rE zp*+84`?fb4-~7X&Lw|hly+55e@yY4a&gSO!%a=R7-g6f&T)T0jufIPhFK=!_Vq%JZ c_ujgp;BbMpaB0~ZqcMv&jQ{sz0^@&w0|o63%m4rY literal 0 HcmV?d00001 diff --git a/Art/Districts/Annotations/WaterPark_lights.pcx b/Art/Districts/Annotations/WaterPark_lights.pcx new file mode 100644 index 0000000000000000000000000000000000000000..edcca3e31a9215a15f117abe63d2bbe82eb24a2b GIT binary patch literal 7979 zcmeHMd0dS9_rF;#x)J$NNmA%0HKs16M*DIzDoUeUnbEFAeWgYFC<@^!EfTt=l~%b# zMV!w}+JvqpJC$oERLJr?zw^}Zy5+y`|G#?8na|8T&pGFFmiIZI^GJ`C8vg_S(Ne$t z@WUuKDQ5j&{Ym{W>c79~UkUVge@)+grs@CxX1Gb3=pI@){0o}yz^{_N|Eil#ZK2&F zfQ4$VeCfvmt3uxH~{Pyg2M?DT=SiP=rQR zn-Ieg1sf$DRTpSi3OznG7-$n@ue|j4GeDEyluc&J9=T2>)0CKcj3VxQqpzr|l;vUc zAqF~8o@Sz^L<~_1+M?atjGQB`@tLWvaLXY>ZLvV+aBU8NfwpS4u2I%;l{F4Q%BDVP z2cSVh?-bJ)s63FgQg9a|GYLLX)N>Tn@r}Nq{-QjWoE73va04@I`Ah{x11SX^ep!W{ zp^|56yyZg2MCi51kX18aLwh-?G2rN=hm@*hZ4aFWSK$-sLx zY)dB>lD%aLG-#;0po7W*vXwYC(5EXm*8jLs&=K+p?vnG>$o)0+Xu(^OA)!Udh(M$> z`2tmf`FMjc8+*py+DfqI7uT>eI%P*$>LdA$MtJjp0vKt_mXeIRg?sg3?+QYTND_H{ zEGd}`Bqt?{sW`2L)zF7&S`F>9S-sRJ6yNxbcB6P!66t{#nGs2OVl=9&HQaWMPhFx# zqHMHv6b#Cu6Vrd*9UCVeota8R@nTnNh4})j(BoOMuBp(I9$X8zpfmsMuNCBNh$nds z4@71W2to)zd7fe_WAbckzKp7%f^YVgqENK>8|_37DCJ;gz7`2F+RsBzoeqcPnjcS6 zmmGGp;j0P^^;De!Dx&lN$QZ`c;vVtqv!1rbYf=36v2#^pYRk@6i^?+BM=vp}ujPhV zT6?RyVWNOL+#`+kd>JkGXyWXsYG305mTvLyUQ$0gw zTXR$e2C?;07;%pL`d!5c%>yMuvB=fQE_D0xtk7ELh4Iz-;s>eKi##@Nn9EXUOUVkN z_-dmnF+85Qr^7WvjoGn$_g@7D(CGFGMK;o(hS7T{nDDscXYs9_<2DED)W|M5CSpzS z-koX2(I%V_=!%7I4%@IUrMMb8MG8N0brp<=|5VNCvsPoc5;B0{)d}bGgeMPhB#Tq;=k82FME>jWERb}U^%-!d_wwREAGO_8hnBI3 zxvS*%Ik=l2Z|PcX>nQd&QjwD#8^G38oJmj$1A_3P4DKN;e!`vH%O0a^hM6dG<{t`< zs|v{mc!U~s(2P_5q1RClpJ{Xa0_U?0*@A%Raw(QDiD_3LJv&W#sZW~E#JNj-mIoFG z&Qw@OENm>7n7Il?La{4@t-CAK*IT$(gGk5QejkXz&O6{a(kPoakp__xebwbWV$GePml%vuP}CJG@?^C2xCYKnrKCnFJH7S+N`F8zn*2lC(Gv5e+2edn(@x299qXex zQqGQ+BoBAecu0BEtjbe4j(W4^Pg@5Mp#fjrxS5|>8ZLqbcJhjHy1~=sZJ-&>n}bAs z&H|pi3em*Cke72%bYAZrm&hkB0!1b7Y@#h~;3DNj(rZS;&APaqK;J_JHQj)O2qvhmJPG~q5uU=8>!`yE`o=KYN;OmAL21PY0#A_{(_}}hhG@x*ft$|W zTU^rNLJC~?gQN(gmxruNGZ{B=$wX~6Z5e0?D$MftSR_APMc;mkS`GBo&t(*1%j-`f zsEvQtUd?$fWCBmF+3Hn)HmI%~;HU zb_jPc4OI3CB&M=zvYfagcvcY)sx_En*pui z##U>gNzPiUss^6J2h^?~o{Q;As9Wy{+DJ9vfy5zb)9M+@#B{0YMsfk#v<1GK$;-_= z;#9X_oLR(yaT}w3aEtfg&WQ!f#yT4ltFYDcjg1ziiiGj0B)^*E+nVVe!yIUX%LNX` zo5Dl=NtS<571r3>=UAz58&$(3$5$8h#ae@;*1d4gR1G)5QPlg(pmI+$8t4|(?!yS$ zfVCJKws7J=c++O+mDj_=dT2ydZf3{vf`eAFw*+~D1Z%mcd-g(4p(QjF5#u1@WmQUU z*4eUzBvpL(7`aiBYMV+Bhib@6a%m4i?KHzMtu}7hST5^|D9k_*(-0&TH?1HV$w4~IzD_G_dp{rRy9-~-5Wi8ynZCyGZI>~>!9NZ%B z4xEBV@J`CmJU(Ai;^|zIjz9O;qamLUhFU*dftJcvY-X8r=x_?CSS!6V@D%)o8~cdY zBLv~<1H4CDe?YGH@pTKj@|a%oH6F9!Q%SPO)p9mx^AUh$cJrqA#uPxu>Fwfqi;IFS z#!I^l>X_h?35~Vr#cR`vztJRpb%kZw@FYD$(OFs$=ST#4iH@iwla$ZlG5Lb4rx|S{ zG#k%ld;KQ`Da_)@ zxM;*q(3V!M2S}V8hdA~bWqpN*eCZ21F1EP4=Vf2BT8Upv`AA9HU!TslA<| z)Mj68j`J7+=|yL^SuI%O3zsn3y{w2=Ze%Vr?I8AE>pj_uY^Qle#u`k}cjkw}D`*Nj zQsklHAF3&@xFDFTn=x5S+B(XuOxkzNV8C$Ky zqnOZx=!ZK5L93k#@z+#W;|1$_lZC1rw}ivwP*vLqNF4&%X6y&>6j{Zj27Dw;>@hL) z9-e1&b;s*+$(F@T-O^K<7hDQWBoeM~FF^O*3U~05Y=c)}R(m|`V*ZnrXa2)CPuI!s-#m>mo~$q}{!%f0=FJK(f;S$< zR*IO6og!$0R~}^EETS&2xeC?LWvA;Ia10GJvIiA-ig+}<@D8op@RZa{SF#ml44t*f zOhXrb`P_{prFbzS*&FB;RiDF&3l1F639XUa2*zmcHm?}3NP92nU;^Qdm5e6_>Un6} zZp>&yE{84BpR_kiGX@U}GO_|b8m@mZdEPgUXhVitlFN8lZl*UEte!>=AmBW#v4+M2 zX#7W{_`uveY|-$zhq?R=Gx{dHB&|hWYl;2VJzhH^x8_2}9xq}%g^z7{a|E;p!$t8M zxQseo4_h?JDAdS4CWE^;7p?IznORQS2W!m}XbXY1-8h~R zCX?5~q!NUi*QptW7>F;$y+v#F$yzTo#8xj7UO?;;80jJf3l#i@pgBQ1lXh^UEPz9x|545G$c3_)g0)P)84aovhKXp2Y|)uk80<)Z~C z9YKuT!bH7{0U239il7z6T`G*>0WbcQl#k4vNWJFIs z)Sq9+X-Okpiqc*{7kMP^$C$$^i?k2!adlU+4Y?r`3Xkp~mnDV1rEsaV7OshJp$?Zy z3$qohGvkGs+RDz*DnS)6Rk>4m4pTfZp&p^PvCkWR7#Ui3P`5DYn1sV!GSZlW^c-bk zuUnU$Q6rGzt~7KUoBlsoMDJul)AlUrV2qht5KIb7YZEgHOR?5nJ*e~36q|UKnGTUw zv?Bxj=WB&W|or0XDUxt zLJI?GPvK!zt#7cx7+---Mmi=AQ+}{w!t)$NlsAYW_Mv3!Y3TS4r`+F$C_@94P8@!=&~eg- z1|fQ|YGAHloH8vFe)k@8_7-MM17-|vf$3&H)T@%{1Oh6f8OwtQdl4(86M&@y}o zHnjd52g5CNB~u$2<9@~BHyDRvj6w1REr485Kc;bbicyK32Dw677%l%D>kX}+P|uLl z&i%OI7JOZS2Jus-xD$rwN96Q%@cxh366~+4g%+G84%qxpV(?l5xFKQ#OQKn;BN9AW6m!T>(*82OxH=*h)r>g#upL53cTd>!#v zW%z>@nX!{OQ|095%T1U)eUjYFX;W3^kDBc@gEdQOp|*)SlK=TdbN+mmwynC%Xx-F> zEHynXH5Fa%^hLaJi#MC==*>1!QZx>oX}V6|_!o|`k;bw~zZ&S8n8|aNO`gNEo61XZ zU#e(p!m_Z?;PBK}n<>q+_TX4KIGQY3Zf>q?qd&#=DBH$DV5;F@p%Gy)(ceUlXKl2K zXJl@>+?E%r$=|Yyua{~eE3`NCShYlbZK0hL-@<*y+SSV)ot7)RpVsn-T<)fA=cXuH zrIx#VtmRt2v=tLKthUiux6{?x#nN5iC7d0(`j-$#qa6-78TrN9!_LAzlI4>%&!^46 zcSFEh-qHZqAXii44LpJOI!|w>K#$)X*Dv?;vpwM~qZd(G?k<<k%63z3vG-i< zxWdQ{9$_KozL7fqq3b-NE%jn39G6abwE5$W;C8ZRWAbb}oC*ybPTnO4nMR{_<9o+K#=(SvxlD*}eWy zlAuMTw>#O>F0*rgin(F_iOl5Ra(4L@?a|KPy+&~8_~|6em!hdx6V;>l<^7(P;`2vQ z`TrH57X% zowzZO&n~Wx&#bK|KKW-yS#a6OqWv`uC#$z!E#F#yTDV10QFEeb8#MKoSVl|kww~Aq zrw=}<@~=F%zprx5#Z!Ch>f@jP>2l#r{QEzxuGb~pJiqs^M$r?o`+dpgCnW2y*4+b= z-|jahzi!yxaejhGGi?3XIbm@N9f7iZuU3ovy%>Vxa?G70m literal 0 HcmV?d00001 diff --git a/C3X.h b/C3X.h index bdd9ad6e..fe8cd227 100644 --- a/C3X.h +++ b/C3X.h @@ -940,7 +940,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {"Industrialization"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"Bridge.pcx"}, .dependent_improvements = {0}, .custom_width = 176, .custom_height = 112, .y_offset = 24, .x_offset = 0, .buildable_square_types_mask = (1 << SQ_Coast), .auto_add_road = true, - .img_path_count = 1, .img_column_count = 7, .btn_tile_sheet_column = 7, .btn_tile_sheet_row = 0, + .img_path_count = 1, .img_column_count = 10, .btn_tile_sheet_column = 7, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, @@ -949,7 +949,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {"Industrialization"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 0, .img_paths = {"Canal.pcx"}, .dependent_improvements = {0}, .custom_width = 176, .custom_height = 112, .y_offset = 24, .x_offset = 0, .buildable_square_types_mask = (1 << SQ_Desert) | (1 << SQ_Plains) | (1 << SQ_Grassland) | (1 << SQ_Tundra) | (1 << SQ_FloodPlain), - .img_path_count = 1, .img_column_count = 8, .btn_tile_sheet_column = 8, .btn_tile_sheet_row = 0, + .img_path_count = 1, .img_column_count = 10, .btn_tile_sheet_column = 8, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, @@ -1618,6 +1618,7 @@ struct injected_state { int handling_ai_district_fallback; // Used in the code that adds additional info to the tile info box + bool tile_info_open; int viewing_tile_info_x, viewing_tile_info_y; // Used in patch_Tile_m43_Get_field_30_for_city_loc_eval to change how the AI evaluates overlap between cities @@ -1950,7 +1951,7 @@ struct injected_state { Sprite LM_Forests_Small_Images[10]; Sprite LM_Forests_Pines_Images[12]; Sprite LM_Hills_Images[16]; - Sprite District_Images[COUNT_DISTRICT_TYPES][10][4][6]; // [district][variant][era][building_stage] + Sprite District_Images[COUNT_DISTRICT_TYPES][10][4][10]; // [district][variant][era][building_stage] Sprite Abandoned_District_Image; Sprite Abandoned_Maritime_District_Image; struct wonder_district_image_set Wonder_District_Images[MAX_WONDER_DISTRICT_TYPES]; diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index 72251797..5e7339f1 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -495,9 +495,9 @@ inlead, 0x55D310, 0x5693A0, 0x55D2C0, "Leader_sum_unit_maintenance", "int ( define, 0xA52684, 0xA74E7C, 0xA52644, "p_game_difficulty", "int *" define, 0x5CD960, 0x5DC940, 0x5CD880, "Unit_is_terrain_impassable", "bool (__fastcall *) (Unit * this, int edx, int terrain_type_index)" define, 0x41CD22, 0x41E04C, 0x41CDA2, "ADDR_LUXURY_BOX_ROW_HEIGHT", "byte *" -define, 0x30, 0x38, 0x30, "LUXURY_BOX_ROW_HEIGHT_STACK_OFFSET", "int" +define, 0x30, 0x38, 0x30, "LUXURY_BOX_ROW_HEIGHT_STACK_OFFSET", "int" define, 0x740A00, 0x75ADA0, 0x7409C0, "p_city_form", "City_Form *" -define, 0x5F82E0, 0x6087E0, 0x5F8210, "Sprite_draw", "int (__fastcall *) (Sprite * this, int edx, PCX_Image * canvas, int pixel_x, int pixel_y, PCX_Color_Table * color_table)" +inlead, 0x5F82E0, 0x6087E0, 0x5F8210, "Sprite_draw", "int (__fastcall *) (Sprite * this, int edx, PCX_Image * canvas, int pixel_x, int pixel_y, PCX_Color_Table * color_table)" repl call, 0x421295, 0x4227F5, 0x421315, "Sprite_draw_improv_img_on_city_form", "" inlead, 0x421240, 0x4227A0, 0x4212C0, "draw_improv_icons_on_city_screen", "void (__cdecl *) (Base_List_Control * control, int improv_id, int item_index, int offset_x, int offset_y)" define, 0x4B0710, 0x4B76C0, 0x4B07A0, "City_get_tourism_amount", "int (__fastcall *) (City * this, int edx, int improv_id)" diff --git a/injected_code.c b/injected_code.c index 76653589..bebe4ca0 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16197,6 +16197,8 @@ bool load_day_night_hour_images(struct day_night_cycle_img_set *this, const char int era_count = cfg->vary_img_by_era ? 4 : 1; int column_count = cfg->img_column_count + 1; + int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; + int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; for (int variant_i = 0; variant_i < variant_count; variant_i++) { const char * img_path = cfg->img_paths[variant_i]; @@ -16209,9 +16211,9 @@ bool load_day_night_hour_images(struct day_night_cycle_img_set *this, const char for (int era = 0; era < era_count; era++) { for (int col = 0; col < column_count; col++) { - int tile_x = 128 * col; - int tile_y = 64 * era; - Sprite_slice_pcx (&this->District_Images[dc][variant_i][era][col], __, &img, tile_x, tile_y, 128, 64, 1, 1); + int tile_x = sprite_width * col; + int tile_y = sprite_height * era; + Sprite_slice_pcx (&this->District_Images[dc][variant_i][era][col], __, &img, tile_x, tile_y, sprite_width, sprite_height, 1, 1); } } } @@ -22403,10 +22405,13 @@ patch_open_tile_info (void * this, int edx, int mouse_x, int mouse_y, int civ_id (! Main_Screen_Form_get_tile_coords_under_mouse (p_main_screen_form, __, mouse_x, mouse_y, &tx, &ty))) { is->viewing_tile_info_x = tx; is->viewing_tile_info_y = ty; + is->tile_info_open = true; } else is->viewing_tile_info_x = is->viewing_tile_info_y = -1; - return open_tile_info (this, __, mouse_x, mouse_y, civ_id); + open_tile_info (this, __, mouse_x, mouse_y, civ_id); + + is->tile_info_open = false; } int __fastcall @@ -22560,10 +22565,17 @@ patch_City_compute_corrupted_yield (City * this, int edx, int gross_yield, bool return tr; } +int __fastcall +patch_Sprite_draw (Sprite * this, int edx, PCX_Image * canvas, int pixel_x, int pixel_y, PCX_Color_Table * color_table) +{ + Sprite * to_draw = get_sprite_proxy_for_current_hour(this); + return Sprite_draw(to_draw ? to_draw : this, __, canvas, pixel_x, pixel_y, color_table); +} + int __fastcall patch_Sprite_draw_on_map (Sprite * this, int edx, Map_Renderer * map_renderer, int pixel_x, int pixel_y, int param_4, int param_5, int param_6, int param_7) { - Sprite *to_draw = get_sprite_proxy_for_current_hour(this); + Sprite * to_draw = get_sprite_proxy_for_current_hour(this); return Sprite_draw_on_map(to_draw ? to_draw : this, __, map_renderer, pixel_x, pixel_y, param_4, param_5, param_6, param_7); } @@ -31993,12 +32005,13 @@ wonder_should_use_alternative_direction_image (int tile_x, int tile_y, int owner void draw_district_on_map_or_canvas(Sprite * sprite, Map_Renderer * map_renderer, int pixel_x, int pixel_y) { - if (is->current_config.show_detailed_tile_info) { + if (is->tile_info_open) { PCX_Image * canvas = (PCX_Image *)map_renderer; - Sprite_draw (sprite, __, canvas, pixel_x, pixel_y, NULL); - } else { - patch_Sprite_draw_on_map (sprite, __, map_renderer, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); + patch_Sprite_draw (sprite, __, canvas, pixel_x, pixel_y, NULL); + return; } + + patch_Sprite_draw_on_map (sprite, __, map_renderer, pixel_x, pixel_y, 1, 1, (p_bic_data->is_zoomed_out != false) + 1, 0); } bool @@ -32278,10 +32291,6 @@ draw_canal_on_map_or_canvas(Sprite * sprite, int tile_x, int tile_y, int dir, bo draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y); - char ss[200]; - snprintf (ss, sizeof(ss), "Drawing canal dir = %d, DIR_SE = %d, water dirs[SE] = %d\n", dir, DIR_SE, water_dirs[DIR_SE]); - (*p_OutputDebugStringA)(ss); - // In certain cases, add an additional draw if adjacent to water so that the canal appears to extend far enough if (dir == DIR_N && water_dirs[DIR_N]) draw_district_on_map_or_canvas(sprite, map_renderer, draw_x, draw_y - y_offset); @@ -32756,8 +32765,7 @@ patch_Map_Renderer_m11_Draw_Tile_Irrigation (Map_Renderer *this, int edx, int vi } // If it has a completed district that serves as pseudo-irrigation source, suppress drawing irrigation - if (is->district_configs[inst->district_id].allow_irrigation_from - && district_is_complete (is->current_render_tile, inst->district_id)) + if (is->district_configs[inst->district_id].allow_irrigation_from && district_is_complete (is->current_render_tile, inst->district_id)) return; Map_Renderer_m11_Draw_Tile_Irrigation (this, __, visible_to_civ, tile_x, tile_y, param_4, param_5, param_6); From 5d212def01161542a461e4b1cd8f0edd104827f9 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 2 Feb 2026 08:28:07 -0800 Subject: [PATCH 333/356] Fix changelog entry description for empty army divisor crash issue --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 90d1a99e..31d91832 100644 --- a/changelog.txt +++ b/changelog.txt @@ -12,7 +12,7 @@ - aggressively_penalize_bankruptcy now alternates between selling buildings or disbanding units first for bankrupt AI players and every third turn cuts their research spending - Fix crash caused by failure to find default option for popup asking player to select new build in city that completed its previous one - Except nuclear weapons from disallow_useless_bombard_vs_airfields -- Fix crash caused by empty armies entering combat despite having zero attack and defense strength +- Fix crash caused by modded empty armies with non-zero attack/defense being allowed to enter combat and dividing by their unit count * RELEASE 26 - Add some options to make land transports viable, controlled by land_transport_rules setting From bbfaa931f654cea399b7d878775ab6e4e7a574c7 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 2 Feb 2026 09:45:55 -0800 Subject: [PATCH 334/356] Refactor and systematize img column count, sync w/ day-night cycle images --- C3X.h | 9 ++++++--- injected_code.c | 19 ++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/C3X.h b/C3X.h index fe8cd227..33f350c6 100644 --- a/C3X.h +++ b/C3X.h @@ -22,6 +22,9 @@ typedef unsigned char byte; #define COUNT_DISTRICT_TYPES (COUNT_SPECIAL_DISTRICT_TYPES + MAX_DYNAMIC_DISTRICT_TYPES) #define MAX_WONDER_DISTRICT_TYPES 32 #define MAX_NATURAL_WONDER_DISTRICT_TYPES 32 +#define MAX_DISTRICT_VARIANT_COUNT 5 +#define MAX_DISTRICT_ERA_COUNT 4 +#define MAX_DISTRICT_COLUMN_COUNT 10 #define C3X_DISTRICT_COMMAND_BASE (-11000000) // Initialize to zero. Implementation is in common.c @@ -931,7 +934,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {"Industrialization"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_count = 4, .img_paths = {"EnergyGrid.pcx"}, .dependent_improvements = {"Coal Plant", "Hydro Plant", "Solar Plant", "Nuclear Plant"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .custom_height = 84, - .img_path_count = 1, .img_column_count = 14, .btn_tile_sheet_column = 6, .btn_tile_sheet_row = 0, + .img_path_count = 1, .img_column_count = 4, .btn_tile_sheet_column = 6, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 2, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, @@ -1951,7 +1954,7 @@ struct injected_state { Sprite LM_Forests_Small_Images[10]; Sprite LM_Forests_Pines_Images[12]; Sprite LM_Hills_Images[16]; - Sprite District_Images[COUNT_DISTRICT_TYPES][10][4][10]; // [district][variant][era][building_stage] + Sprite District_Images[COUNT_DISTRICT_TYPES][MAX_DISTRICT_VARIANT_COUNT][4][MAX_DISTRICT_COLUMN_COUNT]; // [district][variant][era][column] Sprite Abandoned_District_Image; Sprite Abandoned_Maritime_District_Image; struct wonder_district_image_set Wonder_District_Images[MAX_WONDER_DISTRICT_TYPES]; @@ -1968,7 +1971,7 @@ struct injected_state { struct natural_wonder_district_config natural_wonder_configs[MAX_NATURAL_WONDER_DISTRICT_TYPES]; struct district_image_set { - Sprite imgs[10][4][20]; // [variant][era][building_stage] + Sprite imgs[MAX_DISTRICT_VARIANT_COUNT][4][MAX_DISTRICT_COLUMN_COUNT]; // [variant][era][column] } district_img_sets[COUNT_DISTRICT_TYPES]; Sprite abandoned_district_img; Sprite abandoned_maritime_district_img; diff --git a/injected_code.c b/injected_code.c index bebe4ca0..7f2e05cb 100644 --- a/injected_code.c +++ b/injected_code.c @@ -81,6 +81,10 @@ struct injected_state * is = ADDR_INJECTED_STATE; #define CANAL_DISTRICT_ID 9 #define GREAT_WALL_DISTRICT_ID 10 +#define MAX_DISTRICT_VARIANT_COUNT 5 +#define MAX_DISTRICT_ERA_COUNT 4 +#define MAX_DISTRICT_COLUMN_COUNT 10 + // Max grid of tiles that an AI will evaluate a candidate bridge or canal for, // used to limit computational complexity #define AI_BRIDGE_CANAL_CANDIDATE_MAX_EVAL_TILES 10 @@ -8286,11 +8290,7 @@ override_special_district_from_definition (struct parsed_district_definition * d def->dependent_improvements[i] = NULL; } if (! def->has_img_column_count) { - cfg->img_column_count = cfg->dependent_improvement_count; - if (cfg->img_column_count > 20) - cfg->img_column_count = 20; - if (cfg->img_column_count < 0) - cfg->img_column_count = 0; + cfg->img_column_count = clamp (0, MAX_DISTRICT_COLUMN_COUNT, cfg->dependent_improvement_count); cfg->has_img_column_count_override = false; } } @@ -8315,11 +8315,7 @@ override_special_district_from_definition (struct parsed_district_definition * d } if (def->has_img_column_count) { - cfg->img_column_count = def->img_column_count; - if (cfg->img_column_count > 20) - cfg->img_column_count = 20; - if (cfg->img_column_count < 0) - cfg->img_column_count = 0; + cfg->img_column_count = clamp (0, MAX_DISTRICT_COLUMN_COUNT, cfg->img_column_count); cfg->has_img_column_count_override = true; } @@ -32030,6 +32026,7 @@ district_allows_river (struct district_config const * cfg) int get_energy_grid_image_index (int tile_x, int tile_y) { + return 0; struct district_infos * info = &is->district_infos[ENERGY_GRID_DISTRICT_ID]; int coal_plant_id = info->dependent_building_ids[0]; int hydro_plant_id = info->dependent_building_ids[1]; @@ -32565,7 +32562,7 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc int offset_y = draw_pixel_y + cfg->y_offset; int draw_x = offset_x - ((sprite_width - 128) / 2); int draw_y = offset_y - (sprite_height - 64); - Sprite (*sprites)[4][20] = is->district_img_sets[district_id].imgs; + Sprite (*sprites)[MAX_DISTRICT_ERA_COUNT][MAX_DISTRICT_COLUMN_COUNT] = is->district_img_sets[district_id].imgs; // Render switch (district_id) { From 9717545f517f1449166e9bb707c41caec18b8d82 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 2 Feb 2026 10:25:56 -0800 Subject: [PATCH 335/356] Fix off-by-one img_column_count / dependent_improvements_max_index bug; Simplify Energy Grid to only show first energy plant found --- Art/Districts/1200/EnergyGrid.PCX | Bin 104481 -> 39578 bytes Art/Districts/Annotations/Port_NE_lights.PCX | Bin 20827 -> 20826 bytes Art/Districts/Annotations/Wonders_lights.PCX | Bin 37263 -> 37263 bytes C3X.h | 46 +++---- default.districts_config.txt | 2 +- injected_code.c | 119 +++++++------------ 6 files changed, 67 insertions(+), 100 deletions(-) diff --git a/Art/Districts/1200/EnergyGrid.PCX b/Art/Districts/1200/EnergyGrid.PCX index 363e44d19c09c9f1e710025621e7d8066be4138c..d557c6e985747772a0544214dbac6d24c8eb9709 100644 GIT binary patch delta 6359 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 literal 104481 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 rNA?^$az!H16fCJ diff --git a/Art/Districts/Annotations/Port_NE_lights.PCX b/Art/Districts/Annotations/Port_NE_lights.PCX index f948a73b89272ca90c2e834a398c39c36093fe99..ecdfbdc223a1d5da964541bfecf6cc445c216c51 100644 GIT binary patch delta 47 wcmcb;i1F4U#tjb@m=1|-eymW#2%-~}ri1BU%Ig>zMJBIOJp&T0PQwOsU^$;6oIpM GCgUm?Zxs{( diff --git a/Art/Districts/Annotations/Wonders_lights.PCX b/Art/Districts/Annotations/Wonders_lights.PCX index 1ae656b6433298872d67df302b4eafcb4b24db25..e5fc1ecb91a77f78a135b87f02490955564a1a01 100644 GIT binary patch delta 39 tcmeBQ%+$Y_X@h1No!+bL^3n2)7pHcT8$Az|EZZQ0st-d4_E*I delta 39 tcmeBQ%+$Y_X@h1Nbuildable_by_pact_allies = defaults->buildable_by_pact_allies; for (int i = 0; i < ARRAY_LEN (cfg->dependent_improvements); i++) { - char const * default_value = (i < defaults->dependent_improvement_count) ? defaults->dependent_improvements[i] : NULL; + char const * default_value = (i < defaults->dependent_improvement_max_index) ? defaults->dependent_improvements[i] : NULL; if ((cfg->dependent_improvements[i] != NULL) && (cfg->dependent_improvements[i] != default_value)) { free ((void *)cfg->dependent_improvements[i]); } cfg->dependent_improvements[i] = NULL; } - cfg->dependent_improvement_count = defaults->dependent_improvement_count; + cfg->dependent_improvement_max_index = defaults->dependent_improvement_max_index; for (int i = 0; i < ARRAY_LEN (cfg->img_paths); i++) { char const * default_value = (i < defaults->img_path_count) ? defaults->img_paths[i] : NULL; @@ -7452,13 +7452,13 @@ free_parsed_district_definition (struct parsed_district_definition * def) def->buildable_by_civ_cultures_count = 0; def->buildable_by_civ_cultures_id_count = 0; - for (int i = 0; i < def->dependent_improvement_count; i++) { + for (int i = 0; i < def->dependent_improvement_max_index; i++) { if (def->dependent_improvements[i] != NULL) { free (def->dependent_improvements[i]); def->dependent_improvements[i] = NULL; } } - def->dependent_improvement_count = 0; + def->dependent_improvement_max_index = 0; for (int i = 0; i < def->img_path_count; i++) { if (def->img_paths[i] != NULL) { @@ -8274,23 +8274,23 @@ override_special_district_from_definition (struct parsed_district_definition * d if (def->has_dependent_improvements) { for (int i = 0; i < ARRAY_LEN (cfg->dependent_improvements); i++) { - char const * default_value = (i < defaults->dependent_improvement_count) ? defaults->dependent_improvements[i] : NULL; + char const * default_value = (i < defaults->dependent_improvement_max_index) ? defaults->dependent_improvements[i] : NULL; if ((cfg->dependent_improvements[i] != NULL) && (cfg->dependent_improvements[i] != default_value)) free ((void *)cfg->dependent_improvements[i]); cfg->dependent_improvements[i] = NULL; } - cfg->dependent_improvement_count = def->dependent_improvement_count; + cfg->dependent_improvement_max_index = def->dependent_improvement_max_index; const int max_entries = ARRAY_LEN (cfg->dependent_improvements); - if (cfg->dependent_improvement_count > max_entries) - cfg->dependent_improvement_count = max_entries; - for (int i = 0; i < cfg->dependent_improvement_count; i++) { + if (cfg->dependent_improvement_max_index > max_entries) + cfg->dependent_improvement_max_index = max_entries; + for (int i = 0; i < cfg->dependent_improvement_max_index; i++) { cfg->dependent_improvements[i] = def->dependent_improvements[i]; def->dependent_improvements[i] = NULL; } if (! def->has_img_column_count) { - cfg->img_column_count = clamp (0, MAX_DISTRICT_COLUMN_COUNT, cfg->dependent_improvement_count); + cfg->img_column_count = clamp (1, MAX_DISTRICT_COLUMN_COUNT, cfg->dependent_improvement_max_index + 1); cfg->has_img_column_count_override = false; } } @@ -8315,7 +8315,7 @@ override_special_district_from_definition (struct parsed_district_definition * d } if (def->has_img_column_count) { - cfg->img_column_count = clamp (0, MAX_DISTRICT_COLUMN_COUNT, cfg->img_column_count); + cfg->img_column_count = clamp (1, MAX_DISTRICT_COLUMN_COUNT, cfg->img_column_count); cfg->has_img_column_count_override = true; } @@ -8548,11 +8548,11 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.generated_resource_flags = 0; } - new_cfg.dependent_improvement_count = def->has_dependent_improvements ? def->dependent_improvement_count : 0; + new_cfg.dependent_improvement_max_index = def->has_dependent_improvements ? def->dependent_improvement_max_index : 0; const int max_dependent_entries = ARRAY_LEN (is->district_configs[0].dependent_improvements); - if (new_cfg.dependent_improvement_count > max_dependent_entries) - new_cfg.dependent_improvement_count = max_dependent_entries; - for (int i = 0; i < new_cfg.dependent_improvement_count; i++) { + if (new_cfg.dependent_improvement_max_index > max_dependent_entries) + new_cfg.dependent_improvement_max_index = max_dependent_entries; + for (int i = 0; i < new_cfg.dependent_improvement_max_index; i++) { new_cfg.dependent_improvements[i] = def->dependent_improvements[i]; def->dependent_improvements[i] = NULL; } @@ -8571,11 +8571,7 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i return false; } - new_cfg.img_column_count = def->has_img_column_count ? def->img_column_count : new_cfg.dependent_improvement_count; - if (new_cfg.img_column_count > 20) - new_cfg.img_column_count = 20; - if (new_cfg.img_column_count < 0) - new_cfg.img_column_count = 0; + new_cfg.img_column_count = clamp (1, MAX_DISTRICT_COLUMN_COUNT, def->has_img_column_count ? def->img_column_count : new_cfg.dependent_improvement_max_index + 1); new_cfg.has_img_column_count_override = def->has_img_column_count; if (reusing_existing) @@ -9004,10 +9000,10 @@ handle_district_definition_key (struct parsed_district_definition * def, parse_errors, line_number, "dependent_improvs")) { - def->dependent_improvement_count = list_count; + def->dependent_improvement_max_index = list_count; def->has_dependent_improvements = true; } else { - def->dependent_improvement_count = 0; + def->dependent_improvement_max_index = 0; def->has_dependent_improvements = false; } free (value_text); @@ -10429,7 +10425,7 @@ district_config_has_dependent_improvement (struct district_config * cfg, char co if ((cfg == NULL) || (name == NULL) || (name[0] == '\0')) return false; - for (int i = 0; i < cfg->dependent_improvement_count; i++) { + for (int i = 0; i < cfg->dependent_improvement_max_index; i++) { char const * existing = cfg->dependent_improvements[i]; if ((existing != NULL) && (strcmp (existing, name) == 0)) return true; @@ -10571,7 +10567,7 @@ set_wonders_dependent_on_wonder_district (void) if (district_config_has_dependent_improvement (cfg, wonder_name)) continue; - int dest = cfg->dependent_improvement_count; + int dest = cfg->dependent_improvement_max_index; if (dest >= ARRAY_LEN (cfg->dependent_improvements)) { continue; } @@ -10582,12 +10578,12 @@ set_wonders_dependent_on_wonder_district (void) } cfg->dependent_improvements[dest] = copy; - cfg->dependent_improvement_count = dest + 1; + cfg->dependent_improvement_max_index = dest + 1; } if (! cfg->has_img_column_count_override && - (cfg->img_column_count < cfg->dependent_improvement_count)) - cfg->img_column_count = cfg->dependent_improvement_count; + (cfg->img_column_count < cfg->dependent_improvement_max_index + 1)) + cfg->img_column_count = cfg->dependent_improvement_max_index + 1; } void @@ -10831,7 +10827,7 @@ void parse_building_and_tech_ids () // Map improvement prereqs to districts int stored_count = 0; - for (int j = 0; j < is->district_configs[i].dependent_improvement_count; j++) { + for (int j = 0; j < is->district_configs[i].dependent_improvement_max_index; j++) { int improv_id; if (is->district_configs[i].dependent_improvements[j] == "" || is->district_configs[i].dependent_improvements[j] == NULL) continue; @@ -16191,8 +16187,8 @@ bool load_day_night_hour_images(struct day_night_cycle_img_set *this, const char if (variant_count > variant_capacity) variant_count = variant_capacity; - int era_count = cfg->vary_img_by_era ? 4 : 1; - int column_count = cfg->img_column_count + 1; + int era_count = cfg->vary_img_by_era ? 4 : 1; + int column_count = cfg->img_column_count; int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; @@ -16448,8 +16444,8 @@ build_sprite_proxies_24(Map_Renderer *mr) { if (variant_count > variant_capacity) variant_count = variant_capacity; - int era_count = cfg->vary_img_by_era ? 4 : 1; - int column_count = cfg->img_column_count + 1; + int era_count = cfg->vary_img_by_era ? 4 : 1; + int column_count = cfg->img_column_count; for (int variant_i = 0; variant_i < variant_count; variant_i++) { if ((cfg->img_paths[variant_i] == NULL) || (cfg->img_paths[variant_i][0] == '\0')) @@ -31374,8 +31370,8 @@ init_district_images () if (variant_count <= 0) continue; - int era_count = cfg->vary_img_by_era ? 4 : 1; - int column_count = cfg->img_column_count + 1; + int era_count = cfg->vary_img_by_era ? 4 : 1; + int column_count = cfg->img_column_count; int sprite_width = (cfg->custom_width > 0) ? cfg->custom_width : 128; int sprite_height = (cfg->custom_height > 0) ? cfg->custom_height : 64; @@ -32026,36 +32022,16 @@ district_allows_river (struct district_config const * cfg) int get_energy_grid_image_index (int tile_x, int tile_y) { - return 0; struct district_infos * info = &is->district_infos[ENERGY_GRID_DISTRICT_ID]; - int coal_plant_id = info->dependent_building_ids[0]; - int hydro_plant_id = info->dependent_building_ids[1]; - int solar_plant_id = info->dependent_building_ids[2]; - int nuclear_plant_id = info->dependent_building_ids[3]; - bool coal = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, ENERGY_GRID_DISTRICT_ID, coal_plant_id); - bool hydro = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, ENERGY_GRID_DISTRICT_ID, hydro_plant_id); - bool solar = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, ENERGY_GRID_DISTRICT_ID, solar_plant_id); - bool nuclear = tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, ENERGY_GRID_DISTRICT_ID, nuclear_plant_id); - - int index = 0; - - if (! coal && ! hydro && ! solar && ! nuclear) index = 0; // None - else if (coal && ! hydro && ! solar && ! nuclear) index = 1; // Coal - else if (! coal && hydro && ! solar && ! nuclear) index = 2; // Hydro - else if (coal && hydro && ! solar && ! nuclear) index = 3; // Coal, Hydro - else if (! coal && ! hydro && ! solar && nuclear) index = 4; // Nuclear - else if (! coal && ! hydro && solar && ! nuclear) index = 5; // Solar - else if (coal && hydro && solar && nuclear) index = 6; // All - else if (coal && hydro && ! solar && nuclear) index = 7; // Coal, Hydro, Nuclear - else if (! coal && hydro && solar && nuclear) index = 8; // Hydro, Solar, Nuclear - else if (! coal && hydro && ! solar && nuclear) index = 9; // Hydro, Nuclear - else if (! coal && hydro && solar && ! nuclear) index = 10; // Hydro, Solar - else if (! coal && ! hydro && solar && nuclear) index = 11; // Solar, Nuclear - else if (coal && ! hydro && ! solar && nuclear) index = 12; // Coal, Nuclear - else if (coal && ! hydro && solar && nuclear) index = 13; // Coal, Solar, Nuclear - else if (coal && ! hydro && solar && ! nuclear) index = 14; // Coal, Solar - - return index; + for (int i = 0; i < info->dependent_building_count; i++) { + // Zero is "no building"; Buildings start from index one + int column_index = i + 1; + int building_id = info->dependent_building_ids[i]; + if (tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, ENERGY_GRID_DISTRICT_ID, building_id)) + return column_index; + } + + return 0; } int @@ -32443,7 +32419,7 @@ draw_district_generated_resource_on_tile (Map_Renderer * this, Tile * tile, stru int count_completed_buildings_in_district_radius (int tile_x, int tile_y, int district_id) { - struct district_config const * cfg = &is->district_configs[district_id]; + struct district_config const * cfg = &is->district_configs[district_id]; struct district_infos * district_info = &is->district_infos[district_id]; int completed_count = 0; for (int i = 0; i < district_info->dependent_building_count; i++) { @@ -32457,23 +32433,16 @@ count_completed_buildings_in_district_radius (int tile_x, int tile_y, int distri void draw_district_on_map_or_canvas_by_buildings (Sprite * base_sprite, Map_Renderer * map_renderer, int district_id, int variant, int era, int tile_x, int tile_y, int draw_x, int draw_y) { - struct district_config const * cfg = &is->district_configs[district_id]; + struct district_config const * cfg = &is->district_configs[district_id]; struct district_infos * district_info = &is->district_infos[district_id]; - int max_stage = cfg->img_column_count; - int stage_limit = ARRAY_LEN (is->district_img_sets[0].imgs[0][0]) - 1; - - if (max_stage > stage_limit) - max_stage = stage_limit; + int max_column_index = cfg->img_column_count - 1; draw_district_on_map_or_canvas(base_sprite, map_renderer, draw_x, draw_y); for (int i = 0; i < district_info->dependent_building_count; i++) { int building_id = district_info->dependent_building_ids[i]; - int stage = i + 1; - if (stage > max_stage) - break; if ((building_id >= 0) && tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, district_id, building_id)) { - Sprite * district_sprite = &is->district_img_sets[district_id].imgs[variant][era][stage]; + Sprite * district_sprite = &is->district_img_sets[district_id].imgs[variant][era][i]; draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); } } @@ -32643,8 +32612,6 @@ draw_district_on_tile (Map_Renderer * this, Tile * tile, struct district_instanc if (! is->current_config.enable_energy_grid_districts) return; - // Energy grids can have multiple buildings that - unlike most district buildings - don't have each other as prereqs (e.g., Coal Plant & Nuclear Plant), - // and thus have a combinatorial number of images based on which power plants are built in their radius. buildings = get_energy_grid_image_index (tile_x, tile_y); draw_district_on_map_or_canvas(&sprites[variant][era][buildings], map_renderer, draw_x, draw_y); return; From 28317f400c40189134c6f2fca8eb2b53099564f8 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 2 Feb 2026 10:31:12 -0800 Subject: [PATCH 336/356] Ensure day-night cycle checks mod/scenario folders as well --- injected_code.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/injected_code.c b/injected_code.c index eedd79aa..431ca23d 100644 --- a/injected_code.c +++ b/injected_code.c @@ -16176,8 +16176,10 @@ bool load_day_night_hour_images(struct day_night_cycle_img_set *this, const char // Districts (if enabled) if (is->current_config.enable_districts) { - char districts_hour_dir[200]; - snprintf (districts_hour_dir, sizeof districts_hour_dir, "%s\\Art\\Districts\\%s", is->mod_rel_dir, hour); + char art_dir[200]; + char temp_path[2*MAX_PATH]; + snprintf (art_dir, sizeof art_dir, "Districts/%s", hour); + get_mod_art_path (art_dir, temp_path, sizeof temp_path); for (int dc = 0; dc < is->district_count; dc++) { struct district_config const * cfg = &is->district_configs[dc]; int variant_capacity = ARRAY_LEN (this->District_Images[dc]); @@ -16197,7 +16199,7 @@ bool load_day_night_hour_images(struct day_night_cycle_img_set *this, const char if ((img_path == NULL) || (img_path[0] == '\0')) continue; - read_in_dir (&img, districts_hour_dir, img_path, NULL); + read_in_dir (&img, temp_path, img_path, NULL); if (img.JGL.Image == NULL) return false; @@ -16212,7 +16214,7 @@ bool load_day_night_hour_images(struct day_night_cycle_img_set *this, const char } // Abandoned district art (land + maritime) for this hour - read_in_dir (&img, districts_hour_dir, "Abandoned.pcx", NULL); + read_in_dir (&img, temp_path, "Abandoned.pcx", NULL); if (img.JGL.Image == NULL) return false; Sprite_slice_pcx (&this->Abandoned_District_Image, __, &img, 0, 0, 128, 64, 1, 1); @@ -16236,7 +16238,7 @@ bool load_day_night_hour_images(struct day_night_cycle_img_set *this, const char if (pcx_loaded) wpcx.vtable->clear_JGL (&wpcx); - read_in_dir (&wpcx, districts_hour_dir, img_path, NULL); + read_in_dir (&wpcx, temp_path, img_path, NULL); if (wpcx.JGL.Image == NULL) { pcx_loaded = false; @@ -16283,8 +16285,10 @@ bool load_day_night_hour_images(struct day_night_cycle_img_set *this, const char } if (is->current_config.enable_natural_wonders && (is->natural_wonder_count > 0)) { - char natural_dir[200]; - snprintf (natural_dir, sizeof natural_dir, "%s\\Art\\Districts\\%s", is->mod_rel_dir, hour); + char art_dir[200]; + char temp_path[2*MAX_PATH]; + snprintf (art_dir, sizeof art_dir, "Districts/%s", hour); + get_mod_art_path (art_dir, temp_path, sizeof temp_path); char const * last_img_path = NULL; PCX_Image nwpcx; @@ -16301,7 +16305,7 @@ bool load_day_night_hour_images(struct day_night_cycle_img_set *this, const char if (pcx_loaded) nwpcx.vtable->clear_JGL (&nwpcx); - read_in_dir (&nwpcx, natural_dir, img_path, NULL); + read_in_dir (&nwpcx, temp_path, img_path, NULL); if (nwpcx.JGL.Image == NULL) { pcx_loaded = false; From 7b466ec4a9565156cf09d574fd4750b0c6861b1b Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 2 Feb 2026 11:13:22 -0800 Subject: [PATCH 337/356] Update Port NE, Ski Resort, Hoover Dam light annotations --- Art/Districts/1200/Port_NE.PCX | Bin 20837 -> 20843 bytes Art/Districts/Annotations/Port_NE_lights.PCX | Bin 20826 -> 20841 bytes .../Annotations/SkiResort_lights.PCX | Bin 5303 -> 5288 bytes .../Annotations/Wonders_3_lights.PCX | Bin 21460 -> 21429 bytes default.districts_config.txt | 8 ++++++++ 5 files changed, 8 insertions(+) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index e5c40db90f67665f94bcfffc1c1ef32283d9b893..e130ce4f4f346c20e408d515db6a2bf5659e1359 100644 GIT binary patch delta 37 qcmaF5i1GCz#tll!EGPaSd@xx>`6SCR5aYe_6_&$5#^$3c3v>W|3lz-& delta 31 pcmV+)0O0@Yq5^pe*=)5Ay&3 diff --git a/Art/Districts/Annotations/Port_NE_lights.PCX b/Art/Districts/Annotations/Port_NE_lights.PCX index ecdfbdc223a1d5da964541bfecf6cc445c216c51..fb04838be4c9dc0a6baffdc1edb662bd11a10dcb 100644 GIT binary patch delta 2252 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 Q2REl6X3IFa<;SSM09Tq-<^TWy diff --git a/Art/Districts/Annotations/SkiResort_lights.PCX b/Art/Districts/Annotations/SkiResort_lights.PCX index 3ebb4a0c3efb41cd00317f920c3e38214dac48d3..0f6e3781aec71f01e94e22713f082c293a0a4bd0 100644 GIT binary patch delta 458 zcmYk0&1(};6vfY-$;5P$yh$i+@Pl9(vY3=8jS!HM!GS@=aZ?JTP;_Hcp^}9}aHZ{H z+_(_rVd% zyT05(j#GW|&M*~JuxTy*Zw}KR>HUQj1%FP37Zl_fqgKJZn)0V`!@f9##XCG|(J|g9 zdE0nRBxg;ZXmHDXPLo4u-+dk_``q*lA5fz}vTm(p`4ib!NItQyYW};`(tIuZkc=VZ ziz8bZN=}fxnY)vrrL!bI=3Gr>CIU^JpP2tgJ+LI?7jE{`m~UD@owN48#r zJnpzv4F}yZwZxx!oA;V=lmwW;rDlu>@|xpS&_lmiMjy#~5=OWTKMspJ^Qq%af)5b% zA|!oQZ6ChU*&m$K4(?%dwLLo*_PS*V$fKoGdEGPUch%TQE|!`(ti>Jv*2LgZ$)oJA O$JhV-#NbW&(D)1XWtsp0 delta 497 zcmYk0&ui0Q7=ZZ_Q`+c+>kc0X zf*`&SJbCFwMP!hJ?)ZMa7iIPjP+M!~gmk^g7}OqpPS5G#d1HJuHulLS9NIGemUH(j zG>W6KV!Js~*A!W(kN#tCv^!e6xx!5=&+U1d22;qMu!}rpr3&}t1)iIeu#LR}wohWn z;E}LNIGhtL0mI;d_>QD-fM1K_dWbzPUp<=P!wbqRoRL-}{!L!0!3*gQS%AJ&AVzRH z@{EYIb{XCu+>mg}K6ok<@ISczyN-}{JE0P1N^Vo(b@+&e8 zN8+D^8#1)ww}mMUbY)A#@KEqqG01poTGfdMbE+djITdKBdb&x~GIbm4*ySDUV;6gpRXWzFP|J(tvL(- z`pe_S7WPf2%xFF{bKP{zvIn;k#&H~Y%%9L4pR;vlm)%9(a_kAt;AO%%tm3diy|T}I zL3w&;ng7Oo$xvkwLm14 z@%!X&B2g>{KLEKQqG>?xZqYEHIfp(ReB3d4pQy@YJFzyVL-v!8i)AproNOQ-#d660 s;D^bE;_92XiKnvyohB#i!33nz<+Y5MzfFwWJS9+-iRs_h$-9Fk0Ym_C*#H0l delta 273 zcmdnGobk$X#tlqtjE5()vQ1(;bn4)_!@njkWb5IbcktW6Uoo=|{sPkXCR?+&G2YvJ zh&_mj>2S>CL@wFQrd+cb8NY45$Snt=KJeUQVmtWc;H`s?Ctu+2V>|d5$b2$6LEwNO z|K@}5Hg5w8yek1Q51pT^F4!w@=sZxAzvSS%gO9g?6inVNxR({E=h^1TRYIwZ-zGm0 za%17!eDK+14dK-+Mh734Ouj0-j-{srXhEw;DC4imw?(2@4t=oPI@wY*4ai*}8pd+) z>Q>9iKSb9vANJfl1>)+Ht;CI(4_<29oF_hkmGR}~|1us-KsS5HYZ);! TFfeQ`4OC@fVqlm&Cs+~y3~htV diff --git a/default.districts_config.txt b/default.districts_config.txt index 7796783e..54d8b754 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -432,6 +432,10 @@ name = Bridge tooltip = Build Bridge btn_tile_sheet_row = 0 btn_tile_sheet_column = 7 +custom_height = 112 +custom_width = 176 +y_offset = 24 +x_offset = 0 advance_prereqs = Industrialization buildable_on = coast auto_add_road = 1 @@ -449,6 +453,10 @@ name = Canal tooltip = Build Canal btn_tile_sheet_row = 0 btn_tile_sheet_column = 8 +custom_height = 112 +custom_width = 176 +y_offset = 24 +x_offset = 0 advance_prereqs = Industrialization buildable_on = desert,plains,grassland,tundra,floodplain defense_bonus_percent = 0 From f393762ed98163ed4a824624a958a3f6ca56e590 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 2 Feb 2026 11:40:00 -0800 Subject: [PATCH 338/356] Add Bridge industrial light annotations --- Art/Districts/Annotations/Bridge_lights.PCX | Bin 88323 -> 88348 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/Annotations/Bridge_lights.PCX b/Art/Districts/Annotations/Bridge_lights.PCX index a61bd5c0e11e0cd7fb414a7ce0ed403a34a5f1b4..e24d0669b6aad615ac296d44f720dc12e9a4193f 100644 GIT binary patch delta 25741 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 MZq-cq8;Pg?2BYs_!vFvP delta 25131 zcmeHv4OmlGp7)Cus}fLAlMqNCkw_9gH6|fO4e2z2)JZJ6`w$kzk9p@2Xw}xi*_nN2 z2w%55-PxUafium_Q*4ws3!}CwDvG1E?YW^^s`!aWYg?hM>$DXpWx6}<^Ul5R|J<8| z&|>{UcXl71Kk>$sbN}amKJGc^{{QFq+kP;z_F!ad)D(f?F%>V~Y$4kENQS_c6~&8t ziJD6=JSN~pUvJ1&qB3egX%adSDG`{n)Vz50 z8Z`7cdNie4wAhNTqpE51rfJj?Pr3rh|0x5t8fDtAn6qprqn6|063S%wiIwsg@W&UK zc1+XED`%L`+0)E^%uteE$Ya`|%F(Q%P|~AYg~j;fb2j|P(~zT+v1YD=Opezn)M|^5 zb&8G3ijBOel;Uis30<>&KMw9lJ?e6}+;4OD$mFrkRvpdk@}bku$Iqp67ii|HLz0mf zNuv!v$xIw!-evvFlyZ+nguC$#Ifc8kmW$3Xdv>?ukMT)_bEVPLMMht~xx9+Xof#=d z=l*#GO3Pa~ubjezqF73*pkFrjtxlMF(ZsOl@jX)GwO0dA(-OE~w>cboJM3QJ%nH>C9_fVJ-(v7!6WovSrsA-6qW>9xjQZ=O0=l-07yZVqs9&I*W>@E` zb7Uwc+LR-p8E8|AtEMB>-J*j&`pByFI`VPn65NSL$as01!6VHUsB2hj;*T(6IiNxI zhJ06fS+s?B$;HGyN=W*PHhxH0|UI{)W)nR{W%`i(d*x!iHV?VxV)#Q~N zL4)77n5R_G%x|pWjhx?H7=wrT;arb9D=X-*mR_IMiW$+A18pgo!7T1Zn;epbRv#6R zLPepo?IL>}jJU$!A^S$$b?mi9O0N*%Q)s_qWyDeR{~VI^P#<(QpM|Vl3|M;;4+c9z zgW3I(RVA2t9W#3@DD8y=L9Hc$7rH41J>&dgL=S3wAwGVzH_n5ea}2%~>JN0`h4_SI ztLQVfAAag*dRTG7)@71xUYd%Qp(u|DRjyJ;>_cllZo$iqQM3 z#FFx;Uba-8_tFLS8vK5Z>1Fl4GE*njWJ0rkw-_y*v#9d_;vW1_35Bopu)m=yn#zjS zOHH~N_(QQyjjxMTi!-+w%d+_fz63fhF;#p6dp3JkTALf!NyStVHOrT|Zv75wy(-qO zwrDQ1bc(Hfg-&)fH|0G0KHT*_l#B=QK+&pV5ryf%A5AwY(S_d`kUBQw2YO0WfV*A0 z{^se6>`UcUzQU&RC-J3NJOtU-;~|q<^|-za9`cdnP0hHo`7&tjX6O;p34Y46c^hRC zSLs)2sg*u$+9uDoY0^}ganlajMK(n~*JO?NZCH)YdGHZgu0EjZ0$i8LPPNF^BB&m8!f2~M(H2YlZlGRZ<%u?!e?14?xrbcPrQG*8U zm@kys@}qgN9LF6Jv9qB+powN%;IYZrv2bdALpQ40oWAiA^8wo~))%Cz@Qt|??pri% zdl&nw*J&2_SQ;rjMC-Fvt+JIq{Dpv~8o}cC-iAR&23jYyNi$smy<6;|DW7EBYc-(Y z(`0EZX0@*iyV(6@nNE+7+7_9sMvXh;r0R@PUh4AY*zb1Xj*kMW{Hz;Y5Q=8tQ{EvK zs{S4t-Z?kIj^qVGbZ*m3_{1Pnfp`W5X1!Nzp(JQ+fnr_0fEhBXbQ zV=Xk>P-U`XBrlYsjJO5S`>7UU1#N|lmrzx+I!f>MdQ>o%9mF)+UYL-0D7ZtZ*47Nb zq(>&c4ydEKjTquZ!_D)g_>CGNX;Mh(QqcRZ!OysZjJFYdJ_^ZNgi$YJwywnhZNYY- zIZvgdCnWRp+Fyi6fQ5AdrawHaP#aeM>{!7OC6z7J#imMs_IwZQOvvs8=8c=Uvv5=N zYpbA|cyp{zFRqSGSuW8>DWI_^RJ}_o&3!9BTaq^vxP=&qPr{mU3QUCd#J`uVOwEO& zSUvvYNj%)(I{fNoc3%nh{}lU|nxOnI@Q}f-0E1Zy4>TVU<7-6?bc@5=Y~2n6;tkAN zq0LW$&LMmSqrOr(S{JX(+p~u2Lrgo?=~B`aK9Sy2yHW=`7`e>mTj4m`3tK0$eR_ul z&0kb$X^&1)w92Qqlou4~ipw=3)02vW(mgS#X^(877>XG-D}nO zycX(`uv#U{wL`=C;TeEO=TON7)|OLj#68fvdd#W~D0+`_CL4_hjM$&6DM59sbCj6< zL+cvQ!{QFi?s|@bD&5Gk!bQKEGCsK((=oVvIvQRri&G17^C+#ySI}a`^t3#=p`NU4 z_$-B_Qkl}Gl<}f1tQlcXLG4a5x>WU{D0~vOFXZ9r+=j1#`F?A?qDG^( z%X4kH8amg8yGsmoKK9elGo%vsTmx#7CeMZCbjWMb*DHlmT5V912M3*%{@q+nT{}Lh zFYqO-ruO19co+}*P)@NlVR@|755wD20K?Z!rAOMNg3MCPM(whg_M_P0$iplOUxUHf+H!M&w9*B3g$BcHooR z0&1%TpFwXNSU97D+PZs(-danc;R7jx(B{8-b1s^mq@G$sH=z83>PT9n&VjWmNf^1W zMnlU`)w;z~1*hc8oER$TbpBV$Uzql zWlpzQ6{+$y_O(c$$V=Ky>6<*_OqJ9SyF>^3q)Vb|+$FO53RWJegFYHWTMsLvEU*XL zy^WH<1`h2!JkONjOZkk&Fk~?EpKL|l=u1qq`YT9#hn3oCokHbSxg2g<)lEniS+j1o@#zHlkll)c0E+kOKP4Q7^(GR5& zm1xxu#R*bM_U|xy$Oe7{?%8XLH}ay|s760m4n(vf*$IiX0y=vTce;L|O6z66g!b!cXcls{>f7L(scqviSg21Ohql^Sm|b_=RMW7g zMyE2ine@ku^~c(ZtS$Ou&G0N8Id;6w`5N|@DNN#er>Uq#Vk%NN^-gJnr2YuqR*z5B zzXqDRs_NUUl|?NAcY2LdUsFgs;U~Yo$hlDBlxUr_UMwkc>R%SWEOv^W@DF_BzP#*w z+3j#SdeH@GYGNmIojvS>HLARfQY-2NCfvP&*0mKBHa|tGiZ*N%O(jp`vnj$Pc-kfX zfjMiC6&FdAOVfCfD#c61R^!=hC56z!v(#u}MKyIfWSlLa05L12Qv$=V`gbLJX&$(Z|edY%SNSAUss zjr{{`dIm8j+3UPQv7o|HV=J(>IA{xcm6i(YIw*|EX2N;kNX=Ydq+SPmiGwJjV?~Tk zQKnZ|TkH)b4YWdrnlvxOUWF-42rU5f8ms}ZU2~&fznLO@lk&j#bqC7RW2sH}hD|W@ z;483RcD?Bk(%o*S1fMrrOKUXtqqGoS-tf(iz=9hy{{mUgF5-?B(8&5=VS|OY8|6Hm z8Ev90!gVtmX!ZpD^fIb?I(;TAcP-MyWolTNUrzhm*_UBir*?6+_+jF^j@G@E8RLw# z>bKESvOclui)ihp$7lGv9fySYL%6FcT|fH|t<>{yPtR^Vpijj&et-^iCTM-j%Z@mU znmi)k`qaHeR#}unuYgION_n*_u#5YUtyk1mHCOUd)!2+SbS^_b%SbOdOj-gqF0Y7E z4eb&yjPDM?hQ+wQXf;gi4t16(9d~>Z*wez}4uk)->hc{rlejd+;PVtL7j5#$Vx`9g z>p_Jx^6fy*KDIEesHq}`7gO?56rWj;ya&pG)?%sFpW_?&v)z=}5v?={nhPmtxo*({ z6|CuK#}<)RmX(r6ReN?6n5<7JBh@;qjI3EKCE52TyjYT#%MVy*(NpQ67f*!%4Q+Ws zdXj8@X!ey|_>%k(bVa{lje_?3D=PI==5q98mN5PCSZOlsO53;j^eSI^ik$O=u;2+P zin`((o~P^KtMd&|;PyAiEiWDyN>I(h#J9uoSL&<#U) z05vUAW%()Ew@qS&)i=x=KonhLV9*TG1IcyCB1<7JQ=VFe*hM+>c=rnDVldfeZBTB* zeKhJjw}5DCAL~JJM)rcTe&!O4BRpIs(H2UpDtX!4v;pl~te-d5;~>~p2itm7d_lCt zPrbe)%nh)=n39mUsgIZoU|BOBEL)u|tzF7XMQzQ}Rrw8<(0*q;YMnK2+B*!40OFVt zp%=9Cbz$BUt{Z6RhHg-HojJ$CzF?#>#u#UVEea>iO@cI2zWM?h)-FJOj_K%g$0NnP z#P5X}eDalW=K=B13~)|}8!%^+Av@|{33;)ckz|8^gXw~znk#C{#HlMJyjW^AVp<|N zhlZo$QQeZ+@x5#>ye`6irjzrN5I0)Ti-Qha5-;L?FJaxapC&G<&)H9y(`1G5;^9h( z4IEUQR95FA**4>B-m^sfYrI1Tt<}Y8!OIfrtB{k9?9xAjmnS?>0Va&#GjX|z@w`+u zi%_f1I14;$qh0UFM>Bg8GAl;ADpYG9*@F*|7qNkgGEwlA1b2!vx{y069zFZx;e?sJNLj#8E;2z1Idym$)@D1Yg)Xj@i5VB2cvywjRswY+sYk`Qh9k&G9vp2XE| z51L1`kSx03_CKg4dW;pE-l(9#&;ne7Ez7`*Hk(h-D3|f#ty6JoZ9huoB~-F4sKi@G ztj&z>2r7XrW^&V^%|HLgDfM;J(fJjBLT4kEPX-Dm0|k?Tf{>GXGEguXD3}ZsOa=;q zv4Y7!!DOIdGEguXD3}ZsOa=-j0|k?Tg2_O^WT0R&P%s%NV7Q3GWT0R&P%s%Nm<$yB z4-FLjaq5`pKz;SRSquxn0^Fxj(`FSpYu{Qe9)m1^JTVLlSOZ5CZFWTw6adK*jy2%3 zK#B`;JSHIM11?MO#2lRZ4`f-E?l5c2u5^L}AX&ok2z(Yuanx*aZK(;_5{6I+*rJSr zf@rV>FbiXBfqO!>ICKWEC8#AF#SpY5%3+2|hiVj#Yk(}X85UA-w`>dU+ae1V6^?a) zOwf9e0s&)#*_9P4D-8Y+D2q#R#ajs=f>>+|14Qsy!czj38ihy%w1k5u#xyWk7swK9 z@}Rw;lT@Lm2~Xh)3Y~x=z={);Z1hMJN%(|H9*eSwe=Z|>^u&^0^EH}n{XY7)=l*_t z?8V|x^6Gz5jY2Y11`->-H%Nvy?v^kPwBi>FqQ^owp8ZAsT|zh_pU;>I#}<(8dHIxE z13iB6eA;&dJz~EN^gv%fuaOg6gqL=>yb!?l@^}cZ+vV`eX@@t260v;yW}5vH!ku5j zQTQ+6>`X5g?Y&#P$oGV|0GFtKDgRc?C_9N1GfWOGXT$mVdk2}QLLd{Uz<27){})kG zIc4?YH>wRTHy-ic9=7tA8A;y-S&@DxWCd0It6~vT>{V&pEpFURyS3!d0~~kkag&}o z$&f($IvxDvvI;aR?M`1WcsQ>@OKOh*+ zztkmxX>f{#1Nb=n?pwoqbhz8?_+=E2gSsug$5__HZsw-0k69roRFx*V22oVMp}l zdxn#_)VXGgfXV25bm@1CDoHn8g>L%2@4aJm zL&CR31HKJ*=20$3G7oUmqP2~24-(*}Mau1SC$a#C!De=qjUwnW(TCd;2*~Qeqv-M{ zt{7nmtk-8&n_bq?2)uzCpO=~U*jM26_rrkfsa>&xD}kfk0MA1E=0AD|2);@{@WH75 zFEbRqGJi$_01Z1F`O&w88FEIF=l$Wp3`IIRRZa>c_{4rV3yfabIsI;Nhn05z{)d5! z{FcFRz!>e{8A)t*5%)*w9b+X^MY0EokBn?_!3k@WC5pSlLJ|RliMv%g+Ld*8D9jZu z*=^95s~;fx5>@=$?TRmk;(6bc@0jVr@611aKQ=XL}RUWcaODpyJ+;&#KZ>yw@yZl2PAi*n1o@TD7|wWH^I*W zG87&SK!iSYKzBm&{glhalNJ#m`S05nOa~Xg-|L{gsH;ss4v>5SQXP(Y1iHwJ_O?mU zlZU7Omw?Gvj0+~Ov5VjZ9y;M(@a>O(8{dw*)et4iQbXMDE704^y3Dv6oU&$rH@u>Q z-<|kp&F&Vn3q0*Y2YBMqBklEyZ)K30T~henPP=*mxjw*76yCjS>R4UIyTs1(5kMsY z&pZBb`5wUYc$DsqbeP?s{wGMfae_DUdT{@DuvLce9c|2^86(s zYS&TSPmI-#hrQ!7t&m7*?b0CAN|%GEP+eq7=;?Q zcYOX9K6+^j61apZBsSg$g-hk_Ia~rPFkf)j(V~EnWh|lZ_;fM2(~J9rIw$S+VrJL3 zN;3du3>Vv!1Sw;}iX(NKSkjn?ZqXc`fyV3BgcH!L4iM15(WP#o0p8W!w;-UwT;y^M zFwiL6hNE4;PeV9r2H``jy-a3;@mOhA-vcX+*5vk{2G$zFT=OY+4XO9iZtfhI7k3IR zg!N`|K)>7Tmgd^5c*Mo5p$w^ys=sdTb}TDdoTd8AqEB|O;Q;L_ouZ%Up^=V^W7D34A?@Hx3N9vyVLM;7TO zluKhsc(~J*o8fdN;fDSO2M-@Mo5qpw{6!x>F8R*F0Qn9aUF451lItbLt2IZGYSC^h zcWMwN8ic6<(jGGYyM<;|wAa0dlsF9QK7C%|po7sYAoD?gxgvh-{z-h$nqKkaw^w8J zx)r%*a-@XWs{YBm5Pwk4ZTN$LBOdEN8!+41j_z7WrULy4TyGaq9guX}_$fotWh1`M14k#$dW8{cc zxD>I#V}w3^SN$CnMwwTa$O*xbpYgjLyQ zC$$lmosAySd0 zV?`{XS0Z}G&?`Y6IE)WT{CX%0S~Qk&DNve1#4RjbLvY92!JdncA=cR<0iC7d) z%mjG^qyY>ajw}NqY9blJiJBk-03slTo3@q_>Lz0H80sd-113WfKfk1g>K;z##9IR? ue1sc%+zy)J#xgwdnlP|FaZjoNG?__xX!FnIhnDrd`qTPq*|h(~68ZliP Date: Mon, 2 Feb 2026 11:59:41 -0800 Subject: [PATCH 339/356] Fix Bridge industrial light annotations; Fix by-building draw algorithm --- Art/Districts/Annotations/Bridge_lights.PCX | Bin 88348 -> 88471 bytes injected_code.c | 5 +++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Art/Districts/Annotations/Bridge_lights.PCX b/Art/Districts/Annotations/Bridge_lights.PCX index e24d0669b6aad615ac296d44f720dc12e9a4193f..65a89466e69f16d2461d164be9eef66614cb7a32 100644 GIT binary patch delta 25290 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 SKedistrict_configs[district_id]; struct district_infos * district_info = &is->district_infos[district_id]; - int max_column_index = cfg->img_column_count - 1; draw_district_on_map_or_canvas(base_sprite, map_renderer, draw_x, draw_y); for (int i = 0; i < district_info->dependent_building_count; i++) { + // Zero is "base texture"; Actual building column art starts from index one + int column_index = i + 1; int building_id = district_info->dependent_building_ids[i]; if ((building_id >= 0) && tile_coords_has_city_with_building_in_district_radius (tile_x, tile_y, district_id, building_id)) { - Sprite * district_sprite = &is->district_img_sets[district_id].imgs[variant][era][i]; + Sprite * district_sprite = &is->district_img_sets[district_id].imgs[variant][era][column_index]; draw_district_on_map_or_canvas(district_sprite, map_renderer, draw_x, draw_y); } } From 611553316c644f4d841d2c0f43dd170b2e34733d Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 2 Feb 2026 12:15:24 -0800 Subject: [PATCH 340/356] Add modern bridge light art --- Art/Districts/Annotations/Bridge_lights.PCX | Bin 88471 -> 88657 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/Annotations/Bridge_lights.PCX b/Art/Districts/Annotations/Bridge_lights.PCX index 65a89466e69f16d2461d164be9eef66614cb7a32..d67ac5f9d604388cee7e449c740225c1dde1316e 100644 GIT binary patch delta 9101 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 Date: Mon, 2 Feb 2026 12:53:19 -0800 Subject: [PATCH 341/356] Add Port NW light annotations --- Art/Districts/1200/Port_NE.PCX | Bin 20843 -> 20845 bytes Art/Districts/Annotations/Port_NE_lights.PCX | Bin 20841 -> 20770 bytes Art/Districts/Annotations/Port_NW_lights.PCX | Bin 21246 -> 21161 bytes Art/Districts/Annotations/Port_SE_lights.PCX | Bin 20756 -> 20756 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_NE.PCX b/Art/Districts/1200/Port_NE.PCX index e130ce4f4f346c20e408d515db6a2bf5659e1359..e3707673a7364af0941ed5cab4230a6f3f48ea72 100644 GIT binary patch delta 56 zcmaF8i1F+Fu8f2OVxPANf=}d9LGa#@fx5 HPG9%{k(n5L diff --git a/Art/Districts/Annotations/Port_NE_lights.PCX b/Art/Districts/Annotations/Port_NE_lights.PCX index fb04838be4c9dc0a6baffdc1edb662bd11a10dcb..7e1be40e062f7832593622edbb72c0ee93c3f294 100644 GIT binary patch delta 1742 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 J(>IS>{te*UaL@n% delta 3657 zcmcgvdr(x@8Fw#4qXB}TJOokRAim-v?233f;tE2j6H{|dXnagt)6$w~+mtqwyGz>J zHc8tV^mL|^VY)3tY2!E0yK9LtBI|-Yw4FWu&Rr0&{L#NU z!`-`k?>WEk`+mRQ_ub3kR#T|ebnFXL#P>>HX}e>(5osdy!`Wv1%#U9#)qR&L@lT1< z+wr*(q>jay?kJb6Nf8p(}`bo&!6eTANh@L4m})h=B+5>ISlt z?n-@aE%O99+_UBt=Vrov#{HpBxy!=>%;VxDS|=?Kv@@SeY!#aZ8Q#-9G;Z-*yJt*r zfWt7{gAo{IV{rdG|0Hyk&$#eBC%}@LVL*c}hP7cGK9Qw3bj%T}`q$!bMp)n+-J7bTEVcYkTAUB3>|+6yd6B=1 zij{gYtR@$Y&CEoq)@D{OMPZTz&pz;j5A^_#%d4S~=tyMYqC$V0SyW6^rjbJE-oY%^ zhD-cXXn-@8@eCz=!pDKj1DJm6f)2CdCac!-WX}!~EhW^APb9 z5A=|earG3OgC3#*vz`3&m)-|Z1ZI+r^C8|5>I!k*%b{N*l1r0P8&yj~H#+DJ60b6% zT3ex~S-7PGK{_y{0NlasgxD9kMpR0&6X`%!780=_yFfB7!@>psfGzIjK`+V?6l1jmeaA}CfqY1{3Ci!Gb$2g8WsYE=N{<5g4X>W zxT*mV&_x;a@tkGrnO6Ixg;-jVPoL<#0W63xQAZGlpi6D7v0^niMTF^kP+EH@Z)F;3 z(P}mNa*kCP^WB`)68`!mr^+E@H>w}p0q2ebJHb2(3eX`YkF!A|?@f<)E%bpgF%14- z=%KsO87MCfl(2C@Cry}7KXyG1CKOEcNecr}%jx5%PryWk+2IiM-P}r(LW8;6GBAD8 z*i2vmlSfDc77>`nTC~&fU>`90)x2jTh4l2Vc_qanaJWf@Jt3e9!{~;ckr;Kns#796 zJeaP*17MOy3-XVqC@_?yLC=vxCr_O`WoI7ktR1>>!5OlZ@`kPo%xenoSz5FL+_zWa0EmL4KkbgqpCk$Ox4h*PEs+e<^BO zoxfOqc;1wZ$%?0@LcP9X#}iW%zP@73^zA7tpNX298U5kPtf_d1S8l@u|GhGA8qBIy z+VoxLR{hANwUw{F95IIl6>vizO)Xp@;i;;y^5La-3YUvZ=L^je_~?VeZ<+p1Us`)W zy29y|waF3=#o@I-TL=L~_rncvGXZoXLDm)Krw5CEl_I800Q_TVb2A^f6&yQm znU)o2XM=Bui@x&1t$A?!^UyUe?2qQc#|v?o(JuHTqCYL(lMKCZ_bkIG2{QO(@FyUW z7vuS8sJOs%mljykVmd|Z3J+h$2ibk9Skz<~g|TCYiAr#rU*5b$5dgT&OK(}yp2lzi z=3{QW!rZ8tAF(kP6!10|t%Bb_gD>WZh72!le{@}kk!-Vl&Mf+@^nug$~& zdSv9phg5)9VX{KGb;ZIkAK*@KO{I#gTa-6JA+E+GFiWgWHd4SbPSB#jisV`F5BkQs zCJ8#};u2fxL*fOa7|f4*9`dV}WaNy8LlR+B5s1t!PJ(|$ZO2(QOAth%L(k7?&%wM7KKGBs=-wwlBic!WE7#F1;OKln+mdd69NC3 zfJp@#1Oy8@2tBc}JQBtnb~?7PSb_juUA{CMp9!PQ==D*!=Rh2+$_az`2SYYQ7!~X% zDD*q!=EU|vXA|_d<7?(^@gjpS15#APV-9+@Jk8WjedQbIUfYv#_>zLub>SPqJ){xe zoP4x&Q%Nce5IgicTiFiAKMURBq@72rVShAJx+y(n`hsqb@2}yfQP55=ZNhX1>1^9( z{9mKSwhlkvv=yd4DmXq>+%TZRfR1}UwowTk@a@g1^HCcPNCG<0$0ivqLXKH(GW z6gPddvUL8Vg3YJBl`mjgDB`#e5K}G+NOw?7?e`uW#)Z7 Date: Mon, 2 Feb 2026 13:09:57 -0800 Subject: [PATCH 342/356] Add Port SE light annotations --- Art/Districts/1200/Port_SE.PCX | Bin 20756 -> 20741 bytes Art/Districts/Annotations/Port_NE_lights.PCX | Bin 20770 -> 20761 bytes Art/Districts/Annotations/Port_SE_lights.PCX | Bin 20756 -> 20662 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Port_SE.PCX b/Art/Districts/1200/Port_SE.PCX index 15a83233461790736c67c812ce2d2efa0392ac29..18f1c1551aeccffc48187f5d3b90d08e9421d7ae 100644 GIT binary patch delta 109 zcmbQTh_Q7M&G>%uEE9FcyPJ=hd|(2J?KC%GI`MDw z2XhrRrb7=VOW1v6JM{3-<%174^V?rxWj^@e<>V(W>sdh}FDFlP4Ff6q;JO8-Xq8(Q F7XbV0H*o*} delta 122 zcmZoiRU`%@560*jNreICyz7yWK~&!w-SXN1OlIU14Q>HTkN`dgg#40lRk?RQ%wZ_eo`QQ_g$uHchnE$wmOipzN@?VNfUf^EEBqlOh*2A31U1V~Y zM=410IS*szd5R*F**r6u7)2%*dFrqoymIgX(Co#Ysf-UMb9x!D9K0eYGTGLv2c&TG IS+7tw0B7GnO#lD@ delta 183 zcmbQah;h*(#trrk%pwOrPj+;;!X#2RS={jglgPZuFCA|JMZZlx;&cN@f0;bb`8tsP zG`ZYm4wFd3WKmZQkYan+IY80Nlkd8oU=lerdAXYZokXDwkF8=GZ6^dZ|gS2A1 zU{4yGo5GK5`Q6UStmnR?@-NbI7qcfAUv4k5W7L|OH*V_qD80S1VyDVm>@K)?%w7!p zb9)Kw5qs$}w$uh2m4b;)m^EomYk~c6OPP6!PfWceU?68-7#mTcmKsSG{Jm2jl7?hs;6}_e#f!a zrgAOY>0$K&8s6V;8H3hs)2CFosw>3q&rJZ5kPpy_hqHZp7*Vo+WMg}BveOuyrrXD9 zV$XgWF)|tV<~S5Kz}s@_Y-f2Wr#w;39G5ECAC3~~hgwM(8lw|Obef_;nqYi1r^k*7 z8qjIHtDC|N0LQy?oze~EzJioCIP+njcDffEOz>f+$`3dlv)?UAG%gu%t+ulP%Q8PT zC#@?cQpDs|SEUTU)pc)qjH2x5fukCcLBx6@mM~~AYK@z|)bwQABG0h-AqknDh|@Tz zcNHO`<#Hx5VKDB>E7>SN30l1Nzfz^0mX<$La*)M+>R8${;FvF*_B=eF7ryWdEd(KVP~1Aw0;4}VGB(UBBOck=u|&{zAQ&!eZ0F&Ke4bPUc*d3W__+*k$X2nOe90;}ZMgJ}V^jUU_eVUA-VEILn-Z zk^>Bj%$DSO@wX79y`%~@SRy%iw?yW5w-=HUndNYc*Nds2d23xTWO_YJw6SDMS; zE|n@XSX%G+-;4(rWb7;pVb^cU9*134-U<71c{6OJ{M)e3RWfqRs_iy*@W848g>P0@ z@Ykx6<)!SZ%87s#d~!sy(oqs;lMlOV!14_^>*E9Zjh;A-hba(=k(= zr$6JWUet6YY_Ua4gAh&8hx-p9J-cx0dGn}F*FG7eDQpeW89(D{jYq0o4f8U(MboQ= zMHInN8Wj^NouSJY%#%1XFh(PSP*$8OY9c_f z08sv|W>tz{i~MzdNCWyW!X33zq#v)X_!^@KV%^+F@qmcyxYOei{`=a8v3gfsVUA#8 zx|5=!i_w=fy@*Er0pN%Sbia(;Ubk72@LpYRiUfRv{w^kH8Z&3=-iL!1?pv9-esL*( z=gco43>QrP$NOsX@V>~@!&4eKi3`BWV04qN!}oP)@Kwg`zM>4lmdaU?nAI_h2vewl zZ+kLJ*8ufK))Kmfn)-?=X9#2IMDz1-Hx&vhG)9x*C?s6O0UEJl2@T@;^&P3w{j{Vy zBPcz;QeT|wmpn<0x~a`CVAqfy(r6T!)&FogqX}`7Y2XDf!0MI2nP3piU`40IQjGCe zS7$2$#`mw@wwz_4RF_$22sb~oVO7x_n<&ynobv05MffKG6`9)HmFBw|ijxFXmhWru zWDNgE+>dB;6e37aP1;Y{}weMdpa#Y3A2vcc6sa(^x34(bpTZ zWZizySdk6v2pJQo>p0T9f-=YDfvA8Kf7Do2COZHNBiIlN%w>OE73nk{4rE|JW3X0;)?}CbiS_e$n%Y);H3L5>G$nL?s?~{y6Cl;* z9jMPWC>(SjCjkGAW^W^->ABjoAYF&j$wo(IgEA3tGIs(J)@OklsN}q)|GQbL!gKzH z3?}9)&(J97|HP9&;{Oviyn4TTIrGaZ)3k>FLXO{GfJTl6pt_SOdRWuDb&c_fHb{&1 z6!!l_t8~m11AQ~q8A6#Ts`bn_O+@RKov?jPVG2{E{0C*pkkC78ob~{K_RlGofLIT& z*#V!s8W`WW*6m`lCIKejK9qPAS3t`COpl;&(Dt=&DeNjQYw;{a3xHzAx3m<=*W;y@ z_3~epXiF{7U6;h52clj5C>dzcXWrWZUf8-zGJci6+3G=WPll<_IVar&`@|p^s0r zt^uN&2R1-0B-a0aC*OSFWvu44Nz@Z9oJ36*u=NbqzyIKOVTT{|%e2Sm zpD3Aj&bmjj^!&Qr67#KfjdG}Ke^esq=X=}Bu^4H88dmbY=^@DY!QVc#W66I2S`fvQ delta 3051 zcmc&$eNa@_75BZx#bbkHtP+E|8lBR*i=|Et?z)SzNSpV1?t(x+ z{_h{}-Z%H2dp>^Wch0?k=n{^+E1Y;%&>fH*r*{fJ6$A-}#mdxZf->DwvEu+V>q;{^ z4(wB&Y4YuQ_JjSaa)0(5UWS9Ze4W(6;BB2PgJ!9zdGnU-f6QoKRka_(Fr+gH9T3-* z(r8F46ZSz{TE(J$J1or^yBuGFpQLRU-aCCgExG9bW4SHEwn&$a=i!Gr4#5k@atcMS z8-{YK1qNxkg)~}on{-S&1K-cB6wbhJa@$1=KqR*!4Fhy+iD9#dKGUB&n zE!HnSIIPlsf`xq{8HX^8A^Kt%>a2gdNAA<)fcAwtE2&82ft}=zEz!$MI_u$~VI5hn z&nvLG>s|QChy9uo)TUIsSkuauvV(t8@t*8;pFDdK&u`1K%l3K~c=CQ+iQ^Kw)oe~3d$HjYD~(I5;!v$0Ohxdty8SLh@hf*zwO z4?|~;_ae$4wIWk!5zn8IaU6pf#}NF**u9iTjsY2C-Y)btN{AA?m0v*0PUXMOVQ4NW z5-|q53M_&j&KKmsy9I{S-+CR3)x#oFLk5Nenx=lLCbT>1u&PKheABe5M3Tv1?KBD; zEjNx}%uRoU->Ke{y<|#Z2p^b?B8$NVlg*5m86oTThcLz>{Fke}s7`33Dxox{NTMII z3d{0Dzany^Ix)gxeI$Jf**=`=RamzI&lMUor?K;e{UK#`2i_3YTfCb>7lbM2-fgjW<18BX~W1{p=mD;@)wpfDmBN=ZqxA}pX` zkVU&BPYuT1u(ik{G6h~PD&~{7C@UR(iu){1DLCjpr@*?5fCvk4kon)JP20im%Q`Z>u%a_&@TCEZZkxu!q>%X;pL>8- zKwOwmad;56RU7j$iH~qnQwLOD!4Vaw{Lu?4(|ju5fj6r2MZ6EcuO|QQL#Vn$#7X!^ zwUtlini|~+nzz-IrshL6MXhX{)PyV>IzeNOO( z+AHI|i%}dWK{sA?9*1a+)#k#hI2u;*2KreR4l!gdpLT+rO*SwMae(yT7>m20srD(u z^g5;DuZX-N@2c0ehboBVpKJwwbaVPP_?>hxr75`b>b*STr48c_MSc zj)tA-l;04J;&`XWjbm`3VP%>N%2Xt#}~(_1o@6C9iO*2GN14g7;*Bq!%B|-*hLDc_SZypreZ3PE(H6g}k$hF3v`ui!KwY9n#WDm?wxzUKZ|y%}pEh z^Oi8|hpwg);R0N2+Pj!oaF1>XGUN3@Yje4iqMDy-d_a}RsZKOZ0hgS2f`wgvjo^tg zxxR<640;&+u9=!i1S*`3IXu<#Nx&{Lxi0lRgb6WV(dv zxxNv4!pI&>xXFE|%gJ0U%nP~yaZ6-9cokl3*(*}L6*gwVc#Fj{oe$T%eDI)WSR|7n z7exV)mSwPcU8istF0ZqGc{UNCt+NRMC|&hU%veB&OkhiW3$2m4zsq~z_^7P=oDZP_s1T9?TSzE8s;x@6`u;Ar~=&p>}0 zMG%FW4O=**c-5~-^B_I4;Ux~~S2li^XbvxIbY>6+^c0!77E&1WZhU?kgDj}qw2ORy iXOn}*&o(_zV^jMIa%6A&i`>Yq_6|~Wy8UZ=7yb(<{I<;i From bfb4df5ab37df8b1518eca361a32a2c741f161d2 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 2 Feb 2026 13:22:36 -0800 Subject: [PATCH 343/356] Add Port SW light annotations --- Art/Districts/Annotations/Port_SW_lights.PCX | Bin 21565 -> 21420 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/Annotations/Port_SW_lights.PCX b/Art/Districts/Annotations/Port_SW_lights.PCX index 02ccf89fbdca34fdfb70f2af3c79409ae35c00c2..98a633cb9842d2505e9b91ce28c57c9ce96a0a74 100644 GIT binary patch delta 3814 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 delta 3800 zcmdT``*Rc56-H~E5Q0r{u)vNV7%&(co7%E18DonLF;FnC4n#DbP{=g2@zkACcW^t? zU5U>2hosXC&FyriVJgz0qBKAVn81|Qfpb@WQE-hVOh^-uxF{U+RE20w76QBbP2WS6cwOV=Pzt^v&1dWuTRQd z(GWMpIy40|=mj6rP2NbeKL=s(IXQJCnD)E$P7RsqRlsY5Uh376N=OJ|fqodEgD}*`IyD)*xZaQJX_)dy?*tfJ$X7CcH2WAu2Ou_`Z6Y@^ z(h^2f)ny_>8T;1(Rp>FL35M&C-xgL}y2XmPAF~@8M0f!7?ZXi0o(BozQv`y-r9&cm zEsrTcoSC~b^lYf&h?(LQ*HNk}Fbq^sh;fl6K1Sj<7rnX*r_iGees1NKmCG_*L(5NO z1yr7c%{;r|TS3;PQN+frR2?Wrmm%s%MJXSRQ*%r?MRkVbvNp^NA>X7rE8U=w+N`qV zG5E?ko)Y+U@-JrX5+F!r(k;$WE>vsyfFu{3M55KKYXrLb5Lr5>k7HQv@TWmg$(PmO%jlNKYP_S zq!9;I3{N0=yNf)|ubmd|fda?0 zSv<4q=NRR!ReKDs#s{l53iAQaCK&jfp|W0$U1$8AtSwq;gq!gA@@})h7B#zBPsAqx&F`F_= zXPi-)Ac-9ex^ZO$H9c4OLK6Vrm=Sr~RKlZQF=g}Hm!`FGGvGGK zDPEC;cdP~)lA!B-c%p;ZTl}&BZNycan+R=o!70HN+OK2zXK}RvgCwVy&}TkN6rBB#~_hV$yNavsVL1{q>7ST6ler@7kZY^ z$d5D(peqVP2!TkYOY_{ll57PJ+~VI$-I~)bwedA$!*0$s28M?c=_)nNL(F|>-Unba zu104=B-vaL2O*JEnX~l!_ssbM1jujA%XR#h%|!;0^qQA07A5cs)I+V%10E}7*o68Z zDjT^MY)AdzC8d^}^vS~#z9IqsBX}P|!hT{oxD&ip;B_E&T!ZR>2frZrK^cI1v*AAx z>^WAq-)5yY@JrYeAu3@BMC|;OL<&Y$N{AX$2}5uOCNN0Wl>K5J-(Rxbfr#(n>1qTI z`A1nn8jgmoqoX1x%Z~}!ggJ5XR$^TJ)BQ*>_R{>3@7kMhvY7Krdm$(kH`Ed@6Zwja`5~ zl3G!<1l1qFioo`5RlQV3yQd4nUV-HtARQIy0ygAeMf&FP0+?VB3Xj?`uwMA z0*jFx606tpQo)a~GH7>T^PE&U5V0XoI_X%`ss>X@M`pcrdlpW#V?ur8!Asxep`MbC z>|3|if)o5qh(MHv#DrEJ?I}ZS*%UQSXx2pN9U++9Tz8l2^6|?*eqjnhNO@J^l;d*M zlcW)PWpsuZunq%6S-+q2OAGvHQ+NP!_Z)3dxJ>=-dQhw{+*fcdX*Ee3zn+$Cb z`&0<4RJHT+o;=q=3M(?m)=g7G@ou`S=hVF!H+B#Cpt?l>Pve0t4RJ~LF79akz4b32 WpFHx*H@>~AeAi}E Date: Mon, 2 Feb 2026 13:31:40 -0800 Subject: [PATCH 344/356] Add Park light annotations --- Art/Districts/1200/Park.PCX | Bin 24063 -> 24059 bytes Art/Districts/Annotations/Park_lights.PCX | Bin 24063 -> 24055 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Park.PCX b/Art/Districts/1200/Park.PCX index 121b267cc1bd460fb7f1ecc5b157af816615fe7a..c968e079ae4bcc5dc28ea08aef859250bf80b8ff 100644 GIT binary patch delta 94 zcmV-k0HOc?y8-*V0kFCRvsD{42a`=XEwdgBNddD2Bzy+5O+f?%vrYkQ1G7~d>=y$J z0z#9X99@y8-{Z0kFCR0mGBL1Y`l3lOY9Y0iBZ}9Bu-^-;1v+c6!Qhil2qFQ(lW_=X0>QzP+Xx~7L6iOnXaT~L{vRFz zezP4QX#%tF3w8kkev<(t6#~J{lY9+%0>QzPkt7}g!jrHhd>F#+zo1#dU1+3PNy3v^ znzX_0!Ov@O!N8NL4tWB>%agt(7XfUOI}dXd!qvjWFkM!{d&9S#!k1lsTf%#{vtA}1 z1hYv&1O&5yF8u+sO9P|`lZQ|clUF$(lN=0dvmhfK5CI{RJ~MCuI+L$6bOc92GZ~Zb K23xa&G@&tz|QB_Nr{9t9LW$u7&JAT{{~ zd+O#Fa*LQJ`&#O5mgDhc6z6H(;y7((RhX-oV{FFC<_!nW9z1>U9na)?-b$dhU%YZa zQj)J`vw>;=>tmXUZLXBDK;Q7g?6{9vkvS?-& z=K)%O?cl4-gU=6s<7gM3bnuqa!Mj2{lWS}$g`^I?cJd3@*_AHtbntmuMMtdEW_H_0 zJirir?!!{Mr&Jd-26ue;pushFf)Vmy-Tu^#BoI1lt6t1IfQiVUu?SsESIdx#W~=nNhQUW{AG6 zzJRPMuON@$|VzKL`QknJLfUembT6P=QdNS3G=Ko8p=QOw23BeO$`+fiCBpy>TF^pl2E)C zsbE=p(?ubC15qRWzIhQ?NkWtqc_=W9v~H3v)V{?F4?NHNT>PHBIdnLOre?*+2QS36 zXasmk0~!Nf)xwcAxTk7EUTBeiMv0Iw)4}5;2Ukv^TBGKu7J4;x99f4VxeigdCU4{j zflthwhXZ*7iwtpt8|V)1^mp&;HLNwI?><3y=Ep0jt6{Pt zk3z+$m+X+`tF2@Ko`$gI61+4hQ8Q$Q-iV;@>0+Ym67BL$kI@G%x07~F<4U7Oj2FsN zMBvVOi*KFXM>t>9iV**QlYn_iJjV=Mz_dl?KmwbfK?3g8cqA-hSWFoed-$OQJf$BB zVNtfZDxB<@)n>91TzQUcUl;5Hvl;N*Tp7&=N652hCn%1RsK_c=Co87*s20I$j)GmO zQz>Bby01Pq${1_6lHX)CS*lXlaRv-0^`Hzmo_q(zz{BK0q{ZF}2|2kN}P}BZKm!t%(@al`KDPM^+@Y%_!PN-NoFzd7I% zFlX5e((yqdlF&0vWUo8xX+Q;4cEP56Ryl#6_~IlcMi!}pD6-!|sAzCH>#^xr> Date: Mon, 2 Feb 2026 13:52:07 -0800 Subject: [PATCH 345/356] Add Energy Grid light annotations; Fix Energy Grid art column index miscount --- .../Annotations/EnergyGrid_lights.PCX | Bin 104481 -> 39562 bytes C3X.h | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Art/Districts/Annotations/EnergyGrid_lights.PCX b/Art/Districts/Annotations/EnergyGrid_lights.PCX index 363e44d19c09c9f1e710025621e7d8066be4138c..5d75302b66a67e6f7f304fbd2f05dc8edc8186fc 100644 GIT binary patch delta 9409 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 rNA?^$az!H16fCJ diff --git a/C3X.h b/C3X.h index 0c81e9c5..f1c7d73c 100644 --- a/C3X.h +++ b/C3X.h @@ -934,7 +934,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .advance_prereqs = {"Industrialization"}, .advance_prereq_count = 1, .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = true, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_max_index = 4, .img_paths = {"EnergyGrid.pcx"}, .dependent_improvements = {"Coal Plant", "Hydro Plant", "Nuclear Plant", "Solar Plant"}, .buildable_square_types_mask = DEFAULT_DISTRICT_BUILDABLE_MASK, .custom_height = 84, - .img_path_count = 1, .img_column_count = 4, .btn_tile_sheet_column = 6, .btn_tile_sheet_row = 0, + .img_path_count = 1, .img_column_count = 5, .btn_tile_sheet_column = 6, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 2, .happiness_bonus = 0, .defense_bonus_percent = 0, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 }, From e6b84dc400fb94102d5237acf3a60df09f7ae9c5 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Mon, 2 Feb 2026 14:06:11 -0800 Subject: [PATCH 346/356] Touch up Hoover Dam light annotations --- .../Annotations/Wonders_3_lights.PCX | Bin 21429 -> 21418 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/Annotations/Wonders_3_lights.PCX b/Art/Districts/Annotations/Wonders_3_lights.PCX index 80bc5299663244703767ddd7f478cfe8e0a2d71c..9049590ebfd3e6d1d2fc7eb9851492a74c8e8a7c 100644 GIT binary patch delta 65 zcmdnGoN?81#trBB8MjWpz~9gKcyfZk0mieF)dc$(&urc$n8L<%$bPb-#5oZ4QDOx0Y8(_503#qlVA|Q2FE{i53|FQArX!S!#}eRbjGu=5o87f j!P0b-78uR}hLeaGC6fvnfB`y_iy76E0U9K;gc>F}u!9{l From b3fa6bb7d3a852c7f667e07ec0fb9efb29a69e70 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 3 Feb 2026 09:24:35 -0800 Subject: [PATCH 347/356] Add Canal light annotations; Allow wonder districts on mountains --- Art/Districts/Annotations/Canal_lights.PCX | Bin 115795 -> 115814 bytes Notes/district_todos.md | 8 ++++++++ default.districts_config.txt | 2 +- injected_code.c | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Art/Districts/Annotations/Canal_lights.PCX b/Art/Districts/Annotations/Canal_lights.PCX index 4de393f8971c9fd1c0b276da7133f24167a9e889..71c101935f74e971ff58260d4130bb256705ab75 100644 GIT binary patch delta 610 zcmW+yQAkr^6z02kwA}<@_|_sPZh=9tU=MAOt&$Zf=!7JDFd-izVWrW7B~urSF=u03 z@;(%nQ2J7n_K)tHc<3Pv8H2=X~FP&Uuk_f6BTiEBM$e znU7{M>Mn&!E7!EP_UdB3qt1UNR zh)}g}L|8A5;|pBI8;V=sJFZZAana;-)gP^pe`Ffpd7QyKnhEu(JWhl3_cKUS^WXa? zo#vKUlh3r2gF8+|+JKKe)mVdroncIuT(1#Pk&|EQ2Uhz5tRl(8av w$l7%Tk9c17-OE-2@q8$yFij+VOq0pDr1cuODE}!A=#LG&g|zuwTdd;Ve+Q-%m;e9( delta 50 zcmV-20L}mAhzHY%2Y|Ezk diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 219d401b..e9f048d3 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -2,6 +2,14 @@ - Municipal District (ZergMazter doing art) - Central Rail Hub (ZergMazter doing art) - Light annotations + - Offshore Extraction Zone + - Data Center + - Great Wall + - Water Park + - Holy Site (mideast) + - Commercial Hub (modern, left) + - Municipal District + - Central Rail Hub ## To ask Flintlock - Naval units can't enter canals diff --git a/default.districts_config.txt b/default.districts_config.txt index 54d8b754..f184fc18 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -330,7 +330,7 @@ happiness_bonus = 0 #District name = Wonder District tooltip = Build Wonder District -buildable_on = desert,plains,grassland,tundra,floodplain,hills,coast +buildable_on = desert,plains,grassland,tundra,floodplain,hills,coast,mountains advance_prereqs = defense_bonus_percent = 0 allow_multiple = 1 diff --git a/injected_code.c b/injected_code.c index 67604a34..10326050 100644 --- a/injected_code.c +++ b/injected_code.c @@ -31864,6 +31864,7 @@ align_variant_and_pixel_offsets_with_coastline (Tile * tile, int * out_variant, else if (*out_variant == SE && anchor == DIR_W && (anchor_sprite_index == 18)) { *out_pixel_x += 6; *out_pixel_y += 4; } else if (*out_variant == NE && anchor == DIR_SW && (anchor_sprite_index == 51)) { *out_pixel_x += 20; *out_pixel_y -= 20; } else if (*out_variant == SW && anchor == DIR_NE && (anchor_sprite_index == 0)) { *out_pixel_x -= 4; *out_pixel_y += 4; } + else if (*out_variant == NW && anchor == DIR_SE && (anchor_sprite_index == 44)) { *out_pixel_x -= 8; *out_pixel_y -= 12; } } // Sheet 1 else if (anchor_sheet_index == 1) { From b70d6d0a8fd49a1f004eedbfb77d2b10e0ee50f6 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 3 Feb 2026 10:07:34 -0800 Subject: [PATCH 348/356] Add offshore extraction zone light annotations --- .../OffshoreExtractionZone_lights.PCX | Bin 4353 -> 4331 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/Annotations/OffshoreExtractionZone_lights.PCX b/Art/Districts/Annotations/OffshoreExtractionZone_lights.PCX index bb1d750c9301f65b55aa24bd39f169f49229dfe4..38a718a98c374df6bd2c99d4455372b8e6403b0a 100644 GIT binary patch delta 663 zcmZvZUr3Wt7>E77O&e2M6f46eYAFSh7dAwQt|AgN>OwN5NQi@lLE~IR$%l+^)`g)Q zH7G000*n5b1M$Z6{pM`CQc2x#mTMWGm1#wpkomrybVaUFXN{Dx5(zrz>gsWd~l*ZqH zlDpEv?v#y??k(FIcL8ssjYW?wf(`m2*Mc5%M4`y?!9!FPxEyZ|Ef5NeztakByQG!G0MevxGl! zPHsxFL=U8q2MgTfz!wkC!U)rH(Y0sn*el^WF3HpadPrZSg7MZ|7m3#1mQKL3!a+MW zNVtY8GU3GussvIM1C=@nM)l3ZWGovT#(D`?aaAU!agtz2UIkvDDhJuE^$s$1!=y-d zv`Y9D*JWxFr?x#BV1ryJXJj5OJOsug&AMC$i%v3iupit(uAFK3Lnne^0mKm!P2D|C zYO&K2E~1y(>%ajYzy}k3mU-D|-_GJ&&GB#hEVfh&fP-vCc8gC4hR@)(G{v;r9Mr@* z_%yDJ`T6v?7XS8 zjyaW-ZDU4do~lI1%HDZCshh1UOS#9aK!^$l|HVulY{Cqzz!ps9wZ?RP`SLGi}L<`$8*s2Si_2D@1jsA?_#)@!)v}Zq^)F&clW^bcdi6 zU|Bw8ulmT}!kVU@6?lb$9SDonDin&;>e|yax5u(ZGit6>=yL3N7mDxPxO=m@u;QNF KwO$J|aDM>zZ Date: Tue, 3 Feb 2026 10:18:07 -0800 Subject: [PATCH 349/356] Ensure land/naval units are despawned if bridge/canal destroyed --- injected_code.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/injected_code.c b/injected_code.c index 10326050..402db700 100644 --- a/injected_code.c +++ b/injected_code.c @@ -220,6 +220,7 @@ bool __fastcall patch_Unit_can_pillage (Unit * this, int edx, int tile_x, int ti bool __fastcall patch_City_has_resource (City * this, int edx, int resource_id); bool __fastcall patch_Leader_can_build_city_improvement (Leader * this, int edx, int i_improv, bool param_2); char __fastcall patch_Leader_can_do_worker_job (Leader * this, int edx, enum Worker_Jobs job, int tile_x, int tile_y, int ask_if_replacing); +void __fastcall patch_Unit_despawn (Unit * this, int edx, int civ_id_responsible, byte param_2, byte param_3, byte param_4, byte param_5, byte param_6, byte param_7); bool can_build_district_on_tile (Tile * tile, int district_id, int civ_id); bool city_can_build_district (City * city, int district_id); bool leader_can_build_district (Leader * leader, int district_id); @@ -14419,6 +14420,46 @@ handle_district_destroyed_by_attack (Tile * tile, int tile_x, int tile_y, bool l return; } } + if (district_id == BRIDGE_DISTRICT_ID || district_id == CANAL_DISTRICT_ID) { + enum UnitTypeClasses target_class = (district_id == BRIDGE_DISTRICT_ID) ? UTC_Land : UTC_Sea; + clear_memo (); + FOR_UNITS_ON (uti, tile) { + Unit * unit = uti.unit; + if (unit == NULL) + continue; + int unit_type_id = unit->Body.UnitTypeID; + if ((unit_type_id < 0) || (unit_type_id >= p_bic_data->UnitTypeCount)) + continue; + UnitType * type = &p_bic_data->UnitTypes[unit_type_id]; + bool matches = (type->Unit_Class == target_class); + if (! matches && unit->Body.Container_Unit >= 0) { + Unit * container = get_unit_ptr (unit->Body.Container_Unit); + if (container != NULL) { + int container_type_id = container->Body.UnitTypeID; + if ((container_type_id >= 0) && (container_type_id < p_bic_data->UnitTypeCount)) { + UnitType * container_type = &p_bic_data->UnitTypes[container_type_id]; + matches = (container_type->Unit_Class == target_class); + } + } + } + if (matches) { + bool already = false; + for (int n = 0; n < is->memo_len; n++) + if (is->memo[n] == unit->Body.ID) { + already = true; + break; + } + if (! already) + memoize (unit->Body.ID); + } + } + for (int n = 0; n < is->memo_len; n++) { + Unit * to_despawn = get_unit_ptr (is->memo[n]); + if (to_despawn != NULL) + patch_Unit_despawn (to_despawn, __, 0, 1, 0, 0, 0, 0, 0); + } + clear_memo (); + } handle_district_removed (tile, district_id, tile_x, tile_y, leave_ruins); } } From 3398490d28bebaaa2f26a117c80f6cb0bea75789 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 3 Feb 2026 10:40:10 -0800 Subject: [PATCH 350/356] Add Data Center light annotations --- Art/Districts/1200/DataCenter.PCX | Bin 15249 -> 15248 bytes .../Annotations/DataCenter_lights.PCX | Bin 15249 -> 15174 bytes DayNight/civ3_day_night.py | 270 +++++++++--------- Notes/district_todos.md | 1 - 4 files changed, 132 insertions(+), 139 deletions(-) diff --git a/Art/Districts/1200/DataCenter.PCX b/Art/Districts/1200/DataCenter.PCX index 84c85db9a7f8cc874641012eaadb67410250d13e..be33f924649a72a5e9cc514c7a568977d7afaff5 100644 GIT binary patch delta 97 zcmV-n0G|JmcaV3m?jZ%i!JEOtlky?-1HQq^ljtJz1GAigv)&``0Rtpgs*}(q`vlR! z)|->gCiwz~ZnMcJ;{*c1|C6mO_W{C_rY-9M!;_OP<^jZ$doS7n#*! z(I)u(^b diff --git a/Art/Districts/Annotations/DataCenter_lights.PCX b/Art/Districts/Annotations/DataCenter_lights.PCX index 84c85db9a7f8cc874641012eaadb67410250d13e..11401750d1ac5df478958b6da59cf9f6bd24c48f 100644 GIT binary patch delta 3061 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 A-~a#s diff --git a/DayNight/civ3_day_night.py b/DayNight/civ3_day_night.py index f1bc020d..b7b88bc3 100644 --- a/DayNight/civ3_day_night.py +++ b/DayNight/civ3_day_night.py @@ -1,14 +1,16 @@ #!/usr/bin/env python3 """ -civ3_daynight_pcx.py — v4.3 (stronger nighttime blue + stable noon-neutral zone) +civ3_daynight_pcx.py — v4.4 (remove #00FF00 from palette entirely) Highlights: - Half-hour (or any divisor of an hour) time slices via --step-minutes. -- Green→Magenta index remap (on by default). +- Green→Magenta pixel remap (on by default). +- NEW: After pixel remap, ANY palette entries exactly #00FF00 are replaced with black (#000000), + so #00FF00 no longer exists anywhere in the palette. - Palette-only tinting; indices preserved (except the optional index remap). - Noon-neutral zone keeps ~10:00–14:00 close to base 1200. -- NEW: Stronger nighttime blue response using the existing --blue knob, - with an additional night-only hue shift (blue up, red/green down). +- Stronger nighttime blue response using the existing --blue knob, + with an additional night-only hue shift (blue up, red/green down). Tested knobs (your current favorites work unchanged): --warmth 1.05 --blue 2.0 --darkness 1.1 --desat 0.85 --sat 1.2 --contrast 1.08 @@ -19,7 +21,7 @@ import argparse import shutil from pathlib import Path -from typing import Iterable, List, Sequence, Tuple, Set +from typing import Iterable, List, Sequence, Tuple, Set, Dict from PIL import Image from civ3_city_lights import is_night_hour @@ -29,8 +31,11 @@ PROTECTED_FALLBACK_NEIGHBOR_RADIUS = 1 # 1 => 3x3 window, 2 => 5x5, etc. PROTECTED_GAP_BRIDGE = 1 -from collections import defaultdict -from typing import List, Tuple, Sequence +from collections import defaultdict, Counter +from math import sqrt + + +# --------------------------- Helpers for protected ranges --------------------------- def _bridge_small_gaps( ranges: Sequence[Tuple[Tuple[int, int], Tuple[int, int]]], @@ -50,7 +55,6 @@ def _bridge_small_gaps( # Not horizontal; keep separate by_y[(y1, y2, 'nonh')].append((min(x1, x2), max(x1, x2), y1, y2)) continue - # horizontal by_y[y1].append((min(x1, x2), max(x1, x2))) out: List[Tuple[Tuple[int, int], Tuple[int, int]]] = [] @@ -64,12 +68,11 @@ def _bridge_small_gaps( # Bridge gaps on horizontal scanlines for y, spans in by_y.items(): - spans.sort() # sort by x1 then x2 + spans.sort() cur_x1, cur_x2 = spans[0] for nx1, nx2 in spans[1:]: gap = nx1 - cur_x2 - 1 # inclusive ranges if gap <= max_gap: - # Extend current span to cover the gap and next span cur_x2 = max(cur_x2, nx2) else: out.append(((cur_x1, y), (cur_x2, y))) @@ -79,30 +82,37 @@ def _bridge_small_gaps( return out -from math import sqrt -from collections import Counter -from typing import Set, Dict, Iterable, Tuple, List +def clamp_byte(x: float) -> int: + return int(max(0, min(255, round(x)))) + def _nudge_if_reserved(nrgb, reserved_set, orig_rgb): if nrgb in reserved_set: # Nudge 1 toward original to avoid exact collision - r0,g0,b0 = orig_rgb; r,g,b = nrgb - def nudge(c, c0): + r0, g0, b0 = orig_rgb + r, g, b = nrgb + + def nudge(c, c0): return clamp_byte(c - 1 if c > c0 else c + 1 if c < c0 else c) + return (nudge(r, r0), nudge(g, g0), nudge(b, b0)) return nrgb + def _rgb_of_index(pal: List[int], idx: int) -> Tuple[int, int, int]: - return pal[3*idx], pal[3*idx+1], pal[3*idx+2] + return pal[3 * idx], pal[3 * idx + 1], pal[3 * idx + 2] + + +def _color_dist2(a: Tuple[int, int, int], b: Tuple[int, int, int]) -> float: + dr, dg, db = a[0] - b[0], a[1] - b[1], a[2] - b[2] + return dr * dr + dg * dg + db * db -def _color_dist2(a: Tuple[int,int,int], b: Tuple[int,int,int]) -> float: - dr, dg, db = a[0]-b[0], a[1]-b[1], a[2]-b[2] - return dr*dr + dg*dg + db*db def _index_pixel_counts(im: Image.Image) -> Dict[int, int]: - colors = im.getcolors(maxcolors=256*256) or [] + colors = im.getcolors(maxcolors=256 * 256) or [] return {int(idx): int(cnt) for (cnt, idx) in colors} + def _indices_used_in_coords( im: Image.Image, ranges: Sequence[Tuple[Tuple[int, int], Tuple[int, int]]], @@ -110,14 +120,15 @@ def _indices_used_in_coords( w, h = im.size px = im.load() s: Set[int] = set() - for (x1,y1), (x2,y2) in ranges: + for (x1, y1), (x2, y2) in ranges: x_start, x_end = (x1, x2) if x1 <= x2 else (x2, x1) y_start, y_end = (y1, y2) if y1 <= y2 else (y2, y1) - for y in range(max(0,y_start), min(h, y_end+1)): - for x in range(max(0,x_start), min(w, x_end+1)): - s.add(int(px[x,y])) + for y in range(max(0, y_start), min(h, y_end + 1)): + for x in range(max(0, x_start), min(w, x_end + 1)): + s.add(int(px[x, y])) return s + def _find_nearest_index( pal: List[int], src_idx: int, @@ -135,13 +146,25 @@ def _find_nearest_index( best = int(j) return best + +def _used_palette_indices(im: Image.Image) -> Set[int]: + """ + Return the set of palette indices actually used by the image. + """ + colors = im.getcolors(maxcolors=256 * 256) or [] + used: Set[int] = set() + for _, idx in colors: + used.add(int(idx)) + return used + + def _free_palette_slots( im: Image.Image, pal: List[int], needed: int, *, - protected_source_indices: Set[int], # indices that appear inside protected coords - reserved_by_color_rgbs: Set[Tuple[int,int,int]], # magenta/green/light-keys as RGB + protected_source_indices: Set[int], # indices that appear inside protected coords + reserved_by_color_rgbs: Set[Tuple[int, int, int]], # magenta/green/light-keys as RGB ) -> int: """ Try to free up to `needed` palette slots by remapping the *least-used* indices @@ -160,7 +183,7 @@ def _free_palette_slots( reserved_by_color_indices: Set[int] = set() for i in range(256): if i in used_indices: - rgb = (pal[3*i], pal[3*i+1], pal[3*i+2]) + rgb = (pal[3 * i], pal[3 * i + 1], pal[3 * i + 2]) if rgb in reserved_by_color_rgbs: reserved_by_color_indices.add(i) @@ -169,7 +192,6 @@ def _free_palette_slots( # Candidates we are allowed to merge away candidates = [i for i in used_indices if i not in forbidden] - # If nothing to do, bail if not candidates: return 0 @@ -177,16 +199,14 @@ def _free_palette_slots( candidates.sort(key=lambda i: counts.get(i, 0)) freed = 0 - # Allowed targets for merging: any used index that is not the candidate itself - # and not forbidden. Build once and update as we go. allowed_targets = set(used_indices) - forbidden for src_idx in candidates: if freed >= needed: break if src_idx not in used_indices: - continue # may have been merged already - # Targets exclude this src + continue + options = allowed_targets - {src_idx} if not options: continue @@ -198,10 +218,9 @@ def _free_palette_slots( # Remap all pixels from src_idx -> tgt for y in range(h): for x in range(w): - if px[x,y] == src_idx: - px[x,y] = tgt + if px[x, y] == src_idx: + px[x, y] = tgt - # Update usage bookkeeping used_indices.discard(src_idx) allowed_targets.discard(src_idx) freed += 1 @@ -209,10 +228,6 @@ def _free_palette_slots( return freed -# --------------------------- Helpers for protected coords --------------------------- -from typing import Set, Dict -from collections import Counter - def _sample_neighbor_index(px, x: int, y: int, w: int, h: int, r: int) -> int | None: """ Return the most common palette index in the (2r+1)x(2r+1) neighborhood @@ -227,48 +242,15 @@ def _sample_neighbor_index(px, x: int, y: int, w: int, h: int, r: int) -> int | for xx in xs: if xx == x and yy == y: continue - counts[ int(px[xx, yy]) ] += 1 + counts[int(px[xx, yy])] += 1 return counts.most_common(1)[0][0] if counts else None -def _try_allocate_duplicate_index(pal: List[int], used: set[int]) -> int | None: - """ - Return an unused index if available, else None. - (Non-raising version to enable graceful fallback.) - """ - for i in range(256): - if i not in used: - used.add(i) - return i - return None - - -def _used_palette_indices(im: Image.Image) -> Set[int]: - """ - Return the set of palette indices actually used by the image. - """ - # getcolors may return None if there are too many colors; use a large maxcolors - colors = im.getcolors(maxcolors=256*256) or [] - used: Set[int] = set() - for count, idx in colors: - used.add(int(idx)) - return used - -def _allocate_duplicate_index(pal: List[int], used: Set[int]) -> int: - """ - Find an unused palette index (0..255). Raises RuntimeError if none available. - """ - for i in range(256): - if i not in used: - used.add(i) - return i - raise RuntimeError("No free palette indices available to duplicate protected pixels.") - def _protect_exact_pixels_by_index( im: Image.Image, pal: List[int], ranges: Sequence[Tuple[Tuple[int, int], Tuple[int, int]]], - reserved_by_color_rgbs: Set[Tuple[int,int,int]] = set(), + reserved_by_color_rgbs: Set[Tuple[int, int, int]] = set(), ) -> Set[int]: """ Ensure protected coords keep their original (noon) colors by duplicating indices. @@ -280,18 +262,17 @@ def _protect_exact_pixels_by_index( w, h = im.size px = im.load() - # 1) Determine which *source* indices appear inside protected coords protected_src_indices = _indices_used_in_coords(im, ranges) - # 2) See how many free slots we need (one per distinct source index) used_now = _used_palette_indices(im) free_now = [i for i in range(256) if i not in used_now] need = max(0, len(protected_src_indices) - len(free_now)) if need > 0: - # Try to free `need` slots by merging least-used, non-protected, non-reserved indices _ = _free_palette_slots( - im, pal, need, + im, + pal, + need, protected_source_indices=protected_src_indices, reserved_by_color_rgbs=reserved_by_color_rgbs, ) @@ -305,31 +286,31 @@ def _protect_exact_pixels_by_index( radius = int(PROTECTED_FALLBACK_NEIGHBOR_RADIUS) - # 3) Walk through protected coords; duplicate on first encounter of a src index + # Duplicate on first encounter of a src index for (x1, y1), (x2, y2) in ranges: x_start, x_end = (x1, x2) if x1 <= x2 else (x2, x1) y_start, y_end = (y1, y2) if y1 <= y2 else (y2, y1) for y in range(y_start, y_end + 1): - if not (0 <= y < h): continue + if not (0 <= y < h): + continue for x in range(x_start, x_end + 1): - if not (0 <= x < w): continue + if not (0 <= x < w): + continue orig_idx = int(px[x, y]) - # Reuse existing duplicate if we made one for this orig_idx dup = index_map.get(orig_idx) if dup is not None: px[x, y] = dup continue - # Allocate a fresh duplicate slot if available if free_pool: dup_idx = free_pool.pop(0) - r, g, b = pal[3*orig_idx:3*orig_idx+3] - pal[3*dup_idx+0] = r - pal[3*dup_idx+1] = g - pal[3*dup_idx+2] = b + r, g, b = pal[3 * orig_idx: 3 * orig_idx + 3] + pal[3 * dup_idx + 0] = r + pal[3 * dup_idx + 1] = g + pal[3 * dup_idx + 2] = b index_map[orig_idx] = dup_idx reserved_indices.add(dup_idx) px[x, y] = dup_idx @@ -345,8 +326,6 @@ def _protect_exact_pixels_by_index( return reserved_indices - - # --------------------------- CLI & helpers --------------------------- def parse_rgb(s: str) -> Tuple[int, int, int]: @@ -355,7 +334,7 @@ def parse_rgb(s: str) -> Tuple[int, int, int]: s = s[1:] if len(s) != 6: raise ValueError(f"Bad hex color: #{s}") - return tuple(int(s[i:i+2], 16) for i in (0, 2, 4)) # type: ignore + return tuple(int(s[i:i + 2], 16) for i in (0, 2, 4)) # type: ignore if "," in s: parts = [p.strip() for p in s.split(",")] if len(parts) != 3: @@ -384,10 +363,6 @@ def time_labels(step_minutes: int) -> List[str]: return labels -def clamp_byte(x: float) -> int: - return int(max(0, min(255, round(x)))) - - # --------------------------- TONEMAP MODEL --------------------------- def _gauss(x: float, mu: float, sigma: float) -> float: @@ -441,8 +416,6 @@ def hour_adjustments( gray_blend = min(max(gray_blend, 0.0), 0.85) # Night-only blue push factor (extra hue shift at night) - # - grows with (blue_scale - 1) and with 'night' - # - kept separate from b_mul so daytime remains unchanged blue_push = max(0.0, blue_scale - 1.0) * night # 0 at day, up to (blue-1) at night # Clamp gentle bounds @@ -487,13 +460,11 @@ def _apply_night_blue_push( - Slightly reduces R and G - Increases B 'blue_push' is ~ (blue_scale - 1) * night, so this is zero in daytime. - Coefficients tuned to be visible but not cartoonish; adjust if needed. """ if blue_push <= 0.0: return rgb r, g, b = rgb - c = blue_push # c in [0..(blue-1)] scaled by night - # Gentle but noticeable: at c=1 (e.g., --blue 2.0 at full night) + c = blue_push r *= (1.0 - 0.15 * c) g *= (1.0 - 0.10 * c) b *= (1.0 + 0.35 * c) @@ -549,22 +520,18 @@ def _interval_membership(x: float, a: float, b: float, soft: float) -> float: soft = max(0.0, soft) def segment_membership(x: float, s: float, e: float, soft: float) -> float: - # Non-wrapped segment s <= e in [0,24] if soft <= 0.0: return 1.0 if (s <= x <= e) else 0.0 - # Left ramp [s-soft, s] if s - soft <= x < s: - t = (x - (s - soft)) / soft # 0..1 + t = (x - (s - soft)) / soft return _smoothstep01(t) - # Core [s, e] if s <= x <= e: return 1.0 - # Right ramp [e, e+soft] if e < x <= e + soft: - t = (x - e) / soft # 0..1 + t = (x - e) / soft return 1.0 - _smoothstep01(t) return 0.0 @@ -572,7 +539,6 @@ def segment_membership(x: float, s: float, e: float, soft: float) -> float: if a <= b: return segment_membership(x, a, b, soft) else: - # Wrapped interval: union of [a,24) and [0,b] m1 = segment_membership(x, a, 24.0, soft) m2 = segment_membership(x, 0.0, b, soft) return max(m1, m2) @@ -585,7 +551,6 @@ def _noon_weight(hour_value: float, blend: float, sigma: float, - Gaussian around 12:00 with width 'sigma' (hours), scaled by 'blend'. - Smooth interval window [w_start, w_end] with soft edges 'w_soft', also scaled by 'blend'. - - The final weight is max of both components, clamped 0..1. """ try: h = hour_value % 24.0 @@ -600,14 +565,12 @@ def _noon_weight(hour_value: float, blend: float, sigma: float, if blend <= 0.0: return 0.0 - # Gaussian around noon from math import exp d = abs(h - 12.0) d = min(d, 24.0 - d) g = exp(-0.5 * (d / sigma) ** 2) if sigma > 0.0 else 0.0 g *= blend - # Smooth window window_m = _interval_membership(h, w_start, w_end, w_soft) * blend w = max(g, window_m) @@ -638,12 +601,55 @@ def set_palette(img: Image.Image, pal: Sequence[int]) -> None: def find_color_index(pal: Sequence[int], color: Tuple[int, int, int]) -> int: cr, cg, cb = color for i in range(256): - r, g, b = pal[3*i:3*i+3] + r, g, b = pal[3 * i: 3 * i + 3] if r == cr and g == cg and b == cb: return i return -1 +# --------------------------- Green removal (NEW) --------------------------- + +def remap_all_green_to_magenta_and_blacken_palette( + im: Image.Image, + pal: List[int], + *, + green: Tuple[int, int, int] = (0, 255, 0), + magenta: Tuple[int, int, int] = (255, 0, 255), + black: Tuple[int, int, int] = (0, 0, 0), +) -> Image.Image: + """ + 1) Remap pixels from ANY palette index that is exactly green -> magenta index. + 2) Replace ANY palette entry that is exactly green with black. + This removes #00FF00 from the palette entirely. + """ + magenta_idx = find_color_index(pal, magenta) + if magenta_idx < 0: + return im # can't remap without magenta entry + + green_indices: List[int] = [] + for i in range(256): + r, g, b = pal[3 * i: 3 * i + 3] + if (r, g, b) == green: + green_indices.append(i) + + if not green_indices: + return im + + # Remap pixels: any green index -> magenta index + lut = list(range(256)) + for gi in green_indices: + lut[gi] = magenta_idx + im = im.point(lut, mode="P") + + # Replace those palette entries with black + for gi in green_indices: + pal[3 * gi + 0] = black[0] + pal[3 * gi + 1] = black[1] + pal[3 * gi + 2] = black[2] + + return im + + # --------------------------- Core operations --------------------------- def adjust_palette_for_time( @@ -682,34 +688,31 @@ def adjust_palette_for_time( reserved_color_set = set(reserved_colors) reserved_index_set = set(int(i) for i in reserved_indices) - # Noon weight (0..1): stronger near 10:00–14:00, peak at 12:00 noon_w = _noon_weight( hour_value, noon_blend, noon_sigma, noon_window_start, noon_window_end, noon_window_soft ) - # Damp sat/contrast near noon to avoid “pop” sat_eff = 1.0 + (sat_boost - 1.0) * (1.0 - noon_w) contrast_eff = 1.0 + (contrast - 1.0) * (1.0 - noon_w) out = pal[:] # copy for i in range(256): - r, g, b = pal[3 * i:3 * i + 3] + r, g, b = pal[3 * i: 3 * i + 3] # Skip EXACT indices first (highest priority) if i in reserved_index_set: - out[3*i+0], out[3*i+1], out[3*i+2] = r, g, b + out[3 * i + 0], out[3 * i + 1], out[3 * i + 2] = r, g, b continue - # Then skip by color (magenta/green/light-keys) + # Then skip by color (magenta + user light-keys) if (r, g, b) in reserved_color_set: - out[3*i+0], out[3*i+1], out[3*i+2] = r, g, b + out[3 * i + 0], out[3 * i + 1], out[3 * i + 2] = r, g, b continue nr, ng, nb = tint_rgb((r, g, b), params, sat_boost=sat_eff, contrast=contrast_eff) if noon_w > 0.0: - # Blend back toward the original (noon) palette color at the SAME index nr = int(round((1.0 - noon_w) * nr + noon_w * r)) ng = int(round((1.0 - noon_w) * ng + noon_w * g)) nb = int(round((1.0 - noon_w) * nb + noon_w * b)) @@ -723,15 +726,6 @@ def adjust_palette_for_time( return out -def remap_green_to_magenta_indices(img: Image.Image, pal: Sequence[int]) -> Image.Image: - green_idx = find_color_index(pal, (0, 255, 0)) - magenta_idx = find_color_index(pal, (255, 0, 255)) - if green_idx < 0 or magenta_idx < 0 or green_idx == magenta_idx: - return img - lut = [magenta_idx if i == green_idx else i for i in range(256)] - return img.point(lut, mode="P") - - # --------------------------- File ops --------------------------- def copy_noon_to_label(noon_dir: Path, label_dir: Path) -> List[Path]: @@ -789,9 +783,9 @@ def process_time_label( im = im.convert("P") pal = get_palette(im) - # Optional green→magenta index remap first + # Optional green→magenta pixel remap AND remove green from palette (set to black) if do_index_remap: - im = remap_green_to_magenta_indices(im, pal) + im = remap_all_green_to_magenta_and_blacken_palette(im, pal) effective_reserved_colors: List[Tuple[int, int, int]] = list(reserved_colors) reserved_by_color_rgbs = set(effective_reserved_colors) @@ -809,7 +803,7 @@ def process_time_label( new_pal = adjust_palette_for_time( pal, hour_value, effective_reserved_colors, - reserved_indices=reserved_idx, # <-- NEW + reserved_indices=reserved_idx, warmth_scale=warmth_scale, blue_scale=blue_scale, darkness_scale=darkness_scale, @@ -830,7 +824,6 @@ def process_time_label( im.save(pcx_path, format="PCX") - # --------------------------- Main --------------------------- def main(): @@ -869,7 +862,7 @@ def main(): p.add_argument("--twilight-width", type=float, default=1.8, help="Sigma for sunrise/sunset warmth spread (higher = broader).") - # Noon-neutral zone controls (defaults keep ~10:00–14:00 close to noon) + # Noon-neutral zone controls p.add_argument("--noon-blend", type=float, default=0.85, help="0..1 strength to blend toward base palette near 12:00 (0=off).") p.add_argument("--noon-sigma", type=float, default=1.1, @@ -884,9 +877,9 @@ def main(): # Index remap control g = p.add_mutually_exclusive_group() g.add_argument("--keep-green-index", action="store_true", - help="Do NOT remap green index to magenta; leave pixel indices unchanged.") + help="Do NOT remap green pixels to magenta; also do NOT remove #00FF00 from the palette.") g.add_argument("--map-green-index", dest="map_green_index", action="store_true", - help="Force remap green index to magenta (default behavior).") + help="Force remap green pixels to magenta and replace any #00FF00 palette entries with black (default behavior).") args = p.parse_args() @@ -895,15 +888,16 @@ def main(): if not noon_dir.is_dir(): raise SystemExit(f"Noon folder not found: {noon_dir}") - # Reserved colors (Magenta + Green + user light-keys) - reserved: List[Tuple[int, int, int]] = [(255, 0, 255), (0, 255, 0)] + # Reserved colors: + # - Magenta is kept stable + # - Green is NOT reserved anymore because we remove it from the palette when remapping is enabled + reserved: List[Tuple[int, int, int]] = [(255, 0, 255)] for s in args.light_key: reserved.append(parse_rgb(s)) # Determine remap behavior (default ON) do_index_remap = not args.keep_green_index or args.map_green_index - # Build time labels for the given step, then exclude noon folder itself labels = [lbl for lbl in time_labels(args.step_minutes) if lbl != args.noon] # only-hour (accept 0000 -> 2400) @@ -919,7 +913,7 @@ def main(): print(f"Base: {base_dir}") print(f"Noon source: {noon_dir}") print(f"Time step: {args.step_minutes} minutes") - print(f"Index remap green→magenta: {'ON' if do_index_remap else 'OFF'}") + print(f"Green pixels→magenta + remove #00FF00 from palette: {'ON' if do_index_remap else 'OFF'}") print(f"Generating labels: {', '.join(labels)}") for lbl in labels: @@ -947,4 +941,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/Notes/district_todos.md b/Notes/district_todos.md index e9f048d3..4a01be44 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -2,7 +2,6 @@ - Municipal District (ZergMazter doing art) - Central Rail Hub (ZergMazter doing art) - Light annotations - - Offshore Extraction Zone - Data Center - Great Wall - Water Park From a5ced756ac5b91c226b1e14f23d90de45b2d5677 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 3 Feb 2026 10:58:56 -0800 Subject: [PATCH 351/356] Add Great Wall, updated Canal light annotations --- Art/Districts/Annotations/Canal_lights.PCX | Bin 115814 -> 115873 bytes .../Annotations/GreatWall_lights.pcx | Bin 20994 -> 20989 bytes Notes/district_todos.md | 2 -- 3 files changed, 2 deletions(-) diff --git a/Art/Districts/Annotations/Canal_lights.PCX b/Art/Districts/Annotations/Canal_lights.PCX index 71c101935f74e971ff58260d4130bb256705ab75..0dd85820f45203ad8d2685379bf51efa60e6cf51 100644 GIT binary patch delta 82 zcmaFX!M?DQeS-(n_9sD%@d=ZUUkaQ)Wiw;u=7!5LjN7BPGIH^3mkDH4Pr@bRSpbrs QQ^fpjV%+rIhZ$D`0K&u~!Tz9S8O&4kqOw^4FHN~i2xQRJeYpgj zm>u~XoOCVH0=%3h`YG(EEv<6|UYVI3w9r8Z?|T5Eu>D~v_d~I2ew5nw8Hnhnah4l$ z_+eWN1(CoVEbp|!61o$=1g0^j2`4H!ozoNpcri1{2;b;<(iyVH&Y!i+rl;4Le}UWl zx=t_W`sCEmfZo#G2C#^lo*432hvCN?iLQ(;^vHme6`HkCP{Cs?x5n^7x%w9Fr}YIy zpG6J5Y5*4w^c^4a)V7?XJb)wApTC!nhAIo<`A}@0M3@wKdvQf;{3~ONw+W!eTGt2>Wpt%P1c80M zg2Sbe0(<|&HrNX_auShgP8tR!v4>GZd5~mtJ2tmtb2~O*$EodO>@}O<=5hGg3w+s` z_jj6v?VeyW>@63+VLmp`ZH!GZ9DbJn8#9Ohj2U?bbKV-A<#&Bt?-OU(#OTzy0D%6C z9$e_Dkc%F!o^gC|;6Yvezd3}4-hQkWq^Rw?_8)O%4yF*`5w4V+x6SLIXhSGnDz(*6 PsN5m=zi%-wJD}?qw$$xt diff --git a/Art/Districts/Annotations/GreatWall_lights.pcx b/Art/Districts/Annotations/GreatWall_lights.pcx index 348dcca777337951a32554bf1efc4c3a462c0c38..28e5490b40ff28e3bc01f85f0a5279d058880609 100644 GIT binary patch delta 602 zcmY*WTWAwO6m@o+)Yh10H@2y?8`la|43wBSD$#{hOBJjZQ_vr&{RoIs5ClaKloV71 z3+ntzX-U+gDMUvwf+fAPh+wfoG$;rrJ}7~eQp5)hy6a3o1b^ntxpVHhj~U6JKWUUx zQ47OUFbnS=^}r`sjJI#z9qMK{K`N+|9z-KZVwe=A2?1y1F#*kWRSpkbcac2K={@(7 zh~BAx!GWG$xlg3frwx)MF^j^gQ)oTIx4si5pM4EmHNh-F59Q6%I z%p}xj^2+3#k`xagl%N1dX_FZSqNxFnCMhfHc=)EOAxzGyX(1^aI^F4i!`nBhzd4Nl z*XfweOjqr1e2Q29&Z^xRjmLH!XkwUgOI_F?wao>;MXk>*!wPOxTbZ&6%xf^m+pS?b z6gZB&SoXC0JaL%MXD{_)T56rHQy4BfHQRBX^YgW{mM<}_75EY{N$TAk=%hY?6ddW> zzDGL(mA;M9-VF&J_H-BxLbX36Mxw)^cgsn8yW)ag?fT9qiqMPUpzLQj4~sC%#x565 zbPR@r+qfVXz3+A{{}4GA6BE!>Op}G-xM2udHfruQ3>R+0M>g>UW==+;Y>D47UFe~W U^3NNCG}B)}vi)NPxpL_IUn9L38~^|S delta 598 zcmZ9JUr3W-7{z(tZ}~6X+}75NZ4L#EhN4@`n0+`mv8ZV{$&6JrkgN!b!ix+OM|EKh zq~~_5nTrfHj5l%_HlNQTNG7zhg5As@DHbJB$cF7}MnrUTu72n6Jm>rzA>I&~R1kGZ z-2~2K{6Q1G#}%7)o88#hIp~4}sbHd?oSRVb9w(X?2`iJgOHF~H%(y_lT+jC%wpIV z8XCu_0JXAJ+OQY;thL}SZkOO3zJbZ&IXV=99(flht9lAHbt&yvfk&+?SrcYwzGarP zm&E_-*G!A73L4agKM+}ibBYmaS3;aOusY>y@;j9kscH!QpX^s_$-id0CO|@cTE3w` zp%LU;aqc=UU=q^~c4TumkrJ*@TU=b}vOcSh zqmEnP=lSgWEY7EMR240e+qfhQKf>8lCbN*jB@|rjQxhxTdfRrfoBI=l8;y2@x9#FD DZqW+? diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 4a01be44..4824ca9c 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -2,8 +2,6 @@ - Municipal District (ZergMazter doing art) - Central Rail Hub (ZergMazter doing art) - Light annotations - - Data Center - - Great Wall - Water Park - Holy Site (mideast) - Commercial Hub (modern, left) From 26d070a4b6aa76d4c8920e9d996623051571ba2e Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 3 Feb 2026 11:10:04 -0800 Subject: [PATCH 352/356] Add Water Park, updated Holy Site mideast light annotations --- .../Annotations/HolySite_MIDEAST_lights.PCX | Bin 13242 -> 13147 bytes .../Annotations/WaterPark_lights.pcx | Bin 7979 -> 7974 bytes Notes/district_todos.md | 2 -- 3 files changed, 2 deletions(-) diff --git a/Art/Districts/Annotations/HolySite_MIDEAST_lights.PCX b/Art/Districts/Annotations/HolySite_MIDEAST_lights.PCX index 4d9edae86ae9dda8f0067c997cf8fc072c6b4f23..18dc89e7e2d1983433e4ee38f61d5f001e121376 100644 GIT binary patch delta 1568 zcmXw(e@qi+7{~2Pttj-$2^B`6MQ97iptMX>O6%TGoa4Yvhs>?c>D(miESo!$COF+P zFN@1!WInEme=KgfEdh+irDn}s;_|-NvMf5RokcFYK|R7a%#b;?bfZRJT0DpgW6j7P8-TwRwc1>OpT?B3*jHF z3HsK4!X;EmaQ>ja8&^lu`U`MSR|&gyH*5Y-vFM@}qLNjT(zX)SMp76kgFgMxnyZX_ z^fnnKcgZ~bq;tWsbrIWdDhfX1MI1?xWIb8cdA$4@@>cZq1{x*vu|+Zo^XqoN z$=q+Ne^nLa_8SlAp3{7lS8l4O-GyGcqzlb42~lHYwL zvY2Pp*k@HeZ!#k67s(P&Rv48c37T0y#bOdUBqaw;S71BNCA!|;U-B&CIS@G1Vti`;BrA1yuin7(+re@R4^eNa0DD=*^{fuW|<-@ zSi<{}M3Ftxx^aW1YLcN%gEI9?PA1G@&fpX@ zHaJ*tP{uN5^)Mly+#~xuLT8WBN2M_CEGlU0YkQ;Z{KcV>&{{IBEHZ@!rZGdtM!{RM zvQzKif+9mRN%$%5Kkh2=$gc2yugJTK%|&7$>?|x8Q1*Q`GCUFuRc9-iCyR6@D{>c( zKeps{C}L2&EM<#0g9pRTk6csGHNVDk-0L4#?l@ta?BU9BDEl{iTbF}ZrVj{ZqGK7 zG)RJvqTP`KvtDCW(xm}ErVPX!Dr;ib-gywN4w1(Tj0E{84lWS7yiX0%yqqo`g*r?m^*qm(ewv!-Tnl;SsjN{HLc*N zS>SiJG=kL7 hVHjcl&K!|RhOiL$yrBY?8oD%dz&k1;M;*^={{?SL@~i*= delta 1484 zcmYL|U2GIp6vxxMOKqV`DZ97QPP?-oZ7I+WFcK4ES;W0yTm}`~8DC7JEJ%%^q|g@^ z@QIiPA|#%6YK_R6#-v4J)jkluxp#I;!658XTMd;ZV!{Kdh8Pp0aMv?pVrK44GWnhV z{hf0&x86Fl^Q-AH?~GKg!%W$F@03)oK?B)PUXVl~SD}MMp^xa5f60L<$;iMYTp&G_ ze@bOa4pU%Ktp12}yYGmPorv-lX9H{wSp2R}Q%t<+F)6@Wm z9&e`MfjmKv<|OBQn$I5R*F=FAb8<`<@va=qd7EI}ybnUZA!KAFpX1YFmfzq7o_D!# z94Uw?hy%XzX1Fx33M$IKY`TLVsUYG%TohfL7qO&xew>#^9D-ZN%G=;!`Q^sja?Bye zxD<1_S)^v1JkmvvC|N0m!+TF`fN;gxmRWg%3G*ZVf20dzB%cu&i!`JBE_7C`fQuE4 zu)eaO{U}FjI?N2pK9(+uS@9j8z!KM{z-L8Cr%8V2)ab|;yg0sUez>gZ2Wh2-_}l?8 zhh4hbZl}|am9TRxP~L~^{1wo?pb5?{xD}p}78@i7+)XgPi0>l$xZy=&>D26mDqlPF zRz+Z*?{;Kb>U2;LWZfwi`J98T;toa3COV@ghnIaCT=GR>)57WK_wpoNZMpovQ%8yy zodR}C48aUD$nSDET-^qxg%Rkh&Ntqa-Wc7_EQe%W7tD3YHcPX%C$ts^u2r|d%0+E3 zxkv;H^4YaD)MS>9uq*Bm!%A75!XMSVIapV-8UC)h(2$pcH7kD0X0~oj5KA#_Q`JN# zwP+T0Eq)1F{ojW2vY%Notz$16){Wuq#wZo0K~stv^;m+Y960W`peJxH#7nxxgcW<< z-z#Ev3_&%JibeeaMPNx%z}JCBC=;}zj=f|5=EqKdowqP=JvvY9H6Jj2fzk#tq9c@ozBGFru`AtZ#?UmD9~m>BTXGEoM8+(QP~k z8W0%nx5+TVG;St$F9%=47P#4Pwn>zR4p}C>Q2Pd>T&^}hE_s(I1K&a zQ}|yD4rTh+ZbHyyncGt`kTiFTbRsRfYjbcTyaMdVlyXZtnf^Cj2Zt$TG*0m-;@ydK z_r@&Tk7$sLE`cl2OZB(q&_HHv9|~By*D?1c;)!IpwlP<#9RIATvMlhkJo@oT9dk@W zPsRlrO_atjHj}cNds1XR)Y)DWHM@fRm&@=%%hOP?G`HZs%%nw^=!4FJ4;gTS_+?Sjp(HmaavR&%H1pLk4VYYbvlZER$quW^~w1w?Tmz zL!yZZy-i3>mPTM=fDkOP29{0Ftr3@~%i>4;;bMk5CDAGIBbhPND?jd&bI*C6b8g9qnqIL+?ocgIM)MYd*`%&#{*mari2+1;SC&rETYAtHUeO@}=lKFJ;s#g1u@=fFO)zH~hm zS(*l0))olyN80)d5MA;6ZoD>6_xXc`{&Zvj-Qme(JiOk!e*&}VZ=61&8Om_TCh~k3l>-%k`dLfl)FGT zTVnL_0TY+Onxx%o^el3*)u*My8D_R9RS5@=3CBIDR5T{#))(My&-0c*1$R8w&Ml>N zLn;KsxWG>3DuQt(*Si}tEwr&%YEj|}xZ+fBv`>ZJ9n#hgsm!3L!H;tVt7i??jC@4- zYznho%~;_xpj&e%8ZUsoVVo=kkEXv_;a@F8yQ2I9y1B-G=AXUJ!yzYFL*BS3cs*~v z5UpB0X8w<(Z<;pN#f5xf;UUE!`W<`ZU@dBw`pQOiVBB=N~x z<2k$GxO5+s~36WGv`5DD^G?e+Zs>2 z*j*}kV)BFLhayQZKP+RIK1u&Zx9Au8@_eO12`gY*`Gc9=U|kj_NOvvQREQm7iGr8J e&uX=2m{;qp+bY#xQ>U2QR;zmWCsso?%KQ&|Tw16A delta 980 zcmW;KT}+#G6b5krecRIS))v|qA#^mfY^9X8Df9#O;s9k`VFT76?aHuBl38bDwfh7* zh%>}^1*el#qc~t=4r~y#1{QJWUkzL0vJf@VgvE?4Q!bpMyBQa1}At_AokB~|Do4-mpSQNaNM_7AcKJA8o1-sM*_&x5>C-?w=%H6Mc=VfpR zn{q@Jr0EC@2~9lQfEnRSHN;Em(w)>5s4E=6djy}PaeH!DS?aRmHX{Co>*aP;BtWZ6 zQj4Ay>#^3(GS=h=Ti)wL3?Jp4%g^@s**)Rr_+m=gGmd{GV=MSGREUbjs$KqotTdbs zK%1yTT2?x^GXGU3F+cGdd@9lr8GI|Mbr8f4I|Hll&f;VG^m=zT(bLryA6-nvSAta| znd$5=;~8h~$v4WZCL2+P&j$=VaEPr$1QBsLVc{Kdsf22pD4|0wh7f%IfAm1SSIOSM z^|UZGM)g!b@EKT;G>F8s|+BvYzfZ&zAy18+Ab@z!2-Rs)>uIbpDCjxHC$QP}}vg->MF zLefQ(%M3h}83`XG@><>eHCw+n?4YI!AJrPYeGD%wK$JsR&JhA1$n8aVbE*;7=8Re{ z#dT25%;^t>7+x@^YT!{$FznZ# zOQSVIueY?~Q`|Z`ail~?&0NlS)SoRoy!MDY!lPhP4;IU(aSQKv505%E?M{VxEUM^d zOMJ4>?jdm{tf~*>kqgHwT|qu5H5T1;9Dm1K{nxJc>Z5V9uCOBJbzD+ephMG~M`Fj> z3s(&AvBoKH)6=NgWbnlteY6VhYa+!bsL^-j9Ib!l)BtWR?2nH&*B?I9X3RNF1su)) zhd80TAfAuwG5jw3C*IBV{CLW#;No06JS^zt(Kgtx1u_F0+8c}@nGf&l&XRJ_u%+bE YQZ>8HFanhe_7r=VM!S2duNZm%0XI5&KL7v# diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 4824ca9c..102bb243 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -2,8 +2,6 @@ - Municipal District (ZergMazter doing art) - Central Rail Hub (ZergMazter doing art) - Light annotations - - Water Park - - Holy Site (mideast) - Commercial Hub (modern, left) - Municipal District - Central Rail Hub From 98d002558b723fd956eaa11be963472bc89487fe Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Tue, 3 Feb 2026 11:37:34 -0800 Subject: [PATCH 353/356] Update Commercial Hub light annotations for Shibuya like area and inconsistencies --- .../Annotations/CommercialHub_AMER_lights.PCX | Bin 51788 -> 50726 bytes .../CommercialHub_ASIAN_lights.PCX | Bin 48567 -> 47682 bytes .../Annotations/CommercialHub_EURO_lights.PCX | Bin 46485 -> 45525 bytes .../CommercialHub_MIDEAST_lights.PCX | Bin 45674 -> 44844 bytes .../CommercialHub_ROMAN_lights.PCX | Bin 48769 -> 47671 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/Annotations/CommercialHub_AMER_lights.PCX b/Art/Districts/Annotations/CommercialHub_AMER_lights.PCX index 6ce56ebc154b4e66576a050094ed6d2429079a0d..fe5c9edd8c282ed25c1a4a1597163debadb4229c 100644 GIT binary patch delta 3342 zcmb_edrTB(5Wl?xgldY~1`#-3qShKJ1TAeb6s|}uG}_eu!D)>)+8R@>RTQmOe9&&x zsKt!Z(gUB|9*>i|6OUrjn9Nd@6F4{^cpSAV0xORwT8lMJb~}rPHiU-?f84k8&HQ$L zGxNoC(zx2dw7sKV@|iMZ3$Sa%!yfz-Pnar>^4ba=MZYxxjB-#1GBaSs3fud zf6q8ZfL&Wcn6~~k2qvVxLHo?KFy?mu8VdK)BA8ncvvoRiqkjzn*VYB(u>oqf&4%J_ zUL@BIkXh2zLh$zau;`m{B-;nb$NF?%Z#gYIyRc5r zS>fI9*88;&YxW=7%tO!jF5fo!bpz*#f(NVBQM=daS?uLmSiyejd;0~AiSWTbonM=X z)e087ShVZNCpNmX_&W(k7Bcs*^SeC?^FmmCKV%o6!03WMTa}OIHJH_7LFYF z%CF^x&G83YeOkm6US~xcr)P`FHTmUPeHkPk+Te5RxlLajZkc*hj7A|^Lx)(8HkBT) zx$~?JohA)rW+ubJ%o(FZbWXi4qHPTdZW(-v23@7pFwPhgR`Cireq_@qtV!@H98))w zceJCBl7Rx}j>1P&b{kg7&FNCWqNA(E0FeMb$Fc&Nh$#_kv7VSxi0NY#bPBp=9lXK{ zH?!UY@2r_)0&)a4()2-F#-6kD=Jm|93L6y>2;TnA}J>laMxlbv#QWU7?&{b}PSl#CV)skHt z4L}2dxj(9}5Jd_Kr{&i8#2{CZSW1m_Tn%gj^c;_a{kgA&)JRvwu|qZm*oZZ4bDo9x z>hDUnyyF(QZHo3W;)abP`QDr>8M|9S)z`7*tQ6 zBBRG7f#poLn*6Xd6|Cq0naV{I85>GySX5CLxMH^7O)^&>;VpsMI)SX>wxS|e49 zBYNzE%lyqnr>F8#s86iYQ(0(aRpl=2%Dy6WB(CAX|Lkn3 zo^N_`T=LtD2Uh6Z-3r~bt0>vaG}sl8ZVClcv1T|x6ku`0umBHANzSUobb1n_Vhq?@ zLzNzQmLlOdO9u#s`|gyrXhVEp;_2k2G~*v|(yYt96I?yOjR z=oOXVIV%ogt;;7}k=n$a1GK@AT^axxR^P#`CE2A(N+uA#En5yn7hWRi!wYNs*7r&t IUsy8nU#~w22mk;8 delta 4445 zcmcIneQZ=!7H3}RSk|r!8KxCF1v;HhV{xk8LZl9vsbvO7s%eS1u^3EiW1^d)NL#;l z37Xj*H7p;y2PoV3D#?9&UTusE zacyzTy!VoO&hOsex##QN3H>Q8u`?}VZQMM_18<+>?`w<)G(PYHgRl5ig5U>u8Iul7 zYWyTW=D-o>+oSx%_r=OI#FzQkJf^{>#!PR5A7fz_#}U@kGR)%|CL?&-ffr1k%&`{gae2zgHY16 zz${N&V}UJAwygK~9#loV!om?Ji{Nl1jH+c2B29T&>;_Z|!PHzlZ*ozUi8zA80Wy{r zMb$9?xy`nDlk47q)(YLtcUl;4)lJqPqyXVCs!mir`sgH#HW$qQDe649|5@*?-|$wZ zOp**&)55C>z-Y5>sL3ZTnvuq9?AZix5|ocdW`7{pGE~^qUG<<|J;h~D>tND z4i7$GGl#4~^|;8Y9ZV!&M0+9pym!v!DN5Zh?9UnJtwI&5^e`j#R!5v<+=P>RkHfYX zR_9#$NqQ%oda=&>AI@5~nk$U7Yjxr!id;4_>0vVaI9zBo0dS$rmVtGe>Qwu!6&@6ibXM2fr>Bei( zqmz=s$8?7aQICik!q}!&1^-Staw)9Z{HpaF-&2Eqcp)m%&xaQT>Y}GCI8vSKwD_Wy zTvTDh=EtmKDTFPD{FlK-#5YG#k;vJU_S6w^5LIKi8iy)879aHpiUjn(++ZW>lmhPs zyB}<$yd|4zS&`CY;`d-8*oFN#M6`C|rmCzU`qlm?Y$GWKu7KP>ii<{hYa0_Kk#H*+wE1eRjPL84SPj zl=b73!Op+>ue^^renzZjKOF*bx6Xp+P%UvFtCD1o1LIKp+WLH^4W|&d5|(a}tRK-) zSmGCFm_`3wop;7tT{R*OkO6VpdfZ2TuHEvqP1aHitc3fv?z1I1zDp?;tCH$PzZ)-z zmeamR)(qj;g5M9Z>O`jxjNyClC2l>cK0MLAH}tK&G$Wk zaY-O*{T{@qUlTJOm(bAKkU~^BEEWH1M=eSv(}!vx3)Q>pMHXWT!9^RUZY)hWf>L(_ ztP|H4^=rNS=Ym(j%GcY9WUUIvjbuxSeifpN0@Y4J4C2@eys95u3MK_e-`hML zc=PLJ1{vbkb^F>2WDWaVIMn4{Q&xmSevE|z7)uM*OVnV$*-ODuj^cSZu&@5M z59puMRMB^2jHPch$TI~7kM1v?GRgO*J#iqEkJ>YT_?0)Kiwd#RPt_m5&(oLTrP5qH zC5Y7bl`w_4|%M8+6n%n&{Ba3@Y$h~X`msvKWFFs$IHvsdeb{<%AoFnH^d`L0(U#p@SzcLY^}5#LI9t|MnjiAsh96=K z-s!Z$ryZ^wMf;HdFs4*^PXI%weJZfVHxAz7baJKl3}S>&?(FS!J1*xpvQWdo%02aH=~Wvb){aNh{!R_rmL> zXKr51E;Zp7?sDcWhmy!b7!BWhjjZ;X@p4E;q*?Nk%*9qOeksG|BlZ6?%7a^umR%>Y m^lU#`EEsNr9nk{#-QSi$PV^TOqZROE)NZ(`XGe7R?EeBx9(e%( diff --git a/Art/Districts/Annotations/CommercialHub_ASIAN_lights.PCX b/Art/Districts/Annotations/CommercialHub_ASIAN_lights.PCX index 19cba7850f749088feb01cec4a5fe0a5426b1d88..aab9c94c7790cbc924d3b2cf30c2381f96745a98 100644 GIT binary patch delta 3240 zcmb_eeN0nV6z{7%OQ((*ivk4%hf_h=1C%W$8jXwFqI{@}xG+ui50h<1Xa%7veoQPV z3T$WU*6GDsTad|u(QOG!rsq-$LTME@6F-}M7e_M;uIpj{Gs#iqgP?_ShC@elF5 zg!Y1O*-=LwJKQZc57E0G>B_$eT`(>CCGHNa&YsBq`e;3w9ml!hcD9=9e7pw3J2_Lh zp8<0wa~+S@FbLQ*o#{23!beHstwDP-*A4->QQVEkYXlt1oy%C?!TP*u5Rj)}a_bYa zj@d3iciyXPcMjU);Y)qWL))s&bJ*^HfA*PhY4ZyIKJg?vYWG}#`CAtF_dZqW-eMTn z!w|O#TWRe&ofAs8E*N)rSW7Hq<*yFx7IoC=htAs71!%9Z7U&v9l)$tt2ENIk5ZE;$ zbRRos*6Z}3@H1H})>RamsH;w-ki7l%z&rmLNr2=X>(y@YtPCIcp$px-3Ek*K2i5Tf zx{5jp-rtd;?(_+dht8dup>9#eQYfH}FJS&%COz6|=TU4xo1Q}2u30eW19fQU|3ESa zb$GlO(PkhSf|f@Iiy~k@72w#er9rp6fFaKWEzny~8QdXSWde2zlxP01UMb);kAMQ~ zHzdOL-C@DEd}5eGeiEtv;nR|0L5nR)Rty`9g8c&AFf0wZ=>t3)Ru*jxzA2_rUe79E zV_|Ap1m>MprJ({g7p1DH)b0bG0DXJ1f`1gTi(nU^(}YerHWVwVz@xno;J3Z0KA^Lp zv{)C^CZcmL(Wqz|u-vq`NXom;llHGlQ=&*E~s65ws}@{y@-a@wBX4 z2HQ&FVR6Ys9~qX3S6GB}vq?qLl;jP<%g~)gWqO!jnl>V6Hl&tisxPydf=*Qoo?pQp zSxHQUr!qEkJUx?HmK@e3`N~LyUp~wZY8C4|aY#ueU8W^gVlFtq8i)1%^B*OLG)p6g zUx8A{n^gbMIZa2~5|;fo!ny|CbqJ9kFCQT`3tm2uAJV}5rjdg@S!87CAxdmRWS!#@ z^5FA>NsxIk(r3ykHM>Bar`BMjmGB(FPNHQIuo4?$umSEKdfP`l3CJggNzD@Y@kVSR z=`263L|ezcV06_&ig707e45}R9sor~Ju4%ySx~IUmI*9~n`E#nq1|LrVJn<6E(yQH zem}fYqDJFoWfI2*`e)OLUp}FTeMUlJYtF5^U9vF(u!`&fl4@B z9tjSTGz=#eG-T>zm((PB_Z2H=A)Ct!Lv7NV*rHVk#B+Vf(R~)(z(PKtE|LyXd1{19 zY7vJasmI!Qyc)02QdJeFff+}q{;N~t2A?9Z!L&DoN_agtM`2LlIrcHj>)hJKEDc%d zb17^+riR#Kv%OQI;iBoqaHn)p^y+CK<0^AeP%QSWr9E7RhF5CvoJPA0-BoPP%AoXk zD6BIlggT|~MK7c{Xg5#u<|WQg;i>M~<1n6+b4Khh$+fclI*s^1fHjttPql`?B5MHb zx6FJt>ZxD4wRAl8?B>w1Q=7pCiERVr-i0!2YLG)}6Ju!p)4*`JS`i9O*1+LNN5>Qw z8vQ7T^_<24w=2~!uQE|}o?Xf@`FJmXD7btGdGU#_#?X72IqwzE$unfGg)dH|ywD(Z zh;c)WVNh6=@C4CgQ=k5`>NY3G8&Fdn3Cik)1L8a=u8v~j>6&UB&8>n3XJ+_ckR-8Q TT(}CZpGh1&AfCQ{=9>Rs!?QuV delta 4195 zcmb`Je@s+Y7RR0GyE|B;V4c#1;iqNS*sbKD;#$d?_(yGNw_@F5nw^PZN&hHJ#WFZL zQwjrR;5pJ=O;2sn((AxFjGeMZ;vXin3H01I4AkP(8e<%aSj}$thihb^JJzM%*E`rh z+SHl>Chz4Y@0`zl-+RwF_vEs#^rxrwCts*@UEuPGYkXK^T-NycpZNHiPYa|Ar!kCC z3~T%x$4eLudQM#7XTEQb%*r0)x1qN#^~n)2iz&<>WLMKa=bva8cJ~XN5mhLiNz?~p zb-!FX%w-HKZpGv8K~?Gf@Ddd5HZIe6coz1;rGq>$;9*M8?NQx|+?##@Dt4QestH>& zpl|nbSBNjBWPmAd4|Chx3bkGkF?3X+7O=@KB`7u+G3YU(;D~#m2csHiNu2<>_6^Hs z)<78v+T@^{S}8`)L#I9G;qSy|;d$;M##1N}k3VRm9_pa}bC9~HAT3d29=x#UH^%?- z6c+ypvEewy2z4kuXCSs`g()N|u*ci=_1yfnhi>zf5D|Z5fWM^kftiK z;=VO|uPaTgg$@!nj!{M&)ENe^V?$ajLF41F(7FAQuXu_9N2z$3Ie_DCPrulEU=U6^ zH$5^rC!Y)}U)!E>le6@q5-`U@*-xuja3+@2IS3nH+mvx*&fZl}UcV#zI`0h->rRK6 z>|8`@srY$RRF9Kd8BsaZy#6cj)-TKcS5l}q^Zvq`boIYaqh}v*55h{bz zK@d_Kp33_&NziJz-SA4@HNLpE8%LZ(JT4qD;B>n+h~rfrtIKJvqFvC`xFzq4Btb>c z;@XufB&(f7V)5D(&FNEWR&X2#!C`e-MQCA3Q(3N1OaikOe%`z*{|ZmXYeGxXt3uM@ z!y!aGi-Xj;(<+JvD0DTKaWAsBDl~e~t{V>s5mdmuylfbNo3gy-8Q64Bak_9Eih_@(NfR_<#iC1Vx{4NU5 z?<<=((X6b68~bdzm-t^q5k$mJwh6Hqju4-W$b6Ip{8VUd+P@l>>^CKm>&m{!i)uEP z3Q1Cfv3jFVepY0TN}@KYjl%T)r{+!kwcVF}fj5+cjFVf5$QZ_q3rbjMyHf>$B! z%`LefYsp}WVecPz<(%i#$V$j&^7|Pr3{fVIS^)n|#Gwv@?oVY&LDoRZf!(?1gxDS& z?T*(+3@1hO#9{s$i%5FWPTUk$9e6JP6D?WPui@}PN2a+6N1c8jG1iN#8Ii?-6B)ZY z5YJfu!LppQT7=IRSqt)84JM|wH*J$kL^ZsK*Zq=VMv95l1u1+0>!1emlfd-lpytH0 z-BjL7adI1B5#}@4sRUH=`>5?S=-w`0{+Sl$$->qF9BTW8s^Mt0nabtj2CyjJuZYOp z#dgtAKEk0^aaj?q#Q^dzj+RGJTfY+TSlYzeLKHtT zQX=Dxg^f6|p{m^=;zJY$S%=G3+|5oR>~hFDlJWpERKPQb^XD}0&=F<1r=+Q}8i(lJ zbq!%itSUZ8W(?`u43b-d%+7px@x674p{w-F0GHldF$=o1Q!!<*}e(9wOF#W+>Trf|$+FRf;cIJ^NT zF=W}*A?}Jty;<;&qpM-UE8QO})Q1f4nfK8IsEu8aYO%N5ak!ohHDjpFQUZP5nea{5 z#(UUT#uJ*>{n$^r#<~wIQKu}-o{6_GW@&iR0}@3 zVBh*f?1@uu|8<@2EvWtArI|4wEP-rzI8bo+>y#zn{h3aNJ11o^7WS>58P}XT(sw-N ETTguAB>(^b diff --git a/Art/Districts/Annotations/CommercialHub_EURO_lights.PCX b/Art/Districts/Annotations/CommercialHub_EURO_lights.PCX index 44671292dee463f8394d055bdfc6d5878ced5596..d21c1e3607b5a3f6058d2ae6f26fd1ccf204cfa5 100644 GIT binary patch delta 2831 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 He&PQB)^_8} diff --git a/Art/Districts/Annotations/CommercialHub_MIDEAST_lights.PCX b/Art/Districts/Annotations/CommercialHub_MIDEAST_lights.PCX index cb7eaca476fb07f330a615cbf9af529242a5b2a9..4d7d2d0a0f6eb27cc5422b083d5e35d33b5602b8 100644 GIT binary patch delta 5870 zcmd5=4Qv$G5q>z^WeaIAG>yq(V_@qaj%VTgl zIUxmE^1V0v&D;5(c^kQqP;)lnooNMm*R7Mbur%BH^rppitD7h_c->Rxv8}&br#@T% z_48{uT%e|2v);3X2d|W2bBMsvo3@ZNCvEfo*}E=XR#UEJlO2Dt;m;>qTA2P-*p4h(G%0Ecv)1G?o^i|)IE?LHvQ|gP5;juM?I+=gZVH_QH1k~;p6M)g zrOJhE7}gX4#fwKwx^j=4@`D#v=3b&KG`5gF9YK!apoz_*mV0UgA=|9bX6lr5T^J#? zT7)W3H8@5OIvUEltf+nw_QQe_o_p~gIHRLv_2f&IQ6n^L;2sk@oJ@^~Hj1J~Y%oPs zR$N}4d-oeoT$xPK4QwG*TB(@1g|3i-q!)%^_|mM&7w@TqDweLy>m-Q^v7w5&H6N$) zKw4>dU1&UDgk;sp368BUZ>`2;CS;|u3cI(6FliO=i-;~1K=;VJj<0nH1@9d>gfH{t zbXqlrhHaX`NsCHVJ3>ELuc?A7a!BEt7n^FGra{`sj4Dz)d8&!xFf3z>A4->H=XBil z6TS!Mm;Yw$1af(2gu|kCh zR;Xjo-^BwqjXQ5m7o0R`aCGQ0$P<)6jR8ZJob>2+NkZ|5y!j+?R5-q%KNDLR$)C6? z2@b4G9ry8FJ>liFbJjw(kkepbDwOG0RmusBc)263E^~jroASC9o?Vrf5wgDtYzEYq zE*^KrGM{96qL|KuQ#f@o;SC5wA$K!b3aC_An*9OB8Mf1&O96Okep(C7BxYhJo>Ys4J7FP6fm)AFE-eXBTBFmCab3>13^t z<1X@=a7v9J=lP*>?ZfcQ+N8U}yfN*VBd{o-`f^QWQy67uFjP<8)hW8jU^k)*vR{5M zLFh1l9gmwK6H3>qaijNSpRmp8Y=^<-7c!HnQ?NdW9a_F3>yi^^jwGg22r0;DV3R^J zta@c)T3k$Ezx-6(NEZzBF3zI+*5ea*V3ZjWqbBDIVtoJ`GqF+8S)JtAMGcv;V!_BP zRqIpXr|VPIFXw{w>mThI@^;pHHpeqYp}`7N3LjsHsy+=$U9mbPBq!TU$0H;F6A~0} zn3NtL2b|kL&1{3q6{gE`uUH1aij7Mgxv1;lRnx$Ks4U3b*zqe22NG)(og!Jk;UAKV z2jq|hynK9KFE|8g<0OO0+~i~T)QJ;xNkC5d$jM*#5GqIv(l))6dD5oNiH&RnO_~x} z)}4r#sgTv+Wg2HF)~Op&d>I_j_aHSdXU^vMmqR1OvPLj?c-sG@t}bj_oF81%;Xmaz=h&pR(fOf{m{xLoQ_Yjw68t zd+XJ8qw4G#hELWQ&3;M{n}WP%i|Akl)VH0U=~#?YqB=k>B3BCL?`z$?S5;t#eh!_jTW_-~pqC`ZP5j#%ac2UOd4~5rCg< zosijR|IvyE52@RR!0D|S-;Wm@OC{K~ZC&~rn2jhYQXviQr#6ckvv7+>zN?dX>;r>-F3oVHkn5ooR6S_2l@V)x*Rhynq*bwtk0`H%bTA+oJijXkp4;6&@#bgn;_lJ#^zPxedhpJG zlBz*BT7x8*Ot+Eodw%i-+NM2E--4C}552J@kp#h`6D6HY=Cpf1?0h5lCZh1e)msxY zA^*)6?k}ek?yn;~6hdx$b3CN(mHYpX=!fjRlWy9Ng6h4?5=0A}-TN49uYLkHR2zL$ zW(qy?)+ delta 4521 zcmb_feNa@_6~D;N!(u9H(Wq|)#KkXks)L>VK?>{E2_Q{JMprHow_Ys9ufxA*UlQ#EE}=I(gT`qX}bILh)#(% z!h*Hq##jTkJPN;FJ8|?MWGds>A#_a_T9c+T?C*qzwP~Zp01Yn4S~orQt~(}ppvfK3 z$QKKCz|3`-sdv4_tT2(6Gm-VIp&s3Ry@o@fo=3^I$ozGHa5eQ4GYJ7I$+Bj zSg`TOuD?k!f@2?caf2DOKvpLLv5+yd8CGn3(sg4{_F1rg)3a&UjhzbK!5i2g@YdlqiiQ_c`qb5ePSQ3$qVJ8o}u}y@T(TGu&P3aIQN^iCY56Oj3%e~_-NiNEZJt{8`)jGrO zUEE}zVS*v^E>)kgfg;njq~EQew`lrRT7xNrba*8{vlRxk&uHE-Gl zVn*SizD4N~s^um|b_r8eRAFcg-qfDTc*hds;;Km zqED7zu>?M;a10tU4^HgbLbXG!w`Zpt`%GT85u>W&rqpLbHE6?u_FXG7-?h)np+V(A zVWn>p+NOb*n0qF}+dH{1MOeXJrcx7BX~C>%kpib)d>jr|x~5#TTj=OE^zc(u$eseu z+?CAJV3=W!k@P{#qBmSDlbs+sZlj6t{Rn%lA|k9s}W z7AmhO_c%>#cbf`EcB+EspA8uJIbMd}=!H}7%^)7^q~Y`PcQ~vaih%G-lj9`#%AnD^ zDF^J?S$lkbY@@oub|>R*JeNNTZxzqv@3o^)l|41T0Sk7rg4JPze!J>BIwf`s|fT7;y zV2Xo_So7jF6A~17t9sf)j2Z^IYj*uyKlTQ3&g0>!dh(iVGs73&{JD9 zBVu>S`vHVPEu9Xcjxf`)mBZPWT?x(5{jz)Nz(#z(u5xTWfuJ->A32@tszKG9nYAs_gCFtUCzoU3>I zKZ6Ta?R9~3@A&%}Urot_&E)XbX#6bX7d}56-CLB^W`8LEM=uoNphU{nbgt9C^MD3d zUzrU1Uzsp54&d=*i%2L$EtVfiYJV)Je@&MLkL-K=>#&&01=_wHiF0XK!@k8} z?oa+g9tYd@=YK_Z!6*B*gjN`T;75;G_6<25TVAB2e@u}9HxE2{*OUQEUONQ!uPuQm z4t|54>2UU78U*RSC)Yyap{Ejs{g0YMnUHc=g)4`~k}93#5G+6ZgG99Nz|zALVCj)G zn0Vw-(v2s150oAq3FeV$P<%8OmLE0X%+b6gTcRj?#t*-EeKxFp-I)-Rwj`HrrR~>2 z?Nrnpo0dp#FymOx7thC__Sk$lcFa9IV%xG!0>$e1EagkF1U4Q|hu;M1& diff --git a/Art/Districts/Annotations/CommercialHub_ROMAN_lights.PCX b/Art/Districts/Annotations/CommercialHub_ROMAN_lights.PCX index 85e9beac886219ccd2a37641788cba668bc7064b..7c29bd4d8b0b0b00f387d77d4e8dcf68494343a8 100644 GIT binary patch delta 3235 zcmb_eYfMx}6rNq(G&Ujvj1MrZf9A~0_vL(Z z=FFLyExqbf57j3-#W9cN^9q**MSj%Zfq*qZkq_lM1wGh??p5ek%XA6tDrT` zC0pNb>y0^myWtSU}6Zca62EAl0H@!Y-aT7XHP1s>mV9Msj+Iz2oG9A{X{6Jz7MIxY!{F6`2si z!ST?%eQWS7s)Tv0LdOjnafSC-xmPD+BbUJc$Hk#`W$17m*s{_>ZjyV68FUO?dbXKS zAdTMhDN4|mwJ_|Q;tM_#w(Q&%@&_qocsEGL40`r0Lx;pgE>Ste>`ELuy8BGTb-Iwy zsf)o2nY3^{!(MVuo}EVT$w}Xn@ERDiVPAf+_IJ{uw@?{&1^ccQ|?XUf>PbEsfAKk!~bx zN}=*@)S(AT3UzS$z|_G=lT{q>Ktsu%7k-)NodbkQ;-_35M}BTHbQps%E1WZ;I!Ef zY{ipbiKwQOgMGb9hE&q#U>@8WUy`o%}2Vz9R&O@Uhwq)X~ zF;xr-5A6%%3f{pQuwb3#B-N^#c9eh2xR^1IL;PVatS)_L5Kb(pN_CO-%J1^PzGC~5 zu>Npf@PxvG!gsCMa0(szijx({7m0~q<9u0t>%f=1N@Fwi>(Su_O=UeGZ(Mn&72C8G% zer`>K(ql_rG9LlE%ERF1v8Vy?s*(B0=4x)mv(^f%gUAXk^pwvEyg;vTOjTCZGvF); z`ziEgV67j|!n3e5e|93)QD@=tNEmf|#&ffuJ~uPd(tJExt@;m&P9(tP%8`9N0eUOL Pp{jDilaZyjGIR9*$rW>a delta 4395 zcmcInZE#an8a|D-B3l(~>44><&GiFv1xHqPmTfx~`GMjnFgn^9D%{ywun9$M!gkpW zDYAkN=`vPd2%$8kwH56+qjc_7hhOwPAr&IBEXg%kKEkk>odpV{g}M#wp1bdDcV<^{ zB`sklx09UrIrn+)^M0J$FRofP{>!qrf7OPlkgR9LL4ym)5Xb({k3n&rAox$bj43;& z3~^LA?M{a^F2eu2dL-@D??g%*GBD+V#npE|byzsLi}yMC`rH%ZD{~nK?RX_fMB(&8 z@Q3P$^A8FMQ!bCofqi%!ue-44iQ~{(U69{B!azR!wYn%TBoyp-aF^5L2zp!+_T)z4 z>DALF7$P{r$W!?T#2AJ;_H&oV!9BGe7Yjt;>(x^W48cbnT9`L{JH~Zqt>pBu0J9$h z<+WmxHhj1ABQF(jkwemkK7Gzw=3olLzN2u@nujNH!x&Yty5O3~;o9(RYuyZEOmX4y zFeKN^gf(l%77UIuys021a)O#EH^%9{gBr$A4}1w(H6^h6^}7oGG0JemIFiIdI(OT- zP6iBg!{VBG6!yiy7DIW-vZ#h$(IGs z&8y1Bh{nj;L8fr$kh7#V1WVRFGx_2z0_MQUwX3Wb1okLoYNdh^)GPOC$eEMbsmySI z1<3m#7;B%lp35L?HauIq*7kRiLs=cbShj{q7bbB4qZ&pWl;oV$O1^p!_qIeiX_5j`!1!4k~tN`C~a;-1t!8iCe~WOFcqqvXP@zsM|heT>CYKsQBsGG$M}C zm1OY7n@Wap<)CibY&|aUgvMiX1#x39^_z}S2}3-}GzI&p{TVFYyfmY*GH|_BSNx60 z+p*F|Zlb<}h~qZmeU8{;Os3LiwG7U@Jr_QCds2px`-`HYvngA`_zGg{W!P)e77(X- z3}X)wuZPFCC>aBl!?`VW(~b#dB(NNQ^^SK6H+&ebkgAAYUsr>DvMNy*F)nL#dcu*0GH7|HsQAxD zSY!_Qr9_9}05vF+@BqU&^$(Bns0vKZrfMP+SnUGne0N2Lab=KvZ`<^sfw6jwFsX@N zL+CXjMpIa)Q{Y=4)AnLwq3Hz8D-{Z1#iJ4W2V(>q9?uHG^Duif+YWAPG^O!_#;T>b5T%3D^eBzMMOQ9V&;;?eZvhcKCKaghoj* zc6+H>L)3q*p*GM;FV(Yzv zpYQX^WDO72&jLK((2CzGn#nwE*Ycl?-`6xVONB+d9!mpN*0g;}Njsj!Ff8u zamMjNgRIGz48p~x%9|pl!nMHoJ3doCgQ)>|1n7I4w@sSc=x>~lXT9L7=dEp}A7jEx zC7Z+;Jhr Date: Tue, 3 Feb 2026 11:48:48 -0800 Subject: [PATCH 354/356] Touch up ancient Port light annotations; Update day-night scripts to ensure green pixels are removed --- Art/Districts/Annotations/Port_NW_lights.PCX | Bin 21161 -> 21161 bytes Art/Districts/Annotations/Port_SE_lights.PCX | Bin 20662 -> 20662 bytes DayNight/civ3_city_lights.py | 24 +++++--- DayNight/civ3_postprocess_pixels.py | 56 +++++++++++++------ DayNight/generate.sh | 1 + Notes/district_todos.md | 1 - 6 files changed, 57 insertions(+), 25 deletions(-) diff --git a/Art/Districts/Annotations/Port_NW_lights.PCX b/Art/Districts/Annotations/Port_NW_lights.PCX index 0d457feeb30aa90f492d94030d6db2cfd0fe8d20..9381b17ea95f04e7211279a9e4c6d5e89be57fef 100644 GIT binary patch delta 67 zcmZ3vlyT)!#totz%<^T2CO>2so2CnvTag8uGdVACVVB0{CIA2c delta 71 zcmZ3vlyT)!#totz%#vk?CO>2so2e%77Un?}aS|wT0037)4aooi diff --git a/DayNight/civ3_city_lights.py b/DayNight/civ3_city_lights.py index 1b1870dd..794f4637 100644 --- a/DayNight/civ3_city_lights.py +++ b/DayNight/civ3_city_lights.py @@ -7,7 +7,7 @@ - Multiple --light-key values (union mask) - **Per-light-key styles** via --light-style: per-key core/glow colors and optional overrides - Night (19..24,1..6): also replaces non-lights file -- Pins magenta #ff00ff to palette index 255; preserves #00ff00 +- Pins magenta #ff00ff to palette index 255; removes #00ff00 from palette """ import argparse, os, math, shutil @@ -98,7 +98,7 @@ def ensure_palette_has_colors(image: Image.Image, colors: List[Tuple[int,int,int hist = image.histogram() if image.mode=='P' else None candidates = sorted(range(256), key=(lambda i: hist[i])) if (hist and len(hist)==256) else list(range(255,-1,-1)) protected=set() - for rgb in colors+[MAGENTA,GREEN]: + for rgb in colors+[MAGENTA]: idx = find_color_index(pal, rgb) if idx!=-1: protected.add(idx) replace=[] @@ -283,16 +283,24 @@ def build_composite_with_styles(base_bg_rgba: Image.Image, def save_with_palette_and_magic(comp_rgba: Image.Image, out_path: str, base_for_magic_rgb: Image.Image) -> None: imP = quantize_to_p_256(comp_rgba) - pal_after = ensure_palette_has_colors(imP, [MAGENTA, GREEN]); put_palette(imP, pal_after) + pal_after = ensure_palette_has_colors(imP, [MAGENTA]); put_palette(imP, pal_after) replacement_idx = force_magenta_at_255(imP) - pal_after = get_palette(imP); idx_grn = find_color_index(pal_after, GREEN) dst_px = imP.load(); src_px = base_for_magic_rgb.load(); w,h = imP.size for y in range(h): for x in range(w): r,g,b = src_px[x,y]; cur = dst_px[x,y] - if (r,g,b)==MAGENTA: dst_px[x,y]=255 - elif (r,g,b)==GREEN and idx_grn!=-1: dst_px[x,y]=idx_grn - elif cur==255 and replacement_idx!=255: dst_px[x,y]=replacement_idx + if (r,g,b)==MAGENTA: + dst_px[x,y]=255 + elif (r,g,b)==GREEN: + dst_px[x,y]=255 + elif cur==255 and replacement_idx!=255: + dst_px[x,y]=replacement_idx + # Remove #00ff00 from palette entirely if present. + pal_after = get_palette(imP) + for i in range(256): + if (pal_after[3*i], pal_after[3*i+1], pal_after[3*i+2]) == GREEN: + pal_after[3*i:3*i+3] = [0, 0, 0] + put_palette(imP, pal_after) os.makedirs(os.path.dirname(out_path), exist_ok=True) imP.save(out_path, format='PCX') @@ -396,7 +404,7 @@ def main(): "Render glowing city lights from *_lights.pcx files.\n" "• Multiple light-keys supported (+ per-key styles)\n" "• Night hours (19..24,1..6) also overwrite the paired non-lights file\n" - "• MAGENTA pinned to index 255; GREEN written out as MAGENTA" + "• MAGENTA pinned to index 255; GREEN removed from palette if present" ), formatter_class=RawTextHelpFormatter, epilog=( diff --git a/DayNight/civ3_postprocess_pixels.py b/DayNight/civ3_postprocess_pixels.py index fd436799..a411f1f1 100644 --- a/DayNight/civ3_postprocess_pixels.py +++ b/DayNight/civ3_postprocess_pixels.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ -Civ3 post-process: remove *isolated* GREEN (#00ff00) pixels from PCX outputs. +Civ3 post-process: remove GREEN (#00ff00) pixels from PCX outputs. Rules ----- @@ -10,8 +10,9 @@ • Only touch files that have a corresponding "*_lights.pcx" in the same directory: - For "NAME.pcx" → process only if "NAME_lights.pcx" exists. - For "NAME_lights.pcx" → always process (it is the corresponding lights file). -• Only replace *isolated* green pixels: a green pixel whose neighbors within +• By default, only replace *isolated* green pixels: a green pixel whose neighbors within --isolation-radius (default 1, i.e. the 8-connected ring) contain no other green pixels. +• With --remove-all-green, replace *all* green pixels. • Replacement uses *neighbor palette indices* so the palette stays unchanged. If no non-green neighbors exist, fall back to nearest palette color to local RGB average. @@ -22,6 +23,7 @@ # --isolation-radius 1 (1 = 8-neighborhood) # --search-radius 3 # --max-radius 5 + # --remove-all-green """ import argparse, os, collections @@ -116,7 +118,8 @@ def local_average_rgb(rgb_img: Image.Image, x: int, y: int, radius: int, # ---------- core fixer ---------- -def fix_green_in_image(path: str, isolation_radius: int, search_radius: int, max_radius: int, verbose: bool=False) -> int: +def fix_green_in_image(path: str, isolation_radius: int, search_radius: int, max_radius: int, + remove_all_green: bool, verbose: bool=False) -> int: """ Returns number of pixels changed (only isolated greens are touched). """ @@ -135,14 +138,20 @@ def fix_green_in_image(path: str, isolation_radius: int, search_radius: int, max px = imRGB.load() w, h = imRGB.size - # Collect isolated green pixels - isolated_coords = [] - for y in range(h): - for x in range(w): - if px[x,y] == GREEN and not has_green_neighbor(imRGB, x, y, isolation_radius): - isolated_coords.append((x,y)) + # Collect target green pixels + coords = [] + if remove_all_green: + for y in range(h): + for x in range(w): + if px[x, y] == GREEN: + coords.append((x, y)) + else: + for y in range(h): + for x in range(w): + if px[x, y] == GREEN and not has_green_neighbor(imRGB, x, y, isolation_radius): + coords.append((x, y)) - if not isolated_coords: + if not coords: return 0 outP = imP.copy() @@ -152,7 +161,12 @@ def fix_green_in_image(path: str, isolation_radius: int, search_radius: int, max if mag_idx != -1: banned_indices.add(mag_idx) changed = 0 - for (x,y) in isolated_coords: + for (x,y) in coords: + if remove_all_green and mag_idx != -1: + out_px[x, y] = mag_idx + changed += 1 + continue + picked_index = None # 1) Most frequent neighbor index (expand search out to max_radius if needed) @@ -179,7 +193,10 @@ def fix_green_in_image(path: str, isolation_radius: int, search_radius: int, max outP.putpalette(pal) # keep palette exactly the same outP.save(path, format='PCX') if verbose: - print(f"[fixed] {path}: {changed} isolated green px") + if remove_all_green: + print(f"[fixed] {path}: {changed} green px") + else: + print(f"[fixed] {path}: {changed} isolated green px") else: if verbose: print(f"[ok] {path}: no isolated green px") @@ -206,7 +223,7 @@ def has_corresponding_lights(dirpath: str, fname: str) -> bool: def walk_and_fix(data_dir: str, noon_folder: str, isolation_radius: int, search_radius: int, max_radius: int, - only_hour: int=None, verbose: bool=False) -> None: + remove_all_green: bool, only_hour: int=None, verbose: bool=False) -> None: noon_abs = os.path.normpath(os.path.join(data_dir, noon_folder)) targets = [] @@ -233,22 +250,29 @@ def walk_and_fix(data_dir: str, noon_folder: str, if not has_corresponding_lights(dirpath, fname): continue path = os.path.join(dirpath, fname) - total_changed += fix_green_in_image(path, isolation_radius, search_radius, max_radius, verbose=verbose) + total_changed += fix_green_in_image( + path, isolation_radius, search_radius, max_radius, + remove_all_green, verbose=verbose + ) print(f"Done. Total isolated green pixels fixed: {total_changed}") def main(): - ap = argparse.ArgumentParser(description="Remove *isolated* #00ff00 pixels from PCX outputs (only files with a matching _lights.pcx).") + ap = argparse.ArgumentParser(description="Remove #00ff00 pixels from PCX outputs (only files with a matching _lights.pcx).") ap.add_argument("--data", required=True, help="Path to Data root (contains noon + hour folders).") ap.add_argument("--noon", default="1200", help="Name of noon folder to SKIP. Default: 1200") ap.add_argument("--only-hour", type=int, help="Restrict to a single hour folder (e.g., 2400)") ap.add_argument("--isolation-radius", type=int, default=1, help="Green is 'isolated' if no other green is found within this Chebyshev radius. Default: 1 (8-neighborhood)") ap.add_argument("--search-radius", type=int, default=3, help="Neighbor radius (pixels) for index voting. Default: 3") ap.add_argument("--max-radius", type=int, default=5, help="Max expansion radius if no neighbor found. Default: 5") + ap.add_argument("--remove-all-green", action="store_true", help="Replace all #00ff00 pixels (default: only isolated).") ap.add_argument("--verbose", action="store_true", help="Print per-file changes.") args = ap.parse_args() - walk_and_fix(args.data, args.noon, args.isolation_radius, args.search_radius, args.max_radius, args.only_hour, verbose=args.verbose) + walk_and_fix( + args.data, args.noon, args.isolation_radius, args.search_radius, args.max_radius, + args.remove_all_green, args.only_hour, verbose=args.verbose + ) if __name__ == "__main__": main() diff --git a/DayNight/generate.sh b/DayNight/generate.sh index e7df7f64..e3f037b3 100644 --- a/DayNight/generate.sh +++ b/DayNight/generate.sh @@ -171,6 +171,7 @@ process_art_set() { --data "$data_dir" --noon "$NOON_SUBFOLDER" --verbose + --remove-all-green ) if [[ -n "${ONLY_HOUR}" ]]; then diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 102bb243..ab315197 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -2,7 +2,6 @@ - Municipal District (ZergMazter doing art) - Central Rail Hub (ZergMazter doing art) - Light annotations - - Commercial Hub (modern, left) - Municipal District - Central Rail Hub From 9c3a92460a70ec83160b551bafa18764abd85b0c Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 4 Feb 2026 07:42:32 -0800 Subject: [PATCH 355/356] Fix Pyramids shadow angle --- Art/Districts/1200/Wonders.PCX | Bin 37166 -> 37157 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Wonders.PCX b/Art/Districts/1200/Wonders.PCX index 5c29c17bb2964e43fb438bd97685b9fc0d7d84da..144373b737793f7a826c41e4460abd097695af9e 100644 GIT binary patch delta 128 zcmZ3th-v8}rVVSwm~Q;vyiv@Yk?G3+&2Pl*7@02o->fHT#mIE#|K=Pi8%Cy+|2MCd zwq;~F@?Xkq^9LDiM&?`pq&6$c=|LF5@&QbYn>H^|yu`))@ZYA%6*{)ePl5Dy9W$1f c{|;@M{9Z?&PzpsU345y;r=qWdZt02=5zfB*mh delta 137 zcmV;40CxYSq5`g>0Pf=KhnYEH(n`{*&S?F9PrWlNBu@0`vZ}J1y#C5zRsX From a441fdb66303f779a891e0c792d88f6d1ae8708f Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 4 Feb 2026 17:55:19 -0800 Subject: [PATCH 356/356] Refactor to separate districts buildable optionally on an overlay (Great Wall) vs those that require an overlay, with updates to config; Fix Great wall only on other civ border territory id bug; Allow AI workers to replace a district if another district can require replacing it --- C3X.h | 23 +++- default.c3x_config.ini | 2 +- default.districts_config.txt | 11 +- default.districts_wonders_config.txt | 31 ++--- injected_code.c | 182 +++++++++++++++++++++------ 5 files changed, 182 insertions(+), 67 deletions(-) diff --git a/C3X.h b/C3X.h index f1c7d73c..d3b9c505 100644 --- a/C3X.h +++ b/C3X.h @@ -715,6 +715,7 @@ struct district_config { unsigned int buildable_square_types_mask; unsigned int buildable_adjacent_to_square_types_mask; unsigned int buildable_on_overlays_mask; + unsigned int buildable_only_on_overlays_mask; unsigned int buildable_adjacent_to_overlays_mask; bool buildable_adjacent_to_allows_city; bool allow_multiple; @@ -769,6 +770,7 @@ struct district_config { bool has_buildable_adjacent_to; bool has_buildable_adjacent_to_districts; bool has_buildable_on_overlays; + bool has_buildable_only_on_overlays; bool has_buildable_adjacent_to_overlays; char const * buildable_by_civs[32]; int buildable_by_civ_count; @@ -799,11 +801,14 @@ struct wonder_district_config { img_alt_dir_row, img_alt_dir_column; unsigned int buildable_square_types_mask; - unsigned int buildable_on_overlays_mask; + unsigned int buildable_only_on_overlays_mask; + unsigned int buildable_adjacent_to_square_types_mask; unsigned int buildable_adjacent_to_overlays_mask; + bool buildable_adjacent_to_allows_city; bool enable_img_alt_dir; bool is_dynamic; - bool has_buildable_on_overlays; + bool has_buildable_only_on_overlays; + bool has_buildable_adjacent_to; bool has_buildable_adjacent_to_overlays; }; @@ -825,7 +830,8 @@ enum district_overlay_mask_bits { DOM_JUNGLE = 1u << 6, DOM_FOREST = 1u << 7, DOM_SWAMP = 1u << 8, - DOM_RIVER = 1u << 9 + DOM_RIVER = 1u << 9, + DOM_AIRFIELD = 1u << 10 }; struct natural_wonder_district_config { @@ -960,7 +966,7 @@ const struct district_config special_district_defaults[USED_SPECIAL_DISTRICT_TYP .command = UCV_Build_GreatWall, .name = "Great Wall", .tooltip = "Build Great Wall", .display_name = "Great Wall", .advance_prereqs = {0}, .advance_prereq_count = 0, .obsoleted_by = "Metallurgy", .resource_prereqs = {0}, .resource_prereq_on_tile = NULL, .allow_multiple = true, .vary_img_by_era = false, .vary_img_by_culture = false, .is_dynamic = false, .resource_prereq_count = 0, .dependent_improvement_max_index = 0, .img_paths = {"GreatWall.pcx"}, .dependent_improvements = {0}, .custom_height = 88, .wonder_prereqs = {"The Great Wall"}, .wonder_prereq_count = 1, - .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Mountains) | (1 << SQ_Forest) | (1 << SQ_Swamp) | (1 << SQ_Jungle)), .draw_over_resources = true, + .buildable_square_types_mask = (unsigned int)(DEFAULT_DISTRICT_BUILDABLE_MASK | (1 << SQ_Mountains) | (1 << SQ_Forest) | (1 << SQ_Swamp) | (1 << SQ_Jungle) | (1 << SQ_Volcano)), .draw_over_resources = true, .img_path_count = 1, .img_column_count = 9, .btn_tile_sheet_column = 9, .btn_tile_sheet_row = 0, .culture_bonus = 0, .science_bonus = 0, .food_bonus = 0, .gold_bonus = 0, .shield_bonus = 0, .happiness_bonus = 0, .defense_bonus_percent = 50, .generated_resource = NULL, .generated_resource_id = -1, .generated_resource_flags = 0 @@ -1024,6 +1030,7 @@ struct parsed_district_definition { unsigned int buildable_square_types_mask; unsigned int buildable_adjacent_to_square_types_mask; unsigned int buildable_on_overlays_mask; + unsigned int buildable_only_on_overlays_mask; unsigned int buildable_adjacent_to_overlays_mask; bool buildable_adjacent_to_allows_city; char * buildable_by_civs[32]; @@ -1065,6 +1072,7 @@ struct parsed_district_definition { bool has_buildable_on; bool has_buildable_adjacent_to; bool has_buildable_on_overlays; + bool has_buildable_only_on_overlays; bool has_buildable_adjacent_to_overlays; bool has_resource_prereq_on_tile; bool has_buildable_by_civs; @@ -1108,8 +1116,10 @@ struct parsed_wonder_definition { int img_alt_dir_column; bool enable_img_alt_dir; unsigned int buildable_square_types_mask; - unsigned int buildable_on_overlays_mask; + unsigned int buildable_only_on_overlays_mask; + unsigned int buildable_adjacent_to_square_types_mask; unsigned int buildable_adjacent_to_overlays_mask; + bool buildable_adjacent_to_allows_city; bool has_name; bool has_img_path; bool has_img_row; @@ -1122,7 +1132,8 @@ struct parsed_wonder_definition { bool has_img_alt_dir_column; bool has_enable_img_alt_dir; bool has_buildable_on; - bool has_buildable_on_overlays; + bool has_buildable_only_on_overlays; + bool has_buildable_adjacent_to; bool has_buildable_adjacent_to_overlays; }; diff --git a/default.c3x_config.ini b/default.c3x_config.ini index 41e7e8e5..e6ac9193 100644 --- a/default.c3x_config.ini +++ b/default.c3x_config.ini @@ -1048,7 +1048,7 @@ disable_great_wall_city_defense_bonus = false great_wall_districts_impassible_by_others = true auto_build_great_wall_around_territory = true great_wall_auto_build_wonder_name = "The Great Wall" -ai_auto_build_great_wall_strategy = all-borders +ai_auto_build_great_wall_strategy = other-civ-bordered-only ; When enabled, holding down the Control key while a worker is selected will highlight all tiles within the work radii of nearby cities. City centers ; are highlighted more brightly, while tiles that fall within multiple cities' work radii are highlighted more intensely. This visual aid helps you diff --git a/default.districts_config.txt b/default.districts_config.txt index f184fc18..f0dc0484 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -28,10 +28,11 @@ ; - wonder_prereqs : Comma-separated wonder names. District cannot be built unless all Wonders are built by same civ. ; - natural_wonder_prereqs : Comma-separated natural wonder names. District cannot be built unless all Natural Wonders are within civ's territory. ; - buildable_on : Comma-separated square types: desert, plains, grassland, tundra, floodplain, hills, mountains, volcano, - coast, sea, ocean, snow-forest, snow-mountains, snow-volcano, lake. - ; - buildable_on_overlays : Comma-separated overlays: irrigation, mine, fortress, barricade, outpost, radar-tower, jungle, forest, swamp, river. + ; coast, sea, ocean, snow-forest, snow-mountains, snow-volcano, lake. (Tile *must* be one of these) + ; - buildable_on_overlays : Comma-separated overlays: jungle, forest, swamp. (Optional; Tile *may* be one of these, workers don't need to remove them). + ; - buildable_only_on_overlays : Comma-separated overlays: irrigation, mine, fortress, barricade, jungle, forest, swamp, river. (Optional; Tile *must* be one of these) ; - buildable_adjacent_to : Same as buildable_on, plus "city". - ; - buildable_adjacent_to_overlays : Same as buildable_on_overlays. + ; - buildable_adjacent_to_overlays : Same as buildable_only_on_overlays. ; - buildable_on_districts : Comma-separated district names (tile must already have a completed district of any of these types, which it would replace). ; - buildable_adjacent_to_districts : Comma-separated district names (adjacent tile must have a completed district of any of these types). ; - buildable_by_civs : Comma-separated civ names (e.g., Romans, Egyptians). @@ -235,7 +236,7 @@ vary_img_by_era = 1 vary_img_by_culture = 0 advance_prereqs = Engineering dependent_improvs = -buildable_on_overlays = forest +buildable_only_on_overlays = forest defense_bonus_percent = 0 allow_multiple = 1 culture_bonus = 2 @@ -475,7 +476,7 @@ btn_tile_sheet_column = 9 tooltip = Build Great Wall obsoleted_by = Metallurgy wonder_prereqs = "The Great Wall" -buildable_on = desert,plains,grassland,tundra,floodplain,mountains,hills +buildable_on = desert,plains,grassland,tundra,floodplain,mountains,hills,volcano buildable_on_overlays = forest,swamp,jungle draw_over_resources = 1 defense_bonus_percent = 50 diff --git a/default.districts_wonders_config.txt b/default.districts_wonders_config.txt index 3a1100bb..bf8a5fb6 100644 --- a/default.districts_wonders_config.txt +++ b/default.districts_wonders_config.txt @@ -8,21 +8,22 @@ [ ; Wonder config fields (each Wonder block begins with "#Wonder") - ; - name : Text (required). Must match the in-game Wonder improvement name. - ; - buildable_on : Comma-separated square types: desert, plains, grassland, tundra, floodplain, hills, mountains, volcano, + ; - name : Text (required). Must match the in-game Wonder improvement name. + ; - buildable_on : Comma-separated square types: desert, plains, grassland, tundra, floodplain, hills, mountains, volcano, coast, sea, ocean, snow-forest, snow-mountains, snow-volcano, any. - ; - buildable_on_overlays : Comma-separated overlays: irrigation, mine, fortress, barricade, outpost, radar-tower, jungle, forest, swamp, river. - ; - buildable_adjacent_to_overlays : Same as buildable_on_overlays. - ; - img_path : Text. PCX filename (under Art/Districts/1200/). Defaults to Wonders.pcx if omitted. - ; - img_construct_row : Number (required). Row index in the PCX for the "under construction" wonder art (0-based). - ; - img_construct_column : Number (required). Column index in the PCX for the "under construction" wonder art (0-based). - ; - img_row : Number (required). Row index in the PCX for the completed wonder art (0-based). - ; - img_column : Number (required). Column index in the PCX for the completed wonder art (0-based). - ; - enable_img_alt_dir : 0 or 1. If 1, use alternate art when river/city direction implies a flip (see img_alt_dir_* fields). - ; - img_alt_dir_construct_row : Number. Row index for alternate "under construction" art (0-based). - ; - img_alt_dir_construct_column : Number. Column index for alternate "under construction" art (0-based). - ; - img_alt_dir_row : Number. Row index for alternate completed art (0-based). - ; - img_alt_dir_column : Number. Column index for alternate completed art (0-based). + ; - buildable_only_on_overlays : Overlays list for wonders only supports "river". Wonder can only be built if tile has river. + ; - buildable_adjacent_to : Same as buildable_on, plus "city". + ; - buildable_adjacent_to_overlays : Same as buildable_only_on_overlays. + ; - img_path : Text. PCX filename (under Art/Districts/1200/). Defaults to Wonders.pcx if omitted. + ; - img_construct_row : Number (required). Row index in the PCX for the "under construction" wonder art (0-based). + ; - img_construct_column : Number (required). Column index in the PCX for the "under construction" wonder art (0-based). + ; - img_row : Number (required). Row index in the PCX for the completed wonder art (0-based). + ; - img_column : Number (required). Column index in the PCX for the completed wonder art (0-based). + ; - enable_img_alt_dir : 0 or 1. If 1, use alternate art when river/city direction implies a flip (see img_alt_dir_* fields). + ; - img_alt_dir_construct_row : Number. Row index for alternate "under construction" art (0-based). + ; - img_alt_dir_construct_column : Number. Column index for alternate "under construction" art (0-based). + ; - img_alt_dir_row : Number. Row index for alternate completed art (0-based). + ; - img_alt_dir_column : Number. Column index for alternate completed art (0-based). ] #Wonder @@ -157,7 +158,7 @@ img_alt_dir_column = 3 #Wonder name = Hoover Dam buildable_on = mountains -buildable_on_overlays = river +buildable_only_on_overlays = river enable_img_alt_dir = 1 img_path = Wonders_3.pcx img_construct_row = 0 diff --git a/injected_code.c b/injected_code.c index 402db700..5c87618f 100644 --- a/injected_code.c +++ b/injected_code.c @@ -68,6 +68,7 @@ struct injected_state * is = ADDR_INJECTED_STATE; #define TILE_FLAG_ROAD 0x1 #define TILE_FLAG_RAILROAD 0x2 #define TILE_FLAG_MINE 0x4 +#define TILE_FLAG_IRRIGATION 0x8 #define NEIGHBORHOOD_DISTRICT_ID 0 #define WONDER_DISTRICT_ID 1 @@ -1761,7 +1762,7 @@ bool read_distribution_hub_yield_division_mode (struct string_slice const * s, int * out_val) { struct string_slice trimmed = trim_string_slice (s, 1); - if (slice_matches_str (&trimmed, "flat" )) { *out_val = DHYDM_FLAT; return true; } + if (slice_matches_str (&trimmed, "flat" )) { *out_val = DHYDM_FLAT; return true; } else if (slice_matches_str (&trimmed, "scale-by-city-count" )) { *out_val = DHYDM_SCALE_BY_CITY_COUNT; return true; } else return false; @@ -1771,7 +1772,7 @@ bool read_ai_distribution_hub_build_strategy (struct string_slice const * s, int * out_val) { struct string_slice trimmed = trim_string_slice (s, 1); - if (slice_matches_str (&trimmed, "auto" )) { *out_val = ADHBS_AUTO; return true; } + if (slice_matches_str (&trimmed, "auto" )) { *out_val = ADHBS_AUTO; return true; } else if (slice_matches_str (&trimmed, "by-city-count" )) { *out_val = ADHBS_BY_CITY_COUNT; return true; } else return false; @@ -1781,7 +1782,7 @@ bool read_ai_auto_build_great_wall_strategy (struct string_slice const * s, int * out_val) { struct string_slice trimmed = trim_string_slice (s, 1); - if (slice_matches_str (&trimmed, "all-borders" )) { *out_val = AAGWS_ALL_BORDERS; return true; } + if (slice_matches_str (&trimmed, "all-borders" )) { *out_val = AAGWS_ALL_BORDERS; return true; } else if (slice_matches_str (&trimmed, "other-civ-bordered-only")) { *out_val = AAGWS_OTHER_CIV_BORDERED_ONLY; return true; } else return false; @@ -1993,6 +1994,8 @@ tile_matches_overlay_mask (Tile * tile, unsigned int mask) return true; if ((mask & DOM_RADAR_TOWER) && ((overlays & 0x40000000) != 0)) return true; + if ((mask & DOM_AIRFIELD) && ((overlays & 0x20000000) != 0)) + return true; if ((mask & DOM_JUNGLE) && tile_matches_square_type (tile, SQ_Jungle)) return true; if ((mask & DOM_FOREST) && tile_matches_square_type (tile, SQ_Forest)) @@ -3813,14 +3816,22 @@ district_is_buildable_on_square_type (struct district_config const * cfg, Tile * if (mask == 0) mask = district_default_buildable_mask (); - if (! tile_matches_square_type_mask (tile, mask)) - return false; + bool square_matches = tile_matches_square_type_mask (tile, mask); + bool overlay_allowed = false; + bool overlay_required = false; + + if (cfg->has_buildable_on_overlays) + overlay_allowed = tile_matches_overlay_mask (tile, cfg->buildable_on_overlays_mask); - if (cfg->has_buildable_on_overlays) { - if (! tile_matches_overlay_mask (tile, cfg->buildable_on_overlays_mask)) + if (cfg->has_buildable_only_on_overlays) { + overlay_required = tile_matches_overlay_mask (tile, cfg->buildable_only_on_overlays_mask); + if (! overlay_required) return false; } + if (! square_matches && ! overlay_allowed && ! overlay_required) + return false; + if (cfg->has_buildable_on_districts) { struct district_instance * inst = get_district_instance (tile); if (inst == NULL) @@ -5847,8 +5858,34 @@ wonder_is_buildable_on_square_type (struct wonder_district_config const * cfg, T if (! tile_matches_square_type_mask (tile, wonder_buildable_square_type_mask (cfg))) return false; - if (cfg->has_buildable_on_overlays) { - if (! tile_matches_overlay_mask (tile, cfg->buildable_on_overlays_mask)) + if (cfg->has_buildable_only_on_overlays) { + if (! tile_matches_overlay_mask (tile, cfg->buildable_only_on_overlays_mask)) + return false; + } + + if (cfg->has_buildable_adjacent_to) { + int tile_x, tile_y; + if (! tile_coords_from_ptr (&p_bic_data->Map, tile, &tile_x, &tile_y)) + return false; + + bool matches = false; + bool city_adjacent = false; + FOR_TILES_AROUND (tai, 9, tile_x, tile_y) { + if (tai.n == 0) + continue; + if (Tile_has_city (tai.tile)) { + city_adjacent = true; + if (cfg->buildable_adjacent_to_allows_city) + matches = true; + } + if (tile_matches_square_type_mask (tai.tile, cfg->buildable_adjacent_to_square_types_mask)) + matches = true; + if (matches && (cfg->buildable_adjacent_to_allows_city || ! city_adjacent)) + break; + } + if (city_adjacent && ! cfg->buildable_adjacent_to_allows_city) + return false; + if (! matches) return false; } @@ -7345,6 +7382,7 @@ init_parsed_district_definition (struct parsed_district_definition * def) def->buildable_square_types_mask = district_default_buildable_mask (); def->buildable_adjacent_to_square_types_mask = 0; def->buildable_on_overlays_mask = 0; + def->buildable_only_on_overlays_mask = 0; def->buildable_adjacent_to_overlays_mask = 0; def->buildable_adjacent_to_allows_city = false; } @@ -7730,10 +7768,15 @@ parse_buildable_overlay_mask (struct string_slice const * value, { char * value_text = trim_and_extract_slice (value, 0); unsigned int mask = 0; + unsigned int allowed_mask = (DOM_MINE | DOM_IRRIGATION | DOM_FORTRESS | DOM_BARRICADE | + DOM_OUTPOST | DOM_RADAR_TOWER | DOM_JUNGLE | DOM_FOREST | + DOM_SWAMP | DOM_RIVER | DOM_AIRFIELD); int entry_count = 0; if (key_name == NULL) key_name = "buildable_on_overlays"; + if (strcmp (key_name, "buildable_on_overlays") == 0) + allowed_mask = (DOM_JUNGLE | DOM_FOREST | DOM_SWAMP); if (value_text != NULL) { char * cursor = value_text; @@ -7751,35 +7794,40 @@ parse_buildable_overlay_mask (struct string_slice const * value, struct string_slice item_slice = { .str = item_start, .len = (int)(item_end - item_start) }; if (item_slice.len > 0) { + unsigned int bit = 0; if (slice_matches_str (&item_slice, "mine")) { - mask |= DOM_MINE; - entry_count += 1; + bit = DOM_MINE; } else if (slice_matches_str (&item_slice, "irrigation")) { - mask |= DOM_IRRIGATION; - entry_count += 1; + bit = DOM_IRRIGATION; } else if (slice_matches_str (&item_slice, "fortress")) { - mask |= DOM_FORTRESS; - entry_count += 1; + bit = DOM_FORTRESS; } else if (slice_matches_str (&item_slice, "barricade")) { - mask |= DOM_BARRICADE; - entry_count += 1; + bit = DOM_BARRICADE; } else if (slice_matches_str (&item_slice, "outpost")) { - mask |= DOM_OUTPOST; - entry_count += 1; + bit = DOM_OUTPOST; } else if (slice_matches_str (&item_slice, "radar-tower")) { - mask |= DOM_RADAR_TOWER; - entry_count += 1; + bit = DOM_RADAR_TOWER; + } else if (slice_matches_str (&item_slice, "airfield")) { + bit = DOM_AIRFIELD; } else if (slice_matches_str (&item_slice, "jungle")) { - mask |= DOM_JUNGLE; - entry_count += 1; + bit = DOM_JUNGLE; } else if (slice_matches_str (&item_slice, "forest")) { - mask |= DOM_FOREST; - entry_count += 1; + bit = DOM_FOREST; } else if (slice_matches_str (&item_slice, "swamp")) { - mask |= DOM_SWAMP; - entry_count += 1; + bit = DOM_SWAMP; } else if (slice_matches_str (&item_slice, "river")) { - mask |= DOM_RIVER; + bit = DOM_RIVER; + } + + if (bit != 0) { + if ((allowed_mask & bit) == 0) { + struct error_line * err = add_error_line (parse_errors); + snprintf (err->text, sizeof err->text, "^ Line %d: %.*s (invalid %s entry)", line_number, item_slice.len, item_slice.str, key_name); + err->text[(sizeof err->text) - 1] = '\0'; + free (value_text); + return false; + } + mask |= bit; entry_count += 1; } else { struct error_line * err = add_error_line (parse_errors); @@ -8259,6 +8307,10 @@ override_special_district_from_definition (struct parsed_district_definition * d cfg->buildable_on_overlays_mask = def->buildable_on_overlays_mask; cfg->has_buildable_on_overlays = true; } + if (def->has_buildable_only_on_overlays) { + cfg->buildable_only_on_overlays_mask = def->buildable_only_on_overlays_mask; + cfg->has_buildable_only_on_overlays = true; + } if (def->has_buildable_adjacent_to_overlays) { cfg->buildable_adjacent_to_overlays_mask = def->buildable_adjacent_to_overlays_mask; cfg->has_buildable_adjacent_to_overlays = true; @@ -8520,6 +8572,8 @@ add_dynamic_district_from_definition (struct parsed_district_definition * def, i new_cfg.buildable_adjacent_to_allows_city = def->has_buildable_adjacent_to ? def->buildable_adjacent_to_allows_city : false; new_cfg.buildable_on_overlays_mask = def->has_buildable_on_overlays ? def->buildable_on_overlays_mask : 0; new_cfg.has_buildable_on_overlays = def->has_buildable_on_overlays; + new_cfg.buildable_only_on_overlays_mask = def->has_buildable_only_on_overlays ? def->buildable_only_on_overlays_mask : 0; + new_cfg.has_buildable_only_on_overlays = def->has_buildable_only_on_overlays; new_cfg.buildable_adjacent_to_overlays_mask = def->has_buildable_adjacent_to_overlays ? def->buildable_adjacent_to_overlays_mask : 0; new_cfg.has_buildable_adjacent_to_overlays = def->has_buildable_adjacent_to_overlays; @@ -9025,6 +9079,15 @@ handle_district_definition_key (struct parsed_district_definition * def, def->has_buildable_on_overlays = false; } + } else if (slice_matches_str (key, "buildable_only_on_overlays")) { + unsigned int mask; + if (parse_buildable_overlay_mask (value, &mask, parse_errors, line_number, "buildable_only_on_overlays")) { + def->buildable_only_on_overlays_mask = mask; + def->has_buildable_only_on_overlays = true; + } else { + def->has_buildable_only_on_overlays = false; + } + } else if (slice_matches_str (key, "buildable_adjacent_to")) { unsigned int mask; def->buildable_adjacent_to_allows_city = false; @@ -9476,8 +9539,10 @@ init_parsed_wonder_definition (struct parsed_wonder_definition * def) { memset (def, 0, sizeof *def); def->buildable_square_types_mask = district_default_buildable_mask (); - def->buildable_on_overlays_mask = 0; + def->buildable_only_on_overlays_mask = 0; + def->buildable_adjacent_to_square_types_mask = 0; def->buildable_adjacent_to_overlays_mask = 0; + def->buildable_adjacent_to_allows_city = false; } void @@ -9528,9 +9593,12 @@ add_dynamic_wonder_from_definition (struct parsed_wonder_definition * def, int s new_cfg.img_alt_dir_column = def->img_alt_dir_column; new_cfg.enable_img_alt_dir = def->enable_img_alt_dir; new_cfg.buildable_square_types_mask = def->has_buildable_on ? def->buildable_square_types_mask : district_default_buildable_mask (); - new_cfg.buildable_on_overlays_mask = def->has_buildable_on_overlays ? def->buildable_on_overlays_mask : 0; + new_cfg.buildable_only_on_overlays_mask = def->has_buildable_only_on_overlays ? def->buildable_only_on_overlays_mask : 0; new_cfg.buildable_adjacent_to_overlays_mask = def->has_buildable_adjacent_to_overlays ? def->buildable_adjacent_to_overlays_mask : 0; - new_cfg.has_buildable_on_overlays = def->has_buildable_on_overlays; + new_cfg.has_buildable_only_on_overlays = def->has_buildable_only_on_overlays; + new_cfg.buildable_adjacent_to_square_types_mask = def->has_buildable_adjacent_to ? def->buildable_adjacent_to_square_types_mask : 0; + new_cfg.buildable_adjacent_to_allows_city = def->has_buildable_adjacent_to ? def->buildable_adjacent_to_allows_city : false; + new_cfg.has_buildable_adjacent_to = def->has_buildable_adjacent_to; new_cfg.has_buildable_adjacent_to_overlays = def->has_buildable_adjacent_to_overlays; if (existing_index >= 0) { @@ -9792,13 +9860,28 @@ handle_wonder_definition_key (struct parsed_wonder_definition * def, def->has_buildable_on = false; } - } else if (slice_matches_str (key, "buildable_on_overlays")) { + } else if (slice_matches_str (key, "buildable_only_on_overlays")) { unsigned int mask; - if (parse_buildable_overlay_mask (value, &mask, parse_errors, line_number, "buildable_on_overlays")) { - def->buildable_on_overlays_mask = mask; - def->has_buildable_on_overlays = true; + if (parse_buildable_overlay_mask (value, &mask, parse_errors, line_number, "buildable_only_on_overlays")) { + if (mask != DOM_RIVER) { + add_key_parse_error (parse_errors, line_number, key, value, "(only \"river\" is allowed for wonders)"); + def->has_buildable_only_on_overlays = false; + } else { + def->buildable_only_on_overlays_mask = mask; + def->has_buildable_only_on_overlays = true; + } } else { - def->has_buildable_on_overlays = false; + def->has_buildable_only_on_overlays = false; + } + + } else if (slice_matches_str (key, "buildable_adjacent_to")) { + unsigned int mask; + def->buildable_adjacent_to_allows_city = false; + if (parse_buildable_square_type_mask (value, &mask, parse_errors, line_number, "buildable_adjacent_to", &def->buildable_adjacent_to_allows_city)) { + def->buildable_adjacent_to_square_types_mask = mask; + def->has_buildable_adjacent_to = true; + } else { + def->has_buildable_adjacent_to = false; } } else if (slice_matches_str (key, "buildable_adjacent_to_overlays")) { @@ -20595,6 +20678,26 @@ patch_Leader_can_do_worker_job (Leader * this, int edx, enum Worker_Jobs job, in if (district_is_obsolete_for_civ (district_id, this->ID)) allow_ai_change = true; + // Allow if district could be used as a prerequisite for other buildable districts + if (! allow_ai_change) { + for (int other_id = 0; other_id < is->district_count; other_id++) { + if (other_id == district_id) + continue; + struct district_config const * other_cfg = &is->district_configs[other_id]; + if (! other_cfg->has_buildable_on_districts) + continue; + for (int i = 0; i < other_cfg->buildable_on_district_id_count; i++) { + if (other_cfg->buildable_on_district_ids[i] == district_id) { + if (leader_can_build_district (this, other_id)) + allow_ai_change = true; + break; + } + } + if (allow_ai_change) + break; + } + } + // Allow AI to build roads/rails on bridge districts if (! allow_ai_change && (district_id == BRIDGE_DISTRICT_ID) && (job == WJ_Build_Road || job == WJ_Build_Railroad)) @@ -23315,8 +23418,7 @@ auto_build_great_wall_districts_for_civ (int civ_id) is->great_wall_auto_build = GWABS_RUNNING; - unsigned int const irrigation_flag = 0x8; - unsigned int const replaceable_flags = TILE_FLAG_MINE | irrigation_flag; + unsigned int const replaceable_flags = TILE_FLAG_MINE | TILE_FLAG_IRRIGATION; bool require_other_civ_border = (! is_human) && is->current_config.ai_auto_build_great_wall_strategy == AAGWS_OTHER_CIV_BORDERED_ONLY; for (int index = 0; index < p_bic_data->Map.TileCount; index++) { @@ -23343,7 +23445,7 @@ auto_build_great_wall_districts_for_civ (int civ_id) int owner_id = neighbor->vtable->m38_Get_Territory_OwnerID (neighbor); if (owner_id != civ_id) { has_border = true; - if (owner_id >= 0) { + if (owner_id > 0) { has_other_civ_border = true; break; } @@ -31636,7 +31738,7 @@ wonder_allows_river (struct wonder_district_config const * cfg) build_mask = district_default_buildable_mask (); if ((build_mask & square_type_mask_bit (SQ_RIVER)) != 0) return true; - if (cfg->has_buildable_on_overlays && ((cfg->buildable_on_overlays_mask & DOM_RIVER) != 0)) + if (cfg->has_buildable_only_on_overlays && ((cfg->buildable_only_on_overlays_mask & DOM_RIVER) != 0)) return true; return false; }

L{7Obi;HB?=&Y!3ZEOtCm+Imca$Wck(>N(G$L_% zFRu5OVaEI*P+j=a>&Gy+z7IbC@Jp|szzn<;avy)`^@=zG-vfcCfBic0H>T}Boxh** z2ky}wolEE5nR#H-id`EY*x$cnD-`%YG;*EQuX@bq&%E{g#+I&DW?5yy0(G`=QT``~ zf86q5KId&b7-7#wmJIfm@7TU&Q`d?IjU`51L4A$-XI=N+PZvM))Ps>1TO++oFYm2B zHspDtdCBp?)p~l)llJ-VA85I7`oB7VSi9@>4Zr?HXSmO9T%7M)YkZA=c=7yfZ>R3Z z9p7tuD6?ki{3VM^yxk4;4=vjA(>A)|uBCc)Nl|8tRr8{kSy7Y!R7)ZEg7-lG(>Ynw zd!M;)&nrvKRr00=!?u?kd;8rd-|XGI>Csx!bI zL(BjChlcGhE_~6iW)|P-+PGFzo^#~`|1zc^e|Fj?7gIrHqE}WdS)5&M%zf;Uq8ED? z`8Q}B9r^CAT>bsUul|w^zxc3j(H(cGzO%dk5l#85yNhOSe`)DU&oADvuDrA)qpNda zxq9ZJ1@iUl<^}w!E1&GxxIx#uiP{mVT={U0>)Cm^xmit3E9TCftybqRUOdm|t1T>? zHDkt<)sDOU{*vx))niTH3H-FGjV%0~6jLAP* zUsirk0sB<-{&Q_rD`uz{eS6`u~7j*#H0l diff --git a/Art/Districts/1200/HolySite_ROMAN.PCX b/Art/Districts/1200/HolySite_ROMAN.PCX index 37d2f7325a0f1e648bca59306c3e67a5522adac9..05428f1d91f573bd2e2d83131a9b4415e35beff1 100644 GIT binary patch literal 15685 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 EPo3LKIsgCw literal 12774 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%0A~j7i)F;^M>u6wS=ibq>`grsh=bZ)X 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 GzyA+V5(?P> diff --git a/Notes/district_todos.md b/Notes/district_todos.md index fc6baf98..1e16767e 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -1,17 +1,17 @@ - Districts: - Central Rail Hub (+25% distro hub yields) - Mass Transit System - - Energy Grid - - Coal Plant - - Solar Plant - - Hydro Plant - - Nuclear Plant - Canal (2 opposite sides have coast "isthmus") - Bridge (2 opposite sides have land "strait" or bridges) - Light annotations - Add commented instructions on fields in Wonder, Natural Wonder config files - Double check PCX third column alignment, clean up ground in industrial zone, Newton's University, Grand Cathedral? + - To ask Flintlock + - Enabling pillage button for naval units + - Worker on coast wake up issue + - Colony structure and loop for relation penalty + ## Maritime Districts - ~~Negative bonuses~~ @@ -85,6 +85,7 @@ - ~~Park~~ - ~~Offshore Extraction Zone~~ - ~~Data Center~~ + - ~~Energy Grid~~ From 81ac996c95f21c31d2556c9d63a0c0e28e002ea9 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 10:07:36 -0800 Subject: [PATCH 284/356] Add commented instructions for wonders and natural wonders --- Notes/district_todos.md | 10 +++++----- default.districts_natural_wonders_config.txt | 18 ++++++++++++++++++ default.districts_wonders_config.txt | 17 +++++++++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/Notes/district_todos.md b/Notes/district_todos.md index 1e16767e..c3e32bd6 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -4,17 +4,17 @@ - Canal (2 opposite sides have coast "isthmus") - Bridge (2 opposite sides have land "strait" or bridges) - Light annotations - - Add commented instructions on fields in Wonder, Natural Wonder config files - Double check PCX third column alignment, clean up ground in industrial zone, Newton's University, Grand Cathedral? - - To ask Flintlock - - Enabling pillage button for naval units - - Worker on coast wake up issue - - Colony structure and loop for relation penalty +## To ask Flintlock + - Enabling pillage button for naval units + - Worker on coast wake up issue + - Colony structure and loop for relation penalty ## Maritime Districts - ~~Negative bonuses~~ + - ~~Add commented instructions on fields in Wonder, Natural Wonder config files~~ - ~~Firm up logic for river district rendering~~ - ~~Validate and upfront bridge/canal algorithm~~ - ~~Validate resource generation~~ diff --git a/default.districts_natural_wonders_config.txt b/default.districts_natural_wonders_config.txt index 70251996..1f0e1b47 100644 --- a/default.districts_natural_wonders_config.txt +++ b/default.districts_natural_wonders_config.txt @@ -6,6 +6,24 @@ [user-defined districts will be used. ] [====================================================================================================================================================] +[ + ; Natural Wonder config fields (each Wonder block begins with "#Wonder") + ; - name : Text (required). Internal natural wonder name; must be unique. + ; - terrain_type : Text (required). Base terrain: desert, plains, grassland, jungle, tundra, floodplain, swamp, hills, mountains, + forest, volcano, snow-forest, snow-mountains, snow-volcano, coast, sea, ocean. + ; - adjacent_to : Text. Optional adjacency requirement: same list as buildable square types plus river, any (no requirement). + ; - adjacency_dir : Text. Optional direction filter for adjacent_to (northeast, east, southeast, south, southwest, west, northwest, north). + ; - img_path : Text (required). PCX filename (under Art/Districts/1200/). + ; - img_row : Number (required). Row index in the PCX (0-based). + ; - img_column : Number (required). Column index in the PCX (0-based). + ; - culture_bonus : Number. Culture yield bonus when worked. + ; - science_bonus : Number. Science yield bonus when worked. + ; - food_bonus : Number. Food yield bonus when worked. + ; - gold_bonus : Number. Gold yield bonus when worked. + ; - shield_bonus : Number. Shield yield bonus when worked. + ; - happiness_bonus : Number. Happiness bonus when worked. +] + #Wonder name = Angel Falls terrain_type = grassland diff --git a/default.districts_wonders_config.txt b/default.districts_wonders_config.txt index 32fd6649..01e8db71 100644 --- a/default.districts_wonders_config.txt +++ b/default.districts_wonders_config.txt @@ -6,6 +6,23 @@ [be used. ] [====================================================================================================================================================] +[ + ; Wonder config fields (each Wonder block begins with "#Wonder") + ; - name : Text (required). Must match the in-game Wonder improvement name. + ; - buildable_on : Comma-separated square types: desert, plains, grassland, tundra, floodplain, hills, mountains, forest, jungle, swamp, volcano, + coast, sea, ocean, river, snow-forest, snow-mountains, snow-volcano, mine, irrigation, any. + ; - img_path : Text. PCX filename (under Art/Districts/1200/). Defaults to Wonders.pcx if omitted. + ; - img_construct_row : Number (required). Row index in the PCX for the "under construction" wonder art (0-based). + ; - img_construct_column : Number (required). Column index in the PCX for the "under construction" wonder art (0-based). + ; - img_row : Number (required). Row index in the PCX for the completed wonder art (0-based). + ; - img_column : Number (required). Column index in the PCX for the completed wonder art (0-based). + ; - enable_img_alt_dir : 0 or 1. If 1, use alternate art when river/city direction implies a flip (see img_alt_dir_* fields). + ; - img_alt_dir_construct_row : Number. Row index for alternate "under construction" art (0-based). + ; - img_alt_dir_construct_column : Number. Column index for alternate "under construction" art (0-based). + ; - img_alt_dir_row : Number. Row index for alternate completed art (0-based). + ; - img_alt_dir_column : Number. Column index for alternate completed art (0-based). +] + #Wonder name = The Pyramids buildable_on = desert From c2a247d8c0626e635f9a9bda26765f3c74aa562a Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 11:16:13 -0800 Subject: [PATCH 285/356] Touch up bridge art --- Art/Districts/1200/Bridge.PCX | Bin 33609 -> 36760 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Bridge.PCX b/Art/Districts/1200/Bridge.PCX index 8ac5ac4ab1bc7295c48fed51af2fbddb710b0458..9211d945176cc7bbc9c49b7a99f574000fc911da 100644 GIT binary patch delta 8538 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 je5AStT5QmC2e9APG$U|p>l<-*(^|;rt~Yy84 From 1b477a4caae19b51ecf6da3a2411371414aa9d55 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 12:25:32 -0800 Subject: [PATCH 286/356] Working through art touchups --- Art/Districts/1200/DistributionHub.PCX | Bin 21154 -> 20137 bytes Art/Districts/1200/Encampment.PCX | Bin 40712 -> 40696 bytes Art/Districts/1200/IndustrialZone.PCX | Bin 26587 -> 24779 bytes Art/Districts/1200/Neighborhood_MIDEAST.PCX | Bin 73004 -> 72990 bytes Art/Districts/1200/Neighborhood_ROMAN.PCX | Bin 74536 -> 74536 bytes Art/Districts/1200/Park.PCX | Bin 24080 -> 24063 bytes Art/Districts/1200/Wonders_2.PCX | Bin 36703 -> 107562 bytes .../Annotations/Encampment_lights.PCX | Bin 41078 -> 41058 bytes .../Annotations/IndustrialZone_lights.PCX | Bin 26885 -> 24942 bytes .../Neighborhood_MIDEAST_lights.PCX | Bin 72811 -> 72797 bytes .../Annotations/Neighborhood_ROMAN_lights.PCX | Bin 74125 -> 74125 bytes Notes/district_todos.md | 2 +- 12 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Art/Districts/1200/DistributionHub.PCX b/Art/Districts/1200/DistributionHub.PCX index c9add16633de3d09f07dc210a4c4cebfd80268d6..b042d8b0b14c68d1f4cd744e9e0e70fc600c8492 100644 GIT binary patch delta 2384 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 O{okB_FUh{>Yw>SD;PuV` delta 3703 zcmcInYgAO%6gS129P1H6DkqcfT6)0{z9lhTnUQ|X7T4j1LATx zY~anG2uj4IaLd4#mk27vMKH?HpFa{h5I=+shHLnJp&RjCxMH}T=L>y^Z^PtaVf>aL zAeO^t!?tq~VXoK@{lj*1E#f<3FT@W|;ysA(iCr*kL^{_XR)}giGa`$(AubkMLBjvW zTM(Ct&CtUC&KnUw6YC*o+kPUO`UECKZ$OXWz-E zpoC4nLBX1Caf5=`Z6TxJUbnnVL64vEEhWQc>S;>S{i#PNdBIkDDXCAaqA1yc*4v2$ zH?2d79F8`@M4D8a)kLNm8-F61r>*ZG`IHcE>zSx0rAo*XCzz}mW@0K6C*+Ae!Ye_6 zJn0x%^&IX%Cie>30c!UQta_SvBi5oabnMp-ta>H%A?_EDYxsO<)vHK&>lsXr9_jV= z5_WmTpW-pzOWxc$BhQNdSai}RZFbwcY8%_eLcOCSllzCo7mksvzu72cJCaL1HAOmkW3mp zJ4rs(vs6ZMy~I#tPb4*O-Q~`Hv+ufB7ybUKx0=qq;L}Lo`_r68vLpSyRXQ1 zQkb$ho$@z%Ndo2d+>&U@_r#?U#Dlb@pAbKKmu@89Y+M#Ve7d;Ik9ekCCP~zncqJD4 zE~M>!(IgM~+cpG-bXSC8>m6tknt}CyCN^u~ZCQ!JDodRX_3PjpP%Jm+gKCAg#x*V|hgc#HI?q-3yjWDh-qi(M ziCEXTmi}778xZRn*OWCcc@1J+;~MutH?KsjYg}X3DtS3#Y+TY{4KGEg>sx)nZM+Dv zu5Y!kQ}Kt0b$zS;!zz9kv95333#lVfU0&Zpz3S@?Z+ck9Z=TYgog1r2P#tbP{KX>>YnW`}MQM;)ratUq5p+MVvFw;EVc zC3qFUGkVIHK4GoYGr)S9rwm>1h0th6)&XhJ6ImC0747nlTDUAz%QX9C;_N?u_+v!! zk6YJz%8GS+JD@e%#k@_pDj#^85fWi7S^N*VL{^-OZ&lzP5gY8BhFc%d4X5Rh6zgp91PhZiSCp0bCHsB3 z&B-gq_z}J<#dNjcm|7KU3NK>Eo0VXVk{V)x;AoH@G-IdSt@KD5SJvfD6v*K&3Xf_GUnImGW;#YA|p%bxW^=dKM*3qiY` zSuY&e9m(23wcC@az;@3O)(qe5naf(BdCy7K0O~zvaB8ocQ4{j7SS-WuTJ4Jh-+dme z4v{5D_j$5<$lrH~)xe{D7H~G+%Sfl|XJWBX2$=~s(u53rm6;HXK~W~;u_{=#-wDe1 zzsD+I;(PP9@!=q2zQ|H z&gtbDL^ed); zo6?m`4Vxg9bwe}EL;5?NNWt{YJmHJ!>o}2!Y3)1VgK7QlWIU$3{iGL{W4)*rYN7I! z4^*9+jfAK?<;8Df<){_vAtTEhg0mz@nT)L2NST7H2&9rC%N?l{e)=O;0#T=3SrG`Q z*Rm(za%LJUg~T%<>^Yo2&ruO01o9yHk`AB0WY&&a#1MzKfwXwCa?uiQjvHhRg Z^Uu2TXl2gMD=BL|UF-@k8vMT7;9t)n3LF3c diff --git a/Art/Districts/1200/IndustrialZone.PCX b/Art/Districts/1200/IndustrialZone.PCX index f6a4c6c62da804b2083a2c6c6f5e82993e239e55..d997d01492d757e64325b83134e7a474d9bd1ed3 100644 GIT binary patch delta 6455 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 XeDBXZq4L^6o>A(@{I3hP+@t?5vOdJe diff --git a/Art/Districts/1200/Neighborhood_MIDEAST.PCX b/Art/Districts/1200/Neighborhood_MIDEAST.PCX index bc7ad2bc99019bba421fc80c391683e0fb904e0e..b8df8866a549f00be2908277c0521d434cbf3458 100644 GIT binary patch delta 3809 zcma);d0bCfAID!`6Inu$Qih=+W5~WoNGTQaOFddd<)^fWABBdj4HBv|x?^S-jM*?V zX6}q;?DtpNZP99NQ?zLPMY#9*oS4@u5{)EVbiF%3$H094VLm+374@W#RhH_|%O(~?4_K+`2Fe$``&tndvpr^;=#JRQd=w#uiTyTHuK0Vu$%G(k-`_wt#TET$ z5nX3L4A9VZvEK(X2?6^IMA+iPfpCzo4K8}KG%i-4iqy8fzv4f%!SeDOBM@C_zi6ckz zji!u>MhLc}+e1)~Sx@)G`(p<2;FPf&FxW5-IjCT4{2F>7cAS8`Jz+)@1L%<$F|iX* z)uu^-olE2)rap^Q7Hrm(-b|iUPxb8CCcv&Lpg4`tCLRXEXTe3E2;o6`{i}18LM^WT5rm`(OJRHRv`Uz=i?}awTl(VUFnj7LRrll zk1qC~SBlcv&!0{T+cCd^T3N(`1+Z5aG*Ammc2!Y}*mWV$Y-Z-F#nddWRiP1U*?u=) zY7lGPJ|wDUM(&>jAkDw_W(`PG#fvH#U0z!A53<&|i zCZY3*@|Zd-0E5ghJHD((cp#QVhPNLF$*Tk5;Da^o2Zf(z1zcxn=JAi3tP8N z+qoXG7Td9nLH{vV-nl8>8$Ea%GFbM0WeQS`3Iy@J#zWaCL?cx1n(M~t_Cm@Ud2POl0OJ-_FwRrvi%PJr!?TzJp_h&Q>c|EV=#|8Tmv`! z6wHAv`#AzUdHxd4Ny|LL<<;R=HCNQ1e%l5HM~Pm15c~T&43ytp`7+nDYq2c#>>}>K z?9VlDu=h`I?z-}?5V&?b*M15lp4Z@`9WMZUZ0BM)w)5LXyeVc4mzD!akINwX0TZtH zA|BlpNOCC~tM$dLB0&qaf6OkYEC!H$Q<1l)Z1B~E$WG){eV(`IG%uWbMQM#_hMaWV zWLUq93Lr?CPOvVSWyt#ItU0jjvkFlT*K9i|>%r_hC{S>YBWA>$9F%hFHG4{BY1c9+ zn;p154Kq2{uK>h-H>@d>W!?}0;^v!{uqSRF8CW1^D^hu4zzA#Um2>ArXDMA<<8>%H z>0n&D`k}*a?E#@RxBA0&&D}KMApkLz%KKOwnQwQN+NL=llxpIpMJ-;Ij+TDNPUNmI zEt7MUNJ)~iR!CB)v)3o_f&5(=8+6;hcLlz8e$bBOeg&1f=zJTn|d-{s9j-&W>*gy0SH-SNC4|6_odkROSe&IOZ! z;H-jmxNw{;7!L%i3-;4Z7F9SCeRIC>8^E`;Xg2EbP0TgNxW9yW z+r*c8KtL-?yYoRynGZFIs&c4l9rLg7f@WTT4& z73|TI5Ok!=({a34w^px$cs#2f%e&a3HV|DLTZ?8dX6bbyR4k5r29?WaVfCxg`48%W zPA(hO$giB=H9Bxw>YDgH*RR=~bClc?364hUJUL;zTlodH<8vRr%>TuDuvh#dfDde5 zuEyY(mmYjsr`H-R+xXg-J1CUH;lNQ@jAaLpQ3PF2hOB|nTp_dr;j_tQ9SEPw7pCZu zhVqk7>(MOiR;cSp5b|W7PK3Ms17W8=h0^`x27MZUC=B z2>T4liQ0rhLyTStCydCBWZ{Vs-5{;-S1+1FDM`I4m#zx8dy@^N3%!gfjWUEHV~nze zzJ2HtT@woW&}6zH7@E)-x+O>^G=XjlttNDg3WP(ZG#vk~nj#N(1?#?Kfq%YzDcI~G zfOE7qmRgg76Jj$Iv#>-d@6b`;D3tUiC*v|O_r+HRQX7OPTlre50*t~8Ga9d7DHn4B zg(x!`x}~}u!D?J-BRp;@Eplr+hR@!yt@4X@B;VX~|FJJ{x8?WDH+SBD?EfvHdib%D KY-vuN-upKlG3V&vP^{0P<98`7~3$68DosO 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 delta 434 zcmV;j0Zsn@y8)2A0kBmYv$_N~2eXPIdI7U%4Soi*fGWWV1GNG|lb#%16~Xs29W%kw z4PHON>T*X>h`Pb_GjWp;9bXs-KDwjAvl#^(!P&v!!O|~w8E(P(LNghY?*?2Kv%$=Q zXCoOVBT+)Z@xj?O1{?`dv%%7noit<`h_fs|kA;i1zroYN=)wKL)4|Qb(;{kMCJB=p zHE<-s^uf=<>cPqaX@VCJ!RJSEE4+P<6T#EL>cQ+}5>W|=Lc!gWaR_7yA7{bprNQxo zShE^7u>k=;lgu|P1Hss&lRF_?3PL`?*f&fYjluJiUpOWN!Slh-lglAm4TizOS;5mE zq=~`R!ReFkI4uFflN~u|0>Sf>hB+z%!Ss`{IcWmH){`4LCs@eE|Mtva{? z7i7WU!Pdd*X~E~g_6Ua;lEK;{!QB!q1UYxr%2OLc!;Dc!*tSlLtL+ z5Vm1yf{1h%LQ6!kazbf{29t$7a1_DT!Og++gB(-_!U-277Cu5G!Pb)pK5Z49x53uG c!Pdg;Xu;FL?7`_>AU{!OlP(ccv)n!-AKEC-{{R30 diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index 8567dd7aff6820280d7e82019bc2bd977dc0750f..868f2af356320529c58bda23765f18d175d7911b 100644 GIT binary patch literal 107562 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 z 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 Q{{$=N=db^Nv7_?;2Xz^}ga7~l literal 36703 zcmb@v4Rl-Qc_s)Lytn`eLb$vlhyoxa0s#q<6~QZ5pg{-#*~Jy1LR4g*SwbS?A8o=& zal~mQa#!hO1t+#U$MIToG@c!*O?KgCeVCJWH_h&zZa3RA)6V@aC7G5bSt6-lTjq~Q zilib^QqykIDZYK)OUIKmowjq%CMF2t=icvo-{*bapYQ!XIRvxKg#U3fH<`@%|9|QK z%%-RQ%fIxWZo(^!^UBX3CC0T%bt|v@jq&o|R?hRuio`3A@Pt?XV@2Z%vjyuL#aF9& z<*)GK-{O<+bG;(53frmCM}LgJdF3aSYF2S-+?rN2{;IbAg=&pEdF3zh=HFC)ibqk$ zopM?QZ;So=M6I%uSMFCXTxQA;w zZNH0eC$vX87nmcd%?a9)#*(xx#nmn3U_!jpfesN0oKFfT8s-u91qpxDFuFT#q=w-rhF)q`8n7}z6 zsezs<0OB<69qrSmU82Mym~Uia<04-(?#^xXZ93mKNf5>DG1GqBSrH30=5)cY z#2|N(nUxH$d{Fs_k5zw&5llnEY4JV&%r|iNK32-&z7D~`1bhRcFdLJAg6U6^)>Yoz zsg@)?C$(rZ4%XkGZCv6jz)vBiH>5P43TnLl0te{)B2(~nMeNVu(f^Xyx-XY`$)-p( zN*Yt(l32hOmJXC*{DYdVPZOWLK~0^e%`C3 zc;#GWik}Snv?pcXo+#VUmiqy=vHr_^xv~hI6kI;l7SegG++c`>^dmA!q-oJl0Y88z z^Gh$WOisy3nG{sFCASYFhR_vWIbWIOPpi`y-v^LX4F+(Y>#5-pjR(qlNr#%4;DH_W zd9;NI9y!l{h#TdyoT|!c4XWp-vFKw8{;BvepCTJaRBo(_`OEI_XL|S!9Qx;2ZDh3wa4OlE@Bn{oI}uN6D0C0tpxy+_X5!$l0 zNVd(UG5Pnv7?BHxRcr8>w>W%IR9k^i?rHP**}(Ky4Fx{Y9sszHwb`gHMOc524m)T4 z8Ir{qaP)EIPURvW)tJxw{C68tufz5uv9v9u?yy;HK|6G&^h={DGwUx%Ht8MFXUViB zY;6a{w2}sJ#ST$9$Nvgm(8JUu^AvaO>DaR?>fEjIqmH5nN?v5M5XQY%O4@yGNyq&4 z87R?^hkO~nTj#zM=(>E~FuzCIGK>z?<;eo2Q4g_dNr3i18hs#)**<-`=n?);SSq|F zOzDNO~kEvHGC(nUocNk@<6(f^vaBMg+2} zs7=SU*`yj^Q8w){biHvz=ySY+5tFvrqP74jEE?~SqX3MnnPbn%D+n9T@#p{L`@FIB zfBCx3{|HD})d>NMo6bxZV70JV003%##QI8oG}V$0OqAdaet3?*(QytT!beq6WBiNf0a$Qkh&kKcr1C!aYp_`()YuYh!+rA+=Vb!*#Ze+f&{Sv zQLL4<>1Ve1#`@tzdt7imB^Fgd+EhxJM1=7PZzLo*HUR;NKycT(wtKJTeQ z!smSIpd2*k3W#+JJ4e$QKTC^B+V|_SRP7(B_a$@UP7%SAa)~d&P44wD5=tVFh!S}h zv(ajrGR8sdh#%6zk^{`mrUZ|JoYWJc=wS7ZJ$Oerc@^QwMV>}*2gB2zz*bfu2pG$9 zysQq6#|?=*!JpbxsXS4NIv^Fyh&}M6g;L)ie@Wy2H{*_007(%qBAOtWYC~%et1w`cvp9h2Q zh3bkbSR|my0-z__Y#APB1;X6G z`)e?E@_HMW`PIr5z~Dfj0TwmVkUEeq9PEo9+G4Evga*T2FCG(PYr5k#gF9GSy1gJmT=MVJyog-3>j5`86jnz;4|VuX1; zf(#!C5kP9Hl)*ID9a*-&-jmK;fe)R64|$!9GU?qNpmv%sA;=N!DeZc@A+9}OPe9$c z04z+q2cbY1Jrdi{5#0hf#W8 zmy4Z7y5w$vU!*KB2GLqpdlbZ@@!d<%f?GC&S_{3Z0Zy zS6c+g0HjSgkvd?u5L}#);+ZJ32j#dEuFF%30C~=i{qT3b$W?fn8Qzt6y)fvj zW&*{5L`YMRGVJNZ+k{zdDvLj6^(T-DA{St7me8gHXJtrh1-x$rr-*ET)#cZDn95u_r4LFi1DkM-QN@VR2T)km!Lw@y7;25Tmts@_(7-# zJV9!B0{=n8zXZJAGv0gvS>X$KAO3h#a71;09bD{2^EH04G6}?tG}uxs=~R0TreofN zgQJOpZRf#YtKwIlwbd6wwNEo={9vlOAs#KsZAc6w-ktsq$gV@g)UQ?M_%3ZW)CKKE z)%s|HXdn2RaTLK1NR)B^82}^D$AlKlThP{`0?YP!vI!w7G`r0vL8(TXDR3f;_;y4v?`Ykjfl+Q*FImDidccLOS;Wuw_Q! zHHBD+WsJlWzwY6MW&BLReDb|hD4|^daE|+Sc|$%meeBG@!Bk8gC~2%M-QC{dR~uM; zLn7#GN!Xr|opSxZ9;t8N?PrO@uvpQLGK(#Ii(jrR@DL05io1L);fsbmDtsc9RUDpZ zA`2&lz>*+^>CFj_zN`vv6QYPP9Pa0Y+n|jb9FD-JJ)*B@t3Ii(1PQdgs%^BkX_vJ5 zz9Pb?MJC|oNBEhA)Uw(X=m#DHZWnJZcX&#kzKFI6Q18-~J3WGw6|TdqZljt+ls{c6 zlGq_DWRltj^hAc~_}bg3gRFb-67WS5G!O`BuE1m$pe8)5=x`|7%ET(E2@r8qJCJ( zj8H5Ifpd`9G)6Sj+|>9HjNul42|jg_d9?RNpAz;Qg}59o?5N{(i!TcZlidn%sng}6 z_8!V!9&kB%%qMXVy(8{H@jwBk2{CQCI_r^?CfLU$Vs!}YA%3QTCV@rJ|(^qsz58omwE*UZ;LcNa*&v=N4&Y_;N zz(<@RU(bLy$x_|`C^2a(1pS$^%P%Jz;z8dtPurzLW}BF|I;%s$4zrSs#3dA>6fxfv z{s>i>WnK&<698qR7#feONKX48D2n1|svS{>P~FR_k$$ilPoEkPd?=FXj)n9=)A*~6NEhgYlHj^+!E<}z5Qer*wtR&HO!0|DJ zNCI8O-^=G#@E)*}^nYBqHC;Wd>@U!8X>==B&+xe2DTGc{o;B`;ecpwpDB{f-e z^a6W`=REZemKFLsqvTbA$1Fh-8Lujo9eKChJ&OT+%>TVtF)^}dkvJp6glyq(#8H$yP41@dTPTjsf({|JILx<1ehtp! zk&apGt3c0vcp0+FRnXoPAUy?qLW1}l_#3eU0e2psAgMv81iv+H;T6csi22~Hkv<6Q z^c~!YBsW~Ymw9)keJ{sdm4huzB9I9TsbIzxoYS5Gv;7-@Tj#tWL2UW zF_Dv+rM&JIHU$@AYL6!|*i~eS9&PCi<(5!Q6e$k`R}>P0N_LF@`W#UK005^!-5dF) z=fZP_@CK07m9+pH3W)+AVfw&q^iUU+bqveiDn+paSJ>{ z@B()|gTJQ%N{WOKL9;tx65o=!l2<}-KmiuMq)gpP2H2d^MkE&*`MNHC3*iEJIpBHytY;%p#i zS-?%6_RtF`L!yfG7!eoAaPsFeq-o%7@NK?1d>vd&(GTnL9m^eWPzSx^F{j!!m~`!| z4azCy8PwAoZwhs1={M!!bKr;thI%xy0g)+1n5bi{k##%p zWrUM#!_gcTreItYh!=WZJ8)br1jf#sIu=Qw?w(LOTEkv{a-iYqjNg~4&zVDZXK+Wm zTLeznZ93wfXTe61iB2m1h^f|hvcg; zf{PTq&bkhcBwinICDVbf1CjJ=J*w9qsox!{64QI_Vlukl-2b%A>Xd7OiQumX8IalJ zmhT`L{D=p%Nr(~IWKTd1pzfe*kBf<-!In%F;@K!!JB%G#Ml9}Z&WH2vCgu)nOCKW{ zyv6~U@nnL<_r{@Yc$aDIkx*tuBZ4eunFnl(KTu>6NW{Q6CXN*V#5|aZ;0JLomog}Z zM}(+0C8iyM`}BuM45vXH&mKfgl%zNbRz)G2Li94oZ5cA0$8QwLQ2&E8kTQUYm$U`& zj}anbZK8xgbfd(k93>;anbH;QIiJ>28+ zjiitJ(r1342JESNdB2j(D7EHNPv;55V7$XgNxOs4{GZrLB?uWp9kp+ zgoE&q#Iy=lXeMOR`qnBfs{^O2fKb-cB6vDb*@!Fn*jgwmB9F%Bnfo%z5!ZMbku8zh zECPKMrUD6S@VkqIGeC)AYbxqzd;8R=FKURD$~J3+LQpxX?EoqSJO61ML=ieke+8FV zVrv)$$q-)q5*XwPR2CmkGGFS0fKawG`X-WoXRJaUOTM*4)l3+%mu{+-k06ypL z)|P~ijH*{+AfW1#s$Sf4)W~rW_yBSc*A!LI0SY4r^9Xq%udTXEoe0M14ZsE12KCx> zPga6Sw%U--I6RVo{NMxBzdqz%lv(1ZUV)cYOK@b#^i!R?e$4};h`#xDS%{X@H@`qN z-!coPc>hwDc-gw@9h~>km9n1ry-sg`8qQ{ePkgo@#zC;;$;39zxH--a+ zjEFoSqej-;gy_0a?gq#f(0HOI;>iRej&i&cDxB@qrn_O1&2Tr&?GW;mP@{T7j%2z0 zg)F6A_yhlGQ;pdPLD4;Zk~63+!*Rfv5ot9F@1vZ03Bk;ngWhp%@)R6LBE9$Uvjbm$ zxw>}5^Qtzp=UWc)3@ata+i$+z>F9C*M@T^th);WZJPaa%lqjPk?cj!nC7;Y6V&RLR z(J9n2lWZKJpRel&-Y%c_U@Cnkks7sm&5``yBut_P ze~~A<4idLg@LWzDEdq#?_RXIMb6l=t>+6j2}+Mz8e`(tWK-yPox`c z{*cpKy;F3HPIn&0g1l%I2C4=Qo*566Qvp`&f%k)4V8k2I6g;LILx&LMMkdS}D>c4A zQY{-1|6DP2qTHxGYR)^rNN%zmhnp?89QneorzcPX#4@}Cd~$#HIYBHoXv^gkeWbtn zkh|$QlkgrEYbJRq=8Ab=qvAjupa4;Z5-+|P+x6;L_FLLUh<&Zpws+6lC%zVfEzx>1 zVhx}IKP68ONr2L~2{2AGgYslXazODALN2{Hp74ddKX^SAI2MR`BQbBonNUKBIFEd# z!`ZKh9eZnQ#GSoO-QlKg7Dhz;5Cu%_aeVl-GthWI?ToSMq{qQFzLsZ)fFVq;v3Jf` zv4dC>PO8jKvQj8j2BG9nW3q@M@|Ye--1BM&15txQFk&cik*wz4t6yyG)FwP_+C2Wy zroT_N&soeP&77}9uEw#GpRr-5wLaoZO$XA z`OEv!Tf$E>w~+tCLokQ% z`&h+&k9$YPLq*d46f!{g??mA36VC{7zdEY+!L{idCtG$0TibiPJ2=qbQ`($%-#(`l61O1y$Wusr6O9^}qCP-5XuUnmni})MJ@jDc7|}5=1x&MX zxQ1!9ETC|SFceh;6%37<96We?=hwbK;lxrimcubE;T-R7a-R?whGN3Zeu{Pc#ES<5 zKAOlq{5+%yA?P;(S}fl&GH2whP~307aC8@boy-f(jqIxsjNB^=uvqvP@}{3aNWlB! zxsa?yWb5Z{5xvfU_r==@i~~m z4Gc=JRPZ=~UKee8z%@=uE{0W(W3jc2`~eb26hakk*w%#mP#ys{H9RriQu4-dac>Ig zAk_#VP^b>WnlFBb;6Wg9{I|q(2(=qo_YJt(1ptCFi3?ruwt++;rG0QJ6+-765Sc&J z2&KymrEHE&^=Ei|52GpvVpwz49v~Xuh5Bt@wn(K6;*@2<4Vav63R4s4d6)teJRXN1 z$GayRb`d9m&yfXEX=dgm%O7$JjbI5(djWlPfO_)S8SMgWFs`P2YRC^PHig~c#%@72 z263_SSGe-Uigp9TU@;c(CbN6gUA-tzQQ$?+Z8?9s3Cc!b!z=GYS?U1u29kS%)j>J$ zZp^cjM?sqT#zXRLIMZdi=kx5?xGxcMHZTetAsV^Y(8HC=V_f5kaWn70^M9U@!<}s{ zkRGJYNL?o2iB~RDLmU}8bi1c5x;vb2Is_thH--;2$wK2z!~!e8Itn^S2&2y0y}BDz z7Z$>#>#52^b;4)E4O;*KmPWU2c&Z^I!=A3Q^EXg*qR4 zUkLVwb^OWUxK{Ca6M~l~FuG6h$8~&Dz6ZzqCl?GA ztW+q`n!t0d0^F_@wIy`k6(>KH#zz&az1=;*_mBo2(EVnz#&xmsUMs&3ANNUR``PV! z<=byixE?gK#Bl|gi)Ri_{U(orH-~8`>^F!GA&B|F)li$;D)=v~jb8?pq1ylxLmE~F9RYY4&M&HZ9&W-gtn;u9 zG;0?18EbW05J8ZI1#M25#&H{t$LCGS!S>($Ev$sgyW4mF^AEvAH!AF`lWniNRC((x zZt`RibriP9QQc$fV66?fD=J_wUg1K%k1fI@Lb|`KdI+BuSpFW!=?EAt8n>rYHaFw; z^IS%^_ZyKQq3=2d^gEH5kDjVG_dq{ zDM5pd_UpWziHB>p*;9;%G1Brmo(@lNKM~$F%p+sP8YMDR7Id~cgJRGsH|o}~$gD@& zzetU2Hvl21a{EPK?d;Ycqd(rua54dNVQdN&5KO7+QQ7*7OA!F{;f$k%-b0EB!qBw`#Or`u<_(jHRJLB8O?*T zT|C5O%QNjF+NykJYmk8TLqF#6TqO3KUFR_pJaKu(n(uZ+>U}O>y_1D}qOUPOVRhQY zl&{t)*6#kDAxhJ4SGK=#0f;)g^$`|JDCMRY4KtXdIlKoTV_VJ7v9M@7Tcz=%s7RFy z`V`Fua{zu&^yJ75XVIZa%ds=KAWd|$DG@yIHw0^l7!ES1^FxtfN^JLeZK+z(=idgP zi#9as*ctD@hq_1)=aJz?Y;J3}Gvy4qs)G^TY4te6q7202OROPPFIsoAf1&f=JCD!| zlK~4(^0&4=A(w$blDsFILtEq&rh(8ByhKAbEECHlG)~HTHZV>=30d}%LDXn4DZ5b* zT4IfEY9GOy6ysO0xJ`JXbt!|qX&aObTHf+~3;Mk~7*EMNn3AbctdVEDVux1=+aoEo zCu-9+Ok3m6YJ8l$6+=lX;;a?zZJ~&>Ayqr!47Fyxa)a2~R-3m)s#z%IM0+KANOrex z+kGDK@9oN4Aoq*V(_f;KSljqpvW|6q9_vIj2ZO)q8I_Wt63mTSlZlBaw1)cu1VI60 z%Cz9y+1eC_vWm#>u}%t2LuE9>O~RhX7to9VS3#S$WGKbUc){*dT%yIbMP^>Rul?6L zyeXwd?x?Z3fR+otxeXgEz)|lby7_>Ay)al`+bXtIJ3}cZwuf}IQAP<6en zp7}yP=v21LSlM_FSqG_3LtW?WcFd33a`?{(g(Q1}vG7O|pDSje3)07efje5?$KUUB zOlk>bJELj>FS4$p;Xu%NG!-i0Yh-wk3NoN(N@O>+hxHL{35&*mi2-i|B{1r?l)Moe zQ?_}-c4t~jb!Oz6Es<1M@hVaWHi)1NPp*Qdzx@HCmP?$Sh#haRI)k+#XR5xs-q{d} zH6XNcgz!Ra%TwO^>Qs%F)nNZZkRd&ABc1#hGd){(8S}INqd=&R44qU70$A6nv5O)R zqF`8#PTB=(K_i4y!tH&171NuFswJp=Hl9Ea+Z<#8)i-N1ZYuiD;%XHnrN%1sBUnQt z0tjp?aLH&gXiwWj$s6`Y)dbs83`PW3s_Q8iYwz;cxGkxOy+h~EYy4kcMmTeYKRYlO z8wf^PMQ6(ARBNy)#Mi3N>0iM|AQ@pa$R@l>cRG&a#n6va== zd6XYjyiM{zHvSMM!LTo@Y9G|+_1n`KDV=H;)duuC_id?FwHcRe@7f}FdBrX5cAHe? z$F46}C2P0KmWv2yu5#b;81wqDamSZP`QWCUp&(@b2;jg3)Yb;?mYD8RpYl2LOb)i9 zZy}5w9@JN_^X+Fzk#AtG_i5A*5zOeDOW_#a1kcu!TG=ubLZ0*fdEO29A^XP5i_kFY zAy>(#?@?4fP?F%g>O0+dpI}dA6D(9b0Mej9Usd%sZMq9WSd`Ls=aw3;((bj^ zTb)_2ftEF!Q>qbVw&fWovqx%_XI1Av%Y^lOyW4yT@yZlG;Eg3&%^OF)tE0Nw9tyOl-x^YFUEV3F#yDdN}=?NM!`L+z8J&2$<RB(u0RbaMT>}gEp{WmwAOTR&H67OGmVnHr zH#BZ>fttFMnrAxfVB7Srj#ytYq*S+i5@9GKe5OXJdEqHn*e0d7*do?#r`$4_Mz&ss zYrn~T$w6<-H#iUxTl0-pCPu(bQAf2?+i*EuiP{!fZw=;Uci3j#Qzo=)_jgU9zJ8Tl zr~&P_&U266ZM?N8$Suz`HKGjJ%Nph0Mx+$TPap@<`^+UikNaigC^w-86R&|}y@{mP zHGXiM%yk7F9CJn~Om<^k0o`J#UkKd{HC&Paim;!LW5tcFH-}M%LvP7E0GrzO2gtmB z-{<=G(c@}IOdN3)#Ba!b#YS3+*ueS&4Xm9#C7pWels%lTk=wme=)daG!@4|8E)!;V zDmLI97%oJ7jW*fZ7<2~nEkP!?oNUZDy2C+hur{c;b=iq2P&9)J=Bc0bis5Jt+eTC- zho6%X)fiQ1AfX9m^hSYoqrA~H;YO(qU&ItJXMEy{(Z#OKda1fj4b2OsgW~}`&b+kl zI(L2nN}gg_eJ+dgc%v?KV@*R_2I1Yu6{u@yF6Bj!ExO9!ys%(8cNa+hb&X>)o`^== zF2oDZ5A0CfMY%l|@fAh8w&qb=&P@p8u5AS}+iTH8Ni{DB;szG$E|Eum<1%Mw z0YzQVQFelMHiKF8N+WkbXk{>Zk*#N2dh>1+sbx|sXdN@cwl8h%DprUwg!$2qG!k-Ix)SIw%zFOUn_zJC z7nbAWp@C3jAeCm$RWydZ?0LJz`SUaaqnh8VSc z-_tlrVGcF~ilir-(dVMk2@0=>9AVA~>Comw5;CL&i)uon1j%UODrabO1D|4T1RD>L zvZj6Napz!Uc;GcxBqiHxd{H^3E!BFhHgBR`iKP{}BZh6}?v7A6W-Z##I+OMM@HVJ$ z7R?Qh~qpC_GLIDNf$lpOOJP88Z z{sH$CTZYPYL!f)gZZI1PZ%9r{I#C_vv+DpIEiSLkp#S9-M^`{g zw%5}emPr7NJTBnUhYQbQ;*`=-S`8nIBxSRMk>aUUwxn++v}lvZS5W0(vq2gA9f+*( zWkiG5_#T2JtP?bNhgQE!8nn;J+=d`hi(}tpe81YK3nB+^j zZ82=mIE%H|jcRS<_dWuPpylZlmBO*=oIH;GEH(9go?xprtjLyTOVIAN2Wy*YHxyMq z42ghFDVw_A_%Rap3n0L^&R*m#d%-LZe;c3qbdEIC%{fxhgc@HL{;_!y@%9Svz0|8X z3t>LOv$Y$jE8gI8xIPtSoo&IkU@!Wm7*upgq{gm3e9J4nzt(0w*-yU!EeW@%Su?Gw8YghbF2| z8?Fn6MynK5&_T^gTfT)ZL4*a1Y|0}9tDVteb(R%>l%?GySfzn68UTgl7~v1?iR{zH zZv3^pDNnYj-!q<(@0?~74Lpv9R9cEa(>*qW=|}~1nAInggX|qVPX>F1Ho*SeBgiesXQ$n#|nAJUi2vVh=w?p_%dHfB1cfyS3OCqMb3nGI_&kgTej@(|?o~D45-(RoY0u_%Vv30PsS4NM&@c zfR0iQ#pno4I~khT4DK=Jv6*#mQUM+}!D|r0&&3XsFlupB>DnwpY{vD-(iuV9P=G#* z20A1^ok-j(_ju%RT0QWk=-zY}i@Q3s$B}q>JYK8V5E$zsi`Z+ekzMU17Js5O+-fhK zZtb^{VMAuCzjqJm?lSFbE|KsVwF*wY-h-||!XTAGh+olzgdPGzvea^e=cukDn}!bP zDs(NX{g6?kn{K1o9R}|}_|)GASpL3#cKf-r=eTDV?e?QSMl{pMZZu-zpR@8N zb>TG~-r*d^K=my=r_U|G*^+lOgm3IIFXr9dAK@+wJeo$+Ni>um2w^)PQ(dtEA3DY# zC!G-)cZGXyg+6gv5!;5ONRYbfmx`V+mY&7afACZg;)TV2ss->fnkio?qqAFk7&n~X zD zlq8M(>f{Zhp|Snr0??-OVMrWz#<;LUbCYloIHA!lfpQVnTHz)TeieUIZ3JPoZvfDp zW$5h(6>tuAH$%8*8+h z<34S*2~t{evvxE@8WLLjUH2#Oo%4L)%)w_%I$Fi!)a1;{n>%Aji1Y(GL6OlIGBt8h zFhMqoz8Bi{O&=;7pFft&$OT{s4ag8P;9@Wdwd#}sSD%Vc&}}xeQbO@E0TCCV zn4UyVY)*owzwYCaV7Y-FbG6jxLEpy5FuO_QSiRl5*aPZ=`Q_QJLd2Am8*|-!{D)^e zjjk$eA6utZ{ENJ~JKT4FG^ z21s%ACUHQlXdcC%Aa9_Bq`t{1_mk031A0i%icH=3a9!cZm$e5}b~h~Zvnd-@Lt}ar zlKsQe2Db921>6Ox*$w2#Sfb2l(Orhz8qL3;6F?Y!h`Rtk>|4j);)YO^q&^Q}l1rq* zDP;pBNaS-^88$M!bMtsc%7A_#x#oO#BScG?aycGwVYh;fMH?c4Uov@Q=YM@RW}z5)`sx3^+kF#h^T&t-(OF0t3u=F- z5MKkph)1y2KQ5Awh2P{#b%&^I|l*!W%;D=L6P_Lph1+e?@@xb%rYCN3^y&hMSY24&^ zSK22tbr-ZS^9^mqs%<2^eL*&Z_VyK&uN|u9Qf|9a)NOQI6~*8?Sk)vl%d%}>HKy~3;JP|vHe#OC4lq$e5aij^py9ByMN$OK!5%r;JZ-@)R zVlxocA=)oYt@$^(M~2VK!v~UH$L58mf!lS4^i z-p7(LS2^KL55#dvkVO{fPtj^F+5=D#FxD1ueN+P0bt59BO)P+%F@ndegiRZ$doN>j zXgN&&MWn-bSSLiGeR;OzNm0*eu^k((UD_(@b+oAp(+xvShyGyV5RgPG2Bm=mBUEgO zC%!v~X!5~1gfmnd$HK`pUZ>?Ek~88N{GQD>>(uUVoa+GPP+M@`9mKnfQ0)Wy8g4-< z#eyq_O}>r7JcE+)_}SP?E!a>2D#rORNKCOi=MvVIP*|VFHkx?Jx3>+)Pt8%00pNgK zkU1lU0XyJ6>(~*ktqKm>ut23d!0H@%hiEtUH(WzA%;wG}HChirc@Jxv0X8M##)%QM z5_g|6Zxrof2vuPqf8w^FeRu+BLt_fiRvL&MWM{?`(Nvra0Y9{mZq(~A80wXwWN##k5EQ@31Kc3exQdzHL)1i>8_Kcgx$s+*xY0f1weq zjvW*Dfi8^rKwFWUSrZfJEG|f7o*lv2Y0st--l%q=Lz@q4kB~YP(f_b)#Tq%x$C9Vr z5Tx556D+*3=SQ%l;Z<>T5#oVTD%3gQK22LmPKPmfG+|RKKGrd);!m;JeW(!_L9-58 zL$QHt4_?QV(I(3TY%xI2+l{X1Ml@XKdl|B^ZhV(Sx%@tsj3=pU;W1095Y7S;O~{Yp zQ$jl(pFol7QZ`@TLR5iaVw>m-)B*fe8T8DsvyWk;$W^q*&2Txp=SZh5kqqtaXbbP{ zWS6jqNxPP?H@m+tpo0~=N^Vh48#GOh3#SE6p5IWLVd_oLCt7+D*4#FF?$4p;&KLrL zmj(h42_>WO)FRi?+&uY4_8hDmt3sz~jSFhT^44{%?xPV1`5!SM7Ia{8hFVeG2cjuV zS;Y1k=IN%n;m8E6i9#Ca=7KRW`Kz=LG|m4SIs@fCjfscys4vaN{j3 z*OQU}RyDM%1+|XG(~v{=Y1WM%U1XlrP!C8^lZz2_5+Jo@j34V30$TZDeC`*}K^_*c z*5YnNEADmlU*K1F*xju|#260t1K~&)l&$6_06sE9Y;6Hhu|779ZdX{#9P2z1K^A$4 zV1exX##!S-6+ALrj|p-Z z)9UX11P){Y88Re!pPHC2P-u!V%_jrNKJ8u>ug~q-i@4O#XD@0j)OGhWJf6oRb&p>} zrC3}3(S5vxRT6^G6t;6rW+~31uN7Ny4P@@Kd80l31EZDveLPYdI6jYrm?m(AtZhk6 z2I2;yj0JTXJ+)yq!yf4XS?UYFj7RG1#@ZP%?;>p{n1X7t2Lg@sI2nd5;FK-I(5wl& zgXjQU>h!*bM{3*t6f~;~c#S$9w54YQ$M>R`&W6vLV!(f=3y2|e%oB4U?_j(0JdV>KR)c-B8CA#H(%Xu6n-0kS`s0F!aq9{Z+)2pYPwvGPKX=3510iR9T)HkxaT-tn%ZyG*?av zTg^G0&h4e0BG#0WmdrzSLv^M))S|( z#0=tTxgQlnL7Ma1QXOpGo+RJUD&pneX$_xX!o=*X~r zet8=;7p=q2%!*UAn6|=nW^t39&tc*Jr=N8wtgeaX zKNPMrg;^asQVHjmdA6i`uHgAybgTW@v6oWYc6a0sUxGV-P^ml5tLnx^BumvG{z z>pgTD_HAOi|4>SN7iR#s{l*o<0&Bn_whh!}Fd1i+2p)K{)dEpH9+9dfx{tZeG$fk) zhnj{Y^m!6%z?&<%QBhYcL%1bwyPc8ba8EM}pTkFPBG#*IWeEPTWlYEU54@Fbew`@$ zKg3_h(Jp@9^GC0OSXKZ+vk9`RVq)EpDThO5Ww=XiddQM{xsa=?GUI6Ep*k}(Db>-X zr*L=s7t{S_3p0mJ=B6PG4+CVTt)^y^=r)_&7w{=MN<7qp23u{?GcG*i(Rdqe{ySV_D_Kim!VHO+R-cxvz&Qog6Dj=yJ%rz&scv|P8`KsCb6K@Su%9lD=@iK z`fwp*7MaOyvTUuBOy;^Fy>6)4BD+mH!*1&9{}3{6!@YwLD1vzOM|p%+^x=hCN!^#R z{u5DO8u(_3?Y8eW;(IccTuQMhnJU245+2Ina9_ZxZm1wehTP`DNPm?jXS`M=mFU z+tGRzpP+Tl{fTHK;cS%oa0q^@~;J$XI_}RT`QkGm*^1k2=sc5E~pGU{@Rbx~-8gwac#9>La-yNteX?$cyq-_SDV#qR&A4sQ0pWPj9S2BNo3t_`2 zG%7Ln4s?b8a!T0TvvUwOK0H#8hYAu7HLvT>4V%neL-el|jIbd(FZ&uFc`^3#@qt)6 z%!ZnPdyCs-Gg-pS=2rIMg{^gLD^c||-q4DFt6@ijJs4?crSDA8UN;EOb>KIS;sEh! zxQRRb*T14&81dpTS2Rf;y@ROeW8h*Rx?ULuRHlc1(@yi&e#tg66vn?HFlb?6HnP(M zD$TttnQJs&^&#TIX;#S;sz;)Nwd0qS5mtL0zU5;+knnx^8zZ@n-Lh3X|1ko?J3xp}jG$?C z5ESVvVDm$P)ky^xm`fUVNkb;l+MhAQktned36?N)4RQ7~#{uobgE7{_4h#>-bzzGb zwrsHmonc@egk1;5Mt%_f(1@ocUu!4su){vSqc@5S3MV3<3cE)@>qoldXzh-h@HL+kIBc zjz;@X6wu*Fa9>#B?9e?H{1n(FY@?T)X+CW^6sFmBe;l6oerc-LzRSmUm>ET z3an>j7%me3!bgr-a-ung(T$ojCW}}VX6D-{*j(o}OeURTg><36kYi)9fx+SSjAf_2 z3T}fF+34`dSU6?Z;r@uxnA`4`!aQ3s+7wWLa zCI35L@^=J(dw(rDshrBl4#q?Cpsf||QxC9#)V32ixA*7@3Sd-ah0Id#u#v9eW1~I3 zi6ZqXLc`c|6~gFb`th<#+Z|4T~bo2^JGzy-&?&=5Is4?MBPzmiIz zd#K&tAzOcA4VBDG076bA==2Pa^`r{`Sz)vw>U1nV{*{f48K8#1hD^iu8gUlY@H^Z& z{8IYG*nwkX;VQ-K#wT)PgVrH@ghB<2TL#-Imhk8Hn|$?(Tc$H>?(sLige(>N0Gu7= zkhSm2s-Klm&YtqXGG2byx5K&wd;Bqf@lSoDpYu($XLkP>wdsq5(MW1EmwtDI6~+b% zqa!_qcSlpUoFcu9f6Kxw!PjI!QrO+9*o}Dvv6#N#`o?w!xoICcYE@ z^;ElcM@s29vVn!B3qZ)bqv_Fi2UCTS!pLB{2M-0Gl&i_HjH$Uw*;=K9V`8QsBTa{! z1@sfpA!Gx?>B5UWW9SJ6QDe`k*;I#!X{gR^u|fD4WDqf-3`Ar zg>A`1C-~*rVB3*zzxfV4yz?Q1w$3|QeZ-eMp%lLIub;QZ?e!hX?wW=j@&?jC9rUQp z7P^kJcLxgnBL~3LFdjByh3_z%Y+yjJ;;=hn7A3bB&NW*UwZK#jS?3M6^2JZl;F$4J0Ji;hkeN6u2QNjlm%23!g!{bSlEFsACo)3j4qf(J}{mD zn~#hg7#u_JFg7xf4x0g8n9S?ngMnF23hgKf|mDb2zPZunz{JuJ#c++A2W)xEEPhs6zF z0=u9K?8`2`=`KiC5Yi~ZN`NFP8o4(JIo`&yVj$PVG<8i3#%{z*>1%4*ZEqUI#7#|S z7DX&1io_T-;X{qls1!-kHffpro25DZJom5r$Nl3b$3%2y-kE2fuX%nm&*Rv>XvL<* z2#^jctxev{`|=me4_^=}F04WtB<+029>EH5uS3q$Ihq6ehG$k$ zW2bQ->49uHTP|<^0q z9cf5Rh&p%{){vkNp$Yd;sAD7Y&IA}~oMp#?Xr>z4;Ak|f+Zvn=LYdPX79JHG5)~RH zhDKR&PK^jk!p;TJN-Uud)+r@_o?uyR5krl6#`wiX15&1SBHZLEM-FOeKCRBAi(hNx zEOeXY{fo(dFj~1W*PgvjX%^yQ=F^NA&HVg2N2<~+pa?|VMT-)j0|D=)YKUlE!J!h${Q?VME-iCl^Q}b5@Rybjp6ylH?iD4 z7#zD=OZzR&4rfPq2iDeF$Z$~6z#QdJKuE6w$AhS8)+w$p-7p)o&YI0yS+0^i@o z%E_?4K7DaE71Sz|HQiN`ir0|#hp-+){BnL($BIW9aD6|4b-Kzb0@=AdNHKPNd#7@} zUT_;8G3e_PGUhAO^%Q|7V}w4U@GAC>C!W?)b*#Sa zib{Me<~of3C|^aV&Q-CxEPNL(?SMJ>o)l9^We2C&A88cUw*j3sIwSivW(Vwi6LWP*g-g&$pD1NXfw4O zkVI;MVRJTiJGv!(NMw)_EIn64X%@epRyAtrT4w|ZE8rvv!R5hBZ#b)WSg=nJ0 z5JX|NKqbvu5QLOFvp<1*g*z#?O8lJ#vPRa$JvFz$WwN?d@e%QcjF<&vqQ0baegJYuT7IK(DLrrFV5CUt}bCLNQH~WiHFT7TRSq!> zuR-CmCV~C^QStXyBE?+dReqG_nINXX8t}NRSU{|(j82n%#DsdPh&i3}c0%LJ3K(c`aUc&+4BZdj$+ANLvJ)Z-kg5FtrVv`YbJ^(q!e9(bnpT*E>kSU zwNeK%xWgD%simrBoJ=inckpZkI&>-Wvw@6bI9pw0oq3y3)&e&pNWX-1W;n;dWNpEF zlOQCk`~-(75SrJC06Zzkd|J+Z!Xu*fdB&)?A+|apP%CgD_lxD_x4;o zv-0QS`9}H1D&@Q~a>ioJw}i&b*VJXjk;$gIat^1C7S@w$6v8++3MQ6GfAqfv0pkazGJ&ZOAi;cQfc z1ekeC>#EXKbYgPD@wmE#Y^r2p3@6gdte`$7v4HA2-K9cpM}ruFIawiYhT?dNyP%0n z>D(Da8Dy+d$I7wF=HO&<%iJJVIdS;KZiYbc@iZ0WyMy`YOi1-5#8F2QG8+a}E_;mI zr3sicP^zR8vl;c+3tR;y(V$NbIX8v_`z1E_=cySf2{1HJ;U@j2^5~d+WlXwQpFbxd z@%hBLuN2I{yFlxPwXvIItq@Liq`$#Q9bC*qX5mq6!Z;!w7nxA)j@9WM5mEQT--}7O zhJeNxN?ahb>M&M6oIGdlIHDIHvAL;*sVG!XM?Dr}ooGm(gPIX|95aKp4srE@WUeNt zJaX6RXn?eZR3dGn?j&2ZA;Q8FQB%q%I^t zT)#B69zZTQz+3$h08%W6 zSH4244W>C=f13WobS-@ODb!E&GbH5T>7fuQ?q5Rf$N5jcpT-{|sLfl{@}4|n)Ny`8 zuAhSczL<{3S>}vv&0CF#)>@8YOyL`3z>W0OgZ#(OG2B|p(pKkLWpc*P;yGRiM0e2D z;wA&V0lW^?bqQO)Q!BP;O%_Evha)&F=!I78XQ!rR4vY4(c7?^HZ4tFQ*W-l9r*dst zK81L`MEboaAz5EcE0YhqFiFYdRjR_mdHDV_z(^jMvK|CK`*8e=<-Ja_@GzCq>ydB- z9X^~qhp+R>bnHn$6$c3Gp-lFi3V?TrOhTv!$*%)JaG#e@tyJ4USt{zg@j2mnp@v#XOQ_d}pfE`HYvM=UHS`#t-sVTARu2j&_{h{ZkRD<)Qk76j z&bWOl%t=D!OcY#;;5~RcSS-&6G0w&Brn~qcNIQb^y#OY8V-^1HOsTRHzmMj#8lCPU z=#-0Oub6QE05lk9itTrFlzquReiW0>?>v&$2 zO8~6MWDERf{)utY$Ct!u&NHUf^)gfdWvh0-sXuO3%b4aoj_zE<1o-69iQntIOZr;K zCH@)EZh#kx?H0PofQ$#b*bG4pN^p#ay9Z>%OU{#9 zZZd%{+~vy~1`C3kYE%G5V~q?4UZ_TqW)6t)ltSb#T28oeBj>!AjLaZ&?=s{eQ3zeT z8#Q>xf^MqZPL9p#I6CqkTqj&6#XEy8G9;7H)9g53-Wk9jx)6XZow=ydBPq$Srg3+d zfPn;#cD)P6LOU`llc6rWbHr8N_u+tC0|cP(qimj3>I5_E+fgBR1F(^u5t}(|`5U;$ zUR=W*fy>nIOE}GBzR&?M#pZ~efRs0&m~XqD_$|&-NyzjwEl06VgTIg&Cc%fJ#~2yO zZt;Eq*X0WA2hPqQo!OHo=E~$GrYeYt-a?ExLfHr7wT%qH#poxKJ*2Pe6!UQ+odSRG zHlfJe0Vj*bPF7xI*A6pQ>=^h^~`i8L)$)tKR5=oV!jvyVWPtEo;t01 z4y3mhsRO{`<-F>zpZimsv-?ZHNatk_89K#ePOV<>3W7Ci8Wvg$-7rV3t?N0C*aq-o zD)Q?W6b)o--jGMee)pBk408zP9&tV06o>3GI6FgA&G3*8y=DgQ)l5IN=?fgDNJfs{S z#Q!AiJ9xwcO|7j3b6$epJGA+%id3GMh$ zE|?qhz6hsgj2++Z*2UpVElwN>C`z_KmG)nN68?aJ6p))-e6n5eiRFZnGlnXBv-~6I zR20AJ#}-ebi@(#^f{(}P+}n?j!Rz@D{w1nCZ_-E+p>;Z+jdtr1Q78UJjCUFS8X|Z7 zZYrC+*h=Oj172KO#Cjz%#Iq!R$S2nbZOwq~blZHoy3hzogF5Zv2s9wFEAWPBf7VL| zpq5FW*FEVq3vOW-^(H9c@>cWkJ;ukFC z4ngv%Rc;k8c&mW~Y*gpzN8zY{ihgc+JGtWp1D+p&bN&(2=czYBMzF|s5na?iNpKYE zY?dqV%d!3EPvI+fwqr$oJXC%TTSWEq<0Dh^+NdN%ssjwV0*HS|#PV?*U2Xyw>)Y0o zEw`Ys@fh^P2wNTz5=__%ti-BRM+#JG%Pa1TpD*gKLb%76+{aqv?4E6;&wew2_grO} zRK2-b>zA};l5%}*raCnmcM3FDahm%KhS9Dd!!j9cCG;eduX1taThtt#s?MxkuawFH z9XI2wbsa)=mC3mCMO(=r0ssoQFQ4H!_8y=CsJe2EMN*NzJZOwaqd~JK5<)~+-6uHk z-sb1nejnKHj<@wFMwt2p@^XdM1)4;z(HM-8P#fr~k}R4!%2wO}gMWf{C)v?fP>f7+ zB{p~)CsrQj;fi&;UTN;e@*ULRXu{9Xm7gLefRN{)?C$jNEGZO*qyGkY_8Nndg!*pc zjX~&cvr@ksom8KJt!|(Z8d7-io=!JNF@ZDvZ$Nt3mxY!#b#$b&E0mB(l-IC13EOiM z(UVC=TLXe9b&)gNJbea!xz6?w=Crj*jz~g6yPT2H>NX3knCmvS8l;AyBWJp#fDWLo zlZblsBf<`?-)fW)Pbx+Ilh}P`GQucd(^5I5(Rk z*JXmh-N1QD$-rvKn2Wqr6WEM9XoZ&4L+-SKWCQZ&0bt_}PoyQ0sdinh#3{`CE|X8NF3hZdoA39CKKem;3x(qbkyHf6KY(=@yW))y zhq}9P9KT|mUFg6G_RbfW&;(~O(HP%l#2O9+KA@1afG$#V%0I_|U$A#@`(}W%A+<$yCAYvF)+@PTNpK(UaxbTxc z41ResAYot^Dk(OAt0YKSOOK+}EtF!6OfF)RXojZTW(1^*>k3h$AXD9{F4>D6ZQ_Qa z28IH^R&jMP#UR3*GUq1(dIC3uYG5X6K>c>8A%$1&Qh_uIjmT96RM>ft1_C?dCvyES{b%9~_zYfCSaZwEBp9!)nVIz|T0uXUrO5=O>O=*jR+ zXuI1??qw~lGN%()++gxZjI1!Zz3{zljgg$_)UceZ?FmLf@ME_Ex_UW9Dnh9)vJu0I zUbjh6L#99*HP_U)*fHGM_D*19c(j%a(^het16y#G44DtZ z-MJaCHXw#MEWIB7NH>0~5aXOx3(H$zPq@Lk33JR2o8c3jy$#ayaQX_)h6kccr524* z-P~SS4rdnM`3bj|H(0Tb__Jg4gyb6mYx$Fa3KPl;+nd!!O^Z|-jcFq(-!&LZG#2t= zU56`*ZeY{7wZ{%Cnk|w}Qnl$d2tI-o7}>#%{WWH(g8MLT(j^ztrSUuzhl)#q(ItZs zt=m-SS6YYdFXP63radZA6;%Y9^9LLO_a#>nW>cRkG(7SRuZWlcXlUWi%N3^1w5-T1 zpeYYM_E7wlfZGDi3&-9mdf$i)4^>g$Y=mj;U~=xY{C#`=R^l>0`$rybFYj8GU}+`Yq>x6 z_w9M1fQww@$=Ny5LNbt?s#&IQPf6Q~92Y5eY%upaKW3eas8*MkoVGqKXLdsT#{qW} zTMKcEYuH*)xEi%@>5@1Va)#mTq2~VPKlb6_wF|6tF|JBUqEA0>qcB^#C*5XGO&5W` zac}xED_^vGiOZCoFgs^%Ax*s;@TBn_A@IV(v%Gs)Q(S}A}QM4H9&=i%E-r_Wz-tzcC0pYw!s$G=48+AXCku){F;9}sQ&;(Kd z)WmK1D|PtZgaeo5mlTnSY(sou9C??V?BvIVf7Y7^@^DNYJnq%1Rl-3sRMe2NOiEp| zH7EWlh-&G~x#$C=?n4gz;Tmd+PsvFsPMI6ePd)rtbO@iuqonKQB^AkYApsaPt32Zyp;QV!hGrYS@Gl=e(q*dy-1|<0pQE>jTX@%fv59$Pl0E(si{`UG|IJb(1eq3`c|@b?JkSY}Z*J@$s1rl{>Ekq^URDqYFE zoF)AB{Qwp~uj0wEOHB(~-=N2;i*&rv>Ay7MFB8tO*3f!#67^mng?!=PL?Llo5X(m) zn3MIPt-RwmFA^@WReV-6spW?;^Hsb+z&9@s{y#7|^DIDl^U&jO?WkCiu0QKp+y7$q zdnev{eOKLqZ7UD$s65v3@Qo`U$}c|g+?EGdFV8Nrq^vA9{^9-SpQte&+WT)qN1wcQ zw7hSB!COau>UnN$%}Q~lBYVBm^5f@9%kIy0x78edb<5HH6<-WiUOe5=(o}w_@978b zH$2sBzkT+JiShs1vF(xmU+sS9jU7Mx&*qgS#+RR6`Sxq;O6)0ncQ~H^-qOZ8y|cnz zULx+@)v&&<_@`apy}xjA#RC>GNB_iQ(yMzPTD;LVDM zS1o94_|9Q(`{0R}M$hm6k>~MuUa$Vn^2A+jOLp#Pe5f*OWm)?Dg-MUC&AD*8_N$u> zM_yg_>ORYZCE7hNJ}VWZem%Kw^@G{Q%=w-@4;Fq~Pu^TxUXoI|GHvswoLBc3@7pc4 zf6w^Rb7>Eh$Rf zbbH>ued4ZNS(_VU4|J{i?vpt?p3UC7FXi&+u7igvp5L8g7Sl`1vY&ZwK=CHSYmd(l$K#!uyb$Wg2XwkKd=_u zm;Gd0<$Kq^yK1dqDUM!-3abCU_>nD1*`~CgzV*x#-_P0Msl2~3sbb@zBfWdqZoc2! z_r!fAi_-E`FYIseeXygXGS`rsc;Kxj%Zdf|vc*~V2@S0eq~y#^Sfr_VwD5(njT-X=cg}6qA64}fo zI*Ai<9HYjNjKh1Exl|B%o|jaLe_!#X0t*poh-jEE6W?e5Xv~y+CE`H#V++>H-$m>JU2Yyz z@b{7Bu!M79oqQc~2c&WyOvX1L%b=9=VlDg=fui?fT`8MQ6IHs|H z)$<+5^-!m=gw^tLA$!rL@To{5EU4|I?JoXL@E1v%uhA6N%+z0d&x z6Rxo~eLCL@5t5GLCs;Jhm z7zg=bm9*6YAB)5MYnA%d9vF+Sd4U&Q^5*FApI^V}LBqUe1wKhR`bTt_5+===IbyM1 zaF}B~Vlf45o#RBiJUwU3Vy$yLsVdgy-c+r4^JP@Ua`V+x-B}jvsp=UPA5%BFEh4E) z+bxr*dtH_XsjJCW2dLY+);XktudUCJI{fEeBGoXPT2fP!%>z=|&+oO9`p((DCRJXr z8z!}0vVTb`zBq4y)P3GTNvc2RI527lD0b>2n|w9DpX~Fca~~&#JeTJfGhD|$PjOSy z`lR>AtQzAkqvvn&kkRi3{6S9nczX^}o=X?>Q~ry*254Uk7PixV?H1Kj9VRZm$GXw- z;t|xg_y?-nnI+#*J%hcAsm>FYenIs=k34$x1uZ*D{c2y9M12fj9!dReUA~k09^$i& z^ibv#N;;Xl!jJTmw!({Cb!&xls`^M5V?WCDwxE9#C8`($@$eVjrsM}zLt)HT0{=fS zm(g#W7GNa0P90SigD-ujvJq#(4d2-#LrMu9zBXhbBR@y7P>A1{g>wB|X{!&BN2ip& zzYCQn$lskxmgnz7rF`xmNF`plGL%aH&B`!piNUI9YE|~CbZX($fJ|!bR{=+;<<6^% zF=zFZk|B>uBros+>%z}#1qJvARj_tsaY`Ajsb;On;*{!J)5V&Q#VPgt10{QmEKaGm zwO#BHvN)w0f(O|{WN}J0gecfOWN}K>tdp}l$l{c`x&9feKo+Oexer^(LirmyX)+z! z*hCx|+CXapKYB)Xa@+JzvgNy*o5@ZSw{(!51mw{P`s*M2$xbh}_K}@r+nUKvHDUE+ zr}H1zk(~~IQccIrrtKAEC;J_DsJ2?+QmXmg@Dj`)H;Fff!#mGn{_2~A%dSGqUwy-P zv@0L;SKmD1cV}V#>Kn{?Mae0h6*9Z|Y?_kV|vF7UGVvIh<@)%pgT0ClUQe4fx zmG0ZHZ>E*Aj;DAG)xuk`4qP>O#TsA?j&+@0#n%Wa**Ok=jt*XK5fZ7?%G$(S>mjbK zf^)HppeA+#C}Rz|a+nmif-8mfai$nk;sT&JP6ry|W^g5->KkSiUjcR*@4>b7NN7yAg^d{w+Uome1>S~VWY|MhMjCWx%!A~8?{jzIZ~IDM za^^fZo$10=LPO>yu*tH8t6A<`8NAFY0l!Zjpy^X59F2ziB@n*f5p)lD;yd31KfxCV z9KiXYBd!cTcmw`^a2GzI=+I9fKeSbR^5H9>dnAm*d+w3TVENfLt_}u1D*^AL+qBib zzJvGnkB(Ww;$sOAdTb-^ZpQH<2s^%tYlaEgXJK7-5Y|RB=g$z7vzqIG!JLz@1bjF- zG{CoDeg@1{C$ zTliMcEpUPR`L5zibso2(@=^;NAf>=je4##M1zTWxp)FSniG^$7n?g&})^Oo^ur9Jz zxw?y2!0jRvC@wM;U#LiVkte);${2i4xxtQ8(>WQOIb{P^PwA@$K@cI}0>}~^Bt~$BC>mFWXq>nrvMLIq5k|p<<+>mk zjguS`H6A@iiNj1H)yU=o3MvW+hyt>8%i2Oq)!d@#t8>oG{K@=z^;OmDSNGoU``&$B zlcm>{rI&Pgq97_HN$eGcTk`ddm@V<;GU_4pM$Ep)$&j?u?}$= z9MYS?AB&BMAHf5?S^R;hL|hDG^_TH`VjJQ@h|v$=cf>Blx1d&k4bK;Q5a)u=fUW$d zC?Zxs&VXPpAH2F zCd8#u1Ngr83$I69F4e+N!>9Z?;wtGGs12)m72@a8Q>Yx&#>)}cOHZKGsGFA}Zjv5C z>F+eW1hGoG4^Q52<3)(urMpl(xRc*TtdeKpO5X8< zd@72*=?yAY0b*Sd6U_uF?$R+CRP-EG)lXVC&wRU0@F<3t_o|xTcv!56rXq!SxIBokkQpoHNuaI(>T?r|w z-0lG>EyKQn6nJ)0FDdim&=M zId|v_Xt6|>mwngmu5aIkxvA+lAs@Z9>?Zem`o{9<_4IA;KPf3DcaI*5b^44>%E8sM zi*lRxaRcRSHM5jzG2oN?ybY}{Z$%ZKT%+2h&bmZ3_3=7KwHCcj69?aUpC)eR&OS_> zRn3kfF4xQnA&$%E1QPeYbH5}_{Bv#qX~lfr0@6^#JWq1huk)N^v|D>4Zt$Hb)jN`L z4r*4V13JGIqshAannQcv_!cI!cbO~kd5NTi8H)>eBVxIW z4Ovpe>k!LbtZqpQuR$z#v9iz9yb`h8#U3wh;bn;BE_P>GH-Cg!?qYYAt9UVDxr-@Q zD0v}bxr_b0vYOvQEO)U}t7^$!3I0vgxuX93jPTlkGMY2%^J=o$hpS(ZWrwe+(b+8J zi$-!;D`H)Pty$YiF01@Y2f6IYmo?-v#kx{***EJSk;|et6wyxPx3Pd+X0homl~%n; zK_$=HoPptEj(DJm4m^wDYY!SWThcIm?E&QWmQ)O1dobA;6o=t!4=i0lkr==BAQQ1Q zh@{cHbu~$5aqvo#QbzDxk{A#1q@CS2l5aW(u9!_d>_YKyzpB>q3XKP?C)^lwcNSJF;06q)bJ-LRzNi_{U~%Cq;*; zhrEk4!kVy2AH2jfXJ(8bzm63;6dRolgpToECV4oa4lah-4u65IcUra-d}d={u(N@+ zk++B3Hful?Hi=cknDF7O5}d;)7*}BHzHn1g#0L6X+8CJcl8>ro5E^dF9>eKyW0ZKK z)P#rBd*LQ~bA{Ua{AIuc?i*k%xSsEe1`v1G@*4B1~8zctyCfJFq)q zB)bJ^5i?mnR7RMwT;-JxxtP_$jh#QU8WX1C!ZZ`2Ij9#&hX_1K1yQz?cdSUd7n&AGC)b4>_}k@ZwMg=GA=2 z4)PDXU~Vk&Jm&0^=#M!MN=j$X;j<)P_6)j`&a!$ClP1I6WDBfYYx1{P)$U|_$WF1q zD%YnRV=7Rk*g^o9u@=aO^Q;}l9$Ca%A@|5W)&+x)dP3sS1Y~N|F;8S_)v*|4@S9^F zA%i21??h(D9(P4%3y()4v?{|kwt1|a|d diff --git a/Art/Districts/Annotations/IndustrialZone_lights.PCX b/Art/Districts/Annotations/IndustrialZone_lights.PCX index a7b27c79e75e184027610e079874109db493dea9..b0fbb6195b9add645668cbd0a1e77005a7534a1e 100644 GIT binary patch delta 6319 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 Wm?6%=y}^HO2LHKnVC_p5-G2c0I`A$4 diff --git a/Art/Districts/Annotations/Neighborhood_MIDEAST_lights.PCX b/Art/Districts/Annotations/Neighborhood_MIDEAST_lights.PCX index 1ce50e820cc5b7cfc0d09d828bacab5fc52d5df0..2366f91bf4535cb84826fdde71ff95e737ece10b 100644 GIT binary patch delta 3704 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 M*Q8X13H9#vA0(BH761SM delta 3793 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 diff --git a/Art/Districts/Annotations/Neighborhood_ROMAN_lights.PCX b/Art/Districts/Annotations/Neighborhood_ROMAN_lights.PCX index 7367404725667423b9ffeb3bf1faf741c54fcffd..fa1c585c311752c1a343f5b55dba86700b9bc49e 100644 GIT binary patch delta 45 ycmeA@%+h<9WyAC==0pEZZJw3&juFT>xw$)sw;#y3ym|KTbs&+;n|=N|ssI2o*%;&i delta 45 ycmeA@%+h<9WyAC==7axEZ=RL)juFT>wYfWow;#y3vU&FJbs&)|n|=N|ssI2o(HP_a diff --git a/Notes/district_todos.md b/Notes/district_todos.md index c3e32bd6..fa9a656d 100644 --- a/Notes/district_todos.md +++ b/Notes/district_todos.md @@ -4,7 +4,7 @@ - Canal (2 opposite sides have coast "isthmus") - Bridge (2 opposite sides have land "strait" or bridges) - Light annotations - - Double check PCX third column alignment, clean up ground in industrial zone, Newton's University, Grand Cathedral? + - Double check PCX third column alignment, Newton's University, Grand Cathedral? ## To ask Flintlock - Enabling pillage button for naval units From c083b6b1edac7723e01f763a11e9085a0f789168 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 13:08:39 -0800 Subject: [PATCH 287/356] Update cathedral and newtons university art --- Art/Districts/1200/Wonders_2.PCX | Bin 107562 -> 40012 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index 868f2af356320529c58bda23765f18d175d7911b..89bed63ddb15fb2fa07bffdea5da8d5089727da5 100644 GIT binary patch literal 40012 zcmcG%dt8%enm2BdQ4zbF@sm0%60H=6v`AP&!#WPyDW|ZT6ov)|CZ77c(C7@aPXh=MIQ2T%*#i2kBRdA3?Th3bW;4Af2nwziJR>aT$N+b zNzF>(rflDK+;$O-%C(Uuqut1r#$uByl^dsk!uw*aQxJ2RSnEN{xoIr)Dv+whFD61C zh)Qj9G2uUnreFU>9228dJaOBGZCQy%l_@KFd3EZdH^tC6T2ZK(D9h%lc5bv6+p|-% zRgp&16qe?cShz+SIL5w)=la+nmLqqKhlPAtkaMB4;`7CW=;A6C>~%qG84%q}{BB|v zm**%=UQ-6&CRgn=sg^cHhkr*5okx3$NI_k!NHuayMQ$`H&IA*)jM3Y2+B&%SE!E!L=W>-iE3@v&Jtj7! z3jAh!sTev)M{dyN$?byFlF$J+jPxW%da|2+ynj#LF_C@2MRt#U1`G+wVhnhOJ!BX( zVSGf~{$63^s^=1O@)As`1rVGp)v`p{^5|s;A~O#^xi!qhgyb68Yt0?z$2b0q7&?V9 zb9w6Xsa%_?)-q9{(#EJ$-0GB?ij8jNJT-4FQ@qe!Y1B?iLl*<>j0@shBc!MpJHTCo zVS9XO>$>vOIy|`^3ti%n1|RS8)QX{_A$J|G+0Q00ZoqnUcKnR}wvP=7qO!QTl1&4L z8U6s1_yX8}!DiUU?1mUR84|?5Afbmn~3lbN#ZM%jv` zuav5GxTlL@b=K74xg$*kHVq$3H9vkXrL+oRkmR#GMDoy4gobO_Tu{?9-jI^=sF}X zEzV#KI0W$^8*h=;kEGDY3w@ZglOAb!@WNc)QJBVSb3M6*wH65I>fE=01`D}7V(17K zhMjC(-&(|OV8SzazW7Kg-)F3DK!mv7XeJcc5O3!F#Y%fOmX%O|oJ!C1?hAGqz_$^_rF~vlWQr*}CWX71u)~z+ zE^%{3$_wtwcLniSC_FWszZ^aWLnw$z>6*BtHfu}ZV8A6A&wMChAdP!`&fKmwkd#7C zN4_VGchs&a^rRJfcp*d|5)Yy15JLl)LMuBdZ(WZ`z0V#r@52-?vaxQ=b!bl>>1`jI zfZ_s~vF?60%|7m~=)`0GAsiSYl`hO#=RJWfeSQ%28!!*t4w0oNZj5 zx%cJFWt+p~afS_D2`+=Pqcm>*lbJ6seA%#b?(5GwmofOswL&Ot6_=~BWZMd~keN-N z5jFy_;aW8}3HD|MJe?33V=Pgs`3_TxeI8)ZGNHs>B7>zD;HV)t3T{*aNA3HeD;P}d zH`1+3o=9@Gq_sIPaqc74;g~+}s)cb~<0{N`b>&)M3(^9Gke{dDDrBd-@_oF+x2EGo zNDqg-hXz~@XF@Z(v4_21T(_^R#@)*9!9I{Q;oH`lJS8xC0A2Y)LI<)0oFo&0d!Rjp z7$82$?v>FKK8Uko1j-@)f{BOTUiz(7Szrl?Ci%83qbWPU6F15ucPg_oGY(cqhh;7e z#z*h0o}Zbqc133(p?YqZDl1W*l>lW3{isP1A&=mRm^RPIQo>>025?-iz`mEZ>U zN^UGu%;QGIyhy;4qVBEBPRUbbrzo}d7fN=>6-KTGd;=jB50|H^iYx8f#$s-4t~?(4 z2-8pcD{B-_e5M5r#)}23y`SQ zx(YqHTnvRqz6Q|FZrGWit!u3lT7i~?UQNtC$zc$5q-J<@3m*fokJ%|c!S6Lg{61qh z_OSysuFM9T zQgb_?Hy#gNLW8ku9C5mDw6v|vZ-7#AXB(T^%f{Q-C|F0~TfY1bXKqJYM_Me*wt?q! zPhp3KjXIz9K%04C09;5AzY4(@y-#xZzMb8xWWw_`OvtO@5JbR1qWGY(2!aX(p_sE+ zJ^*s3fZq_{B=ZJPoB566ZoIii3f~7jV<3qnxDUR9J=weSf3KE5Z`vj&5iBUm+EBp5 z66JxtJ0rsi;D}xG!!j2ebk!@0;?`^Xiyx23HfA;?W)vspghdKj5W}o( zHhZEeC)~OBcakWsS95K#NxR-;Y%ZP?t_V|?sTGZ|v^-LgsxT_*8`UX(wK0zyf#()= zHeAG>VnM;gaFyCvoGKWV#sY3vXpN1>K_ZWePk+OpPg>Msc?|x+nUo)JSoaD6HX4My zdDav@ZRz4`(h38GPx}mPu$FOifbo^Sd`~_U-q+^y42oZZa|3{j>?Gi9X7_4ZwROPm zr1D+J;tjkzRfDfd7$tJ`=ylfBf6=TJ2+#-k9TWHsHX<3pFW9|$>5cHnGC;+@1f*nc zqh(p#l$h}NHl9(unX4iSlsjJU3>Y`;SgfrmcXmc*Mn;Ei*jcwiUfr4SxK6d*_Z?YS zvMQ@XsVdzN79lTC@tlmD8l!z%skXW>d}sIz6o<&4%hJjrp**`eyI32kG-|`M*%&*u ze-p24H1bHbDm+EKqc}CDTdfuH_P@(_Xp8&9weDiILKy=qpjDXc1(nU&rj&VdS$LK9 z7^v-W@#$D=TKYGvmNZM6Gd3>$V6J0tkWD$+Xra%O7TZ31!0wq8InJ4i|fKRX+JNDoTHA-?0V=ADV0g-Qr;?b&S5WW;G^JW!92P1e`^wthp7&~9) ze?y+oxic(Nzd0;%j%%(jW8wV7wV4IJSL9)dDpSPb(g=B2Mi@72&q)O7+7>PMIl}=o zo6ML5vNHLs%h8&I;%pAS-fUD<@aD$6%Kq*$g zNH``VPc>>Q?akb%Rv1lTk;U$^V+aLK0@`++9#RX&Volc@teT`2HpQ;+W9%jzQAe5w zE;_f?139&LItm>v9y2?Zv@V#R+u>PP*x{)i5h)lXY?)Bg3K(xR?d#nKIXx9Fp$qrY zy+^X8pAj>K9U$SQ+ac-um_S629FgR*Np^8{2jur6aJ>j*Z?wXv-y1|PgW~s@xb^i! z1gY7kZKh>8oq=lKcfNOc!{*k+upk>&Svy_IZ4ph2*Tp@PxixyTKSfutHhFQDUK3H2 zA>Wp`c~0blgzyOY<_x9R6tUObQMWmKM?&PL?wsq;Ug*3@TU?o1*{n6ri%}QL6^!j? zOzp0;yPHjA#>P~IGF6?TF4>c!n3s~R2$yG@v>-@!MWjM4M?eP&HWgk0T;!75j0KN>PIx!&G1=v!u!R#VT zF@U_+xCiu!ygZ37{M7^LGUOerac>B%4~h9qtX>$A*{M=$3s&uvDdm}lj_7dJ=51b6 z_0GkL$2jwLb2rfGQ&i+taC^2qCc8wPT4Ptwi;Usn zO6__UAbw9)ze;lI={Aq=leYQhIMSbu(u<&-Jr|$z34+&6Nu=%jMrgAn}y|G4~uspcx3bdDN6_F5D zWj2?q!|mP8H7`{Dyt#%0*{c4Zv*9Ka_!jJj+ZZknj|5+U53`%JMy*;ADO07WWn5t_ z#&^w3*pyNsS#5ur{U`$3Gh(dSz|$ACq~+)G*mOfwn$^KZ7ioNLZ{?@?fa=q29fe69 z0Yp6G0k8%*x{i3KrPc%H1>fTHoD@F}eMoiz0Iw%@AV6^U0Wd)cFz;~^+F6|^;xvs& zr+E)dE9CQDDsN2%eZPncL+~B28Nk<$2v?|KCyo2DFytif9YqhL;$CieeC^zfln7ad zJZ#0bt{q{o^u3^FC-~v`lpq`P?^t1NIs8wqu+qhI=WJdP9s!D4U_uOR%*ik*O{H>` z>u^F?z*j1tzpi1eYF@N!w@#*W&i3 zD(>I!Rz=3Z7%1vskClQJkrI)tN;|BvEEVodE$7+0!_;i3pE1~$X1luNN$x(1p!}?u zq-pu4Beunol%E!B(B$Vfw6tkF2N_3%Sj*EqPa}%7a*M8|4JKiPQ_x@CjWk1OhH#3)x=J#)xwO-s9-txL6pb=bq@bPbTbC%NO>QcJ3_Q$al_tlh>pK zz4q5T*FKpMc7QL=a8*Zexd~#s4MAs?sj*0vtqQap4qKU=z~^u6%vikDE4Q=b?3gP) zAtPlEn{2&AxiFS77;DXs(&$|VOa9Y~ z@(oc>8}dC1qS$dC7(p7+EiX1${@U82(fMF7rhNH~17$EVN3D+B-s;@0Lf@EpA#_39 zS7LP60imWmh#!zajL-^fAFwZKAe`jL5I&6Up@5XE!3aD%FyL>DK;wB&VjHtcn(WZDu(utW9C=IXh|4bQyECUf5Kb?lwLY@2^` zcrdIpEFvsA%amA@qY@Als1mbsf^QyBsuEU5MW;MdIyb_QwbT%NJp6Fh#;g^vO2bVI zt&dUa#;7C*l~=_p>(!Or6}+UZve8}RhuUx1RKKGFAS=}2JW?rFr$UAWyRn$tQ)7&g z3Z-IR43Z-Xu9nq+z|Aq{RWw%lyPF`yM?>S{UnRB2>$zED)>#&qb64h?V_or{wu2q( zJb^Zc%WP#|wj1IXK!UOIyDaQjyVIj{cxs=9h4AEdaL*a>!_Y+-iJC@Z8GFB}mzN-% zvKu9czhEQ#CE@^SsMNwxOW;0WmwQQUu@)rV5BBWGEAcfL4vF*vUZWKGj}$kv2dB`% zDN&zjSQb4eIglA)2rORcd{Z9iuYYo}?~SeO*xn`2d}pJ#*jF9Mj0QJAj9^bJ$k^5h zIhM}}7R?Di+`B>9nVj&QXG)(8TO3?IcYeZJUjZB5u&lg(uYlIih{_nkNu`Xb%o8fL zykwKRrW>qLaMxfFhW0nrZ!$J*3)?yhIp3>XPr5@TBe7Orzy7p%-r(yv_fv^g#C;!1dr)1D5GCD5McIH=(+ z-AYSui>|>2VWvEkQ~zQ|8!&G3vB|UI>Cgn|FKj~1z8I3=eT_Xu#YQHSv5_7Q03TEu z8wt}HJoQTafT-<7;tqHQ*`<$ya3c>_D7PY1?r8?*lc&-0QE|hd_*3Xj)YgU3iAy8j z+^JZRsjMz4ezULiJBwf6o4n${w%#4DR9j0CvzY>jh+%)DVJT%QQ?e_(cVF1rOqDTu z13R(PSyZ|(Iyq&=Wt_~qJRIMN~KK2l`%54Y@V`)msB+EZtU*2 z?`VWf3D`Ip3VkF#WoXws4A%VEsPvVVBx}0u8;s|(>+D!thqZ;bx0ufq*AVJv(dNR0>B)Oqnl=rScQFB)5w1QXY;-`DgmH@4QGD{@+tv?L1w zim(OTVV@xEqxI}NdF!PKR&{gK#Cs}Qx5QCaV6g0{s8SC7gu)#Y{x|kkhebs4Y}J7*ld`~M%u3uAo_*Myhy;Im z_}cAT_wJm#ST=udX@+dYLe+Qrc@I00`O2^M&R_e?5c)We?GT?*r`K^zTzEWy3LHC#enX{=i>>}xd=bWOMdq6?tG z>prz#?e|a4*AXm*JVR&UnUSMuPe>sBa3Bm$T=q5~8s)1rxmx0zRj6&f4vXD_&{# z=WOj<8kvx#oVRpw>DBmEjJ zYY+L@?SQKngs-j3qjTyCkO;j1Un4=Aad2Fde7(4J6k{tid=yQW)Zti^YI`1Pee86_}q#>!-wIL9AdGmM<<{ zY{(3s)44afIOmPbg1;#a_=@T_D&Fkzzfyg;=5HbrBjpjvTTNOBc+QIG&h61*MeG_I z%Z!#OU{2<4FW-~dn-R^!cgj}jmnzp*=-COE+?xa6*uVWKIvN*Ws#vc~)y62avJ_bB z-!}KwSM6@9e*vke{hJ#5f4Y-yoGVs!CHp_JemV?7cjZQrY4Y2{RAOkO>0dNyVq(=Rq~5_ z@OCB-s3Ayh0(`_MkSij<;Tvz1u^UDFvLKmotO}(yDTf4ZQVi;c%-S7x`x2TxDdr1e z^6PB8dc`-BlMcisFMeij;`|lKuWU))Ie($NtH`!i`DVf4!ptz2GfI`oqmy%ZR!+h; z`Qv8$fwJgVitU>Z9#|+ZT@mc-$}Do_WUN@rh7vaHm95;IoDkgBV^y+=9@la7G%o&R z_ogbYR?3u-sj_T$MRQ|S&88arrV1pbg(}Evl{ymqBUO9Eq*YcJ!^_xjyZ392mB{Yr zY3*)f*`925nNb0f(ppzlQ{hG`xMmZ%*xfJjO)m{0&q;7UfxR`ZZ_hWxebcbgY*>=S z4KN)oY}E5s6oM3;2NB};*x-xtnl2!I9gpKMG>(9$q1I>4Eo5I7vfIKPHc6OY7faa5 zver7J-9W63>&Z~>F(T<(2CzZ2VPuK`DcNkn&E5y*CG0X_p9LEC+(3v;K9j&F({r+U zV1}b{LjIUo%Ejcp?>S%Jt5PmduR{oKS++K3;fm-?dB@Jjvp23#ZSCLkO5!0;!t)ys z%iayo$c#`ePl?WoSS%0vo>bYjs<~P0LXgkTZuCAGEbrQ!$xZ}S(106^E#Qk2wiej> zf*CoEwevH#(*m3qpDS?}z3_M6nx|ICV$^c&es`6<0(`I0ScL@4Dc;yyIS>2b3N?am zWSWtSi)^m1P=j1oVHNDWqRie{hh0ByUL%`8=%{vAlw`Y`csCoa*}|E-zNvl~t3Zb4 zsrHrnr1*A={+o;9^hs88LmWayi>ub!reViB^c{uP+}c8fJ2&|;mzAAH$ZpoV%!T}p zk4!uJk{~*QcHDJk`|65-F19}jPrDM<9Uk&s_Jtc^A@a;%F_(+j$KczDYt&LANs7P8 z%#atwGbzSBi2Nt)7Qf9W$%2iFnMpqcUK$}e=kX1Ork|EHnQZmXsFr5VkF3rt?tCMh zo!Hp9)8zb#!F7n8czkbI*5*iA^1&1`EWyOgrW`hrZ4D&MO%7HE%a}C zg-!ICc4I3o8TFvNXiZ|-ejGh14}hW@ey zJy{oWx{{T_I{=T{=>^!|Lesa{wFxvlBL3pg;_od>d7SV3U+I6dx+!`w-%{Tx%W$Zk zDbHk+J#Wai7iHBj5d@YU;|F=QAwrcrKO&)eXX2bE6L&5we}$c>UYg>^xVB^fiQ$dQ z>NZ1bzV!;A-`*RR8Lir|o}D`oSljuyZMoS=4)&Dz1Mc2bU-c3f^U46)JXuU+N}bVO zxqC~qzqP)OeO##?5M|obdGfN#6hxcKRHT|3wMO1g@&2CTP26AKREaD_rNTH*rIy9G z?fkdSSH4#&P9?=k4d)CVDwc;-Dc3R9k6yB0Hoj`R_)XuEzor}4cU;0BXJ`=xq`<96L z;iZY~yz~w5fVFw-1RJk-tsdJ0-^+~R)tL)(mPa;Y3()L z{@wLW6(r|agd#+y>UoMvq*l06TU1inz2EpkUX8t`nRoBr(@ zx)(v{IGpVGTY%&y4E#;W&|D|VaT+b5Mu5G)e&3!LW0Mh(*1(P4sAD%4vIiw>rVLyW zKnrz}bOY)ejX)YZEPXZnK@S-wf|^7e6y_4zLpjOeEeqb|qCSEDlqblHWlg0!GxAg* zFt&C+e!z$P)P>&+h>tIgNX*<>v|LrUIU?b(FLS%gZ|af(<#R8`Wd=4c+Z^=6t5#(RDYmvz?hQuBiLL2s#-NpDl7%G`U|oA}Uf8O;wRxAxo*UBWK2Tzq|Y0A+e@R zDND^p8p^1U+j*tYWX#^O!wziSh@U_Y>vwNybmMkx!*y#bwF6>(MdNE_dG5N_VT46T zk^At+t&BB4mL8|e*DOhL<}X=b)pt1C4>{N%WlVxTT|<7?8VwHs*L7?ZK@y&k{v%-~ zG{(mfAYTv*%D9w|E~_!tsX0=ukX$#rw~&1i16K+jBNRzKavH#b8G?xS!H2?T!*&q= zxdoRw#jagKV?$zfJfsx6<-dT#tchB|WeL$-&PKNLu*LNy2L|)GTc7aU|h|B-} z@rcsMu+p7@*r01^_Ok6>HeAj|mhDJjCwz;u8aG>!Anbg3gD;sKvqDf0Z!K86*#T^+ zAC20$qb^T-L{zGk`%0AB8e?%|Nf|`hoawWTjEH6%KGYpI74jyQ-*vVWz_^N?QA^XqpS5C z>@s(FdS5gLf^in71{+xh3C}z1bExt43rOdj6}{^K7`q1+ga}e?tl7tF%1R`|06-zH zd38qiMG>1oQmT@D1~75=4OoMF>?{8D!rUS;y?|M|c9!OC_{COlbb>6QNVa&zGmDq< z5?(JEjd3yut?gUu7(dkVsv&AcP9~D9^E#K8ZeZ_kTd3Q}gzPs!ITmJYx9#(H8GQV3 zhI;X@2E{`e8-Dz&pS(1J9tK4vm!*~&vyX@jXTpEuM1G_ZFr? z7LnLbB^$eEJwDWtV1BlcJuIqOPavMOqs)$dhU^l&*q4$deu8GMgJ*$BOaQ|SbISj+ zltT<<*yh6#$e?1!FRQ4}yKLikbRY~0?Y2N^ z!JcksXV*O2-X9H!uXQd=cRG%uVbpa2F=UpEWdovuD+lmat4wk4=Hfn>kMl@_VE?1Y z7=zRk_Dbie;m*42clf*aSM9FnsGFCJ!9 zmp2zI->fjT^7e)S(OJFy70Y|H8z6{^7$rycfg6jFZcwMlj3`};k;Q;$y?X#HpU0Y? zIM7?yRF8g@G7}O*YI)v%1lxVP>rm4HKeZA1+ue^GY1xp-pEdrprfNscuy_hvPA5ej zj|Z7GEQ!)DG3ZxX@}ta_rvv&fCz3&iHe2DYgKdG1bjDxQ89KPdAxR6(9+{y}kcYJj zn(t!|bX9XP zGh*(-PE^pz61G-%vI~bgUA;_9$jn|Cp1ByCOr^;MPreygXmr)6zEhschL;@<9%Sc# zHYip*8vg0+4H++uqCJR~LI6X^t>`R*DHjFjru-nzj9Ncf%L^^h#3LXD)RLWX3! zzYn?0svSlY^t!#<%JYI7vy1uAj%&;}{fCvy$IaqS)+SxVlb4q>N3&j+NEq zabhu~bC4cBd0E=3%v>Q~@y0e6GFGxgTW~?WVejD$@;V1ceolNTA~P)WnaqrZ8FPa> z6AnknGXns5B4^{S(m;Z(-T7~WV%vrer^9j-O#uW>3MNw`)wR#gkz-Wk@pqMBO;E1` z!il;RlrbSt;_?`I8QG!z?s{au+>QKHeVM&`NPO;DUbP7!$gp?@JLyM7TY9|N;Lt}s zX3@lIlJw3beM^+X>~Xr-@uW7_8jr7y@q_DpFM@c`%1+TUpJKPqVQ+g(EZ04;Bx)5r z$UQmP_mgF$PGrv^Kwp)?6H0y%B>n0Y+&?7BRvfNj}$7|Agltp7PBZvyS|6=wJw(#^nlj?CT4$=$k*qXJNCqR z6qyrHLd0{Rw_kvDNh^OFXk8a3ui(BR5%I`HpDgUj9i^E}+%*3;OjM)55b;5BLiNjA z`NGxFD>6$Gd$+E&b_SLjH~;SjcAO^L*^pK5N#FVQ2pSm_BV!`fvICf`Oc7(O0TWd! z^4#wZh|i_SvuU~KRa7CUs(0^iY{gptdRYAKPhR7jN?sZk&!fy|MASq@W1Z8iahgRD zaV_z>MNc^zy4oG=x}j@bp{uvU*F~8!^0c=}f6t@d1~W{LU82!rU6HRr0L7*T@33pv z#V^nvJ03N6#r72JN&nq%hD5}MmK6~RQS&!MuZRw2x-{Lz$r0+8+d+KRJF`k(+`4xZ z4HBu&mK}i53JRnB-9a&LP<&3ED9etSr<6yQH8yweuXp!JYc(XUdbY|<6X4)Kk##maEX=Ta87c;Xh)u=TPQ_DG2G@>qphX z6sM&F)4e9rVS|+3%moP<$<>InmcMT0K67GeL?F|f5lB|{G7gGS{ZGsDJ(*3TXoY;5 z0{aI5pDXfq?>j2$5;ZatU{@@ysIu3*R`33GW9t#|2hTnGlPY)Krk@Urmr(3?L|wkIpE1nq_&hB3uPDt- zNSUAfo8_b8)@uFSDT# z1mY}?3kac*^o(@LV4Qy4^5`cq@DtT-BjT@QTYX!TZ7uBV5iz*6d~@Nf=nhM_A0gPv zu#=HeaZG$JRlO8?S!L(HehrBI~}_w6O|t2E{`%Qqt|4pd!B zY6n{aQ%dVM&XM4z(%7%0=MnLTi!IB;Qr30u+`!HbiSOva>DdL;GQumL=+Kn@)2#H3 zNOuon>$WllYZ-&I#8L6PI`y+ERl=s1elR3nMWOT&(WZ-y`gU}@&T27O z9UYQbA%~1Lo5BzP2KtbOaWwQL8XkoeU7F@h3tN|Pn2nzhf9isj+FK-v4|RML>RSU! z1hXtpaeoL_n34Cwrm8KlK-Y1~1KlO@*m#54;c7uwvwTkqAkfX#S>#WKK1V|*(9z#V z<*$e?Jq)urET%K@wICr)4>k@Bb=4$@5EhO3XBkBib+={HxUu}Z(G`h}ZUv|=eg!@-^l_-^viGm0X|k4v9t z+s}TtqKWSr7VqL*5sC2c{_L6VDS5a4-C-El--GOm81&>&7Sf4`k5?<7`ho^XWn6F*r%sZOg;M* zPN7Wq(SYc}7a>moaycc0-j>wy(NDVA5q{>xA9}&@p5OHgp?*p-_IU}E|2ZIi`FV() zJs5y-8E0pCh<&h&jSt|=7<&EGh0oOW=pW`VfR4TFJ{#`$`n?6z3iMz4_Onofz861u z?|6iE!HZt-DY_O9t-N){-YhFG~zsA6yrX{%@qA-a49<^lRKX`R) zd>Ag_;}9F^l+AcQTGcpUzcqkBr zxw!+UeVX8(#$`Cm5o(_2;IPFg*6kzAkYE);{2jat33&GpgOUtjTGIgP8bg4`uJLI< zO&DPO8G`3SOpJ2|4+&!VE)v1tg^+KV@v|8mJ^KfAa`qS`2RD7lKRpuaVrPU;fkud< zm+uRbL|zN^0m^I0PG0*!^rOcy8-VG{A&~wQq3@TU2&jeBiu zn3h&rngUvy4`G*p0M-A`2x3k-dX zk&JvqLpmOp-xXkIX%Ot}JJj`U>iP^V-#fw?tUp$iI{WrjMEE^FBU{gLm<-j<)A0V& z?Ai&@>&NHeg8l-8y^y;GEnM~(x7GlHnPj-(BrphGphZ&2h9vBflob)NtX zAQrp$M1hz6sQ^f#H;rZbGkN1MaZm#>m~RQNJ6sH6KI0B1{ut%UB!6cytoNKr2JZ6l zQN+tv;6}#Te*^Dv(GcY?&}PsMeRb^?)b_r1FAP|$*voT7mi z;4_vizp4#j4;j%d1ze|*SjEv5GGjLdR6zK6fGT}(ux$#%m9&$8dXz-(47)Tf z4w3F-ut!L!hWp`O@8d+s7+}M20q!Kb1C8y$0>jv0fyb~hh;Peb`K00A$B=38e~|YO zprerjU0;VMjvWgkWx>V;It6%$jpwk@Bccv`6?YJUI*H-G2Xt=-);U`A0;=eoy)^dI z?6!}OW9(CSaOc6mAqK?ZexUVqpo(PkHZuJDKAlb?A?!zP;6D5k*_sI)Y#?(Wjg?R0 zReI0qJIt#bQj7Ocm=MfZ2tS$lgW3BJ2y8FM5RVEtr#K!2XTh2eYS=L!$^Z?}m{CaB z+t{5)r=K{J*e5}puA-GV3gcZH0O`0M=WI8`8QNXkVZgxx@bZCxE9kQr4!V2?554!x z5ip|*(D5G+0V`UD&#*;=@nRV`rUJjx50Ci}k6GcE!0TO_1D~e5$nVgEFcHWRp9GX3 zI(`mc1Or3Dhy54H%zh<_IYEuFLdEd>6(k%d8IKwC0n0(3q2myVzYhmT#30A+&L9_; z(6S7HCOM%R0e0PGaXI38*{D-+1=u+kBnqemg7`6Sv9$XFhd7X8M~{GA(xk{pjzAYK z-Gw)KG%1ny4-7-&X4w5fnjT3KY}Hs_zaPfr8eI@*x#%(6SHSKFlb9^_yaZ?vCdEVl zOagWdb}omG2VySb+pGVP4H~c`#c{}Sd@uLZqQK3m(fiu;9yS$pTCiH)z^owRBuzZ| zE&&_7PS-&ns=OUeSBvG~fXe|%8`0y)pbu~OLCB%Ampd#jgnlR{f+6CEj)1*ifabvP zAC;Eto`~gmI0KF8gZhs2_w|#3?^D&wu*>TLCjrGGcNXJgbu_}5lrUeb2IGpT>TMdGs4;t99b!~c!2j$ak z>jH6t_-%F;jx!F{YZQUfG4X$@543ZG z!+^ELVnUP={8QL8yMPSYBhs&XP$(u+gxH6H@!>viUw;o9!JKJ=vjR8_YvGq9^Dbt1 zcR=j37TD0&q#!m}95yl%WK1S(=p8%A>@L<2vdOOXu!*}E>>-#5Y<}TzlinHNo;EP0LN>eHkHrkpwLbqH z3K?z^(LfR*j*aVS12sS3E5t-9uxWjI%6}#qncHI@Y0uL5nlk2CIUZp1_Ds zik9?6N$GUoX`jnta9P*kbe(~n7G`KWLU1{_9*18odN{LD@=#M4$rQhH6qyBLR_qeI zBhB&%W{IOpgUGpHP5UuJui(d&Ai=`00JjM#g?8PA6Os)549pX3$q}*7pwU?z1z11I z5uwZws1O6VjmM-F+vJ$5e;Vq+U9bg%Z(&LG9l`wC!h95%`m8P9)w@<7w6Ylh5L^N= zUZVG{hGz&AvO%0a?;wTc#~_Dn@F>Q1N;Eu`#PtS^1gno$VhvipW}$O zm*0n1y)KF8G*)qf6mj|#h((`mmsJn%iS1wZ5Of!1EbI>H!mOhHj`RfUinC+AhrCOG z$V6d&(6xFUEYLB1pdF$K(?Sw-Kj12a5RUWJ8W#^b*Lf^f7du*uK;t%X>#pM%+nAW7 zPtx%uz02p)bB7B{i!|kFHsb}coFSHIk;PfKRX`TtHbI55M=98#4@hpEM8@w7JR^7# z=6!@+dIw91<@xb-sPBC?GlKl;plCZT_NT|LDwl=@Hx8Y}iV?dx4iq_<4yh{)Ss&z( z6n2VD11q#cuC)h77_tp4T=@lnYFHTK-o6`8C14hb8BFao z)|vE!h9gR@^XeJV-wz=mh>ju;k0@otnx0gSuCDb-_~6}0&!O+HiU(?t4FaeSozmV3 zJtom4ZCua7B`pPdy$|!g%aDWJ%~|`$y=)>sCD@6tSpXA1q$<52b_UJ5$I)HGGpU`QyAyMe&6e2593^M=7U(&ae=BENK&9AU{t3INRin6_A%@hoRS>&BW-yHx^Pao zE`2?LQHcLIN$Qw7%aPT}B?NKnNo+2Ti++97 zDqfI-;B{rZM$mD+jot4*GRBAbh|mw`J&bm5f*W0z7{pCB4f~81`uzPCeY+oB(3s(Q z`MLf}5ae}C!~twj=?}Ab+Sc14KsFzvV0tQ{g zpgfIz9NKzqzg3!H(6itBO4EOgy96($k zb+`riNeM~_jD%het;#9vYU9YlaKq2?lgv7(a*r-aXY-p2Nr{L|uU{-jVKbrd#qkOuDb#iK&eHq%Ajc z0Jo(BFOuX*`)ULoJ`t>w5F!L<*yp}|9#J^XDFL;20NuHF*pc#I;+}KDq?GW$Z3ON_ zKkU|@PZ!vcNJD>t8kBxkYwwStreZ?uY2Vf0;0=ECBlf@&QnZH`&^_>;X*|GBk)T3i z!%u9MboUkqS-=FxPKkV^r@!w*lrj21Y;dN}`jl&t^NI8&aUgEv!F7fPHlD<;9n|xH z9%ocNN%@enwj})79CoM8!KRV|J%}lwzrgD43mDI3(N?Z2i1t6#-r`DfKrszS=H8Yv zP=mk()Cdz9g&2@j5mpep2DphYG4tOOP+lqWyFHFH!3bH~PvN%C^{^|yEMP}|*+a8} zbs+*uSY4y&`#(>z0qS-3kxeJ@z>R$fB}mTBpu*=o>>NJ_NJk)$=kS0t@p~a)-ahXx z#9d%QxE16WM1t7k05UcFnji)F-g55EDF?)(82JQEydML)23v|C1f*hwp@VTTfSQTn z+$7hcB*#;Mr1ZEptJAU$e+l4@!4=?6S3pXa_?-H}xTHdZ1wmR8&PTB`*x-KHi&-7L zh*mxpZ4m{wf>jW`r_9{3u6-A}15H9ejU@n#U*a(uWOssO;YiNT0YIdQBwS$Gfeh@! z%}df@j4@&r2!5_GULO6{>`Z5=FijQdB{qY=<-eZpBh!yuKX}9pLjn*vHb~)FRHU6H zMnBbVX%OJ)$=d_ZN#4V4JrH5=G~(VAI}HOxihcnrUBfHF3Sc_z&9QlNcwcV|s2L52 zJQk(mhJ_K1_AiMp-`_L-#NXDKiEDGVQrg(DE-fbNR$CA$q-CCdy5 zCE21B>3zh3geKw&cKah_S_X++Kp?%lYzTt>I6v@hHsk-lOi}R8dQ4~?nnn5n**rFV z9yOF`kg@}ax7cW}tD&bC!rAlhY{c8+we9MG=A!bQU&CL=kWl1z2ME7M7;;45gr>jT z%X7Tt4GwEN2P6D)OhmjRpz)8Skc%%$N_%?AQ~EgF5-U6Q)RKd7i=1$+h47%qxItrp zAzNJ-1ersqMw#L3d)o1ajqC!be(=v6L`Hy|gz>JV zAxLEml3-2@kdjO3F-dpEqP^wb@?Sa1`~5jEy*QY~M~2YF6|tkwYjIj#=7af~gE+|X zs$of561y4Y3wS!#`40JNgB`1r=+HU5X`BU6d$nsE7ejiIPONVCVZyE`GOqdWIM)eP5O>&yjopez`;tHjpK(ydAkMg&n z?Ev?5&o0NV{y(3wH;L8bMO)G^fbTY1keFaEXzqKIKU zVMmw)WHO%8=*ihhijKhSYegp$O7kKy2KOfXaW7uMiy*o{S@2^qL7HLjImV5%FwR%= ztx0deGvqJfy12G-r>-DgchD06Cl`F&89W$pqCkjLvx5qMr`YMB4F!Ft*90VWox$QB zy@pXw(jQ>30cLK?S@qZgp+J&o*clE}c^_g+BoD1XK=BS?8hs*NBhe?-N9;#7l?0hM z($rPTieab42C+kuoqalZ20rFoJGT|^zkmRvpmu4x5*YtiAaY2cAv8pcrU}o7>@EQh znVxLfW!>fPLk13+Ch_0-$iE9c&;cSx65s@`WEEzC#x(&>Y!H1Ok$@f;hK(q<30`Ob zNy~MRE!f3|e22fmk)Q7Hy0|qiDc#`EmFrwijDcL>1UoI|iYI(HZBmHz3sE|xea->x zb8OhhK7U6B%8gOt`XjK*x!D_4ptBW3>&SUUVGc9ghG!T7!jupuL?Ghu3-n5cmfk&- zzN2+O#+uzEm<9NM?|VPSzRjon(g4z|NKL|dg9___{$Ea=77YH0p#HC>@5R)ofG&FF zwsHcDECwjhq}Ocg!#=lnSC7Bv-+RHHU^OI4Kr%}1ksQ`tJSfAD)+IVHE5#GTV*|r| zIlP?1!8}6u<^{C-k!UmPVl57f!@7uD%CU)Vdo@WDXb4au1k~7KYuLqY0R&5h_&6?c z>v8;n5TIjo{WvLLhV6fwp+<*Jh9NF91Ji<4Hr=X%$I1(IxQ*ue5O59`fNN94PuKxy z^0JZ~&*DmO5W3SUKS$*{nTbJcLGr%$Xe;3owq^h;Qln=mY-HR}V1riv>8wd1fGTP8 zU(Q=7$Ixd<|6OdHNuo<`4Sz(z-?yuQ&ccFra=$GH@ekVF#l5&vK1<_BEoQ9+`Zxk4 zDKQD#OJRe*oabODUOs}tim7vG@{(AN$j__?%Q*fsn`4Pfmu@R}md9zl`3UeRIRh-Q zN;KI~Jnq6W-3)Z;40v}7fLsIVJQk4T4Jf_M?tFwsKNi!YR$6RAL4-}mqp^rlXhT9) zY6pgZ@}3x`UuuDrf-D))d1(a-q}!&k@Sj6!K@#Z5!exl1U`l$HEW( zYjTyTItj1svb< z@k!0Dpfpd!nRp$S(4AQK4*p^lLGW*wGLA0N0bPA;ycsCkRz>Ir3J?%W%1#I&cK~}& zLP_gFl0?aL`b4TPeOB45Hew8Pt^$>;bO-@Qt5KoW&T}Zc{%4cCD~UGz*Q2Zdd7=a* zN?`c?K01N`#``5cP9crZ4x=#a?;D^XsE0^A93?nc3&ljUVkad!Fg#dHSj~xnv0+3` zWH^xzr!*QRsSush(Ql+7G`ggCozvmx4vj9(9IzCCEd+YlRm}9hYz(?Os(&L8fwv5)wz3utpfEKg6;tpa>Gx`+ii1*`B!83`@YKa$C>@%p`l z02wjj_mUj)`?>}P{e>YqFM$GTI%fdSG3EST4ifl(m>oF`{DRocf0|%U0bu(avqUzr z|182dIAJRD8Zqm^%CE6=QvA3JS8%l*+n&<5kG@HCDo zK3oTqx(jZef}Cq$QsQ|H@`z|yWVrilNEOTq zABERs!s*l3N{-Y4T^%3;cc1aSEl|790V57s(Ak1BsK9?UB1Qfm<}%m-lvx211)ZC@8>g7Y&yUm zn)SQjr)SK7ZJP8MEd3wfA^T$6W$QUaoqvdy#>J%Am8+~gsvtUo=RlVSX^zu?hF6qv(pkOmjp{V3`wdH zvD|@XuF`4)r(MzwG~1gP=beKMEyxLB9+T`!djl9fSkOd&xgS&xb_RMa1!aBxM|s(5)?Avh1GBt^ zw^)~U_(=F2X;8FWUy(R}fJV=Y-tzQ#jd@W4kI?1tRpmr>W^BZECIB5ND0yRok9;(N zi3Pw;2WB&lEZQl`M^IAm4mM!QVc*M91R~uyhBg1o36XuHw8fSp2K6wL4( zhJ6)j6{;`&9Qz$-2|tuj;>c)HJfuo$c3Qv=0GwH04hAyZ=a+J9{(e8ymCOVsRs??m z%rUJjwEYa)J}deQ;#X==r0>^6S#&l{xdl7>1V9hwLqkB;mS!D?r}1`Nc}S9mfk4dp zaUl0OK2JSDZIaTs=S6eU%D8y*qKKST0{jJef@_>|?O%gNR*wua!3NOqDnSO|v6|>lKyI-5b@J^hHyDcykpm1Xc~rc)!J?x&58@EB$t~K0M~w|GG@!6&_Bf_QzA&QkbJDSa5%HgC z$w;KV{b2U^<`eXE8W*S8O=41z6XdHWIVDOPkYD7e;cGyGOCk@r6~=f#^ul}K2g6j+ zkCO=BPl{b-MZd__@L|~Tes&H}a-^uoB<&h0r-9C%W;#oAk>sD;-W?2d8vFo_L2vji zK6wgF;tzQZ^W%MHZZYc{zTJj)r*c5-dr(OMtBu`u(kDunB1I~hL9FyGvSNCk6dScl zZ)NufNRDtQhGReCbLvjI`*ZB`(EG`A*iocEV*ZD80Vmf=o5HjYO!>=6ej0-!S4M`I zwn^_$*#<$0rNp^z+y@Vcr2SQFfxsIMA$5h|a1^_|_~WU_hw7{-e?Uv%X+%NCkfo<| z1-*x-YiCy=b2I^{Ena~OMD`|LNpWGS@CoJ&-$D{kp;2TR#__|`u(dp3)?sQm;DLd0 z{tw?b{||GJCi#yvJRm~Q0+Y{yi634tPPgjFL;Q6lGOZF5nw1T*|FfTQN{vn%3<3`d z>cCaTuz!s0eM$vki|wEU#W}QklYfFf2~9dsLgxel38&I(2naFgFM%rNNozw^7WrgS zW=UG0GSu`0hg88P!%)yTY1b71!fq0WqF1udk{ka3t$ir=^!2nmT;SfcPjX$FgCsyS z;YXjSFWOj;X8olk2~ZNN10X{WX8_^n5}f!o#`+ZjeE}5&m#}M&p+Onwflc4pDUV?s zH!;Uq1AwK$A`n%cloU}4!Vztce~3yAdX>II<^YcBOKA@W91sW~Tieq=Fw##JViH~m zYJ<5=eSlw`75h1Srjx=>tjE`~c^jWmc7U8K?xRx>xDRV>HB*|7>Hub^i>&+=>72r8 zDqg|(Noen2jO6@jC9q(45J9_&azdITeRCC8iF(nN6(L2~Pw&!!1`~5cAqZH4lx(_! z-+-}@pid%i`6V9H&>FDk5NReAuyI0kcKvA%eDWhy*j&M1u;XKJ73ghJDoK*;3#0-) z5qglGT^fA>;^lMwe%iDmF}ZmexAI2^6h0(VO~ac71rzuP6-rkTm1Cm!Z%Wl$5&_4e z=`^}sSU7AQ_S1;kDO~xIB;=xW+w3`muTZ*0e{K^ZIQ{Q8@CEixNUDc5IQ%q19Fh`0 zFn?am9iyZ|@Y%xw^h{O$xE;YI&F3U-ykE1TUrC@*F@Q->nHsP;Cw)(~{#5Z#1^XyB zgy3Jtl`)c%Ve2aV4#7YVh!;TtV@GGPzAoOU5`L<}eN>Fcw z-j)Pt_#=EV5n>-n*b`m4jeSWnZ8X5I3-CV{Z{rtIAHSo-rx>~%0(2ih@6i{(Lq`U_ zuDPE;tuLyz$NoPKQIe{VC;2t%{_86H3n)YWuj=VP{BP>$PmBM5meA8bnF8s?zY~I& z*$-b|8o>3hs5~2py4Vpawf|!|{_Oi1+)#^uxnhCvR~>8+Wv7Ews(wUtSzdL*O&vq& z;fG@AbFit|690!d41m7~2-LLOI`S>}1G-&@@DCyk9%6T> z(b)qDLpV^HI}9AZ2)zg`9W4B7{QCgddaOn7vG~Jcv+dzuZxKSj6rnXZOZBSr|Frk@ z(QRDUo#7uiXLxoNBo7zZS-IP7K!{U#u^c82Vw9O63~Fa70!9#x$29<^UfFKLl$y{E zq$CRm4uB?x5i=i6Ofn(m!;)-5ve?B*R5^@kvFFSKp$}TLTuP>BQItec5>48YXvdM; z#L?T|9mpp=+2;TKW6vQ-V&=`8ckjDj@80|FizJ4oScR>QP}&VX0EFxpdKLDuv~@95 zDc{NwX&mJxBd;Zj=!?2TzCOGDe!r`~ zC5_|U)0XHR&vea$M&1HPBnC%N1KfIw$6ZCR4^MQVU}X_SgaZUUW&Yp70U>L|p^IAH z@R*e%DoD|QG4dr=eIvc=O*;JLeJY*j!LjiS8=6AK&Px`>R(LU=!|2iFDze7#+AbJK zye*JG1p-ck?lO5cKV+_OFbt%61(k}srr3C%9f2>6&@=+^MZj(13UYyoe;~e&r^?GG ztuH5d)TJ71W<1in0FHX|mU#qnvSJ>wyArG-f-0^ukP)Wu2v8X5%CJfnhjUnC!bB4X zALHYikxeknFh1J^B|g_j7~TqS3FcMkuO%YZ6?hzdWQUVhT7Jd4@-pvF#YGgZhYfKO zasYc0K#LGPM>2pohjI&}E$FgRRJ$>s6-+7K&M_c!X=Je2HiB2-GWC9jY@K|>Gg!`> z6}**(aeFv#GMZ&;Bt^DKr`DW-{fx8;#*^Mcd6=frT%-Y=f#T>7)pRDObj4iwCkV4m z(0CDVC5%_tAa%18DI*(=bOSGpn3YJK8ljs-HzUdbO(CWE2)*SLhBP~*wN;U`X)YBp zTx;xTzeG4%Mb9_QC2TsRcWfWE9y-a>$ROn*uemIn8$Jk9L|lk*PJw-r@tXGZRN66h z3PO4V@*HF0#e{=JiX(G8qSL5AHEd9@w_$999>Uhrpb8#F@pXE^NpQcR^{ScTAVz?> z0Y-r>pcX?LDFZ-?T9Y*e3WHfz-4X`EVU)0q0u(uGF-7hrQs#@up+tT!QPfFm*}Oa zw25p7l!=CLAi4sFE>$O%{>;(*E+Mwgz}cvuL_4@%Lar-D2;{*O2KCIV$rQGJNaluY zUJFC~;m(A)_)k{TSGgAN%f@rKOokIhNh_~H(XM0GJWZZ@y$D)I=c_cW*GKB|gP2xx zH9}|w6icq`AZ8!4-Il<#(}0hHss>dSgz1ki39CRU@dU<8f&qVEOcsIl74(Z6OfwK9q!KE-n;e72?jl&G*I%-sf(C}u$sAOdWNq*HeyiDETd`xPEkL`RURG$ISd zG*%!R7{_Qq0!#1~ZEG3Gq3Z+_Z7C^(Q4D18PnVIyjht4>oyTyY+Ih<4u8FM4UbULN z#+ww)@E9}b=R`geeGOLB1sd<5;=yd=2fV*o6uLy9Xp=cy*or!$CU8Cqgigc(3_3?3 zKe>yIybS%f$X!9Dt|_QsZwAA(0EZQfd#PlRF(ut?=ZCE880itVT76~!v&%~p14zR_ z%$KF)xn(%NfO4GHf<+8JbQ)c+@PN|n3TLJmCDbmgz>Zu61r8x?8n}VS0KeM66Gd~i zEm3koxF#YA5v;b!TwUf8lu4v6Q4A0TzXFT-G*=x$Q_v|YI*o0FWH2~xGZ(}Vt*3BHkU`|j?+Ax9 zfJ+oJkqioSp`>mb#4cu^ReMx^zb?81WRA=oZ{&kA7r6?A+^_BAPa~4z=M3?JepnMAtjmi!J9mTG*BGCas|F2 zc*=m*D%a_1HgBj*a5-RPR1z!#H8EIJFl0|LV~v-P#)@a*)m!2vK&e4MYHFJrVRj`b zI?NN8jjmZ5?h)d_?W%CVaKVd^&bfejeHjm*=OqZT>}1vmvBcEO_*g13GNh3U66Gp^ z-HT|m!Q-KwI{Y0pS}3@M4J)sAnd4X;PZ!fvQ4kHyF4|q$Oaxp9O;*lyjo>skwv>SB z0E)(Pt8&ir*A518CW3*iYc*TU7pIuV;j){X#%vj_NH7q?ZO_6Ugjo!YyouB*6EUbu z6uHbnn(3f~P-4n6gnGyGL)!GPc`TQAp$gkhHr4ih*PH63Ba^c8h^veU_HkpgG7mf!!Bql$X4~JgJm2smcW5(7y>a3As1tC z%mxN8T49o$L)AzR1EpX|%riBN*u~NawR_(NBh`WIWWWTOXm0v7b4j1X=*<->5_OsH zA0qFCj)9NI(xu_{JOz^?F>=w0jtvEILYCI#4B8Q>+?6GrUR9bjD)zr{~&Cd7jPzR zej~r~k^y1?mm<3t#160H1$6W}N4gt#scAMAg9xh{n^T460LBWn>b|@kkZWhvEC2a!|2osWpkkGdQLCUP9XTT-6jP`H^ zuA!SPUdmx%D`odiWT)YbH5Z;w?E^e8<4_XM?2k_{#Vj07w^>f4~K~!L7iPJEwiVRxFT*0z`bIL*i*vn#;+W=ibs1?9ij&y*AlmY7M4Xhd< zIL*aGL@J~*p)SzJn7K$`u}rG4dl$&CfR8zU9j$L*JLIHC32H)UJmYE&&SdiASVSvv zc>5X5@*BL2?6WJpkV>l#7qkJCg6e_jA|kZ3Jm|(^9$GV3RPYc34K6{~!^e4hpzy?Q zAg}BSERZvQY(tz&%nn1MN?;D%w<{E83 z=Y8=By+6cK`B3k8%9Vx!o7p`vjM>{|55wf;hqlr521#i{^#y`1Uq@#ymZDNk}Jt-2g`n;_QM85Fg>q`5$acsXR&63qgn1YiUyg4_&R^bm+k znGv{0$+LR13Z&x3vQbs+Dg=kvq&3;a0Sh|J_4`0>qg3lQbpUuR+C;ns{!YM)Z}1Oq zSzwO!8Hh!6NA>tvUo1OQ@aZE%skUjvrVggCkVMKSFI1rnCfNYy!~+;)!_n@cZK$(( zq}0p=l^~W_*v?%AvA=;RTjV;VmrcyzKdfHpFM% zWl&3bc=(}Bh{i4&ei#n zq4u0pAApY!xoa!1ZhEbfcd(fa=qt)KLn2&vIMkuqo0wg6fS;gdvw4?V1Z83hW>s@Z z8zCr(&;T`neivheMYGiAhPM8~o)a3K2fVn^!6fA`j zMEQ84FUgM66R&e4+|(RRLe=%5u4_yL}PWA%<;=Lv+FNa0pIYO?J>_SwtXsB3hCf6hE?b-a3X#<(?5JT!#g; z!YB2h;)}|X*c8Wq!LVvLV31+g2f#CL=BbYg9;R?6Q9p?sL`OI9>_vbWOQ#O*>_x3j z>>drvg|G&)8%9vI&^a>{8IQ-Z$imJXLUO_J%Xs1v_iGG<8f>j?h_@@{ByyxPGw?v2 z7|)E)l*aREESguk@|j7zrLqQNYysi7K3G$u9-KZU7)*ou7n^LVO@OHJn@c%bvk_p{ z5G$BM3hR)L1BmOEJ=`Bh9=oqeum_u*Op9oCvNy?a1c?$wyl070c z#Y1t}mN25P;9Q)E@qDH$(lz74|1)Z$>jF&WlUTK-qTzU2$?lKXRl}t!PSvIec1WEn zOy)|K<^W7YHpmfxROLe$Xy>skPWfC-vd@4LQjDN#4ANG1YdvC36*v#E(0O3uS0Gl3 zNN&fxtCvFNbc zm(_BDOH*p&o&BhFS(@#g%0z}{vmPMBm=aSjn#-$Jw$Q663%N5KFL)yRJ$6-B1RU1N z?5v38MTrE;Cg$lTFi-M;VxU%v2d&z`v@W;g4T%ziT(P$0o7ahUEoA^mNYo-T;R@QW z@>oZ#0}ym1JGuvQ*ll`H&y4nkrV-VfKMV*)hmlBf5iOBNehmDx2Aa58h$*U(Rh6I- zY3ziplFg3~c_z#=5ztgNgS0Bk5n`^dSlK%$QW^I#B%P~y(gvtQCj>o~vjd<&gwu8~ zOB(>S#1pg;6G&deS{cQULoTOx=MJxcf{<9^LR-IQK}@=UAYQ+UW~;oTrO-Vbo9>Pk zIxIZn1$O|#RUif+TmT6o2O;W9W|>hJ%uOl{T7Cx1wlbCI?HcHC z^%_X`7{$7B!8}$?xH-i~ldxl?LDrUaiY||+B(Yo})0l8i+y5{RMU0t}GQlca7{s}?cM)|D%02BIj0;vLv&7ET4z2VQhM8c8JC5QDAOM4w6UTujR zjE!mnY+Zu8>@3$6$(PQm@X7<9)lp*);T6l3778bQg@ds|ANF=p>s1T{8>TmgQ@u>q z^1a6R#Gzp*S!)MfH49`BVh>yMLUCWTTU6l;2Dn6Tl-WnLcGcWmgKP0D_hAt-jj>Wj z9ZjW$hnq0mLNWoq!Pq6s1&L|F-f#0pKTGw2$j}K93)q%2FtE#DH07h*)VQ%ff|WJ_ zVD)tgj@Q@tX!!)*i#!2*)-U4{=ST4y%Yv5x3(|zpos}a4^qx9@J1-&?21CcYTq^v= zH25o*;A&px2a$CFAxp^yW&FNK^gVAbU$BxF7Eu>;Czjn24=pl5Bb1o%e&xh!{lr^v zqpxGc*b#>uQAR!slK8*6fR^u9HvHFl2RNv71+P4T)(EK+R1`J;ZGJ)Zf66kzzAxlg zRI>;E^>4}~xQ^@93V*--O{oHu4)NOzfa?FhTXpX$+EB#*cZ=y=!gY%4|8A|k_v_~U zZc)5y5dZ(hp#85AdbgtY@7-#_eiFb%k^2m$7k|WL%X+>OI;&rB6`>)vGnA1V5PC;38!8RknW~i<=B%iG0!urr z7WamY+HhQ}uc443r4UG7;W$oom-(%$=&`0=i-&8CFu8QGU__g%x8!4poC#j(2&ce) zu5TYKb}qp?3-DM#&ZyR0rT#9HimVA;B$;wWvQN&0{$uQCNKrQBcHp9jXwv zNsWk&n7ltakeoyF^;=RIPe`)KYa3hM5ADOtCj@n^L23+OECS<2RM?%Zwm-qWQlsSc zA-Z@=q~JaZInD7LvK6RlpiyejaxwfnO`}=6C7Oss`m9GzSh4&gs3FVUd0VtKgbG4s zVK8&MuGj^HZlnfeBo`V^aJhvz0VR0mcxJGS?%bi)s4Z_GpM%hM;Z}=$f;S+Y&)XnT zy#5;Ssh5Faw=+B<5Sk*JYHNQn8+E#6 z3~I7k__EBsCs5+(SfRVQwxb${N07M8ZGqabKb_R<@EYrL`~XD^&p_-%B{9?yi{crg zDk|i5V2(nQ+^!|l{%~y|h*D$}Cp?3MtsAw1q>g5(ry2`?nWIiqy)L#l zB?>w88}OlE9_+~U%V>WRL4_T)sSdG69-Ze=0)$FSq-l-7W4-|1kSipb+C?3m=oBHQ zI62yl$~|I7s&>b#oPyan8MTZ9cm++=qS+e8{Yt>(duPWedEh0O=~8W|=dicY+?wZ@ zEV{P9$7c5;J4Ep_UXzXDL4kk>Q$UyE)u@?eZZ&!j_k?PtGgkO{5kWi#1UcmpGOj@K zG2I~VKi4P;;3zba)PpScj@D-J7;hwG9L03g{SxXH0p@-f#39+KL%=Yd!mxC|@fa7I zTj{wT=`|~8e40Juyxrbm*39IdV_YVr&~`tt zlA8^dRPga3ceuAT3a=1E#tJhit%_QJWqZKs&BJ(n1)AytkY6;q3qAE(Biv-Nl0yc7 z0Z~woTnXgyz!vcKbPPALR{1Hs=oGG;LJ|dv{&E>_%j6U}99J5(Ku@9DC{mf@Q}m9f zWY*d^+|lD*wPLl)JYMGZFa#SjTEpOtzkS!uUW;-Wi-N8eby3nhKN z+;t#n090e3y}MD+RdX>N(a1`_z?*TX&ki3Z=yfY%xW-v_%ACj17a=DTbido8uplMB zRAz@RVF|Itlg(0VNUlA~Wm=$I;0T?<_UA#1!rXqH3C@toVmJ`U$X-T^H7+0JwQ{Ic zYEEuhk-p1ZVeT0jNFQ9 zF60EodT3^;^^YaS^L`P}1OzS7i)q-z-K)Ii2)8q7M@OT#^O_Y(d!70h zeA%4S2540_uDb9Y6a6|??l*X+EX8E6Y^!kZCN5l|S%w^SQ9~fr=FDZ=@j&1W+}z~e z3b&zkOp-fqSV6BXlIwoW>7Mp8v1Iq-jG~B$f_YnTdZ73sxJVEipbp-^_YJ<^G&2 z^bVIByeLg?!)Ac%_`XTQcKX{Dpw5Pfo7S*X$oo(!?*?vO=Y=^8dxzw0kl(RFMAxaP zpU_g1v(*d!MzIZasWn-WDSI7Gx0_t9-68D&u$|lZ`Uc4ih@~cwZcUrm=ojj3Iadd8 zn0O1fw>iu#FWT42H?5e>HnbaLdD!L@bTrzHkAe{FSnj|!JeU6rQ^s2>ixn=vjjz}E zf!r8M5rWf&-T3J6V}PeoX(4Rj2%Rahw6ne;x$+VG2{qa( zOrVwp!h<%WUU2$Bg-XQ1@)p7D$DLchfwuGZ05R{vEP9(J6vV>vjtItI?{^CIhBg=u zXz!xwyM&h>Z>{tjG^Kul6lYMcVY1#Qd1`V7Pzuy^Rl99q9gP@twc9SM+j=8cEBD)DCn^sJh8_&0!kl=2pT(1=EV2TZHxoW5C}#fff8>0-%zFtj?kg zfhW!J&UdUyM9*k0B#;`XP4xN)jD~i#1#Cl;_zv$x^1bA3mT3-;f*zry`)hnms`r}< zWB!^zFM0+l_Ew3{CzsK2%i9*|p<}w)8>sP*nG61UCi#N>;C7hS-5p1e z4oJeGZ0NTYhOr+#8cIFGpL~^PnCxxFh~Kgp1_Z%`dXnY>n5MQBJ?p6Hz!hSdw`j-! z(BhYuAV-ZveTyw1$`U5cMPeLcwho^*V)S1?EMvsYH!LP^gLtjOhom^LT`$REKy9f{ z7{uo{I7Wchzpz}K8(hx|f(g^*HyzuGq440C3dZ_`p?kP?^#WK1m9c-;9!t)NW@L2BBS+l}>* zFSQRFo;RVsUWJzdb6W4wgtQQFW)txJ0Tbw3B?WwiYe7)Jv3MH$%ds^DsW_yDvD_lw z9M7Y@Gk_fGXKZ z|MqjVDd{Y+{8NVne*;jIFmGgQ&^OzQ$naVcb5BO#c^(U(^lJ!3QLr+Gj|bkO`SX?) zh`9&*4LJ6~3AMNrp4MR;uN^p)gMsfRQ}FjZN}j1@uKAk7g3*|rtOH&J^WBLE>iSF= zqhYdb&v7HrKADP#@G|U!CAZo+%f7`cb!%&61n#9%bq8ZkWa2>9V`)6#)5y&IHy>TK z4E9+6Q^EG>@uo&|ecC9g33J_ek+EDML}lji#KA%uG9Fxt8P7@64)$8QYTjp&vxc9wz zi@a^+_SgUK)~mYj_$~1RANar>chvpi9e2n({zLNzcm7fHzxRKz$?y74cgp|KbEkaI z&c+XWydP5j;NxEDLw7ZN_^vzJTJQ9J)YJZ&u`Xl}BruM)eej>2*zkmFXKlusINAGF*MDVW808Y&N zgOArVrpy601&d;d7lDL?S1|MkIp?s+8mk^TP9hj-omnNM{l)sFjj zwLTGOzW+Wc_tC6=pZkmV-1%VmuHJhd-Tj%n_jG>tf!(eD|@2A zt_QpS{GmO25}(_%YyU$Z`r@9Q2kuKhtG7S?(C+R>wa-8NSHRK}kNmgHL(!){yF0h% zp0Dod+SC2mvx)n^|4`fR$MwC2lKiXorymVGzW1Xm_kO2;Pe-QvzQ+x%I~n@oz7KwR z@0Y&!myaw(AMbzU>yJPFm&wO^Q@xLU^^4JOJhm(M*rU1qcjxwfs_!fMcfS14Q1bJm z$=#2BV^7~#KT&*Ke>FBpIpDsN4#LyFuzOe7pPk-$jV_yxk zp3yIT^P%tj^`DP?O@H$1!!!G`-+$Ww&8HrJ`kBDwllz|i`V;-n+&TQ$$$a7IZ}&xi z@Xh=FHrGG%t+3s3#o_Xa%rq*{9BvxlC}{mtJTD1Ph9 z&wjt~+;_kAqwjw4d(ZYi|K0AXfiHYv;>mB1exWq6=Fsd%wx%94fBqLgf4+F;r%#@o{_3ge|G9MN*(=l0 z!!I8G-$(Z^{QS_-fBatMA5XkI_w3x<++wBg^sz@yR$e$gcX;LKH(r|`cxmC#FBjq` zUpaa0m36eH*7QoV)m)<(0GVoL;!Ryt;kqC!6c9UA}Vp?Mqwlz4gQG?YDpR QFE4P;{~fR4AGq~@09Ok@(f|Me literal 107562 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 z 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 Q{{$=N=db^Nv7_?;2Xz^}ga7~l From 86140d1150bcfa315092635394a92da9ec415362 Mon Sep 17 00:00:00 2001 From: Insta Fluff Date: Wed, 28 Jan 2026 13:14:47 -0800 Subject: [PATCH 288/356] Fix wonder at 3rd column alignment --- Art/Districts/1200/Wonders.PCX | Bin 37182 -> 37166 bytes Art/Districts/1200/Wonders_2.PCX | Bin 40012 -> 40012 bytes Art/Districts/1200/Wonders_3.PCX | Bin 23194 -> 23179 bytes Notes/district_todos.md | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Art/Districts/1200/Wonders.PCX b/Art/Districts/1200/Wonders.PCX index e882b03582af7345b9833aa26d1b623c057eda90..5c29c17bb2964e43fb438bd97685b9fc0d7d84da 100644 GIT binary patch delta 4148 zcmd6qdtA+F9>-r#)Tuaex#vDi$w;}(F3-F)cCBH| z8jEEXqnUA;{rwI_S)r6V>8e~BdI=e&=eM6=-S<4d=l5K{pU?CB zE`6OlkE%BDzXH|$F(6eNMq@L9vZlpR|@sb+WfVh?>X{AW2STkZZ zEzw#isbG&0SJ0q-D)5T$3(#S2%Hs z6S%s3PRLQCypYb--Z!{|RSFjm9y^tH5XJXUtfB@we=vH55uHAE>S+G4{DV2Q0;A`2 zz8vy`sfGN%@H~GkpX0gOe6F@m=%7iGzmjLvSl67iQzzYXq_H2EVEmfgpi>6R!R*29q=;@Z@d3X#X(t8rgsCssWZF)6 zT0O)cG#~l`9BAf4_%q>V{RtjNng@b8<^o>J85Tl{&J+zJ1npfP9zbp!*0Uh!M}I^R zxqW#22!j42EyYn}TmFi1Wmt^?f3kXk@tzv#3qBnA81pFoC;&9FZo&NCSj$0In{v!s z$5syd+1|l=7~09fP&*#$q%Cy@<;Z(g-(`qZmiN?nZp}7d2!7Z-8&mFYP)GMD;0LA1n#Oygkp^%>nn(W>0>@8{O$?8gHXGv>Mwi~$EMLgH20DR3}vwf#Tc|2$9auvLix7HSQ zqmhirPAE%L!e8L-at&gv(N-soO){YeTy%Q>WoY#HZ#BDH5Z~&;yJBzuYF`s~VjSJd z?8Vop*{#xEdUMe^{xeYEE)oJSWVPae}ctMQo^) zy19-wFJae(qba-)W~(sKvsDShxiZa$?{=L;ifO&81=!^})v6E`2`U~{naHg46w=|& zv#d;1!oVD{dLdorHUrsK5M^-YIVH~PAQCw$jJ!*+h|V7%W3sQMP-0@`oPMuZSwKqlVG3y^^Y0U2m*T%a?U9=HL$JPmXKM+AL}-v1ah z8Y~Q2i%|s!JAsRW*J88{!BS9Hk%mdEQrLibilvyGPKY(=7?OfXe;zUdJRTB<#UP<( z;IPnGlQ!tfhW9t~x$L0i9jvvhgf_YkwRLd^a4%CuYloXkC%f#j!%c-xJ1gpnUQHQoU4(ZU{~Y<7*5IrcQ9{4Dh!82 z$wAMkRH*AflxRCQqLQIHk7yYdbpk6Xb4th>n2~~eP z$pchRS^>33$H>9Vm?feY#CU;ru~~43Z(}{d>e#Jtjd_zjz`c`y2RE^w;tkH4@&ztG zZBzWHFnP4Sn|+Befez~*U}(aZ7axOULT z(^csGWI`bLOTs0L>i7(Q@WG4=80|M72Y~e-=VKQ8W_p1oGf!i7S+o4W3$u=6)(dC5 zf%|6XVKv<6$iT!o+ptOx=h%S*6IWyPwj|nsM-t~)cHq`H&1T}9RA27-0j`XzBdP@6 z!6<>ZF2_~RX6^*+!P2>AVD?|CRlGnET8r7hd*x zkNB_brw{j8oMhR37uuaPLh|;GbT-MZ=L6&wtxb{+YGPbl0kbI3Ty*H*0y!ozUNVqlQUk&<_sha?-q_Im7HU7B zFOnlkB@Iq7CyjJ^$~5HXcPW;nmflE7C3mRrd?``W+4HlY_xkzvq=M=$$RoGt!3B;e zxwhaCw4b)n1>CprJ7_;LRR*q4y$J35EOG}^7hQn%?bBSr_%szVZunwnFk&$e?dvZQ z9UyoKkG$1dDmsAY(woRn4J+>#Cts+4?XreZ3m` zowQ*rNHVA26sc#8oupv-22i*Jr_C;8xK9UTzHp zJ+i;TK)r4IG3g`Qyuj*hUtlp-0N^T|=uPj%*yS>Rx$D%IZ zZV%>dUr5-V9d=l8>5f_0gDr?v^yrSs*qhEB7NFVAi55jTIk-9-u8yn6zaOUIUn|#e z9~n|iGj}Qml;BL@Tbo7o(&C-I@4i;`cR9TK&hXteGNb1^19yZEy%&6#F#FJ@5|{l~ UeSQ%9$9utV3-gTM=i2@L35BzC5C8xG delta 4185 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 A{r~^~ diff --git a/Art/Districts/1200/Wonders_2.PCX b/Art/Districts/1200/Wonders_2.PCX index 89bed63ddb15fb2fa07bffdea5da8d5089727da5..82137a44338d9bfbcd51a225929c3d9d7f57a8d6 100644 GIT binary patch delta 44 wcmX@JgXzo;rVWV!%!mFx-kcJU#0X?O+I%u_nH`YvadOdw5D>ljOV delta 44 wcmX@JgXzo;rVWV!%m@EH*_;xP#0X?O-h47}nH`YvX>!qo5D>ljOV diff --git a/Art/Districts/1200/Wonders_3.PCX b/Art/Districts/1200/Wonders_3.PCX index 5bc8a7f15af118709b379789568a4c3d15133d12..b399d7950640dd96973f0e3b2c0969ec272fa7e4 100644 GIT binary patch delta 5724 zcmeHLU2GIp6waYQyI3kzC={fC6iBg90RdYmg_f2O3nCIhQUh(#=!@Y&jWLqJ2Z=w# zNRC>f;lZehCWgd}#E2 z_fu=Yf$v1-fE^u?bwGG3YCGeVs7A(fwp!r<^;B8y$FX_6yJ<|1x_HfETOd^1v&vnll%m6}QjLgI4*Qx!h>R;`lshmgmh& z0B~dK{1?G37tK!saG~;O0l4JE1&;&N;O>M&P$U1CumYe8FD)#9Dmi1(GGJ%Cytn`= zqPxNRC_tuPGKT*gg zM6Tv>S}|o!J{xy(O`K}>cWV!@*%42&tJ*Po-F|kp{VsP1(G@CpI~G#q-kBI0lN+ z%h{;>%^7UXSDTBOIhc{n(eiD^88uF}Y=z(P^DSqf60^6aK^-33`T;+kmYK?CZOeR% zpB87PuvPxdLmX+lvX;SRd?RZI<8N7u;7`1gmBLXvV_Tf$r#jJ30jd%X@4LlHx8mVI zry36a4i0=pQEE>eRXmFKS0__l{2)Ms{Ia0lQS_72X7?$ixRf9}$k|iDZv+fDOe3RN zrRA1}l6n~-F`9w@9L*hR{a6hgK4opC^eyj6y}7mhaZ)E{t;(@g`L-(es$fXs9@N9CBddX}CK_g#G^&caWmcZJ ztLxZvR<8a#-3TqEq^;$DtQ*1L^+>@84_A+WTQ@?tf+loh?D23yHy-%&Mi1F~Npj!iz|luGb?2@A$kNq9+mR2DO{L7z laf@u~=HC3TYKUyAoS1)B-6or=CvoP#RYW!+f4XmK#9wf1j(h+B delta 5760 zcmeH~TWnNC7{~KbpcPAnaxF*&xusaBpn#Nn3nfHAL?cLQpe-7G<3Wuvl7kNrFT_YD z3eoUj)I<|QVvfX!Ddu!(OIv8|b#1rwwzNyT^iEsag&F@d+tZ$24#~ES^?`l(%{en? zXXcyvzVAQt`6c@15`B4Qjo=QE$4{LUypg8~!9Cn)Gdx8+UBn9R;~A*(@l27!jk0%* zmN3D+XRmCIXys{plUFZ}*qRK?z>x4}w^CKn5x zaGJ^(n-D`z=9qASN?20RS}I`Yf-X}oJ1{Yhve;J>uTVC7bS3!NuL8qE_ft1Z39ZHR z?y0Zf`NGr|Jbw}P5}qrd>|6Aw+HLlkh{-s#k}H6E(5B zGY(RtzGo(0yzI!V*Qs9rW7ZrZ56hpONVWQ^Idh5JY}edGs@8v)id+5X6*l&}99xq>Ka7xS7J~XTM%;u6Fm`w-YVJm&!)hH z;MEw%&0^Lh!ML+)W=pewdhRgH4tgF-)xbj69-?~o=2}jT?AW>k;P311QVaWK{XS}C zzV&s~!D?Q35nQ#Qh$S@7qu|~`*?g~F{QDDO*^r_XEx!@k~j5j8uv$HK0Y+dqXbO^Mq;#cW61 z2b9lJ<6@|kHO3vKBDODn8I`m5;&)R8`zwAS%&3Zwp&B-A$87BmFA^QRi{}bQ-N&g$ zi%~839bN$E_rmcF!zes|!l*Xt{>(#F6B@dBH$E1;U9pK9wP_xM8zqK%Zv(p#H`(~2 zOYxp!C+Oo3dZS9l)_wJ?^`Lmbg^ECvhI-=z>STlpZfzObnZGZ)!L8H2Yfe_fY`c`X z&5A9GemgsO2?q5l-cf7-J79Sk>OG3L73;uu-pVUb?^e8}SOd25W?qB(O2sP03a}Yz zr%q^syHE&Tu2=>(V#o&6I2HdGA^_h|yzouR?KsT@E91RY|tBPr0HLpaqL&ypT+9k1NU?snaGE?*@`V_kr zZ^A4gazv+Mmtu#aimh_o z)G2xtYZVK5KFUf_qv%$wR?Np*3E>tM9P96f74k(|;)WpHKiqZy;kn{D38(2so zyrM)=PSh*pq`XjwER@ZnKv7PoS;(QAMHUuWh<1^yCRA)fx}^w|~gtMzo)%y=eoML&MtVl! zeG5Ni-R(EaeuMj+LUSTMq=%SZVed&+_Z#Sa_AYDh=F)i(8q)s3v0+9NcC0t1{Ly3y zzX8!_7p`{UZA}N7H6PgJ51DX=%Qtoj$}XV|lK?f90qruTU4|VtB?qJ*rhI6AeHf|| zrlt&S858|5xa3W+BoTl>A>1J>4zD>nv|4r&<`_>Y2&0HW-^EUf$E1; z8PTU2R%IF3Vy{LPJ<*_o)+vlyP|#Z-dIJ01gM@r^f)b#fXuand(;xlrI0`{|h@PN{ zTTf?FPqg0ioEeWQ97iK40qTi1-~c-Yf)b#fXhW|ieP{d;2}*!^0{ Date: Wed, 28 Jan 2026 13:25:19 -0800 Subject: [PATCH 289/356] Update modern marketplace art, redo color indices on commerical hubs --- Art/Districts/1200/CommercialHub_AMER.PCX | Bin 48484 -> 46144 bytes Art/Districts/1200/CommercialHub_ASIAN.PCX | Bin 49644 -> 47332 bytes Art/Districts/1200/CommercialHub_EURO.PCX | Bin 46028 -> 46327 bytes Art/Districts/1200/CommercialHub_MIDEAST.PCX | Bin 46313 -> 45706 bytes Art/Districts/1200/CommercialHub_ROMAN.PCX | Bin 48896 -> 46117 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Art/Districts/1200/CommercialHub_AMER.PCX b/Art/Districts/1200/CommercialHub_AMER.PCX index 32481d25995a9310299a8b3361d8a5d5b2d67361..363a9df52a1fa5c85ed3bf556903605ad9b50de9 100644 GIT binary patch literal 46144 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 literal 48484 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 Lks-g}+5i6^cO~K3 diff --git a/Art/Districts/1200/CommercialHub_ASIAN.PCX b/Art/Districts/1200/CommercialHub_ASIAN.PCX index bedc7543d11bc35936ba89530e70c015e24f8c13..1b33f24b176b3924c8df9ecced66cd3c4f7ff307 100644 GIT binary patch literal 47332 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 bFPz_R>;K3jO-pii)@@q3VDbYZ|Ns93Mm()+ diff --git a/Art/Districts/1200/CommercialHub_EURO.PCX b/Art/Districts/1200/CommercialHub_EURO.PCX index 2e1f53debd157d45a360bea7ca2aa4b2cef14618..9948fe0c9f1dc673d688619ba01824a2cead1de6 100644 GIT binary patch literal 46327 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 Z``cTu&Hv*3?ys(9efj0b<3#%V{{RwyatQzc literal 46028 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