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

Revision 44, 54.5 kB (checked in by yumileroy, 17 years ago)

[svn] * Merge Temp dev SVN with Assembla.
* Changes include:

  • Implementation of w12x's Outdoor PvP and Game Event Systems.
  • Temporary removal of IRC Chat Bot (until infinite loop when disabled is fixed).
  • All mangos -> trinity (to convert your mangos_string table, please run mangos_string_to_trinity_string.sql).
  • Improved Config cleanup.
  • And many more changes.

Original author: Seline
Date: 2008-10-14 11:57:03-05:00

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