Files
nampower/nampower/main.cpp
T
2026-02-12 09:10:14 -08:00

1734 lines
80 KiB
C++

/*
Copyright (c) 2017-2023, namreeb (legal@namreeb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
#include "logging.hpp"
#include "offsets.hpp"
#include "game.hpp"
#include "main.hpp"
#include "spellevents.hpp"
#include "spellcast.hpp"
#include "misc_scripts.hpp"
#include "spell_scripts.hpp"
#include "spellchannel.hpp"
#include "helper.hpp"
#include "items.hpp"
#include "item_scripts.hpp"
#include "cooldown_scripts.hpp"
#include "item_scripts.hpp"
#include "auras.hpp"
#include "lua_refs.hpp"
#include "autoattack.hpp"
#include <cstdint>
#include <memory>
#include <atomic>
#include <chrono>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <cstdio>
#include <windows.h>
#include <sys/stat.h>
BOOL WINAPI DllMain(HINSTANCE, uint32_t, void *);
namespace Nampower {
uint32_t gLastErrorTimeMs;
uint32_t gLastBufferIncreaseTimeMs;
uint32_t gLastBufferDecreaseTimeMs;
uint32_t gBufferTimeMs; // adjusts dynamically depending on errors
uint32_t gDisenchantItemId = 0;
uint32_t gDisenchantQuality = 0; // 0 = unset
bool gDisenchantIncludeSoulbound = false;
uint32_t gNextDisenchantTimeMs = 0;
bool gForceQueueCast;
bool gNoQueueCast;
bool gQueuesProcessed;
bool lastCastUsedServerDelay;
uint64_t gNextCastId = 1;
uint32_t gRunningAverageLatencyMs;
uint32_t gLastServerSpellDelayMs;
hadesmem::PatchDetourBase *castSpellDetour;
UserSettings gUserSettings;
LastCastData gLastCastData;
CastData gCastData;
CastSpellParams gLastNormalCastParams;
CastSpellParams gLastOnSwingCastParams;
CastQueue gNonGcdCastQueue = CastQueue(6);
CastQueue gCastHistory = CastQueue(30);
std::unique_ptr<hadesmem::PatchDetour<SysMsgInitializeT> > gSysMsgInitDetour;
std::unique_ptr<hadesmem::PatchDetour<LoadScriptFunctionsT> > gLoadScriptFunctionsDetour;
std::unique_ptr<hadesmem::PatchDetour<FrameScript_CreateEventsT> > gCreateEventsDetour;
std::unique_ptr<hadesmem::PatchDetour<FramescriptSetEventCountT> > gSetEventCountDetour;
std::unique_ptr<hadesmem::PatchDetour<SetCVarT> > gSetCVarDetour;
std::unique_ptr<hadesmem::PatchDetour<CGSpellBook_CastSpellT> > gCGSpellBook_CastSpellDetour;
std::unique_ptr<hadesmem::PatchDetour<Spell_C_CastSpellT> > gCastDetour;
std::unique_ptr<hadesmem::PatchDetour<SendCastT> > gSendCastDetour;
std::unique_ptr<hadesmem::PatchDetour<CancelSpellT> > gCancelSpellDetour;
std::unique_ptr<hadesmem::PatchDetour<SignalEventT> > gSignalEventDetour;
std::unique_ptr<hadesmem::PatchDetour<Spell_C_SpellFailedT> > gSpellFailedDetour;
std::unique_ptr<hadesmem::PatchDetour<Spell_C_GetSpellModifiersT> > gSpell_C_GetSpellModifiersDetour;
std::unique_ptr<hadesmem::PatchDetour<Spell_C_GetSpellRadiusT> > gSpell_C_GetSpellRadiusDetour;
std::unique_ptr<hadesmem::PatchRaw> gCastbarPatch;
std::unique_ptr<hadesmem::PatchDetour<ISceneEndT> > gIEndSceneDetour;
std::unique_ptr<hadesmem::PatchDetour<Spell_C_GetAutoRepeatingSpellT> > gSpell_C_GetAutoRepeatingSpellDetour;
std::unique_ptr<hadesmem::PatchDetour<Spell_C_CooldownEventTriggeredT> > gSpell_C_CooldownEventTriggeredDetour;
std::unique_ptr<hadesmem::PatchDetour<SpellGoT> > gSpellGoDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT> > gSpellTargetUnitDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT> > gSpellStopCastingDetour;
std::unique_ptr<hadesmem::PatchDetour<OnSpriteRightClickT> > gOnSpriteRightClickDetour;
std::unique_ptr<hadesmem::PatchDetour<Spell_C_HandleSpriteClickT> > gSpell_C_HandleSpriteClickDetour;
std::unique_ptr<hadesmem::PatchDetour<Spell_C_HandleTerrainClickT> > gSpell_C_HandleTerrainClickDetour;
std::unique_ptr<hadesmem::PatchDetour<CGWorldFrame_OnLayerTrackTerrainT> > gCGWorldFrame_OnLayerTrackTerrainDetour;
std::unique_ptr<hadesmem::PatchDetour<Spell_C_TargetSpellT> > gSpell_C_TargetSpellDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT> > gSpellCooldownDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT> > gSpellDelayedDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT> > gCastResultHandlerDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT> > gSpellFailedOtherHandlerDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT> > gSpellChannelStartHandlerDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT> > gSpellChannelUpdateHandlerDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT> > gPlaySpellVisualHandlerDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT> > gSpellHealingLogHandlerDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT> > gSpellEnergizeLogHandlerDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT> > gProcResistHandlerDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT> > gSpellLogMissHandlerDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT> > gSpellOrDamageImmuneHandlerDetour;
std::unique_ptr<hadesmem::PatchDetour<FastCallPacketHandlerT> > gSpellStartHandlerDetour;
std::unique_ptr<hadesmem::PatchDetour<FastCallPacketHandlerT> > gPeriodicAuraLogHandlerDetour;
std::unique_ptr<hadesmem::PatchDetour<FastCallPacketHandlerT> > gSpellNonMeleeDmgLogHandlerDetour;
std::unique_ptr<hadesmem::PatchDetour<CGPlayer_C_OnAttackIconPressedT> > gCGPlayer_C_OnAttackIconPressedDetour;
std::unique_ptr<hadesmem::PatchDetour<AttackRoundInfo_ReadPacketT> > gAttackRoundInfo_ReadPacketDetour;
std::unique_ptr<hadesmem::PatchDetour<CGActionBar_UseActionT> > gCGActionBar_UseActionDetour;
std::unique_ptr<hadesmem::PatchDetour<CGUnit_C_OnAuraRemovedT> > gCGUnit_C_OnAuraRemovedDetour;
std::unique_ptr<hadesmem::PatchDetour<CGUnit_C_OnAuraAddedT> > gCGUnit_C_OnAuraAddedDetour;
std::unique_ptr<hadesmem::PatchDetour<CGUnit_C_OnAuraStacksChangedT> > gCGUnit_C_OnAuraStacksChangedDetour;
std::unique_ptr<hadesmem::PatchDetour<UnitCombatLogUnitDeadT> > gUnitCombatLogUnitDeadDetour;
std::unique_ptr<hadesmem::PatchDetour<CGBuffBar_UpdateDurationT> > gCGBuffBar_UpdateDurationDetour;
std::unique_ptr<hadesmem::PatchDetour<InvalidFunctionPtrCheckT> > gInvalidFunctionPtrCheckDetour;
std::unique_ptr<hadesmem::PatchDetour<GetSpellSlotFromLuaT> > gGetSpellSlotFromLuaDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT> > gCSimpleFrame_GetNameDetour;
// Flags for one-time initialization
std::once_flag loadFlag;
std::once_flag initHooksFlag;
// Forward declarations for hook functions
void LoadScriptFunctionsHook(hadesmem::PatchDetourBase *detour);
void FrameScript_CreateEventsHook(hadesmem::PatchDetourBase *detour, int param_1, uint32_t maxEventId);
void Framescript_SetEventCountHook(hadesmem::PatchDetourBase *detour, void *thisPtr, void *dummy_edx,
uint32_t count);
uint32_t GetTime() {
return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()).count()) - gStartTime;
}
std::string GetHumanReadableTime() {
auto now = std::chrono::system_clock::now();
auto in_time_t = std::chrono::system_clock::to_time_t(now);
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
std::tm buf;
localtime_s(&buf, &in_time_t);
std::stringstream ss;
ss << std::put_time(&buf, "%Y-%m-%d %X");
ss << '.' << std::setfill('0') << std::setw(3) << ms.count();
return ss.str();
}
uint64_t GetWowTimeMs() {
auto const osGetAsyncTimeMs = reinterpret_cast<GetTimeMsT>(Offsets::OsGetAsyncTimeMs);
return osGetAsyncTimeMs();
}
void LuaCall(const char *code) {
auto function = (LuaCallT) Offsets::lua_call;
function(code, "Unused");
}
uintptr_t *GetLuaStatePtr() {
typedef uintptr_t *(__fastcall *GETCONTEXT)(void);
static auto p_GetContext = reinterpret_cast<GETCONTEXT>(Offsets::lua_state_ptr);
return p_GetContext();
}
void SetTarget(uint64_t target) {
auto setTargetFn = reinterpret_cast<TargetUnitT>(Offsets::ScriptTargetUnit);
setTargetFn(&target);
}
void SetSelectionTarget(uint64_t target) {
auto dataStore = CDataStore();
uint32_t opcode = 317; // CMSG_SET_SELECTION
dataStore.Put(opcode);
dataStore.Put(target);
dataStore.Finalize();
auto const clientServicesSend = reinterpret_cast<ClientServices_SendT>(Offsets::ClientServices_Send);
clientServicesSend(&dataStore);
}
void SetAttackTarget(uint64_t target) {
auto dataStore = CDataStore();
uint32_t opcode = 0x141; // CMSG_ATTACKSWING
dataStore.Put(opcode);
dataStore.Put(target);
dataStore.Finalize();
auto const clientServicesSend = reinterpret_cast<ClientServices_SendT>(Offsets::ClientServices_Send);
clientServicesSend(&dataStore);
}
void RegisterLuaFunction(char *name, uintptr_t *func) {
DEBUG_LOG("Registering " << name << " to " << func);
auto const registerFunction = reinterpret_cast<FrameScript_RegisterFunctionT>(
Offsets::FrameScript_RegisterFunction);
registerFunction(name, func);
}
// when the game first launches there won't be a cached latency value for 10-20 seconds and this will return 0
uint32_t GetLatencyMs() {
auto const getConnection = reinterpret_cast<GetClientConnectionT>(Offsets::GetClientConnection);
auto const getNetStats = reinterpret_cast<GetNetStatsT>(Offsets::GetNetStats);
auto connectionPtr = getConnection();
float bytesIn, bytesOut;
uint32_t latency;
getNetStats(connectionPtr, &bytesIn, &bytesOut, &latency);
// update running average
if (latency > 0) {
if (gRunningAverageLatencyMs == 0) {
// first time
gRunningAverageLatencyMs = latency;
// ignore big spikes
} else if (latency < gRunningAverageLatencyMs * 2) {
gRunningAverageLatencyMs = (gRunningAverageLatencyMs * 9 + latency) / 10;
}
}
return gRunningAverageLatencyMs;
}
uint32_t GetServerDelayMs() {
// if we have a gLastServerSpellDelayMs that seems reasonable, use it
// occasionally the server will take a long time to respond to a spell cast, in which case default to gBufferTimeMs
// if gLastServerSpellDelayMs == 0, then we don't have a valid value for the last cast
if (gUserSettings.optimizeBufferUsingPacketTimings && !lastCastUsedServerDelay) {
if (gLastServerSpellDelayMs > 0 && gLastServerSpellDelayMs < 200) {
lastCastUsedServerDelay = true;
return gLastServerSpellDelayMs;
}
}
lastCastUsedServerDelay = false;
return gBufferTimeMs;
}
bool InSpellQueueWindow(uint32_t remainingCastTime, uint32_t remainingGcd, bool spellIsTargeting) {
auto currentTime = GetTime();
uint32_t queueWindow = 0;
if (gCastData.channeling) {
if (gUserSettings.queueChannelingSpells) {
if (gCastData.cancelChannelNextTick) {
return true;
}
auto const remainingChannelTime = (gCastData.channelEndMs > currentTime)
? gCastData.channelEndMs -
currentTime
: 0;
return remainingChannelTime < gUserSettings.channelQueueWindowMs;
}
} else if (spellIsTargeting) {
queueWindow = gUserSettings.targetingQueueWindowMs;
} else {
queueWindow = gUserSettings.spellQueueWindowMs;
}
if (remainingCastTime > 0) {
return remainingCastTime < queueWindow || gForceQueueCast;
}
if (remainingGcd > 0) {
return remainingGcd < queueWindow || gForceQueueCast;
}
return false;
}
bool IsNonSwingSpellQueued() {
return gCastData.nonGcdSpellQueued || gCastData.normalSpellQueued;
}
uint32_t EffectiveCastEndMs() {
if (gCastData.channeling && gUserSettings.queueChannelingSpells) {
if (gUserSettings.interruptChannelsOutsideQueueWindow) {
auto currentTime = GetTime();
auto const remainingChannelTime = (gCastData.channelEndMs > currentTime)
? gCastData.channelEndMs -
currentTime
: 0;
if (remainingChannelTime < gUserSettings.channelQueueWindowMs) {
return gCastData.channelEndMs;
}
} else {
return gCastData.channelEndMs;
}
}
return max(gCastData.castEndMs, gCastData.delayEndMs);
}
void ResetChannelingFlags() {
gCastData.cancelChannelNextTick = false;
gCastData.channeling = false;
gCastData.channelStartMs = 0;
gCastData.channelEndMs = 0;
gCastData.channelSpellId = 0;
gCastData.channelTickTimeMs = 0;
gCastData.channelNumTicks = 0;
}
void ResetCastFlags() {
// don't reset delayEndMs
gCastData.castEndMs = 0;
gCastData.castSpellId = 0;
gCastData.gcdEndMs = 0;
ResetChannelingFlags();
}
void ResetOnSwingFlags() {
gCastData.onSwingQueued = false;
gCastData.onSwingSpellId = 0;
}
void ClearQueuedSpells() {
if (gCastData.normalSpellQueued || gCastData.cooldownNormalSpellQueued) {
TriggerSpellQueuedEvent(NORMAL_QUEUE_POPPED, gLastNormalCastParams.spellId);
gCastData.normalSpellQueued = false;
gCastData.cooldownNormalSpellQueued = false;
}
if (gCastData.nonGcdSpellQueued || gCastData.cooldownNonGcdSpellQueued) {
while (!gNonGcdCastQueue.isEmpty()) {
auto castParams = gNonGcdCastQueue.pop();
TriggerSpellQueuedEvent(NON_GCD_QUEUE_POPPED, castParams.spellId);
}
gCastData.nonGcdSpellQueued = false;
gCastData.cooldownNonGcdSpellQueued = false;
}
gCastData.targetingSpellQueued = false;
gCastData.targetingSpellId = 0;
}
void ResetDisenchantState() {
gDisenchantItemId = 0;
gDisenchantQuality = 0;
gDisenchantIncludeSoulbound = false;
gNextDisenchantTimeMs = 0;
}
void checkForStopChanneling() {
if (gUserSettings.queueChannelingSpells && IsNonSwingSpellQueued()) {
// for channels just end channeling
auto const currentTime = GetTime();
auto const elapsed = currentTime - gLastCastData.channelStartTimeMs;
auto remainingChannelTime = (gCastData.channelEndMs > currentTime)
? gCastData.channelEndMs - currentTime
: 0;
auto const currentLatency = GetLatencyMs();
uint32_t latencyReduction = 0;
if (currentLatency > 0 && gUserSettings.channelLatencyReductionPercentage != 0) {
latencyReduction = ((uint32_t) currentLatency * gUserSettings.channelLatencyReductionPercentage) / 100;
if (remainingChannelTime > latencyReduction) {
remainingChannelTime -= latencyReduction;
} else {
remainingChannelTime = 0;
}
}
if (remainingChannelTime <= 0) {
DEBUG_LOG("Ending channel [" << elapsed << " elapsed > "
<< gCastData.channelDuration << " original duration "
<< " latency reduction " << latencyReduction << "]"
<< " triggering queued spells");
ResetChannelingFlags();
} else if (gCastData.cancelChannelNextTick &&
gCastData.channelStartMs > 0 &&
currentTime - gCastData.channelStartMs < 60000) {
auto nextTickTimeMs = gCastData.channelStartMs;
// find the next tick time but don't choose the next tick until gBufferTimeMs has passed after a tick
while (nextTickTimeMs < currentTime - gBufferTimeMs) {
nextTickTimeMs += gCastData.channelTickTimeMs;
}
uint32_t remainingTickTime = 0;
if (nextTickTimeMs > currentTime) {
remainingTickTime = nextTickTimeMs - currentTime;
}
if (remainingTickTime > 0) {
if (currentLatency > 0 && gUserSettings.channelLatencyReductionPercentage != 0) {
if (remainingTickTime > latencyReduction) {
remainingTickTime -= latencyReduction;
} else {
remainingTickTime = 0;
}
} else {
// default to reducing by gBufferTimeMs
if (remainingTickTime > gBufferTimeMs) {
remainingTickTime -= gBufferTimeMs;
} else {
remainingTickTime = 0;
}
}
}
if (remainingTickTime <= 0) {
DEBUG_LOG("Ending channel due to cancelChannelNextTick. "
<< "Remaining tick time: " << nextTickTimeMs - currentTime
<< " latency reduction: " << latencyReduction);
ResetChannelingFlags();
}
}
}
}
bool processQueues() {
if (!gCastData.channeling && !gQueuesProcessed) {
gQueuesProcessed = true;
// check for high priority script
if (RunQueuedScript(1)) {
// script ran, stop processing
return true;
}
// check for non gcd spell
if (gCastData.nonGcdSpellQueued) {
auto currentTime = GetTime();
if (EffectiveCastEndMs() <= currentTime) {
CastQueuedNonGcdSpell();
return true;
}
}
// check for cooldown non gcd spell
if (gCastData.cooldownNonGcdSpellQueued) {
auto currentTime = GetTime();
if (gCastData.cooldownNonGcdEndMs <= currentTime) {
DEBUG_LOG("Non gcd spell cooldown up, casting queued spell");
// trigger the regular non gcd queuing and turn off cooldownNonGcdSpellQueued
gCastData.cooldownNonGcdSpellQueued = false;
gCastData.nonGcdSpellQueued = true;
CastQueuedNonGcdSpell();
return true;
}
}
// check for medium priority script
if (RunQueuedScript(2)) {
// script ran, stop processing
return true;
}
// check for normal spell
if (gCastData.normalSpellQueued) {
auto currentTime = GetTime();
auto effectiveCastEndMs = EffectiveCastEndMs();
// get max of cooldown and gcd
auto delay = effectiveCastEndMs > gCastData.gcdEndMs ? effectiveCastEndMs : gCastData.gcdEndMs;
if (delay <= currentTime) {
// if more than MAX_TIME_SINCE_LAST_CAST_FOR_QUEUE seconds have passed since the last cast, ignore
if (currentTime - gLastCastData.startTimeMs < MAX_TIME_SINCE_LAST_CAST_FOR_QUEUE) {
CastQueuedNormalSpell();
return true;
} else {
DEBUG_LOG("Ignoring queued cast of " << game::GetSpellName(gLastNormalCastParams.spellId)
<< " due to max time since last cast");
TriggerSpellQueuedEvent(NORMAL_QUEUE_POPPED, gLastNormalCastParams.spellId);
gCastData.normalSpellQueued = false;
gCastData.targetingSpellQueued = false;
gCastData.targetingSpellId = 0;
}
}
}
// check for cooldown normal spell
if (gCastData.cooldownNormalSpellQueued) {
auto currentTime = GetTime();
if (gCastData.cooldownNormalEndMs <= currentTime) {
DEBUG_LOG("Spell cooldown up, casting queued spell");
// trigger the regular non gcd queuing and turn off cooldownNonGcdSpellQueued
gCastData.cooldownNormalSpellQueued = false;
gCastData.normalSpellQueued = true;
CastQueuedNormalSpell();
return true;
}
}
if (RunQueuedScript(3)) {
// script ran, stop processing
return true;
}
// Check for disenchant all
if (gDisenchantItemId != 0 || gDisenchantQuality != 0) {
auto currentTime = GetTime();
// Try to disenchant when timer has elapsed
if (gNextDisenchantTimeMs <= currentTime) {
if (TryDisenchant()) {
return true;
}
}
}
}
gQueuesProcessed = true;
// Process item export if active (low priority, runs when nothing else is queued)
// ProcessItemExport()
return false;
}
int OnSpriteRightClickHook(hadesmem::PatchDetourBase *detour, uint64_t objectGUID) {
auto const onSpriteRightClick = detour->GetTrampolineT<OnSpriteRightClickT>();
auto const currentTargetGuid = game::GetCurrentTargetGuid();
// if we have a target that is not the right click target, ignore the right click
if (gUserSettings.preventRightClickTargetChange && currentTargetGuid &&
currentTargetGuid != objectGUID) {
auto unitOrPlayer = game::ClntObjMgrObjectPtr(
static_cast<game::TypeMask>(game::TYPEMASK_PLAYER | game::TYPEMASK_UNIT), objectGUID);
// only prevent right click if guid is a unit/player
if (unitOrPlayer) {
auto GetUnitFromName = reinterpret_cast<GetUnitFromNameT>(Offsets::GetUnitFromName);
auto const unit = GetUnitFromName("player");
// combat check from Script_UnitAffectingCombat
// only prevent right click if in combat
if (unit && game::UnitIsInCombat(unit)) {
return 1;
}
}
}
if (gUserSettings.preventRightClickPvPAttack) {
// check if clicking player
auto playerUnit = game::ClntObjMgrObjectPtr(game::TYPEMASK_PLAYER, objectGUID);
if (playerUnit && game::UnitIsPvpFlagged(playerUnit)) {
// check if they have pvp enabled
return 1;
}
}
return onSpriteRightClick(objectGUID);
}
int *ISceneEndHook(hadesmem::PatchDetourBase *detour, uintptr_t *ptr) {
auto const iSceneEnd = detour->GetTrampolineT<ISceneEndT>();
// check if it's time to end channeling
if (gCastData.channeling) {
checkForStopChanneling();
}
// process any queued spells/scripts
processQueues();
gQueuesProcessed = false;
return iSceneEnd(ptr);
}
void InvalidFunctionPtrCheckHook(hadesmem::PatchDetourBase *detour, uint32_t param_1) {
// remove original call to allow registering lua functions at any address range
}
void
Spell_C_GetSpellModifiersHook(hadesmem::PatchDetourBase *detour, const game::SpellRec *spellRec, int *returnVal,
game::SpellModOp modOp) {
auto const getSpellModifiers = detour->GetTrampolineT<Spell_C_GetSpellModifiersT>();
getSpellModifiers(spellRec, returnVal, modOp);
}
void updateFromCvar(const char *cvar, const char *value) {
if (strcmp(cvar, "NP_QueueCastTimeSpells") == 0) {
gUserSettings.queueCastTimeSpells = atoi(value) != 0;
DEBUG_LOG("Set NP_QueueCastTimeSpells to " << gUserSettings.queueCastTimeSpells);
} else if (strcmp(cvar, "NP_QueueInstantSpells") == 0) {
gUserSettings.queueInstantSpells = atoi(value) != 0;
DEBUG_LOG("Set NP_QueueInstantSpells to " << gUserSettings.queueInstantSpells);
} else if (strcmp(cvar, "NP_QueueOnSwingSpells") == 0) {
gUserSettings.queueOnSwingSpells = atoi(value) != 0;
DEBUG_LOG("Set NP_QueueOnSwingSpells to " << gUserSettings.queueOnSwingSpells);
} else if (strcmp(cvar, "NP_QueueChannelingSpells") == 0) {
gUserSettings.queueChannelingSpells = atoi(value) != 0;
DEBUG_LOG("Set NP_QueueChannelingSpells to " << gUserSettings.queueChannelingSpells);
} else if (strcmp(cvar, "NP_QueueTargetingSpells") == 0) {
gUserSettings.queueTargetingSpells = atoi(value) != 0;
DEBUG_LOG("Set NP_QueueTargetingSpells to " << gUserSettings.queueTargetingSpells);
} else if (strcmp(cvar, "NP_QueueSpellsOnCooldown") == 0) {
gUserSettings.queueSpellsOnCooldown = atoi(value) != 0;
DEBUG_LOG("Set NP_QueueSpellsOnCooldown to " << gUserSettings.queueSpellsOnCooldown);
} else if (strcmp(cvar, "NP_InterruptChannelsOutsideQueueWindow") == 0) {
gUserSettings.interruptChannelsOutsideQueueWindow = atoi(value) != 0;
DEBUG_LOG("Set NP_InterruptChannelsOutsideQueueWindow to "
<< gUserSettings.interruptChannelsOutsideQueueWindow);
} else if ((strcmp(cvar, "NP_RetryServerRejectedSpells") == 0)) {
gUserSettings.retryServerRejectedSpells = atoi(value) != 0;
DEBUG_LOG("Set NP_RetryServerRejectedSpells to " << gUserSettings.retryServerRejectedSpells);
} else if (strcmp(cvar, "NP_QuickcastTargetingSpells") == 0) {
gUserSettings.quickcastTargetingSpells = atoi(value) != 0;
DEBUG_LOG("Set NP_QuickcastTargetingSpells to " << gUserSettings.quickcastTargetingSpells);
} else if (strcmp(cvar, "NP_ReplaceMatchingNonGcdCategory") == 0) {
gUserSettings.replaceMatchingNonGcdCategory = atoi(value) != 0;
DEBUG_LOG("Set NP_ReplaceMatchingNonGcdCategory to " << gUserSettings.replaceMatchingNonGcdCategory);
} else if (strcmp(cvar, "NP_OptimizeBufferUsingPacketTimings") == 0) {
gUserSettings.optimizeBufferUsingPacketTimings = atoi(value) != 0;
DEBUG_LOG("Set NP_OptimizeBufferUsingPacketTimings to " << gUserSettings.optimizeBufferUsingPacketTimings);
} else if (strcmp(cvar, "NP_PreventRightClickTargetChange") == 0) {
gUserSettings.preventRightClickTargetChange = atoi(value) != 0;
DEBUG_LOG("Set NP_PreventRightClickTargetChange to " << gUserSettings.preventRightClickTargetChange);
} else if (strcmp(cvar, "NP_PreventRightClickPvPAttack") == 0) {
gUserSettings.preventRightClickPvPAttack = atoi(value) != 0;
DEBUG_LOG("Set NP_PreventRightClickPvPAttack to " << gUserSettings.preventRightClickPvPAttack);
} else if (strcmp(cvar, "NP_DoubleCastToEndChannelEarly") == 0) {
gUserSettings.doubleCastToEndChannelEarly = atoi(value) != 0;
DEBUG_LOG("Set NP_DoubleCastToEndChannelEarly to " << gUserSettings.doubleCastToEndChannelEarly);
} else if (strcmp(cvar, "NP_QuickcastOnDoubleCast") == 0) {
gUserSettings.quickcastOnDoubleCast = atoi(value) != 0;
DEBUG_LOG("Set NP_QuickcastOnDoubleCast to " << gUserSettings.quickcastOnDoubleCast);
} 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_EnableAuraCastEvents") == 0) {
gUserSettings.enableAuraCastEvents = atoi(value) != 0;
DEBUG_LOG("Set NP_EnableAuraCastEvents to " << gUserSettings.enableAuraCastEvents);
} else if (strcmp(cvar, "NP_EnableAutoAttackEvents") == 0) {
gUserSettings.enableAutoAttackEvents = atoi(value) != 0;
DEBUG_LOG("Set NP_EnableAutoAttackEvents to " << gUserSettings.enableAutoAttackEvents);
} else if (strcmp(cvar, "NP_EnableSpellStartEvents") == 0) {
gUserSettings.enableSpellStartEvents = atoi(value) != 0;
DEBUG_LOG("Set NP_EnableSpellStartEvents to " << gUserSettings.enableSpellStartEvents);
} else if (strcmp(cvar, "NP_EnableSpellGoEvents") == 0) {
gUserSettings.enableSpellGoEvents = atoi(value) != 0;
DEBUG_LOG("Set NP_EnableSpellGoEvents to " << gUserSettings.enableSpellGoEvents);
} else if (strcmp(cvar, "NP_EnableSpellHealEvents") == 0) {
gUserSettings.enableSpellHealEvents = atoi(value) != 0;
DEBUG_LOG("Set NP_EnableSpellHealEvents to " << gUserSettings.enableSpellHealEvents);
} else if (strcmp(cvar, "NP_EnableSpellEnergizeEvents") == 0) {
gUserSettings.enableSpellEnergizeEvents = atoi(value) != 0;
DEBUG_LOG("Set NP_EnableSpellEnergizeEvents to " << gUserSettings.enableSpellEnergizeEvents);
} else if (strcmp(cvar, "NP_PreventMountingWhenBuffCapped") == 0) {
gUserSettings.preventMountingWhenBuffCapped = atoi(value) != 0;
DEBUG_LOG("Set NP_PreventMountingWhenBuffCapped to " << gUserSettings.preventMountingWhenBuffCapped);
} else if (strcmp(cvar, "NP_MinBufferTimeMs") == 0) {
gUserSettings.minBufferTimeMs = atoi(value);
DEBUG_LOG("Set NP_MinBufferTimeMs and current buffer to " << gUserSettings.minBufferTimeMs);
gBufferTimeMs = gUserSettings.minBufferTimeMs;
} else if (strcmp(cvar, "NP_NonGcdBufferTimeMs") == 0) {
gUserSettings.nonGcdBufferTimeMs = atoi(value);
DEBUG_LOG("Set NP_NonGcdBufferTimeMs to " << gUserSettings.nonGcdBufferTimeMs);
} else if (strcmp(cvar, "NP_MaxBufferIncreaseMs") == 0) {
gUserSettings.maxBufferIncreaseMs = atoi(value);
DEBUG_LOG("Set NP_MaxBufferIncreaseMs to " << gUserSettings.maxBufferIncreaseMs);
} else if (strcmp(cvar, "NP_SpellQueueWindowMs") == 0) {
gUserSettings.spellQueueWindowMs = atoi(value);
DEBUG_LOG("Set NP_SpellQueueWindowMs to " << gUserSettings.spellQueueWindowMs);
} else if (strcmp(cvar, "NP_OnSwingBufferCooldownMs") == 0) {
gUserSettings.onSwingBufferCooldownMs = atoi(value);
DEBUG_LOG("Set NP_OnSwingBufferCooldownMs to " << gUserSettings.onSwingBufferCooldownMs);
} else if (strcmp(cvar, "NP_ChannelQueueWindowMs") == 0) {
gUserSettings.channelQueueWindowMs = atoi(value);
DEBUG_LOG("Set NP_ChannelQueueWindowMs to " << gUserSettings.channelQueueWindowMs);
} else if (strcmp(cvar, "NP_TargetingQueueWindowMs") == 0) {
gUserSettings.targetingQueueWindowMs = atoi(value);
DEBUG_LOG("Set NP_TargetingQueueWindowMs to " << gUserSettings.targetingQueueWindowMs);
} else if (strcmp(cvar, "NP_CooldownQueueWindowMs") == 0) {
gUserSettings.cooldownQueueWindowMs = atoi(value);
DEBUG_LOG("Set NP_CooldownQueueWindowMs to " << gUserSettings.cooldownQueueWindowMs);
} else if (strcmp(cvar, "NP_ChannelLatencyReductionPercentage") == 0) {
gUserSettings.channelLatencyReductionPercentage = atoi(value);
DEBUG_LOG(
"Set NP_ChannelLatencyReductionPercentage to " << gUserSettings.channelLatencyReductionPercentage);
} else if (strcmp(cvar, "NP_NameplateDistance") == 0) {
auto distance = std::stof(value);
SetNameplateDistance(distance);
DEBUG_LOG(
"Set NP_NameplateDistance to " << distance);
}
}
int Script_SetCVarHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
auto const cvarSetOrig = gSetCVarDetour->GetTrampolineT<SetCVarT>();
auto const lua_isstring = reinterpret_cast<lua_isstringT>(Offsets::lua_isstring);
if (lua_isstring(luaState, 1)) {
auto const lua_tostring = reinterpret_cast<lua_tostringT>(Offsets::lua_tostring);
auto const cVarName = lua_tostring(luaState, 1);
auto const cVarValue = lua_tostring(luaState, 2);
// if cvar starts with "NP_", then we need to handle it
if (strncmp(cVarName, "NP_", 3) == 0) {
updateFromCvar(cVarName, cVarValue);
}
} // original function handles errors
return cvarSetOrig(luaState);
}
int *getCvar(const char *cvar) {
auto const cvarLookup = hadesmem::detail::AliasCast<CVarLookupT>(Offsets::CVarLookup);
uintptr_t *cvarPtr = cvarLookup(cvar);
if (cvarPtr) {
return reinterpret_cast<int *>(cvarPtr +
10); // get intValue from CVar which is consistent, strValue more complicated
}
return nullptr;
}
void loadUserVar(const char *cvar) {
int *value = getCvar(cvar);
if (value) {
updateFromCvar(cvar, std::to_string(*value).c_str());
} else {
DEBUG_LOG("Using default value for " << cvar);
}
}
bool isFileInUse(const char *filename) {
HANDLE file = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
if (error == ERROR_SHARING_VIOLATION || error == ERROR_LOCK_VIOLATION) {
return true;
}
return false;
}
CloseHandle(file);
return false;
}
bool safeRemove(const char *filename) {
// If file doesn't exist, consider it successfully removed
if (GetFileAttributesA(filename) == INVALID_FILE_ATTRIBUTES) {
return true;
}
// Clear read-only attribute if set
SetFileAttributesA(filename, FILE_ATTRIBUTE_NORMAL);
// Try to delete with retries
for (int i = 0; i < 3; i++) {
if (DeleteFileA(filename)) {
return true;
}
Sleep(10);
}
return false;
}
bool safeRename(const char *oldname, const char *newname) {
if (isFileInUse(oldname) || isFileInUse(newname)) {
return false;
}
return rename(oldname, newname) == 0;
}
void loadConfig() {
gStartTime = static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()).count());
// create logs directory if it doesn't exist
CreateDirectoryA("Logs", NULL);
// move any existing logs from root directory to Logs folder
if (GetFileAttributesA("nampower_debug.log") != INVALID_FILE_ATTRIBUTES)
safeRename("nampower_debug.log", "Logs\\nampower_debug.log");
if (GetFileAttributesA("nampower_debug.log.1") != INVALID_FILE_ATTRIBUTES)
safeRename("nampower_debug.log.1", "Logs\\nampower_debug.log.1");
if (GetFileAttributesA("nampower_debug.log.2") != INVALID_FILE_ATTRIBUTES)
safeRename("nampower_debug.log.2", "Logs\\nampower_debug.log.2");
if (GetFileAttributesA("nampower_debug.log.3") != INVALID_FILE_ATTRIBUTES)
safeRename("nampower_debug.log.3", "Logs\\nampower_debug.log.3");
// remove/rename previous logs
safeRemove("Logs\\nampower_debug.log.3");
safeRename("Logs\\nampower_debug.log.2", "Logs\\nampower_debug.log.3");
safeRename("Logs\\nampower_debug.log.1", "Logs\\nampower_debug.log.2");
safeRename("Logs\\nampower_debug.log", "Logs\\nampower_debug.log.1");
// open new log file
debugLogFile.open("Logs\\nampower_debug.log");
DEBUG_LOG("Loading nampower v" << MAJOR_VERSION << "." << MINOR_VERSION << "." << PATCH_VERSION);
char defaultTrue[] = "1";
char defaultFalse[] = "0";
DEBUG_LOG("Registering/Loading CVars");
// register cvars
auto const CVarRegister = hadesmem::detail::AliasCast<CVarRegisterT>(Offsets::RegisterCVar);
char NP_QueueCastTimeSpells[] = "NP_QueueCastTimeSpells";
CVarRegister(NP_QueueCastTimeSpells, // name
nullptr, // help
0, // unk1
gUserSettings.queueCastTimeSpells ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
5, // category
0, // unk2
0); // unk3
char NP_QueueInstantSpells[] = "NP_QueueInstantSpells";
CVarRegister(NP_QueueInstantSpells, // name
nullptr, // help
0, // unk1
gUserSettings.queueInstantSpells ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_QueueChannelingSpells[] = "NP_QueueChannelingSpells";
CVarRegister(NP_QueueChannelingSpells, // name
nullptr, // help
0, // unk1
gUserSettings.queueChannelingSpells ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_QueueTargetingSpells[] = "NP_QueueTargetingSpells";
CVarRegister(NP_QueueTargetingSpells, // name
nullptr, // help
0, // unk1
gUserSettings.queueTargetingSpells ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_QueueOnSwingSpells[] = "NP_QueueOnSwingSpells";
CVarRegister(NP_QueueOnSwingSpells, // name
nullptr, // help
0, // unk1
gUserSettings.queueOnSwingSpells ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_QueueSpellsOnCooldown[] = "NP_QueueSpellsOnCooldown";
CVarRegister(NP_QueueSpellsOnCooldown, // name
nullptr, // help
0, // unk1
gUserSettings.queueSpellsOnCooldown ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_InterruptChannelsOutsideQueueWindow[] = "NP_InterruptChannelsOutsideQueueWindow";
CVarRegister(NP_InterruptChannelsOutsideQueueWindow, // name
nullptr, // help
0, // unk1
gUserSettings.interruptChannelsOutsideQueueWindow
? defaultTrue
: defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_RetryServerRejectedSpells[] = "NP_RetryServerRejectedSpells";
CVarRegister(NP_RetryServerRejectedSpells, // name
nullptr, // help
0, // unk1
gUserSettings.retryServerRejectedSpells ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_QuickcastTargetingSpells[] = "NP_QuickcastTargetingSpells";
CVarRegister(NP_QuickcastTargetingSpells, // name
nullptr, // help
0, // unk1
gUserSettings.quickcastTargetingSpells ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_MinBufferTimeMs[] = "NP_MinBufferTimeMs";
CVarRegister(NP_MinBufferTimeMs, // name
nullptr, // help
0, // unk1
std::to_string(gUserSettings.minBufferTimeMs).c_str(), // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_NonGcdBufferTimeMs[] = "NP_NonGcdBufferTimeMs";
CVarRegister(NP_NonGcdBufferTimeMs, // name
nullptr, // help
0, // unk1
std::to_string(gUserSettings.nonGcdBufferTimeMs).c_str(), // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_MaxBufferIncreaseMs[] = "NP_MaxBufferIncreaseMs";
CVarRegister(NP_MaxBufferIncreaseMs, // name
nullptr, // help
0, // unk1
std::to_string(gUserSettings.maxBufferIncreaseMs).c_str(), // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_SpellQueueWindowMs[] = "NP_SpellQueueWindowMs";
CVarRegister(NP_SpellQueueWindowMs, // name
nullptr, // help
0, // unk1
std::to_string(gUserSettings.spellQueueWindowMs).c_str(), // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_ChannelQueueWindowMs[] = "NP_ChannelQueueWindowMs";
CVarRegister(NP_ChannelQueueWindowMs, // name
nullptr, // help
0, // unk1
std::to_string(gUserSettings.channelQueueWindowMs).c_str(), // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_TargetingQueueWindowMs[] = "NP_TargetingQueueWindowMs";
CVarRegister(NP_TargetingQueueWindowMs, // name
nullptr, // help
0, // unk1
std::to_string(gUserSettings.targetingQueueWindowMs).c_str(), // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_CooldownQueueWindowMs[] = "NP_CooldownQueueWindowMs";
CVarRegister(NP_CooldownQueueWindowMs, // name
nullptr, // help
0, // unk1
std::to_string(gUserSettings.cooldownQueueWindowMs).c_str(), // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_EnableAuraCastEvents[] = "NP_EnableAuraCastEvents";
CVarRegister(NP_EnableAuraCastEvents, // name
nullptr, // help
0, // unk1
gUserSettings.enableAuraCastEvents ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_EnableAutoAttackEvents[] = "NP_EnableAutoAttackEvents";
CVarRegister(NP_EnableAutoAttackEvents, // name
nullptr, // help
0, // unk1
gUserSettings.enableAutoAttackEvents ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_EnableSpellStartEvents[] = "NP_EnableSpellStartEvents";
CVarRegister(NP_EnableSpellStartEvents, // name
nullptr, // help
0, // unk1
gUserSettings.enableSpellStartEvents ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_EnableSpellGoEvents[] = "NP_EnableSpellGoEvents";
CVarRegister(NP_EnableSpellGoEvents, // name
nullptr, // help
0, // unk1
gUserSettings.enableSpellGoEvents ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_EnableSpellHealEvents[] = "NP_EnableSpellHealEvents";
CVarRegister(NP_EnableSpellHealEvents, // name
nullptr, // help
0, // unk1
gUserSettings.enableSpellHealEvents ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_EnableSpellEnergizeEvents[] = "NP_EnableSpellEnergizeEvents";
CVarRegister(NP_EnableSpellEnergizeEvents, // name
nullptr, // help
0, // unk1
gUserSettings.enableSpellEnergizeEvents ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_PreventMountingWhenBuffCapped[] = "NP_PreventMountingWhenBuffCapped";
CVarRegister(NP_PreventMountingWhenBuffCapped, // name
nullptr, // help
0, // unk1
gUserSettings.preventMountingWhenBuffCapped ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_OnSwingBufferCooldownMs[] = "NP_OnSwingBufferCooldownMs";
CVarRegister(NP_OnSwingBufferCooldownMs, // name
nullptr, // help
0, // unk1
std::to_string(gUserSettings.onSwingBufferCooldownMs).c_str(), // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_ReplaceMatchingNonGcdCategory[] = "NP_ReplaceMatchingNonGcdCategory";
CVarRegister(NP_ReplaceMatchingNonGcdCategory, // name
nullptr, // help
0, // unk1
gUserSettings.replaceMatchingNonGcdCategory ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_OptimizeBufferUsingPacketTimings[] = "NP_OptimizeBufferUsingPacketTimings";
CVarRegister(NP_OptimizeBufferUsingPacketTimings, // name
nullptr, // help
0, // unk1
gUserSettings.optimizeBufferUsingPacketTimings
? defaultTrue
: defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_PreventRightClickTargetChange[] = "NP_PreventRightClickTargetChange";
CVarRegister(NP_PreventRightClickTargetChange, // name
nullptr, // help
0, // unk1
gUserSettings.preventRightClickTargetChange ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_PreventRightClickPvPAttack[] = "NP_PreventRightClickPvPAttack";
CVarRegister(NP_PreventRightClickPvPAttack, // name
nullptr, // help
0, // unk1
gUserSettings.preventRightClickPvPAttack ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_DoubleCastToEndChannelEarly[] = "NP_DoubleCastToEndChannelEarly";
CVarRegister(NP_DoubleCastToEndChannelEarly, // name
nullptr, // help
0, // unk1
gUserSettings.doubleCastToEndChannelEarly ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_QuickcastOnDoubleCast[] = "NP_QuickcastOnDoubleCast";
CVarRegister(NP_QuickcastOnDoubleCast, // name
nullptr, // help
0, // unk1
gUserSettings.quickcastOnDoubleCast ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_SpamProtectionEnabled[] = "NP_SpamProtectionEnabled";
CVarRegister(NP_SpamProtectionEnabled, // name
nullptr, // help
0, // unk1
gUserSettings.spamProtectionEnabled ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_ChannelLatencyReductionPercentage[] = "NP_ChannelLatencyReductionPercentage";
CVarRegister(NP_ChannelLatencyReductionPercentage, // name
nullptr, // help
0, // unk1
std::to_string(gUserSettings.channelLatencyReductionPercentage).c_str(), // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_NameplateDistance[] = "NP_NameplateDistance";
CVarRegister(NP_NameplateDistance, // name
nullptr, // help
0, // unk1
std::to_string(GetNameplateDistance()).c_str(), // use the game's DAT value as the default
nullptr, // callback
1, // category
0, // unk2
0); // unk3
// update from cvars
loadUserVar("NP_QueueCastTimeSpells");
loadUserVar("NP_QueueInstantSpells");
loadUserVar("NP_QueueOnSwingSpells");
loadUserVar("NP_QueueChannelingSpells");
loadUserVar("NP_QueueTargetingSpells");
loadUserVar("NP_QueueSpellsOnCooldown");
loadUserVar("NP_InterruptChannelsOutsideQueueWindow");
loadUserVar("NP_RetryServerRejectedSpells");
loadUserVar("NP_QuickcastTargetingSpells");
loadUserVar("NP_QuickcastOnDoubleCast");
loadUserVar("NP_ReplaceMatchingNonGcdCategory");
loadUserVar("NP_OptimizeBufferUsingPacketTimings");
loadUserVar("NP_PreventRightClickTargetChange");
loadUserVar("NP_PreventRightClickPvPAttack");
loadUserVar("NP_DoubleCastToEndChannelEarly");
loadUserVar("NP_SpamProtectionEnabled");
loadUserVar("NP_EnableAuraCastEvents");
loadUserVar("NP_EnableAutoAttackEvents");
loadUserVar("NP_EnableSpellStartEvents");
loadUserVar("NP_EnableSpellGoEvents");
loadUserVar("NP_EnableSpellHealEvents");
loadUserVar("NP_EnableSpellEnergizeEvents");
loadUserVar("NP_PreventMountingWhenBuffCapped");
loadUserVar("NP_MinBufferTimeMs");
loadUserVar("NP_NonGcdBufferTimeMs");
loadUserVar("NP_MaxBufferIncreaseMs");
loadUserVar("NP_SpellQueueWindowMs");
loadUserVar("NP_ChannelQueueWindowMs");
loadUserVar("NP_TargetingQueueWindowMs");
loadUserVar("NP_OnSwingBufferCooldownMs");
loadUserVar("NP_CooldownQueueWindowMs");
loadUserVar("NP_ChannelLatencyReductionPercentage");
loadUserVar("NP_NameplateDistance");
gBufferTimeMs = gUserSettings.minBufferTimeMs;
}
// Template function to simplify hook initialization with specific storage
template<typename FuncT, typename HookT>
std::unique_ptr<hadesmem::PatchDetour<FuncT> >
createHook(const hadesmem::Process &process, Offsets offset, HookT hookFunc) {
auto const originalFunc = hadesmem::detail::AliasCast<FuncT>(offset);
const int maxRetries = 10;
std::string lastError;
for (int attempt = 0; attempt < maxRetries; ++attempt) {
try {
auto detour = std::make_unique<hadesmem::PatchDetour<FuncT> >(process, originalFunc, hookFunc);
detour->Apply();
if (attempt > 0) {
DEBUG_LOG(" Hook succeeded after " << (attempt + 1) << " attempts (offset 0x"
<< std::hex << static_cast<uint32_t>(offset) << std::dec << ")");
}
return detour;
} catch (const hadesmem::Error& e) {
lastError = e.what();
if (attempt == 0) {
DEBUG_LOG(" Hook failed on offset 0x" << std::hex << static_cast<uint32_t>(offset)
<< std::dec << ": " << lastError << " - retrying...");
}
if (attempt < maxRetries - 1) {
Sleep(10); // Wait 10ms before retry
continue;
} else {
// Out of retries
DEBUG_LOG(" FAILED after " << maxRetries << " attempts (offset 0x"
<< std::hex << static_cast<uint32_t>(offset) << std::dec
<< "): " << lastError);
throw;
}
} catch (const std::exception& e) {
lastError = e.what();
if (attempt == 0) {
DEBUG_LOG(" Hook failed with std::exception on offset 0x"
<< std::hex << static_cast<uint32_t>(offset) << std::dec
<< ": " << lastError << " - retrying...");
}
if (attempt < maxRetries - 1) {
Sleep(10); // Wait 10ms before retry
continue;
} else {
DEBUG_LOG(" FAILED after " << maxRetries << " attempts (offset 0x"
<< std::hex << static_cast<uint32_t>(offset) << std::dec
<< "): " << lastError);
throw;
}
} catch (...) {
lastError = "Unknown exception";
if (attempt == 0) {
DEBUG_LOG(" Hook failed with unknown exception on offset 0x"
<< std::hex << static_cast<uint32_t>(offset) << std::dec << " - retrying...");
// Try to get more info about the memory at this address
MEMORY_BASIC_INFORMATION mbi;
if (VirtualQuery(reinterpret_cast<LPCVOID>(offset), &mbi, sizeof(mbi))) {
DEBUG_LOG(" Memory info - Base: 0x" << std::hex << mbi.BaseAddress
<< " Size: " << mbi.RegionSize
<< " State: " << mbi.State
<< " Protect: " << mbi.Protect
<< " Type: " << mbi.Type << std::dec);
}
}
if (attempt < maxRetries - 1) {
Sleep(10); // Wait 10ms before retry
continue;
} else {
DEBUG_LOG(" FAILED after " << maxRetries << " attempts (offset 0x"
<< std::hex << static_cast<uint32_t>(offset) << std::dec
<< "): " << lastError);
throw;
}
}
}
// Shouldn't reach here, but just in case
HADESMEM_DETAIL_THROW_EXCEPTION(
hadesmem::Error{} << hadesmem::ErrorString{"Failed to create hook after all retry attempts"});
}
void initHooks() {
const hadesmem::Process process(::GetCurrentProcessId());
DEBUG_LOG("=== Starting hook initialization ===");
gSetCVarDetour = createHook<SetCVarT>(process, Offsets::Script_SetCVar, &Script_SetCVarHook);
gCGSpellBook_CastSpellDetour = createHook<CGSpellBook_CastSpellT>(
process, Offsets::CGSpellBook_CastSpell, &CGSpellBook_CastSpellHook);
gCGActionBar_UseActionDetour = createHook<CGActionBar_UseActionT>(
process, Offsets::CGActionBar_UseAction, &CGActionBar_UseActionHook);
gCastDetour = createHook<Spell_C_CastSpellT>(process, Offsets::Spell_C_CastSpell, &Spell_C_CastSpellHook);
gSendCastDetour = createHook<SendCastT>(process, Offsets::SendCast, &SendCastHook);
gCancelSpellDetour = createHook<CancelSpellT>(process, Offsets::CancelSpell, &CancelSpellHook);
gCastResultHandlerDetour = createHook<PacketHandlerT>(process, Offsets::CastResultHandler,
&CastResultHandlerHook);
gSpellStartHandlerDetour = createHook<FastCallPacketHandlerT>(process, Offsets::SpellStartHandler,
&SpellStartHandlerHook);
gPeriodicAuraLogHandlerDetour = createHook<FastCallPacketHandlerT>(process, Offsets::PeriodicAuraLogHandler,
&PeriodicAuraLogHandlerHook);
gSpellNonMeleeDmgLogHandlerDetour = createHook<FastCallPacketHandlerT>(process,
Offsets::SpellNonMeleeDmgLogHandler,
&SpellNonMeleeDmgLogHandlerHook);
gSpellChannelStartHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellChannelStartHandler,
&SpellChannelStartHandlerHook);
gSpellChannelUpdateHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellChannelUpdateHandler,
&SpellChannelUpdateHandlerHook);
gSpellHealingLogHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellHealingLogHandler,
&SpellHealingLogHandlerHook);
gSpellEnergizeLogHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellEnergizeLogHandler,
&SpellEnergizeLogHandlerHook);
gProcResistHandlerDetour = createHook<PacketHandlerT>(process, Offsets::ProcResistHandler,
&ProcResistHandlerHook);
gSpellLogMissHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellLogMissHandler,
&SpellLogMissHandlerHook);
gSpellOrDamageImmuneHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellOrDamageImmuneHandler,
&SpellOrDamageImmuneHandlerHook);
gSpellFailedOtherHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellFailedOtherHandler,
&SpellFailedOtherHandlerHook);
gSpellFailedDetour = createHook<Spell_C_SpellFailedT>(process, Offsets::Spell_C_SpellFailed,
&Spell_C_SpellFailedHook);
gSpellGoDetour = createHook<SpellGoT>(process, Offsets::SpellGo, &SpellGoHook);
gSpellDelayedDetour = createHook<PacketHandlerT>(process, Offsets::SpellDelayed, &SpellDelayedHook);
gSpellTargetUnitDetour = createHook<LuaScriptT>(process, Offsets::Script_SpellTargetUnit,
&Script_SpellTargetUnitHook);
gSpellStopCastingDetour = createHook<LuaScriptT>(process, Offsets::Script_SpellStopCasting,
&Script_SpellStopCastingHook);
gSpell_C_TargetSpellDetour = createHook<Spell_C_TargetSpellT>(process, Offsets::Spell_C_TargetSpell,
&Spell_C_TargetSpellHook);
gSpell_C_HandleTerrainClickDetour = createHook<Spell_C_HandleTerrainClickT>(
process, Offsets::Spell_C_HandleTerrainClick,
&Spell_C_HandleTerrainClickHook);
gCGWorldFrame_OnLayerTrackTerrainDetour = createHook<CGWorldFrame_OnLayerTrackTerrainT>(
process, Offsets::CGWorldFrame_OnLayerTrackTerrain,
&CGWorldFrame_OnLayerTrackTerrainHook);
gOnSpriteRightClickDetour = createHook<OnSpriteRightClickT>(process, Offsets::OnSpriteRightClick,
OnSpriteRightClickHook);
gIEndSceneDetour = createHook<ISceneEndT>(process, Offsets::ISceneEndPtr, &ISceneEndHook);
gInvalidFunctionPtrCheckDetour = createHook<InvalidFunctionPtrCheckT>(process, Offsets::InvalidFunctionPtrCheck,
&InvalidFunctionPtrCheckHook);
gGetSpellSlotFromLuaDetour = createHook<GetSpellSlotFromLuaT>(process, Offsets::GetSpellSlotFromLua,
&GetSpellSlotFromLuaHook);
gCGUnit_C_OnAuraRemovedDetour = createHook<CGUnit_C_OnAuraRemovedT>(process, Offsets::CGUnit_C_OnAuraRemoved,
&CGUnit_C_OnAuraRemovedHook);
gCGUnit_C_OnAuraAddedDetour = createHook<CGUnit_C_OnAuraAddedT>(process, Offsets::CGUnit_C_OnAuraAdded,
&CGUnit_C_OnAuraAddedHook);
gCGUnit_C_OnAuraStacksChangedDetour = createHook<CGUnit_C_OnAuraStacksChangedT>(
process, Offsets::CGUnit_C_OnAuraStacksChanged,
&CGUnit_C_OnAuraStacksChangedHook);
gUnitCombatLogUnitDeadDetour = createHook<UnitCombatLogUnitDeadT>(process, Offsets::UnitCombatLogUnitDead,
&UnitCombatLogUnitDeadHook);
gCGBuffBar_UpdateDurationDetour = createHook<CGBuffBar_UpdateDurationT>(process, Offsets::CGBuffBar_UpdateDuration,
&CGBuffBar_UpdateDurationHook);
gAttackRoundInfo_ReadPacketDetour = createHook<AttackRoundInfo_ReadPacketT>(process, Offsets::AttackRoundInfo_ReadPacket,
&AttackRoundInfo_ReadPacketHook);
gCSimpleFrame_GetNameDetour = createHook<LuaScriptT>(process, Offsets::CSimpleFrame_GetName,
&CSimpleFrame_GetNameHook);
gLoadScriptFunctionsDetour = createHook<LoadScriptFunctionsT>(process, Offsets::LoadScriptFunctions,
&LoadScriptFunctionsHook);
gCreateEventsDetour = createHook<FrameScript_CreateEventsT>(process, Offsets::FrameScript_CreateEvents,
&FrameScript_CreateEventsHook);
gSetEventCountDetour = createHook<FramescriptSetEventCountT>(process, Offsets::Framescript_SetEventCount,
&Framescript_SetEventCountHook);
DEBUG_LOG("=== All hooks initialized successfully ===");
}
void SysMsgInitializeHook(hadesmem::PatchDetourBase *detour) {
auto const sysMsgInitialize = detour->GetTrampolineT<SysMsgInitializeT>();
sysMsgInitialize();
std::call_once(initHooksFlag, []() {
loadConfig();
initHooks();
});
}
void addCustomEvent(uint32_t code, char *name) {
char **FrameScript_EventObject_Data = *reinterpret_cast<char ***>(Offsets::Framescript_EventObject_Data);
auto SStrDupA = reinterpret_cast<char* (__stdcall *)(char *, char *, int)>(Offsets::SStrDupA);
FrameScript_EventObject_Data[code * 4] = SStrDupA(name, name, 1308);
DEBUG_LOG("Added custom event code:" << code << " " << name);
}
void initCustomEvents() {
char SPELL_QUEUE_EVENT[] = "SPELL_QUEUE_EVENT";
addCustomEvent(game::SPELL_QUEUE_EVENT, SPELL_QUEUE_EVENT);
char SPELL_CAST_EVENT[] = "SPELL_CAST_EVENT";
addCustomEvent(game::SPELL_CAST_EVENT, SPELL_CAST_EVENT);
char SPELL_DAMAGE_EVENT_SELF[] = "SPELL_DAMAGE_EVENT_SELF";
addCustomEvent(game::SPELL_DAMAGE_EVENT_SELF, SPELL_DAMAGE_EVENT_SELF);
char SPELL_DAMAGE_EVENT_OTHER[] = "SPELL_DAMAGE_EVENT_OTHER";
addCustomEvent(game::SPELL_DAMAGE_EVENT_OTHER, SPELL_DAMAGE_EVENT_OTHER);
char DEBUFF_ADDED_SELF[] = "DEBUFF_ADDED_SELF";
addCustomEvent(game::DEBUFF_ADDED_SELF, DEBUFF_ADDED_SELF);
char DEBUFF_REMOVED_SELF[] = "DEBUFF_REMOVED_SELF";
addCustomEvent(game::DEBUFF_REMOVED_SELF, DEBUFF_REMOVED_SELF);
char DEBUFF_ADDED_OTHER[] = "DEBUFF_ADDED_OTHER";
addCustomEvent(game::DEBUFF_ADDED_OTHER, DEBUFF_ADDED_OTHER);
char DEBUFF_REMOVED_OTHER[] = "DEBUFF_REMOVED_OTHER";
addCustomEvent(game::DEBUFF_REMOVED_OTHER, DEBUFF_REMOVED_OTHER);
char BUFF_ADDED_SELF[] = "BUFF_ADDED_SELF";
addCustomEvent(game::BUFF_ADDED_SELF, BUFF_ADDED_SELF);
char BUFF_REMOVED_SELF[] = "BUFF_REMOVED_SELF";
addCustomEvent(game::BUFF_REMOVED_SELF, BUFF_REMOVED_SELF);
char BUFF_ADDED_OTHER[] = "BUFF_ADDED_OTHER";
addCustomEvent(game::BUFF_ADDED_OTHER, BUFF_ADDED_OTHER);
char BUFF_REMOVED_OTHER[] = "BUFF_REMOVED_OTHER";
addCustomEvent(game::BUFF_REMOVED_OTHER, BUFF_REMOVED_OTHER);
char UNIT_DIED[] = "UNIT_DIED";
addCustomEvent(game::UNIT_DIED, UNIT_DIED);
char AURA_CAST_ON_SELF[] = "AURA_CAST_ON_SELF";
addCustomEvent(game::AURA_CAST_ON_SELF, AURA_CAST_ON_SELF);
char AURA_CAST_ON_OTHER[] = "AURA_CAST_ON_OTHER";
addCustomEvent(game::AURA_CAST_ON_OTHER, AURA_CAST_ON_OTHER);
char AUTO_ATTACK_SELF[] = "AUTO_ATTACK_SELF";
addCustomEvent(game::AUTO_ATTACK_SELF, AUTO_ATTACK_SELF);
char AUTO_ATTACK_OTHER[] = "AUTO_ATTACK_OTHER";
addCustomEvent(game::AUTO_ATTACK_OTHER, AUTO_ATTACK_OTHER);
char SPELL_START_SELF[] = "SPELL_START_SELF";
addCustomEvent(game::SPELL_START_SELF, SPELL_START_SELF);
char SPELL_START_OTHER[] = "SPELL_START_OTHER";
addCustomEvent(game::SPELL_START_OTHER, SPELL_START_OTHER);
char SPELL_GO_SELF[] = "SPELL_GO_SELF";
addCustomEvent(game::SPELL_GO_SELF, SPELL_GO_SELF);
char SPELL_GO_OTHER[] = "SPELL_GO_OTHER";
addCustomEvent(game::SPELL_GO_OTHER, SPELL_GO_OTHER);
char SPELL_FAILED_SELF[] = "SPELL_FAILED_SELF";
addCustomEvent(game::SPELL_FAILED_SELF, SPELL_FAILED_SELF);
char SPELL_FAILED_OTHER[] = "SPELL_FAILED_OTHER";
addCustomEvent(game::SPELL_FAILED_OTHER, SPELL_FAILED_OTHER);
char SPELL_DELAYED_SELF[] = "SPELL_DELAYED_SELF";
addCustomEvent(game::SPELL_DELAYED_SELF, SPELL_DELAYED_SELF);
char SPELL_DELAYED_OTHER[] = "SPELL_DELAYED_OTHER";
addCustomEvent(game::SPELL_DELAYED_OTHER, SPELL_DELAYED_OTHER);
char SPELL_CHANNEL_START[] = "SPELL_CHANNEL_START";
addCustomEvent(game::SPELL_CHANNEL_START, SPELL_CHANNEL_START);
char SPELL_CHANNEL_UPDATE[] = "SPELL_CHANNEL_UPDATE";
addCustomEvent(game::SPELL_CHANNEL_UPDATE, SPELL_CHANNEL_UPDATE);
char SPELL_HEAL_BY_SELF[] = "SPELL_HEAL_BY_SELF";
addCustomEvent(game::SPELL_HEAL_BY_SELF, SPELL_HEAL_BY_SELF);
char SPELL_HEAL_BY_OTHER[] = "SPELL_HEAL_BY_OTHER";
addCustomEvent(game::SPELL_HEAL_BY_OTHER, SPELL_HEAL_BY_OTHER);
char SPELL_HEAL_ON_SELF[] = "SPELL_HEAL_ON_SELF";
addCustomEvent(game::SPELL_HEAL_ON_SELF, SPELL_HEAL_ON_SELF);
char SPELL_ENERGIZE_BY_SELF[] = "SPELL_ENERGIZE_BY_SELF";
addCustomEvent(game::SPELL_ENERGIZE_BY_SELF, SPELL_ENERGIZE_BY_SELF);
char SPELL_ENERGIZE_BY_OTHER[] = "SPELL_ENERGIZE_BY_OTHER";
addCustomEvent(game::SPELL_ENERGIZE_BY_OTHER, SPELL_ENERGIZE_BY_OTHER);
char SPELL_ENERGIZE_ON_SELF[] = "SPELL_ENERGIZE_ON_SELF";
addCustomEvent(game::SPELL_ENERGIZE_ON_SELF, SPELL_ENERGIZE_ON_SELF);
char BUFF_UPDATE_DURATION_SELF[] = "BUFF_UPDATE_DURATION_SELF";
addCustomEvent(game::BUFF_UPDATE_DURATION_SELF, BUFF_UPDATE_DURATION_SELF);
char DEBUFF_UPDATE_DURATION_SELF[] = "DEBUFF_UPDATE_DURATION_SELF";
addCustomEvent(game::DEBUFF_UPDATE_DURATION_SELF, DEBUFF_UPDATE_DURATION_SELF);
char SPELL_MISS_SELF[] = "SPELL_MISS_SELF";
addCustomEvent(game::SPELL_MISS_SELF, SPELL_MISS_SELF);
char SPELL_MISS_OTHER[] = "SPELL_MISS_OTHER";
addCustomEvent(game::SPELL_MISS_OTHER, SPELL_MISS_OTHER);
}
void FrameScript_CreateEventsHook(hadesmem::PatchDetourBase *detour, int param_1, uint32_t maxEventId) {
auto const createEvents = detour->GetTrampolineT<FrameScript_CreateEventsT>();
createEvents(param_1, maxEventId);
if (maxEventId > 200) {
initCustomEvents();
}
}
void Framescript_SetEventCountHook(hadesmem::PatchDetourBase *detour, void *thisPtr, void *dummy_edx,
uint32_t count) {
auto const setEventCount = detour->GetTrampolineT<FramescriptSetEventCountT>();
if (count > 200) {
DEBUG_LOG("Increasing event count from " << count << " to 700");
count = 700; // extend to support additional custom events (this matches how superwow does it)
}
setEventCount(thisPtr, dummy_edx, count);
}
void LoadScriptFunctionsHook(hadesmem::PatchDetourBase *detour) {
auto const loadScriptFunctions = detour->GetTrampolineT<LoadScriptFunctionsT>();
loadScriptFunctions();
CleanupAllLuaTableRefs();
ClearItemCaches();
// register our own lua functions
DEBUG_LOG("Registering Custom Lua functions");
char queueSpellByName[] = "QueueSpellByName";
RegisterLuaFunction(queueSpellByName, reinterpret_cast<uintptr_t *>(Script_QueueSpellByName));
char castSpellByNameNoQueue[] = "CastSpellByNameNoQueue";
RegisterLuaFunction(castSpellByNameNoQueue, reinterpret_cast<uintptr_t *>(Script_CastSpellByNameNoQueue));
char queueScript[] = "QueueScript";
RegisterLuaFunction(queueScript, reinterpret_cast<uintptr_t *>(Script_QueueScript));
char isSpellInRange[] = "IsSpellInRange";
RegisterLuaFunction(isSpellInRange, reinterpret_cast<uintptr_t *>(Script_IsSpellInRange));
char isSpellUsable[] = "IsSpellUsable";
RegisterLuaFunction(isSpellUsable, reinterpret_cast<uintptr_t *>(Script_IsSpellUsable));
char getCurrentCastingInfo[] = "GetCurrentCastingInfo";
RegisterLuaFunction(getCurrentCastingInfo, reinterpret_cast<uintptr_t *>(Script_GetCurrentCastingInfo));
char getSpellIdForName[] = "GetSpellIdForName";
RegisterLuaFunction(getSpellIdForName, reinterpret_cast<uintptr_t *>(Script_GetSpellIdForName));
char getSpellNameAndRankForId[] = "GetSpellNameAndRankForId";
RegisterLuaFunction(getSpellNameAndRankForId, reinterpret_cast<uintptr_t *>(Script_GetSpellNameAndRankForId));
char getSpellSlotTypeIdForName[] = "GetSpellSlotTypeIdForName";
RegisterLuaFunction(getSpellSlotTypeIdForName, reinterpret_cast<uintptr_t *>(Script_GetSpellSlotTypeIdForName));
char channelStopCastingNextTick[] = "ChannelStopCastingNextTick";
RegisterLuaFunction(channelStopCastingNextTick,
reinterpret_cast<uintptr_t *>(Script_ChannelStopCastingNextTick));
char getNampowerVersion[] = "GetNampowerVersion";
RegisterLuaFunction(getNampowerVersion, reinterpret_cast<uintptr_t *>(Script_GetNampowerVersion));
char getItemILevel[] = "GetItemLevel";
RegisterLuaFunction(getItemILevel, reinterpret_cast<uintptr_t *>(Script_GetItemLevel));
// 2.14 additions
char getItemStats[] = "GetItemStats";
RegisterLuaFunction(getItemStats, reinterpret_cast<uintptr_t *>(Script_GetItemStats));
char startItemExport[] = "StartItemExport";
RegisterLuaFunction(startItemExport, reinterpret_cast<uintptr_t *>(Script_StartItemExport));
char getSpellRec[] = "GetSpellRec";
RegisterLuaFunction(getSpellRec, reinterpret_cast<uintptr_t *>(Script_GetSpellRec));
char getItemStatsField[] = "GetItemStatsField";
RegisterLuaFunction(getItemStatsField, reinterpret_cast<uintptr_t *>(Script_GetItemStatsField));
char getSpellRecField[] = "GetSpellRecField";
RegisterLuaFunction(getSpellRecField, reinterpret_cast<uintptr_t *>(Script_GetSpellRecField));
char getUnitData[] = "GetUnitData";
RegisterLuaFunction(getUnitData, reinterpret_cast<uintptr_t *>(Script_GetUnitData));
char getUnitField[] = "GetUnitField";
RegisterLuaFunction(getUnitField, reinterpret_cast<uintptr_t *>(Script_GetUnitField));
char getSpellModifiers[] = "GetSpellModifiers";
RegisterLuaFunction(getSpellModifiers, reinterpret_cast<uintptr_t *>(Script_GetSpellModifiers));
// 2.16 additions
char findPlayerItemSlot[] = "FindPlayerItemSlot";
RegisterLuaFunction(findPlayerItemSlot, reinterpret_cast<uintptr_t *>(Script_FindPlayerItemSlot));
char getEquippedItems[] = "GetEquippedItems";
RegisterLuaFunction(getEquippedItems, reinterpret_cast<uintptr_t *>(Script_GetEquippedItems));
char getEquippedItem[] = "GetEquippedItem";
RegisterLuaFunction(getEquippedItem, reinterpret_cast<uintptr_t *>(Script_GetEquippedItem));
char getBagItems[] = "GetBagItems";
RegisterLuaFunction(getBagItems, reinterpret_cast<uintptr_t *>(Script_GetBagItems));
char getBagItem[] = "GetBagItem";
RegisterLuaFunction(getBagItem, reinterpret_cast<uintptr_t *>(Script_GetBagItem));
// 2.17 additions
char GetCastInfo[] = "GetCastInfo";
RegisterLuaFunction(GetCastInfo, reinterpret_cast<uintptr_t *>(Script_GetCastInfo));
char getSpellIdCooldown[] = "GetSpellIdCooldown";
RegisterLuaFunction(getSpellIdCooldown, reinterpret_cast<uintptr_t *>(Script_GetSpellIdCooldown));
char getItemIdCooldown[] = "GetItemIdCooldown";
RegisterLuaFunction(getItemIdCooldown, reinterpret_cast<uintptr_t *>(Script_GetItemIdCooldown));
char getTrinketCooldown[] = "GetTrinketCooldown";
RegisterLuaFunction(getTrinketCooldown, reinterpret_cast<uintptr_t *>(Script_GetTrinketCooldown));
// 2.19 additions
char useItemIdOrName[] = "UseItemIdOrName";
RegisterLuaFunction(useItemIdOrName, reinterpret_cast<uintptr_t *>(Script_UseItemIdOrName));
char useTrinket[] = "UseTrinket";
RegisterLuaFunction(useTrinket, reinterpret_cast<uintptr_t *>(Script_UseTrinket));
char getTrinkets[] = "GetTrinkets";
RegisterLuaFunction(getTrinkets, reinterpret_cast<uintptr_t *>(Script_GetTrinkets));
char disenchantAll[] = "DisenchantAll";
RegisterLuaFunction(disenchantAll, reinterpret_cast<uintptr_t *>(Script_DisenchantAll));
char getItemIconTexture[] = "GetItemIconTexture";
RegisterLuaFunction(getItemIconTexture, reinterpret_cast<uintptr_t *>(Script_GetItemIconTexture));
char getSpellIconTexture[] = "GetSpellIconTexture";
RegisterLuaFunction(getSpellIconTexture, reinterpret_cast<uintptr_t *>(Script_GetSpellIconTexture));
char getAmmo[] = "GetAmmo";
RegisterLuaFunction(getAmmo, reinterpret_cast<uintptr_t *>(Script_GetAmmo));
char getPlayerAuraDuration[] = "GetPlayerAuraDuration";
RegisterLuaFunction(getPlayerAuraDuration, reinterpret_cast<uintptr_t *>(Script_GetPlayerAuraDuration));
char cancelPlayerAuraSlot[] = "CancelPlayerAuraSlot";
RegisterLuaFunction(cancelPlayerAuraSlot, reinterpret_cast<uintptr_t *>(Script_CancelPlayerAuraSlot));
char cancelPlayerAuraSpellId[] = "CancelPlayerAuraSpellId";
RegisterLuaFunction(cancelPlayerAuraSpellId, reinterpret_cast<uintptr_t *>(Script_CancelPlayerAuraSpellId));
char getSpellPower[] = "GetSpellPower";
RegisterLuaFunction(getSpellPower, reinterpret_cast<uintptr_t *>(Script_GetSpellPower));
}
void load() {
std::call_once(loadFlag, []() {
// hook SysMsgInitialize (bootstrap hook)
const hadesmem::Process process(::GetCurrentProcessId());
auto const sysMsgInitOrig = hadesmem::detail::AliasCast<SysMsgInitializeT>(
Offsets::SysMsgInitialize);
gSysMsgInitDetour = std::make_unique<hadesmem::PatchDetour<SysMsgInitializeT> >(
process,
sysMsgInitOrig,
&SysMsgInitializeHook);
gSysMsgInitDetour->Apply();
}
);
}
}
extern "C" __declspec(dllexport) uint32_t Load() {
try {
Nampower::load();
} catch (const std::exception &e) {
std::string errorMsg = "Exception in Nampower::load(): ";
errorMsg += e.what();
MessageBoxA(nullptr, errorMsg.c_str(), "Nampower Error", MB_OK | MB_ICONERROR);
return EXIT_FAILURE;
} catch (...) {
MessageBoxA(nullptr, "Unknown exception in Nampower::load()", "Nampower Error", MB_OK | MB_ICONERROR);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}