diff --git a/C3X.h b/C3X.h index 5294675d..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; @@ -982,7 +982,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 c1918076..8d889332 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); @@ -4691,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) { @@ -4703,6 +4716,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) { @@ -20731,10 +20762,14 @@ 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)) + (dest->vtable->m50_Get_Square_BaseType (dest) == SQ_Coast) && + tile_allows_worker_coast_entry_source (source)) { base_validity = AMV_OK; + } + } // Allow land units to enter bridge tiles @@ -20874,8 +20909,10 @@ 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)) + (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 if (is->current_config.enable_bridge_districts && @@ -20919,6 +20956,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 && @@ -21497,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]; @@ -21711,6 +21764,41 @@ 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[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); + 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 @@ -22395,11 +22483,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; @@ -27620,26 +27703,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 { - 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; - } - } - } + (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) { @@ -27685,6 +27751,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; @@ -33703,12 +33770,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); @@ -35495,24 +35563,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; } } @@ -35534,6 +35589,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) { @@ -35546,7 +35634,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 && @@ -35721,6 +35810,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); @@ -35771,6 +35866,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)) { @@ -35796,6 +35897,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)) {