diff options
57 files changed, 1216 insertions, 687 deletions
diff --git a/sql/updates/world/2014_05_30_00_world_quest_update.sql b/sql/updates/world/2014_05_30_00_world_quest_update.sql new file mode 100644 index 00000000000..a44baf75074 --- /dev/null +++ b/sql/updates/world/2014_05_30_00_world_quest_update.sql @@ -0,0 +1 @@ +UPDATE `quest_template` SET `NextQuestId`=1106,`ExclusiveGroup`=-1104 WHERE `Id` IN (1104,1105); 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/collision/Maps/MapTree.cpp b/src/server/collision/Maps/MapTree.cpp index bb57079c389..d592d795125 100644 --- a/src/server/collision/Maps/MapTree.cpp +++ b/src/server/collision/Maps/MapTree.cpp @@ -157,8 +157,7 @@ namespace VMAP { float maxDist = (pos2 - pos1).magnitude(); // return false if distance is over max float, in case of cheater teleporting to the end of the universe - if (maxDist == std::numeric_limits<float>::max() || - maxDist == std::numeric_limits<float>::infinity()) + if (maxDist == std::numeric_limits<float>::max() || !std::isfinite(maxDist)) return false; // valid map coords should *never ever* produce float overflow, but this would produce NaNs too diff --git a/src/server/collision/Models/GameObjectModel.cpp b/src/server/collision/Models/GameObjectModel.cpp index 1b99e282132..de97943bb37 100644 --- a/src/server/collision/Models/GameObjectModel.cpp +++ b/src/server/collision/Models/GameObjectModel.cpp @@ -140,6 +140,7 @@ bool GameObjectModel::initialize(const GameObject& go, const GameObjectDisplayIn } #endif + owner = &go; return true; } @@ -161,7 +162,7 @@ GameObjectModel* GameObjectModel::Create(const GameObject& go) bool GameObjectModel::intersectRay(const G3D::Ray& ray, float& MaxDist, bool StopAtFirstHit, uint32 ph_mask) const { - if (!(phasemask & ph_mask)) + if (!(phasemask & ph_mask) || !owner->isSpawned()) return false; float time = ray.intersectionTime(iBound); diff --git a/src/server/collision/Models/GameObjectModel.h b/src/server/collision/Models/GameObjectModel.h index 6088b924343..99c9b1337b3 100644 --- a/src/server/collision/Models/GameObjectModel.h +++ b/src/server/collision/Models/GameObjectModel.h @@ -44,8 +44,9 @@ class GameObjectModel /*, public Intersectable*/ float iInvScale; float iScale; VMAP::WorldModel* iModel; + GameObject const* owner; - GameObjectModel() : phasemask(0), iInvScale(0), iScale(0), iModel(NULL) { } + GameObjectModel() : phasemask(0), iInvScale(0), iScale(0), iModel(NULL), owner(NULL) { } bool initialize(const GameObject& go, const GameObjectDisplayInfoEntry& info); public: diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp index f0a75403c8c..7c640f9a66d 100644 --- a/src/server/game/AI/CoreAI/PetAI.cpp +++ b/src/server/game/AI/CoreAI/PetAI.cpp @@ -89,7 +89,8 @@ void PetAI::UpdateAI(uint32 diff) if (me->GetVictim() && me->EnsureVictim()->IsAlive()) { // is only necessary to stop casting, the pet must not exit combat - if (me->EnsureVictim()->HasBreakableByDamageCrowdControlAura(me)) + if (!me->GetCurrentSpell(CURRENT_CHANNELED_SPELL) && // ignore channeled spells (Pin, Seduction) + me->EnsureVictim()->HasBreakableByDamageCrowdControlAura(me)) { me->InterruptNonMeleeSpells(false); return; 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 7447e290ba9..838171a544e 100644 --- a/src/server/game/AI/CreatureAIImpl.h +++ b/src/server/game/AI/CreatureAIImpl.h @@ -311,359 +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) { } - - /** - * @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 Cancels the closest event and reschedules it. - * @param time Time until the event occurs. - */ - void RepeatEvent(uint32 time) - { - if (Empty()) - return; - - uint32 eventId = _eventMap.begin()->second; - _eventMap.erase(_eventMap.begin()); - ScheduleEvent(eventId, time); - } - - /** - * @name PopEvent - * @brief Remove the first event in the map. - */ - void PopEvent() - { - if (!Empty()) - _eventMap.erase(_eventMap.begin()); - } - - /** - * @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); - _eventMap.erase(itr); - return eventId; - } - } - - return 0; - } - - /** - * @name GetEvent - * @brief Returns the next event to execute. - * @return Id of the event to execute. - */ - uint32 GetEvent() - { - while (!Empty()) - { - EventStore::iterator itr = _eventMap.begin(); - - if (itr->first > _time) - return 0; - else if (_phase && (itr->second & 0xFF000000) && !(itr->second & (_phase << 24))) - _eventMap.erase(itr); - else - return (itr->second & 0x0000FFFF); - } - - 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))); - } - - 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; -}; - enum AITarget { AITARGET_SELF, diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp index d8f61a22314..773e169e5c2 100644 --- a/src/server/game/Accounts/AccountMgr.cpp +++ b/src/server/game/Accounts/AccountMgr.cpp @@ -517,7 +517,7 @@ bool AccountMgr::HasPermission(uint32 accountId, uint32 permissionId, uint32 rea return false; } - rbac::RBACData rbac(accountId, "", realmId); + rbac::RBACData rbac(accountId, "", realmId, GetSecurity(accountId)); rbac.LoadFromDB(); bool hasPermission = rbac.HasPermission(permissionId); diff --git a/src/server/game/Chat/Channels/Channel.h b/src/server/game/Chat/Channels/Channel.h index 9ad6877d929..115e340762e 100644 --- a/src/server/game/Chat/Channels/Channel.h +++ b/src/server/game/Chat/Channels/Channel.h @@ -25,7 +25,7 @@ #include "Common.h" -#include "Opcodes.h" +#include "WorldSession.h" #include "WorldPacket.h" class Player; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index e0374db3ece..4cf7d34cc11 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -2013,6 +2013,10 @@ void GameObject::SetLootState(LootState state, Unit* unit) m_lootStateUnitGUID = unit ? unit->GetGUID() : 0; AI()->OnStateChanged(state, unit); sScriptMgr->OnGameObjectLootStateChanged(this, state, unit); + + if (GetGoType() == GAMEOBJECT_TYPE_DOOR) // only set collision for doors on SetGoState + return; + if (m_model) { bool collision = false; diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index ee389ed7311..4573b85c7f3 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -27,7 +27,7 @@ #include "ScriptMgr.h" #include "ConditionMgr.h" #include "Player.h" -#include "Opcodes.h" +#include "WorldSession.h" void AddItemsSetItem(Player* player, Item* item) { diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index eb4e2fcf2ee..768f5907c19 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -30,7 +30,7 @@ #include "Unit.h" #include "Util.h" #include "Group.h" -#include "Opcodes.h" +#include "WorldSession.h" #define PET_XP_FACTOR 0.05f diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 71e37b1a256..f1adae8f337 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -689,6 +689,8 @@ Player::Player(WorldSession* session): Unit(true) m_areaUpdateId = 0; m_team = 0; + + m_needsZoneUpdate = false; m_nextSave = sWorld->getIntConfig(CONFIG_INTERVAL_SAVE); @@ -5938,32 +5940,33 @@ float Player::OCTRegenMPPerSpirit() void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply) { + float oldRating = m_baseRatingValue[cr]; m_baseRatingValue[cr]+=(apply ? value : -value); - // explicit affected values - switch (cr) - { - case CR_HASTE_MELEE: - { - float RatingChange = value * GetRatingMultiplier(cr); - ApplyAttackTimePercentMod(BASE_ATTACK, RatingChange, apply); - ApplyAttackTimePercentMod(OFF_ATTACK, RatingChange, apply); - break; - } - case CR_HASTE_RANGED: - { - float RatingChange = value * GetRatingMultiplier(cr); - ApplyAttackTimePercentMod(RANGED_ATTACK, RatingChange, apply); - break; - } - case CR_HASTE_SPELL: - { - float RatingChange = value * GetRatingMultiplier(cr); - ApplyCastTimePercentMod(RatingChange, apply); - break; + if (cr == CR_HASTE_MELEE || cr == CR_HASTE_RANGED || cr == CR_HASTE_SPELL) + { + float const mult = GetRatingMultiplier(cr); + float const oldVal = oldRating * mult; + float const newVal = m_baseRatingValue[cr] * mult; + switch (cr) + { + case CR_HASTE_MELEE: + ApplyAttackTimePercentMod(BASE_ATTACK, oldVal, false); + ApplyAttackTimePercentMod(OFF_ATTACK, oldVal, false); + ApplyAttackTimePercentMod(BASE_ATTACK, newVal, true); + ApplyAttackTimePercentMod(OFF_ATTACK, newVal, true); + break; + case CR_HASTE_RANGED: + ApplyAttackTimePercentMod(RANGED_ATTACK, oldVal, false); + ApplyAttackTimePercentMod(RANGED_ATTACK, newVal, true); + break; + case CR_HASTE_SPELL: + ApplyCastTimePercentMod(oldVal, false); + ApplyCastTimePercentMod(newVal, true); + break; + default: // shut up compiler warnings + break; } - default: - break; } UpdateRating(cr); @@ -6757,6 +6760,15 @@ bool Player::UpdatePosition(float x, float y, float z, float orientation, bool t // mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING); //AURA_INTERRUPT_FLAG_JUMP not sure + // Update player zone if needed + if (m_needsZoneUpdate) + { + uint32 newZone, newArea; + GetZoneAndAreaId(newZone, newArea); + UpdateZone(newZone, newArea); + m_needsZoneUpdate = false; + } + // group update if (GetGroup()) SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION); @@ -7480,6 +7492,19 @@ void Player::UpdateArea(uint32 newArea) } else RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); + + uint32 const areaRestFlag = (GetTeam() == ALLIANCE) ? AREA_FLAG_REST_ZONE_ALLIANCE : AREA_FLAG_REST_ZONE_HORDE; + if (area && area->flags & areaRestFlag) + { + SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING); + SetRestType(REST_TYPE_IN_FACTION_AREA); + InnEnter(time(0), GetMapId(), 0, 0, 0); + } + else if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) && GetRestType() == REST_TYPE_IN_FACTION_AREA) + { + RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING); + SetRestType(REST_TYPE_NO); + } } void Player::UpdateZone(uint32 newZone, uint32 newArea) @@ -7565,8 +7590,9 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) SetRestType(REST_TYPE_NO); } } - else // Recently left a capital city + else if (GetRestType() != REST_TYPE_IN_FACTION_AREA) // handled in UpdateArea { + // Recently left a capital city RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING); SetRestType(REST_TYPE_NO); } @@ -8201,7 +8227,12 @@ void Player::_ApplyWeaponDependentAuraDamageMod(Item* item, WeaponAttackType att { HandleStatModifier(unitMod, unitModType, float(aura->GetAmount()), apply); if (unitModType == TOTAL_VALUE) - ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS, aura->GetAmount(), apply); + { + if (aura->GetAmount() > 0) + ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS, aura->GetAmount(), apply); + else + ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG, aura->GetAmount(), apply); + } } } @@ -16352,12 +16383,8 @@ void Player::ItemAddedQuestCheck(uint32 entry, uint32 count) uint16 curitemcount = q_status.ItemCount[j]; if (curitemcount < reqitemcount) { - uint16 additemcount = curitemcount + count <= reqitemcount ? count : reqitemcount - curitemcount; - q_status.ItemCount[j] += additemcount; - + q_status.ItemCount[j] = std::min<uint16>(q_status.ItemCount[j] + count, reqitemcount); m_QuestStatusSave[questid] = true; - - SendQuestUpdateAddItem(qInfo, j, additemcount); } if (CanCompleteQuest(questid)) CompleteQuest(questid); @@ -16375,9 +16402,11 @@ void Player::ItemRemovedQuestCheck(uint32 entry, uint32 count) uint32 questid = GetQuestSlotQuestId(i); if (!questid) continue; + Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid); if (!qInfo) continue; + if (!qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER)) continue; @@ -16389,18 +16418,17 @@ void Player::ItemRemovedQuestCheck(uint32 entry, uint32 count) QuestStatusData& q_status = m_QuestStatus[questid]; uint32 reqitemcount = qInfo->RequiredItemCount[j]; - uint16 curitemcount; - if (q_status.Status != QUEST_STATUS_COMPLETE) - curitemcount = q_status.ItemCount[j]; - else - curitemcount = GetItemCount(entry, true); - if (curitemcount < reqitemcount + count) - { - uint16 remitemcount = curitemcount <= reqitemcount ? count : count + reqitemcount - curitemcount; - q_status.ItemCount[j] = (curitemcount <= remitemcount) ? 0 : curitemcount - remitemcount; + uint16 curitemcount = q_status.ItemCount[j]; - m_QuestStatusSave[questid] = true; + if (q_status.ItemCount[j] >= reqitemcount) // we may have more than what the status shows + curitemcount = GetItemCount(entry, false); + uint16 newItemCount = (count > curitemcount) ? 0 : curitemcount - count; + newItemCount = std::min<uint16>(newItemCount, reqitemcount); + if (newItemCount != q_status.ItemCount[j]) + { + q_status.ItemCount[j] = newItemCount; + m_QuestStatusSave[questid] = true; IncompleteQuest(questid); } return; @@ -26728,3 +26756,8 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy return pet; } + +bool Player::IsLoading() const +{ + return GetSession()->PlayerLoading(); +} diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 7e1d9be0f88..94175834969 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -729,9 +729,10 @@ class InstanceSave; enum RestType { - REST_TYPE_NO = 0, - REST_TYPE_IN_TAVERN = 1, - REST_TYPE_IN_CITY = 2 + REST_TYPE_NO = 0, + REST_TYPE_IN_TAVERN = 1, + REST_TYPE_IN_CITY = 2, + REST_TYPE_IN_FACTION_AREA = 3 // used with AREA_FLAG_REST_ZONE_* }; enum TeleportToOptions @@ -1674,7 +1675,8 @@ class Player : public Unit, public GridObject<Player> void UpdatePvP(bool state, bool override=false); void UpdateZone(uint32 newZone, uint32 newArea); void UpdateArea(uint32 newArea); - + void SetNeedsZoneUpdate(bool needsUpdate) { m_needsZoneUpdate = needsUpdate; } + void UpdateZoneDependentAuras(uint32 zone_id); // zones void UpdateAreaDependentAuras(uint32 area_id); // subzones @@ -2301,6 +2303,8 @@ class Player : public Unit, public GridObject<Player> std::string GetMapAreaAndZoneString(); std::string GetCoordsMapAreaAndZoneString(); + bool IsLoading() const; + protected: // Gamemaster whisper whitelist WhisperListContainer WhisperList; @@ -2560,6 +2564,8 @@ class Player : public Unit, public GridObject<Player> bool IsAlwaysDetectableFor(WorldObject const* seer) const; uint8 m_grantableLevels; + + bool m_needsZoneUpdate; private: // internal common parts for CanStore/StoreItem functions diff --git a/src/server/game/Entities/Player/SocialMgr.cpp b/src/server/game/Entities/Player/SocialMgr.cpp index 8c8e470f80b..25315a30da1 100644 --- a/src/server/game/Entities/Player/SocialMgr.cpp +++ b/src/server/game/Entities/Player/SocialMgr.cpp @@ -19,7 +19,7 @@ #include "SocialMgr.h" #include "DatabaseEnv.h" -#include "Opcodes.h" +#include "WorldSession.h" #include "WorldPacket.h" #include "Player.h" #include "ObjectMgr.h" diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index abc16256b73..4efa1374ec7 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -774,7 +774,8 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam if (damagetype != NODAMAGE && damage) { - if (victim != this && victim->GetTypeId() == TYPEID_PLAYER) // does not support creature push_back + if (victim != this && victim->GetTypeId() == TYPEID_PLAYER && // does not support creature push_back + (!spellProto || !(spellProto->AttributesEx7 & SPELL_ATTR7_NO_PUSHBACK_ON_DAMAGE))) { if (damagetype != DOT) if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL]) @@ -977,86 +978,90 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama SpellSchoolMask damageSchoolMask = SpellSchoolMask(damageInfo->schoolMask); uint32 crTypeMask = victim->GetCreatureTypeMask(); - if (IsDamageReducedByArmor(damageSchoolMask, spellInfo)) - damage = CalcArmorReducedDamage(victim, damage, spellInfo, attackType); - - bool blocked = false; - // Per-school calc - switch (spellInfo->DmgClass) + // Spells with SPELL_ATTR4_FIXED_DAMAGE ignore resilience because their damage is based off another spell's damage. + if (!(spellInfo->AttributesEx4 & SPELL_ATTR4_FIXED_DAMAGE)) { - // Melee and Ranged Spells - case SPELL_DAMAGE_CLASS_RANGED: - case SPELL_DAMAGE_CLASS_MELEE: + if (IsDamageReducedByArmor(damageSchoolMask, spellInfo)) + damage = CalcArmorReducedDamage(victim, damage, spellInfo, attackType); + + bool blocked = false; + // Per-school calc + switch (spellInfo->DmgClass) { - // Physical Damage - if (damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL) + // Melee and Ranged Spells + case SPELL_DAMAGE_CLASS_RANGED: + case SPELL_DAMAGE_CLASS_MELEE: { - // Get blocked status - blocked = isSpellBlocked(victim, spellInfo, attackType); - } + // Physical Damage + if (damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL) + { + // Get blocked status + blocked = isSpellBlocked(victim, spellInfo, attackType); + } - if (crit) - { - damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT; - - // Calculate crit bonus - uint32 crit_bonus = damage; - // Apply crit_damage bonus for melee spells - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus); - damage += crit_bonus; - - // Apply SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE or SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE - float critPctDamageMod = 0.0f; - if (attackType == RANGED_ATTACK) - critPctDamageMod += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE); - else - critPctDamageMod += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE); + if (crit) + { + damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT; + + // Calculate crit bonus + uint32 crit_bonus = damage; + // Apply crit_damage bonus for melee spells + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus); + damage += crit_bonus; + + // Apply SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE or SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE + float critPctDamageMod = 0.0f; + if (attackType == RANGED_ATTACK) + critPctDamageMod += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE); + else + critPctDamageMod += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE); - // Increase crit damage from SPELL_AURA_MOD_CRIT_DAMAGE_BONUS - critPctDamageMod += (GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS, spellInfo->GetSchoolMask()) - 1.0f) * 100; + // Increase crit damage from SPELL_AURA_MOD_CRIT_DAMAGE_BONUS + critPctDamageMod += (GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS, spellInfo->GetSchoolMask()) - 1.0f) * 100; - // Increase crit damage from SPELL_AURA_MOD_CRIT_PERCENT_VERSUS - critPctDamageMod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, crTypeMask); + // Increase crit damage from SPELL_AURA_MOD_CRIT_PERCENT_VERSUS + critPctDamageMod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, crTypeMask); - if (critPctDamageMod != 0) - AddPct(damage, critPctDamageMod); - } + if (critPctDamageMod != 0) + AddPct(damage, critPctDamageMod); + } - // Spell weapon based damage CAN BE crit & blocked at same time - if (blocked) - { - damageInfo->blocked = victim->GetShieldBlockValue(); - // double blocked amount if block is critical - if (victim->isBlockCritical()) - damageInfo->blocked += damageInfo->blocked; - if (damage < int32(damageInfo->blocked)) - damageInfo->blocked = uint32(damage); - damage -= damageInfo->blocked; - } + // Spell weapon based damage CAN BE crit & blocked at same time + if (blocked) + { + damageInfo->blocked = victim->GetShieldBlockValue(); + // double blocked amount if block is critical + if (victim->isBlockCritical()) + damageInfo->blocked += damageInfo->blocked; + if (damage < int32(damageInfo->blocked)) + damageInfo->blocked = uint32(damage); + damage -= damageInfo->blocked; + } - if (attackType != RANGED_ATTACK) - ApplyResilience(victim, NULL, &damage, crit, CR_CRIT_TAKEN_MELEE); - else - ApplyResilience(victim, NULL, &damage, crit, CR_CRIT_TAKEN_RANGED); - break; - } - // Magical Attacks - case SPELL_DAMAGE_CLASS_NONE: - case SPELL_DAMAGE_CLASS_MAGIC: - { - // If crit add critical bonus - if (crit) - { - damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT; - damage = SpellCriticalDamageBonus(spellInfo, damage, victim); + if (attackType != RANGED_ATTACK) + ApplyResilience(victim, NULL, &damage, crit, CR_CRIT_TAKEN_MELEE); + else + ApplyResilience(victim, NULL, &damage, crit, CR_CRIT_TAKEN_RANGED); + break; } + // Magical Attacks + case SPELL_DAMAGE_CLASS_NONE: + case SPELL_DAMAGE_CLASS_MAGIC: + { + // If crit add critical bonus + if (crit) + { + damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT; + damage = SpellCriticalDamageBonus(spellInfo, damage, victim); + } - ApplyResilience(victim, NULL, &damage, crit, CR_CRIT_TAKEN_SPELL); - break; + ApplyResilience(victim, NULL, &damage, crit, CR_CRIT_TAKEN_SPELL); + break; + } + default: + break; } - default: - break; } // Script Hook For CalculateSpellDamageTaken -- Allow scripts to change the Damage post class mitigation calculations @@ -1834,14 +1839,15 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe splitDamage = RoundToInterval(splitDamage, uint32(0), uint32(dmgInfo.GetDamage())); dmgInfo.AbsorbDamage(splitDamage); - uint32 splitted = splitDamage; uint32 split_absorb = 0; - DealDamageMods(caster, splitted, &split_absorb); + DealDamageMods(caster, splitDamage, &split_absorb); - SendSpellNonMeleeDamageLog(caster, (*itr)->GetSpellInfo()->Id, splitted, schoolMask, split_absorb, 0, false, 0, false); + SendSpellNonMeleeDamageLog(caster, (*itr)->GetSpellInfo()->Id, splitDamage, schoolMask, split_absorb, 0, false, 0, false); - CleanDamage cleanDamage = CleanDamage(splitted, 0, BASE_ATTACK, MELEE_HIT_NORMAL); - DealDamage(caster, splitted, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*itr)->GetSpellInfo(), false); + CleanDamage cleanDamage = CleanDamage(splitDamage, 0, BASE_ATTACK, MELEE_HIT_NORMAL); + DealDamage(caster, splitDamage, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*itr)->GetSpellInfo(), false); + // break 'Fear' and similar auras + caster->ProcDamageAndSpellFor(true, this, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_EX_NORMAL_HIT, BASE_ATTACK, (*itr)->GetSpellInfo(), splitDamage); } } @@ -9842,11 +9848,6 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin if (spellProto->AttributesEx3 & SPELL_ATTR3_NO_DONE_BONUS) return pdamage; - // small exception for Deep Wounds, can't find any general rule - // should ignore ALL damage mods, they already calculated in trigger spell - if (spellProto->Id == 12721) // Deep Wounds - return pdamage; - // For totems get damage bonus from owner if (GetTypeId() == TYPEID_UNIT && ToCreature()->IsTotem()) if (Unit* owner = GetOwner()) @@ -9862,7 +9863,7 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin DoneTotalMod *= ToCreature()->GetSpellDamageMod(ToCreature()->GetCreatureTemplate()->rank); // Some spells don't benefit from pct done mods - if (!(spellProto->AttributesEx6 & SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS) && !spellProto->IsRankOf(sSpellMgr->GetSpellInfo(12162))) + if (!(spellProto->AttributesEx6 & SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS)) { AuraEffectList const& mModDamagePercentDone = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); for (AuraEffectList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i) @@ -10259,19 +10260,16 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui int32 TakenTotal = 0; float TakenTotalMod = 1.0f; float TakenTotalCasterMod = 0.0f; - - // get all auras from caster that allow the spell to ignore resistance (sanctified wrath) - AuraEffectList const& IgnoreResistAuras = caster->GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_TARGET_RESIST); - for (AuraEffectList::const_iterator i = IgnoreResistAuras.begin(); i != IgnoreResistAuras.end(); ++i) + + // Mod damage from spell mechanic + if (uint32 mechanicMask = spellProto->GetAllEffectsMechanicMask()) { - if ((*i)->GetMiscValue() & spellProto->GetSchoolMask()) - TakenTotalCasterMod += (float((*i)->GetAmount())); + AuraEffectList const& mDamageDoneMechanic = GetAuraEffectsByType(SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT); + for (AuraEffectList::const_iterator i = mDamageDoneMechanic.begin(); i != mDamageDoneMechanic.end(); ++i) + if (mechanicMask & uint32(1 << ((*i)->GetMiscValue()))) + AddPct(TakenTotalMod, (*i)->GetAmount()); } - // from positive and negative SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN - // multiplicative bonus, for example Dispersion + Shadowform (0.10*0.85=0.085) - TakenTotalMod *= GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, spellProto->GetSchoolMask()); - //.. taken pct: dummy auras AuraEffectList const& mDummyAuras = GetAuraEffectsByType(SPELL_AURA_DUMMY); for (AuraEffectList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i) @@ -10290,45 +10288,51 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui break; } } + // Spells with SPELL_ATTR4_FIXED_DAMAGE should only benefit from mechanic damage mod auras. + if (!(spellProto->AttributesEx4 & SPELL_ATTR4_FIXED_DAMAGE)) + { + // get all auras from caster that allow the spell to ignore resistance (sanctified wrath) + AuraEffectList const& IgnoreResistAuras = caster->GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_TARGET_RESIST); + for (AuraEffectList::const_iterator i = IgnoreResistAuras.begin(); i != IgnoreResistAuras.end(); ++i) + { + if ((*i)->GetMiscValue() & spellProto->GetSchoolMask()) + TakenTotalCasterMod += (float((*i)->GetAmount())); + } - // From caster spells - AuraEffectList const& mOwnerTaken = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_FROM_CASTER); - for (AuraEffectList::const_iterator i = mOwnerTaken.begin(); i != mOwnerTaken.end(); ++i) - if ((*i)->GetCasterGUID() == caster->GetGUID() && (*i)->IsAffectedOnSpell(spellProto)) - AddPct(TakenTotalMod, (*i)->GetAmount()); + // from positive and negative SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN + // multiplicative bonus, for example Dispersion + Shadowform (0.10*0.85=0.085) + TakenTotalMod *= GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, spellProto->GetSchoolMask()); - // Mod damage from spell mechanic - if (uint32 mechanicMask = spellProto->GetAllEffectsMechanicMask()) - { - AuraEffectList const& mDamageDoneMechanic = GetAuraEffectsByType(SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT); - for (AuraEffectList::const_iterator i = mDamageDoneMechanic.begin(); i != mDamageDoneMechanic.end(); ++i) - if (mechanicMask & uint32(1<<((*i)->GetMiscValue()))) + // From caster spells + AuraEffectList const& mOwnerTaken = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_FROM_CASTER); + for (AuraEffectList::const_iterator i = mOwnerTaken.begin(); i != mOwnerTaken.end(); ++i) + if ((*i)->GetCasterGUID() == caster->GetGUID() && (*i)->IsAffectedOnSpell(spellProto)) AddPct(TakenTotalMod, (*i)->GetAmount()); - } - int32 TakenAdvertisedBenefit = SpellBaseDamageBonusTaken(spellProto->GetSchoolMask()); + int32 TakenAdvertisedBenefit = SpellBaseDamageBonusTaken(spellProto->GetSchoolMask()); - // Check for table values - float coeff = 0; - SpellBonusEntry const* bonus = sSpellMgr->GetSpellBonusData(spellProto->Id); - if (bonus) - coeff = (damagetype == DOT) ? bonus->dot_damage : bonus->direct_damage; + // Check for table values + float coeff = 0; + SpellBonusEntry const* bonus = sSpellMgr->GetSpellBonusData(spellProto->Id); + if (bonus) + coeff = (damagetype == DOT) ? bonus->dot_damage : bonus->direct_damage; - // Default calculation - if (TakenAdvertisedBenefit) - { - if (!bonus || coeff < 0) - coeff = CalculateDefaultCoefficient(spellProto, damagetype) * int32(stack); - - float factorMod = CalculateLevelPenalty(spellProto) * stack; - // level penalty still applied on Taken bonus - is it blizzlike? - if (Player* modOwner = GetSpellModOwner()) + // Default calculation + if (TakenAdvertisedBenefit) { - coeff *= 100.0f; - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff); - coeff /= 100.0f; + if (!bonus || coeff < 0) + coeff = CalculateDefaultCoefficient(spellProto, damagetype) * int32(stack); + + float factorMod = CalculateLevelPenalty(spellProto) * stack; + // level penalty still applied on Taken bonus - is it blizzlike? + if (Player* modOwner = GetSpellModOwner()) + { + coeff *= 100.0f; + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff); + coeff /= 100.0f; + } + TakenTotal += int32(TakenAdvertisedBenefit * coeff * factorMod); } - TakenTotal+= int32(TakenAdvertisedBenefit * coeff * factorMod); } float tmpDamage = 0.0f; diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index f4ea4971190..d4af17ca78b 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -243,8 +243,6 @@ void WorldSession::HandleCharEnum(PreparedQueryResult result) void WorldSession::HandleCharEnumOpcode(WorldPacket & /*recvData*/) { - AntiDOS.AllowOpcode(CMSG_CHAR_ENUM, false); - // remove expired bans PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_BANS); CharacterDatabase.Execute(stmt); @@ -681,7 +679,6 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte data << uint8(CHAR_CREATE_SUCCESS); SendPacket(&data); - AntiDOS.AllowOpcode(CMSG_CHAR_ENUM, true); std::string IP_str = GetRemoteAddress(); TC_LOG_INFO("entities.player.character", "Account: %d (IP: %s) Create Character:[%s] (GUID: %u)", GetAccountId(), IP_str.c_str(), createInfo->Name.c_str(), newChar.GetGUIDLow()); sScriptMgr->OnPlayerCreate(&newChar); @@ -758,15 +755,14 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) WorldPacket data(SMSG_CHAR_DELETE, 1); data << uint8(CHAR_DELETE_SUCCESS); SendPacket(&data); - - AntiDOS.AllowOpcode(CMSG_CHAR_ENUM, true); } 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; } @@ -1168,7 +1164,6 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recvData) void WorldSession::HandleChangePlayerNameOpcodeCallBack(PreparedQueryResult result, std::string const& newName) { - AntiDOS.AllowOpcode(CMSG_CHAR_ENUM, true); if (!result) { WorldPacket data(SMSG_CHAR_RENAME, 1); @@ -1426,8 +1421,6 @@ void WorldSession::HandleCharCustomize(WorldPacket& recvData) stmt->setUInt32(0, GUID_LOPART(guid)); // TODO: Make async with callback - // TODO 2: Allow opcode at end of callback - AntiDOS.AllowOpcode(CMSG_CHAR_ENUM, true); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) @@ -1682,8 +1675,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) uint8 playerClass = nameData->m_class; uint8 level = nameData->m_level; - // TO Do: Make async and allow opcode on callback - AntiDOS.AllowOpcode(CMSG_CHAR_ENUM, true); + // TO Do: Make async PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_AT_LOGIN_TITLES); stmt->setUInt32(0, lowGuid); PreparedQueryResult result = CharacterDatabase.Query(stmt); diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index c4b4b35bf37..0ca7885b82b 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -470,19 +470,6 @@ void WorldSession::HandleReadItem(WorldPacket& recvData) _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL); } -void WorldSession::HandlePageQuerySkippedOpcode(WorldPacket& recvData) -{ - TC_LOG_DEBUG("network", "WORLD: Received CMSG_PAGE_TEXT_QUERY"); - - uint32 itemid; - uint64 guid; - - recvData >> itemid >> guid; - - TC_LOG_INFO("network", "Packet Info: itemid: %u guidlow: %u guidentry: %u guidhigh: %u", - itemid, GUID_LOPART(guid), GUID_ENPART(guid), GUID_HIPART(guid)); -} - void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) { TC_LOG_DEBUG("network", "WORLD: Received CMSG_SELL_ITEM"); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index df86c9b4b83..11fa89d9d6b 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -186,11 +186,6 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData) { TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_WHO Message"); - time_t now = time(NULL); - if (now - timeLastWhoCommand < 5) - return; - else timeLastWhoCommand = now; - uint32 matchcount = 0; uint32 level_min, level_max, racemask, classmask, zones_count, str_count; @@ -505,10 +500,9 @@ void WorldSession::HandleZoneUpdateOpcode(WorldPacket& recvData) TC_LOG_DEBUG("network", "WORLD: Recvd ZONE_UPDATE: %u", newZone); - // use server size data - uint32 newzone, newarea; - GetPlayer()->GetZoneAndAreaId(newzone, newarea); - GetPlayer()->UpdateZone(newzone, newarea); + // use server side data, but only after update the player position. See Player::UpdatePosition(). + GetPlayer()->SetNeedsZoneUpdate(true); + //GetPlayer()->SendInitWorldStates(true, newZone); } 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/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index de08392b86a..dbcfb1c4970 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -405,19 +405,23 @@ void WorldSession::HandleQuestPOIQuery(WorldPacket& recvData) uint32 count; recvData >> count; // quest count, max=25 - if (count >= MAX_QUEST_LOG_SIZE) + if (count > MAX_QUEST_LOG_SIZE) { recvData.rfinish(); return; } - WorldPacket data(SMSG_QUEST_POI_QUERY_RESPONSE, 4+(4+4)*count); - data << uint32(count); // count - + // Read quest ids and add the in a unordered_set so we don't send POIs for the same quest multiple times + std::unordered_set<uint32> questIds; for (uint32 i = 0; i < count; ++i) + questIds.insert(recvData.read<uint32>()); // quest id + + WorldPacket data(SMSG_QUEST_POI_QUERY_RESPONSE, 4 + (4 + 4)*questIds.size()); + data << uint32(questIds.size()); // count + + for (auto itr = questIds.begin(); itr != questIds.end(); ++itr) { - uint32 questId; - recvData >> questId; // quest id + uint32 questId = *itr; bool questOk = false; 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/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index ba271235330..d3be33cb441 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -2854,10 +2854,9 @@ bool InstanceMap::CanEnter(Player* player) return false; } - // cannot enter while an encounter is in progress on raids - /*Group* group = player->GetGroup(); - if (!player->IsGameMaster() && group && group->InCombatToInstance(GetInstanceId()) && player->GetMapId() != GetId())*/ - if (IsRaid() && GetInstanceScript() && GetInstanceScript()->IsEncounterInProgress()) + // cannot enter while an encounter is in progress + // allow if just loading + if (!player->IsLoading() && IsRaid() && GetInstanceScript() && GetInstanceScript()->IsEncounterInProgress()) { player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT); return false; diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 0704c4eb9fe..4ec1d5750a4 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -427,7 +427,7 @@ enum SpellAttr4 SPELL_ATTR4_UNK5 = 0x00000020, // 5 SPELL_ATTR4_NOT_STEALABLE = 0x00000040, // 6 although such auras might be dispellable, they cannot be stolen SPELL_ATTR4_TRIGGERED = 0x00000080, // 7 spells forced to be triggered - SPELL_ATTR4_FIXED_DAMAGE = 0x00000100, // 8 ignores taken percent damage mods? + SPELL_ATTR4_FIXED_DAMAGE = 0x00000100, // 8 Ignores resilience and any (except mechanic related) damage or % damage taken auras on target. SPELL_ATTR4_TRIGGER_ACTIVATE = 0x00000200, // 9 initially disabled / trigger activate from event (Execute, Riposte, Deep Freeze end other) SPELL_ATTR4_SPELL_VS_EXTEND_COST = 0x00000400, // 10 Rogue Shiv have this flag SPELL_ATTR4_UNK11 = 0x00000800, // 11 @@ -533,7 +533,7 @@ enum SpellAttr7 SPELL_ATTR7_IS_CHEAT_SPELL = 0x00000008, // 3 Cannot cast if caster doesn't have UnitFlag2 & UNIT_FLAG2_ALLOW_CHEAT_SPELLS SPELL_ATTR7_UNK4 = 0x00000010, // 4 Only 47883 (Soulstone Resurrection) and test spell. SPELL_ATTR7_SUMMON_PLAYER_TOTEM = 0x00000020, // 5 Only Shaman player totems. - SPELL_ATTR7_UNK6 = 0x00000040, // 6 Dark Surge, Surge of Light, Burning Breath triggers (boss spells). + SPELL_ATTR7_NO_PUSHBACK_ON_DAMAGE = 0x00000040, // 6 Does not cause spell pushback on damage SPELL_ATTR7_UNK7 = 0x00000080, // 7 66218 (Launch) spell. SPELL_ATTR7_HORDE_ONLY = 0x00000100, // 8 Teleports, mounts and other spells. SPELL_ATTR7_ALLIANCE_ONLY = 0x00000200, // 9 Teleports, mounts and other spells. diff --git a/src/server/game/Reputation/ReputationMgr.cpp b/src/server/game/Reputation/ReputationMgr.cpp index 69e677bd89d..46b73e74068 100644 --- a/src/server/game/Reputation/ReputationMgr.cpp +++ b/src/server/game/Reputation/ReputationMgr.cpp @@ -24,7 +24,7 @@ #include "World.h" #include "ObjectMgr.h" #include "ScriptMgr.h" -#include "Opcodes.h" +#include "WorldSession.h" const int32 ReputationMgr::PointsInRank[MAX_REPUTATION_RANK] = {36000, 3000, 3000, 3000, 6000, 12000, 21000, 1000}; diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h index e581d7d1544..7b07abdf962 100644 --- a/src/server/game/Server/Protocol/Opcodes.h +++ b/src/server/game/Server/Protocol/Opcodes.h @@ -25,12 +25,6 @@ #include "Common.h" -// Note: this include need for be sure have full definition of class WorldSession -// if this class definition not complete then VS for x64 release use different size for -// struct OpcodeHandler in this header and Opcode.cpp and get totally wrong data from -// table opcodeTable in source when Opcode.h included but WorldSession.h not included -#include "WorldSession.h" - /// List of Opcodes enum Opcodes { @@ -1366,8 +1360,15 @@ enum PacketProcessing PROCESS_THREADSAFE //packet is thread-safe - process it in Map::Update() }; +class WorldSession; class WorldPacket; +#if defined(__GNUC__) +#pragma pack(1) +#else +#pragma pack(push, 1) +#endif + struct OpcodeHandler { char const* name; @@ -1378,6 +1379,12 @@ struct OpcodeHandler extern OpcodeHandler opcodeTable[NUM_MSG_TYPES]; +#if defined(__GNUC__) +#pragma pack() +#else +#pragma pack(pop) +#endif + /// Lookup opcode name for human understandable logging inline const char* LookupOpcodeName(uint16 id) { diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 89242bada6e..eda7b8917a7 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -121,8 +121,9 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 m_TutorialsChanged(false), recruiterId(recruiter), isRecruiter(isARecruiter), - timeLastWhoCommand(0), - _RBACData(NULL) + _RBACData(NULL), + expireTime(60000), // 1 min after socket loss, session is deleted + forceExit(false) { memset(m_Tutorials, 0, sizeof(m_Tutorials)); @@ -277,12 +278,13 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) //! loop caused by re-enqueueing the same packets over and over again, we stop updating this session //! and continue updating others. The re-enqueued packets will be handled in the next Update call for this session. uint32 processedPackets = 0; + time_t currentTime = time(NULL); while (m_Socket && !m_Socket->IsClosed() && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater)) { - if (!AntiDOS.EvaluateOpcode(*packet)) + if (!AntiDOS.EvaluateOpcode(*packet, currentTime)) { KickPlayer(); } @@ -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(); @@ -569,7 +574,6 @@ void WorldSession::LogoutPlayer(bool save) m_playerLogout = false; m_playerSave = false; m_playerRecentlyLogout = true; - AntiDOS.AllowOpcode(CMSG_CHAR_ENUM, true); LogoutRequest(0); } @@ -577,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, ...) @@ -1236,14 +1243,39 @@ void WorldSession::InvalidateRBACData() _RBACData = NULL; } -bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p) const +bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) const { - if (IsOpcodeAllowed(p.GetOpcode())) - return true; + PacketCounter& packetCounter = _PacketThrottlingMap[p.GetOpcode()]; + if (packetCounter.lastReceiveTime != time) + { + packetCounter.lastReceiveTime = time; + packetCounter.amountCounter = 0; + } + + uint32 maxPacketCounterAllowed = GetMaxPacketCounterAllowed(p.GetOpcode()); + + bool dosTriggered = false; + // Check if player is flooding some packets + if (++packetCounter.amountCounter > maxPacketCounterAllowed) + { + dosTriggered = true; + 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); + } + + // Then check if player is sending packets not allowed + if (!IsOpcodeAllowed(p.GetOpcode())) + { + dosTriggered = true; + // Opcode not allowed, let the punishment begin + TC_LOG_WARN("network", "AntiDOS: Account %u, IP: %s, sent unacceptable packet (opc: %u, size: %u)", + Session->GetAccountId(), Session->GetRemoteAddress().c_str(), p.GetOpcode(), (uint32)p.size()); + } - // Opcode not allowed, let the punishment begin - TC_LOG_INFO("network", "AntiDOS: Account %u, IP: %s, sent unacceptable packet (opc: %u, size: %u)", - Session->GetAccountId(), Session->GetRemoteAddress().c_str(), p.GetOpcode(), (uint32)p.size()); + // Return true if everything is fine, otherwise apply the configured policy + if (!dosTriggered) + return true; switch (_policy) { @@ -1272,3 +1304,225 @@ bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p) const return true; } } + + +uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) const +{ + uint32 maxPacketCounterAllowed; + switch (opcode) + { + // These opcodes are spammed by few addons so a very high limit is required + case CMSG_QUEST_QUERY: + case CMSG_MESSAGECHAT: + case CMSG_ITEM_QUERY_SINGLE: + case CMSG_ITEM_NAME_QUERY: + case CMSG_GAMEOBJECT_QUERY: + case CMSG_NAME_QUERY: + case CMSG_PET_NAME_QUERY: + case CMSG_CREATURE_QUERY: + case CMSG_NPC_TEXT_QUERY: + case CMSG_QUESTGIVER_STATUS_QUERY: + { + maxPacketCounterAllowed = 5000; + break; + } + + case CMSG_ATTACKSTOP: + case CMSG_GUILD_QUERY: + case CMSG_ARENA_TEAM_QUERY: + case CMSG_TAXINODE_STATUS_QUERY: + case CMSG_TAXIQUERYAVAILABLENODES: + case CMSG_QUESTGIVER_QUERY_QUEST: + case CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY: + case CMSG_QUERY_QUESTS_COMPLETED: + case CMSG_QUEST_POI_QUERY: + case CMSG_QUERY_TIME: + case CMSG_PAGE_TEXT_QUERY: + case CMSG_PETITION_QUERY: + case CMSG_QUERY_INSPECT_ACHIEVEMENTS: + case CMSG_AREA_SPIRIT_HEALER_QUERY: + case CMSG_CORPSE_MAP_POSITION_QUERY: + case CMSG_MOVE_TIME_SKIPPED: + case CMSG_GUILD_BANK_QUERY_TAB: + case MSG_GUILD_BANK_LOG_QUERY: + case MSG_QUERY_GUILD_BANK_TEXT: + case MSG_CORPSE_QUERY: + case MSG_QUERY_NEXT_MAIL_TIME: + case MSG_GUILD_EVENT_LOG_QUERY: + case MSG_MOVE_SET_FACING: + case CMSG_INSPECT: + { + maxPacketCounterAllowed = 500; + break; + } + + case CMSG_REQUEST_PARTY_MEMBER_STATS: + case CMSG_WHO: + case CMSG_SETSHEATHED: + case CMSG_CONTACT_LIST: + case CMSG_GUILD_SET_PUBLIC_NOTE: + case CMSG_GUILD_SET_OFFICER_NOTE: + { + maxPacketCounterAllowed = 50; + break; + } + + case CMSG_SPELLCLICK: + case CMSG_GAMEOBJ_USE: + case CMSG_GAMEOBJ_REPORT_USE: + case MSG_RAID_TARGET_UPDATE: + case CMSG_QUESTGIVER_COMPLETE_QUEST: + case CMSG_PLAYER_VEHICLE_ENTER: + case CMSG_PETITION_SIGN: + { + maxPacketCounterAllowed = 20; + break; + } + + case CMSG_PLAYER_LOGOUT: + case CMSG_LOGOUT_REQUEST: + case CMSG_LOGOUT_CANCEL: + case CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE: + case CMSG_REQUEST_VEHICLE_PREV_SEAT: + case CMSG_REQUEST_VEHICLE_NEXT_SEAT: + case CMSG_REQUEST_VEHICLE_SWITCH_SEAT: + case CMSG_TOGGLE_PVP: + case CMSG_ADD_FRIEND: + case CMSG_DEL_FRIEND: + case CMSG_SET_CONTACT_NOTES: + case CMSG_RESET_INSTANCES: + case CMSG_HEARTH_AND_RESURRECT: + case CMSG_CHAR_CREATE: + case CMSG_READY_FOR_ACCOUNT_DATA_TIMES: + case CMSG_CHAR_ENUM: + case CMSG_REALM_SPLIT: + case CMSG_CHAR_DELETE: + case CMSG_PLAYER_LOGIN: + case CMSG_PET_ABANDON: + case CMSG_PET_RENAME: + case CMSG_CHAR_RENAME: + case CMSG_CHAR_CUSTOMIZE: + case CMSG_CHAR_RACE_CHANGE: + case CMSG_CHAR_FACTION_CHANGE: + case CMSG_GMTICKET_CREATE: + case CMSG_GMTICKET_UPDATETEXT: + case CMSG_GMTICKET_DELETETICKET: + case CMSG_GMSURVEY_SUBMIT: + case CMSG_GM_REPORT_LAG: + case CMSG_BUG: + case CMSG_GMRESPONSE_RESOLVE: + case CMSG_ACTIVATETAXIEXPRESS: + case CMSG_ACTIVATETAXI: + case CMSG_SELF_RES: + case CMSG_INITIATE_TRADE: + case CMSG_BEGIN_TRADE: + case CMSG_UNLEARN_SKILL: + case CMSG_DISMISS_CONTROLLED_VEHICLE: + case CMSG_REQUEST_VEHICLE_EXIT: + case CMSG_LEARN_PREVIEW_TALENTS: + case CMSG_LEARN_PREVIEW_TALENTS_PET: + case CMSG_CONTROLLER_EJECT_PASSENGER: + case CMSG_EQUIPMENT_SET_SAVE: + case CMSG_DELETEEQUIPMENT_SET: + case CMSG_REMOVE_GLYPH: + case CMSG_ALTER_APPEARANCE: + case CMSG_QUESTGIVER_ACCEPT_QUEST: + case CMSG_QUESTGIVER_CHOOSE_REWARD: + case CMSG_QUESTGIVER_REQUEST_REWARD: + case CMSG_QUESTGIVER_CANCEL: + case CMSG_QUESTLOG_REMOVE_QUEST: + case CMSG_QUEST_CONFIRM_ACCEPT: + case CMSG_DISMISS_CRITTER: + case CMSG_REPOP_REQUEST: + case CMSG_PETITION_BUY: + case CMSG_TURN_IN_PETITION: + case CMSG_COMPLETE_CINEMATIC: + case CMSG_ITEM_REFUND: + case CMSG_SOCKET_GEMS: + case CMSG_WRAP_ITEM: + case CMSG_BUY_BANK_SLOT: + case CMSG_GROUP_ACCEPT: + case CMSG_GROUP_DECLINE: + case CMSG_GROUP_UNINVITE_GUID: + case CMSG_GROUP_UNINVITE: + case CMSG_GROUP_SET_LEADER: + case CMSG_GROUP_DISBAND: + case CMSG_GROUP_RAID_CONVERT: + case CMSG_GROUP_CHANGE_SUB_GROUP: + case CMSG_GROUP_ASSISTANT_LEADER: + case CMSG_OPT_OUT_OF_LOOT: + case CMSG_BATTLEMASTER_JOIN_ARENA: + case CMSG_LEAVE_BATTLEFIELD: + case CMSG_REPORT_PVP_AFK: + case CMSG_DUEL_ACCEPTED: + case CMSG_DUEL_CANCELLED: + case CMSG_CALENDAR_GET_CALENDAR: + case CMSG_CALENDAR_ADD_EVENT: + case CMSG_CALENDAR_UPDATE_EVENT: + case CMSG_CALENDAR_REMOVE_EVENT: + case CMSG_CALENDAR_COPY_EVENT: + case CMSG_CALENDAR_EVENT_INVITE: + case CMSG_CALENDAR_EVENT_SIGNUP: + case CMSG_CALENDAR_EVENT_RSVP: + case CMSG_CALENDAR_EVENT_REMOVE_INVITE: + case CMSG_CALENDAR_EVENT_MODERATOR_STATUS: + case CMSG_CALENDAR_COMPLAIN: + case CMSG_ARENA_TEAM_INVITE: + case CMSG_ARENA_TEAM_ACCEPT: + case CMSG_ARENA_TEAM_DECLINE: + case CMSG_ARENA_TEAM_LEAVE: + case CMSG_ARENA_TEAM_DISBAND: + case CMSG_ARENA_TEAM_REMOVE: + case CMSG_ARENA_TEAM_LEADER: + case CMSG_LOOT_METHOD: + case CMSG_GUILD_INVITE: + case CMSG_GUILD_ACCEPT: + case CMSG_GUILD_DECLINE: + case CMSG_GUILD_LEAVE: + case CMSG_GUILD_DISBAND: + case CMSG_GUILD_LEADER: + case CMSG_GUILD_MOTD: + case CMSG_GUILD_RANK: + case CMSG_GUILD_ADD_RANK: + case CMSG_GUILD_DEL_RANK: + case CMSG_GUILD_INFO_TEXT: + case CMSG_GUILD_BANK_DEPOSIT_MONEY: + case CMSG_GUILD_BANK_WITHDRAW_MONEY: + case CMSG_GUILD_BANK_BUY_TAB: + case CMSG_GUILD_BANK_UPDATE_TAB: + case CMSG_SET_GUILD_BANK_TEXT: + case MSG_SAVE_GUILD_EMBLEM: + case MSG_PETITION_RENAME: + case MSG_PETITION_DECLINE: + case MSG_TALENT_WIPE_CONFIRM: + case MSG_SET_DUNGEON_DIFFICULTY: + case MSG_SET_RAID_DIFFICULTY: + case MSG_RANDOM_ROLL: + case MSG_PARTY_ASSIGNMENT: + case MSG_RAID_READY_CHECK: + { + maxPacketCounterAllowed = 3; + break; + } + + case CMSG_SET_ACTION_BUTTON: + { + maxPacketCounterAllowed = MAX_ACTION_BUTTONS; + break; + } + + case CMSG_ITEM_REFUND_INFO: + { + maxPacketCounterAllowed = PLAYER_SLOTS_COUNT; + break; + } + + default: + { + maxPacketCounterAllowed = 100; + break; + } + } + + return maxPacketCounterAllowed; +} diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 9d1a05ae753..82125aafd6d 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -28,9 +28,11 @@ #include "AddonMgr.h" #include "DatabaseEnv.h" #include "World.h" +#include "Opcodes.h" #include "WorldPacket.h" #include "Cryptography/BigNumber.h" #include "AccountMgr.h" +#include <unordered_set> class Creature; class GameObject; @@ -196,6 +198,12 @@ class CharacterCreateInfo uint8 CharCount; }; +struct PacketCounter +{ + time_t lastReceiveTime; + uint32 amountCounter; +}; + /// Player session in the World class WorldSession { @@ -721,7 +729,6 @@ class WorldSession void HandleCompleteCinematic(WorldPacket& recvPacket); void HandleNextCinematicCamera(WorldPacket& recvPacket); - void HandlePageQuerySkippedOpcode(WorldPacket& recvPacket); void HandlePageTextQueryOpcode(WorldPacket& recvPacket); void HandleTutorialFlag (WorldPacket& recvData); @@ -929,7 +936,7 @@ class WorldSession friend class World; public: DosProtection(WorldSession* s) : Session(s), _policy((Policy)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_POLICY)) { } - bool EvaluateOpcode(WorldPacket& p) const; + bool EvaluateOpcode(WorldPacket& p, time_t time) const; void AllowOpcode(uint16 opcode, bool allow) { _isOpcodeAllowed[opcode] = allow; } protected: enum Policy @@ -948,12 +955,17 @@ class WorldSession return itr->second; } + uint32 GetMaxPacketCounterAllowed(uint16 opcode) const; + WorldSession* Session; private: typedef std::unordered_map<uint16, bool> OpcodeStatusMap; OpcodeStatusMap _isOpcodeAllowed; // could be bool array, but wouldn't be practical for game versions with non-linear opcodes Policy _policy; + typedef std::unordered_map<uint16, PacketCounter> PacketThrottlingMap; + // mark this member as "mutable" so it can be modified even in const functions + mutable PacketThrottlingMap _PacketThrottlingMap; DosProtection(DosProtection const& right) = delete; DosProtection& operator=(DosProtection const& right) = delete; @@ -1008,8 +1020,9 @@ class WorldSession uint32 recruiterId; bool isRecruiter; ACE_Based::LockedQueue<WorldPacket*, ACE_Thread_Mutex> _recvQueue; - time_t timeLastWhoCommand; 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/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 0dc6acdd4d8..07691e14e4f 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -5856,7 +5856,8 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const damage = caster->SpellCriticalDamageBonus(m_spellInfo, damage, target); int32 dmg = damage; - caster->ApplyResilience(target, NULL, &dmg, crit, CR_CRIT_TAKEN_SPELL); + if (!(GetSpellInfo()->AttributesEx4 & SPELL_ATTR4_FIXED_DAMAGE)) + caster->ApplyResilience(target, NULL, &dmg, crit, CR_CRIT_TAKEN_SPELL); damage = dmg; caster->CalcAbsorbResist(target, GetSpellInfo()->GetSchoolMask(), DOT, damage, &absorb, &resist, GetSpellInfo()); @@ -5923,7 +5924,8 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c } int32 dmg = damage; - caster->ApplyResilience(target, NULL, &dmg, crit, CR_CRIT_TAKEN_SPELL); + if (!(GetSpellInfo()->AttributesEx4 & SPELL_ATTR4_FIXED_DAMAGE)) + caster->ApplyResilience(target, NULL, &dmg, crit, CR_CRIT_TAKEN_SPELL); damage = dmg; caster->CalcAbsorbResist(target, GetSpellInfo()->GetSchoolMask(), DOT, damage, &absorb, &resist, m_spellInfo); 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/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 21c36510a32..ba4c3deca85 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -24,6 +24,7 @@ #include "Player.h" #include "Battleground.h" #include "Vehicle.h" +#include "Pet.h" uint32 GetTargetFlagMask(SpellTargetObjectTypes objType) { @@ -1505,8 +1506,16 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta // creature/player specific target checks if (unitTarget) { - if (AttributesEx & SPELL_ATTR1_CANT_TARGET_IN_COMBAT && unitTarget->IsInCombat()) - return SPELL_FAILED_TARGET_AFFECTING_COMBAT; + if (AttributesEx & SPELL_ATTR1_CANT_TARGET_IN_COMBAT) + { + if (unitTarget->IsInCombat()) + return SPELL_FAILED_TARGET_AFFECTING_COMBAT; + // player with active pet counts as a player in combat + else if (Player const* player = unitTarget->ToPlayer()) + if (Pet* pet = player->GetPet()) + if (pet->GetVictim() && !pet->HasUnitState(UNIT_STATE_CONTROLLED)) + return SPELL_FAILED_TARGET_AFFECTING_COMBAT; + } // only spells with SPELL_ATTR3_ONLY_TARGET_GHOSTS can target ghosts if (((AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_GHOSTS) != 0) != unitTarget->HasAuraType(SPELL_AURA_GHOST)) diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 501cb1e77f2..9eadd178eb3 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3207,6 +3207,9 @@ void SpellMgr::LoadSpellInfoCorrections() case 63675: // Improved Devouring Plague spellInfo->AttributesEx3 |= SPELL_ATTR3_NO_DONE_BONUS; break; + case 12721: // Deep Wounds shouldnt ignore resillience or damage taken auras because its damage is not based off a spell. + spellInfo->AttributesEx4 = 0; + break; case 8145: // Tremor Totem (instant pulse) case 6474: // Earthbind Totem (instant pulse) spellInfo->AttributesEx5 |= SPELL_ATTR5_START_PERIODIC_AT_APPLY; @@ -3382,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/game/Weather/Weather.cpp b/src/server/game/Weather/Weather.cpp index cb332df9a41..8d39f553910 100644 --- a/src/server/game/Weather/Weather.cpp +++ b/src/server/game/Weather/Weather.cpp @@ -28,7 +28,7 @@ #include "ObjectMgr.h" #include "Util.h" #include "ScriptMgr.h" -#include "Opcodes.h" +#include "WorldSession.h" /// Create the Weather object Weather::Weather(uint32 zone, WeatherData const* weatherChances) diff --git a/src/server/game/Weather/WeatherMgr.cpp b/src/server/game/Weather/WeatherMgr.cpp index 59dc591ccd0..5cf5cde75fd 100644 --- a/src/server/game/Weather/WeatherMgr.cpp +++ b/src/server/game/Weather/WeatherMgr.cpp @@ -27,7 +27,7 @@ #include "AutoPtr.h" #include "Player.h" #include "WorldPacket.h" -#include "Opcodes.h" +#include "WorldSession.h" namespace WeatherMgr { diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp index e645dd383f2..b3a55e1fe4a 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp @@ -26,7 +26,7 @@ #include "ScriptedCreature.h" #include "blackrock_spire.h" -uint32 const DragonspireRunes[7] = { GO_HALL_RUNE_1, GO_HALL_RUNE_2, GO_HALL_RUNE_3, GO_HALL_RUNE_4, GO_HALL_RUNE_5, GO_HALL_RUNE_6, GO_HALL_RUNE_7 }; +//uint32 const DragonspireRunes[7] = { GO_HALL_RUNE_1, GO_HALL_RUNE_2, GO_HALL_RUNE_3, GO_HALL_RUNE_4, GO_HALL_RUNE_5, GO_HALL_RUNE_6, GO_HALL_RUNE_7 }; uint32 const DragonspireMobs[3] = { NPC_BLACKHAND_DREADWEAVER, NPC_BLACKHAND_SUMMONER, NPC_BLACKHAND_VETERAN }; 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/Naxxramas/boss_kelthuzad.cpp b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp index 8b3ac64fb89..c5c70cf3957 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp @@ -368,38 +368,33 @@ public: if (Phase == 1) { - while (uint32 eventId = events.GetEvent()) + while (uint32 eventId = events.ExecuteEvent()) { switch (eventId) { case EVENT_WASTE: DoSummon(NPC_WASTE, Pos[RAND(0, 3, 6, 9)]); - events.RepeatEvent(urand(2000, 5000)); + events.Repeat(2000, 5000); break; case EVENT_ABOMIN: if (nAbomination < 8) { DoSummon(NPC_ABOMINATION, Pos[RAND(1, 4, 7, 10)]); nAbomination++; - events.RepeatEvent(20000); + events.Repeat(20000); } - else - events.PopEvent(); break; case EVENT_WEAVER: if (nWeaver < 8) { DoSummon(NPC_WEAVER, Pos[RAND(0, 3, 6, 9)]); nWeaver++; - events.RepeatEvent(25000); + events.Repeat(25000); } - else - events.PopEvent(); break; case EVENT_TRIGGER: if (GameObject* trigger = ObjectAccessor::GetGameObject(*me, instance->GetData64(DATA_KELTHUZAD_TRIGGER))) trigger->SetPhaseMask(2, true); - events.PopEvent(); break; case EVENT_PHASE: events.Reset(); @@ -419,7 +414,6 @@ public: Phase = 2; break; default: - events.PopEvent(); break; } } @@ -461,17 +455,17 @@ public: if (me->HasUnitState(UNIT_STATE_CASTING)) return; - if (uint32 eventId = events.GetEvent()) + if (uint32 eventId = events.ExecuteEvent()) { switch (eventId) { case EVENT_BOLT: DoCastVictim(SPELL_FROST_BOLT); - events.RepeatEvent(urand(5000, 10000)); + events.Repeat(5000, 10000); break; case EVENT_NOVA: DoCastAOE(SPELL_FROST_BOLT_AOE); - events.RepeatEvent(urand(15000, 30000)); + events.Repeat(15000, 30000); break; case EVENT_CHAIN: { @@ -490,7 +484,7 @@ public: } if (!chained.empty()) Talk(SAY_CHAIN); - events.RepeatEvent(urand(100000, 180000)); + events.Repeat(100000, 180000); break; } case EVENT_CHAINED_SPELL: @@ -565,10 +559,8 @@ public: ++itr; } - if (chained.empty()) - events.PopEvent(); - else - events.RepeatEvent(5000); + if (!chained.empty()) + events.Repeat(5000); break; } @@ -596,23 +588,22 @@ public: Talk(SAY_SPECIAL); } - events.RepeatEvent(urand(20000, 50000)); + events.Repeat(20000, 50000); break; } case EVENT_FISSURE: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) DoCast(target, SPELL_SHADOW_FISURE); - events.RepeatEvent(urand(10000, 45000)); + events.Repeat(10000, 45000); break; case EVENT_BLAST: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, RAID_MODE(1, 0), 0, true)) DoCast(target, SPELL_FROST_BLAST); if (rand()%2) Talk(SAY_FROST_BLAST); - events.RepeatEvent(urand(30000, 90000)); + events.Repeat(30000, 90000); break; default: - events.PopEvent(); break; } } 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/Outland/TempestKeep/Eye/boss_kaelthas.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp index 6fbe633dd45..a24e1d5d34f 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp @@ -143,7 +143,7 @@ uint32 m_auiSpellSummonWeapon[]= }; const float CAPERNIAN_DISTANCE = 20.0f; //she casts away from the target -const float KAEL_VISIBLE_RANGE = 50.0f; +//const float KAEL_VISIBLE_RANGE = 50.0f; const float afGravityPos[3] = {795.0f, 0.0f, 70.0f}; diff --git a/src/server/scripts/Outland/zone_shadowmoon_valley.cpp b/src/server/scripts/Outland/zone_shadowmoon_valley.cpp index ba20c7e020b..116beb3d081 100644 --- a/src/server/scripts/Outland/zone_shadowmoon_valley.cpp +++ b/src/server/scripts/Outland/zone_shadowmoon_valley.cpp @@ -1867,13 +1867,6 @@ public: { npc_shadowmoon_tuber_nodeAI(Creature* creature) : ScriptedAI(creature) { } - void Reset() override - { - tapped = false; - tuberGUID = 0; - resetTimer = 60000; - } - void SetData(uint32 id, uint32 data) override { if (id == TYPE_BOAR && data == DATA_BOAR) @@ -1884,49 +1877,23 @@ public: // Despawn the tuber if (GameObject* tuber = me->FindNearestGameObject(GO_SHADOWMOON_TUBER_MOUND, 5.0f)) { - tuberGUID = tuber->GetGUID(); - // @Workaround: find how to properly despawn the GO - tuber->SetPhaseMask(2, true); + tuber->SetLootState(GO_JUST_DEACTIVATED); + me->DespawnOrUnsummon(); } } } void SpellHit(Unit* /*caster*/, const SpellInfo* spell) override { - if (!tapped && spell->Id == SPELL_WHISTLE) + if (spell->Id == SPELL_WHISTLE) { if (Creature* boar = me->FindNearestCreature(NPC_BOAR_ENTRY, 30.0f)) { - // Disable trigger and force nearest boar to walk to him - tapped = true; boar->SetWalk(false); boar->GetMotionMaster()->MovePoint(POINT_TUBER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); } } } - - void UpdateAI(uint32 diff) override - { - if (tapped) - { - if (resetTimer <= diff) - { - // Respawn the tuber - if (tuberGUID) - if (GameObject* tuber = GameObject::GetGameObject(*me, tuberGUID)) - // @Workaround: find how to properly respawn the GO - tuber->SetPhaseMask(1, true); - - Reset(); - } - else - resetTimer -= diff; - } - } - private: - bool tapped; - uint64 tuberGUID; - uint32 resetTimer; }; CreatureAI* GetAI(Creature* creature) const override 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_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index 2739a8453df..725312eafce 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -744,8 +744,13 @@ class spell_hun_sniper_training : public SpellScriptLoader { Unit* target = GetTarget(); uint32 spellId = SPELL_HUNTER_SNIPER_TRAINING_BUFF_R1 + GetId() - SPELL_HUNTER_SNIPER_TRAINING_R1; - if (!target->HasAura(spellId)) - target->CastSpell(target, spellId, true, 0, aurEff); + target->CastSpell(target, spellId, true, 0, aurEff); + if (Player* playerTarget = GetUnitOwner()->ToPlayer()) + { + int32 baseAmount = aurEff->GetBaseAmount(); + int32 amount = playerTarget->CalculateSpellDamage(playerTarget, GetSpellInfo(), aurEff->GetEffIndex(), &baseAmount); + GetEffect(EFFECT_0)->SetAmount(amount); + } } } 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/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp index f96a30c903a..dea67b5222d 100644 --- a/src/server/scripts/Spells/spell_priest.cpp +++ b/src/server/scripts/Spells/spell_priest.cpp @@ -205,7 +205,7 @@ class spell_pri_divine_hymn : public SpellScriptLoader void Register() override { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_pri_divine_hymn_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_pri_divine_hymn_SpellScript::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ALLY); } }; @@ -336,7 +336,7 @@ class spell_pri_hymn_of_hope : public SpellScriptLoader void Register() override { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_pri_hymn_of_hope_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_pri_hymn_of_hope_SpellScript::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ALLY); } }; 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/Containers.h b/src/server/shared/Containers.h index d6ba98e4ed4..9121fbe2a97 100644 --- a/src/server/shared/Containers.h +++ b/src/server/shared/Containers.h @@ -64,6 +64,34 @@ namespace Trinity std::advance(it, urand(0, container.size() - 1)); return *it; } + + /** + * @fn bool Trinity::Containers::Intersects(Iterator first1, Iterator last1, Iterator first2, Iterator last2) + * + * @brief Checks if two SORTED containers have a common element + * + * @param first1 Iterator pointing to start of the first container + * @param last1 Iterator pointing to end of the first container + * @param first2 Iterator pointing to start of the second container + * @param last2 Iterator pointing to end of the second container + * + * @return true if containers have a common element, false otherwise. + */ + template<class Iterator1, class Iterator2> + bool Intersects(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) + { + while (first1 != last1 && first2 != last2) + { + if (*first1 < *first2) + ++first1; + else if (*first2 < *first1) + ++first2; + else + return true; + } + + return false; + } } //! namespace Containers } diff --git a/src/server/shared/Packets/ByteBuffer.cpp b/src/server/shared/Packets/ByteBuffer.cpp index b8cb5215665..f446592e922 100644 --- a/src/server/shared/Packets/ByteBuffer.cpp +++ b/src/server/shared/Packets/ByteBuffer.cpp @@ -27,11 +27,10 @@ ByteBufferPositionException::ByteBufferPositionException(bool add, size_t pos, size_t size, size_t valueSize) { std::ostringstream ss; - ACE_Stack_Trace trace; ss << "Attempted to " << (add ? "put" : "get") << " value with size: " << valueSize << " in ByteBuffer (pos: " << pos << " size: " << size - << ")\n\n" << trace.c_str(); + << ")"; message().assign(ss.str()); } @@ -40,12 +39,10 @@ ByteBufferSourceException::ByteBufferSourceException(size_t pos, size_t size, size_t valueSize) { std::ostringstream ss; - ACE_Stack_Trace trace; ss << "Attempted to put a " << (valueSize > 0 ? "NULL-pointer" : "zero-sized value") - << " in ByteBuffer (pos: " << pos << " size: " << size << ")\n\n" - << trace.c_str(); + << " in ByteBuffer (pos: " << pos << " size: " << size << ")"; message().assign(ss.str()); } diff --git a/src/server/shared/Packets/ByteBuffer.h b/src/server/shared/Packets/ByteBuffer.h index dd0a9d5fdf4..9f766a72d19 100644 --- a/src/server/shared/Packets/ByteBuffer.h +++ b/src/server/shared/Packets/ByteBuffer.h @@ -31,6 +31,7 @@ #include <vector> #include <cstring> #include <time.h> +#include <math.h> // Root of ByteBuffer exception hierarchy class ByteBufferException : public std::exception @@ -241,12 +242,16 @@ class ByteBuffer ByteBuffer &operator>>(float &value) { value = read<float>(); + if (!std::isfinite(value)) + throw ByteBufferException(); return *this; } ByteBuffer &operator>>(double &value) { value = read<double>(); + if (!std::isfinite(value)) + throw ByteBufferException(); return *this; } 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 |