diff --git a/data/base/images/intfac.img b/data/base/images/intfac.img index a72978822e0..7186f80cd20 100644 --- a/data/base/images/intfac.img +++ b/data/base/images/intfac.img @@ -446,3 +446,4 @@ 0,0,image_volume_mute.png 0,0,image_but_empty_up.png 0,0,image_but_empty_down.png +0,0,image_but_inner_glow.png diff --git a/data/base/images/intfac/image_but_inner_glow.png b/data/base/images/intfac/image_but_inner_glow.png new file mode 100644 index 00000000000..1d833684cc9 Binary files /dev/null and b/data/base/images/intfac/image_but_inner_glow.png differ diff --git a/src/hci.cpp b/src/hci.cpp index a96a970eeef..83dc731c3da 100644 --- a/src/hci.cpp +++ b/src/hci.cpp @@ -176,6 +176,7 @@ static enum _edit_pos_mode OBJECT_MODE objMode; std::shared_ptr interfaceController = nullptr; +std::shared_ptr groupsUI = nullptr; /* The current stats list being used by the stats screen */ static BASE_STATS **ppsStatsList; @@ -965,6 +966,8 @@ void interfaceShutDown() replayOverlayScreen = nullptr; } + groupsUI = nullptr; + psWScreen = nullptr; free(apsStructStatsList); @@ -1035,10 +1038,9 @@ void intDoScreenRefresh() { if (IntGroupsRefreshPending && getGroupButtonEnabled()) { - GroupsForum* groupsForum = (GroupsForum*)widgGetFromID(psWScreen, IDOBJ_GROUP); - if (groupsForum) + if (groupsUI) { - groupsForum->updateData(); + groupsUI->updateData(); } IntGroupsRefreshPending = false; } @@ -1493,6 +1495,7 @@ INT_RETVAL intRunWidgets() addIntelScreen(); // remove the groups Menu widgDelete(psWScreen, IDOBJ_GROUP); + groupsUI.reset(); reticuleCallback(RETBUT_INTELMAP); break; @@ -1507,6 +1510,7 @@ INT_RETVAL intRunWidgets() intShowPowerBar(); // remove the groups Menu widgDelete(psWScreen, IDOBJ_GROUP); + groupsUI.reset(); intAddDesign(false); intMode = INT_DESIGN; gInputManager.contexts().pushState(); @@ -2036,23 +2040,30 @@ void intGroupsChanged(bool selectionOnly) void intGroupDamaged(UBYTE group, uint64_t additionalDamage, bool unitKilled) { - // FUTURE TODO: Could update the groups UI with group damage lastTime, severity, etc + if (getGroupButtonEnabled()) + { + groupsUI->addGroupDamageForCurrentTick(group, additionalDamage, unitKilled); + } } bool intShowGroupSelectionMenu() { if (getGroupButtonEnabled()) { - GroupsForum* groupsForum = (GroupsForum*)widgGetFromID(psWScreen, IDOBJ_GROUP); - if (!groupsForum) + GroupsForum* groupsUIFormWidg = (GroupsForum*)widgGetFromID(psWScreen, IDOBJ_GROUP); + if (!groupsUIFormWidg) { - auto newGroupsForum = GroupsForum::make(); - psWScreen->psForm->attach(newGroupsForum); + if (!groupsUI) + { + groupsUI = GroupsForum::make(); + } + psWScreen->psForm->attach(groupsUI); } } else { widgDelete(psWScreen, IDOBJ_GROUP); + groupsUI.reset(); } return true; } diff --git a/src/hci/groups.cpp b/src/hci/groups.cpp index 10a83a7b2d0..f150452133e 100644 --- a/src/hci/groups.cpp +++ b/src/hci/groups.cpp @@ -39,7 +39,15 @@ class GroupsUIController: public std::enable_shared_from_this(curr.lastAccumulatedDamage, 1); + } + } + void selectGroup(size_t groupNumber) { // select the group @@ -77,10 +97,11 @@ class GroupButton : public DynamicIntFancyButton std::shared_ptr controller; std::shared_ptr groupNumberLabel; std::shared_ptr groupCountLabel; + size_t groupNumber; + uint32_t lastUpdatedGlowAlphaTime = 0; protected: GroupButton() : DynamicIntFancyButton() { } public: - size_t groupNumber; static std::shared_ptr make(const std::shared_ptr& controller, size_t groupNumber) { class make_shared_enabler: public GroupButton {}; @@ -102,6 +123,8 @@ class GroupButton : public DynamicIntFancyButton groupCountLabel->setGeometry(OBJ_TEXTX + 40, OBJ_B1TEXTY + 20, 16, 16); groupCountLabel->setString(""); groupCountLabel->setTransparentToMouse(true); + + buttonBackgroundEmpty = true; } void clickPrimary() override @@ -122,6 +145,29 @@ class GroupButton : public DynamicIntFancyButton { return; } + + if (groupInfo->lastAccumulatedDamage > 0) + { + groupInfo->currAttackGlowAlpha = std::max(groupInfo->currAttackGlowAlpha, accumulatedDamageToTargetGlowAlpha(groupInfo->lastAccumulatedDamage, groupInfo->totalGroupMaxHealth, groupInfo->lastUnitsKilled)); + lastUpdatedGlowAlphaTime = realTime; + // reset consumed values + groupInfo->lastAccumulatedDamage = 0; + groupInfo->lastUnitsKilled = 0; + } + else if (groupInfo->currAttackGlowAlpha > 0 && (realTime - lastUpdatedGlowAlphaTime) > 100) + { + auto fadeAmount = 50 * (realTime - lastUpdatedGlowAlphaTime) / GAME_TICKS_PER_SEC; + if (fadeAmount > static_cast(groupInfo->currAttackGlowAlpha)) + { + groupInfo->currAttackGlowAlpha = 0; + } + else + { + groupInfo->currAttackGlowAlpha -= static_cast(fadeAmount); + } + lastUpdatedGlowAlphaTime = realTime; + } + if (!groupInfo->numberInGroup) { groupCountLabel->setString(""); @@ -132,6 +178,12 @@ class GroupButton : public DynamicIntFancyButton displayIMD(AtlasImage(), ImdObject::DroidTemplate(&(groupInfo->displayDroidTemplate)), xOffset, yOffset); groupCountLabel->setString(WzString::fromUtf8(astringf("%u", groupInfo->numberInGroup))); } + + if (groupInfo->currAttackGlowAlpha > 0) + { + // Based on damage, display an inner glow (animated) + iV_DrawImageTint(IntImages, IMAGE_BUT_INNER_GLOW, xOffset + x(), yOffset + y(), pal_RGBA(170, 0, 0, groupInfo->currAttackGlowAlpha)); + } } std::string getTip() override { @@ -141,6 +193,17 @@ class GroupButton : public DynamicIntFancyButton { return false; } +private: + #define MAX_DAMAGE_GLOW_ALPHA 100 + inline uint8_t accumulatedDamageToTargetGlowAlpha(uint64_t accumulatedDamage, uint64_t totalGroupMaxHealth, uint64_t unitsKilled) + { + uint64_t damageVisualPercent = (accumulatedDamage * 100) / totalGroupMaxHealth; + if (unitsKilled > 0) + { + damageVisualPercent = 100; + } + return static_cast((std::min(damageVisualPercent, 100) * MAX_DAMAGE_GLOW_ALPHA) / 100); + } }; void GroupsForum::display(int xOffset, int yOffset) @@ -176,6 +239,7 @@ void GroupsUIController::updateData() struct AccumulatedGroupInfo { size_t numberInGroup = 0; + uint64_t totalGroupMaxHealth = 0; DROID *displayDroid = nullptr; std::map, size_t> unitcounter; size_t most_droids_of_same_type_in_group = 0; @@ -200,6 +264,7 @@ void GroupsUIController::updateData() currGroupInfo.displayDroid = psDroid; } currGroupInfo.numberInGroup++; + currGroupInfo.totalGroupMaxHealth += psDroid->originalBody; } for (size_t idx = 0; idx < calculatedGroupInfo.size(); ++idx) @@ -207,6 +272,7 @@ void GroupsUIController::updateData() const auto& calculatedInfo = calculatedGroupInfo[idx]; auto& storedGroupInfo = groupInfo[idx]; storedGroupInfo.numberInGroup = calculatedInfo.numberInGroup; + storedGroupInfo.totalGroupMaxHealth = calculatedInfo.totalGroupMaxHealth; if (calculatedInfo.numberInGroup > 0) { // generate a DROID_TEMPLATE from the displayDroid @@ -220,6 +286,11 @@ void GroupsForum::updateData() groupsUIController->updateData(); } +void GroupsForum::addGroupDamageForCurrentTick(size_t group, uint64_t additionalDamage, bool unitKilled) +{ + groupsUIController->addGroupDamageForCurrentTick(group, additionalDamage, unitKilled); +} + void GroupsForum::addTabList() { attach(groupsList = IntListTabWidget::make(TabAlignment::RightAligned)); diff --git a/src/hci/groups.h b/src/hci/groups.h index 08f514ed9af..a43ec2732dd 100644 --- a/src/hci/groups.h +++ b/src/hci/groups.h @@ -52,6 +52,7 @@ class GroupsForum: public IntFormAnimated return widget; } void updateData(); + void addGroupDamageForCurrentTick(size_t group, uint64_t additionalDamage, bool unitKilled); private: std::shared_ptr makeGroupButton(size_t groupNumber); void addTabList(); diff --git a/src/intfac.h b/src/intfac.h index 7e6cb08314d..bbd0aa1a513 100644 --- a/src/intfac.h +++ b/src/intfac.h @@ -480,6 +480,7 @@ enum INTFAC_TYPE IMAGE_INTFAC_VOLUME_MUTE, IMAGE_BUT_EMPTY_UP, IMAGE_BUT_EMPTY_DOWN, + IMAGE_BUT_INNER_GLOW }; #endif //__INCLUDED_SRC_INTFAC_H__