OregonCore  revision fb2a440-git
Your Favourite TBC server
PoolMgr.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the OregonCore Project. See AUTHORS file for Copyright information
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "PoolMgr.h"
19 #include "ObjectMgr.h"
20 #include "Log.h"
21 #include "MapManager.h"
22 
24 
26 // template class ActivePoolData
27 
28 // Method that tell amount spawned objects/subpools
30 {
31  ActivePoolPools::const_iterator itr = mSpawnedPools.find(pool_id);
32  return itr != mSpawnedPools.end() ? itr->second : 0;
33 }
34 
35 // Method that tell if a creature is spawned currently
36 template<>
37 bool ActivePoolData::IsActiveObject<Creature>(uint32 db_guid) const
38 {
39  return mSpawnedCreatures.find(db_guid) != mSpawnedCreatures.end();
40 }
41 
42 // Method that tell if a gameobject is spawned currently
43 template<>
44 bool ActivePoolData::IsActiveObject<GameObject>(uint32 db_guid) const
45 {
46  return mSpawnedGameobjects.find(db_guid) != mSpawnedGameobjects.end();
47 }
48 
49 // Method that tell if a pool is spawned currently
50 template<>
51 bool ActivePoolData::IsActiveObject<Pool>(uint32 sub_pool_id) const
52 {
53  return mSpawnedPools.find(sub_pool_id) != mSpawnedPools.end();
54 }
55 
56 // Method that tell if a quest can be started
57 template<>
58 bool ActivePoolData::IsActiveObject<Quest>(uint32 quest_id) const
59 {
60  return mActiveQuests.find(quest_id) != mActiveQuests.end();
61 }
62 
63 template<>
64 void ActivePoolData::ActivateObject<Creature>(uint32 db_guid, uint32 pool_id)
65 {
66  mSpawnedCreatures.insert(db_guid);
67  ++mSpawnedPools[pool_id];
68 }
69 
70 template<>
71 void ActivePoolData::ActivateObject<GameObject>(uint32 db_guid, uint32 pool_id)
72 {
73  mSpawnedGameobjects.insert(db_guid);
74  ++mSpawnedPools[pool_id];
75 }
76 
77 template<>
78 void ActivePoolData::ActivateObject<Pool>(uint32 sub_pool_id, uint32 pool_id)
79 {
80  mSpawnedPools[sub_pool_id] = 0;
81  ++mSpawnedPools[pool_id];
82 }
83 
84 template<>
85 void ActivePoolData::ActivateObject<Quest>(uint32 quest_id, uint32 pool_id)
86 {
87  mActiveQuests.insert(quest_id);
88  ++mSpawnedPools[pool_id];
89 }
90 
91 template<>
92 void ActivePoolData::RemoveObject<Creature>(uint32 db_guid, uint32 pool_id)
93 {
94  mSpawnedCreatures.erase(db_guid);
95  uint32& val = mSpawnedPools[pool_id];
96  if (val > 0)
97  --val;
98 }
99 
100 template<>
101 void ActivePoolData::RemoveObject<GameObject>(uint32 db_guid, uint32 pool_id)
102 {
103  mSpawnedGameobjects.erase(db_guid);
104  uint32& val = mSpawnedPools[pool_id];
105  if (val > 0)
106  --val;
107 }
108 
109 template<>
110 void ActivePoolData::RemoveObject<Pool>(uint32 sub_pool_id, uint32 pool_id)
111 {
112  mSpawnedPools.erase(sub_pool_id);
113  uint32& val = mSpawnedPools[pool_id];
114  if (val > 0)
115  --val;
116 }
117 
118 template<>
119 void ActivePoolData::RemoveObject<Quest>(uint32 quest_id, uint32 pool_id)
120 {
121  mActiveQuests.erase(quest_id);
122  uint32& val = mSpawnedPools[pool_id];
123  if (val > 0)
124  --val;
125 }
126 
128 // Methods of template class PoolGroup
129 
130 // Method to add a gameobject/creature guid to the proper list depending on pool type and chance value
131 template <class T>
132 void PoolGroup<T>::AddEntry(PoolObject& poolitem, uint32 maxentries)
133 {
134  if (poolitem.chance != 0 && maxentries == 1)
135  ExplicitlyChanced.push_back(poolitem);
136  else
137  EqualChanced.push_back(poolitem);
138 }
139 
140 // Method to check the chances are proper in this object pool
141 template <class T>
143 {
144  if (EqualChanced.empty())
145  {
146  float chance = 0;
147  for (uint32 i = 0; i < ExplicitlyChanced.size(); ++i)
148  chance += ExplicitlyChanced[i].chance;
149  if (chance != 100 && chance != 0)
150  return false;
151  }
152  return true;
153 }
154 
155 template <class T>
157 {
158  if (!ExplicitlyChanced.empty())
159  {
160  float roll = (float)rand_chance();
161 
162  for (uint32 i = 0; i < ExplicitlyChanced.size(); ++i)
163  {
164  roll -= ExplicitlyChanced[i].chance;
165  // Triggering object is marked as spawned at this time and can be also rolled (respawn case)
166  // so this need explicit check for this case
167  if (roll < 0 && (ExplicitlyChanced[i].guid == triggerFrom || !spawns.IsActiveObject<T>(ExplicitlyChanced[i].guid)))
168  return &ExplicitlyChanced[i];
169  }
170  }
171  if (!EqualChanced.empty())
172  {
173  uint32 index = urand(0, EqualChanced.size() - 1);
174  // Triggering object is marked as spawned at this time and can be also rolled (respawn case)
175  // so this need explicit check for this case
176  if (EqualChanced[index].guid == triggerFrom || !spawns.IsActiveObject<T>(EqualChanced[index].guid))
177  return &EqualChanced[index];
178  }
179 
180  return NULL;
181 }
182 
183 // Main method to despawn a creature or gameobject in a pool
184 // If no guid is passed, the pool is just removed (event end case)
185 // If guid is filled, cache will be used and no removal will occur, it just fill the cache
186 template<class T>
188 {
189  for (size_t i = 0; i < EqualChanced.size(); ++i)
190  {
191  // if spawned
192  if (spawns.IsActiveObject<T>(EqualChanced[i].guid))
193  {
194  if (!guid || EqualChanced[i].guid == guid)
195  {
196  Despawn1Object(EqualChanced[i].guid);
197  spawns.RemoveObject<T>(EqualChanced[i].guid, poolId);
198  }
199  }
200  }
201 
202  for (size_t i = 0; i < ExplicitlyChanced.size(); ++i)
203  {
204  // spawned
205  if (spawns.IsActiveObject<T>(ExplicitlyChanced[i].guid))
206  {
207  if (!guid || ExplicitlyChanced[i].guid == guid)
208  {
209  Despawn1Object(ExplicitlyChanced[i].guid);
210  spawns.RemoveObject<T>(ExplicitlyChanced[i].guid, poolId);
211  }
212  }
213  }
214 }
215 
216 // Method that is actualy doing the removal job on one creature
217 template<>
219 {
220  if (CreatureData const* data = sObjectMgr.GetCreatureData(guid))
221  {
222  sObjectMgr.RemoveCreatureFromGrid(guid, data);
223 
224  if (Creature* pCreature = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(guid, data->id, HIGHGUID_UNIT), (Creature*)NULL))
225  pCreature->AddObjectToRemoveList();
226  }
227 }
228 
229 // Same on one gameobject
230 template<>
232 {
233  if (GameObjectData const* data = sObjectMgr.GetGOData(guid))
234  {
235  sObjectMgr.RemoveGameobjectFromGrid(guid, data);
236 
237  if (GameObject* pGameobject = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(guid, data->id, HIGHGUID_GAMEOBJECT), (GameObject*)NULL))
238  pGameobject->AddObjectToRemoveList();
239  }
240 }
241 
242 // Same on one pool
243 template<>
245 {
246  sPoolMgr.DespawnPool(child_pool_id);
247 }
248 
249 // Same on one quest
250 template<>
252 {
253  // Creatures
254  QuestRelations* questMap = sObjectMgr.GetCreatureQuestRelationMap();
255  PooledQuestRelationBoundsNC qr = sPoolMgr.mQuestCreatureRelation.equal_range(quest_id);
256  for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr)
257  {
258  QuestRelations::iterator qitr = questMap->find(itr->second);
259  if (qitr == questMap->end())
260  continue;
261  QuestRelations::iterator lastElement = questMap->upper_bound(itr->second);
262  for (; qitr != lastElement; ++qitr)
263  {
264  if (qitr->first == itr->second && qitr->second == itr->first)
265  {
266  questMap->erase(qitr); // iterator is now no more valid
267  break; // but we can exit loop since the element is found
268  }
269  }
270  }
271 
272  // Gameobjects
273  questMap = sObjectMgr.GetGOQuestRelationMap();
274  qr = sPoolMgr.mQuestGORelation.equal_range(quest_id);
275  for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr)
276  {
277  QuestRelations::iterator qitr = questMap->find(itr->second);
278  if (qitr == questMap->end())
279  continue;
280  QuestRelations::iterator lastElement = questMap->upper_bound(itr->second);
281  for (; qitr != lastElement; ++qitr)
282  {
283  if (qitr->first == itr->second && qitr->second == itr->first)
284  {
285  questMap->erase(qitr); // iterator is now no more valid
286  break; // but we can exit loop since the element is found
287  }
288  }
289  }
290 }
291 
292 // Method for a pool only to remove any found record causing a circular dependency loop
293 template<>
295 {
296  for (PoolObjectList::iterator itr = ExplicitlyChanced.begin(); itr != ExplicitlyChanced.end(); ++itr)
297  {
298  if (itr->guid == child_pool_id)
299  {
300  ExplicitlyChanced.erase(itr);
301  break;
302  }
303  }
304  for (PoolObjectList::iterator itr = EqualChanced.begin(); itr != EqualChanced.end(); ++itr)
305  {
306  if (itr->guid == child_pool_id)
307  {
308  EqualChanced.erase(itr);
309  break;
310  }
311  }
312 }
313 
314 // Method that is actualy doing the spawn job on 1 creature
315 template <>
317 {
318  if (CreatureData const* data = sObjectMgr.GetCreatureData(obj->guid))
319  {
320  sObjectMgr.AddCreatureToGrid(obj->guid, data);
321 
322  // Spawn if necessary (loaded grids only)
323  Map* map = const_cast<Map*>(MapManager::Instance().CreateBaseMap(data->mapid));
324  // We use spawn coords to spawn
325  if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
326  {
327  Creature* creature = new Creature();
328  //sLog.outDebug("Spawning creature %u", guid);
329  if (!creature->LoadCreatureFromDB(obj->guid, map))
330  {
331  delete creature;
332  return;
333  }
334  }
335  }
336 }
337 
338 // Same for 1 gameobject
339 template <>
341 {
342  if (GameObjectData const* data = sObjectMgr.GetGOData(obj->guid))
343  {
344  sObjectMgr.AddGameobjectToGrid(obj->guid, data);
345  // Spawn if necessary (loaded grids only)
346  // this base map checked as non-instanced and then only existed
347  Map* map = const_cast<Map*>(MapManager::Instance().CreateBaseMap(data->mapid));
348  // We use current coords to unspawn, not spawn coords since creature can have changed grid
349  if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
350  {
351  GameObject* pGameobject = new GameObject;
352  //sLog.outDebug("Spawning gameobject %u", guid);
353  if (!pGameobject->LoadGameObjectFromDB(obj->guid, map, false))
354  {
355  delete pGameobject;
356  return;
357  }
358  else
359  {
360  if (pGameobject->isSpawnedByDefault())
361  map->AddToMap(pGameobject);
362  }
363  }
364  }
365 }
366 
367 // Same for 1 pool
368 template <>
370 {
371  sPoolMgr.SpawnPool(obj->guid);
372 }
373 
374 // Same for 1 quest
375 template<>
377 {
378  // Creatures
379  QuestRelations* questMap = sObjectMgr.GetCreatureQuestRelationMap();
380  PooledQuestRelationBoundsNC qr = sPoolMgr.mQuestCreatureRelation.equal_range(obj->guid);
381  for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr)
382  {
383  sLog.outDebug("PoolGroup<Quest>: Adding quest %u to creature %u", itr->first, itr->second);
384  questMap->insert(QuestRelations::value_type(itr->second, itr->first));
385  }
386 
387  // Gameobjects
388  questMap = sObjectMgr.GetGOQuestRelationMap();
389  qr = sPoolMgr.mQuestGORelation.equal_range(obj->guid);
390  for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr)
391  {
392  sLog.outDebug("PoolGroup<Quest>: Adding quest %u to GO %u", itr->first, itr->second);
393  questMap->insert(QuestRelations::value_type(itr->second, itr->first));
394  }
395 }
396 
397 // Method that does the respawn job on the specified creature
398 template <>
400 {
401  // Creature is still on map, nothing to do
402 }
403 
404 // Method that does the respawn job on the specified gameobject
405 template <>
407 {
408  // GameObject is still on map, nothing to do
409 }
410 
411 // Nothing to do for a child Pool
412 template <>
414 
415 // Nothing to do for a quest
416 template <>
418 
419 template <>
421 {
422  sLog.outDebug("PoolGroup<Quest>: Spawning pool %u", poolId);
423  // load state from db
424  if (!triggerFrom)
425  {
426  QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT quest_id FROM pool_quest_save WHERE pool_id = %u", poolId);
427  if (result)
428  {
429  do
430  {
431  uint32 questId = result->Fetch()[0].GetUInt32();
432  spawns.ActivateObject<Quest>(questId, poolId);
433  PoolObject tempObj(questId, 0.0f);
434  Spawn1Object(&tempObj);
435  --limit;
436  } while (result->NextRow() && limit);
437  return;
438  }
439  }
440  ActivePoolObjects currentQuests = spawns.GetActiveQuests();
441  ActivePoolObjects newQuests;
442 
443  // always try to select different quests
444  for (PoolObjectList::iterator itr = EqualChanced.begin(); itr != EqualChanced.end(); ++itr)
445  {
446  if (spawns.IsActiveObject<Quest>(itr->guid))
447  continue;
448  newQuests.insert(itr->guid);
449  }
450 
451  // clear the pool
452  DespawnObject(spawns);
453 
454  // recycle minimal amount of quests if possible count is lower than limit
455  if (limit > newQuests.size() && !currentQuests.empty())
456  {
457  do
458  {
459  uint32 questId = SelectRandomContainerElement(currentQuests);
460  newQuests.insert(questId);
461  currentQuests.erase(questId);
462  } while (newQuests.size() < limit && !currentQuests.empty()); // failsafe
463  }
464 
465  if (newQuests.empty())
466  return;
467 
468  // activate <limit> random quests
469  do
470  {
471  uint32 questId = SelectRandomContainerElement(newQuests);
472  spawns.ActivateObject<Quest>(questId, poolId);
473  PoolObject tempObj(questId, 0.0f);
474  Spawn1Object(&tempObj);
475  newQuests.erase(questId);
476  --limit;
477  } while (limit && !newQuests.empty());
478 
479  // if we are here it means the pool is initialized at startup and did not have previous saved state
480  if (!triggerFrom)
481  sPoolMgr.SaveQuestsToDB();
482 }
483 
484 
485 template <class T>
486 void PoolGroup<T>::SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 triggerFrom)
487 {
488  uint32 lastDespawned = 0;
489  int count = limit - spawns.GetActiveObjectCount(poolId);
490 
491  // If triggered from some object respawn this object is still marked as spawned
492  // and also counted into m_SpawnedPoolAmount so we need increase count to be
493  // spawned by 1
494  if (triggerFrom)
495  ++count;
496 
497  // This will try to spawn the rest of pool, not guaranteed
498  for (int i = 0; i < count; ++i)
499  {
500  PoolObject* obj = RollOne(spawns, triggerFrom);
501  if (!obj)
502  continue;
503  if (obj->guid == lastDespawned)
504  continue;
505 
506  if (obj->guid == triggerFrom)
507  {
508  ReSpawn1Object(obj);
509  triggerFrom = 0;
510  continue;
511  }
512  spawns.ActivateObject<T>(obj->guid, poolId);
513  Spawn1Object(obj);
514 
515  if (triggerFrom)
516  {
517  // One spawn one despawn no count increase
518  DespawnObject(spawns, triggerFrom);
519  lastDespawned = triggerFrom;
520  triggerFrom = 0;
521  }
522  }
523 }
524 
526 // Methods of class PoolMgr
527 
529 
531 {
532  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT MAX(entry) FROM pool_template");
533  if (!result)
534  {
535  sLog.outString(">> Loaded 0 object pools. DB table `pool_template` is empty.");
536  return;
537  }
538  else
539  {
540  Field* fields = result->Fetch();
541  max_pool_id = fields[0].GetUInt32();
542  }
543 
544  mPoolTemplate.resize(max_pool_id + 1);
545 
546  result = WorldDatabase.Query("SELECT entry,max_limit FROM pool_template");
547  if (!result)
548  {
549  mPoolTemplate.clear();
550  sLog.outString(">> Loaded 0 object pools. DB table `pool_template` is empty.");
551  return;
552  }
553 
554  uint32 count = 0;
555 
556  do
557  {
558  ++count;
559  Field* fields = result->Fetch();
560 
561  uint32 pool_id = fields[0].GetUInt32();
562 
563  PoolTemplateData& pPoolTemplate = mPoolTemplate[pool_id];
564  pPoolTemplate.MaxLimit = fields[1].GetUInt32();
565 
566  }
567  while (result->NextRow());
568 
569  sLog.outString(">> Loaded %u objects pools", count);
570 
571  // Creatures
572  mPoolCreatureGroups.resize(max_pool_id + 1);
573  mCreatureSearchMap.clear();
574  // 1 2 3
575  result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_creature");
576 
577  count = 0;
578  if (!result)
579 
580  sLog.outString(">> Loaded 0 creatures in pools. DB table `pool_creature` is empty.");
581  else
582  {
583 
584  do
585  {
586  Field* fields = result->Fetch();
587 
588 
589  uint32 guid = fields[0].GetUInt32();
590  uint32 pool_id = fields[1].GetUInt32();
591  float chance = fields[2].GetFloat();
592 
593  CreatureData const* data = sObjectMgr.GetCreatureData(guid);
594  if (!data)
595  {
596  sLog.outErrorDb("`pool_creature` has a non existing creature spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id);
597  continue;
598  }
599  if (pool_id > max_pool_id)
600  {
601  sLog.outErrorDb("`pool_creature` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.", pool_id);
602  continue;
603  }
604  if (chance < 0 || chance > 100)
605  {
606  sLog.outErrorDb("`pool_creature` has an invalid chance (%f) for creature guid (%u) in pool id (%i), skipped.", chance, guid, pool_id);
607  continue;
608  }
609  PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id];
610  ++count;
611 
612  PoolObject plObject = PoolObject(guid, chance);
613  PoolGroup<Creature>& cregroup = mPoolCreatureGroups[pool_id];
614  cregroup.SetPoolId(pool_id);
615  cregroup.AddEntry(plObject, pPoolTemplate->MaxLimit);
616  SearchPair p(guid, pool_id);
617  mCreatureSearchMap.insert(p);
618 
619  }
620  while (result->NextRow());
621  sLog.outString(">> Loaded %u creatures in pools", count);
622  }
623 
624  // Gameobjects
625  mPoolGameobjectGroups.resize(max_pool_id + 1);
626  mGameobjectSearchMap.clear();
627  // 1 2 3
628  result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_gameobject");
629 
630  count = 0;
631  if (!result)
632  sLog.outString(">> Loaded 0 gameobjects in pools. DB table `pool_gameobject` is empty.");
633  else
634  {
635  do
636  {
637  Field* fields = result->Fetch();
638 
639  uint32 guid = fields[0].GetUInt32();
640  uint32 pool_id = fields[1].GetUInt32();
641  float chance = fields[2].GetFloat();
642 
643  GameObjectData const* data = sObjectMgr.GetGOData(guid);
644  if (!data)
645  {
646  sLog.outErrorDb("`pool_gameobject` has a non existing gameobject spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id);
647  continue;
648  }
649  GameObjectInfo const* goinfo = sObjectMgr.GetGameObjectInfo(data->id);
650  if (goinfo->type != GAMEOBJECT_TYPE_CHEST &&
651  goinfo->type != GAMEOBJECT_TYPE_GOOBER &&
653  {
654  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);
655  continue;
656  }
657  if (pool_id > max_pool_id)
658  {
659  sLog.outErrorDb("`pool_gameobject` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.", pool_id);
660  continue;
661  }
662  if (chance < 0 || chance > 100)
663  {
664  sLog.outErrorDb("`pool_gameobject` has an invalid chance (%f) for gameobject guid (%u) in pool id (%i), skipped.", chance, guid, pool_id);
665  continue;
666  }
667  PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id];
668 
669  ++count;
670 
671  PoolObject plObject = PoolObject(guid, chance);
672  PoolGroup<GameObject>& gogroup = mPoolGameobjectGroups[pool_id];
673  gogroup.SetPoolId(pool_id);
674  gogroup.AddEntry(plObject, pPoolTemplate->MaxLimit);
675  SearchPair p(guid, pool_id);
676  mGameobjectSearchMap.insert(p);
677 
678  }
679  while (result->NextRow());
680  sLog.outString(">> Loaded %u gameobject in pools", count);
681  }
682 
683  // Pool of pools
684  mPoolPoolGroups.resize(max_pool_id + 1);
685  // 1 2 3
686  result = WorldDatabase.Query("SELECT pool_id, mother_pool, chance FROM pool_pool");
687 
688  count = 0;
689  if (!result)
690  sLog.outString(">> Loaded 0 pools in pools");
691  else
692  {
693  do
694  {
695  Field* fields = result->Fetch();
696 
697  uint32 child_pool_id = fields[0].GetUInt32();
698  uint32 mother_pool_id = fields[1].GetUInt32();
699  float chance = fields[2].GetFloat();
700 
701  if (mother_pool_id > max_pool_id)
702  {
703  sLog.outErrorDb("`pool_pool` mother_pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.", mother_pool_id);
704  continue;
705  }
706  if (child_pool_id > max_pool_id)
707  {
708  sLog.outErrorDb("`pool_pool` included pool_id (%i) is out of range compared to max pool id in `pool_template`, skipped.", child_pool_id);
709  continue;
710  }
711  if (mother_pool_id == child_pool_id)
712  {
713  sLog.outErrorDb("`pool_pool` pool_id (%i) includes itself, dead-lock detected, skipped.", child_pool_id);
714  continue;
715  }
716  if (chance < 0 || chance > 100)
717  {
718  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);
719  continue;
720  }
721  PoolTemplateData* pPoolTemplateMother = &mPoolTemplate[mother_pool_id];
722 
723  ++count;
724 
725  PoolObject plObject = PoolObject(child_pool_id, chance);
726  PoolGroup<Pool>& plgroup = mPoolPoolGroups[mother_pool_id];
727  plgroup.SetPoolId(mother_pool_id);
728  plgroup.AddEntry(plObject, pPoolTemplateMother->MaxLimit);
729  SearchPair p(child_pool_id, mother_pool_id);
730  mPoolSearchMap.insert(p);
731 
732  } while (result->NextRow());
733 
734  // Now check for circular reference
735  for (uint32 i = 0; i < max_pool_id; ++i)
736  {
737  std::set<uint32> checkedPools;
738  for (SearchMap::iterator poolItr = mPoolSearchMap.find(i); poolItr != mPoolSearchMap.end(); poolItr = mPoolSearchMap.find(poolItr->second))
739  {
740  checkedPools.insert(poolItr->first);
741  if (checkedPools.find(poolItr->second) != checkedPools.end())
742  {
743  std::ostringstream ss;
744  ss << "The pool(s) ";
745  for (std::set<uint32>::const_iterator itr = checkedPools.begin(); itr != checkedPools.end(); ++itr)
746  ss << *itr << " ";
747  ss << "create(s) a circular reference, which can cause the server to freeze.\nRemoving the last link between mother pool "
748  << poolItr->first << " and child pool " << poolItr->second;
749  sLog.outErrorDb("%s", ss.str().c_str());
750  mPoolPoolGroups[poolItr->second].RemoveOneRelation(poolItr->first);
751  mPoolSearchMap.erase(poolItr);
752  --count;
753  break;
754  }
755  }
756  }
757 
758  sLog.outString(">> Loaded %u pools in mother pools", count);
759  }
760 }
761 
763 {
764  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, pool_entry FROM pool_quest");
765 
766  mQuestSearchMap.clear();
767  mPoolQuestGroups.resize(max_pool_id + 1);
768 
769  uint32 count = 0;
770  if (!result)
771  {
772  sLog.outString(">> Loaded 0 quests in pools");
773  return;
774  }
775 
776  PooledQuestRelationBounds creBounds;
777  PooledQuestRelationBounds goBounds;
778 
779  enum eQuestTypes
780  {
781  QUEST_NONE = 0,
782  QUEST_DAILY = 1,
783  };
784 
785  std::map<uint32, int32> poolTypeMap;
786 
787  do
788  {
789  Field* fields = result->Fetch();
790 
791 
792  uint32 entry = fields[0].GetUInt32();
793  uint32 pool_id = fields[1].GetUInt32();
794 
795  Quest const* pQuest = sObjectMgr.GetQuestTemplate(entry);
796  if (!pQuest)
797  {
798  sLog.outErrorDb("`pool_quest` has a non existing quest template (Entry: %u) defined for pool id (%u), skipped.", entry, pool_id);
799  continue;
800  }
801 
802  if (pool_id > max_pool_id)
803  {
804  sLog.outErrorDb("`pool_quest` pool id (%u) is out of range compared to max pool id in `pool_template`, skipped.", pool_id);
805  continue;
806  }
807 
808  if (!pQuest->IsDaily())
809  {
810  sLog.outErrorDb("`pool_quest` has an quest (%u) which is not daily or weekly in pool id (%u), use ExclusiveGroup instead, skipped.", entry, pool_id);
811  continue;
812  }
813 
814  if (poolTypeMap[pool_id] == QUEST_NONE)
815  poolTypeMap[pool_id] = pQuest->IsDaily() ? QUEST_DAILY : QUEST_NONE;
816 
817  int32 currType = pQuest->IsDaily() ? QUEST_DAILY : QUEST_NONE;
818 
819  if (poolTypeMap[pool_id] != currType)
820  {
821  sLog.outErrorDb("`pool_quest` quest %u is %s but pool (%u) is specified for %s, mixing not allowed, skipped.", entry, currType == QUEST_DAILY ? "QUEST_DAILY" : "QUEST_NONE", pool_id, poolTypeMap[pool_id] == QUEST_DAILY ? "QUEST_DAILY" : "QUEST_NONE");
822  continue;
823  }
824 
825  creBounds = mQuestCreatureRelation.equal_range(entry);
826  goBounds = mQuestGORelation.equal_range(entry);
827 
828  if (creBounds.first == creBounds.second && goBounds.first == goBounds.second)
829  {
830  sLog.outErrorDb("`pool_quest` lists entry (%u) as member of pool (%u) but is not started anywhere, skipped.", entry, pool_id);
831  continue;
832  }
833 
834 
835  PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id];
836  ++count;
837 
838  PoolObject plObject = PoolObject(entry, 0.0f);
839  PoolGroup<Quest>& questgroup = mPoolQuestGroups[pool_id];
840  questgroup.SetPoolId(pool_id);
841  questgroup.AddEntry(plObject, pPoolTemplate->MaxLimit);
842  SearchPair p(entry, pool_id);
843  mQuestSearchMap.insert(p);
844 
845  } while (result->NextRow());
846 
847  sLog.outString(">> Loaded %u quests in pools", count);
848 }
849 
850 // 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
852 {
853  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT DISTINCT pool_template.entry, pool_pool.pool_id, pool_pool.mother_pool 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");
854  uint32 count = 0;
855  if (result)
856  {
857  do
858  {
859  Field* fields = result->Fetch();
860  uint32 pool_entry = fields[0].GetUInt32();
861  uint32 pool_pool_id = fields[1].GetUInt32();
862  if (!CheckPool(pool_entry))
863  {
864  if (pool_pool_id)
865  // The pool is a child pool in pool_pool table. Ideally we should remove it from the pool handler to ensure it never gets spawned,
866  // however that could recursively invalidate entire chain of mother pools. It can be done in the future but for now we'll do nothing.
867  //sLog.outErrorDb("Pool Id %u has no equal chance pooled entites defined and explicit chance sum is not 100. This broken pool is a child pool of Id %u and cannot be safely removed.", pool_entry, fields[2].GetUInt32());
868  //else
869  //sLog.outErrorDb("Pool Id %u has no equal chance pooled entites defined and explicit chance sum is not 100. The pool will not be spawned.", pool_entry);
870  continue;
871  }
872 
873  // Don't spawn child pools, they are spawned recursively by their parent pools
874  if (!pool_pool_id)
875  {
876  SpawnPool(pool_entry);
877  count++;
878  }
879  }
880  while (result->NextRow());
881  }
882 
883  sLog.outBasic("Pool handling system initialized, %u pools spawned.", count);
884 }
885 
887 {
889  std::ostringstream query;
890  query << "DELETE FROM pool_quest_save WHERE pool_id IN (";
891  bool first = true;
892  for (PoolGroupQuestMap::iterator itr = mPoolQuestGroups.begin(); itr != mPoolQuestGroups.end(); ++itr)
893  {
894  if (itr->GetPoolId() != 0)
895  {
896  if (!first)
897  query << ",";
898  first = false;
899  query << itr->GetPoolId();
900  }
901  }
902  if (!first)
903  {
904  query << ")";
905  CharacterDatabase.PExecute("%s", query.str().c_str());
906  }
907 
908  first = true;
909  query.rdbuf()->str("");
910  query << "INSERT INTO pool_quest_save (pool_id, quest_id) VALUES ";
911  for (SearchMap::iterator itr = mQuestSearchMap.begin(); itr != mQuestSearchMap.end(); ++itr)
912  {
913  if (IsSpawnedObject<Quest>(itr->first))
914  {
915  if (!first)
916  query << ",";
917  first = false;
918  query << "(" << itr->second << "," << itr->first << ")";
919  }
920  }
921 
922  if (!first)
923  CharacterDatabase.PExecute("%s", query.str().c_str());
924 
926 }
927 
929 {
930  for (PoolGroupQuestMap::iterator itr = mPoolQuestGroups.begin(); itr != mPoolQuestGroups.end(); ++itr)
931  {
932  if (sObjectMgr.GetQuestTemplate(itr->GetFirstEqualChancedObjectId()))
933  {
934  UpdatePool<Quest>(itr->GetPoolId(), 1); // anything non-zero means don't load from db
935  }
936  }
937 
938  SaveQuestsToDB();
939 }
940 
941 // Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
942 // If it's same, the creature is respawned only (added back to map)
943 template<>
944 void PoolMgr::SpawnPool<Creature>(uint32 pool_id, uint32 db_guid)
945 {
946  if (!mPoolCreatureGroups[pool_id].isEmpty())
947  mPoolCreatureGroups[pool_id].SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, db_guid);
948 }
949 
950 // Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
951 // If it's same, the gameobject is respawned only (added back to map)
952 template<>
953 void PoolMgr::SpawnPool<GameObject>(uint32 pool_id, uint32 db_guid)
954 {
955  if (!mPoolGameobjectGroups[pool_id].isEmpty())
956  mPoolGameobjectGroups[pool_id].SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, db_guid);
957 }
958 
959 // Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
960 // If it's same, the pool is respawned only
961 template<>
962 void PoolMgr::SpawnPool<Pool>(uint32 pool_id, uint32 sub_pool_id)
963 {
964  if (!mPoolPoolGroups[pool_id].isEmpty())
965  mPoolPoolGroups[pool_id].SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, sub_pool_id);
966 }
967 
968 // Call to spawn a pool
969 template<>
970 void PoolMgr::SpawnPool<Quest>(uint32 pool_id, uint32 quest_id)
971 {
972  if (!mPoolQuestGroups[pool_id].isEmpty())
973  mPoolQuestGroups[pool_id].SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, quest_id);
974 }
975 
977 {
978  SpawnPool<Pool>(pool_id, 0);
979  SpawnPool<GameObject>(pool_id, 0);
980  SpawnPool<Creature>(pool_id, 0);
981  SpawnPool<Quest>(pool_id, 0);
982 }
983 
984 // Call to despawn a pool, all gameobjects/creatures in this pool are removed
986 {
987  if (!mPoolCreatureGroups[pool_id].isEmpty())
988  mPoolCreatureGroups[pool_id].DespawnObject(mSpawnedData);
989 
990  if (!mPoolGameobjectGroups[pool_id].isEmpty())
991  mPoolGameobjectGroups[pool_id].DespawnObject(mSpawnedData);
992 
993  if (!mPoolPoolGroups[pool_id].isEmpty())
994  mPoolPoolGroups[pool_id].DespawnObject(mSpawnedData);
995 
996  if (!mPoolQuestGroups[pool_id].isEmpty())
997  mPoolQuestGroups[pool_id].DespawnObject(mSpawnedData);
998 }
999 
1000 // Method that check chance integrity of the creatures and gameobjects in this pool
1001 bool PoolMgr::CheckPool(uint32 pool_id) const
1002 {
1003  return pool_id <= max_pool_id &&
1004  mPoolGameobjectGroups[pool_id].CheckPool() &&
1005  mPoolCreatureGroups[pool_id].CheckPool() &&
1006  mPoolPoolGroups[pool_id].CheckPool() &&
1007  mPoolQuestGroups[pool_id].CheckPool();
1008 }
1009 
1010 // Call to update the pool when a gameobject/creature part of pool [pool_id] is ready to respawn
1011 // Here we cache only the creature/gameobject whose guid is passed as parameter
1012 // Then the spawn pool call will use this cache to decide
1013 template<typename T>
1014 void PoolMgr::UpdatePool(uint32 pool_id, uint32 db_guid_or_pool_id)
1015 {
1016  if (uint32 motherpoolid = IsPartOfAPool<Pool>(pool_id))
1017  SpawnPool<Pool>(motherpoolid, pool_id);
1018  else
1019  SpawnPool<T>(pool_id, db_guid_or_pool_id);
1020 }
1021 
1022 template void PoolMgr::UpdatePool<Pool>(uint32 pool_id, uint32 db_guid_or_pool_id);
1023 template void PoolMgr::UpdatePool<GameObject>(uint32 pool_id, uint32 db_guid_or_pool_id);
1024 template void PoolMgr::UpdatePool<Creature>(uint32 pool_id, uint32 db_guid_or_pool_id);
1025 template void PoolMgr::UpdatePool<Quest>(uint32 pool_id, uint32 db_guid_or_pool_id);
void ReSpawn1Object(PoolObject *obj)
bool AddToMap(T *)
Definition: Map.cpp:424
bool LoadCreatureFromDB(uint32 guid, Map *map, bool addToMap=true)
Definition: Creature.cpp:1300
void Despawn1Object(uint32 guid)
DatabaseType WorldDatabase
Accessor to the world database.
Definition: Main.cpp:53
void Initialize()
Definition: PoolMgr.cpp:851
bool BeginTransaction()
Definition: Database.cpp:533
std::pair< PooledQuestRelation::iterator, PooledQuestRelation::iterator > PooledQuestRelationBoundsNC
Definition: PoolMgr.h:102
QueryResult_AutoPtr PQuery(const char *format,...) ATTR_PRINTF(2
Definition: Database.cpp:400
Definition: Field.h:24
bool IsActiveObject(uint32 db_guid_or_pool_id) const
#define sLog
Log class singleton.
Definition: Log.h:187
void SpawnObject(ActivePoolData &spawns, uint32 limit, uint32 triggerFrom)
Definition: PoolMgr.cpp:486
ACE_INT32 int32
Definition: Define.h:67
QueryResult_AutoPtr Query(const char *sql)
Definition: Database.cpp:383
bool IsGridLoaded(float x, float y) const
Definition: Map.h:307
std::pair< uint32, uint32 > SearchPair
Definition: PoolMgr.h:145
bool IsDaily() const
Definition: QuestDef.h:378
#define sObjectMgr
Definition: ObjectMgr.h:1285
bool CommitTransaction()
Definition: Database.cpp:551
Map const * CreateBaseMap(uint32 id) const
Definition: MapManager.h:41
void LoadQuestPools()
Definition: PoolMgr.cpp:762
void SaveQuestsToDB()
Definition: PoolMgr.cpp:886
void UpdatePool(uint32 pool_id, uint32 db_guid_or_pool_id)
Definition: PoolMgr.cpp:1014
ActivePoolObjects mSpawnedCreatures
Definition: PoolMgr.h:62
std::multimap< uint32, uint32 > QuestRelations
Definition: ObjectMgr.h:390
#define sPoolMgr
Definition: PoolMgr.h:162
void RemoveObject(uint32 db_guid_or_pool_id, uint32 pool_id)
void Spawn1Object(PoolObject *obj)
INSTANTIATE_SINGLETON_1(PoolMgr)
#define MAKE_NEW_GUID(l, e, h)
Definition: ObjectGuid.h:80
void RemoveOneRelation(uint32 child_pool_id)
void AddEntry(PoolObject &poolitem, uint32 maxentries)
Definition: PoolMgr.cpp:132
void ChangeDailyQuests()
Definition: PoolMgr.cpp:928
PoolObject * RollOne(ActivePoolData &spawns, uint32 triggerFrom)
Definition: PoolMgr.cpp:156
bool PExecute(const char *format,...) ATTR_PRINTF(2
Definition: Database.cpp:441
void DespawnObject(ActivePoolData &spawns, uint32 guid=0)
Definition: PoolMgr.cpp:187
ActivePoolPools mSpawnedPools
Definition: PoolMgr.h:65
Definition: Map.h:266
PoolMgr()
Definition: PoolMgr.cpp:528
ActivePoolObjects mSpawnedGameobjects
Definition: PoolMgr.h:63
bool CheckPool() const
Definition: PoolMgr.cpp:142
uint32 GetActiveObjectCount(uint32 pool_id) const
Definition: PoolMgr.cpp:29
ActivePoolObjects mActiveQuests
Definition: PoolMgr.h:64
void ActivateObject(uint32 db_guid_or_pool_id, uint32 pool_id)
ACE_Refcounted_Auto_Ptr< QueryResult, ACE_Null_Mutex > QueryResult_AutoPtr
Definition: QueryResult.h:113
double rand_chance()
Definition: Util.cpp:53
std::pair< PooledQuestRelation::const_iterator, PooledQuestRelation::const_iterator > PooledQuestRelationBounds
Definition: PoolMgr.h:101
uint32 MaxLimit
Definition: PoolMgr.h:29
bool Instanceable() const
Definition: Map.h:422
void SpawnPool(uint32 pool_id)
Definition: PoolMgr.cpp:976
float chance
Definition: PoolMgr.h:35
void DespawnPool(uint32 pool_id)
Definition: PoolMgr.cpp:985
DatabaseType CharacterDatabase
Accessor to the character database.
Definition: Main.cpp:54
ACE_UINT32 uint32
Definition: Define.h:71
bool isSpawnedByDefault() const
Definition: GameObject.h:705
ActivePoolObjects GetActiveQuests() const
Definition: PoolMgr.h:60
void SetPoolId(uint32 pool_id)
Definition: PoolMgr.h:74
uint32 guid
Definition: PoolMgr.h:34
bool CheckPool(uint32 pool_id) const
Definition: PoolMgr.cpp:1001
C::value_type const & SelectRandomContainerElement(C const &container)
Definition: Util.h:352
std::set< uint32 > ActivePoolObjects
Definition: PoolMgr.h:43
void LoadFromDB()
Definition: PoolMgr.cpp:530
bool LoadGameObjectFromDB(uint32 guid, Map *map, bool addToMap=true)
Definition: GameObject.cpp:664
uint32 urand(uint32 min, uint32 max)
Definition: Util.cpp:33