diff --git a/.gitattributes b/.gitattributes index 9793ee465..9fa544d91 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,16 @@ +# Lineendings *.sln eol=crlf *.vcproj eol=crlf *.vcxproj* eol=crlf + +# Whitespace rules +# strict (no trailing, no tabs) +*.cpp whitespace=trailing-space,space-before-tab,tab-in-indent,cr-at-eol +*.h whitespace=trailing-space,space-before-tab,tab-in-indent,cr-at-eol + +# normal (no trailing) +*.sql whitespace=trailing-space,space-before-tab,cr-at-eol +*.txt whitespace=trailing-space,space-before-tab,cr-at-eol + +# special files which must ignore whitespace +*.patch whitespace=-trailing-space diff --git a/CMakeLists.txt b/CMakeLists.txt index caefe0694..25c5e9bc4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,7 +125,9 @@ if(CMAKE_INSTALL_PREFIX STREQUAL "/usr/local") set(CMAKE_INSTALL_PREFIX ${PREFIX_ABSOLUTE} CACHE PATH "Install path prefix." FORCE) endif() if(PREFIX) - string(REGEX REPLACE "^~" "$ENV{HOME}" PREFIX ${PREFIX}) + if(!WIN32) + string(REGEX REPLACE "^~" "$ENV{HOME}" PREFIX ${PREFIX}) + endif() get_filename_component(PREFIX_ABSOLUTE ${PREFIX} ABSOLUTE) set(CMAKE_INSTALL_PREFIX ${PREFIX} CACHE PATH "Install path prefix." FORCE) else() @@ -285,12 +287,12 @@ if(UNIX) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Wfatal-errors -Wextra") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wfatal-errors -Wextra") elseif(WIN32) - # Disable warnings in Visual Studio 8 and above + # Disable warnings in Visual Studio 8 and above and add /MP if(MSVC AND NOT CMAKE_GENERATOR MATCHES "Visual Studio 7") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /wd4996 /wd4355 /wd4244 /wd4985 /wd4267") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /wd4996 /wd4355 /wd4244 /wd4267") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /wd4996 /wd4355 /wd4244 /wd4985 /wd4267") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /wd4996 /wd4355 /wd4244 /wd4985 /wd4267") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /wd4996 /wd4355 /wd4244 /wd4985 /wd4267 /MP") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /wd4996 /wd4355 /wd4244 /wd4267 /MP") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /wd4996 /wd4355 /wd4244 /wd4985 /wd4267 /MP") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /wd4996 /wd4355 /wd4244 /wd4985 /wd4267 /MP") endif() endif() diff --git a/README.PLAYERBOT b/README.PLAYERBOT index 80ccc6a28..f986ade9b 100644 --- a/README.PLAYERBOT +++ b/README.PLAYERBOT @@ -26,7 +26,6 @@ Commands: /t BOTNAME equip /t BOTNAME reset (will reset states, orders and loot list) /t BOTNAME report (bot reports all items needed to finish quests) -/t BOTNAME point (bot will attack selected target) /t BOTNAME stats (bot shows available money, free inventory space and estimated item repair costs) /t BOTNAME survey (bot shows all available gameobjects, within a local perimeter around the bot) /t BOTNAME find (bot will travel to the gameobject location and then wait) @@ -151,16 +150,16 @@ I added the following in SharedDefines.h. enum SpellCategory { - SPELL_CATEGORY_FOOD = 11, - SPELL_CATEGORY_DRINK = 59 + SPELL_CATEGORY_FOOD = 11, + SPELL_CATEGORY_DRINK = 59 }; I also had to add the following to Player.h: enum PlayerStateType { - PLAYER_STATE_NONE = 0, - PLAYER_STATE_SIT = 1 + PLAYER_STATE_NONE = 0, + PLAYER_STATE_SIT = 1 }; @@ -183,3 +182,48 @@ Some Problems: ============== The bots don't always face in the right direction. Sometimes when a bot makes the kill, the corpse is not lootable. The mage bot sometimes get stuck when he begins to cast a spell (but this is corrected the next time he enters combat). + +-- BotGuy +What it is: +=========== + +The new revised'botguy' utilizes NPCs already distributed throughout the world, to allow players to +summon and dismiss bots at will, from their own account. + +This is a revised (more stable) version that utilizes the new 'GOSSIP MENU SYSTEM' to modify the menus of existing NPCs +(e.g Trainers etc) to include the bot Recruit/Dismiss menu. (No GameMaster account necessary). + +Install (Server administrators only) +======= +Please apply 'mangos_botguy.sql' once to the world database to update the 'gossip_menu_option' table. + +-- ToDo +[DONE] ///---Quest---/// +[DONE] Bot can accept quest. +[DONE] Bot can join quest. + +[DONE] ///---Loot---/// +[DONE] Bot can loot. +[DONE] Bot can loot --- maybe only needed q item. + +//---Instance teleport Problems---/// +[DONE] Needs some fix. + +///---Combat and Movement Orders---/// +[DONE] Movement orders (stay, follow) +[DONE] Combat orders (protect, assist) +Combat order TANK +Combat order HEAL + +///---Temporary item enchantments---/// +[DONE] Rogue : Poison +Warrior : Sharpening Stone, Rune of Warding, Rune of Shielding. + +//---After fear bot's lose target---/// +Needs some fix. + +///---Move behind target---/// +Rogue : some abilitys require Rogue to be stealthed and behind target. + +///---Implement locale independet way of getting spellIDs---/// +[DONE] Hardcode lowest rank spellID, use function to get highest rank diff --git a/README.PlayerBot b/README.PlayerBot new file mode 100644 index 000000000..f986ade9b --- /dev/null +++ b/README.PlayerBot @@ -0,0 +1,229 @@ +What it is: +=========== + +Playerbot lets you add another character from your account as a bot that you can control and which will hopefully help you. Only characters from your account can be used, so you can have a maximum of 9 bots at one time. + +This was taken from the Trinity site, and modified slightly by me to get some of the kinks out. I reworked the priest class and also added a mage class and a warrior class, which are still in crude form. Any class can be used as a bot - just don't expect much in the way of spells or abilities until someone writes the code for them. + +Bots will only use abilities that they have - for example, a priest will only use the renew spell if it has been trained. Also, bot's equipment will lose durability like any other character. So every so often you'll need to log in and repair and train your bot. + +For Mangos 7800+ + +Commands: +========= + +/s .bot add BOTNAME (add character to world) +/s .bot remove BOTNAME +/s .bot co|combatorder BOTNAME COMBATORDER [TARGET] +/invite BOTNAME (bot will auto accept invite) +/t BOTNAME attack (bot will attack selected target, similar to the way a pet can attack) +/t BOTNAME follow (orders bot to follow player; will also revive bot if dead or teleport bot if far away) +/t BOTNAME stay +/t BOTNAME assist (you'll need to be attacking something and the bot only does melee atm) +/t BOTNAME spells (replies with all spells known to bot) +/t BOTNAME cast +/t BOTNAME use +/t BOTNAME equip +/t BOTNAME reset (will reset states, orders and loot list) +/t BOTNAME report (bot reports all items needed to finish quests) +/t BOTNAME stats (bot shows available money, free inventory space and estimated item repair costs) +/t BOTNAME survey (bot shows all available gameobjects, within a local perimeter around the bot) +/t BOTNAME find (bot will travel to the gameobject location and then wait) +/t BOTNAME get (bot will fetch the selected gameobject and then return to the player) +/t BOTNAME quests (List bot's current quests) +/t BOTNAME drop (Drop a quest) +/t BOTNAME orders (Shows bot's combat orders) +/t BOTNAME pet spells (Shows spells known to bot's pet. Autocast spells will be shown in green) +/t BOTNAME pet cast +/t BOTNAME pet toggle (Toggle autocast for a given spell) +/t BOTNAME pet state (Shows current react mode of bot's pet) +/t BOTNAME pet react <(a)ggressive | (d)efensive | (p)assive> (Set bot's pet reaction mode) + +Shortcuts: +c = cast +e = equip +u = use + +Gameobject interaction with bots: +================================= + + The bot(s) can now interact with gameobjects. This is particularly useful, in order to complete 'gather' type + quests (e.g Milly's harvest in Northshire). The bot(s) can also now, harvest 'ore deposits' and 'herbs' + + Three new commands have been introduced 'survey, 'find' & 'get', to facilitate this new feature. + + The 'survey' command provides the means for bot(s) to detect gameobjects in the world. It can be used to detect + available gameobjects local to a single bot, or more effectively (wider area) those for a party of bots. + + Suggestion: setup the 'survey' command as an assigned macro button, on the client (e.g /p survey). You can + then quickly refresh the gameobject list. + + Gameobject list (Currently bots can only interact with ore, herb and needed quest items) + --------------- + + [Copper Vein][Silverleaf][Earthroot][Milly's Harvest][Battered Chest][Food Crate] + + Then, use the 'find' or 'get' commands to interect with the gameobject. + + Using the gameobject list information, it is possible to locate and/or fetch each of the gameobjects. To select + a , hold down the shift key and click on the relevant link with your mouse. + +Repair with bots: +================= + + The bot(s) can now be repaired, as the player repairs. You can decide whether you wish the bot(s) to pay for + their own repair or if available, use the guild bank. Choose the appropriate repair 'Anvil' at your local + NPC. Only group bot(s) members can be repaired. If you wish to exclude certain bot(s) from repair, then + temporarily uninvite bot(s) from the group. + + Limitations: Bot(s) cannot repair individual items. + If the player does not require repair, you cannot repair bot(s). + + The new 'stats' command provides useful information to help in the repair decision. + + First: Money available to bot(s) + Second: Free inventory slots for bot(s) + Third: Estimated (excludes NPC reputation discount) item damage cost for bot(s). + +Combat Orders explained: +======================== + + There are primary and secondary commands which can be combined. In this way it is + possible to define a bot to assist the main tank and also protect the healer, making + combat management much easier. + The commands assist and protect require a target parameter or a friendly player + selected by bots master. + Available Combat Orders: + tank pri try to bind all targets involved in combat by gaining highest threat + assist pri do damage on selected targets attacker without getting highest threat + heal pri concentrate on healing - no offensive spells, try to keep threat low + protect sec if target of protect get's directly attacked gain higher threat on attacker + reset - clear out assist and protect targets and set combat order to nothing + Examples: + .bot co TheTank tank + .bot co MyHealer heal + .bot co TheBrutal assist TheTank + .bot co TheBrutal protect MyHealer + +Trading with bots: +================== + +To trade items/money with your bot simply initiate a trade and the bot will tell you how much money and items are available. To request an item simple whisper the bot and shift click the link of the item you would like. You can link multiple items on the same line. You can also request money in the following manner when the trade window is open: +/w BOTNAME 10g <-- request that the bot give you 10 gold +/w BOTNAME 6g500s25c <-- request 6 gold, 500 silver, and 25 cooper + +A bot is also able to show an item in its 'Will not be traded' slot. The item can be either +in its bags or be equipped and even be soulbound. By this you can cast spells/enchantments +on soulbound items ('nt' stands for 'not trading'). +/w BOTNAME nt [Powerful Soulbound Item] + +More Information: +================= + +If specifying a spell substring, the spell chosen will be in priority of exact name match, highest spell rank, and spell using no reagents. Case does not matter. Here's some examples: +/t BOTNAME c greater heal +/t BOTNAME cast pain +/w BOTNAME c poly +/w BOTNAME cast fort +/t BOTNAME cast +- OR - +/w BOTNAME c + +Also all commands can be broadcast to the party. For example: +/p follow +/p spells + +To use or equip items for your bot say: +/w BOTNAME use +/w BOTNAME equip +- OR - +/w BOTNAME u +/w BOTNAME e + +If you inspect your bot, your bot will tell you what items you have in your inventory that you can equip. To create a link in the chat window, hold the shift key and press the left mouse button when clicking the link. + + +Changes from Trinity to Mangos: +=============================== + +I added the following in SharedDefines.h. + +enum SpellCategory +{ + SPELL_CATEGORY_FOOD = 11, + SPELL_CATEGORY_DRINK = 59 +}; + +I also had to add the following to Player.h: + +enum PlayerStateType +{ + PLAYER_STATE_NONE = 0, + PLAYER_STATE_SIT = 1 +}; + + +Configuration variables: +======================== +Also see src/mangosd/mangosd.conf.dist for configuration variables! + + PlayerbotAI.DebugWhisper + Enable debug output by whispering master + Default: 0 - off + 1 - on + + PlayerbotAI.FollowDistanceMin + PlayerbotAI.FollowDistanceMax + Min. and max. follow distance for bots + Default: 0.5 / 1.0 + + +Some Problems: +============== + +The bots don't always face in the right direction. Sometimes when a bot makes the kill, the corpse is not lootable. The mage bot sometimes get stuck when he begins to cast a spell (but this is corrected the next time he enters combat). + +-- BotGuy +What it is: +=========== + +The new revised'botguy' utilizes NPCs already distributed throughout the world, to allow players to +summon and dismiss bots at will, from their own account. + +This is a revised (more stable) version that utilizes the new 'GOSSIP MENU SYSTEM' to modify the menus of existing NPCs +(e.g Trainers etc) to include the bot Recruit/Dismiss menu. (No GameMaster account necessary). + +Install (Server administrators only) +======= +Please apply 'mangos_botguy.sql' once to the world database to update the 'gossip_menu_option' table. + +-- ToDo +[DONE] ///---Quest---/// +[DONE] Bot can accept quest. +[DONE] Bot can join quest. + +[DONE] ///---Loot---/// +[DONE] Bot can loot. +[DONE] Bot can loot --- maybe only needed q item. + +//---Instance teleport Problems---/// +[DONE] Needs some fix. + +///---Combat and Movement Orders---/// +[DONE] Movement orders (stay, follow) +[DONE] Combat orders (protect, assist) +Combat order TANK +Combat order HEAL + +///---Temporary item enchantments---/// +[DONE] Rogue : Poison +Warrior : Sharpening Stone, Rune of Warding, Rune of Shielding. + +//---After fear bot's lose target---/// +Needs some fix. + +///---Move behind target---/// +Rogue : some abilitys require Rogue to be stealthed and behind target. + +///---Implement locale independet way of getting spellIDs---/// +[DONE] Hardcode lowest rank spellID, use function to get highest rank diff --git a/addition/707_mangos_spell_hacks.sql b/addition/707_mangos_spell_hacks.sql index 67367651c..c5cad5b5b 100644 --- a/addition/707_mangos_spell_hacks.sql +++ b/addition/707_mangos_spell_hacks.sql @@ -8,3 +8,8 @@ DELETE FROM spell_proc_event WHERE entry IN (57529, 57531); INSERT INTO spell_proc_event (entry, SchoolMask, SpellFamilyName, SpellFamilyMaskA0, SpellFamilyMaskA1, SpellFamilyMaskA2, SpellFamilyMaskB0, SpellFamilyMaskB1, SpellFamilyMaskB2, SpellFamilyMaskC0, SpellFamilyMaskC1, SpellFamilyMaskC2, procFlags, procEx, ppmRate, CustomChance, Cooldown) VALUES (57529, 0x00, 3, 0x61400035 | 0x200000 | 0x20000 | 0x0000040 | 0x000080 | 0x00000002 | 0x00000200 | 0x00001000 | 0x00800000 , 0,0 , 0x00001000 | 0x00008000 | 0x00000002 | 0x00001000 | 0x00000040, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0, 0, 0); -- dbc | arc. mis.| frost no| ice lance | blizzard | fire blast | cone of co | arc. expl. | dragon's br., , dbc | arc. barr | fire Blast | frotfireb. | blast wave + +-- from FallenangelX +DELETE FROM `spell_proc_event` WHERE `entry` IN (51682); +INSERT INTO `spell_proc_event` VALUES +(51682, 0x00, 8, 0x10014000, 0x10014000, 0x10014000, 0x00080000, 0x00080000, 0x00080000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), diff --git a/addition/732_mangos_vehicle_accessory.sql b/addition/732_mangos_vehicle_accessory.sql index f3f19af2c..393fe8e05 100644 --- a/addition/732_mangos_vehicle_accessory.sql +++ b/addition/732_mangos_vehicle_accessory.sql @@ -29,6 +29,7 @@ INSERT INTO `vehicle_accessory`(`entry`,`accessory_entry`,`seat_id`,`minion`,`de (33114,33142,1,1,'Overload Control Device'), (33114,33143,2,1,'Leviathan Defense Turret'), (36678,38309,0,1,'Professor Putricide - trigger'), +(36678,38308,1,1,'Professor Putricide - trigger'), (33214,33218,1,1,'Mechanolift 304-A'), (35637,34705,0,0,'Marshal Jacob Alerius\' Mount'), (35633,34702,0,0,'Ambrose Boltspark\'s Mount'), @@ -50,7 +51,6 @@ INSERT INTO `vehicle_accessory`(`entry`,`accessory_entry`,`seat_id`,`minion`,`de (29625,29694,0,0,'Hyldsmeet Proto-Drake'), (30330,30332,0,0,'Jotunheim Proto-Drake'), (32189,32190,0,0,'Skybreaker Recon Fighter'), -(36678,38308,1,1,'Professor Putricide - trigger'), (32640,32642,1,0,'Traveler Mammoth (H) - Vendor'), (32640,32641,2,0,'Traveler Mammoth (H) - Vendor & Repairer'), (32633,32638,1,0,'Traveler Mammoth (A) - Vendor'), @@ -59,4 +59,8 @@ INSERT INTO `vehicle_accessory`(`entry`,`accessory_entry`,`seat_id`,`minion`,`de (29555,29556,0,0,'Goblin Sapper'), (28018,28006,0,1,'Thiassi the Light Bringer'), (28054,28053,0,0,'Lucky Wilhelm - Apple'), -(28614,28616,0,1,'Scarlet Gryphon Rider'); +(28614,28616,0,1,'Scarlet Gryphon Rider'), +(36476,36477,0,0,'Krick and Ick'), +(36661,36658,0,0,'Scourgelord Tyrannus and Rimefang'); + +UPDATE `creature_template_addon` SET `auras` = '' WHERE `entry` IN (32638,32642); diff --git a/addition/732_mangos_vehicle_tables.sql b/addition/732_mangos_vehicle_tables.sql index 5bdc731ad..c6edfe63e 100644 --- a/addition/732_mangos_vehicle_tables.sql +++ b/addition/732_mangos_vehicle_tables.sql @@ -7,6 +7,5 @@ ALTER TABLE creature_template ADD COLUMN `spell6` mediumint(8) unsigned NOT NULL default '0' AFTER `spell5`, ADD COLUMN `spell7` mediumint(8) unsigned NOT NULL default '0' AFTER `spell6`, ADD COLUMN `spell8` mediumint(8) unsigned NOT NULL default '0' AFTER `spell7`, - ADD COLUMN `VehicleId` mediumint(8) unsigned NOT NULL default '0' AFTER `PetSpellDataId`, ADD COLUMN `PowerType` tinyint(3) unsigned NOT NULL default '0' AFTER `MaxHealth`; diff --git a/addition/732_quests_vehicle_data_mangos.sql b/addition/732_quests_vehicle_data_mangos.sql index 5274b40cc..28554231d 100644 --- a/addition/732_quests_vehicle_data_mangos.sql +++ b/addition/732_quests_vehicle_data_mangos.sql @@ -11,7 +11,7 @@ UPDATE creature_template SET spell4 = 0, spell5 = 0, spell6 = 0, - VehicleId = 123 + vehicle_id = 123 WHERE entry IN (28605, 28606, 28607); DELETE FROM npc_spellclick_spells WHERE npc_entry IN (28605, 28606, 28607); @@ -30,7 +30,7 @@ spell3 = 0, spell4 = 0, spell5 = 52588, spell6 = 0, -VehicleId = 79 +vehicle_id = 79 WHERE entry IN (28833); UPDATE creature_template SET @@ -40,7 +40,7 @@ spell3 = 0, spell4 = 0, spell5 = 52588, spell6 = 0, -VehicleId = 68 +vehicle_id = 68 WHERE entry IN (28887); INSERT INTO npc_spellclick_spells VALUES ('28833', '52447', '12701', '1', '12701', '1'); @@ -60,7 +60,7 @@ UPDATE `creature_template` SET spell4 = 0, spell5 = 0, spell6 = 0, - VehicleId = 213 + vehicle_id = 213 WHERE entry IN (30066); DELETE FROM `npc_spellclick_spells` WHERE `npc_entry` IN (30066); @@ -78,7 +78,7 @@ UPDATE creature_template SET spell4 = 0, spell5 = 0, spell6 = 0, - VehicleId = 68 + vehicle_id = 68 WHERE entry IN (27714); DELETE FROM npc_spellclick_spells WHERE npc_entry IN (27714); @@ -94,7 +94,7 @@ UPDATE creature_template SET spell4 = 0, spell5 = 48610, spell6 = 0, - VehicleId = 49 + vehicle_id = 49 WHERE entry IN (27354); DELETE FROM npc_spellclick_spells WHERE npc_entry IN (27354); @@ -111,7 +111,7 @@ UPDATE creature_template SET spell4 = 0, spell5 = 0, spell6 = 0, - VehicleId = 36 + vehicle_id = 36 WHERE entry IN (26523); DELETE FROM npc_spellclick_spells WHERE npc_entry IN (26523); @@ -126,7 +126,7 @@ UPDATE creature_template SET spell4 = 62552, spell5 = 64077, spell6 = 62863, - VehicleId = 349 + vehicle_id = 349 WHERE entry IN (33844, 33845); UPDATE creature_template SET KillCredit1 = 33340 WHERE entry IN (33272); UPDATE creature_template SET KillCredit1 = 33339 WHERE entry IN (33243); @@ -151,7 +151,7 @@ UPDATE `creature_template` SET spell4 = 0, spell5 = 0, spell6 = 0, - VehicleId = 308 + vehicle_id = 308 WHERE entry IN (29598); DELETE FROM `npc_spellclick_spells` WHERE `npc_entry` IN (29598); @@ -168,7 +168,7 @@ UPDATE creature_template SET spell4 = 0, spell5 = 0, spell6 = 0, - VehicleId = 146 + vehicle_id = 146 WHERE entry IN (28864); /* Frostbrood Vanquisher */ @@ -179,7 +179,7 @@ UPDATE creature_template SET spell4 = 0, spell5 = 0, spell6 = 0, - VehicleId = 156 + vehicle_id = 156 WHERE entry IN (28670); UPDATE creature_template SET maxhealth = 133525, minhealth = 133525, maxmana = 51360, minmana = 51360, InhabitType = 3 WHERE entry = 28670; @@ -238,7 +238,7 @@ spell3 = 0, spell4 = 0, spell5 = 0, spell6 = 0, -VehicleId = 29 +vehicle_id = 29 WHERE `entry` IN (25596); INSERT IGNORE INTO `spell_script_target` VALUES (45877, 1, 25596); @@ -251,7 +251,7 @@ spell3 = 50677, spell4 = 47849, spell5 = 47962, spell6 = 0, -VehicleId = 26 +vehicle_id = 26 WHERE `entry` IN (25334); DELETE FROM `npc_spellclick_spells` WHERE `npc_entry` IN (25334, 27107); @@ -272,7 +272,7 @@ spell3 = 47966, spell4 = 47938, spell5 = 0, spell6 = 0, -VehicleId = 300 +vehicle_id = 300 WHERE `entry` IN (27061); DELETE FROM `npc_spellclick_spells` WHERE npc_entry IN (27061); @@ -287,7 +287,7 @@ UPDATE `creature_template` SET spell4 = 0, spell5 = 0, spell6 = 0, - VehicleId = 244 + vehicle_id = 244 WHERE `entry` IN (30236); DELETE FROM `npc_spellclick_spells` WHERE npc_entry IN (30236); @@ -302,7 +302,7 @@ UPDATE `creature_template` SET spell4 = 0, spell5 = 0, spell6 = 0, - VehicleId = 99 + vehicle_id = 99 WHERE `entry` IN (27996); DELETE FROM `npc_spellclick_spells` WHERE npc_entry IN (27996); @@ -314,7 +314,7 @@ REPLACE INTO `creature_template_addon` (entry, auras) VALUES (27996, '53112'); -- from me -- Quest Reclamation (12546) UPDATE `creature_template` SET `spell1` = 50978,`spell2` = 50980,`spell3` = 50983,`spell4` = 50985, -`VehicleId` = 111 +`vehicle_id` = 111 WHERE `entry` = 28222; -- from YTDB/TC 578 @@ -330,7 +330,7 @@ INSERT INTO `npc_spellclick_spells` (`npc_entry`, `spell_id`, `quest_start`, `qu (32629, 60968, 0, 0, 0, 1); -- Quest 12996 -UPDATE `creature_template` SET `spell1` = 54459,`spell2` = 54458,`spell3` = 54460,`VehicleId` = 208 WHERE `creature_template`.`entry` = 29918; +UPDATE `creature_template` SET `spell1` = 54459,`spell2` = 54458,`spell3` = 54460,`vehicle_id` = 208 WHERE `creature_template`.`entry` = 29918; -- Quest 13236 Gift of the Lich King REPLACE INTO `spell_script_target` VALUES (58916,2,31254); diff --git a/addition/732_vehicles_mangos.sql b/addition/732_vehicles_mangos.sql index fa00baa45..9891abcfd 100644 --- a/addition/732_vehicles_mangos.sql +++ b/addition/732_vehicles_mangos.sql @@ -1,234 +1,234 @@ -- Known vehicles from zergtmn -#UPDATE `creature_template` SET `VehicleId` = 0; +#UPDATE `creature_template` SET `vehicle_id` = 0; -- -UPDATE `creature_template` SET `VehicleId` = 23 WHERE `entry` = 23693; -UPDATE `creature_template` SET `VehicleId` = 108 WHERE `entry` = 24083; -UPDATE `creature_template` SET `VehicleId` = 8 WHERE `entry` = 24418; -UPDATE `creature_template` SET `VehicleId` = 16 WHERE `entry` = 24705; -UPDATE `creature_template` SET `VehicleId` = 17 WHERE `entry` = 24750; -UPDATE `creature_template` SET `VehicleId` = 26 WHERE `entry` = 25334; -UPDATE `creature_template` SET `VehicleId` = 29 WHERE `entry` = 25596; -UPDATE `creature_template` SET `VehicleId` = 72 WHERE `entry` = 25743; -UPDATE `creature_template` SET `VehicleId` = 27 WHERE `entry` = 25762; -UPDATE `creature_template` SET `VehicleId` = 30 WHERE `entry` = 25968; -UPDATE `creature_template` SET `VehicleId` = 62 WHERE `entry` = 26472; -UPDATE `creature_template` SET `VehicleId` = 36 WHERE `entry` = 26523; -UPDATE `creature_template` SET `VehicleId` = 33 WHERE `entry` = 26525; -UPDATE `creature_template` SET `VehicleId` = 34 WHERE `entry` = 26572; -UPDATE `creature_template` SET `VehicleId` = 53 WHERE `entry` = 26590; -UPDATE `creature_template` SET `VehicleId` = 37 WHERE `entry` = 26777; -UPDATE `creature_template` SET `VehicleId` = 38 WHERE `entry` = 26778; -UPDATE `creature_template` SET `VehicleId` = 40 WHERE `entry` = 26893; -UPDATE `creature_template` SET `VehicleId` = 53 WHERE `entry` = 27131; -UPDATE `creature_template` SET `VehicleId` = 43 WHERE `entry` = 27213; -UPDATE `creature_template` SET `VehicleId` = 48 WHERE `entry` = 27241; -UPDATE `creature_template` SET `VehicleId` = 44 WHERE `entry` = 27258; -UPDATE `creature_template` SET `VehicleId` = 50 WHERE `entry` = 27261; -UPDATE `creature_template` SET `VehicleId` = 46 WHERE `entry` = 27270; -UPDATE `creature_template` SET `VehicleId` = 50 WHERE `entry` = 27292; -UPDATE `creature_template` SET `VehicleId` = 49 WHERE `entry` = 27354; -UPDATE `creature_template` SET `VehicleId` = 55 WHERE `entry` = 27496; -UPDATE `creature_template` SET `VehicleId` = 56 WHERE `entry` = 27587; -UPDATE `creature_template` SET `VehicleId` = 57 WHERE `entry` = 27593; -UPDATE `creature_template` SET `VehicleId` = 59 WHERE `entry` = 27626; -UPDATE `creature_template` SET `VehicleId` = 60 WHERE `entry` = 27629; -UPDATE `creature_template` SET `VehicleId` = 61 WHERE `entry` = 27661; -UPDATE `creature_template` SET `VehicleId` = 154 WHERE `entry` = 27671; -UPDATE `creature_template` SET `VehicleId` = 68 WHERE `entry` = 27714; -UPDATE `creature_template` SET `VehicleId` = 70 WHERE `entry` = 27755; -UPDATE `creature_template` SET `VehicleId` = 256 WHERE `entry` = 27761; -UPDATE `creature_template` SET `VehicleId` = 68 WHERE `entry` = 27839; -UPDATE `creature_template` SET `VehicleId` = 79 WHERE `entry` = 27881; -UPDATE `creature_template` SET `VehicleId` = 160 WHERE `entry` = 27894; -UPDATE `creature_template` SET `VehicleId` = 89 WHERE `entry` = 27924; -UPDATE `creature_template` SET `VehicleId` = 97 WHERE `entry` = 27992; -UPDATE `creature_template` SET `VehicleId` = 97 WHERE `entry` = 27993; -UPDATE `creature_template` SET `VehicleId` = 99 WHERE `entry` = 27996; -UPDATE `creature_template` SET `VehicleId` = 105 WHERE `entry` = 28009; -UPDATE `creature_template` SET `VehicleId` = 100 WHERE `entry` = 28018; -UPDATE `creature_template` SET `VehicleId` = 102 WHERE `entry` = 28054; -UPDATE `creature_template` SET `VehicleId` = 106 WHERE `entry` = 28094; -UPDATE `creature_template` SET `VehicleId` = 110 WHERE `entry` = 28192; -UPDATE `creature_template` SET `VehicleId` = 117 WHERE `entry` = 28312; -UPDATE `creature_template` SET `VehicleId` = 116 WHERE `entry` = 28319; -UPDATE `creature_template` SET `VehicleId` = 244 WHERE `entry` = 28366; -UPDATE `creature_template` SET `VehicleId` = 200 WHERE `entry` = 28605; -UPDATE `creature_template` SET `VehicleId` = 123 WHERE `entry` = 28606; -UPDATE `creature_template` SET `VehicleId` = 200 WHERE `entry` = 28607; -UPDATE `creature_template` SET `VehicleId` = 124 WHERE `entry` = 28614; -UPDATE `creature_template` SET `VehicleId` = 156 WHERE `entry` = 28670; -UPDATE `creature_template` SET `VehicleId` = 158 WHERE `entry` = 28781; -UPDATE `creature_template` SET `VehicleId` = 145 WHERE `entry` = 28851; -UPDATE `creature_template` SET `VehicleId` = 68 WHERE `entry` = 28887; -UPDATE `creature_template` SET `VehicleId` = 153 WHERE `entry` = 29043; -UPDATE `creature_template` SET `VehicleId` = 25 WHERE `entry` = 29144; -UPDATE `creature_template` SET `VehicleId` = 166 WHERE `entry` = 29414; -UPDATE `creature_template` SET `VehicleId` = 168 WHERE `entry` = 29433; -UPDATE `creature_template` SET `VehicleId` = 190 WHERE `entry` = 29679; -UPDATE `creature_template` SET `VehicleId` = 192 WHERE `entry` = 29691; -UPDATE `creature_template` SET `VehicleId` = 193 WHERE `entry` = 29698; -UPDATE `creature_template` SET `VehicleId` = 207 WHERE `entry` = 29753; -UPDATE `creature_template` SET `VehicleId` = 202 WHERE `entry` = 29857; -UPDATE `creature_template` SET `VehicleId` = 208 WHERE `entry` = 29918; -UPDATE `creature_template` SET `VehicleId` = 318 WHERE `entry` = 29929; -UPDATE `creature_template` SET `VehicleId` = 196 WHERE `entry` = 30013; -UPDATE `creature_template` SET `VehicleId` = 213 WHERE `entry` = 30066; -UPDATE `creature_template` SET `VehicleId` = 222 WHERE `entry` = 30174; -UPDATE `creature_template` SET `VehicleId` = 225 WHERE `entry` = 30204; -UPDATE `creature_template` SET `VehicleId` = 234 WHERE `entry` = 30228; -UPDATE `creature_template` SET `VehicleId` = 233 WHERE `entry` = 30275; -UPDATE `creature_template` SET `VehicleId` = 177 WHERE `entry` = 30320; -UPDATE `creature_template` SET `VehicleId` = 228 WHERE `entry` = 30330; -UPDATE `creature_template` SET `VehicleId` = 229 WHERE `entry` = 30337; -UPDATE `creature_template` SET `VehicleId` = 245 WHERE `entry` = 30342; -UPDATE `creature_template` SET `VehicleId` = 230 WHERE `entry` = 30343; -UPDATE `creature_template` SET `VehicleId` = 236 WHERE `entry` = 30403; -UPDATE `creature_template` SET `VehicleId` = 242 WHERE `entry` = 30470; -UPDATE `creature_template` SET `VehicleId` = 247 WHERE `entry` = 30564; -UPDATE `creature_template` SET `VehicleId` = 248 WHERE `entry` = 30585; -UPDATE `creature_template` SET `VehicleId` = 250 WHERE `entry` = 30645; -UPDATE `creature_template` SET `VehicleId` = 262 WHERE `entry` = 31125; -UPDATE `creature_template` SET `VehicleId` = 270 WHERE `entry` = 31137; -UPDATE `creature_template` SET `VehicleId` = 263 WHERE `entry` = 31139; -UPDATE `creature_template` SET `VehicleId` = 265 WHERE `entry` = 31224; -UPDATE `creature_template` SET `VehicleId` = 267 WHERE `entry` = 31262; -UPDATE `creature_template` SET `VehicleId` = 279 WHERE `entry` = 31583; -UPDATE `creature_template` SET `VehicleId` = 280 WHERE `entry` = 31641; -UPDATE `creature_template` SET `VehicleId` = 109 WHERE `entry` = 31689; -UPDATE `creature_template` SET `VehicleId` = 284 WHERE `entry` = 31702; -UPDATE `creature_template` SET `VehicleId` = 174 WHERE `entry` = 31722; -UPDATE `creature_template` SET `VehicleId` = 312 WHERE `entry` = 31857; -UPDATE `creature_template` SET `VehicleId` = 312 WHERE `entry` = 31858; -UPDATE `creature_template` SET `VehicleId` = 315 WHERE `entry` = 31861; -UPDATE `creature_template` SET `VehicleId` = 315 WHERE `entry` = 31862; -UPDATE `creature_template` SET `VehicleId` = 290 WHERE `entry` = 31881; -UPDATE `creature_template` SET `VehicleId` = 291 WHERE `entry` = 31884; -UPDATE `creature_template` SET `VehicleId` = 294 WHERE `entry` = 32189; -UPDATE `creature_template` SET `VehicleId` = 312 WHERE `entry` = 32212; -UPDATE `creature_template` SET `VehicleId` = 312 WHERE `entry` = 32213; -UPDATE `creature_template` SET `VehicleId` = 298 WHERE `entry` = 32225; -UPDATE `creature_template` SET `VehicleId` = 318 WHERE `entry` = 32286; -UPDATE `creature_template` SET `VehicleId` = 113 WHERE `entry` = 32323; -UPDATE `creature_template` SET `VehicleId` = 304 WHERE `entry` = 32490; -UPDATE `creature_template` SET `VehicleId` = 165 WHERE `entry` = 32535; -UPDATE `creature_template` SET `VehicleId` = 324 WHERE `entry` = 32627; -UPDATE `creature_template` SET `VehicleId` = 116 WHERE `entry` = 32629; -UPDATE `creature_template` SET `VehicleId` = 312 WHERE `entry` = 32633; -UPDATE `creature_template` SET `VehicleId` = 313 WHERE `entry` = 32640; -UPDATE `creature_template` SET `VehicleId` = 160 WHERE `entry` = 32795; -UPDATE `creature_template` SET `VehicleId` = 158 WHERE `entry` = 32796; -UPDATE `creature_template` SET `VehicleId` = 328 WHERE `entry` = 32930; -UPDATE `creature_template` SET `VehicleId` = 380 WHERE `entry` = 32934; -UPDATE `creature_template` SET `VehicleId` = 336 WHERE `entry` = 33060; -UPDATE `creature_template` SET `VehicleId` = 335 WHERE `entry` = 33062; -UPDATE `creature_template` SET `VehicleId` = 337 WHERE `entry` = 33067; -UPDATE `creature_template` SET `VehicleId` = 338 WHERE `entry` = 33109; -UPDATE `creature_template` SET `VehicleId` = 387 WHERE `entry` = 33113; -UPDATE `creature_template` SET `VehicleId` = 341 WHERE `entry` = 33114; -UPDATE `creature_template` SET `VehicleId` = 342 WHERE `entry` = 33118; -UPDATE `creature_template` SET `VehicleId` = 345 WHERE `entry` = 33167; -UPDATE `creature_template` SET `VehicleId` = 348 WHERE `entry` = 33214; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33217; -UPDATE `creature_template` SET `VehicleId` = 353 WHERE `entry` = 33293; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33319; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33321; -UPDATE `creature_template` SET `VehicleId` = 368 WHERE `entry` = 33513; -UPDATE `creature_template` SET `VehicleId` = 372 WHERE `entry` = 33669; -UPDATE `creature_template` SET `VehicleId` = 375 WHERE `entry` = 33687; -UPDATE `creature_template` SET `VehicleId` = 108 WHERE `entry` = 33778; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33844; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33845; -UPDATE `creature_template` SET `VehicleId` = 390 WHERE `entry` = 34120; -UPDATE `creature_template` SET `VehicleId` = 397 WHERE `entry` = 34161; -UPDATE `creature_template` SET `VehicleId` = 430 WHERE `entry` = 34658; -UPDATE `creature_template` SET `VehicleId` = 477 WHERE `entry` = 34703; -UPDATE `creature_template` SET `VehicleId` = 509 WHERE `entry` = 34775; -UPDATE `creature_template` SET `VehicleId` = 438 WHERE `entry` = 34793; -UPDATE `creature_template` SET `VehicleId` = 442 WHERE `entry` = 34796; -UPDATE `creature_template` SET `VehicleId` = 446 WHERE `entry` = 34826; -UPDATE `creature_template` SET `VehicleId` = 452 WHERE `entry` = 34929; -UPDATE `creature_template` SET `VehicleId` = 453 WHERE `entry` = 34935; -UPDATE `creature_template` SET `VehicleId` = 510 WHERE `entry` = 34944; -UPDATE `creature_template` SET `VehicleId` = 447 WHERE `entry` = 35273; -UPDATE `creature_template` SET `VehicleId` = 107 WHERE `entry` = 35373; -UPDATE `creature_template` SET `VehicleId` = 487 WHERE `entry` = 35474; -UPDATE `creature_template` SET `VehicleId` = 107 WHERE `entry` = 35491; -UPDATE `creature_template` SET `VehicleId` = 477 WHERE `entry` = 35572; -UPDATE `creature_template` SET `VehicleId` = 478 WHERE `entry` = 35633; -UPDATE `creature_template` SET `VehicleId` = 479 WHERE `entry` = 35634; -UPDATE `creature_template` SET `VehicleId` = 481 WHERE `entry` = 35636; -UPDATE `creature_template` SET `VehicleId` = 482 WHERE `entry` = 35637; -UPDATE `creature_template` SET `VehicleId` = 483 WHERE `entry` = 35638; -UPDATE `creature_template` SET `VehicleId` = 484 WHERE `entry` = 35640; -UPDATE `creature_template` SET `VehicleId` = 529 WHERE `entry` = 35644; -UPDATE `creature_template` SET `VehicleId` = 489 WHERE `entry` = 35768; -UPDATE `creature_template` SET `VehicleId` = 655 WHERE `entry` = 35819; -UPDATE `creature_template` SET `VehicleId` = 436 WHERE `entry` = 36356; -UPDATE `creature_template` SET `VehicleId` = 522 WHERE `entry` = 36476; -UPDATE `creature_template` SET `VehicleId` = 529 WHERE `entry` = 36559; -UPDATE `creature_template` SET `VehicleId` = 535 WHERE `entry` = 36661; -UPDATE `creature_template` SET `VehicleId` = 551 WHERE `entry` = 36794; -UPDATE `creature_template` SET `VehicleId` = 554 WHERE `entry` = 36838; -UPDATE `creature_template` SET `VehicleId` = 560 WHERE `entry` = 36891; -UPDATE `creature_template` SET `VehicleId` = 562 WHERE `entry` = 36896; -UPDATE `creature_template` SET `VehicleId` = 622 WHERE `entry` = 37120; -UPDATE `creature_template` SET `VehicleId` = 611 WHERE `entry` = 37968; -UPDATE `creature_template` SET `VehicleId` = 636 WHERE `entry` = 38500; +UPDATE `creature_template` SET `vehicle_id` = 23 WHERE `entry` = 23693; +UPDATE `creature_template` SET `vehicle_id` = 108 WHERE `entry` = 24083; +UPDATE `creature_template` SET `vehicle_id` = 8 WHERE `entry` = 24418; +UPDATE `creature_template` SET `vehicle_id` = 16 WHERE `entry` = 24705; +UPDATE `creature_template` SET `vehicle_id` = 17 WHERE `entry` = 24750; +UPDATE `creature_template` SET `vehicle_id` = 26 WHERE `entry` = 25334; +UPDATE `creature_template` SET `vehicle_id` = 29 WHERE `entry` = 25596; +UPDATE `creature_template` SET `vehicle_id` = 72 WHERE `entry` = 25743; +UPDATE `creature_template` SET `vehicle_id` = 27 WHERE `entry` = 25762; +UPDATE `creature_template` SET `vehicle_id` = 30 WHERE `entry` = 25968; +UPDATE `creature_template` SET `vehicle_id` = 62 WHERE `entry` = 26472; +UPDATE `creature_template` SET `vehicle_id` = 36 WHERE `entry` = 26523; +UPDATE `creature_template` SET `vehicle_id` = 33 WHERE `entry` = 26525; +UPDATE `creature_template` SET `vehicle_id` = 34 WHERE `entry` = 26572; +UPDATE `creature_template` SET `vehicle_id` = 53 WHERE `entry` = 26590; +UPDATE `creature_template` SET `vehicle_id` = 37 WHERE `entry` = 26777; +UPDATE `creature_template` SET `vehicle_id` = 38 WHERE `entry` = 26778; +UPDATE `creature_template` SET `vehicle_id` = 40 WHERE `entry` = 26893; +UPDATE `creature_template` SET `vehicle_id` = 53 WHERE `entry` = 27131; +UPDATE `creature_template` SET `vehicle_id` = 43 WHERE `entry` = 27213; +UPDATE `creature_template` SET `vehicle_id` = 48 WHERE `entry` = 27241; +UPDATE `creature_template` SET `vehicle_id` = 44 WHERE `entry` = 27258; +UPDATE `creature_template` SET `vehicle_id` = 50 WHERE `entry` = 27261; +UPDATE `creature_template` SET `vehicle_id` = 46 WHERE `entry` = 27270; +UPDATE `creature_template` SET `vehicle_id` = 50 WHERE `entry` = 27292; +UPDATE `creature_template` SET `vehicle_id` = 49 WHERE `entry` = 27354; +UPDATE `creature_template` SET `vehicle_id` = 55 WHERE `entry` = 27496; +UPDATE `creature_template` SET `vehicle_id` = 56 WHERE `entry` = 27587; +UPDATE `creature_template` SET `vehicle_id` = 57 WHERE `entry` = 27593; +UPDATE `creature_template` SET `vehicle_id` = 59 WHERE `entry` = 27626; +UPDATE `creature_template` SET `vehicle_id` = 60 WHERE `entry` = 27629; +UPDATE `creature_template` SET `vehicle_id` = 61 WHERE `entry` = 27661; +UPDATE `creature_template` SET `vehicle_id` = 154 WHERE `entry` = 27671; +UPDATE `creature_template` SET `vehicle_id` = 68 WHERE `entry` = 27714; +UPDATE `creature_template` SET `vehicle_id` = 70 WHERE `entry` = 27755; +UPDATE `creature_template` SET `vehicle_id` = 256 WHERE `entry` = 27761; +UPDATE `creature_template` SET `vehicle_id` = 68 WHERE `entry` = 27839; +UPDATE `creature_template` SET `vehicle_id` = 79 WHERE `entry` = 27881; +UPDATE `creature_template` SET `vehicle_id` = 160 WHERE `entry` = 27894; +UPDATE `creature_template` SET `vehicle_id` = 89 WHERE `entry` = 27924; +UPDATE `creature_template` SET `vehicle_id` = 97 WHERE `entry` = 27992; +UPDATE `creature_template` SET `vehicle_id` = 97 WHERE `entry` = 27993; +UPDATE `creature_template` SET `vehicle_id` = 99 WHERE `entry` = 27996; +UPDATE `creature_template` SET `vehicle_id` = 105 WHERE `entry` = 28009; +UPDATE `creature_template` SET `vehicle_id` = 100 WHERE `entry` = 28018; +UPDATE `creature_template` SET `vehicle_id` = 102 WHERE `entry` = 28054; +UPDATE `creature_template` SET `vehicle_id` = 106 WHERE `entry` = 28094; +UPDATE `creature_template` SET `vehicle_id` = 110 WHERE `entry` = 28192; +UPDATE `creature_template` SET `vehicle_id` = 117 WHERE `entry` = 28312; +UPDATE `creature_template` SET `vehicle_id` = 116 WHERE `entry` = 28319; +UPDATE `creature_template` SET `vehicle_id` = 244 WHERE `entry` = 28366; +UPDATE `creature_template` SET `vehicle_id` = 200 WHERE `entry` = 28605; +UPDATE `creature_template` SET `vehicle_id` = 123 WHERE `entry` = 28606; +UPDATE `creature_template` SET `vehicle_id` = 200 WHERE `entry` = 28607; +UPDATE `creature_template` SET `vehicle_id` = 124 WHERE `entry` = 28614; +UPDATE `creature_template` SET `vehicle_id` = 156 WHERE `entry` = 28670; +UPDATE `creature_template` SET `vehicle_id` = 158 WHERE `entry` = 28781; +UPDATE `creature_template` SET `vehicle_id` = 145 WHERE `entry` = 28851; +UPDATE `creature_template` SET `vehicle_id` = 68 WHERE `entry` = 28887; +UPDATE `creature_template` SET `vehicle_id` = 153 WHERE `entry` = 29043; +UPDATE `creature_template` SET `vehicle_id` = 25 WHERE `entry` = 29144; +UPDATE `creature_template` SET `vehicle_id` = 166 WHERE `entry` = 29414; +UPDATE `creature_template` SET `vehicle_id` = 168 WHERE `entry` = 29433; +UPDATE `creature_template` SET `vehicle_id` = 190 WHERE `entry` = 29679; +UPDATE `creature_template` SET `vehicle_id` = 192 WHERE `entry` = 29691; +UPDATE `creature_template` SET `vehicle_id` = 193 WHERE `entry` = 29698; +UPDATE `creature_template` SET `vehicle_id` = 207 WHERE `entry` = 29753; +UPDATE `creature_template` SET `vehicle_id` = 202 WHERE `entry` = 29857; +UPDATE `creature_template` SET `vehicle_id` = 208 WHERE `entry` = 29918; +UPDATE `creature_template` SET `vehicle_id` = 318 WHERE `entry` = 29929; +UPDATE `creature_template` SET `vehicle_id` = 196 WHERE `entry` = 30013; +UPDATE `creature_template` SET `vehicle_id` = 213 WHERE `entry` = 30066; +UPDATE `creature_template` SET `vehicle_id` = 222 WHERE `entry` = 30174; +UPDATE `creature_template` SET `vehicle_id` = 225 WHERE `entry` = 30204; +UPDATE `creature_template` SET `vehicle_id` = 234 WHERE `entry` = 30228; +UPDATE `creature_template` SET `vehicle_id` = 233 WHERE `entry` = 30275; +UPDATE `creature_template` SET `vehicle_id` = 177 WHERE `entry` = 30320; +UPDATE `creature_template` SET `vehicle_id` = 228 WHERE `entry` = 30330; +UPDATE `creature_template` SET `vehicle_id` = 229 WHERE `entry` = 30337; +UPDATE `creature_template` SET `vehicle_id` = 245 WHERE `entry` = 30342; +UPDATE `creature_template` SET `vehicle_id` = 230 WHERE `entry` = 30343; +UPDATE `creature_template` SET `vehicle_id` = 236 WHERE `entry` = 30403; +UPDATE `creature_template` SET `vehicle_id` = 242 WHERE `entry` = 30470; +UPDATE `creature_template` SET `vehicle_id` = 247 WHERE `entry` = 30564; +UPDATE `creature_template` SET `vehicle_id` = 248 WHERE `entry` = 30585; +UPDATE `creature_template` SET `vehicle_id` = 250 WHERE `entry` = 30645; +UPDATE `creature_template` SET `vehicle_id` = 262 WHERE `entry` = 31125; +UPDATE `creature_template` SET `vehicle_id` = 270 WHERE `entry` = 31137; +UPDATE `creature_template` SET `vehicle_id` = 263 WHERE `entry` = 31139; +UPDATE `creature_template` SET `vehicle_id` = 265 WHERE `entry` = 31224; +UPDATE `creature_template` SET `vehicle_id` = 267 WHERE `entry` = 31262; +UPDATE `creature_template` SET `vehicle_id` = 279 WHERE `entry` = 31583; +UPDATE `creature_template` SET `vehicle_id` = 280 WHERE `entry` = 31641; +UPDATE `creature_template` SET `vehicle_id` = 109 WHERE `entry` = 31689; +UPDATE `creature_template` SET `vehicle_id` = 284 WHERE `entry` = 31702; +UPDATE `creature_template` SET `vehicle_id` = 174 WHERE `entry` = 31722; +UPDATE `creature_template` SET `vehicle_id` = 312 WHERE `entry` = 31857; +UPDATE `creature_template` SET `vehicle_id` = 312 WHERE `entry` = 31858; +UPDATE `creature_template` SET `vehicle_id` = 315 WHERE `entry` = 31861; +UPDATE `creature_template` SET `vehicle_id` = 315 WHERE `entry` = 31862; +UPDATE `creature_template` SET `vehicle_id` = 290 WHERE `entry` = 31881; +UPDATE `creature_template` SET `vehicle_id` = 291 WHERE `entry` = 31884; +UPDATE `creature_template` SET `vehicle_id` = 294 WHERE `entry` = 32189; +UPDATE `creature_template` SET `vehicle_id` = 312 WHERE `entry` = 32212; +UPDATE `creature_template` SET `vehicle_id` = 312 WHERE `entry` = 32213; +UPDATE `creature_template` SET `vehicle_id` = 298 WHERE `entry` = 32225; +UPDATE `creature_template` SET `vehicle_id` = 318 WHERE `entry` = 32286; +UPDATE `creature_template` SET `vehicle_id` = 113 WHERE `entry` = 32323; +UPDATE `creature_template` SET `vehicle_id` = 304 WHERE `entry` = 32490; +UPDATE `creature_template` SET `vehicle_id` = 165 WHERE `entry` = 32535; +UPDATE `creature_template` SET `vehicle_id` = 324 WHERE `entry` = 32627; +UPDATE `creature_template` SET `vehicle_id` = 116 WHERE `entry` = 32629; +UPDATE `creature_template` SET `vehicle_id` = 312 WHERE `entry` = 32633; +UPDATE `creature_template` SET `vehicle_id` = 313 WHERE `entry` = 32640; +UPDATE `creature_template` SET `vehicle_id` = 160 WHERE `entry` = 32795; +UPDATE `creature_template` SET `vehicle_id` = 158 WHERE `entry` = 32796; +UPDATE `creature_template` SET `vehicle_id` = 328 WHERE `entry` = 32930; +UPDATE `creature_template` SET `vehicle_id` = 380 WHERE `entry` = 32934; +UPDATE `creature_template` SET `vehicle_id` = 336 WHERE `entry` = 33060; +UPDATE `creature_template` SET `vehicle_id` = 335 WHERE `entry` = 33062; +UPDATE `creature_template` SET `vehicle_id` = 337 WHERE `entry` = 33067; +UPDATE `creature_template` SET `vehicle_id` = 338 WHERE `entry` = 33109; +UPDATE `creature_template` SET `vehicle_id` = 387 WHERE `entry` = 33113; +UPDATE `creature_template` SET `vehicle_id` = 341 WHERE `entry` = 33114; +UPDATE `creature_template` SET `vehicle_id` = 342 WHERE `entry` = 33118; +UPDATE `creature_template` SET `vehicle_id` = 345 WHERE `entry` = 33167; +UPDATE `creature_template` SET `vehicle_id` = 348 WHERE `entry` = 33214; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33217; +UPDATE `creature_template` SET `vehicle_id` = 353 WHERE `entry` = 33293; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33319; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33321; +UPDATE `creature_template` SET `vehicle_id` = 368 WHERE `entry` = 33513; +UPDATE `creature_template` SET `vehicle_id` = 372 WHERE `entry` = 33669; +UPDATE `creature_template` SET `vehicle_id` = 375 WHERE `entry` = 33687; +UPDATE `creature_template` SET `vehicle_id` = 108 WHERE `entry` = 33778; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33844; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33845; +UPDATE `creature_template` SET `vehicle_id` = 390 WHERE `entry` = 34120; +UPDATE `creature_template` SET `vehicle_id` = 397 WHERE `entry` = 34161; +UPDATE `creature_template` SET `vehicle_id` = 430 WHERE `entry` = 34658; +UPDATE `creature_template` SET `vehicle_id` = 477 WHERE `entry` = 34703; +UPDATE `creature_template` SET `vehicle_id` = 509 WHERE `entry` = 34775; +UPDATE `creature_template` SET `vehicle_id` = 438 WHERE `entry` = 34793; +UPDATE `creature_template` SET `vehicle_id` = 442 WHERE `entry` = 34796; +UPDATE `creature_template` SET `vehicle_id` = 446 WHERE `entry` = 34826; +UPDATE `creature_template` SET `vehicle_id` = 452 WHERE `entry` = 34929; +UPDATE `creature_template` SET `vehicle_id` = 453 WHERE `entry` = 34935; +UPDATE `creature_template` SET `vehicle_id` = 510 WHERE `entry` = 34944; +UPDATE `creature_template` SET `vehicle_id` = 447 WHERE `entry` = 35273; +UPDATE `creature_template` SET `vehicle_id` = 107 WHERE `entry` = 35373; +UPDATE `creature_template` SET `vehicle_id` = 487 WHERE `entry` = 35474; +UPDATE `creature_template` SET `vehicle_id` = 107 WHERE `entry` = 35491; +UPDATE `creature_template` SET `vehicle_id` = 477 WHERE `entry` = 35572; +UPDATE `creature_template` SET `vehicle_id` = 478 WHERE `entry` = 35633; +UPDATE `creature_template` SET `vehicle_id` = 479 WHERE `entry` = 35634; +UPDATE `creature_template` SET `vehicle_id` = 481 WHERE `entry` = 35636; +UPDATE `creature_template` SET `vehicle_id` = 482 WHERE `entry` = 35637; +UPDATE `creature_template` SET `vehicle_id` = 483 WHERE `entry` = 35638; +UPDATE `creature_template` SET `vehicle_id` = 484 WHERE `entry` = 35640; +UPDATE `creature_template` SET `vehicle_id` = 529 WHERE `entry` = 35644; +UPDATE `creature_template` SET `vehicle_id` = 489 WHERE `entry` = 35768; +UPDATE `creature_template` SET `vehicle_id` = 655 WHERE `entry` = 35819; +UPDATE `creature_template` SET `vehicle_id` = 436 WHERE `entry` = 36356; +UPDATE `creature_template` SET `vehicle_id` = 522 WHERE `entry` = 36476; +UPDATE `creature_template` SET `vehicle_id` = 529 WHERE `entry` = 36559; +UPDATE `creature_template` SET `vehicle_id` = 535 WHERE `entry` = 36661; +UPDATE `creature_template` SET `vehicle_id` = 551 WHERE `entry` = 36794; +UPDATE `creature_template` SET `vehicle_id` = 554 WHERE `entry` = 36838; +UPDATE `creature_template` SET `vehicle_id` = 560 WHERE `entry` = 36891; +UPDATE `creature_template` SET `vehicle_id` = 562 WHERE `entry` = 36896; +UPDATE `creature_template` SET `vehicle_id` = 622 WHERE `entry` = 37120; +UPDATE `creature_template` SET `vehicle_id` = 611 WHERE `entry` = 37968; +UPDATE `creature_template` SET `vehicle_id` = 636 WHERE `entry` = 38500; -- Mechano-hog, Mekgineer's Chopper -UPDATE `creature_template` SET `VehicleId` = 318, IconName = 'vehichleCursor' WHERE `entry` IN (29929, 32286); +UPDATE `creature_template` SET `vehicle_id` = 318, IconName = 'vehichleCursor' WHERE `entry` IN (29929, 32286); -- Traveler's Tundra Mammoth -- Grand Ice Mammoth -- Grand Black War Mammoth -- Grand Caravan Mammoth -UPDATE `creature_template` SET `VehicleId` = 312, IconName = 'vehichleCursor' WHERE `entry` IN (32633, 32640, 31857, 31858, 31861, 31862, 32212, 32213); +UPDATE `creature_template` SET `vehicle_id` = 312, IconName = 'vehichleCursor' WHERE `entry` IN (32633, 32640, 31857, 31858, 31861, 31862, 32212, 32213); -- X-53 Touring Rocket -UPDATE `creature_template` SET `VehicleId` = 774, IconName = 'vehichleCursor' WHERE `entry` = 40725; +UPDATE `creature_template` SET `vehicle_id` = 774, IconName = 'vehichleCursor' WHERE `entry` = 40725; # Sniffed by zergtmn -UPDATE `creature_template` SET `VehicleId` = 328 WHERE `entry` = 32930; -UPDATE `creature_template` SET `VehicleId` = 380 WHERE `entry` = 32934; -UPDATE `creature_template` SET `VehicleId` = 336 WHERE `entry` = 33060; -UPDATE `creature_template` SET `VehicleId` = 335 WHERE `entry` = 33062; -UPDATE `creature_template` SET `VehicleId` = 337 WHERE `entry` = 33067; -UPDATE `creature_template` SET `VehicleId` = 347 WHERE `entry` = 33108; -UPDATE `creature_template` SET `VehicleId` = 338 WHERE `entry` = 33109; -UPDATE `creature_template` SET `VehicleId` = 387 WHERE `entry` = 33113; -UPDATE `creature_template` SET `VehicleId` = 341 WHERE `entry` = 33114; -UPDATE `creature_template` SET `VehicleId` = 342 WHERE `entry` = 33118; -UPDATE `creature_template` SET `VehicleId` = 345 WHERE `entry` = 33167; -UPDATE `creature_template` SET `VehicleId` = 348 WHERE `entry` = 33214; -UPDATE `creature_template` SET `VehicleId` = 381 WHERE `entry` = 33288; -UPDATE `creature_template` SET `VehicleId` = 353 WHERE `entry` = 33293; -UPDATE `creature_template` SET `VehicleId` = 356 WHERE `entry` = 33364; -UPDATE `creature_template` SET `VehicleId` = 357 WHERE `entry` = 33366; -UPDATE `creature_template` SET `VehicleId` = 358 WHERE `entry` = 33369; -UPDATE `creature_template` SET `VehicleId` = 371 WHERE `entry` = 33651; -UPDATE `creature_template` SET `VehicleId` = 372 WHERE `entry` = 33669; -UPDATE `creature_template` SET `VehicleId` = 108 WHERE `entry` = 33778; -UPDATE `creature_template` SET `VehicleId` = 385 WHERE `entry` = 33983; -UPDATE `creature_template` SET `VehicleId` = 390 WHERE `entry` = 34120; -UPDATE `creature_template` SET `VehicleId` = 392 WHERE `entry` = 34146; -UPDATE `creature_template` SET `VehicleId` = 395 WHERE `entry` = 34150; -UPDATE `creature_template` SET `VehicleId` = 396 WHERE `entry` = 34151; -UPDATE `creature_template` SET `VehicleId` = 397 WHERE `entry` = 34161; -UPDATE `creature_template` SET `VehicleId` = 399 WHERE `entry` = 34183; +UPDATE `creature_template` SET `vehicle_id` = 328 WHERE `entry` = 32930; +UPDATE `creature_template` SET `vehicle_id` = 380 WHERE `entry` = 32934; +UPDATE `creature_template` SET `vehicle_id` = 336 WHERE `entry` = 33060; +UPDATE `creature_template` SET `vehicle_id` = 335 WHERE `entry` = 33062; +UPDATE `creature_template` SET `vehicle_id` = 337 WHERE `entry` = 33067; +UPDATE `creature_template` SET `vehicle_id` = 347 WHERE `entry` = 33108; +UPDATE `creature_template` SET `vehicle_id` = 338 WHERE `entry` = 33109; +UPDATE `creature_template` SET `vehicle_id` = 387 WHERE `entry` = 33113; +UPDATE `creature_template` SET `vehicle_id` = 341 WHERE `entry` = 33114; +UPDATE `creature_template` SET `vehicle_id` = 342 WHERE `entry` = 33118; +UPDATE `creature_template` SET `vehicle_id` = 345 WHERE `entry` = 33167; +UPDATE `creature_template` SET `vehicle_id` = 348 WHERE `entry` = 33214; +UPDATE `creature_template` SET `vehicle_id` = 381 WHERE `entry` = 33288; +UPDATE `creature_template` SET `vehicle_id` = 353 WHERE `entry` = 33293; +UPDATE `creature_template` SET `vehicle_id` = 356 WHERE `entry` = 33364; +UPDATE `creature_template` SET `vehicle_id` = 357 WHERE `entry` = 33366; +UPDATE `creature_template` SET `vehicle_id` = 358 WHERE `entry` = 33369; +UPDATE `creature_template` SET `vehicle_id` = 371 WHERE `entry` = 33651; +UPDATE `creature_template` SET `vehicle_id` = 372 WHERE `entry` = 33669; +UPDATE `creature_template` SET `vehicle_id` = 108 WHERE `entry` = 33778; +UPDATE `creature_template` SET `vehicle_id` = 385 WHERE `entry` = 33983; +UPDATE `creature_template` SET `vehicle_id` = 390 WHERE `entry` = 34120; +UPDATE `creature_template` SET `vehicle_id` = 392 WHERE `entry` = 34146; +UPDATE `creature_template` SET `vehicle_id` = 395 WHERE `entry` = 34150; +UPDATE `creature_template` SET `vehicle_id` = 396 WHERE `entry` = 34151; +UPDATE `creature_template` SET `vehicle_id` = 397 WHERE `entry` = 34161; +UPDATE `creature_template` SET `vehicle_id` = 399 WHERE `entry` = 34183; -UPDATE `creature_template` SET `VehicleId` = 143 WHERE `entry` = 28864; -- Scourge Gryphon -UPDATE `creature_template` SET `VehicleId` = 123 WHERE `entry` = 28605; -- Havenshire Stallion -UPDATE `creature_template` SET `VehicleId` = 135 WHERE `entry` = 28782; -- Acherus Deathcharger -UPDATE `creature_template` SET `VehicleId` = 138 WHERE `entry` = 28817; -- Mine Car -UPDATE `creature_template` SET `VehicleId` = 139 WHERE `entry` = 28833; -- Scarlet Cannon +UPDATE `creature_template` SET `vehicle_id` = 143 WHERE `entry` = 28864; -- Scourge Gryphon +UPDATE `creature_template` SET `vehicle_id` = 123 WHERE `entry` = 28605; -- Havenshire Stallion +UPDATE `creature_template` SET `vehicle_id` = 135 WHERE `entry` = 28782; -- Acherus Deathcharger +UPDATE `creature_template` SET `vehicle_id` = 138 WHERE `entry` = 28817; -- Mine Car +UPDATE `creature_template` SET `vehicle_id` = 139 WHERE `entry` = 28833; -- Scarlet Cannon -UPDATE `creature_template` SET `VehicleId` = 370 WHERE `entry` = 33432; -- Leviathan Mk II -UPDATE `creature_template` SET `VehicleId` = 373 WHERE `entry` = 33670; -- Aerial Command Unit +UPDATE `creature_template` SET `vehicle_id` = 370 WHERE `entry` = 33432; -- Leviathan Mk II +UPDATE `creature_template` SET `vehicle_id` = 373 WHERE `entry` = 33670; -- Aerial Command Unit -UPDATE `creature_template` SET `VehicleId` = 736 WHERE `entry` = 40305; -- Spirit of the Tiger +UPDATE `creature_template` SET `vehicle_id` = 736 WHERE `entry` = 40305; -- Spirit of the Tiger # -UPDATE `creature_template` SET `VehicleId` = 220 WHERE `entry` = 30161; -UPDATE `creature_template` SET `VehicleId` = 224 WHERE `entry` = 30234; -UPDATE `creature_template` SET `VehicleId` = 223 WHERE `entry` = 30248; +UPDATE `creature_template` SET `vehicle_id` = 220 WHERE `entry` = 30161; +UPDATE `creature_template` SET `vehicle_id` = 224 WHERE `entry` = 30234; +UPDATE `creature_template` SET `vehicle_id` = 223 WHERE `entry` = 30248; -- fom Burned UPDATE creature_template SET IconName="vehichleCursor" WHERE entry IN @@ -241,15 +241,15 @@ UPDATE creature_template SET IconName="Gunner" WHERE entry IN -- From Timmit -- bone spike -UPDATE `creature_template` SET `VehicleId` = 647 WHERE `entry` IN (38711,38970,38971,38972); -UPDATE `creature_template` SET `VehicleId` = 533 WHERE `entry` IN (36619,38233,38459,38460); +UPDATE `creature_template` SET `vehicle_id` = 647 WHERE `entry` IN (38711,38970,38971,38972); +UPDATE `creature_template` SET `vehicle_id` = 533 WHERE `entry` IN (36619,38233,38459,38460); -- Putricide -UPDATE `creature_template` SET `VehicleId` = 587 WHERE `entry` IN (36678,38431,38585,38586); -UPDATE `creature_template` SET `VehicleId` = 591 WHERE `entry` IN (37672,38605,38786,38787); +UPDATE `creature_template` SET `vehicle_id` = 587 WHERE `entry` IN (36678,38431,38585,38586); +UPDATE `creature_template` SET `vehicle_id` = 591 WHERE `entry` IN (37672,38605,38786,38787); # full fix -UPDATE `creature_template` SET `IconName` = 'vehichleCursor' WHERE `VehicleId` > 0 AND `IconName` IS NULL; +UPDATE `creature_template` SET `IconName` = 'vehichleCursor' WHERE `vehicle_id` > 0 AND `IconName` IS NULL; # spellclicks -- from zergtmn @@ -288,190 +288,190 @@ UPDATE `creature_template` SET `unit_flags`=33587968 WHERE `entry`=33620; -- from Lordron -- Spectral tiger -UPDATE `creature_template` SET `VehicleId` = 354 WHERE `entry` = 33357; +UPDATE `creature_template` SET `vehicle_id` = 354 WHERE `entry` = 33357; -- Shalewing -UPDATE `creature_template` SET `VehicleId` = 146 WHERE `entry` = 28875; +UPDATE `creature_template` SET `vehicle_id` = 146 WHERE `entry` = 28875; -- Drakkari Skullcrusher -UPDATE `creature_template` SET `VehicleId` = 774 WHERE `entry` = 28844; +UPDATE `creature_template` SET `vehicle_id` = 774 WHERE `entry` = 28844; -- ICC -UPDATE `creature_template` SET `vehicleId` = 531 WHERE `entry` IN (36598); -UPDATE `creature_template` SET `vehicleId` = 532 WHERE `entry` IN (36609,39120,39121,39122); +UPDATE `creature_template` SET `vehicle_id` = 531 WHERE `entry` IN (36598); +UPDATE `creature_template` SET `vehicle_id` = 532 WHERE `entry` IN (36609,39120,39121,39122); -- from YTDB/TC 570 -UPDATE `creature_template` SET `VehicleId` = 51 WHERE `entry` = 27409; -UPDATE `creature_template` SET `VehicleId` = 107 WHERE `entry` = 28135; -UPDATE `creature_template` SET `VehicleId` = 206 WHERE `entry` = 28379; -UPDATE `creature_template` SET `VehicleId` = 121 WHERE `entry` = 28468; -UPDATE `creature_template` SET `VehicleId` = 492 WHERE `entry` = 25765; -UPDATE `creature_template` SET `VehicleId` = 25 WHERE `entry` = 27516; -UPDATE `creature_template` SET `VehicleId` = 156 WHERE `entry` = 26788; -UPDATE `creature_template` SET `VehicleId` = 129 WHERE `entry` = 28710; -UPDATE `creature_template` SET `VehicleId` = 25 WHERE `entry` = 28446; -UPDATE `creature_template` SET `VehicleId` = 22 WHERE `entry` = 24825; -UPDATE `creature_template` SET `VehicleId` = 22 WHERE `entry` = 24821; -UPDATE `creature_template` SET `VehicleId` = 22 WHERE `entry` = 24823; -UPDATE `creature_template` SET `VehicleId` = 22 WHERE `entry` = 24806; -UPDATE `creature_template` SET `VehicleId` = 200 WHERE `entry` = 26191; -UPDATE `creature_template` SET `VehicleId` = 113 WHERE `entry` = 28246; -UPDATE `creature_template` SET `VehicleId` = 156 WHERE `entry` = 27850; -UPDATE `creature_template` SET `VehicleId` = 156 WHERE `entry` = 27838; -UPDATE `creature_template` SET `VehicleId` = 30 WHERE `entry` = 25881; -UPDATE `creature_template` SET `VehicleId` = 156 WHERE `entry` = 26807; -UPDATE `creature_template` SET `VehicleId` = 34 WHERE `entry` = 26585; -UPDATE `creature_template` SET `VehicleId` = 39 WHERE `entry` = 26813; -UPDATE `creature_template` SET `VehicleId` = 127 WHERE `entry` = 28669; -UPDATE `creature_template` SET `VehicleId` = 203 WHERE `entry` = 29863; -UPDATE `creature_template` SET `VehicleId` = 200 WHERE `entry` = 27883; -UPDATE `creature_template` SET `VehicleId` = 111 WHERE `entry` = 28222; -UPDATE `creature_template` SET `VehicleId` = 115 WHERE `entry` = 28308; -UPDATE `creature_template` SET `VehicleId` = 191 WHERE `entry` = 29306; -UPDATE `creature_template` SET `VehicleId` = 176 WHERE `entry` = 29351; -UPDATE `creature_template` SET `VehicleId` = 177 WHERE `entry` = 29358; -UPDATE `creature_template` SET `VehicleId` = 165 WHERE `entry` = 29403; -UPDATE `creature_template` SET `VehicleId` = 169 WHERE `entry` = 29460; -UPDATE `creature_template` SET `VehicleId` = 173 WHERE `entry` = 29500; -UPDATE `creature_template` SET `VehicleId` = 175 WHERE `entry` = 29555; -UPDATE `creature_template` SET `VehicleId` = 179 WHERE `entry` = 29579; -UPDATE `creature_template` SET `VehicleId` = 181 WHERE `entry` = 29602; -UPDATE `creature_template` SET `VehicleId` = 183 WHERE `entry` = 29625; -UPDATE `creature_template` SET `VehicleId` = 186 WHERE `entry` = 29677; -UPDATE `creature_template` SET `VehicleId` = 198 WHERE `entry` = 29708; -UPDATE `creature_template` SET `VehicleId` = 194 WHERE `entry` = 29709; -UPDATE `creature_template` SET `VehicleId` = 243 WHERE `entry` = 29736; -UPDATE `creature_template` SET `VehicleId` = 197 WHERE `entry` = 29754; -UPDATE `creature_template` SET `VehicleId` = 201 WHERE `entry` = 29838; -UPDATE `creature_template` SET `VehicleId` = 205 WHERE `entry` = 29884; -UPDATE `creature_template` SET `VehicleId` = 209 WHERE `entry` = 29931; -UPDATE `creature_template` SET `VehicleId` = 214 WHERE `entry` = 30090; -UPDATE `creature_template` SET `VehicleId` = 217 WHERE `entry` = 30124; -UPDATE `creature_template` SET `VehicleId` = 219 WHERE `entry` = 30134; -UPDATE `creature_template` SET `VehicleId` = 221 WHERE `entry` = 30165; -UPDATE `creature_template` SET `VehicleId` = 227 WHERE `entry` = 30301; -UPDATE `creature_template` SET `VehicleId` = 25 WHERE `entry` = 30378; -UPDATE `creature_template` SET `VehicleId` = 316 WHERE `entry` = 30468; -UPDATE `creature_template` SET `VehicleId` = 252 WHERE `entry` = 30719; -UPDATE `creature_template` SET `VehicleId` = 259 WHERE `entry` = 31110; -UPDATE `creature_template` SET `VehicleId` = 265 WHERE `entry` = 31163; -UPDATE `creature_template` SET `VehicleId` = 265 WHERE `entry` = 31220; -UPDATE `creature_template` SET `VehicleId` = 265 WHERE `entry` = 31221; -UPDATE `creature_template` SET `VehicleId` = 269 WHERE `entry` = 31268; -UPDATE `creature_template` SET `VehicleId` = 268 WHERE `entry` = 31269; -UPDATE `creature_template` SET `VehicleId` = 273 WHERE `entry` = 31406; -UPDATE `creature_template` SET `VehicleId` = 277 WHERE `entry` = 31407; -UPDATE `creature_template` SET `VehicleId` = 274 WHERE `entry` = 31408; -UPDATE `creature_template` SET `VehicleId` = 278 WHERE `entry` = 31409; -UPDATE `creature_template` SET `VehicleId` = 282 WHERE `entry` = 31784; -UPDATE `creature_template` SET `VehicleId` = 282 WHERE `entry` = 31785; -UPDATE `creature_template` SET `VehicleId` = 736 WHERE `entry` = 31788; -UPDATE `creature_template` SET `VehicleId` = 512 WHERE `entry` = 31830; -UPDATE `creature_template` SET `VehicleId` = 287 WHERE `entry` = 31838; -UPDATE `creature_template` SET `VehicleId` = 291 WHERE `entry` = 32227; -UPDATE `creature_template` SET `VehicleId` = 300 WHERE `entry` = 32326; -UPDATE `creature_template` SET `VehicleId` = 301 WHERE `entry` = 32344; -UPDATE `creature_template` SET `VehicleId` = 302 WHERE `entry` = 32348; -UPDATE `creature_template` SET `VehicleId` = 273 WHERE `entry` = 32512; -UPDATE `creature_template` SET `VehicleId` = 369 WHERE `entry` = 32531; -UPDATE `creature_template` SET `VehicleId` = 40 WHERE `entry` = 30775; -UPDATE `creature_template` SET `VehicleId` = 201 WHERE `entry` = 30935; -UPDATE `creature_template` SET `VehicleId` = 209 WHERE `entry` = 30936; -UPDATE `creature_template` SET `VehicleId` = 191 WHERE `entry` = 31368; -UPDATE `creature_template` SET `VehicleId` = 108 WHERE `entry` = 31669; -UPDATE `creature_template` SET `VehicleId` = 380 WHERE `entry` = 32933; -UPDATE `creature_template` SET `VehicleId` = 342 WHERE `entry` = 33190; -UPDATE `creature_template` SET `VehicleId` = 0 WHERE `entry` = 33297; -UPDATE `creature_template` SET `VehicleId` = 0 WHERE `entry` = 33298; -UPDATE `creature_template` SET `VehicleId` = 0 WHERE `entry` = 33300; -UPDATE `creature_template` SET `VehicleId` = 0 WHERE `entry` = 33301; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33316; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33317; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33318; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33320; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33322; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33323; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33324; -UPDATE `creature_template` SET `VehicleId` = 0 WHERE `entry` = 33408; -UPDATE `creature_template` SET `VehicleId` = 0 WHERE `entry` = 33409; -UPDATE `creature_template` SET `VehicleId` = 0 WHERE `entry` = 33414; -UPDATE `creature_template` SET `VehicleId` = 0 WHERE `entry` = 33416; -UPDATE `creature_template` SET `VehicleId` = 0 WHERE `entry` = 33418; -UPDATE `creature_template` SET `VehicleId` = 368 WHERE `entry` = 33519; -UPDATE `creature_template` SET `VehicleId` = 369 WHERE `entry` = 33531; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33790; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33791; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33792; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33793; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33794; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33795; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33796; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33798; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33799; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33800; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33842; -UPDATE `creature_template` SET `VehicleId` = 349 WHERE `entry` = 33843; -UPDATE `creature_template` SET `VehicleId` = 353 WHERE `entry` = 33885; -UPDATE `creature_template` SET `VehicleId` = 328 WHERE `entry` = 33909; -UPDATE `creature_template` SET `VehicleId` = 380 WHERE `entry` = 33910; -UPDATE `creature_template` SET `VehicleId` = 380 WHERE `entry` = 33911; -UPDATE `creature_template` SET `VehicleId` = 381 WHERE `entry` = 33955; -UPDATE `creature_template` SET `VehicleId` = 385 WHERE `entry` = 33984; -UPDATE `creature_template` SET `VehicleId` = 387 WHERE `entry` = 34003; -UPDATE `creature_template` SET `VehicleId` = 335 WHERE `entry` = 34045; -UPDATE `creature_template` SET `VehicleId` = 370 WHERE `entry` = 34106; -UPDATE `creature_template` SET `VehicleId` = 371 WHERE `entry` = 34108; -UPDATE `creature_template` SET `VehicleId` = 373 WHERE `entry` = 34109; -UPDATE `creature_template` SET `VehicleId` = 0 WHERE `entry` = 34125; -UPDATE `creature_template` SET `VehicleId` = 397 WHERE `entry` = 34162; -UPDATE `creature_template` SET `VehicleId` = 399 WHERE `entry` = 34214; -UPDATE `creature_template` SET `VehicleId` = 0 WHERE `entry` = 36557; -UPDATE `creature_template` SET `VehicleId` = 485 WHERE `entry` = 35641; -UPDATE `creature_template` SET `VehicleId` = 480 WHERE `entry` = 35635; -UPDATE `creature_template` SET `VehicleId` = 106 WHERE `entry` = 34802; -UPDATE `creature_template` SET `VehicleId` = 0 WHERE `entry` = 36558; -UPDATE `creature_template` SET `VehicleId` = 477 WHERE `entry` = 36087; -UPDATE `creature_template` SET `VehicleId` = 477 WHERE `entry` = 36089; -UPDATE `creature_template` SET `VehicleId` = 442 WHERE `entry` = 35438; -UPDATE `creature_template` SET `VehicleId` = 442 WHERE `entry` = 35439; -UPDATE `creature_template` SET `VehicleId` = 442 WHERE `entry` = 35440; -UPDATE `creature_template` SET `VehicleId` = 446 WHERE `entry` = 35270; -UPDATE `creature_template` SET `VehicleId` = 446 WHERE `entry` = 35271; -UPDATE `creature_template` SET `VehicleId` = 446 WHERE `entry` = 35272; -UPDATE `creature_template` SET `VehicleId` = 555 WHERE `entry` = 36839; -UPDATE `creature_template` SET `VehicleId` = 599 WHERE `entry` = 37187; -UPDATE `creature_template` SET `VehicleId` = 648 WHERE `entry` = 38712; -UPDATE `creature_template` SET `VehicleId` = 562 WHERE `entry` = 37636; -UPDATE `creature_template` SET `VehicleId` = 560 WHERE `entry` = 37626; -UPDATE `creature_template` SET `VehicleId` = 522 WHERE `entry` = 37627; -UPDATE `creature_template` SET `VehicleId` = 648 WHERE `entry` = 38974; -UPDATE `creature_template` SET `VehicleId` = 648 WHERE `entry` = 38973; -UPDATE `creature_template` SET `VehicleId` = 648 WHERE `entry` = 38975; -UPDATE `creature_template` SET `VehicleId` = 106 WHERE `entry` = 35419; -UPDATE `creature_template` SET `VehicleId` = 106 WHERE `entry` = 35421; -UPDATE `creature_template` SET `VehicleId` = 106 WHERE `entry` = 35415; -UPDATE `creature_template` SET `VehicleId` = 436 WHERE `entry` = 36358; -UPDATE `creature_template` SET `VehicleId` = 36 WHERE `entry` = 35413; -UPDATE `creature_template` SET `VehicleId` = 452 WHERE `entry` = 35410; -UPDATE `creature_template` SET `VehicleId` = 591 WHERE `entry` = 38285; -UPDATE `creature_template` SET `VehicleId` = 718 WHERE `entry` = 40081; -UPDATE `creature_template` SET `VehicleId` = 718 WHERE `entry` = 40470; -UPDATE `creature_template` SET `VehicleId` = 718 WHERE `entry` = 40471; -UPDATE `creature_template` SET `VehicleId` = 718 WHERE `entry` = 40472; -UPDATE `creature_template` SET `VehicleId` = 79 WHERE `entry` = 35427; -UPDATE `creature_template` SET `VehicleId` = 79 WHERE `entry` = 35429; -UPDATE `creature_template` SET `VehicleId` = 591 WHERE `entry` = 38788; -UPDATE `creature_template` SET `VehicleId` = 591 WHERE `entry` = 38789; -UPDATE `creature_template` SET `VehicleId` = 591 WHERE `entry` = 38790; -UPDATE `creature_template` SET `VehicleId` = 700 WHERE `entry` = 39682; -UPDATE `creature_template` SET `VehicleId` = 745 WHERE `entry` = 39713; -UPDATE `creature_template` SET `VehicleId` = 745 WHERE `entry` = 39714; -UPDATE `creature_template` SET `VehicleId` = 753 WHERE `entry` = 39759; -UPDATE `creature_template` SET `VehicleId` = 763 WHERE `entry` = 39819; -UPDATE `creature_template` SET `VehicleId` = 711 WHERE `entry` = 39860; -UPDATE `creature_template` SET `VehicleId` = 747 WHERE `entry` = 40479; +UPDATE `creature_template` SET `vehicle_id` = 51 WHERE `entry` = 27409; +UPDATE `creature_template` SET `vehicle_id` = 107 WHERE `entry` = 28135; +UPDATE `creature_template` SET `vehicle_id` = 206 WHERE `entry` = 28379; +UPDATE `creature_template` SET `vehicle_id` = 121 WHERE `entry` = 28468; +UPDATE `creature_template` SET `vehicle_id` = 492 WHERE `entry` = 25765; +UPDATE `creature_template` SET `vehicle_id` = 25 WHERE `entry` = 27516; +UPDATE `creature_template` SET `vehicle_id` = 156 WHERE `entry` = 26788; +UPDATE `creature_template` SET `vehicle_id` = 129 WHERE `entry` = 28710; +UPDATE `creature_template` SET `vehicle_id` = 25 WHERE `entry` = 28446; +UPDATE `creature_template` SET `vehicle_id` = 22 WHERE `entry` = 24825; +UPDATE `creature_template` SET `vehicle_id` = 22 WHERE `entry` = 24821; +UPDATE `creature_template` SET `vehicle_id` = 22 WHERE `entry` = 24823; +UPDATE `creature_template` SET `vehicle_id` = 22 WHERE `entry` = 24806; +UPDATE `creature_template` SET `vehicle_id` = 200 WHERE `entry` = 26191; +UPDATE `creature_template` SET `vehicle_id` = 113 WHERE `entry` = 28246; +UPDATE `creature_template` SET `vehicle_id` = 156 WHERE `entry` = 27850; +UPDATE `creature_template` SET `vehicle_id` = 156 WHERE `entry` = 27838; +UPDATE `creature_template` SET `vehicle_id` = 30 WHERE `entry` = 25881; +UPDATE `creature_template` SET `vehicle_id` = 156 WHERE `entry` = 26807; +UPDATE `creature_template` SET `vehicle_id` = 34 WHERE `entry` = 26585; +UPDATE `creature_template` SET `vehicle_id` = 39 WHERE `entry` = 26813; +UPDATE `creature_template` SET `vehicle_id` = 127 WHERE `entry` = 28669; +UPDATE `creature_template` SET `vehicle_id` = 203 WHERE `entry` = 29863; +UPDATE `creature_template` SET `vehicle_id` = 200 WHERE `entry` = 27883; +UPDATE `creature_template` SET `vehicle_id` = 111 WHERE `entry` = 28222; +UPDATE `creature_template` SET `vehicle_id` = 115 WHERE `entry` = 28308; +UPDATE `creature_template` SET `vehicle_id` = 191 WHERE `entry` = 29306; +UPDATE `creature_template` SET `vehicle_id` = 176 WHERE `entry` = 29351; +UPDATE `creature_template` SET `vehicle_id` = 177 WHERE `entry` = 29358; +UPDATE `creature_template` SET `vehicle_id` = 165 WHERE `entry` = 29403; +UPDATE `creature_template` SET `vehicle_id` = 169 WHERE `entry` = 29460; +UPDATE `creature_template` SET `vehicle_id` = 173 WHERE `entry` = 29500; +UPDATE `creature_template` SET `vehicle_id` = 175 WHERE `entry` = 29555; +UPDATE `creature_template` SET `vehicle_id` = 179 WHERE `entry` = 29579; +UPDATE `creature_template` SET `vehicle_id` = 181 WHERE `entry` = 29602; +UPDATE `creature_template` SET `vehicle_id` = 183 WHERE `entry` = 29625; +UPDATE `creature_template` SET `vehicle_id` = 186 WHERE `entry` = 29677; +UPDATE `creature_template` SET `vehicle_id` = 198 WHERE `entry` = 29708; +UPDATE `creature_template` SET `vehicle_id` = 194 WHERE `entry` = 29709; +UPDATE `creature_template` SET `vehicle_id` = 243 WHERE `entry` = 29736; +UPDATE `creature_template` SET `vehicle_id` = 197 WHERE `entry` = 29754; +UPDATE `creature_template` SET `vehicle_id` = 201 WHERE `entry` = 29838; +UPDATE `creature_template` SET `vehicle_id` = 205 WHERE `entry` = 29884; +UPDATE `creature_template` SET `vehicle_id` = 209 WHERE `entry` = 29931; +UPDATE `creature_template` SET `vehicle_id` = 214 WHERE `entry` = 30090; +UPDATE `creature_template` SET `vehicle_id` = 217 WHERE `entry` = 30124; +UPDATE `creature_template` SET `vehicle_id` = 219 WHERE `entry` = 30134; +UPDATE `creature_template` SET `vehicle_id` = 221 WHERE `entry` = 30165; +UPDATE `creature_template` SET `vehicle_id` = 227 WHERE `entry` = 30301; +UPDATE `creature_template` SET `vehicle_id` = 25 WHERE `entry` = 30378; +UPDATE `creature_template` SET `vehicle_id` = 316 WHERE `entry` = 30468; +UPDATE `creature_template` SET `vehicle_id` = 252 WHERE `entry` = 30719; +UPDATE `creature_template` SET `vehicle_id` = 259 WHERE `entry` = 31110; +UPDATE `creature_template` SET `vehicle_id` = 265 WHERE `entry` = 31163; +UPDATE `creature_template` SET `vehicle_id` = 265 WHERE `entry` = 31220; +UPDATE `creature_template` SET `vehicle_id` = 265 WHERE `entry` = 31221; +UPDATE `creature_template` SET `vehicle_id` = 269 WHERE `entry` = 31268; +UPDATE `creature_template` SET `vehicle_id` = 268 WHERE `entry` = 31269; +UPDATE `creature_template` SET `vehicle_id` = 273 WHERE `entry` = 31406; +UPDATE `creature_template` SET `vehicle_id` = 277 WHERE `entry` = 31407; +UPDATE `creature_template` SET `vehicle_id` = 274 WHERE `entry` = 31408; +UPDATE `creature_template` SET `vehicle_id` = 278 WHERE `entry` = 31409; +UPDATE `creature_template` SET `vehicle_id` = 282 WHERE `entry` = 31784; +UPDATE `creature_template` SET `vehicle_id` = 282 WHERE `entry` = 31785; +UPDATE `creature_template` SET `vehicle_id` = 736 WHERE `entry` = 31788; +UPDATE `creature_template` SET `vehicle_id` = 512 WHERE `entry` = 31830; +UPDATE `creature_template` SET `vehicle_id` = 287 WHERE `entry` = 31838; +UPDATE `creature_template` SET `vehicle_id` = 291 WHERE `entry` = 32227; +UPDATE `creature_template` SET `vehicle_id` = 300 WHERE `entry` = 32326; +UPDATE `creature_template` SET `vehicle_id` = 301 WHERE `entry` = 32344; +UPDATE `creature_template` SET `vehicle_id` = 302 WHERE `entry` = 32348; +UPDATE `creature_template` SET `vehicle_id` = 273 WHERE `entry` = 32512; +UPDATE `creature_template` SET `vehicle_id` = 369 WHERE `entry` = 32531; +UPDATE `creature_template` SET `vehicle_id` = 40 WHERE `entry` = 30775; +UPDATE `creature_template` SET `vehicle_id` = 201 WHERE `entry` = 30935; +UPDATE `creature_template` SET `vehicle_id` = 209 WHERE `entry` = 30936; +UPDATE `creature_template` SET `vehicle_id` = 191 WHERE `entry` = 31368; +UPDATE `creature_template` SET `vehicle_id` = 108 WHERE `entry` = 31669; +UPDATE `creature_template` SET `vehicle_id` = 380 WHERE `entry` = 32933; +UPDATE `creature_template` SET `vehicle_id` = 342 WHERE `entry` = 33190; +UPDATE `creature_template` SET `vehicle_id` = 0 WHERE `entry` = 33297; +UPDATE `creature_template` SET `vehicle_id` = 0 WHERE `entry` = 33298; +UPDATE `creature_template` SET `vehicle_id` = 0 WHERE `entry` = 33300; +UPDATE `creature_template` SET `vehicle_id` = 0 WHERE `entry` = 33301; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33316; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33317; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33318; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33320; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33322; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33323; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33324; +UPDATE `creature_template` SET `vehicle_id` = 0 WHERE `entry` = 33408; +UPDATE `creature_template` SET `vehicle_id` = 0 WHERE `entry` = 33409; +UPDATE `creature_template` SET `vehicle_id` = 0 WHERE `entry` = 33414; +UPDATE `creature_template` SET `vehicle_id` = 0 WHERE `entry` = 33416; +UPDATE `creature_template` SET `vehicle_id` = 0 WHERE `entry` = 33418; +UPDATE `creature_template` SET `vehicle_id` = 368 WHERE `entry` = 33519; +UPDATE `creature_template` SET `vehicle_id` = 369 WHERE `entry` = 33531; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33790; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33791; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33792; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33793; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33794; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33795; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33796; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33798; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33799; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33800; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33842; +UPDATE `creature_template` SET `vehicle_id` = 349 WHERE `entry` = 33843; +UPDATE `creature_template` SET `vehicle_id` = 353 WHERE `entry` = 33885; +UPDATE `creature_template` SET `vehicle_id` = 328 WHERE `entry` = 33909; +UPDATE `creature_template` SET `vehicle_id` = 380 WHERE `entry` = 33910; +UPDATE `creature_template` SET `vehicle_id` = 380 WHERE `entry` = 33911; +UPDATE `creature_template` SET `vehicle_id` = 381 WHERE `entry` = 33955; +UPDATE `creature_template` SET `vehicle_id` = 385 WHERE `entry` = 33984; +UPDATE `creature_template` SET `vehicle_id` = 387 WHERE `entry` = 34003; +UPDATE `creature_template` SET `vehicle_id` = 335 WHERE `entry` = 34045; +UPDATE `creature_template` SET `vehicle_id` = 370 WHERE `entry` = 34106; +UPDATE `creature_template` SET `vehicle_id` = 371 WHERE `entry` = 34108; +UPDATE `creature_template` SET `vehicle_id` = 373 WHERE `entry` = 34109; +UPDATE `creature_template` SET `vehicle_id` = 0 WHERE `entry` = 34125; +UPDATE `creature_template` SET `vehicle_id` = 397 WHERE `entry` = 34162; +UPDATE `creature_template` SET `vehicle_id` = 399 WHERE `entry` = 34214; +UPDATE `creature_template` SET `vehicle_id` = 0 WHERE `entry` = 36557; +UPDATE `creature_template` SET `vehicle_id` = 485 WHERE `entry` = 35641; +UPDATE `creature_template` SET `vehicle_id` = 480 WHERE `entry` = 35635; +UPDATE `creature_template` SET `vehicle_id` = 106 WHERE `entry` = 34802; +UPDATE `creature_template` SET `vehicle_id` = 0 WHERE `entry` = 36558; +UPDATE `creature_template` SET `vehicle_id` = 477 WHERE `entry` = 36087; +UPDATE `creature_template` SET `vehicle_id` = 477 WHERE `entry` = 36089; +UPDATE `creature_template` SET `vehicle_id` = 442 WHERE `entry` = 35438; +UPDATE `creature_template` SET `vehicle_id` = 442 WHERE `entry` = 35439; +UPDATE `creature_template` SET `vehicle_id` = 442 WHERE `entry` = 35440; +UPDATE `creature_template` SET `vehicle_id` = 446 WHERE `entry` = 35270; +UPDATE `creature_template` SET `vehicle_id` = 446 WHERE `entry` = 35271; +UPDATE `creature_template` SET `vehicle_id` = 446 WHERE `entry` = 35272; +UPDATE `creature_template` SET `vehicle_id` = 555 WHERE `entry` = 36839; +UPDATE `creature_template` SET `vehicle_id` = 599 WHERE `entry` = 37187; +UPDATE `creature_template` SET `vehicle_id` = 648 WHERE `entry` = 38712; +UPDATE `creature_template` SET `vehicle_id` = 562 WHERE `entry` = 37636; +UPDATE `creature_template` SET `vehicle_id` = 560 WHERE `entry` = 37626; +UPDATE `creature_template` SET `vehicle_id` = 522 WHERE `entry` = 37627; +UPDATE `creature_template` SET `vehicle_id` = 648 WHERE `entry` = 38974; +UPDATE `creature_template` SET `vehicle_id` = 648 WHERE `entry` = 38973; +UPDATE `creature_template` SET `vehicle_id` = 648 WHERE `entry` = 38975; +UPDATE `creature_template` SET `vehicle_id` = 106 WHERE `entry` = 35419; +UPDATE `creature_template` SET `vehicle_id` = 106 WHERE `entry` = 35421; +UPDATE `creature_template` SET `vehicle_id` = 106 WHERE `entry` = 35415; +UPDATE `creature_template` SET `vehicle_id` = 436 WHERE `entry` = 36358; +UPDATE `creature_template` SET `vehicle_id` = 36 WHERE `entry` = 35413; +UPDATE `creature_template` SET `vehicle_id` = 452 WHERE `entry` = 35410; +UPDATE `creature_template` SET `vehicle_id` = 591 WHERE `entry` = 38285; +UPDATE `creature_template` SET `vehicle_id` = 718 WHERE `entry` = 40081; +UPDATE `creature_template` SET `vehicle_id` = 718 WHERE `entry` = 40470; +UPDATE `creature_template` SET `vehicle_id` = 718 WHERE `entry` = 40471; +UPDATE `creature_template` SET `vehicle_id` = 718 WHERE `entry` = 40472; +UPDATE `creature_template` SET `vehicle_id` = 79 WHERE `entry` = 35427; +UPDATE `creature_template` SET `vehicle_id` = 79 WHERE `entry` = 35429; +UPDATE `creature_template` SET `vehicle_id` = 591 WHERE `entry` = 38788; +UPDATE `creature_template` SET `vehicle_id` = 591 WHERE `entry` = 38789; +UPDATE `creature_template` SET `vehicle_id` = 591 WHERE `entry` = 38790; +UPDATE `creature_template` SET `vehicle_id` = 700 WHERE `entry` = 39682; +UPDATE `creature_template` SET `vehicle_id` = 745 WHERE `entry` = 39713; +UPDATE `creature_template` SET `vehicle_id` = 745 WHERE `entry` = 39714; +UPDATE `creature_template` SET `vehicle_id` = 753 WHERE `entry` = 39759; +UPDATE `creature_template` SET `vehicle_id` = 763 WHERE `entry` = 39819; +UPDATE `creature_template` SET `vehicle_id` = 711 WHERE `entry` = 39860; +UPDATE `creature_template` SET `vehicle_id` = 747 WHERE `entry` = 40479; -- YTDB updates 571-578 -UPDATE `creature_template` SET `VehicleId` = 265 WHERE `entry` = 31225; -UPDATE `creature_template` SET `VehicleId` = 224 WHERE `entry` = 31748; -UPDATE `creature_template` SET `VehicleId` = 223 WHERE `entry` = 31749; -UPDATE `creature_template` SET `VehicleId` = 220 WHERE `entry` = 31752; +UPDATE `creature_template` SET `vehicle_id` = 265 WHERE `entry` = 31225; +UPDATE `creature_template` SET `vehicle_id` = 224 WHERE `entry` = 31748; +UPDATE `creature_template` SET `vehicle_id` = 223 WHERE `entry` = 31749; +UPDATE `creature_template` SET `vehicle_id` = 220 WHERE `entry` = 31752; -- Ymirjar Skycaller true fix (delete hack from YTDB) DELETE FROM `creature_template_addon` WHERE `entry` IN (31260, 37643); diff --git a/addition/744_mangos_botguy.sql b/addition/744_mangos_botguy.sql new file mode 100644 index 000000000..b269b76a7 --- /dev/null +++ b/addition/744_mangos_botguy.sql @@ -0,0 +1,5 @@ +-- +INSERT INTO `command` (`name`,`security`,`help`) VALUES +('bot',0,'Syntax:\r.bot add BOTNAME (add character to world)\r.bot remove BOTNAME (remove character from world)\r.bot co|combatorder BOTNAME COMBATORDER [TARGET]'); +-- +INSERT INTO `gossip_menu_option` VALUES('0','16','0','GOSSIP_OPTION_BOT','18','1','0','0','0','0','0',NULL,'0','0','0','0','0','0','0','0','0'); diff --git a/dep/ACE_wrappers/CMakeLists.txt b/dep/ACE_wrappers/CMakeLists.txt index 2c76626f8..e759933cb 100644 --- a/dep/ACE_wrappers/CMakeLists.txt +++ b/dep/ACE_wrappers/CMakeLists.txt @@ -51,8 +51,7 @@ if(WIN32 AND MSVC) ) ExternalProject_Add_Step(ACE_Project ACE_Build DEPENDEES ACE_Upgrade - COMMAND ${ACE_BUILD_TOOL} \\ace\\ace_vc8.sln /build Debug|${ACE_CONFIGURATION} - COMMAND ${ACE_BUILD_TOOL} \\ace\\ace_vc8.sln /build Release|${ACE_CONFIGURATION} + COMMAND ${ACE_BUILD_TOOL} \\ace\\ace_vc8.sln /project ACE /build ${CMAKE_BUILD_TYPE}|${ACE_CONFIGURATION} ALWAYS 0 ) elseif(UNIX) diff --git a/dep/ACE_wrappers/ace/ACE_vc8.vcproj b/dep/ACE_wrappers/ace/ACE_vc8.vcproj index 360230633..42264db11 100644 --- a/dep/ACE_wrappers/ace/ACE_vc8.vcproj +++ b/dep/ACE_wrappers/ace/ACE_vc8.vcproj @@ -1,6869 +1,6873 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dep/include/openssl/asn1.h b/dep/include/openssl/asn1.h index f7718b5a9..59540e4e7 100644 --- a/dep/include/openssl/asn1.h +++ b/dep/include/openssl/asn1.h @@ -1067,7 +1067,7 @@ ASN1_STRING *ASN1_pack_string(void *obj, i2d_of_void *i2d, ASN1_STRING *ASN1_item_pack(void *obj, const ASN1_ITEM *it, ASN1_OCTET_STRING **oct); void ASN1_STRING_set_default_mask(unsigned long mask); -int ASN1_STRING_set_default_mask_asc(char *p); +int ASN1_STRING_set_default_mask_asc(const char *p); unsigned long ASN1_STRING_get_default_mask(void); int ASN1_mbstring_copy(ASN1_STRING **out, const unsigned char *in, int len, int inform, unsigned long mask); diff --git a/dep/include/openssl/dtls1.h b/dep/include/openssl/dtls1.h index af363a984..2900d1d8a 100644 --- a/dep/include/openssl/dtls1.h +++ b/dep/include/openssl/dtls1.h @@ -167,6 +167,7 @@ typedef struct hm_fragment_st { struct hm_header_st msg_header; unsigned char *fragment; + unsigned char *reassembly; } hm_fragment; typedef struct dtls1_state_st diff --git a/dep/include/openssl/engine.h b/dep/include/openssl/engine.h index 7fbd95f63..943aeae21 100644 --- a/dep/include/openssl/engine.h +++ b/dep/include/openssl/engine.h @@ -677,6 +677,7 @@ typedef struct st_dynamic_fns { * can be fully instantiated with IMPLEMENT_DYNAMIC_CHECK_FN(). */ typedef unsigned long (*dynamic_v_check_fn)(unsigned long ossl_version); #define IMPLEMENT_DYNAMIC_CHECK_FN() \ + OPENSSL_EXPORT unsigned long v_check(unsigned long v); \ OPENSSL_EXPORT unsigned long v_check(unsigned long v) { \ if(v >= OSSL_DYNAMIC_OLDEST) return OSSL_DYNAMIC_VERSION; \ return 0; } @@ -699,6 +700,8 @@ typedef unsigned long (*dynamic_v_check_fn)(unsigned long ossl_version); typedef int (*dynamic_bind_engine)(ENGINE *e, const char *id, const dynamic_fns *fns); #define IMPLEMENT_DYNAMIC_BIND_FN(fn) \ + OPENSSL_EXPORT \ + int bind_engine(ENGINE *e, const char *id, const dynamic_fns *fns); \ OPENSSL_EXPORT \ int bind_engine(ENGINE *e, const char *id, const dynamic_fns *fns) { \ if(ENGINE_get_static_state() == fns->static_state) goto skip_cbs; \ diff --git a/dep/include/openssl/opensslconf.h b/dep/include/openssl/opensslconf.h index 937e6ca84..c348a63d8 100644 --- a/dep/include/openssl/opensslconf.h +++ b/dep/include/openssl/opensslconf.h @@ -74,8 +74,8 @@ #if !(defined(VMS) || defined(__VMS)) /* VMS uses logical names instead */ #if defined(HEADER_CRYPTLIB_H) && !defined(OPENSSLDIR) -#define ENGINESDIR "c:\\openssl/lib/engines" -#define OPENSSLDIR "c:\\openssl/ssl" +#define ENGINESDIR "c:\\openssl\\release/lib/engines" +#define OPENSSLDIR "c:\\openssl\\release/ssl" #endif #endif diff --git a/dep/include/openssl/opensslv.h b/dep/include/openssl/opensslv.h index cbe52648d..e7fca8345 100644 --- a/dep/include/openssl/opensslv.h +++ b/dep/include/openssl/opensslv.h @@ -25,11 +25,11 @@ * (Prior to 0.9.5a beta1, a different scheme was used: MMNNFFRBB for * major minor fix final patch/beta) */ -#define OPENSSL_VERSION_NUMBER 0x1000000fL +#define OPENSSL_VERSION_NUMBER 0x1000004fL #ifdef OPENSSL_FIPS -#define OPENSSL_VERSION_TEXT "OpenSSL 1.0.0-fips 29 Mar 2010" +#define OPENSSL_VERSION_TEXT "OpenSSL 1.0.0d-fips 8 Feb 2011" #else -#define OPENSSL_VERSION_TEXT "OpenSSL 1.0.0 29 Mar 2010" +#define OPENSSL_VERSION_TEXT "OpenSSL 1.0.0d 8 Feb 2011" #endif #define OPENSSL_VERSION_PTEXT " part of " OPENSSL_VERSION_TEXT diff --git a/dep/include/openssl/pem.h b/dep/include/openssl/pem.h index 22231c26d..8a6ababe3 100644 --- a/dep/include/openssl/pem.h +++ b/dep/include/openssl/pem.h @@ -548,10 +548,11 @@ EVP_PKEY *b2i_PrivateKey_bio(BIO *in); EVP_PKEY *b2i_PublicKey_bio(BIO *in); int i2b_PrivateKey_bio(BIO *out, EVP_PKEY *pk); int i2b_PublicKey_bio(BIO *out, EVP_PKEY *pk); - +#ifndef OPENSSL_NO_RC4 EVP_PKEY *b2i_PVK_bio(BIO *in, pem_password_cb *cb, void *u); int i2b_PVK_bio(BIO *out, EVP_PKEY *pk, int enclevel, pem_password_cb *cb, void *u); +#endif /* BEGIN ERROR CODES */ diff --git a/dep/include/openssl/safestack.h b/dep/include/openssl/safestack.h index d616b4aab..39914bdde 100644 --- a/dep/include/openssl/safestack.h +++ b/dep/include/openssl/safestack.h @@ -179,7 +179,8 @@ DECLARE_SPECIAL_STACK_OF(OPENSSL_BLOCK, void) sk_is_sorted(CHECKED_STACK_OF(type, st)) #define SKM_ASN1_SET_OF_d2i(type, st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ - (STACK_OF(type) *)d2i_ASN1_SET(CHECKED_STACK_OF(type, st), \ + (STACK_OF(type) *)d2i_ASN1_SET( \ + (STACK_OF(OPENSSL_BLOCK) **)CHECKED_PTR_OF(STACK_OF(type)*, st), \ pp, length, \ CHECKED_D2I_OF(type, d2i_func), \ CHECKED_SK_FREE_FUNC(type, free_func), \ @@ -2030,81 +2031,81 @@ DECLARE_SPECIAL_STACK_OF(OPENSSL_BLOCK, void) #define sk_void_sort(st) SKM_sk_sort(void, (st)) #define sk_void_is_sorted(st) SKM_sk_is_sorted(void, (st)) +#define sk_OPENSSL_STRING_new(cmp) ((STACK_OF(OPENSSL_STRING) *)sk_new(CHECKED_SK_CMP_FUNC(char, cmp))) +#define sk_OPENSSL_STRING_new_null() ((STACK_OF(OPENSSL_STRING) *)sk_new_null()) +#define sk_OPENSSL_STRING_push(st, val) sk_push(CHECKED_STACK_OF(OPENSSL_STRING, st), CHECKED_PTR_OF(char, val)) +#define sk_OPENSSL_STRING_find(st, val) sk_find(CHECKED_STACK_OF(OPENSSL_STRING, st), CHECKED_PTR_OF(char, val)) +#define sk_OPENSSL_STRING_value(st, i) ((OPENSSL_STRING)sk_value(CHECKED_STACK_OF(OPENSSL_STRING, st), i)) +#define sk_OPENSSL_STRING_num(st) SKM_sk_num(OPENSSL_STRING, st) +#define sk_OPENSSL_STRING_pop_free(st, free_func) sk_pop_free(CHECKED_STACK_OF(OPENSSL_STRING, st), CHECKED_SK_FREE_FUNC2(OPENSSL_STRING, free_func)) +#define sk_OPENSSL_STRING_insert(st, val, i) sk_insert(CHECKED_STACK_OF(OPENSSL_STRING, st), CHECKED_PTR_OF(char, val), i) +#define sk_OPENSSL_STRING_free(st) SKM_sk_free(OPENSSL_STRING, st) +#define sk_OPENSSL_STRING_set(st, i, val) sk_set(CHECKED_STACK_OF(OPENSSL_STRING, st), i, CHECKED_PTR_OF(char, val)) +#define sk_OPENSSL_STRING_zero(st) SKM_sk_zero(OPENSSL_STRING, (st)) +#define sk_OPENSSL_STRING_unshift(st, val) sk_unshift(CHECKED_STACK_OF(OPENSSL_STRING, st), CHECKED_PTR_OF(char, val)) +#define sk_OPENSSL_STRING_find_ex(st, val) sk_find_ex((_STACK *)CHECKED_CONST_PTR_OF(STACK_OF(OPENSSL_STRING), st), CHECKED_CONST_PTR_OF(char, val)) +#define sk_OPENSSL_STRING_delete(st, i) SKM_sk_delete(OPENSSL_STRING, (st), (i)) +#define sk_OPENSSL_STRING_delete_ptr(st, ptr) (OPENSSL_STRING *)sk_delete_ptr(CHECKED_STACK_OF(OPENSSL_STRING, st), CHECKED_PTR_OF(char, ptr)) +#define sk_OPENSSL_STRING_set_cmp_func(st, cmp) \ + ((int (*)(const char * const *,const char * const *)) \ + sk_set_cmp_func(CHECKED_STACK_OF(OPENSSL_STRING, st), CHECKED_SK_CMP_FUNC(char, cmp))) +#define sk_OPENSSL_STRING_dup(st) SKM_sk_dup(OPENSSL_STRING, st) +#define sk_OPENSSL_STRING_shift(st) SKM_sk_shift(OPENSSL_STRING, (st)) +#define sk_OPENSSL_STRING_pop(st) (char *)sk_pop(CHECKED_STACK_OF(OPENSSL_STRING, st)) +#define sk_OPENSSL_STRING_sort(st) SKM_sk_sort(OPENSSL_STRING, (st)) +#define sk_OPENSSL_STRING_is_sorted(st) SKM_sk_is_sorted(OPENSSL_STRING, (st)) + + #define sk_OPENSSL_BLOCK_new(cmp) ((STACK_OF(OPENSSL_BLOCK) *)sk_new(CHECKED_SK_CMP_FUNC(void, cmp))) #define sk_OPENSSL_BLOCK_new_null() ((STACK_OF(OPENSSL_BLOCK) *)sk_new_null()) -#define sk_OPENSSL_BLOCK_push(st, val) sk_push(CHECKED_PTR_OF(STACK_OF(OPENSSL_BLOCK), st), CHECKED_PTR_OF(void, val)) -#define sk_OPENSSL_BLOCK_find(st, val) sk_find(CHECKED_PTR_OF(STACK_OF(OPENSSL_BLOCK), st), CHECKED_PTR_OF(void, val)) -#define sk_OPENSSL_BLOCK_value(st, i) ((OPENSSL_BLOCK)sk_value(CHECKED_PTR_OF(STACK_OF(OPENSSL_BLOCK), st), i)) +#define sk_OPENSSL_BLOCK_push(st, val) sk_push(CHECKED_STACK_OF(OPENSSL_BLOCK, st), CHECKED_PTR_OF(void, val)) +#define sk_OPENSSL_BLOCK_find(st, val) sk_find(CHECKED_STACK_OF(OPENSSL_BLOCK, st), CHECKED_PTR_OF(void, val)) +#define sk_OPENSSL_BLOCK_value(st, i) ((OPENSSL_BLOCK)sk_value(CHECKED_STACK_OF(OPENSSL_BLOCK, st), i)) #define sk_OPENSSL_BLOCK_num(st) SKM_sk_num(OPENSSL_BLOCK, st) -#define sk_OPENSSL_BLOCK_pop_free(st, free_func) sk_pop_free(CHECKED_PTR_OF(STACK_OF(OPENSSL_BLOCK), st), CHECKED_SK_FREE_FUNC2(OPENSSL_BLOCK, free_func)) -#define sk_OPENSSL_BLOCK_insert(st, val, i) sk_insert(CHECKED_PTR_OF(STACK_OF(OPENSSL_BLOCK), st), CHECKED_PTR_OF(void, val), i) +#define sk_OPENSSL_BLOCK_pop_free(st, free_func) sk_pop_free(CHECKED_STACK_OF(OPENSSL_BLOCK, st), CHECKED_SK_FREE_FUNC2(OPENSSL_BLOCK, free_func)) +#define sk_OPENSSL_BLOCK_insert(st, val, i) sk_insert(CHECKED_STACK_OF(OPENSSL_BLOCK, st), CHECKED_PTR_OF(void, val), i) #define sk_OPENSSL_BLOCK_free(st) SKM_sk_free(OPENSSL_BLOCK, st) -#define sk_OPENSSL_BLOCK_set(st, i, val) sk_set((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_BLOCK), st), i, CHECKED_PTR_OF(void, val)) +#define sk_OPENSSL_BLOCK_set(st, i, val) sk_set(CHECKED_STACK_OF(OPENSSL_BLOCK, st), i, CHECKED_PTR_OF(void, val)) #define sk_OPENSSL_BLOCK_zero(st) SKM_sk_zero(OPENSSL_BLOCK, (st)) -#define sk_OPENSSL_BLOCK_unshift(st, val) sk_unshift((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_BLOCK), st), CHECKED_PTR_OF(void, val)) +#define sk_OPENSSL_BLOCK_unshift(st, val) sk_unshift(CHECKED_STACK_OF(OPENSSL_BLOCK, st), CHECKED_PTR_OF(void, val)) #define sk_OPENSSL_BLOCK_find_ex(st, val) sk_find_ex((_STACK *)CHECKED_CONST_PTR_OF(STACK_OF(OPENSSL_BLOCK), st), CHECKED_CONST_PTR_OF(void, val)) #define sk_OPENSSL_BLOCK_delete(st, i) SKM_sk_delete(OPENSSL_BLOCK, (st), (i)) -#define sk_OPENSSL_BLOCK_delete_ptr(st, ptr) (OPENSSL_BLOCK *)sk_delete_ptr((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_BLOCK), st), CHECKED_PTR_OF(void, ptr)) +#define sk_OPENSSL_BLOCK_delete_ptr(st, ptr) (OPENSSL_BLOCK *)sk_delete_ptr(CHECKED_STACK_OF(OPENSSL_BLOCK, st), CHECKED_PTR_OF(void, ptr)) #define sk_OPENSSL_BLOCK_set_cmp_func(st, cmp) \ ((int (*)(const void * const *,const void * const *)) \ - sk_set_cmp_func((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_BLOCK), st), CHECKED_SK_CMP_FUNC(void, cmp))) + sk_set_cmp_func(CHECKED_STACK_OF(OPENSSL_BLOCK, st), CHECKED_SK_CMP_FUNC(void, cmp))) #define sk_OPENSSL_BLOCK_dup(st) SKM_sk_dup(OPENSSL_BLOCK, st) #define sk_OPENSSL_BLOCK_shift(st) SKM_sk_shift(OPENSSL_BLOCK, (st)) -#define sk_OPENSSL_BLOCK_pop(st) (void *)sk_pop((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_BLOCK), st)) +#define sk_OPENSSL_BLOCK_pop(st) (void *)sk_pop(CHECKED_STACK_OF(OPENSSL_BLOCK, st)) #define sk_OPENSSL_BLOCK_sort(st) SKM_sk_sort(OPENSSL_BLOCK, (st)) #define sk_OPENSSL_BLOCK_is_sorted(st) SKM_sk_is_sorted(OPENSSL_BLOCK, (st)) #define sk_OPENSSL_PSTRING_new(cmp) ((STACK_OF(OPENSSL_PSTRING) *)sk_new(CHECKED_SK_CMP_FUNC(OPENSSL_STRING, cmp))) #define sk_OPENSSL_PSTRING_new_null() ((STACK_OF(OPENSSL_PSTRING) *)sk_new_null()) -#define sk_OPENSSL_PSTRING_push(st, val) sk_push(CHECKED_PTR_OF(STACK_OF(OPENSSL_PSTRING), st), CHECKED_PTR_OF(OPENSSL_STRING, val)) -#define sk_OPENSSL_PSTRING_find(st, val) sk_find(CHECKED_PTR_OF(STACK_OF(OPENSSL_PSTRING), st), CHECKED_PTR_OF(OPENSSL_STRING, val)) -#define sk_OPENSSL_PSTRING_value(st, i) ((OPENSSL_PSTRING)sk_value(CHECKED_PTR_OF(STACK_OF(OPENSSL_PSTRING), st), i)) +#define sk_OPENSSL_PSTRING_push(st, val) sk_push(CHECKED_STACK_OF(OPENSSL_PSTRING, st), CHECKED_PTR_OF(OPENSSL_STRING, val)) +#define sk_OPENSSL_PSTRING_find(st, val) sk_find(CHECKED_STACK_OF(OPENSSL_PSTRING, st), CHECKED_PTR_OF(OPENSSL_STRING, val)) +#define sk_OPENSSL_PSTRING_value(st, i) ((OPENSSL_PSTRING)sk_value(CHECKED_STACK_OF(OPENSSL_PSTRING, st), i)) #define sk_OPENSSL_PSTRING_num(st) SKM_sk_num(OPENSSL_PSTRING, st) -#define sk_OPENSSL_PSTRING_pop_free(st, free_func) sk_pop_free(CHECKED_PTR_OF(STACK_OF(OPENSSL_PSTRING), st), CHECKED_SK_FREE_FUNC2(OPENSSL_PSTRING, free_func)) -#define sk_OPENSSL_PSTRING_insert(st, val, i) sk_insert(CHECKED_PTR_OF(STACK_OF(OPENSSL_PSTRING), st), CHECKED_PTR_OF(OPENSSL_STRING, val), i) +#define sk_OPENSSL_PSTRING_pop_free(st, free_func) sk_pop_free(CHECKED_STACK_OF(OPENSSL_PSTRING, st), CHECKED_SK_FREE_FUNC2(OPENSSL_PSTRING, free_func)) +#define sk_OPENSSL_PSTRING_insert(st, val, i) sk_insert(CHECKED_STACK_OF(OPENSSL_PSTRING, st), CHECKED_PTR_OF(OPENSSL_STRING, val), i) #define sk_OPENSSL_PSTRING_free(st) SKM_sk_free(OPENSSL_PSTRING, st) -#define sk_OPENSSL_PSTRING_set(st, i, val) sk_set((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_PSTRING), st), i, CHECKED_PTR_OF(OPENSSL_STRING, val)) +#define sk_OPENSSL_PSTRING_set(st, i, val) sk_set(CHECKED_STACK_OF(OPENSSL_PSTRING, st), i, CHECKED_PTR_OF(OPENSSL_STRING, val)) #define sk_OPENSSL_PSTRING_zero(st) SKM_sk_zero(OPENSSL_PSTRING, (st)) -#define sk_OPENSSL_PSTRING_unshift(st, val) sk_unshift((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_PSTRING), st), CHECKED_PTR_OF(OPENSSL_STRING, val)) +#define sk_OPENSSL_PSTRING_unshift(st, val) sk_unshift(CHECKED_STACK_OF(OPENSSL_PSTRING, st), CHECKED_PTR_OF(OPENSSL_STRING, val)) #define sk_OPENSSL_PSTRING_find_ex(st, val) sk_find_ex((_STACK *)CHECKED_CONST_PTR_OF(STACK_OF(OPENSSL_PSTRING), st), CHECKED_CONST_PTR_OF(OPENSSL_STRING, val)) #define sk_OPENSSL_PSTRING_delete(st, i) SKM_sk_delete(OPENSSL_PSTRING, (st), (i)) -#define sk_OPENSSL_PSTRING_delete_ptr(st, ptr) (OPENSSL_PSTRING *)sk_delete_ptr((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_PSTRING), st), CHECKED_PTR_OF(OPENSSL_STRING, ptr)) +#define sk_OPENSSL_PSTRING_delete_ptr(st, ptr) (OPENSSL_PSTRING *)sk_delete_ptr(CHECKED_STACK_OF(OPENSSL_PSTRING, st), CHECKED_PTR_OF(OPENSSL_STRING, ptr)) #define sk_OPENSSL_PSTRING_set_cmp_func(st, cmp) \ ((int (*)(const OPENSSL_STRING * const *,const OPENSSL_STRING * const *)) \ - sk_set_cmp_func((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_PSTRING), st), CHECKED_SK_CMP_FUNC(OPENSSL_STRING, cmp))) + sk_set_cmp_func(CHECKED_STACK_OF(OPENSSL_PSTRING, st), CHECKED_SK_CMP_FUNC(OPENSSL_STRING, cmp))) #define sk_OPENSSL_PSTRING_dup(st) SKM_sk_dup(OPENSSL_PSTRING, st) #define sk_OPENSSL_PSTRING_shift(st) SKM_sk_shift(OPENSSL_PSTRING, (st)) -#define sk_OPENSSL_PSTRING_pop(st) (OPENSSL_STRING *)sk_pop((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_PSTRING), st)) +#define sk_OPENSSL_PSTRING_pop(st) (OPENSSL_STRING *)sk_pop(CHECKED_STACK_OF(OPENSSL_PSTRING, st)) #define sk_OPENSSL_PSTRING_sort(st) SKM_sk_sort(OPENSSL_PSTRING, (st)) #define sk_OPENSSL_PSTRING_is_sorted(st) SKM_sk_is_sorted(OPENSSL_PSTRING, (st)) -#define sk_OPENSSL_STRING_new(cmp) ((STACK_OF(OPENSSL_STRING) *)sk_new(CHECKED_SK_CMP_FUNC(char, cmp))) -#define sk_OPENSSL_STRING_new_null() ((STACK_OF(OPENSSL_STRING) *)sk_new_null()) -#define sk_OPENSSL_STRING_push(st, val) sk_push(CHECKED_PTR_OF(STACK_OF(OPENSSL_STRING), st), CHECKED_PTR_OF(char, val)) -#define sk_OPENSSL_STRING_find(st, val) sk_find(CHECKED_PTR_OF(STACK_OF(OPENSSL_STRING), st), CHECKED_PTR_OF(char, val)) -#define sk_OPENSSL_STRING_value(st, i) ((OPENSSL_STRING)sk_value(CHECKED_PTR_OF(STACK_OF(OPENSSL_STRING), st), i)) -#define sk_OPENSSL_STRING_num(st) SKM_sk_num(OPENSSL_STRING, st) -#define sk_OPENSSL_STRING_pop_free(st, free_func) sk_pop_free(CHECKED_PTR_OF(STACK_OF(OPENSSL_STRING), st), CHECKED_SK_FREE_FUNC2(OPENSSL_STRING, free_func)) -#define sk_OPENSSL_STRING_insert(st, val, i) sk_insert(CHECKED_PTR_OF(STACK_OF(OPENSSL_STRING), st), CHECKED_PTR_OF(char, val), i) -#define sk_OPENSSL_STRING_free(st) SKM_sk_free(OPENSSL_STRING, st) -#define sk_OPENSSL_STRING_set(st, i, val) sk_set((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_STRING), st), i, CHECKED_PTR_OF(char, val)) -#define sk_OPENSSL_STRING_zero(st) SKM_sk_zero(OPENSSL_STRING, (st)) -#define sk_OPENSSL_STRING_unshift(st, val) sk_unshift((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_STRING), st), CHECKED_PTR_OF(char, val)) -#define sk_OPENSSL_STRING_find_ex(st, val) sk_find_ex((_STACK *)CHECKED_CONST_PTR_OF(STACK_OF(OPENSSL_STRING), st), CHECKED_CONST_PTR_OF(char, val)) -#define sk_OPENSSL_STRING_delete(st, i) SKM_sk_delete(OPENSSL_STRING, (st), (i)) -#define sk_OPENSSL_STRING_delete_ptr(st, ptr) (OPENSSL_STRING *)sk_delete_ptr((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_STRING), st), CHECKED_PTR_OF(char, ptr)) -#define sk_OPENSSL_STRING_set_cmp_func(st, cmp) \ - ((int (*)(const char * const *,const char * const *)) \ - sk_set_cmp_func((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_STRING), st), CHECKED_SK_CMP_FUNC(char, cmp))) -#define sk_OPENSSL_STRING_dup(st) SKM_sk_dup(OPENSSL_STRING, st) -#define sk_OPENSSL_STRING_shift(st) SKM_sk_shift(OPENSSL_STRING, (st)) -#define sk_OPENSSL_STRING_pop(st) (char *)sk_pop((_STACK *)CHECKED_PTR_OF(STACK_OF(OPENSSL_STRING), st)) -#define sk_OPENSSL_STRING_sort(st) SKM_sk_sort(OPENSSL_STRING, (st)) -#define sk_OPENSSL_STRING_is_sorted(st) SKM_sk_is_sorted(OPENSSL_STRING, (st)) - - #define d2i_ASN1_SET_OF_ACCESS_DESCRIPTION(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ SKM_ASN1_SET_OF_d2i(ACCESS_DESCRIPTION, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) #define i2d_ASN1_SET_OF_ACCESS_DESCRIPTION(st, pp, i2d_func, ex_tag, ex_class, is_set) \ diff --git a/dep/include/openssl/symhacks.h b/dep/include/openssl/symhacks.h index 151b68314..3fd4a8169 100644 --- a/dep/include/openssl/symhacks.h +++ b/dep/include/openssl/symhacks.h @@ -399,6 +399,12 @@ #undef dtls1_retransmit_buffered_messages #define dtls1_retransmit_buffered_messages dtls1_retransmit_buffered_msgs +/* Hack some long UI names */ +#undef UI_method_get_prompt_constructor +#define UI_method_get_prompt_constructor UI_method_get_prompt_constructr +#undef UI_method_set_prompt_constructor +#define UI_method_set_prompt_constructor UI_method_set_prompt_constructr + #endif /* defined OPENSSL_SYS_VMS */ diff --git a/dep/include/openssl/x509.h b/dep/include/openssl/x509.h index 604f4fb27..e6f8a4039 100644 --- a/dep/include/openssl/x509.h +++ b/dep/include/openssl/x509.h @@ -258,6 +258,7 @@ typedef struct x509_cinf_st ASN1_BIT_STRING *issuerUID; /* [ 1 ] optional in v2 */ ASN1_BIT_STRING *subjectUID; /* [ 2 ] optional in v2 */ STACK_OF(X509_EXTENSION) *extensions; /* [ 3 ] optional in v3 */ + ASN1_ENCODING enc; } X509_CINF; /* This stuff is certificate "auxiliary info" diff --git a/dep/lib/win32_debug/libeay32.dll b/dep/lib/win32_debug/libeay32.dll index 67f6a862a..090039590 100644 Binary files a/dep/lib/win32_debug/libeay32.dll and b/dep/lib/win32_debug/libeay32.dll differ diff --git a/dep/lib/win32_debug/libeay32.lib b/dep/lib/win32_debug/libeay32.lib index b5cbeef02..2381a0bb7 100644 Binary files a/dep/lib/win32_debug/libeay32.lib and b/dep/lib/win32_debug/libeay32.lib differ diff --git a/dep/lib/win32_release/libeay32.dll b/dep/lib/win32_release/libeay32.dll index 64b5304ae..090039590 100644 Binary files a/dep/lib/win32_release/libeay32.dll and b/dep/lib/win32_release/libeay32.dll differ diff --git a/dep/lib/win32_release/libeay32.lib b/dep/lib/win32_release/libeay32.lib index 138edcde1..2381a0bb7 100644 Binary files a/dep/lib/win32_release/libeay32.lib and b/dep/lib/win32_release/libeay32.lib differ diff --git a/dep/lib/x64_Debug/libeay32.dll b/dep/lib/x64_Debug/libeay32.dll index d2aafa21f..324fbb2e9 100644 Binary files a/dep/lib/x64_Debug/libeay32.dll and b/dep/lib/x64_Debug/libeay32.dll differ diff --git a/dep/lib/x64_Debug/libeay32.lib b/dep/lib/x64_Debug/libeay32.lib index 56fef2009..59ea158a3 100644 Binary files a/dep/lib/x64_Debug/libeay32.lib and b/dep/lib/x64_Debug/libeay32.lib differ diff --git a/dep/lib/x64_release/libeay32.dll b/dep/lib/x64_release/libeay32.dll index 0dca16e2f..324fbb2e9 100644 Binary files a/dep/lib/x64_release/libeay32.dll and b/dep/lib/x64_release/libeay32.dll differ diff --git a/dep/lib/x64_release/libeay32.lib b/dep/lib/x64_release/libeay32.lib index 4cfd23457..59ea158a3 100644 Binary files a/dep/lib/x64_release/libeay32.lib and b/dep/lib/x64_release/libeay32.lib differ diff --git a/dep/tbb/CMakeLists.txt b/dep/tbb/CMakeLists.txt index 8b7c9fc22..33cfeb92a 100644 --- a/dep/tbb/CMakeLists.txt +++ b/dep/tbb/CMakeLists.txt @@ -48,8 +48,8 @@ if(WIN32 AND MSVC) ) ExternalProject_Add_Step(TBB_Project TBB_Build DEPENDEES TBB_Upgrade - COMMAND ${TBB_BUILD_TOOL} ${TBB_SOURCE}\\makefile.sln /build Debug|${TBB_CONFIGURATION} - COMMAND ${TBB_BUILD_TOOL} ${TBB_SOURCE}\\makefile.sln /build Release|${TBB_CONFIGURATION} + COMMAND ${TBB_BUILD_TOOL} ${TBB_SOURCE}\\makefile.sln /project tbb /build ${CMAKE_BUILD_TYPE}|${TBB_CONFIGURATION} + COMMAND ${TBB_BUILD_TOOL} ${TBB_SOURCE}\\makefile.sln /project tbbmalloc /build ${CMAKE_BUILD_TYPE}|${TBB_CONFIGURATION} ALWAYS 0 ) elseif(UNIX) diff --git a/sql/characters.sql b/sql/characters.sql index 4837a0b4e..d559b95da 100644 --- a/sql/characters.sql +++ b/sql/characters.sql @@ -21,7 +21,7 @@ DROP TABLE IF EXISTS `character_db_version`; CREATE TABLE `character_db_version` ( - `required_11299_02_characters_pet_aura` bit(1) default NULL + `required_11391_01_characters_auction` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB'; -- @@ -147,6 +147,7 @@ CREATE TABLE `auction` ( `itemowner` int(11) unsigned NOT NULL default '0', `buyoutprice` int(11) NOT NULL default '0', `time` bigint(40) NOT NULL default '0', + `moneyTime` bigint(40) NOT NULL default '0', `buyguid` int(11) unsigned NOT NULL default '0', `lastbid` int(11) NOT NULL default '0', `startbid` int(11) NOT NULL default '0', diff --git a/sql/mangos.sql b/sql/mangos.sql index fbf2cd359..2bb095f46 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -24,7 +24,7 @@ CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, `cache_id` int(10) default '0', - `required_11348_01_mangos_spell_bonus_data` bit(1) default NULL + `required_11385_01_mangos_creature_template` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -1260,6 +1260,7 @@ CREATE TABLE `creature_template` ( `questItem6` int(11) UNSIGNED DEFAULT '0' NOT NULL, `movementId` int(11) UNSIGNED DEFAULT '0' NOT NULL, `RegenHealth` tinyint(3) unsigned NOT NULL default '1', + `vehicle_id` mediumint(8) unsigned NOT NULL default '0', `equipment_id` mediumint(8) unsigned NOT NULL default '0', `trainer_id` mediumint(8) unsigned NOT NULL default '0', `vendor_id` mediumint(8) unsigned NOT NULL default '0', @@ -1276,7 +1277,7 @@ CREATE TABLE `creature_template` ( LOCK TABLES `creature_template` WRITE; /*!40000 ALTER TABLE `creature_template` DISABLE KEYS */; INSERT INTO `creature_template` VALUES -(1,0,0,0,0,0,10045,0,0,0,'Waypoint(Only GM can see it)','Visual',NULL,0,1,1,64,64,0,0,5,35,35,0,0.91,1.14286,1,0,2,3,0,10,1,2000,2200,8,4096,0,0,0,0,0,0,1,2,100,8,5242886,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,3,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,130,''); +(1,0,0,0,0,0,10045,0,0,0,'Waypoint(Only GM can see it)','Visual',NULL,0,1,1,64,64,0,0,5,35,35,0,0.91,1.14286,1,0,2,3,0,10,1,2000,2200,8,4096,0,0,0,0,0,0,1,2,100,8,5242886,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,3,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,130,''); /*!40000 ALTER TABLE `creature_template` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/11385_01_mangos_creature_template.sql b/sql/updates/11385_01_mangos_creature_template.sql new file mode 100644 index 000000000..47956acb2 --- /dev/null +++ b/sql/updates/11385_01_mangos_creature_template.sql @@ -0,0 +1,4 @@ +ALTER TABLE db_version CHANGE COLUMN required_11348_01_mangos_spell_bonus_data required_11385_01_mangos_creature_template bit; + +ALTER TABLE creature_template + ADD COLUMN `vehicle_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER `RegenHealth`; diff --git a/sql/updates/11391_01_characters_auction.sql b/sql/updates/11391_01_characters_auction.sql new file mode 100644 index 000000000..ac2280ce6 --- /dev/null +++ b/sql/updates/11391_01_characters_auction.sql @@ -0,0 +1,4 @@ +ALTER TABLE character_db_version CHANGE COLUMN required_11299_02_characters_pet_aura required_11391_01_characters_auction bit; + +ALTER TABLE `auction` + ADD COLUMN `moneyTime` BIGINT(40) DEFAULT '0' NOT NULL AFTER `time`; diff --git a/src/game/AuctionHouseBot/AuctionHouseBot.cpp b/src/game/AuctionHouseBot/AuctionHouseBot.cpp index 2e805bb55..e1442159b 100644 --- a/src/game/AuctionHouseBot/AuctionHouseBot.cpp +++ b/src/game/AuctionHouseBot/AuctionHouseBot.cpp @@ -269,7 +269,7 @@ uint32 AHB_Buyer::GetBuyableEntry(AHB_Buyer_Config& config) for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = auctionHouse->GetAuctionsBegin();itr != auctionHouse->GetAuctionsEnd();++itr) { AuctionEntry *Aentry = itr->second; - Item *item = sAuctionMgr.GetAItem(Aentry->item_guidlow); + Item *item = sAuctionMgr.GetAItem(Aentry->itemGuidLow); if (item) { ItemPrototype const *prototype = item->GetProto(); @@ -483,7 +483,7 @@ void AHB_Buyer::PlaceBidToEntry(AuctionHouseObject* auctionHouse, AuctionEntry* if ((auction->bidder!=0)&&(auction->bidder != sAHB_BaseConfig.GetAHBObjectGuid().GetRawValue())) { // If Entry is already bidded send mail and money back. - m_Session->SendAuctionOutbiddedMail(auction, bidPrice); + m_Session->SendAuctionOutbiddedMail(auction); } auction->bidder = sAHB_BaseConfig.GetAHBObjectGuid().GetRawValue(); @@ -498,7 +498,7 @@ void AHB_Buyer::BuyEntry(AuctionHouseObject* auctionHouse, AuctionEntry* auction if ((auction->bidder!=0)&&(auction->bidder != sAHB_BaseConfig.GetAHBObjectGuid().GetRawValue())) { // If Entry is already bidded send mail and money back. - m_Session->SendAuctionOutbiddedMail(auction, auction->buyout); + m_Session->SendAuctionOutbiddedMail(auction); } auction->bidder = sAHB_BaseConfig.GetAHBObjectGuid().GetRawValue(); auction->bid = auction->buyout; @@ -508,7 +508,7 @@ void AHB_Buyer::BuyEntry(AuctionHouseObject* auctionHouse, AuctionEntry* auction sAuctionMgr.SendAuctionWonMail(auction); // Remove item from auctionhouse - sAuctionMgr.RemoveAItem(auction->item_guidlow); + sAuctionMgr.RemoveAItem(auction->itemGuidLow); // Remove auction auctionHouse->RemoveAuction(auction->Id); // Remove from database @@ -1155,7 +1155,7 @@ uint32 AHB_Seller::SetStat(AHB_Seller_Config& config) for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = auctionHouse->GetAuctionsBegin();itr != auctionHouse->GetAuctionsEnd();++itr) { AuctionEntry *Aentry = itr->second; - Item *item = sAuctionMgr.GetAItem(Aentry->item_guidlow); + Item *item = sAuctionMgr.GetAItem(Aentry->itemGuidLow); if (item) { ItemPrototype const *prototype = item->GetProto(); @@ -1326,15 +1326,15 @@ void AHB_Seller::addNewAuctions(AHB_Seller_Config& config) // Add Auction now on the AH AuctionEntry* auctionEntry = new AuctionEntry; auctionEntry->Id = sObjectMgr.GenerateAuctionID(); - auctionEntry->item_guidlow = item->GetGUIDLow(); - auctionEntry->item_template = item->GetEntry(); + auctionEntry->itemGuidLow = item->GetGUIDLow(); + auctionEntry->itemTemplate = item->GetEntry(); auctionEntry->owner =((uint32) sAHB_BaseConfig.GetAHBObjectGuid().GetRawValue()); auctionEntry->startbid = bidPrice; auctionEntry->buyout = buyoutPrice; auctionEntry->bidder = 0; auctionEntry->bid = 0; auctionEntry->deposit = 0; - auctionEntry->expire_time = (time_t) (urand(config.GetMinTime(), config.GetMaxTime()) * 60 * 60 + time(NULL)); + auctionEntry->expireTime = (time_t) (urand(config.GetMinTime(), config.GetMaxTime()) * 60 * 60 + time(NULL)); auctionEntry->auctionHouseEntry = ahEntry; //item->SaveToDB(); sAuctionMgr.AddAItem(item); @@ -1482,7 +1482,7 @@ void AuctionHouseBot::PrepStatusInfos() for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = auctionHouse->GetAuctionsBegin();itr != auctionHouse->GetAuctionsEnd();++itr) { AuctionEntry *Aentry = itr->second; - Item *item = sAuctionMgr.GetAItem(Aentry->item_guidlow); + Item *item = sAuctionMgr.GetAItem(Aentry->itemGuidLow); if (item) { ItemPrototype const *prototype = item->GetProto(); @@ -1526,8 +1526,8 @@ void AuctionHouseBot::Rebuild(bool all) { if (itr->second->owner == sAHB_BaseConfig.GetAHBObjectGuid().GetRawValue()) { - if (all==true) itr->second->expire_time = sWorld.GetGameTime(); - else if (itr->second->bid == 0) itr->second->expire_time = sWorld.GetGameTime(); + if (all==true) itr->second->expireTime = sWorld.GetGameTime(); + else if (itr->second->bid == 0) itr->second->expireTime = sWorld.GetGameTime(); } } } diff --git a/src/game/AuctionHouseHandler.cpp b/src/game/AuctionHouseHandler.cpp index 1744d99a2..fea7b156a 100644 --- a/src/game/AuctionHouseHandler.cpp +++ b/src/game/AuctionHouseHandler.cpp @@ -35,7 +35,7 @@ // post-incrementation is always slower than pre-incrementation ! // void called when player click on auctioneer npc -void WorldSession::HandleAuctionHelloOpcode( WorldPacket & recv_data ) +void WorldSession::HandleAuctionHelloOpcode(WorldPacket & recv_data) { ObjectGuid auctioneerGuid; // NPC guid recv_data >> auctioneerGuid; @@ -48,7 +48,7 @@ void WorldSession::HandleAuctionHelloOpcode( WorldPacket & recv_data ) } // remove fake death - if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); SendAuctionHello(unit); @@ -60,7 +60,7 @@ void WorldSession::SendAuctionHello(Unit* unit) // always return pointer AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntry(unit); - WorldPacket data( MSG_AUCTION_HELLO, 12 ); + WorldPacket data(MSG_AUCTION_HELLO, 12); data << unit->GetObjectGuid(); data << uint32(ahEntry->houseId); data << uint8(1); // 3.3.3: 1 - AH enabled, 0 - AH disabled @@ -68,47 +68,100 @@ void WorldSession::SendAuctionHello(Unit* unit) } // call this method when player bids, creates, or deletes auction -void WorldSession::SendAuctionCommandResult(uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError ) +void WorldSession::SendAuctionCommandResult(AuctionEntry *auc, AuctionAction Action, AuctionError ErrorCode, InventoryResult invError) { - WorldPacket data( SMSG_AUCTION_COMMAND_RESULT, 16 ); - data << uint32(auctionId); + WorldPacket data(SMSG_AUCTION_COMMAND_RESULT, 16); + data << uint32(auc ? auc->Id : 0); data << uint32(Action); data << uint32(ErrorCode); - if ( !ErrorCode && Action ) - data << uint32(bidError); // when bid, then send 0, once... + + switch (ErrorCode) + { + case AUCTION_OK: + if (Action == AUCTION_BID_PLACED) + data << uint32(auc->GetAuctionOutBid()); // new AuctionOutBid? + break; + case AUCTION_ERR_INVENTORY: + data << uint32(invError); + break; + case AUCTION_ERR_HIGHER_BID: + data << ObjectGuid(HIGHGUID_PLAYER,auc->bidder);// new bidder guid + data << uint32(auc->bid); // new bid + data << uint32(auc->GetAuctionOutBid()); // new AuctionOutBid? + break; + default: + break; + } + SendPacket(&data); } // this function sends notification, if bidder is online -void WorldSession::SendAuctionBidderNotification( uint32 location, uint32 auctionId, ObjectGuid bidderGuid, uint32 bidSum, uint32 diff, uint32 item_template) +void WorldSession::SendAuctionBidderNotification(AuctionEntry* auction) { WorldPacket data(SMSG_AUCTION_BIDDER_NOTIFICATION, (8*4)); - data << uint32(location); - data << uint32(auctionId); - data << bidderGuid; - data << uint32(bidSum); - data << uint32(diff); - data << uint32(item_template); - data << uint32(0); + data << uint32(auction->GetHouseId()); + data << uint32(auction->Id); + data << ObjectGuid(HIGHGUID_PLAYER, auction->bidder); + + // if 0, client shows ERR_AUCTION_WON_S, else ERR_AUCTION_OUTBID_S + data << uint32(auction->moneyDeliveryTime ? 0 : auction->bid); + data << uint32(auction->GetAuctionOutBid()); // AuctionOutBid? + data << uint32(auction->itemTemplate); + + Item *item = sAuctionMgr.GetAItem(auction->itemGuidLow); + uint32 randomId = item ? item->GetItemRandomPropertyId() : 0; + + data << uint32(randomId); // random property (value > 0) or suffix (value < 0) + SendPacket(&data); } // this void causes on client to display: "Your auction sold" -void WorldSession::SendAuctionOwnerNotification( AuctionEntry* auction) +void WorldSession::SendAuctionOwnerNotification(AuctionEntry* auction) { WorldPacket data(SMSG_AUCTION_OWNER_NOTIFICATION, (7*4)); data << uint32(auction->Id); - data << uint32(auction->bid); - data << uint32(0); // unk - data << uint32(0); // unk - data << uint32(0); // unk - data << uint32(auction->item_template); - data << uint32(0); // unk + data << uint32(auction->bid); // if 0, client shows ERR_AUCTION_EXPIRED_S, else ERR_AUCTION_SOLD_S (works only when guid==0) + data << uint32(auction->GetAuctionOutBid()); // AuctionOutBid? + + ObjectGuid guid = ObjectGuid(); + if (!auction->moneyDeliveryTime) // not sold yet + guid = ObjectGuid(HIGHGUID_PLAYER, auction->bidder);// bidder==0 and moneyDeliveryTime==0 for expired auctions, so it will show error message properly + + // if guid!=0, client updates auctions with new bid, outbid and bidderGuid, else it shows error messages as described above + data << guid; // bidder guid + data << uint32(auction->itemTemplate); // item entry + + Item *item = sAuctionMgr.GetAItem(auction->itemGuidLow); + uint32 randomId = item ? item->GetItemRandomPropertyId() : 0; + + data << uint32(randomId); // random property (value > 0) or suffix (value < 0) + + float timeLeft = float(auction->moneyDeliveryTime - time(NULL)) / float(DAY); + + data << float(timeLeft); // time till money arrive? only used if bid != 0 + + SendPacket(&data); +} + +// shows ERR_AUCTION_REMOVED_S +void WorldSession::SendAuctionRemovedNotification(AuctionEntry* auction) +{ + WorldPacket data(SMSG_AUCTION_REMOVED_NOTIFICATION, (3*4)); + data << uint32(auction->Id); + data << uint32(auction->itemTemplate); + + Item *item = sAuctionMgr.GetAItem(auction->itemGuidLow); + uint32 randomId = item ? item->GetItemRandomPropertyId() : 0; + + data << uint32(randomId); // random property (value > 0) or suffix (value < 0) + SendPacket(&data); } // this function sends mail to old bidder -void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPrice) +void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction) { ObjectGuid oldBidder_guid = ObjectGuid(HIGHGUID_PLAYER, auction->bidder); Player *oldBidder = sObjectMgr.GetPlayer(oldBidder_guid); @@ -118,18 +171,13 @@ void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPri oldBidder_accId = sObjectMgr.GetPlayerAccountIdByGUID(oldBidder_guid); // old bidder exist - if(oldBidder || oldBidder_accId) + if (oldBidder || oldBidder_accId) { std::ostringstream msgAuctionOutbiddedSubject; - msgAuctionOutbiddedSubject << auction->item_template << ":0:" << AUCTION_OUTBIDDED << ":0:0"; + msgAuctionOutbiddedSubject << auction->itemTemplate << ":0:" << AUCTION_OUTBIDDED << ":0:0"; - // Added for AHBot - if (oldBidder && !_player) - oldBidder->GetSession()->SendAuctionBidderNotification( auction->GetHouseId(), auction->Id, auctionbot.GetAHBObjectGuid(), newPrice, auction->GetAuctionOutBid(), auction->item_template); - - // Modified for AHBot - if (oldBidder && _player) - oldBidder->GetSession()->SendAuctionBidderNotification( auction->GetHouseId(), auction->Id, _player->GetObjectGuid(), newPrice, auction->GetAuctionOutBid(), auction->item_template); + if (oldBidder) + oldBidder->GetSession()->SendAuctionBidderNotification(auction); MailDraft(msgAuctionOutbiddedSubject.str(), "") // TODO: fix body .SetMoney(auction->bid) @@ -138,20 +186,23 @@ void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPri } // this function sends mail, when auction is cancelled to old bidder -void WorldSession::SendAuctionCancelledToBidderMail( AuctionEntry* auction ) +void WorldSession::SendAuctionCancelledToBidderMail(AuctionEntry* auction) { ObjectGuid bidder_guid = ObjectGuid(HIGHGUID_PLAYER, auction->bidder); Player *bidder = sObjectMgr.GetPlayer(bidder_guid); uint32 bidder_accId = 0; - if(!bidder) + if (!bidder) bidder_accId = sObjectMgr.GetPlayerAccountIdByGUID(bidder_guid); // bidder exist - if(bidder || bidder_accId) + if (bidder || bidder_accId) { std::ostringstream msgAuctionCancelledSubject; - msgAuctionCancelledSubject << auction->item_template << ":0:" << AUCTION_CANCELLED_TO_BIDDER << ":0:0"; + msgAuctionCancelledSubject << auction->itemTemplate << ":0:" << AUCTION_CANCELLED_TO_BIDDER << ":0:0"; + + if (bidder) + bidder->GetSession()->SendAuctionRemovedNotification(auction); MailDraft(msgAuctionCancelledSubject.str(), "") // TODO: fix body .SetMoney(auction->bid) @@ -192,22 +243,38 @@ AuctionHouseEntry const* WorldSession::GetCheckedAuctionHouseForAuctioneer(Objec } // this void creates new auction and adds auction to some auctionhouse -void WorldSession::HandleAuctionSellItem( WorldPacket & recv_data ) +void WorldSession::HandleAuctionSellItem(WorldPacket & recv_data) { DEBUG_LOG("WORLD: HandleAuctionSellItem"); ObjectGuid auctioneerGuid; - ObjectGuid itemGuid; - uint32 etime, bid, buyout; + uint32 etime, bid, buyout, itemCount; + std::vector guids; + std::vector stackSizes; + recv_data >> auctioneerGuid; - recv_data.read_skip(); // const 1? - recv_data >> itemGuid; - recv_data.read_skip(); // stack size + recv_data >> itemCount; + + if (itemCount > MAX_BAG_SIZE * 5) + { + recv_data.rpos(recv_data.wpos()); // should not happen + return; + } + + guids.resize(itemCount); + stackSizes.resize(itemCount); + + for (uint32 i = 0; i < itemCount; ++i) + { + recv_data >> guids[i]; // item guid + recv_data >> stackSizes[i]; // stack size + } + recv_data >> bid; recv_data >> buyout; recv_data >> etime; - if (itemGuid.IsEmpty() || !bid || !etime) + if (!bid || !etime) return; // check for cheaters Player *pl = GetPlayer(); @@ -223,7 +290,7 @@ void WorldSession::HandleAuctionSellItem( WorldPacket & recv_data ) etime *= MINUTE; // client understand only 3 auction time - switch(etime) + switch (etime) { case 1*MIN_AUCTION_TIME: case 2*MIN_AUCTION_TIME: @@ -234,88 +301,112 @@ void WorldSession::HandleAuctionSellItem( WorldPacket & recv_data ) } // remove fake death - if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - Item *it = pl->GetItemByGuid(itemGuid); - // do not allow to sell already auctioned items - if (sAuctionMgr.GetAItem(itemGuid.GetCounter())) + for (uint32 i = 0; i < itemCount; ++i) { - sLog.outError("AuctionError, %s is sending %s, but item is already in another auction", pl->GetGuidStr().c_str(), itemGuid.GetString().c_str()); - SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); - return; - } - // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to auction) - if(!it) - { - SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_ITEM_NOT_FOUND); - return; - } + ObjectGuid itemGuid = guids[i]; - if(!it->CanBeTraded()) - { - SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); - return; - } + if (itemGuid.IsEmpty()) + continue; - if ((it->GetProto()->Flags & ITEM_FLAG_CONJURED) || it->GetUInt32Value(ITEM_FIELD_DURATION)) - { - SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); - return; - } + uint32 stackSize = stackSizes[i]; - //we have to take deposit : - uint32 deposit = AuctionHouseMgr::GetAuctionDeposit( auctionHouseEntry, etime, it ); - if ( pl->GetMoney() < deposit ) - { - SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_NOT_ENOUGHT_MONEY); - return; - } + Item *it = pl->GetItemByGuid(itemGuid); - if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE) ) - { - sLog.outCommand(GetAccountId(),"GM %s (Account: %u) create auction: %s (Entry: %u Count: %u)", - GetPlayerName(), GetAccountId(), it->GetProto()->Name1, it->GetEntry(), it->GetCount()); - } + // do not allow to sell already auctioned items + if (sAuctionMgr.GetAItem(itemGuid.GetCounter())) + { + sLog.outError("AuctionError, %s is sending %s, but item is already in another auction", pl->GetGuidStr().c_str(), itemGuid.GetString().c_str()); + SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_INVENTORY, EQUIP_ERR_ITEM_NOT_FOUND); + continue; + } + + // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to auction) + if (!it) + { + SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_INVENTORY, EQUIP_ERR_ITEM_NOT_FOUND); + continue; + } + + if (!it->CanBeTraded()) + { + SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_INVENTORY, EQUIP_ERR_CANNOT_TRADE_THAT); + continue; + } + + if ((it->GetProto()->Flags & ITEM_FLAG_CONJURED) || it->GetUInt32Value(ITEM_FIELD_DURATION)) + { + SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_INVENTORY, EQUIP_ERR_CANNOT_TRADE_THAT); + continue; + } + + // check money for deposit + uint32 deposit = AuctionHouseMgr::GetAuctionDeposit(auctionHouseEntry, etime, it); + if (pl->GetMoney() < deposit) + { + SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_NOT_ENOUGH_MONEY); + continue; + } - pl->ModifyMoney( -int32(deposit) ); + if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) + { + sLog.outCommand(GetAccountId(),"GM %s (Account: %u) create auction: %s (Entry: %u Count: %u)", + GetPlayerName(), GetAccountId(), it->GetProto()->Name1, it->GetEntry(), it->GetCount()); + } - uint32 auction_time = uint32(etime * sWorld.getConfig(CONFIG_FLOAT_RATE_AUCTION_TIME)); + if (stackSize == 0) + stackSize = 1; - AuctionEntry *AH = new AuctionEntry; - AH->Id = sObjectMgr.GenerateAuctionID(); - AH->item_guidlow = itemGuid.GetCounter(); - AH->item_template = it->GetEntry(); - AH->owner = pl->GetGUIDLow(); - AH->startbid = bid; - AH->bidder = 0; - AH->bid = 0; - AH->buyout = buyout; - AH->expire_time = time(NULL) + auction_time; - AH->deposit = deposit; - AH->auctionHouseEntry = auctionHouseEntry; + if (stackSize > it->GetMaxStackCount()) // too big stack size + stackSize = it->GetMaxStackCount(); - DETAIL_LOG("selling %s to auctioneer %s with initial bid %u with buyout %u and with time %u (in sec) in auctionhouse %u", - itemGuid.GetString().c_str(), auctioneerGuid.GetString().c_str(), bid, buyout, auction_time, AH->GetHouseId()); - auctionHouse->AddAuction(AH); + if (!pl->HasItemCount(it->GetEntry(), stackSize)) // not enough items + continue; - sAuctionMgr.AddAItem(it); - pl->MoveItemFromInventory( it->GetBagSlot(), it->GetSlot(), true); + Item *newItem = it->CloneItem(stackSize, pl); - CharacterDatabase.BeginTransaction(); - it->DeleteFromInventoryDB(); - it->SaveToDB(); // recursive and not have transaction guard into self, not in inventiory and can be save standalone - AH->SaveToDB(); - pl->SaveInventoryAndGoldToDB(); - CharacterDatabase.CommitTransaction(); + pl->DestroyItemCount(it, stackSize, true); - SendAuctionCommandResult(AH->Id, AUCTION_SELL_ITEM, AUCTION_OK); + pl->ModifyMoney(-int32(deposit)); - GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION, 1); + uint32 auction_time = uint32(etime * sWorld.getConfig(CONFIG_FLOAT_RATE_AUCTION_TIME)); + + AuctionEntry *AH = new AuctionEntry; + AH->Id = sObjectMgr.GenerateAuctionID(); + AH->itemGuidLow = newItem->GetObjectGuid().GetCounter(); + AH->itemTemplate = newItem->GetEntry(); + AH->owner = pl->GetGUIDLow(); + AH->startbid = bid; + AH->bidder = 0; + AH->bid = 0; + AH->buyout = buyout; + AH->expireTime = time(NULL) + auction_time; + AH->moneyDeliveryTime = 0; + AH->deposit = deposit; + AH->auctionHouseEntry = auctionHouseEntry; + + DETAIL_LOG("selling %s to auctioneer %s with initial bid %u with buyout %u and with time %u (in sec) in auctionhouse %u", + itemGuid.GetString().c_str(), auctioneerGuid.GetString().c_str(), bid, buyout, auction_time, AH->GetHouseId()); + auctionHouse->AddAuction(AH); + + sAuctionMgr.AddAItem(newItem); + + CharacterDatabase.BeginTransaction(); + newItem->SaveToDB(); + AH->SaveToDB(); + pl->SaveInventoryAndGoldToDB(); + CharacterDatabase.CommitTransaction(); + + SendAuctionCommandResult(AH, AUCTION_STARTED, AUCTION_OK); + + GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION, 1); + } } // this function is called when client bids or buys out auction -void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data ) +void WorldSession::HandleAuctionPlaceBid(WorldPacket & recv_data) { DEBUG_LOG("WORLD: HandleAuctionPlaceBid"); @@ -336,17 +427,16 @@ void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data ) AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); // remove fake death - if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - AuctionEntry *auction = auctionHouse->GetAuction(auctionId); Player *pl = GetPlayer(); - if( !auction || auction->owner == pl->GetGUIDLow() ) + if (!auction || auction->owner == pl->GetGUIDLow()) { // you cannot bid your own auction: - SendAuctionCommandResult( 0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR ); + SendAuctionCommandResult(NULL, AUCTION_BID_PLACED, AUCTION_ERR_BID_OWN); return; } @@ -357,12 +447,12 @@ void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data ) if (!auction_owner && sObjectMgr.GetPlayerAccountIdByGUID(ownerGuid) == pl->GetSession()->GetAccountId()) { // you cannot bid your another character auction: - SendAuctionCommandResult( 0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR ); + SendAuctionCommandResult(NULL, AUCTION_BID_PLACED, AUCTION_ERR_BID_OWN); return; } // cheating - if(price <= auction->bid || price < auction->startbid) + if (price <= auction->bid || price < auction->startbid) return; // price too low for next bid if not buyout @@ -376,68 +466,61 @@ void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data ) if (price > pl->GetMoney()) { // you don't have enough money!, client tests! - // SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???); + // SendAuctionCommandResult(auction->auctionId, AUCTION_ERR_INVENTORY, EQUIP_ERR_NOT_ENOUGH_MONEY); return; } - if ((price < auction->buyout) || (auction->buyout == 0)) + if ((price < auction->buyout) || (auction->buyout == 0))// bid { - if (auction->bidder > 0) + if (pl->GetGUIDLow() == auction->bidder) { - if ( auction->bidder == pl->GetGUIDLow() ) - { - pl->ModifyMoney( -int32(price - auction->bid)); - } - else - { - // mail to last bidder and return money - SendAuctionOutbiddedMail( auction , price ); - pl->ModifyMoney( -int32(price) ); - } + pl->ModifyMoney(-int32(price - auction->bid)); } else { - pl->ModifyMoney( -int32(price) ); + pl->ModifyMoney(-int32(price)); + if (auction->bidder) // return money to old bidder if present + SendAuctionOutbiddedMail(auction); } + auction->bidder = pl->GetGUIDLow(); auction->bid = price; + + if (auction_owner) + auction_owner->GetSession()->SendAuctionOwnerNotification(auction); + GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price); // after this update we should save player's money ... - CharacterDatabase.PExecute("UPDATE auction SET buyguid = '%u',lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id); + CharacterDatabase.PExecute("UPDATE auction SET buyguid = '%u', lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id); - SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK, 0 ); + SendAuctionCommandResult(auction, AUCTION_BID_PLACED, AUCTION_OK); } - else + else // buyout { - // buyout: - if (pl->GetGUIDLow() == auction->bidder ) + if (pl->GetGUIDLow() == auction->bidder) { pl->ModifyMoney(-int32(auction->buyout - auction->bid)); } else { pl->ModifyMoney(-int32(auction->buyout)); - if ( auction->bidder ) // buyout for bidded auction .. - { - SendAuctionOutbiddedMail( auction, auction->buyout ); - } + if (auction->bidder) // return money to old bidder if present + SendAuctionOutbiddedMail(auction); } + auction->bidder = pl->GetGUIDLow(); auction->bid = auction->buyout; + GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, auction->buyout); - sAuctionMgr.SendAuctionSalePendingMail( auction ); - sAuctionMgr.SendAuctionSuccessfulMail( auction ); - sAuctionMgr.SendAuctionWonMail( auction ); + auction->moneyDeliveryTime = time(NULL) + HOUR; - SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK); + sAuctionMgr.SendAuctionWonMail(auction); - sAuctionMgr.RemoveAItem(auction->item_guidlow); - auctionHouse->RemoveAuction(auction->Id); - auction->DeleteFromDB(); + SendAuctionCommandResult(auction, AUCTION_BID_PLACED, AUCTION_OK); - delete auction; + CharacterDatabase.PExecute("UPDATE auction SET moneyTime = '" UI64FMTD "', buyguid = '%u', lastbid = '%u' WHERE id = '%u'", (uint64)auction->moneyDeliveryTime, auction->bidder, auction->bid, auction->Id); } CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); @@ -445,7 +528,7 @@ void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data ) } // this void is called when auction_owner cancels his auction -void WorldSession::HandleAuctionRemoveItem( WorldPacket & recv_data ) +void WorldSession::HandleAuctionRemoveItem(WorldPacket & recv_data) { DEBUG_LOG("WORLD: HandleAuctionRemoveItem"); @@ -453,7 +536,7 @@ void WorldSession::HandleAuctionRemoveItem( WorldPacket & recv_data ) uint32 auctionId; recv_data >> auctioneerGuid; recv_data >> auctionId; - //DEBUG_LOG( "Cancel AUCTION AuctionID: %u", auctionId); + //DEBUG_LOG("Cancel AUCTION AuctionID: %u", auctionId); AuctionHouseEntry const* auctionHouseEntry = GetCheckedAuctionHouseForAuctioneer(auctioneerGuid); if (!auctionHouseEntry) @@ -463,7 +546,7 @@ void WorldSession::HandleAuctionRemoveItem( WorldPacket & recv_data ) AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); // remove fake death - if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); AuctionEntry *auction = auctionHouse->GetAuction(auctionId); @@ -471,21 +554,21 @@ void WorldSession::HandleAuctionRemoveItem( WorldPacket & recv_data ) if (auction && auction->owner == pl->GetGUIDLow()) { - Item *pItem = sAuctionMgr.GetAItem(auction->item_guidlow); + Item *pItem = sAuctionMgr.GetAItem(auction->itemGuidLow); if (pItem) { if (auction->bidder > 0) // If we have a bidder, we have to send him the money he paid { uint32 auctionCut = auction->GetAuctionCut(); - if ( pl->GetMoney() < auctionCut ) //player doesn't have enough money, maybe message needed + if (pl->GetMoney() < auctionCut) // player doesn't have enough money, maybe message needed return; - //some auctionBidderNotification would be needed, but don't know that parts.. - SendAuctionCancelledToBidderMail( auction ); - pl->ModifyMoney( -int32(auctionCut) ); + + SendAuctionCancelledToBidderMail(auction); + pl->ModifyMoney(-int32(auctionCut)); } // Return the item by mail std::ostringstream msgAuctionCanceledOwner; - msgAuctionCanceledOwner << auction->item_template << ":0:" << AUCTION_CANCELED << ":0:0"; + msgAuctionCanceledOwner << auction->itemTemplate << ":0:" << AUCTION_CANCELED << ":0:0"; // item will deleted or added to received mail list MailDraft(msgAuctionCanceledOwner.str(), "") // TODO: fix body @@ -494,33 +577,33 @@ void WorldSession::HandleAuctionRemoveItem( WorldPacket & recv_data ) } else { - sLog.outError("Auction id: %u has nonexistent item (item guid : %u)!!!", auction->Id, auction->item_guidlow); - SendAuctionCommandResult( 0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR ); + sLog.outError("Auction id: %u has nonexistent item (item guid : %u)!!!", auction->Id, auction->itemGuidLow); + SendAuctionCommandResult(NULL, AUCTION_REMOVED, AUCTION_ERR_INVENTORY, EQUIP_ERR_ITEM_NOT_FOUND); return; } } else { - SendAuctionCommandResult( 0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR ); - //this code isn't possible ... maybe there should be ASSERT - sLog.outError("CHEATER : %u, he tried to cancel auction (id: %u) of another player, or auction is NULL", pl->GetGUIDLow(), auctionId ); + SendAuctionCommandResult(NULL, AUCTION_REMOVED, AUCTION_ERR_DATABASE); + // this code isn't possible ... maybe there should be ASSERT + sLog.outError("CHEATER : %u, he tried to cancel auction (id: %u) of another player, or auction is NULL", pl->GetGUIDLow(), auctionId); return; } - //inform player, that auction is removed - SendAuctionCommandResult( auction->Id, AUCTION_CANCEL, AUCTION_OK ); + // inform player, that auction is removed + SendAuctionCommandResult(auction, AUCTION_REMOVED, AUCTION_OK); // Now remove the auction CharacterDatabase.BeginTransaction(); auction->DeleteFromDB(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); - sAuctionMgr.RemoveAItem( auction->item_guidlow ); - auctionHouse->RemoveAuction( auction->Id ); + sAuctionMgr.RemoveAItem(auction->itemGuidLow); + auctionHouse->RemoveAuction(auction->Id); delete auction; } -//called when player lists his bids -void WorldSession::HandleAuctionListBidderItems( WorldPacket & recv_data ) +// called when player lists his bids +void WorldSession::HandleAuctionListBidderItems(WorldPacket & recv_data) { DEBUG_LOG("WORLD: HandleAuctionListBidderItems"); @@ -531,9 +614,9 @@ void WorldSession::HandleAuctionListBidderItems( WorldPacket & recv_data ) recv_data >> auctioneerGuid; recv_data >> listfrom; // not used in fact (this list not have page control in client) recv_data >> outbiddedCount; - if (recv_data.size() != (16 + outbiddedCount * 4 )) + if (recv_data.size() != (16 + outbiddedCount * 4)) { - sLog.outError("Client sent bad opcode!!! with count: %u and size : %lu (must be: %u)", outbiddedCount, (unsigned long)recv_data.size(),(16 + outbiddedCount * 4 )); + sLog.outError("Client sent bad opcode!!! with count: %u and size : %u (must be: %u)", outbiddedCount, (uint32)recv_data.size(), (16 + outbiddedCount * 4)); outbiddedCount = 0; } @@ -545,21 +628,21 @@ void WorldSession::HandleAuctionListBidderItems( WorldPacket & recv_data ) AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); // remove fake death - if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - WorldPacket data( SMSG_AUCTION_BIDDER_LIST_RESULT, (4+4+4) ); + WorldPacket data(SMSG_AUCTION_BIDDER_LIST_RESULT, (4+4+4)); Player *pl = GetPlayer(); data << uint32(0); // add 0 as count uint32 count = 0; uint32 totalcount = 0; - while ( outbiddedCount > 0) // add all data, which client requires + while (outbiddedCount > 0) // add all data, which client requires { --outbiddedCount; uint32 outbiddedAuctionId; recv_data >> outbiddedAuctionId; - AuctionEntry * auction = auctionHouse->GetAuction( outbiddedAuctionId ); - if ( auction && auction->BuildAuctionInfo(data)) + AuctionEntry *auction = auctionHouse->GetAuction(outbiddedAuctionId); + if (auction && auction->BuildAuctionInfo(data)) { ++totalcount; ++count; @@ -567,14 +650,14 @@ void WorldSession::HandleAuctionListBidderItems( WorldPacket & recv_data ) } auctionHouse->BuildListBidderItems(data, pl, count, totalcount); - data.put( 0, count ); // add count to placeholder + data.put(0, count); // add count to placeholder data << uint32(totalcount); - data << uint32(300); // unk 2.3.0 delay for next list request? + data << uint32(300); // unk 2.3.0 delay for next isFull request? SendPacket(&data); } // this void sends player info about his auctions -void WorldSession::HandleAuctionListOwnerItems( WorldPacket & recv_data ) +void WorldSession::HandleAuctionListOwnerItems(WorldPacket & recv_data) { DEBUG_LOG("WORLD: HandleAuctionListOwnerItems"); @@ -592,11 +675,11 @@ void WorldSession::HandleAuctionListOwnerItems( WorldPacket & recv_data ) AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); // remove fake death - if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - WorldPacket data( SMSG_AUCTION_OWNER_LIST_RESULT, (4+4+4) ); - data << (uint32) 0; // amount place holder + WorldPacket data(SMSG_AUCTION_OWNER_LIST_RESULT, (4+4+4)); + data << uint32(0); // amount place holder uint32 count = 0; uint32 totalcount = 0; @@ -604,18 +687,18 @@ void WorldSession::HandleAuctionListOwnerItems( WorldPacket & recv_data ) auctionHouse->BuildListOwnerItems(data, _player, count, totalcount); data.put(0, count); data << uint32(totalcount); - data << uint32(0); // 2.3.0 delay for next list request? + data << uint32(300); // 2.3.0 delay for next isFull request? SendPacket(&data); } -//this void is called when player clicks on search button -void WorldSession::HandleAuctionListItems( WorldPacket & recv_data ) +// this void is called when player clicks on search button +void WorldSession::HandleAuctionListItems(WorldPacket & recv_data) { DEBUG_LOG("WORLD: HandleAuctionListItems"); ObjectGuid auctioneerGuid; std::string searchedname; - uint8 levelmin, levelmax, usable; + uint8 levelmin, levelmax, usable, isFull, sortCount; uint32 listfrom, auctionSlotID, auctionMainCategory, auctionSubCategory, quality; recv_data >> auctioneerGuid; @@ -623,10 +706,27 @@ void WorldSession::HandleAuctionListItems( WorldPacket & recv_data ) recv_data >> searchedname; recv_data >> levelmin >> levelmax; - recv_data >> auctionSlotID >> auctionMainCategory >> auctionSubCategory; - recv_data >> quality >> usable; + recv_data >> auctionSlotID >> auctionMainCategory >> auctionSubCategory >> quality; + recv_data >> usable >> isFull >> sortCount; - recv_data.read_skip(16); // unknown 16 bytes: 00 07 01 00 00 01 05 00 06 00 09 01 08 00 03 00 + if (sortCount >= MAX_AUCTION_SORT) + return; + + uint8 Sort[MAX_AUCTION_SORT]; + memset(Sort, MAX_AUCTION_SORT, MAX_AUCTION_SORT); + + // auction columns sorting + for (uint32 i = 0; i < sortCount; ++i) + { + uint8 column, reversed; + recv_data >> column; + + if (column >= MAX_AUCTION_SORT) + return; + + recv_data >> reversed; + Sort[i] = (reversed > 0) ? (column |= AUCTION_SORT_REVERSED) : column; + } AuctionHouseEntry const* auctionHouseEntry = GetCheckedAuctionHouseForAuctioneer(auctioneerGuid); if (!auctionHouseEntry) @@ -635,37 +735,43 @@ void WorldSession::HandleAuctionListItems( WorldPacket & recv_data ) // always return pointer AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); + // Sort + AuctionHouseObject::AuctionEntryMap *aucs = auctionHouse->GetAuctions(); + std::list auctions; + for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = aucs->begin(); itr != aucs->end(); ++itr) + auctions.push_back(itr->second); + AuctionSorter sorter(Sort); + auctions.sort(sorter); + // remove fake death - if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); //DEBUG_LOG("Auctionhouse search %s list from: %u, searchedname: %s, levelmin: %u, levelmax: %u, auctionSlotID: %u, auctionMainCategory: %u, auctionSubCategory: %u, quality: %u, usable: %u", // auctioneerGuid.GetString().c_str(), listfrom, searchedname.c_str(), levelmin, levelmax, auctionSlotID, auctionMainCategory, auctionSubCategory, quality, usable); - WorldPacket data( SMSG_AUCTION_LIST_RESULT, (4+4+4) ); + WorldPacket data(SMSG_AUCTION_LIST_RESULT, (4+4+4)); uint32 count = 0; uint32 totalcount = 0; data << uint32(0); // converting string that we try to find to lower case std::wstring wsearchedname; - if(!Utf8toWStr(searchedname,wsearchedname)) + if (!Utf8toWStr(searchedname, wsearchedname)) return; wstrToLower(wsearchedname); - auctionHouse->BuildListAuctionItems(data, _player, - wsearchedname, listfrom, levelmin, levelmax, usable, - auctionSlotID, auctionMainCategory, auctionSubCategory, quality, - count, totalcount); + BuildListAuctionItems(auctions, data, wsearchedname, listfrom, levelmin, levelmax, usable, + auctionSlotID, auctionMainCategory, auctionSubCategory, quality, count, totalcount, isFull); data.put(0, count); data << uint32(totalcount); - data << uint32(300); // 2.3.0 delay for next list request? + data << uint32(300); // 2.3.0 delay for next isFull request? SendPacket(&data); } -void WorldSession::HandleAuctionListPendingSales( WorldPacket & recv_data ) +void WorldSession::HandleAuctionListPendingSales(WorldPacket & recv_data) { DEBUG_LOG("CMSG_AUCTION_LIST_PENDING_SALES"); @@ -677,17 +783,14 @@ void WorldSession::HandleAuctionListPendingSales( WorldPacket & recv_data ) if (!auctionHouseEntry) return; + // always return pointer + AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); + uint32 count = 0; WorldPacket data(SMSG_AUCTION_LIST_PENDING_SALES, 4); data << uint32(count); // count - /*for(uint32 i = 0; i < count; ++i) - { - data << ""; // string - data << ""; // string - data << uint32(0); - data << uint32(0); - data << float(0); - }*/ + auctionHouse->BuildListPendingSales(data, _player, count); + data.put(0, count); SendPacket(&data); } diff --git a/src/game/AuctionHouseMgr.cpp b/src/game/AuctionHouseMgr.cpp index 0d8e5c3e3..d392826fb 100644 --- a/src/game/AuctionHouseMgr.cpp +++ b/src/game/AuctionHouseMgr.cpp @@ -36,7 +36,7 @@ #include "Policies/SingletonImp.h" -INSTANTIATE_SINGLETON_1( AuctionHouseMgr ); +INSTANTIATE_SINGLETON_1(AuctionHouseMgr); AuctionHouseMgr::AuctionHouseMgr() { @@ -64,7 +64,7 @@ AuctionHouseObject * AuctionHouseMgr::GetAuctionsMap(AuctionHouseEntry const* ho uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item *pItem) { - float deposit = float(pItem->GetProto()->SellPrice * pItem->GetCount() * (time / MIN_AUCTION_TIME )); + float deposit = float(pItem->GetProto()->SellPrice * pItem->GetCount() * (time / MIN_AUCTION_TIME)); deposit = deposit * entry->depositPercent * 3.0f / 100.0f; @@ -76,11 +76,11 @@ uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 return uint32(deposit * sWorld.getConfig(CONFIG_FLOAT_RATE_AUCTION_DEPOSIT)); } -//does not clear ram -void AuctionHouseMgr::SendAuctionWonMail( AuctionEntry *auction ) +// does not clear ram +void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry *auction) { - Item *pItem = GetAItem(auction->item_guidlow); - if(!pItem) + Item *pItem = GetAItem(auction->itemGuidLow); + if (!pItem) return; ObjectGuid bidder_guid = ObjectGuid(HIGHGUID_PLAYER, auction->bidder); @@ -89,7 +89,7 @@ void AuctionHouseMgr::SendAuctionWonMail( AuctionEntry *auction ) uint32 bidder_accId = 0; // data for gm.log - if( sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE) ) + if (sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) { uint32 bidder_security = 0; std::string bidder_name; @@ -104,7 +104,7 @@ void AuctionHouseMgr::SendAuctionWonMail( AuctionEntry *auction ) bidder_accId = sObjectMgr.GetPlayerAccountIdByGUID(bidder_guid); bidder_security = sAccountMgr.GetSecurity(bidder_accId); - if (bidder_security > SEC_PLAYER ) // not do redundant DB requests + if (bidder_security > SEC_PLAYER) // not do redundant DB requests { if (!sObjectMgr.GetPlayerNameByGUID(bidder_guid, bidder_name)) bidder_name = sObjectMgr.GetMangosStringForDBCLocale(LANG_UNKNOWN); @@ -115,7 +115,7 @@ void AuctionHouseMgr::SendAuctionWonMail( AuctionEntry *auction ) { ObjectGuid owner_guid = ObjectGuid(HIGHGUID_PLAYER, auction->owner); std::string owner_name; - if(!sObjectMgr.GetPlayerNameByGUID(owner_guid, owner_name)) + if (!sObjectMgr.GetPlayerNameByGUID(owner_guid, owner_name)) owner_name = sObjectMgr.GetMangosStringForDBCLocale(LANG_UNKNOWN); uint32 owner_accid = sObjectMgr.GetPlayerAccountIdByGUID(owner_guid); @@ -127,17 +127,22 @@ void AuctionHouseMgr::SendAuctionWonMail( AuctionEntry *auction ) else if (!bidder) bidder_accId = sObjectMgr.GetPlayerAccountIdByGUID(bidder_guid); + ObjectGuid ownerGuid = ObjectGuid(HIGHGUID_PLAYER, auction->owner); + Player* auction_owner = sObjectMgr.GetPlayer(ownerGuid); + if (auction_owner) + auction_owner->GetSession()->SendAuctionOwnerNotification(auction); + // receiver exist - if(bidder || bidder_accId) + if (bidder || bidder_accId) { std::ostringstream msgAuctionWonSubject; - msgAuctionWonSubject << auction->item_template << ":0:" << AUCTION_WON; + msgAuctionWonSubject << auction->itemTemplate << ":0:" << AUCTION_WON; std::ostringstream msgAuctionWonBody; msgAuctionWonBody.width(16); msgAuctionWonBody << std::right << std::hex << auction->owner; msgAuctionWonBody << std::dec << ":" << auction->bid << ":" << auction->buyout; - DEBUG_LOG( "AuctionWon body string : %s", msgAuctionWonBody.str().c_str() ); + DEBUG_LOG("AuctionWon body string : %s", msgAuctionWonBody.str().c_str()); // set owner to bidder (to prevent delete item with sender char deleting) // owner in `data` will set at mail receive and item extracting @@ -146,7 +151,7 @@ void AuctionHouseMgr::SendAuctionWonMail( AuctionEntry *auction ) if (bidder) { - bidder->GetSession()->SendAuctionBidderNotification( auction->GetHouseId(), auction->Id, bidder_guid, 0, 0, auction->item_template); + bidder->GetSession()->SendAuctionBidderNotification(auction); // FIXME: for offline player need also bidder->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS, 1); } @@ -167,16 +172,16 @@ void AuctionHouseMgr::SendAuctionWonMail( AuctionEntry *auction ) } } -void AuctionHouseMgr::SendAuctionSalePendingMail( AuctionEntry * auction ) +void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry * auction) { ObjectGuid owner_guid = ObjectGuid(HIGHGUID_PLAYER, auction->owner); Player *owner = sObjectMgr.GetPlayer(owner_guid); // owner exist (online or offline) - if(owner || sObjectMgr.GetPlayerAccountIdByGUID(owner_guid)) + if (owner || sObjectMgr.GetPlayerAccountIdByGUID(owner_guid)) { std::ostringstream msgAuctionSalePendingSubject; - msgAuctionSalePendingSubject << auction->item_template << ":0:" << AUCTION_SALE_PENDING; + msgAuctionSalePendingSubject << auction->itemTemplate << ":0:" << AUCTION_SALE_PENDING; std::ostringstream msgAuctionSalePendingBody; uint32 auctionCut = auction->GetAuctionCut(); @@ -196,21 +201,21 @@ void AuctionHouseMgr::SendAuctionSalePendingMail( AuctionEntry * auction ) } } -//call this method to send mail to auction owner, when auction is successful, it does not clear ram -void AuctionHouseMgr::SendAuctionSuccessfulMail( AuctionEntry * auction ) +// call this method to send mail to auction owner, when auction is successful, it does not clear ram +void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry * auction) { ObjectGuid owner_guid = ObjectGuid(HIGHGUID_PLAYER, auction->owner); Player *owner = sObjectMgr.GetPlayer(owner_guid); uint32 owner_accId = 0; - if(!owner) + if (!owner) owner_accId = sObjectMgr.GetPlayerAccountIdByGUID(owner_guid); // owner exist - if(owner || owner_accId) + if (owner || owner_accId) { std::ostringstream msgAuctionSuccessfulSubject; - msgAuctionSuccessfulSubject << auction->item_template << ":0:" << AUCTION_SUCCESSFUL; + msgAuctionSuccessfulSubject << auction->itemTemplate << ":0:" << AUCTION_SUCCESSFUL; std::ostringstream auctionSuccessfulBody; uint32 auctionCut = auction->GetAuctionCut(); @@ -226,26 +231,24 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail( AuctionEntry * auction ) if (owner) { - //FIXME: what do if owner offline + // FIXME: what do if owner offline owner->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS, profit); owner->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD, auction->bid); - //send auction owner notification, bidder must be current! - owner->GetSession()->SendAuctionOwnerNotification( auction ); } MailDraft(msgAuctionSuccessfulSubject.str(), auctionSuccessfulBody.str()) .SetMoney(profit) - .SendMailTo(MailReceiver(owner, owner_guid), auction, MAIL_CHECK_MASK_COPIED, HOUR); + .SendMailTo(MailReceiver(owner, owner_guid), auction, MAIL_CHECK_MASK_COPIED); } } -//does not clear ram -void AuctionHouseMgr::SendAuctionExpiredMail( AuctionEntry * auction ) -{ //return an item in auction to its owner by mail - Item *pItem = GetAItem(auction->item_guidlow); - if(!pItem) +// does not clear ram +void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry * auction) +{ // return an item in auction to its owner by mail + Item *pItem = GetAItem(auction->itemGuidLow); + if (!pItem) { - sLog.outError("Auction item (GUID: %u) not found, and lost.",auction->item_guidlow); + sLog.outError("Auction item (GUID: %u) not found, and lost.", auction->itemGuidLow); return; } @@ -253,17 +256,17 @@ void AuctionHouseMgr::SendAuctionExpiredMail( AuctionEntry * auction ) Player *owner = sObjectMgr.GetPlayer(owner_guid); uint32 owner_accId = 0; - if(!owner) + if (!owner) owner_accId = sObjectMgr.GetPlayerAccountIdByGUID(owner_guid); // owner exist - if(owner || owner_accId) + if (owner || owner_accId) { std::ostringstream subject; - subject << auction->item_template << ":0:" << AUCTION_EXPIRED << ":0:0"; + subject << auction->itemGuidLow << ":0:" << AUCTION_EXPIRED << ":0:0"; - if ( owner ) - owner->GetSession()->SendAuctionOwnerNotification( auction ); + if (owner) + owner->GetSession()->SendAuctionOwnerNotification(auction); else RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !! @@ -288,9 +291,9 @@ void AuctionHouseMgr::LoadAuctionItems() CharacterDatabase.PExecute("DELETE FROM item_instance WHERE owner_guid = '%u'",std::numeric_limits< uint32 >::max()); // data needs to be at first place for Item::LoadFromDB 0 1 2 3 - QueryResult *result = CharacterDatabase.Query( "SELECT data,text,itemguid,item_template FROM auction JOIN item_instance ON itemguid = guid" ); + QueryResult *result = CharacterDatabase.Query("SELECT data,text,itemguid,item_template FROM auction JOIN item_instance ON itemguid = guid"); - if( !result ) + if (!result) { barGoLink bar(1); bar.step(); @@ -299,7 +302,7 @@ void AuctionHouseMgr::LoadAuctionItems() return; } - barGoLink bar( (int)result->GetRowCount() ); + barGoLink bar((int)result->GetRowCount()); uint32 count = 0; @@ -314,15 +317,15 @@ void AuctionHouseMgr::LoadAuctionItems() ItemPrototype const *proto = ObjectMgr::GetItemPrototype(item_template); - if(!proto) + if (!proto) { - sLog.outError( "AuctionHouseMgr::LoadAuctionItems: Unknown item (GUID: %u id: #%u) in auction, skipped.", item_guid,item_template); + sLog.outError("AuctionHouseMgr::LoadAuctionItems: Unknown item (GUID: %u id: #%u) in auction, skipped.", item_guid,item_template); continue; } Item *item = NewItemOrBag(proto); - if(!item->LoadFromDB(item_guid, fields)) + if (!item->LoadFromDB(item_guid, fields)) { delete item; continue; @@ -331,17 +334,17 @@ void AuctionHouseMgr::LoadAuctionItems() ++count; } - while( result->NextRow() ); + while (result->NextRow()); delete result; sLog.outString(); - sLog.outString( ">> Loaded %u auction items", count ); + sLog.outString(">> Loaded %u auction items", count); } void AuctionHouseMgr::LoadAuctions() { QueryResult *result = CharacterDatabase.Query("SELECT COUNT(*) FROM auction"); - if( !result ) + if (!result) { barGoLink bar(1); bar.step(); @@ -354,7 +357,7 @@ void AuctionHouseMgr::LoadAuctions() uint32 AuctionCount=fields[0].GetUInt32(); delete result; - if(!AuctionCount) + if (!AuctionCount) { barGoLink bar(1); bar.step(); @@ -363,8 +366,8 @@ void AuctionHouseMgr::LoadAuctions() return; } - result = CharacterDatabase.Query( "SELECT id,houseid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit FROM auction" ); - if( !result ) + result = CharacterDatabase.Query("SELECT id,houseid,itemguid,item_template,itemowner,buyoutprice,time,moneyTime,buyguid,lastbid,startbid,deposit FROM auction"); + if (!result) { barGoLink bar(1); bar.step(); @@ -373,7 +376,7 @@ void AuctionHouseMgr::LoadAuctions() return; } - barGoLink bar( AuctionCount ); + barGoLink bar(AuctionCount); AuctionEntry *auction; @@ -386,24 +389,25 @@ void AuctionHouseMgr::LoadAuctions() auction = new AuctionEntry; auction->Id = fields[0].GetUInt32(); uint32 houseid = fields[1].GetUInt32(); - auction->item_guidlow = fields[2].GetUInt32(); - auction->item_template = fields[3].GetUInt32(); + auction->itemGuidLow = fields[2].GetUInt32(); + auction->itemTemplate = fields[3].GetUInt32(); auction->owner = fields[4].GetUInt32(); auction->buyout = fields[5].GetUInt32(); - auction->expire_time = fields[6].GetUInt32(); - auction->bidder = fields[7].GetUInt32(); - auction->bid = fields[8].GetUInt32(); - auction->startbid = fields[9].GetUInt32(); - auction->deposit = fields[10].GetUInt32(); + auction->expireTime = fields[6].GetUInt32(); + auction->moneyDeliveryTime = fields[7].GetUInt32(); + auction->bidder = fields[8].GetUInt32(); + auction->bid = fields[9].GetUInt32(); + auction->startbid = fields[10].GetUInt32(); + auction->deposit = fields[11].GetUInt32(); auction->auctionHouseEntry = NULL; // init later // check if sold item exists for guid // and item_template in fact (GetAItem will fail if problematic in result check in AuctionHouseMgr::LoadAuctionItems) - Item* pItem = GetAItem(auction->item_guidlow); + Item* pItem = GetAItem(auction->itemGuidLow); if (!pItem) { auction->DeleteFromDB(); - sLog.outError("Auction %u has not a existing item : %u, deleted", auction->Id, auction->item_guidlow); + sLog.outError("Auction %u has not a existing item : %u, deleted", auction->Id, auction->itemGuidLow); delete auction; continue; } @@ -417,14 +421,14 @@ void AuctionHouseMgr::LoadAuctions() // Attempt send item back to owner std::ostringstream msgAuctionCanceledOwner; - msgAuctionCanceledOwner << auction->item_template << ":0:" << AUCTION_CANCELED << ":0:0"; + msgAuctionCanceledOwner << auction->itemTemplate << ":0:" << AUCTION_CANCELED << ":0:0"; // item will deleted or added to received mail list MailDraft(msgAuctionCanceledOwner.str(), "") // TODO: fix body .AddItem(pItem) .SendMailTo(MailReceiver(ObjectGuid(HIGHGUID_PLAYER, auction->owner)), auction, MAIL_CHECK_MASK_COPIED); - RemoveAItem(auction->item_guidlow); + RemoveAItem(auction->itemGuidLow); auction->DeleteFromDB(); delete auction; @@ -437,17 +441,17 @@ void AuctionHouseMgr::LoadAuctions() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u auctions", AuctionCount ); + sLog.outString(">> Loaded %u auctions", AuctionCount); } -void AuctionHouseMgr::AddAItem( Item* it ) +void AuctionHouseMgr::AddAItem(Item* it) { - MANGOS_ASSERT( it ); - MANGOS_ASSERT( mAitems.find(it->GetGUIDLow()) == mAitems.end()); + MANGOS_ASSERT(it); + MANGOS_ASSERT(mAitems.find(it->GetGUIDLow()) == mAitems.end()); mAitems[it->GetGUIDLow()] = it; } -bool AuctionHouseMgr::RemoveAItem( uint32 id ) +bool AuctionHouseMgr::RemoveAItem(uint32 id) { ItemMap::iterator i = mAitems.find(id); if (i == mAitems.end()) @@ -470,7 +474,7 @@ uint32 AuctionHouseMgr::GetAuctionHouseTeam(AuctionHouseEntry const* house) // auction houses have faction field pointing to PLAYER,* factions, // but player factions not have filled team field, and hard go from faction value to faction_template value, // so more easy just sort by auction house ids - switch(house->houseId) + switch (house->houseId) { case 1: case 2: case 3: return ALLIANCE; @@ -486,7 +490,7 @@ AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntry(Unit* unit) { uint32 houseid = 1; // dwarf auction house (used for normal cut/etc percents) - if(!sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + if (!sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) { if (unit->GetTypeId() == TYPEID_UNIT) { @@ -494,7 +498,7 @@ AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntry(Unit* unit) // AuctionHouse.dbc have faction field with _player_ factions associated with auction house races. // but no easy way convert creature faction to player race faction for specific city uint32 factionTemplateId = unit->getFaction(); - switch(factionTemplateId) + switch (factionTemplateId) { case 12: houseid = 1; break; // human case 29: houseid = 6; break; // orc, and generic for horde @@ -511,11 +515,11 @@ AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntry(Unit* unit) default: // for unknown case { FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId); - if(!u_entry) + if (!u_entry) houseid = 7; // goblin auction house - else if(u_entry->ourMask & FACTION_MASK_ALLIANCE) + else if (u_entry->ourMask & FACTION_MASK_ALLIANCE) houseid = 1; // human auction house - else if(u_entry->ourMask & FACTION_MASK_HORDE) + else if (u_entry->ourMask & FACTION_MASK_HORDE) houseid = 6; // orc auction house else houseid = 7; // goblin auction house @@ -547,32 +551,48 @@ void AuctionHouseObject::Update() time_t curTime = sWorld.GetGameTime(); ///- Handle expired auctions AuctionEntryMap::iterator next; - for (AuctionEntryMap::iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end();itr = next) + for (AuctionEntryMap::iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); itr = next) { next = itr; ++next; - if (curTime > (itr->second->expire_time)) + + if (itr->second->moneyDeliveryTime) { - ///- Either cancel the auction if there was no bidder - if (itr->second->bidder == 0) + if (curTime > itr->second->moneyDeliveryTime) { - sAuctionMgr.SendAuctionExpiredMail( itr->second ); + itr->second->SetDeleted(); + sAuctionMgr.SendAuctionSuccessfulMail(itr->second); + + itr->second->DeleteFromDB(); + sAuctionMgr.RemoveAItem(itr->second->itemGuidLow); + delete itr->second; + RemoveAuction(itr->first); } - ///- Or perform the transaction - else + } + else + { + if (curTime > itr->second->expireTime) { - //we should send an "item sold" message if the seller is online - //we send the item to the winner - //we send the money to the seller - sAuctionMgr.SendAuctionSuccessfulMail( itr->second ); - sAuctionMgr.SendAuctionWonMail( itr->second ); - } + itr->second->SetDeleted(); + ///- Either cancel the auction if there was no bidder + if (itr->second->bidder == 0) + { + sAuctionMgr.SendAuctionExpiredMail(itr->second); + } + ///- Or perform the transaction + else + { + itr->second->moneyDeliveryTime = time(NULL) + HOUR; + sAuctionMgr.SendAuctionWonMail(itr->second); + continue; + } - ///- In any case clear the auction - itr->second->DeleteFromDB(); - sAuctionMgr.RemoveAItem(itr->second->item_guidlow); - delete itr->second; - RemoveAuction(itr->first); + ///- In any case clear the auction + itr->second->DeleteFromDB(); + sAuctionMgr.RemoveAItem(itr->second->itemGuidLow); + delete itr->second; + RemoveAuction(itr->first); + } } } } @@ -582,7 +602,9 @@ void AuctionHouseObject::BuildListBidderItems(WorldPacket& data, Player* player, for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin();itr != AuctionsMap.end();++itr) { AuctionEntry *Aentry = itr->second; - if( Aentry && Aentry->bidder == player->GetGUIDLow() ) + if (Aentry->moneyDeliveryTime) + continue; + if (Aentry && Aentry->bidder == player->GetGUIDLow()) { if (itr->second->BuildAuctionInfo(data)) ++count; @@ -593,83 +615,301 @@ void AuctionHouseObject::BuildListBidderItems(WorldPacket& data, Player* player, void AuctionHouseObject::BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount) { - for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin();itr != AuctionsMap.end();++itr) + for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) { AuctionEntry *Aentry = itr->second; - if( Aentry && Aentry->owner == player->GetGUIDLow() ) + if (Aentry->moneyDeliveryTime) + continue; + if (Aentry && Aentry->owner == player->GetGUIDLow()) { - if(Aentry->BuildAuctionInfo(data)) + if (Aentry->BuildAuctionInfo(data)) ++count; ++totalcount; } } } -void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player, - std::wstring const& wsearchedname, uint32 listfrom, uint32 levelmin, uint32 levelmax, uint32 usable, - uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, - uint32& count, uint32& totalcount) +bool AuctionEntry::CompareAuctionEntry(uint32 column, const AuctionEntry *auc) const { - int loc_idx = player->GetSession()->GetSessionDbLocaleIndex(); + if (IsDeleted() || auc->IsDeleted()) + return false; - for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin();itr != AuctionsMap.end();++itr) + Item *item1 = sAuctionMgr.GetAItem(itemGuidLow); + Item *item2 = sAuctionMgr.GetAItem(auc->itemGuidLow); + Player *pl1 = NULL; + Player *pl2 = NULL; + int res = 0; + time_t currentTime = time(NULL); + + switch (column) { - AuctionEntry *Aentry = itr->second; - Item *item = sAuctionMgr.GetAItem(Aentry->item_guidlow); - if (!item) - continue; + case 0: // level = 0 + if (!item1 || !item2) + break; + if (item1->GetProto()->RequiredLevel < item2->GetProto()->RequiredLevel) + return true; + else if (item1->GetProto()->RequiredLevel > item2->GetProto()->RequiredLevel) + return false; + break; + case 1: // quality = 1 + if (!item1 || !item2) + break; + if (item1->GetProto()->Quality < item2->GetProto()->Quality) + return true; + else if (item1->GetProto()->Quality > item2->GetProto()->Quality) + return false; + break; + case 2: // buyoutthenbid = 2 + if (buyout) + { + if (buyout < auc->buyout) + return true; + else if (buyout > auc->buyout) + return false; + } + else + { + if (bid < auc->bid) + return true; + else if (bid > auc->bid) + return false; + } + break; + case 3: // duration = 3 + if ((expireTime - currentTime) < (auc->expireTime - currentTime)) + return true; + else if ((expireTime - currentTime) > (auc->expireTime - currentTime)) + return false; + break; + case 4: // status = 4 + if (bidder < auc->bidder) + return true; + else if (bidder > auc->bidder) + return false; + break; + case 5: // name = 5 + if (!item1 || !item2) + break; + res = strcmp(item1->GetProto()->Name1, item2->GetProto()->Name1); + if (res < 0) + return true; + else if (res > 0) + return false; + break; + case 6: // minbidbuyout = 6 + if (bid) + { + if (bid < auc->bid) + return true; + else if (bid > auc->bid) + return false; + } + else if (startbid) + { + if (startbid < auc->startbid) + return true; + else if (startbid > auc->startbid) + return false; + } + else + { + if (buyout < auc->buyout) + return true; + else if (buyout > auc->buyout) + return false; + } + break; + case 7: // seller = 7 + pl1 = sObjectMgr.GetPlayer(ObjectGuid(HIGHGUID_PLAYER, owner)); + pl2 = sObjectMgr.GetPlayer(ObjectGuid(HIGHGUID_PLAYER, auc->owner)); + if (!pl1 || !pl2) + break; + res = strcmp(pl1->GetName(), pl2->GetName()); + if (res < 0) + return true; + else if (res > 0) + return false; + break; + case 8: // bid = 8 + if (bid) + { + if (bid < auc->bid) + return true; + else if (bid > auc->bid) + return false; + } + else + { + if (startbid < auc->startbid) + return true; + else if (startbid > auc->startbid) + return false; + } + break; + case 9: // quantity = 9 + if (!item1 || !item2) + break; + if (item1->GetCount() < item2->GetCount()) + return true; + else if (item1->GetCount() > item2->GetCount()) + return false; + break; + case 10: // buyout = 10 + if (buyout < auc->buyout) + return true; + else if (buyout > auc->buyout) + return false; + break; + case 11: // unused = 11 + break; + default: + break; + } - ItemPrototype const *proto = item->GetProto(); + if (Id < auc->Id) + return true; + else if (Id > auc->Id) + return false; - if (itemClass != 0xffffffff && proto->Class != itemClass) - continue; + return false; +} - if (itemSubClass != 0xffffffff && proto->SubClass != itemSubClass) - continue; +bool AuctionSorter::operator()(const AuctionEntry *auc1, const AuctionEntry *auc2) const +{ + bool result = false; + uint32 column = 0; - if (inventoryType != 0xffffffff && proto->InventoryType != inventoryType) - continue; + for (uint32 i = 0; i < MAX_AUCTION_SORT; ++i) + { + if (m_sort[i] == MAX_AUCTION_SORT) // end of sort + { + column = m_sort[0]; // use main column + break; + } - if (quality != 0xffffffff && proto->Quality < quality) - continue; + column = m_sort[i]; - if (levelmin != 0x00 && (proto->RequiredLevel < levelmin || (levelmax != 0x00 && proto->RequiredLevel > levelmax))) - continue; + result = auc1->CompareAuctionEntry(column & ~AUCTION_SORT_REVERSED, auc2); - if (usable != 0x00 && player->CanUseItem( item ) != EQUIP_ERR_OK) - continue; + if (result) + break; + } - std::string name = proto->Name1; - if(name.empty()) + if (column & AUCTION_SORT_REVERSED) // reversed flag + result = !result; + + return result; +} + +void WorldSession::BuildListAuctionItems(std::list &auctions, WorldPacket& data, std::wstring const& wsearchedname, uint32 listfrom, uint32 levelmin, + uint32 levelmax, uint32 usable, uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, uint32& count, uint32& totalcount, bool isFull) +{ + int loc_idx = _player->GetSession()->GetSessionDbLocaleIndex(); + + for (std::list::const_iterator itr = auctions.begin(); itr != auctions.end();++itr) + { + AuctionEntry *Aentry = *itr; + if (Aentry->moneyDeliveryTime) + continue; + Item *item = sAuctionMgr.GetAItem(Aentry->itemGuidLow); + if (!item) continue; - // local name - if ( loc_idx >= 0 ) + if (isFull) + { + ++count; + Aentry->BuildAuctionInfo(data); + } + else { - ItemLocale const *il = sObjectMgr.GetItemLocale(proto->ItemId); - if (il) + ItemPrototype const *proto = item->GetProto(); + + if (itemClass != 0xffffffff && proto->Class != itemClass) + continue; + + if (itemSubClass != 0xffffffff && proto->SubClass != itemSubClass) + continue; + + if (inventoryType != 0xffffffff && proto->InventoryType != inventoryType) + continue; + + if (quality != 0xffffffff && proto->Quality < quality) + continue; + + if (levelmin != 0x00 && (proto->RequiredLevel < levelmin || (levelmax != 0x00 && proto->RequiredLevel > levelmax))) + continue; + + if (usable != 0x00 && _player->CanUseItem(item) != EQUIP_ERR_OK) + continue; + + std::string name = proto->Name1; + if (name.empty()) + continue; + + // local name + if (loc_idx >= 0) { - if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty()) - name = il->Name[loc_idx]; + ItemLocale const *il = sObjectMgr.GetItemLocale(proto->ItemId); + if (il) + { + if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty()) + name = il->Name[loc_idx]; + } + } + + if (!wsearchedname.empty() && !Utf8FitTo(name, wsearchedname)) + continue; + + if (count < 50 && totalcount >= listfrom) + { + ++count; + Aentry->BuildAuctionInfo(data); } } - if (!wsearchedname.empty() && !Utf8FitTo(name, wsearchedname) ) - continue; + ++totalcount; + } +} - if (count < 50 && totalcount >= listfrom) +void AuctionHouseObject::BuildListPendingSales(WorldPacket& data, Player* player, uint32& count) +{ + for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) + { + AuctionEntry *Aentry = itr->second; + if (!Aentry->moneyDeliveryTime) + continue; + if (Aentry && Aentry->owner == player->GetGUIDLow()) { + Item *pItem = sAuctionMgr.GetAItem(Aentry->itemGuidLow); + if (!pItem) + { + sLog.outError("Auction: item with guid %u doesn't exist!", Aentry->itemGuidLow); + continue; + } + + std::ostringstream str1; + str1 << Aentry->itemTemplate << ":" << pItem->GetItemRandomPropertyId() << ":" << AUCTION_SUCCESSFUL << ":" << Aentry->Id << ":" << pItem->GetCount(); + + std::ostringstream str2; + str2.width(16); + str2 << std::right << std::hex << Aentry->bidder << std::dec << ":"; + str2 << Aentry->bid << ":" << Aentry->buyout << ":" << Aentry->deposit << ":" << Aentry->GetAuctionCut(); + + data << str1.str(); // string "%d:%d:%d:%d:%d" -> itemId, ItemRandomPropertyId, 2, auctionId, unk1 (stack size?, unused) + data << str2.str(); // string "%16I64X:%d:%d:%d:%d" -> bidderGuid, bid, buyout, deposit, auctionCut + data << uint32(97250); // unk1 + data << uint32(68); // unk2 + float timeLeft = float(Aentry->moneyDeliveryTime - time(NULL)) / float(DAY); + data << float(timeLeft); // time left ++count; - Aentry->BuildAuctionInfo(data); } - ++totalcount; } } -//this function inserts to WorldPacket auction's data +// this function inserts to WorldPacket auction's data bool AuctionEntry::BuildAuctionInfo(WorldPacket & data) const { - Item *pItem = sAuctionMgr.GetAItem(item_guidlow); + Item *pItem = sAuctionMgr.GetAItem(itemGuidLow); if (!pItem) { sLog.outError("auction to item, that doesn't exist !!!!"); @@ -685,19 +925,18 @@ bool AuctionEntry::BuildAuctionInfo(WorldPacket & data) const data << uint32(pItem->GetEnchantmentCharges(EnchantmentSlot(i))); } - data << uint32(pItem->GetItemRandomPropertyId()); //random item property id - data << uint32(pItem->GetItemSuffixFactor()); //SuffixFactor - data << uint32(pItem->GetCount()); //item->count - data << uint32(pItem->GetSpellCharges()); //item->charge FFFFFFF - data << uint32(0); //Unknown - data << ObjectGuid(HIGHGUID_PLAYER, owner); //Auction->owner - data << uint32(startbid); //Auction->startbid (not sure if useful) - data << uint32(bid ? GetAuctionOutBid() : 0); - //minimal outbid - data << uint32(buyout); //auction->buyout - data << uint32((expire_time-time(NULL))*IN_MILLISECONDS);//time left - data << ObjectGuid(HIGHGUID_PLAYER, bidder); //auction->bidder current - data << uint32(bid); //current bid + data << uint32(pItem->GetItemRandomPropertyId()); // random item property id + data << uint32(pItem->GetItemSuffixFactor()); // SuffixFactor + data << uint32(pItem->GetCount()); // item->count + data << uint32(pItem->GetSpellCharges()); // item->charge FFFFFFF + data << uint32(0); // item flags (dynamic?) (0x04 no lockId?) + data << ObjectGuid(HIGHGUID_PLAYER, owner); // Auction->owner + data << uint32(startbid); // Auction->startbid (not sure if useful) + data << uint32(bid ? GetAuctionOutBid() : 0); // minimal outbid + data << uint32(buyout); // auction->buyout + data << uint32((expireTime-time(NULL))*IN_MILLISECONDS);// time left + data << ObjectGuid(HIGHGUID_PLAYER, bidder); // auction->bidder current + data << uint32(bid); // current bid return true; } @@ -718,13 +957,13 @@ uint32 AuctionEntry::GetAuctionOutBid() const void AuctionEntry::DeleteFromDB() const { //No SQL injection (Id is integer) - CharacterDatabase.PExecute("DELETE FROM auction WHERE id = '%u'",Id); + CharacterDatabase.PExecute("DELETE FROM auction WHERE id = '%u'", Id); } void AuctionEntry::SaveToDB() const { //No SQL injection (no strings) - CharacterDatabase.PExecute("INSERT INTO auction (id,houseid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit) " - "VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '" UI64FMTD "', '%u', '%u', '%u', '%u')", - Id, auctionHouseEntry->houseId, item_guidlow, item_template, owner, buyout, (uint64)expire_time, bidder, bid, startbid, deposit); + CharacterDatabase.PExecute("INSERT INTO auction (id,houseid,itemguid,item_template,itemowner,buyoutprice,time,moneyTime,buyguid,lastbid,startbid,deposit) " + "VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '" UI64FMTD "', '" UI64FMTD "', '%u', '%u', '%u', '%u')", + Id, auctionHouseEntry->houseId, itemGuidLow, itemTemplate, owner, buyout, (uint64)expireTime, (uint64)moneyDeliveryTime, bidder, bid, startbid, deposit); } diff --git a/src/game/AuctionHouseMgr.h b/src/game/AuctionHouseMgr.h index d967e3a51..1f49b9f7d 100644 --- a/src/game/AuctionHouseMgr.h +++ b/src/game/AuctionHouseMgr.h @@ -30,35 +30,46 @@ class Unit; class WorldPacket; #define MIN_AUCTION_TIME (12*HOUR) +#define MAX_AUCTION_SORT 12 +#define AUCTION_SORT_REVERSED 0x10 enum AuctionError { - AUCTION_OK = 0, - AUCTION_INTERNAL_ERROR = 2, - AUCTION_NOT_ENOUGHT_MONEY = 3, - AUCTION_ITEM_NOT_FOUND = 4, - CANNOT_BID_YOUR_AUCTION_ERROR = 10 + AUCTION_OK = 0, // depends on enum AuctionAction + AUCTION_ERR_INVENTORY = 1, // depends on enum InventoryChangeResult + AUCTION_ERR_DATABASE = 2, // ERR_AUCTION_DATABASE_ERROR (default) + AUCTION_ERR_NOT_ENOUGH_MONEY = 3, // ERR_NOT_ENOUGH_MONEY + AUCTION_ERR_ITEM_NOT_FOUND = 4, // ERR_ITEM_NOT_FOUND + AUCTION_ERR_HIGHER_BID = 5, // ERR_AUCTION_HIGHER_BID + AUCTION_ERR_BID_INCREMENT = 7, // ERR_AUCTION_BID_INCREMENT + AUCTION_ERR_BID_OWN = 10, // ERR_AUCTION_BID_OWN + AUCTION_ERR_RESTRICTED_ACCOUNT = 13 // ERR_RESTRICTED_ACCOUNT }; enum AuctionAction { - AUCTION_SELL_ITEM = 0, - AUCTION_CANCEL = 1, - AUCTION_PLACE_BID = 2 + AUCTION_STARTED = 0, // ERR_AUCTION_STARTED + AUCTION_REMOVED = 1, // ERR_AUCTION_REMOVED + AUCTION_BID_PLACED = 2 // ERR_AUCTION_BID_PLACED }; struct AuctionEntry { + AuctionEntry() : m_deleted(false) {}; + + bool m_deleted; + uint32 Id; - uint32 item_guidlow; - uint32 item_template; + uint32 itemGuidLow; + uint32 itemTemplate; uint32 owner; - uint32 startbid; //maybe useless + uint32 startbid; // maybe useless uint32 bid; uint32 buyout; - time_t expire_time; + time_t expireTime; + time_t moneyDeliveryTime; uint32 bidder; - uint32 deposit; //deposit can be calculated only when creating auction + uint32 deposit; // deposit can be calculated only when creating auction AuctionHouseEntry const* auctionHouseEntry; // in AuctionHouse.dbc // helpers @@ -69,6 +80,10 @@ struct AuctionEntry bool BuildAuctionInfo(WorldPacket & data) const; void DeleteFromDB() const; void SaveToDB() const; + bool CompareAuctionEntry(uint32 column, const AuctionEntry *auc) const; + + bool IsDeleted() const { return m_deleted; }; + void SetDeleted() { m_deleted = true; }; }; //this class is used as auctionhouse instance @@ -84,7 +99,9 @@ class AuctionHouseObject typedef std::map AuctionEntryMap; - uint32 Getcount() { return AuctionsMap.size(); } + uint32 GetCount() { return AuctionsMap.size(); } + + AuctionEntryMap *GetAuctions() { return &AuctionsMap; } // Added by AHBot AuctionEntryMap::iterator GetAuctionsBegin() {return AuctionsMap.begin();} @@ -111,15 +128,22 @@ class AuctionHouseObject void BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount); void BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount); - void BuildListAuctionItems(WorldPacket& data, Player* player, - std::wstring const& searchedname, uint32 listfrom, uint32 levelmin, uint32 levelmax, uint32 usable, - uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, - uint32& count, uint32& totalcount); + void BuildListPendingSales(WorldPacket& data, Player* player, uint32& count); private: AuctionEntryMap AuctionsMap; }; +class AuctionSorter +{ + public: + AuctionSorter(uint8 *sort) : m_sort(sort) {} + bool operator()(const AuctionEntry *auc1, const AuctionEntry *auc2) const; + + private: + uint8* m_sort; +}; + class AuctionHouseMgr { public: diff --git a/src/game/BattleGroundMgr.cpp b/src/game/BattleGroundMgr.cpp index c11d80b68..b11fcd98e 100644 --- a/src/game/BattleGroundMgr.cpp +++ b/src/game/BattleGroundMgr.cpp @@ -44,7 +44,7 @@ #include "GameEventMgr.h" #include "Formulas.h" #include "Player.h" -#include "PlayerBot/PlayerbotClassAI.h" +#include "playerbot/PlayerbotClassAI.h" #include "Policies/SingletonImp.h" diff --git a/src/game/CMakeLists.txt b/src/game/CMakeLists.txt index c66c545bf..d3d2e1d1e 100644 --- a/src/game/CMakeLists.txt +++ b/src/game/CMakeLists.txt @@ -22,6 +22,7 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/vmap ${CMAKE_CURRENT_SOURCE_DIR}/AuctionHouseBot + ${CMAKE_CURRENT_SOURCE_DIR}/playerbot ${CMAKE_SOURCE_DIR}/dep/include/g3dlite ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour ${CMAKE_SOURCE_DIR}/dep/recastnavigation/ diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index 5331f561e..c6fadf939 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -38,7 +38,7 @@ #include "Util.h" #include "ArenaTeam.h" #include "Language.h" -#include "PlayerBot/PlayerbotMgr.h" +#include "playerbot/PlayerbotMgr.h" #include "SystemConfig.h" // config option SkipCinematics supported values @@ -137,21 +137,15 @@ class CharacterHandler } session->HandlePlayerLogin((LoginQueryHolder*)holder); } - // Playerbot mod: is different from the normal HandlePlayerLoginCallback in that it - // sets up the bot's world session and also stores the pointer to the bot player in the master's - // world session m_playerBots map void HandlePlayerBotLoginCallback(QueryResult * /*dummy*/, SqlQueryHolder * holder) { if (!holder) return; LoginQueryHolder* lqh = (LoginQueryHolder*) holder; - - // The bot's WorldSession is owned by the bot's Player object - // The bot's WorldSession is deleted by PlayerbotMgr::LogoutPlayerBot WorldSession *botSession = new WorldSession(lqh->GetAccountId(), NULL, SEC_ADMINISTRATOR, 3, 0, LOCALE_enUS); botSession->m_Address = "bot"; - botSession->HandlePlayerLogin(lqh); // will delete lqh + botSession->HandlePlayerLogin(lqh); } } chrHandler; @@ -222,6 +216,11 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) recv_data >> race_; recv_data >> class_; + // extract other data required for player creating + uint8 gender, skin, face, hairStyle, hairColor, facialHair, outfitId; + recv_data >> gender >> skin >> face; + recv_data >> hairStyle >> hairColor >> facialHair >> outfitId; + WorldPacket data(SMSG_CHAR_CREATE, 1); // returned with diff.values in all cases if(GetSecurity() == SEC_PLAYER) @@ -462,11 +461,6 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) return; } - // extract other data required for player creating - uint8 gender, skin, face, hairStyle, hairColor, facialHair, outfitId; - recv_data >> gender >> skin >> face; - recv_data >> hairStyle >> hairColor >> facialHair >> outfitId; - Player *pNewChar = new Player(this); if (!pNewChar->Create(sObjectMgr.GeneratePlayerLowGuid(), name, race_, class_, gender, skin, face, hairStyle, hairColor, facialHair, outfitId)) { @@ -710,6 +704,7 @@ void PlayerbotMgr::AddAllBots() } } } + void WorldSession::HandlePlayerLogin(LoginQueryHolder *holder) { ObjectGuid playerGuid = holder->GetGuid(); @@ -1455,7 +1450,7 @@ void WorldSession::HandleEquipmentSetUseOpcode(WorldPacket &recv_data) continue; ItemPosCountVec sDest; - uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, sDest, uItem, false ); + InventoryResult msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, sDest, uItem, false ); if(msg == EQUIP_ERR_OK) { _player->RemoveItem(INVENTORY_SLOT_BAG_0, i, true); @@ -1474,6 +1469,6 @@ void WorldSession::HandleEquipmentSetUseOpcode(WorldPacket &recv_data) } WorldPacket data(SMSG_USE_EQUIPMENT_SET_RESULT, 1); - data << uint8(0); // 4 - equipment swap failed - inventory is full + data << uint8(0); // 4 - equipment swap failed - inventory is full SendPacket(&data); } diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index c1a5c05b9..76652544a 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -215,6 +215,7 @@ ChatCommand * ChatHandler::getCommandTable() { "spellmods", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSpellModsCommand, "", NULL }, { "spellcheck", SEC_CONSOLE, true, &ChatHandler::HandleDebugSpellCheckCommand, "", NULL }, { "spellcoefs", SEC_ADMINISTRATOR, true, &ChatHandler::HandleDebugSpellCoefsCommand, "", NULL }, + { "spellmods", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSpellModsCommand, "", NULL }, { "uws", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugUpdateWorldStateCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 1a1ef76c0..a4a0031c6 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -189,7 +189,7 @@ Creature::~Creature() void Creature::AddToWorld() { ///- Register the creature for guid lookup - if(!IsInWorld() && GetObjectGuid().IsCreatureOrVehicle()) + if (!IsInWorld() && GetObjectGuid().IsCreatureOrVehicle()) GetMap()->GetObjectsStore().insert(GetGUID(), (Creature*)this); Unit::AddToWorld(); @@ -201,7 +201,7 @@ void Creature::AddToWorld() void Creature::RemoveFromWorld() { ///- Remove the creature from the accessor - if(IsInWorld() && GetObjectGuid().IsCreatureOrVehicle()) + if (IsInWorld() && GetObjectGuid().IsCreatureOrVehicle()) GetMap()->GetObjectsStore().erase(GetGUID(), (Creature*)NULL); Unit::RemoveFromWorld(); @@ -401,6 +401,8 @@ bool Creature::UpdateEntry(uint32 Entry, Team team, const CreatureData *data /*= for(int i = 0; i < CREATURE_MAX_SPELLS; ++i) m_spells[i] = GetCreatureInfo()->spells[i]; + SetVehicleId(GetCreatureInfo()->vehicleId); + // if eventData set then event active and need apply spell_start if (eventData) ApplyGameEventSpells(eventData, true); @@ -495,6 +497,9 @@ void Creature::Update(uint32 update_diff, uint32 diff) UpdateEntry(m_originalEntry, TEAM_NONE, NULL, eventData); } + if (GetDisplayId() != GetNativeDisplayId() ) + SetDisplayId(GetNativeDisplayId() ); + CreatureInfo const *cinfo = GetCreatureInfo(); SelectLevel(cinfo); @@ -545,8 +550,9 @@ void Creature::Update(uint32 update_diff, uint32 diff) else StopGroupLoot(); } + m_Events.Update(update_diff); + _UpdateSpells(update_diff); } - break; } case ALIVE: @@ -661,9 +667,9 @@ void Creature::Regenerate(Powers power) break; } case POWER_ENERGY: - if (GetObjectGuid().IsVehicle()) + if (IsVehicle()) { - if (VehicleEntry const* vehicleInfo = sVehicleStore.LookupEntry(GetCreatureInfo()->VehicleId)) + if (VehicleEntry const* vehicleInfo = sVehicleStore.LookupEntry(GetCreatureInfo()->vehicleId)) { switch (vehicleInfo->m_powerType) @@ -782,13 +788,6 @@ bool Creature::Create(uint32 guidlow, CreatureCreatePos& cPos, CreatureInfo cons SetMap(cPos.GetMap()); SetPhaseMask(cPos.GetPhaseMask(), false); - HighGuid hi = cinfo->VehicleId ? HIGHGUID_VEHICLE : HIGHGUID_UNIT; - - ObjectGuid guid(hi, cinfo->Entry, guidlow); - - if (cPos.GetMap()->GetCreature(guid)) - return false; - if (!CreateFromProto(guidlow, cinfo, team, data, eventData)) return false; @@ -1267,8 +1266,8 @@ bool Creature::CreateFromProto(uint32 guidlow, CreatureInfo const* cinfo, Team t return false; // Checked at startup - if (GetCreatureInfo()->VehicleId) - CreateVehicleKit(GetCreatureInfo()->VehicleId); + if (GetCreatureInfo()->vehicleId) + SetVehicleId(GetCreatureInfo()->vehicleId); return true; } diff --git a/src/game/Creature.h b/src/game/Creature.h index aa6cd17ac..235e991f7 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -122,7 +122,6 @@ struct CreatureInfo int32 resistance6; uint32 spells[CREATURE_MAX_SPELLS]; uint32 PetSpellDataId; - uint32 VehicleId; uint32 mingold; uint32 maxgold; char const* AIName; @@ -134,6 +133,7 @@ struct CreatureInfo uint32 questItems[6]; uint32 movementId; bool RegenHealth; + uint32 vehicleId; uint32 equipmentId; uint32 trainerId; uint32 vendorId; @@ -142,10 +142,9 @@ struct CreatureInfo uint32 ScriptID; // helpers - // completed: return HIGHGUID_UNIT/HIGHGUID_VEHICLE base at currently missing creature template data HighGuid GetHighGuid() const { - return VehicleId ? HIGHGUID_VEHICLE : HIGHGUID_UNIT; + return vehicleId ? HIGHGUID_VEHICLE : HIGHGUID_UNIT; } SkillType GetRequiredLootSkill() const diff --git a/src/game/DynamicObject.cpp b/src/game/DynamicObject.cpp index 5e235746f..ecbe56e0e 100644 --- a/src/game/DynamicObject.cpp +++ b/src/game/DynamicObject.cpp @@ -58,7 +58,7 @@ void DynamicObject::RemoveFromWorld() Object::RemoveFromWorld(); } -bool DynamicObject::Create( uint32 guidlow, Unit *caster, uint32 spellId, SpellEffectIndex effIndex, float x, float y, float z, int32 duration, float radius ) +bool DynamicObject::Create(uint32 guidlow, Unit *caster, uint32 spellId, SpellEffectIndex effIndex, float x, float y, float z, int32 duration, float radius, DynamicObjectType type) { WorldObject::_Create(guidlow, HIGHGUID_DYNAMICOBJECT, caster->GetPhaseMask()); SetMap(caster->GetMap()); @@ -86,7 +86,7 @@ bool DynamicObject::Create( uint32 guidlow, Unit *caster, uint32 spellId, SpellE bytes |= 0x00 << 16; bytes |= 0x00 << 24; */ - SetUInt32Value(DYNAMICOBJECT_BYTES, 0x00000001); + SetByteValue(DYNAMICOBJECT_BYTES, 0, type); SetUInt32Value(DYNAMICOBJECT_SPELLID, spellId); SetFloatValue(DYNAMICOBJECT_RADIUS, radius); diff --git a/src/game/DynamicObject.h b/src/game/DynamicObject.h index 292354379..e9b127ac4 100644 --- a/src/game/DynamicObject.h +++ b/src/game/DynamicObject.h @@ -23,6 +23,13 @@ #include "DBCEnums.h" #include "Unit.h" +enum DynamicObjectType +{ + DYNAMIC_OBJECT_PORTAL = 0x0, // unused + DYNAMIC_OBJECT_AREA_SPELL = 0x1, + DYNAMIC_OBJECT_FARSIGHT_FOCUS = 0x2, +}; + struct SpellEntry; class DynamicObject : public WorldObject @@ -34,7 +41,7 @@ class DynamicObject : public WorldObject void AddToWorld(); void RemoveFromWorld(); - bool Create(uint32 guidlow, Unit *caster, uint32 spellId, SpellEffectIndex effIndex, float x, float y, float z, int32 duration, float radius); + bool Create(uint32 guidlow, Unit *caster, uint32 spellId, SpellEffectIndex effIndex, float x, float y, float z, int32 duration, float radius, DynamicObjectType type); void Update(uint32 update_diff, uint32 p_time); void Delete(); uint32 GetSpellId() const { return m_spellId; } @@ -43,6 +50,7 @@ class DynamicObject : public WorldObject ObjectGuid const& GetCasterGuid() const { return GetGuidValue(DYNAMICOBJECT_CASTER); } Unit* GetCaster() const; float GetRadius() const { return m_radius; } + DynamicObjectType GetType() const { return (DynamicObjectType)GetByteValue(DYNAMICOBJECT_BYTES,0); } bool IsAffecting(Unit *unit) const { return m_affected.find(unit->GetObjectGuid()) != m_affected.end(); } void AddAffected(Unit *unit) { m_affected.insert(unit->GetObjectGuid()); } void RemoveAffected(Unit *unit) { m_affected.erase(unit->GetObjectGuid()); } diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp index d60550a67..b0eb7880f 100644 --- a/src/game/GameObject.cpp +++ b/src/game/GameObject.cpp @@ -39,7 +39,9 @@ #include "Util.h" #include "ScriptMgr.h" -GameObject::GameObject() : WorldObject() +GameObject::GameObject() : WorldObject(), + m_goInfo(NULL), + m_displayInfo(NULL) { m_objectType |= TYPEMASK_GAMEOBJECT; m_objectTypeId = TYPEID_GAMEOBJECT; @@ -54,7 +56,6 @@ GameObject::GameObject() : WorldObject() m_useTimes = 0; m_spellId = 0; m_cooldownTime = 0; - m_goInfo = NULL; m_rotation = 0; @@ -142,8 +143,7 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMa SetFlag(GAMEOBJECT_FLAGS, (GO_FLAG_TRANSPORT | GO_FLAG_NODESPAWN)); SetEntry(goinfo->id); - - SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId); + SetDisplayId(goinfo->displayId); // GAMEOBJECT_BYTES_1, index at 0, 1, 2 and 3 SetGoState(go_state); @@ -1717,7 +1717,7 @@ void GameObject::DamageTaken(Unit* pDoneBy, uint32 damage) { RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED); SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED); - SetUInt32Value(GAMEOBJECT_DISPLAYID, m_goInfo->destructibleBuilding.destroyedDisplayId); + SetDisplayId(m_goInfo->destructibleBuilding.destroyedDisplayId); GetMap()->ScriptsStart(sEventScripts, m_goInfo->destructibleBuilding.destroyedEvent, pDoneBy, this); } } @@ -1726,7 +1726,7 @@ void GameObject::DamageTaken(Unit* pDoneBy, uint32 damage) if (m_health <= m_goInfo->destructibleBuilding.damagedNumHits) { SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED); - SetUInt32Value(GAMEOBJECT_DISPLAYID, m_goInfo->destructibleBuilding.damagedDisplayId); + SetDisplayId(m_goInfo->destructibleBuilding.damagedDisplayId); GetMap()->ScriptsStart(sEventScripts, m_goInfo->destructibleBuilding.damageEvent, pDoneBy, this); // if we have a "dead" display we can "kill" the building after its damaged if (m_goInfo->destructibleBuilding.destroyedDisplayId) @@ -1749,7 +1749,7 @@ void GameObject::Rebuild(Unit* pWho) return; RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED | GO_FLAG_DESTROYED); - SetUInt32Value(GAMEOBJECT_DISPLAYID, m_goInfo->displayId); + SetDisplayId(m_goInfo->displayId); m_health = GetMaxHealth(); GetMap()->ScriptsStart(sEventScripts, m_goInfo->destructibleBuilding.rebuildingEvent, pWho, this); @@ -1888,13 +1888,19 @@ bool GameObject::IsFriendlyTo(Unit const* unit) const return tester_faction->IsFriendlyTo(*target_faction); } +void GameObject::SetDisplayId(uint32 modelId) +{ + SetUInt32Value(GAMEOBJECT_DISPLAYID, modelId); + m_displayInfo = sGameObjectDisplayInfoStore.LookupEntry(modelId); +} + float GameObject::GetObjectBoundingRadius() const { //FIXME: // 1. This is clearly hack way because GameObjectDisplayInfoEntry have 6 floats related to GO sizes, but better that use DEFAULT_WORLD_OBJECT_SIZE // 2. In some cases this must be only interactive size, not GO size, current way can affect creature target point auto-selection in strange ways for big underground/virtual GOs - if (GameObjectDisplayInfoEntry const* dispEntry = sGameObjectDisplayInfoStore.LookupEntry(GetGOInfo()->displayId)) - return fabs(dispEntry->minX) * GetObjectScale(); + if (m_displayInfo) + return fabs(m_displayInfo->minX) * GetObjectScale(); return DEFAULT_WORLD_OBJECT_SIZE; } diff --git a/src/game/GameObject.h b/src/game/GameObject.h index 5d5ea59c4..715e5ffbd 100644 --- a/src/game/GameObject.h +++ b/src/game/GameObject.h @@ -582,6 +582,7 @@ enum LootState }; class Unit; +struct GameObjectDisplayInfoEntry; // 5 sec for bobber catch #define FISHING_BOBBER_READY_TIME 5 @@ -673,6 +674,8 @@ class MANGOS_DLL_SPEC GameObject : public WorldObject void SetGoArtKit(uint8 artkit) { SetByteValue(GAMEOBJECT_BYTES_1, 2, artkit); } uint8 GetGoAnimProgress() const { return GetByteValue(GAMEOBJECT_BYTES_1, 3); } void SetGoAnimProgress(uint8 animprogress) { SetByteValue(GAMEOBJECT_BYTES_1, 3, animprogress); } + uint32 GetDisplayId() const { return GetUInt32Value(GAMEOBJECT_DISPLAYID); } + void SetDisplayId(uint32 modelId); float GetObjectBoundingRadius() const; // overwrite WorldObject version @@ -751,6 +754,7 @@ class MANGOS_DLL_SPEC GameObject : public WorldObject GuidsSet m_UniqueUsers; // all players who use item, some items activated after specific amount unique uses GameObjectInfo const* m_goInfo; + GameObjectDisplayInfoEntry const* m_displayInfo; uint64 m_rotation; private: void SwitchDoorOrButton(bool activate, bool alternative = false); diff --git a/src/game/GridMap.cpp b/src/game/GridMap.cpp index cf20024f1..e8d7b06ad 100644 --- a/src/game/GridMap.cpp +++ b/src/game/GridMap.cpp @@ -812,9 +812,13 @@ float TerrainInfo::GetHeight(float x, float y, float z, bool pUseVmaps, float ma bool TerrainInfo::IsNextZcoordOK(float x, float y, float oldZ, float maxDiff) const { + if (fabs(oldZ) + fabs(maxDiff) >= INVALID_HEIGHT) + return false; + // The fastest way to get an accurate result 90% of the time. // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long. maxDiff = maxDiff >= 100.0f ? 10.0f : sqrtf(maxDiff); + bool useVmaps = false; if (GetHeight(x, y, oldZ, false) < GetHeight(x, y, oldZ, true)) // check use of vmaps useVmaps = true; diff --git a/src/game/Group.cpp b/src/game/Group.cpp index dc1bb69a9..ebbe2db05 100644 --- a/src/game/Group.cpp +++ b/src/game/Group.cpp @@ -860,7 +860,7 @@ void Group::CountTheRoll(Rolls::iterator& rollI) ItemPosCountVec dest; LootItem *item = &(roll->getLoot()->items[roll->itemSlot]); - uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count ); + InventoryResult msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count ); if ( msg == EQUIP_ERR_OK ) { item->is_looted = true; @@ -916,7 +916,7 @@ void Group::CountTheRoll(Rolls::iterator& rollI) if(rollvote == ROLL_GREED) { ItemPosCountVec dest; - uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count ); + InventoryResult msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count ); if ( msg == EQUIP_ERR_OK ) { item->is_looted = true; diff --git a/src/game/GroupHandler.cpp b/src/game/GroupHandler.cpp index fe0b80f2a..faead768a 100644 --- a/src/game/GroupHandler.cpp +++ b/src/game/GroupHandler.cpp @@ -174,7 +174,8 @@ void WorldSession::HandleGroupInviteOpcode( WorldPacket & recv_data ) void WorldSession::HandleGroupAcceptOpcode( WorldPacket & recv_data ) { - //recv_data.read_skip(); // roles mask? + if (!GetPlayer()->GetPlayerbotAI()) + recv_data.read_skip(); // roles mask? Group *group = GetPlayer()->GetGroupInvite(); if (!group) diff --git a/src/game/Guild.cpp b/src/game/Guild.cpp index a6b544d29..7496fa00a 100644 --- a/src/game/Guild.cpp +++ b/src/game/Guild.cpp @@ -1737,7 +1737,7 @@ void Guild::RemoveItem(uint8 tab, uint8 slot ) GetId(), uint32(tab), uint32(slot)); } -uint8 Guild::_CanStoreItem_InSpecificSlot( uint8 tab, uint8 slot, GuildItemPosCountVec &dest, uint32& count, bool swap, Item* pSrcItem ) const +InventoryResult Guild::_CanStoreItem_InSpecificSlot( uint8 tab, uint8 slot, GuildItemPosCountVec &dest, uint32& count, bool swap, Item* pSrcItem ) const { Item* pItem2 = m_TabListMap[tab]->Slots[slot]; @@ -1780,7 +1780,7 @@ uint8 Guild::_CanStoreItem_InSpecificSlot( uint8 tab, uint8 slot, GuildItemPosCo return EQUIP_ERR_OK; } -uint8 Guild::_CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec &dest, uint32& count, bool merge, Item* pSrcItem, uint8 skip_slot ) const +InventoryResult Guild::_CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec &dest, uint32& count, bool merge, Item* pSrcItem, uint8 skip_slot ) const { for (uint32 j = 0; j < GUILD_BANK_MAX_SLOTS; ++j) { @@ -1837,7 +1837,7 @@ uint8 Guild::_CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec &dest, uint32& return EQUIP_ERR_OK; } -uint8 Guild::CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec &dest, uint32 count, Item *pItem, bool swap ) const +InventoryResult Guild::CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec &dest, uint32 count, Item *pItem, bool swap ) const { DEBUG_LOG( "GUILD STORAGE: CanStoreItem tab = %u, slot = %u, item = %u, count = %u", tab, slot, pItem->GetEntry(), count); @@ -1850,7 +1850,7 @@ uint8 Guild::CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec &dest, ui // in specific slot if (slot != NULL_SLOT) { - uint8 res = _CanStoreItem_InSpecificSlot(tab,slot,dest,count,swap,pItem); + InventoryResult res = _CanStoreItem_InSpecificSlot(tab,slot,dest,count,swap,pItem); if (res != EQUIP_ERR_OK) return res; @@ -1863,7 +1863,7 @@ uint8 Guild::CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec &dest, ui // search stack in tab for merge to if (pItem->GetMaxStackCount() > 1) { - uint8 res = _CanStoreItem_InTab(tab, dest, count, true, pItem, slot); + InventoryResult res = _CanStoreItem_InTab(tab, dest, count, true, pItem, slot); if (res != EQUIP_ERR_OK) return res; @@ -1872,7 +1872,7 @@ uint8 Guild::CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec &dest, ui } // search free slot in bag for place to - uint8 res = _CanStoreItem_InTab(tab, dest, count, false, pItem, slot); + InventoryResult res = _CanStoreItem_InTab(tab, dest, count, false, pItem, slot); if (res != EQUIP_ERR_OK) return res; @@ -1950,7 +1950,7 @@ void Guild::SwapItems(Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 BankT if (SplitedAmount) { // Bank -> Bank item split (in empty or non empty slot GuildItemPosCountVec dest; - uint8 msg = CanStoreItem(BankTabDst, BankTabSlotDst, dest, SplitedAmount, pItemSrc, false); + InventoryResult msg = CanStoreItem(BankTabDst, BankTabSlotDst, dest, SplitedAmount, pItemSrc, false); if (msg != EQUIP_ERR_OK) { pl->SendEquipError( msg, pItemSrc, NULL ); @@ -1977,7 +1977,7 @@ void Guild::SwapItems(Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 BankT else // non split { GuildItemPosCountVec gDest; - uint8 msg = CanStoreItem(BankTabDst,BankTabSlotDst,gDest,pItemSrc->GetCount(), pItemSrc, false); + InventoryResult msg = CanStoreItem(BankTabDst,BankTabSlotDst,gDest,pItemSrc->GetCount(), pItemSrc, false); if (msg == EQUIP_ERR_OK) // merge to { CharacterDatabase.BeginTransaction(); @@ -2057,7 +2057,7 @@ void Guild::MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, u } ItemPosCountVec dest; - uint8 msg = pl->CanStoreItem(PlayerBag, PlayerSlot, dest, pNewItem, false); + InventoryResult msg = pl->CanStoreItem(PlayerBag, PlayerSlot, dest, pNewItem, false); if (msg != EQUIP_ERR_OK) { pl->SendEquipError( msg, pNewItem, NULL ); @@ -2088,7 +2088,7 @@ void Guild::MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, u else // Bank -> Char swap with slot (move) { ItemPosCountVec dest; - uint8 msg = pl->CanStoreItem(PlayerBag, PlayerSlot, dest, pItemBank, false); + InventoryResult msg = pl->CanStoreItem(PlayerBag, PlayerSlot, dest, pItemBank, false); if (msg == EQUIP_ERR_OK) // merge case { // check source pos rights (item moved to inventory) @@ -2208,7 +2208,7 @@ void Guild::MoveFromCharToBank( Player * pl, uint8 PlayerBag, uint8 PlayerSlot, if (SplitedAmount) { // Char -> Bank split to empty or non-empty slot (partly move) GuildItemPosCountVec dest; - uint8 msg = CanStoreItem(BankTab, BankTabSlot, dest, SplitedAmount, pItemChar, false); + InventoryResult msg = CanStoreItem(BankTab, BankTabSlot, dest, SplitedAmount, pItemChar, false); if (msg != EQUIP_ERR_OK) { pl->SendEquipError( msg, pItemChar, NULL ); @@ -2245,7 +2245,7 @@ void Guild::MoveFromCharToBank( Player * pl, uint8 PlayerBag, uint8 PlayerSlot, else // Char -> Bank swap with empty or non-empty (move) { GuildItemPosCountVec dest; - uint8 msg = CanStoreItem(BankTab, BankTabSlot, dest, pItemChar->GetCount(), pItemChar, false); + InventoryResult msg = CanStoreItem(BankTab, BankTabSlot, dest, pItemChar->GetCount(), pItemChar, false); if (msg == EQUIP_ERR_OK) // merge { // logging item move to bank diff --git a/src/game/Guild.h b/src/game/Guild.h index 531902832..e9811e2cb 100644 --- a/src/game/Guild.h +++ b/src/game/Guild.h @@ -485,7 +485,7 @@ class Guild // used only from high level Swap/Move functions Item* GetItem(uint8 TabId, uint8 SlotId); - uint8 CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32 count, Item *pItem, bool swap = false) const; + InventoryResult CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32 count, Item *pItem, bool swap = false) const; Item* StoreItem( uint8 tab, GuildItemPosCountVec const& pos, Item *pItem ); void RemoveItem(uint8 tab, uint8 slot ); void DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2 = -1); @@ -493,8 +493,8 @@ class Guild // internal common parts for CanStore/StoreItem functions void AppendDisplayGuildBankSlot( WorldPacket& data, GuildBankTab const *tab, int32 slot ); - uint8 _CanStoreItem_InSpecificSlot( uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32& count, bool swap, Item *pSrcItem ) const; - uint8 _CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec& dest, uint32& count, bool merge, Item *pSrcItem, uint8 skip_slot ) const; + InventoryResult _CanStoreItem_InSpecificSlot( uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32& count, bool swap, Item *pSrcItem ) const; + InventoryResult _CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec& dest, uint32& count, bool merge, Item *pSrcItem, uint8 skip_slot ) const; Item* _StoreItem( uint8 tab, uint8 slot, Item *pItem, uint32 count, bool clone ); }; #endif diff --git a/src/game/Item.cpp b/src/game/Item.cpp index be8db97dd..7a4b139fd 100644 --- a/src/game/Item.cpp +++ b/src/game/Item.cpp @@ -1248,7 +1248,7 @@ void Item::BuildUpdateData(UpdateDataMapType& update_players) ClearUpdateMask(false); } -uint8 Item::CanBeMergedPartlyWith( ItemPrototype const* proto ) const +InventoryResult Item::CanBeMergedPartlyWith( ItemPrototype const* proto ) const { // check item type if (GetEntry() != proto->ItemId) diff --git a/src/game/Item.h b/src/game/Item.h index 3f3bfcec1..e0c43158d 100644 --- a/src/game/Item.h +++ b/src/game/Item.h @@ -38,7 +38,7 @@ struct ItemSetEffect SpellEntry const *spells[8]; }; -enum InventoryChangeFailure +enum InventoryResult { EQUIP_ERR_OK = 0, EQUIP_ERR_CANT_EQUIP_LEVEL_I = 1, // ERR_CANT_EQUIP_LEVEL_I @@ -81,7 +81,7 @@ enum InventoryChangeFailure EQUIP_ERR_YOU_ARE_DEAD = 38, // ERR_PLAYER_DEAD EQUIP_ERR_CANT_DO_RIGHT_NOW = 39, // ERR_CLIENT_LOCKED_OUT EQUIP_ERR_INT_BAG_ERROR = 40, // ERR_INTERNAL_BAG_ERROR - EQUIP_ERR_CAN_EQUIP_ONLY1_QUIVER2 = 41, // ERR_ONLY_ONE_BOLT + EQUIP_ERR_CAN_EQUIP_ONLY1_BOLT = 41, // ERR_ONLY_ONE_BOLT EQUIP_ERR_CAN_EQUIP_ONLY1_AMMOPOUCH = 42, // ERR_ONLY_ONE_AMMO EQUIP_ERR_STACKABLE_CANT_BE_WRAPPED = 43, // ERR_CANT_WRAP_STACKABLE EQUIP_ERR_EQUIPPED_CANT_BE_WRAPPED = 44, // ERR_CANT_WRAP_EQUIPPED @@ -132,7 +132,7 @@ enum InventoryChangeFailure EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS = 89 }; -enum BuyFailure +enum BuyResult { BUY_ERR_CANT_FIND_ITEM = 0, BUY_ERR_ITEM_ALREADY_SOLD = 1, @@ -145,7 +145,7 @@ enum BuyFailure BUY_ERR_REPUTATION_REQUIRE = 12 }; -enum SellFailure +enum SellResult { SELL_ERR_CANT_FIND_ITEM = 1, SELL_ERR_CANT_SELL_ITEM = 2, // merchant doesn't like that item @@ -315,7 +315,7 @@ class MANGOS_DLL_SPEC Item : public Object uint32 GetMaxStackCount() const { return GetProto()->GetMaxStackSize(); } uint8 GetGemCountWithID(uint32 GemID) const; uint8 GetGemCountWithLimitCategory(uint32 limitCategory) const; - uint8 CanBeMergedPartlyWith(ItemPrototype const* proto) const; + InventoryResult CanBeMergedPartlyWith(ItemPrototype const* proto) const; uint8 GetSlot() const {return m_slot;} Bag *GetContainer() { return m_container; } diff --git a/src/game/ItemHandler.cpp b/src/game/ItemHandler.cpp index 2c64bbb46..85a085855 100644 --- a/src/game/ItemHandler.cpp +++ b/src/game/ItemHandler.cpp @@ -152,7 +152,7 @@ void WorldSession::HandleAutoEquipItemOpcode( WorldPacket & recv_data ) return; // only at cheat uint16 dest; - uint8 msg = _player->CanEquipItem( NULL_SLOT, dest, pSrcItem, !pSrcItem->IsBag() ); + InventoryResult msg = _player->CanEquipItem( NULL_SLOT, dest, pSrcItem, !pSrcItem->IsBag() ); if( msg != EQUIP_ERR_OK ) { _player->SendEquipError( msg, pSrcItem, NULL ); @@ -246,7 +246,7 @@ void WorldSession::HandleDestroyItemOpcode( WorldPacket & recv_data ) // prevent drop unequipable items (in combat, for example) and non-empty bags if(_player->IsEquipmentPos(pos) || _player->IsBagPos(pos)) { - uint8 msg = _player->CanUnequipItem( pos, false ); + InventoryResult msg = _player->CanUnequipItem( pos, false ); if( msg != EQUIP_ERR_OK ) { _player->SendEquipError( msg, _player->GetItemByPos(pos), NULL ); @@ -454,7 +454,7 @@ void WorldSession::HandleReadItemOpcode( WorldPacket & recv_data ) { WorldPacket data; - uint8 msg = _player->CanUseItem( pItem ); + InventoryResult msg = _player->CanUseItem( pItem ); if( msg == EQUIP_ERR_OK ) { data.Initialize (SMSG_READ_ITEM_OK, 8); @@ -629,7 +629,7 @@ void WorldSession::HandleBuybackItem(WorldPacket & recv_data) } ItemPosCountVec dest; - uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); + InventoryResult msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); if( msg == EQUIP_ERR_OK ) { _player->ModifyMoney( -(int32)price ); @@ -858,7 +858,7 @@ void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket & recv_data ) // check unequip potability for equipped items and bank bags if(_player->IsEquipmentPos ( src ) || _player->IsBagPos ( src )) { - uint8 msg = _player->CanUnequipItem( src, !_player->IsBagPos ( src )); + InventoryResult msg = _player->CanUnequipItem( src, !_player->IsBagPos ( src )); if(msg != EQUIP_ERR_OK) { _player->SendEquipError( msg, pItem, NULL ); @@ -867,7 +867,7 @@ void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket & recv_data ) } ItemPosCountVec dest; - uint8 msg = _player->CanStoreItem( dstbag, NULL_SLOT, dest, pItem, false ); + InventoryResult msg = _player->CanStoreItem( dstbag, NULL_SLOT, dest, pItem, false ); if( msg != EQUIP_ERR_OK ) { _player->SendEquipError( msg, pItem, NULL ); @@ -971,7 +971,7 @@ void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket) return; ItemPosCountVec dest; - uint8 msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); + InventoryResult msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); if( msg != EQUIP_ERR_OK ) { _player->SendEquipError( msg, pItem, NULL ); @@ -1005,7 +1005,7 @@ void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket) if(_player->IsBankPos(srcbag, srcslot)) // moving from bank to inventory { ItemPosCountVec dest; - uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); + InventoryResult msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); if( msg != EQUIP_ERR_OK ) { _player->SendEquipError( msg, pItem, NULL ); @@ -1018,7 +1018,7 @@ void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket) else // moving from inventory to bank { ItemPosCountVec dest; - uint8 msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); + InventoryResult msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); if( msg != EQUIP_ERR_OK ) { _player->SendEquipError( msg, pItem, NULL ); @@ -1378,7 +1378,7 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recv_data) // for equipped item check all equipment for duplicate equipped gems if(itemTarget->IsEquipped()) { - if(uint8 res = _player->CanEquipUniqueItem(Gems[i], slot, limit_newcount >= 0 ? limit_newcount : 0)) + if(InventoryResult res = _player->CanEquipUniqueItem(Gems[i], slot, limit_newcount >= 0 ? limit_newcount : 0)) { _player->SendEquipError( res, itemTarget, NULL ); return; diff --git a/src/game/LFG.cpp b/src/game/LFG.cpp index aa16a34e0..5b2dca7e7 100644 --- a/src/game/LFG.cpp +++ b/src/game/LFG.cpp @@ -40,6 +40,7 @@ void LFGPlayerState::Clear() m_DungeonsList.clear(); m_LockMap.clear(); m_comment.clear(); + accept = LFG_ANSWER_PENDING; } LFGLockStatusMap* LFGPlayerState::GetLockMap() @@ -108,6 +109,7 @@ void LFGGroupState::Clear() LFG_MEMBER_FLAG_COMMENT | LFG_MEMBER_FLAG_ROLES | LFG_MEMBER_FLAG_BIND; + m_proposal = NULL; } uint8 LFGGroupState::GetRoles(LFGRoles role) diff --git a/src/game/LFG.h b/src/game/LFG.h index a0bfd2bf4..ed40a48b0 100644 --- a/src/game/LFG.h +++ b/src/game/LFG.h @@ -194,14 +194,15 @@ enum LFGJoinResult LFG_JOIN_FAILED2 = 18, // RoleCheck Failed }; -enum LFGRoleCheckResult +enum LFGRoleCheckState { - LFG_ROLECHECK_FINISHED = 1, // Role check finished - LFG_ROLECHECK_INITIALITING = 2, // Role check begins - LFG_ROLECHECK_MISSING_ROLE = 3, // Someone didn't selected a role after 2 mins - LFG_ROLECHECK_WRONG_ROLES = 4, // Can't form a group with that role selection - LFG_ROLECHECK_ABORTED = 5, // Someone leave the group - LFG_ROLECHECK_NO_ROLE = 6, // Someone selected no role + LFG_ROLECHECK_NONE = 0, // Internal use = Not initialized. + LFG_ROLECHECK_FINISHED = 1, // Role check finished + LFG_ROLECHECK_INITIALITING = 2, // Role check begins + LFG_ROLECHECK_MISSING_ROLE = 3, // Someone didn't selected a role after 2 mins + LFG_ROLECHECK_WRONG_ROLES = 4, // Can't form a group with that role selection + LFG_ROLECHECK_ABORTED = 5, // Someone leave the group + LFG_ROLECHECK_NO_ROLE = 6 // Someone selected no role }; enum LFGAnswer @@ -248,6 +249,9 @@ struct LFGPlayerState void AddFlags(uint32 flags) { m_flags = m_flags | flags;}; void RemoveFlags(uint32 flags) { m_flags = m_flags & ~flags;}; + void SetAnswer(LFGAnswer _accept) { accept = _accept;}; + LFGAnswer GetAnswer() { return accept;}; + LFGType GetType(); private: @@ -259,8 +263,11 @@ struct LFGPlayerState LFGDungeonSet m_DungeonsList; // Dungeons the player have applied for LFGLockStatusMap m_LockMap; // Dungeons lock map std::string m_comment; + LFGAnswer accept; ///< Accept status (-1 not answer | 0 Not agree | 1 agree) }; +struct LFGProposal; + struct LFGGroupState { LFGGroupState(Group* group) : m_group(group) @@ -272,6 +279,9 @@ struct LFGGroupState void Update(bool _update = true) { update = _update; }; LFGDungeonSet* GetDungeons() { return &m_DungeonsList; }; + LFGProposal* GetProposal() { return m_proposal; }; + void SetProposal(LFGProposal* proposal) { m_proposal = proposal; }; + uint32* GetFlags() { return &m_flags;}; LFGType GetType(); uint8 GetRoles(LFGRoles role); @@ -285,6 +295,7 @@ struct LFGGroupState bool kickActive; LFGDungeonStatus status; LFGDungeonSet m_DungeonsList; // Dungeons the group have applied for + LFGProposal* m_proposal; }; #endif diff --git a/src/game/LFGHandler.cpp b/src/game/LFGHandler.cpp index 5284fa7b4..f4be804ad 100644 --- a/src/game/LFGHandler.cpp +++ b/src/game/LFGHandler.cpp @@ -94,6 +94,17 @@ void WorldSession::HandleLfgLeaveOpcode( WorldPacket & /*recv_data*/ ) } +void WorldSession::HandleLfgGetStatus(WorldPacket & /*recv_data*/) +{ + Group* group = GetPlayer()->GetGroup(); + + DEBUG_LOG("CMSG_LFG_GET_STATUS %u in group: %u", GetPlayer()->GetObjectGuid().GetCounter(), group ? 1 : 0); + + // for GetPlayer()->GetLFGState()->GetDungeons() + // LFGQueueStatus = sLFGMgr.GetStatus(dungeon); + // SendLfgQueueStatus(LFGQueueStatus* status); +} + void WorldSession::HandleLfrSearchOpcode( WorldPacket & recv_data ) { uint32 entry; // Raid id to search @@ -533,7 +544,7 @@ void WorldSession::SendLfgUpdatePlayer(LFGUpdateType updateType, LFGType type) } -void WorldSession::SendLfgUpdateList(uint32 dungeonEntry) +void WorldSession::SendLfgUpdateList(uint32 dungeonID) { if (!sWorld.getConfig(CONFIG_BOOL_LFR_ENABLE)) { @@ -541,11 +552,11 @@ void WorldSession::SendLfgUpdateList(uint32 dungeonEntry) return; } - DEBUG_LOG("SMSG_LFG_SEARCH_RESULTS %u dungeonentry: %u ", GetPlayer()->GetObjectGuid().GetCounter(), dungeonEntry); + DEBUG_LOG("SMSG_LFG_SEARCH_RESULTS %u dungeonentry: %u ", GetPlayer()->GetObjectGuid().GetCounter(), dungeonID); - LFGDungeonEntry const* dungeon = sLFGMgr.GetDungeon(dungeonEntry); + LFGDungeonEntry const* dungeon = sLFGMgr.GetDungeon(dungeonID); - if (!dungeonEntry) + if (!dungeon) return; Team team = sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_GROUP) ? TEAM_NONE : GetPlayer()->GetTeam(); @@ -742,27 +753,27 @@ void WorldSession::SendLfgUpdateList(uint32 dungeonEntry) data << uint8(player->getRace()); for(int i = 0; i < 3; ++i) - data << uint8(player->GetTalentsCount(i)); // spent talents count in specific tab + data << uint8(player->GetTalentsCount(i)); // spent talents count in specific tab data << uint32(player->GetArmor()); // armor data << uint32(player->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_SPELL)); // spd data << uint32(player->SpellBaseHealingBonusDone(SPELL_SCHOOL_MASK_SPELL)); // heal - data << uint32(player->GetRatingBonusValue(CR_HASTE_MELEE)); // haste rating melee - data << uint32(player->GetRatingBonusValue(CR_HASTE_RANGED)); // haste rating ranged - data << uint32(player->GetRatingBonusValue(CR_HASTE_SPELL)); // haste rating spell - data << float(player->GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER)); // mp5 - data << float(player->GetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER)); // unk + data << uint32(player->GetRatingBonusValue(CR_CRIT_MELEE)); // crit rating melee + data << uint32(player->GetRatingBonusValue(CR_CRIT_RANGED)); // crit rating ranged + data << uint32(player->GetRatingBonusValue(CR_CRIT_SPELL)); // crit rating spell + data << float(player->GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER)*5); // mp5 + data << float(player->GetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER)*5); // unk data << uint32(player->GetTotalAttackPowerValue(BASE_ATTACK)); // attack power data << uint32(player->GetTotalStatValue(STAT_AGILITY)); // agility data << uint32(player->GetMaxHealth()); // health data << uint32(player->GetMaxPower(player->getPowerType())); // power - data << uint32(0); // unk - data << float(0); // unk + data << uint32(player->GetFreeTalentPoints()); // free talent points (TOM_RUS) + data << float(0); // unk data << uint32(player->GetRatingBonusValue(CR_DEFENSE_SKILL)); // defence rating data << uint32(player->GetRatingBonusValue(CR_DODGE)); // dodge rating data << uint32(player->GetRatingBonusValue(CR_BLOCK)); // block rating data << uint32(player->GetRatingBonusValue(CR_PARRY)); // parry rating - data << uint32(player->GetRatingBonusValue(CR_CRIT_MELEE)); // crit rating + data << uint32(player->GetRatingBonusValue(CR_HASTE_MELEE)); // crit rating data << uint32(player->GetRatingBonusValue(CR_EXPERTISE)); // expertize } @@ -838,19 +849,24 @@ void WorldSession::SendLfgDisabled() SendPacket(&data); } -void WorldSession::SendLfgOfferContinue(uint32 dungeonEntry) +void WorldSession::SendLfgOfferContinue(uint32 dungeonID) { - DEBUG_LOG("SMSG_LFG_OFFER_CONTINUE %u dungeon entry: %u", GetPlayer()->GetObjectGuid().GetCounter(), dungeonEntry); + LFGDungeonEntry const* dungeon = sLFGMgr.GetDungeon(dungeonID); + + if (!dungeon) + return; + + DEBUG_LOG("SMSG_LFG_OFFER_CONTINUE %u dungeon entry: %u", GetPlayer()->GetObjectGuid().GetCounter(), dungeonID); WorldPacket data(SMSG_LFG_OFFER_CONTINUE, 4); - data << uint32(dungeonEntry); + data << uint32(dungeon->Entry()); SendPacket(&data); } -void WorldSession::SendLfgTeleportError(uint8 err) +void WorldSession::SendLfgTeleportError(LFGTeleportError msg) { - DEBUG_LOG("SMSG_LFG_TELEPORT_DENIED %u reason: %u", GetPlayer()->GetObjectGuid().GetCounter(), err); + DEBUG_LOG("SMSG_LFG_TELEPORT_DENIED %u reason: %u", GetPlayer()->GetObjectGuid().GetCounter(), msg); WorldPacket data(SMSG_LFG_TELEPORT_DENIED, 4); - data << uint32(err); + data << uint32(msg); SendPacket(&data); } @@ -891,3 +907,148 @@ void WorldSession::SendLfgPlayerReward(LFGDungeonEntry const* dungeon, const LFG } SendPacket(&data); } + +void WorldSession::SendLfgQueueStatus(LFGQueueStatus* status) +{ + DEBUG_LOG("SMSG_LFG_QUEUE_STATUS %u dungeonEntry: %u ", GetPlayer()->GetObjectGuid().GetCounter(), status->dungeon->ID); + + WorldPacket data(SMSG_LFG_QUEUE_STATUS, 4 + 4 + 4 + 4 + 4 +4 + 1 + 1 + 1 + 4); + data << uint32(status->dungeon->Entry()); // Dungeon + data << int32(status->avgWaitTime); // Average Wait time + data << int32(status->waitTime); // Wait Time + data << int32(status->waitTimeTanks); // Wait Tanks + data << int32(status->waitTimeHealer); // Wait Healers + data << int32(status->waitTimeDps); // Wait Dps + data << uint8(status->tanks); // Tanks needed + data << uint8(status->healers); // Healers needed + data << uint8(status->dps); // Dps needed + data << uint32(status->queuedTime); // Player wait time in queue + SendPacket(&data); +} + +void WorldSession::SendLfgRoleChosen(ObjectGuid guid, uint8 roles) +{ + DEBUG_LOG("SMSG_ROLE_CHOSEN %u guid: %u roles: %u", GetPlayer()->GetObjectGuid().GetCounter(), guid.GetCounter(), roles); + + WorldPacket data(SMSG_ROLE_CHOSEN, 8 + 1 + 4); + data << guid; // Guid + data << uint8(roles != LFG_ROLE_MASK_NONE); // Ready + data << uint32(roles); // Roles + SendPacket(&data); +} + +void WorldSession::SendLfgBootPlayer(LFGPlayerBoot* pBoot) +{ + ObjectGuid guid = GetPlayer()->GetObjectGuid(); + LFGAnswer playerVote = pBoot->votes.find(guid)->second; + uint8 votesNum = 0; + uint8 agreeNum = 0; + uint32 secsleft = uint8((pBoot->cancelTime - time(NULL)) / 1000); + for (LFGAnswerMap::const_iterator it = pBoot->votes.begin(); it != pBoot->votes.end(); ++it) + { + if (it->second != LFG_ANSWER_PENDING) + { + ++votesNum; + if (it->second == LFG_ANSWER_AGREE) + ++agreeNum; + } + } + DEBUG_LOG("SMSG_LFG_BOOT_PLAYER %u inProgress: %u - didVote: %u - agree: %u - victim: %u votes: %u - agrees: %u - left: %u - needed: %u - reason %s", + GetPlayer()->GetObjectGuid().GetCounter(), uint8(pBoot->inProgress), uint8(playerVote != LFG_ANSWER_PENDING), uint8(playerVote == LFG_ANSWER_AGREE), pBoot->victim.GetCounter(), votesNum, agreeNum, secsleft, pBoot->votedNeeded, pBoot->reason.c_str()); + + WorldPacket data(SMSG_LFG_BOOT_PLAYER, 1 + 1 + 1 + 8 + 4 + 4 + 4 + 4 + pBoot->reason.length()+1); + data << uint8(pBoot->inProgress); // Vote in progress + data << uint8(playerVote != LFG_ANSWER_PENDING); // Did Vote + data << uint8(playerVote == LFG_ANSWER_AGREE); // Agree + data << pBoot->victim; // Victim GUID + data << uint32(votesNum); // Total Votes + data << uint32(agreeNum); // Agree Count + data << uint32(secsleft); // Time Left + data << uint32(pBoot->votedNeeded); // Needed Votes + data << pBoot->reason.c_str(); // Kick reason + SendPacket(&data); +} + +void WorldSession::SendLfgUpdateProposal(LFGProposal* pProposal) +{ + if (!pProposal) + return; + + ObjectGuid guid = GetPlayer()->GetObjectGuid(); + +// LfgProposalPlayer* ppPlayer = itPlayer->second; +// uint32 pLowGroupGuid = ppPlayer->groupLowGuid; +// uint32 dLowGuid = pProp->groupLowGuid; +// uint32 dungeonId = pProp->dungeonId; + bool isSameDungeon = false; + bool isContinue = false; + + uint32 completedEncounters = 0; + + if (pProposal->group) + { + isContinue = pProposal->group->isLFDGroup(); + // && sLFGMgr->GetState(gguid) != LFG_STATE_FINISHED_DUNGEON; + isSameDungeon = GetPlayer()->GetGroup() == pProposal->group && isContinue; + } + + DEBUG_LOG("SMSG_LFG_PROPOSAL_UPDATE %u state: %u", GetPlayer()->GetObjectGuid().GetCounter(), pProposal->state); + + WorldPacket data(SMSG_LFG_PROPOSAL_UPDATE, 4 + 1 + 4 + 4 + 1 + 1 + pProposal->players.size() * (4 + 1 + 1 + 1 + 1 +1)); + + LFGDungeonEntry const* dungeon = NULL; + + if (!isContinue) // Only show proposal dungeon if it's continue + { + LFGDungeonSet* playerDungeons = GetPlayer()->GetLFGState()->GetDungeons(); + if (playerDungeons->size() == 1) + dungeon = (*playerDungeons->begin()); + } + + if (dungeon) + { + // Select a player inside to be get completed encounters from + if (pProposal->group) + { + for (GroupReference* itr = pProposal->group->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* groupMember = itr->getSource(); + if (groupMember && groupMember->GetMapId() == uint32(dungeon->map)) + { + if (InstancePlayerBind* bind = groupMember->GetBoundInstance(dungeon->map, Difficulty(dungeon->difficulty))) + { + if (DungeonPersistentState* state = bind->state) + completedEncounters = state->GetCompletedEncountersMask(); + } + } + } + } + } + + data << uint32(dungeon->Entry()); // Dungeon + data << uint8(pProposal->state); // Result state + data << uint32(pProposal->ID); // Internal Proposal ID + data << uint32(completedEncounters); // Bosses killed + data << uint8(isSameDungeon); // Silent (show client window) + data << uint8(pProposal->players.size()); // Group size + + for (LFGQueuePlayerSet::const_iterator itr = pProposal->players.begin(); itr != pProposal->players.end(); ++itr) + { + Player* pPlayer = *itr; + data << uint32(pPlayer->GetLFGState()->GetRoles()); // Role + data << uint8(pPlayer->GetObjectGuid() == guid); // Self player + if (!(pPlayer->GetGroup() == pProposal->group)) // Player not it a group + { + data << uint8(0); // Not in dungeon + data << uint8(0); // Not same group + } + else + { + data << uint8(1); // In dungeon (silent) + data << uint8(1); // Same Group than player + } + data << uint8(pPlayer->GetLFGState()->GetAnswer() != LFG_ANSWER_PENDING); // Answered + data << uint8(pPlayer->GetLFGState()->GetAnswer() == LFG_ANSWER_AGREE); // Accepted + } + SendPacket(&data); +} diff --git a/src/game/LFGMgr.cpp b/src/game/LFGMgr.cpp index 8d1441650..21a7e4309 100644 --- a/src/game/LFGMgr.cpp +++ b/src/game/LFGMgr.cpp @@ -48,6 +48,7 @@ LFGMgr::LFGMgr() if (LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i)) m_dungeonMap.insert(std::make_pair(dungeon->ID, dungeon)); } + m_proposalMap.clear(); } LFGMgr::~LFGMgr() @@ -62,6 +63,7 @@ LFGMgr::~LFGMgr() m_queueInfoMap[i].clear(); m_groupQueueInfoMap[i].clear(); } + m_proposalMap.clear(); } void LFGMgr::Update(uint32 diff) @@ -711,3 +713,52 @@ void LFGMgr::SendLFGReward(Player* player) DEBUG_LOG("LFGMgr::RewardDungeonDoneFor: %u done dungeon %u, %s previously done.", player->GetObjectGuid().GetCounter(), dungeon->ID, index > 0 ? " " : " not"); player->GetSession()->SendLfgPlayerReward(dungeon, reward, qReward, index == 0); } + +bool LFGMgr::CreateProposal(LFGDungeonEntry const* dungeon, Group* group) +{ + if (!dungeon) + return false; + + uint32 ID = GenerateProposalID(); + LFGProposal proposal = LFGProposal(dungeon); + proposal.cancelTime = time_t(time(NULL)) + LFG_TIME_PROPOSAL; + proposal.state = LFG_PROPOSAL_INITIATING; + proposal.group = group; + m_proposalMap.insert(std::make_pair(ID, proposal)); + + if (group) + { + group->GetLFGState()->SetProposal(GetProposal(ID)); + } + + DEBUG_LOG("LFGMgr::CreateProposal: %u, dungeon %u, %s", ID, dungeon->ID, group ? " in group" : " not in group"); +} + +LFGProposal* LFGMgr::GetProposal(uint32 ID) +{ + LFGProposalMap::iterator itr = m_proposalMap.find(ID); + return itr != m_proposalMap.end() ? &itr->second : NULL; +} + +void LFGMgr::RemoveProposal(uint32 ID) +{ + LFGProposalMap::iterator itr = m_proposalMap.find(ID); + if (itr == m_proposalMap.end()) + return; + + if (Group* group = (*itr).second.group) + { + group->GetLFGState()->SetProposal(NULL); + m_proposalMap.erase(itr); + } +} + +uint32 LFGMgr::GenerateProposalID() +{ + for (int32 i = 1; i != -1 ; ++i) + { + if (m_proposalMap.find(i) == m_proposalMap.end()) + return uint32(i); + } + return 0; +} diff --git a/src/game/LFGMgr.h b/src/game/LFGMgr.h index 01957aaf0..b49858551 100644 --- a/src/game/LFGMgr.h +++ b/src/game/LFGMgr.h @@ -20,13 +20,27 @@ #define _LFGMGR_H #include "Common.h" +#include "ObjectGuid.h" #include "Policies/Singleton.h" #include "LFG.h" class Group; class Player; -class ObjectGuid; +// forward struct declarations +struct LFGReward; +struct LFGQueueInfo; +struct LFGProposal; + +typedef std::multimap LFGRewardMap; +typedef std::pair LFGRewardMapBounds; +typedef std::map LFGQueueInfoMap; +typedef std::map LFGDungeonMap; +typedef std::set LFGQueuePlayerSet; +typedef std::set LFGQueueGroupSet; +typedef std::map LFGRolesMap; +typedef std::map LFGAnswerMap; +typedef std::map LFGProposalMap; // Reward info struct LFGReward @@ -78,12 +92,41 @@ struct LFGQueueStatus time_t queuedTime; // Player wait time in queue }; -typedef std::multimap LFGRewardMap; -typedef std::pair LFGRewardMapBounds; -typedef std::map LFGQueueInfoMap; -typedef std::map LFGDungeonMap; -typedef std::set LFGQueuePlayerSet; -typedef std::set LFGQueueGroupSet; +/// Stores group data related to proposal to join +struct LFGProposal +{ + LFGProposal(LFGDungeonEntry const* _dungeon): dungeon(_dungeon), state(LFG_PROPOSAL_INITIATING), group(NULL), cancelTime(0) {}; + + LFGDungeonEntry const* dungeon; // Dungeon + LFGQueuePlayerSet players; // Players in this proposal + LFGProposalState state; // State of the proposal + Group* group; // Proposal group (NULL if not created) + time_t cancelTime; // Time when we will cancel this proposal + uint32 ID; // Proposal id +}; + +/// Stores all rolecheck info of a group that wants to join +struct LFGRoleCheck +{ + time_t cancelTime; ///< Time when the rolecheck will fail + LFGRolesMap roles; ///< Player selected roles + LFGRoleCheckState state; ///< State of the rolecheck + LFGDungeonSet dungeons; ///< Dungeons group is applying for (expanded random dungeons) + uint32 randomID; ///< Random Dungeon Id. + ObjectGuid leaderGuid; ///< Leader of the group +}; + +/// Stores information of a current vote to kick someone from a group +struct LFGPlayerBoot +{ + time_t cancelTime; ///< Time left to vote + bool inProgress; ///< Vote in progress + LFGAnswerMap votes; ///< Player votes (-1 not answer | 0 Not agree | 1 agree) + ObjectGuid victim; ///< Player guid to be kicked (can't vote) + uint8 votedNeeded; ///< Votes needed to kick the player + std::string reason; ///< kick reason +}; + class LFGMgr { @@ -102,13 +145,19 @@ class LFGMgr void ClearLFRList(Player* player); + // reward system void LoadRewards(); LFGReward const* GetRandomDungeonReward(LFGDungeonEntry const* dungeon, Player* player); void SendLFGRewards(Player* player); void SendLFGReward(Player* player); - LFGDungeonEntry const* GetDungeon(uint32 dungeonID); + // Proposal system + bool CreateProposal(LFGDungeonEntry const* dungeon, Group* group = NULL); + LFGProposal* GetProposal(uint32 ID); + void RemoveProposal(uint32 ID); + // Dungeon operations + LFGDungeonEntry const* GetDungeon(uint32 dungeonID); bool IsRandomDungeon(LFGDungeonEntry const* dungeon); LFGDungeonSet GetRandomDungeonsForPlayer(Player* player); @@ -126,11 +175,13 @@ class LFGMgr void _Leave(ObjectGuid guid, LFGType excludeType = LFG_TYPE_NONE); void _JoinGroup(ObjectGuid guid, LFGType type); void _LeaveGroup(ObjectGuid guid, LFGType excludeType = LFG_TYPE_NONE); + uint32 GenerateProposalID(); LFGRewardMap m_RewardMap; // Stores rewards for random dungeons LFGQueueInfoMap m_queueInfoMap[LFG_TYPE_MAX]; // Queued players LFGQueueInfoMap m_groupQueueInfoMap[LFG_TYPE_MAX]; // Queued groups LFGDungeonMap m_dungeonMap; // sorted dungeon map LFGQueueStatus m_queueStatus[LFG_TYPE_MAX]; // Queue statisic + LFGProposalMap m_proposalMap; // Proposal store }; diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp index c22d550d9..e1d26bb3c 100644 --- a/src/game/Level2.cpp +++ b/src/game/Level2.cpp @@ -26,7 +26,6 @@ #include "TemporarySummon.h" #include "Totem.h" #include "Pet.h" -#include "Vehicle.h" #include "GameObject.h" #include "Opcodes.h" #include "Chat.h" @@ -1594,8 +1593,7 @@ bool ChatHandler::HandleNpcAddCommand(char* args) return false; CreatureInfo const *cinfo = ObjectMgr::GetCreatureTemplate(id); - //FIXME: need vehicle support like GenerateStaticCreatureLowGuid when its will allowed static spawns - if (!cinfo || cinfo->GetHighGuid() != HIGHGUID_UNIT) + if (!cinfo) { PSendSysMessage(LANG_COMMAND_INVALIDCREATUREID, id); SetSentErrorMessage(true); diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index b58617773..3a1f7d308 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -3224,7 +3224,7 @@ bool ChatHandler::HandleAddItemSetCommand(char* args) { found = true; ItemPosCountVec dest; - uint8 msg = plTarget->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pProto->ItemId, 1 ); + InventoryResult msg = plTarget->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pProto->ItemId, 1 ); if (msg == EQUIP_ERR_OK) { Item* item = plTarget->StoreNewItem( dest, pProto->ItemId, true); @@ -4827,8 +4827,8 @@ bool ChatHandler::HandleNpcInfoCommand(char* /*args*/) else PSendSysMessage(LANG_NPCINFO_CHAR, target->GetGUIDLow(), faction, npcflags, Entry, displayid, nativeid); - if (cInfo->VehicleId) - PSendSysMessage("VehicleId: %u", cInfo->VehicleId); + if (cInfo->vehicleId) + PSendSysMessage("VehicleId: %u", cInfo->vehicleId); PSendSysMessage(LANG_NPCINFO_LEVEL, target->getLevel()); PSendSysMessage(LANG_NPCINFO_HEALTH,target->GetCreateHealth(), target->GetMaxHealth(), target->GetHealth()); diff --git a/src/game/LootHandler.cpp b/src/game/LootHandler.cpp index 2d46fe0d9..47afcafae 100644 --- a/src/game/LootHandler.cpp +++ b/src/game/LootHandler.cpp @@ -128,7 +128,7 @@ void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data ) pItem->SetLootState(ITEM_LOOT_CHANGED); ItemPosCountVec dest; - uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count ); + InventoryResult msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count ); if ( msg == EQUIP_ERR_OK ) { AllowedLooterSet* looters = item->GetAllowedLooters(); @@ -524,7 +524,7 @@ void WorldSession::HandleLootMasterGiveOpcode( WorldPacket & recv_data ) Loot *pLoot = NULL; - if(lootguid.IsCreatureOrVehicle()) + if (lootguid.IsCreatureOrVehicle()) { Creature *pCreature = GetPlayer()->GetMap()->GetCreature(lootguid); if(!pCreature) @@ -552,7 +552,7 @@ void WorldSession::HandleLootMasterGiveOpcode( WorldPacket & recv_data ) LootItem& item = pLoot->items[slotid]; ItemPosCountVec dest; - uint8 msg = target->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item.itemid, item.count ); + InventoryResult msg = target->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item.itemid, item.count ); if ( msg != EQUIP_ERR_OK ) { target->SendEquipError( msg, NULL, NULL, item.itemid ); diff --git a/src/game/Mail.cpp b/src/game/Mail.cpp index e08983ea8..df853307a 100644 --- a/src/game/Mail.cpp +++ b/src/game/Mail.cpp @@ -616,7 +616,7 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data ) data << uint32((*itr)->stationery); // stationery (Stationery.dbc) data << uint32((*itr)->money); // copper data << uint32((*itr)->checked); // flags - data << float(((*itr)->expire_time-time(NULL))/DAY);// Time + data << float(float((*itr)->expire_time - time(NULL)) / float(DAY));// Time data << uint32((*itr)->mailTemplateId); // mail template (MailTemplate.dbc) data << (*itr)->subject; // Subject string - once 00, when mail type = 3, max 256 data << (*itr)->body; // message? max 8000 diff --git a/src/game/Map.cpp b/src/game/Map.cpp index a41929108..c4986f4dc 100644 --- a/src/game/Map.cpp +++ b/src/game/Map.cpp @@ -19,7 +19,6 @@ #include "Map.h" #include "MapManager.h" #include "Player.h" -#include "Vehicle.h" #include "GridNotifiers.h" #include "Log.h" #include "GridStates.h" @@ -142,7 +141,7 @@ template<> void Map::AddToGrid(Creature* obj, NGridType *grid, Cell const& cell) { // add to world object registry in grid - if(obj->IsPet()) + if (obj->IsPet()) { (*grid)(cell.CellX(), cell.CellY()).AddWorldObject(obj); obj->SetCurrentCell(cell); @@ -186,7 +185,7 @@ template<> void Map::RemoveFromGrid(Creature* obj, NGridType *grid, Cell const& cell) { // remove from world object registry in grid - if(obj->IsPet()) + if (obj->IsPet()) { (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject(obj); } @@ -448,7 +447,7 @@ void Map::Update(const uint32 &t_diff) WorldSession * pSession = plr->GetSession(); MapSessionFilter updater(pSession); - pSession->Update(t_diff, updater); + pSession->Update(updater); } } @@ -615,7 +614,9 @@ void Map::Remove(Player *player, bool remove) SendRemoveTransports(player); UpdateObjectVisibility(player,cell,p); - player->ResetMap(); + if (!player->GetPlayerbotAI()) + player->ResetMap(); + if( remove ) DeleteFromWorld(player); } @@ -701,40 +702,27 @@ Map::PlayerRelocation(Player *player, float x, float y, float z, float orientati } } -void -Map::CreatureRelocation(Creature *creature, float x, float y, float z, float ang) +void Map::CreatureRelocation(Creature *creature, float x, float y, float z, float ang) { MANGOS_ASSERT(CheckGridIntegrity(creature,false)); Cell old_cell = creature->GetCurrentCell(); + Cell new_cell(MaNGOS::ComputeCellPair(x, y)); - CellPair new_val = MaNGOS::ComputeCellPair(x, y); - Cell new_cell(new_val); - - if (old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell)) - { - DEBUG_FILTER_LOG(LOG_FILTER_CREATURE_MOVES, "Creature (GUID: %u Entry: %u) added to moving list from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", creature->GetGUIDLow(), creature->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); - - // do move or do move to respawn or remove creature if previous all fail - if (CreatureCellRelocation(creature,new_cell)) - { - // update pos - creature->Relocate(x, y, z, ang); - creature->OnRelocated(); - } - // if creature can't be move in new cell/grid (not loaded) move it to repawn cell/grid - // creature coordinates will be updated and notifiers send - else if (!CreatureRespawnRelocation(creature)) - { - // ... or unload (if respawn grid also not loaded) - DEBUG_FILTER_LOG(LOG_FILTER_CREATURE_MOVES, "Creature (GUID: %u Entry: %u ) can't be move to unloaded respawn grid.",creature->GetGUIDLow(),creature->GetEntry()); - } - } - else + // do move or do move to respawn or remove creature if previous all fail + if (CreatureCellRelocation(creature,new_cell)) { + // update pos creature->Relocate(x, y, z, ang); creature->OnRelocated(); } + // if creature can't be move in new cell/grid (not loaded) move it to repawn cell/grid + // creature coordinates will be updated and notifiers send + else if (!CreatureRespawnRelocation(creature)) + { + // ... or unload (if respawn grid also not loaded) + DEBUG_FILTER_LOG(LOG_FILTER_CREATURE_MOVES, "Creature (GUID: %u Entry: %u ) can't be move to unloaded respawn grid.",creature->GetGUIDLow(),creature->GetEntry()); + } MANGOS_ASSERT(CheckGridIntegrity(creature,true)); } @@ -742,63 +730,26 @@ Map::CreatureRelocation(Creature *creature, float x, float y, float z, float ang bool Map::CreatureCellRelocation(Creature *c, Cell new_cell) { Cell const& old_cell = c->GetCurrentCell(); - if(!old_cell.DiffGrid(new_cell) ) // in same grid + if (old_cell.DiffGrid(new_cell)) { - // if in same cell then none do - if(old_cell.DiffCell(new_cell)) - { - DEBUG_FILTER_LOG(LOG_FILTER_CREATURE_MOVES, "Creature (GUID: %u Entry: %u) moved in grid[%u,%u] from cell[%u,%u] to cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.CellX(), new_cell.CellY()); - - RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell); - - NGridType* new_grid = getNGrid(new_cell.GridX(), new_cell.GridY()); - AddToGrid(c,new_grid,new_cell); - - c->GetViewPoint().Event_GridChanged( &(*new_grid)(new_cell.CellX(),new_cell.CellY()) ); - } - else + if (!c->isActiveObject() && !loaded(new_cell.gridPair())) { - DEBUG_FILTER_LOG(LOG_FILTER_CREATURE_MOVES, "Creature (GUID: %u Entry: %u) move in same grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY()); + DEBUG_FILTER_LOG(LOG_FILTER_CREATURE_MOVES, "Creature (GUID: %u Entry: %u) attempt move from grid[%u,%u]cell[%u,%u] to unloaded grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); + return false; } - - return true; - } - - // in diff. grids but active creature - if(c->isActiveObject()) - { EnsureGridLoadedAtEnter(new_cell); - - DEBUG_FILTER_LOG(LOG_FILTER_CREATURE_MOVES, "Active creature (GUID: %u Entry: %u) moved from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); - - RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell); - - NGridType* new_grid = getNGrid(new_cell.GridX(), new_cell.GridY()); - AddToGrid(c,new_grid,new_cell); - c->GetViewPoint().Event_GridChanged( &(*new_grid)(new_cell.CellX(),new_cell.CellY()) ); - - return true; } - // in diff. loaded grid normal creature - if(loaded(GridPair(new_cell.GridX(), new_cell.GridY()))) + if (old_cell != new_cell) { - DEBUG_FILTER_LOG(LOG_FILTER_CREATURE_MOVES, "Creature (GUID: %u Entry: %u) moved from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); - - RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell); - { - EnsureGridCreated(GridPair(new_cell.GridX(), new_cell.GridY())); - NGridType* new_grid = getNGrid(new_cell.GridX(), new_cell.GridY()); - AddToGrid(c,new_grid,new_cell); - c->GetViewPoint().Event_GridChanged( &(*new_grid)(new_cell.CellX(),new_cell.CellY()) ); - } - - return true; + DEBUG_FILTER_LOG(LOG_FILTER_CREATURE_MOVES, "Creature (GUID: %u Entry: %u) moved in grid[%u,%u] from cell[%u,%u] to cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.CellX(), new_cell.CellY()); + NGridType* oldGrid = getNGrid(old_cell.GridX(), old_cell.GridY()); + NGridType* newGrid = getNGrid(new_cell.GridX(), new_cell.GridY()); + RemoveFromGrid(c, oldGrid, old_cell); + AddToGrid(c, newGrid, new_cell); + c->GetViewPoint().Event_GridChanged(&(*newGrid)(new_cell.CellX(),new_cell.CellY())); } - - // fail to move: normal creature attempt move to unloaded grid - DEBUG_FILTER_LOG(LOG_FILTER_CREATURE_MOVES, "Creature (GUID: %u Entry: %u) attempt move from grid[%u,%u]cell[%u,%u] to unloaded grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); - return false; + return true; } bool Map::CreatureRespawnRelocation(Creature *c) @@ -2953,9 +2904,9 @@ Player* Map::GetPlayer(ObjectGuid guid) } /** - * Function return creature (non-pet and then most summoned by spell creatures, and not vehicle) that in world at CURRENT map + * Function return creature (non-pet and then most summoned by spell creatures) that in world at CURRENT map * - * @param guid must be creature guid (HIGHGUID_UNIT) + * @param guid must be creature or vehicle guid (HIGHGUID_UNIT HIGHGUID_VEHICLE) */ Creature* Map::GetCreature(ObjectGuid guid) { @@ -2976,14 +2927,8 @@ Creature* Map::GetCreature(ObjectGuid guid) */ Pet* Map::GetPet(ObjectGuid guid) { - __try - { - return m_objectsStore.find(guid.GetRawValue(), (Pet*)NULL); - } - __except ( EXCEPTION_EXECUTE_HANDLER ) - { - return NULL; - } + Pet* pet = ObjectAccessor::FindPet(guid); // return only in world pets + return pet && pet->GetMap() == this ? pet : NULL; } /** diff --git a/src/game/MapPersistentStateMgr.cpp b/src/game/MapPersistentStateMgr.cpp index 4936d371d..3b4c96c06 100644 --- a/src/game/MapPersistentStateMgr.cpp +++ b/src/game/MapPersistentStateMgr.cpp @@ -121,7 +121,6 @@ void MapPersistentState::SaveGORespawnTime(uint32 loguid, time_t t) void MapPersistentState::SetCreatureRespawnTime( uint32 loguid, time_t t ) { - sMapPersistentStateMgr.m_persistentStateLock.acquire(); if (t > sWorld.GetGameTime()) m_creatureRespawnTimes[loguid] = t; else @@ -129,12 +128,10 @@ void MapPersistentState::SetCreatureRespawnTime( uint32 loguid, time_t t ) m_creatureRespawnTimes.erase(loguid); UnloadIfEmpty(); } - sMapPersistentStateMgr.m_persistentStateLock.release(); } void MapPersistentState::SetGORespawnTime( uint32 loguid, time_t t ) { - sMapPersistentStateMgr.m_persistentStateLock.acquire(); if (t > sWorld.GetGameTime()) m_goRespawnTimes[loguid] = t; else @@ -142,17 +139,14 @@ void MapPersistentState::SetGORespawnTime( uint32 loguid, time_t t ) m_goRespawnTimes.erase(loguid); UnloadIfEmpty(); } - sMapPersistentStateMgr.m_persistentStateLock.release(); } void MapPersistentState::ClearRespawnTimes() { - sMapPersistentStateMgr.m_persistentStateLock.acquire(); m_goRespawnTimes.clear(); m_creatureRespawnTimes.clear(); UnloadIfEmpty(); - sMapPersistentStateMgr.m_persistentStateLock.release(); } void MapPersistentState::AddCreatureToGrid( uint32 guid, CreatureData const* data ) diff --git a/src/game/MapPersistentStateMgr.h b/src/game/MapPersistentStateMgr.h index e60253114..4244cf21a 100644 --- a/src/game/MapPersistentStateMgr.h +++ b/src/game/MapPersistentStateMgr.h @@ -148,13 +148,15 @@ class MapPersistentState RespawnTimes m_creatureRespawnTimes; // lock MapPersistentState from unload, for example for temporary bound dungeon unload delay RespawnTimes m_goRespawnTimes; // lock MapPersistentState from unload, for example for temporary bound dungeon unload delay MapCellObjectGuidsMap m_gridObjectGuids; // Single map copy specific grid spawn data, like pool spawns - }; inline bool MapPersistentState::CanBeUnload() const { // prevent unload if used for loaded map - return !m_usedByMap; + if (Map* map = GetMap()) + return false; + else + return true; } class WorldPersistentState : public MapPersistentState @@ -371,8 +373,6 @@ class MANGOS_DLL_DECL MapPersistentStateManager : public MaNGOS::Singleton void DoForAllStatesWithMapId(uint32 mapId, Do& _do); - ACE_Recursive_Thread_Mutex m_persistentStateLock; - public: // DungeonPersistentState specific void CleanupInstances(); void PackInstances(); diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp index ba219bb6f..ebf9b18f0 100644 --- a/src/game/MovementHandler.cpp +++ b/src/game/MovementHandler.cpp @@ -46,13 +46,13 @@ void WorldSession::HandleMoveWorldportAckOpcode() if(!GetPlayer()->IsBeingTeleportedFar()) return; + if (_player->GetVehicleKit()) + _player->GetVehicleKit()->RemoveAllPassengers(); + // get start teleport coordinates (will used later in fail case) WorldLocation old_loc; GetPlayer()->GetPosition(old_loc); - if (_player->GetVehicleKit()) - _player->GetVehicleKit()->RemoveAllPassengers(); - // get the teleport destination WorldLocation &loc = GetPlayer()->GetTeleportDest(); @@ -423,177 +423,6 @@ void WorldSession::HandleMoveNotActiveMoverOpcode(WorldPacket &recv_data) _player->m_movementInfo = mi; } -void WorldSession::HandleDismissControlledVehicle(WorldPacket &recv_data) -{ - DEBUG_LOG("WORLD: Received CMSG_DISMISS_CONTROLLED_VEHICLE"); - recv_data.hexlike(); - - ObjectGuid guid; - MovementInfo mi; - - recv_data >> guid.ReadAsPacked(); - recv_data >> mi; - - if(!GetPlayer()->GetVehicle()) - return; - - bool dismiss = true; - - if (GetPlayer()->GetVehicle()->GetVehicleInfo()->m_flags & (VEHICLE_FLAG_NOT_DISMISS | VEHICLE_FLAG_ACCESSORY)) - dismiss = false; - - GetPlayer()->m_movementInfo = mi; - GetPlayer()->ExitVehicle(); - - if (dismiss) - if (Creature* vehicle = GetPlayer()->GetMap()->GetAnyTypeCreature(guid)) - vehicle->ForcedDespawn(); -} - -void WorldSession::HandleRequestVehicleExit(WorldPacket &recv_data) -{ - DEBUG_LOG("WORLD: Received CMSG_REQUEST_VEHICLE_EXIT"); - - GetPlayer()->ExitVehicle(); -} - -void WorldSession::HandleRequestVehiclePrevSeat(WorldPacket &recv_data) -{ - DEBUG_LOG("WORLD: Received CMSG_REQUEST_VEHICLE_PREV_SEAT"); - - GetPlayer()->ChangeSeat(-1, false); -} - -void WorldSession::HandleRequestVehicleNextSeat(WorldPacket &recv_data) -{ - DEBUG_LOG("WORLD: Received CMSG_REQUEST_VEHICLE_NEXT_SEAT"); - - GetPlayer()->ChangeSeat(-1, true); -} - -void WorldSession::HandleRequestVehicleSwitchSeat(WorldPacket &recv_data) -{ - DEBUG_LOG("WORLD: Received CMSG_REQUEST_VEHICLE_SWITCH_SEAT"); - recv_data.hexlike(); - - ObjectGuid guid; - recv_data >> guid.ReadAsPacked(); - - int8 seatId; - recv_data >> seatId; - - VehicleKit* pVehicle = GetPlayer()->GetVehicle(); - - if (!pVehicle) - return; - - if (GetPlayer()->GetVehicle()->GetVehicleInfo()->m_flags & VEHICLE_FLAG_DISABLE_SWITCH) - GetPlayer()->ExitVehicle(); - - if (pVehicle->GetBase()->GetObjectGuid() == guid) - GetPlayer()->ChangeSeat(seatId); - else if (Unit *Vehicle2 = GetPlayer()->GetMap()->GetUnit(guid)) - { - if (VehicleKit *pVehicle2 = Vehicle2->GetVehicleKit()) - if (pVehicle2->HasEmptySeat(seatId)) - { - GetPlayer()->ExitVehicle(); - GetPlayer()->EnterVehicle(pVehicle2, seatId); - } - } -} - -void WorldSession::HandleEnterPlayerVehicle(WorldPacket &recv_data) -{ - DEBUG_LOG("WORLD: Received CMSG_PLAYER_VEHICLE_ENTER"); - recv_data.hexlike(); - - ObjectGuid guid; - recv_data >> guid; - - Player* player = sObjectMgr.GetPlayer(guid); - - if (!player) - return; - - if (!GetPlayer()->IsInSameRaidWith(player)) - return; - - if (!GetPlayer()->IsWithinDistInMap(player, INTERACTION_DISTANCE)) - return; - - if (player->GetTransport()) - return; - - if (VehicleKit* pVehicle = player->GetVehicleKit()) - GetPlayer()->EnterVehicle(pVehicle); -} - -void WorldSession::HandleEjectPasenger(WorldPacket &recv_data) -{ - DEBUG_LOG("WORLD: Received CMSG_EJECT_PASSENGER"); - recv_data.hexlike(); - - ObjectGuid guid; - recv_data >> guid; - - Unit* passenger = ObjectAccessor::GetUnit(*GetPlayer(), guid); - - if (!passenger) - return; - - if (!passenger->GetVehicle() || passenger->GetVehicle() != GetPlayer()->GetVehicleKit()) - return; - - passenger->ExitVehicle(); - - // eject and remove creatures of player mounts - if (passenger->GetTypeId() == TYPEID_UNIT) - passenger->AddObjectToRemoveList(); -} - -void WorldSession::HandleChangeSeatsOnControlledVehicle(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: Recvd CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE"); - recv_data.hexlike(); - - ObjectGuid guid, guid2; - recv_data >> guid.ReadAsPacked(); - - MovementInfo mi; - recv_data >> mi; - GetPlayer()->m_movementInfo = mi; - - recv_data >> guid2.ReadAsPacked(); //guid of vehicle or of vehicle in target seat - - int8 seatId; - recv_data >> seatId; - - VehicleKit* pVehicle = GetPlayer()->GetVehicle(); - - if (!pVehicle) - return; - - if (GetPlayer()->GetVehicle()->GetVehicleInfo()->m_flags & VEHICLE_FLAG_DISABLE_SWITCH) - GetPlayer()->ExitVehicle(); - - if(guid.GetRawValue() == guid2.GetRawValue()) - GetPlayer()->ChangeSeat(seatId, false); - - else if (guid2.IsVehicle()) - { - if (Creature* vehicle = GetPlayer()->GetMap()->GetAnyTypeCreature(guid2)) - { - if (VehicleKit* pVehicle2 = vehicle->GetVehicleKit()) - if(pVehicle2->HasEmptySeat(seatId)) - { - GetPlayer()->ExitVehicle(); - GetPlayer()->EnterVehicle(pVehicle2, seatId); - } - } - } -} - void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recvdata*/) { //DEBUG_LOG("WORLD: Recvd CMSG_MOUNTSPECIAL_ANIM"); @@ -722,7 +551,7 @@ void WorldSession::HandleMoverRelocation(MovementInfo& movementInfo) { if ((*iter)->GetObjectGuid() == movementInfo.GetTransportGuid()) { - plMover->SetTransport(*iter); + plMover->m_transport = (*iter); (*iter)->AddPassenger(plMover); if (plMover->GetVehicleKit()) diff --git a/src/game/Object.cpp b/src/game/Object.cpp index b5dd80674..9ddc86118 100644 --- a/src/game/Object.cpp +++ b/src/game/Object.cpp @@ -230,9 +230,11 @@ void Object::BuildMovementUpdate(ByteBuffer * data, uint16 updateFlags) const { uint16 moveFlags2 = (isType(TYPEMASK_UNIT) ? ((Unit*)this)->m_movementInfo.GetMovementFlags2() : MOVEFLAG2_NONE); +/* removed by zergtmn. strange... if(GetTypeId() == TYPEID_UNIT) if(((Creature*)this)->GetVehicleKit()) moveFlags2 |= MOVEFLAG2_ALLOW_PITCHING; // always allow pitch +*/ *data << uint16(updateFlags); // update flags @@ -256,12 +258,12 @@ void Object::BuildMovementUpdate(ByteBuffer * data, uint16 updateFlags) const // (ok) most seem to have this unit->m_movementInfo.AddMovementFlag(MOVEFLAG_LEVITATING); - if (!((Creature*)unit)->hasUnitState(UNIT_STAT_MOVING)) + /*if (!((Creature*)unit)->hasUnitState(UNIT_STAT_MOVING)) { // (ok) possibly some "hover" mode unit->m_movementInfo.AddMovementFlag(MOVEFLAG_ROOT); } - else + else*/ { if (((Creature*)unit)->IsMounted()) { @@ -506,9 +508,9 @@ void Object::BuildMovementUpdate(ByteBuffer * data, uint16 updateFlags) const } // 0x80 - if(updateFlags & UPDATEFLAG_VEHICLE) + if (updateFlags & UPDATEFLAG_VEHICLE) { - *data << uint32(((Unit*)this)->GetVehicleKit()->GetVehicleId()); // vehicle id + *data << uint32(((Unit*)this)->GetVehicleInfo()->GetEntry()->m_ID); // vehicle id *data << float(((WorldObject*)this)->GetOrientation()); } @@ -1482,6 +1484,8 @@ void WorldObject::GetRandomPoint( float x, float y, float z, float distance, flo void WorldObject::UpdateGroundPositionZ(float x, float y, float &z, float maxDiff) const { + UpdateAllowedPositionZ(x, y, z); + maxDiff = maxDiff >= 100.0f ? 10.0f : sqrtf(maxDiff); bool useVmaps = false; if( GetTerrain()->GetHeight(x, y, z, false) < GetTerrain()->GetHeight(x, y, z, true) ) // check use of vmaps @@ -1779,7 +1783,7 @@ Creature* WorldObject::SummonCreature(uint32 id, float x, float y, float z, floa if (x == 0.0f && y == 0.0f && z == 0.0f) pos = CreatureCreatePos(this, GetOrientation(), CONTACT_DISTANCE, ang); - if (!pCreature->Create(GetMap()->GenerateLocalLowGuid(HIGHGUID_UNIT), pos, cinfo, team)) + if (!pCreature->Create(GetMap()->GenerateLocalLowGuid(cinfo->GetHighGuid()), pos, cinfo, team)) { delete pCreature; return NULL; diff --git a/src/game/ObjectGridLoader.cpp b/src/game/ObjectGridLoader.cpp index f4bec4721..c4ba3d2dc 100644 --- a/src/game/ObjectGridLoader.cpp +++ b/src/game/ObjectGridLoader.cpp @@ -58,7 +58,7 @@ ObjectGridRespawnMover::Visit(CreatureMapType &m) Creature * c = iter->getSource(); - MANGOS_ASSERT((!c->IsPet()) && "ObjectGridRespawnMover don't must be called for pets"); + MANGOS_ASSERT(!c->IsPet() && "ObjectGridRespawnMover don't must be called for pets"); Cell const& cur_cell = c->GetCurrentCell(); diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 3c1f981f9..232770ba0 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -730,23 +730,18 @@ void ObjectMgr::LoadCreatureTemplates() } } - if (cInfo->VehicleId) - { - VehicleEntry const* pVehicleEntry = sVehicleStore.LookupEntry(cInfo->VehicleId); - - if (!pVehicleEntry) - { - sLog.outErrorDb("Creature (Entry: %u) has non-existing VehicleId (%u)", cInfo->Entry, cInfo->VehicleId); - const_cast(cInfo)->VehicleId = 0; - } - } - if(cInfo->MovementType >= MAX_DB_MOTION_TYPE) { sLog.outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignore and set to IDLE.",cInfo->Entry,cInfo->MovementType); const_cast(cInfo)->MovementType = IDLE_MOTION_TYPE; } + if (cInfo->vehicleId && !sVehicleStore.LookupEntry(cInfo->vehicleId)) + { + sLog.outErrorDb("Creature (Entry: %u) has non-existing vehicle_id (%u), set to 0.", cInfo->Entry, cInfo->vehicleId); + const_cast(cInfo)->vehicleId = 0; + } + if(cInfo->equipmentId > 0) // 0 no equipment { if(!GetEquipmentInfo(cInfo->equipmentId)) diff --git a/src/game/Opcodes.cpp b/src/game/Opcodes.cpp index ab72d6ada..b980dd987 100644 --- a/src/game/Opcodes.cpp +++ b/src/game/Opcodes.cpp @@ -688,7 +688,7 @@ OpcodeHandler opcodeTable[NUM_MSG_TYPES] = /*0x293*/ { "SMSG_LFG_OFFER_CONTINUE", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, /*0x294*/ { "CMSG_MEETINGSTONE_CHEAT", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL }, /*0x295*/ { "SMSG_MEETINGSTONE_SETQUEUE", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, - /*0x296*/ { "CMSG_LFG_GET_STATUS", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL }, + /*0x296*/ { "CMSG_LFG_GET_STATUS", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfgGetStatus }, /*0x297*/ { "SMSG_MEETINGSTONE_COMPLETE", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, /*0x298*/ { "SMSG_MEETINGSTONE_IN_PROGRESS", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, /*0x299*/ { "SMSG_MEETINGSTONE_MEMBER_ADDED", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, @@ -725,7 +725,7 @@ OpcodeHandler opcodeTable[NUM_MSG_TYPES] = /*0x2B8*/ { "SMSG_AREA_TRIGGER_MESSAGE", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, /*0x2B9*/ { "CMSG_SHOWING_HELM", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleShowingHelmOpcode }, /*0x2BA*/ { "CMSG_SHOWING_CLOAK", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleShowingCloakOpcode }, - /*0x2BB*/ { "SMSG_LFG_ROLE_CHOSEN", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, + /*0x2BB*/ { "SMSG_ROLE_CHOSEN", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, /*0x2BC*/ { "SMSG_PLAYER_SKINNED", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, /*0x2BD*/ { "SMSG_DURABILITY_DAMAGE_DEATH", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, /*0x2BE*/ { "CMSG_SET_EXPLORATION", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL }, @@ -890,7 +890,7 @@ OpcodeHandler opcodeTable[NUM_MSG_TYPES] = /*0x35D*/ { "CMSG_LFG_LEAVE", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfgLeaveOpcode }, /*0x35E*/ { "CMSG_SEARCH_LFG_JOIN", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfrSearchOpcode }, /*0x35F*/ { "CMSG_SEARCH_LFG_LEAVE", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfrLeaveOpcode }, - /*0x360*/ { "SMSG_UPDATE_LFG_LIST", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, + /*0x360*/ { "SMSG_LFG_SEARCH_RESULTS", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, /*0x361*/ { "SMSG_LFG_PROPOSAL_UPDATE", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, /*0x362*/ { "CMSG_LFG_PROPOSAL_RESULT", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfgProposalResultOpcode }, /*0x363*/ { "SMSG_LFG_ROLE_CHECK_UPDATE", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, @@ -1217,8 +1217,8 @@ OpcodeHandler opcodeTable[NUM_MSG_TYPES] = /*0x4A4*/ { "CMSG_QUERY_VEHICLE_STATUS", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL }, /*0x4A5*/ { "UMSG_UNKNOWN_1189", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL }, /*0x4A6*/ { "SMSG_BATTLEGROUND_INFO_THROTTLED", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, - /*0x4A7*/ { "SMSG_PLAYER_VEHICLE_DATA", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, - /*0x4A8*/ { "CMSG_PLAYER_VEHICLE_ENTER", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleEnterPlayerVehicle }, + /*0x4A7*/ { "SMSG_SET_VEHICLE_REC_ID", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, + /*0x4A8*/ { "CMSG_RIDE_VEHICLE_INTERACT", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleEnterPlayerVehicle }, /*0x4A9*/ { "CMSG_EJECT_PASSENGER", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleEjectPasenger }, /*0x4AA*/ { "SMSG_PET_GUIDS", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, /*0x4AB*/ { "SMSG_CLIENTCACHE_VERSION", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, diff --git a/src/game/PetitionsHandler.cpp b/src/game/PetitionsHandler.cpp index fc4a0c0e7..c3cbfc176 100644 --- a/src/game/PetitionsHandler.cpp +++ b/src/game/PetitionsHandler.cpp @@ -185,10 +185,10 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data) } ItemPosCountVec dest; - uint8 msg = _player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, charterid, pProto->BuyCount ); + InventoryResult msg = _player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, charterid, pProto->BuyCount ); if(msg != EQUIP_ERR_OK) { - _player->SendBuyError(msg, pCreature, charterid, 0); + _player->SendEquipError(msg, NULL, NULL, charterid); return; } diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 0cddfa403..e52532b0c 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -26,7 +26,6 @@ #include "WorldPacket.h" #include "WorldSession.h" #include "UpdateMask.h" -#include "Vehicle.h" #include "SkillDiscovery.h" #include "QuestDef.h" #include "GossipDef.h" @@ -60,8 +59,8 @@ #include "SocialMgr.h" #include "AchievementMgr.h" #include "Mail.h" -#include "PlayerBot/PlayerbotAI.h" -#include "PlayerBot/PlayerbotMgr.h" +#include "playerbot/PlayerbotAI.h" +#include "playerbot/PlayerbotMgr.h" #include @@ -406,7 +405,6 @@ UpdateMask Player::updateVisualBits; Player::Player (WorldSession *session): Unit(), m_mover(this), m_camera(this), m_achievementMgr(this), m_reputationMgr(this) { - // Playerbot mod: m_playerbotAI = NULL; m_playerbotMgr = NULL; for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) @@ -648,7 +646,6 @@ Player::~Player () delete m_runes; delete m_LFGState; - // Playerbot mod if (m_playerbotAI) { delete m_playerbotAI; m_playerbotAI = NULL; @@ -1532,12 +1529,10 @@ void Player::Update( uint32 update_diff, uint32 p_time ) if (IsHasDelayedTeleport()) TeleportTo(m_teleport_dest, m_teleport_options); - // Playerbot mod if (m_playerbotAI && m_playerbotMgr) m_playerbotAI->UpdateAI(p_time); else if (m_playerbotMgr) m_playerbotMgr->UpdateAI(p_time); - } void Player::SetDeathState(DeathState s) @@ -1793,8 +1788,6 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later) Pet* pet = GetPet(); - // Playerbot mod: if this user has bots, tell them to stop following master - // so they don't try to follow the master after the master teleports if (GetPlayerbotMgr()) GetPlayerbotMgr()->Stay(); @@ -2721,7 +2714,8 @@ void Player::GiveLevel(uint32 level) UpdateAllStats(); // set current level health and mana/energy to maximum after applying all mods. - SetHealth(GetMaxHealth()); + if (isAlive()) + SetHealth(GetMaxHealth()); SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY)); if(GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE)) @@ -9265,20 +9259,20 @@ uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap return NULL_SLOT; } -uint8 Player::CanUnequipItems( uint32 item, uint32 count ) const +InventoryResult Player::CanUnequipItems( uint32 item, uint32 count ) const { Item *pItem; uint32 tempcount = 0; - uint8 res = EQUIP_ERR_OK; + InventoryResult res = EQUIP_ERR_OK; for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i) { pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); if( pItem && pItem->GetEntry() == item ) { - uint8 ires = CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i, false); - if(ires==EQUIP_ERR_OK) + InventoryResult ires = CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i, false); + if(ires == EQUIP_ERR_OK) { tempcount += pItem->GetCount(); if( tempcount >= count ) @@ -9827,7 +9821,7 @@ bool Player::HasItemOrGemWithLimitCategoryEquipped( uint32 limitCategory, uint32 return false; } -uint8 Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count) const +InventoryResult Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count) const { ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(entry); if (!pProto) @@ -9907,7 +9901,7 @@ bool Player::HasItemTotemCategory( uint32 TotemCategory ) const return false; } -uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool swap, Item* pSrcItem ) const +InventoryResult Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool swap, Item* pSrcItem ) const { Item* pItem2 = GetItemByPos( bag, slot ); @@ -9958,7 +9952,7 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV else { // can be merged at least partly - uint8 res = pItem2->CanBeMergedPartlyWith(pProto); + InventoryResult res = pItem2->CanBeMergedPartlyWith(pProto); if (res != EQUIP_ERR_OK) return res; @@ -9978,10 +9972,10 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV return EQUIP_ERR_OK; } -uint8 Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool merge, bool non_specialized, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot ) const +InventoryResult Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool merge, bool non_specialized, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot ) const { // skip specific bag already processed in first called _CanStoreItem_InBag - if (bag==skip_bag) + if (bag == skip_bag) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; // skip nonexistent bag or self targeted bag @@ -10045,7 +10039,7 @@ uint8 Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototy return EQUIP_ERR_OK; } -uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool merge, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot ) const +InventoryResult Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool merge, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot ) const { for(uint32 j = slot_begin; j < slot_end; ++j) { @@ -10056,7 +10050,7 @@ uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, Item* pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, j ); // ignore move item (this slot will be empty at move) - if (pItem2==pSrcItem) + if (pItem2 == pSrcItem) pItem2 = NULL; // if merge skip empty, if !merge skip non-empty @@ -10092,7 +10086,7 @@ uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, return EQUIP_ERR_OK; } -uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint32 entry, uint32 count, Item *pItem, bool swap, uint32* no_space_count ) const +InventoryResult Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint32 entry, uint32 count, Item *pItem, bool swap, uint32* no_space_count ) const { DEBUG_LOG( "STORAGE: CanStoreItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, entry, count); @@ -10124,10 +10118,10 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 // check count of items (skip for auto move for same player from bank) uint32 no_similar_count = 0; // can't store this amount similar items - uint8 res = _CanTakeMoreSimilarItems(entry,count,pItem,&no_similar_count); - if (res!=EQUIP_ERR_OK) + InventoryResult res = _CanTakeMoreSimilarItems(entry,count,pItem,&no_similar_count); + if (res != EQUIP_ERR_OK) { - if (count==no_similar_count) + if (count == no_similar_count) { if (no_space_count) *no_space_count = no_similar_count; @@ -10140,16 +10134,16 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 if (bag != NULL_BAG && slot != NULL_SLOT) { res = _CanStoreItem_InSpecificSlot(bag,slot,dest,pProto,count,swap,pItem); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) { if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if (count==0) + if (count == 0) { - if (no_similar_count==0) + if (no_similar_count == 0) return EQUIP_ERR_OK; if (no_space_count) @@ -10169,16 +10163,16 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 if (bag == INVENTORY_SLOT_BAG_0) // inventory { res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,CURRENCYTOKEN_SLOT_END,dest,pProto,count,true,pItem,bag,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) { if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if (count==0) + if (count == 0) { - if (no_similar_count==0) + if (no_similar_count == 0) return EQUIP_ERR_OK; if (no_space_count) @@ -10187,16 +10181,16 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 } res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) { if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if (count==0) + if (count == 0) { - if (no_similar_count==0) + if (no_similar_count == 0) return EQUIP_ERR_OK; if (no_space_count) @@ -10208,19 +10202,19 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 { // we need check 2 time (specialized/non_specialized), use NULL_BAG to prevent skipping bag res = _CanStoreItem_InBag(bag,dest,pProto,count,true,false,pItem,NULL_BAG,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) res = _CanStoreItem_InBag(bag,dest,pProto,count,true,true,pItem,NULL_BAG,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) { if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if (count==0) + if (count == 0) { - if (no_similar_count==0) + if (no_similar_count == 0) return EQUIP_ERR_OK; if (no_space_count) @@ -10245,7 +10239,7 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 return res; } - if (count==0) + if (count == 0) { if (no_similar_count==0) return EQUIP_ERR_OK; @@ -10256,16 +10250,16 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 } res = _CanStoreItem_InInventorySlots(CURRENCYTOKEN_SLOT_START,CURRENCYTOKEN_SLOT_END,dest,pProto,count,false,pItem,bag,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) { if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if (count==0) + if (count == 0) { - if (no_similar_count==0) + if (no_similar_count == 0) return EQUIP_ERR_OK; if (no_space_count) @@ -10283,9 +10277,9 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 return res; } - if (count==0) + if (count == 0) { - if (no_similar_count==0) + if (no_similar_count == 0) return EQUIP_ERR_OK; if (no_space_count) @@ -10295,16 +10289,16 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 } res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) { if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if (count==0) + if (count == 0) { - if (no_similar_count==0) + if (no_similar_count == 0) return EQUIP_ERR_OK; if (no_space_count) @@ -10315,19 +10309,19 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 else // equipped bag { res = _CanStoreItem_InBag(bag,dest,pProto,count,false,false,pItem,NULL_BAG,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) res = _CanStoreItem_InBag(bag,dest,pProto,count,false,true,pItem,NULL_BAG,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) { if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if (count==0) + if (count == 0) { - if (no_similar_count==0) + if (no_similar_count == 0) return EQUIP_ERR_OK; if (no_space_count) @@ -10343,16 +10337,16 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 if (pProto->Stackable != 1) { res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,CURRENCYTOKEN_SLOT_END,dest,pProto,count,true,pItem,bag,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) { if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if (count==0) + if (count == 0) { - if (no_similar_count==0) + if (no_similar_count == 0) return EQUIP_ERR_OK; if (no_space_count) @@ -10361,16 +10355,16 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 } res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) { if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if (count==0) + if (count == 0) { - if (no_similar_count==0) + if (no_similar_count == 0) return EQUIP_ERR_OK; if (no_space_count) @@ -10383,12 +10377,12 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) { res = _CanStoreItem_InBag(i,dest,pProto,count,true,false,pItem,bag,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) continue; - if (count==0) + if (count == 0) { - if (no_similar_count==0) + if (no_similar_count == 0) return EQUIP_ERR_OK; if (no_space_count) @@ -10401,10 +10395,10 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) { res = _CanStoreItem_InBag(i,dest,pProto,count,true,true,pItem,bag,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) continue; - if (count==0) + if (count == 0) { if (no_similar_count==0) return EQUIP_ERR_OK; @@ -10423,16 +10417,16 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 { uint32 keyringSize = GetMaxKeyringSize(); res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_START+keyringSize,dest,pProto,count,false,pItem,bag,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) { if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if (count==0) + if (count == 0) { - if (no_similar_count==0) + if (no_similar_count == 0) return EQUIP_ERR_OK; if (no_space_count) @@ -10443,16 +10437,16 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 else if (pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) { res = _CanStoreItem_InInventorySlots(CURRENCYTOKEN_SLOT_START,CURRENCYTOKEN_SLOT_END,dest,pProto,count,false,pItem,bag,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) { if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if (count==0) + if (count == 0) { - if (no_similar_count==0) + if (no_similar_count == 0) return EQUIP_ERR_OK; if (no_space_count) @@ -10464,10 +10458,10 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) { res = _CanStoreItem_InBag(i,dest,pProto,count,false,false,pItem,bag,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) continue; - if (count==0) + if (count == 0) { if (no_similar_count==0) return EQUIP_ERR_OK; @@ -10485,14 +10479,14 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 // search free slot res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) { if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if (count==0) + if (count == 0) { if (no_similar_count==0) return EQUIP_ERR_OK; @@ -10505,12 +10499,12 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) { res = _CanStoreItem_InBag(i,dest,pProto,count,false,true,pItem,bag,slot); - if (res!=EQUIP_ERR_OK) + if (res != EQUIP_ERR_OK) continue; - if (count==0) + if (count == 0) { - if (no_similar_count==0) + if (no_similar_count == 0) return EQUIP_ERR_OK; if (no_space_count) @@ -10526,7 +10520,7 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 } ////////////////////////////////////////////////////////////////////////// -uint8 Player::CanStoreItems( Item **pItems,int count) const +InventoryResult Player::CanStoreItems( Item **pItems,int count) const { Item *pItem2; @@ -10613,7 +10607,7 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const ItemPrototype const *pBagProto; // item is 'one item only' - uint8 res = CanTakeMoreSimilarItems(pItem); + InventoryResult res = CanTakeMoreSimilarItems(pItem); if (res != EQUIP_ERR_OK) return res; @@ -10785,13 +10779,13 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const } ////////////////////////////////////////////////////////////////////////// -uint8 Player::CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, bool swap ) const +InventoryResult Player::CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, bool swap ) const { dest = 0; Item *pItem = Item::CreateItem( item, 1, this ); if (pItem) { - uint8 result = CanEquipItem(slot, dest, pItem, swap ); + InventoryResult result = CanEquipItem(slot, dest, pItem, swap ); delete pItem; return result; } @@ -10799,7 +10793,7 @@ uint8 Player::CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, bool swap return EQUIP_ERR_ITEM_NOT_FOUND; } -uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bool not_loading ) const +InventoryResult Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bool not_loading ) const { dest = 0; if (pItem) @@ -10816,7 +10810,7 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo return EQUIP_ERR_DONT_OWN_THAT_ITEM; // check count of items (skip for auto move for same player from bank) - uint8 res = CanTakeMoreSimilarItems(pItem); + InventoryResult res = CanTakeMoreSimilarItems(pItem); if (res != EQUIP_ERR_OK) return res; @@ -10861,14 +10855,14 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo if (eslot == NULL_SLOT) return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED; - uint8 msg = CanUseItem(pItem , not_loading); + InventoryResult msg = CanUseItem(pItem , not_loading); if (msg != EQUIP_ERR_OK) return msg; if (!swap && GetItemByPos(INVENTORY_SLOT_BAG_0, eslot)) return EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE; // if swap ignore item (equipped also) - if (uint8 res2 = CanEquipUniqueItem(pItem, swap ? eslot : NULL_SLOT)) + if (InventoryResult res2 = CanEquipUniqueItem(pItem, swap ? eslot : NULL_SLOT)) return res2; // check unique-equipped special item classes @@ -10941,7 +10935,7 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo return !swap ? EQUIP_ERR_ITEM_NOT_FOUND : EQUIP_ERR_ITEMS_CANT_BE_SWAPPED; } -uint8 Player::CanUnequipItem( uint16 pos, bool swap ) const +InventoryResult Player::CanUnequipItem( uint16 pos, bool swap ) const { // Applied only to equipped items and bank bags if (!IsEquipmentPos(pos) && !IsBagPos(pos)) @@ -10986,7 +10980,7 @@ uint8 Player::CanUnequipItem( uint16 pos, bool swap ) const return EQUIP_ERR_OK; } -uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *pItem, bool swap, bool not_loading ) const +InventoryResult Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *pItem, bool swap, bool not_loading ) const { if (!pItem) return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND; @@ -11006,7 +11000,7 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p return EQUIP_ERR_DONT_OWN_THAT_ITEM; // check count of items (skip for auto move for same player from bank) - uint8 res = CanTakeMoreSimilarItems(pItem); + InventoryResult res = CanTakeMoreSimilarItems(pItem); if (res != EQUIP_ERR_OK) return res; @@ -11168,7 +11162,7 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p return EQUIP_ERR_BANK_FULL; } -uint8 Player::CanUseItem( Item *pItem, bool not_loading ) const +InventoryResult Player::CanUseItem( Item *pItem, bool not_loading ) const { if (pItem) { @@ -11228,7 +11222,7 @@ uint8 Player::CanUseItem( Item *pItem, bool not_loading ) const if (pItem->IsBindedNotWith(this)) return EQUIP_ERR_DONT_OWN_THAT_ITEM; - uint8 msg = CanUseItem(pProto); + InventoryResult msg = CanUseItem(pProto); if (msg != EQUIP_ERR_OK) return msg; @@ -11279,7 +11273,7 @@ uint8 Player::CanUseItem( Item *pItem, bool not_loading ) const return EQUIP_ERR_ITEM_NOT_FOUND; } -uint8 Player::CanUseItem( ItemPrototype const *pProto ) const +InventoryResult Player::CanUseItem( ItemPrototype const *pProto ) const { // Used by group, function NeedBeforeGreed, to know if a prototype can be used by a player @@ -11316,7 +11310,7 @@ uint8 Player::CanUseItem( ItemPrototype const *pProto ) const return EQUIP_ERR_ITEM_NOT_FOUND; } -uint8 Player::CanUseAmmo( uint32 item ) const +InventoryResult Player::CanUseAmmo( uint32 item ) const { //DEBUG_LOG( "STORAGE: CanUseAmmo item = %u", item); if( !isAlive() ) @@ -11329,7 +11323,7 @@ uint8 Player::CanUseAmmo( uint32 item ) const if( pProto->InventoryType!= INVTYPE_AMMO ) return EQUIP_ERR_ONLY_AMMO_CAN_GO_HERE; - uint8 msg = CanUseItem(pProto); + InventoryResult msg = CanUseItem(pProto); if (msg != EQUIP_ERR_OK) return msg; @@ -11358,7 +11352,7 @@ void Player::SetAmmo( uint32 item ) // check ammo if (item) { - uint8 msg = CanUseAmmo( item ); + InventoryResult msg = CanUseAmmo( item ); if (msg != EQUIP_ERR_OK) { SendEquipError(msg, NULL, NULL, item); @@ -12226,7 +12220,7 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count ) pSrcItem->SetCount( pSrcItem->GetCount() - count ); ItemPosCountVec dest; - uint8 msg = CanStoreItem( dstbag, dstslot, dest, pNewItem, false ); + InventoryResult msg = CanStoreItem( dstbag, dstslot, dest, pNewItem, false ); if (msg != EQUIP_ERR_OK) { delete pNewItem; @@ -12246,7 +12240,7 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count ) pSrcItem->SetCount( pSrcItem->GetCount() - count ); ItemPosCountVec dest; - uint8 msg = CanBankItem( dstbag, dstslot, dest, pNewItem, false ); + InventoryResult msg = CanBankItem( dstbag, dstslot, dest, pNewItem, false ); if( msg != EQUIP_ERR_OK ) { delete pNewItem; @@ -12266,7 +12260,7 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count ) pSrcItem->SetCount( pSrcItem->GetCount() - count ); uint16 dest; - uint8 msg = CanEquipItem( dstslot, dest, pNewItem, false ); + InventoryResult msg = CanEquipItem( dstslot, dest, pNewItem, false ); if (msg != EQUIP_ERR_OK) { delete pNewItem; @@ -12311,7 +12305,7 @@ void Player::SwapItem( uint16 src, uint16 dst ) if (IsEquipmentPos(src) || IsBagPos(src)) { // bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later) - uint8 msg = CanUnequipItem( src, !IsBagPos ( src ) || IsBagPos ( dst ) || (pDstItem && pDstItem->IsBag() && ((Bag*)pDstItem)->IsEmpty())); + InventoryResult msg = CanUnequipItem( src, !IsBagPos ( src ) || IsBagPos ( dst ) || (pDstItem && pDstItem->IsBag() && ((Bag*)pDstItem)->IsEmpty())); if (msg != EQUIP_ERR_OK) { SendEquipError( msg, pSrcItem, pDstItem ); @@ -12341,7 +12335,7 @@ void Player::SwapItem( uint16 src, uint16 dst ) if(IsEquipmentPos ( dst ) || IsBagPos ( dst )) { // bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later) - uint8 msg = CanUnequipItem( dst, !IsBagPos ( dst ) || IsBagPos ( src ) || (pSrcItem->IsBag() && ((Bag*)pSrcItem)->IsEmpty())); + InventoryResult msg = CanUnequipItem( dst, !IsBagPos ( dst ) || IsBagPos ( src ) || (pSrcItem->IsBag() && ((Bag*)pSrcItem)->IsEmpty())); if(msg != EQUIP_ERR_OK) { SendEquipError( msg, pSrcItem, pDstItem ); @@ -12359,7 +12353,7 @@ void Player::SwapItem( uint16 src, uint16 dst ) if( IsInventoryPos( dst ) ) { ItemPosCountVec dest; - uint8 msg = CanStoreItem( dstbag, dstslot, dest, pSrcItem, false ); + InventoryResult msg = CanStoreItem( dstbag, dstslot, dest, pSrcItem, false ); if( msg != EQUIP_ERR_OK ) { SendEquipError( msg, pSrcItem, NULL ); @@ -12372,7 +12366,7 @@ void Player::SwapItem( uint16 src, uint16 dst ) else if( IsBankPos ( dst ) ) { ItemPosCountVec dest; - uint8 msg = CanBankItem( dstbag, dstslot, dest, pSrcItem, false); + InventoryResult msg = CanBankItem( dstbag, dstslot, dest, pSrcItem, false); if( msg != EQUIP_ERR_OK ) { SendEquipError( msg, pSrcItem, NULL ); @@ -12385,7 +12379,7 @@ void Player::SwapItem( uint16 src, uint16 dst ) else if( IsEquipmentPos ( dst ) ) { uint16 dest; - uint8 msg = CanEquipItem( dstslot, dest, pSrcItem, false ); + InventoryResult msg = CanEquipItem( dstslot, dest, pSrcItem, false ); if( msg != EQUIP_ERR_OK ) { SendEquipError( msg, pSrcItem, NULL ); @@ -12403,7 +12397,7 @@ void Player::SwapItem( uint16 src, uint16 dst ) // attempt merge to / fill target item if(!pSrcItem->IsBag() && !pDstItem->IsBag()) { - uint8 msg; + InventoryResult msg; ItemPosCountVec sDest; uint16 eDest; if( IsInventoryPos( dst ) ) @@ -12449,7 +12443,7 @@ void Player::SwapItem( uint16 src, uint16 dst ) } // impossible merge/fill, do real swap - uint8 msg; + InventoryResult msg; // check src->dest move possibility ItemPosCountVec sDest; @@ -12531,7 +12525,6 @@ void Player::SwapItem( uint16 src, uint16 dst ) ++count; } - if (count > emptyBag->GetBagSize()) { // too small targeted bag @@ -12666,7 +12659,7 @@ void Player::RemoveItemFromBuyBackSlot( uint32 slot, bool del ) } } -void Player::SendEquipError( uint8 msg, Item* pItem, Item *pItem2, uint32 itemid /*= 0*/ ) const +void Player::SendEquipError( InventoryResult msg, Item* pItem, Item *pItem2, uint32 itemid /*= 0*/ ) const { DEBUG_LOG( "WORLD: Sent SMSG_INVENTORY_CHANGE_FAILURE (%u)", msg); WorldPacket data(SMSG_INVENTORY_CHANGE_FAILURE, 1+8+8+1); @@ -12736,7 +12729,7 @@ void Player::SendEquipError( uint8 msg, Item* pItem, Item *pItem2, uint32 itemid GetSession()->SendPacket(&data); } -void Player::SendBuyError( uint8 msg, Creature* pCreature, uint32 item, uint32 param ) +void Player::SendBuyError( BuyResult msg, Creature* pCreature, uint32 item, uint32 param ) { DEBUG_LOG( "WORLD: Sent SMSG_BUY_FAILED" ); WorldPacket data( SMSG_BUY_FAILED, (8+4+4+1) ); @@ -12748,7 +12741,7 @@ void Player::SendBuyError( uint8 msg, Creature* pCreature, uint32 item, uint32 p GetSession()->SendPacket(&data); } -void Player::SendSellError( uint8 msg, Creature* pCreature, uint64 guid, uint32 param ) +void Player::SendSellError( SellResult msg, Creature* pCreature, uint64 guid, uint32 param ) { DEBUG_LOG( "WORLD: Sent SMSG_SELL_ITEM" ); WorldPacket data( SMSG_SELL_ITEM,(8+8+(param?4:0)+1)); // last check 2.0.10 @@ -13637,12 +13630,12 @@ void Player::OnGossipSelect(WorldObject* pSource, uint32 gossipListId, uint32 me } } - GossipMenuItemData pMenuData = gossipmenu.GetItemData(gossipListId); - switch(gossipOptionId) { case GOSSIP_OPTION_GOSSIP: { + GossipMenuItemData pMenuData = gossipmenu.GetItemData(gossipListId); + if (pMenuData.m_gAction_poi) PlayerTalkClass->SendPointOfInterest(pMenuData.m_gAction_poi); @@ -14172,7 +14165,7 @@ bool Player::CanRewardQuest(Quest const *pQuest, uint32 reward, bool msg) const if (pQuest->RewChoiceItemId[reward]) { ItemPosCountVec dest; - uint8 res = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward] ); + InventoryResult res = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward] ); if (res != EQUIP_ERR_OK) { SendEquipError(res, NULL, NULL, pQuest->RewChoiceItemId[reward]); @@ -14188,7 +14181,7 @@ bool Player::CanRewardQuest(Quest const *pQuest, uint32 reward, bool msg) const if (pQuest->RewItemId[i]) { ItemPosCountVec dest; - uint8 res = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewItemId[i], pQuest->RewItemCount[i] ); + InventoryResult res = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewItemId[i], pQuest->RewItemCount[i] ); if (res != EQUIP_ERR_OK) { SendEquipError(res, NULL, NULL); @@ -14900,7 +14893,7 @@ bool Player::CanGiveQuestSourceItem( Quest const *pQuest, ItemPosCountVec* dest if( count <= 0 ) count = 1; - uint8 msg; + InventoryResult msg; if (!dest) { ItemPosCountVec destTemp; @@ -14952,7 +14945,7 @@ bool Player::TakeQuestSourceItem( uint32 quest_id, bool msg ) // exist one case when destroy source quest item not possible: // non un-equippable item (equipped non-empty bag, for example) - uint8 res = CanUnequipItems(srcitem,count); + InventoryResult res = CanUnequipItems(srcitem,count); if(res != EQUIP_ERR_OK) { if(msg) @@ -15527,7 +15520,7 @@ void Player::SendQuestReward( Quest const *pQuest, uint32 XP, Object * questGive GetSession()->SendPacket( &data ); } -void Player::SendQuestFailed( uint32 quest_id, InventoryChangeFailure reason) +void Player::SendQuestFailed( uint32 quest_id, InventoryResult reason) { if( quest_id ) { @@ -19881,7 +19874,7 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorGuid, uint32 vendorslot, uin if ((bag == NULL_BAG && slot == NULL_SLOT) || IsInventoryPos(bag, slot)) { ItemPosCountVec dest; - uint8 msg = CanStoreNewItem(bag, slot, dest, item, totalCount); + InventoryResult msg = CanStoreNewItem(bag, slot, dest, item, totalCount); if (msg != EQUIP_ERR_OK) { SendEquipError(msg, NULL, NULL, item); @@ -19904,7 +19897,7 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorGuid, uint32 vendorslot, uin } uint16 dest; - uint8 msg = CanEquipNewItem(slot, dest, item, false); + InventoryResult msg = CanEquipNewItem(slot, dest, item, false); if (msg != EQUIP_ERR_OK) { SendEquipError(msg, NULL, NULL, item); @@ -22167,7 +22160,7 @@ void Player::AutoStoreLoot(Loot& loot, bool broadcast, uint8 bag, uint8 slot) LootItem* lootItem = loot.LootItemInSlot(i,this); ItemPosCountVec dest; - uint8 msg = CanStoreNewItem (bag,slot,dest,lootItem->itemid,lootItem->count); + InventoryResult msg = CanStoreNewItem(bag,slot,dest,lootItem->itemid,lootItem->count); if(msg != EQUIP_ERR_OK && slot != NULL_SLOT) msg = CanStoreNewItem( bag, NULL_SLOT,dest,lootItem->itemid,lootItem->count); if( msg != EQUIP_ERR_OK && bag != NULL_BAG) @@ -22348,12 +22341,12 @@ uint32 Player::GetPhaseMaskForSpawn() const return PHASEMASK_NORMAL; } -uint8 Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limit_count) const +InventoryResult Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limit_count) const { ItemPrototype const* pProto = pItem->GetProto(); // proto based limitations - if(uint8 res = CanEquipUniqueItem(pProto,eslot,limit_count)) + if(InventoryResult res = CanEquipUniqueItem(pProto,eslot,limit_count)) return res; // check unique-equipped on gems @@ -22374,14 +22367,14 @@ uint8 Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limit_count) c uint32 gem_limit_count = !pItem->IsEquipped() && pGem->ItemLimitCategory ? pItem->GetGemCountWithLimitCategory(pGem->ItemLimitCategory) : 1; - if(uint8 res = CanEquipUniqueItem(pGem, eslot,gem_limit_count)) + if(InventoryResult res = CanEquipUniqueItem(pGem, eslot,gem_limit_count)) return res; } return EQUIP_ERR_OK; } -uint8 Player::CanEquipUniqueItem( ItemPrototype const* itemProto, uint8 except_slot, uint32 limit_count) const +InventoryResult Player::CanEquipUniqueItem( ItemPrototype const* itemProto, uint8 except_slot, uint32 limit_count) const { // check unique-equipped on item if (itemProto->Flags & ITEM_FLAG_UNIQUE_EQUIPPED) @@ -23065,8 +23058,8 @@ void Player::_SaveEquipmentSets() stmt.addUInt32(GetGUIDLow()); stmt.addUInt64(eqset.Guid); stmt.addUInt32(index); - stmt.addString(eqset.IconName); stmt.addString(eqset.Name); + stmt.addString(eqset.IconName); for (int i = 0; i < EQUIPMENT_SLOT_END; ++i) stmt.addUInt32(eqset.Items[i]); @@ -23776,7 +23769,7 @@ uint8 Player::GetTalentsCount(uint8 tab) if(talent.m_talentEntry->TalentTab != talentTabId) continue; - ++talentCount; + talentCount += talent.currentRank + 1; } return talentCount; } diff --git a/src/game/Player.h b/src/game/Player.h index 310d9a803..b93be6bcf 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -51,13 +51,11 @@ class PlayerMenu; class UpdateMask; class SpellCastTargets; class PlayerSocial; -class Vehicle; class DungeonPersistentState; class Spell; class Item; struct AreaTrigger; -// Playerbot mod #include "playerbot/PlayerbotMgr.h" #include "playerbot/PlayerbotAI.h" @@ -1212,13 +1210,13 @@ class MANGOS_DLL_SPEC Player : public Unit bool CanNoReagentCast(SpellEntry const* spellInfo) const; bool HasItemOrGemWithIdEquipped( uint32 item, uint32 count, uint8 except_slot = NULL_SLOT) const; bool HasItemOrGemWithLimitCategoryEquipped( uint32 limitCategory, uint32 count, uint8 except_slot = NULL_SLOT) const; - uint8 CanTakeMoreSimilarItems(Item* pItem) const { return _CanTakeMoreSimilarItems(pItem->GetEntry(), pItem->GetCount(), pItem); } - uint8 CanTakeMoreSimilarItems(uint32 entry, uint32 count) const { return _CanTakeMoreSimilarItems(entry, count, NULL); } - uint8 CanStoreNewItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 item, uint32 count, uint32* no_space_count = NULL ) const + InventoryResult CanTakeMoreSimilarItems(Item* pItem) const { return _CanTakeMoreSimilarItems(pItem->GetEntry(), pItem->GetCount(), pItem); } + InventoryResult CanTakeMoreSimilarItems(uint32 entry, uint32 count) const { return _CanTakeMoreSimilarItems(entry, count, NULL); } + InventoryResult CanStoreNewItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 item, uint32 count, uint32* no_space_count = NULL ) const { return _CanStoreItem(bag, slot, dest, item, count, NULL, false, no_space_count ); } - uint8 CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, Item *pItem, bool swap = false ) const + InventoryResult CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, Item *pItem, bool swap = false ) const { if(!pItem) return EQUIP_ERR_ITEM_NOT_FOUND; @@ -1226,19 +1224,19 @@ class MANGOS_DLL_SPEC Player : public Unit return _CanStoreItem( bag, slot, dest, pItem->GetEntry(), count, pItem, swap, NULL ); } - uint8 CanStoreItems( Item **pItem,int count) const; - uint8 CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, bool swap ) const; - uint8 CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bool not_loading = true ) const; - - uint8 CanEquipUniqueItem( Item * pItem, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1 ) const; - uint8 CanEquipUniqueItem( ItemPrototype const* itemProto, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1 ) const; - uint8 CanUnequipItems( uint32 item, uint32 count ) const; - uint8 CanUnequipItem( uint16 src, bool swap ) const; - uint8 CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, Item *pItem, bool swap, bool not_loading = true ) const; - uint8 CanUseItem( Item *pItem, bool not_loading = true ) const; + InventoryResult CanStoreItems( Item **pItem,int count) const; + InventoryResult CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, bool swap ) const; + InventoryResult CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bool not_loading = true ) const; + + InventoryResult CanEquipUniqueItem( Item * pItem, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1 ) const; + InventoryResult CanEquipUniqueItem( ItemPrototype const* itemProto, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1 ) const; + InventoryResult CanUnequipItems( uint32 item, uint32 count ) const; + InventoryResult CanUnequipItem( uint16 src, bool swap ) const; + InventoryResult CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, Item *pItem, bool swap, bool not_loading = true ) const; + InventoryResult CanUseItem( Item *pItem, bool not_loading = true ) const; bool HasItemTotemCategory( uint32 TotemCategory ) const; - uint8 CanUseItem( ItemPrototype const *pItem ) const; - uint8 CanUseAmmo( uint32 item ) const; + InventoryResult CanUseItem( ItemPrototype const *pItem ) const; + InventoryResult CanUseAmmo( uint32 item ) const; Item* StoreNewItem( ItemPosCountVec const& pos, uint32 item, bool update,int32 randomPropertyId = 0, AllowedLooterSet* allowedLooters = NULL ); Item* StoreItem( ItemPosCountVec const& pos, Item *pItem, bool update ); Item* EquipNewItem( uint16 pos, uint32 item, bool update ); @@ -1352,8 +1350,8 @@ class MANGOS_DLL_SPEC Player : public Unit //end of helpers. ///end of Flying mounts everywhere mode - uint8 _CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count = NULL) const; - uint8 _CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item *pItem = NULL, bool swap = false, uint32* no_space_count = NULL ) const; + InventoryResult _CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count = NULL) const; + InventoryResult _CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item *pItem = NULL, bool swap = false, uint32* no_space_count = NULL ) const; void ApplyEquipCooldown( Item * pItem ); void SetAmmo( uint32 item ); @@ -1388,9 +1386,9 @@ class MANGOS_DLL_SPEC Player : public Unit void TakeExtendedCost(uint32 extendedCostId, uint32 count); uint32 GetMaxKeyringSize() const { return KEYRING_SLOT_END-KEYRING_SLOT_START; } - void SendEquipError( uint8 msg, Item* pItem, Item *pItem2 = NULL, uint32 itemid = 0 ) const; - void SendBuyError( uint8 msg, Creature* pCreature, uint32 item, uint32 param ); - void SendSellError( uint8 msg, Creature* pCreature, uint64 guid, uint32 param ); + void SendEquipError( InventoryResult msg, Item* pItem, Item *pItem2 = NULL, uint32 itemid = 0 ) const; + void SendBuyError( BuyResult msg, Creature* pCreature, uint32 item, uint32 param ); + void SendSellError( SellResult msg, Creature* pCreature, uint64 guid, uint32 param ); void AddWeaponProficiency(uint32 newflag) { m_WeaponProficiency |= newflag; } void AddArmorProficiency(uint32 newflag) { m_ArmorProficiency |= newflag; } uint32 GetWeaponProficiency() const { return m_WeaponProficiency; } @@ -1550,7 +1548,7 @@ class MANGOS_DLL_SPEC Player : public Unit void SendQuestCompleteEvent(uint32 quest_id); void SendQuestReward( Quest const *pQuest, uint32 XP, Object* questGiver ); - void SendQuestFailed( uint32 quest_id, InventoryChangeFailure reason = EQUIP_ERR_OK); + void SendQuestFailed( uint32 quest_id, InventoryResult reason = EQUIP_ERR_OK); void SendQuestTimerFailed( uint32 quest_id ); void SendCanTakeQuestResponse( uint32 msg ) const; void SendQuestConfirmAccept(Quest const* pQuest, Player* pReceiver); @@ -1567,6 +1565,11 @@ class MANGOS_DLL_SPEC Player : public Unit void AddTimedQuest( uint32 quest_id ) { m_timedquests.insert(quest_id); } void RemoveTimedQuest( uint32 quest_id ) { m_timedquests.erase(quest_id); } + void chompAndTrim(std::string& str); + bool getNextQuestId(const std::string& pString, unsigned int& pStartPos, unsigned int& pId); + void skill(std::list& m_spellsToLearn); + bool requiredQuests(const char* pQuestIdString); + /*********************************************************/ /*** LOAD SYSTEM ***/ /*********************************************************/ @@ -2483,9 +2486,6 @@ class MANGOS_DLL_SPEC Player : public Unit bool canSeeSpellClickOn(Creature const* creature) const; - // Playerbot mod: - // A Player can either have a playerbotMgr (to manage its bots), or have playerbotAI (if it is a bot), or - // neither. Code that enables bots must create the playerbotMgr and set it using SetPlayerbotMgr. void SetPlayerbotAI(PlayerbotAI* ai) { m_playerbotAI = ai; } PlayerbotAI* GetPlayerbotAI() { return m_playerbotAI; } void SetPlayerbotMgr(PlayerbotMgr* mgr) { m_playerbotMgr = mgr; } @@ -2736,9 +2736,9 @@ class MANGOS_DLL_SPEC Player : public Unit private: void _HandleDeadlyPoison(Unit* Target, WeaponAttackType attType, SpellEntry const *spellInfo); // internal common parts for CanStore/StoreItem functions - uint8 _CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool swap, Item *pSrcItem ) const; - uint8 _CanStoreItem_InBag( uint8 bag, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool merge, bool non_specialized, Item *pSrcItem, uint8 skip_bag, uint8 skip_slot ) const; - uint8 _CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool merge, Item *pSrcItem, uint8 skip_bag, uint8 skip_slot ) const; + InventoryResult _CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool swap, Item *pSrcItem ) const; + InventoryResult _CanStoreItem_InBag( uint8 bag, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool merge, bool non_specialized, Item *pSrcItem, uint8 skip_bag, uint8 skip_slot ) const; + InventoryResult _CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool merge, Item *pSrcItem, uint8 skip_bag, uint8 skip_slot ) const; Item* _StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, bool update ); void UpdateKnownCurrencies(uint32 itemId, bool apply); @@ -2771,7 +2771,6 @@ class MANGOS_DLL_SPEC Player : public Unit GridReference m_gridRef; MapReference m_mapRef; - // Playerbot mod: PlayerbotAI* m_playerbotAI; PlayerbotMgr* m_playerbotMgr; uint32 m_levelAtLoading; diff --git a/src/game/PlayerBot/PlayerbotMgr.cpp b/src/game/PlayerBot/PlayerbotMgr.cpp index 535d3ce6d..0896bf59f 100644 --- a/src/game/PlayerBot/PlayerbotMgr.cpp +++ b/src/game/PlayerBot/PlayerbotMgr.cpp @@ -425,8 +425,6 @@ void PlayerbotMgr::Stay() { } - -// Playerbot mod: logs out a Playerbot. void PlayerbotMgr::LogoutPlayerBot(uint64 guid) { Player* bot = GetPlayerBot(guid); @@ -455,7 +453,6 @@ void PlayerbotMgr::LogoutPlayerBot(uint64 guid) } } -// Playerbot mod: Gets a player bot Player object for this WorldSession master Player* PlayerbotMgr::GetPlayerBot(uint64 playerGuid) const { HashMapHolder < Player > ::MapType& m = sObjectAccessor.GetPlayers(); diff --git a/src/game/PlayerStuff.cpp b/src/game/PlayerStuff.cpp index 71a60b817..84f795224 100644 --- a/src/game/PlayerStuff.cpp +++ b/src/game/PlayerStuff.cpp @@ -20,7 +20,7 @@ #include "Chat.h" #include "ArenaTeam.h" #include "SpellMgr.h" -#include "PlayerBot/PlayerbotAI.h" +#include "playerbot/PlayerbotAI.h" void Player::GiveMeBestItemForMyLevel() { diff --git a/src/game/PointMovementGenerator.cpp b/src/game/PointMovementGenerator.cpp index c708bb113..68dfea5a2 100644 --- a/src/game/PointMovementGenerator.cpp +++ b/src/game/PointMovementGenerator.cpp @@ -48,7 +48,7 @@ void PointMovementGenerator::Initialize(T &unit) } if (unit.GetTypeId() == TYPEID_UNIT && ((Creature*)&unit)->CanFly()) - ((Creature&)unit).AddSplineFlag(SPLINEFLAG_UNKNOWN7); + ((Creature&)unit).AddSplineFlag(SPLINEFLAG_FLYING); } template diff --git a/src/game/QueryHandler.cpp b/src/game/QueryHandler.cpp index f8bfab281..582bda47a 100644 --- a/src/game/QueryHandler.cpp +++ b/src/game/QueryHandler.cpp @@ -353,7 +353,7 @@ void WorldSession::HandleNpcTextQueryOpcode( WorldPacket & recv_data ) GossipText const* pGossip = sObjectMgr.GetGossipText(textID); - WorldPacket data( SMSG_NPC_TEXT_UPDATE, 100 ); // guess size + WorldPacket data( SMSG_NPC_TEXT_UPDATE, 100 ); // guess size data << textID; if (!pGossip) diff --git a/src/game/QuestHandler.cpp b/src/game/QuestHandler.cpp index 63633a131..dc11c3f85 100644 --- a/src/game/QuestHandler.cpp +++ b/src/game/QuestHandler.cpp @@ -29,7 +29,7 @@ #include "ObjectAccessor.h" #include "ScriptMgr.h" #include "Group.h" -#include "PlayerBot/PlayerbotAI.h" +#include "playerbot/PlayerbotAI.h" void WorldSession::HandleQuestgiverStatusQueryOpcode( WorldPacket & recv_data ) { diff --git a/src/game/RandomMovementGenerator.cpp b/src/game/RandomMovementGenerator.cpp index d78e4c1a2..d65cadfea 100644 --- a/src/game/RandomMovementGenerator.cpp +++ b/src/game/RandomMovementGenerator.cpp @@ -82,7 +82,7 @@ RandomMovementGenerator::_setRandomLocation(Creature &creature) if (is_air_ok) { i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); - creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); + creature.AddSplineFlag(SPLINEFLAG_FLYING); } //else if (is_water_ok) // Swimming mode to be done with more than this check else @@ -99,7 +99,7 @@ void RandomMovementGenerator::Initialize(Creature &creature) return; if (creature.CanFly()) - creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); + creature.AddSplineFlag(SPLINEFLAG_FLYING); else creature.AddSplineFlag(SPLINEFLAG_WALKMODE); @@ -153,7 +153,7 @@ bool RandomMovementGenerator::Update(Creature &creature, const uint32 if (i_nextMoveTime.Passed()) { if (creature.CanFly()) - creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); + creature.AddSplineFlag(SPLINEFLAG_FLYING); else creature.AddSplineFlag(SPLINEFLAG_WALKMODE); diff --git a/src/game/ReputationMgr.cpp b/src/game/ReputationMgr.cpp index 63345cf41..264bdbf74 100644 --- a/src/game/ReputationMgr.cpp +++ b/src/game/ReputationMgr.cpp @@ -160,7 +160,7 @@ void ReputationMgr::SendInitialReputations() RepListID a = 0; - for (FactionStateList::const_iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr) + for (FactionStateList::iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr) { // fill in absent fields for (; a != itr->first; a++) @@ -173,6 +173,8 @@ void ReputationMgr::SendInitialReputations() data << uint8 (itr->second.Flags); data << uint32 (itr->second.Standing); + itr->second.needSend = false; + ++a; } diff --git a/src/game/SQLStorages.cpp b/src/game/SQLStorages.cpp index 22eca852a..d1f6501b8 100644 --- a/src/game/SQLStorages.cpp +++ b/src/game/SQLStorages.cpp @@ -21,8 +21,8 @@ #include "Database/SQLStorageImpl.h" #include "Database/DatabaseEnv.h" -const char CreatureInfosrcfmt[]="iiiiiiiiiisssiiiiibiiiiiifffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiiiis"; -const char CreatureInfodstfmt[]="iiiiiiiiiisssiiiiibiiiiiifffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiiiii"; +const char CreatureInfosrcfmt[]="iiiiiiiiiisssiiiiibiiiiiifffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiiiiis"; +const char CreatureInfodstfmt[]="iiiiiiiiiisssiiiiibiiiiiifffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiiiiii"; const char CreatureDataAddonInfofmt[]="iiibbiis"; const char CreatureModelfmt[]="iffbii"; const char CreatureInfoAddonInfofmt[]="iiibbiis"; diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 45a085021..7aafa16e1 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -451,6 +451,64 @@ bool Spell::FillCustomTargetMap(SpellEffectIndex i, UnitList &targetUnitMap) else radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); + //Corpse Explosion + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && m_spellInfo->SpellIconID == 1737) + { + Unit* unitTarget = NULL; + + targetUnitMap.remove(m_caster); + unitTarget = m_targets.getUnitTarget(); + + if (unitTarget) + { + // Cast on corpses... + if ((unitTarget->getDeathState() == CORPSE && !unitTarget->IsTaxiFlying() && + (unitTarget->GetDisplayId() == unitTarget->GetNativeDisplayId()) && m_caster->IsWithinDistInMap(unitTarget, radius) && + (unitTarget->GetCreatureTypeMask() & CREATURE_TYPEMASK_MECHANICAL_OR_ELEMENTAL) == 0) || + // ...or own Risen Ghoul pet - self explode effect + (unitTarget->GetEntry() == 26125 && unitTarget->GetCreatorGuid() == m_caster->GetObjectGuid()) ) + { + targetUnitMap.push_back(unitTarget); + return true; + } + } + + WorldObject* result = FindCorpseUsing (); + if (result) + { + switch(result->GetTypeId()) + { + case TYPEID_UNIT: + case TYPEID_PLAYER: + targetUnitMap.push_back((Unit*)result); + break; + case TYPEID_CORPSE: + m_targets.setCorpseTarget((Corpse*)result); + if (Player* owner = ObjectAccessor::FindPlayer(((Corpse*)result)->GetOwnerGuid())) + targetUnitMap.push_back(owner); + break; + default: + targetUnitMap.push_back((Unit*)m_caster); + break; + }; + } + + if (targetUnitMap.empty()) + { + // no valid targets, clear cooldown at fail + if (m_caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id, true); + SendCastResult(SPELL_FAILED_NO_VALID_TARGETS); + finish(false); + return false; + } + + // OK, we have all possible targets, let's sort them by distance from m_caster and keep the closest one +// targetUnitMap.sort(TargetDistanceOrder(m_caster)); + targetUnitMap.resize(1); + return true; + } + // Resulting effect depends on spell that we want to cast switch (m_spellInfo->Id) { @@ -1097,7 +1155,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) // recheck for visibility of target if ((m_spellInfo->speed > 0.0f || (m_spellInfo->EffectImplicitTargetA[0] == TARGET_CHAIN_DAMAGE && GetSpellCastTime(m_spellInfo, this) > 0)) && - !unit->isVisibleForOrDetect(m_caster, m_caster, false)) + (!unit->isVisibleForOrDetect(m_caster, m_caster, false) && !m_IsTriggeredSpell)) { caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); missInfo = SPELL_MISS_EVADE; @@ -1117,7 +1175,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) { // can cause back attack (if detected) if (!(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) && !IsPositiveSpell(m_spellInfo->Id) && - m_caster->isVisibleForOrDetect(unit, unit, false)) + (m_caster->isVisibleForOrDetect(unit, unit, false) && !m_IsTriggeredSpell)) { if (!unit->isInCombat() && unit->GetTypeId() != TYPEID_PLAYER && ((Creature*)unit)->AI()) ((Creature*)unit)->AI()->AttackedBy(real_caster); @@ -1270,13 +1328,13 @@ void Spell::DoSpellHitOnUnit(Unit *unit, uint32 effectMask) return; } - if (unit->GetTypeId() == TYPEID_PLAYER) + if (unit->GetTypeId() == TYPEID_PLAYER && unit->IsInWorld()) { ((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, m_spellInfo->Id); ((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, m_spellInfo->Id); } - if (realCaster && realCaster->GetTypeId() == TYPEID_PLAYER) + if (realCaster && realCaster->GetTypeId() == TYPEID_PLAYER && realCaster->IsInWorld()) ((Player*)realCaster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2, m_spellInfo->Id, 0, unit); if (realCaster && realCaster != unit) @@ -1295,7 +1353,7 @@ void Spell::DoSpellHitOnUnit(Unit *unit, uint32 effectMask) { // for delayed spells ignore not visible explicit target if (m_spellInfo->speed > 0.0f && unit == m_targets.getUnitTarget() && - !unit->isVisibleForOrDetect(m_caster, m_caster, false)) + (!unit->isVisibleForOrDetect(m_caster, m_caster, false) && !m_IsTriggeredSpell)) { realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); ResetEffectDamageAndHeal(); @@ -1373,6 +1431,7 @@ void Spell::DoSpellHitOnUnit(Unit *unit, uint32 effectMask) { m_spellAuraHolder = CreateSpellAuraHolder(m_spellInfo, unit, realCaster, m_CastItem); m_spellAuraHolder->setDiminishGroup(m_diminishGroup); + m_spellAuraHolder->SetInUse(true); } else m_spellAuraHolder = NULL; @@ -1426,9 +1485,23 @@ void Spell::DoSpellHitOnUnit(Unit *unit, uint32 effectMask) } unit->AddSpellAuraHolder(m_spellAuraHolder); + m_spellAuraHolder->SetInUse(false); } else - delete m_spellAuraHolder; + { + if (!m_spellAuraHolder || m_spellAuraHolder->IsDeleted()) + return; + + m_spellAuraHolder->SetInUse(false); + + if (m_spellAuraHolder->IsInUse()) + { + m_spellAuraHolder->SetDeleted(); + unit->AddSpellAuraHolderToRemoveList(m_spellAuraHolder); + } + else + delete m_spellAuraHolder; + } } } @@ -1757,6 +1830,12 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& unMaxTargets = 2; break; } + case SPELLFAMILY_DEATHKNIGHT: + { + if (m_spellInfo->SpellIconID == 1737) // Corpse Explosion + unMaxTargets = 1; + break; + } case SPELLFAMILY_PALADIN: if (m_spellInfo->Id == 20424) // Seal of Command (2 more target for single targeted spell) { @@ -3127,8 +3206,8 @@ void Spell::prepare(SpellCastTargets const* targets, Aura* triggeredByAura) TriggerGlobalCooldown(); } - // execute triggered without cast time explicitly in call point - else if(m_timer == 0) + // execute triggered or without cast time explicitly in call point + else if(m_timer == 0 || m_IsTriggeredSpell) cast(true); // else triggered with cast time will execute execute at next tick or later // without adding to cast type slot @@ -3486,21 +3565,15 @@ void Spell::handle_immediate() _handle_immediate_phase(); for(TargetList::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - m_UniqueTargetBuffer.push(*ihit); - - while(!m_UniqueTargetBuffer.empty()) { - DoAllEffectOnTarget(&m_UniqueTargetBuffer.front()); - m_UniqueTargetBuffer.pop(); + TargetInfo buffer = *ihit; + DoAllEffectOnTarget(&buffer); } for(GOTargetList::iterator ihit = m_UniqueGOTargetInfo.begin(); ihit != m_UniqueGOTargetInfo.end(); ++ihit) - m_UniqueGOTargetBuffer.push(*ihit); - - while(!m_UniqueGOTargetBuffer.empty()) { - DoAllEffectOnTarget(&m_UniqueGOTargetBuffer.front()); - m_UniqueGOTargetBuffer.pop(); + GOTargetInfo buffer = *ihit; + DoAllEffectOnTarget(&buffer); } // spell is finished, perform some last features of the spell here @@ -3593,12 +3666,9 @@ void Spell::_handle_immediate_phase() // process items for(ItemTargetList::iterator ihit = m_UniqueItemInfo.begin(); ihit != m_UniqueItemInfo.end(); ++ihit) - m_UniqueItemBuffer.push(*ihit); - - while(!m_UniqueItemBuffer.empty()) { - DoAllEffectOnTarget(&m_UniqueItemBuffer.front()); - m_UniqueItemBuffer.pop(); + ItemTargetInfo buffer = *ihit; + DoAllEffectOnTarget(&buffer); } // process ground @@ -3669,7 +3739,7 @@ void Spell::update(uint32 difftime) { if(m_timer) { - if (m_targets.getUnitTarget() && m_targets.getUnitTarget()->isAlive() && !m_targets.getUnitTarget()->isVisibleForOrDetect(m_caster, m_caster, false)) + if (m_targets.getUnitTarget() && m_targets.getUnitTarget()->isAlive() && !m_targets.getUnitTarget()->isVisibleForOrDetect(m_caster, m_caster, false) && !m_IsTriggeredSpell ) cancel(); if(difftime >= m_timer) @@ -6002,7 +6072,7 @@ SpellCastResult Spell::CheckPetCast(Unit* target) return SPELL_FAILED_BAD_TARGETS; } } - else if (!_target->isTargetableForAttack() || !_target->isVisibleForOrDetect(m_caster,m_caster,true)) + else if (!_target->isTargetableForAttack() || (!_target->isVisibleForOrDetect(m_caster,m_caster,true) && !m_IsTriggeredSpell)) { DEBUG_LOG("Charmed creature attempt to cast spell %d, but target (guid %u) is not targetable or not detectable",m_spellInfo->Id,target->GetObjectGuid().GetRawValue()); return SPELL_FAILED_BAD_TARGETS; // guessed error @@ -6679,7 +6749,7 @@ SpellCastResult Spell::CheckItems() } ItemPosCountVec dest; - uint8 msg = p_caster->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, m_spellInfo->EffectItemType[i], 1 ); + InventoryResult msg = p_caster->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, m_spellInfo->EffectItemType[i], 1 ); if (msg != EQUIP_ERR_OK ) { p_caster->SendEquipError( msg, NULL, NULL, m_spellInfo->EffectItemType[i] ); @@ -6709,7 +6779,7 @@ SpellCastResult Spell::CheckItems() if (isVellumTarget && m_spellInfo->EffectItemType[i]) { ItemPosCountVec dest; - uint8 msg = p_caster->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, m_spellInfo->EffectItemType[i], 1 ); + InventoryResult msg = p_caster->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, m_spellInfo->EffectItemType[i], 1 ); if (msg != EQUIP_ERR_OK) { p_caster->SendEquipError( msg, NULL, NULL ); diff --git a/src/game/Spell.h b/src/game/Spell.h index 94c87ac7a..6c0096625 100644 --- a/src/game/Spell.h +++ b/src/game/Spell.h @@ -618,15 +618,6 @@ class Spell GOTargetList m_UniqueGOTargetInfo; ItemTargetList m_UniqueItemInfo; - // Targets double buffering - typedef std::queue TargetBuffer; - typedef std::queue GOTargetBuffer; - typedef std::queue ItemTargetBuffer; - - TargetBuffer m_UniqueTargetBuffer; - GOTargetBuffer m_UniqueGOTargetBuffer; - ItemTargetBuffer m_UniqueItemBuffer; - void AddUnitTarget(Unit* target, SpellEffectIndex effIndex); void AddUnitTarget(uint64 unitGUID, SpellEffectIndex effIndex); void AddGOTarget(GameObject* target, SpellEffectIndex effIndex); diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index ca0b4e0c0..e6e71dbd9 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -458,7 +458,7 @@ m_isPersistent(false), m_in_use(0), m_spellAuraHolder(holder) uint32 _periodicTime = m_modifier.periodictime; // Calculate new periodic timer - int32 ticks = oldDuration / _periodicTime; + int32 ticks = oldDuration / _periodicTime + 1; _periodicTime = new_duration / ticks; @@ -2070,6 +2070,14 @@ void Aura::HandleAuraDummy(bool apply, bool Real) case 43873: // Headless Horseman Laugh target->PlayDistanceSound(11965); return; + case 45963: // Call Alliance Deserter + { + // Escorting Alliance Deserter + if (target->GetMiniPet()) + target->CastSpell(target, 45957, true); + + return; + } case 46699: // Requires No Ammo if (target->GetTypeId() == TYPEID_PLAYER) // not use ammo and not allow use @@ -2577,6 +2585,12 @@ void Aura::HandleAuraDummy(bool apply, bool Real) target->DealDamage(target, target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); return; } + case 45963: // Call Alliance Deserter + { + // Escorting Alliance Deserter + target->RemoveAurasDueToSpell(45957); + return; + } case 46308: // Burning Winds { // casted only at creatures at spawn @@ -3216,7 +3230,7 @@ void Aura::HandleAuraMounted(bool apply, bool Real) if (minfo) display_id = minfo->modelid; - target->Mount(display_id, GetId(), ci->VehicleId, GetMiscValue()); + target->Mount(display_id, GetId(), ci->vehicleId, GetMiscValue()); } else { @@ -3984,7 +3998,7 @@ void Aura::HandleChannelDeathItem(bool apply, bool Real) uint32 count = m_modifier.m_amount; ItemPosCountVec dest; - uint8 msg = ((Player*)caster)->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, spellInfo->EffectItemType[m_effIndex], count, &noSpaceForCount); + InventoryResult msg = ((Player*)caster)->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, spellInfo->EffectItemType[m_effIndex], count, &noSpaceForCount); if( msg != EQUIP_ERR_OK ) { count-=noSpaceForCount; @@ -6143,10 +6157,18 @@ void Aura::HandleAuraModIncreaseMaxHealth(bool apply, bool /*Real*/) void Aura::HandleAuraModIncreaseEnergy(bool apply, bool Real) { - Unit *target = GetTarget(); + Unit* target = GetTarget(); + + if (!target) + return; + Powers powerType = target->getPowerType(); + if(int32(powerType) != m_modifier.m_miscvalue) - return; + { + DEBUG_LOG("HandleAuraModIncreaseEnergy: unit %u change energy %u but current type %u", target->GetObjectGuid().GetCounter(), m_modifier.m_miscvalue, powerType); + powerType = Powers(m_modifier.m_miscvalue); + } UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + powerType); @@ -6168,13 +6190,22 @@ void Aura::HandleAuraModIncreaseEnergy(bool apply, bool Real) void Aura::HandleAuraModIncreaseEnergyPercent(bool apply, bool /*Real*/) { - Powers powerType = GetTarget()->getPowerType(); - if(int32(powerType) != m_modifier.m_miscvalue) + Unit* target = GetTarget(); + + if (!target) return; + Powers powerType = target->getPowerType(); + + if(int32(powerType) != m_modifier.m_miscvalue) + { + DEBUG_LOG("HandleAuraModIncreaseEnergy: unit %u change energy %u but current type %u", target->GetObjectGuid().GetCounter(), m_modifier.m_miscvalue, powerType); + powerType = Powers(m_modifier.m_miscvalue); + } + UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + powerType); - GetTarget()->HandleStatModifier(unitMod, TOTAL_PCT, float(m_modifier.m_amount), apply); + target->HandleStatModifier(unitMod, TOTAL_PCT, float(m_modifier.m_amount), apply); } void Aura::HandleAuraModIncreaseHealthPercent(bool apply, bool /*Real*/) @@ -8140,6 +8171,15 @@ void Aura::PeriodicDummyTick() case 52441: // Cool Down target->CastSpell(target, 52443, true); return; + case 53035: // Summon Anub'ar Champion (Azjol Nerub) + target->CastSpell(target, 53014, true); + return; + case 53036: // Summon Anub'ar Necromancer (Azjol Nerub) + target->CastSpell(target, 53015, true); + return; + case 53037: // Summon Crypt Fiend (Azjol Nerub) + target->CastSpell(target, 53016, true); + return; case 53520: // Carrion Beetles target->CastSpell(target, 53521, true, NULL, this); target->CastSpell(target, 53521, true, NULL, this); @@ -8530,34 +8570,33 @@ void Aura::HandleAuraControlVehicle(bool apply, bool Real) if(!Real) return; - Unit* caster = GetCaster(); - - if (!caster) - return; - Unit* target = GetTarget(); - - if (!target) + if (!target->IsVehicle()) return; - VehicleKit* pVehicle = target->GetVehicleKit(); + // TODO: Check for free seat - if (target->GetTypeId() != TYPEID_UNIT || !pVehicle) + Unit *caster = GetCaster(); + if (!caster) return; if (apply) { -// ((Player*)caster)->RemovePet(PET_SAVE_AS_CURRENT); - // Maybe seat number stored somewhere - caster->EnterVehicle(pVehicle); + if (caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)caster)->RemovePet(PET_SAVE_AS_CURRENT); + + caster->EnterVehicle(target->GetVehicleKit()); } else { // some SPELL_AURA_CONTROL_VEHICLE auras have a dummy effect on the player - remove them caster->RemoveAurasDueToSpell(GetId()); - if (caster->GetVehicle() == pVehicle) + if (caster->GetVehicle() == target->GetVehicleKit()) caster->ExitVehicle(); + + if (caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)caster)->ResummonPetTemporaryUnSummonedIfAny(); } } @@ -9679,8 +9718,31 @@ void SpellAuraHolder::HandleSpellSpecificBoosts(bool apply) break; } case SPELLFAMILY_ROGUE: + { + // remove debuf savage combat + if (GetSpellProto()->SpellFamilyFlags & UI64LIT(0x0008000010014000)) + { + // search poison + bool found = false; + Unit::SpellAuraHolderMap const& auras = m_target->GetSpellAuraHolderMap(); + for (Unit::SpellAuraHolderMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) + { + uint32 flags1 = m_target->HasAuraState(AURA_STATE_DEADLY_POISON); + if (itr->second->GetSpellProto()->SpellFamilyName == SPELLFAMILY_ROGUE && (flags1 & (0x80000))) + { + found = true; + break; + } + } + + if (!found) + { + m_target->RemoveAurasDueToSpell(58684); // Savage Combat rank 1 + m_target->RemoveAurasDueToSpell(58683); // Savage Combat rank 2 + } + } // Sprint (skip non player casted spells by category) - if (GetSpellProto()->SpellFamilyFlags & UI64LIT(0x0000000000000040) && GetSpellProto()->Category == 44) + else if (GetSpellProto()->SpellFamilyFlags & UI64LIT(0x0000000000000040) && GetSpellProto()->Category == 44) { if(!apply || m_target->HasAura(58039)) // Glyph of Blurred Speed spellId1 = 61922; // Sprint (waterwalk) @@ -9690,6 +9752,7 @@ void SpellAuraHolder::HandleSpellSpecificBoosts(bool apply) else return; break; + } case SPELLFAMILY_HUNTER: { switch (GetId()) @@ -10329,8 +10392,7 @@ void Aura::HandleAuraSetVehicle(bool apply, bool real) if (apply) { - if (!target->CreateVehicleKit(vehicleId)) - return; + target->SetVehicleId(vehicleId); } else if (target->GetVehicleKit()) diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index ce4230aa7..4e32d0203 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -1519,14 +1519,17 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) ((Player*)m_caster)->KilledMonster(pCreature->GetCreatureInfo(), pCreature->GetObjectGuid()); return; } - case 43014: // Despawn Self - { - if (m_caster->GetTypeId() != TYPEID_UNIT) + case 43014: // Despawn Self + { // used by ACID event to run away and despawn + if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) return; - ((Creature*)m_caster)->ForcedDespawn(); + ((Creature*)unitTarget)->ForcedDespawn(2000); + float x, y, z; + unitTarget->GetClosePoint(x, y, z, unitTarget->GetObjectBoundingRadius(), 10.0f, unitTarget->GetOrientation()); + unitTarget->SendMonsterMove(x, y, z, SPLINETYPE_NORMAL, SPLINEFLAG_WALKMODE, 2000); return; - } + } case 43036: // Dismembering Corpse { if (!unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER) @@ -1631,6 +1634,11 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) break; } + case 45958: // Signal Alliance + { + m_caster->CastSpell(m_caster, m_spellInfo->CalculateSimpleValue(eff_idx), true); + return; + } case 45980: // Re-Cursive Transmatter Injection { if (m_caster->GetTypeId() == TYPEID_PLAYER && unitTarget) @@ -3285,6 +3293,31 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) unitTarget->CastSpell(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), spellId, true); return; } + // Corpse Explosion. Execute for Effect1 only + else if (m_spellInfo->SpellIconID == 1737 && eff_idx == EFFECT_INDEX_1) + { + if (!unitTarget) + return; + + // casting on a ghoul-pet makes it explode! :D + // target validation is done in Spell:SetTargetMap + if (unitTarget->GetEntry() == 26125 && unitTarget->isAlive() ) + { + int32 bp0 = int32(unitTarget->GetMaxHealth() * 0.25); // AoE dmg + int32 bp1 = int32(unitTarget->GetHealth() ); // self damage + unitTarget->InterruptNonMeleeSpells(false); + unitTarget->CastCustomSpell(unitTarget, 47496, &bp0, &bp1, 0, false); + } + else + { + int32 damage = m_spellInfo->CalculateSimpleValue(SpellEffectIndex(EFFECT_INDEX_0)); + uint32 spell = m_spellInfo->CalculateSimpleValue(EFFECT_INDEX_1); + + m_caster->CastSpell(unitTarget, 51270, true); // change modelId (is this generic spell for this kind of spells?) + m_caster->CastCustomSpell(unitTarget, spell, &damage, NULL, NULL, true); + } + return; + } // Obliterate else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0002000000000000)) { @@ -4254,7 +4287,7 @@ void Spell::DoCreateItem(SpellEffectIndex eff_idx, uint32 itemtype) // can the player store the new item? ItemPosCountVec dest; uint32 no_space = 0; - uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, newitemid, num_to_add, &no_space ); + InventoryResult msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, newitemid, num_to_add, &no_space ); if( msg != EQUIP_ERR_OK ) { // convert to possible store amount @@ -4356,7 +4389,8 @@ void Spell::EffectPersistentAA(SpellEffectIndex eff_idx) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius); DynamicObject* dynObj = new DynamicObject; - if (!dynObj->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, m_spellInfo->Id, eff_idx, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, m_duration, radius)) + if (!dynObj->Create(pCaster->GetMap()->GenerateLocalLowGuid(HIGHGUID_DYNAMICOBJECT), pCaster, m_spellInfo->Id, + eff_idx, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, m_duration, radius, DYNAMIC_OBJECT_AREA_SPELL)) { delete dynObj; return; @@ -4406,7 +4440,6 @@ void Spell::EffectEnergize(SpellEffectIndex eff_idx) case 63375: // Improved Stormstrike case 67545: // Empowered Fire case 68082: // Glyph of Seal of Command - case 71132: // Glyph of Shadow Word: Pain damage = damage * unitTarget->GetCreateMana() / 100; break; case 67487: // Mana Potion Injector @@ -5290,15 +5323,13 @@ void Spell::EffectAddFarsight(SpellEffectIndex eff_idx) DynamicObject* dynObj = new DynamicObject; // set radius to 0: spell not expected to work as persistent aura - if(!dynObj->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, m_spellInfo->Id, eff_idx, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, duration, 0)) + if(!dynObj->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, + m_spellInfo->Id, eff_idx, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, duration, 0, DYNAMIC_OBJECT_FARSIGHT_FOCUS)) { delete dynObj; return; } - // DYNAMICOBJECT_BYTES is apparently different from the default bytes set in ::Create - dynObj->SetUInt32Value(DYNAMICOBJECT_BYTES, 0x80000002); - m_caster->AddDynObject(dynObj); m_caster->GetMap()->Add(dynObj); @@ -6744,22 +6775,6 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) unitTarget->CastSpell(unitTarget, roll_chance_i(50) ? 24714 : 24715, true); return; } - case 26275: // PX-238 Winter Wondervolt TRAP - { - uint32 spells[4] = { 26272, 26157, 26273, 26274 }; - - // check presence - for(int j = 0; j < 4; ++j) - if (unitTarget->HasAura(spells[j], EFFECT_INDEX_0)) - return; - - // select spell - uint32 iTmpSpellId = spells[urand(0,3)]; - - // cast - unitTarget->CastSpell(unitTarget, iTmpSpellId, true); - return; - } case 25140: // Orb teleport spells case 25143: case 25650: @@ -6808,6 +6823,19 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) m_caster->CastSpell(unitTarget, spells[urand(0, 2)], true); return; } + case 26275: // PX-238 Winter Wondervolt TRAP + { + uint32 spells[4] = {26272, 26157, 26273, 26274}; + + // check presence + for(int j = 0; j < 4; ++j) + if(unitTarget->HasAura(spells[j], EFFECT_INDEX_0)) + return; + + // cast + unitTarget->CastSpell(unitTarget, spells[urand(0,3)], true); + return; + } case 26465: // Mercurial Shield - need remove one 26464 Mercurial Shield aura unitTarget->RemoveAuraHolderFromStack(26464); return; @@ -6883,6 +6911,14 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) unitTarget->RemoveAurasAtMechanicImmunity(IMMUNE_TO_ROOT_AND_SNARE_MASK,30918,true); break; } + case 38358: // Tidal Surge + { + if (!unitTarget) + return; + + unitTarget->CastSpell(unitTarget, 38353, true, NULL, NULL, m_caster->GetObjectGuid()); + return; + } case 39681: // Summon Goblin Tonk { if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) @@ -7163,6 +7199,15 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) m_caster->SetDisplayId(display_id); return; } + case 45958: // Signal Alliance + { + // "escort" aura not present, so let nothing happen + if (!m_caster->HasAura(m_spellInfo->CalculateSimpleValue(eff_idx))) + return; + // "escort" aura is present so break; and let DB table spell_scripts be used and process further. + else + break; + } case 46203: // Goblin Weather Machine { if (!unitTarget) @@ -7465,10 +7510,6 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) unitTarget->CastSpell(unitTarget, 48809, true); return; } - case 48724: // Q: The Denouncement - case 48726: - case 48728: - case 48730: case 52694: // Recall Eye of Acherus { if (!m_caster || m_caster->GetTypeId() != TYPEID_UNIT) @@ -9261,7 +9302,7 @@ void Spell::EffectCharge(SpellEffectIndex /*eff_idx*/) //TODO: research more ContactPoint/attack distance. //3.666666 instead of ATTACK_DISTANCE(5.0f) in below seem to give more accurate result. float x, y, z; - unitTarget->GetContactPoint(m_caster, x, y, z, 3.6f); + unitTarget->GetContactPoint(m_caster, x, y, z, 3.666666f); // Try to normalize Z coord cuz GetContactPoint do nothing with Z axis unitTarget->UpdateGroundPositionZ(x, y, z, 5.0f); @@ -9293,7 +9334,7 @@ void Spell::EffectCharge2(SpellEffectIndex /*eff_idx*/) ((Creature *)unitTarget)->StopMoving(); } else if (unitTarget && unitTarget != m_caster) - unitTarget->GetContactPoint(m_caster, x, y, z, 3.6f); + unitTarget->GetContactPoint(m_caster, x, y, z, 3.666666f); else return; diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index 3f55f26bf..d4c69b2b6 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -96,7 +96,7 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) return; } - uint8 msg = pUser->CanUseItem(pItem); + InventoryResult msg = pUser->CanUseItem(pItem); if (msg != EQUIP_ERR_OK) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at not read packet tail diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 10c13ba10..9f36e70bb 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -662,6 +662,11 @@ bool IsPositiveEffect(SpellEntry const *spellproto, SpellEffectIndex effIndex) switch(spellproto->Id) { + case 72219: // Gastric Bloat 10 N + case 72551: // Gastric Bloat 10 H + case 72552: // Gastric Bloat 25 N + case 72553: // Gastric Bloat 25 H + return false; case 47540: // Penance start dummy aura - Rank 1 case 53005: // Penance start dummy aura - Rank 2 case 53006: // Penance start dummy aura - Rank 3 @@ -2579,7 +2584,7 @@ SpellEntry const* SpellMgr::SelectAuraRankForLevel(SpellEntry const* spellInfo, break; // if found appropriate level - if (level + 10 >= spellInfo->spellLevel) + if (level + 10 >= nextSpellInfo->spellLevel) return nextSpellInfo; // one rank less then diff --git a/src/game/TargetedMovementGenerator.cpp b/src/game/TargetedMovementGenerator.cpp index 95fc0a113..503a1f99a 100644 --- a/src/game/TargetedMovementGenerator.cpp +++ b/src/game/TargetedMovementGenerator.cpp @@ -132,7 +132,7 @@ void TargetedMovementGeneratorMedium::_setTargetLocation(T &owner) D::_addUnitStateMove(owner); if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->CanFly()) - ((Creature&)owner).AddSplineFlag(SPLINEFLAG_UNKNOWN7); + ((Creature&)owner).AddSplineFlag(SPLINEFLAG_FLYING); } template<> @@ -201,6 +201,16 @@ bool TargetedMovementGeneratorMedium::Update(T &owner, const uint32 & time_ if (!i_destinationHolder.HasDestination()) _setTargetLocation(owner); + if (owner.IsStopped() && !i_destinationHolder.HasArrived()) + { + D::_addUnitStateMove(owner); + if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->CanFly()) + ((Creature&)owner).AddSplineFlag(SPLINEFLAG_FLYING); + + i_destinationHolder.StartTravel(traveller); + return true; + } + if (i_destinationHolder.UpdateTraveller(traveller, time_diff, i_recalculateTravel || owner.IsStopped())) { if (!IsActive(owner)) // force stop processing (movement can move out active zone with cleanup movegens list) @@ -288,7 +298,7 @@ void ChaseMovementGenerator::Initialize(Creature &owner) owner.RemoveSplineFlag(SPLINEFLAG_WALKMODE); if (((Creature*)&owner)->CanFly()) - owner.AddSplineFlag(SPLINEFLAG_UNKNOWN7); + owner.AddSplineFlag(SPLINEFLAG_FLYING); _setTargetLocation(owner); } @@ -370,7 +380,7 @@ void FollowMovementGenerator::Initialize(Creature &owner) _updateSpeed(owner); if (((Creature*)&owner)->CanFly()) - owner.AddSplineFlag(SPLINEFLAG_UNKNOWN7); + owner.AddSplineFlag(SPLINEFLAG_FLYING); _setTargetLocation(owner); } diff --git a/src/game/Transports.cpp b/src/game/Transports.cpp index 08af53d74..9e3b7e40b 100644 --- a/src/game/Transports.cpp +++ b/src/game/Transports.cpp @@ -178,7 +178,7 @@ bool Transport::Create(uint32 guidlow, uint32 mapid, float x, float y, float z, SetUInt32Value(GAMEOBJECT_LEVEL, m_period); SetEntry(goinfo->id); - SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId); + SetDisplayId(goinfo->displayId); SetGoState(GO_STATE_READY); SetGoType(GameobjectTypes(goinfo->type)); diff --git a/src/game/Traveller.h b/src/game/Traveller.h index 17b0d2650..97ad8194d 100644 --- a/src/game/Traveller.h +++ b/src/game/Traveller.h @@ -75,7 +75,7 @@ inline float Traveller::Speed() { if(i_traveller.HasSplineFlag(SPLINEFLAG_WALKMODE)) return i_traveller.GetSpeed(MOVE_WALK); - else if(i_traveller.HasSplineFlag(SPLINEFLAG_UNKNOWN7)) + else if(i_traveller.HasSplineFlag(SPLINEFLAG_FLYING)) return i_traveller.GetSpeed(MOVE_FLIGHT); else return i_traveller.GetSpeed(MOVE_RUN); diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index d13128705..0ba761c80 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -39,6 +39,7 @@ #include "Pet.h" #include "Util.h" #include "Totem.h" +#include "Vehicle.h" #include "BattleGround.h" #include "InstanceData.h" #include "MapPersistentStateMgr.h" @@ -183,8 +184,10 @@ void GlobalCooldownMgr::CancelGlobalCooldown(SpellEntry const* spellInfo) //////////////////////////////////////////////////////////// // Methods of class Unit -Unit::Unit() -: WorldObject(), i_motionMaster(this), m_ThreatManager(this), m_HostileRefManager(this) +Unit::Unit() : + i_motionMaster(this), m_ThreatManager(this), m_HostileRefManager(this), + m_charmInfo(NULL), + m_vehicleInfo(NULL) { m_objectType |= TYPEMASK_UNIT; m_objectTypeId = TYPEID_UNIT; @@ -272,8 +275,8 @@ Unit::Unit() m_transport = NULL; - m_pVehicle = NULL; m_pVehicleKit = NULL; + m_pVehicle = NULL; m_comboPoints = 0; @@ -307,8 +310,8 @@ Unit::~Unit() } } - if (m_charmInfo) - delete m_charmInfo; + delete m_charmInfo; + delete m_vehicleInfo; // those should be already removed at "RemoveFromWorld()" call MANGOS_ASSERT(m_gameObj.size() == 0); @@ -5785,6 +5788,9 @@ bool Unit::IsHostileTo(Unit const* unit) const bool Unit::IsFriendlyTo(Unit const* unit) const { + if (!unit) + return true; + // always friendly to self if(unit==this) return true; @@ -8185,22 +8191,11 @@ void Unit::Mount(uint32 mount, uint32 spellId, uint32 vehicleId, uint32 creature if (vehicleId) { - if (CreateVehicleKit(vehicleId)) - { - GetVehicleKit()->Reset(); + SetVehicleId(vehicleId); + GetVehicleKit()->Reset(); - // Send others that we now have a vehicle - WorldPacket data(SMSG_SET_VEHICLE_REC_ID, 8+4); - data << GetPackGUID(); - data << uint32(vehicleId); - SendMessageToSet(&data, true); - - data.Initialize(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, 0); - ((Player*)this)->GetSession()->SendPacket(&data); - - // mounts can also have accessories - GetVehicleKit()->InstallAllAccessories(creatureEntry); - } + // mounts can also have accessories + GetVehicleKit()->InstallAllAccessories(creatureEntry); } } } @@ -10010,9 +10005,10 @@ void Unit::CleanupsBeforeDelete() { if(m_uint32Values) // only for fully created object { - RemoveVehicleKit(); - ExitVehicle(); - + if (GetVehicle()) + ExitVehicle(); + if (GetVehicleKit()) + RemoveVehicleKit(); InterruptNonMeleeSpells(true); m_Events.KillAllEvents(false); // non-delatable (currently casted spells) will not deleted now but it will deleted at call in Map::RemoveAllObjectsInRemoveList CombatStop(); @@ -11843,18 +11839,6 @@ struct SetPvPHelper bool state; }; -bool Unit::CreateVehicleKit(uint32 vehicleId) -{ - VehicleEntry const *vehicleInfo = sVehicleStore.LookupEntry(vehicleId); - - if (!vehicleInfo) - return false; - - m_pVehicleKit = new VehicleKit(this, vehicleInfo); - m_updateFlag |= UPDATEFLAG_VEHICLE; - return true; -} - void Unit::RemoveVehicleKit() { if (!m_pVehicleKit) @@ -11862,8 +11846,8 @@ void Unit::RemoveVehicleKit() m_pVehicleKit->RemoveAllPassengers(); - delete m_pVehicleKit; - m_pVehicleKit = NULL; +// delete m_pVehicleKit; +// m_pVehicleKit = NULL; m_updateFlag &= ~UPDATEFLAG_VEHICLE; RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); @@ -12430,3 +12414,33 @@ ObjectGuid const& Unit::GetCreatorGuid() const return ObjectGuid(); } } + +void Unit::SetVehicleId(uint32 entry) +{ + delete m_vehicleInfo; + + if (entry) + { + VehicleEntry const* ventry = sVehicleStore.LookupEntry(entry); + MANGOS_ASSERT(ventry != NULL); + + m_vehicleInfo = new VehicleInfo(ventry); + m_updateFlag |= UPDATEFLAG_VEHICLE; + + if (!m_pVehicleKit) + m_pVehicleKit = new VehicleKit(this); + } + else + { + m_vehicleInfo = NULL; + m_updateFlag &= ~UPDATEFLAG_VEHICLE; + } + + if (GetTypeId() == TYPEID_PLAYER) + { + WorldPacket data(SMSG_SET_VEHICLE_REC_ID, 16); + data << GetPackGUID(); + data << uint32(entry); + SendMessageToSet(&data, true); + } +} diff --git a/src/game/Unit.h b/src/game/Unit.h index 5bf56d577..f37df713f 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -270,8 +270,7 @@ class Item; class Pet; class PetAura; class Totem; -class Transport; -class VehicleKit; +class VehicleInfo; struct SpellImmune { @@ -1135,6 +1134,7 @@ typedef std::set GroupPetList; #define REGEN_TIME_PRECISE 500 // Used in Spell::CheckPower for precise regeneration in spell cast time struct SpellProcEventEntry; // used only privately +class VehicleKit; class MANGOS_DLL_SPEC Unit : public WorldObject { @@ -1328,6 +1328,10 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void Mount(uint32 mount, uint32 spellId = 0, uint32 vehicleId = 0, uint32 creatureEntry = 0); void Unmount(bool from_aura = false); + VehicleInfo* GetVehicleInfo() { return m_vehicleInfo; } + bool IsVehicle() const { return m_vehicleInfo != NULL; } + void SetVehicleId(uint32 entry); + uint16 GetMaxSkillValueForLevel(Unit const* target = NULL) const { return (target ? GetLevelForTarget(target) : getLevel()) * 5; } void DealDamageMods(Unit *pVictim, uint32 &damage, uint32* absorb); uint32 DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss); @@ -1607,6 +1611,8 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void RemoveSingleAuraFromSpellAuraHolder(SpellAuraHolder *holder, SpellEffectIndex index, AuraRemoveMode mode = AURA_REMOVE_BY_DEFAULT); void RemoveSingleAuraFromSpellAuraHolder(uint32 id, SpellEffectIndex index, uint64 casterGUID, AuraRemoveMode mode = AURA_REMOVE_BY_DEFAULT); + void AddSpellAuraHolderToRemoveList(SpellAuraHolder* holder) { m_deletedHolders.push_back(holder);}; + // removing specific aura stacks by diff reasons and selections void RemoveAurasDueToSpell(uint32 spellId, SpellAuraHolder* except = NULL, AuraRemoveMode mode = AURA_REMOVE_BY_DEFAULT); void RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId); @@ -1821,9 +1827,9 @@ class MANGOS_DLL_SPEC Unit : public WorldObject uint32 m_AuraFlags; - uint32 GetDisplayId() { return GetUInt32Value(UNIT_FIELD_DISPLAYID); } + uint32 GetDisplayId() const { return GetUInt32Value(UNIT_FIELD_DISPLAYID); } void SetDisplayId(uint32 modelId); - uint32 GetNativeDisplayId() { return GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID); } + uint32 GetNativeDisplayId() const { return GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID); } void SetNativeDisplayId(uint32 modelId) { SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, modelId); } void setTransForm(uint32 spellid) { m_transform = spellid;} uint32 getTransForm() const { return m_transform;} @@ -2026,7 +2032,6 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void ChangeSeat(int8 seatId, bool next = true); VehicleKit* GetVehicle() const { return m_pVehicle; } VehicleKit* GetVehicleKit() const { return m_pVehicleKit; } - bool CreateVehicleKit(uint32 vehicleId); void RemoveVehicleKit(); void ScheduleAINotify(uint32 delay); @@ -2094,8 +2099,9 @@ class MANGOS_DLL_SPEC Unit : public WorldObject // Transports Transport* m_transport; - VehicleKit* m_pVehicle; - VehicleKit* m_pVehicleKit; + VehicleInfo* m_vehicleInfo; + VehicleKit* m_pVehicleKit; + VehicleKit* m_pVehicle; private: void CleanupDeletedAuras(); diff --git a/src/game/UnitAuraProcHandler.cpp b/src/game/UnitAuraProcHandler.cpp index 04dfd1c66..4944858a4 100644 --- a/src/game/UnitAuraProcHandler.cpp +++ b/src/game/UnitAuraProcHandler.cpp @@ -992,6 +992,31 @@ SpellAuraProcResult Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura if (basepoints[0] < 0) return SPELL_AURA_PROC_FAILED; break; + case 71169: + { + // Shadow's Fate + if (GetTypeId() != TYPEID_UNIT) + return SPELL_AURA_PROC_FAILED; + switch (((Creature*)this)->GetCreatureInfo()->Entry) + { + case 38431: // Puthricide 25 + case 38586: + CastSpell(this, 71518, true); + break; + case 38434: // Lanathel 25 + case 38436: + CastSpell(this, 72934, true); + break; + case 38265: // Sindragosa 25 + case 38267: + CastSpell(this, 72289, true); + break; + default: + break; + } + CastSpell(triggeredByAura->GetCaster(), 71203, true); + return SPELL_AURA_PROC_OK; + } // Item - Shadowmourne Legendary case 71903: { diff --git a/src/game/Vehicle.cpp b/src/game/Vehicle.cpp index efad9471d..e87c898d2 100644 --- a/src/game/Vehicle.cpp +++ b/src/game/Vehicle.cpp @@ -25,22 +25,27 @@ #include "Util.h" #include "WorldPacket.h" -VehicleKit::VehicleKit(Unit* base, VehicleEntry const* vehicleInfo) : m_vehicleInfo(vehicleInfo), m_pBase(base), m_uiNumFreeSeats(0) +VehicleInfo::VehicleInfo(VehicleEntry const* entry) : + m_vehicleEntry(entry) +{ +} + +VehicleKit::VehicleKit(Unit* base) : m_pBase(base), m_uiNumFreeSeats(0) { for (uint32 i = 0; i < MAX_VEHICLE_SEAT; ++i) { - uint32 seatId = m_vehicleInfo->m_seatID[i]; + uint32 seatId = GetBase()->GetVehicleInfo()->GetEntry()->m_seatID[i]; if (!seatId) continue; if(base) { - if(m_vehicleInfo->m_flags & VEHICLE_FLAG_NO_STRAFE) - base->m_movementInfo.AddMovementFlag2(MOVEFLAG2_NO_STRAFE); + if(GetBase()->GetVehicleInfo()->GetEntry()->m_flags & VEHICLE_FLAG_NO_STRAFE) + GetBase()->m_movementInfo.AddMovementFlag2(MOVEFLAG2_NO_STRAFE); - if(m_vehicleInfo->m_flags & VEHICLE_FLAG_NO_JUMPING) - base->m_movementInfo.AddMovementFlag2(MOVEFLAG2_NO_JUMPING); + if(GetBase()->GetVehicleInfo()->GetEntry()->m_flags & VEHICLE_FLAG_NO_JUMPING) + GetBase()->m_movementInfo.AddMovementFlag2(MOVEFLAG2_NO_JUMPING); } if (VehicleSeatEntry const *seatInfo = sVehicleSeatStore.LookupEntry(seatId)) @@ -267,6 +272,7 @@ void VehicleKit::RemovePassenger(Unit *passenger) if (seat->second.seatInfo->m_flags & SEAT_FLAG_CAN_CONTROL) { + passenger->SetCharm(NULL); passenger->RemoveSpellsCausingAura(SPELL_AURA_CONTROL_VEHICLE); @@ -388,4 +394,4 @@ VehicleSeatEntry const* VehicleKit::GetSeatInfo(Unit* passenger) return itr->second.seatInfo; } return NULL; -} +} \ No newline at end of file diff --git a/src/game/Vehicle.h b/src/game/Vehicle.h index d3bf7b2cc..f1bfaf6ac 100644 --- a/src/game/Vehicle.h +++ b/src/game/Vehicle.h @@ -25,6 +25,17 @@ #include "Unit.h" #include "SharedDefines.h" +struct VehicleEntry; + +class VehicleInfo +{ + VehicleEntry const* m_vehicleEntry; + public: + explicit VehicleInfo(VehicleEntry const* entry); + + VehicleEntry const* GetEntry() const { return m_vehicleEntry; } +}; + struct VehicleSeat { VehicleSeat(VehicleSeatEntry const *pSeatInfo = NULL) : seatInfo(pSeatInfo), passenger(NULL) {} @@ -48,42 +59,32 @@ typedef std::map VehicleAccessoryMap; class MANGOS_DLL_SPEC VehicleKit { -public: - explicit VehicleKit(Unit* base, VehicleEntry const* vehicleInfo); - ~VehicleKit(); - - void Reset(); - void InstallAllAccessories(uint32 entry); - - bool HasEmptySeat(int8 seatId) const; - Unit *GetPassenger(int8 seatId) const; - int8 GetNextEmptySeat(int8 seatId, bool next) const; - bool AddPassenger(Unit *passenger, int8 seatId = -1); - void RemovePassenger(Unit *passenger); - void RelocatePassengers(float x, float y, float z, float ang); - void RemoveAllPassengers(); - VehicleSeatEntry const* GetSeatInfo(Unit* passenger); - - uint32 GetVehicleId() const { return m_vehicleInfo->m_ID; } - VehicleEntry const* GetVehicleInfo() const { return m_vehicleInfo; } - Unit* GetBase() { return m_pBase; } -private: - void UpdateFreeSeatCount(); - void InstallAccessory(uint32 entry, int8 seatId, bool minion = true); - - SeatMap m_Seats; - uint32 m_uiNumFreeSeats; - VehicleEntry const *m_vehicleInfo; - Unit* m_pBase; - - void SaveToDB(uint32, uint8, uint32) // overwrited of Creature::SaveToDB - don't must be called - { - MANGOS_ASSERT(false); - } - void DeleteFromDB() // overwrited of Creature::DeleteFromDB - don't must be called - { - MANGOS_ASSERT(false); - } + public: + explicit VehicleKit(Unit* base); + ~VehicleKit(); + + void Reset(); + void InstallAllAccessories(uint32 entry); + + bool HasEmptySeat(int8 seatId) const; + Unit *GetPassenger(int8 seatId) const; + int8 GetNextEmptySeat(int8 seatId, bool next) const; + bool AddPassenger(Unit *passenger, int8 seatId = -1); + void RemovePassenger(Unit *passenger); + void RelocatePassengers(float x, float y, float z, float ang); + void RemoveAllPassengers(); + VehicleSeatEntry const* GetSeatInfo(Unit* passenger); + + Unit* GetBase() { return m_pBase; } + + private: + void UpdateFreeSeatCount(); + void InstallAccessory(uint32 entry, int8 seatId, bool minion = true); + + SeatMap m_Seats; + uint32 m_uiNumFreeSeats; + Unit* m_pBase; + }; #endif diff --git a/src/game/VehicleHandler.cpp b/src/game/VehicleHandler.cpp index b3e1db2be..f0fe20a8a 100644 --- a/src/game/VehicleHandler.cpp +++ b/src/game/VehicleHandler.cpp @@ -41,15 +41,19 @@ void WorldSession::HandleDismissControlledVehicle(WorldPacket &recv_data) bool dismiss = true; - if (GetPlayer()->GetVehicle()->GetVehicleInfo()->m_flags & (VEHICLE_FLAG_NOT_DISMISS | VEHICLE_FLAG_ACCESSORY)) + Creature* vehicle = GetPlayer()->GetMap()->GetAnyTypeCreature(guid); + + if (!vehicle || !vehicle->GetVehicleInfo()) + return; + + if (vehicle->GetVehicleInfo()->GetEntry()->m_flags & (VEHICLE_FLAG_NOT_DISMISS | VEHICLE_FLAG_ACCESSORY)) dismiss = false; GetPlayer()->m_movementInfo = mi; GetPlayer()->ExitVehicle(); if (dismiss) - if (Creature* vehicle = GetPlayer()->GetMap()->GetAnyTypeCreature(guid)) - vehicle->ForcedDespawn(); + vehicle->ForcedDespawn(); } @@ -90,11 +94,12 @@ void WorldSession::HandleRequestVehicleSwitchSeat(WorldPacket &recv_data) if (!pVehicle) return; - if (GetPlayer()->GetVehicle()->GetVehicleInfo()->m_flags & VEHICLE_FLAG_DISABLE_SWITCH) + if (pVehicle->GetBase()->GetVehicleInfo()->GetEntry()->m_flags & VEHICLE_FLAG_DISABLE_SWITCH) return; if (pVehicle->GetBase()->GetObjectGuid() == guid) GetPlayer()->ChangeSeat(seatId); + else if (Unit *Vehicle2 = GetPlayer()->GetMap()->GetUnit(guid)) { if (VehicleKit *pVehicle2 = Vehicle2->GetVehicleKit()) @@ -177,7 +182,7 @@ void WorldSession::HandleChangeSeatsOnControlledVehicle(WorldPacket &recv_data) if (!pVehicle) return; - if (GetPlayer()->GetVehicle()->GetVehicleInfo()->m_flags & VEHICLE_FLAG_DISABLE_SWITCH) + if (pVehicle->GetBase()->GetVehicleInfo()->GetEntry()->m_flags & VEHICLE_FLAG_DISABLE_SWITCH) return; if(guid.GetRawValue() == guid2.GetRawValue()) diff --git a/src/game/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp index 3196566c4..1fd4bbfd5 100644 --- a/src/game/WaypointMovementGenerator.cpp +++ b/src/game/WaypointMovementGenerator.cpp @@ -76,7 +76,7 @@ void WaypointMovementGenerator::LoadPath(Creature &creature) } if (creature.CanFly()) - creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); + creature.AddSplineFlag(SPLINEFLAG_FLYING); // We have to set the destination here (for the first point), right after Initialize. Without, we may not have valid xyz for GetResetPosition CreatureTraveller traveller(creature); @@ -153,7 +153,7 @@ bool WaypointMovementGenerator::Update(Creature &creature, const uint3 creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (creature.CanFly()) - creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); + creature.AddSplineFlag(SPLINEFLAG_FLYING); // Now we re-set destination to same node and start travel MoveToNextNode(traveller); @@ -249,7 +249,7 @@ bool WaypointMovementGenerator::Update(Creature &creature, const uint3 creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (creature.CanFly()) - creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); + creature.AddSplineFlag(SPLINEFLAG_FLYING); if (WaypointBehavior *behavior = i_path->at(i_currentNode).behavior) { diff --git a/src/game/World.cpp b/src/game/World.cpp index b4b2e26bf..1b93abd69 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -31,7 +31,6 @@ #include "WorldPacket.h" #include "Weather.h" #include "Player.h" -#include "Vehicle.h" #include "SkillExtraItems.h" #include "SkillDiscovery.h" #include "AccountMgr.h" @@ -65,7 +64,7 @@ #include "Util.h" #include "AuctionHouseBot/AuctionHouseBot.h" #include "CharacterDatabaseCleaner.h" -#include "PlayerBot/PlayerbotMgr.h" +#include "playerbot/PlayerbotMgr.h" #include "LFGMgr.h" INSTANTIATE_SINGLETON_1( World ); @@ -1313,8 +1312,6 @@ void World::SetInitialWorldSettings() LoginDatabase.PExecute("INSERT INTO uptime (realmid, starttime, startstring, uptime) VALUES('%u', " UI64FMTD ", '%s', 0)", realmID, uint64(m_startTime), isoDate); - m_timers[WUPDATE_OBJECTS].SetInterval(0); - m_timers[WUPDATE_SESSIONS].SetInterval(0); m_timers[WUPDATE_WEATHERS].SetInterval(1*IN_MILLISECONDS); m_timers[WUPDATE_AUCTIONS].SetInterval(MINUTE*IN_MILLISECONDS); m_timers[WUPDATE_UPTIME].SetInterval(getConfig(CONFIG_UINT32_UPTIME_UPDATE)*MINUTE*IN_MILLISECONDS); @@ -1485,13 +1482,8 @@ void World::Update(uint32 diff) m_timers[WUPDATE_AHBOT].Reset(); } - ///
  • Handle session updates when the timer has passed - if (m_timers[WUPDATE_SESSIONS].Passed()) - { - m_timers[WUPDATE_SESSIONS].Reset(); - - UpdateSessions(diff); - } + ///
  • Handle session updates + UpdateSessions(diff); ///
  • Handle weather updates when the timer has passed if (m_timers[WUPDATE_WEATHERS].Passed()) @@ -1523,14 +1515,9 @@ void World::Update(uint32 diff) } ///
  • Handle all other objects - if (m_timers[WUPDATE_OBJECTS].Passed()) - { - m_timers[WUPDATE_OBJECTS].Reset(); - ///- Update objects when the timer has passed (maps, transport, creatures,...) - sMapMgr.Update(diff); // As interval = 0 - - sBattleGroundMgr.Update(diff); - } + ///- Update objects (maps, transport, creatures,...) + sMapMgr.Update(diff); + sBattleGroundMgr.Update(diff); ///- Delete all characters which have been deleted X days before if (m_timers[WUPDATE_DELETECHARS].Passed()) @@ -1975,7 +1962,7 @@ void World::UpdateSessions( uint32 diff ) WorldSession * pSession = itr->second; WorldSessionFilter updater(pSession); - if(!pSession->Update(diff, updater)) // As interval = 0 + if(!pSession->Update(updater)) { RemoveQueuedSession(pSession); m_sessions.erase(itr); diff --git a/src/game/World.h b/src/game/World.h index 4eacd389b..de612f0ae 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -71,16 +71,15 @@ enum ShutdownExitCode /// Timers for different object refresh rates enum WorldTimers { - WUPDATE_OBJECTS = 0, - WUPDATE_SESSIONS = 1, - WUPDATE_AUCTIONS = 2, - WUPDATE_WEATHERS = 3, - WUPDATE_UPTIME = 4, - WUPDATE_CORPSES = 5, - WUPDATE_EVENTS = 6, - WUPDATE_DELETECHARS = 7, - WUPDATE_AHBOT = 8, - WUPDATE_COUNT = 9 + WUPDATE_AUCTIONS = 0, + WUPDATE_WEATHERS = 1, + WUPDATE_UPTIME = 2, + WUPDATE_CORPSES = 3, + WUPDATE_EVENTS = 4, + WUPDATE_DELETECHARS = 5, + WUPDATE_AUTOBROADCAST = 6, + WUPDATE_AHBOT = 7, + WUPDATE_COUNT = 8 }; /// Configuration elements @@ -195,6 +194,9 @@ enum eConfigUInt32Values CONFIG_UINT32_LFG_MAXKICKS, CONFIG_UINT32_LFG_KICKVOTES, CONFIG_UINT32_MIN_LEVEL_FOR_RAID, + CONFIG_UINT32_PLAYERBOT_MAXBOTS, + CONFIG_UINT32_PLAYERBOT_RESTRICTLEVEL, + CONFIG_UINT32_PLAYERBOT_BOTGUYCOST, CONFIG_UINT32_VALUE_COUNT }; @@ -356,6 +358,8 @@ enum eConfigBoolValues CONFIG_BOOL_ARMORY_SUPPORT, CONFIG_BOOL_LFG_ENABLE, CONFIG_BOOL_LFR_ENABLE, + CONFIG_BOOL_PLAYERBOT_DISABLE, + CONFIG_BOOL_PLAYERBOT_DEBUGWHISPER, CONFIG_BOOL_VALUE_COUNT }; diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp index d4dc7b3b5..3a6246b35 100644 --- a/src/game/WorldSession.cpp +++ b/src/game/WorldSession.cpp @@ -20,7 +20,7 @@ \ingroup u2w */ -#include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it +#include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it #include "Common.h" #include "Database/DatabaseEnv.h" #include "Log.h" @@ -39,8 +39,8 @@ #include "Auth/AuthCrypt.h" #include "Auth/HMACSHA1.h" #include "zlib/zlib.h" -#include "PlayerBot/PlayerbotMgr.h" -#include "PlayerBot/PlayerbotAI.h" +#include "playerbot/PlayerbotMgr.h" +#include "playerbot/PlayerbotAI.h" // select opcodes appropriate for processing in Map::Update context for current session state static bool MapSessionFilterHelper(WorldSession* session, OpcodeHandler const& opHandle) @@ -132,7 +132,6 @@ char const* WorldSession::GetPlayerName() const /// Send a packet to the client void WorldSession::SendPacket(WorldPacket const* packet) { - // Playerbot mod: send packet to bot AI if (GetPlayer()) { /*if (!GetPlayer()->IsBot()) @@ -249,7 +248,7 @@ void WorldSession::LogUnprocessedTail(WorldPacket *packet) } /// Update the WorldSession (triggered by World update) -bool WorldSession::Update(uint32 diff, PacketFilter& updater) +bool WorldSession::Update(PacketFilter& updater) { ///- Retrieve packets from the receive queue and call the appropriate handlers /// not process packets if socket already closed @@ -279,12 +278,10 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer - // playerbot mod if (_player && !IsBotSession() && _player->GetPlayerbotMgr()) _player->GetPlayerbotMgr()->HandleMasterIncomingPacket(*packet); /*if (_player && !_player->IsBot()) packet->Print();*/ - // playerbot mod end break; case STATUS_LOGGEDIN_OR_RECENTLY_LOGGEDOUT: if(!_player && !m_playerRecentlyLogout) @@ -357,10 +354,6 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) delete packet; } - // Playerbot mod - Process player bot packets - // The PlayerbotAI class adds to the packet queue to simulate a real player - // since Playerbots are known to the World obj only by its master's WorldSession object - // we need to process all master's bot's packets. if (GetPlayer()) { HashMapHolder::MapType& m = sObjectAccessor.GetPlayers(); @@ -520,8 +513,8 @@ void WorldSession::LogoutPlayer(bool Save) { static SqlStatementID id; - SqlStatement stmt1 = LoginDatabase.CreateStatement(id, "UPDATE account SET active_realm_id = ? WHERE id = ?"); - stmt1.PExecute(uint32(0), GetAccountId()); + SqlStatement stmt = LoginDatabase.CreateStatement(id, "UPDATE account SET active_realm_id = ? WHERE id = ?"); + stmt.PExecute(uint32(0), GetAccountId()); } ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members @@ -579,7 +572,7 @@ void WorldSession::LogoutPlayer(bool Save) sSocialMgr.RemovePlayerSocial (_player->GetGUIDLow ()); // Playerbot - remember player GUID for update SQL below - uint32 guid = _player->GetGUIDLow(); + uint32 guid = GetPlayer()->GetGUIDLow(); ///- Remove the player from the world // the player may not be in the world when logging out @@ -602,13 +595,10 @@ void WorldSession::LogoutPlayer(bool Save) WorldPacket data( SMSG_LOGOUT_COMPLETE, 0 ); SendPacket( &data ); - ///- Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline - //No SQL injection as AccountId is uint32 - static SqlStatementID updChars; - SqlStatement stmt2 = CharacterDatabase.CreateStatement(updChars, "UPDATE characters SET online = 0 WHERE guid = ?"); - stmt2.PExecute(guid); + SqlStatement stmt = CharacterDatabase.CreateStatement(updChars, "UPDATE characters SET online = 0 WHERE guid = ?"); + stmt.PExecute(guid); DEBUG_LOG( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" ); } diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index 6576e6965..17e695536 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -27,12 +27,14 @@ #include "SharedDefines.h" #include "ObjectGuid.h" #include "LFG.h" +#include "LFGMgr.h" +#include "AuctionHouseMgr.h" +#include "Item.h" struct ItemPrototype; struct AuctionEntry; struct AuctionHouseEntry; struct DeclinedName; -struct LFGReward; class ObjectGuid; class Creature; @@ -154,12 +156,6 @@ enum LfgJoinResult ERR_LFG_ROLE_CHECK_FAILED2 = 0x12, }; -enum LfgUpdateType -{ - LFG_UPDATE_JOIN = 5, - LFG_UPDATE_LEAVE = 7, -}; - enum ChatRestrictionType { ERR_CHAT_RESTRICTED = 0, @@ -275,7 +271,7 @@ class MANGOS_DLL_SPEC WorldSession void QueuePacket(WorldPacket* new_packet); - bool Update(uint32 diff, PacketFilter& updater); + bool Update(PacketFilter& updater); /// Handle the authentication waiting queue (to be completed) void SendAuthWaitQue(uint32 position); @@ -340,12 +336,16 @@ class MANGOS_DLL_SPEC WorldSession bool SendItemInfo( uint32 itemid, WorldPacket data ); //auction - void SendAuctionHello(Unit * unit); - void SendAuctionCommandResult( uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError = 0); - void SendAuctionBidderNotification( uint32 location, uint32 auctionId, ObjectGuid bidderGuid, uint32 bidSum, uint32 diff, uint32 item_template); - void SendAuctionOwnerNotification( AuctionEntry * auction ); - void SendAuctionOutbiddedMail( AuctionEntry * auction, uint32 newPrice ); - void SendAuctionCancelledToBidderMail( AuctionEntry* auction ); + void SendAuctionHello(Unit *unit); + void SendAuctionCommandResult(AuctionEntry *auc, AuctionAction Action, AuctionError ErrorCode, InventoryResult invError = EQUIP_ERR_OK); + void SendAuctionBidderNotification(AuctionEntry *auction); + void SendAuctionOwnerNotification(AuctionEntry *auction); + void SendAuctionRemovedNotification(AuctionEntry* auction); + void SendAuctionOutbiddedMail(AuctionEntry *auction); + void SendAuctionCancelledToBidderMail(AuctionEntry *auction); + void BuildListAuctionItems(std::list &auctions, WorldPacket& data, std::wstring const& searchedname, uint32 listfrom, uint32 levelmin, + uint32 levelmax, uint32 usable, uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, uint32& count, uint32& totalcount, bool isFull); + AuctionHouseEntry const* GetCheckedAuctionHouseForAuctioneer(ObjectGuid guid); //Item Enchantment @@ -857,6 +857,7 @@ class MANGOS_DLL_SPEC WorldSession void HandleLfgClearOpcode(WorldPacket& recv_data); void HandleSetLfgCommentOpcode(WorldPacket& recv_data); void HandleLfgSetRolesOpcode(WorldPacket& recv_data); + void HandleLfgGetStatus(WorldPacket& recv_data); // void HandleLfgSetBootVoteOpcode(WorldPacket &recv_data); void HandleLfgProposalResultOpcode(WorldPacket &recv_data); @@ -869,14 +870,18 @@ class MANGOS_DLL_SPEC WorldSession void SendLfgUpdateSearch(bool update); void SendLfgJoinResult(LFGJoinResult checkResult, uint8 checkValue = 0, bool withLockMap = false); void SendLfgPlayerReward(LFGDungeonEntry const* dungeon, const LFGReward* reward, const Quest* qRew, bool isSecond = false); + void SendLfgQueueStatus(LFGQueueStatus* status); + void SendLfgRoleChosen(ObjectGuid guid, uint8 roles); + void SendLfgBootPlayer(LFGPlayerBoot* pBoot); + void SendLfgUpdateProposal(LFGProposal* proposal); + void SendLfgOfferContinue(uint32 dungeonID); + void SendLfgTeleportError(LFGTeleportError msg); // LFR void HandleLfrSearchOpcode(WorldPacket& recv_data); void HandleLfrLeaveOpcode(WorldPacket& recv_data); // send data - void SendLfgUpdateList(uint32 dungeonEntry); + void SendLfgUpdateList(uint32 dungeonID); void SendLfgDisabled(); - void SendLfgOfferContinue(uint32 dungeonEntry); - void SendLfgTeleportError(uint8 err); private: // private trade methods diff --git a/src/game/debugcmds.cpp b/src/game/debugcmds.cpp index 221fd1bc7..8dd7e3441 100644 --- a/src/game/debugcmds.cpp +++ b/src/game/debugcmds.cpp @@ -19,7 +19,6 @@ #include "Common.h" #include "Database/DatabaseEnv.h" #include "WorldPacket.h" -#include "Vehicle.h" #include "Player.h" #include "Opcodes.h" #include "Chat.h" @@ -111,7 +110,7 @@ bool ChatHandler::HandleDebugSendEquipErrorCommand(char* args) return false; uint8 msg = atoi(args); - m_session->GetPlayer()->SendEquipError(msg, NULL, NULL); + m_session->GetPlayer()->SendEquipError(InventoryResult(msg), NULL, NULL); return true; } @@ -121,7 +120,7 @@ bool ChatHandler::HandleDebugSendSellErrorCommand(char* args) return false; uint8 msg = atoi(args); - m_session->GetPlayer()->SendSellError(msg, 0, 0, 0); + m_session->GetPlayer()->SendSellError(SellResult(msg), 0, 0, 0); return true; } @@ -131,7 +130,7 @@ bool ChatHandler::HandleDebugSendBuyErrorCommand(char* args) return false; uint8 msg = atoi(args); - m_session->GetPlayer()->SendBuyError(msg, 0, 0, 0); + m_session->GetPlayer()->SendBuyError(BuyResult(msg), 0, 0, 0); return true; } diff --git a/src/game/playerbot/PlayerbotAI.cpp b/src/game/playerbot/PlayerbotAI.cpp new file mode 100644 index 000000000..1b9e6d13a --- /dev/null +++ b/src/game/playerbot/PlayerbotAI.cpp @@ -0,0 +1,3297 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Config/Config.h" +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "../ItemPrototype.h" +#include "../World.h" +#include "../SpellMgr.h" +#include "PlayerbotAI.h" +#include "PlayerbotMgr.h" +#include "PlayerbotDeathKnightAI.h" +#include "PlayerbotDruidAI.h" +#include "PlayerbotHunterAI.h" +#include "PlayerbotMageAI.h" +#include "PlayerbotPaladinAI.h" +#include "PlayerbotPriestAI.h" +#include "PlayerbotRogueAI.h" +#include "PlayerbotShamanAI.h" +#include "PlayerbotWarlockAI.h" +#include "PlayerbotWarriorAI.h" +#include "../Player.h" +#include "../ObjectMgr.h" +#include "../Chat.h" +#include "WorldPacket.h" +#include "../Spell.h" +#include "../Unit.h" +#include "../SpellAuras.h" +#include "../SharedDefines.h" +#include "Log.h" +#include "../GossipDef.h" +#include "../ArenaTeam.h" +#include "../MotionMaster.h" +#include "../BattleGroundMgr.h" + +// returns a float in range of.. +float rand_float(float low, float high) +{ + return (rand() / (static_cast (RAND_MAX) + 1.0)) * (high - low) + low; +} + +typedef std::map BattleGroundPlayerMap; + +// ChatHandler already implements some useful commands the master can call on bots +// These commands are protected inside the ChatHandler class so this class provides access to the commands +// we'd like to call on our bots +class PlayerbotChatHandler : protected ChatHandler +{ +public: + explicit PlayerbotChatHandler(Player* pMasterPlayer) : ChatHandler(pMasterPlayer) {} + void sysmessage(const char *str) { SendSysMessage(str); } + bool dropQuest(char *str) { return HandleQuestRemoveCommand(str); } +}; + +PlayerbotAI::PlayerbotAI(PlayerbotMgr* const mgr, Player* const bot) : + m_mgr(mgr), m_bot(bot), m_ignoreAIUpdatesUntilTime(0), + m_TimeDoneEating(0), m_TimeDoneDrinking(0), + m_CurrentlyCastingSpellId(0), m_enterBg(0), m_leaveBg(0), + m_targetGuidCommand(0), m_classAI(0), + m_role(0), m_new_role(1) +{ + SetLeader(bot); + + // set bot state and needed item list + m_botState = BOTSTATE_NORMAL; + m_targetCombat = NULL; + m_followTarget = m_bot; + + SetQuestNeedItems(); + + m_bot->GetPosition(orig_x, orig_y, orig_z); + orig_map = m_bot->GetMapId(); + + // get class specific ai + switch (m_bot->getClass()) + { + case CLASS_WARRIOR: + m_classAI = (PlayerbotClassAI*) new PlayerbotWarriorAI(m_bot, this); + break; + case CLASS_PALADIN: + m_classAI = (PlayerbotClassAI*) new PlayerbotPaladinAI(m_bot, this); + break; + case CLASS_HUNTER: + m_classAI = (PlayerbotClassAI*) new PlayerbotHunterAI(m_bot, this); + break; + case CLASS_ROGUE: + m_classAI = (PlayerbotClassAI*) new PlayerbotRogueAI(m_bot, this); + break; + case CLASS_PRIEST: + m_classAI = (PlayerbotClassAI*) new PlayerbotPriestAI(m_bot, this); + break; + case CLASS_DEATH_KNIGHT: + m_classAI = (PlayerbotClassAI*) new PlayerbotDeathKnightAI(m_bot, this); + break; + case CLASS_SHAMAN: + m_classAI = (PlayerbotClassAI*) new PlayerbotShamanAI(m_bot, this); + break; + case CLASS_MAGE: + m_classAI = (PlayerbotClassAI*) new PlayerbotMageAI(m_bot, this); + break; + case CLASS_WARLOCK: + m_classAI = (PlayerbotClassAI*) new PlayerbotWarlockAI(m_bot, this); + break; + case CLASS_DRUID: + m_classAI = (PlayerbotClassAI*) new PlayerbotDruidAI(m_bot, this); + break; + } +} + +PlayerbotAI::~PlayerbotAI() +{ + delete m_classAI; +} + +void PlayerbotAI::ReinitAI() +{ + m_botState = BOTSTATE_NORMAL; + m_targetCombat = NULL; + m_followTarget = m_bot; + + if (m_bot == GetLeader()) + { + m_bot->GiveLevel(m_bot->GetLevelAtLoading()); + CheckStuff(); + m_bot->TeleportTo(orig_map, orig_x, orig_y, orig_z, 0.0f); + for (uint8 i = 0; i < MAX_ARENA_SLOT; ++i) + { + uint32 a_id = m_bot->GetArenaTeamId(i); + if (a_id==0) + continue; + + ArenaTeam *at = sObjectMgr.GetArenaTeamById(a_id); + if (!at) + continue; + + if (at->DisbandNoSave(m_bot)) + delete at; + } + if (m_bot->GetGroup()) + m_bot->RemoveFromGroup(); + } +} + +Player* PlayerbotAI::GetLeader() const +{ + return m_mgr->GetLeader(); +} + +void PlayerbotAI::SetLeader(Player* pl) +{ + m_mgr->SetLeader(pl); +} + +// finds spell ID for matching substring args +// in priority of full text match, spells not taking reagents, and highest rank +uint32 PlayerbotAI::getSpellId(const char* args, bool master) const +{ + if (!*args) + return 0; + + std::string namepart = args; + std::wstring wnamepart; + + if (!Utf8toWStr(namepart, wnamepart)) + return 0; + + // converting string that we try to find to lower case + wstrToLower(wnamepart); + + int loc = 0; + if (master) + loc = GetLeader()->GetSession()->GetSessionDbcLocale(); + else + loc = m_bot->GetSession()->GetSessionDbcLocale(); + + uint32 foundSpellId = 0; + bool foundExactMatch = false; + bool foundMatchUsesNoReagents = false; + + for (PlayerSpellMap::iterator itr = m_bot->GetSpellMap().begin(); itr != m_bot->GetSpellMap().end(); ++itr) + { + uint32 spellId = itr->first; + + if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.disabled || IsPassiveSpell(spellId)) + continue; + + const SpellEntry* pSpellInfo = sSpellStore.LookupEntry(spellId); + if (!pSpellInfo) + continue; + + const std::string name = pSpellInfo->SpellName[loc]; + if (name.empty() || !Utf8FitTo(name, wnamepart)) + continue; + + bool isExactMatch = (name.length() == wnamepart.length()) ? true : false; + bool usesNoReagents = (pSpellInfo->Reagent[0] <= 0) ? true : false; + + // if we already found a spell + bool useThisSpell = true; + if (foundSpellId > 0) + { + if (isExactMatch && !foundExactMatch) {} + else if (usesNoReagents && !foundMatchUsesNoReagents) {} + else if (spellId > foundSpellId) {} + else + useThisSpell = false; + } + if (useThisSpell) + { + foundSpellId = spellId; + foundExactMatch = isExactMatch; + foundMatchUsesNoReagents = usesNoReagents; + } + } + + return foundSpellId; +} + + +uint32 PlayerbotAI::getPetSpellId(const char* args) const +{ + if (!*args) + return 0; + + Pet* pet = m_bot->GetPet(); + if (!pet) + return 0; + + std::string namepart = args; + std::wstring wnamepart; + + if (!Utf8toWStr(namepart, wnamepart)) + return 0; + + // converting string that we try to find to lower case + wstrToLower(wnamepart); + + int loc = GetLeader()->GetSession()->GetSessionDbcLocale(); + + uint32 foundSpellId = 0; + bool foundExactMatch = false; + bool foundMatchUsesNoReagents = false; + + for (PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr) + { + uint32 spellId = itr->first; + + if (itr->second.state == PETSPELL_REMOVED || IsPassiveSpell(spellId)) + continue; + + const SpellEntry* pSpellInfo = sSpellStore.LookupEntry(spellId); + if (!pSpellInfo) + continue; + + const std::string name = pSpellInfo->SpellName[loc]; + if (name.empty() || !Utf8FitTo(name, wnamepart)) + continue; + + bool isExactMatch = (name.length() == wnamepart.length()) ? true : false; + bool usesNoReagents = (pSpellInfo->Reagent[0] <= 0) ? true : false; + + // if we already found a spell + bool useThisSpell = true; + if (foundSpellId > 0) + { + if (isExactMatch && !foundExactMatch) {} + else if (usesNoReagents && !foundMatchUsesNoReagents) {} + else if (spellId > foundSpellId) {} + else + useThisSpell = false; + } + if (useThisSpell) + { + foundSpellId = spellId; + foundExactMatch = isExactMatch; + foundMatchUsesNoReagents = usesNoReagents; + } + } + + return foundSpellId; +} + +uint32 PlayerbotAI::initSpell(uint32 spellId) +{ + // Check if bot knows this spell + if (!m_bot->HasSpell(spellId)) + return 0; + + uint32 next = 0; + SpellChainMapNext const& nextMap = sSpellMgr.GetSpellChainNext(); + for (SpellChainMapNext::const_iterator itr = nextMap.lower_bound(spellId); itr != nextMap.upper_bound(spellId); ++itr) + { + // Work around buggy chains + if (sSpellStore.LookupEntry(spellId)->SpellIconID != sSpellStore.LookupEntry(itr->second)->SpellIconID) + continue; + + SpellChainNode const* node = sSpellMgr.GetSpellChainNode(itr->second); + // If next spell is a requirement for this one then skip it + if (node->req == spellId) + continue; + if (node->prev == spellId) + { + next = initSpell(itr->second); + break; + } + } + if (next == 0) + { + const SpellEntry* const pSpellInfo = sSpellStore.LookupEntry(spellId); + sLog.outDebug("Playerbot spell init: %s is %u", pSpellInfo->SpellName[0], spellId); + + // Add spell to spellrange map + Spell *spell = new Spell(m_bot, pSpellInfo, false); + SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(pSpellInfo->rangeIndex); + float range = GetSpellMaxRange(srange, IsPositiveSpell(spellId)); + m_bot->ApplySpellMod(spellId, SPELLMOD_RANGE, range, spell); + m_spellRangeMap.insert(std::pair(spellId, range)); + delete spell; + } + return (next == 0) ? spellId : next; +} + + +// Pet spells do not form chains like player spells. +// One of the options to initialize a spell is to use spell icon id +uint32 PlayerbotAI::initPetSpell(uint32 spellIconId) +{ + Pet * pet = m_bot->GetPet(); + + if (!pet) + return 0; + + for (PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr) + { + const uint32 spellId = itr->first; + + if (itr->second.state == PETSPELL_REMOVED || IsPassiveSpell(spellId)) + continue; + + const SpellEntry* const pSpellInfo = sSpellStore.LookupEntry(spellId); + if (!pSpellInfo) + continue; + + if (pSpellInfo->SpellIconID == spellIconId) + return spellId; + } + + // Nothing found + return 0; +} + +/* + * Send a list of equipment that is in bot's inventor that is currently unequipped. + * This is called when the master is inspecting the bot. + */ + +// handle outgoing packets the server would send to the client +void PlayerbotAI::HandleBotOutgoingPacket(const WorldPacket& packet) +{ + switch (packet.GetOpcode()) + { + case SMSG_DUEL_WINNER: + { + m_bot->HandleEmoteCommand(EMOTE_ONESHOT_APPLAUD); + return; + } + case SMSG_DUEL_COMPLETE: + { + m_bot->GetMotionMaster()->Clear(true); + return; + } + case SMSG_DUEL_OUTOFBOUNDS: + { + m_bot->HandleEmoteCommand(EMOTE_ONESHOT_CHICKEN); + return; + } + case SMSG_DUEL_REQUESTED: + { + WorldPacket p(packet); + uint64 flagGuid; + p >> flagGuid; + uint64 playerGuid; + p >> playerGuid; + + WorldPacket* const packet = new WorldPacket(CMSG_DUEL_ACCEPTED, 8); + *packet << flagGuid; + m_bot->GetSession()->QueuePacket(packet); // queue the packet to get around race condition + + m_ignoreAIUpdatesUntilTime = time(0) + 3; + return; + } + + case SMSG_SPELL_FAILURE: + { + WorldPacket p(packet); + uint8 castCount; + uint32 spellId; + + uint64 casterGuid = p.readPackGUID(); + if (casterGuid != m_bot->GetGUID()) + return; + + p >> castCount >> spellId; + if (m_CurrentlyCastingSpellId == spellId) + { + m_ignoreAIUpdatesUntilTime = time(0); + m_CurrentlyCastingSpellId = 0; + } + return; + } + + // handle flying acknowledgement + case SMSG_MOVE_SET_CAN_FLY: + { + WorldPacket p(packet); + uint64 guid = p.readPackGUID(); + if (guid != m_bot->GetGUID()) + return; + m_bot->m_movementInfo.AddMovementFlag(MOVEFLAG_FLYING); + //m_bot->SetSpeed(MOVE_RUN, GetLeader()->GetSpeed(MOVE_FLIGHT) +0.1f, true); + return; + } + + // handle dismount flying acknowledgement + case SMSG_MOVE_UNSET_CAN_FLY: + { + WorldPacket p(packet); + uint64 guid = p.readPackGUID(); + if (guid != m_bot->GetGUID()) + return; + m_bot->m_movementInfo.RemoveMovementFlag(MOVEFLAG_FLYING); + //m_bot->SetSpeed(MOVE_RUN,GetLeader()->GetSpeedRate(MOVE_RUN),true); + return; + } + + // If the master role was given to the bot automatically give it to the master + // if the master is in the group, otherwise leave group + case SMSG_GROUP_SET_LEADER: + { + WorldPacket p(packet); + std::string name; + p >> name; + if (m_bot->GetGroup() && name == m_bot->GetName()) + { + Player* newLeader = FindNewGroupLeader(); + if (newLeader) + { + SetLeader(newLeader); + m_bot->GetGroup()->ChangeLeader(newLeader->GetGUID()); + } + else if (m_bot->GetGroup()->IsMember(GetLeader()->GetGUID())) + { + m_bot->GetGroup()->ChangeLeader(newLeader->GetGUID()); + } + } + return; + } + + // Handle Group invites (auto accept if master is in group, otherwise decline & send message + case SMSG_GROUP_INVITE: + { + if (m_bot->GetGroupInvite()) + { + const Group* const grp = m_bot->GetGroupInvite(); + if (!grp) + return; + + Player* const inviter = sObjectMgr.GetPlayer(grp->GetLeaderGuid()); + if (!inviter) + { + m_bot->SetGroupInvite(NULL); + return; + } + + if (inviter->GetBattleGround()) + { + m_bot->SetGroupInvite(NULL); + return; + } + + WorldPacket p; + m_bot->GetSession()->HandleGroupAcceptOpcode(p); + MovementClear(); + if (!inviter->IsBot()) + SetMovementTarget(inviter); + m_ignoreAIUpdatesUntilTime = time(0); + } + return; + } + + case SMSG_PETITION_SHOW_SIGNATURES: + { + WorldPacket p(packet); + ObjectGuid petitionguid; + p >> petitionguid; + + WorldPacket packet = WorldPacket(CMSG_PETITION_SIGN, 8+1); + packet << petitionguid; + packet << uint8(1); + m_bot->GetSession()->HandlePetitionSignOpcode(packet); + return; + } + + case SMSG_ARENA_TEAM_INVITE: + { + WorldPacket pk; + m_bot->GetSession()->HandleArenaTeamAcceptOpcode(pk); + break; + } + + case SMSG_BATTLEFIELD_STATUS: + { + WorldPacket p(packet); + if (p.size() == 4+8) + return; + + uint32 QueueSlot; + uint64 Type; + uint8 unk1; + uint8 unk2; + uint32 ClientInstanceID; + uint8 Rated; + uint32 StatusID; + + p >> QueueSlot >> Type >> unk1 >> unk2 >> ClientInstanceID >> Rated >> StatusID; + + if (StatusID == STATUS_WAIT_JOIN) + { + WorldPacket packet = WorldPacket(CMSG_BATTLEFIELD_PORT,1+1+4+2+1); + packet << uint64(Type); + packet << uint8(m_bot->InBattleGroundQueue()); // enter battle 0x1, leave queue 0x0 + m_bot->GetSession()->HandleBattleFieldPortOpcode(packet); + m_bot->GetPlayerbotAI()->SetEnterBGTime(60); + } + return; + } + case MSG_PVP_LOG_DATA: + { + BattleGround *bg = m_bot->GetBattleGround(); + if (bg && bg->GetStatus() == STATUS_WAIT_LEAVE) + { + m_leaveBg = time(0) + 10; + m_ignoreAIUpdatesUntilTime = time(0) + 10; + } + return; + } + // Handle when another player opens the trade window with the bot + // also sends list of tradable items bot can trade if bot is allowed to obey commands from + /*case SMSG_TRADE_STATUS: + { + if (m_bot->GetTrader() == NULL) + break; + + WorldPacket p(packet); + uint32 status; + p >> status; + p.resize(4); + + //4 == TRADE_STATUS_TRADE_ACCEPT + if (status == 4) + m_bot->GetSession()->HandleAcceptTradeOpcode(p); // packet not used + + //1 == TRADE_STATUS_BEGIN_TRADE + else if (status == 1) + { + m_bot->GetSession()->HandleBeginTradeOpcode(p); // packet not used + + if (!canObeyCommandFrom(*(m_bot->GetTrader()))) + return; + + // list out items available for trade + + + // list out items in main backpack + for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) + { + const Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + if (pItem && pItem->CanBeTraded()) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + + std::string itemName = pItemProto->Name1; + ItemLocalization(itemName, pItemProto->ItemId); + + + << ":0:0:0:0:0:0:0" << "|h[" << itemName << "]|h|r"; + if (pItem->GetCount() > 1) + + } + } + // list out items in other removable backpacks + for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) + { + const Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); + if (pBag) + for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot) + { + const Item* const pItem = m_bot->GetItemByPos(bag, slot); + if (pItem && pItem->CanBeTraded()) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + + std::string itemName = pItemProto->Name1; + ItemLocalization(itemName, pItemProto->ItemId); + + // item link format: http://www.wowwiki.com/ItemString + // itemId, enchantId, jewelId1, jewelId2, jewelId3, jewelId4, suffixId, uniqueId + + << ":0:0:0:0:0:0:0" << "|h[" << itemName + << "]|h|r"; + if (pItem->GetCount() > 1) + + } + } + } + + // calculate how much money bot has + uint32 copper = m_bot->GetMoney(); + uint32 gold = uint32(copper / 10000); + copper -= (gold * 10000); + uint32 silver = uint32(copper / 100); + copper -= (silver * 100); + + // send bot the message + + whisper << "Je possède actuellement |cff00ff00" << gold + << "|r|cfffffc00g|r|cff00ff00" << silver + << "|r|cffcdcdcds|r|cff00ff00" << copper + << "|r|cffffd333c|r" << " et je peux échanger les objets suivants :"; + + ChatHandler ch(m_bot->GetTrader()); + ch.SendSysMessage(out.str().c_str()); + } + return; + }*/ + + case SMSG_SPELL_GO: + { + WorldPacket p(packet); + uint64 castItemGuid = p.readPackGUID(); + uint64 casterGuid = p.readPackGUID(); + if (casterGuid != m_bot->GetGUID()) + return; + + uint32 spellId; + p >> spellId; + uint16 castFlags; + p >> castFlags; + uint32 msTime; + p >> msTime; + uint8 numHit; + p >> numHit; + + if (m_CurrentlyCastingSpellId == spellId) + { + Spell* const pSpell = m_bot->FindCurrentSpellBySpellId(spellId); + if (!pSpell) + return; + + if (pSpell->IsChannelActive() || pSpell->IsAutoRepeat()) + m_ignoreAIUpdatesUntilTime = time(0) + (GetSpellDuration(pSpell->m_spellInfo) / 1000) + 1; + else if (pSpell->IsAutoRepeat()) + m_ignoreAIUpdatesUntilTime = time(0) + 6; + else + { + m_ignoreAIUpdatesUntilTime = time(0) + 1; + m_CurrentlyCastingSpellId = 0; + } + } + return; + } + + // if someone tries to resurrect, then accept + case SMSG_RESURRECT_REQUEST: + { + if (!m_bot->isAlive()) + { + WorldPacket p(packet); + ObjectGuid guid; + p >> guid; + + WorldPacket* const packet = new WorldPacket(CMSG_RESURRECT_RESPONSE, 8+1); + *packet << guid; + *packet << uint8(1); // accept + m_bot->GetSession()->QueuePacket(packet); // queue the packet to get around race condition + + // set back to normal + SetState(BOTSTATE_NORMAL); + SetIgnoreUpdateTime(0); + } + return; + } + /*case SMSG_MONSTER_MOVE: + case SMSG_UPDATE_WORLD_STATE: + case SMSG_COMPRESSED_UPDATE_OBJECT: + case MSG_MOVE_SET_FACING: + case MSG_MOVE_STOP: + case MSG_MOVE_HEARTBEAT: + case MSG_MOVE_STOP_STRAFE: + case MSG_MOVE_START_STRAFE_LEFT: + case SMSG_UPDATE_OBJECT: + case MSG_MOVE_START_FORWARD: + case MSG_MOVE_START_STRAFE_RIGHT: + case SMSG_DESTROY_OBJECT: + case MSG_MOVE_START_BACKWARD: + case SMSG_AURA_UPDATE_ALL: + case MSG_MOVE_FALL_LAND: + case MSG_MOVE_JUMP: + case CMSG_NAME_QUERY: + case CMSG_STANDSTATECHANGE: + case CMSG_QUERY_TIME: + case CMSG_CREATURE_QUERY: + case CMSG_GAMEOBJECT_QUERY: + case SMSG_TIME_SYNC_REQ: + case CMSG_TIME_SYNC_RESP: + case CMSG_WORLD_STATE_UI_TIMER_UPDATE: + case SMSG_WORLD_STATE_UI_TIMER_UPDATE: + case SMSG_EMOTE: + case SMSG_POWER_UPDATE: + case SMSG_AURA_UPDATE: + case SMSG_PERIODICAURALOG: + break; + default: + { + const char* oc = LookupOpcodeName(packet.GetOpcode()); + sLog.outString(oc); + }*/ + } +} + +uint8 PlayerbotAI::GetHealthPercent(const Unit& target) const +{ + return (static_cast (target.GetHealth()) / target.GetMaxHealth()) * 100; +} + +uint8 PlayerbotAI::GetHealthPercent() const +{ + return GetHealthPercent(*m_bot); +} + +uint8 PlayerbotAI::GetManaPercent(const Unit& target) const +{ + return (static_cast (target.GetPower(POWER_MANA)) / target.GetMaxPower(POWER_MANA)) * 100; +} + +uint8 PlayerbotAI::GetManaPercent() const +{ + return GetManaPercent(*m_bot); +} + +uint8 PlayerbotAI::GetBaseManaPercent(const Unit& target) const +{ + if (target.GetPower(POWER_MANA) >= target.GetCreateMana()) + return (100); + else + return (static_cast (target.GetPower(POWER_MANA)) / target.GetCreateMana()) * 100; +} + +uint8 PlayerbotAI::GetBaseManaPercent() const +{ + return GetBaseManaPercent(*m_bot); +} + +uint8 PlayerbotAI::GetRageAmount(const Unit& target) const +{ + return (static_cast (target.GetPower(POWER_RAGE))); +} + +uint8 PlayerbotAI::GetRageAmount() const +{ + return GetRageAmount(*m_bot); +} + +uint8 PlayerbotAI::GetEnergyAmount(const Unit& target) const +{ + return (static_cast (target.GetPower(POWER_ENERGY))); +} + +uint8 PlayerbotAI::GetEnergyAmount() const +{ + return GetEnergyAmount(*m_bot); +} + +uint8 PlayerbotAI::GetRunicPower(const Unit& target) const +{ + return (static_cast(target.GetPower(POWER_RUNIC_POWER))); +} + +uint8 PlayerbotAI::GetRunicPower() const +{ + return GetRunicPower(*m_bot); +} + +// looks through all items / spells that bot could have to get a mount +Item* PlayerbotAI::FindMount(uint32 matchingRidingSkill) const +{ + // list out items in main backpack + + Item* partialMatch = NULL; + + for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) + { + Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + if (!pItemProto || m_bot->CanUseItem(pItemProto) != EQUIP_ERR_OK || pItemProto->RequiredSkill != SKILL_RIDING) + continue; + + if (pItemProto->RequiredSkillRank == matchingRidingSkill) + return pItem; + + else if (!partialMatch || (partialMatch && partialMatch->GetProto()->RequiredSkillRank < pItemProto->RequiredSkillRank)) + partialMatch = pItem; + } + } + + // list out items in other removable backpacks + for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) + { + const Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); + if (pBag) + for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot) + { + Item* const pItem = m_bot->GetItemByPos(bag, slot); + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + if (!pItemProto || m_bot->CanUseItem(pItemProto) != EQUIP_ERR_OK || pItemProto->RequiredSkill != SKILL_RIDING) + continue; + + if (pItemProto->RequiredSkillRank == matchingRidingSkill) + return pItem; + + else if (!partialMatch || (partialMatch && partialMatch->GetProto()->RequiredSkillRank < pItemProto->RequiredSkillRank)) + partialMatch = pItem; + } + } + } + return partialMatch; +} + +Player* PlayerbotAI::FindNewGroupLeader() +{ + Player* pl = NULL; + if (m_bot->GetGroup()) + { + GroupReference *ref = m_bot->GetGroup()->GetFirstMember(); + while (ref) + { + if (!ref->getSource()->IsBot()) + { + pl = ref->getSource(); + break; + } + ref = ref->next(); + } + } + return pl; +} + +void PlayerbotAI::CheckBG() +{ + BattleGround *bg = m_bot->GetBattleGround(); + if (bg) + { + bool existsHuman = false; + BattleGroundPlayerMap players = bg->GetPlayers(); + for(BattleGroundPlayerMap::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + Player *plr = ObjectAccessor::FindPlayer(itr->first); + if (!plr || !plr->IsInWorld() || plr->IsBot()) + continue; + + existsHuman = true; + break; + } + if ( (!existsHuman && m_enterBg < time(0)) || (bg->GetStatus() == STATUS_WAIT_LEAVE && m_leaveBg < time(0)) ) + { + m_bot->LeaveBattleground(); + if (GetLeader()->IsBot()) + { + SetLeader(m_bot); + ReinitAI(); + } + } + } +} + +void PlayerbotAI::CheckMount() +{ + if ((GetLeader()->IsMounted()) && (!m_bot->IsMounted())) + { + if (!GetLeader()->GetAurasByType(SPELL_AURA_MOUNTED).empty()) + { + int32 speed1 = GetLeader()->GetAurasByType(SPELL_AURA_MOUNTED).front()->GetSpellProto()->EffectBasePoints[1]; + int32 speed2 = GetLeader()->GetAurasByType(SPELL_AURA_MOUNTED).front()->GetSpellProto()->EffectBasePoints[2]; + + Item* pItem225 = m_bot->GetPlayerbotAI()->FindMount(225); + Item* pItem300 = m_bot->GetPlayerbotAI()->FindMount(300); + + if (pItem300 && (speed1 > 150 || speed2 > 150)) + { + m_bot->GetPlayerbotAI()->UseItem(pItem300); + return; + } + else if (pItem225) + { + m_bot->GetPlayerbotAI()->UseItem(pItem225); + return; + } + if (pItem300) + { + m_bot->GetPlayerbotAI()->UseItem(pItem300); + return; + } + + for (PlayerSpellMap::iterator itr = m_bot->GetSpellMap().begin(); itr != m_bot->GetSpellMap().end(); ++itr) + { + uint32 spellId = itr->first; + if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.disabled || IsPassiveSpell(spellId)) + continue; + + const SpellEntry* pSpellInfo = sSpellStore.LookupEntry(spellId); + if (!pSpellInfo) + continue; + + if (pSpellInfo->EffectApplyAuraName[0] == SPELL_AURA_MOUNTED) + { + if (pSpellInfo->EffectApplyAuraName[1] == SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED + && pSpellInfo->EffectBasePoints[1] == speed1) + { + CastSpell(spellId, m_bot); + break; + } + else if ((pSpellInfo->EffectApplyAuraName[1] == SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED) + && (pSpellInfo->EffectApplyAuraName[2] == SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED) + && (pSpellInfo->EffectBasePoints[1] == speed1) + && (pSpellInfo->EffectBasePoints[2] == speed2)) + { + CastSpell(spellId, m_bot); + break; + } + else if ((pSpellInfo->EffectApplyAuraName[2] == SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED) + && (pSpellInfo->EffectApplyAuraName[1] == SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED) + && (pSpellInfo->EffectBasePoints[2] == speed2) + && (pSpellInfo->EffectBasePoints[1] == speed1)) + { + CastSpell(spellId, m_bot); + break; + } + } + } + } + } + else if (!GetLeader()->IsMounted() && m_bot->IsMounted()) + { + WorldPacket emptyPacket; + m_bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket); + m_bot->Unmount(); + m_bot->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + m_bot->RemoveSpellsCausingAura(SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED); + m_bot->RemoveSpellsCausingAura(SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED); + } +} + +Item* PlayerbotAI::FindFood() const +{ + // list out items in main backpack + for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) + { + Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + if (!pItemProto || m_bot->CanUseItem(pItemProto) != EQUIP_ERR_OK) + continue; + + if (pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_FOOD) + // if is FOOD + // this enum is no longer defined in mangos. Is it no longer valid? + // according to google it was 11 + if (pItemProto->Spells[0].SpellCategory == 11) + return pItem; + } + } + // list out items in other removable backpacks + for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) + { + const Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); + if (pBag) + for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot) + { + Item* const pItem = m_bot->GetItemByPos(bag, slot); + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + + if (!pItemProto || m_bot->CanUseItem(pItemProto) != EQUIP_ERR_OK) + continue; + + // this enum is no longer defined in mangos. Is it no longer valid? + // according to google it was 11 + if (pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_FOOD) + // if is FOOD + // this enum is no longer defined in mangos. Is it no longer valid? + // according to google it was 11 + // if (pItemProto->Spells[0].SpellCategory == SPELL_CATEGORY_FOOD) + if (pItemProto->Spells[0].SpellCategory == 11) + return pItem; + } + } + } + return NULL; +} + +Item* PlayerbotAI::FindDrink() const +{ + // list out items in main backpack + for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) + { + Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + + if (!pItemProto || m_bot->CanUseItem(pItemProto) != EQUIP_ERR_OK) + continue; + + if (pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_FOOD) + // if (pItemProto->Spells[0].SpellCategory == SPELL_CATEGORY_DRINK) + + // this enum is no longer defined in mangos. Is it no longer valid? + // according to google it was 59 + // if (pItemProto->Spells[0].SpellCategory == 59) + if (pItemProto->Spells[0].SpellCategory == 59) + return pItem; + } + } + // list out items in other removable backpacks + for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) + { + const Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); + if (pBag) + for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot) + { + Item* const pItem = m_bot->GetItemByPos(bag, slot); + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + + if (!pItemProto || m_bot->CanUseItem(pItemProto) != EQUIP_ERR_OK) + continue; + + if (pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_FOOD) + // if is WATER + // SPELL_CATEGORY_DRINK is no longer defined in an enum in mangos + // google says the valus is 59. Is this still valid? + // if (pItemProto->Spells[0].SpellCategory == SPELL_CATEGORY_DRINK) + if (pItemProto->Spells[0].SpellCategory == 59) + return pItem; + } + } + } + return NULL; +} + +Item* PlayerbotAI::FindBandage() const +{ + // list out items in main backpack + for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) + { + Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + + if (!pItemProto || m_bot->CanUseItem(pItemProto) != EQUIP_ERR_OK) + continue; + + if (pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_BANDAGE) + return pItem; + } + } + // list out items in other removable backpacks + for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) + { + const Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); + if (pBag) + for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot) + { + Item* const pItem = m_bot->GetItemByPos(bag, slot); + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + + if (!pItemProto || m_bot->CanUseItem(pItemProto) != EQUIP_ERR_OK) + continue; + + if (pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_BANDAGE) + return pItem; + } + } + } + return NULL; +} +//Find Poison ...Natsukawa +Item* PlayerbotAI::FindPoison() const +{ + // list out items in main backpack + for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) + { + Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + + if (!pItemProto || m_bot->CanUseItem(pItemProto) != EQUIP_ERR_OK) + continue; + + if (pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == 6) + return pItem; + } + } + // list out items in other removable backpacks + for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) + { + const Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); + if (pBag) + for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot) + { + Item* const pItem = m_bot->GetItemByPos(bag, slot); + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + + if (!pItemProto || m_bot->CanUseItem(pItemProto) != EQUIP_ERR_OK) + continue; + + if (pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == 6) + return pItem; + } + } + } + return NULL; +} + +Item* PlayerbotAI::FindConsumable(uint32 displayId) const +{ + // list out items in main backpack + for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) + { + Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + + if (!pItemProto || m_bot->CanUseItem(pItemProto) != EQUIP_ERR_OK) + continue; + + if (pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->DisplayInfoID == displayId) + return pItem; + } + } + // list out items in other removable backpacks + for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) + { + const Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); + if (pBag) + for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot) + { + Item* const pItem = m_bot->GetItemByPos(bag, slot); + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + + if (!pItemProto || m_bot->CanUseItem(pItemProto) != EQUIP_ERR_OK) + continue; + + if (pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->DisplayInfoID == displayId) + return pItem; + } + } + } + return NULL; +} + +void PlayerbotAI::InterruptCurrentCastingSpell() +{ + WorldPacket* const packet = new WorldPacket(CMSG_CANCEL_CAST, 5); //changed from thetourist suggestion + *packet << m_CurrentlyCastingSpellId; + *packet << m_targetGuidCommand; //changed from thetourist suggestion + m_CurrentlyCastingSpellId = 0; + m_bot->GetSession()->QueuePacket(packet); +} + +void PlayerbotAI::Feast() +{ + // stand up if we are done feasting + if (!(m_bot->GetHealth() < m_bot->GetMaxHealth() || (m_bot->getPowerType() == POWER_MANA && m_bot->GetPower(POWER_MANA) < m_bot->GetMaxPower(POWER_MANA)))) + { + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + return; + } + + // wait 3 seconds before checking if we need to drink more or eat more + time_t currentTime = time(0); + m_ignoreAIUpdatesUntilTime = currentTime + 3; + + // should we drink another + if (m_bot->getPowerType() == POWER_MANA && currentTime > m_TimeDoneDrinking + && ((static_cast (m_bot->GetPower(POWER_MANA)) / m_bot->GetMaxPower(POWER_MANA)) < 0.8)) + { + Item* pItem = FindDrink(); + if (pItem != NULL) + { + UseItem(pItem); + m_TimeDoneDrinking = currentTime + 30; + return; + } + } + + // should we eat another + if (currentTime > m_TimeDoneEating && ((static_cast (m_bot->GetHealth()) / m_bot->GetMaxHealth()) < 0.8)) + { + Item* pItem = FindFood(); + if (pItem != NULL) + { + UseItem(pItem); + m_TimeDoneEating = currentTime + 30; + return; + } + } + + // if we are no longer eating or drinking + // because we are out of items or we are above 80% in both stats + if (currentTime > m_TimeDoneEating && currentTime > m_TimeDoneDrinking) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); +} + +// intelligently sets a reasonable combat order for this bot +// based on its class / level / etc +bool PlayerbotAI::GetCombatTarget(Unit* forcedTarget) +{ + if (m_botState != BOTSTATE_COMBAT) + { + SetState(BOTSTATE_COMBAT); + SetQuestNeedItems(); + m_lootCreature.clear(); + m_lootCurrent = 0; + m_targetCombat = NULL; + } + + if (forcedTarget) + { + m_targetCombat = forcedTarget; + return true; + } + + /*TODO BG WP*/ + if (m_bot->GetBattleGround()) + { + Unit* target = FindEnemy(); + if (target && target != m_targetCombat) + { + m_targetCombat = target; + return true; + } + } + + m_targetCombat = NULL; + + if (!m_bot->getAttackers().empty()) + { + for(Unit::AttackerSet::const_iterator itr = m_bot->getAttackers().begin(); itr != m_bot->getAttackers().end(); ++itr) + { + m_targetCombat = (*itr)->GetOwner(); + if (!m_targetCombat || !m_targetCombat->isAlive()) + m_targetCombat = (*itr); + if (!m_targetCombat) + { + m_targetCombat = FindEnemy(); + if (!m_targetCombat) + return false; + return true; + } + else + return true; + } + } + if (!GetLeader()->getAttackers().empty()) + { + for(Unit::AttackerSet::const_iterator itr = GetLeader()->getAttackers().begin(); itr != GetLeader()->getAttackers().end(); ++itr) + { + m_targetCombat = (*itr)->GetOwner(); + if (!m_targetCombat || !m_targetCombat->isAlive()) + m_targetCombat = (*itr); + if (!m_targetCombat) + { + m_targetCombat = FindEnemy(); + if (!m_targetCombat) + return false; + return true; + } + else + return true; + } + } + if (m_bot->GetGroup()) + { + GroupReference *ref = m_bot->GetGroup()->GetFirstMember(); + while (ref) + { + if (ref->getSource()->GetPet() && !ref->getSource()->GetPet()->getAttackers().empty()) + { + for(Unit::AttackerSet::const_iterator itr = ref->getSource()->GetPet()->getAttackers().begin(); itr != ref->getSource()->GetPet()->getAttackers().end(); ++itr) + { + m_targetCombat = (*itr)->GetOwner(); + if (!m_targetCombat || !m_targetCombat->isAlive()) + m_targetCombat = (*itr); + if (!m_targetCombat) + { + m_targetCombat = FindEnemy(); + if (!m_targetCombat) + return false; + return true; + } + else + return true; + } + } + ref = ref->next(); + } + } + + if (m_bot->GetPet() && !m_bot->GetPet()->getAttackers().empty()) + { + for(Unit::AttackerSet::const_iterator itr = m_bot->GetPet()->getAttackers().begin(); itr != m_bot->GetPet()->getAttackers().end(); ++itr) + { + m_targetCombat = (*itr)->GetOwner(); + if (!m_targetCombat || !m_targetCombat->isAlive()) + m_targetCombat = (*itr); + if (!m_targetCombat) + { + m_targetCombat = FindEnemy(); + if (!m_targetCombat) + return false; + return true; + } + else + return true; + } + } + if (GetLeader()->GetPet() && !GetLeader()->GetPet()->getAttackers().empty()) + { + for(Unit::AttackerSet::const_iterator itr = GetLeader()->GetPet()->getAttackers().begin(); itr != GetLeader()->GetPet()->getAttackers().end(); ++itr) + { + m_targetCombat = (*itr)->GetOwner(); + if (!m_targetCombat || !m_targetCombat->isAlive()) + m_targetCombat = (*itr); + if (!m_targetCombat) + { + m_targetCombat = FindEnemy(); + if (!m_targetCombat) + return false; + return true; + } + else + return true; + } + } + if (m_bot->GetGroup()) + { + GroupReference *ref = m_bot->GetGroup()->GetFirstMember(); + while (ref) + { + if (!ref->getSource()->getAttackers().empty()) + { + for(Unit::AttackerSet::const_iterator itr = ref->getSource()->getAttackers().begin(); itr != ref->getSource()->getAttackers().end(); ++itr) + { + m_targetCombat = (*itr)->GetOwner(); + if (!m_targetCombat || !m_targetCombat->isAlive()) + m_targetCombat = (*itr); + if (!m_targetCombat) + { + m_targetCombat = FindEnemy(); + if (!m_targetCombat) + return false; + return true; + } + else + return true; + } + } + ref = ref->next(); + } + } + + return false; +} + +void PlayerbotAI::DoNextCombatManeuver() +{ + bool targetChanged = false; + if (m_bot->GetBattleGround()) + { + if (!m_targetCombat || m_targetCombat->isDead() || !m_targetCombat->IsInWorld() + || !m_bot->IsInMap(m_targetCombat) || !((Player*)m_targetCombat)->GetBattleGround()) + { + targetChanged = GetCombatTarget(); + } + + if (!m_targetCombat || m_targetCombat->isDead() || !m_targetCombat->IsInWorld() + || !m_bot->IsInMap(m_targetCombat) || !((Player*)m_targetCombat)->GetBattleGround()) + { + m_bot->AttackStop(); + m_bot->SetSelectionGuid(ObjectGuid()); + m_bot->InterruptNonMeleeSpells(true); + m_targetCombat = NULL; + SetMovementTarget(GetLeader()); + return; + } + else + { + SetMovementTarget(m_targetCombat); + if (targetChanged) + { + m_bot->SetSelectionGuid((m_targetCombat->GetObjectGuid())); + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + m_bot->Attack(m_targetCombat, true); + m_lootCreature.push_back(m_targetCombat->GetGUID()); + GetClassAI()->DoFirstCombatManeuver(m_targetCombat); + } + else + GetClassAI()->DoNextCombatManeuver(m_targetCombat); + } + } + else + { + if (!m_targetCombat || m_targetCombat->isDead() || !m_targetCombat->IsInWorld() + || !m_bot->IsWithinDistInMap(m_targetCombat, 50) || !m_bot->IsHostileTo(m_targetCombat) + || !m_bot->IsInMap(m_targetCombat)) + { + targetChanged = GetCombatTarget(); + } + + if (!m_targetCombat || m_targetCombat->isDead() || !m_targetCombat->IsInWorld() + || !m_bot->IsWithinDistInMap(m_targetCombat, 50) || !m_bot->IsHostileTo(m_targetCombat) + || !m_bot->IsInMap(m_targetCombat)) + { + m_bot->AttackStop(); + m_bot->SetSelectionGuid(ObjectGuid()); + m_bot->InterruptNonMeleeSpells(true); + m_targetCombat = NULL; + SetMovementTarget(GetLeader()); + return; + } + else + { + SetMovementTarget(m_targetCombat); + if (targetChanged) + { + m_bot->SetSelectionGuid((m_targetCombat->GetObjectGuid())); + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + m_bot->Attack(m_targetCombat, true); + m_lootCreature.push_back(m_targetCombat->GetGUID()); + GetClassAI()->DoFirstCombatManeuver(m_targetCombat); + } + else + GetClassAI()->DoNextCombatManeuver(m_targetCombat); + } + } +} + +void PlayerbotAI::SetQuestNeedItems() +{ + // reset values first + m_needItemList.clear(); + m_lootCreature.clear(); + m_lootCurrent = 0; + + // run through accepted quests, get quest infoand data + for (QuestStatusMap::iterator iter = m_bot->getQuestStatusMap().begin(); iter != m_bot->getQuestStatusMap().end(); ++iter) + { + const Quest *qInfo = sObjectMgr.GetQuestTemplate(iter->first); + if (!qInfo) + continue; + + QuestStatusData *qData = &iter->second; + // only check quest if it is incomplete + if (qData->m_status != QUEST_STATUS_INCOMPLETE) + continue; + + // check for items we not have enough of + for (int i = 0; i < QUEST_OBJECTIVES_COUNT; i++) + { + if (!qInfo->ReqItemCount[i] || (qInfo->ReqItemCount[i] - qData->m_itemcount[i]) <= 0) + continue; + m_needItemList[qInfo->ReqItemId[i]] = (qInfo->ReqItemCount[i] - qData->m_itemcount[i]); + } + } +} + +void PlayerbotAI::SetState(BotState state) +{ + //sLog.outDebug( "[PlayerbotAI]: %s switch state %d to %d", m_bot->GetName(), m_botState, state ); + m_botState = state; +} + +uint8 PlayerbotAI::GetFreeBagSpace() const +{ + uint8 space = 0; + for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) + { + Item *pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i); + if (!pItem) + ++space; + } + for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) + { + Bag* pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i); + if (pBag && pBag->GetProto()->BagFamily == BAG_FAMILY_MASK_NONE) + space += pBag->GetFreeSlots(); + } + return space; +} + +void PlayerbotAI::DoLoot() +{ + if (!m_lootCurrent && m_lootCreature.empty()) + { + //sLog.outDebug( "[PlayerbotAI]: %s reset loot list / go back to idle", m_bot->GetName() ); + m_botState = BOTSTATE_NORMAL; + SetQuestNeedItems(); + return; + } + + if (!m_lootCurrent) + { + m_lootCurrent = m_lootCreature.front(); + m_lootCreature.pop_front(); + Creature *c = m_bot->GetMap()->GetCreature(m_lootCurrent); + // check if we got a creature and if it is still a corpse, otherwise bot runs to spawn point + if (!c || c->getDeathState() != CORPSE || GetLeader()->GetDistance(c) > BOTLOOT_DISTANCE) + { + m_lootCurrent = 0; + return; + } + m_bot->GetMotionMaster()->MovePoint(c->GetMapId(), c->GetPositionX(), c->GetPositionY(), c->GetPositionZ()); + //sLog.outDebug( "[PlayerbotAI]: %s is going to loot '%s' deathState=%d", m_bot->GetName(), c->GetName(), c->getDeathState() ); + } + else + { + Creature *c = m_bot->GetMap()->GetCreature(m_lootCurrent); + if (!c || c->getDeathState() != CORPSE || GetLeader()->GetDistance(c) > BOTLOOT_DISTANCE) + { + m_lootCurrent = 0; + return; + } + if (m_bot->IsWithinDistInMap(c, INTERACTION_DISTANCE)) + { + // check for needed items + m_bot->SendLoot(m_lootCurrent, LOOT_CORPSE); + Loot *loot = &c->loot; + uint32 lootNum = loot->GetMaxSlotInLootFor(m_bot); + //sLog.outDebug( "[PlayerbotAI]: %s looting: '%s' got %d items", m_bot->GetName(), c->GetName(), loot->GetMaxSlotInLootFor( m_bot ) ); + for (uint32 l = 0; l < lootNum; l++) + { + QuestItem *qitem = 0, *ffaitem = 0, *conditem = 0; + LootItem *item = loot->LootItemInSlot(l, m_bot, &qitem, &ffaitem, &conditem); + if (!item) + continue; + + if (!qitem && item->is_blocked) + { + m_bot->SendLootRelease(m_bot->GetLootGUID()); + continue; + } + + if (m_needItemList[item->itemid] > 0) + { + //sLog.outDebug( "[PlayerbotAI]: %s looting: needed item '%s'", m_bot->GetName(), sObjectMgr.GetItemLocale(item->itemid)->Name ); + ItemPosCountVec dest; + if (m_bot->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item->itemid, item->count) == EQUIP_ERR_OK) + { + Item * newitem = m_bot->StoreNewItem(dest, item->itemid, true, item->randomPropertyId); + + if (qitem) + { + qitem->is_looted = true; + if (item->freeforall || loot->GetPlayerQuestItems().size() == 1) + m_bot->SendNotifyLootItemRemoved(l); + else + loot->NotifyQuestItemRemoved(qitem->index); + } + else + { + if (ffaitem) + { + ffaitem->is_looted = true; + m_bot->SendNotifyLootItemRemoved(l); + } + else + { + if (conditem) + conditem->is_looted = true; + loot->NotifyItemRemoved(l); + } + } + if (!item->freeforall) + item->is_looted = true; + --loot->unlootedCount; + m_bot->SendNewItem(newitem, uint32(item->count), false, false, true); + m_bot->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count); + } + } + } + // release loot + // if( uint64 lguid = m_bot->GetLootGUID() && m_bot->GetSession() ) + m_bot->GetSession()->DoLootRelease(m_lootCurrent); + + // clear movement target, take next target on next update + m_bot->GetMotionMaster()->Clear(); + m_bot->GetMotionMaster()->MoveIdle(); + SetQuestNeedItems(); + } + } +} + +void PlayerbotAI::AcceptQuest(Quest const *qInfo, Player *pGiver) +{ + if (!qInfo || !pGiver) + return; + + uint32 quest = qInfo->GetQuestId(); + + if (!pGiver->CanShareQuest(qInfo->GetQuestId())) + { + // giver can't share quest + m_bot->SetDivider(0); + return; + } + + if (!m_bot->CanTakeQuest(qInfo, false)) + { + // can't take quest + m_bot->SetDivider(0); + return; + } + + if (m_bot->GetDivider() != 0) + { + // send msg to quest giving player + pGiver->SendPushToPartyResponse(m_bot, QUEST_PARTY_MSG_ACCEPT_QUEST); + m_bot->SetDivider(0); + } + + if (m_bot->CanAddQuest(qInfo, false)) + { + m_bot->AddQuest(qInfo, pGiver); + + if (m_bot->CanCompleteQuest(quest)) + m_bot->CompleteQuest(quest); + + // Runsttren: did not add typeid switch from WorldSession::HandleQuestgiverAcceptQuestOpcode! + // I think it's not needed, cause typeid should be TYPEID_PLAYER - and this one is not handled + // there and there is no default case also. + + if (qInfo->GetSrcSpell() > 0) + m_bot->CastSpell(m_bot, qInfo->GetSrcSpell(), true); + } +} + +void PlayerbotAI::TurnInQuests(WorldObject *questgiver) +{ + ObjectGuid giverGUID = questgiver->GetObjectGuid(); + + if (m_bot->IsInMap(questgiver)) + { + m_bot->SetSelectionGuid(giverGUID); + + // auto complete every completed quest this NPC has + m_bot->PrepareQuestMenu(giverGUID.GetRawValue()); + QuestMenu& questMenu = m_bot->PlayerTalkClass->GetQuestMenu(); + for (uint32 iI = 0; iI < questMenu.MenuItemCount(); ++iI) + { + QuestMenuItem const& qItem = questMenu.GetItem(iI); + uint32 questID = qItem.m_qId; + Quest const* pQuest = sObjectMgr.GetQuestTemplate(questID); + + std::string questTitle = pQuest->GetTitle(); + QuestLocalization(questTitle, questID); + + QuestStatus status = m_bot->GetQuestStatus(questID); + + // if quest is complete, turn it in + if (status == QUEST_STATUS_COMPLETE) + { + // if bot hasn't already turned quest in + if (!m_bot->GetQuestRewardStatus(questID)) + { + // auto reward quest if no choice in reward + if (pQuest->GetRewChoiceItemsCount() == 0) + { + if (m_bot->CanRewardQuest(pQuest, false)) + { + m_bot->RewardQuest(pQuest, 0, questgiver, false); + } + } + + // auto reward quest if one item as reward + else if (pQuest->GetRewChoiceItemsCount() == 1) + { + int rewardIdx = 0; + ItemPrototype const *pRewardItem = sObjectMgr.GetItemPrototype(pQuest->RewChoiceItemId[rewardIdx]); + std::string itemName = pRewardItem->Name1; + ItemLocalization(itemName, pRewardItem->ItemId); + if (m_bot->CanRewardQuest(pQuest, rewardIdx, false)) + m_bot->RewardQuest(pQuest, rewardIdx, questgiver, true); + } + } + } + } + } +} + +Player* PlayerbotAI::TargetPlayerFocus() +{ + if (GetCurrentTarget() && GetCurrentTarget()->GetTypeId()==TYPEID_PLAYER) + { + return (Player*)GetCurrentTarget(); + } + + if (GetLeader()->GetPlayerbotAI() + && GetLeader()->GetPlayerbotAI()->GetCurrentTarget() + && GetLeader()->GetPlayerbotAI()->GetCurrentTarget()->GetTypeId()==TYPEID_PLAYER) + { + return (Player*)GetLeader()->GetPlayerbotAI()->GetCurrentTarget(); + } + + if (m_bot->GetGroup()) + { + GroupReference *ref = m_bot->GetGroup()->GetFirstMember(); + while (ref) + { + if (ref->getSource()->GetPlayerbotAI() + && ref->getSource()->GetPlayerbotAI()->GetCurrentTarget() + && ref->getSource()->GetPlayerbotAI()->GetCurrentTarget()->GetTypeId()==TYPEID_PLAYER) + return (Player*)ref->getSource()->GetPlayerbotAI()->GetCurrentTarget(); + ref = ref->next(); + } + } + return NULL; +} + +bool PlayerbotAI::IsInCombat() +{ + if (m_bot->duel) + return true; + + if (m_bot->GetBattleGround()) + return true; + + Player* plTarget = TargetPlayerFocus(); + if (plTarget && plTarget->isAlive() && plTarget->IsInWorld() && m_bot->IsHostileTo(plTarget) && m_bot->IsInMap(plTarget)) + { + int difflvl = ((int)plTarget->getLevel()) - ((int)m_bot->getLevel()); + if (difflvl > 4) + { + if(m_bot->GetGroup() && !plTarget->GetGroup()) + return true; + else + return false; + } + return true; + } + + if (!m_bot->getAttackers().empty() + || (m_bot->GetPet() && !m_bot->GetPet()->getAttackers().empty())) + return true; + + if (!GetLeader()->getAttackers().empty() + || (GetLeader()->GetPet() && !GetLeader()->GetPet()->getAttackers().empty())) + return true; + + if (m_bot->GetGroup()) + { + GroupReference *ref = m_bot->GetGroup()->GetFirstMember(); + while (ref) + { + if (!ref->getSource()->getAttackers().empty() + || (ref->getSource()->GetPet() && !ref->getSource()->GetPet()->getAttackers().empty())) + return true; + ref = ref->next(); + } + } + return false; +} + +uint32 PlayerbotAI::EstRepairAll() +{ + uint32 TotalCost = 0; + // equipped, backpack, bags itself + for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) + TotalCost += EstRepair(((INVENTORY_SLOT_BAG_0 << 8) | i)); + + // bank, buyback and keys not repaired + + // items in inventory bags + for (int j = INVENTORY_SLOT_BAG_START; j < INVENTORY_SLOT_BAG_END; ++j) + for (int i = 0; i < MAX_BAG_SIZE; ++i) + TotalCost += EstRepair(((j << 8) | i)); + return TotalCost; +} + +uint32 PlayerbotAI::EstRepair(uint16 pos) +{ + Item* item = m_bot->GetItemByPos(pos); + + uint32 TotalCost = 0; + if (!item) + return TotalCost; + + uint32 maxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY); + if (!maxDurability) + return TotalCost; + + uint32 curDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY); + + uint32 LostDurability = maxDurability - curDurability; + if (LostDurability > 0) + { + ItemPrototype const *ditemProto = item->GetProto(); + + DurabilityCostsEntry const *dcost = sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel); + if (!dcost) + { + sLog.outError("RepairDurability: Wrong item lvl %u", ditemProto->ItemLevel); + return TotalCost; + } + + uint32 dQualitymodEntryId = (ditemProto->Quality + 1) * 2; + DurabilityQualityEntry const *dQualitymodEntry = sDurabilityQualityStore.LookupEntry(dQualitymodEntryId); + if (!dQualitymodEntry) + { + sLog.outError("RepairDurability: Wrong dQualityModEntry %u", dQualitymodEntryId); + return TotalCost; + } + + uint32 dmultiplier = dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class, ditemProto->SubClass)]; + uint32 costs = uint32(LostDurability * dmultiplier * double(dQualitymodEntry->quality_mod)); + + if (costs == 0) //fix for ITEM_QUALITY_ARTIFACT + costs = 1; + + TotalCost = costs; + } + return TotalCost; +} + +void PlayerbotAI::SetMovementTarget(Unit *followTarget) +{ + if (!followTarget) + { + MovementClear(); + return; + } + + m_followTarget = followTarget; + + Player* target = NULL; + if (m_followTarget->GetTypeId() == TYPEID_PLAYER) + target = ((Player*)m_followTarget); + + if (target && target->IsBeingTeleported()) + return; + + if (target && target->GetCorpse()) + { + if (!FollowCheckTeleport(target->GetCorpse())) + return; + } + else if (!FollowCheckTeleport(m_followTarget)) + return; + + if (m_bot == GetLeader()) + { + if (!m_bot->GetBattleGround()) + { + if (m_bot->IsWithinDistInMap(m_followTarget, orig_x, orig_y, orig_z, 5.0f)) + { + if (FindPOI()) + { + m_ignoreAIUpdatesUntilTime = time(0) + urand(5, 30); + m_bot->GetMotionMaster()->MoveFollow(m_followTarget, 1.0f, rand_float(0, M_PI_F)); + } + else + m_bot->GetMotionMaster()->MovePoint(0, orig_x, orig_y, orig_z); + } + else if (m_bot->IsWithinDistInMap(m_followTarget, orig_x, orig_y, orig_z, 500.0f)) + { + float xcb, ycb, zcb; + float xdb, ydb, zdb; + float xt, yt, zt; + m_bot->GetMotionMaster()->GetDestination(xdb, ydb, zdb); + m_bot->GetPosition(xcb, ycb, zcb); + m_followTarget->GetPosition(xt, yt, zt); + if (!isInside(xcb, ycb, xdb, ydb, xt, yt, 10.0f)) + { + SetInFront(m_followTarget); + m_bot->GetMotionMaster()->MoveFollow(m_followTarget, 1.0f, rand_float(0, M_PI_F)); + } + } + else + { + if (FindPOI()) + m_bot->GetMotionMaster()->MoveFollow(m_followTarget, 1.0f, rand_float(0, M_PI_F)); + else + m_bot->GetMotionMaster()->MovePoint(0, orig_x, orig_y, orig_z); + } + } + else + { + if (m_bot->IsWithinDistInMap(m_followTarget, orig_x, orig_y, orig_z, 3.0f)) + { + if (SetInFront(m_followTarget)) + MovementClear(); + } + else + { + float xcb, ycb, zcb; + float xdb, ydb, zdb; + float xt, yt, zt; + m_bot->GetMotionMaster()->GetDestination(xdb, ydb, zdb); + m_bot->GetPosition(xcb, ycb, zcb); + m_followTarget->GetPosition(xt, yt, zt); + if (!isInside(xcb, ycb, xdb, ydb, xt, yt, 3.0f)) + { + m_bot->GetMotionMaster()->MoveFollow(m_followTarget, 1.0f, rand_float(0, M_PI_F)); + } + } + } + } + else + { + if (!m_bot->GetBattleGround()) + { + if (m_bot->IsWithinDistInMap(m_followTarget, 3.0f)) + { + if (SetInFront(m_followTarget)) + MovementClear(); + } + else if (m_bot->IsWithinDistInMap(m_followTarget, 100.0f)) + { + if (!m_bot->IsWithinDistInMap(m_followTarget, 15.0f)) + { + float xcb, ycb, zcb; + float xdb, ydb, zdb; + float xt, yt, zt; + m_bot->GetMotionMaster()->GetDestination(xdb, ydb, zdb); + m_bot->GetPosition(xcb, ycb, zcb); + m_followTarget->GetPosition(xt, yt, zt); + if (!isInside(xcb, ycb, xdb, ydb, xt, yt, 10.0f)) + { + float angle = rand_float(0, M_PI_F); + float dist = rand_float(1.0f, 3.0f); + m_bot->GetMotionMaster()->MoveFollow(m_followTarget, dist, angle); + } + } + } + } + else + { + if (m_bot->IsWithinDistInMap(m_followTarget, 3.0f)) + { + if (SetInFront(m_followTarget)) + MovementClear(); + } + else + { + float xcb, ycb, zcb; + float xdb, ydb, zdb; + float xt, yt, zt; + m_bot->GetMotionMaster()->GetDestination(xdb, ydb, zdb); + m_bot->GetPosition(xcb, ycb, zcb); + m_followTarget->GetPosition(xt, yt, zt); + if (!isInside(xcb, ycb, xdb, ydb, xt, yt, 3.0f)) + { + SetInFront(m_followTarget); + float angle = rand_float(0, M_PI_F); + float dist = rand_float(1.0f, 3.0f); + m_bot->GetMotionMaster()->MoveFollow(m_followTarget, dist, angle); + } + } + } + } +} + +bool PlayerbotAI::isInside(float Ax, float Ay, float Bx, float By, float Cx, float Cy, float delta) +{ + float distAC = sqrt(pow(Cx-Ax, 2)+pow(Cy-Ay, 2)); + float distABC = sqrt(pow(Bx-Ax, 2)+pow(By-Ay, 2)) + sqrt(pow(Cx-Bx, 2)+pow(Cy-By, 2)); + + if ((distAC * (1+delta/100)) < distABC) + return false; + + return true; +} + +bool PlayerbotAI::FindPOI() +{ + Unit* target = m_bot->SelectRandomFriendlyTargetBetween(0, 10.0f, 500.0f); + + if (!m_followTarget) + { + if (target) + { + m_followTarget = target; + return true; + } + else + return false; + } + else + { + if (target) + { + if (target == m_followTarget) + { + m_followTarget = target; + return false; + } + else + { + m_followTarget = target; + return true; + } + } + else + { + m_followTarget = NULL; + return false; + } + } + return false; +} + +Unit* PlayerbotAI::FindEnemy() +{ + if (m_bot->duel) + return m_bot->duel->opponent; + else if (m_bot->GetBattleGround()) + { + BattleGroundPlayerMap players = m_bot->GetBattleGround()->GetPlayers(); + + for(BattleGroundPlayerMap::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + Player *plr = ObjectAccessor::FindPlayer(itr->first); + if (!plr || !plr->IsInWorld() || !plr->isAlive()) + continue; + + if (plr->HasAura(32727) || plr->HasAura(32728) || plr->HasAura(44521)) + return NULL; + } + + for(BattleGroundPlayerMap::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + Player *plr = ObjectAccessor::FindPlayer(itr->first); + if (!plr || !plr->IsInWorld() || !plr->isAlive()) + continue; + + if (plr->GetBGTeam() == m_bot->GetBGTeam()) + continue; + + return plr; + } + } + else + { + Unit* target = m_bot->SelectRandomUnfriendlyTargetWithBgCheck(0, 500.0f); + if (target) + { + int difflvl = ((int)target->getLevel()) - ((int)m_bot->getLevel()); + if (3 >= difflvl && difflvl >= 0) + return target; + } + } + return NULL; +} + +void PlayerbotAI::MovementClear() +{ + m_bot->GetMotionMaster()->Clear(true); + m_bot->clearUnitState(UNIT_STAT_CHASE); + m_bot->clearUnitState(UNIT_STAT_FOLLOW); +} + +bool PlayerbotAI::IsMoving() +{ + return (m_bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE ? false : true); +} + +bool PlayerbotAI::SetInFront(const Unit* obj) +{ + if (m_bot->HasInArc((float)M_PI/16.0f, obj)) + return false; + + m_bot->SetInFront(obj); + float ori = m_bot->GetAngle(obj); + float x, y, z; + x = m_bot->m_movementInfo.GetPos()->x; + y = m_bot->m_movementInfo.GetPos()->y; + z = m_bot->m_movementInfo.GetPos()->z; + m_bot->m_movementInfo.ChangePosition(x,y,z,ori); + m_bot->SendHeartBeat(false); + return true; +} + +void PlayerbotAI::UpdateAI(const uint32 p_time) +{ + time_t currentTime = time(0); + if (currentTime < m_ignoreAIUpdatesUntilTime) + return; + m_ignoreAIUpdatesUntilTime = currentTime + 1; + + if (m_bot->GetTrader()) + return; + + if (!CheckTeleport()) + return; + + if (!CheckMaster()) + return; + + CheckBG(); + CheckMMaps(); + + if (!m_bot->isAlive()) + { + if (m_botState != BOTSTATE_DEAD && m_botState != BOTSTATE_DEADRELEASED) + { + m_lootCreature.clear(); + m_lootCurrent = 0; + m_bot->SetSelectionGuid(ObjectGuid()); + m_bot->GetMotionMaster()->Clear(true); + SetState(BOTSTATE_DEAD); + } + else if (m_botState == BOTSTATE_DEAD) + { + if (m_bot != GetLeader() && !GetLeader()->GetMap()->IsBattleGroundOrArena()) + { + if (m_bot->GetCorpse()) + { + SetState(BOTSTATE_DEADRELEASED); + return; + } + m_bot->SetBotDeathTimer(); + m_bot->BuildPlayerRepop(); + SetState(BOTSTATE_DEADRELEASED); + + WorldLocation loc; + Corpse *corpse = m_bot->GetCorpse(); + corpse->GetPosition(loc); + m_bot->TeleportTo(loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z, m_bot->GetOrientation()); + return; + } + else + { + if (m_bot->GetMap()->IsBattleArena()) + return; + + if (m_bot->GetCorpse()) + { + SetState(BOTSTATE_DEADRELEASED); + return; + } + m_bot->SetBotDeathTimer(); + m_bot->BuildPlayerRepop(); + SetState(BOTSTATE_DEADRELEASED); + + if (m_bot->GetMap()->IsBattleGround()) + return; + + WorldLocation loc; + Corpse *corpse = m_bot->GetCorpse(); + corpse->GetPosition(loc); + m_bot->TeleportTo(loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z, m_bot->GetOrientation()); + } + } + else if (m_botState == BOTSTATE_DEADRELEASED) + { + Corpse *corpse = m_bot->GetCorpse(); + if (!corpse) + return; + + FollowCheckTeleport(corpse); + + if (corpse->GetGhostTime() + m_bot->GetCorpseReclaimDelay(corpse->GetType() == CORPSE_RESURRECTABLE_PVP) > time(0)) + { + m_ignoreAIUpdatesUntilTime = corpse->GetGhostTime() + m_bot->GetCorpseReclaimDelay(corpse->GetType() == CORPSE_RESURRECTABLE_PVP); + return; + } + + m_ignoreAIUpdatesUntilTime = time(0) + 6; + PlayerbotChatHandler ch(GetLeader()); + + m_bot->ResurrectPlayer(0.5f); + m_bot->SpawnCorpseBones(); + + if (m_bot->getAttackers().empty()) + { + uint32 lvl = m_bot->getLevel() +1; + if (lvl < 81 && m_bot == GetLeader()) + { + m_bot->GiveLevel(lvl); + GetClassAI()->InitSpells(m_bot->GetPlayerbotAI()); + } + m_bot->GMStartup(); + m_bot->SetHealth(m_bot->GetMaxHealth()); + m_bot->SetPower(m_bot->getPowerType(), m_bot->GetMaxPower(m_bot->getPowerType())); + } + + SetState(BOTSTATE_NORMAL); + } + } + else + { + Spell* const pSpell = GetCurrentSpell(); + if (pSpell && !(pSpell->IsChannelActive() || pSpell->IsAutoRepeat())) + InterruptCurrentCastingSpell(); + + else if (!IsInCombat() && m_bot->IsInWorld()) + { + CheckRoles(); + if(CheckLevel()) + CheckStuff(); + } + + if (IsInCombat() || m_targetCombat) + { + if (!pSpell || !pSpell->IsChannelActive()) + DoNextCombatManeuver(); + else + SetIgnoreUpdateTime(1); + } + + else if (m_botState == BOTSTATE_COMBAT) + { + SetState(BOTSTATE_LOOTING); + SetIgnoreUpdateTime(); + } + + else if (m_botState == BOTSTATE_LOOTING) + { + DoLoot(); + SetIgnoreUpdateTime(); + } + + else if (!IsInCombat()) + { + SetMovementTarget(GetLeader()); + GetClassAI()->DoNonCombatActions(); + CheckMount(); + } + } +} + +bool PlayerbotAI::CheckTeleport() +{ + if (GetLeader()->IsBeingTeleported()) + return false; + + if (m_bot->IsBeingTeleported()) + return false; + + if (m_bot->GetGroup()) + { + GroupReference *ref = m_bot->GetGroup()->GetFirstMember(); + while (ref) + { + if (ref->getSource()->IsBeingTeleported()) + return false; + ref = ref->next(); + } + } + return true; +} + +bool PlayerbotAI::CheckMaster() +{ + if (m_bot != GetLeader()) + { + if (!GetLeader() || !GetLeader()->IsInWorld()) + { + SetLeader(m_bot); + ReinitAI(); + return false; + } + + if (!m_bot->GetGroup()) + { + SetLeader(m_bot); + ReinitAI(); + return false; + } + + if (!GetLeader()->GetGroup() && !GetLeader()->GetOriginalGroup()) + { + SetLeader(m_bot); + ReinitAI(); + return false; + } + } + return true; +} + +void PlayerbotAI::CheckMMaps() +{ + Player* leader = GetLeader(); + if (leader->GetTransport() || (m_bot->GetBattleGround() && m_bot->GetBattleGround()->isArena())) + { + if(!m_bot->hasUnitState(UNIT_STAT_IGNORE_PATHFINDING)) + m_bot->addUnitState(UNIT_STAT_IGNORE_PATHFINDING); + } + else + { + if(m_bot->hasUnitState(UNIT_STAT_IGNORE_PATHFINDING)) + m_bot->clearUnitState(UNIT_STAT_IGNORE_PATHFINDING); + } +} + +void PlayerbotAI::CheckRoles() +{ + switch (m_bot->getClass()) + { + case CLASS_WARRIOR: + if (m_role != m_new_role) + { + if (m_new_role == 1) + { + switch (urand(0, 2)) + { + case 0: m_new_role = WarriorArms; break; + case 1: m_new_role = WarriorProtection; break; + default: m_new_role = WarriorFury; break; + } + } + m_role = m_new_role; + delete m_classAI; + m_classAI = (PlayerbotClassAI*) new PlayerbotWarriorAI(m_bot, this); + } + break; + case CLASS_PALADIN: + if (m_role != m_new_role) + { + if (m_new_role == 1) + { + switch (urand(0, 2)) + { + case 0: m_new_role = PaladinCombat; break; + case 1: m_new_role = PaladinHoly; break; + default: m_new_role = PaladinProtection; break; + } + } + m_role = m_new_role; + delete m_classAI; + m_classAI = (PlayerbotClassAI*) new PlayerbotPaladinAI(m_bot, this); + } + break; + case CLASS_HUNTER: + if (m_role != m_new_role) + { + if (m_new_role == 1) + { + switch (urand(0, 2)) + { + case 0: m_new_role = HunterBeastMastery; break; + case 1: m_new_role = HunterSurvival; break; + default: m_new_role = HunterMarksmanship; break; + } + } + m_role = m_new_role; + delete m_classAI; + m_classAI = (PlayerbotClassAI*) new PlayerbotHunterAI(m_bot, this); + } + break; + case CLASS_ROGUE: + if (m_role != m_new_role) + { + if (m_new_role == 1) + { + switch (urand(0, 2)) + { + case 0: m_new_role = RogueCombat; break; + case 1: m_new_role = RogueAssassination; break; + default: m_new_role = RogueSubtlety; break; + } + } + m_role = m_new_role; + delete m_classAI; + m_classAI = (PlayerbotClassAI*) new PlayerbotRogueAI(m_bot, this); + } + break; + case CLASS_PRIEST: + if (m_role != m_new_role) + { + if (m_new_role == 1) + { + switch (urand(0, 2)) + { + case 0: m_new_role = PriestDiscipline; break; + case 1: m_new_role = PriestHoly; break; + default: m_new_role = PriestShadow; break; + } + } + m_role = m_new_role; + delete m_classAI; + m_classAI = (PlayerbotClassAI*) new PlayerbotPriestAI(m_bot, this); + } + break; + case CLASS_DEATH_KNIGHT: + if (m_role != m_new_role) + { + if (m_new_role == 1) + { + switch (urand(0, 2)) + { + case 0: m_new_role = DeathKnightBlood; break; + case 1: m_new_role = DeathKnightFrost; break; + default: m_new_role = DeathKnightUnholy; break; + } + } + m_role = m_new_role; + delete m_classAI; + m_classAI = (PlayerbotClassAI*) new PlayerbotDeathKnightAI(m_bot, this); + } + break; + case CLASS_SHAMAN: + if (m_role != m_new_role) + { + if (m_new_role == 1) + { + switch (urand(0, 2)) + { + case 0: m_new_role = ShamanElementalCombat; break; + case 1: m_new_role = ShamanRestoration; break; + default: m_new_role = ShamanEnhancement; break; + } + } + m_role = m_new_role; + delete m_classAI; + m_classAI = (PlayerbotClassAI*) new PlayerbotShamanAI(m_bot, this); + } + break; + case CLASS_MAGE: + if (m_role != m_new_role) + { + if (m_new_role == 1) + { + switch (urand(0, 2)) + { + case 0: m_new_role = MageFire; break; + case 1: m_new_role = MageArcane; break; + default: m_new_role = MageFrost; break; + } + } + if (m_new_role!=MageFire && m_bot->getLevel() < 10) + { + m_role = MageFire; + m_new_role = MageFire; + } + else if((m_new_role==MageArcane) && m_bot->getLevel() < 60) + { + if (urand(0,1)==0) + { + m_role = MageFire; + m_new_role = MageFire; + } + else + { + m_role = MageFrost; + m_new_role = MageFrost; + } + } + else + { + m_role = m_new_role; + } + delete m_classAI; + m_classAI = (PlayerbotClassAI*) new PlayerbotMageAI(m_bot, this); + } + break; + case CLASS_WARLOCK: + if (m_role != m_new_role) + { + if (m_new_role == 1) + { + switch (urand(0, 2)) + { + case 0: m_new_role = WarlockDestruction; break; + case 1: m_new_role = WarlockCurses; break; + default: m_new_role = WarlockSummoning; break; + } + } + m_role = m_new_role; + delete m_classAI; + m_classAI = (PlayerbotClassAI*) new PlayerbotWarlockAI(m_bot, this); + } + break; + case CLASS_DRUID: + if (m_role != m_new_role) + { + if (m_new_role == 1) + { + switch (urand(0, 2)) + { + case 0: m_new_role = DruidFeralCombat; break; + case 1: m_new_role = DruidRestoration; break; + default: m_new_role = DruidBalance; break; + } + } + m_role = m_new_role; + delete m_classAI; + m_classAI = (PlayerbotClassAI*) new PlayerbotDruidAI(m_bot, this); + } + break; + } +} + +bool PlayerbotAI::CheckLevel() +{ + if (GetLeader()->getLevel() == m_bot->getLevel()) + return false; + + m_bot->GiveLevel(GetLeader()->getLevel()); + return true; +} + +void PlayerbotAI::CheckStuff() +{ + m_bot->GMStartup(); + m_bot->SetHealth(m_bot->GetMaxHealth()); + m_bot->SetPower(m_bot->getPowerType(), m_bot->GetMaxPower(m_bot->getPowerType())); + GetClassAI()->InitSpells(m_bot->GetPlayerbotAI()); +} + +Spell* PlayerbotAI::GetCurrentSpell() const +{ + if (m_CurrentlyCastingSpellId == 0) + return NULL; + + Spell* const pSpell = m_bot->FindCurrentSpellBySpellId(m_CurrentlyCastingSpellId); + return pSpell; +} + +bool PlayerbotAI::canObeyCommandFrom(const Player& player) const +{ + return player.GetSession()->GetAccountId() == GetLeader()->GetSession()->GetAccountId(); +} + +bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target) +{ + if (spellId <= 0) + return false; + + ObjectGuid oldSel = m_bot->GetSelectionGuid(); + m_bot->SetSelectionGuid(target->GetObjectGuid()); + bool rv = CastSpell(spellId); + m_bot->SetSelectionGuid(oldSel); + return rv; +} + +bool PlayerbotAI::HasAura(uint32 spellId, const Unit* unit) const +{ + if (spellId <= 0) + return false; + + for (Unit::SpellAuraHolderMap::const_iterator iter = unit->GetSpellAuraHolderMap().begin(); iter != unit->GetSpellAuraHolderMap().end(); ++iter) + { + if (iter->second->GetId() == spellId) + return true; + } + return false; +} + +bool PlayerbotAI::CastAura(uint32 spellId, Unit* target) +{ + if (HasAura(spellId, target)) + return true; + + return CastSpell(spellId, target); +} + +bool PlayerbotAI::CastSpell(uint32 spellId) +{ + // some AIs don't check if the bot doesn't have spell before using it + // so just return false when this happens + if (spellId <= 0) + return false; + + // check spell cooldown + if (m_bot->HasSpellCooldown(spellId)) + return false; + + // see Creature.cpp 1738 for reference + // don't allow bot to cast damage spells on friends + const SpellEntry* const pSpellInfo = sSpellStore.LookupEntry(spellId); + if (!pSpellInfo) + return false; + + // set target + ObjectGuid targetGUID = m_bot->GetSelectionGuid(); + Unit* pTarget = ObjectAccessor::GetUnit(*m_bot, targetGUID); + + if (!pTarget) + pTarget = m_bot; + + // Check spell range + SpellRanges::iterator it = m_spellRangeMap.find(spellId); + if (it != m_spellRangeMap.end() && (int)it->second != 0) + { + float dist = m_bot->GetCombatDistance(pTarget); + if (dist > it->second + 1.25) // See Spell::CheckRange for modifier value + return false; + } + + // Check line of sight + if (!m_bot->IsWithinLOSInMap(pTarget)) + return false; + + if (IsPositiveSpell(spellId)) + { + if (pTarget && !m_bot->IsFriendlyTo(pTarget)) + pTarget = m_bot; + } + else + { + if (pTarget && m_bot->IsFriendlyTo(pTarget)) + return false; + + SetInFront(pTarget); + } + + // stop movement to prevent cancel spell casting + SpellCastTimesEntry const * castTimeEntry = sSpellCastTimesStore.LookupEntry(pSpellInfo->CastingTimeIndex); + if (castTimeEntry && castTimeEntry->CastTime) + MovementClear(); + + // actually cast spell + m_bot->CastSpell(pTarget, pSpellInfo, false); + + Spell* const pSpell = m_bot->FindCurrentSpellBySpellId(spellId); + if (!pSpell) + return false; + + m_CurrentlyCastingSpellId = spellId; + m_ignoreAIUpdatesUntilTime = time(0) + (int32) ((float) pSpell->GetCastTime() / 1000.0f) + 1; + + return true; +} + +bool PlayerbotAI::CastPetSpell(uint32 spellId, Unit* target) +{ + if (spellId == 0) + return false; + + Pet* pet = m_bot->GetPet(); + if (!pet) + return false; + + if (pet->HasSpellCooldown(spellId)) + return false; + + const SpellEntry* const pSpellInfo = sSpellStore.LookupEntry(spellId); + if (!pSpellInfo) + return false; + + // set target + Unit* pTarget; + if (!target) + { + ObjectGuid targetGUID = m_bot->GetSelectionGuid(); + pTarget = ObjectAccessor::GetUnit(*m_bot, targetGUID); + } + else + pTarget = target; + + if (IsPositiveSpell(spellId)) + { + if (pTarget && !m_bot->IsFriendlyTo(pTarget)) + pTarget = m_bot; + } + else + { + if (pTarget && m_bot->IsFriendlyTo(pTarget)) + return false; + + if (!pet->isInFrontInMap(pTarget, 10)) // distance probably should be calculated + pet->SetInFront(pTarget); + } + + pet->CastSpell(pTarget, pSpellInfo, false); + + Spell* const pSpell = pet->FindCurrentSpellBySpellId(spellId); + if (!pSpell) + return false; + + return true; +} + +// Perform sanity checks and cast spell +bool PlayerbotAI::Buff(uint32 spellId, Unit* target, void (*beforeCast)(Player *)) +{ + if (spellId == 0) + return false; + + SpellEntry const * spellProto = sSpellStore.LookupEntry(spellId); + + if (!spellProto) + return false; + + if (!target) + return false; + + // Select appropriate spell rank for target's level + spellProto = sSpellMgr.SelectAuraRankForLevel(spellProto, target->getLevel()); + if (!spellProto) + return false; + + // Check if spell will boost one of already existent auras + bool willBenefitFromSpell = false; + for (uint8 i = 0; i < MAX_EFFECT_INDEX; ++i) + { + if (spellProto->EffectApplyAuraName[i] == SPELL_AURA_NONE) + break; + + bool sameOrBetterAuraFound = false; + int32 bonus = m_bot->CalculateSpellDamage(target, spellProto, SpellEffectIndex(i)); + Unit::AuraList const& auras = target->GetAurasByType(AuraType(spellProto->EffectApplyAuraName[i])); + for (Unit::AuraList::const_iterator it = auras.begin(); it != auras.end(); ++it) + if ((*it)->GetModifier()->m_miscvalue == spellProto->EffectMiscValue[i] && (*it)->GetModifier()->m_amount >= bonus) + { + sameOrBetterAuraFound = true; + break; + } + willBenefitFromSpell = willBenefitFromSpell || !sameOrBetterAuraFound; + } + + if (!willBenefitFromSpell) + return false; + + // Druids may need to shapeshift before casting + if (beforeCast) + (*beforeCast)(m_bot); + + return CastSpell(spellProto->Id, target); +} + +// Can be used for personal buffs like Mage Armor and Inner Fire +bool PlayerbotAI::SelfBuff(uint32 spellId) +{ + if (spellId == 0) + return false; + + if (m_bot->HasAura(spellId)) + return false; + + return CastSpell(spellId, m_bot); +} + +// Checks if spell is single per target per caster and will make any effect on target +bool PlayerbotAI::CanReceiveSpecificSpell(uint8 spec, Unit* target) const +{ + if (IsSingleFromSpellSpecificPerTargetPerCaster(SpellSpecific(spec), SpellSpecific(spec))) + { + Unit::SpellAuraHolderMap holders = target->GetSpellAuraHolderMap(); + Unit::SpellAuraHolderMap::iterator it; + for (it = holders.begin(); it != holders.end(); ++it) + if ((*it).second->GetCasterGUID() == m_bot->GetGUID() && GetSpellSpecific((*it).second->GetId()) == SpellSpecific(spec)) + return false; + } + return true; +} + +Item* PlayerbotAI::FindItem(uint32 ItemId) +{ + // list out items in main backpack + //INVENTORY_SLOT_ITEM_START = 23 + //INVENTORY_SLOT_ITEM_END = 39 + + for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) + { + // sLog.outDebug("[%s's]backpack slot = %u",m_bot->GetName(),slot); // 23 to 38 = 16 + Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); // 255, 23 to 38 + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + if (!pItemProto) + continue; + + if (pItemProto->ItemId == ItemId) // have required item + return pItem; + } + } + // list out items in other removable backpacks + //INVENTORY_SLOT_BAG_START = 19 + //INVENTORY_SLOT_BAG_END = 23 + + for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) // 20 to 23 = 4 + { + const Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); // 255, 20 to 23 + if (pBag) + for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot) + { + sLog.outDebug("[%s's]bag[%u] slot = %u", m_bot->GetName(), bag, slot); // 1 to bagsize = ? + Item* const pItem = m_bot->GetItemByPos(bag, slot); // 20 to 23, 1 to bagsize + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + if (!pItemProto) + continue; + + if (pItemProto->ItemId == ItemId) // have required item + return pItem; + } + } + } + return NULL; +} + +bool PlayerbotAI::HasPick() +{ + if (m_bot->HasItemTotemCategory(TC_MINING_PICK)) + return true; + + return false; +} + +bool PlayerbotAI::HasSpellReagents(uint32 spellId) +{ + const SpellEntry* const pSpellInfo = sSpellStore.LookupEntry(spellId); + if (!pSpellInfo) + return false; + + if (m_bot->CanNoReagentCast(pSpellInfo)) + return true; + + for (uint32 i = 0; i < MAX_SPELL_REAGENTS; ++i) + { + if(pSpellInfo->Reagent[i] <= 0) + continue; + + uint32 itemid = pSpellInfo->Reagent[i]; + uint32 count = pSpellInfo->ReagentCount[i]; + + if (!m_bot->HasItemCount(itemid, count)) + return false; + } + + return true; +} + +// extracts all item ids in format below +// I decided to roll my own extractor rather then use the one in ChatHandler +// because this one works on a const string, and it handles multiple links +// |color|linkType:key:something1:...:somethingN|h[name]|h|r +void PlayerbotAI::extractItemIds(const std::string& text, std::list& itemIds) const +{ + uint8 pos = 0; + while (true) + { + int i = text.find("Hitem:", pos); + if (i == -1) + break; + pos = i + 6; + int endPos = text.find(':', pos); + if (endPos == -1) + break; + std::string idC = text.substr(pos, endPos - pos); + uint32 id = atol(idC.c_str()); + pos = endPos; + if (id) + itemIds.push_back(id); + } +} + +bool PlayerbotAI::extractSpellId(const std::string& text, uint32 &spellId) const +{ + + // Link format + // |cffffffff|Hspell:" << spellId << ":" << "|h[" << pSpellInfo->SpellName[loc] << "]|h|r"; + // cast |cff71d5ff|Hspell:686|h[Shadow Bolt]|h|r"; + // 012345678901234567890123456 + // base = 16 >| +7 >| + + uint8 pos = 0; + + int i = text.find("Hspell:", pos); + if (i == -1) + return false; + + // DEBUG_LOG("extractSpellId first pos %u i %u",pos,i); + pos = i + 7; // start of window in text 16 + 7 = 23 + int endPos = text.find('|', pos); + if (endPos == -1) + return false; + + // DEBUG_LOG("extractSpellId second endpos : %u pos : %u",endPos,pos); + std::string idC = text.substr(pos, endPos - pos); // 26 - 23 + spellId = atol(idC.c_str()); + pos = endPos; // end + return true; +} + +bool PlayerbotAI::extractGOinfo(const std::string& text, uint32 &guid, uint32 &entry, int &mapid, float &x, float &y, float &z) const +{ + + // Link format + // |cFFFFFF00|Hfound:" << guid << ':' << entry << ':' << x << ':' << y << ':' << z << ':' << mapid << ':' << "|h[" << gInfo->name << "]|h|r"; + // |cFFFFFF00|Hfound:5093:1731:-9295:-270:81.874:0:|h[Copper Vein]|h|r + + uint8 pos = 0; + + // extract GO guid + int i = text.find("Hfound:", pos); // base H = 11 + if (i == -1) // break if error + return false; + + pos = i + 7; //start of window in text 11 + 7 = 18 + int endPos = text.find(':', pos); // end of window in text 22 + if (endPos == -1) //break if error + return false; + std::string guidC = text.substr(pos, endPos - pos); // get string within window i.e guid 22 - 18 = 4 + guid = atol(guidC.c_str()); // convert ascii to long int + + // extract GO entry + pos = endPos + 1; + endPos = text.find(':', pos); // end of window in text + if (endPos == -1) //break if error + return false; + + std::string entryC = text.substr(pos, endPos - pos); // get string within window i.e entry + entry = atol(entryC.c_str()); // convert ascii to float + + // extract GO x + pos = endPos + 1; + endPos = text.find(':', pos); // end of window in text + if (endPos == -1) //break if error + return false; + + std::string xC = text.substr(pos, endPos - pos); // get string within window i.e x + x = atof(xC.c_str()); // convert ascii to float + + // extract GO y + pos = endPos + 1; + endPos = text.find(':', pos); // end of window in text + if (endPos == -1) //break if error + return false; + + std::string yC = text.substr(pos, endPos - pos); // get string within window i.e y + y = atof(yC.c_str()); // convert ascii to float + + // extract GO z + pos = endPos + 1; + endPos = text.find(':', pos); // end of window in text + if (endPos == -1) //break if error + return false; + + std::string zC = text.substr(pos, endPos - pos); // get string within window i.e z + z = atof(zC.c_str()); // convert ascii to float + + //extract GO mapid + pos = endPos + 1; + endPos = text.find(':', pos); // end of window in text + if (endPos == -1) //break if error + return false; + + std::string mapidC = text.substr(pos, endPos - pos); // get string within window i.e mapid + mapid = atoi(mapidC.c_str()); // convert ascii to int + pos = endPos; // end + return true; +} + +// extracts currency in #g#s#c format +uint32 PlayerbotAI::extractMoney(const std::string& text) const +{ + // if user specified money in ##g##s##c format + std::string acum = ""; + uint32 copper = 0; + for (uint8 i = 0; i < text.length(); i++) + { + if (text[i] == 'g') + { + copper += (atol(acum.c_str()) * 100 * 100); + acum = ""; + } + else if (text[i] == 'c') + { + copper += atol(acum.c_str()); + acum = ""; + } + else if (text[i] == 's') + { + copper += (atol(acum.c_str()) * 100); + acum = ""; + } + else if (text[i] == ' ') + break; + else if (text[i] >= 48 && text[i] <= 57) + acum += text[i]; + else + { + copper = 0; + break; + } + } + return copper; +} + +// finds items in equipment and adds Item* to foundItemList +// also removes found item IDs from itemIdSearchList when found +void PlayerbotAI::findItemsInEquip(std::list& itemIdSearchList, std::list& foundItemList) const +{ + for (uint8 slot = EQUIPMENT_SLOT_START; itemIdSearchList.size() > 0 && slot < EQUIPMENT_SLOT_END; slot++) + { + Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + if (!pItem) + continue; + + for (std::list::iterator it = itemIdSearchList.begin(); it != itemIdSearchList.end(); ++it) + { + if (pItem->GetProto()->ItemId != *it) + continue; + + foundItemList.push_back(pItem); + itemIdSearchList.erase(it); + break; + } + } +} + +// finds items in inventory and adds Item* to foundItemList +// also removes found item IDs from itemIdSearchList when found +void PlayerbotAI::findItemsInInv(std::list& itemIdSearchList, std::list& foundItemList) const +{ + + // look for items in main bag + for (uint8 slot = INVENTORY_SLOT_ITEM_START; itemIdSearchList.size() > 0 && slot < INVENTORY_SLOT_ITEM_END; ++slot) + { + Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + if (!pItem) + continue; + + for (std::list::iterator it = itemIdSearchList.begin(); it != itemIdSearchList.end(); ++it) + { + if (pItem->GetProto()->ItemId != *it) + continue; + + foundItemList.push_back(pItem); + itemIdSearchList.erase(it); + break; + } + } + + // for all for items in other bags + for (uint8 bag = INVENTORY_SLOT_BAG_START; itemIdSearchList.size() > 0 && bag < INVENTORY_SLOT_BAG_END; ++bag) + { + Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); + if (!pBag) + continue; + + for (uint8 slot = 0; itemIdSearchList.size() > 0 && slot < pBag->GetBagSize(); ++slot) + { + Item* const pItem = m_bot->GetItemByPos(bag, slot); + if (!pItem) + continue; + + for (std::list::iterator it = itemIdSearchList.begin(); it != itemIdSearchList.end(); ++it) + { + if (pItem->GetProto()->ItemId != *it) + continue; + + foundItemList.push_back(pItem); + itemIdSearchList.erase(it); + break; + } + } + } +} + +// use item on self +void PlayerbotAI::UseItem(Item *item) +{ + UseItem(item, TARGET_FLAG_SELF, ObjectGuid()); +} + +// use item on equipped item +void PlayerbotAI::UseItem(Item *item, uint8 targetInventorySlot) +{ + if (targetInventorySlot >= EQUIPMENT_SLOT_END) + return; + + Item* const targetItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, targetInventorySlot); + if (!targetItem) + return; + + UseItem(item, TARGET_FLAG_ITEM, targetItem->GetObjectGuid()); +} + +// use item on unit +void PlayerbotAI::UseItem(Item *item, Unit *target) +{ + if (!target) + return; + + UseItem(item, TARGET_FLAG_UNIT, target->GetObjectGuid()); +} + +// generic item use method +void PlayerbotAI::UseItem(Item *item, uint32 targetFlag, ObjectGuid targetGUID) +{ + if (!item) + return; + + uint8 bagIndex = item->GetBagSlot(); + uint8 slot = item->GetSlot(); + uint8 cast_count = 1; + ObjectGuid item_guid = item->GetObjectGuid(); + uint32 glyphIndex = 0; + uint8 unk_flags = 0; + + uint32 spellId = 0; + for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + if (item->GetProto()->Spells[i].SpellId > 0) + { + spellId = item->GetProto()->Spells[i].SpellId; + break; + } + } + + WorldPacket *packet = new WorldPacket(CMSG_USE_ITEM, 28); + *packet << bagIndex << slot << cast_count << spellId << item_guid + << glyphIndex << unk_flags << targetFlag; + + if (targetFlag & (TARGET_FLAG_UNIT | TARGET_FLAG_ITEM)) + *packet << targetGUID.WriteAsPacked(); + + m_bot->GetSession()->QueuePacket(packet); + + SpellEntry const * spellInfo = sSpellStore.LookupEntry(spellId); + if (!spellInfo) + return; + + SpellCastTimesEntry const * castingTimeEntry = sSpellCastTimesStore.LookupEntry(spellInfo->CastingTimeIndex); + if (!castingTimeEntry) + return; + + uint8 duration, castTime; + castTime = (uint8)((float)castingTimeEntry->CastTime / 1000.0f); + + if (item->GetProto()->Class == ITEM_CLASS_CONSUMABLE && item->GetProto()->SubClass == ITEM_SUBCLASS_FOOD) + { + duration = (uint8)((float)GetSpellDuration(spellInfo) / 1000.0f); + SetIgnoreUpdateTime(castTime + duration); + } + else + SetIgnoreUpdateTime(castTime); +} + +// submits packet to use an item +void PlayerbotAI::EquipItem(Item& item) +{ + uint8 bagIndex = item.GetBagSlot(); + uint8 slot = item.GetSlot(); + + WorldPacket* const packet = new WorldPacket(CMSG_AUTOEQUIP_ITEM, 2); + *packet << bagIndex << slot; + m_bot->GetSession()->QueuePacket(packet); +} + +bool PlayerbotAI::FollowCheckTeleport(WorldObject *obj) +{ + if (m_bot->GetMap() && m_bot->GetMap()->IsBattleGroundOrArena()) + return true; + + if (!m_bot->IsWithinDistInMap(obj, 100, true) && GetLeader()->isAlive() && !GetLeader()->IsTaxiFlying()) + { + m_targetCombat = NULL; + m_followTarget = NULL; + + if (m_bot == GetLeader()) + return false; + + if (m_bot->IsBeingTeleported()) + return false; + + Map* pMap = GetLeader()->GetMap(); + + if (pMap->IsBattleGroundOrArena()) + { + if (m_bot->GetBattleGroundId() && GetLeader()->GetBattleGroundId() != m_bot->GetBattleGroundId()) + return false; + + m_bot->SetBattleGroundId(GetLeader()->GetBattleGroundId(), GetLeader()->GetBattleGroundTypeId()); + if (!m_bot->GetMap()->IsBattleGroundOrArena()) + m_bot->SetBattleGroundEntryPoint(); + } + else if (pMap->IsDungeon()) + { + Map* cMap = m_bot->GetMap(); + if (cMap->Instanceable() && cMap->GetInstanceId() != pMap->GetInstanceId()) + return false; + + if (!GetLeader()->GetGroup() || !m_bot->GetGroup() || + (m_bot->GetGroup()->GetLeaderGuid() != GetLeader()->GetObjectGuid()) || + (GetLeader()->GetGroup()->GetLeaderGuid() != GetLeader()->GetObjectGuid())) + return false; + } + + if (m_bot->IsTaxiFlying()) + { + m_bot->GetMotionMaster()->MovementExpired(); + m_bot->m_taxi.ClearTaxiDestinations(); + } + else + m_bot->SaveRecallPosition(); + + float x,y,z; + GetLeader()->GetClosePoint(x, y, z, m_bot->GetObjectBoundingRadius()); + m_bot->TeleportTo(GetLeader()->GetMapId(),x,y,z,m_bot->GetOrientation()); + } + return true; +} + +void PlayerbotAI::HandleTeleportAck() +{ + m_bot->GetMotionMaster()->Clear(true); + if (m_bot->IsBeingTeleportedNear()) + { + WorldPacket p = WorldPacket(MSG_MOVE_TELEPORT_ACK, 8 + 4 + 4); + p.appendPackGUID(m_bot->GetGUID()); + p << (uint32) 0; // supposed to be flags? not used currently + p << (uint32) time(0); // time - not currently used + m_bot->GetSession()->HandleMoveTeleportAckOpcode(p); + } + else if (m_bot->IsBeingTeleportedFar()) + m_bot->GetSession()->HandleMoveWorldportAckOpcode(); +} + +// Localization support +void PlayerbotAI::ItemLocalization(std::string& itemName, const uint32 itemID) const +{ + uint32 loc = GetLeader()->GetSession()->GetSessionDbLocaleIndex(); + std::wstring wnamepart; + + ItemLocale const *pItemInfo = sObjectMgr.GetItemLocale(itemID); + if (pItemInfo) + if (pItemInfo->Name.size() > loc && !pItemInfo->Name[loc].empty()) + { + const std::string name = pItemInfo->Name[loc]; + if (Utf8FitTo(name, wnamepart)) + itemName = name.c_str(); + } +} + +void PlayerbotAI::QuestLocalization(std::string& questTitle, const uint32 questID) const +{ + uint32 loc = GetLeader()->GetSession()->GetSessionDbLocaleIndex(); + std::wstring wnamepart; + + QuestLocale const *pQuestInfo = sObjectMgr.GetQuestLocale(questID); + if (pQuestInfo) + if (pQuestInfo->Title.size() > loc && !pQuestInfo->Title[loc].empty()) + { + const std::string title = pQuestInfo->Title[loc]; + if (Utf8FitTo(title, wnamepart)) + questTitle = title.c_str(); + } +} diff --git a/src/game/playerbot/PlayerbotAI.h b/src/game/playerbot/PlayerbotAI.h new file mode 100644 index 000000000..bdddf674a --- /dev/null +++ b/src/game/playerbot/PlayerbotAI.h @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PLAYERBOTAI_H +#define _PLAYERBOTAI_H + +#include "Common.h" +#include "../QuestDef.h" +#include "../GameEventMgr.h" +#include "../ObjectGuid.h" + +class WorldPacket; +class WorldObject; +class Player; +class Unit; +class Object; +class Item; +class PlayerbotClassAI; +class PlayerbotMgr; + +#define BOTLOOT_DISTANCE 25.0f + +enum RacialTraits +{ + ARCANE_TORRENT_MANA_CLASSES = 28730, + ARCANE_TORRENT_DEATH_KNIGHT = 50613, + ARCANE_TORRENT_ROGUE = 25046, + BERSERKING_ALL = 26297, + BLOOD_FURY_MELEE_CLASSES = 20572, + BLOOD_FURY_WARLOCK = 33702, + BLOOD_FURY_SHAMAN = 33697, + ESCAPE_ARTIST_ALL = 20589, + EVERY_MAN_FOR_HIMSELF_ALL = 59752, + GIFT_OF_THE_NAARU_DEATH_KNIGHT = 59545, + GIFT_OF_THE_NAARU_HUNTER = 59543, + GIFT_OF_THE_NAARU_MAGE = 59548, + GIFT_OF_THE_NAARU_PALADIN = 59542, + GIFT_OF_THE_NAARU_PRIEST = 59544, + GIFT_OF_THE_NAARU_SHAMAN = 59547, + GIFT_OF_THE_NAARU_WARRIOR = 28880, + SHADOWMELD_ALL = 58984, + STONEFORM_ALL = 20594, + WAR_STOMP_ALL = 20549, + WILL_OF_THE_FORSAKEN_ALL = 7744 +}; + +class MANGOS_DLL_SPEC PlayerbotAI +{ +public: + enum BotState + { + BOTSTATE_NORMAL, // normal AI routines are processed + BOTSTATE_COMBAT, // bot is in combat + BOTSTATE_DEAD, // we are dead and wait for becoming ghost + BOTSTATE_DEADRELEASED, // we released as ghost and wait to revive + BOTSTATE_LOOTING // looting mode, used just after combat + }; + + typedef std::map BotNeedItem; + typedef std::list BotLootCreature; + typedef std::map SpellRanges; + +public: + PlayerbotAI(PlayerbotMgr * const mgr, Player * const bot); + virtual ~PlayerbotAI(); + void ReinitAI(); + void InitSpells(PlayerbotAI* const ai); + + // This is called from Unit.cpp and is called every second (I think) + void UpdateAI(const uint32 p_time); + + // This is called by WorldSession.cpp + // It provides a view of packets normally sent to the client. + // Since there is no client at the other end, the packets are dropped of course. + // For a list of opcodes that can be caught see Opcodes.cpp (SMSG_* opcodes only) + void HandleBotOutgoingPacket(const WorldPacket& packet); + + // This is called by WorldSession.cpp + // when it detects that a bot is being teleported. It acknowledges to the server to complete the + // teleportation + void HandleTeleportAck(); + + PlayerbotClassAI* GetClassAI() { return m_classAI; } + PlayerbotMgr* const GetManager() { return m_mgr; } + + // finds spell ID for matching substring args + // in priority of full text match, spells not taking reagents, and highest rank + uint32 getSpellId(const char* args, bool master = false) const; + uint32 getPetSpellId(const char* args) const; + // Initialize spell using rank 1 spell id + uint32 initSpell(uint32 spellId); + uint32 initPetSpell(uint32 spellIconId); + + // extracts item ids from links + void extractItemIds(const std::string& text, std::list& itemIds) const; + + // extract spellid from links + bool extractSpellId(const std::string& text, uint32 &spellId) const; + + // extracts currency from a string as #g#s#c and returns the total in copper + uint32 extractMoney(const std::string& text) const; + + // extracts gameobject info from link + bool extractGOinfo(const std::string& text, uint32 &guid, uint32 &entry, int &mapid, float &x, float &y, float &z) const; + + // finds items in bots equipment and adds them to foundItemList, removes found items from itemIdSearchList + void findItemsInEquip(std::list& itemIdSearchList, std::list& foundItemList) const; + // finds items in bots inventory and adds them to foundItemList, removes found items from itemIdSearchList + void findItemsInInv(std::list& itemIdSearchList, std::list& foundItemList) const; + + // currently bots only obey commands from the master + bool canObeyCommandFrom(const Player& player) const; + + // get current casting spell (will return NULL if no spell!) + Spell* GetCurrentSpell() const; + + bool HasAura(uint32 spellId, const Unit* unit) const; + + bool CanReceiveSpecificSpell(uint8 spec, Unit* target) const; + + bool PickPocket(Unit* pTarget); + bool HasPick(); + bool HasSpellReagents(uint32 spellId); + + uint8 GetHealthPercent(const Unit& target) const; + uint8 GetHealthPercent() const; + uint8 GetBaseManaPercent(const Unit& target) const; + uint8 GetBaseManaPercent() const; + uint8 GetManaPercent(const Unit& target) const; + uint8 GetManaPercent() const; + uint8 GetRageAmount(const Unit& target) const; + uint8 GetRageAmount() const; + uint8 GetEnergyAmount(const Unit& target) const; + uint8 GetEnergyAmount() const; + uint8 GetRunicPower(const Unit& target) const; + uint8 GetRunicPower() const; + + Item* FindFood() const; + Item* FindDrink() const; + Item* FindBandage() const; + Item* FindPoison() const; + Item* FindMount(uint32 matchingRidingSkill) const; + Item* FindItem(uint32 ItemId); + Item* FindConsumable(uint32 displayId) const; + void CheckMount(); + Player* FindNewGroupLeader(); + + // ******* Actions **************************************** + // Your handlers can call these actions to make the bot do things. + bool CastSpell(uint32 spellId); + bool CastSpell(uint32 spellId, Unit* target); + bool CastAura(uint32 spellId, Unit* target); + bool CastPetSpell(uint32 spellId, Unit* target = NULL); + bool Buff(uint32 spellId, Unit* target, void (*beforeCast)(Player *) = NULL); + bool SelfBuff(uint32 spellId); + + void UseItem(Item *item, uint32 targetFlag, ObjectGuid targetGUID); + void UseItem(Item *item, uint8 targetInventorySlot); + void UseItem(Item *item, Unit *target); + void UseItem(Item *item); + + void EquipItem(Item& item); + void Feast(); + void InterruptCurrentCastingSpell(); + bool GetCombatTarget(Unit* forcedTarged = NULL); + Unit *GetCurrentTarget() { return m_targetCombat; }; + void DoNextCombatManeuver(); + void SetIgnoreUpdateTime(uint8 t = 0) { m_ignoreAIUpdatesUntilTime = time(0) + t; }; + void SetEnterBGTime(uint8 t = 0) { m_enterBg = time(0) + t; }; + + Player *GetPlayer() const { return m_bot; } + Player *GetLeader() const; + void SetLeader(Player* pl); + + uint16 getRole() { return m_role; }; + void setRole(uint16 role) { m_new_role = role; }; + + BotState GetState() { return m_botState; }; + void SetState(BotState state); + void SetQuestNeedItems(); + void SendQuestItemList(Player& player); + bool FollowCheckTeleport(WorldObject *obj); + void DoLoot(); + + bool CheckTeleport(); + bool CheckMaster(); + void CheckMMaps(); + bool CheckLevel(); + void CheckStuff(); + void CheckRoles(); + void CheckBG(); + + uint32 EstRepairAll(); + uint32 EstRepair(uint16 pos); + + void AcceptQuest(Quest const *qInfo, Player *pGiver); + void TurnInQuests(WorldObject *questgiver); + + bool IsInCombat(); + Player* TargetPlayerFocus(); + void SetMovementTarget(Unit *followTarget = NULL); + bool isInside(float x1, float y1, float x2, float y2, float x3, float y3, float delta); + void MovementClear(); + bool IsMoving(); + bool FindPOI(); + Unit* FindEnemy(); + + bool SetInFront(const Unit* obj); + + void ItemLocalization(std::string& itemName, const uint32 itemID) const; + void QuestLocalization(std::string& questTitle, const uint32 questID) const; + + uint8 GetFreeBagSpace() const; + + Unit* GetFollowTarget() { return m_followTarget; } + +private: + // ****** Closed Actions ******************************** + // it is safe to keep these back reference pointers because m_bot + // owns the "this" object and m_master owns m_bot. The owner always cleans up. + PlayerbotMgr* const m_mgr; + Player* const m_bot; + PlayerbotClassAI* m_classAI; + uint16 m_role, m_new_role; + + // ignores AI updates until time specified + // no need to waste CPU cycles during casting etc + time_t m_ignoreAIUpdatesUntilTime; + time_t m_enterBg, m_leaveBg; + + // defines the state of behaviour of the bot + BotState m_botState; + + // list of items needed to fullfill quests + BotNeedItem m_needItemList; + + // list of creatures we recently attacked and want to loot + BotLootCreature m_lootCreature; // list of creatures + uint64 m_lootCurrent; // current remains of interest + + time_t m_TimeDoneEating; + time_t m_TimeDoneDrinking; + uint32 m_CurrentlyCastingSpellId; + + // if master commands bot to do something, store here until updateAI + // can do it + uint64 m_targetGuidCommand; + + Unit *m_targetCombat; // current combat target + Unit *m_followTarget; // whom to follow in non combat situation? + + float orig_x, orig_y, orig_z; + uint32 orig_map; + + SpellRanges m_spellRangeMap; +}; + +#endif diff --git a/src/game/playerbot/PlayerbotClassAI.cpp b/src/game/playerbot/PlayerbotClassAI.cpp new file mode 100644 index 000000000..65297822d --- /dev/null +++ b/src/game/playerbot/PlayerbotClassAI.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PlayerbotClassAI.h" +#include "Common.h" + +PlayerbotClassAI::PlayerbotClassAI(Player* const bot, PlayerbotAI* const ai): m_bot(bot), m_ai(ai) {} +PlayerbotClassAI::~PlayerbotClassAI() {} + +bool PlayerbotClassAI::DoFirstCombatManeuver(Unit *) +{ + // return false, if done with opening moves/spells + return false; +} +void PlayerbotClassAI::DoNextCombatManeuver(Unit *) {} + +void PlayerbotClassAI::DoNonCombatActions(){} + +bool PlayerbotClassAI::BuffPlayer(Player* target) { return false; } + +void PlayerbotClassAI::InitSpells(PlayerbotAI* const ai) {} \ No newline at end of file diff --git a/src/game/playerbot/PlayerbotClassAI.h b/src/game/playerbot/PlayerbotClassAI.h new file mode 100644 index 000000000..dac225891 --- /dev/null +++ b/src/game/playerbot/PlayerbotClassAI.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PLAYERBOTCLASSAI_H +#define _PLAYERBOTCLASSAI_H + +#include "Common.h" +#include "../World.h" +#include "../SpellMgr.h" +#include "../Player.h" +#include "../ObjectMgr.h" +#include "WorldPacket.h" +#include "../Unit.h" +#include "../SharedDefines.h" +#include "PlayerbotAI.h" + +class Player; +class PlayerbotAI; + +class MANGOS_DLL_SPEC PlayerbotClassAI +{ +public: + PlayerbotClassAI(Player * const bot, PlayerbotAI * const ai); + virtual ~PlayerbotClassAI(); + + // all combat actions go here + virtual bool DoFirstCombatManeuver(Unit*); + virtual void DoNextCombatManeuver(Unit*); + + // all non combat actions go here, ex buffs, heals, rezzes + virtual void DoNonCombatActions(); + + // buff a specific player, usually a real PC who is not in group + virtual bool BuffPlayer(Player* target); + virtual void InitSpells(PlayerbotAI* const ai); + + // Utilities + Player* GetPlayerBot() { return m_bot; } + PlayerbotAI* GetAI (){ return m_ai; }; + + +private: + Player* m_bot; + PlayerbotAI* m_ai; +}; + +#endif diff --git a/src/game/playerbot/PlayerbotDeathKnightAI.cpp b/src/game/playerbot/PlayerbotDeathKnightAI.cpp new file mode 100644 index 000000000..0bcd935ad --- /dev/null +++ b/src/game/playerbot/PlayerbotDeathKnightAI.cpp @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PlayerbotDeathKnightAI.h" +#include "PlayerbotMgr.h" + +class PlayerbotAI; + +PlayerbotDeathKnightAI::PlayerbotDeathKnightAI(Player* const bot, PlayerbotAI* const ai): PlayerbotClassAI(bot, ai) +{ + InitSpells(ai); +} + +void PlayerbotDeathKnightAI::InitSpells(PlayerbotAI* const ai) +{ + PLAGUE_STRIKE = ai->initSpell(PLAGUE_STRIKE_1); // Unholy + DEATH_GRIP = ai->initSpell(DEATH_GRIP_1); + DEATH_COIL = ai->initSpell(DEATH_COIL_DEATH_KNIGHT_1); + DEATH_STRIKE = ai->initSpell(DEATH_STRIKE_1); + UNHOLY_BLIGHT = 0; // Passive + SCOURGE_STRIKE = ai->initSpell(SCOURGE_STRIKE_1); + DEATH_AND_DECAY = ai->initSpell(DEATH_AND_DECAY_1); + CORPSE_EXPLOSION = ai->initSpell(CORPSE_EXPLOSION_1); + BONE_SHIELD = ai->initSpell(BONE_SHIELD_1); // buffs + ANTI_MAGIC_SHELL = ai->initSpell(ANTI_MAGIC_SHELL_1); + ANTI_MAGIC_ZONE = ai->initSpell(ANTI_MAGIC_ZONE_1); + GHOUL_FRENZY = ai->initSpell(GHOUL_FRENZY_1); + RAISE_DEAD = ai->initSpell(RAISE_DEAD_1); // pets + SUMMON_GARGOYLE = ai->initSpell(SUMMON_GARGOYLE_1); + ARMY_OF_THE_DEAD = ai->initSpell(ARMY_OF_THE_DEAD_1); + ICY_TOUCH = ai->initSpell(ICY_TOUCH_1); // Frost + OBLITERATE = ai->initSpell(OBLITERATE_1); + HOWLING_BLAST = ai->initSpell(HOWLING_BLAST_1); + FROST_STRIKE = ai->initSpell(FROST_STRIKE_1); + CHAINS_OF_ICE = ai->initSpell(CHAINS_OF_ICE_1); + RUNE_STRIKE = ai->initSpell(RUNE_STRIKE_1); + ICY_CLUTCH = 0; // No such spell + MIND_FREEZE = ai->initSpell(MIND_FREEZE_1); + HUNGERING_COLD = ai->initSpell(HUNGERING_COLD_1); + KILLING_MACHINE = 0; // Passive + DEATHCHILL = ai->initSpell(DEATHCHILL_1); + HORN_OF_WINTER = ai->initSpell(HORN_OF_WINTER_1); + ICEBOUND_FORTITUDE = ai->initSpell(ICEBOUND_FORTITUDE_1); + EMPOWER_WEAPON = ai->initSpell(EMPOWER_RUNE_WEAPON_1); + UNBREAKABLE_ARMOR = ai->initSpell(UNBREAKABLE_ARMOR_1); + BLOOD_STRIKE = ai->initSpell(BLOOD_STRIKE_1); // Blood + PESTILENCE = ai->initSpell(PESTILENCE_1); + STRANGULATE = ai->initSpell(STRANGULATE_1); + BLOOD_BOIL = ai->initSpell(BLOOD_BOIL_1); + HEART_STRIKE = ai->initSpell(HEART_STRIKE_1); + DANCING_WEAPON = ai->initSpell(DANCING_RUNE_WEAPON_1); + DARK_COMMAND = ai->initSpell(DARK_COMMAND_1); + MARK_OF_BLOOD = ai->initSpell(MARK_OF_BLOOD_1); // buffs + RUNE_TAP = ai->initSpell(RUNE_TAP_1); + VAMPIRIC_BLOOD = ai->initSpell(VAMPIRIC_BLOOD_1); + DEATH_PACT = ai->initSpell(DEATH_PACT_1); + HYSTERIA = ai->initSpell(HYSTERIA_1); + UNHOLY_PRESENCE = ai->initSpell(UNHOLY_PRESENCE_1); // presence (TODO: better spell == presence) + FROST_PRESENCE = ai->initSpell(FROST_PRESENCE_1); + BLOOD_PRESENCE = ai->initSpell(BLOOD_PRESENCE_1); + + RECENTLY_BANDAGED = 11196; // first aid check + + // racial + ARCANE_TORRENT = ai->initSpell(ARCANE_TORRENT_DEATH_KNIGHT); // blood elf + GIFT_OF_THE_NAARU = ai->initSpell(GIFT_OF_THE_NAARU_DEATH_KNIGHT); // draenei + STONEFORM = ai->initSpell(STONEFORM_ALL); // dwarf + ESCAPE_ARTIST = ai->initSpell(ESCAPE_ARTIST_ALL); // gnome + EVERY_MAN_FOR_HIMSELF = ai->initSpell(EVERY_MAN_FOR_HIMSELF_ALL); // human + BLOOD_FURY = ai->initSpell(BLOOD_FURY_MELEE_CLASSES); // orc + WAR_STOMP = ai->initSpell(WAR_STOMP_ALL); // tauren + BERSERKING = ai->initSpell(BERSERKING_ALL); // troll + WILL_OF_THE_FORSAKEN = ai->initSpell(WILL_OF_THE_FORSAKEN_ALL); // undead +} + +PlayerbotDeathKnightAI::~PlayerbotDeathKnightAI() {} + +void PlayerbotDeathKnightAI::DoNextCombatManeuver(Unit *pTarget) +{ + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + //ai->SetMovementTarget( PlayerbotAI::MOVEMENT_FOLLOW, m_master ); // dont want to melee mob + + // DK Attacks: Unholy, Frost & Blood + + // damage spells + ai->SetInFront(pTarget); //<--- + Unit* pVictim = pTarget->getVictim(); + Pet *pet = m_bot->GetPet(); + float dist = m_bot->GetDistance(pTarget); + + + switch (SpellSequence) + { + case SPELL_DK_UNHOLY: + if (UNHOLY_PRESENCE > 0) + (!m_bot->HasAura(UNHOLY_PRESENCE, EFFECT_INDEX_0) && !m_bot->HasAura(BLOOD_PRESENCE, EFFECT_INDEX_0) && !m_bot->HasAura(FROST_PRESENCE, EFFECT_INDEX_0) && ai->CastSpell (UNHOLY_PRESENCE, m_bot)); + + // check for BONE_SHIELD in combat + if (BONE_SHIELD > 0) + (!m_bot->HasAura(BONE_SHIELD, EFFECT_INDEX_0) && !m_bot->HasAura(ARMY_OF_THE_DEAD, EFFECT_INDEX_0) && ai->CastSpell (BONE_SHIELD, m_bot)); + + if (ARMY_OF_THE_DEAD > 0 && LastSpellUnholyDK < 1) + { + ai->CastSpell(ARMY_OF_THE_DEAD); + + if (ARMY_OF_THE_DEAD > 0 && m_bot->HasAura(ARMY_OF_THE_DEAD, EFFECT_INDEX_0)) + ai->SetIgnoreUpdateTime(7); + SpellSequence = SPELL_DK_FROST; + LastSpellUnholyDK = LastSpellUnholyDK + 1; + break; + } + else if (PLAGUE_STRIKE > 0 && !pTarget->HasAura(PLAGUE_STRIKE, EFFECT_INDEX_0) && LastSpellUnholyDK < 2) + { + ai->CastSpell(PLAGUE_STRIKE, pTarget); + + SpellSequence = SPELL_DK_FROST; + LastSpellUnholyDK = LastSpellUnholyDK + 1; + break; + } + else if (DEATH_GRIP > 0 && !pTarget->HasAura(DEATH_GRIP, EFFECT_INDEX_0) && LastSpellUnholyDK < 3) + { + ai->CastSpell(DEATH_GRIP, pTarget); + + SpellSequence = SPELL_DK_FROST; + LastSpellUnholyDK = LastSpellUnholyDK + 1; + break; + } + else if (DEATH_COIL > 0 && LastSpellUnholyDK < 4 && ai->GetRunicPower() >= 40) + { + ai->CastSpell(DEATH_COIL, pTarget); + + SpellSequence = SPELL_DK_FROST; + LastSpellUnholyDK = LastSpellUnholyDK + 1; + break; + } + else if (DEATH_STRIKE > 0 && !pTarget->HasAura(DEATH_STRIKE, EFFECT_INDEX_0) && LastSpellUnholyDK < 5) + { + ai->CastSpell(DEATH_STRIKE, pTarget); + + SpellSequence = SPELL_DK_FROST; + LastSpellUnholyDK = LastSpellUnholyDK + 1; + break; + } + else if (UNHOLY_BLIGHT > 0 && !pTarget->HasAura(UNHOLY_BLIGHT, EFFECT_INDEX_0) && LastSpellUnholyDK < 6) + { + ai->CastSpell(UNHOLY_BLIGHT); + + SpellSequence = SPELL_DK_FROST; + LastSpellUnholyDK = LastSpellUnholyDK + 1; + break; + } + else if (SCOURGE_STRIKE > 0 && LastSpellUnholyDK < 7) + { + ai->CastSpell(SCOURGE_STRIKE, pTarget); + + SpellSequence = SPELL_DK_FROST; + LastSpellUnholyDK = LastSpellUnholyDK + 1; + break; + } + else if (DEATH_AND_DECAY > 0 && dist <= ATTACK_DISTANCE && !pTarget->HasAura(DEATH_AND_DECAY, EFFECT_INDEX_0) && LastSpellUnholyDK < 8) + { + ai->CastSpell(DEATH_AND_DECAY); + + ai->SetIgnoreUpdateTime(1); + SpellSequence = SPELL_DK_FROST; + LastSpellUnholyDK = LastSpellUnholyDK + 1; + break; + } + else if (SUMMON_GARGOYLE > 0 && !m_bot->HasAura(ARMY_OF_THE_DEAD, EFFECT_INDEX_0) && !pTarget->HasAura(SUMMON_GARGOYLE, EFFECT_INDEX_0) && LastSpellUnholyDK < 9 && ai->GetRunicPower() >= 60) + { + ai->CastSpell(SUMMON_GARGOYLE, pTarget); + + ai->SetIgnoreUpdateTime(2); + SpellSequence = SPELL_DK_FROST; + LastSpellUnholyDK = LastSpellUnholyDK + 1; + break; + } + else if (CORPSE_EXPLOSION > 0 && dist <= ATTACK_DISTANCE && LastSpellUnholyDK < 10) + { + ai->CastSpell(CORPSE_EXPLOSION, pTarget); + + SpellSequence = SPELL_DK_FROST; + LastSpellUnholyDK = LastSpellUnholyDK + 1; + break; + } + else if (ANTI_MAGIC_SHELL > 0 && pTarget->IsNonMeleeSpellCasted(true) && !m_bot->HasAura(ANTI_MAGIC_SHELL, EFFECT_INDEX_0) && LastSpellUnholyDK < 11 && ai->GetRunicPower() >= 20) + { + ai->CastSpell(ANTI_MAGIC_SHELL, m_bot); + + SpellSequence = SPELL_DK_FROST; + LastSpellUnholyDK = LastSpellUnholyDK + 1; + break; + } + else if (ANTI_MAGIC_ZONE > 0 && pTarget->IsNonMeleeSpellCasted(true) && !m_bot->HasAura(ANTI_MAGIC_SHELL, EFFECT_INDEX_0) && LastSpellUnholyDK < 12) + { + ai->CastSpell(ANTI_MAGIC_ZONE, m_bot); + + SpellSequence = SPELL_DK_FROST; + LastSpellUnholyDK = LastSpellUnholyDK + 1; + break; + } + else if ((!pet) + && (RAISE_DEAD > 0 && !m_bot->HasAura(ARMY_OF_THE_DEAD, EFFECT_INDEX_0) && LastSpellUnholyDK < 13)) + { + ai->CastSpell(RAISE_DEAD); + + SpellSequence = SPELL_DK_FROST; + LastSpellUnholyDK = LastSpellUnholyDK + 1; + break; + } + else if ((pet) + && (GHOUL_FRENZY > 0 && pVictim == pet && !pet->HasAura(GHOUL_FRENZY, EFFECT_INDEX_0) && LastSpellUnholyDK < 14)) + { + ai->CastSpell(GHOUL_FRENZY, pet); + + SpellSequence = SPELL_DK_FROST; + LastSpellUnholyDK = LastSpellUnholyDK + 1; + break; + } + else if (LastSpellUnholyDK > 15) + { + LastSpellUnholyDK = 0; + SpellSequence = SPELL_DK_FROST; + break; + } + + LastSpellUnholyDK = 0; + + case SPELL_DK_FROST: + if (FROST_PRESENCE > 0) + (!m_bot->HasAura(FROST_PRESENCE, EFFECT_INDEX_0) && !m_bot->HasAura(BLOOD_PRESENCE, EFFECT_INDEX_0) && !m_bot->HasAura(UNHOLY_PRESENCE, EFFECT_INDEX_0) && ai->CastSpell (FROST_PRESENCE, m_bot)); + + if (DEATHCHILL > 0) + (!m_bot->HasAura(DEATHCHILL, EFFECT_INDEX_0) && !m_bot->HasAura(KILLING_MACHINE, EFFECT_INDEX_0) && ai->CastSpell (DEATHCHILL, m_bot)); + else if (KILLING_MACHINE > 0) + (!m_bot->HasAura(KILLING_MACHINE, EFFECT_INDEX_0) && !m_bot->HasAura(DEATHCHILL, EFFECT_INDEX_0) && ai->CastSpell (KILLING_MACHINE, m_bot)); + + if (ICY_TOUCH > 0 && !pTarget->HasAura(ICY_TOUCH, EFFECT_INDEX_0) && LastSpellFrostDK < 1) + { + ai->CastSpell(ICY_TOUCH, pTarget); + + SpellSequence = SPELL_DK_BLOOD; + LastSpellFrostDK = LastSpellFrostDK + 1; + break; + } + else if (OBLITERATE > 0 && LastSpellFrostDK < 2) + { + ai->CastSpell(OBLITERATE, pTarget); + + SpellSequence = SPELL_DK_BLOOD; + LastSpellFrostDK = LastSpellFrostDK + 1; + break; + } + else if (FROST_STRIKE > 0 && LastSpellFrostDK < 3 && ai->GetRunicPower() >= 40) + { + ai->CastSpell(FROST_STRIKE, pTarget); + + SpellSequence = SPELL_DK_BLOOD; + LastSpellFrostDK = LastSpellFrostDK + 1; + break; + } + else if (HOWLING_BLAST > 0 && LastSpellFrostDK < 4) + { + ai->CastSpell(HOWLING_BLAST, pTarget); + + SpellSequence = SPELL_DK_BLOOD; + LastSpellFrostDK = LastSpellFrostDK + 1; + break; + } + else if (CHAINS_OF_ICE > 0 && !pTarget->HasAura(CHAINS_OF_ICE, EFFECT_INDEX_0) && LastSpellFrostDK < 5) + { + ai->CastSpell(CHAINS_OF_ICE, pTarget); + + SpellSequence = SPELL_DK_BLOOD; + LastSpellFrostDK = LastSpellFrostDK + 1; + break; + } + else if (RUNE_STRIKE > 0 && LastSpellFrostDK < 6 && ai->GetRunicPower() >= 20) + { + ai->CastSpell(RUNE_STRIKE, pTarget); + + SpellSequence = SPELL_DK_BLOOD; + LastSpellFrostDK = LastSpellFrostDK + 1; + break; + } + else if (ICY_CLUTCH > 0 && !pTarget->HasAura(ICY_CLUTCH, EFFECT_INDEX_0) && LastSpellFrostDK < 7) + { + ai->CastSpell(ICY_CLUTCH, pTarget); + + SpellSequence = SPELL_DK_BLOOD; + LastSpellFrostDK = LastSpellFrostDK + 1; + break; + } + else if (ICEBOUND_FORTITUDE > 0 && ai->GetHealthPercent() < 50 && pVictim == m_bot && !m_bot->HasAura(ICEBOUND_FORTITUDE, EFFECT_INDEX_0) && LastSpellFrostDK < 8 && ai->GetRunicPower() >= 20) + { + ai->CastSpell(ICEBOUND_FORTITUDE, m_bot); + + SpellSequence = SPELL_DK_BLOOD; + LastSpellFrostDK = LastSpellFrostDK + 1; + break; + } + else if (MIND_FREEZE > 0 && pTarget->IsNonMeleeSpellCasted(true) && dist <= ATTACK_DISTANCE && LastSpellFrostDK < 9 && ai->GetRunicPower() >= 20) + { + ai->CastSpell(MIND_FREEZE, pTarget); + + SpellSequence = SPELL_DK_BLOOD; + LastSpellFrostDK = LastSpellFrostDK + 1; + break; + } + else if (HUNGERING_COLD > 0 && dist <= ATTACK_DISTANCE && LastSpellFrostDK < 10 && ai->GetRunicPower() >= 40) + { + ai->CastSpell(HUNGERING_COLD, pTarget); + + SpellSequence = SPELL_DK_BLOOD; + LastSpellFrostDK = LastSpellFrostDK + 1; + break; + } + else if (EMPOWER_WEAPON > 0 && ai->GetRunicPower() < 20 && LastSpellFrostDK < 11) + { + ai->CastSpell(EMPOWER_WEAPON, m_bot); + + SpellSequence = SPELL_DK_BLOOD; + LastSpellFrostDK = LastSpellFrostDK + 1; + break; + } + else if (UNBREAKABLE_ARMOR > 0 && !m_bot->HasAura(UNBREAKABLE_ARMOR, EFFECT_INDEX_0) && ai->GetHealthPercent() < 70 && pVictim == m_bot && LastSpellFrostDK < 12) + { + ai->CastSpell(UNBREAKABLE_ARMOR, m_bot); + + SpellSequence = SPELL_DK_BLOOD; + LastSpellFrostDK = LastSpellFrostDK + 1; + break; + } + else if (LastSpellFrostDK > 13) + { + LastSpellFrostDK = 0; + SpellSequence = SPELL_DK_BLOOD; + break; + } + + LastSpellFrostDK = 0; + + case SPELL_DK_BLOOD: + if (BLOOD_PRESENCE > 0) + (!m_bot->HasAura(BLOOD_PRESENCE, EFFECT_INDEX_0) && !m_bot->HasAura(UNHOLY_PRESENCE, EFFECT_INDEX_0) && !m_bot->HasAura(FROST_PRESENCE, EFFECT_INDEX_0) && ai->CastSpell (BLOOD_PRESENCE, m_bot)); + + if (MARK_OF_BLOOD > 0 && !pTarget->HasAura(MARK_OF_BLOOD, EFFECT_INDEX_0) && LastSpellBloodDK < 1) + { + ai->CastSpell(MARK_OF_BLOOD, pTarget); + + SpellSequence = SPELL_DK_UNHOLY; + LastSpellBloodDK = LastSpellBloodDK + 1; + break; + } + else if (BLOOD_STRIKE > 0 && LastSpellBloodDK < 2) + { + ai->CastSpell(BLOOD_STRIKE, pTarget); + + SpellSequence = SPELL_DK_UNHOLY; + LastSpellBloodDK = LastSpellBloodDK + 1; + break; + } + else if (PESTILENCE > 0 && dist <= ATTACK_DISTANCE && LastSpellBloodDK < 3) + { + ai->CastSpell(PESTILENCE, pTarget); + + SpellSequence = SPELL_DK_UNHOLY; + LastSpellBloodDK = LastSpellBloodDK + 1; + break; + } + else if (STRANGULATE > 0 && !pTarget->HasAura(STRANGULATE, EFFECT_INDEX_0) && LastSpellBloodDK < 4) + { + ai->CastSpell(STRANGULATE, pTarget); + + SpellSequence = SPELL_DK_UNHOLY; + LastSpellBloodDK = LastSpellBloodDK + 1; + break; + } + else if (BLOOD_BOIL > 0 && dist <= ATTACK_DISTANCE && LastSpellBloodDK < 5) + { + ai->CastSpell(BLOOD_BOIL, pTarget); + + SpellSequence = SPELL_DK_UNHOLY; + LastSpellBloodDK = LastSpellBloodDK + 1; + break; + } + else if (HEART_STRIKE > 0 && LastSpellBloodDK < 6) + { + ai->CastSpell(HEART_STRIKE, pTarget); + + SpellSequence = SPELL_DK_UNHOLY; + LastSpellBloodDK = LastSpellBloodDK + 1; + break; + } + else if (VAMPIRIC_BLOOD > 0 && ai->GetHealthPercent() < 70 && !m_bot->HasAura(VAMPIRIC_BLOOD, EFFECT_INDEX_0) && LastSpellBloodDK < 7) + { + ai->CastSpell(VAMPIRIC_BLOOD, m_bot); + + SpellSequence = SPELL_DK_UNHOLY; + LastSpellBloodDK = LastSpellBloodDK + 1; + break; + } + else if (RUNE_TAP > 0 && ai->GetHealthPercent() < 70 && !m_bot->HasAura(VAMPIRIC_BLOOD, EFFECT_INDEX_0) && LastSpellBloodDK < 8) + { + ai->CastSpell(RUNE_TAP, m_bot); + + SpellSequence = SPELL_DK_UNHOLY; + LastSpellBloodDK = LastSpellBloodDK + 1; + break; + } + else if (HYSTERIA > 0 && ai->GetHealthPercent() > 25 && !m_bot->HasAura(HYSTERIA, EFFECT_INDEX_0) && LastSpellBloodDK < 9) + { + ai->CastSpell(HYSTERIA, m_bot); + + SpellSequence = SPELL_DK_UNHOLY; + LastSpellBloodDK = LastSpellBloodDK + 1; + break; + } + else if (DANCING_WEAPON > 0 && !m_bot->HasAura(DANCING_WEAPON, EFFECT_INDEX_0) && ai->GetRunicPower() >= 60 && LastSpellBloodDK < 10) + { + ai->CastSpell(DANCING_WEAPON, pTarget); + + SpellSequence = SPELL_DK_UNHOLY; + LastSpellBloodDK = LastSpellBloodDK + 1; + break; + } + else if (DARK_COMMAND > 0 && ai->GetHealthPercent() > 50 && pVictim != m_bot && !pTarget->HasAura(DARK_COMMAND, EFFECT_INDEX_0) && dist <= ATTACK_DISTANCE && LastSpellBloodDK < 11) + { + ai->CastSpell(DARK_COMMAND, pTarget); + + SpellSequence = SPELL_DK_UNHOLY; + LastSpellBloodDK = LastSpellBloodDK + 1; + break; + } + else if ((pet) + && (DEATH_PACT > 0 && ai->GetHealthPercent() < 50 && LastSpellBloodDK < 12 && ai->GetRunicPower() >= 40)) + { + ai->CastSpell(DEATH_PACT, pet); + + SpellSequence = SPELL_DK_UNHOLY; + LastSpellBloodDK = LastSpellBloodDK + 1; + break; + } + else if (LastSpellBloodDK > 13) + { + LastSpellBloodDK = 0; + SpellSequence = SPELL_DK_UNHOLY; + break; + } + else + { + LastSpellBloodDK = 0; + SpellSequence = SPELL_DK_UNHOLY; + } + } +} // end DoNextCombatManeuver + +void PlayerbotDeathKnightAI::DoNonCombatActions() +{ + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + SpellSequence = SPELL_DK_UNHOLY; + + // buff master with HORN_OF_WINTER + if (HORN_OF_WINTER > 0) + (!m_master->HasAura(HORN_OF_WINTER, EFFECT_INDEX_0) && ai->CastSpell (HORN_OF_WINTER, m_master)); + + // hp check + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + + Item* pItem = ai->FindFood(); + Item* fItem = ai->FindBandage(); + + if (pItem != NULL && ai->GetHealthPercent() < 30) + { + ai->UseItem(pItem); + return; + } + else if (pItem == NULL && fItem != NULL && !m_bot->HasAura(RECENTLY_BANDAGED, EFFECT_INDEX_0) && ai->GetHealthPercent() < 70) + { + ai->UseItem(fItem); + return; + } +} // end DoNonCombatActions diff --git a/src/game/playerbot/PlayerbotDeathKnightAI.h b/src/game/playerbot/PlayerbotDeathKnightAI.h new file mode 100644 index 000000000..965b740a5 --- /dev/null +++ b/src/game/playerbot/PlayerbotDeathKnightAI.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PLAYERDEATHKNIGHTAI_H +#define _PLAYERDEATHKNIGHTAI_H + +#include "PlayerbotClassAI.h" + +enum +{ + SPELL_DK_UNHOLY, + SPELL_DK_FROST, + SPELL_DK_BLOOD +}; + +enum DeathKnightSpells +{ + ANTI_MAGIC_SHELL_1 = 48707, + ANTI_MAGIC_ZONE_1 = 51052, + ARMY_OF_THE_DEAD_1 = 42650, + BLOOD_BOIL_1 = 48721, + BLOOD_PRESENCE_1 = 48266, + BLOOD_STRIKE_1 = 45902, + BLOOD_TAP_1 = 45529, + BONE_SHIELD_1 = 49222, + CHAINS_OF_ICE_1 = 45524, + CORPSE_EXPLOSION_1 = 49158, + DANCING_RUNE_WEAPON_1 = 49028, + DARK_COMMAND_1 = 56222, + DEATH_AND_DECAY_1 = 43265, + DEATH_COIL_DEATH_KNIGHT_1 = 47541, + DEATH_GRIP_1 = 49576, + DEATH_PACT_1 = 48743, + DEATH_STRIKE_1 = 49998, + DEATHCHILL_1 = 49796, + EMPOWER_RUNE_WEAPON_1 = 47568, + FROST_PRESENCE_1 = 48263, + FROST_STRIKE_1 = 49143, + GHOUL_FRENZY_1 = 63560, + HEART_STRIKE_1 = 55050, + HORN_OF_WINTER_1 = 57330, + HOWLING_BLAST_1 = 49184, + HUNGERING_COLD_1 = 49203, + HYSTERIA_1 = 49016, + ICEBOUND_FORTITUDE_1 = 48792, + ICY_TOUCH_1 = 45477, + LICHBORNE_1 = 49039, + MARK_OF_BLOOD_1 = 49005, + MIND_FREEZE_1 = 47528, + OBLITERATE_1 = 49020, + PATH_OF_FROST_1 = 3714, + PESTILENCE_1 = 50842, + PLAGUE_STRIKE_1 = 45462, + RAISE_ALLY_1 = 61999, + RAISE_DEAD_1 = 46584, + RUNE_STRIKE_1 = 56815, + RUNE_TAP_1 = 48982, + SCOURGE_STRIKE_1 = 55090, + STRANGULATE_1 = 47476, + SUMMON_GARGOYLE_1 = 49206, + UNBREAKABLE_ARMOR_1 = 51271, + UNHOLY_PRESENCE_1 = 48265, + VAMPIRIC_BLOOD_1 = 55233 +}; +//class Player; + +class MANGOS_DLL_SPEC PlayerbotDeathKnightAI : PlayerbotClassAI +{ +public: + PlayerbotDeathKnightAI(Player* const bot, PlayerbotAI* const ai); + virtual ~PlayerbotDeathKnightAI(); + + // all combat actions go here + void DoNextCombatManeuver(Unit*); + + // all non combat actions go here, ex buffs, heals, rezzes + void DoNonCombatActions(); + + // buff a specific player, usually a real PC who is not in group + //void BuffPlayer(Player *target); + + void InitSpells(PlayerbotAI* const ai); + +private: + + // Unholy + uint32 BONE_SHIELD, PLAGUE_STRIKE, DEATH_GRIP, DEATH_COIL, DEATH_STRIKE, UNHOLY_BLIGHT, SCOURGE_STRIKE, DEATH_AND_DECAY, UNHOLY_PRESENCE, RAISE_DEAD, ARMY_OF_THE_DEAD, SUMMON_GARGOYLE, ANTI_MAGIC_SHELL, ANTI_MAGIC_ZONE, GHOUL_FRENZY, CORPSE_EXPLOSION; + + // Frost + uint32 ICY_TOUCH, OBLITERATE, HOWLING_BLAST, FROST_STRIKE, CHAINS_OF_ICE, RUNE_STRIKE, ICY_CLUTCH, HORN_OF_WINTER, KILLING_MACHINE, FROST_PRESENCE, DEATHCHILL, ICEBOUND_FORTITUDE, MIND_FREEZE, EMPOWER_WEAPON, HUNGERING_COLD, UNBREAKABLE_ARMOR; + + // Blood + uint32 BLOOD_STRIKE, PESTILENCE, STRANGULATE, BLOOD_BOIL, HEART_STRIKE, MARK_OF_BLOOD, BLOOD_PRESENCE, RUNE_TAP, VAMPIRIC_BLOOD, DEATH_PACT, DEATH_RUNE_MASTERY, HYSTERIA, DANCING_WEAPON, DARK_COMMAND; + + // first aid + uint32 RECENTLY_BANDAGED; + + // racial + uint32 ARCANE_TORRENT, GIFT_OF_THE_NAARU, STONEFORM, ESCAPE_ARTIST, EVERY_MAN_FOR_HIMSELF, SHADOWMELD, BLOOD_FURY, WAR_STOMP, BERSERKING, WILL_OF_THE_FORSAKEN; + + uint32 SpellSequence, LastSpellUnholyDK, LastSpellFrostDK, LastSpellBloodDK; +}; + +#endif diff --git a/src/game/playerbot/PlayerbotDruidAI.cpp b/src/game/playerbot/PlayerbotDruidAI.cpp new file mode 100644 index 000000000..7d773b4ee --- /dev/null +++ b/src/game/playerbot/PlayerbotDruidAI.cpp @@ -0,0 +1,681 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "PlayerbotDruidAI.h" + +class PlayerbotAI; + +PlayerbotDruidAI::PlayerbotDruidAI(Player* const bot, PlayerbotAI* const ai): PlayerbotClassAI(bot, ai) +{ + InitSpells(ai); +} + +void PlayerbotDruidAI::InitSpells(PlayerbotAI* const ai) +{ + MOONFIRE = ai->initSpell(MOONFIRE_1); // attacks + STARFIRE = ai->initSpell(STARFIRE_1); + STARFALL = ai->initSpell(STARFALL_1); + WRATH = ai->initSpell(WRATH_1); + ROOTS = ai->initSpell(ENTANGLING_ROOTS_1); + INSECT_SWARM = ai->initSpell(INSECT_SWARM_1); + FORCE_OF_NATURE = ai->initSpell(FORCE_OF_NATURE_1); + HURRICANE = ai->initSpell(HURRICANE_1); + MARK_OF_THE_WILD = ai->initSpell(MARK_OF_THE_WILD_1); // buffs + GIFT_OF_THE_WILD = ai->initSpell(GIFT_OF_THE_WILD_1); + THORNS = ai->initSpell(THORNS_1); + BARKSKIN = ai->initSpell(BARKSKIN_1); + INNERVATE = ai->initSpell(INNERVATE_1); + FAERIE_FIRE = ai->initSpell(FAERIE_FIRE_1); // debuffs + REJUVENATION = ai->initSpell(REJUVENATION_1); // heals + REGROWTH = ai->initSpell(REGROWTH_1); + WILD_GROWTH = ai->initSpell(WILD_GROWTH_1); + LIFEBLOOM = ai->initSpell(LIFEBLOOM_1); + NOURISH = ai->initSpell(NOURISH_1); + HEALING_TOUCH = ai->initSpell(HEALING_TOUCH_1); + SWIFTMEND = ai->initSpell(SWIFTMEND_1); + TRANQUILITY = ai->initSpell(TRANQUILITY_1); + REVIVE = ai->initSpell(REVIVE_1); + // Druid Forms + MOONKIN_FORM = ai->initSpell(MOONKIN_FORM_1); + DIRE_BEAR_FORM = ai->initSpell(DIRE_BEAR_FORM_1); + BEAR_FORM = ai->initSpell(BEAR_FORM_1); + CAT_FORM = ai->initSpell(CAT_FORM_1); + TREE_OF_LIFE = ai->initSpell(TREE_OF_LIFE_1); + TRAVEL_FORM = ai->initSpell(TRAVEL_FORM_1); + // Cat Attack type's + RAKE = ai->initSpell(RAKE_1); + CLAW = ai->initSpell(CLAW_1); // 45 + COWER = ai->initSpell(COWER_1); // 20 + MANGLE = ai->initSpell(MANGLE_1); // 45 + TIGERS_FURY = ai->initSpell(TIGERS_FURY_1); + // Cat Finishing Move's + RIP = ai->initSpell(RIP_1); // 30 + FEROCIOUS_BITE = ai->initSpell(FEROCIOUS_BITE_1); // 35 + MAIM = ai->initSpell(MAIM_1); // 35 + // Bear/Dire Bear Attacks & Buffs + BASH = ai->initSpell(BASH_1); + MAUL = ai->initSpell(MAUL_1); // 15 + SWIPE = ai->initSpell(SWIPE_BEAR_1); // 20 + DEMORALIZING_ROAR = ai->initSpell(DEMORALIZING_ROAR_1); // 10 + CHALLENGING_ROAR = ai->initSpell(CHALLENGING_ROAR_1); + ENRAGE = ai->initSpell(ENRAGE_1); + GROWL = ai->initSpell(GROWL_1); + + RECENTLY_BANDAGED = 11196; // first aid check + + // racial + SHADOWMELD = ai->initSpell(SHADOWMELD_ALL); + WAR_STOMP = ai->initSpell(WAR_STOMP_ALL); // tauren +} + +PlayerbotDruidAI::~PlayerbotDruidAI() {} + +bool PlayerbotDruidAI::HealTarget(Unit *target) +{ + PlayerbotAI* ai = GetAI(); + uint8 hp = target->GetHealth() * 100 / target->GetMaxHealth(); + + if (hp >= 70) + return false; + + // Reset form if needed + GoBuffForm(GetPlayerBot()); + + if (hp < 70 && REJUVENATION > 0 && !target->HasAura(REJUVENATION) && ai->CastSpell(REJUVENATION, target)) + return true; + + if (hp < 60 && LIFEBLOOM > 0 && !target->HasAura(LIFEBLOOM) && ai->CastSpell(LIFEBLOOM, target)) + return true; + + if (hp < 55 && REGROWTH > 0 && !target->HasAura(REGROWTH) && ai->CastSpell(REGROWTH, target)) + return true; + + if (hp < 50 && SWIFTMEND > 0 && (target->HasAura(REJUVENATION) || target->HasAura(REGROWTH)) && ai->CastSpell(SWIFTMEND, target)) + return true; + + if (hp < 45 && WILD_GROWTH > 0 && !target->HasAura(WILD_GROWTH) && ai->CastSpell(WILD_GROWTH, target)) + return true; + + if (hp < 30 && NOURISH > 0 && ai->CastSpell(NOURISH, target)) + return true; + + if (hp < 25 && HEALING_TOUCH > 0 && ai->CastSpell(HEALING_TOUCH, target)) + return true; + + return false; +} // end HealTarget + +void PlayerbotDruidAI::DoNextCombatManeuver(Unit *pTarget) +{ + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + uint32 masterHP = m_master->GetHealth() * 100 / m_master->GetMaxHealth(); + + ai->SetInFront(pTarget); + Unit* pVictim = pTarget->getVictim(); + + if (pVictim && ai->GetHealthPercent() >= 40 && m_master->GetHealth() >= m_master->GetMaxHealth() * 0.4) + { + if (pVictim == m_bot) + SpellSequence = DruidTank; + } + else if (pTarget->GetHealth() > pTarget->GetMaxHealth() * 0.8 && pVictim) + { + if (pVictim != m_bot) + SpellSequence = DruidSpell; + } + else if (ai->GetHealthPercent() <= 40 || m_master->GetHealth() <= m_master->GetMaxHealth() * 0.4) + SpellSequence = DruidHeal; + else + SpellSequence = DruidCombat; + + switch (SpellSequence) + { + case DruidTank: // Its now a tank druid! + if (!m_bot->HasInArc(M_PI_F, pTarget)) + { + m_bot->SetInFront(pTarget); + } + if (m_bot->HasAura(CAT_FORM, EFFECT_INDEX_0)) + m_bot->RemoveAurasDueToSpell(768); + if (MOONKIN_FORM > 0 && !m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0)) + ai->CastSpell (MOONKIN_FORM); + else if (DIRE_BEAR_FORM > 0 && !m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0) && !m_bot->HasAura(DIRE_BEAR_FORM, EFFECT_INDEX_0)) + ai->CastSpell (DIRE_BEAR_FORM); + else if (BEAR_FORM > 0 && !m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0) && !m_bot->HasAura(DIRE_BEAR_FORM, EFFECT_INDEX_0) && !m_bot->HasAura(BEAR_FORM, EFFECT_INDEX_0)) + ai->CastSpell (BEAR_FORM); + else if (DEMORALIZING_ROAR > 0 && (m_bot->HasAura(DIRE_BEAR_FORM, EFFECT_INDEX_0) || m_bot->HasAura(BEAR_FORM, EFFECT_INDEX_0)) && !m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0) && !pTarget->HasAura(DEMORALIZING_ROAR, EFFECT_INDEX_0) && ai->GetRageAmount() >= 10) + ai->CastSpell(DEMORALIZING_ROAR, pTarget); + if (FAERIE_FIRE > 0 && DruidSpellCombat < 1 && !pTarget->HasAura(FAERIE_FIRE, EFFECT_INDEX_0)) + { + ai->CastSpell(FAERIE_FIRE, pTarget); + DruidSpellCombat++; + break; + } + else if (MOONFIRE > 0 && m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0) && DruidSpellCombat < 2 && !pTarget->HasAura(MOONFIRE, EFFECT_INDEX_0) && ai->GetManaPercent() >= 24) + { + ai->CastSpell(MOONFIRE, pTarget); + DruidSpellCombat++; + break; + } + else if (ROOTS > 0 && m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0) && DruidSpellCombat < 3 && !pTarget->HasAura(ROOTS, EFFECT_INDEX_0) && ai->GetManaPercent() >= 8) + { + ai->CastSpell(ROOTS, pTarget); + DruidSpellCombat++; + break; + } + else if (HURRICANE > 0 && m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0) && DruidSpellCombat < 4 && ai->GetManaPercent() >= 91) + { + ai->CastSpell(HURRICANE, pTarget); + ai->SetIgnoreUpdateTime(10); + DruidSpellCombat++; + break; + } + else if (WRATH > 0 && m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0) && DruidSpellCombat < 5 && ai->GetManaPercent() >= 13) + { + ai->CastSpell(WRATH, pTarget); + DruidSpellCombat++; + break; + } + else if (INSECT_SWARM > 0 && m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0) && DruidSpellCombat < 6 && !pTarget->HasAura(INSECT_SWARM, EFFECT_INDEX_0) && ai->GetManaPercent() >= 9) + { + ai->CastSpell(INSECT_SWARM, pTarget); + DruidSpellCombat++; + break; + } + else if (STARFIRE > 0 && m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0) && DruidSpellCombat < 7 && ai->GetManaPercent() >= 18) + { + ai->CastSpell(STARFIRE, pTarget); + DruidSpellCombat++; + break; + } + else if (FORCE_OF_NATURE > 0 && m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0) && DruidSpellCombat < 8 && ai->GetManaPercent() >= 12) + { + ai->CastSpell(FORCE_OF_NATURE); + DruidSpellCombat++; + break; + } + else if (STARFALL > 0 && m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0) && !m_bot->HasAura(STARFALL, EFFECT_INDEX_0) && DruidSpellCombat < 9 && ai->GetManaPercent() >= 39) + { + ai->CastSpell(STARFALL, pTarget); + DruidSpellCombat++; + break; + } + else if (BARKSKIN > 0 && pVictim == m_bot && m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0) && ai->GetHealthPercent() < 75 && DruidSpellCombat < 10 && !m_bot->HasAura(BARKSKIN, EFFECT_INDEX_0)) + { + ai->CastSpell(BARKSKIN, m_bot); + DruidSpellCombat++; + break; + } + else if (INNERVATE > 0 && m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0) && ai->GetManaPercent() < 50 && DruidSpellCombat < 11 && !m_bot->HasAura(INNERVATE, EFFECT_INDEX_0)) + { + ai->CastSpell(INNERVATE, m_bot); + DruidSpellCombat++; + break; + } + else if (ENRAGE > 0 && (m_bot->HasAura(DIRE_BEAR_FORM, EFFECT_INDEX_0) || m_bot->HasAura(BEAR_FORM, EFFECT_INDEX_0)) && DruidSpellCombat < 2 && !m_bot->HasAura(ENRAGE, EFFECT_INDEX_0)) + { + ai->CastSpell(ENRAGE, m_bot); + DruidSpellCombat = DruidSpellCombat + 2; + break; + } + else if (SWIPE > 0 && (m_bot->HasAura(DIRE_BEAR_FORM, EFFECT_INDEX_0) || m_bot->HasAura(BEAR_FORM, EFFECT_INDEX_0)) && DruidSpellCombat < 4 && ai->GetRageAmount() >= 20) + { + ai->CastSpell(SWIPE, pTarget); + DruidSpellCombat = DruidSpellCombat + 2; + break; + } + else if (MAUL > 0 && (m_bot->HasAura(DIRE_BEAR_FORM, EFFECT_INDEX_0) || m_bot->HasAura(BEAR_FORM, EFFECT_INDEX_0)) && DruidSpellCombat < 6 && ai->GetRageAmount() >= 15) + { + ai->CastSpell(MAUL, pTarget); + DruidSpellCombat = DruidSpellCombat + 2; + break; + } + else if (BASH > 0 && (m_bot->HasAura(DIRE_BEAR_FORM, EFFECT_INDEX_0) || m_bot->HasAura(BEAR_FORM, EFFECT_INDEX_0)) && !pTarget->HasAura(BASH, EFFECT_INDEX_0) && DruidSpellCombat < 8 && ai->GetRageAmount() >= 10) + { + ai->CastSpell(BASH, pTarget); + DruidSpellCombat = DruidSpellCombat + 2; + break; + } + else if (CHALLENGING_ROAR > 0 && (m_bot->HasAura(DIRE_BEAR_FORM, EFFECT_INDEX_0) || m_bot->HasAura(BEAR_FORM, EFFECT_INDEX_0)) && pVictim != m_bot && DruidSpellCombat < 10 && !pTarget->HasAura(CHALLENGING_ROAR, EFFECT_INDEX_0) && !pTarget->HasAura(GROWL, EFFECT_INDEX_0) && ai->GetRageAmount() >= 15) + { + ai->CastSpell(CHALLENGING_ROAR, pTarget); + DruidSpellCombat = DruidSpellCombat + 2; + break; + } + else if (GROWL > 0 && (m_bot->HasAura(DIRE_BEAR_FORM, EFFECT_INDEX_0) || m_bot->HasAura(BEAR_FORM, EFFECT_INDEX_0)) && pVictim != m_bot && DruidSpellCombat < 12 && !pTarget->HasAura(CHALLENGING_ROAR, EFFECT_INDEX_0) && !pTarget->HasAura(GROWL, EFFECT_INDEX_0)) + { + ai->CastSpell(GROWL, pTarget); + DruidSpellCombat = DruidSpellCombat + 2; + break; + } + else if (DruidSpellCombat > 13) + { + DruidSpellCombat = 0; + break; + } + else + { + DruidSpellCombat = 0; + break; + } + break; + + case DruidSpell: + if (m_bot->HasAura(CAT_FORM, EFFECT_INDEX_0)) + { + m_bot->RemoveAurasDueToSpell(768); + + break; + } + if (m_bot->HasAura(BEAR_FORM, EFFECT_INDEX_0)) + { + m_bot->RemoveAurasDueToSpell(5487); + + break; + } + if (m_bot->HasAura(DIRE_BEAR_FORM, EFFECT_INDEX_0)) + { + m_bot->RemoveAurasDueToSpell(9634); + + break; + } + if (m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0)) + { + m_bot->RemoveAurasDueToSpell(24858); + + break; + } + if (FAERIE_FIRE > 0 && DruidSpellCombat < 1 && !pTarget->HasAura(FAERIE_FIRE, EFFECT_INDEX_0) && ai->GetManaPercent() >= 8) + { + ai->CastSpell(FAERIE_FIRE, pTarget); + DruidSpellCombat++; + break; + } + else if (MOONFIRE > 0 && DruidSpellCombat < 2 && !pTarget->HasAura(MOONFIRE, EFFECT_INDEX_0) && ai->GetManaPercent() >= 24) + { + ai->CastSpell(MOONFIRE, pTarget); + DruidSpellCombat++; + break; + } + else if (ROOTS > 0 && DruidSpellCombat < 3 && !pTarget->HasAura(ROOTS, EFFECT_INDEX_0) && ai->GetManaPercent() >= 8) + { + ai->CastSpell(ROOTS, pTarget); + DruidSpellCombat++; + break; + } + else if (HURRICANE > 0 && DruidSpellCombat < 4 && ai->GetManaPercent() >= 91) + { + + ai->CastSpell(HURRICANE, pTarget); + ai->SetIgnoreUpdateTime(10); + DruidSpellCombat++; + break; + } + else if (WRATH > 0 && DruidSpellCombat < 5 && ai->GetManaPercent() >= 13) + { + ai->CastSpell(WRATH, pTarget); + DruidSpellCombat++; + break; + } + else if (INSECT_SWARM > 0 && DruidSpellCombat < 6 && !pTarget->HasAura(INSECT_SWARM, EFFECT_INDEX_0) && ai->GetManaPercent() >= 9) + { + ai->CastSpell(INSECT_SWARM, pTarget); + DruidSpellCombat++; + break; + } + else if (STARFIRE > 0 && DruidSpellCombat < 7 && ai->GetManaPercent() >= 18) + { + ai->CastSpell(STARFIRE, pTarget); + DruidSpellCombat++; + break; + } + else if (FORCE_OF_NATURE > 0 && DruidSpellCombat < 8 && ai->GetManaPercent() >= 12) + { + + ai->CastSpell(FORCE_OF_NATURE); + DruidSpellCombat++; + break; + } + else if (STARFALL > 0 && !m_bot->HasAura(STARFALL, EFFECT_INDEX_0) && DruidSpellCombat < 9 && ai->GetManaPercent() >= 39) + { + ai->CastSpell(STARFALL, pTarget); + DruidSpellCombat++; + break; + } + else if (BARKSKIN > 0 && pVictim == m_bot && ai->GetHealthPercent() < 75 && DruidSpellCombat < 10 && !m_bot->HasAura(BARKSKIN, EFFECT_INDEX_0)) + { + ai->CastSpell(BARKSKIN, m_bot); + DruidSpellCombat++; + break; + } + else if (INNERVATE > 0 && ai->GetManaPercent() < 50 && DruidSpellCombat < 11 && !m_bot->HasAura(INNERVATE, EFFECT_INDEX_0)) + { + ai->CastSpell(INNERVATE, m_bot); + DruidSpellCombat++; + break; + } + else if (DruidSpellCombat > 13) + { + DruidSpellCombat = 0; + break; + } + else + { + DruidSpellCombat = 0; + break; + } + break; + + case DruidHeal: + + if (m_bot->HasAura(CAT_FORM, EFFECT_INDEX_0)) + { + m_bot->RemoveAurasDueToSpell(768); + + break; + } + if (m_bot->HasAura(BEAR_FORM, EFFECT_INDEX_0)) + { + m_bot->RemoveAurasDueToSpell(5487); + + break; + } + if (m_bot->HasAura(DIRE_BEAR_FORM, EFFECT_INDEX_0)) + { + m_bot->RemoveAurasDueToSpell(9634); + + break; + } + if (m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0)) + { + m_bot->RemoveAurasDueToSpell(24858); + + break; + } + if (ai->GetHealthPercent() <= 40) + { + HealTarget (m_bot); + break; + } + if (masterHP <= 40) + { + HealTarget (m_master); + break; + } + else + { + DruidSpellCombat = 0; + break; + } + break; + + case DruidCombat: + + if (!m_bot->HasInArc(M_PI_F, pTarget)) + { + m_bot->SetInFront(pTarget); + } + if (m_bot->HasAura(BEAR_FORM, EFFECT_INDEX_0)) + { + m_bot->RemoveAurasDueToSpell(5487); + + break; + } + if (m_bot->HasAura(DIRE_BEAR_FORM, EFFECT_INDEX_0)) + { + m_bot->RemoveAurasDueToSpell(9634); + + break; + } + if (m_bot->HasAura(MOONKIN_FORM, EFFECT_INDEX_0)) + { + m_bot->RemoveAurasDueToSpell(24858); + + break; + } + if (CAT_FORM > 0 && !m_bot->HasAura(CAT_FORM, EFFECT_INDEX_0)) + ai->CastSpell (CAT_FORM); +/* + if (COWER > 0 && m_bot->GetComboPoints() == 1 && ai->GetEnergyAmount() >= 20) + { + ai->CastSpell(COWER); + + }*/ + if (MAIM > 0 && m_bot->GetComboPoints() >= 1 && pTarget->IsNonMeleeSpellCasted(true)) + { + ai->CastSpell(MAIM, pTarget); + + break; + } + + if (RAKE > 0 && m_bot->GetComboPoints() <= 1 && ai->GetEnergyAmount() >= 40) + { + ai->CastSpell (RAKE, pTarget); + + break; + } + else if (CLAW > 0 && m_bot->GetComboPoints() <= 2 && ai->GetEnergyAmount() >= 45) + { + ai->CastSpell (CLAW, pTarget); + + break; + } + else if (MANGLE > 0 && m_bot->GetComboPoints() <= 3 && ai->GetEnergyAmount() >= 45) + { + ai->CastSpell (MANGLE, pTarget); + + break; + } + else if (CLAW > 0 && m_bot->GetComboPoints() <= 4 && ai->GetEnergyAmount() >= 45) + { + ai->CastSpell (CLAW, pTarget); + + break; + } + + if (m_bot->GetComboPoints() == 5) + { + if (RIP > 0 && pTarget->getClass() == CLASS_ROGUE && ai->GetEnergyAmount() >= 30) + ai->CastSpell(RIP, pTarget); + + else if (MAIM > 0 && pTarget->getClass() == CLASS_DRUID && ai->GetEnergyAmount() >= 35) + ai->CastSpell(MAIM, pTarget); + + else if (MAIM > 0 && pTarget->getClass() == CLASS_SHAMAN && ai->GetEnergyAmount() >= 35) + ai->CastSpell(MAIM, pTarget); + + else if (MAIM > 0 && pTarget->getClass() == CLASS_WARLOCK && ai->GetEnergyAmount() >= 35) + ai->CastSpell(MAIM, pTarget); + + else if (FEROCIOUS_BITE > 0 && pTarget->getClass() == CLASS_HUNTER && ai->GetEnergyAmount() >= 35) + ai->CastSpell(FEROCIOUS_BITE, pTarget); + + else if (FEROCIOUS_BITE > 0 && pTarget->getClass() == CLASS_WARRIOR && ai->GetEnergyAmount() >= 35) + ai->CastSpell(FEROCIOUS_BITE, pTarget); + + else if (FEROCIOUS_BITE > 0 && pTarget->getClass() == CLASS_PALADIN && ai->GetEnergyAmount() >= 35) + ai->CastSpell(FEROCIOUS_BITE, pTarget); + + else if (FEROCIOUS_BITE > 0 && pTarget->getClass() == CLASS_DEATH_KNIGHT && ai->GetEnergyAmount() >= 25) + ai->CastSpell(FEROCIOUS_BITE, pTarget); + + else if (MAIM > 0 && pTarget->getClass() == CLASS_MAGE && ai->GetEnergyAmount() >= 35) + ai->CastSpell(MAIM, pTarget); + + else if (MAIM > 0 && pTarget->getClass() == CLASS_PRIEST && ai->GetEnergyAmount() >= 35) + ai->CastSpell(MAIM, pTarget); + + else if (MAIM > 0 && ai->GetEnergyAmount() >= 35) + ai->CastSpell(MAIM, pTarget); + + break; + } + else + { + DruidSpellCombat = 0; + break; + } + break; + } +} // end DoNextCombatManeuver + +void PlayerbotDruidAI::DoNonCombatActions() +{ + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + // mana check + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + + Item* pItem = ai->FindDrink(); + Item* fItem = ai->FindBandage(); + + if (pItem != NULL && ai->GetManaPercent() < 30) + { + + ai->UseItem(pItem); + return; + } + else if (!pItem && INNERVATE > 0 && !m_bot->HasAura(INNERVATE) && ai->GetManaPercent() <= 20 && ai->CastSpell(INNERVATE, m_bot)) + return; + + // hp check + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + + pItem = ai->FindFood(); + + if (pItem != NULL && ai->GetHealthPercent() < 30) + { + + ai->UseItem(pItem); + return; + } + else if (pItem == NULL && fItem != NULL && !m_bot->HasAura(RECENTLY_BANDAGED, EFFECT_INDEX_0) && ai->GetHealthPercent() < 70) + { + + ai->UseItem(fItem); + return; + } + + // buff and heal master's group + if (m_master->GetGroup()) + { + // Buff master with group buff + if (m_master->isAlive() && GIFT_OF_THE_WILD && ai->HasSpellReagents(GIFT_OF_THE_WILD) && ai->Buff(GIFT_OF_THE_WILD, m_master)) + return; + + Group::MemberSlotList const& groupSlot = m_master->GetGroup()->GetMemberSlots(); + for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) + { + Player *tPlayer = sObjectMgr.GetPlayer(itr->guid); + if (!tPlayer || tPlayer == m_bot) + continue; + + // Resurrect member if needed + if (!tPlayer->isAlive()) + { + if (ai->CastSpell(REVIVE, tPlayer)) + return; + else + continue; + } + else + { + // buff and heal + if (BuffPlayer(tPlayer)) + return; + + if (HealTarget(tPlayer)) + return; + } + } + } + else + { + if (m_master->isAlive()) + { + if (BuffPlayer(m_master)) + return; + if (HealTarget(m_master)) + return; + } + else + ai->CastSpell(REVIVE, m_master); + + } + + BuffPlayer(m_bot); +} // end DoNonCombatActions + +bool PlayerbotDruidAI::BuffPlayer(Player* target) +{ + PlayerbotAI * ai = GetAI(); + + Pet * pet = target->GetPet(); + if (pet) + { + if (ai->Buff(MARK_OF_THE_WILD, pet, &(PlayerbotDruidAI::GoBuffForm))) + return true; + else if (ai->Buff(THORNS, pet, &(PlayerbotDruidAI::GoBuffForm))) + return true; + } + + if (ai->Buff(MARK_OF_THE_WILD, target, &(PlayerbotDruidAI::GoBuffForm))) + return true; + else if (ai->Buff(THORNS, target, &(PlayerbotDruidAI::GoBuffForm))) + return true; + else + return false; +} + +void PlayerbotDruidAI::GoBuffForm(Player *self) +{ + // RANK_1 spell ids used because this is a static method which does not have access to instance. + // There is only one rank for these spells anyway. + if (self->HasAura(CAT_FORM_1)) + self->RemoveAurasDueToSpell(CAT_FORM_1); + if (self->HasAura(BEAR_FORM_1)) + self->RemoveAurasDueToSpell(BEAR_FORM_1); + if (self->HasAura(DIRE_BEAR_FORM_1)) + self->RemoveAurasDueToSpell(DIRE_BEAR_FORM_1); + if (self->HasAura(MOONKIN_FORM_1)) + self->RemoveAurasDueToSpell(MOONKIN_FORM_1); + if (self->HasAura(TRAVEL_FORM_1)) + self->RemoveAurasDueToSpell(TRAVEL_FORM_1); +} diff --git a/src/game/playerbot/PlayerbotDruidAI.h b/src/game/playerbot/PlayerbotDruidAI.h new file mode 100644 index 000000000..3bad5503b --- /dev/null +++ b/src/game/playerbot/PlayerbotDruidAI.h @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PLAYERBOTDRUIDAI_H +#define _PLAYERBOTDRUIDAI_H + +#include "PlayerbotClassAI.h" + +enum +{ + DruidCombat, + DruidTank, + DruidHeal, + DruidSpell +}; + +enum DruidSpells +{ + ABOLISH_POISON_1 = 2893, + AQUATIC_FORM_1 = 1066, + BARKSKIN_1 = 22812, + BASH_1 = 5211, + BEAR_FORM_1 = 5487, + BERSERK_1 = 50334, + CAT_FORM_1 = 768, + CHALLENGING_ROAR_1 = 5209, + CLAW_1 = 1082, + COWER_1 = 8998, + CURE_POISON_1 = 8946, + CYCLONE_1 = 33786, + DASH_1 = 1850, + DEMORALIZING_ROAR_1 = 99, + DIRE_BEAR_FORM_1 = 9634, + ENRAGE_1 = 5229, + ENTANGLING_ROOTS_1 = 339, + FAERIE_FIRE_1 = 770, + FAERIE_FIRE_FERAL_1 = 16857, + FERAL_CHARGE_1 = 49377, + FERAL_CHARGE_BEAR_1 = 16979, + FERAL_CHARGE_CAT_1 = 49376, + FEROCIOUS_BITE_1 = 22568, + FLIGHT_FORM_1 = 33943, + FORCE_OF_NATURE_1 = 33831, + FRENZIED_REGENERATION_1 = 22842, + GIFT_OF_THE_WILD_1 = 21849, + GROWL_1 = 6795, + HEALING_TOUCH_1 = 5185, + HIBERNATE_1 = 2637, + HURRICANE_1 = 16914, + INNERVATE_1 = 29166, + INSECT_SWARM_1 = 5570, + LACERATE_1 = 33745, + LIFEBLOOM_1 = 33763, + MAIM_1 = 22570, + MANGLE_1 = 33917, + MANGLE_BEAR_1 = 33878, + MANGLE_CAT_1 = 33876, + MARK_OF_THE_WILD_1 = 1126, + MAUL_1 = 6807, + MOONFIRE_1 = 8921, + MOONKIN_FORM_1 = 24858, + NATURES_GRASP_1 = 16689, + NATURES_SWIFTNESS_DRUID_1 = 17116, + NOURISH_1 = 50464, + POUNCE_1 = 9005, + PROWL_1 = 5215, + RAKE_1 = 1822, + RAVAGE_1 = 6785, + REBIRTH_1 = 20484, + REGROWTH_1 = 8936, + REJUVENATION_1 = 774, + REMOVE_CURSE_DRUID_1 = 2782, + REVIVE_1 = 50769, + RIP_1 = 1079, + SAVAGE_ROAR_1 = 52610, + SHRED_1 = 5221, + SOOTHE_ANIMAL_1 = 2908, + STARFALL_1 = 48505, + STARFIRE_1 = 2912, + SURVIVAL_INSTINCTS_1 = 61336, + SWIFTMEND_1 = 18562, + SWIFT_FLIGHT_FORM_1 = 40120, + SWIPE_BEAR_1 = 779, + SWIPE_CAT_1 = 62078, + THORNS_1 = 467, + TIGERS_FURY_1 = 5217, + TRANQUILITY_1 = 740, + TRAVEL_FORM_1 = 783, + TREE_OF_LIFE_1 = 33891, + TYPHOON_1 = 50516, + WILD_GROWTH_1 = 48438, + WRATH_1 = 5176 +}; + +//class Player; + +class MANGOS_DLL_SPEC PlayerbotDruidAI : PlayerbotClassAI +{ +public: + PlayerbotDruidAI(Player* const bot, PlayerbotAI* const ai); + virtual ~PlayerbotDruidAI(); + + // all combat actions go here + void DoNextCombatManeuver(Unit*); + + // all non combat actions go here, ex buffs, heals, rezzes + void DoNonCombatActions(); + + // buff a specific player, usually a real PC who is not in group + bool BuffPlayer(Player *target); + + void InitSpells(PlayerbotAI* const ai); + +private: + // Heals the target based off its hps + bool HealTarget (Unit *target); + // Callback method to reset shapeshift forms blocking buffs and heals + static void GoBuffForm(Player *self); + + // druid cat/bear/dire bear/moonkin/tree of life forms + uint32 CAT_FORM, + BEAR_FORM, + DIRE_BEAR_FORM, + MOONKIN_FORM, + TREE_OF_LIFE, + TRAVEL_FORM; + + // druid cat attacks + uint32 CLAW, + COWER, + TIGERS_FURY, + RAKE, + RIP, + FEROCIOUS_BITE, + MAIM, + MANGLE; + + // druid bear/dire bear attacks & buffs + uint32 BASH, + MAUL, + SWIPE, + DEMORALIZING_ROAR, + CHALLENGING_ROAR, + GROWL, + ENRAGE; + + // druid attacks & debuffs + uint32 MOONFIRE, + ROOTS, + WRATH, + STARFALL, + STARFIRE, + INSECT_SWARM, + FAERIE_FIRE, + FORCE_OF_NATURE, + HURRICANE; + + // druid buffs + uint32 MARK_OF_THE_WILD, + GIFT_OF_THE_WILD, + THORNS, + INNERVATE, + BARKSKIN; + + // druid heals + uint32 LIFEBLOOM, + REJUVENATION, + REGROWTH, + NOURISH, + HEALING_TOUCH, + WILD_GROWTH, + SWIFTMEND, + TRANQUILITY, + REVIVE; + + // first aid + uint32 RECENTLY_BANDAGED; + + // racial + uint32 ARCANE_TORRENT, + GIFT_OF_THE_NAARU, + STONEFORM, + ESCAPE_ARTIST, + EVERY_MAN_FOR_HIMSELF, + SHADOWMELD, + BLOOD_FURY, + WAR_STOMP, + BERSERKING, + WILL_OF_THE_FORSAKEN; + + uint32 SpellSequence, DruidSpellCombat; +}; + +#endif diff --git a/src/game/playerbot/PlayerbotHunterAI.cpp b/src/game/playerbot/PlayerbotHunterAI.cpp new file mode 100644 index 000000000..5ee980162 --- /dev/null +++ b/src/game/playerbot/PlayerbotHunterAI.cpp @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PlayerbotHunterAI.h" +#include "PlayerbotMgr.h" + +class PlayerbotAI; + +PlayerbotHunterAI::PlayerbotHunterAI(Player* const bot, PlayerbotAI* const ai): PlayerbotClassAI(bot, ai) +{ + InitSpells(ai); + m_petSummonFailed = false; + m_rangedCombat = true; +} + +void PlayerbotHunterAI::InitSpells(PlayerbotAI* const ai) +{ + // PET CTRL + PET_SUMMON = ai->initSpell(CALL_PET_1); + PET_DISMISS = ai->initSpell(DISMISS_PET_1); + PET_REVIVE = ai->initSpell(REVIVE_PET_1); + PET_MEND = ai->initSpell(MEND_PET_1); + PET_FEED = 1539; + + INTIMIDATION = ai->initSpell(INTIMIDATION_1); // (generic) + + // PET SKILLS must be initialized by pets + SONIC_BLAST = 0; // bat + DEMORALIZING_SCREECH = 0; + BAD_ATTITUDE = 0; // crocolisk + NETHER_SHOCK = 0; + + // RANGED COMBAT + AUTO_SHOT = ai->initSpell(AUTO_SHOT_1); + HUNTERS_MARK = ai->initSpell(HUNTERS_MARK_1); + ARCANE_SHOT = ai->initSpell(ARCANE_SHOT_1); + CONCUSSIVE_SHOT = ai->initSpell(CONCUSSIVE_SHOT_1); + DISTRACTING_SHOT = ai->initSpell(DISTRACTING_SHOT_1); + MULTI_SHOT = ai->initSpell(MULTISHOT_1); + EXPLOSIVE_SHOT = ai->initSpell(EXPLOSIVE_SHOT_1); + SERPENT_STING = ai->initSpell(SERPENT_STING_1); + SCORPID_STING = ai->initSpell(SCORPID_STING_1); + WYVERN_STING = ai->initSpell(WYVERN_STING_1); + VIPER_STING = ai->initSpell(VIPER_STING_1); + AIMED_SHOT = ai->initSpell(AIMED_SHOT_1); + STEADY_SHOT = ai->initSpell(STEADY_SHOT_1); + CHIMERA_SHOT = ai->initSpell(CHIMERA_SHOT_1); + VOLLEY = ai->initSpell(VOLLEY_1); + BLACK_ARROW = ai->initSpell(BLACK_ARROW_1); + KILL_SHOT = ai->initSpell(KILL_SHOT_1); + + // MELEE + RAPTOR_STRIKE = ai->initSpell(RAPTOR_STRIKE_1); + WING_CLIP = ai->initSpell(WING_CLIP_1); + MONGOOSE_BITE = ai->initSpell(MONGOOSE_BITE_1); + DISENGAGE = ai->initSpell(DISENGAGE_1); + MISDIRECTION = ai->initSpell(MISDIRECTION_1); + DETERRENCE = ai->initSpell(DETERRENCE_1); + + // TRAPS + BEAR_TRAP = 0; // non-player spell + FREEZING_TRAP = ai->initSpell(FREEZING_TRAP_1); + IMMOLATION_TRAP = ai->initSpell(IMMOLATION_TRAP_1); + FROST_TRAP = ai->initSpell(FROST_TRAP_1); + EXPLOSIVE_TRAP = ai->initSpell(EXPLOSIVE_TRAP_1); + ARCANE_TRAP = 0; // non-player spell + SNAKE_TRAP = ai->initSpell(SNAKE_TRAP_1); + + // BUFFS + ASPECT_OF_THE_HAWK = ai->initSpell(ASPECT_OF_THE_HAWK_1); + ASPECT_OF_THE_MONKEY = ai->initSpell(ASPECT_OF_THE_MONKEY_1); + RAPID_FIRE = ai->initSpell(RAPID_FIRE_1); + TRUESHOT_AURA = ai->initSpell(TRUESHOT_AURA_1); + + RECENTLY_BANDAGED = 11196; // first aid check + + // racial + ARCANE_TORRENT = ai->initSpell(ARCANE_TORRENT_MANA_CLASSES); + GIFT_OF_THE_NAARU = ai->initSpell(GIFT_OF_THE_NAARU_HUNTER); // draenei + STONEFORM = ai->initSpell(STONEFORM_ALL); // dwarf + SHADOWMELD = ai->initSpell(SHADOWMELD_ALL); + BLOOD_FURY = ai->initSpell(BLOOD_FURY_MELEE_CLASSES); // orc + WAR_STOMP = ai->initSpell(WAR_STOMP_ALL); // tauren + BERSERKING = ai->initSpell(BERSERKING_ALL); // troll +} + +PlayerbotHunterAI::~PlayerbotHunterAI() {} + +bool PlayerbotHunterAI::HasPet(Player* bot) +{ + QueryResult* result = CharacterDatabase.PQuery("SELECT * FROM character_pet WHERE owner = '%u' AND (slot = '%u' OR slot = '%u')", bot->GetGUIDLow(), PET_SAVE_AS_CURRENT, PET_SAVE_NOT_IN_SLOT); + + if (result) + { + delete result; + return true; //hunter has current pet + } + else + return false; //hunter either has no pet or stabled +} // end HasPet + +void PlayerbotHunterAI::DoNextCombatManeuver(Unit *pTarget) +{ + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + // Hunter + ai->SetInFront(pTarget); + Unit* pVictim = pTarget->getVictim(); + + // check for pet and heal if neccessary + Pet *pet = m_bot->GetPet(); + if ((pet) + && (((float) pet->GetHealth() / (float) pet->GetMaxHealth()) < 0.5f) + && (PET_MEND > 0 && !pet->getDeathState() != ALIVE && pVictim != m_bot && !pet->HasAura(PET_MEND, EFFECT_INDEX_0) && ai->GetManaPercent() >= 13 && ai->CastSpell(PET_MEND, m_bot))) + { + + return; + } + else if ((pet) + && (INTIMIDATION > 0 && pVictim == pet && !pet->HasAura(INTIMIDATION, EFFECT_INDEX_0) && ai->CastSpell(INTIMIDATION, m_bot))) + + return; + + // racial traits + if (m_bot->getRace() == RACE_ORC && !m_bot->HasAura(BLOOD_FURY, EFFECT_INDEX_0)) + ai->CastSpell(BLOOD_FURY, m_bot); + + else if (m_bot->getRace() == RACE_TROLL && !m_bot->HasAura(BERSERKING, EFFECT_INDEX_0)) + ai->CastSpell(BERSERKING, m_bot); + + + // check if ranged combat is possible (set m_rangedCombat and switch auras + float dist = m_bot->GetDistance(pTarget); + if ((dist <= ATTACK_DISTANCE || !m_bot->GetUInt32Value(PLAYER_AMMO_ID)) && m_rangedCombat) + { + // switch to melee combat (target in melee range, out of ammo) + m_rangedCombat = false; + if (!m_bot->GetUInt32Value(PLAYER_AMMO_ID)) + + // become monkey (increases dodge chance)... + (ASPECT_OF_THE_MONKEY > 0 && !m_bot->HasAura(ASPECT_OF_THE_MONKEY, EFFECT_INDEX_0) && ai->CastSpell(ASPECT_OF_THE_MONKEY, m_bot)); + } + else if (dist > ATTACK_DISTANCE && !m_rangedCombat) + { + // switch to ranged combat + m_rangedCombat = true; + // increase ranged attack power... + (ASPECT_OF_THE_HAWK > 0 && !m_bot->HasAura(ASPECT_OF_THE_HAWK, EFFECT_INDEX_0) && ai->CastSpell(ASPECT_OF_THE_HAWK, m_bot)); + } + else if (m_rangedCombat && !m_bot->HasAura(ASPECT_OF_THE_HAWK, EFFECT_INDEX_0)) + // check if we have hawk aspect in ranged combat + (ASPECT_OF_THE_HAWK > 0 && ai->CastSpell(ASPECT_OF_THE_HAWK, m_bot)); + else if (!m_rangedCombat && !m_bot->HasAura(ASPECT_OF_THE_MONKEY, EFFECT_INDEX_0)) + // check if we have monkey aspect in melee combat + (ASPECT_OF_THE_MONKEY > 0 && ai->CastSpell(ASPECT_OF_THE_MONKEY, m_bot)); + + // activate auto shot + if (AUTO_SHOT > 0 && m_rangedCombat && !m_bot->FindCurrentSpellBySpellId(AUTO_SHOT)) + ai->CastSpell(AUTO_SHOT, pTarget); + + else if (AUTO_SHOT > 0 && m_bot->FindCurrentSpellBySpellId(AUTO_SHOT)) + m_bot->InterruptNonMeleeSpells(true, AUTO_SHOT); + + + // damage spells + + if (m_rangedCombat) + { + + if (HUNTERS_MARK > 0 && ai->GetManaPercent() >= 3 && !pTarget->HasAura(HUNTERS_MARK, EFFECT_INDEX_0) && ai->CastSpell(HUNTERS_MARK, pTarget)) + {} + else if (RAPID_FIRE > 0 && ai->GetManaPercent() >= 3 && !m_bot->HasAura(RAPID_FIRE, EFFECT_INDEX_0) && ai->CastSpell(RAPID_FIRE, m_bot)) + {} + else if (MULTI_SHOT > 0 && ai->GetManaPercent() >= 13 && ai->CastSpell(MULTI_SHOT, pTarget)) + {} + else if (ARCANE_SHOT > 0 && ai->GetManaPercent() >= 7 && ai->CastSpell(ARCANE_SHOT, pTarget)) + {} + else if (CONCUSSIVE_SHOT > 0 && ai->GetManaPercent() >= 6 && !pTarget->HasAura(CONCUSSIVE_SHOT, EFFECT_INDEX_0) && ai->CastSpell(CONCUSSIVE_SHOT, pTarget)) + {} + else if (EXPLOSIVE_SHOT > 0 && ai->GetManaPercent() >= 10 && !pTarget->HasAura(EXPLOSIVE_SHOT, EFFECT_INDEX_0) && ai->CastSpell(EXPLOSIVE_SHOT, pTarget)) + {} + else if (VIPER_STING > 0 && ai->GetManaPercent() >= 8 && pTarget->GetPower(POWER_MANA) > 0 && ai->GetManaPercent() < 70 && !pTarget->HasAura(VIPER_STING, EFFECT_INDEX_0) && ai->CastSpell(VIPER_STING, pTarget)) + {} + else if (SERPENT_STING > 0 && ai->GetManaPercent() >= 13 && !pTarget->HasAura(SERPENT_STING, EFFECT_INDEX_0) && !pTarget->HasAura(SCORPID_STING, EFFECT_INDEX_0) && !pTarget->HasAura(VIPER_STING, EFFECT_INDEX_0) && ai->CastSpell(SERPENT_STING, pTarget)) + {} + else if (SCORPID_STING > 0 && ai->GetManaPercent() >= 11 && !pTarget->HasAura(WYVERN_STING, EFFECT_INDEX_0) && !pTarget->HasAura(SCORPID_STING, EFFECT_INDEX_0) && !pTarget->HasAura(SERPENT_STING, EFFECT_INDEX_0) && !pTarget->HasAura(VIPER_STING, EFFECT_INDEX_0) && ai->CastSpell(SCORPID_STING, pTarget)) + {} + else if (CHIMERA_SHOT > 0 && ai->GetManaPercent() >= 12 && ai->CastSpell(CHIMERA_SHOT, pTarget)) + {} + else if (VOLLEY > 0 && ai->GetManaPercent() >= 24 && ai->CastSpell(VOLLEY, pTarget)) + {} + else if (BLACK_ARROW > 0 && ai->GetManaPercent() >= 6 && !pTarget->HasAura(BLACK_ARROW, EFFECT_INDEX_0) && ai->CastSpell(BLACK_ARROW, pTarget)) + {} + else if (AIMED_SHOT > 0 && ai->GetManaPercent() >= 12 && ai->CastSpell(AIMED_SHOT, pTarget)) + {} + else if (STEADY_SHOT > 0 && ai->GetManaPercent() >= 5 && ai->CastSpell(STEADY_SHOT, pTarget)) + {} + else if (KILL_SHOT > 0 && ai->GetManaPercent() >= 7 && pTarget->GetHealth() < pTarget->GetMaxHealth() * 0.2 && ai->CastSpell(KILL_SHOT, pTarget)) + {} + } + else + { + if (RAPTOR_STRIKE > 0 && ai->GetManaPercent() >= 6 && ai->CastSpell(RAPTOR_STRIKE, pTarget)) + {} + else if (EXPLOSIVE_TRAP > 0 && ai->GetManaPercent() >= 27 && !pTarget->HasAura(EXPLOSIVE_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(ARCANE_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(IMMOLATION_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(FROST_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(BEAR_TRAP, EFFECT_INDEX_0) && ai->CastSpell(EXPLOSIVE_TRAP, pTarget)) + {} + else if (WING_CLIP > 0 && ai->GetManaPercent() >= 6 && !pTarget->HasAura(WING_CLIP, EFFECT_INDEX_0) && ai->CastSpell(WING_CLIP, pTarget)) + {} + else if (IMMOLATION_TRAP > 0 && ai->GetManaPercent() >= 13 && !pTarget->HasAura(IMMOLATION_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(ARCANE_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(EXPLOSIVE_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(FROST_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(BEAR_TRAP, EFFECT_INDEX_0) && ai->CastSpell(IMMOLATION_TRAP, pTarget)) + {} + else if (MONGOOSE_BITE > 0 && ai->GetManaPercent() >= 4 && ai->CastSpell(MONGOOSE_BITE, pTarget)) + {} + else if (FROST_TRAP > 0 && ai->GetManaPercent() >= 2 && !pTarget->HasAura(FROST_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(ARCANE_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(IMMOLATION_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(EXPLOSIVE_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(BEAR_TRAP, EFFECT_INDEX_0) && ai->CastSpell(FROST_TRAP, pTarget)) + {} + else if (ARCANE_TRAP > 0 && !pTarget->HasAura(ARCANE_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(BEAR_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(EXPLOSIVE_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(IMMOLATION_TRAP, EFFECT_INDEX_0) && !pTarget->HasAura(FROST_TRAP, EFFECT_INDEX_0) && ai->CastSpell(ARCANE_TRAP, pTarget)) + {} + else if (DETERRENCE > 0 && pVictim == m_bot && m_bot->GetHealth() < m_bot->GetMaxHealth() * 0.5 && !m_bot->HasAura(DETERRENCE, EFFECT_INDEX_0) && ai->CastSpell(DETERRENCE, m_bot)) + {} + else if (m_bot->getRace() == RACE_TAUREN && !pTarget->HasAura(WAR_STOMP, EFFECT_INDEX_0) && ai->CastSpell(WAR_STOMP, pTarget)) + {} + else if (m_bot->getRace() == RACE_BLOODELF && !pTarget->HasAura(ARCANE_TORRENT, EFFECT_INDEX_0) && ai->CastSpell(ARCANE_TORRENT, pTarget)) + {} + else if (m_bot->getRace() == RACE_DWARF && m_bot->HasAuraState(AURA_STATE_DEADLY_POISON) && ai->CastSpell(STONEFORM, m_bot)) + {} + else if (m_bot->getRace() == RACE_NIGHTELF && pVictim == m_bot && ai->GetHealthPercent() < 25 && !m_bot->HasAura(SHADOWMELD, EFFECT_INDEX_0) && ai->CastSpell(SHADOWMELD, m_bot)) + {} + else if (m_bot->getRace() == RACE_DRAENEI && ai->GetHealthPercent() < 25 && !m_bot->HasAura(GIFT_OF_THE_NAARU, EFFECT_INDEX_0) && ai->CastSpell(GIFT_OF_THE_NAARU, m_bot)) + {} + else if ((pet && !pet->getDeathState() != ALIVE) + && (MISDIRECTION > 0 && pVictim == m_bot && !m_bot->HasAura(MISDIRECTION, EFFECT_INDEX_0) && ai->GetManaPercent() >= 9 && ai->CastSpell(MISDIRECTION, pet))) + {} + } + +} // end DoNextCombatManeuver + +void PlayerbotHunterAI::DoNonCombatActions() +{ + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + // reset ranged combat state + if (!m_rangedCombat) + m_rangedCombat = true; + + // buff group + if (TRUESHOT_AURA > 0) + (!m_bot->HasAura(TRUESHOT_AURA, EFFECT_INDEX_0) && ai->CastSpell (TRUESHOT_AURA, m_bot)); + + // buff myself + if (ASPECT_OF_THE_HAWK > 0) + (!m_bot->HasAura(ASPECT_OF_THE_HAWK, EFFECT_INDEX_0) && ai->CastSpell (ASPECT_OF_THE_HAWK, m_bot)); + + // mana check + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + + Item* pItem = ai->FindDrink(); + Item* fItem = ai->FindBandage(); + + if (pItem != NULL && ai->GetManaPercent() < 30) + { + + ai->UseItem(pItem); + return; + } + + // hp check + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + + pItem = ai->FindFood(); + + if (pItem != NULL && ai->GetHealthPercent() < 30) + { + + ai->UseItem(pItem); + return; + } + else if (pItem == NULL && fItem != NULL && !m_bot->HasAura(RECENTLY_BANDAGED, EFFECT_INDEX_0) && ai->GetHealthPercent() < 70) + { + + ai->UseItem(fItem); + return; + } + else if (pItem == NULL && fItem == NULL && m_bot->getRace() == RACE_DRAENEI && !m_bot->HasAura(GIFT_OF_THE_NAARU, EFFECT_INDEX_0) && ai->GetHealthPercent() < 70) + { + + ai->CastSpell(GIFT_OF_THE_NAARU, m_bot); + return; + } + + // check for pet + if (PET_SUMMON > 0 && !m_petSummonFailed && HasPet(m_bot)) + { + // we can summon pet, and no critical summon errors before + Pet *pet = m_bot->GetPet(); + if (!pet) + { + // summon pet + if (PET_SUMMON > 0 && ai->CastSpell(PET_SUMMON, m_bot)) + { + } + else + { + m_petSummonFailed = true; + } + } + else if (pet->getDeathState() != ALIVE) + { + // revive pet + if (PET_REVIVE > 0 && ai->GetManaPercent() >= 80 && ai->CastSpell(PET_REVIVE, m_bot)) + { + } + } + else if (((float) pet->GetHealth() / (float) pet->GetMaxHealth()) < 0.5f) + { + // heal pet when health lower 50% + if (PET_MEND > 0 && !pet->getDeathState() != ALIVE && !pet->HasAura(PET_MEND, EFFECT_INDEX_0) && ai->GetManaPercent() >= 13 && ai->CastSpell(PET_MEND, m_bot)) + { + } + } + else if (pet->GetHappinessState() != HAPPY) // if pet is hungry + { + Unit *caster = (Unit*) m_bot; + // list out items in main backpack + for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) + { + Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + if (!pItemProto) + continue; + + if (pet->HaveInDiet(pItemProto)) // is pItem in pets diet + { + //sLog.outDebug("Food for pet: %s",pItemProto->Name1); + caster->CastSpell(caster, 51284, true); // pet feed visual + uint32 count = 1; // number of items used + int32 benefit = pet->GetCurrentFoodBenefitLevel(pItemProto->ItemLevel); // nutritional value of food + m_bot->DestroyItemCount(pItem, count, true); // remove item from inventory + m_bot->CastCustomSpell(m_bot, PET_FEED, &benefit, NULL, NULL, true); // feed pet + + ai->SetIgnoreUpdateTime(10); + return; + } + } + } + // list out items in other removable backpacks + for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) + { + const Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); + if (pBag) + for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot) + { + Item* const pItem = m_bot->GetItemByPos(bag, slot); + if (pItem) + { + const ItemPrototype* const pItemProto = pItem->GetProto(); + if (!pItemProto) + continue; + + if (pet->HaveInDiet(pItemProto)) // is pItem in pets diet + { + //sLog.outDebug("Food for pet: %s",pItemProto->Name1); + caster->CastSpell(caster, 51284, true); // pet feed visual + uint32 count = 1; // number of items used + int32 benefit = pet->GetCurrentFoodBenefitLevel(pItemProto->ItemLevel); // nutritional value of food + m_bot->DestroyItemCount(pItem, count, true); // remove item from inventory + m_bot->CastCustomSpell(m_bot, PET_FEED, &benefit, NULL, NULL, true); // feed pet + + ai->SetIgnoreUpdateTime(10); + return; + } + } + } + } + if (pet->HasAura(PET_MEND, EFFECT_INDEX_0) && !pet->HasAura(PET_FEED, EFFECT_INDEX_0)) + + ai->SetIgnoreUpdateTime(7); + } + } +} // end DoNonCombatActions diff --git a/src/game/playerbot/PlayerbotHunterAI.h b/src/game/playerbot/PlayerbotHunterAI.h new file mode 100644 index 000000000..3d9d3963b --- /dev/null +++ b/src/game/playerbot/PlayerbotHunterAI.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PLAYERHUNTERAI_H +#define _PLAYERHUNTERAI_H + +#include "PlayerbotClassAI.h" + +enum +{ + SPELL_HUNTER +}; + +enum HunterSpells +{ + ARCANE_SHOT_1 = 3044, + ASPECT_OF_THE_BEAST_1 = 13161, + ASPECT_OF_THE_CHEETAH_1 = 5118, + ASPECT_OF_THE_DRAGONHAWK_1 = 61846, + ASPECT_OF_THE_HAWK_1 = 13165, + ASPECT_OF_THE_MONKEY_1 = 13163, + ASPECT_OF_THE_PACK_1 = 13159, + ASPECT_OF_THE_VIPER_1 = 34074, + ASPECT_OF_THE_WILD_1 = 20043, + AUTO_SHOT_1 = 75, + BEAST_LORE_1 = 1462, + CALL_PET_1 = 883, + CALL_STABLED_PET_1 = 62757, + CONCUSSIVE_SHOT_1 = 5116, + DETERRENCE_1 = 19263, + DISENGAGE_1 = 781, + DISMISS_PET_1 = 2641, + DISTRACTING_SHOT_1 = 20736, + EAGLE_EYE_1 = 6197, + EXPLOSIVE_TRAP_1 = 13813, + EYES_OF_THE_BEAST_1 = 1002, + FEED_PET_1 = 6991, + FEIGN_DEATH_1 = 5384, + FLARE_1 = 1543, + FREEZING_ARROW_1 = 60192, + FREEZING_TRAP_1 = 1499, + FROST_TRAP_1 = 13809, + HUNTERS_MARK_1 = 1130, + IMMOLATION_TRAP_1 = 13795, + KILL_COMMAND_1 = 34026, + KILL_SHOT_1 = 53351, + MASTERS_CALL_1 = 53271, + MEND_PET_1 = 136, + MISDIRECTION_1 = 34477, + MONGOOSE_BITE_1 = 1495, + MULTISHOT_1 = 2643, + RAPID_FIRE_1 = 3045, + RAPTOR_STRIKE_1 = 2973, + REVIVE_PET_1 = 982, + SCARE_BEAST_1 = 1513, + SCORPID_STING_1 = 3043, + SERPENT_STING_1 = 1978, + SNAKE_TRAP_1 = 34600, + STEADY_SHOT_1 = 56641, + TAME_BEAST_1 = 1515, + TRACK_BEASTS_1 = 1494, + TRACK_DEMONS_1 = 19878, + TRACK_DRAGONKIN_1 = 19879, + TRACK_ELEMENTALS_1 = 19880, + TRACK_GIANTS_1 = 19882, + TRACK_HIDDEN_1 = 19885, + TRACK_HUMANOIDS_1 = 19883, + TRACK_UNDEAD_1 = 19884, + TRANQUILIZING_SHOT_1 = 19801, + VIPER_STING_1 = 3034, + VOLLEY_1 = 1510, + WING_CLIP_1 = 2974, + AIMED_SHOT_1 = 19434, + BESTIAL_WRATH_1 = 19574, + BLACK_ARROW_1 = 3674, + CHIMERA_SHOT_1 = 53209, + COUNTERATTACK_1 = 19306, + EXPLOSIVE_SHOT_1 = 53301, + INTIMIDATION_1 = 19577, + READINESS_1 = 23989, + SCATTER_SHOT_1 = 19503, + SILENCING_SHOT_1 = 34490, + TRUESHOT_AURA_1 = 19506, + WYVERN_STING_1 = 19386 +}; + +//class Player; + +class MANGOS_DLL_SPEC PlayerbotHunterAI : PlayerbotClassAI +{ +public: + PlayerbotHunterAI(Player* const bot, PlayerbotAI* const ai); + virtual ~PlayerbotHunterAI(); + bool HasPet(Player* bot); + + // all combat actions go here + void DoNextCombatManeuver(Unit*); + + // all non combat actions go here, ex buffs, heals, rezzes + void DoNonCombatActions(); + + // buff a specific player, usually a real PC who is not in group + //void BuffPlayer(Player *target); + + void InitSpells(PlayerbotAI* const ai); + +private: + // Hunter + bool m_petSummonFailed; + bool m_rangedCombat; + + uint32 PET_SUMMON, PET_DISMISS, PET_REVIVE, PET_MEND, PET_FEED, BAD_ATTITUDE, SONIC_BLAST, NETHER_SHOCK, DEMORALIZING_SCREECH, INTIMIDATION; + uint32 AUTO_SHOT, HUNTERS_MARK, ARCANE_SHOT, CONCUSSIVE_SHOT, DISTRACTING_SHOT, MULTI_SHOT, EXPLOSIVE_SHOT, SERPENT_STING, SCORPID_STING, VIPER_STING, WYVERN_STING, AIMED_SHOT, STEADY_SHOT, CHIMERA_SHOT, VOLLEY, BLACK_ARROW, KILL_SHOT; + uint32 RAPTOR_STRIKE, WING_CLIP, MONGOOSE_BITE, DISENGAGE, DETERRENCE; + uint32 BEAR_TRAP, FREEZING_TRAP, IMMOLATION_TRAP, FROST_TRAP, EXPLOSIVE_TRAP, ARCANE_TRAP, SNAKE_TRAP; + uint32 ASPECT_OF_THE_HAWK, ASPECT_OF_THE_MONKEY, RAPID_FIRE, TRUESHOT_AURA, MISDIRECTION; + + // first aid + uint32 RECENTLY_BANDAGED; + + // racial + uint32 ARCANE_TORRENT, GIFT_OF_THE_NAARU, STONEFORM, ESCAPE_ARTIST, EVERY_MAN_FOR_HIMSELF, SHADOWMELD, BLOOD_FURY, WAR_STOMP, BERSERKING, WILL_OF_THE_FORSAKEN; +}; + +#endif diff --git a/src/game/playerbot/PlayerbotMageAI.cpp b/src/game/playerbot/PlayerbotMageAI.cpp new file mode 100644 index 000000000..dea8473d2 --- /dev/null +++ b/src/game/playerbot/PlayerbotMageAI.cpp @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PlayerbotMageAI.h" + +class PlayerbotAI; + +PlayerbotMageAI::PlayerbotMageAI(Player* const bot, PlayerbotAI* const ai): PlayerbotClassAI(bot, ai) +{ + InitSpells(ai); + LastSpellFrost = 0; + LastSpellFire = 0; + LastSpellArcane = 0; +} + +void PlayerbotMageAI::InitSpells(PlayerbotAI* const ai) +{ + ARCANE_MISSILES = ai->initSpell(ARCANE_MISSILES_1); + ARCANE_EXPLOSION = ai->initSpell(ARCANE_EXPLOSION_1); + COUNTERSPELL = ai->initSpell(COUNTERSPELL_1); + SLOW = ai->initSpell(SLOW_1); + ARCANE_BARRAGE = ai->initSpell(ARCANE_BARRAGE_1); + ARCANE_BLAST = ai->initSpell(ARCANE_BLAST_1); + ARCANE_POWER = ai->initSpell(ARCANE_POWER_1); + DAMPEN_MAGIC = ai->initSpell(DAMPEN_MAGIC_1); + AMPLIFY_MAGIC = ai->initSpell(AMPLIFY_MAGIC_1); + MAGE_ARMOR = ai->initSpell(MAGE_ARMOR_1); + MIRROR_IMAGE = ai->initSpell(MIRROR_IMAGE_1); + ARCANE_INTELLECT = ai->initSpell(ARCANE_INTELLECT_1); + ARCANE_BRILLIANCE = ai->initSpell(ARCANE_BRILLIANCE_1); + DALARAN_INTELLECT = ai->initSpell(DALARAN_INTELLECT_1); + DALARAN_BRILLIANCE = ai->initSpell(DALARAN_BRILLIANCE_1); + MANA_SHIELD = ai->initSpell(MANA_SHIELD_1); + CONJURE_WATER = ai->initSpell(CONJURE_WATER_1); + CONJURE_FOOD = ai->initSpell(CONJURE_FOOD_1); + FIREBALL = ai->initSpell(FIREBALL_1); + FIRE_BLAST = ai->initSpell(FIRE_BLAST_1); + FLAMESTRIKE = ai->initSpell(FLAMESTRIKE_1); + SCORCH = ai->initSpell(SCORCH_1); + PYROBLAST = ai->initSpell(PYROBLAST_1); + BLAST_WAVE = ai->initSpell(BLAST_WAVE_1); + COMBUSTION = ai->initSpell(COMBUSTION_1); + DRAGONS_BREATH = ai->initSpell(DRAGONS_BREATH_1); + LIVING_BOMB = ai->initSpell(LIVING_BOMB_1); + FROSTFIRE_BOLT = ai->initSpell(FROSTFIRE_BOLT_1); + FIRE_WARD = ai->initSpell(FIRE_WARD_1); + MOLTEN_ARMOR = ai->initSpell(MOLTEN_ARMOR_1); + ICY_VEINS = ai->initSpell(ICY_VEINS_1); + DEEP_FREEZE = ai->initSpell(DEEP_FREEZE_1); + FROSTBOLT = ai->initSpell(FROSTBOLT_1); + FROST_NOVA = ai->initSpell(FROST_NOVA_1); + BLIZZARD = ai->initSpell(BLIZZARD_1); + CONE_OF_COLD = ai->initSpell(CONE_OF_COLD_1); + ICE_BARRIER = ai->initSpell(ICE_BARRIER_1); + SUMMON_WATER_ELEMENTAL = ai->initSpell(SUMMON_WATER_ELEMENTAL_1); + FROST_WARD = ai->initSpell(FROST_WARD_1); + ICE_LANCE = ai->initSpell(ICE_LANCE_1); + FROST_ARMOR = ai->initSpell(FROST_ARMOR_1); + ICE_ARMOR = ai->initSpell(ICE_ARMOR_1); + ICE_BLOCK = ai->initSpell(ICE_BLOCK_1); + COLD_SNAP = ai->initSpell(COLD_SNAP_1); + + RECENTLY_BANDAGED = 11196; // first aid check + + // racial + ARCANE_TORRENT = ai->initSpell(ARCANE_TORRENT_MANA_CLASSES); // blood elf + GIFT_OF_THE_NAARU = ai->initSpell(GIFT_OF_THE_NAARU_MAGE); // draenei + ESCAPE_ARTIST = ai->initSpell(ESCAPE_ARTIST_ALL); // gnome + EVERY_MAN_FOR_HIMSELF = ai->initSpell(EVERY_MAN_FOR_HIMSELF_ALL); // human + BERSERKING = ai->initSpell(BERSERKING_ALL); // troll + WILL_OF_THE_FORSAKEN = ai->initSpell(WILL_OF_THE_FORSAKEN_ALL); // undead +} + +PlayerbotMageAI::~PlayerbotMageAI() {} + +void PlayerbotMageAI::DoNextCombatManeuver(Unit *pTarget) +{ + PlayerbotAI* ai = GetAI(); + Player* m_bot = GetPlayerBot(); + Player* m_master = ai->GetLeader(); + + ai->SetInFront(pTarget); + + switch (m_bot->getRole()) + { + case MageFire: + { + static uint32 SpellFire[] = {SCORCH, FROSTFIRE_BOLT, FIRE_BLAST, SCORCH, FIREBALL, + FLAMESTRIKE, SCORCH, FIREBALL, BLAST_WAVE, SCORCH, PYROBLAST, SCORCH, FIREBALL, + LIVING_BOMB, DRAGONS_BREATH}; + static uint32 elt = sizeof(SpellFire)/sizeof(uint32); + + for (uint32 i = 1; i <= elt; ++i) + { + if (ai->CastSpell(SpellFire[(i+LastSpellFire)%elt], pTarget)) + { + LastSpellFire = (i+LastSpellFire)%elt; + break; + } + } + break; + } + case MageArcane: + { + static uint32 SpellArcane[] = {ARCANE_MISSILES, ARCANE_BLAST, ARCANE_BLAST, ARCANE_BLAST, + ARCANE_BLAST, ARCANE_MISSILES, ARCANE_BARRAGE}; + static uint32 elt = sizeof(SpellArcane)/sizeof(uint32); + + for (uint32 i = 1; i <= elt; ++i) + { + if (ai->CastSpell(SpellArcane[(i+LastSpellArcane)%elt]), pTarget) + { + LastSpellArcane = (i+LastSpellArcane)%elt; + break; + } + } + break; + } + case MageFrost: + { + static uint32 SpellFrost[] = {FROSTFIRE_BOLT, FROSTBOLT, FROSTBOLT, ICE_LANCE, FROSTBOLT, + FROSTBOLT, CONE_OF_COLD, FROSTBOLT, FROSTBOLT, ICE_LANCE, FROST_NOVA, BLIZZARD}; + static uint32 elt = sizeof(SpellFrost)/sizeof(uint32); + + for (uint32 i = 1; i <= elt; ++i) + { + if (ai->CastSpell(SpellFrost[(i+LastSpellFrost)%elt]), pTarget) + { + LastSpellFrost = (i+LastSpellFrost)%elt; + break; + } + } + break; + } + } + + if (BuffPlayer()) + return; +} + +void PlayerbotMageAI::DoNonCombatActions() +{ + PlayerbotAI* ai = GetAI(); + Player* m_bot = GetPlayerBot(); + Player* m_master = ai->GetLeader(); + + switch (m_bot->getRole()) + { + case MageFire: + { + if (!ai->CastAura(MOLTEN_ARMOR, m_bot)) + if (!ai->CastAura(ICE_ARMOR, m_bot)) + if (ai->CastAura(FROST_ARMOR, m_bot)) + return; + break; + } + case MageArcane: + { + if (!ai->CastAura(MAGE_ARMOR, m_bot)) + if (!ai->CastAura(ICE_ARMOR, m_bot)) + if (ai->CastAura(FROST_ARMOR, m_bot)) + return; + break; + } + case MageFrost: + { + if (!ai->CastAura(ICE_ARMOR, m_bot)) + if (ai->CastAura(FROST_ARMOR, m_bot)) + return; + break; + } + } + + if (BuffPlayer()) + return; +} + +bool PlayerbotMageAI::BuffPlayer() +{ + PlayerbotAI* ai = GetAI(); + Player* m_bot = GetPlayerBot(); + Player* m_master = ai->GetLeader(); + + if (m_bot == m_master) + { + if (!ai->Buff(DALARAN_INTELLECT, m_bot)) + if (ai->Buff(ARCANE_INTELLECT, m_bot)) + return true; + + Unit* buffTarget = m_bot->SelectRandomPlayerToBuffHim(10.0f); + if (buffTarget && buffTarget->getPowerType()==POWER_MANA) + if (!ai->Buff(DALARAN_INTELLECT, buffTarget)) + if (ai->Buff(ARCANE_INTELLECT, buffTarget)) + return true; + } + else + { + if (!ai->Buff(DALARAN_INTELLECT, m_bot)) + if (ai->Buff(ARCANE_INTELLECT, m_bot)) + return true; + + if (m_master->isAlive() && m_master->getPowerType()==POWER_MANA) + { + if (!ai->Buff(DALARAN_INTELLECT, m_master)) + if (ai->Buff(ARCANE_INTELLECT, m_master)) + return true; + } + + if (m_bot->GetGroup()) + { + GroupReference *ref = NULL; + ref = m_bot->GetGroup()->GetFirstMember(); + while (ref) + { + Player* pl = ref->getSource(); + if (pl!=m_bot && pl!=m_master && pl->isAlive() && !pl->duel && pl->getPowerType()==POWER_MANA) + if (!ai->Buff(DALARAN_INTELLECT, pl)) + if (ai->Buff(ARCANE_INTELLECT, pl)) + return true; + ref = ref->next(); + } + ref = m_bot->GetGroup()->GetFirstMember(); + while (ref) + { + Player* pl = ref->getSource(); + if (pl!=m_bot && pl!=m_master && pl->isAlive() && !pl->duel && pl->GetPet() && pl->GetPet()->getPowerType()==POWER_MANA) + if (!ai->Buff(DALARAN_INTELLECT, pl->GetPet())) + if (ai->Buff(ARCANE_INTELLECT, pl->GetPet())) + return true; + ref = ref->next(); + } + } + } + return false; +} diff --git a/src/game/playerbot/PlayerbotMageAI.h b/src/game/playerbot/PlayerbotMageAI.h new file mode 100644 index 000000000..26ef74720 --- /dev/null +++ b/src/game/playerbot/PlayerbotMageAI.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PLAYERBOTMAGEAI_H +#define _PLAYERBOTMAGEAI_H + +#include "PlayerbotClassAI.h" + +enum MageSpells +{ + AMPLIFY_MAGIC_1 = 1008, + ARCANE_BARRAGE_1 = 44425, + ARCANE_BLAST_1 = 30451, + ARCANE_BRILLIANCE_1 = 23028, + ARCANE_EXPLOSION_1 = 1449, + ARCANE_INTELLECT_1 = 1459, + ARCANE_MISSILES_1 = 5143, + ARCANE_POWER_1 = 12042, + BLAST_WAVE_1 = 11113, + BLINK_1 = 1953, + BLIZZARD_1 = 10, + COLD_SNAP_1 = 11958, + COMBUSTION_1 = 11129, + CONE_OF_COLD_1 = 120, + CONJURE_FOOD_1 = 587, + CONJURE_MANA_GEM_1 = 759, + CONJURE_REFRESHMENT_1 = 42955, + CONJURE_WATER_1 = 5504, + COUNTERSPELL_1 = 2139, + DALARAN_BRILLIANCE_1 = 61316, + DALARAN_INTELLECT_1 = 61024, + DAMPEN_MAGIC_1 = 604, + DEEP_FREEZE_1 = 44572, + DRAGONS_BREATH_1 = 31661, + EVOCATION_1 = 12051, + FIRE_BLAST_1 = 2136, + FIRE_WARD_1 = 543, + FIREBALL_1 = 133, + FLAMESTRIKE_1 = 2120, + FOCUS_MAGIC_1 = 54646, + FROST_ARMOR_1 = 168, + FROST_NOVA_1 = 122, + FROST_WARD_1 = 6143, + FROSTBOLT_1 = 116, + FROSTFIRE_BOLT_1 = 44614, + ICE_ARMOR_1 = 7302, + ICE_BARRIER_1 = 11426, + ICE_BLOCK_1 = 45438, + ICE_LANCE_1 = 30455, + ICY_VEINS_1 = 12472, + INVISIBILITY_1 = 66, + LIVING_BOMB_1 = 44457, + MAGE_ARMOR_1 = 6117, + MANA_SHIELD_1 = 1463, + MIRROR_IMAGE_1 = 55342, + MOLTEN_ARMOR_1 = 30482, + PRESENCE_OF_MIND_1 = 12043, + PYROBLAST_1 = 11366, + REMOVE_CURSE_MAGE_1 = 475, + RITUAL_OF_REFRESHMENT_1 = 43987, + SCORCH_1 = 2948, + SLOW_1 = 31589, + SLOW_FALL_1 = 130, + SPELLSTEAL_1 = 30449, + SUMMON_WATER_ELEMENTAL_1 = 31687 +}; +//class Player; + +class MANGOS_DLL_SPEC PlayerbotMageAI : PlayerbotClassAI +{ +public: + PlayerbotMageAI(Player* const bot, PlayerbotAI* const ai); + virtual ~PlayerbotMageAI(); + + // all combat actions go here + void DoNextCombatManeuver(Unit*); + + // all non combat actions go here, ex buffs, heals, rezzes + void DoNonCombatActions(); + + // buff a specific player, usually a real PC who is not in group + bool BuffPlayer(); + + void InitSpells(PlayerbotAI* const ai); + +private: + // ARCANE + uint32 ARCANE_MISSILES, + ARCANE_EXPLOSION, + COUNTERSPELL, + SLOW, + ARCANE_BARRAGE, + ARCANE_BLAST, + MIRROR_IMAGE, + ARCANE_POWER; + + // FIRE + uint32 FIREBALL, + FIRE_BLAST, + FLAMESTRIKE, + SCORCH, + PYROBLAST, + BLAST_WAVE, + COMBUSTION, + DRAGONS_BREATH, + LIVING_BOMB, + FROSTFIRE_BOLT, + FIRE_WARD; + + // FROST + uint32 DEEP_FREEZE, + FROSTBOLT, + FROST_NOVA, + BLIZZARD, + ICY_VEINS, + CONE_OF_COLD, + ICE_BARRIER, + SUMMON_WATER_ELEMENTAL, + ICE_LANCE, + FROST_WARD, + ICE_BLOCK, + COLD_SNAP; + + // buffs + uint32 FROST_ARMOR, + ICE_ARMOR, + MAGE_ARMOR, + MOLTEN_ARMOR, + ARCANE_INTELLECT, + ARCANE_BRILLIANCE, + DALARAN_INTELLECT, + DALARAN_BRILLIANCE, + MANA_SHIELD, + DAMPEN_MAGIC, + AMPLIFY_MAGIC; + + // first aid + uint32 RECENTLY_BANDAGED; + + // racial + uint32 ARCANE_TORRENT, + GIFT_OF_THE_NAARU, + STONEFORM, + ESCAPE_ARTIST, + EVERY_MAN_FOR_HIMSELF, + SHADOWMELD, + BLOOD_FURY, + WAR_STOMP, + BERSERKING, + WILL_OF_THE_FORSAKEN; + + uint32 CONJURE_WATER, + CONJURE_FOOD; + + uint32 LastSpellFrost, LastSpellFire, LastSpellArcane; +}; + +#endif diff --git a/src/game/playerbot/PlayerbotMgr.cpp b/src/game/playerbot/PlayerbotMgr.cpp new file mode 100644 index 000000000..0896bf59f --- /dev/null +++ b/src/game/playerbot/PlayerbotMgr.cpp @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Config/Config.h" +#include "../Player.h" +#include "PlayerbotAI.h" +#include "PlayerbotMgr.h" +#include "WorldPacket.h" +#include "../Chat.h" +#include "../ObjectMgr.h" +#include "../GossipDef.h" +#include "../Chat.h" +#include "../Language.h" +#include "../Guild.h" +#include "../ArenaTeam.h" + +class LoginQueryHolder; +class CharacterHandler; + +PlayerbotMgr::PlayerbotMgr() +{ +} + +PlayerbotMgr::~PlayerbotMgr() +{ +} + +void PlayerbotMgr::UpdateAI(const uint32 p_time) {} + +void PlayerbotMgr::HandleMasterIncomingPacket(const WorldPacket& packet) +{ + switch (packet.GetOpcode()) + { + // handle emotes from the master + //case CMSG_EMOTE: + case CMSG_TEXT_EMOTE: + { + WorldPacket p(packet); + p.rpos(0); // reset reader + uint32 emoteNum; + p >> emoteNum; + switch (emoteNum) + { + case TEXTEMOTE_BOW: + { + // Buff anyone who bows before me. Useful for players not in bot's group + // How do I get correct target??? + //Player* const pPlayer = GetPlayerBot(m_master->GetSelection()); + //if (pPlayer->GetPlayerbotAI()->GetClassAI()) + // pPlayer->GetPlayerbotAI()->GetClassAI()->BuffPlayer(pPlayer); + return; + } + /* + case TEXTEMOTE_BONK: + { + Player* const pPlayer = GetPlayerBot(m_master->GetSelection()); + if (!pPlayer || !pPlayer->GetPlayerbotAI()) + return; + PlayerbotAI* const pBot = pPlayer->GetPlayerbotAI(); + + ChatHandler ch(m_master); + { + + + << " m_ignoreAIUpdatesUntilTime: " << pBot->m_ignoreAIUpdatesUntilTime; + ch.SendSysMessage(out.str().c_str()); + } + { + + + << " m_TimeDoneDrinking: " << pBot->m_TimeDoneDrinking; + ch.SendSysMessage(out.str().c_str()); + } + { + + + ch.SendSysMessage(out.str().c_str()); + } + { + + + ch.SendSysMessage(out.str().c_str()); + } + { + + bool tradeActive = (pBot->GetPlayer()->GetTrader()) ? true : false; + + ch.SendSysMessage(out.str().c_str()); + } + { + + + ch.SendSysMessage(out.str().c_str()); + } + return; + } + */ + + case TEXTEMOTE_EAT: + case TEXTEMOTE_DRINK: + { + if (!m_master->GetGroup()) + return; + + for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* const bot = itr->getSource(); + bot->GetPlayerbotAI()->Feast(); + } + return; + } + + // emote to attack selected target + case TEXTEMOTE_POINT: + { + ObjectGuid attackOnGuid = m_master->GetSelectionGuid(); + if (attackOnGuid.IsEmpty()) + return; + + Unit* thingToAttack = ObjectAccessor::GetUnit(*m_master, attackOnGuid); + if (!thingToAttack) return; + + Player *bot = 0; + if (!m_master->GetGroup()) + return; + + for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) + { + bot = itr->getSource(); + if (!bot->IsFriendlyTo(thingToAttack) && bot->IsWithinLOSInMap(thingToAttack)) + bot->GetPlayerbotAI()->GetCombatTarget(thingToAttack); + } + return; + } + + // emote to stay + case TEXTEMOTE_STAND: + { + Player* const bot = GetPlayerBot(m_master->GetSelectionGuid().GetRawValue()); + if (bot) + bot->GetPlayerbotAI()->SetMovementTarget(); + else + { + if (!m_master->GetGroup()) + return; + + for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* const bot = itr->getSource(); + bot->GetPlayerbotAI()->SetMovementTarget(); + } + } + return; + } + + // 324 is the followme emote (not defined in enum) + // if master has bot selected then only bot follows, else all bots follow + case 324: + case TEXTEMOTE_WAVE: + { + Player* const bot = GetPlayerBot(m_master->GetSelectionGuid().GetRawValue()); + if (bot) + bot->GetPlayerbotAI()->SetMovementTarget(m_master); + else + { + if (!m_master->GetGroup()) + return; + + for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* const bot = itr->getSource(); + bot->GetPlayerbotAI()->SetMovementTarget(m_master); + } + } + return; + } + } + + return; + } /* EMOTE ends here */ + + case CMSG_GAMEOBJ_USE: // not sure if we still need this one + case CMSG_GAMEOBJ_REPORT_USE: + { + WorldPacket p(packet); + p.rpos(0); // reset reader + uint64 objGUID; + p >> objGUID; + + GameObject *obj = m_master->GetMap()->GetGameObject(objGUID); + if (!obj) + return; + + if (!m_master->GetGroup()) + return; + + for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* const bot = itr->getSource(); + + if (obj->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER) + bot->GetPlayerbotAI()->TurnInQuests(obj); + // add other go types here, i.e.: + // GAMEOBJECT_TYPE_CHEST - loot quest items of chest + } + + return; + } + break; + + // if master talks to an NPC + case CMSG_GOSSIP_HELLO: + case CMSG_QUESTGIVER_HELLO: + { + WorldPacket p(packet); + p.rpos(0); // reset reader + uint64 npcGUID; + p >> npcGUID; + + WorldObject* pNpc = m_master->GetMap()->GetWorldObject(npcGUID); + if (!pNpc) + return; + + if (!m_master->GetGroup()) + return; + + // for all master's bots + for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* const bot = itr->getSource(); + bot->GetPlayerbotAI()->TurnInQuests(pNpc); + } + + return; + } + + // if master accepts a quest, bots should also try to accept quest + case CMSG_QUESTGIVER_ACCEPT_QUEST: + { + WorldPacket p(packet); + p.rpos(0); // reset reader + uint64 guid; + uint32 quest; + p >> guid >> quest; + Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest); + if (qInfo) + { + if (!m_master->GetGroup()) + return; + + for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* const bot = itr->getSource(); + + if (bot->GetQuestStatus(quest) == QUEST_STATUS_COMPLETE) + { + } + else if (!bot->CanTakeQuest(qInfo, false)) + { + } + else if (!bot->SatisfyQuestLog(false)) + { + } + else if (!bot->CanAddQuest(qInfo, false)) + { + } + else + { + p.rpos(0); // reset reader + bot->GetSession()->HandleQuestgiverAcceptQuestOpcode(p); + } + } + } + return; + } + case CMSG_LOOT_ROLL: + { + + WorldPacket p(packet); //WorldPacket packet for CMSG_LOOT_ROLL, (8+4+1) + uint64 Guid; + uint32 NumberOfPlayers; + uint8 rollType; + p.rpos(0); //reset packet pointer + p >> Guid; //guid of the item rolled + p >> NumberOfPlayers; //number of players invited to roll + p >> rollType; //need,greed or pass on roll + + if (!m_master->GetGroup()) + return; + + for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) + { + uint32 choice = urand(0, 3); //returns 0,1,2 or 3 + + Player* const bot = itr->getSource(); + if(!bot) + return; + + Group* group = bot->GetGroup(); + if (!group) + return; + + group->CountRollVote(bot, Guid, NumberOfPlayers, RollVote(choice)); + + switch (choice) + { + case ROLL_NEED: + bot->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED, 1); + break; + case ROLL_GREED: + bot->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED, 1); + break; + } + } + return; + } + case CMSG_REPAIR_ITEM: + { + + WorldPacket p(packet); // WorldPacket packet for CMSG_REPAIR_ITEM, (8+8+1) + + sLog.outDebug("PlayerbotMgr: CMSG_REPAIR_ITEM"); + + ObjectGuid npcGUID; + uint64 itemGUID; + uint8 guildBank; + + p.rpos(0); //reset packet pointer + p >> npcGUID; + p >> itemGUID; // Not used for bot but necessary opcode data retrieval + p >> guildBank; // Flagged if guild repair selected + + if (!m_master->GetGroup()) + return; + + for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* const bot = itr->getSource(); + if (!bot) + return; + + Group* group = bot->GetGroup(); // check if bot is a member of group + if (!group) + return; + + Creature *unit = bot->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_REPAIR); + if (!unit) // Check if NPC can repair bot or not + { + sLog.outDebug("PlayerbotMgr: HandleRepairItemOpcode - Unit (GUID: %s) not found or you can't interact with him.", npcGUID.GetString().c_str()); + return; + } + + // remove fake death + if (bot->hasUnitState(UNIT_STAT_DIED)) + bot->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + + // reputation discount + float discountMod = bot->GetReputationPriceDiscount(unit); + + uint32 TotalCost = 0; + if (itemGUID) // Handle redundant feature (repair individual item) for bot + { + sLog.outDebug("ITEM: Repair single item is not applicable for %s", bot->GetName()); + continue; + } + else // Handle feature (repair all items) for bot + { + sLog.outDebug("ITEM: Repair all items, npcGUID = %s", npcGUID.GetString().c_str()); + + TotalCost = bot->DurabilityRepairAll(true, discountMod, guildBank > 0 ? true : false); + } + if (guildBank) // Handle guild repair + { + uint32 GuildId = bot->GetGuildId(); + if (!GuildId) + return; + Guild *pGuild = sObjectMgr.GetGuildById(GuildId); + if (!pGuild) + return; + pGuild->LogBankEvent(GUILD_BANK_LOG_REPAIR_MONEY, 0, bot->GetGUIDLow(), TotalCost); + pGuild->SendMoneyInfo(bot->GetSession(), bot->GetGUIDLow()); + } + + } + return; + } + case CMSG_SPIRIT_HEALER_ACTIVATE: + { + // sLog.outDebug("SpiritHealer is resurrecting the Player %s",m_master->GetName()); + if (!m_master->GetGroup()) + return; + + for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* const bot = itr->getSource(); + Group *grp = bot->GetGroup(); + if (grp) + grp->RemoveMember(bot->GetGUID(), 1); + } + return; + } + } +} + +void PlayerbotMgr::HandleMasterOutgoingPacket(const WorldPacket& packet) +{ +} + +void PlayerbotMgr::Stay() +{ +} + +void PlayerbotMgr::LogoutPlayerBot(uint64 guid) +{ + Player* bot = GetPlayerBot(guid); + if (bot) + { + for (uint8 i = 0; i < MAX_ARENA_SLOT; ++i) + { + uint32 a_id = bot->GetArenaTeamId(i); + if (a_id==0) + continue; + + ArenaTeam *at = sObjectMgr.GetArenaTeamById(a_id); + if (!at) + continue; + + if (at->DisbandNoSave(bot)) + delete at; + } + + if (bot->GetGroup()) + bot->RemoveFromGroup(); + + WorldSession * botWorldSessionPtr = bot->GetSession(); + botWorldSessionPtr->LogoutPlayer(true); // this will delete the bot Player object and PlayerbotAI object + delete botWorldSessionPtr; // finally delete the bot's WorldSession + } +} + +Player* PlayerbotMgr::GetPlayerBot(uint64 playerGuid) const +{ + HashMapHolder < Player > ::MapType& m = sObjectAccessor.GetPlayers(); + for (HashMapHolder < Player > ::MapType::const_iterator itr = m.begin(); itr != m.end(); ++itr) + { + Player* const bot = itr->second; + if (bot && bot->GetGUID() == playerGuid) + return bot; + } + return NULL; +} + +void PlayerbotMgr::OnBotLogin(Player * const bot) +{ + PlayerbotAI* ai = bot->GetPlayerbotAI(); + if (!ai) + { + PlayerbotAI* ai = new PlayerbotAI(this, bot); + if (ai) + bot->SetPlayerbotAI(ai); + } + ai = bot->GetPlayerbotAI(); + + for (uint8 i = 0; i < MAX_ARENA_SLOT; ++i) + { + uint32 a_id = bot->GetArenaTeamId(i); + if (a_id==0) + continue; + + ArenaTeam *at = sObjectMgr.GetArenaTeamById(a_id); + if (!at) + continue; + + at->DelMember(bot->GetGUID()); + } + + if (bot->GetGroup()) + bot->RemoveFromGroup(); + + bot->GiveLevel(bot->GetLevelAtLoading()); + bot->GMStartup(); + bot->SetHealth(bot->GetMaxHealth()); + bot->SetPower(bot->getPowerType(), bot->GetMaxPower(bot->getPowerType())); +} diff --git a/src/game/playerbot/PlayerbotMgr.h b/src/game/playerbot/PlayerbotMgr.h new file mode 100644 index 000000000..718997c80 --- /dev/null +++ b/src/game/playerbot/PlayerbotMgr.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PLAYERBOTMGR_H +#define _PLAYERBOTMGR_H + +#include "Common.h" + +class WorldPacket; +class Player; +class Unit; +class Object; +class Item; +class PlayerbotClassAI; + +class MANGOS_DLL_SPEC PlayerbotMgr +{ +public: + PlayerbotMgr(); + virtual ~PlayerbotMgr(); + + // This is called from Unit.cpp and is called every second (I think) + void UpdateAI(const uint32 p_time); + + // This is called whenever the master sends a packet to the server. + // These packets can be viewed, but not edited. + // It allows bot creators to craft AI in response to a master's actions. + // For a list of opcodes that can be caught see Opcodes.cpp (CMSG_* opcodes only) + // Notice: that this is static which means it is called once for all bots of the master. + void HandleMasterIncomingPacket(const WorldPacket& packet); + void HandleMasterOutgoingPacket(const WorldPacket& packet); + + static void AddAllBots(); + + void LogoutPlayerBot(uint64 guid); + Player* GetPlayerBot (uint64 guid) const; + Player* GetLeader() const { return m_master; }; + void SetLeader(Player* pl) { m_master = pl; }; + + void LogoutAllBots(); + void OnBotLogin(Player * const bot); + void Stay(); + +protected: + Player* m_master; +}; + +#endif diff --git a/src/game/playerbot/PlayerbotPaladinAI.cpp b/src/game/playerbot/PlayerbotPaladinAI.cpp new file mode 100644 index 000000000..4a871b960 --- /dev/null +++ b/src/game/playerbot/PlayerbotPaladinAI.cpp @@ -0,0 +1,540 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PlayerbotPaladinAI.h" +#include "PlayerbotMgr.h" + +class PlayerbotAI; + +PlayerbotPaladinAI::PlayerbotPaladinAI(Player* const bot, PlayerbotAI* const ai): PlayerbotClassAI(bot, ai) +{ + InitSpells(ai); +} + +void PlayerbotPaladinAI::InitSpells(PlayerbotAI* const ai) +{ + RETRIBUTION_AURA = ai->initSpell(RETRIBUTION_AURA_1); + CRUSADER_AURA = ai->initSpell(CRUSADER_AURA_1); + CRUSADER_STRIKE = ai->initSpell(CRUSADER_STRIKE_1); + SEAL_OF_COMMAND = ai->initSpell(SEAL_OF_COMMAND_1); + JUDGEMENT_OF_LIGHT = ai->initSpell(JUDGEMENT_OF_LIGHT_1); + JUDGEMENT_OF_WISDOM = ai->initSpell(JUDGEMENT_OF_WISDOM_1); + JUDGEMENT_OF_JUSTICE = ai->initSpell(JUDGEMENT_OF_JUSTICE_1); + DIVINE_STORM = ai->initSpell(DIVINE_STORM_1); + BLESSING_OF_MIGHT = ai->initSpell(BLESSING_OF_MIGHT_1); + GREATER_BLESSING_OF_MIGHT = ai->initSpell(GREATER_BLESSING_OF_MIGHT_1); + HAMMER_OF_WRATH = ai->initSpell(HAMMER_OF_WRATH_1); + FLASH_OF_LIGHT = ai->initSpell(FLASH_OF_LIGHT_1); // Holy + HOLY_LIGHT = ai->initSpell(HOLY_LIGHT_1); + HOLY_SHOCK = ai->initSpell(HOLY_SHOCK_1); + HOLY_WRATH = ai->initSpell(HOLY_WRATH_1); + DIVINE_FAVOR = ai->initSpell(DIVINE_FAVOR_1); + CONCENTRATION_AURA = ai->initSpell(CONCENTRATION_AURA_1); + BLESSING_OF_WISDOM = ai->initSpell(BLESSING_OF_WISDOM_1); + GREATER_BLESSING_OF_WISDOM = ai->initSpell(GREATER_BLESSING_OF_WISDOM_1); + CONSECRATION = ai->initSpell(CONSECRATION_1); + AVENGING_WRATH = ai->initSpell(AVENGING_WRATH_1); + LAY_ON_HANDS = ai->initSpell(LAY_ON_HANDS_1); + EXORCISM = ai->initSpell(EXORCISM_1); + SACRED_SHIELD = ai->initSpell(SACRED_SHIELD_1); + DIVINE_PLEA = ai->initSpell(DIVINE_PLEA_1); + BLESSING_OF_KINGS = ai->initSpell(BLESSING_OF_KINGS_1); + GREATER_BLESSING_OF_KINGS = ai->initSpell(GREATER_BLESSING_OF_KINGS_1); + BLESSING_OF_SANCTUARY = ai->initSpell(BLESSING_OF_SANCTUARY_1); + GREATER_BLESSING_OF_SANCTUARY = ai->initSpell(GREATER_BLESSING_OF_SANCTUARY_1); + HAMMER_OF_JUSTICE = ai->initSpell(HAMMER_OF_JUSTICE_1); + RIGHTEOUS_FURY = ai->initSpell(RIGHTEOUS_FURY_1); + RIGHTEOUS_DEFENSE = ai->initSpell(RIGHTEOUS_DEFENSE_1); + SHADOW_RESISTANCE_AURA = ai->initSpell(SHADOW_RESISTANCE_AURA_1); + DEVOTION_AURA = ai->initSpell(DEVOTION_AURA_1); + FIRE_RESISTANCE_AURA = ai->initSpell(FIRE_RESISTANCE_AURA_1); + FROST_RESISTANCE_AURA = ai->initSpell(FROST_RESISTANCE_AURA_1); + HAND_OF_PROTECTION = ai->initSpell(HAND_OF_PROTECTION_1); + DIVINE_PROTECTION = ai->initSpell(DIVINE_PROTECTION_1); + DIVINE_INTERVENTION = ai->initSpell(DIVINE_INTERVENTION_1); + DIVINE_SACRIFICE = ai->initSpell(DIVINE_SACRIFICE_1); + DIVINE_SHIELD = ai->initSpell(DIVINE_SHIELD_1); + HOLY_SHIELD = ai->initSpell(HOLY_SHIELD_1); + AVENGERS_SHIELD = ai->initSpell(AVENGERS_SHIELD_1); + HAND_OF_SACRIFICE = ai->initSpell(HAND_OF_SACRIFICE_1); + SHIELD_OF_RIGHTEOUSNESS = ai->initSpell(SHIELD_OF_RIGHTEOUSNESS_1); + REDEMPTION = ai->initSpell(REDEMPTION_1); + + // Warrior auras + DEFENSIVE_STANCE = 71; //Def Stance + BERSERKER_STANCE = 2458; //Ber Stance + BATTLE_STANCE = 2457; //Bat Stance + + FORBEARANCE = 25771; // cannot be protected + + RECENTLY_BANDAGED = 11196; // first aid check + + // racial + ARCANE_TORRENT = ai->initSpell(ARCANE_TORRENT_MANA_CLASSES); + GIFT_OF_THE_NAARU = ai->initSpell(GIFT_OF_THE_NAARU_PALADIN); // draenei + STONEFORM = ai->initSpell(STONEFORM_ALL); // dwarf + EVERY_MAN_FOR_HIMSELF = ai->initSpell(EVERY_MAN_FOR_HIMSELF_ALL); // human +} + +PlayerbotPaladinAI::~PlayerbotPaladinAI() {} + +bool PlayerbotPaladinAI::HealTarget(Unit *target) +{ + PlayerbotAI* ai = GetAI(); + uint8 hp = target->GetHealth() * 100 / target->GetMaxHealth(); + + if (hp < 25 && ai->CastSpell(LAY_ON_HANDS, target)) + return true; + + if (hp < 30 && ai->CastSpell(FLASH_OF_LIGHT, target)) + return true; + + if (hp < 35 && ai->CastSpell(HOLY_SHOCK, target)) + return true; + + if (hp < 40 && ai->CastSpell(HOLY_LIGHT, target)) + return true; + + return false; +} // end HealTarget + +void PlayerbotPaladinAI::DoNextCombatManeuver(Unit *pTarget) +{ + Unit* pVictim = pTarget->getVictim(); + + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + // damage spells + ai->SetInFront(pTarget); + Group *m_group = m_bot->GetGroup(); + float dist = m_bot->GetDistance(pTarget); + + + //Shield master if low hp. + uint32 masterHP = m_master->GetHealth() * 100 / m_master->GetMaxHealth(); + + if (m_master->isAlive()) + if (masterHP < 25 && HAND_OF_PROTECTION > 0 && !m_master->HasAura(FORBEARANCE, EFFECT_INDEX_0) && !m_master->HasAura(HAND_OF_PROTECTION, EFFECT_INDEX_0) && !m_master->HasAura(DIVINE_PROTECTION, EFFECT_INDEX_0) && !m_master->HasAura(DIVINE_SHIELD, EFFECT_INDEX_0)) + ai->CastSpell(HAND_OF_PROTECTION, m_master); + + // heal group inside combat, but do not heal if tank + if (m_group && pVictim != m_bot) // possible tank + { + Group::MemberSlotList const& groupSlot = m_group->GetMemberSlots(); + for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) + { + Player *m_groupMember = sObjectMgr.GetPlayer(itr->guid); + if (!m_groupMember || !m_groupMember->isAlive()) + continue; + + uint32 memberHP = m_groupMember->GetHealth() * 100 / m_groupMember->GetMaxHealth(); + if (memberHP < 40 && ai->GetManaPercent() >= 40) // do not heal bots without plenty of mana for master & self + if (HealTarget(m_groupMember)) + return; + } + } + + if (RIGHTEOUS_FURY > 0 && !m_bot->HasAura(RIGHTEOUS_FURY, EFFECT_INDEX_0)) + ai->CastSpell (RIGHTEOUS_FURY, m_bot); + + if (SHADOW_RESISTANCE_AURA > 0 && !m_bot->HasAura(SHADOW_RESISTANCE_AURA, EFFECT_INDEX_0) && pTarget->getClass() == CLASS_WARLOCK) + ai->CastSpell (SHADOW_RESISTANCE_AURA, m_bot); + + if (DEVOTION_AURA > 0 && !m_bot->HasAura(DEVOTION_AURA, EFFECT_INDEX_0) && pTarget->getClass() == CLASS_WARRIOR) + ai->CastSpell (DEVOTION_AURA, m_bot); + + if (FIRE_RESISTANCE_AURA > 0 && !m_bot->HasAura(FIRE_RESISTANCE_AURA, EFFECT_INDEX_0) && pTarget->getClass() == CLASS_MAGE) + ai->CastSpell (FIRE_RESISTANCE_AURA, m_bot); + + if (RETRIBUTION_AURA > 0 && !m_bot->HasAura(RETRIBUTION_AURA, EFFECT_INDEX_0) && pTarget->getClass() == CLASS_PRIEST) + ai->CastSpell (RETRIBUTION_AURA, m_bot); + + if (DEVOTION_AURA > 0 && !m_bot->HasAura(DEVOTION_AURA, EFFECT_INDEX_0) && pTarget->getClass() == CLASS_SHAMAN) + ai->CastSpell (DEVOTION_AURA, m_bot); + + if (DEVOTION_AURA > 0 && !m_bot->HasAura(DEVOTION_AURA, EFFECT_INDEX_0) && pTarget->getClass() == CLASS_ROGUE) + ai->CastSpell (DEVOTION_AURA, m_bot); + + if (DEVOTION_AURA > 0 && !m_bot->HasAura(DEVOTION_AURA, EFFECT_INDEX_0) && pTarget->getClass() == CLASS_PALADIN) + ai->CastSpell (DEVOTION_AURA, m_bot); + + if (ai->GetHealthPercent() <= 40 || m_master->GetHealth() <= m_master->GetMaxHealth() * 0.4) + SpellSequence = Healing; + else + SpellSequence = Combat; + + switch (SpellSequence) + { + case Combat: + if (JUDGEMENT_OF_LIGHT > 0 && !pTarget->HasAura(JUDGEMENT_OF_LIGHT, EFFECT_INDEX_0) && CombatCounter < 1 && ai->GetManaPercent() >= 5) + { + ai->CastSpell (JUDGEMENT_OF_LIGHT, pTarget); + + CombatCounter++; + break; + } + else if (SEAL_OF_COMMAND > 0 && !m_bot->HasAura(SEAL_OF_COMMAND, EFFECT_INDEX_0) && CombatCounter < 2 && ai->GetManaPercent() >= 14) + { + ai->CastSpell (SEAL_OF_COMMAND, m_bot); + + CombatCounter++; + break; + } + else if (HAMMER_OF_JUSTICE > 0 && !pTarget->HasAura(HAMMER_OF_JUSTICE, EFFECT_INDEX_0) && CombatCounter < 3 && ai->GetManaPercent() >= 3) + { + ai->CastSpell (HAMMER_OF_JUSTICE, pTarget); + + CombatCounter++; + break; + } + else if (CRUSADER_STRIKE > 0 && CombatCounter < 4 && ai->GetManaPercent() >= 5) + { + ai->CastSpell (CRUSADER_STRIKE, pTarget); + + CombatCounter++; + break; + } + else if (AVENGING_WRATH > 0 && CombatCounter < 5 && !m_bot->HasAura(AVENGING_WRATH, EFFECT_INDEX_0) && ai->GetManaPercent() >= 8) + { + ai->CastSpell (AVENGING_WRATH, m_bot); + + CombatCounter++; + break; + } + else if (SACRED_SHIELD > 0 && CombatCounter < 6 && pVictim == m_bot && ai->GetHealthPercent() < 70 && !m_bot->HasAura(SACRED_SHIELD, EFFECT_INDEX_0) && ai->GetManaPercent() >= 12) + { + ai->CastSpell (SACRED_SHIELD, m_bot); + + CombatCounter++; + break; + } + else if (DIVINE_STORM > 0 && CombatCounter < 7 && dist <= ATTACK_DISTANCE && ai->GetManaPercent() >= 12) + { + ai->CastSpell (DIVINE_STORM, pTarget); + + CombatCounter++; + break; + } + else if (HAMMER_OF_WRATH > 0 && CombatCounter < 8 && pTarget->GetHealth() < pTarget->GetMaxHealth() * 0.20 && ai->GetManaPercent() >= 14) + { + ai->CastSpell (HAMMER_OF_WRATH, pTarget); + + CombatCounter++; + break; + } + else if (HOLY_WRATH > 0 && CombatCounter < 9 && dist <= ATTACK_DISTANCE && ai->GetManaPercent() >= 24) + { + ai->CastSpell (HOLY_WRATH, pTarget); + + CombatCounter++; + break; + } + else if (HAND_OF_SACRIFICE > 0 && pVictim == m_master && !m_master->HasAura(HAND_OF_SACRIFICE, EFFECT_INDEX_0) && CombatCounter < 10 && ai->GetManaPercent() >= 6) + { + ai->CastSpell (HAND_OF_SACRIFICE, m_master); + + CombatCounter++; + break; + } + else if (DIVINE_PROTECTION > 0 && pVictim == m_bot && !m_bot->HasAura(FORBEARANCE, EFFECT_INDEX_0) && ai->GetHealthPercent() < 30 && CombatCounter < 11 && ai->GetManaPercent() >= 3) + { + ai->CastSpell (DIVINE_PROTECTION, m_bot); + + CombatCounter++; + break; + } + else if (RIGHTEOUS_DEFENSE > 0 && pVictim != m_bot && ai->GetHealthPercent() > 70 && CombatCounter < 12) + { + ai->CastSpell (RIGHTEOUS_DEFENSE, pTarget); + + CombatCounter++; + break; + } + else if (DIVINE_PLEA > 0 && !m_bot->HasAura(DIVINE_PLEA, EFFECT_INDEX_0) && ai->GetManaPercent() < 50 && CombatCounter < 13) + { + ai->CastSpell (DIVINE_PLEA, m_bot); + + CombatCounter++; + break; + } + else if (DIVINE_FAVOR > 0 && !m_bot->HasAura(DIVINE_FAVOR, EFFECT_INDEX_0) && CombatCounter < 14) + { + ai->CastSpell (DIVINE_FAVOR, m_bot); + + CombatCounter++; + break; + } + else if (CombatCounter > 15) + { + CombatCounter = 0; + + break; + } + else + { + CombatCounter = 0; + + break; + } + + case Healing: + if (ai->GetHealthPercent() <= 40) + { + HealTarget (m_bot); + + break; + } + if (masterHP <= 40) + { + HealTarget (m_master); + + break; + } + else + { + CombatCounter = 0; + + break; + } + } + + + if (AVENGING_WRATH > 0 && !m_bot->HasAura(AVENGING_WRATH, EFFECT_INDEX_0) && ai->GetManaPercent() >= 8) + ai->CastSpell(AVENGING_WRATH, m_bot); + + if (DIVINE_SHIELD > 0 && ai->GetHealthPercent() < 30 && pVictim == m_bot && !m_bot->HasAura(FORBEARANCE, EFFECT_INDEX_0) && !m_bot->HasAura(DIVINE_SHIELD, EFFECT_INDEX_0) && ai->GetManaPercent() >= 3) + ai->CastSpell(DIVINE_SHIELD, m_bot); + + if (DIVINE_SACRIFICE > 0 && ai->GetHealthPercent() > 50 && pVictim != m_bot && !m_bot->HasAura(DIVINE_SACRIFICE, EFFECT_INDEX_0)) + ai->CastSpell(DIVINE_SACRIFICE, m_bot); +} + +void PlayerbotPaladinAI::DoNonCombatActions() +{ + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + // Buff myself + BuffPlayer(m_bot); + + // Buff master + BuffPlayer(m_master); + + // mana check + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + + Item* pItem = ai->FindDrink(); + Item* fItem = ai->FindBandage(); + + if (pItem != NULL && ai->GetManaPercent() < 40) + { + + ai->UseItem(pItem); + return; + } + + // hp check original + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + + pItem = ai->FindFood(); + + if (pItem != NULL && ai->GetHealthPercent() < 40) + { + + ai->UseItem(pItem); + return; + } + else if (pItem == NULL && fItem != NULL && !m_bot->HasAura(RECENTLY_BANDAGED, EFFECT_INDEX_0) && ai->GetHealthPercent() < 70) + { + + ai->UseItem(fItem); + return; + } + + // heal and buff group + if (m_master->GetGroup()) + { + Group::MemberSlotList const& groupSlot = m_master->GetGroup()->GetMemberSlots(); + for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) + { + Player *tPlayer = sObjectMgr.GetPlayer(itr->guid); + if (!tPlayer) + continue; + + if (!tPlayer->isAlive()) + { + if (ai->CastSpell(REDEMPTION, tPlayer)) + { + std::string msg = "Resurrecting "; + msg += tPlayer->GetName(); + m_bot->Say(msg, LANG_UNIVERSAL); + return; + } + else + continue; + } + + if (HealTarget(tPlayer)) + return; + + if (tPlayer != m_bot && tPlayer != m_master) + if (BuffPlayer(tPlayer)) + return; + } + } +} + +bool PlayerbotPaladinAI::BuffPlayer(Player* target) +{ + PlayerbotAI * ai = GetAI(); + uint8 SPELL_BLESSING = 2; // See SpellSpecific enum in SpellMgr.h + + Pet * pet = target->GetPet(); + bool petCanBeBlessed = false; + if (pet) + petCanBeBlessed = ai->CanReceiveSpecificSpell(SPELL_BLESSING, pet); + + if (!ai->CanReceiveSpecificSpell(SPELL_BLESSING, target) && !petCanBeBlessed) + return false; + + switch (target->getClass()) + { + case CLASS_DRUID: + case CLASS_SHAMAN: + case CLASS_PALADIN: + if (Bless(BLESSING_OF_MIGHT, target)) + return true; + if (Bless(BLESSING_OF_KINGS, target)) + return true; + if (Bless(BLESSING_OF_WISDOM, target)) + return true; + if (Bless(BLESSING_OF_SANCTUARY, target)) + return true; + else + return false; + case CLASS_DEATH_KNIGHT: + case CLASS_HUNTER: + if (petCanBeBlessed) + if (Bless(BLESSING_OF_MIGHT, pet)) + return true; + if (Bless(BLESSING_OF_KINGS, pet)) + return true; + if (Bless(BLESSING_OF_SANCTUARY, pet)) + return true; + case CLASS_ROGUE: + case CLASS_WARRIOR: + if (Bless(BLESSING_OF_MIGHT, target)) + return true; + if (Bless(BLESSING_OF_KINGS, target)) + return true; + if (Bless(BLESSING_OF_SANCTUARY, target)) + return true; + else + return false; + case CLASS_WARLOCK: + if (petCanBeBlessed) + { + if (pet->getPowerType() == POWER_MANA) + { + if (Bless(BLESSING_OF_WISDOM, pet)) + return true; + } + else + { + if (Bless(BLESSING_OF_MIGHT, pet)) + return true; + } + if (Bless(BLESSING_OF_KINGS, pet)) + return true; + if (Bless(BLESSING_OF_SANCTUARY, pet)) + return true; + } + case CLASS_PRIEST: + case CLASS_MAGE: + if (Bless(BLESSING_OF_WISDOM, target)) + return true; + if (Bless(BLESSING_OF_KINGS, target)) + return true; + if (Bless(BLESSING_OF_SANCTUARY, target)) + return true; + else + return false; + } + return false; +} + +bool PlayerbotPaladinAI::Bless(uint32 spellId, Unit *target) +{ + if (spellId == 0) + return false; + + PlayerbotAI * ai = GetAI(); + + if (spellId == BLESSING_OF_MIGHT) + { + if (GREATER_BLESSING_OF_MIGHT && ai->HasSpellReagents(GREATER_BLESSING_OF_MIGHT) && ai->Buff(GREATER_BLESSING_OF_MIGHT, target)) + return true; + else + return ai->Buff(spellId, target); + } + else if (spellId == BLESSING_OF_WISDOM) + { + if (GREATER_BLESSING_OF_WISDOM && ai->HasSpellReagents(GREATER_BLESSING_OF_WISDOM) && ai->Buff(GREATER_BLESSING_OF_WISDOM, target)) + return true; + else + return ai->Buff(spellId, target); + } + else if (spellId == BLESSING_OF_KINGS) + { + if (GREATER_BLESSING_OF_KINGS && ai->HasSpellReagents(GREATER_BLESSING_OF_KINGS) && ai->Buff(GREATER_BLESSING_OF_KINGS, target)) + return true; + else + return ai->Buff(spellId, target); + } + else if (spellId == BLESSING_OF_SANCTUARY) + { + if (GREATER_BLESSING_OF_SANCTUARY && ai->HasSpellReagents(GREATER_BLESSING_OF_SANCTUARY) && ai->Buff(GREATER_BLESSING_OF_SANCTUARY, target)) + return true; + else + return ai->Buff(spellId, target); + } + + // Should not happen, but let it be here + return false; +} diff --git a/src/game/playerbot/PlayerbotPaladinAI.h b/src/game/playerbot/PlayerbotPaladinAI.h new file mode 100644 index 000000000..b4b8fd550 --- /dev/null +++ b/src/game/playerbot/PlayerbotPaladinAI.h @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PLAYERBOTPALADINAI_H +#define _PLAYERBOTPALADINAI_H + +#include "PlayerbotClassAI.h" + +enum +{ + Combat, + Healing +}; + +enum PaladinSpells +{ + AURA_MASTERY_1 = 31821, + AVENGERS_SHIELD_1 = 31935, + AVENGING_WRATH_1 = 31884, + BEACON_OF_LIGHT_1 = 53563, + BLESSING_OF_KINGS_1 = 20217, + BLESSING_OF_MIGHT_1 = 19740, + BLESSING_OF_SANCTUARY_1 = 20911, + BLESSING_OF_WISDOM_1 = 19742, + CLEANSE_1 = 4987, + CONCENTRATION_AURA_1 = 19746, + CONSECRATION_1 = 26573, + CRUSADER_AURA_1 = 32223, + CRUSADER_STRIKE_1 = 35395, + DEVOTION_AURA_1 = 465, + DIVINE_FAVOR_1 = 20216, + DIVINE_ILLUMINATION_1 = 31842, + DIVINE_INTERVENTION_1 = 19752, + DIVINE_PLEA_1 = 54428, + DIVINE_PROTECTION_1 = 498, + DIVINE_SACRIFICE_1 = 64205, + DIVINE_SHIELD_1 = 642, + DIVINE_STORM_1 = 53385, + EXORCISM_1 = 879, + FIRE_RESISTANCE_AURA_1 = 19891, + FLASH_OF_LIGHT_1 = 19750, + FROST_RESISTANCE_AURA_1 = 19888, + GREATER_BLESSING_OF_KINGS_1 = 25898, + GREATER_BLESSING_OF_MIGHT_1 = 25782, + GREATER_BLESSING_OF_SANCTUARY_1 = 25899, + GREATER_BLESSING_OF_WISDOM_1 = 25894, + HAMMER_OF_JUSTICE_1 = 853, + HAMMER_OF_THE_RIGHTEOUS_1 = 53595, + HAMMER_OF_WRATH_1 = 24275, + HAND_OF_FREEDOM_1 = 1044, + HAND_OF_PROTECTION_1 = 1022, + HAND_OF_RECKONING_1 = 62124, + HAND_OF_SACRIFICE_1 = 6940, + HAND_OF_SALVATION_1 = 1038, + HOLY_LIGHT_1 = 635, + HOLY_SHIELD_1 = 20925, + HOLY_SHOCK_1 = 20473, + HOLY_WRATH_1 = 2812, + JUDGEMENT_OF_JUSTICE_1 = 53407, + JUDGEMENT_OF_LIGHT_1 = 20271, + JUDGEMENT_OF_WISDOM_1 = 53408, + LAY_ON_HANDS_1 = 633, + PURIFY_1 = 1152, + REDEMPTION_1 = 7328, + REPENTANCE_1 = 20066, + RETRIBUTION_AURA_1 = 7294, + RIGHTEOUS_DEFENSE_1 = 31789, + RIGHTEOUS_FURY_1 = 25780, + SACRED_SHIELD_1 = 53601, + SEAL_OF_COMMAND_1 = 20375, + SEAL_OF_CORRUPTION = 53736, + SEAL_OF_JUSTICE_1 = 20164, + SEAL_OF_LIGHT_1 = 20165, + SEAL_OF_RIGHTEOUSNESS_1 = 20154, + SEAL_OF_VENGEANCE = 31801, + SEAL_OF_WISDOM_1 = 20166, + SENSE_UNDEAD_1 = 5502, + SHADOW_RESISTANCE_AURA_1 = 19876, + SHIELD_OF_RIGHTEOUSNESS_1 = 53600, + TURN_EVIL_1 = 10326 +}; +//class Player; + +class MANGOS_DLL_SPEC PlayerbotPaladinAI : PlayerbotClassAI +{ +public: + PlayerbotPaladinAI(Player* const bot, PlayerbotAI* const ai); + virtual ~PlayerbotPaladinAI(); + + // all combat actions go here + void DoNextCombatManeuver(Unit*); + + // all non combat actions go here, ex buffs, heals, rezzes + void DoNonCombatActions(); + + // buff a specific player, usually a real PC who is not in group + bool BuffPlayer(Player *target); + + void InitSpells(PlayerbotAI* const ai); + +private: + // Heals the target based off its hps + bool HealTarget (Unit *target); + // Bless target using greater blessing if possible + bool Bless(uint32 spellId, Unit *target); + + // Retribution + uint32 RETRIBUTION_AURA, + SEAL_OF_COMMAND, + JUDGEMENT_OF_LIGHT, + JUDGEMENT_OF_WISDOM, + GREATER_BLESSING_OF_WISDOM, + GREATER_BLESSING_OF_MIGHT, + BLESSING_OF_WISDOM, + BLESSING_OF_MIGHT, + HAMMER_OF_JUSTICE, + RIGHTEOUS_FURY, + CRUSADER_AURA, + CRUSADER_STRIKE, + AVENGING_WRATH, + DIVINE_STORM, + JUDGEMENT_OF_JUSTICE; + + // Holy + uint32 FLASH_OF_LIGHT, + HOLY_LIGHT, + DIVINE_SHIELD, + HAMMER_OF_WRATH, + CONSECRATION, + CONCENTRATION_AURA, + DIVINE_FAVOR, + SACRED_SHIELD, + HOLY_SHOCK, + HOLY_WRATH, + LAY_ON_HANDS, + EXORCISM, + REDEMPTION, + DIVINE_PLEA; + + // Protection + uint32 GREATER_BLESSING_OF_KINGS, + BLESSING_OF_KINGS, + HAND_OF_PROTECTION, + SHADOW_RESISTANCE_AURA, + DEVOTION_AURA, + FIRE_RESISTANCE_AURA, + FROST_RESISTANCE_AURA, + DEFENSIVE_STANCE, + BERSERKER_STANCE, + BATTLE_STANCE, + DIVINE_SACRIFICE, + DIVINE_PROTECTION, + DIVINE_INTERVENTION, + HOLY_SHIELD, + AVENGERS_SHIELD, + RIGHTEOUS_DEFENSE, + BLESSING_OF_SANCTUARY, + GREATER_BLESSING_OF_SANCTUARY, + HAND_OF_SACRIFICE, + SHIELD_OF_RIGHTEOUSNESS; + + // cannot be protected + uint32 FORBEARANCE; + + // first aid + uint32 RECENTLY_BANDAGED; + + // racial + uint32 ARCANE_TORRENT, + GIFT_OF_THE_NAARU, + STONEFORM, + ESCAPE_ARTIST, + EVERY_MAN_FOR_HIMSELF, + SHADOWMELD, + BLOOD_FURY, + WAR_STOMP, + BERSERKING, + WILL_OF_THE_FORSAKEN; + + uint32 SpellSequence, CombatCounter, HealCounter; +}; + +#endif diff --git a/src/game/playerbot/PlayerbotPriestAI.cpp b/src/game/playerbot/PlayerbotPriestAI.cpp new file mode 100644 index 000000000..8b37de251 --- /dev/null +++ b/src/game/playerbot/PlayerbotPriestAI.cpp @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PlayerbotPriestAI.h" + +class PlayerbotAI; + +PlayerbotPriestAI::PlayerbotPriestAI(Player* const bot, PlayerbotAI* const ai): PlayerbotClassAI(bot, ai) +{ + InitSpells(ai); +} + +void PlayerbotPriestAI::InitSpells(PlayerbotAI* const ai) +{ + RENEW = ai->initSpell(RENEW_1); + HEAL = ai->initSpell(HEAL_1); + LESSER_HEAL = ai->initSpell(LESSER_HEAL_1); + GREATER_HEAL = ai->initSpell(GREATER_HEAL_1); + FLASH_HEAL = ai->initSpell(FLASH_HEAL_1); + RESURRECTION = ai->initSpell(RESURRECTION_1); + SMITE = ai->initSpell(SMITE_1); + MANA_BURN = ai->initSpell(MANA_BURN_1); + HOLY_NOVA = ai->initSpell(HOLY_NOVA_1); + HOLY_FIRE = ai->initSpell(HOLY_FIRE_1); + DESPERATE_PRAYER = ai->initSpell(DESPERATE_PRAYER_1); + PRAYER_OF_HEALING = ai->initSpell(PRAYER_OF_HEALING_1); + CIRCLE_OF_HEALING = ai->initSpell(CIRCLE_OF_HEALING_1); + BINDING_HEAL = ai->initSpell(BINDING_HEAL_1); + PRAYER_OF_MENDING = ai->initSpell(PRAYER_OF_MENDING_1); + + // SHADOW + FADE = ai->initSpell(FADE_1); + SHADOW_WORD_PAIN = ai->initSpell(SHADOW_WORD_PAIN_1); + MIND_BLAST = ai->initSpell(MIND_BLAST_1); + SCREAM = ai->initSpell(PSYCHIC_SCREAM_1); + MIND_FLAY = ai->initSpell(MIND_FLAY_1); + DEVOURING_PLAGUE = ai->initSpell(DEVOURING_PLAGUE_1); + SHADOW_PROTECTION = ai->initSpell(SHADOW_PROTECTION_1); + VAMPIRIC_TOUCH = ai->initSpell(VAMPIRIC_TOUCH_1); + PRAYER_OF_SHADOW_PROTECTION = ai->initSpell(PRAYER_OF_SHADOW_PROTECTION_1); + SHADOWFIEND = ai->initSpell(SHADOWFIEND_1); + MIND_SEAR = ai->initSpell(MIND_SEAR_1); + + // DISCIPLINE + PENANCE = ai->initSpell(PENANCE_1); + INNER_FIRE = ai->initSpell(INNER_FIRE_1); + POWER_WORD_SHIELD = ai->initSpell(POWER_WORD_SHIELD_1); + POWER_WORD_FORTITUDE = ai->initSpell(POWER_WORD_FORTITUDE_1); + PRAYER_OF_FORTITUDE = ai->initSpell(PRAYER_OF_FORTITUDE_1); + FEAR_WARD = ai->initSpell(FEAR_WARD_1); + DIVINE_SPIRIT = ai->initSpell(DIVINE_SPIRIT_1); + PRAYER_OF_SPIRIT = ai->initSpell(PRAYER_OF_SPIRIT_1); + MASS_DISPEL = ai->initSpell(MASS_DISPEL_1); + POWER_INFUSION = ai->initSpell(POWER_INFUSION_1); + INNER_FOCUS = ai->initSpell(INNER_FOCUS_1); + + RECENTLY_BANDAGED = 11196; // first aid check + + // racial + ARCANE_TORRENT = ai->initSpell(ARCANE_TORRENT_MANA_CLASSES); + GIFT_OF_THE_NAARU = ai->initSpell(GIFT_OF_THE_NAARU_PRIEST); // draenei + STONEFORM = ai->initSpell(STONEFORM_ALL); // dwarf + EVERY_MAN_FOR_HIMSELF = ai->initSpell(EVERY_MAN_FOR_HIMSELF_ALL); // human + SHADOWMELD = ai->initSpell(SHADOWMELD_ALL); + BERSERKING = ai->initSpell(BERSERKING_ALL); // troll + WILL_OF_THE_FORSAKEN = ai->initSpell(WILL_OF_THE_FORSAKEN_ALL); // undead +} + +PlayerbotPriestAI::~PlayerbotPriestAI() {} + +bool PlayerbotPriestAI::HealTarget(Unit* target) +{ + PlayerbotAI* ai = GetAI(); + uint8 hp = target->GetHealth() * 100 / target->GetMaxHealth(); + + if (hp >= 80) + return false; + + if (hp < 25 && FLASH_HEAL && ai->CastSpell(FLASH_HEAL, target)) + return true; + else if (hp < 30 && GREATER_HEAL > 0 && ai->CastSpell(GREATER_HEAL, target)) + return true; + else if (hp < 33 && BINDING_HEAL > 0 && ai->CastSpell(BINDING_HEAL, target)) + return true; + else if (hp < 40 && PRAYER_OF_HEALING > 0 && ai->CastSpell(PRAYER_OF_HEALING, target)) + return true; + else if (hp < 50 && CIRCLE_OF_HEALING > 0 && ai->CastSpell(CIRCLE_OF_HEALING, target)) + return true; + else if (hp < 60 && HEAL > 0 && ai->CastSpell(HEAL, target)) + return true; + else if (hp < 80 && RENEW > 0 && ai->CastSpell(RENEW, target)) + return true; + else + return false; +} // end HealTarget + +void PlayerbotPriestAI::DoNextCombatManeuver(Unit *pTarget) +{ + Unit* pVictim = pTarget->getVictim(); + PlayerbotAI* ai = GetAI(); + if (!ai) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + //ai->SetMovementTarget(PlayerbotAI::MOVEMENT_FOLLOW, m_master); // dont want to melee mob + + Player *m_bot = GetPlayerBot(); + Group *m_group = m_bot->GetGroup(); + + // Heal myself + if (ai->GetHealthPercent() < 15 && FADE > 0 && !m_bot->HasAura(FADE, EFFECT_INDEX_0)) + { + + ai->CastSpell(FADE, m_bot); + } + else if (ai->GetHealthPercent() < 25 && POWER_WORD_SHIELD > 0 && !m_bot->HasAura(POWER_WORD_SHIELD, EFFECT_INDEX_0)) + { + + ai->CastSpell(POWER_WORD_SHIELD); + } + else if (ai->GetHealthPercent() < 35 && DESPERATE_PRAYER > 0) + { + + ai->CastSpell(DESPERATE_PRAYER, m_bot); + } + else if (ai->GetHealthPercent() < 80) + HealTarget (m_bot); + + // Heal master + uint32 masterHP = m_master->GetHealth() * 100 / m_master->GetMaxHealth(); + if (m_master->isAlive()) + { + if (masterHP < 25 && POWER_WORD_SHIELD > 0 && !m_master->HasAura(POWER_WORD_SHIELD, EFFECT_INDEX_0)) + ai->CastSpell(POWER_WORD_SHIELD, m_master); + else if (masterHP < 80) + HealTarget (m_master); + } + + // Heal group + if (m_group) + { + Group::MemberSlotList const& groupSlot = m_group->GetMemberSlots(); + for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) + { + Player *m_groupMember = sObjectMgr.GetPlayer(itr->guid); + if (!m_groupMember || !m_groupMember->isAlive()) + continue; + + uint32 memberHP = m_groupMember->GetHealth() * 100 / m_groupMember->GetMaxHealth(); + if (memberHP < 25) + HealTarget(m_groupMember); + } + } + + // Damage Spells + ai->SetInFront(pTarget); + float dist = m_bot->GetDistance(pTarget); + + switch (SpellSequence) + { + case SPELL_HOLY: + if (SMITE > 0 && LastSpellHoly < 1 && !pTarget->HasAura(SMITE, EFFECT_INDEX_0) && ai->GetManaPercent() >= 17) + { + ai->CastSpell(SMITE, pTarget); + SpellSequence = SPELL_SHADOWMAGIC; + LastSpellHoly = LastSpellHoly + 1; + break; + } + else if (MANA_BURN > 0 && LastSpellHoly < 2 && pTarget->GetPower(POWER_MANA) > 0 && ai->GetManaPercent() < 70 && ai->GetManaPercent() >= 14) + { + + ai->CastSpell(MANA_BURN, pTarget); + ai->SetIgnoreUpdateTime(3); + SpellSequence = SPELL_SHADOWMAGIC; + LastSpellHoly = LastSpellHoly + 1; + break; + } + else if (HOLY_NOVA > 0 && LastSpellHoly < 3 && dist <= ATTACK_DISTANCE && ai->GetManaPercent() >= 22) + { + + ai->CastSpell(HOLY_NOVA); + SpellSequence = SPELL_SHADOWMAGIC; + LastSpellHoly = LastSpellHoly + 1; + break; + } + else if (HOLY_FIRE > 0 && LastSpellHoly < 4 && !pTarget->HasAura(HOLY_FIRE, EFFECT_INDEX_0) && ai->GetManaPercent() >= 13) + { + + ai->CastSpell(HOLY_FIRE, pTarget); + SpellSequence = SPELL_SHADOWMAGIC; + LastSpellHoly = LastSpellHoly + 1; + break; + } + else if (PRAYER_OF_MENDING > 0 && LastSpellHoly < 5 && pVictim == m_master && m_master->GetHealth() <= m_master->GetMaxHealth() * 0.7 && !m_master->HasAura(PRAYER_OF_MENDING, EFFECT_INDEX_0) && ai->GetManaPercent() >= 15) + { + + ai->CastSpell(PRAYER_OF_MENDING, m_master); + SpellSequence = SPELL_SHADOWMAGIC; + LastSpellHoly = LastSpellHoly + 1; + break; + } + else if (LastSpellHoly > 6) + { + LastSpellHoly = 0; + SpellSequence = SPELL_SHADOWMAGIC; + break; + } + LastSpellHoly = LastSpellHoly + 1; + //SpellSequence = SPELL_SHADOWMAGIC; + //break; + + case SPELL_SHADOWMAGIC: + if (SHADOW_WORD_PAIN > 0 && LastSpellShadowMagic < 1 && !pTarget->HasAura(SHADOW_WORD_PAIN, EFFECT_INDEX_0) && ai->GetManaPercent() >= 25) + { + + ai->CastSpell(SHADOW_WORD_PAIN, pTarget); + SpellSequence = SPELL_DISCIPLINE; + LastSpellShadowMagic = LastSpellShadowMagic + 1; + break; + } + else if (MIND_BLAST > 0 && LastSpellShadowMagic < 2 && ai->GetManaPercent() >= 19) + { + + ai->CastSpell(MIND_BLAST, pTarget); + SpellSequence = SPELL_DISCIPLINE; + LastSpellShadowMagic = LastSpellShadowMagic + 1; + break; + } + else if (SCREAM > 0 && LastSpellShadowMagic < 3 && ai->GetManaPercent() >= 15) + { + + ai->CastSpell(SCREAM); + SpellSequence = SPELL_DISCIPLINE; + (LastSpellShadowMagic = LastSpellShadowMagic + 1); + break; + } + + else if (MIND_FLAY > 0 && LastSpellShadowMagic < 4 && !pTarget->HasAura(MIND_FLAY, EFFECT_INDEX_0) && ai->GetManaPercent() >= 10) + { + + ai->CastSpell(MIND_FLAY, pTarget); + ai->SetIgnoreUpdateTime(3); + SpellSequence = SPELL_DISCIPLINE; + LastSpellShadowMagic = LastSpellShadowMagic + 1; + break; + } + else if (DEVOURING_PLAGUE > 0 && LastSpellShadowMagic < 5 && !pTarget->HasAura(DEVOURING_PLAGUE, EFFECT_INDEX_0) && ai->GetManaPercent() >= 28) + { + ai->CastSpell(DEVOURING_PLAGUE, pTarget); + SpellSequence = SPELL_DISCIPLINE; + LastSpellShadowMagic = LastSpellShadowMagic + 1; + break; + } + else if (SHADOW_PROTECTION > 0 && LastSpellShadowMagic < 6 && ai->GetManaPercent() >= 60) + { + ai->CastSpell(SHADOW_PROTECTION, pTarget); + SpellSequence = SPELL_DISCIPLINE; + LastSpellShadowMagic = LastSpellShadowMagic + 1; + break; + } + else if (VAMPIRIC_TOUCH > 0 && LastSpellShadowMagic < 7 && !pTarget->HasAura(VAMPIRIC_TOUCH, EFFECT_INDEX_0) && ai->GetManaPercent() >= 18) + { + ai->CastSpell(VAMPIRIC_TOUCH, pTarget); + SpellSequence = SPELL_DISCIPLINE; + LastSpellShadowMagic = LastSpellShadowMagic + 1; + break; + } + else if (SHADOWFIEND > 0 && LastSpellShadowMagic < 8) + { + ai->CastSpell(SHADOWFIEND); + SpellSequence = SPELL_DISCIPLINE; + LastSpellShadowMagic = LastSpellShadowMagic + 1; + break; + } + else if (MIND_SEAR > 0 && LastSpellShadowMagic < 9 && ai->GetManaPercent() >= 28) + { + ai->CastSpell(MIND_SEAR, pTarget); + ai->SetIgnoreUpdateTime(5); + SpellSequence = SPELL_DISCIPLINE; + LastSpellShadowMagic = LastSpellShadowMagic + 1; + break; + } + else if (LastSpellShadowMagic > 10) + { + LastSpellShadowMagic = 0; + SpellSequence = SPELL_DISCIPLINE; + break; + } + LastSpellShadowMagic = LastSpellShadowMagic + 1; + //SpellSequence = SPELL_DISCIPLINE; + //break; + + case SPELL_DISCIPLINE: + if (FEAR_WARD > 0 && LastSpellDiscipline < 1 && ai->GetManaPercent() >= 3) + { + + ai->CastSpell(FEAR_WARD, m_master); + SpellSequence = SPELL_HOLY; + LastSpellDiscipline = LastSpellDiscipline + 1; + break; + } + else if (POWER_INFUSION > 0 && LastSpellDiscipline < 2 && ai->GetManaPercent() >= 16) + { + + ai->CastSpell(POWER_INFUSION, m_master); + SpellSequence = SPELL_HOLY; + LastSpellDiscipline = LastSpellDiscipline + 1; + break; + } + else if (MASS_DISPEL > 0 && LastSpellDiscipline < 3 && ai->GetManaPercent() >= 33) + { + + ai->CastSpell(MASS_DISPEL); + SpellSequence = SPELL_HOLY; + LastSpellDiscipline = LastSpellDiscipline + 1; + break; + } + else if (INNER_FOCUS > 0 && !m_bot->HasAura(INNER_FOCUS, EFFECT_INDEX_0) && LastSpellDiscipline < 4) + { + + ai->CastSpell(INNER_FOCUS, m_bot); + SpellSequence = SPELL_HOLY; + LastSpellDiscipline = LastSpellDiscipline + 1; + break; + } + else if (PENANCE > 0 && LastSpellDiscipline < 5 && ai->GetManaPercent() >= 16) + { + + ai->CastSpell(PENANCE); + SpellSequence = SPELL_HOLY; + LastSpellDiscipline = LastSpellDiscipline + 1; + break; + } + else if (LastSpellDiscipline > 6) + { + LastSpellDiscipline = 0; + SpellSequence = SPELL_HOLY; + break; + } + else + { + LastSpellDiscipline = LastSpellDiscipline + 1; + SpellSequence = SPELL_HOLY; + } + } +} // end DoNextCombatManeuver + +void PlayerbotPriestAI::DoNonCombatActions() +{ + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + SpellSequence = SPELL_HOLY; + + // selfbuff goes first + if (ai->SelfBuff(INNER_FIRE)) + return; + + // mana check + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + + Item* pItem = ai->FindDrink(); + Item* fItem = ai->FindBandage(); + + if (pItem != NULL && ai->GetManaPercent() < 30) + { + ai->UseItem(pItem); + return; + } + + // hp check + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + + pItem = ai->FindFood(); + + if (pItem != NULL && ai->GetHealthPercent() < 30) + { + ai->UseItem(pItem); + return; + } + else if (pItem == NULL && fItem != NULL && !m_bot->HasAura(RECENTLY_BANDAGED, EFFECT_INDEX_0) && ai->GetHealthPercent() < 70) + { + ai->UseItem(fItem); + return; + } + + // buff and heal master's group + if (m_master->GetGroup()) + { + // Buff master with group buffs + if (m_master->isAlive()) + { + if (PRAYER_OF_FORTITUDE && ai->HasSpellReagents(PRAYER_OF_FORTITUDE) && ai->Buff(PRAYER_OF_FORTITUDE, m_master)) + return; + + if (PRAYER_OF_SPIRIT && ai->HasSpellReagents(PRAYER_OF_SPIRIT) && ai->Buff(PRAYER_OF_SPIRIT, m_master)) + return; + } + + Group::MemberSlotList const& groupSlot = m_master->GetGroup()->GetMemberSlots(); + for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) + { + Player *tPlayer = sObjectMgr.GetPlayer(itr->guid); + if (!tPlayer || tPlayer == m_bot) + continue; + + // first rezz em + if (!tPlayer->isAlive()) + { + if (ai->CastSpell(RESURRECTION, tPlayer)) + { + std::string msg = "Resurrecting "; + msg += tPlayer->GetName(); + m_bot->Say(msg, LANG_UNIVERSAL); + return; + } + else + continue; + } + else + { + // buff and heal + if (BuffPlayer(tPlayer)) + return; + + if (HealTarget(tPlayer)) + return; + } + } + } + else + { + if (m_master->isAlive()) + { + if (BuffPlayer(m_master)) + return; + if (HealTarget(m_master)) + return; + } + else + ai->CastSpell(RESURRECTION, m_master); + } + + BuffPlayer(m_bot); +} // end DoNonCombatActions + +bool PlayerbotPriestAI::BuffPlayer(Player* target) +{ + PlayerbotAI * ai = GetAI(); + Pet * pet = target->GetPet(); + + if (pet && ai->Buff(POWER_WORD_FORTITUDE, pet)) + return true; + + if (ai->Buff(POWER_WORD_FORTITUDE, target)) + return true; + + if ((target->getClass() == CLASS_DRUID || target->getPowerType() == POWER_MANA) && ai->Buff(DIVINE_SPIRIT, target)) + return true; + + return false; +} diff --git a/src/game/playerbot/PlayerbotPriestAI.h b/src/game/playerbot/PlayerbotPriestAI.h new file mode 100644 index 000000000..a48851bff --- /dev/null +++ b/src/game/playerbot/PlayerbotPriestAI.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PLAYERBOTPRIESTAI_H +#define _PLAYERBOTPRIESTAI_H + +#include "PlayerbotClassAI.h" + +enum +{ + SPELL_HOLY, + SPELL_SHADOWMAGIC, + SPELL_DISCIPLINE +}; + +enum PriestSpells +{ + ABOLISH_DISEASE_1 = 552, + BINDING_HEAL_1 = 32546, + BLESSED_HEALING_1 = 70772, + CIRCLE_OF_HEALING_1 = 34861, + CURE_DISEASE_1 = 528, + DESPERATE_PRAYER_1 = 19236, + DEVOURING_PLAGUE_1 = 2944, + DISPEL_MAGIC_1 = 527, + DISPERSION_1 = 47585, + DIVINE_HYMN_1 = 64843, + DIVINE_SPIRIT_1 = 14752, + FADE_1 = 586, + FEAR_WARD_1 = 6346, + FLASH_HEAL_1 = 2061, + GREATER_HEAL_1 = 2060, + GUARDIAN_SPIRIT_1 = 47788, + HEAL_1 = 2054, + HOLY_FIRE_1 = 14914, + HOLY_NOVA_1 = 15237, + HYMN_OF_HOPE_1 = 64901, + INNER_FIRE_1 = 588, + INNER_FOCUS_1 = 14751, + LESSER_HEAL_1 = 2050, + LEVITATE_1 = 1706, + LIGHTWELL_1 = 724, + MANA_BURN_1 = 8129, + MASS_DISPEL_1 = 32375, + MIND_BLAST_1 = 8092, + MIND_CONTROL_1 = 605, + MIND_FLAY_1 = 15407, + MIND_SEAR_1 = 48045, + MIND_SOOTHE_1 = 453, + MIND_VISION_1 = 2096, + PAIN_SUPPRESSION_1 = 33206, + PENANCE_1 = 47540, + POWER_INFUSION_1 = 10060, + POWER_WORD_FORTITUDE_1 = 1243, + POWER_WORD_SHIELD_1 = 17, + PRAYER_OF_FORTITUDE_1 = 21562, + PRAYER_OF_HEALING_1 = 596, + PRAYER_OF_MENDING_1 = 33076, + PRAYER_OF_SHADOW_PROTECTION_1 = 27683, + PRAYER_OF_SPIRIT_1 = 27681, + PSYCHIC_HORROR_1 = 64044, + PSYCHIC_SCREAM_1 = 8122, + RENEW_1 = 139, + RESURRECTION_1 = 2006, + SHACKLE_UNDEAD_1 = 9484, + SHADOW_PROTECTION_1 = 976, + SHADOW_WORD_DEATH_1 = 32379, + SHADOW_WORD_PAIN_1 = 589, + SHADOWFIEND_1 = 34433, + SHADOWFORM_1 = 15473, + SILENCE_1 = 15487, + SMITE_1 = 585, + VAMPIRIC_EMBRACE_1 = 15286, + VAMPIRIC_TOUCH_1 = 34914 +}; +//class Player; + +class MANGOS_DLL_SPEC PlayerbotPriestAI : PlayerbotClassAI +{ +public: + PlayerbotPriestAI(Player* const bot, PlayerbotAI* const ai); + virtual ~PlayerbotPriestAI(); + + // all combat actions go here + void DoNextCombatManeuver(Unit*); + + // all non combat actions go here, ex buffs, heals, rezzes + void DoNonCombatActions(); + + // buff a specific player, usually a real PC who is not in group + bool BuffPlayer(Player *target); + + void InitSpells(PlayerbotAI* const ai); + +private: + // Heals the target based off its hps + bool HealTarget (Unit* target); + + // holy + uint32 BINDING_HEAL, + CIRCLE_OF_HEALING, + CLEARCASTING, + DESPERATE_PRAYER, + FLASH_HEAL, + GREATER_HEAL, + HEAL, + HOLY_FIRE, + HOLY_NOVA, + LESSER_HEAL, + MANA_BURN, + PRAYER_OF_HEALING, + PRAYER_OF_MENDING, + RENEW, + RESURRECTION, + SMITE; + + // shadowmagic + uint32 FADE, + SHADOW_WORD_PAIN, + MIND_BLAST, + SCREAM, + MIND_FLAY, + DEVOURING_PLAGUE, + SHADOW_PROTECTION, + VAMPIRIC_TOUCH, + PRAYER_OF_SHADOW_PROTECTION, + SHADOWFIEND, + MIND_SEAR; + + // discipline + uint32 POWER_WORD_SHIELD, + INNER_FIRE, + POWER_WORD_FORTITUDE, + PRAYER_OF_FORTITUDE, + FEAR_WARD, + POWER_INFUSION, + MASS_DISPEL, + PENANCE, + DIVINE_SPIRIT, + PRAYER_OF_SPIRIT, + INNER_FOCUS; + + // first aid + uint32 RECENTLY_BANDAGED; + + // racial + uint32 ARCANE_TORRENT, + GIFT_OF_THE_NAARU, + STONEFORM, + ESCAPE_ARTIST, + EVERY_MAN_FOR_HIMSELF, + SHADOWMELD, + WAR_STOMP, + BERSERKING, + WILL_OF_THE_FORSAKEN; + + uint32 SpellSequence, LastSpellHoly, LastSpellShadowMagic, LastSpellDiscipline; +}; + +#endif diff --git a/src/game/playerbot/PlayerbotRogueAI.cpp b/src/game/playerbot/PlayerbotRogueAI.cpp new file mode 100644 index 000000000..ce920189f --- /dev/null +++ b/src/game/playerbot/PlayerbotRogueAI.cpp @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PlayerbotRogueAI.h" +#include "PlayerbotMgr.h" + +class PlayerbotAI; + +PlayerbotRogueAI::PlayerbotRogueAI(Player* const bot, PlayerbotAI* const ai): PlayerbotClassAI(bot, ai) +{ + InitSpells(ai); +} + +void PlayerbotRogueAI::InitSpells(PlayerbotAI* const ai) +{ + SINISTER_STRIKE = ai->initSpell(SINISTER_STRIKE_1); + BACKSTAB = ai->initSpell(BACKSTAB_1); + KICK = ai->initSpell(KICK_1); + FEINT = ai->initSpell(FEINT_1); + FAN_OF_KNIVES = ai->initSpell(FAN_OF_KNIVES_1); + GOUGE = ai->initSpell(GOUGE_1); + SPRINT = ai->initSpell(SPRINT_1); + + SHADOWSTEP = ai->initSpell(SHADOWSTEP_1); + STEALTH = ai->initSpell(STEALTH_1); + VANISH = ai->initSpell(VANISH_1); + EVASION = ai->initSpell(EVASION_1); + CLOAK_OF_SHADOWS = ai->initSpell(CLOAK_OF_SHADOWS_1); + HEMORRHAGE = ai->initSpell(HEMORRHAGE_1); + GHOSTLY_STRIKE = ai->initSpell(GHOSTLY_STRIKE_1); + SHADOW_DANCE = ai->initSpell(SHADOW_DANCE_1); + BLIND = ai->initSpell(BLIND_1); + DISTRACT = ai->initSpell(DISTRACT_1); + PREPARATION = ai->initSpell(PREPARATION_1); + PREMEDITATION = ai->initSpell(PREMEDITATION_1); + PICK_POCKET = ai->initSpell(PICK_POCKET_1); + + EVISCERATE = ai->initSpell(EVISCERATE_1); + KIDNEY_SHOT = ai->initSpell(KIDNEY_SHOT_1); + SLICE_DICE = ai->initSpell(SLICE_AND_DICE_1); + GARROTE = ai->initSpell(GARROTE_1); + EXPOSE_ARMOR = ai->initSpell(EXPOSE_ARMOR_1); + RUPTURE = ai->initSpell(RUPTURE_1); + DISMANTLE = ai->initSpell(DISMANTLE_1); + CHEAP_SHOT = ai->initSpell(CHEAP_SHOT_1); + AMBUSH = ai->initSpell(AMBUSH_1); + MUTILATE = ai->initSpell(MUTILATE_1); + + RECENTLY_BANDAGED = 11196; // first aid check + // racial + ARCANE_TORRENT = ai->initSpell(ARCANE_TORRENT_ROGUE); + STONEFORM = ai->initSpell(STONEFORM_ALL); // dwarf + ESCAPE_ARTIST = ai->initSpell(ESCAPE_ARTIST_ALL); // gnome + EVERY_MAN_FOR_HIMSELF = ai->initSpell(EVERY_MAN_FOR_HIMSELF_ALL); // human + SHADOWMELD = ai->initSpell(SHADOWMELD_ALL); + BLOOD_FURY = ai->initSpell(BLOOD_FURY_MELEE_CLASSES); // orc + BERSERKING = ai->initSpell(BERSERKING_ALL); // troll + WILL_OF_THE_FORSAKEN = ai->initSpell(WILL_OF_THE_FORSAKEN_ALL); // undead +} + +PlayerbotRogueAI::~PlayerbotRogueAI() {} + +bool PlayerbotRogueAI::DoFirstCombatManeuver(Unit *pTarget) +{ + PlayerbotAI* ai = GetAI(); + Player * m_bot = GetPlayerBot(); + + if (STEALTH > 0 && !m_bot->HasAura(STEALTH, EFFECT_INDEX_0) && ai->CastSpell(STEALTH, m_bot)) + { + + m_bot->addUnitState(UNIT_STAT_CHASE); // ensure that the bot does not use MoveChase(), as this doesn't seem to work with STEALTH + + return true; + } + else if (m_bot->HasAura(STEALTH, EFFECT_INDEX_0)) + { + m_bot->GetMotionMaster()->MoveFollow(pTarget, 4.5f, m_bot->GetOrientation()); + return false; + } + return false; +} + +void PlayerbotRogueAI::DoNextCombatManeuver(Unit *pTarget) +{ + if (!pTarget) + return; + + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + ai->SetInFront(pTarget); + Unit* pVictim = pTarget->getVictim(); + float fTargetDist = m_bot->GetDistance(pTarget); + + // TODO: make this work better... + /*if (pVictim) + { + if( pVictim!=m_bot && !m_bot->hasUnitState(UNIT_STAT_FOLLOW) && !pTarget->isInBackInMap(m_bot,10) ) { + + m_bot->GetMotionMaster()->Clear( true ); + m_bot->GetMotionMaster()->MoveFollow( pTarget, 1, 2*M_PI ); + } + else if( pVictim==m_bot && m_bot->hasUnitState(UNIT_STAT_FOLLOW) ) + { + + m_bot->GetMotionMaster()->Clear( true ); + m_bot->GetMotionMaster()->MoveChase( pTarget ); + } + }*/ + + //Rouge like behaviour. ^^ +/* if (VANISH > 0 && m_master->isDead()) { //Causes the server to crash :( removed for now. + m_bot->AttackStop(); + m_bot->RemoveAllAttackers(); + ai->CastSpell(VANISH); + // m_bot->RemoveAllSpellCooldown(); + + }*/ + + // decide what to do: + if (pVictim == m_bot && CLOAK_OF_SHADOWS > 0 && pVictim->HasAura(SPELL_AURA_PERIODIC_DAMAGE) && !m_bot->HasAura(CLOAK_OF_SHADOWS, EFFECT_INDEX_0) && ai->CastSpell(CLOAK_OF_SHADOWS)) + { + + return; + } + else if (m_bot->HasAura(STEALTH, EFFECT_INDEX_0)) + SpellSequence = RogueSpeStealth; + else if (pTarget->IsNonMeleeSpellCasted(true)) + SpellSequence = RogueSpeSpellPreventing; + else if (pVictim == m_bot && ai->GetHealthPercent() < 40) + SpellSequence = RogueSpeThreat; + else + SpellSequence = RogueSpeCombat; + + // we fight in melee, target is not in range, skip the next part! + if (fTargetDist > ATTACK_DISTANCE) + return; + + + switch (SpellSequence) + { + case RogueSpeStealth: + + /*if (PICK_POCKET > 0 && ai->CastSpell(PICK_POCKET, pTarget) && ai->PickPocket(pTarget)) + + else */if (PREMEDITATION > 0 && ai->CastSpell(PREMEDITATION, pTarget)) + {} + else if (AMBUSH > 0 && ai->GetEnergyAmount() >= 60 && ai->CastSpell(AMBUSH, pTarget)) + {} + else if (CHEAP_SHOT > 0 && !pTarget->HasAura(CHEAP_SHOT, EFFECT_INDEX_0) && ai->GetEnergyAmount() >= 60 && ai->CastSpell(CHEAP_SHOT, pTarget)) + {} + else if (GARROTE > 0 && ai->GetEnergyAmount() >= 50 && ai->CastSpell(GARROTE, pTarget)) + {} + else + m_bot->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + break; + case RogueSpeThreat: + + if (GOUGE > 0 && ai->GetEnergyAmount() >= 45 && !pTarget->HasAura(GOUGE, EFFECT_INDEX_0) && ai->CastSpell(GOUGE, pTarget)) + {} + else if (EVASION > 0 && ai->GetHealthPercent() <= 35 && !m_bot->HasAura(EVASION, EFFECT_INDEX_0) && ai->CastSpell(EVASION)) + {} + else if (BLIND > 0 && ai->GetHealthPercent() <= 30 && !pTarget->HasAura(BLIND, EFFECT_INDEX_0) && ai->GetEnergyAmount() >= 30 && ai->CastSpell(BLIND, pTarget)) + {} + else if (FEINT > 0 && ai->GetHealthPercent() <= 25 && ai->GetEnergyAmount() >= 20 && ai->CastSpell(FEINT)) + {} + else if (VANISH > 0 && ai->GetHealthPercent() <= 20 && !m_bot->HasAura(FEINT, EFFECT_INDEX_0) && ai->CastSpell(VANISH)) + {} + else if (PREPARATION > 0 && ai->CastSpell(PREPARATION)) + {} + else if (m_bot->getRace() == RACE_NIGHTELF && ai->GetHealthPercent() <= 15 && !m_bot->HasAura(SHADOWMELD, EFFECT_INDEX_0) && ai->CastSpell(SHADOWMELD, m_bot)) + {} + break; + case RogueSpeSpellPreventing: + + if (KIDNEY_SHOT > 0 && ai->GetEnergyAmount() >= 25 && m_bot->GetComboPoints() >= 2 && ai->CastSpell(KIDNEY_SHOT, pTarget)) + {} + else if (KICK > 0 && ai->GetEnergyAmount() >= 25 && ai->CastSpell(KICK, pTarget)) + {} + break; + case RogueSpeCombat: + default: + + if (m_bot->GetComboPoints() <= 4) + { + if (SHADOW_DANCE > 0 && !m_bot->HasAura(SHADOW_DANCE, EFFECT_INDEX_0) && ai->CastSpell(SHADOW_DANCE, m_bot)) + {} + else if (CHEAP_SHOT > 0 && m_bot->HasAura(SHADOW_DANCE, EFFECT_INDEX_0) && !pTarget->HasAura(CHEAP_SHOT, EFFECT_INDEX_0) && ai->GetEnergyAmount() >= 60 && ai->CastSpell(CHEAP_SHOT, pTarget)) + {} + else if (AMBUSH > 0 && m_bot->HasAura(SHADOW_DANCE, EFFECT_INDEX_0) && ai->GetEnergyAmount() >= 60 && ai->CastSpell(AMBUSH, pTarget)) + {} + else if (GARROTE > 0 && m_bot->HasAura(SHADOW_DANCE, EFFECT_INDEX_0) && ai->GetEnergyAmount() >= 50 && ai->CastSpell(GARROTE, pTarget)) + {} + else if (BACKSTAB > 0 && pTarget->isInBackInMap(m_bot, 1) && ai->GetEnergyAmount() >= 60 && ai->CastSpell(BACKSTAB, pTarget)) + {} + else if (MUTILATE > 0 && ai->GetEnergyAmount() >= 60 && ai->CastSpell(MUTILATE, pTarget)) + {} + else if (SINISTER_STRIKE > 0 && ai->GetEnergyAmount() >= 45 && ai->CastSpell(SINISTER_STRIKE, pTarget)) + {} + else if (GHOSTLY_STRIKE > 0 && ai->GetEnergyAmount() >= 40 && ai->CastSpell(GHOSTLY_STRIKE, pTarget)) + {} + else if (HEMORRHAGE > 0 && ai->GetEnergyAmount() >= 35 && ai->CastSpell(HEMORRHAGE, pTarget)) + {} + else if (DISMANTLE > 0 && !pTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) && ai->GetEnergyAmount() >= 25 && ai->CastSpell(DISMANTLE, pTarget)) + {} + else if (SHADOWSTEP > 0 && ai->GetEnergyAmount() >= 10 && ai->CastSpell(SHADOWSTEP, pTarget)) + {} + else if (m_bot->getRace() == RACE_BLOODELF && !pTarget->HasAura(ARCANE_TORRENT, EFFECT_INDEX_0) && ai->CastSpell(ARCANE_TORRENT, pTarget)) + {} + else if (m_bot->getRace() == RACE_HUMAN && m_bot->hasUnitState(UNIT_STAT_STUNNED) || m_bot->HasAuraType(SPELL_AURA_MOD_FEAR) || m_bot->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED) || m_bot->HasAuraType(SPELL_AURA_MOD_CHARM) && ai->CastSpell(EVERY_MAN_FOR_HIMSELF, m_bot)) + {} + else if (m_bot->getRace() == RACE_UNDEAD && m_bot->HasAuraType(SPELL_AURA_MOD_FEAR) || m_bot->HasAuraType(SPELL_AURA_MOD_CHARM) && ai->CastSpell(WILL_OF_THE_FORSAKEN, m_bot)) + {} + else if (m_bot->getRace() == RACE_DWARF && m_bot->HasAuraState(AURA_STATE_DEADLY_POISON) && ai->CastSpell(STONEFORM, m_bot)) + {} + else if (m_bot->getRace() == RACE_GNOME && m_bot->hasUnitState(UNIT_STAT_STUNNED) || m_bot->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED) && ai->CastSpell(ESCAPE_ARTIST, m_bot)) + {} + else if (m_bot->getRace() == RACE_ORC && !m_bot->HasAura(BLOOD_FURY, EFFECT_INDEX_0) && ai->CastSpell(BLOOD_FURY, m_bot)) + {} + else if (m_bot->getRace() == RACE_TROLL && !m_bot->HasAura(BERSERKING, EFFECT_INDEX_0) && ai->CastSpell(BERSERKING, m_bot)) + {} + } + else + { + if (EVISCERATE > 0 && pTarget->getClass() == CLASS_ROGUE && ai->GetEnergyAmount() >= 35 && ai->CastSpell(EVISCERATE, pTarget)) + {} + else if (EVISCERATE > 0 && pTarget->getClass() == CLASS_DRUID && ai->GetEnergyAmount() >= 35 && ai->CastSpell(EVISCERATE, pTarget)) + {} + else if (KIDNEY_SHOT > 0 && pTarget->getClass() == CLASS_SHAMAN && ai->GetEnergyAmount() >= 25 && ai->CastSpell(KIDNEY_SHOT, pTarget)) + {} + else if (SLICE_DICE > 0 && pTarget->getClass() == CLASS_WARLOCK && ai->GetEnergyAmount() >= 25 && ai->CastSpell(SLICE_DICE, pTarget)) + {} + else if (SLICE_DICE > 0 && pTarget->getClass() == CLASS_HUNTER && ai->GetEnergyAmount() >= 25 && ai->CastSpell(SLICE_DICE, pTarget)) + {} + else if (EXPOSE_ARMOR > 0 && pTarget->getClass() == CLASS_WARRIOR && !pTarget->HasAura(EXPOSE_ARMOR, EFFECT_INDEX_0) && ai->GetEnergyAmount() >= 25 && ai->CastSpell(EXPOSE_ARMOR, pTarget)) + {} + else if (EXPOSE_ARMOR > 0 && pTarget->getClass() == CLASS_PALADIN && !pTarget->HasAura(EXPOSE_ARMOR, EFFECT_INDEX_0) && ai->GetEnergyAmount() >= 25 && ai->CastSpell(EXPOSE_ARMOR, pTarget)) + {} + else if (EXPOSE_ARMOR > 0 && pTarget->getClass() == CLASS_DEATH_KNIGHT && !pTarget->HasAura(EXPOSE_ARMOR, EFFECT_INDEX_0) && ai->GetEnergyAmount() >= 25 && ai->CastSpell(EXPOSE_ARMOR, pTarget)) + {} + else if (RUPTURE > 0 && pTarget->getClass() == CLASS_MAGE && ai->GetEnergyAmount() >= 25 && ai->CastSpell(RUPTURE, pTarget)) + {} + else if (RUPTURE > 0 && pTarget->getClass() == CLASS_PRIEST && ai->GetEnergyAmount() >= 25 && ai->CastSpell(RUPTURE, pTarget)) + {} + else if (EVISCERATE > 0 && ai->GetEnergyAmount() >= 35 && ai->CastSpell(EVISCERATE, pTarget)) + {} + } + break; + } + +} + +// end DoNextCombatManeuver + +void PlayerbotRogueAI::DoNonCombatActions() +{ + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + // remove stealth + if (m_bot->HasAura(STEALTH)) + m_bot->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + // hp check + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + + Item* pItem = ai->FindFood(); + Item* fItem = ai->FindBandage(); + + if (pItem != NULL && ai->GetHealthPercent() < 30) + { + + ai->UseItem(pItem); + return; + } + else if (pItem == NULL && fItem != NULL && !m_bot->HasAura(RECENTLY_BANDAGED, EFFECT_INDEX_0) && ai->GetHealthPercent() < 70) + { + + ai->UseItem(fItem); + return; + } + + // Search and apply poisons to weapons + // Mainhand ... + Item * poison, * weapon; + weapon = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); + if (weapon && weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0) + { + poison = ai->FindConsumable(INSTANT_POISON_DISPLAYID); + if (!poison) + poison = ai->FindConsumable(WOUND_POISON_DISPLAYID); + if (!poison) + poison = ai->FindConsumable(DEADLY_POISON_DISPLAYID); + if (poison) + { + ai->UseItem(poison, EQUIPMENT_SLOT_MAINHAND); + ai->SetIgnoreUpdateTime(5); + } + } + //... and offhand + weapon = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + if (weapon && weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0) + { + poison = ai->FindConsumable(DEADLY_POISON_DISPLAYID); + if (!poison) + poison = ai->FindConsumable(WOUND_POISON_DISPLAYID); + if (!poison) + poison = ai->FindConsumable(INSTANT_POISON_DISPLAYID); + if (poison) + { + ai->UseItem(poison, EQUIPMENT_SLOT_OFFHAND); + ai->SetIgnoreUpdateTime(5); + } + } + +} // end DoNonCombatActions diff --git a/src/game/playerbot/PlayerbotRogueAI.h b/src/game/playerbot/PlayerbotRogueAI.h new file mode 100644 index 000000000..66e5e8dfa --- /dev/null +++ b/src/game/playerbot/PlayerbotRogueAI.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PLAYERBOTROGUEAI_H +#define _PLAYERBOTROGUEAI_H + +#include "PlayerbotClassAI.h" + +enum +{ + RogueSpeCombat, + RogueSpeSpellPreventing, + RogueSpeThreat, + RogueSpeStealth +}; + +enum RoguePoisonDisplayId +{ + DEADLY_POISON_DISPLAYID = 13707, + INSTANT_POISON_DISPLAYID = 13710, + WOUND_POISON_DISPLAYID = 37278 +}; + +enum RogueSpells +{ + ADRENALINE_RUSH_1 = 13750, + AMBUSH_1 = 8676, + BACKSTAB_1 = 53, + BLADE_FLURRY_1 = 13877, + BLIND_1 = 2094, + CHEAP_SHOT_1 = 1833, + CLOAK_OF_SHADOWS_1 = 31224, + COLD_BLOOD_1 = 14177, + DEADLY_THROW_1 = 26679, + DISARM_TRAP_1 = 1842, + DISMANTLE_1 = 51722, + DISTRACT_1 = 1725, + ENVENOM_1 = 32645, + EVASION_1 = 5277, + EVISCERATE_1 = 2098, + EXPOSE_ARMOR_1 = 8647, + FAN_OF_KNIVES_1 = 51723, + FEINT_1 = 1966, + GARROTE_1 = 703, + GHOSTLY_STRIKE_1 = 14278, + GOUGE_1 = 1776, + HEMORRHAGE_1 = 16511, + HUNGER_FOR_BLOOD_1 = 51662, + KICK_1 = 1766, + KIDNEY_SHOT_1 = 408, + KILLING_SPREE_1 = 51690, + MUTILATE_1 = 1329, + PICK_LOCK_1 = 1804, + PICK_POCKET_1 = 921, + PREMEDITATION_1 = 14183, + PREPARATION_1 = 14185, + RIPOSTE_1 = 14251, + RUPTURE_1 = 1943, + SAP_1 = 6770, + SHADOW_DANCE_1 = 51713, + SHADOWSTEP_1 = 36554, + SHIV_1 = 5938, + SINISTER_STRIKE_1 = 1752, + SLICE_AND_DICE_1 = 5171, + SPRINT_1 = 2983, + STEALTH_1 = 1784, + TRICKS_OF_THE_TRADE_1 = 57934, + VANISH_1 = 1856 +}; +//class Player; + +class MANGOS_DLL_SPEC PlayerbotRogueAI : PlayerbotClassAI +{ +public: + PlayerbotRogueAI(Player* const bot, PlayerbotAI* const ai); + virtual ~PlayerbotRogueAI(); + + // all combat actions go here + bool DoFirstCombatManeuver(Unit*); + void DoNextCombatManeuver(Unit*); + + // all non combat actions go here, ex buffs, heals, rezzes + void DoNonCombatActions(); + + void InitSpells(PlayerbotAI* const ai); + +private: + // COMBAT + uint32 SINISTER_STRIKE, BACKSTAB, GOUGE, EVASION, SPRINT, KICK, FEINT, SHIV, FAN_OF_KNIVES; + + // SUBTLETY + uint32 SHADOWSTEP, STEALTH, VANISH, HEMORRHAGE, BLIND, SHADOW_DANCE, PICK_POCKET, CLOAK_OF_SHADOWS, TRICK_TRADE, CRIPPLING_POISON, DEADLY_POISON, MIND_NUMBING_POISON, GHOSTLY_STRIKE, DISTRACT, PREPARATION, PREMEDITATION; + + // ASSASSINATION + uint32 EVISCERATE, SLICE_DICE, GARROTE, EXPOSE_ARMOR, AMBUSH, RUPTURE, DISMANTLE, CHEAP_SHOT, KIDNEY_SHOT, MUTILATE, ENVENOM, DEADLY_THROW; + + // first aid + uint32 RECENTLY_BANDAGED; + + // racial + uint32 ARCANE_TORRENT, GIFT_OF_THE_NAARU, STONEFORM, ESCAPE_ARTIST, EVERY_MAN_FOR_HIMSELF, SHADOWMELD, BLOOD_FURY, WAR_STOMP, BERSERKING, WILL_OF_THE_FORSAKEN; + + uint32 SpellSequence, LastSpellCombat, LastSpellSubtlety, LastSpellAssassination, Aura; +}; + +#endif diff --git a/src/game/playerbot/PlayerbotShamanAI.cpp b/src/game/playerbot/PlayerbotShamanAI.cpp new file mode 100644 index 000000000..d30b47fd6 --- /dev/null +++ b/src/game/playerbot/PlayerbotShamanAI.cpp @@ -0,0 +1,547 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PlayerbotShamanAI.h" + +class PlayerbotAI; + +PlayerbotShamanAI::PlayerbotShamanAI(Player* const bot, PlayerbotAI* const ai): PlayerbotClassAI(bot, ai) +{ + InitSpells(ai); +} + +void PlayerbotShamanAI::InitSpells(PlayerbotAI* const ai) +{ + // restoration + CHAIN_HEAL = ai->initSpell(CHAIN_HEAL_1); + HEALING_WAVE = ai->initSpell(HEALING_WAVE_1); + LESSER_HEALING_WAVE = ai->initSpell(LESSER_HEALING_WAVE_1); + RIPTIDE = ai->initSpell(RIPTIDE_1); + ANCESTRAL_SPIRIT = ai->initSpell(ANCESTRAL_SPIRIT_1); + EARTH_SHIELD = ai->initSpell(EARTH_SHIELD_1); + WATER_SHIELD = ai->initSpell(WATER_SHIELD_1); + EARTHLIVING_WEAPON = ai->initSpell(EARTHLIVING_WEAPON_1); + TREMOR_TOTEM = ai->initSpell(TREMOR_TOTEM_1); // totems + HEALING_STREAM_TOTEM = ai->initSpell(HEALING_STREAM_TOTEM_1); + MANA_SPRING_TOTEM = ai->initSpell(MANA_SPRING_TOTEM_1); + MANA_TIDE_TOTEM = ai->initSpell(MANA_TIDE_TOTEM_1); + // enhancement + FOCUSED = 0; // Focused what? + STORMSTRIKE = ai->initSpell(STORMSTRIKE_1); + LAVA_LASH = ai->initSpell(LAVA_LASH_1); + SHAMANISTIC_RAGE = ai->initSpell(SHAMANISTIC_RAGE_1); + BLOODLUST = ai->initSpell(BLOODLUST_1); + HEROISM = ai->initSpell(HEROISM_1); + FERAL_SPIRIT = ai->initSpell(FERAL_SPIRIT_1); + LIGHTNING_SHIELD = ai->initSpell(LIGHTNING_SHIELD_1); + ROCKBITER_WEAPON = ai->initSpell(ROCKBITER_WEAPON_1); + FLAMETONGUE_WEAPON = ai->initSpell(FLAMETONGUE_WEAPON_1); + FROSTBRAND_WEAPON = ai->initSpell(FROSTBRAND_WEAPON_1); + WINDFURY_WEAPON = ai->initSpell(WINDFURY_WEAPON_1); + STONESKIN_TOTEM = ai->initSpell(STONESKIN_TOTEM_1); // totems + STRENGTH_OF_EARTH_TOTEM = ai->initSpell(STRENGTH_OF_EARTH_TOTEM_1); + FROST_RESISTANCE_TOTEM = ai->initSpell(FROST_RESISTANCE_TOTEM_1); + FLAMETONGUE_TOTEM = ai->initSpell(FLAMETONGUE_TOTEM_1); + FIRE_RESISTANCE_TOTEM = ai->initSpell(FIRE_RESISTANCE_TOTEM_1); + GROUNDING_TOTEM = ai->initSpell(GROUNDING_TOTEM_1); + NATURE_RESISTANCE_TOTEM = ai->initSpell(NATURE_RESISTANCE_TOTEM_1); + WIND_FURY_TOTEM = ai->initSpell(WINDFURY_TOTEM_1); + STONESKIN_TOTEM = ai->initSpell(STONESKIN_TOTEM_1); + WRATH_OF_AIR_TOTEM = ai->initSpell(WRATH_OF_AIR_TOTEM_1); + EARTH_ELEMENTAL_TOTEM = ai->initSpell(EARTH_ELEMENTAL_TOTEM_1); + // elemental + LIGHTNING_BOLT = ai->initSpell(LIGHTNING_BOLT_1); + EARTH_SHOCK = ai->initSpell(EARTH_SHOCK_1); + FLAME_SHOCK = ai->initSpell(FLAME_SHOCK_1); + PURGE = ai->initSpell(PURGE_1); + WIND_SHOCK = 0; //NPC spell + FROST_SHOCK = ai->initSpell(FROST_SHOCK_1); + CHAIN_LIGHTNING = ai->initSpell(CHAIN_LIGHTNING_1); + LAVA_BURST = ai->initSpell(LAVA_BURST_1); + HEX = ai->initSpell(HEX_1); + STONECLAW_TOTEM = ai->initSpell(STONECLAW_TOTEM_1); // totems + SEARING_TOTEM = ai->initSpell(SEARING_TOTEM_1); + FIRE_NOVA_TOTEM = 0; // NPC only spell, check FIRE_NOVA_1 + MAGMA_TOTEM = ai->initSpell(MAGMA_TOTEM_1); + EARTHBIND_TOTEM = ai->initSpell(EARTHBIND_TOTEM_1); + TOTEM_OF_WRATH = ai->initSpell(TOTEM_OF_WRATH_1); + FIRE_ELEMENTAL_TOTEM = ai->initSpell(FIRE_ELEMENTAL_TOTEM_1); + + RECENTLY_BANDAGED = 11196; // first aid check + + // racial + GIFT_OF_THE_NAARU = ai->initSpell(GIFT_OF_THE_NAARU_SHAMAN); // draenei + BLOOD_FURY = ai->initSpell(BLOOD_FURY_SHAMAN); // orc + WAR_STOMP = ai->initSpell(WAR_STOMP_ALL); // tauren + BERSERKING = ai->initSpell(BERSERKING_ALL); // troll +} + +PlayerbotShamanAI::~PlayerbotShamanAI() {} + +void PlayerbotShamanAI::HealTarget(Unit *target, uint8 hp) +{ + PlayerbotAI* ai = GetAI(); + Player *m_bot = GetPlayerBot(); + + if (hp < 30 && HEALING_WAVE > 0 && ai->GetManaPercent() >= 32) + ai->CastSpell(HEALING_WAVE, target); + else if (hp < 45 && LESSER_HEALING_WAVE > 0 && ai->GetManaPercent() >= 19) + ai->CastSpell(LESSER_HEALING_WAVE, target); + else if (hp < 55 && RIPTIDE > 0 && !target->HasAura(RIPTIDE, EFFECT_INDEX_0) && ai->GetManaPercent() >= 21) + ai->CastSpell(RIPTIDE, target); + else if (hp < 70 && CHAIN_HEAL > 0 && ai->GetManaPercent() >= 24) + ai->CastSpell(CHAIN_HEAL, target); + // end HealTarget +} + +void PlayerbotShamanAI::DoNextCombatManeuver(Unit *pTarget) +{ + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + //ai->SetMovementTarget(PlayerbotAI::MOVEMENT_FOLLOW, m_master); // dont want to melee mob <----changed + + Group *m_group = m_bot->GetGroup(); + + // Heal myself + if (ai->GetHealthPercent() < 30 && ai->GetManaPercent() >= 32) + ai->CastSpell(HEALING_WAVE); + else if (ai->GetHealthPercent() < 50 && ai->GetManaPercent() >= 19) + ai->CastSpell(LESSER_HEALING_WAVE); + else if (ai->GetHealthPercent() < 70) + HealTarget(m_bot, ai->GetHealthPercent()); + + // Heal master + uint32 masterHP = m_master->GetHealth() * 100 / m_master->GetMaxHealth(); + if (m_master->isAlive()) + { + if (masterHP < 30 && ai->GetManaPercent() >= 32) + ai->CastSpell(HEALING_WAVE, m_master); + else if (masterHP < 70) + HealTarget(m_master, masterHP); + } + + // Heal group + if (m_group) + { + Group::MemberSlotList const& groupSlot = m_group->GetMemberSlots(); + for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) + { + Player *m_groupMember = sObjectMgr.GetPlayer(itr->guid); + if (!m_groupMember || !m_groupMember->isAlive()) + continue; + + uint32 memberHP = m_groupMember->GetHealth() * 100 / m_groupMember->GetMaxHealth(); + if (memberHP < 30) + HealTarget(m_groupMember, memberHP); + } + } + + // Damage Spells + ai->SetInFront(pTarget); + + switch (SpellSequence) + { + case SPELL_ENHANCEMENT: + if (STRENGTH_OF_EARTH_TOTEM > 0 && LastSpellEnhancement == 1 && (!m_bot->HasAura(STRENGTH_OF_EARTH_TOTEM, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 13) + { + ai->CastSpell(STRENGTH_OF_EARTH_TOTEM); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (STONESKIN_TOTEM > 0 && LastSpellEnhancement == 5 && (!m_bot->HasAura(STONESKIN_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(STRENGTH_OF_EARTH_TOTEM, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 13) + { + ai->CastSpell(STONESKIN_TOTEM); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (FOCUSED > 0 && LastSpellEnhancement == 2) + { + ai->CastSpell(FOCUSED, pTarget); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (FROST_RESISTANCE_TOTEM > 0 && LastSpellEnhancement == 10 && (!m_bot->HasAura(FROST_RESISTANCE_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(TOTEM_OF_WRATH, EFFECT_INDEX_0)) && (!m_bot->HasAura(FLAMETONGUE_TOTEM, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 10) + { + ai->CastSpell(FROST_RESISTANCE_TOTEM); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (FLAMETONGUE_TOTEM > 0 && LastSpellEnhancement == 15 && (!m_bot->HasAura(FLAMETONGUE_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(TOTEM_OF_WRATH, EFFECT_INDEX_0)) && (!m_bot->HasAura(FROST_RESISTANCE_TOTEM, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 14) + { + ai->CastSpell(FLAMETONGUE_TOTEM); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (FIRE_RESISTANCE_TOTEM > 0 && LastSpellEnhancement == 20 && (!m_bot->HasAura(FIRE_RESISTANCE_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(HEALING_STREAM_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(MANA_SPRING_TOTEM, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 10) + { + ai->CastSpell(FIRE_RESISTANCE_TOTEM); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (GROUNDING_TOTEM > 0 && LastSpellEnhancement == 25 && (!m_bot->HasAura(GROUNDING_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(WRATH_OF_AIR_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(WIND_FURY_TOTEM, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 5) + { + ai->CastSpell(GROUNDING_TOTEM); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (NATURE_RESISTANCE_TOTEM > 0 && LastSpellEnhancement == 30 && (!m_bot->HasAura(NATURE_RESISTANCE_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(WRATH_OF_AIR_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(GROUNDING_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(WIND_FURY_TOTEM, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 10) + { + ai->CastSpell(NATURE_RESISTANCE_TOTEM); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (WIND_FURY_TOTEM > 0 && LastSpellEnhancement == 35 && (!m_bot->HasAura(WIND_FURY_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(WRATH_OF_AIR_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(GROUNDING_TOTEM, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 11) + { + ai->CastSpell(WIND_FURY_TOTEM); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (STORMSTRIKE > 0 && LastSpellEnhancement == 4 && (!pTarget->HasAura(STORMSTRIKE, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 8) + { + ai->CastSpell(STORMSTRIKE, pTarget); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (LAVA_LASH > 0 && LastSpellEnhancement == 6 && ai->GetManaPercent() >= 4) + { + ai->CastSpell(LAVA_LASH, pTarget); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (FERAL_SPIRIT > 0 && LastSpellEnhancement == 7 && ai->GetManaPercent() >= 12) + { + ai->CastSpell(FERAL_SPIRIT); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (WRATH_OF_AIR_TOTEM > 0 && (!m_bot->HasAura(WRATH_OF_AIR_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(GROUNDING_TOTEM, EFFECT_INDEX_0)) && LastSpellEnhancement == 40) + { + ai->CastSpell(WRATH_OF_AIR_TOTEM); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (EARTH_ELEMENTAL_TOTEM > 0 && LastSpellEnhancement == 45 && ai->GetManaPercent() >= 24) + { + ai->CastSpell(EARTH_ELEMENTAL_TOTEM); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (BLOODLUST > 0 && LastSpellEnhancement == 8 && (!m_master->HasAura(BLOODLUST, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 26) + { + ai->CastSpell(BLOODLUST); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (HEROISM > 0 && LastSpellEnhancement == 10 && (!m_master->HasAura(HEROISM, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 26) + { + ai->CastSpell(HEROISM); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (SHAMANISTIC_RAGE > 0 && (!m_bot->HasAura(SHAMANISTIC_RAGE, EFFECT_INDEX_0)) && LastSpellEnhancement == 11) + { + ai->CastSpell(SHAMANISTIC_RAGE, m_bot); + SpellSequence = SPELL_RESTORATION; + LastSpellEnhancement = LastSpellEnhancement + 1; + break; + } + else if (LastSpellEnhancement > 50) + { + LastSpellEnhancement = 1; + SpellSequence = SPELL_RESTORATION; + break; + } + LastSpellEnhancement = LastSpellEnhancement + 1; + //SpellSequence = SPELL_RESTORATION; + //break; + + case SPELL_RESTORATION: + if (HEALING_STREAM_TOTEM > 0 && LastSpellRestoration < 3 && ai->GetHealthPercent() < 50 && (!m_bot->HasAura(HEALING_STREAM_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(MANA_SPRING_TOTEM, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 4) + { + ai->CastSpell(HEALING_STREAM_TOTEM); + SpellSequence = SPELL_ELEMENTAL; + LastSpellRestoration = LastSpellRestoration + 1; + break; + } + else if (MANA_SPRING_TOTEM > 0 && LastSpellRestoration < 4 && (!m_bot->HasAura(MANA_SPRING_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(HEALING_STREAM_TOTEM, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 5) + { + ai->CastSpell(MANA_SPRING_TOTEM); + SpellSequence = SPELL_ELEMENTAL; + LastSpellRestoration = LastSpellRestoration + 1; + break; + } + else if (MANA_TIDE_TOTEM > 0 && LastSpellRestoration < 5 && ai->GetManaPercent() < 50 && ai->GetManaPercent() >= 3) + { + ai->CastSpell(MANA_TIDE_TOTEM); + SpellSequence = SPELL_ELEMENTAL; + LastSpellRestoration = LastSpellRestoration + 1; + break; + } + /*else if (TREMOR_TOTEM > 0 && LastSpellRestoration < 6 && (!m_bot->HasAura(STRENGTH_OF_EARTH_TOTEM, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 2) + { + ai->CastSpell(TREMOR_TOTEM); + SpellSequence = SPELL_ELEMENTAL; + LastSpellRestoration = LastSpellRestoration +1; + break; + }*/ + else if (LastSpellRestoration > 6) + { + LastSpellRestoration = 0; + SpellSequence = SPELL_ELEMENTAL; + break; + } + LastSpellRestoration = LastSpellRestoration + 1; + //SpellSequence = SPELL_ELEMENTAL; + //break; + + case SPELL_ELEMENTAL: + if (LIGHTNING_BOLT > 0 && LastSpellElemental == 1 && ai->GetManaPercent() >= 13) + { + ai->CastSpell(LIGHTNING_BOLT, pTarget); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + else if (SEARING_TOTEM > 0 && LastSpellElemental == 2 && (!pTarget->HasAura(SEARING_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(TOTEM_OF_WRATH, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 9) + { + ai->CastSpell(SEARING_TOTEM); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + else if (STONECLAW_TOTEM > 0 && ai->GetHealthPercent() < 51 && LastSpellElemental == 3 && (!pTarget->HasAura(STONECLAW_TOTEM, EFFECT_INDEX_0)) && (!pTarget->HasAura(EARTHBIND_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(STRENGTH_OF_EARTH_TOTEM, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 8) + { + ai->CastSpell(STONECLAW_TOTEM); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + else if (FLAME_SHOCK > 0 && LastSpellElemental == 4 && (!pTarget->HasAura(FLAME_SHOCK, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 22) + { + ai->CastSpell(FLAME_SHOCK, pTarget); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + else if (LAVA_BURST > 0 && LastSpellElemental == 5 && (pTarget->HasAura(FLAME_SHOCK, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 10) + { + ai->CastSpell(LAVA_BURST, pTarget); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + else if (MAGMA_TOTEM > 0 && LastSpellElemental == 6 && (!m_bot->HasAura(TOTEM_OF_WRATH, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 35) + { + ai->CastSpell(MAGMA_TOTEM); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + else if (EARTHBIND_TOTEM > 0 && LastSpellElemental == 7 && (!pTarget->HasAura(EARTHBIND_TOTEM, EFFECT_INDEX_0)) && (!m_bot->HasAura(STRENGTH_OF_EARTH_TOTEM, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 5) + { + ai->CastSpell(EARTHBIND_TOTEM); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + else if (EARTH_SHOCK > 0 && LastSpellElemental == 8 && ai->GetManaPercent() >= 23) + { + ai->CastSpell(EARTH_SHOCK, pTarget); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + else if (PURGE > 0 && LastSpellElemental == 9 && ai->GetManaPercent() >= 8) + { + ai->CastSpell(PURGE, pTarget); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + else if (WIND_SHOCK > 0 && LastSpellElemental == 10 && ai->GetManaPercent() >= 8) + { + ai->CastSpell(WIND_SHOCK, pTarget); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + else if (FIRE_NOVA_TOTEM > 0 && LastSpellElemental == 11 && ai->GetManaPercent() >= 33) + { + ai->CastSpell(FIRE_NOVA_TOTEM); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + else if (FROST_SHOCK > 0 && LastSpellElemental == 12 && (!pTarget->HasAura(FROST_SHOCK, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 23) + { + ai->CastSpell(FROST_SHOCK, pTarget); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + else if (CHAIN_LIGHTNING > 0 && LastSpellElemental == 13 && ai->GetManaPercent() >= 33) + { + ai->CastSpell(CHAIN_LIGHTNING, pTarget); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + else if (TOTEM_OF_WRATH > 0 && LastSpellElemental == 14 && (!m_bot->HasAura(TOTEM_OF_WRATH, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 5) + { + ai->CastSpell(TOTEM_OF_WRATH); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + else if (FIRE_ELEMENTAL_TOTEM > 0 && LastSpellElemental == 15 && ai->GetManaPercent() >= 23) + { + ai->CastSpell(FIRE_ELEMENTAL_TOTEM); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + } + /*else if (HEX > 0 && LastSpellElemental == 16 && (!pTarget->HasAura(HEX, EFFECT_INDEX_0)) && ai->GetManaPercent() >= 3) + { + ai->CastSpell(HEX); + SpellSequence = SPELL_ENHANCEMENT; + LastSpellElemental = LastSpellElemental + 1; + break; + }*/ + else if (LastSpellElemental > 16) + { + LastSpellElemental = 1; + SpellSequence = SPELL_ENHANCEMENT; + break; + } + else + { + LastSpellElemental = LastSpellElemental + 1; + SpellSequence = SPELL_ENHANCEMENT; + } + } +} // end DoNextCombatManeuver + +void PlayerbotShamanAI::DoNonCombatActions() +{ + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + SpellSequence = SPELL_ENHANCEMENT; + + // buff master with EARTH_SHIELD + if (EARTH_SHIELD > 0) + (!m_master->HasAura(EARTH_SHIELD, EFFECT_INDEX_0) && ai->CastSpell(EARTH_SHIELD, m_master)); + + // buff myself with WATER_SHIELD, LIGHTNING_SHIELD + if (WATER_SHIELD > 0) + (!m_bot->HasAura(WATER_SHIELD, EFFECT_INDEX_0) && !m_bot->HasAura(LIGHTNING_SHIELD, EFFECT_INDEX_0) && ai->CastSpell(WATER_SHIELD, m_bot)); + else if (LIGHTNING_SHIELD > 0) + (!m_bot->HasAura(LIGHTNING_SHIELD, EFFECT_INDEX_0) && !m_bot->HasAura(WATER_SHIELD, EFFECT_INDEX_0) && ai->CastSpell(LIGHTNING_SHIELD, m_bot)); +/* + // buff myself weapon + if (ROCKBITER_WEAPON > 0) + (!m_bot->HasAura(ROCKBITER_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(EARTHLIVING_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(WINDFURY_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(FLAMETONGUE_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(FROSTBRAND_WEAPON, EFFECT_INDEX_0) && ai->CastSpell(ROCKBITER_WEAPON,*m_bot) ); + else if (EARTHLIVING_WEAPON > 0) + (!m_bot->HasAura(EARTHLIVING_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(EARTHLIVING_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(FLAMETONGUE_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(FROSTBRAND_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(ROCKBITER_WEAPON, EFFECT_INDEX_0) && ai->CastSpell(WINDFURY_WEAPON,*m_bot) ); + else if (WINDFURY_WEAPON > 0) + (!m_bot->HasAura(WINDFURY_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(EARTHLIVING_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(FLAMETONGUE_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(FROSTBRAND_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(ROCKBITER_WEAPON, EFFECT_INDEX_0) && ai->CastSpell(WINDFURY_WEAPON,*m_bot) ); + else if (FLAMETONGUE_WEAPON > 0) + (!m_bot->HasAura(FLAMETONGUE_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(EARTHLIVING_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(WINDFURY_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(FROSTBRAND_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(ROCKBITER_WEAPON, EFFECT_INDEX_0) && ai->CastSpell(FLAMETONGUE_WEAPON,*m_bot) ); + else if (FROSTBRAND_WEAPON > 0) + (!m_bot->HasAura(FROSTBRAND_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(EARTHLIVING_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(WINDFURY_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(FLAMETONGUE_WEAPON, EFFECT_INDEX_0) && !m_bot->HasAura(ROCKBITER_WEAPON, EFFECT_INDEX_0) && ai->CastSpell(FROSTBRAND_WEAPON,*m_bot) ); + */ + // mana check + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + + Item* pItem = ai->FindDrink(); + Item* fItem = ai->FindBandage(); + + if (pItem != NULL && ai->GetManaPercent() < 30) + { + ai->UseItem(pItem); + return; + } + + // hp check + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + + pItem = ai->FindFood(); + + if (pItem != NULL && ai->GetHealthPercent() < 30) + { + ai->UseItem(pItem); + return; + } + else if (pItem == NULL && fItem != NULL && !m_bot->HasAura(RECENTLY_BANDAGED, EFFECT_INDEX_0) && ai->GetHealthPercent() < 70) + { + ai->UseItem(fItem); + return; + } + + // heal master's group + if (m_master->GetGroup()) + { + Group::MemberSlotList const& groupSlot = m_master->GetGroup()->GetMemberSlots(); + for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) + { + Player *tPlayer = sObjectMgr.GetPlayer(itr->guid); + if (!tPlayer || !tPlayer->isAlive()) + continue; + + // heal + (HealTarget(tPlayer, tPlayer->GetHealth() * 100 / tPlayer->GetMaxHealth())); + } + } +} // end DoNonCombatActions diff --git a/src/game/playerbot/PlayerbotShamanAI.h b/src/game/playerbot/PlayerbotShamanAI.h new file mode 100644 index 000000000..e628665ec --- /dev/null +++ b/src/game/playerbot/PlayerbotShamanAI.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PLAYERBOTSHAMANAI_H +#define _PLAYERBOTSHAMANAI_H + +#include "PlayerbotClassAI.h" + +enum +{ + SPELL_ENHANCEMENT, + SPELL_RESTORATION, + SPELL_ELEMENTAL +}; + +enum +{ + ANCESTRAL_SPIRIT_1 = 2008, + ASTRAL_RECALL_1 = 556, + BLOODLUST_1 = 2825, + CALL_OF_THE_ANCESTORS_1 = 66843, + CALL_OF_THE_ELEMENTS_1 = 66842, + CALL_OF_THE_SPIRITS_1 = 66844, + CHAIN_HEAL_1 = 1064, + CHAIN_LIGHTNING_1 = 421, + CHAINED_HEAL_1 = 70809, + CLEANSE_SPIRIT_1 = 51886, + CLEANSING_TOTEM_1 = 8170, + CURE_TOXINS_1 = 526, + EARTH_ELEMENTAL_TOTEM_1 = 2062, + EARTH_SHIELD_1 = 974, + EARTH_SHOCK_1 = 8042, + EARTHBIND_TOTEM_1 = 2484, + EARTHLIVING_WEAPON_1 = 51730, + ELEMENTAL_MASTERY_1 = 16166, + FERAL_SPIRIT_1 = 51533, + FIRE_ELEMENTAL_TOTEM_1 = 2894, + FIRE_NOVA_1 = 1535, + FIRE_RESISTANCE_TOTEM_1 = 8184, + FLAME_SHOCK_1 = 8050, + FLAMETONGUE_TOTEM_1 = 8227, + FLAMETONGUE_WEAPON_1 = 8024, + FROST_RESISTANCE_TOTEM_1 = 8181, + FROST_SHOCK_1 = 8056, + FROSTBRAND_WEAPON_1 = 8033, + GHOST_WOLF_1 = 2645, + GROUNDING_TOTEM_1 = 8177, + HEALING_STREAM_TOTEM_1 = 5394, + HEALING_WAVE_1 = 331, + HEROISM_1 = 32182, + HEX_1 = 51514, + LAVA_BURST_1 = 51505, + LAVA_LASH_1 = 60103, + LESSER_HEALING_WAVE_1 = 8004, + LIGHTNING_BOLT_1 = 403, + LIGHTNING_SHIELD_1 = 324, + MAGMA_TOTEM_1 = 8190, + MANA_SPRING_TOTEM_1 = 5675, + MANA_TIDE_TOTEM_1 = 16190, + NATURE_RESISTANCE_TOTEM_1 = 10595, + NATURES_SWIFTNESS_SHAMAN_1 = 16188, + PURGE_1 = 370, + RIPTIDE_1 = 61295, + ROCKBITER_WEAPON_1 = 8017, + SEARING_TOTEM_1 = 3599, + SENTRY_TOTEM_1 = 6495, + SHAMANISTIC_RAGE_1 = 30823, + STONECLAW_TOTEM_1 = 5730, + STONESKIN_TOTEM_1 = 8071, + STORMSTRIKE_1 = 17364, + STRENGTH_OF_EARTH_TOTEM_1 = 8075, + THUNDERSTORM_1 = 51490, + TIDAL_FORCE_1 = 55198, + TOTEM_OF_WRATH_1 = 30706, + TOTEMIC_RECALL_1 = 36936, + TREMOR_TOTEM_1 = 8143, + WATER_BREATHING_1 = 131, + WATER_SHIELD_1 = 52127, + WATER_WALKING_1 = 546, + WIND_SHEAR_1 = 57994, + WINDFURY_TOTEM_1 = 8512, + WINDFURY_WEAPON_1 = 8232, + WRATH_OF_AIR_TOTEM_1 = 3738 +}; +//class Player; + +class MANGOS_DLL_SPEC PlayerbotShamanAI : PlayerbotClassAI +{ +public: + PlayerbotShamanAI(Player* const bot, PlayerbotAI* const ai); + virtual ~PlayerbotShamanAI(); + + // all combat actions go here + void DoNextCombatManeuver(Unit*); + + // all non combat actions go here, ex buffs, heals, rezzes + void DoNonCombatActions(); + + void InitSpells(PlayerbotAI* const ai); + +private: + // Heals the target based off its hps + void HealTarget (Unit* target, uint8 hp); + + // ENHANCEMENT + uint32 ROCKBITER_WEAPON, STONESKIN_TOTEM, LIGHTNING_SHIELD, FLAMETONGUE_WEAPON, STRENGTH_OF_EARTH_TOTEM, FOCUSED, FROSTBRAND_WEAPON, FROST_RESISTANCE_TOTEM, FLAMETONGUE_TOTEM, FIRE_RESISTANCE_TOTEM, WINDFURY_WEAPON, GROUNDING_TOTEM, NATURE_RESISTANCE_TOTEM, WIND_FURY_TOTEM, STORMSTRIKE, LAVA_LASH, SHAMANISTIC_RAGE, WRATH_OF_AIR_TOTEM, EARTH_ELEMENTAL_TOTEM, BLOODLUST, HEROISM, FERAL_SPIRIT; + + // RESTORATION + uint32 HEALING_WAVE, LESSER_HEALING_WAVE, ANCESTRAL_SPIRIT, TREMOR_TOTEM, HEALING_STREAM_TOTEM, MANA_SPRING_TOTEM, CHAIN_HEAL, MANA_TIDE_TOTEM, EARTH_SHIELD, WATER_SHIELD, EARTHLIVING_WEAPON, RIPTIDE; + + // ELEMENTAL + uint32 LIGHTNING_BOLT, EARTH_SHOCK, STONECLAW_TOTEM, FLAME_SHOCK, SEARING_TOTEM, PURGE, FIRE_NOVA_TOTEM, WIND_SHOCK, FROST_SHOCK, MAGMA_TOTEM, CHAIN_LIGHTNING, TOTEM_OF_WRATH, FIRE_ELEMENTAL_TOTEM, LAVA_BURST, EARTHBIND_TOTEM, HEX; + + // first aid + uint32 RECENTLY_BANDAGED; + + // racial + uint32 ARCANE_TORRENT, GIFT_OF_THE_NAARU, STONEFORM, ESCAPE_ARTIST, EVERY_MAN_FOR_HIMSELF, SHADOWMELD, BLOOD_FURY, WAR_STOMP, BERSERKING, WILL_OF_THE_FORSAKEN; + + uint32 SpellSequence, LastSpellEnhancement, LastSpellRestoration, LastSpellElemental; +}; + +#endif diff --git a/src/game/playerbot/PlayerbotWarlockAI.cpp b/src/game/playerbot/PlayerbotWarlockAI.cpp new file mode 100644 index 000000000..97692fc3f --- /dev/null +++ b/src/game/playerbot/PlayerbotWarlockAI.cpp @@ -0,0 +1,600 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PlayerbotWarlockAI.h" + +class PlayerbotAI; + +PlayerbotWarlockAI::PlayerbotWarlockAI(Player* const bot, PlayerbotAI* const ai): PlayerbotClassAI(bot, ai) +{ + InitSpells(ai); + m_lastDemon = 0; + m_demonOfChoice = DEMON_IMP; + m_isTempImp = false; +} + +void PlayerbotWarlockAI::InitSpells(PlayerbotAI* const ai) +{ + // DESTRUCTION + SHADOW_BOLT = ai->initSpell(SHADOW_BOLT_1); + IMMOLATE = ai->initSpell(IMMOLATE_1); + INCINERATE = ai->initSpell(INCINERATE_1); + SEARING_PAIN = ai->initSpell(SEARING_PAIN_1); + CONFLAGRATE = ai->initSpell(CONFLAGRATE_1); + SHADOWFURY = ai->initSpell(SHADOWFURY_1); + CHAOS_BOLT = ai->initSpell(CHAOS_BOLT_1); + SHADOWFLAME = ai->initSpell(SHADOWFLAME_1); + HELLFIRE = ai->initSpell(HELLFIRE_1); + RAIN_OF_FIRE = ai->initSpell(RAIN_OF_FIRE_1); + SOUL_FIRE = ai->initSpell(SOUL_FIRE_1); // soul shard spells + SHADOWBURN = ai->initSpell(SHADOWBURN_1); + // CURSE + CURSE_OF_WEAKNESS = ai->initSpell(CURSE_OF_WEAKNESS_1); + CURSE_OF_THE_ELEMENTS = ai->initSpell(CURSE_OF_THE_ELEMENTS_1); + CURSE_OF_AGONY = ai->initSpell(CURSE_OF_AGONY_1); + CURSE_OF_EXHAUSTION = ai->initSpell(CURSE_OF_EXHAUSTION_1); + CURSE_OF_TONGUES = ai->initSpell(CURSE_OF_TONGUES_1); + CURSE_OF_DOOM = ai->initSpell(CURSE_OF_DOOM_1); + // AFFLICTION + CORRUPTION = ai->initSpell(CORRUPTION_1); + DRAIN_SOUL = ai->initSpell(DRAIN_SOUL_1); + DRAIN_LIFE = ai->initSpell(DRAIN_LIFE_1); + DRAIN_MANA = ai->initSpell(DRAIN_MANA_1); + LIFE_TAP = ai->initSpell(LIFE_TAP_1); + UNSTABLE_AFFLICTION = ai->initSpell(UNSTABLE_AFFLICTION_1); + HAUNT = ai->initSpell(HAUNT_1); + SEED_OF_CORRUPTION = ai->initSpell(SEED_OF_CORRUPTION_1); + DARK_PACT = ai->initSpell(DARK_PACT_1); + HOWL_OF_TERROR = ai->initSpell(HOWL_OF_TERROR_1); + FEAR = ai->initSpell(FEAR_1); + // DEMONOLOGY + DEMON_SKIN = ai->initSpell(DEMON_SKIN_1); + DEMON_ARMOR = ai->initSpell(DEMON_ARMOR_1); + DEMONIC_EMPOWERMENT = ai->initSpell(DEMONIC_EMPOWERMENT_1); + FEL_ARMOR = ai->initSpell(FEL_ARMOR_1); + SHADOW_WARD = ai->initSpell(SHADOW_WARD_1); + SOULSHATTER = ai->initSpell(SOULSHATTER_1); + SOUL_LINK = ai->initSpell(SOUL_LINK_1); + SOUL_LINK_AURA = 25228; // dummy aura applied, after spell SOUL_LINK + HEALTH_FUNNEL = ai->initSpell(HEALTH_FUNNEL_1); + DETECT_INVISIBILITY = ai->initSpell(DETECT_INVISIBILITY_1); + CREATE_FIRESTONE = ai->initSpell(CREATE_FIRESTONE_1); + CREATE_HEALTHSTONE = ai->initSpell(CREATE_HEALTHSTONE_1); + CREATE_SOULSTONE = ai->initSpell(CREATE_SOULSTONE_1); + // demon summon + SUMMON_IMP = ai->initSpell(SUMMON_IMP_1); + SUMMON_VOIDWALKER = ai->initSpell(SUMMON_VOIDWALKER_1); + SUMMON_SUCCUBUS = ai->initSpell(SUMMON_SUCCUBUS_1); + SUMMON_FELHUNTER = ai->initSpell(SUMMON_FELHUNTER_1); + SUMMON_FELGUARD = ai->initSpell(SUMMON_FELGUARD_1); + // demon skills should be initialized on demons + BLOOD_PACT = 0; // imp skill + CONSUME_SHADOWS = 0; // voidwalker skill + FEL_INTELLIGENCE = 0; // felhunter skill + + RECENTLY_BANDAGED = 11196; // first aid check + + // racial + ARCANE_TORRENT = ai->initSpell(ARCANE_TORRENT_MANA_CLASSES); // blood elf + ESCAPE_ARTIST = ai->initSpell(ESCAPE_ARTIST_ALL); // gnome + EVERY_MAN_FOR_HIMSELF = ai->initSpell(EVERY_MAN_FOR_HIMSELF_ALL); // human + BLOOD_FURY = ai->initSpell(BLOOD_FURY_WARLOCK); // orc + WILL_OF_THE_FORSAKEN = ai->initSpell(WILL_OF_THE_FORSAKEN_ALL); // undead +} + +PlayerbotWarlockAI::~PlayerbotWarlockAI() {} + +void PlayerbotWarlockAI::DoNextCombatManeuver(Unit *pTarget) +{ + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + //ai->SetMovementTarget( PlayerbotAI::MOVEMENT_FOLLOW, m_master ); // dont want to melee mob + + ai->SetInFront(pTarget); + Unit* pVictim = pTarget->getVictim(); + Pet *pet = m_bot->GetPet(); + + // Empower demon + if (pet && DEMONIC_EMPOWERMENT && !m_bot->HasSpellCooldown(DEMONIC_EMPOWERMENT)) + ai->CastSpell(DEMONIC_EMPOWERMENT); + + // Use voidwalker sacrifice on low health if possible + if (ai->GetHealthPercent() < 50) + { + if (pet && pet->GetEntry() == DEMON_VOIDWALKER && SACRIFICE && !m_bot->HasAura(SACRIFICE)) + ai->CastPetSpell(SACRIFICE); + } + + // Use healthstone + if (ai->GetHealthPercent() < 30) + { + Item* healthStone = ai->FindConsumable(HEALTHSTONE_DISPLAYID); + if (healthStone) + ai->UseItem(healthStone); + } + + // Damage Spells + switch (SpellSequence) + { + case SPELL_CURSES: + if (CURSE_OF_AGONY && !pTarget->HasAura(CURSE_OF_AGONY) && !pTarget->HasAura(SHADOWFLAME) && LastSpellCurse < 1) + { + ai->CastSpell(CURSE_OF_AGONY, pTarget); + SpellSequence = SPELL_AFFLICTION; + ++LastSpellCurse; + break; + } + else if (CURSE_OF_THE_ELEMENTS && !pTarget->HasAura(CURSE_OF_THE_ELEMENTS) && !pTarget->HasAura(SHADOWFLAME) && !pTarget->HasAura(CURSE_OF_AGONY) && !pTarget->HasAura(CURSE_OF_WEAKNESS) && LastSpellCurse < 2) + { + ai->CastSpell(CURSE_OF_THE_ELEMENTS, pTarget); + SpellSequence = SPELL_AFFLICTION; + ++LastSpellCurse; + break; + } + else if (CURSE_OF_WEAKNESS && !pTarget->HasAura(CURSE_OF_WEAKNESS) && !pTarget->HasAura(SHADOWFLAME) && !pTarget->HasAura(CURSE_OF_AGONY) && !pTarget->HasAura(CURSE_OF_THE_ELEMENTS) && LastSpellCurse < 3) + { + ai->CastSpell(CURSE_OF_WEAKNESS, pTarget); + SpellSequence = SPELL_AFFLICTION; + ++LastSpellCurse; + break; + } + else if (CURSE_OF_TONGUES && !pTarget->HasAura(CURSE_OF_TONGUES) && !pTarget->HasAura(SHADOWFLAME) && !pTarget->HasAura(CURSE_OF_WEAKNESS) && !pTarget->HasAura(CURSE_OF_AGONY) && !pTarget->HasAura(CURSE_OF_THE_ELEMENTS) && LastSpellCurse < 4) + { + ai->CastSpell(CURSE_OF_TONGUES, pTarget); + SpellSequence = SPELL_AFFLICTION; + ++LastSpellCurse; + break; + } + LastSpellCurse = 0; + //SpellSequence = SPELL_AFFLICTION; + //break; + + case SPELL_AFFLICTION: + if (LIFE_TAP && LastSpellAffliction < 1 && ai->GetManaPercent() <= 50 && ai->GetHealthPercent() > 50) + { + ai->CastSpell(LIFE_TAP, m_bot); + SpellSequence = SPELL_DESTRUCTION; + ++LastSpellAffliction; + break; + } + else if (CORRUPTION && !pTarget->HasAura(CORRUPTION) && !pTarget->HasAura(SHADOWFLAME) && !pTarget->HasAura(SEED_OF_CORRUPTION) && LastSpellAffliction < 2) + { + ai->CastSpell(CORRUPTION, pTarget); + SpellSequence = SPELL_DESTRUCTION; + ++LastSpellAffliction; + break; + } + else if (DRAIN_SOUL && pTarget->GetHealth() < pTarget->GetMaxHealth() * 0.40 && !pTarget->HasAura(DRAIN_SOUL) && LastSpellAffliction < 3) + { + ai->CastSpell(DRAIN_SOUL, pTarget); + //ai->SetIgnoreUpdateTime(15); + SpellSequence = SPELL_DESTRUCTION; + ++LastSpellAffliction; + break; + } + else if (DRAIN_LIFE && LastSpellAffliction < 4 && !pTarget->HasAura(DRAIN_SOUL) && !pTarget->HasAura(SEED_OF_CORRUPTION) && !pTarget->HasAura(DRAIN_LIFE) && !pTarget->HasAura(DRAIN_MANA) && ai->GetHealthPercent() <= 70) + { + ai->CastSpell(DRAIN_LIFE, pTarget); + //ai->SetIgnoreUpdateTime(5); + SpellSequence = SPELL_DESTRUCTION; + ++LastSpellAffliction; + break; + } + else if (UNSTABLE_AFFLICTION && LastSpellAffliction < 5 && !pTarget->HasAura(UNSTABLE_AFFLICTION) && !pTarget->HasAura(SHADOWFLAME)) + { + ai->CastSpell(UNSTABLE_AFFLICTION, pTarget); + SpellSequence = SPELL_DESTRUCTION; + ++LastSpellAffliction; + break; + } + else if (HAUNT && LastSpellAffliction < 6 && !pTarget->HasAura(HAUNT)) + { + ai->CastSpell(HAUNT, pTarget); + SpellSequence = SPELL_DESTRUCTION; + ++LastSpellAffliction; + break; + } + else if (SEED_OF_CORRUPTION && !pTarget->HasAura(SEED_OF_CORRUPTION) && LastSpellAffliction < 7) + { + ai->CastSpell(SEED_OF_CORRUPTION, pTarget); + SpellSequence = SPELL_DESTRUCTION; + ++LastSpellAffliction; + break; + } + else if (HOWL_OF_TERROR && !pTarget->HasAura(HOWL_OF_TERROR) && LastSpellAffliction < 8) + { + ai->CastSpell(HOWL_OF_TERROR, pTarget); + + SpellSequence = SPELL_DESTRUCTION; + ++LastSpellAffliction; + break; + } + else if (FEAR && !pTarget->HasAura(FEAR) && pVictim == m_bot && LastSpellAffliction < 9) + { + ai->CastSpell(FEAR, pTarget); + + //ai->SetIgnoreUpdateTime(1.5); + SpellSequence = SPELL_DESTRUCTION; + ++LastSpellAffliction; + break; + } + else if ((pet) + && (DARK_PACT > 0 && ai->GetManaPercent() <= 50 && LastSpellAffliction < 10 && pet->GetPower(POWER_MANA) > 0)) + { + ai->CastSpell(DARK_PACT, m_bot); + SpellSequence = SPELL_DESTRUCTION; + ++LastSpellAffliction; + break; + } + LastSpellAffliction = 0; + //SpellSequence = SPELL_DESTRUCTION; + //break; + + case SPELL_DESTRUCTION: + if (SHADOWFURY && LastSpellDestruction < 1 && !pTarget->HasAura(SHADOWFURY)) + { + ai->CastSpell(SHADOWFURY, pTarget); + SpellSequence = SPELL_CURSES; + ++LastSpellDestruction; + break; + } + else if (SHADOW_BOLT && LastSpellDestruction < 2) + { + ai->CastSpell(SHADOW_BOLT, pTarget); + SpellSequence = SPELL_CURSES; + ++LastSpellDestruction; + break; + } + else if (RAIN_OF_FIRE && LastSpellDestruction < 3) + { + ai->CastSpell(RAIN_OF_FIRE, pTarget); + + //ai->SetIgnoreUpdateTime(8); + SpellSequence = SPELL_CURSES; + ++LastSpellDestruction; + break; + } + else if (SHADOWFLAME && !pTarget->HasAura(SHADOWFLAME) && LastSpellDestruction < 4) + { + ai->CastSpell(SHADOWFLAME, pTarget); + SpellSequence = SPELL_CURSES; + ++LastSpellDestruction; + break; + } + else if (IMMOLATE && !pTarget->HasAura(IMMOLATE) && !pTarget->HasAura(SHADOWFLAME) && LastSpellDestruction < 5) + { + ai->CastSpell(IMMOLATE, pTarget); + SpellSequence = SPELL_CURSES; + ++LastSpellDestruction; + break; + } + else if (CONFLAGRATE && LastSpellDestruction < 6) + { + ai->CastSpell(CONFLAGRATE, pTarget); + SpellSequence = SPELL_CURSES; + ++LastSpellDestruction; + break; + } + else if (INCINERATE && LastSpellDestruction < 7) + { + ai->CastSpell(INCINERATE, pTarget); + SpellSequence = SPELL_CURSES; + ++LastSpellDestruction; + break; + } + else if (SEARING_PAIN && LastSpellDestruction < 8) + { + ai->CastSpell(SEARING_PAIN, pTarget); + SpellSequence = SPELL_CURSES; + ++LastSpellDestruction; + break; + } + else if (SOUL_FIRE && LastSpellDestruction < 9) + { + ai->CastSpell(SOUL_FIRE, pTarget); + //ai->SetIgnoreUpdateTime(6); + SpellSequence = SPELL_CURSES; + ++LastSpellDestruction; + break; + } + else if (CHAOS_BOLT && LastSpellDestruction < 10) + { + ai->CastSpell(CHAOS_BOLT, pTarget); + SpellSequence = SPELL_CURSES; + ++LastSpellDestruction; + break; + } + else if (SHADOWBURN && LastSpellDestruction < 11 && pTarget->GetHealth() < pTarget->GetMaxHealth() * 0.20 && !pTarget->HasAura(SHADOWBURN)) + { + ai->CastSpell(SHADOWBURN, pTarget); + SpellSequence = SPELL_CURSES; + ++LastSpellDestruction; + break; + } + else if (HELLFIRE && LastSpellDestruction < 12 && !m_bot->HasAura(HELLFIRE) && ai->GetHealthPercent() >= 50) + { + ai->CastSpell(HELLFIRE); + + //ai->SetIgnoreUpdateTime(15); + SpellSequence = SPELL_CURSES; + ++LastSpellDestruction; + break; + } + else + { + LastSpellDestruction = 0; + SpellSequence = SPELL_CURSES; + } + } +} // end DoNextCombatManeuver + +void PlayerbotWarlockAI::DoNonCombatActions() +{ + SpellSequence = SPELL_CURSES; + + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + Pet *pet = m_bot->GetPet(); + + // Initialize pet spells + if (pet && pet->GetEntry() != m_lastDemon) + { + switch (pet->GetEntry()) + { + case DEMON_IMP: + { + BLOOD_PACT = ai->initPetSpell(BLOOD_PACT_ICON); + FIREBOLT = ai->initPetSpell(FIREBOLT_ICON); + FIRE_SHIELD = ai->initPetSpell(FIRE_SHIELD_ICON); + break; + } + case DEMON_VOIDWALKER: + { + CONSUME_SHADOWS = ai->initPetSpell(CONSUME_SHADOWS_ICON); + SACRIFICE = ai->initPetSpell(SACRIFICE_ICON); + SUFFERING = ai->initPetSpell(SUFFERING_ICON); + TORMENT = ai->initPetSpell(TORMENT_ICON); + break; + } + case DEMON_SUCCUBUS: + { + LASH_OF_PAIN = ai->initPetSpell(LASH_OF_PAIN_ICON); + SEDUCTION = ai->initPetSpell(SEDUCTION_ICON); + SOOTHING_KISS = ai->initPetSpell(SOOTHING_KISS_ICON); + break; + } + case DEMON_FELHUNTER: + { + DEVOUR_MAGIC = ai->initPetSpell(DEVOUR_MAGIC_ICON); + FEL_INTELLIGENCE = ai->initPetSpell(FEL_INTELLIGENCE_ICON); + SHADOW_BITE = ai->initPetSpell(SHADOW_BITE_ICON); + SPELL_LOCK = ai->initPetSpell(SPELL_LOCK_ICON); + break; + } + case DEMON_FELGUARD: + { + ANGUISH = ai->initPetSpell(ANGUISH_ICON); + CLEAVE = ai->initPetSpell(CLEAVE_ICON); + INTERCEPT = ai->initPetSpell(INTERCEPT_ICON); + break; + } + } + + m_lastDemon = pet->GetEntry(); + + if (!m_isTempImp) + m_demonOfChoice = pet->GetEntry(); + } + + // Destroy extra soul shards + uint8 shardCount = m_bot->GetItemCount(SOUL_SHARD, false, NULL); + uint8 freeSpace = ai->GetFreeBagSpace(); + if (shardCount > MAX_SHARD_COUNT || (freeSpace == 0 && shardCount > 1)) + m_bot->DestroyItemCount(SOUL_SHARD, shardCount > MAX_SHARD_COUNT ? shardCount - MAX_SHARD_COUNT : 1, true, false); + + // buff myself DEMON_SKIN, DEMON_ARMOR, FEL_ARMOR + if (FEL_ARMOR) + { + if (ai->SelfBuff(FEL_ARMOR)) + return; + } + else if (DEMON_ARMOR) + { + if (ai->SelfBuff(DEMON_ARMOR)) + return; + } + else if (DEMON_SKIN) + { + if (ai->SelfBuff(DEMON_SKIN)) + return; + } + + // healthstone creation + if (CREATE_HEALTHSTONE && shardCount > 0) + { + Item* const healthStone = ai->FindConsumable(HEALTHSTONE_DISPLAYID); + if (!healthStone && ai->CastSpell(CREATE_HEALTHSTONE)) + return; + } + + // soulstone creation and use + if (CREATE_SOULSTONE) + { + Item* soulStone = ai->FindConsumable(SOULSTONE_DISPLAYID); + if (!soulStone) + { + if (shardCount > 0 && !m_bot->HasSpellCooldown(CREATE_SOULSTONE) && ai->CastSpell(CREATE_SOULSTONE)) + return; + } + else + { + uint32 soulStoneSpell = soulStone->GetProto()->Spells[0].SpellId; + if (!m_master->HasAura(soulStoneSpell) && !m_bot->HasSpellCooldown(soulStoneSpell)) + { + ai->UseItem(soulStone, m_master); + return; + } + } + } + + // firestone creation and use + Item* const weapon = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); + if (weapon && weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0) + { + Item* const stone = ai->FindConsumable(FIRESTONE_DISPLAYID); + if (!stone) + { + if (CREATE_FIRESTONE && shardCount > 0 && ai->CastSpell(CREATE_FIRESTONE)) + return; + } + else + { + ai->UseItem(stone, EQUIPMENT_SLOT_MAINHAND); + return; + } + } + + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + + // mana check + if (pet && DARK_PACT && pet->GetPower(POWER_MANA) > 0 && ai->GetManaPercent() <= 50) + { + if (ai->CastSpell(DARK_PACT, m_bot)) + return; + } + + if (LIFE_TAP && ai->GetManaPercent() <= 50 && ai->GetHealthPercent() > 50) + { + if (ai->CastSpell(LIFE_TAP, m_bot)) + return; + } + + if (ai->GetManaPercent() < 25) + { + Item* pItem = ai->FindDrink(); + if (pItem) + { + + ai->UseItem(pItem); + return; + } + } + + // hp check + if (ai->GetHealthPercent() < 30) + { + Item* pItem = ai->FindFood(); + if (pItem) + { + + ai->UseItem(pItem); + return; + } + } + + if (ai->GetHealthPercent() < 50 && !m_bot->HasAura(RECENTLY_BANDAGED)) + { + Item* fItem = ai->FindBandage(); + if (fItem) + { + + ai->UseItem(fItem); + return; + } + } + + //Heal Voidwalker + if (pet && pet->GetEntry() == DEMON_VOIDWALKER && CONSUME_SHADOWS && pet->GetHealthPercent() < 75 && !pet->HasAura(CONSUME_SHADOWS)) + ai->CastPetSpell(CONSUME_SHADOWS); + + // Summon demon + if (!pet || m_isTempImp) + { + uint32 summonSpellId; + if (m_demonOfChoice != DEMON_IMP && shardCount > 0) + { + switch (m_demonOfChoice) + { + case DEMON_VOIDWALKER: + summonSpellId = SUMMON_VOIDWALKER; + break; + case DEMON_FELGUARD: + summonSpellId = SUMMON_FELGUARD; + break; + case DEMON_FELHUNTER: + summonSpellId = SUMMON_FELHUNTER; + break; + case DEMON_SUCCUBUS: + summonSpellId = SUMMON_SUCCUBUS; + break; + default: + summonSpellId = 0; + } + if (ai->CastSpell(summonSpellId)) + { + + m_isTempImp = false; + return; + } + } + else + { + if (!pet && SUMMON_IMP && ai->CastSpell(SUMMON_IMP)) + { + if (m_demonOfChoice != DEMON_IMP) + m_isTempImp = true; + + + return; + } + } + } + + // Soul link demon + if (pet && SOUL_LINK && !m_bot->HasAura(SOUL_LINK_AURA) && ai->CastSpell(SOUL_LINK, m_bot)) + return; + + // Check demon buffs + if (pet && pet->GetEntry() == DEMON_IMP && BLOOD_PACT && !m_bot->HasAura(BLOOD_PACT) && ai->CastPetSpell(BLOOD_PACT)) + return; + + if (pet && pet->GetEntry() == DEMON_FELHUNTER && FEL_INTELLIGENCE && !m_bot->HasAura(FEL_INTELLIGENCE) && ai->CastPetSpell(FEL_INTELLIGENCE)) + return; + +} // end DoNonCombatActions diff --git a/src/game/playerbot/PlayerbotWarlockAI.h b/src/game/playerbot/PlayerbotWarlockAI.h new file mode 100644 index 000000000..2b3bb8d82 --- /dev/null +++ b/src/game/playerbot/PlayerbotWarlockAI.h @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PLAYERBOTWARLOCKAI_H +#define _PLAYERBOTWARLOCKAI_H + +#include "PlayerbotClassAI.h" + +#define SOUL_SHARD 6265 +#define MAX_SHARD_COUNT 4 // Maximum soul shard count bot should keep + +enum +{ + SPELL_CURSES, + SPELL_AFFLICTION, + SPELL_DESTRUCTION, + SPELL_DEMONOLOGY +}; + +enum StoneDisplayId +{ + FIRESTONE_DISPLAYID = 7409, + SPELLSTONE_DISPLAYID = 13291, + SOULSTONE_DISPLAYID = 6009, + HEALTHSTONE_DISPLAYID = 8026 +}; + +enum DemonEntry +{ + DEMON_IMP = 416, + DEMON_VOIDWALKER = 1860, + DEMON_SUCCUBUS = 1863, + DEMON_FELHUNTER = 417, + DEMON_FELGUARD = 17252 +}; + +enum DemonSpellIconIds +{ + // Imp + BLOOD_PACT_ICON = 541, + FIREBOLT_ICON = 18, + FIRE_SHIELD_ICON = 16, + // Felguard + ANGUISH_ICON = 173, + CLEAVE_ICON = 277, + INTERCEPT_ICON = 516, + // Felhunter + DEVOUR_MAGIC_ICON = 47, + FEL_INTELLIGENCE_ICON = 1940, + SHADOW_BITE_ICON = 2027, + SPELL_LOCK_ICON = 77, + // Succubus + LASH_OF_PAIN_ICON = 596, + SEDUCTION_ICON = 48, + SOOTHING_KISS_ICON = 694, + // Voidwalker + CONSUME_SHADOWS_ICON = 207, + SACRIFICE_ICON = 693, + SUFFERING_ICON = 9, + TORMENT_ICON = 173 +}; + +enum WarlockSpells +{ + BANISH_1 = 710, + CHALLENGING_HOWL_1 = 59671, + CHAOS_BOLT_1 = 50796, + CONFLAGRATE_1 = 17962, + CORRUPTION_1 = 172, + CREATE_FIRESTONE_1 = 6366, + CREATE_HEALTHSTONE_1 = 6201, + CREATE_SOULSTONE_1 = 693, + CREATE_SPELLSTONE_1 = 2362, + CURSE_OF_AGONY_1 = 980, + CURSE_OF_DOOM_1 = 603, + CURSE_OF_EXHAUSTION_1 = 18223, + CURSE_OF_THE_ELEMENTS_1 = 1490, + CURSE_OF_TONGUES_1 = 1714, + CURSE_OF_WEAKNESS_1 = 702, + DARK_PACT_1 = 18220, + DEATH_COIL_WARLOCK_1 = 6789, + DEMON_ARMOR_1 = 706, + DEMON_CHARGE_1 = 54785, + DEMON_SKIN_1 = 687, + DEMONIC_CIRCLE_SUMMON_1 = 48018, + DEMONIC_CIRCLE_TELEPORT_1 = 48020, + DEMONIC_EMPOWERMENT_1 = 47193, + DEMONIC_IMMOLATE_1 = 75445, + DETECT_INVISIBILITY_1 = 132, + DRAIN_LIFE_1 = 689, + DRAIN_MANA_1 = 5138, + DRAIN_SOUL_1 = 1120, + ENSLAVE_DEMON_1 = 1098, + EYE_OF_KILROGG_1 = 126, + FEAR_1 = 5782, + FEL_ARMOR_1 = 28176, + FEL_DOMINATION_1 = 18708, + HAUNT_1 = 48181, + HEALTH_FUNNEL_1 = 755, + HELLFIRE_1 = 1949, + HOWL_OF_TERROR_1 = 5484, + IMMOLATE_1 = 348, + IMMOLATION_AURA_1 = 50589, + INCINERATE_1 = 29722, + INFERNO_1 = 1122, + LIFE_TAP_1 = 1454, + METAMORPHOSIS_1 = 59672, + RAIN_OF_FIRE_1 = 5740, + RITUAL_OF_DOOM_1 = 18540, + RITUAL_OF_SOULS_1 = 29893, + RITUAL_OF_SUMMONING_1 = 698, + SEARING_PAIN_1 = 5676, + SEED_OF_CORRUPTION_1 = 27243, + SENSE_DEMONS_1 = 5500, + SHADOW_BOLT_1 = 686, + SHADOW_CLEAVE_1 = 50581, + SHADOW_WARD_1 = 6229, + SHADOWBURN_1 = 17877, + SHADOWFLAME_1 = 47897, + SHADOWFURY_1 = 30283, + SOUL_FIRE_1 = 6353, + SOUL_LINK_1 = 19028, + SOULSHATTER_1 = 29858, + SUMMON_FELGUARD_1 = 30146, + SUMMON_FELHUNTER_1 = 691, + SUMMON_IMP_1 = 688, + SUMMON_SUCCUBUS_1 = 712, + SUMMON_VOIDWALKER_1 = 697, + UNENDING_BREATH_1 = 5697, + UNSTABLE_AFFLICTION_1 = 30108 +}; + +//class Player; +class MANGOS_DLL_SPEC PlayerbotWarlockAI : PlayerbotClassAI +{ +public: + PlayerbotWarlockAI(Player* const bot, PlayerbotAI* const ai); + virtual ~PlayerbotWarlockAI(); + + // all combat actions go here + void DoNextCombatManeuver(Unit*); + + // all non combat actions go here, ex buffs, heals, rezzes + void DoNonCombatActions(); + + // buff a specific player, usually a real PC who is not in group + //void BuffPlayer(Player *target); + + void InitSpells(PlayerbotAI* const ai); + +private: + + // CURSES + uint32 CURSE_OF_WEAKNESS, + CURSE_OF_AGONY, + CURSE_OF_EXHAUSTION, + CURSE_OF_TONGUES, + CURSE_OF_THE_ELEMENTS, + CURSE_OF_DOOM; + + // AFFLICTION + uint32 CORRUPTION, + DRAIN_SOUL, + DRAIN_LIFE, + DRAIN_MANA, + LIFE_TAP, + UNSTABLE_AFFLICTION, + HAUNT, + SEED_OF_CORRUPTION, + DARK_PACT, + HOWL_OF_TERROR, + FEAR; + + // DESTRUCTION + uint32 SHADOW_BOLT, + IMMOLATE, + INCINERATE, + SEARING_PAIN, + CONFLAGRATE, + SOUL_FIRE, + SHADOWFURY, + CHAOS_BOLT, + SHADOWFLAME, + HELLFIRE, + RAIN_OF_FIRE, + SHADOWBURN; + + // DEMONOLOGY + uint32 DEMON_SKIN, + DEMON_ARMOR, + DEMONIC_EMPOWERMENT, + SHADOW_WARD, + FEL_ARMOR, + SOULSHATTER, + SOUL_LINK, + SOUL_LINK_AURA, + HEALTH_FUNNEL, + DETECT_INVISIBILITY, + CREATE_FIRESTONE, + CREATE_SOULSTONE, + CREATE_HEALTHSTONE; + + // DEMON SUMMON + uint32 SUMMON_IMP, + SUMMON_VOIDWALKER, + SUMMON_SUCCUBUS, + SUMMON_FELHUNTER, + SUMMON_FELGUARD; + + // DEMON SKILLS + uint32 BLOOD_PACT, + FIREBOLT, + FIRE_SHIELD, + ANGUISH, + CLEAVE, + INTERCEPT, + DEVOUR_MAGIC, + FEL_INTELLIGENCE, + SHADOW_BITE, + SPELL_LOCK, + LASH_OF_PAIN, + SEDUCTION, + SOOTHING_KISS, + CONSUME_SHADOWS, + SACRIFICE, + SUFFERING, + TORMENT; + + // first aid + uint32 RECENTLY_BANDAGED; + + // racial + uint32 ARCANE_TORRENT, + GIFT_OF_THE_NAARU, + STONEFORM, + ESCAPE_ARTIST, + EVERY_MAN_FOR_HIMSELF, + SHADOWMELD, + BLOOD_FURY, + WAR_STOMP, + BERSERKING, + WILL_OF_THE_FORSAKEN; + + uint32 SpellSequence, + LastSpellCurse, + LastSpellAffliction, + LastSpellDestruction; + + uint32 m_lastDemon; // Last demon entry used for spell initialization + uint32 m_demonOfChoice; // Preferred demon entry + bool m_isTempImp; // True if imp summoned temporarily until soul shard acquired for demon of choice. +}; + +#endif diff --git a/src/game/playerbot/PlayerbotWarriorAI.cpp b/src/game/playerbot/PlayerbotWarriorAI.cpp new file mode 100644 index 000000000..b14074bba --- /dev/null +++ b/src/game/playerbot/PlayerbotWarriorAI.cpp @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "PlayerbotWarriorAI.h" +#include "PlayerbotMgr.h" + +class PlayerbotAI; + +PlayerbotWarriorAI::PlayerbotWarriorAI(Player* const bot, PlayerbotAI* const ai): PlayerbotClassAI(bot, ai) +{ + InitSpells(ai); +} + +void PlayerbotWarriorAI::InitSpells(PlayerbotAI* const ai) +{ + BATTLE_STANCE = ai->initSpell(BATTLE_STANCE_1); //ARMS + CHARGE = ai->initSpell(CHARGE_1); //ARMS + OVERPOWER = ai->initSpell(OVERPOWER_1); // ARMS + HEROIC_STRIKE = ai->initSpell(HEROIC_STRIKE_1); //ARMS + REND = ai->initSpell(REND_1); //ARMS + THUNDER_CLAP = ai->initSpell(THUNDER_CLAP_1); //ARMS + HAMSTRING = ai->initSpell(HAMSTRING_1); //ARMS + MOCKING_BLOW = ai->initSpell(MOCKING_BLOW_1); //ARMS + RETALIATION = ai->initSpell(RETALIATION_1); //ARMS + SWEEPING_STRIKES = ai->initSpell(SWEEPING_STRIKES_1); //ARMS + MORTAL_STRIKE = ai->initSpell(MORTAL_STRIKE_1); //ARMS + BLADESTORM = ai->initSpell(BLADESTORM_1); //ARMS + HEROIC_THROW = ai->initSpell(HEROIC_THROW_1); //ARMS + SHATTERING_THROW = ai->initSpell(SHATTERING_THROW_1); //ARMS + BLOODRAGE = ai->initSpell(BLOODRAGE_1); //PROTECTION + DEFENSIVE_STANCE = ai->initSpell(DEFENSIVE_STANCE_1); //PROTECTION + DEVASTATE = ai->initSpell(DEVASTATE_1); //PROTECTION + SUNDER_ARMOR = ai->initSpell(SUNDER_ARMOR_1); //PROTECTION + TAUNT = ai->initSpell(TAUNT_1); //PROTECTION + SHIELD_BASH = ai->initSpell(SHIELD_BASH_1); //PROTECTION + REVENGE = ai->initSpell(REVENGE_1); //PROTECTION + SHIELD_BLOCK = ai->initSpell(SHIELD_BLOCK_1); //PROTECTION + DISARM = ai->initSpell(DISARM_1); //PROTECTION + SHIELD_WALL = ai->initSpell(SHIELD_WALL_1); //PROTECTION + SHIELD_SLAM = ai->initSpell(SHIELD_SLAM_1); //PROTECTION + VIGILANCE = ai->initSpell(VIGILANCE_1); //PROTECTION + DEVASTATE = ai->initSpell(DEVASTATE_1); //PROTECTION + SHOCKWAVE = ai->initSpell(SHOCKWAVE_1); //PROTECTION + CONCUSSION_BLOW = ai->initSpell(CONCUSSION_BLOW_1); //PROTECTION + SPELL_REFLECTION = ai->initSpell(SPELL_REFLECTION_1); //PROTECTION + LAST_STAND = ai->initSpell(LAST_STAND_1); //PROTECTION + BATTLE_SHOUT = ai->initSpell(BATTLE_SHOUT_1); //FURY + DEMORALIZING_SHOUT = ai->initSpell(DEMORALIZING_SHOUT_1); //FURY + CLEAVE = ai->initSpell(CLEAVE_1); //FURY + INTIMIDATING_SHOUT = ai->initSpell(INTIMIDATING_SHOUT_1); //FURY + EXECUTE = ai->initSpell(EXECUTE_1); //FURY + CHALLENGING_SHOUT = ai->initSpell(CHALLENGING_SHOUT_1); //FURY + SLAM = ai->initSpell(SLAM_1); //FURY + BERSERKER_STANCE = ai->initSpell(BERSERKER_STANCE_1); //FURY + INTERCEPT = ai->initSpell(INTERCEPT_1); //FURY + DEATH_WISH = ai->initSpell(DEATH_WISH_1); //FURY + BERSERKER_RAGE = ai->initSpell(BERSERKER_RAGE_1); //FURY + WHIRLWIND = ai->initSpell(WHIRLWIND_1); //FURY + PUMMEL = ai->initSpell(PUMMEL_1); //FURY + BLOODTHIRST = ai->initSpell(BLOODTHIRST_1); //FURY + RECKLESSNESS = ai->initSpell(RECKLESSNESS_1); //FURY + RAMPAGE = 0; // passive + HEROIC_FURY = ai->initSpell(HEROIC_FURY_1); //FURY + COMMANDING_SHOUT = ai->initSpell(COMMANDING_SHOUT_1); //FURY + ENRAGED_REGENERATION = ai->initSpell(ENRAGED_REGENERATION_1); //FURY + PIERCING_HOWL = ai->initSpell(PIERCING_HOWL_1); //FURY + + RECENTLY_BANDAGED = 11196; // first aid check + + // racial + GIFT_OF_THE_NAARU = ai->initSpell(GIFT_OF_THE_NAARU_WARRIOR); // draenei + STONEFORM = ai->initSpell(STONEFORM_ALL); // dwarf + ESCAPE_ARTIST = ai->initSpell(ESCAPE_ARTIST_ALL); // gnome + EVERY_MAN_FOR_HIMSELF = ai->initSpell(EVERY_MAN_FOR_HIMSELF_ALL); // human + SHADOWMELD = ai->initSpell(SHADOWMELD_ALL); // night elf + BLOOD_FURY = ai->initSpell(BLOOD_FURY_MELEE_CLASSES); // orc + WAR_STOMP = ai->initSpell(WAR_STOMP_ALL); // tauren + BERSERKING = ai->initSpell(BERSERKING_ALL); // troll + WILL_OF_THE_FORSAKEN = ai->initSpell(WILL_OF_THE_FORSAKEN_ALL); // undead +} +PlayerbotWarriorAI::~PlayerbotWarriorAI() {} + +bool PlayerbotWarriorAI::DoFirstCombatManeuver(Unit *pTarget) +{ + Player *m_bot = GetPlayerBot(); + PlayerbotAI *ai = GetAI(); + float fTargetDist = m_bot->GetDistance(pTarget); + + if (DEFENSIVE_STANCE > 0 && !m_bot->HasAura(DEFENSIVE_STANCE, EFFECT_INDEX_0) && ai->CastSpell(DEFENSIVE_STANCE)) + { + + return true; + } + else if (TAUNT > 0 && m_bot->HasAura(DEFENSIVE_STANCE, EFFECT_INDEX_0) && ai->CastSpell(TAUNT, pTarget)) + { + + return false; + } + else if (BATTLE_STANCE > 0 && !m_bot->HasAura(BATTLE_STANCE, EFFECT_INDEX_0) && ai->CastSpell(BATTLE_STANCE)) + { + + return true; + } + else if (BATTLE_STANCE > 0 && CHARGE > 0 && m_bot->HasAura(BATTLE_STANCE, EFFECT_INDEX_0)) + { + if (fTargetDist < 8.0f) + return false; + else if (fTargetDist > 25.0f) + return true; + else if (CHARGE > 0 && ai->CastSpell(CHARGE, pTarget)) + { + float x, y, z; + pTarget->GetContactPoint(m_bot, x, y, z, 3.666666f); + m_bot->Relocate(x, y, z); + + + return false; + } + } + + return false; +} + +void PlayerbotWarriorAI::DoNextCombatManeuver(Unit *pTarget) +{ + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + //ai->SetMovementTarget( PlayerbotAI::MOVEMENT_FOLLOW, m_master ); // dont want to melee mob + + // Damage Attacks + + ai->SetInFront(pTarget); + Unit* pVictim = pTarget->getVictim(); + float fTargetDist = m_bot->GetDistance(pTarget); + + // decide what stance to use + if (!m_bot->HasAura(DEFENSIVE_STANCE, EFFECT_INDEX_0) && ai->CastSpell(DEFENSIVE_STANCE)) + { + } + else if (!m_bot->HasAura(BATTLE_STANCE, EFFECT_INDEX_0) && ai->CastSpell(BATTLE_STANCE)) + { + } + + // get spell sequence + if (pTarget->IsNonMeleeSpellCasted(true)) + SpellSequence = WarriorSpellPreventing; + else if (m_bot->HasAura(BATTLE_STANCE, EFFECT_INDEX_0)) + SpellSequence = WarriorBattle; + else if (m_bot->HasAura(DEFENSIVE_STANCE, EFFECT_INDEX_0)) + SpellSequence = WarriorDefensive; + else if (m_bot->HasAura(BERSERKER_STANCE, EFFECT_INDEX_0)) + SpellSequence = WarriorBerserker; + + // do shouts, berserker rage, etc... + if (BERSERKER_RAGE > 0 && !m_bot->HasAura(BERSERKER_RAGE, EFFECT_INDEX_0) && ai->CastSpell(BERSERKER_RAGE)) + { + } + else if (DEMORALIZING_SHOUT > 0 && ai->GetRageAmount() >= 10 && !pTarget->HasAura(DEMORALIZING_SHOUT, EFFECT_INDEX_0) && ai->CastSpell(DEMORALIZING_SHOUT)) + { + } + else if (BATTLE_SHOUT > 0 && ai->GetRageAmount() >= 10 && !m_bot->HasAura(BATTLE_SHOUT, EFFECT_INDEX_0) && ai->CastSpell(BATTLE_SHOUT)) + { + } + + + switch (SpellSequence) + { + case WarriorSpellPreventing: + + if (SHIELD_BASH > 0 && ai->GetRageAmount() >= 10 && ai->CastSpell(SHIELD_BASH, pTarget)) + {} + else if (PUMMEL > 0 && ai->GetRageAmount() >= 10 && ai->CastSpell(PUMMEL, pTarget)) + {} + else if (SPELL_REFLECTION > 0 && ai->GetRageAmount() >= 15 && !m_bot->HasAura(SPELL_REFLECTION, EFFECT_INDEX_0) && ai->CastSpell(SPELL_REFLECTION, m_bot)) + {} + break; + + case WarriorBattle: + + if (EXECUTE > 0 && ai->GetRageAmount() >= 15 && pTarget->GetHealth() < pTarget->GetMaxHealth() * 0.2 && ai->CastSpell(EXECUTE, pTarget)) + {} + else if (LAST_STAND > 0 && !m_bot->HasAura(LAST_STAND, EFFECT_INDEX_0) && m_bot->GetHealth() < m_bot->GetMaxHealth() * 0.5 && ai->CastSpell(LAST_STAND, m_bot)) + {} + else if (BLOODRAGE > 0 && ai->GetRageAmount() < 50 && !m_bot->HasAura(BLOODRAGE, EFFECT_INDEX_0) && ai->CastSpell(BLOODRAGE, m_bot)) + {} + else if (DEATH_WISH > 0 && ai->GetRageAmount() >= 10 && !m_bot->HasAura(DEATH_WISH, EFFECT_INDEX_0) && ai->CastSpell(DEATH_WISH, m_bot)) + {} + else if (RETALIATION > 0 && pVictim == m_bot && !m_bot->HasAura(RETALIATION, EFFECT_INDEX_0) && ai->CastSpell(RETALIATION, m_bot)) + {} + else if (DEMORALIZING_SHOUT > 0 && ai->GetRageAmount() >= 10 && !pTarget->HasAura(DEMORALIZING_SHOUT, EFFECT_INDEX_0) && ai->CastSpell(DEMORALIZING_SHOUT, pTarget)) + {} + else if (SWEEPING_STRIKES > 0 && ai->GetRageAmount() >= 30 && !m_bot->HasAura(SWEEPING_STRIKES, EFFECT_INDEX_0) && ai->CastSpell(SWEEPING_STRIKES, m_bot)) + {} + else if (BLADESTORM > 0 && ai->GetRageAmount() >= 25 && pVictim == m_bot && !m_bot->HasAura(BLADESTORM, EFFECT_INDEX_0) && ai->CastSpell(BLADESTORM, pTarget)) + {} + else if (MORTAL_STRIKE > 0 && ai->GetRageAmount() >= 30 && !pTarget->HasAura(MORTAL_STRIKE, EFFECT_INDEX_0) && ai->CastSpell(MORTAL_STRIKE, pTarget)) + {} + else if (INTIMIDATING_SHOUT > 0 && ai->GetRageAmount() >= 25 && ai->CastSpell(INTIMIDATING_SHOUT, pTarget)) + {} + else if (THUNDER_CLAP > 0 && ai->GetRageAmount() >= 20 && pVictim == m_bot && !pTarget->HasAura(THUNDER_CLAP, EFFECT_INDEX_0) && ai->CastSpell(THUNDER_CLAP, pTarget)) + {} + else if (ENRAGED_REGENERATION > 0 && ai->GetRageAmount() >= 15 && !m_bot->HasAura(BERSERKER_RAGE, EFFECT_INDEX_0) && !m_bot->HasAura(ENRAGED_REGENERATION, EFFECT_INDEX_0) && m_bot->GetHealth() < m_bot->GetMaxHealth() * 0.5 && ai->CastSpell(ENRAGED_REGENERATION, m_bot)) + {} + else if (SHOCKWAVE > 0 && ai->GetRageAmount() >= 15 && pVictim == m_bot && !pTarget->HasAura(WAR_STOMP, EFFECT_INDEX_0) && !pTarget->HasAura(PIERCING_HOWL, EFFECT_INDEX_0) && !pTarget->HasAura(SHOCKWAVE, EFFECT_INDEX_0) && !pTarget->HasAura(CONCUSSION_BLOW, EFFECT_INDEX_0) && ai->CastSpell(SHOCKWAVE, pTarget)) + {} + else if (REND > 0 && ai->GetRageAmount() >= 10 && !pTarget->HasAura(REND, EFFECT_INDEX_0) && ai->CastSpell(REND, pTarget)) + {} + else if (HAMSTRING > 0 && ai->GetRageAmount() >= 10 && !pTarget->HasAura(HAMSTRING, EFFECT_INDEX_0) && ai->CastSpell(HAMSTRING, pTarget)) + {} + else if (CHALLENGING_SHOUT > 0 && ai->GetRageAmount() >= 5 && pVictim != m_bot && ai->GetHealthPercent() > 25 && !pTarget->HasAura(MOCKING_BLOW, EFFECT_INDEX_0) && !pTarget->HasAura(CHALLENGING_SHOUT, EFFECT_INDEX_0) && ai->CastSpell(CHALLENGING_SHOUT, pTarget)) + {} + else if (BLOODTHIRST > 0 && ai->GetRageAmount() >= 20 && !m_bot->HasAura(BLOODTHIRST, EFFECT_INDEX_0) && m_bot->GetHealth() < m_bot->GetMaxHealth() * 0.7 && ai->CastSpell(BLOODTHIRST, pTarget)) + {} + else if (CLEAVE > 0 && ai->GetRageAmount() >= 20 && ai->CastSpell(CLEAVE, pTarget)) + {} + else if (HEROIC_STRIKE > 0 && ai->GetRageAmount() >= 15 && ai->CastSpell(HEROIC_STRIKE, pTarget)) + {} + else if (CONCUSSION_BLOW > 0 && ai->GetRageAmount() >= 15 && !pTarget->HasAura(WAR_STOMP, EFFECT_INDEX_0) && !pTarget->HasAura(PIERCING_HOWL, EFFECT_INDEX_0) && !pTarget->HasAura(SHOCKWAVE, EFFECT_INDEX_0) && !pTarget->HasAura(CONCUSSION_BLOW, EFFECT_INDEX_0) && ai->CastSpell(CONCUSSION_BLOW, pTarget)) + {} + else if (SLAM > 0 && ai->GetRageAmount() >= 15 && ai->CastSpell(SLAM, pTarget)) + {} + else if (PIERCING_HOWL > 0 && ai->GetRageAmount() >= 10 && !pTarget->HasAura(WAR_STOMP, EFFECT_INDEX_0) && !pTarget->HasAura(PIERCING_HOWL, EFFECT_INDEX_0) && !pTarget->HasAura(SHOCKWAVE, EFFECT_INDEX_0) && !pTarget->HasAura(CONCUSSION_BLOW, EFFECT_INDEX_0) && ai->CastSpell(PIERCING_HOWL, pTarget)) + {} + else if (MOCKING_BLOW > 0 && ai->GetRageAmount() >= 10 && pVictim != m_bot && ai->GetHealthPercent() > 25 && !pTarget->HasAura(MOCKING_BLOW, EFFECT_INDEX_0) && !pTarget->HasAura(CHALLENGING_SHOUT, EFFECT_INDEX_0) && ai->CastSpell(MOCKING_BLOW, pTarget)) + {} + else if (OVERPOWER > 0 && ai->GetRageAmount() >= 5 && ai->CastSpell(OVERPOWER, pTarget)) + {} + else if (SUNDER_ARMOR > 0 && ai->CastSpell(SUNDER_ARMOR, pTarget)) + {} + else if (SHATTERING_THROW > 0 && !pTarget->HasAura(SHATTERING_THROW, EFFECT_INDEX_0) && ai->CastSpell(SHATTERING_THROW, pTarget)) + {} + else if (HEROIC_THROW > 0 && ai->CastSpell(HEROIC_THROW, pTarget)) + {} + else if (m_bot->getRace() == RACE_TAUREN && !pTarget->HasAura(WAR_STOMP, EFFECT_INDEX_0) && !pTarget->HasAura(PIERCING_HOWL, EFFECT_INDEX_0) && !pTarget->HasAura(SHOCKWAVE, EFFECT_INDEX_0) && !pTarget->HasAura(CONCUSSION_BLOW, EFFECT_INDEX_0) && ai->CastSpell(WAR_STOMP, pTarget)) + {} + else if (m_bot->getRace() == RACE_HUMAN && m_bot->hasUnitState(UNIT_STAT_STUNNED) || m_bot->HasAuraType(SPELL_AURA_MOD_FEAR) || m_bot->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED) || m_bot->HasAuraType(SPELL_AURA_MOD_CHARM) && ai->CastSpell(EVERY_MAN_FOR_HIMSELF, m_bot)) + {} + else if (m_bot->getRace() == RACE_UNDEAD && m_bot->HasAuraType(SPELL_AURA_MOD_FEAR) || m_bot->HasAuraType(SPELL_AURA_MOD_CHARM) && ai->CastSpell(WILL_OF_THE_FORSAKEN, m_bot)) + {} + else if (m_bot->getRace() == RACE_DWARF && m_bot->HasAuraState(AURA_STATE_DEADLY_POISON) && ai->CastSpell(STONEFORM, m_bot)) + {} + else if (m_bot->getRace() == RACE_GNOME && m_bot->hasUnitState(UNIT_STAT_STUNNED) || m_bot->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED) && ai->CastSpell(ESCAPE_ARTIST, m_bot)) + {} + else if (m_bot->getRace() == RACE_NIGHTELF && pVictim == m_bot && ai->GetHealthPercent() < 25 && !m_bot->HasAura(SHADOWMELD, EFFECT_INDEX_0) && ai->CastSpell(SHADOWMELD, m_bot)) + {} + else if (m_bot->getRace() == RACE_ORC && !m_bot->HasAura(BLOOD_FURY, EFFECT_INDEX_0) && ai->CastSpell(BLOOD_FURY, m_bot)) + {} + else if (m_bot->getRace() == RACE_TROLL && !m_bot->HasAura(BERSERKING, EFFECT_INDEX_0) && ai->CastSpell(BERSERKING, m_bot)) + {} + else if (m_bot->getRace() == RACE_DRAENEI && ai->GetHealthPercent() < 25 && !m_bot->HasAura(GIFT_OF_THE_NAARU, EFFECT_INDEX_0) && ai->CastSpell(GIFT_OF_THE_NAARU, m_bot)) + {} + break; + + case WarriorDefensive: + + if (DISARM > 0 && ai->GetRageAmount() >= 15 && !pTarget->HasAura(DISARM, EFFECT_INDEX_0) && ai->CastSpell(DISARM, pTarget)) + {} + else if (SUNDER_ARMOR > 0 && ai->GetRageAmount() >= 15 && ai->CastSpell(SUNDER_ARMOR, pTarget)) + {} + else if (REVENGE > 0 && ai->GetRageAmount() >= 5 && ai->CastSpell(REVENGE, pTarget)) + {} + else if (SHIELD_BLOCK > 0 && !m_bot->HasAura(SHIELD_BLOCK, EFFECT_INDEX_0) && ai->CastSpell(SHIELD_BLOCK, m_bot)) + {} + else if (SHIELD_WALL > 0 && !m_bot->HasAura(SHIELD_WALL, EFFECT_INDEX_0) && ai->CastSpell(SHIELD_WALL, m_bot)) + {} + break; + + case WarriorBerserker: + + if (WHIRLWIND > 0 && ai->GetRageAmount() >= 25 && ai->CastSpell(WHIRLWIND, pTarget)) + {} + break; + } + +} + +void PlayerbotWarriorAI::DoNonCombatActions() +{ + PlayerbotAI *ai = GetAI(); + if (!ai) + return; + + Player * m_bot = GetPlayerBot(); + if (!m_bot) + return; + + Player* m_master = ai->GetLeader(); + if (!m_master) + return; + + // TODO (by Runsttren): check if shout aura bot has is casted by this bot, + // otherwise cast other useful shout + // If the bot is protect talented, she/he needs stamina not attack power. + // With stance change can the shout change to. + // Inserted line to battle shout m_bot->HasAura( COMMANDING_SHOUT, EFFECT_INDEX_0) + // Natsukawa + if (((COMMANDING_SHOUT > 0 && !m_bot->HasAura(COMMANDING_SHOUT, EFFECT_INDEX_0)) || + (BATTLE_SHOUT > 0 && !m_bot->HasAura(BATTLE_SHOUT, EFFECT_INDEX_0))) && + ai->GetRageAmount() < 10 && BLOODRAGE > 0 && !m_bot->HasAura(BLOODRAGE, EFFECT_INDEX_0)) + // we do have a useful shout, no rage coming but can cast bloodrage... do it + ai->CastSpell(BLOODRAGE, m_bot); + else if (COMMANDING_SHOUT > 0 && !m_bot->HasAura(COMMANDING_SHOUT, EFFECT_INDEX_0)) + // use commanding shout now + ai->CastSpell(COMMANDING_SHOUT, m_bot); + else if (BATTLE_SHOUT > 0 && !m_bot->HasAura(BATTLE_SHOUT, EFFECT_INDEX_0) && !m_bot->HasAura(COMMANDING_SHOUT, EFFECT_INDEX_0)) + // use battle shout + ai->CastSpell(BATTLE_SHOUT, m_bot); + + // buff master with VIGILANCE + if (VIGILANCE > 0) + (!m_master->HasAura(VIGILANCE, EFFECT_INDEX_0) && ai->CastSpell(VIGILANCE, m_master)); + + // hp check + if (m_bot->getStandState() != UNIT_STAND_STATE_STAND) + m_bot->SetStandState(UNIT_STAND_STATE_STAND); + + Item* pItem = ai->FindFood(); + Item* fItem = ai->FindBandage(); + + if (pItem != NULL && ai->GetHealthPercent() < 30) + { + + ai->UseItem(pItem); + return; + } + else if (pItem == NULL && fItem != NULL && !m_bot->HasAura(RECENTLY_BANDAGED, EFFECT_INDEX_0) && ai->GetHealthPercent() < 70) + { + + ai->UseItem(fItem); + return; + } + else if (pItem == NULL && fItem == NULL && m_bot->getRace() == RACE_DRAENEI && !m_bot->HasAura(GIFT_OF_THE_NAARU, EFFECT_INDEX_0) && ai->GetHealthPercent() < 70) + { + + ai->CastSpell(GIFT_OF_THE_NAARU, m_bot); + return; + } +} // end DoNonCombatActions diff --git a/src/game/playerbot/PlayerbotWarriorAI.h b/src/game/playerbot/PlayerbotWarriorAI.h new file mode 100644 index 000000000..2a711d107 --- /dev/null +++ b/src/game/playerbot/PlayerbotWarriorAI.h @@ -0,0 +1,105 @@ +#ifndef _PlayerbotWarriorAI_H +#define _PlayerbotWarriorAI_H + +#include "PlayerbotClassAI.h" + +enum +{ + WarriorSpellPreventing, + WarriorBattle, + WarriorDefensive, + WarriorBerserker +}; + +enum WarriorSpells +{ + BATTLE_SHOUT_1 = 6673, + BATTLE_STANCE_1 = 2457, + BERSERKER_RAGE_1 = 18499, + BERSERKER_STANCE_1 = 2458, + BLADESTORM_1 = 46924, + BLOODRAGE_1 = 2687, + BLOODTHIRST_1 = 23881, + CHALLENGING_SHOUT_1 = 1161, + CHARGE_1 = 100, + CLEAVE_1 = 845, + COMMANDING_SHOUT_1 = 469, + CONCUSSION_BLOW_1 = 12809, + DEATH_WISH_1 = 12292, + DEFENSIVE_STANCE_1 = 71, + DEMORALIZING_SHOUT_1 = 1160, + DEVASTATE_1 = 20243, + DISARM_1 = 676, + ENRAGED_REGENERATION_1 = 55694, + EXECUTE_1 = 5308, + HAMSTRING_1 = 1715, + HEROIC_FURY_1 = 60970, + HEROIC_STRIKE_1 = 78, + HEROIC_THROW_1 = 57755, + INTERCEPT_1 = 20252, + INTERVENE_1 = 3411, + INTIMIDATING_SHOUT_1 = 5246, + LAST_STAND_1 = 12975, + MOCKING_BLOW_1 = 694, + MORTAL_STRIKE_1 = 12294, + OVERPOWER_1 = 7384, + PIERCING_HOWL_1 = 12323, + PUMMEL_1 = 6552, + RECKLESSNESS_1 = 1719, + REND_1 = 772, + RETALIATION_1 = 20230, + REVENGE_1 = 6572, + SHATTERING_THROW_1 = 64382, + SHIELD_BASH_1 = 72, + SHIELD_BLOCK_1 = 2565, + SHIELD_SLAM_1 = 23922, + SHIELD_WALL_1 = 871, + SHOCKWAVE_1 = 46968, + SLAM_1 = 1464, + SPELL_REFLECTION_1 = 23920, + SUNDER_ARMOR_1 = 7386, + SWEEPING_STRIKES_1 = 12328, + TAUNT_1 = 355, + THUNDER_CLAP_1 = 6343, + VICTORY_RUSH_1 = 34428, + VIGILANCE_1 = 50720, + WHIRLWIND_1 = 1680 +}; + +//class Player; + +class MANGOS_DLL_SPEC PlayerbotWarriorAI : PlayerbotClassAI +{ +public: + PlayerbotWarriorAI(Player* const bot, PlayerbotAI* const ai); + virtual ~PlayerbotWarriorAI(); + + // all combat actions go here + bool DoFirstCombatManeuver(Unit*); + void DoNextCombatManeuver(Unit*); + + // all non combat actions go here, ex buffs, heals, rezzes + void DoNonCombatActions(); + + void InitSpells(PlayerbotAI* const ai); + +private: + // ARMS + uint32 BATTLE_STANCE, CHARGE, HEROIC_STRIKE, REND, THUNDER_CLAP, HAMSTRING, MOCKING_BLOW, RETALIATION, SWEEPING_STRIKES, MORTAL_STRIKE, BLADESTORM, HEROIC_THROW, SHATTERING_THROW; + + // PROTECTION + uint32 DEFENSIVE_STANCE, BLOODRAGE, SUNDER_ARMOR, TAUNT, SHIELD_BASH, REVENGE, SHIELD_BLOCK, DISARM, SHIELD_WALL, SHIELD_SLAM, VIGILANCE, DEVASTATE, SHOCKWAVE, CONCUSSION_BLOW, SPELL_REFLECTION, LAST_STAND; + + // FURY + uint32 BERSERKER_STANCE, BATTLE_SHOUT, DEMORALIZING_SHOUT, OVERPOWER, CLEAVE, INTIMIDATING_SHOUT, EXECUTE, CHALLENGING_SHOUT, SLAM, INTERCEPT, DEATH_WISH, BERSERKER_RAGE, WHIRLWIND, PUMMEL, BLOODTHIRST, RECKLESSNESS, RAMPAGE, HEROIC_FURY, COMMANDING_SHOUT, ENRAGED_REGENERATION, PIERCING_HOWL; + + // first aid + uint32 RECENTLY_BANDAGED; + + // racial + uint32 ARCANE_TORRENT, GIFT_OF_THE_NAARU, STONEFORM, ESCAPE_ARTIST, EVERY_MAN_FOR_HIMSELF, SHADOWMELD, BLOOD_FURY, WAR_STOMP, BERSERKING, WILL_OF_THE_FORSAKEN; + + uint32 SpellSequence; +}; + +#endif diff --git a/src/mangosd/CMakeLists.txt b/src/mangosd/CMakeLists.txt index c237d0d68..eeb93bebd 100644 --- a/src/mangosd/CMakeLists.txt +++ b/src/mangosd/CMakeLists.txt @@ -24,6 +24,12 @@ endif() file(GLOB_RECURSE EXECUTABLE_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp *.h) +if(WIN32) + list(APPEND EXECUTABLE_SRCS + mangosd.rc + ) +endif() + include_directories( ${CMAKE_SOURCE_DIR}/src/shared ${CMAKE_SOURCE_DIR}/dep/include/gsoap @@ -64,9 +70,7 @@ if(WIN32) debug ${OPENSSL_DEBUG_LIBRARIES} ) if(PLATFORM MATCHES X86) - target_link_libraries(${EXECUTABLE_NAME} - debug ${WIN_DEBUGLIBS} - ) + target_link_libraries(${EXECUTABLE_NAME}) endif() endif() diff --git a/src/realmd/CMakeLists.txt b/src/realmd/CMakeLists.txt index 35c26c342..69d86fff1 100644 --- a/src/realmd/CMakeLists.txt +++ b/src/realmd/CMakeLists.txt @@ -24,6 +24,12 @@ endif() file(GLOB_RECURSE EXECUTABLE_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp *.h) +if(WIN32) + list(APPEND EXECUTABLE_SRCS + realmd.rc + ) +endif() + include_directories( ${CMAKE_SOURCE_DIR}/src/shared ${CMAKE_SOURCE_DIR}/src/framework @@ -57,9 +63,7 @@ if(WIN32) debug ${OPENSSL_DEBUG_LIBRARIES} ) if(PLATFORM MATCHES X86) - target_link_libraries(${EXECUTABLE_NAME} - debug ${WIN_DEBUGLIBS} - ) + target_link_libraries(${EXECUTABLE_NAME}) endif() endif() diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 8d4edc91a..32c0873c0 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "11379" + #define REVISION_NR "11401" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index 1fb5de4a3..a8d206552 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -1,6 +1,6 @@ #ifndef __REVISION_SQL_H__ #define __REVISION_SQL_H__ - #define REVISION_DB_CHARACTERS "required_11299_02_characters_pet_aura" - #define REVISION_DB_MANGOS "required_11348_01_mangos_spell_bonus_data" + #define REVISION_DB_CHARACTERS "required_11391_01_characters_auction" + #define REVISION_DB_MANGOS "required_11385_01_mangos_creature_template" #define REVISION_DB_REALMD "required_10008_01_realmd_realmd_db_version" #endif // __REVISION_SQL_H__ diff --git a/win/VC100/game.vcxproj b/win/VC100/game.vcxproj index eff731c91..5dab23ce8 100644 --- a/win/VC100/game.vcxproj +++ b/win/VC100/game.vcxproj @@ -517,6 +517,7 @@ + diff --git a/win/VC100/game.vcxproj.filters b/win/VC100/game.vcxproj.filters index 7be3f309b..a3c2e4688 100644 --- a/win/VC100/game.vcxproj.filters +++ b/win/VC100/game.vcxproj.filters @@ -538,6 +538,9 @@ World/Handlers + + World/Handlers +