TrinityCore
GameEventMgr.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore 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 "GameEventMgr.h"
19#include "BattlegroundMgr.h"
20#include "Creature.h"
21#include "CreatureAI.h"
22#include "DatabaseEnv.h"
23#include "DB2Stores.h"
24#include "GameObject.h"
25#include "GameObjectAI.h"
26#include "GameTime.h"
27#include "Language.h"
28#include "Log.h"
29#include "Map.h"
30#include "MapManager.h"
31#include "ObjectMgr.h"
32#include "Player.h"
33#include "PoolMgr.h"
34#include "StringConvert.h"
35#include "World.h"
36#include "WorldStateMgr.h"
37
39{
41 return &instance;
42}
43
45{
46 switch (mGameEvent[entry].state)
47 {
48 default:
50 {
51 time_t currenttime = GameTime::GetGameTime();
52 // Get the event information
53 return mGameEvent[entry].start < currenttime
54 && currenttime < mGameEvent[entry].end
55 && (currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE) < mGameEvent[entry].length * MINUTE;
56 }
57 // if the state is conditions or nextphase, then the event should be active
60 return true;
61 // finished world events are inactive
64 return false;
65 // if inactive world event, check the prerequisite events
67 {
68 time_t currenttime = GameTime::GetGameTime();
69 for (std::set<uint16>::const_iterator itr = mGameEvent[entry].prerequisite_events.begin(); itr != mGameEvent[entry].prerequisite_events.end(); ++itr)
70 {
71 if ((mGameEvent[*itr].state != GAMEEVENT_WORLD_NEXTPHASE && mGameEvent[*itr].state != GAMEEVENT_WORLD_FINISHED) || // if prereq not in nextphase or finished state, then can't start this one
72 mGameEvent[*itr].nextstart > currenttime) // if not in nextphase state for long enough, can't start this one
73 return false;
74 }
75 // all prerequisite events are met
76 // but if there are no prerequisites, this can be only activated through gm command
77 return !(mGameEvent[entry].prerequisite_events.empty());
78 }
79 }
80}
81
83{
84 time_t currenttime = GameTime::GetGameTime();
85
86 // for NEXTPHASE state world events, return the delay to start the next event, so the followup event will be checked correctly
87 if ((mGameEvent[entry].state == GAMEEVENT_WORLD_NEXTPHASE || mGameEvent[entry].state == GAMEEVENT_WORLD_FINISHED) && mGameEvent[entry].nextstart >= currenttime)
88 return uint32(mGameEvent[entry].nextstart - currenttime);
89
90 // for CONDITIONS state world events, return the length of the wait period, so if the conditions are met, this check will be called again to set the timer as NEXTPHASE event
91 if (mGameEvent[entry].state == GAMEEVENT_WORLD_CONDITIONS)
92 {
93 if (mGameEvent[entry].length)
94 return mGameEvent[entry].length * 60;
95 else
96 return max_ge_check_delay;
97 }
98
99 // outdated event: we return max
100 if (currenttime > mGameEvent[entry].end)
101 return max_ge_check_delay;
102
103 // never started event, we return delay before start
104 if (mGameEvent[entry].start > currenttime)
105 return uint32(mGameEvent[entry].start - currenttime);
106
107 uint32 delay;
108 // in event, we return the end of it
109 if ((((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * 60)) < (mGameEvent[entry].length * 60)))
110 // we return the delay before it ends
111 delay = (mGameEvent[entry].length * MINUTE) - ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE));
112 else // not in window, we return the delay before next start
113 delay = (mGameEvent[entry].occurence * MINUTE) - ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE));
114 // In case the end is before next check
115 if (mGameEvent[entry].end < time_t(currenttime + delay))
116 return uint32(mGameEvent[entry].end - currenttime);
117 else
118 return delay;
119}
120
122{
123 if (event_id < 1 || event_id >= mGameEvent.size())
124 return;
125
126 if (!mGameEvent[event_id].isValid())
127 return;
128
129 if (m_ActiveEvents.find(event_id) != m_ActiveEvents.end())
130 return;
131
132 StartEvent(event_id);
133}
134
135bool GameEventMgr::StartEvent(uint16 event_id, bool overwrite)
136{
137 GameEventData &data = mGameEvent[event_id];
138 if (data.state == GAMEEVENT_NORMAL || data.state == GAMEEVENT_INTERNAL)
139 {
140 AddActiveEvent(event_id);
141 ApplyNewEvent(event_id);
142 if (overwrite)
143 {
144 mGameEvent[event_id].start = GameTime::GetGameTime();
145 if (data.end <= data.start)
146 data.end = data.start + data.length;
147 }
148 return false;
149 }
150 else
151 {
153 // set to conditions phase
155
156 // add to active events
157 AddActiveEvent(event_id);
158 // add spawns
159 ApplyNewEvent(event_id);
160
161 // check if can go to next state
162 bool conditions_met = CheckOneGameEventConditions(event_id);
163 // save to db
164 SaveWorldEventStateToDB(event_id);
165 // force game event update to set the update timer if conditions were met from a command
166 // this update is needed to possibly start events dependent on the started one
167 // or to scedule another update where the next event will be started
168 if (overwrite && conditions_met)
169 sWorld->ForceGameEventUpdate();
170
171 return conditions_met;
172 }
173}
174
175void GameEventMgr::StopEvent(uint16 event_id, bool overwrite)
176{
177 GameEventData &data = mGameEvent[event_id];
178 bool serverwide_evt = data.state != GAMEEVENT_NORMAL && data.state != GAMEEVENT_INTERNAL;
179
180 RemoveActiveEvent(event_id);
181 UnApplyEvent(event_id);
182
183 if (overwrite && !serverwide_evt)
184 {
185 data.start = GameTime::GetGameTime() - data.length * MINUTE;
186 if (data.end <= data.start)
187 data.end = data.start + data.length;
188 }
189 else if (serverwide_evt)
190 {
191 // if finished world event, then only gm command can stop it
192 if (overwrite || data.state != GAMEEVENT_WORLD_FINISHED)
193 {
194 // reset conditions
195 data.nextstart = 0;
197 GameEventConditionMap::iterator itr;
198 for (itr = data.conditions.begin(); itr != data.conditions.end(); ++itr)
199 itr->second.done = 0;
200
201 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
203 stmt->setUInt8(0, uint8(event_id));
204 trans->Append(stmt);
205
206 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GAME_EVENT_SAVE);
207 stmt->setUInt8(0, uint8(event_id));
208 trans->Append(stmt);
209
210 CharacterDatabase.CommitTransaction(trans);
211 }
212 }
213}
214
216{
217 {
218 uint32 oldMSTime = getMSTime();
219 // 0 1 2 3 4 5 6 7 8 9
220 QueryResult result = WorldDatabase.Query("SELECT eventEntry, UNIX_TIMESTAMP(start_time), UNIX_TIMESTAMP(end_time), occurence, length, holiday, holidayStage, description, world_event, announce FROM game_event");
221 if (!result)
222 {
223 mGameEvent.clear();
224 TC_LOG_INFO("server.loading", ">> Loaded 0 game events. DB table `game_event` is empty.");
225 return;
226 }
227
228 uint32 count = 0;
229 do
230 {
231 Field* fields = result->Fetch();
232
233 uint8 event_id = fields[0].GetUInt8();
234 if (event_id == 0)
235 {
236 TC_LOG_ERROR("sql.sql", "`game_event`: game event entry 0 is reserved and can't be used.");
237 continue;
238 }
239
240 GameEventData& pGameEvent = mGameEvent[event_id];
241 uint64 starttime = fields[1].GetUInt64();
242 pGameEvent.start = time_t(starttime);
243 uint64 endtime = fields[2].GetUInt64();
244 pGameEvent.end = time_t(endtime);
245 pGameEvent.occurence = fields[3].GetUInt64();
246 pGameEvent.length = fields[4].GetUInt64();
247 pGameEvent.holiday_id = HolidayIds(fields[5].GetUInt32());
248 pGameEvent.holidayStage = fields[6].GetUInt8();
249 pGameEvent.description = fields[7].GetString();
250 pGameEvent.state = (GameEventState)(fields[8].GetUInt8());
251 pGameEvent.announce = fields[9].GetUInt8();
252 pGameEvent.nextstart = 0;
253
254 ++count;
255
256 if (pGameEvent.length == 0 && pGameEvent.state == GAMEEVENT_NORMAL) // length>0 is validity check
257 {
258 TC_LOG_ERROR("sql.sql", "`game_event`: game event id ({}) is not a world event and has length = 0, thus cannot be used.", event_id);
259 continue;
260 }
261
262 if (pGameEvent.holiday_id != HOLIDAY_NONE)
263 {
264 if (!sHolidaysStore.LookupEntry(pGameEvent.holiday_id))
265 {
266 TC_LOG_ERROR("sql.sql", "`game_event`: game event id ({}) contains nonexisting holiday id {}.", event_id, pGameEvent.holiday_id);
267 pGameEvent.holiday_id = HOLIDAY_NONE;
268 continue;
269 }
270 if (pGameEvent.holidayStage > MAX_HOLIDAY_DURATIONS)
271 {
272 TC_LOG_ERROR("sql.sql", "`game_event` game event id ({}) has out of range holidayStage {}.", event_id, pGameEvent.holidayStage);
273 pGameEvent.holidayStage = 0;
274 continue;
275 }
276
277 SetHolidayEventTime(pGameEvent);
278 }
279
280 }
281 while (result->NextRow());
282
283 TC_LOG_INFO("server.loading", ">> Loaded {} game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
284
285 }
286
287 TC_LOG_INFO("server.loading", "Loading Game Event Saves Data...");
288 {
289 uint32 oldMSTime = getMSTime();
290
291 // 0 1 2
292 QueryResult result = CharacterDatabase.Query("SELECT eventEntry, state, next_start FROM game_event_save");
293
294 if (!result)
295 TC_LOG_INFO("server.loading", ">> Loaded 0 game event saves in game events. DB table `game_event_save` is empty.");
296 else
297 {
298 uint32 count = 0;
299 do
300 {
301 Field* fields = result->Fetch();
302
303 uint8 event_id = fields[0].GetUInt8();
304
305 if (event_id >= mGameEvent.size())
306 {
307 TC_LOG_ERROR("sql.sql", "`game_event_save`: game event entry ({}) is out of range compared to max event entry in `game_event`.", event_id);
308 continue;
309 }
310
311 if (mGameEvent[event_id].state != GAMEEVENT_NORMAL && mGameEvent[event_id].state != GAMEEVENT_INTERNAL)
312 {
313 mGameEvent[event_id].state = (GameEventState)(fields[1].GetUInt8());
314 mGameEvent[event_id].nextstart = time_t(fields[2].GetUInt32());
315 }
316 else
317 {
318 TC_LOG_ERROR("sql.sql", "game_event_save includes event save for non-worldevent id {}.", event_id);
319 continue;
320 }
321
322 ++count;
323 }
324 while (result->NextRow());
325
326 TC_LOG_INFO("server.loading", ">> Loaded {} game event saves in game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
327
328 }
329 }
330
331 TC_LOG_INFO("server.loading", "Loading Game Event Prerequisite Data...");
332 {
333 uint32 oldMSTime = getMSTime();
334
335 // 0 1
336 QueryResult result = WorldDatabase.Query("SELECT eventEntry, prerequisite_event FROM game_event_prerequisite");
337 if (!result)
338 TC_LOG_INFO("server.loading", ">> Loaded 0 game event prerequisites in game events. DB table `game_event_prerequisite` is empty.");
339 else
340 {
341 uint32 count = 0;
342 do
343 {
344 Field* fields = result->Fetch();
345
346 uint16 event_id = fields[0].GetUInt8();
347
348 if (event_id >= mGameEvent.size())
349 {
350 TC_LOG_ERROR("sql.sql", "`game_event_prerequisite`: game event id ({}) is out of range compared to max event id in `game_event`.", event_id);
351 continue;
352 }
353
354 if (mGameEvent[event_id].state != GAMEEVENT_NORMAL && mGameEvent[event_id].state != GAMEEVENT_INTERNAL)
355 {
356 uint16 prerequisite_event = fields[1].GetUInt32();
357 if (prerequisite_event >= mGameEvent.size())
358 {
359 TC_LOG_ERROR("sql.sql", "`game_event_prerequisite`: game event prerequisite id ({}) is out of range compared to max event id in `game_event`.", prerequisite_event);
360 continue;
361 }
362 mGameEvent[event_id].prerequisite_events.insert(prerequisite_event);
363 }
364 else
365 {
366 TC_LOG_ERROR("sql.sql", "game_event_prerequisiste includes event entry for non-worldevent id {}.", event_id);
367 continue;
368 }
369
370 ++count;
371 }
372 while (result->NextRow());
373
374 TC_LOG_INFO("server.loading", ">> Loaded {} game event prerequisites in game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
375
376 }
377 }
378
379 TC_LOG_INFO("server.loading", "Loading Game Event Creature Data...");
380 {
381 uint32 oldMSTime = getMSTime();
382
383 // 0 1
384 QueryResult result = WorldDatabase.Query("SELECT guid, eventEntry FROM game_event_creature");
385
386 if (!result)
387 TC_LOG_INFO("server.loading", ">> Loaded 0 creatures in game events. DB table `game_event_creature` is empty.");
388 else
389 {
390 uint32 count = 0;
391 do
392 {
393 Field* fields = result->Fetch();
394
395 ObjectGuid::LowType guid = fields[0].GetUInt64();
396 int16 event_id = fields[1].GetInt8();
397
398 int32 internal_event_id = mGameEvent.size() + event_id - 1;
399
400 CreatureData const* data = sObjectMgr->GetCreatureData(guid);
401 if (!data)
402 {
403 TC_LOG_ERROR("sql.sql", "`game_event_creature` contains creature (GUID: {}) not found in `creature` table.", guid);
404 continue;
405 }
406
407 if (internal_event_id < 0 || internal_event_id >= int32(mGameEventCreatureGuids.size()))
408 {
409 TC_LOG_ERROR("sql.sql", "`game_event_creature`: game event id ({}) is out of range compared to max event id in `game_event`.", event_id);
410 continue;
411 }
412
413 // Log error for pooled object, but still spawn it
414 if (data->poolId)
415 TC_LOG_ERROR("sql.sql", "`game_event_creature`: game event id ({}) contains creature ({}) which is part of a pool ({}). This should be spawned in game_event_pool", event_id, guid, data->poolId);
416
417 GuidList& crelist = mGameEventCreatureGuids[internal_event_id];
418 crelist.push_back(guid);
419
420 ++count;
421 }
422 while (result->NextRow());
423
424 TC_LOG_INFO("server.loading", ">> Loaded {} creatures in game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
425
426 }
427 }
428
429 TC_LOG_INFO("server.loading", "Loading Game Event GO Data...");
430 {
431 uint32 oldMSTime = getMSTime();
432
433 // 0 1
434 QueryResult result = WorldDatabase.Query("SELECT guid, eventEntry FROM game_event_gameobject");
435
436 if (!result)
437 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobjects in game events. DB table `game_event_gameobject` is empty.");
438 else
439 {
440 uint32 count = 0;
441 do
442 {
443 Field* fields = result->Fetch();
444
445 ObjectGuid::LowType guid = fields[0].GetUInt64();
446 int16 event_id = fields[1].GetInt8();
447
448 int32 internal_event_id = mGameEvent.size() + event_id - 1;
449
450 GameObjectData const* data = sObjectMgr->GetGameObjectData(guid);
451 if (!data)
452 {
453 TC_LOG_ERROR("sql.sql", "`game_event_gameobject` contains gameobject (GUID: {}) not found in `gameobject` table.", guid);
454 continue;
455 }
456
457 if (internal_event_id < 0 || internal_event_id >= int32(mGameEventGameobjectGuids.size()))
458 {
459 TC_LOG_ERROR("sql.sql", "`game_event_gameobject`: game event id ({}) is out of range compared to max event id in `game_event`.", event_id);
460 continue;
461 }
462
463 // Log error for pooled object, but still spawn it
464 if (data->poolId)
465 TC_LOG_ERROR("sql.sql", "`game_event_gameobject`: game event id ({}) contains game object ({}) which is part of a pool ({}). This should be spawned in game_event_pool", event_id, guid, data->poolId);
466
467 GuidList& golist = mGameEventGameobjectGuids[internal_event_id];
468 golist.push_back(guid);
469
470 ++count;
471 }
472 while (result->NextRow());
473
474 TC_LOG_INFO("server.loading", ">> Loaded {} gameobjects in game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
475 }
476 }
477
478 TC_LOG_INFO("server.loading", "Loading Game Event Model/Equipment Change Data...");
479 {
480 uint32 oldMSTime = getMSTime();
481
482 // 0 1 2 3 4
483 QueryResult result = WorldDatabase.Query("SELECT creature.guid, creature.id, game_event_model_equip.eventEntry, game_event_model_equip.modelid, game_event_model_equip.equipment_id "
484 "FROM creature JOIN game_event_model_equip ON creature.guid = game_event_model_equip.guid");
485
486 if (!result)
487 TC_LOG_INFO("server.loading", ">> Loaded 0 model/equipment changes in game events. DB table `game_event_model_equip` is empty.");
488 else
489 {
490 uint32 count = 0;
491 do
492 {
493 Field* fields = result->Fetch();
494
495 ObjectGuid::LowType guid = fields[0].GetUInt64();
496 uint32 entry = fields[1].GetUInt32();
497 uint16 event_id = fields[2].GetUInt8();
498
499 if (event_id >= mGameEventModelEquip.size())
500 {
501 TC_LOG_ERROR("sql.sql", "`game_event_model_equip`: game event id ({}) is out of range compared to max event id in `game_event`.", event_id);
502 continue;
503 }
504
505 ModelEquipList& equiplist = mGameEventModelEquip[event_id];
506 ModelEquip newModelEquipSet;
507 newModelEquipSet.modelid = fields[3].GetUInt32();
508 newModelEquipSet.equipment_id = fields[4].GetUInt8();
509 newModelEquipSet.equipement_id_prev = 0;
510 newModelEquipSet.modelid_prev = 0;
511
512 if (newModelEquipSet.equipment_id > 0)
513 {
514 int8 equipId = static_cast<int8>(newModelEquipSet.equipment_id);
515 if (!sObjectMgr->GetEquipmentInfo(entry, equipId))
516 {
517 TC_LOG_ERROR("sql.sql", "Table `game_event_model_equip` contains creature (Guid: {}, entry: {}) with equipment_id {} not found in table `creature_equip_template`. Setting entry to no equipment.",
518 guid, entry, newModelEquipSet.equipment_id);
519 continue;
520 }
521 }
522
523 equiplist.push_back(std::pair<ObjectGuid::LowType, ModelEquip>(guid, newModelEquipSet));
524
525 ++count;
526 }
527 while (result->NextRow());
528
529 TC_LOG_INFO("server.loading", ">> Loaded {} model/equipment changes in game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
530 }
531 }
532
533 TC_LOG_INFO("server.loading", "Loading Game Event Quest Data...");
534 {
535 uint32 oldMSTime = getMSTime();
536
537 // 0 1 2
538 QueryResult result = WorldDatabase.Query("SELECT id, quest, eventEntry FROM game_event_creature_quest");
539
540 if (!result)
541 TC_LOG_INFO("server.loading", ">> Loaded 0 quests additions in game events. DB table `game_event_creature_quest` is empty.");
542 else
543 {
544 uint32 count = 0;
545 do
546 {
547 Field* fields = result->Fetch();
548
549 uint32 id = fields[0].GetUInt32();
550 uint32 quest = fields[1].GetUInt32();
551 uint16 event_id = fields[2].GetUInt8();
552
553 if (event_id >= mGameEventCreatureQuests.size())
554 {
555 TC_LOG_ERROR("sql.sql", "`game_event_creature_quest`: game event id ({}) is out of range compared to max event id in `game_event`.", event_id);
556 continue;
557 }
558
559 QuestRelList& questlist = mGameEventCreatureQuests[event_id];
560 questlist.push_back(QuestRelation(id, quest));
561
562 ++count;
563 }
564 while (result->NextRow());
565
566 TC_LOG_INFO("server.loading", ">> Loaded {} quests additions in game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
567 }
568 }
569
570 TC_LOG_INFO("server.loading", "Loading Game Event GO Quest Data...");
571 {
572 uint32 oldMSTime = getMSTime();
573
574 // 0 1 2
575 QueryResult result = WorldDatabase.Query("SELECT id, quest, eventEntry FROM game_event_gameobject_quest");
576
577 if (!result)
578 TC_LOG_INFO("server.loading", ">> Loaded 0 go quests additions in game events. DB table `game_event_gameobject_quest` is empty.");
579 else
580 {
581 uint32 count = 0;
582 do
583 {
584 Field* fields = result->Fetch();
585
586 uint32 id = fields[0].GetUInt32();
587 uint32 quest = fields[1].GetUInt32();
588 uint16 event_id = fields[2].GetUInt8();
589
590 if (event_id >= mGameEventGameObjectQuests.size())
591 {
592 TC_LOG_ERROR("sql.sql", "`game_event_gameobject_quest`: game event id ({}) is out of range compared to max event id in `game_event`.", event_id);
593 continue;
594 }
595
596 QuestRelList& questlist = mGameEventGameObjectQuests[event_id];
597 questlist.push_back(QuestRelation(id, quest));
598
599 ++count;
600 }
601 while (result->NextRow());
602
603 TC_LOG_INFO("server.loading", ">> Loaded {} quests additions in game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
604 }
605 }
606
607 TC_LOG_INFO("server.loading", "Loading Game Event Quest Condition Data...");
608 {
609 uint32 oldMSTime = getMSTime();
610
611 // 0 1 2 3
612 QueryResult result = WorldDatabase.Query("SELECT quest, eventEntry, condition_id, num FROM game_event_quest_condition");
613
614 if (!result)
615 TC_LOG_INFO("server.loading", ">> Loaded 0 quest event conditions in game events. DB table `game_event_quest_condition` is empty.");
616 else
617 {
618 uint32 count = 0;
619 do
620 {
621 Field* fields = result->Fetch();
622
623 uint32 quest = fields[0].GetUInt32();
624 uint16 event_id = fields[1].GetUInt8();
625 uint32 condition = fields[2].GetUInt32();
626 float num = fields[3].GetFloat();
627
628 if (event_id >= mGameEvent.size())
629 {
630 TC_LOG_ERROR("sql.sql", "`game_event_quest_condition`: game event id ({}) is out of range compared to max event id in `game_event`.", event_id);
631 continue;
632 }
633
634 mQuestToEventConditions[quest].event_id = event_id;
635 mQuestToEventConditions[quest].condition = condition;
636 mQuestToEventConditions[quest].num = num;
637
638 ++count;
639 }
640 while (result->NextRow());
641
642 TC_LOG_INFO("server.loading", ">> Loaded {} quest event conditions in game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
643 }
644 }
645
646 TC_LOG_INFO("server.loading", "Loading Game Event Condition Data...");
647 {
648 uint32 oldMSTime = getMSTime();
649
650 // 0 1 2 3 4
651 QueryResult result = WorldDatabase.Query("SELECT eventEntry, condition_id, req_num, max_world_state_field, done_world_state_field FROM game_event_condition");
652
653 if (!result)
654 TC_LOG_INFO("server.loading", ">> Loaded 0 conditions in game events. DB table `game_event_condition` is empty.");
655 else
656 {
657 uint32 count = 0;
658 do
659 {
660 Field* fields = result->Fetch();
661
662 uint16 event_id = fields[0].GetUInt8();
663 uint32 condition = fields[1].GetUInt32();
664
665 if (event_id >= mGameEvent.size())
666 {
667 TC_LOG_ERROR("sql.sql", "`game_event_condition`: game event id ({}) is out of range compared to max event id in `game_event`.", event_id);
668 continue;
669 }
670
671 mGameEvent[event_id].conditions[condition].reqNum = fields[2].GetFloat();
672 mGameEvent[event_id].conditions[condition].done = 0;
673 mGameEvent[event_id].conditions[condition].max_world_state = fields[3].GetUInt16();
674 mGameEvent[event_id].conditions[condition].done_world_state = fields[4].GetUInt16();
675
676 ++count;
677 }
678 while (result->NextRow());
679
680 TC_LOG_INFO("server.loading", ">> Loaded {} conditions in game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
681 }
682 }
683
684 TC_LOG_INFO("server.loading", "Loading Game Event Condition Save Data...");
685 {
686 uint32 oldMSTime = getMSTime();
687
688 // 0 1 2
689 QueryResult result = CharacterDatabase.Query("SELECT eventEntry, condition_id, done FROM game_event_condition_save");
690
691 if (!result)
692 TC_LOG_INFO("server.loading", ">> Loaded 0 condition saves in game events. DB table `game_event_condition_save` is empty.");
693 else
694 {
695 uint32 count = 0;
696 do
697 {
698 Field* fields = result->Fetch();
699
700 uint16 event_id = fields[0].GetUInt8();
701 uint32 condition = fields[1].GetUInt32();
702
703 if (event_id >= mGameEvent.size())
704 {
705 TC_LOG_ERROR("sql.sql", "`game_event_condition_save`: game event id ({}) is out of range compared to max event id in `game_event`.", event_id);
706 continue;
707 }
708
709 GameEventConditionMap::iterator itr = mGameEvent[event_id].conditions.find(condition);
710 if (itr != mGameEvent[event_id].conditions.end())
711 {
712 itr->second.done = fields[2].GetFloat();
713 }
714 else
715 {
716 TC_LOG_ERROR("sql.sql", "game_event_condition_save contains not present condition event id {} condition id {}.", event_id, condition);
717 continue;
718 }
719
720 ++count;
721 }
722 while (result->NextRow());
723
724 TC_LOG_INFO("server.loading", ">> Loaded {} condition saves in game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
725 }
726 }
727
728 TC_LOG_INFO("server.loading", "Loading Game Event NPCflag Data...");
729 {
730 uint32 oldMSTime = getMSTime();
731
732 // 0 1 2
733 QueryResult result = WorldDatabase.Query("SELECT guid, eventEntry, npcflag FROM game_event_npcflag");
734
735 if (!result)
736 TC_LOG_INFO("server.loading", ">> Loaded 0 npcflags in game events. DB table `game_event_npcflag` is empty.");
737 else
738 {
739 uint32 count = 0;
740 do
741 {
742 Field* fields = result->Fetch();
743
744 ObjectGuid::LowType guid = fields[0].GetUInt64();
745 uint16 event_id = fields[1].GetUInt8();
746 uint64 npcflag = fields[2].GetUInt64();
747
748 if (event_id >= mGameEvent.size())
749 {
750 TC_LOG_ERROR("sql.sql", "`game_event_npcflag`: game event id ({}) is out of range compared to max event id in `game_event`.", event_id);
751 continue;
752 }
753
754 mGameEventNPCFlags[event_id].push_back(GuidNPCFlagPair(guid, npcflag));
755
756 ++count;
757 }
758 while (result->NextRow());
759
760 TC_LOG_INFO("server.loading", ">> Loaded {} npcflags in game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
761 }
762 }
763
764 TC_LOG_INFO("server.loading", "Loading Game Event Seasonal Quest Relations...");
765 {
766 uint32 oldMSTime = getMSTime();
767
768 // 0 1
769 QueryResult result = WorldDatabase.Query("SELECT questId, eventEntry FROM game_event_seasonal_questrelation");
770
771 if (!result)
772 TC_LOG_INFO("server.loading", ">> Loaded 0 seasonal quests additions in game events. DB table `game_event_seasonal_questrelation` is empty.");
773 else
774 {
775 uint32 count = 0;
776 do
777 {
778 Field* fields = result->Fetch();
779
780 uint32 questId = fields[0].GetUInt32();
781 uint32 eventEntry = fields[1].GetUInt32();
782
783 Quest* questTemplate = const_cast<Quest*>(sObjectMgr->GetQuestTemplate(questId));
784 if (!questTemplate)
785 {
786 TC_LOG_ERROR("sql.sql", "`game_event_seasonal_questrelation`: quest id ({}) does not exist in `quest_template`.", questId);
787 continue;
788 }
789
790 if (eventEntry >= mGameEvent.size())
791 {
792 TC_LOG_ERROR("sql.sql", "`game_event_seasonal_questrelation`: event id ({}) is out of range compared to max event in `game_event`.", eventEntry);
793 continue;
794 }
795
796 questTemplate->SetEventIdForQuest(static_cast<uint16>(eventEntry));
797 ++count;
798 }
799 while (result->NextRow());
800
801 TC_LOG_INFO("server.loading", ">> Loaded {} quests additions in game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
802 }
803 }
804
805 TC_LOG_INFO("server.loading", "Loading Game Event Vendor Additions Data...");
806 {
807 uint32 oldMSTime = getMSTime();
808
809 // 0 1 2 3 4 5 6 7 8 9
810 QueryResult result = WorldDatabase.Query("SELECT eventEntry, guid, item, maxcount, incrtime, ExtendedCost, type, BonusListIDs, PlayerConditionId, IgnoreFiltering FROM game_event_npc_vendor ORDER BY guid, slot ASC");
811
812 if (!result)
813 TC_LOG_INFO("server.loading", ">> Loaded 0 vendor additions in game events. DB table `game_event_npc_vendor` is empty.");
814 else
815 {
816 uint32 count = 0;
817 do
818 {
819 Field* fields = result->Fetch();
820
821 uint8 event_id = fields[0].GetUInt8();
822 ObjectGuid::LowType guid = fields[1].GetUInt64();
823
824 if (event_id >= mGameEventVendors.size())
825 {
826 TC_LOG_ERROR("sql.sql", "`game_event_npc_vendor`: game event id ({}) is out of range compared to max event id in `game_event`.", event_id);
827 continue;
828 }
829
830 // get the event npc flag for checking if the npc will be vendor during the event or not
831 uint32 event_npc_flag = 0;
832 NPCFlagList& flist = mGameEventNPCFlags[event_id];
833 for (NPCFlagList::const_iterator itr = flist.begin(); itr != flist.end(); ++itr)
834 {
835 if (itr->first == guid)
836 {
837 event_npc_flag = itr->second;
838 break;
839 }
840 }
841
842 uint32 entry = 0;
843 if (CreatureData const* data = sObjectMgr->GetCreatureData(guid))
844 entry = data->id;
845
846 VendorItem vItem;
847 vItem.item = fields[2].GetUInt32();
848 vItem.maxcount = fields[3].GetUInt32();
849 vItem.incrtime = fields[4].GetUInt32();
850 vItem.ExtendedCost = fields[5].GetUInt32();
851 vItem.Type = fields[6].GetUInt8();
852 vItem.PlayerConditionId = fields[8].GetUInt32();
853 vItem.IgnoreFiltering = fields[9].GetBool();
854
855 for (std::string_view token : Trinity::Tokenize(fields[7].GetStringView(), ' ', false))
856 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(token))
857 vItem.BonusListIDs.push_back(*bonusListID);
858
859 // check validity with event's npcflag
860 if (!sObjectMgr->IsVendorItemValid(entry, vItem, nullptr, nullptr, event_npc_flag))
861 continue;
862
863 NPCVendorMap& vendors = mGameEventVendors[event_id];
864 vendors[entry].emplace_back(std::move(vItem));
865
866 ++count;
867 }
868 while (result->NextRow());
869
870 TC_LOG_INFO("server.loading", ">> Loaded {} vendor additions in game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
871 }
872 }
873
874 TC_LOG_INFO("server.loading", "Loading Game Event Pool Data...");
875 {
876 uint32 oldMSTime = getMSTime();
877
878 // 0 1
879 QueryResult result = WorldDatabase.Query("SELECT pool_template.entry, game_event_pool.eventEntry FROM pool_template"
880 " JOIN game_event_pool ON pool_template.entry = game_event_pool.pool_entry");
881
882 if (!result)
883 TC_LOG_INFO("server.loading", ">> Loaded 0 pools for game events. DB table `game_event_pool` is empty.");
884 else
885 {
886 uint32 count = 0;
887 do
888 {
889 Field* fields = result->Fetch();
890
891 uint32 entry = fields[0].GetUInt32();
892 int16 event_id = fields[1].GetInt8();
893
894 int32 internal_event_id = mGameEvent.size() + event_id - 1;
895
896 if (internal_event_id < 0 || internal_event_id >= int32(mGameEventPoolIds.size()))
897 {
898 TC_LOG_ERROR("sql.sql", "`game_event_pool`: game event id ({}) is out of range compared to max event id in `game_event`.", event_id);
899 continue;
900 }
901
902 if (!sPoolMgr->CheckPool(entry))
903 {
904 TC_LOG_ERROR("sql.sql", "Pool Id ({}) has all creatures or gameobjects with explicit chance sum <> 100 and no equal chance defined. The pool system cannot pick one to spawn.", entry);
905 continue;
906 }
907
908 IdList& poollist = mGameEventPoolIds[internal_event_id];
909 poollist.push_back(entry);
910
911 ++count;
912 }
913 while (result->NextRow());
914
915 TC_LOG_INFO("server.loading", ">> Loaded {} pools for game events in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
916 }
917 }
918}
919
921{
922 uint64 mask = 0;
923 ObjectGuid::LowType guid = cr->GetSpawnId();
924
925 for (ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr)
926 {
927 for (NPCFlagList::iterator itr = mGameEventNPCFlags[*e_itr].begin();
928 itr != mGameEventNPCFlags[*e_itr].end();
929 ++ itr)
930 if (itr->first == guid)
931 mask |= itr->second;
932 }
933
934 return mask;
935}
936
938{
939 QueryResult result = WorldDatabase.Query("SELECT MAX(eventEntry) FROM game_event");
940 if (result)
941 {
942 Field* fields = result->Fetch();
943
944 uint32 maxEventId = fields[0].GetUInt8();
945
946 // Id starts with 1 and vector with 0, thus increment
947 maxEventId++;
948
949 mGameEvent.resize(maxEventId);
950 mGameEventCreatureGuids.resize(maxEventId * 2 - 1);
951 mGameEventGameobjectGuids.resize(maxEventId * 2 - 1);
952 mGameEventCreatureQuests.resize(maxEventId);
953 mGameEventGameObjectQuests.resize(maxEventId);
954 mGameEventVendors.resize(maxEventId);
955 mGameEventPoolIds.resize(maxEventId * 2 - 1);
956 mGameEventNPCFlags.resize(maxEventId);
957 mGameEventModelEquip.resize(maxEventId);
958 }
959}
960
961uint32 GameEventMgr::StartSystem() // return the next event delay in ms
962{
963 m_ActiveEvents.clear();
964 uint32 delay = Update();
965 isSystemInit = true;
966 return delay;
967}
968
970{
971 uint8 season = sWorld->getIntConfig(CONFIG_ARENA_SEASON_ID);
972 QueryResult result = WorldDatabase.PQuery("SELECT eventEntry FROM game_event_arena_seasons WHERE season = '{}'", season);
973
974 if (!result)
975 {
976 TC_LOG_ERROR("gameevent", "ArenaSeason ({}) must be an existing Arena Season.", season);
977 return;
978 }
979
980 Field* fields = result->Fetch();
981 uint16 eventId = fields[0].GetUInt8();
982
983 if (eventId >= mGameEvent.size())
984 {
985 TC_LOG_ERROR("gameevent", "EventEntry {} for ArenaSeason ({}) does not exist.", eventId, season);
986 return;
987 }
988
989 StartEvent(eventId, true);
990 TC_LOG_INFO("gameevent", "Arena Season {} started...", season);
991
992}
993
994uint32 GameEventMgr::Update() // return the next event delay in ms
995{
996 time_t currenttime = GameTime::GetGameTime();
997 uint32 nextEventDelay = max_ge_check_delay; // 1 day
998 uint32 calcDelay;
999 std::set<uint16> activate, deactivate;
1000 for (uint16 itr = 1; itr < mGameEvent.size(); ++itr)
1001 {
1002 // must do the activating first, and after that the deactivating
1003 // so first queue it
1004 //TC_LOG_ERROR("sql.sql", "Checking event {}", itr);
1005 if (CheckOneGameEvent(itr))
1006 {
1007 // if the world event is in NEXTPHASE state, and the time has passed to finish this event, then do so
1008 if (mGameEvent[itr].state == GAMEEVENT_WORLD_NEXTPHASE && mGameEvent[itr].nextstart <= currenttime)
1009 {
1010 // set this event to finished, null the nextstart time
1012 mGameEvent[itr].nextstart = 0;
1013 // save the state of this gameevent
1015 // queue for deactivation
1016 if (IsActiveEvent(itr))
1017 deactivate.insert(itr);
1018 // go to next event, this no longer needs an event update timer
1019 continue;
1020 }
1022 // changed, save to DB the gameevent state, will be updated in next update cycle
1024
1025 //TC_LOG_DEBUG("misc", "GameEvent {} is active", itr->first);
1026 // queue for activation
1027 if (!IsActiveEvent(itr))
1028 activate.insert(itr);
1029 }
1030 else
1031 {
1032 //TC_LOG_DEBUG("misc", "GameEvent {} is not active", itr->first);
1033 if (IsActiveEvent(itr))
1034 deactivate.insert(itr);
1035 else
1036 {
1037 if (!isSystemInit)
1038 {
1039 int16 event_nid = (-1) * (itr);
1040 // spawn all negative ones for this event
1041 GameEventSpawn(event_nid);
1042 }
1043 }
1044 }
1045 calcDelay = NextCheck(itr);
1046 if (calcDelay < nextEventDelay)
1047 nextEventDelay = calcDelay;
1048 }
1049 // now activate the queue
1050 // a now activated event can contain a spawn of a to-be-deactivated one
1051 // following the activate - deactivate order, deactivating the first event later will leave the spawn in (wont disappear then reappear clientside)
1052 for (std::set<uint16>::iterator itr = activate.begin(); itr != activate.end(); ++itr)
1053 // start the event
1054 // returns true the started event completed
1055 // in that case, initiate next update in 1 second
1056 if (StartEvent(*itr))
1057 nextEventDelay = 0;
1058 for (std::set<uint16>::iterator itr = deactivate.begin(); itr != deactivate.end(); ++itr)
1059 StopEvent(*itr);
1060 TC_LOG_INFO("gameevent", "Next game event check in {} seconds.", nextEventDelay + 1);
1061 return (nextEventDelay + 1) * IN_MILLISECONDS; // Add 1 second to be sure event has started/stopped at next call
1062}
1063
1065{
1066 TC_LOG_INFO("gameevent", "GameEvent {} \"{}\" removed.", event_id, mGameEvent[event_id].description);
1068 RunSmartAIScripts(event_id, false);
1069 // un-spawn positive event tagged objects
1070 GameEventUnspawn(event_id);
1071 // spawn negative event tagget objects
1072 int16 event_nid = (-1) * event_id;
1073 GameEventSpawn(event_nid);
1074 // restore equipment or model
1075 ChangeEquipOrModel(event_id, false);
1076 // Remove quests that are events only to non event npc
1077 UpdateEventQuests(event_id, false);
1078 UpdateWorldStates(event_id, false);
1079 // update npcflags in this event
1080 UpdateEventNPCFlags(event_id);
1081 // remove vendor items
1082 UpdateEventNPCVendor(event_id, false);
1083}
1084
1086{
1087 uint8 announce = mGameEvent[event_id].announce;
1088 if (announce == 1 || (announce == 2 && sWorld->getBoolConfig(CONFIG_EVENT_ANNOUNCE)))
1089 sWorld->SendWorldText(LANG_EVENTMESSAGE, mGameEvent[event_id].description.c_str());
1090
1091 TC_LOG_INFO("gameevent", "GameEvent {} \"{}\" started.", event_id, mGameEvent[event_id].description);
1092
1093 // spawn positive event tagget objects
1094 GameEventSpawn(event_id);
1095 // un-spawn negative event tagged objects
1096 int16 event_nid = (-1) * event_id;
1097 GameEventUnspawn(event_nid);
1098 // Change equipement or model
1099 ChangeEquipOrModel(event_id, true);
1100 // Add quests that are events only to non event npc
1101 UpdateEventQuests(event_id, true);
1102 UpdateWorldStates(event_id, true);
1103 // update npcflags in this event
1104 UpdateEventNPCFlags(event_id);
1105 // add vendor items
1106 UpdateEventNPCVendor(event_id, true);
1107
1109 RunSmartAIScripts(event_id, true);
1110
1111 // check for seasonal quest reset.
1112 sWorld->ResetEventSeasonalQuests(event_id, GetLastStartTime(event_id));
1113}
1114
1116{
1117 std::unordered_map<uint32, std::unordered_set<ObjectGuid::LowType>> creaturesByMap;
1118
1119 // go through the creatures whose npcflags are changed in the event
1120 for (NPCFlagList::iterator itr = mGameEventNPCFlags[event_id].begin(); itr != mGameEventNPCFlags[event_id].end(); ++itr)
1121 // get the creature data from the low guid to get the entry, to be able to find out the whole guid
1122 if (CreatureData const* data = sObjectMgr->GetCreatureData(itr->first))
1123 creaturesByMap[data->mapId].insert(itr->first);
1124
1125 for (auto const& p : creaturesByMap)
1126 {
1127 sMapMgr->DoForAllMapsWithMapId(p.first, [this, &p](Map* map)
1128 {
1129 for (auto& spawnId : p.second)
1130 {
1131 auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(spawnId);
1132 for (auto itr = creatureBounds.first; itr != creatureBounds.second; ++itr)
1133 {
1134 Creature* creature = itr->second;
1135 uint64 npcflag = GetNPCFlag(creature);
1136 if (CreatureTemplate const* creatureTemplate = creature->GetCreatureTemplate())
1137 npcflag |= creatureTemplate->npcflag;
1138
1139 creature->ReplaceAllNpcFlags(NPCFlags(npcflag & 0xFFFFFFFF));
1140 creature->ReplaceAllNpcFlags2(NPCFlags2(npcflag >> 32));
1141 // reset gossip options, since the flag change might have added / removed some
1142 //cr->ResetGossipOptions();
1143 }
1144 }
1145 });
1146 }
1147}
1148
1149void GameEventMgr::UpdateEventNPCVendor(uint16 event_id, bool activate)
1150{
1151 for (NPCVendorMap::iterator itr = mGameEventVendors[event_id].begin(); itr != mGameEventVendors[event_id].end(); ++itr)
1152 {
1153 for (VendorItem const& vItem : itr->second)
1154 {
1155 if (activate)
1156 sObjectMgr->AddVendorItem(itr->first, vItem, false);
1157 else
1158 sObjectMgr->RemoveVendorItem(itr->first, vItem.item, vItem.Type, false);
1159 }
1160 }
1161}
1162
1164{
1165 int32 internal_event_id = mGameEvent.size() + event_id - 1;
1166
1167 if (internal_event_id < 0 || internal_event_id >= int32(mGameEventCreatureGuids.size()))
1168 {
1169 TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempted access to out of range mGameEventCreatureGuids element {} (size: {}).",
1170 internal_event_id, mGameEventCreatureGuids.size());
1171 return;
1172 }
1173
1174 for (GuidList::iterator itr = mGameEventCreatureGuids[internal_event_id].begin(); itr != mGameEventCreatureGuids[internal_event_id].end(); ++itr)
1175 {
1176 // Add to correct cell
1177 if (CreatureData const* data = sObjectMgr->GetCreatureData(*itr))
1178 {
1179 sObjectMgr->AddCreatureToGrid(data);
1180
1181 // Spawn if necessary (loaded grids only)
1182 sMapMgr->DoForAllMapsWithMapId(data->mapId, [&itr, data](Map* map)
1183 {
1184 map->RemoveRespawnTime(SPAWN_TYPE_CREATURE, *itr);
1185 // We use spawn coords to spawn
1186 if (map->IsGridLoaded(data->spawnPoint))
1187 Creature::CreateCreatureFromDB(*itr, map);
1188 });
1189 }
1190 }
1191
1192 if (internal_event_id >= int32(mGameEventGameobjectGuids.size()))
1193 {
1194 TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempted access to out of range mGameEventGameobjectGuids element {} (size: {}).",
1195 internal_event_id, mGameEventGameobjectGuids.size());
1196 return;
1197 }
1198
1199 for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin(); itr != mGameEventGameobjectGuids[internal_event_id].end(); ++itr)
1200 {
1201 // Add to correct cell
1202 if (GameObjectData const* data = sObjectMgr->GetGameObjectData(*itr))
1203 {
1204 sObjectMgr->AddGameobjectToGrid(data);
1205 // Spawn if necessary (loaded grids only)
1206 // this base map checked as non-instanced and then only existed
1207 sMapMgr->DoForAllMapsWithMapId(data->mapId, [&itr, data](Map* map)
1208 {
1209 map->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, *itr);
1210 // We use current coords to unspawn, not spawn coords since creature can have changed grid
1211 if (map->IsGridLoaded(data->spawnPoint))
1212 {
1213 if (GameObject* go = GameObject::CreateGameObjectFromDB(*itr, map, false))
1214 {
1216 if (go->isSpawnedByDefault())
1217 {
1218 if (!map->AddToMap(go))
1219 delete go;
1220 }
1221 }
1222 }
1223 });
1224 }
1225 }
1226
1227 if (internal_event_id >= int32(mGameEventPoolIds.size()))
1228 {
1229 TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempted access to out of range mGameEventPoolIds element {} (size: {}).",
1230 internal_event_id, mGameEventPoolIds.size());
1231 return;
1232 }
1233
1234 for (IdList::iterator itr = mGameEventPoolIds[internal_event_id].begin(); itr != mGameEventPoolIds[internal_event_id].end(); ++itr)
1235 {
1236 if (PoolTemplateData const* poolTemplate = sPoolMgr->GetPoolTemplate(*itr))
1237 {
1238 sMapMgr->DoForAllMapsWithMapId(poolTemplate->MapId, [&itr](Map* map)
1239 {
1240 sPoolMgr->SpawnPool(map->GetPoolData(), *itr);
1241 });
1242 }
1243 }
1244}
1245
1247{
1248 int32 internal_event_id = mGameEvent.size() + event_id - 1;
1249
1250 if (internal_event_id < 0 || internal_event_id >= int32(mGameEventCreatureGuids.size()))
1251 {
1252 TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempted access to out of range mGameEventCreatureGuids element {} (size: {}).",
1253 internal_event_id, mGameEventCreatureGuids.size());
1254 return;
1255 }
1256
1257 for (GuidList::iterator itr = mGameEventCreatureGuids[internal_event_id].begin(); itr != mGameEventCreatureGuids[internal_event_id].end(); ++itr)
1258 {
1259 // check if it's needed by another event, if so, don't remove
1260 if (event_id > 0 && hasCreatureActiveEventExcept(*itr, event_id))
1261 continue;
1262 // Remove the creature from grid
1263 if (CreatureData const* data = sObjectMgr->GetCreatureData(*itr))
1264 {
1265 sObjectMgr->RemoveCreatureFromGrid(data);
1266
1267 sMapMgr->DoForAllMapsWithMapId(data->mapId, [&itr](Map* map)
1268 {
1269 map->RemoveRespawnTime(SPAWN_TYPE_CREATURE, *itr);
1270 auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(*itr);
1271 for (auto itr2 = creatureBounds.first; itr2 != creatureBounds.second;)
1272 {
1273 Creature* creature = itr2->second;
1274 ++itr2;
1275 creature->AddObjectToRemoveList();
1276 }
1277 });
1278 }
1279 }
1280
1281 if (internal_event_id < 0 || internal_event_id >= int32(mGameEventGameobjectGuids.size()))
1282 {
1283 TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempted access to out of range mGameEventGameobjectGuids element {} (size: {}).",
1284 internal_event_id, mGameEventGameobjectGuids.size());
1285 return;
1286 }
1287
1288 for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin(); itr != mGameEventGameobjectGuids[internal_event_id].end(); ++itr)
1289 {
1290 // check if it's needed by another event, if so, don't remove
1291 if (event_id >0 && hasGameObjectActiveEventExcept(*itr, event_id))
1292 continue;
1293 // Remove the gameobject from grid
1294 if (GameObjectData const* data = sObjectMgr->GetGameObjectData(*itr))
1295 {
1296 sObjectMgr->RemoveGameobjectFromGrid(data);
1297
1298 sMapMgr->DoForAllMapsWithMapId(data->mapId, [&itr](Map* map)
1299 {
1300 map->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, *itr);
1301 auto gameobjectBounds = map->GetGameObjectBySpawnIdStore().equal_range(*itr);
1302 for (auto itr2 = gameobjectBounds.first; itr2 != gameobjectBounds.second;)
1303 {
1304 GameObject* go = itr2->second;
1305 ++itr2;
1306 go->AddObjectToRemoveList();
1307 }
1308 });
1309 }
1310 }
1311
1312 if (internal_event_id < 0 || internal_event_id >= int32(mGameEventPoolIds.size()))
1313 {
1314 TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempted access to out of range mGameEventPoolIds element {} (size: {}).", internal_event_id, mGameEventPoolIds.size());
1315 return;
1316 }
1317
1318 for (IdList::iterator itr = mGameEventPoolIds[internal_event_id].begin(); itr != mGameEventPoolIds[internal_event_id].end(); ++itr)
1319 {
1320 if (PoolTemplateData const* poolTemplate = sPoolMgr->GetPoolTemplate(*itr))
1321 {
1322 sMapMgr->DoForAllMapsWithMapId(poolTemplate->MapId, [&itr](Map* map)
1323 {
1324 sPoolMgr->DespawnPool(map->GetPoolData(), *itr, true);
1325 });
1326 }
1327 }
1328}
1329
1330void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate)
1331{
1332 for (ModelEquipList::iterator itr = mGameEventModelEquip[event_id].begin(); itr != mGameEventModelEquip[event_id].end(); ++itr)
1333 {
1334 // Remove the creature from grid
1335 CreatureData const* data = sObjectMgr->GetCreatureData(itr->first);
1336 if (!data)
1337 continue;
1338
1339 // Update if spawned
1340 sMapMgr->DoForAllMapsWithMapId(data->mapId, [&itr, activate](Map* map)
1341 {
1342 auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(itr->first);
1343 for (auto itr2 = creatureBounds.first; itr2 != creatureBounds.second; ++itr2)
1344 {
1345 Creature* creature = itr2->second;
1346 if (activate)
1347 {
1348 itr->second.equipement_id_prev = creature->GetCurrentEquipmentId();
1349 itr->second.modelid_prev = creature->GetDisplayId();
1350 creature->LoadEquipment(itr->second.equipment_id, true);
1351 if (itr->second.modelid > 0 && itr->second.modelid_prev != itr->second.modelid && sObjectMgr->GetCreatureModelInfo(itr->second.modelid))
1352 creature->SetDisplayId(itr->second.modelid, true);
1353 }
1354 else
1355 {
1356 creature->LoadEquipment(itr->second.equipement_id_prev, true);
1357 if (itr->second.modelid_prev > 0 && itr->second.modelid_prev != itr->second.modelid && sObjectMgr->GetCreatureModelInfo(itr->second.modelid_prev))
1358 creature->SetDisplayId(itr->second.modelid_prev, true);
1359 }
1360 }
1361 });
1362
1363 // now last step: put in data
1364 CreatureData& data2 = sObjectMgr->NewOrExistCreatureData(itr->first);
1365 if (activate)
1366 {
1367 itr->second.modelid_prev = data2.display ? data2.display->CreatureDisplayID : 0;
1368 itr->second.equipement_id_prev = data2.equipmentId;
1369 if (itr->second.modelid)
1370 data2.display.emplace(itr->second.modelid, DEFAULT_PLAYER_DISPLAY_SCALE, 1.0f);
1371 else
1372 data2.display.reset();
1373 data2.equipmentId = itr->second.equipment_id;
1374 }
1375 else
1376 {
1377 if (itr->second.modelid_prev)
1378 data2.display.emplace(itr->second.modelid_prev, DEFAULT_PLAYER_DISPLAY_SCALE, 1.0f);
1379 else
1380 data2.display.reset();
1381 data2.equipmentId = itr->second.equipement_id_prev;
1382 }
1383 }
1384}
1385
1387{
1388 for (ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr)
1389 {
1390 if ((*e_itr) != event_id)
1391 for (QuestRelList::iterator itr = mGameEventCreatureQuests[*e_itr].begin();
1392 itr != mGameEventCreatureQuests[*e_itr].end();
1393 ++ itr)
1394 if (itr->second == quest_id)
1395 return true;
1396 }
1397 return false;
1398}
1399
1401{
1402 for (ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr)
1403 {
1404 if ((*e_itr) != event_id)
1405 for (QuestRelList::iterator itr = mGameEventGameObjectQuests[*e_itr].begin();
1406 itr != mGameEventGameObjectQuests[*e_itr].end();
1407 ++ itr)
1408 if (itr->second == quest_id)
1409 return true;
1410 }
1411 return false;
1412}
1414{
1415 for (ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr)
1416 {
1417 if ((*e_itr) != event_id)
1418 {
1419 int32 internal_event_id = mGameEvent.size() + (*e_itr) - 1;
1420 for (GuidList::iterator itr = mGameEventCreatureGuids[internal_event_id].begin();
1421 itr != mGameEventCreatureGuids[internal_event_id].end();
1422 ++ itr)
1423 if (*itr == creature_id)
1424 return true;
1425 }
1426 }
1427 return false;
1428}
1430{
1431 for (ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr)
1432 {
1433 if ((*e_itr) != event_id)
1434 {
1435 int32 internal_event_id = mGameEvent.size() + (*e_itr) - 1;
1436 for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin();
1437 itr != mGameEventGameobjectGuids[internal_event_id].end();
1438 ++ itr)
1439 if (*itr == go_id)
1440 return true;
1441 }
1442 }
1443 return false;
1444}
1445
1446void GameEventMgr::UpdateEventQuests(uint16 event_id, bool activate)
1447{
1448 QuestRelList::iterator itr;
1449 for (itr = mGameEventCreatureQuests[event_id].begin(); itr != mGameEventCreatureQuests[event_id].end(); ++itr)
1450 {
1451 QuestRelations* CreatureQuestMap = sObjectMgr->GetCreatureQuestRelationMapHACK();
1452 if (activate) // Add the pair(id, quest) to the multimap
1453 CreatureQuestMap->insert(QuestRelations::value_type(itr->first, itr->second));
1454 else
1455 {
1456 if (!hasCreatureQuestActiveEventExcept(itr->second, event_id))
1457 {
1458 // Remove the pair(id, quest) from the multimap
1459 QuestRelations::iterator qitr = CreatureQuestMap->find(itr->first);
1460 if (qitr == CreatureQuestMap->end())
1461 continue;
1462 QuestRelations::iterator lastElement = CreatureQuestMap->upper_bound(itr->first);
1463 for (; qitr != lastElement; ++qitr)
1464 {
1465 if (qitr->second == itr->second)
1466 {
1467 CreatureQuestMap->erase(qitr); // iterator is now no more valid
1468 break; // but we can exit loop since the element is found
1469 }
1470 }
1471 }
1472 }
1473 }
1474 for (itr = mGameEventGameObjectQuests[event_id].begin(); itr != mGameEventGameObjectQuests[event_id].end(); ++itr)
1475 {
1476 QuestRelations* GameObjectQuestMap = sObjectMgr->GetGOQuestRelationMapHACK();
1477 if (activate) // Add the pair(id, quest) to the multimap
1478 GameObjectQuestMap->insert(QuestRelations::value_type(itr->first, itr->second));
1479 else
1480 {
1481 if (!hasGameObjectQuestActiveEventExcept(itr->second, event_id))
1482 {
1483 // Remove the pair(id, quest) from the multimap
1484 QuestRelations::iterator qitr = GameObjectQuestMap->find(itr->first);
1485 if (qitr == GameObjectQuestMap->end())
1486 continue;
1487 QuestRelations::iterator lastElement = GameObjectQuestMap->upper_bound(itr->first);
1488 for (; qitr != lastElement; ++qitr)
1489 {
1490 if (qitr->second == itr->second)
1491 {
1492 GameObjectQuestMap->erase(qitr); // iterator is now no more valid
1493 break; // but we can exit loop since the element is found
1494 }
1495 }
1496 }
1497 }
1498 }
1499}
1500
1501void GameEventMgr::UpdateWorldStates(uint16 event_id, bool Activate)
1502{
1503 GameEventData const& event = mGameEvent[event_id];
1504 if (event.holiday_id != HOLIDAY_NONE)
1505 if (BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(BattlegroundMgr::WeekendHolidayIdToBGType(event.holiday_id)))
1506 if (bl->HolidayWorldState)
1507 sWorldStateMgr->SetValue(bl->HolidayWorldState, Activate ? 1 : 0, false, nullptr);
1508}
1509
1510GameEventMgr::GameEventMgr() : isSystemInit(false)
1511{
1512}
1513
1515{
1516}
1517
1519{
1520 // translate the quest to event and condition
1521 QuestIdToEventConditionMap::iterator itr = mQuestToEventConditions.find(quest_id);
1522 // quest is registered
1523 if (itr != mQuestToEventConditions.end())
1524 {
1525 uint16 event_id = itr->second.event_id;
1526 uint32 condition = itr->second.condition;
1527 float num = itr->second.num;
1528
1529 // the event is not active, so return, don't increase condition finishes
1530 if (!IsActiveEvent(event_id))
1531 return;
1532 // not in correct phase, return
1533 if (mGameEvent[event_id].state != GAMEEVENT_WORLD_CONDITIONS)
1534 return;
1535 GameEventConditionMap::iterator citr = mGameEvent[event_id].conditions.find(condition);
1536 // condition is registered
1537 if (citr != mGameEvent[event_id].conditions.end())
1538 {
1539 // increase the done count, only if less then the req
1540 if (citr->second.done < citr->second.reqNum)
1541 {
1542 citr->second.done += num;
1543 // check max limit
1544 if (citr->second.done > citr->second.reqNum)
1545 citr->second.done = citr->second.reqNum;
1546 // save the change to db
1547 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
1548
1550 stmt->setUInt8(0, uint8(event_id));
1551 stmt->setUInt32(1, condition);
1552 trans->Append(stmt);
1553
1554 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GAME_EVENT_CONDITION_SAVE);
1555 stmt->setUInt8(0, uint8(event_id));
1556 stmt->setUInt32(1, condition);
1557 stmt->setFloat(2, citr->second.done);
1558 trans->Append(stmt);
1559 CharacterDatabase.CommitTransaction(trans);
1560 // check if all conditions are met, if so, update the event state
1561 if (CheckOneGameEventConditions(event_id))
1562 {
1563 // changed, save to DB the gameevent state
1564 SaveWorldEventStateToDB(event_id);
1565 // force update events to set timer
1566 sWorld->ForceGameEventUpdate();
1567 }
1568 }
1569 }
1570 }
1571}
1572
1574{
1575 for (GameEventConditionMap::const_iterator itr = mGameEvent[event_id].conditions.begin(); itr != mGameEvent[event_id].conditions.end(); ++itr)
1576 if (itr->second.done < itr->second.reqNum)
1577 // return false if a condition doesn't match
1578 return false;
1579 // set the phase
1580 mGameEvent[event_id].state = GAMEEVENT_WORLD_NEXTPHASE;
1581 // set the followup events' start time
1582 if (!mGameEvent[event_id].nextstart)
1583 {
1584 time_t currenttime = GameTime::GetGameTime();
1585 mGameEvent[event_id].nextstart = currenttime + mGameEvent[event_id].length * 60;
1586 }
1587 return true;
1588}
1589
1591{
1592 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
1593
1595 stmt->setUInt8(0, uint8(event_id));
1596 trans->Append(stmt);
1597
1598 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GAME_EVENT_SAVE);
1599 stmt->setUInt8(0, uint8(event_id));
1600 stmt->setUInt8(1, mGameEvent[event_id].state);
1601 stmt->setInt64(2, mGameEvent[event_id].nextstart ? mGameEvent[event_id].nextstart : SI64LIT(0));
1602 trans->Append(stmt);
1603 CharacterDatabase.CommitTransaction(trans);
1604}
1605
1607{
1608 GameEventConditionMap::const_iterator itr;
1609 for (itr = mGameEvent[event_id].conditions.begin(); itr !=mGameEvent[event_id].conditions.end(); ++itr)
1610 {
1611 if (itr->second.done_world_state)
1612 player->SendUpdateWorldState(itr->second.done_world_state, (uint32)(itr->second.done));
1613 if (itr->second.max_world_state)
1614 player->SendUpdateWorldState(itr->second.max_world_state, (uint32)(itr->second.reqNum));
1615 }
1616}
1617
1619{
1620public:
1621 GameEventAIHookWorker(uint16 eventId, bool activate) : _eventId(eventId), _activate(activate) { }
1622
1623 void Visit(std::unordered_map<ObjectGuid, Creature*>& creatureMap)
1624 {
1625 for (auto const& p : creatureMap)
1626 if (p.second->IsInWorld() && p.second->IsAIEnabled())
1627 p.second->AI()->OnGameEvent(_activate, _eventId);
1628 }
1629
1630 void Visit(std::unordered_map<ObjectGuid, GameObject*>& gameObjectMap)
1631 {
1632 for (auto const& p : gameObjectMap)
1633 if (p.second->IsInWorld())
1634 p.second->AI()->OnGameEvent(_activate, _eventId);
1635 }
1636
1637 template<class T>
1638 void Visit(std::unordered_map<ObjectGuid, T*>&) { }
1639
1640private:
1643};
1644
1645void GameEventMgr::RunSmartAIScripts(uint16 event_id, bool activate)
1646{
1649 sMapMgr->DoForAllMaps([event_id, activate](Map* map)
1650 {
1651 GameEventAIHookWorker worker(event_id, activate);
1653 visitor.Visit(map->GetObjectsStore());
1654 });
1655}
1656
1658{
1659 if (!event.holidayStage) // Ignore holiday
1660 return;
1661
1662 HolidaysEntry const* holiday = sHolidaysStore.LookupEntry(event.holiday_id);
1663 if (!holiday->Date[0] || !holiday->Duration[0]) // Invalid definitions
1664 {
1665 TC_LOG_ERROR("sql.sql", "Missing date or duration for holiday {}.", event.holiday_id);
1666 return;
1667 }
1668
1669 uint8 stageIndex = event.holidayStage - 1;
1670 event.length = holiday->Duration[stageIndex] * HOUR / MINUTE;
1671
1672 time_t stageOffset = 0;
1673 for (uint8 i = 0; i < stageIndex; ++i)
1674 stageOffset += holiday->Duration[i] * HOUR;
1675
1676 switch (holiday->CalendarFilterType)
1677 {
1678 case -1: // Yearly
1679 event.occurence = YEAR / MINUTE; // Not all too useful
1680 break;
1681 case 0: // Weekly
1682 event.occurence = WEEK / MINUTE;
1683 break;
1684 case 1: // Defined dates only (Darkmoon Faire)
1685 break;
1686 case 2: // Only used for looping events (Call to Arms)
1687 break;
1688 }
1689
1690 if (holiday->Looping)
1691 {
1692 event.occurence = 0;
1693 for (uint8 i = 0; i < MAX_HOLIDAY_DURATIONS && holiday->Duration[i]; ++i)
1694 event.occurence += holiday->Duration[i] * HOUR / MINUTE;
1695 }
1696
1697 bool singleDate = ((holiday->Date[0] >> 24) & 0x1F) == 31; // Events with fixed date within year have - 1
1698
1699 time_t curTime = GameTime::GetGameTime();
1700 for (uint8 i = 0; i < MAX_HOLIDAY_DATES && holiday->Date[i]; ++i)
1701 {
1702 uint32 date = holiday->Date[i];
1703
1704 tm timeInfo;
1705 if (singleDate)
1706 {
1707 localtime_r(&curTime, &timeInfo);
1708 timeInfo.tm_year -= 1; // First try last year (event active through New Year)
1709 }
1710 else
1711 timeInfo.tm_year = ((date >> 24) & 0x1F) + 100;
1712
1713 timeInfo.tm_mon = (date >> 20) & 0xF;
1714 timeInfo.tm_mday = ((date >> 14) & 0x3F) + 1;
1715 timeInfo.tm_hour = (date >> 6) & 0x1F;
1716 timeInfo.tm_min = date & 0x3F;
1717 timeInfo.tm_sec = 0;
1718 timeInfo.tm_wday = 0;
1719 timeInfo.tm_yday = 0;
1720 timeInfo.tm_isdst = -1;
1721
1722 // try to get next start time (skip past dates)
1723 time_t startTime = mktime(&timeInfo);
1724 if (curTime < startTime + event.length * MINUTE)
1725 {
1726 event.start = startTime + stageOffset;
1727 break;
1728 }
1729 else if (singleDate)
1730 {
1731 tm tmCopy;
1732 localtime_r(&curTime, &tmCopy);
1733 int year = tmCopy.tm_year; // This year
1734 tmCopy = timeInfo;
1735 tmCopy.tm_year = year;
1736
1737 event.start = mktime(&tmCopy) + stageOffset;
1738 break;
1739 }
1740 else
1741 {
1742 // date is due and not a singleDate event, try with next DBC date (modified by holiday_dates)
1743 // if none is found we don't modify start date and use the one in game_event
1744 }
1745 }
1746}
1747
1749{
1750 if (event_id >= mGameEvent.size())
1751 return 0;
1752
1753 if (mGameEvent[event_id].state != GAMEEVENT_NORMAL)
1754 return 0;
1755
1757 SystemTimePoint eventInitialStart = std::chrono::system_clock::from_time_t(mGameEvent[event_id].start);
1758 Minutes occurence(mGameEvent[event_id].occurence);
1759 SystemTimePoint::duration durationSinceLastStart = (now - eventInitialStart) % occurence;
1760 return std::chrono::system_clock::to_time_t(now - durationSinceLastStart);
1761}
1762
1764{
1765 if (id == HOLIDAY_NONE)
1766 return false;
1767
1768 GameEventMgr::GameEventDataMap const& events = sGameEventMgr->GetEventMap();
1769 GameEventMgr::ActiveEvents const& ae = sGameEventMgr->GetActiveEventList();
1770
1771 for (GameEventMgr::ActiveEvents::const_iterator itr = ae.begin(); itr != ae.end(); ++itr)
1772 if (events[*itr].holiday_id == id)
1773 return true;
1774
1775 return false;
1776}
1777
1779{
1780 GameEventMgr::ActiveEvents const& ae = sGameEventMgr->GetActiveEventList();
1781 return ae.find(eventId) != ae.end();
1782}
@ CHAR_DEL_GAME_EVENT_SAVE
@ CHAR_DEL_ALL_GAME_EVENT_CONDITION_SAVE
@ CHAR_INS_GAME_EVENT_SAVE
@ CHAR_DEL_GAME_EVENT_CONDITION_SAVE
@ CHAR_INS_GAME_EVENT_CONDITION_SAVE
@ IN_MILLISECONDS
Definition: Common.h:35
@ MINUTE
Definition: Common.h:29
@ HOUR
Definition: Common.h:30
@ WEEK
Definition: Common.h:32
@ YEAR
Definition: Common.h:34
DB2Storage< BattlemasterListEntry > sBattlemasterListStore("BattlemasterList.db2", &BattlemasterListLoadInfo::Instance)
DB2Storage< HolidaysEntry > sHolidaysStore("Holidays.db2", &HolidaysLoadInfo::Instance)
#define MAX_HOLIDAY_DURATIONS
#define MAX_HOLIDAY_DATES
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
Definition: DatabaseEnv.cpp:21
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
Definition: DatabaseEnv.cpp:20
uint8_t uint8
Definition: Define.h:144
#define SI64LIT(N)
Definition: Define.h:130
int16_t int16
Definition: Define.h:139
int8_t int8
Definition: Define.h:140
int32_t int32
Definition: Define.h:138
uint64_t uint64
Definition: Define.h:141
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
std::chrono::system_clock::time_point SystemTimePoint
Definition: Duration.h:42
std::chrono::minutes Minutes
Minutes shorthand typedef.
Definition: Duration.h:35
bool IsHolidayActive(HolidayIds id)
bool IsEventActive(uint16 eventId)
#define sGameEventMgr
Definition: GameEventMgr.h:177
#define max_ge_check_delay
Definition: GameEventMgr.h:31
GameEventState
Definition: GameEventMgr.h:34
@ GAMEEVENT_NORMAL
Definition: GameEventMgr.h:35
@ GAMEEVENT_INTERNAL
Definition: GameEventMgr.h:40
@ GAMEEVENT_WORLD_FINISHED
Definition: GameEventMgr.h:39
@ GAMEEVENT_WORLD_CONDITIONS
Definition: GameEventMgr.h:37
@ GAMEEVENT_WORLD_NEXTPHASE
Definition: GameEventMgr.h:38
@ GAMEEVENT_WORLD_INACTIVE
Definition: GameEventMgr.h:36
@ LANG_EVENTMESSAGE
Definition: Language.h:36
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
#define sMapMgr
Definition: MapManager.h:184
#define DEFAULT_PLAYER_DISPLAY_SCALE
Definition: ObjectDefines.h:41
#define sObjectMgr
Definition: ObjectMgr.h:1946
std::multimap< uint32, uint32 > QuestRelations
Definition: ObjectMgr.h:559
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
#define sPoolMgr
Definition: PoolMgr.h:179
HolidayIds
@ HOLIDAY_NONE
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition: Timer.h:57
uint32 getMSTime()
Definition: Timer.h:33
#define sWorldStateMgr
Definition: WorldStateMgr.h:50
static BattlegroundTypeId WeekendHolidayIdToBGType(HolidayIds holiday)
ObjectGuid::LowType GetSpawnId() const
Definition: Creature.h:98
Class used to access individual fields of database query result.
Definition: Field.h:90
uint8 GetUInt8() const
Definition: Field.cpp:30
std::string GetString() const
Definition: Field.cpp:118
int8 GetInt8() const
Definition: Field.cpp:38
uint64 GetUInt64() const
Definition: Field.cpp:78
uint16 GetUInt16() const
Definition: Field.cpp:46
float GetFloat() const
Definition: Field.cpp:94
bool GetBool() const
Definition: Field.h:98
uint32 GetUInt32() const
Definition: Field.cpp:62
void Visit(std::unordered_map< ObjectGuid, T * > &)
void Visit(std::unordered_map< ObjectGuid, Creature * > &creatureMap)
void Visit(std::unordered_map< ObjectGuid, GameObject * > &gameObjectMap)
GameEventAIHookWorker(uint16 eventId, bool activate)
static GameEventMgr * instance()
uint32 NextCheck(uint16 entry) const
void HandleQuestComplete(uint32 quest_id)
ActiveEvents m_ActiveEvents
Definition: GameEventMgr.h:169
bool StartEvent(uint16 event_id, bool overwrite=false)
GameEventDataMap mGameEvent
Definition: GameEventMgr.h:166
GameEventNPCFlagMap mGameEventNPCFlags
Definition: GameEventMgr.h:168
bool IsActiveEvent(uint16 event_id)
Definition: GameEventMgr.h:110
GameEventIdMap mGameEventPoolIds
Definition: GameEventMgr.h:165
std::vector< GameEventData > GameEventDataMap
Definition: GameEventMgr.h:103
void GameEventSpawn(int16 event_id)
void GameEventUnspawn(int16 event_id)
GameEventGuidMap mGameEventCreatureGuids
Definition: GameEventMgr.h:173
void RemoveActiveEvent(uint16 event_id)
Definition: GameEventMgr.h:123
void UnApplyEvent(uint16 event_id)
void UpdateEventQuests(uint16 event_id, bool activate)
void UpdateEventNPCVendor(uint16 event_id, bool activate)
void UpdateWorldStates(uint16 event_id, bool Activate)
bool hasCreatureActiveEventExcept(ObjectGuid::LowType creature_guid, uint16 event_id)
time_t GetLastStartTime(uint16 event_id) const
uint32 Update()
void AddActiveEvent(uint16 event_id)
Definition: GameEventMgr.h:122
void StartInternalEvent(uint16 event_id)
std::list< ObjectGuid::LowType > GuidList
Definition: GameEventMgr.h:143
void ApplyNewEvent(uint16 event_id)
std::pair< ObjectGuid::LowType, uint64 > GuidNPCFlagPair
Definition: GameEventMgr.h:156
GameEventModelEquipMap mGameEventModelEquip
Definition: GameEventMgr.h:162
std::list< ModelEquipPair > ModelEquipList
Definition: GameEventMgr.h:148
GameEventNPCVendorMap mGameEventVendors
Definition: GameEventMgr.h:161
GameEventQuestMap mGameEventCreatureQuests
Definition: GameEventMgr.h:159
GameEventGuidMap mGameEventGameobjectGuids
Definition: GameEventMgr.h:174
void SaveWorldEventStateToDB(uint16 event_id)
void UpdateEventNPCFlags(uint16 event_id)
bool CheckOneGameEventConditions(uint16 event_id)
void ChangeEquipOrModel(int16 event_id, bool activate)
uint32 StartSystem()
void RunSmartAIScripts(uint16 event_id, bool activate)
Runs SMART_EVENT_GAME_EVENT_START/_END SAI.
std::set< uint16 > ActiveEvents
Definition: GameEventMgr.h:102
QuestIdToEventConditionMap mQuestToEventConditions
Definition: GameEventMgr.h:167
bool hasCreatureQuestActiveEventExcept(uint32 quest_id, uint16 event_id)
std::list< GuidNPCFlagPair > NPCFlagList
Definition: GameEventMgr.h:157
std::list< uint32 > IdList
Definition: GameEventMgr.h:144
GameEventQuestMap mGameEventGameObjectQuests
Definition: GameEventMgr.h:160
void SetHolidayEventTime(GameEventData &event)
std::list< QuestRelation > QuestRelList
Definition: GameEventMgr.h:151
void SendWorldStateUpdate(Player *player, uint16 event_id)
bool hasGameObjectActiveEventExcept(ObjectGuid::LowType go_guid, uint16 event_id)
std::unordered_map< uint32, std::vector< VendorItem > > NPCVendorMap
Definition: GameEventMgr.h:153
bool CheckOneGameEvent(uint16 entry) const
void StopEvent(uint16 event_id, bool overwrite=false)
void StartArenaSeason()
bool hasGameObjectQuestActiveEventExcept(uint32 quest_id, uint16 event_id)
std::pair< uint32, uint32 > QuestRelation
Definition: GameEventMgr.h:150
uint64 GetNPCFlag(Creature *cr)
Definition: Map.h:189
MapStoredObjectTypesContainer & GetObjectsStore()
Definition: Map.h:422
uint64 LowType
Definition: ObjectGuid.h:278
void SendUpdateWorldState(uint32 variable, uint32 value, bool hidden=false) const
Definition: Player.cpp:9174
void setFloat(const uint8 index, const float value)
void setUInt8(const uint8 index, const uint8 value)
void setInt64(const uint8 index, const int64 value)
void setUInt32(const uint8 index, const uint32 value)
void SetEventIdForQuest(uint16 eventId)
Definition: QuestDef.h:712
void Visit(TYPE_CONTAINER &c)
#define sWorld
Definition: World.h:931
@ CONFIG_ARENA_SEASON_ID
Definition: World.h:347
@ CONFIG_EVENT_ANNOUNCE
Definition: World.h:171
SystemTimePoint GetSystemTime()
Current chrono system_clock time point.
Definition: GameTime.cpp:54
time_t GetGameTime()
Definition: GameTime.cpp:44
TC_COMMON_API std::vector< std::string_view > Tokenize(std::string_view str, char sep, bool keepEmpty)
Definition: Util.cpp:56
Optional< CreatureModel > display
Definition: CreatureData.h:591
uint32 occurence
Definition: GameEventMgr.h:67
uint8 holidayStage
Definition: GameEventMgr.h:70
GameEventState state
Definition: GameEventMgr.h:71
HolidayIds holiday_id
Definition: GameEventMgr.h:69
GameEventConditionMap conditions
Definition: GameEventMgr.h:72
time_t nextstart
Definition: GameEventMgr.h:66
std::string description
Definition: GameEventMgr.h:74
int8 CalendarFilterType
std::array< uint32, MAX_HOLIDAY_DATES > Date
std::array< uint16, MAX_HOLIDAY_DURATIONS > Duration
uint32 modelid
Definition: GameEventMgr.h:82
uint8 equipement_id_prev
Definition: GameEventMgr.h:85
uint32 modelid_prev
Definition: GameEventMgr.h:83
uint8 equipment_id
Definition: GameEventMgr.h:84
uint32 poolId
Definition: SpawnData.h:110
uint32 mapId
Definition: SpawnData.h:94
uint32 ExtendedCost
Definition: CreatureData.h:658
bool IgnoreFiltering
Definition: CreatureData.h:662
uint32 PlayerConditionId
Definition: CreatureData.h:661
uint32 item
Definition: CreatureData.h:655
uint32 maxcount
Definition: CreatureData.h:656
std::vector< int32 > BonusListIDs
Definition: CreatureData.h:660
uint32 incrtime
Definition: CreatureData.h:657