From e2191dffdda1e4a8e42e53bc446341c2be2056e9 Mon Sep 17 00:00:00 2001 From: Andrettin <6322423+Andrettin@users.noreply.github.com> Date: Tue, 4 Jan 2022 20:17:08 +0100 Subject: [PATCH] Refactored the way units contained in another unit are stored, to make handling them easier --- src/action/action_resource.cpp | 4 +- src/action/action_unload.cpp | 17 ++-- src/action/action_upgradeto.cpp | 12 ++- src/ai/ai_plan.cpp | 3 +- src/animation/animation.cpp | 2 +- src/game/loadgame.cpp | 2 +- src/player/player.cpp | 7 +- src/ui/botpanel.cpp | 18 ++-- src/ui/contenttype.cpp | 6 +- src/ui/mainscr.cpp | 15 ++-- src/ui/mouse.cpp | 13 +-- src/unit/script_unit.cpp | 4 +- src/unit/unit.cpp | 146 ++++++++++---------------------- src/unit/unit.h | 27 +++++- src/unit/unit_save.cpp | 12 +-- 15 files changed, 132 insertions(+), 156 deletions(-) diff --git a/src/action/action_resource.cpp b/src/action/action_resource.cpp index 888eed24a..13e13c044 100644 --- a/src/action/action_resource.cpp +++ b/src/action/action_resource.cpp @@ -693,7 +693,7 @@ int COrder_Resource::StartGathering(CUnit &unit) if (!goal->Type->BoolFlag[HARVESTFROMOUTSIDE_INDEX].value) { //Wyrmgus end //Wyrmgus start -// if (goal->Variable[MAXHARVESTERS_INDEX].Value == 0 || goal->Variable[MAXHARVESTERS_INDEX].Value > goal->InsideCount) { +// if (goal->Variable[MAXHARVESTERS_INDEX].Value == 0 || goal->Variable[MAXHARVESTERS_INDEX].Value > goal->get_units_inside().size()) { if (goal->Variable[MAXHARVESTERS_INDEX].Value == 0 || goal->Variable[MAXHARVESTERS_INDEX].Value > goal->Resource.Active) { //Wyrmgus end this->clear_goal(); @@ -706,7 +706,7 @@ int COrder_Resource::StartGathering(CUnit &unit) unit.Removed = 1; } //Wyrmgus start -// } else if (goal->Variable[MAXHARVESTERS_INDEX].Value <= goal->InsideCount) { +// } else if (goal->Variable[MAXHARVESTERS_INDEX].Value <= goal->get_units_inside().size()) { } else if (goal->Variable[MAXHARVESTERS_INDEX].Value <= goal->Resource.Active) { //Wyrmgus end //Resource is full, wait diff --git a/src/action/action_unload.cpp b/src/action/action_unload.cpp index 4b8ea6ff8..11ad40d10 100644 --- a/src/action/action_unload.cpp +++ b/src/action/action_unload.cpp @@ -370,8 +370,7 @@ static bool IsDropZonePossible(const CUnit &transporter, const Vec2i &pos, int z Vec2i dummyPos; //Wyrmgus start /* - CUnit *unit = transporter.UnitInside; - for (int i = 0; i < transporter.InsideCount; ++i, unit = unit->NextContained) { + for (const CUnit *unit : transporter.get_units_inside()) { //Wyrmgus start // if (FindUnloadPosition(transporter, *unit, pos, maxUnloadRange, &dummyPos)) { if (FindUnloadPosition(transporter, *unit, pos, maxUnloadRange, &dummyPos, z, landmass)) { @@ -385,8 +384,7 @@ static bool IsDropZonePossible(const CUnit &transporter, const Vec2i &pos, int z return true; } } else { - CUnit *unit = transporter.UnitInside; - for (int i = 0; i < transporter.InsideCount; ++i, unit = unit->NextContained) { + for (const CUnit *unit : transporter.get_units_inside()) { if (FindUnloadPosition(transporter, *unit, pos, maxUnloadRange, &dummyPos, z, landmass)) { return true; } @@ -482,9 +480,10 @@ static int ClosestFreeDropZone(CUnit &transporter, const Vec2i &startPos, int ma //Wyrmgus end { // Check there are units onboard - if (!transporter.UnitInside) { + if (!transporter.has_units_inside()) { return 0; } + const bool isTransporterRemoved = transporter.Removed; const bool selected = transporter.Selected; @@ -567,9 +566,11 @@ bool COrder_Unload::LeaveTransporter(CUnit &transporter) ++stillonboard; } } else { - // Unload all units. - CUnit *goal = transporter.UnitInside; - for (int i = transporter.InsideCount; i; --i, goal = goal->NextContained) { + //copy the vector since we may modify it + const std::vector units_inside = transporter.get_units_inside(); + + //unload all units + for (CUnit *goal : units_inside) { if (goal->Boarded) { //Wyrmgus start // if (!UnloadUnit(transporter, *goal)) { diff --git a/src/action/action_upgradeto.cpp b/src/action/action_upgradeto.cpp index 159b44d02..40635423b 100644 --- a/src/action/action_upgradeto.cpp +++ b/src/action/action_upgradeto.cpp @@ -216,14 +216,20 @@ int TransformUnitIntoType(CUnit &unit, const wyrmgus::unit_type &newtype) } //drop units that can no longer be in the container - if (unit.UnitInside) { - CUnit *uins = unit.UnitInside; - for (int i = unit.InsideCount; i && unit.BoardCount > newtype.MaxOnBoard; --i, uins = uins->NextContained) { + if (unit.has_units_inside() && unit.BoardCount > newtype.MaxOnBoard) { + //copy the vector since we may modify it + const std::vector units_inside = unit.get_units_inside(); + + for (CUnit *uins : units_inside) { if (uins->Boarded) { uins->Boarded = 0; unit.BoardCount -= uins->Type->BoardSize; DropOutOnSide(*uins, LookingW, &unit); } + + if (unit.BoardCount <= newtype.MaxOnBoard) { + break; + } } } diff --git a/src/ai/ai_plan.cpp b/src/ai/ai_plan.cpp index 067d2767f..99d6096cf 100644 --- a/src/ai/ai_plan.cpp +++ b/src/ai/ai_plan.cpp @@ -776,8 +776,7 @@ void PlayerAi::check_transporters() continue; } - CUnit *uins = ai_transporter->UnitInside; - for (int j = 0; j < ai_transporter->InsideCount; ++j, uins = uins->NextContained) { + for (CUnit *uins : ai_transporter->get_units_inside()) { if (uins->GroupId == 0 && !this->is_site_transport_unit(uins)) { //if the unit no longer is part of a force, then it likely has been reset and the attack through water has been cancelled, so unload it CommandUnload(*ai_transporter, ai_transporter->tilePos, uins, 0, ai_transporter->MapLayer->ID); diff --git a/src/animation/animation.cpp b/src/animation/animation.cpp index 5688d547a..e4102ce30 100644 --- a/src/animation/animation.cpp +++ b/src/animation/animation.cpp @@ -151,7 +151,7 @@ int ParseAnimInt(const CUnit &unit, const std::string &parseint) } else if (cur == "ResourceActive") { return goal->Resource.Active; } else if (cur == "InsideCount") { - return goal->InsideCount; + return static_cast(goal->get_units_inside().size()); } else if (cur == "_Distance") { return unit.MapDistanceTo(*goal); } diff --git a/src/game/loadgame.cpp b/src/game/loadgame.cpp index b17fcdbde..3a65b591a 100644 --- a/src/game/loadgame.cpp +++ b/src/game/loadgame.cpp @@ -239,7 +239,7 @@ static void PlaceUnits() //Wyrmgus start //calculate attack range for containers now, as when loading a game it couldn't be done when the container was initially loaded - if (unit->BoardCount > 0 && unit->InsideCount > 0) { + if (unit->BoardCount > 0 && unit->has_units_inside()) { unit->UpdateContainerAttackRange(); } diff --git a/src/player/player.cpp b/src/player/player.cpp index f276e4fc3..d6a2ba677 100644 --- a/src/player/player.cpp +++ b/src/player/player.cpp @@ -2957,10 +2957,11 @@ bool CPlayer::capture_unit(CUnit *unit) } } - if (unit->UnitInside != nullptr) { - CUnit *unit_inside = unit->UnitInside; + if (unit->has_units_inside()) { + //copy the vector since we may modify it + const std::vector units_inside = unit->get_units_inside(); - for (int i = unit->InsideCount; i; --i, unit_inside = unit_inside->NextContained) { + for (CUnit *unit_inside : units_inside) { if (unit_inside->Player->is_neutral_player()) { continue; } diff --git a/src/ui/botpanel.cpp b/src/ui/botpanel.cpp index 114a979bd..41c2e8a48 100644 --- a/src/ui/botpanel.cpp +++ b/src/ui/botpanel.cpp @@ -1121,8 +1121,8 @@ void CButtonPanel::Draw(std::vector> &render_com QColor border_color; // if there is a single unit selected, show the icon of its weapon/shield/boots/arrows equipped for the appropriate buttons - if (button->Icon.Name.empty() && button->Action == ButtonCmd::Attack && Selected[0]->Type->CanTransport() && Selected[0]->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value && Selected[0]->BoardCount > 0 && Selected[0]->UnitInside != nullptr && Selected[0]->UnitInside->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value && Selected[0]->UnitInside->GetButtonIcon(button->Action) != nullptr) { - button_icon = Selected[0]->UnitInside->GetButtonIcon(button->Action); + if (button->Icon.Name.empty() && button->Action == ButtonCmd::Attack && Selected[0]->Type->CanTransport() && Selected[0]->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value && Selected[0]->BoardCount > 0 && Selected[0]->has_units_inside() && Selected[0]->get_units_inside().at(0)->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value && Selected[0]->get_units_inside().at(0)->GetButtonIcon(button->Action) != nullptr) { + button_icon = Selected[0]->get_units_inside().at(0)->GetButtonIcon(button->Action); } else if (button->Icon.Name.empty() && Selected[0]->GetButtonIcon(button->Action) != nullptr) { button_icon = Selected[0]->GetButtonIcon(button->Action); } else if (button->Action == ButtonCmd::ExperienceUpgradeTo && Selected[0]->GetVariation() && button_unit_type->GetVariation(Selected[0]->GetVariation()->get_identifier()) != nullptr && !button_unit_type->GetVariation(Selected[0]->GetVariation()->get_identifier())->Icon.Name.empty()) { @@ -1211,23 +1211,23 @@ void CButtonPanel::Draw(std::vector> &render_com //Wyrmgus start if (ButtonAreaUnderCursor == ButtonAreaTransporting) { - CUnit *uins = Selected[0]->UnitInside; - size_t j = 0; + size_t i = 0; - for (int i = 0; i < Selected[0]->InsideCount; ++i, uins = uins->NextContained) { - if (!uins->Boarded || j >= UI.TransportingButtons.size() || (Selected[0]->Player != CPlayer::GetThisPlayer() && uins->Player != CPlayer::GetThisPlayer())) { + for (const CUnit *uins : Selected[0]->get_units_inside()) { + if (!uins->Boarded || i >= UI.TransportingButtons.size() || (Selected[0]->Player != CPlayer::GetThisPlayer() && uins->Player != CPlayer::GetThisPlayer())) { continue; } - if (static_cast(ButtonUnderCursor) == j) { + if (static_cast(ButtonUnderCursor) == i) { const wyrmgus::font_color *text_color = nullptr; if (uins->get_unique() != nullptr || uins->get_character() != nullptr) { text_color = wyrmgus::defines::get()->get_unique_font_color(); } else if (uins->Prefix != nullptr || uins->Suffix != nullptr) { text_color = wyrmgus::defines::get()->get_magic_font_color(); } - DrawGenericPopup(uins->GetMessageName(), UI.TransportingButtons[j].X, UI.TransportingButtons[j].Y, text_color, nullptr, render_commands); + DrawGenericPopup(uins->GetMessageName(), UI.TransportingButtons[i].X, UI.TransportingButtons[i].Y, text_color, nullptr, render_commands); } - ++j; + + ++i; } } //Wyrmgus end diff --git a/src/ui/contenttype.cpp b/src/ui/contenttype.cpp index 39334b6c0..c7397dbee 100644 --- a/src/ui/contenttype.cpp +++ b/src/ui/contenttype.cpp @@ -240,7 +240,11 @@ static const CUnit *GetUnitRef(const CUnit &unit, EnumUnit e) case UnitRefItSelf: return &unit; case UnitRefInside: - return unit.UnitInside; + if (unit.has_units_inside()) { + return unit.get_units_inside().at(0); + } + + return nullptr; case UnitRefContainer: return unit.Container; case UnitRefWorker : diff --git a/src/ui/mainscr.cpp b/src/ui/mainscr.cpp index 6e010cd56..80099b2d7 100644 --- a/src/ui/mainscr.cpp +++ b/src/ui/mainscr.cpp @@ -604,10 +604,9 @@ static bool DrawUnitInfo_single_selection(const CUnit &unit, std::vector> &render_commands) { - CUnit *uins = unit.UnitInside; size_t j = 0; - for (int i = 0; i < unit.InsideCount; ++i, uins = uins->NextContained) { + for (const CUnit *uins : unit.get_units_inside()) { //Wyrmgus start // if (!uins->Boarded || j >= UI.TransportingButtons.size()) { if (!uins->Boarded || j >= UI.TransportingButtons.size() || (unit.Player != CPlayer::GetThisPlayer() && uins->Player != CPlayer::GetThisPlayer())) { @@ -639,10 +638,9 @@ static void DrawUnitInfo_transporter(CUnit &unit, std::vector> &render_commands) { - CUnit *uins = unit.UnitInside; size_t j = 0; - for (int i = 0; i < unit.InsideCount; ++i, uins = uins->NextContained) { + for (const CUnit *uins : unit.get_units_inside()) { if (!uins->Type->BoolFlag[ITEM_INDEX].value || j >= UI.InventoryButtons.size()) { continue; } @@ -709,7 +707,7 @@ static void DrawUnitInfo(CUnit &unit, std::vectorget_inventory_button_level()) { + if (unit.HasInventory() && unit.has_units_inside() && CurrentButtonLevel == defines::get()->get_inventory_button_level()) { DrawUnitInfo_inventory(unit, render_commands); return; } @@ -830,11 +828,10 @@ void DrawPopups(std::vector> &render_commands) LastDrawnButtonPopup = nullptr; } - if (!(Selected[0]->Player != CPlayer::GetThisPlayer() && !CPlayer::GetThisPlayer()->is_allied_with(*Selected[0]->Player) && !CPlayer::GetThisPlayer()->has_building_access(Selected[0])) && Selected[0]->HasInventory() && Selected[0]->InsideCount && CurrentButtonLevel == wyrmgus::defines::get()->get_inventory_button_level()) { - CUnit *uins = Selected[0]->UnitInside; - size_t j = 0; + if (!(Selected[0]->Player != CPlayer::GetThisPlayer() && !CPlayer::GetThisPlayer()->is_allied_with(*Selected[0]->Player) && !CPlayer::GetThisPlayer()->has_building_access(Selected[0])) && Selected[0]->HasInventory() && Selected[0]->has_units_inside() && CurrentButtonLevel == defines::get()->get_inventory_button_level()) { + size_t j = 0; - for (int i = 0; i < Selected[0]->InsideCount; ++i, uins = uins->NextContained) { + for (const CUnit *uins : Selected[0]->get_units_inside()) { if (!uins->Type->BoolFlag[ITEM_INDEX].value || j >= UI.InventoryButtons.size()) { continue; } diff --git a/src/ui/mouse.cpp b/src/ui/mouse.cpp index e8f86ab7d..90d0e62cf 100644 --- a/src/ui/mouse.cpp +++ b/src/ui/mouse.cpp @@ -1094,7 +1094,7 @@ static void HandleMouseOn(const PixelPos screenPos) if (Selected.size() == 1 && Selected[0]->HasInventory() && CurrentButtonLevel == wyrmgus::defines::get()->get_inventory_button_level()) { const size_t size = UI.InventoryButtons.size(); - for (size_t i = std::min(Selected[0]->InsideCount, size); i != 0;) { + for (size_t i = std::min(Selected[0]->get_units_inside().size(), size); i != 0;) { --i; if (UI.InventoryButtons[i].Contains(screenPos)) { ButtonAreaUnderCursor = ButtonAreaInventory; @@ -2522,10 +2522,9 @@ static void UIHandleButtonUp_OnButton(unsigned button, const Qt::KeyboardModifie if (!GameObserve && !game::get()->is_paused() && !GameEstablishing && (CPlayer::GetThisPlayer()->IsTeamed(*Selected[0]) || CPlayer::GetThisPlayer()->is_allied_with(*Selected[0]) || CPlayer::GetThisPlayer()->has_building_access(Selected[0]))) { //Wyrmgus end if (Selected[0]->BoardCount >= ButtonUnderCursor) { - CUnit *uins = Selected[0]->UnitInside; size_t j = 0; - for (int i = 0; i < Selected[0]->InsideCount; ++i, uins = uins->NextContained) { + for (CUnit *uins : Selected[0]->get_units_inside()) { if (!uins->Boarded || j >= UI.TransportingButtons.size() || (Selected[0]->Player != CPlayer::GetThisPlayer() && uins->Player != CPlayer::GetThisPlayer())) { continue; } @@ -2544,11 +2543,13 @@ static void UIHandleButtonUp_OnButton(unsigned button, const Qt::KeyboardModifie } else if (ButtonAreaUnderCursor == ButtonAreaInventory) { // for inventory unit if (!GameObserve && !game::get()->is_paused() && !GameEstablishing && CPlayer::GetThisPlayer()->IsTeamed(*Selected[0])) { - if (Selected[0]->InsideCount >= ButtonUnderCursor) { - CUnit *uins = Selected[0]->UnitInside; + if (static_cast(Selected[0]->get_units_inside().size()) >= ButtonUnderCursor) { + //copy the vector since we may modify it + const std::vector units_inside = Selected[0]->get_units_inside(); + size_t j = 0; - for (int i = 0; i < Selected[0]->InsideCount; ++i, uins = uins->NextContained) { + for (CUnit *uins : units_inside) { if (!uins->Type->BoolFlag[ITEM_INDEX].value || j >= UI.InventoryButtons.size() || (Selected[0]->Player != CPlayer::GetThisPlayer() && uins->Player != CPlayer::GetThisPlayer())) { continue; } diff --git a/src/unit/script_unit.cpp b/src/unit/script_unit.cpp index 5e1f209b0..2b721135e 100644 --- a/src/unit/script_unit.cpp +++ b/src/unit/script_unit.cpp @@ -1626,8 +1626,8 @@ static int CclGetUnitsInsideUnit(lua_State *l) lua_newtable(l); - CUnit *unit = transporter.UnitInside; - for (int i = 0; i < transporter.InsideCount; ++i, unit = unit->NextContained) { + for (size_t i = 0; i < transporter.get_units_inside().size(); ++i) { + const CUnit *unit = transporter.get_units_inside().at(i); lua_pushnumber(l, UnitNumber(*unit)); lua_rawseti(l, -2, i + 1); } diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp index 8d91bfda4..471a74821 100644 --- a/src/unit/unit.cpp +++ b/src/unit/unit.cpp @@ -160,22 +160,6 @@ ** free. This points to the transporter for units on board, or to ** the building for peasants inside(when they are mining). ** -** CUnit::UnitInside -** -** Pointer to the last unit added inside. Order doesn't really -** matter. All units inside are kept in a circular linked list. -** This is null if there are no units inside. Multiple levels -** of inclusion are allowed, though not very useful right now -** -** CUnit::NextContained, CUnit::PrevContained -** -** The next and previous element in the curent container. Bogus -** values allowed for units not contained. -** -** CUnit::InsideCount -** -** The number of units inside the container. -** ** CUnit::BoardCount ** ** The number of units transported inside the container. This @@ -412,12 +396,9 @@ void CUnit::Init() this->ref.reset(); this->ReleaseCycle = 0; this->PlayerSlot = static_cast(-1); - this->InsideCount = 0; + this->units_inside.clear(); this->BoardCount = 0; - this->UnitInside = nullptr; this->Container = nullptr; - this->NextContained = nullptr; - this->PrevContained = nullptr; this->Resource.Workers.clear(); this->Resource.Active = 0; @@ -989,9 +970,7 @@ void CUnit::auto_use_item() return; } - CUnit *uins = this->UnitInside; - - for (int i = 0; i < this->InsideCount; ++i, uins = uins->NextContained) { + for (CUnit *uins : this->get_units_inside()) { if (!uins->Type->BoolFlag[ITEM_INDEX].value || uins->Elixir) { continue; } @@ -1022,7 +1001,7 @@ void CUnit::auto_use_item() if (this->CriticalOrder == nullptr) { this->CriticalOrder = COrder::NewActionUse(*uins); } - break; + return; } } } @@ -1942,9 +1921,8 @@ void CUnit::ApplyAura(const int aura_index) SelectAroundUnit(*this, aura_range, table, MakeOrPredicate(MakeOrPredicate(HasSamePlayerAs(*this->Player), IsAlliedWith(*this->Player)), HasSamePlayerAs(*CPlayer::get_neutral_player()))); for (CUnit *nearby_unit : table) { - if (nearby_unit->UnitInside) { - CUnit *uins = nearby_unit->UnitInside; - for (int j = 0; j < nearby_unit->InsideCount; ++j, uins = uins->NextContained) { + if (nearby_unit->has_units_inside()) { + for (CUnit *uins : nearby_unit->get_units_inside()) { if (uins->Player == this->Player || uins->is_allied_with(*this->Player)) { uins->ApplyAuraEffect(aura_index); } @@ -2191,9 +2169,7 @@ void CUnit::CheckIdentification() return; } - CUnit *uins = this->UnitInside; - - for (int i = 0; i < this->InsideCount; ++i, uins = uins->NextContained) { + for (CUnit *uins : this->get_units_inside()) { if (!uins->Type->BoolFlag[ITEM_INDEX].value) { continue; } @@ -3362,8 +3338,7 @@ static void MapMarkUnitSightRec(const CUnit &unit, const Vec2i &pos, int width, } //Wyrmgus end - CUnit *unit_inside = unit.UnitInside; - for (int i = unit.InsideCount; i--; unit_inside = unit_inside->NextContained) { + for (const CUnit *unit_inside : unit.get_units_inside()) { //Wyrmgus start // MapMarkUnitSightRec(*unit_inside, pos, width, height, f, f2); MapMarkUnitSightRec(*unit_inside, pos, width, height); @@ -3491,8 +3466,7 @@ void UpdateUnitSightRange(CUnit &unit) } } - CUnit *unit_inside = unit.UnitInside; - for (int i = unit.InsideCount; i--; unit_inside = unit_inside->NextContained) { + for (CUnit *unit_inside : unit.get_units_inside()) { UpdateUnitSightRange(*unit_inside); } } @@ -3600,20 +3574,13 @@ void UnmarkUnitFieldFlags(const CUnit &unit) */ void CUnit::AddInContainer(CUnit &host) { - assert_throw(Container == nullptr); - Container = &host; - if (host.InsideCount == 0) { - NextContained = PrevContained = this; - } else { - NextContained = host.UnitInside; - PrevContained = host.UnitInside->PrevContained; - host.UnitInside->PrevContained->NextContained = this; - host.UnitInside->PrevContained = this; - } - host.UnitInside = this; - host.InsideCount++; + assert_throw(this->Container == nullptr); + this->Container = &host; + host.add_unit_inside(this); + //Wyrmgus start - if (!SaveGameLoading) { //if host has no range by itself, but the unit has range, and the unit can attack from a transporter, change the host's range to the unit's; but don't do this while loading, as it causes a crash (since one unit needs to be loaded before the other, and when this function is processed both won't already have their variables set) + if (!SaveGameLoading) { + //if host has no range by itself, but the unit has range, and the unit can attack from a transporter, change the host's range to the unit's; but don't do this while loading, as it causes a crash (since one unit needs to be loaded before the other, and when this function is processed both won't already have their variables set) host.UpdateContainerAttackRange(); } //Wyrmgus end @@ -3628,18 +3595,9 @@ static void RemoveUnitFromContainer(CUnit &unit) { CUnit *host = unit.Container; // transporter which contain unit. assert_throw(unit.Container != nullptr); - assert_throw(unit.Container->InsideCount > 0); + assert_throw(unit.Container->has_units_inside()); - host->InsideCount--; - unit.NextContained->PrevContained = unit.PrevContained; - unit.PrevContained->NextContained = unit.NextContained; - if (host->InsideCount == 0) { - host->UnitInside = nullptr; - } else { - if (host->UnitInside == &unit) { - host->UnitInside = unit.NextContained; - } - } + host->remove_unit_inside(&unit); unit.Container = nullptr; //Wyrmgus start //reset host attack range @@ -3655,8 +3613,7 @@ void CUnit::UpdateContainerAttackRange() //recalculate attack range, if this unit is a transporter (or garrisonable building) from which units can attack if (this->Type->CanTransport() && this->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value) { if (this->BoardCount > 0) { - CUnit *boarded_unit = this->UnitInside; - for (int i = 0; i < this->InsideCount; ++i, boarded_unit = boarded_unit->NextContained) { + for (const CUnit *boarded_unit : this->get_units_inside()) { if (boarded_unit->GetModifiedVariable(ATTACKRANGE_INDEX) > this->best_contained_unit_attack_range && boarded_unit->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value) { //if container has no range by itself, but the unit has range, and the unit can attack from a transporter, change the container's range to the unit's this->best_contained_unit_attack_range = boarded_unit->GetModifiedVariable(ATTACKRANGE_INDEX); } @@ -3923,15 +3880,13 @@ void CUnit::XPChanged() */ static void UnitInXY(CUnit &unit, const Vec2i &pos, const int z) { - const wyrmgus::time_of_day *old_time_of_day = unit.get_center_tile_time_of_day(); + const time_of_day *old_time_of_day = unit.get_center_tile_time_of_day(); - CUnit *unit_inside = unit.UnitInside; - unit.tilePos = pos; unit.Offset = CMap::get()->get_pos_index(pos, z); unit.MapLayer = CMap::get()->MapLayers[z].get(); - const wyrmgus::time_of_day *new_time_of_day = unit.get_center_tile_time_of_day(); + const time_of_day *new_time_of_day = unit.get_center_tile_time_of_day(); //Wyrmgus start if (!SaveGameLoading && old_time_of_day != new_time_of_day) { @@ -3939,7 +3894,7 @@ static void UnitInXY(CUnit &unit, const Vec2i &pos, const int z) } //Wyrmgus end - for (int i = unit.InsideCount; i--; unit_inside = unit_inside->NextContained) { + for (CUnit *unit_inside : unit.get_units_inside()) { UnitInXY(*unit_inside, pos, z); } } @@ -4883,15 +4838,13 @@ void CUnit::ChangeOwner(CPlayer &newplayer, bool show_change) // Rescue all units in buildings/transporters. //Wyrmgus start -// CUnit *uins = UnitInside; -// for (int i = InsideCount; i; --i, uins = uins->NextContained) { +// for (CUnit *uins : this->get_units_inside()) { // uins->ChangeOwner(newplayer); // } //only rescue units inside if the player is actually a rescuable player (to avoid, for example, unintended worker owner changes when a depot changes hands) if (oldplayer->get_type() == player_type::rescue_active || oldplayer->get_type() == player_type::rescue_passive) { - CUnit *uins = UnitInside; - for (int i = InsideCount; i; --i, uins = uins->NextContained) { + for (CUnit *uins : this->get_units_inside()) { uins->ChangeOwner(newplayer); } } @@ -4902,7 +4855,7 @@ void CUnit::ChangeOwner(CPlayer &newplayer, bool show_change) // Now the new side! - if (Type->BoolFlag[BUILDING_INDEX].value) { + if (this->Type->BoolFlag[BUILDING_INDEX].value) { //Wyrmgus start // if (!Type->BoolFlag[WALL_INDEX].value) { //Wyrmgus end @@ -5466,9 +5419,10 @@ void DropOutNearest(CUnit &unit, const Vec2i &goalPos, const CUnit *container) */ void DropOutAll(const CUnit &source) { - CUnit *unit = source.UnitInside; + //copy the vector since we may modify it + const std::vector units_inside = source.get_units_inside(); - for (int i = source.InsideCount; i; --i, unit = unit->NextContained) { + for (CUnit *unit : units_inside) { DropOutOnSide(*unit, LookingW, &source); //Wyrmgus start @@ -6148,9 +6102,9 @@ int CUnit::get_resource_step(const resource *resource) const return resource_step; } -int CUnit::GetTotalInsideCount(const CPlayer *player, const bool ignore_items, const bool ignore_saved_cargo, const wyrmgus::unit_type *type) const +int CUnit::GetTotalInsideCount(const CPlayer *player, const bool ignore_items, const bool ignore_saved_cargo, const unit_type *type) const { - if (!this->UnitInside) { + if (!this->has_units_inside()) { return 0; } @@ -6160,8 +6114,7 @@ int CUnit::GetTotalInsideCount(const CPlayer *player, const bool ignore_items, c int inside_count = 0; - CUnit *inside_unit = this->UnitInside; - for (int j = 0; j < this->InsideCount; ++j, inside_unit = inside_unit->NextContained) { + for (const CUnit *inside_unit : this->get_units_inside()) { if ( //only count units of the faction, ignore items (!player || inside_unit->Player == player) && (!ignore_items || !inside_unit->Type->BoolFlag[ITEM_INDEX].value) @@ -6169,6 +6122,7 @@ int CUnit::GetTotalInsideCount(const CPlayer *player, const bool ignore_items, c ) { inside_count++; } + inside_count += inside_unit->GetTotalInsideCount(player, ignore_items, ignore_saved_cargo); } @@ -6179,8 +6133,7 @@ bool CUnit::CanAttack(bool count_inside) const { if (this->Type->CanTransport() && this->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value && !this->Type->BoolFlag[CANATTACK_INDEX].value) { //transporters without an attack can only attack through a unit within them if (count_inside && this->BoardCount > 0) { - CUnit *boarded_unit = this->UnitInside; - for (int i = 0; i < this->InsideCount; ++i, boarded_unit = boarded_unit->NextContained) { + for (const CUnit *boarded_unit : this->get_units_inside()) { if (boarded_unit->GetModifiedVariable(ATTACKRANGE_INDEX) > 1 && boarded_unit->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value) { return true; } @@ -7182,7 +7135,7 @@ void LetUnitDie(CUnit &unit, bool suicide) // removed units, just remove. if (unit.Removed) { DebugPrint("Killing a removed unit?\n"); - if (unit.UnitInside) { + if (unit.has_units_inside()) { DestroyAllInside(unit); } UnitLost(unit); @@ -7237,21 +7190,14 @@ void LetUnitDie(CUnit &unit, bool suicide) //Wyrmgus end // Transporters lose or save their units and buildings their workers - //Wyrmgus start -// if (unit.UnitInside && unit.Type->BoolFlag[SAVECARGO_INDEX].value) { - if ( - unit.UnitInside - && ( - unit.Type->BoolFlag[SAVECARGO_INDEX].value - || (unit.HasInventory() && unit.get_character() == nullptr) - ) - ) { - //Wyrmgus end - DropOutAll(unit); - } else if (unit.UnitInside) { - DestroyAllInside(unit); + if (unit.has_units_inside()) { + if (unit.Type->BoolFlag[SAVECARGO_INDEX].value || (unit.HasInventory() && unit.get_character() == nullptr)) { + DropOutAll(unit); + } else { + DestroyAllInside(unit); + } } - + //Wyrmgus start //drop items upon death if (!suicide && unit.CurrentAction() != UnitAction::Built && (unit.get_character() != nullptr || unit.Type->BoolFlag[BUILDING_INDEX].value || SyncRand(100) >= 66)) { //66% chance nothing will be dropped, unless the unit is a character or building, in which it case it will always drop an item @@ -7329,12 +7275,13 @@ void LetUnitDie(CUnit &unit, bool suicide) */ void DestroyAllInside(CUnit &source) { - CUnit *unit = source.UnitInside; + //copy the vector since we may modify it + const std::vector units_inside = source.get_units_inside(); // No Corpses, we are inside something, and we can't be seen - for (int i = source.InsideCount; i; --i, unit = unit->NextContained) { + for (CUnit *unit : units_inside) { // Transporter inside a transporter? - if (unit->UnitInside) { + if (unit->has_units_inside()) { DestroyAllInside(*unit); } UnitLost(*unit); @@ -7522,9 +7469,8 @@ void HitUnit_IncreaseScoreForKill(CUnit &attacker, CUnit &target, const bool inc attacker.Player->on_unit_destroyed(&target); //also increase score for units inside the target that will be destroyed when the target dies - if (target.UnitInside && include_contained_units) { - CUnit *boarded_unit = target.UnitInside; - for (int i = 0; i < target.InsideCount; ++i, boarded_unit = boarded_unit->NextContained) { + if (target.has_units_inside() && include_contained_units) { + for (CUnit *boarded_unit : target.get_units_inside()) { if (!boarded_unit->Type->BoolFlag[ITEM_INDEX].value) { //ignore items HitUnit_IncreaseScoreForKill(attacker, *boarded_unit, include_contained_units); } @@ -8169,7 +8115,7 @@ bool CanPickUp(const CUnit &picker, const CUnit &unit) if (&picker == &unit) { // Cannot pick up itself. return false; } - if (picker.HasInventory() && unit.Type->BoolFlag[ITEM_INDEX].value && picker.InsideCount >= ((int) UI.InventoryButtons.size())) { // full + if (picker.HasInventory() && unit.Type->BoolFlag[ITEM_INDEX].value && picker.get_units_inside().size() >= UI.InventoryButtons.size()) { //full if (picker.Player == CPlayer::GetThisPlayer()) { std::string picker_name = picker.Name + "'s (" + picker.get_type_name() + ")"; picker.Player->Notify(NotifyRed, picker.tilePos, picker.MapLayer->ID, _("%s inventory is full."), picker_name.c_str()); diff --git a/src/unit/unit.h b/src/unit/unit.h index 837443163..6c1f0eef3 100644 --- a/src/unit/unit.h +++ b/src/unit/unit.h @@ -487,6 +487,26 @@ class CUnit final CUnit *GetFirstContainer() const; + const std::vector &get_units_inside() const + { + return this->units_inside; + } + + bool has_units_inside() const + { + return !this->get_units_inside().empty(); + } + + void add_unit_inside(CUnit *unit) + { + this->units_inside.push_back(unit); + } + + void remove_unit_inside(CUnit *unit) + { + std::erase(this->units_inside, unit); + } + bool has_rally_point() const { return this->get_rally_point_pos().x() != -1 && this->get_rally_point_pos().y() != -1; @@ -793,12 +813,11 @@ class CUnit final CUnitManagerData UnitManagerData; size_t PlayerSlot; /// index in Player->Units - int InsideCount; /// Number of units inside. +private: + std::vector units_inside; +public: int BoardCount; /// Number of units transported inside. - CUnit *UnitInside; /// Pointer to one of the units inside. CUnit *Container; /// Pointer to the unit containing it (or 0) - CUnit *NextContained; /// Next unit in the container. - CUnit *PrevContained; /// Previous unit in the container. struct { std::vector> Workers; ///references to the workers assigned to this resource. diff --git a/src/unit/unit_save.cpp b/src/unit/unit_save.cpp index f774d3397..1e7e55d87 100644 --- a/src/unit/unit_save.cpp +++ b/src/unit/unit_save.cpp @@ -340,14 +340,16 @@ void SaveUnit(const CUnit &unit, CFile &file) file.printf(" \"units-boarded-count\", %d,", unit.BoardCount); //Wyrmgus start -// if (unit.UnitInside) { - if (unit.UnitInside && !(unit.get_character() != nullptr && unit.HasInventory())) { // don't save items for persistent heroes +// if (unit.has_units_inside()) { + if (unit.has_units_inside() && !(unit.get_character() != nullptr && unit.HasInventory())) { //don't save items for persistent heroes //Wyrmgus end file.printf("\n \"units-contained\", {"); - CUnit *uins = unit.UnitInside->PrevContained; - for (int i = unit.InsideCount; i; --i, uins = uins->PrevContained) { + bool first = true; + for (const CUnit *uins : unit.get_units_inside()) { file.printf("\"%s\"", UnitReference(uins).c_str()); - if (i > 1) { + if (first) { + first = false; + } else { file.printf(", "); } }