关于芒果魔兽 副本怪物生成线路逻辑

玩家进入副本的时候 会生成一个持久性针对当前地图的副本数据 MapPersistentState 这个副本数据会保存在这个持久化类里面 等玩家重置副本时候  下一帧会卸载掉这个数据 重新进入又会生成

调用方法如下

    DungeonPersistentState* state = (DungeonPersistentState*)sMapPersistentStateMgr.AddPersistentState(mapEntry, instanceId, Difficulty(difficulty), resetTime, !perm, true);

生成方法如下


MapPersistentState* MapPersistentStateManager::AddPersistentState(MapEntry const* mapEntry, uint32 instanceId, Difficulty difficulty, time_t resetTime, bool canReset, bool load /*=false*/, uint32 completedEncountersMask /*= 0*/)
{
    if (MapPersistentState* old_save = GetPersistentState(mapEntry->MapID, instanceId))
        return old_save;

    if (mapEntry->IsDungeon())
    {
        if (!resetTime)
        {
            // initialize reset time
            // for normal instances if no creatures are killed the instance will reset in 30 minutes
            if (mapEntry->map_type == MAP_RAID || difficulty > DUNGEON_DIFFICULTY_NORMAL)
                resetTime = m_Scheduler.GetResetTimeFor(mapEntry->MapID);
            else
            {
                resetTime = time(nullptr) + NORMAL_INSTANCE_RESET_TIME;
                // normally this will be removed soon after in DungeonMap::Add, prevent error
                m_Scheduler.ScheduleReset(true, resetTime, DungeonResetEvent(RESET_EVENT_NORMAL_DUNGEON, mapEntry->MapID, instanceId));
            }
        }
    }

    DEBUG_LOG("MapPersistentStateManager::AddPersistentState: mapid = %d, instanceid = %d, reset time = '" UI64FMTD "', canRset = %u", mapEntry->MapID, instanceId, uint64(resetTime), canReset ? 1 : 0);

    MapPersistentState* state;
    if (mapEntry->IsDungeon())
    {
        DungeonPersistentState* dungeonState = new DungeonPersistentState(mapEntry->MapID, instanceId, difficulty, resetTime, canReset, completedEncountersMask);
        if (!load)
            dungeonState->SaveToDB();
        state = dungeonState;
    }
    else if (mapEntry->IsBattleGroundOrArena())
        state = new BattleGroundPersistentState(mapEntry->MapID, instanceId, difficulty);
    else
        state = new WorldPersistentState(mapEntry->MapID);


    if (instanceId)
        m_instanceSaveByInstanceId[instanceId] = state;
    else
        m_instanceSaveByMapId[mapEntry->MapID] = state;

    return state;
}

特殊分组怪物数据从void ObjectMgr::LoadSpawnGroups()函数读取后 会从世界怪物里面删除

    for (auto& guidData : entry.DbGuids)
        {
            if (entry.Type == SPAWN_GROUP_CREATURE)
            {
                CreatureData const* data = GetCreatureData(guidData.DbGuid);
                RemoveCreatureFromGrid(guidData.DbGuid, data);
                newContainer->spawnGroupByGuidMap.emplace(std::make_pair(guidData.DbGuid, uint32(TYPEID_UNIT)), &entry);
                if (sWorld.getConfig(CONFIG_BOOL_AUTOLOAD_ACTIVE))
                {
                    for (auto& data : entry.RandomEntries)
                    {
                        if (CreatureInfo const* cinfo = GetCreatureTemplate(data.Entry))
                        {
                            if ((cinfo->ExtraFlags & CREATURE_EXTRA_FLAG_ACTIVE) != 0)
                            {
                                entry.Active = true;
                                break;
                            }
                        }
                    }
                }
                if (data->spawnMask == 0)
                    entry.EnabledByDefault = false;
                if (data->id)
                    guidData.OwnEntry = data->id;
                if (GetAllRandomCreatureEntries(guidData.DbGuid))
                    guidData.RandomEntry = true;
            }
            else
            {
                GameObjectData const* data = GetGOData(guidData.DbGuid);
                RemoveGameobjectFromGrid(guidData.DbGuid, data);
                newContainer->spawnGroupByGuidMap.emplace(std::make_pair(guidData.DbGuid, uint32(TYPEID_GAMEOBJECT)), &entry);
                if (sWorld.getConfig(CONFIG_BOOL_AUTOLOAD_ACTIVE))
                {
                    for (auto& data : entry.RandomEntries)
                    {
                        if (CreatureInfo const* cinfo = GetCreatureTemplate(data.Entry))
                        {
                            if ((cinfo->ExtraFlags & CREATURE_EXTRA_FLAG_ACTIVE) != 0)
                            {
                                entry.Active = true;
                                break;
                            }
                        }
                    }
                }
                if (data->spawnMask == 0)
                    entry.EnabledByDefault = false;
                if (data->id)
                    guidData.OwnEntry = data->id;
                if (GetAllRandomGameObjectEntries(guidData.DbGuid))
                    guidData.RandomEntry = true;
            }
        }
    }

下面就是整个副本初始化时候生成怪物的流程 上面已经生成了持久化的切片数据 然后就可以初始化切片内的怪物数据了 void Map::Initialize(bool loadInstanceData /*= true*/ 里面调用了 InitPools()函数

   m_persistentState = sMapPersistentStateMgr.AddPersistentState(i_mapEntry, GetInstanceId(), GetDifficulty(), 0, IsDungeon(), false);
    m_persistentState->SetUsedByMapState(this);
    m_persistentState->InitPools();

下面就是初始化

void MapPersistentState::InitPools()
{
    // pool system initialized already for persistent state (can be shared by map states)
    if (!GetSpawnedPoolData().IsInitialized())
    {
        GetSpawnedPoolData().SetInitialized();
        sPoolMgr.Initialize(this);                          // init pool system data for map persistent state
        sGameEventMgr.Initialize(this);                     // init pool system data for map persistent state
    }
}
void PoolManager::Initialize(MapPersistentState* state)
{
    // spawn pools for expected map or for not initialized shared pools state for non-instanceable maps
    for (uint16 pool_entry = 0; pool_entry < mPoolTemplate.size(); ++pool_entry)
        if (mPoolTemplate[pool_entry].AutoSpawn)
            InitSpawnPool(*state, pool_entry);
}
void PoolManager::InitSpawnPool(MapPersistentState& mapState, uint16 pool_id)
{
    // spawn pool for expected map
    if (mPoolTemplate[pool_id].CanBeSpawnedAtMap(mapState.GetMapEntry()))
        SpawnPool(mapState, pool_id, true);
}
void PoolManager::SpawnPool(MapPersistentState& mapState, uint16 pool_id, bool instantly)
{
    SpawnPoolGroup<Pool>(mapState, pool_id, 0, instantly);
    SpawnPoolGroup<GameObject>(mapState, pool_id, 0, instantly);
    SpawnPoolGroup<Creature>(mapState, pool_id, 0, instantly);
}

上面就会依次生成一个地图切片里不同的数据

void PoolManager::SpawnPoolGroup<Creature>(MapPersistentState& mapState, uint16 pool_id, uint32 db_guid, bool instantly)
{
    if (!mPoolCreatureGroups[pool_id].isEmpty())
        mPoolCreatureGroups[pool_id].SpawnObject(mapState, mPoolTemplate[pool_id].MaxLimit, db_guid, instantly);
}

template <class T>
void PoolGroup<T>::SpawnObject(MapPersistentState& mapState, uint32 limit, uint32 triggerFrom, bool instantly)
{
    SpawnedPoolData& spawns = mapState.GetSpawnedPoolData();

    uint32 lastDespawned = 0;
    int count = limit - spawns.GetSpawnedObjects(poolId);

    // If triggered from some object respawn this object is still marked as spawned
    // and also counted into m_SpawnedPoolAmount so we need increase count to be
    // spawned by 1
    if (triggerFrom)
    {
        if (spawns.IsSpawnedObject<T>(triggerFrom))
            ++count;
        else
            triggerFrom = 0;
    }

    // This will try to spawn the rest of pool, not guaranteed
    for (int i = 0; i < count; ++i)
    {
        PoolObject* obj = RollOne(spawns, triggerFrom, mapState);
        if (!obj)
            continue;
        if (obj->guid == lastDespawned)
            continue;

        if (obj->guid == triggerFrom)
        {
            MANGOS_ASSERT(spawns.IsSpawnedObject<T>(obj->guid));
            MANGOS_ASSERT(spawns.GetSpawnedObjects(poolId) > 0);
            ReSpawn1Object(mapState, obj);
            triggerFrom = 0;
            continue;
        }

        spawns.AddSpawn<T>(obj->guid, poolId);
        Spawn1Object(mapState, obj, instantly);

        if (triggerFrom)
        {
            // One spawn one despawn no count increase
            DespawnObject(mapState, triggerFrom);
            lastDespawned = triggerFrom;
            triggerFrom = 0;
        }
    }
}
void MapPersistentState::InitPools()
{
    // pool system initialized already for persistent state (can be shared by map states)
    if (!GetSpawnedPoolData().IsInitialized())
    {
        GetSpawnedPoolData().SetInitialized();
        sPoolMgr.Initialize(this);                          // init pool system data for map persistent state
        sGameEventMgr.Initialize(this);                     // init pool system data for map persistent state
    }
}
template <>
void PoolGroup<Creature>::Spawn1Object(MapPersistentState& mapState, PoolObject* obj, bool instantly)
{
    if (CreatureData const* data = sObjectMgr.GetCreatureData(obj->guid))
    {
        mapState.AddCreatureToGrid(obj->guid, data);
        Map* dataMap = mapState.GetMap();

        // We use spawn coords to spawn
        if (dataMap && dataMap->IsLoaded(data->posX, data->posY))
        {
            Creature* pCreature = new Creature;
            if (!pCreature->LoadFromDB(obj->guid, dataMap, obj->guid, 0))
                delete pCreature;
            else
            {
                // if new spawn replaces a just despawned creature, not instantly spawn but set respawn timer
                if (!instantly)
                {
                    pCreature->SetRespawnDelay(data->GetRandomRespawnTime());
                    pCreature->SetRespawnTime(pCreature->GetRespawnDelay());
                    if (sWorld.getConfig(CONFIG_BOOL_SAVE_RESPAWN_TIME_IMMEDIATELY) || pCreature->IsWorldBoss())
                        pCreature->SaveRespawnTime();
                }
            }
        }
        // for not loaded grid just update respawn time (avoid work for instances until implemented support)
        else if (!instantly)
            mapState.SaveCreatureRespawnTime(obj->guid, time(nullptr) + data->GetRandomRespawnTime());
    }
}

下面是怪物死亡后 更新的过程 这里设置了重生时间 


void Creature::Update(const uint32 diff)
{
    switch (m_deathState)
    {
        case JUST_ALIVED:
            // Don't must be called, see Creature::SetDeathState JUST_ALIVED -> ALIVE promoting.
            sLog.outError("Creature (GUIDLow: %u Entry: %u ) in wrong state: JUST_ALIVED (4)", GetGUIDLow(), GetEntry());
            break;
        case JUST_DIED:
            // Don't must be called, see Creature::SetDeathState JUST_DIED -> CORPSE promoting.
            sLog.outError("Creature (GUIDLow: %u Entry: %u ) in wrong state: JUST_DEAD (1)", GetGUIDLow(), GetEntry());
            break;
        case DEAD:
        {
            if (m_respawnTime <= time(nullptr) && (!m_isSpawningLinked || GetMap()->GetCreatureLinkingHolder()->CanSpawn(this)))
            {
                DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Respawning...");
                m_respawnTime = 0;
                SetCanAggro(false);
                delete m_loot;
                m_loot = nullptr;

                // Clear possible auras having IsDeathPersistent() attribute
                RemoveAllAuras();

                SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE);
                SetDeathState(JUST_ALIVED);

                // Call AI respawn virtual function
                if (AI())
                    AI()->JustRespawned();

                // Inform Instance Data
                if (InstanceData* mapInstance = GetInstanceData())
                    mapInstance->OnCreatureRespawn(this);

                if (m_isCreatureLinkingTrigger)
                    GetMap()->GetCreatureLinkingHolder()->DoCreatureLinkingEvent(LINKING_EVENT_RESPAWN, this);

                if (GetCreatureGroup())
                    GetCreatureGroup()->TriggerLinkingEvent(CREATURE_GROUP_EVENT_RESPAWN, this);

                GetMap()->Add(this);

                if (GetObjectGuid().GetHigh() != HIGHGUID_PET)
                    if (uint16 poolid = sPoolMgr.IsPartOfAPool<Creature>(GetDbGuid()))
                        sPoolMgr.UpdatePool<Creature>(*GetMap()->GetPersistentState(), poolid, GetDbGuid());
            }
            break;
        }
        case CORPSE:
        {
            Unit::Update(diff);

            if (m_loot)
                m_loot->Update();

            if (IsCorpseExpired())
                RemoveCorpse();

            break;
        }
        case ALIVE:
        {
            Unit::Update(diff);

            // creature can be dead after Unit::Update call
            // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
            if (!IsAlive())
                break;

            // Creature can be dead after unit update
            if (IsAlive())
                RegenerateAll(diff);

            break;
        }
        default:
            break;
    }
}

上面代码判断生物是死亡后  当重生时间到了 并且能重生 就会调用

       if (GetObjectGuid().GetHigh() != HIGHGUID_PET)
                    if (uint16 poolid = sPoolMgr.IsPartOfAPool<Creature>(GetDbGuid()))
                        sPoolMgr.UpdatePool<Creature>(*GetMap()->GetPersistentState(), poolid, GetDbGuid());

里面就会调用 上面生成的代码 就会重生怪物

下面代码实现了添加新的怪物组到世界流程  

void SpawnManager::Update()
{
    auto now = m_map.GetCurrentClockTime();
    for (auto itr = m_spawns.begin(); itr != m_spawns.end();)
    {
        auto& spawnInfo = *itr;
        if (spawnInfo.IsUsed() ||
            (spawnInfo.GetRespawnTime() <= now && spawnInfo.ConstructForMap(m_map)))
            itr = m_spawns.erase(itr);
        else
            ++itr;
    }

    for (auto& group : m_spawnGroups)
        group.second->Update();
}
void SpawnGroup::Update()
{
    Spawn(false);
}
void SpawnGroup::Spawn(bool force)
{
    if (!m_enabled && !force)
        return;

    // duplicated code for optimization - way fewer cond fails
    if ((m_entry.Flags & SPAWN_GROUP_DESPAWN_ON_COND_FAIL) != 0) // must be before count check
    {
        if (!m_objects.empty() && !IsWorldstateConditionSatisfied())
        {
            Despawn(0, 1); // respawn delay 1 to immediately be available for spawn on next condition success
            return;
        }
    }

    if (m_objects.size() >= m_entry.MaxCount)
        return;

    if (!IsWorldstateConditionSatisfied())
        return;

    if (m_entry.HasChancedSpawns && m_chosenSpawns.size() >= m_entry.MaxCount)
        return;

    std::vector<SpawnGroupDbGuids const*> eligibleGuids;
    std::map<uint32, uint32> validEntries;
    std::map<uint32, uint32> minEntries;

    for (auto& randomEntry : m_entry.RandomEntries)
    {
        validEntries[randomEntry.Entry] = randomEntry.MaxCount > 0 ? randomEntry.MaxCount : std::numeric_limits<uint32>::max();
        if (randomEntry.MinCount > 0)
            minEntries.emplace(randomEntry.Entry, randomEntry.MinCount);
    }

    for (auto& guid : m_entry.DbGuids)
        eligibleGuids.push_back(&guid);

    for (auto& data : m_objects)
    {
        eligibleGuids.erase(std::remove_if(eligibleGuids.begin(), eligibleGuids.end(), [dbGuid = data.first](SpawnGroupDbGuids const* entry) { return entry->DbGuid == dbGuid; }), eligibleGuids.end());
        if (validEntries.size() > 0)
        {
            uint32 curCount = validEntries[data.second];
            validEntries[data.second] = curCount > 0 ? curCount - 1 : 0;
        }
        if (minEntries.size() > 0)
        {
            auto itr = minEntries.find(data.second);
            if (itr != minEntries.end())
            {
                --(*itr).second;
                if ((*itr).second == 0)
                    minEntries.erase(itr);
            }
        }
    }

    time_t now = time(nullptr);
    for (auto itr = eligibleGuids.begin(); itr != eligibleGuids.end();)
    {
        if (m_map.GetPersistentState()->GetObjectRespawnTime(GetObjectTypeId(), (*itr)->DbGuid) > now)
        {
            if (!force)
            {
                if (m_entry.MaxCount == 1) // rare mob case - prevent respawn until all are off CD
                    return;
                itr = eligibleGuids.erase(itr);
                continue;
            }
            else
                m_map.GetPersistentState()->SaveObjectRespawnTime(GetObjectTypeId(), (*itr)->DbGuid, now);
        }
        ++itr;
    }

    for (auto itr = eligibleGuids.begin(); itr != eligibleGuids.end();)
    {
        uint32 spawnMask = 0; // safeguarded on db load
        if (GetObjectTypeId() == TYPEID_UNIT)
            spawnMask = sObjectMgr.GetCreatureData((*itr)->DbGuid)->spawnMask;
        else
            spawnMask = sObjectMgr.GetGOData((*itr)->DbGuid)->spawnMask;
        if (spawnMask && (spawnMask & (1 << m_map.GetDifficulty())) == 0)
        {
            itr = eligibleGuids.erase(itr);
            continue;
        }
        ++itr;
    }

    std::shuffle(eligibleGuids.begin(), eligibleGuids.end(), *GetRandomGenerator());

    for (auto itr = eligibleGuids.begin(); itr != eligibleGuids.end() && !eligibleGuids.empty() && m_objects.size() < m_entry.MaxCount; ++itr)
    {
        uint32 dbGuid = (*itr)->DbGuid;
        uint32 entry = 0;
        if (m_entry.HasChancedSpawns)
        {
            if ((*itr)->Chance)
            {
                auto spawnItr = m_chosenSpawns.find(dbGuid);
                bool spawn = true;
                if (spawnItr == m_chosenSpawns.end())
                {
                    spawn = roll_chance_i((*itr)->Chance);
                    m_chosenSpawns[dbGuid] = spawn;
                }
                else
                    spawn = spawnItr->second;

                if (!spawn)
                    continue;
            }
            else // filling redundant entries when a group has chanced spawns for optimization so we can stop at start
                m_chosenSpawns[dbGuid] = true;
        }

        // creatures pick random entry on first spawn in dungeons - else always pick random entry
        if (GetObjectTypeId() == TYPEID_UNIT)
        {
            if (m_map.IsDungeon())
            {
                // only held in memory - implement saving to db if it becomes a major issue
                if (m_chosenEntries.find(dbGuid) == m_chosenEntries.end())
                {
                    // some group members can have static entry, or selfcontained random entry
                    if ((*itr)->RandomEntry)
                        entry = sObjectMgr.GetRandomCreatureEntry(dbGuid);
                    else if ((*itr)->OwnEntry)
                        entry = (*itr)->OwnEntry;
                    else
                        entry = GetEligibleEntry(validEntries, minEntries);
                    m_chosenEntries[dbGuid] = entry;
                }
                else
                    entry = m_chosenEntries[dbGuid];
            }
            else
                entry = GetEligibleEntry(validEntries, minEntries);
        }
        else // GOs always pick random entry
        {
            if ((*itr)->RandomEntry)
                entry = sObjectMgr.GetRandomGameObjectEntry(dbGuid);
            else if ((*itr)->OwnEntry)
                entry = (*itr)->OwnEntry;
            else
                entry = GetEligibleEntry(validEntries, minEntries);
        }

        float x, y;
        if (GetObjectTypeId() == TYPEID_UNIT)
        {
            auto data = sObjectMgr.GetCreatureData(dbGuid);
            x = data->posX; y = data->posY;
            m_map.GetPersistentState()->AddCreatureToGrid(dbGuid, data);
        }
        else
        {
            auto data = sObjectMgr.GetGOData(dbGuid);
            x = data->posX; y = data->posY;
            m_map.GetPersistentState()->AddGameobjectToGrid(dbGuid, data);
        }
        AddObject(dbGuid, entry);
        if (force || m_entry.Active || m_map.IsLoaded(x, y))
        {
            if (GetObjectTypeId() == TYPEID_UNIT)
                WorldObject::SpawnCreature(dbGuid, &m_map, entry);
            else
                WorldObject::SpawnGameObject(dbGuid, &m_map, entry);
        }
        if (entry && validEntries[entry])
            --validEntries[entry];
    }
}

这样副本就会在玩家进入后的下一帧  生成对应的怪物进入副本

PS 额外一个问题 就是游戏事件触发的时候 生物及物品生成在下面函数内

void GameEventMgr::GameEventSpawn(int16 event_id)
{
    int32 internal_event_id = m_gameEvents.size() + event_id - 1;

    if (internal_event_id < 0 || (size_t)internal_event_id >= m_gameEventCreatureGuids.size())
    {
        sLog.outError("GameEventMgr::GameEventSpawn attempt access to out of range mGameEventCreatureGuids element %i (size: " SIZEFMTD ")", internal_event_id, m_gameEventCreatureGuids.size());
        return;
    }

    for (uint32& itr : m_gameEventCreatureGuids[internal_event_id])
    {
        // Add to correct cell
        CreatureData const* data = sObjectMgr.GetCreatureData(itr);
        if (data)
        {
            // negative event id for pool element meaning allow be used in next pool spawn
            if (event_id < 0)
            {
                if (uint16 pool_id = sPoolMgr.IsPartOfAPool<Creature>(itr))
                {
                    // will have chance at next pool update
                    sPoolMgr.SetExcludeObject<Creature>(pool_id, itr, false);
                    sPoolMgr.UpdatePoolInMaps<Creature>(pool_id);
                    continue;
                }
            }

            sObjectMgr.AddCreatureToGrid(itr, data);

            Creature::SpawnInMaps(itr, data);
        }
    }

具体 怪物生成 有ELUNA版本的 可以参考PerformIngameSpawn 函数 里面有具体不从数据库读取数据生成代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值