From aeeaaebc1abdfbf75f4679db2a6548e4dd15569d Mon Sep 17 00:00:00 2001 From: Instafluff Date: Fri, 1 May 2026 17:03:56 -0700 Subject: [PATCH 1/7] Fix workers on coast waking up by setting unit state; Loosen AI worker restrictions on coastal movement --- C3X.h | 2 ++ injected_code.c | 49 +++++++++++++++++++++++++++++++------------------ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/C3X.h b/C3X.h index 5294675d..9ad6be37 100644 --- a/C3X.h +++ b/C3X.h @@ -482,6 +482,8 @@ struct c3x_config { int ai_auto_build_great_wall_strategy; bool enable_city_work_radii_highlights; + + bool do_not_place_barb_huts_or_camps_on_impassable_tiles; }; enum stackable_command { diff --git a/injected_code.c b/injected_code.c index c1918076..36214655 100644 --- a/injected_code.c +++ b/injected_code.c @@ -20733,8 +20733,18 @@ 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->vtable->m35_Check_Is_Water (dest) && - (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) - base_validity = AMV_OK; + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) { + bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; + if (is_human) { + + } else { + // Allow AI to enter coast only if their territory, else constant + // AI worker movement across coasts can look odd or almost like cheating + if (this->Body.CivID == dest->Territory_OwnerID) { + base_validity = AMV_OK; + } + } + } } // Allow land units to enter bridge tiles @@ -20874,8 +20884,18 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr (base_cost < 0) && (unit != NULL) && is_worker (unit) && to_valid && to->vtable->m35_Check_Is_Water (to) && - (to->vtable->m50_Get_Square_BaseType (to) == SQ_Coast)) - base_cost = Unit_get_max_move_points (unit); + (to->vtable->m50_Get_Square_BaseType (to) == SQ_Coast)) { + bool is_human = (*p_human_player_bits & (1 << unit->Body.CivID)) != 0; + if (is_human) { + base_cost = Unit_get_max_move_points (unit); + } else { + if (unit->Body.CivID == to->Territory_OwnerID) { + base_cost = Unit_get_max_move_points (unit); + } else { + return -1; + } + } + } // Let the pathfinder consider bridge tiles reachable for land units if (is->current_config.enable_bridge_districts && @@ -22535,8 +22555,9 @@ ai_handle_district_production_requirements (City * city, City_Order * out) if (swapped_to_fallback) { *out = fallback_order; } - if ((swapped_to_fallback || should_mark_district) && (required_district_id >= 0)) + 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; @@ -27625,19 +27646,10 @@ patch_Unit_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, bool 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; - } + // Allow AI to enter coast only if their territory, to prevent + // + if (this->Body.CivID == dest->Territory_OwnerID) { + should_override = true; } } } @@ -27685,6 +27697,7 @@ patch_Unit_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, bool this->Body.Container_Unit = is->coast_walk_prev_container; Unit_set_state (this, __, is->coast_walk_prev_state); if (is->coast_walk_restore_goto_path) { + this->Body.UnitState = prev_state; this->Body.path_len = is->coast_walk_prev_path_len; this->Body.path_dest_x = is->coast_walk_prev_path_dest_x; this->Body.path_dest_y = is->coast_walk_prev_path_dest_y; From 94667fd0c5d816846696fb64e03364804f70bf42 Mon Sep 17 00:00:00 2001 From: Instafluff Date: Fri, 1 May 2026 17:21:18 -0700 Subject: [PATCH 2/7] Add missed movement OK check for human workers on coastal tiles --- injected_code.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/injected_code.c b/injected_code.c index 36214655..22ea016a 100644 --- a/injected_code.c +++ b/injected_code.c @@ -20736,7 +20736,7 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) { bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; if (is_human) { - + base_validity = AMV_OK; } else { // Allow AI to enter coast only if their territory, else constant // AI worker movement across coasts can look odd or almost like cheating From 3de19a658886a4db405e7aaa7912e8280ad0a8bd Mon Sep 17 00:00:00 2001 From: Instafluff Date: Tue, 5 May 2026 17:21:50 -0700 Subject: [PATCH 3/7] Fix units not loading in transports in ports if naval_units_use_port_districts_not_cities is true --- C3X.h | 2 +- civ_prog_objects.csv | 2 +- default.districts_config.txt | 2 +- injected_code.c | 139 ++++++++++++++++++++++++++++------- 4 files changed, 115 insertions(+), 30 deletions(-) diff --git a/C3X.h b/C3X.h index 9ad6be37..4d5cf32a 100644 --- a/C3X.h +++ b/C3X.h @@ -984,7 +984,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_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_max_index = 2, .align_to_coast = true, + .advance_prereqs = {"Alphabet"}, .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 = 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, .img_column_count = 4, .btn_tile_sheet_column = 4, .btn_tile_sheet_row = 0, .align_to_coast = true, diff --git a/civ_prog_objects.csv b/civ_prog_objects.csv index d96a42ac..9234b28f 100644 --- a/civ_prog_objects.csv +++ b/civ_prog_objects.csv @@ -190,7 +190,7 @@ repl vptr, 0x6659F8, 0x682A5C, 0x6659F8, "City_Form_m82_handle_key_event", "voi define, 0x437540, 0x4390F0, 0x4375C0, "Improvement_has_wonder_flag", "bool (__fastcall *) (Improvement * this, int edx, enum ImprovementTypeWonderFeatures flag)" define, 0x437710, 0x4392F0, 0x437790, "UnitType_has_ai_strategy", "bool (__fastcall *) (UnitType * this, int edx, byte n)" repl call, 0x431038, 0x432AE7, 0x4310B8, "UnitType_has_strat_0_for_ai_prod", "" -define, 0x44CE50, 0x44EE40, 0x44CED0, "Unit_find_transport", "Unit * (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" +inlead, 0x44CE50, 0x44EE40, 0x44CED0, "Unit_find_transport", "Unit * (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y)" inlead, 0x5C5F70, 0x5D4D50, 0x5C5C80, "Unit_select_transport", "Unit * (__fastcall *) (Unit * this, int edx, int tile_x, int tile_y, bool should_show_popup)" inlead, 0x5C5110, 0x5D3DF0, 0x5C4E20, "Unit_load", "void (__fastcall *) (Unit * this, int edx, Unit * transport)" define, 0xA526BC, 0xA74EB4, 0xA5267C, "p_human_player_bits", "int *" diff --git a/default.districts_config.txt b/default.districts_config.txt index da740387..d4bef795 100644 --- a/default.districts_config.txt +++ b/default.districts_config.txt @@ -357,7 +357,7 @@ happiness_bonus = 0 name = Port tooltip = Build Port buildable_on = coast -advance_prereqs = Map Making +advance_prereqs = Alphabet dependent_improvs = Harbor, "Commercial Dock" heal_units_in_one_turn = 1 defense_bonus_percent = 0 diff --git a/injected_code.c b/injected_code.c index 22ea016a..de866553 100644 --- a/injected_code.c +++ b/injected_code.c @@ -220,6 +220,7 @@ 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); +int __fastcall patch_Unit_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, bool param_2, int param_3, byte param_4); 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); @@ -20736,15 +20737,16 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) { bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; if (is_human) { - base_validity = AMV_OK; + base_validity = AMV_OK; } else { - // Allow AI to enter coast only if their territory, else constant - // AI worker movement across coasts can look odd or almost like cheating - if (this->Body.CivID == dest->Territory_OwnerID) { - base_validity = AMV_OK; - } + // Allow AI to enter coast only if their territory, else constant + // AI worker movement across coasts can look odd or almost like cheating + if (this->Body.CivID == dest->Territory_OwnerID) { + base_validity = AMV_OK; + } } } + } // Allow land units to enter bridge tiles @@ -20887,13 +20889,13 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr (to->vtable->m50_Get_Square_BaseType (to) == SQ_Coast)) { bool is_human = (*p_human_player_bits & (1 << unit->Body.CivID)) != 0; if (is_human) { - base_cost = Unit_get_max_move_points (unit); + base_cost = Unit_get_max_move_points (unit); } else { - if (unit->Body.CivID == to->Territory_OwnerID) { - base_cost = Unit_get_max_move_points (unit); - } else { - return -1; - } + if (unit->Body.CivID == to->Territory_OwnerID) { + base_cost = Unit_get_max_move_points (unit); + } else { + return -1; + } } } @@ -20939,6 +20941,15 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr } } + if ((unit != NULL) && + (base_cost < 0) && + 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_Land) && + tile_has_friendly_port_district (to, unit->Body.CivID) && + (Unit_find_transport (unit, __, to_x, to_y) != NULL)) + base_cost = Unit_get_max_move_points (unit); + if ((unit != NULL) && (base_cost >= 0) && is->current_config.enable_port_districts && @@ -21731,6 +21742,40 @@ patch_Unit_can_load (Unit * this, int edx, Unit * passenger) void __fastcall patch_Unit_load (Unit * this, int edx, Unit * transport) { + if (is->current_config.enable_districts && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities && + (this != NULL) && (transport != NULL) && + ((this->Body.X != transport->Body.X) || (this->Body.Y != transport->Body.Y)) && + (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Land)) { + Tile * unit_tile = tile_at (this->Body.X, this->Body.Y); + Tile * transport_tile = tile_at (transport->Body.X, transport->Body.Y); + if ((unit_tile != NULL) && + (unit_tile != p_null_tile) && + Tile_has_city (unit_tile) && + tile_has_friendly_port_district (transport_tile, this->Body.CivID)) { + // If directly adjacent to the transport, load directly + for (int n = 1; n <= 8; n++) { + int nx, ny; + get_neighbor_coords (&p_bic_data->Map, this->Body.X, this->Body.Y, n, &nx, &ny); + if ((nx == transport->Body.X) && (ny == transport->Body.Y)) { + patch_Unit_move_to_adjacent_tile (this, __, n, false, 0, 1); + return; + } + } + // Otherwise, set the unit on a path to it + if (patch_Trade_Net_set_unit_path (is->trade_net, __, this->Body.X, this->Body.Y, + transport->Body.X, transport->Body.Y, + this, this->Body.CivID, 0x81, NULL) > 0) { + Unit_set_escortee (this, __, -1); + Unit_set_state (this, __, UnitState_Go_To); + this->Body.path_dest_x = transport->Body.X; + this->Body.path_dest_y = transport->Body.Y; + } + return; + } + } + Unit_load (this, __, transport); // Tie the unit to the transport if configured to do so @@ -22415,11 +22460,6 @@ 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; @@ -22555,9 +22595,8 @@ ai_handle_district_production_requirements (City * city, City_Order * out) if (swapped_to_fallback) { *out = fallback_order; } - if ((swapped_to_fallback || should_mark_district) && (required_district_id >= 0)) { + 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; @@ -27643,15 +27682,10 @@ patch_Unit_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, bool dest->vtable->m35_Check_Is_Water (dest) && (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) { bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; - if (is_human) { + if (is_human) should_override = true; - } else { - // Allow AI to enter coast only if their territory, to prevent - // - if (this->Body.CivID == dest->Territory_OwnerID) { - should_override = true; - } - } + else if (this->Body.CivID == dest->Territory_OwnerID) +- should_override = true; } if (! should_override && allow_bridge_walk) { @@ -35547,6 +35581,39 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t return base; } + +Unit * __fastcall +patch_Unit_find_transport (Unit * this, int edx, int tile_x, int tile_y) +{ + Unit * transport = Unit_find_transport (this, __, tile_x, tile_y); + + if ((transport == NULL) && + is->current_config.enable_districts && + is->current_config.enable_port_districts && + is->current_config.naval_units_use_port_districts_not_cities) { + City * city = city_at (tile_x, tile_y); + if ((city != NULL) && (city->Body.CivID == this->Body.CivID)) { + int port_x = -1, port_y = -1; + Tile * port = get_completed_district_tile_for_city (city, PORT_DISTRICT_ID, &port_x, &port_y); + if (tile_has_friendly_port_district (port, this->Body.CivID)) { + FOR_UNITS_ON (uti, port) { + UnitType * type = &p_bic_data->UnitTypes[uti.unit->Body.UnitTypeID]; + bool same_civ = uti.unit->Body.CivID == this->Body.CivID; + bool naval_transport = (type->AI_Strategy & UTAI_Naval_Transport) != 0; + bool undamaged = uti.unit->Body.Damage == 0; + bool can_load = patch_Unit_can_load (uti.unit, __, this); + if (same_civ && naval_transport && undamaged && can_load) { + transport = uti.unit; + break; + } + } + } + } + } + + return transport; +} + Unit * __fastcall patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool do_show_popup) { @@ -35734,6 +35801,12 @@ patch_Unit_ai_move_naval_power_unit (Unit * this) return; } + // If carrying units, use vanilla logic + if (Unit_count_contained_units (this) > 0) { + Unit_ai_move_naval_power_unit (this); + return; + } + Tile * here = tile_at (this->Body.X, this->Body.Y); if (here == NULL) { Unit_ai_move_naval_power_unit (this); @@ -35784,6 +35857,12 @@ patch_Unit_ai_move_naval_transport (Unit * this) return; } + // If carrying units, use vanilla logic + if (Unit_count_contained_units (this) > 0) { + 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)) { @@ -35809,6 +35888,12 @@ patch_Unit_ai_move_naval_missile_transport (Unit * this) return; } + // If carrying units, use vanilla logic + if (Unit_count_contained_units (this) > 0) { + 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)) { From 2c13f1d49e9c91d0298d18ac32856627e73a8e77 Mon Sep 17 00:00:00 2001 From: Instafluff Date: Wed, 6 May 2026 14:28:52 -0700 Subject: [PATCH 4/7] workers_can_enter_coast now only allows coastal movement if originating from land or a bridge --- injected_code.c | 78 +++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 45 deletions(-) diff --git a/injected_code.c b/injected_code.c index de866553..32612e18 100644 --- a/injected_code.c +++ b/injected_code.c @@ -4704,6 +4704,24 @@ tile_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_allows_worker_coast_entry_source (Tile * tile) +{ + if ((tile == NULL) || (tile == p_null_tile)) + return false; + + if (! tile->vtable->m35_Check_Is_Water (tile)) + return true; + + if (! is->current_config.enable_bridge_districts) + return false; + + struct district_instance * inst = get_district_instance (tile); + return (inst != NULL) && + (inst->district_id == BRIDGE_DISTRICT_ID) && + district_is_complete (tile, inst->district_id); +} + bool tile_is_water (int tile_x, int tile_y) { @@ -20732,19 +20750,12 @@ patch_Unit_can_move_to_adjacent_tile (Unit * this, int edx, int neighbor_index, // 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))) { + Tile * source = tile_at (this->Body.X, this->Body.Y); if ((dest != NULL) && dest->vtable->m35_Check_Is_Water (dest) && - (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast)) { - bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; - if (is_human) { - base_validity = AMV_OK; - } else { - // Allow AI to enter coast only if their territory, else constant - // AI worker movement across coasts can look odd or almost like cheating - if (this->Body.CivID == dest->Territory_OwnerID) { - base_validity = AMV_OK; - } - } + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast) && + tile_allows_worker_coast_entry_source (source)) { + base_validity = AMV_OK; } } @@ -20886,17 +20897,9 @@ patch_Trade_Net_get_movement_cost (Trade_Net * this, int edx, int from_x, int fr (base_cost < 0) && (unit != NULL) && is_worker (unit) && to_valid && to->vtable->m35_Check_Is_Water (to) && - (to->vtable->m50_Get_Square_BaseType (to) == SQ_Coast)) { - bool is_human = (*p_human_player_bits & (1 << unit->Body.CivID)) != 0; - if (is_human) { - base_cost = Unit_get_max_move_points (unit); - } else { - if (unit->Body.CivID == to->Territory_OwnerID) { - base_cost = Unit_get_max_move_points (unit); - } else { - return -1; - } - } + (to->vtable->m50_Get_Square_BaseType (to) == SQ_Coast) && + tile_allows_worker_coast_entry_source (tile_at (from_x, from_y))) { + base_cost = Unit_get_max_move_points (unit); } // Let the pathfinder consider bridge tiles reachable for land units @@ -27680,12 +27683,9 @@ 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)) { - bool is_human = (*p_human_player_bits & (1 << this->Body.CivID)) != 0; - if (is_human) - should_override = true; - else if (this->Body.CivID == dest->Territory_OwnerID) -- should_override = true; + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast) && + tile_allows_worker_coast_entry_source (tile_at (this->Body.X, this->Body.Y))) { + should_override = true; } if (! should_override && allow_bridge_walk) { @@ -35542,24 +35542,11 @@ patch_Unit_can_pass_between (Unit * this, int edx, int from_x, int from_y, int t if (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 PBV_OK; - if (to_valid && to->vtable->m35_Check_Is_Water (to) && - (to->vtable->m50_Get_Square_BaseType (to) == 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; - - struct district_worker_record * rec = get_tracked_worker_record (this); - struct pending_district_request * req = (rec != NULL) ? rec->pending_req : NULL; - if (req != NULL) - return PBV_OK; + (to->vtable->m50_Get_Square_BaseType (to) == SQ_Coast) && + tile_allows_worker_coast_entry_source (source)) { + return PBV_OK; } } @@ -35626,7 +35613,8 @@ patch_Unit_select_transport (Unit * this, int edx, int tile_x, int tile_y, bool 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)) + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast) && + tile_allows_worker_coast_entry_source (tile_at (this->Body.X, this->Body.Y))) allow_move = true; if (! allow_move && is->current_config.enable_bridge_districts && From 2f7f2ed6860e02d5d02564c4c715bb95f6e6c6ce Mon Sep 17 00:00:00 2001 From: Instafluff Date: Wed, 6 May 2026 16:01:44 -0700 Subject: [PATCH 5/7] Dont add walls to cities automatically if disable_great_wall_city_defense_bonus is true; Dont visually connect great wall tiles if not same territory owner --- injected_code.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/injected_code.c b/injected_code.c index 32612e18..62ec8bb9 100644 --- a/injected_code.c +++ b/injected_code.c @@ -4692,6 +4692,18 @@ tile_has_district_at (int tile_x, int tile_y, int district_id) return (inst != NULL) && (inst->district_id == district_id) && (district_is_complete (tile, district_id)); } +bool +tile_has_district_at_with_owner (int tile_x, int tile_y, int district_id, int owner_id) +{ + 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) || (tile->Territory_OwnerID != owner_id)) + return false; + + struct district_instance * inst = get_district_instance (tile); + return (inst != NULL) && (inst->district_id == 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) { @@ -21531,6 +21543,13 @@ int __fastcall patch_Game_get_wonder_city_id (Game * this, int edx, int wonder_improvement_id) { int ret_addr = ((int *)&wonder_improvement_id)[-1]; + if (is->current_config.enable_districts && + is->current_config.enable_great_wall_districts && + is->current_config.disable_great_wall_city_defense_bonus && + (ret_addr == ADDR_SMALL_WONDER_FREE_IMPROVS_RETURN) && + (wonder_improvement_id == is->current_config.great_wall_auto_build_wonder_improv_id)) { + return -1; + } if ((is->current_config.enable_free_buildings_from_small_wonders) && (ret_addr == ADDR_SMALL_WONDER_FREE_IMPROVS_RETURN)) { Leader * leader = is->leader_param_for_patch_get_wonder_city_id; Improvement * improv = &p_bic_data->Improvements[wonder_improvement_id]; @@ -33750,12 +33769,13 @@ draw_canal_district (Tile * tile, int tile_x, int tile_y, Map_Renderer * map_ren 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_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 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); + int owner_id = tile->Territory_OwnerID; + bool wall_nw = tile_has_district_at_with_owner (tile_x - 1, tile_y - 1, GREAT_WALL_DISTRICT_ID, owner_id); + bool wall_ne = tile_has_district_at_with_owner (tile_x + 1, tile_y - 1, GREAT_WALL_DISTRICT_ID, owner_id); + bool wall_se = tile_has_district_at_with_owner (tile_x + 1, tile_y + 1, GREAT_WALL_DISTRICT_ID, owner_id); + bool wall_sw = tile_has_district_at_with_owner (tile_x - 1, tile_y + 1, GREAT_WALL_DISTRICT_ID, owner_id); + bool wall_s = tile_has_district_at_with_owner (tile_x, tile_y + 2, GREAT_WALL_DISTRICT_ID, owner_id); + bool wall_n = tile_has_district_at_with_owner (tile_x, tile_y - 2, GREAT_WALL_DISTRICT_ID, owner_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); From 8f723e42f867f301fb72ec248e359e21404c658f Mon Sep 17 00:00:00 2001 From: Instafluff Date: Wed, 6 May 2026 16:03:10 -0700 Subject: [PATCH 6/7] Move do_not_place_barb_huts_or_camps_on_impassable_tiles to align with place it is declared upstream --- C3X.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/C3X.h b/C3X.h index 4d5cf32a..1e5bcce9 100644 --- a/C3X.h +++ b/C3X.h @@ -322,7 +322,7 @@ struct c3x_config { bool allow_upgrades_in_any_city; bool do_not_generate_volcanos; bool do_not_pollute_impassable_tiles; - bool do_not_place_barbarian_huts_on_impassable_tiles; + bool do_not_place_barb_huts_or_camps_on_impassable_tiles; bool show_hp_of_stealth_attack_options; bool exclude_invisible_units_from_stealth_attack; bool exclude_passengers_from_stealth_attack; @@ -482,8 +482,6 @@ struct c3x_config { int ai_auto_build_great_wall_strategy; bool enable_city_work_radii_highlights; - - bool do_not_place_barb_huts_or_camps_on_impassable_tiles; }; enum stackable_command { From d5f76b5d9a34cbce9dafd0fa53f00b365b5b2479 Mon Sep 17 00:00:00 2001 From: Instafluff Date: Wed, 6 May 2026 16:11:53 -0700 Subject: [PATCH 7/7] Ensure transport to load in port is sea unit, to avoid messing with army loading --- injected_code.c | 1 + 1 file changed, 1 insertion(+) diff --git a/injected_code.c b/injected_code.c index 62ec8bb9..8d889332 100644 --- a/injected_code.c +++ b/injected_code.c @@ -21769,6 +21769,7 @@ patch_Unit_load (Unit * this, int edx, Unit * transport) is->current_config.naval_units_use_port_districts_not_cities && (this != NULL) && (transport != NULL) && ((this->Body.X != transport->Body.X) || (this->Body.Y != transport->Body.Y)) && + (p_bic_data->UnitTypes[transport->Body.UnitTypeID].Unit_Class == UTC_Sea) && (p_bic_data->UnitTypes[this->Body.UnitTypeID].Unit_Class == UTC_Land)) { Tile * unit_tile = tile_at (this->Body.X, this->Body.Y); Tile * transport_tile = tile_at (transport->Body.X, transport->Body.Y);