diff --git a/src/command.cpp b/src/command.cpp index 959610cd28..d43b3e5115 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -106,6 +106,8 @@ CommandProc CmdDecreaseLoan; CommandProc CmdWantEnginePreview; +CommandProc CmdSetVehicleUnitNumber; + CommandProc CmdRenameVehicle; CommandProc CmdRenameEngine; @@ -175,10 +177,27 @@ CommandProc CmdRemoveSignalTrack; CommandProc CmdSetAutoReplace; +CommandProc CmdToggleReuseDepotVehicles; +CommandProc CmdToggleKeepRemainingVehicles; +CommandProc CmdToggleRefitAsTemplate; + +CommandProc CmdVirtualTrainFromTemplateVehicle; +CommandProc CmdVirtualTrainFromTrain; +CommandProc CmdDeleteVirtualTrain; +CommandProc CmdBuildVirtualRailVehicle; +CommandProc CmdReplaceTemplateVehicle; + +CommandProc CmdTemplateVehicleFromTrain; +CommandProc CmdDeleteTemplateVehicle; + +CommandProc CmdIssueTemplateReplacement; +CommandProc CmdDeleteTemplateReplacement; + CommandProc CmdCloneVehicle; CommandProc CmdStartStopVehicle; CommandProc CmdMassStartStopVehicle; CommandProc CmdAutoreplaceVehicle; +CommandProc CmdTemplateReplaceVehicle; CommandProc CmdDepotSellAllVehicles; CommandProc CmdDepotMassAutoReplace; @@ -267,6 +286,8 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdWantEnginePreview, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_WANT_ENGINE_PREVIEW + DEF_CMD(CmdSetVehicleUnitNumber, 0, CMDT_OTHER_MANAGEMENT ), // CMD_SET_VEHICLE_UNIT_NUMBER + DEF_CMD(CmdRenameVehicle, 0, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_VEHICLE DEF_CMD(CmdRenameEngine, CMD_SERVER, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_ENGINE @@ -334,10 +355,28 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdChangeSetting, CMD_SERVER, CMDT_SERVER_SETTING ), // CMD_CHANGE_SETTING DEF_CMD(CmdChangeCompanySetting, 0, CMDT_COMPANY_SETTING ), // CMD_CHANGE_COMPANY_SETTING DEF_CMD(CmdSetAutoReplace, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_SET_AUTOREPLACE + + DEF_CMD(CmdToggleReuseDepotVehicles, CMD_ALL_TILES, CMDT_VEHICLE_MANAGEMENT ), // CMD_TOGGLE_REUSE_DEPOT_VEHICLES + DEF_CMD(CmdToggleKeepRemainingVehicles, CMD_ALL_TILES, CMDT_VEHICLE_MANAGEMENT ), // CMD_TOGGLE_KEEP_REMAINING_VEHICLES + DEF_CMD(CmdToggleRefitAsTemplate, CMD_ALL_TILES, CMDT_VEHICLE_MANAGEMENT ), // CMD_TOGGLE_REFIT_AS_TEMPLATE + + DEF_CMD(CmdVirtualTrainFromTemplateVehicle, CMD_ALL_TILES, CMDT_VEHICLE_MANAGEMENT ), // CMD_VIRTUAL_TRAIN_FROM_TEMPLATE_VEHICLE + DEF_CMD(CmdVirtualTrainFromTrain, CMD_ALL_TILES, CMDT_VEHICLE_MANAGEMENT ), // CMD_VIRTUAL_TRAIN_FROM_TRAIN + DEF_CMD(CmdDeleteVirtualTrain, CMD_ALL_TILES, CMDT_VEHICLE_MANAGEMENT ), // CMD_DELETE_VIRTUAL_TRAIN + DEF_CMD(CmdBuildVirtualRailVehicle, CMD_ALL_TILES, CMDT_VEHICLE_MANAGEMENT ), // CMD_BUILD_VIRTUAL_RAIL_VEHICLE + DEF_CMD(CmdReplaceTemplateVehicle, CMD_ALL_TILES, CMDT_VEHICLE_MANAGEMENT ), // CMD_REPLACE_TEMPLATE_VEHICLE + + DEF_CMD(CmdTemplateVehicleFromTrain, CMD_ALL_TILES, CMDT_VEHICLE_MANAGEMENT ), // CMD_CLONE_TEMPLATE_VEHICLE_FROM_TRAIN + DEF_CMD(CmdDeleteTemplateVehicle, CMD_ALL_TILES, CMDT_VEHICLE_MANAGEMENT ), // CMD_DELETE_TEMPLATE_VEHICLE + + DEF_CMD(CmdIssueTemplateReplacement, CMD_ALL_TILES, CMDT_VEHICLE_MANAGEMENT ), // CMD_ISSUE_TEMPLATE_REPLACEMENT + 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(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 + DEF_CMD(CmdTemplateReplaceVehicle, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_TEMPLATE_REPLACE_VEHICLE DEF_CMD(CmdDepotSellAllVehicles, 0, CMDT_VEHICLE_CONSTRUCTION ), // CMD_DEPOT_SELL_ALL_VEHICLES DEF_CMD(CmdDepotMassAutoReplace, 0, CMDT_VEHICLE_CONSTRUCTION ), // CMD_DEPOT_MASS_AUTOREPLACE DEF_CMD(CmdCreateGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_CREATE_GROUP diff --git a/src/command_func.h b/src/command_func.h index c4cc51e3da..d719aba570 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -125,4 +125,12 @@ CommandCallback CcFoundRandomTown; CommandCallback CcBuildPrimaryVehicle; CommandCallback CcStartStopVehicle; +/* tbtr_template_gui_create.cpp */ +CommandCallback CcSetVirtualTrain; +CommandCallback CcVirtualTrainWaggonsMoved; +CommandCallback CcDeleteVirtualTrain; + +/* tbtr_template_gui_create_virtualtrain.cpp */ +CommandCallback CcAddVirtualEngine; + #endif /* COMMAND_FUNC_H */ diff --git a/src/command_type.h b/src/command_type.h index f318216acc..3976620f4a 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -240,6 +240,8 @@ enum Commands { CMD_WANT_ENGINE_PREVIEW, ///< confirm the preview of an engine + CMD_SET_VEHICLE_UNIT_NUMBER, ///< sets the unit number of a vehicle + CMD_RENAME_VEHICLE, ///< rename a whole vehicle CMD_RENAME_ENGINE, ///< rename a engine (in the engine list) CMD_RENAME_COMPANY, ///< change the company name @@ -306,10 +308,27 @@ enum Commands { CMD_SET_AUTOREPLACE, ///< set an autoreplace entry + CMD_TOGGLE_REUSE_DEPOT_VEHICLES, ///< toggle 'reuse depot vehicles' on template + CMD_TOGGLE_KEEP_REMAINING_VEHICLES, ///< toggle 'keep remaining vehicles' on template + CMD_TOGGLE_REFIT_AS_TEMPLATE, ///< toggle 'refit as template' on template + + CMD_VIRTUAL_TRAIN_FROM_TEMPLATE_VEHICLE, ///< Creates a virtual train from a template + CMD_VIRTUAL_TRAIN_FROM_TRAIN, ///< Creates a virtual train from a regular train + CMD_DELETE_VIRTUAL_TRAIN, ///< Delete a virtual train + CMD_BUILD_VIRTUAL_RAIL_VEHICLE, ///< Build a virtual train + CMD_REPLACE_TEMPLATE_VEHICLE, ///< Replace a template vehicle with another one based on a virtual train + + CMD_CLONE_TEMPLATE_VEHICLE_FROM_TRAIN, ///< clone a train and create a new template vehicle based on it + CMD_DELETE_TEMPLATE_VEHICLE, ///< delete a template vehicle + + CMD_ISSUE_TEMPLATE_REPLACEMENT, ///< issue a template replacement for a vehicle group + CMD_DELETE_TEMPLATE_REPLACEMENT, ///< delete a template replacement from a vehicle group + CMD_CLONE_VEHICLE, ///< clone a vehicle 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 + CMD_TEMPLATE_REPLACE_VEHICLE, ///< template replace a vehicle while it is in a depot CMD_DEPOT_SELL_ALL_VEHICLES, ///< sell all vehicles which are in a given depot CMD_DEPOT_MASS_AUTOREPLACE, ///< force the autoreplace to take action in a given depot diff --git a/src/ground_vehicle.hpp b/src/ground_vehicle.hpp index 56b97875fc..b39600796e 100644 --- a/src/ground_vehicle.hpp +++ b/src/ground_vehicle.hpp @@ -295,6 +295,16 @@ struct GroundVehicle : public SpecializedVehicle { */ inline void ClearFreeWagon() { ClrBit(this->subtype, GVSF_FREE_WAGON); } + /** + * Set a vehicle as a virtual vehicle. + */ + inline void SetVirtual() { SetBit(this->subtype, GVSF_VIRTUAL); } + + /** + * Clear a vehicle from being a virtual vehicle. + */ + inline void ClearVirtual() { ClrBit(this->subtype, GVSF_VIRTUAL); } + /** * Set a vehicle as a multiheaded engine. */ @@ -329,6 +339,12 @@ struct GroundVehicle : public SpecializedVehicle { */ inline bool IsMultiheaded() const { return HasBit(this->subtype, GVSF_MULTIHEADED); } + /** + * Tell if we are dealing with a virtual vehicle (used for templates). + * @return True if the vehicle is a virtual vehicle. + */ + inline bool IsVirtual() const { return HasBit(this->subtype, GVSF_VIRTUAL); } + /** * Tell if we are dealing with the rear end of a multiheaded engine. * @return True if the engine is the rear part of a dualheaded engine. diff --git a/src/network/network_command.cpp b/src/network/network_command.cpp index 6e5458fd86..6862191473 100644 --- a/src/network/network_command.cpp +++ b/src/network/network_command.cpp @@ -51,6 +51,10 @@ static CommandCallback * const _callback_table[] = { /* 0x19 */ CcStartStopVehicle, /* 0x1A */ CcGame, /* 0x1B */ CcAddVehicleNewGroup, + /* 0x1C */ CcSetVirtualTrain, + /* 0x1D */ CcVirtualTrainWaggonsMoved, + /* 0x1E */ CcDeleteVirtualTrain, + /* 0x1F */ CcAddVirtualEngine, }; /** diff --git a/src/tbtr_template_gui_create.cpp b/src/tbtr_template_gui_create.cpp index c2dc436cab..c7a475d1ac 100644 --- a/src/tbtr_template_gui_create.cpp +++ b/src/tbtr_template_gui_create.cpp @@ -43,9 +43,10 @@ uint16 TRAIN_FRONT_SPACE = 16; enum TemplateReplaceWindowWidgets { TCW_CAPTION, - TCW_MATRIX_NEW_TMPL, + TCW_NEW_TMPL_PANEL, TCW_INFO_PANEL, - TCW_SCROLLBAR_NEW_TMPL, + TCW_SCROLLBAR_H_NEW_TMPL, + TCW_SCROLLBAR_V_NEW_TMPL, TCW_SELL_TMPL, TCW_NEW, TCW_OK, @@ -59,23 +60,25 @@ static const NWidgetPart _widgets[] = { NWidget(WWT_CLOSEBOX, COLOUR_GREY), NWidget(WWT_CAPTION, COLOUR_GREY, TCW_CAPTION), SetDataTip(STR_TMPL_CREATEGUI_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(NWID_VERTICAL), - NWidget(WWT_MATRIX, COLOUR_GREY, TCW_MATRIX_NEW_TMPL), SetMinimalSize(216, 60), SetFill(1, 0), SetDataTip(0x1, STR_REPLACE_HELP_LEFT_ARRAY), SetResize(1, 0), SetScrollbar(TCW_SCROLLBAR_NEW_TMPL), - NWidget(WWT_PANEL, COLOUR_GREY, TCW_INFO_PANEL), SetMinimalSize(216,80), SetResize(1,1), EndContainer(), - NWidget(NWID_HSCROLLBAR, COLOUR_GREY, TCW_SCROLLBAR_NEW_TMPL), SetResize(1,0), + NWidget(WWT_PANEL, COLOUR_GREY, TCW_NEW_TMPL_PANEL), SetMinimalSize(250, 30), SetResize(1, 0), SetScrollbar(TCW_SCROLLBAR_H_NEW_TMPL), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, TCW_INFO_PANEL), SetMinimalSize(250, 100), SetResize(1, 1), SetScrollbar(TCW_SCROLLBAR_V_NEW_TMPL), EndContainer(), + NWidget(NWID_HSCROLLBAR, COLOUR_GREY, TCW_SCROLLBAR_H_NEW_TMPL), EndContainer(), - NWidget(WWT_IMGBTN, COLOUR_GREY, TCW_SELL_TMPL), SetDataTip(0x0, STR_NULL), SetMinimalSize(23,23), SetResize(0, 1), SetFill(0, 1), + NWidget(WWT_IMGBTN, COLOUR_GREY, TCW_SELL_TMPL), SetMinimalSize(40, 40), SetDataTip(0x0, STR_NULL), SetResize(0, 1), SetFill(0, 1), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TCW_SCROLLBAR_V_NEW_TMPL), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TCW_OK), SetMinimalSize(52, 12), SetResize(1,0), SetDataTip(STR_TMPL_CONFIRM, STR_TMPL_CONFIRM), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TCW_NEW), SetMinimalSize(52, 12), SetResize(1,0), SetDataTip(STR_TMPL_NEW, STR_TMPL_NEW), - NWidget(WWT_TEXTBTN, COLOUR_GREY, TCW_CLONE), SetMinimalSize(52, 12), SetResize(1,0), SetDataTip(STR_TMPL_CREATE_CLONE_VEH, STR_TMPL_CREATE_CLONE_VEH), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TCW_REFIT), SetMinimalSize(52, 12), SetResize(1,0), SetDataTip(STR_TMPL_REFIT, STR_TMPL_REFIT), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TCW_CANCEL), SetMinimalSize(52, 12), SetResize(1,0), SetDataTip(STR_TMPL_CANCEL, STR_TMPL_CANCEL), - NWidget(WWT_RESIZEBOX, COLOUR_GREY), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TCW_OK), SetMinimalSize(52, 12), SetResize(1, 0), SetDataTip(STR_TMPL_CONFIRM, STR_TMPL_CONFIRM), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TCW_NEW), SetMinimalSize(52, 12), SetResize(1, 0), SetDataTip(STR_TMPL_NEW, STR_TMPL_NEW), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TCW_CLONE), SetMinimalSize(52, 12), SetResize(1, 0), SetDataTip(STR_TMPL_CREATE_CLONE_VEH, STR_TMPL_CREATE_CLONE_VEH), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TCW_REFIT), SetMinimalSize(52, 12), SetResize(1, 0), SetDataTip(STR_TMPL_REFIT, STR_TMPL_REFIT), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TCW_CANCEL), SetMinimalSize(52, 12), SetResize(1, 0), SetDataTip(STR_TMPL_CANCEL, STR_TMPL_CANCEL), + NWidget(WWT_RESIZEBOX, COLOUR_GREY), EndContainer(), }; @@ -84,7 +87,7 @@ static WindowDesc _template_create_window_desc( "template create window", // const char* ini_key 456, 100, // window size WC_CREATE_TEMPLATE, // window class - WC_TEMPLATEGUI_MAIN, // parent window class + WC_NONE, // parent window class WDF_CONSTRUCTION, // window flags _widgets, lengthof(_widgets) // widgets + num widgets ); @@ -104,12 +107,13 @@ static void TrainDepotMoveVehicle(const Vehicle *wagon, VehicleID sel, const Veh if (wagon == v) return; - CmdMoveRailVehicle(INVALID_TILE, DC_EXEC, (_ctrl_pressed ? 1:0)<<20 | (1<<21) | v->index, wagon == NULL ? INVALID_VEHICLE : wagon->index, 0); + DoCommandP(v->tile, v->index | (_ctrl_pressed ? 1 : 0) << 20 | 1 << 21, wagon == NULL ? INVALID_VEHICLE : wagon->index, CMD_MOVE_RAIL_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_MOVE_VEHICLE), CcVirtualTrainWaggonsMoved); } class TemplateCreateWindow : public Window { private: Scrollbar *hscroll; + Scrollbar *vscroll; int line_height; Train* virtual_train; bool editMode; @@ -124,8 +128,9 @@ public: TemplateCreateWindow(WindowDesc* _wdesc, TemplateVehicle *to_edit, bool *notice, bool *windowOpen, int step_h) : Window(_wdesc) { this->line_height = step_h; - this->CreateNestedTree(_wdesc); - this->hscroll = this->GetScrollbar(TCW_SCROLLBAR_NEW_TMPL); + this->CreateNestedTree(_wdesc != NULL); + this->hscroll = this->GetScrollbar(TCW_SCROLLBAR_H_NEW_TMPL); + this->vscroll = this->GetScrollbar(TCW_SCROLLBAR_V_NEW_TMPL); this->FinishInitNested(VEH_TRAIN); /* a sprite */ this->GetWidget(TCW_SELL_TMPL)->widget_data = SPR_SELL_TRAIN; @@ -137,20 +142,25 @@ public: virtualTrainChangedNotice = false; this->editTemplate = to_edit; - if ( to_edit ) editMode = true; + if (to_edit) editMode = true; else editMode = false; this->sel = INVALID_VEHICLE; this->vehicle_over = INVALID_VEHICLE; - this->virtual_train = VirtualTrainFromTemplateVehicle(to_edit); + if (to_edit) { + DoCommandP(0, to_edit->index, 0, CMD_VIRTUAL_TRAIN_FROM_TEMPLATE_VEHICLE, CcSetVirtualTrain); + } this->resize.step_height = 1; } + ~TemplateCreateWindow() { - if ( virtual_train ) - delete virtual_train; + if (virtual_train != nullptr) { + DoCommandP(0, virtual_train->index, 0, CMD_DELETE_VIRTUAL_TRAIN); + virtual_train = nullptr; + } SetWindowClassesDirty(WC_TRAINS_LIST); @@ -160,19 +170,22 @@ public: } - virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) + void SetVirtualTrain(Train* const train) { - switch (widget) { - case TCW_MATRIX_NEW_TMPL: - size->height = 20; - break; + if (virtual_train != nullptr) { + DoCommandP(0, virtual_train->index, 0, CMD_DELETE_VIRTUAL_TRAIN); } + + virtual_train = train; } + virtual void OnResize() { - NWidgetCore *nwi = this->GetWidget(TCW_MATRIX_NEW_TMPL); - this->hscroll->SetCapacity(nwi->current_x); - nwi->widget_data = (this->hscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START); + NWidgetCore *template_panel = this->GetWidget(TCW_NEW_TMPL_PANEL); + this->hscroll->SetCapacity(template_panel->current_x); + + NWidgetCore *info_panel = this->GetWidget(TCW_INFO_PANEL); + this->vscroll->SetCapacity(info_panel->current_y); } @@ -180,12 +193,13 @@ public: { virtualTrainChangedNotice = true; } + virtual void OnClick(Point pt, int widget, int click_count) { switch(widget) { - case TCW_MATRIX_NEW_TMPL: { - NWidgetBase *nwi = this->GetWidget(TCW_MATRIX_NEW_TMPL); - ClickedOnVehiclePanel(pt.x - nwi->pos_x-TRAIN_FRONT_SPACE, pt.y - nwi->pos_y); + case TCW_NEW_TMPL_PANEL: { + NWidgetBase *nwi = this->GetWidget(TCW_NEW_TMPL_PANEL); + ClickedOnVehiclePanel(pt.x - nwi->pos_x, pt.y - nwi->pos_y); break; } case TCW_NEW: { @@ -204,10 +218,14 @@ public: break; } case TCW_OK: { - TemplateVehicle *tv = NULL; - if ( editMode ) tv = DeleteTemplateVehicle(editTemplate); - editTemplate = TemplateVehicleFromVirtualTrain(virtual_train); - if ( tv ) *noticeParent = true; + uint32 templateIndex = (editTemplate != nullptr) ? editTemplate->index : INVALID_VEHICLE; + + if (virtual_train != nullptr) { + DoCommandP(0, templateIndex, virtual_train->index, CMD_REPLACE_TEMPLATE_VEHICLE); + virtual_train = nullptr; + } else if (templateIndex != INVALID_VEHICLE) { + DoCommandP(0, templateIndex, 0, CMD_DELETE_TEMPLATE_VEHICLE); + } delete this; break; } @@ -221,25 +239,30 @@ public: } } } + virtual bool OnVehicleSelect(const Vehicle *v) { // throw away the current virtual train - if ( virtual_train ) - delete this->virtual_train; + if (virtual_train != nullptr) { + DoCommandP(0, virtual_train->index, 0, CMD_DELETE_VIRTUAL_TRAIN); + virtual_train = nullptr; + } + // create a new one - this->virtual_train = CloneVirtualTrainFromTrain((const Train*)v); + DoCommandP(0, v->index, 0, CMD_VIRTUAL_TRAIN_FROM_TRAIN, CcSetVirtualTrain); this->ToggleWidgetLoweredState(TCW_CLONE); ResetObjectToPlace(); this->SetDirty(); return true; } + virtual void DrawWidget(const Rect &r, int widget) const { switch(widget) { - case TCW_MATRIX_NEW_TMPL: { + case TCW_NEW_TMPL_PANEL: { if ( this->virtual_train ) { - DrawTrainImage(virtual_train, r.left+TRAIN_FRONT_SPACE, r.right, r.top+2, this->sel, EIT_PURCHASE, this->hscroll->GetPosition(), this->vehicle_over); + DrawTrainImage(virtual_train, r.left+TRAIN_FRONT_SPACE, r.right-25, r.top+2, this->sel, EIT_PURCHASE, this->hscroll->GetPosition(), this->vehicle_over); SetDParam(0, CeilDiv(virtual_train->gcache.cached_total_length * 10, TILE_SIZE)); SetDParam(1, 1); DrawString(r.left, r.right, r.top, STR_TINY_BLACK_DECIMAL, TC_BLACK, SA_RIGHT); @@ -248,27 +271,36 @@ public: } case TCW_INFO_PANEL: { if ( this->virtual_train ) { + DrawPixelInfo tmp_dpi, *old_dpi; + + if (!FillDrawPixelInfo(&tmp_dpi, r.left, r.top, r.right - r.left, r.bottom - r.top)) break; + + old_dpi = _cur_dpi; + _cur_dpi = &tmp_dpi; + /* Draw vehicle performance info */ const GroundVehicleCache *gcache = this->virtual_train->GetGroundVehicleCache(); SetDParam(2, this->virtual_train->GetDisplayMaxSpeed()); SetDParam(1, gcache->cached_power); SetDParam(0, gcache->cached_weight); SetDParam(3, gcache->cached_max_te / 1000); - DrawString(r.left+8, r.right, r.top+4, STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE); + DrawString(8, r.right, 4 - this->vscroll->GetPosition(), STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE); /* Draw cargo summary */ CargoArray cargo_caps; for ( const Train *tmp=this->virtual_train; tmp; tmp=tmp->Next() ) cargo_caps[tmp->cargo_type] += tmp->cargo_cap; - int y = r.top+24; + int y = 30 - this->vscroll->GetPosition(); for (CargoID i = 0; i < NUM_CARGO; ++i) { if ( cargo_caps[i] > 0 ) { SetDParam(0, i); SetDParam(1, cargo_caps[i]); SetDParam(2, _settings_game.vehicle.freight_trains); - DrawString(r.left+8, r.right, y, STR_TMPL_CARGO_SUMMARY, TC_WHITE, SA_LEFT); - y += this->line_height/2; + DrawString(8, r.right, y, STR_TMPL_CARGO_SUMMARY, TC_LIGHT_BLUE, SA_LEFT); + y += this->line_height/3; } } + + _cur_dpi = old_dpi; } break; } @@ -278,7 +310,7 @@ public: } virtual void OnTick() { - if ( virtualTrainChangedNotice ) { + if (virtualTrainChangedNotice) { this->SetDirty(); virtualTrainChangedNotice = false; } @@ -286,22 +318,20 @@ public: virtual void OnDragDrop(Point pt, int widget) { switch (widget) { - case TCW_MATRIX_NEW_TMPL: { + case TCW_NEW_TMPL_PANEL: { const Vehicle *v = NULL; - VehicleID sel; - if ( virtual_train ) sel = virtual_train->index; - else sel = INVALID_VEHICLE; + VehicleID sel = this->sel; + this->sel = INVALID_VEHICLE; this->SetDirty(); - NWidgetBase *nwi = this->GetWidget(TCW_MATRIX_NEW_TMPL); + NWidgetBase *nwi = this->GetWidget(TCW_NEW_TMPL_PANEL); GetDepotVehiclePtData gdvp = { NULL, NULL }; if (this->GetVehicleFromDepotWndPt(pt.x - nwi->pos_x, pt.y - nwi->pos_y, &v, &gdvp) == MODE_DRAG_VEHICLE && sel != INVALID_VEHICLE) { if (gdvp.wagon == NULL || gdvp.wagon->index != sel) { this->vehicle_over = INVALID_VEHICLE; TrainDepotMoveVehicle(gdvp.wagon, sel, gdvp.head); - virtual_train = virtual_train->First(); } } break; @@ -310,7 +340,14 @@ public: if (this->IsWidgetDisabled(widget)) return; if (this->sel == INVALID_VEHICLE) return; - virtual_train = DeleteVirtualTrain(virtual_train, Train::Get(this->sel)); + int sell_cmd = (_ctrl_pressed) ? 1 : 0; + + Train* train_to_delete = Train::Get(this->sel); + + if (virtual_train == train_to_delete) + virtual_train = (_ctrl_pressed) ? nullptr : virtual_train->GetNextUnit(); + + DoCommandP(0, this->sel | sell_cmd << 20 | 1 << 21, 0, GetCmdSellVeh(VEH_TRAIN)); this->sel = INVALID_VEHICLE; @@ -325,14 +362,15 @@ public: this->sel = INVALID_VEHICLE; this->SetDirty(); } + virtual void OnMouseDrag(Point pt, int widget) { if (this->sel == INVALID_VEHICLE) return; /* A rail vehicle is dragged.. */ - if (widget != TCW_MATRIX_NEW_TMPL) { // ..outside of the depot matrix. + if (widget != TCW_NEW_TMPL_PANEL) { // ..outside of the depot matrix. if (this->vehicle_over != INVALID_VEHICLE) { this->vehicle_over = INVALID_VEHICLE; - this->SetWidgetDirty(TCW_MATRIX_NEW_TMPL); + this->SetWidgetDirty(TCW_NEW_TMPL_PANEL); } return; } @@ -361,16 +399,33 @@ public: this->vehicle_over = new_vehicle_over; this->SetWidgetDirty(widget); } + virtual void OnPaint() { - uint max_width = 32; + uint min_width = 32; + uint min_height = 30; uint width = 0; - if ( virtual_train ) - for (Train *v = virtual_train; v != NULL; v = v->Next()) - width += v->GetDisplayImageWidth(); + uint height = 30; + CargoArray cargo_caps; - max_width = max(max_width, width); - this->hscroll->SetCount(max_width+25); + if (virtual_train != nullptr) { + for (Train *train = virtual_train; train != nullptr; train = train->Next()) { + width += train->GetDisplayImageWidth(); + cargo_caps[train->cargo_type] += train->cargo_cap; + } + + for (CargoID i = 0; i < NUM_CARGO; ++i) { + if ( cargo_caps[i] > 0 ) { + height += this->line_height/3; + } + } + } + + min_width = max(min_width, width); + this->hscroll->SetCount(min_width + 50); + + min_height = max(min_height, height); + this->vscroll->SetCount(min_height); this->DrawWidgets(); } @@ -388,11 +443,14 @@ public: uint count_width; uint header_width; - DepotGUIAction GetVehicleFromDepotWndPt(int x, int y, const Vehicle **veh, GetDepotVehiclePtData *d) const + + DepotGUIAction GetVehicleFromDepotWndPt(int x, int y, const Vehicle **veh, GetDepotVehiclePtData *d) const { - const NWidgetCore *matrix_widget = this->GetWidget(TCW_MATRIX_NEW_TMPL); + const NWidgetCore *matrix_widget = this->GetWidget(TCW_NEW_TMPL_PANEL); /* In case of RTL the widgets are swapped as a whole */ if (_current_text_dir == TD_RTL) x = matrix_widget->current_x - x; + + x -= TRAIN_FRONT_SPACE; uint xm = x; @@ -436,6 +494,7 @@ public: if (sel != INVALID_VEHICLE) { this->sel = INVALID_VEHICLE; + TrainDepotMoveVehicle(v, sel, gdvp.head); } else if (v != NULL) { int image = v->GetImage(_current_text_dir == TD_RTL ? DIR_E : DIR_W, EIT_PURCHASE); SetObjectToPlaceWnd(image, GetVehiclePalette(v), HT_DRAG, this); @@ -448,6 +507,10 @@ public: } } + void RearrangeVirtualTrain() + { + virtual_train = virtual_train->First(); + } }; void ShowTemplateCreateWindow(TemplateVehicle *to_edit, bool *noticeParent, bool *createWindowOpen, int step_h) @@ -456,4 +519,31 @@ void ShowTemplateCreateWindow(TemplateVehicle *to_edit, bool *noticeParent, bool new TemplateCreateWindow(&_template_create_window_desc, to_edit, noticeParent, createWindowOpen, step_h); } +void CcSetVirtualTrain(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2) +{ + if (result.Failed()) return; + Window* window = FindWindowById(WC_CREATE_TEMPLATE, 0); + if (window) { + Train* train = Train::From(Vehicle::Get(_new_vehicle_id)); + ((TemplateCreateWindow*)window)->SetVirtualTrain(train); + window->InvalidateData(); + } +} + +void CcVirtualTrainWaggonsMoved(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2) +{ + if (result.Failed()) return; + + Window* window = FindWindowById(WC_CREATE_TEMPLATE, 0); + if (window) { + ((TemplateCreateWindow*)window)->RearrangeVirtualTrain(); + window->InvalidateData(); + } +} + +void CcDeleteVirtualTrain(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2) +{ + VehicleID virtual_train_id = p2; + DoCommandP(0, virtual_train_id, 0, CMD_DELETE_VIRTUAL_TRAIN); +} \ No newline at end of file diff --git a/src/tbtr_template_gui_create_virtualtrain.cpp b/src/tbtr_template_gui_create_virtualtrain.cpp index 23351671dd..63612ddb6e 100644 --- a/src/tbtr_template_gui_create_virtualtrain.cpp +++ b/src/tbtr_template_gui_create_virtualtrain.cpp @@ -650,8 +650,7 @@ struct BuildVirtualTrainWindow : Window { case WID_BV_BUILD: { EngineID sel_eng = this->sel_engine; if (sel_eng != INVALID_ENGINE) { - Train *tmp = CmdBuildVirtualRailVehicle(sel_eng); - if (tmp) AddVirtualEngine(tmp); + DoCommandP(0, sel_engine, 0, CMD_BUILD_VIRTUAL_RAIL_VEHICLE, CcAddVirtualEngine); } break; } @@ -667,12 +666,7 @@ struct BuildVirtualTrainWindow : Window { { if (!gui_scope) return; /* When switching to original acceleration model for road vehicles, clear the selected sort criteria if it is not available now. */ - if (this->vehicle_type == VEH_ROAD && - _settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL && - this->sort_criteria > 7) { - this->sort_criteria = 0; - _last_sort_criteria[VEH_ROAD] = 0; - } + this->eng_list.ForceRebuild(); } @@ -796,18 +790,29 @@ struct BuildVirtualTrainWindow : Window { void AddVirtualEngine(Train *toadd) { - if ( !*virtual_train ) { + if (*virtual_train == NULL) { *virtual_train = toadd; - } - else { + } else { VehicleID target = (*(this->virtual_train))->GetLastUnit()->index; - CommandCost movec; - movec = CmdMoveRailVehicle(INVALID_TILE, DC_EXEC, (1<<21) | toadd->index, target, 0); + + DoCommandP(0, (1<<21) | toadd->index, target, CMD_MOVE_RAIL_VEHICLE); } *noticeParent = true; } }; +void CcAddVirtualEngine(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2) +{ + if (result.Failed()) return; + + Window* window = FindWindowById(WC_BUILD_VIRTUAL_TRAIN, 0); + if (window) { + Train* train = Train::From(Vehicle::Get(_new_vehicle_id)); + ((BuildVirtualTrainWindow*)window)->AddVirtualEngine(train); + window->InvalidateData(); + } +} + static WindowDesc _build_vehicle_desc( WDP_AUTO, // window position "template create virtual train",// const char* ini_key diff --git a/src/tbtr_template_gui_main.cpp b/src/tbtr_template_gui_main.cpp index 5bd39c517a..82b6c18029 100644 --- a/src/tbtr_template_gui_main.cpp +++ b/src/tbtr_template_gui_main.cpp @@ -24,6 +24,7 @@ #include "settings_func.h" #include "core/geometry_func.hpp" #include "rail_gui.h" +#include "network/network.h" #include "table/sprites.h" #include "table/strings.h" @@ -60,6 +61,7 @@ enum TemplateReplaceWindowWidgets { TRW_WIDGET_INSET_TEMPLATES, TRW_WIDGET_BOTTOM_MATRIX, + TRW_WIDGET_MIDDLE_SCROLLBAR, TRW_WIDGET_BOTTOM_SCROLLBAR, TRW_WIDGET_TMPL_INFO_INSET, @@ -76,7 +78,7 @@ enum TemplateReplaceWindowWidgets { TRW_WIDGET_TMPL_BUTTONS_EDIT, TRW_WIDGET_TMPL_BUTTONS_CLONE, TRW_WIDGET_TMPL_BUTTONS_DELETE, - TRW_WIDGET_TMPL_BUTTONS_RPLALL, + //TRW_WIDGET_TMPL_BUTTONS_RPLALL, TRW_WIDGET_TMPL_BUTTON_FLUFF, TRW_WIDGET_TMPL_BUTTONS_EDIT_RIGHTPANEL, @@ -84,7 +86,7 @@ enum TemplateReplaceWindowWidgets { TRW_WIDGET_TITLE_INFO_TEMPLATE, TRW_WIDGET_INFO_GROUP, - TRW_WIDGET_INFO_TEMPLATE, + TRW_WIDGET_INFO_TEMPLATE, TRW_WIDGET_TMPL_BUTTONS_SPACER, @@ -103,11 +105,12 @@ static const NWidgetPart _widgets[] = { NWidget(WWT_CLOSEBOX, COLOUR_GREY), NWidget(WWT_CAPTION, COLOUR_GREY, TRW_CAPTION), SetDataTip(STR_TMPL_RPL_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), //Top Matrix NWidget(NWID_VERTICAL), - NWidget(WWT_INSET, COLOUR_GREY, TRW_WIDGET_INSET_GROUPS), SetMinimalSize(216,12), SetDataTip(STR_TMPL_MAINGUI_DEFINEDGROUPS, STR_TMPL_MAINGUI_DEFINEDGROUPS), SetResize(1, 0), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_INSET_GROUPS), SetMinimalTextLines(1, WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM), SetResize(1, 0), EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(WWT_MATRIX, COLOUR_GREY, TRW_WIDGET_TOP_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetDataTip(0x1, STR_REPLACE_HELP_LEFT_ARRAY), SetResize(1, 0), SetScrollbar(TRW_WIDGET_TOP_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TRW_WIDGET_TOP_SCROLLBAR), @@ -115,16 +118,19 @@ static const NWidgetPart _widgets[] = { EndContainer(), // Template Display NWidget(NWID_VERTICAL), - NWidget(WWT_INSET, COLOUR_GREY, TRW_WIDGET_INSET_TEMPLATES), SetMinimalSize(216,12), SetDataTip(STR_TMPL_AVAILABLE_TEMPLATES, STR_TMPL_AVAILABLE_TEMPLATES), SetResize(1, 0), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_INSET_TEMPLATES), SetMinimalTextLines(1, WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM), SetResize(1, 0), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_MATRIX, COLOUR_GREY, TRW_WIDGET_BOTTOM_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetDataTip(0x1, STR_REPLACE_HELP_RIGHT_ARRAY), SetResize(1, 1), SetScrollbar(TRW_WIDGET_BOTTOM_SCROLLBAR), - NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TRW_WIDGET_BOTTOM_SCROLLBAR), + NWidget(WWT_MATRIX, COLOUR_GREY, TRW_WIDGET_BOTTOM_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetDataTip(0x1, STR_REPLACE_HELP_RIGHT_ARRAY), SetResize(1, 1), SetScrollbar(TRW_WIDGET_MIDDLE_SCROLLBAR), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TRW_WIDGET_MIDDLE_SCROLLBAR), EndContainer(), EndContainer(), // Info Area NWidget(NWID_VERTICAL), - NWidget(WWT_INSET, COLOUR_GREY, TRW_WIDGET_TMPL_INFO_INSET), SetMinimalSize(216,12), SetResize(1,0), SetDataTip(STR_TMPL_AVAILABLE_TEMPLATES, STR_TMPL_AVAILABLE_TEMPLATES), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TMPL_INFO_PANEL), SetMinimalSize(216,50), SetResize(1,0), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TMPL_INFO_INSET), SetMinimalTextLines(1, WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM), SetResize(1,0), EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TMPL_INFO_PANEL), SetMinimalSize(216,120), SetResize(1,0), SetScrollbar(TRW_WIDGET_BOTTOM_SCROLLBAR), EndContainer(), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TRW_WIDGET_BOTTOM_SCROLLBAR), + EndContainer(), EndContainer(), // Control Area NWidget(NWID_VERTICAL), @@ -139,11 +145,10 @@ static const NWidgetPart _widgets[] = { EndContainer(), // Edit buttons NWidget(NWID_HORIZONTAL), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_DEFINE), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_DEFINE_TEMPLATE, STR_REPLACE_ENGINE_WAGON_SELECT_HELP), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_EDIT), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_EDIT_TEMPLATE, STR_REPLACE_ENGINE_WAGON_SELECT_HELP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CLONE), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_CREATE_CLONE_VEH, STR_REPLACE_ENGINE_WAGON_SELECT_HELP), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_DELETE), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_DELETE_TEMPLATE, STR_REPLACE_ENGINE_WAGON_SELECT_HELP), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_RPLALL), SetMinimalSize(150,12), SetResize(0,0), SetDataTip(STR_TMPL_RPL_ALL_TMPL, STR_REPLACE_ENGINE_WAGON_SELECT_HELP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_DEFINE), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_DEFINE_TEMPLATE, STR_TMPL_DEFINE_TEMPLATE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_EDIT), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_EDIT_TEMPLATE, STR_TMPL_EDIT_TEMPLATE), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CLONE), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_CREATE_CLONE_VEH, STR_TMPL_CREATE_CLONE_VEH), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_DELETE), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_DELETE_TEMPLATE, STR_TMPL_DELETE_TEMPLATE), NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_EDIT_RIGHTPANEL), SetMinimalSize(50,12), SetResize(1,0), EndContainer(), EndContainer(), EndContainer(), @@ -174,12 +179,14 @@ private: GUIGroupList groups; ///< List of groups byte unitnumber_digits; + SmallVector indents; ///< Indentation levels + short line_height; short matrixContentLeftMargin; int details_height; ///< Minimal needed height of the details panels (found so far). RailType sel_railtype; ///< Type of rail tracks selected. - Scrollbar *vscroll[2]; + Scrollbar *vscroll[3]; // listing/sorting continued GUITemplateList templates; GUITemplateList::SortFunction **template_sorter_funcs; @@ -204,20 +211,17 @@ public: this->line_height = step_h; - this->CreateNestedTree(wdesc); + this->CreateNestedTree(wdesc != NULL); this->vscroll[0] = this->GetScrollbar(TRW_WIDGET_TOP_SCROLLBAR); - this->vscroll[1] = this->GetScrollbar(TRW_WIDGET_BOTTOM_SCROLLBAR); - this->vscroll[0]->SetStepSize(step_h / 2); - this->vscroll[1]->SetStepSize(step_h); + this->vscroll[1] = this->GetScrollbar(TRW_WIDGET_MIDDLE_SCROLLBAR); + this->vscroll[2] = this->GetScrollbar(TRW_WIDGET_BOTTOM_SCROLLBAR); this->FinishInitNested(VEH_TRAIN); this->owner = _local_company; this->groups.ForceRebuild(); this->groups.NeedResort(); - this->BuildGroupList(_local_company); - this->groups.Sort(&GroupNameSorter); - + this->BuildGroupList(_local_company); this->matrixContentLeftMargin = 40; this->selected_template_index = -1; @@ -286,6 +290,18 @@ public: DrawTemplateInfo(this->line_height, r); break; } + case TRW_WIDGET_INSET_GROUPS: { + DrawString(r.left + 2, r.right - 2, r.top + 2, STR_TMPL_MAINGUI_DEFINEDGROUPS); + break; + } + case TRW_WIDGET_INSET_TEMPLATES: { + DrawString(r.left + 2, r.right - 2, r.top + 2, STR_TMPL_AVAILABLE_TEMPLATES); + break; + } + case TRW_WIDGET_TMPL_INFO_INSET: { + DrawString(r.left + 2, r.right - 2, r.top + 2, STR_TMPL_TEMPLATE_INFO); + break; + } } } @@ -294,7 +310,6 @@ public: BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype); this->BuildGroupList(_local_company); - this->groups.Sort(&GroupNameSorter); if ( templateNotice ) { BuildTemplateGuiList(&this->templates, vscroll[1], _local_company, this->sel_railtype); @@ -308,6 +323,34 @@ public: /* Show the selected railtype in the pulldown menu */ this->GetWidget(TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN)->widget_data = GetRailTypeInfo(sel_railtype)->strings.replace_text; + if ((this->selected_template_index < 0) || (this->selected_template_index >= (short)this->templates.Length())) { + this->vscroll[2]->SetCount(24); + } else { + const TemplateVehicle *tmp = this->templates[this->selected_template_index]; + uint min_height = 30; + uint height = 30; + CargoArray cargo_caps; + short count_columns = 0; + short max_columns = 2; + + for ( ; tmp; tmp=tmp->Next()) { + cargo_caps[tmp->cargo_type] += tmp->cargo_cap; + } + + for (CargoID i = 0; i < NUM_CARGO; ++i) { + if ( cargo_caps[i] > 0 ) { + if (count_columns % max_columns == 0) { + height += this->line_height/3; + } + + ++count_columns; + } + } + + min_height = max(min_height, height); + this->vscroll[2]->SetCount(min_height); + } + this->DrawWidgets(); } @@ -317,31 +360,35 @@ public: switch (widget) { case TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REUSE: { - if ( this->selected_template_index >= 0 ) { - TemplateVehicle *sel = TemplateVehicle::Get(((this->templates)[selected_template_index])->index); - sel->ToggleReuseDepotVehicles(); + if ((this->selected_template_index >= 0) && (this->selected_template_index < (short)this->templates.Length())) { + uint32 template_index = ((this->templates)[selected_template_index])->index; + + DoCommandP(0, template_index, 0, CMD_TOGGLE_REUSE_DEPOT_VEHICLES, NULL); } break; } case TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_KEEP: { - if ( this->selected_template_index >= 0 ) { - TemplateVehicle *sel = TemplateVehicle::Get(((this->templates)[selected_template_index])->index); - sel->ToggleKeepRemainingVehicles(); + if ((this->selected_template_index >= 0) && (this->selected_template_index < (short)this->templates.Length())) { + uint32 template_index = ((this->templates)[selected_template_index])->index; + + DoCommandP(0, template_index, 0, CMD_TOGGLE_KEEP_REMAINING_VEHICLES, NULL); } break; } case TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REFIT: { - if ( this->selected_template_index >= 0 ) { - TemplateVehicle *sel = TemplateVehicle::Get(((this->templates)[selected_template_index])->index); - sel->ToggleRefitAsTemplate(); + if ((this->selected_template_index >= 0) && (this->selected_template_index < (short)this->templates.Length())) { + uint32 template_index = ((this->templates)[selected_template_index])->index; + + DoCommandP(0, template_index, 0, CMD_TOGGLE_REFIT_AS_TEMPLATE, NULL); } break; } - case TRW_WIDGET_TMPL_BUTTONS_DEFINE: + case TRW_WIDGET_TMPL_BUTTONS_DEFINE: { ShowTemplateCreateWindow(0, &templateNotice, &editInProgress, this->line_height); break; + } case TRW_WIDGET_TMPL_BUTTONS_EDIT: { - if ( this->selected_template_index >= 0 ) { + if ((this->selected_template_index >= 0) && (this->selected_template_index < (short)this->templates.Length())) { editInProgress = true; TemplateVehicle *sel = TemplateVehicle::Get(((this->templates)[selected_template_index])->index); ShowTemplateCreateWindow(sel, &templateNotice, &editInProgress, this->line_height); @@ -361,61 +408,58 @@ public: break; } case TRW_WIDGET_TMPL_BUTTONS_DELETE: - if ( selected_template_index >= 0 && !editInProgress ) { - // identify template to delete - TemplateVehicle *del = TemplateVehicle::Get(((this->templates)[selected_template_index])->index); - // remove a corresponding template replacement if existing - TemplateReplacement *tr = GetTemplateReplacementByTemplateID(del->index); - if ( tr ) { - delete tr; + if ((this->selected_template_index >= 0) && (this->selected_template_index < (short)this->templates.Length()) && !editInProgress) { + + uint32 template_index = ((this->templates)[selected_template_index])->index; + + bool succeeded = DoCommandP(0, template_index, 0, CMD_DELETE_TEMPLATE_VEHICLE, NULL); + + if (succeeded) { + BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype); + selected_template_index = -1; } - delete del; - BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype); - selected_template_index = -1; } break; - case TRW_WIDGET_TMPL_BUTTONS_RPLALL: { - ShowTemplateReplaceAllGui(); - break; - } case TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN: // Railtype selection dropdown menu ShowDropDownList(this, GetRailTypeDropDownList(true), sel_railtype, TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN); break; case TRW_WIDGET_TOP_MATRIX: { - uint16 newindex = (uint16)((pt.y - this->nested_array[TRW_WIDGET_TOP_MATRIX]->pos_y) / (this->line_height/2) ) + this->vscroll[0]->GetPosition(); + uint16 newindex = (uint16)((pt.y - this->nested_array[TRW_WIDGET_TOP_MATRIX]->pos_y) / (this->line_height/2) ) + this->vscroll[0]->GetPosition(); if ( newindex == this->selected_group_index || newindex >= this->groups.Length() ) { this->selected_group_index = -1; } - else if ( newindex < this->groups.Length() ) { + else if ((newindex >= 0) && (newindex < this->groups.Length())) { this->selected_group_index = newindex; } break; } case TRW_WIDGET_BOTTOM_MATRIX: { - uint16 newindex = (uint16)((pt.y - this->nested_array[TRW_WIDGET_BOTTOM_MATRIX]->pos_y) / this->line_height) + this->vscroll[1]->GetPosition(); + uint16 newindex = (uint16)((pt.y - this->nested_array[TRW_WIDGET_BOTTOM_MATRIX]->pos_y) / this->line_height) + this->vscroll[1]->GetPosition(); if ( newindex == this->selected_template_index || newindex >= templates.Length() ) { this->selected_template_index = -1; } - else if ( newindex < templates.Length() ) { + else if ((newindex >= 0) && (newindex < templates.Length())) { this->selected_template_index = newindex; } break; } case TRW_WIDGET_START: { - if ( this->selected_template_index >= 0 && this->selected_group_index >= 0) { + if ((this->selected_template_index >= 0) && (this->selected_template_index < (short)this->templates.Length()) && + (this->selected_group_index >= 0) && (this->selected_group_index < (short)this->groups.Length())) { uint32 tv_index = ((this->templates)[selected_template_index])->index; int current_group_index = (this->groups)[this->selected_group_index]->index; - IssueTemplateReplacement(current_group_index, tv_index); + + DoCommandP(0, current_group_index, tv_index, CMD_ISSUE_TEMPLATE_REPLACEMENT, NULL); } break; } case TRW_WIDGET_STOP: - if ( this->selected_group_index == -1 ) + if ((this->selected_group_index < 0) || (this->selected_group_index >= (short)this->groups.Length())) return; + int current_group_index = (this->groups)[this->selected_group_index]->index; - TemplateReplacement *tr = GetTemplateReplacementByGroupID(current_group_index); - if ( tr ) - delete tr; + + DoCommandP(0, current_group_index, 0, CMD_DELETE_TEMPLATE_REPLACEMENT, NULL); break; } this->SetDirty(); @@ -423,9 +467,9 @@ public: virtual bool OnVehicleSelect(const Vehicle *v) { - // create a new template from the clicked vehicle - TemplateVehicle *tv = CloneTemplateVehicleFromTrain((const Train*)v); - if ( !tv ) return false; + bool succeeded = DoCommandP(0, v->index, 0, CMD_CLONE_TEMPLATE_VEHICLE_FROM_TRAIN, NULL); + + if (!succeeded) return false; BuildTemplateGuiList(&this->templates, vscroll[1], _local_company, this->sel_railtype); this->ToggleWidgetLoweredState(TRW_WIDGET_TMPL_BUTTONS_CLONE); @@ -457,6 +501,9 @@ public: NWidgetCore *nwi2 = this->GetWidget(TRW_WIDGET_BOTTOM_MATRIX); this->vscroll[1]->SetCapacityFromWidget(this, TRW_WIDGET_BOTTOM_MATRIX); nwi2->widget_data = (this->vscroll[1]->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START); + /* Info panel */ + NWidgetCore *nwi3 = this->GetWidget(TRW_WIDGET_TMPL_INFO_PANEL); + this->vscroll[2]->SetCapacity(nwi3->current_y); } virtual void OnTick() @@ -489,6 +536,17 @@ public: return -1; } + void AddParents(GUIGroupList *source, GroupID parent, int indent) + { + for (const Group **g = source->Begin(); g != source->End(); g++) { + if ((*g)->parent == parent) { + *this->groups.Append() = *g; + *this->indents.Append() = indent; + AddParents(source, (*g)->index, indent + 1); + } + } + } + /** Sort the groups by their name */ static int CDECL GroupNameSorter(const Group * const *a, const Group * const *b) { @@ -514,18 +572,25 @@ public: void BuildGroupList(Owner owner) { - if (!this->groups.NeedRebuild()) { - return; - } + if (!this->groups.NeedRebuild()) return; + this->groups.Clear(); + this->indents.Clear(); + + GUIGroupList list; const Group *g; FOR_ALL_GROUPS(g) { - if (g->owner == owner ) { - *this->groups.Append() = g; + if (g->owner == owner && g->vehicle_type == VEH_TRAIN) { + *list.Append() = g; } } + list.ForceResort(); + list.Sort(&GroupNameSorter); + + AddParents(&list, INVALID_GROUP, 0); + this->groups.Compact(); this->groups.RebuildDone(); this->vscroll[0]->SetCount(groups.Length()); @@ -550,7 +615,7 @@ public: SetDParam(0, g_id); StringID str = STR_GROUP_NAME; - DrawString(left+30, right, y+2, str, TC_BLACK); + DrawString(left+30+ this->indents[i] * 10, right, y+2, str, TC_BLACK); /* Draw the template in use for this group, if there is one */ short template_in_use = FindTemplateIndexForGroup(g_id); @@ -621,24 +686,24 @@ public: /* Index of current template vehicle in the list of all templates for its company */ SetDParam(0, i); - DrawString(left+5, left+25, y + line_height/2, STR_BLACK_INT, TC_BLACK, SA_RIGHT); + DrawString(left+5, left+25, y + 2, STR_BLACK_INT, TC_BLACK, SA_RIGHT); /* Draw whether the current template is in use by any group */ if ( v->NumGroupsUsingTemplate() > 0 ) { - DrawString(left+200, right, y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 2, STR_TMP_TEMPLATE_IN_USE, TC_GREEN, SA_LEFT); + DrawString(left+35, right, y + line_height - FONT_HEIGHT_SMALL * 2 - 4 - WD_FRAMERECT_BOTTOM - 2, STR_TMP_TEMPLATE_IN_USE, TC_GREEN, SA_LEFT); } /* Draw information about template configuration settings */ TextColour color; if ( v->IsSetReuseDepotVehicles() ) color = TC_LIGHT_BLUE; else color = TC_GREY; - DrawString(left+200, right, y+2, STR_TMPL_CONFIG_USEDEPOT, color, SA_LEFT); + DrawString(left+300, right, y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 2, STR_TMPL_CONFIG_USEDEPOT, color, SA_LEFT); if ( v->IsSetKeepRemainingVehicles() ) color = TC_LIGHT_BLUE; else color = TC_GREY; - DrawString(left+275, right, y+2, STR_TMPL_CONFIG_KEEPREMAINDERS, color, SA_LEFT); + DrawString(left+400, right, y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 2, STR_TMPL_CONFIG_KEEPREMAINDERS, color, SA_LEFT); if ( v->IsSetRefitAsTemplate() ) color = TC_LIGHT_BLUE; else color = TC_GREY; - DrawString(left+350, right, y+2, STR_TMPL_CONFIG_REFIT, color, SA_LEFT); + DrawString(left+500, right, y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 2, STR_TMPL_CONFIG_REFIT, color, SA_LEFT); y += line_height; } @@ -646,9 +711,17 @@ public: void DrawTemplateInfo(int line_height, const Rect &r) const { - if ( this->selected_template_index == -1 || (short)this->templates.Length() <= this->selected_template_index ) + if ((this->selected_template_index < 0) || (this->selected_template_index >= (short)this->templates.Length())) return; + DrawPixelInfo tmp_dpi, *old_dpi; + + if (!FillDrawPixelInfo(&tmp_dpi, r.left, r.top, r.right - r.left, r.bottom - r.top)) + return; + + old_dpi = _cur_dpi; + _cur_dpi = &tmp_dpi; + const TemplateVehicle *tmp = this->templates[this->selected_template_index]; /* Draw vehicle performance info */ @@ -656,32 +729,34 @@ public: SetDParam(1, tmp->power); SetDParam(0, tmp->weight); SetDParam(3, tmp->max_te); - DrawString(r.left+8, r.right, r.top+4, STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE); + DrawString(8, r.right, 4 - this->vscroll[2]->GetPosition(), STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE); /* Draw cargo summary */ - short top = r.top + 24; - short left = r.left + 8; - short count_rows = 0; - short max_rows = 2; + short top = 30 - this->vscroll[2]->GetPosition(); + short left = 8; + short count_columns = 0; + short max_columns = 2; CargoArray cargo_caps; for ( ; tmp; tmp=tmp->Next() ) cargo_caps[tmp->cargo_type] += tmp->cargo_cap; - int y = top; + int x = left; for (CargoID i = 0; i < NUM_CARGO; ++i) { if ( cargo_caps[i] > 0 ) { - count_rows++; + count_columns++; SetDParam(0, i); SetDParam(1, cargo_caps[i]); SetDParam(2, _settings_game.vehicle.freight_trains); - DrawString(left, r.right, y, FreightWagonMult(i) > 1 ? STR_TMPL_CARGO_SUMMARY_MULTI : STR_TMPL_CARGO_SUMMARY, TC_WHITE, SA_LEFT); - y += this->line_height/2; - if ( count_rows % max_rows == 0 ) { - y = top; - left += 150; + DrawString(x, r.right, top, FreightWagonMult(i) > 1 ? STR_TMPL_CARGO_SUMMARY_MULTI : STR_TMPL_CARGO_SUMMARY, TC_LIGHT_BLUE, SA_LEFT); + x += 250; + if ( count_columns % max_columns == 0 ) { + x = left; + top += this->line_height/3; } } } + + _cur_dpi = old_dpi; } }; diff --git a/src/tbtr_template_gui_replaceall.cpp b/src/tbtr_template_gui_replaceall.cpp index 51b8cea4db..36d4396582 100644 --- a/src/tbtr_template_gui_replaceall.cpp +++ b/src/tbtr_template_gui_replaceall.cpp @@ -145,8 +145,7 @@ private: public: TemplateReplacementReplaceAllWindow(WindowDesc *wdesc) : Window(wdesc) { - - this->CreateNestedTree(wdesc); + this->CreateNestedTree(wdesc != nullptr); this->vscroll_tl = this->GetScrollbar(RPLALL_GUI_SCROLL_TL); this->vscroll_tr = this->GetScrollbar(RPLALL_GUI_SCROLL_TR); @@ -193,7 +192,7 @@ public: virtual void OnPaint() { - this->GetWidget(RPLALL_GUI_PANEL_BUTTONFLUFF_3)->colour = _company_colours[_local_company]; + this->GetWidget(RPLALL_GUI_PANEL_BUTTONFLUFF_3)->colour = _company_colours[_local_company]; this->DrawWidgets(); } diff --git a/src/tbtr_template_vehicle.h b/src/tbtr_template_vehicle.h index 82aeecfcda..1773cddc2d 100644 --- a/src/tbtr_template_vehicle.h +++ b/src/tbtr_template_vehicle.h @@ -27,8 +27,6 @@ struct TemplateVehicle; struct TemplateReplacement; -CommandCost CmdBuildTemplateVehicle(uint i, DoCommandFlag flags, uint p1, uint p2, char const* text); -CommandCost CmdTemplateReplaceVehicle(uint i, DoCommandFlag flags, uint p1, uint p2, char const* text); typedef uint16 TemplateID; @@ -171,11 +169,11 @@ struct TemplateReplacement : TemplateReplacementPool::PoolItem<&_template_replac inline GroupID Group() { return this->group; } inline GroupID Template() { return this->sel_template; } - inline void SetGroup(GroupID gid) { this->group = gid; } - inline void SetTemplate(TemplateID tid) { this->sel_template = tid; } + inline void SetGroup(GroupID gid) { this->group = gid; } + inline void SetTemplate(TemplateID tid) { this->sel_template = tid; } - inline TemplateID GetTemplateVehicleID() { return sel_template; } - inline const TemplateVehicle* GetTemplateVehicle() { + inline TemplateID GetTemplateVehicleID() { return sel_template; } + inline const TemplateVehicle* GetTemplateVehicle() { const TemplateVehicle *tv; FOR_ALL_TEMPLATES(tv) { if ( tv->index == this->sel_template ) diff --git a/src/tbtr_template_vehicle_func.cpp b/src/tbtr_template_vehicle_func.cpp index 69f847a674..55cfdc01c0 100644 --- a/src/tbtr_template_vehicle_func.cpp +++ b/src/tbtr_template_vehicle_func.cpp @@ -142,52 +142,6 @@ inline void SetupTemplateVehicleFromVirtual(TemplateVehicle *tmp, TemplateVehicl tmp->image_width = virt->GetDisplayImageWidth(p); } -// create a new virtual train as clone of a real train -Train* CloneVirtualTrainFromTrain(const Train *clicked) -{ - if ( !clicked ) return 0; - CommandCost c; - Train *tmp, *head, *tail; - - head = CmdBuildVirtualRailVehicle(clicked->engine_type); - if ( !head ) return 0; - - tail = head; - clicked = clicked->GetNextUnit(); - while ( clicked ) { - tmp = CmdBuildVirtualRailVehicle(clicked->engine_type); - if ( tmp ) { - tmp->cargo_type = clicked->cargo_type; - tmp->cargo_subtype = clicked->cargo_subtype; - CmdMoveRailVehicle(0, DC_EXEC, (1<<21) | tmp->index, tail->index, 0); - tail = tmp; - } - clicked = clicked->GetNextUnit(); - } - return head; -} -TemplateVehicle* CloneTemplateVehicleFromTrain(const Train *t) -{ - Train *clicked = Train::Get(t->index); - if ( !clicked ) - return 0; - - Train *init_clicked = clicked; - - int len = CountVehiclesInChain(clicked); - if ( !TemplateVehicle::CanAllocateItem(len) ) - return 0; - - TemplateVehicle *tmp, *prev=0; - for ( ; clicked; clicked=clicked->Next() ) { - tmp = new TemplateVehicle(clicked->engine_type); - SetupTemplateVehicleFromVirtual(tmp, prev, clicked); - prev = tmp; - } - - tmp->First()->SetRealLength(CeilDiv(init_clicked->gcache.cached_total_length * 10, TILE_SIZE)); - return tmp->First(); -} // create a full TemplateVehicle based train according to a virtual train TemplateVehicle* TemplateVehicleFromVirtualTrain(Train *virt) { @@ -211,32 +165,6 @@ TemplateVehicle* TemplateVehicleFromVirtualTrain(Train *virt) return tmp->First(); } -// attempt to buy a train after a given template vehicle -// this might fail if the template e.g. deprecated and contains engines that are not sold anymore -Train* VirtualTrainFromTemplateVehicle(TemplateVehicle *tv) -{ - if ( !tv ) return 0; - CommandCost c; - Train *tmp, *head, *tail; - - head = CmdBuildVirtualRailVehicle(tv->engine_type); - if ( !head ) return 0; - - tail = head; - tv = tv->GetNextUnit(); - while ( tv ) { - tmp = CmdBuildVirtualRailVehicle(tv->engine_type); - if ( tmp ) { - tmp->cargo_type = tv->cargo_type; - tmp->cargo_subtype = tv->cargo_subtype; - CmdMoveRailVehicle(INVALID_TILE, DC_EXEC, (1<<21) | tmp->index, tail->index, 0); - tail = tmp; - } - tv = tv->GetNextUnit(); - } - return head; -} - // return last in a chain (really last, so even a singular articulated part of a vehicle if the last one is artic) inline TemplateVehicle* Last(TemplateVehicle *chain) { if ( !chain ) return 0; @@ -337,7 +265,7 @@ Train* DepotContainsEngine(TileIndex tile, EngineID eid, Train *not_in=0) { Train *t; FOR_ALL_TRAINS(t) { // conditions: v is stopped in the given depot, has the right engine and if 'not_in' is given v must not be contained within 'not_in' - // if 'not_in' is NULL, no check is needed + // if 'not_in' is NULL, no check is needed if ( t->tile==tile // If the veh belongs to a chain, wagons will not return true on IsStoppedInDepot(), only primary vehicles will // in case of t not a primary veh, we demand it to be a free wagon to consider it for replacement @@ -366,8 +294,9 @@ void CopyStatus(Train *from, Train *to) { } void NeutralizeStatus(Train *t) { DoCommand(t->tile, DEFAULT_GROUP, t->index, DC_EXEC, CMD_ADD_VEHICLE_GROUP); - - t->name = 0; + DoCommand(0, t->index | CO_UNSHARE << 30, 0, DC_EXEC, CMD_CLONE_ORDER); + DoCommand(0, t->index, FreeUnitIDGenerator(VEH_TRAIN, t->owner).NextID(), DC_EXEC, CMD_SET_VEHICLE_UNIT_NUMBER); + DoCommand(0, t->index, 0, DC_EXEC, CMD_RENAME_VEHICLE, NULL); } bool TrainMatchesTemplate(const Train *t, TemplateVehicle *tv) { while ( t && tv ) { @@ -440,7 +369,7 @@ int countOccurrencesInDepot(TileIndex tile, EngineID eid, Train *not_in=0) { Vehicle *v; FOR_ALL_VEHICLES(v) { // conditions: v is stopped in the given depot, has the right engine and if 'not_in' is given v must not be contained within 'not_in' - // if 'not_in' is NULL, no check is needed + // if 'not_in' is NULL, no check is needed if ( v->tile==tile && v->IsStoppedInDepot() && v->engine_type==eid && (not_in==0 || ChainContainsVehicle(not_in, (Train*)v)==0)) count++; @@ -494,13 +423,13 @@ int NumTrainsNeedTemplateReplacement(GroupID g_id, TemplateVehicle *tv) return count; } // refit each vehicle in t as is in tv, assume t and tv contain the same types of vehicles -static void RefitTrainFromTemplate(Train *t, TemplateVehicle *tv) +void CmdRefitTrainFromTemplate(Train *t, TemplateVehicle *tv, DoCommandFlag flags) { while ( t && tv ) { // refit t as tv uint32 cb = GetCmdRefitVeh(t); - DoCommandP(t->tile, t->index, tv->cargo_type | tv->cargo_subtype << 8 | 1 << 16 , cb); + DoCommand(t->tile, t->index, tv->cargo_type | tv->cargo_subtype << 8 | 1 << 16 | (1 << 5), flags, cb); // next t = t->GetNextUnit(); @@ -546,7 +475,7 @@ void TransferCargoForTrain(Train *old_veh, Train *new_head) { // calculate the free space for new cargo on the current vehicle uint curCap = tmp->cargo_cap - tmp->cargo.TotalCount(); - uint moveAmount = std::min(remainingAmount, curCap); + uint moveAmount = min(remainingAmount, curCap); // move (parts of) the old vehicle's cargo onto the current vehicle of the new chain if (moveAmount > 0) { @@ -562,184 +491,7 @@ void TransferCargoForTrain(Train *old_veh, Train *new_head) //if (src->cargo_cap < src->cargo.TotalCount()) src->cargo.Truncate(src->cargo.TotalCount() - src->cargo_cap); /* Update train weight etc., the old vehicle will be sold anyway */ - new_head->ConsistChanged(ConsistChangeFlags::CCF_LOADUNLOAD); -} - -// TODO: fit signature to regular cmd-structure -// do something with move_cost, it is not used right now -// if exec==DC_EXEC, test first and execute if sucessful -CommandCost CmdTemplateReplaceVehicle(Train *incoming, bool stayInDepot, DoCommandFlag flags) { - Train *new_chain=0, - *remainder_chain=0, - *tmp_chain=0; - TileIndex tile = incoming->tile; - TemplateVehicle *tv = GetTemplateVehicleByGroupID(incoming->group_id); - EngineID eid = tv->engine_type; - - CommandCost buy(EXPENSES_NEW_VEHICLES); - CommandCost move_cost(EXPENSES_NEW_VEHICLES); - CommandCost tmp_result(EXPENSES_NEW_VEHICLES); - - - /* first some tests on necessity and sanity */ - if ( !tv ) - return buy; - bool need_replacement = !TrainMatchesTemplate(incoming, tv); - bool need_refit = !TrainMatchesTemplateRefit(incoming, tv); - bool use_refit = tv->refit_as_template; - CargoID store_refit_ct = CT_INVALID; - short store_refit_csubt = 0; - // if a train shall keep its old refit, store the refit setting of its first vehicle - if ( !use_refit ) { - for ( Train *getc=incoming; getc; getc=getc->GetNextUnit() ) - if ( getc->cargo_type != CT_INVALID ) { - store_refit_ct = getc->cargo_type; - break; - } - } - - // TODO: set result status to success/no success before returning - if ( !need_replacement ) { - if ( !need_refit || !use_refit ) { - /* before returning, release incoming train first if 2nd param says so */ - if ( !stayInDepot ) incoming->vehstatus &= ~VS_STOPPED; - return buy; - } - } else { - CommandCost buyCost = TestBuyAllTemplateVehiclesInChain(tv, tile); - if ( !buyCost.Succeeded() || !CheckCompanyHasMoney(buyCost) ) { - if ( !stayInDepot ) incoming->vehstatus &= ~VS_STOPPED; - return buy; - } - } - - /* define replacement behaviour */ - bool reuseDepot = tv->IsSetReuseDepotVehicles(); - bool keepRemainders = tv->IsSetKeepRemainingVehicles(); - - if ( need_replacement ) { - /// step 1: generate primary for newchain and generate remainder_chain - // 1. primary of incoming might already fit the template - // leave incoming's primary as is and move the rest to a free chain = remainder_chain - // 2. needed primary might be one of incoming's member vehicles - // 3. primary might be available as orphan vehicle in the depot - // 4. we need to buy a new engine for the primary - // all options other than 1. need to make sure to copy incoming's primary's status - if ( eid == incoming->engine_type ) { // 1 - new_chain = incoming; - remainder_chain = incoming->GetNextUnit(); - if ( remainder_chain ) - move_cost.AddCost(CmdMoveRailVehicle(tile, flags, remainder_chain->index|(1<<20), INVALID_VEHICLE, 0)); - } - else if ( (tmp_chain = ChainContainsEngine(eid, incoming)) && tmp_chain!=NULL ) { // 2 - // new_chain is the needed engine, move it to an empty spot in the depot - new_chain = tmp_chain; - move_cost.AddCost(DoCommand(tile, new_chain->index, INVALID_VEHICLE, flags,CMD_MOVE_RAIL_VEHICLE)); - remainder_chain = incoming; - } - else if ( reuseDepot && (tmp_chain = DepotContainsEngine(tile, eid, incoming)) && tmp_chain!=NULL ) { // 3 - new_chain = tmp_chain; - move_cost.AddCost(DoCommand(tile, new_chain->index, INVALID_VEHICLE, flags, CMD_MOVE_RAIL_VEHICLE)); - remainder_chain = incoming; - } - else { // 4 - tmp_result = DoCommand(tile, eid, 0, flags, CMD_BUILD_VEHICLE); - /* break up in case buying the vehicle didn't succeed */ - if ( !tmp_result.Succeeded() ) - return tmp_result; - buy.AddCost(tmp_result); - new_chain = Train::Get(_new_vehicle_id); - /* make sure the newly built engine is not attached to any free wagons inside the depot */ - move_cost.AddCost ( DoCommand(tile, new_chain->index, INVALID_VEHICLE, flags, CMD_MOVE_RAIL_VEHICLE) ); - /* prepare the remainder chain */ - remainder_chain = incoming; - } - // If we bought a new engine or reused one from the depot, copy some parameters from the incoming primary engine - if ( incoming != new_chain && flags == DC_EXEC) { - CopyHeadSpecificThings(incoming, new_chain, flags); - NeutralizeStatus(incoming); - // additionally, if we don't want to use the template refit, refit as incoming - // the template refit will be set further down, if we use it at all - if ( !use_refit ) { - uint32 cb = GetCmdRefitVeh(new_chain); - DoCommandP(new_chain->tile, new_chain->index, store_refit_ct | store_refit_csubt << 8 | 1 << 16 , cb); - } - - } - - /// step 2: fill up newchain according to the template - // foreach member of template (after primary): - // 1. needed engine might be within remainder_chain already - // 2. needed engine might be orphaned within the depot (copy status) - // 3. we need to buy (again) (copy status) - TemplateVehicle *cur_tmpl = tv->GetNextUnit(); - Train *last_veh = new_chain; - while (cur_tmpl) { - // 1. engine contained in remainder chain - if ( (tmp_chain = ChainContainsEngine(cur_tmpl->engine_type, remainder_chain)) && tmp_chain!=NULL ) { - // advance remainder_chain (if necessary) to not lose track of it - if ( tmp_chain == remainder_chain ) - remainder_chain = remainder_chain->GetNextUnit(); - move_cost.AddCost(CmdMoveRailVehicle(tile, flags, tmp_chain->index, last_veh->index, 0)); - } - // 2. engine contained somewhere else in the depot - else if ( reuseDepot && (tmp_chain = DepotContainsEngine(tile, cur_tmpl->engine_type, new_chain)) && tmp_chain!=NULL ) { - move_cost.AddCost(CmdMoveRailVehicle(tile, flags, tmp_chain->index, last_veh->index, 0)); - } - // 3. must buy new engine - else { - tmp_result = DoCommand(tile, cur_tmpl->engine_type, 0, flags, CMD_BUILD_VEHICLE); - if ( !tmp_result.Succeeded() ) - return tmp_result; - buy.AddCost(tmp_result); - tmp_chain = Train::Get(_new_vehicle_id); - move_cost.AddCost(CmdMoveRailVehicle(tile, flags, tmp_chain->index, last_veh->index, 0)); - } - // TODO: is this enough ? might it be that we bought a new wagon here and it now has std refit ? - if ( need_refit && flags == DC_EXEC ) { - if ( use_refit ) { - uint32 cb = GetCmdRefitVeh(tmp_chain); - DoCommandP(tmp_chain->tile, tmp_chain->index, cur_tmpl->cargo_type | cur_tmpl->cargo_subtype << 8 | 1 << 16 , cb); - // old - // CopyWagonStatus(cur_tmpl, tmp_chain); - } else { - uint32 cb = GetCmdRefitVeh(tmp_chain); - DoCommandP(tmp_chain->tile, tmp_chain->index, store_refit_ct | store_refit_csubt << 8 | 1 << 16 , cb); - } - } - cur_tmpl = cur_tmpl->GetNextUnit(); - last_veh = tmp_chain; - } - } - /* no replacement done */ - else { - new_chain = incoming; - } - /// step 3: reorder and neutralize the remaining vehicles from incoming - // wagons remaining from remainder_chain should be filled up in as few freewagonchains as possible - // each locos might be left as singular in the depot - // neutralize each remaining engine's status - - // refit, only if the template option is set so - if ( use_refit && (need_refit || need_replacement) ) { - RefitTrainFromTemplate(new_chain, tv); - } - - if ( new_chain && remainder_chain ) - for ( Train *ct=remainder_chain; ct; ct=ct->GetNextUnit() ) - TransferCargoForTrain(ct, new_chain); - - // point incoming to the newly created train so that starting/stopping from the calling function can be done - incoming = new_chain; - if ( !stayInDepot && flags == DC_EXEC ) - new_chain->vehstatus &= ~VS_STOPPED; - - if ( remainder_chain && keepRemainders && flags == DC_EXEC ) - BreakUpRemainders(remainder_chain); - else if ( remainder_chain ) { - buy.AddCost(DoCommand(tile, remainder_chain->index | (1<<20), 0, flags, CMD_SELL_VEHICLE)); - } - return buy; + new_head->ConsistChanged(CCF_LOADUNLOAD); } diff --git a/src/tbtr_template_vehicle_func.h b/src/tbtr_template_vehicle_func.h index b6926f4ba3..2078191090 100644 --- a/src/tbtr_template_vehicle_func.h +++ b/src/tbtr_template_vehicle_func.h @@ -8,6 +8,10 @@ #include "tbtr_template_vehicle.h" //void DrawTemplateVehicle(TemplateVehicle*, int, const Rect&); + + +Train* VirtualTrainFromTemplateVehicle(TemplateVehicle* tv); + void DrawTemplateVehicle(const TemplateVehicle*, int, int, int, VehicleID, int, VehicleID); void BuildTemplateGuiList(GUITemplateList*, Scrollbar*, Owner, RailType); @@ -26,9 +30,9 @@ TemplateVehicle *CreateNewTemplateVehicle(EngineID); void setupVirtTrain(const TemplateVehicle*, Train*); -TemplateVehicle* TemplateVehicleFromVirtualTrain(Train*); +TemplateVehicle* TemplateVehicleFromVirtualTrain(Train *virt); -Train* VirtualTrainFromTemplateVehicle(TemplateVehicle*); +//Train* VirtualTrainFromTemplateVehicle(TemplateVehicle*); inline TemplateVehicle* Last(TemplateVehicle*); @@ -51,16 +55,21 @@ Train* DepotContainsEngine(TileIndex, EngineID, Train*); int NumTrainsNeedTemplateReplacement(GroupID, TemplateVehicle*); -CommandCost TestBuyAllTemplateVehiclesInChain(Train*); CommandCost CalculateTemplateReplacementCost(Train*); +CommandCost TestBuyAllTemplateVehiclesInChain(TemplateVehicle *tv, TileIndex tile); + +void CmdRefitTrainFromTemplate(Train *t, TemplateVehicle *tv, DoCommandFlag); +void BreakUpRemainders(Train *t); short CountEnginesInChain(Train*); bool TemplateVehicleContainsEngineOfRailtype(const TemplateVehicle*, RailType); -Train* CloneVirtualTrainFromTrain(const Train *); -TemplateVehicle* CloneTemplateVehicleFromTrain(const Train *); - void TransferCargoForTrain(Train*, Train*); +void NeutralizeStatus(Train *t); + +bool TrainMatchesTemplate(const Train *t, TemplateVehicle *tv); +bool TrainMatchesTemplateRefit(const Train *t, TemplateVehicle *tv); + #endif diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 8256de1fcc..511669a40e 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -35,14 +35,15 @@ #include "order_backup.h" #include "zoom_func.h" #include "newgrf_debug.h" +#include "tbtr_template_vehicle_func.h" +#include "autoreplace_func.h" +#include "engine_func.h" #include "table/strings.h" #include "table/train_cmd.h" #include "safeguards.h" -#include "engine_func.h" - static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck); static bool TrainCheckIfLineEnds(Train *v, bool reverse = true); bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // Also used in vehicle_sl.cpp. @@ -4119,23 +4120,19 @@ Train* CmdBuildVirtualRailWagon(const Engine *e) AddArticulatedParts(v); + // Make sure we set EVERYTHING to virtual, even articulated parts. + for (Train* train_part = v; train_part != NULL; train_part = train_part->Next()) { + train_part->SetVirtual(); + } + _new_vehicle_id = v->index; - // from revision r22xxx - // VehicleMove(v, false); - // new v->UpdateViewport(true, false); - v->First()->ConsistChanged(ConsistChangeFlags::CCF_ARRANGE); - //UpdateTrainGroupID(v->First()); + v->First()->ConsistChanged(CCF_ARRANGE); CheckConsistencyOfArticulatedVehicle(v); - /* The GVSF_VIRTUAL flag is used to prevent depot-tile sanity checks */ - SetBit(v->subtype, GVSF_VIRTUAL); - -// GroupStatistics::CountVehicle( v, -1 ); - return v; } @@ -4150,13 +4147,18 @@ Train* CmdBuildVirtualRailWagon(const Engine *e) */ Train* CmdBuildVirtualRailVehicle(EngineID eid) { - if ( !IsEngineBuildable(eid, VEH_TRAIN, _current_company) ) return 0; + if (!IsEngineBuildable(eid, VEH_TRAIN, _current_company)) + return nullptr; + const Engine* e = Engine::Get(eid); const RailVehicleInfo *rvi = &e->u.rail; int num_vehicles = (e->u.rail.railveh_type == RAILVEH_MULTIHEAD ? 2 : 1) + CountArticulatedParts(eid, false); - if ( !Train::CanAllocateItem(num_vehicles) ) return 0; - if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildVirtualRailWagon(e); + if (!Train::CanAllocateItem(num_vehicles)) + return nullptr; + + if (rvi->railveh_type == RAILVEH_WAGON) + return CmdBuildVirtualRailWagon(e); Train *v = new Train(); @@ -4191,9 +4193,6 @@ Train* CmdBuildVirtualRailVehicle(EngineID eid) v->SetFrontEngine(); v->SetEngine(); - // from revision r22xxx -// VehicleMove(v, false); - // new v->UpdateViewport(true, false); if (rvi->railveh_type == RAILVEH_MULTIHEAD) { @@ -4202,14 +4201,246 @@ Train* CmdBuildVirtualRailVehicle(EngineID eid) AddArticulatedParts(v); } - v->ConsistChanged(ConsistChangeFlags::CCF_ARRANGE); - //UpdateTrainGroupID(v); + // Make sure we set EVERYTHING to virtual, even articulated parts. + for (Train* train_part = v; train_part != NULL; train_part = train_part->Next()) { + train_part->SetVirtual(); + } + + v->ConsistChanged(CCF_ARRANGE); CheckConsistencyOfArticulatedVehicle(v); - SetBit(v->subtype, GVSF_VIRTUAL); - -// GroupStatistics::CountVehicle( v, -1 ); - return v; } + +/** + * Build a virtual train vehicle. + * @param tile unused + * @param flags type of operation + * @param p1 the engine ID to build + * @param p2 unused + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdBuildVirtualRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + EngineID eid = p1; + CommandCost cost = CommandCost(); + + bool should_execute = (flags & DC_EXEC) != 0; + + if (should_execute) { + Train* train = CmdBuildVirtualRailVehicle(eid); + + if (train == nullptr) + return CMD_ERROR; + } + + return CommandCost(); +} + +/** +* Replace a vehicle based on a template replacement order. +* @param tile unused +* @param flags type of operation +* @param p1 the ID of the vehicle to replace. +* @param p2 whether the vehicle should stay in the depot. +* @param text unused +* @return the cost of this operation or an error +*/ +CommandCost CmdTemplateReplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + VehicleID vehicle_id = p1; + + Vehicle* vehicle = Vehicle::GetIfValid(vehicle_id); + + if (vehicle == nullptr || vehicle->type != VEH_TRAIN) + return CMD_ERROR; + + bool should_execute = (flags & DC_EXEC) != 0; + + if (!should_execute) + return CommandCost(); + + Train* incoming = Train::From(vehicle); + bool stayInDepot = p2 != 0; + + Train *new_chain = 0, + *remainder_chain = 0, + *tmp_chain = 0; + TemplateVehicle *tv = GetTemplateVehicleByGroupID(incoming->group_id); + EngineID eid = tv->engine_type; + + CommandCost buy(EXPENSES_NEW_VEHICLES); + CommandCost move_cost(EXPENSES_NEW_VEHICLES); + CommandCost tmp_result(EXPENSES_NEW_VEHICLES); + + + /* first some tests on necessity and sanity */ + if (!tv) + return buy; + bool need_replacement = !TrainMatchesTemplate(incoming, tv); + bool need_refit = !TrainMatchesTemplateRefit(incoming, tv); + bool use_refit = tv->refit_as_template; + CargoID store_refit_ct = CT_INVALID; + short store_refit_csubt = 0; + // if a train shall keep its old refit, store the refit setting of its first vehicle + if (!use_refit) { + for (Train *getc = incoming; getc; getc = getc->GetNextUnit()) + if (getc->cargo_type != CT_INVALID) { + store_refit_ct = getc->cargo_type; + break; + } + } + + // TODO: set result status to success/no success before returning + if (!need_replacement) { + if (!need_refit || !use_refit) { + /* before returning, release incoming train first if 2nd param says so */ + if (!stayInDepot) incoming->vehstatus &= ~VS_STOPPED; + return buy; + } + } + else { + CommandCost buyCost = TestBuyAllTemplateVehiclesInChain(tv, tile); + if (!buyCost.Succeeded() || !CheckCompanyHasMoney(buyCost)) { + if (!stayInDepot) incoming->vehstatus &= ~VS_STOPPED; + return buy; + } + } + + /* define replacement behavior */ + bool reuseDepot = tv->IsSetReuseDepotVehicles(); + bool keepRemainders = tv->IsSetKeepRemainingVehicles(); + + if (need_replacement) { + /// step 1: generate primary for newchain and generate remainder_chain + // 1. primary of incoming might already fit the template + // leave incoming's primary as is and move the rest to a free chain = remainder_chain + // 2. needed primary might be one of incoming's member vehicles + // 3. primary might be available as orphan vehicle in the depot + // 4. we need to buy a new engine for the primary + // all options other than 1. need to make sure to copy incoming's primary's status + if (eid == incoming->engine_type) { // 1 + new_chain = incoming; + remainder_chain = incoming->GetNextUnit(); + if (remainder_chain) + move_cost.AddCost(CmdMoveRailVehicle(tile, flags, remainder_chain->index | (1 << 20), INVALID_VEHICLE, 0)); + } + else if ((tmp_chain = ChainContainsEngine(eid, incoming)) && tmp_chain != NULL) { // 2 + // new_chain is the needed engine, move it to an empty spot in the depot + new_chain = tmp_chain; + move_cost.AddCost(DoCommand(tile, new_chain->index, INVALID_VEHICLE, flags, CMD_MOVE_RAIL_VEHICLE)); + remainder_chain = incoming; + } + else if (reuseDepot && (tmp_chain = DepotContainsEngine(tile, eid, incoming)) && tmp_chain != NULL) { // 3 + new_chain = tmp_chain; + move_cost.AddCost(DoCommand(tile, new_chain->index, INVALID_VEHICLE, flags, CMD_MOVE_RAIL_VEHICLE)); + remainder_chain = incoming; + } + else { // 4 + tmp_result = DoCommand(tile, eid, 0, flags, CMD_BUILD_VEHICLE); + /* break up in case buying the vehicle didn't succeed */ + if (!tmp_result.Succeeded()) + return tmp_result; + buy.AddCost(tmp_result); + new_chain = Train::Get(_new_vehicle_id); + /* make sure the newly built engine is not attached to any free wagons inside the depot */ + move_cost.AddCost(DoCommand(tile, new_chain->index, INVALID_VEHICLE, flags, CMD_MOVE_RAIL_VEHICLE)); + /* prepare the remainder chain */ + remainder_chain = incoming; + } + // If we bought a new engine or reused one from the depot, copy some parameters from the incoming primary engine + if (incoming != new_chain && flags == DC_EXEC) { + CopyHeadSpecificThings(incoming, new_chain, flags); + NeutralizeStatus(incoming); + + + // additionally, if we don't want to use the template refit, refit as incoming + // the template refit will be set further down, if we use it at all + if (!use_refit) { + uint32 cb = GetCmdRefitVeh(new_chain); + DoCommand(new_chain->tile, new_chain->index, store_refit_ct | store_refit_csubt << 8 | 1 << 16 | (1 << 5), flags, cb); + } + + } + + /// step 2: fill up newchain according to the template + // foreach member of template (after primary): + // 1. needed engine might be within remainder_chain already + // 2. needed engine might be orphaned within the depot (copy status) + // 3. we need to buy (again) (copy status) + TemplateVehicle *cur_tmpl = tv->GetNextUnit(); + Train *last_veh = new_chain; + while (cur_tmpl) { + // 1. engine contained in remainder chain + if ((tmp_chain = ChainContainsEngine(cur_tmpl->engine_type, remainder_chain)) && tmp_chain != NULL) { + // advance remainder_chain (if necessary) to not lose track of it + if (tmp_chain == remainder_chain) + remainder_chain = remainder_chain->GetNextUnit(); + move_cost.AddCost(CmdMoveRailVehicle(tile, flags, tmp_chain->index, last_veh->index, 0)); + } + // 2. engine contained somewhere else in the depot + else if (reuseDepot && (tmp_chain = DepotContainsEngine(tile, cur_tmpl->engine_type, new_chain)) && tmp_chain != NULL) { + move_cost.AddCost(CmdMoveRailVehicle(tile, flags, tmp_chain->index, last_veh->index, 0)); + } + // 3. must buy new engine + else { + tmp_result = DoCommand(tile, cur_tmpl->engine_type, 0, flags, CMD_BUILD_VEHICLE); + if (!tmp_result.Succeeded()) + return tmp_result; + buy.AddCost(tmp_result); + tmp_chain = Train::Get(_new_vehicle_id); + move_cost.AddCost(CmdMoveRailVehicle(tile, flags, tmp_chain->index, last_veh->index, 0)); + } + // TODO: is this enough ? might it be that we bought a new wagon here and it now has std refit ? + if (need_refit && flags == DC_EXEC) { + if (use_refit) { + uint32 cb = GetCmdRefitVeh(tmp_chain); + DoCommand(tmp_chain->tile, tmp_chain->index, cur_tmpl->cargo_type | cur_tmpl->cargo_subtype << 8 | 1 << 16 | (1 << 5), flags, cb); + // old + // CopyWagonStatus(cur_tmpl, tmp_chain); + } + else { + uint32 cb = GetCmdRefitVeh(tmp_chain); + DoCommand(tmp_chain->tile, tmp_chain->index, store_refit_ct | store_refit_csubt << 8 | 1 << 16 | (1 << 5), flags, cb); + } + } + cur_tmpl = cur_tmpl->GetNextUnit(); + last_veh = tmp_chain; + } + } + /* no replacement done */ + else { + new_chain = incoming; + } + /// step 3: reorder and neutralize the remaining vehicles from incoming + // wagons remaining from remainder_chain should be filled up in as few freewagonchains as possible + // each locos might be left as singular in the depot + // neutralize each remaining engine's status + + // refit, only if the template option is set so + if (use_refit && (need_refit || need_replacement)) { + CmdRefitTrainFromTemplate(new_chain, tv, flags); + } + + if (new_chain && remainder_chain) + for (Train *ct = remainder_chain; ct; ct = ct->GetNextUnit()) + TransferCargoForTrain(ct, new_chain); + + // point incoming to the newly created train so that starting/stopping from the calling function can be done + incoming = new_chain; + if (!stayInDepot && flags == DC_EXEC) + new_chain->vehstatus &= ~VS_STOPPED; + + if (remainder_chain && keepRemainders && flags == DC_EXEC) + BreakUpRemainders(remainder_chain); + else if (remainder_chain) { + buy.AddCost(DoCommand(tile, remainder_chain->index | (1 << 20), 0, flags, CMD_SELL_VEHICLE)); + } + + /* Redraw main gui for changed statistics */ + SetWindowClassesDirty(WC_TEMPLATEGUI_MAIN); + + return buy; +} diff --git a/src/vehicle.cpp b/src/vehicle.cpp index d9f3afbd30..139fe559eb 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1032,9 +1032,7 @@ void CallVehicleTicks() bool stayInDepot = it->second; it->first->vehstatus |= VS_STOPPED; - CmdTemplateReplaceVehicle(t, stayInDepot, DC_EXEC); - /* Redraw main gui for changed statistics */ - SetWindowClassesDirty(WC_TEMPLATEGUI_MAIN); + DoCommand(t->tile, t->index, stayInDepot ? 1 : 0, DC_EXEC, CMD_TEMPLATE_REPLACE_VEHICLE); } tmpl_cur_company.Restore(); } diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp index c8fee79095..fc817bc213 100644 --- a/src/vehicle_cmd.cpp +++ b/src/vehicle_cmd.cpp @@ -31,6 +31,8 @@ #include "ship.h" #include "newgrf.h" #include "company_base.h" +#include "tbtr_template_vehicle.h" +#include "tbtr_template_vehicle_func.h" #include "table/strings.h" @@ -174,7 +176,10 @@ CommandCost CmdSellVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 if (front->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_VEHICLE_IS_DESTROYED); - if (!front->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT + front->type); + /* Do this check only if the vehicle to be moved is non-virtual */ + if (!HasBit(p1, 21)) { + if (!front->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT + front->type); + } /* Can we actually make the order backup, i.e. are there enough orders? */ if (p1 & MAKE_ORDER_BACKUP_FLAG && @@ -776,6 +781,442 @@ static void CloneVehicleName(const Vehicle *src, Vehicle *dst) /* All done. If we didn't find a name, it'll just use its default. */ } +inline void SetupTemplateVehicleFromVirtual(TemplateVehicle *tmp, TemplateVehicle *prev, Train *virt) +{ + if (prev) { + prev->SetNext(tmp); + tmp->SetPrev(prev); + tmp->SetFirst(prev->First()); + } + tmp->railtype = virt->railtype; + tmp->owner = virt->owner; + tmp->value = virt->value; + + // set the subtype but also clear the virtual flag while doing it + tmp->subtype = virt->subtype & ~(1 << GVSF_VIRTUAL); + // set the cargo type and capacity + tmp->cargo_type = virt->cargo_type; + tmp->cargo_subtype = virt->cargo_subtype; + tmp->cargo_cap = virt->cargo_cap; + + const GroundVehicleCache *gcache = virt->GetGroundVehicleCache(); + tmp->max_speed = virt->GetDisplayMaxSpeed(); + tmp->power = gcache->cached_power; + tmp->weight = gcache->cached_weight; + tmp->max_te = gcache->cached_max_te / 1000; + + tmp->spritenum = virt->spritenum; + tmp->cur_image = virt->GetImage(DIR_W, EIT_PURCHASE); + Point *p = new Point(); + tmp->image_width = virt->GetDisplayImageWidth(p); +} + +/** + * Toggles 'reuse depot vehicles' on a template vehicle. + * @param tile unused + * @param flags type of operation + * @param p1 the template vehicle's index + * @param p2 unused + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdToggleReuseDepotVehicles(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + // Identify template to toggle + TemplateVehicle *template_vehicle = TemplateVehicle::GetIfValid(p1); + + if (template_vehicle == NULL) + return CMD_ERROR; + + bool should_execute = (flags & DC_EXEC) != 0; + + if (should_execute) { + template_vehicle->ToggleReuseDepotVehicles(); + + InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0); + } + + return CommandCost(); +} + +/** + * Toggles 'keep remaining vehicles' on a template vehicle. + * @param tile unused + * @param flags type of operation + * @param p1 the template vehicle's index + * @param p2 unused + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdToggleKeepRemainingVehicles(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + // Identify template to toggle + TemplateVehicle *template_vehicle = TemplateVehicle::GetIfValid(p1); + + if (template_vehicle == NULL) + return CMD_ERROR; + + bool should_execute = (flags & DC_EXEC) != 0; + + if (should_execute) { + template_vehicle->ToggleKeepRemainingVehicles(); + + InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0); + } + + return CommandCost(); +} + +/** + * Toggles 'refit as template' on a template vehicle. + * @param tile unused + * @param flags type of operation + * @param p1 the template vehicle's index + * @param p2 unused + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdToggleRefitAsTemplate(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + // Identify template to toggle + TemplateVehicle *template_vehicle = TemplateVehicle::GetIfValid(p1); + + if (template_vehicle == NULL) + return CMD_ERROR; + + bool should_execute = (flags & DC_EXEC) != 0; + + if (should_execute) { + template_vehicle->ToggleRefitAsTemplate(); + + InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0); + } + + return CommandCost(); +} + +/** + * Create a virtual train from a template vehicle. + * @param tile unused + * @param flags type of operation + * @param p1 the original vehicle's index + * @param p2 unused + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdVirtualTrainFromTemplateVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + VehicleID template_vehicle_id = p1; + + TemplateVehicle* tv = TemplateVehicle::GetIfValid(template_vehicle_id); + + if (tv == NULL) + return CMD_ERROR; + + bool should_execute = (flags & DC_EXEC) != 0; + + if (should_execute) { + Train* train = VirtualTrainFromTemplateVehicle(tv); + + if (train == nullptr) + return CMD_ERROR; + } + + return CommandCost(); +} + +Train* VirtualTrainFromTemplateVehicle(TemplateVehicle* tv) +{ + CommandCost c; + Train *tmp, *head, *tail; + + head = CmdBuildVirtualRailVehicle(tv->engine_type); + if ( !head ) return nullptr; + + tail = head; + tv = tv->GetNextUnit(); + while ( tv ) { + tmp = CmdBuildVirtualRailVehicle(tv->engine_type); + if ( tmp ) { + tmp->cargo_type = tv->cargo_type; + tmp->cargo_subtype = tv->cargo_subtype; + CmdMoveRailVehicle(INVALID_TILE, DC_EXEC, (1<<21) | tmp->index, tail->index, 0); + tail = tmp; + } + tv = tv->GetNextUnit(); + } + + _new_vehicle_id = head->index; + + return head; +} + +/** + * Create a virtual train from a regular train. + * @param tile unused + * @param flags type of operation + * @param p1 the train index + * @param p2 unused + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdVirtualTrainFromTrain(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + VehicleID vehicle_id = p1; + Vehicle* vehicle = Vehicle::GetIfValid(vehicle_id); + + if (vehicle == nullptr || vehicle->type != VEH_TRAIN) + return CMD_ERROR; + + Train* train = Train::From(vehicle); + + bool should_execute = (flags & DC_EXEC) != 0; + + if (should_execute) { + CommandCost c; + Train *tmp, *head, *tail; + + head = CmdBuildVirtualRailVehicle(train->engine_type); + if ( !head ) return CMD_ERROR; + + tail = head; + train = train->GetNextUnit(); + while ( train ) { + tmp = CmdBuildVirtualRailVehicle(train->engine_type); + if ( tmp ) { + tmp->cargo_type = train->cargo_type; + tmp->cargo_subtype = train->cargo_subtype; + CmdMoveRailVehicle(0, DC_EXEC, (1<<21) | tmp->index, tail->index, 0); + tail = tmp; + } + train = train->GetNextUnit(); + } + + _new_vehicle_id = head->index; + } + + return CommandCost(); +} + +/** + * Create a virtual train from a template vehicle. + * @param tile unused + * @param flags type of operation + * @param p1 the vehicle's index + * @param p2 unused + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdDeleteVirtualTrain(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + VehicleID vehicle_id = p1; + + Vehicle* vehicle = Vehicle::GetIfValid(vehicle_id); + + if (vehicle == nullptr || vehicle->type != VEH_TRAIN) + return CMD_ERROR; + + Train* train = Train::From(vehicle); + + bool should_execute = (flags & DC_EXEC) != 0; + + if (should_execute) { + delete train; + } + + return CommandCost(); +} + +/** + * Replace a template vehicle with another one based on a virtual train. + * @param tile unused + * @param flags type of operation + * @param p1 the template vehicle's index + * @param p2 the virtual train's index + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdReplaceTemplateVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + VehicleID template_vehicle_id = p1; + VehicleID virtual_train_id = p2; + + TemplateVehicle* template_vehicle = TemplateVehicle::GetIfValid(template_vehicle_id); + Vehicle* vehicle = Vehicle::GetIfValid(virtual_train_id); + + if (vehicle == nullptr || vehicle->type != VEH_TRAIN) + return CMD_ERROR; + + Train* train = Train::From(vehicle); + + bool should_execute = (flags & DC_EXEC) != 0; + + if (should_execute) { + VehicleID old_ID = INVALID_VEHICLE; + + if (template_vehicle != nullptr) { + old_ID = template_vehicle->index; + delete template_vehicle; + template_vehicle = nullptr; + } + + template_vehicle = TemplateVehicleFromVirtualTrain(train); + + if (template_vehicle == nullptr) + return CMD_ERROR; + + // Make sure our replacements still point to the correct thing. + if (old_ID != template_vehicle->index) { + TemplateReplacement* tr; + FOR_ALL_TEMPLATE_REPLACEMENTS(tr) { + if (tr->GetTemplateVehicleID() == old_ID) { + tr->SetTemplate(template_vehicle->index); + } + } + } + + InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0); + } + + return CommandCost(); +} + +/** + * Clone a vehicle to create a template vehicle. + * @param tile unused + * @param flags type of operation + * @param p1 the original vehicle's index + * @param p2 unused + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdTemplateVehicleFromTrain(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + // create a new template from the clicked vehicle + TemplateVehicle *tv; + + Vehicle *t = Vehicle::GetIfValid(p1); + + Train *clicked = Train::GetIfValid(t->index); + if (!clicked) + return CMD_ERROR; + + Train *init_clicked = clicked; + + int len = CountVehiclesInChain(clicked); + if (!TemplateVehicle::CanAllocateItem(len)) + return CMD_ERROR; + + bool should_execute = (flags & DC_EXEC) != 0; + + if (should_execute) { + TemplateVehicle *tmp, *prev=0; + for (; clicked; clicked=clicked->Next()) { + tmp = new TemplateVehicle(clicked->engine_type); + SetupTemplateVehicleFromVirtual(tmp, prev, clicked); + prev = tmp; + } + + tmp->First()->SetRealLength(CeilDiv(init_clicked->gcache.cached_total_length * 10, TILE_SIZE)); + tv = tmp->First(); + + if (!tv) return CMD_ERROR; + + InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0); + } + + return CommandCost(); +} + +/** + * Delete a template vehicle. + * @param tile unused + * @param flags type of operation + * @param p1 the template vehicle's index + * @param p2 unused + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdDeleteTemplateVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + // Identify template to delete + TemplateVehicle *del = TemplateVehicle::GetIfValid(p1); + + if (del == NULL) + return CMD_ERROR; + + bool should_execute = (flags & DC_EXEC) != 0; + + if (should_execute) { + // Remove a corresponding template replacement if existing + TemplateReplacement *tr = GetTemplateReplacementByTemplateID(del->index); + if (tr != NULL) { + delete tr; + } + + delete del; + + InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0); + } + + return CommandCost(); +} + +/** + * Issues a template replacement for a vehicle group + * @param tile unused + * @param flags type of operation + * @param p1 the group index + * @param p2 the template vehicle's index + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdIssueTemplateReplacement(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + bool should_execute = (flags & DC_EXEC) != 0; + + GroupID group_id = p1; + TemplateID template_id = p2; + + if (should_execute) { + bool succeeded = IssueTemplateReplacement(group_id, template_id); + + if (!succeeded) + return CMD_ERROR; + + InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0); + } + + return CommandCost(); +} + +/** + * Deletes a template replacement from a vehicle group + * @param tile unused + * @param flags type of operation + * @param p1 the group index + * @param p2 unused + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdDeleteTemplateReplacement(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + bool should_execute = (flags & DC_EXEC) != 0; + + GroupID group_id = p1; + + if (should_execute) { + TemplateReplacement* tr = GetTemplateReplacementByGroupID(group_id); + if (tr != NULL) + delete tr; + + InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0); + } + + return CommandCost(); +} + + /** * Clone a vehicle. If it is a train, it will clone all the cars too * @param tile tile of the depot where the cloned vehicle is build @@ -1020,6 +1461,30 @@ CommandCost CmdSendVehicleToDepot(TileIndex tile, DoCommandFlag flags, uint32 p1 return v->SendToDepot(flags, (DepotCommand)(p1 & DEPOT_COMMAND_MASK)); } +/** + * Sets the vehicle unit number + * @param tile unused + * @param flags type of operation + * @param p1 vehicle ID to set number on + * @param p2 vehicle unit number + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdSetVehicleUnitNumber(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + Vehicle *v = Vehicle::GetIfValid(p1); + if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR; + + CommandCost ret = CheckOwnership(v->owner); + if (ret.Failed()) return ret; + + if (flags & DC_EXEC) { + v->unitnumber = (UnitID)p2; + } + + return CommandCost(); +} + /** * Give a custom name to your vehicle * @param tile unused