mirror of
https://github.com/brues-code/nampower.git
synced 2026-10-12 00:20:01 +00:00
Merge pull request 'add more access to spell/item/unit data' (#1) from beta into master
Reviewed-on: https://gitea.com/avitasia/nampower/pulls/1
This commit is contained in:
+144
@@ -0,0 +1,144 @@
|
||||
# DBC Field Names
|
||||
|
||||
Related structs live in `nampower/game.hpp` (notably `SpellRec` and `ItemStats_C`).
|
||||
|
||||
## ItemStats_C Fields (alphabetical)
|
||||
- `allowableClass`
|
||||
- `allowableRace`
|
||||
- `ammoType`
|
||||
- `area`
|
||||
- `bagFamily`
|
||||
- `block`
|
||||
- `bonding`
|
||||
- `buyPrice`
|
||||
- `class`
|
||||
- `containerSlots`
|
||||
- `delay`
|
||||
- `displayInfoID`
|
||||
- `duration`
|
||||
- `flags`
|
||||
- `inventoryType`
|
||||
- `itemLevel`
|
||||
- `itemSet`
|
||||
- `languageID`
|
||||
- `lockID`
|
||||
- `map`
|
||||
- `material`
|
||||
- `maxCount`
|
||||
- `maxDurability`
|
||||
- `pageMaterial`
|
||||
- `pageText`
|
||||
- `quality`
|
||||
- `randomProperty`
|
||||
- `rangedModRange`
|
||||
- `requiredCityRank`
|
||||
- `requiredHonorRank`
|
||||
- `requiredLevel`
|
||||
- `requiredRep`
|
||||
- `requiredRepRank`
|
||||
- `requiredSkill`
|
||||
- `requiredSkillRank`
|
||||
- `requiredSpell`
|
||||
- `sellPrice`
|
||||
- `sheatheType`
|
||||
- `stackable`
|
||||
- `startQuestID`
|
||||
- `subclass`
|
||||
|
||||
### ItemStats_C Array Fields (alphabetical)
|
||||
- `bonusAmount`
|
||||
- `bonusStat`
|
||||
- `damageType`
|
||||
- `maxDamage`
|
||||
- `minDamage`
|
||||
- `resistances`
|
||||
- `spellCategory`
|
||||
- `spellCategoryCooldown`
|
||||
- `spellCharges`
|
||||
- `spellCooldown`
|
||||
- `spellID`
|
||||
- `spellTrigger`
|
||||
|
||||
## SpellRec Fields (alphabetical)
|
||||
- `activeIconID`
|
||||
- `attributes`
|
||||
- `attributesEx`
|
||||
- `attributesEx2`
|
||||
- `attributesEx3`
|
||||
- `attributesEx4`
|
||||
- `auraInterruptFlags`
|
||||
- `baseLevel`
|
||||
- `castUI`
|
||||
- `category`
|
||||
- `categoryRecoveryTime`
|
||||
- `casterAuraState`
|
||||
- `castingTimeIndex`
|
||||
- `channelInterruptFlags`
|
||||
- `dispel`
|
||||
- `dmgClass`
|
||||
- `durationIndex`
|
||||
- `equippedItemClass`
|
||||
- `equippedItemInventoryTypeMask`
|
||||
- `equippedItemSubClassMask`
|
||||
- `id`
|
||||
- `interruptFlags`
|
||||
- `manaCost`
|
||||
- `manaCostPercentage`
|
||||
- `manaCostPerlevel`
|
||||
- `manaPerSecond`
|
||||
- `manaPerSecondPerLevel`
|
||||
- `maxAffectedTargets`
|
||||
- `maxLevel`
|
||||
- `maxTargetLevel`
|
||||
- `mechanic`
|
||||
- `minFactionId`
|
||||
- `minReputation`
|
||||
- `modalNextSpell`
|
||||
- `powerType`
|
||||
- `preventionType`
|
||||
- `procChance`
|
||||
- `procCharges`
|
||||
- `procFlags`
|
||||
- `rangeIndex`
|
||||
- `recoveryTime`
|
||||
- `requiredAuraVision`
|
||||
- `school`
|
||||
- `spellFamilyFlags`
|
||||
- `spellFamilyName`
|
||||
- `spellIconID`
|
||||
- `spellLevel`
|
||||
- `spellPriority`
|
||||
- `spellVisual`
|
||||
- `spellVisual2`
|
||||
- `stackAmount`
|
||||
- `stances`
|
||||
- `stancesNot`
|
||||
- `startRecoveryCategory`
|
||||
- `startRecoveryTime`
|
||||
- `targetAuraState`
|
||||
- `targetCreatureType`
|
||||
- `targets`
|
||||
|
||||
### SpellRec Array Fields (alphabetical)
|
||||
- `dmgMultiplier`
|
||||
- `effect`
|
||||
- `effectAmplitude`
|
||||
- `effectApplyAuraName`
|
||||
- `effectBaseDice`
|
||||
- `effectBasePoints`
|
||||
- `effectChainTarget`
|
||||
- `effectDicePerLevel`
|
||||
- `effectDieSides`
|
||||
- `effectImplicitTargetA`
|
||||
- `effectImplicitTargetB`
|
||||
- `effectItemType`
|
||||
- `effectMechanic`
|
||||
- `effectMiscValue`
|
||||
- `effectMultipleValue`
|
||||
- `effectPointsPerComboPoint`
|
||||
- `effectRadiusIndex`
|
||||
- `effectRealPointsPerLevel`
|
||||
- `effectTriggerSpell`
|
||||
- `reagent`
|
||||
- `reagentCount`
|
||||
- `totem`
|
||||
@@ -98,8 +98,206 @@ SET NP_TargetingQueueWindowMs "1000"
|
||||
|
||||
- `NP_NameplateDistance` - The distance in yards to display nameplates. Defaults to whatever was set by the game or vanilla tweaks.
|
||||
|
||||
### Existing Lua Changes
|
||||
|
||||
#### Improved flexibility on spellbook Lua functions
|
||||
These built-in Lua spell APIs now accept any of the following as their first argument: 1) spell slot (original behavior), 2) spell name, or 3) `spellId:number`.
|
||||
|
||||
Name and spellId lookups are cached internally and validated against current spellbook contents before reuse so you don't have to worry about performance implications or issues after respec'ing.
|
||||
|
||||
See examples below for differences in how BOOKTYPE works.
|
||||
|
||||
Functions: `GetSpellTexture`, `GetSpellName`, `GetSpellCooldown`, `GetSpellAutocast`, `ToggleSpellAutocast`, `PickupSpell`, `CastSpell`, `IsCurrentCast`, `IsSpellPassive`.
|
||||
|
||||
Examples:
|
||||
```
|
||||
/run print(GetSpellTexture(1, "spell")) -- booktype required
|
||||
/run print(GetSpellTexture("spellId:25978")) -- defaults to BOOKTYPE_SPELL
|
||||
/run print(GetSpellTexture("spellId:6268", "pet")) -- "pet" needed for pet spells
|
||||
/run print(GetSpellTexture("Fireball")) -- name search
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
+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
|
||||
@@ -24,6 +24,12 @@ set(SOURCE_FILES
|
||||
spellevents.cpp
|
||||
scripts.hpp
|
||||
scripts.cpp
|
||||
items.hpp
|
||||
items.cpp
|
||||
dbc_fields.hpp
|
||||
dbc_fields.cpp
|
||||
unit_fields.hpp
|
||||
unit_fields.cpp
|
||||
)
|
||||
|
||||
add_library(${DLL_NAME} SHARED ${SOURCE_FILES})
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
//
|
||||
// Created by pmacc on 1/8/2025.
|
||||
//
|
||||
|
||||
#include "dbc_fields.hpp"
|
||||
#include "offsets.hpp"
|
||||
|
||||
namespace Nampower {
|
||||
// Hash tables for O(1) field lookups
|
||||
std::unordered_map<std::string, size_t> itemStatsFieldMap;
|
||||
std::unordered_map<std::string, size_t> itemStatsArrayFieldMap;
|
||||
std::unordered_map<std::string, size_t> spellRecFieldMap;
|
||||
std::unordered_map<std::string, size_t> spellRecArrayFieldMap;
|
||||
static bool fieldMapsInitialized = false;
|
||||
|
||||
// ItemStats_C field descriptors
|
||||
const FieldDescriptor itemStatsFields[] = {
|
||||
{"class", offsetof(game::ItemStats_C, m_class), FieldType::INT32},
|
||||
{"subclass", offsetof(game::ItemStats_C, m_subclass), FieldType::INT32},
|
||||
{"displayInfoID", offsetof(game::ItemStats_C, m_displayInfoID), FieldType::INT32},
|
||||
{"quality", offsetof(game::ItemStats_C, m_quality), FieldType::INT32},
|
||||
{"flags", offsetof(game::ItemStats_C, m_flags), FieldType::INT32},
|
||||
{"buyPrice", offsetof(game::ItemStats_C, m_buyPrice), FieldType::INT32},
|
||||
{"sellPrice", offsetof(game::ItemStats_C, m_sellPrice), FieldType::INT32},
|
||||
{"inventoryType", offsetof(game::ItemStats_C, m_inventoryType), FieldType::INT32},
|
||||
{"allowableClass", offsetof(game::ItemStats_C, m_allowableClass), FieldType::INT32},
|
||||
{"allowableRace", offsetof(game::ItemStats_C, m_allowableRace), FieldType::INT32},
|
||||
{"itemLevel", offsetof(game::ItemStats_C, m_itemLevel), FieldType::INT32},
|
||||
{"requiredLevel", offsetof(game::ItemStats_C, m_requiredLevel), FieldType::INT32},
|
||||
{"requiredSkill", offsetof(game::ItemStats_C, m_requiredSkill), FieldType::INT32},
|
||||
{"requiredSkillRank", offsetof(game::ItemStats_C, m_requiredSkillRank), FieldType::INT32},
|
||||
{"requiredSpell", offsetof(game::ItemStats_C, m_requiredSpell), FieldType::INT32},
|
||||
{"requiredHonorRank", offsetof(game::ItemStats_C, m_requiredHonorRank), FieldType::INT32},
|
||||
{"requiredCityRank", offsetof(game::ItemStats_C, m_requiredCityRank), FieldType::INT32},
|
||||
{"requiredRep", offsetof(game::ItemStats_C, m_requiredRep), FieldType::INT32},
|
||||
{"requiredRepRank", offsetof(game::ItemStats_C, m_requiredRepRank), FieldType::INT32},
|
||||
{"maxCount", offsetof(game::ItemStats_C, m_maxCount), FieldType::INT32},
|
||||
{"stackable", offsetof(game::ItemStats_C, m_stackable), FieldType::INT32},
|
||||
{"containerSlots", offsetof(game::ItemStats_C, m_containerSlots), FieldType::INT32},
|
||||
{"delay", offsetof(game::ItemStats_C, m_delay), FieldType::INT32},
|
||||
{"ammoType", offsetof(game::ItemStats_C, m_ammoType), FieldType::INT32},
|
||||
{"rangedModRange", offsetof(game::ItemStats_C, m_rangedModRange), FieldType::INT32},
|
||||
{"bonding", offsetof(game::ItemStats_C, m_bonding), FieldType::INT32},
|
||||
{"pageText", offsetof(game::ItemStats_C, m_pageText), FieldType::INT32},
|
||||
{"languageID", offsetof(game::ItemStats_C, m_languageID), FieldType::INT32},
|
||||
{"pageMaterial", offsetof(game::ItemStats_C, m_pageMaterial), FieldType::INT32},
|
||||
{"startQuestID", offsetof(game::ItemStats_C, m_startQuestID), FieldType::INT32},
|
||||
{"lockID", offsetof(game::ItemStats_C, m_lockID), FieldType::INT32},
|
||||
{"material", offsetof(game::ItemStats_C, m_material), FieldType::INT32},
|
||||
{"sheatheType", offsetof(game::ItemStats_C, m_sheatheType), FieldType::INT32},
|
||||
{"randomProperty", offsetof(game::ItemStats_C, m_randomProperty), FieldType::INT32},
|
||||
{"block", offsetof(game::ItemStats_C, m_block), FieldType::INT32},
|
||||
{"itemSet", offsetof(game::ItemStats_C, m_itemSet), FieldType::INT32},
|
||||
{"maxDurability", offsetof(game::ItemStats_C, m_maxDurability), FieldType::INT32},
|
||||
{"area", offsetof(game::ItemStats_C, m_area), FieldType::INT32},
|
||||
{"map", offsetof(game::ItemStats_C, m_map), FieldType::INT32},
|
||||
{"duration", offsetof(game::ItemStats_C, m_duration), FieldType::INT32},
|
||||
{"bagFamily", offsetof(game::ItemStats_C, m_bagFamily), FieldType::INT32},
|
||||
};
|
||||
const size_t itemStatsFieldsCount = sizeof(itemStatsFields) / sizeof(itemStatsFields[0]);
|
||||
|
||||
const ArrayFieldDescriptor itemStatsArrayFields[] = {
|
||||
{"bonusStat", offsetof(game::ItemStats_C, m_bonusStat), FieldType::INT32, 10},
|
||||
{"bonusAmount", offsetof(game::ItemStats_C, m_bonusAmount), FieldType::INT32, 10},
|
||||
{"minDamage", offsetof(game::ItemStats_C, m_minDamage), FieldType::FLOAT, 5},
|
||||
{"maxDamage", offsetof(game::ItemStats_C, m_maxDamage), FieldType::FLOAT, 5},
|
||||
{"damageType", offsetof(game::ItemStats_C, m_damageType), FieldType::INT32, 5},
|
||||
{"resistances", offsetof(game::ItemStats_C, m_resistances), FieldType::INT32, 7},
|
||||
{"spellID", offsetof(game::ItemStats_C, m_spellID), FieldType::INT32, 5},
|
||||
{"spellTrigger", offsetof(game::ItemStats_C, m_spellTrigger), FieldType::INT32, 5},
|
||||
{"spellCharges", offsetof(game::ItemStats_C, m_spellCharges), FieldType::INT32, 5},
|
||||
{"spellCooldown", offsetof(game::ItemStats_C, m_spellCooldown), FieldType::INT32, 5},
|
||||
{"spellCategory", offsetof(game::ItemStats_C, m_spellCategory), FieldType::INT32, 5},
|
||||
{"spellCategoryCooldown", offsetof(game::ItemStats_C, m_spellCategoryCooldown), FieldType::INT32, 5},
|
||||
};
|
||||
const size_t itemStatsArrayFieldsCount = sizeof(itemStatsArrayFields) / sizeof(itemStatsArrayFields[0]);
|
||||
|
||||
// SpellRec field descriptors
|
||||
const FieldDescriptor spellRecFields[] = {
|
||||
{"id", offsetof(game::SpellRec, Id), FieldType::UINT32},
|
||||
{"school", offsetof(game::SpellRec, School), FieldType::UINT32},
|
||||
{"category", offsetof(game::SpellRec, Category), FieldType::UINT32},
|
||||
{"castUI", offsetof(game::SpellRec, castUI), FieldType::UINT32},
|
||||
{"dispel", offsetof(game::SpellRec, Dispel), FieldType::UINT32},
|
||||
{"mechanic", offsetof(game::SpellRec, Mechanic), FieldType::UINT32},
|
||||
{"attributes", offsetof(game::SpellRec, Attributes), FieldType::UINT32},
|
||||
{"attributesEx", offsetof(game::SpellRec, AttributesEx), FieldType::UINT32},
|
||||
{"attributesEx2", offsetof(game::SpellRec, AttributesEx2), FieldType::UINT32},
|
||||
{"attributesEx3", offsetof(game::SpellRec, AttributesEx3), FieldType::UINT32},
|
||||
{"attributesEx4", offsetof(game::SpellRec, AttributesEx4), FieldType::UINT32},
|
||||
{"stances", offsetof(game::SpellRec, Stances), FieldType::UINT32},
|
||||
{"stancesNot", offsetof(game::SpellRec, StancesNot), FieldType::UINT32},
|
||||
{"targets", offsetof(game::SpellRec, Targets), FieldType::UINT32},
|
||||
{"targetCreatureType", offsetof(game::SpellRec, TargetCreatureType), FieldType::UINT32},
|
||||
{"requiresSpellFocus", offsetof(game::SpellRec, RequiresSpellFocus), FieldType::UINT32},
|
||||
{"casterAuraState", offsetof(game::SpellRec, CasterAuraState), FieldType::UINT32},
|
||||
{"targetAuraState", offsetof(game::SpellRec, TargetAuraState), FieldType::UINT32},
|
||||
{"castingTimeIndex", offsetof(game::SpellRec, CastingTimeIndex), FieldType::UINT32},
|
||||
{"recoveryTime", offsetof(game::SpellRec, RecoveryTime), FieldType::UINT32},
|
||||
{"categoryRecoveryTime", offsetof(game::SpellRec, CategoryRecoveryTime), FieldType::UINT32},
|
||||
{"interruptFlags", offsetof(game::SpellRec, InterruptFlags), FieldType::UINT32},
|
||||
{"auraInterruptFlags", offsetof(game::SpellRec, AuraInterruptFlags), FieldType::UINT32},
|
||||
{"channelInterruptFlags", offsetof(game::SpellRec, ChannelInterruptFlags), FieldType::UINT32},
|
||||
{"procFlags", offsetof(game::SpellRec, procFlags), FieldType::UINT32},
|
||||
{"procChance", offsetof(game::SpellRec, procChance), FieldType::UINT32},
|
||||
{"procCharges", offsetof(game::SpellRec, procCharges), FieldType::UINT32},
|
||||
{"maxLevel", offsetof(game::SpellRec, maxLevel), FieldType::UINT32},
|
||||
{"baseLevel", offsetof(game::SpellRec, baseLevel), FieldType::UINT32},
|
||||
{"spellLevel", offsetof(game::SpellRec, spellLevel), FieldType::UINT32},
|
||||
{"durationIndex", offsetof(game::SpellRec, DurationIndex), FieldType::UINT32},
|
||||
{"powerType", offsetof(game::SpellRec, powerType), FieldType::UINT32},
|
||||
{"manaCost", offsetof(game::SpellRec, manaCost), FieldType::UINT32},
|
||||
{"manaCostPerlevel", offsetof(game::SpellRec, manaCostPerlevel), FieldType::UINT32},
|
||||
{"manaPerSecond", offsetof(game::SpellRec, manaPerSecond), FieldType::UINT32},
|
||||
{"manaPerSecondPerLevel", offsetof(game::SpellRec, manaPerSecondPerLevel), FieldType::UINT32},
|
||||
{"rangeIndex", offsetof(game::SpellRec, rangeIndex), FieldType::UINT32},
|
||||
{"speed", offsetof(game::SpellRec, speed), FieldType::FLOAT},
|
||||
{"modalNextSpell", offsetof(game::SpellRec, modalNextSpell), FieldType::UINT32},
|
||||
{"stackAmount", offsetof(game::SpellRec, StackAmount), FieldType::UINT32},
|
||||
{"equippedItemClass", offsetof(game::SpellRec, EquippedItemClass), FieldType::INT32},
|
||||
{"equippedItemSubClassMask", offsetof(game::SpellRec, EquippedItemSubClassMask), FieldType::INT32},
|
||||
{"equippedItemInventoryTypeMask", offsetof(game::SpellRec, EquippedItemInventoryTypeMask), FieldType::INT32},
|
||||
{"spellVisual", offsetof(game::SpellRec, SpellVisual), FieldType::UINT32},
|
||||
{"spellVisual2", offsetof(game::SpellRec, SpellVisual2), FieldType::UINT32},
|
||||
{"spellIconID", offsetof(game::SpellRec, SpellIconID), FieldType::UINT32},
|
||||
{"activeIconID", offsetof(game::SpellRec, activeIconID), FieldType::UINT32},
|
||||
{"spellPriority", offsetof(game::SpellRec, spellPriority), FieldType::UINT32},
|
||||
{"manaCostPercentage", offsetof(game::SpellRec, ManaCostPercentage), FieldType::UINT32},
|
||||
{"startRecoveryCategory", offsetof(game::SpellRec, StartRecoveryCategory), FieldType::UINT32},
|
||||
{"startRecoveryTime", offsetof(game::SpellRec, StartRecoveryTime), FieldType::UINT32},
|
||||
{"maxTargetLevel", offsetof(game::SpellRec, MaxTargetLevel), FieldType::UINT32},
|
||||
{"spellFamilyName", offsetof(game::SpellRec, SpellFamilyName), FieldType::UINT32},
|
||||
{"spellFamilyFlags", offsetof(game::SpellRec, SpellFamilyFlags), FieldType::UINT64},
|
||||
{"maxAffectedTargets", offsetof(game::SpellRec, MaxAffectedTargets), FieldType::UINT32},
|
||||
{"dmgClass", offsetof(game::SpellRec, DmgClass), FieldType::UINT32},
|
||||
{"preventionType", offsetof(game::SpellRec, PreventionType), FieldType::UINT32},
|
||||
{"stanceBarOrder", offsetof(game::SpellRec, StanceBarOrder), FieldType::UINT32},
|
||||
{"minFactionId", offsetof(game::SpellRec, MinFactionId), FieldType::UINT32},
|
||||
{"minReputation", offsetof(game::SpellRec, MinReputation), FieldType::UINT32},
|
||||
{"requiredAuraVision", offsetof(game::SpellRec, RequiredAuraVision), FieldType::UINT32},
|
||||
};
|
||||
const size_t spellRecFieldsCount = sizeof(spellRecFields) / sizeof(spellRecFields[0]);
|
||||
|
||||
const ArrayFieldDescriptor spellRecArrayFields[] = {
|
||||
{"totem", offsetof(game::SpellRec, Totem), FieldType::UINT32, 2},
|
||||
{"reagent", offsetof(game::SpellRec, Reagent), FieldType::INT32, 8},
|
||||
{"reagentCount", offsetof(game::SpellRec, ReagentCount), FieldType::UINT32, 8},
|
||||
{"effect", offsetof(game::SpellRec, Effect), FieldType::UINT32, 3},
|
||||
{"effectDieSides", offsetof(game::SpellRec, EffectDieSides), FieldType::INT32, 3},
|
||||
{"effectBaseDice", offsetof(game::SpellRec, EffectBaseDice), FieldType::UINT32, 3},
|
||||
{"effectDicePerLevel", offsetof(game::SpellRec, EffectDicePerLevel), FieldType::FLOAT, 3},
|
||||
{"effectRealPointsPerLevel", offsetof(game::SpellRec, EffectRealPointsPerLevel), FieldType::FLOAT, 3},
|
||||
{"effectBasePoints", offsetof(game::SpellRec, EffectBasePoints), FieldType::INT32, 3},
|
||||
{"effectMechanic", offsetof(game::SpellRec, EffectMechanic), FieldType::UINT32, 3},
|
||||
{"effectImplicitTargetA", offsetof(game::SpellRec, EffectImplicitTargetA), FieldType::UINT32, 3},
|
||||
{"effectImplicitTargetB", offsetof(game::SpellRec, EffectImplicitTargetB), FieldType::UINT32, 3},
|
||||
{"effectRadiusIndex", offsetof(game::SpellRec, EffectRadiusIndex), FieldType::UINT32, 3},
|
||||
{"effectApplyAuraName", offsetof(game::SpellRec, EffectApplyAuraName), FieldType::UINT32, 3},
|
||||
{"effectAmplitude", offsetof(game::SpellRec, EffectAmplitude), FieldType::UINT32, 3},
|
||||
{"effectMultipleValue", offsetof(game::SpellRec, EffectMultipleValue), FieldType::FLOAT, 3},
|
||||
{"effectChainTarget", offsetof(game::SpellRec, EffectChainTarget), FieldType::UINT32, 3},
|
||||
{"effectItemType", offsetof(game::SpellRec, EffectItemType), FieldType::UINT32, 3},
|
||||
{"effectMiscValue", offsetof(game::SpellRec, EffectMiscValue), FieldType::INT32, 3},
|
||||
{"effectTriggerSpell", offsetof(game::SpellRec, EffectTriggerSpell), FieldType::UINT32, 3},
|
||||
{"effectPointsPerComboPoint", offsetof(game::SpellRec, EffectPointsPerComboPoint), FieldType::FLOAT, 3},
|
||||
{"dmgMultiplier", offsetof(game::SpellRec, DmgMultiplier), FieldType::FLOAT, 3},
|
||||
};
|
||||
const size_t spellRecArrayFieldsCount = sizeof(spellRecArrayFields) / sizeof(spellRecArrayFields[0]);
|
||||
|
||||
void InitializeFieldMaps() {
|
||||
if (fieldMapsInitialized) return;
|
||||
|
||||
// Build hash maps for O(1) lookups
|
||||
for (size_t i = 0; i < itemStatsFieldsCount; ++i) {
|
||||
itemStatsFieldMap[itemStatsFields[i].name] = i;
|
||||
}
|
||||
for (size_t i = 0; i < itemStatsArrayFieldsCount; ++i) {
|
||||
itemStatsArrayFieldMap[itemStatsArrayFields[i].name] = i;
|
||||
}
|
||||
for (size_t i = 0; i < spellRecFieldsCount; ++i) {
|
||||
spellRecFieldMap[spellRecFields[i].name] = i;
|
||||
}
|
||||
for (size_t i = 0; i < spellRecArrayFieldsCount; ++i) {
|
||||
spellRecArrayFieldMap[spellRecArrayFields[i].name] = i;
|
||||
}
|
||||
|
||||
fieldMapsInitialized = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
//
|
||||
// Created by pmacc on 1/8/2025.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "main.hpp"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <cstddef>
|
||||
|
||||
namespace Nampower {
|
||||
// Field type enumeration
|
||||
enum class FieldType {
|
||||
INT32,
|
||||
UINT32,
|
||||
FLOAT,
|
||||
STRING,
|
||||
UINT64
|
||||
};
|
||||
|
||||
// Field descriptor for simple fields
|
||||
struct FieldDescriptor {
|
||||
const char* name;
|
||||
size_t offset;
|
||||
FieldType type;
|
||||
};
|
||||
|
||||
// Field descriptor for array fields
|
||||
struct ArrayFieldDescriptor {
|
||||
const char* name;
|
||||
size_t offset;
|
||||
FieldType type;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
// ItemStats_C field descriptors
|
||||
extern const FieldDescriptor itemStatsFields[];
|
||||
extern const size_t itemStatsFieldsCount;
|
||||
extern const ArrayFieldDescriptor itemStatsArrayFields[];
|
||||
extern const size_t itemStatsArrayFieldsCount;
|
||||
|
||||
// SpellRec field descriptors
|
||||
extern const FieldDescriptor spellRecFields[];
|
||||
extern const size_t spellRecFieldsCount;
|
||||
extern const ArrayFieldDescriptor spellRecArrayFields[];
|
||||
extern const size_t spellRecArrayFieldsCount;
|
||||
|
||||
// Hash tables for O(1) field lookups
|
||||
extern std::unordered_map<std::string, size_t> itemStatsFieldMap;
|
||||
extern std::unordered_map<std::string, size_t> itemStatsArrayFieldMap;
|
||||
extern std::unordered_map<std::string, size_t> spellRecFieldMap;
|
||||
extern std::unordered_map<std::string, size_t> spellRecArrayFieldMap;
|
||||
|
||||
// Initialize field maps (call once before using field lookups)
|
||||
void InitializeFieldMaps();
|
||||
|
||||
// Forward declarations for Lua functions (defined in offsets.hpp/scripts.cpp)
|
||||
using lua_pushnumberT = void (__fastcall *)(uintptr_t *, double);
|
||||
using lua_pushstringT = void (__fastcall *)(uintptr_t *, char *);
|
||||
using lua_newtableT = void (__fastcall *)(uintptr_t *);
|
||||
using lua_settableT = void (__fastcall *)(uintptr_t *, int);
|
||||
|
||||
extern lua_pushnumberT lua_pushnumber;
|
||||
extern lua_pushstringT lua_pushstring;
|
||||
extern lua_newtableT lua_newtable;
|
||||
extern lua_settableT lua_settable;
|
||||
|
||||
// Helper template functions to push fields to Lua (inline for performance)
|
||||
template<typename T>
|
||||
inline void PushFieldsToLua(uintptr_t* luaState, const T* obj, const FieldDescriptor* fields, size_t fieldCount) {
|
||||
for (size_t i = 0; i < fieldCount; ++i) {
|
||||
const auto& field = fields[i];
|
||||
lua_pushstring(luaState, const_cast<char*>(field.name));
|
||||
|
||||
const char* fieldPtr = reinterpret_cast<const char*>(obj) + field.offset;
|
||||
|
||||
switch (field.type) {
|
||||
case FieldType::INT32:
|
||||
lua_pushnumber(luaState, *reinterpret_cast<const int32_t*>(fieldPtr));
|
||||
break;
|
||||
case FieldType::UINT32:
|
||||
lua_pushnumber(luaState, *reinterpret_cast<const uint32_t*>(fieldPtr));
|
||||
break;
|
||||
case FieldType::FLOAT:
|
||||
lua_pushnumber(luaState, *reinterpret_cast<const float*>(fieldPtr));
|
||||
break;
|
||||
case FieldType::UINT64:
|
||||
lua_pushnumber(luaState, static_cast<double>(*reinterpret_cast<const uint64_t*>(fieldPtr)));
|
||||
break;
|
||||
case FieldType::STRING: {
|
||||
const char* str = *reinterpret_cast<const char* const*>(fieldPtr);
|
||||
lua_pushstring(luaState, str ? const_cast<char*>(str) : const_cast<char*>(""));
|
||||
break;
|
||||
}
|
||||
}
|
||||
lua_settable(luaState, -3);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void PushArrayFieldsToLua(uintptr_t* luaState, const T* obj, const ArrayFieldDescriptor* fields, size_t fieldCount) {
|
||||
for (size_t i = 0; i < fieldCount; ++i) {
|
||||
const auto& field = fields[i];
|
||||
lua_pushstring(luaState, const_cast<char*>(field.name));
|
||||
lua_newtable(luaState);
|
||||
|
||||
const char* fieldPtr = reinterpret_cast<const char*>(obj) + field.offset;
|
||||
|
||||
for (size_t j = 0; j < field.count; ++j) {
|
||||
lua_pushnumber(luaState, j + 1); // Lua is 1-indexed
|
||||
|
||||
switch (field.type) {
|
||||
case FieldType::INT32:
|
||||
lua_pushnumber(luaState, reinterpret_cast<const int32_t*>(fieldPtr)[j]);
|
||||
break;
|
||||
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;
|
||||
case FieldType::UINT64:
|
||||
lua_pushnumber(luaState, static_cast<double>(reinterpret_cast<const uint64_t*>(fieldPtr)[j]));
|
||||
break;
|
||||
case FieldType::STRING: {
|
||||
const char* str = reinterpret_cast<const char* const*>(fieldPtr)[j];
|
||||
lua_pushstring(luaState, str ? const_cast<char*>(str) : const_cast<char*>(""));
|
||||
break;
|
||||
}
|
||||
}
|
||||
lua_settable(luaState, -3);
|
||||
}
|
||||
lua_settable(luaState, -3);
|
||||
}
|
||||
}
|
||||
}
|
||||
+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);
|
||||
|
||||
+3
-2
@@ -30,6 +30,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace game {
|
||||
#pragma pack(push, 1)
|
||||
@@ -209,7 +210,7 @@ namespace game {
|
||||
int m_containerSlots;
|
||||
int m_bonusStat[10];
|
||||
int m_bonusAmount[10];
|
||||
int m_minDamage[5];
|
||||
float m_minDamage[5];
|
||||
float m_maxDamage[5];
|
||||
int m_damageType[5];
|
||||
int m_resistances[7];
|
||||
@@ -229,7 +230,7 @@ namespace game {
|
||||
int m_pageMaterial;
|
||||
int m_startQuestID;
|
||||
int m_lockID;
|
||||
char *m_material;
|
||||
int m_material;
|
||||
int m_sheatheType;
|
||||
int m_randomProperty;
|
||||
int m_block;
|
||||
|
||||
+148
-1
@@ -5,8 +5,155 @@
|
||||
#include "helper.hpp"
|
||||
#include "offsets.hpp"
|
||||
#include "main.hpp"
|
||||
#include "dbc_fields.hpp"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
namespace Nampower {
|
||||
|
||||
uint32_t GetSpellSlotAndTypeForName(const char *spellName, uint32_t *spellType) {
|
||||
struct CachedEntry {
|
||||
uint32_t slot;
|
||||
uint32_t type;
|
||||
std::string cachedNameLower;
|
||||
};
|
||||
static std::unordered_map<std::string, CachedEntry> spellSlotCache;
|
||||
|
||||
auto const normalize = [](const char *name) -> std::string {
|
||||
std::string out = name ? name : "";
|
||||
std::transform(out.begin(), out.end(), out.begin(), [](unsigned char c) { return char(std::tolower(c)); });
|
||||
return out;
|
||||
};
|
||||
|
||||
const std::string key = normalize(spellName);
|
||||
|
||||
auto const spellNameMatchesAtSlot = [&normalize](const char *requestedLower, uint32_t slot, uint32_t type) -> bool {
|
||||
if (!requestedLower) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t spellId = 0;
|
||||
if (type == 0) {
|
||||
spellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownSpells) + slot * 4);
|
||||
} else if (type == 1) {
|
||||
spellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownPetSpells) + slot * 4);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spellId == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const spell = game::GetSpellInfo(spellId);
|
||||
if (!spell) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const language = *reinterpret_cast<uint32_t *>(Offsets::Language);
|
||||
auto const actualName = reinterpret_cast<const char *>(spell->SpellName[language]);
|
||||
if (!actualName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const actualLower = normalize(actualName);
|
||||
return _stricmp(actualLower.c_str(), requestedLower) == 0;
|
||||
};
|
||||
|
||||
if (!key.empty()) {
|
||||
auto cacheIt = spellSlotCache.find(key);
|
||||
if (cacheIt != spellSlotCache.end()) {
|
||||
auto const &entry = cacheIt->second;
|
||||
if (entry.slot < 1024 && spellNameMatchesAtSlot(key.c_str(), entry.slot, entry.type)) {
|
||||
if (spellType) {
|
||||
*spellType = entry.type;
|
||||
}
|
||||
return entry.slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto const getSpellSlotAndType = reinterpret_cast<GetSpellSlotAndTypeT>(Offsets::GetSpellSlotAndType);
|
||||
auto const slot = getSpellSlotAndType(spellName, spellType);
|
||||
|
||||
if (slot < 1024 && spellType && !key.empty()) {
|
||||
spellSlotCache[key] = {slot, *spellType, key};
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
uint32_t GetSpellIdFromSpellName(const char *spellName) {
|
||||
uint32_t bookType;
|
||||
uint32_t spellSlot = GetSpellSlotAndTypeForName(spellName, &bookType);
|
||||
|
||||
uint32_t spellId = 0;
|
||||
if (spellSlot < 1024) {
|
||||
if (bookType == 0) {
|
||||
spellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownSpells) +
|
||||
spellSlot * 4);
|
||||
} else {
|
||||
spellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownPetSpells) +
|
||||
spellSlot * 4);
|
||||
}
|
||||
}
|
||||
|
||||
return spellId;
|
||||
}
|
||||
|
||||
uint32_t ConvertSpellIdToSpellSlot(uint32_t spellId, uint32_t bookType) {
|
||||
static std::unordered_map<uint64_t, uint32_t> spellIdToSlotCache;
|
||||
auto const cacheKey = (uint64_t(spellId) << 1) | (bookType & 0x1);
|
||||
|
||||
auto const slotHasSpellId = [bookType](uint32_t slot, uint32_t expectedId) -> bool {
|
||||
if (slot >= 1024) {
|
||||
return false;
|
||||
}
|
||||
uint32_t slotSpellId = 0;
|
||||
if (bookType == 0) {
|
||||
slotSpellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownSpells) + slot * 4);
|
||||
} else {
|
||||
slotSpellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownPetSpells) + slot * 4);
|
||||
}
|
||||
return slotSpellId == expectedId;
|
||||
};
|
||||
|
||||
auto cacheIt = spellIdToSlotCache.find(cacheKey);
|
||||
if (cacheIt != spellIdToSlotCache.end()) {
|
||||
if (slotHasSpellId(cacheIt->second, spellId)) {
|
||||
return cacheIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
// Search through player spellbook
|
||||
if (bookType == 0) {
|
||||
for (uint32_t spellSlot = 0; spellSlot < 1024; spellSlot++) {
|
||||
uint32_t slotSpellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownSpells) +
|
||||
spellSlot * 4);
|
||||
if (slotSpellId == spellId) {
|
||||
spellIdToSlotCache[cacheKey] = spellSlot;
|
||||
return spellSlot;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Search through pet spellbook
|
||||
for (uint32_t spellSlot = 0; spellSlot < 1024; spellSlot++) {
|
||||
uint32_t slotSpellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownPetSpells) +
|
||||
spellSlot * 4);
|
||||
if (slotSpellId == spellId) {
|
||||
spellIdToSlotCache[cacheKey] = spellSlot;
|
||||
return spellSlot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool SpellIsOnGcd(const game::SpellRec *spell) {
|
||||
if (spell->Id == 51714) {
|
||||
// power overwhelming gcd removed but client not updated
|
||||
@@ -122,4 +269,4 @@ namespace Nampower {
|
||||
void SetNameplateDistance(float distance) {
|
||||
*reinterpret_cast<float *>(Offsets::NameplateDistance) = distance * distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
#include "game.hpp"
|
||||
|
||||
namespace Nampower {
|
||||
uint32_t GetSpellSlotAndTypeForName(const char *spellName, uint32_t *spellType);
|
||||
|
||||
uint32_t GetSpellIdFromSpellName(const char *spellName);
|
||||
|
||||
uint32_t ConvertSpellIdToSpellSlot(uint32_t spellId, uint32_t bookType);
|
||||
|
||||
bool SpellIsOnGcd(const game::SpellRec *spell);
|
||||
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
//
|
||||
// Created by pmacc on 9/21/2024.
|
||||
//
|
||||
|
||||
#include "items.hpp"
|
||||
#include "offsets.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Nampower {
|
||||
// Global dictionary to store itemId -> ItemStats_C mappings
|
||||
static std::unordered_map<uint32_t, game::ItemStats_C *> itemStatsCache;
|
||||
|
||||
auto const getRow = reinterpret_cast<DBCache_ItemCacheDBGetRowT>(Offsets::DBCache_ItemCacheDBGetRow);
|
||||
auto const itemCache = reinterpret_cast<void *>(Offsets::ItemDBCache);
|
||||
|
||||
// Callback function for DBCache
|
||||
void __stdcall ItemStatsCallback(uintptr_t *tooltip, char param_2) {
|
||||
if (tooltip != nullptr) {
|
||||
auto itemId = *reinterpret_cast<uint32_t *>(tooltip + 0xE6);
|
||||
|
||||
int ptr;
|
||||
auto itemStats = getRow(itemCache, nullptr, itemId, &ptr, &ItemStatsCallback, nullptr, 0);
|
||||
if (itemStats) {
|
||||
DEBUG_LOG("Queried itemId " << itemId << " -> " << itemStats);
|
||||
// Store in cache
|
||||
itemStatsCache[itemId] = reinterpret_cast<game::ItemStats_C *>(itemStats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LoadItem(uint32_t itemId) {
|
||||
// Check if already in cache
|
||||
auto it = itemStatsCache.find(itemId);
|
||||
if (it != itemStatsCache.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!itemCache) {
|
||||
return;
|
||||
}
|
||||
|
||||
int ptr;
|
||||
auto itemStats = getRow(itemCache, nullptr, itemId, &ptr, &ItemStatsCallback, nullptr, 0);
|
||||
if (itemStats) {
|
||||
// Store in cache
|
||||
itemStatsCache[itemId] = reinterpret_cast<game::ItemStats_C *>(itemStats);
|
||||
} else {
|
||||
// wait for .005 sec
|
||||
Sleep(5);
|
||||
}
|
||||
}
|
||||
|
||||
game::ItemStats_C* GetItemStats(uint32_t itemId) {
|
||||
auto it = itemStatsCache.find(itemId);
|
||||
if (it != itemStatsCache.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string escapeJsonString(const char *str) {
|
||||
if (!str) return "null";
|
||||
|
||||
std::string result = "\"";
|
||||
for (const char *p = str; *p; ++p) {
|
||||
switch (*p) {
|
||||
case '"':
|
||||
result += "\\\"";
|
||||
break;
|
||||
case '\\':
|
||||
result += "\\\\";
|
||||
break;
|
||||
case '\b':
|
||||
result += "\\b";
|
||||
break;
|
||||
case '\f':
|
||||
result += "\\f";
|
||||
break;
|
||||
case '\n':
|
||||
result += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
result += "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
result += "\\t";
|
||||
break;
|
||||
default:
|
||||
if (static_cast<unsigned char>(*p) < 0x20) {
|
||||
char buf[8];
|
||||
snprintf(buf, sizeof(buf), "\\u%04x", static_cast<unsigned char>(*p));
|
||||
result += buf;
|
||||
} else {
|
||||
result += *p;
|
||||
}
|
||||
}
|
||||
}
|
||||
result += "\"";
|
||||
return result;
|
||||
}
|
||||
|
||||
// Register a function if you want to use this
|
||||
// Don't want everyone scraping all items.
|
||||
void ExportAllItems() {
|
||||
DEBUG_LOG("Starting getAllItems - fetching items 1 to 100000");
|
||||
|
||||
// Loop through all item IDs
|
||||
for (uint32_t itemId = 1; itemId <= 100000; ++itemId) {
|
||||
LoadItem(itemId);
|
||||
|
||||
// Log progress every 1000 items
|
||||
if (itemId % 1000 == 0) {
|
||||
DEBUG_LOG("Progress: " << itemId << " / 100000 items processed");
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_LOG("Finished fetching items, found " << itemStatsCache.size() << " valid items");
|
||||
DEBUG_LOG("Writing items to items.json");
|
||||
|
||||
// Write all cached items to JSON file (overwrite mode)
|
||||
try {
|
||||
std::ofstream jsonFile("items.json", std::ios::trunc);
|
||||
if (!jsonFile.is_open()) {
|
||||
DEBUG_LOG("Failed to open items.json for writing");
|
||||
return;
|
||||
}
|
||||
|
||||
jsonFile << "[\n";
|
||||
|
||||
bool first = true;
|
||||
for (const auto &pair: itemStatsCache) {
|
||||
if (!first) {
|
||||
jsonFile << ",\n";
|
||||
}
|
||||
first = false;
|
||||
|
||||
uint32_t itemId = pair.first;
|
||||
game::ItemStats_C *item = pair.second;
|
||||
|
||||
if (!item) continue;
|
||||
|
||||
std::ostringstream json;
|
||||
json << " {\n";
|
||||
json << " \"id\": " << itemId << ",\n";
|
||||
json << " \"class\": " << item->m_class << ",\n";
|
||||
json << " \"subclass\": " << item->m_subclass << ",\n";
|
||||
json << " \"displayName\": " << escapeJsonString(item->m_displayName[0]) << ",\n";
|
||||
json << " \"displayInfoID\": " << item->m_displayInfoID << ",\n";
|
||||
json << " \"quality\": " << item->m_quality << ",\n";
|
||||
json << " \"flags\": " << static_cast<int>(item->m_flags) << ",\n";
|
||||
json << " \"buyPrice\": " << item->m_buyPrice << ",\n";
|
||||
json << " \"sellPrice\": " << item->m_sellPrice << ",\n";
|
||||
json << " \"inventoryType\": " << item->m_inventoryType << ",\n";
|
||||
json << " \"allowableClass\": " << item->m_allowableClass << ",\n";
|
||||
json << " \"allowableRace\": " << item->m_allowableRace << ",\n";
|
||||
json << " \"itemLevel\": " << item->m_itemLevel << ",\n";
|
||||
json << " \"requiredLevel\": " << item->m_requiredLevel << ",\n";
|
||||
json << " \"requiredSkill\": " << item->m_requiredSkill << ",\n";
|
||||
json << " \"requiredSkillRank\": " << item->m_requiredSkillRank << ",\n";
|
||||
json << " \"requiredSpell\": " << item->m_requiredSpell << ",\n";
|
||||
json << " \"requiredHonorRank\": " << item->m_requiredHonorRank << ",\n";
|
||||
json << " \"requiredCityRank\": " << item->m_requiredCityRank << ",\n";
|
||||
json << " \"requiredRep\": " << item->m_requiredRep << ",\n";
|
||||
json << " \"requiredRepRank\": " << item->m_requiredRepRank << ",\n";
|
||||
json << " \"maxCount\": " << item->m_maxCount << ",\n";
|
||||
json << " \"stackable\": " << item->m_stackable << ",\n";
|
||||
json << " \"containerSlots\": " << item->m_containerSlots << ",\n";
|
||||
|
||||
// Arrays
|
||||
json << " \"bonusStat\": [";
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
json << item->m_bonusStat[i];
|
||||
if (i < 9) json << ", ";
|
||||
}
|
||||
json << "],\n";
|
||||
|
||||
json << " \"bonusAmount\": [";
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
json << item->m_bonusAmount[i];
|
||||
if (i < 9) json << ", ";
|
||||
}
|
||||
json << "],\n";
|
||||
|
||||
json << " \"minDamage\": [";
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
json << item->m_minDamage[i];
|
||||
if (i < 4) json << ", ";
|
||||
}
|
||||
json << "],\n";
|
||||
|
||||
json << " \"maxDamage\": [";
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
json << item->m_maxDamage[i];
|
||||
if (i < 4) json << ", ";
|
||||
}
|
||||
json << "],\n";
|
||||
|
||||
json << " \"damageType\": [";
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
json << item->m_damageType[i];
|
||||
if (i < 4) json << ", ";
|
||||
}
|
||||
json << "],\n";
|
||||
|
||||
json << " \"resistances\": [";
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
json << item->m_resistances[i];
|
||||
if (i < 6) json << ", ";
|
||||
}
|
||||
json << "],\n";
|
||||
|
||||
json << " \"delay\": " << item->m_delay << ",\n";
|
||||
json << " \"ammoType\": " << item->m_ammoType << ",\n";
|
||||
json << " \"rangedModRange\": " << item->m_rangedModRange << ",\n";
|
||||
|
||||
json << " \"spellID\": [";
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
json << item->m_spellID[i];
|
||||
if (i < 4) json << ", ";
|
||||
}
|
||||
json << "],\n";
|
||||
|
||||
json << " \"spellTrigger\": [";
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
json << item->m_spellTrigger[i];
|
||||
if (i < 4) json << ", ";
|
||||
}
|
||||
json << "],\n";
|
||||
|
||||
json << " \"spellCharges\": [";
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
json << item->m_spellCharges[i];
|
||||
if (i < 4) json << ", ";
|
||||
}
|
||||
json << "],\n";
|
||||
|
||||
json << " \"spellCooldown\": [";
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
json << item->m_spellCooldown[i];
|
||||
if (i < 4) json << ", ";
|
||||
}
|
||||
json << "],\n";
|
||||
|
||||
json << " \"spellCategory\": [";
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
json << item->m_spellCategory[i];
|
||||
if (i < 4) json << ", ";
|
||||
}
|
||||
json << "],\n";
|
||||
|
||||
json << " \"spellCategoryCooldown\": [";
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
json << item->m_spellCategoryCooldown[i];
|
||||
if (i < 4) json << ", ";
|
||||
}
|
||||
json << "],\n";
|
||||
|
||||
json << " \"bonding\": " << item->m_bonding << ",\n";
|
||||
json << " \"description\": " << escapeJsonString(item->m_description) << ",\n";
|
||||
json << " \"pageText\": " << item->m_pageText << ",\n";
|
||||
json << " \"languageID\": " << item->m_languageID << ",\n";
|
||||
json << " \"pageMaterial\": " << item->m_pageMaterial << ",\n";
|
||||
json << " \"startQuestID\": " << item->m_startQuestID << ",\n";
|
||||
json << " \"lockID\": " << item->m_lockID << ",\n";
|
||||
json << " \"material\": " << item->m_material << ",\n";
|
||||
json << " \"sheatheType\": " << item->m_sheatheType << ",\n";
|
||||
json << " \"randomProperty\": " << item->m_randomProperty << ",\n";
|
||||
json << " \"block\": " << item->m_block << ",\n";
|
||||
json << " \"itemSet\": " << item->m_itemSet << ",\n";
|
||||
json << " \"maxDurability\": " << item->m_maxDurability << ",\n";
|
||||
json << " \"area\": " << item->m_area << ",\n";
|
||||
json << " \"map\": " << item->m_map << ",\n";
|
||||
json << " \"duration\": " << item->m_duration << ",\n";
|
||||
json << " \"bagFamily\": " << item->m_bagFamily << "\n";
|
||||
json << " }";
|
||||
|
||||
jsonFile << json.str();
|
||||
}
|
||||
|
||||
jsonFile << "\n]\n";
|
||||
jsonFile.close();
|
||||
|
||||
DEBUG_LOG("Successfully wrote " << itemStatsCache.size() << " items to items.json");
|
||||
} catch (const std::exception &e) {
|
||||
DEBUG_LOG("Exception writing to items.json: " << e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// Created by pmacc on 9/21/2024.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "main.hpp"
|
||||
|
||||
namespace Nampower {
|
||||
using TooltipItemStatsCallbackT = void (__stdcall *)(uintptr_t *tooltip, char param_2);
|
||||
|
||||
using DBCache_ItemCacheDBGetRowT = uint32_t * (__fastcall *)(void *this_ptr, void *dummy_edx, uint32_t itemId, int *param_2, TooltipItemStatsCallbackT callback, int *param_4, char param_5);
|
||||
|
||||
void ExportAllItems();
|
||||
void LoadItem(uint32_t itemId);
|
||||
game::ItemStats_C* GetItemStats(uint32_t itemId);
|
||||
}
|
||||
+85
-50
@@ -36,6 +36,7 @@
|
||||
#include "scripts.hpp"
|
||||
#include "spellchannel.hpp"
|
||||
#include "helper.hpp"
|
||||
#include "items.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -92,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;
|
||||
@@ -99,23 +101,10 @@ namespace Nampower {
|
||||
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<LuaScriptT>> gCastSpellByNameNoQueueDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gQueueSpellByNameDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> qQueueScriptDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gIsSpellInRangeDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gIsSpellUsableDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gGetCurrentCastingInfoDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gGetSpellIdForNameDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gGetSpellNameAndRankForIdDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gGetSpellSlotAndTypeForNameDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gChannelStopCastingNextTickDetour;
|
||||
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_TargetSpellT>> gSpell_C_TargetSpellDetour;
|
||||
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gGetNampowerVersionDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gGetItemLevelDetour;
|
||||
|
||||
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT>> gSpellCooldownDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT>> gSpellDelayedDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT>> gCastResultHandlerDetour;
|
||||
@@ -128,6 +117,12 @@ namespace Nampower {
|
||||
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<InvalidFunctionPtrCheckT>> gInvalidFunctionPtrCheckDetour;
|
||||
|
||||
std::unique_ptr<hadesmem::PatchDetour<GetSpellSlotFromLuaT>> gGetSpellSlotFromLuaDetour;
|
||||
|
||||
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;
|
||||
@@ -163,6 +158,34 @@ namespace Nampower {
|
||||
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);
|
||||
@@ -513,6 +536,17 @@ namespace Nampower {
|
||||
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;
|
||||
@@ -1086,29 +1120,14 @@ namespace Nampower {
|
||||
&Script_SpellStopCastingHook);
|
||||
gSpell_C_TargetSpellDetour = createHook<Spell_C_TargetSpellT>(process, Offsets::Spell_C_TargetSpell,
|
||||
&Spell_C_TargetSpellHook);
|
||||
gCastSpellByNameNoQueueDetour = createHook<LuaScriptT>(process, Offsets::Script_CastSpellByNameNoQueue,
|
||||
Script_CastSpellByNameNoQueue);
|
||||
gQueueSpellByNameDetour = createHook<LuaScriptT>(process, Offsets::Script_QueueSpellByName,
|
||||
Script_QueueSpellByName);
|
||||
qQueueScriptDetour = createHook<LuaScriptT>(process, Offsets::Script_QueueScript, Script_QueueScript);
|
||||
gIsSpellInRangeDetour = createHook<LuaScriptT>(process, Offsets::Script_IsSpellInRange, Script_IsSpellInRange);
|
||||
gIsSpellUsableDetour = createHook<LuaScriptT>(process, Offsets::Script_IsSpellUsable, Script_IsSpellUsable);
|
||||
gGetCurrentCastingInfoDetour = createHook<LuaScriptT>(process, Offsets::Script_GetCurrentCastingInfo,
|
||||
Script_GetCurrentCastingInfo);
|
||||
gGetSpellIdForNameDetour = createHook<LuaScriptT>(process, Offsets::Script_GetSpellIdForName,
|
||||
Script_GetSpellIdForName);
|
||||
gGetSpellNameAndRankForIdDetour = createHook<LuaScriptT>(process, Offsets::Script_GetSpellNameAndRankForId,
|
||||
Script_GetSpellNameAndRankForId);
|
||||
gGetSpellSlotAndTypeForNameDetour = createHook<LuaScriptT>(process, Offsets::Script_GetSpellSlotTypeIdForName,
|
||||
Script_GetSpellSlotTypeIdForName);
|
||||
gOnSpriteRightClickDetour = createHook<OnSpriteRightClickT>(process, Offsets::OnSpriteRightClick,
|
||||
OnSpriteRightClickHook);
|
||||
gChannelStopCastingNextTickDetour = createHook<LuaScriptT>(process, Offsets::Script_ChannelStopCastingNextTick,
|
||||
Script_ChannelStopCastingNextTick);
|
||||
gGetNampowerVersionDetour = createHook<LuaScriptT>(process, Offsets::Script_GetNampowerVersion,
|
||||
Script_GetNampowerVersion);
|
||||
gGetItemLevelDetour = createHook<LuaScriptT>(process, Offsets::Script_GetItemLevel, Script_GetItemLevel);
|
||||
gIEndSceneDetour = createHook<ISceneEndT>(process, Offsets::ISceneEndPtr, &ISceneEndHook);
|
||||
gInvalidFunctionPtrCheckDetour = createHook<InvalidFunctionPtrCheckT>(process, Offsets::InvalidFunctionPtrCheck,
|
||||
&InvalidFunctionPtrCheckHook);
|
||||
gGetSpellSlotFromLuaDetour = createHook<GetSpellSlotFromLuaT>(process, Offsets::GetSpellSlotFromLua,
|
||||
&GetSpellSlotFromLuaHook);
|
||||
|
||||
}
|
||||
|
||||
void SpellVisualsInitializeHook(hadesmem::PatchDetourBase *detour) {
|
||||
@@ -1135,46 +1154,62 @@ namespace Nampower {
|
||||
// register our own lua functions
|
||||
DEBUG_LOG("Registering Custom Lua functions");
|
||||
char queueSpellByName[] = "QueueSpellByName";
|
||||
RegisterLuaFunction(queueSpellByName, reinterpret_cast<uintptr_t *>(Offsets::Script_QueueSpellByName));
|
||||
RegisterLuaFunction(queueSpellByName, reinterpret_cast<uintptr_t *>(Script_QueueSpellByName));
|
||||
|
||||
char castSpellByNameNoQueue[] = "CastSpellByNameNoQueue";
|
||||
RegisterLuaFunction(castSpellByNameNoQueue,
|
||||
reinterpret_cast<uintptr_t *>(Offsets::Script_CastSpellByNameNoQueue));
|
||||
RegisterLuaFunction(castSpellByNameNoQueue, reinterpret_cast<uintptr_t *>(Script_CastSpellByNameNoQueue));
|
||||
|
||||
char queueScript[] = "QueueScript";
|
||||
RegisterLuaFunction(queueScript, reinterpret_cast<uintptr_t *>(Offsets::Script_QueueScript));
|
||||
RegisterLuaFunction(queueScript, reinterpret_cast<uintptr_t *>(Script_QueueScript));
|
||||
|
||||
char isSpellInRange[] = "IsSpellInRange";
|
||||
RegisterLuaFunction(isSpellInRange, reinterpret_cast<uintptr_t *>(Offsets::Script_IsSpellInRange));
|
||||
RegisterLuaFunction(isSpellInRange, reinterpret_cast<uintptr_t *>(Script_IsSpellInRange));
|
||||
|
||||
char isSpellUsable[] = "IsSpellUsable";
|
||||
RegisterLuaFunction(isSpellUsable, reinterpret_cast<uintptr_t *>(Offsets::Script_IsSpellUsable));
|
||||
RegisterLuaFunction(isSpellUsable, reinterpret_cast<uintptr_t *>(Script_IsSpellUsable));
|
||||
|
||||
char getCurrentCastingInfo[] = "GetCurrentCastingInfo";
|
||||
RegisterLuaFunction(getCurrentCastingInfo,
|
||||
reinterpret_cast<uintptr_t *>(Offsets::Script_GetCurrentCastingInfo));
|
||||
RegisterLuaFunction(getCurrentCastingInfo, reinterpret_cast<uintptr_t *>(Script_GetCurrentCastingInfo));
|
||||
|
||||
char getSpellIdForName[] = "GetSpellIdForName";
|
||||
RegisterLuaFunction(getSpellIdForName,
|
||||
reinterpret_cast<uintptr_t *>(Offsets::Script_GetSpellIdForName));
|
||||
RegisterLuaFunction(getSpellIdForName, reinterpret_cast<uintptr_t *>(Script_GetSpellIdForName));
|
||||
|
||||
char getSpellNameAndRankForId[] = "GetSpellNameAndRankForId";
|
||||
RegisterLuaFunction(getSpellNameAndRankForId,
|
||||
reinterpret_cast<uintptr_t *>(Offsets::Script_GetSpellNameAndRankForId));
|
||||
RegisterLuaFunction(getSpellNameAndRankForId, reinterpret_cast<uintptr_t *>(Script_GetSpellNameAndRankForId));
|
||||
|
||||
char getSpellSlotTypeIdForName[] = "GetSpellSlotTypeIdForName";
|
||||
RegisterLuaFunction(getSpellSlotTypeIdForName,
|
||||
reinterpret_cast<uintptr_t *>(Offsets::Script_GetSpellSlotTypeIdForName));
|
||||
RegisterLuaFunction(getSpellSlotTypeIdForName, reinterpret_cast<uintptr_t *>(Script_GetSpellSlotTypeIdForName));
|
||||
|
||||
char channelStopCastingNextTick[] = "ChannelStopCastingNextTick";
|
||||
RegisterLuaFunction(channelStopCastingNextTick,
|
||||
reinterpret_cast<uintptr_t *>(Offsets::Script_ChannelStopCastingNextTick));
|
||||
reinterpret_cast<uintptr_t *>(Script_ChannelStopCastingNextTick));
|
||||
|
||||
char getNampowerVersion[] = "GetNampowerVersion";
|
||||
RegisterLuaFunction(getNampowerVersion, reinterpret_cast<uintptr_t *>(Offsets::Script_GetNampowerVersion));
|
||||
RegisterLuaFunction(getNampowerVersion, reinterpret_cast<uintptr_t *>(Script_GetNampowerVersion));
|
||||
|
||||
char getItemILevel[] = "GetItemLevel";
|
||||
RegisterLuaFunction(getItemILevel, reinterpret_cast<uintptr_t *>(Offsets::Script_GetItemLevel));
|
||||
RegisterLuaFunction(getItemILevel, reinterpret_cast<uintptr_t *>(Script_GetItemLevel));
|
||||
|
||||
char getItemStats[] = "GetItemStats";
|
||||
RegisterLuaFunction(getItemStats, reinterpret_cast<uintptr_t *>(Script_GetItemStats));
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
std::once_flag loadFlag;
|
||||
|
||||
+20
-3
@@ -30,8 +30,8 @@ namespace Nampower {
|
||||
constexpr uint32_t BUFFER_DECREASE_FREQUENCY = 10000; // time in ms between changes to lower buffer
|
||||
|
||||
constexpr uint32_t MAJOR_VERSION = 2;
|
||||
constexpr uint32_t MINOR_VERSION = 11;
|
||||
constexpr uint32_t PATCH_VERSION = 1;
|
||||
constexpr uint32_t MINOR_VERSION = 12;
|
||||
constexpr uint32_t PATCH_VERSION = 0;
|
||||
|
||||
constexpr int32_t LUA_REGISTRYINDEX = -10000;
|
||||
constexpr int32_t LUA_GLOBALSINDEX = -10001;
|
||||
@@ -93,8 +93,12 @@ 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 GetSpellSlotAndTypeT = int (__fastcall *)(const char *, uint32_t *);
|
||||
using CGPlayer_C_OnAttackIconPressedT = int (__fastcall *)(uintptr_t *this_ptr, void *dummy_edx, uint64_t guid);
|
||||
|
||||
using GetSpellSlotAndTypeT = uint32_t (__fastcall *)(const char *, uint32_t *);
|
||||
using GetSpellSlotFromLuaT = uint32_t (__fastcall *)(int param_1, uint32_t *slot, uint32_t *type);
|
||||
using GetTimeMsT = uint64_t (__stdcall *)();
|
||||
|
||||
using GetClientConnectionT = uintptr_t *(__stdcall *)();
|
||||
@@ -122,6 +126,8 @@ namespace Nampower {
|
||||
using lua_pushnilT = void (__fastcall *)(uintptr_t *);
|
||||
using lua_errorT = void (__cdecl *)(uintptr_t *, const char *);
|
||||
using lua_settopT = void (__fastcall *)(uintptr_t *, int);
|
||||
using lua_newtableT = void (__fastcall *)(uintptr_t *);
|
||||
using lua_settableT = void (__fastcall *)(uintptr_t *, int);
|
||||
|
||||
using Spell_C_CooldownEventTriggeredT = void (__fastcall *)(uint32_t spellId,
|
||||
uint64_t *targetGUID,
|
||||
@@ -137,12 +143,16 @@ namespace Nampower {
|
||||
|
||||
using GetBuffByIndexT = uintptr_t *(__fastcall *)(int index);
|
||||
|
||||
using TargetUnitT = void (__fastcall *)(uint64_t *guid);
|
||||
|
||||
using CVarLookupT = uintptr_t *(__fastcall *)(const char *);
|
||||
using SetCVarT = int (__fastcall *)(uintptr_t *luaPtr);
|
||||
using CVarRegisterT = int *(__fastcall *)(char *name, char *help, int unk1, const char *defaultValuePtr,
|
||||
void *callbackPtr,
|
||||
int category, char unk2, int unk3);
|
||||
|
||||
using InvalidFunctionPtrCheckT = void (__fastcall *)(uint32_t param_1);
|
||||
|
||||
void RegisterLuaFunction(char *, uintptr_t *func);
|
||||
|
||||
void LuaCall(const char *code);
|
||||
@@ -170,4 +180,11 @@ namespace Nampower {
|
||||
bool processQueues();
|
||||
|
||||
uint32_t EffectiveCastEndMs();
|
||||
|
||||
void SetTarget(uint64_t target);
|
||||
|
||||
void SetSelectionTarget(uint64_t target);
|
||||
|
||||
void SetAttackTarget(uint64_t target);
|
||||
|
||||
}
|
||||
+15
-15
@@ -49,6 +49,7 @@ enum class Offsets : std::uint32_t {
|
||||
AutoRepeatingSpellId = 0xCEAC30,
|
||||
VisualSpellId = 0X00CEAC58,
|
||||
GetSpellSlotAndType = 0X004B3950,
|
||||
GetSpellSlotFromLua = 0X004B3EC0,
|
||||
OsGetAsyncTimeMs = 0X0042B790,
|
||||
ChannelTargetGuid = 0xC4D980,
|
||||
NameplateDistance = 0xC4D988, // float containing the distance squared ready to be pythagorean'd
|
||||
@@ -76,6 +77,7 @@ enum class Offsets : std::uint32_t {
|
||||
LockedTargetGuid = 0x00B4E2D8,
|
||||
OnSpriteRightClick = 0x00492820,
|
||||
CGGameUI_Target = 0X00493540,
|
||||
ScriptTargetUnit = 0X00489A40,
|
||||
|
||||
SpellVisualsHandleCastStart = 0X006EC220,
|
||||
PlaySpellVisualHandler = 0X006E98D0,
|
||||
@@ -102,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,
|
||||
@@ -123,20 +126,6 @@ enum class Offsets : std::uint32_t {
|
||||
Script_RunScript = 0x0048B980,
|
||||
Script_SpellStopCasting = 0x006E6E80,
|
||||
|
||||
// Added script functions
|
||||
Script_QueueSpellByName = 0x004B4B38, // unused address at the end of Script_CastSpellByName. Need to be < 0x7FEDAC to avoid Invalid Function Pointer
|
||||
Script_CastSpellByNameNoQueue = 0X004B4B64,
|
||||
Script_QueueScript = 0X0048B968, // unused address at the end of Script_StopCinematic
|
||||
Script_IsSpellInRange = 0x004E76D8,
|
||||
Script_IsSpellUsable = 0X004E77A4,
|
||||
Script_GetCurrentCastingInfo = 0X004E77F8,
|
||||
Script_GetSpellIdForName = 0X004E7828,
|
||||
Script_GetSpellNameAndRankForId = 0X004E7844,
|
||||
Script_GetSpellSlotTypeIdForName = 0X004E784A,
|
||||
Script_ChannelStopCastingNextTick = 0X004E7858,
|
||||
Script_GetNampowerVersion = 0X004E7874,
|
||||
Script_GetItemLevel = 0X004E787A,
|
||||
|
||||
lua_state_ptr = 0x7040D0,
|
||||
|
||||
lua_isstring = 0x006F3510,
|
||||
@@ -151,6 +140,11 @@ enum class Offsets : std::uint32_t {
|
||||
lua_pcall = 0x006F41A0,
|
||||
lua_error = 0X006F4940,
|
||||
lua_settop = 0X006F3080,
|
||||
lua_newtable = 0x006F3C90,
|
||||
lua_settable = 0x006F3E20,
|
||||
lua_pushboolean = 0x006F39F0,
|
||||
lua_rawseti = 0x006F3EA0,
|
||||
lua_gettop = 0x006F3070,
|
||||
|
||||
|
||||
CGInputControlGetActive = 0XBE1148,
|
||||
@@ -174,4 +168,10 @@ enum class Offsets : std::uint32_t {
|
||||
|
||||
CGUnit_C_ClearCastingSpell = 0x0060d040,
|
||||
CGUnit_C_ClearSpellEffect = 0x00614150,
|
||||
|
||||
CGPlayer_C_OnAttackIconPressed = 0X006131A0,
|
||||
|
||||
DBCache_ItemCacheDBGetRow = 0x0055BA30,
|
||||
|
||||
InvalidFunctionPtrCheck = 0x0042A320,
|
||||
};
|
||||
|
||||
+559
-41
@@ -4,6 +4,11 @@
|
||||
|
||||
#include "scripts.hpp"
|
||||
#include "offsets.hpp"
|
||||
#include "items.hpp"
|
||||
#include "dbc_fields.hpp"
|
||||
#include "unit_fields.hpp"
|
||||
#include "helper.hpp"
|
||||
#include <cstring>
|
||||
|
||||
namespace Nampower {
|
||||
auto const lua_error = reinterpret_cast<lua_errorT>(Offsets::lua_error);
|
||||
@@ -14,38 +19,18 @@ namespace Nampower {
|
||||
auto const lua_tostring = reinterpret_cast<lua_tostringT>(Offsets::lua_tostring);
|
||||
auto const lua_tonumber = reinterpret_cast<lua_tonumberT>(Offsets::lua_tonumber);
|
||||
|
||||
auto const lua_pushnumber = reinterpret_cast<lua_pushnumberT>(Offsets::lua_pushnumber);
|
||||
auto const lua_pushstring = reinterpret_cast<lua_pushstringT>(Offsets::lua_pushstring);
|
||||
// Export Lua functions for dbc_fields.hpp templates
|
||||
lua_pushnumberT lua_pushnumber = reinterpret_cast<lua_pushnumberT>(Offsets::lua_pushnumber);
|
||||
lua_pushstringT lua_pushstring = reinterpret_cast<lua_pushstringT>(Offsets::lua_pushstring);
|
||||
lua_pushnilT lua_pushnil = reinterpret_cast<lua_pushnilT>(Offsets::lua_pushnil);
|
||||
lua_newtableT lua_newtable = reinterpret_cast<lua_newtableT>(Offsets::lua_newtable);
|
||||
lua_settableT lua_settable = reinterpret_cast<lua_settableT>(Offsets::lua_settable);
|
||||
|
||||
bool gScriptQueued;
|
||||
int gScriptPriority = 1;
|
||||
char *queuedScript;
|
||||
|
||||
uint32_t GetSpellSlotAndTypeForName(const char *spellName, uint32_t *spellType) {
|
||||
auto const getSpellSlotAndType = reinterpret_cast<GetSpellSlotAndTypeT>(Offsets::GetSpellSlotAndType);
|
||||
return getSpellSlotAndType(spellName, spellType);
|
||||
}
|
||||
|
||||
|
||||
uint32_t GetSpellIdFromSpellName(const char *spellName) {
|
||||
uint32_t bookType;
|
||||
uint32_t spellSlot = GetSpellSlotAndTypeForName(spellName, &bookType);
|
||||
|
||||
uint32_t spellId = 0;
|
||||
if (spellSlot < 1024) {
|
||||
if (bookType == 0) {
|
||||
spellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownSpells) +
|
||||
spellSlot * 4);
|
||||
} else {
|
||||
spellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownPetSpells) +
|
||||
spellSlot * 4);
|
||||
}
|
||||
}
|
||||
|
||||
return spellId;
|
||||
}
|
||||
|
||||
uint32_t Script_CastSpellByNameNoQueue(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
|
||||
uint32_t Script_CastSpellByNameNoQueue(uintptr_t *luaState) {
|
||||
DEBUG_LOG("Casting next spell without queuing");
|
||||
// turn on forceQueue and then call regular CastSpellByName
|
||||
gNoQueueCast = true;
|
||||
@@ -56,7 +41,7 @@ namespace Nampower {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t Script_QueueSpellByName(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
|
||||
uint32_t Script_QueueSpellByName(uintptr_t *luaState) {
|
||||
DEBUG_LOG("Force queuing next cast spell");
|
||||
// turn on forceQueue and then call regular CastSpellByName
|
||||
gForceQueueCast = true;
|
||||
@@ -78,7 +63,7 @@ namespace Nampower {
|
||||
return spellStopCasting(luaState);
|
||||
}
|
||||
|
||||
uint32_t Script_IsSpellInRange(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
|
||||
uint32_t Script_IsSpellInRange(uintptr_t *luaState) {
|
||||
auto param1IsString = lua_isstring(luaState, 1);
|
||||
auto param1IsNumber = lua_isnumber(luaState, 1);
|
||||
if (param1IsString || param1IsNumber) {
|
||||
@@ -150,7 +135,7 @@ namespace Nampower {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Script_IsSpellUsable(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
|
||||
uint32_t Script_IsSpellUsable(uintptr_t *luaState) {
|
||||
auto param1IsString = lua_isstring(luaState, 1);
|
||||
auto param1IsNumber = lua_isnumber(luaState, 1);
|
||||
if (param1IsString || param1IsNumber) {
|
||||
@@ -204,7 +189,7 @@ namespace Nampower {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Script_GetCurrentCastingInfo(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
|
||||
uint32_t Script_GetCurrentCastingInfo(uintptr_t *luaState) {
|
||||
auto const castingSpellId = reinterpret_cast<uint32_t *>(Offsets::CastingSpellId);
|
||||
lua_pushnumber(luaState, *castingSpellId);
|
||||
|
||||
@@ -246,7 +231,7 @@ namespace Nampower {
|
||||
return 7;
|
||||
}
|
||||
|
||||
uint32_t Script_GetSpellIdForName(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
|
||||
uint32_t Script_GetSpellIdForName(uintptr_t *luaState) {
|
||||
if (lua_isstring(luaState, 1)) {
|
||||
auto const spellName = lua_tostring(luaState, 1);
|
||||
auto const spellId = GetSpellIdFromSpellName(spellName);
|
||||
@@ -259,7 +244,7 @@ namespace Nampower {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Script_GetSpellNameAndRankForId(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
|
||||
uint32_t Script_GetSpellNameAndRankForId(uintptr_t *luaState) {
|
||||
if (lua_isnumber(luaState, 1)) {
|
||||
auto const spellId = uint32_t(lua_tonumber(luaState, 1));
|
||||
auto const spell = game::GetSpellInfo(spellId);
|
||||
@@ -271,7 +256,7 @@ namespace Nampower {
|
||||
return 2;
|
||||
} else {
|
||||
DEBUG_LOG("Spell not found for id: " << spellId);
|
||||
lua_error(luaState, "Spell not found");
|
||||
lua_error(luaState, "Spell not found");
|
||||
}
|
||||
} else {
|
||||
lua_error(luaState, "Usage: GetSpellNameAndRankForId(spellId)");
|
||||
@@ -280,7 +265,7 @@ namespace Nampower {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Script_GetSpellSlotTypeIdForName(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
|
||||
uint32_t Script_GetSpellSlotTypeIdForName(uintptr_t *luaState) {
|
||||
if (lua_isstring(luaState, 1)) {
|
||||
auto const spellName = lua_tostring(luaState, 1);
|
||||
uint32_t bookType;
|
||||
@@ -330,7 +315,7 @@ namespace Nampower {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Script_ChannelStopCastingNextTick(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
|
||||
uint32_t Script_ChannelStopCastingNextTick(uintptr_t *luaState) {
|
||||
if (gCastData.channeling) {
|
||||
DEBUG_LOG("ChannelStopCastingNextTick activated, canceling next tick");
|
||||
gCastData.cancelChannelNextTick = true;
|
||||
@@ -339,7 +324,7 @@ namespace Nampower {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Script_GetNampowerVersion(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
|
||||
uint32_t Script_GetNampowerVersion(uintptr_t *luaState) {
|
||||
lua_pushnumber(luaState, MAJOR_VERSION);
|
||||
lua_pushnumber(luaState, MINOR_VERSION);
|
||||
lua_pushnumber(luaState, PATCH_VERSION);
|
||||
@@ -347,7 +332,7 @@ namespace Nampower {
|
||||
return 3;
|
||||
}
|
||||
|
||||
uint32_t Script_GetItemLevel(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
|
||||
uint32_t Script_GetItemLevel(uintptr_t *luaState) {
|
||||
if (lua_isnumber(luaState, 1)) {
|
||||
auto const itemId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
|
||||
|
||||
@@ -380,7 +365,7 @@ namespace Nampower {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Script_QueueScript(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
|
||||
uint32_t Script_QueueScript(uintptr_t *luaState) {
|
||||
DEBUG_LOG("Trying to queue script");
|
||||
|
||||
auto const currentTime = GetTime();
|
||||
@@ -418,7 +403,7 @@ namespace Nampower {
|
||||
return runScript(luaState);
|
||||
}
|
||||
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool RunQueuedScript(int priority) {
|
||||
@@ -440,4 +425,537 @@ namespace Nampower {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Script_GetItemStats(uintptr_t *luaState) {
|
||||
if (!lua_isnumber(luaState, 1)) {
|
||||
lua_error(luaState, "Usage: GetItemStats(itemId)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t itemId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
|
||||
|
||||
// Load item if not cached
|
||||
LoadItem(itemId);
|
||||
|
||||
// Get from cache
|
||||
game::ItemStats_C *item = GetItemStats(itemId);
|
||||
if (!item) {
|
||||
lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create new table
|
||||
lua_newtable(luaState);
|
||||
|
||||
// Push all simple fields using descriptors
|
||||
PushFieldsToLua(luaState, item, itemStatsFields, itemStatsFieldsCount);
|
||||
|
||||
// Push string fields manually (require language)
|
||||
auto const language = *reinterpret_cast<uint32_t *>(Offsets::Language);
|
||||
lua_pushstring(luaState, const_cast<char *>("displayName"));
|
||||
lua_pushstring(luaState,
|
||||
item->m_displayName[language] ? item->m_displayName[language] : const_cast<char *>(""));
|
||||
lua_settable(luaState, -3);
|
||||
|
||||
lua_pushstring(luaState, const_cast<char *>("description"));
|
||||
lua_pushstring(luaState, item->m_description ? item->m_description : const_cast<char *>(""));
|
||||
lua_settable(luaState, -3);
|
||||
|
||||
// Push all array fields using descriptors
|
||||
PushArrayFieldsToLua(luaState, item, itemStatsArrayFields, itemStatsArrayFieldsCount);
|
||||
|
||||
return 1; // Return the table
|
||||
}
|
||||
|
||||
uint32_t Script_GetSpellRec(uintptr_t *luaState) {
|
||||
if (!lua_isnumber(luaState, 1)) {
|
||||
lua_error(luaState, "Usage: GetSpellRec(spellId)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t spellId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
|
||||
|
||||
// Get spell info
|
||||
auto spell = game::GetSpellInfo(spellId);
|
||||
if (!spell) {
|
||||
lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create new table
|
||||
lua_newtable(luaState);
|
||||
|
||||
// Push all simple fields using descriptors
|
||||
PushFieldsToLua(luaState, spell, spellRecFields, spellRecFieldsCount);
|
||||
|
||||
// Push string fields manually (require language)
|
||||
auto const language = *reinterpret_cast<uint32_t *>(Offsets::Language);
|
||||
lua_pushstring(luaState, const_cast<char *>("name"));
|
||||
lua_pushstring(luaState, spell->SpellName[language] ? const_cast<char *>(spell->SpellName[language])
|
||||
: const_cast<char *>(""));
|
||||
lua_settable(luaState, -3);
|
||||
|
||||
lua_pushstring(luaState, const_cast<char *>("rank"));
|
||||
lua_pushstring(luaState, reinterpret_cast<const char *>(spell->Rank[language])
|
||||
? const_cast<char *>(reinterpret_cast<const char *>(spell->Rank[language]))
|
||||
: const_cast<char *>(""));
|
||||
lua_settable(luaState, -3);
|
||||
|
||||
// Push all array fields using descriptors
|
||||
PushArrayFieldsToLua(luaState, spell, spellRecArrayFields, spellRecArrayFieldsCount);
|
||||
|
||||
return 1; // Return the table
|
||||
}
|
||||
|
||||
uint32_t Script_GetItemStatsField(uintptr_t *luaState) {
|
||||
if (!lua_isnumber(luaState, 1) || !lua_isstring(luaState, 2)) {
|
||||
lua_error(luaState, "Usage: GetItemStatsField(itemId, fieldName)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
InitializeFieldMaps();
|
||||
|
||||
uint32_t itemId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
|
||||
const char *fieldName = lua_tostring(luaState, 2);
|
||||
|
||||
// Load item if not cached
|
||||
LoadItem(itemId);
|
||||
|
||||
// Get from cache
|
||||
game::ItemStats_C *item = GetItemStats(itemId);
|
||||
if (!item) {
|
||||
lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// O(1) hash table lookup for simple fields
|
||||
auto simpleIt = itemStatsFieldMap.find(fieldName);
|
||||
if (simpleIt != itemStatsFieldMap.end()) {
|
||||
size_t i = simpleIt->second;
|
||||
const char *fieldPtr = reinterpret_cast<const char *>(item) + itemStatsFields[i].offset;
|
||||
|
||||
switch (itemStatsFields[i].type) {
|
||||
case FieldType::INT32:
|
||||
lua_pushnumber(luaState, *reinterpret_cast<const int32_t *>(fieldPtr));
|
||||
return 1;
|
||||
case FieldType::UINT32:
|
||||
lua_pushnumber(luaState, *reinterpret_cast<const uint32_t *>(fieldPtr));
|
||||
return 1;
|
||||
case FieldType::FLOAT:
|
||||
lua_pushnumber(luaState, *reinterpret_cast<const float *>(fieldPtr));
|
||||
return 1;
|
||||
case FieldType::STRING: {
|
||||
const char *str = *reinterpret_cast<const char *const *>(fieldPtr);
|
||||
lua_pushstring(luaState, str ? const_cast<char *>(str) : const_cast<char *>(""));
|
||||
return 1;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// O(1) hash table lookup for array fields
|
||||
auto arrayIt = itemStatsArrayFieldMap.find(fieldName);
|
||||
if (arrayIt != itemStatsArrayFieldMap.end()) {
|
||||
size_t i = arrayIt->second;
|
||||
const auto &field = itemStatsArrayFields[i];
|
||||
lua_newtable(luaState);
|
||||
|
||||
const char *fieldPtr = reinterpret_cast<const char *>(item) + field.offset;
|
||||
|
||||
for (size_t j = 0; j < field.count; ++j) {
|
||||
lua_pushnumber(luaState, j + 1);
|
||||
|
||||
switch (field.type) {
|
||||
case FieldType::INT32:
|
||||
lua_pushnumber(luaState, reinterpret_cast<const int32_t *>(fieldPtr)[j]);
|
||||
break;
|
||||
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;
|
||||
}
|
||||
|
||||
// Check for special string fields
|
||||
if (strcmp(fieldName, "displayName") == 0) {
|
||||
auto const language = *reinterpret_cast<uint32_t *>(Offsets::Language);
|
||||
lua_pushstring(luaState,
|
||||
item->m_displayName[language] ? item->m_displayName[language] : const_cast<char *>(""));
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(fieldName, "description") == 0) {
|
||||
lua_pushstring(luaState, item->m_description ? item->m_description : const_cast<char *>(""));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Field not found
|
||||
lua_error(luaState, "Unknown field name");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Script_GetSpellRecField(uintptr_t *luaState) {
|
||||
if (!lua_isnumber(luaState, 1) || !lua_isstring(luaState, 2)) {
|
||||
lua_error(luaState, "Usage: GetSpellRecField(spellId, fieldName)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
InitializeFieldMaps();
|
||||
|
||||
uint32_t spellId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
|
||||
const char *fieldName = lua_tostring(luaState, 2);
|
||||
|
||||
// Get spell info
|
||||
auto spell = game::GetSpellInfo(spellId);
|
||||
if (!spell) {
|
||||
lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// O(1) hash table lookup for simple fields
|
||||
auto simpleIt = spellRecFieldMap.find(fieldName);
|
||||
if (simpleIt != spellRecFieldMap.end()) {
|
||||
size_t i = simpleIt->second;
|
||||
const char *fieldPtr = reinterpret_cast<const char *>(spell) + spellRecFields[i].offset;
|
||||
|
||||
switch (spellRecFields[i].type) {
|
||||
case FieldType::INT32:
|
||||
lua_pushnumber(luaState, *reinterpret_cast<const int32_t *>(fieldPtr));
|
||||
return 1;
|
||||
case FieldType::UINT32:
|
||||
lua_pushnumber(luaState, *reinterpret_cast<const uint32_t *>(fieldPtr));
|
||||
return 1;
|
||||
case FieldType::FLOAT:
|
||||
lua_pushnumber(luaState, *reinterpret_cast<const float *>(fieldPtr));
|
||||
return 1;
|
||||
case FieldType::UINT64:
|
||||
lua_pushnumber(luaState, static_cast<double>(*reinterpret_cast<const uint64_t *>(fieldPtr)));
|
||||
return 1;
|
||||
case FieldType::STRING: {
|
||||
const char *str = *reinterpret_cast<const char *const *>(fieldPtr);
|
||||
lua_pushstring(luaState, str ? const_cast<char *>(str) : const_cast<char *>(""));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// O(1) hash table lookup for array fields
|
||||
auto arrayIt = spellRecArrayFieldMap.find(fieldName);
|
||||
if (arrayIt != spellRecArrayFieldMap.end()) {
|
||||
size_t i = arrayIt->second;
|
||||
const auto &field = spellRecArrayFields[i];
|
||||
lua_newtable(luaState);
|
||||
|
||||
const char *fieldPtr = reinterpret_cast<const char *>(spell) + field.offset;
|
||||
|
||||
for (size_t j = 0; j < field.count; ++j) {
|
||||
lua_pushnumber(luaState, j + 1);
|
||||
|
||||
switch (field.type) {
|
||||
case FieldType::INT32:
|
||||
lua_pushnumber(luaState, reinterpret_cast<const int32_t *>(fieldPtr)[j]);
|
||||
break;
|
||||
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;
|
||||
}
|
||||
|
||||
// Check for special string fields
|
||||
if (strcmp(fieldName, "name") == 0) {
|
||||
auto const language = *reinterpret_cast<uint32_t *>(Offsets::Language);
|
||||
lua_pushstring(luaState, spell->SpellName[language] ? const_cast<char *>(spell->SpellName[language])
|
||||
: const_cast<char *>(""));
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(fieldName, "rank") == 0) {
|
||||
auto const language = *reinterpret_cast<uint32_t *>(Offsets::Language);
|
||||
lua_pushstring(luaState, reinterpret_cast<const char *>(spell->Rank[language])
|
||||
? const_cast<char *>(reinterpret_cast<const char *>(spell->Rank[language]))
|
||||
: const_cast<char *>(""));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Field not found
|
||||
lua_error(luaState, "Unknown field name");
|
||||
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();
|
||||
|
||||
// Check if first parameter is not a number
|
||||
if (lua_isnumber(luaState, 1) == 0) {
|
||||
const char *spellStr = lua_tostring(luaState, 1);
|
||||
|
||||
// Check if string starts with "spellId:" (case-insensitive)
|
||||
if (_strnicmp(spellStr, "spellId:", 8) == 0) {
|
||||
// Extract spell ID from string
|
||||
uint32_t spellId = atoi(spellStr + 8);
|
||||
|
||||
if (spellId > 0) {
|
||||
// Check if second parameter has "pet"
|
||||
uint32_t bookType = 0;
|
||||
if (lua_isstring(luaState, 2)) {
|
||||
const char *bookTypeStr = lua_tostring(luaState, 2);
|
||||
if (_strcmpi(bookTypeStr, "pet") == 0) {
|
||||
bookType = 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t spellSlot = ConvertSpellIdToSpellSlot(spellId, bookType);
|
||||
if(!spellSlot){
|
||||
lua_error(luaState, "Unable convert spell id to spell slot.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (slot) {
|
||||
*slot = spellSlot;
|
||||
}
|
||||
if (type) {
|
||||
*type = bookType; // Use the requested bookType
|
||||
}
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
lua_error(luaState, "Unable parse spell id. Format is spellId:123.");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
uint32_t bookType;
|
||||
auto spellSlot = GetSpellSlotAndTypeForName(spellStr, &bookType);
|
||||
// returns large number if spell not found
|
||||
if (spellSlot == 0 || spellSlot > 100000) {
|
||||
lua_error(luaState, "Unable to find spell slot for spell name.");
|
||||
return 0;
|
||||
} else {
|
||||
if (slot) {
|
||||
*slot = spellSlot;
|
||||
}
|
||||
if (type) {
|
||||
*type = bookType;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
auto const getSpellSlotFromLua = detour->GetTrampolineT<GetSpellSlotFromLuaT>();
|
||||
// Fall back to original function
|
||||
return getSpellSlotFromLua(param_1, slot, type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
+28
-12
@@ -8,31 +8,47 @@
|
||||
#include "main.hpp"
|
||||
|
||||
namespace Nampower {
|
||||
uint32_t Script_CastSpellByNameNoQueue(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
uint32_t Script_CastSpellByNameNoQueue(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_QueueSpellByName(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
uint32_t Script_QueueSpellByName(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_IsSpellInRange(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
uint32_t Script_IsSpellInRange(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_IsSpellUsable(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
uint32_t Script_IsSpellUsable(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_SpellStopCastingHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_GetCurrentCastingInfo(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
uint32_t Script_GetCurrentCastingInfo(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_GetSpellIdForName(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
uint32_t Script_GetSpellIdForName(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_GetSpellNameAndRankForId(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
uint32_t Script_GetSpellNameAndRankForId(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_GetSpellSlotTypeIdForName(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
uint32_t Script_GetSpellSlotTypeIdForName(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_ChannelStopCastingNextTick(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
uint32_t Script_ChannelStopCastingNextTick(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_GetNampowerVersion(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
uint32_t Script_GetNampowerVersion(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_GetItemLevel(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
uint32_t Script_GetItemLevel(uintptr_t *luaState);
|
||||
|
||||
bool Script_QueueScript(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
|
||||
uint32_t Script_QueueScript(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_GetItemStats(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_GetSpellRec(uintptr_t *luaState);
|
||||
|
||||
uint32_t Script_GetItemStatsField(uintptr_t *luaState);
|
||||
|
||||
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);
|
||||
}
|
||||
+10
-23
@@ -108,8 +108,6 @@ namespace Nampower {
|
||||
}
|
||||
|
||||
gLastCastData.startTimeMs = currentTime;
|
||||
|
||||
// if queued cast, simulate spellcast start
|
||||
}
|
||||
|
||||
void CastQueuedNonGcdSpell() {
|
||||
@@ -120,7 +118,7 @@ namespace Nampower {
|
||||
|
||||
gCastData.castingQueuedSpell = true;
|
||||
gCastData.numRetries = nonGcdCastParams.numRetries;
|
||||
Spell_C_CastSpellHook(castSpellDetour, nonGcdCastParams.playerUnit, nonGcdCastParams.spellId,
|
||||
Spell_C_CastSpellHook(castSpellDetour, nonGcdCastParams.casterUnit, nonGcdCastParams.spellId,
|
||||
nonGcdCastParams.item, nonGcdCastParams.guid);
|
||||
gLastCastData.wasQueued = true;
|
||||
} else {
|
||||
@@ -140,7 +138,7 @@ namespace Nampower {
|
||||
DEBUG_LOG("Triggering queued cast of " << game::GetSpellName(gLastNormalCastParams.spellId));
|
||||
gCastData.castingQueuedSpell = true;
|
||||
gCastData.numRetries = gLastNormalCastParams.numRetries;
|
||||
Spell_C_CastSpellHook(castSpellDetour, gLastNormalCastParams.playerUnit, gLastNormalCastParams.spellId,
|
||||
Spell_C_CastSpellHook(castSpellDetour, gLastNormalCastParams.casterUnit, gLastNormalCastParams.spellId,
|
||||
gLastNormalCastParams.item, gLastNormalCastParams.guid);
|
||||
gLastCastData.wasQueued = true;
|
||||
} else {
|
||||
@@ -163,7 +161,7 @@ namespace Nampower {
|
||||
}
|
||||
|
||||
void SaveCastParams(CastSpellParams *params,
|
||||
uint32_t *playerUnit,
|
||||
uint32_t *casterUnit,
|
||||
uint32_t spellId,
|
||||
uintptr_t *item,
|
||||
std::uint64_t guid,
|
||||
@@ -172,7 +170,7 @@ namespace Nampower {
|
||||
uint32_t castStartTimeMs,
|
||||
CastType castType,
|
||||
uint32_t numRetries) {
|
||||
params->playerUnit = playerUnit;
|
||||
params->casterUnit = casterUnit;
|
||||
params->spellId = spellId;
|
||||
params->item = item;
|
||||
params->guid = guid;
|
||||
@@ -218,18 +216,6 @@ namespace Nampower {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool
|
||||
Spell_C_CastSpellHook(hadesmem::PatchDetourBase *detour, uint32_t *casterUnit, uint32_t spellId, uintptr_t *item,
|
||||
std::uint64_t guid) {
|
||||
@@ -257,14 +243,14 @@ namespace Nampower {
|
||||
auto const isSpecialSpell = SpellIsAttackTradeskillOrEnchant(spell);
|
||||
auto const currentTargetGuid = game::GetCurrentTargetGuid();
|
||||
|
||||
if(spellIsChanneling) {
|
||||
if (spellIsChanneling) {
|
||||
auto casterGuid = game::UnitGetGuid(casterUnit);
|
||||
if (casterGuid == game::ClntObjMgrGetActivePlayerGuid()) {
|
||||
// check that locked target guid matches our unit target guid
|
||||
auto unitTargetGuid = game::UnitGetTargetGuid(casterUnit);
|
||||
if (unitTargetGuid != currentTargetGuid) {
|
||||
DEBUG_LOG("Updating selection target to " << currentTargetGuid << " from " << unitTargetGuid);
|
||||
setSelectionTarget(currentTargetGuid);
|
||||
SetSelectionTarget(currentTargetGuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,7 +285,8 @@ namespace Nampower {
|
||||
itemId = game::GetItemId((game::CGItem_C *) item);
|
||||
}
|
||||
|
||||
DEBUG_LOG("Attempt cast " << spellName << " item " << item << " on guid " << guid << " target " << currentTargetGuid
|
||||
DEBUG_LOG("Attempt cast " << spellName << " item " << item << " on guid " << guid << " target "
|
||||
<< currentTargetGuid
|
||||
<< ", time since last cast " << currentTime - gLastCastData.startTimeMs);
|
||||
|
||||
// clear cooldown queue if we are casting a spell
|
||||
@@ -522,7 +509,7 @@ namespace Nampower {
|
||||
// otherwise can break cooldown in the client and cause unnecessary errors
|
||||
if (castTime == 0 || spell->Attributes & game::SPELL_ATTR_DISABLED_WHILE_ACTIVE) {
|
||||
// check if spam protection is enabled
|
||||
if(gUserSettings.spamProtectionEnabled) {
|
||||
if (gUserSettings.spamProtectionEnabled) {
|
||||
auto castParams = gCastHistory.findNewestWaitingForServerSpellId(spellId);
|
||||
if (castParams &&
|
||||
currentTime - castParams->castStartTimeMs < 500) {
|
||||
@@ -643,7 +630,7 @@ namespace Nampower {
|
||||
|
||||
|
||||
TriggerSpellQueuedEvent(ON_SWING_QUEUE_POPPED, gLastOnSwingCastParams.spellId);
|
||||
Spell_C_CastSpellHook(castSpellDetour, gLastOnSwingCastParams.playerUnit,
|
||||
Spell_C_CastSpellHook(castSpellDetour, gLastOnSwingCastParams.casterUnit,
|
||||
gLastOnSwingCastParams.spellId,
|
||||
gLastOnSwingCastParams.item, gLastOnSwingCastParams.guid);
|
||||
gCastData.onSwingQueued = false;
|
||||
|
||||
@@ -277,7 +277,6 @@ namespace Nampower {
|
||||
return spellDelayed(opCode, packet);
|
||||
}
|
||||
|
||||
|
||||
int CastResultHandlerHook(hadesmem::PatchDetourBase *detour, uint32_t *opCode, CDataStore *packet) {
|
||||
auto const castResultHandler = detour->GetTrampolineT<PacketHandlerT>();
|
||||
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ struct CastSpellParams {
|
||||
/* Original cast spell function arguments */
|
||||
uint64_t castId;
|
||||
|
||||
uint32_t *playerUnit;
|
||||
uint32_t *casterUnit;
|
||||
uint32_t spellId;
|
||||
uintptr_t *item;
|
||||
uint64_t guid;
|
||||
|
||||
@@ -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