Feature: Create group of vehicles from manage vehicle list button. (#10890)

This commit is contained in:
PeterN 2023-06-18 20:48:04 +01:00 committed by GitHub
parent 70de70bdcd
commit 2a2443dd01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 104 additions and 52 deletions

View File

@ -504,6 +504,7 @@ add_files(
vehicle_type.h
vehiclelist.cpp
vehiclelist.h
vehiclelist_cmd.h
viewport.cpp
viewport_cmd.h
viewport_func.h

View File

@ -403,7 +403,7 @@ static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head,
if (cost.Succeeded() && old_head != new_head) cost.AddCost(Command<CMD_CLONE_ORDER>::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<CMD_ADD_VEHICLE_GROUP>::Do(DC_EXEC, old_head->group_id, new_head->index, false)));
if (cost.Succeeded() && old_head != new_head) cost.AddCost(std::get<0>(Command<CMD_ADD_VEHICLE_GROUP>::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()) {

View File

@ -512,48 +512,63 @@ static void AddVehicleToGroup(Vehicle *v, GroupID new_g)
* @param add_shared Add shared vehicles as well.
* @return the cost of this operation or an error
*/
std::tuple<CommandCost, GroupID> CmdAddVehicleGroup(DoCommandFlag flags, GroupID group_id, VehicleID veh_id, bool add_shared)
std::tuple<CommandCost, GroupID> 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(DoCommandFlag flags, GroupID id_g, VehicleT
/* 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<CMD_ADD_VEHICLE_GROUP>::Do(flags, id_g, v2->index, false);
if (v2->group_id != id_g) Command<CMD_ADD_VEHICLE_GROUP>::Do(flags, id_g, v2->index, false, VehicleListIdentifier{});
}
}
}
@ -610,7 +625,7 @@ CommandCost CmdRemoveAllVehiclesGroup(DoCommandFlag flags, GroupID group_id)
if (v->group_id != group_id) continue;
/* Add The Vehicle to the default group */
Command<CMD_ADD_VEHICLE_GROUP>::Do(flags, DEFAULT_GROUP, v->index, false);
Command<CMD_ADD_VEHICLE_GROUP>::Do(flags, DEFAULT_GROUP, v->index, false, VehicleListIdentifier{});
}
}

View File

@ -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<CommandCost, GroupID> 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<CommandCost, GroupID> CmdAddVehicleGroup(DoCommandFlag flags, GroupID group_id, VehicleID veh_id, bool add_shared);
std::tuple<CommandCost, GroupID> 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, CmdSetGroupFlag, 0, CMDT_
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 */

View File

@ -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<CMD_ADD_VEHICLE_GROUP>::Post(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE, DEFAULT_GROUP, this->vehicle_sel, _ctrl_pressed || this->grouping == GB_SHARED_ORDERS);
Command<CMD_ADD_VEHICLE_GROUP>::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<CMD_ADD_VEHICLE_GROUP>::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<CMD_ADD_VEHICLE_GROUP>::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<CMD_ADD_VEHICLE_GROUP>::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 CommandCost &result, GroupID new_group, V
* @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);
}
/**

View File

@ -3849,6 +3849,7 @@ STR_VEHICLE_LIST_MANAGE_LIST :{BLACK}Manage l
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}

View File

@ -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<CMD_ADD_VEHICLE_GROUP>::Do(DC_EXEC, this->group, v->index, false);
Command<CMD_ADD_VEHICLE_GROUP>::Do(DC_EXEC, this->group, v->index, false, VehicleListIdentifier{});
}
/**

View File

@ -132,7 +132,7 @@
EnforcePrecondition(false, IsValidGroup(group_id) || group_id == GROUP_DEFAULT);
EnforcePrecondition(false, ScriptVehicle::IsPrimaryVehicle(vehicle_id));
return ScriptObject::Command<CMD_ADD_VEHICLE_GROUP>::Do(group_id, vehicle_id, false);
return ScriptObject::Command<CMD_ADD_VEHICLE_GROUP>::Do(group_id, vehicle_id, false, VehicleListIdentifier{});
}
/* static */ bool ScriptGroup::EnableWagonRemoval(bool enable_removal)

View File

@ -920,7 +920,7 @@ std::tuple<CommandCost, VehicleID> CmdCloneVehicle(DoCommandFlag flags, TileInde
if (flags & DC_EXEC) {
/* Cloned vehicles belong to the same group */
Command<CMD_ADD_VEHICLE_GROUP>::Do(flags, v_front->group_id, w_front->index, false);
Command<CMD_ADD_VEHICLE_GROUP>::Do(flags, v_front->group_id, w_front->index, false, VehicleListIdentifier{});
}

View File

@ -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<CommandCost, VehicleID, uint, uint16, CargoArray> CmdBuildVehicle(DoCommandFlag flags, TileIndex tile, EngineID eid, bool use_free_vehicles, CargoID cargo, ClientID client_id);
@ -43,17 +44,6 @@ DEF_CMD_TRAIT(CMD_DEPOT_MASS_AUTOREPLACE, CmdDepotMassAutoReplace, 0,
void CcBuildPrimaryVehicle(Commands cmd, const CommandCost &result, VehicleID new_veh_id, uint, uint16, CargoArray);
void CcStartStopVehicle(Commands cmd, const CommandCost &result, VehicleID veh_id, bool);
template <typename Tcont, typename Titer>
inline EndianBufferWriter<Tcont, Titer> &operator <<(EndianBufferWriter<Tcont, Titer> &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 <typename Tcont, typename Titer>
inline EndianBufferWriter<Tcont, Titer> &operator <<(EndianBufferWriter<Tcont, Titer> &buffer, const CargoArray &cargo_array)
{

View File

@ -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::FilterVehicleList()
* 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::GetActionDropdownSize(bool show_autoreplace, bo
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::BuildActionDropdownList(bool show_autoreplac
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<CMD_SEND_VEHICLE_TO_DEPOT>::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<CMD_ADD_VEHICLE_GROUP>::Post(CcAddVehicleNewGroup, NEW_GROUP, INVALID_VEHICLE, false, this->vli);
break;
default: NOT_REACHED();
}
break;

View File

@ -102,6 +102,7 @@ struct BaseVehicleListWindow : public Window {
ADI_DEPOT,
ADI_ADD_SHARED,
ADI_REMOVE_ALL,
ADI_CREATE_GROUP,
};
static const StringID vehicle_depot_name[];
@ -124,8 +125,8 @@ struct BaseVehicleListWindow : public Window {
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()
{

27
src/vehiclelist_cmd.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/** @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 <typename Tcont, typename Titer>
inline EndianBufferWriter<Tcont, Titer> &operator <<(EndianBufferWriter<Tcont, Titer> &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 */