diff --git a/CHANGELOG.md b/CHANGELOG.md index de30557..7421500 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,15 @@ All notable changes to `mod-gold-rush` will be documented in this file. global `GoldRush.NodeEntries` pool exactly as before. ### Fixed +- Herb/ore categorization no longer guesses from display-name keywords. The old + `SplitByCategory()` matched "lotus"/"clover"/"thorn"/"bloom" for herbs and + "vein" for ore, which only worked for the original Northrend default pool and + silently fell back to the unsplit pool for almost every v2 zone (Outland/Classic + ore uses "Deposit", not "Vein"; most herb names match no keyword). Replaced with + a static ID→category lookup table built from the DB-verified entry IDs in + `docs/zone-specific-resources-spec.md`, with the old keyword check retained only + as a fallback for entries not in the table. See + `docs/herb-ore-category-fix-spec.md`. - Event start/end announcements no longer crash the server. `StartEvent()` and `EndEvent()` both used `ChatHandler(nullptr).SendWorldText(...)` to broadcast server-wide -- `ChatHandler::SendWorldText` assumes a real session and calls diff --git a/src/GoldRush.cpp b/src/GoldRush.cpp index 9e7e873..99f94ce 100644 --- a/src/GoldRush.cpp +++ b/src/GoldRush.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,47 @@ static constexpr float MaxVerticalDrift = 20.0f; // reject ground this far // Mirrors INVALID_HEIGHT from GridTerrainData.h without taking a dependency on that header. static constexpr float InvalidHeightSentinel = -99999.0f; +// Static ID -> category lookup for herb/ore classification. Every entry ID used +// across the 57-zone GoldRush.ZonePool (plus the module's own default global +// GoldRush.NodeEntries) was individually verified against +// acore_world.gameobject_template when the pool was built +// (docs/zone-specific-resources-spec.md). Guessing the category from the display +// name (the old keyword-list approach) broke for almost every zone: Outland/Classic +// ore uses "Deposit" rather than "Vein", and most herb names don't contain any of +// the four hardcoded herb keywords. true = herb, false = ore. +static const std::unordered_map IsHerbById = { + // Classic herbs + { 1618, true }, { 3724, true }, { 1617, true }, { 3725, true }, { 1619, true }, { 3726, true }, // Peacebloom, Silverleaf, Earthroot + { 1620, true }, { 3727, true }, { 1621, true }, { 3729, true }, { 2045, true }, { 1622, true }, { 3730, true }, // Mageroyal, Briarthorn, Stranglekelp, Bruiseweed + { 1623, true }, { 1624, true }, { 2041, true }, { 2042, true }, { 2046, true }, { 2043, true }, { 2866, true }, // Wild Steelbloom, Kingsblood, Liferoot, Fadeleaf, Goldthorn, Khadgar's Whisker, Firebloom + { 142140, true }, { 180165, true }, { 142141, true }, { 176642, true }, // Purple Lotus, Arthas' Tears + { 142142, true }, { 176636, true }, { 180164, true }, { 142143, true }, { 183046, true }, // Sungrass, Blindweed + { 142144, true }, { 142145, true }, { 176637, true }, { 176587, true }, { 176641, true }, // Ghost Mushroom, Gromsblood, Plaguebloom + { 176583, true }, { 176638, true }, { 180167, true }, { 176584, true }, { 176639, true }, // Golden Sansam, Dreamfoil + { 180168, true }, { 176586, true }, { 176640, true }, { 180166, true }, { 176588, true }, // Mountain Silversage, Icecap + { 191303, true }, { 176589, true }, // Firethorn, Black Lotus + // Outland herbs + { 181270, true }, { 183044, true }, { 181271, true }, { 183045, true }, { 181275, true }, // Felweed, Dreaming Glory, Ragveil + { 183043, true }, { 181277, true }, { 181276, true }, { 181279, true }, { 181280, true }, { 181281, true }, // Terocone, Flame Cap, Netherbloom, Nightmare Vine, Mana Thistle + // Northrend herbs + { 189973, true }, { 190171, true }, { 190172, true }, { 190176, true }, { 190170, true }, // Goldclover, Lichbloom, Icethorn, Frost Lotus, Talandra's Rose + { 191019, true }, { 190169, true }, // Adder's Tongue, Tiger Lily + // Classic ore + { 1731, false }, { 2055, false }, { 3763, false }, { 103713, false }, { 181248, false }, // Copper Vein + { 1732, false }, { 2054, false }, { 3764, false }, { 103711, false }, { 181249, false }, // Tin Vein + { 1733, false }, { 105569, false }, // Silver Vein + { 1735, false }, // Iron Deposit + { 1734, false }, { 150080, false }, { 181109, false }, // Gold Vein + { 2040, false }, { 150079, false }, { 176645, false }, // Mithril Deposit + { 2047, false }, { 150081, false }, { 181108, false }, // Truesilver Deposit + { 324, false }, { 150082, false }, { 176643, false }, { 175404, false }, // Small/Rich Thorium Vein + { 165658, false }, // Dark Iron Deposit + // Outland ore + { 181555, false }, { 181556, false }, { 181569, false }, { 181570, false }, { 181557, false }, // Fel Iron, Adamantite (+ Rich), Khorium + // Northrend ore + { 189978, false }, { 189979, false }, { 189980, false }, { 189981, false }, { 191133, false }, // Cobalt/Saronite Deposit (+ Rich), Titanium Vein +}; + struct GoldRushSite { std::string ZoneLabel; @@ -657,9 +699,23 @@ private: if (!goinfo) continue; - std::string name = Normalize(goinfo->name); - bool isHerb = ContainsWord(name, "lotus") || ContainsWord(name, "clover") || ContainsWord(name, "thorn") || ContainsWord(name, "bloom"); - bool isOre = ContainsWord(name, "vein"); + // Prefer the verified ID -> category lookup table. The old name-keyword + // guess is kept only as a defensive fallback for entries not in the table + // (e.g. the original global-pool defaults or anything added by hand later + // without an explicit table update). + bool isHerb; + auto const it = IsHerbById.find(entry); + if (it != IsHerbById.end()) + { + isHerb = it->second; + } + else + { + std::string name = Normalize(goinfo->name); + isHerb = ContainsWord(name, "lotus") || ContainsWord(name, "clover") || ContainsWord(name, "thorn") || ContainsWord(name, "bloom"); + } + + bool isOre = !isHerb; if (herbs && isHerb) result.push_back(entry);