diff --git a/src/group.h b/src/group.h index fabbaf3137..28de518012 100644 --- a/src/group.h +++ b/src/group.h @@ -11,6 +11,7 @@ #define GROUP_H #include "group_type.h" +#include "core/flatset_type.hpp" #include "core/pool_type.hpp" #include "company_type.h" #include "vehicle_type.h" @@ -78,6 +79,7 @@ struct Group : GroupPool::PoolItem<&_group_pool> { Livery livery{}; ///< Custom colour scheme for vehicles in this group GroupStatistics statistics{}; ///< NOSAVE: Statistics and caches on the vehicles in the group. + FlatSet children; ///< NOSAVE: child groups belonging to this group. bool folded = false; ///< NOSAVE: Is this group folded in the group view? GroupID parent = GroupID::Invalid(); ///< Parent group @@ -104,6 +106,7 @@ inline bool IsAllGroupID(GroupID id_g) } +void UpdateGroupChildren(); uint GetGroupNumEngines(CompanyID company, GroupID id_g, EngineID id_e); uint GetGroupNumVehicle(CompanyID company, GroupID id_g, VehicleType type); uint GetGroupNumVehicleMinAge(CompanyID company, GroupID id_g, VehicleType type); diff --git a/src/group_cmd.cpp b/src/group_cmd.cpp index 3a0497f6bd..84527d9b9f 100644 --- a/src/group_cmd.cpp +++ b/src/group_cmd.cpp @@ -41,6 +41,16 @@ void GroupStatistics::Clear() this->num_engines.clear(); } +/** + * Update children list for each group. + */ +void UpdateGroupChildren() +{ + for (Group *g : Group::Iterate()) { + if (g->parent != GroupID::Invalid()) Group::Get(g->parent)->children.insert(g->index); + } +} + /** * Get number of vehicles of a specific engine ID. * @param engine Engine ID. @@ -294,12 +304,11 @@ static void PropagateChildLivery(const Group *g, bool reset_cache) } } - for (Group *cg : Group::Iterate()) { - if (cg->parent == g->index) { - if (!HasBit(cg->livery.in_use, 0)) cg->livery.colour1 = g->livery.colour1; - if (!HasBit(cg->livery.in_use, 1)) cg->livery.colour2 = g->livery.colour2; - PropagateChildLivery(cg, reset_cache); - } + for (const GroupID &childgroup : g->children) { + Group *cg = Group::Get(childgroup); + if (!HasBit(cg->livery.in_use, 0)) cg->livery.colour1 = g->livery.colour1; + if (!HasBit(cg->livery.in_use, 1)) cg->livery.colour2 = g->livery.colour2; + PropagateChildLivery(cg, reset_cache); } } @@ -333,7 +342,7 @@ std::tuple CmdCreateGroup(DoCommandFlags flags, VehicleTyp if (!Group::CanAllocateItem()) return { CMD_ERROR, GroupID::Invalid() }; - const Group *pg = Group::GetIfValid(parent_group); + Group *pg = Group::GetIfValid(parent_group); if (pg != nullptr) { if (pg->owner != _current_company) return { CMD_ERROR, GroupID::Invalid() }; if (pg->vehicle_type != vt) return { CMD_ERROR, GroupID::Invalid() }; @@ -353,6 +362,7 @@ std::tuple CmdCreateGroup(DoCommandFlags flags, VehicleTyp g->livery.colour1 = pg->livery.colour1; g->livery.colour2 = pg->livery.colour2; g->flags = pg->flags; + pg->children.insert(g->index); } InvalidateWindowData(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_GROUP_LIST, vt, _current_company).ToWindowNumber()); @@ -380,10 +390,8 @@ CommandCost CmdDeleteGroup(DoCommandFlags flags, GroupID group_id) Command::Do(flags, group_id); /* Delete sub-groups */ - for (const Group *gp : Group::Iterate()) { - if (gp->parent == g->index) { - Command::Do(flags, gp->index); - } + for (const GroupID &childgroup : g->children) { + Command::Do(flags, childgroup); } if (flags.Test(DoCommandFlag::Execute)) { @@ -401,6 +409,11 @@ CommandCost CmdDeleteGroup(DoCommandFlags flags, GroupID group_id) c->freegroups.ReleaseID(g->number); } + if (g->parent != GroupID::Invalid()) { + Group *pg = Group::Get(g->parent); + pg->children.erase(g->index); + } + VehicleType vt = g->vehicle_type; /* Delete the Replace Vehicle Windows */ @@ -445,6 +458,9 @@ CommandCost CmdAlterGroup(DoCommandFlags flags, AlterGroupMode mode, GroupID gro } } } else if (mode == AlterGroupMode::SetParent) { + /* Do nothing if the parent group isn't actually changed. */ + if (g->parent == parent_id) return CommandCost(); + /* Set group parent */ const Group *pg = Group::GetIfValid(parent_id); @@ -458,7 +474,10 @@ CommandCost CmdAlterGroup(DoCommandFlags flags, AlterGroupMode mode, GroupID gro } if (flags.Test(DoCommandFlag::Execute)) { + if (g->parent != GroupID::Invalid()) Group::Get(g->parent)->children.erase(g->index); g->parent = (pg == nullptr) ? GroupID::Invalid() : pg->index; + if (g->parent != GroupID::Invalid()) Group::Get(g->parent)->children.insert(g->index); + GroupStatistics::UpdateAutoreplace(g->owner); if (!HasBit(g->livery.in_use, 0) || !HasBit(g->livery.in_use, 1)) { @@ -696,8 +715,8 @@ static void SetGroupFlag(Group *g, GroupFlag flag, bool set, bool children) if (!children) return; - for (Group *pg : Group::Iterate()) { - if (pg->parent == g->index) SetGroupFlag(pg, flag, set, true); + for (const GroupID &childgroup : g->children) { + SetGroupFlag(Group::Get(childgroup), flag, set, true); } } @@ -790,11 +809,14 @@ void UpdateTrainGroupID(Train *v) uint GetGroupNumEngines(CompanyID company, GroupID id_g, EngineID id_e) { uint count = 0; - const Engine *e = Engine::Get(id_e); - for (const Group *g : Group::Iterate()) { - if (g->parent == id_g) count += GetGroupNumEngines(company, g->index, id_e); + + if (Group *g = Group::GetIfValid(id_g); g != nullptr) { + for (const GroupID &childgroup : g->children) { + count += GetGroupNumEngines(company, childgroup, id_e); + } } - return count + GroupStatistics::Get(company, id_g, e->type).GetNumEngines(id_e); + + return count + GroupStatistics::Get(company, id_g, Engine::Get(id_e)->type).GetNumEngines(id_e); } /** @@ -808,9 +830,13 @@ uint GetGroupNumEngines(CompanyID company, GroupID id_g, EngineID id_e) uint GetGroupNumVehicle(CompanyID company, GroupID id_g, VehicleType type) { uint count = 0; - for (const Group *g : Group::Iterate()) { - if (g->parent == id_g) count += GetGroupNumVehicle(company, g->index, type); + + if (Group *g = Group::GetIfValid(id_g); g != nullptr) { + for (const GroupID &childgroup : g->children) { + count += GetGroupNumVehicle(company, childgroup, type); + } } + return count + GroupStatistics::Get(company, id_g, type).num_vehicle; } @@ -825,9 +851,13 @@ uint GetGroupNumVehicle(CompanyID company, GroupID id_g, VehicleType type) uint GetGroupNumVehicleMinAge(CompanyID company, GroupID id_g, VehicleType type) { uint count = 0; - for (const Group *g : Group::Iterate()) { - if (g->parent == id_g) count += GetGroupNumVehicleMinAge(company, g->index, type); + + if (Group *g = Group::GetIfValid(id_g); g != nullptr) { + for (const GroupID &childgroup : g->children) { + count += GetGroupNumVehicleMinAge(company, childgroup, type); + } } + return count + GroupStatistics::Get(company, id_g, type).num_vehicle_min_age; } @@ -842,9 +872,13 @@ uint GetGroupNumVehicleMinAge(CompanyID company, GroupID id_g, VehicleType type) Money GetGroupProfitLastYearMinAge(CompanyID company, GroupID id_g, VehicleType type) { Money sum = 0; - for (const Group *g : Group::Iterate()) { - if (g->parent == id_g) sum += GetGroupProfitLastYearMinAge(company, g->index, type); + + if (Group *g = Group::GetIfValid(id_g); g != nullptr) { + for (const GroupID &childgroup : g->children) { + sum += GetGroupProfitLastYearMinAge(company, childgroup, type); + } } + return sum + GroupStatistics::Get(company, id_g, type).profit_last_year_min_age; } diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index e04645dc36..50934f9814 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -575,6 +575,9 @@ bool AfterLoadGame() * that otherwise won't exist in the tree. */ RebuildViewportKdtree(); + /* Group hierarchy may be evaluated during conversion, so ensure its correct early on. */ + UpdateGroupChildren(); + if (IsSavegameVersionBefore(SLV_98)) _gamelog.GRFAddList(_grfconfig); if (IsSavegameVersionBefore(SLV_119)) {