From 9956f9b538ce72f8c383552abe838fcdd9f9d28c Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Mon, 28 Aug 2023 13:20:06 -0400 Subject: [PATCH] Refactor groups UI state handling, throttle updates to only when changes occur --- src/droid.cpp | 31 +++++++++++ src/hci.cpp | 32 ++++++++++- src/hci.h | 4 ++ src/hci/groups.cpp | 133 +++++++++++++++++++++++++++++++++++---------- src/hci/groups.h | 3 + src/keybind.cpp | 1 + 6 files changed, 174 insertions(+), 30 deletions(-) diff --git a/src/droid.cpp b/src/droid.cpp index 31f0b3ec014..bdf70316342 100644 --- a/src/droid.cpp +++ b/src/droid.cpp @@ -288,6 +288,11 @@ int32_t droidDamage(DROID *psDroid, unsigned damage, WEAPON_CLASS weaponClass, W relativeDamage = objDamage(psDroid, damage, psDroid->originalBody, weaponClass, weaponSubClass, isDamagePerSecond, minDamage, empRadiusHit); + if (relativeDamage != 0 && psDroid->player == selectedPlayer && psDroid->group != UBYTE_MAX && psDroid->timeLastHit == gameTime) + { + intGroupDamaged(psDroid->group, (relativeDamage > 0) ? static_cast(relativeDamage) : 0, (relativeDamage < 0)); // update UI information + } + if (relativeDamage > 0) { // reset the attack level @@ -563,6 +568,11 @@ bool removeDroidBase(DROID *psDel) } } + if (psDel->group != UBYTE_MAX) + { + intGroupsChanged(); + } + if (psDel->player == selectedPlayer) { intRefreshScreen(); @@ -1821,6 +1831,7 @@ void assignDroidsToGroup(UDWORD playerNumber, UDWORD groupNumber, bool clearGrou { DROID *psDroid; bool bAtLeastOne = false; + size_t numCleared = 0; FLAG_POSITION *psFlagPos; ASSERT_OR_RETURN(, playerNumber < MAX_PLAYERS, "Invalid player: %" PRIu32 "", playerNumber); @@ -1834,6 +1845,7 @@ void assignDroidsToGroup(UDWORD playerNumber, UDWORD groupNumber, bool clearGrou if (clearGroup && psDroid->group == groupNumber) { psDroid->group = UBYTE_MAX; + ++numCleared; } /* Only assign the currently selected ones */ @@ -1857,6 +1869,10 @@ void assignDroidsToGroup(UDWORD playerNumber, UDWORD groupNumber, bool clearGrou groupConsoleInformOfCreation(groupNumber); secondarySetAverageGroupState(selectedPlayer, groupNumber); } + if (bAtLeastOne || numCleared > 0) + { + intGroupsChanged(); + } } @@ -1878,6 +1894,7 @@ void removeDroidsFromGroup(UDWORD playerNumber) if (removedCount) { groupConsoleInformOfRemoval(); + intGroupsChanged(); } } @@ -1885,6 +1902,7 @@ bool activateGroupAndMove(UDWORD playerNumber, UDWORD groupNumber) { DROID *psDroid, *psCentreDroid = nullptr; bool selected = false; + size_t numDeselected = 0; FLAG_POSITION *psFlagPos; ASSERT_OR_RETURN(false, playerNumber < MAX_PLAYERS, "Invalid player: %" PRIu32 "", playerNumber); @@ -1897,6 +1915,7 @@ bool activateGroupAndMove(UDWORD playerNumber, UDWORD groupNumber) if (psDroid->selected && psDroid->group != groupNumber) { DeSelectDroid(psDroid); + ++numDeselected; } /* Get the right ones */ if (psDroid->group == groupNumber) @@ -1940,6 +1959,11 @@ bool activateGroupAndMove(UDWORD playerNumber, UDWORD groupNumber) groupConsoleInformOfCentering(groupNumber); } + if (selected || numDeselected > 0) + { + intGroupsChanged(true); + } + return selected; } @@ -1974,6 +1998,7 @@ bool activateNoGroup(UDWORD playerNumber, const SELECTIONTYPE selectionType, con psFlagPos->selected = false; } } + intGroupsChanged(true); CONPRINTF(ngettext("%u unit selected", "%u units selected", selectionCount), selectionCount); return selected; } @@ -1982,6 +2007,7 @@ bool activateGroup(UDWORD playerNumber, UDWORD groupNumber) { DROID *psDroid; bool selected = false; + size_t numDeselected = 0; FLAG_POSITION *psFlagPos; ASSERT_OR_RETURN(false, playerNumber < MAX_PLAYERS, "Invalid player: %" PRIu32 "", playerNumber); @@ -1994,6 +2020,7 @@ bool activateGroup(UDWORD playerNumber, UDWORD groupNumber) if (psDroid->selected && psDroid->group != groupNumber) { DeSelectDroid(psDroid); + ++numDeselected; } /* Get the right ones */ if (psDroid->group == groupNumber) @@ -2015,6 +2042,10 @@ bool activateGroup(UDWORD playerNumber, UDWORD groupNumber) } groupConsoleInformOfSelection(groupNumber); } + if (selected || numDeselected > 0) + { + intGroupsChanged(true); + } return selected; } diff --git a/src/hci.cpp b/src/hci.cpp index ecfe1d77614..a96a970eeef 100644 --- a/src/hci.cpp +++ b/src/hci.cpp @@ -990,6 +990,7 @@ void interfaceShutDown() } static bool IntRefreshPending = false; +static bool IntGroupsRefreshPending = false; // Set widget refresh pending flag. // @@ -1003,6 +1004,11 @@ bool intIsRefreshing() return Refreshing; } +void intRefreshGroupsUI() +{ + IntGroupsRefreshPending = true; +} + // see if a delivery point is selected static FLAG_POSITION *intFindSelectedDelivPoint() @@ -1027,6 +1033,16 @@ static FLAG_POSITION *intFindSelectedDelivPoint() // void intDoScreenRefresh() { + if (IntGroupsRefreshPending && getGroupButtonEnabled()) + { + GroupsForum* groupsForum = (GroupsForum*)widgGetFromID(psWScreen, IDOBJ_GROUP); + if (groupsForum) + { + groupsForum->updateData(); + } + IntGroupsRefreshPending = false; + } + if (!IntRefreshPending) { return; @@ -2010,6 +2026,19 @@ void intAlliedResearchChanged() } } +void intGroupsChanged(bool selectionOnly) +{ + if (getGroupButtonEnabled()) + { + intRefreshGroupsUI(); + } +} + +void intGroupDamaged(UBYTE group, uint64_t additionalDamage, bool unitKilled) +{ + // FUTURE TODO: Could update the groups UI with group damage lastTime, severity, etc +} + bool intShowGroupSelectionMenu() { if (getGroupButtonEnabled()) @@ -2020,7 +2049,8 @@ bool intShowGroupSelectionMenu() auto newGroupsForum = GroupsForum::make(); psWScreen->psForm->attach(newGroupsForum); } - } else + } + else { widgDelete(psWScreen, IDOBJ_GROUP); } diff --git a/src/hci.h b/src/hci.h index 14ef7c243a3..5a1eb4c1874 100644 --- a/src/hci.h +++ b/src/hci.h @@ -304,6 +304,10 @@ void intSetMapPos(UDWORD x, UDWORD y); void intResearchFinished(STRUCTURE *psBuilding); void intAlliedResearchChanged(); +/* Tell the interface that groups have changed */ +void intGroupsChanged(bool selectionOnly = false); +void intGroupDamaged(UBYTE group, uint64_t additionalDamage, bool unitKilled); + /* Sync the interface to an object */ void intObjectSelected(BASE_OBJECT *psObj); diff --git a/src/hci/groups.cpp b/src/hci/groups.cpp index 521b252592a..10a83a7b2d0 100644 --- a/src/hci/groups.cpp +++ b/src/hci/groups.cpp @@ -31,27 +31,66 @@ void setGroupButtonEnabled(bool bNewState) bool getGroupButtonEnabled() { return groupButtonEnabled; - } +class GroupsUIController: public std::enable_shared_from_this +{ +public: + struct GroupDisplayInfo + { + size_t numberInGroup = 0; + DROID_TEMPLATE displayDroidTemplate; + }; +public: + GroupDisplayInfo* getGroupInfoAt(size_t index) + { + ASSERT_OR_RETURN(nullptr, index < groupInfo.size(), "Invalid group index (%zu); max: (%zu)", index, groupInfo.size()); + return &groupInfo[index]; + } + + size_t size() const + { + return groupInfo.size(); + } + + void updateData(); + + void selectGroup(size_t groupNumber) + { + // select the group + kf_SelectGrouping(groupNumber); + } + + void assignSelectedDroidsToGroup(size_t groupNumber) + { + assignDroidsToGroup(selectedPlayer, groupNumber, true); + } + +private: + std::array groupInfo; +}; + class GroupButton : public DynamicIntFancyButton { private: typedef DynamicIntFancyButton BaseWidget; + std::shared_ptr controller; std::shared_ptr groupNumberLabel; std::shared_ptr groupCountLabel; protected: GroupButton() : DynamicIntFancyButton() { } public: size_t groupNumber; - static std::shared_ptr make(size_t groupNumber) + static std::shared_ptr make(const std::shared_ptr& controller, size_t groupNumber) { class make_shared_enabler: public GroupButton {}; auto widget = std::make_shared(); + widget->controller = controller; widget->groupNumber = groupNumber; widget->initialize(); return widget; } + void initialize() { attach(groupNumberLabel = std::make_shared()); @@ -67,46 +106,31 @@ class GroupButton : public DynamicIntFancyButton void clickPrimary() override { - // select the group - kf_SelectGrouping(groupNumber); - + controller->selectGroup(groupNumber); } + void clickSecondary() override { - assignDroidsToGroup(selectedPlayer, groupNumber, true); + controller->assignSelectedDroidsToGroup(groupNumber); } protected: void display(int xOffset, int yOffset) override { - DROID *psDroid, *displayDroid = NULL; - size_t numberInGroup = 0; - std::map, size_t> unitcounter; - size_t most_droids_of_same_type_in_group = 0; - - for (psDroid = apsDroidLists[selectedPlayer]; psDroid != nullptr; psDroid = psDroid->psNext) + auto groupInfo = controller->getGroupInfoAt(groupNumber); + if (!groupInfo) { - // display whatever unit occurs the most in this group - if (psDroid->group == groupNumber) - { - // find the identifier for this droid - std::vector components = buildComponentsFromDroid(psDroid); - if (++unitcounter[components] > most_droids_of_same_type_in_group) - { - most_droids_of_same_type_in_group = unitcounter[components]; - displayDroid = psDroid; - } - numberInGroup++; - } + return; } - if (!numberInGroup) + if (!groupInfo->numberInGroup) { groupCountLabel->setString(""); displayBlank(xOffset, yOffset, false); - } else + } + else { - displayIMD(AtlasImage(), ImdObject::Droid(displayDroid), xOffset, yOffset); - groupCountLabel->setString(WzString::fromUtf8(astringf("%u", numberInGroup))); + displayIMD(AtlasImage(), ImdObject::DroidTemplate(&(groupInfo->displayDroidTemplate)), xOffset, yOffset); + groupCountLabel->setString(WzString::fromUtf8(astringf("%u", groupInfo->numberInGroup))); } } std::string getTip() override @@ -127,6 +151,8 @@ void GroupsForum::display(int xOffset, int yOffset) void GroupsForum::initialize() { + groupsUIController = std::make_shared(); + // the layout should be like this when the build menu is open id = IDOBJ_GROUP; setCalcLayout(LAMBDA_CALCLAYOUT_SIMPLE({ @@ -145,6 +171,55 @@ void GroupsForum::initialize() } } +void GroupsUIController::updateData() +{ + struct AccumulatedGroupInfo + { + size_t numberInGroup = 0; + DROID *displayDroid = nullptr; + std::map, size_t> unitcounter; + size_t most_droids_of_same_type_in_group = 0; + }; + + std::array calculatedGroupInfo; + for (DROID *psDroid = apsDroidLists[selectedPlayer]; psDroid != nullptr; psDroid = psDroid->psNext) + { + if (psDroid->group >= calculatedGroupInfo.size()) + { + continue; + } + + auto& currGroupInfo = calculatedGroupInfo[psDroid->group]; + + // display whatever unit occurs the most in this group + // find the identifier for this droid + std::vector components = buildComponentsFromDroid(psDroid); + if (++currGroupInfo.unitcounter[components] > currGroupInfo.most_droids_of_same_type_in_group) + { + currGroupInfo.most_droids_of_same_type_in_group = currGroupInfo.unitcounter[components]; + currGroupInfo.displayDroid = psDroid; + } + currGroupInfo.numberInGroup++; + } + + for (size_t idx = 0; idx < calculatedGroupInfo.size(); ++idx) + { + const auto& calculatedInfo = calculatedGroupInfo[idx]; + auto& storedGroupInfo = groupInfo[idx]; + storedGroupInfo.numberInGroup = calculatedInfo.numberInGroup; + if (calculatedInfo.numberInGroup > 0) + { + // generate a DROID_TEMPLATE from the displayDroid + templateSetParts(calculatedInfo.displayDroid, &(storedGroupInfo.displayDroidTemplate)); + } + } +} + +void GroupsForum::updateData() +{ + groupsUIController->updateData(); +} + void GroupsForum::addTabList() { attach(groupsList = IntListTabWidget::make(TabAlignment::RightAligned)); @@ -160,7 +235,7 @@ void GroupsForum::addTabList() std::shared_ptr GroupsForum::makeGroupButton(size_t groupNumber) { - return GroupButton::make(groupNumber); + return GroupButton::make(groupsUIController, groupNumber); } diff --git a/src/hci/groups.h b/src/hci/groups.h index c3d36e4e298..08f514ed9af 100644 --- a/src/hci/groups.h +++ b/src/hci/groups.h @@ -33,6 +33,7 @@ void setGroupButtonEnabled(bool bNewState); bool getGroupButtonEnabled(); class GroupButton; +class GroupsUIController; class GroupsForum: public IntFormAnimated { @@ -50,11 +51,13 @@ class GroupsForum: public IntFormAnimated widget->initialize(); return widget; } + void updateData(); private: std::shared_ptr makeGroupButton(size_t groupNumber); void addTabList(); private: std::shared_ptr groupsList; + std::shared_ptr groupsUIController; }; #endif // __INCLUDED_SRC_HCI_GROUPS_H__ diff --git a/src/keybind.cpp b/src/keybind.cpp index e261b900612..c627614c9c4 100644 --- a/src/keybind.cpp +++ b/src/keybind.cpp @@ -1106,6 +1106,7 @@ void kf_SelectGrouping(UDWORD groupNumber) { Selected = activateGroup(selectedPlayer, groupNumber); } + intGroupsChanged(true); /* play group audio but only if they weren't already selected - AM */ if (Selected && !bAlreadySelected)