Browse Source

Adria refill mana as mod

pull/8419/head
Yuri Pourre 2 months ago committed by GitHub
parent
commit
b684fd143c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CMake/Assets.cmake
  2. 7
      Source/engine/demomode.cpp
  3. 14
      Source/lua/lua_event.hpp
  4. 18
      Source/lua/lua_global.cpp
  5. 1
      Source/lua/lua_global.hpp
  6. 21
      Source/lua/modules/audio.cpp
  7. 8
      Source/lua/modules/player.cpp
  8. 4
      Source/options.cpp
  9. 2
      Source/options.h
  10. 48
      Source/stores.cpp
  11. 4
      assets/lua/devilutionx/events.lua
  12. 23
      assets/lua/mods/adria_refills_mana/init.lua

1
CMake/Assets.cmake

@ -158,6 +158,7 @@ set(devilutionx_assets
lua_internal/get_lua_function_signature.lua
lua/devilutionx/events.lua
lua/inspect.lua
lua/mods/adria_refills_mana/init.lua
lua/mods/clock/init.lua
"lua/mods/Floating Numbers - Damage/init.lua"
"lua/mods/Floating Numbers - XP/init.lua"

7
Source/engine/demomode.cpp

@ -127,7 +127,6 @@ struct {
bool autoElixirPickup = false;
bool autoOilPickup = false;
bool autoPickupInTown = false;
bool adriaRefillsMana = false;
bool autoEquipWeapons = false;
bool autoEquipArmor = false;
bool autoEquipHelms = false;
@ -166,7 +165,7 @@ void ReadSettings(FILE *in, uint8_t version) // NOLINT(readability-identifier-le
DemoSettings.autoElixirPickup = ReadByte(in) != 0;
DemoSettings.autoOilPickup = ReadByte(in) != 0;
DemoSettings.autoPickupInTown = ReadByte(in) != 0;
DemoSettings.adriaRefillsMana = ReadByte(in) != 0;
(void)ReadByte(in); // adriaRefillsMana (removed feature, kept for backward compatibility)
DemoSettings.autoEquipWeapons = ReadByte(in) != 0;
DemoSettings.autoEquipArmor = ReadByte(in) != 0;
DemoSettings.autoEquipHelms = ReadByte(in) != 0;
@ -195,7 +194,6 @@ void ReadSettings(FILE *in, uint8_t version) // NOLINT(readability-identifier-le
{ _("Auto Elixir Pickup"), DemoSettings.autoGoldPickup },
{ _("Auto Oil Pickup"), DemoSettings.autoOilPickup },
{ _("Auto Pickup in Town"), DemoSettings.autoPickupInTown },
{ _("Adria Refills Mana"), DemoSettings.adriaRefillsMana },
{ _("Auto Equip Weapons"), DemoSettings.autoEquipWeapons },
{ _("Auto Equip Armor"), DemoSettings.autoEquipArmor },
{ _("Auto Equip Helms"), DemoSettings.autoEquipHelms },
@ -231,7 +229,7 @@ void WriteSettings(FILE *out)
WriteByte(out, static_cast<uint8_t>(*options.Gameplay.autoElixirPickup));
WriteByte(out, static_cast<uint8_t>(*options.Gameplay.autoOilPickup));
WriteByte(out, static_cast<uint8_t>(*options.Gameplay.autoPickupInTown));
WriteByte(out, static_cast<uint8_t>(*options.Gameplay.adriaRefillsMana));
WriteByte(out, 0); // adriaRefillsMana (removed feature, kept for backward compatibility)
WriteByte(out, static_cast<uint8_t>(*options.Gameplay.autoEquipWeapons));
WriteByte(out, static_cast<uint8_t>(*options.Gameplay.autoEquipArmor));
WriteByte(out, static_cast<uint8_t>(*options.Gameplay.autoEquipHelms));
@ -650,7 +648,6 @@ void OverrideOptions()
options.Gameplay.autoElixirPickup.SetValue(DemoSettings.autoElixirPickup);
options.Gameplay.autoOilPickup.SetValue(DemoSettings.autoOilPickup);
options.Gameplay.autoPickupInTown.SetValue(DemoSettings.autoPickupInTown);
options.Gameplay.adriaRefillsMana.SetValue(DemoSettings.adriaRefillsMana);
options.Gameplay.autoEquipWeapons.SetValue(DemoSettings.autoEquipWeapons);
options.Gameplay.autoEquipArmor.SetValue(DemoSettings.autoEquipArmor);
options.Gameplay.autoEquipHelms.SetValue(DemoSettings.autoEquipHelms);

14
Source/lua/lua_event.hpp

@ -0,0 +1,14 @@
#pragma once
#include <string_view>
namespace devilution {
/**
* @brief Triggers a Lua event by name.
* This is a minimal header for code that only needs to trigger events.
*/
void LuaEvent(std::string_view name);
void LuaEvent(std::string_view name, std::string_view arg);
} // namespace devilution

18
Source/lua/lua_global.cpp

@ -205,6 +205,9 @@ sol::environment CreateLuaSandbox()
sandbox["require"] = lua["requireGen"](sandbox, CurrentLuaState->commonPackages, LuaLoadScriptFromAssets);
// Expose commonly used enums globally for mods
sandbox["SfxID"] = lua["SfxID"];
return sandbox;
}
@ -347,6 +350,21 @@ void LuaEvent(std::string_view name, const Player *player, uint32_t arg1)
CallLuaEvent(name, player, arg1);
}
void LuaEvent(std::string_view name, std::string_view arg)
{
if (!CurrentLuaState.has_value()) {
return;
}
const auto trigger = CurrentLuaState->events.traverse_get<std::optional<sol::object>>(name, "trigger");
if (!trigger.has_value() || !trigger->is<sol::protected_function>()) {
LogError("events.{}.trigger is not a function", name);
return;
}
const sol::protected_function fn = trigger->as<sol::protected_function>();
SafeCallResult(fn(arg), /*optional=*/true);
}
sol::state &GetLuaState()
{
return CurrentLuaState->sol;

1
Source/lua/lua_global.hpp

@ -15,6 +15,7 @@ void LuaInitialize();
void LuaReloadActiveMods();
void LuaShutdown();
void LuaEvent(std::string_view name);
void LuaEvent(std::string_view name, std::string_view arg);
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);

21
Source/lua/modules/audio.cpp

@ -1,9 +1,11 @@
#include "lua/modules/audio.hpp"
#include <magic_enum/magic_enum.hpp>
#include <sol/sol.hpp>
#include "effects.h"
#include "lua/metadoc.hpp"
#include "sound_effect_enums.h"
namespace devilution {
@ -14,10 +16,27 @@ bool IsValidSfx(int16_t psfx)
return psfx >= 0 && psfx <= static_cast<int16_t>(SfxID::LAST);
}
void RegisterSfxIDEnum(sol::state_view &lua)
{
constexpr auto enumValues = magic_enum::enum_values<SfxID>();
sol::table enumTable = lua.create_table();
for (const auto enumValue : enumValues) {
const std::string_view name = magic_enum::enum_name(enumValue);
if (!name.empty() && name != "LAST" && name != "None") {
enumTable[name] = static_cast<int16_t>(enumValue);
}
}
// Add LAST and None explicitly
enumTable["LAST"] = static_cast<int16_t>(SfxID::LAST);
enumTable["None"] = static_cast<int16_t>(SfxID::None);
lua["SfxID"] = enumTable;
}
} // namespace
sol::table LuaAudioModule(sol::state_view &lua)
{
RegisterSfxIDEnum(lua);
sol::table table = lua.create_table();
LuaSetDocFn(table,
"playSfx", "(id: number)",
@ -25,6 +44,8 @@ sol::table LuaAudioModule(sol::state_view &lua)
LuaSetDocFn(table,
"playSfxLoc", "(id: number, x: number, y: number)",
[](int16_t psfx, int x, int y) { if (IsValidSfx(psfx)) PlaySfxLoc(static_cast<SfxID>(psfx), { x, y }); });
// Expose SfxID enum through the module table
table["SfxID"] = lua["SfxID"];
return table;
}

8
Source/lua/modules/player.cpp

@ -4,6 +4,8 @@
#include <sol/sol.hpp>
#include "effects.h"
#include "engine/backbuffer_state.hpp"
#include "engine/point.hpp"
#include "engine/random.hpp"
#include "inv.h"
@ -103,6 +105,12 @@ void InitPlayerUserType(sol::state_view &lua)
player._pMana = player._pMaxMana;
player._pManaBase = player._pMaxManaBase;
});
LuaSetDocReadonlyProperty(playerType, "mana", "number",
"Current mana (readonly)",
[](Player &player) { return player._pMana >> 6; });
LuaSetDocReadonlyProperty(playerType, "maxMana", "number",
"Maximum mana (readonly)",
[](Player &player) { return player._pMaxMana >> 6; });
}
} // namespace

4
Source/options.cpp

@ -72,7 +72,7 @@ namespace {
void DiscoverMods()
{
// Add mods available by default:
std::unordered_set<std::string> modNames = { "clock", "Floating Numbers - Damage", "Floating Numbers - XP" };
std::unordered_set<std::string> modNames = { "clock", "adria_refills_mana", "Floating Numbers - Damage", "Floating Numbers - XP" };
if (HaveHellfire()) {
modNames.insert("Hellfire");
@ -852,7 +852,6 @@ GameplayOptions::GameplayOptions()
, autoElixirPickup("Auto Elixir Pickup", OptionEntryFlags::None, N_("Auto Elixir Pickup"), N_("Elixirs are automatically collected when in close proximity to the player."), false)
, autoOilPickup("Auto Oil Pickup", OptionEntryFlags::OnlyHellfire, N_("Auto Oil Pickup"), N_("Oils are automatically collected when in close proximity to the player."), false)
, autoPickupInTown("Auto Pickup in Town", OptionEntryFlags::None, N_("Auto Pickup in Town"), N_("Automatically pickup items in town."), false)
, adriaRefillsMana("Adria Refills Mana", OptionEntryFlags::None, N_("Adria Refills Mana"), N_("Adria will refill your mana when you visit her shop."), false)
, autoEquipWeapons("Auto Equip Weapons", OptionEntryFlags::None, N_("Auto Equip Weapons"), N_("Weapons will be automatically equipped on pickup or purchase if enabled."), true)
, autoEquipArmor("Auto Equip Armor", OptionEntryFlags::None, N_("Auto Equip Armor"), N_("Armor will be automatically equipped on pickup or purchase if enabled."), false)
, autoEquipHelms("Auto Equip Helms", OptionEntryFlags::None, N_("Auto Equip Helms"), N_("Helms will be automatically equipped on pickup or purchase if enabled."), false)
@ -913,7 +912,6 @@ std::vector<OptionEntryBase *> GameplayOptions::GetEntries()
&numFullRejuPotionPickup,
&autoPickupInTown,
&disableCripplingShrines,
&adriaRefillsMana,
&grabInput,
&pauseOnFocusLoss,
&skipLoadingScreenThresholdMs,

2
Source/options.h

@ -599,8 +599,6 @@ struct GameplayOptions : OptionCategoryBase {
OptionEntryBoolean autoOilPickup;
/** @brief Enable or Disable auto-pickup in town */
OptionEntryBoolean autoPickupInTown;
/** @brief Recover mana when talking to Adria. */
OptionEntryBoolean adriaRefillsMana;
/** @brief Automatically attempt to equip weapon-type items when picking them up. */
OptionEntryBoolean autoEquipWeapons;
/** @brief Automatically attempt to equip armor-type items when picking them up. */

48
Source/stores.cpp

@ -21,6 +21,7 @@
#include "engine/render/text_render.hpp"
#include "engine/trn.hpp"
#include "game_mode.hpp"
#include "lua/lua_event.hpp"
#include "minitext.h"
#include "multi.h"
#include "options.h"
@ -653,24 +654,8 @@ void StartSmithRepair()
AddItemListBackButton();
}
void FillManaPlayer()
{
if (!*GetOptions().Gameplay.adriaRefillsMana)
return;
Player &myPlayer = *MyPlayer;
if (myPlayer._pMana != myPlayer._pMaxMana) {
PlaySFX(SfxID::CastHealing);
}
myPlayer._pMana = myPlayer._pMaxMana;
myPlayer._pManaBase = myPlayer._pMaxManaBase;
RedrawComponent(PanelDrawComponent::Mana);
}
void StartWitch()
{
FillManaPlayer();
IsTextFullSize = false;
HasScrollbar = false;
AddSText(0, 2, _("Witch's shack"), UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
@ -2215,6 +2200,37 @@ void StartStore(TalkID s)
CloseGoldDrop();
ClearSText(0, NumStoreLines);
ReleaseStoreBtn();
// Fire StoreOpened Lua event for main store entries
switch (s) {
case TalkID::Smith:
LuaEvent("StoreOpened", "griswold");
break;
case TalkID::Witch:
LuaEvent("StoreOpened", "adria");
break;
case TalkID::Boy:
LuaEvent("StoreOpened", "wirt");
break;
case TalkID::Healer:
LuaEvent("StoreOpened", "pepin");
break;
case TalkID::Storyteller:
LuaEvent("StoreOpened", "cain");
break;
case TalkID::Tavern:
LuaEvent("StoreOpened", "ogden");
break;
case TalkID::Drunk:
LuaEvent("StoreOpened", "farnham");
break;
case TalkID::Barmaid:
LuaEvent("StoreOpened", "gillian");
break;
default:
break;
}
switch (s) {
case TalkID::Smith:
StartSmith();

4
assets/lua/devilutionx/events.lua

@ -69,6 +69,10 @@ local events = {
GameDrawComplete = CreateEvent(),
__doc_GameDrawComplete = "Called every frame at the end.",
---Called when opening a towner store. Passes the towner name as argument (e.g., "griswold", "adria", "pepin", "wirt", "cain").
StoreOpened = CreateEvent(),
__doc_StoreOpened = "Called when opening a towner store. Passes the towner name as argument.",
---Called when a Monster takes damage.
OnMonsterTakeDamage = CreateEvent(),
__doc_OnMonsterTakeDamage = "Called when a Monster takes damage.",

23
assets/lua/mods/adria_refills_mana/init.lua

@ -0,0 +1,23 @@
-- Adria Refills Mana Mod
-- When you visit Adria's shop, your mana is restored to full.
local events = require("devilutionx.events")
local player = require("devilutionx.player")
local audio = require("devilutionx.audio")
events.StoreOpened.add(function(townerName)
if townerName ~= "adria" then
return
end
local p = player.self()
if p == nil then
return
end
-- Restore mana if player has mana capacity and it's not already full
if p.maxMana > 0 and p.mana < p.maxMana then
audio.playSfx(audio.SfxID.CastHealing)
p:restoreFullMana()
end
end)
Loading…
Cancel
Save