mirror of
https://github.com/brues-code/nampower.git
synced 2026-10-17 10:40:50 +00:00
add ability to save autocasts for greater demons
add CastSpellNoQueue default to current target in SPELL_CAST_EVENT
This commit is contained in:
@@ -45,6 +45,8 @@ set(SOURCE_FILES
|
||||
autoattack.cpp
|
||||
player_scripts.hpp
|
||||
player_scripts.cpp
|
||||
pet.hpp
|
||||
pet.cpp
|
||||
unit.hpp
|
||||
unit.cpp
|
||||
)
|
||||
|
||||
@@ -34,6 +34,21 @@
|
||||
|
||||
namespace game {
|
||||
#pragma pack(push, 1)
|
||||
struct CVar {
|
||||
uint8_t unk0[0x1C];
|
||||
uint32_t m_flags;
|
||||
char *m_stringValue;
|
||||
float m_floatValue;
|
||||
int32_t m_intValue;
|
||||
int32_t m_modified;
|
||||
char *m_defaultValue;
|
||||
char *m_resetValue;
|
||||
char *m_latchedValue;
|
||||
uint8_t unk3C[0x80];
|
||||
void *m_callback;
|
||||
void *m_arg;
|
||||
};
|
||||
|
||||
enum ItemClass {
|
||||
ITEM_CLASS_CONSUMABLE = 0,
|
||||
ITEM_CLASS_CONTAINER = 1,
|
||||
@@ -1144,6 +1159,19 @@ namespace game {
|
||||
TARGET_FLAG_UNK2 = 0x00010000, // pguid, not used in any spells (can be set dynamically)
|
||||
};
|
||||
|
||||
constexpr uint32_t UnitActionButtonAction(uint32_t x) { return x & 0x00FFFFFFu; }
|
||||
constexpr uint32_t UnitActionButtonType(uint32_t x) { return (x & 0xFF000000u) >> 24; }
|
||||
|
||||
enum ActiveStates : uint8_t
|
||||
{
|
||||
ACT_DECIDE = 0x00, // custom
|
||||
ACT_PASSIVE = 0x01, // 0x01 - passive
|
||||
ACT_REACTION = 0x06, // 0x02 | 0x04
|
||||
ACT_COMMAND = 0x07, // 0x01 | 0x02 | 0x04
|
||||
ACT_DISABLED = 0x81, // 0x80 - castable
|
||||
ACT_ENABLED = 0xC1, // 0x40 | 0x80 - auto cast + castable
|
||||
};
|
||||
|
||||
enum Events : std::uint32_t {
|
||||
UNIT_PET = 0u,
|
||||
UNIT_PET_02 = 2u,
|
||||
|
||||
+61
-5
@@ -46,6 +46,7 @@
|
||||
#include "lua_refs.hpp"
|
||||
#include "autoattack.hpp"
|
||||
#include "unit.hpp"
|
||||
#include "pet.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -162,6 +163,9 @@ namespace Nampower {
|
||||
std::unique_ptr<hadesmem::PatchDetour<GetGUIDFromNameT> > gGetGUIDFromNameDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT> > gCastSpellByNameDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<SendUnitSignalT> > gSendUnitSignalDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<CGPetInfo_SetPetT> > gCGPetInfo_SetPetDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<TogglePetSlotAutocastT> > gTogglePetSlotAutocastDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<CGPetInfo_GetPetSpellActionT> > gCGPetInfo_GetPetSpellActionDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<CGGameUI_ShowCombatFeedbackT> > gCGGameUI_ShowCombatFeedbackDetour;
|
||||
|
||||
// Flags for one-time initialization
|
||||
@@ -687,6 +691,9 @@ namespace Nampower {
|
||||
} else if (strcmp(cvar, "NP_SpamProtectionEnabled") == 0) {
|
||||
gUserSettings.spamProtectionEnabled = atoi(value) != 0;
|
||||
DEBUG_LOG("Set NP_SpamProtectionEnabled to " << gUserSettings.spamProtectionEnabled);
|
||||
} else if (strcmp(cvar, "NP_PreserveGreaterDemonAutocast") == 0) {
|
||||
gUserSettings.preserveGreaterDemonAutocast = atoi(value) != 0;
|
||||
DEBUG_LOG("Set NP_PreserveGreaterDemonAutocast to " << gUserSettings.preserveGreaterDemonAutocast);
|
||||
} else if (strcmp(cvar, "NP_EnableUnitEventsPet") == 0) {
|
||||
gUserSettings.enableUnitEventsPet = atoi(value) != 0;
|
||||
DEBUG_LOG("Set NP_EnableUnitEventsPet to " << gUserSettings.enableUnitEventsPet);
|
||||
@@ -780,19 +787,28 @@ namespace Nampower {
|
||||
return cvarSetOrig(luaState);
|
||||
}
|
||||
|
||||
int *getCvar(const char *cvar) {
|
||||
int *GetIntCVar(const char *cvar) {
|
||||
auto const cvarLookup = hadesmem::detail::AliasCast<CVarLookupT>(Offsets::CVarLookup);
|
||||
uintptr_t *cvarPtr = cvarLookup(cvar);
|
||||
auto *cvarPtr = cvarLookup(cvar);
|
||||
|
||||
if (cvarPtr) {
|
||||
return reinterpret_cast<int *>(cvarPtr +
|
||||
10); // get intValue from CVar which is consistent, strValue more complicated
|
||||
return &cvarPtr->m_intValue;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *GetStringCvar(const char *cvar) {
|
||||
auto const cvarLookup = hadesmem::detail::AliasCast<CVarLookupT>(Offsets::CVarLookup);
|
||||
auto *cvarPtr = cvarLookup(cvar);
|
||||
if (!cvarPtr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return cvarPtr->m_stringValue;
|
||||
}
|
||||
|
||||
void loadUserVar(const char *cvar) {
|
||||
int *value = getCvar(cvar);
|
||||
int *value = GetIntCVar(cvar);
|
||||
if (value) {
|
||||
updateFromCvar(cvar, std::to_string(*value).c_str());
|
||||
} else {
|
||||
@@ -1216,6 +1232,36 @@ namespace Nampower {
|
||||
0, // unk2
|
||||
0); // unk3
|
||||
|
||||
char NP_PreserveGreaterDemonAutocast[] = "NP_PreserveGreaterDemonAutocast";
|
||||
CVarRegister(NP_PreserveGreaterDemonAutocast, // name
|
||||
nullptr, // help
|
||||
0, // unk1
|
||||
gUserSettings.preserveGreaterDemonAutocast ? defaultTrue : defaultFalse, // default value address
|
||||
nullptr, // callback
|
||||
1, // category
|
||||
0, // unk2
|
||||
0); // unk3
|
||||
|
||||
char NP_FelguardAutocastData[] = "NP_FelguardAutocastData";
|
||||
CVarRegister(NP_FelguardAutocastData, // name
|
||||
nullptr, // help
|
||||
0, // unk1
|
||||
"", // default value address
|
||||
nullptr, // callback
|
||||
1, // category
|
||||
0, // unk2
|
||||
0); // unk3
|
||||
|
||||
char NP_DoomguardAutocastData[] = "NP_DoomguardAutocastData";
|
||||
CVarRegister(NP_DoomguardAutocastData, // name
|
||||
nullptr, // help
|
||||
0, // unk1
|
||||
"", // default value address
|
||||
nullptr, // callback
|
||||
1, // category
|
||||
0, // unk2
|
||||
0); // unk3
|
||||
|
||||
char NP_ChannelLatencyReductionPercentage[] = "NP_ChannelLatencyReductionPercentage";
|
||||
CVarRegister(NP_ChannelLatencyReductionPercentage, // name
|
||||
nullptr, // help
|
||||
@@ -1259,6 +1305,7 @@ namespace Nampower {
|
||||
loadUserVar("NP_DoubleCastToEndChannelEarly");
|
||||
|
||||
loadUserVar("NP_SpamProtectionEnabled");
|
||||
loadUserVar("NP_PreserveGreaterDemonAutocast");
|
||||
|
||||
loadUserVar("NP_EnableUnitEventsPet");
|
||||
loadUserVar("NP_EnableUnitEventsParty");
|
||||
@@ -1469,6 +1516,12 @@ namespace Nampower {
|
||||
process, Offsets::CSimpleTop_OnKeyUp, &CSimpleTop_OnKeyUpHook);
|
||||
gSendUnitSignalDetour = createHook<SendUnitSignalT>(process, Offsets::SendUnitSignal,
|
||||
&SendUnitSignalHook);
|
||||
gCGPetInfo_SetPetDetour = createHook<CGPetInfo_SetPetT>(process, Offsets::CGPetInfo_SetPet,
|
||||
&CGPetInfo_SetPetHook);
|
||||
gTogglePetSlotAutocastDetour = createHook<TogglePetSlotAutocastT>(
|
||||
process, Offsets::TogglePetSlotAutocast, &TogglePetSlotAutocastHook);
|
||||
gCGPetInfo_GetPetSpellActionDetour = createHook<CGPetInfo_GetPetSpellActionT>(
|
||||
process, Offsets::CGPetInfo_GetPetSpellAction, &CGPetInfo_GetPetSpellActionHook);
|
||||
gCGGameUI_ShowCombatFeedbackDetour = createHook<CGGameUI_ShowCombatFeedbackT>(
|
||||
process, Offsets::CGGameUI_ShowCombatFeedback, &CGGameUI_ShowCombatFeedbackHook);
|
||||
gLoadScriptFunctionsDetour = createHook<LoadScriptFunctionsT>(process, Offsets::LoadScriptFunctions,
|
||||
@@ -1697,6 +1750,9 @@ namespace Nampower {
|
||||
char castSpellByNameNoQueue[] = "CastSpellByNameNoQueue";
|
||||
RegisterLuaFunction(castSpellByNameNoQueue, reinterpret_cast<uintptr_t *>(Script_CastSpellByNameNoQueue));
|
||||
|
||||
char castSpellNoQueue[] = "CastSpellNoQueue";
|
||||
RegisterLuaFunction(castSpellNoQueue, reinterpret_cast<uintptr_t *>(Script_CastSpellNoQueue));
|
||||
|
||||
char queueScript[] = "QueueScript";
|
||||
RegisterLuaFunction(queueScript, reinterpret_cast<uintptr_t *>(Script_QueueScript));
|
||||
|
||||
|
||||
+7
-3
@@ -7,6 +7,7 @@
|
||||
#include <hadesmem/patcher.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "game.hpp"
|
||||
#include "types.h"
|
||||
@@ -25,7 +26,7 @@ namespace Nampower {
|
||||
constexpr uint32_t DISENCHANT_QUALITY_PURPLE = 0x04; // Epic
|
||||
|
||||
constexpr uint32_t MAJOR_VERSION = 3;
|
||||
constexpr uint32_t MINOR_VERSION = 0;
|
||||
constexpr uint32_t MINOR_VERSION = 1;
|
||||
constexpr uint32_t PATCH_VERSION = 0;
|
||||
|
||||
constexpr int32_t LUA_REGISTRYINDEX = -10000;
|
||||
@@ -112,7 +113,7 @@ namespace Nampower {
|
||||
using CGCharacterInfo_UseItemT = void (__fastcall *)(uintptr_t *this_ptr, void *dummy_edx, uint32_t itemSlot, uint64_t *targetGuid);
|
||||
|
||||
using GetSpellSlotAndTypeT = int32_t (__fastcall *)(const char *, uint32_t *);
|
||||
using GetSpellSlotFromLuaT = uint32_t (__fastcall *)(int param_1, uint32_t *slot, uint32_t *type);
|
||||
using GetSpellSlotFromLuaT = uint32_t (__fastcall *)(uintptr_t *luaState, uint32_t *slot, uint32_t *type);
|
||||
using GetTimeMsT = uint64_t (__stdcall *)();
|
||||
|
||||
using GetClientConnectionT = uintptr_t *(__stdcall *)();
|
||||
@@ -178,7 +179,7 @@ namespace Nampower {
|
||||
|
||||
using TargetUnitT = void (__fastcall *)(uint64_t *guid);
|
||||
|
||||
using CVarLookupT = uintptr_t *(__fastcall *)(const char *);
|
||||
using CVarLookupT = game::CVar *(__fastcall *)(const char *);
|
||||
using SetCVarT = int (__fastcall *)(uintptr_t *luaPtr);
|
||||
using CVarRegisterT = int *(__fastcall *)(char *name, char *help, int unk1, const char *defaultValuePtr,
|
||||
void *callbackPtr,
|
||||
@@ -226,5 +227,8 @@ namespace Nampower {
|
||||
|
||||
void SetAttackTarget(uint64_t target);
|
||||
|
||||
const char *GetStringCvar(const char *cvar);
|
||||
|
||||
void loadUserVar(const char *cvar);
|
||||
|
||||
}
|
||||
|
||||
@@ -158,6 +158,7 @@ enum class Offsets : std::uint32_t {
|
||||
|
||||
// Existing script functions
|
||||
GetGUIDFromName = 0X00515970,
|
||||
Script_CastSpell = 0x004B42F0,
|
||||
Script_CastSpellByName = 0x004B4AB0,
|
||||
Script_SpellTargetUnit = 0x006E6D90,
|
||||
Script_SetCVar = 0x00488C10,
|
||||
@@ -262,4 +263,10 @@ enum class Offsets : std::uint32_t {
|
||||
XMLNode_GetAttributeByName = 0x006f2cf0,
|
||||
|
||||
RaidTargetMarkGuidBase = 0xB71368,
|
||||
|
||||
CGPetInfo_SetPet = 0x004bc7e0,
|
||||
TogglePetSlotAutocast = 0x004bcbb0,
|
||||
CGPetInfo_GetPetSpellAction = 0x004bd190,
|
||||
PetActionBarSlots = 0x00b71438,
|
||||
ActivePetGuid = 0x00b714a0,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
//
|
||||
// Created by pmacc on 3/1/2026.
|
||||
//
|
||||
|
||||
#include "pet.hpp"
|
||||
|
||||
#include "game.hpp"
|
||||
#include "helper.hpp"
|
||||
#include "logging.hpp"
|
||||
#include "offsets.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace Nampower {
|
||||
constexpr uint32_t kAutocastEnabledType = 0xC1;
|
||||
constexpr uint32_t kAutocastDisabledType = 0x81;
|
||||
constexpr uint32_t kFelguardDisplayId = 7970;
|
||||
constexpr uint32_t kDoomguardDisplayId = 1912;
|
||||
uint32_t gPendingToggleAutocastSlot = 0;
|
||||
|
||||
uint32_t GetActivePetDisplayId() {
|
||||
auto const activePetGuid = *reinterpret_cast<uint64_t *>(Offsets::ActivePetGuid);
|
||||
if (activePetGuid == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto *petUnit = game::GetObjectPtr(activePetGuid);
|
||||
if (!petUnit) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto *unitFields = *reinterpret_cast<game::UnitFields **>(petUnit + 68);
|
||||
if (!unitFields) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return unitFields->displayId;
|
||||
}
|
||||
|
||||
const char *GetAutocastCvarNameForDisplayId(uint32_t displayId) {
|
||||
if (displayId == kFelguardDisplayId) {
|
||||
return "NP_FelguardAutocastData";
|
||||
}
|
||||
if (displayId == kDoomguardDisplayId) {
|
||||
return "NP_DoomguardAutocastData";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string SerializeAutocastData(const std::map<uint32_t, uint32_t> &autocastData) {
|
||||
std::ostringstream out;
|
||||
bool first = true;
|
||||
for (auto const &entry: autocastData) {
|
||||
if (!first) {
|
||||
out << ",";
|
||||
}
|
||||
out << entry.first << ":" << entry.second;
|
||||
first = false;
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
|
||||
void ParseAutocastData(const std::string &serialized, std::map<uint32_t, uint32_t> &autocastData) {
|
||||
autocastData.clear();
|
||||
if (serialized.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t start = 0;
|
||||
while (start < serialized.size()) {
|
||||
size_t end = serialized.find(',', start);
|
||||
if (end == std::string::npos) {
|
||||
end = serialized.size();
|
||||
}
|
||||
|
||||
std::string token = serialized.substr(start, end - start);
|
||||
size_t colonPos = token.find(':');
|
||||
if (colonPos != std::string::npos && colonPos > 0 && colonPos + 1 < token.size()) {
|
||||
std::string slotStr = token.substr(0, colonPos);
|
||||
std::string actionStr = token.substr(colonPos + 1);
|
||||
|
||||
char *slotEnd = nullptr;
|
||||
char *actionEnd = nullptr;
|
||||
unsigned long slotValue = std::strtoul(slotStr.c_str(), &slotEnd, 10);
|
||||
unsigned long actionValue = std::strtoul(actionStr.c_str(), &actionEnd, 10);
|
||||
|
||||
if (slotEnd && *slotEnd == '\0' &&
|
||||
actionEnd && *actionEnd == '\0' &&
|
||||
slotValue <= 0xFFFFFFFFul &&
|
||||
actionValue <= 0xFFFFFFFFul) {
|
||||
autocastData[static_cast<uint32_t>(slotValue)] = static_cast<uint32_t>(actionValue);
|
||||
}
|
||||
}
|
||||
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void SaveGreaterDemonAutocastPreferences() {
|
||||
SaveGreaterDemonAutocastPreferences(0, 0);
|
||||
}
|
||||
|
||||
void SaveGreaterDemonAutocastPreferences(uint32_t spellSlot, uint32_t actionData) {
|
||||
uint32_t displayId = GetActivePetDisplayId();
|
||||
if (displayId != kFelguardDisplayId && displayId != kDoomguardDisplayId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char *cvarName = GetAutocastCvarNameForDisplayId(displayId);
|
||||
if (!cvarName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char *cvarDataPtr = GetStringCvar(cvarName);
|
||||
std::string cvarData = cvarDataPtr ? cvarDataPtr : "";
|
||||
|
||||
std::map<uint32_t, uint32_t> autocastData;
|
||||
ParseAutocastData(cvarData, autocastData);
|
||||
|
||||
uint32_t actionId = game::UnitActionButtonAction(actionData);
|
||||
uint32_t type = game::UnitActionButtonType(actionData);
|
||||
|
||||
if (spellSlot >= 3 && spellSlot <= 7) {
|
||||
if (type == kAutocastEnabledType) {
|
||||
autocastData[spellSlot] = actionData;
|
||||
} else if (type == kAutocastDisabledType) {
|
||||
autocastData.erase(spellSlot);
|
||||
}
|
||||
}
|
||||
|
||||
std::string serialized = SerializeAutocastData(autocastData);
|
||||
std::string lua = "SetCVar(\"";
|
||||
lua += cvarName;
|
||||
lua += "\",\"";
|
||||
lua += serialized;
|
||||
lua += "\")";
|
||||
LuaCall(lua.c_str());
|
||||
|
||||
DEBUG_LOG("Saved " << cvarName << "=" << serialized
|
||||
<< " (slot=" << spellSlot << ", actionData=" << actionData
|
||||
<< ", actionId=" << actionId << ", type=0x" << std::hex << type << std::dec << ")");
|
||||
}
|
||||
|
||||
void RestoreGreaterDemonAutocastPreferences() {
|
||||
uint32_t displayId = GetActivePetDisplayId();
|
||||
const char *cvarName = GetAutocastCvarNameForDisplayId(displayId);
|
||||
|
||||
if (!cvarName) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Restoring greater demon autocast for displayId=" << displayId << " using cvar " << cvarName);
|
||||
|
||||
const char *cvarDataPtr = GetStringCvar(cvarName);
|
||||
std::string cvarData = cvarDataPtr ? cvarDataPtr : "";
|
||||
|
||||
if (cvarData.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::map<uint32_t, uint32_t> autocastData;
|
||||
ParseAutocastData(cvarData, autocastData);
|
||||
if (autocastData.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto const petGuid = *reinterpret_cast<uint64_t *>(Offsets::ActivePetGuid);
|
||||
if (petGuid == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto const clientServicesSend = reinterpret_cast<ClientServices_SendT>(Offsets::ClientServices_Send);
|
||||
if (!clientServicesSend) {
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr uint32_t CMSG_PET_SET_ACTION = 372;
|
||||
uint32_t packetsSent = 0;
|
||||
|
||||
for (auto const &entry: autocastData) {
|
||||
uint32_t spellSlot = entry.first;
|
||||
uint32_t actionData = entry.second;
|
||||
|
||||
if (spellSlot < 3 || spellSlot > 7) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto *petActionBarSlots = reinterpret_cast<uint32_t *>(Offsets::PetActionBarSlots);
|
||||
if (petActionBarSlots) {
|
||||
petActionBarSlots[spellSlot] = actionData;
|
||||
}
|
||||
|
||||
CDataStore dataStore;
|
||||
dataStore.Put(CMSG_PET_SET_ACTION);
|
||||
dataStore.Put(petGuid);
|
||||
dataStore.Put(spellSlot);
|
||||
dataStore.Put(actionData);
|
||||
dataStore.Finalize();
|
||||
|
||||
clientServicesSend(&dataStore);
|
||||
packetsSent++;
|
||||
}
|
||||
|
||||
auto const signalEvent = reinterpret_cast<SignalEventT>(Offsets::SignalEvent);
|
||||
if (signalEvent) {
|
||||
signalEvent(game::PET_BAR_UPDATE);
|
||||
}
|
||||
|
||||
DEBUG_LOG("Restored greater demon autocast for displayId=" << displayId
|
||||
<< " petGuid=" << petGuid
|
||||
<< " cvar=" << cvarName
|
||||
<< " packetsSent=" << packetsSent);
|
||||
}
|
||||
|
||||
void CGPetInfo_SetPetHook(hadesmem::PatchDetourBase *detour, void *thisptr, void *dummy_edx,
|
||||
uint32_t guid1, uint32_t guid2) {
|
||||
auto activePetGuid = *reinterpret_cast<uint64_t *>(Offsets::ActivePetGuid);
|
||||
|
||||
auto const origSetPet = detour->GetTrampolineT<CGPetInfo_SetPetT>();
|
||||
origSetPet(thisptr, dummy_edx, guid1, guid2);
|
||||
|
||||
uint64_t newPetGuid = *reinterpret_cast<uint64_t *>(Offsets::ActivePetGuid);
|
||||
if (activePetGuid != newPetGuid) {
|
||||
DEBUG_LOG("Active pet guid changed from " << activePetGuid << " to " << newPetGuid);
|
||||
|
||||
if (gUserSettings.preserveGreaterDemonAutocast && newPetGuid != 0) {
|
||||
RestoreGreaterDemonAutocastPreferences();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TogglePetSlotAutocastHook(hadesmem::PatchDetourBase *detour, uint32_t slot) {
|
||||
auto const orig = detour->GetTrampolineT<TogglePetSlotAutocastT>();
|
||||
gPendingToggleAutocastSlot = slot;
|
||||
orig(slot);
|
||||
gPendingToggleAutocastSlot = 0;
|
||||
}
|
||||
|
||||
void CGPetInfo_GetPetSpellActionHook(hadesmem::PatchDetourBase *detour, uint32_t *action) {
|
||||
auto const orig = detour->GetTrampolineT<CGPetInfo_GetPetSpellActionT>();
|
||||
orig(action);
|
||||
|
||||
if (!action) return;
|
||||
uint32_t spellSlot = gPendingToggleAutocastSlot;
|
||||
|
||||
uint32_t actionData = action[0];
|
||||
uint32_t actionId = game::UnitActionButtonAction(actionData);
|
||||
uint32_t type = game::UnitActionButtonType(actionData);
|
||||
|
||||
// save autocast preferences
|
||||
auto activePetGuid = *reinterpret_cast<uint64_t *>(Offsets::ActivePetGuid);
|
||||
if (gUserSettings.preserveGreaterDemonAutocast && activePetGuid != 0) {
|
||||
DEBUG_LOG("Saving greater demon autocast preferences for slot " << spellSlot);
|
||||
SaveGreaterDemonAutocastPreferences(spellSlot, actionData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Created by pmacc on 3/1/2026.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "main.hpp"
|
||||
|
||||
namespace Nampower {
|
||||
using CGPetInfo_SetPetT = void (__fastcall *)(void *thisptr, void *dummy_edx, uint32_t guid1, uint32_t guid2);
|
||||
using TogglePetSlotAutocastT = void (__fastcall *)(uint32_t slot);
|
||||
using CGPetInfo_GetPetSpellActionT = void (__fastcall *)(uint32_t *action);
|
||||
|
||||
void CGPetInfo_SetPetHook(hadesmem::PatchDetourBase *detour, void *thisptr, void *dummy_edx, uint32_t guid1, uint32_t guid2);
|
||||
void TogglePetSlotAutocastHook(hadesmem::PatchDetourBase *detour, uint32_t slot);
|
||||
void CGPetInfo_GetPetSpellActionHook(hadesmem::PatchDetourBase *detour, uint32_t *action);
|
||||
|
||||
void SaveGreaterDemonAutocastPreferences();
|
||||
void SaveGreaterDemonAutocastPreferences(uint32_t spellSlot, uint32_t actionData);
|
||||
void RestoreGreaterDemonAutocastPreferences();
|
||||
}
|
||||
@@ -64,6 +64,40 @@ namespace Nampower {
|
||||
return castSpellByName(luaState);
|
||||
}
|
||||
|
||||
|
||||
uint32_t Script_CastSpellNoQueue(uintptr_t *luaState) {
|
||||
luaState = GetLuaStatePtr();
|
||||
|
||||
// If 3rd arg is a unit token, resolve target and call directly
|
||||
if (lua_isstring(luaState, 3)) {
|
||||
uint32_t spellSlot = 0, bookType = 0;
|
||||
auto const getSpellSlotFromLua = reinterpret_cast<GetSpellSlotFromLuaT>(Offsets::GetSpellSlotFromLua);
|
||||
if (!getSpellSlotFromLua(luaState, &spellSlot, &bookType)) {
|
||||
lua_error(luaState, "Invalid spell slot in CastSpellNoQueue");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto const unitToken = lua_tostring(luaState, 3);
|
||||
auto const targetGuid = GetUnitGuidFromString(unitToken);
|
||||
|
||||
DEBUG_LOG("CastSpellNoQueue with unit token");
|
||||
gNoQueueCast = true;
|
||||
auto const castSpell = reinterpret_cast<CGSpellBook_CastSpellT>(Offsets::CGSpellBook_CastSpell);
|
||||
castSpell(spellSlot, bookType, targetGuid);
|
||||
gNoQueueCast = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Casting next spell without queuing");
|
||||
gNoQueueCast = true;
|
||||
auto const scriptCastSpell = reinterpret_cast<LuaScriptT>(Offsets::Script_CastSpell);
|
||||
auto result = scriptCastSpell(luaState);
|
||||
gNoQueueCast = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t Script_QueueSpellByName(uintptr_t *luaState) {
|
||||
luaState = GetLuaStatePtr(); // pcall leads to corrupted lua state pointer on added scripts, not sure why
|
||||
|
||||
@@ -536,9 +570,8 @@ namespace Nampower {
|
||||
return 3;
|
||||
}
|
||||
|
||||
uint32_t GetSpellSlotFromLuaHook(hadesmem::PatchDetourBase *detour, int param_1, uint32_t *slot, uint32_t *type) {
|
||||
// Get Lua state pointer
|
||||
uintptr_t *luaState = GetLuaStatePtr();
|
||||
uint32_t GetSpellSlotFromLuaHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState, uint32_t *slot, uint32_t *type) {
|
||||
luaState = GetLuaStatePtr();
|
||||
|
||||
// Check if first parameter is not a number
|
||||
if (lua_isnumber(luaState, 1) == 0) {
|
||||
@@ -598,7 +631,7 @@ namespace Nampower {
|
||||
} else {
|
||||
auto const getSpellSlotFromLua = detour->GetTrampolineT<GetSpellSlotFromLuaT>();
|
||||
// Fall back to original function
|
||||
return getSpellSlotFromLua(param_1, slot, type);
|
||||
return getSpellSlotFromLua(luaState, slot, type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
namespace Nampower {
|
||||
uint32_t Script_CastSpellByNameNoQueue(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_CastSpellNoQueue(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_CastSpellByNameHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_QueueSpellByName(uintptr_t *luaState);
|
||||
@@ -36,5 +38,5 @@ namespace Nampower {
|
||||
|
||||
uint32_t Script_GetSpellDuration(uintptr_t *luaState);
|
||||
|
||||
uint32_t GetSpellSlotFromLuaHook(hadesmem::PatchDetourBase *detour, int param_1, uint32_t *slot, uint32_t *type);
|
||||
uint32_t GetSpellSlotFromLuaHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState, uint32_t *slot, uint32_t *type);
|
||||
}
|
||||
|
||||
@@ -328,6 +328,11 @@ namespace Nampower {
|
||||
void
|
||||
TriggerSpellCastEvent(bool result, uint32_t spellId, CastType castType, std::uint64_t guid, uint32_t itemId) {
|
||||
static char format[] = "%d%d%d%s%d";
|
||||
|
||||
if (!guid) {
|
||||
// default to current target
|
||||
guid = game::GetCurrentTargetGuid();
|
||||
}
|
||||
char *guidStr = ConvertGuidToString(guid);
|
||||
|
||||
((int (__cdecl *)(int, char *, uint32_t, uint32_t, uint32_t, char *, uint32_t)) Offsets::SignalEventParam)(
|
||||
|
||||
@@ -29,6 +29,7 @@ struct UserSettings {
|
||||
|
||||
bool spamProtectionEnabled = true;
|
||||
bool enableAuraCastEvents = false;
|
||||
bool preserveGreaterDemonAutocast = true;
|
||||
|
||||
bool enableUnitEventsPet = true;
|
||||
bool enableUnitEventsParty = true;
|
||||
|
||||
Reference in New Issue
Block a user