* Clone a vehicle. If it is a train, it will clone all the cars too * @param flags type of operation * @param tile tile of the depot where the cloned vehicle is build * @param veh_id the original vehicle's index * @param share_orders shared orders, else copied orders * @return the cost of this operation + the new vehicle ID or an error */
| 828 | * @return the cost of this operation + the new vehicle ID or an error |
| 829 | */ |
| 830 | std::tuple<CommandCost, VehicleID> CmdCloneVehicle(DoCommandFlags flags, TileIndex tile, VehicleID veh_id, bool share_orders) |
| 831 | { |
| 832 | CommandCost total_cost(EXPENSES_NEW_VEHICLES); |
| 833 | |
| 834 | Vehicle *v = Vehicle::GetIfValid(veh_id); |
| 835 | if (v == nullptr || !v->IsPrimaryVehicle()) return { CMD_ERROR, VehicleID::Invalid() }; |
| 836 | Vehicle *v_front = v; |
| 837 | Vehicle *w = nullptr; |
| 838 | Vehicle *w_front = nullptr; |
| 839 | Vehicle *w_rear = nullptr; |
| 840 | |
| 841 | /* |
| 842 | * v_front is the front engine in the original vehicle |
| 843 | * v is the car/vehicle of the original vehicle that is currently being copied |
| 844 | * w_front is the front engine of the cloned vehicle |
| 845 | * w is the car/vehicle currently being cloned |
| 846 | * w_rear is the rear end of the cloned train. It's used to add more cars and is only used by trains |
| 847 | */ |
| 848 | |
| 849 | CommandCost ret = CheckOwnership(v->owner); |
| 850 | if (ret.Failed()) return { ret, VehicleID::Invalid() }; |
| 851 | |
| 852 | /* Crashed trains can only be cloned before cleanup begins. */ |
| 853 | if (v->type == VEH_TRAIN && (!v->IsFrontEngine() || Train::From(v)->crash_anim_pos >= 4400)) return { CommandCost(STR_ERROR_VEHICLE_IS_DESTROYED), VehicleID::Invalid() }; |
| 854 | |
| 855 | /* check that we can allocate enough vehicles */ |
| 856 | if (!flags.Test(DoCommandFlag::Execute)) { |
| 857 | int veh_counter = 0; |
| 858 | do { |
| 859 | veh_counter++; |
| 860 | } while ((v = v->Next()) != nullptr); |
| 861 | |
| 862 | if (!Vehicle::CanAllocateItem(veh_counter)) { |
| 863 | return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), VehicleID::Invalid() }; |
| 864 | } |
| 865 | } |
| 866 | |
| 867 | v = v_front; |
| 868 | |
| 869 | VehicleID new_veh_id = VehicleID::Invalid(); |
| 870 | do { |
| 871 | if (v->type == VEH_TRAIN && Train::From(v)->IsRearDualheaded()) { |
| 872 | /* we build the rear ends of multiheaded trains with the front ones */ |
| 873 | continue; |
| 874 | } |
| 875 | |
| 876 | /* In case we're building a multi headed vehicle and the maximum number of |
| 877 | * vehicles is almost reached (e.g. max trains - 1) not all vehicles would |
| 878 | * be cloned. When the non-primary engines were build they were seen as |
| 879 | * 'new' vehicles whereas they would immediately be joined with a primary |
| 880 | * engine. This caused the vehicle to be not build as 'the limit' had been |
| 881 | * reached, resulting in partially build vehicles and such. */ |
| 882 | DoCommandFlags build_flags = flags; |
| 883 | if (flags.Test(DoCommandFlag::Execute) && !v->IsPrimaryVehicle()) build_flags.Set(DoCommandFlag::AutoReplace); |
| 884 | |
| 885 | CommandCost cost; |
| 886 | std::tie(cost, new_veh_id, std::ignore, std::ignore, std::ignore) = Command<CMD_BUILD_VEHICLE>::Do(build_flags, tile, v->engine_type, false, INVALID_CARGO, INVALID_CLIENT_ID); |
| 887 |
nothing calls this directly
no test coverage detected