[7123] Cleanup in using STD's containers erase method.
[getmangos.git] / src / game / GameEvent.cpp
blob3ec42bb652863589a2e96fb8db78ea046aede36f
1 /*
2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
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 "GameEvent.h"
20 #include "World.h"
21 #include "ObjectMgr.h"
22 #include "ProgressBar.h"
23 #include "Language.h"
24 #include "Log.h"
25 #include "MapManager.h"
26 #include "Policies/SingletonImp.h"
28 INSTANTIATE_SINGLETON_1(GameEvent);
30 bool GameEvent::CheckOneGameEvent(uint16 entry) const
32 // Get the event information
33 time_t currenttime = time(NULL);
34 if( mGameEvent[entry].start < currenttime && currenttime < mGameEvent[entry].end &&
35 ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE)) < (mGameEvent[entry].length * MINUTE) )
36 return true;
37 else
38 return false;
41 uint32 GameEvent::NextCheck(uint16 entry) const
43 time_t currenttime = time(NULL);
45 // outdated event: we return max
46 if (currenttime > mGameEvent[entry].end)
47 return max_ge_check_delay;
49 // never started event, we return delay before start
50 if (mGameEvent[entry].start > currenttime)
51 return (mGameEvent[entry].start - currenttime);
53 uint32 delay;
54 // in event, we return the end of it
55 if ((((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * 60)) < (mGameEvent[entry].length * 60)))
56 // we return the delay before it ends
57 delay = (mGameEvent[entry].length * MINUTE) - ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE));
58 else // not in window, we return the delay before next start
59 delay = (mGameEvent[entry].occurence * MINUTE) - ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE));
60 // In case the end is before next check
61 if (mGameEvent[entry].end < time_t(currenttime + delay))
62 return (mGameEvent[entry].end - currenttime);
63 else
64 return delay;
67 void GameEvent::StartEvent( uint16 event_id, bool overwrite )
69 AddActiveEvent(event_id);
70 ApplyNewEvent(event_id);
71 if(overwrite)
73 mGameEvent[event_id].start = time(NULL);
74 if(mGameEvent[event_id].end <= mGameEvent[event_id].start)
75 mGameEvent[event_id].end = mGameEvent[event_id].start+mGameEvent[event_id].length;
79 void GameEvent::StopEvent( uint16 event_id, bool overwrite )
81 RemoveActiveEvent(event_id);
82 UnApplyEvent(event_id);
83 if(overwrite)
85 mGameEvent[event_id].start = time(NULL) - mGameEvent[event_id].length * MINUTE;
86 if(mGameEvent[event_id].end <= mGameEvent[event_id].start)
87 mGameEvent[event_id].end = mGameEvent[event_id].start+mGameEvent[event_id].length;
91 void GameEvent::LoadFromDB()
94 QueryResult *result = WorldDatabase.Query("SELECT MAX(entry) FROM game_event");
95 if( !result )
97 sLog.outString(">> Table game_event is empty.");
98 sLog.outString();
99 return;
102 Field *fields = result->Fetch();
104 uint32 max_event_id = fields[0].GetUInt16();
105 delete result;
107 mGameEvent.resize(max_event_id+1);
110 QueryResult *result = WorldDatabase.Query("SELECT entry,UNIX_TIMESTAMP(start_time),UNIX_TIMESTAMP(end_time),occurence,length,description FROM game_event");
111 if( !result )
113 mGameEvent.clear();
114 sLog.outString(">> Table game_event is empty:");
115 sLog.outString();
116 return;
119 uint32 count = 0;
121 barGoLink bar( result->GetRowCount() );
124 ++count;
125 Field *fields = result->Fetch();
127 bar.step();
129 uint16 event_id = fields[0].GetUInt16();
130 if(event_id==0)
132 sLog.outErrorDb("`game_event` game event id (%i) is reserved and can't be used.",event_id);
133 continue;
136 GameEventData& pGameEvent = mGameEvent[event_id];
137 uint64 starttime = fields[1].GetUInt64();
138 pGameEvent.start = time_t(starttime);
139 uint64 endtime = fields[2].GetUInt64();
140 pGameEvent.end = time_t(endtime);
141 pGameEvent.occurence = fields[3].GetUInt32();
142 pGameEvent.length = fields[4].GetUInt32();
144 if(pGameEvent.length==0) // length>0 is validity check
146 sLog.outErrorDb("`game_event` game event id (%i) have length 0 and can't be used.",event_id);
147 continue;
150 pGameEvent.description = fields[5].GetCppString();
152 } while( result->NextRow() );
154 sLog.outString();
155 sLog.outString( ">> Loaded %u game events", count );
156 delete result;
158 mGameEventCreatureGuids.resize(mGameEvent.size()*2-1);
159 // 1 2
160 result = WorldDatabase.Query("SELECT creature.guid, game_event_creature.event "
161 "FROM creature JOIN game_event_creature ON creature.guid = game_event_creature.guid");
163 count = 0;
164 if( !result )
166 barGoLink bar2(1);
167 bar2.step();
169 sLog.outString();
170 sLog.outString(">> Loaded %u creatures in game events", count );
172 else
175 barGoLink bar2( result->GetRowCount() );
178 Field *fields = result->Fetch();
180 bar2.step();
182 uint32 guid = fields[0].GetUInt32();
183 int16 event_id = fields[1].GetInt16();
185 int32 internal_event_id = mGameEvent.size() + event_id - 1;
187 if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size())
189 sLog.outErrorDb("`game_event_creature` game event id (%i) is out of range compared to max event id in `game_event`",event_id);
190 continue;
193 ++count;
194 GuidList& crelist = mGameEventCreatureGuids[internal_event_id];
195 crelist.push_back(guid);
197 } while( result->NextRow() );
198 sLog.outString();
199 sLog.outString( ">> Loaded %u creatures in game events", count );
200 delete result;
203 mGameEventGameobjectGuids.resize(mGameEvent.size()*2-1);
204 // 1 2
205 result = WorldDatabase.Query("SELECT gameobject.guid, game_event_gameobject.event "
206 "FROM gameobject JOIN game_event_gameobject ON gameobject.guid=game_event_gameobject.guid");
208 count = 0;
209 if( !result )
211 barGoLink bar3(1);
212 bar3.step();
214 sLog.outString();
215 sLog.outString(">> Loaded %u gameobjects in game events", count );
217 else
220 barGoLink bar3( result->GetRowCount() );
223 Field *fields = result->Fetch();
225 bar3.step();
227 uint32 guid = fields[0].GetUInt32();
228 int16 event_id = fields[1].GetInt16();
230 int32 internal_event_id = mGameEvent.size() + event_id - 1;
232 if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size())
234 sLog.outErrorDb("`game_event_gameobject` game event id (%i) is out of range compared to max event id in `game_event`",event_id);
235 continue;
238 ++count;
239 GuidList& golist = mGameEventGameobjectGuids[internal_event_id];
240 golist.push_back(guid);
242 } while( result->NextRow() );
243 sLog.outString();
244 sLog.outString( ">> Loaded %u gameobjects in game events", count );
246 delete result;
249 mGameEventModelEquip.resize(mGameEvent.size());
250 // 0 1 2
251 result = WorldDatabase.Query("SELECT creature.guid, game_event_model_equip.event, game_event_model_equip.modelid,"
252 // 3
253 "game_event_model_equip.equipment_id "
254 "FROM creature JOIN game_event_model_equip ON creature.guid=game_event_model_equip.guid");
256 count = 0;
257 if( !result )
259 barGoLink bar3(1);
260 bar3.step();
262 sLog.outString();
263 sLog.outString(">> Loaded %u model/equipment changes in game events", count );
265 else
268 barGoLink bar3( result->GetRowCount() );
271 Field *fields = result->Fetch();
273 bar3.step();
274 uint32 guid = fields[0].GetUInt32();
275 uint16 event_id = fields[1].GetUInt16();
277 if(event_id >= mGameEventModelEquip.size())
279 sLog.outErrorDb("`game_event_model_equip` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
280 continue;
283 ++count;
284 ModelEquipList& equiplist = mGameEventModelEquip[event_id];
285 ModelEquip newModelEquipSet;
286 newModelEquipSet.modelid = fields[2].GetUInt32();
287 newModelEquipSet.equipment_id = fields[3].GetUInt32();
288 newModelEquipSet.equipement_id_prev = 0;
289 newModelEquipSet.modelid_prev = 0;
291 if(newModelEquipSet.equipment_id > 0)
293 if(!objmgr.GetEquipmentInfo(newModelEquipSet.equipment_id))
295 sLog.outErrorDb("Table `game_event_model_equip` have creature (Guid: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", guid, newModelEquipSet.equipment_id);
296 continue;
300 equiplist.push_back(std::pair<uint32, ModelEquip>(guid, newModelEquipSet));
302 } while( result->NextRow() );
303 sLog.outString();
304 sLog.outString( ">> Loaded %u model/equipment changes in game events", count );
306 delete result;
309 mGameEventQuests.resize(mGameEvent.size());
310 // 0 1 2
311 result = WorldDatabase.Query("SELECT id, quest, event FROM game_event_creature_quest");
313 count = 0;
314 if( !result )
316 barGoLink bar3(1);
317 bar3.step();
319 sLog.outString();
320 sLog.outString(">> Loaded %u quests additions in game events", count );
322 else
325 barGoLink bar3( result->GetRowCount() );
328 Field *fields = result->Fetch();
330 bar3.step();
331 uint32 id = fields[0].GetUInt32();
332 uint32 quest = fields[1].GetUInt32();
333 uint16 event_id = fields[2].GetUInt16();
335 if(event_id >= mGameEventQuests.size())
337 sLog.outErrorDb("`game_event_creature_quest` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
338 continue;
341 ++count;
342 QuestRelList& questlist = mGameEventQuests[event_id];
343 questlist.push_back(QuestRelation(id, quest));
345 } while( result->NextRow() );
346 sLog.outString();
347 sLog.outString( ">> Loaded %u quests additions in game events", count );
349 delete result;
353 uint32 GameEvent::Initialize() // return the next event delay in ms
355 m_ActiveEvents.clear();
356 uint32 delay = Update();
357 sLog.outBasic("Game Event system initialized." );
358 isSystemInit = true;
359 return delay;
362 uint32 GameEvent::Update() // return the next event delay in ms
364 uint32 nextEventDelay = max_ge_check_delay; // 1 day
365 uint32 calcDelay;
366 for (uint16 itr = 1; itr < mGameEvent.size(); ++itr)
368 //sLog.outErrorDb("Checking event %u",itr);
369 if (CheckOneGameEvent(itr))
371 //sLog.outDebug("GameEvent %u is active",itr->first);
372 if (!IsActiveEvent(itr))
373 StartEvent(itr);
375 else
377 //sLog.outDebug("GameEvent %u is not active",itr->first);
378 if (IsActiveEvent(itr))
379 StopEvent(itr);
380 else
382 if (!isSystemInit)
384 int16 event_nid = (-1) * (itr);
385 // spawn all negative ones for this event
386 GameEventSpawn(event_nid);
390 calcDelay = NextCheck(itr);
391 if (calcDelay < nextEventDelay)
392 nextEventDelay = calcDelay;
394 sLog.outBasic("Next game event check in %u seconds.", nextEventDelay + 1);
395 return (nextEventDelay + 1) * 1000; // Add 1 second to be sure event has started/stopped at next call
398 void GameEvent::UnApplyEvent(uint16 event_id)
400 sLog.outString("GameEvent %u \"%s\" removed.", event_id, mGameEvent[event_id].description.c_str());
401 // un-spawn positive event tagged objects
402 GameEventUnspawn(event_id);
403 // spawn negative event tagget objects
404 int16 event_nid = (-1) * event_id;
405 GameEventSpawn(event_nid);
406 // restore equipment or model
407 ChangeEquipOrModel(event_id, false);
408 // Remove quests that are events only to non event npc
409 UpdateEventQuests(event_id, false);
412 void GameEvent::ApplyNewEvent(uint16 event_id)
414 switch(sWorld.getConfig(CONFIG_EVENT_ANNOUNCE))
416 case 0: // disable
417 break;
418 case 1: // announce events
419 sWorld.SendWorldText(LANG_EVENTMESSAGE, mGameEvent[event_id].description.c_str());
420 break;
423 sLog.outString("GameEvent %u \"%s\" started.", event_id, mGameEvent[event_id].description.c_str());
424 // spawn positive event tagget objects
425 GameEventSpawn(event_id);
426 // un-spawn negative event tagged objects
427 int16 event_nid = (-1) * event_id;
428 GameEventUnspawn(event_nid);
429 // Change equipement or model
430 ChangeEquipOrModel(event_id, true);
431 // Add quests that are events only to non event npc
432 UpdateEventQuests(event_id, true);
435 void GameEvent::GameEventSpawn(int16 event_id)
437 int32 internal_event_id = mGameEvent.size() + event_id - 1;
439 if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size())
441 sLog.outError("GameEvent::GameEventSpawn attempt access to out of range mGameEventCreatureGuids element %i (size: %u)",internal_event_id,mGameEventCreatureGuids.size());
442 return;
445 for (GuidList::iterator itr = mGameEventCreatureGuids[internal_event_id].begin();itr != mGameEventCreatureGuids[internal_event_id].end();++itr)
447 // Add to correct cell
448 CreatureData const* data = objmgr.GetCreatureData(*itr);
449 if (data)
451 objmgr.AddCreatureToGrid(*itr, data);
453 // Spawn if necessary (loaded grids only)
454 Map* map = const_cast<Map*>(MapManager::Instance().GetBaseMap(data->mapid));
455 // We use spawn coords to spawn
456 if(!map->Instanceable() && !map->IsRemovalGrid(data->posX,data->posY))
458 Creature* pCreature = new Creature;
459 //sLog.outDebug("Spawning creature %u",*itr);
460 if (!pCreature->LoadFromDB(*itr, map))
462 delete pCreature;
464 else
466 map->Add(pCreature);
472 if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size())
474 sLog.outError("GameEvent::GameEventSpawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %u)",internal_event_id,mGameEventGameobjectGuids.size());
475 return;
478 for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin();itr != mGameEventGameobjectGuids[internal_event_id].end();++itr)
480 // Add to correct cell
481 GameObjectData const* data = objmgr.GetGOData(*itr);
482 if (data)
484 objmgr.AddGameobjectToGrid(*itr, data);
485 // Spawn if necessary (loaded grids only)
486 // this base map checked as non-instanced and then only existed
487 Map* map = const_cast<Map*>(MapManager::Instance().GetBaseMap(data->mapid));
488 // We use current coords to unspawn, not spawn coords since creature can have changed grid
489 if(!map->Instanceable() && !map->IsRemovalGrid(data->posX, data->posY))
491 GameObject* pGameobject = new GameObject;
492 //sLog.outDebug("Spawning gameobject %u", *itr);
493 if (!pGameobject->LoadFromDB(*itr, map))
495 delete pGameobject;
497 else
499 if(pGameobject->isSpawnedByDefault())
500 map->Add(pGameobject);
507 void GameEvent::GameEventUnspawn(int16 event_id)
509 int32 internal_event_id = mGameEvent.size() + event_id - 1;
511 if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size())
513 sLog.outError("GameEvent::GameEventUnspawn attempt access to out of range mGameEventCreatureGuids element %i (size: %u)",internal_event_id,mGameEventCreatureGuids.size());
514 return;
517 for (GuidList::iterator itr = mGameEventCreatureGuids[internal_event_id].begin();itr != mGameEventCreatureGuids[internal_event_id].end();++itr)
519 // Remove the creature from grid
520 if( CreatureData const* data = objmgr.GetCreatureData(*itr) )
522 objmgr.RemoveCreatureFromGrid(*itr, data);
524 if( Creature* pCreature = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(*itr, data->id, HIGHGUID_UNIT), (Creature*)NULL) )
526 pCreature->CleanupsBeforeDelete();
527 pCreature->AddObjectToRemoveList();
532 if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size())
534 sLog.outError("GameEvent::GameEventUnspawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %u)",internal_event_id,mGameEventGameobjectGuids.size());
535 return;
538 for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin();itr != mGameEventGameobjectGuids[internal_event_id].end();++itr)
540 // Remove the gameobject from grid
541 if(GameObjectData const* data = objmgr.GetGOData(*itr))
543 objmgr.RemoveGameobjectFromGrid(*itr, data);
545 if( GameObject* pGameobject = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(*itr, data->id, HIGHGUID_GAMEOBJECT), (GameObject*)NULL) )
546 pGameobject->AddObjectToRemoveList();
551 void GameEvent::ChangeEquipOrModel(int16 event_id, bool activate)
553 for(ModelEquipList::iterator itr = mGameEventModelEquip[event_id].begin();itr != mGameEventModelEquip[event_id].end();++itr)
555 // Remove the creature from grid
556 CreatureData const* data = objmgr.GetCreatureData(itr->first);
557 if(!data)
558 continue;
560 // Update if spawned
561 Creature* pCreature = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(itr->first, data->id,HIGHGUID_UNIT), (Creature*)NULL);
562 if (pCreature)
564 if (activate)
566 itr->second.equipement_id_prev = pCreature->GetCurrentEquipmentId();
567 itr->second.modelid_prev = pCreature->GetDisplayId();
568 pCreature->LoadEquipment(itr->second.equipment_id, true);
569 if (itr->second.modelid >0 && itr->second.modelid_prev != itr->second.modelid)
571 CreatureModelInfo const *minfo = objmgr.GetCreatureModelInfo(itr->second.modelid);
572 if (minfo)
574 pCreature->SetDisplayId(itr->second.modelid);
575 pCreature->SetNativeDisplayId(itr->second.modelid);
576 pCreature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS,minfo->bounding_radius);
577 pCreature->SetFloatValue(UNIT_FIELD_COMBATREACH,minfo->combat_reach );
581 else
583 pCreature->LoadEquipment(itr->second.equipement_id_prev, true);
584 if (itr->second.modelid_prev >0 && itr->second.modelid_prev != itr->second.modelid)
586 CreatureModelInfo const *minfo = objmgr.GetCreatureModelInfo(itr->second.modelid_prev);
587 if (minfo)
589 pCreature->SetDisplayId(itr->second.modelid_prev);
590 pCreature->SetNativeDisplayId(itr->second.modelid_prev);
591 pCreature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS,minfo->bounding_radius);
592 pCreature->SetFloatValue(UNIT_FIELD_COMBATREACH,minfo->combat_reach );
597 else // If not spawned
599 CreatureData const* data = objmgr.GetCreatureData(itr->first);
600 if (data && activate)
602 CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(data->id);
603 uint32 display_id = objmgr.ChooseDisplayId(0,cinfo,data);
604 CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id);
605 if (minfo)
606 display_id = minfo->modelid;
607 if (data->equipmentId == 0)
608 itr->second.equipement_id_prev = cinfo->equipmentId;
609 else if (data->equipmentId != -1)
610 itr->second.equipement_id_prev = data->equipmentId;
611 itr->second.modelid_prev = display_id;
614 // now last step: put in data
615 // just to have write access to it
616 CreatureData& data2 = objmgr.NewOrExistCreatureData(itr->first);
617 if (activate)
619 data2.displayid = itr->second.modelid;
620 data2.equipmentId = itr->second.equipment_id;
622 else
624 data2.displayid = itr->second.modelid_prev;
625 data2.equipmentId = itr->second.equipement_id_prev;
630 void GameEvent::UpdateEventQuests(uint16 event_id, bool Activate)
632 QuestRelList::iterator itr;
633 for (itr = mGameEventQuests[event_id].begin();itr != mGameEventQuests[event_id].end();++itr)
635 QuestRelations &CreatureQuestMap = objmgr.mCreatureQuestRelations;
636 if (Activate) // Add the pair(id,quest) to the multimap
637 CreatureQuestMap.insert(QuestRelations::value_type(itr->first, itr->second));
638 else
639 { // Remove the pair(id,quest) from the multimap
640 QuestRelations::iterator qitr = CreatureQuestMap.find(itr->first);
641 if (qitr == CreatureQuestMap.end())
642 continue;
643 QuestRelations::iterator lastElement = CreatureQuestMap.upper_bound(itr->first);
644 for ( ;qitr != lastElement;++qitr)
646 if (qitr->second == itr->second)
648 CreatureQuestMap.erase(qitr); // iterator is now no more valid
649 break; // but we can exit loop since the element is found
656 GameEvent::GameEvent()
658 isSystemInit = false;