From 35298a3714e1f2e434d83ecf6030d79784e0c143 Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:06:47 -0600 Subject: [PATCH] [core] Add some startup sanity checks for NMs --- src/map/utils/zoneutils.cpp | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/map/utils/zoneutils.cpp b/src/map/utils/zoneutils.cpp index 44e31fa84ff..f2fa2b90bce 100644 --- a/src/map/utils/zoneutils.cpp +++ b/src/map/utils/zoneutils.cpp @@ -680,6 +680,48 @@ auto LoadMOBList(Scheduler& scheduler, const std::vector& zoneIds) -> Ta PZone->ForEachMob( [&PZone](CMobEntity* PMob) { + // Check if mob is spawnable via lottery + if (PMob->m_SpawnType == SPAWNTYPE_LOTTERY) + { + auto zone = PMob->loc.zone->getName(); + auto name = PMob->getName(); + + auto phList = lua["xi"]["zones"][zone]["mobs"][name]["phList"]; + if (phList.valid()) + { + auto table = phList.get(); + bool hasEntries = false; + bool mobInList = false; + + for (auto& [phObj, nmObj] : table) + { + [[maybe_unused]] uint32 phID = phObj.as(); // TODO: check if the PH is spawnable? + uint32 nmID = nmObj.as(); + + if (nmID == PMob->id) + { + mobInList = true; + } + + hasEntries = true; + } + + if (!hasEntries) + { + ShowErrorFmt("Lottery spawn NM '{}' in zone '{}' has a blank phList", name, zone); + } + + if (!mobInList) + { + ShowErrorFmt("Lottery spawn NM '{}' ID ({}) in zone '{}' has a phList that doesn't contain itself as a spawnable NM", name, PMob->id, zone); + } + } + else + { + ShowErrorFmt("Lottery spawn NM '{}' in zone '{}' is missing a phList and may not be spawnable", name, zone); + } + } + // Skip mobs already registered via setRespawnTime in onMobInitialize - let SpawnHandler handle them if (PZone->spawnHandler().isRegistered(PMob)) {