Browse Source

move floating damage nums to lua mod & add xp mod

pull/8415/head
Trihedraf 2 months ago committed by Anders Jenbo
parent
commit
32a0666201
  1. 2
      CMake/Assets.cmake
  2. 2
      Source/CMakeLists.txt
  3. 2
      Source/engine/render/scrollrt.cpp
  4. 29
      Source/lua/lua_global.cpp
  5. 6
      Source/lua/lua_global.hpp
  6. 51
      Source/lua/modules/floatingnumbers.cpp
  7. 9
      Source/lua/modules/floatingnumbers.hpp
  8. 18
      Source/lua/modules/monsters.cpp
  9. 10
      Source/lua/modules/player.cpp
  10. 4
      Source/monster.cpp
  11. 9
      Source/options.cpp
  12. 11
      Source/options.h
  13. 6
      Source/player.cpp
  14. 133
      Source/qol/floatingnumbers.cpp
  15. 11
      Source/qol/floatingnumbers.h
  16. 17
      assets/lua/devilutionx/events.lua
  17. 88
      assets/lua/mods/Floating Numbers - Damage/init.lua
  18. 25
      assets/lua/mods/Floating Numbers - XP/init.lua

2
CMake/Assets.cmake

@ -159,6 +159,8 @@ set(devilutionx_assets
lua/devilutionx/events.lua
lua/inspect.lua
lua/mods/clock/init.lua
"lua/mods/Floating Numbers - Damage/init.lua"
"lua/mods/Floating Numbers - XP/init.lua"
lua/repl_prelude.lua
plrgfx/warrior/whu/whufm.trn
plrgfx/warrior/whu/whulm.trn

2
Source/CMakeLists.txt

@ -117,6 +117,7 @@ set(libdevilutionx_SRCS
lua/modules/dev/quests.cpp
lua/modules/dev/search.cpp
lua/modules/dev/towners.cpp
lua/modules/floatingnumbers.cpp
lua/modules/i18n.cpp
lua/modules/items.cpp
lua/modules/log.cpp
@ -653,6 +654,7 @@ target_link_dependencies(libdevilutionx_player
DevilutionX::SDL
fmt::fmt
magic_enum::magic_enum
sol2::sol2
tl
unordered_dense::unordered_dense
libdevilutionx_game_mode

2
Source/engine/render/scrollrt.cpp

@ -1816,7 +1816,7 @@ void DrawAndBlit()
const Rectangle &mainPanel = GetMainPanel();
if (gnScreenWidth > mainPanel.size.width || IsRedrawEverything() || *GetOptions().Gameplay.enableFloatingNumbers != FloatingNumbers::Off) {
if (gnScreenWidth > mainPanel.size.width || IsRedrawEverything()) {
drawHealth = true;
drawMana = true;
drawControlButtons = true;

29
Source/lua/lua_global.cpp

@ -13,6 +13,7 @@
#include "effects.h"
#include "engine/assets.hpp"
#include "lua/modules/audio.hpp"
#include "lua/modules/floatingnumbers.hpp"
#include "lua/modules/hellfire.hpp"
#include "lua/modules/i18n.hpp"
#include "lua/modules/items.hpp"
@ -21,7 +22,9 @@
#include "lua/modules/player.hpp"
#include "lua/modules/render.hpp"
#include "lua/modules/towners.hpp"
#include "monster.h"
#include "options.h"
#include "player.h"
#include "plrmsg.h"
#include "utils/console.h"
#include "utils/log.hpp"
@ -283,6 +286,7 @@ void LuaInitialize()
"devilutionx.render", LuaRenderModule(lua),
"devilutionx.towners", LuaTownersModule(lua),
"devilutionx.hellfire", LuaHellfireModule(lua),
"devilutionx.floatingnumbers", LuaFloatingNumbersModule(lua),
"devilutionx.message", [](std::string_view text) { EventPlrMsg(text, UiFlags::ColorRed); },
// This package is loaded without a sandbox:
"inspect", RunScript(/*env=*/std::nullopt, "inspect", /*optional=*/false));
@ -305,7 +309,8 @@ void LuaShutdown()
CurrentLuaState = std::nullopt;
}
void LuaEvent(std::string_view name)
template <typename... Args>
void CallLuaEvent(std::string_view name, Args &&...args)
{
if (!CurrentLuaState.has_value()) {
return;
@ -317,7 +322,27 @@ void LuaEvent(std::string_view name)
return;
}
const sol::protected_function fn = trigger->as<sol::protected_function>();
SafeCallResult(fn(), /*optional=*/true);
SafeCallResult(fn(std::forward<Args>(args)...), /*optional=*/true);
}
void LuaEvent(std::string_view name)
{
CallLuaEvent(name);
}
void LuaEvent(std::string_view name, const Player *player, int arg1, int arg2)
{
CallLuaEvent(name, player, arg1, arg2);
}
void LuaEvent(std::string_view name, const Monster *monster, int arg1, int arg2)
{
CallLuaEvent(name, monster, arg1, arg2);
}
void LuaEvent(std::string_view name, const Player *player, uint32_t arg1)
{
CallLuaEvent(name, player, arg1);
}
sol::state &GetLuaState()

6
Source/lua/lua_global.hpp

@ -8,10 +8,16 @@
namespace devilution {
struct Player;
struct Monster;
void LuaInitialize();
void LuaReloadActiveMods();
void LuaShutdown();
void LuaEvent(std::string_view name);
void LuaEvent(std::string_view name, const Player *player, int arg1, int arg2);
void LuaEvent(std::string_view name, const Monster *monster, int arg1, int arg2);
void LuaEvent(std::string_view name, const Player *player, uint32_t arg1);
sol::state &GetLuaState();
sol::environment CreateLuaSandbox();
sol::object SafeCallResult(sol::protected_function_result result, bool optional);

51
Source/lua/modules/floatingnumbers.cpp

@ -0,0 +1,51 @@
#include "lua/modules/floatingnumbers.hpp"
#include <sol/sol.hpp>
#ifdef USE_SDL3
#include <SDL3/SDL_timer.h>
#else
#include <SDL.h>
#endif
#include "DiabloUI/ui_flags.hpp"
#include "engine/point.hpp"
#include "lua/metadoc.hpp"
#include "qol/floatingnumbers.h"
namespace devilution {
sol::table LuaFloatingNumbersModule(sol::state_view &lua)
{
sol::table table = lua.create_table();
LuaSetDocFn(table, "add", "(text: string, pos: Point, style: UiFlags, id: integer = 0, reverseDirection: boolean = false)",
"Add a floating number",
[](const std::string &text, Point pos, UiFlags style, std::optional<int> id, std::optional<bool> reverseDirection) {
AddFloatingNumber(pos, { 0, 0 }, text, style, id.value_or(0), reverseDirection.value_or(false));
});
LuaSetDocFn(table, "get_ticks", "() -> integer", "Returns the number of milliseconds since the game started.",
[]() { return static_cast<int>(SDL_GetTicks()); });
auto flags = lua.create_table();
flags["DarkRed"] = UiFlags::ColorUiSilver;
flags["Yellow"] = UiFlags::ColorYellow;
flags["Gold"] = UiFlags::ColorGold;
flags["Black"] = UiFlags::ColorBlack;
flags["White"] = UiFlags::ColorWhite;
flags["WhiteGold"] = UiFlags::ColorWhitegold;
flags["Red"] = UiFlags::ColorRed;
flags["Blue"] = UiFlags::ColorBlue;
flags["Orange"] = UiFlags::ColorOrange;
flags["Small"] = UiFlags::FontSize12;
flags["Medium"] = UiFlags::FontSize24;
flags["Large"] = UiFlags::FontSize30;
table["Flags"] = flags;
return table;
}
} // namespace devilution

9
Source/lua/modules/floatingnumbers.hpp

@ -0,0 +1,9 @@
#pragma once
#include <sol/forward.hpp>
namespace devilution {
sol::table LuaFloatingNumbersModule(sol::state_view &lua);
} // namespace devilution

18
Source/lua/modules/monsters.cpp

@ -6,7 +6,9 @@
#include <sol/sol.hpp>
#include "data/file.hpp"
#include "engine/point.hpp"
#include "lua/metadoc.hpp"
#include "monster.h"
#include "tables/monstdat.h"
#include "utils/language.h"
#include "utils/str_split.hpp"
@ -27,10 +29,26 @@ void AddUniqueMonsterDataFromTsv(const std::string_view path)
LoadUniqueMonstDatFromFile(dataFile, path);
}
void InitMonsterUserType(sol::state_view &lua)
{
sol::usertype<Monster> monsterType = lua.new_usertype<Monster>(sol::no_constructor);
LuaSetDocReadonlyProperty(monsterType, "position", "Point",
"Monster's current position (readonly)",
[](const Monster &monster) {
return Point { monster.position.tile };
});
LuaSetDocReadonlyProperty(monsterType, "id", "integer",
"Monster's unique ID (readonly)",
[](const Monster &monster) {
return static_cast<int>(reinterpret_cast<uintptr_t>(&monster));
});
}
} // namespace
sol::table LuaMonstersModule(sol::state_view &lua)
{
InitMonsterUserType(lua);
sol::table table = lua.create_table();
LuaSetDocFn(table, "addMonsterDataFromTsv", "(path: string)", AddMonsterDataFromTsv);
LuaSetDocFn(table, "addUniqueMonsterDataFromTsv", "(path: string)", AddUniqueMonsterDataFromTsv);

10
Source/lua/modules/player.cpp

@ -20,6 +20,16 @@ void InitPlayerUserType(sol::state_view &lua)
LuaSetDocReadonlyProperty(playerType, "name", "string",
"Player's name (readonly)",
&Player::name);
LuaSetDocReadonlyProperty(playerType, "id", "integer",
"Player's unique ID (readonly)",
[](const Player &player) {
return static_cast<int>(reinterpret_cast<uintptr_t>(&player));
});
LuaSetDocReadonlyProperty(playerType, "position", "Point",
"Player's current position (readonly)",
[](const Player &player) -> Point {
return Point { player.position.tile };
});
LuaSetDocFn(playerType, "addExperience", "(experience: integer, monsterLevel: integer = nil)",
"Adds experience to this player based on the current game mode",
[](Player &player, uint32_t experience, std::optional<int> monsterLevel) {

4
Source/monster.cpp

@ -69,6 +69,7 @@
#include "levels/tile_properties.hpp"
#include "levels/trigs.h"
#include "lighting.h"
#include "lua/lua_global.hpp"
#include "minitext.h"
#include "missiles.h"
#include "movie.h"
@ -77,7 +78,6 @@
#include "objects.h"
#include "options.h"
#include "player.h"
#include "qol/floatingnumbers.h"
#include "quests.h"
#include "sound_effect_enums.h"
#include "storm/storm_net.hpp"
@ -3778,7 +3778,7 @@ void AddDoppelganger(Monster &monster)
void ApplyMonsterDamage(DamageType damageType, Monster &monster, int damage)
{
AddFloatingNumber(damageType, monster, damage);
LuaEvent("OnMonsterTakeDamage", &monster, damage, static_cast<int>(damageType));
monster.hitPoints -= damage;

9
Source/options.cpp

@ -72,7 +72,7 @@ namespace {
void DiscoverMods()
{
// Add mods available by default:
std::unordered_set<std::string> modNames = { "clock" };
std::unordered_set<std::string> modNames = { "clock", "Floating Numbers - Damage", "Floating Numbers - XP" };
if (HaveHellfire()) {
modNames.insert("Hellfire");
@ -870,12 +870,6 @@ GameplayOptions::GameplayOptions()
, numFullManaPotionPickup("Full Mana Potion Pickup", OptionEntryFlags::None, N_("Full Mana Potion Pickup"), N_("Number of Full Mana potions to pick up automatically."), 0, { 0, 1, 2, 4, 8, 16 })
, numRejuPotionPickup("Rejuvenation Potion Pickup", OptionEntryFlags::None, N_("Rejuvenation Potion Pickup"), N_("Number of Rejuvenation potions to pick up automatically."), 0, { 0, 1, 2, 4, 8, 16 })
, numFullRejuPotionPickup("Full Rejuvenation Potion Pickup", OptionEntryFlags::None, N_("Full Rejuvenation Potion Pickup"), N_("Number of Full Rejuvenation potions to pick up automatically."), 0, { 0, 1, 2, 4, 8, 16 })
, enableFloatingNumbers("Enable floating numbers", OptionEntryFlags::None, N_("Enable floating numbers"), N_("Enables floating numbers on gaining XP / dealing damage etc."), FloatingNumbers::Off,
{
{ FloatingNumbers::Off, N_("Off") },
{ FloatingNumbers::Random, N_("Random Angles") },
{ FloatingNumbers::Vertical, N_("Vertical Only") },
})
, skipLoadingScreenThresholdMs("Skip loading screen threshold, ms", OptionEntryFlags::Invisible, "", "", 0)
{
}
@ -902,7 +896,6 @@ std::vector<OptionEntryBase *> GameplayOptions::GetEntries()
&floatingInfoBox,
&showMonsterType,
&showItemLabels,
&enableFloatingNumbers,
&autoRefillBelt,
&autoEquipWeapons,
&autoEquipArmor,

11
Source/options.h

@ -92,15 +92,6 @@ enum class Resampler : uint8_t {
std::string_view ResamplerToString(Resampler resampler);
std::optional<Resampler> ResamplerFromString(std::string_view resampler);
enum class FloatingNumbers : uint8_t {
/** @brief Show no floating numbers. */
Off = 0,
/** @brief Show floating numbers at random angles. */
Random = 1,
/** @brief Show floating numbers vertically only. */
Vertical = 2,
};
enum class OptionEntryType : uint8_t {
Boolean,
List,
@ -644,8 +635,6 @@ struct GameplayOptions : OptionCategoryBase {
OptionEntryInt<int> numRejuPotionPickup;
/** @brief Number of Full Rejuvenating potions to pick up automatically */
OptionEntryInt<int> numFullRejuPotionPickup;
/** @brief Enable floating numbers. */
OptionEntryEnum<FloatingNumbers> enableFloatingNumbers;
/**
* @brief If loading takes less than this value, skips displaying the loading screen.

6
Source/player.cpp

@ -41,6 +41,7 @@
#include "levels/trigs.h"
#include "lighting.h"
#include "loadsave.h"
#include "lua/lua_global.hpp"
#include "minitext.h"
#include "missiles.h"
#include "monster.h"
@ -49,7 +50,6 @@
#include "options.h"
#include "player.h"
#include "qol/autopickup.h"
#include "qol/floatingnumbers.h"
#include "qol/stash.h"
#include "spells.h"
#include "stores.h"
@ -2442,6 +2442,8 @@ void Player::_addExperience(uint32_t experience, int levelDelta)
clampedExp = std::min<uint32_t>({ clampedExp, /* level 1-5: */ getNextExperienceThreshold() / 20U, /* level 6-50: */ 200U * getCharacterLevel() });
}
LuaEvent("OnPlayerGainExperience", this, clampedExp);
const uint32_t maxExperience = GetNextExperienceThresholdForLevel(getMaxCharacterLevel());
// ensure we only add enough experience to reach the max experience cap so we don't overflow
@ -2822,7 +2824,7 @@ void ApplyPlrDamage(DamageType damageType, Player &player, int dam, int minHP /*
{
int totalDamage = (dam << 6) + frac;
if (&player == MyPlayer && !player.hasNoLife()) {
AddFloatingNumber(damageType, player, totalDamage);
LuaEvent("OnPlayerTakeDamage", &player, totalDamage, static_cast<int>(damageType));
}
if (totalDamage > 0 && player.pManaShield && HasNoneOf(player._pIFlags, ItemSpecialEffect::NoMana)) {
const uint8_t manaShieldLevel = player._pSplLvl[static_cast<int8_t>(SpellID::ManaShield)];

133
Source/qol/floatingnumbers.cpp

@ -28,9 +28,7 @@ struct FloatingNumber {
uint32_t time;
uint32_t lastMerge;
UiFlags style;
DamageType type;
int value;
size_t index;
int id;
bool reverseDirection;
};
@ -47,142 +45,45 @@ void ClearExpiredNumbers()
}
}
GameFontTables GetGameFontSizeByDamage(int value)
GameFontTables GetGameFontSize(UiFlags flags)
{
value >>= 6;
if (value >= 300)
if (HasAnyOf(flags, UiFlags::FontSize30))
return GameFont30;
if (value >= 100)
if (HasAnyOf(flags, UiFlags::FontSize24))
return GameFont24;
return GameFont12;
}
UiFlags GetFontSizeByDamage(int value)
{
value >>= 6;
if (value >= 300)
return UiFlags::FontSize30;
if (value >= 100)
return UiFlags::FontSize24;
return UiFlags::FontSize12;
}
void UpdateFloatingData(FloatingNumber &num)
{
if (num.value > 0 && num.value < 64) {
num.text = fmt::format("{:.2f}", num.value / 64.0);
} else {
num.text = StrCat(num.value >> 6);
}
num.style &= ~(UiFlags::FontSize12 | UiFlags::FontSize24 | UiFlags::FontSize30);
num.style |= GetFontSizeByDamage(num.value);
switch (num.type) {
case DamageType::Physical:
num.style |= UiFlags::ColorGold;
break;
case DamageType::Fire:
num.style |= UiFlags::ColorUiSilver; // UiSilver appears dark red ingame
break;
case DamageType::Lightning:
num.style |= UiFlags::ColorBlue;
break;
case DamageType::Magic:
num.style |= UiFlags::ColorOrange;
break;
case DamageType::Acid:
num.style |= UiFlags::ColorYellow;
break;
}
}
} // namespace
void AddFloatingNumber(Point pos, Displacement offset, DamageType type, int value, size_t index, bool damageToPlayer)
void AddFloatingNumber(Point pos, Displacement offset, std::string text, UiFlags style, int id, bool reverseDirection)
{
// 45 deg angles to avoid jitter caused by px alignment
const Displacement goodAngles[] = {
{ 0, -140 },
{ 100, -100 },
{ -100, -100 },
};
Displacement endOffset;
if (*GetOptions().Gameplay.enableFloatingNumbers == FloatingNumbers::Random) {
endOffset = goodAngles[rand() % 3];
} else if (*GetOptions().Gameplay.enableFloatingNumbers == FloatingNumbers::Vertical) {
endOffset = goodAngles[0];
}
if (damageToPlayer)
endOffset = -endOffset;
if (!reverseDirection)
endOffset = { 0, -140 };
else
endOffset = { 0, 140 };
for (auto &num : FloatingQueue) {
if (num.reverseDirection == damageToPlayer && num.type == type && num.index == index && (SDL_GetTicks() - static_cast<int>(num.lastMerge)) <= 100) {
num.value += value;
if (id != 0 && num.id == id && (SDL_GetTicks() - static_cast<int>(num.lastMerge)) <= 100) {
num.text = text;
num.lastMerge = SDL_GetTicks();
UpdateFloatingData(num);
num.style = style;
num.startPos = pos;
return;
}
}
FloatingNumber num {
pos, offset, endOffset, "",
pos, offset, endOffset, text,
static_cast<uint32_t>(SDL_GetTicks() + 2500),
static_cast<uint32_t>(SDL_GetTicks()),
UiFlags::Outlined, type, value, index, damageToPlayer
style | UiFlags::Outlined, id, reverseDirection
};
UpdateFloatingData(num);
FloatingQueue.push_back(num);
}
} // namespace
void AddFloatingNumber(DamageType damageType, const Monster &monster, int damage)
{
if (*GetOptions().Gameplay.enableFloatingNumbers == FloatingNumbers::Off)
return;
Displacement offset = {};
if (monster.isWalking()) {
offset = GetOffsetForWalking(monster.animInfo, monster.direction);
if (monster.mode == MonsterMode::MoveSideways) {
if (monster.direction == Direction::West)
offset -= Displacement { 64, 0 };
else
offset += Displacement { 64, 0 };
}
}
if (monster.animInfo.sprites) {
const ClxSprite sprite = monster.animInfo.currentSprite();
offset.deltaY -= sprite.height() / 2;
}
AddFloatingNumber(monster.position.tile, offset, damageType, damage, monster.getId(), false);
}
void AddFloatingNumber(DamageType damageType, const Player &player, int damage)
{
if (*GetOptions().Gameplay.enableFloatingNumbers == FloatingNumbers::Off)
return;
Displacement offset = {};
if (player.isWalking()) {
offset = GetOffsetForWalking(player.AnimInfo, player._pdir);
if (player._pmode == PM_WALK_SIDEWAYS) {
if (player._pdir == Direction::West)
offset -= Displacement { 64, 0 };
else
offset += Displacement { 64, 0 };
}
}
AddFloatingNumber(player.position.tile, offset, damageType, damage, player.getId(), true);
}
void DrawFloatingNumbers(const Surface &out, Point viewPosition, Displacement offset)
{
if (*GetOptions().Gameplay.enableFloatingNumbers == FloatingNumbers::Off)
return;
for (auto &floatingNum : FloatingQueue) {
Displacement worldOffset = viewPosition - floatingNum.startPos;
worldOffset = worldOffset.worldToScreen() + offset + Displacement { TILE_WIDTH / 2, -TILE_HEIGHT / 2 } + floatingNum.startOffset;
@ -193,7 +94,7 @@ void DrawFloatingNumbers(const Surface &out, Point viewPosition, Displacement of
Point screenPosition { worldOffset.deltaX, worldOffset.deltaY };
const int lineWidth = GetLineWidth(floatingNum.text, GetGameFontSizeByDamage(floatingNum.value));
const int lineWidth = GetLineWidth(floatingNum.text, GetGameFontSize(floatingNum.style));
screenPosition.x -= lineWidth / 2;
const uint32_t timeLeft = floatingNum.time - SDL_GetTicks();
const float mul = 1 - (timeLeft / 2500.0f);

11
Source/qol/floatingnumbers.h

@ -5,15 +5,16 @@
*/
#pragma once
#include <string>
#include "DiabloUI/ui_flags.hpp"
#include "engine/displacement.hpp"
#include "engine/point.hpp"
#include "monster.h"
#include "player.h"
#include "tables/misdat.h"
#include "engine/surface.hpp"
namespace devilution {
void AddFloatingNumber(DamageType damageType, const Monster &monster, int damage);
void AddFloatingNumber(DamageType damageType, const Player &player, int damage);
void AddFloatingNumber(Point pos, Displacement offset, std::string text, UiFlags style, int id = 0, bool reverseDirection = false);
void DrawFloatingNumbers(const Surface &out, Point viewPosition, Displacement offset);
void ClearFloatingNumbers();

17
assets/lua/devilutionx/events.lua

@ -25,9 +25,10 @@ local function CreateEvent()
---The arguments are forwarded to handlers.
---@param ... any
trigger = function(...)
if arg ~= nil then
local args = {...}
if #args > 0 then
for _, func in ipairs(functions) do
func(table.unpack(arg))
func(table.unpack(args))
end
else
for _, func in ipairs(functions) do
@ -67,6 +68,18 @@ local events = {
---Called every frame at the end.
GameDrawComplete = CreateEvent(),
__doc_GameDrawComplete = "Called every frame at the end.",
---Called when a Monster takes damage.
OnMonsterTakeDamage = CreateEvent(),
__doc_OnMonsterTakeDamage = "Called when a Monster takes damage.",
---Called when Player takes damage.
OnPlayerTakeDamage = CreateEvent(),
__doc_OnPlayerTakeDamage = "Called when Player takes damage.",
---Called when Player gains experience.
OnPlayerGainExperience = CreateEvent(),
__doc_OnPlayerGainExperience = "Called when Player gains experience.",
}
---Registers a custom event type with the given name.

88
assets/lua/mods/Floating Numbers - Damage/init.lua

@ -0,0 +1,88 @@
local floatingnumbers = require("devilutionx.floatingnumbers")
local events = require("devilutionx.events")
local player = require("devilutionx.player")
local DAMAGE_TYPE = {
PHYSICAL = 0,
FIRE = 1,
LIGHTNING = 2,
MAGIC = 3,
ACID = 4,
}
local function get_damage_style(damage_val, damage_type)
local style = 0
local v = damage_val
if v >= 64 * 300 then
style = style | floatingnumbers.Flags.Large
elseif v >= 64 * 100 then
style = style | floatingnumbers.Flags.Medium
else
style = style | floatingnumbers.Flags.Small
end
local damage_type_styles = {
[DAMAGE_TYPE.PHYSICAL] = floatingnumbers.Flags.Gold,
[DAMAGE_TYPE.FIRE] = floatingnumbers.Flags.DarkRed,
[DAMAGE_TYPE.LIGHTNING] = floatingnumbers.Flags.Blue,
[DAMAGE_TYPE.MAGIC] = floatingnumbers.Flags.Orange,
[DAMAGE_TYPE.ACID] = floatingnumbers.Flags.Yellow,
}
local type_style = damage_type_styles[damage_type]
if type_style then
style = style | type_style
end
return style
end
local function format_damage(damage_val)
if damage_val > 0 and damage_val < 64 then
return string.format("%.2f", damage_val / 64.0)
else
return tostring(math.floor(damage_val / 64))
end
end
local accumulated_damage = {}
local MERGE_WINDOW_MS = 100
events.OnMonsterTakeDamage.add(function(monster, damage, damage_type)
local id = monster.id
local now = floatingnumbers.get_ticks()
local entry = accumulated_damage[id]
if entry and (now - entry.time) < MERGE_WINDOW_MS then
entry.damage = entry.damage + damage
else
entry = { damage = damage, time = now }
accumulated_damage[id] = entry
end
entry.time = now
local text = format_damage(entry.damage)
local style = get_damage_style(entry.damage, damage_type)
floatingnumbers.add(text, monster.position, style, id, false)
end)
events.OnPlayerTakeDamage.add(function(_player, damage, damage_type)
if _player == player.self() then
local id = _player.id
local now = floatingnumbers.get_ticks()
local entry = accumulated_damage[id]
if entry and (now - entry.time) < MERGE_WINDOW_MS then
entry.damage = entry.damage + damage
else
entry = { damage = damage, time = now }
accumulated_damage[id] = entry
end
entry.time = now
local text = format_damage(entry.damage)
local style = get_damage_style(entry.damage, damage_type)
floatingnumbers.add(text, _player.position, style, id, true)
end
end)

25
assets/lua/mods/Floating Numbers - XP/init.lua

@ -0,0 +1,25 @@
local floatingnumbers = require("devilutionx.floatingnumbers")
local events = require("devilutionx.events")
local player = require("devilutionx.player")
local accumulated_xp = {}
local MERGE_WINDOW_MS = 100
events.OnPlayerGainExperience.add(function(_player, experience)
if _player == player.self() then
local id = _player.id
local now = floatingnumbers.get_ticks()
local entry = accumulated_xp[id]
if entry and (now - entry.time) < MERGE_WINDOW_MS then
entry.experience = entry.experience + experience
else
entry = { experience = experience, time = now }
accumulated_xp[id] = entry
end
entry.time = now
local text = tostring(entry.experience) .. " XP"
floatingnumbers.add(text, _player.position, floatingnumbers.Flags.White, id, true)
end
end)
Loading…
Cancel
Save