* Build a vehicle. * @param flags for command * @param tile tile of depot where the vehicle is built * @param eid vehicle type being built. * @param use_free_vehicles use free vehicles when building the vehicle. * @param cargo refit cargo type. * @param client_id User * @return the cost of this operation + the new vehicle ID + the refitted capacity + the refitted mail capacity (aircraft) or
| 98 | * @return the cost of this operation + the new vehicle ID + the refitted capacity + the refitted mail capacity (aircraft) or an error |
| 99 | */ |
| 100 | std::tuple<CommandCost, VehicleID, uint, uint16_t, CargoArray> CmdBuildVehicle(DoCommandFlags flags, TileIndex tile, EngineID eid, bool use_free_vehicles, CargoType cargo, ClientID client_id) |
| 101 | { |
| 102 | /* Elementary check for valid location. */ |
| 103 | if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return { CMD_ERROR, VehicleID::Invalid(), 0, 0, {} }; |
| 104 | |
| 105 | VehicleType type = GetDepotVehicleType(tile); |
| 106 | |
| 107 | /* Validate the engine type. */ |
| 108 | if (!IsEngineBuildable(eid, type, _current_company)) return { CommandCost(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + type), VehicleID::Invalid(), 0, 0, {} }; |
| 109 | |
| 110 | /* Validate the cargo type. */ |
| 111 | if (cargo >= NUM_CARGO && IsValidCargoType(cargo)) return { CMD_ERROR, VehicleID::Invalid(), 0, 0, {} }; |
| 112 | |
| 113 | const Engine *e = Engine::Get(eid); |
| 114 | CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost()); |
| 115 | |
| 116 | /* Engines without valid cargo should not be available */ |
| 117 | CargoType default_cargo = e->GetDefaultCargoType(); |
| 118 | if (!IsValidCargoType(default_cargo)) return { CMD_ERROR, VehicleID::Invalid(), 0, 0, {} }; |
| 119 | |
| 120 | bool refitting = IsValidCargoType(cargo) && cargo != default_cargo; |
| 121 | |
| 122 | /* Check whether the number of vehicles we need to build can be built according to pool space. */ |
| 123 | uint num_vehicles; |
| 124 | switch (type) { |
| 125 | case VEH_TRAIN: num_vehicles = (e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_MULTIHEAD ? 2 : 1) + CountArticulatedParts(eid, false); break; |
| 126 | case VEH_ROAD: num_vehicles = 1 + CountArticulatedParts(eid, false); break; |
| 127 | case VEH_SHIP: num_vehicles = 1; break; |
| 128 | case VEH_AIRCRAFT: num_vehicles = e->VehInfo<AircraftVehicleInfo>().subtype & AIR_CTOL ? 2 : 3; break; |
| 129 | default: NOT_REACHED(); // Safe due to IsDepotTile() |
| 130 | } |
| 131 | if (!Vehicle::CanAllocateItem(num_vehicles)) return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), VehicleID::Invalid(), 0, 0, {} }; |
| 132 | |
| 133 | /* Check whether we can allocate a unit number. Autoreplace does not allocate |
| 134 | * an unit number as it will (always) reuse the one of the replaced vehicle |
| 135 | * and (train) wagons don't have an unit number in any scenario. */ |
| 136 | UnitID unit_num = (flags.Test(DoCommandFlag::QueryCost) || flags.Test(DoCommandFlag::AutoReplace) || (type == VEH_TRAIN && e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON)) ? 0 : GetFreeUnitNumber(type); |
| 137 | if (unit_num == UINT16_MAX) return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), VehicleID::Invalid(), 0, 0, {} }; |
| 138 | |
| 139 | /* If we are refitting we need to temporarily purchase the vehicle to be able to |
| 140 | * test it. */ |
| 141 | DoCommandFlags subflags = flags; |
| 142 | if (refitting && !flags.Test(DoCommandFlag::Execute)) subflags.Set({DoCommandFlag::Execute, DoCommandFlag::AutoReplace}); |
| 143 | |
| 144 | /* Vehicle construction needs random bits, so we have to save the random |
| 145 | * seeds to prevent desyncs. */ |
| 146 | SavedRandomSeeds saved_seeds; |
| 147 | SaveRandomSeeds(&saved_seeds); |
| 148 | |
| 149 | Vehicle *v = nullptr; |
| 150 | switch (type) { |
| 151 | case VEH_TRAIN: value.AddCost(CmdBuildRailVehicle(subflags, tile, e, &v)); break; |
| 152 | case VEH_ROAD: value.AddCost(CmdBuildRoadVehicle(subflags, tile, e, &v)); break; |
| 153 | case VEH_SHIP: value.AddCost(CmdBuildShip (subflags, tile, e, &v)); break; |
| 154 | case VEH_AIRCRAFT: value.AddCost(CmdBuildAircraft (subflags, tile, e, &v)); break; |
| 155 | default: NOT_REACHED(); // Safe due to IsDepotTile() |
| 156 | } |
| 157 |
nothing calls this directly
no test coverage detected