1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2026-02-11 13:08:30 +00:00

Rework spell icon update (#8946)

This commit is contained in:
Alexei Kotov 2026-01-31 23:06:29 +03:00
parent 3ac4a744a8
commit 1241bf270c
2 changed files with 113 additions and 183 deletions

View file

@ -1,6 +1,5 @@
#include "spellicons.hpp"
#include <format>
#include <iomanip>
#include <sstream>
@ -24,184 +23,139 @@
namespace MWGui
{
void SpellIcons::updateWidgets(MyGUI::Widget* parent, bool adjustSize)
namespace
{
MWWorld::Ptr player = MWMechanics::getPlayer();
const MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
std::map<ESM::RefId, std::vector<MagicEffectInfo>> effects;
for (const auto& params : stats.getActiveSpells())
MyGUI::ImageBox* createIcon(MyGUI::Widget& parent, std::string_view name, std::string_view icon, int size)
{
for (const auto& effect : params.getEffects())
{
if (!(effect.mFlags & ESM::ActiveEffect::Flag_Applied))
continue;
MagicEffectInfo newEffectSource;
newEffectSource.mKey = MWMechanics::EffectKey(effect.mEffectId, effect.getSkillOrAttribute());
newEffectSource.mMagnitude = static_cast<int>(effect.mMagnitude);
newEffectSource.mPermanent = effect.mDuration == -1.f;
newEffectSource.mRemainingTime = effect.mTimeLeft;
newEffectSource.mSource = params.getDisplayName();
newEffectSource.mTotalTime = effect.mDuration;
effects[effect.mEffectId].push_back(std::move(newEffectSource));
}
const VFS::Manager& vfs = *MWBase::Environment::get().getResourceSystem()->getVFS();
const MyGUI::IntCoord coord(0, 0, size, size);
MyGUI::ImageBox* widget = parent.createWidget<MyGUI::ImageBox>("ImageBox", coord, MyGUI::Align::Default);
widget->setImageTexture(Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(icon), vfs));
ToolTipInfo tooltipInfo;
tooltipInfo.caption = name;
tooltipInfo.icon = icon;
tooltipInfo.imageSize = size;
tooltipInfo.wordWrap = false;
widget->setUserData(tooltipInfo);
widget->setUserString("ToolTipType", "ToolTipInfo");
widget->setVisible(false);
return widget;
}
int w = 2;
const auto& store = MWBase::Environment::get().getESMStore();
for (const auto& [effectId, effectInfos] : effects)
std::string printEffectMagnitude(float srcMagnitude, ESM::MagicEffect::MagnitudeDisplayType displayType)
{
const ESM::MagicEffect* effect = store->get<ESM::MagicEffect>().find(effectId);
std::string result;
if (displayType == ESM::MagicEffect::MDT_None)
return result;
float remainingDuration = 0;
float totalDuration = 0;
std::string sourcesDescription;
static const float fadeTime
= store->get<ESM::GameSetting>().find("fMagicStartIconBlink")->mValue.getFloat();
bool addNewLine = false;
for (const MagicEffectInfo& effectInfo : effectInfos)
const int magnitude = static_cast<int>(srcMagnitude);
if (displayType == ESM::MagicEffect::MDT_TimesInt)
{
if (addNewLine)
sourcesDescription += '\n';
// if at least one of the effect sources is permanent, the effect will never wear off
if (effectInfo.mPermanent)
{
remainingDuration = fadeTime;
totalDuration = fadeTime;
}
else
{
remainingDuration = std::max(remainingDuration, effectInfo.mRemainingTime);
totalDuration = std::max(totalDuration, effectInfo.mTotalTime);
}
sourcesDescription += effectInfo.mSource;
if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill)
{
const ESM::Skill* skill = store->get<ESM::Skill>().find(effectInfo.mKey.mArg);
sourcesDescription += " (" + skill->mName + ')';
}
if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
{
const ESM::Attribute* attribute = store->get<ESM::Attribute>().find(effectInfo.mKey.mArg);
sourcesDescription += " (" + attribute->mName + ')';
}
ESM::MagicEffect::MagnitudeDisplayType displayType = effect->getMagnitudeDisplayType();
if (displayType == ESM::MagicEffect::MDT_TimesInt)
{
std::string_view timesInt
= MWBase::Environment::get().getWindowManager()->getGameSettingString("sXTimesINT", {});
std::stringstream formatter;
formatter << std::fixed << std::setprecision(1) << " " << (effectInfo.mMagnitude / 10.0f)
<< timesInt;
sourcesDescription += formatter.str();
}
else if (displayType != ESM::MagicEffect::MDT_None)
{
sourcesDescription += ": " + MyGUI::utility::toString(effectInfo.mMagnitude);
if (displayType == ESM::MagicEffect::MDT_Percentage)
sourcesDescription
+= MWBase::Environment::get().getWindowManager()->getGameSettingString("spercent", {});
else if (displayType == ESM::MagicEffect::MDT_Feet)
{
sourcesDescription += ' ';
sourcesDescription
+= MWBase::Environment::get().getWindowManager()->getGameSettingString("sfeet", {});
}
else if (displayType == ESM::MagicEffect::MDT_Level)
{
sourcesDescription += ' ';
if (effectInfo.mMagnitude > 1)
sourcesDescription
+= MWBase::Environment::get().getWindowManager()->getGameSettingString("sLevels", {});
else
sourcesDescription
+= MWBase::Environment::get().getWindowManager()->getGameSettingString("sLevel", {});
}
else // ESM::MagicEffect::MDT_Points
{
sourcesDescription += ' ';
if (effectInfo.mMagnitude > 1)
sourcesDescription
+= MWBase::Environment::get().getWindowManager()->getGameSettingString("spoints", {});
else
sourcesDescription
+= MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", {});
}
}
if (effectInfo.mRemainingTime > -1 && Settings::game().mShowEffectDuration)
sourcesDescription
+= MWGui::ToolTips::getDurationString(effectInfo.mRemainingTime, " #{sDuration}");
addNewLine = true;
std::stringstream formatter;
formatter << std::fixed << std::setprecision(1) << " " << (magnitude / 10.0f);
result += formatter.str();
}
else
{
result += ": " + MyGUI::utility::toString(magnitude);
if (displayType != ESM::MagicEffect::MDT_Percentage)
result += ' ';
}
if (remainingDuration > 0.f)
std::string_view unit;
if (displayType == ESM::MagicEffect::MDT_TimesInt)
unit = "sXTimesINT";
else if (displayType == ESM::MagicEffect::MDT_Percentage)
unit = "sPercent";
else if (displayType == ESM::MagicEffect::MDT_Feet)
unit = "sFeet";
else if (displayType == ESM::MagicEffect::MDT_Level)
unit = magnitude > 1 ? "sLevels" : "sLevel";
else if (displayType == ESM::MagicEffect::MDT_Points)
unit = magnitude > 1 ? "sPoints" : "sPoint";
result += MWBase::Environment::get().getWindowManager()->getGameSettingString(unit, {});
return result;
}
}
void SpellIcons::updateWidgets(MyGUI::Widget* parent, bool adjustSize)
{
for (auto& [effectId, widget] : mWidgetMap)
{
widget->setVisible(false);
widget->setAlpha(1.f);
widget->getUserData<ToolTipInfo>()->text.clear();
}
int horizontalOffset = 2;
constexpr int verticalOffset = 2;
constexpr int size = 16;
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
static const float fadeTime = store.get<ESM::GameSetting>().find("fMagicStartIconBlink")->mValue.getFloat();
const MWWorld::Ptr player = MWMechanics::getPlayer();
const MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
for (const auto& params : stats.getActiveSpells())
{
for (const auto& source : params.getEffects())
{
MyGUI::ImageBox* image;
if (!(source.mFlags & ESM::ActiveEffect::Flag_Applied))
continue;
if (source.mDuration != -1.f && source.mTimeLeft <= 0.f)
continue;
const ESM::RefId effectId = source.mEffectId;
const ESM::MagicEffect& effect = *store.get<ESM::MagicEffect>().find(effectId);
const ESM::RefId arg = source.getSkillOrAttribute();
if (mWidgetMap.find(effectId) == mWidgetMap.end())
mWidgetMap[effectId] = createIcon(*parent, effect.mName, effect.mIcon, size);
MyGUI::ImageBox& widget = *mWidgetMap[effectId];
if (!widget.getVisible())
{
image = parent->createWidget<MyGUI::ImageBox>(
"ImageBox", MyGUI::IntCoord(w, 2, 16, 16), MyGUI::Align::Default);
mWidgetMap[effectId] = image;
image->setImageTexture(
Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(effect->mIcon),
*MWBase::Environment::get().getResourceSystem()->getVFS()));
ToolTipInfo tooltipInfo;
tooltipInfo.caption = effect->mName;
tooltipInfo.icon = effect->mIcon;
tooltipInfo.imageSize = 16;
tooltipInfo.wordWrap = false;
image->setUserData(tooltipInfo);
image->setUserString("ToolTipType", "ToolTipInfo");
widget.setPosition(horizontalOffset, verticalOffset);
widget.setVisible(true);
if (source.mDuration >= fadeTime && fadeTime > 0.f)
widget.setAlpha(std::min(source.mTimeLeft / fadeTime, 1.f));
horizontalOffset += size;
}
else
image = mWidgetMap[effectId];
image->setPosition(w, 2);
image->setVisible(true);
w += 16;
std::string& desc = widget.getUserData<ToolTipInfo>()->text;
if (!desc.empty())
desc += '\n';
ToolTipInfo* tooltipInfo = image->getUserData<ToolTipInfo>();
tooltipInfo->text = std::move(sourcesDescription);
desc += params.getDisplayName();
// Fade out
if (totalDuration >= fadeTime && fadeTime > 0.f)
image->setAlpha(std::min(remainingDuration / fadeTime, 1.f));
}
else if (mWidgetMap.find(effectId) != mWidgetMap.end())
{
MyGUI::ImageBox* image = mWidgetMap[effectId];
image->setVisible(false);
image->setAlpha(1.f);
if (effect.mData.mFlags & ESM::MagicEffect::TargetSkill)
{
const ESM::Skill& skill = *store.get<ESM::Skill>().find(arg);
desc += " (" + skill.mName + ')';
}
if (effect.mData.mFlags & ESM::MagicEffect::TargetAttribute)
{
const ESM::Attribute& attribute = *store.get<ESM::Attribute>().find(arg);
desc += " (" + attribute.mName + ')';
}
desc += printEffectMagnitude(source.mMagnitude, effect.getMagnitudeDisplayType());
if (source.mTimeLeft > -1 && Settings::game().mShowEffectDuration)
desc += MWGui::ToolTips::getDurationString(source.mTimeLeft, " #{sDuration}");
}
}
if (adjustSize)
{
int s = w + 2;
if (effects.empty())
s = 0;
int diff = parent->getWidth() - s;
parent->setSize(s, parent->getHeight());
const int newWidth = horizontalOffset > 2 ? horizontalOffset + 2 : 0;
const int diff = parent->getWidth() - newWidth;
parent->setSize(newWidth, parent->getHeight());
parent->setPosition(parent->getLeft() + diff, parent->getTop());
}
// hide inactive effects
for (auto& widgetPair : mWidgetMap)
{
if (effects.find(widgetPair.first) == effects.end())
widgetPair.second->setVisible(false);
}
}
}

View file

@ -1,43 +1,19 @@
#ifndef MWGUI_SPELLICONS_H
#define MWGUI_SPELLICONS_H
#include <string>
#include <vector>
#include <map>
#include "../mwmechanics/magiceffects.hpp"
#include <components/esm/refid.hpp>
namespace MyGUI
{
class Widget;
class ImageBox;
}
namespace ESM
{
struct ENAMstruct;
struct EffectList;
}
namespace MWGui
{
// information about a single magic effect source as required for display in the tooltip
struct MagicEffectInfo
{
MagicEffectInfo()
: mMagnitude(0)
, mRemainingTime(0.f)
, mTotalTime(0.f)
, mPermanent(false)
{
}
std::string mSource; // display name for effect source (e.g. potion name)
MWMechanics::EffectKey mKey;
int mMagnitude;
float mRemainingTime;
float mTotalTime;
bool mPermanent; // the effect is permanent
};
class SpellIcons
{
public: