* Move a rail vehicle around inside the depot. * @param flags type of operation * Note: DoCommandFlag::AutoReplace is set when autoreplace tries to undo its modifications or moves vehicles to temporary locations inside the depot. * @param src_veh source vehicle index * @param dest_veh what wagon to put the source wagon AFTER, XXX - VehicleID::Invalid() to make a new line * @param
| 1219 | * @return the cost of this operation or an error |
| 1220 | */ |
| 1221 | CommandCost CmdMoveRailVehicle(DoCommandFlags flags, VehicleID src_veh, VehicleID dest_veh, bool move_chain) |
| 1222 | { |
| 1223 | Train *src = Train::GetIfValid(src_veh); |
| 1224 | if (src == nullptr) return CMD_ERROR; |
| 1225 | |
| 1226 | CommandCost ret = CheckOwnership(src->owner); |
| 1227 | if (ret.Failed()) return ret; |
| 1228 | |
| 1229 | /* Do not allow moving crashed vehicles inside the depot, it is likely to cause asserts later */ |
| 1230 | if (src->vehstatus.Test(VehState::Crashed)) return CMD_ERROR; |
| 1231 | |
| 1232 | /* if nothing is selected as destination, try and find a matching vehicle to drag to. */ |
| 1233 | Train *dst; |
| 1234 | if (dest_veh == VehicleID::Invalid()) { |
| 1235 | dst = (src->IsEngine() || flags.Test(DoCommandFlag::AutoReplace)) ? nullptr : FindGoodVehiclePos(src); |
| 1236 | } else { |
| 1237 | dst = Train::GetIfValid(dest_veh); |
| 1238 | if (dst == nullptr) return CMD_ERROR; |
| 1239 | |
| 1240 | ret = CheckOwnership(dst->owner); |
| 1241 | if (ret.Failed()) return ret; |
| 1242 | |
| 1243 | /* Do not allow appending to crashed vehicles, too */ |
| 1244 | if (dst->vehstatus.Test(VehState::Crashed)) return CMD_ERROR; |
| 1245 | } |
| 1246 | |
| 1247 | /* if an articulated part is being handled, deal with its parent vehicle */ |
| 1248 | src = src->GetFirstEnginePart(); |
| 1249 | if (dst != nullptr) { |
| 1250 | dst = dst->GetFirstEnginePart(); |
| 1251 | } |
| 1252 | |
| 1253 | /* don't move the same vehicle.. */ |
| 1254 | if (src == dst) return CommandCost(); |
| 1255 | |
| 1256 | /* locate the head of the two chains */ |
| 1257 | Train *src_head = src->First(); |
| 1258 | Train *dst_head; |
| 1259 | if (dst != nullptr) { |
| 1260 | dst_head = dst->First(); |
| 1261 | if (dst_head->tile != src_head->tile) return CMD_ERROR; |
| 1262 | /* Now deal with articulated part of destination wagon */ |
| 1263 | dst = dst->GetLastEnginePart(); |
| 1264 | } else { |
| 1265 | dst_head = nullptr; |
| 1266 | } |
| 1267 | |
| 1268 | if (src->IsRearDualheaded()) return CommandCost(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT); |
| 1269 | |
| 1270 | /* When moving all wagons, we can't have the same src_head and dst_head */ |
| 1271 | if (move_chain && src_head == dst_head) return CommandCost(); |
| 1272 | |
| 1273 | /* When moving a multiheaded part to be place after itself, bail out. */ |
| 1274 | if (!move_chain && dst != nullptr && dst->IsRearDualheaded() && src == dst->other_multiheaded_part) return CommandCost(); |
| 1275 | |
| 1276 | /* Check if all vehicles in the source train are stopped inside a depot. */ |
| 1277 | if (!src_head->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT); |
| 1278 |
nothing calls this directly
no test coverage detected