• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[1.4.2] Spawn when player on screen with teleport effect.

OTcreator

Active Member
Joined
Feb 14, 2022
Messages
425
Solutions
1
Reaction score
44
Hi!
I need change in sources for TFS 1.4.2.
Respawn when player is on the screen - like Tibia Global.
 
Zgaduję, że mówisz o spawnowaniu stworzeń?
This is not for 1.4.2 ...

Code:
/var/www/compiled/src/spawn.cpp: In member function ‘void Spawn::scheduleSpawn(uint32_t, spawnBlock_t, uint32_t)’:
/var/www/compiled/src/spawn.cpp:380:28: error: ‘struct spawnBlock_t’ has no member named ‘mType’; did you mean ‘mTypes’?
  380 |   spawnMonster(spawnId, sb.mType, sb.pos, sb.direction);
      |                            ^~~~~
      |                            mTypes
make[2]: *** [CMakeFiles/tfs.dir/build.make:1009: CMakeFiles/tfs.dir/src/spawn.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:125: CMakeFiles/tfs.dir/all] Error 2
make: *** [Makefile:84: all] Error 2
 
Last edited:
I use TFS 1.4.2 here is my spawn.cpp
I have no issues

C++:
// Copyright 2022 The Forgotten Server Authors. All rights reserved.
// Use of this source code is governed by the GPL-2.0 License that can be found in the LICENSE file.

#include "otpch.h"

#include "spawn.h"
#include "game.h"
#include "monster.h"
#include "configmanager.h"
#include "scheduler.h"

#include "pugicast.h"
#include "events.h"

extern ConfigManager g_config;
extern Monsters g_monsters;
extern Game g_game;
extern Events* g_events;

static constexpr int32_t MINSPAWN_INTERVAL = 10 * 1000; // 10 seconds to match RME
static constexpr int32_t MAXSPAWN_INTERVAL = 24 * 60 * 60 * 1000; // 1 day

bool Spawns::loadFromXml(const std::string& filename)
{
    if (loaded) {
        return true;
    }

    pugi::xml_document doc;
    pugi::xml_parse_result result = doc.load_file(filename.c_str());
    if (!result) {
        printXMLError("Error - Spawns::loadFromXml", filename, result);
        return false;
    }

    this->filename = filename;
    loaded = true;

    for (auto spawnNode : doc.child("spawns").children()) {
        Position centerPos(
            pugi::cast<uint16_t>(spawnNode.attribute("centerx").value()),
            pugi::cast<uint16_t>(spawnNode.attribute("centery").value()),
            pugi::cast<uint16_t>(spawnNode.attribute("centerz").value())
        );

        int32_t radius;
        pugi::xml_attribute radiusAttribute = spawnNode.attribute("radius");
        if (radiusAttribute) {
            radius = pugi::cast<int32_t>(radiusAttribute.value());
        } else {
            radius = -1;
        }

        if (radius > 30) {
            std::cout << "[Warning - Spawns::loadFromXml] Radius size bigger than 30 at position: " << centerPos << ", consider lowering it." << std::endl;
        }

        if (!spawnNode.first_child()) {
            std::cout << "[Warning - Spawns::loadFromXml] Empty spawn at position: " << centerPos << " with radius: " << radius << '.' << std::endl;
            continue;
        }

        spawnList.emplace_front(centerPos, radius);
        Spawn& spawn = spawnList.front();

        for (auto childNode : spawnNode.children()) {
            if (strcasecmp(childNode.name(), "monsters") == 0) {
                Position pos(
                    centerPos.x + pugi::cast<uint16_t>(childNode.attribute("x").value()),
                    centerPos.y + pugi::cast<uint16_t>(childNode.attribute("y").value()),
                    centerPos.z
                );

                int32_t interval = pugi::cast<int32_t>(childNode.attribute("spawntime").value()) * 1000;
                if (interval < MINSPAWN_INTERVAL) {
                    std::cout << "[Warning - Spawns::loadFromXml] " << pos << " spawntime can not be less than " << MINSPAWN_INTERVAL / 1000 << " seconds." << std::endl;
                    continue;
                } else if (interval > MAXSPAWN_INTERVAL) {
                    std::cout << "[Warning - Spawns::loadFromXml] " << pos << " spawntime can not be more than " << MAXSPAWN_INTERVAL / 1000 << " seconds." << std::endl;
                    continue;
                }

                size_t monstersCount = std::distance(childNode.children().begin(), childNode.children().end());
                if (monstersCount == 0) {
                    std::cout << "[Warning - Spawns::loadFromXml] " << pos << " empty monsters set." << std::endl;
                    continue;
                }

                uint16_t totalChance = 0;
                spawnBlock_t sb;
                sb.pos = pos;
                sb.direction = DIRECTION_NORTH;
                sb.interval = interval;
                sb.lastSpawn = 0;

                for (auto monsterNode : childNode.children()) {
                    pugi::xml_attribute nameAttribute = monsterNode.attribute("name");
                    if (!nameAttribute) {
                        continue;
                    }

                    MonsterType* mType = g_monsters.getMonsterType(nameAttribute.as_string());
                    if (!mType) {
                        std::cout << "[Warning - Spawn::loadFromXml] " << pos << " can not find " << nameAttribute.as_string() << std::endl;
                        continue;
                    }

                    uint16_t chance = 100 / monstersCount;
                    pugi::xml_attribute chanceAttribute = monsterNode.attribute("chance");
                    if (chanceAttribute) {
                        chance = pugi::cast<uint16_t>(chanceAttribute.value());
                    }

                    if (chance + totalChance > 100) {
                        chance = 100 - totalChance;
                        totalChance = 100;
                        std::cout << "[Warning - Spawns::loadFromXml] " << mType->name << ' ' << pos << " total chance for set can not be higher than 100." << std::endl;
                    } else {
                        totalChance += chance;
                    }

                    sb.mTypes.push_back({mType, chance});
                }

                if (sb.mTypes.empty()) {
                    std::cout << "[Warning - Spawns::loadFromXml] " << pos << " empty monsters set." << std::endl;
                    continue;
                }

                sb.mTypes.shrink_to_fit();
                if (sb.mTypes.size() > 1) {
                    std::sort(sb.mTypes.begin(), sb.mTypes.end(), [](std::pair<MonsterType*, uint16_t> a, std::pair<MonsterType*, uint16_t> b) {
                        return a.second > b.second;
                    });
                }

                spawn.addBlock(sb);
            } else if (strcasecmp(childNode.name(), "monster") == 0) {
                pugi::xml_attribute nameAttribute = childNode.attribute("name");
                if (!nameAttribute) {
                    continue;
                }

                Direction dir;

                pugi::xml_attribute directionAttribute = childNode.attribute("direction");
                if (directionAttribute) {
                    dir = static_cast<Direction>(pugi::cast<uint16_t>(directionAttribute.value()));
                } else {
                    dir = DIRECTION_NORTH;
                }

                Position pos(
                    centerPos.x + pugi::cast<uint16_t>(childNode.attribute("x").value()),
                    centerPos.y + pugi::cast<uint16_t>(childNode.attribute("y").value()),
                    centerPos.z
                );
                int32_t interval = pugi::cast<int32_t>(childNode.attribute("spawntime").value()) * 1000;
                if (interval >= MINSPAWN_INTERVAL && interval <= MAXSPAWN_INTERVAL) {
                    spawn.addMonster(nameAttribute.as_string(), pos, dir, static_cast<uint32_t>(interval));
                } else {
                    if (interval < MINSPAWN_INTERVAL) {
                        std::cout << "[Warning - Spawns::loadFromXml] " << nameAttribute.as_string() << ' ' << pos << " spawntime can not be less than " << MINSPAWN_INTERVAL / 1000 << " seconds." << std::endl;
                    } else {
                        std::cout << "[Warning - Spawns::loadFromXml] " << nameAttribute.as_string() << ' ' << pos << " spawntime can not be more than " << MAXSPAWN_INTERVAL / 1000 << " seconds." << std::endl;
                    }
                }
            } else if (strcasecmp(childNode.name(), "npc") == 0) {
                pugi::xml_attribute nameAttribute = childNode.attribute("name");
                if (!nameAttribute) {
                    continue;
                }

                Npc* npc = Npc::createNpc(nameAttribute.as_string());
                if (!npc) {
                    continue;
                }

                pugi::xml_attribute directionAttribute = childNode.attribute("direction");
                if (directionAttribute) {
                    npc->setDirection(static_cast<Direction>(pugi::cast<uint16_t>(directionAttribute.value())));
                }

                npc->setMasterPos(Position(
                    centerPos.x + pugi::cast<uint16_t>(childNode.attribute("x").value()),
                    centerPos.y + pugi::cast<uint16_t>(childNode.attribute("y").value()),
                    centerPos.z
                ), radius);
                npcList.push_front(npc);
            }
        }
    }
    return true;
}

void Spawns::startup()
{
    if (!loaded || isStarted()) {
        return;
    }

    for (Npc* npc : npcList) {
        if (!g_game.placeCreature(npc, npc->getMasterPos(), false, true)) {
            std::cout << "[Warning - Spawns::startup] Couldn't spawn npc \"" << npc->getName() << "\" on position: " << npc->getMasterPos() << '.' << std::endl;
            delete npc;
        }
    }
    npcList.clear();

    for (Spawn& spawn : spawnList) {
        spawn.startup();
    }

    started = true;
}

void Spawns::clear()
{
    for (Spawn& spawn : spawnList) {
        spawn.stopEvent();
    }
    spawnList.clear();

    loaded = false;
    started = false;
    filename.clear();
}

bool Spawns::isInZone(const Position& centerPos, int32_t radius, const Position& pos)
{
    if (radius == -1) {
        return true;
    }

    return ((pos.getX() >= centerPos.getX() - radius) && (pos.getX() <= centerPos.getX() + radius) &&
            (pos.getY() >= centerPos.getY() - radius) && (pos.getY() <= centerPos.getY() + radius));
}

void Spawn::startSpawnCheck()
{
    if (checkSpawnEvent == 0) {
        checkSpawnEvent = g_scheduler.addEvent(createSchedulerTask(getInterval(), std::bind(&Spawn::checkSpawn, this)));
    }
}

Spawn::~Spawn()
{
    for (const auto& it : spawnedMap) {
        Monster* monster = it.second;
        monster->setSpawn(nullptr);
        monster->decrementReferenceCounter();
    }
}

bool Spawn::isInSpawnZone(const Position& pos)
{
    return Spawns::isInZone(centerPos, radius, pos);
}

bool Spawn::spawnMonster(uint32_t spawnId, spawnBlock_t sb, bool startup/* = false*/)
{
    bool isBlocked = false; // modified to false, monsters will spawn regardless of player's presence
size_t monstersCount = sb.mTypes.size(), blockedMonsters = 0;

const auto spawnFunc = [&](bool roll) {
    for (const auto& pair : sb.mTypes) {
        if (!roll) {
            return spawnMonster(spawnId, pair.first, sb.pos, sb.direction, startup);
        }

        if (pair.second >= normal_random(1, 100) && spawnMonster(spawnId, pair.first, sb.pos, sb.direction, startup)) {
            return true;
        }
    }

    return false;
};


// Try to spawn something with chance check, unless it's single spawn
if (spawnFunc(monstersCount > 1)) {
    return true;
}

// Just try to spawn something without chance check
return spawnFunc(false);
}
bool Spawn::spawnMonster(uint32_t spawnId, MonsterType* mType, const Position& pos, Direction dir, bool startup/*= false*/)
{
    std::unique_ptr<Monster> monster_ptr(new Monster(mType));
    if (!g_events->eventMonsterOnSpawn(monster_ptr.get(), pos, startup, false)) {
        return false;
    }

    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_ptr.get(), pos, true)) {
            std::cout << "[Warning - Spawns::startup] Couldn't spawn monster \"" << monster_ptr->getName() << "\" on position: " << pos << '.' << std::endl;
            return false;
        }
    } else {
        if (!g_game.placeCreature(monster_ptr.get(), pos, false, true)) {
            return false;
        }
    }

    Monster* monster = monster_ptr.release();
    monster->setDirection(dir);
    monster->setSpawn(this);
    monster->setMasterPos(pos);
    monster->incrementReferenceCounter();

    spawnedMap.insert({spawnId, monster});
    spawnMap[spawnId].lastSpawn = OTSYS_TIME();
    return true;
}

void Spawn::startup()
{
    for (const auto& it : spawnMap) {
        uint32_t spawnId = it.first;
        const spawnBlock_t& sb = it.second;
        spawnMonster(spawnId, sb, true);
    }
}

void Spawn::checkSpawn()
{
    checkSpawnEvent = 0;

    cleanup();

    uint32_t spawnCount = 0;

    for (auto& it : spawnMap) {
        uint32_t spawnId = it.first;
        if (spawnedMap.find(spawnId) != spawnedMap.end()) {
            continue;
        }

        spawnBlock_t& sb = it.second;
        if (OTSYS_TIME() >= sb.lastSpawn + sb.interval) {
            if (!spawnMonster(spawnId, sb)) {
                sb.lastSpawn = OTSYS_TIME();
                continue;
            }

            if (++spawnCount >= static_cast<uint32_t>(g_config.getNumber(ConfigManager::RATE_SPAWN))) {
                break;
            }
        }
    }

    if (spawnedMap.size() < spawnMap.size()) {
        checkSpawnEvent = g_scheduler.addEvent(createSchedulerTask(getInterval(), std::bind(&Spawn::checkSpawn, this)));
    }
}

void Spawn::cleanup()
{
    auto it = spawnedMap.begin();
    while (it != spawnedMap.end()) {
        uint32_t spawnId = it->first;
        Monster* monster = it->second;
        if (monster->isRemoved()) {
            monster->decrementReferenceCounter();
            it = spawnedMap.erase(it);
        } else if (!isInSpawnZone(monster->getPosition()) && spawnId != 0) {
            spawnedMap.insert({0, monster});
            it = spawnedMap.erase(it);
        } else {
            ++it;
        }
    }
}

bool Spawn::addBlock(spawnBlock_t sb)
{
    interval = std::min(interval, sb.interval);
    spawnMap[spawnMap.size() + 1] = sb;

    return true;
}

bool Spawn::addMonster(const std::string& name, const Position& pos, Direction dir, uint32_t interval)
{
    MonsterType* mType = g_monsters.getMonsterType(name);
    if (!mType) {
        std::cout << "[Warning - Spawn::addMonster] Can not find " << name << std::endl;
        return false;
    }

    spawnBlock_t sb;
    sb.mTypes.push_back({mType, 100});
    sb.pos = pos;
    sb.direction = dir;
    sb.interval = interval;
    sb.lastSpawn = 0;

    return addBlock(sb);
}

void Spawn::removeMonster(Monster* monster)
{
    for (auto it = spawnedMap.begin(), end = spawnedMap.end(); it != end; ++it) {
        if (it->second == monster) {
            monster->decrementReferenceCounter();
            spawnedMap.erase(it);
            break;
        }
    }
}

void Spawn::stopEvent()
{
    if (checkSpawnEvent != 0) {
        g_scheduler.stopEvent(checkSpawnEvent);
        checkSpawnEvent = 0;
    }
}
 
I use TFS 1.4.2 here is my spawn.cpp
I have no issues

C++:
// Copyright 2022 The Forgotten Server Authors. All rights reserved.
// Use of this source code is governed by the GPL-2.0 License that can be found in the LICENSE file.

#include "otpch.h"

#include "spawn.h"
#include "game.h"
#include "monster.h"
#include "configmanager.h"
#include "scheduler.h"

#include "pugicast.h"
#include "events.h"

extern ConfigManager g_config;
extern Monsters g_monsters;
extern Game g_game;
extern Events* g_events;

static constexpr int32_t MINSPAWN_INTERVAL = 10 * 1000; // 10 seconds to match RME
static constexpr int32_t MAXSPAWN_INTERVAL = 24 * 60 * 60 * 1000; // 1 day

bool Spawns::loadFromXml(const std::string& filename)
{
    if (loaded) {
        return true;
    }

    pugi::xml_document doc;
    pugi::xml_parse_result result = doc.load_file(filename.c_str());
    if (!result) {
        printXMLError("Error - Spawns::loadFromXml", filename, result);
        return false;
    }

    this->filename = filename;
    loaded = true;

    for (auto spawnNode : doc.child("spawns").children()) {
        Position centerPos(
            pugi::cast<uint16_t>(spawnNode.attribute("centerx").value()),
            pugi::cast<uint16_t>(spawnNode.attribute("centery").value()),
            pugi::cast<uint16_t>(spawnNode.attribute("centerz").value())
        );

        int32_t radius;
        pugi::xml_attribute radiusAttribute = spawnNode.attribute("radius");
        if (radiusAttribute) {
            radius = pugi::cast<int32_t>(radiusAttribute.value());
        } else {
            radius = -1;
        }

        if (radius > 30) {
            std::cout << "[Warning - Spawns::loadFromXml] Radius size bigger than 30 at position: " << centerPos << ", consider lowering it." << std::endl;
        }

        if (!spawnNode.first_child()) {
            std::cout << "[Warning - Spawns::loadFromXml] Empty spawn at position: " << centerPos << " with radius: " << radius << '.' << std::endl;
            continue;
        }

        spawnList.emplace_front(centerPos, radius);
        Spawn& spawn = spawnList.front();

        for (auto childNode : spawnNode.children()) {
            if (strcasecmp(childNode.name(), "monsters") == 0) {
                Position pos(
                    centerPos.x + pugi::cast<uint16_t>(childNode.attribute("x").value()),
                    centerPos.y + pugi::cast<uint16_t>(childNode.attribute("y").value()),
                    centerPos.z
                );

                int32_t interval = pugi::cast<int32_t>(childNode.attribute("spawntime").value()) * 1000;
                if (interval < MINSPAWN_INTERVAL) {
                    std::cout << "[Warning - Spawns::loadFromXml] " << pos << " spawntime can not be less than " << MINSPAWN_INTERVAL / 1000 << " seconds." << std::endl;
                    continue;
                } else if (interval > MAXSPAWN_INTERVAL) {
                    std::cout << "[Warning - Spawns::loadFromXml] " << pos << " spawntime can not be more than " << MAXSPAWN_INTERVAL / 1000 << " seconds." << std::endl;
                    continue;
                }

                size_t monstersCount = std::distance(childNode.children().begin(), childNode.children().end());
                if (monstersCount == 0) {
                    std::cout << "[Warning - Spawns::loadFromXml] " << pos << " empty monsters set." << std::endl;
                    continue;
                }

                uint16_t totalChance = 0;
                spawnBlock_t sb;
                sb.pos = pos;
                sb.direction = DIRECTION_NORTH;
                sb.interval = interval;
                sb.lastSpawn = 0;

                for (auto monsterNode : childNode.children()) {
                    pugi::xml_attribute nameAttribute = monsterNode.attribute("name");
                    if (!nameAttribute) {
                        continue;
                    }

                    MonsterType* mType = g_monsters.getMonsterType(nameAttribute.as_string());
                    if (!mType) {
                        std::cout << "[Warning - Spawn::loadFromXml] " << pos << " can not find " << nameAttribute.as_string() << std::endl;
                        continue;
                    }

                    uint16_t chance = 100 / monstersCount;
                    pugi::xml_attribute chanceAttribute = monsterNode.attribute("chance");
                    if (chanceAttribute) {
                        chance = pugi::cast<uint16_t>(chanceAttribute.value());
                    }

                    if (chance + totalChance > 100) {
                        chance = 100 - totalChance;
                        totalChance = 100;
                        std::cout << "[Warning - Spawns::loadFromXml] " << mType->name << ' ' << pos << " total chance for set can not be higher than 100." << std::endl;
                    } else {
                        totalChance += chance;
                    }

                    sb.mTypes.push_back({mType, chance});
                }

                if (sb.mTypes.empty()) {
                    std::cout << "[Warning - Spawns::loadFromXml] " << pos << " empty monsters set." << std::endl;
                    continue;
                }

                sb.mTypes.shrink_to_fit();
                if (sb.mTypes.size() > 1) {
                    std::sort(sb.mTypes.begin(), sb.mTypes.end(), [](std::pair<MonsterType*, uint16_t> a, std::pair<MonsterType*, uint16_t> b) {
                        return a.second > b.second;
                    });
                }

                spawn.addBlock(sb);
            } else if (strcasecmp(childNode.name(), "monster") == 0) {
                pugi::xml_attribute nameAttribute = childNode.attribute("name");
                if (!nameAttribute) {
                    continue;
                }

                Direction dir;

                pugi::xml_attribute directionAttribute = childNode.attribute("direction");
                if (directionAttribute) {
                    dir = static_cast<Direction>(pugi::cast<uint16_t>(directionAttribute.value()));
                } else {
                    dir = DIRECTION_NORTH;
                }

                Position pos(
                    centerPos.x + pugi::cast<uint16_t>(childNode.attribute("x").value()),
                    centerPos.y + pugi::cast<uint16_t>(childNode.attribute("y").value()),
                    centerPos.z
                );
                int32_t interval = pugi::cast<int32_t>(childNode.attribute("spawntime").value()) * 1000;
                if (interval >= MINSPAWN_INTERVAL && interval <= MAXSPAWN_INTERVAL) {
                    spawn.addMonster(nameAttribute.as_string(), pos, dir, static_cast<uint32_t>(interval));
                } else {
                    if (interval < MINSPAWN_INTERVAL) {
                        std::cout << "[Warning - Spawns::loadFromXml] " << nameAttribute.as_string() << ' ' << pos << " spawntime can not be less than " << MINSPAWN_INTERVAL / 1000 << " seconds." << std::endl;
                    } else {
                        std::cout << "[Warning - Spawns::loadFromXml] " << nameAttribute.as_string() << ' ' << pos << " spawntime can not be more than " << MAXSPAWN_INTERVAL / 1000 << " seconds." << std::endl;
                    }
                }
            } else if (strcasecmp(childNode.name(), "npc") == 0) {
                pugi::xml_attribute nameAttribute = childNode.attribute("name");
                if (!nameAttribute) {
                    continue;
                }

                Npc* npc = Npc::createNpc(nameAttribute.as_string());
                if (!npc) {
                    continue;
                }

                pugi::xml_attribute directionAttribute = childNode.attribute("direction");
                if (directionAttribute) {
                    npc->setDirection(static_cast<Direction>(pugi::cast<uint16_t>(directionAttribute.value())));
                }

                npc->setMasterPos(Position(
                    centerPos.x + pugi::cast<uint16_t>(childNode.attribute("x").value()),
                    centerPos.y + pugi::cast<uint16_t>(childNode.attribute("y").value()),
                    centerPos.z
                ), radius);
                npcList.push_front(npc);
            }
        }
    }
    return true;
}

void Spawns::startup()
{
    if (!loaded || isStarted()) {
        return;
    }

    for (Npc* npc : npcList) {
        if (!g_game.placeCreature(npc, npc->getMasterPos(), false, true)) {
            std::cout << "[Warning - Spawns::startup] Couldn't spawn npc \"" << npc->getName() << "\" on position: " << npc->getMasterPos() << '.' << std::endl;
            delete npc;
        }
    }
    npcList.clear();

    for (Spawn& spawn : spawnList) {
        spawn.startup();
    }

    started = true;
}

void Spawns::clear()
{
    for (Spawn& spawn : spawnList) {
        spawn.stopEvent();
    }
    spawnList.clear();

    loaded = false;
    started = false;
    filename.clear();
}

bool Spawns::isInZone(const Position& centerPos, int32_t radius, const Position& pos)
{
    if (radius == -1) {
        return true;
    }

    return ((pos.getX() >= centerPos.getX() - radius) && (pos.getX() <= centerPos.getX() + radius) &&
            (pos.getY() >= centerPos.getY() - radius) && (pos.getY() <= centerPos.getY() + radius));
}

void Spawn::startSpawnCheck()
{
    if (checkSpawnEvent == 0) {
        checkSpawnEvent = g_scheduler.addEvent(createSchedulerTask(getInterval(), std::bind(&Spawn::checkSpawn, this)));
    }
}

Spawn::~Spawn()
{
    for (const auto& it : spawnedMap) {
        Monster* monster = it.second;
        monster->setSpawn(nullptr);
        monster->decrementReferenceCounter();
    }
}

bool Spawn::isInSpawnZone(const Position& pos)
{
    return Spawns::isInZone(centerPos, radius, pos);
}

bool Spawn::spawnMonster(uint32_t spawnId, spawnBlock_t sb, bool startup/* = false*/)
{
    bool isBlocked = false; // modified to false, monsters will spawn regardless of player's presence
size_t monstersCount = sb.mTypes.size(), blockedMonsters = 0;

const auto spawnFunc = [&](bool roll) {
    for (const auto& pair : sb.mTypes) {
        if (!roll) {
            return spawnMonster(spawnId, pair.first, sb.pos, sb.direction, startup);
        }

        if (pair.second >= normal_random(1, 100) && spawnMonster(spawnId, pair.first, sb.pos, sb.direction, startup)) {
            return true;
        }
    }

    return false;
};


// Try to spawn something with chance check, unless it's single spawn
if (spawnFunc(monstersCount > 1)) {
    return true;
}

// Just try to spawn something without chance check
return spawnFunc(false);
}
bool Spawn::spawnMonster(uint32_t spawnId, MonsterType* mType, const Position& pos, Direction dir, bool startup/*= false*/)
{
    std::unique_ptr<Monster> monster_ptr(new Monster(mType));
    if (!g_events->eventMonsterOnSpawn(monster_ptr.get(), pos, startup, false)) {
        return false;
    }

    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_ptr.get(), pos, true)) {
            std::cout << "[Warning - Spawns::startup] Couldn't spawn monster \"" << monster_ptr->getName() << "\" on position: " << pos << '.' << std::endl;
            return false;
        }
    } else {
        if (!g_game.placeCreature(monster_ptr.get(), pos, false, true)) {
            return false;
        }
    }

    Monster* monster = monster_ptr.release();
    monster->setDirection(dir);
    monster->setSpawn(this);
    monster->setMasterPos(pos);
    monster->incrementReferenceCounter();

    spawnedMap.insert({spawnId, monster});
    spawnMap[spawnId].lastSpawn = OTSYS_TIME();
    return true;
}

void Spawn::startup()
{
    for (const auto& it : spawnMap) {
        uint32_t spawnId = it.first;
        const spawnBlock_t& sb = it.second;
        spawnMonster(spawnId, sb, true);
    }
}

void Spawn::checkSpawn()
{
    checkSpawnEvent = 0;

    cleanup();

    uint32_t spawnCount = 0;

    for (auto& it : spawnMap) {
        uint32_t spawnId = it.first;
        if (spawnedMap.find(spawnId) != spawnedMap.end()) {
            continue;
        }

        spawnBlock_t& sb = it.second;
        if (OTSYS_TIME() >= sb.lastSpawn + sb.interval) {
            if (!spawnMonster(spawnId, sb)) {
                sb.lastSpawn = OTSYS_TIME();
                continue;
            }

            if (++spawnCount >= static_cast<uint32_t>(g_config.getNumber(ConfigManager::RATE_SPAWN))) {
                break;
            }
        }
    }

    if (spawnedMap.size() < spawnMap.size()) {
        checkSpawnEvent = g_scheduler.addEvent(createSchedulerTask(getInterval(), std::bind(&Spawn::checkSpawn, this)));
    }
}

void Spawn::cleanup()
{
    auto it = spawnedMap.begin();
    while (it != spawnedMap.end()) {
        uint32_t spawnId = it->first;
        Monster* monster = it->second;
        if (monster->isRemoved()) {
            monster->decrementReferenceCounter();
            it = spawnedMap.erase(it);
        } else if (!isInSpawnZone(monster->getPosition()) && spawnId != 0) {
            spawnedMap.insert({0, monster});
            it = spawnedMap.erase(it);
        } else {
            ++it;
        }
    }
}

bool Spawn::addBlock(spawnBlock_t sb)
{
    interval = std::min(interval, sb.interval);
    spawnMap[spawnMap.size() + 1] = sb;

    return true;
}

bool Spawn::addMonster(const std::string& name, const Position& pos, Direction dir, uint32_t interval)
{
    MonsterType* mType = g_monsters.getMonsterType(name);
    if (!mType) {
        std::cout << "[Warning - Spawn::addMonster] Can not find " << name << std::endl;
        return false;
    }

    spawnBlock_t sb;
    sb.mTypes.push_back({mType, 100});
    sb.pos = pos;
    sb.direction = dir;
    sb.interval = interval;
    sb.lastSpawn = 0;

    return addBlock(sb);
}

void Spawn::removeMonster(Monster* monster)
{
    for (auto it = spawnedMap.begin(), end = spawnedMap.end(); it != end; ++it) {
        if (it->second == monster) {
            monster->decrementReferenceCounter();
            spawnedMap.erase(it);
            break;
        }
    }
}

void Spawn::stopEvent()
{
    if (checkSpawnEvent != 0) {
        g_scheduler.stopEvent(checkSpawnEvent);
        checkSpawnEvent = 0;
    }
}
Ok, this is clean TFS 1.4.2 spawn.cpp, but you don't have add code with teleport effect.
 
Back
Top