diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index 0353ee49bb0..c4266633f21 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -101,17 +101,7 @@ bool SpawnsMonster::loadFromXML(const std::string &filemonstername) { 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) * 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); } } } @@ -175,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; } @@ -247,7 +237,6 @@ void SpawnMonster::checkSpawnMonster() { checkSpawnMonsterEvent = 0; cleanup(); - uint32_t spawnMonsterCount = 0; for (auto &[spawnMonsterId, sb] : spawnMonsterMap) { if (spawnedMonsterMap.contains(spawnMonsterId)) { @@ -258,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; } @@ -271,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))) { - break; - } } if (spawnedMonsterMap.size() < spawnMonsterMap.size()) { @@ -288,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"); } } @@ -303,6 +284,7 @@ void SpawnMonster::cleanup() { } } for (const auto &spawnMonsterId : removeList) { + spawnMonsterMap[spawnMonsterId].lastSpawn = OTSYS_TIME(); spawnedMonsterMap.erase(spawnMonsterId); } } @@ -321,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); };