2 * Copyright (C) 2005-2010 MaNGOS <http://www.mangosproject.org/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "PoolManager.h"
20 #include "ObjectMgr.h"
21 #include "ObjectDefines.h"
22 #include "ProgressBar.h"
24 #include "MapManager.h"
25 #include "Policies/SingletonImp.h"
27 INSTANTIATE_SINGLETON_1(PoolManager
);
29 ////////////////////////////////////////////////////////////
30 // Methods of template class PoolGroup
32 // Method to add a gameobject/creature guid to the proper list depending on pool type and chance value
34 void PoolGroup
<T
>::AddEntry(PoolObject
& poolitem
, uint32 maxentries
)
36 if (poolitem
.chance
!= 0 && maxentries
== 1)
37 ExplicitlyChanced
.push_back(poolitem
);
39 EqualChanced
.push_back(poolitem
);
42 // Method to check the chances are proper in this object pool
44 bool PoolGroup
<T
>::CheckPool() const
46 if (EqualChanced
.size() == 0)
49 for (uint32 i
=0; i
<ExplicitlyChanced
.size(); ++i
)
50 chance
+= ExplicitlyChanced
[i
].chance
;
51 if (chance
!= 100 && chance
!= 0)
57 // Method that tell if the gameobject, creature or pool is spawned currently
59 bool PoolGroup
<T
>::IsSpawnedObject(uint32 guid
) const
61 for (uint32 i
= 0; i
< ExplicitlyChanced
.size(); ++i
)
62 if (ExplicitlyChanced
[i
].guid
== guid
)
63 return ExplicitlyChanced
[i
].spawned
;
64 for (uint32 i
= 0; i
< EqualChanced
.size(); ++i
)
65 if (EqualChanced
[i
].guid
== guid
)
66 return EqualChanced
[i
].spawned
;
71 PoolObject
* PoolGroup
<T
>::RollOne(uint32 triggerFrom
)
73 if (!ExplicitlyChanced
.empty())
75 float roll
= (float)rand_chance();
77 for (uint32 i
= 0; i
< ExplicitlyChanced
.size(); ++i
)
79 roll
-= ExplicitlyChanced
[i
].chance
;
80 // Triggering object is marked as spawned at this time and can be also rolled (respawn case)
81 // so this need explicit check for this case
82 if (roll
< 0 && (!ExplicitlyChanced
[i
].spawned
|| ExplicitlyChanced
[i
].guid
== triggerFrom
))
83 return &ExplicitlyChanced
[i
];
87 if (!EqualChanced
.empty())
89 int32 index
= irand(0, EqualChanced
.size()-1);
90 // Triggering object is marked as spawned at this time and can be also rolled (respawn case)
91 // so this need explicit check for this case
92 if (!EqualChanced
[index
].spawned
|| EqualChanced
[index
].guid
== triggerFrom
)
93 return &EqualChanced
[index
];
99 // Main method to despawn a creature or gameobject in a pool
100 // If no guid is passed, the pool is just removed (event end case)
101 // If guid is filled, cache will be used and no removal will occur, it just fill the cache
103 void PoolGroup
<T
>::DespawnObject(uint32 guid
)
105 for (size_t i
= 0; i
< EqualChanced
.size(); ++i
)
107 if (EqualChanced
[i
].spawned
)
109 if (!guid
|| EqualChanced
[i
].guid
== guid
)
111 Despawn1Object(EqualChanced
[i
].guid
);
112 EqualChanced
[i
].spawned
= false;
114 if (m_SpawnedPoolAmount
> 0)
115 --m_SpawnedPoolAmount
;
120 for (size_t i
= 0; i
< ExplicitlyChanced
.size(); ++i
)
122 if (ExplicitlyChanced
[i
].spawned
)
124 if (!guid
|| ExplicitlyChanced
[i
].guid
== guid
)
126 Despawn1Object(ExplicitlyChanced
[i
].guid
);
127 ExplicitlyChanced
[i
].spawned
= false;
129 if (m_SpawnedPoolAmount
> 0)
130 --m_SpawnedPoolAmount
;
136 // Method that is actualy doing the removal job on one creature
138 void PoolGroup
<Creature
>::Despawn1Object(uint32 guid
)
140 if (CreatureData
const* data
= sObjectMgr
.GetCreatureData(guid
))
142 sObjectMgr
.RemoveCreatureFromGrid(guid
, data
);
144 if (Creature
* pCreature
= ObjectAccessor::GetCreatureInWorld(MAKE_NEW_GUID(guid
, data
->id
, HIGHGUID_UNIT
)))
145 pCreature
->AddObjectToRemoveList();
149 // Same on one gameobject
151 void PoolGroup
<GameObject
>::Despawn1Object(uint32 guid
)
153 if (GameObjectData
const* data
= sObjectMgr
.GetGOData(guid
))
155 sObjectMgr
.RemoveGameobjectFromGrid(guid
, data
);
157 if (GameObject
* pGameobject
= ObjectAccessor::GetGameObjectInWorld(MAKE_NEW_GUID(guid
, data
->id
, HIGHGUID_GAMEOBJECT
)))
158 pGameobject
->AddObjectToRemoveList();
164 void PoolGroup
<Pool
>::Despawn1Object(uint32 child_pool_id
)
166 sPoolMgr
.DespawnPool(child_pool_id
);
169 // Method for a pool only to remove any found record causing a circular dependency loop
171 void PoolGroup
<Pool
>::RemoveOneRelation(uint16 child_pool_id
)
173 for (PoolObjectList::iterator itr
= ExplicitlyChanced
.begin(); itr
!= ExplicitlyChanced
.end(); ++itr
)
175 if(itr
->guid
== child_pool_id
)
177 ExplicitlyChanced
.erase(itr
);
181 for (PoolObjectList::iterator itr
= EqualChanced
.begin(); itr
!= EqualChanced
.end(); ++itr
)
183 if(itr
->guid
== child_pool_id
)
185 EqualChanced
.erase(itr
);
192 void PoolGroup
<T
>::SpawnObject(uint32 limit
, uint32 triggerFrom
)
194 uint32 lastDespawned
= 0;
195 int count
= limit
- m_SpawnedPoolAmount
;
197 // If triggered from some object respawn this object is still marked as spawned
198 // and also counted into m_SpawnedPoolAmount so we need increase count to be
203 // This will try to spawn the rest of pool, not guaranteed
204 for (int i
= 0; i
< count
; ++i
)
206 PoolObject
* obj
= RollOne(triggerFrom
);
209 if (obj
->guid
== lastDespawned
)
212 if (obj
->guid
== triggerFrom
)
223 // One spawn one despawn no count increase
224 DespawnObject(triggerFrom
);
225 lastDespawned
= triggerFrom
;
229 ++m_SpawnedPoolAmount
;
233 // Method that is actualy doing the spawn job on 1 creature
235 void PoolGroup
<Creature
>::Spawn1Object(PoolObject
* obj
)
237 if (CreatureData
const* data
= sObjectMgr
.GetCreatureData(obj
->guid
))
239 sObjectMgr
.AddCreatureToGrid(obj
->guid
, data
);
241 // Spawn if necessary (loaded grids only)
242 Map
* map
= const_cast<Map
*>(sMapMgr
.CreateBaseMap(data
->mapid
));
243 // We use spawn coords to spawn
244 if (!map
->Instanceable() && map
->IsLoaded(data
->posX
, data
->posY
))
246 Creature
* pCreature
= new Creature
;
247 //sLog.outDebug("Spawning creature %u",obj->guid);
248 if (!pCreature
->LoadFromDB(obj
->guid
, map
))
251 obj
->spawned
= false;
260 obj
->spawned
= false;
263 // Same for 1 gameobject
265 void PoolGroup
<GameObject
>::Spawn1Object(PoolObject
* obj
)
267 if (GameObjectData
const* data
= sObjectMgr
.GetGOData(obj
->guid
))
269 sObjectMgr
.AddGameobjectToGrid(obj
->guid
, data
);
270 // Spawn if necessary (loaded grids only)
271 // this base map checked as non-instanced and then only existed
272 Map
* map
= const_cast<Map
*>(sMapMgr
.CreateBaseMap(data
->mapid
));
273 // We use current coords to unspawn, not spawn coords since creature can have changed grid
274 if (!map
->Instanceable() && map
->IsLoaded(data
->posX
, data
->posY
))
276 GameObject
* pGameobject
= new GameObject
;
277 //sLog.outDebug("Spawning gameobject %u", obj->guid);
278 if (!pGameobject
->LoadFromDB(obj
->guid
, map
))
281 obj
->spawned
= false;
286 if (pGameobject
->isSpawnedByDefault())
287 map
->Add(pGameobject
);
293 obj
->spawned
= false;
298 void PoolGroup
<Pool
>::Spawn1Object(PoolObject
* obj
)
300 sPoolMgr
.SpawnPool(obj
->guid
);
304 // Method that does the respawn job on the specified creature
306 void PoolGroup
<Creature
>::ReSpawn1Object(PoolObject
* obj
)
308 if (CreatureData
const* data
= sObjectMgr
.GetCreatureData(obj
->guid
))
310 if (Creature
* pCreature
= ObjectAccessor::GetCreatureInWorld(MAKE_NEW_GUID(obj
->guid
, data
->id
, HIGHGUID_UNIT
)))
311 pCreature
->GetMap()->Add(pCreature
);
316 obj
->spawned
= false;
319 // Same for 1 gameobject
321 void PoolGroup
<GameObject
>::ReSpawn1Object(PoolObject
* obj
)
323 if (GameObjectData
const* data
= sObjectMgr
.GetGOData(obj
->guid
))
325 if (GameObject
* pGameobject
= ObjectAccessor::GetGameObjectInWorld(MAKE_NEW_GUID(obj
->guid
, data
->id
, HIGHGUID_GAMEOBJECT
)))
326 pGameobject
->GetMap()->Add(pGameobject
);
331 obj
->spawned
= false;
334 // Nothing to do for a child Pool
336 void PoolGroup
<Pool
>::ReSpawn1Object(PoolObject
* obj
)
342 ////////////////////////////////////////////////////////////
343 // Methods of class PoolManager
345 PoolManager::PoolManager()
349 void PoolManager::LoadFromDB()
351 QueryResult
*result
= WorldDatabase
.Query("SELECT MAX(entry) FROM pool_template");
354 sLog
.outString(">> Table pool_template is empty.");
360 Field
*fields
= result
->Fetch();
361 max_pool_id
= fields
[0].GetUInt16();
365 mPoolTemplate
.resize(max_pool_id
+ 1);
367 result
= WorldDatabase
.Query("SELECT entry,max_limit FROM pool_template");
370 mPoolTemplate
.clear();
371 sLog
.outString(">> Table pool_template is empty:");
378 barGoLink
bar(result
->GetRowCount());
382 Field
*fields
= result
->Fetch();
386 uint16 pool_id
= fields
[0].GetUInt16();
388 PoolTemplateData
& pPoolTemplate
= mPoolTemplate
[pool_id
];
389 pPoolTemplate
.MaxLimit
= fields
[1].GetUInt32();
391 } while (result
->NextRow());
394 sLog
.outString( ">> Loaded %u objects pools", count
);
399 mPoolCreatureGroups
.resize(max_pool_id
+ 1);
400 mCreatureSearchMap
.clear();
402 result
= WorldDatabase
.Query("SELECT guid, pool_entry, chance FROM pool_creature");
411 sLog
.outString(">> Loaded %u creatures in pools", count
);
416 barGoLink
bar2(result
->GetRowCount());
419 Field
*fields
= result
->Fetch();
423 uint32 guid
= fields
[0].GetUInt32();
424 uint16 pool_id
= fields
[1].GetUInt16();
425 float chance
= fields
[2].GetFloat();
427 CreatureData
const* data
= sObjectMgr
.GetCreatureData(guid
);
430 sLog
.outErrorDb("`pool_creature` has a non existing creature spawn (GUID: %u) defined for pool id (%u), skipped.", guid
, pool_id
);
433 if (pool_id
> max_pool_id
)
435 sLog
.outErrorDb("`pool_creature` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.",pool_id
);
438 if (chance
< 0 || chance
> 100)
440 sLog
.outErrorDb("`pool_creature` has an invalid chance (%f) for creature guid (%u) in pool id (%i), skipped.", chance
, guid
, pool_id
);
443 PoolTemplateData
*pPoolTemplate
= &mPoolTemplate
[pool_id
];
446 PoolObject plObject
= PoolObject(guid
, chance
);
447 PoolGroup
<Creature
>& cregroup
= mPoolCreatureGroups
[pool_id
];
448 cregroup
.AddEntry(plObject
, pPoolTemplate
->MaxLimit
);
449 SearchPair
p(guid
, pool_id
);
450 mCreatureSearchMap
.insert(p
);
452 } while (result
->NextRow());
454 sLog
.outString( ">> Loaded %u creatures in pools", count
);
460 mPoolGameobjectGroups
.resize(max_pool_id
+ 1);
461 mGameobjectSearchMap
.clear();
463 result
= WorldDatabase
.Query("SELECT guid, pool_entry, chance FROM pool_gameobject");
472 sLog
.outString(">> Loaded %u gameobject in pools", count
);
477 barGoLink
bar2(result
->GetRowCount());
480 Field
*fields
= result
->Fetch();
484 uint32 guid
= fields
[0].GetUInt32();
485 uint16 pool_id
= fields
[1].GetUInt16();
486 float chance
= fields
[2].GetFloat();
488 GameObjectData
const* data
= sObjectMgr
.GetGOData(guid
);
491 sLog
.outErrorDb("`pool_gameobject` has a non existing gameobject spawn (GUID: %u) defined for pool id (%u), skipped.", guid
, pool_id
);
494 GameObjectInfo
const* goinfo
= ObjectMgr::GetGameObjectInfo(data
->id
);
495 if (goinfo
->type
!= GAMEOBJECT_TYPE_CHEST
&&
496 goinfo
->type
!= GAMEOBJECT_TYPE_GOOBER
&&
497 goinfo
->type
!= GAMEOBJECT_TYPE_FISHINGHOLE
)
499 sLog
.outErrorDb("`pool_gameobject` has a not lootable gameobject spawn (GUID: %u, type: %u) defined for pool id (%u), skipped.", guid
, goinfo
->type
, pool_id
);
502 if (pool_id
> max_pool_id
)
504 sLog
.outErrorDb("`pool_gameobject` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.",pool_id
);
507 if (chance
< 0 || chance
> 100)
509 sLog
.outErrorDb("`pool_gameobject` has an invalid chance (%f) for gameobject guid (%u) in pool id (%i), skipped.", chance
, guid
, pool_id
);
512 PoolTemplateData
*pPoolTemplate
= &mPoolTemplate
[pool_id
];
516 PoolObject plObject
= PoolObject(guid
, chance
);
517 PoolGroup
<GameObject
>& gogroup
= mPoolGameobjectGroups
[pool_id
];
518 gogroup
.AddEntry(plObject
, pPoolTemplate
->MaxLimit
);
519 SearchPair
p(guid
, pool_id
);
520 mGameobjectSearchMap
.insert(p
);
522 } while( result
->NextRow() );
524 sLog
.outString( ">> Loaded %u gameobject in pools", count
);
529 mPoolPoolGroups
.resize(max_pool_id
+ 1);
531 result
= WorldDatabase
.Query("SELECT pool_id, mother_pool, chance FROM pool_pool");
540 sLog
.outString(">> Loaded %u pools in pools", count
);
545 barGoLink
bar2( result
->GetRowCount() );
548 Field
*fields
= result
->Fetch();
552 uint16 child_pool_id
= fields
[0].GetUInt16();
553 uint16 mother_pool_id
= fields
[1].GetUInt16();
554 float chance
= fields
[2].GetFloat();
556 if (mother_pool_id
> max_pool_id
)
558 sLog
.outErrorDb("`pool_pool` mother_pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.",mother_pool_id
);
561 if (child_pool_id
> max_pool_id
)
563 sLog
.outErrorDb("`pool_pool` included pool_id (%i) is out of range compared to max pool id in `pool_template`, skipped.",child_pool_id
);
566 if (mother_pool_id
== child_pool_id
)
568 sLog
.outErrorDb("`pool_pool` pool_id (%i) includes itself, dead-lock detected, skipped.",child_pool_id
);
571 if (chance
< 0 || chance
> 100)
573 sLog
.outErrorDb("`pool_pool` has an invalid chance (%f) for pool id (%u) in mother pool id (%i), skipped.", chance
, child_pool_id
, mother_pool_id
);
576 PoolTemplateData
*pPoolTemplateMother
= &mPoolTemplate
[mother_pool_id
];
580 PoolObject plObject
= PoolObject(child_pool_id
, chance
);
581 PoolGroup
<Pool
>& plgroup
= mPoolPoolGroups
[mother_pool_id
];
582 plgroup
.AddEntry(plObject
, pPoolTemplateMother
->MaxLimit
);
583 SearchPair
p(child_pool_id
, mother_pool_id
);
584 mPoolSearchMap
.insert(p
);
586 } while( result
->NextRow() );
588 // Now check for circular reference
589 for(uint16 i
=0; i
<max_pool_id
; ++i
)
591 std::set
<uint16
> checkedPools
;
592 for(SearchMap::iterator poolItr
= mPoolSearchMap
.find(i
); poolItr
!= mPoolSearchMap
.end(); poolItr
= mPoolSearchMap
.find(poolItr
->second
))
594 checkedPools
.insert(poolItr
->first
);
595 if(checkedPools
.find(poolItr
->second
) != checkedPools
.end())
597 std::ostringstream ss
;
599 for (std::set
<uint16
>::const_iterator itr
=checkedPools
.begin(); itr
!=checkedPools
.end(); ++itr
)
601 ss
<< "create(s) a circular reference, which can cause the server to freeze.\nRemoving the last link between mother pool "
602 << poolItr
->first
<< " and child pool " << poolItr
->second
;
603 sLog
.outErrorDb("%s", ss
.str().c_str());
604 mPoolPoolGroups
[poolItr
->second
].RemoveOneRelation(poolItr
->first
);
605 mPoolSearchMap
.erase(poolItr
);
612 sLog
.outString( ">> Loaded %u pools in mother pools", count
);
617 // The initialize method will spawn all pools not in an event and not in another pool, this is why there is 2 left joins with 2 null checks
618 void PoolManager::Initialize()
620 QueryResult
*result
= WorldDatabase
.Query("SELECT DISTINCT pool_template.entry FROM pool_template LEFT JOIN game_event_pool ON pool_template.entry=game_event_pool.pool_entry LEFT JOIN pool_pool ON pool_template.entry=pool_pool.pool_id WHERE game_event_pool.pool_entry IS NULL AND pool_pool.pool_id IS NULL");
626 Field
*fields
= result
->Fetch();
627 uint16 pool_entry
= fields
[0].GetUInt16();
628 if (!CheckPool(pool_entry
))
630 sLog
.outErrorDb("Pool Id (%u) has all creatures or gameobjects with explicit chance sum <>100 and no equal chance defined. The pool system cannot pick one to spawn.", pool_entry
);
633 SpawnPool(pool_entry
);
635 } while (result
->NextRow());
639 sLog
.outBasic("Pool handling system initialized, %u pools spawned.", count
);
642 // Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
643 // If it's same, the creature is respawned only (added back to map)
645 void PoolManager::SpawnPool
<Creature
>(uint16 pool_id
, uint32 db_guid
)
647 if (!mPoolCreatureGroups
[pool_id
].isEmpty())
648 mPoolCreatureGroups
[pool_id
].SpawnObject(mPoolTemplate
[pool_id
].MaxLimit
, db_guid
);
651 // Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
652 // If it's same, the gameobject is respawned only (added back to map)
654 void PoolManager::SpawnPool
<GameObject
>(uint16 pool_id
, uint32 db_guid
)
656 if (!mPoolGameobjectGroups
[pool_id
].isEmpty())
657 mPoolGameobjectGroups
[pool_id
].SpawnObject(mPoolTemplate
[pool_id
].MaxLimit
, db_guid
);
660 // Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
661 // If it's same, the pool is respawned only
663 void PoolManager::SpawnPool
<Pool
>(uint16 pool_id
, uint32 sub_pool_id
)
665 if (!mPoolPoolGroups
[pool_id
].isEmpty())
666 mPoolPoolGroups
[pool_id
].SpawnObject(mPoolTemplate
[pool_id
].MaxLimit
, sub_pool_id
);
669 void PoolManager::SpawnPool( uint16 pool_id
)
671 SpawnPool
<Pool
>(pool_id
, 0);
672 SpawnPool
<GameObject
>(pool_id
, 0);
673 SpawnPool
<Creature
>(pool_id
, 0);
676 // Call to despawn a pool, all gameobjects/creatures in this pool are removed
677 void PoolManager::DespawnPool(uint16 pool_id
)
679 if (!mPoolCreatureGroups
[pool_id
].isEmpty())
680 mPoolCreatureGroups
[pool_id
].DespawnObject();
682 if (!mPoolGameobjectGroups
[pool_id
].isEmpty())
683 mPoolGameobjectGroups
[pool_id
].DespawnObject();
685 if (!mPoolPoolGroups
[pool_id
].isEmpty())
686 mPoolPoolGroups
[pool_id
].DespawnObject();
689 // Method that check chance integrity of the creatures and gameobjects in this pool
690 bool PoolManager::CheckPool(uint16 pool_id
) const
692 return pool_id
<= max_pool_id
&&
693 mPoolGameobjectGroups
[pool_id
].CheckPool() &&
694 mPoolCreatureGroups
[pool_id
].CheckPool() &&
695 mPoolPoolGroups
[pool_id
].CheckPool();
698 // Method that tell if a creature in pool_id is spawned currently
700 bool PoolManager::IsSpawnedObject
<Creature
>(uint16 pool_id
, uint32 db_guid
) const
702 if (pool_id
> max_pool_id
)
704 return mPoolCreatureGroups
[pool_id
].IsSpawnedObject(db_guid
);
707 // Method that tell if a gameobject in pool_id is spawned currently
709 bool PoolManager::IsSpawnedObject
<GameObject
>(uint16 pool_id
, uint32 db_guid
) const
711 if (pool_id
> max_pool_id
)
713 return mPoolGameobjectGroups
[pool_id
].IsSpawnedObject(db_guid
);
716 // Method that tell if a pool in pool_id is spawned currently
718 bool PoolManager::IsSpawnedObject
<Pool
>(uint16 pool_id
, uint32 sub_pool_id
) const
720 if (pool_id
> max_pool_id
)
722 return mPoolPoolGroups
[pool_id
].IsSpawnedObject(sub_pool_id
);
725 // Call to update the pool when a gameobject/creature part of pool [pool_id] is ready to respawn
726 // Here we cache only the creature/gameobject whose guid is passed as parameter
727 // Then the spawn pool call will use this cache to decide
729 void PoolManager::UpdatePool(uint16 pool_id
, uint32 db_guid_or_pool_id
)
731 if (uint16 motherpoolid
= IsPartOfAPool
<Pool
>(pool_id
))
732 SpawnPool
<Pool
>(motherpoolid
, 0);
734 SpawnPool
<T
>(pool_id
, db_guid_or_pool_id
);
737 template void PoolManager::UpdatePool
<Pool
>(uint16 pool_id
, uint32 db_guid_or_pool_id
);
738 template void PoolManager::UpdatePool
<GameObject
>(uint16 pool_id
, uint32 db_guid_or_pool_id
);
739 template void PoolManager::UpdatePool
<Creature
>(uint16 pool_id
, uint32 db_guid_or_pool_id
);