From 5515304a8a910a7dca673ed132edb1f083241c05 Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Sat, 18 Nov 2023 17:05:36 -0800 Subject: [PATCH 1/5] feat: weighted random monsters spawn --- config.lua.dist | 2 + src/config/config_definitions.hpp | 1 + src/config/configmanager.cpp | 1 + .../monsters/spawns/spawn_monster.cpp | 172 ++++++++++++++---- .../monsters/spawns/spawn_monster.hpp | 9 +- 5 files changed, 146 insertions(+), 39 deletions(-) diff --git a/config.lua.dist b/config.lua.dist index 57257748195..e7dabfcccaa 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -207,6 +207,7 @@ onlyPremiumAccount = false -- NOTE: buyBlessCommandFee will add fee when player buy bless by command (!bless), active changing value between 1 and 100 (fee percent. ex: 3 = 3%, 30 = 30%) -- NOTE: teleportPlayerToVocationRoom will enable oressa to teleport player to his/her room vocation -- NOTE: toggleReceiveReward = true, will enable players to choose one of reward exercise weapon by command !reward +-- NOTE: randomMonsterSpawn = true, will enable monsters from the same spawn to be randomized between them, thus making a variable hunt weatherRain = false thunderEffect = false allConsoleLog = false @@ -222,6 +223,7 @@ buyAolCommandFee = 0 buyBlessCommandFee = 0 teleportPlayerToVocationRoom = true toggleReceiveReward = false +randomMonsterSpawn = false -- Teleport summon -- Set to true will never remove the summon diff --git a/src/config/config_definitions.hpp b/src/config/config_definitions.hpp index 700f5bfdb0c..582531a3afd 100644 --- a/src/config/config_definitions.hpp +++ b/src/config/config_definitions.hpp @@ -173,6 +173,7 @@ enum ConfigKey_t : uint16_t { PVP_RATE_DAMAGE_REDUCTION_PER_LEVEL, PVP_RATE_DAMAGE_TAKEN_PER_LEVEL, PZ_LOCKED, + RANDOM_MONSTER_SPAWN, RATE_ATTACK_SPEED, RATE_BOSS_ATTACK, RATE_BOSS_DEFENSE, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index d90c89f0e26..fb2f11d3dea 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -44,6 +44,7 @@ bool ConfigManager::load() { loadBoolConfig(L, OPTIMIZE_DATABASE, "startupDatabaseOptimization", true); loadBoolConfig(L, TOGGLE_MAP_CUSTOM, "toggleMapCustom", true); loadBoolConfig(L, TOGGLE_MAINTAIN_MODE, "toggleMaintainMode", false); + loadBoolConfig(L, RANDOM_MONSTER_SPAWN, "randomMonsterSpawn", false); loadStringConfig(L, MAINTAIN_MODE_MESSAGE, "maintainModeMessage", ""); loadStringConfig(L, IP, "ip", "127.0.0.1"); diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index 9549ae5930e..0083130fb74 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -95,13 +95,25 @@ bool SpawnsMonster::loadFromXML(const std::string &filemonstername) { boostedrate = 2; } + pugi::xml_attribute weightAttribute = childMonsterNode.attribute("weight"); + uint32_t weight = 1; + if (weightAttribute) { + weight = pugi::cast(weightAttribute.value()); + } + + pugi::xml_attribute weightAttribute = childMonsterNode.attribute("weight"); + uint32_t weight = 1; + if (weightAttribute) { + weight = pugi::cast(weightAttribute.value()); + } + uint32_t interval = pugi::cast(childMonsterNode.attribute("spawntime").value()) * 1000 * 100 / std::max((uint32_t)1, (g_configManager().getNumber(RATE_SPAWN, __FUNCTION__) * boostedrate * eventschedule)); if (interval >= MONSTER_MINSPAWN_INTERVAL && interval <= MONSTER_MAXSPAWN_INTERVAL) { - spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, static_cast(interval)); + spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, static_cast(interval), weight); } else { if (interval <= MONSTER_MINSPAWN_INTERVAL) { g_logger().warn("[SpawnsMonster::loadFromXml] - {} {} spawntime cannot be less than {} seconds, set to {} by default.", nameAttribute.as_string(), pos.toString(), MONSTER_MINSPAWN_INTERVAL / 1000, MONSTER_MINSPAWN_INTERVAL / 1000); - spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, MONSTER_MINSPAWN_INTERVAL); + spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, MONSTER_MINSPAWN_INTERVAL, weight); } else { g_logger().warn("[SpawnsMonster::loadFromXml] - {} {} spawntime can not be more than {} seconds", nameAttribute.as_string(), pos.toString(), MONSTER_MAXSPAWN_INTERVAL / 1000); } @@ -195,11 +207,31 @@ bool SpawnMonster::spawnMonster(uint32_t spawnMonsterId, const std::shared_ptrisBoss()) { + continue; + } + for (auto otherIt = std::next(it); otherIt != spawnMonsterMap.end(); ++otherIt) { + auto &[id, otherSb] = *otherIt; + if (id == spawnMonsterId) { + continue; + } + if (otherSb.hasBoss()) { + continue; + } + if (otherSb.monsterTypes.contains(monsterType)) { + weight += otherSb.monsterTypes[monsterType]; + } + otherSb.monsterTypes.emplace(monsterType, weight); + sb.monsterTypes.emplace(monsterType, weight); + } + } + } } + checkSpawnMonster(); } void SpawnMonster::checkSpawnMonster() { @@ -216,26 +248,30 @@ void SpawnMonster::checkSpawnMonster() { } spawnBlock_t &sb = it.second; - if (!sb.monsterType->canSpawn(sb.pos)) { + const auto &mType = sb.getMonsterType(); + if (!mType) { + continue; + } + if (!mType->canSpawn(sb.pos)) { sb.lastSpawn = OTSYS_TIME(); continue; } + if (mType->info.isBlockable && findPlayer(sb.pos)) { + sb.lastSpawn = OTSYS_TIME(); + continue; + } + if (OTSYS_TIME() < sb.lastSpawn + sb.interval) { + continue; + } - if (OTSYS_TIME() >= sb.lastSpawn + sb.interval) { - if (sb.monsterType->info.isBlockable && findPlayer(sb.pos)) { - sb.lastSpawn = OTSYS_TIME(); - continue; - } - - if (sb.monsterType->info.isBlockable) { - spawnMonster(spawnMonsterId, sb.monsterType, sb.pos, sb.direction); - } else { - scheduleSpawn(spawnMonsterId, sb, 3 * NONBLOCKABLE_SPAWN_MONSTER_INTERVAL); - } + if (mType->info.isBlockable) { + spawnMonster(spawnMonsterId, mType, sb.pos, sb.direction, true); + } else { + scheduleSpawn(spawnMonsterId, sb, mType, 3 * NONBLOCKABLE_SPAWN_MONSTER_INTERVAL); + } - if (++spawnMonsterCount >= static_cast(g_configManager().getNumber(RATE_SPAWN, __FUNCTION__))) { - break; - } + if (++spawnMonsterCount >= static_cast(g_configManager().getNumber(RATE_SPAWN, __FUNCTION__))) { + break; } } @@ -244,12 +280,12 @@ void SpawnMonster::checkSpawnMonster() { } } -void SpawnMonster::scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, uint16_t interval) { +void SpawnMonster::scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, const std::shared_ptr mType, uint16_t interval) { if (interval <= 0) { - spawnMonster(spawnMonsterId, sb.monsterType, sb.pos, sb.direction); + spawnMonster(spawnMonsterId, mType, sb.pos, sb.direction); } else { g_game().addMagicEffect(sb.pos, CONST_ME_TELEPORT); - g_dispatcher().scheduleEvent(1400, std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, interval - NONBLOCKABLE_SPAWN_MONSTER_INTERVAL), "SpawnMonster::scheduleSpawn"); + g_dispatcher().scheduleEvent(1400, std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, mType, interval - NONBLOCKABLE_SPAWN_MONSTER_INTERVAL), "SpawnMonster::scheduleSpawn"); } } @@ -267,7 +303,7 @@ void SpawnMonster::cleanup() { } } -bool SpawnMonster::addMonster(const std::string &name, const Position &pos, Direction dir, uint32_t scheduleInterval) { +bool SpawnMonster::addMonster(const std::string &name, const Position &pos, Direction dir, uint32_t scheduleInterval, uint32_t weight /*= 1*/) { std::string variant = ""; for (const auto &zone : Zone::getZones(pos)) { if (!zone->getMonsterVariant().empty()) { @@ -283,15 +319,37 @@ bool SpawnMonster::addMonster(const std::string &name, const Position &pos, Dire this->interval = std::min(this->interval, scheduleInterval); - spawnBlock_t sb; - sb.monsterType = monsterType; - sb.pos = pos; - sb.direction = dir; - sb.interval = scheduleInterval; - sb.lastSpawn = 0; - + spawnBlock_t* sb = nullptr; uint32_t spawnMonsterId = spawnMonsterMap.size() + 1; - spawnMonsterMap[spawnMonsterId] = sb; + for (auto &[id, maybeSb] : spawnMonsterMap) { + if (maybeSb.pos == pos) { + sb = &maybeSb; + spawnMonsterId = id; + break; + } + } + if (sb) { + if (sb->monsterTypes.contains(monsterType)) { + g_logger().error("[SpawnMonster] Monster {} already exists in spawn block at {}", name, pos.toString()); + return false; + } + if (monsterType->isBoss() && sb->monsterTypes.size() > 0) { + g_logger().error("[SpawnMonster] Boss monster {} has been added to spawn block with other monsters. This is not allowed.", name); + return false; + } + if (sb->hasBoss()) { + g_logger().error("[SpawnMonster] Monster {} has been added to spawn block with a boss. This is not allowed.", name); + return false; + } + } + if (!sb) { + sb = &spawnMonsterMap.emplace(spawnMonsterId, spawnBlock_t()).first->second; + } + sb->monsterTypes.emplace(monsterType, weight); + sb->pos = pos; + sb->direction = dir; + sb->interval = scheduleInterval; + sb->lastSpawn = 0; return true; } @@ -306,9 +364,13 @@ void SpawnMonster::removeMonster(std::shared_ptr monster) { void SpawnMonster::setMonsterVariant(const std::string &variant) { for (auto &it : spawnMonsterMap) { - auto variantName = variant + it.second.monsterType->typeName; - auto variantType = g_monsters().getMonsterType(variantName, false); - it.second.monsterType = variantType ? variantType : it.second.monsterType; + std::unordered_map, uint32_t> monsterTypes; + for (const auto &[monsterType, weight] : it.second.monsterTypes) { + auto variantName = variant + monsterType->typeName; + auto variantType = g_monsters().getMonsterType(variantName, false); + monsterTypes.emplace(variantType, weight); + } + it.second.monsterTypes = monsterTypes; } } @@ -318,3 +380,41 @@ void SpawnMonster::stopEvent() { checkSpawnMonsterEvent = 0; } } + +std::shared_ptr spawnBlock_t::getMonsterType() const { + if (monsterTypes.empty()) { + return nullptr; + } + uint32_t totalWeight = 0; + for (const auto &[mType, weight] : monsterTypes) { + if (mType->isBoss()) { + if (monsterTypes.size() > 1) { + g_logger().warn("[SpawnMonster] Boss monster {} has been added to spawn block with other monsters. This is not allowed.", mType->name); + } + return mType; + } + totalWeight += weight; + } + uint32_t randomWeight = uniform_random(0, totalWeight - 1); + // order monsters by weight DESC + std::vector, uint32_t>> orderedMonsterTypes(monsterTypes.begin(), monsterTypes.end()); + std::sort(orderedMonsterTypes.begin(), orderedMonsterTypes.end(), [](const auto &a, const auto &b) { + return a.second > b.second; + }); + for (const auto &[mType, weight] : orderedMonsterTypes) { + if (randomWeight < weight) { + return mType; + } + randomWeight -= weight; + } + return nullptr; +} + +bool spawnBlock_t::hasBoss() const { + for (const auto &[monsterType, weight] : monsterTypes) { + if (monsterType->isBoss()) { + return true; + } + } + return false; +} diff --git a/src/creatures/monsters/spawns/spawn_monster.hpp b/src/creatures/monsters/spawns/spawn_monster.hpp index 749856525d9..bf4a47b1f52 100644 --- a/src/creatures/monsters/spawns/spawn_monster.hpp +++ b/src/creatures/monsters/spawns/spawn_monster.hpp @@ -17,10 +17,13 @@ class MonsterType; struct spawnBlock_t { Position pos; - std::shared_ptr monsterType; + std::unordered_map, uint32_t> monsterTypes; int64_t lastSpawn; uint32_t interval; Direction direction; + + std::shared_ptr getMonsterType() const; + bool hasBoss() const; }; class SpawnMonster { @@ -33,7 +36,7 @@ class SpawnMonster { SpawnMonster(const SpawnMonster &) = delete; SpawnMonster &operator=(const SpawnMonster &) = delete; - bool addMonster(const std::string &name, const Position &pos, Direction dir, uint32_t interval); + bool addMonster(const std::string &name, const Position &pos, Direction dir, uint32_t interval, uint32_t weight = 1); void removeMonster(std::shared_ptr monster); uint32_t getInterval() const { @@ -71,7 +74,7 @@ class SpawnMonster { static bool findPlayer(const Position &pos); bool spawnMonster(uint32_t spawnMonsterId, const std::shared_ptr monsterType, const Position &pos, Direction dir, bool startup = false); void checkSpawnMonster(); - void scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, uint16_t interval); + void scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, const std::shared_ptr monsterType, uint16_t interval); }; class SpawnsMonster { From 3a90f878ff01cfda358c95760ea09d524ba4c051 Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Sun, 19 Nov 2023 11:59:25 -0800 Subject: [PATCH 2/5] fix: crash in spawn_monster --- .../monsters/spawns/spawn_monster.cpp | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index 0083130fb74..ed7e1c6cbab 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -231,14 +231,22 @@ void SpawnMonster::startup() { } } } - checkSpawnMonster(); + for (auto &[spawnMonsterId, sb] : spawnMonsterMap) { + const auto &mType = sb.getMonsterType(); + if (!mType) { + continue; + } + spawnMonster(spawnMonsterId, mType, sb.pos, sb.direction, true); + } } void SpawnMonster::checkSpawnMonster() { - checkSpawnMonsterEvent = 0; + if (checkSpawnMonsterEvent == 0) { + return; + } + checkSpawnMonsterEvent = 0; cleanup(); - uint32_t spawnMonsterCount = 0; for (auto &it : spawnMonsterMap) { @@ -292,10 +300,8 @@ void SpawnMonster::scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, cons void SpawnMonster::cleanup() { auto it = spawnedMonsterMap.begin(); while (it != spawnedMonsterMap.end()) { - uint32_t spawnMonsterId = it->first; - std::shared_ptr monster = it->second; - if (!monster || monster->isRemoved()) { - spawnMonsterMap[spawnMonsterId].lastSpawn = OTSYS_TIME(); + auto monster = it->second; + if (monster == nullptr || monster->isRemoved()) { it = spawnedMonsterMap.erase(it); } else { ++it; @@ -387,6 +393,9 @@ std::shared_ptr spawnBlock_t::getMonsterType() const { } uint32_t totalWeight = 0; for (const auto &[mType, weight] : monsterTypes) { + if (!mType) { + continue; + } if (mType->isBoss()) { if (monsterTypes.size() > 1) { g_logger().warn("[SpawnMonster] Boss monster {} has been added to spawn block with other monsters. This is not allowed.", mType->name); From 0875b88e6fe7e632303261b2041910c64415a952 Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Mon, 20 Nov 2023 12:47:59 -0800 Subject: [PATCH 3/5] fix: map safety on spawn_monster --- .../monsters/spawns/spawn_monster.cpp | 42 +++++++++++-------- .../monsters/spawns/spawn_monster.hpp | 8 ++-- src/game/scheduling/task.hpp | 1 + 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index ed7e1c6cbab..3032ac73920 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -162,8 +162,7 @@ void SpawnMonster::startSpawnMonsterCheck() { } SpawnMonster::~SpawnMonster() { - for (const auto &it : spawnedMonsterMap) { - std::shared_ptr monster = it.second; + for (const auto &[_, monster] : spawnedMonsterMap) { monster->setSpawnMonster(nullptr); } } @@ -183,6 +182,9 @@ bool SpawnMonster::isInSpawnMonsterZone(const Position &pos) { } bool SpawnMonster::spawnMonster(uint32_t spawnMonsterId, const std::shared_ptr monsterType, const Position &pos, Direction dir, bool startup /*= false*/) { + if (spawnedMonsterMap.contains(spawnMonsterId)) { + return false; + } auto monster = std::make_shared(monsterType); if (startup) { // No need to send out events to the surrounding since there is no one out there to listen! @@ -199,14 +201,14 @@ bool SpawnMonster::spawnMonster(uint32_t spawnMonsterId, const std::shared_ptrsetSpawnMonster(this); monster->setMasterPos(pos); - spawnedMonsterMap.insert(spawned_pair(spawnMonsterId, monster)); + spawnedMonsterMap[spawnMonsterId] = monster; spawnMonsterMap[spawnMonsterId].lastSpawn = OTSYS_TIME(); g_events().eventMonsterOnSpawn(monster, pos); g_callbacks().executeCallback(EventCallback_t::monsterOnSpawn, &EventCallback::monsterOnSpawn, monster, pos); return true; } -void SpawnMonster::startup() { +void SpawnMonster::startup(bool delayed) { if (g_configManager().getBoolean(RANDOM_MONSTER_SPAWN)) { for (auto it = spawnMonsterMap.begin(); it != spawnMonsterMap.end(); ++it) { auto &[spawnMonsterId, sb] = *it; @@ -236,7 +238,11 @@ void SpawnMonster::startup() { if (!mType) { continue; } - spawnMonster(spawnMonsterId, mType, sb.pos, sb.direction, true); + if (delayed) { + g_dispatcher().addEvent(std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, mType, 0), "SpawnMonster::startup"); + } else { + scheduleSpawn(spawnMonsterId, sb, mType, 0); + } } } @@ -249,13 +255,11 @@ void SpawnMonster::checkSpawnMonster() { cleanup(); uint32_t spawnMonsterCount = 0; - for (auto &it : spawnMonsterMap) { - uint32_t spawnMonsterId = it.first; - if (spawnedMonsterMap.find(spawnMonsterId) != spawnedMonsterMap.end()) { + for (auto &[spawnMonsterId, sb] : spawnMonsterMap) { + if (spawnedMonsterMap.contains(spawnMonsterId)) { continue; } - spawnBlock_t &sb = it.second; const auto &mType = sb.getMonsterType(); if (!mType) { continue; @@ -298,15 +302,15 @@ void SpawnMonster::scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, cons } void SpawnMonster::cleanup() { - auto it = spawnedMonsterMap.begin(); - while (it != spawnedMonsterMap.end()) { - auto monster = it->second; + std::vector removeList; + for (const auto &[spawnMonsterId, monster] : spawnedMonsterMap) { if (monster == nullptr || monster->isRemoved()) { - it = spawnedMonsterMap.erase(it); - } else { - ++it; + removeList.push_back(spawnMonsterId); } } + for (const auto &spawnMonsterId : removeList) { + spawnedMonsterMap.erase(spawnMonsterId); + } } bool SpawnMonster::addMonster(const std::string &name, const Position &pos, Direction dir, uint32_t scheduleInterval, uint32_t weight /*= 1*/) { @@ -360,12 +364,14 @@ bool SpawnMonster::addMonster(const std::string &name, const Position &pos, Dire } void SpawnMonster::removeMonster(std::shared_ptr monster) { - for (auto it = spawnedMonsterMap.begin(), end = spawnedMonsterMap.end(); it != end; ++it) { - if (it->second == monster) { - spawnedMonsterMap.erase(it); + uint32_t spawnMonsterId = 0; + for (const auto &[id, m] : spawnedMonsterMap) { + if (m == monster) { + spawnMonsterId = id; break; } } + spawnedMonsterMap.erase(spawnMonsterId); } void SpawnMonster::setMonsterVariant(const std::string &variant) { diff --git a/src/creatures/monsters/spawns/spawn_monster.hpp b/src/creatures/monsters/spawns/spawn_monster.hpp index bf4a47b1f52..36dd2e1ca09 100644 --- a/src/creatures/monsters/spawns/spawn_monster.hpp +++ b/src/creatures/monsters/spawns/spawn_monster.hpp @@ -42,7 +42,7 @@ class SpawnMonster { uint32_t getInterval() const { return interval; } - void startup(); + void startup(bool delayed = false); void startSpawnMonsterCheck(); void stopEvent(); @@ -58,12 +58,10 @@ class SpawnMonster { private: // map of the spawned creatures - using SpawnedMap = std::multimap>; - using spawned_pair = SpawnedMap::value_type; - SpawnedMap spawnedMonsterMap; + phmap::parallel_flat_hash_map_m> spawnedMonsterMap; // map of creatures in the spawn - std::map spawnMonsterMap; + phmap::parallel_flat_hash_map_m spawnMonsterMap; Position centerPos; int32_t radius; diff --git a/src/game/scheduling/task.hpp b/src/game/scheduling/task.hpp index 480072f427b..e02a1ce4056 100644 --- a/src/game/scheduling/task.hpp +++ b/src/game/scheduling/task.hpp @@ -96,6 +96,7 @@ class Task { "Raids::checkRaids", "SpawnMonster::checkSpawnMonster", "SpawnMonster::scheduleSpawn", + "SpawnMonster::startup", "SpawnNpc::checkSpawnNpc", "Webhook::run", "Protocol::sendRecvMessageCallback", From d79a069950b882a97162ad4861ac36069a5ec1c7 Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Thu, 23 Nov 2023 10:21:11 -0800 Subject: [PATCH 4/5] fix: spawn monster cadence --- .../monsters/spawns/spawn_monster.cpp | 69 ++++++++----------- .../monsters/spawns/spawn_monster.hpp | 2 +- 2 files changed, 31 insertions(+), 40 deletions(-) diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index 3032ac73920..c4266633f21 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -101,23 +101,7 @@ bool SpawnsMonster::loadFromXML(const std::string &filemonstername) { weight = pugi::cast(weightAttribute.value()); } - pugi::xml_attribute weightAttribute = childMonsterNode.attribute("weight"); - uint32_t weight = 1; - if (weightAttribute) { - weight = pugi::cast(weightAttribute.value()); - } - - uint32_t interval = pugi::cast(childMonsterNode.attribute("spawntime").value()) * 1000 * 100 / std::max((uint32_t)1, (g_configManager().getNumber(RATE_SPAWN, __FUNCTION__) * boostedrate * eventschedule)); - if (interval >= MONSTER_MINSPAWN_INTERVAL && interval <= MONSTER_MAXSPAWN_INTERVAL) { - spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, static_cast(interval), weight); - } else { - if (interval <= MONSTER_MINSPAWN_INTERVAL) { - g_logger().warn("[SpawnsMonster::loadFromXml] - {} {} spawntime cannot be less than {} seconds, set to {} by default.", nameAttribute.as_string(), pos.toString(), MONSTER_MINSPAWN_INTERVAL / 1000, MONSTER_MINSPAWN_INTERVAL / 1000); - spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, MONSTER_MINSPAWN_INTERVAL, weight); - } else { - g_logger().warn("[SpawnsMonster::loadFromXml] - {} {} spawntime can not be more than {} seconds", nameAttribute.as_string(), pos.toString(), MONSTER_MAXSPAWN_INTERVAL / 1000); - } - } + spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, pugi::cast(childMonsterNode.attribute("spawntime").value()) * 1000, weight); } } } @@ -181,30 +165,30 @@ bool SpawnMonster::isInSpawnMonsterZone(const Position &pos) { return SpawnsMonster::isInZone(centerPos, radius, pos); } -bool SpawnMonster::spawnMonster(uint32_t spawnMonsterId, const std::shared_ptr monsterType, const Position &pos, Direction dir, bool startup /*= false*/) { +bool SpawnMonster::spawnMonster(uint32_t spawnMonsterId, spawnBlock_t &sb, const std::shared_ptr monsterType, bool startup /*= false*/) { if (spawnedMonsterMap.contains(spawnMonsterId)) { return false; } auto monster = std::make_shared(monsterType); if (startup) { // No need to send out events to the surrounding since there is no one out there to listen! - if (!g_game().internalPlaceCreature(monster, pos, true)) { + if (!g_game().internalPlaceCreature(monster, sb.pos, true)) { return false; } } else { - if (!g_game().placeCreature(monster, pos, false, true)) { + if (!g_game().placeCreature(monster, sb.pos, false, true)) { return false; } } - monster->setDirection(dir); + monster->setDirection(sb.direction); monster->setSpawnMonster(this); - monster->setMasterPos(pos); + monster->setMasterPos(sb.pos); spawnedMonsterMap[spawnMonsterId] = monster; - spawnMonsterMap[spawnMonsterId].lastSpawn = OTSYS_TIME(); - g_events().eventMonsterOnSpawn(monster, pos); - g_callbacks().executeCallback(EventCallback_t::monsterOnSpawn, &EventCallback::monsterOnSpawn, monster, pos); + sb.lastSpawn = OTSYS_TIME(); + g_events().eventMonsterOnSpawn(monster, sb.pos); + g_callbacks().executeCallback(EventCallback_t::monsterOnSpawn, &EventCallback::monsterOnSpawn, monster, sb.pos); return true; } @@ -253,7 +237,6 @@ void SpawnMonster::checkSpawnMonster() { checkSpawnMonsterEvent = 0; cleanup(); - uint32_t spawnMonsterCount = 0; for (auto &[spawnMonsterId, sb] : spawnMonsterMap) { if (spawnedMonsterMap.contains(spawnMonsterId)) { @@ -264,11 +247,7 @@ void SpawnMonster::checkSpawnMonster() { if (!mType) { continue; } - if (!mType->canSpawn(sb.pos)) { - sb.lastSpawn = OTSYS_TIME(); - continue; - } - if (mType->info.isBlockable && findPlayer(sb.pos)) { + if (!mType->canSpawn(sb.pos) || (mType->info.isBlockable && findPlayer(sb.pos))) { sb.lastSpawn = OTSYS_TIME(); continue; } @@ -277,14 +256,10 @@ void SpawnMonster::checkSpawnMonster() { } if (mType->info.isBlockable) { - spawnMonster(spawnMonsterId, mType, sb.pos, sb.direction, true); + spawnMonster(spawnMonsterId, sb, mType, true); } else { scheduleSpawn(spawnMonsterId, sb, mType, 3 * NONBLOCKABLE_SPAWN_MONSTER_INTERVAL); } - - if (++spawnMonsterCount >= static_cast(g_configManager().getNumber(RATE_SPAWN, __FUNCTION__))) { - break; - } } if (spawnedMonsterMap.size() < spawnMonsterMap.size()) { @@ -294,10 +269,10 @@ void SpawnMonster::checkSpawnMonster() { void SpawnMonster::scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, const std::shared_ptr mType, uint16_t interval) { if (interval <= 0) { - spawnMonster(spawnMonsterId, mType, sb.pos, sb.direction); + spawnMonster(spawnMonsterId, sb, mType, startup); } else { g_game().addMagicEffect(sb.pos, CONST_ME_TELEPORT); - g_dispatcher().scheduleEvent(1400, std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, mType, interval - NONBLOCKABLE_SPAWN_MONSTER_INTERVAL), "SpawnMonster::scheduleSpawn"); + g_dispatcher().scheduleEvent(NONBLOCKABLE_SPAWN_MONSTER_INTERVAL, std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, mType, interval - NONBLOCKABLE_SPAWN_MONSTER_INTERVAL, startup), "SpawnMonster::scheduleSpawn"); } } @@ -309,6 +284,7 @@ void SpawnMonster::cleanup() { } } for (const auto &spawnMonsterId : removeList) { + spawnMonsterMap[spawnMonsterId].lastSpawn = OTSYS_TIME(); spawnedMonsterMap.erase(spawnMonsterId); } } @@ -327,7 +303,22 @@ bool SpawnMonster::addMonster(const std::string &name, const Position &pos, Dire return false; } - this->interval = std::min(this->interval, scheduleInterval); + uint32_t eventschedule = g_eventsScheduler().getSpawnMonsterSchedule(); + std::string boostedMonster = g_game().getBoostedMonsterName(); + int32_t boostedrate = 1; + if (name == boostedMonster) { + boostedrate = 2; + } + // eventschedule is a whole percentage, so we need to multiply by 100 to match the order of magnitude of the other values + scheduleInterval = scheduleInterval * 100 / std::max((uint32_t)1, (g_configManager().getNumber(RATE_SPAWN) * boostedrate * eventschedule)); + if (scheduleInterval < MONSTER_MINSPAWN_INTERVAL) { + g_logger().warn("[SpawnsMonster::addMonster] - {} {} spawntime cannot be less than {} seconds, set to {} by default.", name, pos.toString(), MONSTER_MINSPAWN_INTERVAL / 1000, MONSTER_MINSPAWN_INTERVAL / 1000); + scheduleInterval = MONSTER_MINSPAWN_INTERVAL; + } else if (scheduleInterval > MONSTER_MAXSPAWN_INTERVAL) { + g_logger().warn("[SpawnsMonster::addMonster] - {} {} spawntime can not be more than {} seconds, set to {} by default", name, pos.toString(), MONSTER_MAXSPAWN_INTERVAL / 1000, MONSTER_MAXSPAWN_INTERVAL / 1000); + scheduleInterval = MONSTER_MAXSPAWN_INTERVAL; + } + this->interval = std::gcd(this->interval, scheduleInterval); spawnBlock_t* sb = nullptr; uint32_t spawnMonsterId = spawnMonsterMap.size() + 1; diff --git a/src/creatures/monsters/spawns/spawn_monster.hpp b/src/creatures/monsters/spawns/spawn_monster.hpp index 36dd2e1ca09..79f329aefc0 100644 --- a/src/creatures/monsters/spawns/spawn_monster.hpp +++ b/src/creatures/monsters/spawns/spawn_monster.hpp @@ -70,7 +70,7 @@ class SpawnMonster { uint32_t checkSpawnMonsterEvent = 0; static bool findPlayer(const Position &pos); - bool spawnMonster(uint32_t spawnMonsterId, const std::shared_ptr monsterType, const Position &pos, Direction dir, bool startup = false); + bool spawnMonster(uint32_t spawnMonsterId, spawnBlock_t &sb, const std::shared_ptr monsterType, bool startup = false); void checkSpawnMonster(); void scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, const std::shared_ptr monsterType, uint16_t interval); }; From e85201571a38f8ab5a4de70fb0d04a9ea4679332 Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Fri, 1 Dec 2023 22:12:57 -0800 Subject: [PATCH 5/5] fix: rebase issues --- src/creatures/monsters/spawns/spawn_monster.cpp | 10 +++++----- src/creatures/monsters/spawns/spawn_monster.hpp | 2 +- src/pch.hpp | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index c4266633f21..05435d6b4a8 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -193,7 +193,7 @@ bool SpawnMonster::spawnMonster(uint32_t spawnMonsterId, spawnBlock_t &sb, const } void SpawnMonster::startup(bool delayed) { - if (g_configManager().getBoolean(RANDOM_MONSTER_SPAWN)) { + if (g_configManager().getBoolean(RANDOM_MONSTER_SPAWN, __FUNCTION__)) { for (auto it = spawnMonsterMap.begin(); it != spawnMonsterMap.end(); ++it) { auto &[spawnMonsterId, sb] = *it; for (auto &[monsterType, weight] : sb.monsterTypes) { @@ -223,9 +223,9 @@ void SpawnMonster::startup(bool delayed) { continue; } if (delayed) { - g_dispatcher().addEvent(std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, mType, 0), "SpawnMonster::startup"); + g_dispatcher().addEvent(std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, mType, 0, true), "SpawnMonster::startup"); } else { - scheduleSpawn(spawnMonsterId, sb, mType, 0); + scheduleSpawn(spawnMonsterId, sb, mType, 0, true); } } } @@ -267,7 +267,7 @@ void SpawnMonster::checkSpawnMonster() { } } -void SpawnMonster::scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, const std::shared_ptr mType, uint16_t interval) { +void SpawnMonster::scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, const std::shared_ptr mType, uint16_t interval, bool startup /*= false*/) { if (interval <= 0) { spawnMonster(spawnMonsterId, sb, mType, startup); } else { @@ -310,7 +310,7 @@ bool SpawnMonster::addMonster(const std::string &name, const Position &pos, Dire boostedrate = 2; } // eventschedule is a whole percentage, so we need to multiply by 100 to match the order of magnitude of the other values - scheduleInterval = scheduleInterval * 100 / std::max((uint32_t)1, (g_configManager().getNumber(RATE_SPAWN) * boostedrate * eventschedule)); + scheduleInterval = scheduleInterval * 100 / std::max((uint32_t)1, (g_configManager().getNumber(RATE_SPAWN, __FUNCTION__) * boostedrate * eventschedule)); if (scheduleInterval < MONSTER_MINSPAWN_INTERVAL) { g_logger().warn("[SpawnsMonster::addMonster] - {} {} spawntime cannot be less than {} seconds, set to {} by default.", name, pos.toString(), MONSTER_MINSPAWN_INTERVAL / 1000, MONSTER_MINSPAWN_INTERVAL / 1000); scheduleInterval = MONSTER_MINSPAWN_INTERVAL; diff --git a/src/creatures/monsters/spawns/spawn_monster.hpp b/src/creatures/monsters/spawns/spawn_monster.hpp index 79f329aefc0..9ea29e4f317 100644 --- a/src/creatures/monsters/spawns/spawn_monster.hpp +++ b/src/creatures/monsters/spawns/spawn_monster.hpp @@ -72,7 +72,7 @@ class SpawnMonster { static bool findPlayer(const Position &pos); bool spawnMonster(uint32_t spawnMonsterId, spawnBlock_t &sb, const std::shared_ptr monsterType, bool startup = false); void checkSpawnMonster(); - void scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, const std::shared_ptr monsterType, uint16_t interval); + void scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, const std::shared_ptr monsterType, uint16_t interval, bool startup = false); }; class SpawnsMonster { diff --git a/src/pch.hpp b/src/pch.hpp index 04f692a0bf1..ea0233ee1a8 100644 --- a/src/pch.hpp +++ b/src/pch.hpp @@ -41,6 +41,7 @@ #include #include #include +#include // -------------------- // System Includes