diff --git a/src/command.cpp b/src/command.cpp index 21ef93d0c5..b89b996e73 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -215,6 +215,7 @@ CommandProc CmdIssueTemplateReplacement; CommandProc CmdDeleteTemplateReplacement; CommandProc CmdCloneVehicle; +CommandProc CmdCloneVehicleFromTemplate; CommandProc CmdStartStopVehicle; CommandProc CmdMassStartStopVehicle; CommandProc CmdAutoreplaceVehicle; @@ -450,6 +451,7 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdDeleteTemplateReplacement, CMD_ALL_TILES, CMDT_VEHICLE_MANAGEMENT ), // CMD_DELETE_TEMPLATE_REPLACEMENT DEF_CMD(CmdCloneVehicle, CMD_NO_TEST, CMDT_VEHICLE_CONSTRUCTION ), // CMD_CLONE_VEHICLE; NewGRF callbacks influence building and refitting making it impossible to correctly estimate the cost + DEF_CMD(CmdCloneVehicleFromTemplate, CMD_NO_TEST, CMDT_VEHICLE_CONSTRUCTION ), // CMD_CLONE_VEHICLE_FROM_TEMPLATE; NewGRF callbacks influence building and refitting making it impossible to correctly estimate the cost DEF_CMD(CmdStartStopVehicle, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_START_STOP_VEHICLE DEF_CMD(CmdMassStartStopVehicle, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_MASS_START_STOP DEF_CMD(CmdAutoreplaceVehicle, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_AUTOREPLACE_VEHICLE diff --git a/src/command_type.h b/src/command_type.h index 840b4645c1..b7b29cde0a 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -396,6 +396,7 @@ enum Commands { CMD_DELETE_TEMPLATE_REPLACEMENT, ///< delete a template replacement from a vehicle group CMD_CLONE_VEHICLE, ///< clone a vehicle + CMD_CLONE_VEHICLE_FROM_TEMPLATE, ///< clone a vehicle from a template CMD_START_STOP_VEHICLE, ///< start or stop a vehicle CMD_MASS_START_STOP, ///< start/stop all vehicles (in a depot) CMD_AUTOREPLACE_VEHICLE, ///< replace/renew a vehicle while it is in a depot diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index f5ca6f1afd..5be78f465b 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -28,6 +28,7 @@ #include "infrastructure_func.h" #include "order_backup.h" #include "zoom_func.h" +#include "tbtr_template_vehicle.h" #include "widgets/depot_widget.h" @@ -917,6 +918,21 @@ struct DepotWindow : Window { return true; } + /** + * Clones a vehicle + * @param v the original vehicle to clone + * @return Always true. + */ + bool OnTemplateVehicleSelect(const TemplateVehicle *v) override + { + /* Copy-clone, open viewport for new vehicle, and deselect the tool (assume player wants to changs things on new vehicle) */ + if (DoCommandP(this->window_number, v->index, 0, CMD_CLONE_VEHICLE_FROM_TEMPLATE | CMD_MSG(STR_ERROR_CAN_T_BUY_TRAIN), CcCloneVehicle)) { + ResetObjectToPlace(); + } + + return true; + } + void OnPlaceObjectAbort() override { /* abort clone */ diff --git a/src/tbtr_template_gui_main.cpp b/src/tbtr_template_gui_main.cpp index 306b0bb14f..67482f1b2e 100644 --- a/src/tbtr_template_gui_main.cpp +++ b/src/tbtr_template_gui_main.cpp @@ -447,6 +447,8 @@ public: if (newindex == this->selected_template_index || newindex >= templates.size()) { this->selected_template_index = -1; } else if (newindex < templates.size()) { + const TemplateVehicle *tmp = this->templates[newindex]; + if (tmp != nullptr && TemplateVehicleClicked(tmp)) return; this->selected_template_index = newindex; } this->UpdateButtonState(); @@ -840,3 +842,19 @@ void ShowTemplateReplaceWindow() new TemplateReplaceWindow(&_replace_rail_vehicle_desc); } } + +/** + * Dispatch a "template vehicle selected" event if any window waits for it. + * @param v selected vehicle; + * @return did any window accept vehicle selection? + */ +bool TemplateVehicleClicked(const TemplateVehicle *v) +{ + assert(v != nullptr); + if (!(_thd.place_mode & HT_VEHICLE)) return false; + + v = v->First(); + if (!v->IsPrimaryVehicle()) return false; + + return _thd.GetCallbackWnd()->OnTemplateVehicleSelect(v); +} diff --git a/src/tbtr_template_gui_main.h b/src/tbtr_template_gui_main.h index f16cea9e24..5e5ac36d4c 100644 --- a/src/tbtr_template_gui_main.h +++ b/src/tbtr_template_gui_main.h @@ -23,4 +23,6 @@ typedef GUIList GUIGroupList; void ShowTemplateReplaceWindow(); +bool TemplateVehicleClicked(const TemplateVehicle *v); + #endif diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp index f20a6c5bd9..ebd94a566c 100644 --- a/src/vehicle_cmd.cpp +++ b/src/vehicle_cmd.cpp @@ -33,6 +33,7 @@ #include "core/random_func.hpp" #include "tbtr_template_vehicle.h" #include "tbtr_template_vehicle_func.h" +#include "scope.h" #include #include #include @@ -1537,6 +1538,47 @@ CommandCost CmdCloneVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint return total_cost; } +/** + * Clone a vehicle from a template. + * @param tile tile of the depot where the cloned vehicle is build + * @param flags type of operation + * @param p1 the original template vehicle's index + * @param p2 unused + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdCloneVehicleFromTemplate(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + TemplateVehicle* tv = TemplateVehicle::GetIfValid(p1); + + if (tv == nullptr) { + return CMD_ERROR; + } + + CommandCost ret = CheckOwnership(tv->owner); + if (ret.Failed()) return ret; + + /* Vehicle construction needs random bits, so we have to save the random + * seeds to prevent desyncs. */ + SavedRandomSeeds saved_seeds; + SaveRandomSeeds(&saved_seeds); + + auto guard = scope_guard([&]() { + if (!(flags & DC_EXEC)) RestoreRandomSeeds(saved_seeds); + }); + + ret = DoCommand(0, tv->index, 0, DC_EXEC, CMD_VIRTUAL_TRAIN_FROM_TEMPLATE_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_BUY_TRAIN)); + if (ret.Failed()) return ret; + + Train* virt = Train::From(Vehicle::Get(_new_vehicle_id)); + + ret = DoCommand(tile, _new_vehicle_id, 0, flags, CMD_CLONE_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_BUY_TRAIN)); + + delete virt; + + return ret; +} + /** * Send all vehicles of type to depots * @param flags the flags used for DoCommand() diff --git a/src/window_gui.h b/src/window_gui.h index a34bf0ff56..39ca800432 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -823,6 +823,13 @@ public: */ virtual bool OnVehicleSelect(const struct Vehicle *v) { return false; } + /** + * The user clicked on a template vehicle while HT_VEHICLE has been set. + * @param v clicked vehicle. It is guaranteed to be v->IsPrimaryVehicle() == true + * @return True if the click is handled, false if it is ignored. + */ + virtual bool OnTemplateVehicleSelect(const struct TemplateVehicle *v) { return false; } + /** * The user cancelled a tile highlight mode that has been set. */