mirror of
https://github.com/brues-code/nampower.git
synced 2026-10-10 15:40:02 +00:00
add unit field access as well as spell modifiers
This commit is contained in:
@@ -119,6 +119,185 @@ Examples:
|
||||
|
||||
### Custom Lua Functions
|
||||
|
||||
### Spell/Item/Unit information
|
||||
|
||||
#### GetItemStats(itemId)
|
||||
Returns a Lua table containing all fields for the item’s `ItemStats` record (including localized `displayName` and `description`). Returns nil if the item cannot be found or loaded.
|
||||
|
||||
Full field name lists are in [`DBC_FIELDS.md`](DBC_FIELDS.md).
|
||||
|
||||
#### GetItemStatsField(itemId, fieldName)
|
||||
Fast lookup for a single field on an item. Returns the requested field value; returns nil if the item is not found; raises a Lua error if the field name is invalid.
|
||||
|
||||
Full field name lists are in [`DBC_FIELDS.md`](DBC_FIELDS.md).
|
||||
|
||||
**Examples:**
|
||||
```lua
|
||||
-- Get item name
|
||||
local name = GetItemStatsField(19019, "displayName")
|
||||
print(name) -- "Thunderfury, Blessed Blade of the Windseeker"
|
||||
|
||||
-- Get item level
|
||||
local ilvl = GetItemStatsField(22589, "itemLevel")
|
||||
print("Atiesh item level: " .. ilvl) -- 90
|
||||
|
||||
-- Get item quality (0=Poor, 1=Common, 2=Uncommon, 3=Rare, 4=Epic, 5=Legendary)
|
||||
local quality = GetItemStatsField(19019, "quality")
|
||||
print("Quality: " .. quality) -- 5 (Legendary)
|
||||
|
||||
-- Get item delay (weapon speed in milliseconds)
|
||||
local delay = GetItemStatsField(19019, "delay")
|
||||
print("Weapon speed: " .. (delay / 1000) .. " seconds") -- 1.9 seconds
|
||||
```
|
||||
|
||||
#### GetSpellRec(spellId)
|
||||
Returns a Lua table containing all fields for the spell’s `SpellRec` record (including localized `name` and `rank`). Returns nil if the spell cannot be found.
|
||||
|
||||
Full field name lists are in [`DBC_FIELDS.md`](DBC_FIELDS.md).
|
||||
|
||||
#### GetSpellRecField(spellId, fieldName)
|
||||
Fast lookup for a single field on a spell. Returns the requested field value; returns nil if the spell is not found; raises a Lua error if the field name is invalid.
|
||||
|
||||
Full field name lists are in [`DBC_FIELDS.md`](DBC_FIELDS.md).
|
||||
|
||||
**Examples:**
|
||||
```lua
|
||||
-- Get spell name
|
||||
local name = GetSpellRecField(116, "name")
|
||||
print(name) -- "Frostbolt"
|
||||
|
||||
-- Get spell rank
|
||||
local rank = GetSpellRecField(116, "rank")
|
||||
print(rank) -- "Rank 1"
|
||||
|
||||
-- Get spell cast time in milliseconds
|
||||
local castTime = GetSpellRecField(133, "castTime")
|
||||
print("Fireball cast time: " .. (castTime / 1000) .. " seconds") -- 3.5 seconds
|
||||
|
||||
-- Get spell range (max range in yards * 10, so divide by 10)
|
||||
local maxRange = GetSpellRecField(116, "rangeMax")
|
||||
print("Frostbolt max range: " .. (maxRange / 10) .. " yards") -- 30 yards
|
||||
|
||||
-- Get spell mana cost
|
||||
local manaCost = GetSpellRecField(116, "manaCost")
|
||||
print("Mana cost: " .. manaCost)
|
||||
|
||||
-- Get spell school (0=Physical, 1=Holy, 2=Fire, 3=Nature, 4=Frost, 5=Shadow, 6=Arcane)
|
||||
local school = GetSpellRecField(116, "school")
|
||||
print("School: " .. school) -- 4 (Frost)
|
||||
|
||||
-- Get spell icon ID
|
||||
local spellIconID = GetSpellRecField(116, "spellIconID")
|
||||
print("Icon ID: " .. spellIconID)
|
||||
```
|
||||
|
||||
#### GetSpellModifiers(spellId, modifierType)
|
||||
Returns the current spell modifiers applied to a spell for the player. This includes buffs, talents, and other effects that modify spell behavior.
|
||||
|
||||
**Parameters:**
|
||||
- `spellId` (number): The spell ID to check
|
||||
- `modifierType` (number): The type of modifier to check (see list below)
|
||||
|
||||
**Returns:**
|
||||
- 1st param (number): Flat modification value (e.g., +50 damage)
|
||||
- 2nd param (number): Percent modification value (e.g., 10 for +10%)
|
||||
- 3rd param (number): Return value from the function (whether there was any percent or flat modifier)
|
||||
|
||||
**Modifier Types:**
|
||||
- 0 = DAMAGE
|
||||
- 1 = DURATION
|
||||
- 2 = THREAT
|
||||
- 3 = ATTACK_POWER
|
||||
- 4 = CHARGES
|
||||
- 5 = RANGE
|
||||
- 6 = RADIUS
|
||||
- 7 = CRITICAL_CHANCE
|
||||
- 8 = ALL_EFFECTS
|
||||
- 9 = NOT_LOSE_CASTING_TIME
|
||||
- 10 = CASTING_TIME
|
||||
- 11 = COOLDOWN
|
||||
- 12 = SPEED
|
||||
- 14 = COST
|
||||
- 15 = CRIT_DAMAGE_BONUS
|
||||
- 16 = RESIST_MISS_CHANCE
|
||||
- 17 = JUMP_TARGETS
|
||||
- 18 = CHANCE_OF_SUCCESS
|
||||
- 19 = ACTIVATION_TIME
|
||||
- 20 = EFFECT_PAST_FIRST
|
||||
- 21 = CASTING_TIME_OLD
|
||||
- 22 = DOT
|
||||
- 23 = HASTE
|
||||
- 24 = SPELL_BONUS_DAMAGE
|
||||
- 27 = MULTIPLE_VALUE
|
||||
- 28 = RESIST_DISPEL_CHANCE
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
-- Check damage modifiers on Frostbolt (spell ID 116)
|
||||
local flatMod, percentMod, ret = GetSpellModifiers(116, 0)
|
||||
print("Flat damage bonus: " .. flatMod)
|
||||
print("Percent damage bonus: " .. percentMod .. "%")
|
||||
```
|
||||
|
||||
#### GetUnitData(unitToken)
|
||||
Returns a Lua table containing all unit fields for the specified unit. This provides access to low-level unit data like health, mana, stats, auras, resistances, and more.
|
||||
|
||||
**Parameters:**
|
||||
- `unitToken` (string): Can be a standard unit token ("player", "target", "pet", "mouseover", etc.) or a GUID string (e.g., "0xF5300000000000A5")
|
||||
|
||||
**Returns:**
|
||||
- A Lua table containing all unit fields, or nil if the unit cannot be found
|
||||
|
||||
Full field name lists are in [`UNIT_FIELDS.md`](UNIT_FIELDS.md).
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
-- Get all unit data for your current target
|
||||
local data = GetUnitData("target")
|
||||
if data then
|
||||
print("Target health: " .. data.health .. "/" .. data.maxHealth)
|
||||
print("Target level: " .. data.level)
|
||||
print("Target display ID: " .. data.displayId)
|
||||
end
|
||||
|
||||
-- Using a GUID
|
||||
local data = GetUnitData("0xF5300000000000A5")
|
||||
```
|
||||
|
||||
#### GetUnitField(unitToken, fieldName)
|
||||
Fast lookup for a single field on a unit. More efficient than GetUnitData when you only need one specific field.
|
||||
|
||||
**Parameters:**
|
||||
- `unitToken` (string): Can be a standard unit token ("player", "target", "pet", "mouseover", etc.) or a GUID string
|
||||
- `fieldName` (string): The name of the field to retrieve
|
||||
|
||||
**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
|
||||
|
||||
Full field name lists are in [`UNIT_FIELDS.md`](UNIT_FIELDS.md).
|
||||
|
||||
**Examples:**
|
||||
```lua
|
||||
-- Get target's current health
|
||||
local health = GetUnitField("target", "health")
|
||||
print("Target health: " .. health)
|
||||
|
||||
-- Get player's current mana (power1)
|
||||
local mana = GetUnitField("player", "power1")
|
||||
print("Player mana: " .. mana)
|
||||
|
||||
-- Get all auras on target (returns a table)
|
||||
local auras = GetUnitField("target", "aura")
|
||||
for i, auraId in ipairs(auras) do
|
||||
print("Aura " .. i .. ": " .. auraId)
|
||||
end
|
||||
|
||||
-- Get all resistances (returns a table)
|
||||
local resistances = GetUnitField("player", "resistances")
|
||||
-- resistances[1] = armor, [2] = holy, [3] = fire, [4] = nature, [5] = frost, [6] = shadow, [7] = arcane
|
||||
```
|
||||
|
||||
#### QueueSpellByName(spellName)
|
||||
Will force queue a spell regardless of the appropriate queue window. If no spell is currently being cast it will be cast immediately.
|
||||
For example can make a macro with
|
||||
@@ -228,26 +407,6 @@ Examples:
|
||||
/run local slot, bookType, spellId=GetSpellSlotTypeIdForName("Frostbolt");print(slot);print(bookType);print(spellId)
|
||||
```
|
||||
|
||||
#### GetItemStats(itemId)
|
||||
Returns a Lua table containing all fields for the item’s `ItemStats` record (including localized `displayName` and `description`). Returns nil if the item cannot be found or loaded.
|
||||
|
||||
Full field name lists are in [`DBC_FIELDS.md`](DBC_FIELDS.md).
|
||||
|
||||
#### GetItemStatsField(itemId, fieldName)
|
||||
Fast lookup for a single field on an item. Returns the requested field value; returns nil if the item is not found; raises a Lua error if the field name is invalid.
|
||||
|
||||
Full field name lists are in [`DBC_FIELDS.md`](DBC_FIELDS.md).
|
||||
|
||||
#### GetSpellRec(spellId)
|
||||
Returns a Lua table containing all fields for the spell’s `SpellRec` record (including localized `name` and `rank`). Returns nil if the spell cannot be found.
|
||||
|
||||
Full field name lists are in [`DBC_FIELDS.md`](DBC_FIELDS.md).
|
||||
|
||||
#### GetSpellRecField(spellId, fieldName)
|
||||
Fast lookup for a single field on a spell. Returns the requested field value; returns nil if the spell is not found; raises a Lua error if the field name is invalid.
|
||||
|
||||
Full field name lists are in [`DBC_FIELDS.md`](DBC_FIELDS.md).
|
||||
|
||||
#### GetNampowerVersion()
|
||||
Returns the current version of Nampower split into major, minor and patch numbers.
|
||||
|
||||
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
# Unit Fields Reference
|
||||
|
||||
This document lists all available unit fields that can be accessed via `GetUnitData()` and `GetUnitField()` functions.
|
||||
|
||||
## Overview
|
||||
|
||||
Unit fields provide low-level access to game data for any unit (player, target, pet, NPCs, etc.). These fields are part of the WoW 1.12 client's internal data structures.
|
||||
|
||||
## Usage
|
||||
|
||||
```lua
|
||||
-- Get all fields at once
|
||||
local data = GetUnitData("target")
|
||||
if data then
|
||||
print("Health: " .. data.health)
|
||||
end
|
||||
|
||||
-- Get a single field (more efficient)
|
||||
local health = GetUnitField("target", "health")
|
||||
```
|
||||
|
||||
## Simple Fields
|
||||
|
||||
These fields return a single value (number).
|
||||
|
||||
### Object References (UINT64)
|
||||
These fields contain GUIDs (globally unique identifiers) for game objects:
|
||||
|
||||
- **charm** - GUID of the unit that has charmed this unit
|
||||
- **summon** - GUID of the unit's current summon
|
||||
- **charmedBy** - GUID of the unit that charmed this unit
|
||||
- **summonedBy** - GUID of the unit that summoned this unit
|
||||
- **createdBy** - GUID of the unit that created this unit
|
||||
- **target** - GUID of the unit's current target
|
||||
- **persuaded** - GUID of the unit being persuaded
|
||||
- **channelObject** - GUID of the object being channeled (for channeling spells)
|
||||
|
||||
### Health and Power (UINT32)
|
||||
- **health** - Current health points
|
||||
- **power1** - Current mana (or energy/rage depending on class)
|
||||
- **power2** - Current rage
|
||||
- **power3** - Current focus (hunter pets)
|
||||
- **power4** - Current energy (rogues, druids in cat/bear form)
|
||||
- **power5** - Current happiness (hunter pets)
|
||||
- **maxHealth** - Maximum health points
|
||||
- **maxPower1** - Maximum mana
|
||||
- **maxPower2** - Maximum rage
|
||||
- **maxPower3** - Maximum focus
|
||||
- **maxPower4** - Maximum energy
|
||||
- **maxPower5** - Maximum happiness
|
||||
|
||||
### Basic Unit Info (UINT32)
|
||||
- **level** - Unit's level
|
||||
- **factionTemplate** - Unit's faction template ID
|
||||
- **flags** - Unit flags (combat, dead, mounted, etc.)
|
||||
- **dynamicFlags** - Dynamic unit flags (lootable, tapped, etc.)
|
||||
- **auraState** - Aura state flags (reactive abilities like Revenge, Overpower)
|
||||
- **npcFlags** - NPC flags (vendor, trainer, quest giver, etc.)
|
||||
- **npcEmoteState** - Current emote state ID
|
||||
|
||||
### Display and Appearance (UINT32)
|
||||
- **displayId** - Current display model ID
|
||||
- **nativeDisplayId** - Original/native display model ID
|
||||
- **mountDisplayId** - Mount display model ID (if mounted)
|
||||
|
||||
### Combat Stats (UINT32)
|
||||
- **baseAttackTime** - Base main-hand attack time in milliseconds
|
||||
- **offhandAttackTime** - Off-hand attack time in milliseconds
|
||||
- **rangedAttackTime** - Ranged attack time in milliseconds
|
||||
|
||||
### Combat Stats (FLOAT)
|
||||
- **boundingRadius** - Unit's bounding radius for collision
|
||||
- **combatReach** - Unit's combat reach distance
|
||||
- **minDamage** - Minimum main-hand damage
|
||||
- **maxDamage** - Maximum main-hand damage
|
||||
- **minOffhandDamage** - Minimum off-hand damage
|
||||
- **maxOffhandDamage** - Maximum off-hand damage
|
||||
- **minRangedDamage** - Minimum ranged damage
|
||||
- **maxRangedDamage** - Maximum ranged damage
|
||||
|
||||
### Pet Info (UINT32)
|
||||
- **petNumber** - Pet's number identifier
|
||||
- **petNameTimestamp** - Timestamp of pet name
|
||||
- **petExperience** - Pet's current experience
|
||||
- **petNextLevelExp** - Experience needed for pet's next level
|
||||
|
||||
### Spell/Channel Info (UINT32)
|
||||
- **channelSpell** - Spell ID of currently channeling spell
|
||||
- **createdBySpell** - Spell ID that created this unit (for summons)
|
||||
|
||||
### Casting Speed (FLOAT)
|
||||
- **modCastSpeed** - Cast speed modifier (1.0 = normal, <1.0 = faster, >1.0 = slower)
|
||||
|
||||
### Training (UINT32)
|
||||
- **trainingPoints** - Available training points (for pets and talents)
|
||||
|
||||
### Base Stats (UINT32)
|
||||
- **stat0** - Strength
|
||||
- **stat1** - Agility
|
||||
- **stat2** - Stamina
|
||||
- **stat3** - Intellect
|
||||
- **stat4** - Spirit
|
||||
- **baseMana** - Base mana before modifiers
|
||||
- **baseHealth** - Base health before modifiers
|
||||
|
||||
### Attack Power (UINT32)
|
||||
- **attackPower** - Total melee attack power
|
||||
- **attackPowerMods** - Attack power modifiers
|
||||
- **rangedAttackPower** - Total ranged attack power
|
||||
- **rangedAttackPowerMods** - Ranged attack power modifiers
|
||||
|
||||
### Attack Power Multipliers (FLOAT)
|
||||
- **attackPowerMultiplier** - Melee attack power multiplier
|
||||
- **rangedAttackPowerMultiplier** - Ranged attack power multiplier
|
||||
|
||||
### Packed Byte Fields (UINT32)
|
||||
These are special fields that pack multiple values into a single 32-bit integer:
|
||||
|
||||
- **bytes0** - Contains race, class, gender, and power type
|
||||
- Byte 0: Race
|
||||
- Byte 1: Class
|
||||
- Byte 2: Gender
|
||||
- Byte 3: Power type
|
||||
|
||||
- **bytes1** - Contains stand state, pet talent points, vis flags, and anim tier
|
||||
- Byte 0: Stand state
|
||||
- Byte 1: Pet talent points
|
||||
- Byte 2: Vis flags
|
||||
- Byte 3: Anim tier
|
||||
|
||||
- **bytes2** - Contains sheath state, pvp flags, pet flags, and shape shift form
|
||||
- Byte 0: Sheath state
|
||||
- Byte 1: PvP flags
|
||||
- Byte 2: Pet flags
|
||||
- Byte 3: Shape shift form
|
||||
|
||||
## Array Fields
|
||||
|
||||
These fields return a Lua table with numeric indices (1-based).
|
||||
|
||||
### Visual Items (UINT32 arrays)
|
||||
- **virtualItemDisplay** [3] - Display IDs for virtual items (weapon visuals, etc.)
|
||||
- **virtualItemInfo** [6] - Additional virtual item information
|
||||
|
||||
### Auras/Buffs/Debuffs (UINT32 arrays)
|
||||
- **aura** [48] - Array of aura (buff/debuff) spell IDs on the unit
|
||||
- Index 1-48 contains spell IDs (0 = no aura in that slot)
|
||||
|
||||
- **auraFlags** [6] - Bit flags for aura properties (positive, negative, cancelable, etc.)
|
||||
- Each UINT32 contains flags for 8 auras (4 bits per aura)
|
||||
|
||||
- **auraLevels** [12] - Aura caster levels
|
||||
- Each UINT32 contains 4 aura levels (8 bits per level)
|
||||
|
||||
- **auraApplications** [12] - Aura stack counts
|
||||
- Each UINT32 contains 4 aura stacks (8 bits per stack)
|
||||
|
||||
### Resistances (UINT32 arrays)
|
||||
- **resistances** [7] - Resistance values for all schools
|
||||
- Index 1: Armor (physical resistance)
|
||||
- Index 2: Holy resistance
|
||||
- Index 3: Fire resistance
|
||||
- Index 4: Nature resistance
|
||||
- Index 5: Frost resistance
|
||||
- Index 6: Shadow resistance
|
||||
- Index 7: Arcane resistance
|
||||
|
||||
### Spell Power Modifiers (FLOAT arrays)
|
||||
- **powerCostModifier** [7] - Flat mana cost modifications per school
|
||||
- Index 1-7: Physical, Holy, Fire, Nature, Frost, Shadow, Arcane
|
||||
|
||||
- **powerCostMultiplier** [7] - Percent mana cost modifications per school
|
||||
- Index 1-7: Physical, Holy, Fire, Nature, Frost, Shadow, Arcane
|
||||
|
||||
## Examples
|
||||
|
||||
### Health Monitoring
|
||||
```lua
|
||||
-- Monitor target health percentage
|
||||
local health = GetUnitField("target", "health")
|
||||
local maxHealth = GetUnitField("target", "maxHealth")
|
||||
local healthPct = (health / maxHealth) * 100
|
||||
print("Target health: " .. healthPct .. "%")
|
||||
```
|
||||
|
||||
### Checking Auras
|
||||
```lua
|
||||
-- Check if target has a specific debuff
|
||||
local auras = GetUnitField("target", "aura")
|
||||
local CURSE_OF_AGONY = 980
|
||||
|
||||
for i, spellId in ipairs(auras) do
|
||||
if spellId == CURSE_OF_AGONY then
|
||||
print("Target has Curse of Agony!")
|
||||
break
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Reading Resistances
|
||||
```lua
|
||||
-- Get all player resistances
|
||||
local resistances = GetUnitField("player", "resistances")
|
||||
print("Armor: " .. resistances[1])
|
||||
print("Holy resistance: " .. resistances[2])
|
||||
print("Fire resistance: " .. resistances[3])
|
||||
print("Nature resistance: " .. resistances[4])
|
||||
print("Frost resistance: " .. resistances[5])
|
||||
print("Shadow resistance: " .. resistances[6])
|
||||
print("Arcane resistance: " .. resistances[7])
|
||||
```
|
||||
|
||||
### Checking Combat State
|
||||
```lua
|
||||
-- Check if unit is in combat (flags field bit check)
|
||||
local flags = GetUnitField("target", "flags")
|
||||
local UNIT_FLAG_IN_COMBAT = 0x00080000
|
||||
local inCombat = bit.band(flags, UNIT_FLAG_IN_COMBAT) ~= 0
|
||||
print("In combat: " .. tostring(inCombat))
|
||||
```
|
||||
|
||||
### Monitoring Cast Speed
|
||||
```lua
|
||||
-- Check your current cast speed modifier
|
||||
local modCastSpeed = GetUnitField("player", "modCastSpeed")
|
||||
-- 1.0 = normal, 0.9 = 10% faster, 1.1 = 10% slower
|
||||
print("Cast speed modifier: " .. modCastSpeed)
|
||||
```
|
||||
|
||||
### Attack Power Info
|
||||
```lua
|
||||
-- Get your attack power
|
||||
local ap = GetUnitField("player", "attackPower")
|
||||
local apMods = GetUnitField("player", "attackPowerMods")
|
||||
local apMult = GetUnitField("player", "attackPowerMultiplier")
|
||||
print("Base AP: " .. ap)
|
||||
print("AP Mods: " .. apMods)
|
||||
print("AP Multiplier: " .. apMult)
|
||||
```
|
||||
|
||||
### Pet Information
|
||||
```lua
|
||||
-- Check pet experience
|
||||
if UnitExists("pet") then
|
||||
local petXP = GetUnitField("pet", "petExperience")
|
||||
local petNextXP = GetUnitField("pet", "petNextLevelExp")
|
||||
local progress = (petXP / petNextXP) * 100
|
||||
print("Pet XP: " .. progress .. "%")
|
||||
end
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- GUID fields return numbers but represent 64-bit values. Lua handles these as doubles.
|
||||
- Array indices are 1-based (Lua convention), not 0-based.
|
||||
- Returns `nil` if the unit doesn't exist or the field cannot be read.
|
||||
- Some fields may require specific game state to be meaningful (e.g., pet fields only work when you have a pet).
|
||||
- Unit flags and other bit fields require bitwise operations to interpret properly.
|
||||
|
||||
## Related Functions
|
||||
|
||||
- `GetUnitData(unitToken)` - Get all fields at once
|
||||
- `GetUnitField(unitToken, fieldName)` - Get a single field efficiently
|
||||
- `GetSpellModifiers(spellId, modifierType)` - Get spell modifiers
|
||||
- `GetItemStats(itemId)` - Get item data
|
||||
- `GetSpellRec(spellId)` - Get spell data
|
||||
@@ -28,6 +28,8 @@ set(SOURCE_FILES
|
||||
items.cpp
|
||||
dbc_fields.hpp
|
||||
dbc_fields.cpp
|
||||
unit_fields.hpp
|
||||
unit_fields.cpp
|
||||
)
|
||||
|
||||
add_library(${DLL_NAME} SHARED ${SOURCE_FILES})
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ namespace game {
|
||||
int GetSpellModifier(const SpellRec *spellRec, SpellModOp spellMod) {
|
||||
using Spell_C_GetSpellModifiersT = void (__fastcall *)(const SpellRec *spellRec, int *returnVal, SpellModOp modOp);
|
||||
|
||||
auto const getModifiers = reinterpret_cast<Spell_C_GetSpellModifiersT>(Offsets::Spell_C_GetSpellModifiers);
|
||||
auto const getModifiers = reinterpret_cast<Spell_C_GetSpellModifiersT>(Offsets::Spell_C_ApplySpellModifiers);
|
||||
auto modificationPercentage = 0;
|
||||
|
||||
getModifiers(spellRec, &modificationPercentage, spellMod);
|
||||
|
||||
+19
-4
@@ -93,6 +93,7 @@ namespace Nampower {
|
||||
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::PatchRaw> gCastbarPatch;
|
||||
std::unique_ptr<hadesmem::PatchDetour<ISceneEndT>> gIEndSceneDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<Spell_C_GetAutoRepeatingSpellT>> gSpell_C_GetAutoRepeatingSpellDetour;
|
||||
@@ -539,6 +540,13 @@ namespace Nampower {
|
||||
// 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;
|
||||
@@ -1120,9 +1128,6 @@ namespace Nampower {
|
||||
gGetSpellSlotFromLuaDetour = createHook<GetSpellSlotFromLuaT>(process, Offsets::GetSpellSlotFromLua,
|
||||
&GetSpellSlotFromLuaHook);
|
||||
|
||||
// gCGPlayer_C_OnAttackIconPressedDetour = createHook<CGPlayer_C_OnAttackIconPressedT>(process,
|
||||
// Offsets::CGPlayer_C_OnAttackIconPressed,
|
||||
// &CGPlayer_C_OnAttackIconPressedHook);
|
||||
}
|
||||
|
||||
void SpellVisualsInitializeHook(hadesmem::PatchDetourBase *detour) {
|
||||
@@ -1176,7 +1181,8 @@ namespace Nampower {
|
||||
RegisterLuaFunction(getSpellSlotTypeIdForName, reinterpret_cast<uintptr_t *>(Script_GetSpellSlotTypeIdForName));
|
||||
|
||||
char channelStopCastingNextTick[] = "ChannelStopCastingNextTick";
|
||||
RegisterLuaFunction(channelStopCastingNextTick, reinterpret_cast<uintptr_t *>(Script_ChannelStopCastingNextTick));
|
||||
RegisterLuaFunction(channelStopCastingNextTick,
|
||||
reinterpret_cast<uintptr_t *>(Script_ChannelStopCastingNextTick));
|
||||
|
||||
char getNampowerVersion[] = "GetNampowerVersion";
|
||||
RegisterLuaFunction(getNampowerVersion, reinterpret_cast<uintptr_t *>(Script_GetNampowerVersion));
|
||||
@@ -1195,6 +1201,15 @@ namespace Nampower {
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
std::once_flag loadFlag;
|
||||
|
||||
@@ -93,6 +93,7 @@ namespace Nampower {
|
||||
using Spell_C_GetSpellCooldownT = int (__fastcall *)(uint32_t spellId, uint32_t isPetSpell,
|
||||
uint32_t *duration, uint64_t *startTime, uint32_t *enable);
|
||||
using Spell_C_IsSpellUsableT = int (__fastcall *)(const game::SpellRec *spellRec, uint32_t *usesManaReturn);
|
||||
using Spell_C_GetSpellModifiersT = void (__fastcall *)(const game::SpellRec *spellRec, int *returnVal, game::SpellModOp modOp);
|
||||
|
||||
using CGPlayer_C_OnAttackIconPressedT = int (__fastcall *)(uintptr_t *this_ptr, void *dummy_edx, uint64_t guid);
|
||||
|
||||
|
||||
@@ -104,7 +104,8 @@ enum class Offsets : std::uint32_t {
|
||||
Spell_C_GetSpellCooldown = 0X006E2EA0,
|
||||
Spell_C_IsSpellUsable = 0X006E3D60,
|
||||
Spell_C_GetDuration = 0X006EA000,
|
||||
Spell_C_GetSpellModifiers = 0x006e6af0,
|
||||
Spell_C_ApplySpellModifiers = 0x006e6af0,
|
||||
Spell_C_GetSpellModifierValues = 0x006e6b30,
|
||||
|
||||
CVarLookup = 0x0063DEC0,
|
||||
RegisterCVar = 0X0063DB90,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "offsets.hpp"
|
||||
#include "items.hpp"
|
||||
#include "dbc_fields.hpp"
|
||||
#include "unit_fields.hpp"
|
||||
#include "helper.hpp"
|
||||
#include <cstring>
|
||||
|
||||
@@ -697,6 +698,199 @@ namespace Nampower {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Script_GetUnitData(uintptr_t *luaState) {
|
||||
if (!lua_isstring(luaState, 1)) {
|
||||
lua_error(luaState, "Usage: GetUnitData(unitToken) - unitToken can be 'player', 'target', 'pet', etc., or a GUID string");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *unitToken = lua_tostring(luaState, 1);
|
||||
uint64_t guid;
|
||||
|
||||
// Check if it's a GUID string (starts with "0x" or "0X")
|
||||
if (strncmp(unitToken, "0x", 2) == 0 || strncmp(unitToken, "0X", 2) == 0) {
|
||||
guid = std::stoull(unitToken, nullptr, 16);
|
||||
} else {
|
||||
// Get GUID from unit token
|
||||
auto const getGUIDFromName = reinterpret_cast<GetGUIDFromNameT>(Offsets::GetGUIDFromName);
|
||||
guid = getGUIDFromName(unitToken);
|
||||
}
|
||||
|
||||
if (guid == 0) {
|
||||
lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get unit object pointer
|
||||
auto unit = game::GetObjectPtr(guid);
|
||||
if (!unit) {
|
||||
lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get unit fields (offset 68 from unit pointer)
|
||||
auto *unitFields = *reinterpret_cast<game::UnitFields **>(unit + 68);
|
||||
if (!unitFields) {
|
||||
lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create new table
|
||||
lua_newtable(luaState);
|
||||
|
||||
// Push all simple fields using descriptors
|
||||
PushFieldsToLua(luaState, unitFields, unitFieldsFields, unitFieldsFieldsCount);
|
||||
|
||||
// Push all array fields using descriptors
|
||||
PushArrayFieldsToLua(luaState, unitFields, unitFieldsArrayFields, unitFieldsArrayFieldsCount);
|
||||
|
||||
return 1; // Return the table
|
||||
}
|
||||
|
||||
uint32_t Script_GetUnitField(uintptr_t *luaState) {
|
||||
if (!lua_isstring(luaState, 1) || !lua_isstring(luaState, 2)) {
|
||||
lua_error(luaState, "Usage: GetUnitField(unitToken, fieldName) - unitToken can be 'player', 'target', 'pet', etc., or a GUID string");
|
||||
return 0;
|
||||
}
|
||||
|
||||
InitializeUnitFieldMaps();
|
||||
|
||||
const char *unitToken = lua_tostring(luaState, 1);
|
||||
const char *fieldName = lua_tostring(luaState, 2);
|
||||
|
||||
uint64_t guid;
|
||||
|
||||
// Check if it's a GUID string (starts with "0x" or "0X")
|
||||
if (strncmp(unitToken, "0x", 2) == 0 || strncmp(unitToken, "0X", 2) == 0) {
|
||||
guid = std::stoull(unitToken, nullptr, 16);
|
||||
} else {
|
||||
// Get GUID from unit token
|
||||
auto const getGUIDFromName = reinterpret_cast<GetGUIDFromNameT>(Offsets::GetGUIDFromName);
|
||||
guid = getGUIDFromName(unitToken);
|
||||
}
|
||||
|
||||
if (guid == 0) {
|
||||
lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get unit object pointer
|
||||
auto unit = game::GetObjectPtr(guid);
|
||||
if (!unit) {
|
||||
lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get unit fields (offset 68 from unit pointer)
|
||||
auto *unitFields = *reinterpret_cast<game::UnitFields **>(unit + 68);
|
||||
if (!unitFields) {
|
||||
lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// O(1) hash table lookup for simple fields
|
||||
auto simpleIt = unitFieldsFieldMap.find(fieldName);
|
||||
if (simpleIt != unitFieldsFieldMap.end()) {
|
||||
size_t i = simpleIt->second;
|
||||
const char *fieldPtr = reinterpret_cast<const char *>(unitFields) + unitFieldsFields[i].offset;
|
||||
|
||||
switch (unitFieldsFields[i].type) {
|
||||
case FieldType::UINT32:
|
||||
lua_pushnumber(luaState, *reinterpret_cast<const uint32_t *>(fieldPtr));
|
||||
return 1;
|
||||
case FieldType::UINT64:
|
||||
lua_pushnumber(luaState, static_cast<double>(*reinterpret_cast<const uint64_t *>(fieldPtr)));
|
||||
return 1;
|
||||
case FieldType::FLOAT:
|
||||
lua_pushnumber(luaState, *reinterpret_cast<const float *>(fieldPtr));
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// O(1) hash table lookup for array fields
|
||||
auto arrayIt = unitFieldsArrayFieldMap.find(fieldName);
|
||||
if (arrayIt != unitFieldsArrayFieldMap.end()) {
|
||||
size_t i = arrayIt->second;
|
||||
const auto &field = unitFieldsArrayFields[i];
|
||||
lua_newtable(luaState);
|
||||
|
||||
const char *fieldPtr = reinterpret_cast<const char *>(unitFields) + field.offset;
|
||||
|
||||
for (size_t j = 0; j < field.count; ++j) {
|
||||
lua_pushnumber(luaState, j + 1);
|
||||
|
||||
switch (field.type) {
|
||||
case FieldType::UINT32:
|
||||
lua_pushnumber(luaState, reinterpret_cast<const uint32_t *>(fieldPtr)[j]);
|
||||
break;
|
||||
case FieldType::FLOAT:
|
||||
lua_pushnumber(luaState, reinterpret_cast<const float *>(fieldPtr)[j]);
|
||||
break;
|
||||
default:
|
||||
lua_pushnumber(luaState, 0);
|
||||
break;
|
||||
}
|
||||
lua_settable(luaState, -3);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Field not found
|
||||
lua_error(luaState, "Unknown field name");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Script_GetSpellModifiers(uintptr_t *luaState) {
|
||||
if (!lua_isnumber(luaState, 1) || !lua_isnumber(luaState, 2)) {
|
||||
lua_error(luaState, "Usage: GetSpellModifiers(spellId, modifierType) - modifierType: 0=DAMAGE, 1=DURATION, 2=THREAT, 3=ATTACK_POWER, 4=CHARGES, 5=RANGE, 6=RADIUS, 7=CRITICAL_CHANCE, 8=ALL_EFFECTS, 9=NOT_LOSE_CASTING_TIME, 10=CASTING_TIME, 11=COOLDOWN, 12=SPEED, 14=COST, 15=CRIT_DAMAGE_BONUS, 16=RESIST_MISS_CHANCE, 17=JUMP_TARGETS, 18=CHANCE_OF_SUCCESS, 19=ACTIVATION_TIME, 20=EFFECT_PAST_FIRST, 21=CASTING_TIME_OLD, 22=DOT, 23=HASTE, 24=SPELL_BONUS_DAMAGE, 27=MULTIPLE_VALUE, 28=RESIST_DISPEL_CHANCE");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t spellId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
|
||||
int modifierType = static_cast<int>(lua_tonumber(luaState, 2));
|
||||
|
||||
// Validate modifier type
|
||||
if (modifierType < 0 || modifierType >= game::MAX_SPELLMOD) {
|
||||
lua_error(luaState, "Invalid modifier type. Must be between 0 and 28.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get spell info
|
||||
auto spell = game::GetSpellInfo(spellId);
|
||||
if (!spell) {
|
||||
lua_error(luaState, "Invalid spell id");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Call Spell_C_GetSpellModifierValues
|
||||
using Spell_C_GetSpellModifierValuesT = uint32_t (__fastcall *)(game::SpellRec *spellRec, int mod, int *flatMod, uint32_t *percentMod);
|
||||
auto const getModifierValues = reinterpret_cast<Spell_C_GetSpellModifierValuesT>(Offsets::Spell_C_GetSpellModifierValues);
|
||||
|
||||
int flatModificationValue = 0;
|
||||
uint32_t percentModificationValue = 0;
|
||||
|
||||
auto ret = getModifierValues(const_cast<game::SpellRec *>(spell), modifierType, &flatModificationValue, &percentModificationValue);
|
||||
|
||||
// show flat modifications
|
||||
lua_pushnumber(luaState, flatModificationValue);
|
||||
|
||||
if (flatModificationValue != 0 && percentModificationValue >= 100) {
|
||||
percentModificationValue -= 100;
|
||||
} else if (modifierType == 9 && percentModificationValue >= 100) {
|
||||
percentModificationValue -= 100;
|
||||
}
|
||||
|
||||
// show percent modifications
|
||||
lua_pushnumber(luaState, percentModificationValue);
|
||||
|
||||
// show return
|
||||
lua_pushnumber(luaState, ret);
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
uint32_t GetSpellSlotFromLuaHook(hadesmem::PatchDetourBase *detour, int param_1, uint32_t *slot, uint32_t *type) {
|
||||
// Get Lua state pointer
|
||||
uintptr_t *luaState = GetLuaStatePtr();
|
||||
|
||||
@@ -42,6 +42,12 @@ namespace Nampower {
|
||||
|
||||
uint32_t Script_GetSpellRecField(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_GetUnitData(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_GetUnitField(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_GetSpellModifiers(uintptr_t *luaState);
|
||||
|
||||
uint32_t GetSpellSlotFromLuaHook(hadesmem::PatchDetourBase *detour, int param_1, uint32_t *slot, uint32_t *type);
|
||||
|
||||
bool RunQueuedScript(int priority);
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// Created by pmacc on 1/8/2025.
|
||||
//
|
||||
|
||||
#include "unit_fields.hpp"
|
||||
#include "game.hpp"
|
||||
|
||||
namespace Nampower {
|
||||
// Hash tables for O(1) field lookups
|
||||
std::unordered_map<std::string, size_t> unitFieldsFieldMap;
|
||||
std::unordered_map<std::string, size_t> unitFieldsArrayFieldMap;
|
||||
static bool unitFieldMapsInitialized = false;
|
||||
|
||||
// UnitFields field descriptors
|
||||
const FieldDescriptor unitFieldsFields[] = {
|
||||
{"charm", offsetof(game::UnitFields, charm), FieldType::UINT64},
|
||||
{"summon", offsetof(game::UnitFields, summon), FieldType::UINT64},
|
||||
{"charmedBy", offsetof(game::UnitFields, charmedBy), FieldType::UINT64},
|
||||
{"summonedBy", offsetof(game::UnitFields, summonedBy), FieldType::UINT64},
|
||||
{"createdBy", offsetof(game::UnitFields, createdBy), FieldType::UINT64},
|
||||
{"target", offsetof(game::UnitFields, target), FieldType::UINT64},
|
||||
{"persuaded", offsetof(game::UnitFields, persuaded), FieldType::UINT64},
|
||||
{"channelObject", offsetof(game::UnitFields, channelObject), FieldType::UINT64},
|
||||
{"health", offsetof(game::UnitFields, health), FieldType::UINT32},
|
||||
{"power1", offsetof(game::UnitFields, power1), FieldType::UINT32},
|
||||
{"power2", offsetof(game::UnitFields, power2), FieldType::UINT32},
|
||||
{"power3", offsetof(game::UnitFields, power3), FieldType::UINT32},
|
||||
{"power4", offsetof(game::UnitFields, power4), FieldType::UINT32},
|
||||
{"power5", offsetof(game::UnitFields, power5), FieldType::UINT32},
|
||||
{"maxHealth", offsetof(game::UnitFields, maxHealth), FieldType::UINT32},
|
||||
{"maxPower1", offsetof(game::UnitFields, maxPower1), FieldType::UINT32},
|
||||
{"maxPower2", offsetof(game::UnitFields, maxPower2), FieldType::UINT32},
|
||||
{"maxPower3", offsetof(game::UnitFields, maxPower3), FieldType::UINT32},
|
||||
{"maxPower4", offsetof(game::UnitFields, maxPower4), FieldType::UINT32},
|
||||
{"maxPower5", offsetof(game::UnitFields, maxPower5), FieldType::UINT32},
|
||||
{"level", offsetof(game::UnitFields, level), FieldType::UINT32},
|
||||
{"factionTemplate", offsetof(game::UnitFields, factionTemplate), FieldType::UINT32},
|
||||
{"bytes0", offsetof(game::UnitFields, bytes0), FieldType::UINT32},
|
||||
{"flags", offsetof(game::UnitFields, flags), FieldType::UINT32},
|
||||
{"auraState", offsetof(game::UnitFields, auraState), FieldType::UINT32},
|
||||
{"baseAttackTime", offsetof(game::UnitFields, baseAttackTime), FieldType::UINT32},
|
||||
{"offhandAttackTime", offsetof(game::UnitFields, offhandAttackTime), FieldType::UINT32},
|
||||
{"rangedAttackTime", offsetof(game::UnitFields, rangedAttackTime), FieldType::UINT32},
|
||||
{"boundingRadius", offsetof(game::UnitFields, boundingRadius), FieldType::FLOAT},
|
||||
{"combatReach", offsetof(game::UnitFields, combatReach), FieldType::FLOAT},
|
||||
{"displayId", offsetof(game::UnitFields, displayId), FieldType::UINT32},
|
||||
{"nativeDisplayId", offsetof(game::UnitFields, nativeDisplayId), FieldType::UINT32},
|
||||
{"mountDisplayId", offsetof(game::UnitFields, mountDisplayId), FieldType::UINT32},
|
||||
{"minDamage", offsetof(game::UnitFields, minDamage), FieldType::FLOAT},
|
||||
{"maxDamage", offsetof(game::UnitFields, maxDamage), FieldType::FLOAT},
|
||||
{"minOffhandDamage", offsetof(game::UnitFields, minOffhandDamage), FieldType::FLOAT},
|
||||
{"maxOffhandDamage", offsetof(game::UnitFields, maxOffhandDamage), FieldType::FLOAT},
|
||||
{"bytes1", offsetof(game::UnitFields, bytes1), FieldType::UINT32},
|
||||
{"petNumber", offsetof(game::UnitFields, petNumber), FieldType::UINT32},
|
||||
{"petNameTimestamp", offsetof(game::UnitFields, petNameTimestamp), FieldType::UINT32},
|
||||
{"petExperience", offsetof(game::UnitFields, petExperience), FieldType::UINT32},
|
||||
{"petNextLevelExp", offsetof(game::UnitFields, petNextLevelExp), FieldType::UINT32},
|
||||
{"dynamicFlags", offsetof(game::UnitFields, dynamicFlags), FieldType::UINT32},
|
||||
{"channelSpell", offsetof(game::UnitFields, channelSpell), FieldType::UINT32},
|
||||
{"modCastSpeed", offsetof(game::UnitFields, modCastSpeed), FieldType::FLOAT},
|
||||
{"createdBySpell", offsetof(game::UnitFields, createdBySpell), FieldType::UINT32},
|
||||
{"npcFlags", offsetof(game::UnitFields, npcFlags), FieldType::UINT32},
|
||||
{"npcEmoteState", offsetof(game::UnitFields, npcEmoteState), FieldType::UINT32},
|
||||
{"trainingPoints", offsetof(game::UnitFields, trainingPoints), FieldType::UINT32},
|
||||
{"stat0", offsetof(game::UnitFields, stat0), FieldType::UINT32},
|
||||
{"stat1", offsetof(game::UnitFields, stat1), FieldType::UINT32},
|
||||
{"stat2", offsetof(game::UnitFields, stat2), FieldType::UINT32},
|
||||
{"stat3", offsetof(game::UnitFields, stat3), FieldType::UINT32},
|
||||
{"stat4", offsetof(game::UnitFields, stat4), FieldType::UINT32},
|
||||
{"baseMana", offsetof(game::UnitFields, baseMana), FieldType::UINT32},
|
||||
{"baseHealth", offsetof(game::UnitFields, baseHealth), FieldType::UINT32},
|
||||
{"bytes2", offsetof(game::UnitFields, bytes2), FieldType::UINT32},
|
||||
{"attackPower", offsetof(game::UnitFields, attackPower), FieldType::UINT32},
|
||||
{"attackPowerMods", offsetof(game::UnitFields, attackPowerMods), FieldType::UINT32},
|
||||
{"attackPowerMultiplier", offsetof(game::UnitFields, attackPowerMultiplier), FieldType::FLOAT},
|
||||
{"rangedAttackPower", offsetof(game::UnitFields, rangedAttackPower), FieldType::UINT32},
|
||||
{"rangedAttackPowerMods", offsetof(game::UnitFields, rangedAttackPowerMods), FieldType::UINT32},
|
||||
{"rangedAttackPowerMultiplier", offsetof(game::UnitFields, rangedAttackPowerMultiplier), FieldType::FLOAT},
|
||||
{"minRangedDamage", offsetof(game::UnitFields, minRangedDamage), FieldType::FLOAT},
|
||||
{"maxRangedDamage", offsetof(game::UnitFields, maxRangedDamage), FieldType::FLOAT},
|
||||
};
|
||||
const size_t unitFieldsFieldsCount = sizeof(unitFieldsFields) / sizeof(unitFieldsFields[0]);
|
||||
|
||||
const ArrayFieldDescriptor unitFieldsArrayFields[] = {
|
||||
{"virtualItemDisplay", offsetof(game::UnitFields, virtualItemDisplay), FieldType::UINT32, 3},
|
||||
{"virtualItemInfo", offsetof(game::UnitFields, virtualItemInfo), FieldType::UINT32, 6},
|
||||
{"aura", offsetof(game::UnitFields, aura), FieldType::UINT32, 48},
|
||||
{"auraFlags", offsetof(game::UnitFields, auraFlags), FieldType::UINT32, 6},
|
||||
{"auraLevels", offsetof(game::UnitFields, auraLevels), FieldType::UINT32, 12},
|
||||
{"auraApplications", offsetof(game::UnitFields, auraApplications), FieldType::UINT32, 12},
|
||||
{"resistances", offsetof(game::UnitFields, resistances), FieldType::UINT32, 7},
|
||||
{"powerCostModifier", offsetof(game::UnitFields, powerCostModifier), FieldType::FLOAT, 7},
|
||||
{"powerCostMultiplier", offsetof(game::UnitFields, powerCostMultiplier), FieldType::FLOAT, 7},
|
||||
};
|
||||
const size_t unitFieldsArrayFieldsCount = sizeof(unitFieldsArrayFields) / sizeof(unitFieldsArrayFields[0]);
|
||||
|
||||
void InitializeUnitFieldMaps() {
|
||||
if (unitFieldMapsInitialized) return;
|
||||
|
||||
// Build hash maps for O(1) lookups
|
||||
for (size_t i = 0; i < unitFieldsFieldsCount; ++i) {
|
||||
unitFieldsFieldMap[unitFieldsFields[i].name] = i;
|
||||
}
|
||||
for (size_t i = 0; i < unitFieldsArrayFieldsCount; ++i) {
|
||||
unitFieldsArrayFieldMap[unitFieldsArrayFields[i].name] = i;
|
||||
}
|
||||
|
||||
unitFieldMapsInitialized = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// Created by pmacc on 1/8/2025.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "main.hpp"
|
||||
#include "dbc_fields.hpp"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <cstddef>
|
||||
|
||||
namespace Nampower {
|
||||
// UnitFields field descriptors
|
||||
extern const FieldDescriptor unitFieldsFields[];
|
||||
extern const size_t unitFieldsFieldsCount;
|
||||
extern const ArrayFieldDescriptor unitFieldsArrayFields[];
|
||||
extern const size_t unitFieldsArrayFieldsCount;
|
||||
|
||||
// Hash tables for O(1) field lookups
|
||||
extern std::unordered_map<std::string, size_t> unitFieldsFieldMap;
|
||||
extern std::unordered_map<std::string, size_t> unitFieldsArrayFieldMap;
|
||||
|
||||
// Initialize unit fields maps (call once before using field lookups)
|
||||
void InitializeUnitFieldMaps();
|
||||
}
|
||||
Reference in New Issue
Block a user