Merge branch 'template_train_replacement-sx' into jgrpp

Remove a duplicated declaration.

# Conflicts:
#	projects/openttd_vs100.vcxproj
#	projects/openttd_vs100.vcxproj.filters
#	projects/openttd_vs140.vcxproj
#	projects/openttd_vs140.vcxproj.filters
#	projects/openttd_vs80.vcproj
#	projects/openttd_vs90.vcproj
#	source.list
#	src/group_gui.cpp
#	src/lang/english.txt
#	src/network/network_command.cpp
#	src/saveload/extended_ver_sl.cpp
#	src/saveload/extended_ver_sl.h
#	src/saveload/saveload.cpp
#	src/train_cmd.cpp
#	src/vehicle.cpp
#	src/vehicle_gui.cpp
#	src/vehicle_gui_base.h
#	src/window_type.h
pull/6/merge
Jonathan G Rennison 8 years ago
commit 580b2f7584

@ -291,6 +291,16 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\tbtr_template_gui_main.cpp" />
<ClCompile Include="..\src\tbtr_template_gui_create.cpp" />
<ClCompile Include="..\src\tbtr_template_gui_create_virtualtrain.cpp" />
<ClCompile Include="..\src\tbtr_template_vehicle.cpp" />
<ClCompile Include="..\src\tbtr_template_vehicle_func.cpp" />
<ClInclude Include="..\src\tbtr_template_gui_main.h" />
<ClInclude Include="..\src\tbtr_template_gui_create.h" />
<ClInclude Include="..\src\tbtr_template_gui_create_virtualtrain.h" />
<ClInclude Include="..\src\tbtr_template_vehicle.h" />
<ClInclude Include="..\src\tbtr_template_vehicle_func.h" />
<ClCompile Include="..\src\airport.cpp" />
<ClCompile Include="..\src\animated_tile.cpp" />
<ClCompile Include="..\src\articulated_vehicles.cpp" />
@ -897,6 +907,8 @@
<ClCompile Include="..\src\saveload\signal_sl.cpp" />
<ClInclude Include="..\src\saveload\extended_ver_sl.h" />
<ClCompile Include="..\src\saveload\extended_ver_sl.cpp" />
<ClCompile Include="..\src\saveload\tbtr_template_replacement_sl.cpp" />
<ClCompile Include="..\src\saveload\tbtr_template_veh_sl.cpp" />
<ClInclude Include="..\src\table\airport_defaults.h" />
<ClInclude Include="..\src\table\airport_movement.h" />
<ClInclude Include="..\src\table\airporttile_ids.h" />

@ -102,6 +102,36 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\tbtr_template_gui_main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_gui_create.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_gui_create_virtualtrain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_vehicle.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_vehicle_func.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClInclude Include="..\src\tbtr_template_gui_main.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_gui_create.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_gui_create_virtualtrain.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_vehicle.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_vehicle_func.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClCompile Include="..\src\airport.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -1920,6 +1950,12 @@
<ClCompile Include="..\src\saveload\extended_ver_sl.cpp">
<Filter>Save/Load handlers</Filter>
</ClCompile>
<ClCompile Include="..\src\saveload\tbtr_template_replacement_sl.cpp">
<Filter>Save/Load handlers</Filter>
</ClCompile>
<ClCompile Include="..\src\saveload\tbtr_template_veh_sl.cpp">
<Filter>Save/Load handlers</Filter>
</ClCompile>
<ClInclude Include="..\src\table\airport_defaults.h">
<Filter>Tables</Filter>
</ClInclude>

@ -308,6 +308,16 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\tbtr_template_gui_main.cpp" />
<ClCompile Include="..\src\tbtr_template_gui_create.cpp" />
<ClCompile Include="..\src\tbtr_template_gui_create_virtualtrain.cpp" />
<ClCompile Include="..\src\tbtr_template_vehicle.cpp" />
<ClCompile Include="..\src\tbtr_template_vehicle_func.cpp" />
<ClInclude Include="..\src\tbtr_template_gui_main.h" />
<ClInclude Include="..\src\tbtr_template_gui_create.h" />
<ClInclude Include="..\src\tbtr_template_gui_create_virtualtrain.h" />
<ClInclude Include="..\src\tbtr_template_vehicle.h" />
<ClInclude Include="..\src\tbtr_template_vehicle_func.h" />
<ClCompile Include="..\src\airport.cpp" />
<ClCompile Include="..\src\animated_tile.cpp" />
<ClCompile Include="..\src\articulated_vehicles.cpp" />
@ -914,6 +924,8 @@
<ClCompile Include="..\src\saveload\signal_sl.cpp" />
<ClInclude Include="..\src\saveload\extended_ver_sl.h" />
<ClCompile Include="..\src\saveload\extended_ver_sl.cpp" />
<ClCompile Include="..\src\saveload\tbtr_template_replacement_sl.cpp" />
<ClCompile Include="..\src\saveload\tbtr_template_veh_sl.cpp" />
<ClInclude Include="..\src\table\airport_defaults.h" />
<ClInclude Include="..\src\table\airport_movement.h" />
<ClInclude Include="..\src\table\airporttile_ids.h" />

@ -102,6 +102,36 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\tbtr_template_gui_main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_gui_create.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_gui_create_virtualtrain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_vehicle.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_vehicle_func.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClInclude Include="..\src\tbtr_template_gui_main.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_gui_create.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_gui_create_virtualtrain.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_vehicle.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_vehicle_func.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClCompile Include="..\src\airport.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -1920,6 +1950,12 @@
<ClCompile Include="..\src\saveload\extended_ver_sl.cpp">
<Filter>Save/Load handlers</Filter>
</ClCompile>
<ClCompile Include="..\src\saveload\tbtr_template_replacement_sl.cpp">
<Filter>Save/Load handlers</Filter>
</ClCompile>
<ClCompile Include="..\src\saveload\tbtr_template_veh_sl.cpp">
<Filter>Save/Load handlers</Filter>
</ClCompile>
<ClInclude Include="..\src\table\airport_defaults.h">
<Filter>Tables</Filter>
</ClInclude>

@ -434,6 +434,46 @@
<Filter
Name="Source Files"
>
<File
RelativePath=".\..\src\tbtr_template_gui_main.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create_virtualtrain.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle_func.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_main.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create_virtualtrain.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle_func.h"
>
</File>
<File
RelativePath=".\..\src\airport.cpp"
>
@ -2882,6 +2922,14 @@
RelativePath=".\..\src\saveload\extended_ver_sl.cpp"
>
</File>
<File
RelativePath=".\..\src\saveload\tbtr_template_replacement_sl.cpp"
>
</File>
<File
RelativePath=".\..\src\saveload\tbtr_template_veh_sl.cpp"
>
</File>
</Filter>
<Filter
Name="Tables"

@ -431,6 +431,46 @@
<Filter
Name="Source Files"
>
<File
RelativePath=".\..\src\tbtr_template_gui_main.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create_virtualtrain.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle_func.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_main.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create_virtualtrain.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle_func.h"
>
</File>
<File
RelativePath=".\..\src\airport.cpp"
>
@ -2879,6 +2919,14 @@
RelativePath=".\..\src\saveload\extended_ver_sl.cpp"
>
</File>
<File
RelativePath=".\..\src\saveload\tbtr_template_replacement_sl.cpp"
>
</File>
<File
RelativePath=".\..\src\saveload\tbtr_template_veh_sl.cpp"
>
</File>
</Filter>
<Filter
Name="Tables"

@ -1,4 +1,15 @@
# Source Files
tbtr_template_gui_main.cpp
tbtr_template_gui_create.cpp
tbtr_template_gui_create_virtualtrain.cpp
tbtr_template_vehicle.cpp
tbtr_template_vehicle_func.cpp
tbtr_template_gui_main.h
tbtr_template_gui_create.h
tbtr_template_gui_create_virtualtrain.h
tbtr_template_vehicle.h
tbtr_template_vehicle_func.h
airport.cpp
animated_tile.cpp
articulated_vehicles.cpp
@ -661,6 +672,8 @@ saveload/waypoint_sl.cpp
saveload/signal_sl.cpp
saveload/extended_ver_sl.h
saveload/extended_ver_sl.cpp
saveload/tbtr_template_replacement_sl.cpp
saveload/tbtr_template_veh_sl.cpp
# Tables
table/airport_defaults.h

@ -347,7 +347,7 @@ static inline CommandCost CmdMoveVehicle(const Vehicle *v, const Vehicle *after,
* @param new_head The new head of the completely replaced vehicle chain
* @param flags the command flags to use
*/
static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, DoCommandFlag flags)
CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, DoCommandFlag flags)
{
CommandCost cost = CommandCost();

@ -99,4 +99,6 @@ static inline CommandCost RemoveEngineReplacementForCompany(Company *c, EngineID
bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company);
CommandCost CopyHeadSpecificThings(Vehicle*, Vehicle*, DoCommandFlag);
#endif /* AUTOREPLACE_FUNC_H */

@ -108,6 +108,8 @@ CommandProc CmdDecreaseLoan;
CommandProc CmdWantEnginePreview;
CommandProc CmdSetVehicleUnitNumber;
CommandProc CmdRenameVehicle;
CommandProc CmdRenameEngine;
@ -177,10 +179,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;
@ -287,6 +306,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
@ -354,10 +375,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

@ -128,4 +128,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 */

@ -242,6 +242,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
@ -308,10 +310,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

@ -298,6 +298,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.
*/
@ -332,6 +342,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.

@ -21,6 +21,7 @@
#include "company_func.h"
#include "core/pool_func.hpp"
#include "order_backup.h"
#include "tbtr_template_vehicle.h"
#include "table/strings.h"
@ -137,6 +138,9 @@ void GroupStatistics::Clear()
*/
/* static */ void GroupStatistics::CountVehicle(const Vehicle *v, int delta)
{
/* make virtual trains group-neutral */
if ( HasBit(v->subtype, GVSF_VIRTUAL) ) return;
assert(delta == 1 || delta == -1);
GroupStatistics &stats_all = GroupStatistics::GetAllGroup(v);
@ -341,6 +345,9 @@ CommandCost CmdDeleteGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
VehicleType vt = g->vehicle_type;
/* Delete all template replacements using the just deleted group */
deleteIllegalTemplateReplacements(g->index);
/* Delete the Replace Vehicle Windows */
DeleteWindowById(WC_REPLACE_VEHICLE, g->vehicle_type);
delete g;

@ -25,6 +25,8 @@
#include "vehicle_gui_base.h"
#include "core/geometry_func.hpp"
#include "company_base.h"
#include "tbtr_template_gui_main.h"
#include "widgets/group_widget.h"
#include "core/smallvec_type.hpp"
@ -439,7 +441,7 @@ public:
break;
case WID_GL_MANAGE_VEHICLES_DROPDOWN: {
Dimension d = this->GetActionDropdownSize(true, true);
Dimension d = this->GetActionDropdownSize(true, true, this->vli.vtype == VEH_TRAIN);
d.height += padding.height;
d.width += padding.width;
*size = maxdim(*size, d);
@ -751,6 +753,7 @@ public:
case WID_GL_DELETE_GROUP: { // Delete the selected group
this->group_confirm = this->vli.index;
ShowQuery(STR_QUERY_GROUP_DELETE_CAPTION, STR_GROUP_DELETE_QUERY_TEXT, this, DeleteGroupCallback);
InvalidateWindowData(WC_TEMPLATEGUI_MAIN, 0, 0, 0);
break;
}
@ -789,7 +792,7 @@ public:
break;
case WID_GL_MANAGE_VEHICLES_DROPDOWN: {
DropDownList *list = this->BuildActionDropdownList(true, Group::IsValidID(this->vli.index));
DropDownList *list = this->BuildActionDropdownList(true, Group::IsValidID(this->vli.index), this->vli.vtype == VEH_TRAIN);
ShowDropDownList(this, list, 0, WID_GL_MANAGE_VEHICLES_DROPDOWN);
break;
}
@ -898,6 +901,7 @@ public:
virtual void OnQueryTextFinished(char *str)
{
if (str != NULL) DoCommandP(0, this->group_rename, 0, CMD_ALTER_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_RENAME), NULL, str);
InvalidateWindowData(WC_TEMPLATEGUI_MAIN, 0, 0, 0);
this->group_rename = INVALID_GROUP;
}
@ -918,6 +922,11 @@ public:
assert(this->vehicles.Length() != 0);
switch (index) {
case ADI_TEMPLATE_REPLACE: // TemplateReplace Window
if (vli.vtype == VEH_TRAIN) {
ShowTemplateReplaceWindow(this->unitnumber_digits, this->resize.step_height);
}
break;
case ADI_REPLACE: // Replace window
ShowReplaceGroupVehicleWindow(this->vli.index, this->vli.vtype);
break;

@ -5582,3 +5582,62 @@ STR_ZONING_STA_CATCH_OPEN :Station catchme
STR_ZONING_BUL_UNSER :Unserved buildings
STR_ZONING_IND_UNSER :Unserved industries
STR_ZONING_TRACERESTRICT :Restricted signals
STR_TMPL_RPL_TITLE :{WHITE}Template Replacement
STR_TMPL_TEMPLATE_REPLACEMENT :Template Replacement
STR_TMPL_TRAINS_IN_GROUP :{BLACK}Trains in group
STR_TMPL_AVAILABLE_TEMPLATES :{BLACK}Available Templates
STR_TMPL_DEFINE_TEMPLATE :{BLACK}New
STR_TMPL_EDIT_TEMPLATE :{BLACK}Edit
STR_TMPL_CREATE_CLONE_VEH :{BLACK}Clone
STR_TMPL_DELETE_TEMPLATE :{BLACK}Delete
STR_TMPL_RPL_ALL_TMPL :{BLACK}Replace All Templates
STR_TMPL_NEW_VEHICLE :{BLACK}New Vehicle
STR_TMPL_CONFIRM :{BLACK}Ok
STR_TMPL_CANCEL :{BLACK}Cancel
STR_TMPL_NEW :{BLACK}New Template Vehicle
STR_TMPL_REFIT :{BLACK}Refit
STR_TMPL_GROUP_INFO :{BLACK}Group Info: {ORANGE}
STR_TMPL_TEMPLATE_INFO :{BLACK}Template Info: {ORANGE}
STR_TMPL_RPL_START :{BLACK}Start replacing
STR_TMPL_RPL_STOP :{BLACK}Stop replacing
STR_TMPL_TRAIN_OVR_VALUE :{TINY_FONT}{BLACK}Train Value: {CURRENCY_SHORT}
STR_TMPL_TEMPLATE_OVR_VALUE :{TINY_FONT}{BLACK}Buying Cost: {GOLD}{CURRENCY_LONG}
STR_TMPL_TEMPLATE_OVR_VALUE_nogold :{TINY_FONT}{BLACK}Buying Cost: {CURRENCY_LONG}
STR_TMPL_TEMPLATE_OVR_VALUE_nogoldandcurrency :{TINY_FONT}{BLACK}Buying Cost:
STR_TMPL_TEMPLATE_OVR_VALUE_notinyfont :{BLACK}Buying Cost: {GOLD}{CURRENCY_LONG}
STR_TMPL_TEMPLATE_OVR_VALUE_notinyfontandblack :Buying Cost: {GOLD}{CURRENCY_LONG}
STR_TMPL_WARNING_FREE_WAGON :{RED}Free Chain: not runnable!
STR_TMPL_TEST :{ORANGE}Test String: {RAW_STRING} {RAW_STRING}
STR_TMPL_GROUP_USES_TEMPLATE :{BLACK}Template in use: {NUM}
STR_TMP_TEMPLATE_IN_USE :Template is in use
STR_TMPL_GROUP_NUM_TRAINS :{BLACK}{NUM}
STR_TMPL_CREATEGUI_TITLE :{WHITE}Create/Edit Template Vehicle
STR_TMPL_MAINGUI_DEFINEDGROUPS :{BLACK}Defined Groups for Company
STR_TMPL_TMPLRPL_EX_DIFF_RAILTYPE :Uses Template of different rail type
STR_TMPL_SET_USEDEPOT :{BLACK}Use vehicles in depot
STR_TMPL_SET_USEDEPOT_TIP :{BLACK}Use vehicles inside the depot that are in a neutral and idle state to compose trains on template replacement in order to reduce buying costs
STR_TMPL_SET_KEEPREMAINDERS :{BLACK}Keep remainders
STR_TMPL_SET_KEEPREMAINDERS_TIP :{BLACK}After finishing template replacement keep all remaining vehicles from the old train in a neutral and idle state for later use
STR_TMPL_SET_REFIT :{BLACK}Use Refit
STR_TMPL_SET_REFIT_TIP :{BLACK}If set, the train will use exactly the cargo refit specified by the template. If not every wagon that is to be newly bought or retrieved from the depot, will *attempt* to be refitted as the old one was. Standard refit if this is impossible.
STR_TMPL_CONFIG_USEDEPOT :use depot
STR_TMPL_CONFIG_KEEPREMAINDERS :keep rem
STR_TMPL_CONFIG_REFIT :refit
STR_TMPL_NUM_TRAINS_NEED_RPL :# trains to replace:
STR_TMPL_CARGO_SUMMARY :{CARGO_LONG}
STR_TMPL_CARGO_SUMMARY_MULTI :{CARGO_LONG} (x{NUM})
STR_TMPL_RPLALLGUI_TITLE :{WHITE}Replace all Templace Vehicles
STR_TMPL_RPLALLGUI_INSET_TOP :{BLACK}Choose Vehicle Type and Replacement
STR_TMPL_RPLALLGUI_INSET_TOP_1 :{BLACK}Template Engines
STR_TMPL_RPLALLGUI_INSET_TOP_2 :{BLACK}Buyable Engines
STR_TMPL_RPLALLGUI_INSET_BOTTOM :{BLACK}Current Template List (updated only after replacement)
STR_TMPL_RPLALLGUI_BUTTON_RPLALL :{BLACK}Replace All
STR_TMPL_RPLALLGUI_BUTTON_APPLY :{BLACK}Apply
STR_TMPL_RPLALLGUI_BUTTON_CANCEL :{BLACK}Cancel
STR_TMPL_RPLALLGUI_USE_TIP :{BLACK}Select a vehicle type from each list and press 'Replace All'. If you are happy with the result displayed in the template list, press 'Apply' to actually apply these changes.

@ -167,7 +167,7 @@ STR_ABBREV_ALL :{TINY_FONT}ALL
# 'Mode' of transport for cargoes
STR_PASSENGERS :{COMMA}{NBSP}passager{P "" s}
STR_BAGS :{COMMA}{NBSP}sac{P "" s}
STR_BAGS :{COMMA}{NBSP}sac{P 0 "" s}
STR_TONS :{COMMA}{NBSP}tonne{P "" s}
STR_LITERS :{COMMA}{NBSP}litre{P "" s}
STR_ITEMS :{COMMA}{NBSP}unité{P "" s}
@ -815,8 +815,8 @@ STR_PRESIDENT_NAME_MANAGER :{BLACK}{PRESIDE
STR_NEWS_NEW_TOWN :{BLACK}{BIG_FONT}{STRING} a sponsorisé la construction de la nouvelle ville {TOWN}{NBSP}!
STR_NEWS_INDUSTRY_CONSTRUCTION :{BIG_FONT}{BLACK}Un{G "" "" e} nouv{G eau el elle} {STRING} en construction près de {TOWN}{NBSP}!
STR_NEWS_INDUSTRY_PLANTED :{BIG_FONT}{BLACK}Un{G "" "" e} nouv{G eau el elle} {STRING} s'implante près de {TOWN}{NBSP}!
STR_NEWS_INDUSTRY_CONSTRUCTION :{BIG_FONT}{BLACK}Un{G 0 "" "" e} nouv{G 0 eau el elle} {STRING} en construction près de {TOWN}{NBSP}!
STR_NEWS_INDUSTRY_PLANTED :{BIG_FONT}{BLACK}Un{G 0 "" "" e} nouv{G 0 eau el elle} {STRING} s'implante près de {TOWN}{NBSP}!
STR_NEWS_INDUSTRY_CLOSURE_GENERAL :{BIG_FONT}{BLACK}{STRING} annonce une fermeture imminente{NBSP}!
STR_NEWS_INDUSTRY_CLOSURE_SUPPLY_PROBLEMS :{BIG_FONT}{BLACK}Des problèmes d'approvisionnement obligent {STRING} à fermer bientôt{NBSP}!
@ -858,9 +858,9 @@ STR_NEWS_AIRCRAFT_DEST_TOO_FAR :{WHITE}{VEHICLE
STR_NEWS_ORDER_REFIT_FAILED :{WHITE}L'échec du réaménagement a stoppé {VEHICLE}
STR_NEWS_VEHICLE_AUTORENEW_FAILED :{WHITE}Le renouvellement automatique de {VEHICLE} a échoué{}{STRING}
STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE :{BIG_FONT}{BLACK}Nouv{G eau el elle} {STRING} disponible{NBSP}!
STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE :{BIG_FONT}{BLACK}Nouv{G 0 eau el elle} {STRING} disponible{NBSP}!
STR_NEWS_NEW_VEHICLE_TYPE :{BIG_FONT}{BLACK}{ENGINE}
STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE :{BLACK}Nouv{G eau el elle} {STRING} disponible{NBSP}! - {ENGINE}
STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE :{BLACK}Nouv{G 0 eau el elle} {STRING} disponible{NBSP}! - {ENGINE}
STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO :{WHITE}{STATION} n'accepte plus {STRING}
STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO_OR_CARGO :{WHITE}{STATION} n'accepte plus {STRING} ou {STRING}
@ -1215,8 +1215,8 @@ STR_CONFIG_SETTING_SIGNALSIDE_RIGHT :à droite de la
STR_CONFIG_SETTING_SHOWFINANCES :Afficher le bilan financier en fin d'année{NBSP}: {STRING}
STR_CONFIG_SETTING_SHOWFINANCES_HELPTEXT :Si activé, le bilan financier sera affiché à la fin de chaque année pour permettre un contrôle rapide de l'état de la compagnie.
STR_CONFIG_SETTING_NONSTOP_BY_DEFAULT :Les nouveaux ordres sont « sans arrêt » par défaut{NBSP}: {STRING}
STR_CONFIG_SETTING_NONSTOP_BY_DEFAULT_HELPTEXT :Normalement, un véhicule s'arrête dans chaque station qu'il traverse. En activant ce paramètre, il traversera toutes les stations sur le chemin de sa destination finale sans s'arrêter. Noter que ce paramètre défini uniquement une valeur par défaut pour les nouveaux ordres. Les ordres peuvent par ailleurs être réglés individuellement avec un autre comportement
STR_CONFIG_SETTING_STOP_LOCATION :Les nouveaux ordres arrêtent les trains {G 0:2 au au "à la"} {STRING} du quai par défaut
STR_CONFIG_SETTING_NONSTOP_BY_DEFAULT_HELPTEXT :Normalement, un véhicule s'arrête dans chaque station qu'il traverse. En activant ce paramètre, il traversera toutes les stations sur le chemin de sa destination finale sans s'arrêter. Noter que ce paramètre définit uniquement une valeur par défaut pour les nouveaux ordres. Les ordres peuvent par après être réglés individuellement avec un autre comportement
STR_CONFIG_SETTING_STOP_LOCATION :Les nouveaux ordres arrêtent les trains {G 0 au au "à la"} {STRING} du quai par défaut
STR_CONFIG_SETTING_STOP_LOCATION_HELPTEXT :Endroit du quai où un train s'arrête par défaut. "queue" signifie proche du point d'entrée. "milieu" signifie au milieu du quai. "tête" signifie à l'opposé du point d'entrée. Noter que ce paramètre défini uniquement une valeur par défaut pour les nouveaux ordres. Les ordres peuvent par ailleurs être réglés individuellement avec un autre comportement
STR_CONFIG_SETTING_STOP_LOCATION_NEAR_END :{G=f}queue
STR_CONFIG_SETTING_STOP_LOCATION_MIDDLE :{G=m}milieu
@ -1407,7 +1407,7 @@ STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_FIRST :Premier disponi
STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_LAST :Dernier disponible
STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_MOST_USED :Le plus utilisé
STR_CONFIG_SETTING_SHOW_TRACK_RESERVATION :Afficher les réservations de chemin sur les voies{NBSP}: {STRING}
STR_CONFIG_SETTING_SHOW_TRACK_RESERVATION_HELPTEXT :Donne une couleur différente aux voies réservées pour aider à résoudre les problèmes de trains refusant d'entrer dans des blocs basés sur le chemin
STR_CONFIG_SETTING_SHOW_TRACK_RESERVATION_HELPTEXT :Donne une couleur différente aux voies réservées afin d'aider à résoudre les problèmes de trains refusant de s'engager dans des tronçons encadrés par des signaux de passage
STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS :Conserver les outils de construction actifs après usage{NBSP}: {STRING}
STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :Garde les outils de construction de ponts, tunnels, etc. ouverts après usage
STR_CONFIG_SETTING_EXPENSES_LAYOUT :Regrouper les dépenses dans la fenêtre des finances{NBSP}: {STRING}
@ -1464,15 +1464,15 @@ STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES_HELPTEXT :Nombre maximum
STR_CONFIG_SETTING_SERVINT_ISPERCENT :Les intervalles de service sont en pourcentage{NBSP}: {STRING}
STR_CONFIG_SETTING_SERVINT_ISPERCENT_HELPTEXT :Choisir si l'entretien des véhicule est activé par le temps passé depuis le dernier entretien ou par la fiabilité passant sous un pourcentage de la fiabilité maximum
STR_CONFIG_SETTING_SERVINT_TRAINS :Intervalle d'entretien par défaut pour les trains{NBSP}: {STRING}
STR_CONFIG_SETTING_SERVINT_TRAINS_HELPTEXT :Défini l'intervalle d'entretien par défaut des nouveaux véhicules ferroviaires, si aucun intervalle d'entretien n'est définit pour le véhicule
STR_CONFIG_SETTING_SERVINT_TRAINS_HELPTEXT :Définit l'intervalle d'entretien par défaut des nouveaux véhicules ferroviaires, si aucun intervalle d'entretien n'est défini pour le véhicule
STR_CONFIG_SETTING_SERVINT_VALUE :{COMMA}{NBSP}jour{P 0 "" s}/%
STR_CONFIG_SETTING_SERVINT_DISABLED :Désactivé
STR_CONFIG_SETTING_SERVINT_ROAD_VEHICLES :Intervalle d'entretien par défaut pour les véhicules routiers{NBSP}: {STRING}
STR_CONFIG_SETTING_SERVINT_ROAD_VEHICLES_HELPTEXT :Défini l'intervalle d'entretien par défaut des nouveaux véhicules routiers, si aucun intervalle d'entretien n'est définit pour le véhicule
STR_CONFIG_SETTING_SERVINT_ROAD_VEHICLES_HELPTEXT :Définit l'intervalle d'entretien par défaut des nouveaux véhicules routiers, si aucun intervalle d'entretien n'est défini pour le véhicule
STR_CONFIG_SETTING_SERVINT_AIRCRAFT :Intervalle d'entretien par défaut pour les aéronefs{NBSP}: {STRING}
STR_CONFIG_SETTING_SERVINT_AIRCRAFT_HELPTEXT :Défini l'intervalle d'entretien par défaut des nouveaux aéronefs, si aucun intervalle d'entretien n'est définit pour le véhicule
STR_CONFIG_SETTING_SERVINT_AIRCRAFT_HELPTEXT :Définit l'intervalle d'entretien par défaut des nouveaux aéronefs, si aucun intervalle d'entretien n'est défini pour le véhicule
STR_CONFIG_SETTING_SERVINT_SHIPS :Intervalle d'entretien par défaut pour les navires{NBSP}: {STRING}
STR_CONFIG_SETTING_SERVINT_SHIPS_HELPTEXT :Défini l'intervalle d'entretien par défaut des nouveaux navires, si aucun intervalle d'entretien n'est définit pour le véhicule
STR_CONFIG_SETTING_SERVINT_SHIPS_HELPTEXT :Définit l'intervalle d'entretien par défaut des nouveaux navires, si aucun intervalle d'entretien n'est défini pour le véhicule
STR_CONFIG_SETTING_NOSERVICE :Désactiver l'entretien quand les pannes sont inactives{NBSP}: {STRING}
STR_CONFIG_SETTING_NOSERVICE_HELPTEXT :Lorsqu'il est activé, les véhicules ne sont pas entretenus s'ils ne peuvent pas tomber en panne
STR_CONFIG_SETTING_WAGONSPEEDLIMITS :Activer la vitesse limite des wagons{NBSP}: {STRING}
@ -2221,7 +2221,7 @@ STR_CONTENT_DETAIL_SUBTITLE_SELECTED :{SILVER}Pré-s
STR_CONTENT_DETAIL_SUBTITLE_AUTOSELECTED :{SILVER}Cette dépendance a été sélectionnée pour être téléchargée
STR_CONTENT_DETAIL_SUBTITLE_ALREADY_HERE :{SILVER}Vous avez déjà ceci
STR_CONTENT_DETAIL_SUBTITLE_DOES_NOT_EXIST :{SILVER}Ce module est inconnu et ne peut pas être téléchargé dans OpenTTD
STR_CONTENT_DETAIL_UPDATE :{SILVER}Ceci est un remplacement pour {G "un" "des" "une"} {STRING} existant{G 0 "" "s" "e"}
STR_CONTENT_DETAIL_UPDATE :{SILVER}Ceci est un remplacement pour {G 0 "un" "des" "une"} {STRING} existant{G 0 "" "s" "e"}
STR_CONTENT_DETAIL_NAME :{SILVER}Nom{NBSP}: {WHITE}{STRING}
STR_CONTENT_DETAIL_VERSION :{SILVER}Version{NBSP}: {WHITE}{STRING}
STR_CONTENT_DETAIL_DESCRIPTION :{SILVER}Description{NBSP}: {WHITE}{STRING}

@ -2888,6 +2888,7 @@ STR_SPRITE_ALIGNER_PREVIOUS_BUTTON :{BLACK}Vorherig
STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP :{BLACK}Gehe zum vorherigen normalen Sprite und überspringe alle Pseudo-, Recolour- und Schriftsprites (springt ggf. vom letzten bis zum ersten Sprite)
STR_SPRITE_ALIGNER_SPRITE_TOOLTIP :{BLACK}Darstellung des aktuellen Sprites. Die Ausrichtung wird beim Zeichnen des Sprites ignoriert
STR_SPRITE_ALIGNER_MOVE_TOOLTIP :{BLACK}Bewege die Sprites und ändere dadurch die X- und Y-Offsets. Drücke Strg+Click um die Sprites 8 Blöcke weit zu bewegen.
STR_SPRITE_ALIGNER_RESET_BUTTON :{BLACK}Setze zurück
STR_SPRITE_ALIGNER_RESET_TOOLTIP :{BLACK}Setze die aktuelle Verschiebung zurück.
STR_SPRITE_ALIGNER_OFFSETS_ABS :{BLACK}X offset: {NUM}, Y offset: {NUM} (Absolut)
STR_SPRITE_ALIGNER_OFFSETS_REL :{BLACK}X offset: {NUM}, Y offset: {NUM} (Relativ)

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

@ -1402,7 +1402,7 @@ static ChangeInfoResult RoadVehicleChangeInfo(uint engine, int numinfo, int prop
break;
case 0x12: // SFX
rvi->sfx = buf->ReadByte();
rvi->sfx = GetNewGRFSoundID(_cur.grffile, buf->ReadByte());
break;
case PROP_ROADVEH_POWER: // Power in units of 10 HP.
@ -1590,7 +1590,7 @@ static ChangeInfoResult ShipVehicleChangeInfo(uint engine, int numinfo, int prop
break;
case 0x10: // SFX
svi->sfx = buf->ReadByte();
svi->sfx = GetNewGRFSoundID(_cur.grffile, buf->ReadByte());
break;
case 0x11: { // Cargoes available for refitting
@ -1758,7 +1758,7 @@ static ChangeInfoResult AircraftVehicleChangeInfo(uint engine, int numinfo, int
break;
case 0x12: // SFX
avi->sfx = buf->ReadByte();
avi->sfx = GetNewGRFSoundID(_cur.grffile, buf->ReadByte());
break;
case 0x13: { // Cargoes available for refitting

@ -197,4 +197,6 @@ StringID MapGRFStringID(uint32 grfid, StringID str);
void ShowNewGRFError();
int CountSelectedGRFs(GRFConfig *grfconf);
struct TemplateVehicle;
#endif /* NEWGRF_H */

@ -161,6 +161,22 @@ bool LoadNewGRFSound(SoundEntry *sound)
return false;
}
/**
* Resolve NewGRF sound ID.
* @param file NewGRF to get sound from.
* @param sound_id GRF-specific sound ID. (GRF-local for IDs above ORIGINAL_SAMPLE_COUNT)
* @return Translated (global) sound ID, or INVALID_SOUND.
*/
SoundID GetNewGRFSoundID(const GRFFile *file, SoundID sound_id)
{
/* Global sound? */
if (sound_id < ORIGINAL_SAMPLE_COUNT) return sound_id;
sound_id -= ORIGINAL_SAMPLE_COUNT;
if (file == NULL || sound_id >= file->num_sounds) return INVALID_SOUND;
return file->sound_offset + sound_id;
}
/**
* Checks whether a NewGRF wants to play a different vehicle sound effect.
@ -185,14 +201,10 @@ bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event)
/* Play default sound if callback fails */
if (callback == CALLBACK_FAILED) return false;
if (callback >= ORIGINAL_SAMPLE_COUNT) {
callback -= ORIGINAL_SAMPLE_COUNT;
/* Play no sound if result is out of range */
if (callback > file->num_sounds) return true;
callback = GetNewGRFSoundID(file, callback);
callback += file->sound_offset;
}
/* Play no sound, if result is invalid */
if (callback == INVALID_SOUND) return true;
assert(callback < GetNumSounds());
SndPlayVehicleFx(callback, v);
@ -207,11 +219,8 @@ bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event)
*/
void PlayTileSound(const GRFFile *file, SoundID sound_id, TileIndex tile)
{
if (sound_id >= ORIGINAL_SAMPLE_COUNT) {
sound_id -= ORIGINAL_SAMPLE_COUNT;
if (sound_id > file->num_sounds) return;
sound_id += file->sound_offset;
}
sound_id = GetNewGRFSoundID(file, sound_id);
if (sound_id == INVALID_SOUND) return;
assert(sound_id < GetNumSounds());
SndPlayTileFx(sound_id, tile);

@ -33,6 +33,7 @@ enum VehicleSoundEvent {
SoundEntry *AllocateSound(uint num);
void InitializeSoundPool();
bool LoadNewGRFSound(SoundEntry *sound);
SoundID GetNewGRFSoundID(const struct GRFFile *file, SoundID sound_id);
SoundEntry *GetSound(SoundID sound_id);
uint GetNumSounds();
bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event);

@ -1887,7 +1887,8 @@ void CheckOrders(const Vehicle *v)
if (v->FirstShared() != v) return;
/* Only check every 20 days, so that we don't flood the message log */
if (v->owner == _local_company && v->day_counter % 20 == 0) {
/* The check is skipped entirely in case the current vehicle is virtual (a.k.a a 'template train') */
if (v->owner == _local_company && v->day_counter % 20 == 0 && !HasBit(v->subtype, GVSF_VIRTUAL)) {
const Order *order;
StringID message = INVALID_STRING_ID;

@ -839,6 +839,9 @@ bool AfterLoadGame()
/* Update all vehicles */
AfterLoadVehicles(true);
/* Update template vehicles */
AfterLoadTemplateVehicles();
/* Make sure there is an AI attached to an AI company */
{
Company *c;

@ -68,6 +68,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_REVERSE_AT_WAYPOINT, XSCF_NULL, 1, 1, "reverse_at_waypoint", NULL, NULL, NULL },
{ XSLFI_VEH_LIFETIME_PROFIT, XSCF_NULL, 1, 1, "veh_lifetime_profit", NULL, NULL, NULL },
{ XSLFI_LINKGRAPH_DAY_SCALE, XSCF_NULL, 1, 1, "linkgraph_day_scale", NULL, NULL, NULL },
{ XSLFI_TEMPLATE_REPLACEMENT, XSCF_NULL, 1, 1, "template_replacement", NULL, NULL, "TRPL,TMPL" },
{ XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker
};

@ -42,6 +42,7 @@ enum SlXvFeatureIndex {
XSLFI_REVERSE_AT_WAYPOINT, ///< Reverse at waypoint orders
XSLFI_VEH_LIFETIME_PROFIT, ///< Vehicle lifetime profit patch
XSLFI_LINKGRAPH_DAY_SCALE, ///< Linkgraph job duration & interval may be in non-scaled days
XSLFI_TEMPLATE_REPLACEMENT, ///< Template-based train replacement
XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit
XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk

@ -44,6 +44,8 @@
#include "../fios.h"
#include "../error.h"
#include "../tbtr_template_vehicle.h"
#include "table/strings.h"
#include "saveload_internal.h"
@ -457,6 +459,8 @@ extern const ChunkHandler _persistent_storage_chunk_handlers[];
extern const ChunkHandler _trace_restrict_chunk_handlers[];
extern const ChunkHandler _signal_chunk_handlers[];
extern const ChunkHandler _plan_chunk_handlers[];
extern const ChunkHandler _template_replacement_chunk_handlers[];
extern const ChunkHandler _template_vehicle_chunk_handlers[];
/** Array of all chunks in a savegame, \c NULL terminated. */
static const ChunkHandler * const _chunk_handlers[] = {
@ -497,6 +501,8 @@ static const ChunkHandler * const _chunk_handlers[] = {
_trace_restrict_chunk_handlers,
_signal_chunk_handlers,
_plan_chunk_handlers,
_template_replacement_chunk_handlers,
_template_vehicle_chunk_handlers,
NULL,
};
@ -1271,6 +1277,7 @@ static size_t ReferenceToInt(const void *obj, SLRefType rt)
switch (rt) {
case REF_VEHICLE_OLD: // Old vehicles we save as new ones
case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1;
case REF_TEMPLATE_VEHICLE: return ((const TemplateVehicle*)obj)->index + 1;
case REF_STATION: return ((const Station*)obj)->index + 1;
case REF_TOWN: return ((const Town*)obj)->index + 1;
case REF_ORDER: return ((const Order*)obj)->index + 1;
@ -1330,6 +1337,10 @@ static void *IntToReference(size_t index, SLRefType rt)
if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
SlErrorCorrupt("Referencing invalid Vehicle");
case REF_TEMPLATE_VEHICLE:
if (TemplateVehicle::IsValidID(index)) return TemplateVehicle::Get(index);
SlErrorCorrupt("Referencing invalid TemplateVehicle");
case REF_STATION:
if (Station::IsValidID(index)) return Station::Get(index);
SlErrorCorrupt("Referencing invalid Station");

@ -88,6 +88,7 @@ enum SLRefType {
REF_STORAGE = 9, ///< Load/save a reference to a persistent storage.
REF_LINK_GRAPH = 10, ///< Load/save a reference to a link graph.
REF_LINK_GRAPH_JOB = 11, ///< Load/save a reference to a link graph job.
REF_TEMPLATE_VEHICLE = 12, ///< Load/save a reference to a template vehicle
};
/** Highest possible savegame version. */

@ -28,6 +28,7 @@ const SaveLoad *GetBaseStationDescription();
void AfterLoadVehicles(bool part_of_load);
void FixupTrainLengths();
void AfterLoadTemplateVehicles();
void AfterLoadStations();
void AfterLoadRoadStops();
void AfterLoadLabelMaps();

@ -0,0 +1,35 @@
#include "../stdafx.h"
#include "../tbtr_template_vehicle.h"
#include "saveload.h"
static const SaveLoad _template_replacement_desc[] = {
SLE_VAR(TemplateReplacement, sel_template, SLE_UINT16),
SLE_VAR(TemplateReplacement, group, SLE_UINT16),
SLE_END()
};
static void Save_TMPL_RPLS()
{
TemplateReplacement *tr;
FOR_ALL_TEMPLATE_REPLACEMENTS(tr) {
SlSetArrayIndex(tr->index);
SlObject(tr, _template_replacement_desc);
}
}
static void Load_TMPL_RPLS()
{
int index;
while ((index = SlIterateArray()) != -1) {
TemplateReplacement *tr = new (index) TemplateReplacement();
SlObject(tr, _template_replacement_desc);
}
}
extern const ChunkHandler _template_replacement_chunk_handlers[] = {
{'TRPL', Save_TMPL_RPLS, Load_TMPL_RPLS, NULL, NULL, CH_ARRAY | CH_LAST},
};

@ -0,0 +1,99 @@
#include "../stdafx.h"
#include "../tbtr_template_vehicle.h"
#include "saveload.h"
const SaveLoad* GTD() {
static const SaveLoad _template_veh_desc[] = {
SLE_REF(TemplateVehicle, next, REF_TEMPLATE_VEHICLE),
SLE_VAR(TemplateVehicle, reuse_depot_vehicles, SLE_UINT8),
SLE_VAR(TemplateVehicle, keep_remaining_vehicles, SLE_UINT8),
SLE_VAR(TemplateVehicle, refit_as_template, SLE_UINT8),
SLE_VAR(TemplateVehicle, owner, SLE_UINT32),
SLE_VAR(TemplateVehicle, owner_b, SLE_UINT8),
SLE_VAR(TemplateVehicle, engine_type, SLE_UINT16),
SLE_VAR(TemplateVehicle, cargo_type, SLE_UINT8),
SLE_VAR(TemplateVehicle, cargo_cap, SLE_UINT16),
SLE_VAR(TemplateVehicle, cargo_subtype, SLE_UINT8),
SLE_VAR(TemplateVehicle, subtype, SLE_UINT8),
SLE_VAR(TemplateVehicle, railtype, SLE_UINT8),
SLE_VAR(TemplateVehicle, index, SLE_UINT32),
SLE_VAR(TemplateVehicle, real_consist_length, SLE_UINT16),
SLE_VAR(TemplateVehicle, max_speed, SLE_UINT16),
SLE_VAR(TemplateVehicle, power, SLE_UINT32),
SLE_VAR(TemplateVehicle, weight, SLE_UINT32),
SLE_VAR(TemplateVehicle, max_te, SLE_UINT32),
SLE_VAR(TemplateVehicle, spritenum, SLE_UINT8),
SLE_VAR(TemplateVehicle, cur_image, SLE_UINT32),
SLE_VAR(TemplateVehicle, image_width, SLE_UINT32),
SLE_END()
};
static const SaveLoad * const _ret[] = {
_template_veh_desc,
};
return _ret[0];
}
static void Save_TMPLS()
{
TemplateVehicle *tv;
FOR_ALL_TEMPLATES(tv) {
SlSetArrayIndex(tv->index);
SlObject(tv, GTD());
}
}
static void Load_TMPLS()
{
int index;
while ((index = SlIterateArray()) != -1) {
TemplateVehicle *tv = new (index) TemplateVehicle(); //TODO:check with veh sl code
SlObject(tv, GTD());
}
}
static void Ptrs_TMPLS()
{
TemplateVehicle *tv;
FOR_ALL_TEMPLATES(tv) {
SlObject(tv, GTD());
}
}
void AfterLoadTemplateVehicles()
{
TemplateVehicle *tv;
FOR_ALL_TEMPLATES(tv) {
/* Reinstate the previous pointer */
if (tv->next != NULL) tv->next->previous = tv;
tv->first =NULL;
}
FOR_ALL_TEMPLATES(tv) {
/* Fill the first pointers */
if (tv->previous == NULL) {
for (TemplateVehicle *u = tv; u != NULL; u = u->Next()) {
u->first = tv;
}
}
}
}
extern const ChunkHandler _template_vehicle_chunk_handlers[] = {
{'TMPL', Save_TMPLS, Load_TMPLS, Ptrs_TMPLS, NULL, CH_ARRAY | CH_LAST},
};

@ -119,4 +119,6 @@ static const uint ORIGINAL_SAMPLE_COUNT = 73;
typedef uint16 SoundID;
static const SoundID INVALID_SOUND = 0xFFFF;
#endif /* SOUND_TYPE_H */

@ -0,0 +1,577 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file tbtr_template_gui_create.cpp Template-based train replacement: template creation GUI. */
#include "stdafx.h"
#include "gfx_func.h"
#include "direction_type.h"
#include "strings_func.h"
#include "window_func.h"
#include "company_func.h"
#include "window_gui.h"
#include "settings_func.h"
#include "core/geometry_func.hpp"
#include "table/sprites.h"
#include "table/strings.h"
#include "viewport_func.h"
#include "window_func.h"
#include "gui.h"
#include "textbuf_gui.h"
#include "command_func.h"
#include "depot_base.h"
#include "vehicle_gui.h"
#include "spritecache.h"
#include "strings_func.h"
#include "window_func.h"
#include "vehicle_func.h"
#include "company_func.h"
#include "tilehighlight_func.h"
#include "window_gui.h"
#include "vehiclelist.h"
#include "order_backup.h"
#include "group.h"
#include "company_base.h"
#include "train.h"
#include "tbtr_template_gui_create.h"
#include "tbtr_template_vehicle.h"
#include "tbtr_template_vehicle_func.h"
#include "safeguards.h"
class TemplateReplaceWindow;
// some space in front of the virtual train in the matrix
uint16 TRAIN_FRONT_SPACE = 16;
enum TemplateReplaceWindowWidgets {
TCW_CAPTION,
TCW_NEW_TMPL_PANEL,
TCW_INFO_PANEL,
TCW_SCROLLBAR_H_NEW_TMPL,
TCW_SCROLLBAR_V_NEW_TMPL,
TCW_SELL_TMPL,
TCW_NEW,
TCW_OK,
TCW_CANCEL,
TCW_REFIT,
TCW_CLONE,
};
static const NWidgetPart _widgets[] = {
NWidget(NWID_HORIZONTAL),
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_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), 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),
EndContainer(),
};
static WindowDesc _template_create_window_desc(
WDP_AUTO, // window position
"template create window", // const char* ini_key
456, 100, // window size
WC_CREATE_TEMPLATE, // window class
WC_TEMPLATEGUI_MAIN, // parent window class
WDF_CONSTRUCTION, // window flags
_widgets, lengthof(_widgets) // widgets + num widgets
);
static void TrainDepotMoveVehicle(const Vehicle *wagon, VehicleID sel, const Vehicle *head)
{
const Vehicle *v = Vehicle::Get(sel);
if (v == wagon) return;
if (wagon == NULL) {
if (head != NULL) wagon = head->Last();
} else {
wagon = wagon->Previous();
if (wagon == NULL) return;
}
if (wagon == v) return;
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;
bool *noticeParent;
bool *createWindowOpen; /// used to notify main window of progress (dummy way of disabling 'delete' while editing a template)
bool virtualTrainChangedNotice;
VehicleID sel;
VehicleID vehicle_over;
TemplateVehicle *editTemplate;
public:
TemplateCreateWindow(WindowDesc* _wdesc, TemplateVehicle *to_edit, bool *notice, bool *windowOpen, int step_h) : Window(_wdesc)
{
this->line_height = step_h;
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;
this->owner = _local_company;
noticeParent = notice;
createWindowOpen = windowOpen;
virtualTrainChangedNotice = false;
this->editTemplate = to_edit;
editMode = (to_edit != NULL);
this->sel = INVALID_VEHICLE;
this->vehicle_over = INVALID_VEHICLE;
if (to_edit) {
DoCommandP(0, to_edit->index, 0, CMD_VIRTUAL_TRAIN_FROM_TEMPLATE_VEHICLE, CcSetVirtualTrain);
}
this->resize.step_height = 1;
UpdateButtonState();
}
~TemplateCreateWindow()
{
if (virtual_train != NULL) {
DoCommandP(0, virtual_train->index, 0, CMD_DELETE_VIRTUAL_TRAIN);
virtual_train = NULL;
}
SetWindowClassesDirty(WC_TRAINS_LIST);
/* more cleanup */
*createWindowOpen = false;
DeleteWindowById(WC_BUILD_VIRTUAL_TRAIN, this->window_number);
}
void SetVirtualTrain(Train* const train)
{
if (virtual_train != NULL) {
DoCommandP(0, virtual_train->index, 0, CMD_DELETE_VIRTUAL_TRAIN);
}
virtual_train = train;
UpdateButtonState();
}
virtual void OnResize()
{
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);
}
virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
{
virtualTrainChangedNotice = true;
UpdateButtonState();
}
virtual void OnClick(Point pt, int widget, int click_count)
{
switch(widget) {
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: {
ShowBuildVirtualTrainWindow(&virtual_train, &virtualTrainChangedNotice);
break;
}
case TCW_CLONE: {
this->SetWidgetDirty(TCW_CLONE);
this->ToggleWidgetLoweredState(TCW_CLONE);
if (this->IsWidgetLowered(TCW_CLONE)) {
SetObjectToPlaceWnd(SPR_CURSOR_CLONE_TRAIN, PAL_NONE, HT_VEHICLE, this);
} else {
ResetObjectToPlace();
}
break;
}
case TCW_OK: {
uint32 templateIndex = (editTemplate != NULL) ? editTemplate->index : INVALID_VEHICLE;
if (virtual_train != NULL) {
DoCommandP(0, templateIndex, virtual_train->index, CMD_REPLACE_TEMPLATE_VEHICLE);
virtual_train = NULL;
} else if (templateIndex != INVALID_VEHICLE) {
DoCommandP(0, templateIndex, 0, CMD_DELETE_TEMPLATE_VEHICLE);
}
delete this;
break;
}
case TCW_CANCEL: {
delete this;
break;
}
case TCW_REFIT: {
if (virtual_train != NULL) {
ShowVehicleRefitWindow(virtual_train, INVALID_VEH_ORDER_ID, this, false, true);
}
break;
}
}
}
virtual bool OnVehicleSelect(const Vehicle *v)
{
// throw away the current virtual train
if (virtual_train != NULL) {
DoCommandP(0, virtual_train->index, 0, CMD_DELETE_VIRTUAL_TRAIN);
virtual_train = NULL;
}
// create a new one
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_NEW_TMPL_PANEL: {
if (this->virtual_train) {
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);
}
break;
}
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(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 != NULL; tmp = tmp->Next()) {
cargo_caps[tmp->cargo_type] += tmp->cargo_cap;
}
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]);
DrawString(8, r.right, y, STR_TMPL_CARGO_SUMMARY, TC_LIGHT_BLUE, SA_LEFT);
y += this->line_height / 3;
}
}
_cur_dpi = old_dpi;
}
break;
}
default:
break;
}
}
virtual void OnTick()
{
if (virtualTrainChangedNotice) {
this->SetDirty();
virtualTrainChangedNotice = false;
UpdateButtonState();
}
}
virtual void OnDragDrop(Point pt, int widget)
{
switch (widget) {
case TCW_NEW_TMPL_PANEL: {
const Vehicle *v = NULL;
VehicleID sel = this->sel;
this->sel = INVALID_VEHICLE;
this->SetDirty();
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);
}
}
break;
}
case TCW_SELL_TMPL: {
if (this->IsWidgetDisabled(widget)) return;
if (this->sel == INVALID_VEHICLE) return;
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) ? NULL : virtual_train->GetNextUnit();
}
DoCommandP(0, this->sel | (sell_cmd << 20) | (1 << 21), 0, GetCmdSellVeh(VEH_TRAIN));
this->sel = INVALID_VEHICLE;
this->SetDirty();
UpdateButtonState();
break;
}
default:
this->sel = INVALID_VEHICLE;
this->SetDirty();
break;
}
_cursor.vehchain = false;
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_NEW_TMPL_PANEL) { // ..outside of the depot matrix.
if (this->vehicle_over != INVALID_VEHICLE) {
this->vehicle_over = INVALID_VEHICLE;
this->SetWidgetDirty(TCW_NEW_TMPL_PANEL);
}
return;
}
NWidgetBase *matrix = this->GetWidget<NWidgetBase>(widget);
const Vehicle *v = NULL;
GetDepotVehiclePtData gdvp = {NULL, NULL};
if (this->GetVehicleFromDepotWndPt(pt.x - matrix->pos_x, pt.y - matrix->pos_y, &v, &gdvp) != MODE_DRAG_VEHICLE) return;
VehicleID new_vehicle_over = INVALID_VEHICLE;
if (gdvp.head != NULL) {
if (gdvp.wagon == NULL && gdvp.head->Last()->index != this->sel) { // ..at the end of the train.
/* NOTE: As a wagon can't be moved at the begin of a train, head index isn't used to mark a drag-and-drop
* destination inside a train. This head index is then used to indicate that a wagon is inserted at
* the end of the train.
*/
new_vehicle_over = gdvp.head->index;
} else if (gdvp.wagon != NULL && gdvp.head != gdvp.wagon &&
gdvp.wagon->index != this->sel &&
gdvp.wagon->Previous()->index != this->sel) { // ..over an existing wagon.
new_vehicle_over = gdvp.wagon->index;
}
}
if (this->vehicle_over == new_vehicle_over) return;
this->vehicle_over = new_vehicle_over;
this->SetWidgetDirty(widget);
}
virtual void OnPaint()
{
uint min_width = 32;
uint min_height = 30;
uint width = 0;
uint height = 30;
CargoArray cargo_caps;
if (virtual_train != NULL) {
for (Train *train = virtual_train; train != NULL; 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();
}
struct GetDepotVehiclePtData {
const Vehicle *head;
const Vehicle *wagon;
};
enum DepotGUIAction {
MODE_ERROR,
MODE_DRAG_VEHICLE,
MODE_SHOW_VEHICLE,
MODE_START_STOP,
};
uint count_width;
uint header_width;
DepotGUIAction GetVehicleFromDepotWndPt(int x, int y, const Vehicle **veh, GetDepotVehiclePtData *d) const
{
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;
bool wagon = false;
x += this->hscroll->GetPosition();
const Train *v = virtual_train;
d->head = d->wagon = v;
if (xm <= this->header_width) {
if (wagon) return MODE_ERROR;
return MODE_SHOW_VEHICLE;
}
/* Account for the header */
x -= this->header_width;
/* find the vehicle in this row that was clicked */
for (; v != NULL; v = v->Next()) {
x -= v->GetDisplayImageWidth();
if (x < 0) break;
}
d->wagon = (v != NULL ? v->GetFirstEnginePart() : NULL);
return MODE_DRAG_VEHICLE;
}
void ClickedOnVehiclePanel(int x, int y)
{
GetDepotVehiclePtData gdvp = { NULL, NULL };
const Vehicle *v = NULL;
this->GetVehicleFromDepotWndPt(x, y, &v, &gdvp);
v = gdvp.wagon;
if (v != NULL && VehicleClicked(v)) return;
VehicleID sel = this->sel;
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);
this->sel = v->index;
this->SetDirty();
_cursor.short_vehicle_offset = v->IsGroundVehicle() ? 16 - v->GetGroundVehicleCache()->cached_veh_length * 2 : 0;
_cursor.vehchain = _ctrl_pressed;
}
}
void RearrangeVirtualTrain()
{
virtual_train = virtual_train->First();
}
void UpdateButtonState()
{
this->SetWidgetDisabledState(TCW_REFIT, virtual_train == NULL);
}
};
void ShowTemplateCreateWindow(TemplateVehicle *to_edit, bool *noticeParent, bool *createWindowOpen, int step_h)
{
if (BringWindowToFrontById(WC_CREATE_TEMPLATE, VEH_TRAIN) != NULL) return;
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);
}

@ -0,0 +1,20 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file tbtr_template_gui_create.h Template-based train replacement: template creation GUI header. */
#ifndef TEMPLATE_GUI_CREATE
#define TEMPLATE_GUI_CREATE
#include "tbtr_template_vehicle.h"
#include "tbtr_template_gui_create_virtualtrain.h"
void ShowTemplateCreateWindow(TemplateVehicle*, bool*, bool*, int);
#endif

@ -0,0 +1,837 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file tbtr_template_gui_create_virtualtrain.cpp Template-based train replacement: template creation vehicle build GUI. */
#include "stdafx.h"
#include "engine_base.h"
#include "engine_func.h"
#include "station_base.h"
#include "articulated_vehicles.h"
#include "textbuf_gui.h"
#include "command_func.h"
#include "company_func.h"
#include "vehicle_gui.h"
#include "newgrf_engine.h"
#include "newgrf_text.h"
#include "group.h"
#include "string_func.h"
#include "strings_func.h"
#include "window_func.h"
#include "date_func.h"
#include "vehicle_func.h"
#include "widgets/dropdown_func.h"
#include "engine_gui.h"
#include "cargotype.h"
#include "core/geometry_func.hpp"
#include "vehicle_gui.h"
#include "tbtr_template_gui_create_virtualtrain.h"
#include "widgets/build_vehicle_widget.h"
#include "table/strings.h"
#include "safeguards.h"
static const NWidgetPart _nested_build_vehicle_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, WID_BV_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_SHADEBOX, COLOUR_GREY),
NWidget(WWT_STICKYBOX, COLOUR_GREY),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY),
NWidget(NWID_HORIZONTAL),
NWidget(NWID_VERTICAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0),
NWidget(NWID_SPACER), SetFill(1, 1),
EndContainer(),
NWidget(NWID_VERTICAL),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_CARGO_FILTER_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_FILTER_CRITERIA),
EndContainer(),
EndContainer(),
EndContainer(),
/* Vehicle list. */
NWidget(NWID_HORIZONTAL),
NWidget(WWT_MATRIX, COLOUR_GREY, WID_BV_LIST), SetResize(1, 1), SetFill(1, 0), SetDataTip(0x101, STR_NULL), SetScrollbar(WID_BV_SCROLLBAR),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BV_SCROLLBAR),
EndContainer(),
/* Panel with details. */
NWidget(WWT_PANEL, COLOUR_GREY, WID_BV_PANEL), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
/* Build/rename buttons, resize button. */
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_BUILD), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_TMPL_CONFIRM, STR_TMPL_CONFIRM),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_RENAME), SetResize(1, 0), SetFill(1, 0),
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
EndContainer(),
};
/** Special cargo filter criteria */
static const CargoID CF_ANY = CT_NO_REFIT; ///< Show all vehicles independent of carried cargo (i.e. no filtering)
static const CargoID CF_NONE = CT_INVALID; ///< Show only vehicles which do not carry cargo (e.g. train engines)
static bool _internal_sort_order; ///< false = descending, true = ascending
static byte _last_sort_criteria[] = {0, 0, 0, 0};
static bool _last_sort_order[] = {false, false, false, false};
static CargoID _last_filter_criteria[] = {CF_ANY, CF_ANY, CF_ANY, CF_ANY};
/**
* Determines order of engines by engineID
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineNumberSorter(const EngineID *a, const EngineID *b)
{
int r = Engine::Get(*a)->list_position - Engine::Get(*b)->list_position;
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by introduction date
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineIntroDateSorter(const EngineID *a, const EngineID *b)
{
const int va = Engine::Get(*a)->intro_date;
const int vb = Engine::Get(*b)->intro_date;
const int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by name
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineNameSorter(const EngineID *a, const EngineID *b)
{
static EngineID last_engine[2] = { INVALID_ENGINE, INVALID_ENGINE };
static char last_name[2][64] = { "\0", "\0" };
const EngineID va = *a;
const EngineID vb = *b;
if (va != last_engine[0]) {
last_engine[0] = va;
SetDParam(0, va);
GetString(last_name[0], STR_ENGINE_NAME, lastof(last_name[0]));
}
if (vb != last_engine[1]) {
last_engine[1] = vb;
SetDParam(0, vb);
GetString(last_name[1], STR_ENGINE_NAME, lastof(last_name[1]));
}
int r = strnatcmp(last_name[0], last_name[1]); // Sort by name (natural sorting).
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by reliability
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineReliabilitySorter(const EngineID *a, const EngineID *b)
{
const int va = Engine::Get(*a)->reliability;
const int vb = Engine::Get(*b)->reliability;
const int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by purchase cost
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineCostSorter(const EngineID *a, const EngineID *b)
{
Money va = Engine::Get(*a)->GetCost();
Money vb = Engine::Get(*b)->GetCost();
int r = ClampToI32(va - vb);
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by speed
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineSpeedSorter(const EngineID *a, const EngineID *b)
{
int va = Engine::Get(*a)->GetDisplayMaxSpeed();
int vb = Engine::Get(*b)->GetDisplayMaxSpeed();
int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by power
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EnginePowerSorter(const EngineID *a, const EngineID *b)
{
int va = Engine::Get(*a)->GetPower();
int vb = Engine::Get(*b)->GetPower();
int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by tractive effort
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineTractiveEffortSorter(const EngineID *a, const EngineID *b)
{
int va = Engine::Get(*a)->GetDisplayMaxTractiveEffort();
int vb = Engine::Get(*b)->GetDisplayMaxTractiveEffort();
int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by running costs
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineRunningCostSorter(const EngineID *a, const EngineID *b)
{
Money va = Engine::Get(*a)->GetRunningCost();
Money vb = Engine::Get(*b)->GetRunningCost();
int r = ClampToI32(va - vb);
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by running costs
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EnginePowerVsRunningCostSorter(const EngineID *a, const EngineID *b)
{
const Engine *e_a = Engine::Get(*a);
const Engine *e_b = Engine::Get(*b);
/* Here we are using a few tricks to get the right sort.
* We want power/running cost, but since we usually got higher running cost than power and we store the result in an int,
* we will actually calculate cunning cost/power (to make it more than 1).
* Because of this, the return value have to be reversed as well and we return b - a instead of a - b.
* Another thing is that both power and running costs should be doubled for multiheaded engines.
* Since it would be multipling with 2 in both numerator and denumerator, it will even themselves out and we skip checking for multiheaded. */
Money va = (e_a->GetRunningCost()) / max(1U, (uint)e_a->GetPower());
Money vb = (e_b->GetRunningCost()) / max(1U, (uint)e_b->GetPower());
int r = ClampToI32(vb - va);
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/* Train sorting functions */
/**
* Determines order of train engines by capacity
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL TrainEngineCapacitySorter(const EngineID *a, const EngineID *b)
{
const RailVehicleInfo *rvi_a = RailVehInfo(*a);
const RailVehicleInfo *rvi_b = RailVehInfo(*b);
int va = GetTotalCapacityOfArticulatedParts(*a) * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
int vb = GetTotalCapacityOfArticulatedParts(*b) * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of train engines by engine / wagon
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL TrainEnginesThenWagonsSorter(const EngineID *a, const EngineID *b)
{
int val_a = (RailVehInfo(*a)->railveh_type == RAILVEH_WAGON ? 1 : 0);
int val_b = (RailVehInfo(*b)->railveh_type == RAILVEH_WAGON ? 1 : 0);
int r = val_a - val_b;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/** Sort functions for the vehicle sort criteria, for each vehicle type. */
static EngList_SortTypeFunction * const _sorter[][11] = {{
/* Trains */
&EngineNumberSorter,
&EngineCostSorter,
&EngineSpeedSorter,
&EnginePowerSorter,
&EngineTractiveEffortSorter,
&EngineIntroDateSorter,
&EngineNameSorter,
&EngineRunningCostSorter,
&EnginePowerVsRunningCostSorter,
&EngineReliabilitySorter,
&TrainEngineCapacitySorter,
}};
static const StringID _sort_listing[][12] = {{
/* Trains */
STR_SORT_BY_ENGINE_ID,
STR_SORT_BY_COST,
STR_SORT_BY_MAX_SPEED,
STR_SORT_BY_POWER,
STR_SORT_BY_TRACTIVE_EFFORT,
STR_SORT_BY_INTRO_DATE,
STR_SORT_BY_NAME,
STR_SORT_BY_RUNNING_COST,
STR_SORT_BY_POWER_VS_RUNNING_COST,
STR_SORT_BY_RELIABILITY,
STR_SORT_BY_CARGO_CAPACITY,
INVALID_STRING_ID
}};
/** Cargo filter functions */
static bool CDECL CargoFilter(const EngineID *eid, const CargoID cid)
{
if (cid == CF_ANY) return true;
uint32 refit_mask = GetUnionOfArticulatedRefitMasks(*eid, true);
return (cid == CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cid));
}
static GUIEngineList::FilterFunction * const _filter_funcs[] = {
&CargoFilter,
};
/**
* Engine drawing loop
* @param type Type of vehicle (VEH_*)
* @param l The left most location of the list
* @param r The right most location of the list
* @param y The top most location of the list
* @param eng_list What engines to draw
* @param min where to start in the list
* @param max where in the list to end
* @param selected_id what engine to highlight as selected, if any
* @param show_count Whether to show the amount of engines or not
* @param selected_group the group to list the engines of
*/
static void DrawEngineList(VehicleType type, int l, int r, int y, const GUIEngineList *eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group)
{
static const int sprite_widths[] = { 60, 60, 76, 67 };
static const int sprite_y_offsets[] = { -1, -1, -2, -2 };
/* Obligatory sanity checks! */
assert((uint)type < lengthof(sprite_widths));
assert_compile(lengthof(sprite_y_offsets) == lengthof(sprite_widths));
assert(max <= eng_list->Length());
bool rtl = _current_text_dir == TD_RTL;
int step_size = GetEngineListHeight(type);
int sprite_width = sprite_widths[type];
int sprite_x = (rtl ? r - sprite_width / 2 : l + sprite_width / 2) - 1;
int sprite_y_offset = sprite_y_offsets[type] + step_size / 2;
int text_left = l + (rtl ? WD_FRAMERECT_LEFT : sprite_width);
int text_right = r - (rtl ? sprite_width : WD_FRAMERECT_RIGHT);
int normal_text_y_offset = (step_size - FONT_HEIGHT_NORMAL) / 2;
int small_text_y_offset = step_size - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 1;
for (; min < max; min++, y += step_size) {
const EngineID engine = (*eng_list)[min];
/* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
const uint num_engines = GetGroupNumEngines(_local_company, selected_group, engine);
SetDParam(0, engine);
DrawString(text_left, text_right, y + normal_text_y_offset, STR_ENGINE_NAME, engine == selected_id ? TC_WHITE : TC_BLACK);
DrawVehicleEngine(l, r, sprite_x, y + sprite_y_offset, engine, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_company), EIT_PURCHASE);
if (show_count) {
SetDParam(0, num_engines);
DrawString(text_left, text_right, y + small_text_y_offset, STR_TINY_BLACK_COMA, TC_FROMSTRING, SA_RIGHT);
}
}
}
struct BuildVirtualTrainWindow : Window {
VehicleType vehicle_type;
union {
RailTypeByte railtype;
RoadTypes roadtypes;
} filter;
bool descending_sort_order;
byte sort_criteria;
bool listview_mode;
EngineID sel_engine;
EngineID rename_engine;
GUIEngineList eng_list;
CargoID cargo_filter[NUM_CARGO + 2]; ///< Available cargo filters; CargoID or CF_ANY or CF_NONE
StringID cargo_filter_texts[NUM_CARGO + 3]; ///< Texts for filter_cargo, terminated by INVALID_STRING_ID
byte cargo_filter_criteria; ///< Selected cargo filter
int details_height; ///< Minimal needed height of the details panels (found so far).
Scrollbar *vscroll;
Train **virtual_train; ///< the virtual train that is currently being created
bool *noticeParent;
BuildVirtualTrainWindow(WindowDesc *desc, Train **vt, bool *notice) : Window(desc)
{
this->vehicle_type = VEH_TRAIN;
this->window_number = 0;
this->sel_engine = INVALID_ENGINE;
this->sort_criteria = _last_sort_criteria[VEH_TRAIN];
this->descending_sort_order = _last_sort_order[VEH_TRAIN];
this->filter.railtype = RAILTYPE_END;
this->listview_mode = (this->window_number <= VEH_END);
this->CreateNestedTree(desc);
this->vscroll = this->GetScrollbar(WID_BV_SCROLLBAR);
NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_BV_LIST);
widget = this->GetWidget<NWidgetCore>(WID_BV_BUILD);
widget = this->GetWidget<NWidgetCore>(WID_BV_RENAME);
widget->widget_data = STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON + VEH_TRAIN;
widget->tool_tip = STR_BUY_VEHICLE_TRAIN_RENAME_TOOLTIP + VEH_TRAIN;
this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
this->FinishInitNested(VEH_TRAIN);
this->owner = _local_company;
this->eng_list.ForceRebuild();
this->GenerateBuildList();
if (this->eng_list.Length() > 0) this->sel_engine = this->eng_list[0];
this->virtual_train = vt;
this->noticeParent = notice;
}
/** Populate the filter list and set the cargo filter criteria. */
void SetCargoFilterArray()
{
uint filter_items = 0;
/* Add item for disabling filtering. */
this->cargo_filter[filter_items] = CF_ANY;
this->cargo_filter_texts[filter_items] = STR_PURCHASE_INFO_ALL_TYPES;
filter_items++;
/* Add item for vehicles not carrying anything, e.g. train engines.
* This could also be useful for eyecandy vehicles of other types, but is likely too confusing for joe, */
if (this->vehicle_type == VEH_TRAIN) {
this->cargo_filter[filter_items] = CF_NONE;
this->cargo_filter_texts[filter_items] = STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY_NONE;
filter_items++;
}
/* Collect available cargo types for filtering. */
const CargoSpec *cs;
FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
this->cargo_filter[filter_items] = cs->Index();
this->cargo_filter_texts[filter_items] = cs->name;
filter_items++;
}
/* Terminate the filter list. */
this->cargo_filter_texts[filter_items] = INVALID_STRING_ID;
/* If not found, the cargo criteria will be set to all cargoes. */
this->cargo_filter_criteria = 0;
/* Find the last cargo filter criteria. */
for (uint i = 0; i < filter_items; ++i) {
if (this->cargo_filter[i] == _last_filter_criteria[this->vehicle_type]) {
this->cargo_filter_criteria = i;
break;
}
}
this->eng_list.SetFilterFuncs(_filter_funcs);
this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
}
void OnInit()
{
this->SetCargoFilterArray();
}
/** Filter the engine list against the currently selected cargo filter */
void FilterEngineList()
{
this->eng_list.Filter(this->cargo_filter[this->cargo_filter_criteria]);
if (0 == this->eng_list.Length()) { // no engine passed through the filter, invalidate the previously selected engine
this->sel_engine = INVALID_ENGINE;
} else if (!this->eng_list.Contains(this->sel_engine)) { // previously selected engine didn't pass the filter, select the first engine of the list
this->sel_engine = this->eng_list[0];
}
}
/** Filter a single engine */
bool FilterSingleEngine(EngineID eid)
{
CargoID filter_type = this->cargo_filter[this->cargo_filter_criteria];
return (filter_type == CF_ANY || CargoFilter(&eid, filter_type));
}
/* Figure out what train EngineIDs to put in the list */
void GenerateBuildTrainList()
{
EngineID sel_id = INVALID_ENGINE;
int num_engines = 0;
int num_wagons = 0;
this->filter.railtype = (this->listview_mode) ? RAILTYPE_END : GetRailType(this->window_number);
this->eng_list.Clear();
/* Make list of all available train engines and wagons.
* Also check to see if the previously selected engine is still available,
* and if not, reset selection to INVALID_ENGINE. This could be the case
* when engines become obsolete and are removed */
const Engine *e;
FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
EngineID eid = e->index;
const RailVehicleInfo *rvi = &e->u.rail;
if (this->filter.railtype != RAILTYPE_END && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue;
if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
/* Filter now! So num_engines and num_wagons is valid */
if (!FilterSingleEngine(eid)) continue;
*this->eng_list.Append() = eid;
if (rvi->railveh_type != RAILVEH_WAGON) {
num_engines++;
} else {
num_wagons++;
}
if (eid == this->sel_engine) sel_id = eid;
}
this->sel_engine = sel_id;
/* make engines first, and then wagons, sorted by ListPositionOfEngine() */
_internal_sort_order = false;
EngList_Sort(&this->eng_list, TrainEnginesThenWagonsSorter);
/* and then sort engines */
_internal_sort_order = this->descending_sort_order;
EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], 0, num_engines);
/* and finally sort wagons */
EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], num_engines, num_wagons);
}
/* Generate the list of vehicles */
void GenerateBuildList()
{
if (!this->eng_list.NeedRebuild()) return;
this->GenerateBuildTrainList();
this->eng_list.Compact();
this->eng_list.RebuildDone();
return; // trains should not reach the last sorting
this->FilterEngineList();
_internal_sort_order = this->descending_sort_order;
EngList_Sort(&this->eng_list, _sorter[this->vehicle_type][this->sort_criteria]);
this->eng_list.Compact();
this->eng_list.RebuildDone();
}
virtual void OnClick(Point pt, int widget, int click_count)
{
switch (widget) {
case WID_BV_SORT_ASCENDING_DESCENDING:
this->descending_sort_order ^= true;
_last_sort_order[this->vehicle_type] = this->descending_sort_order;
this->eng_list.ForceRebuild();
this->SetDirty();
break;
case WID_BV_LIST: {
uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BV_LIST);
size_t num_items = this->eng_list.Length();
this->sel_engine = (i < num_items) ? this->eng_list[i] : INVALID_ENGINE;
this->SetDirty();
if (click_count > 1 && !this->listview_mode) this->OnClick(pt, WID_BV_BUILD, 1);
break;
}
case WID_BV_SORT_DROPDOWN: { // Select sorting criteria dropdown menu
uint32 hidden_mask = 0;
/* Disable sorting by power or tractive effort when the original acceleration model for road vehicles is being used. */
if (this->vehicle_type == VEH_ROAD &&
_settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL) {
SetBit(hidden_mask, 3); // power
SetBit(hidden_mask, 4); // tractive effort
SetBit(hidden_mask, 8); // power by running costs
}
/* Disable sorting by tractive effort when the original acceleration model for trains is being used. */
if (this->vehicle_type == VEH_TRAIN &&
_settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
SetBit(hidden_mask, 4); // tractive effort
}
ShowDropDownMenu(this, _sort_listing[this->vehicle_type], this->sort_criteria, WID_BV_SORT_DROPDOWN, 0, hidden_mask);
break;
}
case WID_BV_CARGO_FILTER_DROPDOWN: // Select cargo filtering criteria dropdown menu
ShowDropDownMenu(this, this->cargo_filter_texts, this->cargo_filter_criteria, WID_BV_CARGO_FILTER_DROPDOWN, 0, 0);
break;
case WID_BV_BUILD: {
EngineID sel_eng = this->sel_engine;
if (sel_eng != INVALID_ENGINE) {
DoCommandP(0, sel_engine, 0, CMD_BUILD_VIRTUAL_RAIL_VEHICLE, CcAddVirtualEngine);
}
break;
}
}
}
/**
* Some data on this window has become invalid.
* @param data Information about the changed data.
* @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
*/
virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
{
if (!gui_scope) return;
/* When switching to original acceleration model for road vehicles, clear the selected sort criteria if it is not available now. */
this->eng_list.ForceRebuild();
}
virtual void SetStringParameters(int widget) const
{
switch (widget) {
case WID_BV_CAPTION:
if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) {
const RailtypeInfo *rti = GetRailTypeInfo(this->filter.railtype);
SetDParam(0, rti->strings.build_caption);
} else {
SetDParam(0, (this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type);
}
break;
case WID_BV_SORT_DROPDOWN:
SetDParam(0, _sort_listing[this->vehicle_type][this->sort_criteria]);
break;
case WID_BV_CARGO_FILTER_DROPDOWN:
SetDParam(0, this->cargo_filter_texts[this->cargo_filter_criteria]);
break;
}
}
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
{
switch (widget) {
case WID_BV_LIST:
resize->height = GetEngineListHeight(this->vehicle_type);
size->height = 3 * resize->height;
break;
case WID_BV_PANEL:
size->height = this->details_height;
break;
case WID_BV_SORT_ASCENDING_DESCENDING: {
Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
d.width += padding.width + WD_CLOSEBOX_WIDTH * 2; // Doubled since the string is centred and it also looks better.
d.height += padding.height;
*size = maxdim(*size, d);
break;
}
}
}
virtual void DrawWidget(const Rect &r, int widget) const
{
switch (widget) {
case WID_BV_LIST:
DrawEngineList(this->vehicle_type, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP,
&this->eng_list, this->vscroll->GetPosition(), min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(),
this->eng_list.Length()), this->sel_engine, false, DEFAULT_GROUP);
break;
case WID_BV_SORT_ASCENDING_DESCENDING:
this->DrawSortButtonState(WID_BV_SORT_ASCENDING_DESCENDING, this->descending_sort_order ? SBS_DOWN : SBS_UP);
break;
}
}
virtual void OnPaint()
{
this->GenerateBuildList();
this->vscroll->SetCount(this->eng_list.Length());
this->DrawWidgets();
if (!this->IsShaded()) {
int needed_height = this->details_height;
/* Draw details panels. */
if (this->sel_engine != INVALID_ENGINE) {
NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_BV_PANEL);
int text_end = DrawVehiclePurchaseInfo(nwi->pos_x + WD_FRAMETEXT_LEFT, nwi->pos_x + nwi->current_x - WD_FRAMETEXT_RIGHT,
nwi->pos_y + WD_FRAMERECT_TOP, this->sel_engine);
needed_height = max(needed_height, text_end - (int)nwi->pos_y + WD_FRAMERECT_BOTTOM);
}
if (needed_height != this->details_height) { // Details window are not high enough, enlarge them.
int resize = needed_height - this->details_height;
this->details_height = needed_height;
this->ReInit(0, resize);
return;
}
}
}
virtual void OnQueryTextFinished(char *str)
{
if (str == NULL) return;
DoCommandP(0, this->rename_engine, 0, CMD_RENAME_ENGINE | CMD_MSG(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type), NULL, str);
}
virtual void OnDropdownSelect(int widget, int index)
{
switch (widget) {
case WID_BV_SORT_DROPDOWN:
if (this->sort_criteria != index) {
this->sort_criteria = index;
_last_sort_criteria[this->vehicle_type] = this->sort_criteria;
this->eng_list.ForceRebuild();
}
break;
case WID_BV_CARGO_FILTER_DROPDOWN: // Select a cargo filter criteria
if (this->cargo_filter_criteria != index) {
this->cargo_filter_criteria = index;
_last_filter_criteria[this->vehicle_type] = this->cargo_filter[this->cargo_filter_criteria];
/* deactivate filter if criteria is 'Show All', activate it otherwise */
this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
this->eng_list.ForceRebuild();
}
break;
}
this->SetDirty();
}
virtual void OnResize()
{
this->vscroll->SetCapacityFromWidget(this, WID_BV_LIST);
this->GetWidget<NWidgetCore>(WID_BV_LIST)->widget_data = (this->vscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
}
void AddVirtualEngine(Train *toadd)
{
if (*virtual_train == NULL) {
*virtual_train = toadd;
} else {
VehicleID target = (*(this->virtual_train))->GetLastUnit()->index;
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
240, 268, // window size
WC_BUILD_VIRTUAL_TRAIN, // window class
WC_CREATE_TEMPLATE, // parent window class
WDF_CONSTRUCTION, // window flags
_nested_build_vehicle_widgets, lengthof(_nested_build_vehicle_widgets) // widgets + num widgets
);
void ShowBuildVirtualTrainWindow(Train **vt, bool *noticeParent)
{
// '0' as in VEH_TRAIN = Tile=0
assert(IsCompanyBuildableVehicleType(VEH_TRAIN));
DeleteWindowById(WC_BUILD_VEHICLE, 0);
new BuildVirtualTrainWindow(&_build_vehicle_desc, vt, noticeParent);
}

@ -0,0 +1,19 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file tbtr_template_gui_create_virtualtrain.cpp Template-based train replacement: template creation vehicle build GUI header. */
#ifndef BUILD_VIRTUAL_TRAIN_GUI
#define BUILD_VIRTUAL_TRAIN_GUI
#include "train.h"
void ShowBuildVirtualTrainWindow(Train**, bool*);
#endif

@ -0,0 +1,803 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file tbtr_template_gui_main.cpp Template-based train replacement: main GUI. */
#include "stdafx.h"
#include "command_func.h"
#include "vehicle_gui.h"
#include "newgrf_engine.h"
#include "group.h"
#include "rail.h"
#include "strings_func.h"
#include "window_func.h"
#include "autoreplace_func.h"
#include "company_func.h"
#include "engine_base.h"
#include "window_gui.h"
#include "viewport_func.h"
#include "tilehighlight_func.h"
#include "engine_gui.h"
#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"
// test creating pool -> creating vehicles
#include "core/pool_func.hpp"
#include "vehicle_gui_base.h"
#include "vehicle_base.h"
#include "train.h"
#include "vehicle_func.h"
#include "gfx_type.h"
#include "engine_func.h"
// drawing the vehicle length based on occupied tiles
#include "spritecache.h"
#include "tbtr_template_gui_main.h"
#include "tbtr_template_gui_create.h"
#include "tbtr_template_vehicle.h"
#include <iostream>
#include <stdio.h>
#include "safeguards.h"
typedef GUIList<const Group*> GUIGroupList;
enum TemplateReplaceWindowWidgets {
TRW_CAPTION,
TRW_WIDGET_INSET_GROUPS,
TRW_WIDGET_TOP_MATRIX,
TRW_WIDGET_TOP_SCROLLBAR,
TRW_WIDGET_INSET_TEMPLATES,
TRW_WIDGET_BOTTOM_MATRIX,
TRW_WIDGET_MIDDLE_SCROLLBAR,
TRW_WIDGET_BOTTOM_SCROLLBAR,
TRW_WIDGET_TMPL_INFO_INSET,
TRW_WIDGET_TMPL_INFO_PANEL,
TRW_WIDGET_TMPL_PRE_BUTTON_FLUFF,
TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REUSE,
TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_KEEP,
TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REFIT,
TRW_WIDGET_TMPL_BUTTONS_CONFIG_RIGHTPANEL,
TRW_WIDGET_TMPL_BUTTONS_DEFINE,
TRW_WIDGET_TMPL_BUTTONS_EDIT,
TRW_WIDGET_TMPL_BUTTONS_CLONE,
TRW_WIDGET_TMPL_BUTTONS_DELETE,
TRW_WIDGET_TMPL_BUTTONS_EDIT_RIGHTPANEL,
TRW_WIDGET_TITLE_INFO_GROUP,
TRW_WIDGET_TITLE_INFO_TEMPLATE,
TRW_WIDGET_INFO_GROUP,
TRW_WIDGET_INFO_TEMPLATE,
TRW_WIDGET_TMPL_BUTTONS_SPACER,
TRW_WIDGET_START,
TRW_WIDGET_TRAIN_FLUFF_LEFT,
TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN,
TRW_WIDGET_TRAIN_FLUFF_RIGHT,
TRW_WIDGET_STOP,
TRW_WIDGET_SEL_TMPL_DISPLAY_CREATE,
};
static const NWidgetPart _widgets[] = {
// Title bar
NWidget(NWID_HORIZONTAL),
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_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),
EndContainer(),
EndContainer(),
// Template Display
NWidget(NWID_VERTICAL),
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_MIDDLE_SCROLLBAR),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TRW_WIDGET_MIDDLE_SCROLLBAR),
EndContainer(),
EndContainer(),
// Info Area
NWidget(NWID_VERTICAL),
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),
// Spacing
NWidget(WWT_INSET, COLOUR_GREY, TRW_WIDGET_TMPL_PRE_BUTTON_FLUFF), SetMinimalSize(139, 12), SetResize(1,0), EndContainer(),
// Config buttons
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REUSE), SetMinimalSize(150,12), SetResize(0,0), SetDataTip(STR_TMPL_SET_USEDEPOT, STR_TMPL_SET_USEDEPOT_TIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_KEEP), SetMinimalSize(150,12), SetResize(0,0), SetDataTip(STR_TMPL_SET_KEEPREMAINDERS, STR_TMPL_SET_KEEPREMAINDERS_TIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REFIT), SetMinimalSize(150,12), SetResize(0,0), SetDataTip(STR_TMPL_SET_REFIT, STR_TMPL_SET_REFIT_TIP),
NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CONFIG_RIGHTPANEL), SetMinimalSize(12,12), SetResize(1,0), EndContainer(),
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_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(),
// Start/Stop buttons
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_START), SetMinimalSize(150, 12), SetDataTip(STR_TMPL_RPL_START, STR_REPLACE_ENGINE_WAGON_SELECT_HELP),
NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TRAIN_FLUFF_LEFT), SetMinimalSize(15, 12), EndContainer(),
NWidget(WWT_DROPDOWN, COLOUR_GREY, TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(0x0, STR_REPLACE_HELP_RAILTYPE), SetResize(1, 0),
NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TRAIN_FLUFF_RIGHT), SetMinimalSize(16, 12), EndContainer(),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_STOP), SetMinimalSize(150, 12), SetDataTip(STR_TMPL_RPL_STOP, STR_REPLACE_REMOVE_WAGON_HELP),
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
EndContainer(),
};
static WindowDesc _replace_rail_vehicle_desc(
WDP_AUTO,
"template replace window",
456, 156,
WC_TEMPLATEGUI_MAIN,
WC_NONE, // parent window class
WDF_CONSTRUCTION,
_widgets, lengthof(_widgets)
);
class TemplateReplaceWindow : public Window {
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[3];
// listing/sorting continued
GUITemplateList templates;
GUITemplateList::SortFunction **template_sorter_funcs;
short selected_template_index;
short selected_group_index;
bool templateNotice;
bool editInProgress;
public:
TemplateReplaceWindow(WindowDesc *wdesc, byte dig, int step_h) : Window(wdesc)
{
// listing/sorting
templates.SetSortFuncs(this->template_sorter_funcs);
// From BaseVehicleListWindow
this->unitnumber_digits = dig;
this->sel_railtype = RAILTYPE_BEGIN;
this->details_height = 10 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
this->line_height = step_h;
this->CreateNestedTree(wdesc != NULL);
this->vscroll[0] = this->GetScrollbar(TRW_WIDGET_TOP_SCROLLBAR);
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->matrixContentLeftMargin = 40;
this->selected_template_index = -1;
this->selected_group_index = -1;
this->UpdateButtonState();
this->templateNotice = false;
this->editInProgress = false;
this->templates.ForceRebuild();
BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype);
}
~TemplateReplaceWindow() {
DeleteWindowById(WC_CREATE_TEMPLATE, this->window_number);
}
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
{
switch (widget) {
case TRW_WIDGET_TOP_MATRIX:
resize->height = GetVehicleListHeight(VEH_TRAIN, FONT_HEIGHT_NORMAL + WD_MATRIX_TOP) / 2;
size->height = 8 * resize->height;
break;
case TRW_WIDGET_BOTTOM_MATRIX:
resize->height = GetVehicleListHeight(VEH_TRAIN, FONT_HEIGHT_NORMAL + WD_MATRIX_TOP);
size->height = 4 * resize->height;
break;
case TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN: {
Dimension d = {0, 0};
for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
const RailtypeInfo *rti = GetRailTypeInfo(rt);
// Skip rail type if it has no label
if (rti->label == 0) continue;
d = maxdim(d, GetStringBoundingBox(rti->strings.replace_text));
}
d.width += padding.width;
d.height += padding.height;
*size = maxdim(*size, d);
break;
}
}
}
virtual void SetStringParameters(int widget) const
{
switch (widget) {
case TRW_CAPTION:
SetDParam(0, STR_TMPL_RPL_TITLE);
break;
}
}
virtual void DrawWidget(const Rect &r, int widget) const
{
switch (widget) {
case TRW_WIDGET_TOP_MATRIX: {
DrawAllGroupsFunction(this->line_height, r);
break;
}
case TRW_WIDGET_BOTTOM_MATRIX: {
DrawTemplateList(this->line_height, r);
break;
}
case TRW_WIDGET_TMPL_INFO_PANEL: {
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;
}
}
}
virtual void OnPaint()
{
BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype);
this->BuildGroupList(_local_company);
if (templateNotice) {
BuildTemplateGuiList(&this->templates, vscroll[1], _local_company, this->sel_railtype);
templateNotice = false;
this->SetDirty();
}
/* sets the colour of that art thing */
this->GetWidget<NWidgetCore>(TRW_WIDGET_TRAIN_FLUFF_LEFT)->colour = _company_colours[_local_company];
this->GetWidget<NWidgetCore>(TRW_WIDGET_TRAIN_FLUFF_RIGHT)->colour = _company_colours[_local_company];
/* 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 != NULL; 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();
}
virtual void OnClick(Point pt, int widget, int click_count)
{
if (this->editInProgress) return;
switch (widget) {
case TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REUSE: {
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) && (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) && (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: {
ShowTemplateCreateWindow(0, &templateNotice, &editInProgress, this->line_height);
break;
}
case TRW_WIDGET_TMPL_BUTTONS_EDIT: {
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);
}
break;
}
case TRW_WIDGET_TMPL_BUTTONS_CLONE: {
this->SetWidgetDirty(TRW_WIDGET_TMPL_BUTTONS_CLONE);
this->ToggleWidgetLoweredState(TRW_WIDGET_TMPL_BUTTONS_CLONE);
if (this->IsWidgetLowered(TRW_WIDGET_TMPL_BUTTONS_CLONE)) {
static const CursorID clone_icon = SPR_CURSOR_CLONE_TRAIN;
SetObjectToPlaceWnd(clone_icon, PAL_NONE, HT_VEHICLE, this);
} else {
ResetObjectToPlace();
}
break;
}
case TRW_WIDGET_TMPL_BUTTONS_DELETE:
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;
}
}
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();
if (newindex == this->selected_group_index || newindex >= this->groups.Length()) {
this->selected_group_index = -1;
} else if (newindex < this->groups.Length()) {
this->selected_group_index = newindex;
}
this->UpdateButtonState();
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();
if (newindex == this->selected_template_index || newindex >= templates.Length()) {
this->selected_template_index = -1;
} else if (newindex < templates.Length()) {
this->selected_template_index = newindex;
}
this->UpdateButtonState();
break;
}
case TRW_WIDGET_START: {
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;
DoCommandP(0, current_group_index, tv_index, CMD_ISSUE_TEMPLATE_REPLACEMENT, NULL);
this->UpdateButtonState();
}
break;
}
case TRW_WIDGET_STOP:
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;
DoCommandP(0, current_group_index, 0, CMD_DELETE_TEMPLATE_REPLACEMENT, NULL);
this->UpdateButtonState();
break;
}
this->SetDirty();
}
virtual bool OnVehicleSelect(const Vehicle *v)
{
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);
ResetObjectToPlace();
this->SetDirty();
return true;
}
virtual void OnDropdownSelect(int widget, int index)
{
RailType temp = (RailType) index;
if (temp == this->sel_railtype) return; // we didn't select a new one. No need to change anything
this->sel_railtype = temp;
/* Reset scrollbar positions */
this->vscroll[0]->SetPosition(0);
this->vscroll[1]->SetPosition(0);
BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype);
this->SetDirty();
}
virtual void OnResize()
{
/* Top Matrix */
NWidgetCore *nwi = this->GetWidget<NWidgetCore>(TRW_WIDGET_TOP_MATRIX);
this->vscroll[0]->SetCapacityFromWidget(this, TRW_WIDGET_TOP_MATRIX);
nwi->widget_data = (this->vscroll[0]->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
/* Bottom Matrix */
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()
{
if (templateNotice) {
BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype);
this->SetDirty();
templateNotice = false;
}
}
virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
{
this->groups.ForceRebuild();
this->templates.ForceRebuild();
this->UpdateButtonState();
}
/** For a given group (id) find the template that is issued for template replacement for this group and return this template's index
* from the gui list */
short FindTemplateIndexForGroup(short gid) const
{
TemplateReplacement *tr = GetTemplateReplacementByGroupID(gid);
if (!tr) return -1;
for (uint32 i = 0; i < this->templates.Length(); ++i) {
if (templates[i]->index == tr->sel_template) {
return i;
}
}
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)
{
static const Group *last_group[2] = { NULL, NULL };
static char last_name[2][64] = { "", "" };
if (*a != last_group[0]) {
last_group[0] = *a;
SetDParam(0, (*a)->index);
GetString(last_name[0], STR_GROUP_NAME, lastof(last_name[0]));
}
if (*b != last_group[1]) {
last_group[1] = *b;
SetDParam(0, (*b)->index);
GetString(last_name[1], STR_GROUP_NAME, lastof(last_name[1]));
}
int r = strnatcmp(last_name[0], last_name[1]); // Sort by name (natural sorting).
if (r == 0) return (*a)->index - (*b)->index;
return r;
}
void BuildGroupList(Owner owner)
{
if (!this->groups.NeedRebuild()) return;
this->groups.Clear();
this->indents.Clear();
GUIGroupList list;
const Group *g;
FOR_ALL_GROUPS(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());
}
void DrawAllGroupsFunction(int line_height, const Rect &r) const
{
int left = r.left + WD_MATRIX_LEFT;
int right = r.right - WD_MATRIX_RIGHT;
int y = r.top;
int max = min(this->vscroll[0]->GetPosition() + this->vscroll[0]->GetCapacity(), this->groups.Length());
/* Then treat all groups defined by/for the current company */
for (int i = this->vscroll[0]->GetPosition(); i < max; ++i) {
const Group *g = (this->groups)[i];
short g_id = g->index;
/* Fill the background of the current cell in a darker tone for the currently selected template */
if (this->selected_group_index == i) {
GfxFillRect(left, y, right, y+(this->line_height) / 2, _colour_gradient[COLOUR_GREY][3]);
}
SetDParam(0, g_id);
StringID str = STR_GROUP_NAME;
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);
if (template_in_use >= 0) {
SetDParam(0, template_in_use);
DrawString (left, right, y + 2, STR_TMPL_GROUP_USES_TEMPLATE, TC_BLACK, SA_HOR_CENTER);
} else if (GetTemplateReplacementByGroupID(g_id)) { /* If there isn't a template applied from the current group, check if there is one for another rail type */
DrawString (left, right, y + 2, STR_TMPL_TMPLRPL_EX_DIFF_RAILTYPE, TC_SILVER, SA_HOR_CENTER);
}
/* Draw the number of trains that still need to be treated by the currently selected template replacement */
TemplateReplacement *tr = GetTemplateReplacementByGroupID(g_id);
if (tr) {
TemplateVehicle *tv = TemplateVehicle::Get(tr->sel_template);
int num_trains = NumTrainsNeedTemplateReplacement(g_id, tv);
// Draw text
TextColour color = TC_GREY;
if (num_trains) color = TC_BLACK;
DrawString(left, right - 16, y + 2, STR_TMPL_NUM_TRAINS_NEED_RPL, color, SA_RIGHT);
// Draw number
if (num_trains ) {
color = TC_ORANGE;
} else {
color = TC_GREY;
}
SetDParam(0, num_trains);
DrawString(left, right - 4, y + 2, STR_JUST_INT, color, SA_RIGHT);
}
y += line_height / 2;
}
}
void DrawTemplateList(int line_height, const Rect &r) const
{
int left = r.left;
int right = r.right;
int y = r.top;
Scrollbar *draw_vscroll = vscroll[1];
uint max = min(draw_vscroll->GetPosition() + draw_vscroll->GetCapacity(), this->templates.Length());
const TemplateVehicle *v;
for (uint i = draw_vscroll->GetPosition(); i < max; ++i) {
v = (this->templates)[i];
/* Fill the background of the current cell in a darker tone for the currently selected template */
if (this->selected_template_index == (int32) i) {
GfxFillRect(left, y, right, y + this->line_height, _colour_gradient[COLOUR_GREY][3]);
}
/* Draw a notification string for chains that are not runnable */
if (v->IsFreeWagonChain()) {
DrawString(left, right - 2, y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 2, STR_TMPL_WARNING_FREE_WAGON, TC_RED, SA_RIGHT);
}
/* Draw the template's length in tile-units */
SetDParam(0, v->GetRealLength());
SetDParam(1, 1);
DrawString(left, right - 4, y + 2, STR_TINY_BLACK_DECIMAL, TC_BLACK, SA_RIGHT);
/* Draw the template */
DrawTemplate(v, left + 50, right, y);
/* Buying cost */
SetDParam(0, CalculateOverallTemplateCost(v));
DrawString(left + 35, right, y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 2, STR_TMPL_TEMPLATE_OVR_VALUE_notinyfont, TC_BLUE, SA_LEFT);
/* Index of current template vehicle in the list of all templates for its company */
SetDParam(0, i);
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 + 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;
color = v->IsSetReuseDepotVehicles() ? TC_LIGHT_BLUE : TC_GREY;
DrawString(right - 225, right, y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 2, STR_TMPL_CONFIG_USEDEPOT, color, SA_LEFT);
color = v->IsSetKeepRemainingVehicles() ? TC_LIGHT_BLUE : TC_GREY;
DrawString(right - 150, right, y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 2, STR_TMPL_CONFIG_KEEPREMAINDERS, color, SA_LEFT);
color = v->IsSetRefitAsTemplate() ? TC_LIGHT_BLUE : TC_GREY;
DrawString(right - 75, right, y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 2, STR_TMPL_CONFIG_REFIT, color, SA_LEFT);
y += line_height;
}
}
void DrawTemplateInfo(int line_height, const Rect &r) const
{
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 */
SetDParam(2, tmp->max_speed);
SetDParam(1, tmp->power);
SetDParam(0, tmp->weight);
SetDParam(3, tmp->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 = 30 - this->vscroll[2]->GetPosition();
short left = 8;
short count_columns = 0;
short max_columns = 2;
CargoArray cargo_caps;
for (; tmp != NULL; tmp = tmp->Next()) {
cargo_caps[tmp->cargo_type] += tmp->cargo_cap;
}
int x = left;
for (CargoID i = 0; i < NUM_CARGO; ++i) {
if (cargo_caps[i] > 0) {
count_columns++;
SetDParam(0, i);
SetDParam(1, cargo_caps[i]);
SetDParam(2, _settings_game.vehicle.freight_trains);
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;
}
void UpdateButtonState()
{
bool selected_ok = (this->selected_template_index >= 0) && (this->selected_template_index < (short)this->templates.Length());
bool group_ok = (this->selected_group_index >= 0) && (this->selected_group_index < (short)this->groups.Length());
short g_id = -1;
if (group_ok) {
const Group *g = (this->groups)[this->selected_group_index];
g_id = g->index;
}
this->SetWidgetDisabledState(TRW_WIDGET_TMPL_BUTTONS_EDIT, !selected_ok);
this->SetWidgetDisabledState(TRW_WIDGET_TMPL_BUTTONS_DELETE, !selected_ok);
this->SetWidgetDisabledState(TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REUSE, !selected_ok);
this->SetWidgetDisabledState(TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_KEEP, !selected_ok);
this->SetWidgetDisabledState(TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REFIT, !selected_ok);
this->SetWidgetDisabledState(TRW_WIDGET_START, !(selected_ok && group_ok && FindTemplateIndexForGroup(g_id) != this->selected_template_index));
this->SetWidgetDisabledState(TRW_WIDGET_STOP, !(group_ok && GetTemplateReplacementByGroupID(g_id) != NULL));
}
};
void ShowTemplateReplaceWindow(byte dig, int step_h)
{
new TemplateReplaceWindow(&_replace_rail_vehicle_desc, dig, step_h);
}

@ -0,0 +1,28 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file tbtr_template_gui_main.h Template-based train replacement: main GUI header. */
#ifndef TEMPLATE_GUI_H
#define TEMPLATE_GUI_H
#include "engine_type.h"
#include "group_type.h"
#include "vehicle_type.h"
#include "string_func.h"
#include "strings_func.h"
#include "tbtr_template_vehicle.h"
#include "tbtr_template_vehicle_func.h"
typedef GUIList<const Group*> GUIGroupList;
void ShowTemplateReplaceWindow(byte, int);
#endif

@ -0,0 +1,214 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file tbtr_template_vehicle.cpp Template-based train replacement: template vehicle. */
#include "stdafx.h"
#include "company_func.h"
#include "train.h"
#include "command_func.h"
#include "engine_func.h"
#include "vehicle_func.h"
#include "autoreplace_func.h"
#include "autoreplace_gui.h"
#include "group.h"
#include "articulated_vehicles.h"
#include "core/random_func.hpp"
#include "core/pool_type.hpp"
#include "engine_type.h"
#include "group_type.h"
#include "core/pool_func.hpp"
#include "table/strings.h"
#include "newgrf.h"
#include "vehicle_type.h"
#include "vehicle_base.h"
#include "vehicle_func.h"
#include "table/train_cmd.h"
#include "tbtr_template_vehicle.h"
// since doing stuff with sprites
#include "newgrf_spritegroup.h"
#include "newgrf_engine.h"
#include "newgrf_cargo.h"
#include "safeguards.h"
TemplatePool _template_pool("TemplatePool");
INSTANTIATE_POOL_METHODS(Template)
TemplateReplacementPool _template_replacement_pool("TemplateReplacementPool");
INSTANTIATE_POOL_METHODS(TemplateReplacement)
TemplateVehicle::TemplateVehicle(VehicleType ty, EngineID eid, byte subtypeflag, Owner current_owner)
{
this->type = ty;
this->engine_type = eid;
this->reuse_depot_vehicles = true;
this->keep_remaining_vehicles = true;
this->first = this;
this->next = 0x0;
this->previous = 0x0;
this->owner_b = _current_company;
this->cur_image = SPR_IMG_QUERY;
this->owner = current_owner;
this->real_consist_length = 0;
}
TemplateVehicle::~TemplateVehicle() {
TemplateVehicle *v = this->Next();
this->SetNext(NULL);
delete v;
}
/** getting */
void TemplateVehicle::SetNext(TemplateVehicle *v) { this->next = v; }
void TemplateVehicle::SetPrev(TemplateVehicle *v) { this->previous = v; }
void TemplateVehicle::SetFirst(TemplateVehicle *v) { this->first = v; }
TemplateVehicle* TemplateVehicle::GetNextUnit() const
{
TemplateVehicle *tv = this->Next();
while (tv && HasBit(tv->subtype, GVSF_ARTICULATED_PART)) {
tv = tv->Next();
}
if (tv && HasBit(tv->subtype, GVSF_MULTIHEADED) && !HasBit(tv->subtype, GVSF_ENGINE)) tv = tv->Next();
return tv;
}
TemplateVehicle* TemplateVehicle::GetPrevUnit()
{
TemplateVehicle *tv = this->Prev();
while (tv && HasBit(tv->subtype, GVSF_ARTICULATED_PART|GVSF_ENGINE)) {
tv = tv->Prev();
}
if (tv && HasBit(tv->subtype, GVSF_MULTIHEADED|GVSF_ENGINE)) tv = tv->Prev();
return tv;
}
/** setting */
void appendTemplateVehicle(TemplateVehicle *orig, TemplateVehicle *newv)
{
if (!orig) return;
while (orig->Next()) orig = orig->Next();
orig->SetNext(newv);
newv->SetPrev(orig);
newv->SetFirst(orig->First());
}
void insertTemplateVehicle(TemplateVehicle *orig, TemplateVehicle *newv, TemplateVehicle *insert_after)
{
if (!orig || !insert_after) return;
TemplateVehicle *insert_before = insert_after->Next();
insert_after->SetNext(newv);
insert_before->SetPrev(newv);
newv->SetPrev(insert_after);
newv->SetNext(insert_before);
newv->SetFirst(insert_after);
}
/** Length()
* @return: length of vehicle, including current part
*/
int TemplateVehicle::Length() const
{
int l = 1;
const TemplateVehicle *tmp = this;
while (tmp->Next()) {
tmp = tmp->Next();
l++;
}
return l;
}
TemplateReplacement* GetTemplateReplacementByGroupID(GroupID gid)
{
TemplateReplacement *tr;
FOR_ALL_TEMPLATE_REPLACEMENTS(tr) {
if (tr->Group() == gid) {
return tr;
}
}
return NULL;
}
TemplateReplacement* GetTemplateReplacementByTemplateID(TemplateID tid)
{
TemplateReplacement *tr;
FOR_ALL_TEMPLATE_REPLACEMENTS(tr) {
if (tr->Template() == tid) {
return tr;
}
}
return NULL;
}
bool IssueTemplateReplacement(GroupID gid, TemplateID tid)
{
TemplateReplacement *tr = GetTemplateReplacementByGroupID(gid);
if (tr) {
/* Then set the new TemplateVehicle and return */
tr->SetTemplate(tid);
return true;
} else if (TemplateReplacement::CanAllocateItem()) {
tr = new TemplateReplacement(gid, tid);
return true;
}
else return false;
}
short TemplateVehicle::NumGroupsUsingTemplate() const
{
short amount = 0;
const TemplateReplacement *tr;
FOR_ALL_TEMPLATE_REPLACEMENTS(tr) {
if (tr->sel_template == this->index) {
amount++;
}
}
return amount;
}
short TemplateVehicle::CountEnginesInChain()
{
TemplateVehicle *tv = this->first;
short count = 0;
for (; tv != NULL; tv = tv->GetNextUnit()) {
if (HasBit(tv->subtype, GVSF_ENGINE)) {
count++;
}
}
return count;
}
short deleteIllegalTemplateReplacements(GroupID g_id)
{
short del_amount = 0;
const TemplateReplacement *tr;
FOR_ALL_TEMPLATE_REPLACEMENTS(tr) {
if (tr->group == g_id) {
delete tr;
del_amount++;
}
}
return del_amount;
}

@ -0,0 +1,208 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file tbtr_template_vehicle.h Template-based train replacement: template vehicle header. */
#ifndef TEMPLATE_VEH_H
#define TEMPLATE_VEH_H
#include "company_func.h"
#include "vehicle_type.h"
#include "vehicle_base.h"
#include "vehicle_func.h"
#include "articulated_vehicles.h"
#include "newgrf_callbacks.h"
#include "newgrf_engine.h"
#include "newgrf_spritegroup.h"
#include "engine_base.h"
#include "engine_type.h"
#include "engine_func.h"
#include "sortlist_type.h"
#define FOR_ALL_TEMPLATES_FROM(var, start) FOR_ALL_ITEMS_FROM(TemplateVehicle, template_index, var, start)
#define FOR_ALL_TEMPLATES(var) FOR_ALL_TEMPLATES_FROM(var, 0)
#define FOR_ALL_TEMPLATE_REPLACEMENTS_FROM(var, start) FOR_ALL_ITEMS_FROM(TemplateReplacement, template_replacement_index, var, start)
#define FOR_ALL_TEMPLATE_REPLACEMENTS(var) FOR_ALL_TEMPLATE_REPLACEMENTS_FROM(var, 0)
struct TemplateVehicle;
struct TemplateReplacement;
typedef uint16 TemplateID;
static const uint16 CONSIST_HEAD = 0x0;
static const uint16 CONSIST_TAIL = 0xffff;
/** A pool allowing to store up to ~64k templates */
typedef Pool<TemplateVehicle, TemplateID, 512, 0x10000> TemplatePool;
extern TemplatePool _template_pool;
/// listing/sorting templates
typedef GUIList<const TemplateVehicle*> GUITemplateList;
struct TemplateVehicle : TemplatePool::PoolItem<&_template_pool>, BaseVehicle {
private:
TemplateVehicle *next; ///< pointer to the next vehicle in the chain
TemplateVehicle *previous; ///< NOSAVE: pointer to the previous vehicle in the chain
TemplateVehicle *first; ///< NOSAVE: pointer to the first vehicle in the chain
public:
friend const SaveLoad* GTD();
friend void AfterLoadTemplateVehicles();
// Template usage configuration
bool reuse_depot_vehicles;
bool keep_remaining_vehicles;
bool refit_as_template;
// Things derived from a virtual train
TemplateVehicle *other_multiheaded_part; ///< Multiheaded Engine support
Money value; ///< Value of the vehicle
Owner owner;
OwnerByte owner_b;
EngineID engine_type; ///< The type of engine used for this vehicle.
CargoID cargo_type; ///< type of cargo this vehicle is carrying
uint16 cargo_cap; ///< total capacity
byte cargo_subtype;
byte subtype;
RailTypeByte railtype;
VehicleID index;
uint16 real_consist_length;
uint16 max_speed;
uint32 power;
uint32 weight;
uint32 max_te;
byte spritenum;
SpriteID cur_image;
uint32 image_width;
const SpriteGroup *sgroup;
TemplateVehicle(VehicleType type = VEH_INVALID, EngineID e = INVALID_ENGINE, byte B = 0, Owner = _local_company);
TemplateVehicle(EngineID, RailVehicleInfo*);
TemplateVehicle(EngineID eid)
{
next = NULL;
previous = NULL;
first = this;
engine_type = eid;
this->reuse_depot_vehicles = true;
this->keep_remaining_vehicles = true;
this->refit_as_template = true;
}
~TemplateVehicle();
inline TemplateVehicle* Next() const { return this->next; }
inline TemplateVehicle* Prev() const { return this->previous; }
inline TemplateVehicle* First() const { return this->first; }
void SetNext(TemplateVehicle*);
void SetPrev(TemplateVehicle*);
void SetFirst(TemplateVehicle*);
TemplateVehicle* GetNextUnit() const;
TemplateVehicle* GetPrevUnit();
bool IsSetReuseDepotVehicles() const { return this->reuse_depot_vehicles; }
bool IsSetKeepRemainingVehicles() const { return this->keep_remaining_vehicles; }
bool IsSetRefitAsTemplate() const { return this->refit_as_template; }
void ToggleReuseDepotVehicles() { this->reuse_depot_vehicles = !this->reuse_depot_vehicles; }
void ToggleKeepRemainingVehicles() { this->keep_remaining_vehicles = !this->keep_remaining_vehicles; }
void ToggleRefitAsTemplate() { this->refit_as_template = !this->refit_as_template; }
bool IsPrimaryVehicle() const { return this->IsFrontEngine(); }
inline bool IsFrontEngine() const { return HasBit(this->subtype, GVSF_FRONT); }
inline bool HasArticulatedPart() const { return this->Next() != NULL && this->Next()->IsArticulatedPart(); }
inline bool IsArticulatedPart() const { return HasBit(this->subtype, GVSF_ARTICULATED_PART); }
inline bool IsMultiheaded() const { return HasBit(this->subtype, GVSF_MULTIHEADED); }
inline bool IsFreeWagonChain() const { return HasBit(this->subtype, GVSF_FREE_WAGON); }
// since CmdBuildTemplateVehicle(...)
inline void SetFrontEngine() { SetBit(this->subtype, GVSF_FRONT); }
inline void SetEngine() { SetBit(this->subtype, GVSF_ENGINE); }
inline void SetArticulatedPart() { SetBit(this->subtype, GVSF_ARTICULATED_PART); }
inline void SetMultiheaded() { SetBit(this->subtype, GVSF_MULTIHEADED); }
inline void SetWagon() { SetBit(this->subtype, GVSF_WAGON); }
inline void SetFreeWagon() { SetBit(this->subtype, GVSF_FREE_WAGON); }
inline uint16 GetRealLength() const { return this->real_consist_length; }
inline void SetRealLength(uint16 len) { this->real_consist_length = len; }
int Length() const;
SpriteID GetImage(Direction) const;
SpriteID GetSpriteID() const;
short NumGroupsUsingTemplate() const;
short CountEnginesInChain();
};
void appendTemplateVehicle(TemplateVehicle*, TemplateVehicle*);
void insertTemplateVehicle(TemplateVehicle*, TemplateVehicle*, TemplateVehicle*);
void NeutralizeVehicleStatus(Train*);
void SplitVehicleRemainders(Train*);
// TemplateReplacement stuff
typedef Pool<TemplateReplacement, uint16, 16, 1024> TemplateReplacementPool;
extern TemplateReplacementPool _template_replacement_pool;
struct TemplateReplacement : TemplateReplacementPool::PoolItem<&_template_replacement_pool> {
GroupID group;
TemplateID sel_template;
TemplateReplacement(GroupID gid, TemplateID tid) { this->group=gid; this->sel_template=tid; }
TemplateReplacement() {}
~TemplateReplacement() {}
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 TemplateID GetTemplateVehicleID() { return sel_template; }
inline const TemplateVehicle* GetTemplateVehicle()
{
const TemplateVehicle *tv;
FOR_ALL_TEMPLATES(tv) {
if (tv->index == this->sel_template) {
return tv;
}
}
return NULL;
}
};
TemplateReplacement* GetTemplateReplacementByGroupID(GroupID);
TemplateReplacement* GetTemplateReplacementByTemplateID(TemplateID);
bool IssueTemplateReplacement(GroupID, TemplateID);
short deleteIllegalTemplateReplacements(GroupID);
#endif /* TEMPLATE_VEH_H */

@ -0,0 +1,547 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file tbtr_template_vehicle_func.cpp Template-based train replacement: template vehicle functions. */
#include "stdafx.h"
#include "window_gui.h"
#include "gfx_func.h"
#include "window_func.h"
#include "command_func.h"
#include "vehicle_gui.h"
#include "train.h"
#include "strings_func.h"
#include "vehicle_func.h"
#include "core/geometry_type.hpp"
#include "debug.h"
#include "table/sprites.h"
#include "table/strings.h"
#include "cargoaction.h"
#include "train.h"
#include "company_func.h"
#include "newgrf.h"
#include "spritecache.h"
#include "articulated_vehicles.h"
#include "autoreplace_func.h"
#include "depot_base.h"
#include "tbtr_template_vehicle.h"
#include "tbtr_template_vehicle_func.h"
#include <map>
#include <stdio.h>
#include "safeguards.h"
Vehicle *vhead, *vtmp;
static const uint MAX_ARTICULATED_PARTS = 100;
#ifdef _DEBUG
// debugging printing functions for convenience, usually called from gdb
void tbtr_debug_pat()
{
TemplateVehicle *tv;
FOR_ALL_TEMPLATES(tv) {
if (tv->Prev()) continue;
tbtr_debug_ptv(tv);
printf("__________\n");
}
}
void tbtr_debug_pav()
{
Train *t;
FOR_ALL_TRAINS(t) {
if (t->Previous()) continue;
tbtr_debug_pvt(t);
printf("__________\n");
}
}
void tbtr_debug_ptv(TemplateVehicle* tv)
{
if (!tv) return;
while (tv->Next() ) {
printf("eid:%3d st:%2d tv:%p next:%p cargo: %d cargo_sub: %d\n", tv->engine_type, tv->subtype, tv, tv->Next(), tv->cargo_type, tv->cargo_subtype);
tv = tv->Next();
}
printf("eid:%3d st:%2d tv:%p next:%p cargo: %d cargo_sub: %d\n", tv->engine_type, tv->subtype, tv, tv->Next(), tv->cargo_type, tv->cargo_subtype);
}
void tbtr_debug_pvt (const Train *printme)
{
for (const Train *tmp = printme; tmp; tmp = tmp->Next()) {
if (tmp->index <= 0) {
printf("train has weird index: %d %d %p\n", tmp->index, tmp->engine_type, tmp);
return;
}
printf("eid:%3d index:%2d subtype:%2d vehstat: %d cargo_t: %d cargo_sub: %d ref:%p\n", tmp->engine_type, tmp->index, tmp->subtype, tmp->vehstatus, tmp->cargo_type, tmp->cargo_subtype, tmp);
}
}
#endif
void BuildTemplateGuiList(GUITemplateList *list, Scrollbar *vscroll, Owner oid, RailType railtype)
{
list->Clear();
const TemplateVehicle *tv;
FOR_ALL_TEMPLATES(tv) {
if (tv->owner == oid && (tv->IsPrimaryVehicle() || tv->IsFreeWagonChain()) && TemplateVehicleContainsEngineOfRailtype(tv, railtype)) {
*list->Append() = tv;
}
}
list->RebuildDone();
if (vscroll) vscroll->SetCount(list->Length());
}
Money CalculateOverallTemplateCost(const TemplateVehicle *tv)
{
Money val = 0;
for (; tv; tv = tv->Next()) {
val += (Engine::Get(tv->engine_type))->GetCost();
}
return val;
}
void DrawTemplate(const TemplateVehicle *tv, int left, int right, int y)
{
if (!tv) return;
const TemplateVehicle *t = tv;
int offset = left;
while (t) {
PaletteID pal = GetEnginePalette(t->engine_type, _current_company);
DrawSprite(t->cur_image, pal, offset, y + 12);
offset += t->image_width;
t = t->Next();
}
}
// copy important stuff from the virtual vehicle to the template
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);
}
// create a full TemplateVehicle based train according to a virtual train
TemplateVehicle* TemplateVehicleFromVirtualTrain(Train *virt)
{
if (!virt) return NULL;
Train *init_virt = virt;
int len = CountVehiclesInChain(virt);
if (!TemplateVehicle::CanAllocateItem(len)) {
return NULL;
}
TemplateVehicle *tmp;
TemplateVehicle *prev = NULL;
for (; virt; virt = virt->Next()) {
tmp = new TemplateVehicle(virt->engine_type);
SetupTemplateVehicleFromVirtual(tmp, prev, virt);
prev = tmp;
}
tmp->First()->SetRealLength(CeilDiv(init_virt->gcache.cached_total_length * 10, TILE_SIZE));
return tmp->First();
}
// 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 NULL;
while (chain->Next()) {
chain = chain->Next();
}
return chain;
}
inline Train* Last(Train *chain)
{
if (!chain) return NULL;
while (chain->GetNextUnit()) {
chain = chain->GetNextUnit();
}
return chain;
}
// return: pointer to former vehicle
TemplateVehicle *DeleteTemplateVehicle(TemplateVehicle *todel)
{
if (!todel) return NULL;
TemplateVehicle *cur = todel;
delete todel;
return cur;
}
// forward declaration, defined in train_cmd.cpp
CommandCost CmdSellRailWagon(DoCommandFlag, Vehicle*, uint16, uint32);
Train* DeleteVirtualTrain(Train *chain, Train *to_del) {
if (chain != to_del) {
CmdSellRailWagon(DC_EXEC, to_del, 0, 0);
return chain;
}
else {
chain = chain->GetNextUnit();
CmdSellRailWagon(DC_EXEC, to_del, 0, 0);
return chain;
}
}
// retrieve template vehicle from templatereplacement that belongs to the given group
TemplateVehicle* GetTemplateVehicleByGroupID(GroupID gid) {
TemplateReplacement *tr;
// first try to find a templatereplacement issued for the given groupid
FOR_ALL_TEMPLATE_REPLACEMENTS(tr) {
if (tr->Group() == gid) {
return TemplateVehicle::GetIfValid(tr->Template()); // there can be only one
}
}
// if that didn't work, try to find a templatereplacement for ALL_GROUP
if (gid != ALL_GROUP) {
FOR_ALL_TEMPLATE_REPLACEMENTS(tr) {
if (tr->Group() == ALL_GROUP) {
return TemplateVehicle::GetIfValid(tr->Template());
}
}
}
// if all failed, just return null
return NULL;
}
/**
* Check a template consist whether it contains any engine of the given railtype
*/
bool TemplateVehicleContainsEngineOfRailtype(const TemplateVehicle *tv, RailType type)
{
/* For standard rail engines, allow only those */
if (type == RAILTYPE_BEGIN || type == RAILTYPE_RAIL) {
while (tv) {
if (tv->railtype != type) {
return false;
}
tv = tv->GetNextUnit();
}
return true;
}
/* For electrified rail engines, standard wagons or engines are allowed to be included */
while (tv) {
if (tv->railtype == type) {
return true;
}
tv = tv->GetNextUnit();
}
return false;
}
//helper
bool ChainContainsVehicle(Train *chain, Train *mem)
{
for (; chain; chain = chain->Next()) {
if (chain == mem) {
return true;
}
}
return false;
}
// has O(n)
Train* ChainContainsEngine(EngineID eid, Train *chain) {
for (; chain; chain=chain->GetNextUnit())
if (chain->engine_type == eid)
return chain;
return NULL;
}
// has O(n^2)
Train* DepotContainsEngine(TileIndex tile, EngineID eid, Train *not_in = NULL)
{
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 (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
&& ((t->IsPrimaryVehicle() && t->IsStoppedInDepot()) || t->IsFreeWagon())
&& t->engine_type == eid
&& (not_in == NULL || ChainContainsVehicle(not_in, t) == false)) {
return t;
}
}
return NULL;
}
void CopyStatus(Train *from, Train *to)
{
DoCommand(to->tile, from->group_id, to->index, DC_EXEC, CMD_ADD_VEHICLE_GROUP);
to->cargo_type = from->cargo_type;
to->cargo_subtype = from->cargo_subtype;
// swap names
char *tmp = to->name;
to->name = from->name;
from->name = tmp;
}
void NeutralizeStatus(Train *t)
{
DoCommand(t->tile, DEFAULT_GROUP, t->index, DC_EXEC, CMD_ADD_VEHICLE_GROUP);
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) {
if (t->engine_type != tv->engine_type) {
return false;
}
t = t->GetNextUnit();
tv = tv->GetNextUnit();
}
if ((t && !tv) || (!t && tv)) {
return false;
}
return true;
}
bool TrainMatchesTemplateRefit(const Train *t, TemplateVehicle *tv)
{
if (!tv->refit_as_template) {
return true;
}
while (t && tv) {
if (t->cargo_type != tv->cargo_type || t->cargo_subtype != tv->cargo_subtype) {
return false;
}
t = t->GetNextUnit();
tv = tv->GetNextUnit();
}
return true;
}
void BreakUpRemainders(Train *t)
{
while (t) {
Train *move;
if (HasBit(t->subtype, GVSF_ENGINE)) {
move = t;
t = t->Next();
DoCommand(move->tile, move->index, INVALID_VEHICLE, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
NeutralizeStatus(move);
} else {
t = t->Next();
}
}
}
short CountEnginesInChain(Train *t)
{
short count = 0;
for (; t != NULL; t = t->GetNextUnit()) {
if (HasBit(t->subtype, GVSF_ENGINE)) {
count++;
}
}
return count;
}
int countOccurrencesInTrain(Train *t, EngineID eid)
{
int count = 0;
Train *tmp = t;
for (; tmp != NULL; tmp = tmp->GetNextUnit()) {
if (tmp->engine_type == eid) {
count++;
}
}
return count;
}
int countOccurrencesInTemplateVehicle(TemplateVehicle *contain, EngineID eid)
{
int count = 0;
for (; contain; contain=contain->GetNextUnit()) {
if (contain->engine_type == eid) {
count++;
}
}
return count;
}
int countOccurrencesInDepot(TileIndex tile, EngineID eid, Train *not_in = NULL)
{
int count = 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 (v->tile == tile && v->IsStoppedInDepot() && v->engine_type == eid &&
(not_in == 0 || ChainContainsVehicle(not_in, (Train*)v) == false)) {
count++;
}
}
return count;
}
// basically does the same steps as CmdTemplateReplaceVehicle but without actually moving things around
CommandCost CalculateTemplateReplacementCost(Train *incoming)
{
TileIndex tile = incoming->tile;
TemplateVehicle *tv = GetTemplateVehicleByGroupID(incoming->group_id);
CommandCost estimate(EXPENSES_NEW_VEHICLES);
// count for each different eid in the incoming train
std::map<EngineID, short> unique_eids;
for (TemplateVehicle *tmp = tv; tmp != NULL; tmp = tmp->GetNextUnit()) {
unique_eids[tmp->engine_type]++;
}
std::map<EngineID, short>::iterator it = unique_eids.begin();
for (; it != unique_eids.end(); it++) {
it->second -= countOccurrencesInTrain(incoming, it->first);
it->second -= countOccurrencesInDepot(incoming->tile, it->first, incoming);
if (it->second < 0) it->second = 0;
}
// get overall buying cost
for (it = unique_eids.begin(); it != unique_eids.end(); it++) {
for (int j = 0; j < it->second; j++) {
estimate.AddCost(DoCommand(tile, it->first, 0, DC_NONE, CMD_BUILD_VEHICLE));
}
}
return estimate;
}
// make sure the real train wagon has the right cargo
void CopyWagonStatus(TemplateVehicle *from, Train *to)
{
to->cargo_type = from->cargo_type;
to->cargo_subtype = from->cargo_subtype;
}
int NumTrainsNeedTemplateReplacement(GroupID g_id, TemplateVehicle *tv)
{
int count = 0;
if (!tv) return count;
const Train *t;
FOR_ALL_TRAINS(t) {
if (t->IsPrimaryVehicle() && t->group_id == g_id && (!TrainMatchesTemplate(t, tv) || !TrainMatchesTemplateRefit(t, tv))) {
count++;
}
}
return count;
}
// refit each vehicle in t as is in tv, assume t and tv contain the same types of vehicles
void CmdRefitTrainFromTemplate(Train *t, TemplateVehicle *tv, DoCommandFlag flags)
{
while (t && tv) {
// refit t as tv
uint32 cb = GetCmdRefitVeh(t);
DoCommand(t->tile, t->index, tv->cargo_type | tv->cargo_subtype << 8 | 1 << 16 | (1 << 5), flags, cb);
// next
t = t->GetNextUnit();
tv = tv->GetNextUnit();
}
}
/** using cmdtemplatereplacevehicle as test-function (i.e. with flag DC_NONE) is not a good idea as that function relies on
* actually moving vehicles around to work properly.
* We do this worst-cast test instead.
*/
CommandCost TestBuyAllTemplateVehiclesInChain(TemplateVehicle *tv, TileIndex tile)
{
CommandCost cost(EXPENSES_NEW_VEHICLES);
for (; tv; tv = tv->GetNextUnit()) {
cost.AddCost(DoCommand(tile, tv->engine_type, 0, DC_NONE, CMD_BUILD_VEHICLE));
}
return cost;
}
/** Transfer as much cargo from a given (single train) vehicle onto a chain of vehicles.
* I.e., iterate over the chain from head to tail and use all available cargo capacity (w.r.t. cargo type of course)
* to store the cargo from the given single vehicle.
* @param old_veh: ptr to the single vehicle, which's cargo shall be moved
* @param new_head: ptr to the head of the chain, which shall obtain old_veh's cargo
* @return: amount of moved cargo, TODO
*/
void TransferCargoForTrain(Train *old_veh, Train *new_head)
{
assert(new_head->IsPrimaryVehicle());
CargoID _cargo_type = old_veh->cargo_type;
byte _cargo_subtype = old_veh->cargo_subtype;
// how much cargo has to be moved (if possible)
uint remainingAmount = old_veh->cargo.TotalCount();
// each vehicle in the new chain shall be given as much of the old cargo as possible, until none is left
for (Train *tmp = new_head; tmp != NULL && remainingAmount > 0; tmp = tmp->GetNextUnit()) {
if (tmp->cargo_type == _cargo_type && tmp->cargo_subtype == _cargo_subtype) {
// calculate the free space for new cargo on the current vehicle
uint curCap = tmp->cargo_cap - tmp->cargo.TotalCount();
uint moveAmount = min(remainingAmount, curCap);
// move (parts of) the old vehicle's cargo onto the current vehicle of the new chain
if (moveAmount > 0) {
old_veh->cargo.Shift(moveAmount, &tmp->cargo);
remainingAmount -= moveAmount;
}
}
}
// TODO: needs to be implemented, too
// // from autoreplace_cmd.cpp : 121
/* Any left-overs will be thrown away, but not their feeder share. */
//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(CCF_LOADUNLOAD);
}

@ -0,0 +1,83 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file tbtr_template_vehicle_func.h Template-based train replacement: template vehicle functions headers. */
#ifndef TEMPLATE_VEHICLE_FUNC_H
#define TEMPLATE_VEHICLE_FUNC_H
#include "stdafx.h"
#include "window_gui.h"
#include "tbtr_template_vehicle.h"
Train* VirtualTrainFromTemplateVehicle(TemplateVehicle* tv);
void DrawTemplateVehicle(const TemplateVehicle*, int, int, int, VehicleID, int, VehicleID);
void BuildTemplateGuiList(GUITemplateList*, Scrollbar*, Owner, RailType);
Money CalculateOverallTemplateCost(const TemplateVehicle*);
void DrawTemplateTrain(const TemplateVehicle*, int, int, int);
SpriteID GetSpriteID(EngineID, bool);
void DrawTemplate(const TemplateVehicle*, int, int, int);
int GetTemplateDisplayImageWidth(EngineID);
TemplateVehicle *CreateNewTemplateVehicle(EngineID);
void setupVirtTrain(const TemplateVehicle*, Train*);
TemplateVehicle* TemplateVehicleFromVirtualTrain(Train *virt);
inline TemplateVehicle* Last(TemplateVehicle*);
TemplateVehicle *DeleteTemplateVehicle(TemplateVehicle*);
Train* DeleteVirtualTrainPart(Train*, Train*);
Train* DeleteVirtualTrain(Train*, Train *);
CommandCost CmdTemplateReplaceVehicle(Train*, bool, DoCommandFlag);
#ifdef _DEBUG
// for testing
void tbtr_debug_pat();
void tbtr_debug_pav();
void tbtr_debug_ptv(TemplateVehicle*);
void tbtr_debug_pvt(const Train*);
#endif
TemplateVehicle* GetTemplateVehicleByGroupID(GroupID);
bool ChainContainsVehicle(Train*, Train*);
Train* ChainContainsEngine(EngineID, Train*);
Train* DepotContainsEngine(TileIndex, EngineID, Train*);
int NumTrainsNeedTemplateReplacement(GroupID, TemplateVehicle*);
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);
void TransferCargoForTrain(Train*, Train*);
void NeutralizeStatus(Train *t);
bool TrainMatchesTemplate(const Train *t, TemplateVehicle *tv);
bool TrainMatchesTemplateRefit(const Train *t, TemplateVehicle *tv);
#endif

@ -137,6 +137,7 @@ struct Train FINAL : public GroundVehicle<Train, VEH_TRAIN> {
bool Tick();
void OnNewDay();
uint Crash(bool flooded = false);
Money CalculateCurrentOverallValue() const;
Trackdir GetVehicleTrackdir() const;
TileIndex GetOrderStationLocation(StationID station);
bool FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse);
@ -177,6 +178,17 @@ struct Train FINAL : public GroundVehicle<Train, VEH_TRAIN> {
return v;
}
/* Get the last vehicle of a chain
* @return pointer the last vehicle in a chain
*/
inline Train *GetLastUnit() {
Train *tmp = this;
while (tmp->GetNextUnit()) {
tmp = tmp->GetNextUnit();
}
return tmp;
}
/**
* Calculate the offset from this vehicle's center to the following center taking the vehicle lengths into account.
* @return Offset from center to center.
@ -368,6 +380,13 @@ protected: // These functions should not be called outside acceleration code.
}
};
CommandCost CmdMoveRailVehicle(TileIndex, DoCommandFlag , uint32, uint32, const char *);
CommandCost CmdMoveVirtualRailVehicle(TileIndex, DoCommandFlag, uint32, uint32, const char*);
Train* CmdBuildVirtualRailWagon(const Engine*);
Train* CmdBuildVirtualRailVehicle(EngineID);
#define FOR_ALL_TRAINS(var) FOR_ALL_VEHICLES_OF_TYPE(Train, var)
#endif /* TRAIN_H */

@ -37,6 +37,9 @@
#include "zoom_func.h"
#include "newgrf_debug.h"
#include "tracerestrict.h"
#include "tbtr_template_vehicle_func.h"
#include "autoreplace_func.h"
#include "engine_func.h"
#include "table/strings.h"
#include "table/train_cmd.h"
@ -308,7 +311,7 @@ void Train::ConsistChanged(ConsistChangeFlags allowed_changes)
if (this->IsFrontEngine()) {
this->UpdateAcceleration();
SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
if (!HasBit(this->subtype, GVSF_VIRTUAL)) SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
InvalidateWindowData(WC_VEHICLE_REFIT, this->index, VIWD_CONSIST_CHANGED);
InvalidateWindowData(WC_VEHICLE_ORDERS, this->index, VIWD_CONSIST_CHANGED);
InvalidateNewGRFInspectWindow(GSF_TRAINS, this->index);
@ -1225,6 +1228,7 @@ static void NormaliseTrainHead(Train *head)
* @param p1 various bitstuffed elements
* - p1 (bit 0 - 19) source vehicle index
* - p1 (bit 20) move all vehicles following the source vehicle
* - p1 (bit 21) this is a virtual vehicle (for creating TemplateVehicles)
* @param p2 what wagon to put the source wagon AFTER, XXX - INVALID_VEHICLE to make a new line
* @param text unused
* @return the cost of this operation or an error
@ -1289,10 +1293,16 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u
if (!move_chain && dst != NULL && dst->IsRearDualheaded() && src == dst->other_multiheaded_part) return CommandCost();
/* Check if all vehicles in the source train are stopped inside a depot. */
if (!src_head->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
/* Do this check only if the vehicle to be moved is non-virtual */
if (!HasBit(p1, 21)) {
if (!src_head->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
}
/* Check if all vehicles in the destination train are stopped inside a depot. */
if (dst_head != NULL && !dst_head->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
/* Do this check only if the destination vehicle is non-virtual */
if (!HasBit(p1, 21)) {
if (dst_head != NULL && !dst_head->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
}
/* First make a backup of the order of the trains. That way we can do
* whatever we want with the order and later on easily revert. */
@ -1401,8 +1411,11 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u
if (dst_head != NULL) dst_head->First()->MarkDirty();
/* We are undoubtedly changing something in the depot and train list. */
InvalidateWindowData(WC_VEHICLE_DEPOT, src->tile);
InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
/* But only if the moved vehicle is not virtual */
if (!HasBit(src->subtype, GVSF_VIRTUAL)) {
InvalidateWindowData(WC_VEHICLE_DEPOT, src->tile);
InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
}
} else {
/* We don't want to execute what we're just tried. */
RestoreTrainBackup(original_src);
@ -1485,8 +1498,11 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, uint16 data, uint3
NormaliseTrainHead(new_head);
/* We are undoubtedly changing something in the depot and train list. */
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
/* Unless its a virtual train */
if (!HasBit(v->subtype, GVSF_VIRTUAL)) {
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
}
/* Actually delete the sold 'goods' */
delete sell_head;
@ -4199,6 +4215,16 @@ static bool TrainCheckIfLineEnds(Train *v, bool reverse)
return true;
}
/* Calculate the summed up value of all parts of a train */
Money Train::CalculateCurrentOverallValue() const
{
Money ovr_value = 0;
const Train *v = this;
do {
ovr_value += v->value;
} while ((v = v->GetNextVehicle()) != NULL);
return ovr_value;
}
static bool TrainLocoHandler(Train *v, bool mode)
{
@ -4543,3 +4569,389 @@ void DeleteVisibleTrain(Train *v)
UpdateSignalsInBuffer();
}
/* Get the pixel-width of the image that is used for the train vehicle
* @return: the image width number in pixel
*/
int GetDisplayImageWidth(Train *t, Point *offset)
{
int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
int vehicle_pitch = 0;
const Engine *e = Engine::Get(t->engine_type);
if (e->grf_prop.grffile != NULL && is_custom_sprite(e->u.rail.image_index)) {
reference_width = e->grf_prop.grffile->traininfo_vehicle_width;
vehicle_pitch = e->grf_prop.grffile->traininfo_vehicle_pitch;
}
if (offset != NULL) {
offset->x = reference_width / 2;
offset->y = vehicle_pitch;
}
return t->gcache.cached_veh_length * reference_width / VEHICLE_LENGTH;
}
Train* CmdBuildVirtualRailWagon(const Engine *e)
{
const RailVehicleInfo *rvi = &e->u.rail;
Train *v = new Train();
v->x_pos = 0;
v->y_pos = 0;
v->spritenum = rvi->image_index;
v->engine_type = e->index;
v->gcache.first_engine = INVALID_ENGINE; // needs to be set before first callback
v->direction = DIR_W;
v->tile = 0; // INVALID_TILE;
v->owner = _current_company;
v->track = TRACK_BIT_DEPOT;
v->vehstatus = VS_HIDDEN | VS_DEFPAL;
v->SetWagon();
v->SetFreeWagon();
v->cargo_type = e->GetDefaultCargoType();
v->cargo_cap = rvi->capacity;
v->railtype = rvi->railtype;
v->build_year = _cur_year;
v->cur_image = SPR_IMG_QUERY;
v->random_bits = VehicleRandomBits();
v->group_id = DEFAULT_GROUP;
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;
v->UpdateViewport(true, false);
v->First()->ConsistChanged(CCF_ARRANGE);
CheckConsistencyOfArticulatedVehicle(v);
return v;
}
/**
* Build a railroad vehicle.
* @param tile tile of the depot where rail-vehicle is built.
* @param flags type of operation.
* @param e the engine to build.
* @param data bit 0 prevents any free cars from being added to the train.
* @param ret[out] the vehicle that has been built.
* @return the cost of this operation or an error.
*/
Train* CmdBuildVirtualRailVehicle(EngineID eid)
{
if (!IsEngineBuildable(eid, VEH_TRAIN, _current_company)) {
return NULL;
}
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 NULL;
}
if (rvi->railveh_type == RAILVEH_WAGON) {
return CmdBuildVirtualRailWagon(e);
}
Train *v = new Train();
v->x_pos = 0;
v->y_pos = 0;
v->direction = DIR_W;
v->tile = 0; // INVALID_TILE;
v->owner = _current_company;
v->track = TRACK_BIT_DEPOT;
v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
v->spritenum = rvi->image_index;
v->cargo_type = e->GetDefaultCargoType();
v->cargo_cap = rvi->capacity;
v->last_station_visited = INVALID_STATION;
v->engine_type = e->index;
v->gcache.first_engine = INVALID_ENGINE; // needs to be set before first callback
v->reliability = e->reliability;
v->reliability_spd_dec = e->reliability_spd_dec;
v->max_age = e->GetLifeLengthInDays();
v->railtype = rvi->railtype;
_new_vehicle_id = v->index;
v->cur_image = SPR_IMG_QUERY;
v->random_bits = VehicleRandomBits();
v->group_id = DEFAULT_GROUP;
v->SetFrontEngine();
v->SetEngine();
v->UpdateViewport(true, false);
if (rvi->railveh_type == RAILVEH_MULTIHEAD) {
AddRearEngineToMultiheadedTrain(v);
} else {
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();
}
v->ConsistChanged(CCF_ARRANGE);
CheckConsistencyOfArticulatedVehicle(v);
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;
bool should_execute = (flags & DC_EXEC) != 0;
if (should_execute) {
Train* train = CmdBuildVirtualRailVehicle(eid);
if (train == NULL) {
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 == NULL || 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 = NULL;
Train *remainder_chain = NULL;
Train *tmp_chain = NULL;
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 == NULL) 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 != NULL; 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_cmd_error(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY);
}
}
/* 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);
} 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;
}

@ -54,6 +54,7 @@
#include "linkgraph/linkgraph.h"
#include "linkgraph/refresh.h"
#include "blitter/factory.hpp"
#include "tbtr_template_vehicle_func.h"
#include "table/strings.h"
@ -703,6 +704,13 @@ void ResetVehicleColourMap()
typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
static AutoreplaceMap _vehicles_to_autoreplace;
/**
* List of vehicles that are issued for template replacement this tick.
* Mapping is {vehicle : leave depot after replacement}
*/
typedef SmallMap<Train *, bool, 4> TemplateReplacementMap;
static TemplateReplacementMap _vehicles_to_templatereplace;
void InitializeVehicles()
{
_vehicles_to_autoreplace.Reset();
@ -905,8 +913,18 @@ Vehicle::~Vehicle()
*/
void VehicleEnteredDepotThisTick(Vehicle *v)
{
/* Vehicle should stop in the depot if it was in 'stopping' state */
_vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
/* Template Replacement Setup stuff */
bool stayInDepot = v->current_order.GetDepotActionType();
TemplateReplacement *tr = GetTemplateReplacementByGroupID(v->group_id);
if (tr != NULL) {
_vehicles_to_templatereplace[(Train*) v] = stayInDepot;
} else {
/* Moved the assignment for auto replacement here to prevent auto replacement
* from happening if template replacement is also scheduled */
/* Vehicle should stop in the depot if it was in 'stopping' state */
_vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
}
/* We ALWAYS set the stopped state. Even when the vehicle does not plan on
* stopping in the depot, so we stop it to ensure that it will not reserve
@ -951,9 +969,29 @@ static void RunVehicleDayProc()
}
}
static void ShowAutoReplaceAdviceMessage(const CommandCost &res, const Vehicle *v)
{
StringID error_message = res.GetErrorMessage();
if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) return;
if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
StringID message;
if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
message = error_message;
} else {
message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
}
SetDParam(0, v->index);
SetDParam(1, error_message);
AddVehicleAdviceNewsItem(message, v->index);
}
void CallVehicleTicks()
{
_vehicles_to_autoreplace.Clear();
_vehicles_to_templatereplace.Clear();
if (_tick_skip_counter == 0) RunVehicleDayProc();
@ -1039,6 +1077,7 @@ void CallVehicleTicks()
}
}
/* do Auto Replacement */
Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
v = it->first;
@ -1067,24 +1106,39 @@ void CallVehicleTicks()
continue;
}
StringID error_message = res.GetErrorMessage();
if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
ShowAutoReplaceAdviceMessage(res, v);
}
cur_company.Restore();
if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
/* do Template Replacement */
Backup<CompanyByte> tmpl_cur_company(_current_company, FILE_LINE);
for (TemplateReplacementMap::iterator it = _vehicles_to_templatereplace.Begin(); it != _vehicles_to_templatereplace.End(); it++) {
Train *t = it->first;
StringID message;
if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
message = error_message;
} else {
message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
/* Store the position of the effect as the vehicle pointer will become invalid later */
int x = t->x_pos;
int y = t->y_pos;
int z = t->z_pos;
tmpl_cur_company.Change(t->owner);
bool stayInDepot = it->second;
it->first->vehstatus |= VS_STOPPED;
CommandCost res = DoCommand(t->tile, t->index, stayInDepot ? 1 : 0, DC_EXEC, CMD_TEMPLATE_REPLACE_VEHICLE);
if (!IsLocalCompany()) continue;
if (res.Succeeded()) {
if (res.GetCost() != 0) {
ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
}
continue;
}
SetDParam(0, v->index);
SetDParam(1, error_message);
AddVehicleAdviceNewsItem(message, v->index);
ShowAutoReplaceAdviceMessage(res, t);
}
cur_company.Restore();
tmpl_cur_company.Restore();
}
/**

@ -28,6 +28,8 @@
#include <list>
#include <map>
CommandCost CmdRefitVehicle(TileIndex, DoCommandFlag, uint32, uint32, const char*);
/** Vehicle status bits in #Vehicle::vehstatus. */
enum VehStatus {
VS_HIDDEN = 0x01, ///< Vehicle is not visible.
@ -122,6 +124,7 @@ enum GroundVehicleSubtypeFlags {
GVSF_ENGINE = 3, ///< Engine that can be front engine, but might be placed behind another engine (not used for road vehicles).
GVSF_FREE_WAGON = 4, ///< First in a wagon chain (in depot) (not used for road vehicles).
GVSF_MULTIHEADED = 5, ///< Engine is multiheaded (not used for road vehicles).
GVSF_VIRTUAL = 6, ///< Used for virtual trains during template design, it is needed to skip checks for tile or depot status
};
/** Cached often queried values common to all vehicles. */
@ -543,6 +546,7 @@ public:
Money GetDisplayProfitLifetime() const { return ((this->profit_lifetime + this->profit_this_year) >> 8); }
void SetNext(Vehicle *next);
inline void SetFirst(Vehicle *f) { this->first = f; }
/**
* Get the next vehicle of this vehicle.

@ -32,6 +32,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"
@ -71,7 +73,6 @@ CommandCost CmdBuildRailVehicle(TileIndex tile, DoCommandFlag flags, const Engin
CommandCost CmdBuildRoadVehicle(TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **v);
CommandCost CmdBuildShip (TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **v);
CommandCost CmdBuildAircraft (TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **v);
CommandCost CmdRefitVehicle (TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text);
static CommandCost GetRefitCost(const Vehicle *v, EngineID engine_type, CargoID new_cid, byte new_subtype, bool *auto_refit_allowed);
/**
@ -208,7 +209,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 &&
@ -455,6 +459,7 @@ static CommandCost RefitVehicle(Vehicle *v, bool only_this, uint8 num_vehicles,
* @param p1 vehicle ID to refit
* @param p2 various bitstuffed elements
* - p2 = (bit 0-4) - New cargo type to refit to.
* - p2 = (bit 5) - Is a virtual train (used by template replacement to allow refitting without stopped-in-depot checks)
* - p2 = (bit 6) - Automatic refitting.
* - p2 = (bit 7) - Refit only this vehicle. Used only for cloning vehicles.
* - p2 = (bit 8-15) - New cargo subtype to refit to. 0xFF means to try keeping the same subtype according to GetBestFittingSubType().
@ -478,12 +483,19 @@ CommandCost CmdRefitVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
if (ret.Failed()) return ret;
bool auto_refit = HasBit(p2, 6);
bool is_virtual_train = HasBit(p2, 5);
bool free_wagon = v->type == VEH_TRAIN && Train::From(front)->IsFreeWagon(); // used by autoreplace/renew
/* Don't allow shadows and such to be refitted. */
if (v != front && (v->type == VEH_SHIP || v->type == VEH_AIRCRAFT)) return CMD_ERROR;
/* Allow auto-refitting only during loading and normal refitting only in a depot. */
if (!is_virtual_train) {
if (!free_wagon && (!auto_refit || !front->current_order.IsType(OT_LOADING)) && !front->IsStoppedInDepot()) {
return_cmd_error(STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT + front->type);
}
if (front->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_VEHICLE_IS_DESTROYED);
}
if ((flags & DC_QUERY_COST) == 0 && // used by the refit GUI, including the order refit GUI.
!free_wagon && // used by autoreplace/renew
(!auto_refit || !front->current_order.IsType(OT_LOADING)) && // refit inside stations
@ -533,7 +545,12 @@ CommandCost CmdRefitVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
InvalidateWindowData(WC_VEHICLE_DETAILS, front->index);
InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
}
SetWindowDirty(WC_VEHICLE_DEPOT, front->tile);
/* virtual vehicles get their cargo changed by the TemplateCreateWindow, so set this dirty instead of a depot window */
if (HasBit(v->subtype, GVSF_VIRTUAL)) {
SetWindowClassesDirty(WC_CREATE_TEMPLATE);
} else {
SetWindowDirty(WC_VEHICLE_DEPOT, front->tile);
}
} else {
/* Always invalidate the cache; querycost might have filled it. */
v->InvalidateNewGRFCacheOfChain();
@ -805,6 +822,453 @@ 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 == NULL) {
return CMD_ERROR;
}
}
return CommandCost();
}
Train* VirtualTrainFromTemplateVehicle(TemplateVehicle* tv)
{
CommandCost c;
Train *tmp, *head, *tail;
head = CmdBuildVirtualRailVehicle(tv->engine_type);
if (!head) return NULL;
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 == NULL || 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 == NULL || 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 == NULL || 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 != NULL) {
old_ID = template_vehicle->index;
delete template_vehicle;
template_vehicle = NULL;
}
template_vehicle = TemplateVehicleFromVirtualTrain(train);
if (template_vehicle == NULL) {
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;
TemplateVehicle *prev = NULL;
for (; clicked != NULL; 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
@ -1049,6 +1513,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

@ -39,6 +39,7 @@
#include "infrastructure_func.h"
#include "tilehighlight_func.h"
#include "train.h"
#include "tbtr_template_gui_main.h"
#include "zoom_func.h"
#include "safeguards.h"
@ -150,11 +151,14 @@ void BaseVehicleListWindow::BuildVehicleList()
* @param show_group If true include group-related stuff.
* @return Required size.
*/
Dimension BaseVehicleListWindow::GetActionDropdownSize(bool show_autoreplace, bool show_group, StringID change_order_str)
Dimension BaseVehicleListWindow::GetActionDropdownSize(bool show_autoreplace, bool show_group, bool show_template_replace, StringID change_order_str)
{
Dimension d = {0, 0};
if (show_autoreplace) d = maxdim(d, GetStringBoundingBox(STR_VEHICLE_LIST_REPLACE_VEHICLES));
if (show_autoreplace && show_template_replace) {
d = maxdim(d, GetStringBoundingBox(STR_TMPL_TEMPLATE_REPLACEMENT));
}
d = maxdim(d, GetStringBoundingBox(STR_VEHICLE_LIST_SEND_FOR_SERVICING));
d = maxdim(d, GetStringBoundingBox(this->vehicle_depot_name[this->vli.vtype]));
@ -176,11 +180,14 @@ Dimension BaseVehicleListWindow::GetActionDropdownSize(bool show_autoreplace, bo
* @param show_group If true include group-related stuff.
* @return Itemlist for dropdown
*/
DropDownList *BaseVehicleListWindow::BuildActionDropdownList(bool show_autoreplace, bool show_group, StringID change_order_str)
DropDownList *BaseVehicleListWindow::BuildActionDropdownList(bool show_autoreplace, bool show_group, bool show_template_replace, StringID change_order_str)
{
DropDownList *list = new DropDownList();
if (show_autoreplace) *list->Append() = new DropDownListStringItem(STR_VEHICLE_LIST_REPLACE_VEHICLES, ADI_REPLACE, false);
if (show_autoreplace && show_template_replace) {
*list->Append() = new DropDownListStringItem(STR_TMPL_TEMPLATE_REPLACEMENT, ADI_TEMPLATE_REPLACE, false);
}
*list->Append() = new DropDownListStringItem(STR_VEHICLE_LIST_SEND_FOR_SERVICING, ADI_SERVICE, false);
*list->Append() = new DropDownListStringItem(this->vehicle_depot_name[this->vli.vtype], ADI_DEPOT, false);
@ -440,6 +447,7 @@ struct RefitWindow : public Window {
VehicleID selected_vehicle; ///< First vehicle in the current selection.
uint8 num_vehicles; ///< Number of selected vehicles.
bool auto_refit; ///< Select cargo for auto-refitting.
bool is_virtual_train; ///< TemplateReplacement, whether the selected vehicle is virtual
/**
* Collects all (cargo, subcargo) refit options of a vehicle chain.
@ -621,11 +629,12 @@ struct RefitWindow : public Window {
return &l[this->sel[1]];
}
RefitWindow(WindowDesc *desc, const Vehicle *v, VehicleOrderID order, bool auto_refit) : Window(desc)
RefitWindow(WindowDesc *desc, const Vehicle *v, VehicleOrderID order, bool auto_refit, bool is_virtual) : Window(desc)
{
this->sel[0] = -1;
this->sel[1] = 0;
this->auto_refit = auto_refit;
this->is_virtual_train = is_virtual;
this->order = order;
this->CreateNestedTree();
@ -1015,9 +1024,12 @@ struct RefitWindow : public Window {
if (this->order == INVALID_VEH_ORDER_ID) {
bool delete_window = this->selected_vehicle == v->index && this->num_vehicles == UINT8_MAX;
if (DoCommandP(v->tile, this->selected_vehicle, this->cargo->cargo | this->cargo->subtype << 8 | this->num_vehicles << 16, GetCmdRefitVeh(v)) && delete_window) delete this;
if (DoCommandP(v->tile, this->selected_vehicle, this->cargo->cargo | this->cargo->subtype << 8 | this->num_vehicles << 16 | this->is_virtual_train << 5,
GetCmdRefitVeh(v)) && delete_window) {
delete this;
}
} else {
if (DoCommandP(v->tile, v->index, this->cargo->cargo | this->order << 16, CMD_ORDER_REFIT)) delete this;
if (DoCommandP(v->tile, v->index, this->cargo->cargo | this->cargo->subtype << 8 | this->order << 16 | this->is_virtual_train << 5, CMD_ORDER_REFIT)) delete this;
}
}
break;
@ -1098,10 +1110,10 @@ static WindowDesc _vehicle_refit_desc(
* @param parent the parent window of the refit window
* @param auto_refit Choose cargo for auto-refitting
*/
void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit)
void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit, bool is_virtual_train)
{
DeleteWindowById(WC_VEHICLE_REFIT, v->index);
RefitWindow *w = new RefitWindow(&_vehicle_refit_desc, v, order, auto_refit);
RefitWindow *w = new RefitWindow(&_vehicle_refit_desc, v, order, auto_refit, is_virtual_train);
w->parent = parent;
}
@ -1614,7 +1626,8 @@ public:
}
case WID_VL_MANAGE_VEHICLES_DROPDOWN: {
Dimension d = this->GetActionDropdownSize(this->vli.type == VL_STANDARD, false, this->GetChangeOrderStringID());
Dimension d = this->GetActionDropdownSize(this->vli.type == VL_STANDARD, false,
this->vli.vtype == VEH_TRAIN, this->GetChangeOrderStringID());
d.height += padding.height;
d.width += padding.width;
*size = maxdim(*size, d);
@ -1740,7 +1753,7 @@ public:
case WID_VL_MANAGE_VEHICLES_DROPDOWN: {
DropDownList *list = this->BuildActionDropdownList(VehicleListIdentifier(this->window_number).type == VL_STANDARD, false,
this->GetChangeOrderStringID());
this->vli.vtype == VEH_TRAIN, this->GetChangeOrderStringID());
ShowDropDownList(this, list, 0, WID_VL_MANAGE_VEHICLES_DROPDOWN);
break;
}
@ -1765,6 +1778,11 @@ public:
case ADI_REPLACE: // Replace window
ShowReplaceGroupVehicleWindow(ALL_GROUP, this->vli.vtype);
break;
case ADI_TEMPLATE_REPLACE:
if (vli.vtype == VEH_TRAIN) {
ShowTemplateReplaceWindow(this->unitnumber_digits, this->resize.step_height);
}
break;
case ADI_SERVICE: // Send for servicing
case ADI_DEPOT: // Send to Depots
DoCommandP(0, DEPOT_MASS_SEND | (index == ADI_SERVICE ? DEPOT_SERVICE : (DepotCommand)0), this->window_number, GetCmdSendToDepot(this->vli.vtype));

@ -20,7 +20,7 @@
#include "company_type.h"
#include "widgets/dropdown_func.h"
void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit = false);
void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit = false, bool is_virtual_train = false);
/** The tabs in the train details window */
enum TrainDetailsWindowTabs {

@ -27,6 +27,7 @@ struct BaseVehicleListWindow : public Window {
VehicleListIdentifier vli; ///< Identifier of the vehicle list we want to currently show.
enum ActionDropdownItem {
ADI_TEMPLATE_REPLACE,
ADI_REPLACE,
ADI_SERVICE,
ADI_DEPOT,
@ -47,8 +48,8 @@ struct BaseVehicleListWindow : public Window {
void DrawVehicleListItems(VehicleID selected_vehicle, int line_height, const Rect &r) const;
void SortVehicleList();
void BuildVehicleList();
Dimension GetActionDropdownSize(bool show_autoreplace, bool show_group, StringID change_order_str = 0);
DropDownList *BuildActionDropdownList(bool show_autoreplace, bool show_group, StringID change_order_str = 0);
Dimension GetActionDropdownSize(bool show_autoreplace, bool show_group, bool show_template_replace, StringID change_order_str = 0);
DropDownList *BuildActionDropdownList(bool show_autoreplace, bool show_group, bool show_template_replace, StringID change_order_str = 0);
};
uint GetVehicleListHeight(VehicleType type, uint divisor = 1);

@ -147,7 +147,7 @@ bool GenerateVehicleSortList(VehicleList *list, const VehicleListIdentifier &vli
case VL_GROUP_LIST:
if (vli.index != ALL_GROUP) {
FOR_ALL_VEHICLES(v) {
if (v->type == vli.vtype && v->IsPrimaryVehicle() &&
if (!HasBit(v->subtype, GVSF_VIRTUAL) && v->type == vli.vtype && v->IsPrimaryVehicle() &&
v->owner == vli.company && GroupIsInGroup(v->group_id, vli.index)) {
*list->Append() = v;
}
@ -158,7 +158,7 @@ bool GenerateVehicleSortList(VehicleList *list, const VehicleListIdentifier &vli
case VL_STANDARD:
FOR_ALL_VEHICLES(v) {
if (v->type == vli.vtype && v->owner == vli.company && v->IsPrimaryVehicle()) {
if (!HasBit(v->subtype, GVSF_VIRTUAL) && v->type == vli.vtype && v->owner == vli.company && v->IsPrimaryVehicle()) {
*list->Append() = v;
}
}

@ -3297,9 +3297,10 @@ bool HandleViewportClicked(const ViewPort *vp, int x, int y)
DEBUG(misc, 2, "Vehicle %d (index %d) at %p", v->unitnumber, v->index, v);
if (IsCompanyBuildableVehicleType(v)) {
v = v->First();
WindowClass wc = _thd.GetCallbackWnd()->window_class;
if (_ctrl_pressed && v->owner == _local_company) {
StartStopVehicle(v, true);
} else {
} else if (wc != WC_CREATE_TEMPLATE && wc != WC_TEMPLATEGUI_MAIN) {
ShowVehicleViewWindow(v);
}
}

@ -715,6 +715,11 @@ enum WindowClass {
*/
WC_PLANS,
WC_TEMPLATEGUI_MAIN,
WC_TEMPLATEGUI_RPLALL,
WC_BUILD_VIRTUAL_TRAIN,
WC_CREATE_TEMPLATE,
WC_INVALID = 0xFFFF, ///< Invalid window.
};

Loading…
Cancel
Save