-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathRadius.cpp
More file actions
120 lines (109 loc) · 5.03 KB
/
Copy pathRadius.cpp
File metadata and controls
120 lines (109 loc) · 5.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// This file is part of ClassicAPI.
//
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
// of the GNU General Public License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
// `C_Spell.GetSpellRadius(spellID)` and the parallel
// `GetSpellRadius(slot, bookType)` -> (baseRadius, modifiedRadius)
//
// Returns the spell's AOE radius in yards, or nil for a non-AOE spell
// (no effect carries a radius). Two values:
// - baseRadius — the raw SpellRadius.dbc value (caster-independent,
// what an AOE tracker watching other units wants)
// - modifiedRadius — baseRadius with the LOCAL PLAYER's spell modifiers
// (talents / items, e.g. a Mage's Arctic Reach)
// applied. Equal to baseRadius when the player has
// no matching modifier.
//
// Two signatures mirroring the other dual-signature spell globals
// (`GetSpellInfo`, `SpellHasRange`, etc.): the namespaced form takes a
// modern spell *identifier* (numeric ID or name), the bare global takes
// the vanilla `(slot, bookType)` spellbook position. `bookType` is the
// `GetSpellName` convention: `"pet"` → pet book, anything else (or
// omitted) → player book.
//
// baseRadius reads `Spell.dbc`'s per-effect `EffectRadiusIndex[3]` and
// resolves each into `SpellRadius.dbc`, taking the largest base radius
// across the three effects. No caster-level scaling (the engine's
// FUN_006e6350 also adds `radiusPerLevel * casterLevel`, which needs a
// unit context; modern `GetSpellRadius` reports the base value).
//
// modifiedRadius applies the local player's SpellMods for op 6 (radius)
// via `Spell::Mod::Apply`. It is inherently local-player-only — the
// client tracks spell mods for the player alone, so there's no way to
// know another caster's talents.
//
// All DBCs / mod tables are resident from boot, so the lookup is
// synchronous with no caching.
#include "Game.h"
#include "Offsets.h"
#include "dbc/Lookup.h"
#include "spell/Arg.h"
#include "spell/Lookup.h"
#include "spell/Mod.h"
#include <cstdint>
#include <cstring>
namespace Spell::Radius {
// Largest base radius across the spell's effects, or a negative sentinel
// (< 0) if no effect has a radius.
static float MaxBaseRadius(const uint8_t *record) {
float best = -1.0f;
for (int i = 0; i < Offsets::SPELL_RECORD_EFFECT_COUNT; ++i) {
const int radiusIndex = *reinterpret_cast<const int *>(
record + Offsets::OFF_SPELL_RECORD_EFFECT_RADIUS_INDEX + i * 4);
const uint8_t *rad = DBC::Record(Offsets::VAR_SPELL_RADIUS_RECORDS,
Offsets::VAR_SPELL_RADIUS_COUNT,
static_cast<uint32_t>(radiusIndex));
if (rad == nullptr)
continue;
const float r = *reinterpret_cast<const float *>(rad + Offsets::OFF_SPELL_RADIUS_VALUE);
if (r > best)
best = r;
}
return best;
}
// Pushes (baseRadius, modifiedRadius) for `spellID`, or nil (0 returns)
// for an invalid / non-AOE spell.
static int PushRadiusForSpellID(void *L, int spellID) {
const uint8_t *record = Spell::Lookup::RecordForID(spellID);
if (record == nullptr)
return 0; // nil
const float base = MaxBaseRadius(record);
if (base < 0.0f)
return 0; // nil — not an AOE spell
Game::Lua::PushNumber(L, static_cast<double>(base));
Game::Lua::PushNumber(L,
static_cast<double>(Spell::Mod::Apply(record, Offsets::SPELLMOD_OP_RADIUS, base)));
return 2;
}
// `C_Spell.GetSpellRadius(spellID)` — modern spell-identifier form.
static int __fastcall Script_C_Spell_GetSpellRadius(void *L) {
return PushRadiusForSpellID(L, Spell::Arg::ResolveSpellID(L, 1));
}
// `GetSpellRadius(slot, bookType)` — vanilla-style positional shape.
static int __fastcall Script_GetSpellRadius(void *L) {
if (!Game::Lua::IsNumber(L, 1))
return 0; // nil
const int slot = static_cast<int>(Game::Lua::ToNumber(L, 1));
int bookType = 0;
if (Game::Lua::IsString(L, 2)) {
const char *s = Game::Lua::ToString(L, 2);
if (s != nullptr && _stricmp(s, "pet") == 0)
bookType = 1;
}
return PushRadiusForSpellID(L, Spell::Lookup::SpellbookSlotToID(slot, bookType));
}
static void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Spell", "GetSpellRadius",
&Script_C_Spell_GetSpellRadius);
Game::Lua::RegisterGlobalFunction("GetSpellRadius", &Script_GetSpellRadius);
}
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
} // namespace Spell::Radius