aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/updates/world/2014_05_30_00_world_quest_update.sql1
-rw-r--r--sql/updates/world/2014_06_03_world_creature.sql1
-rw-r--r--sql/updates/world/2014_06_06_00_world_misc.sql92
-rw-r--r--sql/updates/world/2014_06_06_00_world_quest_template.sql4
-rw-r--r--sql/updates/world/2014_06_07_00_world_misc.sql31
-rw-r--r--src/server/collision/Maps/MapTree.cpp3
-rw-r--r--src/server/collision/Models/GameObjectModel.cpp3
-rw-r--r--src/server/collision/Models/GameObjectModel.h3
-rw-r--r--src/server/game/AI/CoreAI/PetAI.cpp3
-rw-r--r--src/server/game/AI/CreatureAI.cpp5
-rw-r--r--src/server/game/AI/CreatureAI.h1
-rw-r--r--src/server/game/AI/CreatureAIImpl.h353
-rw-r--r--src/server/game/Accounts/AccountMgr.cpp2
-rw-r--r--src/server/game/Chat/Channels/Channel.h2
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp4
-rw-r--r--src/server/game/Entities/Item/Item.cpp2
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp2
-rw-r--r--src/server/game/Entities/Player/Player.cpp113
-rw-r--r--src/server/game/Entities/Player/Player.h14
-rw-r--r--src/server/game/Entities/Player/SocialMgr.cpp2
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp246
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp14
-rw-r--r--src/server/game/Handlers/ItemHandler.cpp13
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp12
-rw-r--r--src/server/game/Handlers/PetHandler.cpp7
-rw-r--r--src/server/game/Handlers/QueryHandler.cpp16
-rw-r--r--src/server/game/Handlers/SkillHandler.cpp7
-rw-r--r--src/server/game/Handlers/TicketHandler.cpp12
-rw-r--r--src/server/game/Maps/Map.cpp7
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h4
-rw-r--r--src/server/game/Reputation/ReputationMgr.cpp2
-rw-r--r--src/server/game/Server/Protocol/Opcodes.h19
-rw-r--r--src/server/game/Server/WorldSession.cpp280
-rw-r--r--src/server/game/Server/WorldSession.h19
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp6
-rw-r--r--src/server/game/Spells/Spell.cpp2
-rw-r--r--src/server/game/Spells/SpellInfo.cpp13
-rw-r--r--src/server/game/Spells/SpellMgr.cpp6
-rw-r--r--src/server/game/Texts/CreatureTextMgr.cpp13
-rw-r--r--src/server/game/Weather/Weather.cpp2
-rw-r--r--src/server/game/Weather/WeatherMgr.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp2
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/instance_culling_of_stratholme.cpp2
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h2
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp35
-rw-r--r--src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp5
-rw-r--r--src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp2
-rw-r--r--src/server/scripts/Outland/zone_shadowmoon_valley.cpp39
-rw-r--r--src/server/scripts/Spells/spell_dk.cpp10
-rw-r--r--src/server/scripts/Spells/spell_hunter.cpp9
-rw-r--r--src/server/scripts/Spells/spell_paladin.cpp4
-rw-r--r--src/server/scripts/Spells/spell_priest.cpp4
-rw-r--r--src/server/scripts/World/npcs_special.cpp56
-rw-r--r--src/server/shared/Containers.h28
-rw-r--r--src/server/shared/Packets/ByteBuffer.cpp7
-rw-r--r--src/server/shared/Packets/ByteBuffer.h5
-rw-r--r--src/server/shared/Utilities/Util.h350
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