Compare commits
No commits in common. "28248f0fa72f690e3570772c9d92a87b3a9bc221" and "3a15d9af3667c24585f927ee51f86a251e3bbfb5" have entirely different histories.
28248f0fa7
...
3a15d9af36
3 changed files with 18 additions and 79 deletions
20
CHANGELOG.md
20
CHANGELOG.md
|
|
@ -4,27 +4,7 @@ All notable changes to `mod-gold-rush` will be documented in this file.
|
|||
|
||||
## [Unreleased — hallsworth fork]
|
||||
|
||||
### Added
|
||||
- Per-zone resource nodes. `GoldRush.ZonePool` entries can now carry their own
|
||||
node and bonus pools (`ZoneName|NodeEntries|BonusEntries`) instead of every
|
||||
hotspot drawing from one global list -- an event in Un'Goro spawns Un'Goro-
|
||||
appropriate herbs/ore, Sholazar spawns Northrend resources, etc. `gold_rush.conf.dist`
|
||||
ships with all ~57 valid WotLK 3.3.5a open-world zones pre-populated (see
|
||||
`docs/zone-specific-resources-spec.md` for how each zone's resource list was
|
||||
derived and verified). New `GoldRush.BonusChancePercent` (default 15) config
|
||||
controls how often a spawn pulls from a zone's rarer bonus pool instead of its
|
||||
normal one. A zone left without node/bonus data still falls back to the
|
||||
global `GoldRush.NodeEntries` pool exactly as before.
|
||||
|
||||
### Fixed
|
||||
- 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
|
||||
`GetSession()->SendPacket()` unconditionally, so a null session was an
|
||||
immediate null-pointer crash. Confirmed via a symbolized backtrace against a
|
||||
captured coredump on the live server (two crashes within ~24h). Replaced with
|
||||
`sWorldSessionMgr->SendServerMessage(SERVER_MSG_STRING, text)`, the session-free
|
||||
broadcast path the built-in `.announce` GM command uses.
|
||||
- Bot routing is no longer a silent no-op. `Engine::ChangeStrategy` dispatches on the
|
||||
first character of the strategy name and ignores anything without a `+`/`-`/`~`/`?`
|
||||
prefix, so the bare `"new rpg"` calls did nothing. Now sends `"+new rpg"`, and only
|
||||
|
|
|
|||
|
|
@ -109,14 +109,6 @@ GoldRush.BotPulseSeconds = 30
|
|||
|
||||
GoldRush.SpawnRadiusYards = 25.0
|
||||
|
||||
#
|
||||
# GoldRush.BonusChancePercent
|
||||
# Description: Per-node chance (0-100) to use a zone's BonusEntries pool
|
||||
# instead of the normal node pool. Set to 0 to disable bonus nodes.
|
||||
# Default: 15
|
||||
#
|
||||
GoldRush.BonusChancePercent = 15
|
||||
|
||||
#
|
||||
# GoldRush.NodeEntries
|
||||
# Description: Semicolon-separated list of temporary gameobject template entries.
|
||||
|
|
@ -127,13 +119,11 @@ GoldRush.NodeEntries = 191133;190176;190171;190172;189973
|
|||
|
||||
#
|
||||
# GoldRush.ZonePool
|
||||
# Description: Semicolon-separated list of eligible zones in the format
|
||||
# ZoneName|NodeEntries|BonusEntries. Empty segments are allowed;
|
||||
# a zone without entries falls back to GoldRush.NodeEntries.
|
||||
# This file uses the v2 2-segment format from docs/zone-specific-resources-spec.md.
|
||||
# Default: "Dun Morogh|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Elwynn Forest|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Tirisfal Glades|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Durotar|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Teldrassil|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Mulgore|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Azuremyst Isle|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Bloodmyst Isle|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Loch Modan|1620;3727;1621;3729;1731;2055;1732;2054|; Westfall|1620;3727;1621;3729;1731;2055;1732;2054|; Silverpine Forest|1620;3727;1621;3729;1731;2055;1732;2054|; Darkshore|1620;3727;1621;3729;1731;2055;1732;2054|; The Barrens|1620;3727;1621;3729;1731;2055;1732;2054|; Redridge Mountains|1622;3730;1623;1732;2054;1733;105569|; Duskwood|1622;3730;1623;1732;2054;1733;105569|; Ashenvale|1622;3730;1621;3729;1732;2054;1733;105569|; Stranglethorn Vale|1622;3730;1623;2045;1732;2054;1733;105569|; Wetlands|1624;1735|; Hillsbrad Foothills|1624;1735|; Stonetalon Mountains|1624;1735|; Thousand Needles|1624;1735|; The Hinterlands|2041;2042;1735;1734;150080|; Arathi Highlands|2041;2042;1735;1734;150080|; Desolace|2041;2042;1735;1734;150080|; Western Plaguelands|2046;2043;1734;150080;2040;150079|; Swamp of Sorrows|2046;2043;1734;150080;2040;150079|; Dustwallow Marsh|2046;2043;1734;150080;2040;150079|; Badlands|2866;2040;150079;176645|; Searing Gorge|2866;165658;2040;150079;176645|; Feralas|2866;2040;150079;176645|; Blasted Lands|142140;180165;142141;176642;2047;150081;181108|; Burning Steppes|142140;180165;142141;176642;165658;2047;150081;181108|; Tanaris|142142;176636;180164;142143;183046;2047;150081;181108|; Azshara|142140;180165;142141;176642;2047;150081;181108|; Eastern Plaguelands|142144;176587;176641;324;150082;176643;175404|176589; Felwood|142145;176637;324;150082;176643;175404|176589; Silithus|176588;142145;176637;324;150082;176643;175404|176589; Un'Goro Crater|191303;324;150082;176643;175404|176589; Winterspring|142140;180165;142142;176636;180164;324;150082;176643;175404|176589; Isle of Quel'Danas|181270;183044;181271;183045;181279;181555;181556|181557; Hellfire Peninsula|181270;183044;181271;183045;181555;181556|181569;181570; Zangarmarsh|181270;183044;181275;183043;181555;181556|181569;181570; Terokkar Forest|181270;183044;181271;183045;181555;181556|181569;181570; Nagrand|181279;181277;181556|181557; Blade's Edge Mountains|181277;181280;181556|181557; Netherstorm|181280;181281;181276;181557|181557; Shadowmoon Valley|181281;181276;181557|181557; Borean Tundra|189973;191019;189978|189979; Howling Fjord|189973;191019;189978|189979; Dragonblight|190169;190170;189978;189980|189979;189981; Grizzly Hills|190169;190170;189978;189980|189979;189981; Zul'Drak|190171;190172;189980;191133|189981; Storm Peaks|190171;190172;189980;191133|189981; Icecrown|190176;190171;190172;189973;190170;191019;191133|189979;189981; Crystalsong Forest|190176;190171;190172;189973;190170;191019;191133|189979;189981; Sholazar Basin|190171;190172;189973;190170;191019;189978;189979;189980;189981;191133|189979;189981; Hrothgar's Landing|190176;190171;190172;191133|189979;189981"
|
||||
# Description: Semicolon-separated list of eligible zones.
|
||||
# Use Zone|ApproximateSubArea for a better announcement.
|
||||
# Default: "Un'Goro Crater|Fire Plume Ridge; Winterspring|Frostfire Hot Springs; Eastern Plaguelands|Terrorweb Tunnel; Sholazar Basin|River's Heart"
|
||||
|
||||
GoldRush.ZonePool = "Dun Morogh|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Elwynn Forest|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Tirisfal Glades|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Durotar|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Teldrassil|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Mulgore|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Azuremyst Isle|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Bloodmyst Isle|1618;3724;1617;3725;1619;3726;1731;2055;3763|; Loch Modan|1620;3727;1621;3729;1731;2055;1732;2054|; Westfall|1620;3727;1621;3729;1731;2055;1732;2054|; Silverpine Forest|1620;3727;1621;3729;1731;2055;1732;2054|; Darkshore|1620;3727;1621;3729;1731;2055;1732;2054|; The Barrens|1620;3727;1621;3729;1731;2055;1732;2054|; Redridge Mountains|1622;3730;1623;1732;2054;1733;105569|; Duskwood|1622;3730;1623;1732;2054;1733;105569|; Ashenvale|1622;3730;1621;3729;1732;2054;1733;105569|; Stranglethorn Vale|1622;3730;1623;2045;1732;2054;1733;105569|; Wetlands|1624;1735|; Hillsbrad Foothills|1624;1735|; Stonetalon Mountains|1624;1735|; Thousand Needles|1624;1735|; The Hinterlands|2041;2042;1735;1734;150080|; Arathi Highlands|2041;2042;1735;1734;150080|; Desolace|2041;2042;1735;1734;150080|; Western Plaguelands|2046;2043;1734;150080;2040;150079|; Swamp of Sorrows|2046;2043;1734;150080;2040;150079|; Dustwallow Marsh|2046;2043;1734;150080;2040;150079|; Badlands|2866;2040;150079;176645|; Searing Gorge|2866;165658;2040;150079;176645|; Feralas|2866;2040;150079;176645|; Blasted Lands|142140;180165;142141;176642;2047;150081;181108|; Burning Steppes|142140;180165;142141;176642;165658;2047;150081;181108|; Tanaris|142142;176636;180164;142143;183046;2047;150081;181108|; Azshara|142140;180165;142141;176642;2047;150081;181108|; Eastern Plaguelands|142144;176587;176641;324;150082;176643;175404|176589; Felwood|142145;176637;324;150082;176643;175404|176589; Silithus|176588;142145;176637;324;150082;176643;175404|176589; Un'Goro Crater|191303;324;150082;176643;175404|176589; Winterspring|142140;180165;142142;176636;180164;324;150082;176643;175404|176589; Isle of Quel'Danas|181270;183044;181271;183045;181279;181555;181556|181557; Hellfire Peninsula|181270;183044;181271;183045;181555;181556|181569;181570; Zangarmarsh|181270;183044;181275;183043;181555;181556|181569;181570; Terokkar Forest|181270;183044;181271;183045;181555;181556|181569;181570; Nagrand|181279;181277;181556|181557; Blade's Edge Mountains|181277;181280;181556|181557; Netherstorm|181280;181281;181276;181557|181557; Shadowmoon Valley|181281;181276;181557|181557; Borean Tundra|189973;191019;189978|189979; Howling Fjord|189973;191019;189978|189979; Dragonblight|190169;190170;189978;189980|189979;189981; Grizzly Hills|190169;190170;189978;189980|189979;189981; Zul'Drak|190171;190172;189980;191133|189981; Storm Peaks|190171;190172;189980;191133|189981; Icecrown|190176;190171;190172;189973;190170;191019;191133|189979;189981; Crystalsong Forest|190176;190171;190172;189973;190170;191019;191133|189979;189981; Sholazar Basin|190171;190172;189973;190170;191019;189978;189979;189980;189981;191133|189979;189981; Hrothgar's Landing|190176;190171;190172;191133|189979;189981"
|
||||
GoldRush.ZonePool = "Un'Goro Crater|Fire Plume Ridge; Winterspring|Frostfire Hot Springs; Eastern Plaguelands|Terrorweb Tunnel; Sholazar Basin|River's Heart"
|
||||
|
||||
#
|
||||
# GoldRush.Blacklist
|
||||
|
|
|
|||
|
|
@ -45,8 +45,6 @@ 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;
|
||||
|
|
@ -87,7 +85,6 @@ 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;
|
||||
|
|
@ -184,19 +181,8 @@ 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");
|
||||
// No hardcoded fallback zone list here on purpose: the real default lives in
|
||||
// gold_rush.conf.dist (the full 57-zone v2 pool from
|
||||
// docs/zone-specific-resources-spec.md). That file uses the 2-segment
|
||||
// ZoneName|NodeEntries|BonusEntries format; a hardcoded fallback string here
|
||||
// previously still used the old ZoneName|AreaLabel format, which the current
|
||||
// parser reads as NodeEntries and silently fails to parse as node IDs (safe --
|
||||
// BuildNodeEntries uses a non-throwing parse -- but pointless). If ZonePool is
|
||||
// ever missing from the loaded config entirely, an empty pool here just means
|
||||
// no configured hotspots, matching the "eligible live-player zones" fallback
|
||||
// path already used elsewhere in this module rather than a stale example.
|
||||
_config.ZonePool = sConfigMgr->GetOption<std::string>("GoldRush.ZonePool", "");
|
||||
_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");
|
||||
_config.StartMessage = sConfigMgr->GetOption<std::string>("GoldRush.StartMessage", "A seismic anomaly has exposed a massive vein of rich minerals in {}!");
|
||||
_config.EndMessage = sConfigMgr->GetOption<std::string>("GoldRush.EndMessage", "The Gold Rush in {} has been exhausted.");
|
||||
|
|
@ -492,17 +478,7 @@ 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))
|
||||
{
|
||||
|
|
@ -515,14 +491,13 @@ private:
|
|||
GoldRushSite site;
|
||||
site.ZoneLabel = parts.empty() ? token : Trim(std::string(parts[0]));
|
||||
if (parts.size() > 1)
|
||||
site.NodeEntries = BuildNodeEntries(Trim(std::string(parts[1])));
|
||||
if (parts.size() > 2)
|
||||
site.BonusEntries = BuildNodeEntries(Trim(std::string(parts[2])));
|
||||
site.AreaLabel = Trim(std::string(parts[1]));
|
||||
|
||||
if (site.ZoneLabel.empty())
|
||||
continue;
|
||||
|
||||
AreaTableEntry const* zoneArea = ResolveAreaEntry(site.ZoneLabel);
|
||||
AreaTableEntry const* areaEntry = site.AreaLabel.empty() ? zoneArea : ResolveAreaEntry(site.AreaLabel);
|
||||
if (!zoneArea)
|
||||
continue;
|
||||
|
||||
|
|
@ -530,8 +505,11 @@ private:
|
|||
if (!rootZone)
|
||||
continue;
|
||||
|
||||
if (!site.AreaLabel.empty() && !areaEntry)
|
||||
continue;
|
||||
|
||||
site.ZoneId = rootZone->ID;
|
||||
site.AreaId = 0; // no area label in the 2-segment config format
|
||||
site.AreaId = !site.AreaLabel.empty() && areaEntry ? areaEntry->ID : 0;
|
||||
site.MapId = rootZone->mapid;
|
||||
|
||||
if (!IsValidZoneArea(rootZone) || IsBlockedSite(site))
|
||||
|
|
@ -949,20 +927,15 @@ private:
|
|||
uint32 nodeCount = RandomValue(minNodes, maxNodes);
|
||||
nodeCount = std::max<uint32>(1, nodeCount);
|
||||
|
||||
// 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;
|
||||
std::vector<uint32> oreEntries = SplitByCategory(_nodeEntries, false);
|
||||
std::vector<uint32> herbEntries = SplitByCategory(_nodeEntries, true);
|
||||
std::vector<uint32> fallbackEntries = _nodeEntries;
|
||||
bool useHerb = true;
|
||||
|
||||
if (_config.VerboseLogging)
|
||||
{
|
||||
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());
|
||||
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());
|
||||
}
|
||||
|
||||
// Map::SummonGameObject feeds this straight into GameObject::SetRespawnTime(int32),
|
||||
|
|
@ -988,11 +961,7 @@ private:
|
|||
if (!pool || pool->empty())
|
||||
continue;
|
||||
|
||||
uint32 entry = 0;
|
||||
if (!siteBonusPool.empty() && RandomValue(1u, 100u) <= _config.BonusChancePercent)
|
||||
entry = Acore::Containers::SelectRandomContainerElement(siteBonusPool);
|
||||
else
|
||||
entry = Acore::Containers::SelectRandomContainerElement(*pool);
|
||||
uint32 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