Files
nampower/nampower/spellchannel.cpp
T

176 lines
7.2 KiB
C++

//
// Created by pmacc on 9/21/2024.
//
#include "spellchannel.hpp"
#include "spellcast.hpp"
#include "logging.hpp"
#include "offsets.hpp"
#include "helper.hpp"
namespace Nampower {
void TriggerSpellChannelStartEvent(uint32_t spellId, uint32_t duration) {
static char format[] = "%d%s%d";
auto playerGuid = game::ClntObjMgrGetActivePlayerGuid();
auto playerUnit = game::GetObjectPtr(playerGuid);
auto unitTargetGuid = game::UnitGetTargetGuid(playerUnit); // channel target guid gets updated later
char *targetGuidStr = ConvertGuidToString(unitTargetGuid);
((int (__cdecl *)(int eventCode,
char *fmt,
uint32_t spellIdParam,
char *targetGuidParam,
uint32_t durationParam)) Offsets::SignalEventParam)(
game::SPELL_CHANNEL_START,
format,
spellId,
targetGuidStr,
duration);
FreeGuidString(targetGuidStr);
}
void TriggerSpellChannelUpdateEvent(uint32_t spellId, uint32_t channelRemainingTime) {
static char format[] = "%d%s%d";
auto channelTargetGuid = *reinterpret_cast<uint64_t *>(Offsets::ChannelTargetGuid);
char *targetGuidStr = ConvertGuidToString(channelTargetGuid);
((int (__cdecl *)(int eventCode,
char *fmt,
uint32_t spellIdParam,
char *targetGuidParam,
uint32_t remainingParam)) Offsets::SignalEventParam)(
game::SPELL_CHANNEL_UPDATE,
format,
spellId,
targetGuidStr,
channelRemainingTime);
FreeGuidString(targetGuidStr);
}
int SpellChannelStartHandlerHook(hadesmem::PatchDetourBase *detour, uint32_t *opCode, CDataStore *packet) {
auto const rpos = packet->m_read;
uint32_t spellId;
packet->Get(spellId);
uint32_t duration;
packet->Get(duration);
packet->m_read = rpos;
DEBUG_LOG("Channel start: " << game::GetSpellName(spellId) << " duration " << duration);
if (duration > 0) {
// check if spell has "Far sight" flag which is used on mind control style abilities
auto spell = game::GetSpellInfo(spellId);
if (spell && !(spell->AttributesEx & game::SPELL_ATTR_EX_TOGGLE_FARSIGHT ||
spellId == 19832 || // bwl orb spell
spellId == 23014 || // bwl orb spell
spellId == 13180) // mind control cap
) {
auto currentTimeMs = GetTime();
gCastData.channeling = true;
gCastData.channelStartMs = currentTimeMs;
gCastData.channelEndMs = currentTimeMs + duration;
gCastData.channelSpellId = spellId;
gCastData.channelDuration = duration;
auto originalDuration = game::GetDurationObject(spell->DurationIndex)->m_Duration;
float durationReduction = 1.0f;
// if this was an Arcane Missiles cast, we need to check if we have the +1 aura
if (spellId == 5143 ||
spellId == 5144 ||
spellId == 5145 ||
spellId == 8416 ||
spellId == 8417 ||
spellId == 10211 ||
spellId == 10212 ||
spellId == 25345) {
// check if we have duration mod > 0
auto durationModifier = game::GetSpellModifier(spell, game::SPELLMOD_DURATION);
if (durationModifier > 0) {
DEBUG_LOG("Adding 1 second to Arcane Missiles duration due to duration mod " << durationModifier);
originalDuration += 1000;
}
}
if (originalDuration > 0 && originalDuration < 1000000) {
durationReduction = float(duration) / float(originalDuration);
} else {
DEBUG_LOG("Invalid originalDuration of " << originalDuration << " for "
<< game::GetSpellName(spellId));
}
uint32_t amplitude = spell->EffectAmplitude[0];
if (amplitude <= 0 || amplitude > duration) {
amplitude = spell->EffectAmplitude[1];
}
if (amplitude <= 0 || amplitude > duration) {
amplitude = spell->EffectAmplitude[2];
}
gCastData.channelTickTimeMs = uint32_t(float(amplitude) *
durationReduction); // the base tick time is usually on the first effect, scale it based on the duration reduction due to haste mechanics
DEBUG_LOG("Original tick time " << amplitude << " scaled tick time "
<< gCastData.channelTickTimeMs);
if (gCastData.channelTickTimeMs <= 0 || gCastData.channelTickTimeMs > duration) {
DEBUG_LOG("Invalid channel tick time for " << game::GetSpellName(spellId)
<< ", defaulting to duration");
gCastData.channelTickTimeMs = duration;
}
gCastData.channelNumTicks = 0;
gLastCastData.channelStartTimeMs = currentTimeMs;
} else {
DEBUG_LOG("Ignoring/resetting channeling for " << game::GetSpellName(spellId));
ResetChannelingFlags();
}
} else {
ResetChannelingFlags();
}
auto const spellChannelStartHandler = detour->GetTrampolineT<PacketHandlerT>();
auto ret = spellChannelStartHandler(opCode, packet);
TriggerSpellChannelStartEvent(spellId, duration);
return ret;
}
// this gets called on damage and when channel ends with the end time of the channel
int SpellChannelUpdateHandlerHook(hadesmem::PatchDetourBase *detour, uint32_t *opCode, CDataStore *packet) {
auto const rpos = packet->m_read;
uint32_t channelRemainingTime;
packet->Get(channelRemainingTime);
packet->m_read = rpos;
TriggerSpellChannelUpdateEvent(gCastData.channelSpellId, channelRemainingTime);
if (channelRemainingTime <= 0) {
DEBUG_LOG("Channel done: " << game::GetSpellName(gCastData.channelSpellId)
<< " elapsed " << (GetTime() - gLastCastData.channelStartTimeMs)
<< " original duration " << gCastData.channelDuration);
ResetChannelingFlags();
} else {
DEBUG_LOG("Channel update: " << game::GetSpellName(gCastData.channelSpellId) << " remaining "
<< channelRemainingTime);
gCastData.channelEndMs = GetTime() + channelRemainingTime;
}
auto const spellChannelUpdateHandler = detour->GetTrampolineT<PacketHandlerT>();
return spellChannelUpdateHandler(opCode, packet);
}
}