diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -504,6 +504,7 @@ add_files( vehicle_type.h vehiclelist.cpp vehiclelist.h + vehiclelist_cmd.h viewport.cpp viewport_cmd.h viewport_func.h diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -403,7 +403,7 @@ static CommandCost CopyHeadSpecificThing if (cost.Succeeded() && old_head != new_head) cost.AddCost(Command::Do(DC_EXEC, CO_SHARE, new_head->index, old_head->index)); /* Copy group membership */ - if (cost.Succeeded() && old_head != new_head) cost.AddCost(std::get<0>(Command::Do(DC_EXEC, old_head->group_id, new_head->index, false))); + if (cost.Succeeded() && old_head != new_head) cost.AddCost(std::get<0>(Command::Do(DC_EXEC, old_head->group_id, new_head->index, false, VehicleListIdentifier{}))); /* Perform start/stop check whether the new vehicle suits newgrf restrictions etc. */ if (cost.Succeeded()) { diff --git a/src/group_cmd.cpp b/src/group_cmd.cpp --- a/src/group_cmd.cpp +++ b/src/group_cmd.cpp @@ -512,48 +512,63 @@ static void AddVehicleToGroup(Vehicle *v * @param add_shared Add shared vehicles as well. * @return the cost of this operation or an error */ -std::tuple CmdAddVehicleGroup(DoCommandFlag flags, GroupID group_id, VehicleID veh_id, bool add_shared) +std::tuple CmdAddVehicleGroup(DoCommandFlag flags, GroupID group_id, VehicleID veh_id, bool add_shared, const VehicleListIdentifier &vli) { - Vehicle *v = Vehicle::GetIfValid(veh_id); GroupID new_g = group_id; + if (!Group::IsValidID(new_g) && !IsDefaultGroupID(new_g) && new_g != NEW_GROUP) return { CMD_ERROR, INVALID_GROUP }; - if (v == nullptr || (!Group::IsValidID(new_g) && !IsDefaultGroupID(new_g) && new_g != NEW_GROUP)) return { CMD_ERROR, INVALID_GROUP }; + VehicleList list; + if (veh_id == INVALID_VEHICLE && vli.Valid()) { + if (!GenerateVehicleSortList(&list, vli) || list.empty()) return { CMD_ERROR, INVALID_GROUP }; + } else { + Vehicle *v = Vehicle::GetIfValid(veh_id); + if (v == nullptr) return { CMD_ERROR, INVALID_GROUP }; + list.push_back(v); + } + + VehicleType vtype = list.front()->type; + for (const Vehicle *v : list) { + if (v->owner != _current_company || !v->IsPrimaryVehicle()) return { CMD_ERROR, INVALID_GROUP }; + } if (Group::IsValidID(new_g)) { Group *g = Group::Get(new_g); - if (g->owner != _current_company || g->vehicle_type != v->type) return { CMD_ERROR, INVALID_GROUP }; + if (g->owner != _current_company || g->vehicle_type != vtype) return { CMD_ERROR, INVALID_GROUP }; } - if (v->owner != _current_company || !v->IsPrimaryVehicle()) return { CMD_ERROR, INVALID_GROUP }; - if (new_g == NEW_GROUP) { /* Create new group. */ - auto [ret, new_group_id] = CmdCreateGroup(flags, v->type, INVALID_GROUP); + auto [ret, new_group_id] = CmdCreateGroup(flags, vtype, INVALID_GROUP); if (ret.Failed()) return { ret, new_group_id }; new_g = new_group_id; } if (flags & DC_EXEC) { - AddVehicleToGroup(v, new_g); + for (const Vehicle *vc : list) { + /* VehicleList is const but we need to modify the vehicle. */ + Vehicle *v = Vehicle::Get(vc->index); + AddVehicleToGroup(v, new_g); - if (add_shared) { - /* Add vehicles in the shared order list as well. */ - for (Vehicle *v2 = v->FirstShared(); v2 != nullptr; v2 = v2->NextShared()) { - if (v2->group_id != new_g) AddVehicleToGroup(v2, new_g); + if (add_shared) { + /* Add vehicles in the shared order list as well. */ + for (Vehicle *v2 = v->FirstShared(); v2 != nullptr; v2 = v2->NextShared()) { + if (v2->group_id != new_g) AddVehicleToGroup(v2, new_g); + } } + + SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); + SetWindowDirty(WC_VEHICLE_VIEW, v->index); + SetWindowDirty(WC_VEHICLE_DETAILS, v->index); + InvalidateWindowData(WC_VEHICLE_VIEW, v->index); + InvalidateWindowData(WC_VEHICLE_DETAILS, v->index); } - GroupStatistics::UpdateAutoreplace(v->owner); + GroupStatistics::UpdateAutoreplace(_current_company); /* Update the Replace Vehicle Windows */ - SetWindowDirty(WC_REPLACE_VEHICLE, v->type); - SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); - SetWindowDirty(WC_VEHICLE_VIEW, v->index); - SetWindowDirty(WC_VEHICLE_DETAILS, v->index); - InvalidateWindowData(GetWindowClassForVehicleType(v->type), VehicleListIdentifier(VL_GROUP_LIST, v->type, _current_company).Pack()); - InvalidateWindowData(WC_VEHICLE_VIEW, v->index); - InvalidateWindowData(WC_VEHICLE_DETAILS, v->index); + SetWindowDirty(WC_REPLACE_VEHICLE, vtype); + InvalidateWindowData(GetWindowClassForVehicleType(vtype), VehicleListIdentifier(VL_GROUP_LIST, vtype, _current_company).Pack()); } return { CommandCost(), new_g }; @@ -579,7 +594,7 @@ CommandCost CmdAddSharedVehicleGroup(DoC /* For each shared vehicles add it to the group */ for (Vehicle *v2 = v->FirstShared(); v2 != nullptr; v2 = v2->NextShared()) { - if (v2->group_id != id_g) Command::Do(flags, id_g, v2->index, false); + if (v2->group_id != id_g) Command::Do(flags, id_g, v2->index, false, VehicleListIdentifier{}); } } } @@ -610,7 +625,7 @@ CommandCost CmdRemoveAllVehiclesGroup(Do if (v->group_id != group_id) continue; /* Add The Vehicle to the default group */ - Command::Do(flags, DEFAULT_GROUP, v->index, false); + Command::Do(flags, DEFAULT_GROUP, v->index, false, VehicleListIdentifier{}); } } diff --git a/src/group_cmd.h b/src/group_cmd.h --- a/src/group_cmd.h +++ b/src/group_cmd.h @@ -13,6 +13,8 @@ #include "command_type.h" #include "group_type.h" #include "vehicle_type.h" +#include "vehiclelist.h" +#include "vehiclelist_cmd.h" enum Colours : byte; enum GroupFlags : uint8; @@ -26,7 +28,7 @@ enum class AlterGroupMode : byte { std::tuple CmdCreateGroup(DoCommandFlag flags, VehicleType vt, GroupID parent_group); CommandCost CmdAlterGroup(DoCommandFlag flags, AlterGroupMode mode, GroupID group_id, GroupID parent_id, const std::string &text); CommandCost CmdDeleteGroup(DoCommandFlag flags, GroupID group_id); -std::tuple CmdAddVehicleGroup(DoCommandFlag flags, GroupID group_id, VehicleID veh_id, bool add_shared); +std::tuple CmdAddVehicleGroup(DoCommandFlag flags, GroupID group_id, VehicleID veh_id, bool add_shared, const VehicleListIdentifier &vli); CommandCost CmdAddSharedVehicleGroup(DoCommandFlag flags, GroupID id_g, VehicleType type); CommandCost CmdRemoveAllVehiclesGroup(DoCommandFlag flags, GroupID group_id); CommandCost CmdSetGroupFlag(DoCommandFlag flags, GroupID group_id, GroupFlags flag, bool value, bool recursive); @@ -42,6 +44,6 @@ DEF_CMD_TRAIT(CMD_SET_GROUP_FLAG, DEF_CMD_TRAIT(CMD_SET_GROUP_LIVERY, CmdSetGroupLivery, 0, CMDT_ROUTE_MANAGEMENT) void CcCreateGroup(Commands cmd, const CommandCost &result, GroupID new_group, VehicleType vt, GroupID parent_group); -void CcAddVehicleNewGroup(Commands cmd, const CommandCost &result, GroupID new_group, GroupID, VehicleID veh_id, bool); +void CcAddVehicleNewGroup(Commands cmd, const CommandCost &result, GroupID new_group, GroupID, VehicleID veh_id, bool, const VehicleListIdentifier &); #endif /* GROUP_CMD_H */ diff --git a/src/group_gui.cpp b/src/group_gui.cpp --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -428,7 +428,7 @@ public: break; case WID_GL_MANAGE_VEHICLES_DROPDOWN: { - Dimension d = this->GetActionDropdownSize(true, true); + Dimension d = this->GetActionDropdownSize(true, true, true); d.height += padding.height; d.width += padding.width; *size = maxdim(*size, d); @@ -809,7 +809,7 @@ public: break; case WID_GL_MANAGE_VEHICLES_DROPDOWN: { - ShowDropDownList(this, this->BuildActionDropdownList(true, Group::IsValidID(this->vli.index)), 0, WID_GL_MANAGE_VEHICLES_DROPDOWN); + ShowDropDownList(this, this->BuildActionDropdownList(true, Group::IsValidID(this->vli.index), IsDefaultGroupID(this->vli.index)), 0, WID_GL_MANAGE_VEHICLES_DROPDOWN); break; } @@ -865,7 +865,7 @@ public: { switch (widget) { case WID_GL_DEFAULT_VEHICLES: // Ungrouped vehicles - Command::Post(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE, DEFAULT_GROUP, this->vehicle_sel, _ctrl_pressed || this->grouping == GB_SHARED_ORDERS); + Command::Post(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE, DEFAULT_GROUP, this->vehicle_sel, _ctrl_pressed || this->grouping == GB_SHARED_ORDERS, VehicleListIdentifier{}); this->vehicle_sel = INVALID_VEHICLE; this->group_over = INVALID_GROUP; @@ -882,7 +882,7 @@ public: auto it = this->group_sb->GetScrolledItemFromWidget(this->groups, pt.y, this, WID_GL_LIST_GROUP); GroupID new_g = it == this->groups.end() ? NEW_GROUP : (*it)->index; - Command::Post(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE, new_g == NEW_GROUP ? CcAddVehicleNewGroup : nullptr, new_g, vindex, _ctrl_pressed || this->grouping == GB_SHARED_ORDERS); + Command::Post(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE, new_g == NEW_GROUP ? CcAddVehicleNewGroup : nullptr, new_g, vindex, _ctrl_pressed || this->grouping == GB_SHARED_ORDERS, VehicleListIdentifier{}); break; } @@ -975,6 +975,10 @@ public: break; } + case ADI_CREATE_GROUP: // Create group + Command::Post(CcAddVehicleNewGroup, NEW_GROUP, INVALID_VEHICLE, false, this->vli); + break; + case ADI_ADD_SHARED: // Add shared Vehicles assert(Group::IsValidID(this->vli.index)); @@ -1190,12 +1194,12 @@ void CcCreateGroup(Commands cmd, const C * @param new_group ID of the created group. * @param veh_id vehicle to add to a group */ -void CcAddVehicleNewGroup(Commands cmd, const CommandCost &result, GroupID new_group, GroupID, VehicleID veh_id, bool) +void CcAddVehicleNewGroup(Commands cmd, const CommandCost &result, GroupID new_group, GroupID, VehicleID veh_id, bool, const VehicleListIdentifier &) { if (result.Failed()) return; - assert(Vehicle::IsValidID(veh_id)); - CcCreateGroup(new_group, Vehicle::Get(veh_id)->type); + const Group *g = Group::Get(new_group); + CcCreateGroup(new_group, g->vehicle_type); } /** diff --git a/src/lang/english.txt b/src/lang/english.txt --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3849,6 +3849,7 @@ STR_VEHICLE_LIST_MANAGE_LIST STR_VEHICLE_LIST_MANAGE_LIST_TOOLTIP :{BLACK}Send instructions to all vehicles in this list STR_VEHICLE_LIST_REPLACE_VEHICLES :Replace vehicles STR_VEHICLE_LIST_SEND_FOR_SERVICING :Send for Servicing +STR_VEHICLE_LIST_CREATE_GROUP :Create group STR_VEHICLE_LIST_PROFIT_THIS_YEAR_LAST_YEAR :{TINY_FONT}{BLACK}Profit this year: {CURRENCY_LONG} (last year: {CURRENCY_LONG}) STR_VEHICLE_LIST_CARGO :[{CARGO_LIST}] STR_VEHICLE_LIST_NAME_AND_CARGO :{STRING1} {STRING1} diff --git a/src/order_backup.cpp b/src/order_backup.cpp --- a/src/order_backup.cpp +++ b/src/order_backup.cpp @@ -94,7 +94,7 @@ void OrderBackup::DoRestore(Vehicle *v) if (v->cur_implicit_order_index >= v->GetNumOrders()) v->cur_implicit_order_index = v->cur_real_order_index; /* Restore vehicle group */ - Command::Do(DC_EXEC, this->group, v->index, false); + Command::Do(DC_EXEC, this->group, v->index, false, VehicleListIdentifier{}); } /** diff --git a/src/script/api/script_group.cpp b/src/script/api/script_group.cpp --- a/src/script/api/script_group.cpp +++ b/src/script/api/script_group.cpp @@ -132,7 +132,7 @@ EnforcePrecondition(false, IsValidGroup(group_id) || group_id == GROUP_DEFAULT); EnforcePrecondition(false, ScriptVehicle::IsPrimaryVehicle(vehicle_id)); - return ScriptObject::Command::Do(group_id, vehicle_id, false); + return ScriptObject::Command::Do(group_id, vehicle_id, false, VehicleListIdentifier{}); } /* static */ bool ScriptGroup::EnableWagonRemoval(bool enable_removal) diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp --- a/src/vehicle_cmd.cpp +++ b/src/vehicle_cmd.cpp @@ -920,7 +920,7 @@ std::tuple CmdCl if (flags & DC_EXEC) { /* Cloned vehicles belong to the same group */ - Command::Do(flags, v_front->group_id, w_front->index, false); + Command::Do(flags, v_front->group_id, w_front->index, false, VehicleListIdentifier{}); } diff --git a/src/vehicle_cmd.h b/src/vehicle_cmd.h --- a/src/vehicle_cmd.h +++ b/src/vehicle_cmd.h @@ -14,6 +14,7 @@ #include "engine_type.h" #include "vehicle_type.h" #include "vehiclelist.h" +#include "vehiclelist_cmd.h" #include "cargo_type.h" std::tuple CmdBuildVehicle(DoCommandFlag flags, TileIndex tile, EngineID eid, bool use_free_vehicles, CargoID cargo, ClientID client_id); @@ -44,17 +45,6 @@ void CcBuildPrimaryVehicle(Commands cmd, void CcStartStopVehicle(Commands cmd, const CommandCost &result, VehicleID veh_id, bool); template -inline EndianBufferWriter &operator <<(EndianBufferWriter &buffer, const VehicleListIdentifier &vli) -{ - return buffer << vli.type << vli.vtype << vli.company << vli.index; -} - -inline EndianBufferReader &operator >>(EndianBufferReader &buffer, VehicleListIdentifier &vli) -{ - return buffer >> vli.type >> vli.vtype >> vli.company >> vli.index; -} - -template inline EndianBufferWriter &operator <<(EndianBufferWriter &buffer, const CargoArray &cargo_array) { for (CargoID c = 0; c < NUM_CARGO; c++) { diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -43,6 +43,7 @@ #include "roadveh_cmd.h" #include "train_cmd.h" #include "hotkeys.h" +#include "group_cmd.h" #include "safeguards.h" @@ -344,9 +345,10 @@ void BaseVehicleListWindow::FilterVehicl * Compute the size for the Action dropdown. * @param show_autoreplace If true include the autoreplace item. * @param show_group If true include group-related stuff. + * @param show_create If true include group-create item. * @return Required size. */ -Dimension BaseVehicleListWindow::GetActionDropdownSize(bool show_autoreplace, bool show_group) +Dimension BaseVehicleListWindow::GetActionDropdownSize(bool show_autoreplace, bool show_group, bool show_create) { Dimension d = {0, 0}; @@ -357,6 +359,8 @@ Dimension BaseVehicleListWindow::GetActi if (show_group) { d = maxdim(d, GetStringBoundingBox(STR_GROUP_ADD_SHARED_VEHICLE)); d = maxdim(d, GetStringBoundingBox(STR_GROUP_REMOVE_ALL_VEHICLES)); + } else if (show_create) { + d = maxdim(d, GetStringBoundingBox(STR_VEHICLE_LIST_CREATE_GROUP)); } return d; @@ -372,9 +376,10 @@ void BaseVehicleListWindow::OnInit() * Display the Action dropdown window. * @param show_autoreplace If true include the autoreplace item. * @param show_group If true include group-related stuff. + * @param show_create If true include group-create item. * @return Itemlist for dropdown */ -DropDownList BaseVehicleListWindow::BuildActionDropdownList(bool show_autoreplace, bool show_group) +DropDownList BaseVehicleListWindow::BuildActionDropdownList(bool show_autoreplace, bool show_group, bool show_create) { DropDownList list; @@ -385,6 +390,8 @@ DropDownList BaseVehicleListWindow::Buil if (show_group) { list.emplace_back(new DropDownListStringItem(STR_GROUP_ADD_SHARED_VEHICLE, ADI_ADD_SHARED, false)); list.emplace_back(new DropDownListStringItem(STR_GROUP_REMOVE_ALL_VEHICLES, ADI_REMOVE_ALL, false)); + } else if (show_create) { + list.emplace_back(new DropDownListStringItem(STR_VEHICLE_LIST_CREATE_GROUP, ADI_CREATE_GROUP, false)); } return list; @@ -1889,7 +1896,7 @@ public: break; case WID_VL_MANAGE_VEHICLES_DROPDOWN: { - Dimension d = this->GetActionDropdownSize(this->vli.type == VL_STANDARD, false); + Dimension d = this->GetActionDropdownSize(this->vli.type == VL_STANDARD, false, true); d.height += padding.height; d.width += padding.width; *size = maxdim(*size, d); @@ -2070,7 +2077,7 @@ public: break; case WID_VL_MANAGE_VEHICLES_DROPDOWN: { - ShowDropDownList(this, this->BuildActionDropdownList(VehicleListIdentifier::UnPack(this->window_number).type == VL_STANDARD, false), 0, WID_VL_MANAGE_VEHICLES_DROPDOWN); + ShowDropDownList(this, this->BuildActionDropdownList(VehicleListIdentifier::UnPack(this->window_number).type == VL_STANDARD, false, true), 0, WID_VL_MANAGE_VEHICLES_DROPDOWN); break; } @@ -2108,6 +2115,10 @@ public: Command::Post(GetCmdSendToDepotMsg(this->vli.vtype), 0, DepotCommand::MassSend | (index == ADI_SERVICE ? DepotCommand::Service : DepotCommand::None), this->vli); break; + case ADI_CREATE_GROUP: // Create group + Command::Post(CcAddVehicleNewGroup, NEW_GROUP, INVALID_VEHICLE, false, this->vli); + break; + default: NOT_REACHED(); } break; diff --git a/src/vehicle_gui_base.h b/src/vehicle_gui_base.h --- a/src/vehicle_gui_base.h +++ b/src/vehicle_gui_base.h @@ -102,6 +102,7 @@ struct BaseVehicleListWindow : public Wi ADI_DEPOT, ADI_ADD_SHARED, ADI_REMOVE_ALL, + ADI_CREATE_GROUP, }; static const StringID vehicle_depot_name[]; @@ -124,8 +125,8 @@ struct BaseVehicleListWindow : public Wi void SetCargoFilterIndex(byte index); void SetCargoFilterArray(); void FilterVehicleList(); - Dimension GetActionDropdownSize(bool show_autoreplace, bool show_group); - DropDownList BuildActionDropdownList(bool show_autoreplace, bool show_group); + Dimension GetActionDropdownSize(bool show_autoreplace, bool show_group, bool show_create); + DropDownList BuildActionDropdownList(bool show_autoreplace, bool show_group, bool show_create); const StringID *GetVehicleSorterNames() { diff --git a/src/vehiclelist_cmd.h b/src/vehiclelist_cmd.h new file mode 100644 --- /dev/null +++ b/src/vehiclelist_cmd.h @@ -0,0 +1,27 @@ +/* + * 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 . + */ + +/** @file vehiclelist_cmd.h Functions and type for serializing vehicle lists. */ + +#ifndef VEHICLELIST_CMD_H +#define VEHICLELIST_CMD_H + +#include "command_func.h" +#include "vehiclelist.h" + +template +inline EndianBufferWriter &operator <<(EndianBufferWriter &buffer, const VehicleListIdentifier &vli) +{ + return buffer << vli.type << vli.vtype << vli.company << vli.index; +} + +inline EndianBufferReader &operator >>(EndianBufferReader &buffer, VehicleListIdentifier &vli) +{ + return buffer >> vli.type >> vli.vtype >> vli.company >> vli.index; +} + +#endif /* VEHICLELIST_CMD_H */