Fix #10032: Capacities of articulated vehicles in build window
See also: #9954
This commit is contained in:

committed by
rubidium42

parent
07fba75238
commit
10e76b2788
@@ -84,25 +84,25 @@ const StringID _send_to_depot_msg_table[] = {
|
||||
* @param client_id User
|
||||
* @return the cost of this operation + the new vehicle ID + the refitted capacity + the refitted mail capacity (aircraft) or an error
|
||||
*/
|
||||
std::tuple<CommandCost, VehicleID, uint, uint16> CmdBuildVehicle(DoCommandFlag flags, TileIndex tile, EngineID eid, bool use_free_vehicles, CargoID cargo, ClientID client_id)
|
||||
std::tuple<CommandCost, VehicleID, uint, uint16, CargoArray> CmdBuildVehicle(DoCommandFlag flags, TileIndex tile, EngineID eid, bool use_free_vehicles, CargoID cargo, ClientID client_id)
|
||||
{
|
||||
/* Elementary check for valid location. */
|
||||
if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return { CMD_ERROR, INVALID_VEHICLE, 0, 0 };
|
||||
if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return { CMD_ERROR, INVALID_VEHICLE, 0, 0, {} };
|
||||
|
||||
VehicleType type = GetDepotVehicleType(tile);
|
||||
|
||||
/* Validate the engine type. */
|
||||
if (!IsEngineBuildable(eid, type, _current_company)) return { CommandCost(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + type), INVALID_VEHICLE, 0, 0 };
|
||||
if (!IsEngineBuildable(eid, type, _current_company)) return { CommandCost(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + type), INVALID_VEHICLE, 0, 0, {} };
|
||||
|
||||
/* Validate the cargo type. */
|
||||
if (cargo >= NUM_CARGO && cargo != CT_INVALID) return { CMD_ERROR, INVALID_VEHICLE, 0, 0 };
|
||||
if (cargo >= NUM_CARGO && cargo != CT_INVALID) return { CMD_ERROR, INVALID_VEHICLE, 0, 0, {} };
|
||||
|
||||
const Engine *e = Engine::Get(eid);
|
||||
CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());
|
||||
|
||||
/* Engines without valid cargo should not be available */
|
||||
CargoID default_cargo = e->GetDefaultCargoType();
|
||||
if (default_cargo == CT_INVALID) return { CMD_ERROR, INVALID_VEHICLE, 0, 0 };
|
||||
if (default_cargo == CT_INVALID) return { CMD_ERROR, INVALID_VEHICLE, 0, 0, {} };
|
||||
|
||||
bool refitting = cargo != CT_INVALID && cargo != default_cargo;
|
||||
|
||||
@@ -115,13 +115,13 @@ std::tuple<CommandCost, VehicleID, uint, uint16> CmdBuildVehicle(DoCommandFlag f
|
||||
case VEH_AIRCRAFT: num_vehicles = e->u.air.subtype & AIR_CTOL ? 2 : 3; break;
|
||||
default: NOT_REACHED(); // Safe due to IsDepotTile()
|
||||
}
|
||||
if (!Vehicle::CanAllocateItem(num_vehicles)) return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), INVALID_VEHICLE, 0, 0 };
|
||||
if (!Vehicle::CanAllocateItem(num_vehicles)) return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), INVALID_VEHICLE, 0, 0, {} };
|
||||
|
||||
/* Check whether we can allocate a unit number. Autoreplace does not allocate
|
||||
* an unit number as it will (always) reuse the one of the replaced vehicle
|
||||
* and (train) wagons don't have an unit number in any scenario. */
|
||||
UnitID unit_num = (flags & DC_QUERY_COST || flags & DC_AUTOREPLACE || (type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON)) ? 0 : GetFreeUnitNumber(type);
|
||||
if (unit_num == UINT16_MAX) return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), INVALID_VEHICLE, 0, 0 };
|
||||
if (unit_num == UINT16_MAX) return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), INVALID_VEHICLE, 0, 0, {} };
|
||||
|
||||
/* If we are refitting we need to temporarily purchase the vehicle to be able to
|
||||
* test it. */
|
||||
@@ -145,6 +145,7 @@ std::tuple<CommandCost, VehicleID, uint, uint16> CmdBuildVehicle(DoCommandFlag f
|
||||
VehicleID veh_id = INVALID_VEHICLE;
|
||||
uint refitted_capacity = 0;
|
||||
uint16 refitted_mail_capacity = 0;
|
||||
CargoArray cargo_capacities;
|
||||
if (value.Succeeded()) {
|
||||
if (subflags & DC_EXEC) {
|
||||
v->unitnumber = unit_num;
|
||||
@@ -155,11 +156,20 @@ std::tuple<CommandCost, VehicleID, uint, uint16> CmdBuildVehicle(DoCommandFlag f
|
||||
if (refitting) {
|
||||
/* Refit only one vehicle. If we purchased an engine, it may have gained free wagons. */
|
||||
CommandCost cc;
|
||||
std::tie(cc, refitted_capacity, refitted_mail_capacity) = CmdRefitVehicle(flags, v->index, cargo, 0, false, false, 1);
|
||||
std::tie(cc, refitted_capacity, refitted_mail_capacity, cargo_capacities) = CmdRefitVehicle(flags, v->index, cargo, 0, false, false, 1);
|
||||
value.AddCost(cc);
|
||||
} else {
|
||||
/* Fill in non-refitted capacities */
|
||||
refitted_capacity = e->GetDisplayDefaultCapacity(&refitted_mail_capacity);
|
||||
if (e->type == VEH_TRAIN || e->type == VEH_ROAD) {
|
||||
cargo_capacities = GetCapacityOfArticulatedParts(eid);
|
||||
refitted_capacity = cargo_capacities[default_cargo];
|
||||
refitted_mail_capacity = 0;
|
||||
} else {
|
||||
refitted_capacity = e->GetDisplayDefaultCapacity(&refitted_mail_capacity);
|
||||
cargo_capacities.Clear();
|
||||
cargo_capacities[default_cargo] = refitted_capacity;
|
||||
cargo_capacities[CT_MAIL] = refitted_mail_capacity;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
@@ -191,7 +201,7 @@ std::tuple<CommandCost, VehicleID, uint, uint16> CmdBuildVehicle(DoCommandFlag f
|
||||
/* Only restore if we actually did some refitting */
|
||||
if (flags != subflags) RestoreRandomSeeds(saved_seeds);
|
||||
|
||||
return { value, veh_id, refitted_capacity, refitted_mail_capacity };
|
||||
return { value, veh_id, refitted_capacity, refitted_mail_capacity, cargo_capacities };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -339,12 +349,13 @@ struct RefitResult {
|
||||
* @param auto_refit Refitting is done as automatic refitting outside a depot.
|
||||
* @return Refit cost + refittet capacity + mail capacity (aircraft).
|
||||
*/
|
||||
static std::tuple<CommandCost, uint, uint16> RefitVehicle(Vehicle *v, bool only_this, uint8 num_vehicles, CargoID new_cid, byte new_subtype, DoCommandFlag flags, bool auto_refit)
|
||||
static std::tuple<CommandCost, uint, uint16, CargoArray> RefitVehicle(Vehicle *v, bool only_this, uint8 num_vehicles, CargoID new_cid, byte new_subtype, DoCommandFlag flags, bool auto_refit)
|
||||
{
|
||||
CommandCost cost(v->GetExpenseType(false));
|
||||
uint total_capacity = 0;
|
||||
uint total_mail_capacity = 0;
|
||||
num_vehicles = num_vehicles == 0 ? UINT8_MAX : num_vehicles;
|
||||
CargoArray cargo_capacities;
|
||||
|
||||
VehicleSet vehicles_to_refit;
|
||||
if (!only_this) {
|
||||
@@ -369,7 +380,11 @@ static std::tuple<CommandCost, uint, uint16> RefitVehicle(Vehicle *v, bool only_
|
||||
/* If the vehicle is not refittable, or does not allow automatic refitting,
|
||||
* count its capacity nevertheless if the cargo matches */
|
||||
bool refittable = HasBit(e->info.refit_mask, new_cid) && (!auto_refit || HasBit(e->info.misc_flags, EF_AUTO_REFIT));
|
||||
if (!refittable && v->cargo_type != new_cid) continue;
|
||||
if (!refittable && v->cargo_type != new_cid) {
|
||||
uint amount = e->DetermineCapacity(v, nullptr);
|
||||
if (amount > 0) cargo_capacities[v->cargo_type] += amount;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Determine best fitting subtype if requested */
|
||||
if (actual_subtype == 0xFF) {
|
||||
@@ -390,6 +405,9 @@ static std::tuple<CommandCost, uint, uint16> RefitVehicle(Vehicle *v, bool only_
|
||||
/* mail_capacity will always be zero if the vehicle is not an aircraft. */
|
||||
total_mail_capacity += mail_capacity;
|
||||
|
||||
cargo_capacities[new_cid] += amount;
|
||||
cargo_capacities[CT_MAIL] += mail_capacity;
|
||||
|
||||
if (!refittable) continue;
|
||||
|
||||
/* Restore the original cargo type */
|
||||
@@ -445,7 +463,7 @@ static std::tuple<CommandCost, uint, uint16> RefitVehicle(Vehicle *v, bool only_
|
||||
}
|
||||
|
||||
refit_result.clear();
|
||||
return { cost, total_capacity, total_mail_capacity };
|
||||
return { cost, total_capacity, total_mail_capacity, cargo_capacities };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -460,42 +478,42 @@ static std::tuple<CommandCost, uint, uint16> RefitVehicle(Vehicle *v, bool only_
|
||||
* Only used if "refit only this vehicle" is false.
|
||||
* @return the cost of this operation or an error
|
||||
*/
|
||||
std::tuple<CommandCost, uint, uint16> CmdRefitVehicle(DoCommandFlag flags, VehicleID veh_id, CargoID new_cid, byte new_subtype, bool auto_refit, bool only_this, uint8 num_vehicles)
|
||||
std::tuple<CommandCost, uint, uint16, CargoArray> CmdRefitVehicle(DoCommandFlag flags, VehicleID veh_id, CargoID new_cid, byte new_subtype, bool auto_refit, bool only_this, uint8 num_vehicles)
|
||||
{
|
||||
Vehicle *v = Vehicle::GetIfValid(veh_id);
|
||||
if (v == nullptr) return { CMD_ERROR, 0, 0 };
|
||||
if (v == nullptr) return { CMD_ERROR, 0, 0, {} };
|
||||
|
||||
/* Don't allow disasters and sparks and such to be refitted.
|
||||
* We cannot check for IsPrimaryVehicle as autoreplace also refits in free wagon chains. */
|
||||
if (!IsCompanyBuildableVehicleType(v->type)) return { CMD_ERROR, 0, 0 };
|
||||
if (!IsCompanyBuildableVehicleType(v->type)) return { CMD_ERROR, 0, 0, {} };
|
||||
|
||||
Vehicle *front = v->First();
|
||||
|
||||
CommandCost ret = CheckOwnership(front->owner);
|
||||
if (ret.Failed()) return { ret, 0, 0 };
|
||||
if (ret.Failed()) return { ret, 0, 0, {} };
|
||||
|
||||
bool free_wagon = v->type == VEH_TRAIN && Train::From(front)->IsFreeWagon(); // used by autoreplace/renew
|
||||
|
||||
/* Don't allow shadows and such to be refitted. */
|
||||
if (v != front && (v->type == VEH_SHIP || v->type == VEH_AIRCRAFT)) return { CMD_ERROR, 0, 0 };
|
||||
if (v != front && (v->type == VEH_SHIP || v->type == VEH_AIRCRAFT)) return { CMD_ERROR, 0, 0, {} };
|
||||
|
||||
/* Allow auto-refitting only during loading and normal refitting only in a depot. */
|
||||
if ((flags & DC_QUERY_COST) == 0 && // used by the refit GUI, including the order refit GUI.
|
||||
!free_wagon && // used by autoreplace/renew
|
||||
(!auto_refit || !front->current_order.IsType(OT_LOADING)) && // refit inside stations
|
||||
!front->IsStoppedInDepot()) { // refit inside depots
|
||||
return { CommandCost(STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT + front->type), 0, 0};
|
||||
return { CommandCost(STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT + front->type), 0, 0, {} };
|
||||
}
|
||||
|
||||
if (front->vehstatus & VS_CRASHED) return { CommandCost(STR_ERROR_VEHICLE_IS_DESTROYED), 0, 0};
|
||||
if (front->vehstatus & VS_CRASHED) return { CommandCost(STR_ERROR_VEHICLE_IS_DESTROYED), 0, 0, {} };
|
||||
|
||||
/* Check cargo */
|
||||
if (new_cid >= NUM_CARGO) return { CMD_ERROR, 0, 0 };
|
||||
if (new_cid >= NUM_CARGO) return { CMD_ERROR, 0, 0, {} };
|
||||
|
||||
/* For ships and aircraft there is always only one. */
|
||||
only_this |= front->type == VEH_SHIP || front->type == VEH_AIRCRAFT;
|
||||
|
||||
auto [cost, refit_capacity, mail_capacity] = RefitVehicle(v, only_this, num_vehicles, new_cid, new_subtype, flags, auto_refit);
|
||||
auto [cost, refit_capacity, mail_capacity, cargo_capacities] = RefitVehicle(v, only_this, num_vehicles, new_cid, new_subtype, flags, auto_refit);
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
/* Update the cached variables */
|
||||
@@ -532,7 +550,7 @@ std::tuple<CommandCost, uint, uint16> CmdRefitVehicle(DoCommandFlag flags, Vehic
|
||||
v->InvalidateNewGRFCacheOfChain();
|
||||
}
|
||||
|
||||
return { cost, refit_capacity, mail_capacity };
|
||||
return { cost, refit_capacity, mail_capacity, cargo_capacities };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -851,7 +869,7 @@ std::tuple<CommandCost, VehicleID> CmdCloneVehicle(DoCommandFlag flags, TileInde
|
||||
if ((flags & DC_EXEC) && !v->IsPrimaryVehicle()) build_flags |= DC_AUTOREPLACE;
|
||||
|
||||
CommandCost cost;
|
||||
std::tie(cost, new_veh_id, std::ignore, std::ignore) = Command<CMD_BUILD_VEHICLE>::Do(build_flags, tile, v->engine_type, false, CT_INVALID, INVALID_CLIENT_ID);
|
||||
std::tie(cost, new_veh_id, std::ignore, std::ignore, std::ignore) = Command<CMD_BUILD_VEHICLE>::Do(build_flags, tile, v->engine_type, false, CT_INVALID, INVALID_CLIENT_ID);
|
||||
|
||||
if (cost.Failed()) {
|
||||
/* Can't build a part, then sell the stuff we already made; clear up the mess */
|
||||
|
Reference in New Issue
Block a user