fix: expansion-aware SpellRange.dbc loading and Classic spell tooltip fields

SpellRange.dbc layout fix:
- Classic 1.12 uses field 2 (MaxRange), TBC/WotLK use field 4 (MaxRangeHostile)
- Add SpellRange layout to each expansion's dbc_layouts.json
- Replace hardcoded field 5 with layout-driven lookup in SpellRange loading
- Corrects previously wrong range values in WotLK spellbook tooltips

Classic 1.12 Spell.dbc field additions:
- Add CastingTimeIndex=15, PowerType=28, ManaCost=29, RangeIndex=33 to
  classic/dbc_layouts.json so Classic spellbook shows mana cost, cast time,
  and range in tooltips

Trainer fieldCount guard:
- Lower Trainer::loadSpellNameCache() Spell.dbc fieldCount threshold from
  154 to 148 so Classic trainers correctly resolve spell names from Spell.dbc
This commit is contained in:
Kelsi 2026-03-10 18:09:21 -07:00
parent 2f3f9f1a21
commit 53d144c51e
5 changed files with 23 additions and 6 deletions

View file

@ -1,8 +1,10 @@
{
"Spell": {
"ID": 0, "Attributes": 5, "IconID": 117,
"Name": 120, "Tooltip": 147, "Rank": 129, "SchoolEnum": 1
"Name": 120, "Tooltip": 147, "Rank": 129, "SchoolEnum": 1,
"CastingTimeIndex": 15, "PowerType": 28, "ManaCost": 29, "RangeIndex": 33
},
"SpellRange": { "MaxRange": 2 },
"ItemDisplayInfo": {
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,

View file

@ -3,6 +3,7 @@
"ID": 0, "Attributes": 5, "IconID": 124,
"Name": 127, "Tooltip": 154, "Rank": 136, "SchoolMask": 215
},
"SpellRange": { "MaxRange": 4 },
"ItemDisplayInfo": {
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,

View file

@ -4,6 +4,7 @@
"Name": 136, "Tooltip": 139, "Rank": 153, "SchoolMask": 225,
"PowerType": 14, "ManaCost": 39, "CastingTimeIndex": 47, "RangeIndex": 49
},
"SpellRange": { "MaxRange": 4 },
"ItemDisplayInfo": {
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,

View file

@ -15671,8 +15671,10 @@ void GameHandler::loadSpellNameCache() {
return;
}
if (dbc->getFieldCount() < 154) {
LOG_WARNING("Trainer: Spell.dbc has too few fields");
// Classic 1.12 Spell.dbc has 148 fields; TBC/WotLK have more.
// Require at least 148 so Classic trainers can resolve spell names.
if (dbc->getFieldCount() < 148) {
LOG_WARNING("Trainer: Spell.dbc has too few fields (", dbc->getFieldCount(), ")");
return;
}

View file

@ -66,15 +66,26 @@ void SpellbookScreen::loadSpellDBC(pipeline::AssetManager* assetManager) {
}
}
// Load SpellRange.dbc: field 0=ID, field 5=MaxRangeHostile (float)
// Load SpellRange.dbc. Field layout differs by expansion:
// Classic 1.12: 0=ID, 1=MinRange, 2=MaxRange, 3=Flags, 4+=strings
// TBC / WotLK: 0=ID, 1=MinRangeFriendly, 2=MinRangeHostile,
// 3=MaxRangeFriendly, 4=MaxRangeHostile, 5=Flags, 6+=strings
// The correct field is declared in each expansion's dbc_layouts.json.
uint32_t spellRangeMaxField = 4; // WotLK / TBC default: MaxRangeHostile
const auto* spellRangeL = pipeline::getActiveDBCLayout()
? pipeline::getActiveDBCLayout()->getLayout("SpellRange")
: nullptr;
if (spellRangeL) {
try { spellRangeMaxField = (*spellRangeL)["MaxRange"]; } catch (...) {}
}
std::unordered_map<uint32_t, float> rangeMap; // index → max yards
auto rangeDbc = assetManager->loadDBC("SpellRange.dbc");
if (rangeDbc && rangeDbc->isLoaded()) {
uint32_t rangeFieldCount = rangeDbc->getFieldCount();
if (rangeFieldCount >= 6) {
if (rangeFieldCount > spellRangeMaxField) {
for (uint32_t i = 0; i < rangeDbc->getRecordCount(); ++i) {
uint32_t id = rangeDbc->getUInt32(i, 0);
float maxRange = rangeDbc->getFloat(i, 5);
float maxRange = rangeDbc->getFloat(i, spellRangeMaxField);
if (id > 0 && maxRange > 0.0f)
rangeMap[id] = maxRange;
}