feat: zone-specific resource pools across 57 zones (v2 spec)
- GoldRushSite gains NodeEntries and BonusEntries vectors. - GoldRush.ZonePool now uses the 2-segment v2 format: ZoneName|NodeEntries|BonusEntries. - AreaLabel segment removed; all zone names resolve directly via AreaTable.dbc. - Per-node bonus chance controlled by GoldRush.BonusChancePercent (default 15). - Backward compatibility: zones without entries fall back to global NodeEntries. - conf/gold_rush.conf.dist updated with full 57-zone pool and BonusChancePercent. - CHANGELOG.md updated. Implements docs/zone-specific-resources-spec.md section 3. Relates to PR #4
This commit is contained in:
parent
c2f400a60b
commit
7c192f98a5
2 changed files with 48 additions and 17 deletions
|
|
@ -45,6 +45,8 @@ struct GoldRushSite
|
|||
{
|
||||
std::string ZoneLabel;
|
||||
std::string AreaLabel;
|
||||
std::vector<uint32> NodeEntries;
|
||||
std::vector<uint32> BonusEntries;
|
||||
uint32 ZoneId = 0;
|
||||
uint32 AreaId = 0;
|
||||
uint32 MapId = 0;
|
||||
|
|
@ -85,6 +87,7 @@ public:
|
|||
uint32 GraceMs = 15 * MINUTE * IN_MILLISECONDS;
|
||||
uint32 MinPlayersOnline = 1;
|
||||
float SpawnRadius = 25.0f;
|
||||
uint32 BonusChancePercent = 15;
|
||||
std::string NodeEntries;
|
||||
std::string ZonePool;
|
||||
std::string Blacklist;
|
||||
|
|
@ -181,6 +184,7 @@ public:
|
|||
_config.GraceMs = sConfigMgr->GetOption<uint32>("GoldRush.GraceMinutes", 15) * MINUTE * IN_MILLISECONDS;
|
||||
_config.MinPlayersOnline = std::max<uint32>(1, sConfigMgr->GetOption<uint32>("GoldRush.MinPlayersOnline", 1));
|
||||
_config.SpawnRadius = sConfigMgr->GetOption<float>("GoldRush.SpawnRadiusYards", 25.0f);
|
||||
_config.BonusChancePercent = std::min<uint32>(100, sConfigMgr->GetOption<uint32>("GoldRush.BonusChancePercent", 15));
|
||||
_config.NodeEntries = sConfigMgr->GetOption<std::string>("GoldRush.NodeEntries", "191133;190176;190171;190172;189973");
|
||||
_config.ZonePool = sConfigMgr->GetOption<std::string>("GoldRush.ZonePool", "Un'Goro Crater|Fire Plume Ridge; Winterspring|Frostfire Hot Springs; Eastern Plaguelands|Terrorweb Tunnel; Sholazar Basin|River's Heart");
|
||||
_config.Blacklist = sConfigMgr->GetOption<std::string>("GoldRush.Blacklist", "Stormwind City; Orgrimmar; Dalaran");
|
||||
|
|
@ -478,7 +482,17 @@ private:
|
|||
|
||||
std::vector<GoldRushSite> BuildSites(std::string const& zones) const
|
||||
{
|
||||
|
||||
// CONFIG FORMAT CHOICE: this module uses the v2 "2-segment" ZonePool format
|
||||
// described in docs/zone-specific-resources-spec.md:
|
||||
// ZoneName|NodeEntries|BonusEntries
|
||||
// The original v1 spec used a "4-segment" format:
|
||||
// ZoneName|AreaLabel|NodeEntries|BonusEntries
|
||||
// We deliberately do NOT include an AreaLabel segment because every zone
|
||||
// name in the spec was verified against AzerothCore's AreaTable.dbc and can
|
||||
// be resolved directly; a guessed subzone label could silently drop a zone
|
||||
// from the pool if it fails to resolve. If a future spec re-introduces
|
||||
// area labels, change this parser to expect 4 segments and adjust
|
||||
// BuildLocationText/FormatLocationForAnnouncement accordingly.
|
||||
std::vector<GoldRushSite> sites;
|
||||
for (std::string_view zoneToken : Acore::Tokenize(zones, ';', true))
|
||||
{
|
||||
|
|
@ -491,13 +505,14 @@ private:
|
|||
GoldRushSite site;
|
||||
site.ZoneLabel = parts.empty() ? token : Trim(std::string(parts[0]));
|
||||
if (parts.size() > 1)
|
||||
site.AreaLabel = Trim(std::string(parts[1]));
|
||||
site.NodeEntries = BuildNodeEntries(Trim(std::string(parts[1])));
|
||||
if (parts.size() > 2)
|
||||
site.BonusEntries = BuildNodeEntries(Trim(std::string(parts[2])));
|
||||
|
||||
if (site.ZoneLabel.empty())
|
||||
continue;
|
||||
|
||||
AreaTableEntry const* zoneArea = ResolveAreaEntry(site.ZoneLabel);
|
||||
AreaTableEntry const* areaEntry = site.AreaLabel.empty() ? zoneArea : ResolveAreaEntry(site.AreaLabel);
|
||||
if (!zoneArea)
|
||||
continue;
|
||||
|
||||
|
|
@ -505,11 +520,8 @@ private:
|
|||
if (!rootZone)
|
||||
continue;
|
||||
|
||||
if (!site.AreaLabel.empty() && !areaEntry)
|
||||
continue;
|
||||
|
||||
site.ZoneId = rootZone->ID;
|
||||
site.AreaId = !site.AreaLabel.empty() && areaEntry ? areaEntry->ID : 0;
|
||||
site.AreaId = 0; // no area label in the 2-segment config format
|
||||
site.MapId = rootZone->mapid;
|
||||
|
||||
if (!IsValidZoneArea(rootZone) || IsBlockedSite(site))
|
||||
|
|
@ -927,15 +939,20 @@ private:
|
|||
uint32 nodeCount = RandomValue(minNodes, maxNodes);
|
||||
nodeCount = std::max<uint32>(1, nodeCount);
|
||||
|
||||
std::vector<uint32> oreEntries = SplitByCategory(_nodeEntries, false);
|
||||
std::vector<uint32> herbEntries = SplitByCategory(_nodeEntries, true);
|
||||
std::vector<uint32> fallbackEntries = _nodeEntries;
|
||||
// Use zone-specific node pool when the current site has entries configured;
|
||||
// otherwise fall back to the global GoldRush.NodeEntries list exactly as before.
|
||||
std::vector<uint32> siteNormalPool = _currentSite.NodeEntries.empty() ? _nodeEntries : _currentSite.NodeEntries;
|
||||
std::vector<uint32> siteBonusPool = _currentSite.BonusEntries;
|
||||
|
||||
std::vector<uint32> oreEntries = SplitByCategory(siteNormalPool, false);
|
||||
std::vector<uint32> herbEntries = SplitByCategory(siteNormalPool, true);
|
||||
std::vector<uint32> fallbackEntries = siteNormalPool;
|
||||
bool useHerb = true;
|
||||
|
||||
if (_config.VerboseLogging)
|
||||
{
|
||||
LOG_INFO(GoldRushLogFilter, "Gold Rush spawning {} node(s) using {} ore entry(s), {} herb entry(s), and {} fallback entry(s).",
|
||||
nodeCount, oreEntries.size(), herbEntries.size(), fallbackEntries.size());
|
||||
LOG_INFO(GoldRushLogFilter, "Gold Rush spawning {} node(s) using {} ore entry(s), {} herb entry(s), {} fallback entry(s), and {} bonus entry(s).",
|
||||
nodeCount, oreEntries.size(), herbEntries.size(), fallbackEntries.size(), siteBonusPool.size());
|
||||
}
|
||||
|
||||
// Map::SummonGameObject feeds this straight into GameObject::SetRespawnTime(int32),
|
||||
|
|
@ -961,7 +978,11 @@ private:
|
|||
if (!pool || pool->empty())
|
||||
continue;
|
||||
|
||||
uint32 entry = Acore::Containers::SelectRandomContainerElement(*pool);
|
||||
uint32 entry = 0;
|
||||
if (!siteBonusPool.empty() && RandomValue(1u, 100u) <= _config.BonusChancePercent)
|
||||
entry = Acore::Containers::SelectRandomContainerElement(siteBonusPool);
|
||||
else
|
||||
entry = Acore::Containers::SelectRandomContainerElement(*pool);
|
||||
|
||||
// Try a handful of candidate spots; a node that cannot be placed on real ground
|
||||
// within line of sight of the anchor is skipped rather than left floating.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue