root/trunk/src/game/GameEvent.cpp @ 157

Revision 111, 56.1 kB (checked in by yumileroy, 17 years ago)

[svn] * Fixed xp calculation for low level characters when grouped with a higher level player. Patch provided by Reiner030.
* Fixed positive spells being not resistable when cast on hostile units. Patch provided by QAston.
* Fixed compile warnings in gcc. Patch provided by WarHead?.

Original author: w12x
Date: 2008-10-26 11:50:07-05:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
4 * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include "GameEvent.h"
22#include "World.h"
23#include "ObjectMgr.h"
24#include "ProgressBar.h"
25#include "Language.h"
26#include "Log.h"
27#include "MapManager.h"
28#include "Policies/SingletonImp.h"
29#include "GossipDef.h"
30#include "Player.h"
31#include "BattleGroundMgr.h"
32
33INSTANTIATE_SINGLETON_1(GameEvent);
34
35bool GameEvent::CheckOneGameEvent(uint16 entry) const
36{
37    time_t currenttime = time(NULL);
38    // if the state is conditions or nextphase, then the event should be active
39    if (mGameEvent[entry].state == GAMEEVENT_WORLD_CONDITIONS || mGameEvent[entry].state == GAMEEVENT_WORLD_NEXTPHASE)
40        return true;
41    // finished world events are inactive
42    else if (mGameEvent[entry].state == GAMEEVENT_WORLD_FINISHED)
43        return false;
44    // if inactive world event, check the prerequisite events
45    else if (mGameEvent[entry].state == GAMEEVENT_WORLD_INACTIVE)
46    {
47        for(std::set<uint16>::const_iterator itr = mGameEvent[entry].prerequisite_events.begin(); itr != mGameEvent[entry].prerequisite_events.end(); ++itr)
48        {
49            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
50                mGameEvent[*itr].nextstart > currenttime)               // if not in nextphase state for long enough, can't start this one
51                return false;
52        }
53        // all prerequisite events are met
54        // but if there are no prerequisites, this can be only activated through gm command
55        return !(mGameEvent[entry].prerequisite_events.empty());
56    }
57    // Get the event information
58    if( mGameEvent[entry].start < currenttime && currenttime < mGameEvent[entry].end &&
59        ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE)) < (mGameEvent[entry].length * MINUTE) )
60        return true;
61    else
62        return false;
63}
64
65uint32 GameEvent::NextCheck(uint16 entry) const
66{
67    time_t currenttime = time(NULL);
68
69    // for NEXTPHASE state world events, return the delay to start the next event, so the followup event will be checked correctly
70    if ((mGameEvent[entry].state == GAMEEVENT_WORLD_NEXTPHASE || mGameEvent[entry].state == GAMEEVENT_WORLD_FINISHED) && mGameEvent[entry].nextstart >= currenttime)
71        return (mGameEvent[entry].nextstart - currenttime);
72
73    // 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
74    if (mGameEvent[entry].state == GAMEEVENT_WORLD_CONDITIONS)
75        return mGameEvent[entry].length ? mGameEvent[entry].length * 60 : max_ge_check_delay;
76
77    // outdated event: we return max
78    if (currenttime > mGameEvent[entry].end)
79        return max_ge_check_delay;
80
81    // never started event, we return delay before start
82    if (mGameEvent[entry].start > currenttime)
83        return (mGameEvent[entry].start - currenttime);
84
85    uint32 delay;
86    // in event, we return the end of it
87    if ((((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * 60)) < (mGameEvent[entry].length * 60)))
88        // we return the delay before it ends
89        delay = (mGameEvent[entry].length * MINUTE) - ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE));
90    else                                                    // not in window, we return the delay before next start
91        delay = (mGameEvent[entry].occurence * MINUTE) - ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE));
92    // In case the end is before next check
93    if (mGameEvent[entry].end  < time_t(currenttime + delay))
94        return (mGameEvent[entry].end - currenttime);
95    else
96        return delay;
97}
98
99bool GameEvent::StartEvent( uint16 event_id, bool overwrite )
100{
101    if(mGameEvent[event_id].state == GAMEEVENT_NORMAL)
102    {
103        AddActiveEvent(event_id);
104        ApplyNewEvent(event_id);
105        if(overwrite)
106        {
107            mGameEvent[event_id].start = time(NULL);
108            if(mGameEvent[event_id].end <= mGameEvent[event_id].start)
109                mGameEvent[event_id].end = mGameEvent[event_id].start+mGameEvent[event_id].length;
110        }
111        return false;
112    }
113    else
114    {
115        if( mGameEvent[event_id].state == GAMEEVENT_WORLD_INACTIVE )
116            // set to conditions phase
117            mGameEvent[event_id].state = GAMEEVENT_WORLD_CONDITIONS;
118
119        // add to active events
120        AddActiveEvent(event_id);
121        // add spawns
122        ApplyNewEvent(event_id);
123
124        // check if can go to next state
125        bool conditions_met = CheckOneGameEventConditions(event_id);
126        // save to db
127        SaveWorldEventStateToDB(event_id);
128        // force game event update to set the update timer if conditions were met from a command
129        // this update is needed to possibly start events dependent on the started one
130        // or to scedule another update where the next event will be started
131        if(overwrite && conditions_met)
132            sWorld.ForceGameEventUpdate();
133
134        return conditions_met;
135    }
136}
137
138void GameEvent::StopEvent( uint16 event_id, bool overwrite )
139{
140    bool serverwide_evt = mGameEvent[event_id].state != GAMEEVENT_NORMAL;
141
142    RemoveActiveEvent(event_id);
143    UnApplyEvent(event_id);
144
145    if(overwrite && !serverwide_evt)
146    {
147        mGameEvent[event_id].start = time(NULL) - mGameEvent[event_id].length * MINUTE;
148        if(mGameEvent[event_id].end <= mGameEvent[event_id].start)
149            mGameEvent[event_id].end = mGameEvent[event_id].start+mGameEvent[event_id].length;
150    }
151    else if(serverwide_evt)
152    {
153        // if finished world event, then only gm command can stop it
154        if(overwrite || mGameEvent[event_id].state != GAMEEVENT_WORLD_FINISHED)
155        {
156            // reset conditions
157            mGameEvent[event_id].nextstart = 0;
158            mGameEvent[event_id].state = GAMEEVENT_WORLD_INACTIVE;
159            std::map<uint32 /*condition id*/, GameEventFinishCondition>::iterator itr;
160            for(itr = mGameEvent[event_id].conditions.begin(); itr != mGameEvent[event_id].conditions.end(); ++itr)
161                itr->second.done = 0;
162            CharacterDatabase.BeginTransaction();
163            CharacterDatabase.PExecute("DELETE FROM game_event_save WHERE event_id = '%u'",event_id);
164            CharacterDatabase.PExecute("DELETE FROM game_event_condition_save WHERE event_id = '%u'",event_id);
165            CharacterDatabase.CommitTransaction();
166        }
167    }
168}
169
170void GameEvent::LoadFromDB()
171{
172    {
173        QueryResult *result = WorldDatabase.Query("SELECT MAX(entry) FROM game_event");
174        if( !result )
175        {
176            sLog.outString(">> Table game_event is empty.");
177            sLog.outString();
178            return;
179        }
180
181        Field *fields = result->Fetch();
182
183        uint32 max_event_id = fields[0].GetUInt16();
184        delete result;
185
186        mGameEvent.resize(max_event_id+1);
187    }
188
189    QueryResult *result = WorldDatabase.Query("SELECT entry,UNIX_TIMESTAMP(start_time),UNIX_TIMESTAMP(end_time),occurence,length,description,world_event FROM game_event");
190    if( !result )
191    {
192        mGameEvent.clear();
193        sLog.outString(">> Table game_event is empty:");
194        sLog.outString();
195        return;
196    }
197
198    uint32 count = 0;
199
200    barGoLink bar( result->GetRowCount() );
201    do
202    {
203        ++count;
204        Field *fields = result->Fetch();
205
206        bar.step();
207
208        uint16 event_id = fields[0].GetUInt16();
209        if(event_id==0)
210        {
211            sLog.outErrorDb("`game_event` game event id (%i) is reserved and can't be used.",event_id);
212            continue;
213        }
214
215        GameEventData& pGameEvent = mGameEvent[event_id];
216        uint64 starttime        = fields[1].GetUInt64();
217        pGameEvent.start        = time_t(starttime);
218        uint64 endtime          = fields[2].GetUInt64();
219        pGameEvent.end          = time_t(endtime);
220        pGameEvent.occurence    = fields[3].GetUInt32();
221        pGameEvent.length       = fields[4].GetUInt32();
222        pGameEvent.description  = fields[5].GetCppString();
223        pGameEvent.state        = (GameEventState)(fields[6].GetUInt8());
224        pGameEvent.nextstart    = 0;
225
226        if(pGameEvent.length==0 && pGameEvent.state == GAMEEVENT_NORMAL)                            // length>0 is validity check
227        {
228            sLog.outErrorDb("`game_event` game event id (%i) isn't a world event and has length = 0, thus it can't be used.",event_id);
229            continue;
230        }
231
232    } while( result->NextRow() );
233
234    sLog.outString();
235    sLog.outString( ">> Loaded %u game events", count );
236    delete result;
237
238    // load game event saves
239    //                                       0         1      2
240    result = CharacterDatabase.Query("SELECT event_id, state, UNIX_TIMESTAMP(next_start) FROM game_event_save");
241
242    count = 0;
243    if( !result )
244    {
245        barGoLink bar2(1);
246        bar2.step();
247
248        sLog.outString();
249        sLog.outString(">> Loaded %u game event saves in game events", count );
250    }
251    else
252    {
253
254        barGoLink bar2( result->GetRowCount() );
255        do
256        {
257            Field *fields = result->Fetch();
258
259            bar2.step();
260
261            uint16 event_id = fields[0].GetUInt16();
262
263            if(event_id >= mGameEvent.size())
264            {
265                sLog.outErrorDb("`game_event_save` game event id (%i) is out of range compared to max event id in `game_event`",event_id);
266                continue;
267            }
268
269            if(mGameEvent[event_id].state != GAMEEVENT_NORMAL)
270            {
271                mGameEvent[event_id].state = (GameEventState)(fields[1].GetUInt8());
272                mGameEvent[event_id].nextstart    = time_t(fields[2].GetUInt64());
273            }
274            else
275            {
276                sLog.outErrorDb("game_event_save includes event save for non-worldevent id %u",event_id);
277                continue;
278            }
279
280            ++count;
281
282        } while( result->NextRow() );
283        sLog.outString();
284        sLog.outString( ">> Loaded %u game event saves in game events", count );
285        delete result;
286    }
287
288    // load game event links (prerequisites)
289    result = WorldDatabase.Query("SELECT event_id, prerequisite_event FROM game_event_prerequisite");
290    if( !result )
291    {
292        barGoLink bar2(1);
293        bar2.step();
294
295        sLog.outString();
296        sLog.outString(">> Loaded %u game event prerequisites in game events", count );
297    }
298    else
299    {
300
301        barGoLink bar2( result->GetRowCount() );
302        do
303        {
304            Field *fields = result->Fetch();
305
306            bar2.step();
307
308            uint16 event_id = fields[0].GetUInt16();
309
310            if(event_id >= mGameEvent.size())
311            {
312                sLog.outErrorDb("`game_event_prerequisite` game event id (%i) is out of range compared to max event id in `game_event`",event_id);
313                continue;
314            }
315
316
317            if(mGameEvent[event_id].state != GAMEEVENT_NORMAL)
318            {
319                uint16 prerequisite_event = fields[1].GetUInt16();
320                if(prerequisite_event >= mGameEvent.size())
321                {
322                    sLog.outErrorDb("`game_event_prerequisite` game event prerequisite id (%i) is out of range compared to max event id in `game_event`",prerequisite_event);
323                    continue;
324                }
325                mGameEvent[event_id].prerequisite_events.insert(prerequisite_event);
326            }
327            else
328            {
329                sLog.outErrorDb("game_event_prerequisiste includes event entry for non-worldevent id %u",event_id);
330                continue;
331            }
332
333            ++count;
334
335        } while( result->NextRow() );
336        sLog.outString();
337        sLog.outString( ">> Loaded %u game event prerequisites in game events", count );
338        delete result;
339    }
340
341    mGameEventCreatureGuids.resize(mGameEvent.size()*2-1);
342    //                                   1              2
343    result = WorldDatabase.Query("SELECT creature.guid, game_event_creature.event "
344        "FROM creature JOIN game_event_creature ON creature.guid = game_event_creature.guid");
345
346    count = 0;
347    if( !result )
348    {
349        barGoLink bar2(1);
350        bar2.step();
351
352        sLog.outString();
353        sLog.outString(">> Loaded %u creatures in game events", count );
354    }
355    else
356    {
357
358        barGoLink bar2( result->GetRowCount() );
359        do
360        {
361            Field *fields = result->Fetch();
362
363            bar2.step();
364
365            uint32 guid    = fields[0].GetUInt32();
366            int16 event_id = fields[1].GetInt16();
367
368            int32 internal_event_id = mGameEvent.size() + event_id - 1;
369
370            if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size())
371            {
372                sLog.outErrorDb("`game_event_creature` game event id (%i) is out of range compared to max event id in `game_event`",event_id);
373                continue;
374            }
375
376            ++count;
377            GuidList& crelist = mGameEventCreatureGuids[internal_event_id];
378            crelist.push_back(guid);
379
380        } while( result->NextRow() );
381        sLog.outString();
382        sLog.outString( ">> Loaded %u creatures in game events", count );
383        delete result;
384    }
385
386    mGameEventGameobjectGuids.resize(mGameEvent.size()*2-1);
387    //                                   1                2
388    result = WorldDatabase.Query("SELECT gameobject.guid, game_event_gameobject.event "
389        "FROM gameobject JOIN game_event_gameobject ON gameobject.guid=game_event_gameobject.guid");
390
391    count = 0;
392    if( !result )
393    {
394        barGoLink bar3(1);
395        bar3.step();
396
397        sLog.outString();
398        sLog.outString(">> Loaded %u gameobjects in game events", count );
399    }
400    else
401    {
402
403        barGoLink bar3( result->GetRowCount() );
404        do
405        {
406            Field *fields = result->Fetch();
407
408            bar3.step();
409
410            uint32 guid    = fields[0].GetUInt32();
411            int16 event_id = fields[1].GetInt16();
412
413            int32 internal_event_id = mGameEvent.size() + event_id - 1;
414
415            if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size())
416            {
417                sLog.outErrorDb("`game_event_gameobject` game event id (%i) is out of range compared to max event id in `game_event`",event_id);
418                continue;
419            }
420
421            ++count;
422            GuidList& golist = mGameEventGameobjectGuids[internal_event_id];
423            golist.push_back(guid);
424
425        } while( result->NextRow() );
426        sLog.outString();
427        sLog.outString( ">> Loaded %u gameobjects in game events", count );
428
429        delete result;
430    }
431
432    mGameEventModelEquip.resize(mGameEvent.size());
433    //                                   0              1                             2
434    result = WorldDatabase.Query("SELECT creature.guid, game_event_model_equip.event, game_event_model_equip.modelid,"
435    //   3
436        "game_event_model_equip.equipment_id "
437        "FROM creature JOIN game_event_model_equip ON creature.guid=game_event_model_equip.guid");
438
439    count = 0;
440    if( !result )
441    {
442        barGoLink bar3(1);
443        bar3.step();
444
445        sLog.outString();
446        sLog.outString(">> Loaded %u model/equipment changes in game events", count );
447    }
448    else
449    {
450
451        barGoLink bar3( result->GetRowCount() );
452        do
453        {
454            Field *fields = result->Fetch();
455
456            bar3.step();
457            uint32 guid     = fields[0].GetUInt32();
458            uint16 event_id = fields[1].GetUInt16();
459
460            if(event_id >= mGameEventModelEquip.size())
461            {
462                sLog.outErrorDb("`game_event_model_equip` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
463                continue;
464            }
465
466            ++count;
467            ModelEquipList& equiplist = mGameEventModelEquip[event_id];
468            ModelEquip newModelEquipSet;
469            newModelEquipSet.modelid = fields[2].GetUInt32();
470            newModelEquipSet.equipment_id = fields[3].GetUInt32();
471            newModelEquipSet.equipement_id_prev = 0;
472            newModelEquipSet.modelid_prev = 0;
473
474            if(newModelEquipSet.equipment_id > 0)
475            {
476                if(!objmgr.GetEquipmentInfo(newModelEquipSet.equipment_id))
477                {
478                    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);
479                    continue;
480                }
481            }
482
483            equiplist.push_back(std::pair<uint32, ModelEquip>(guid, newModelEquipSet));
484
485        } while( result->NextRow() );
486        sLog.outString();
487        sLog.outString( ">> Loaded %u model/equipment changes in game events", count );
488
489        delete result;
490    }
491
492    mGameEventCreatureQuests.resize(mGameEvent.size());
493    //                                   0   1      2
494    result = WorldDatabase.Query("SELECT id, quest, event FROM game_event_creature_quest");
495
496    count = 0;
497    if( !result )
498    {
499        barGoLink bar3(1);
500        bar3.step();
501
502        sLog.outString();
503        sLog.outString(">> Loaded %u quests additions in game events", count );
504    }
505    else
506    {
507
508        barGoLink bar3( result->GetRowCount() );
509        do
510        {
511            Field *fields = result->Fetch();
512
513            bar3.step();
514            uint32 id       = fields[0].GetUInt32();
515            uint32 quest    = fields[1].GetUInt32();
516            uint16 event_id = fields[2].GetUInt16();
517
518            if(event_id >= mGameEventCreatureQuests.size())
519            {
520                sLog.outErrorDb("`game_event_creature_quest` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
521                continue;
522            }
523
524            ++count;
525            QuestRelList& questlist = mGameEventCreatureQuests[event_id];
526            questlist.push_back(QuestRelation(id, quest));
527
528        } while( result->NextRow() );
529        sLog.outString();
530        sLog.outString( ">> Loaded %u quests additions in game events", count );
531
532        delete result;
533    }
534
535    mGameEventGameObjectQuests.resize(mGameEvent.size());
536    //                                   0   1      2
537    result = WorldDatabase.Query("SELECT id, quest, event FROM game_event_gameobject_quest");
538
539    count = 0;
540    if( !result )
541    {
542        barGoLink bar3(1);
543        bar3.step();
544
545        sLog.outString();
546        sLog.outString(">> Loaded %u go quests additions in game events", count );
547    }
548    else
549    {
550
551        barGoLink bar3( result->GetRowCount() );
552        do
553        {
554            Field *fields = result->Fetch();
555
556            bar3.step();
557            uint32 id       = fields[0].GetUInt32();
558            uint32 quest    = fields[1].GetUInt32();
559            uint16 event_id = fields[2].GetUInt16();
560
561            if(event_id >= mGameEventGameObjectQuests.size())
562            {
563                sLog.outErrorDb("`game_event_gameobject_quest` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
564                continue;
565            }
566
567            ++count;
568            QuestRelList& questlist = mGameEventGameObjectQuests[event_id];
569            questlist.push_back(QuestRelation(id, quest));
570
571        } while( result->NextRow() );
572        sLog.outString();
573        sLog.outString( ">> Loaded %u quests additions in game events", count );
574
575        delete result;
576    }
577
578    // Load quest to (event,condition) mapping
579    //                                   0      1         2             3
580    result = WorldDatabase.Query("SELECT quest, event_id, condition_id, num FROM game_event_quest_condition");
581
582    count = 0;
583    if( !result )
584    {
585        barGoLink bar3(1);
586        bar3.step();
587
588        sLog.outString();
589        sLog.outString(">> Loaded %u quest event conditions in game events", count );
590    }
591    else
592    {
593
594        barGoLink bar3( result->GetRowCount() );
595        do
596        {
597            Field *fields = result->Fetch();
598
599            bar3.step();
600            uint32 quest     = fields[0].GetUInt32();
601            uint16 event_id  = fields[1].GetUInt16();
602            uint32 condition = fields[2].GetUInt32();
603            float num       = fields[3].GetFloat();
604
605            if(event_id >= mGameEvent.size())
606            {
607                sLog.outErrorDb("`game_event_quest_condition` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
608                continue;
609            }
610
611            ++count;
612            mQuestToEventConditions[quest].event_id = event_id;
613            mQuestToEventConditions[quest].condition = condition;
614            mQuestToEventConditions[quest].num = num;
615
616        } while( result->NextRow() );
617        sLog.outString();
618        sLog.outString( ">> Loaded %u quest event conditions in game events", count );
619
620        delete result;
621    }
622
623    // load conditions of the events
624    //                                   0         1             2        3                      4         
625    result = WorldDatabase.Query("SELECT event_id, condition_id, req_num, max_world_state_field, done_world_state_field FROM game_event_condition");
626
627    count = 0;
628    if( !result )
629    {
630        barGoLink bar3(1);
631        bar3.step();
632
633        sLog.outString();
634        sLog.outString(">> Loaded %u conditions in game events", count );
635    }
636    else
637    {
638
639        barGoLink bar3( result->GetRowCount() );
640        do
641        {
642            Field *fields = result->Fetch();
643
644            bar3.step();
645            uint16 event_id  = fields[0].GetUInt16();
646            uint32 condition = fields[1].GetUInt32();
647
648            if(event_id >= mGameEvent.size())
649            {
650                sLog.outErrorDb("`game_event_condition` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
651                continue;
652            }
653
654            mGameEvent[event_id].conditions[condition].reqNum = fields[2].GetFloat();
655            mGameEvent[event_id].conditions[condition].done = 0;
656            mGameEvent[event_id].conditions[condition].max_world_state = fields[3].GetUInt32();
657            mGameEvent[event_id].conditions[condition].done_world_state = fields[4].GetUInt32();
658
659            ++count;
660
661        } while( result->NextRow() );
662        sLog.outString();
663        sLog.outString( ">> Loaded %u conditions in game events", count );
664
665        delete result;
666    }
667
668    // load condition saves
669    //                                       0         1             2         
670    result = CharacterDatabase.Query("SELECT event_id, condition_id, done FROM game_event_condition_save");
671
672    count = 0;
673    if( !result )
674    {
675        barGoLink bar3(1);
676        bar3.step();
677
678        sLog.outString();
679        sLog.outString(">> Loaded %u condition saves in game events", count );
680    }
681    else
682    {
683
684        barGoLink bar3( result->GetRowCount() );
685        do
686        {
687            Field *fields = result->Fetch();
688
689            bar3.step();
690            uint16 event_id  = fields[0].GetUInt16();
691            uint32 condition = fields[1].GetUInt32();
692
693            if(event_id >= mGameEvent.size())
694            {
695                sLog.outErrorDb("`game_event_condition_save` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
696                continue;
697            }
698
699            std::map<uint32, GameEventFinishCondition>::iterator itr = mGameEvent[event_id].conditions.find(condition);
700            if(itr != mGameEvent[event_id].conditions.end())
701            {
702                itr->second.done = fields[2].GetFloat();
703            }
704            else
705            {
706                sLog.outErrorDb("game_event_condition_save contains not present condition evt id %u cond id %u",event_id, condition);
707                continue;
708            }
709
710            ++count;
711
712        } while( result->NextRow() );
713        sLog.outString();
714        sLog.outString( ">> Loaded %u condition saves in game events", count );
715
716        delete result;
717    }
718
719    mGameEventNPCFlags.resize(mGameEvent.size());
720    // load game event npcflag
721    //                                   0         1        2
722    result = WorldDatabase.Query("SELECT guid, event_id, npcflag FROM game_event_npcflag");
723
724    count = 0;
725    if( !result )
726    {
727        barGoLink bar3(1);
728        bar3.step();
729
730        sLog.outString();
731        sLog.outString(">> Loaded %u npcflags in game events", count );
732    }
733    else
734    {
735
736        barGoLink bar3( result->GetRowCount() );
737        do
738        {
739            Field *fields = result->Fetch();
740
741            bar3.step();
742            uint32 guid     = fields[0].GetUInt32();
743            uint16 event_id = fields[1].GetUInt16();
744            uint32 npcflag  = fields[2].GetUInt32();
745
746            if(event_id >= mGameEvent.size())
747            {
748                sLog.outErrorDb("`game_event_npcflag` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
749                continue;
750            }
751
752            mGameEventNPCFlags[event_id].push_back(GuidNPCFlagPair(guid,npcflag));
753
754            ++count;
755
756        } while( result->NextRow() );
757        sLog.outString();
758        sLog.outString( ">> Loaded %u npcflags in game events", count );
759
760        delete result;
761    }
762
763    mGameEventVendors.resize(mGameEvent.size());
764    //                                   0      1      2     3         4         5
765    result = WorldDatabase.Query("SELECT event, guid, item, maxcount, incrtime, ExtendedCost FROM game_event_npc_vendor");
766
767    count = 0;
768    if( !result )
769    {
770        barGoLink bar3(1);
771        bar3.step();
772
773        sLog.outString();
774        sLog.outString(">> Loaded %u vendor additions in game events", count );
775    }
776    else
777    {
778
779        barGoLink bar3( result->GetRowCount() );
780        do
781        {
782            Field *fields = result->Fetch();
783
784            bar3.step();
785            uint16 event_id  = fields[0].GetUInt16();
786
787            if(event_id >= mGameEventVendors.size())
788            {
789                sLog.outErrorDb("`game_event_npc_vendor` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
790                continue;
791            }
792
793            NPCVendorList& vendors = mGameEventVendors[event_id];
794            NPCVendorEntry newEntry;
795            uint32 guid = fields[1].GetUInt32();
796            newEntry.item = fields[2].GetUInt32();
797            newEntry.maxcount = fields[3].GetUInt32();
798            newEntry.incrtime = fields[4].GetUInt32();
799            newEntry.ExtendedCost = fields[5].GetUInt32();
800            // get the event npc flag for checking if the npc will be vendor during the event or not
801            uint32 event_npc_flag = 0;
802            NPCFlagList& flist = mGameEventNPCFlags[event_id];
803            for(NPCFlagList::const_iterator itr = flist.begin(); itr != flist.end(); ++itr)
804            {
805                if(itr->first == guid)
806                {
807                    event_npc_flag = itr->second;
808                    break;
809                }
810            }
811            // get creature entry
812            newEntry.entry = 0;
813
814            if( CreatureData const* data = objmgr.GetCreatureData(guid) )
815                newEntry.entry = data->id;
816
817            // check validity with event's npcflag
818            if(!objmgr.IsVendorItemValid(newEntry.entry, newEntry.item, newEntry.maxcount, newEntry.incrtime, newEntry.ExtendedCost, NULL, NULL, event_npc_flag))
819                continue;
820            ++count;
821            vendors.push_back(newEntry);
822
823        } while( result->NextRow() );
824        sLog.outString();
825        sLog.outString( ">> Loaded %u vendor additions in game events", count );
826
827        delete result;
828    }
829
830    // load game event npc gossip ids
831    //                                   0         1        2
832    result = WorldDatabase.Query("SELECT guid, event_id, textid FROM game_event_npc_gossip");
833
834    count = 0;
835    if( !result )
836    {
837        barGoLink bar3(1);
838        bar3.step();
839
840        sLog.outString();
841        sLog.outString(">> Loaded %u npc gossip textids in game events", count );
842    }
843    else
844    {
845
846        barGoLink bar3( result->GetRowCount() );
847        do
848        {
849            Field *fields = result->Fetch();
850
851            bar3.step();
852            uint32 guid     = fields[0].GetUInt32();
853            uint16 event_id = fields[1].GetUInt16();
854            uint32 textid  = fields[2].GetUInt32();
855
856            if(event_id >= mGameEvent.size())
857            {
858                sLog.outErrorDb("`game_event_npc_gossip` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
859                continue;
860            }
861
862            mNPCGossipIds[guid]=EventNPCGossipIdPair(event_id, textid);
863
864            ++count;
865
866        } while( result->NextRow() );
867        sLog.outString();
868        sLog.outString( ">> Loaded %u npc gossip textids in game events", count );
869
870        delete result;
871    }
872
873    // set all flags to 0
874    mGameEventBattleGroundHolidays.resize(mGameEvent.size(),0);
875    // load game event battleground flags
876    //                                   0     1
877    result = WorldDatabase.Query("SELECT event, bgflag FROM game_event_battleground_holiday");
878
879    count = 0;
880    if( !result )
881    {
882        barGoLink bar3(1);
883        bar3.step();
884
885        sLog.outString();
886        sLog.outString(">> Loaded %u battleground holidays in game events", count );
887    }
888    else
889    {
890
891        barGoLink bar3( result->GetRowCount() );
892        do
893        {
894            Field *fields = result->Fetch();
895
896            bar3.step();
897
898            uint16 event_id = fields[0].GetUInt16();
899
900            if(event_id >= mGameEvent.size())
901            {
902                sLog.outErrorDb("`game_event_battleground_holiday` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
903                continue;
904            }
905
906            ++count;
907
908            mGameEventBattleGroundHolidays[event_id] = fields[1].GetUInt32();
909
910        } while( result->NextRow() );
911        sLog.outString();
912        sLog.outString( ">> Loaded %u battleground holidays in game events", count );
913
914        delete result;
915    }
916}
917
918uint32 GameEvent::GetNPCFlag(Creature * cr)
919{
920    uint32 mask = 0;
921    uint32 guid = cr->GetDBTableGUIDLow();
922
923    for(ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr)
924    {
925        for(NPCFlagList::iterator itr = mGameEventNPCFlags[*e_itr].begin();
926            itr != mGameEventNPCFlags[*e_itr].end();
927            ++ itr)
928            if(itr->first == guid)
929                mask |= itr->second;
930    }
931
932    return mask;
933}
934
935uint32 GameEvent::GetNpcTextId(uint32 guid)
936{
937    GuidEventNpcGossipIdMap::iterator itr = mNPCGossipIds.find(guid);
938    if(itr != mNPCGossipIds.end())
939        if(IsActiveEvent(itr->second.first))
940            return itr->second.second;
941    return 0;
942}
943
944uint32 GameEvent::Initialize()                              // return the next event delay in ms
945{
946    m_ActiveEvents.clear();
947    uint32 delay = Update();
948    sLog.outBasic("Game Event system initialized." );
949    isSystemInit = true;
950    return delay;
951}
952
953uint32 GameEvent::Update()                                  // return the next event delay in ms
954{
955    time_t currenttime = time(NULL);
956    uint32 nextEventDelay = max_ge_check_delay;             // 1 day
957    uint32 calcDelay;
958    std::set<uint16> activate, deactivate;
959    for (uint16 itr = 1; itr < mGameEvent.size(); itr++)
960    {
961        // must do the activating first, and after that the deactivating
962        // so first queue it
963        //sLog.outErrorDb("Checking event %u",itr);
964        if (CheckOneGameEvent(itr))
965        {
966            // if the world event is in NEXTPHASE state, and the time has passed to finish this event, then do so
967            if (mGameEvent[itr].state == GAMEEVENT_WORLD_NEXTPHASE && mGameEvent[itr].nextstart <= currenttime)
968            {
969                // set this event to finished, null the nextstart time
970                mGameEvent[itr].state = GAMEEVENT_WORLD_FINISHED;
971                mGameEvent[itr].nextstart = 0;
972                // save the state of this gameevent
973                SaveWorldEventStateToDB(itr);
974                // queue for deactivation
975                if(IsActiveEvent(itr))
976                    deactivate.insert(itr);
977                // go to next event, this no longer needs an event update timer
978                continue;
979            }
980            else if (mGameEvent[itr].state == GAMEEVENT_WORLD_CONDITIONS && CheckOneGameEventConditions(itr))
981                // changed, save to DB the gameevent state, will be updated in next update cycle
982                SaveWorldEventStateToDB(itr);
983
984            //sLog.outDebug("GameEvent %u is active",itr->first);
985            // queue for activation
986            if (!IsActiveEvent(itr))
987                activate.insert(itr);
988        }
989        else
990        {
991            //sLog.outDebug("GameEvent %u is not active",itr->first);
992            if (IsActiveEvent(itr))
993                deactivate.insert(itr);
994            else
995            {
996                if (!isSystemInit)
997                {
998                    int16 event_nid = (-1) * (itr);
999                    // spawn all negative ones for this event
1000                    GameEventSpawn(event_nid);
1001                }
1002            }
1003        }
1004        calcDelay = NextCheck(itr);
1005        if (calcDelay < nextEventDelay)
1006            nextEventDelay = calcDelay;
1007    }
1008    // now activate the queue
1009    // a now activated event can contain a spawn of a to-be-deactivated one
1010    // following the activate - deactivate order, deactivating the first event later will leave the spawn in (wont disappear then reappear clientside)
1011    for(std::set<uint16>::iterator itr = activate.begin(); itr != activate.end(); ++itr)
1012        // start the event
1013        // returns true the started event completed
1014        // in that case, initiate next update in 1 second
1015        if(StartEvent(*itr))
1016            nextEventDelay = 0;
1017    for(std::set<uint16>::iterator itr = deactivate.begin(); itr != deactivate.end(); ++itr)
1018        StopEvent(*itr);
1019    sLog.outBasic("Next game event check in %u seconds.", nextEventDelay + 1);
1020    return (nextEventDelay + 1) * 1000;                     // Add 1 second to be sure event has started/stopped at next call
1021}
1022
1023void GameEvent::UnApplyEvent(uint16 event_id)
1024{
1025    sLog.outString("GameEvent %u \"%s\" removed.", event_id, mGameEvent[event_id].description.c_str());
1026    // un-spawn positive event tagged objects
1027    GameEventUnspawn(event_id);
1028    // spawn negative event tagget objects
1029    int16 event_nid = (-1) * event_id;
1030    GameEventSpawn(event_nid);
1031    // restore equipment or model
1032    ChangeEquipOrModel(event_id, false);
1033    // Remove quests that are events only to non event npc
1034    UpdateEventQuests(event_id, false);
1035    // update npcflags in this event
1036    UpdateEventNPCFlags(event_id);
1037    // remove vendor items
1038    UpdateEventNPCVendor(event_id, false);
1039    // update bg holiday
1040    UpdateBattleGroundSettings();
1041}
1042
1043void GameEvent::ApplyNewEvent(uint16 event_id)
1044{
1045    switch(sWorld.getConfig(CONFIG_EVENT_ANNOUNCE))
1046    {
1047        case 0:                                             // disable
1048            break;
1049        case 1:                                             // announce events
1050            sWorld.SendWorldText(LANG_EVENTMESSAGE, mGameEvent[event_id].description.c_str());
1051            break;
1052    }
1053
1054    sLog.outString("GameEvent %u \"%s\" started.", event_id, mGameEvent[event_id].description.c_str());
1055
1056    // spawn positive event tagget objects
1057    GameEventSpawn(event_id);
1058    // un-spawn negative event tagged objects
1059    int16 event_nid = (-1) * event_id;
1060    GameEventUnspawn(event_nid);
1061    // Change equipement or model
1062    ChangeEquipOrModel(event_id, true);
1063    // Add quests that are events only to non event npc
1064    UpdateEventQuests(event_id, true);
1065    // update npcflags in this event
1066    UpdateEventNPCFlags(event_id);
1067    // add vendor items
1068    UpdateEventNPCVendor(event_id, true);
1069    // update bg holiday
1070    UpdateBattleGroundSettings();
1071}
1072
1073void GameEvent::UpdateEventNPCFlags(uint16 event_id)
1074{
1075    // go through the creatures whose npcflags are changed in the event
1076    for(NPCFlagList::iterator itr = mGameEventNPCFlags[event_id].begin(); itr != mGameEventNPCFlags[event_id].end(); ++itr)
1077    {
1078        // get the creature data from the low guid to get the entry, to be able to find out the whole guid
1079        if( CreatureData const* data = objmgr.GetCreatureData(itr->first) )
1080        {
1081            Creature * cr = HashMapHolder<Creature>::Find(MAKE_NEW_GUID(itr->first,data->id,HIGHGUID_UNIT));
1082            // if we found the creature, modify its npcflag
1083            if(cr)
1084            {
1085                uint32 npcflag = GetNPCFlag(cr);
1086                if(const CreatureInfo * ci = cr->GetCreatureInfo())
1087                    npcflag |= ci->npcflag;
1088                cr->SetUInt32Value(UNIT_NPC_FLAGS,npcflag);
1089                // reset gossip options, since the flag change might have added / removed some
1090                cr->ResetGossipOptions();
1091                // update to world
1092                cr->SendUpdateObjectToAllExcept(NULL);
1093            }
1094            // if we didn't find it, then the npcflag will be updated when the creature is loaded
1095        }
1096    }
1097}
1098
1099void GameEvent::UpdateBattleGroundSettings()
1100{
1101    uint32 mask = 0;
1102    for(ActiveEvents::const_iterator itr = m_ActiveEvents.begin(); itr != m_ActiveEvents.end(); ++itr )
1103        mask |= mGameEventBattleGroundHolidays[*itr];
1104    sBattleGroundMgr.SetHolidayWeekends(mask);
1105}
1106
1107void GameEvent::UpdateEventNPCVendor(uint16 event_id, bool activate)
1108{
1109    for(NPCVendorList::iterator itr = mGameEventVendors[event_id].begin(); itr != mGameEventVendors[event_id].end(); ++itr)
1110    {
1111        if(activate)
1112            objmgr.AddVendorItem(itr->entry, itr->item, itr->maxcount, itr->incrtime, itr->ExtendedCost, false);
1113        else
1114            objmgr.RemoveVendorItem(itr->entry, itr->item, false);
1115    }
1116}
1117
1118void GameEvent::GameEventSpawn(int16 event_id)
1119{
1120    int32 internal_event_id = mGameEvent.size() + event_id - 1;
1121
1122    if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size())
1123    {
1124        sLog.outError("GameEvent::GameEventSpawn attempt access to out of range mGameEventCreatureGuids element %i (size: %u)",internal_event_id,mGameEventCreatureGuids.size());
1125        return;
1126    }
1127
1128    for (GuidList::iterator itr = mGameEventCreatureGuids[internal_event_id].begin();itr != mGameEventCreatureGuids[internal_event_id].end();++itr)
1129    {
1130        // Add to correct cell
1131        CreatureData const* data = objmgr.GetCreatureData(*itr);
1132        if (data)
1133        {
1134            objmgr.AddCreatureToGrid(*itr, data);
1135
1136            // Spawn if necessary (loaded grids only)
1137            Map* map = const_cast<Map*>(MapManager::Instance().GetBaseMap(data->mapid));
1138            // We use spawn coords to spawn
1139            if(!map->Instanceable() && !map->IsRemovalGrid(data->posX,data->posY))
1140            {
1141                Creature* pCreature = new Creature;
1142                //sLog.outDebug("Spawning creature %u",*itr);
1143                if (!pCreature->LoadFromDB(*itr, map))
1144                {
1145                    delete pCreature;
1146                }
1147                else
1148                {
1149                    map->Add(pCreature);
1150                }
1151            }
1152        }
1153    }
1154
1155    if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size())
1156    {
1157        sLog.outError("GameEvent::GameEventSpawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %u)",internal_event_id,mGameEventGameobjectGuids.size());
1158        return;
1159    }
1160
1161    for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin();itr != mGameEventGameobjectGuids[internal_event_id].end();++itr)
1162    {
1163        // Add to correct cell
1164        GameObjectData const* data = objmgr.GetGOData(*itr);
1165        if (data)
1166        {
1167            objmgr.AddGameobjectToGrid(*itr, data);
1168            // Spawn if necessary (loaded grids only)
1169            // this base map checked as non-instanced and then only existed
1170            Map* map = const_cast<Map*>(MapManager::Instance().GetBaseMap(data->mapid));
1171            // We use current coords to unspawn, not spawn coords since creature can have changed grid
1172            if(!map->Instanceable() && !map->IsRemovalGrid(data->posX, data->posY))
1173            {
1174                GameObject* pGameobject = new GameObject;
1175                //sLog.outDebug("Spawning gameobject %u", *itr);
1176                if (!pGameobject->LoadFromDB(*itr, map))
1177                {
1178                    delete pGameobject;
1179                }
1180                else
1181                {
1182                    if(pGameobject->isSpawnedByDefault())
1183                        map->Add(pGameobject);
1184                }
1185            }
1186        }
1187    }
1188}
1189
1190void GameEvent::GameEventUnspawn(int16 event_id)
1191{
1192    int32 internal_event_id = mGameEvent.size() + event_id - 1;
1193
1194    if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size())
1195    {
1196        sLog.outError("GameEvent::GameEventUnspawn attempt access to out of range mGameEventCreatureGuids element %i (size: %u)",internal_event_id,mGameEventCreatureGuids.size());
1197        return;
1198    }
1199
1200    for (GuidList::iterator itr = mGameEventCreatureGuids[internal_event_id].begin();itr != mGameEventCreatureGuids[internal_event_id].end();++itr)
1201    {
1202        // check if it's needed by another event, if so, don't remove
1203        if( event_id > 0 && hasCreatureActiveEventExcept(*itr,event_id) )
1204            continue;
1205        // Remove the creature from grid
1206        if( CreatureData const* data = objmgr.GetCreatureData(*itr) )
1207        {
1208            objmgr.RemoveCreatureFromGrid(*itr, data);
1209
1210            if( Creature* pCreature = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(*itr, data->id, HIGHGUID_UNIT), (Creature*)NULL) )
1211            {
1212                pCreature->CleanupsBeforeDelete();
1213                pCreature->AddObjectToRemoveList();
1214            }
1215        }
1216    }
1217
1218    if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size())
1219    {
1220        sLog.outError("GameEvent::GameEventUnspawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %u)",internal_event_id,mGameEventGameobjectGuids.size());
1221        return;
1222    }
1223
1224    for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin();itr != mGameEventGameobjectGuids[internal_event_id].end();++itr)
1225    {
1226        // check if it's needed by another event, if so, don't remove
1227        if( event_id >0 && hasGameObjectActiveEventExcept(*itr,event_id) )
1228            continue;
1229        // Remove the gameobject from grid
1230        if(GameObjectData const* data = objmgr.GetGOData(*itr))
1231        {
1232            objmgr.RemoveGameobjectFromGrid(*itr, data);
1233
1234            if( GameObject* pGameobject = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(*itr, data->id, HIGHGUID_GAMEOBJECT), (GameObject*)NULL) )
1235                pGameobject->AddObjectToRemoveList();
1236        }
1237    }
1238}
1239
1240void GameEvent::ChangeEquipOrModel(int16 event_id, bool activate)
1241{
1242    for(ModelEquipList::iterator itr = mGameEventModelEquip[event_id].begin();itr != mGameEventModelEquip[event_id].end();++itr)
1243    {
1244        // Remove the creature from grid
1245        CreatureData const* data = objmgr.GetCreatureData(itr->first);
1246        if(!data)
1247            continue;
1248
1249        // Update if spawned
1250        Creature* pCreature = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(itr->first, data->id,HIGHGUID_UNIT), (Creature*)NULL);
1251        if (pCreature)
1252        {
1253            if (activate)
1254            {
1255                itr->second.equipement_id_prev = pCreature->GetCurrentEquipmentId();
1256                itr->second.modelid_prev = pCreature->GetDisplayId();
1257                pCreature->LoadEquipment(itr->second.equipment_id, true);
1258                if (itr->second.modelid >0 && itr->second.modelid_prev != itr->second.modelid)
1259                {
1260                    CreatureModelInfo const *minfo = objmgr.GetCreatureModelInfo(itr->second.modelid);
1261                    if (minfo)
1262                    {
1263                        pCreature->SetDisplayId(itr->second.modelid);
1264                        pCreature->SetNativeDisplayId(itr->second.modelid);
1265                        pCreature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS,minfo->bounding_radius);
1266                        pCreature->SetFloatValue(UNIT_FIELD_COMBATREACH,minfo->combat_reach );
1267                    }
1268                }
1269            }
1270            else
1271            {
1272                pCreature->LoadEquipment(itr->second.equipement_id_prev, true);
1273                if (itr->second.modelid_prev >0 && itr->second.modelid_prev != itr->second.modelid)
1274                {
1275                    CreatureModelInfo const *minfo = objmgr.GetCreatureModelInfo(itr->second.modelid_prev);
1276                    if (minfo)
1277                    {
1278                        pCreature->SetDisplayId(itr->second.modelid_prev);
1279                        pCreature->SetNativeDisplayId(itr->second.modelid_prev);
1280                        pCreature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS,minfo->bounding_radius);
1281                        pCreature->SetFloatValue(UNIT_FIELD_COMBATREACH,minfo->combat_reach );
1282                    }
1283                }
1284            }
1285        }
1286        else                                                // If not spawned
1287        {
1288            CreatureData const* data = objmgr.GetCreatureData(itr->first);
1289            if (data && activate)
1290            {
1291                CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(data->id);
1292                uint32 display_id = objmgr.ChooseDisplayId(0,cinfo,data);
1293                CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id);
1294                if (minfo)
1295                    display_id = minfo->modelid;
1296                if (data->equipmentId == 0)
1297                    itr->second.equipement_id_prev = cinfo->equipmentId;
1298                else if (data->equipmentId != -1)
1299                    itr->second.equipement_id_prev = data->equipmentId;
1300                itr->second.modelid_prev = display_id;
1301            }
1302        }
1303        // now last step: put in data
1304                                                            // just to have write access to it
1305        CreatureData& data2 = objmgr.NewOrExistCreatureData(itr->first);
1306        if (activate)
1307        {
1308            data2.displayid = itr->second.modelid;
1309            data2.equipmentId = itr->second.equipment_id;
1310        }
1311        else
1312        {
1313            data2.displayid = itr->second.modelid_prev;
1314            data2.equipmentId = itr->second.equipement_id_prev;
1315        }
1316    }
1317}
1318
1319bool GameEvent::hasCreatureQuestActiveEventExcept(uint32 quest_id, uint16 event_id)
1320{
1321    for(ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr)
1322    {
1323        if((*e_itr) != event_id)
1324            for(QuestRelList::iterator itr = mGameEventCreatureQuests[*e_itr].begin();
1325                itr != mGameEventCreatureQuests[*e_itr].end();
1326                ++ itr)
1327                if(itr->second == quest_id)
1328                    return true;
1329    }
1330    return false;
1331}
1332
1333bool GameEvent::hasGameObjectQuestActiveEventExcept(uint32 quest_id, uint16 event_id)
1334{
1335    for(ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr)
1336    {
1337        if((*e_itr) != event_id)
1338            for(QuestRelList::iterator itr = mGameEventGameObjectQuests[*e_itr].begin();
1339                itr != mGameEventGameObjectQuests[*e_itr].end();
1340                ++ itr)
1341                if(itr->second == quest_id)
1342                    return true;
1343    }
1344    return false;
1345}
1346bool GameEvent::hasCreatureActiveEventExcept(uint32 creature_id, uint16 event_id)
1347{
1348    for(ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr)
1349    {
1350        if((*e_itr) != event_id)
1351        {
1352            int32 internal_event_id = mGameEvent.size() + (*e_itr) - 1;
1353            for(GuidList::iterator itr = mGameEventCreatureGuids[internal_event_id].begin();
1354                itr != mGameEventCreatureGuids[internal_event_id].end();
1355                ++ itr)
1356                if(*itr == creature_id)
1357                    return true;
1358        }
1359    }
1360    return false;
1361}
1362bool GameEvent::hasGameObjectActiveEventExcept(uint32 go_id, uint16 event_id)
1363{
1364    for(ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr)
1365    {
1366        if((*e_itr) != event_id)
1367        {
1368            int32 internal_event_id = mGameEvent.size() + (*e_itr) - 1;
1369            for(GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin();
1370                itr != mGameEventGameobjectGuids[internal_event_id].end();
1371                ++ itr)
1372                if(*itr == go_id)
1373                    return true;
1374        }
1375    }
1376    return false;
1377}
1378
1379void GameEvent::UpdateEventQuests(uint16 event_id, bool Activate)
1380{
1381    QuestRelList::iterator itr;
1382    for (itr = mGameEventCreatureQuests[event_id].begin();itr != mGameEventCreatureQuests[event_id].end();++itr)
1383    {
1384        QuestRelations &CreatureQuestMap = objmgr.mCreatureQuestRelations;
1385        if (Activate)                                       // Add the pair(id,quest) to the multimap
1386            CreatureQuestMap.insert(QuestRelations::value_type(itr->first, itr->second));
1387        else
1388        {   
1389            if(!hasCreatureQuestActiveEventExcept(itr->second,event_id))
1390            {
1391                // Remove the pair(id,quest) from the multimap
1392                QuestRelations::iterator qitr = CreatureQuestMap.find(itr->first);
1393                if (qitr == CreatureQuestMap.end())
1394                    continue;
1395                QuestRelations::iterator lastElement = CreatureQuestMap.upper_bound(itr->first);
1396                for ( ;qitr != lastElement;++qitr)
1397                {
1398                    if (qitr->second == itr->second)
1399                    {
1400                        CreatureQuestMap.erase(qitr);           // iterator is now no more valid
1401                        break;                                  // but we can exit loop since the element is found
1402                    }
1403                }
1404            }
1405        }
1406    }
1407    for (itr = mGameEventGameObjectQuests[event_id].begin();itr != mGameEventGameObjectQuests[event_id].end();++itr)
1408    {
1409        QuestRelations &GameObjectQuestMap = objmgr.mGOQuestRelations;
1410        if (Activate)                                       // Add the pair(id,quest) to the multimap
1411            GameObjectQuestMap.insert(QuestRelations::value_type(itr->first, itr->second));
1412        else
1413        {   
1414            if(!hasGameObjectQuestActiveEventExcept(itr->second,event_id))
1415            {
1416                // Remove the pair(id,quest) from the multimap
1417                QuestRelations::iterator qitr = GameObjectQuestMap.find(itr->first);
1418                if (qitr == GameObjectQuestMap.end())
1419                    continue;
1420                QuestRelations::iterator lastElement = GameObjectQuestMap.upper_bound(itr->first);
1421                for ( ;qitr != lastElement;++qitr)
1422                {
1423                    if (qitr->second == itr->second)
1424                    {
1425                        GameObjectQuestMap.erase(qitr);           // iterator is now no more valid
1426                        break;                                  // but we can exit loop since the element is found
1427                    }
1428                }
1429            }
1430        }
1431    }}
1432
1433GameEvent::GameEvent()
1434{
1435    isSystemInit = false;
1436}
1437
1438void GameEvent::HandleQuestComplete(uint32 quest_id)
1439{
1440    // translate the quest to event and condition
1441    QuestIdToEventConditionMap::iterator itr = mQuestToEventConditions.find(quest_id);
1442    // quest is registered
1443    if(itr != mQuestToEventConditions.end())
1444    {
1445        uint16 event_id = itr->second.event_id;
1446        uint32 condition = itr->second.condition;
1447        float num = itr->second.num;
1448
1449        // the event is not active, so return, don't increase condition finishes
1450        if(!IsActiveEvent(event_id))
1451            return;
1452        // not in correct phase, return
1453        if(mGameEvent[event_id].state != GAMEEVENT_WORLD_CONDITIONS)
1454            return;
1455        std::map<uint32,GameEventFinishCondition>::iterator citr = mGameEvent[event_id].conditions.find(condition);
1456        // condition is registered
1457        if(citr != mGameEvent[event_id].conditions.end())
1458        {
1459            // increase the done count, only if less then the req
1460            if(citr->second.done < citr->second.reqNum)
1461            {
1462                citr->second.done += num;
1463                // check max limit
1464                if(citr->second.done > citr->second.reqNum)
1465                    citr->second.done = citr->second.reqNum;
1466                // save the change to db
1467                CharacterDatabase.BeginTransaction();
1468                CharacterDatabase.PExecute("DELETE FROM game_event_condition_save WHERE event_id = '%u' AND condition_id = '%u'",event_id,condition);
1469                CharacterDatabase.PExecute("INSERT INTO game_event_condition_save (event_id, condition_id, done) VALUES (%u,%u,%f)",event_id,condition,citr->second.done);
1470                CharacterDatabase.CommitTransaction();
1471                // check if all conditions are met, if so, update the event state
1472                if(CheckOneGameEventConditions(event_id))
1473                {
1474                    // changed, save to DB the gameevent state
1475                    SaveWorldEventStateToDB(event_id);
1476                    // force update events to set timer
1477                    sWorld.ForceGameEventUpdate();
1478                }
1479            }
1480        }
1481    }
1482}
1483
1484bool GameEvent::CheckOneGameEventConditions(uint16 event_id)
1485{
1486    for(std::map<uint32,GameEventFinishCondition>::iterator itr = mGameEvent[event_id].conditions.begin(); itr != mGameEvent[event_id].conditions.end(); ++itr)
1487        if(itr->second.done < itr->second.reqNum)
1488            // return false if a condition doesn't match
1489            return false;
1490    // set the phase
1491    mGameEvent[event_id].state = GAMEEVENT_WORLD_NEXTPHASE;
1492    // set the followup events' start time
1493    if(!mGameEvent[event_id].nextstart)
1494    {
1495        time_t currenttime = time(NULL);
1496        mGameEvent[event_id].nextstart = currenttime + mGameEvent[event_id].length * 60;
1497    }
1498    return true;
1499}
1500
1501void GameEvent::SaveWorldEventStateToDB(uint16 event_id)
1502{
1503    CharacterDatabase.BeginTransaction();
1504    CharacterDatabase.PExecute("DELETE FROM game_event_save WHERE event_id = '%u'",event_id);
1505    if(mGameEvent[event_id].nextstart)
1506        CharacterDatabase.PExecute("INSERT INTO game_event_save (event_id, state, next_start) VALUES ('%u','%u',FROM_UNIXTIME('"I64FMTD"'))",event_id,mGameEvent[event_id].state,mGameEvent[event_id].nextstart);
1507    else
1508        CharacterDatabase.PExecute("INSERT INTO game_event_save (event_id, state, next_start) VALUES ('%u','%u','0000-00-00 00:00:00')",event_id,mGameEvent[event_id].state);
1509    CharacterDatabase.CommitTransaction();
1510}
1511
1512void GameEvent::HandleWorldEventGossip(Player *plr, Creature *c)
1513{
1514    // this function is used to send world state update before sending gossip menu
1515    // find the npc's gossip id (if set) in an active game event
1516    // if present, send the event's world states
1517    GuidEventNpcGossipIdMap::iterator itr = mNPCGossipIds.find(c->GetDBTableGUIDLow());
1518    if(itr != mNPCGossipIds.end())
1519        if(IsActiveEvent(itr->second.first))
1520            // send world state updates to the player about the progress
1521            SendWorldStateUpdate(plr, itr->second.first);
1522}
1523
1524void GameEvent::SendWorldStateUpdate(Player * plr, uint16 event_id)
1525{
1526    std::map<uint32,GameEventFinishCondition>::iterator itr;
1527    for(itr = mGameEvent[event_id].conditions.begin(); itr !=mGameEvent[event_id].conditions.end(); ++itr)
1528    {
1529        if(itr->second.done_world_state)
1530            plr->SendUpdateWorldState(itr->second.done_world_state, (uint32)(itr->second.done));
1531        if(itr->second.max_world_state)
1532            plr->SendUpdateWorldState(itr->second.max_world_state, (uint32)(itr->second.reqNum));
1533    }
1534}
Note: See TracBrowser for help on using the browser.