Update from KeldorKatarn branch

This approximately corresponds to 971ba4928a5c7c7916fea55d91a3b6dd5bba140c,
excluding the different virtual train build GUI, but including the basic
changes to the original train build GUI for multiplayer to work.

Fixup
pull/6/merge
Jonathan G Rennison 8 years ago
parent 769462f537
commit 6be2efc084

@ -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

@ -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 */

@ -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

@ -295,6 +295,16 @@ struct GroundVehicle : public SpecializedVehicle<T, Type> {
*/
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<T, Type> {
*/
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.

@ -51,6 +51,10 @@ static CommandCallback * const _callback_table[] = {
/* 0x19 */ CcStartStopVehicle,
/* 0x1A */ CcGame,
/* 0x1B */ CcAddVehicleNewGroup,
/* 0x1C */ CcSetVirtualTrain,
/* 0x1D */ CcVirtualTrainWaggonsMoved,
/* 0x1E */ CcDeleteVirtualTrain,
/* 0x1F */ CcAddVirtualEngine,
};
/**

@ -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<NWidgetCore>(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<NWidgetCore>(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<NWidgetCore>(TCW_NEW_TMPL_PANEL);
this->hscroll->SetCapacity(template_panel->current_x);
NWidgetCore *info_panel = this->GetWidget<NWidgetCore>(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<NWidgetBase>(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<NWidgetBase>(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<NWidgetBase>(TCW_MATRIX_NEW_TMPL);
NWidgetBase *nwi = this->GetWidget<NWidgetBase>(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<NWidgetCore>(TCW_MATRIX_NEW_TMPL);
const NWidgetCore *matrix_widget = this->GetWidget<NWidgetCore>(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);
}

@ -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

@ -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<int, 16> 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<NWidgetCore>(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<NWidgetCore>(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<NWidgetCore>(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;
}
};

@ -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<NWidgetCore>(RPLALL_GUI_PANEL_BUTTONFLUFF_3)->colour = _company_colours[_local_company];
this->GetWidget<NWidgetCore>(RPLALL_GUI_PANEL_BUTTONFLUFF_3)->colour = _company_colours[_local_company];
this->DrawWidgets();
}

@ -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 )

@ -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);
}

@ -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

@ -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);
return v;
}
// GroupStatistics::CountVehicle( v, -1 );
/**
* 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();
return v;
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;
}

@ -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();
}

@ -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

Loading…
Cancel
Save