diff options
24 files changed, 610 insertions, 372 deletions
diff --git a/sql/updates/world/2014_06_03_world_creature.sql b/sql/updates/world/2014_06_03_world_creature.sql new file mode 100644 index 00000000000..81953785fd5 --- /dev/null +++ b/sql/updates/world/2014_06_03_world_creature.sql @@ -0,0 +1 @@ +UPDATE `creature` SET `phaseMask`=1 WHERE `id`=21347; diff --git a/sql/updates/world/2014_06_06_00_world_misc.sql b/sql/updates/world/2014_06_06_00_world_misc.sql new file mode 100644 index 00000000000..a1d4dd621c0 --- /dev/null +++ b/sql/updates/world/2014_06_06_00_world_misc.sql @@ -0,0 +1,92 @@ + +UPDATE creature_template SET ScriptName="npc_imp_in_a_ball" WHERE entry=23224; + +SET @COUNT := 0; +DELETE FROM creature_text WHERE entry=23224; +INSERT INTO creature_text (`entry`, `groupid`, `id`, `text`, `type`, `language`, `probability`, `emote`, `duration`, `sound`, `comment`, `BroadcastTextID`) VALUES +(23224, 0, @COUNT := @COUNT + 1, 'Yes, unless I have anything to do with it.', 15, 0, 100, 0, 0, 0, '', 21157), +(23224, 0, @COUNT := @COUNT + 1, 'I see that happening sometime between tomorrow and the next decade. Definitely.', 15, 0, 100, 0, 0, 0, '', 21158), +(23224, 0, @COUNT := @COUNT + 1, 'Looks good for you...and bad for me.', 15, 0, 100, 0, 0, 0, '', 21160), +(23224, 0, @COUNT := @COUNT + 1, 'I\'ve consulted my fellow imps, and we think YES, except for that one imp.', 15, 0, 100, 0, 0, 0, '', 21161), +(23224, 0, @COUNT := @COUNT + 1, 'The outlook is positive, but I\'m still negative.', 15, 0, 100, 0, 0, 0, '', 21162), +(23224, 0, @COUNT := @COUNT + 1, 'It pains me to say this, but "Yes".', 15, 0, 100, 0, 0, 0, '', 21163), +(23224, 0, @COUNT := @COUNT + 1, 'When dwarves fly! They do? Then yes!', 15, 0, 100, 0, 0, 0, '', 21164), +(23224, 0, @COUNT := @COUNT + 1, 'Sure, but you\'re not going to like it.', 15, 0, 100, 0, 0, 0, '', 21165), +(23224, 0, @COUNT := @COUNT + 1, 'Be quiet \'bout what you hear and see around here, $r.', 15, 0, 100, 0, 0, 0, '', 21166), +(23224, 0, @COUNT := @COUNT + 1, 'Unfortunately... yes.', 15, 0, 100, 0, 0, 0, '', 21169), +(23224, 0, @COUNT := @COUNT + 1, 'I can\'t see why not, although, I can\'t see a lot of things right now.', 15, 0, 100, 0, 0, 0, '', 21170), +(23224, 0, @COUNT := @COUNT + 1, 'I would bet your soul on it.', 15, 0, 100, 0, 0, 0, '', 21171), +(23224, 0, @COUNT := @COUNT + 1, 'Yes, but if anyone asks... It wasn\'t me who told you.', 15, 0, 100, 0, 0, 0, '', 21172), +(23224, 0, @COUNT := @COUNT + 1, 'Didn\'t you already ask that once? Yes already!', 15, 0, 100, 0, 0, 0, '', 21173), +(23224, 0, @COUNT := @COUNT + 1, 'Please... Is Kil\'jaeden red?', 15, 0, 100, 0, 0, 0, '', 21175), +(23224, 0, @COUNT := @COUNT + 1, 'Yeah, sure. You just keep thinking that.', 15, 0, 100, 0, 0, 0, '', 21176), +(23224, 0, @COUNT := @COUNT + 1, 'I suppose.', 15, 0, 100, 0, 0, 0, '', 21177), +(23224, 0, @COUNT := @COUNT + 1, 'Definitely.', 15, 0, 100, 0, 0, 0, '', 21178), +(23224, 0, @COUNT := @COUNT + 1, 'Jump three times and dance for ten minutes and it will definitely happen!', 15, 0, 100, 0, 0, 0, '', 21179), +(23224, 0, @COUNT := @COUNT + 1, 'Word on the peninsula is YES!', 15, 0, 100, 0, 0, 0, '', 21180), +(23224, 0, @COUNT := @COUNT + 1, 'Oh, that one\'s for sure.', 15, 0, 100, 0, 0, 0, '', 21181), +(23224, 0, @COUNT := @COUNT + 1, 'Yes, but not in the way you imagine.', 15, 0, 100, 0, 0, 0, '', 21182), +(23224, 0, @COUNT := @COUNT + 1, 'Yes, yes, a thousand times, yes already!', 15, 0, 100, 0, 0, 0, '', 21183), +(23224, 0, @COUNT := @COUNT + 1, 'Yes, now stop pestering me!', 15, 0, 100, 0, 0, 0, '', 21184), +(23224, 0, @COUNT := @COUNT + 1, 'The answer will be a yes if you let me out of here.', 15, 0, 100, 0, 0, 0, '', 21185), +(23224, 0, @COUNT := @COUNT + 1, 'It\'s as sure as the warts on my backside!', 15, 0, 100, 0, 0, 0, '', 21186), +(23224, 0, @COUNT := @COUNT + 1, 'The answer is yes in here, I don\'t see why it would be any different out there.', 15, 0, 100, 0, 0, 0, '', 21187), +(23224, 0, @COUNT := @COUNT + 1, 'Three words - "ab - so - lutely"!', 15, 0, 100, 0, 0, 0, '', 21188), +(23224, 0, @COUNT := @COUNT + 1, 'Yes, but I hoped I would never have to answer that.', 15, 0, 100, 0, 0, 0, '', 21189), +(23224, 0, @COUNT := @COUNT + 1, 'Ask me again later - I\'m trying to scratch my nose and it\'s hard to concentrate.', 15, 0, 100, 0, 0, 0, '', 21190), +(23224, 0, @COUNT := @COUNT + 1, 'Why should I answer that? Do you tell fortunes when people shake YOU?', 15, 0, 100, 0, 0, 0, '', 21191), +(23224, 0, @COUNT := @COUNT + 1, 'Yes, it will rain. That\'s not what you asked? Too bad!', 15, 0, 100, 0, 0, 0, '', 21192), +(23224, 0, @COUNT := @COUNT + 1, 'I\'m sorry, I can only speak Demonic.', 15, 0, 100, 0, 0, 0, '', 21193), +(23224, 0, @COUNT := @COUNT + 1, 'Yes! I mean no! I mean... which answer gets me out of here?', 15, 0, 100, 0, 0, 0, '', 21194), +(23224, 0, @COUNT := @COUNT + 1, 'Yes, No, Maybe so.', 15, 0, 100, 0, 0, 0, '', 21195), +(23224, 0, @COUNT := @COUNT + 1, 'It won\'t matter, you\'ll be dead by tomorrow.', 15, 0, 100, 0, 0, 0, '', 21196), +(23224, 0, @COUNT := @COUNT + 1, 'You should be asking "Is that rogue behind me going to kill me?"', 15, 0, 100, 0, 0, 0, '', 21197), +(23224, 0, @COUNT := @COUNT + 1, 'It\'s times like these that I wish I had a longer cooldown.', 15, 0, 100, 0, 0, 0, '', 21198), +(23224, 0, @COUNT := @COUNT + 1, '%s shrugs. Who knows?', 15, 0, 100, 0, 0, 0, '', 21199), +(23224, 0, @COUNT := @COUNT + 1, 'It\'s like my mother always said: [Demonic] "Razxx khaj jhashxx xashjx."', 15, 0, 100, 0, 0, 0, '', 21205), +(23224, 0, @COUNT := @COUNT + 1, '%s is ignoring you.', 15, 0, 100, 0, 0, 0, '', 21206), +(23224, 0, @COUNT := @COUNT + 1, 'Why me? Why not a goblin, or a gnome...', 15, 0, 100, 0, 0, 0, '', 21207), +(23224, 0, @COUNT := @COUNT + 1, 'What happens in the twisting nether, stays in the twisting nether.', 15, 0, 100, 0, 0, 0, '', 21208), +(23224, 0, @COUNT := @COUNT + 1, 'Avoid taking unnecessary gambles. Your lucky numbers are two, two and half, and eleven-teen.', 15, 0, 100, 0, 0, 0, '', 21209), +(23224, 0, @COUNT := @COUNT + 1, 'Wouldn\'t you like to know?', 15, 0, 100, 0, 0, 0, '', 21210), +(23224, 0, @COUNT := @COUNT + 1, 'Oh, oh, oh! I can see this one clearly... Nah, lost it.', 15, 0, 100, 0, 0, 0, '', 21211), +(23224, 0, @COUNT := @COUNT + 1, 'This was NOT in my contract!', 15, 0, 100, 0, 0, 0, '', 21212), +(23224, 0, @COUNT := @COUNT + 1, 'XRA RAHKI MAZIZRA!', 15, 0, 100, 0, 0, 0, '', 21213), +(23224, 0, @COUNT := @COUNT + 1, '4 8 15 16 23 42', 15, 0, 100, 0, 0, 0, '', 21214), +(23224, 0, @COUNT := @COUNT + 1, 'Are you making fun of me?', 15, 0, 100, 0, 0, 0, '', 21215), +(23224, 0, @COUNT := @COUNT + 1, 'What kind of imp do you think I am?', 15, 0, 100, 0, 0, 0, '', 21216), +(23224, 0, @COUNT := @COUNT + 1, 'Say please.', 15, 0, 100, 0, 0, 0, '', 21217), +(23224, 0, @COUNT := @COUNT + 1, 'Want to trade places?', 15, 0, 100, 0, 0, 0, '', 21218), +(23224, 0, @COUNT := @COUNT + 1, 'Do you ask this question to everything that\'s trapped in a ball?', 15, 0, 100, 0, 0, 0, '', 21219), +(23224, 0, @COUNT := @COUNT + 1, 'Hey! You try arranging furniture with some jerk shaking your house!', 15, 0, 100, 0, 0, 0, '', 21220), +(23224, 0, @COUNT := @COUNT + 1, 'I can make that happen. Just sign below the dotted line...', 15, 0, 100, 0, 0, 0, '', 21221), +(23224, 0, @COUNT := @COUNT + 1, 'Reply hazy and slightly damp. Dry thoroughly and try again.', 15, 0, 100, 0, 0, 0, '', 21222), +(23224, 0, @COUNT := @COUNT + 1, 'Concentrate (on releasing me from this infernal prison) and try again later.', 15, 0, 100, 0, 0, 0, '', 21223), +(23224, 0, @COUNT := @COUNT + 1, 'Please insert 25 silver pieces and try again.', 15, 0, 100, 0, 0, 0, '', 21224), +(23224, 0, @COUNT := @COUNT + 1, 'Are you my pal, Danny?', 15, 0, 100, 0, 0, 0, '', 21225), +(23224, 0, @COUNT := @COUNT + 1, 'You remember the time you tried to drill that hole in your head?', 15, 0, 100, 0, 0, 0, '', 21226), +(23224, 0, @COUNT := @COUNT + 1, 'Oh that\'s right, don\'t make any effort to make your own fortune!', 15, 0, 100, 0, 0, 0, '', 21227), +(23224, 0, @COUNT := @COUNT + 1, 'Two words - imp-possible!', 15, 0, 100, 0, 0, 0, '', 21228), +(23224, 0, @COUNT := @COUNT + 1, 'You need Arcane Intellect, because that answer is obvious! NO!', 15, 0, 100, 0, 0, 0, '', 21229), +(23224, 0, @COUNT := @COUNT + 1, 'Not on your life!', 15, 0, 100, 0, 0, 0, '', 21230), +(23224, 0, @COUNT := @COUNT + 1, 'I don\'t have to be a fortune-telling imp to know the answer to that one - NO!', 15, 0, 100, 0, 0, 0, '', 21231), +(23224, 0, @COUNT := @COUNT + 1, 'The odds are 32.33 (repeating of course) percent chance of success.', 15, 0, 100, 0, 0, 0, '', 21232), +(23224, 0, @COUNT := @COUNT + 1, 'When Blackrock freezes over!', 15, 0, 100, 0, 0, 0, '', 21233), +(23224, 0, @COUNT := @COUNT + 1, 'Hahahaha, you\'re kidding right?', 15, 0, 100, 0, 0, 0, '', 21234), +(23224, 0, @COUNT := @COUNT + 1, 'Inconceivable!', 15, 0, 100, 0, 0, 0, '', 21235), +(23224, 0, @COUNT := @COUNT + 1, 'My sources say "no". Before the torture, that is.', 15, 0, 100, 0, 0, 0, '', 21236), +(23224, 0, @COUNT := @COUNT + 1, 'That\'s about as likely as me getting out of this ball.', 15, 0, 100, 0, 0, 0, '', 21237), +(23224, 0, @COUNT := @COUNT + 1, 'You picked the wrong time to shake me today, buddy! Prepare for disappointment.', 15, 0, 100, 0, 0, 0, '', 21238), +(23224, 0, @COUNT := @COUNT + 1, 'Not unless you\'re some kind of super-person. And don\'t kid yourself, you\'re not.', 15, 0, 100, 0, 0, 0, '', 21239), +(23224, 0, @COUNT := @COUNT + 1, 'That\'s about as likely as me getting a date with a succubus.', 15, 0, 100, 0, 0, 0, '', 21240), +(23224, 0, @COUNT := @COUNT + 1, 'My fortune telling powers are immeasurable - your chances are though: NO CHANCE', 15, 0, 100, 0, 0, 0, '', 21241), +(23224, 0, @COUNT := @COUNT + 1, 'NO - and don\'t try shaking me again for a better answer!', 15, 0, 100, 0, 0, 0, '', 21242), +(23224, 0, @COUNT := @COUNT + 1, 'Yes is my answer...', 15, 0, 100, 0, 0, 0, '', 21243), +(23224, 0, @COUNT := @COUNT + 1, '...NOT!', 15, 0, 100, 0, 0, 0, '', 21244), +(23224, 0, @COUNT := @COUNT + 1, 'I\'m gonna have to give this one the big N-O.', 15, 0, 100, 0, 0, 0, '', 21245), +(23224, 0, @COUNT := @COUNT + 1, 'The outlook is very bad - for YOU that is! Haha, take it!', 15, 0, 100, 0, 0, 0, '', 21246), +(23224, 0, @COUNT := @COUNT + 1, 'Survey says: BZZZT!', 15, 0, 100, 0, 0, 0, '', 21247); + +DELETE FROM spell_linked_spell WHERE spell_trigger=40527 AND spell_effect=40528; +INSERT INTO spell_linked_spell (`spell_trigger`, `spell_effect`, `type`, `comment`) VALUES +(40527, 40528, 0, 'Imp in a Bottle'); diff --git a/sql/updates/world/2014_06_06_00_world_quest_template.sql b/sql/updates/world/2014_06_06_00_world_quest_template.sql new file mode 100644 index 00000000000..6d174f9a7b0 --- /dev/null +++ b/sql/updates/world/2014_06_06_00_world_quest_template.sql @@ -0,0 +1,4 @@ +-- +UPDATE `quest_template` SET `RequestItemsText` = 'Supposedly this Malcin is outside Razorfen Downs. There''s no question - he has to die.$b$bMy contacts in Orgrimmar tell me their scouts have found signs of the Plague down there. The quilboar are showing signs of being plagued, too; they''re much more powerful. Whatever the Scourge are doing down there needs to end. Now.$b$bFind this Malcin and kill him. Report back here when he''s dead.' WHERE `id` = 14353; +UPDATE `quest_template` SET `OfferRewardText` = 'Good work, $C!$b$bYou''ve done the Horde proud by stopping the Scourge from setting down roots on our soil. An act like that deserves a reward, and the Forsaken have enough lying around that I''m sure they can spare a few things.$b$bWe may not know everything they''ve done in the Downs, but we''ll find out. They can''t slink around in the dark forever.' WHERE `id` = 14353; +UPDATE `quest_template` SET `OfferRewardText` = 'I''m sure Sylvanas will be glad to have that problem taken care of, $N. The task I gave you wasn''t easy, but here you stand, victorious. That commands respect, and what you''ve done won''t be forgotten.' WHERE `id` = 14355; diff --git a/sql/updates/world/2014_06_07_00_world_misc.sql b/sql/updates/world/2014_06_07_00_world_misc.sql new file mode 100644 index 00000000000..ff09f8f552e --- /dev/null +++ b/sql/updates/world/2014_06_07_00_world_misc.sql @@ -0,0 +1,31 @@ +UPDATE `creature_template` SET `gossip_menu_id`=3310 WHERE `entry`=11216; + +DELETE FROM `gossip_menu_option` WHERE `menu_id` in(3310,3309,3308,3307,3306,3305,3304,3303,3302,3301); +INSERT INTO `gossip_menu_option` (`menu_id`, `id`, `option_icon`, `option_text`, `OptionBroadcastTextID`, `option_id`, `npc_option_npcflag`, `action_menu_id`, `action_poi_id`, `box_coded`, `box_money`, `box_text`, `BoxBroadcastTextID`) VALUES +(3310, 0, 0, 'The pleasure is mine, madam. Might I ask what it is that you are doing here?', 6645, 1, 1, 3309, 0, 0, 0, '', 0), +(3310, 1, 0, 'Eva, I have lost the Spectral Essence. I need another.', 6799, 1, 1, 0, 0, 0, 0, '', 0), +(3309, 0, 0, 'Until what, Eva? I must know.', 6647, 1, 1, 3308, 0, 0, 0, '', 0), +(3308, 0, 0, 'What do you mean?', 6649, 1, 1, 3307, 0, 0, 0, '', 0), +(3307, 0, 0, 'Why didn''t you just leave?', 6651, 1, 1, 3306, 0, 0, 0, '', 0), +(3306, 0, 0, 'So what happened?', 6653, 1, 1, 3305, 0, 0, 0, '', 0), +(3305, 0, 0, 'No restraints? Just a circle?', 6655, 1, 1, 3304, 0, 0, 0, '', 0), +(3304, 0, 0, 'Tell me more', 6657, 1, 1, 3303, 0, 0, 0, '', 0), +(3303, 0, 0, 'This is an atrocity.', 6659, 1, 1, 3302, 0, 0, 0, '', 0), +(3302, 0, 0, 'I feel sick', 6661, 1, 1, 3301, 0, 0, 0, '', 0), +(3301, 0, 0, 'What can I do to help?', 45678, 1, 1, 0, 0, 0, 0, '', 0); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup` IN (3310); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 3310, 0, 0, 0, 8, 0, 5382, 0, 0, 1, 0, 0, '', 'Gossip Option requires Doctor Theolen Krastinov, the Butcher not rewarded'), +(15, 3310, 0, 0, 0, 9, 0, 5382, 0, 0, 1, 0, 0, '', 'Gossip Option requires Doctor Theolen Krastinov, the Butcher not taken'), +(15, 3310, 0, 0, 0, 28, 0, 5382, 0, 0, 1, 0, 0, '', 'Gossip Option requires Doctor Theolen Krastinov, the Butcher not complete'), +(15, 3310, 1, 0, 0, 8, 0, 5384, 0, 0, 0, 0, 0, '', 'Gossip Option requires Kirtonos the Herald Rewarded'), +(15, 3310, 1, 0, 0, 2, 0, 13544, 1, 0, 1, 0, 0, '', 'Gossip Option requires Player does not have Spectral Essence'); + +UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry` IN (11216); +DELETE FROM `smart_scripts` WHERE `entryorguid` IN(11216) AND `source_type`=0; +INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`,`target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES +(11216,0,0,1,62,0,100,0,3310,1,0,0,85,17672,2,0,0,0,0,7,0,0,0,0,0,0,0,'Eva Sarkhoff - On Gossip Option 1 Selected - Cast Summon Spectral Essence'), +(11216,0,1,0,61,0,100,0,0,0,0,0,72,0,0,0,0,0,0,7,0,0,0,0,0,0,0,'Eva Sarkhoff - Link - Close Gossip'), +(11216,0,3,4,62,0,100,0,3301,0,0,0,7,5382,0,0,0,0,0,7,0,0,0,0,0,0,0,'Eva Sarkhoff - On Gossip Option 0 Selected - Add Quest Doctor Theolen Krastinov, the Butcher'), +(11216,0,4,0,61,0,100,0,0,0,0,0,72,0,0,0,0,0,0,7,0,0,0,0,0,0,0,'Eva Sarkhoff - Link - Close Gossip'); diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index dd8b42deb9f..ac9de00cd10 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -43,6 +43,11 @@ void CreatureAI::Talk(uint8 id, WorldObject const* whisperTarget /*= NULL*/) sCreatureTextMgr->SendChat(me, id, whisperTarget); } +void CreatureAI::TalkToMap(uint8 id, WorldObject const* whisperTarget /*= NULL*/) +{ + sCreatureTextMgr->SendChat(me, id, whisperTarget, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_MAP); +} + void CreatureAI::DoZoneInCombat(Creature* creature /*= NULL*/, float maxRangeToNearestTarget /* = 50.0f*/) { if (!creature) diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index 6357ac33f1e..209995d359d 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -79,6 +79,7 @@ class CreatureAI : public UnitAI public: void Talk(uint8 id, WorldObject const* whisperTarget = NULL); + void TalkToMap(uint8 id, WorldObject const* whisperTarget = NULL); explicit CreatureAI(Creature* creature) : UnitAI(creature), me(creature), m_MoveInLineOfSight_locked(false) { } virtual ~CreatureAI() { } diff --git a/src/server/game/AI/CreatureAIImpl.h b/src/server/game/AI/CreatureAIImpl.h index 378d3ca18ab..838171a544e 100644 --- a/src/server/game/AI/CreatureAIImpl.h +++ b/src/server/game/AI/CreatureAIImpl.h @@ -311,355 +311,6 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, c } } -class EventMap -{ - /** - * Internal storage type. - * Key: Time as uint32 when the event should occur. - * Value: The event data as uint32. - * - * Structure of event data: - * - Bit 0 - 15: Event Id. - * - Bit 16 - 23: Group - * - Bit 24 - 31: Phase - * - Pattern: 0xPPGGEEEE - */ - typedef std::multimap<uint32, uint32> EventStore; - - public: - EventMap() : _time(0), _phase(0), _lastEvent(0) { } - - /** - * @name Reset - * @brief Removes all scheduled events and resets time and phase. - */ - void Reset() - { - _eventMap.clear(); - _time = 0; - _phase = 0; - } - - /** - * @name Update - * @brief Updates the timer of the event map. - * @param time Value to be added to time. - */ - void Update(uint32 time) - { - _time += time; - } - - /** - * @name GetTimer - * @return Current timer value. - */ - uint32 GetTimer() const - { - return _time; - } - - /** - * @name GetPhaseMask - * @return Active phases as mask. - */ - uint8 GetPhaseMask() const - { - return _phase; - } - - /** - * @name Empty - * @return True, if there are no events scheduled. - */ - bool Empty() const - { - return _eventMap.empty(); - } - - /** - * @name SetPhase - * @brief Sets the phase of the map (absolute). - * @param phase Phase which should be set. Values: 1 - 8. 0 resets phase. - */ - void SetPhase(uint8 phase) - { - if (!phase) - _phase = 0; - else if (phase <= 8) - _phase = (1 << (phase - 1)); - } - - /** - * @name AddPhase - * @brief Activates the given phase (bitwise). - * @param phase Phase which should be activated. Values: 1 - 8 - */ - void AddPhase(uint8 phase) - { - if (phase && phase <= 8) - _phase |= (1 << (phase - 1)); - } - - /** - * @name RemovePhase - * @brief Deactivates the given phase (bitwise). - * @param phase Phase which should be deactivated. Values: 1 - 8. - */ - void RemovePhase(uint8 phase) - { - if (phase && phase <= 8) - _phase &= ~(1 << (phase - 1)); - } - - /** - * @name ScheduleEvent - * @brief Creates new event entry in map. - * @param eventId The id of the new event. - * @param time The time in milliseconds until the event occurs. - * @param group The group which the event is associated to. Has to be between 1 and 8. 0 means it has no group. - * @param phase The phase in which the event can occur. Has to be between 1 and 8. 0 means it can occur in all phases. - */ - void ScheduleEvent(uint32 eventId, uint32 time, uint32 group = 0, uint8 phase = 0) - { - if (group && group <= 8) - eventId |= (1 << (group + 15)); - - if (phase && phase <= 8) - eventId |= (1 << (phase + 23)); - - _eventMap.insert(EventStore::value_type(_time + time, eventId)); - } - - /** - * @name RescheduleEvent - * @brief Cancels the given event and reschedules it. - * @param eventId The id of the event. - * @param time The time in milliseconds until the event occurs. - * @param group The group which the event is associated to. Has to be between 1 and 8. 0 means it has no group. - * @param phase The phase in which the event can occur. Has to be between 1 and 8. 0 means it can occur in all phases. - */ - void RescheduleEvent(uint32 eventId, uint32 time, uint32 group = 0, uint8 phase = 0) - { - CancelEvent(eventId); - ScheduleEvent(eventId, time, group, phase); - } - - /** - * @name RepeatEvent - * @brief Repeats the mostly recently executed event. - * @param time Time until the event occurs. - */ - void Repeat(uint32 time) - { - _eventMap.insert(EventStore::value_type(_time + time, _lastEvent)); - } - - /** - * @name RepeatEvent - * @brief Repeats the mostly recently executed event. - * @param time Time until the event occurs. Equivalent to Repeat(urand(minTime, maxTime). - */ - void Repeat(uint32 minTime, uint32 maxTime) - { - Repeat(urand(minTime, maxTime)); - } - - /** - * @name ExecuteEvent - * @brief Returns the next event to execute and removes it from map. - * @return Id of the event to execute. - */ - uint32 ExecuteEvent() - { - while (!Empty()) - { - EventStore::iterator itr = _eventMap.begin(); - - if (itr->first > _time) - return 0; - else if (_phase && (itr->second & 0xFF000000) && !((itr->second >> 24) & _phase)) - _eventMap.erase(itr); - else - { - uint32 eventId = (itr->second & 0x0000FFFF); - _lastEvent = itr->second; // include phase/group - _eventMap.erase(itr); - return eventId; - } - } - - return 0; - } - - /** - * @name DelayEvents - * @brief Delays all events in the map. If delay is greater than or equal internal timer, delay will be 0. - * @param delay Amount of delay. - */ - void DelayEvents(uint32 delay) - { - _time = delay < _time ? _time - delay : 0; - } - - /** - * @name DelayEvents - * @brief Delay all events of the same group. - * @param delay Amount of delay. - * @param group Group of the events. - */ - void DelayEvents(uint32 delay, uint32 group) - { - if (!group || group > 8 || Empty()) - return; - - EventStore delayed; - - for (EventStore::iterator itr = _eventMap.begin(); itr != _eventMap.end();) - { - if (itr->second & (1 << (group + 15))) - { - delayed.insert(EventStore::value_type(itr->first + delay, itr->second)); - _eventMap.erase(itr++); - } - else - ++itr; - } - - _eventMap.insert(delayed.begin(), delayed.end()); - } - - /** - * @name CancelEvent - * @brief Cancels all events of the specified id. - * @param eventId Event id to cancel. - */ - void CancelEvent(uint32 eventId) - { - if (Empty()) - return; - - for (EventStore::iterator itr = _eventMap.begin(); itr != _eventMap.end();) - { - if (eventId == (itr->second & 0x0000FFFF)) - _eventMap.erase(itr++); - else - ++itr; - } - } - - /** - * @name CancelEventGroup - * @brief Cancel events belonging to specified group. - * @param group Group to cancel. - */ - void CancelEventGroup(uint32 group) - { - if (!group || group > 8 || Empty()) - return; - - for (EventStore::iterator itr = _eventMap.begin(); itr != _eventMap.end();) - { - if (itr->second & (1 << (group + 15))) - _eventMap.erase(itr++); - else - ++itr; - } - } - - /** - * @name GetNextEventTime - * @brief Returns closest occurence of specified event. - * @param eventId Wanted event id. - * @return Time of found event. - */ - uint32 GetNextEventTime(uint32 eventId) const - { - if (Empty()) - return 0; - - for (EventStore::const_iterator itr = _eventMap.begin(); itr != _eventMap.end(); ++itr) - if (eventId == (itr->second & 0x0000FFFF)) - return itr->first; - - return 0; - } - - /** - * @name GetNextEventTime - * @return Time of next event. - */ - uint32 GetNextEventTime() const - { - return Empty() ? 0 : _eventMap.begin()->first; - } - - /** - * @name IsInPhase - * @brief Returns wether event map is in specified phase or not. - * @param phase Wanted phase. - * @return True, if phase of event map contains specified phase. - */ - bool IsInPhase(uint8 phase) - { - return phase <= 8 && (!phase || _phase & (1 << (phase - 1))); - } - - /** - * @name GetTimeUntilEvent - * @brief Returns time in milliseconds until next event. - * @param Id of the event. - * @return Time of next event. - */ - uint32 GetTimeUntilEvent(uint32 eventId) const - { - for (EventStore::const_iterator itr = _eventMap.begin(); itr != _eventMap.end(); ++itr) - if (eventId == (itr->second & 0x0000FFFF)) - return itr->first - _time; - - return std::numeric_limits<uint32>::max(); - } - - private: - /** - * @name _time - * @brief Internal timer. - * - * This does not represent the real date/time value. - * It's more like a stopwatch: It can run, it can be stopped, - * it can be resetted and so on. Events occur when this timer - * has reached their time value. Its value is changed in the - * Update method. - */ - uint32 _time; - - /** - * @name _phase - * @brief Phase mask of the event map. - * - * Contains the phases the event map is in. Multiple - * phases from 1 to 8 can be set with SetPhase or - * AddPhase. RemovePhase deactives a phase. - */ - uint8 _phase; - - /** - * @name _eventMap - * @brief Internal event storage map. Contains the scheduled events. - * - * See typedef at the beginning of the class for more - * details. - */ - EventStore _eventMap; - - - /** - * @name _lastEvent - * @brief Stores information on the most recently executed event - */ - uint32 _lastEvent; -}; - enum AITarget { AITARGET_SELF, diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index eeb72b16f07..ba5f4439838 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -5964,6 +5964,8 @@ void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply) ApplyCastTimePercentMod(oldVal, false); ApplyCastTimePercentMod(newVal, true); break; + default: // shut up compiler warnings + break; } } diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 7d9d83abfde..d4af17ca78b 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -761,7 +761,7 @@ void WorldSession::HandlePlayerLoginOpcode(WorldPacket& recvData) { if (PlayerLoading() || GetPlayer() != NULL) { - TC_LOG_ERROR("network", "Player tryes to login again, AccountId = %d", GetAccountId()); + TC_LOG_ERROR("network", "Player tries to login again, AccountId = %d", GetAccountId()); KickPlayer(); return; } diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index 616ea9c7326..d64f21f2028 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -868,7 +868,10 @@ void WorldSession::HandleLearnPreviewTalentsPet(WorldPacket& recvData) uint32 talentId, talentRank; - for (uint32 i = 0; i < talentsCount; ++i) + // Client has max 24 talents, rounded up : 30 + uint32 const MaxTalentsCount = 30; + + for (uint32 i = 0; i < talentsCount && i < MaxTalentsCount; ++i) { recvData >> talentId >> talentRank; @@ -876,4 +879,6 @@ void WorldSession::HandleLearnPreviewTalentsPet(WorldPacket& recvData) } _player->SendTalentsInfoData(true); + + recvData.rfinish(); } diff --git a/src/server/game/Handlers/SkillHandler.cpp b/src/server/game/Handlers/SkillHandler.cpp index fe893314b87..f90dfef2684 100644 --- a/src/server/game/Handlers/SkillHandler.cpp +++ b/src/server/game/Handlers/SkillHandler.cpp @@ -45,7 +45,10 @@ void WorldSession::HandleLearnPreviewTalents(WorldPacket& recvPacket) uint32 talentId, talentRank; - for (uint32 i = 0; i < talentsCount; ++i) + // Client has max 44 talents for tree for 3 trees, rounded up : 150 + uint32 const MaxTalentsCount = 150; + + for (uint32 i = 0; i < talentsCount && i < MaxTalentsCount; ++i) { recvPacket >> talentId >> talentRank; @@ -53,6 +56,8 @@ void WorldSession::HandleLearnPreviewTalents(WorldPacket& recvPacket) } _player->SendTalentsInfoData(false); + + recvPacket.rfinish(); } void WorldSession::HandleTalentWipeConfirmOpcode(WorldPacket& recvData) diff --git a/src/server/game/Handlers/TicketHandler.cpp b/src/server/game/Handlers/TicketHandler.cpp index 688d7e58b6c..a2aa426c096 100644 --- a/src/server/game/Handlers/TicketHandler.cpp +++ b/src/server/game/Handlers/TicketHandler.cpp @@ -187,6 +187,8 @@ void WorldSession::HandleGMSurveySubmit(WorldPacket& recvData) uint32 mainSurvey; // GMSurveyCurrentSurvey.dbc, column 1 (all 9) ref to GMSurveySurveys.dbc recvData >> mainSurvey; + std::unordered_set<uint32> surveyIds; + SQLTransaction trans = CharacterDatabase.BeginTransaction(); // sub_survey1, r1, comment1, sub_survey2, r2, comment2, sub_survey3, r3, comment3, sub_survey4, r4, comment4, sub_survey5, r5, comment5, sub_survey6, r6, comment6, sub_survey7, r7, comment7, sub_survey8, r8, comment8, sub_survey9, r9, comment9, sub_survey10, r10, comment10, for (uint8 i = 0; i < 10; i++) { @@ -200,12 +202,16 @@ void WorldSession::HandleGMSurveySubmit(WorldPacket& recvData) std::string comment; // comment ("Usage: GMSurveyAnswerSubmit(question, rank, comment)") recvData >> comment; + // make sure the same sub survey is not added to DB twice + if (!surveyIds.insert(subSurveyId).second) + continue; + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SUBSURVEY); stmt->setUInt32(0, nextSurveyID); stmt->setUInt32(1, subSurveyId); stmt->setUInt32(2, rank); stmt->setString(3, comment); - CharacterDatabase.Execute(stmt); + trans->Append(stmt); } std::string comment; // just a guess @@ -217,7 +223,9 @@ void WorldSession::HandleGMSurveySubmit(WorldPacket& recvData) stmt->setUInt32(2, mainSurvey); stmt->setString(3, comment); - CharacterDatabase.Execute(stmt); + trans->Append(stmt); + + CharacterDatabase.CommitTransaction(trans); } void WorldSession::HandleReportLag(WorldPacket& recvData) diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 9f052b75386..eda7b8917a7 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -121,7 +121,9 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 m_TutorialsChanged(false), recruiterId(recruiter), isRecruiter(isARecruiter), - _RBACData(NULL) + _RBACData(NULL), + expireTime(60000), // 1 min after socket loss, session is deleted + forceExit(false) { memset(m_Tutorials, 0, sizeof(m_Tutorials)); @@ -419,8 +421,12 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) ///- Cleanup socket pointer if need if (m_Socket && m_Socket->IsClosed()) { - m_Socket->RemoveReference(); - m_Socket = NULL; + expireTime -= expireTime > diff ? diff : expireTime; + if (expireTime < diff || forceExit) + { + m_Socket->RemoveReference(); + m_Socket = NULL; + } } if (!m_Socket) @@ -446,7 +452,6 @@ void WorldSession::LogoutPlayer(bool save) DoLootRelease(lguid); ///- If the player just died before logging out, make him appear as a ghost - //FIXME: logout must be delayed in case lost connection with client in time of combat if (_player->GetDeathTimer()) { _player->getHostileRefManager().deleteReferences(); @@ -576,7 +581,10 @@ void WorldSession::LogoutPlayer(bool save) void WorldSession::KickPlayer() { if (m_Socket) + { m_Socket->CloseSocket(); + forceExit = true; + } } void WorldSession::SendNotification(const char *format, ...) @@ -1251,8 +1259,8 @@ bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) co if (++packetCounter.amountCounter > maxPacketCounterAllowed) { dosTriggered = true; - TC_LOG_WARN("network", "AntiDOS: Account %u, IP: %s, Character: %s, flooding packet (opc: %s (0x%X), count: %u)", - Session->GetAccountId(), Session->GetRemoteAddress().c_str(), Session->GetPlayerName().c_str(), + TC_LOG_WARN("network", "AntiDOS: Account %u, IP: %s, Ping: %u, Character: %s, flooding packet (opc: %s (0x%X), count: %u)", + Session->GetAccountId(), Session->GetRemoteAddress().c_str(), Session->GetLatency(), Session->GetPlayerName().c_str(), opcodeTable[p.GetOpcode()].name, p.GetOpcode(), packetCounter.amountCounter); } diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 968f5a229ba..82125aafd6d 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -1021,6 +1021,8 @@ class WorldSession bool isRecruiter; ACE_Based::LockedQueue<WorldPacket*, ACE_Thread_Mutex> _recvQueue; rbac::RBACData* _RBACData; + uint32 expireTime; + bool forceExit; WorldSession(WorldSession const& right) = delete; WorldSession& operator=(WorldSession const& right) = delete; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index efdc3ca532f..f5d90bc3612 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -567,7 +567,7 @@ m_caster((info->AttributesEx6 & SPELL_ATTR6_CAST_BY_CHARMER && caster->GetCharme m_spellState = SPELL_STATE_NULL; _triggeredCastFlags = triggerFlags; if (info->AttributesEx4 & SPELL_ATTR4_TRIGGERED) - _triggeredCastFlags = TRIGGERED_FULL_MASK; + _triggeredCastFlags = TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT); m_CastItem = NULL; m_castItemGUID = 0; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 4aced0c92dd..9eadd178eb3 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3385,6 +3385,9 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_60_YARDS); // 60yd spellInfo->Effects[EFFECT_1].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_60_YARDS); // 60yd break; + case 72830: // Achievement Check + spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_50000_YARDS); // 50000yd + break; case 72900: // Start Halls of Reflection Quest AE spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_200_YARDS); // 200yd break; diff --git a/src/server/game/Texts/CreatureTextMgr.cpp b/src/server/game/Texts/CreatureTextMgr.cpp index 57690d7e5c1..8bd7a5a5e71 100644 --- a/src/server/game/Texts/CreatureTextMgr.cpp +++ b/src/server/game/Texts/CreatureTextMgr.cpp @@ -24,6 +24,7 @@ #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "CreatureTextMgr.h" +#include "Group.h" class CreatureTextBuilder { @@ -346,6 +347,18 @@ void CreatureTextMgr::SendNonChatPacket(WorldObject* source, WorldPacket* data, } break; } + case CHAT_MSG_MONSTER_PARTY: + if (!whisperTarget) + return; + + if (Player const* player = whisperTarget->ToPlayer()) + { + if (Group* group = const_cast<Group*>(player->GetGroup())) + for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) + if (Player* member = itr->GetSource()) + member->GetSession()->SendPacket(data); + } + return; default: break; } diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/instance_culling_of_stratholme.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/instance_culling_of_stratholme.cpp index dbd844aa34c..503166e0b12 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/instance_culling_of_stratholme.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/instance_culling_of_stratholme.cpp @@ -187,7 +187,7 @@ class instance_culling_of_stratholme : public InstanceMapScript // Summon Chromie and global whisper if (Creature* chromie = instance->SummonCreature(NPC_CHROMIE_2, ChromieSummonPos)) if (!instance->GetPlayers().isEmpty()) - sCreatureTextMgr->SendChat(chromie, SAY_CRATES_COMPLETED, NULL, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_MAP); + chromie->AI()->TalkToMap(SAY_CRATES_COMPLETED); } DoUpdateWorldState(WORLDSTATE_CRATES_REVEALED, _crateCount); break; diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h index 562105c0866..8a62453d7c1 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h @@ -40,7 +40,7 @@ enum SpellIds enum MiscData { - DESPAWN_TIME = 300000, + DESPAWN_TIME = 1200000, DISPLAYID_DESTROYED_FLOOR = 9060 }; diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index 192249955cf..fefdfa633ea 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -664,7 +664,7 @@ public: Talk(SAY_BUFF_SPARK); } else if (spell->Id == SPELL_MALYGOS_BERSERK) - sCreatureTextMgr->SendChat(me, EMOTE_HIT_BERSERKER_TIMER, NULL, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_MAP); + TalkToMap(EMOTE_HIT_BERSERKER_TIMER); } void MoveInLineOfSight(Unit* who) override @@ -1113,8 +1113,7 @@ public: npc_power_sparkAI(Creature* creature) : ScriptedAI(creature) { _instance = creature->GetInstanceScript(); - // Talk range was not enough for this encounter - sCreatureTextMgr->SendChat(me, EMOTE_POWER_SPARK_SUMMONED, NULL, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_MAP); + TalkToMap(EMOTE_POWER_SPARK_SUMMONED); MoveToMalygos(); } diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 6a6ee144aac..5ae0e1601c5 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -322,13 +322,14 @@ class spell_dk_blood_gorged : public SpellScriptLoader class CorpseExplosionCheck { public: - explicit CorpseExplosionCheck(uint64 casterGUID) : _casterGUID(casterGUID) { } + explicit CorpseExplosionCheck(uint64 casterGUID, bool allowGhoul) : _casterGUID(casterGUID), + _allowGhoul(allowGhoul) { } bool operator()(WorldObject* obj) const { if (Unit* target = obj->ToUnit()) { - if ((target->isDead() || (target->GetEntry() == NPC_DK_GHOUL && target->GetOwnerGUID() == _casterGUID)) + if ((target->isDead() || (_allowGhoul && target->GetEntry() == NPC_DK_GHOUL && target->GetOwnerGUID() == _casterGUID)) && !(target->GetCreatureTypeMask() & CREATURE_TYPEMASK_MECHANICAL_OR_ELEMENTAL) && target->GetDisplayId() == target->GetNativeDisplayId()) return false; @@ -339,6 +340,7 @@ public: private: uint64 _casterGUID; + bool _allowGhoul; }; // 49158 - Corpse Explosion (51325, 51326, 51327, 51328) @@ -369,7 +371,7 @@ class spell_dk_corpse_explosion : public SpellScriptLoader void CheckTarget(WorldObject*& target) { - if (CorpseExplosionCheck(GetCaster()->GetGUID())(target)) + if (CorpseExplosionCheck(GetCaster()->GetGUID(), true)(target)) target = NULL; _target = target; @@ -380,7 +382,7 @@ class spell_dk_corpse_explosion : public SpellScriptLoader WorldObject* target = _target; if (!target) { - targets.remove_if(CorpseExplosionCheck(GetCaster()->GetGUID())); + targets.remove_if(CorpseExplosionCheck(GetCaster()->GetGUID(), false)); if (targets.empty()) { FinishCast(SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW); diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index bf4f1b77a19..447cb645e76 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -866,8 +866,8 @@ class spell_pal_improved_aura : public SpellScriptLoader void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { Unit* target = GetTarget(); - if (!target->GetOwnedAura(_spellId)) - target->CastSpell(target, _spellId, true); + GetTarget()->RemoveOwnedAura(_spellId, GetCasterGUID()); // need to remove to reapply spellmods + target->CastSpell(target, _spellId, true); } void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index bb7da1b7042..c32edff09bc 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -56,6 +56,7 @@ EndContentData */ #include "CellImpl.h" #include "SpellAuras.h" #include "Pet.h" +#include "CreatureTextMgr.h" /*######## # npc_air_force_bots @@ -2353,6 +2354,60 @@ public: }; }; +class npc_imp_in_a_ball : public CreatureScript +{ +private: + enum + { + SAY_RANDOM, + + EVENT_TALK = 1, + }; + +public: + npc_imp_in_a_ball() : CreatureScript("npc_imp_in_a_ball") { } + + struct npc_imp_in_a_ballAI : public ScriptedAI + { + npc_imp_in_a_ballAI(Creature* creature) : ScriptedAI(creature) + { + summonerGUID = 0; + } + + void IsSummonedBy(Unit* summoner) override + { + if (summoner->GetTypeId() == TYPEID_PLAYER) + { + summonerGUID = summoner->GetGUID(); + events.ScheduleEvent(EVENT_TALK, 3000); + } + } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + if (events.ExecuteEvent() == EVENT_TALK) + { + if (Player* owner = ObjectAccessor::GetPlayer(*me, summonerGUID)) + { + sCreatureTextMgr->SendChat(me, SAY_RANDOM, owner, + owner->GetGroup() ? CHAT_MSG_MONSTER_PARTY : CHAT_MSG_MONSTER_WHISPER, LANG_ADDON, TEXT_RANGE_NORMAL); + } + } + } + + private: + EventMap events; + uint64 summonerGUID; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_imp_in_a_ballAI(creature); + } +}; + void AddSC_npcs_special() { new npc_air_force_bots(); @@ -2375,4 +2430,5 @@ void AddSC_npcs_special() new npc_experience(); new npc_firework(); new npc_spring_rabbit(); + new npc_imp_in_a_ball(); } diff --git a/src/server/shared/Utilities/Util.h b/src/server/shared/Utilities/Util.h index b086a28134c..d6ead607c55 100644 --- a/src/server/shared/Utilities/Util.h +++ b/src/server/shared/Utilities/Util.h @@ -26,6 +26,7 @@ #include <string> #include <vector> #include <list> +#include <map> #include <ace/INET_Addr.h> // Searcher for map of structs @@ -565,4 +566,353 @@ bool CompareValues(ComparisionType type, T val1, T val2) } } +class EventMap +{ + /** + * Internal storage type. + * Key: Time as uint32 when the event should occur. + * Value: The event data as uint32. + * + * Structure of event data: + * - Bit 0 - 15: Event Id. + * - Bit 16 - 23: Group + * - Bit 24 - 31: Phase + * - Pattern: 0xPPGGEEEE + */ + typedef std::multimap<uint32, uint32> EventStore; + + public: + EventMap() : _time(0), _phase(0), _lastEvent(0) { } + + /** + * @name Reset + * @brief Removes all scheduled events and resets time and phase. + */ + void Reset() + { + _eventMap.clear(); + _time = 0; + _phase = 0; + } + + /** + * @name Update + * @brief Updates the timer of the event map. + * @param time Value to be added to time. + */ + void Update(uint32 time) + { + _time += time; + } + + /** + * @name GetTimer + * @return Current timer value. + */ + uint32 GetTimer() const + { + return _time; + } + + /** + * @name GetPhaseMask + * @return Active phases as mask. + */ + uint8 GetPhaseMask() const + { + return _phase; + } + + /** + * @name Empty + * @return True, if there are no events scheduled. + */ + bool Empty() const + { + return _eventMap.empty(); + } + + /** + * @name SetPhase + * @brief Sets the phase of the map (absolute). + * @param phase Phase which should be set. Values: 1 - 8. 0 resets phase. + */ + void SetPhase(uint8 phase) + { + if (!phase) + _phase = 0; + else if (phase <= 8) + _phase = (1 << (phase - 1)); + } + + /** + * @name AddPhase + * @brief Activates the given phase (bitwise). + * @param phase Phase which should be activated. Values: 1 - 8 + */ + void AddPhase(uint8 phase) + { + if (phase && phase <= 8) + _phase |= (1 << (phase - 1)); + } + + /** + * @name RemovePhase + * @brief Deactivates the given phase (bitwise). + * @param phase Phase which should be deactivated. Values: 1 - 8. + */ + void RemovePhase(uint8 phase) + { + if (phase && phase <= 8) + _phase &= ~(1 << (phase - 1)); + } + + /** + * @name ScheduleEvent + * @brief Creates new event entry in map. + * @param eventId The id of the new event. + * @param time The time in milliseconds until the event occurs. + * @param group The group which the event is associated to. Has to be between 1 and 8. 0 means it has no group. + * @param phase The phase in which the event can occur. Has to be between 1 and 8. 0 means it can occur in all phases. + */ + void ScheduleEvent(uint32 eventId, uint32 time, uint32 group = 0, uint8 phase = 0) + { + if (group && group <= 8) + eventId |= (1 << (group + 15)); + + if (phase && phase <= 8) + eventId |= (1 << (phase + 23)); + + _eventMap.insert(EventStore::value_type(_time + time, eventId)); + } + + /** + * @name RescheduleEvent + * @brief Cancels the given event and reschedules it. + * @param eventId The id of the event. + * @param time The time in milliseconds until the event occurs. + * @param group The group which the event is associated to. Has to be between 1 and 8. 0 means it has no group. + * @param phase The phase in which the event can occur. Has to be between 1 and 8. 0 means it can occur in all phases. + */ + void RescheduleEvent(uint32 eventId, uint32 time, uint32 group = 0, uint8 phase = 0) + { + CancelEvent(eventId); + ScheduleEvent(eventId, time, group, phase); + } + + /** + * @name RepeatEvent + * @brief Repeats the mostly recently executed event. + * @param time Time until the event occurs. + */ + void Repeat(uint32 time) + { + _eventMap.insert(EventStore::value_type(_time + time, _lastEvent)); + } + + /** + * @name RepeatEvent + * @brief Repeats the mostly recently executed event. + * @param time Time until the event occurs. Equivalent to Repeat(urand(minTime, maxTime). + */ + void Repeat(uint32 minTime, uint32 maxTime) + { + Repeat(urand(minTime, maxTime)); + } + + /** + * @name ExecuteEvent + * @brief Returns the next event to execute and removes it from map. + * @return Id of the event to execute. + */ + uint32 ExecuteEvent() + { + while (!Empty()) + { + EventStore::iterator itr = _eventMap.begin(); + + if (itr->first > _time) + return 0; + else if (_phase && (itr->second & 0xFF000000) && !((itr->second >> 24) & _phase)) + _eventMap.erase(itr); + else + { + uint32 eventId = (itr->second & 0x0000FFFF); + _lastEvent = itr->second; // include phase/group + _eventMap.erase(itr); + return eventId; + } + } + + return 0; + } + + /** + * @name DelayEvents + * @brief Delays all events in the map. If delay is greater than or equal internal timer, delay will be 0. + * @param delay Amount of delay. + */ + void DelayEvents(uint32 delay) + { + _time = delay < _time ? _time - delay : 0; + } + + /** + * @name DelayEvents + * @brief Delay all events of the same group. + * @param delay Amount of delay. + * @param group Group of the events. + */ + void DelayEvents(uint32 delay, uint32 group) + { + if (!group || group > 8 || Empty()) + return; + + EventStore delayed; + + for (EventStore::iterator itr = _eventMap.begin(); itr != _eventMap.end();) + { + if (itr->second & (1 << (group + 15))) + { + delayed.insert(EventStore::value_type(itr->first + delay, itr->second)); + _eventMap.erase(itr++); + } + else + ++itr; + } + + _eventMap.insert(delayed.begin(), delayed.end()); + } + + /** + * @name CancelEvent + * @brief Cancels all events of the specified id. + * @param eventId Event id to cancel. + */ + void CancelEvent(uint32 eventId) + { + if (Empty()) + return; + + for (EventStore::iterator itr = _eventMap.begin(); itr != _eventMap.end();) + { + if (eventId == (itr->second & 0x0000FFFF)) + _eventMap.erase(itr++); + else + ++itr; + } + } + + /** + * @name CancelEventGroup + * @brief Cancel events belonging to specified group. + * @param group Group to cancel. + */ + void CancelEventGroup(uint32 group) + { + if (!group || group > 8 || Empty()) + return; + + for (EventStore::iterator itr = _eventMap.begin(); itr != _eventMap.end();) + { + if (itr->second & (1 << (group + 15))) + _eventMap.erase(itr++); + else + ++itr; + } + } + + /** + * @name GetNextEventTime + * @brief Returns closest occurence of specified event. + * @param eventId Wanted event id. + * @return Time of found event. + */ + uint32 GetNextEventTime(uint32 eventId) const + { + if (Empty()) + return 0; + + for (EventStore::const_iterator itr = _eventMap.begin(); itr != _eventMap.end(); ++itr) + if (eventId == (itr->second & 0x0000FFFF)) + return itr->first; + + return 0; + } + + /** + * @name GetNextEventTime + * @return Time of next event. + */ + uint32 GetNextEventTime() const + { + return Empty() ? 0 : _eventMap.begin()->first; + } + + /** + * @name IsInPhase + * @brief Returns wether event map is in specified phase or not. + * @param phase Wanted phase. + * @return True, if phase of event map contains specified phase. + */ + bool IsInPhase(uint8 phase) + { + return phase <= 8 && (!phase || _phase & (1 << (phase - 1))); + } + + /** + * @name GetTimeUntilEvent + * @brief Returns time in milliseconds until next event. + * @param Id of the event. + * @return Time of next event. + */ + uint32 GetTimeUntilEvent(uint32 eventId) const + { + for (EventStore::const_iterator itr = _eventMap.begin(); itr != _eventMap.end(); ++itr) + if (eventId == (itr->second & 0x0000FFFF)) + return itr->first - _time; + + return std::numeric_limits<uint32>::max(); + } + + private: + /** + * @name _time + * @brief Internal timer. + * + * This does not represent the real date/time value. + * It's more like a stopwatch: It can run, it can be stopped, + * it can be resetted and so on. Events occur when this timer + * has reached their time value. Its value is changed in the + * Update method. + */ + uint32 _time; + + /** + * @name _phase + * @brief Phase mask of the event map. + * + * Contains the phases the event map is in. Multiple + * phases from 1 to 8 can be set with SetPhase or + * AddPhase. RemovePhase deactives a phase. + */ + uint8 _phase; + + /** + * @name _eventMap + * @brief Internal event storage map. Contains the scheduled events. + * + * See typedef at the beginning of the class for more + * details. + */ + EventStore _eventMap; + + + /** + * @name _lastEvent + * @brief Stores information on the most recently executed event + */ + uint32 _lastEvent; +}; + #endif |