mirror of
https://github.com/brues-code/nampower.git
synced 2026-10-05 05:00:02 +00:00
Add KEY_UP/DOWN events, implement GetGUIDFromName support for owner/target/pet/mark, bug fixes
This commit is contained in:
@@ -44,6 +44,7 @@ For custom Lua functions, see [SCRIPTS.md](SCRIPTS.md). For general usage inform
|
||||
- [SPELL_MISS_SELF and SPELL_MISS_OTHER](#spell_miss_self-and-spell_miss_other)
|
||||
- [UNIT_DIED](#unit_died)
|
||||
- [Unit GUID Events](#unit-guid-events)
|
||||
- [KEY_DOWN and KEY_UP](#key_down-and-key_up)
|
||||
|
||||
---
|
||||
|
||||
@@ -575,6 +576,128 @@ frame:RegisterEvent("UNIT_DIED", function(guid)
|
||||
end)
|
||||
```
|
||||
|
||||
### KEY_DOWN and KEY_UP
|
||||
Fire when keyboard input is processed by `CSimpleTop::OnKeyDown` / `CSimpleTop::OnKeyUp`. You could accomplish similar already
|
||||
by calling EnableKeyBoard and using 'OnKeyDown' / 'OnKeyUp' scripts but that blocks all keyboard input to the game. You could also poll for IsShiftKeyDown, IsAltKeyDown, in OnUpdate but that was also limiting.
|
||||
|
||||
Parameters:
|
||||
1. int key
|
||||
2. int metaKeyState
|
||||
3. int repeat
|
||||
4. int time
|
||||
|
||||
Note: `repeat` currently appears to stay constant in testing and may not reflect real key repeat count/state.
|
||||
|
||||
`metaKeyState` bitmask:
|
||||
- Shift = `1`
|
||||
- Ctrl = `2`
|
||||
- Alt = `4`
|
||||
|
||||
```c
|
||||
typedef enum KEY {
|
||||
KEY_NONE=-1,
|
||||
KEY_SHIFT=0,
|
||||
KEY_CONTROL=1,
|
||||
KEY_ALT=2,
|
||||
KEY_LASTMETAKEY=2,
|
||||
KEY_SPACE=32,
|
||||
KEY_0=48,
|
||||
KEY_1=49,
|
||||
KEY_2=50,
|
||||
KEY_3=51,
|
||||
KEY_4=52,
|
||||
KEY_5=53,
|
||||
KEY_6=54,
|
||||
KEY_7=55,
|
||||
KEY_8=56,
|
||||
KEY_9=57,
|
||||
KEY_A=65,
|
||||
KEY_B=66,
|
||||
KEY_C=67,
|
||||
KEY_D=68,
|
||||
KEY_E=69,
|
||||
KEY_F=70,
|
||||
KEY_G=71,
|
||||
KEY_H=72,
|
||||
KEY_I=73,
|
||||
KEY_J=74,
|
||||
KEY_K=75,
|
||||
KEY_L=76,
|
||||
KEY_M=77,
|
||||
KEY_N=78,
|
||||
KEY_O=79,
|
||||
KEY_P=80,
|
||||
KEY_Q=81,
|
||||
KEY_R=82,
|
||||
KEY_S=83,
|
||||
KEY_T=84,
|
||||
KEY_U=85,
|
||||
KEY_V=86,
|
||||
KEY_W=87,
|
||||
KEY_X=88,
|
||||
KEY_Y=89,
|
||||
KEY_Z=90,
|
||||
KEY_TILDE=256,
|
||||
KEY_NUMPAD0=257,
|
||||
KEY_NUMPAD1=258,
|
||||
KEY_NUMPAD2=259,
|
||||
KEY_NUMPAD3=260,
|
||||
KEY_NUMPAD4=261,
|
||||
KEY_NUMPAD5=262,
|
||||
KEY_NUMPAD6=263,
|
||||
KEY_NUMPAD7=264,
|
||||
KEY_NUMPAD8=265,
|
||||
KEY_NUMPAD9=266,
|
||||
KEY_NUMPAD_PLUS=267,
|
||||
KEY_NUMPAD_MINUS=268,
|
||||
KEY_NUMPAD_MULTIPLY=269,
|
||||
KEY_NUMPAD_DIVIDE=270,
|
||||
KEY_NUMPAD_DECIMAL=271,
|
||||
KEY_PLUS=272,
|
||||
KEY_MINUS=273,
|
||||
KEY_BRACKET_OPEN=274,
|
||||
KEY_BRACKET_CLOSE=275,
|
||||
KEY_SLASH=276,
|
||||
KEY_BACKSLASH=277,
|
||||
KEY_SEMICOLON=278,
|
||||
KEY_APOSTROPHE=279,
|
||||
KEY_COMMA=280,
|
||||
KEY_PERIOD=281,
|
||||
KEY_ESCAPE=512,
|
||||
KEY_ENTER=513,
|
||||
KEY_BACKSPACE=514,
|
||||
KEY_TAB=515,
|
||||
KEY_LEFT=516,
|
||||
KEY_UP=517,
|
||||
KEY_RIGHT=518,
|
||||
KEY_DOWN=519,
|
||||
KEY_INSERT=520,
|
||||
KEY_DELETE=521,
|
||||
KEY_HOME=522,
|
||||
KEY_END=523,
|
||||
KEY_PAGEUP=524,
|
||||
KEY_PAGEDOWN=525,
|
||||
KEY_CAPSLOCK=526,
|
||||
KEY_NUMLOCK=527,
|
||||
KEY_SCROLLLOCK=528,
|
||||
KEY_PAUSE=529,
|
||||
KEY_PRINTSCREEN=530,
|
||||
KEY_F1=768,
|
||||
KEY_F2=769,
|
||||
KEY_F3=770,
|
||||
KEY_F4=771,
|
||||
KEY_F5=772,
|
||||
KEY_F6=773,
|
||||
KEY_F7=774,
|
||||
KEY_F8=775,
|
||||
KEY_F9=776,
|
||||
KEY_F10=777,
|
||||
KEY_F11=778,
|
||||
KEY_F12=779,
|
||||
KEY_LAST=780
|
||||
} KEY;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Unit GUID Events
|
||||
|
||||
@@ -142,6 +142,31 @@ Examples:
|
||||
/run print(GetSpellTexture("Fireball")) -- name search
|
||||
```
|
||||
|
||||
### Unit Token Extensions (`UnitGUID` + all `unitToken`/`target` string params)
|
||||
`UnitGUID(unitToken)` now uses the same extended unit-token parser used by Nampower's unit-string APIs.
|
||||
In practice, any Lua function documented as taking a unit string (`unitToken`, `target`, etc.) can use the formats below.
|
||||
|
||||
Supported formats:
|
||||
- Standard unit tokens supported by the client parser (`player`, `target`, `pet`, `mouseover`, `party1`, `raid1`, etc.)
|
||||
- Raw GUID strings: `0x` + 16 hex digits (example: `0xF5300000000000A5`)
|
||||
- Raid target markers: `mark1` through `mark8`
|
||||
- Derived-unit suffixes appended directly to a base unit string (no separator): `<base>owner`, `<base>target`, `<base>pet`
|
||||
|
||||
How base resolution works:
|
||||
- `<base>` can be a normal unit token (`targetowner`, `party1pet`, etc.)
|
||||
- `<base>` can be a GUID string (`0xF5300000000000A5owner`, `0xF5300000000000A5target`, `0xF5300000000000A5pet`)
|
||||
- `<base>` can be a raid marker (`mark1target`, `mark8owner`, etc.)
|
||||
- Suffix matching is case-insensitive (`targetOwner`, `mark1PET`, etc.)
|
||||
|
||||
Examples:
|
||||
```
|
||||
/run print(UnitGUID("mark1"))
|
||||
/run print(UnitGUID("mark1target"))
|
||||
/run print(UnitGUID("targetowner"))
|
||||
/run print(UnitGUID("party1pet"))
|
||||
/run print(UnitGUID("0xF5300000000000A5target"))
|
||||
```
|
||||
|
||||
|
||||
### CSimpleFrame:GetName() Enhancement
|
||||
The built-in `CSimpleFrame:GetName()` method is hooked to support an optional argument for retrieving the GUID of the unit associated with a nameplate frame.
|
||||
@@ -176,7 +201,7 @@ Valid ranges are: `talentPage` = `1-3`, `talentIndex` = `1-32`, `rank` = `1-5`.
|
||||
For unit data APIs, object-reference GUID fields are returned as strings (hex format) rather than numbers to avoid Lua 64-bit precision issues.
|
||||
This applies to both `GetUnitData` and `GetUnitField` for fields such as: `charm`, `summon`, `charmedBy`, `summonedBy`, `createdBy`, `target`, `persuaded`, and `channelObject`.
|
||||
|
||||
`CancelPlayerAuraSpellId(spellId, [ignoreMissing])` supports an optional second parameter where `1` skips the aura-slot presence check (useful for buff-capped cases) and `0`/omitted keeps the default check.
|
||||
`CancelPlayerAuraSpellId(spellId, [ignoreMissing])` supports an optional second parameter where `1` skips the aura-slot presence check (thought this might work when buff-capped but doesn't seem to work) and `0`/omitted keeps the default check.
|
||||
|
||||
Cooldown detail tables now also expose `itemId`, `itemHasActiveSpell`, and `itemActiveSpellId` alongside the existing per-category timing data.
|
||||
|
||||
@@ -198,6 +223,7 @@ Available events:
|
||||
- SPELL_HEAL_BY_SELF, SPELL_HEAL_BY_OTHER, and SPELL_HEAL_ON_SELF - Spell healing events (fires when spell heals are processed; includes target, caster, spell ID, amount, critical flag, and periodic flag). Set `NP_EnableSpellHealEvents=1` to enable. See EVENTS.md for details.
|
||||
- SPELL_ENERGIZE_BY_SELF, SPELL_ENERGIZE_BY_OTHER, and SPELL_ENERGIZE_ON_SELF - Spell energize events (fires when power is restored via spells; includes target, caster, spell ID, power type, amount, and periodic flag). Set `NP_EnableSpellEnergizeEvents=1` to enable. See EVENTS.md for details.
|
||||
- UNIT_DIED - Fires when a unit dies
|
||||
- KEY_DOWN and KEY_UP - Keyboard key events from `CSimpleTop::OnKeyDown` / `CSimpleTop::OnKeyUp` (see [EVENTS.md](EVENTS.md#key_down-and-key_up) for full argument and key-code details)
|
||||
|
||||
## Bug Reporting
|
||||
If you encounter any bugs please report them in the issues tab. Please include the nampower_debug.txt file in the same directory as your WoW.exe to help me diagnose the issue. If you are able to reproduce the bug please include the steps to reproduce it. In a future version once bugs are ironed out I'll make logging optional.
|
||||
|
||||
+39
@@ -32,6 +32,9 @@ For custom events, see [EVENTS.md](EVENTS.md). For installation, configuration,
|
||||
- [GetItemIconTexture](#getitemicontexturedisplayinfoid)
|
||||
- [GetSpellIconTexture](#getspellicontexturespelliconid)
|
||||
- [GetPlayerAuraDuration](#getplayerauradurationauraslot)
|
||||
- [CancelPlayerAuraSlot](#cancelplayerauraslotauraslot)
|
||||
- [CancelPlayerAuraSpellId](#cancelplayerauraspellidspellid-ignoremissing)
|
||||
- [LearnTalentRank](#learntalentranktalentpage-talentindex-rank)
|
||||
- [Spell Casting and Queuing](#spell-casting-and-queuing)
|
||||
- [QueueSpellByName](#queuespellbynamespellname)
|
||||
- [CastSpellByNameNoQueue](#castspellbynamenoqueuespellname)
|
||||
@@ -500,6 +503,39 @@ for slot = 32, 47 do
|
||||
end
|
||||
```
|
||||
|
||||
#### CancelPlayerAuraSpellId(spellId, [ignoreMissing])
|
||||
Cancels a player aura by spell ID.
|
||||
|
||||
**Parameters:**
|
||||
- `spellId` (number): Aura spell ID to cancel
|
||||
- `ignoreMissing` (number, optional): Pass `1` to skip the aura-slot presence check (useful for buff-capped cases). Pass `0` or omit to keep the default check.
|
||||
|
||||
**Returns:**
|
||||
- `1` if cancel was attempted
|
||||
- `0` if `spellId` is invalid or (when `ignoreMissing` is `0`/omitted) no matching aura is present
|
||||
|
||||
#### CancelPlayerAuraSlot(auraSlot)
|
||||
Cancels a player aura by raw aura slot index.
|
||||
|
||||
**Parameters:**
|
||||
- `auraSlot` (number): Aura slot index to cancel
|
||||
|
||||
**Returns:**
|
||||
- `1` if the slot contains an aura and cancel was attempted
|
||||
- `0` if the slot is invalid/out of range or no aura is present in that slot
|
||||
|
||||
#### LearnTalentRank(talentPage, talentIndex, rank)
|
||||
Learns a specific talent rank directly by tab/index.
|
||||
|
||||
**Parameters:**
|
||||
- `talentPage` (number): Valid range `1-3`
|
||||
- `talentIndex` (number): Valid range `1-32`
|
||||
- `rank` (number): Valid range `1-5`
|
||||
|
||||
**Returns:**
|
||||
- `1` on success
|
||||
- Raises a Lua error on invalid parameters or if the talent entry cannot be resolved
|
||||
|
||||
#### GetSpellRec(spellId, [copy])
|
||||
Returns a Lua table reference containing all fields for the spell's `SpellRec` record (including localized `name` and `rank`). Returns nil if the spell cannot be found.
|
||||
|
||||
@@ -650,6 +686,7 @@ Returns a Lua table reference containing all unit fields for the specified unit.
|
||||
|
||||
**Returns:**
|
||||
- A Lua table reference containing all unit fields, or nil if the unit cannot be found
|
||||
- Object-reference GUID fields are returned as hex GUID strings (not numbers) to avoid Lua 64-bit precision issues. This includes fields such as `charm`, `summon`, `charmedBy`, `summonedBy`, `createdBy`, `target`, `persuaded`, and `channelObject`.
|
||||
|
||||
Full field name lists are in [`UNIT_FIELDS.md`](UNIT_FIELDS.md).
|
||||
|
||||
@@ -678,6 +715,7 @@ Fast lookup for a single field on a unit. More efficient than GetUnitData when y
|
||||
**Returns:**
|
||||
- The requested field value; returns nil if the unit is not found; raises a Lua error if the field name is invalid
|
||||
- For array fields (like "aura", "resistances"), returns a Lua table with numeric indices
|
||||
- Object-reference GUID fields are returned as hex GUID strings (not numbers) to avoid Lua 64-bit precision issues. This includes fields such as `charm`, `summon`, `charmedBy`, `summonedBy`, `createdBy`, `target`, `persuaded`, and `channelObject`.
|
||||
|
||||
Full field name lists are in [`UNIT_FIELDS.md`](UNIT_FIELDS.md).
|
||||
|
||||
@@ -929,6 +967,7 @@ A Lua table where each entry contains:
|
||||
- `itemId` (number)
|
||||
- `trinketName` (string, `"Unknown"` if no name available)
|
||||
- `texture` (string): Texture name for the item icon
|
||||
- `itemLevel` (number): Item level
|
||||
- `bagIndex` (number|nil): `nil` when equipped; `0` for backpack; `1-4` for equipped bags
|
||||
- `slotIndex` (number): Lua 1-based slot within the container (or 1/2 for equipped trinket slots)
|
||||
|
||||
|
||||
+13227
-12937
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,29 @@
|
||||
#include <cstdint>
|
||||
|
||||
namespace game {
|
||||
namespace {
|
||||
// CGUnit_C descriptor pointer is stored at object + 0x110 in this client build.
|
||||
constexpr uintptr_t kUnitDescriptorPtrOffset = 0x110;
|
||||
constexpr uintptr_t kOwnerGuidOffset = 0x10;
|
||||
constexpr uintptr_t kOwnerGuidFallbackOffset = 0x20;
|
||||
constexpr uintptr_t kPetGuidOffset = 0x08;
|
||||
constexpr uintptr_t kTargetGuidOffset = 0x28;
|
||||
|
||||
uint8_t *GetUnitDescriptor(uintptr_t *unit) {
|
||||
if (!unit) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto descriptorPtr = *reinterpret_cast<uintptr_t *>(reinterpret_cast<uint8_t *>(unit) +
|
||||
kUnitDescriptorPtrOffset);
|
||||
if (descriptorPtr == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<uint8_t *>(descriptorPtr);
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t *GetObjectPtr(std::uint64_t guid) {
|
||||
uintptr_t *(__stdcall *getObjectPtr)(std::uint64_t) = hadesmem::detail::AliasCast<decltype(getObjectPtr)>(
|
||||
Offsets::GetObjectPtr);
|
||||
@@ -171,6 +194,65 @@ namespace game {
|
||||
return unitFields->target;
|
||||
}
|
||||
|
||||
uint64_t UnitGetOwnerGuid(uintptr_t *unit) {
|
||||
auto *desc = GetUnitDescriptor(unit);
|
||||
if (!desc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t low = *reinterpret_cast<uint32_t *>(desc + kOwnerGuidOffset);
|
||||
uint32_t high = *reinterpret_cast<uint32_t *>(desc + kOwnerGuidOffset + sizeof(uint32_t));
|
||||
if (low == 0 && high == 0) {
|
||||
low = *reinterpret_cast<uint32_t *>(desc + kOwnerGuidFallbackOffset);
|
||||
high = *reinterpret_cast<uint32_t *>(desc + kOwnerGuidFallbackOffset + sizeof(uint32_t));
|
||||
}
|
||||
|
||||
return (static_cast<uint64_t>(high) << 32) | low;
|
||||
}
|
||||
|
||||
uint64_t UnitGetPetGuid(uintptr_t *unit) {
|
||||
auto *desc = GetUnitDescriptor(unit);
|
||||
if (!desc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t low = *reinterpret_cast<uint32_t *>(desc + kPetGuidOffset);
|
||||
uint32_t high = *reinterpret_cast<uint32_t *>(desc + kPetGuidOffset + sizeof(uint32_t));
|
||||
return (static_cast<uint64_t>(high) << 32) | low;
|
||||
}
|
||||
|
||||
uint64_t UnitGetOwnerGuidForGuid(uint64_t guid) {
|
||||
auto *unit = ClntObjMgrObjectPtr(static_cast<TypeMask>(TYPEMASK_UNIT | TYPEMASK_PLAYER), guid);
|
||||
return UnitGetOwnerGuid(unit);
|
||||
}
|
||||
|
||||
uint64_t UnitGetTargetGuidForGuid(uint64_t guid) {
|
||||
auto *unit = ClntObjMgrObjectPtr(static_cast<TypeMask>(TYPEMASK_UNIT | TYPEMASK_PLAYER), guid);
|
||||
if (!unit) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t targetGuid = UnitGetTargetGuid(unit);
|
||||
if (targetGuid != 0) {
|
||||
return targetGuid;
|
||||
}
|
||||
|
||||
// Fallback to descriptor offsets used by legacy code paths.
|
||||
auto *desc = GetUnitDescriptor(unit);
|
||||
if (!desc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t low = *reinterpret_cast<uint32_t *>(desc + kTargetGuidOffset);
|
||||
uint32_t high = *reinterpret_cast<uint32_t *>(desc + kTargetGuidOffset + sizeof(uint32_t));
|
||||
return (static_cast<uint64_t>(high) << 32) | low;
|
||||
}
|
||||
|
||||
uint64_t UnitGetPetGuidForGuid(uint64_t guid) {
|
||||
auto *unit = ClntObjMgrObjectPtr(static_cast<TypeMask>(TYPEMASK_UNIT | TYPEMASK_PLAYER), guid);
|
||||
return UnitGetPetGuid(unit);
|
||||
}
|
||||
|
||||
bool UnitIsPvpFlagged(uintptr_t *unit) {
|
||||
if (!unit) {
|
||||
return false;
|
||||
|
||||
@@ -1635,6 +1635,10 @@ namespace game {
|
||||
UNIT_AURA_GUID = 595,
|
||||
UNIT_DYNAMIC_FLAGS_GUID = 596,
|
||||
PLAYER_GUILD_UPDATE_GUID = 597,
|
||||
KEY_DOWN = 598,
|
||||
KEY_UP = 599,
|
||||
|
||||
UNIT_CASTEVENT=600 // superwow
|
||||
};
|
||||
|
||||
enum TypeMask {
|
||||
@@ -1935,6 +1939,12 @@ namespace game {
|
||||
bool UnitCanAttackUnit(uintptr_t *unit1, uintptr_t *unit2);
|
||||
|
||||
uint64_t UnitGetTargetGuid(uintptr_t *unit);
|
||||
uint64_t UnitGetOwnerGuid(uintptr_t *unit);
|
||||
uint64_t UnitGetPetGuid(uintptr_t *unit);
|
||||
|
||||
uint64_t UnitGetOwnerGuidForGuid(uint64_t guid);
|
||||
uint64_t UnitGetTargetGuidForGuid(uint64_t guid);
|
||||
uint64_t UnitGetPetGuidForGuid(uint64_t guid);
|
||||
|
||||
bool UnitIsPvpFlagged(uintptr_t *unit);
|
||||
|
||||
|
||||
+23
-9
@@ -6,8 +6,7 @@
|
||||
#include "offsets.hpp"
|
||||
#include "main.hpp"
|
||||
#include "dbc_fields.hpp"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
@@ -20,6 +19,7 @@ namespace Nampower {
|
||||
lua_typeT lua_type = reinterpret_cast<lua_typeT>(Offsets::lua_type);
|
||||
lua_isstringT lua_isstring = reinterpret_cast<lua_isstringT>(Offsets::lua_isstring);
|
||||
lua_isnumberT lua_isnumber = reinterpret_cast<lua_isnumberT>(Offsets::lua_isnumber);
|
||||
lua_toflagT lua_toflag = reinterpret_cast<lua_toflagT>(Offsets::lua_toflag);
|
||||
lua_tostringT lua_tostring = reinterpret_cast<lua_tostringT>(Offsets::lua_tostring);
|
||||
lua_tonumberT lua_tonumber = reinterpret_cast<lua_tonumberT>(Offsets::lua_tonumber);
|
||||
lua_pushnumberT lua_pushnumber = reinterpret_cast<lua_pushnumberT>(Offsets::lua_pushnumber);
|
||||
@@ -369,14 +369,28 @@ namespace Nampower {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if it's a GUID string (starts with "0x" or "0X")
|
||||
if (strncmp(unitToken, "0x", 2) == 0 || strncmp(unitToken, "0X", 2) == 0) {
|
||||
return std::stoull(unitToken, nullptr, 16);
|
||||
} else {
|
||||
// Get GUID from unit token
|
||||
auto const getGUIDFromName = reinterpret_cast<GetGUIDFromNameT>(Offsets::GetGUIDFromName);
|
||||
return getGUIDFromName(unitToken);
|
||||
if (unitToken[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Resolve through the shared unit-token parser first so suffix forms
|
||||
// (owner/target/pet, mark1-mark8, nested forms) work consistently.
|
||||
auto const getGUIDFromName = reinterpret_cast<GetGUIDFromNameT>(Offsets::GetGUIDFromName);
|
||||
const uint64_t resolvedGuid = getGUIDFromName(unitToken);
|
||||
if (resolvedGuid != 0) {
|
||||
return resolvedGuid;
|
||||
}
|
||||
|
||||
// Fallback: strict raw-hex parse when resolver returns 0.
|
||||
if (strncmp(unitToken, "0x", 2) == 0 || strncmp(unitToken, "0X", 2) == 0) {
|
||||
char *endPtr = nullptr;
|
||||
const auto parsed = std::strtoull(unitToken, &endPtr, 16);
|
||||
if (endPtr && *endPtr == '\0') {
|
||||
return static_cast<uint64_t>(parsed);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t GetUnitGuidFromLuaParam(uintptr_t *luaState, int paramIndex) {
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Nampower {
|
||||
extern lua_typeT lua_type;
|
||||
extern lua_isstringT lua_isstring;
|
||||
extern lua_isnumberT lua_isnumber;
|
||||
extern lua_toflagT lua_toflag;
|
||||
extern lua_tostringT lua_tostring;
|
||||
extern lua_tonumberT lua_tonumber;
|
||||
extern lua_pushnumberT lua_pushnumber;
|
||||
|
||||
@@ -157,6 +157,9 @@ namespace Nampower {
|
||||
std::unique_ptr<hadesmem::PatchDetour<GetSpellSlotFromLuaT> > gGetSpellSlotFromLuaDetour;
|
||||
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT> > gCSimpleFrame_GetNameDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<CSimpleTop_OnKeyDownT> > gCSimpleTop_OnKeyDownDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<CSimpleTop_OnKeyUpT> > gCSimpleTop_OnKeyUpDetour;
|
||||
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<CGGameUI_ShowCombatFeedbackT> > gCGGameUI_ShowCombatFeedbackDetour;
|
||||
@@ -1422,6 +1425,8 @@ namespace Nampower {
|
||||
&Spell_C_SpellFailedHook);
|
||||
gSpellGoDetour = createHook<SpellGoT>(process, Offsets::SpellGo, &SpellGoHook);
|
||||
gSpellDelayedDetour = createHook<PacketHandlerT>(process, Offsets::SpellDelayed, &SpellDelayedHook);
|
||||
gGetGUIDFromNameDetour = createHook<GetGUIDFromNameT>(process, Offsets::GetGUIDFromName,
|
||||
&GetGUIDFromNameHook);
|
||||
gCastSpellByNameDetour = createHook<LuaScriptT>(process, Offsets::Script_CastSpellByName,
|
||||
&Script_CastSpellByNameHook);
|
||||
gSpellTargetUnitDetour = createHook<LuaScriptT>(process, Offsets::Script_SpellTargetUnit,
|
||||
@@ -1458,6 +1463,10 @@ namespace Nampower {
|
||||
&AttackRoundInfo_ReadPacketHook);
|
||||
gCSimpleFrame_GetNameDetour = createHook<LuaScriptT>(process, Offsets::CSimpleFrame_GetName,
|
||||
&CSimpleFrame_GetNameHook);
|
||||
gCSimpleTop_OnKeyDownDetour = createHook<CSimpleTop_OnKeyDownT>(
|
||||
process, Offsets::CSimpleTop_OnKeyDown, &CSimpleTop_OnKeyDownHook);
|
||||
gCSimpleTop_OnKeyUpDetour = createHook<CSimpleTop_OnKeyUpT>(
|
||||
process, Offsets::CSimpleTop_OnKeyUp, &CSimpleTop_OnKeyUpHook);
|
||||
gSendUnitSignalDetour = createHook<SendUnitSignalT>(process, Offsets::SendUnitSignal,
|
||||
&SendUnitSignalHook);
|
||||
gCGGameUI_ShowCombatFeedbackDetour = createHook<CGGameUI_ShowCombatFeedbackT>(
|
||||
@@ -1644,6 +1653,12 @@ namespace Nampower {
|
||||
|
||||
char PLAYER_GUILD_UPDATE_GUID[] = "PLAYER_GUILD_UPDATE_GUID";
|
||||
addCustomEvent(game::PLAYER_GUILD_UPDATE_GUID, PLAYER_GUILD_UPDATE_GUID);
|
||||
|
||||
char KEY_DOWN[] = "KEY_DOWN";
|
||||
addCustomEvent(game::KEY_DOWN, KEY_DOWN);
|
||||
|
||||
char KEY_UP[] = "KEY_UP";
|
||||
addCustomEvent(game::KEY_UP, KEY_UP);
|
||||
}
|
||||
|
||||
void FrameScript_CreateEventsHook(hadesmem::PatchDetourBase *detour, int param_1, uint32_t maxEventId) {
|
||||
@@ -1819,6 +1834,9 @@ namespace Nampower {
|
||||
char setMouseoverUnit[] = "SetMouseoverUnit";
|
||||
RegisterLuaFunction(setMouseoverUnit, reinterpret_cast<uintptr_t *>(Script_SetMouseoverUnit));
|
||||
|
||||
char unitGUID[] = "UnitGUID";
|
||||
RegisterLuaFunction(unitGUID, reinterpret_cast<uintptr_t *>(Script_UnitGUID));
|
||||
|
||||
}
|
||||
|
||||
void load() {
|
||||
|
||||
+10
-1
@@ -25,7 +25,7 @@ namespace Nampower {
|
||||
constexpr uint32_t DISENCHANT_QUALITY_PURPLE = 0x04; // Epic
|
||||
|
||||
constexpr uint32_t MAJOR_VERSION = 2;
|
||||
constexpr uint32_t MINOR_VERSION = 40;
|
||||
constexpr uint32_t MINOR_VERSION = 41;
|
||||
constexpr uint32_t PATCH_VERSION = 0;
|
||||
|
||||
constexpr int32_t LUA_REGISTRYINDEX = -10000;
|
||||
@@ -89,6 +89,11 @@ namespace Nampower {
|
||||
using Spell_C_HandleSpriteClickT = bool (__fastcall *)(game::CSpriteClickEvent *event);
|
||||
using Spell_C_HandleTerrainClickT = uint32_t (__fastcall *)(game::CTerrainClickEvent *event);
|
||||
using CGWorldFrame_OnLayerTrackTerrainT = void (__fastcall *)(void *thisptr, int dummy_edx, int param_1);
|
||||
using CSimpleTop_OnKeyDownT = bool (__fastcall *)(EVENT_DATA_KEY *param_1, int param_2);
|
||||
using CSimpleTop_OnKeyUpT = bool (__fastcall *)(EVENT_DATA_KEY *param_1, int param_2);
|
||||
using IsModifierKeyDownT = uint32_t (__fastcall *)(int param_1);
|
||||
using Script_IsAltKeyDownT = uint32_t (__fastcall *)(void *param_1);
|
||||
using UpdateSyncKeyStateT = void (__fastcall *)(void *evtContext, uint32_t key, int *syncType);
|
||||
using Spell_C_TargetSpellT = bool (__fastcall *)(
|
||||
uint32_t *player,
|
||||
uint32_t *spellId,
|
||||
@@ -123,6 +128,7 @@ namespace Nampower {
|
||||
using LuaGetTableT = void (__fastcall *)(uintptr_t *luaState, int globalsIndex);
|
||||
using LuaCallT = void (__fastcall *)(const char *code, const char *unused);
|
||||
using LuaScriptT = uint32_t (__fastcall *)(uintptr_t *luaState);
|
||||
using FrameScript_ExecuteT = void (__fastcall *)(int param_1);
|
||||
using GetGUIDFromNameT = std::uint64_t (__fastcall *)(const char *);
|
||||
using GetUnitFromNameT = uintptr_t *(__fastcall *)(const char *);
|
||||
using GetNamesFromGUIDT = char **(__fastcall *)(uint64_t *guid, int *numNamesReturn);
|
||||
@@ -133,6 +139,7 @@ namespace Nampower {
|
||||
using lua_typeT = int (__fastcall *)(uintptr_t *, int);
|
||||
using lua_isstringT = bool (__fastcall *)(uintptr_t *, int);
|
||||
using lua_isnumberT = bool (__fastcall *)(uintptr_t *, int);
|
||||
using lua_toflagT = uint32_t (__fastcall *)(uintptr_t *, int, uint32_t);
|
||||
using lua_tostringT = char *(__fastcall *)(uintptr_t *, int);
|
||||
using lua_tonumberT = double (__fastcall *)(uintptr_t *, int);
|
||||
using lua_pushnumberT = void (__fastcall *)(uintptr_t *, double);
|
||||
@@ -181,6 +188,8 @@ namespace Nampower {
|
||||
|
||||
using CGGameUI_DisplayErrorT = void (__cdecl *)(uint32_t errorCode);
|
||||
|
||||
using XMLNode_GetAttributeByNameT = uint32_t (__thiscall *)(void *thisptr, const char *name);
|
||||
|
||||
void RegisterLuaFunction(char *, uintptr_t *func);
|
||||
|
||||
void LuaCall(const char *code);
|
||||
|
||||
+186
-8
@@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
#include "misc_scripts.hpp"
|
||||
#include "logging.hpp"
|
||||
#include "offsets.hpp"
|
||||
#include "items.hpp"
|
||||
#include "dbc_fields.hpp"
|
||||
@@ -10,6 +11,9 @@
|
||||
#include "helper.hpp"
|
||||
#include "lua_refs.hpp"
|
||||
#include "auras.hpp"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
namespace Nampower {
|
||||
@@ -916,6 +920,129 @@ namespace Nampower {
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t Script_UnitGUID(uintptr_t *luaState) {
|
||||
luaState = GetLuaStatePtr();
|
||||
|
||||
if (!lua_isstring(luaState, 1)) {
|
||||
lua_error(luaState, "Usage: UnitGUID(unitToken)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *unitToken = lua_tostring(luaState, 1);
|
||||
if (!unitToken || unitToken[0] == '\0') {
|
||||
lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto const getGUIDFromName = reinterpret_cast<GetGUIDFromNameT>(Offsets::GetGUIDFromName);
|
||||
const uint64_t guid = getGUIDFromName(unitToken);
|
||||
if (guid == 0) {
|
||||
lua_pushnil(luaState);
|
||||
} else {
|
||||
PushGuidString(luaState, guid);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64_t GetGUIDFromNameHook(hadesmem::PatchDetourBase *detour, const char *nameStr) {
|
||||
auto const original = detour->GetTrampolineT<GetGUIDFromNameT>();
|
||||
|
||||
if (!nameStr || nameStr[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Hex GUID with optional suffix: "0x[16 hex digits][owner|target|pet]"
|
||||
const size_t tokenLen = std::strlen(nameStr);
|
||||
const bool isHexToken = (nameStr[0] == '0' && (nameStr[1] == 'x' || nameStr[1] == 'X'));
|
||||
const bool hasHexAndUnitSuffix = isHexToken && tokenLen > 18 &&
|
||||
(_stricmp(nameStr + 18, "owner") == 0 ||
|
||||
_stricmp(nameStr + 18, "target") == 0 ||
|
||||
_stricmp(nameStr + 18, "pet") == 0);
|
||||
|
||||
// Handle mark1-mark8 before calling original, so original parser can't error on custom tokens.
|
||||
if (_strnicmp(nameStr, "mark", 4) == 0) {
|
||||
const char digit = nameStr[4];
|
||||
if (digit >= '1' && digit <= '8' && nameStr[5] == '\0') {
|
||||
const uint32_t markIndex = static_cast<uint32_t>(digit - '1');
|
||||
auto const markGuids = reinterpret_cast<uint64_t *>(static_cast<uintptr_t>(Offsets::RaidTargetMarkGuidBase));
|
||||
return markGuids[markIndex];
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasHexAndUnitSuffix) {
|
||||
uint64_t result = original(nameStr);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (isHexToken) {
|
||||
if (tokenLen < 18) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char guidHex[17] = {};
|
||||
std::memcpy(guidHex, nameStr + 2, 16);
|
||||
if (!std::all_of(guidHex, guidHex + 16,
|
||||
[](unsigned char c) { return std::isxdigit(c) != 0; })) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint64_t parsedGuid = std::strtoull(guidHex, nullptr, 16);
|
||||
|
||||
// Suffix must begin exactly after 16 hex digits (no substring matching).
|
||||
const char *const hexSuffix = nameStr + 18;
|
||||
|
||||
// "owner" suffix
|
||||
if (_stricmp(hexSuffix, "owner") == 0) {
|
||||
return game::UnitGetOwnerGuidForGuid(parsedGuid);
|
||||
}
|
||||
|
||||
// "target" suffix
|
||||
if (_stricmp(hexSuffix, "target") == 0) {
|
||||
return game::UnitGetTargetGuidForGuid(parsedGuid);
|
||||
}
|
||||
|
||||
// "pet" suffix
|
||||
if (_stricmp(hexSuffix, "pet") == 0) {
|
||||
return game::UnitGetPetGuidForGuid(parsedGuid);
|
||||
}
|
||||
|
||||
return parsedGuid;
|
||||
}
|
||||
|
||||
// Non-hex token ending in owner/target/pet suffix (up to 20 chars total)
|
||||
if (tokenLen <= 0x14) {
|
||||
auto resolveBaseGuid = [&](size_t suffixLen) -> uint64_t {
|
||||
// Copy prefix (everything before suffix) and resolve it.
|
||||
// Call via the hooked address so nested custom tokens also resolve.
|
||||
char buf[0x15] = {};
|
||||
std::memcpy(buf, nameStr, tokenLen - suffixLen);
|
||||
auto const hooked = reinterpret_cast<GetGUIDFromNameT>(
|
||||
static_cast<uint32_t>(Offsets::GetGUIDFromName));
|
||||
return hooked(buf);
|
||||
};
|
||||
|
||||
if (tokenLen > 5 && _stricmp(nameStr + tokenLen - 5, "owner") == 0) {
|
||||
const uint64_t baseGuid = resolveBaseGuid(5);
|
||||
return game::UnitGetOwnerGuidForGuid(baseGuid);
|
||||
}
|
||||
|
||||
if (tokenLen > 6 && _stricmp(nameStr + tokenLen - 6, "target") == 0) {
|
||||
const uint64_t baseGuid = resolveBaseGuid(6);
|
||||
return game::UnitGetTargetGuidForGuid(baseGuid);
|
||||
}
|
||||
|
||||
if (tokenLen > 3 && _stricmp(nameStr + tokenLen - 3, "pet") == 0) {
|
||||
const uint64_t baseGuid = resolveBaseGuid(3);
|
||||
return game::UnitGetPetGuidForGuid(baseGuid);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t CSimpleFrame_GetNameHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
|
||||
luaState = GetLuaStatePtr();
|
||||
|
||||
@@ -930,6 +1057,7 @@ namespace Nampower {
|
||||
uint32_t typeId = *nameplateTypeId;
|
||||
|
||||
uintptr_t *frameObj = nullptr;
|
||||
uintptr_t *vtable = nullptr;
|
||||
|
||||
int luaArgType = lua_type(luaState, 1);
|
||||
if (luaArgType == LUA_TTABLE) {
|
||||
@@ -940,10 +1068,19 @@ namespace Nampower {
|
||||
|
||||
if (frameObj == nullptr) {
|
||||
lua_error(luaState, "Attempt to find 'this' in non-frame table");
|
||||
} else if (!IsReadableMemory(frameObj, sizeof(uintptr_t))) {
|
||||
// Stale frame pointer (object was destroyed) - return nil silently
|
||||
lua_pushnil(luaState);
|
||||
return 1;
|
||||
} else {
|
||||
vtable = reinterpret_cast<uintptr_t *>(*frameObj);
|
||||
// Validate vtable covers entries [0..4] before touching isType at [4]
|
||||
if (!IsReadableMemory(vtable, 5 * sizeof(uintptr_t))) {
|
||||
lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
// Type check via vtable[4] (offset 0x10)
|
||||
using IsTypeT = char (__thiscall *)(uintptr_t *thisPtr, uint32_t typeId);
|
||||
auto vtable = reinterpret_cast<uintptr_t *>(*frameObj);
|
||||
auto isType = reinterpret_cast<IsTypeT>(vtable[4]);
|
||||
if (isType(frameObj, typeId) == 0) {
|
||||
lua_error(luaState, "Wrong object type for member function");
|
||||
@@ -953,18 +1090,22 @@ namespace Nampower {
|
||||
lua_error(luaState, "Attempt to find 'this' in non-table value");
|
||||
}
|
||||
|
||||
// frameObj and vtable are guaranteed valid here (lua_error longjmps otherwise)
|
||||
|
||||
// Check if second arg is passed (e.g. nameplate:GetName(1) to get GUID)
|
||||
if (lua_isnumber(luaState, 2) && static_cast<int>(lua_tonumber(luaState, 2)) != 0) {
|
||||
// Return GUID string from frame object at offset 0x4E8/0x4EC
|
||||
uint32_t guidLow = frameObj[0x13a];
|
||||
uint32_t guidHigh = frameObj[0x13b];
|
||||
uint64_t guid = (static_cast<uint64_t>(guidHigh) << 32) | guidLow;
|
||||
|
||||
PushGuidString(luaState, guid);
|
||||
if (!IsReadableMemory(frameObj + 0x13a, 2 * sizeof(uint32_t))) {
|
||||
lua_pushnil(luaState);
|
||||
} else {
|
||||
uint32_t guidLow = frameObj[0x13a];
|
||||
uint32_t guidHigh = frameObj[0x13b];
|
||||
uint64_t guid = (static_cast<uint64_t>(guidHigh) << 32) | guidLow;
|
||||
PushGuidString(luaState, guid);
|
||||
}
|
||||
} else {
|
||||
// GetName via vtable[1] (offset 0x4)
|
||||
// GetName via vtable[1] (offset 0x4) - vtable already validated above
|
||||
using GetNameT = char *(__thiscall *)(uintptr_t *thisPtr);
|
||||
auto vtable = reinterpret_cast<uintptr_t *>(*frameObj);
|
||||
auto getName = reinterpret_cast<GetNameT>(vtable[1]);
|
||||
char *name = getName(frameObj);
|
||||
|
||||
@@ -978,4 +1119,41 @@ namespace Nampower {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void EmitKeyEvent(uint32_t eventCode, EVENT_DATA_KEY *keyData) {
|
||||
// don't trigger events until player exists as event won't be registered
|
||||
if (keyData == nullptr || game::ClntObjMgrGetActivePlayerGuid() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
static char format[] = "%d%d%d%d";
|
||||
reinterpret_cast<int (__cdecl *)(int, char *, uint32_t, uint32_t, uint32_t, uint32_t)>(Offsets::SignalEventParam)(
|
||||
eventCode,
|
||||
format,
|
||||
keyData->key,
|
||||
keyData->metaKeyState,
|
||||
keyData->repeat,
|
||||
keyData->time);
|
||||
}
|
||||
|
||||
bool CSimpleTop_OnKeyDownHook(hadesmem::PatchDetourBase *detour, EVENT_DATA_KEY *keyData, int param_2) {
|
||||
auto const onKeyDown = detour->GetTrampolineT<CSimpleTop_OnKeyDownT>();
|
||||
auto result = onKeyDown(keyData, param_2);
|
||||
|
||||
auto const isModifierKeyDown = reinterpret_cast<IsModifierKeyDownT>(Offsets::Script_IsAltKeyDown);
|
||||
auto altDown = isModifierKeyDown(2);
|
||||
|
||||
EmitKeyEvent(game::KEY_DOWN, keyData);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CSimpleTop_OnKeyUpHook(hadesmem::PatchDetourBase *detour, EVENT_DATA_KEY *keyData, int param_2) {
|
||||
auto const onKeyUp = detour->GetTrampolineT<CSimpleTop_OnKeyUpT>();
|
||||
auto result = onKeyUp(keyData, param_2);
|
||||
|
||||
EmitKeyEvent(game::KEY_UP, keyData);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ namespace Nampower {
|
||||
uint32_t Script_CancelPlayerAuraSpellId(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_SetMouseoverUnit(uintptr_t *luaState);
|
||||
uint32_t Script_UnitGUID(uintptr_t *luaState);
|
||||
|
||||
uint32_t CSimpleFrame_GetNameHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
uint64_t GetGUIDFromNameHook(hadesmem::PatchDetourBase *detour, const char *nameStr);
|
||||
bool CSimpleTop_OnKeyDownHook(hadesmem::PatchDetourBase *detour, EVENT_DATA_KEY *keyData, int param_2);
|
||||
bool CSimpleTop_OnKeyUpHook(hadesmem::PatchDetourBase *detour, EVENT_DATA_KEY *keyData, int param_2);
|
||||
}
|
||||
|
||||
@@ -154,6 +154,8 @@ enum class Offsets : std::uint32_t {
|
||||
Framescript_SetEventCount = 0X007053B0,
|
||||
Framescript_EventObject_Data = 0x00CEEF68,
|
||||
|
||||
FrameScript_Execute = 0x00704CF0,
|
||||
|
||||
// Existing script functions
|
||||
GetGUIDFromName = 0X00515970,
|
||||
Script_CastSpellByName = 0x004B4AB0,
|
||||
@@ -161,6 +163,7 @@ enum class Offsets : std::uint32_t {
|
||||
Script_SetCVar = 0x00488C10,
|
||||
Script_RunScript = 0x0048B980,
|
||||
Script_SpellStopCasting = 0x006E6E80,
|
||||
Script_IsAltKeyDown = 0x0041F8F0,
|
||||
Script_UseInventoryItem = 0x004c8de0,
|
||||
Script_UseContainerItem = 0x004fa0e0,
|
||||
|
||||
@@ -171,6 +174,7 @@ enum class Offsets : std::uint32_t {
|
||||
lua_type = 0X006F3400,
|
||||
lua_isstring = 0x006F3510,
|
||||
lua_isnumber = 0X006F34D0,
|
||||
lua_toflag = 0X006F1C10,
|
||||
lua_tostring = 0x006F3690,
|
||||
lua_tonumber = 0X006F3620,
|
||||
lua_pushnumber = 0X006F3810,
|
||||
@@ -192,6 +196,8 @@ enum class Offsets : std::uint32_t {
|
||||
luaL_unref = 0x006F5400,
|
||||
|
||||
CGInputControlGetActive = 0XBE1148,
|
||||
UpdateSyncKeyState = 0x00424810,
|
||||
ClientEventContext = 0x00882670,
|
||||
LastHardwareAction = 0X00CF0BC8,
|
||||
CGInputControlSetReleaseAction = 0x514810,
|
||||
CGInputControlSetControlBit = 0x515090,
|
||||
@@ -248,5 +254,12 @@ enum class Offsets : std::uint32_t {
|
||||
CursorModelSetSequence = 0x00523c20,
|
||||
CGGameUI_DisplayError = 0x00496720,
|
||||
|
||||
CSimpleTop_OnKeyDown = 0x00765f10,
|
||||
CSimpleTop_OnKeyUp = 0x00765fd0,
|
||||
|
||||
CSimpleFrame_GetName = 0x007a1390,
|
||||
|
||||
XMLNode_GetAttributeByName = 0x006f2cf0,
|
||||
|
||||
RaidTargetMarkGuidBase = 0xB71368,
|
||||
};
|
||||
|
||||
@@ -158,3 +158,9 @@ struct CastData {
|
||||
uint32_t channelDuration;
|
||||
};
|
||||
|
||||
struct EVENT_DATA_KEY {
|
||||
uint32_t key;
|
||||
uint32_t metaKeyState;
|
||||
uint32_t repeat;
|
||||
uint32_t time;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user