diff options
author | Neo2003 <none@none> | 2008-10-09 08:42:22 -0500 |
---|---|---|
committer | Neo2003 <none@none> | 2008-10-09 08:42:22 -0500 |
commit | f736567a374e718574763f545b9cdea5d97d9ce0 (patch) | |
tree | 5b0a617c7ef4e5c6bd4aa1df6a9c7afe31757a46 /src | |
parent | e3079a1ba63ebe13a19e85db3f4e1e1a392a04ed (diff) |
[svn] * Updated to 6743 and 685
Moved language id used by Arena to a higher place to solve conflicts
Added the empty script folders
--HG--
branch : trunk
rename : 6731-680 => 6743-685
Diffstat (limited to 'src')
52 files changed, 23106 insertions, 22792 deletions
diff --git a/src/bindings/scripts/ScriptMgr.cpp b/src/bindings/scripts/ScriptMgr.cpp index e382b62e2e3..11d293ffe3d 100644 --- a/src/bindings/scripts/ScriptMgr.cpp +++ b/src/bindings/scripts/ScriptMgr.cpp @@ -1,2105 +1,2135 @@ -/* Copyright (C) 2006 - 2008 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> - * This program is free software licensed under GPL version 2 - * Please see the included DOCS/LICENSE.TXT for more information */ - -#include "precompiled.h" -#include "Config/Config.h" -#include "ProgressBar.h" -#include "Database/DBCStores.h" -#include "Database/DatabaseMysql.h" -#include "ObjectMgr.h" -#include "scripts/creature/mob_event_ai.h" - -#define _FULLVERSION "TrinityScript" - -#ifndef _TSCRIPTCONFVERSION -# define _TSCRIPTCONFVERSION 2008100201 -#endif //_TSCRIPTCONFVERSION - -#ifndef _TRINITY_SCRIPT_CONFIG -# define _TRINITY_SCRIPT_CONFIG "trinityscript.conf" -#endif //_TRINITY_SCRIPT_CONFIG - -//*** Global data *** -int nrscripts; -Script *m_scripts[MAX_SCRIPTS]; - -// Text Map for Event AI -HM_NAMESPACE::hash_map<uint32, std::string> EventAI_Text_Map; - -// Script Text used as says / yells / text emotes / whispers in scripts. -struct ScriptText -{ - uint32 SoundId; - uint8 Type; - uint32 Language; - std::string Text; -}; - -// Enums used by ScriptText::Type -enum ChatType -{ - CHAT_TYPE_SAY = 0, - CHAT_TYPE_YELL = 1, - CHAT_TYPE_TEXT_EMOTE = 2, - CHAT_TYPE_BOSS_EMOTE = 3, - CHAT_TYPE_WHISPER = 4, - CHAT_TYPE_BOSS_WHISPER = 5, -}; - -HM_NAMESPACE::hash_map<uint32, ScriptText> Script_TextMap; - -// Localized Text structure for storing locales (for EAI and SD2 scripts). -struct Localized_Text -{ - std::string locale_1; - std::string locale_2; - std::string locale_3; - std::string locale_4; - std::string locale_5; - std::string locale_6; - std::string locale_7; - std::string locale_8; -}; -HM_NAMESPACE::hash_map<uint32, Localized_Text> EventAI_LocalizedTextMap; -HM_NAMESPACE::hash_map<uint32, Localized_Text> Script_LocalizedTextMap; - -//*** End Global data *** - -//*** EventAI data *** -//Event AI structure. Used exclusivly by mob_event_ai.cpp (60 bytes each) -std::list<EventAI_Event> EventAI_Event_List; - -//Event AI summon structure. Used exclusivly by mob_event_ai.cpp. -HM_NAMESPACE::hash_map<uint32, EventAI_Summon> EventAI_Summon_Map; - -//Event AI error prevention structure. Used at runtime to prevent error log spam of same creature id. -//HM_NAMESPACE::hash_map<uint32, EventAI_CreatureError> EventAI_CreatureErrorPreventionList; - -uint32 EAI_ErrorLevel; - -//*** End EventAI data *** - -DatabaseMysql TScriptDB; -Config TScriptConfig; -uint32 Locale; - -void FillSpellSummary(); - -// -- Scripts to be added -- - -// -- Areatrigger -- -extern void AddSC_areatrigger_scripts(); - -// -- Boss -- -extern void AddSC_boss_emeriss(); -extern void AddSC_boss_taerar(); -extern void AddSC_boss_ysondre(); - -// -- Creature -- -extern void AddSC_mob_event(); -extern void AddSC_generic_creature(); - -// -- Custom -- -extern void AddSC_custom_example(); -extern void AddSC_custom_gossip_codebox(); -extern void AddSC_test(); - -// -- GO -- -extern void AddSC_go_scripts(); - -// -- Guard -- -extern void AddSC_guards(); - -// -- Honor -- - -// -- Item -- -extern void AddSC_item_scripts(); -extern void AddSC_item_test(); - -// -- NPC -- -extern void AddSC_npc_professions(); -extern void AddSC_npcs_special(); - -// -- Servers -- - -//-------------------- -//------ ZONE -------- - -//Alterac Mountains -extern void AddSC_alterac_mountains(); - -//Arathi Highlands -//Ashenvale Forest -//Aunchindoun -//--Auchenai Crypts -extern void AddSC_boss_exarch_maladaar(); -//--Mana Tombs -extern void AddSC_boss_nexusprince_shaffar(); -extern void AddSC_boss_pandemonius(); - -//--Sekketh Halls -extern void AddSC_boss_darkweaver_syth(); -extern void AddSC_boss_talon_king_ikiss(); -extern void AddSC_instance_sethekk_halls(); - -//--Shadow Labyrinth -extern void AddSC_boss_ambassador_hellmaw(); -extern void AddSC_boss_blackheart_the_inciter(); -extern void AddSC_boss_grandmaster_vorpil(); -extern void AddSC_boss_murmur(); -extern void AddSC_instance_shadow_labyrinth(); - -//Azshara -extern void AddSC_boss_azuregos(); -extern void AddSC_azshara(); - -//Azuremyst Isle -extern void AddSC_azuremyst_isle(); - -//Badlands -//Barrens -extern void AddSC_the_barrens(); - -//Black Temple -extern void AddSC_black_temple(); -extern void AddSC_boss_illidan(); -extern void AddSC_boss_shade_of_akama(); -extern void AddSC_boss_supremus(); -extern void AddSC_boss_gurtogg_bloodboil(); -extern void AddSC_boss_mother_shahraz(); -extern void AddSC_boss_reliquary_of_souls(); -extern void AddSC_boss_teron_gorefiend(); -extern void AddSC_boss_najentus(); -extern void AddSC_boss_illidari_council(); -extern void AddSC_instance_black_temple(); - -//Blackfathom Depths -//Blackrock Depths -extern void AddSC_blackrock_depths(); -extern void AddSC_boss_ambassador_flamelash(); -extern void AddSC_boss_angerrel(); -extern void AddSC_boss_anubshiah(); -extern void AddSC_boss_doomrel(); -extern void AddSC_boss_doperel(); -extern void AddSC_boss_draganthaurissan(); -extern void AddSC_boss_general_angerforge(); -extern void AddSC_boss_gloomrel(); -extern void AddSC_boss_gorosh_the_dervish(); -extern void AddSC_boss_grizzle(); -extern void AddSC_boss_haterel(); -extern void AddSC_boss_high_interrogator_gerstahn(); -extern void AddSC_boss_magmus(); -extern void AddSC_boss_moira_bronzebeard(); -extern void AddSC_boss_seethrel(); -extern void AddSC_boss_vilerel(); - -//Blackrock Spire -extern void AddSC_boss_drakkisath(); -extern void AddSC_boss_halycon(); -extern void AddSC_boss_highlordomokk(); -extern void AddSC_boss_mothersmolderweb(); -extern void AddSC_boss_overlordwyrmthalak(); -extern void AddSC_boss_shadowvosh(); -extern void AddSC_boss_thebeast(); -extern void AddSC_boss_warmastervoone(); -extern void AddSC_boss_quatermasterzigris(); -extern void AddSC_boss_pyroguard_emberseer(); -extern void AddSC_boss_gyth(); -extern void AddSC_boss_rend_blackhand(); - -//Blackwing lair -extern void AddSC_boss_razorgore(); -extern void AddSC_boss_vael(); -extern void AddSC_boss_broodlord(); -extern void AddSC_boss_firemaw(); -extern void AddSC_boss_ebonroc(); -extern void AddSC_boss_flamegor(); -extern void AddSC_boss_chromaggus(); -extern void AddSC_boss_nefarian(); -extern void AddSC_boss_victor_nefarius(); - -//Blade's Edge Mountains -extern void AddSC_blades_edge_mountains(); - -//Blasted lands -extern void AddSC_boss_kruul(); -extern void AddSC_blasted_lands(); - -//Bloodmyst Isle -extern void AddSC_bloodmyst_isle(); - -//Burning steppes -extern void AddSC_burning_steppes(); - -//Caverns of Time -//--Battle for Mt. Hyjal -extern void AddSC_hyjal(); -extern void AddSC_boss_archimonde(); -extern void AddSC_instance_mount_hyjal(); - -//--Old Hillsbrad -extern void AddSC_boss_captain_skarloc(); -extern void AddSC_boss_epoch_hunter(); -extern void AddSC_boss_lieutenant_drake(); -extern void AddSC_instance_old_hillsbrad(); -extern void AddSC_old_hillsbrad(); - -//--The Dark Portal -extern void AddSC_boss_aeonus(); -extern void AddSC_boss_chrono_lord_deja(); -extern void AddSC_boss_temporus(); - -//Coilfang Resevoir -//--Serpent Shrine Cavern -extern void AddSC_boss_fathomlord_karathress(); -extern void AddSC_boss_hydross_the_unstable(); -extern void AddSC_boss_lady_vashj(); -extern void AddSC_boss_leotheras_the_blind(); -extern void AddSC_boss_morogrim_tidewalker(); -extern void AddSC_instance_serpentshrine_cavern(); - -//--Slave Pens - -//--Steam Vault -extern void AddSC_boss_hydromancer_thespia(); -extern void AddSC_boss_mekgineer_steamrigger(); -extern void AddSC_boss_warlord_kalithresh(); -extern void AddSC_instance_steam_vault(); - -//--Underbog -extern void AddSC_boss_hungarfen(); - -//Darkshore -//Darnassus -//Deadmines -//Deadwind pass -//Desolace -//Dire Maul -//Dun Morogh -extern void AddSC_dun_morogh(); - -//Durotar -//Duskwood -//Dustwallow marsh -extern void AddSC_dustwallow_marsh(); - -//Eversong Woods -extern void AddSC_eversong_woods(); - -//Exodar -//Eastern Plaguelands -extern void AddSC_eastern_plaguelands(); - -//Elwynn Forest -extern void AddSC_elwynn_forest(); - -//Felwood -extern void AddSC_felwood(); - -//Feralas -extern void AddSC_feralas(); - -//Ghostlands -extern void AddSC_ghostlands(); - -//Gnomeregan -//Gruul's Lair -extern void AddSC_boss_gruul(); -extern void AddSC_boss_high_king_maulgar(); -extern void AddSC_instance_gruuls_lair(); - -//Hellfire Citadel -//--Blood Furnace -extern void AddSC_boss_broggok(); -extern void AddSC_boss_kelidan_the_breaker(); -extern void AddSC_boss_the_maker(); - -//--Magtheridon's Lair -extern void AddSC_boss_magtheridon(); -extern void AddSC_instance_magtheridons_lair(); - -//--Shattered Halls -extern void AddSC_boss_grand_warlock_nethekurse(); -extern void AddSC_boss_warbringer_omrogg(); -extern void AddSC_instance_shattered_halls(); - -//--Ramparts -extern void AddSC_boss_watchkeeper_gargolmar(); -extern void AddSC_boss_omor_the_unscarred(); - -//Hellfire Peninsula -extern void AddSC_boss_doomlordkazzak(); -extern void AddSC_hellfire_peninsula(); - -//Hillsbrad Foothills -//Hinterlands -//Ironforge -extern void AddSC_ironforge(); - -//Isle of Quel'Danas -extern void AddSC_isle_of_queldanas(); - -//Karazhan -extern void AddSC_boss_attumen(); -extern void AddSC_boss_curator(); -extern void AddSC_boss_maiden_of_virtue(); -extern void AddSC_boss_shade_of_aran(); -extern void AddSC_boss_malchezaar(); -extern void AddSC_boss_terestian_illhoof(); -extern void AddSC_netherspite_infernal(); -extern void AddSC_boss_moroes(); -extern void AddSC_bosses_opera(); -extern void AddSC_instance_karazhan(); -extern void AddSC_karazhan(); - -//Loch Modan -extern void AddSC_loch_modan(); - -//Lower Blackrock Spire - -// Magister's Terrace -extern void AddSC_boss_felblood_kaelthas(); -extern void AddSC_boss_selin_fireheart(); -extern void AddSC_boss_vexallus(); -extern void AddSC_boss_priestess_delrissa(); -extern void AddSC_instance_magisters_terrace(); - -//Maraudon -extern void AddSC_boss_celebras_the_cursed(); -extern void AddSC_boss_landslide(); -extern void AddSC_boss_noxxion(); -extern void AddSC_boss_ptheradras(); - -//Molten core -extern void AddSC_boss_lucifron(); -extern void AddSC_boss_magmadar(); -extern void AddSC_boss_gehennas(); -extern void AddSC_boss_garr(); -extern void AddSC_boss_baron_geddon(); -extern void AddSC_boss_shazzrah(); -extern void AddSC_boss_golemagg(); -extern void AddSC_boss_sulfuron(); -extern void AddSC_boss_majordomo(); -extern void AddSC_boss_ragnaros(); -extern void AddSC_instance_molten_core(); -extern void AddSC_molten_core(); - -//Moonglade -extern void AddSC_moonglade(); - -//Mulgore -extern void AddSC_mulgore(); - -//Nagrand -extern void AddSC_nagrand(); - -//Naxxramas -extern void AddSC_boss_anubrekhan(); -extern void AddSC_boss_maexxna(); -extern void AddSC_boss_patchwerk(); -extern void AddSC_boss_razuvious(); -extern void AddSC_boss_highlord_mograine(); -extern void AddSC_boss_lady_blaumeux(); -extern void AddSC_boss_sir_zeliek(); -extern void AddSC_boss_thane_korthazz(); -extern void AddSC_boss_kelthuzad(); -extern void AddSC_boss_faerlina(); -extern void AddSC_boss_loatheb(); -extern void AddSC_boss_noth(); -extern void AddSC_boss_gluth(); -extern void AddSC_boss_sapphiron(); - -//Netherstorm -extern void AddSC_netherstorm(); - -//Onyxia's Lair -extern void AddSC_boss_onyxia(); - -//Orgrimmar -extern void AddSC_orgrimmar(); - -//Ragefire Chasm -//Razorfen Downs -extern void AddSC_boss_amnennar_the_coldbringer(); - -//Redridge Mountains -//Ruins of Ahn'Qiraj -//Scarlet Monastery -extern void AddSC_boss_arcanist_doan(); -extern void AddSC_boss_azshir_the_sleepless(); -extern void AddSC_boss_bloodmage_thalnos(); -extern void AddSC_boss_herod(); -extern void AddSC_boss_high_inquisitor_fairbanks(); -extern void AddSC_boss_high_inquisitor_whitemane(); -extern void AddSC_boss_houndmaster_loksey(); -extern void AddSC_boss_interrogator_vishas(); -extern void AddSC_boss_scarlet_commander_mograine(); -extern void AddSC_boss_scorn(); - -//Scholomance -extern void AddSC_boss_darkmaster_gandling(); -extern void AddSC_boss_death_knight_darkreaver(); -extern void AddSC_boss_theolenkrastinov(); -extern void AddSC_boss_illuciabarov(); -extern void AddSC_boss_instructormalicia(); -extern void AddSC_boss_jandicebarov(); -extern void AddSC_boss_kormok(); -extern void AddSC_boss_lordalexeibarov(); -extern void AddSC_boss_lorekeeperpolkelt(); -extern void AddSC_boss_rasfrost(); -extern void AddSC_boss_theravenian(); -extern void AddSC_boss_vectus(); -extern void AddSC_instance_scholomance(); - -//Searing gorge -extern void AddSC_searing_gorge(); - -//Shadowfang keep -extern void AddSC_shadowfang_keep(); -extern void AddSC_instance_shadowfang_keep(); - -//Shadowmoon Valley -extern void AddSC_boss_doomwalker(); -extern void AddSC_shadowmoon_valley(); - -//Shattrath -extern void AddSC_shattrath_city(); - -//Silithus -extern void AddSC_silithus(); - -//Silvermoon -extern void AddSC_silvermoon_city(); - -//Silverpine forest -extern void AddSC_silverpine_forest(); - -//Stockade -//Stonetalon mountains -extern void AddSC_stonetalon_mountains(); - -//Stormwind City -extern void AddSC_stormwind_city(); - -//Stranglethorn Vale -extern void AddSC_stranglethorn_vale(); - -//Stratholme -extern void AddSC_boss_magistrate_barthilas(); -extern void AddSC_boss_maleki_the_pallid(); -extern void AddSC_boss_nerubenkan(); -extern void AddSC_boss_cannon_master_willey(); -extern void AddSC_boss_baroness_anastari(); -extern void AddSC_boss_ramstein_the_gorger(); -extern void AddSC_boss_timmy_the_cruel(); -extern void AddSC_boss_postmaster_malown(); -extern void AddSC_boss_baron_rivendare(); -extern void AddSC_boss_dathrohan_balnazzar(); -extern void AddSC_boss_order_of_silver_hand(); -extern void AddSC_instance_stratholme(); -extern void AddSC_stratholme(); - -//Sunken Temple -//Tanaris -extern void AddSC_tanaris(); - -//Teldrassil -//Tempest Keep -//--Arcatraz -extern void AddSC_arcatraz(); -extern void AddSC_boss_harbinger_skyriss(); -extern void AddSC_instance_arcatraz(); - -//--Botanica -extern void AddSC_boss_high_botanist_freywinn(); -extern void AddSC_boss_laj(); -extern void AddSC_boss_warp_splinter(); - -//--The Eye -extern void AddSC_boss_kaelthas(); -extern void AddSC_boss_void_reaver(); -extern void AddSC_boss_high_astromancer_solarian(); -extern void AddSC_instance_the_eye(); -extern void AddSC_the_eye(); - -//--The Mechanar -extern void AddSC_boss_gatewatcher_iron_hand(); -extern void AddSC_boss_nethermancer_sepethrea(); - -//Temple of ahn'qiraj -extern void AddSC_boss_cthun(); -extern void AddSC_boss_fankriss(); -extern void AddSC_boss_huhuran(); -extern void AddSC_bug_trio(); -extern void AddSC_boss_sartura(); -extern void AddSC_boss_skeram(); -extern void AddSC_boss_twinemperors(); -extern void AddSC_mob_anubisath_sentinel(); -extern void AddSC_instance_temple_of_ahnqiraj(); - -//Terokkar Forest -extern void AddSC_terokkar_forest(); - -//Thousand Needles -//Thunder Bluff -extern void AddSC_thunder_bluff(); - -//Tirisfal Glades -extern void AddSC_tirisfal_glades(); - -//Uldaman -extern void AddSC_boss_ironaya(); -extern void AddSC_uldaman(); - -//Undercity -extern void AddSC_undercity(); - -//Un'Goro Crater -//Upper blackrock spire -//Wailing caverns - -//Western plaguelands -extern void AddSC_western_plaguelands(); - -//Westfall -//Wetlands -//Winterspring -extern void AddSC_winterspring(); - -//Zangarmarsh -extern void AddSC_zangarmarsh(); - -//Zul'Farrak -//Zul'Gurub -extern void AddSC_boss_jeklik(); -extern void AddSC_boss_venoxis(); -extern void AddSC_boss_marli(); -extern void AddSC_boss_mandokir(); -extern void AddSC_boss_gahzranka(); -extern void AddSC_boss_thekal(); -extern void AddSC_boss_arlokk(); -extern void AddSC_boss_jindo(); -extern void AddSC_boss_hakkar(); -extern void AddSC_boss_grilek(); -extern void AddSC_boss_hazzarah(); -extern void AddSC_boss_renataki(); -extern void AddSC_boss_wushoolay(); -extern void AddSC_instance_zulgurub(); -//Zul'Aman -extern void AddSC_boss_janalai(); -extern void AddSC_boss_nalorakk(); -extern void AddSC_instance_zulaman(); -extern void AddSC_zulaman(); - -// ------------------- -void LoadDatabase() -{ - //Get db string from file - char const* dbstring = NULL; - if(!TScriptConfig.GetString("TScriptDatabaseInfo", &dbstring)) - error_log("TSCR: Missing Trinity Script Database Info in configuration file"); - - //Initilize connection to DB - if(!dbstring || !TScriptDB.Initialize(dbstring)) - error_db_log("TSCR: Unable to connect to Database"); - else - { - //***Preform all DB queries here*** - QueryResult *result; - - //Get Version information - result = TScriptDB.PQuery("SELECT `version`" - "FROM `script_db_version`"); - - if (result) - { - Field *fields = result->Fetch(); - outstring_log(" "); - outstring_log("TSCR: Database version is: %s", fields[0].GetString()); - outstring_log(" "); - delete result; - - }else error_db_log("TSCR: Missing script_db_version information."); - - // Drop existing Event AI Localized Text hash map - EventAI_LocalizedTextMap.clear(); - - // Gather EventAI Localized Texts - result = TScriptDB.PQuery("SELECT `id`,`locale_1`,`locale_2`,`locale_3`,`locale_4`,`locale_5`,`locale_6`,`locale_7`,`locale_8`" - "FROM `eventai_localized_texts`"); - - if(result) - { - outstring_log("TSCR: Loading EAI Localized Texts...."); - barGoLink bar(result->GetRowCount()); - uint32 count = 0; - - do - { - Localized_Text temp; - bar.step(); - - Field *fields = result->Fetch(); - - uint32 i = fields[0].GetInt32(); - - temp.locale_1 = fields[1].GetString(); - temp.locale_2 = fields[2].GetString(); - temp.locale_3 = fields[3].GetString(); - temp.locale_4 = fields[4].GetString(); - temp.locale_5 = fields[5].GetString(); - temp.locale_6 = fields[6].GetString(); - temp.locale_7 = fields[7].GetString(); - temp.locale_8 = fields[8].GetString(); - - EventAI_LocalizedTextMap[i] = temp; - ++count; - - }while(result->NextRow()); - - delete result; - - outstring_log(""); - outstring_log("TSCR: Loaded %u EventAI Localized Texts", count); - }else outstring_log("TSCR: WARNING >> Loaded 0 EventAI Localized Texts. Database table `eventai_localized_texts` is empty"); - - // Drop Existing Script Localized Text Hash Map - Script_LocalizedTextMap.clear(); - - // Gather Script Localized Texts - result = TScriptDB.PQuery("SELECT `id`,`locale_1`,`locale_2`,`locale_3`,`locale_4`,`locale_5`,`locale_6`,`locale_7`,`locale_8`" - "FROM `script_localized_texts`"); - - if(result) - { - outstring_log("TSCR: Loading Script Localized Texts...."); - barGoLink bar(result->GetRowCount()); - uint32 count = 0; - - do - { - Localized_Text temp; - bar.step(); - - Field *fields = result->Fetch(); - - uint32 i = fields[0].GetInt32(); - - temp.locale_1 = fields[1].GetString(); - temp.locale_2 = fields[2].GetString(); - temp.locale_3 = fields[3].GetString(); - temp.locale_4 = fields[4].GetString(); - temp.locale_5 = fields[5].GetString(); - temp.locale_6 = fields[6].GetString(); - temp.locale_7 = fields[7].GetString(); - temp.locale_8 = fields[8].GetString(); - - Script_LocalizedTextMap[i] = temp; - ++count; - - }while(result->NextRow()); - - delete result; - - outstring_log(""); - outstring_log("TSCR: Loaded %u Script Localized Texts", count); - }else outstring_log("TSCR: WARNING >> Loaded 0 Script Localized Texts. Database table `script_localized_texts` is empty"); - - //Drop existing EventAI Text hash map - EventAI_Text_Map.clear(); - - //Gather EventAI Text Entries - result = TScriptDB.PQuery("SELECT `id`,`text` FROM `eventai_texts`"); - - if (result) - { - outstring_log( "TSCR: Loading EventAI_Texts..."); - barGoLink bar(result->GetRowCount()); - uint32 Count = 0; - - do - { - bar.step(); - Field *fields = result->Fetch(); - - uint32 i = fields[0].GetInt32(); - - std::string text = fields[1].GetString(); - - if (!strlen(text.c_str())) - error_db_log("TSCR: EventAI text %u is empty", i); - - EventAI_Text_Map[i] = text; - ++Count; - - }while (result->NextRow()); - - delete result; - - outstring_log(""); - outstring_log("TSCR: >> Loaded %u EventAI_Texts", Count); - - }else outstring_log("TSCR: WARNING >> Loaded 0 EventAI_Texts. DB table `EventAI_Texts` is empty."); - - //Gather event data - result = TScriptDB.PQuery("SELECT `id`,`position_x`,`position_y`,`position_z`,`orientation`,`spawntimesecs`" - "FROM `eventai_summons`"); - - //Drop Existing EventSummon Map - EventAI_Summon_Map.clear(); - - if (result) - { - outstring_log( "TSCR: Loading EventAI_Summons..."); - barGoLink bar(result->GetRowCount()); - uint32 Count = 0; - - do - { - bar.step(); - Field *fields = result->Fetch(); - - EventAI_Summon temp; - - uint32 i = fields[0].GetUInt32(); - temp.position_x = fields[1].GetFloat(); - temp.position_y = fields[2].GetFloat(); - temp.position_z = fields[3].GetFloat(); - temp.orientation = fields[4].GetFloat(); - temp.SpawnTimeSecs = fields[5].GetUInt32(); - - //Add to map - EventAI_Summon_Map[i] = temp; - ++Count; - - }while (result->NextRow()); - - delete result; - outstring_log(""); - outstring_log("TSCR: >> Loaded %u EventAI_Summons", Count); - - }else outstring_log("TSCR: WARNING >> Loaded 0 EventAI_Summons. DB table `EventAI_Summons` is empty."); - - //Gather event data - result = TScriptDB.PQuery("SELECT `id`,`creature_id`,`event_type`,`event_inverse_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`action1_type`,`action1_param1`,`action1_param2`,`action1_param3`,`action2_type`,`action2_param1`,`action2_param2`,`action2_param3`,`action3_type`,`action3_param1`,`action3_param2`,`action3_param3`" - "FROM `eventai_scripts`"); - - //Drop Existing EventAI List - EventAI_Event_List.clear(); - - if (result) - { - outstring_log( "TSCR: Loading EventAI_Scripts..."); - barGoLink bar(result->GetRowCount()); - uint32 Count = 0; - - do - { - bar.step(); - Field *fields = result->Fetch(); - - EventAI_Event temp; - - temp.event_id = fields[0].GetUInt32(); - uint32 i = temp.event_id; - temp.creature_id = fields[1].GetUInt32(); - temp.event_type = fields[2].GetUInt16(); - temp.event_inverse_phase_mask = fields[3].GetUInt32(); - temp.event_chance = fields[4].GetUInt8(); - temp.event_flags = fields[5].GetUInt8(); - temp.event_param1 = fields[6].GetUInt32(); - temp.event_param2 = fields[7].GetUInt32(); - temp.event_param3 = fields[8].GetUInt32(); - temp.event_param4 = fields[9].GetUInt32(); - - //Report any errors in event - if (temp.event_type >= EVENT_T_END) - error_db_log("TSCR: Event %u has incorrect event type. Maybe DB requires updated version of SD2.", i); - - //No chance of this event occuring - if (temp.event_chance == 0) - error_db_log("TSCR: Event %u has 0 percent chance. Event will never trigger!", i); - //Chance above 100, force it to be 100 - if (temp.event_chance > 100) - { - error_db_log("TSCR: Creature %u are using event %u with more than 100 percent chance. Adjusting to 100 percent.", temp.creature_id, i); - temp.event_chance = 100; - } - - //Individual event checks - switch (temp.event_type) - { - case EVENT_T_HP: - case EVENT_T_MANA: - case EVENT_T_TARGET_HP: - { - if (temp.event_param2 > 100) - error_db_log("TSCR: Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i); - - if (temp.event_param1 <= temp.event_param2) - error_db_log("TSCR: Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp.creature_id, i); - - if (temp.event_flags & EFLAG_REPEATABLE && !temp.event_param3 && !temp.event_param4) - { - error_db_log("TSCR: Creature %u has param3 and param4=0 (RepeatMin/RepeatMax) but cannot be repeatable without timers. Removing EFLAG_REPEATABLE for event %u.", temp.creature_id, i); - temp.event_flags &= ~EFLAG_REPEATABLE; - } - } - break; - - case EVENT_T_SPELLHIT: - { - if (temp.event_param1) - { - SpellEntry const* pSpell = GetSpellStore()->LookupEntry(temp.event_param1); - if (!pSpell) - { - error_db_log("TSCR: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.event_param1, i); - continue; - } - - if (temp.event_param2_s != -1 && temp.event_param2 != pSpell->SchoolMask) - error_db_log("TSCR: Creature %u has param1(spellId %u) but param2 is not -1 and not equal to spell's school mask. Event %u can never trigger.", temp.creature_id, temp.event_param1, i); - } - - //TODO: fix this system with SPELL_SCHOOL_MASK. Current complicate things, using int32(-1) instead of just 0 - //SPELL_SCHOOL_MASK_NONE = 0 and does not exist, thus it can not ever trigger or be used in SpellHit() - if (temp.event_param2_s != -1 && temp.event_param2_s > SPELL_SCHOOL_MASK_ALL) - error_db_log("TSCR: Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.event_param2, i); - - if (temp.event_param4 < temp.event_param3) - error_db_log("TSCR: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - } - break; - - case EVENT_T_RANGE: - case EVENT_T_OOC_LOS: - case EVENT_T_FRIENDLY_HP: - case EVENT_T_FRIENDLY_IS_CC: - case EVENT_T_FRIENDLY_MISSING_BUFF: - { - if (temp.event_param4 < temp.event_param3) - error_db_log("TSCR: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - } - break; - - case EVENT_T_TIMER: - case EVENT_T_TIMER_OOC: - { - if (temp.event_param2 < temp.event_param1) - error_db_log("TSCR: Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp.creature_id, i); - - if (temp.event_param4 < temp.event_param3) - error_db_log("TSCR: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - } - break; - - case EVENT_T_KILL: - case EVENT_T_TARGET_CASTING: - { - if (temp.event_param2 < temp.event_param1) - error_db_log("TSCR: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - } - break; - - case EVENT_T_AGGRO: - case EVENT_T_DEATH: - case EVENT_T_EVADE: - case EVENT_T_SPAWNED: - { - if (temp.event_flags & EFLAG_REPEATABLE) - { - error_db_log("TSCR: Creature %u has EFLAG_REPEATABLE set. Event can never be repeatable. Removing flag for event %u.", temp.creature_id, i); - temp.event_flags &= ~EFLAG_REPEATABLE; - } - } - break; - }; - - for (uint32 j = 0; j < MAX_ACTIONS; j++) - { - temp.action[j].type = fields[10+(j*4)].GetUInt16(); - temp.action[j].param1 = fields[11+(j*4)].GetUInt32(); - temp.action[j].param2 = fields[12+(j*4)].GetUInt32(); - temp.action[j].param3 = fields[13+(j*4)].GetUInt32(); - - //Report any errors in actions - switch (temp.action[j].type) - { - case ACTION_T_SAY: - case ACTION_T_YELL: - case ACTION_T_TEXTEMOTE: - if (GetEventAIText(temp.action[j].param1) == DEFAULT_TEXT) - error_db_log("TSCR: Event %u Action %u refrences missing Localized_Text entry", i, j+1); - break; - - case ACTION_T_SOUND: - if (!GetSoundEntriesStore()->LookupEntry(temp.action[j].param1)) - error_db_log("TSCR: Event %u Action %u uses non-existant SoundID %u.", i, j+1, temp.action[j].param1); - break; - - case ACTION_T_RANDOM_SAY: - case ACTION_T_RANDOM_YELL: - case ACTION_T_RANDOM_TEXTEMOTE: - if ((temp.action[j].param1 != 0xffffffff && GetEventAIText(temp.action[j].param1) == DEFAULT_TEXT) || - (temp.action[j].param2 != 0xffffffff && GetEventAIText(temp.action[j].param2) == DEFAULT_TEXT) || - (temp.action[j].param3 != 0xffffffff && GetEventAIText(temp.action[j].param3) == DEFAULT_TEXT)) - error_db_log("TSCR: Event %u Action %u refrences missing Localized_Text entry", i, j+1); - break; - - case ACTION_T_CAST: - { - if (!GetSpellStore()->LookupEntry(temp.action[j].param1)) - error_db_log("TSCR: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param1); - - if (temp.action[j].param2 >= TARGET_T_END) - error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1); - } - break; - - case ACTION_T_REMOVEAURASFROMSPELL: - { - if (!GetSpellStore()->LookupEntry(temp.action[j].param2)) - error_db_log("TSCR: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2); - - if (temp.action[j].param1 >= TARGET_T_END) - error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1); - } - break; - - case ACTION_T_CASTCREATUREGO: - { - if (!GetSpellStore()->LookupEntry(temp.action[j].param2)) - error_db_log("TSCR: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2); - - if (temp.action[j].param3 >= TARGET_T_END) - error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1); - } - break; - - //2nd param target - case ACTION_T_SUMMON_ID: - { - if (EventAI_Summon_Map.find(temp.action[j].param3) == EventAI_Summon_Map.end()) - error_db_log("TSCR: Event %u Action %u summons missing EventAI_Summon %u", i, j+1, temp.action[j].param3); - - if (temp.action[j].param2 >= TARGET_T_END) - error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1); - } - break; - - case ACTION_T_SUMMON: - case ACTION_T_THREAT_SINGLE_PCT: - case ACTION_T_QUEST_EVENT: - case ACTION_T_SET_UNIT_FLAG: - case ACTION_T_REMOVE_UNIT_FLAG: - case ACTION_T_SET_INST_DATA64: - if (temp.action[j].param2 >= TARGET_T_END) - error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1); - break; - - //3rd param target - case ACTION_T_SET_UNIT_FIELD: - if (temp.action[j].param3 >= TARGET_T_END) - error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1); - break; - - case ACTION_T_SET_PHASE: - if (temp.action[j].param1 > 31) - error_db_log("TSCR: Event %u Action %u attempts to set phase > 31. Phase mask cannot be used past phase 31.", i, j+1); - break; - - case ACTION_T_INC_PHASE: - if (!temp.action[j].param1) - error_db_log("TSCR: Event %u Action %u is incrementing phase by 0. Was this intended?", i, j+1); - break; - - case ACTION_T_KILLED_MONSTER: - if (temp.event_type != EVENT_T_DEATH) - outstring_log("TSCR WARNING: Event %u Action %u calling ACTION_T_KILLED_MONSTER outside of EVENT_T_DEATH", i, j+1); - break; - - case ACTION_T_SET_INST_DATA: - if (temp.action[j].param2 > 3) - error_db_log("TSCR: Event %u Action %u attempts to set instance data above encounter state 3. Custom case?", i, j+1); - break; - - default: - break; - } - - if (temp.action[j].type >= ACTION_T_END) - error_db_log("TSCR: Event %u Action %u has incorrect action type. Maybe DB requires updated version of SD2.", i, j+1); - } - - //Add to list - EventAI_Event_List.push_back(temp); - ++Count; - - }while (result->NextRow()); - - delete result; - outstring_log(""); - outstring_log("TSCR: >> Loaded %u EventAI_Events", Count); - - }else outstring_log("TSCR: WARNING >> Loaded 0 EventAI_Scripts. DB table `EventAI_Scripts` is empty."); - - // Gather Script Text - result = TScriptDB.PQuery("SELECT `id`, `sound`, `type`, `language`, `text`" - "FROM `script_texts`;"); - - // Drop Existing Script Text Map - Script_TextMap.clear(); - - if(result) - { - outstring_log("TSCR: Loading Script Text..."); - barGoLink bar(result->GetRowCount()); - uint32 count = 0; - - do - { - bar.step(); - Field* fields = result->Fetch(); - ScriptText temp; - - uint32 i = fields[0].GetInt32(); - temp.SoundId = fields[1].GetInt32(); - temp.Type = fields[2].GetInt32(); - temp.Language = fields[3].GetInt32(); - temp.Text = fields[4].GetString(); - - if (temp.SoundId) - { - if (!GetSoundEntriesStore()->LookupEntry(temp.SoundId)) - error_db_log("TSCR: Id %u in table script_texts has soundid %u but sound does not exist.",i,temp.SoundId); - } - - if(!strlen(temp.Text.c_str())) - error_db_log("TSCR: Id %u in table script_texts has no text.", i); - - Script_TextMap[i] = temp; - ++count; - - }while(result->NextRow()); - - delete result; - - outstring_log(""); - outstring_log("TSCR: Loaded %u Script Texts", count); - - }else outstring_log("TSCR WARNING >> Loaded 0 Script Texts. Database table `script_texts` is empty."); - - //Free database thread and resources - TScriptDB.HaltDelayThread(); - - //***End DB queries*** - } -} - -struct TSpellSummary { - uint8 Targets; // set of enum SelectTarget - uint8 Effects; // set of enum SelectEffect -}extern *SpellSummary; - -MANGOS_DLL_EXPORT -void ScriptsFree() -{ - // Free Spell Summary - delete []SpellSummary; - - // Free resources before library unload - for(int i=0;i<nrscripts;i++) - delete m_scripts[i]; - - nrscripts = 0; -} - -MANGOS_DLL_EXPORT -void ScriptsInit() -{ - //Trinity Script startup - outstring_log(" _____ _ _ _ ____ _ _"); - outstring_log("|_ _| __(_)_ __ (_) |_ _ _/ ___| ___ _ __(_)_ __ | |_ "); - outstring_log(" | || '__| | '_ \\| | __| | | \\___ \\ / __| \'__| | \'_ \\| __|"); - outstring_log(" | || | | | | | | | |_| |_| |___) | (__| | | | |_) | |_ "); - outstring_log(" |_||_| |_|_| |_|_|\\__|\\__, |____/ \\___|_| |_| .__/ \\__|"); - outstring_log(" |___/ |_| "); - outstring_log("Trinity Script initializing %s", _FULLVERSION); - outstring_log(""); - - //Get configuration file - if (!TScriptConfig.SetSource(_TRINITY_SCRIPT_CONFIG)) - error_log("TSCR: Unable to open configuration file, Database will be unaccessible"); - else outstring_log("TSCR: Using configuration file %s", _TRINITY_SCRIPT_CONFIG); - - - //Check config file version - if (TScriptConfig.GetIntDefault("ConfVersion", 0) != _TSCRIPTCONFVERSION) - error_log("TSCR: Configuration file version doesn't match expected version. Some config variables may be wrong or missing."); - - //Locale - Locale = TScriptConfig.GetIntDefault("Locale", 0); - - if (Locale > 8) - { - Locale = 0; - error_log("TSCR: Locale set to invalid language id. Defaulting to 0."); - } - - outstring_log("TSCR: Using locale %u", Locale); - outstring_log(""); - - EAI_ErrorLevel = TScriptConfig.GetIntDefault("EAIErrorLevel", 1); - - switch (EAI_ErrorLevel) - { - case 0: - outstring_log("TSCR: EventAI Error Reporting level set to 0 (Startup Errors only)"); - break; - - case 1: - outstring_log("TSCR: EventAI Error Reporting level set to 1 (Startup errors and Runtime event errors)"); - break; - - case 2: - outstring_log("TSCR: EventAI Error Reporting level set to 2 (Startup errors, Runtime event errors, and Creation errors)"); - break; - - default: - outstring_log("TSCR: Unknown EventAI Error Reporting level. Defaulting to 1 (Startup errors and Runtime event errors)"); - EAI_ErrorLevel = 1; - break; - } - outstring_log(""); - - //Load database (must be called after TScriptConfig.SetSource) - LoadDatabase(); - - nrscripts = 0; - for(int i=0;i<MAX_SCRIPTS;i++) - m_scripts[i]=NULL; - - FillSpellSummary(); - - // -- Scripts to be added -- - - // -- Areatrigger -- - AddSC_areatrigger_scripts(); - - // -- Boss -- - AddSC_boss_emeriss(); - AddSC_boss_taerar(); - AddSC_boss_ysondre(); - - // -- Creature -- - AddSC_mob_event(); - AddSC_generic_creature(); - - // -- Custom -- - AddSC_custom_example(); - AddSC_custom_gossip_codebox(); - AddSC_test(); - - // -- GO -- - AddSC_go_scripts(); - - // -- Guard -- - AddSC_guards(); - - // -- Honor -- - - // -- Item -- - AddSC_item_scripts(); - AddSC_item_test(); - - // -- NPC -- - AddSC_npc_professions(); - AddSC_npcs_special(); - - // -- Servers -- - - //-------------------- - //------ ZONE -------- - - //Alterac Mountains - AddSC_alterac_mountains(); - - //Arathi Highlands - //Ashenvale Forest - //Aunchindoun - //--Auchenai Crypts - AddSC_boss_exarch_maladaar(); - - //--Mana Tombs - AddSC_boss_nexusprince_shaffar(); - AddSC_boss_pandemonius(); - - //--Sekketh Halls - AddSC_boss_darkweaver_syth(); - AddSC_boss_talon_king_ikiss(); - AddSC_instance_sethekk_halls(); - - //--Shadow Labyrinth - AddSC_boss_ambassador_hellmaw(); - AddSC_boss_blackheart_the_inciter(); - AddSC_boss_grandmaster_vorpil(); - AddSC_boss_murmur(); - AddSC_instance_shadow_labyrinth(); - - //Azshara - AddSC_boss_azuregos(); - AddSC_azshara(); - - //Azuremyst Isle - AddSC_azuremyst_isle(); - - //Badlands - //Barrens - AddSC_the_barrens(); - - //Black Temple - AddSC_black_temple(); - AddSC_boss_illidan(); - AddSC_boss_shade_of_akama(); - AddSC_boss_supremus(); - AddSC_boss_gurtogg_bloodboil(); - AddSC_boss_mother_shahraz(); - AddSC_boss_reliquary_of_souls(); - AddSC_boss_teron_gorefiend(); - AddSC_boss_najentus(); - AddSC_boss_illidari_council(); - AddSC_instance_black_temple(); - - //Blackfathom Depths - //Blackrock Depths - AddSC_blackrock_depths(); - AddSC_boss_ambassador_flamelash(); - AddSC_boss_angerrel(); - AddSC_boss_anubshiah(); - AddSC_boss_doomrel(); - AddSC_boss_doperel(); - AddSC_boss_draganthaurissan(); - AddSC_boss_general_angerforge(); - AddSC_boss_gloomrel(); - AddSC_boss_gorosh_the_dervish(); - AddSC_boss_grizzle(); - AddSC_boss_haterel(); - AddSC_boss_high_interrogator_gerstahn(); - AddSC_boss_magmus(); - AddSC_boss_moira_bronzebeard(); - AddSC_boss_seethrel(); - AddSC_boss_vilerel(); - - //Blackrock Spire - AddSC_boss_drakkisath(); - AddSC_boss_halycon(); - AddSC_boss_highlordomokk(); - AddSC_boss_mothersmolderweb(); - AddSC_boss_overlordwyrmthalak(); - AddSC_boss_shadowvosh(); - AddSC_boss_thebeast(); - AddSC_boss_warmastervoone(); - AddSC_boss_quatermasterzigris(); - AddSC_boss_pyroguard_emberseer(); - AddSC_boss_gyth(); - AddSC_boss_rend_blackhand(); - - //Blackwing lair - AddSC_boss_razorgore(); - AddSC_boss_vael(); - AddSC_boss_broodlord(); - AddSC_boss_firemaw(); - AddSC_boss_ebonroc(); - AddSC_boss_flamegor(); - AddSC_boss_chromaggus(); - AddSC_boss_nefarian(); - AddSC_boss_victor_nefarius(); - - //Blade's Edge Mountains - AddSC_blades_edge_mountains(); - - //Blasted lands - AddSC_boss_kruul(); - AddSC_blasted_lands(); - - //Bloodmyst Isle - AddSC_bloodmyst_isle(); - - //Burning steppes - AddSC_burning_steppes(); - - //Caverns of Time - //--Battle for Mt. Hyjal - AddSC_hyjal(); - AddSC_boss_archimonde(); - AddSC_instance_mount_hyjal(); - - //--Old Hillsbrad - AddSC_boss_captain_skarloc(); - AddSC_boss_epoch_hunter(); - AddSC_boss_lieutenant_drake(); - AddSC_instance_old_hillsbrad(); - AddSC_old_hillsbrad(); - - //--The Dark Portal - AddSC_boss_aeonus(); - AddSC_boss_chrono_lord_deja(); - AddSC_boss_temporus(); - - //Coilfang Resevoir - //--Serpent Shrine Cavern - AddSC_boss_fathomlord_karathress(); - AddSC_boss_hydross_the_unstable(); - AddSC_boss_lady_vashj(); - AddSC_boss_leotheras_the_blind(); - AddSC_boss_morogrim_tidewalker(); - AddSC_instance_serpentshrine_cavern(); - - //--Slave Pens - //--Steam Vault - AddSC_boss_hydromancer_thespia(); - AddSC_boss_mekgineer_steamrigger(); - AddSC_boss_warlord_kalithresh(); - AddSC_instance_steam_vault(); - - //--Underbog - AddSC_boss_hungarfen(); - - //Darkshore - //Darnassus - //Deadmines - //Deadwind pass - //Desolace - //Dire Maul - //Dun Morogh - AddSC_dun_morogh(); - - //Durotar - //Duskwood - //Dustwallow marsh - AddSC_dustwallow_marsh(); - - //Eversong Woods - AddSC_eversong_woods(); - - //Exodar - //Eastern Plaguelands - AddSC_eastern_plaguelands(); - - //Elwynn Forest - AddSC_elwynn_forest(); - - //Felwood - AddSC_felwood(); - - //Feralas - AddSC_feralas(); - - //Ghostlands - AddSC_ghostlands(); - - //Gnomeregan - //Gruul's Lair - AddSC_boss_gruul(); - AddSC_boss_high_king_maulgar(); - AddSC_instance_gruuls_lair(); - - //Hellfire Citadel - //--Blood Furnace - AddSC_boss_broggok(); - AddSC_boss_kelidan_the_breaker(); - AddSC_boss_the_maker(); - - //--Magtheridon's Lair - AddSC_boss_magtheridon(); - AddSC_instance_magtheridons_lair(); - - //--Shattered Halls - AddSC_boss_grand_warlock_nethekurse(); - AddSC_boss_warbringer_omrogg(); - AddSC_instance_shattered_halls(); - - //--Ramparts - AddSC_boss_watchkeeper_gargolmar(); - AddSC_boss_omor_the_unscarred(); - - //Hellfire Peninsula - AddSC_boss_doomlordkazzak(); - AddSC_hellfire_peninsula(); - - //Hillsbrad Foothills - //Hinterlands - //Ironforge - AddSC_ironforge(); - - //Isle of Quel'Danas - AddSC_isle_of_queldanas(); - - //Karazhan - AddSC_boss_attumen(); - AddSC_boss_curator(); - AddSC_boss_maiden_of_virtue(); - AddSC_boss_shade_of_aran(); - AddSC_boss_malchezaar(); - AddSC_boss_terestian_illhoof(); - AddSC_netherspite_infernal(); - AddSC_boss_moroes(); - AddSC_bosses_opera(); - AddSC_instance_karazhan(); - AddSC_karazhan(); - - //Loch Modan - AddSC_loch_modan(); - - //Lower Blackrock Spire - - // Magister's Terrace - AddSC_boss_felblood_kaelthas(); - AddSC_boss_selin_fireheart(); - AddSC_boss_vexallus(); - AddSC_boss_priestess_delrissa(); - AddSC_instance_magisters_terrace(); - - //Maraudon - AddSC_boss_celebras_the_cursed(); - AddSC_boss_landslide(); - AddSC_boss_noxxion(); - AddSC_boss_ptheradras(); - - //Molten core - AddSC_boss_lucifron(); - AddSC_boss_magmadar(); - AddSC_boss_gehennas(); - AddSC_boss_garr(); - AddSC_boss_baron_geddon(); - AddSC_boss_shazzrah(); - AddSC_boss_golemagg(); - AddSC_boss_sulfuron(); - AddSC_boss_majordomo(); - AddSC_boss_ragnaros(); - AddSC_instance_molten_core(); - AddSC_molten_core(); - - //Moonglade - AddSC_moonglade(); - - //Mulgore - AddSC_mulgore(); - - //Nagrand - AddSC_nagrand(); - - //Naxxramas - AddSC_boss_anubrekhan(); - AddSC_boss_maexxna(); - AddSC_boss_patchwerk(); - AddSC_boss_razuvious(); - AddSC_boss_highlord_mograine(); - AddSC_boss_lady_blaumeux(); - AddSC_boss_sir_zeliek(); - AddSC_boss_thane_korthazz(); - AddSC_boss_kelthuzad(); - AddSC_boss_faerlina(); - AddSC_boss_loatheb(); - AddSC_boss_noth(); - AddSC_boss_gluth(); - AddSC_boss_sapphiron(); - - //Netherstorm - AddSC_netherstorm(); - - //Onyxia's Lair - AddSC_boss_onyxia(); - - //Orgrimmar - AddSC_orgrimmar(); - - //Ragefire Chasm - //Razorfen Downs - AddSC_boss_amnennar_the_coldbringer(); - - //Redridge Mountains - //Ruins of Ahn'Qiraj - //Scarlet Monastery - AddSC_boss_arcanist_doan(); - AddSC_boss_azshir_the_sleepless(); - AddSC_boss_bloodmage_thalnos(); - AddSC_boss_herod(); - AddSC_boss_high_inquisitor_fairbanks(); - AddSC_boss_high_inquisitor_whitemane(); - AddSC_boss_houndmaster_loksey(); - AddSC_boss_interrogator_vishas(); - AddSC_boss_scarlet_commander_mograine(); - AddSC_boss_scorn(); - - //Scholomance - AddSC_boss_darkmaster_gandling(); - AddSC_boss_death_knight_darkreaver(); - AddSC_boss_theolenkrastinov(); - AddSC_boss_illuciabarov(); - AddSC_boss_instructormalicia(); - AddSC_boss_jandicebarov(); - AddSC_boss_kormok(); - AddSC_boss_lordalexeibarov(); - AddSC_boss_lorekeeperpolkelt(); - AddSC_boss_rasfrost(); - AddSC_boss_theravenian(); - AddSC_boss_vectus(); - AddSC_instance_scholomance(); - - //Searing gorge - AddSC_searing_gorge(); - - //Shadowfang keep - AddSC_shadowfang_keep(); - AddSC_instance_shadowfang_keep(); - - //Shadowmoon Valley - AddSC_boss_doomwalker(); - AddSC_shadowmoon_valley(); - - //Shattrath - AddSC_shattrath_city(); - - //Silithus - AddSC_silithus(); - - //Silvermoon - AddSC_silvermoon_city(); - - //Silverpine forest - AddSC_silverpine_forest(); - - //Stockade - //Stonetalon mountains - AddSC_stonetalon_mountains(); - - //Stormwind City - AddSC_stormwind_city(); - - //Stranglethorn Vale - AddSC_stranglethorn_vale(); - - //Stratholme - AddSC_boss_magistrate_barthilas(); - AddSC_boss_maleki_the_pallid(); - AddSC_boss_nerubenkan(); - AddSC_boss_cannon_master_willey(); - AddSC_boss_baroness_anastari(); - AddSC_boss_ramstein_the_gorger(); - AddSC_boss_timmy_the_cruel(); - AddSC_boss_postmaster_malown(); - AddSC_boss_baron_rivendare(); - AddSC_boss_dathrohan_balnazzar(); - AddSC_boss_order_of_silver_hand(); - AddSC_instance_stratholme(); - AddSC_stratholme(); - - //Sunken Temple - //Tanaris - AddSC_tanaris(); - - //Teldrassil - //Tempest Keep - //--Arcatraz - AddSC_arcatraz(); - AddSC_boss_harbinger_skyriss(); - AddSC_instance_arcatraz(); - - //--Botanica - AddSC_boss_high_botanist_freywinn(); - AddSC_boss_laj(); - AddSC_boss_warp_splinter(); - - //--The Eye - AddSC_boss_kaelthas(); - AddSC_boss_void_reaver(); - AddSC_boss_high_astromancer_solarian(); - AddSC_instance_the_eye(); - AddSC_the_eye(); - - //--The Mechanar - AddSC_boss_gatewatcher_iron_hand(); - AddSC_boss_nethermancer_sepethrea(); - - //Temple of ahn'qiraj - AddSC_boss_cthun(); - AddSC_boss_fankriss(); - AddSC_boss_huhuran(); - AddSC_bug_trio(); - AddSC_boss_sartura(); - AddSC_boss_skeram(); - AddSC_boss_twinemperors(); - AddSC_mob_anubisath_sentinel(); - AddSC_instance_temple_of_ahnqiraj(); - - //Terokkar Forest - AddSC_terokkar_forest(); - - //Thousand Needles - //Thunder Bluff - AddSC_thunder_bluff(); - - //Tirisfal Glades - AddSC_tirisfal_glades(); - - //Uldaman - AddSC_boss_ironaya(); - AddSC_uldaman(); - - //Undercity - AddSC_undercity(); - - //Un'Goro Crater - //Upper blackrock spire - //Wailing caverns - - //Western plaguelands - AddSC_western_plaguelands(); - - //Westfall - //Wetlands - //Winterspring - AddSC_winterspring(); - - //Zangarmarsh - AddSC_zangarmarsh(); - - //Zul'Farrak - //Zul'Gurub - AddSC_boss_jeklik(); - AddSC_boss_venoxis(); - AddSC_boss_marli(); - AddSC_boss_mandokir(); - AddSC_boss_gahzranka(); - AddSC_boss_thekal(); - AddSC_boss_arlokk(); - AddSC_boss_jindo(); - AddSC_boss_hakkar(); - AddSC_boss_grilek(); - AddSC_boss_hazzarah(); - AddSC_boss_renataki(); - AddSC_boss_wushoolay(); - AddSC_instance_zulgurub(); - - //Zul'Aman - AddSC_boss_janalai(); - AddSC_boss_nalorakk(); - AddSC_instance_zulaman(); - AddSC_zulaman(); - - // ------------------- - - outstring_log("TSCR: Loaded %u C++ Scripts", nrscripts); - outstring_log(""); -} - -//********************************* -//*** Functions used internally *** - -const char* GetEventAILocalizedText(uint32 entry) -{ - if (entry == 0xffffffff) - error_log("TSCR: Entry = -1, GetEventAILocalizedText should not be called in this case."); - - const char* temp = NULL; - - HM_NAMESPACE::hash_map<uint32, Localized_Text>::iterator i = EventAI_LocalizedTextMap.find(entry); - - if (i == EventAI_LocalizedTextMap.end()) - { - error_log("TSCR: EventAI Localized Text %u not found", entry); - return DEFAULT_TEXT; - } - - switch (Locale) - { - case 1: - temp = (*i).second.locale_1.c_str(); - break; - - case 2: - temp = (*i).second.locale_2.c_str(); - break; - - case 3: - temp = (*i).second.locale_3.c_str(); - break; - - case 4: - temp = (*i).second.locale_4.c_str(); - break; - - case 5: - temp = (*i).second.locale_5.c_str(); - break; - - case 6: - temp = (*i).second.locale_6.c_str(); - break; - - case 7: - temp = (*i).second.locale_7.c_str(); - break; - - case 8: - temp = (*i).second.locale_8.c_str(); - break; - }; - - if (strlen(temp)) - return temp; - - return DEFAULT_TEXT; -} - -const char* GetScriptLocalizedText(uint32 entry) -{ - const char* temp = NULL; - - HM_NAMESPACE::hash_map<uint32, Localized_Text>::iterator i = Script_LocalizedTextMap.find(entry); - - if (i == Script_LocalizedTextMap.end()) - { - error_log("TSCR: Script Localized Text %u not found", entry); - return DEFAULT_TEXT; - } - - switch (Locale) - { - case 1: - temp = (*i).second.locale_1.c_str(); - break; - - case 2: - temp = (*i).second.locale_2.c_str(); - break; - - case 3: - temp = (*i).second.locale_3.c_str(); - break; - - case 4: - temp = (*i).second.locale_4.c_str(); - break; - - case 5: - temp = (*i).second.locale_5.c_str(); - break; - - case 6: - temp = (*i).second.locale_6.c_str(); - break; - - case 7: - temp = (*i).second.locale_7.c_str(); - break; - - case 8: - temp = (*i).second.locale_8.c_str(); - break; - }; - - if (strlen(temp)) - return temp; - - return DEFAULT_TEXT; -} - -const char* GetEventAIText(uint32 entry) -{ - if(entry == 0xffffffff) - error_log("TSCR: Entry = -1, GetEventAIText should not be called in this case."); - - const char* str = NULL; - - HM_NAMESPACE::hash_map<uint32, std::string>::iterator itr = EventAI_Text_Map.find(entry); - if(itr == EventAI_Text_Map.end()) - { - error_log("TSCR: Unable to find EventAI Text %u", entry); - return DEFAULT_TEXT; - } - - str = (*itr).second.c_str(); - - if(strlen(str)) - return str; - - if(strlen((*itr).second.c_str())) - return (*itr).second.c_str(); - - return DEFAULT_TEXT; -} - -void ProcessScriptText(uint32 id, WorldObject* pSource, Unit* target) -{ - if (!pSource) - { - error_log("TSCR: ProcessScriptText invalid Source pointer."); - return; - } - - HM_NAMESPACE::hash_map<uint32, ScriptText>::iterator i = Script_TextMap.find(id); - - if (i == Script_TextMap.end()) - { - error_log("TSCR: ProcessScriptText could not find id %u.",id); - return; - } - - if((*i).second.SoundId) - { - if(GetSoundEntriesStore()->LookupEntry((*i).second.SoundId)) - { - pSource->SendPlaySound((*i).second.SoundId, false); - } - else - error_log("TSCR: ProcessScriptText id %u tried to process invalid soundid %u.",id,(*i).second.SoundId); - } - - switch((*i).second.Type) - { - case CHAT_TYPE_SAY: - pSource->MonsterSay((*i).second.Text.c_str(), (*i).second.Language, target ? target->GetGUID() : 0); - break; - - case CHAT_TYPE_YELL: - pSource->MonsterYell((*i).second.Text.c_str(), (*i).second.Language, target ? target->GetGUID() : 0); - break; - - case CHAT_TYPE_TEXT_EMOTE: - pSource->MonsterTextEmote((*i).second.Text.c_str(), target ? target->GetGUID() : 0); - break; - - case CHAT_TYPE_BOSS_EMOTE: - pSource->MonsterTextEmote((*i).second.Text.c_str(), target ? target->GetGUID() : 0, true); - break; - - case CHAT_TYPE_WHISPER: - { - if (target && target->GetTypeId() == TYPEID_PLAYER) - pSource->MonsterWhisper((*i).second.Text.c_str(), target->GetGUID()); - else error_log("TSCR: ProcessScriptText id %u cannot whisper without target unit (TYPEID_PLAYER).", id); - }break; - - case CHAT_TYPE_BOSS_WHISPER: - { - if (target && target->GetTypeId() == TYPEID_PLAYER) - pSource->MonsterWhisper((*i).second.Text.c_str(), target->GetGUID(), true); - else error_log("TSCR: ProcessScriptText id %u cannot whisper without target unit (TYPEID_PLAYER).", id); - }break; - } -} - -Script* GetScriptByName(std::string Name) -{ - if(Name.empty()) - return NULL; - - for(int i=0;i<MAX_SCRIPTS;i++) - { - if( m_scripts[i] && m_scripts[i]->Name == Name ) - return m_scripts[i]; - } - return NULL; -} - -//******************************** -//*** Functions to be Exported *** - -MANGOS_DLL_EXPORT -bool GossipHello ( Player * player, Creature *_Creature ) -{ - Script *tmpscript = GetScriptByName(_Creature->GetScriptName()); - if(!tmpscript || !tmpscript->pGossipHello) return false; - - player->PlayerTalkClass->ClearMenus(); - return tmpscript->pGossipHello(player,_Creature); -} - -MANGOS_DLL_EXPORT -bool GossipSelect( Player *player, Creature *_Creature, uint32 sender, uint32 action ) -{ - debug_log("TSCR: Gossip selection, sender: %d, action: %d",sender, action); - - Script *tmpscript = GetScriptByName(_Creature->GetScriptName()); - if(!tmpscript || !tmpscript->pGossipSelect) return false; - - player->PlayerTalkClass->ClearMenus(); - return tmpscript->pGossipSelect(player,_Creature,sender,action); -} - -MANGOS_DLL_EXPORT -bool GossipSelectWithCode( Player *player, Creature *_Creature, uint32 sender, uint32 action, const char* sCode ) -{ - debug_log("TSCR: Gossip selection with code, sender: %d, action: %d",sender, action); - - Script *tmpscript = GetScriptByName(_Creature->GetScriptName()); - if(!tmpscript || !tmpscript->pGossipSelectWithCode) return false; - - player->PlayerTalkClass->ClearMenus(); - return tmpscript->pGossipSelectWithCode(player,_Creature,sender,action,sCode); -} - -MANGOS_DLL_EXPORT -bool QuestAccept( Player *player, Creature *_Creature, Quest const *_Quest ) -{ - Script *tmpscript = GetScriptByName(_Creature->GetScriptName()); - if(!tmpscript || !tmpscript->pQuestAccept) return false; - - player->PlayerTalkClass->ClearMenus(); - return tmpscript->pQuestAccept(player,_Creature,_Quest); -} - -MANGOS_DLL_EXPORT -bool QuestSelect( Player *player, Creature *_Creature, Quest const *_Quest ) -{ - Script *tmpscript = GetScriptByName(_Creature->GetScriptName()); - if(!tmpscript || !tmpscript->pQuestSelect) return false; - - player->PlayerTalkClass->ClearMenus(); - return tmpscript->pQuestSelect(player,_Creature,_Quest); -} - -MANGOS_DLL_EXPORT -bool QuestComplete( Player *player, Creature *_Creature, Quest const *_Quest ) -{ - Script *tmpscript = GetScriptByName(_Creature->GetScriptName()); - if(!tmpscript || !tmpscript->pQuestComplete) return false; - - player->PlayerTalkClass->ClearMenus(); - return tmpscript->pQuestComplete(player,_Creature,_Quest); -} - -MANGOS_DLL_EXPORT -bool ChooseReward( Player *player, Creature *_Creature, Quest const *_Quest, uint32 opt ) -{ - Script *tmpscript = GetScriptByName(_Creature->GetScriptName()); - if(!tmpscript || !tmpscript->pChooseReward) return false; - - player->PlayerTalkClass->ClearMenus(); - return tmpscript->pChooseReward(player,_Creature,_Quest,opt); -} - -MANGOS_DLL_EXPORT -uint32 NPCDialogStatus( Player *player, Creature *_Creature ) -{ - Script *tmpscript = GetScriptByName(_Creature->GetScriptName()); - if(!tmpscript || !tmpscript->pNPCDialogStatus) return 100; - - player->PlayerTalkClass->ClearMenus(); - return tmpscript->pNPCDialogStatus(player,_Creature); -} - -MANGOS_DLL_EXPORT -uint32 GODialogStatus( Player *player, GameObject *_GO ) -{ - Script *tmpscript = GetScriptByName(_GO->GetGOInfo()->ScriptName); - if(!tmpscript || !tmpscript->pGODialogStatus) return 100; - - player->PlayerTalkClass->ClearMenus(); - return tmpscript->pGODialogStatus(player,_GO); -} - -MANGOS_DLL_EXPORT -bool ItemHello( Player *player, Item *_Item, Quest const *_Quest ) -{ - Script *tmpscript = GetScriptByName(_Item->GetProto()->ScriptName); - if(!tmpscript || !tmpscript->pItemHello) return false; - - player->PlayerTalkClass->ClearMenus(); - return tmpscript->pItemHello(player,_Item,_Quest); -} - -MANGOS_DLL_EXPORT -bool ItemQuestAccept( Player *player, Item *_Item, Quest const *_Quest ) -{ - Script *tmpscript = GetScriptByName(_Item->GetProto()->ScriptName); - if(!tmpscript || !tmpscript->pItemQuestAccept) return false; - - player->PlayerTalkClass->ClearMenus(); - return tmpscript->pItemQuestAccept(player,_Item,_Quest); -} - -MANGOS_DLL_EXPORT -bool GOHello( Player *player, GameObject *_GO ) -{ - Script *tmpscript = GetScriptByName(_GO->GetGOInfo()->ScriptName); - if(!tmpscript || !tmpscript->pGOHello) return false; - - player->PlayerTalkClass->ClearMenus(); - return tmpscript->pGOHello(player,_GO); -} - -MANGOS_DLL_EXPORT -bool GOQuestAccept( Player *player, GameObject *_GO, Quest const *_Quest ) -{ - Script *tmpscript = GetScriptByName(_GO->GetGOInfo()->ScriptName); - if(!tmpscript || !tmpscript->pGOQuestAccept) return false; - - player->PlayerTalkClass->ClearMenus(); - return tmpscript->pGOQuestAccept(player,_GO,_Quest); -} - -MANGOS_DLL_EXPORT -bool GOChooseReward( Player *player, GameObject *_GO, Quest const *_Quest, uint32 opt ) -{ - Script *tmpscript = GetScriptByName(_GO->GetGOInfo()->ScriptName); - if(!tmpscript || !tmpscript->pGOChooseReward) return false; - - player->PlayerTalkClass->ClearMenus(); - return tmpscript->pGOChooseReward(player,_GO,_Quest,opt); -} - -MANGOS_DLL_EXPORT -bool AreaTrigger( Player *player, AreaTriggerEntry * atEntry) -{ - Script *tmpscript = NULL; - - tmpscript = GetScriptByName(GetAreaTriggerScriptNameById(atEntry->id)); - if(!tmpscript || !tmpscript->pAreaTrigger) return false; - - return tmpscript->pAreaTrigger(player, atEntry); -} - -MANGOS_DLL_EXPORT -CreatureAI* GetAI(Creature *_Creature) -{ - Script *tmpscript = GetScriptByName(_Creature->GetScriptName()); - - if(!tmpscript || !tmpscript->GetAI) return NULL; - return tmpscript->GetAI(_Creature); -} - -MANGOS_DLL_EXPORT -bool ItemUse( Player *player, Item* _Item, SpellCastTargets const& targets) -{ - Script *tmpscript = GetScriptByName(_Item->GetProto()->ScriptName); - if(!tmpscript || !tmpscript->pItemUse) return false; - - return tmpscript->pItemUse(player,_Item,targets); -} - -MANGOS_DLL_EXPORT -bool ReceiveEmote( Player *player, Creature *_Creature, uint32 emote ) -{ - Script *tmpscript = GetScriptByName(_Creature->GetScriptName()); - if(!tmpscript || !tmpscript->pReceiveEmote) return false; - - return tmpscript->pReceiveEmote(player, _Creature, emote); -} - -MANGOS_DLL_EXPORT -InstanceData* CreateInstanceData(Map *map) -{ - Script *tmpscript = NULL; - - if(!map->IsDungeon()) return false; - - tmpscript = GetScriptByName(((InstanceMap*)map)->GetScript()); - if(!tmpscript || !tmpscript->GetInstanceData) return false; - - return tmpscript->GetInstanceData(map); -} +/* Copyright (C) 2006 - 2008 TrinityScript <https://scriptdev2.svn.sourceforge.net/>
+ * This program is free software licensed under GPL version 2
+ * Please see the included DOCS/LICENSE.TXT for more information */
+
+#include "precompiled.h"
+#include "Config/Config.h"
+#include "Database/DatabaseEnv.h"
+#include "Database/DBCStores.h"
+#include "ObjectMgr.h"
+#include "ProgressBar.h"
+#include "scripts/creature/mob_event_ai.h"
+
+#define _FULLVERSION "TrinityScript"
+
+#ifndef _TSCRIPTCONFVERSION
+# define _TSCRIPTCONFVERSION 2008100201
+#endif //_TSCRIPTCONFVERSION
+
+#ifndef _TRINITY_SCRIPT_CONFIG
+# define _TRINITY_SCRIPT_CONFIG "trinityscript.conf"
+#endif //_TRINITY_SCRIPT_CONFIG
+
+//*** Global data ***
+int nrscripts;
+Script *m_scripts[MAX_SCRIPTS];
+
+DatabaseType TScriptDB;
+Config TScriptConfig;
+uint32 Locale;
+
+// String text additional data, used in TextMap
+struct StringTextData
+{
+ uint32 SoundId;
+ uint8 Type;
+ uint32 Language;
+};
+
+// Enums used by StringTextData::Type
+enum ChatType
+{
+ CHAT_TYPE_SAY = 0,
+ CHAT_TYPE_YELL = 1,
+ CHAT_TYPE_TEXT_EMOTE = 2,
+ CHAT_TYPE_BOSS_EMOTE = 3,
+ CHAT_TYPE_WHISPER = 4,
+ CHAT_TYPE_BOSS_WHISPER = 5,
+};
+
+#define TEXT_SOURCE_RANGE -100000 //the amount of entries each text source has available
+
+// Text Maps
+HM_NAMESPACE::hash_map<uint32, std::string> EventAI_Text_Map;
+HM_NAMESPACE::hash_map<int32, StringTextData> TextMap;
+
+// Localized Text structure for storing locales (for EAI and SD2 scripts).
+struct Localized_Text
+{
+ std::string locale_1;
+ std::string locale_2;
+ std::string locale_3;
+ std::string locale_4;
+ std::string locale_5;
+ std::string locale_6;
+ std::string locale_7;
+ std::string locale_8;
+};
+//*** End Global data ***
+
+//*** EventAI data ***
+HM_NAMESPACE::hash_map<uint32, Localized_Text> EventAI_LocalizedTextMap;
+
+//Event AI structure. Used exclusivly by mob_event_ai.cpp (60 bytes each)
+std::list<EventAI_Event> EventAI_Event_List;
+
+//Event AI summon structure. Used exclusivly by mob_event_ai.cpp.
+HM_NAMESPACE::hash_map<uint32, EventAI_Summon> EventAI_Summon_Map;
+
+//Event AI error prevention structure. Used at runtime to prevent error log spam of same creature id.
+//HM_NAMESPACE::hash_map<uint32, EventAI_CreatureError> EventAI_CreatureErrorPreventionList;
+
+uint32 EAI_ErrorLevel;
+//*** End EventAI data ***
+
+void FillSpellSummary();
+
+// -- Scripts to be added --
+
+// -- Areatrigger --
+extern void AddSC_areatrigger_scripts();
+
+// -- Boss --
+extern void AddSC_boss_emeriss();
+extern void AddSC_boss_taerar();
+extern void AddSC_boss_ysondre();
+
+// -- Creature --
+extern void AddSC_mob_event();
+extern void AddSC_generic_creature();
+
+// -- Custom --
+extern void AddSC_custom_example();
+extern void AddSC_custom_gossip_codebox();
+extern void AddSC_test();
+
+// -- GO --
+extern void AddSC_go_scripts();
+
+// -- Guard --
+extern void AddSC_guards();
+
+// -- Honor --
+
+// -- Item --
+extern void AddSC_item_scripts();
+extern void AddSC_item_test();
+
+// -- NPC --
+extern void AddSC_npc_professions();
+extern void AddSC_npcs_special();
+
+// -- Servers --
+
+//--------------------
+//------ ZONE --------
+
+//Alterac Mountains
+extern void AddSC_alterac_mountains();
+
+//Arathi Highlands
+//Ashenvale Forest
+//Aunchindoun
+//--Auchenai Crypts
+extern void AddSC_boss_exarch_maladaar();
+//--Mana Tombs
+extern void AddSC_boss_nexusprince_shaffar();
+extern void AddSC_boss_pandemonius();
+
+//--Sekketh Halls
+extern void AddSC_boss_darkweaver_syth();
+extern void AddSC_boss_talon_king_ikiss();
+extern void AddSC_instance_sethekk_halls();
+
+//--Shadow Labyrinth
+extern void AddSC_boss_ambassador_hellmaw();
+extern void AddSC_boss_blackheart_the_inciter();
+extern void AddSC_boss_grandmaster_vorpil();
+extern void AddSC_boss_murmur();
+extern void AddSC_instance_shadow_labyrinth();
+
+//Azshara
+extern void AddSC_boss_azuregos();
+extern void AddSC_azshara();
+
+//Azuremyst Isle
+extern void AddSC_azuremyst_isle();
+
+//Badlands
+//Barrens
+extern void AddSC_the_barrens();
+
+//Black Temple
+extern void AddSC_black_temple();
+extern void AddSC_boss_illidan();
+extern void AddSC_boss_shade_of_akama();
+extern void AddSC_boss_supremus();
+extern void AddSC_boss_gurtogg_bloodboil();
+extern void AddSC_boss_mother_shahraz();
+extern void AddSC_boss_reliquary_of_souls();
+extern void AddSC_boss_teron_gorefiend();
+extern void AddSC_boss_najentus();
+extern void AddSC_boss_illidari_council();
+extern void AddSC_instance_black_temple();
+
+//Blackfathom Depths
+//Blackrock Depths
+extern void AddSC_blackrock_depths();
+extern void AddSC_boss_ambassador_flamelash();
+extern void AddSC_boss_angerrel();
+extern void AddSC_boss_anubshiah();
+extern void AddSC_boss_doomrel();
+extern void AddSC_boss_doperel();
+extern void AddSC_boss_draganthaurissan();
+extern void AddSC_boss_general_angerforge();
+extern void AddSC_boss_gloomrel();
+extern void AddSC_boss_gorosh_the_dervish();
+extern void AddSC_boss_grizzle();
+extern void AddSC_boss_haterel();
+extern void AddSC_boss_high_interrogator_gerstahn();
+extern void AddSC_boss_magmus();
+extern void AddSC_boss_moira_bronzebeard();
+extern void AddSC_boss_seethrel();
+extern void AddSC_boss_vilerel();
+
+//Blackrock Spire
+extern void AddSC_boss_drakkisath();
+extern void AddSC_boss_halycon();
+extern void AddSC_boss_highlordomokk();
+extern void AddSC_boss_mothersmolderweb();
+extern void AddSC_boss_overlordwyrmthalak();
+extern void AddSC_boss_shadowvosh();
+extern void AddSC_boss_thebeast();
+extern void AddSC_boss_warmastervoone();
+extern void AddSC_boss_quatermasterzigris();
+extern void AddSC_boss_pyroguard_emberseer();
+extern void AddSC_boss_gyth();
+extern void AddSC_boss_rend_blackhand();
+
+//Blackwing lair
+extern void AddSC_boss_razorgore();
+extern void AddSC_boss_vael();
+extern void AddSC_boss_broodlord();
+extern void AddSC_boss_firemaw();
+extern void AddSC_boss_ebonroc();
+extern void AddSC_boss_flamegor();
+extern void AddSC_boss_chromaggus();
+extern void AddSC_boss_nefarian();
+extern void AddSC_boss_victor_nefarius();
+
+//Blade's Edge Mountains
+extern void AddSC_blades_edge_mountains();
+
+//Blasted lands
+extern void AddSC_boss_kruul();
+extern void AddSC_blasted_lands();
+
+//Bloodmyst Isle
+extern void AddSC_bloodmyst_isle();
+
+//Burning steppes
+extern void AddSC_burning_steppes();
+
+//Caverns of Time
+//--Battle for Mt. Hyjal
+extern void AddSC_hyjal();
+extern void AddSC_boss_archimonde();
+extern void AddSC_instance_mount_hyjal();
+
+//--Old Hillsbrad
+extern void AddSC_boss_captain_skarloc();
+extern void AddSC_boss_epoch_hunter();
+extern void AddSC_boss_lieutenant_drake();
+extern void AddSC_instance_old_hillsbrad();
+extern void AddSC_old_hillsbrad();
+
+//--The Dark Portal
+extern void AddSC_boss_aeonus();
+extern void AddSC_boss_chrono_lord_deja();
+extern void AddSC_boss_temporus();
+
+//Coilfang Resevoir
+//--Serpent Shrine Cavern
+extern void AddSC_boss_fathomlord_karathress();
+extern void AddSC_boss_hydross_the_unstable();
+extern void AddSC_boss_lady_vashj();
+extern void AddSC_boss_leotheras_the_blind();
+extern void AddSC_boss_morogrim_tidewalker();
+extern void AddSC_instance_serpentshrine_cavern();
+
+//--Slave Pens
+
+//--Steam Vault
+extern void AddSC_boss_hydromancer_thespia();
+extern void AddSC_boss_mekgineer_steamrigger();
+extern void AddSC_boss_warlord_kalithresh();
+extern void AddSC_instance_steam_vault();
+
+//--Underbog
+extern void AddSC_boss_hungarfen();
+
+//Darkshore
+//Darnassus
+//Deadmines
+//Deadwind pass
+//Desolace
+//Dire Maul
+//Dun Morogh
+extern void AddSC_dun_morogh();
+
+//Durotar
+//Duskwood
+//Dustwallow marsh
+extern void AddSC_dustwallow_marsh();
+
+//Eversong Woods
+extern void AddSC_eversong_woods();
+
+//Exodar
+//Eastern Plaguelands
+extern void AddSC_eastern_plaguelands();
+
+//Elwynn Forest
+extern void AddSC_elwynn_forest();
+
+//Felwood
+extern void AddSC_felwood();
+
+//Feralas
+extern void AddSC_feralas();
+
+//Ghostlands
+extern void AddSC_ghostlands();
+
+//Gnomeregan
+//Gruul's Lair
+extern void AddSC_boss_gruul();
+extern void AddSC_boss_high_king_maulgar();
+extern void AddSC_instance_gruuls_lair();
+
+//Hellfire Citadel
+//--Blood Furnace
+extern void AddSC_boss_broggok();
+extern void AddSC_boss_kelidan_the_breaker();
+extern void AddSC_boss_the_maker();
+
+//--Magtheridon's Lair
+extern void AddSC_boss_magtheridon();
+extern void AddSC_instance_magtheridons_lair();
+
+//--Shattered Halls
+extern void AddSC_boss_grand_warlock_nethekurse();
+extern void AddSC_boss_warbringer_omrogg();
+extern void AddSC_instance_shattered_halls();
+
+//--Ramparts
+extern void AddSC_boss_watchkeeper_gargolmar();
+extern void AddSC_boss_omor_the_unscarred();
+
+//Hellfire Peninsula
+extern void AddSC_boss_doomlordkazzak();
+extern void AddSC_hellfire_peninsula();
+
+//Hillsbrad Foothills
+//Hinterlands
+//Ironforge
+extern void AddSC_ironforge();
+
+//Isle of Quel'Danas
+extern void AddSC_isle_of_queldanas();
+
+//Karazhan
+extern void AddSC_boss_attumen();
+extern void AddSC_boss_curator();
+extern void AddSC_boss_maiden_of_virtue();
+extern void AddSC_boss_shade_of_aran();
+extern void AddSC_boss_malchezaar();
+extern void AddSC_boss_terestian_illhoof();
+extern void AddSC_netherspite_infernal();
+extern void AddSC_boss_moroes();
+extern void AddSC_bosses_opera();
+extern void AddSC_instance_karazhan();
+extern void AddSC_karazhan();
+
+//Loch Modan
+extern void AddSC_loch_modan();
+
+//Lower Blackrock Spire
+
+// Magister's Terrace
+extern void AddSC_boss_felblood_kaelthas();
+extern void AddSC_boss_selin_fireheart();
+extern void AddSC_boss_vexallus();
+extern void AddSC_boss_priestess_delrissa();
+extern void AddSC_instance_magisters_terrace();
+
+//Maraudon
+extern void AddSC_boss_celebras_the_cursed();
+extern void AddSC_boss_landslide();
+extern void AddSC_boss_noxxion();
+extern void AddSC_boss_ptheradras();
+
+//Molten core
+extern void AddSC_boss_lucifron();
+extern void AddSC_boss_magmadar();
+extern void AddSC_boss_gehennas();
+extern void AddSC_boss_garr();
+extern void AddSC_boss_baron_geddon();
+extern void AddSC_boss_shazzrah();
+extern void AddSC_boss_golemagg();
+extern void AddSC_boss_sulfuron();
+extern void AddSC_boss_majordomo();
+extern void AddSC_boss_ragnaros();
+extern void AddSC_instance_molten_core();
+extern void AddSC_molten_core();
+
+//Moonglade
+extern void AddSC_moonglade();
+
+//Mulgore
+extern void AddSC_mulgore();
+
+//Nagrand
+extern void AddSC_nagrand();
+
+//Naxxramas
+extern void AddSC_boss_anubrekhan();
+extern void AddSC_boss_maexxna();
+extern void AddSC_boss_patchwerk();
+extern void AddSC_boss_razuvious();
+extern void AddSC_boss_highlord_mograine();
+extern void AddSC_boss_lady_blaumeux();
+extern void AddSC_boss_sir_zeliek();
+extern void AddSC_boss_thane_korthazz();
+extern void AddSC_boss_kelthuzad();
+extern void AddSC_boss_faerlina();
+extern void AddSC_boss_loatheb();
+extern void AddSC_boss_noth();
+extern void AddSC_boss_gluth();
+extern void AddSC_boss_sapphiron();
+
+//Netherstorm
+extern void AddSC_netherstorm();
+
+//Onyxia's Lair
+extern void AddSC_boss_onyxia();
+
+//Orgrimmar
+extern void AddSC_orgrimmar();
+
+//Ragefire Chasm
+//Razorfen Downs
+extern void AddSC_boss_amnennar_the_coldbringer();
+
+//Redridge Mountains
+//Ruins of Ahn'Qiraj
+//Scarlet Monastery
+extern void AddSC_boss_arcanist_doan();
+extern void AddSC_boss_azshir_the_sleepless();
+extern void AddSC_boss_bloodmage_thalnos();
+extern void AddSC_boss_herod();
+extern void AddSC_boss_high_inquisitor_fairbanks();
+extern void AddSC_boss_high_inquisitor_whitemane();
+extern void AddSC_boss_houndmaster_loksey();
+extern void AddSC_boss_interrogator_vishas();
+extern void AddSC_boss_scarlet_commander_mograine();
+extern void AddSC_boss_scorn();
+
+//Scholomance
+extern void AddSC_boss_darkmaster_gandling();
+extern void AddSC_boss_death_knight_darkreaver();
+extern void AddSC_boss_theolenkrastinov();
+extern void AddSC_boss_illuciabarov();
+extern void AddSC_boss_instructormalicia();
+extern void AddSC_boss_jandicebarov();
+extern void AddSC_boss_kormok();
+extern void AddSC_boss_lordalexeibarov();
+extern void AddSC_boss_lorekeeperpolkelt();
+extern void AddSC_boss_rasfrost();
+extern void AddSC_boss_theravenian();
+extern void AddSC_boss_vectus();
+extern void AddSC_instance_scholomance();
+
+//Searing gorge
+extern void AddSC_searing_gorge();
+
+//Shadowfang keep
+extern void AddSC_shadowfang_keep();
+extern void AddSC_instance_shadowfang_keep();
+
+//Shadowmoon Valley
+extern void AddSC_boss_doomwalker();
+extern void AddSC_shadowmoon_valley();
+
+//Shattrath
+extern void AddSC_shattrath_city();
+
+//Silithus
+extern void AddSC_silithus();
+
+//Silvermoon
+extern void AddSC_silvermoon_city();
+
+//Silverpine forest
+extern void AddSC_silverpine_forest();
+
+//Stockade
+//Stonetalon mountains
+extern void AddSC_stonetalon_mountains();
+
+//Stormwind City
+extern void AddSC_stormwind_city();
+
+//Stranglethorn Vale
+extern void AddSC_stranglethorn_vale();
+
+//Stratholme
+extern void AddSC_boss_magistrate_barthilas();
+extern void AddSC_boss_maleki_the_pallid();
+extern void AddSC_boss_nerubenkan();
+extern void AddSC_boss_cannon_master_willey();
+extern void AddSC_boss_baroness_anastari();
+extern void AddSC_boss_ramstein_the_gorger();
+extern void AddSC_boss_timmy_the_cruel();
+extern void AddSC_boss_postmaster_malown();
+extern void AddSC_boss_baron_rivendare();
+extern void AddSC_boss_dathrohan_balnazzar();
+extern void AddSC_boss_order_of_silver_hand();
+extern void AddSC_instance_stratholme();
+extern void AddSC_stratholme();
+
+//Sunken Temple
+//Tanaris
+extern void AddSC_tanaris();
+
+//Teldrassil
+//Tempest Keep
+//--Arcatraz
+extern void AddSC_arcatraz();
+extern void AddSC_boss_harbinger_skyriss();
+extern void AddSC_instance_arcatraz();
+
+//--Botanica
+extern void AddSC_boss_high_botanist_freywinn();
+extern void AddSC_boss_laj();
+extern void AddSC_boss_warp_splinter();
+
+//--The Eye
+extern void AddSC_boss_kaelthas();
+extern void AddSC_boss_void_reaver();
+extern void AddSC_boss_high_astromancer_solarian();
+extern void AddSC_instance_the_eye();
+extern void AddSC_the_eye();
+
+//--The Mechanar
+extern void AddSC_boss_gatewatcher_iron_hand();
+extern void AddSC_boss_nethermancer_sepethrea();
+
+//Temple of ahn'qiraj
+extern void AddSC_boss_cthun();
+extern void AddSC_boss_fankriss();
+extern void AddSC_boss_huhuran();
+extern void AddSC_bug_trio();
+extern void AddSC_boss_sartura();
+extern void AddSC_boss_skeram();
+extern void AddSC_boss_twinemperors();
+extern void AddSC_mob_anubisath_sentinel();
+extern void AddSC_instance_temple_of_ahnqiraj();
+
+//Terokkar Forest
+extern void AddSC_terokkar_forest();
+
+//Thousand Needles
+//Thunder Bluff
+extern void AddSC_thunder_bluff();
+
+//Tirisfal Glades
+extern void AddSC_tirisfal_glades();
+
+//Uldaman
+extern void AddSC_boss_ironaya();
+extern void AddSC_uldaman();
+
+//Undercity
+extern void AddSC_undercity();
+
+//Un'Goro Crater
+//Upper blackrock spire
+//Wailing caverns
+
+//Western plaguelands
+extern void AddSC_western_plaguelands();
+
+//Westfall
+//Wetlands
+//Winterspring
+extern void AddSC_winterspring();
+
+//Zangarmarsh
+extern void AddSC_zangarmarsh();
+
+//Zul'Farrak
+//Zul'Gurub
+extern void AddSC_boss_jeklik();
+extern void AddSC_boss_venoxis();
+extern void AddSC_boss_marli();
+extern void AddSC_boss_mandokir();
+extern void AddSC_boss_gahzranka();
+extern void AddSC_boss_thekal();
+extern void AddSC_boss_arlokk();
+extern void AddSC_boss_jindo();
+extern void AddSC_boss_hakkar();
+extern void AddSC_boss_grilek();
+extern void AddSC_boss_hazzarah();
+extern void AddSC_boss_renataki();
+extern void AddSC_boss_wushoolay();
+extern void AddSC_instance_zulgurub();
+//Zul'Aman
+extern void AddSC_boss_janalai();
+extern void AddSC_boss_nalorakk();
+extern void AddSC_instance_zulaman();
+extern void AddSC_zulaman();
+
+// -------------------
+void LoadDatabase()
+{
+ //Get db string from file
+ char const* dbstring = NULL;
+
+ if( !TScriptConfig.GetString("TScriptDatabaseInfo", &dbstring) )
+ {
+ error_log("TSCR: Missing Trinity Script database info from configuration file. Load database aborted.");
+ return;
+ }
+
+ //Initialize connection to DB
+ if( dbstring && TScriptDB.Initialize(dbstring) )
+ outstring_log("TSCR: TrinityScript database: %s",dbstring);
+ else
+ {
+ error_log("TSCR: Unable to connect to Database. Load database aborted.");
+ return;
+ }
+
+ //***Preform all DB queries here***
+ QueryResult *result;
+
+ //Get Version information
+ result = TScriptDB.PQuery("SELECT version FROM script_db_version");
+
+ if (result)
+ {
+ Field *fields = result->Fetch();
+ outstring_log("TSCR: Database version is: %s", fields[0].GetString());
+ outstring_log("");
+ delete result;
+
+ }else
+ {
+ error_log("TSCR: Missing `script_db_version` information.");
+ outstring_log("");
+ }
+
+ // Drop Existing Text Map, only done once and we are ready to add data from multiple sources.
+ TextMap.clear();
+
+ //TODO: Add load from eventai_texts here
+
+ // Load Script Text
+ outstring_log("TSCR: Loading Script Texts...");
+ LoadMangosStrings(TScriptDB,"script_texts",TEXT_SOURCE_RANGE,(TEXT_SOURCE_RANGE*2)+1);
+
+ // Gather Additional data from Script Texts
+ result = TScriptDB.PQuery("SELECT entry, sound, type, language FROM script_texts");
+
+ outstring_log("TSCR: Loading Script Texts additional data...");
+ if (result)
+ {
+ barGoLink bar(result->GetRowCount());
+ uint32 count = 0;
+
+ do
+ {
+ bar.step();
+ Field* fields = result->Fetch();
+ StringTextData temp;
+
+ int32 i = fields[0].GetInt32();
+ temp.SoundId = fields[1].GetInt32();
+ temp.Type = fields[2].GetInt32();
+ temp.Language = fields[3].GetInt32();
+
+ if (i >= 0)
+ {
+ error_db_log("TSCR: Entry %i in table `script_texts` is not a negative value.",i);
+ continue;
+ }
+
+ if (i > TEXT_SOURCE_RANGE || i <= TEXT_SOURCE_RANGE*2)
+ {
+ error_db_log("TSCR: Entry %i in table `script_texts` is out of accepted entry range for table.",i);
+ continue;
+ }
+
+ if (temp.SoundId)
+ {
+ if (!GetSoundEntriesStore()->LookupEntry(temp.SoundId))
+ error_db_log("TSCR: Entry %i in table `script_texts` has soundId %u but sound does not exist.",i,temp.SoundId);
+ }
+
+ if (!GetLanguageDescByID(temp.Language))
+ error_db_log("TSCR: Entry %i in table `script_texts` using Language %u but Language does not exist.",i,temp.Language);
+
+ if (temp.Type > CHAT_TYPE_BOSS_WHISPER)
+ error_db_log("TSCR: Entry %i in table `script_texts` has Type %u but this Chat Type does not exist.",i,temp.Type);
+
+ TextMap[i] = temp;
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ outstring_log("");
+ outstring_log(">> TSCR: Loaded %u additional Script Texts data.", count);
+ }else
+ {
+ barGoLink bar(1);
+ bar.step();
+ outstring_log("");
+ outstring_log(">> Loaded 0 additional Script Texts data. DB table `script_texts` is empty.");
+ }
+
+ // Load Custom Text
+ outstring_log("TSCR: Loading Custom Texts...");
+ LoadMangosStrings(TScriptDB,"custom_texts",TEXT_SOURCE_RANGE*2,(TEXT_SOURCE_RANGE*3)+1);
+
+ // Gather Additional data from Custom Texts
+ result = TScriptDB.PQuery("SELECT entry, sound, type, language FROM custom_texts");
+
+ outstring_log("TSCR: Loading Custom Texts additional data...");
+ if (result)
+ {
+ barGoLink bar(result->GetRowCount());
+ uint32 count = 0;
+
+ do
+ {
+ bar.step();
+ Field* fields = result->Fetch();
+ StringTextData temp;
+
+ int32 i = fields[0].GetInt32();
+ temp.SoundId = fields[1].GetInt32();
+ temp.Type = fields[2].GetInt32();
+ temp.Language = fields[3].GetInt32();
+
+ if (i >= 0)
+ {
+ error_db_log("TSCR: Entry %i in table `custom_texts` is not a negative value.",i);
+ continue;
+ }
+
+ if (i > TEXT_SOURCE_RANGE*2 || i <= TEXT_SOURCE_RANGE*3)
+ {
+ error_db_log("TSCR: Entry %i in table `custom_texts` is out of accepted entry range for table.",i);
+ continue;
+ }
+
+ if (temp.SoundId)
+ {
+ if (!GetSoundEntriesStore()->LookupEntry(temp.SoundId))
+ error_db_log("TSCR: Entry %i in table `custom_texts` has soundId %u but sound does not exist.",i,temp.SoundId);
+ }
+
+ if (!GetLanguageDescByID(temp.Language))
+ error_db_log("TSCR: Entry %i in table `custom_texts` using Language %u but Language does not exist.",i,temp.Language);
+
+ if (temp.Type > CHAT_TYPE_BOSS_WHISPER)
+ error_db_log("TSCR: Entry %i in table `custom_texts` has Type %u but this Chat Type does not exist.",i,temp.Type);
+
+ TextMap[i] = temp;
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ outstring_log("");
+ outstring_log(">> Loaded %u additional Custom Texts data.", count);
+ }else
+ {
+ barGoLink bar(1);
+ bar.step();
+ outstring_log("");
+ outstring_log(">> Loaded 0 additional Custom Texts data. DB table `custom_texts` is empty.");
+ }
+
+ // Drop existing Event AI Localized Text hash map
+ EventAI_LocalizedTextMap.clear();
+
+ // Gather EventAI Localized Texts
+ result = TScriptDB.PQuery("SELECT id, locale_1, locale_2, locale_3, locale_4, locale_5, locale_6, locale_7, locale_8 "
+ "FROM eventai_localized_texts");
+
+ outstring_log("TSCR: Loading EventAI Localized Texts...");
+ if(result)
+ {
+ barGoLink bar(result->GetRowCount());
+ uint32 count = 0;
+
+ do
+ {
+ Localized_Text temp;
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint32 i = fields[0].GetInt32();
+
+ temp.locale_1 = fields[1].GetString();
+ temp.locale_2 = fields[2].GetString();
+ temp.locale_3 = fields[3].GetString();
+ temp.locale_4 = fields[4].GetString();
+ temp.locale_5 = fields[5].GetString();
+ temp.locale_6 = fields[6].GetString();
+ temp.locale_7 = fields[7].GetString();
+ temp.locale_8 = fields[8].GetString();
+
+ EventAI_LocalizedTextMap[i] = temp;
+ ++count;
+
+ }while(result->NextRow());
+
+ delete result;
+
+ outstring_log("");
+ outstring_log(">> Loaded %u EventAI Localized Texts", count);
+ }else
+ {
+ barGoLink bar(1);
+ bar.step();
+ outstring_log("");
+ outstring_log(">> Loaded 0 EventAI Localized Texts. DB table `eventai_localized_texts` is empty");
+ }
+
+ //Drop existing EventAI Text hash map
+ EventAI_Text_Map.clear();
+
+ //Gather EventAI Text Entries
+ result = TScriptDB.PQuery("SELECT id, text FROM eventai_texts");
+
+ outstring_log("TSCR: Loading EventAI_Texts...");
+ if (result)
+ {
+ barGoLink bar(result->GetRowCount());
+ uint32 Count = 0;
+
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+
+ uint32 i = fields[0].GetInt32();
+
+ std::string text = fields[1].GetString();
+
+ if (!strlen(text.c_str()))
+ error_db_log("TSCR: EventAI text %u is empty", i);
+
+ EventAI_Text_Map[i] = text;
+ ++Count;
+
+ }while (result->NextRow());
+
+ delete result;
+
+ outstring_log("");
+ outstring_log(">> Loaded %u EventAI texts", Count);
+ }else
+ {
+ barGoLink bar(1);
+ bar.step();
+ outstring_log("");
+ outstring_log(">> Loaded 0 EventAI texts. DB table `eventai_texts` is empty.");
+ }
+
+ //Gather event data
+ result = TScriptDB.PQuery("SELECT id, position_x, position_y, position_z, orientation, spawntimesecs FROM eventai_summons");
+
+ //Drop Existing EventSummon Map
+ EventAI_Summon_Map.clear();
+
+ outstring_log("TSCR: Loading EventAI_Summons...");
+ if (result)
+ {
+ barGoLink bar(result->GetRowCount());
+ uint32 Count = 0;
+
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+
+ EventAI_Summon temp;
+
+ uint32 i = fields[0].GetUInt32();
+ temp.position_x = fields[1].GetFloat();
+ temp.position_y = fields[2].GetFloat();
+ temp.position_z = fields[3].GetFloat();
+ temp.orientation = fields[4].GetFloat();
+ temp.SpawnTimeSecs = fields[5].GetUInt32();
+
+ //Add to map
+ EventAI_Summon_Map[i] = temp;
+ ++Count;
+ }while (result->NextRow());
+
+ delete result;
+
+ outstring_log("");
+ outstring_log(">> Loaded %u EventAI summon definitions", Count);
+ }else
+ {
+ barGoLink bar(1);
+ bar.step();
+ outstring_log("");
+ outstring_log(">> Loaded 0 EventAI Summon definitions. DB table `eventai_summons` is empty.");
+ }
+
+ //Gather event data
+ result = TScriptDB.PQuery("SELECT id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, "
+ "event_param1, event_param2, event_param3, event_param4, "
+ "action1_type, action1_param1, action1_param2, action1_param3, "
+ "action2_type, action2_param1, action2_param2, action2_param3, "
+ "action3_type, action3_param1, action3_param2, action3_param3 "
+ "FROM eventai_scripts");
+
+ //Drop Existing EventAI List
+ EventAI_Event_List.clear();
+
+ outstring_log("TSCR: Loading EventAI_Scripts...");
+ if (result)
+ {
+ barGoLink bar(result->GetRowCount());
+ uint32 Count = 0;
+
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+
+ EventAI_Event temp;
+
+ temp.event_id = fields[0].GetUInt32();
+ uint32 i = temp.event_id;
+ temp.creature_id = fields[1].GetUInt32();
+ temp.event_type = fields[2].GetUInt16();
+ temp.event_inverse_phase_mask = fields[3].GetUInt32();
+ temp.event_chance = fields[4].GetUInt8();
+ temp.event_flags = fields[5].GetUInt8();
+ temp.event_param1 = fields[6].GetUInt32();
+ temp.event_param2 = fields[7].GetUInt32();
+ temp.event_param3 = fields[8].GetUInt32();
+ temp.event_param4 = fields[9].GetUInt32();
+
+ //Report any errors in event
+ if (temp.event_type >= EVENT_T_END)
+ error_db_log("TSCR: Event %u has incorrect event type. Maybe DB requires updated version of SD2.", i);
+
+ //No chance of this event occuring
+ if (temp.event_chance == 0)
+ error_db_log("TSCR: Event %u has 0 percent chance. Event will never trigger!", i);
+
+ //Chance above 100, force it to be 100
+ if (temp.event_chance > 100)
+ {
+ error_db_log("TSCR: Creature %u are using event %u with more than 100 percent chance. Adjusting to 100 percent.", temp.creature_id, i);
+ temp.event_chance = 100;
+ }
+
+ //Individual event checks
+ switch (temp.event_type)
+ {
+ case EVENT_T_HP:
+ case EVENT_T_MANA:
+ case EVENT_T_TARGET_HP:
+ {
+ if (temp.event_param2 > 100)
+ error_db_log("TSCR: Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i);
+
+ if (temp.event_param1 <= temp.event_param2)
+ error_db_log("TSCR: Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp.creature_id, i);
+
+ if (temp.event_flags & EFLAG_REPEATABLE && !temp.event_param3 && !temp.event_param4)
+ {
+ error_db_log("TSCR: Creature %u has param3 and param4=0 (RepeatMin/RepeatMax) but cannot be repeatable without timers. Removing EFLAG_REPEATABLE for event %u.", temp.creature_id, i);
+ temp.event_flags &= ~EFLAG_REPEATABLE;
+ }
+ }
+ break;
+
+ case EVENT_T_SPELLHIT:
+ {
+ if (temp.event_param1)
+ {
+ SpellEntry const* pSpell = GetSpellStore()->LookupEntry(temp.event_param1);
+ if (!pSpell)
+ {
+ error_db_log("TSCR: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.event_param1, i);
+ continue;
+ }
+
+ if (temp.event_param2_s != -1 && temp.event_param2 != pSpell->SchoolMask)
+ error_db_log("TSCR: Creature %u has param1(spellId %u) but param2 is not -1 and not equal to spell's school mask. Event %u can never trigger.", temp.creature_id, temp.event_param1, i);
+ }
+
+ //TODO: fix this system with SPELL_SCHOOL_MASK. Current complicate things, using int32(-1) instead of just 0
+ //SPELL_SCHOOL_MASK_NONE = 0 and does not exist, thus it can not ever trigger or be used in SpellHit()
+ if (temp.event_param2_s != -1 && temp.event_param2_s > SPELL_SCHOOL_MASK_ALL)
+ error_db_log("TSCR: Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.event_param2, i);
+
+ if (temp.event_param4 < temp.event_param3)
+ error_db_log("TSCR: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
+ }
+ break;
+
+ case EVENT_T_RANGE:
+ case EVENT_T_OOC_LOS:
+ case EVENT_T_FRIENDLY_HP:
+ case EVENT_T_FRIENDLY_IS_CC:
+ case EVENT_T_FRIENDLY_MISSING_BUFF:
+ {
+ if (temp.event_param4 < temp.event_param3)
+ error_db_log("TSCR: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
+ }
+ break;
+
+ case EVENT_T_TIMER:
+ case EVENT_T_TIMER_OOC:
+ {
+ if (temp.event_param2 < temp.event_param1)
+ error_db_log("TSCR: Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp.creature_id, i);
+
+ if (temp.event_param4 < temp.event_param3)
+ error_db_log("TSCR: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
+ }
+ break;
+
+ case EVENT_T_KILL:
+ case EVENT_T_TARGET_CASTING:
+ {
+ if (temp.event_param2 < temp.event_param1)
+ error_db_log("TSCR: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
+ }
+ break;
+
+ case EVENT_T_AGGRO:
+ case EVENT_T_DEATH:
+ case EVENT_T_EVADE:
+ case EVENT_T_SPAWNED:
+ {
+ if (temp.event_flags & EFLAG_REPEATABLE)
+ {
+ error_db_log("TSCR: Creature %u has EFLAG_REPEATABLE set. Event can never be repeatable. Removing flag for event %u.", temp.creature_id, i);
+ temp.event_flags &= ~EFLAG_REPEATABLE;
+ }
+ }
+ break;
+ }
+
+ for (uint32 j = 0; j < MAX_ACTIONS; j++)
+ {
+ temp.action[j].type = fields[10+(j*4)].GetUInt16();
+ temp.action[j].param1 = fields[11+(j*4)].GetUInt32();
+ temp.action[j].param2 = fields[12+(j*4)].GetUInt32();
+ temp.action[j].param3 = fields[13+(j*4)].GetUInt32();
+
+ //Report any errors in actions
+ switch (temp.action[j].type)
+ {
+ case ACTION_T_SAY:
+ case ACTION_T_YELL:
+ case ACTION_T_TEXTEMOTE:
+ if (GetEventAIText(temp.action[j].param1) == DEFAULT_TEXT)
+ error_db_log("TSCR: Event %u Action %u refrences missing Localized_Text entry", i, j+1);
+ break;
+
+ case ACTION_T_SOUND:
+ if (!GetSoundEntriesStore()->LookupEntry(temp.action[j].param1))
+ error_db_log("TSCR: Event %u Action %u uses non-existant SoundID %u.", i, j+1, temp.action[j].param1);
+ break;
+
+ case ACTION_T_RANDOM_SAY:
+ case ACTION_T_RANDOM_YELL:
+ case ACTION_T_RANDOM_TEXTEMOTE:
+ if ((temp.action[j].param1 != 0xffffffff && GetEventAIText(temp.action[j].param1) == DEFAULT_TEXT) ||
+ (temp.action[j].param2 != 0xffffffff && GetEventAIText(temp.action[j].param2) == DEFAULT_TEXT) ||
+ (temp.action[j].param3 != 0xffffffff && GetEventAIText(temp.action[j].param3) == DEFAULT_TEXT))
+ error_db_log("TSCR: Event %u Action %u refrences missing Localized_Text entry", i, j+1);
+ break;
+
+ case ACTION_T_CAST:
+ {
+ if (!GetSpellStore()->LookupEntry(temp.action[j].param1))
+ error_db_log("TSCR: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param1);
+
+ if (temp.action[j].param2 >= TARGET_T_END)
+ error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
+ }
+ break;
+
+ case ACTION_T_REMOVEAURASFROMSPELL:
+ {
+ if (!GetSpellStore()->LookupEntry(temp.action[j].param2))
+ error_db_log("TSCR: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2);
+
+ if (temp.action[j].param1 >= TARGET_T_END)
+ error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
+ }
+ break;
+
+ case ACTION_T_CASTCREATUREGO:
+ {
+ if (!GetSpellStore()->LookupEntry(temp.action[j].param2))
+ error_db_log("TSCR: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2);
+
+ if (temp.action[j].param3 >= TARGET_T_END)
+ error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
+ }
+ break;
+
+ //2nd param target
+ case ACTION_T_SUMMON_ID:
+ {
+ if (EventAI_Summon_Map.find(temp.action[j].param3) == EventAI_Summon_Map.end())
+ error_db_log("TSCR: Event %u Action %u summons missing EventAI_Summon %u", i, j+1, temp.action[j].param3);
+
+ if (temp.action[j].param2 >= TARGET_T_END)
+ error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
+ }
+ break;
+
+ case ACTION_T_SUMMON:
+ case ACTION_T_THREAT_SINGLE_PCT:
+ case ACTION_T_QUEST_EVENT:
+ case ACTION_T_SET_UNIT_FLAG:
+ case ACTION_T_REMOVE_UNIT_FLAG:
+ case ACTION_T_SET_INST_DATA64:
+ if (temp.action[j].param2 >= TARGET_T_END)
+ error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
+ break;
+
+ //3rd param target
+ case ACTION_T_SET_UNIT_FIELD:
+ if (temp.action[j].param3 >= TARGET_T_END)
+ error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
+ break;
+
+ case ACTION_T_SET_PHASE:
+ if (temp.action[j].param1 > 31)
+ error_db_log("TSCR: Event %u Action %u attempts to set phase > 31. Phase mask cannot be used past phase 31.", i, j+1);
+ break;
+
+ case ACTION_T_INC_PHASE:
+ if (!temp.action[j].param1)
+ error_db_log("TSCR: Event %u Action %u is incrementing phase by 0. Was this intended?", i, j+1);
+ break;
+
+ case ACTION_T_KILLED_MONSTER:
+ if (temp.event_type != EVENT_T_DEATH)
+ outstring_log("SD2 WARNING: Event %u Action %u calling ACTION_T_KILLED_MONSTER outside of EVENT_T_DEATH", i, j+1);
+ break;
+
+ case ACTION_T_SET_INST_DATA:
+ if (temp.action[j].param2 > 3)
+ error_db_log("TSCR: Event %u Action %u attempts to set instance data above encounter state 3. Custom case?", i, j+1);
+ break;
+
+ default:
+ if (temp.action[j].type >= ACTION_T_END)
+ error_db_log("TSCR: Event %u Action %u has incorrect action type. Maybe DB requires updated version of SD2.", i, j+1);
+ break;
+ }
+ }
+
+ //Add to list
+ EventAI_Event_List.push_back(temp);
+ ++Count;
+ } while (result->NextRow());
+
+ delete result;
+
+ outstring_log("");
+ outstring_log(">> Loaded %u EventAI scripts", Count);
+ }else
+ {
+ barGoLink bar(1);
+ bar.step();
+ outstring_log("");
+ outstring_log(">> Loaded 0 EventAI scripts. DB table `eventai_scripts` is empty.");
+ }
+
+ //Free database thread and resources
+ TScriptDB.HaltDelayThread();
+
+}
+
+struct TSpellSummary {
+ uint8 Targets; // set of enum SelectTarget
+ uint8 Effects; // set of enum SelectEffect
+}extern *SpellSummary;
+
+MANGOS_DLL_EXPORT
+void ScriptsFree()
+{
+ // Free Spell Summary
+ delete []SpellSummary;
+
+ // Free resources before library unload
+ for(int i=0;i<nrscripts;i++)
+ delete m_scripts[i];
+
+ nrscripts = 0;
+}
+
+MANGOS_DLL_EXPORT
+void ScriptsInit()
+{
+ bool CanLoadDB = true;
+
+ //Trinity Script startup
+ outstring_log(" _____ _ _ _ ____ _ _");
+ outstring_log("|_ _| __(_)_ __ (_) |_ _ _/ ___| ___ _ __(_)_ __ | |_ ");
+ outstring_log(" | || '__| | '_ \\| | __| | | \\___ \\ / __| \'__| | \'_ \\| __|");
+ outstring_log(" | || | | | | | | | |_| |_| |___) | (__| | | | |_) | |_ ");
+ outstring_log(" |_||_| |_|_| |_|_|\\__|\\__, |____/ \\___|_| |_| .__/ \\__|");
+ outstring_log(" |___/ |_| ");
+ outstring_log("Trinity Script initializing %s", _FULLVERSION);
+ outstring_log("");
+
+ //Get configuration file
+ if (!TScriptConfig.SetSource(_TRINITY_SCRIPT_CONFIG))
+ {
+ CanLoadDB = false;
+ error_log("TSCR: Unable to open configuration file. Database will be unaccessible. Configuration values will use default.");
+ }
+ else outstring_log("TSCR: Using configuration file %s",_TRINITY_SCRIPT_CONFIG);
+
+ //Check config file version
+ if (TScriptConfig.GetIntDefault("ConfVersion", 0) != _TSCRIPTCONFVERSION)
+ error_log("TSCR: Configuration file version doesn't match expected version. Some config variables may be wrong or missing.");
+
+ //Locale
+ Locale = TScriptConfig.GetIntDefault("Locale", 0);
+
+ if (Locale > 8)
+ {
+ Locale = 0;
+ error_log("TSCR: Locale set to invalid language id. Defaulting to 0.");
+ }
+
+ outstring_log("TSCR: Using locale %u", Locale);
+
+ EAI_ErrorLevel = TScriptConfig.GetIntDefault("EAIErrorLevel", 1);
+
+ switch (EAI_ErrorLevel)
+ {
+ case 0:
+ outstring_log("TSCR: EventAI Error Reporting level set to 0 (Startup Errors only)");
+ break;
+ case 1:
+ outstring_log("TSCR: EventAI Error Reporting level set to 1 (Startup errors and Runtime event errors)");
+ break;
+ case 2:
+ outstring_log("TSCR: EventAI Error Reporting level set to 2 (Startup errors, Runtime event errors, and Creation errors)");
+ break;
+ default:
+ outstring_log("TSCR: Unknown EventAI Error Reporting level. Defaulting to 1 (Startup errors and Runtime event errors)");
+ EAI_ErrorLevel = 1;
+ break;
+ }
+
+ outstring_log("");
+
+ //Load database (must be called after TScriptConfig.SetSource). In case it failed, no need to even try load.
+ if (CanLoadDB)
+ LoadDatabase();
+
+ outstring_log("TSCR: Loading C++ scripts");
+ barGoLink bar(1);
+ bar.step();
+ outstring_log("");
+
+ nrscripts = 0;
+ for(int i=0;i<MAX_SCRIPTS;i++)
+ m_scripts[i]=NULL;
+
+ FillSpellSummary();
+
+ // -- Scripts to be added --
+
+ // -- Areatrigger --
+ AddSC_areatrigger_scripts();
+
+ // -- Boss --
+ AddSC_boss_emeriss();
+ AddSC_boss_taerar();
+ AddSC_boss_ysondre();
+
+ // -- Creature --
+ AddSC_mob_event();
+ AddSC_generic_creature();
+
+ // -- Custom --
+ AddSC_custom_example();
+ AddSC_custom_gossip_codebox();
+ AddSC_test();
+
+ // -- GO --
+ AddSC_go_scripts();
+
+ // -- Guard --
+ AddSC_guards();
+
+ // -- Honor --
+
+ // -- Item --
+ AddSC_item_scripts();
+ AddSC_item_test();
+
+ // -- NPC --
+ AddSC_npc_professions();
+ AddSC_npcs_special();
+
+ // -- Servers --
+
+ //--------------------
+ //------ ZONE --------
+
+ //Alterac Mountains
+ AddSC_alterac_mountains();
+
+ //Arathi Highlands
+ //Ashenvale Forest
+ //Aunchindoun
+ //--Auchenai Crypts
+ AddSC_boss_exarch_maladaar();
+
+ //--Mana Tombs
+ AddSC_boss_nexusprince_shaffar();
+ AddSC_boss_pandemonius();
+
+ //--Sekketh Halls
+ AddSC_boss_darkweaver_syth();
+ AddSC_boss_talon_king_ikiss();
+ AddSC_instance_sethekk_halls();
+
+ //--Shadow Labyrinth
+ AddSC_boss_ambassador_hellmaw();
+ AddSC_boss_blackheart_the_inciter();
+ AddSC_boss_grandmaster_vorpil();
+ AddSC_boss_murmur();
+ AddSC_instance_shadow_labyrinth();
+
+ //Azshara
+ AddSC_boss_azuregos();
+ AddSC_azshara();
+
+ //Azuremyst Isle
+ AddSC_azuremyst_isle();
+
+ //Badlands
+ //Barrens
+ AddSC_the_barrens();
+
+ //Black Temple
+ AddSC_black_temple();
+ AddSC_boss_illidan();
+ AddSC_boss_shade_of_akama();
+ AddSC_boss_supremus();
+ AddSC_boss_gurtogg_bloodboil();
+ AddSC_boss_mother_shahraz();
+ AddSC_boss_reliquary_of_souls();
+ AddSC_boss_teron_gorefiend();
+ AddSC_boss_najentus();
+ AddSC_boss_illidari_council();
+ AddSC_instance_black_temple();
+
+ //Blackfathom Depths
+ //Blackrock Depths
+ AddSC_blackrock_depths();
+ AddSC_boss_ambassador_flamelash();
+ AddSC_boss_angerrel();
+ AddSC_boss_anubshiah();
+ AddSC_boss_doomrel();
+ AddSC_boss_doperel();
+ AddSC_boss_draganthaurissan();
+ AddSC_boss_general_angerforge();
+ AddSC_boss_gloomrel();
+ AddSC_boss_gorosh_the_dervish();
+ AddSC_boss_grizzle();
+ AddSC_boss_haterel();
+ AddSC_boss_high_interrogator_gerstahn();
+ AddSC_boss_magmus();
+ AddSC_boss_moira_bronzebeard();
+ AddSC_boss_seethrel();
+ AddSC_boss_vilerel();
+
+ //Blackrock Spire
+ AddSC_boss_drakkisath();
+ AddSC_boss_halycon();
+ AddSC_boss_highlordomokk();
+ AddSC_boss_mothersmolderweb();
+ AddSC_boss_overlordwyrmthalak();
+ AddSC_boss_shadowvosh();
+ AddSC_boss_thebeast();
+ AddSC_boss_warmastervoone();
+ AddSC_boss_quatermasterzigris();
+ AddSC_boss_pyroguard_emberseer();
+ AddSC_boss_gyth();
+ AddSC_boss_rend_blackhand();
+
+ //Blackwing lair
+ AddSC_boss_razorgore();
+ AddSC_boss_vael();
+ AddSC_boss_broodlord();
+ AddSC_boss_firemaw();
+ AddSC_boss_ebonroc();
+ AddSC_boss_flamegor();
+ AddSC_boss_chromaggus();
+ AddSC_boss_nefarian();
+ AddSC_boss_victor_nefarius();
+
+ //Blade's Edge Mountains
+ AddSC_blades_edge_mountains();
+
+ //Blasted lands
+ AddSC_boss_kruul();
+ AddSC_blasted_lands();
+
+ //Bloodmyst Isle
+ AddSC_bloodmyst_isle();
+
+ //Burning steppes
+ AddSC_burning_steppes();
+
+ //Caverns of Time
+ //--Battle for Mt. Hyjal
+ AddSC_hyjal();
+ AddSC_boss_archimonde();
+ AddSC_instance_mount_hyjal();
+
+ //--Old Hillsbrad
+ AddSC_boss_captain_skarloc();
+ AddSC_boss_epoch_hunter();
+ AddSC_boss_lieutenant_drake();
+ AddSC_instance_old_hillsbrad();
+ AddSC_old_hillsbrad();
+
+ //--The Dark Portal
+ AddSC_boss_aeonus();
+ AddSC_boss_chrono_lord_deja();
+ AddSC_boss_temporus();
+
+ //Coilfang Resevoir
+ //--Serpent Shrine Cavern
+ AddSC_boss_fathomlord_karathress();
+ AddSC_boss_hydross_the_unstable();
+ AddSC_boss_lady_vashj();
+ AddSC_boss_leotheras_the_blind();
+ AddSC_boss_morogrim_tidewalker();
+ AddSC_instance_serpentshrine_cavern();
+
+ //--Slave Pens
+ //--Steam Vault
+ AddSC_boss_hydromancer_thespia();
+ AddSC_boss_mekgineer_steamrigger();
+ AddSC_boss_warlord_kalithresh();
+ AddSC_instance_steam_vault();
+
+ //--Underbog
+ AddSC_boss_hungarfen();
+
+ //Darkshore
+ //Darnassus
+ //Deadmines
+ //Deadwind pass
+ //Desolace
+ //Dire Maul
+ //Dun Morogh
+ AddSC_dun_morogh();
+
+ //Durotar
+ //Duskwood
+ //Dustwallow marsh
+ AddSC_dustwallow_marsh();
+
+ //Eversong Woods
+ AddSC_eversong_woods();
+
+ //Exodar
+ //Eastern Plaguelands
+ AddSC_eastern_plaguelands();
+
+ //Elwynn Forest
+ AddSC_elwynn_forest();
+
+ //Felwood
+ AddSC_felwood();
+
+ //Feralas
+ AddSC_feralas();
+
+ //Ghostlands
+ AddSC_ghostlands();
+
+ //Gnomeregan
+ //Gruul's Lair
+ AddSC_boss_gruul();
+ AddSC_boss_high_king_maulgar();
+ AddSC_instance_gruuls_lair();
+
+ //Hellfire Citadel
+ //--Blood Furnace
+ AddSC_boss_broggok();
+ AddSC_boss_kelidan_the_breaker();
+ AddSC_boss_the_maker();
+
+ //--Magtheridon's Lair
+ AddSC_boss_magtheridon();
+ AddSC_instance_magtheridons_lair();
+
+ //--Shattered Halls
+ AddSC_boss_grand_warlock_nethekurse();
+ AddSC_boss_warbringer_omrogg();
+ AddSC_instance_shattered_halls();
+
+ //--Ramparts
+ AddSC_boss_watchkeeper_gargolmar();
+ AddSC_boss_omor_the_unscarred();
+
+ //Hellfire Peninsula
+ AddSC_boss_doomlordkazzak();
+ AddSC_hellfire_peninsula();
+
+ //Hillsbrad Foothills
+ //Hinterlands
+ //Ironforge
+ AddSC_ironforge();
+
+ //Isle of Quel'Danas
+ AddSC_isle_of_queldanas();
+
+ //Karazhan
+ AddSC_boss_attumen();
+ AddSC_boss_curator();
+ AddSC_boss_maiden_of_virtue();
+ AddSC_boss_shade_of_aran();
+ AddSC_boss_malchezaar();
+ AddSC_boss_terestian_illhoof();
+ AddSC_netherspite_infernal();
+ AddSC_boss_moroes();
+ AddSC_bosses_opera();
+ AddSC_instance_karazhan();
+ AddSC_karazhan();
+
+ //Loch Modan
+ AddSC_loch_modan();
+
+ //Lower Blackrock Spire
+
+ // Magister's Terrace
+ AddSC_boss_felblood_kaelthas();
+ AddSC_boss_selin_fireheart();
+ AddSC_boss_vexallus();
+ AddSC_boss_priestess_delrissa();
+ AddSC_instance_magisters_terrace();
+
+ //Maraudon
+ AddSC_boss_celebras_the_cursed();
+ AddSC_boss_landslide();
+ AddSC_boss_noxxion();
+ AddSC_boss_ptheradras();
+
+ //Molten core
+ AddSC_boss_lucifron();
+ AddSC_boss_magmadar();
+ AddSC_boss_gehennas();
+ AddSC_boss_garr();
+ AddSC_boss_baron_geddon();
+ AddSC_boss_shazzrah();
+ AddSC_boss_golemagg();
+ AddSC_boss_sulfuron();
+ AddSC_boss_majordomo();
+ AddSC_boss_ragnaros();
+ AddSC_instance_molten_core();
+ AddSC_molten_core();
+
+ //Moonglade
+ AddSC_moonglade();
+
+ //Mulgore
+ AddSC_mulgore();
+
+ //Nagrand
+ AddSC_nagrand();
+
+ //Naxxramas
+ AddSC_boss_anubrekhan();
+ AddSC_boss_maexxna();
+ AddSC_boss_patchwerk();
+ AddSC_boss_razuvious();
+ AddSC_boss_highlord_mograine();
+ AddSC_boss_lady_blaumeux();
+ AddSC_boss_sir_zeliek();
+ AddSC_boss_thane_korthazz();
+ AddSC_boss_kelthuzad();
+ AddSC_boss_faerlina();
+ AddSC_boss_loatheb();
+ AddSC_boss_noth();
+ AddSC_boss_gluth();
+ AddSC_boss_sapphiron();
+
+ //Netherstorm
+ AddSC_netherstorm();
+
+ //Onyxia's Lair
+ AddSC_boss_onyxia();
+
+ //Orgrimmar
+ AddSC_orgrimmar();
+
+ //Ragefire Chasm
+ //Razorfen Downs
+ AddSC_boss_amnennar_the_coldbringer();
+
+ //Redridge Mountains
+ //Ruins of Ahn'Qiraj
+ //Scarlet Monastery
+ AddSC_boss_arcanist_doan();
+ AddSC_boss_azshir_the_sleepless();
+ AddSC_boss_bloodmage_thalnos();
+ AddSC_boss_herod();
+ AddSC_boss_high_inquisitor_fairbanks();
+ AddSC_boss_high_inquisitor_whitemane();
+ AddSC_boss_houndmaster_loksey();
+ AddSC_boss_interrogator_vishas();
+ AddSC_boss_scarlet_commander_mograine();
+ AddSC_boss_scorn();
+
+ //Scholomance
+ AddSC_boss_darkmaster_gandling();
+ AddSC_boss_death_knight_darkreaver();
+ AddSC_boss_theolenkrastinov();
+ AddSC_boss_illuciabarov();
+ AddSC_boss_instructormalicia();
+ AddSC_boss_jandicebarov();
+ AddSC_boss_kormok();
+ AddSC_boss_lordalexeibarov();
+ AddSC_boss_lorekeeperpolkelt();
+ AddSC_boss_rasfrost();
+ AddSC_boss_theravenian();
+ AddSC_boss_vectus();
+ AddSC_instance_scholomance();
+
+ //Searing gorge
+ AddSC_searing_gorge();
+
+ //Shadowfang keep
+ AddSC_shadowfang_keep();
+ AddSC_instance_shadowfang_keep();
+
+ //Shadowmoon Valley
+ AddSC_boss_doomwalker();
+ AddSC_shadowmoon_valley();
+
+ //Shattrath
+ AddSC_shattrath_city();
+
+ //Silithus
+ AddSC_silithus();
+
+ //Silvermoon
+ AddSC_silvermoon_city();
+
+ //Silverpine forest
+ AddSC_silverpine_forest();
+
+ //Stockade
+ //Stonetalon mountains
+ AddSC_stonetalon_mountains();
+
+ //Stormwind City
+ AddSC_stormwind_city();
+
+ //Stranglethorn Vale
+ AddSC_stranglethorn_vale();
+
+ //Stratholme
+ AddSC_boss_magistrate_barthilas();
+ AddSC_boss_maleki_the_pallid();
+ AddSC_boss_nerubenkan();
+ AddSC_boss_cannon_master_willey();
+ AddSC_boss_baroness_anastari();
+ AddSC_boss_ramstein_the_gorger();
+ AddSC_boss_timmy_the_cruel();
+ AddSC_boss_postmaster_malown();
+ AddSC_boss_baron_rivendare();
+ AddSC_boss_dathrohan_balnazzar();
+ AddSC_boss_order_of_silver_hand();
+ AddSC_instance_stratholme();
+ AddSC_stratholme();
+
+ //Sunken Temple
+ //Tanaris
+ AddSC_tanaris();
+
+ //Teldrassil
+ //Tempest Keep
+ //--Arcatraz
+ AddSC_arcatraz();
+ AddSC_boss_harbinger_skyriss();
+ AddSC_instance_arcatraz();
+
+ //--Botanica
+ AddSC_boss_high_botanist_freywinn();
+ AddSC_boss_laj();
+ AddSC_boss_warp_splinter();
+
+ //--The Eye
+ AddSC_boss_kaelthas();
+ AddSC_boss_void_reaver();
+ AddSC_boss_high_astromancer_solarian();
+ AddSC_instance_the_eye();
+ AddSC_the_eye();
+
+ //--The Mechanar
+ AddSC_boss_gatewatcher_iron_hand();
+ AddSC_boss_nethermancer_sepethrea();
+
+ //Temple of ahn'qiraj
+ AddSC_boss_cthun();
+ AddSC_boss_fankriss();
+ AddSC_boss_huhuran();
+ AddSC_bug_trio();
+ AddSC_boss_sartura();
+ AddSC_boss_skeram();
+ AddSC_boss_twinemperors();
+ AddSC_mob_anubisath_sentinel();
+ AddSC_instance_temple_of_ahnqiraj();
+
+ //Terokkar Forest
+ AddSC_terokkar_forest();
+
+ //Thousand Needles
+ //Thunder Bluff
+ AddSC_thunder_bluff();
+
+ //Tirisfal Glades
+ AddSC_tirisfal_glades();
+
+ //Uldaman
+ AddSC_boss_ironaya();
+ AddSC_uldaman();
+
+ //Undercity
+ AddSC_undercity();
+
+ //Un'Goro Crater
+ //Upper blackrock spire
+ //Wailing caverns
+
+ //Western plaguelands
+ AddSC_western_plaguelands();
+
+ //Westfall
+ //Wetlands
+ //Winterspring
+ AddSC_winterspring();
+
+ //Zangarmarsh
+ AddSC_zangarmarsh();
+
+ //Zul'Farrak
+ //Zul'Gurub
+ AddSC_boss_jeklik();
+ AddSC_boss_venoxis();
+ AddSC_boss_marli();
+ AddSC_boss_mandokir();
+ AddSC_boss_gahzranka();
+ AddSC_boss_thekal();
+ AddSC_boss_arlokk();
+ AddSC_boss_jindo();
+ AddSC_boss_hakkar();
+ AddSC_boss_grilek();
+ AddSC_boss_hazzarah();
+ AddSC_boss_renataki();
+ AddSC_boss_wushoolay();
+ AddSC_instance_zulgurub();
+
+ //Zul'Aman
+ AddSC_boss_janalai();
+ AddSC_boss_nalorakk();
+ AddSC_instance_zulaman();
+ AddSC_zulaman();
+
+ // -------------------
+
+ outstring_log("TSCR: Loaded %u C++ Scripts", nrscripts);
+ outstring_log("");
+}
+
+//*********************************
+//*** Functions used internally ***
+
+const char* GetEventAILocalizedText(uint32 entry)
+{
+ if (entry == 0xffffffff)
+ error_log("TSCR: Entry = -1, GetEventAILocalizedText should not be called in this case.");
+
+ const char* temp = NULL;
+
+ HM_NAMESPACE::hash_map<uint32, Localized_Text>::iterator i = EventAI_LocalizedTextMap.find(entry);
+
+ if (i == EventAI_LocalizedTextMap.end())
+ {
+ error_log("TSCR: EventAI Localized Text %u not found", entry);
+ return DEFAULT_TEXT;
+ }
+
+ switch (Locale)
+ {
+ case 1:
+ temp = (*i).second.locale_1.c_str();
+ break;
+
+ case 2:
+ temp = (*i).second.locale_2.c_str();
+ break;
+
+ case 3:
+ temp = (*i).second.locale_3.c_str();
+ break;
+
+ case 4:
+ temp = (*i).second.locale_4.c_str();
+ break;
+
+ case 5:
+ temp = (*i).second.locale_5.c_str();
+ break;
+
+ case 6:
+ temp = (*i).second.locale_6.c_str();
+ break;
+
+ case 7:
+ temp = (*i).second.locale_7.c_str();
+ break;
+
+ case 8:
+ temp = (*i).second.locale_8.c_str();
+ break;
+ };
+
+ if (strlen(temp))
+ return temp;
+
+ return DEFAULT_TEXT;
+}
+
+const char* GetEventAIText(uint32 entry)
+{
+ if(entry == 0xffffffff)
+ error_log("TSCR: Entry = -1, GetEventAIText should not be called in this case.");
+
+ const char* str = NULL;
+
+ HM_NAMESPACE::hash_map<uint32, std::string>::iterator itr = EventAI_Text_Map.find(entry);
+ if(itr == EventAI_Text_Map.end())
+ {
+ error_log("TSCR: Unable to find EventAI Text %u", entry);
+ return DEFAULT_TEXT;
+ }
+
+ str = (*itr).second.c_str();
+
+ if(strlen(str))
+ return str;
+
+ if(strlen((*itr).second.c_str()))
+ return (*itr).second.c_str();
+
+ return DEFAULT_TEXT;
+}
+
+void DoScriptText(int32 textEntry, WorldObject* pSource, Unit* target)
+{
+ if (!pSource)
+ {
+ error_log("TSCR: DoScriptText entry %i, invalid Source pointer.",textEntry);
+ return;
+ }
+
+ if (textEntry >= 0)
+ {
+ error_log("TSCR: DoScriptText attempts to process entry %i, but entry must be negative.",textEntry);
+ return;
+ }
+
+ HM_NAMESPACE::hash_map<int32, StringTextData>::iterator i = TextMap.find(textEntry);
+
+ if (i == TextMap.end())
+ {
+ error_log("TSCR: DoScriptText could not find text entry %i.",textEntry);
+ return;
+ }
+
+ if((*i).second.SoundId)
+ {
+ if( GetSoundEntriesStore()->LookupEntry((*i).second.SoundId) )
+ {
+ pSource->SendPlaySound((*i).second.SoundId, false);
+ }
+ else
+ error_log("TSCR: DoScriptText entry %i tried to process invalid sound id %u.",textEntry,(*i).second.SoundId);
+ }
+
+ switch((*i).second.Type)
+ {
+ case CHAT_TYPE_SAY:
+ pSource->MonsterSay(textEntry, (*i).second.Language, target ? target->GetGUID() : 0);
+ break;
+ case CHAT_TYPE_YELL:
+ pSource->MonsterYell(textEntry, (*i).second.Language, target ? target->GetGUID() : 0);
+ break;
+ case CHAT_TYPE_TEXT_EMOTE:
+ pSource->MonsterTextEmote(textEntry, target ? target->GetGUID() : 0);
+ break;
+ case CHAT_TYPE_BOSS_EMOTE:
+ pSource->MonsterTextEmote(textEntry, target ? target->GetGUID() : 0, true);
+ break;
+ case CHAT_TYPE_WHISPER:
+ {
+ if (target && target->GetTypeId() == TYPEID_PLAYER)
+ pSource->MonsterWhisper(textEntry, target->GetGUID());
+ else error_log("TSCR: DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER).", textEntry);
+ }break;
+ case CHAT_TYPE_BOSS_WHISPER:
+ {
+ if (target && target->GetTypeId() == TYPEID_PLAYER)
+ pSource->MonsterWhisper(textEntry, target->GetGUID(), true);
+ else error_log("TSCR: DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER).", textEntry);
+ }break;
+ }
+}
+
+Script* GetScriptByName(std::string Name)
+{
+ if(Name.empty())
+ return NULL;
+
+ for(int i=0;i<MAX_SCRIPTS;i++)
+ {
+ if( m_scripts[i] && m_scripts[i]->Name == Name )
+ return m_scripts[i];
+ }
+ return NULL;
+}
+
+//********************************
+//*** Functions to be Exported ***
+
+MANGOS_DLL_EXPORT
+bool GossipHello ( Player * player, Creature *_Creature )
+{
+ Script *tmpscript = GetScriptByName(_Creature->GetScriptName());
+ if(!tmpscript || !tmpscript->pGossipHello) return false;
+
+ player->PlayerTalkClass->ClearMenus();
+ return tmpscript->pGossipHello(player,_Creature);
+}
+
+MANGOS_DLL_EXPORT
+bool GossipSelect( Player *player, Creature *_Creature, uint32 sender, uint32 action )
+{
+ debug_log("TSCR: Gossip selection, sender: %d, action: %d",sender, action);
+
+ Script *tmpscript = GetScriptByName(_Creature->GetScriptName());
+ if(!tmpscript || !tmpscript->pGossipSelect) return false;
+
+ player->PlayerTalkClass->ClearMenus();
+ return tmpscript->pGossipSelect(player,_Creature,sender,action);
+}
+
+MANGOS_DLL_EXPORT
+bool GossipSelectWithCode( Player *player, Creature *_Creature, uint32 sender, uint32 action, const char* sCode )
+{
+ debug_log("TSCR: Gossip selection with code, sender: %d, action: %d",sender, action);
+
+ Script *tmpscript = GetScriptByName(_Creature->GetScriptName());
+ if(!tmpscript || !tmpscript->pGossipSelectWithCode) return false;
+
+ player->PlayerTalkClass->ClearMenus();
+ return tmpscript->pGossipSelectWithCode(player,_Creature,sender,action,sCode);
+}
+
+MANGOS_DLL_EXPORT
+bool QuestAccept( Player *player, Creature *_Creature, Quest const *_Quest )
+{
+ Script *tmpscript = GetScriptByName(_Creature->GetScriptName());
+ if(!tmpscript || !tmpscript->pQuestAccept) return false;
+
+ player->PlayerTalkClass->ClearMenus();
+ return tmpscript->pQuestAccept(player,_Creature,_Quest);
+}
+
+MANGOS_DLL_EXPORT
+bool QuestSelect( Player *player, Creature *_Creature, Quest const *_Quest )
+{
+ Script *tmpscript = GetScriptByName(_Creature->GetScriptName());
+ if(!tmpscript || !tmpscript->pQuestSelect) return false;
+
+ player->PlayerTalkClass->ClearMenus();
+ return tmpscript->pQuestSelect(player,_Creature,_Quest);
+}
+
+MANGOS_DLL_EXPORT
+bool QuestComplete( Player *player, Creature *_Creature, Quest const *_Quest )
+{
+ Script *tmpscript = GetScriptByName(_Creature->GetScriptName());
+ if(!tmpscript || !tmpscript->pQuestComplete) return false;
+
+ player->PlayerTalkClass->ClearMenus();
+ return tmpscript->pQuestComplete(player,_Creature,_Quest);
+}
+
+MANGOS_DLL_EXPORT
+bool ChooseReward( Player *player, Creature *_Creature, Quest const *_Quest, uint32 opt )
+{
+ Script *tmpscript = GetScriptByName(_Creature->GetScriptName());
+ if(!tmpscript || !tmpscript->pChooseReward) return false;
+
+ player->PlayerTalkClass->ClearMenus();
+ return tmpscript->pChooseReward(player,_Creature,_Quest,opt);
+}
+
+MANGOS_DLL_EXPORT
+uint32 NPCDialogStatus( Player *player, Creature *_Creature )
+{
+ Script *tmpscript = GetScriptByName(_Creature->GetScriptName());
+ if(!tmpscript || !tmpscript->pNPCDialogStatus) return 100;
+
+ player->PlayerTalkClass->ClearMenus();
+ return tmpscript->pNPCDialogStatus(player,_Creature);
+}
+
+MANGOS_DLL_EXPORT
+uint32 GODialogStatus( Player *player, GameObject *_GO )
+{
+ Script *tmpscript = GetScriptByName(_GO->GetGOInfo()->ScriptName);
+ if(!tmpscript || !tmpscript->pGODialogStatus) return 100;
+
+ player->PlayerTalkClass->ClearMenus();
+ return tmpscript->pGODialogStatus(player,_GO);
+}
+
+MANGOS_DLL_EXPORT
+bool ItemHello( Player *player, Item *_Item, Quest const *_Quest )
+{
+ Script *tmpscript = GetScriptByName(_Item->GetProto()->ScriptName);
+ if(!tmpscript || !tmpscript->pItemHello) return false;
+
+ player->PlayerTalkClass->ClearMenus();
+ return tmpscript->pItemHello(player,_Item,_Quest);
+}
+
+MANGOS_DLL_EXPORT
+bool ItemQuestAccept( Player *player, Item *_Item, Quest const *_Quest )
+{
+ Script *tmpscript = GetScriptByName(_Item->GetProto()->ScriptName);
+ if(!tmpscript || !tmpscript->pItemQuestAccept) return false;
+
+ player->PlayerTalkClass->ClearMenus();
+ return tmpscript->pItemQuestAccept(player,_Item,_Quest);
+}
+
+MANGOS_DLL_EXPORT
+bool GOHello( Player *player, GameObject *_GO )
+{
+ Script *tmpscript = GetScriptByName(_GO->GetGOInfo()->ScriptName);
+ if(!tmpscript || !tmpscript->pGOHello) return false;
+
+ player->PlayerTalkClass->ClearMenus();
+ return tmpscript->pGOHello(player,_GO);
+}
+
+MANGOS_DLL_EXPORT
+bool GOQuestAccept( Player *player, GameObject *_GO, Quest const *_Quest )
+{
+ Script *tmpscript = GetScriptByName(_GO->GetGOInfo()->ScriptName);
+ if(!tmpscript || !tmpscript->pGOQuestAccept) return false;
+
+ player->PlayerTalkClass->ClearMenus();
+ return tmpscript->pGOQuestAccept(player,_GO,_Quest);
+}
+
+MANGOS_DLL_EXPORT
+bool GOChooseReward( Player *player, GameObject *_GO, Quest const *_Quest, uint32 opt )
+{
+ Script *tmpscript = GetScriptByName(_GO->GetGOInfo()->ScriptName);
+ if(!tmpscript || !tmpscript->pGOChooseReward) return false;
+
+ player->PlayerTalkClass->ClearMenus();
+ return tmpscript->pGOChooseReward(player,_GO,_Quest,opt);
+}
+
+MANGOS_DLL_EXPORT
+bool AreaTrigger( Player *player, AreaTriggerEntry * atEntry)
+{
+ Script *tmpscript = NULL;
+
+ tmpscript = GetScriptByName(GetAreaTriggerScriptNameById(atEntry->id));
+ if(!tmpscript || !tmpscript->pAreaTrigger) return false;
+
+ return tmpscript->pAreaTrigger(player, atEntry);
+}
+
+MANGOS_DLL_EXPORT
+CreatureAI* GetAI(Creature *_Creature)
+{
+ Script *tmpscript = GetScriptByName(_Creature->GetScriptName());
+
+ if(!tmpscript || !tmpscript->GetAI) return NULL;
+ return tmpscript->GetAI(_Creature);
+}
+
+MANGOS_DLL_EXPORT
+bool ItemUse( Player *player, Item* _Item, SpellCastTargets const& targets)
+{
+ Script *tmpscript = GetScriptByName(_Item->GetProto()->ScriptName);
+ if(!tmpscript || !tmpscript->pItemUse) return false;
+
+ return tmpscript->pItemUse(player,_Item,targets);
+}
+
+MANGOS_DLL_EXPORT
+bool ReceiveEmote( Player *player, Creature *_Creature, uint32 emote )
+{
+ Script *tmpscript = GetScriptByName(_Creature->GetScriptName());
+ if(!tmpscript || !tmpscript->pReceiveEmote) return false;
+
+ return tmpscript->pReceiveEmote(player, _Creature, emote);
+}
+
+MANGOS_DLL_EXPORT
+InstanceData* CreateInstanceData(Map *map)
+{
+ Script *tmpscript = NULL;
+
+ if(!map->IsDungeon()) return false;
+
+ tmpscript = GetScriptByName(((InstanceMap*)map)->GetScript());
+ if(!tmpscript || !tmpscript->GetInstanceData) return false;
+
+ return tmpscript->GetInstanceData(map);
+}
diff --git a/src/bindings/scripts/ScriptMgr.h b/src/bindings/scripts/ScriptMgr.h index a0f541e23b0..8ff1ee3cfdd 100644 --- a/src/bindings/scripts/ScriptMgr.h +++ b/src/bindings/scripts/ScriptMgr.h @@ -1,94 +1,88 @@ -/* Copyright (C) 2006 - 2008 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> -* This program is free software licensed under GPL version 2 -* Please see the included DOCS/LICENSE.TXT for more information */ - -#ifndef SCRIPTMGR_H -#define SCRIPTMGR_H - -#include "Common.h" -#include "Platform/CompilerDefs.h" -#include "Database/DBCStructure.h" - -class Player; -class Creature; -class CreatureAI; -class InstanceData; -class Quest; -class Item; -class GameObject; -class SpellCastTargets; -class Map; -class Unit; -class WorldObject; - -#define MAX_SCRIPTS 1000 //72 bytes each (approx 71kb) - -//MAX visible range (size of grid) -#define VISIBLE_RANGE (166.0f) - -#define DEFAULT_TEXT "<Trinity Script Text Entry Missing!>" - -// -struct Script -{ - Script() : -pGossipHello(NULL), pQuestAccept(NULL), pGossipSelect(NULL), pGossipSelectWithCode(NULL), -pQuestSelect(NULL), pQuestComplete(NULL), pNPCDialogStatus(NULL), pGODialogStatus(NULL), pChooseReward(NULL), -pItemHello(NULL), pGOHello(NULL), pAreaTrigger(NULL), pItemQuestAccept(NULL), pGOQuestAccept(NULL), -pGOChooseReward(NULL),pReceiveEmote(NULL),pItemUse(NULL), GetAI(NULL), GetInstanceData(NULL) -{} - -std::string Name; - -// -- Quest/gossip Methods to be scripted -- -bool (*pGossipHello )(Player*, Creature*); -bool (*pQuestAccept )(Player*, Creature*, Quest const* ); -bool (*pGossipSelect )(Player*, Creature*, uint32 , uint32 ); -bool (*pGossipSelectWithCode)(Player*, Creature*, uint32 , uint32 , const char* ); -bool (*pQuestSelect )(Player*, Creature*, Quest const* ); -bool (*pQuestComplete )(Player*, Creature*, Quest const* ); -uint32 (*pNPCDialogStatus )(Player*, Creature* ); -uint32 (*pGODialogStatus )(Player *player, GameObject * _GO ); -bool (*pChooseReward )(Player*, Creature*, Quest const*, uint32 ); -bool (*pItemHello )(Player*, Item*, Quest const* ); -bool (*pGOHello )(Player*, GameObject* ); -bool (*pAreaTrigger )(Player*, AreaTriggerEntry* ); -bool (*pItemQuestAccept )(Player*, Item *, Quest const* ); -bool (*pGOQuestAccept )(Player*, GameObject*, Quest const* ); -bool (*pGOChooseReward )(Player*, GameObject*_GO, Quest const*, uint32 ); -bool (*pReceiveEmote )(Player*, Creature*, uint32 ); -bool (*pItemUse )(Player*, Item*, SpellCastTargets const& ); - -CreatureAI* (*GetAI)(Creature*); -InstanceData* (*GetInstanceData)(Map*); -// ----------------------------------------- -}; - -extern int nrscripts; -extern Script *m_scripts[MAX_SCRIPTS]; - -// Localized Text function -const char* GetEventAILocalizedText(uint32 entry); -const char* GetScriptLocalizedText(uint32 entry); - -//EventAI text function -const char* GetEventAIText(uint32 entry); // TODO: Locales - -// Script Text function -void ProcessScriptText(uint32 id, WorldObject* pSource, Unit* target = NULL); // TODO: Locales - -#if COMPILER == COMPILER_GNU -#define FUNC_PTR(name,callconvention,returntype,parameters) typedef returntype(*name)parameters __attribute__ ((callconvention)); -#else -#define FUNC_PTR(name, callconvention, returntype, parameters) typedef returntype(callconvention *name)parameters; -#endif - -#ifdef WIN32 - #define MANGOS_DLL_EXPORT extern "C" __declspec(dllexport) -#elif defined( __GNUC__ ) - #define MANGOS_DLL_EXPORT extern "C" -#else - #define MANGOS_DLL_EXPORT extern "C" export -#endif - -#endif +/* Copyright (C) 2006 - 2008 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
+ * This program is free software licensed under GPL version 2
+ * Please see the included DOCS/LICENSE.TXT for more information */
+
+#ifndef SCRIPTMGR_H
+#define SCRIPTMGR_H
+
+#include "Common.h"
+#include "Platform/CompilerDefs.h"
+#include "Database/DBCStructure.h"
+
+class Player;
+class Creature;
+class CreatureAI;
+class InstanceData;
+class Quest;
+class Item;
+class GameObject;
+class SpellCastTargets;
+class Map;
+class Unit;
+class WorldObject;
+
+#define MAX_SCRIPTS 1000 //72 bytes each (approx 71kb)
+#define VISIBLE_RANGE (166.0f) //MAX visible range (size of grid)
+#define DEFAULT_TEXT "<Trinity Script Text Entry Missing!>"
+
+struct Script
+{
+ Script() :
+pGossipHello(NULL), pQuestAccept(NULL), pGossipSelect(NULL), pGossipSelectWithCode(NULL),
+pQuestSelect(NULL), pQuestComplete(NULL), pNPCDialogStatus(NULL), pGODialogStatus(NULL), pChooseReward(NULL),
+pItemHello(NULL), pGOHello(NULL), pAreaTrigger(NULL), pItemQuestAccept(NULL), pGOQuestAccept(NULL),
+pGOChooseReward(NULL),pReceiveEmote(NULL),pItemUse(NULL), GetAI(NULL), GetInstanceData(NULL)
+{}
+
+std::string Name;
+
+// Quest/gossip Methods to be scripted
+bool (*pGossipHello )(Player*, Creature*);
+bool (*pQuestAccept )(Player*, Creature*, Quest const* );
+bool (*pGossipSelect )(Player*, Creature*, uint32 , uint32 );
+bool (*pGossipSelectWithCode)(Player*, Creature*, uint32 , uint32 , const char* );
+bool (*pQuestSelect )(Player*, Creature*, Quest const* );
+bool (*pQuestComplete )(Player*, Creature*, Quest const* );
+uint32 (*pNPCDialogStatus )(Player*, Creature* );
+uint32 (*pGODialogStatus )(Player *player, GameObject * _GO );
+bool (*pChooseReward )(Player*, Creature*, Quest const*, uint32 );
+bool (*pItemHello )(Player*, Item*, Quest const* );
+bool (*pGOHello )(Player*, GameObject* );
+bool (*pAreaTrigger )(Player*, AreaTriggerEntry* );
+bool (*pItemQuestAccept )(Player*, Item *, Quest const* );
+bool (*pGOQuestAccept )(Player*, GameObject*, Quest const* );
+bool (*pGOChooseReward )(Player*, GameObject*_GO, Quest const*, uint32 );
+bool (*pReceiveEmote )(Player*, Creature*, uint32 );
+bool (*pItemUse )(Player*, Item*, SpellCastTargets const& );
+
+CreatureAI* (*GetAI)(Creature*);
+InstanceData* (*GetInstanceData)(Map*);
+};
+
+extern int nrscripts;
+extern Script *m_scripts[MAX_SCRIPTS];
+
+// Localized Text function
+const char* GetEventAILocalizedText(uint32 entry);
+
+//EventAI text function
+const char* GetEventAIText(uint32 entry); // TODO: Locales
+
+//Generic scripting text function
+void DoScriptText(int32 textEntry, WorldObject* pSource, Unit* target = NULL);
+
+#if COMPILER == COMPILER_GNU
+#define FUNC_PTR(name,callconvention,returntype,parameters) typedef returntype(*name)parameters __attribute__ ((callconvention));
+#else
+#define FUNC_PTR(name, callconvention, returntype, parameters) typedef returntype(callconvention *name)parameters;
+#endif
+
+#ifdef WIN32
+ #define MANGOS_DLL_EXPORT extern "C" __declspec(dllexport)
+#elif defined( __GNUC__ )
+ #define MANGOS_DLL_EXPORT extern "C"
+#else
+ #define MANGOS_DLL_EXPORT extern "C" export
+#endif
+
+#endif
diff --git a/src/bindings/scripts/scripts/zone/karazhan/boss_prince_malchezaar.cpp b/src/bindings/scripts/scripts/zone/karazhan/boss_prince_malchezaar.cpp index 757745d7a15..40111e7c35c 100644 --- a/src/bindings/scripts/scripts/zone/karazhan/boss_prince_malchezaar.cpp +++ b/src/bindings/scripts/scripts/zone/karazhan/boss_prince_malchezaar.cpp @@ -399,7 +399,7 @@ struct MANGOS_DLL_DECL boss_malchezaarAI : public ScriptedAI EnfeebleResetTimer=0;
}else EnfeebleResetTimer -= diff;
- if(m_creature->hasUnitState(UNIT_STAT_STUNDED)) //While shifting to phase 2 malchezaar stuns himself
+ if(m_creature->hasUnitState(UNIT_STAT_STUNNED)) //While shifting to phase 2 malchezaar stuns himself
return;
if(m_creature->GetUInt64Value(UNIT_FIELD_TARGET)!=m_creature->getVictim()->GetGUID())
diff --git a/src/bindings/scripts/scripts/zone/magisters_terrace/boss_priestess_delrissa.cpp b/src/bindings/scripts/scripts/zone/magisters_terrace/boss_priestess_delrissa.cpp index a37069542cc..531fe4077aa 100644 --- a/src/bindings/scripts/scripts/zone/magisters_terrace/boss_priestess_delrissa.cpp +++ b/src/bindings/scripts/scripts/zone/magisters_terrace/boss_priestess_delrissa.cpp @@ -1002,7 +1002,7 @@ struct MANGOS_DLL_DECL boss_garaxxasAI : public boss_priestess_guestAI Freezing_Trap_Timer = 30000;
}else Freezing_Trap_Timer -= diff;
- if(!m_creature->getVictim()->hasUnitState(UNIT_STAT_STUNDED | UNIT_STAT_ROOT | UNIT_STAT_CONFUSED | UNIT_STAT_DISTRACTED))
+ if(!m_creature->getVictim()->hasUnitState(UNIT_STAT_STUNNED | UNIT_STAT_ROOT | UNIT_STAT_CONFUSED | UNIT_STAT_DISTRACTED))
DoMeleeAttackIfReady();
}else
{
diff --git a/src/bindings/scripts/scripts/zone/magisters_terrace/boss_selin_fireheart.cpp b/src/bindings/scripts/scripts/zone/magisters_terrace/boss_selin_fireheart.cpp index 6f1da25f220..96f8a52ff4f 100644 --- a/src/bindings/scripts/scripts/zone/magisters_terrace/boss_selin_fireheart.cpp +++ b/src/bindings/scripts/scripts/zone/magisters_terrace/boss_selin_fireheart.cpp @@ -16,7 +16,7 @@ /* ScriptData
SDName: Boss_Selin_Fireheart
-SD%Complete: 99
+SD%Complete: 90
SDComment: Heroic and Normal Support. Needs further testing.
SDCategory: Magister's Terrace
EndScriptData */
@@ -73,7 +73,7 @@ struct MANGOS_DLL_DECL boss_selin_fireheartAI : public ScriptedAI for(uint8 i = 0; i < size; ++i)
{
uint64 guid = pInstance->GetData64(DATA_FEL_CRYSTAL);
- outstring_log("Selin: Adding Fel Crystal %u to list", guid);
+ debug_log("SD2: Selin: Adding Fel Crystal %u to list", guid);
Crystals.push_back(guid);
}
}
@@ -116,9 +116,9 @@ struct MANGOS_DLL_DECL boss_selin_fireheartAI : public ScriptedAI }
GameObject* Door = GameObject::GetGameObject(*m_creature, pInstance->GetData64(DATA_SELIN_ENCOUNTER_DOOR));
- if(Door)
- Door->SetGoState(0); // Close the door. Open it only in JustDied.
-
+ if( Door )
+ Door->SetGoState(0); // Open the big encounter door. Close it in Aggro and open it only in JustDied(and here)
+ // Small door opened after event are expected to be closed by default
// Set Inst data for encounter
pInstance->SetData(DATA_SELIN_EVENT, NOT_STARTED);
}else error_log(ERROR_INST_DATA);
@@ -160,10 +160,11 @@ struct MANGOS_DLL_DECL boss_selin_fireheartAI : public ScriptedAI }
}
}
- if(CrystalChosen)
+ if( CrystalChosen )
{
DoYell(SAY_ENERGY, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_ENERGY);
+
CrystalChosen->CastSpell(CrystalChosen, SPELL_FEL_CRYSTAL_COSMETIC, true);
float x, y, z; // coords that we move to, close to the crystal.
@@ -185,7 +186,7 @@ struct MANGOS_DLL_DECL boss_selin_fireheartAI : public ScriptedAI {
//Creature* pCrystal = ((Creature*)Unit::GetUnit(*m_creature, FelCrystals[i]));
Creature* pCrystal = ((Creature*)Unit::GetUnit(*m_creature, *itr));
- if(pCrystal && pCrystal->isAlive())
+ if( pCrystal && pCrystal->isAlive())
pCrystal->DealDamage(pCrystal, pCrystal->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
}
}
@@ -194,6 +195,13 @@ struct MANGOS_DLL_DECL boss_selin_fireheartAI : public ScriptedAI {
DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_AGGRO);
+
+ if( pInstance )
+ {
+ GameObject* EncounterDoor = GameObject::GetGameObject(*m_creature, pInstance->GetData64(DATA_SELIN_ENCOUNTER_DOOR));
+ if( EncounterDoor )
+ EncounterDoor->SetGoState(1); //Close the encounter door, open it in JustDied/Reset
+ }
}
void KilledUnit(Unit* victim)
@@ -245,9 +253,14 @@ struct MANGOS_DLL_DECL boss_selin_fireheartAI : public ScriptedAI }
pInstance->SetData(DATA_SELIN_EVENT, DONE); // Encounter complete!
+
GameObject* EncounterDoor = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_SELIN_ENCOUNTER_DOOR));
- if(EncounterDoor)
- EncounterDoor->SetGoState(1); // Open the door
+ if( EncounterDoor )
+ EncounterDoor->SetGoState(0); // Open the encounter door
+
+ GameObject* ContinueDoor = GameObject::GetGameObject(*m_creature, pInstance->GetData64(DATA_SELIN_DOOR));
+ if( ContinueDoor )
+ ContinueDoor->SetGoState(0); // Open the door leading further in
ShatterRemainingCrystals();
}
@@ -302,23 +315,27 @@ struct MANGOS_DLL_DECL boss_selin_fireheartAI : public ScriptedAI }else
{
- if(IsDraining)
- if(EmpowerTimer < diff)
+ if( IsDraining )
{
- IsDraining = false;
- DrainingCrystal = false;
- DoYell(SAY_EMPOWERED, LANG_UNIVERSAL, NULL);
- DoPlaySoundToSet(m_creature, SOUND_EMPOWERED);
- Unit* CrystalChosen = Unit::GetUnit(*m_creature, CrystalGUID);
- if(CrystalChosen && CrystalChosen->isAlive())
- // Use Deal Damage to kill it, not setDeathState.
- CrystalChosen->DealDamage(CrystalChosen, CrystalChosen->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
-
- CrystalGUID = 0;
-
- m_creature->GetMotionMaster()->Clear();
- m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim());
- }else EmpowerTimer -= diff;
+ if( EmpowerTimer < diff )
+ {
+ IsDraining = false;
+ DrainingCrystal = false;
+
+ DoYell(SAY_EMPOWERED, LANG_UNIVERSAL, NULL);
+ DoPlaySoundToSet(m_creature, SOUND_EMPOWERED);
+
+ Unit* CrystalChosen = Unit::GetUnit(*m_creature, CrystalGUID);
+ if( CrystalChosen && CrystalChosen->isAlive() )
+ // Use Deal Damage to kill it, not setDeathState.
+ CrystalChosen->DealDamage(CrystalChosen, CrystalChosen->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+
+ CrystalGUID = 0;
+
+ m_creature->GetMotionMaster()->Clear();
+ m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim());
+ }else EmpowerTimer -= diff;
+ }
}
DoMeleeAttackIfReady(); // No need to check if we are draining crystal here, as the spell has a stun.
diff --git a/src/bindings/scripts/scripts/zone/magisters_terrace/instance_magisters_terrace.cpp b/src/bindings/scripts/scripts/zone/magisters_terrace/instance_magisters_terrace.cpp index b0d3e90f645..49303a06a2a 100644 --- a/src/bindings/scripts/scripts/zone/magisters_terrace/instance_magisters_terrace.cpp +++ b/src/bindings/scripts/scripts/zone/magisters_terrace/instance_magisters_terrace.cpp @@ -16,7 +16,7 @@ /* ScriptData
SDName: Instance_Magisters_Terrace
-SD%Complete: 100
+SD%Complete: 60
SDComment: Designed only for Selin Fireheart
SDCategory: Magister's Terrace
EndScriptData */
@@ -118,15 +118,24 @@ struct MANGOS_DLL_DECL instance_magisters_terrace : public ScriptedInstance {
switch(entry)
{
- case 24723:
- SelinGUID = creature->GetGUID();
- break;
- case 24560:
- DelrissaGUID = creature->GetGUID();
- break;
- case 24722:
- FelCrystals.push_back(creature->GetGUID());
- break;
+ case 24723: SelinGUID = creature->GetGUID(); break;
+ case 24560: DelrissaGUID = creature->GetGUID(); break;
+ case 24722: FelCrystals.push_back(creature->GetGUID()); break;
+ }
+ }
+
+ void OnObjectCreate(GameObject* go)
+ {
+ switch(go->GetEntry())
+ {
+ case 187896: VexallusDoorGUID = go->GetGUID(); break;
+ //SunwellRaid Gate 02
+ case 187979: SelinDoorGUID = go->GetGUID(); break;
+ //Assembly Chamber Door
+ case 188065: SelinEncounterDoorGUID = go->GetGUID(); break;
+ case 187770: DelrissaDoorGUID = go->GetGUID(); break;
+ case 188165: KaelStatue[0] = go->GetGUID(); break;
+ case 188166: KaelStatue[1] = go->GetGUID(); break;
}
}
@@ -164,19 +173,6 @@ struct MANGOS_DLL_DECL instance_magisters_terrace : public ScriptedInstance }
return 0;
}
-
- void OnObjectCreate(GameObject* go)
- {
- switch(go->GetEntry())
- {
- case 187896: VexallusDoorGUID = go->GetGUID(); break;
- case 187979: SelinDoorGUID = go->GetGUID(); break;
- case 188118: SelinEncounterDoorGUID = go->GetGUID(); break;
- case 187770: DelrissaDoorGUID = go->GetGUID(); break;
- case 188165: KaelStatue[0] = go->GetGUID(); break;
- case 188166: KaelStatue[1] = go->GetGUID(); break;
- }
- }
};
InstanceData* GetInstanceData_instance_magisters_terrace(Map* map)
diff --git a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_twinemperors.cpp b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_twinemperors.cpp index e7bd93f5461..b2565f0e79a 100644 --- a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_twinemperors.cpp +++ b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_twinemperors.cpp @@ -87,7 +87,7 @@ struct MANGOS_DLL_DECL boss_twinemperorsAI : public ScriptedAI AfterTeleportTimer = 0;
Abuse_Bug_Timer = 10000 + rand()%7000;
BugsTimer = 2000;
- m_creature->clearUnitState(UNIT_STAT_STUNDED);
+ m_creature->clearUnitState(UNIT_STAT_STUNNED);
DontYellWhenDead = false;
EnrageTimer = 15*60000;
}
@@ -290,7 +290,7 @@ struct MANGOS_DLL_DECL boss_twinemperorsAI : public ScriptedAI DoStopAttack();
DoResetThreat();
DoCast(m_creature, SPELL_TWIN_TELEPORT_VISUAL);
- m_creature->addUnitState(UNIT_STAT_STUNDED);
+ m_creature->addUnitState(UNIT_STAT_STUNNED);
AfterTeleport = true;
AfterTeleportTimer = 2000;
tspellcasted = false;
@@ -302,9 +302,9 @@ struct MANGOS_DLL_DECL boss_twinemperorsAI : public ScriptedAI {
if (!tspellcasted)
{
- m_creature->clearUnitState(UNIT_STAT_STUNDED);
+ m_creature->clearUnitState(UNIT_STAT_STUNNED);
DoCast(m_creature, SPELL_TWIN_TELEPORT);
- m_creature->addUnitState(UNIT_STAT_STUNDED);
+ m_creature->addUnitState(UNIT_STAT_STUNNED);
}
tspellcasted = true;
@@ -312,7 +312,7 @@ struct MANGOS_DLL_DECL boss_twinemperorsAI : public ScriptedAI if (AfterTeleportTimer < diff)
{
AfterTeleport = false;
- m_creature->clearUnitState(UNIT_STAT_STUNDED);
+ m_creature->clearUnitState(UNIT_STAT_STUNNED);
Unit *nearu = PickNearestPlayer();
//DoYell(nearu->GetName(), LANG_UNIVERSAL, 0);
AttackStart(nearu);
diff --git a/src/bindings/scripts/sql/scriptdev2_structure.sql b/src/bindings/scripts/sql/scriptdev2_structure.sql index 66ae5ac6cbf..122deb53357 100644 --- a/src/bindings/scripts/sql/scriptdev2_structure.sql +++ b/src/bindings/scripts/sql/scriptdev2_structure.sql @@ -73,31 +73,41 @@ PRIMARY KEY (`id`) DROP TABLE IF EXISTS `script_texts`;
CREATE TABLE `script_texts` (
-`id` int(11) unsigned NOT NULL auto_increment COMMENT 'Identifier',
-`sound` int(11) unsigned NOT NULL default '0',
-`type` int(11) unsigned NOT NULL default '0',
-`language` int(11) unsigned NOT NULL default '0',
-`text` varchar(255) NOT NULL default '',
-`comment` varchar(255) NOT NULL default '',
-PRIMARY KEY (`id`)
+ `entry` mediumint(8) NOT NULL,
+ `content_default` text NOT NULL,
+ `content_loc1` text,
+ `content_loc2` text,
+ `content_loc3` text,
+ `content_loc4` text,
+ `content_loc5` text,
+ `content_loc6` text,
+ `content_loc7` text,
+ `content_loc8` text,
+ `sound` mediumint(8) unsigned NOT NULL default '0',
+ `type` tinyint unsigned NOT NULL default '0',
+ `language` tinyint unsigned NOT NULL default '0',
+ `comment` text,
+ PRIMARY KEY (`entry`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Script Texts';
-
-DROP TABLE IF EXISTS `script_localized_texts`;
-CREATE TABLE `script_localized_texts` (
-`id` int(11) unsigned NOT NULL auto_increment COMMENT 'Identifier',
-`locale_1` varchar(255) NOT NULL default '',
-`locale_2` varchar(255) NOT NULL default '',
-`locale_3` varchar(255) NOT NULL default '',
-`locale_4` varchar(255) NOT NULL default '',
-`locale_5` varchar(255) NOT NULL default '',
-`locale_6` varchar(255) NOT NULL default '',
-`locale_7` varchar(255) NOT NULL default '',
-`locale_8` varchar(255) NOT NULL default '',
-`comment` varchar(255) NOT NULL default '' COMMENT 'Text Comment',
-PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Localized Script Text';
-
+DROP TABLE IF EXISTS `custom_texts`;
+CREATE TABLE `custom_texts` (
+ `entry` mediumint(8) NOT NULL,
+ `content_default` text NOT NULL,
+ `content_loc1` text,
+ `content_loc2` text,
+ `content_loc3` text,
+ `content_loc4` text,
+ `content_loc5` text,
+ `content_loc6` text,
+ `content_loc7` text,
+ `content_loc8` text,
+ `sound` mediumint(8) unsigned NOT NULL default '0',
+ `type` tinyint unsigned NOT NULL default '0',
+ `language` tinyint unsigned NOT NULL default '0',
+ `comment` text,
+ PRIMARY KEY (`entry`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Custom Texts';
DROP TABLE IF EXISTS `script_db_version`;
CREATE TABLE `script_db_version` (
diff --git a/src/game/AggressorAI.cpp b/src/game/AggressorAI.cpp index a4ef9e39f47..58604183660 100644 --- a/src/game/AggressorAI.cpp +++ b/src/game/AggressorAI.cpp @@ -47,7 +47,7 @@ AggressorAI::MoveInLineOfSight(Unit *u) if( !i_creature.canFly() && i_creature.GetDistanceZ(u) > CREATURE_Z_ATTACK_RANGE )
return;
- if( !i_creature.getVictim() && !i_creature.hasUnitState(UNIT_STAT_STUNDED) && u->isTargetableForAttack() &&
+ if( !i_creature.getVictim() && !i_creature.hasUnitState(UNIT_STAT_STUNNED) && u->isTargetableForAttack() &&
( i_creature.IsHostileTo( u ) /*|| u->getVictim() && i_creature.IsFriendlyTo( u->getVictim() )*/ ) &&
u->isInAccessablePlaceFor(&i_creature) )
{
diff --git a/src/game/ArenaTeamHandler.cpp b/src/game/ArenaTeamHandler.cpp index 33786e93532..58293cb96cb 100644 --- a/src/game/ArenaTeamHandler.cpp +++ b/src/game/ArenaTeamHandler.cpp @@ -1,462 +1,463 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "WorldSession.h" -#include "WorldPacket.h" -#include "Log.h" -#include "Database/DatabaseEnv.h" -#include "Player.h" -#include "ObjectMgr.h" -#include "ArenaTeam.h" -#include "World.h" -#include "SocialMgr.h" - -void WorldSession::HandleInspectArenaStatsOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("MSG_INSPECT_ARENA_TEAMS"); - //recv_data.hexlike(); - - CHECK_PACKET_SIZE(recv_data, 8); - - uint64 guid; - recv_data >> guid; - sLog.outDebug("Inspect Arena stats " I64FMTD, guid); - - if(Player *plr = objmgr.GetPlayer(guid)) - { - for (uint8 i = 0; i < MAX_ARENA_SLOT; i++) - { - if(uint32 a_id = plr->GetArenaTeamId(i)) - { - if(ArenaTeam *at = objmgr.GetArenaTeamById(a_id)) - at->InspectStats(this, plr->GetGUID()); - } - } - } -} - -void WorldSession::HandleArenaTeamQueryOpcode(WorldPacket & recv_data) -{ - sLog.outDebug( "WORLD: Received CMSG_ARENA_TEAM_QUERY" ); - //recv_data.hexlike(); - - CHECK_PACKET_SIZE(recv_data, 4); - - uint32 ArenaTeamId; - recv_data >> ArenaTeamId; - - ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId); - if(!arenateam) // arena team not found - return; - - arenateam->Query(this); - arenateam->Stats(this); -} - -void WorldSession::HandleArenaTeamRosterOpcode(WorldPacket & recv_data) -{ - sLog.outDebug( "WORLD: Received CMSG_ARENA_TEAM_ROSTER" ); - //recv_data.hexlike(); - - CHECK_PACKET_SIZE(recv_data, 4); - - uint32 ArenaTeamId; // arena team id - recv_data >> ArenaTeamId; - - ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId); - if(!arenateam) - return; - - arenateam->Roster(this); -} - -void WorldSession::HandleArenaTeamAddMemberOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("CMSG_ARENA_TEAM_ADD_MEMBER"); - //recv_data.hexlike(); - - CHECK_PACKET_SIZE(recv_data, 4+1); - - uint32 ArenaTeamId; // arena team id - std::string Invitedname; - - Player * player = NULL; - - recv_data >> ArenaTeamId >> Invitedname; - - if(!Invitedname.empty()) - { - if(!normalizePlayerName(Invitedname)) - return; - - player = ObjectAccessor::Instance().FindPlayerByName(Invitedname.c_str()); - } - - if(!player) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", Invitedname, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S); - return; - } - - if(player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) - { - //SendArenaTeamCommandResult(ARENA_TEAM_INVITE_SS,"",Invitedname,ARENA_TEAM_PLAYER_NOT_FOUND_S); - // can't find related opcode - SendNotification("%s is not high enough level to join your team", player->GetName()); - return; - } - - ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId); - if(!arenateam) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM); - return; - } - - // OK result but not send invite - if(player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow())) - return; - - if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeam() != GetPlayer()->GetTeam()) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED); - return; - } - - if(player->GetArenaTeamId(arenateam->GetSlot())) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, player->GetName(), "", ERR_ALREADY_IN_ARENA_TEAM_S); - return; - } - - if(player->GetArenaTeamIdInvited()) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, player->GetName(), "", ERR_ALREADY_INVITED_TO_ARENA_TEAM_S); - return; - } - - if(arenateam->GetMembersSize() >= arenateam->GetType() * 2) - { - // should send an "arena team is full" or the likes message, I just don't know the proper values so... ERR_INTERNAL -// SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_INTERNAL); - SendNotification("Your arena team is full, %s cannot join it.", player->GetName()); - return; - } - - sLog.outDebug("Player %s Invited %s to Join his ArenaTeam", GetPlayer()->GetName(), Invitedname.c_str()); - - player->SetArenaTeamIdInvited(arenateam->GetId()); - - WorldPacket data(SMSG_ARENA_TEAM_INVITE, (8+10)); - data << GetPlayer()->GetName(); - data << arenateam->GetName(); - player->GetSession()->SendPacket(&data); - - sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_INVITE"); -} - -void WorldSession::HandleArenaTeamInviteAcceptOpcode(WorldPacket & /*recv_data*/) -{ - sLog.outDebug("CMSG_ARENA_TEAM_INVITE_ACCEPT"); // empty opcode - - ArenaTeam *at = objmgr.GetArenaTeamById(_player->GetArenaTeamIdInvited()); - if(!at) - { - // arena team not exist - return; - } - - if(_player->GetArenaTeamId(at->GetSlot())) - { - // already in arena team that size - return; - } - - // not let enemies sign petition - if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && _player->GetTeam() != objmgr.GetPlayerTeamByGUID(at->GetCaptain())) - return; - - if(!at->AddMember(_player->GetGUID())) - return; - - // event - WorldPacket data; - BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_JOIN_SS, 2, _player->GetName(), at->GetName(), ""); - at->BroadcastPacket(&data); -} - -void WorldSession::HandleArenaTeamInviteDeclineOpcode(WorldPacket & /*recv_data*/) -{ - sLog.outDebug("CMSG_ARENA_TEAM_INVITE_DECLINE"); // empty opcode - - _player->SetArenaTeamIdInvited(0); // no more invited -} - -void WorldSession::HandleArenaTeamLeaveOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("CMSG_ARENA_TEAM_LEAVE"); - //recv_data.hexlike(); - - CHECK_PACKET_SIZE(recv_data, 4); - - uint32 ArenaTeamId; // arena team id - recv_data >> ArenaTeamId; - - ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); - if(!at) - { - // send command result - return; - } - if(_player->GetGUID() == at->GetCaptain() && at->GetMembersSize() > 1) - { - // check for correctness - SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S); - return; - } - // arena team has only one member (=captain) - if(_player->GetGUID() == at->GetCaptain()) - { - at->Disband(this); - delete at; - return; - } - - at->DelMember(_player->GetGUID()); - - // event - WorldPacket data; - BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_LEAVE_SS, 2, _player->GetName(), at->GetName(), ""); - at->BroadcastPacket(&data); - - //SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, at->GetName(), "", 0); -} - -void WorldSession::HandleArenaTeamDisbandOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("CMSG_ARENA_TEAM_DISBAND"); - //recv_data.hexlike(); - - CHECK_PACKET_SIZE(recv_data, 4); - - uint32 ArenaTeamId; // arena team id - recv_data >> ArenaTeamId; - - ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); - if(!at) - { - // arena team not found - return; - } - - if(at->GetCaptain() != _player->GetGUID()) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS); - return; - } - - at->Disband(this); - delete at; -} - -void WorldSession::HandleArenaTeamRemoveFromTeamOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("CMSG_ARENA_TEAM_REMOVE_FROM_TEAM"); - //recv_data.hexlike(); - - CHECK_PACKET_SIZE(recv_data, 4+1); - - uint32 ArenaTeamId; - std::string name; - - recv_data >> ArenaTeamId; - recv_data >> name; - - ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); - if(!at) - { - // arena team not found - return; - } - - uint64 guid = objmgr.GetPlayerGUIDByName(name); - if(!guid) - { - // player guid not found - return; - } - - if(at->GetCaptain() == guid) - { - // unsure - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS); - return; - } - - if(at->GetCaptain() != _player->GetGUID()) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS); - return; - } - - if(at->GetCaptain() == guid) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S); - return; - } - - at->DelMember(guid); - - // event - WorldPacket data; - BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_REMOVE_SSS, 3, name, at->GetName(), _player->GetName()); - at->BroadcastPacket(&data); -} - -void WorldSession::HandleArenaTeamPromoteToCaptainOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("CMSG_ARENA_TEAM_PROMOTE_TO_CAPTAIN"); - //recv_data.hexlike(); - - CHECK_PACKET_SIZE(recv_data, 4+1); - - uint32 ArenaTeamId; - std::string name; - - recv_data >> ArenaTeamId; - recv_data >> name; - - ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); - if(!at) - { - // arena team not found - return; - } - - uint64 guid = objmgr.GetPlayerGUIDByName(name); - if(!guid) - { - // player guid not found - return; - } - - if(at->GetCaptain() == guid) - { - // target player already captain - return; - } - - if(at->GetCaptain() != _player->GetGUID()) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS); - return; - } - - at->SetCaptain(guid); - - // event - WorldPacket data; - BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_LEADER_CHANGED_SSS, 3, _player->GetName(), name, at->GetName()); - at->BroadcastPacket(&data); -} - -void WorldSession::SendArenaTeamCommandResult(uint32 unk1, std::string str1, std::string str2, uint32 unk3) -{ - WorldPacket data(SMSG_ARENA_TEAM_COMMAND_RESULT, 4+str1.length()+1+str2.length()+1+4); - data << unk1; - data << str1; - data << str2; - data << unk3; - SendPacket(&data); -} - -void WorldSession::BuildArenaTeamEventPacket(WorldPacket *data, uint8 eventid, uint8 str_count, std::string str1, std::string str2, std::string str3) -{ - data->Initialize(SMSG_ARENA_TEAM_EVENT, 1+1+1); - *data << eventid; - *data << str_count; - switch(str_count) - { - case 1: - *data << str1; - break; - case 2: - *data << str1; - *data << str2; - break; - case 3: - *data << str1; - *data << str2; - *data << str3; - break; - default: - sLog.outError("Unhandled str_count %u in SendArenaTeamEvent()", str_count); - return; - } -} - -void WorldSession::SendNotInArenaTeamPacket(uint8 type) -{ - WorldPacket data(SMSG_ARENA_ERROR, 4+1); // 886 - You are not in a %uv%u arena team - uint32 unk = 0; - data << uint32(unk); // unk(0) - if(!unk) - data << uint8(type); // team type (2=2v2,3=3v3,5=5v5), can be used for custom types... - SendPacket(&data); -} - -/* -+ERR_ARENA_NO_TEAM_II "You are not in a %dv%d arena team" - -+ERR_ARENA_TEAM_CREATE_S "%s created. To disband, use /teamdisband [2v2, 3v3, 5v5]." -+ERR_ARENA_TEAM_INVITE_SS "You have invited %s to join %s" -+ERR_ARENA_TEAM_QUIT_S "You are no longer a member of %s" -ERR_ARENA_TEAM_FOUNDER_S "Congratulations, you are a founding member of %s! To leave, use /teamquit [2v2, 3v3, 5v5]." - -+ERR_ARENA_TEAM_INTERNAL "Internal arena team error" -+ERR_ALREADY_IN_ARENA_TEAM "You are already in an arena team of that size" -+ERR_ALREADY_IN_ARENA_TEAM_S "%s is already in an arena team of that size" -+ERR_INVITED_TO_ARENA_TEAM "You have already been invited into an arena team" -+ERR_ALREADY_INVITED_TO_ARENA_TEAM_S "%s has already been invited to an arena team" -+ERR_ARENA_TEAM_NAME_INVALID "That name contains invalid characters, please enter a new name" -+ERR_ARENA_TEAM_NAME_EXISTS_S "There is already an arena team named \"%s\"" -+ERR_ARENA_TEAM_LEADER_LEAVE_S "You must promote a new team captain using /teamcaptain before leaving the team" -+ERR_ARENA_TEAM_PERMISSIONS "You don't have permission to do that" -+ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM "You are not in an arena team of that size" -+ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM_SS "%s is not in %s" -+ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S "\"%s\" not found" -+ERR_ARENA_TEAM_NOT_ALLIED "You cannot invite players from the opposing alliance" - -+ERR_ARENA_TEAM_JOIN_SS "%s has joined %s" -+ERR_ARENA_TEAM_YOU_JOIN_S "You have joined %s. To leave, use /teamquit [2v2, 3v3, 5v5]." - -+ERR_ARENA_TEAM_LEAVE_SS "%s has left %s" - -+ERR_ARENA_TEAM_LEADER_IS_SS "%s is the captain of %s" -+ERR_ARENA_TEAM_LEADER_CHANGED_SSS "%s has made %s the new captain of %s" - -+ERR_ARENA_TEAM_REMOVE_SSS "%s has been kicked out of %s by %s" - -+ERR_ARENA_TEAM_DISBANDED_S "%s has disbanded %s" - -ERR_ARENA_TEAM_TARGET_TOO_LOW_S "%s is not high enough level to join your team" - -ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S "%s is full" - -ERR_ARENA_TEAM_LEVEL_TOO_LOW_I "You must be level %d to form an arena team" -*/ +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "WorldSession.h"
+#include "WorldPacket.h"
+#include "Log.h"
+#include "Database/DatabaseEnv.h"
+#include "Player.h"
+#include "ObjectMgr.h"
+#include "ArenaTeam.h"
+#include "World.h"
+#include "SocialMgr.h"
+#include "Language.h"
+
+void WorldSession::HandleInspectArenaStatsOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug("MSG_INSPECT_ARENA_TEAMS");
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ uint64 guid;
+ recv_data >> guid;
+ sLog.outDebug("Inspect Arena stats " I64FMTD, guid);
+
+ if(Player *plr = objmgr.GetPlayer(guid))
+ {
+ for (uint8 i = 0; i < MAX_ARENA_SLOT; i++)
+ {
+ if(uint32 a_id = plr->GetArenaTeamId(i))
+ {
+ if(ArenaTeam *at = objmgr.GetArenaTeamById(a_id))
+ at->InspectStats(this, plr->GetGUID());
+ }
+ }
+ }
+}
+
+void WorldSession::HandleArenaTeamQueryOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug( "WORLD: Received CMSG_ARENA_TEAM_QUERY" );
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ uint32 ArenaTeamId;
+ recv_data >> ArenaTeamId;
+
+ ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId);
+ if(!arenateam) // arena team not found
+ return;
+
+ arenateam->Query(this);
+ arenateam->Stats(this);
+}
+
+void WorldSession::HandleArenaTeamRosterOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug( "WORLD: Received CMSG_ARENA_TEAM_ROSTER" );
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ uint32 ArenaTeamId; // arena team id
+ recv_data >> ArenaTeamId;
+
+ ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId);
+ if(!arenateam)
+ return;
+
+ arenateam->Roster(this);
+}
+
+void WorldSession::HandleArenaTeamAddMemberOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug("CMSG_ARENA_TEAM_ADD_MEMBER");
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 4+1);
+
+ uint32 ArenaTeamId; // arena team id
+ std::string Invitedname;
+
+ Player * player = NULL;
+
+ recv_data >> ArenaTeamId >> Invitedname;
+
+ if(!Invitedname.empty())
+ {
+ if(!normalizePlayerName(Invitedname))
+ return;
+
+ player = ObjectAccessor::Instance().FindPlayerByName(Invitedname.c_str());
+ }
+
+ if(!player)
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", Invitedname, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S);
+ return;
+ }
+
+ if(player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ //SendArenaTeamCommandResult(ARENA_TEAM_INVITE_SS,"",Invitedname,ARENA_TEAM_PLAYER_NOT_FOUND_S);
+ // can't find related opcode
+ SendNotification(LANG_HIS_ARENA_LEVEL_REQ_ERROR, player->GetName());
+ return;
+ }
+
+ ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId);
+ if(!arenateam)
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM);
+ return;
+ }
+
+ // OK result but not send invite
+ if(player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
+ return;
+
+ if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeam() != GetPlayer()->GetTeam())
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
+ return;
+ }
+
+ if(player->GetArenaTeamId(arenateam->GetSlot()))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, player->GetName(), "", ERR_ALREADY_IN_ARENA_TEAM_S);
+ return;
+ }
+
+ if(player->GetArenaTeamIdInvited())
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, player->GetName(), "", ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
+ return;
+ }
+
+ if(arenateam->GetMembersSize() >= arenateam->GetType() * 2)
+ {
+ // should send an "arena team is full" or the likes message, I just don't know the proper values so... ERR_INTERNAL
+// SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_INTERNAL);
+ SendNotification(LANG_YOUR_ARENA_TEAM_FULL, player->GetName());
+ return;
+ }
+
+ sLog.outDebug("Player %s Invited %s to Join his ArenaTeam", GetPlayer()->GetName(), Invitedname.c_str());
+
+ player->SetArenaTeamIdInvited(arenateam->GetId());
+
+ WorldPacket data(SMSG_ARENA_TEAM_INVITE, (8+10));
+ data << GetPlayer()->GetName();
+ data << arenateam->GetName();
+ player->GetSession()->SendPacket(&data);
+
+ sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_INVITE");
+}
+
+void WorldSession::HandleArenaTeamInviteAcceptOpcode(WorldPacket & /*recv_data*/)
+{
+ sLog.outDebug("CMSG_ARENA_TEAM_INVITE_ACCEPT"); // empty opcode
+
+ ArenaTeam *at = objmgr.GetArenaTeamById(_player->GetArenaTeamIdInvited());
+ if(!at)
+ {
+ // arena team not exist
+ return;
+ }
+
+ if(_player->GetArenaTeamId(at->GetSlot()))
+ {
+ // already in arena team that size
+ return;
+ }
+
+ // not let enemies sign petition
+ if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && _player->GetTeam() != objmgr.GetPlayerTeamByGUID(at->GetCaptain()))
+ return;
+
+ if(!at->AddMember(_player->GetGUID()))
+ return;
+
+ // event
+ WorldPacket data;
+ BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_JOIN_SS, 2, _player->GetName(), at->GetName(), "");
+ at->BroadcastPacket(&data);
+}
+
+void WorldSession::HandleArenaTeamInviteDeclineOpcode(WorldPacket & /*recv_data*/)
+{
+ sLog.outDebug("CMSG_ARENA_TEAM_INVITE_DECLINE"); // empty opcode
+
+ _player->SetArenaTeamIdInvited(0); // no more invited
+}
+
+void WorldSession::HandleArenaTeamLeaveOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug("CMSG_ARENA_TEAM_LEAVE");
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ uint32 ArenaTeamId; // arena team id
+ recv_data >> ArenaTeamId;
+
+ ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId);
+ if(!at)
+ {
+ // send command result
+ return;
+ }
+ if(_player->GetGUID() == at->GetCaptain() && at->GetMembersSize() > 1)
+ {
+ // check for correctness
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S);
+ return;
+ }
+ // arena team has only one member (=captain)
+ if(_player->GetGUID() == at->GetCaptain())
+ {
+ at->Disband(this);
+ delete at;
+ return;
+ }
+
+ at->DelMember(_player->GetGUID());
+
+ // event
+ WorldPacket data;
+ BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_LEAVE_SS, 2, _player->GetName(), at->GetName(), "");
+ at->BroadcastPacket(&data);
+
+ //SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, at->GetName(), "", 0);
+}
+
+void WorldSession::HandleArenaTeamDisbandOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug("CMSG_ARENA_TEAM_DISBAND");
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ uint32 ArenaTeamId; // arena team id
+ recv_data >> ArenaTeamId;
+
+ ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId);
+ if(!at)
+ {
+ // arena team not found
+ return;
+ }
+
+ if(at->GetCaptain() != _player->GetGUID())
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
+ return;
+ }
+
+ at->Disband(this);
+ delete at;
+}
+
+void WorldSession::HandleArenaTeamRemoveFromTeamOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug("CMSG_ARENA_TEAM_REMOVE_FROM_TEAM");
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 4+1);
+
+ uint32 ArenaTeamId;
+ std::string name;
+
+ recv_data >> ArenaTeamId;
+ recv_data >> name;
+
+ ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId);
+ if(!at)
+ {
+ // arena team not found
+ return;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(name);
+ if(!guid)
+ {
+ // player guid not found
+ return;
+ }
+
+ if(at->GetCaptain() == guid)
+ {
+ // unsure
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
+ return;
+ }
+
+ if(at->GetCaptain() != _player->GetGUID())
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
+ return;
+ }
+
+ if(at->GetCaptain() == guid)
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S);
+ return;
+ }
+
+ at->DelMember(guid);
+
+ // event
+ WorldPacket data;
+ BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_REMOVE_SSS, 3, name, at->GetName(), _player->GetName());
+ at->BroadcastPacket(&data);
+}
+
+void WorldSession::HandleArenaTeamPromoteToCaptainOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug("CMSG_ARENA_TEAM_PROMOTE_TO_CAPTAIN");
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 4+1);
+
+ uint32 ArenaTeamId;
+ std::string name;
+
+ recv_data >> ArenaTeamId;
+ recv_data >> name;
+
+ ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId);
+ if(!at)
+ {
+ // arena team not found
+ return;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(name);
+ if(!guid)
+ {
+ // player guid not found
+ return;
+ }
+
+ if(at->GetCaptain() == guid)
+ {
+ // target player already captain
+ return;
+ }
+
+ if(at->GetCaptain() != _player->GetGUID())
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
+ return;
+ }
+
+ at->SetCaptain(guid);
+
+ // event
+ WorldPacket data;
+ BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_LEADER_CHANGED_SSS, 3, _player->GetName(), name, at->GetName());
+ at->BroadcastPacket(&data);
+}
+
+void WorldSession::SendArenaTeamCommandResult(uint32 unk1, std::string str1, std::string str2, uint32 unk3)
+{
+ WorldPacket data(SMSG_ARENA_TEAM_COMMAND_RESULT, 4+str1.length()+1+str2.length()+1+4);
+ data << unk1;
+ data << str1;
+ data << str2;
+ data << unk3;
+ SendPacket(&data);
+}
+
+void WorldSession::BuildArenaTeamEventPacket(WorldPacket *data, uint8 eventid, uint8 str_count, std::string str1, std::string str2, std::string str3)
+{
+ data->Initialize(SMSG_ARENA_TEAM_EVENT, 1+1+1);
+ *data << eventid;
+ *data << str_count;
+ switch(str_count)
+ {
+ case 1:
+ *data << str1;
+ break;
+ case 2:
+ *data << str1;
+ *data << str2;
+ break;
+ case 3:
+ *data << str1;
+ *data << str2;
+ *data << str3;
+ break;
+ default:
+ sLog.outError("Unhandled str_count %u in SendArenaTeamEvent()", str_count);
+ return;
+ }
+}
+
+void WorldSession::SendNotInArenaTeamPacket(uint8 type)
+{
+ WorldPacket data(SMSG_ARENA_ERROR, 4+1); // 886 - You are not in a %uv%u arena team
+ uint32 unk = 0;
+ data << uint32(unk); // unk(0)
+ if(!unk)
+ data << uint8(type); // team type (2=2v2,3=3v3,5=5v5), can be used for custom types...
+ SendPacket(&data);
+}
+
+/*
++ERR_ARENA_NO_TEAM_II "You are not in a %dv%d arena team"
+
++ERR_ARENA_TEAM_CREATE_S "%s created. To disband, use /teamdisband [2v2, 3v3, 5v5]."
++ERR_ARENA_TEAM_INVITE_SS "You have invited %s to join %s"
++ERR_ARENA_TEAM_QUIT_S "You are no longer a member of %s"
+ERR_ARENA_TEAM_FOUNDER_S "Congratulations, you are a founding member of %s! To leave, use /teamquit [2v2, 3v3, 5v5]."
+
++ERR_ARENA_TEAM_INTERNAL "Internal arena team error"
++ERR_ALREADY_IN_ARENA_TEAM "You are already in an arena team of that size"
++ERR_ALREADY_IN_ARENA_TEAM_S "%s is already in an arena team of that size"
++ERR_INVITED_TO_ARENA_TEAM "You have already been invited into an arena team"
++ERR_ALREADY_INVITED_TO_ARENA_TEAM_S "%s has already been invited to an arena team"
++ERR_ARENA_TEAM_NAME_INVALID "That name contains invalid characters, please enter a new name"
++ERR_ARENA_TEAM_NAME_EXISTS_S "There is already an arena team named \"%s\""
++ERR_ARENA_TEAM_LEADER_LEAVE_S "You must promote a new team captain using /teamcaptain before leaving the team"
++ERR_ARENA_TEAM_PERMISSIONS "You don't have permission to do that"
++ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM "You are not in an arena team of that size"
++ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM_SS "%s is not in %s"
++ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S "\"%s\" not found"
++ERR_ARENA_TEAM_NOT_ALLIED "You cannot invite players from the opposing alliance"
+
++ERR_ARENA_TEAM_JOIN_SS "%s has joined %s"
++ERR_ARENA_TEAM_YOU_JOIN_S "You have joined %s. To leave, use /teamquit [2v2, 3v3, 5v5]."
+
++ERR_ARENA_TEAM_LEAVE_SS "%s has left %s"
+
++ERR_ARENA_TEAM_LEADER_IS_SS "%s is the captain of %s"
++ERR_ARENA_TEAM_LEADER_CHANGED_SSS "%s has made %s the new captain of %s"
+
++ERR_ARENA_TEAM_REMOVE_SSS "%s has been kicked out of %s by %s"
+
++ERR_ARENA_TEAM_DISBANDED_S "%s has disbanded %s"
+
+ERR_ARENA_TEAM_TARGET_TOO_LOW_S "%s is not high enough level to join your team"
+
+ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S "%s is full"
+
+ERR_ARENA_TEAM_LEVEL_TOO_LOW_I "You must be level %d to form an arena team"
+*/
diff --git a/src/game/BattleGroundHandler.cpp b/src/game/BattleGroundHandler.cpp index 486f828fbda..eb9a593decf 100644 --- a/src/game/BattleGroundHandler.cpp +++ b/src/game/BattleGroundHandler.cpp @@ -1,952 +1,952 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "Common.h" -#include "WorldPacket.h" -#include "Opcodes.h" -#include "Log.h" -#include "Player.h" -#include "ObjectMgr.h" -#include "WorldSession.h" -#include "MapManager.h" -#include "ObjectAccessor.h" -#include "Object.h" -#include "Chat.h" -#include "Language.h" -#include "BattleGroundMgr.h" -#include "BattleGroundWS.h" -#include "BattleGround.h" -#include "ArenaTeam.h" - -void WorldSession::HandleBattleGroundHelloOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 8); - - uint64 guid; - recv_data >> guid; - sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_HELLO Message from: " I64FMT, guid); - - Creature *unit = ObjectAccessor::GetCreature(*_player, guid); - if(!unit) - return; - - if(!unit->isBattleMaster()) // it's not battlemaster - return; - - // Stop the npc if moving - unit->StopMoving(); - - uint32 bgTypeId = objmgr.GetBattleMasterBG(unit->GetEntry()); - - if(!_player->GetBGAccessByLevel(bgTypeId)) - { - // temp, must be gossip message... - SendNotification("You don't meet Battleground level requirements"); - return; - } - - SendBattlegGroundList(guid, bgTypeId); -} - -void WorldSession::SendBattlegGroundList( uint64 guid, uint32 bgTypeId ) -{ - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundListPacket(&data, guid, _player, bgTypeId); - SendPacket( &data ); -} - -void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 8+4+4+1); - - uint64 guid; - uint32 bgTypeId; - uint32 instanceId; - uint8 joinAsGroup; - Group * grp; - - recv_data >> guid; // battlemaster guid - recv_data >> bgTypeId; // battleground type id (DBC id) - recv_data >> instanceId; // instance id, 0 if First Available selected - recv_data >> joinAsGroup; // join as group - - if(bgTypeId >= MAX_BATTLEGROUND_TYPES) - { - sLog.outError("Battleground: invalid bgtype received. possible cheater? player guid %u",_player->GetGUIDLow()); - return; - } - - sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from: " I64FMT, guid); - - // can do this, since it's battleground, not arena - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, 0); - - // ignore if we already in BG or BG queue - if(_player->InBattleGround()) - return; - - Creature *unit = ObjectAccessor::GetCreature(*_player, guid); - if(!unit) - return; - - if(!unit->isBattleMaster()) // it's not battlemaster - return; - - // get bg instance or bg template if instance not found - BattleGround * bg = 0; - if(instanceId) - BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId); - - if(!bg && !(bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId))) - { - sLog.outError("Battleground: no available bg / template found"); - return; - } - - // check queueing conditions - if(!joinAsGroup) - { - // check Deserter debuff - if( !_player->CanJoinToBattleground() ) - { - WorldPacket data(SMSG_GROUP_JOINED_BATTLEGROUND, 4); - data << (uint32) 0xFFFFFFFE; - _player->GetSession()->SendPacket(&data); - return; - } - // check if already in queue - if (_player->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) - //player is already in this queue - return; - // check if has free queue slots - if(!_player->HasFreeBattleGroundQueueId()) - return; - } - else - { - grp = _player->GetGroup(); - // no group found, error - if(!grp) - return; - uint32 err = grp->CanJoinBattleGroundQueue(bgTypeId, bgQueueTypeId, 0, bg->GetMaxPlayersPerTeam(), false, 0); - switch(err) - { - // TODO: add error-based feedback to players in all cases - case BG_JOIN_ERR_GROUP_TOO_MANY: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_TOO_LARGE), NULL); - SendPacket(&data); - } - return; - break; - case BG_JOIN_ERR_OFFLINE_MEMBER: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_OFFLINE_MEMBER), NULL); - SendPacket(&data); - } - return; - break; - case BG_JOIN_ERR_MIXED_FACTION: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MIXED_FACTION), NULL); - SendPacket(&data); - } - return; - break; - case BG_JOIN_ERR_MIXED_LEVELS: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MIXED_LEVELS), NULL); - SendPacket(&data); - } - return; - break; - case BG_JOIN_ERR_GROUP_MEMBER_ALREADY_IN_QUEUE: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MEMBER_ALREADY_IN_QUEUE), NULL); - SendPacket(&data); - } - return; - break; - case BG_JOIN_ERR_GROUP_DESERTER: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MEMBER_DESERTER), NULL); - SendPacket(&data); - } - return; - break; - case BG_JOIN_ERR_ALL_QUEUES_USED: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MEMBER_NO_FREE_QUEUE_SLOTS), NULL); - SendPacket(&data); - } - return; - break; - // all ok, can join - case BG_JOIN_ERR_OK: - break; - // these aren't possible outcomes in bgs - case BG_JOIN_ERR_GROUP_NOT_ENOUGH: - case BG_JOIN_ERR_MIXED_ARENATEAM: - return; - break; - // not the above? shouldn't happen, don't let join - default: - return; - break; - }; - } - - // if we're here, then the conditions to join a bg are met. We can proceed in joining. - - // _player->GetGroup() was already checked, grp is already initialized - if(joinAsGroup /* && _player->GetGroup()*/) - { - sLog.outDebug("Battleground: the following players are joining as group:"); - GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, 0, false, 0); - for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *member = itr->getSource(); - if(!member) continue; // this should never happen - - uint32 queueSlot = member->AddBattleGroundQueueId(bgQueueTypeId); // add to queue - - // store entry point coords (same as leader entry point) - member->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); - - WorldPacket data; - // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, member->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0); - member->GetSession()->SendPacket(&data); - sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, bgTypeId); - member->GetSession()->SendPacket(&data); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(member, ginfo); - sLog.outDebug("Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,member->GetGUIDLow(), member->GetName()); - } - sLog.outDebug("Battleground: group end"); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel()); - } - else - { - // already checked if queueSlot is valid, now just get it - uint32 queueSlot = _player->AddBattleGroundQueueId(bgQueueTypeId); - // store entry point coords - _player->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); - - WorldPacket data; - // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0); - SendPacket(&data); - - GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, 0, false, 0); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(_player, ginfo); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel()); - sLog.outDebug("Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,_player->GetGUIDLow(), _player->GetName()); - } -} - -void WorldSession::HandleBattleGroundPlayerPositionsOpcode( WorldPacket & /*recv_data*/ ) -{ - // empty opcode - sLog.outDebug("WORLD: Recvd MSG_BATTLEGROUND_PLAYER_POSITIONS Message"); - - BattleGround *bg = _player->GetBattleGround(); - if(!bg) // can't be received if player not in battleground - return; - - if(bg->GetTypeID() == BATTLEGROUND_WS) - { - uint32 count1 = 0; - uint32 count2 = 0; - - Player *ap = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetAllianceFlagPickerGUID()); - if(ap) ++count2; - - Player *hp = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetHordeFlagPickerGUID()); - if(hp) ++count2; - - WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, (4+4+16*count1+16*count2)); - data << count1; // alliance flag holders count - /*for(uint8 i = 0; i < count1; i++) - { - data << uint64(0); // guid - data << (float)0; // x - data << (float)0; // y - }*/ - data << count2; // horde flag holders count - if(ap) - { - data << (uint64)ap->GetGUID(); - data << (float)ap->GetPositionX(); - data << (float)ap->GetPositionY(); - } - if(hp) - { - data << (uint64)hp->GetGUID(); - data << (float)hp->GetPositionX(); - data << (float)hp->GetPositionY(); - } - - SendPacket(&data); - } -} - -void WorldSession::HandleBattleGroundPVPlogdataOpcode( WorldPacket & /*recv_data*/ ) -{ - sLog.outDebug( "WORLD: Recvd MSG_PVP_LOG_DATA Message"); - - BattleGround *bg = _player->GetBattleGround(); - if(!bg) - return; - - WorldPacket data; - sBattleGroundMgr.BuildPvpLogDataPacket(&data, bg); - SendPacket(&data); - - sLog.outDebug( "WORLD: Sent MSG_PVP_LOG_DATA Message"); -} - -void WorldSession::HandleBattleGroundListOpcode( WorldPacket &recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 4); - - sLog.outDebug( "WORLD: Recvd CMSG_BATTLEFIELD_LIST Message"); - - uint32 bgTypeId; - recv_data >> bgTypeId; // id from DBC - - if(bgTypeId >= MAX_BATTLEGROUND_TYPES) - { - sLog.outError("Battleground: invalid bgtype received."); - return; - } - - BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId); - - if(!bl) - return; - - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundListPacket(&data, _player->GetGUID(), _player, bgTypeId); - SendPacket( &data ); -} - -void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 1+1+4+2+1); - - sLog.outDebug( "WORLD: Recvd CMSG_BATTLEFIELD_PORT Message"); - - uint8 type; // arenatype if arena - uint8 unk2; // unk, can be 0x0 (may be if was invited?) and 0x1 - uint32 instanceId; - uint32 bgTypeId; // type id from dbc - uint16 unk; // 0x1F90 constant? - uint8 action; // enter battle 0x1, leave queue 0x0 - - recv_data >> type >> unk2 >> bgTypeId >> unk >> action; - - if(bgTypeId >= MAX_BATTLEGROUND_TYPES) - { - sLog.outError("Battleground: invalid bgtype received."); - // update battleground slots for the player to fix his UI and sent data. - // this is a HACK, I don't know why the client starts sending invalid packets in the first place. - // it usually happens with extremely high latency (if debugging / stepping in the code for example) - if(_player->InBattleGroundQueue()) - { - // update all queues, send invitation info if player is invited, queue info if queued - for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - { - uint32 queue_id = _player->GetBattleGroundQueueId(i); - if(!queue_id) - continue; - BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); - // if the player is not in queue, contine - if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) - continue; - - // no group information, this should never happen - if(!itrPlayerStatus->second.GroupInfo) - continue; - - BattleGround * bg = NULL; - - // get possibly needed data from groupinfo - bgTypeId = itrPlayerStatus->second.GroupInfo->BgTypeId; - uint8 arenatype = itrPlayerStatus->second.GroupInfo->ArenaType; - uint8 israted = itrPlayerStatus->second.GroupInfo->IsRated; - uint8 status = 0; - - - if(!itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID) - { - // not invited to bg, get template - bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - status = STATUS_WAIT_QUEUE; - } - else - { - // get the bg we're invited to - BattleGround * bg = sBattleGroundMgr.GetBattleGround(itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID); - status = STATUS_WAIT_JOIN; - } - - // if bg not found, then continue - if(!bg) - continue; - - // don't invite if already in the instance - if(_player->InBattleGround() && _player->GetBattleGround() && _player->GetBattleGround()->GetInstanceID() == bg->GetInstanceID()) - continue; - - // re - invite player with proper data - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, itrPlayerStatus->second.GroupInfo->Team?itrPlayerStatus->second.GroupInfo->Team:_player->GetTeam(), i, status, INVITE_ACCEPT_WAIT_TIME, 0, arenatype, israted); - SendPacket(&data); - } - } - return; - } - - uint32 bgQueueTypeId = 0; - // get the bg what we were invited to - BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus; - bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId,type); - itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); - - if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) - { - sLog.outError("Battleground: itrplayerstatus not found."); - return; - } - instanceId = itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID; - - // if action == 1, then instanceId is _required_ - if(!instanceId && action == 1) - { - sLog.outError("Battleground: instance not found."); - return; - } - - BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId); - - // bg template might and must be used in case of leaving queue, when instance is not created yet - if(!bg && action == 0) - bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - - if(!bg) - { - sLog.outError("Battleground: bg not found."); - return; - } - - bgTypeId = bg->GetTypeID(); - - if(_player->InBattleGroundQueue()) - { - uint32 queueSlot = 0; - uint32 team = 0; - uint32 arenatype = 0; - uint32 israted = 0; - uint32 rating = 0; - // get the team info from the queue - BattleGroundQueue::QueuedPlayersMap::iterator pitr = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); - if(pitr !=sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end() - && pitr->second.GroupInfo ) - { - team = pitr->second.GroupInfo->Team; - arenatype = pitr->second.GroupInfo->ArenaType; - israted = pitr->second.GroupInfo->IsRated; - rating = pitr->second.GroupInfo->ArenaTeamRating; - } - else - { - sLog.outError("Battleground: Invalid player queue info!"); - return; - } - WorldPacket data; - switch(action) - { - case 1: // port to battleground - if(!_player->IsInvitedForBattleGroundQueueType(bgQueueTypeId)) - return; // cheating? - // resurrect the player - if(!_player->isAlive()) - { - _player->ResurrectPlayer(1.0f,false); - _player->SpawnCorpseBones(); - } - // stop taxi flight at port - if(_player->isInFlight()) - { - _player->GetMotionMaster()->MovementExpired(); - _player->m_taxi.ClearTaxiDestinations(); - } - _player->RemoveFromGroup(); - queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime()); - _player->GetSession()->SendPacket(&data); - // remove battleground queue status from BGmgr - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(_player->GetGUID(), false); - // this is still needed here if battleground "jumping" shouldn't add deserter debuff - // also this required to prevent stuck at old battleground after SetBattleGroundId set to new - if( BattleGround *currentBg = _player->GetBattleGround() ) - currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true); - - // set the destination instance id - _player->SetBattleGroundId(bg->GetInstanceID()); - // set the destination team - _player->SetBGTeam(team); - // bg->HandleBeforeTeleportToBattleGround(_player); - sBattleGroundMgr.SendToBattleGround(_player, instanceId); - // add only in HandleMoveWorldPortAck() - // bg->AddPlayer(_player,team); - sLog.outDebug("Battleground: player %s (%u) joined battle for bg %u, bgtype %u, queue type %u.",_player->GetName(),_player->GetGUIDLow(),bg->GetInstanceID(),bg->GetTypeID(),bgQueueTypeId); - break; - case 0: // leave queue - queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); - _player->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_NONE, 0, 0); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(_player->GetGUID(), true); - // player left queue, we should update it, maybe now his group fits in - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId,_player->GetBattleGroundQueueIdFromLevel(),arenatype,israted,rating); - SendPacket(&data); - sLog.outDebug("Battleground: player %s (%u) left queue for bgtype %u, queue type %u.",_player->GetName(),_player->GetGUIDLow(),bg->GetTypeID(),bgQueueTypeId); - break; - default: - sLog.outError("Battleground port: unknown action %u", action); - break; - } - } -} - -void WorldSession::HandleBattleGroundLeaveOpcode( WorldPacket & /*recv_data*/ ) -{ - //CHECK_PACKET_SIZE(recv_data, 1+1+4+2); - - sLog.outDebug( "WORLD: Recvd CMSG_LEAVE_BATTLEFIELD Message"); - - //uint8 unk1, unk2; - //uint32 bgTypeId; // id from DBC - //uint16 unk3; - - //recv_data >> unk1 >> unk2 >> bgTypeId >> unk3; - no used currently - - //if(bgTypeId >= MAX_BATTLEGROUND_TYPES) // cheating? but not important in this case - // return; - - // not allow leave battleground in combat - if(_player->isInCombat()) - if(BattleGround* bg = _player->GetBattleGround()) - if(bg->GetStatus() != STATUS_WAIT_LEAVE) - return; - - _player->LeaveBattleground(); -} - -void WorldSession::HandleBattlefieldStatusOpcode( WorldPacket & /*recv_data*/ ) -{ - // empty opcode - sLog.outDebug( "WORLD: Battleground status" ); - - WorldPacket data; - - // TODO: we must put player back to battleground in case disconnect (< 5 minutes offline time) or teleport player on login(!) from battleground map to entry point - if(_player->InBattleGround()) - { - BattleGround *bg = _player->GetBattleGround(); - if(bg) - { - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); - uint32 queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); - if((bg->GetStatus() <= STATUS_IN_PROGRESS)) - { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime()); - SendPacket(&data); - } - for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - { - uint32 queue_id = _player->GetBattleGroundQueueId(i); // battlegroundqueueid stores the type id, not the instance id, so this is definitely wrong - uint8 arenatype = sBattleGroundMgr.BGArenaType(queue_id); - uint8 isRated = 0; - if (i == queueSlot || !queue_id) // we need to get the instance ids - continue; - BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); - if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) - continue; - if(itrPlayerStatus->second.GroupInfo) - { - arenatype = itrPlayerStatus->second.GroupInfo->ArenaType; - isRated = itrPlayerStatus->second.GroupInfo->IsRated; - } - BattleGround *bg2 = sBattleGroundMgr.GetBattleGroundTemplate(sBattleGroundMgr.BGTemplateId(queue_id)); // try this - if(bg2) - { - //in this call is small bug, this call should be filled by player's waiting time in queue - //this call nulls all timers for client : - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg2, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0,arenatype,isRated); - SendPacket(&data); - } - } - } - } - else - { - // we should update all queues? .. i'm not sure if this code is correct - for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - { - uint32 queue_id = _player->GetBattleGroundQueueId(i); - if(!queue_id) - continue; - uint32 bgTypeId = sBattleGroundMgr.BGTemplateId(queue_id); - uint8 arenatype = sBattleGroundMgr.BGArenaType(queue_id); - uint8 isRated = 0; - BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); - if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) - continue; - if(itrPlayerStatus->second.GroupInfo) - { - arenatype = itrPlayerStatus->second.GroupInfo->ArenaType; - isRated = itrPlayerStatus->second.GroupInfo->IsRated; - } - if(bg && queue_id) - { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated); - SendPacket(&data); - } - } - } -/* else // not sure if it needed... - { - for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, NULL, _player->GetTeam(),i , STATUS_NONE, 0, 0); - SendPacket(&data); - } - }*/ -} - -void WorldSession::HandleAreaSpiritHealerQueryOpcode( WorldPacket & recv_data ) -{ - sLog.outDebug("WORLD: CMSG_AREA_SPIRIT_HEALER_QUERY"); - - CHECK_PACKET_SIZE(recv_data, 8); - - BattleGround *bg = _player->GetBattleGround(); - if(!bg) - return; - - uint64 guid; - recv_data >> guid; - - Creature *unit = ObjectAccessor::GetCreature(*_player, guid); - if(!unit) - return; - - if(!unit->isSpiritService()) // it's not spirit service - return; - - sBattleGroundMgr.SendAreaSpiritHealerQueryOpcode(_player, bg, guid); -} - -void WorldSession::HandleAreaSpiritHealerQueueOpcode( WorldPacket & recv_data ) -{ - sLog.outDebug("WORLD: CMSG_AREA_SPIRIT_HEALER_QUEUE"); - - CHECK_PACKET_SIZE(recv_data, 8); - - BattleGround *bg = _player->GetBattleGround(); - if(!bg) - return; - - uint64 guid; - recv_data >> guid; - - Creature *unit = ObjectAccessor::GetCreature(*_player, guid); - if(!unit) - return; - - if(!unit->isSpiritService()) // it's not spirit service - return; - - bg->AddPlayerToResurrectQueue(guid, _player->GetGUID()); -} - -void WorldSession::HandleBattleGroundArenaJoin( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 8+1+1+1); - - sLog.outDebug("WORLD: CMSG_BATTLEMASTER_JOIN_ARENA"); - recv_data.hexlike(); - - // ignore if we already in BG or BG queue - if(_player->InBattleGround()) - return; - - uint64 guid; // arena Battlemaster guid - uint8 type; // 2v2, 3v3 or 5v5 - uint8 asGroup; // asGroup - uint8 isRated; // isRated - Group * grp; - - recv_data >> guid >> type >> asGroup >> isRated; - - Creature *unit = ObjectAccessor::GetCreature(*_player, guid); - if(!unit) - return; - - if(!unit->isBattleMaster()) // it's not battle master - return; - - uint8 arenatype = 0; - uint32 arenaRating = 0; - - switch(type) - { - case 0: - arenatype = ARENA_TYPE_2v2; - break; - case 1: - arenatype = ARENA_TYPE_3v3; - break; - case 2: - arenatype = ARENA_TYPE_5v5; - break; - default: - sLog.outError("Unknown arena type %u at HandleBattleGroundArenaJoin()", type); - return; - } - - //check existance - BattleGround* bg = NULL; - if( !(bg = sBattleGroundMgr.GetBattleGroundTemplate(BATTLEGROUND_AA)) ) - { - sLog.outError("Battleground: template bg (all arenas) not found"); - return; - } - - uint8 bgTypeId = bg->GetTypeID(); - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, arenatype); - - // check queueing conditions - if(!asGroup) - { - // check if already in queue - if (_player->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) - //player is already in this queue - return; - // check if has free queue slots - if(!_player->HasFreeBattleGroundQueueId()) - return; - } - else - { - grp = _player->GetGroup(); - // no group found, error - if(!grp) - return; - uint32 err = grp->CanJoinBattleGroundQueue(bgTypeId, bgQueueTypeId, arenatype, arenatype, (bool)isRated, type); - switch(err) - { - // TODO: add error-based feedback to players in all cases - case BG_JOIN_ERR_GROUP_TOO_MANY: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_ARENA_GROUP_TOO_LARGE), NULL); - SendPacket(&data); - } - return; - break; - case BG_JOIN_ERR_GROUP_NOT_ENOUGH: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_ARENA_NOT_ENOUGH_PLAYERS), NULL); - SendPacket(&data); - } - return; - break; - case BG_JOIN_ERR_MIXED_ARENATEAM: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_ARENA_YOUR_TEAM_ONLY), NULL); - SendPacket(&data); - } - return; - break; - case BG_JOIN_ERR_OFFLINE_MEMBER: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_OFFLINE_MEMBER), NULL); - SendPacket(&data); - } - return; - break; - case BG_JOIN_ERR_MIXED_FACTION: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MIXED_FACTION), NULL); - SendPacket(&data); - } - return; - break; - case BG_JOIN_ERR_MIXED_LEVELS: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MIXED_LEVELS), NULL); - SendPacket(&data); - } - return; - break; - case BG_JOIN_ERR_GROUP_MEMBER_ALREADY_IN_QUEUE: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MEMBER_ALREADY_IN_QUEUE), NULL); - SendPacket(&data); - } - return; - break; - case BG_JOIN_ERR_GROUP_DESERTER: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MEMBER_DESERTER), NULL); - SendPacket(&data); - } - return; - break; - case BG_JOIN_ERR_ALL_QUEUES_USED: - { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MEMBER_NO_FREE_QUEUE_SLOTS), NULL); - SendPacket(&data); - } - return; - break; - // all ok, can join - case BG_JOIN_ERR_OK: - break; - // not the above? shouldn't happen, don't let join - default: - return; - break; - }; - } - - uint32 ateamId = 0; - - if(isRated) - { - ateamId = _player->GetArenaTeamId(type); - // check real arenateam existence only here (if it was moved to group->CanJoin .. () then we would ahve to get it twice) - ArenaTeam * at = objmgr.GetArenaTeamById(ateamId); - if(!at) - { - _player->GetSession()->SendNotInArenaTeamPacket(arenatype); - return; - } - // get the team rating for queueing - arenaRating = at->GetRating(); - // the arenateam id must match for everyone in the group - // get the personal ratings for queueing - uint32 avg_pers_rating = 0; - for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *member = itr->getSource(); - - // calc avg personal rating - avg_pers_rating += member->GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (type*6) + 5); - } - - if( arenatype ) - avg_pers_rating /= arenatype; - - // if avg personal rating is more than 150 points below the team’s rating, the team will be queued against an opponent matching or similar to the average personal rating - if(avg_pers_rating + 150 < arenaRating) - arenaRating = avg_pers_rating; - } - - if(asGroup) - { - GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, arenatype, isRated, arenaRating, ateamId); - sLog.outDebug("Battleground: arena join as group start"); - if(isRated) - sLog.outDebug("Battleground: arena team id %u, leader %s queued with rating %u for type %u",_player->GetArenaTeamId(type),_player->GetName(),arenaRating,arenatype); - for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *member = itr->getSource(); - if(!member) continue; - - uint32 queueSlot = member->AddBattleGroundQueueId(bgQueueTypeId);// add to queue - - // store entry point coords (same as leader entry point) - member->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); - - WorldPacket data; - // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, member->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated); - member->GetSession()->SendPacket(&data); - sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, bgTypeId); - member->GetSession()->SendPacket(&data); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(member, ginfo); - sLog.outDebug("Battleground: player joined queue for arena as group bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,member->GetGUIDLow(), member->GetName()); - } - sLog.outDebug("Battleground: arena join as group end"); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(), arenatype, isRated, arenaRating); - } - else - { - uint32 queueSlot = _player->AddBattleGroundQueueId(bgQueueTypeId); - - // store entry point coords - _player->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); - - WorldPacket data; - // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated); - SendPacket(&data); - GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, arenatype, isRated, arenaRating); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(_player, ginfo); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(), arenatype, isRated, arenaRating); - sLog.outDebug("Battleground: player joined queue for arena, skirmish, bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,_player->GetGUIDLow(), _player->GetName()); - } -} - -void WorldSession::HandleBattleGroundReportAFK( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 8); - - uint64 playerGuid; - recv_data >> playerGuid; - Player *reportedPlayer = objmgr.GetPlayer(playerGuid); - - if(!reportedPlayer) - { - sLog.outDebug("WorldSession::HandleBattleGroundReportAFK: player not found"); - return; - } - - sLog.outDebug("WorldSession::HandleBattleGroundReportAFK: %s reported %s", _player->GetName(), reportedPlayer->GetName()); - - reportedPlayer->ReportedAfkBy(_player); -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "WorldPacket.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "Player.h"
+#include "ObjectMgr.h"
+#include "WorldSession.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "Object.h"
+#include "Chat.h"
+#include "Language.h"
+#include "BattleGroundMgr.h"
+#include "BattleGroundWS.h"
+#include "BattleGround.h"
+#include "ArenaTeam.h"
+
+void WorldSession::HandleBattleGroundHelloOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ uint64 guid;
+ recv_data >> guid;
+ sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_HELLO Message from: " I64FMT, guid);
+
+ Creature *unit = ObjectAccessor::GetCreature(*_player, guid);
+ if(!unit)
+ return;
+
+ if(!unit->isBattleMaster()) // it's not battlemaster
+ return;
+
+ // Stop the npc if moving
+ unit->StopMoving();
+
+ uint32 bgTypeId = objmgr.GetBattleMasterBG(unit->GetEntry());
+
+ if(!_player->GetBGAccessByLevel(bgTypeId))
+ {
+ // temp, must be gossip message...
+ SendNotification(LANG_YOUR_BG_LEVEL_REQ_ERROR);
+ return;
+ }
+
+ SendBattlegGroundList(guid, bgTypeId);
+}
+
+void WorldSession::SendBattlegGroundList( uint64 guid, uint32 bgTypeId )
+{
+ WorldPacket data;
+ sBattleGroundMgr.BuildBattleGroundListPacket(&data, guid, _player, bgTypeId);
+ SendPacket( &data );
+}
+
+void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8+4+4+1);
+
+ uint64 guid;
+ uint32 bgTypeId;
+ uint32 instanceId;
+ uint8 joinAsGroup;
+ Group * grp;
+
+ recv_data >> guid; // battlemaster guid
+ recv_data >> bgTypeId; // battleground type id (DBC id)
+ recv_data >> instanceId; // instance id, 0 if First Available selected
+ recv_data >> joinAsGroup; // join as group
+
+ if(bgTypeId >= MAX_BATTLEGROUND_TYPES)
+ {
+ sLog.outError("Battleground: invalid bgtype received. possible cheater? player guid %u",_player->GetGUIDLow());
+ return;
+ }
+
+ sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from: " I64FMT, guid);
+
+ // can do this, since it's battleground, not arena
+ uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, 0);
+
+ // ignore if we already in BG or BG queue
+ if(_player->InBattleGround())
+ return;
+
+ Creature *unit = ObjectAccessor::GetCreature(*_player, guid);
+ if(!unit)
+ return;
+
+ if(!unit->isBattleMaster()) // it's not battlemaster
+ return;
+
+ // get bg instance or bg template if instance not found
+ BattleGround * bg = 0;
+ if(instanceId)
+ BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId);
+
+ if(!bg && !(bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId)))
+ {
+ sLog.outError("Battleground: no available bg / template found");
+ return;
+ }
+
+ // check queueing conditions
+ if(!joinAsGroup)
+ {
+ // check Deserter debuff
+ if( !_player->CanJoinToBattleground() )
+ {
+ WorldPacket data(SMSG_GROUP_JOINED_BATTLEGROUND, 4);
+ data << (uint32) 0xFFFFFFFE;
+ _player->GetSession()->SendPacket(&data);
+ return;
+ }
+ // check if already in queue
+ if (_player->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES)
+ //player is already in this queue
+ return;
+ // check if has free queue slots
+ if(!_player->HasFreeBattleGroundQueueId())
+ return;
+ }
+ else
+ {
+ grp = _player->GetGroup();
+ // no group found, error
+ if(!grp)
+ return;
+ uint32 err = grp->CanJoinBattleGroundQueue(bgTypeId, bgQueueTypeId, 0, bg->GetMaxPlayersPerTeam(), false, 0);
+ switch(err)
+ {
+ // TODO: add error-based feedback to players in all cases
+ case BG_JOIN_ERR_GROUP_TOO_MANY:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_TOO_LARGE), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ case BG_JOIN_ERR_OFFLINE_MEMBER:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_OFFLINE_MEMBER), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ case BG_JOIN_ERR_MIXED_FACTION:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MIXED_FACTION), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ case BG_JOIN_ERR_MIXED_LEVELS:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MIXED_LEVELS), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ case BG_JOIN_ERR_GROUP_MEMBER_ALREADY_IN_QUEUE:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MEMBER_ALREADY_IN_QUEUE), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ case BG_JOIN_ERR_GROUP_DESERTER:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MEMBER_DESERTER), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ case BG_JOIN_ERR_ALL_QUEUES_USED:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MEMBER_NO_FREE_QUEUE_SLOTS), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ // all ok, can join
+ case BG_JOIN_ERR_OK:
+ break;
+ // these aren't possible outcomes in bgs
+ case BG_JOIN_ERR_GROUP_NOT_ENOUGH:
+ case BG_JOIN_ERR_MIXED_ARENATEAM:
+ return;
+ break;
+ // not the above? shouldn't happen, don't let join
+ default:
+ return;
+ break;
+ };
+ }
+
+ // if we're here, then the conditions to join a bg are met. We can proceed in joining.
+
+ // _player->GetGroup() was already checked, grp is already initialized
+ if(joinAsGroup /* && _player->GetGroup()*/)
+ {
+ sLog.outDebug("Battleground: the following players are joining as group:");
+ GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, 0, false, 0);
+ for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *member = itr->getSource();
+ if(!member) continue; // this should never happen
+
+ uint32 queueSlot = member->AddBattleGroundQueueId(bgQueueTypeId); // add to queue
+
+ // store entry point coords (same as leader entry point)
+ member->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation());
+
+ WorldPacket data;
+ // send status packet (in queue)
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, member->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0);
+ member->GetSession()->SendPacket(&data);
+ sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, bgTypeId);
+ member->GetSession()->SendPacket(&data);
+ sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(member, ginfo);
+ sLog.outDebug("Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,member->GetGUIDLow(), member->GetName());
+ }
+ sLog.outDebug("Battleground: group end");
+ sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel());
+ }
+ else
+ {
+ // already checked if queueSlot is valid, now just get it
+ uint32 queueSlot = _player->AddBattleGroundQueueId(bgQueueTypeId);
+ // store entry point coords
+ _player->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation());
+
+ WorldPacket data;
+ // send status packet (in queue)
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0);
+ SendPacket(&data);
+
+ GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, 0, false, 0);
+ sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(_player, ginfo);
+ sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel());
+ sLog.outDebug("Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,_player->GetGUIDLow(), _player->GetName());
+ }
+}
+
+void WorldSession::HandleBattleGroundPlayerPositionsOpcode( WorldPacket & /*recv_data*/ )
+{
+ // empty opcode
+ sLog.outDebug("WORLD: Recvd MSG_BATTLEGROUND_PLAYER_POSITIONS Message");
+
+ BattleGround *bg = _player->GetBattleGround();
+ if(!bg) // can't be received if player not in battleground
+ return;
+
+ if(bg->GetTypeID() == BATTLEGROUND_WS)
+ {
+ uint32 count1 = 0;
+ uint32 count2 = 0;
+
+ Player *ap = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetAllianceFlagPickerGUID());
+ if(ap) ++count2;
+
+ Player *hp = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetHordeFlagPickerGUID());
+ if(hp) ++count2;
+
+ WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, (4+4+16*count1+16*count2));
+ data << count1; // alliance flag holders count
+ /*for(uint8 i = 0; i < count1; i++)
+ {
+ data << uint64(0); // guid
+ data << (float)0; // x
+ data << (float)0; // y
+ }*/
+ data << count2; // horde flag holders count
+ if(ap)
+ {
+ data << (uint64)ap->GetGUID();
+ data << (float)ap->GetPositionX();
+ data << (float)ap->GetPositionY();
+ }
+ if(hp)
+ {
+ data << (uint64)hp->GetGUID();
+ data << (float)hp->GetPositionX();
+ data << (float)hp->GetPositionY();
+ }
+
+ SendPacket(&data);
+ }
+}
+
+void WorldSession::HandleBattleGroundPVPlogdataOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug( "WORLD: Recvd MSG_PVP_LOG_DATA Message");
+
+ BattleGround *bg = _player->GetBattleGround();
+ if(!bg)
+ return;
+
+ WorldPacket data;
+ sBattleGroundMgr.BuildPvpLogDataPacket(&data, bg);
+ SendPacket(&data);
+
+ sLog.outDebug( "WORLD: Sent MSG_PVP_LOG_DATA Message");
+}
+
+void WorldSession::HandleBattleGroundListOpcode( WorldPacket &recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ sLog.outDebug( "WORLD: Recvd CMSG_BATTLEFIELD_LIST Message");
+
+ uint32 bgTypeId;
+ recv_data >> bgTypeId; // id from DBC
+
+ if(bgTypeId >= MAX_BATTLEGROUND_TYPES)
+ {
+ sLog.outError("Battleground: invalid bgtype received.");
+ return;
+ }
+
+ BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId);
+
+ if(!bl)
+ return;
+
+ WorldPacket data;
+ sBattleGroundMgr.BuildBattleGroundListPacket(&data, _player->GetGUID(), _player, bgTypeId);
+ SendPacket( &data );
+}
+
+void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 1+1+4+2+1);
+
+ sLog.outDebug( "WORLD: Recvd CMSG_BATTLEFIELD_PORT Message");
+
+ uint8 type; // arenatype if arena
+ uint8 unk2; // unk, can be 0x0 (may be if was invited?) and 0x1
+ uint32 instanceId;
+ uint32 bgTypeId; // type id from dbc
+ uint16 unk; // 0x1F90 constant?
+ uint8 action; // enter battle 0x1, leave queue 0x0
+
+ recv_data >> type >> unk2 >> bgTypeId >> unk >> action;
+
+ if(bgTypeId >= MAX_BATTLEGROUND_TYPES)
+ {
+ sLog.outError("Battleground: invalid bgtype received.");
+ // update battleground slots for the player to fix his UI and sent data.
+ // this is a HACK, I don't know why the client starts sending invalid packets in the first place.
+ // it usually happens with extremely high latency (if debugging / stepping in the code for example)
+ if(_player->InBattleGroundQueue())
+ {
+ // update all queues, send invitation info if player is invited, queue info if queued
+ for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ uint32 queue_id = _player->GetBattleGroundQueueId(i);
+ if(!queue_id)
+ continue;
+ BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID());
+ // if the player is not in queue, contine
+ if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end())
+ continue;
+
+ // no group information, this should never happen
+ if(!itrPlayerStatus->second.GroupInfo)
+ continue;
+
+ BattleGround * bg = NULL;
+
+ // get possibly needed data from groupinfo
+ bgTypeId = itrPlayerStatus->second.GroupInfo->BgTypeId;
+ uint8 arenatype = itrPlayerStatus->second.GroupInfo->ArenaType;
+ uint8 israted = itrPlayerStatus->second.GroupInfo->IsRated;
+ uint8 status = 0;
+
+
+ if(!itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID)
+ {
+ // not invited to bg, get template
+ bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId);
+ status = STATUS_WAIT_QUEUE;
+ }
+ else
+ {
+ // get the bg we're invited to
+ BattleGround * bg = sBattleGroundMgr.GetBattleGround(itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID);
+ status = STATUS_WAIT_JOIN;
+ }
+
+ // if bg not found, then continue
+ if(!bg)
+ continue;
+
+ // don't invite if already in the instance
+ if(_player->InBattleGround() && _player->GetBattleGround() && _player->GetBattleGround()->GetInstanceID() == bg->GetInstanceID())
+ continue;
+
+ // re - invite player with proper data
+ WorldPacket data;
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, itrPlayerStatus->second.GroupInfo->Team?itrPlayerStatus->second.GroupInfo->Team:_player->GetTeam(), i, status, INVITE_ACCEPT_WAIT_TIME, 0, arenatype, israted);
+ SendPacket(&data);
+ }
+ }
+ return;
+ }
+
+ uint32 bgQueueTypeId = 0;
+ // get the bg what we were invited to
+ BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus;
+ bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId,type);
+ itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID());
+
+ if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end())
+ {
+ sLog.outError("Battleground: itrplayerstatus not found.");
+ return;
+ }
+ instanceId = itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID;
+
+ // if action == 1, then instanceId is _required_
+ if(!instanceId && action == 1)
+ {
+ sLog.outError("Battleground: instance not found.");
+ return;
+ }
+
+ BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId);
+
+ // bg template might and must be used in case of leaving queue, when instance is not created yet
+ if(!bg && action == 0)
+ bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId);
+
+ if(!bg)
+ {
+ sLog.outError("Battleground: bg not found.");
+ return;
+ }
+
+ bgTypeId = bg->GetTypeID();
+
+ if(_player->InBattleGroundQueue())
+ {
+ uint32 queueSlot = 0;
+ uint32 team = 0;
+ uint32 arenatype = 0;
+ uint32 israted = 0;
+ uint32 rating = 0;
+ // get the team info from the queue
+ BattleGroundQueue::QueuedPlayersMap::iterator pitr = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID());
+ if(pitr !=sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()
+ && pitr->second.GroupInfo )
+ {
+ team = pitr->second.GroupInfo->Team;
+ arenatype = pitr->second.GroupInfo->ArenaType;
+ israted = pitr->second.GroupInfo->IsRated;
+ rating = pitr->second.GroupInfo->ArenaTeamRating;
+ }
+ else
+ {
+ sLog.outError("Battleground: Invalid player queue info!");
+ return;
+ }
+ WorldPacket data;
+ switch(action)
+ {
+ case 1: // port to battleground
+ if(!_player->IsInvitedForBattleGroundQueueType(bgQueueTypeId))
+ return; // cheating?
+ // resurrect the player
+ if(!_player->isAlive())
+ {
+ _player->ResurrectPlayer(1.0f,false);
+ _player->SpawnCorpseBones();
+ }
+ // stop taxi flight at port
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ _player->RemoveFromGroup();
+ queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId);
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime());
+ _player->GetSession()->SendPacket(&data);
+ // remove battleground queue status from BGmgr
+ sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(_player->GetGUID(), false);
+ // this is still needed here if battleground "jumping" shouldn't add deserter debuff
+ // also this required to prevent stuck at old battleground after SetBattleGroundId set to new
+ if( BattleGround *currentBg = _player->GetBattleGround() )
+ currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true);
+
+ // set the destination instance id
+ _player->SetBattleGroundId(bg->GetInstanceID());
+ // set the destination team
+ _player->SetBGTeam(team);
+ // bg->HandleBeforeTeleportToBattleGround(_player);
+ sBattleGroundMgr.SendToBattleGround(_player, instanceId);
+ // add only in HandleMoveWorldPortAck()
+ // bg->AddPlayer(_player,team);
+ sLog.outDebug("Battleground: player %s (%u) joined battle for bg %u, bgtype %u, queue type %u.",_player->GetName(),_player->GetGUIDLow(),bg->GetInstanceID(),bg->GetTypeID(),bgQueueTypeId);
+ break;
+ case 0: // leave queue
+ queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId);
+ _player->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_NONE, 0, 0);
+ sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(_player->GetGUID(), true);
+ // player left queue, we should update it, maybe now his group fits in
+ sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId,_player->GetBattleGroundQueueIdFromLevel(),arenatype,israted,rating);
+ SendPacket(&data);
+ sLog.outDebug("Battleground: player %s (%u) left queue for bgtype %u, queue type %u.",_player->GetName(),_player->GetGUIDLow(),bg->GetTypeID(),bgQueueTypeId);
+ break;
+ default:
+ sLog.outError("Battleground port: unknown action %u", action);
+ break;
+ }
+ }
+}
+
+void WorldSession::HandleBattleGroundLeaveOpcode( WorldPacket & /*recv_data*/ )
+{
+ //CHECK_PACKET_SIZE(recv_data, 1+1+4+2);
+
+ sLog.outDebug( "WORLD: Recvd CMSG_LEAVE_BATTLEFIELD Message");
+
+ //uint8 unk1, unk2;
+ //uint32 bgTypeId; // id from DBC
+ //uint16 unk3;
+
+ //recv_data >> unk1 >> unk2 >> bgTypeId >> unk3; - no used currently
+
+ //if(bgTypeId >= MAX_BATTLEGROUND_TYPES) // cheating? but not important in this case
+ // return;
+
+ // not allow leave battleground in combat
+ if(_player->isInCombat())
+ if(BattleGround* bg = _player->GetBattleGround())
+ if(bg->GetStatus() != STATUS_WAIT_LEAVE)
+ return;
+
+ _player->LeaveBattleground();
+}
+
+void WorldSession::HandleBattlefieldStatusOpcode( WorldPacket & /*recv_data*/ )
+{
+ // empty opcode
+ sLog.outDebug( "WORLD: Battleground status" );
+
+ WorldPacket data;
+
+ // TODO: we must put player back to battleground in case disconnect (< 5 minutes offline time) or teleport player on login(!) from battleground map to entry point
+ if(_player->InBattleGround())
+ {
+ BattleGround *bg = _player->GetBattleGround();
+ if(bg)
+ {
+ uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType());
+ uint32 queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId);
+ if((bg->GetStatus() <= STATUS_IN_PROGRESS))
+ {
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime());
+ SendPacket(&data);
+ }
+ for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ uint32 queue_id = _player->GetBattleGroundQueueId(i); // battlegroundqueueid stores the type id, not the instance id, so this is definitely wrong
+ uint8 arenatype = sBattleGroundMgr.BGArenaType(queue_id);
+ uint8 isRated = 0;
+ if (i == queueSlot || !queue_id) // we need to get the instance ids
+ continue;
+ BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID());
+ if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end())
+ continue;
+ if(itrPlayerStatus->second.GroupInfo)
+ {
+ arenatype = itrPlayerStatus->second.GroupInfo->ArenaType;
+ isRated = itrPlayerStatus->second.GroupInfo->IsRated;
+ }
+ BattleGround *bg2 = sBattleGroundMgr.GetBattleGroundTemplate(sBattleGroundMgr.BGTemplateId(queue_id)); // try this
+ if(bg2)
+ {
+ //in this call is small bug, this call should be filled by player's waiting time in queue
+ //this call nulls all timers for client :
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg2, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0,arenatype,isRated);
+ SendPacket(&data);
+ }
+ }
+ }
+ }
+ else
+ {
+ // we should update all queues? .. i'm not sure if this code is correct
+ for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ uint32 queue_id = _player->GetBattleGroundQueueId(i);
+ if(!queue_id)
+ continue;
+ uint32 bgTypeId = sBattleGroundMgr.BGTemplateId(queue_id);
+ uint8 arenatype = sBattleGroundMgr.BGArenaType(queue_id);
+ uint8 isRated = 0;
+ BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId);
+ BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID());
+ if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end())
+ continue;
+ if(itrPlayerStatus->second.GroupInfo)
+ {
+ arenatype = itrPlayerStatus->second.GroupInfo->ArenaType;
+ isRated = itrPlayerStatus->second.GroupInfo->IsRated;
+ }
+ if(bg && queue_id)
+ {
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated);
+ SendPacket(&data);
+ }
+ }
+ }
+/* else // not sure if it needed...
+ {
+ for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, NULL, _player->GetTeam(),i , STATUS_NONE, 0, 0);
+ SendPacket(&data);
+ }
+ }*/
+}
+
+void WorldSession::HandleAreaSpiritHealerQueryOpcode( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: CMSG_AREA_SPIRIT_HEALER_QUERY");
+
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ BattleGround *bg = _player->GetBattleGround();
+ if(!bg)
+ return;
+
+ uint64 guid;
+ recv_data >> guid;
+
+ Creature *unit = ObjectAccessor::GetCreature(*_player, guid);
+ if(!unit)
+ return;
+
+ if(!unit->isSpiritService()) // it's not spirit service
+ return;
+
+ sBattleGroundMgr.SendAreaSpiritHealerQueryOpcode(_player, bg, guid);
+}
+
+void WorldSession::HandleAreaSpiritHealerQueueOpcode( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: CMSG_AREA_SPIRIT_HEALER_QUEUE");
+
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ BattleGround *bg = _player->GetBattleGround();
+ if(!bg)
+ return;
+
+ uint64 guid;
+ recv_data >> guid;
+
+ Creature *unit = ObjectAccessor::GetCreature(*_player, guid);
+ if(!unit)
+ return;
+
+ if(!unit->isSpiritService()) // it's not spirit service
+ return;
+
+ bg->AddPlayerToResurrectQueue(guid, _player->GetGUID());
+}
+
+void WorldSession::HandleBattleGroundArenaJoin( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8+1+1+1);
+
+ sLog.outDebug("WORLD: CMSG_BATTLEMASTER_JOIN_ARENA");
+ recv_data.hexlike();
+
+ // ignore if we already in BG or BG queue
+ if(_player->InBattleGround())
+ return;
+
+ uint64 guid; // arena Battlemaster guid
+ uint8 type; // 2v2, 3v3 or 5v5
+ uint8 asGroup; // asGroup
+ uint8 isRated; // isRated
+ Group * grp;
+
+ recv_data >> guid >> type >> asGroup >> isRated;
+
+ Creature *unit = ObjectAccessor::GetCreature(*_player, guid);
+ if(!unit)
+ return;
+
+ if(!unit->isBattleMaster()) // it's not battle master
+ return;
+
+ uint8 arenatype = 0;
+ uint32 arenaRating = 0;
+
+ switch(type)
+ {
+ case 0:
+ arenatype = ARENA_TYPE_2v2;
+ break;
+ case 1:
+ arenatype = ARENA_TYPE_3v3;
+ break;
+ case 2:
+ arenatype = ARENA_TYPE_5v5;
+ break;
+ default:
+ sLog.outError("Unknown arena type %u at HandleBattleGroundArenaJoin()", type);
+ return;
+ }
+
+ //check existance
+ BattleGround* bg = NULL;
+ if( !(bg = sBattleGroundMgr.GetBattleGroundTemplate(BATTLEGROUND_AA)) )
+ {
+ sLog.outError("Battleground: template bg (all arenas) not found");
+ return;
+ }
+
+ uint8 bgTypeId = bg->GetTypeID();
+ uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, arenatype);
+
+ // check queueing conditions
+ if(!asGroup)
+ {
+ // check if already in queue
+ if (_player->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES)
+ //player is already in this queue
+ return;
+ // check if has free queue slots
+ if(!_player->HasFreeBattleGroundQueueId())
+ return;
+ }
+ else
+ {
+ grp = _player->GetGroup();
+ // no group found, error
+ if(!grp)
+ return;
+ uint32 err = grp->CanJoinBattleGroundQueue(bgTypeId, bgQueueTypeId, arenatype, arenatype, (bool)isRated, type);
+ switch(err)
+ {
+ // TODO: add error-based feedback to players in all cases
+ case BG_JOIN_ERR_GROUP_TOO_MANY:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_ARENA_GROUP_TOO_LARGE), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ case BG_JOIN_ERR_GROUP_NOT_ENOUGH:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_ARENA_NOT_ENOUGH_PLAYERS), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ case BG_JOIN_ERR_MIXED_ARENATEAM:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_ARENA_YOUR_TEAM_ONLY), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ case BG_JOIN_ERR_OFFLINE_MEMBER:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_OFFLINE_MEMBER), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ case BG_JOIN_ERR_MIXED_FACTION:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MIXED_FACTION), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ case BG_JOIN_ERR_MIXED_LEVELS:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MIXED_LEVELS), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ case BG_JOIN_ERR_GROUP_MEMBER_ALREADY_IN_QUEUE:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MEMBER_ALREADY_IN_QUEUE), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ case BG_JOIN_ERR_GROUP_DESERTER:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MEMBER_DESERTER), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ case BG_JOIN_ERR_ALL_QUEUES_USED:
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(LANG_BG_GROUP_MEMBER_NO_FREE_QUEUE_SLOTS), NULL);
+ SendPacket(&data);
+ }
+ return;
+ break;
+ // all ok, can join
+ case BG_JOIN_ERR_OK:
+ break;
+ // not the above? shouldn't happen, don't let join
+ default:
+ return;
+ break;
+ };
+ }
+
+ uint32 ateamId = 0;
+
+ if(isRated)
+ {
+ ateamId = _player->GetArenaTeamId(type);
+ // check real arenateam existence only here (if it was moved to group->CanJoin .. () then we would ahve to get it twice)
+ ArenaTeam * at = objmgr.GetArenaTeamById(ateamId);
+ if(!at)
+ {
+ _player->GetSession()->SendNotInArenaTeamPacket(arenatype);
+ return;
+ }
+ // get the team rating for queueing
+ arenaRating = at->GetRating();
+ // the arenateam id must match for everyone in the group
+ // get the personal ratings for queueing
+ uint32 avg_pers_rating = 0;
+ for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *member = itr->getSource();
+
+ // calc avg personal rating
+ avg_pers_rating += member->GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (type*6) + 5);
+ }
+
+ if( arenatype )
+ avg_pers_rating /= arenatype;
+
+ // if avg personal rating is more than 150 points below the team’s rating, the team will be queued against an opponent matching or similar to the average personal rating
+ if(avg_pers_rating + 150 < arenaRating)
+ arenaRating = avg_pers_rating;
+ }
+
+ if(asGroup)
+ {
+ GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, arenatype, isRated, arenaRating, ateamId);
+ sLog.outDebug("Battleground: arena join as group start");
+ if(isRated)
+ sLog.outDebug("Battleground: arena team id %u, leader %s queued with rating %u for type %u",_player->GetArenaTeamId(type),_player->GetName(),arenaRating,arenatype);
+ for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *member = itr->getSource();
+ if(!member) continue;
+
+ uint32 queueSlot = member->AddBattleGroundQueueId(bgQueueTypeId);// add to queue
+
+ // store entry point coords (same as leader entry point)
+ member->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation());
+
+ WorldPacket data;
+ // send status packet (in queue)
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, member->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated);
+ member->GetSession()->SendPacket(&data);
+ sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, bgTypeId);
+ member->GetSession()->SendPacket(&data);
+ sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(member, ginfo);
+ sLog.outDebug("Battleground: player joined queue for arena as group bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,member->GetGUIDLow(), member->GetName());
+ }
+ sLog.outDebug("Battleground: arena join as group end");
+ sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(), arenatype, isRated, arenaRating);
+ }
+ else
+ {
+ uint32 queueSlot = _player->AddBattleGroundQueueId(bgQueueTypeId);
+
+ // store entry point coords
+ _player->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation());
+
+ WorldPacket data;
+ // send status packet (in queue)
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated);
+ SendPacket(&data);
+ GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, arenatype, isRated, arenaRating);
+ sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(_player, ginfo);
+ sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(), arenatype, isRated, arenaRating);
+ sLog.outDebug("Battleground: player joined queue for arena, skirmish, bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,_player->GetGUIDLow(), _player->GetName());
+ }
+}
+
+void WorldSession::HandleBattleGroundReportAFK( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ uint64 playerGuid;
+ recv_data >> playerGuid;
+ Player *reportedPlayer = objmgr.GetPlayer(playerGuid);
+
+ if(!reportedPlayer)
+ {
+ sLog.outDebug("WorldSession::HandleBattleGroundReportAFK: player not found");
+ return;
+ }
+
+ sLog.outDebug("WorldSession::HandleBattleGroundReportAFK: %s reported %s", _player->GetName(), reportedPlayer->GetName());
+
+ reportedPlayer->ReportedAfkBy(_player);
+}
diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index 929a0543f0b..f6dc47e7b60 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -1,1065 +1,1066 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "Common.h" -#include "Database/DatabaseEnv.h" -#include "WorldPacket.h" -#include "SharedDefines.h" -#include "WorldSession.h" -#include "Opcodes.h" -#include "Log.h" -#include "World.h" -#include "ObjectMgr.h" -#include "Player.h" -#include "Guild.h" -#include "UpdateMask.h" -#include "Auth/md5.h" -#include "MapManager.h" -#include "ObjectAccessor.h" -#include "Group.h" -#include "Database/DatabaseImpl.h" -#include "PlayerDump.h" -#include "SocialMgr.h" -#include "Util.h" - -class LoginQueryHolder : public SqlQueryHolder -{ - private: - uint32 m_accountId; - uint64 m_guid; - public: - LoginQueryHolder(uint32 accountId, uint64 guid) - : m_accountId(accountId), m_guid(guid) { } - uint64 GetGuid() const { return m_guid; } - uint32 GetAccountId() const { return m_accountId; } - bool Initialize(); -}; - -bool LoginQueryHolder::Initialize() -{ - SetSize(MAX_PLAYER_LOGIN_QUERY); - - bool res = true; - - // NOTE: all fields in `characters` must be read to prevent lost character data at next save in case wrong DB structure. - // !!! NOTE: including unused `zone`,`online` - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM, "SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, gmstate, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT leaderGuid FROM group_member WHERE memberGuid ='%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLS, "SELECT spell,slot,active,disabled FROM character_spell WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS, "SELECT quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4 FROM character_queststatus WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS,"SELECT quest,time FROM character_queststatus_daily WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADTUTORIALS, "SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u' AND realmid = '%u'", GetAccountId(), realmID); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADREPUTATION, "SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADINVENTORY, "SELECT data,bag,slot,item,item_template FROM character_inventory JOIN item_instance ON character_inventory.item = item_instance.guid WHERE character_inventory.guid = '%u' ORDER BY bag,slot", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, "SELECT button,action,type,misc FROM character_action WHERE guid = '%u' ORDER BY button", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" I64FMTD "'", GUID_LOPART(m_guid),(uint64)time(NULL)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILDATE, "SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSOCIALLIST, "SELECT friend,flags,note FROM character_social WHERE guid = '%u' LIMIT 255", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADHOMEBIND, "SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS, "SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'", GUID_LOPART(m_guid)); - if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED)) - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = '%u'",GUID_LOPART(m_guid)); - // in other case still be dummy query - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGUILD, "SELECT guildid,rank FROM guild_member WHERE guid = '%u'", GUID_LOPART(m_guid)); - - return res; -} - -// don't call WorldSession directly -// it may get deleted before the query callbacks get executed -// instead pass an account id to this handler -class CharacterHandler -{ - public: - void HandleCharEnumCallback(QueryResult * result, uint32 account) - { - WorldSession * session = sWorld.FindSession(account); - if(!session) - { - delete result; - return; - } - session->HandleCharEnum(result); - } - void HandlePlayerLoginCallback(QueryResult * /*dummy*/, SqlQueryHolder * holder) - { - if (!holder) return; - WorldSession *session = sWorld.FindSession(((LoginQueryHolder*)holder)->GetAccountId()); - if(!session) - { - delete holder; - return; - } - session->HandlePlayerLogin((LoginQueryHolder*)holder); - } -} chrHandler; - -void WorldSession::HandleCharEnum(QueryResult * result) -{ - // keys can be non cleared if player open realm list and close it by 'cancel' - loginDatabase.PExecute("UPDATE account SET v = '0', s = '0' WHERE id = '%u'", GetAccountId()); - - WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size - - uint8 num = 0; - - data << num; - - if( result ) - { - Player *plr = new Player(this); - do - { - sLog.outDetail("Loading char guid %u from account %u.",(*result)[0].GetUInt32(),GetAccountId()); - - if(plr->MinimalLoadFromDB( result, (*result)[0].GetUInt32() )) - { - plr->BuildEnumData( result, &data ); - ++num; - } - } - while( result->NextRow() ); - - delete plr; - delete result; - } - - data.put<uint8>(0, num); - - SendPacket( &data ); -} - -void WorldSession::HandleCharEnumOpcode( WorldPacket & /*recv_data*/ ) -{ - /// get all the data necessary for loading all characters (along with their pets) on the account - CharacterDatabase.AsyncPQuery(&chrHandler, &CharacterHandler::HandleCharEnumCallback, GetAccountId(), - !sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) ? - // ------- Query Without Declined Names -------- - // 0 1 2 3 4 5 6 7 8 - "SELECT characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, characters.at_login, " - // 9 10 11 - "character_pet.entry, character_pet.modelid, character_pet.level " - "FROM characters LEFT JOIN character_pet ON characters.guid=character_pet.owner AND character_pet.slot='0' " - "WHERE characters.account = '%u' ORDER BY characters.guid" - : - // --------- Query With Declined Names --------- - // 0 1 2 3 4 5 6 7 8 - "SELECT characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, characters.at_login, " - // 9 10 11 12 - "character_pet.entry, character_pet.modelid, character_pet.level, genitive " - "FROM characters LEFT JOIN character_pet ON characters.guid = character_pet.owner AND character_pet.slot='0' " - "LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid " - "WHERE characters.account = '%u' ORDER BY characters.guid", - GetAccountId()); -} - -void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,1+1+1+1+1+1+1+1+1+1); - - std::string name; - uint8 race_,class_; - bool pTbc = this->IsTBC() && sWorld.getConfig(CONFIG_EXPANSION) > 0; - recv_data >> name; - - // recheck with known string size - CHECK_PACKET_SIZE(recv_data,(name.size()+1)+1+1+1+1+1+1+1+1+1); - - recv_data >> race_; - recv_data >> class_; - - WorldPacket data(SMSG_CHAR_CREATE, 1); // returned with diff.values in all cases - - if(GetSecurity() == SEC_PLAYER) - { - if(uint32 mask = sWorld.getConfig(CONFIG_CHARACTERS_CREATING_DISABLED)) - { - bool disabled = false; - - uint32 team = Player::TeamForRace(race_); - switch(team) - { - case ALLIANCE: disabled = mask & (1<<0); break; - case HORDE: disabled = mask & (1<<1); break; - } - - if(disabled) - { - data << (uint8)CHAR_CREATE_DISABLED; - SendPacket( &data ); - return; - } - } - } - - if (!sChrClassesStore.LookupEntry(class_)|| - !sChrRacesStore.LookupEntry(race_)) - { - data << (uint8)CHAR_CREATE_FAILED; - SendPacket( &data ); - sLog.outError("Class: %u or Race %u not found in DBC (Wrong DBC files?) or Cheater?", class_, race_); - return; - } - - // prevent character creating Expansion race without Expansion account - if (!pTbc&&(race_>RACE_TROLL)) - { - data << (uint8)CHAR_CREATE_EXPANSION; - sLog.outError("No Expansion Account:[%d] but tried to Create TBC character",GetAccountId()); - SendPacket( &data ); - return; - } - - // prevent character creating with invalid name - if(!normalizePlayerName(name)) - { - data << (uint8)CHAR_NAME_INVALID_CHARACTER; - SendPacket( &data ); - sLog.outError("Account:[%d] but tried to Create character with empty [name] ",GetAccountId()); - return; - } - - // check name limitations - if(!ObjectMgr::IsValidName(name,true)) - { - data << (uint8)CHAR_NAME_INVALID_CHARACTER; - SendPacket( &data ); - return; - } - - if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(name)) - { - data << (uint8)CHAR_NAME_RESERVED; - SendPacket( &data ); - return; - } - - if(objmgr.GetPlayerGUIDByName(name)) - { - data << (uint8)CHAR_CREATE_NAME_IN_USE; - SendPacket( &data ); - return; - } - - QueryResult *resultacct = loginDatabase.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%d'", GetAccountId()); - if ( resultacct ) - { - Field *fields=resultacct->Fetch(); - uint32 acctcharcount = fields[0].GetUInt32(); - delete resultacct; - - if (acctcharcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_ACCOUNT)) - { - data << (uint8)CHAR_CREATE_ACCOUNT_LIMIT; - SendPacket( &data ); - return; - } - } - - QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", GetAccountId()); - uint8 charcount = 0; - if ( result ) - { - Field *fields=result->Fetch(); - charcount = fields[0].GetUInt8(); - delete result; - - if (charcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_REALM)) - { - data << (uint8)CHAR_CREATE_SERVER_LIMIT; - SendPacket( &data ); - return; - } - } - - bool AllowTwoSideAccounts = !sWorld.IsPvPRealm() || sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || GetSecurity() > SEC_PLAYER; - uint32 skipCinematics = sWorld.getConfig(CONFIG_SKIP_CINEMATICS); - - bool have_same_race = false; - if(!AllowTwoSideAccounts || skipCinematics == 1) - { - QueryResult *result2 = CharacterDatabase.PQuery("SELECT DISTINCT race FROM characters WHERE account = '%u' %s", GetAccountId(),skipCinematics == 1 ? "" : "LIMIT 1"); - if(result2) - { - uint32 team_= Player::TeamForRace(race_); - - Field* field = result2->Fetch(); - uint8 race = field[0].GetUInt32(); - - // need to check team only for first character - // TODO: what to if account already has characters of both races? - if (!AllowTwoSideAccounts) - { - uint32 team=0; - if(race > 0) - team = Player::TeamForRace(race); - - if(team != team_) - { - data << (uint8)CHAR_CREATE_PVP_TEAMS_VIOLATION; - SendPacket( &data ); - delete result2; - return; - } - } - - if (skipCinematics == 1) - { - // TODO: check if cinematic already shown? (already logged in?; cinematic field) - while (race_ != race && result2->NextRow()) - { - field = result2->Fetch(); - race = field[0].GetUInt32(); - } - have_same_race = race_ == race; - } - delete result2; - } - } - - // 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( objmgr.GenerateLowGuid(HIGHGUID_PLAYER), name, race_, class_, gender, skin, face, hairStyle, hairColor, facialHair, outfitId )) - { - // Player not create (race/class problem?) - delete pNewChar; - - data << (uint8)CHAR_CREATE_ERROR; - SendPacket( &data ); - - return; - } - - if(have_same_race && skipCinematics == 1 || skipCinematics == 2) - pNewChar->setCinematic(1); // not show intro - - // Player created, save it now - pNewChar->SaveToDB(); - charcount+=1; - - loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", GetAccountId(), realmID); - loginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charcount, GetAccountId(), realmID); - - delete pNewChar; // created only to call SaveToDB() - - data << (uint8)CHAR_CREATE_SUCCESS; - SendPacket( &data ); - - std::string IP_str = GetRemoteAddress().c_str(); - sLog.outBasic("Account: %d (IP: %s) Create Character:[%s]",GetAccountId(),IP_str.c_str(),name.c_str()); - sLog.outChar("Account: %d (IP: %s) Create Character:[%s]",GetAccountId(),IP_str.c_str(),name.c_str()); -} - -void WorldSession::HandleCharDeleteOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,8); - - uint64 guid; - recv_data >> guid; - - // can't delete loaded character - if(objmgr.GetPlayer(guid)) - return; - - uint32 accountId = 0; - std::string name; - - // is guild leader - if(objmgr.GetGuildByLeader(guid)) - { - WorldPacket data(SMSG_CHAR_DELETE, 1); - data << (uint8)CHAR_DELETE_FAILED_GUILD_LEADER; - SendPacket( &data ); - return; - } - - // is arena team captain - if(objmgr.GetArenaTeamByCapitan(guid)) - { - WorldPacket data(SMSG_CHAR_DELETE, 1); - data << (uint8)CHAR_DELETE_FAILED_ARENA_CAPTAIN; - SendPacket( &data ); - return; - } - - QueryResult *result = CharacterDatabase.PQuery("SELECT account,name FROM characters WHERE guid='%u'", GUID_LOPART(guid)); - if(result) - { - Field *fields = result->Fetch(); - accountId = fields[0].GetUInt32(); - name = fields[1].GetCppString(); - delete result; - } - - // prevent deleting other players' characters using cheating tools - if(accountId != GetAccountId()) - return; - - std::string IP_str = GetRemoteAddress(); - sLog.outBasic("Account: %d (IP: %s) Delete Character:[%s] (guid:%u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid)); - sLog.outChar("Account: %d (IP: %s) Delete Character:[%s] (guid: %u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid)); - - if(sLog.IsOutCharDump()) // optimize GetPlayerDump call - { - std::string dump = PlayerDumpWriter().GetDump(GUID_LOPART(guid)); - sLog.outCharDump(dump.c_str(),GetAccountId(),GUID_LOPART(guid),name.c_str()); - } - - Player::DeleteFromDB(guid, GetAccountId()); - - WorldPacket data(SMSG_CHAR_DELETE, 1); - data << (uint8)CHAR_DELETE_SUCCESS; - SendPacket( &data ); -} - -void WorldSession::HandlePlayerLoginOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,8); - - m_playerLoading = true; - uint64 playerGuid = 0; - - DEBUG_LOG( "WORLD: Recvd Player Logon Message" ); - - recv_data >> playerGuid; - - LoginQueryHolder *holder = new LoginQueryHolder(GetAccountId(), playerGuid); - if(!holder->Initialize()) - { - delete holder; // delete all unprocessed queries - m_playerLoading = false; - return; - } - - CharacterDatabase.DelayQueryHolder(&chrHandler, &CharacterHandler::HandlePlayerLoginCallback, holder); -} - -void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) -{ - uint64 playerGuid = holder->GetGuid(); - - Player* pCurrChar = new Player(this); - pCurrChar->GetMotionMaster()->Initialize(); - - // "GetAccountId()==db stored account id" checked in LoadFromDB (prevent login not own character using cheating tools) - if(!pCurrChar->LoadFromDB(GUID_LOPART(playerGuid), holder)) - { - KickPlayer(); // disconnect client, player no set to session and it will not deleted or saved at kick - delete pCurrChar; // delete it manually - delete holder; // delete all unprocessed queries - m_playerLoading = false; - return; - } - - SetPlayer(pCurrChar); - - pCurrChar->SendDungeonDifficulty(false); - - WorldPacket data( SMSG_LOGIN_VERIFY_WORLD, 20 ); - data << pCurrChar->GetMapId(); - data << pCurrChar->GetPositionX(); - data << pCurrChar->GetPositionY(); - data << pCurrChar->GetPositionZ(); - data << pCurrChar->GetOrientation(); - SendPacket(&data); - - data.Initialize( SMSG_ACCOUNT_DATA_TIMES, 128 ); - for(int i = 0; i < 32; i++) - data << uint32(0); - SendPacket(&data); - - data.Initialize(SMSG_FEATURE_SYSTEM_STATUS, 2); // added in 2.2.0 - data << uint8(2); // unknown value - data << uint8(0); // enable(1)/disable(0) voice chat interface in client - SendPacket(&data); - - // Send MOTD - { - data.Initialize(SMSG_MOTD, 50); // new in 2.0.1 - data << (uint32)0; - - uint32 linecount=0; - std::string str_motd = sWorld.GetMotd(); - std::string::size_type pos, nextpos; - - pos = 0; - while ( (nextpos= str_motd.find('@',pos)) != std::string::npos ) - { - if (nextpos != pos) - { - data << str_motd.substr(pos,nextpos-pos); - ++linecount; - } - pos = nextpos+1; - } - - if (pos<str_motd.length()) - { - data << str_motd.substr(pos); - ++linecount; - } - - data.put(0, linecount); - - SendPacket( &data ); - DEBUG_LOG( "WORLD: Sent motd (SMSG_MOTD)" ); - } - - if(pCurrChar->GetGuildId() != 0) - { - Guild* guild = objmgr.GetGuildById(pCurrChar->GetGuildId()); - if(guild) - { - data.Initialize(SMSG_GUILD_EVENT, (2+guild->GetMOTD().size()+1)); - data << (uint8)GE_MOTD; - data << (uint8)1; - data << guild->GetMOTD(); - SendPacket(&data); - DEBUG_LOG( "WORLD: Sent guild-motd (SMSG_GUILD_EVENT)" ); - - data.Initialize(SMSG_GUILD_EVENT, (5+10)); // we guess size - data<<(uint8)GE_SIGNED_ON; - data<<(uint8)1; - data<<pCurrChar->GetName(); - data<<pCurrChar->GetGUID(); - guild->BroadcastPacket(&data); - DEBUG_LOG( "WORLD: Sent guild-signed-on (SMSG_GUILD_EVENT)" ); - - // Increment online members of the guild - guild->IncOnlineMemberCount(); - } - else - { - // remove wrong guild data - sLog.outError("Player %s (GUID: %u) marked as member not existed guild (id: %u), removing guild membership for player.",pCurrChar->GetName(),pCurrChar->GetGUIDLow(),pCurrChar->GetGuildId()); - pCurrChar->SetUInt32Value(PLAYER_GUILDID,0); - pCurrChar->SetUInt32ValueInDB(PLAYER_GUILDID,0,pCurrChar->GetGUID()); - } - } - - if(!pCurrChar->isAlive()) - pCurrChar->SendCorpseReclaimDelay(true); - - pCurrChar->SendInitialPacketsBeforeAddToMap(); - - //Show cinematic at the first time that player login - if( !pCurrChar->getCinematic() ) - { - pCurrChar->setCinematic(1); - - ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace()); - if(rEntry) - { - data.Initialize( SMSG_TRIGGER_CINEMATIC,4 ); - data << uint32(rEntry->startmovie); - SendPacket( &data ); - } - } - - //QueryResult *result = CharacterDatabase.PQuery("SELECT guildid,rank FROM guild_member WHERE guid = '%u'",pCurrChar->GetGUIDLow()); - QueryResult *resultGuild = holder->GetResult(PLAYER_LOGIN_QUERY_LOADGUILD); - - if(resultGuild) - { - Field *fields = resultGuild->Fetch(); - pCurrChar->SetInGuild(fields[0].GetUInt32()); - pCurrChar->SetRank(fields[1].GetUInt32()); - delete resultGuild; - } - else if(pCurrChar->GetGuildId()) // clear guild related fields in case wrong data about non existed membership - { - pCurrChar->SetInGuild(0); - pCurrChar->SetRank(0); - } - - if (!MapManager::Instance().GetMap(pCurrChar->GetMapId(), pCurrChar)->Add(pCurrChar)) - { - AreaTrigger const* at = objmgr.GetGoBackTrigger(pCurrChar->GetMapId()); - if(at) - pCurrChar->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, pCurrChar->GetOrientation()); - else - pCurrChar->TeleportTo(pCurrChar->m_homebindMapId, pCurrChar->m_homebindX, pCurrChar->m_homebindY, pCurrChar->m_homebindZ, pCurrChar->GetOrientation()); - } - - ObjectAccessor::Instance().AddObject(pCurrChar); - //sLog.outDebug("Player %s added to Map.",pCurrChar->GetName()); - pCurrChar->GetSocial()->SendSocialList(); - - pCurrChar->SendInitialPacketsAfterAddToMap(); - - CharacterDatabase.PExecute("UPDATE characters SET online = 1 WHERE guid = '%u'", pCurrChar->GetGUIDLow()); - loginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = '%u'", GetAccountId()); - pCurrChar->SetInGameTime( getMSTime() ); - - // announce group about member online (must be after add to player list to receive announce to self) - if(Group *group = pCurrChar->GetGroup()) - { - //pCurrChar->groupInfo.group->SendInit(this); // useless - group->SendUpdate(); - } - - // friend status - sSocialMgr.SendFriendStatus(pCurrChar, FRIEND_ONLINE, pCurrChar->GetGUIDLow(), "", true); - - // Place character in world (and load zone) before some object loading - pCurrChar->LoadCorpse(); - - // setting Ghost+speed if dead - //if ( pCurrChar->m_deathState == DEAD ) - if (pCurrChar->m_deathState != ALIVE) - { - // not blizz like, we must correctly save and load player instead... - if(pCurrChar->getRace() == RACE_NIGHTELF) - pCurrChar->CastSpell(pCurrChar, 20584, true, 0);// auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form) - pCurrChar->CastSpell(pCurrChar, 8326, true, 0); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?) - - //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+41, 8326); - //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+42, 20584); - //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAFLAGS+6, 238); - //pCurrChar->SetUInt32Value(UNIT_FIELD_AURALEVELS+11, 514); - //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS+11, 65535); - //pCurrChar->SetUInt32Value(UNIT_FIELD_DISPLAYID, 1825); - //if (pCurrChar->getRace() == RACE_NIGHTELF) - //{ - // pCurrChar->SetSpeed(MOVE_RUN, 1.5f*1.2f, true); - // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f*1.2f, true); - //} - //else - //{ - // pCurrChar->SetSpeed(MOVE_RUN, 1.5f, true); - // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f, true); - //} - pCurrChar->SetMovement(MOVE_WATER_WALK); - } - - if(uint32 sourceNode = pCurrChar->m_taxi.GetTaxiSource()) - { - - sLog.outDebug( "WORLD: Restart character %u taxi flight", pCurrChar->GetGUIDLow() ); - - uint32 MountId = objmgr.GetTaxiMount(sourceNode, pCurrChar->GetTeam()); - uint32 path = pCurrChar->m_taxi.GetCurrentTaxiPath(); - - // search appropriate start path node - uint32 startNode = 0; - - TaxiPathNodeList const& nodeList = sTaxiPathNodesByPath[path]; - - float distPrev = MAP_SIZE*MAP_SIZE; - float distNext = - (nodeList[0].x-pCurrChar->GetPositionX())*(nodeList[0].x-pCurrChar->GetPositionX())+ - (nodeList[0].y-pCurrChar->GetPositionY())*(nodeList[0].y-pCurrChar->GetPositionY())+ - (nodeList[0].z-pCurrChar->GetPositionZ())*(nodeList[0].z-pCurrChar->GetPositionZ()); - - for(uint32 i = 1; i < nodeList.size(); ++i) - { - TaxiPathNode const& node = nodeList[i]; - TaxiPathNode const& prevNode = nodeList[i-1]; - - // skip nodes at another map - if(node.mapid != pCurrChar->GetMapId()) - continue; - - distPrev = distNext; - - distNext = - (node.x-pCurrChar->GetPositionX())*(node.x-pCurrChar->GetPositionX())+ - (node.y-pCurrChar->GetPositionY())*(node.y-pCurrChar->GetPositionY())+ - (node.z-pCurrChar->GetPositionZ())*(node.z-pCurrChar->GetPositionZ()); - - float distNodes = - (node.x-prevNode.x)*(node.x-prevNode.x)+ - (node.y-prevNode.y)*(node.y-prevNode.y)+ - (node.z-prevNode.z)*(node.z-prevNode.z); - - if(distNext + distPrev < distNodes) - { - startNode = i; - break; - } - } - - SendDoFlight( MountId, path, startNode ); - } - - // Load pet if any and player is alive and not in taxi flight - if(pCurrChar->isAlive() && pCurrChar->m_taxi.GetTaxiSource()==0) - pCurrChar->LoadPet(); - - // Set FFA PvP for non GM in non-rest mode - if(sWorld.IsFFAPvPRealm() && !pCurrChar->isGameMaster() && !pCurrChar->HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_RESTING) ) - pCurrChar->SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); - - if(pCurrChar->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP)) - pCurrChar->SetContestedPvP(); - - // Apply at_login requests - if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_SPELLS)) - { - pCurrChar->resetSpells(); - SendNotification("Spells has been reset."); - } - - if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_TALENTS)) - { - pCurrChar->resetTalents(true); - SendNotification("Talents has been reset."); - } - - // show time before shutdown if shutdown planned. - if(sWorld.IsShutdowning()) - sWorld.ShutdownMsg(true,pCurrChar); - - if(pCurrChar->isGameMaster()) - SendNotification("GM mode is ON"); - - std::string IP_str = GetRemoteAddress(); - sLog.outChar("Account: %d (IP: %s) Login Character:[%s] (guid:%u)",GetAccountId(),IP_str.c_str(),pCurrChar->GetName() ,pCurrChar->GetGUID()); - - m_playerLoading = false; - delete holder; -} - -void WorldSession::HandleSetFactionAtWar( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,4+1); - - DEBUG_LOG( "WORLD: Received CMSG_SET_FACTION_ATWAR" ); - - uint32 repListID; - uint8 flag; - - recv_data >> repListID; - recv_data >> flag; - - FactionStateList::iterator itr = GetPlayer()->m_factions.find(repListID); - if (itr == GetPlayer()->m_factions.end()) - return; - - // always invisible or hidden faction can't change war state - if(itr->second.Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN) ) - return; - - GetPlayer()->SetFactionAtWar(&itr->second,flag); -} - -//I think this function is never used :/ I dunno, but i guess this opcode not exists -void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ ) -{ - //CHECK_PACKET_SIZE(recv_data,4+4); - - //sLog.outDebug("WORLD SESSION: HandleSetFactionCheat"); - /* - uint32 FactionID; - uint32 Standing; - - recv_data >> FactionID; - recv_data >> Standing; - - std::list<struct Factions>::iterator itr; - - for(itr = GetPlayer()->factions.begin(); itr != GetPlayer()->factions.end(); ++itr) - { - if(itr->ReputationListID == FactionID) - { - itr->Standing += Standing; - itr->Flags = (itr->Flags | 1); - break; - } - } - */ - GetPlayer()->UpdateReputation(); -} - -void WorldSession::HandleMeetingStoneInfo( WorldPacket & /*recv_data*/ ) -{ - DEBUG_LOG( "WORLD: Received CMSG_MEETING_STONE_INFO" ); - - WorldPacket data(SMSG_MEETINGSTONE_SETQUEUE, 5); - data << uint32(0) << uint8(6); - SendPacket(&data); -} - -void WorldSession::HandleTutorialFlag( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,4); - - uint32 iFlag; - recv_data >> iFlag; - - uint32 wInt = (iFlag / 32); - if (wInt >= 8) - { - //sLog.outError("CHEATER? Account:[%d] Guid[%u] tried to send wrong CMSG_TUTORIAL_FLAG", GetAccountId(),GetGUID()); - return; - } - uint32 rInt = (iFlag % 32); - - uint32 tutflag = GetPlayer()->GetTutorialInt( wInt ); - tutflag |= (1 << rInt); - GetPlayer()->SetTutorialInt( wInt, tutflag ); - - //sLog.outDebug("Received Tutorial Flag Set {%u}.", iFlag); -} - -void WorldSession::HandleTutorialClear( WorldPacket & /*recv_data*/ ) -{ - for ( uint32 iI = 0; iI < 8; iI++) - GetPlayer()->SetTutorialInt( iI, 0xFFFFFFFF ); -} - -void WorldSession::HandleTutorialReset( WorldPacket & /*recv_data*/ ) -{ - for ( uint32 iI = 0; iI < 8; iI++) - GetPlayer()->SetTutorialInt( iI, 0x00000000 ); -} - -void WorldSession::HandleSetWatchedFactionIndexOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data,4); - - DEBUG_LOG("WORLD: Received CMSG_SET_WATCHED_FACTION"); - uint32 fact; - recv_data >> fact; - GetPlayer()->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fact); -} - -void WorldSession::HandleSetWatchedFactionInactiveOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data,4+1); - - DEBUG_LOG("WORLD: Received CMSG_SET_FACTION_INACTIVE"); - uint32 replistid; - uint8 inactive; - recv_data >> replistid >> inactive; - - FactionStateList::iterator itr = _player->m_factions.find(replistid); - if (itr == _player->m_factions.end()) - return; - - _player->SetFactionInactive(&itr->second, inactive); -} - -void WorldSession::HandleToggleHelmOpcode( WorldPacket & /*recv_data*/ ) -{ - DEBUG_LOG("CMSG_TOGGLE_HELM for %s", _player->GetName()); - _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM); -} - -void WorldSession::HandleToggleCloakOpcode( WorldPacket & /*recv_data*/ ) -{ - DEBUG_LOG("CMSG_TOGGLE_CLOAK for %s", _player->GetName()); - _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK); -} - -void WorldSession::HandleChangePlayerNameOpcode(WorldPacket& recv_data) -{ - CHECK_PACKET_SIZE(recv_data,8+1); - - uint64 guid; - std::string newname; - std::string oldname; - - CHECK_PACKET_SIZE(recv_data, 8+1); - - recv_data >> guid; - recv_data >> newname; - - QueryResult *result = CharacterDatabase.PQuery("SELECT at_login FROM characters WHERE guid ='%u'", GUID_LOPART(guid)); - if (result) - { - uint32 at_loginFlags; - Field *fields = result->Fetch(); - at_loginFlags = fields[0].GetUInt32(); - delete result; - - if (!(at_loginFlags & AT_LOGIN_RENAME)) - { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << (uint8)CHAR_CREATE_ERROR; - SendPacket( &data ); - return; - } - } - else - { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << (uint8)CHAR_CREATE_ERROR; - SendPacket( &data ); - return; - } - - if(!objmgr.GetPlayerNameByGUID(guid, oldname)) // character not exist, because we have no name for this guid - { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << (uint8)CHAR_LOGIN_NO_CHARACTER; - SendPacket( &data ); - return; - } - - // prevent character rename to invalid name - if(!normalizePlayerName(newname)) - { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << (uint8)CHAR_NAME_NO_NAME; - SendPacket( &data ); - return; - } - - if(!ObjectMgr::IsValidName(newname,true)) - { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << (uint8)CHAR_NAME_INVALID_CHARACTER; - SendPacket( &data ); - return; - } - - // check name limitations - if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) - { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << (uint8)CHAR_NAME_RESERVED; - SendPacket( &data ); - return; - } - - if(objmgr.GetPlayerGUIDByName(newname)) // character with this name already exist - { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << (uint8)CHAR_CREATE_ERROR; - SendPacket( &data ); - return; - } - - if(newname == oldname) // checked by client - { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << (uint8)CHAR_NAME_FAILURE; - SendPacket( &data ); - return; - } - - // we have to check character at_login_flag & AT_LOGIN_RENAME also (fake packets hehe) - - CharacterDatabase.escape_string(newname); - CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_RENAME),GUID_LOPART(guid)); - CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", GUID_LOPART(guid)); - - std::string IP_str = GetRemoteAddress(); - sLog.outChar("Account: %d (IP: %s) Character:[%s] (guid:%u) Changed name to: %s",GetAccountId(),IP_str.c_str(),oldname.c_str(),GUID_LOPART(guid),newname.c_str()); - - WorldPacket data(SMSG_CHAR_RENAME,1+8+(newname.size()+1)); - data << (uint8)RESPONSE_SUCCESS; - data << guid; - data << newname; - SendPacket(&data); -} - -void WorldSession::HandleDeclinedPlayerNameOpcode(WorldPacket& recv_data) -{ - uint64 guid; - - CHECK_PACKET_SIZE(recv_data, 8+6); - recv_data >> guid; - - // not accept declined names for unsupported languages - std::string name; - if(!objmgr.GetPlayerNameByGUID(guid,name)) - { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8); - data << (uint32)1; - data << guid; - SendPacket(&data); - return; - } - - std::wstring wname; - if(!Utf8toWStr(name,wname)) - { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8); - data << (uint32)1; - data << guid; - SendPacket(&data); - return; - } - - if(!isCyrillicCharacter(wname[0])) // name already stored as only single alphabet using - { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8); - data << (uint32)1; - data << guid; - SendPacket(&data); - return; - } - - std::string name2; - DeclinedName declinedname; - - recv_data >> name2; - - if(name2!=name) // character have different name - { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8); - data << (uint32)1; - data << guid; - SendPacket(&data); - return; - } - - for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) - { - recv_data >> declinedname.name[i]; - if(!normalizePlayerName(declinedname.name[i])) - { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8); - data << (uint32)1; - data << guid; - SendPacket(&data); - return; - } - } - - if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname,0),declinedname)) - { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8); - data << (uint32)1; - data << guid; - SendPacket(&data); - return; - } - - for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) - CharacterDatabase.escape_string(declinedname.name[i]); - - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'", GUID_LOPART(guid)); - CharacterDatabase.PExecute("INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%s','%s','%s','%s','%s')", - GUID_LOPART(guid), declinedname.name[0].c_str(),declinedname.name[1].c_str(),declinedname.name[2].c_str(),declinedname.name[3].c_str(),declinedname.name[4].c_str()); - CharacterDatabase.CommitTransaction(); - - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8); - data << (uint32)0; // OK - data << guid; - SendPacket(&data); -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "SharedDefines.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "Guild.h"
+#include "UpdateMask.h"
+#include "Auth/md5.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "Group.h"
+#include "Database/DatabaseImpl.h"
+#include "PlayerDump.h"
+#include "SocialMgr.h"
+#include "Util.h"
+#include "Language.h"
+
+class LoginQueryHolder : public SqlQueryHolder
+{
+ private:
+ uint32 m_accountId;
+ uint64 m_guid;
+ public:
+ LoginQueryHolder(uint32 accountId, uint64 guid)
+ : m_accountId(accountId), m_guid(guid) { }
+ uint64 GetGuid() const { return m_guid; }
+ uint32 GetAccountId() const { return m_accountId; }
+ bool Initialize();
+};
+
+bool LoginQueryHolder::Initialize()
+{
+ SetSize(MAX_PLAYER_LOGIN_QUERY);
+
+ bool res = true;
+
+ // NOTE: all fields in `characters` must be read to prevent lost character data at next save in case wrong DB structure.
+ // !!! NOTE: including unused `zone`,`online`
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM, "SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, gmstate, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT leaderGuid FROM group_member WHERE memberGuid ='%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLS, "SELECT spell,slot,active,disabled FROM character_spell WHERE guid = '%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS, "SELECT quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4 FROM character_queststatus WHERE guid = '%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS,"SELECT quest,time FROM character_queststatus_daily WHERE guid = '%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADTUTORIALS, "SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u' AND realmid = '%u'", GetAccountId(), realmID);
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADREPUTATION, "SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADINVENTORY, "SELECT data,bag,slot,item,item_template FROM character_inventory JOIN item_instance ON character_inventory.item = item_instance.guid WHERE character_inventory.guid = '%u' ORDER BY bag,slot", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, "SELECT button,action,type,misc FROM character_action WHERE guid = '%u' ORDER BY button", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" I64FMTD "'", GUID_LOPART(m_guid),(uint64)time(NULL));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILDATE, "SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSOCIALLIST, "SELECT friend,flags,note FROM character_social WHERE guid = '%u' LIMIT 255", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADHOMEBIND, "SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS, "SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'", GUID_LOPART(m_guid));
+ if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED))
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = '%u'",GUID_LOPART(m_guid));
+ // in other case still be dummy query
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGUILD, "SELECT guildid,rank FROM guild_member WHERE guid = '%u'", GUID_LOPART(m_guid));
+
+ return res;
+}
+
+// don't call WorldSession directly
+// it may get deleted before the query callbacks get executed
+// instead pass an account id to this handler
+class CharacterHandler
+{
+ public:
+ void HandleCharEnumCallback(QueryResult * result, uint32 account)
+ {
+ WorldSession * session = sWorld.FindSession(account);
+ if(!session)
+ {
+ delete result;
+ return;
+ }
+ session->HandleCharEnum(result);
+ }
+ void HandlePlayerLoginCallback(QueryResult * /*dummy*/, SqlQueryHolder * holder)
+ {
+ if (!holder) return;
+ WorldSession *session = sWorld.FindSession(((LoginQueryHolder*)holder)->GetAccountId());
+ if(!session)
+ {
+ delete holder;
+ return;
+ }
+ session->HandlePlayerLogin((LoginQueryHolder*)holder);
+ }
+} chrHandler;
+
+void WorldSession::HandleCharEnum(QueryResult * result)
+{
+ // keys can be non cleared if player open realm list and close it by 'cancel'
+ loginDatabase.PExecute("UPDATE account SET v = '0', s = '0' WHERE id = '%u'", GetAccountId());
+
+ WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size
+
+ uint8 num = 0;
+
+ data << num;
+
+ if( result )
+ {
+ Player *plr = new Player(this);
+ do
+ {
+ sLog.outDetail("Loading char guid %u from account %u.",(*result)[0].GetUInt32(),GetAccountId());
+
+ if(plr->MinimalLoadFromDB( result, (*result)[0].GetUInt32() ))
+ {
+ plr->BuildEnumData( result, &data );
+ ++num;
+ }
+ }
+ while( result->NextRow() );
+
+ delete plr;
+ delete result;
+ }
+
+ data.put<uint8>(0, num);
+
+ SendPacket( &data );
+}
+
+void WorldSession::HandleCharEnumOpcode( WorldPacket & /*recv_data*/ )
+{
+ /// get all the data necessary for loading all characters (along with their pets) on the account
+ CharacterDatabase.AsyncPQuery(&chrHandler, &CharacterHandler::HandleCharEnumCallback, GetAccountId(),
+ !sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) ?
+ // ------- Query Without Declined Names --------
+ // 0 1 2 3 4 5 6 7 8
+ "SELECT characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, characters.at_login, "
+ // 9 10 11
+ "character_pet.entry, character_pet.modelid, character_pet.level "
+ "FROM characters LEFT JOIN character_pet ON characters.guid=character_pet.owner AND character_pet.slot='0' "
+ "WHERE characters.account = '%u' ORDER BY characters.guid"
+ :
+ // --------- Query With Declined Names ---------
+ // 0 1 2 3 4 5 6 7 8
+ "SELECT characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, characters.at_login, "
+ // 9 10 11 12
+ "character_pet.entry, character_pet.modelid, character_pet.level, genitive "
+ "FROM characters LEFT JOIN character_pet ON characters.guid = character_pet.owner AND character_pet.slot='0' "
+ "LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid "
+ "WHERE characters.account = '%u' ORDER BY characters.guid",
+ GetAccountId());
+}
+
+void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1+1+1+1+1+1+1+1);
+
+ std::string name;
+ uint8 race_,class_;
+ bool pTbc = this->IsTBC() && sWorld.getConfig(CONFIG_EXPANSION) > 0;
+ recv_data >> name;
+
+ // recheck with known string size
+ CHECK_PACKET_SIZE(recv_data,(name.size()+1)+1+1+1+1+1+1+1+1+1);
+
+ recv_data >> race_;
+ recv_data >> class_;
+
+ WorldPacket data(SMSG_CHAR_CREATE, 1); // returned with diff.values in all cases
+
+ if(GetSecurity() == SEC_PLAYER)
+ {
+ if(uint32 mask = sWorld.getConfig(CONFIG_CHARACTERS_CREATING_DISABLED))
+ {
+ bool disabled = false;
+
+ uint32 team = Player::TeamForRace(race_);
+ switch(team)
+ {
+ case ALLIANCE: disabled = mask & (1<<0); break;
+ case HORDE: disabled = mask & (1<<1); break;
+ }
+
+ if(disabled)
+ {
+ data << (uint8)CHAR_CREATE_DISABLED;
+ SendPacket( &data );
+ return;
+ }
+ }
+ }
+
+ if (!sChrClassesStore.LookupEntry(class_)||
+ !sChrRacesStore.LookupEntry(race_))
+ {
+ data << (uint8)CHAR_CREATE_FAILED;
+ SendPacket( &data );
+ sLog.outError("Class: %u or Race %u not found in DBC (Wrong DBC files?) or Cheater?", class_, race_);
+ return;
+ }
+
+ // prevent character creating Expansion race without Expansion account
+ if (!pTbc&&(race_>RACE_TROLL))
+ {
+ data << (uint8)CHAR_CREATE_EXPANSION;
+ sLog.outError("No Expansion Account:[%d] but tried to Create TBC character",GetAccountId());
+ SendPacket( &data );
+ return;
+ }
+
+ // prevent character creating with invalid name
+ if(!normalizePlayerName(name))
+ {
+ data << (uint8)CHAR_NAME_INVALID_CHARACTER;
+ SendPacket( &data );
+ sLog.outError("Account:[%d] but tried to Create character with empty [name] ",GetAccountId());
+ return;
+ }
+
+ // check name limitations
+ if(!ObjectMgr::IsValidName(name,true))
+ {
+ data << (uint8)CHAR_NAME_INVALID_CHARACTER;
+ SendPacket( &data );
+ return;
+ }
+
+ if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(name))
+ {
+ data << (uint8)CHAR_NAME_RESERVED;
+ SendPacket( &data );
+ return;
+ }
+
+ if(objmgr.GetPlayerGUIDByName(name))
+ {
+ data << (uint8)CHAR_CREATE_NAME_IN_USE;
+ SendPacket( &data );
+ return;
+ }
+
+ QueryResult *resultacct = loginDatabase.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%d'", GetAccountId());
+ if ( resultacct )
+ {
+ Field *fields=resultacct->Fetch();
+ uint32 acctcharcount = fields[0].GetUInt32();
+ delete resultacct;
+
+ if (acctcharcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_ACCOUNT))
+ {
+ data << (uint8)CHAR_CREATE_ACCOUNT_LIMIT;
+ SendPacket( &data );
+ return;
+ }
+ }
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", GetAccountId());
+ uint8 charcount = 0;
+ if ( result )
+ {
+ Field *fields=result->Fetch();
+ charcount = fields[0].GetUInt8();
+ delete result;
+
+ if (charcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_REALM))
+ {
+ data << (uint8)CHAR_CREATE_SERVER_LIMIT;
+ SendPacket( &data );
+ return;
+ }
+ }
+
+ bool AllowTwoSideAccounts = !sWorld.IsPvPRealm() || sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || GetSecurity() > SEC_PLAYER;
+ uint32 skipCinematics = sWorld.getConfig(CONFIG_SKIP_CINEMATICS);
+
+ bool have_same_race = false;
+ if(!AllowTwoSideAccounts || skipCinematics == 1)
+ {
+ QueryResult *result2 = CharacterDatabase.PQuery("SELECT DISTINCT race FROM characters WHERE account = '%u' %s", GetAccountId(),skipCinematics == 1 ? "" : "LIMIT 1");
+ if(result2)
+ {
+ uint32 team_= Player::TeamForRace(race_);
+
+ Field* field = result2->Fetch();
+ uint8 race = field[0].GetUInt32();
+
+ // need to check team only for first character
+ // TODO: what to if account already has characters of both races?
+ if (!AllowTwoSideAccounts)
+ {
+ uint32 team=0;
+ if(race > 0)
+ team = Player::TeamForRace(race);
+
+ if(team != team_)
+ {
+ data << (uint8)CHAR_CREATE_PVP_TEAMS_VIOLATION;
+ SendPacket( &data );
+ delete result2;
+ return;
+ }
+ }
+
+ if (skipCinematics == 1)
+ {
+ // TODO: check if cinematic already shown? (already logged in?; cinematic field)
+ while (race_ != race && result2->NextRow())
+ {
+ field = result2->Fetch();
+ race = field[0].GetUInt32();
+ }
+ have_same_race = race_ == race;
+ }
+ delete result2;
+ }
+ }
+
+ // 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( objmgr.GenerateLowGuid(HIGHGUID_PLAYER), name, race_, class_, gender, skin, face, hairStyle, hairColor, facialHair, outfitId ))
+ {
+ // Player not create (race/class problem?)
+ delete pNewChar;
+
+ data << (uint8)CHAR_CREATE_ERROR;
+ SendPacket( &data );
+
+ return;
+ }
+
+ if(have_same_race && skipCinematics == 1 || skipCinematics == 2)
+ pNewChar->setCinematic(1); // not show intro
+
+ // Player created, save it now
+ pNewChar->SaveToDB();
+ charcount+=1;
+
+ loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", GetAccountId(), realmID);
+ loginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charcount, GetAccountId(), realmID);
+
+ delete pNewChar; // created only to call SaveToDB()
+
+ data << (uint8)CHAR_CREATE_SUCCESS;
+ SendPacket( &data );
+
+ std::string IP_str = GetRemoteAddress().c_str();
+ sLog.outBasic("Account: %d (IP: %s) Create Character:[%s]",GetAccountId(),IP_str.c_str(),name.c_str());
+ sLog.outChar("Account: %d (IP: %s) Create Character:[%s]",GetAccountId(),IP_str.c_str(),name.c_str());
+}
+
+void WorldSession::HandleCharDeleteOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+ recv_data >> guid;
+
+ // can't delete loaded character
+ if(objmgr.GetPlayer(guid))
+ return;
+
+ uint32 accountId = 0;
+ std::string name;
+
+ // is guild leader
+ if(objmgr.GetGuildByLeader(guid))
+ {
+ WorldPacket data(SMSG_CHAR_DELETE, 1);
+ data << (uint8)CHAR_DELETE_FAILED_GUILD_LEADER;
+ SendPacket( &data );
+ return;
+ }
+
+ // is arena team captain
+ if(objmgr.GetArenaTeamByCapitan(guid))
+ {
+ WorldPacket data(SMSG_CHAR_DELETE, 1);
+ data << (uint8)CHAR_DELETE_FAILED_ARENA_CAPTAIN;
+ SendPacket( &data );
+ return;
+ }
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT account,name FROM characters WHERE guid='%u'", GUID_LOPART(guid));
+ if(result)
+ {
+ Field *fields = result->Fetch();
+ accountId = fields[0].GetUInt32();
+ name = fields[1].GetCppString();
+ delete result;
+ }
+
+ // prevent deleting other players' characters using cheating tools
+ if(accountId != GetAccountId())
+ return;
+
+ std::string IP_str = GetRemoteAddress();
+ sLog.outBasic("Account: %d (IP: %s) Delete Character:[%s] (guid:%u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid));
+ sLog.outChar("Account: %d (IP: %s) Delete Character:[%s] (guid: %u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid));
+
+ if(sLog.IsOutCharDump()) // optimize GetPlayerDump call
+ {
+ std::string dump = PlayerDumpWriter().GetDump(GUID_LOPART(guid));
+ sLog.outCharDump(dump.c_str(),GetAccountId(),GUID_LOPART(guid),name.c_str());
+ }
+
+ Player::DeleteFromDB(guid, GetAccountId());
+
+ WorldPacket data(SMSG_CHAR_DELETE, 1);
+ data << (uint8)CHAR_DELETE_SUCCESS;
+ SendPacket( &data );
+}
+
+void WorldSession::HandlePlayerLoginOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ m_playerLoading = true;
+ uint64 playerGuid = 0;
+
+ DEBUG_LOG( "WORLD: Recvd Player Logon Message" );
+
+ recv_data >> playerGuid;
+
+ LoginQueryHolder *holder = new LoginQueryHolder(GetAccountId(), playerGuid);
+ if(!holder->Initialize())
+ {
+ delete holder; // delete all unprocessed queries
+ m_playerLoading = false;
+ return;
+ }
+
+ CharacterDatabase.DelayQueryHolder(&chrHandler, &CharacterHandler::HandlePlayerLoginCallback, holder);
+}
+
+void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
+{
+ uint64 playerGuid = holder->GetGuid();
+
+ Player* pCurrChar = new Player(this);
+ pCurrChar->GetMotionMaster()->Initialize();
+
+ // "GetAccountId()==db stored account id" checked in LoadFromDB (prevent login not own character using cheating tools)
+ if(!pCurrChar->LoadFromDB(GUID_LOPART(playerGuid), holder))
+ {
+ KickPlayer(); // disconnect client, player no set to session and it will not deleted or saved at kick
+ delete pCurrChar; // delete it manually
+ delete holder; // delete all unprocessed queries
+ m_playerLoading = false;
+ return;
+ }
+
+ SetPlayer(pCurrChar);
+
+ pCurrChar->SendDungeonDifficulty(false);
+
+ WorldPacket data( SMSG_LOGIN_VERIFY_WORLD, 20 );
+ data << pCurrChar->GetMapId();
+ data << pCurrChar->GetPositionX();
+ data << pCurrChar->GetPositionY();
+ data << pCurrChar->GetPositionZ();
+ data << pCurrChar->GetOrientation();
+ SendPacket(&data);
+
+ data.Initialize( SMSG_ACCOUNT_DATA_TIMES, 128 );
+ for(int i = 0; i < 32; i++)
+ data << uint32(0);
+ SendPacket(&data);
+
+ data.Initialize(SMSG_FEATURE_SYSTEM_STATUS, 2); // added in 2.2.0
+ data << uint8(2); // unknown value
+ data << uint8(0); // enable(1)/disable(0) voice chat interface in client
+ SendPacket(&data);
+
+ // Send MOTD
+ {
+ data.Initialize(SMSG_MOTD, 50); // new in 2.0.1
+ data << (uint32)0;
+
+ uint32 linecount=0;
+ std::string str_motd = sWorld.GetMotd();
+ std::string::size_type pos, nextpos;
+
+ pos = 0;
+ while ( (nextpos= str_motd.find('@',pos)) != std::string::npos )
+ {
+ if (nextpos != pos)
+ {
+ data << str_motd.substr(pos,nextpos-pos);
+ ++linecount;
+ }
+ pos = nextpos+1;
+ }
+
+ if (pos<str_motd.length())
+ {
+ data << str_motd.substr(pos);
+ ++linecount;
+ }
+
+ data.put(0, linecount);
+
+ SendPacket( &data );
+ DEBUG_LOG( "WORLD: Sent motd (SMSG_MOTD)" );
+ }
+
+ if(pCurrChar->GetGuildId() != 0)
+ {
+ Guild* guild = objmgr.GetGuildById(pCurrChar->GetGuildId());
+ if(guild)
+ {
+ data.Initialize(SMSG_GUILD_EVENT, (2+guild->GetMOTD().size()+1));
+ data << (uint8)GE_MOTD;
+ data << (uint8)1;
+ data << guild->GetMOTD();
+ SendPacket(&data);
+ DEBUG_LOG( "WORLD: Sent guild-motd (SMSG_GUILD_EVENT)" );
+
+ data.Initialize(SMSG_GUILD_EVENT, (5+10)); // we guess size
+ data<<(uint8)GE_SIGNED_ON;
+ data<<(uint8)1;
+ data<<pCurrChar->GetName();
+ data<<pCurrChar->GetGUID();
+ guild->BroadcastPacket(&data);
+ DEBUG_LOG( "WORLD: Sent guild-signed-on (SMSG_GUILD_EVENT)" );
+
+ // Increment online members of the guild
+ guild->IncOnlineMemberCount();
+ }
+ else
+ {
+ // remove wrong guild data
+ sLog.outError("Player %s (GUID: %u) marked as member not existed guild (id: %u), removing guild membership for player.",pCurrChar->GetName(),pCurrChar->GetGUIDLow(),pCurrChar->GetGuildId());
+ pCurrChar->SetUInt32Value(PLAYER_GUILDID,0);
+ pCurrChar->SetUInt32ValueInDB(PLAYER_GUILDID,0,pCurrChar->GetGUID());
+ }
+ }
+
+ if(!pCurrChar->isAlive())
+ pCurrChar->SendCorpseReclaimDelay(true);
+
+ pCurrChar->SendInitialPacketsBeforeAddToMap();
+
+ //Show cinematic at the first time that player login
+ if( !pCurrChar->getCinematic() )
+ {
+ pCurrChar->setCinematic(1);
+
+ ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace());
+ if(rEntry)
+ {
+ data.Initialize( SMSG_TRIGGER_CINEMATIC,4 );
+ data << uint32(rEntry->startmovie);
+ SendPacket( &data );
+ }
+ }
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT guildid,rank FROM guild_member WHERE guid = '%u'",pCurrChar->GetGUIDLow());
+ QueryResult *resultGuild = holder->GetResult(PLAYER_LOGIN_QUERY_LOADGUILD);
+
+ if(resultGuild)
+ {
+ Field *fields = resultGuild->Fetch();
+ pCurrChar->SetInGuild(fields[0].GetUInt32());
+ pCurrChar->SetRank(fields[1].GetUInt32());
+ delete resultGuild;
+ }
+ else if(pCurrChar->GetGuildId()) // clear guild related fields in case wrong data about non existed membership
+ {
+ pCurrChar->SetInGuild(0);
+ pCurrChar->SetRank(0);
+ }
+
+ if (!MapManager::Instance().GetMap(pCurrChar->GetMapId(), pCurrChar)->Add(pCurrChar))
+ {
+ AreaTrigger const* at = objmgr.GetGoBackTrigger(pCurrChar->GetMapId());
+ if(at)
+ pCurrChar->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, pCurrChar->GetOrientation());
+ else
+ pCurrChar->TeleportTo(pCurrChar->m_homebindMapId, pCurrChar->m_homebindX, pCurrChar->m_homebindY, pCurrChar->m_homebindZ, pCurrChar->GetOrientation());
+ }
+
+ ObjectAccessor::Instance().AddObject(pCurrChar);
+ //sLog.outDebug("Player %s added to Map.",pCurrChar->GetName());
+ pCurrChar->GetSocial()->SendSocialList();
+
+ pCurrChar->SendInitialPacketsAfterAddToMap();
+
+ CharacterDatabase.PExecute("UPDATE characters SET online = 1 WHERE guid = '%u'", pCurrChar->GetGUIDLow());
+ loginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = '%u'", GetAccountId());
+ pCurrChar->SetInGameTime( getMSTime() );
+
+ // announce group about member online (must be after add to player list to receive announce to self)
+ if(Group *group = pCurrChar->GetGroup())
+ {
+ //pCurrChar->groupInfo.group->SendInit(this); // useless
+ group->SendUpdate();
+ }
+
+ // friend status
+ sSocialMgr.SendFriendStatus(pCurrChar, FRIEND_ONLINE, pCurrChar->GetGUIDLow(), "", true);
+
+ // Place character in world (and load zone) before some object loading
+ pCurrChar->LoadCorpse();
+
+ // setting Ghost+speed if dead
+ //if ( pCurrChar->m_deathState == DEAD )
+ if (pCurrChar->m_deathState != ALIVE)
+ {
+ // not blizz like, we must correctly save and load player instead...
+ if(pCurrChar->getRace() == RACE_NIGHTELF)
+ pCurrChar->CastSpell(pCurrChar, 20584, true, 0);// auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form)
+ pCurrChar->CastSpell(pCurrChar, 8326, true, 0); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
+
+ //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+41, 8326);
+ //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+42, 20584);
+ //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAFLAGS+6, 238);
+ //pCurrChar->SetUInt32Value(UNIT_FIELD_AURALEVELS+11, 514);
+ //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS+11, 65535);
+ //pCurrChar->SetUInt32Value(UNIT_FIELD_DISPLAYID, 1825);
+ //if (pCurrChar->getRace() == RACE_NIGHTELF)
+ //{
+ // pCurrChar->SetSpeed(MOVE_RUN, 1.5f*1.2f, true);
+ // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f*1.2f, true);
+ //}
+ //else
+ //{
+ // pCurrChar->SetSpeed(MOVE_RUN, 1.5f, true);
+ // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f, true);
+ //}
+ pCurrChar->SetMovement(MOVE_WATER_WALK);
+ }
+
+ if(uint32 sourceNode = pCurrChar->m_taxi.GetTaxiSource())
+ {
+
+ sLog.outDebug( "WORLD: Restart character %u taxi flight", pCurrChar->GetGUIDLow() );
+
+ uint32 MountId = objmgr.GetTaxiMount(sourceNode, pCurrChar->GetTeam());
+ uint32 path = pCurrChar->m_taxi.GetCurrentTaxiPath();
+
+ // search appropriate start path node
+ uint32 startNode = 0;
+
+ TaxiPathNodeList const& nodeList = sTaxiPathNodesByPath[path];
+
+ float distPrev = MAP_SIZE*MAP_SIZE;
+ float distNext =
+ (nodeList[0].x-pCurrChar->GetPositionX())*(nodeList[0].x-pCurrChar->GetPositionX())+
+ (nodeList[0].y-pCurrChar->GetPositionY())*(nodeList[0].y-pCurrChar->GetPositionY())+
+ (nodeList[0].z-pCurrChar->GetPositionZ())*(nodeList[0].z-pCurrChar->GetPositionZ());
+
+ for(uint32 i = 1; i < nodeList.size(); ++i)
+ {
+ TaxiPathNode const& node = nodeList[i];
+ TaxiPathNode const& prevNode = nodeList[i-1];
+
+ // skip nodes at another map
+ if(node.mapid != pCurrChar->GetMapId())
+ continue;
+
+ distPrev = distNext;
+
+ distNext =
+ (node.x-pCurrChar->GetPositionX())*(node.x-pCurrChar->GetPositionX())+
+ (node.y-pCurrChar->GetPositionY())*(node.y-pCurrChar->GetPositionY())+
+ (node.z-pCurrChar->GetPositionZ())*(node.z-pCurrChar->GetPositionZ());
+
+ float distNodes =
+ (node.x-prevNode.x)*(node.x-prevNode.x)+
+ (node.y-prevNode.y)*(node.y-prevNode.y)+
+ (node.z-prevNode.z)*(node.z-prevNode.z);
+
+ if(distNext + distPrev < distNodes)
+ {
+ startNode = i;
+ break;
+ }
+ }
+
+ SendDoFlight( MountId, path, startNode );
+ }
+
+ // Load pet if any and player is alive and not in taxi flight
+ if(pCurrChar->isAlive() && pCurrChar->m_taxi.GetTaxiSource()==0)
+ pCurrChar->LoadPet();
+
+ // Set FFA PvP for non GM in non-rest mode
+ if(sWorld.IsFFAPvPRealm() && !pCurrChar->isGameMaster() && !pCurrChar->HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_RESTING) )
+ pCurrChar->SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
+
+ if(pCurrChar->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP))
+ pCurrChar->SetContestedPvP();
+
+ // Apply at_login requests
+ if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_SPELLS))
+ {
+ pCurrChar->resetSpells();
+ SendNotification(LANG_RESET_SPELLS);
+ }
+
+ if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
+ {
+ pCurrChar->resetTalents(true);
+ SendNotification(LANG_RESET_TALENTS);
+ }
+
+ // show time before shutdown if shutdown planned.
+ if(sWorld.IsShutdowning())
+ sWorld.ShutdownMsg(true,pCurrChar);
+
+ if(pCurrChar->isGameMaster())
+ SendNotification(LANG_GM_ON);
+
+ std::string IP_str = GetRemoteAddress();
+ sLog.outChar("Account: %d (IP: %s) Login Character:[%s] (guid:%u)",GetAccountId(),IP_str.c_str(),pCurrChar->GetName() ,pCurrChar->GetGUID());
+
+ m_playerLoading = false;
+ delete holder;
+}
+
+void WorldSession::HandleSetFactionAtWar( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+1);
+
+ DEBUG_LOG( "WORLD: Received CMSG_SET_FACTION_ATWAR" );
+
+ uint32 repListID;
+ uint8 flag;
+
+ recv_data >> repListID;
+ recv_data >> flag;
+
+ FactionStateList::iterator itr = GetPlayer()->m_factions.find(repListID);
+ if (itr == GetPlayer()->m_factions.end())
+ return;
+
+ // always invisible or hidden faction can't change war state
+ if(itr->second.Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN) )
+ return;
+
+ GetPlayer()->SetFactionAtWar(&itr->second,flag);
+}
+
+//I think this function is never used :/ I dunno, but i guess this opcode not exists
+void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ )
+{
+ //CHECK_PACKET_SIZE(recv_data,4+4);
+
+ //sLog.outDebug("WORLD SESSION: HandleSetFactionCheat");
+ /*
+ uint32 FactionID;
+ uint32 Standing;
+
+ recv_data >> FactionID;
+ recv_data >> Standing;
+
+ std::list<struct Factions>::iterator itr;
+
+ for(itr = GetPlayer()->factions.begin(); itr != GetPlayer()->factions.end(); ++itr)
+ {
+ if(itr->ReputationListID == FactionID)
+ {
+ itr->Standing += Standing;
+ itr->Flags = (itr->Flags | 1);
+ break;
+ }
+ }
+ */
+ GetPlayer()->UpdateReputation();
+}
+
+void WorldSession::HandleMeetingStoneInfo( WorldPacket & /*recv_data*/ )
+{
+ DEBUG_LOG( "WORLD: Received CMSG_MEETING_STONE_INFO" );
+
+ WorldPacket data(SMSG_MEETINGSTONE_SETQUEUE, 5);
+ data << uint32(0) << uint8(6);
+ SendPacket(&data);
+}
+
+void WorldSession::HandleTutorialFlag( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 iFlag;
+ recv_data >> iFlag;
+
+ uint32 wInt = (iFlag / 32);
+ if (wInt >= 8)
+ {
+ //sLog.outError("CHEATER? Account:[%d] Guid[%u] tried to send wrong CMSG_TUTORIAL_FLAG", GetAccountId(),GetGUID());
+ return;
+ }
+ uint32 rInt = (iFlag % 32);
+
+ uint32 tutflag = GetPlayer()->GetTutorialInt( wInt );
+ tutflag |= (1 << rInt);
+ GetPlayer()->SetTutorialInt( wInt, tutflag );
+
+ //sLog.outDebug("Received Tutorial Flag Set {%u}.", iFlag);
+}
+
+void WorldSession::HandleTutorialClear( WorldPacket & /*recv_data*/ )
+{
+ for ( uint32 iI = 0; iI < 8; iI++)
+ GetPlayer()->SetTutorialInt( iI, 0xFFFFFFFF );
+}
+
+void WorldSession::HandleTutorialReset( WorldPacket & /*recv_data*/ )
+{
+ for ( uint32 iI = 0; iI < 8; iI++)
+ GetPlayer()->SetTutorialInt( iI, 0x00000000 );
+}
+
+void WorldSession::HandleSetWatchedFactionIndexOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ DEBUG_LOG("WORLD: Received CMSG_SET_WATCHED_FACTION");
+ uint32 fact;
+ recv_data >> fact;
+ GetPlayer()->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fact);
+}
+
+void WorldSession::HandleSetWatchedFactionInactiveOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4+1);
+
+ DEBUG_LOG("WORLD: Received CMSG_SET_FACTION_INACTIVE");
+ uint32 replistid;
+ uint8 inactive;
+ recv_data >> replistid >> inactive;
+
+ FactionStateList::iterator itr = _player->m_factions.find(replistid);
+ if (itr == _player->m_factions.end())
+ return;
+
+ _player->SetFactionInactive(&itr->second, inactive);
+}
+
+void WorldSession::HandleToggleHelmOpcode( WorldPacket & /*recv_data*/ )
+{
+ DEBUG_LOG("CMSG_TOGGLE_HELM for %s", _player->GetName());
+ _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM);
+}
+
+void WorldSession::HandleToggleCloakOpcode( WorldPacket & /*recv_data*/ )
+{
+ DEBUG_LOG("CMSG_TOGGLE_CLOAK for %s", _player->GetName());
+ _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK);
+}
+
+void WorldSession::HandleChangePlayerNameOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,8+1);
+
+ uint64 guid;
+ std::string newname;
+ std::string oldname;
+
+ CHECK_PACKET_SIZE(recv_data, 8+1);
+
+ recv_data >> guid;
+ recv_data >> newname;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT at_login FROM characters WHERE guid ='%u'", GUID_LOPART(guid));
+ if (result)
+ {
+ uint32 at_loginFlags;
+ Field *fields = result->Fetch();
+ at_loginFlags = fields[0].GetUInt32();
+ delete result;
+
+ if (!(at_loginFlags & AT_LOGIN_RENAME))
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_CREATE_ERROR;
+ SendPacket( &data );
+ return;
+ }
+ }
+ else
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_CREATE_ERROR;
+ SendPacket( &data );
+ return;
+ }
+
+ if(!objmgr.GetPlayerNameByGUID(guid, oldname)) // character not exist, because we have no name for this guid
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_LOGIN_NO_CHARACTER;
+ SendPacket( &data );
+ return;
+ }
+
+ // prevent character rename to invalid name
+ if(!normalizePlayerName(newname))
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_NAME_NO_NAME;
+ SendPacket( &data );
+ return;
+ }
+
+ if(!ObjectMgr::IsValidName(newname,true))
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_NAME_INVALID_CHARACTER;
+ SendPacket( &data );
+ return;
+ }
+
+ // check name limitations
+ if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname))
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_NAME_RESERVED;
+ SendPacket( &data );
+ return;
+ }
+
+ if(objmgr.GetPlayerGUIDByName(newname)) // character with this name already exist
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_CREATE_ERROR;
+ SendPacket( &data );
+ return;
+ }
+
+ if(newname == oldname) // checked by client
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_NAME_FAILURE;
+ SendPacket( &data );
+ return;
+ }
+
+ // we have to check character at_login_flag & AT_LOGIN_RENAME also (fake packets hehe)
+
+ CharacterDatabase.escape_string(newname);
+ CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_RENAME),GUID_LOPART(guid));
+ CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", GUID_LOPART(guid));
+
+ std::string IP_str = GetRemoteAddress();
+ sLog.outChar("Account: %d (IP: %s) Character:[%s] (guid:%u) Changed name to: %s",GetAccountId(),IP_str.c_str(),oldname.c_str(),GUID_LOPART(guid),newname.c_str());
+
+ WorldPacket data(SMSG_CHAR_RENAME,1+8+(newname.size()+1));
+ data << (uint8)RESPONSE_SUCCESS;
+ data << guid;
+ data << newname;
+ SendPacket(&data);
+}
+
+void WorldSession::HandleDeclinedPlayerNameOpcode(WorldPacket& recv_data)
+{
+ uint64 guid;
+
+ CHECK_PACKET_SIZE(recv_data, 8+6);
+ recv_data >> guid;
+
+ // not accept declined names for unsupported languages
+ std::string name;
+ if(!objmgr.GetPlayerNameByGUID(guid,name))
+ {
+ WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
+ data << (uint32)1;
+ data << guid;
+ SendPacket(&data);
+ return;
+ }
+
+ std::wstring wname;
+ if(!Utf8toWStr(name,wname))
+ {
+ WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
+ data << (uint32)1;
+ data << guid;
+ SendPacket(&data);
+ return;
+ }
+
+ if(!isCyrillicCharacter(wname[0])) // name already stored as only single alphabet using
+ {
+ WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
+ data << (uint32)1;
+ data << guid;
+ SendPacket(&data);
+ return;
+ }
+
+ std::string name2;
+ DeclinedName declinedname;
+
+ recv_data >> name2;
+
+ if(name2!=name) // character have different name
+ {
+ WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
+ data << (uint32)1;
+ data << guid;
+ SendPacket(&data);
+ return;
+ }
+
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ {
+ recv_data >> declinedname.name[i];
+ if(!normalizePlayerName(declinedname.name[i]))
+ {
+ WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
+ data << (uint32)1;
+ data << guid;
+ SendPacket(&data);
+ return;
+ }
+ }
+
+ if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname,0),declinedname))
+ {
+ WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
+ data << (uint32)1;
+ data << guid;
+ SendPacket(&data);
+ return;
+ }
+
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ CharacterDatabase.escape_string(declinedname.name[i]);
+
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'", GUID_LOPART(guid));
+ CharacterDatabase.PExecute("INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%s','%s','%s','%s','%s')",
+ GUID_LOPART(guid), declinedname.name[0].c_str(),declinedname.name[1].c_str(),declinedname.name[2].c_str(),declinedname.name[3].c_str(),declinedname.name[4].c_str());
+ CharacterDatabase.CommitTransaction();
+
+ WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
+ data << (uint32)0; // OK
+ data << guid;
+ SendPacket(&data);
+}
diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index e85053a2204..e8867c516f4 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -353,6 +353,7 @@ ChatCommand * ChatHandler::getCommandTable() static ChatCommand gmCommandTable[] =
{
+ { "chat", SEC_MODERATOR, &ChatHandler::HandleGMChatCommand, "", NULL },
{ "list", SEC_PLAYER, &ChatHandler::HandleGMListCommand, "", NULL },
{ "visible", SEC_MODERATOR, &ChatHandler::HandleVisibleCommand, "", NULL },
{ "fly", SEC_ADMINISTRATOR, &ChatHandler::HandleFlyModeCommand, "", NULL },
@@ -507,18 +508,29 @@ const char *ChatHandler::GetMangosString(int32 entry) return m_session->GetMangosString(entry);
}
-bool ChatHandler::hasStringAbbr(const char* s1, const char* s2)
+bool ChatHandler::hasStringAbbr(const char* name, const char* part)
{
- for(;;)
+ // non "" command
+ if( *name )
{
- if( !*s2 )
- return true;
- else if( !*s1 )
- return false;
- else if( tolower( *s1 ) != tolower( *s2 ) )
+ // "" part from non-"" command
+ if( !*part )
return false;
- ++s1; ++s2;
+
+ for(;;)
+ {
+ if( !*part )
+ return true;
+ else if( !*name )
+ return false;
+ else if( tolower( *name ) != tolower( *part ) )
+ return false;
+ ++name; ++part;
+ }
}
+ // allow with any for ""
+
+ return true;
}
void ChatHandler::SendSysMessage(const char *str)
@@ -594,13 +606,9 @@ bool ChatHandler::ExecuteCommandInTable(ChatCommand *table, const char* text, st while (*text == ' ') ++text;
- if(!cmd.length())
- return false;
-
for(uint32 i = 0; table[i].Name != NULL; i++)
{
- // allow pass "" command name in table
- if(strlen(table[i].Name) && !hasStringAbbr(table[i].Name, cmd.c_str()))
+ if( !hasStringAbbr(table[i].Name, cmd.c_str()) )
continue;
// select subcommand from child commands list
@@ -688,8 +696,7 @@ bool ChatHandler::ShowHelpForSubCommands(ChatCommand *table, char const* cmd, ch if(m_session->GetSecurity() < table[i].SecurityLevel)
continue;
- if(strlen(table[i].Name) && !hasStringAbbr(table[i].Name, subcmd))
- continue;
+ if( !hasStringAbbr(table[i].Name, subcmd) )
(list += "\n ") += table[i].Name;
}
@@ -717,7 +724,7 @@ bool ChatHandler::ShowHelpForCommand(ChatCommand *table, const char* cmd) if(m_session->GetSecurity() < table[i].SecurityLevel)
continue;
- if(strlen(table[i].Name) && !hasStringAbbr(table[i].Name, cmd))
+ if( !hasStringAbbr(table[i].Name, cmd) )
continue;
// have subcommand
diff --git a/src/game/Chat.h b/src/game/Chat.h index 26e3a3a969a..74366659f66 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -69,7 +69,7 @@ class ChatHandler int ParseCommands(const char* text);
protected:
- bool hasStringAbbr(const char* s1, const char* s2);
+ bool hasStringAbbr(const char* name, const char* part);
void SendGlobalSysMessage(const char *str);
bool ExecuteCommandInTable(ChatCommand *table, const char* text, std::string fullcommand);
@@ -94,6 +94,7 @@ class ChatHandler bool HandleAnnounceCommand(const char* args);
bool HandleNotifyCommand(const char* args);
bool HandleGMmodeCommand(const char* args);
+ bool HandleGMChatCommand(const char* args);
bool HandleVisibleCommand(const char* args);
bool HandleGPSCommand(const char* args);
bool HandleTaxiCheatCommand(const char* args);
diff --git a/src/game/ChatHandler.cpp b/src/game/ChatHandler.cpp index dd3a711858d..3a3f2d1d1db 100644 --- a/src/game/ChatHandler.cpp +++ b/src/game/ChatHandler.cpp @@ -1,583 +1,583 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "Common.h" -#include "Log.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "World.h" -#include "Opcodes.h" -#include "ObjectMgr.h" -#include "Chat.h" -#include "Database/DatabaseEnv.h" -#include "ChannelMgr.h" -#include "Group.h" -#include "Guild.h" -#include "MapManager.h" -#include "ObjectAccessor.h" -#include "ScriptCalls.h" -#include "Player.h" -#include "SpellAuras.h" -#include "Language.h" -#include "Util.h" - -void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,4+4+1); - - uint32 type; - uint32 lang; - - recv_data >> type; - recv_data >> lang; - - if(type >= MAX_CHAT_MSG_TYPE) - { - sLog.outError("CHAT: Wrong message type received: %u", type); - return; - } - - //sLog.outDebug("CHAT: packet received. type %u, lang %u", type, lang ); - - // prevent talking at unknown language (cheating) - LanguageDesc const* langDesc = GetLanguageDescByID(lang); - if(!langDesc) - { - SendNotification("Unknown language"); - return; - } - if(langDesc->skill_id != 0 && !_player->HasSkill(langDesc->skill_id)) - { - // also check SPELL_AURA_COMPREHEND_LANGUAGE (client offers option to speak in that language) - Unit::AuraList const& langAuras = _player->GetAurasByType(SPELL_AURA_COMPREHEND_LANGUAGE); - bool foundAura = false; - for(Unit::AuraList::const_iterator i = langAuras.begin();i != langAuras.end(); ++i) - { - if((*i)->GetModifier()->m_miscvalue == lang) - { - foundAura = true; - break; - } - } - if(!foundAura) - { - SendNotification("You don't know that language"); - return; - } - } - - if(lang == LANG_ADDON) - { - // Disabled addon channel? - if(!sWorld.getConfig(CONFIG_ADDON_CHANNEL)) - return; - } - // LANG_ADDON should not be changed nor be affected by flood control - else - { - // send in universal language if player in .gmon mode (ignore spell effects) - if (_player->isGameMaster()) - lang = LANG_UNIVERSAL; - else - { - // send in universal language in two side iteration allowed mode - if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT)) - lang = LANG_UNIVERSAL; - else - { - switch(type) - { - case CHAT_MSG_PARTY: - case CHAT_MSG_RAID: - case CHAT_MSG_RAID_LEADER: - case CHAT_MSG_RAID_WARNING: - // allow two side chat at group channel if two side group allowed - if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP)) - lang = LANG_UNIVERSAL; - break; - case CHAT_MSG_GUILD: - case CHAT_MSG_OFFICER: - // allow two side chat at guild channel if two side guild allowed - if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD)) - lang = LANG_UNIVERSAL; - break; - } - } - - // but overwrite it by SPELL_AURA_MOD_LANGUAGE auras (only single case used) - Unit::AuraList const& ModLangAuras = _player->GetAurasByType(SPELL_AURA_MOD_LANGUAGE); - if(!ModLangAuras.empty()) - lang = ModLangAuras.front()->GetModifier()->m_miscvalue; - } - - if (!_player->CanSpeak()) - { - std::string timeStr = secsToTimeString(m_muteTime - time(NULL)); - SendNotification(GetMangosString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str()); - return; - } - - if (type != CHAT_MSG_AFK && type != CHAT_MSG_DND) - GetPlayer()->UpdateSpeakTime(); - } - - switch(type) - { - case CHAT_MSG_SAY: - case CHAT_MSG_EMOTE: - case CHAT_MSG_YELL: - { - std::string msg = ""; - recv_data >> msg; - - if(msg.empty()) - break; - - if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) - break; - - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); - - if(msg.empty()) - break; - - if(type == CHAT_MSG_SAY) - GetPlayer()->Say(msg, lang); - else if(type == CHAT_MSG_EMOTE) - GetPlayer()->TextEmote(msg); - else if(type == CHAT_MSG_YELL) - GetPlayer()->Yell(msg, lang); - } break; - - case CHAT_MSG_WHISPER: - { - std::string to, msg; - recv_data >> to; - CHECK_PACKET_SIZE(recv_data,4+4+(to.size()+1)+1); - recv_data >> msg; - - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); - - if(msg.empty()) - break; - - if(!normalizePlayerName(to)) - { - WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1)); - data<<to; - SendPacket(&data); - break; - } - - Player *player = objmgr.GetPlayer(to.c_str()); - uint32 tSecurity = GetSecurity(); - uint32 pSecurity = player ? player->GetSession()->GetSecurity() : 0; - if(!player || tSecurity == SEC_PLAYER && pSecurity > SEC_PLAYER && !player->isAcceptWhispers()) - { - WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1)); - data<<to; - SendPacket(&data); - return; - } - - if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) && tSecurity == SEC_PLAYER && pSecurity == SEC_PLAYER ) - { - uint32 sidea = GetPlayer()->GetTeam(); - uint32 sideb = player->GetTeam(); - if( sidea != sideb ) - { - WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1)); - data<<to; - SendPacket(&data); - return; - } - } - - GetPlayer()->Whisper(msg, lang,player->GetGUID()); - } break; - - case CHAT_MSG_PARTY: - { - std::string msg = ""; - recv_data >> msg; - - if(msg.empty()) - break; - - if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) - break; - - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); - - if(msg.empty()) - break; - - Group *group = GetPlayer()->GetGroup(); - if(!group) - return; - - WorldPacket data; - ChatHandler::FillMessageData(&data, this, CHAT_MSG_PARTY, lang, NULL, 0, msg.c_str(),NULL); - group->BroadcastPacket(&data, group->GetMemberGroup(GetPlayer()->GetGUID())); - } - break; - case CHAT_MSG_GUILD: - { - std::string msg = ""; - recv_data >> msg; - - if(msg.empty()) - break; - - if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) - break; - - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); - - if(msg.empty()) - break; - - if (GetPlayer()->GetGuildId()) - { - Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); - if (guild) - guild->BroadcastToGuild(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL); - } - - break; - } - case CHAT_MSG_OFFICER: - { - std::string msg = ""; - recv_data >> msg; - - if(msg.empty()) - break; - - if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) - break; - - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); - - if(msg.empty()) - break; - - if (GetPlayer()->GetGuildId()) - { - Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); - if (guild) - guild->BroadcastToOfficers(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL); - } - break; - } - case CHAT_MSG_RAID: - { - std::string msg=""; - recv_data >> msg; - - if(msg.empty()) - break; - - if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) - break; - - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); - - if(msg.empty()) - break; - - Group *group = GetPlayer()->GetGroup(); - if(!group || !group->isRaidGroup()) - return; - - WorldPacket data; - ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); - } break; - case CHAT_MSG_RAID_LEADER: - { - std::string msg=""; - recv_data >> msg; - - if(msg.empty()) - break; - - if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) - break; - - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); - - if(msg.empty()) - break; - - Group *group = GetPlayer()->GetGroup(); - if(!group || !group->isRaidGroup() || !group->IsLeader(GetPlayer()->GetGUID())) - return; - - WorldPacket data; - ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_LEADER, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); - } break; - case CHAT_MSG_RAID_WARNING: - { - std::string msg=""; - recv_data >> msg; - - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); - - if(msg.empty()) - break; - - Group *group = GetPlayer()->GetGroup(); - if(!group || !group->isRaidGroup() || !(group->IsLeader(GetPlayer()->GetGUID()) || group->IsAssistant(GetPlayer()->GetGUID()))) - return; - - WorldPacket data; - ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_WARNING, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); - } break; - - case CHAT_MSG_BATTLEGROUND: - { - std::string msg=""; - recv_data >> msg; - - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); - - if(msg.empty()) - break; - - Group *group = GetPlayer()->GetGroup(); - if(!group || !group->isRaidGroup()) - return; - - WorldPacket data; - ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); - } break; - - case CHAT_MSG_BATTLEGROUND_LEADER: - { - std::string msg=""; - recv_data >> msg; - - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); - - if(msg.empty()) - break; - - Group *group = GetPlayer()->GetGroup(); - if(!group || !group->isRaidGroup() || !group->IsLeader(GetPlayer()->GetGUID())) - return; - - WorldPacket data; - ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND_LEADER, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); - } break; - - case CHAT_MSG_CHANNEL: - { - std::string channel = "", msg = ""; - recv_data >> channel; - - // recheck - CHECK_PACKET_SIZE(recv_data,4+4+(channel.size()+1)+1); - - recv_data >> msg; - - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); - - if(msg.empty()) - break; - - if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - { - if(Channel *chn = cMgr->GetChannel(channel,_player)) - chn->Say(_player->GetGUID(),msg.c_str(),lang); - } - } break; - - case CHAT_MSG_AFK: - { - std::string msg; - recv_data >> msg; - - if((msg.empty() || !_player->isAFK()) && !_player->isInCombat() ) - { - if(!_player->isAFK()) - { - if(msg.empty()) - msg = GetMangosString(LANG_PLAYER_AFK_DEFAULT); - _player->afkMsg = msg; - } - _player->ToggleAFK(); - if(_player->isAFK() && _player->isDND()) - _player->ToggleDND(); - } - } break; - - case CHAT_MSG_DND: - { - std::string msg; - recv_data >> msg; - - if(msg.empty() || !_player->isDND()) - { - if(!_player->isDND()) - { - if(msg.empty()) - msg = GetMangosString(LANG_PLAYER_DND_DEFAULT); - _player->dndMsg = msg; - } - _player->ToggleDND(); - if(_player->isDND() && _player->isAFK()) - _player->ToggleAFK(); - } - } break; - - default: - sLog.outError("CHAT: unknown message type %u, lang: %u", type, lang); - break; - } -} - -void WorldSession::HandleEmoteOpcode( WorldPacket & recv_data ) -{ - if(!GetPlayer()->isAlive()) - return; - CHECK_PACKET_SIZE(recv_data,4); - - uint32 emote; - recv_data >> emote; - GetPlayer()->HandleEmoteCommand(emote); -} - -void WorldSession::HandleTextEmoteOpcode( WorldPacket & recv_data ) -{ - if(!GetPlayer()->isAlive()) - return; - - if (!GetPlayer()->CanSpeak()) - { - std::string timeStr = secsToTimeString(m_muteTime - time(NULL)); - SendNotification(GetMangosString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str()); - return; - } - - CHECK_PACKET_SIZE(recv_data,4+4+8); - - uint32 text_emote, emoteNum; - uint64 guid; - - recv_data >> text_emote; - recv_data >> emoteNum; - recv_data >> guid; - - const char *nam = 0; - uint32 namlen = 1; - - Unit* unit = ObjectAccessor::GetUnit(*_player, guid); - Creature *pCreature = dynamic_cast<Creature *>(unit); - if(unit) - { - nam = unit->GetName(); - namlen = (nam ? strlen(nam) : 0) + 1; - } - - EmotesTextEntry const *em = sEmotesTextStore.LookupEntry(text_emote); - if (em) - { - uint32 emote_anim = em->textid; - - WorldPacket data; - - switch(emote_anim) - { - case EMOTE_STATE_SLEEP: - case EMOTE_STATE_SIT: - case EMOTE_STATE_KNEEL: - case EMOTE_ONESHOT_NONE: - break; - default: - GetPlayer()->HandleEmoteCommand(emote_anim); - break; - } - - data.Initialize(SMSG_TEXT_EMOTE, (20+namlen)); - data << GetPlayer()->GetGUID(); - data << (uint32)text_emote; - data << emoteNum; - data << (uint32)namlen; - if( namlen > 1 ) - { - data.append(nam, namlen); - } - else - { - data << (uint8)0x00; - } - - GetPlayer()->SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true); - - //Send scripted event call - if (pCreature && Script) - Script->ReceiveEmote(GetPlayer(),pCreature,text_emote); - } -} - -void WorldSession::HandleChatIgnoredOpcode(WorldPacket& recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 8+1); - - uint64 iguid; - uint8 unk; - //sLog.outDebug("WORLD: Received CMSG_CHAT_IGNORED"); - - recv_data >> iguid; - recv_data >> unk; // probably related to spam reporting - - Player *player = objmgr.GetPlayer(iguid); - if(!player || !player->GetSession()) - return; - - WorldPacket data; - ChatHandler::FillMessageData(&data, this, CHAT_MSG_IGNORED, LANG_UNIVERSAL, NULL, GetPlayer()->GetGUID(), GetPlayer()->GetName(),NULL); - player->GetSession()->SendPacket(&data); -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Log.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "Opcodes.h"
+#include "ObjectMgr.h"
+#include "Chat.h"
+#include "Database/DatabaseEnv.h"
+#include "ChannelMgr.h"
+#include "Group.h"
+#include "Guild.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "ScriptCalls.h"
+#include "Player.h"
+#include "SpellAuras.h"
+#include "Language.h"
+#include "Util.h"
+
+void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+4+1);
+
+ uint32 type;
+ uint32 lang;
+
+ recv_data >> type;
+ recv_data >> lang;
+
+ if(type >= MAX_CHAT_MSG_TYPE)
+ {
+ sLog.outError("CHAT: Wrong message type received: %u", type);
+ return;
+ }
+
+ //sLog.outDebug("CHAT: packet received. type %u, lang %u", type, lang );
+
+ // prevent talking at unknown language (cheating)
+ LanguageDesc const* langDesc = GetLanguageDescByID(lang);
+ if(!langDesc)
+ {
+ SendNotification(LANG_UNKNOWN_LANGUAGE);
+ return;
+ }
+ if(langDesc->skill_id != 0 && !_player->HasSkill(langDesc->skill_id))
+ {
+ // also check SPELL_AURA_COMPREHEND_LANGUAGE (client offers option to speak in that language)
+ Unit::AuraList const& langAuras = _player->GetAurasByType(SPELL_AURA_COMPREHEND_LANGUAGE);
+ bool foundAura = false;
+ for(Unit::AuraList::const_iterator i = langAuras.begin();i != langAuras.end(); ++i)
+ {
+ if((*i)->GetModifier()->m_miscvalue == lang)
+ {
+ foundAura = true;
+ break;
+ }
+ }
+ if(!foundAura)
+ {
+ SendNotification(LANG_NOT_LEARNED_LANGUAGE);
+ return;
+ }
+ }
+
+ if(lang == LANG_ADDON)
+ {
+ // Disabled addon channel?
+ if(!sWorld.getConfig(CONFIG_ADDON_CHANNEL))
+ return;
+ }
+ // LANG_ADDON should not be changed nor be affected by flood control
+ else
+ {
+ // send in universal language if player in .gmon mode (ignore spell effects)
+ if (_player->isGameMaster())
+ lang = LANG_UNIVERSAL;
+ else
+ {
+ // send in universal language in two side iteration allowed mode
+ if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT))
+ lang = LANG_UNIVERSAL;
+ else
+ {
+ switch(type)
+ {
+ case CHAT_MSG_PARTY:
+ case CHAT_MSG_RAID:
+ case CHAT_MSG_RAID_LEADER:
+ case CHAT_MSG_RAID_WARNING:
+ // allow two side chat at group channel if two side group allowed
+ if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP))
+ lang = LANG_UNIVERSAL;
+ break;
+ case CHAT_MSG_GUILD:
+ case CHAT_MSG_OFFICER:
+ // allow two side chat at guild channel if two side guild allowed
+ if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD))
+ lang = LANG_UNIVERSAL;
+ break;
+ }
+ }
+
+ // but overwrite it by SPELL_AURA_MOD_LANGUAGE auras (only single case used)
+ Unit::AuraList const& ModLangAuras = _player->GetAurasByType(SPELL_AURA_MOD_LANGUAGE);
+ if(!ModLangAuras.empty())
+ lang = ModLangAuras.front()->GetModifier()->m_miscvalue;
+ }
+
+ if (!_player->CanSpeak())
+ {
+ std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
+ SendNotification(GetMangosString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str());
+ return;
+ }
+
+ if (type != CHAT_MSG_AFK && type != CHAT_MSG_DND)
+ GetPlayer()->UpdateSpeakTime();
+ }
+
+ switch(type)
+ {
+ case CHAT_MSG_SAY:
+ case CHAT_MSG_EMOTE:
+ case CHAT_MSG_YELL:
+ {
+ std::string msg = "";
+ recv_data >> msg;
+
+ if(msg.empty())
+ break;
+
+ if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
+ break;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ if(type == CHAT_MSG_SAY)
+ GetPlayer()->Say(msg, lang);
+ else if(type == CHAT_MSG_EMOTE)
+ GetPlayer()->TextEmote(msg);
+ else if(type == CHAT_MSG_YELL)
+ GetPlayer()->Yell(msg, lang);
+ } break;
+
+ case CHAT_MSG_WHISPER:
+ {
+ std::string to, msg;
+ recv_data >> to;
+ CHECK_PACKET_SIZE(recv_data,4+4+(to.size()+1)+1);
+ recv_data >> msg;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ if(!normalizePlayerName(to))
+ {
+ WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1));
+ data<<to;
+ SendPacket(&data);
+ break;
+ }
+
+ Player *player = objmgr.GetPlayer(to.c_str());
+ uint32 tSecurity = GetSecurity();
+ uint32 pSecurity = player ? player->GetSession()->GetSecurity() : 0;
+ if(!player || tSecurity == SEC_PLAYER && pSecurity > SEC_PLAYER && !player->isAcceptWhispers())
+ {
+ WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1));
+ data<<to;
+ SendPacket(&data);
+ return;
+ }
+
+ if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) && tSecurity == SEC_PLAYER && pSecurity == SEC_PLAYER )
+ {
+ uint32 sidea = GetPlayer()->GetTeam();
+ uint32 sideb = player->GetTeam();
+ if( sidea != sideb )
+ {
+ WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1));
+ data<<to;
+ SendPacket(&data);
+ return;
+ }
+ }
+
+ GetPlayer()->Whisper(msg, lang,player->GetGUID());
+ } break;
+
+ case CHAT_MSG_PARTY:
+ {
+ std::string msg = "";
+ recv_data >> msg;
+
+ if(msg.empty())
+ break;
+
+ if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
+ break;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group)
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, this, CHAT_MSG_PARTY, lang, NULL, 0, msg.c_str(),NULL);
+ group->BroadcastPacket(&data, group->GetMemberGroup(GetPlayer()->GetGUID()));
+ }
+ break;
+ case CHAT_MSG_GUILD:
+ {
+ std::string msg = "";
+ recv_data >> msg;
+
+ if(msg.empty())
+ break;
+
+ if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
+ break;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ if (GetPlayer()->GetGuildId())
+ {
+ Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if (guild)
+ guild->BroadcastToGuild(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
+ }
+
+ break;
+ }
+ case CHAT_MSG_OFFICER:
+ {
+ std::string msg = "";
+ recv_data >> msg;
+
+ if(msg.empty())
+ break;
+
+ if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
+ break;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ if (GetPlayer()->GetGuildId())
+ {
+ Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if (guild)
+ guild->BroadcastToOfficers(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
+ }
+ break;
+ }
+ case CHAT_MSG_RAID:
+ {
+ std::string msg="";
+ recv_data >> msg;
+
+ if(msg.empty())
+ break;
+
+ if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
+ break;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group || !group->isRaidGroup())
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID, lang, "", 0, msg.c_str(),NULL);
+ group->BroadcastPacket(&data);
+ } break;
+ case CHAT_MSG_RAID_LEADER:
+ {
+ std::string msg="";
+ recv_data >> msg;
+
+ if(msg.empty())
+ break;
+
+ if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
+ break;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group || !group->isRaidGroup() || !group->IsLeader(GetPlayer()->GetGUID()))
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_LEADER, lang, "", 0, msg.c_str(),NULL);
+ group->BroadcastPacket(&data);
+ } break;
+ case CHAT_MSG_RAID_WARNING:
+ {
+ std::string msg="";
+ recv_data >> msg;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group || !group->isRaidGroup() || !(group->IsLeader(GetPlayer()->GetGUID()) || group->IsAssistant(GetPlayer()->GetGUID())))
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_WARNING, lang, "", 0, msg.c_str(),NULL);
+ group->BroadcastPacket(&data);
+ } break;
+
+ case CHAT_MSG_BATTLEGROUND:
+ {
+ std::string msg="";
+ recv_data >> msg;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group || !group->isRaidGroup())
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND, lang, "", 0, msg.c_str(),NULL);
+ group->BroadcastPacket(&data);
+ } break;
+
+ case CHAT_MSG_BATTLEGROUND_LEADER:
+ {
+ std::string msg="";
+ recv_data >> msg;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group || !group->isRaidGroup() || !group->IsLeader(GetPlayer()->GetGUID()))
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND_LEADER, lang, "", 0, msg.c_str(),NULL);
+ group->BroadcastPacket(&data);
+ } break;
+
+ case CHAT_MSG_CHANNEL:
+ {
+ std::string channel = "", msg = "";
+ recv_data >> channel;
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,4+4+(channel.size()+1)+1);
+
+ recv_data >> msg;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ {
+ if(Channel *chn = cMgr->GetChannel(channel,_player))
+ chn->Say(_player->GetGUID(),msg.c_str(),lang);
+ }
+ } break;
+
+ case CHAT_MSG_AFK:
+ {
+ std::string msg;
+ recv_data >> msg;
+
+ if((msg.empty() || !_player->isAFK()) && !_player->isInCombat() )
+ {
+ if(!_player->isAFK())
+ {
+ if(msg.empty())
+ msg = GetMangosString(LANG_PLAYER_AFK_DEFAULT);
+ _player->afkMsg = msg;
+ }
+ _player->ToggleAFK();
+ if(_player->isAFK() && _player->isDND())
+ _player->ToggleDND();
+ }
+ } break;
+
+ case CHAT_MSG_DND:
+ {
+ std::string msg;
+ recv_data >> msg;
+
+ if(msg.empty() || !_player->isDND())
+ {
+ if(!_player->isDND())
+ {
+ if(msg.empty())
+ msg = GetMangosString(LANG_PLAYER_DND_DEFAULT);
+ _player->dndMsg = msg;
+ }
+ _player->ToggleDND();
+ if(_player->isDND() && _player->isAFK())
+ _player->ToggleAFK();
+ }
+ } break;
+
+ default:
+ sLog.outError("CHAT: unknown message type %u, lang: %u", type, lang);
+ break;
+ }
+}
+
+void WorldSession::HandleEmoteOpcode( WorldPacket & recv_data )
+{
+ if(!GetPlayer()->isAlive())
+ return;
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 emote;
+ recv_data >> emote;
+ GetPlayer()->HandleEmoteCommand(emote);
+}
+
+void WorldSession::HandleTextEmoteOpcode( WorldPacket & recv_data )
+{
+ if(!GetPlayer()->isAlive())
+ return;
+
+ if (!GetPlayer()->CanSpeak())
+ {
+ std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
+ SendNotification(GetMangosString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str());
+ return;
+ }
+
+ CHECK_PACKET_SIZE(recv_data,4+4+8);
+
+ uint32 text_emote, emoteNum;
+ uint64 guid;
+
+ recv_data >> text_emote;
+ recv_data >> emoteNum;
+ recv_data >> guid;
+
+ const char *nam = 0;
+ uint32 namlen = 1;
+
+ Unit* unit = ObjectAccessor::GetUnit(*_player, guid);
+ Creature *pCreature = dynamic_cast<Creature *>(unit);
+ if(unit)
+ {
+ nam = unit->GetName();
+ namlen = (nam ? strlen(nam) : 0) + 1;
+ }
+
+ EmotesTextEntry const *em = sEmotesTextStore.LookupEntry(text_emote);
+ if (em)
+ {
+ uint32 emote_anim = em->textid;
+
+ WorldPacket data;
+
+ switch(emote_anim)
+ {
+ case EMOTE_STATE_SLEEP:
+ case EMOTE_STATE_SIT:
+ case EMOTE_STATE_KNEEL:
+ case EMOTE_ONESHOT_NONE:
+ break;
+ default:
+ GetPlayer()->HandleEmoteCommand(emote_anim);
+ break;
+ }
+
+ data.Initialize(SMSG_TEXT_EMOTE, (20+namlen));
+ data << GetPlayer()->GetGUID();
+ data << (uint32)text_emote;
+ data << emoteNum;
+ data << (uint32)namlen;
+ if( namlen > 1 )
+ {
+ data.append(nam, namlen);
+ }
+ else
+ {
+ data << (uint8)0x00;
+ }
+
+ GetPlayer()->SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true);
+
+ //Send scripted event call
+ if (pCreature && Script)
+ Script->ReceiveEmote(GetPlayer(),pCreature,text_emote);
+ }
+}
+
+void WorldSession::HandleChatIgnoredOpcode(WorldPacket& recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8+1);
+
+ uint64 iguid;
+ uint8 unk;
+ //sLog.outDebug("WORLD: Received CMSG_CHAT_IGNORED");
+
+ recv_data >> iguid;
+ recv_data >> unk; // probably related to spam reporting
+
+ Player *player = objmgr.GetPlayer(iguid);
+ if(!player || !player->GetSession())
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, this, CHAT_MSG_IGNORED, LANG_UNIVERSAL, NULL, GetPlayer()->GetGUID(), GetPlayer()->GetName(),NULL);
+ player->GetSession()->SendPacket(&data);
+}
diff --git a/src/game/ConfusedMovementGenerator.cpp b/src/game/ConfusedMovementGenerator.cpp index f1e7c2c1548..7b4c5b91f71 100644 --- a/src/game/ConfusedMovementGenerator.cpp +++ b/src/game/ConfusedMovementGenerator.cpp @@ -1,155 +1,155 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "Creature.h" -#include "MapManager.h" -#include "Opcodes.h" -#include "ConfusedMovementGenerator.h" -#include "DestinationHolderImp.h" - -template<class T> -void -ConfusedMovementGenerator<T>::Initialize(T &unit) -{ - const float wander_distance=11; - float x,y,z; - x = unit.GetPositionX(); - y = unit.GetPositionY(); - z = unit.GetPositionZ(); - uint32 mapid=unit.GetMapId(); - - Map const* map = MapManager::Instance().GetBaseMap(mapid); - - i_nextMove = 1; - - bool is_water_ok, is_land_ok; - _InitSpecific(unit, is_water_ok, is_land_ok); - - for(unsigned int idx=0; idx < MAX_CONF_WAYPOINTS+1; ++idx) - { - const float wanderX=wander_distance*rand_norm() - wander_distance/2; - const float wanderY=wander_distance*rand_norm() - wander_distance/2; - - i_waypoints[idx][0] = x + wanderX; - i_waypoints[idx][1] = y + wanderY; - - // prevent invalid coordinates generation - MaNGOS::NormalizeMapCoord(i_waypoints[idx][0]); - MaNGOS::NormalizeMapCoord(i_waypoints[idx][1]); - - bool is_water = map->IsInWater(i_waypoints[idx][0],i_waypoints[idx][1],z); - // if generated wrong path just ignore - if( is_water && !is_water_ok || !is_water && !is_land_ok ) - { - i_waypoints[idx][0] = idx > 0 ? i_waypoints[idx-1][0] : x; - i_waypoints[idx][1] = idx > 0 ? i_waypoints[idx-1][1] : y; - } - unit.UpdateGroundPositionZ(i_waypoints[idx][0],i_waypoints[idx][1],z); - i_waypoints[idx][2] = z; - } - - unit.StopMoving(); - unit.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); - unit.addUnitState(UNIT_STAT_CONFUSED); -} - -template<> -void -ConfusedMovementGenerator<Creature>::_InitSpecific(Creature &creature, bool &is_water_ok, bool &is_land_ok) -{ - is_water_ok = creature.canSwim(); - is_land_ok = creature.canWalk(); -} - -template<> -void -ConfusedMovementGenerator<Player>::_InitSpecific(Player &, bool &is_water_ok, bool &is_land_ok) -{ - is_water_ok = true; - is_land_ok = true; -} - -template<class T> -void -ConfusedMovementGenerator<T>::Reset(T &unit) -{ - i_nextMove = 1; - i_nextMoveTime.Reset(0); - i_destinationHolder.ResetUpdate(); - unit.StopMoving(); -} - -template<class T> -bool -ConfusedMovementGenerator<T>::Update(T &unit, const uint32 &diff) -{ - if(!&unit) - return true; - - if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED)) - return true; - - if( i_nextMoveTime.Passed() ) - { - // currently moving, update location - Traveller<T> traveller(unit); - if( i_destinationHolder.UpdateTraveller(traveller, diff, false)) - { - if( i_destinationHolder.HasArrived()) - { - // arrived, stop and wait a bit - unit.StopMoving(); - - i_nextMove = urand(1,MAX_CONF_WAYPOINTS); - i_nextMoveTime.Reset(urand(0, 1500-1)); // TODO: check the minimum reset time, should be probably higher - } - } - } - else - { - // waiting for next move - i_nextMoveTime.Update(diff); - if( i_nextMoveTime.Passed() ) - { - // start moving - assert( i_nextMove <= MAX_CONF_WAYPOINTS ); - const float x = i_waypoints[i_nextMove][0]; - const float y = i_waypoints[i_nextMove][1]; - const float z = i_waypoints[i_nextMove][2]; - Traveller<T> traveller(unit); - i_destinationHolder.SetDestination(traveller, x, y, z); - } - } - return true; -} - -template<class T> -void -ConfusedMovementGenerator<T>::Finalize(T &unit) -{ - unit.clearUnitState(UNIT_STAT_CONFUSED); -} - -template void ConfusedMovementGenerator<Player>::Initialize(Player &player); -template void ConfusedMovementGenerator<Creature>::Initialize(Creature &creature); -template void ConfusedMovementGenerator<Player>::Finalize(Player &player); -template void ConfusedMovementGenerator<Creature>::Finalize(Creature &creature); -template void ConfusedMovementGenerator<Player>::Reset(Player &player); -template void ConfusedMovementGenerator<Creature>::Reset(Creature &creature); -template bool ConfusedMovementGenerator<Player>::Update(Player &player, const uint32 &diff); -template bool ConfusedMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff); +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Creature.h"
+#include "MapManager.h"
+#include "Opcodes.h"
+#include "ConfusedMovementGenerator.h"
+#include "DestinationHolderImp.h"
+
+template<class T>
+void
+ConfusedMovementGenerator<T>::Initialize(T &unit)
+{
+ const float wander_distance=11;
+ float x,y,z;
+ x = unit.GetPositionX();
+ y = unit.GetPositionY();
+ z = unit.GetPositionZ();
+ uint32 mapid=unit.GetMapId();
+
+ Map const* map = MapManager::Instance().GetBaseMap(mapid);
+
+ i_nextMove = 1;
+
+ bool is_water_ok, is_land_ok;
+ _InitSpecific(unit, is_water_ok, is_land_ok);
+
+ for(unsigned int idx=0; idx < MAX_CONF_WAYPOINTS+1; ++idx)
+ {
+ const float wanderX=wander_distance*rand_norm() - wander_distance/2;
+ const float wanderY=wander_distance*rand_norm() - wander_distance/2;
+
+ i_waypoints[idx][0] = x + wanderX;
+ i_waypoints[idx][1] = y + wanderY;
+
+ // prevent invalid coordinates generation
+ MaNGOS::NormalizeMapCoord(i_waypoints[idx][0]);
+ MaNGOS::NormalizeMapCoord(i_waypoints[idx][1]);
+
+ bool is_water = map->IsInWater(i_waypoints[idx][0],i_waypoints[idx][1],z);
+ // if generated wrong path just ignore
+ if( is_water && !is_water_ok || !is_water && !is_land_ok )
+ {
+ i_waypoints[idx][0] = idx > 0 ? i_waypoints[idx-1][0] : x;
+ i_waypoints[idx][1] = idx > 0 ? i_waypoints[idx-1][1] : y;
+ }
+ unit.UpdateGroundPositionZ(i_waypoints[idx][0],i_waypoints[idx][1],z);
+ i_waypoints[idx][2] = z;
+ }
+
+ unit.StopMoving();
+ unit.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ unit.addUnitState(UNIT_STAT_CONFUSED);
+}
+
+template<>
+void
+ConfusedMovementGenerator<Creature>::_InitSpecific(Creature &creature, bool &is_water_ok, bool &is_land_ok)
+{
+ is_water_ok = creature.canSwim();
+ is_land_ok = creature.canWalk();
+}
+
+template<>
+void
+ConfusedMovementGenerator<Player>::_InitSpecific(Player &, bool &is_water_ok, bool &is_land_ok)
+{
+ is_water_ok = true;
+ is_land_ok = true;
+}
+
+template<class T>
+void
+ConfusedMovementGenerator<T>::Reset(T &unit)
+{
+ i_nextMove = 1;
+ i_nextMoveTime.Reset(0);
+ i_destinationHolder.ResetUpdate();
+ unit.StopMoving();
+}
+
+template<class T>
+bool
+ConfusedMovementGenerator<T>::Update(T &unit, const uint32 &diff)
+{
+ if(!&unit)
+ return true;
+
+ if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED))
+ return true;
+
+ if( i_nextMoveTime.Passed() )
+ {
+ // currently moving, update location
+ Traveller<T> traveller(unit);
+ if( i_destinationHolder.UpdateTraveller(traveller, diff, false))
+ {
+ if( i_destinationHolder.HasArrived())
+ {
+ // arrived, stop and wait a bit
+ unit.StopMoving();
+
+ i_nextMove = urand(1,MAX_CONF_WAYPOINTS);
+ i_nextMoveTime.Reset(urand(0, 1500-1)); // TODO: check the minimum reset time, should be probably higher
+ }
+ }
+ }
+ else
+ {
+ // waiting for next move
+ i_nextMoveTime.Update(diff);
+ if( i_nextMoveTime.Passed() )
+ {
+ // start moving
+ assert( i_nextMove <= MAX_CONF_WAYPOINTS );
+ const float x = i_waypoints[i_nextMove][0];
+ const float y = i_waypoints[i_nextMove][1];
+ const float z = i_waypoints[i_nextMove][2];
+ Traveller<T> traveller(unit);
+ i_destinationHolder.SetDestination(traveller, x, y, z);
+ }
+ }
+ return true;
+}
+
+template<class T>
+void
+ConfusedMovementGenerator<T>::Finalize(T &unit)
+{
+ unit.clearUnitState(UNIT_STAT_CONFUSED);
+}
+
+template void ConfusedMovementGenerator<Player>::Initialize(Player &player);
+template void ConfusedMovementGenerator<Creature>::Initialize(Creature &creature);
+template void ConfusedMovementGenerator<Player>::Finalize(Player &player);
+template void ConfusedMovementGenerator<Creature>::Finalize(Creature &creature);
+template void ConfusedMovementGenerator<Player>::Reset(Player &player);
+template void ConfusedMovementGenerator<Creature>::Reset(Creature &creature);
+template bool ConfusedMovementGenerator<Player>::Update(Player &player, const uint32 &diff);
+template bool ConfusedMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff);
diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 555bb4db02a..814b133a1b3 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -75,6 +75,14 @@ bool VendorItemData::RemoveItem( uint32 item_id ) return false;
}
+size_t VendorItemData::FindItemSlot(uint32 item_id) const
+{
+ for(size_t i = 0; i < m_items.size(); ++i )
+ if(m_items[i]->item==item_id)
+ return i;
+ return m_items.size();
+}
+
VendorItem const* VendorItemData::FindItem(uint32 item_id) const
{
for(VendorItemList::const_iterator i = m_items.begin(); i != m_items.end(); ++i )
@@ -1910,26 +1918,29 @@ time_t Creature::GetRespawnTimeEx() const void Creature::GetRespawnCoord( float &x, float &y, float &z, float* ori, float* dist ) const
{
- if(CreatureData const* data = objmgr.GetCreatureData(GetDBTableGUIDLow()))
- {
- x = data->posX;
- y = data->posY;
- z = data->posZ;
- if(ori)
- *ori = data->orientation;
- if(dist)
- *dist = data->spawndist;
- }
- else
+ if (m_DBTableGuid)
{
- x = GetPositionX();
- y = GetPositionY();
- z = GetPositionZ();
- if(ori)
- *ori = GetOrientation();
- if(dist)
- *dist = 0;
+ if (CreatureData const* data = objmgr.GetCreatureData(GetDBTableGUIDLow()))
+ {
+ x = data->posX;
+ y = data->posY;
+ z = data->posZ;
+ if(ori)
+ *ori = data->orientation;
+ if(dist)
+ *dist = data->spawndist;
+
+ return;
+ }
}
+
+ x = GetPositionX();
+ y = GetPositionY();
+ z = GetPositionZ();
+ if(ori)
+ *ori = GetOrientation();
+ if(dist)
+ *dist = 0;
}
void Creature::AllLootRemovedFromCorpse()
diff --git a/src/game/Creature.h b/src/game/Creature.h index 6145b946892..c32ac7327b9 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -308,6 +308,7 @@ struct VendorItemData }
bool RemoveItem( uint32 item_id );
VendorItem const* FindItem(uint32 item_id) const;
+ size_t FindItemSlot(uint32 item_id) const;
void Clear()
{
diff --git a/src/game/FleeingMovementGenerator.cpp b/src/game/FleeingMovementGenerator.cpp index 80ecf13922f..97862eac567 100644 --- a/src/game/FleeingMovementGenerator.cpp +++ b/src/game/FleeingMovementGenerator.cpp @@ -1,379 +1,379 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "Creature.h" -#include "MapManager.h" -#include "FleeingMovementGenerator.h" -#include "DestinationHolderImp.h" -#include "ObjectAccessor.h" - -#define MIN_QUIET_DISTANCE 28.0f -#define MAX_QUIET_DISTANCE 43.0f - -template<class T> -void -FleeingMovementGenerator<T>::_setTargetLocation(T &owner) -{ - if( !&owner ) - return; - - if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED) ) - return; - - if(!_setMoveData(owner)) - return; - - float x, y, z; - if(!_getPoint(owner, x, y, z)) - return; - - owner.addUnitState(UNIT_STAT_FLEEING); - Traveller<T> traveller(owner); - i_destinationHolder.SetDestination(traveller, x, y, z); -} - -template<class T> -bool -FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float &z) -{ - if(!&owner) - return false; - - x = owner.GetPositionX(); - y = owner.GetPositionY(); - z = owner.GetPositionZ(); - - float temp_x, temp_y, angle; - const Map * _map = MapManager::Instance().GetBaseMap(owner.GetMapId()); - //primitive path-finding - for(uint8 i = 0; i < 18; i++) - { - if(i_only_forward && i > 2) - break; - - float distance = 5.0f; - - switch(i) - { - case 0: - angle = i_cur_angle; - break; - case 1: - angle = i_cur_angle; - distance /= 2; - break; - case 2: - angle = i_cur_angle; - distance /= 4; - break; - case 3: - angle = i_cur_angle + M_PI/4.0f; - break; - case 4: - angle = i_cur_angle - M_PI/4.0f; - break; - case 5: - angle = i_cur_angle + M_PI/4.0f; - distance /= 2; - break; - case 6: - angle = i_cur_angle - M_PI/4.0f; - distance /= 2; - break; - case 7: - angle = i_cur_angle + M_PI/2.0f; - break; - case 8: - angle = i_cur_angle - M_PI/2.0f; - break; - case 9: - angle = i_cur_angle + M_PI/2.0f; - distance /= 2; - break; - case 10: - angle = i_cur_angle - M_PI/2.0f; - distance /= 2; - break; - case 11: - angle = i_cur_angle + M_PI/4.0f; - distance /= 4; - break; - case 12: - angle = i_cur_angle - M_PI/4.0f; - distance /= 4; - break; - case 13: - angle = i_cur_angle + M_PI/2.0f; - distance /= 4; - break; - case 14: - angle = i_cur_angle - M_PI/2.0f; - distance /= 4; - break; - case 15: - angle = i_cur_angle + M_PI*3/4.0f; - distance /= 2; - break; - case 16: - angle = i_cur_angle - M_PI*3/4.0f; - distance /= 2; - break; - case 17: - angle = i_cur_angle + M_PI; - distance /= 2; - break; - } - temp_x = x + distance * cos(angle); - temp_y = y + distance * sin(angle); - MaNGOS::NormalizeMapCoord(temp_x); - MaNGOS::NormalizeMapCoord(temp_y); - if( owner.IsWithinLOS(temp_x,temp_y,z)) - { - bool is_water_now = _map->IsInWater(x,y,z); - - if(is_water_now && _map->IsInWater(temp_x,temp_y,z)) - { - x = temp_x; - y = temp_y; - return true; - } - float new_z = _map->GetHeight(temp_x,temp_y,z,true); - - if(new_z <= INVALID_HEIGHT) - continue; - - bool is_water_next = _map->IsInWater(temp_x,temp_y,new_z); - - if((is_water_now && !is_water_next && !is_land_ok) || (!is_water_now && is_water_next && !is_water_ok)) - continue; - - if( !(new_z - z) || distance / fabs(new_z - z) > 1.0f) - { - float new_z_left = _map->GetHeight(temp_x + 1.0f*cos(angle+M_PI/2),temp_y + 1.0f*sin(angle+M_PI/2),z,true); - float new_z_right = _map->GetHeight(temp_x + 1.0f*cos(angle-M_PI/2),temp_y + 1.0f*sin(angle-M_PI/2),z,true); - if(fabs(new_z_left - new_z) < 1.2f && fabs(new_z_right - new_z) < 1.2f) - { - x = temp_x; - y = temp_y; - z = new_z; - return true; - } - } - } - } - i_to_distance_from_caster = 0.0f; - i_nextCheckTime.Reset( urand(500,1000) ); - return false; -} - -template<class T> -bool -FleeingMovementGenerator<T>::_setMoveData(T &owner) -{ - float cur_dist_xyz = owner.GetDistance(i_caster_x, i_caster_y, i_caster_z); - - if(i_to_distance_from_caster > 0.0f) - { - if((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) || - // if we reach lower distance - (i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz > i_last_distance_from_caster) || - // if we can't be close - (i_last_distance_from_caster < i_to_distance_from_caster && cur_dist_xyz > i_to_distance_from_caster) || - // if we reach bigger distance - (cur_dist_xyz > MAX_QUIET_DISTANCE) || // if we are too far - (i_last_distance_from_caster > MIN_QUIET_DISTANCE && cur_dist_xyz < MIN_QUIET_DISTANCE) ) - // if we leave 'quiet zone' - { - // we are very far or too close, stopping - i_to_distance_from_caster = 0.0f; - i_nextCheckTime.Reset( urand(500,1000) ); - return false; - } - else - { - // now we are running, continue - i_last_distance_from_caster = cur_dist_xyz; - return true; - } - } - - float cur_dist; - float angle_to_caster; - - Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID); - - if(fright) - { - cur_dist = fright->GetDistance(&owner); - if(cur_dist < cur_dist_xyz) - { - i_caster_x = fright->GetPositionX(); - i_caster_y = fright->GetPositionY(); - i_caster_z = fright->GetPositionZ(); - angle_to_caster = fright->GetAngle(&owner); - } - else - { - cur_dist = cur_dist_xyz; - angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + M_PI; - } - } - else - { - cur_dist = cur_dist_xyz; - angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + M_PI; - } - - // if we too close may use 'path-finding' else just stop - i_only_forward = cur_dist >= MIN_QUIET_DISTANCE/3; - - //get angle and 'distance from caster' to run - float angle; - - if(i_cur_angle == 0.0f && i_last_distance_from_caster == 0.0f) //just started, first time - { - angle = rand_norm()*(1.0f - cur_dist/MIN_QUIET_DISTANCE) * M_PI/3 + rand_norm()*M_PI*2/3; - i_to_distance_from_caster = MIN_QUIET_DISTANCE; - i_only_forward = true; - } - else if(cur_dist < MIN_QUIET_DISTANCE) - { - angle = M_PI/6 + rand_norm()*M_PI*2/3; - i_to_distance_from_caster = cur_dist*2/3 + rand_norm()*(MIN_QUIET_DISTANCE - cur_dist*2/3); - } - else if(cur_dist > MAX_QUIET_DISTANCE) - { - angle = rand_norm()*M_PI/3 + M_PI*2/3; - i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f); - } - else - { - angle = rand_norm()*M_PI; - i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f); - } - - int8 sign = rand_norm() > 0.5f ? 1 : -1; - i_cur_angle = sign*angle + angle_to_caster; - - // current distance - i_last_distance_from_caster = cur_dist; - - return true; -} - -template<class T> -void -FleeingMovementGenerator<T>::Initialize(T &owner) -{ - if(!&owner) - return; - - Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID); - if(!fright) - return; - - _Init(owner); - owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); - i_caster_x = fright->GetPositionX(); - i_caster_y = fright->GetPositionY(); - i_caster_z = fright->GetPositionZ(); - i_only_forward = true; - i_cur_angle = 0.0f; - i_last_distance_from_caster = 0.0f; - i_to_distance_from_caster = 0.0f; - _setTargetLocation(owner); -} - -template<> -void -FleeingMovementGenerator<Creature>::_Init(Creature &owner) -{ - if(!&owner) - return; - owner.SetUInt64Value(UNIT_FIELD_TARGET, 0); - is_water_ok = owner.canSwim(); - is_land_ok = owner.canWalk(); -} - -template<> -void -FleeingMovementGenerator<Player>::_Init(Player &) -{ - is_water_ok = true; - is_land_ok = true; -} - -template<class T> -void -FleeingMovementGenerator<T>::Finalize(T &owner) -{ - owner.clearUnitState(UNIT_STAT_FLEEING); -} - -template<class T> -void -FleeingMovementGenerator<T>::Reset(T &owner) -{ - Initialize(owner); -} - -template<class T> -bool -FleeingMovementGenerator<T>::Update(T &owner, const uint32 & time_diff) -{ - if( !&owner || !owner.isAlive() ) - return false; - if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED) ) - return true; - - Traveller<T> traveller(owner); - - i_nextCheckTime.Update(time_diff); - - if( (owner.IsStopped() && !i_destinationHolder.HasArrived()) || !i_destinationHolder.HasDestination() ) - { - _setTargetLocation(owner); - return true; - } - - if (i_destinationHolder.UpdateTraveller(traveller, time_diff, false)) - { - i_destinationHolder.ResetUpdate(50); - if(i_nextCheckTime.Passed() && i_destinationHolder.HasArrived()) - { - _setTargetLocation(owner); - return true; - } - } - return true; -} - -template void FleeingMovementGenerator<Player>::Initialize(Player &); -template void FleeingMovementGenerator<Creature>::Initialize(Creature &); -template bool FleeingMovementGenerator<Player>::_setMoveData(Player &); -template bool FleeingMovementGenerator<Creature>::_setMoveData(Creature &); -template bool FleeingMovementGenerator<Player>::_getPoint(Player &, float &, float &, float &); -template bool FleeingMovementGenerator<Creature>::_getPoint(Creature &, float &, float &, float &); -template void FleeingMovementGenerator<Player>::_setTargetLocation(Player &); -template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature &); -template void FleeingMovementGenerator<Player>::Finalize(Player &); -template void FleeingMovementGenerator<Creature>::Finalize(Creature &); -template void FleeingMovementGenerator<Player>::Reset(Player &); -template void FleeingMovementGenerator<Creature>::Reset(Creature &); -template bool FleeingMovementGenerator<Player>::Update(Player &, const uint32 &); -template bool FleeingMovementGenerator<Creature>::Update(Creature &, const uint32 &); +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Creature.h"
+#include "MapManager.h"
+#include "FleeingMovementGenerator.h"
+#include "DestinationHolderImp.h"
+#include "ObjectAccessor.h"
+
+#define MIN_QUIET_DISTANCE 28.0f
+#define MAX_QUIET_DISTANCE 43.0f
+
+template<class T>
+void
+FleeingMovementGenerator<T>::_setTargetLocation(T &owner)
+{
+ if( !&owner )
+ return;
+
+ if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED) )
+ return;
+
+ if(!_setMoveData(owner))
+ return;
+
+ float x, y, z;
+ if(!_getPoint(owner, x, y, z))
+ return;
+
+ owner.addUnitState(UNIT_STAT_FLEEING);
+ Traveller<T> traveller(owner);
+ i_destinationHolder.SetDestination(traveller, x, y, z);
+}
+
+template<class T>
+bool
+FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float &z)
+{
+ if(!&owner)
+ return false;
+
+ x = owner.GetPositionX();
+ y = owner.GetPositionY();
+ z = owner.GetPositionZ();
+
+ float temp_x, temp_y, angle;
+ const Map * _map = MapManager::Instance().GetBaseMap(owner.GetMapId());
+ //primitive path-finding
+ for(uint8 i = 0; i < 18; i++)
+ {
+ if(i_only_forward && i > 2)
+ break;
+
+ float distance = 5.0f;
+
+ switch(i)
+ {
+ case 0:
+ angle = i_cur_angle;
+ break;
+ case 1:
+ angle = i_cur_angle;
+ distance /= 2;
+ break;
+ case 2:
+ angle = i_cur_angle;
+ distance /= 4;
+ break;
+ case 3:
+ angle = i_cur_angle + M_PI/4.0f;
+ break;
+ case 4:
+ angle = i_cur_angle - M_PI/4.0f;
+ break;
+ case 5:
+ angle = i_cur_angle + M_PI/4.0f;
+ distance /= 2;
+ break;
+ case 6:
+ angle = i_cur_angle - M_PI/4.0f;
+ distance /= 2;
+ break;
+ case 7:
+ angle = i_cur_angle + M_PI/2.0f;
+ break;
+ case 8:
+ angle = i_cur_angle - M_PI/2.0f;
+ break;
+ case 9:
+ angle = i_cur_angle + M_PI/2.0f;
+ distance /= 2;
+ break;
+ case 10:
+ angle = i_cur_angle - M_PI/2.0f;
+ distance /= 2;
+ break;
+ case 11:
+ angle = i_cur_angle + M_PI/4.0f;
+ distance /= 4;
+ break;
+ case 12:
+ angle = i_cur_angle - M_PI/4.0f;
+ distance /= 4;
+ break;
+ case 13:
+ angle = i_cur_angle + M_PI/2.0f;
+ distance /= 4;
+ break;
+ case 14:
+ angle = i_cur_angle - M_PI/2.0f;
+ distance /= 4;
+ break;
+ case 15:
+ angle = i_cur_angle + M_PI*3/4.0f;
+ distance /= 2;
+ break;
+ case 16:
+ angle = i_cur_angle - M_PI*3/4.0f;
+ distance /= 2;
+ break;
+ case 17:
+ angle = i_cur_angle + M_PI;
+ distance /= 2;
+ break;
+ }
+ temp_x = x + distance * cos(angle);
+ temp_y = y + distance * sin(angle);
+ MaNGOS::NormalizeMapCoord(temp_x);
+ MaNGOS::NormalizeMapCoord(temp_y);
+ if( owner.IsWithinLOS(temp_x,temp_y,z))
+ {
+ bool is_water_now = _map->IsInWater(x,y,z);
+
+ if(is_water_now && _map->IsInWater(temp_x,temp_y,z))
+ {
+ x = temp_x;
+ y = temp_y;
+ return true;
+ }
+ float new_z = _map->GetHeight(temp_x,temp_y,z,true);
+
+ if(new_z <= INVALID_HEIGHT)
+ continue;
+
+ bool is_water_next = _map->IsInWater(temp_x,temp_y,new_z);
+
+ if((is_water_now && !is_water_next && !is_land_ok) || (!is_water_now && is_water_next && !is_water_ok))
+ continue;
+
+ if( !(new_z - z) || distance / fabs(new_z - z) > 1.0f)
+ {
+ float new_z_left = _map->GetHeight(temp_x + 1.0f*cos(angle+M_PI/2),temp_y + 1.0f*sin(angle+M_PI/2),z,true);
+ float new_z_right = _map->GetHeight(temp_x + 1.0f*cos(angle-M_PI/2),temp_y + 1.0f*sin(angle-M_PI/2),z,true);
+ if(fabs(new_z_left - new_z) < 1.2f && fabs(new_z_right - new_z) < 1.2f)
+ {
+ x = temp_x;
+ y = temp_y;
+ z = new_z;
+ return true;
+ }
+ }
+ }
+ }
+ i_to_distance_from_caster = 0.0f;
+ i_nextCheckTime.Reset( urand(500,1000) );
+ return false;
+}
+
+template<class T>
+bool
+FleeingMovementGenerator<T>::_setMoveData(T &owner)
+{
+ float cur_dist_xyz = owner.GetDistance(i_caster_x, i_caster_y, i_caster_z);
+
+ if(i_to_distance_from_caster > 0.0f)
+ {
+ if((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) ||
+ // if we reach lower distance
+ (i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz > i_last_distance_from_caster) ||
+ // if we can't be close
+ (i_last_distance_from_caster < i_to_distance_from_caster && cur_dist_xyz > i_to_distance_from_caster) ||
+ // if we reach bigger distance
+ (cur_dist_xyz > MAX_QUIET_DISTANCE) || // if we are too far
+ (i_last_distance_from_caster > MIN_QUIET_DISTANCE && cur_dist_xyz < MIN_QUIET_DISTANCE) )
+ // if we leave 'quiet zone'
+ {
+ // we are very far or too close, stopping
+ i_to_distance_from_caster = 0.0f;
+ i_nextCheckTime.Reset( urand(500,1000) );
+ return false;
+ }
+ else
+ {
+ // now we are running, continue
+ i_last_distance_from_caster = cur_dist_xyz;
+ return true;
+ }
+ }
+
+ float cur_dist;
+ float angle_to_caster;
+
+ Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID);
+
+ if(fright)
+ {
+ cur_dist = fright->GetDistance(&owner);
+ if(cur_dist < cur_dist_xyz)
+ {
+ i_caster_x = fright->GetPositionX();
+ i_caster_y = fright->GetPositionY();
+ i_caster_z = fright->GetPositionZ();
+ angle_to_caster = fright->GetAngle(&owner);
+ }
+ else
+ {
+ cur_dist = cur_dist_xyz;
+ angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + M_PI;
+ }
+ }
+ else
+ {
+ cur_dist = cur_dist_xyz;
+ angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + M_PI;
+ }
+
+ // if we too close may use 'path-finding' else just stop
+ i_only_forward = cur_dist >= MIN_QUIET_DISTANCE/3;
+
+ //get angle and 'distance from caster' to run
+ float angle;
+
+ if(i_cur_angle == 0.0f && i_last_distance_from_caster == 0.0f) //just started, first time
+ {
+ angle = rand_norm()*(1.0f - cur_dist/MIN_QUIET_DISTANCE) * M_PI/3 + rand_norm()*M_PI*2/3;
+ i_to_distance_from_caster = MIN_QUIET_DISTANCE;
+ i_only_forward = true;
+ }
+ else if(cur_dist < MIN_QUIET_DISTANCE)
+ {
+ angle = M_PI/6 + rand_norm()*M_PI*2/3;
+ i_to_distance_from_caster = cur_dist*2/3 + rand_norm()*(MIN_QUIET_DISTANCE - cur_dist*2/3);
+ }
+ else if(cur_dist > MAX_QUIET_DISTANCE)
+ {
+ angle = rand_norm()*M_PI/3 + M_PI*2/3;
+ i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
+ }
+ else
+ {
+ angle = rand_norm()*M_PI;
+ i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
+ }
+
+ int8 sign = rand_norm() > 0.5f ? 1 : -1;
+ i_cur_angle = sign*angle + angle_to_caster;
+
+ // current distance
+ i_last_distance_from_caster = cur_dist;
+
+ return true;
+}
+
+template<class T>
+void
+FleeingMovementGenerator<T>::Initialize(T &owner)
+{
+ if(!&owner)
+ return;
+
+ Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID);
+ if(!fright)
+ return;
+
+ _Init(owner);
+ owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ i_caster_x = fright->GetPositionX();
+ i_caster_y = fright->GetPositionY();
+ i_caster_z = fright->GetPositionZ();
+ i_only_forward = true;
+ i_cur_angle = 0.0f;
+ i_last_distance_from_caster = 0.0f;
+ i_to_distance_from_caster = 0.0f;
+ _setTargetLocation(owner);
+}
+
+template<>
+void
+FleeingMovementGenerator<Creature>::_Init(Creature &owner)
+{
+ if(!&owner)
+ return;
+ owner.SetUInt64Value(UNIT_FIELD_TARGET, 0);
+ is_water_ok = owner.canSwim();
+ is_land_ok = owner.canWalk();
+}
+
+template<>
+void
+FleeingMovementGenerator<Player>::_Init(Player &)
+{
+ is_water_ok = true;
+ is_land_ok = true;
+}
+
+template<class T>
+void
+FleeingMovementGenerator<T>::Finalize(T &owner)
+{
+ owner.clearUnitState(UNIT_STAT_FLEEING);
+}
+
+template<class T>
+void
+FleeingMovementGenerator<T>::Reset(T &owner)
+{
+ Initialize(owner);
+}
+
+template<class T>
+bool
+FleeingMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
+{
+ if( !&owner || !owner.isAlive() )
+ return false;
+ if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED) )
+ return true;
+
+ Traveller<T> traveller(owner);
+
+ i_nextCheckTime.Update(time_diff);
+
+ if( (owner.IsStopped() && !i_destinationHolder.HasArrived()) || !i_destinationHolder.HasDestination() )
+ {
+ _setTargetLocation(owner);
+ return true;
+ }
+
+ if (i_destinationHolder.UpdateTraveller(traveller, time_diff, false))
+ {
+ i_destinationHolder.ResetUpdate(50);
+ if(i_nextCheckTime.Passed() && i_destinationHolder.HasArrived())
+ {
+ _setTargetLocation(owner);
+ return true;
+ }
+ }
+ return true;
+}
+
+template void FleeingMovementGenerator<Player>::Initialize(Player &);
+template void FleeingMovementGenerator<Creature>::Initialize(Creature &);
+template bool FleeingMovementGenerator<Player>::_setMoveData(Player &);
+template bool FleeingMovementGenerator<Creature>::_setMoveData(Creature &);
+template bool FleeingMovementGenerator<Player>::_getPoint(Player &, float &, float &, float &);
+template bool FleeingMovementGenerator<Creature>::_getPoint(Creature &, float &, float &, float &);
+template void FleeingMovementGenerator<Player>::_setTargetLocation(Player &);
+template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature &);
+template void FleeingMovementGenerator<Player>::Finalize(Player &);
+template void FleeingMovementGenerator<Creature>::Finalize(Creature &);
+template void FleeingMovementGenerator<Player>::Reset(Player &);
+template void FleeingMovementGenerator<Creature>::Reset(Creature &);
+template bool FleeingMovementGenerator<Player>::Update(Player &, const uint32 &);
+template bool FleeingMovementGenerator<Creature>::Update(Creature &, const uint32 &);
diff --git a/src/game/GridNotifiers.h b/src/game/GridNotifiers.h index 70d5d7c8928..8eb69b4f6b6 100644 --- a/src/game/GridNotifiers.h +++ b/src/game/GridNotifiers.h @@ -852,7 +852,7 @@ namespace MaNGOS bool operator()(Unit* u)
{
if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) &&
- (u->isFeared() || u->isCharmed() || u->isFrozen() || u->hasUnitState(UNIT_STAT_STUNDED) || u->hasUnitState(UNIT_STAT_STUNDED) || u->hasUnitState(UNIT_STAT_CONFUSED)))
+ (u->isFeared() || u->isCharmed() || u->isFrozen() || u->hasUnitState(UNIT_STAT_STUNNED) || u->hasUnitState(UNIT_STAT_CONFUSED)))
{
return true;
}
diff --git a/src/game/Group.cpp b/src/game/Group.cpp index 1d4850ca82d..273edc135b9 100644 --- a/src/game/Group.cpp +++ b/src/game/Group.cpp @@ -1,1454 +1,1454 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "Common.h" -#include "Opcodes.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Player.h" -#include "World.h" -#include "ObjectMgr.h" -#include "Group.h" -#include "ObjectAccessor.h" -#include "BattleGround.h" -#include "MapManager.h" -#include "InstanceSaveMgr.h" -#include "MapInstanced.h" -#include "Util.h" - -Group::Group() -{ - m_leaderGuid = 0; - m_mainTank = 0; - m_mainAssistant = 0; - m_groupType = (GroupType)0; - m_bgGroup = NULL; - m_lootMethod = (LootMethod)0; - m_looterGuid = 0; - m_lootThreshold = ITEM_QUALITY_UNCOMMON; - - for(int i=0; i<TARGETICONCOUNT; i++) - m_targetIcons[i] = 0; -} - -Group::~Group() -{ - if(m_bgGroup) - { - sLog.outDebug("Group::~Group: battleground group being deleted."); - if(m_bgGroup->GetBgRaid(ALLIANCE) == this) m_bgGroup->SetBgRaid(ALLIANCE, NULL); - else if(m_bgGroup->GetBgRaid(HORDE) == this) m_bgGroup->SetBgRaid(HORDE, NULL); - else sLog.outError("Group::~Group: battleground group is not linked to the correct battleground."); - } - Rolls::iterator itr; - while(!RollId.empty()) - { - itr = RollId.begin(); - Roll *r = *itr; - RollId.erase(itr); - delete(r); - } - - // it is undefined whether objectmgr (which stores the groups) or instancesavemgr - // will be unloaded first so we must be prepared for both cases - // this may unload some instance saves - for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++) - for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) - itr->second.save->RemoveGroup(this); -} - -bool Group::Create(const uint64 &guid, const char * name) -{ - m_leaderGuid = guid; - m_leaderName = name; - - m_groupType = isBGGroup() ? GROUPTYPE_RAID : GROUPTYPE_NORMAL; - m_lootMethod = GROUP_LOOT; - m_lootThreshold = ITEM_QUALITY_UNCOMMON; - m_looterGuid = guid; - - m_difficulty = DIFFICULTY_NORMAL; - if(!isBGGroup()) - { - Player *leader = objmgr.GetPlayer(guid); - if(leader) m_difficulty = leader->GetDifficulty(); - - Player::ConvertInstancesToGroup(leader, this, guid); - - // store group in database - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM groups WHERE leaderGuid ='%u'", GUID_LOPART(m_leaderGuid)); - CharacterDatabase.PExecute("DELETE FROM group_member WHERE leaderGuid ='%u'", GUID_LOPART(m_leaderGuid)); - CharacterDatabase.PExecute("INSERT INTO groups(leaderGuid,mainTank,mainAssistant,lootMethod,looterGuid,lootThreshold,icon1,icon2,icon3,icon4,icon5,icon6,icon7,icon8,isRaid,difficulty) " - "VALUES('%u','%u','%u','%u','%u','%u','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','%u','%u')", - GUID_LOPART(m_leaderGuid), GUID_LOPART(m_mainTank), GUID_LOPART(m_mainAssistant), uint32(m_lootMethod), - GUID_LOPART(m_looterGuid), uint32(m_lootThreshold), m_targetIcons[0], m_targetIcons[1], m_targetIcons[2], m_targetIcons[3], m_targetIcons[4], m_targetIcons[5], m_targetIcons[6], m_targetIcons[7], isRaidGroup(), m_difficulty); - } - - if(!AddMember(guid, name)) - return false; - - if(!isBGGroup()) CharacterDatabase.CommitTransaction(); - - return true; -} - -bool Group::LoadGroupFromDB(const uint64 &leaderGuid, QueryResult *result, bool loadMembers) -{ - if(isBGGroup()) - return false; - - bool external = true; - if(!result) - { - external = false; - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - result = CharacterDatabase.PQuery("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, isRaid, difficulty FROM groups WHERE leaderGuid ='%u'", GUID_LOPART(leaderGuid)); - if(!result) - return false; - } - - m_leaderGuid = leaderGuid; - - // group leader not exist - if(!objmgr.GetPlayerNameByGUID(m_leaderGuid, m_leaderName)) - { - if(!external) delete result; - return false; - } - - m_groupType = (*result)[13].GetBool() ? GROUPTYPE_RAID : GROUPTYPE_NORMAL; - m_difficulty = (*result)[14].GetUInt8(); - m_mainTank = (*result)[0].GetUInt64(); - m_mainAssistant = (*result)[1].GetUInt64(); - m_lootMethod = (LootMethod)(*result)[2].GetUInt8(); - m_looterGuid = MAKE_NEW_GUID((*result)[3].GetUInt32(), 0, HIGHGUID_PLAYER); - m_lootThreshold = (ItemQualities)(*result)[4].GetUInt16(); - - for(int i=0; i<TARGETICONCOUNT; i++) - m_targetIcons[i] = (*result)[5+i].GetUInt64(); - if(!external) delete result; - - if(loadMembers) - { - result = CharacterDatabase.PQuery("SELECT memberGuid, assistant, subgroup FROM group_member WHERE leaderGuid ='%u'", GUID_LOPART(leaderGuid)); - if(!result) - return false; - - do - { - LoadMemberFromDB((*result)[0].GetUInt32(), (*result)[2].GetUInt8(), (*result)[1].GetBool()); - } while( result->NextRow() ); - delete result; - // group too small - if(GetMembersCount() < 2) - return false; - } - - return true; -} - -bool Group::LoadMemberFromDB(uint32 guidLow, uint8 subgroup, bool assistant) -{ - MemberSlot member; - member.guid = MAKE_NEW_GUID(guidLow, 0, HIGHGUID_PLAYER); - - // skip non-existed member - if(!objmgr.GetPlayerNameByGUID(member.guid, member.name)) - return false; - - member.group = subgroup; - member.assistant = assistant; - m_memberSlots.push_back(member); - return true; -} - -bool Group::AddInvite(Player *player) -{ - if(!player || player->GetGroupInvite() || player->GetGroup()) - return false; - - RemoveInvite(player); - - m_invitees.insert(player->GetGUID()); - - player->SetGroupInvite(this); - - return true; -} - -bool Group::AddLeaderInvite(Player *player) -{ - if(!AddInvite(player)) - return false; - - m_leaderGuid = player->GetGUID(); - m_leaderName = player->GetName(); - return true; -} - -uint32 Group::RemoveInvite(Player *player) -{ - for(InvitesList::iterator itr=m_invitees.begin(); itr!=m_invitees.end(); ++itr) - { - if((*itr) == player->GetGUID()) - { - m_invitees.erase(itr); - break; - } - } - - player->SetGroupInvite(NULL); - return GetMembersCount(); -} - -void Group::RemoveAllInvites() -{ - for(InvitesList::iterator itr=m_invitees.begin(); itr!=m_invitees.end(); ++itr) - { - Player *invitee = objmgr.GetPlayer(*itr); - if(invitee) - invitee->SetGroupInvite(NULL); - } - m_invitees.clear(); -} - -bool Group::AddMember(const uint64 &guid, const char* name) -{ - if(!_addMember(guid, name)) - return false; - SendUpdate(); - - Player *player = objmgr.GetPlayer(guid); - if(player) - { - if(!IsLeader(player->GetGUID()) && !isBGGroup()) - { - // reset the new member's instances, unless he is currently in one of them - // including raid/heroic instances that they are not permanently bound to! - player->ResetInstances(INSTANCE_RESET_GROUP_JOIN); - - if(player->getLevel() >= LEVELREQUIREMENT_HEROIC && player->GetDifficulty() != GetDifficulty() ) - { - player->SetDifficulty(m_difficulty); - player->SendDungeonDifficulty(true); - } - } - player->SetGroupUpdateFlag(GROUP_UPDATE_FULL); - UpdatePlayerOutOfRange(player); - } - - return true; -} - -uint32 Group::RemoveMember(const uint64 &guid, const uint8 &method) -{ - // remove member and change leader (if need) only if strong more 2 members _before_ member remove - if(GetMembersCount() > (isBGGroup() ? 1 : 2)) // in BG group case allow 1 members group - { - bool leaderChanged = _removeMember(guid); - - Player *player = objmgr.GetPlayer( guid ); - if (player) - { - WorldPacket data; - - if(method == 1) - { - data.Initialize( SMSG_GROUP_UNINVITE, 0 ); - player->GetSession()->SendPacket( &data ); - } - - data.Initialize(SMSG_GROUP_LIST, 24); - data << uint64(0) << uint64(0) << uint64(0); - player->GetSession()->SendPacket(&data); - - _homebindIfInstance(player); - } - - if(leaderChanged) - { - WorldPacket data(SMSG_GROUP_SET_LEADER, (m_memberSlots.front().name.size()+1)); - data << m_memberSlots.front().name; - BroadcastPacket(&data); - } - - SendUpdate(); - } - // if group before remove <= 2 disband it - else - Disband(true); - - return m_memberSlots.size(); -} - -void Group::ChangeLeader(const uint64 &guid) -{ - member_citerator slot = _getMemberCSlot(guid); - - if(slot==m_memberSlots.end()) - return; - - _setLeader(guid); - - WorldPacket data(SMSG_GROUP_SET_LEADER, slot->name.size()+1); - data << slot->name; - BroadcastPacket(&data); - SendUpdate(); -} - -void Group::Disband(bool hideDestroy) -{ - Player *player; - - for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr) - { - player = objmgr.GetPlayer(citr->guid); - if(!player) - continue; - - player->SetGroup(NULL); - - if(!player->GetSession()) - continue; - - WorldPacket data; - if(!hideDestroy) - { - data.Initialize(SMSG_GROUP_DESTROYED, 0); - player->GetSession()->SendPacket(&data); - } - - data.Initialize(SMSG_GROUP_LIST, 24); - data << uint64(0) << uint64(0) << uint64(0); - player->GetSession()->SendPacket(&data); - - _homebindIfInstance(player); - } - RollId.clear(); - m_memberSlots.clear(); - - RemoveAllInvites(); - - if(!isBGGroup()) - { - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM groups WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid)); - CharacterDatabase.PExecute("DELETE FROM group_member WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid)); - CharacterDatabase.CommitTransaction(); - ResetInstances(INSTANCE_RESET_GROUP_DISBAND, NULL); - } - - m_leaderGuid = 0; - m_leaderName = ""; -} - -/*********************************************************/ -/*** LOOT SYSTEM ***/ -/*********************************************************/ - -void Group::SendLootStartRoll(uint32 CountDown, const Roll &r) -{ - WorldPacket data(SMSG_LOOT_START_ROLL, (8+4+4+4+4+4)); - data << uint64(r.itemGUID); // guid of rolled item - data << uint32(r.totalPlayersRolling); // maybe the number of players rolling for it??? - data << uint32(r.itemid); // the itemEntryId for the item that shall be rolled for - data << uint32(r.itemRandomSuffix); // randomSuffix - data << uint32(r.itemRandomPropId); // item random property ID - data << uint32(CountDown); // the countdown time to choose "need" or "greed" - - for (Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr) - { - Player *p = objmgr.GetPlayer(itr->first); - if(!p || !p->GetSession()) - continue; - - if(itr->second != NOT_VALID) - p->GetSession()->SendPacket( &data ); - } -} - -void Group::SendLootRoll(uint64 SourceGuid, uint64 TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r) -{ - WorldPacket data(SMSG_LOOT_ROLL, (8+4+8+4+4+4+1+1)); - data << uint64(SourceGuid); // guid of the item rolled - data << uint32(0); // unknown, maybe amount of players - data << uint64(TargetGuid); - data << uint32(r.itemid); // the itemEntryId for the item that shall be rolled for - data << uint32(r.itemRandomSuffix); // randomSuffix - data << uint32(r.itemRandomPropId); // Item random property ID - data << uint8(RollNumber); // 0: "Need for: [item name]" > 127: "you passed on: [item name]" Roll number - data << uint8(RollType); // 0: "Need for: [item name]" 0: "You have selected need for [item name] 1: need roll 2: greed roll - data << uint8(0); // 2.4.0 - - for( Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr) - { - Player *p = objmgr.GetPlayer(itr->first); - if(!p || !p->GetSession()) - continue; - - if(itr->second != NOT_VALID) - p->GetSession()->SendPacket( &data ); - } -} - -void Group::SendLootRollWon(uint64 SourceGuid, uint64 TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r) -{ - WorldPacket data(SMSG_LOOT_ROLL_WON, (8+4+4+4+4+8+1+1)); - data << uint64(SourceGuid); // guid of the item rolled - data << uint32(0); // unknown, maybe amount of players - data << uint32(r.itemid); // the itemEntryId for the item that shall be rolled for - data << uint32(r.itemRandomSuffix); // randomSuffix - data << uint32(r.itemRandomPropId); // Item random property - data << uint64(TargetGuid); // guid of the player who won. - data << uint8(RollNumber); // rollnumber realted to SMSG_LOOT_ROLL - data << uint8(RollType); // Rolltype related to SMSG_LOOT_ROLL - - for( Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr) - { - Player *p = objmgr.GetPlayer(itr->first); - if(!p || !p->GetSession()) - continue; - - if(itr->second != NOT_VALID) - p->GetSession()->SendPacket( &data ); - } -} - -void Group::SendLootAllPassed(uint32 NumberOfPlayers, const Roll &r) -{ - WorldPacket data(SMSG_LOOT_ALL_PASSED, (8+4+4+4+4)); - data << uint64(r.itemGUID); // Guid of the item rolled - data << uint32(NumberOfPlayers); // The number of players rolling for it??? - data << uint32(r.itemid); // The itemEntryId for the item that shall be rolled for - data << uint32(r.itemRandomPropId); // Item random property ID - data << uint32(r.itemRandomSuffix); // Item random suffix ID - - for( Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr) - { - Player *p = objmgr.GetPlayer(itr->first); - if(!p || !p->GetSession()) - continue; - - if(itr->second != NOT_VALID) - p->GetSession()->SendPacket( &data ); - } -} - -void Group::GroupLoot(uint64 playerGUID, Loot *loot, Creature *creature) -{ - std::vector<LootItem>::iterator i; - ItemPrototype const *item; - uint8 itemSlot = 0; - Player *player = objmgr.GetPlayer(playerGUID); - Group *group = player->GetGroup(); - - for (i=loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot) - { - item = objmgr.GetItemPrototype(i->itemid); - if (!item) - { - //sLog.outDebug("Group::GroupLoot: missing item prototype for item with id: %d", i->itemid); - continue; - } - - //roll for over-threshold item if it's one-player loot - if (item->Quality >= uint32(m_lootThreshold) && !i->freeforall) - { - uint64 newitemGUID = MAKE_NEW_GUID(objmgr.GenerateLowGuid(HIGHGUID_ITEM),0,HIGHGUID_ITEM); - Roll* r=new Roll(newitemGUID,*i); - - //a vector is filled with only near party members - for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *member = itr->getSource(); - if(!member || !member->GetSession()) - continue; - if ( i->AllowedForPlayer(member) ) - { - if (member->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE)) - { - r->playerVote[member->GetGUID()] = NOT_EMITED_YET; - ++r->totalPlayersRolling; - } - } - } - - r->setLoot(loot); - r->itemSlot = itemSlot; - - group->SendLootStartRoll(60000, *r); - - loot->items[itemSlot].is_blocked = true; - creature->m_groupLootTimer = 60000; - creature->lootingGroupLeaderGUID = GetLeaderGUID(); - - RollId.push_back(r); - } - else - i->is_underthreshold=1; - - } -} - -void Group::NeedBeforeGreed(uint64 playerGUID, Loot *loot, Creature *creature) -{ - ItemPrototype const *item; - Player *player = objmgr.GetPlayer(playerGUID); - Group *group = player->GetGroup(); - - uint8 itemSlot = 0; - for(std::vector<LootItem>::iterator i=loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot) - { - item = objmgr.GetItemPrototype(i->itemid); - - //only roll for one-player items, not for ones everyone can get - if (item->Quality >= uint32(m_lootThreshold) && !i->freeforall) - { - uint64 newitemGUID = MAKE_NEW_GUID(objmgr.GenerateLowGuid(HIGHGUID_ITEM),0,HIGHGUID_ITEM); - Roll* r=new Roll(newitemGUID,*i); - - for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *playerToRoll = itr->getSource(); - if(!playerToRoll || !playerToRoll->GetSession()) - continue; - - if (playerToRoll->CanUseItem(item) && i->AllowedForPlayer(playerToRoll) ) - { - if (playerToRoll->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE)) - { - r->playerVote[playerToRoll->GetGUID()] = NOT_EMITED_YET; - ++r->totalPlayersRolling; - } - } - } - - if (r->totalPlayersRolling > 0) - { - r->setLoot(loot); - r->itemSlot = itemSlot; - - group->SendLootStartRoll(60000, *r); - - loot->items[itemSlot].is_blocked = true; - - RollId.push_back(r); - } - else - { - delete r; - } - } - else - i->is_underthreshold=1; - } -} - -void Group::MasterLoot(uint64 playerGUID, Loot* /*loot*/, Creature *creature) -{ - Player *player = objmgr.GetPlayer(playerGUID); - if(!player) - return; - - sLog.outDebug("Group::MasterLoot (SMSG_LOOT_MASTER_LIST, 330) player = [%s].", player->GetName()); - - uint32 real_count = 0; - - WorldPacket data(SMSG_LOOT_MASTER_LIST, 330); - data << (uint8)GetMembersCount(); - - for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *looter = itr->getSource(); - if (!looter->IsInWorld()) - continue; - - if (looter->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE)) - { - data << looter->GetGUID(); - ++real_count; - } - } - - data.put<uint8>(0,real_count); - - for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *looter = itr->getSource(); - if (looter->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE)) - looter->GetSession()->SendPacket(&data); - } -} - -void Group::CountRollVote(uint64 playerGUID, uint64 Guid, uint32 NumberOfPlayers, uint8 Choise) -{ - Rolls::iterator rollI = GetRoll(Guid); - if (rollI == RollId.end()) - return; - Roll* roll = *rollI; - - Roll::PlayerVote::iterator itr = roll->playerVote.find(playerGUID); - // this condition means that player joins to the party after roll begins - if (itr == roll->playerVote.end()) - return; - - if (roll->getLoot()) - if (roll->getLoot()->items.empty()) - return; - - switch (Choise) - { - case 0: //Player choose pass - { - SendLootRoll(0, playerGUID, 128, 128, *roll); - ++roll->totalPass; - itr->second = PASS; - } - break; - case 1: //player choose Need - { - SendLootRoll(0, playerGUID, 1, 1, *roll); - ++roll->totalNeed; - itr->second = NEED; - } - break; - case 2: //player choose Greed - { - SendLootRoll(0, playerGUID, 2, 2, *roll); - ++roll->totalGreed; - itr->second = GREED; - } - break; - } - if (roll->totalPass + roll->totalGreed + roll->totalNeed >= roll->totalPlayersRolling) - { - CountTheRoll(rollI, NumberOfPlayers); - } -} - -//called when roll timer expires -void Group::EndRoll() -{ - Rolls::iterator itr; - while(!RollId.empty()) - { - //need more testing here, if rolls disappear - itr = RollId.begin(); - CountTheRoll(itr, GetMembersCount()); //i don't have to edit player votes, who didn't vote ... he will pass - } -} - -void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers) -{ - Roll* roll = *rollI; - if(!roll->isValid()) // is loot already deleted ? - { - RollId.erase(rollI); - delete roll; - return; - } - //end of the roll - if (roll->totalNeed > 0) - { - if(!roll->playerVote.empty()) - { - uint8 maxresul = 0; - uint64 maxguid = (*roll->playerVote.begin()).first; - Player *player; - - for( Roll::PlayerVote::const_iterator itr=roll->playerVote.begin(); itr!=roll->playerVote.end(); ++itr) - { - if (itr->second != NEED) - continue; - - uint8 randomN = urand(1, 99); - SendLootRoll(0, itr->first, randomN, 1, *roll); - if (maxresul < randomN) - { - maxguid = itr->first; - maxresul = randomN; - } - } - SendLootRollWon(0, maxguid, maxresul, 1, *roll); - player = objmgr.GetPlayer(maxguid); - - if(player && player->GetSession()) - { - ItemPosCountVec dest; - LootItem *item = &(roll->getLoot()->items[roll->itemSlot]); - uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count ); - if ( msg == EQUIP_ERR_OK ) - { - item->is_looted = true; - roll->getLoot()->NotifyItemRemoved(roll->itemSlot); - --roll->getLoot()->unlootedCount; - player->StoreNewItem( dest, roll->itemid, true, item->randomPropertyId); - } - else - { - item->is_blocked = false; - player->SendEquipError( msg, NULL, NULL ); - } - } - } - } - else if (roll->totalGreed > 0) - { - if(!roll->playerVote.empty()) - { - uint8 maxresul = 0; - uint64 maxguid = (*roll->playerVote.begin()).first; - Player *player; - - Roll::PlayerVote::iterator itr; - for (itr=roll->playerVote.begin(); itr!=roll->playerVote.end(); ++itr) - { - if (itr->second != GREED) - continue; - - uint8 randomN = urand(1, 99); - SendLootRoll(0, itr->first, randomN, 2, *roll); - if (maxresul < randomN) - { - maxguid = itr->first; - maxresul = randomN; - } - } - SendLootRollWon(0, maxguid, maxresul, 2, *roll); - player = objmgr.GetPlayer(maxguid); - - if(player && player->GetSession()) - { - ItemPosCountVec dest; - LootItem *item = &(roll->getLoot()->items[roll->itemSlot]); - uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count ); - if ( msg == EQUIP_ERR_OK ) - { - item->is_looted = true; - roll->getLoot()->NotifyItemRemoved(roll->itemSlot); - --roll->getLoot()->unlootedCount; - player->StoreNewItem( dest, roll->itemid, true, item->randomPropertyId); - } - else - { - item->is_blocked = false; - player->SendEquipError( msg, NULL, NULL ); - } - } - } - } - else - { - SendLootAllPassed(NumberOfPlayers, *roll); - LootItem *item = &(roll->getLoot()->items[roll->itemSlot]); - if(item) item->is_blocked = false; - } - RollId.erase(rollI); - delete roll; -} - -void Group::SetTargetIcon(uint8 id, uint64 guid) -{ - if(id >= TARGETICONCOUNT) - return; - - // clean other icons - if( guid != 0 ) - for(int i=0; i<TARGETICONCOUNT; i++) - if( m_targetIcons[i] == guid ) - SetTargetIcon(i, 0); - - m_targetIcons[id] = guid; - - WorldPacket data(MSG_RAID_TARGET_UPDATE, (2+8)); - data << (uint8)0; - data << id; - data << guid; - BroadcastPacket(&data); -} - -void Group::GetDataForXPAtKill(Unit const* victim, uint32& count,uint32& sum_level, Player* & member_with_max_level) -{ - for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player* member = itr->getSource(); - if(!member || !member->isAlive()) // only for alive - continue; - - if(!member->IsAtGroupRewardDistance(victim)) // at req. distance - continue; - - ++count; - sum_level += member->getLevel(); - if(!member_with_max_level || member_with_max_level->getLevel() < member->getLevel()) - member_with_max_level = member; - } -} - -void Group::SendTargetIconList(WorldSession *session) -{ - if(!session) - return; - - WorldPacket data(MSG_RAID_TARGET_UPDATE, (1+TARGETICONCOUNT*9)); - data << (uint8)1; - - for(int i=0; i<TARGETICONCOUNT; i++) - { - if(m_targetIcons[i] == 0) - continue; - - data << (uint8)i; - data << m_targetIcons[i]; - } - - session->SendPacket(&data); -} - -void Group::SendUpdate() -{ - Player *player; - - for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr) - { - player = objmgr.GetPlayer(citr->guid); - if(!player || !player->GetSession()) - continue; - // guess size - WorldPacket data(SMSG_GROUP_LIST, (1+1+1+1+8+4+GetMembersCount()*20)); - data << (uint8)m_groupType; // group type - data << (uint8)(isBGGroup() ? 1 : 0); // 2.0.x, isBattleGroundGroup? - data << (uint8)(citr->group); // groupid - data << (uint8)(citr->assistant?0x01:0); // 0x2 main assist, 0x4 main tank - data << uint64(0x50000000FFFFFFFELL); // related to voice chat? - data << uint32(GetMembersCount()-1); - for(member_citerator citr2 = m_memberSlots.begin(); citr2 != m_memberSlots.end(); ++citr2) - { - if(citr->guid == citr2->guid) - continue; - - data << citr2->name; - data << (uint64)citr2->guid; - // online-state - data << (uint8)(objmgr.GetPlayer(citr2->guid) ? 1 : 0); - data << (uint8)(citr2->group); // groupid - data << (uint8)(citr2->assistant?0x01:0); // 0x2 main assist, 0x4 main tank - } - - data << uint64(m_leaderGuid); // leader guid - if(GetMembersCount()-1) - { - data << (uint8)m_lootMethod; // loot method - data << (uint64)m_looterGuid; // looter guid - data << (uint8)m_lootThreshold; // loot threshold - data << (uint8)m_difficulty; // Heroic Mod Group - - } - player->GetSession()->SendPacket( &data ); - } -} - -void Group::UpdatePlayerOutOfRange(Player* pPlayer) -{ - if(!pPlayer) - return; - - Player *player; - WorldPacket data; - pPlayer->GetSession()->BuildPartyMemberStatsChangedPacket(pPlayer, &data); - - for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) - { - player = itr->getSource(); - if (player && player != pPlayer && !pPlayer->isVisibleFor(player)) - player->GetSession()->SendPacket(&data); - } -} - -void Group::BroadcastPacket(WorldPacket *packet, int group, uint64 ignore) -{ - for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *pl = itr->getSource(); - if(!pl || (ignore != 0 && pl->GetGUID() == ignore)) - continue; - - if (pl->GetSession() && (group==-1 || itr->getSubGroup()==group)) - pl->GetSession()->SendPacket(packet); - } -} - -void Group::BroadcastReadyCheck(WorldPacket *packet) -{ - for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *pl = itr->getSource(); - if(pl && pl->GetSession()) - if(IsLeader(pl->GetGUID()) || IsAssistant(pl->GetGUID())) - pl->GetSession()->SendPacket(packet); - } -} - -void Group::OfflineReadyCheck() -{ - for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr) - { - Player *pl = objmgr.GetPlayer(citr->guid); - if (!pl || !pl->GetSession()) - { - WorldPacket data(MSG_RAID_READY_CHECK_CONFIRM, 9); - data << citr->guid; - data << (uint8)0; - BroadcastReadyCheck(&data); - } - } -} - -bool Group::_addMember(const uint64 &guid, const char* name, bool isAssistant) -{ - // get first not-full group - uint8 groupid = 0; - std::vector<uint8> temp(MAXRAIDSIZE/MAXGROUPSIZE); - for(member_citerator itr = m_memberSlots.begin(); itr != m_memberSlots.end(); ++itr) - { - if (itr->group >= temp.size()) continue; - ++temp[itr->group]; - if(temp[groupid] >= MAXGROUPSIZE) - ++groupid; - } - - return _addMember(guid, name, isAssistant, groupid); -} - -bool Group::_addMember(const uint64 &guid, const char* name, bool isAssistant, uint8 group) -{ - if(IsFull()) - return false; - - if(!guid) - return false; - - Player *player = objmgr.GetPlayer(guid); - - MemberSlot member; - member.guid = guid; - member.name = name; - member.group = group; - member.assistant = isAssistant; - m_memberSlots.push_back(member); - - if(player) - { - player->SetGroupInvite(NULL); - player->SetGroup(this, group); - // if the same group invites the player back, cancel the homebind timer - InstanceGroupBind *bind = GetBoundInstance(player->GetMapId(), player->GetDifficulty()); - if(bind && bind->save->GetInstanceId() == player->GetInstanceId()) - player->m_InstanceValid = true; - } - - if(!isRaidGroup()) // reset targetIcons for non-raid-groups - { - for(int i=0; i<TARGETICONCOUNT; i++) - m_targetIcons[i] = 0; - } - - if(!isBGGroup()) - { - // insert into group table - CharacterDatabase.PExecute("INSERT INTO group_member(leaderGuid,memberGuid,assistant,subgroup) VALUES('%u','%u','%u','%u')", GUID_LOPART(m_leaderGuid), GUID_LOPART(member.guid), ((member.assistant==1)?1:0), member.group); - } - - return true; -} - -bool Group::_removeMember(const uint64 &guid) -{ - Player *player = objmgr.GetPlayer(guid); - if (player) - { - player->SetGroup(NULL); - } - - _removeRolls(guid); - - member_witerator slot = _getMemberWSlot(guid); - if (slot != m_memberSlots.end()) - m_memberSlots.erase(slot); - - if(!isBGGroup()) - CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid='%u'", GUID_LOPART(guid)); - - if(m_leaderGuid == guid) // leader was removed - { - if(GetMembersCount() > 0) - _setLeader(m_memberSlots.front().guid); - return true; - } - - return false; -} - -void Group::_setLeader(const uint64 &guid) -{ - member_citerator slot = _getMemberCSlot(guid); - if(slot==m_memberSlots.end()) - return; - - if(!isBGGroup()) - { - // TODO: set a time limit to have this function run rarely cause it can be slow - CharacterDatabase.BeginTransaction(); - - // update the group's bound instances when changing leaders - - // remove all permanent binds from the group - // in the DB also remove solo binds that will be replaced with permbinds - // from the new leader - CharacterDatabase.PExecute( - "DELETE FROM group_instance WHERE leaderguid='%u' AND (permanent = 1 OR " - "instance IN (SELECT instance FROM character_instance WHERE guid = '%u')" - ")", GUID_LOPART(m_leaderGuid), GUID_LOPART(slot->guid) - ); - - Player *player = objmgr.GetPlayer(slot->guid); - if(player) - { - for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++) - { - for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end();) - { - if(itr->second.perm) - { - itr->second.save->RemoveGroup(this); - m_boundInstances[i].erase(itr++); - } - else - ++itr; - } - } - } - - // update the group's solo binds to the new leader - CharacterDatabase.PExecute("UPDATE group_instance SET leaderGuid='%u' WHERE leaderGuid = '%u'", GUID_LOPART(slot->guid), GUID_LOPART(m_leaderGuid)); - - // copy the permanent binds from the new leader to the group - // overwriting the solo binds with permanent ones if necessary - // in the DB those have been deleted already - Player::ConvertInstancesToGroup(player, this, slot->guid); - - // update the group leader - CharacterDatabase.PExecute("UPDATE groups SET leaderGuid='%u' WHERE leaderGuid='%u'", GUID_LOPART(slot->guid), GUID_LOPART(m_leaderGuid)); - CharacterDatabase.PExecute("UPDATE group_member SET leaderGuid='%u' WHERE leaderGuid='%u'", GUID_LOPART(slot->guid), GUID_LOPART(m_leaderGuid)); - CharacterDatabase.CommitTransaction(); - } - - m_leaderGuid = slot->guid; - m_leaderName = slot->name; -} - -void Group::_removeRolls(const uint64 &guid) -{ - for (Rolls::iterator it = RollId.begin(); it < RollId.end(); it++) - { - Roll* roll = *it; - Roll::PlayerVote::iterator itr2 = roll->playerVote.find(guid); - if(itr2 == roll->playerVote.end()) - continue; - - if (itr2->second == GREED) --roll->totalGreed; - if (itr2->second == NEED) --roll->totalNeed; - if (itr2->second == PASS) --roll->totalPass; - if (itr2->second != NOT_VALID) --roll->totalPlayersRolling; - - roll->playerVote.erase(itr2); - - CountRollVote(guid, roll->itemGUID, GetMembersCount()-1, 3); - } -} - -void Group::_convertToRaid() -{ - m_groupType = GROUPTYPE_RAID; - - if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET isRaid = 1 WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid)); -} - -bool Group::_setMembersGroup(const uint64 &guid, const uint8 &group) -{ - member_witerator slot = _getMemberWSlot(guid); - if(slot==m_memberSlots.end()) - return false; - - slot->group = group; - if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE group_member SET subgroup='%u' WHERE memberGuid='%u'", group, GUID_LOPART(guid)); - return true; -} - -bool Group::_setAssistantFlag(const uint64 &guid, const bool &state) -{ - member_witerator slot = _getMemberWSlot(guid); - if(slot==m_memberSlots.end()) - return false; - - slot->assistant = state; - if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE group_member SET assistant='%u' WHERE memberGuid='%u'", (state==true)?1:0, GUID_LOPART(guid)); - return true; -} - -bool Group::_setMainTank(const uint64 &guid) -{ - member_citerator slot = _getMemberCSlot(guid); - if(slot==m_memberSlots.end()) - return false; - - if(m_mainAssistant == guid) - _setMainAssistant(0); - m_mainTank = guid; - if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET mainTank='%u' WHERE leaderGuid='%u'", GUID_LOPART(m_mainTank), GUID_LOPART(m_leaderGuid)); - return true; -} - -bool Group::_setMainAssistant(const uint64 &guid) -{ - member_witerator slot = _getMemberWSlot(guid); - if(slot==m_memberSlots.end()) - return false; - - if(m_mainTank == guid) - _setMainTank(0); - m_mainAssistant = guid; - if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET mainAssistant='%u' WHERE leaderGuid='%u'", GUID_LOPART(m_mainAssistant), GUID_LOPART(m_leaderGuid)); - return true; -} - -bool Group::SameSubGroup(Player const* member1, Player const* member2) const -{ - if(!member1 || !member2) return false; - if (member1->GetGroup() != this || member2->GetGroup() != this) return false; - else return member1->GetSubGroup() == member2->GetSubGroup(); -} - -// allows setting subgroup for offline members -void Group::ChangeMembersGroup(const uint64 &guid, const uint8 &group) -{ - if(!isRaidGroup()) - return; - Player *player = objmgr.GetPlayer(guid); - if (!player) - { - if(_setMembersGroup(guid, group)) - SendUpdate(); - } - else ChangeMembersGroup(player, group); -} - -// only for online members -void Group::ChangeMembersGroup(Player *player, const uint8 &group) -{ - if(!player || !isRaidGroup()) - return; - if(_setMembersGroup(player->GetGUID(), group)) - { - player->GetGroupRef().setSubGroup(group); - SendUpdate(); - } -} - -void Group::UpdateLooterGuid( Creature* creature, bool ifneed ) -{ - switch (GetLootMethod()) - { - case MASTER_LOOT: - case FREE_FOR_ALL: - return; - default: - // round robin style looting applies for all low - // quality items in each loot method except free for all and master loot - break; - } - - member_citerator guid_itr = _getMemberCSlot(GetLooterGuid()); - if(guid_itr != m_memberSlots.end()) - { - if(ifneed) - { - // not update if only update if need and ok - Player* looter = ObjectAccessor::FindPlayer(guid_itr->guid); - if(looter && looter->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE)) - return; - } - ++guid_itr; - } - - // search next after current - if(guid_itr != m_memberSlots.end()) - { - for(member_citerator itr = guid_itr; itr != m_memberSlots.end(); ++itr) - { - if(Player* pl = ObjectAccessor::FindPlayer(itr->guid)) - { - if (pl->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE)) - { - bool refresh = pl->GetLootGUID()==creature->GetGUID(); - - //if(refresh) // update loot for new looter - // pl->GetSession()->DoLootRelease(pl->GetLootGUID()); - SetLooterGuid(pl->GetGUID()); - SendUpdate(); - if(refresh) // update loot for new looter - pl->SendLoot(creature->GetGUID(),LOOT_CORPSE); - return; - } - } - } - } - - // search from start - for(member_citerator itr = m_memberSlots.begin(); itr != guid_itr; ++itr) - { - if(Player* pl = ObjectAccessor::FindPlayer(itr->guid)) - { - if (pl->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE)) - { - bool refresh = pl->GetLootGUID()==creature->GetGUID(); - - //if(refresh) // update loot for new looter - // pl->GetSession()->DoLootRelease(pl->GetLootGUID()); - SetLooterGuid(pl->GetGUID()); - SendUpdate(); - if(refresh) // update loot for new looter - pl->SendLoot(creature->GetGUID(),LOOT_CORPSE); - return; - } - } - } - - SetLooterGuid(0); - SendUpdate(); -} - -uint32 Group::CanJoinBattleGroundQueue(uint32 bgTypeId, uint32 bgQueueType, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot) -{ - // check for min / max count - uint32 memberscount = GetMembersCount(); - if(memberscount < MinPlayerCount) - return BG_JOIN_ERR_GROUP_NOT_ENOUGH; - if(memberscount > MaxPlayerCount) - return BG_JOIN_ERR_GROUP_TOO_MANY; - - // get a player as reference, to compare other players' stats to (arena team id, queue id based on level, etc.) - Player * reference = GetFirstMember()->getSource(); - // no reference found, can't join this way - if(!reference) - return BG_JOIN_ERR_OFFLINE_MEMBER; - - uint32 bgQueueId = reference->GetBattleGroundQueueIdFromLevel(); - uint32 arenaTeamId = reference->GetArenaTeamId(arenaSlot); - uint32 team = reference->GetTeam(); - - // check every member of the group to be able to join - for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *member = itr->getSource(); - // offline member? don't let join - if(!member) - return BG_JOIN_ERR_OFFLINE_MEMBER; - // don't allow cross-faction join as group - if(member->GetTeam() != team) - return BG_JOIN_ERR_MIXED_FACTION; - // not in the same battleground level braket, don't let join - if(member->GetBattleGroundQueueIdFromLevel() != bgQueueId) - return BG_JOIN_ERR_MIXED_LEVELS; - // don't let join rated matches if the arena team id doesn't match - if(isRated && member->GetArenaTeamId(arenaSlot) != arenaTeamId) - return BG_JOIN_ERR_MIXED_ARENATEAM; - // don't let join if someone from the group is already in that bg queue - if(member->InBattleGroundQueueForBattleGroundQueueType(bgQueueType)) - return BG_JOIN_ERR_GROUP_MEMBER_ALREADY_IN_QUEUE; - // check for deserter debuff in case not arena queue - if(bgTypeId != BATTLEGROUND_AA && !member->CanJoinToBattleground()) - return BG_JOIN_ERR_GROUP_DESERTER; - // check if member can join any more battleground queues - if(!member->HasFreeBattleGroundQueueId()) - return BG_JOIN_ERR_ALL_QUEUES_USED; - } - return BG_JOIN_ERR_OK; -} - -//=================================================== -//============== Roll =============================== -//=================================================== - -void Roll::targetObjectBuildLink() -{ - // called from link() - this->getTarget()->addLootValidatorRef(this); -} - -void Group::SetDifficulty(uint8 difficulty) -{ - m_difficulty = difficulty; - if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET difficulty = %u WHERE leaderGuid ='%u'", m_difficulty, GUID_LOPART(m_leaderGuid)); - - for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *player = itr->getSource(); - if(!player->GetSession() || player->getLevel() < LEVELREQUIREMENT_HEROIC) - continue; - player->SetDifficulty(difficulty); - player->SendDungeonDifficulty(true); - } -} - -bool Group::InCombatToInstance(uint32 instanceId) -{ - for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *pPlayer = itr->getSource(); - if(pPlayer->getAttackers().size() && pPlayer->GetInstanceId() == instanceId) - return true; - } - return false; -} - -void Group::ResetInstances(uint8 method, Player* SendMsgTo) -{ - if(isBGGroup()) - return; - - // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_DISBAND - - // we assume that when the difficulty changes, all instances that can be reset will be - uint8 dif = GetDifficulty(); - - for(BoundInstancesMap::iterator itr = m_boundInstances[dif].begin(); itr != m_boundInstances[dif].end();) - { - InstanceSave *p = itr->second.save; - const MapEntry *entry = sMapStore.LookupEntry(itr->first); - if(!entry || (!p->CanReset() && method != INSTANCE_RESET_GROUP_DISBAND)) - { - ++itr; - continue; - } - - if(method == INSTANCE_RESET_ALL) - { - // the "reset all instances" method can only reset normal maps - if(dif == DIFFICULTY_HEROIC || entry->map_type == MAP_RAID) - { - ++itr; - continue; - } - } - - bool isEmpty = true; - // if the map is loaded, reset it - Map *map = MapManager::Instance().FindMap(p->GetMapId(), p->GetInstanceId()); - if(map && map->IsDungeon()) - isEmpty = ((InstanceMap*)map)->Reset(method); - - if(SendMsgTo) - { - if(isEmpty) SendMsgTo->SendResetInstanceSuccess(p->GetMapId()); - else SendMsgTo->SendResetInstanceFailed(0, p->GetMapId()); - } - - if(isEmpty || method == INSTANCE_RESET_GROUP_DISBAND || method == INSTANCE_RESET_CHANGE_DIFFICULTY) - { - // do not reset the instance, just unbind if others are permanently bound to it - if(p->CanReset()) p->DeleteFromDB(); - else CharacterDatabase.PExecute("DELETE FROM group_instance WHERE instance = '%u'", p->GetInstanceId()); - // i don't know for sure if hash_map iterators - m_boundInstances[dif].erase(itr); - itr = m_boundInstances[dif].begin(); - // this unloads the instance save unless online players are bound to it - // (eg. permanent binds or GM solo binds) - p->RemoveGroup(this); - } - else - ++itr; - } -} - -InstanceGroupBind* Group::GetBoundInstance(uint32 mapid, uint8 difficulty) -{ - // some instances only have one difficulty - const MapEntry* entry = sMapStore.LookupEntry(mapid); - if(!entry || !entry->SupportsHeroicMode()) difficulty = DIFFICULTY_NORMAL; - - BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid); - if(itr != m_boundInstances[difficulty].end()) - return &itr->second; - else - return NULL; -} - -InstanceGroupBind* Group::BindToInstance(InstanceSave *save, bool permanent, bool load) -{ - if(save && !isBGGroup()) - { - InstanceGroupBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()]; - if(bind.save) - { - // when a boss is killed or when copying the players's binds to the group - if(permanent != bind.perm || save != bind.save) - if(!load) CharacterDatabase.PExecute("UPDATE group_instance SET instance = '%u', permanent = '%u' WHERE leaderGuid = '%u' AND instance = '%u'", save->GetInstanceId(), permanent, GUID_LOPART(GetLeaderGUID()), bind.save->GetInstanceId()); - } - else - if(!load) CharacterDatabase.PExecute("INSERT INTO group_instance (leaderGuid, instance, permanent) VALUES ('%u', '%u', '%u')", GUID_LOPART(GetLeaderGUID()), save->GetInstanceId(), permanent); - - if(bind.save != save) - { - if(bind.save) bind.save->RemoveGroup(this); - save->AddGroup(this); - } - - bind.save = save; - bind.perm = permanent; - if(!load) sLog.outDebug("Group::BindToInstance: %d is now bound to map %d, instance %d, difficulty %d", GUID_LOPART(GetLeaderGUID()), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty()); - return &bind; - } - else - return NULL; -} - -void Group::UnbindInstance(uint32 mapid, uint8 difficulty, bool unload) -{ - BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid); - if(itr != m_boundInstances[difficulty].end()) - { - if(!unload) CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u' AND instance = '%u'", GUID_LOPART(GetLeaderGUID()), itr->second.save->GetInstanceId()); - itr->second.save->RemoveGroup(this); // save can become invalid - m_boundInstances[difficulty].erase(itr); - } -} - -void Group::_homebindIfInstance(Player *player) -{ - if(player && !player->isGameMaster() && sMapStore.LookupEntry(player->GetMapId())->IsDungeon()) - { - // leaving the group in an instance, the homebind timer is started - // unless the player is permanently saved to the instance - InstanceSave *save = sInstanceSaveManager.GetInstanceSave(player->GetInstanceId()); - InstancePlayerBind *playerBind = save ? player->GetBoundInstance(save->GetMapId(), save->GetDifficulty()) : NULL; - if(!playerBind || !playerBind->perm) - player->m_InstanceValid = false; - } -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Opcodes.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Player.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Group.h"
+#include "ObjectAccessor.h"
+#include "BattleGround.h"
+#include "MapManager.h"
+#include "InstanceSaveMgr.h"
+#include "MapInstanced.h"
+#include "Util.h"
+
+Group::Group()
+{
+ m_leaderGuid = 0;
+ m_mainTank = 0;
+ m_mainAssistant = 0;
+ m_groupType = (GroupType)0;
+ m_bgGroup = NULL;
+ m_lootMethod = (LootMethod)0;
+ m_looterGuid = 0;
+ m_lootThreshold = ITEM_QUALITY_UNCOMMON;
+
+ for(int i=0; i<TARGETICONCOUNT; i++)
+ m_targetIcons[i] = 0;
+}
+
+Group::~Group()
+{
+ if(m_bgGroup)
+ {
+ sLog.outDebug("Group::~Group: battleground group being deleted.");
+ if(m_bgGroup->GetBgRaid(ALLIANCE) == this) m_bgGroup->SetBgRaid(ALLIANCE, NULL);
+ else if(m_bgGroup->GetBgRaid(HORDE) == this) m_bgGroup->SetBgRaid(HORDE, NULL);
+ else sLog.outError("Group::~Group: battleground group is not linked to the correct battleground.");
+ }
+ Rolls::iterator itr;
+ while(!RollId.empty())
+ {
+ itr = RollId.begin();
+ Roll *r = *itr;
+ RollId.erase(itr);
+ delete(r);
+ }
+
+ // it is undefined whether objectmgr (which stores the groups) or instancesavemgr
+ // will be unloaded first so we must be prepared for both cases
+ // this may unload some instance saves
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
+ itr->second.save->RemoveGroup(this);
+}
+
+bool Group::Create(const uint64 &guid, const char * name)
+{
+ m_leaderGuid = guid;
+ m_leaderName = name;
+
+ m_groupType = isBGGroup() ? GROUPTYPE_RAID : GROUPTYPE_NORMAL;
+ m_lootMethod = GROUP_LOOT;
+ m_lootThreshold = ITEM_QUALITY_UNCOMMON;
+ m_looterGuid = guid;
+
+ m_difficulty = DIFFICULTY_NORMAL;
+ if(!isBGGroup())
+ {
+ Player *leader = objmgr.GetPlayer(guid);
+ if(leader) m_difficulty = leader->GetDifficulty();
+
+ Player::ConvertInstancesToGroup(leader, this, guid);
+
+ // store group in database
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM groups WHERE leaderGuid ='%u'", GUID_LOPART(m_leaderGuid));
+ CharacterDatabase.PExecute("DELETE FROM group_member WHERE leaderGuid ='%u'", GUID_LOPART(m_leaderGuid));
+ CharacterDatabase.PExecute("INSERT INTO groups(leaderGuid,mainTank,mainAssistant,lootMethod,looterGuid,lootThreshold,icon1,icon2,icon3,icon4,icon5,icon6,icon7,icon8,isRaid,difficulty) "
+ "VALUES('%u','%u','%u','%u','%u','%u','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','%u','%u')",
+ GUID_LOPART(m_leaderGuid), GUID_LOPART(m_mainTank), GUID_LOPART(m_mainAssistant), uint32(m_lootMethod),
+ GUID_LOPART(m_looterGuid), uint32(m_lootThreshold), m_targetIcons[0], m_targetIcons[1], m_targetIcons[2], m_targetIcons[3], m_targetIcons[4], m_targetIcons[5], m_targetIcons[6], m_targetIcons[7], isRaidGroup(), m_difficulty);
+ }
+
+ if(!AddMember(guid, name))
+ return false;
+
+ if(!isBGGroup()) CharacterDatabase.CommitTransaction();
+
+ return true;
+}
+
+bool Group::LoadGroupFromDB(const uint64 &leaderGuid, QueryResult *result, bool loadMembers)
+{
+ if(isBGGroup())
+ return false;
+
+ bool external = true;
+ if(!result)
+ {
+ external = false;
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
+ result = CharacterDatabase.PQuery("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, isRaid, difficulty FROM groups WHERE leaderGuid ='%u'", GUID_LOPART(leaderGuid));
+ if(!result)
+ return false;
+ }
+
+ m_leaderGuid = leaderGuid;
+
+ // group leader not exist
+ if(!objmgr.GetPlayerNameByGUID(m_leaderGuid, m_leaderName))
+ {
+ if(!external) delete result;
+ return false;
+ }
+
+ m_groupType = (*result)[13].GetBool() ? GROUPTYPE_RAID : GROUPTYPE_NORMAL;
+ m_difficulty = (*result)[14].GetUInt8();
+ m_mainTank = (*result)[0].GetUInt64();
+ m_mainAssistant = (*result)[1].GetUInt64();
+ m_lootMethod = (LootMethod)(*result)[2].GetUInt8();
+ m_looterGuid = MAKE_NEW_GUID((*result)[3].GetUInt32(), 0, HIGHGUID_PLAYER);
+ m_lootThreshold = (ItemQualities)(*result)[4].GetUInt16();
+
+ for(int i=0; i<TARGETICONCOUNT; i++)
+ m_targetIcons[i] = (*result)[5+i].GetUInt64();
+ if(!external) delete result;
+
+ if(loadMembers)
+ {
+ result = CharacterDatabase.PQuery("SELECT memberGuid, assistant, subgroup FROM group_member WHERE leaderGuid ='%u'", GUID_LOPART(leaderGuid));
+ if(!result)
+ return false;
+
+ do
+ {
+ LoadMemberFromDB((*result)[0].GetUInt32(), (*result)[2].GetUInt8(), (*result)[1].GetBool());
+ } while( result->NextRow() );
+ delete result;
+ // group too small
+ if(GetMembersCount() < 2)
+ return false;
+ }
+
+ return true;
+}
+
+bool Group::LoadMemberFromDB(uint32 guidLow, uint8 subgroup, bool assistant)
+{
+ MemberSlot member;
+ member.guid = MAKE_NEW_GUID(guidLow, 0, HIGHGUID_PLAYER);
+
+ // skip non-existed member
+ if(!objmgr.GetPlayerNameByGUID(member.guid, member.name))
+ return false;
+
+ member.group = subgroup;
+ member.assistant = assistant;
+ m_memberSlots.push_back(member);
+ return true;
+}
+
+bool Group::AddInvite(Player *player)
+{
+ if(!player || player->GetGroupInvite() || player->GetGroup())
+ return false;
+
+ RemoveInvite(player);
+
+ m_invitees.insert(player->GetGUID());
+
+ player->SetGroupInvite(this);
+
+ return true;
+}
+
+bool Group::AddLeaderInvite(Player *player)
+{
+ if(!AddInvite(player))
+ return false;
+
+ m_leaderGuid = player->GetGUID();
+ m_leaderName = player->GetName();
+ return true;
+}
+
+uint32 Group::RemoveInvite(Player *player)
+{
+ for(InvitesList::iterator itr=m_invitees.begin(); itr!=m_invitees.end(); ++itr)
+ {
+ if((*itr) == player->GetGUID())
+ {
+ m_invitees.erase(itr);
+ break;
+ }
+ }
+
+ player->SetGroupInvite(NULL);
+ return GetMembersCount();
+}
+
+void Group::RemoveAllInvites()
+{
+ for(InvitesList::iterator itr=m_invitees.begin(); itr!=m_invitees.end(); ++itr)
+ {
+ Player *invitee = objmgr.GetPlayer(*itr);
+ if(invitee)
+ invitee->SetGroupInvite(NULL);
+ }
+ m_invitees.clear();
+}
+
+bool Group::AddMember(const uint64 &guid, const char* name)
+{
+ if(!_addMember(guid, name))
+ return false;
+ SendUpdate();
+
+ Player *player = objmgr.GetPlayer(guid);
+ if(player)
+ {
+ if(!IsLeader(player->GetGUID()) && !isBGGroup())
+ {
+ // reset the new member's instances, unless he is currently in one of them
+ // including raid/heroic instances that they are not permanently bound to!
+ player->ResetInstances(INSTANCE_RESET_GROUP_JOIN);
+
+ if(player->getLevel() >= LEVELREQUIREMENT_HEROIC && player->GetDifficulty() != GetDifficulty() )
+ {
+ player->SetDifficulty(m_difficulty);
+ player->SendDungeonDifficulty(true);
+ }
+ }
+ player->SetGroupUpdateFlag(GROUP_UPDATE_FULL);
+ UpdatePlayerOutOfRange(player);
+ }
+
+ return true;
+}
+
+uint32 Group::RemoveMember(const uint64 &guid, const uint8 &method)
+{
+ // remove member and change leader (if need) only if strong more 2 members _before_ member remove
+ if(GetMembersCount() > (isBGGroup() ? 1 : 2)) // in BG group case allow 1 members group
+ {
+ bool leaderChanged = _removeMember(guid);
+
+ Player *player = objmgr.GetPlayer( guid );
+ if (player)
+ {
+ WorldPacket data;
+
+ if(method == 1)
+ {
+ data.Initialize( SMSG_GROUP_UNINVITE, 0 );
+ player->GetSession()->SendPacket( &data );
+ }
+
+ data.Initialize(SMSG_GROUP_LIST, 24);
+ data << uint64(0) << uint64(0) << uint64(0);
+ player->GetSession()->SendPacket(&data);
+
+ _homebindIfInstance(player);
+ }
+
+ if(leaderChanged)
+ {
+ WorldPacket data(SMSG_GROUP_SET_LEADER, (m_memberSlots.front().name.size()+1));
+ data << m_memberSlots.front().name;
+ BroadcastPacket(&data);
+ }
+
+ SendUpdate();
+ }
+ // if group before remove <= 2 disband it
+ else
+ Disband(true);
+
+ return m_memberSlots.size();
+}
+
+void Group::ChangeLeader(const uint64 &guid)
+{
+ member_citerator slot = _getMemberCSlot(guid);
+
+ if(slot==m_memberSlots.end())
+ return;
+
+ _setLeader(guid);
+
+ WorldPacket data(SMSG_GROUP_SET_LEADER, slot->name.size()+1);
+ data << slot->name;
+ BroadcastPacket(&data);
+ SendUpdate();
+}
+
+void Group::Disband(bool hideDestroy)
+{
+ Player *player;
+
+ for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
+ {
+ player = objmgr.GetPlayer(citr->guid);
+ if(!player)
+ continue;
+
+ player->SetGroup(NULL);
+
+ if(!player->GetSession())
+ continue;
+
+ WorldPacket data;
+ if(!hideDestroy)
+ {
+ data.Initialize(SMSG_GROUP_DESTROYED, 0);
+ player->GetSession()->SendPacket(&data);
+ }
+
+ data.Initialize(SMSG_GROUP_LIST, 24);
+ data << uint64(0) << uint64(0) << uint64(0);
+ player->GetSession()->SendPacket(&data);
+
+ _homebindIfInstance(player);
+ }
+ RollId.clear();
+ m_memberSlots.clear();
+
+ RemoveAllInvites();
+
+ if(!isBGGroup())
+ {
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM groups WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid));
+ CharacterDatabase.PExecute("DELETE FROM group_member WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid));
+ CharacterDatabase.CommitTransaction();
+ ResetInstances(INSTANCE_RESET_GROUP_DISBAND, NULL);
+ }
+
+ m_leaderGuid = 0;
+ m_leaderName = "";
+}
+
+/*********************************************************/
+/*** LOOT SYSTEM ***/
+/*********************************************************/
+
+void Group::SendLootStartRoll(uint32 CountDown, const Roll &r)
+{
+ WorldPacket data(SMSG_LOOT_START_ROLL, (8+4+4+4+4+4));
+ data << uint64(r.itemGUID); // guid of rolled item
+ data << uint32(r.totalPlayersRolling); // maybe the number of players rolling for it???
+ data << uint32(r.itemid); // the itemEntryId for the item that shall be rolled for
+ data << uint32(r.itemRandomSuffix); // randomSuffix
+ data << uint32(r.itemRandomPropId); // item random property ID
+ data << uint32(CountDown); // the countdown time to choose "need" or "greed"
+
+ for (Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr)
+ {
+ Player *p = objmgr.GetPlayer(itr->first);
+ if(!p || !p->GetSession())
+ continue;
+
+ if(itr->second != NOT_VALID)
+ p->GetSession()->SendPacket( &data );
+ }
+}
+
+void Group::SendLootRoll(uint64 SourceGuid, uint64 TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r)
+{
+ WorldPacket data(SMSG_LOOT_ROLL, (8+4+8+4+4+4+1+1));
+ data << uint64(SourceGuid); // guid of the item rolled
+ data << uint32(0); // unknown, maybe amount of players
+ data << uint64(TargetGuid);
+ data << uint32(r.itemid); // the itemEntryId for the item that shall be rolled for
+ data << uint32(r.itemRandomSuffix); // randomSuffix
+ data << uint32(r.itemRandomPropId); // Item random property ID
+ data << uint8(RollNumber); // 0: "Need for: [item name]" > 127: "you passed on: [item name]" Roll number
+ data << uint8(RollType); // 0: "Need for: [item name]" 0: "You have selected need for [item name] 1: need roll 2: greed roll
+ data << uint8(0); // 2.4.0
+
+ for( Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr)
+ {
+ Player *p = objmgr.GetPlayer(itr->first);
+ if(!p || !p->GetSession())
+ continue;
+
+ if(itr->second != NOT_VALID)
+ p->GetSession()->SendPacket( &data );
+ }
+}
+
+void Group::SendLootRollWon(uint64 SourceGuid, uint64 TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r)
+{
+ WorldPacket data(SMSG_LOOT_ROLL_WON, (8+4+4+4+4+8+1+1));
+ data << uint64(SourceGuid); // guid of the item rolled
+ data << uint32(0); // unknown, maybe amount of players
+ data << uint32(r.itemid); // the itemEntryId for the item that shall be rolled for
+ data << uint32(r.itemRandomSuffix); // randomSuffix
+ data << uint32(r.itemRandomPropId); // Item random property
+ data << uint64(TargetGuid); // guid of the player who won.
+ data << uint8(RollNumber); // rollnumber realted to SMSG_LOOT_ROLL
+ data << uint8(RollType); // Rolltype related to SMSG_LOOT_ROLL
+
+ for( Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr)
+ {
+ Player *p = objmgr.GetPlayer(itr->first);
+ if(!p || !p->GetSession())
+ continue;
+
+ if(itr->second != NOT_VALID)
+ p->GetSession()->SendPacket( &data );
+ }
+}
+
+void Group::SendLootAllPassed(uint32 NumberOfPlayers, const Roll &r)
+{
+ WorldPacket data(SMSG_LOOT_ALL_PASSED, (8+4+4+4+4));
+ data << uint64(r.itemGUID); // Guid of the item rolled
+ data << uint32(NumberOfPlayers); // The number of players rolling for it???
+ data << uint32(r.itemid); // The itemEntryId for the item that shall be rolled for
+ data << uint32(r.itemRandomPropId); // Item random property ID
+ data << uint32(r.itemRandomSuffix); // Item random suffix ID
+
+ for( Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr)
+ {
+ Player *p = objmgr.GetPlayer(itr->first);
+ if(!p || !p->GetSession())
+ continue;
+
+ if(itr->second != NOT_VALID)
+ p->GetSession()->SendPacket( &data );
+ }
+}
+
+void Group::GroupLoot(uint64 playerGUID, Loot *loot, Creature *creature)
+{
+ std::vector<LootItem>::iterator i;
+ ItemPrototype const *item;
+ uint8 itemSlot = 0;
+ Player *player = objmgr.GetPlayer(playerGUID);
+ Group *group = player->GetGroup();
+
+ for (i=loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot)
+ {
+ item = objmgr.GetItemPrototype(i->itemid);
+ if (!item)
+ {
+ //sLog.outDebug("Group::GroupLoot: missing item prototype for item with id: %d", i->itemid);
+ continue;
+ }
+
+ //roll for over-threshold item if it's one-player loot
+ if (item->Quality >= uint32(m_lootThreshold) && !i->freeforall)
+ {
+ uint64 newitemGUID = MAKE_NEW_GUID(objmgr.GenerateLowGuid(HIGHGUID_ITEM),0,HIGHGUID_ITEM);
+ Roll* r=new Roll(newitemGUID,*i);
+
+ //a vector is filled with only near party members
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *member = itr->getSource();
+ if(!member || !member->GetSession())
+ continue;
+ if ( i->AllowedForPlayer(member) )
+ {
+ if (member->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ {
+ r->playerVote[member->GetGUID()] = NOT_EMITED_YET;
+ ++r->totalPlayersRolling;
+ }
+ }
+ }
+
+ r->setLoot(loot);
+ r->itemSlot = itemSlot;
+
+ group->SendLootStartRoll(60000, *r);
+
+ loot->items[itemSlot].is_blocked = true;
+ creature->m_groupLootTimer = 60000;
+ creature->lootingGroupLeaderGUID = GetLeaderGUID();
+
+ RollId.push_back(r);
+ }
+ else
+ i->is_underthreshold=1;
+
+ }
+}
+
+void Group::NeedBeforeGreed(uint64 playerGUID, Loot *loot, Creature *creature)
+{
+ ItemPrototype const *item;
+ Player *player = objmgr.GetPlayer(playerGUID);
+ Group *group = player->GetGroup();
+
+ uint8 itemSlot = 0;
+ for(std::vector<LootItem>::iterator i=loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot)
+ {
+ item = objmgr.GetItemPrototype(i->itemid);
+
+ //only roll for one-player items, not for ones everyone can get
+ if (item->Quality >= uint32(m_lootThreshold) && !i->freeforall)
+ {
+ uint64 newitemGUID = MAKE_NEW_GUID(objmgr.GenerateLowGuid(HIGHGUID_ITEM),0,HIGHGUID_ITEM);
+ Roll* r=new Roll(newitemGUID,*i);
+
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *playerToRoll = itr->getSource();
+ if(!playerToRoll || !playerToRoll->GetSession())
+ continue;
+
+ if (playerToRoll->CanUseItem(item) && i->AllowedForPlayer(playerToRoll) )
+ {
+ if (playerToRoll->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ {
+ r->playerVote[playerToRoll->GetGUID()] = NOT_EMITED_YET;
+ ++r->totalPlayersRolling;
+ }
+ }
+ }
+
+ if (r->totalPlayersRolling > 0)
+ {
+ r->setLoot(loot);
+ r->itemSlot = itemSlot;
+
+ group->SendLootStartRoll(60000, *r);
+
+ loot->items[itemSlot].is_blocked = true;
+
+ RollId.push_back(r);
+ }
+ else
+ {
+ delete r;
+ }
+ }
+ else
+ i->is_underthreshold=1;
+ }
+}
+
+void Group::MasterLoot(uint64 playerGUID, Loot* /*loot*/, Creature *creature)
+{
+ Player *player = objmgr.GetPlayer(playerGUID);
+ if(!player)
+ return;
+
+ sLog.outDebug("Group::MasterLoot (SMSG_LOOT_MASTER_LIST, 330) player = [%s].", player->GetName());
+
+ uint32 real_count = 0;
+
+ WorldPacket data(SMSG_LOOT_MASTER_LIST, 330);
+ data << (uint8)GetMembersCount();
+
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *looter = itr->getSource();
+ if (!looter->IsInWorld())
+ continue;
+
+ if (looter->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ {
+ data << looter->GetGUID();
+ ++real_count;
+ }
+ }
+
+ data.put<uint8>(0,real_count);
+
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *looter = itr->getSource();
+ if (looter->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ looter->GetSession()->SendPacket(&data);
+ }
+}
+
+void Group::CountRollVote(uint64 playerGUID, uint64 Guid, uint32 NumberOfPlayers, uint8 Choise)
+{
+ Rolls::iterator rollI = GetRoll(Guid);
+ if (rollI == RollId.end())
+ return;
+ Roll* roll = *rollI;
+
+ Roll::PlayerVote::iterator itr = roll->playerVote.find(playerGUID);
+ // this condition means that player joins to the party after roll begins
+ if (itr == roll->playerVote.end())
+ return;
+
+ if (roll->getLoot())
+ if (roll->getLoot()->items.empty())
+ return;
+
+ switch (Choise)
+ {
+ case 0: //Player choose pass
+ {
+ SendLootRoll(0, playerGUID, 128, 128, *roll);
+ ++roll->totalPass;
+ itr->second = PASS;
+ }
+ break;
+ case 1: //player choose Need
+ {
+ SendLootRoll(0, playerGUID, 0, 0, *roll);
+ ++roll->totalNeed;
+ itr->second = NEED;
+ }
+ break;
+ case 2: //player choose Greed
+ {
+ SendLootRoll(0, playerGUID, 128, 2, *roll);
+ ++roll->totalGreed;
+ itr->second = GREED;
+ }
+ break;
+ }
+ if (roll->totalPass + roll->totalGreed + roll->totalNeed >= roll->totalPlayersRolling)
+ {
+ CountTheRoll(rollI, NumberOfPlayers);
+ }
+}
+
+//called when roll timer expires
+void Group::EndRoll()
+{
+ Rolls::iterator itr;
+ while(!RollId.empty())
+ {
+ //need more testing here, if rolls disappear
+ itr = RollId.begin();
+ CountTheRoll(itr, GetMembersCount()); //i don't have to edit player votes, who didn't vote ... he will pass
+ }
+}
+
+void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
+{
+ Roll* roll = *rollI;
+ if(!roll->isValid()) // is loot already deleted ?
+ {
+ RollId.erase(rollI);
+ delete roll;
+ return;
+ }
+ //end of the roll
+ if (roll->totalNeed > 0)
+ {
+ if(!roll->playerVote.empty())
+ {
+ uint8 maxresul = 0;
+ uint64 maxguid = (*roll->playerVote.begin()).first;
+ Player *player;
+
+ for( Roll::PlayerVote::const_iterator itr=roll->playerVote.begin(); itr!=roll->playerVote.end(); ++itr)
+ {
+ if (itr->second != NEED)
+ continue;
+
+ uint8 randomN = urand(1, 99);
+ SendLootRoll(0, itr->first, randomN, 1, *roll);
+ if (maxresul < randomN)
+ {
+ maxguid = itr->first;
+ maxresul = randomN;
+ }
+ }
+ SendLootRollWon(0, maxguid, maxresul, 1, *roll);
+ player = objmgr.GetPlayer(maxguid);
+
+ if(player && player->GetSession())
+ {
+ ItemPosCountVec dest;
+ LootItem *item = &(roll->getLoot()->items[roll->itemSlot]);
+ uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count );
+ if ( msg == EQUIP_ERR_OK )
+ {
+ item->is_looted = true;
+ roll->getLoot()->NotifyItemRemoved(roll->itemSlot);
+ --roll->getLoot()->unlootedCount;
+ player->StoreNewItem( dest, roll->itemid, true, item->randomPropertyId);
+ }
+ else
+ {
+ item->is_blocked = false;
+ player->SendEquipError( msg, NULL, NULL );
+ }
+ }
+ }
+ }
+ else if (roll->totalGreed > 0)
+ {
+ if(!roll->playerVote.empty())
+ {
+ uint8 maxresul = 0;
+ uint64 maxguid = (*roll->playerVote.begin()).first;
+ Player *player;
+
+ Roll::PlayerVote::iterator itr;
+ for (itr=roll->playerVote.begin(); itr!=roll->playerVote.end(); ++itr)
+ {
+ if (itr->second != GREED)
+ continue;
+
+ uint8 randomN = urand(1, 99);
+ SendLootRoll(0, itr->first, randomN, 2, *roll);
+ if (maxresul < randomN)
+ {
+ maxguid = itr->first;
+ maxresul = randomN;
+ }
+ }
+ SendLootRollWon(0, maxguid, maxresul, 2, *roll);
+ player = objmgr.GetPlayer(maxguid);
+
+ if(player && player->GetSession())
+ {
+ ItemPosCountVec dest;
+ LootItem *item = &(roll->getLoot()->items[roll->itemSlot]);
+ uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count );
+ if ( msg == EQUIP_ERR_OK )
+ {
+ item->is_looted = true;
+ roll->getLoot()->NotifyItemRemoved(roll->itemSlot);
+ --roll->getLoot()->unlootedCount;
+ player->StoreNewItem( dest, roll->itemid, true, item->randomPropertyId);
+ }
+ else
+ {
+ item->is_blocked = false;
+ player->SendEquipError( msg, NULL, NULL );
+ }
+ }
+ }
+ }
+ else
+ {
+ SendLootAllPassed(NumberOfPlayers, *roll);
+ LootItem *item = &(roll->getLoot()->items[roll->itemSlot]);
+ if(item) item->is_blocked = false;
+ }
+ RollId.erase(rollI);
+ delete roll;
+}
+
+void Group::SetTargetIcon(uint8 id, uint64 guid)
+{
+ if(id >= TARGETICONCOUNT)
+ return;
+
+ // clean other icons
+ if( guid != 0 )
+ for(int i=0; i<TARGETICONCOUNT; i++)
+ if( m_targetIcons[i] == guid )
+ SetTargetIcon(i, 0);
+
+ m_targetIcons[id] = guid;
+
+ WorldPacket data(MSG_RAID_TARGET_UPDATE, (2+8));
+ data << (uint8)0;
+ data << id;
+ data << guid;
+ BroadcastPacket(&data);
+}
+
+void Group::GetDataForXPAtKill(Unit const* victim, uint32& count,uint32& sum_level, Player* & member_with_max_level)
+{
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* member = itr->getSource();
+ if(!member || !member->isAlive()) // only for alive
+ continue;
+
+ if(!member->IsAtGroupRewardDistance(victim)) // at req. distance
+ continue;
+
+ ++count;
+ sum_level += member->getLevel();
+ if(!member_with_max_level || member_with_max_level->getLevel() < member->getLevel())
+ member_with_max_level = member;
+ }
+}
+
+void Group::SendTargetIconList(WorldSession *session)
+{
+ if(!session)
+ return;
+
+ WorldPacket data(MSG_RAID_TARGET_UPDATE, (1+TARGETICONCOUNT*9));
+ data << (uint8)1;
+
+ for(int i=0; i<TARGETICONCOUNT; i++)
+ {
+ if(m_targetIcons[i] == 0)
+ continue;
+
+ data << (uint8)i;
+ data << m_targetIcons[i];
+ }
+
+ session->SendPacket(&data);
+}
+
+void Group::SendUpdate()
+{
+ Player *player;
+
+ for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
+ {
+ player = objmgr.GetPlayer(citr->guid);
+ if(!player || !player->GetSession())
+ continue;
+ // guess size
+ WorldPacket data(SMSG_GROUP_LIST, (1+1+1+1+8+4+GetMembersCount()*20));
+ data << (uint8)m_groupType; // group type
+ data << (uint8)(isBGGroup() ? 1 : 0); // 2.0.x, isBattleGroundGroup?
+ data << (uint8)(citr->group); // groupid
+ data << (uint8)(citr->assistant?0x01:0); // 0x2 main assist, 0x4 main tank
+ data << uint64(0x50000000FFFFFFFELL); // related to voice chat?
+ data << uint32(GetMembersCount()-1);
+ for(member_citerator citr2 = m_memberSlots.begin(); citr2 != m_memberSlots.end(); ++citr2)
+ {
+ if(citr->guid == citr2->guid)
+ continue;
+
+ data << citr2->name;
+ data << (uint64)citr2->guid;
+ // online-state
+ data << (uint8)(objmgr.GetPlayer(citr2->guid) ? 1 : 0);
+ data << (uint8)(citr2->group); // groupid
+ data << (uint8)(citr2->assistant?0x01:0); // 0x2 main assist, 0x4 main tank
+ }
+
+ data << uint64(m_leaderGuid); // leader guid
+ if(GetMembersCount()-1)
+ {
+ data << (uint8)m_lootMethod; // loot method
+ data << (uint64)m_looterGuid; // looter guid
+ data << (uint8)m_lootThreshold; // loot threshold
+ data << (uint8)m_difficulty; // Heroic Mod Group
+
+ }
+ player->GetSession()->SendPacket( &data );
+ }
+}
+
+void Group::UpdatePlayerOutOfRange(Player* pPlayer)
+{
+ if(!pPlayer)
+ return;
+
+ Player *player;
+ WorldPacket data;
+ pPlayer->GetSession()->BuildPartyMemberStatsChangedPacket(pPlayer, &data);
+
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ player = itr->getSource();
+ if (player && player != pPlayer && !pPlayer->isVisibleFor(player))
+ player->GetSession()->SendPacket(&data);
+ }
+}
+
+void Group::BroadcastPacket(WorldPacket *packet, int group, uint64 ignore)
+{
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *pl = itr->getSource();
+ if(!pl || (ignore != 0 && pl->GetGUID() == ignore))
+ continue;
+
+ if (pl->GetSession() && (group==-1 || itr->getSubGroup()==group))
+ pl->GetSession()->SendPacket(packet);
+ }
+}
+
+void Group::BroadcastReadyCheck(WorldPacket *packet)
+{
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *pl = itr->getSource();
+ if(pl && pl->GetSession())
+ if(IsLeader(pl->GetGUID()) || IsAssistant(pl->GetGUID()))
+ pl->GetSession()->SendPacket(packet);
+ }
+}
+
+void Group::OfflineReadyCheck()
+{
+ for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
+ {
+ Player *pl = objmgr.GetPlayer(citr->guid);
+ if (!pl || !pl->GetSession())
+ {
+ WorldPacket data(MSG_RAID_READY_CHECK_CONFIRM, 9);
+ data << citr->guid;
+ data << (uint8)0;
+ BroadcastReadyCheck(&data);
+ }
+ }
+}
+
+bool Group::_addMember(const uint64 &guid, const char* name, bool isAssistant)
+{
+ // get first not-full group
+ uint8 groupid = 0;
+ std::vector<uint8> temp(MAXRAIDSIZE/MAXGROUPSIZE);
+ for(member_citerator itr = m_memberSlots.begin(); itr != m_memberSlots.end(); ++itr)
+ {
+ if (itr->group >= temp.size()) continue;
+ ++temp[itr->group];
+ if(temp[groupid] >= MAXGROUPSIZE)
+ ++groupid;
+ }
+
+ return _addMember(guid, name, isAssistant, groupid);
+}
+
+bool Group::_addMember(const uint64 &guid, const char* name, bool isAssistant, uint8 group)
+{
+ if(IsFull())
+ return false;
+
+ if(!guid)
+ return false;
+
+ Player *player = objmgr.GetPlayer(guid);
+
+ MemberSlot member;
+ member.guid = guid;
+ member.name = name;
+ member.group = group;
+ member.assistant = isAssistant;
+ m_memberSlots.push_back(member);
+
+ if(player)
+ {
+ player->SetGroupInvite(NULL);
+ player->SetGroup(this, group);
+ // if the same group invites the player back, cancel the homebind timer
+ InstanceGroupBind *bind = GetBoundInstance(player->GetMapId(), player->GetDifficulty());
+ if(bind && bind->save->GetInstanceId() == player->GetInstanceId())
+ player->m_InstanceValid = true;
+ }
+
+ if(!isRaidGroup()) // reset targetIcons for non-raid-groups
+ {
+ for(int i=0; i<TARGETICONCOUNT; i++)
+ m_targetIcons[i] = 0;
+ }
+
+ if(!isBGGroup())
+ {
+ // insert into group table
+ CharacterDatabase.PExecute("INSERT INTO group_member(leaderGuid,memberGuid,assistant,subgroup) VALUES('%u','%u','%u','%u')", GUID_LOPART(m_leaderGuid), GUID_LOPART(member.guid), ((member.assistant==1)?1:0), member.group);
+ }
+
+ return true;
+}
+
+bool Group::_removeMember(const uint64 &guid)
+{
+ Player *player = objmgr.GetPlayer(guid);
+ if (player)
+ {
+ player->SetGroup(NULL);
+ }
+
+ _removeRolls(guid);
+
+ member_witerator slot = _getMemberWSlot(guid);
+ if (slot != m_memberSlots.end())
+ m_memberSlots.erase(slot);
+
+ if(!isBGGroup())
+ CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid='%u'", GUID_LOPART(guid));
+
+ if(m_leaderGuid == guid) // leader was removed
+ {
+ if(GetMembersCount() > 0)
+ _setLeader(m_memberSlots.front().guid);
+ return true;
+ }
+
+ return false;
+}
+
+void Group::_setLeader(const uint64 &guid)
+{
+ member_citerator slot = _getMemberCSlot(guid);
+ if(slot==m_memberSlots.end())
+ return;
+
+ if(!isBGGroup())
+ {
+ // TODO: set a time limit to have this function run rarely cause it can be slow
+ CharacterDatabase.BeginTransaction();
+
+ // update the group's bound instances when changing leaders
+
+ // remove all permanent binds from the group
+ // in the DB also remove solo binds that will be replaced with permbinds
+ // from the new leader
+ CharacterDatabase.PExecute(
+ "DELETE FROM group_instance WHERE leaderguid='%u' AND (permanent = 1 OR "
+ "instance IN (SELECT instance FROM character_instance WHERE guid = '%u')"
+ ")", GUID_LOPART(m_leaderGuid), GUID_LOPART(slot->guid)
+ );
+
+ Player *player = objmgr.GetPlayer(slot->guid);
+ if(player)
+ {
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end();)
+ {
+ if(itr->second.perm)
+ {
+ itr->second.save->RemoveGroup(this);
+ m_boundInstances[i].erase(itr++);
+ }
+ else
+ ++itr;
+ }
+ }
+ }
+
+ // update the group's solo binds to the new leader
+ CharacterDatabase.PExecute("UPDATE group_instance SET leaderGuid='%u' WHERE leaderGuid = '%u'", GUID_LOPART(slot->guid), GUID_LOPART(m_leaderGuid));
+
+ // copy the permanent binds from the new leader to the group
+ // overwriting the solo binds with permanent ones if necessary
+ // in the DB those have been deleted already
+ Player::ConvertInstancesToGroup(player, this, slot->guid);
+
+ // update the group leader
+ CharacterDatabase.PExecute("UPDATE groups SET leaderGuid='%u' WHERE leaderGuid='%u'", GUID_LOPART(slot->guid), GUID_LOPART(m_leaderGuid));
+ CharacterDatabase.PExecute("UPDATE group_member SET leaderGuid='%u' WHERE leaderGuid='%u'", GUID_LOPART(slot->guid), GUID_LOPART(m_leaderGuid));
+ CharacterDatabase.CommitTransaction();
+ }
+
+ m_leaderGuid = slot->guid;
+ m_leaderName = slot->name;
+}
+
+void Group::_removeRolls(const uint64 &guid)
+{
+ for (Rolls::iterator it = RollId.begin(); it < RollId.end(); it++)
+ {
+ Roll* roll = *it;
+ Roll::PlayerVote::iterator itr2 = roll->playerVote.find(guid);
+ if(itr2 == roll->playerVote.end())
+ continue;
+
+ if (itr2->second == GREED) --roll->totalGreed;
+ if (itr2->second == NEED) --roll->totalNeed;
+ if (itr2->second == PASS) --roll->totalPass;
+ if (itr2->second != NOT_VALID) --roll->totalPlayersRolling;
+
+ roll->playerVote.erase(itr2);
+
+ CountRollVote(guid, roll->itemGUID, GetMembersCount()-1, 3);
+ }
+}
+
+void Group::_convertToRaid()
+{
+ m_groupType = GROUPTYPE_RAID;
+
+ if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET isRaid = 1 WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid));
+}
+
+bool Group::_setMembersGroup(const uint64 &guid, const uint8 &group)
+{
+ member_witerator slot = _getMemberWSlot(guid);
+ if(slot==m_memberSlots.end())
+ return false;
+
+ slot->group = group;
+ if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE group_member SET subgroup='%u' WHERE memberGuid='%u'", group, GUID_LOPART(guid));
+ return true;
+}
+
+bool Group::_setAssistantFlag(const uint64 &guid, const bool &state)
+{
+ member_witerator slot = _getMemberWSlot(guid);
+ if(slot==m_memberSlots.end())
+ return false;
+
+ slot->assistant = state;
+ if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE group_member SET assistant='%u' WHERE memberGuid='%u'", (state==true)?1:0, GUID_LOPART(guid));
+ return true;
+}
+
+bool Group::_setMainTank(const uint64 &guid)
+{
+ member_citerator slot = _getMemberCSlot(guid);
+ if(slot==m_memberSlots.end())
+ return false;
+
+ if(m_mainAssistant == guid)
+ _setMainAssistant(0);
+ m_mainTank = guid;
+ if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET mainTank='%u' WHERE leaderGuid='%u'", GUID_LOPART(m_mainTank), GUID_LOPART(m_leaderGuid));
+ return true;
+}
+
+bool Group::_setMainAssistant(const uint64 &guid)
+{
+ member_witerator slot = _getMemberWSlot(guid);
+ if(slot==m_memberSlots.end())
+ return false;
+
+ if(m_mainTank == guid)
+ _setMainTank(0);
+ m_mainAssistant = guid;
+ if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET mainAssistant='%u' WHERE leaderGuid='%u'", GUID_LOPART(m_mainAssistant), GUID_LOPART(m_leaderGuid));
+ return true;
+}
+
+bool Group::SameSubGroup(Player const* member1, Player const* member2) const
+{
+ if(!member1 || !member2) return false;
+ if (member1->GetGroup() != this || member2->GetGroup() != this) return false;
+ else return member1->GetSubGroup() == member2->GetSubGroup();
+}
+
+// allows setting subgroup for offline members
+void Group::ChangeMembersGroup(const uint64 &guid, const uint8 &group)
+{
+ if(!isRaidGroup())
+ return;
+ Player *player = objmgr.GetPlayer(guid);
+ if (!player)
+ {
+ if(_setMembersGroup(guid, group))
+ SendUpdate();
+ }
+ else ChangeMembersGroup(player, group);
+}
+
+// only for online members
+void Group::ChangeMembersGroup(Player *player, const uint8 &group)
+{
+ if(!player || !isRaidGroup())
+ return;
+ if(_setMembersGroup(player->GetGUID(), group))
+ {
+ player->GetGroupRef().setSubGroup(group);
+ SendUpdate();
+ }
+}
+
+void Group::UpdateLooterGuid( Creature* creature, bool ifneed )
+{
+ switch (GetLootMethod())
+ {
+ case MASTER_LOOT:
+ case FREE_FOR_ALL:
+ return;
+ default:
+ // round robin style looting applies for all low
+ // quality items in each loot method except free for all and master loot
+ break;
+ }
+
+ member_citerator guid_itr = _getMemberCSlot(GetLooterGuid());
+ if(guid_itr != m_memberSlots.end())
+ {
+ if(ifneed)
+ {
+ // not update if only update if need and ok
+ Player* looter = ObjectAccessor::FindPlayer(guid_itr->guid);
+ if(looter && looter->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ return;
+ }
+ ++guid_itr;
+ }
+
+ // search next after current
+ if(guid_itr != m_memberSlots.end())
+ {
+ for(member_citerator itr = guid_itr; itr != m_memberSlots.end(); ++itr)
+ {
+ if(Player* pl = ObjectAccessor::FindPlayer(itr->guid))
+ {
+ if (pl->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ {
+ bool refresh = pl->GetLootGUID()==creature->GetGUID();
+
+ //if(refresh) // update loot for new looter
+ // pl->GetSession()->DoLootRelease(pl->GetLootGUID());
+ SetLooterGuid(pl->GetGUID());
+ SendUpdate();
+ if(refresh) // update loot for new looter
+ pl->SendLoot(creature->GetGUID(),LOOT_CORPSE);
+ return;
+ }
+ }
+ }
+ }
+
+ // search from start
+ for(member_citerator itr = m_memberSlots.begin(); itr != guid_itr; ++itr)
+ {
+ if(Player* pl = ObjectAccessor::FindPlayer(itr->guid))
+ {
+ if (pl->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ {
+ bool refresh = pl->GetLootGUID()==creature->GetGUID();
+
+ //if(refresh) // update loot for new looter
+ // pl->GetSession()->DoLootRelease(pl->GetLootGUID());
+ SetLooterGuid(pl->GetGUID());
+ SendUpdate();
+ if(refresh) // update loot for new looter
+ pl->SendLoot(creature->GetGUID(),LOOT_CORPSE);
+ return;
+ }
+ }
+ }
+
+ SetLooterGuid(0);
+ SendUpdate();
+}
+
+uint32 Group::CanJoinBattleGroundQueue(uint32 bgTypeId, uint32 bgQueueType, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot)
+{
+ // check for min / max count
+ uint32 memberscount = GetMembersCount();
+ if(memberscount < MinPlayerCount)
+ return BG_JOIN_ERR_GROUP_NOT_ENOUGH;
+ if(memberscount > MaxPlayerCount)
+ return BG_JOIN_ERR_GROUP_TOO_MANY;
+
+ // get a player as reference, to compare other players' stats to (arena team id, queue id based on level, etc.)
+ Player * reference = GetFirstMember()->getSource();
+ // no reference found, can't join this way
+ if(!reference)
+ return BG_JOIN_ERR_OFFLINE_MEMBER;
+
+ uint32 bgQueueId = reference->GetBattleGroundQueueIdFromLevel();
+ uint32 arenaTeamId = reference->GetArenaTeamId(arenaSlot);
+ uint32 team = reference->GetTeam();
+
+ // check every member of the group to be able to join
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *member = itr->getSource();
+ // offline member? don't let join
+ if(!member)
+ return BG_JOIN_ERR_OFFLINE_MEMBER;
+ // don't allow cross-faction join as group
+ if(member->GetTeam() != team)
+ return BG_JOIN_ERR_MIXED_FACTION;
+ // not in the same battleground level braket, don't let join
+ if(member->GetBattleGroundQueueIdFromLevel() != bgQueueId)
+ return BG_JOIN_ERR_MIXED_LEVELS;
+ // don't let join rated matches if the arena team id doesn't match
+ if(isRated && member->GetArenaTeamId(arenaSlot) != arenaTeamId)
+ return BG_JOIN_ERR_MIXED_ARENATEAM;
+ // don't let join if someone from the group is already in that bg queue
+ if(member->InBattleGroundQueueForBattleGroundQueueType(bgQueueType))
+ return BG_JOIN_ERR_GROUP_MEMBER_ALREADY_IN_QUEUE;
+ // check for deserter debuff in case not arena queue
+ if(bgTypeId != BATTLEGROUND_AA && !member->CanJoinToBattleground())
+ return BG_JOIN_ERR_GROUP_DESERTER;
+ // check if member can join any more battleground queues
+ if(!member->HasFreeBattleGroundQueueId())
+ return BG_JOIN_ERR_ALL_QUEUES_USED;
+ }
+ return BG_JOIN_ERR_OK;
+}
+
+//===================================================
+//============== Roll ===============================
+//===================================================
+
+void Roll::targetObjectBuildLink()
+{
+ // called from link()
+ this->getTarget()->addLootValidatorRef(this);
+}
+
+void Group::SetDifficulty(uint8 difficulty)
+{
+ m_difficulty = difficulty;
+ if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET difficulty = %u WHERE leaderGuid ='%u'", m_difficulty, GUID_LOPART(m_leaderGuid));
+
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *player = itr->getSource();
+ if(!player->GetSession() || player->getLevel() < LEVELREQUIREMENT_HEROIC)
+ continue;
+ player->SetDifficulty(difficulty);
+ player->SendDungeonDifficulty(true);
+ }
+}
+
+bool Group::InCombatToInstance(uint32 instanceId)
+{
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *pPlayer = itr->getSource();
+ if(pPlayer->getAttackers().size() && pPlayer->GetInstanceId() == instanceId)
+ return true;
+ }
+ return false;
+}
+
+void Group::ResetInstances(uint8 method, Player* SendMsgTo)
+{
+ if(isBGGroup())
+ return;
+
+ // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_DISBAND
+
+ // we assume that when the difficulty changes, all instances that can be reset will be
+ uint8 dif = GetDifficulty();
+
+ for(BoundInstancesMap::iterator itr = m_boundInstances[dif].begin(); itr != m_boundInstances[dif].end();)
+ {
+ InstanceSave *p = itr->second.save;
+ const MapEntry *entry = sMapStore.LookupEntry(itr->first);
+ if(!entry || (!p->CanReset() && method != INSTANCE_RESET_GROUP_DISBAND))
+ {
+ ++itr;
+ continue;
+ }
+
+ if(method == INSTANCE_RESET_ALL)
+ {
+ // the "reset all instances" method can only reset normal maps
+ if(dif == DIFFICULTY_HEROIC || entry->map_type == MAP_RAID)
+ {
+ ++itr;
+ continue;
+ }
+ }
+
+ bool isEmpty = true;
+ // if the map is loaded, reset it
+ Map *map = MapManager::Instance().FindMap(p->GetMapId(), p->GetInstanceId());
+ if(map && map->IsDungeon())
+ isEmpty = ((InstanceMap*)map)->Reset(method);
+
+ if(SendMsgTo)
+ {
+ if(isEmpty) SendMsgTo->SendResetInstanceSuccess(p->GetMapId());
+ else SendMsgTo->SendResetInstanceFailed(0, p->GetMapId());
+ }
+
+ if(isEmpty || method == INSTANCE_RESET_GROUP_DISBAND || method == INSTANCE_RESET_CHANGE_DIFFICULTY)
+ {
+ // do not reset the instance, just unbind if others are permanently bound to it
+ if(p->CanReset()) p->DeleteFromDB();
+ else CharacterDatabase.PExecute("DELETE FROM group_instance WHERE instance = '%u'", p->GetInstanceId());
+ // i don't know for sure if hash_map iterators
+ m_boundInstances[dif].erase(itr);
+ itr = m_boundInstances[dif].begin();
+ // this unloads the instance save unless online players are bound to it
+ // (eg. permanent binds or GM solo binds)
+ p->RemoveGroup(this);
+ }
+ else
+ ++itr;
+ }
+}
+
+InstanceGroupBind* Group::GetBoundInstance(uint32 mapid, uint8 difficulty)
+{
+ // some instances only have one difficulty
+ const MapEntry* entry = sMapStore.LookupEntry(mapid);
+ if(!entry || !entry->SupportsHeroicMode()) difficulty = DIFFICULTY_NORMAL;
+
+ BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
+ if(itr != m_boundInstances[difficulty].end())
+ return &itr->second;
+ else
+ return NULL;
+}
+
+InstanceGroupBind* Group::BindToInstance(InstanceSave *save, bool permanent, bool load)
+{
+ if(save && !isBGGroup())
+ {
+ InstanceGroupBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()];
+ if(bind.save)
+ {
+ // when a boss is killed or when copying the players's binds to the group
+ if(permanent != bind.perm || save != bind.save)
+ if(!load) CharacterDatabase.PExecute("UPDATE group_instance SET instance = '%u', permanent = '%u' WHERE leaderGuid = '%u' AND instance = '%u'", save->GetInstanceId(), permanent, GUID_LOPART(GetLeaderGUID()), bind.save->GetInstanceId());
+ }
+ else
+ if(!load) CharacterDatabase.PExecute("INSERT INTO group_instance (leaderGuid, instance, permanent) VALUES ('%u', '%u', '%u')", GUID_LOPART(GetLeaderGUID()), save->GetInstanceId(), permanent);
+
+ if(bind.save != save)
+ {
+ if(bind.save) bind.save->RemoveGroup(this);
+ save->AddGroup(this);
+ }
+
+ bind.save = save;
+ bind.perm = permanent;
+ if(!load) sLog.outDebug("Group::BindToInstance: %d is now bound to map %d, instance %d, difficulty %d", GUID_LOPART(GetLeaderGUID()), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty());
+ return &bind;
+ }
+ else
+ return NULL;
+}
+
+void Group::UnbindInstance(uint32 mapid, uint8 difficulty, bool unload)
+{
+ BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
+ if(itr != m_boundInstances[difficulty].end())
+ {
+ if(!unload) CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u' AND instance = '%u'", GUID_LOPART(GetLeaderGUID()), itr->second.save->GetInstanceId());
+ itr->second.save->RemoveGroup(this); // save can become invalid
+ m_boundInstances[difficulty].erase(itr);
+ }
+}
+
+void Group::_homebindIfInstance(Player *player)
+{
+ if(player && !player->isGameMaster() && sMapStore.LookupEntry(player->GetMapId())->IsDungeon())
+ {
+ // leaving the group in an instance, the homebind timer is started
+ // unless the player is permanently saved to the instance
+ InstanceSave *save = sInstanceSaveManager.GetInstanceSave(player->GetInstanceId());
+ InstancePlayerBind *playerBind = save ? player->GetBoundInstance(save->GetMapId(), save->GetDifficulty()) : NULL;
+ if(!playerBind || !playerBind->perm)
+ player->m_InstanceValid = false;
+ }
+}
diff --git a/src/game/HomeMovementGenerator.cpp b/src/game/HomeMovementGenerator.cpp index 281909b68de..79f312d3b8b 100644 --- a/src/game/HomeMovementGenerator.cpp +++ b/src/game/HomeMovementGenerator.cpp @@ -1,86 +1,86 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "HomeMovementGenerator.h" -#include "Creature.h" -#include "Traveller.h" -#include "MapManager.h" -#include "ObjectAccessor.h" -#include "DestinationHolderImp.h" -#include "ObjectMgr.h" -#include "WorldPacket.h" - -void -HomeMovementGenerator<Creature>::Initialize(Creature & owner) -{ - owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); - _setTargetLocation(owner); -} - -void -HomeMovementGenerator<Creature>::Reset(Creature &) -{ -} - -void -HomeMovementGenerator<Creature>::_setTargetLocation(Creature & owner) -{ - if( !&owner ) - return; - - if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED) ) - return; - - float x, y, z; - owner.GetRespawnCoord(x, y, z); - - CreatureTraveller traveller(owner); - - uint32 travel_time = i_destinationHolder.SetDestination(traveller, x, y, z); - modifyTravelTime(travel_time); - owner.clearUnitState(UNIT_STAT_ALL_STATE); -} - -bool -HomeMovementGenerator<Creature>::Update(Creature &owner, const uint32& time_diff) -{ - CreatureTraveller traveller( owner); - i_destinationHolder.UpdateTraveller(traveller, time_diff, false); - - if (time_diff > i_travel_timer) - { - owner.AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); - - // restore orientation of not moving creature at returning to home - if(owner.GetDefaultMovementType()==IDLE_MOTION_TYPE) - { - if(CreatureData const* data = objmgr.GetCreatureData(owner.GetDBTableGUIDLow())) - { - owner.SetOrientation(data->orientation); - WorldPacket packet; - owner.BuildHeartBeatMsg(&packet); - owner.SendMessageToSet(&packet, false); - } - } - return false; - } - - i_travel_timer -= time_diff; - - return true; -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "HomeMovementGenerator.h"
+#include "Creature.h"
+#include "Traveller.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "DestinationHolderImp.h"
+#include "ObjectMgr.h"
+#include "WorldPacket.h"
+
+void
+HomeMovementGenerator<Creature>::Initialize(Creature & owner)
+{
+ owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ _setTargetLocation(owner);
+}
+
+void
+HomeMovementGenerator<Creature>::Reset(Creature &)
+{
+}
+
+void
+HomeMovementGenerator<Creature>::_setTargetLocation(Creature & owner)
+{
+ if( !&owner )
+ return;
+
+ if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED) )
+ return;
+
+ float x, y, z;
+ owner.GetRespawnCoord(x, y, z);
+
+ CreatureTraveller traveller(owner);
+
+ uint32 travel_time = i_destinationHolder.SetDestination(traveller, x, y, z);
+ modifyTravelTime(travel_time);
+ owner.clearUnitState(UNIT_STAT_ALL_STATE);
+}
+
+bool
+HomeMovementGenerator<Creature>::Update(Creature &owner, const uint32& time_diff)
+{
+ CreatureTraveller traveller( owner);
+ i_destinationHolder.UpdateTraveller(traveller, time_diff, false);
+
+ if (time_diff > i_travel_timer)
+ {
+ owner.AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
+ // restore orientation of not moving creature at returning to home
+ if(owner.GetDefaultMovementType()==IDLE_MOTION_TYPE)
+ {
+ if(CreatureData const* data = objmgr.GetCreatureData(owner.GetDBTableGUIDLow()))
+ {
+ owner.SetOrientation(data->orientation);
+ WorldPacket packet;
+ owner.BuildHeartBeatMsg(&packet);
+ owner.SendMessageToSet(&packet, false);
+ }
+ }
+ return false;
+ }
+
+ i_travel_timer -= time_diff;
+
+ return true;
+}
diff --git a/src/game/Language.h b/src/game/Language.h index dcd76059e2c..fc4f4039e51 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -75,7 +75,9 @@ enum MangosStrings LANG_LEVEL_MINREQUIRED = 49,
LANG_LEVEL_MINREQUIRED_AND_ITEM = 50,
LANG_NPC_TAINER_HELLO = 51,
- // Room for more level 0
+ LANG_COMMAND_INVALID_ITEM_COUNT = 52,
+ LANG_COMMAND_MAIL_ITEMS_LIMIT = 53,
+ // Room for more level 0 54-99 not used
// level 1 chat
LANG_GLOBAL_NOTIFY = 100,
@@ -159,7 +161,7 @@ enum MangosStrings LANG_MAIL_SENT = 169,
LANG_SOUND_NOT_EXIST = 170,
- // Room for more level 1
+ // Room for more level 1 171-199 not used
// level 2 chat
LANG_NO_SELECTION = 200,
@@ -304,8 +306,11 @@ enum MangosStrings LANG_LOOKUP_PLAYER_CHARACTER = 329,
LANG_NO_PLAYERS_FOUND = 330,
LANG_EXTENDED_COST_NOT_EXIST = 331,
-
- // Room for more level 2
+ LANG_GM_ON = 332,
+ LANG_GM_OFF = 333,
+ LANG_GM_CHAT_ON = 334,
+ LANG_GM_CHAT_OFF = 335,
+ // Room for more level 2 336-399 not used
// level 3 chat
LANG_SCRIPTS_RELOADED = 400,
@@ -610,79 +615,49 @@ enum MangosStrings LANG_BG_GROUP_TOO_LARGE = 711,
LANG_ARENA_GROUP_TOO_LARGE = 712,
- LANG_ARENA_YOUR_TEAM_ONLY = 713,
- LANG_ARENA_NOT_ENOUGH_PLAYERS = 714,
- LANG_ARENA_GOLD_WINS = 715,
- LANG_ARENA_GREEN_WINS = 716,
- LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING = 717,
- LANG_BG_GROUP_OFFLINE_MEMBER = 718,
- LANG_BG_GROUP_MIXED_FACTION = 719,
- LANG_BG_GROUP_MIXED_LEVELS = 720,
- LANG_BG_GROUP_MEMBER_ALREADY_IN_QUEUE = 721,
- LANG_BG_GROUP_MEMBER_DESERTER = 722,
- LANG_BG_GROUP_MEMBER_NO_FREE_QUEUE_SLOTS = 723,
-
- LANG_CANNOT_TELE_TO_BG = 724,
- LANG_CANNOT_SUMMON_TO_BG = 725,
- LANG_CANNOT_GO_TO_BG_GM = 726,
- LANG_CANNOT_GO_TO_BG_FROM_BG = 727,
-
- LANG_ARENA_TESTING = 728
-
+ LANG_YOUR_ARENA_LEVEL_REQ_ERROR = 713,
+ LANG_HIS_ARENA_LEVEL_REQ_ERROR = 714,
+ LANG_YOUR_BG_LEVEL_REQ_ERROR = 715,
+ LANG_YOUR_ARENA_TEAM_FULL = 716,
+ // Room for BG/ARENA 717-799 not used
+
+ LANG_ARENA_YOUR_TEAM_ONLY = 730,
+ LANG_ARENA_NOT_ENOUGH_PLAYERS = 731,
+ LANG_ARENA_GOLD_WINS = 732,
+ LANG_ARENA_GREEN_WINS = 733,
+ LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING = 734,
+ LANG_BG_GROUP_OFFLINE_MEMBER = 735,
+ LANG_BG_GROUP_MIXED_FACTION = 736,
+ LANG_BG_GROUP_MIXED_LEVELS = 737,
+ LANG_BG_GROUP_MEMBER_ALREADY_IN_QUEUE = 738,
+ LANG_BG_GROUP_MEMBER_DESERTER = 739,
+ LANG_BG_GROUP_MEMBER_NO_FREE_QUEUE_SLOTS = 740,
+
+ LANG_CANNOT_TELE_TO_BG = 741,
+ LANG_CANNOT_SUMMON_TO_BG = 742,
+ LANG_CANNOT_GO_TO_BG_GM = 743,
+ LANG_CANNOT_GO_TO_BG_FROM_BG = 744,
+
+ LANG_ARENA_TESTING = 745,
+
+ // in game strings
+ LANG_PET_INVALID_NAME = 800,
+ LANG_NOT_ENOUGH_GOLD = 801,
+ LANG_NOT_FREE_TRADE_SLOTS = 802,
+ LANG_NOT_PARTNER_FREE_TRADE_SLOTS = 803,
+ LANG_YOU_NOT_HAVE_PERMISSION = 804,
+ LANG_UNKNOWN_LANGUAGE = 805,
+ LANG_NOT_LEARNED_LANGUAGE = 806,
+ LANG_NEED_CHARACTER_NAME = 807,
+ LANG_PLAYER_NOT_EXIST_OR_OFFLINE = 808,
+ LANG_ACCOUNT_FOR_PLAYER_NOT_FOUND = 809,
+ // Room for in-game strings 810-999 not used
+
+ // FREE IDS 1000-9999
+
+ // Use for not-in-svn patches 10000-10999
+ // Use for custom patches 11000-11999
+
+ // NOT RESERVED IDS 12000-
};
#endif
-
-/* NOT USED VALUES
-// alliance ranks
-#define LANG_ALI_PRIVATE "Private "
-#define LANG_ALI_CORPORAL "Corporal "
-#define LANG_ALI_SERGEANT "Sergeant "
-#define LANG_ALI_MASTER_SERGEANT "Master Sergeant "
-#define LANG_ALI_SERGEANT_MAJOR "Sergeant Major "
-#define LANG_ALI_KNIGHT "Knight "
-#define LANG_ALI_KNIGHT_LIEUTENANT "Knight-Lieutenant "
-#define LANG_ALI_KNIGHT_CAPTAIN "Knight-Captain "
-#define LANG_ALI_KNIGHT_CHAMPION "Knight-Champion "
-#define LANG_ALI_LIEUTENANT_COMMANDER "Lieutenant Commander "
-#define LANG_ALI_COMMANDER "Commander "
-#define LANG_ALI_MARSHAL "Marshal "
-#define LANG_ALI_FIELD_MARSHAL "Field Marshal "
-#define LANG_ALI_GRAND_MARSHAL "Grand Marshal "
-#define LANG_ALI_GAME_MASTER "Game Master "
-
-// horde ranks
-#define LANG_HRD_SCOUT "Scout "
-#define LANG_HRD_GRUNT "Grunt "
-#define LANG_HRD_SERGEANT "Sergeant "
-#define LANG_HRD_SENIOR_SERGEANT "Senior Sergeant "
-#define LANG_HRD_FIRST_SERGEANT "First Sergeant "
-#define LANG_HRD_STONE_GUARD "Stone Guard "
-#define LANG_HRD_BLOOD_GUARD "Blood Guard "
-#define LANG_HRD_LEGIONNARE "Legionnaire "
-#define LANG_HRD_CENTURION "Centurion "
-#define LANG_HRD_CHAMPION "Champion "
-#define LANG_HRD_LIEUTENANT_GENERAL "Lieutenant General "
-#define LANG_HRD_GENERAL "General "
-#define LANG_HRD_WARLORD "Warlord "
-#define LANG_HRD_HIGH_WARLORD "High Warlord "
-#define LANG_HRD_GAME_MASTER "Game Master "
-
-#define LANG_NO_RANK "No rank "
-#define LANG_RANK "%s (Rank %u)"
-#define LANG_HONOR_TODAY "Today: [Honorable kills: |c0000ff00%u|r] [Dishonorable kills: |c00ff0000%u|r]"
-#define LANG_HONOR_YESTERDAY "Yesterday: [Kills: |c0000ff00%u|r] [Honor: %u]"
-#define LANG_HONOR_THIS_WEEK "This week: [Kills: |c0000ff00%u|r] [Honor: %u]"
-#define LANG_HONOR_LAST_WEEK "Last week: [Kills: |c0000ff00%u|r] [Honor: %u] [Standing: %u]"
-#define LANG_HONOR_LIFE "Lifetime: [Honorable kills: |c0000ff00%u|r] [Dishonorable kills: |c00ff0000%u|r] [Highest rank %u: %s]"
-
-// level 2
-#define LANG_ADD_OBJ "AddObject at Chat.cpp" //log
-#define LANG_DEMORPHED "Demorphed %s" //log
-
-// level 3
-#define LANG_SPAWNING_SPIRIT_HEAL "Spawning spirit healers\n"
-#define LANG_NO_SPIRIT_HEAL_DB "No spirit healers in database, exiting."
-
-#define LANG_ADD_OBJ_LV3 "AddObject at Level3.cpp line 1176"
-
-*/
diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp index c1bca8b8b40..19dec5500bc 100644 --- a/src/game/Level1.cpp +++ b/src/game/Level1.cpp @@ -147,9 +147,11 @@ bool ChatHandler::HandleGMmodeCommand(const char* args) {
if(!*args)
{
- SendSysMessage(LANG_USE_BOL);
- SetSentErrorMessage(true);
- return false;
+ if(m_session->GetPlayer()->isGameMaster())
+ m_session->SendNotification(LANG_GM_ON);
+ else
+ m_session->SendNotification(LANG_GM_OFF);
+ return true;
}
std::string argstr = (char*)args;
@@ -157,7 +159,7 @@ bool ChatHandler::HandleGMmodeCommand(const char* args) if (argstr == "on")
{
m_session->GetPlayer()->SetGameMaster(true);
- m_session->SendNotification("GM mode is ON");
+ m_session->SendNotification(LANG_GM_ON);
#ifdef _DEBUG_VMAPS
VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager();
vMapManager->processCommand("stoplog");
@@ -168,7 +170,7 @@ bool ChatHandler::HandleGMmodeCommand(const char* args) if (argstr == "off")
{
m_session->GetPlayer()->SetGameMaster(false);
- m_session->SendNotification("GM mode is OFF");
+ m_session->SendNotification(LANG_GM_OFF);
#ifdef _DEBUG_VMAPS
VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager();
vMapManager->processCommand("startlog");
@@ -181,6 +183,40 @@ bool ChatHandler::HandleGMmodeCommand(const char* args) return false;
}
+// Enables or disables hiding of the staff badge
+bool ChatHandler::HandleGMChatCommand(const char* args)
+{
+ if(!*args)
+ {
+ if(m_session->GetPlayer()->isGMChat())
+ m_session->SendNotification(LANG_GM_CHAT_ON);
+ else
+ m_session->SendNotification(LANG_GM_CHAT_OFF);
+ return true;
+ }
+
+ std::string argstr = (char*)args;
+
+ if (argstr == "on")
+ {
+ m_session->GetPlayer()->SetGMChat(true);
+ m_session->SendNotification(LANG_GM_CHAT_ON);
+ return true;
+ }
+
+ if (argstr == "off")
+ {
+ m_session->GetPlayer()->SetGMChat(false);
+ m_session->SendNotification(LANG_GM_CHAT_OFF);
+ return true;
+ }
+
+ SendSysMessage(LANG_USE_BOL);
+ SetSentErrorMessage(true);
+ return false;
+}
+
+
//Enable\Dissable Invisible mode
bool ChatHandler::HandleVisibleCommand(const char* args)
{
@@ -195,13 +231,13 @@ bool ChatHandler::HandleVisibleCommand(const char* args) if (argstr == "on")
{
m_session->GetPlayer()->SetGMVisible(true);
- m_session->SendNotification(GetMangosString(LANG_INVISIBLE_VISIBLE));
+ m_session->SendNotification(LANG_INVISIBLE_VISIBLE);
return true;
}
if (argstr == "off")
{
- m_session->SendNotification(GetMangosString(LANG_INVISIBLE_INVISIBLE));
+ m_session->SendNotification(LANG_INVISIBLE_INVISIBLE);
m_session->GetPlayer()->SetGMVisible(false);
return true;
}
@@ -1823,19 +1859,107 @@ bool ChatHandler::HandleSendMailCommand(const char* args) if(!*args)
return false;
+ // format: name "subject text" "mail text" item1[:count1] item2[:count2] ... item12[:count12]
+
char* pName = strtok((char*)args, " ");
- char* msgSubject = strtok(NULL, " ");
- char* msgText = strtok(NULL, "");
+ if(!pName)
+ return false;
+
+ char* tail1 = strtok(NULL, "");
+ if(!tail1)
+ return false;
+
+ char* msgSubject;
+ if(*tail1=='"')
+ msgSubject = strtok(tail1+1, "\"");
+ else
+ {
+ char* space = strtok(tail1, "\"");
+ if(!space)
+ return false;
+ msgSubject = strtok(NULL, "\"");
+ }
+
+ if (!msgSubject)
+ return false;
+
+ char* tail2 = strtok(NULL, "");
+ if(!tail2)
+ return false;
+
+ char* msgText;
+ if(*tail2=='"')
+ msgText = strtok(tail2+1, "\"");
+ else
+ {
+ char* space = strtok(tail2, "\"");
+ if(!space)
+ return false;
+ msgText = strtok(NULL, "\"");
+ }
if (!msgText)
return false;
// pName, msgSubject, msgText isn't NUL after prev. check
-
std::string name = pName;
std::string subject = msgSubject;
std::string text = msgText;
+ // extract items
+ typedef std::pair<uint32,uint32> ItemPair;
+ typedef std::list< ItemPair > ItemPairs;
+ ItemPairs items;
+
+ // get all tail string
+ char* tail = strtok(NULL, "");
+
+ // get from tail next item str
+ while(char* itemStr = strtok(tail, " "))
+ {
+ // and get new tail
+ tail = strtok(NULL, "");
+
+ // parse item str
+ char* itemIdStr = strtok(itemStr, ":");
+ char* itemCountStr = strtok(NULL, " ");
+
+ uint32 item_id = atoi(itemIdStr);
+ if(!item_id)
+ return false;
+
+ ItemPrototype const* item_proto = objmgr.GetItemPrototype(item_id);
+ if(!item_proto)
+ {
+ PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, item_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 item_count = itemCountStr ? atoi(itemCountStr) : 1;
+ if(item_count < 1 || item_proto->MaxCount && item_count > item_proto->MaxCount)
+ {
+ PSendSysMessage(LANG_COMMAND_INVALID_ITEM_COUNT, item_count,item_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ while(item_count > item_proto->Stackable)
+ {
+ items.push_back(ItemPair(item_id,item_proto->Stackable));
+ item_count -= item_proto->Stackable;
+ }
+
+ items.push_back(ItemPair(item_id,item_count));
+
+ if(items.size() > MAX_MAIL_ITEMS)
+ {
+ PSendSysMessage(LANG_COMMAND_MAIL_ITEMS_LIMIT, MAX_MAIL_ITEMS);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+
if(!normalizePlayerName(name))
{
SendSysMessage(LANG_PLAYER_NOT_FOUND);
@@ -1844,9 +1968,12 @@ bool ChatHandler::HandleSendMailCommand(const char* args) }
uint64 receiver_guid = objmgr.GetPlayerGUIDByName(name);
-
if(!receiver_guid)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
return false;
+ }
uint32 mailId = objmgr.GenerateMailID();
uint32 sender_guidlo = m_session->GetPlayer()->GetGUIDLow();
@@ -1860,7 +1987,19 @@ bool ChatHandler::HandleSendMailCommand(const char* args) Player *receiver = objmgr.GetPlayer(receiver_guid);
- WorldSession::SendMailTo(receiver,messagetype, stationery, sender_guidlo, GUID_LOPART(receiver_guid), subject, itemTextId, NULL, 0, 0, MAIL_CHECK_MASK_NONE);
+ // fill mail
+ MailItemsInfo mi; // item list preparing
+
+ for(ItemPairs::const_iterator itr = items.begin(); itr != items.end(); ++itr)
+ {
+ if(Item* item = Item::CreateItem(itr->first,itr->second,m_session->GetPlayer()))
+ {
+ item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
+ mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
+ }
+ }
+
+ WorldSession::SendMailTo(receiver,messagetype, stationery, sender_guidlo, GUID_LOPART(receiver_guid), subject, itemTextId, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
PSendSysMessage(LANG_MAIL_SENT, name.c_str());
return true;
diff --git a/src/game/MiscHandler.cpp b/src/game/MiscHandler.cpp index 20d556fb9aa..cb9a7cd673f 100644 --- a/src/game/MiscHandler.cpp +++ b/src/game/MiscHandler.cpp @@ -1,1765 +1,1759 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "Common.h" -#include "Language.h" -#include "Database/DatabaseEnv.h" -#include "WorldPacket.h" -#include "Opcodes.h" -#include "Log.h" -#include "Player.h" -#include "World.h" -#include "ObjectMgr.h" -#include "WorldSession.h" -#include "Auth/BigNumber.h" -#include "Auth/Sha1.h" -#include "UpdateData.h" -#include "LootMgr.h" -#include "Chat.h" -#include "ScriptCalls.h" -#include <zlib/zlib.h> -#include "MapManager.h" -#include "ObjectAccessor.h" -#include "Object.h" -#include "BattleGround.h" -#include "SpellAuras.h" -#include "Pet.h" -#include "SocialMgr.h" - -void WorldSession::HandleRepopRequestOpcode( WorldPacket & /*recv_data*/ ) -{ - sLog.outDebug( "WORLD: Recvd CMSG_REPOP_REQUEST Message" ); - - if(GetPlayer()->isAlive()||GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) - return; - - // the world update order is sessions, players, creatures - // the netcode runs in parallel with all of these - // creatures can kill players - // so if the server is lagging enough the player can - // release spirit after he's killed but before he is updated - if(GetPlayer()->getDeathState() == JUST_DIED) - { - sLog.outDebug("HandleRepopRequestOpcode: got request after player %s(%d) was killed and before he was updated", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow()); - GetPlayer()->KillPlayer(); - } - - //this is spirit release confirm? - GetPlayer()->RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true); - GetPlayer()->BuildPlayerRepop(); - GetPlayer()->RepopAtGraveyard(); -} - -void WorldSession::HandleWhoOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,4+4+1+1+4+4+4+4); - - sLog.outDebug( "WORLD: Recvd CMSG_WHO Message" ); - //recv_data.hexlike(); - - uint32 clientcount = 0; - - uint32 level_min, level_max, racemask, classmask, zones_count, str_count; - uint32 zoneids[10]; // 10 is client limit - std::string player_name, guild_name; - - recv_data >> level_min; // maximal player level, default 0 - recv_data >> level_max; // minimal player level, default 100 - recv_data >> player_name; // player name, case sensitive... - - // recheck - CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+1+4+4+4+4); - - recv_data >> guild_name; // guild name, case sensitive... - - // recheck - CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+(guild_name.size()+1)+4+4+4+4); - - recv_data >> racemask; // race mask - recv_data >> classmask; // class mask - recv_data >> zones_count; // zones count, client limit=10 (2.0.10) - - if(zones_count > 10) - return; // can't be received from real client or broken packet - - // recheck - CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+(guild_name.size()+1)+4+4+4+(4*zones_count)+4); - - for(uint32 i = 0; i < zones_count; i++) - { - uint32 temp; - recv_data >> temp; // zone id, 0 if zone is unknown... - zoneids[i] = temp; - sLog.outDebug("Zone %u: %u", i, zoneids[i]); - } - - recv_data >> str_count; // user entered strings count, client limit=4 (checked on 2.0.10) - - if(str_count > 4) - return; // can't be received from real client or broken packet - - // recheck - CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+(guild_name.size()+1)+4+4+4+(4*zones_count)+4+(1*str_count)); - - sLog.outDebug("Minlvl %u, maxlvl %u, name %s, guild %s, racemask %u, classmask %u, zones %u, strings %u", level_min, level_max, player_name.c_str(), guild_name.c_str(), racemask, classmask, zones_count, str_count); - - std::wstring str[4]; // 4 is client limit - for(uint32 i = 0; i < str_count; i++) - { - // recheck (have one more byte) - CHECK_PACKET_SIZE(recv_data,recv_data.rpos()); - - std::string temp; - recv_data >> temp; // user entered string, it used as universal search pattern(guild+player name)? - - if(!Utf8toWStr(temp,str[i])) - continue; - - wstrToLower(str[i]); - - sLog.outDebug("String %u: %s", i, str[i].c_str()); - } - - std::wstring wplayer_name; - std::wstring wguild_name; - if(!(Utf8toWStr(player_name, wplayer_name) && Utf8toWStr(guild_name, wguild_name))) - return; - wstrToLower(wplayer_name); - wstrToLower(wguild_name); - - // client send in case not set max level value 100 but mangos support 255 max level, - // update it to show GMs with characters after 100 level - if(level_max >= 100) - level_max = 255; - - uint32 team = _player->GetTeam(); - uint32 security = GetSecurity(); - bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST); - bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST); - - WorldPacket data( SMSG_WHO, 50 ); // guess size - data << clientcount; // clientcount place holder - data << clientcount; // clientcount place holder - - //TODO: Guard Player map - HashMapHolder<Player>::MapType& m = ObjectAccessor::Instance().GetPlayers(); - for(HashMapHolder<Player>::MapType::iterator itr = m.begin(); itr != m.end(); ++itr) - { - if (security == SEC_PLAYER) - { - // player can see member of other team only if CONFIG_ALLOW_TWO_SIDE_WHO_LIST - if (itr->second->GetTeam() != team && !allowTwoSideWhoList ) - continue; - - // player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if CONFIG_GM_IN_WHO_LIST - if ((itr->second->GetSession()->GetSecurity() > SEC_PLAYER && !gmInWhoList)) - continue; - } - - // check if target is globally visible for player - if (!(itr->second->IsVisibleGloballyFor(_player))) - continue; - - // check if target's level is in level range - uint32 lvl = itr->second->getLevel(); - if (lvl < level_min || lvl > level_max) - continue; - - // check if class matches classmask - uint32 class_ = itr->second->getClass(); - if (!(classmask & (1 << class_))) - continue; - - // check if race matches racemask - uint32 race = itr->second->getRace(); - if (!(racemask & (1 << race))) - continue; - - uint32 pzoneid = itr->second->GetZoneId(); - - bool z_show = true; - for(uint32 i = 0; i < zones_count; i++) - { - if(zoneids[i] == pzoneid) - { - z_show = true; - break; - } - - z_show = false; - } - if (!z_show) - continue; - - std::string pname = itr->second->GetName(); - std::wstring wpname; - if(!Utf8toWStr(pname,wpname)) - continue; - wstrToLower(wpname); - - if (!(wplayer_name.empty() || wpname.find(wplayer_name) != std::wstring::npos)) - continue; - - std::string gname = objmgr.GetGuildNameById(itr->second->GetGuildId()); - std::wstring wgname; - if(!Utf8toWStr(gname,wgname)) - continue; - wstrToLower(wgname); - - if (!(wguild_name.empty() || wgname.find(wguild_name) != std::wstring::npos)) - continue; - - std::string aname; - if(AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(itr->second->GetZoneId())) - aname = areaEntry->area_name[GetSessionDbcLocale()]; - - bool s_show = true; - for(uint32 i = 0; i < str_count; i++) - { - if (!str[i].empty()) - { - if (wgname.find(str[i]) != std::wstring::npos || - wpname.find(str[i]) != std::wstring::npos || - Utf8FitTo(aname, str[i]) ) - { - s_show = true; - break; - } - s_show = false; - } - } - if (!s_show) - continue; - - data << pname; // player name - data << gname; // guild name - data << uint32( lvl ); // player level - data << uint32( class_ ); // player class - data << uint32( race ); // player race - data << uint8(0); // new 2.4.0 - data << uint32( pzoneid ); // player zone id - - // 49 is maximum player count sent to client - if ((++clientcount) == 49) - break; - } - - data.put( 0, clientcount ); //insert right count - data.put( sizeof(uint32), clientcount ); //insert right count - - SendPacket(&data); - sLog.outDebug( "WORLD: Send SMSG_WHO Message" ); -} - -void WorldSession::HandleLogoutRequestOpcode( WorldPacket & /*recv_data*/ ) -{ - sLog.outDebug( "WORLD: Recvd CMSG_LOGOUT_REQUEST Message, security - %u", GetSecurity() ); - - if (uint64 lguid = GetPlayer()->GetLootGUID()) - DoLootRelease(lguid); - - //instant logout for admins, gm's, mod's - if( GetSecurity() > SEC_PLAYER ) - { - LogoutPlayer(true); - return; - } - - //Can not logout if... - if( GetPlayer()->isInCombat() || //...is in combat - GetPlayer()->duel || //...is in Duel - //...is jumping ...is falling - GetPlayer()->HasUnitMovementFlag(MOVEMENTFLAG_JUMPING | MOVEMENTFLAG_FALLING)) - { - WorldPacket data( SMSG_LOGOUT_RESPONSE, (2+4) ) ; - data << (uint8)0xC; - data << uint32(0); - data << uint8(0); - SendPacket( &data ); - LogoutRequest(0); - return; - } - - //instant logout in taverns/cities or on taxi - if(GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || GetPlayer()->isInFlight()) - { - LogoutPlayer(true); - return; - } - - // not set flags if player can't free move to prevent lost state at logout cancel - if(GetPlayer()->CanFreeMove()) - { - GetPlayer()->SetStandState(PLAYER_STATE_SIT); - - WorldPacket data( SMSG_FORCE_MOVE_ROOT, (8+4) ); // guess size - data.append(GetPlayer()->GetPackGUID()); - data << (uint32)2; - SendPacket( &data ); - GetPlayer()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); - } - - WorldPacket data( SMSG_LOGOUT_RESPONSE, 5 ); - data << uint32(0); - data << uint8(0); - SendPacket( &data ); - LogoutRequest(time(NULL)); -} - -void WorldSession::HandlePlayerLogoutOpcode( WorldPacket & /*recv_data*/ ) -{ - sLog.outDebug( "WORLD: Recvd CMSG_PLAYER_LOGOUT Message" ); -} - -void WorldSession::HandleLogoutCancelOpcode( WorldPacket & /*recv_data*/ ) -{ - sLog.outDebug( "WORLD: Recvd CMSG_LOGOUT_CANCEL Message" ); - - LogoutRequest(0); - - WorldPacket data( SMSG_LOGOUT_CANCEL_ACK, 0 ); - SendPacket( &data ); - - // not remove flags if can't free move - its not set in Logout request code. - if(GetPlayer()->CanFreeMove()) - { - //!we can move again - data.Initialize( SMSG_FORCE_MOVE_UNROOT, 8 ); // guess size - data.append(GetPlayer()->GetPackGUID()); - data << uint32(0); - SendPacket( &data ); - - //! Stand Up - GetPlayer()->SetStandState(PLAYER_STATE_NONE); - - //! DISABLE_ROTATE - GetPlayer()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); - } - - sLog.outDebug( "WORLD: sent SMSG_LOGOUT_CANCEL_ACK Message" ); -} - -void WorldSession::SendGMTicketGetTicket(uint32 status, char const* text) -{ - int len = text ? strlen(text) : 0; - WorldPacket data( SMSG_GMTICKET_GETTICKET, (4+len+1+4+2+4+4) ); - data << uint32(status); // standard 0x0A, 0x06 if text present - if(status == 6) - { - data << text; // ticket text - data << uint8(0x7); // ticket category - data << float(0); // time from ticket creation? - data << float(0); // const - data << float(0); // const - data << uint8(0); // const - data << uint8(0); // const - } - SendPacket( &data ); -} - -void WorldSession::HandleGMTicketGetTicketOpcode( WorldPacket & /*recv_data*/ ) -{ - WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 ); - data << (uint32)time(NULL); - data << (uint32)0; - SendPacket( &data ); - - uint64 guid; - Field *fields; - guid = GetPlayer()->GetGUID(); - - QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(ticket_id) FROM character_ticket WHERE guid = '%u'", GUID_LOPART(guid)); - - if (result) - { - int cnt; - fields = result->Fetch(); - cnt = fields[0].GetUInt32(); - delete result; - - if ( cnt > 0 ) - { - QueryResult *result2 = CharacterDatabase.PQuery("SELECT ticket_text FROM character_ticket WHERE guid = '%u'", GUID_LOPART(guid)); - if(result2) - { - Field *fields2 = result2->Fetch(); - SendGMTicketGetTicket(0x06,fields2[0].GetString()); - delete result2; - } - } - else - SendGMTicketGetTicket(0x0A,0); - } -} - -void WorldSession::HandleGMTicketUpdateTextOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,1); - - std::string ticketText; - recv_data >> ticketText; - - CharacterDatabase.escape_string(ticketText); - CharacterDatabase.PExecute("UPDATE character_ticket SET ticket_text = '%s' WHERE guid = '%u'", ticketText.c_str(), _player->GetGUIDLow()); -} - -void WorldSession::HandleGMTicketDeleteOpcode( WorldPacket & /*recv_data*/ ) -{ - uint32 guid = GetPlayer()->GetGUIDLow(); - - CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u' LIMIT 1",guid); - - WorldPacket data( SMSG_GMTICKET_DELETETICKET, 4 ); - data << uint32(9); - SendPacket( &data ); - - SendGMTicketGetTicket(0x0A, 0); -} - -void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 4*4+1+2*4); - - uint32 map; - float x, y, z; - std::string ticketText = ""; - uint32 unk1, unk2; - - recv_data >> map >> x >> y >> z; // last check 2.4.3 - recv_data >> ticketText; - - // recheck - CHECK_PACKET_SIZE(recv_data,4*4+(ticketText.size()+1)+2*4); - - recv_data >> unk1 >> unk2; - // note: the packet might contain more data, but the exact structure of that is unknown - - sLog.outDebug("TicketCreate: map %u, x %f, y %f, z %f, text %s, unk1 %u, unk2 %u", map, x, y, z, ticketText.c_str(), unk1, unk2); - - CharacterDatabase.escape_string(ticketText); - - QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM character_ticket WHERE guid = '%u'", _player->GetGUIDLow()); - - if (result) - { - int cnt; - Field *fields = result->Fetch(); - cnt = fields[0].GetUInt32(); - delete result; - - if ( cnt > 0 ) - { - WorldPacket data( SMSG_GMTICKET_CREATE, 4 ); - data << uint32(1); - SendPacket( &data ); - } - else - { - CharacterDatabase.PExecute("INSERT INTO character_ticket (guid,ticket_text) VALUES ('%u', '%s')", _player->GetGUIDLow(), ticketText.c_str()); - - WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 ); - data << (uint32)time(NULL); - data << (uint32)0; - SendPacket( &data ); - - data.Initialize( SMSG_GMTICKET_CREATE, 4 ); - data << uint32(2); - SendPacket( &data ); - DEBUG_LOG("update the ticket\n"); - - //TODO: Guard player map - HashMapHolder<Player>::MapType &m = ObjectAccessor::Instance().GetPlayers(); - for(HashMapHolder<Player>::MapType::iterator itr = m.begin(); itr != m.end(); ++itr) - { - if(itr->second->GetSession()->GetSecurity() >= SEC_GAMEMASTER && itr->second->isAcceptTickets()) - ChatHandler(itr->second).PSendSysMessage(LANG_COMMAND_TICKETNEW,GetPlayer()->GetName()); - } - } - } -} - -void WorldSession::HandleGMTicketSystemStatusOpcode( WorldPacket & /*recv_data*/ ) -{ - WorldPacket data( SMSG_GMTICKET_SYSTEMSTATUS,4 ); - data << uint32(1); // we can also disable ticket system by sending 0 value - - SendPacket( &data ); -} - -void WorldSession::HandleGMSurveySubmit( WorldPacket & recv_data) -{ - // GM survey is shown after SMSG_GM_TICKET_STATUS_UPDATE with status = 3 - CHECK_PACKET_SIZE(recv_data,4+4); - uint32 x; - recv_data >> x; // answer range? (6 = 0-5?) - sLog.outDebug("SURVEY: X = %u", x); - - uint8 result[10]; - memset(result, 0, sizeof(result)); - for( int i = 0; i < 10; ++i) - { - CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+4); - uint32 questionID; - recv_data >> questionID; // GMSurveyQuestions.dbc - if (!questionID) - break; - - CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+1+1); - uint8 value; - std::string unk_text; - recv_data >> value; // answer - recv_data >> unk_text; // always empty? - - result[i] = value; - sLog.outDebug("SURVEY: ID %u, value %u, text %s", questionID, value, unk_text.c_str()); - } - - CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+1); - std::string comment; - recv_data >> comment; // addional comment - sLog.outDebug("SURVEY: comment %s", comment.c_str()); - - // TODO: chart this data in some way -} - -void WorldSession::HandleTogglePvP( WorldPacket & recv_data ) -{ - // this opcode can be used in two ways: Either set explicit new status or toggle old status - if(recv_data.size() == 1) - { - bool newPvPStatus; - recv_data >> newPvPStatus; - GetPlayer()->ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP, newPvPStatus); - } - else - { - GetPlayer()->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP); - } - - if(GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP)) - { - if(!GetPlayer()->IsPvP() || GetPlayer()->pvpInfo.endTimer != 0) - GetPlayer()->UpdatePvP(true, true); - } - else - { - if(!GetPlayer()->pvpInfo.inHostileArea && GetPlayer()->IsPvP()) - GetPlayer()->pvpInfo.endTimer = time(NULL); // start toggle-off - } -} - -void WorldSession::HandleZoneUpdateOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,4); - - uint32 newZone; - recv_data >> newZone; - - sLog.outDetail("WORLD: Recvd ZONE_UPDATE: %u", newZone); - - if(newZone != _player->GetZoneId()) - GetPlayer()->SendInitWorldStates(); // only if really enters to new zone, not just area change, works strange... - - GetPlayer()->UpdateZone(newZone); -} - -void WorldSession::HandleSetTargetOpcode( WorldPacket & recv_data ) -{ - // When this packet send? - CHECK_PACKET_SIZE(recv_data,8); - - uint64 guid ; - recv_data >> guid; - - _player->SetUInt32Value(UNIT_FIELD_TARGET,guid); - - // update reputation list if need - Unit* unit = ObjectAccessor::GetUnit(*_player, guid ); - if(!unit) - return; - - _player->SetFactionVisibleForFactionTemplateId(unit->getFaction()); -} - -void WorldSession::HandleSetSelectionOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,8); - - uint64 guid; - recv_data >> guid; - - _player->SetSelection(guid); - - // update reputation list if need - Unit* unit = ObjectAccessor::GetUnit(*_player, guid ); - if(!unit) - return; - - _player->SetFactionVisibleForFactionTemplateId(unit->getFaction()); -} - -void WorldSession::HandleStandStateChangeOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,1); - - sLog.outDebug( "WORLD: Received CMSG_STAND_STATE_CHANGE" ); - uint8 animstate; - recv_data >> animstate; - - _player->SetStandState(animstate); -} - -void WorldSession::HandleFriendListOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 4); - sLog.outDebug( "WORLD: Received CMSG_CONTACT_LIST" ); - uint32 unk; - recv_data >> unk; - sLog.outDebug("unk value is %u", unk); - _player->GetSocial()->SendSocialList(); -} - -void WorldSession::HandleAddFriendOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 1+1); - - sLog.outDebug( "WORLD: Received CMSG_ADD_FRIEND" ); - - std::string friendName = GetMangosString(LANG_FRIEND_IGNORE_UNKNOWN); - std::string friendNote; - FriendsResult friendResult = FRIEND_NOT_FOUND; - Player *pFriend = NULL; - uint64 friendGuid = 0; - - recv_data >> friendName; - - // recheck - CHECK_PACKET_SIZE(recv_data, (friendName.size()+1)+1); - - recv_data >> friendNote; - - if(!normalizePlayerName(friendName)) - return; - - CharacterDatabase.escape_string(friendName); // prevent SQL injection - normal name don't must changed by this call - - sLog.outDebug( "WORLD: %s asked to add friend : '%s'", - GetPlayer()->GetName(), friendName.c_str() ); - - friendGuid = objmgr.GetPlayerGUIDByName(friendName); - - if(friendGuid) - { - pFriend = ObjectAccessor::FindPlayer(friendGuid); - if(pFriend==GetPlayer()) - friendResult = FRIEND_SELF; - else if(GetPlayer()->GetTeam()!=objmgr.GetPlayerTeamByGUID(friendGuid) && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND) && GetSecurity() < SEC_MODERATOR) - friendResult = FRIEND_ENEMY; - else if(GetPlayer()->GetSocial()->HasFriend(GUID_LOPART(friendGuid))) - friendResult = FRIEND_ALREADY; - } - - if (friendGuid && friendResult==FRIEND_NOT_FOUND) - { - if( pFriend && pFriend->IsInWorld() && pFriend->IsVisibleGloballyFor(GetPlayer())) - friendResult = FRIEND_ADDED_ONLINE; - else - friendResult = FRIEND_ADDED_OFFLINE; - - if(!_player->GetSocial()->AddToSocialList(GUID_LOPART(friendGuid), false)) - { - friendResult = FRIEND_LIST_FULL; - sLog.outDebug( "WORLD: %s's friend list is full.", GetPlayer()->GetName()); - } - - _player->GetSocial()->SetFriendNote(GUID_LOPART(friendGuid), friendNote); - - sLog.outDebug( "WORLD: %s Guid found '%u'.", friendName.c_str(), GUID_LOPART(friendGuid)); - } - else if(friendResult==FRIEND_ALREADY) - { - sLog.outDebug( "WORLD: %s Guid Already a Friend.", friendName.c_str() ); - } - else if(friendResult==FRIEND_SELF) - { - sLog.outDebug( "WORLD: %s Guid can't add himself.", friendName.c_str() ); - } - else - { - sLog.outDebug( "WORLD: %s Guid not found.", friendName.c_str() ); - } - - sSocialMgr.SendFriendStatus(GetPlayer(), friendResult, GUID_LOPART(friendGuid), friendName, false); - - sLog.outDebug( "WORLD: Sent (SMSG_FRIEND_STATUS)" ); -} - -void WorldSession::HandleDelFriendOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 8); - - uint64 FriendGUID; - - sLog.outDebug( "WORLD: Received CMSG_DEL_FRIEND" ); - - recv_data >> FriendGUID; - - _player->GetSocial()->RemoveFromSocialList(GUID_LOPART(FriendGUID), false); - - sSocialMgr.SendFriendStatus(GetPlayer(), FRIEND_REMOVED, GUID_LOPART(FriendGUID), "", false); - - sLog.outDebug( "WORLD: Sent motd (SMSG_FRIEND_STATUS)" ); -} - -void WorldSession::HandleAddIgnoreOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,1); - - sLog.outDebug( "WORLD: Received CMSG_ADD_IGNORE" ); - - std::string IgnoreName = GetMangosString(LANG_FRIEND_IGNORE_UNKNOWN); - FriendsResult ignoreResult = FRIEND_IGNORE_NOT_FOUND; - uint64 IgnoreGuid = 0; - - recv_data >> IgnoreName; - - if(!normalizePlayerName(IgnoreName)) - return; - - CharacterDatabase.escape_string(IgnoreName); // prevent SQL injection - normal name don't must changed by this call - - sLog.outDebug( "WORLD: %s asked to Ignore: '%s'", - GetPlayer()->GetName(), IgnoreName.c_str() ); - - IgnoreGuid = objmgr.GetPlayerGUIDByName(IgnoreName); - - if(IgnoreGuid) - { - if(IgnoreGuid==GetPlayer()->GetGUID()) - ignoreResult = FRIEND_IGNORE_SELF; - else - { - if( GetPlayer()->GetSocial()->HasIgnore(GUID_LOPART(IgnoreGuid)) ) - ignoreResult = FRIEND_IGNORE_ALREADY; - } - } - - if (IgnoreGuid && ignoreResult == FRIEND_IGNORE_NOT_FOUND) - { - ignoreResult = FRIEND_IGNORE_ADDED; - - _player->GetSocial()->AddToSocialList(GUID_LOPART(IgnoreGuid), true); - } - else if(ignoreResult==FRIEND_IGNORE_ALREADY) - { - sLog.outDebug( "WORLD: %s Guid Already Ignored.", IgnoreName.c_str() ); - } - else if(ignoreResult==FRIEND_IGNORE_SELF) - { - sLog.outDebug( "WORLD: %s Guid can't add himself.", IgnoreName.c_str() ); - } - else - { - sLog.outDebug( "WORLD: %s Guid not found.", IgnoreName.c_str() ); - } - - sSocialMgr.SendFriendStatus(GetPlayer(), ignoreResult, GUID_LOPART(IgnoreGuid), "", false); - - sLog.outDebug( "WORLD: Sent (SMSG_FRIEND_STATUS)" ); -} - -void WorldSession::HandleDelIgnoreOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 8); - - uint64 IgnoreGUID; - - sLog.outDebug( "WORLD: Received CMSG_DEL_IGNORE" ); - - recv_data >> IgnoreGUID; - - _player->GetSocial()->RemoveFromSocialList(GUID_LOPART(IgnoreGUID), true); - - sSocialMgr.SendFriendStatus(GetPlayer(), FRIEND_IGNORE_REMOVED, GUID_LOPART(IgnoreGUID), "", false); - - sLog.outDebug( "WORLD: Sent motd (SMSG_FRIEND_STATUS)" ); -} - -void WorldSession::HandleSetFriendNoteOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 8+1); - uint64 guid; - std::string note; - recv_data >> guid >> note; - _player->GetSocial()->SetFriendNote(guid, note); -} - -void WorldSession::HandleBugOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,4+4+1+4+1); - - uint32 suggestion, contentlen; - std::string content; - uint32 typelen; - std::string type; - - recv_data >> suggestion >> contentlen >> content; - - //recheck - CHECK_PACKET_SIZE(recv_data,4+4+(content.size()+1)+4+1); - - recv_data >> typelen >> type; - - if( suggestion == 0 ) - sLog.outDebug( "WORLD: Received CMSG_BUG [Bug Report]" ); - else - sLog.outDebug( "WORLD: Received CMSG_BUG [Suggestion]" ); - - sLog.outDebug( type.c_str( ) ); - sLog.outDebug( content.c_str( ) ); - - CharacterDatabase.escape_string(type); - CharacterDatabase.escape_string(content); - CharacterDatabase.PExecute ("INSERT INTO bugreport (type,content) VALUES('%s', '%s')", type.c_str( ), content.c_str( )); -} - -void WorldSession::HandleCorpseReclaimOpcode(WorldPacket &recv_data) -{ - CHECK_PACKET_SIZE(recv_data,8); - - sLog.outDetail("WORLD: Received CMSG_RECLAIM_CORPSE"); - if (GetPlayer()->isAlive()) - return; - - if (BattleGround * bg = _player->GetBattleGround()) - if(bg->isArena()) - return; - - // body not released yet - if(!GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) - return; - - Corpse *corpse = GetPlayer()->GetCorpse(); - - if (!corpse ) - return; - - // prevent resurrect before 30-sec delay after body release not finished - if(corpse->GetGhostTime() + GetPlayer()->GetCorpseReclaimDelay(corpse->GetType()==CORPSE_RESURRECTABLE_PVP) > time(NULL)) - return; - - float dist = corpse->GetDistance2d(GetPlayer()); - sLog.outDebug("Corpse 2D Distance: \t%f",dist); - if (dist > CORPSE_RECLAIM_RADIUS) - return; - - uint64 guid; - recv_data >> guid; - - // resurrect - GetPlayer()->ResurrectPlayer(GetPlayer()->InBattleGround() ? 1.0f : 0.5f); - - // spawn bones - GetPlayer()->SpawnCorpseBones(); - - GetPlayer()->SaveToDB(); -} - -void WorldSession::HandleResurrectResponseOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data,8+1); - - sLog.outDetail("WORLD: Received CMSG_RESURRECT_RESPONSE"); - - if(GetPlayer()->isAlive()) - return; - - uint64 guid; - uint8 status; - recv_data >> guid; - recv_data >> status; - - if(status == 0) - { - GetPlayer()->clearResurrectRequestData(); // reject - return; - } - - if(!GetPlayer()->isRessurectRequestedBy(guid)) - return; - - GetPlayer()->ResurectUsingRequestData(); - GetPlayer()->SaveToDB(); -} - -void WorldSession::HandleAreaTriggerOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data,4); - - sLog.outDebug("WORLD: Received CMSG_AREATRIGGER"); - - uint32 Trigger_ID; - - recv_data >> Trigger_ID; - sLog.outDebug("Trigger ID:%u",Trigger_ID); - - if(GetPlayer()->isInFlight()) - { - sLog.outDebug("Player '%s' (GUID: %u) in flight, ignore Area Trigger ID:%u",GetPlayer()->GetName(),GetPlayer()->GetGUIDLow(), Trigger_ID); - return; - } - - AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID); - if(!atEntry) - { - sLog.outDebug("Player '%s' (GUID: %u) send unknown (by DBC) Area Trigger ID:%u",GetPlayer()->GetName(),GetPlayer()->GetGUIDLow(), Trigger_ID); - return; - } - - if (GetPlayer()->GetMapId()!=atEntry->mapid) - { - sLog.outDebug("Player '%s' (GUID: %u) too far (trigger map: %u player map: %u), ignore Area Trigger ID: %u", GetPlayer()->GetName(), atEntry->mapid, GetPlayer()->GetMapId(), GetPlayer()->GetGUIDLow(), Trigger_ID); - return; - } - - // delta is safe radius - const float delta = 5.0f; - // check if player in the range of areatrigger - Player* pl = GetPlayer(); - - if (atEntry->radius > 0) - { - // if we have radius check it - float dist = pl->GetDistance(atEntry->x,atEntry->y,atEntry->z); - if(dist > atEntry->radius + delta) - { - sLog.outDebug("Player '%s' (GUID: %u) too far (radius: %f distance: %f), ignore Area Trigger ID: %u", - pl->GetName(), pl->GetGUIDLow(), atEntry->radius, dist, Trigger_ID); - return; - } - } - else - { - // we have only extent - float dx = pl->GetPositionX() - atEntry->x; - float dy = pl->GetPositionY() - atEntry->y; - float dz = pl->GetPositionZ() - atEntry->z; - double es = sin(atEntry->box_orientation); - double ec = cos(atEntry->box_orientation); - // calc rotated vector based on extent axis - double rotateDx = dx*ec - dy*es; - double rotateDy = dx*es + dy*ec; - - if( (fabs(rotateDx) > atEntry->box_x/2 + delta) || - (fabs(rotateDy) > atEntry->box_y/2 + delta) || - (fabs(dz) > atEntry->box_z/2 + delta) ) - { - sLog.outDebug("Player '%s' (GUID: %u) too far (1/2 box X: %f 1/2 box Y: %u 1/2 box Z: %u rotate dX: %f rotate dY: %f dZ:%f), ignore Area Trigger ID: %u", - pl->GetName(), pl->GetGUIDLow(), atEntry->box_x/2, atEntry->box_y/2, atEntry->box_z/2, rotateDx, rotateDy, dz, Trigger_ID); - return; - } - } - - if(Script->scriptAreaTrigger(GetPlayer(), atEntry)) - return; - - uint32 quest_id = objmgr.GetQuestForAreaTrigger( Trigger_ID ); - if( quest_id && GetPlayer()->isAlive() && GetPlayer()->IsActiveQuest(quest_id) ) - { - Quest const* pQuest = objmgr.GetQuestTemplate(quest_id); - if( pQuest ) - { - if(GetPlayer()->GetQuestStatus(quest_id) == QUEST_STATUS_INCOMPLETE) - GetPlayer()->AreaExploredOrEventHappens( quest_id ); - } - } - - if(objmgr.IsTavernAreaTrigger(Trigger_ID)) - { - // set resting flag we are in the inn - GetPlayer()->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING); - GetPlayer()->InnEnter(time(NULL), atEntry->mapid, atEntry->x, atEntry->y, atEntry->z); - GetPlayer()->SetRestType(REST_TYPE_IN_TAVERN); - - if(sWorld.IsFFAPvPRealm()) - GetPlayer()->RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); - - return; - } - - if(GetPlayer()->InBattleGround()) - { - BattleGround* bg = GetPlayer()->GetBattleGround(); - if(bg) - if(bg->GetStatus() == STATUS_IN_PROGRESS) - bg->HandleAreaTrigger(GetPlayer(), Trigger_ID); - - return; - } - - // NULL if all values default (non teleport trigger) - AreaTrigger const* at = objmgr.GetAreaTrigger(Trigger_ID); - if(!at) - return; - - if(!GetPlayer()->isGameMaster()) - { - uint32 missingLevel = 0; - if(GetPlayer()->getLevel() < at->requiredLevel && !sWorld.getConfig(CONFIG_INSTANCE_IGNORE_LEVEL)) - missingLevel = at->requiredLevel; - - // must have one or the other, report the first one that's missing - uint32 missingItem = 0; - if(at->requiredItem) - { - if(!GetPlayer()->HasItemCount(at->requiredItem, 1) && - (!at->requiredItem2 || !GetPlayer()->HasItemCount(at->requiredItem2, 1))) - missingItem = at->requiredItem; - } - else if(at->requiredItem2 && !GetPlayer()->HasItemCount(at->requiredItem2, 1)) - missingItem = at->requiredItem2; - - uint32 missingKey = 0; - if(GetPlayer()->GetDifficulty() == DIFFICULTY_HEROIC) - { - if(at->heroicKey) - { - if(!GetPlayer()->HasItemCount(at->heroicKey, 1) && - (!at->heroicKey2 || !GetPlayer()->HasItemCount(at->heroicKey2, 1))) - missingKey = at->heroicKey; - } - else if(at->heroicKey2 && !GetPlayer()->HasItemCount(at->heroicKey2, 1)) - missingKey = at->heroicKey2; - } - - uint32 missingQuest = 0; - if(at->requiredQuest && !GetPlayer()->GetQuestRewardStatus(at->requiredQuest)) - missingQuest = at->requiredQuest; - - if(missingLevel || missingItem || missingKey || missingQuest) - { - // TODO: all this is probably wrong - if(missingItem) - SendAreaTriggerMessage(GetMangosString(LANG_LEVEL_MINREQUIRED_AND_ITEM), at->requiredLevel, objmgr.GetItemPrototype(missingItem)->Name1); - else if(missingKey) - GetPlayer()->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_DIFFICULTY2); - else if(missingQuest) - SendAreaTriggerMessage(at->requiredFailedText.c_str()); - else if(missingLevel) - SendAreaTriggerMessage(GetMangosString(LANG_LEVEL_MINREQUIRED), missingLevel); - return; - } - } - - GetPlayer()->TeleportTo(at->target_mapId,at->target_X,at->target_Y,at->target_Z,at->target_Orientation,TELE_TO_NOT_LEAVE_TRANSPORT); -} - -void WorldSession::HandleUpdateAccountData(WorldPacket &/*recv_data*/) -{ - sLog.outDetail("WORLD: Received CMSG_UPDATE_ACCOUNT_DATA"); - //recv_data.hexlike(); -} - -void WorldSession::HandleRequestAccountData(WorldPacket& /*recv_data*/) -{ - sLog.outDetail("WORLD: Received CMSG_REQUEST_ACCOUNT_DATA"); - //recv_data.hexlike(); -} - -void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recv_data) -{ - CHECK_PACKET_SIZE(recv_data,1+2+1+1); - - sLog.outDebug( "WORLD: Received CMSG_SET_ACTION_BUTTON" ); - uint8 button, misc, type; - uint16 action; - recv_data >> button >> action >> misc >> type; - sLog.outDetail( "BUTTON: %u ACTION: %u TYPE: %u MISC: %u", button, action, type, misc ); - if(action==0) - { - sLog.outDetail( "MISC: Remove action from button %u", button ); - - GetPlayer()->removeActionButton(button); - } - else - { - if(type==ACTION_BUTTON_MACRO || type==ACTION_BUTTON_CMACRO) - { - sLog.outDetail( "MISC: Added Macro %u into button %u", action, button ); - GetPlayer()->addActionButton(button,action,type,misc); - } - else if(type==ACTION_BUTTON_SPELL) - { - sLog.outDetail( "MISC: Added Action %u into button %u", action, button ); - GetPlayer()->addActionButton(button,action,type,misc); - } - else if(type==ACTION_BUTTON_ITEM) - { - sLog.outDetail( "MISC: Added Item %u into button %u", action, button ); - GetPlayer()->addActionButton(button,action,type,misc); - } - else - sLog.outError( "MISC: Unknown action button type %u for action %u into button %u", type, action, button ); - } -} - -void WorldSession::HandleCompleteCinema( WorldPacket & /*recv_data*/ ) -{ - DEBUG_LOG( "WORLD: Player is watching cinema" ); -} - -void WorldSession::HandleNextCinematicCamera( WorldPacket & /*recv_data*/ ) -{ - DEBUG_LOG( "WORLD: Which movie to play" ); -} - -void WorldSession::HandleMoveTimeSkippedOpcode( WorldPacket & /*recv_data*/ ) -{ - /* WorldSession::Update( getMSTime() );*/ - DEBUG_LOG( "WORLD: Time Lag/Synchronization Resent/Update" ); - - /* - CHECK_PACKET_SIZE(recv_data,8+4); - uint64 guid; - uint32 time_skipped; - recv_data >> guid; - recv_data >> time_skipped; - sLog.outDebug( "WORLD: CMSG_MOVE_TIME_SKIPPED" ); - - /// TODO - must be need use in mangos - We substract server Lags to move time ( AntiLags ) - for exmaple - GetPlayer()->ModifyLastMoveTime( -int32(time_skipped) ); - */ -} - -void WorldSession::HandleFeatherFallAck(WorldPacket &/*recv_data*/) -{ - DEBUG_LOG("WORLD: CMSG_MOVE_FEATHER_FALL_ACK"); -} - -void WorldSession::HandleMoveUnRootAck(WorldPacket&/* recv_data*/) -{ - /* - CHECK_PACKET_SIZE(recv_data,8+8+4+4+4+4+4); - - sLog.outDebug( "WORLD: CMSG_FORCE_MOVE_UNROOT_ACK" ); - recv_data.hexlike(); - uint64 guid; - uint64 unknown1; - uint32 unknown2; - float PositionX; - float PositionY; - float PositionZ; - float Orientation; - - recv_data >> guid; - recv_data >> unknown1; - recv_data >> unknown2; - recv_data >> PositionX; - recv_data >> PositionY; - recv_data >> PositionZ; - recv_data >> Orientation; - - // TODO for later may be we can use for anticheat - DEBUG_LOG("Guid " I64FMTD,guid); - DEBUG_LOG("unknown1 " I64FMTD,unknown1); - DEBUG_LOG("unknown2 %u",unknown2); - DEBUG_LOG("X %f",PositionX); - DEBUG_LOG("Y %f",PositionY); - DEBUG_LOG("Z %f",PositionZ); - DEBUG_LOG("O %f",Orientation); - */ -} - -void WorldSession::HandleMoveRootAck(WorldPacket&/* recv_data*/) -{ - /* - CHECK_PACKET_SIZE(recv_data,8+8+4+4+4+4+4); - - sLog.outDebug( "WORLD: CMSG_FORCE_MOVE_ROOT_ACK" ); - recv_data.hexlike(); - uint64 guid; - uint64 unknown1; - uint32 unknown2; - float PositionX; - float PositionY; - float PositionZ; - float Orientation; - - recv_data >> guid; - recv_data >> unknown1; - recv_data >> unknown2; - recv_data >> PositionX; - recv_data >> PositionY; - recv_data >> PositionZ; - recv_data >> Orientation; - - // for later may be we can use for anticheat - DEBUG_LOG("Guid " I64FMTD,guid); - DEBUG_LOG("unknown1 " I64FMTD,unknown1); - DEBUG_LOG("unknown1 %u",unknown2); - DEBUG_LOG("X %f",PositionX); - DEBUG_LOG("Y %f",PositionY); - DEBUG_LOG("Z %f",PositionZ); - DEBUG_LOG("O %f",Orientation); - */ -} - -void WorldSession::HandleMoveTeleportAck(WorldPacket&/* recv_data*/) -{ - /* - CHECK_PACKET_SIZE(recv_data,8+4); - - sLog.outDebug("MSG_MOVE_TELEPORT_ACK"); - uint64 guid; - uint32 flags, time; - - recv_data >> guid; - recv_data >> flags >> time; - DEBUG_LOG("Guid " I64FMTD,guid); - DEBUG_LOG("Flags %u, time %u",flags, time/1000); - */ -} - -void WorldSession::HandleSetActionBar(WorldPacket& recv_data) -{ - CHECK_PACKET_SIZE(recv_data,1); - - uint8 ActionBar; - - recv_data >> ActionBar; - - if(!GetPlayer()) // ignore until not logged (check needed because STATUS_AUTHED) - { - if(ActionBar!=0) - sLog.outError("WorldSession::HandleSetActionBar in not logged state with value: %u, ignored",uint32(ActionBar)); - return; - } - - GetPlayer()->SetByteValue(PLAYER_FIELD_BYTES, 2, ActionBar); -} - -void WorldSession::HandleWardenDataOpcode(WorldPacket& /*recv_data*/) -{ - /* - CHECK_PACKET_SIZE(recv_data,1); - - uint8 tmp; - recv_data >> tmp; - sLog.outDebug("Received opcode CMSG_WARDEN_DATA, not resolve.uint8 = %u",tmp); - */ -} - -void WorldSession::HandlePlayedTime(WorldPacket& /*recv_data*/) -{ - uint32 TotalTimePlayed = GetPlayer()->GetTotalPlayedTime(); - uint32 LevelPlayedTime = GetPlayer()->GetLevelPlayedTime(); - - WorldPacket data(SMSG_PLAYED_TIME, 8); - data << TotalTimePlayed; - data << LevelPlayedTime; - SendPacket(&data); -} - -void WorldSession::HandleInspectOpcode(WorldPacket& recv_data) -{ - CHECK_PACKET_SIZE(recv_data, 8); - - uint64 guid; - recv_data >> guid; - DEBUG_LOG("Inspected guid is " I64FMTD, guid); - - _player->SetSelection(guid); - - Player *plr = objmgr.GetPlayer(guid); - if(!plr) // wrong player - return; - - uint32 talent_points = 0x3D; - uint32 guid_size = plr->GetPackGUID().size(); - WorldPacket data(SMSG_INSPECT_TALENT, 4+talent_points); - data.append(plr->GetPackGUID()); - data << uint32(talent_points); - - // fill by 0 talents array - for(uint32 i = 0; i < talent_points; ++i) - data << uint8(0); - - if(sWorld.getConfig(CONFIG_TALENTS_INSPECTING) || _player->isGameMaster()) - { - // find class talent tabs (all players have 3 talent tabs) - uint32 const* talentTabIds = GetTalentTabPages(plr->getClass()); - - uint32 talentTabPos = 0; // pos of first talent rank in tab including all prev tabs - for(uint32 i = 0; i < 3; ++i) - { - uint32 talentTabId = talentTabIds[i]; - - // fill by real data - for(uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) - { - TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); - if(!talentInfo) - continue; - - // skip another tab talents - if(talentInfo->TalentTab != talentTabId) - continue; - - // find talent rank - uint32 curtalent_maxrank = 0; - for(uint32 k = 5; k > 0; --k) - { - if(talentInfo->RankID[k-1] && plr->HasSpell(talentInfo->RankID[k-1])) - { - curtalent_maxrank = k; - break; - } - } - - // not learned talent - if(!curtalent_maxrank) - continue; - - // 1 rank talent bit index - uint32 curtalent_index = talentTabPos + GetTalentInspectBitPosInTab(talentId); - - uint32 curtalent_rank_index = curtalent_index+curtalent_maxrank-1; - - // slot/offset in 7-bit bytes - uint32 curtalent_rank_slot7 = curtalent_rank_index / 7; - uint32 curtalent_rank_offset7 = curtalent_rank_index % 7; - - // rank pos with skipped 8 bit - uint32 curtalent_rank_index2 = curtalent_rank_slot7 * 8 + curtalent_rank_offset7; - - // slot/offset in 8-bit bytes with skipped high bit - uint32 curtalent_rank_slot = curtalent_rank_index2 / 8; - uint32 curtalent_rank_offset = curtalent_rank_index2 % 8; - - // apply mask - uint32 val = data.read<uint8>(guid_size + 4 + curtalent_rank_slot); - val |= (1 << curtalent_rank_offset); - data.put<uint8>(guid_size + 4 + curtalent_rank_slot, val & 0xFF); - } - - talentTabPos += GetTalentTabInspectBitSize(talentTabId); - } - } - - SendPacket(&data); -} - -void WorldSession::HandleInspectHonorStatsOpcode(WorldPacket& recv_data) -{ - CHECK_PACKET_SIZE(recv_data, 8); - - uint64 guid; - recv_data >> guid; - - Player *player = objmgr.GetPlayer(guid); - - if(!player) - { - sLog.outError("InspectHonorStats: WTF, player not found..."); - return; - } - - WorldPacket data(MSG_INSPECT_HONOR_STATS, 8+1+4*4); - data << uint64(player->GetGUID()); - data << uint8(player->GetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY)); - data << uint32(player->GetUInt32Value(PLAYER_FIELD_KILLS)); - data << uint32(player->GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION)); - data << uint32(player->GetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION)); - data << uint32(player->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS)); - SendPacket(&data); -} - -void WorldSession::HandleWorldTeleportOpcode(WorldPacket& recv_data) -{ - CHECK_PACKET_SIZE(recv_data,4+4+4+4+4+4); - - // write in client console: worldport 469 452 6454 2536 180 or /console worldport 469 452 6454 2536 180 - // Received opcode CMSG_WORLD_TELEPORT - // Time is ***, map=469, x=452.000000, y=6454.000000, z=2536.000000, orient=3.141593 - - //sLog.outDebug("Received opcode CMSG_WORLD_TELEPORT"); - - if(GetPlayer()->isInFlight()) - { - sLog.outDebug("Player '%s' (GUID: %u) in flight, ignore worldport command.",GetPlayer()->GetName(),GetPlayer()->GetGUIDLow()); - return; - } - - uint32 time; - uint32 mapid; - float PositionX; - float PositionY; - float PositionZ; - float Orientation; - - recv_data >> time; // time in m.sec. - recv_data >> mapid; - recv_data >> PositionX; - recv_data >> PositionY; - recv_data >> PositionZ; - recv_data >> Orientation; // o (3.141593 = 180 degrees) - DEBUG_LOG("Time %u sec, map=%u, x=%f, y=%f, z=%f, orient=%f", time/1000, mapid, PositionX, PositionY, PositionZ, Orientation); - - if (GetSecurity() >= SEC_ADMINISTRATOR) - GetPlayer()->TeleportTo(mapid,PositionX,PositionY,PositionZ,Orientation); - else - SendNotification("You do not have permission to perform that function"); - sLog.outDebug("Received worldport command from player %s", GetPlayer()->GetName()); -} - -void WorldSession::HandleWhoisOpcode(WorldPacket& recv_data) -{ - CHECK_PACKET_SIZE(recv_data, 1); - - sLog.outDebug("Received opcode CMSG_WHOIS"); - std::string charname, acc, email, lastip, msg; - recv_data >> charname; - - if (GetSecurity() < SEC_ADMINISTRATOR) - { - SendNotification("You do not have permission to perform that function"); - return; - } - - if(charname.empty()) - { - SendNotification("Please provide character name"); - return; - } - - uint32 accid; - Field *fields; - - Player *plr = objmgr.GetPlayer(charname.c_str()); - - if(plr) - accid = plr->GetSession()->GetAccountId(); - else - { - SendNotification("Player %s not found or offline", charname.c_str()); - return; - } - - if(!accid) - { - SendNotification("Account for character %s not found", charname.c_str()); - return; - } - - QueryResult *result = loginDatabase.PQuery("SELECT username,email,last_ip FROM account WHERE id=%u", accid); - if(result) - { - fields = result->Fetch(); - acc = fields[0].GetCppString(); - if(acc.empty()) - acc = "Unknown"; - email = fields[1].GetCppString(); - if(email.empty()) - email = "Unknown"; - lastip = fields[2].GetCppString(); - if(lastip.empty()) - lastip = "Unknown"; - msg = charname + "'s " + "account is " + acc + ", e-mail: " + email + ", last ip: " + lastip; - - WorldPacket data(SMSG_WHOIS, msg.size()+1); - data << msg; - _player->GetSession()->SendPacket(&data); - } - else - SendNotification("Account for character %s not found", charname.c_str()); - - delete result; - sLog.outDebug("Received whois command from player %s for character %s", GetPlayer()->GetName(), charname.c_str()); -} - -void WorldSession::HandleReportSpamOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 1+8); - sLog.outDebug("WORLD: CMSG_REPORT_SPAM"); - recv_data.hexlike(); - - uint8 spam_type; // 0 - mail, 1 - chat - uint64 spammer_guid; - uint32 unk1, unk2, unk3, unk4 = 0; - std::string description = ""; - recv_data >> spam_type; // unk 0x01 const, may be spam type (mail/chat) - recv_data >> spammer_guid; // player guid - switch(spam_type) - { - case 0: - CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4); - recv_data >> unk1; // const 0 - recv_data >> unk2; // probably mail id - recv_data >> unk3; // const 0 - break; - case 1: - CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4+4+1); - recv_data >> unk1; // probably language - recv_data >> unk2; // message type? - recv_data >> unk3; // probably channel id - recv_data >> unk4; // unk random value - recv_data >> description; // spam description string (messagetype, channel name, player name, message) - break; - } - - // NOTE: all chat messages from this spammer automatically ignored by spam reporter until logout in case chat spam. - // if it's mail spam - ALL mails from this spammer automatically removed by client - - // Complaint Received message - WorldPacket data(SMSG_COMPLAIN_RESULT, 1); - data << uint8(0); - SendPacket(&data); - - sLog.outDebug("REPORT SPAM: type %u, guid %u, unk1 %u, unk2 %u, unk3 %u, unk4 %u, message %s", spam_type, GUID_LOPART(spammer_guid), unk1, unk2, unk3, unk4, description.c_str()); -} - -void WorldSession::HandleRealmStateRequestOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 4); - - sLog.outDebug("CMSG_REALM_SPLIT"); - - uint32 unk; - std::string split_date = "01/01/01"; - recv_data >> unk; - - WorldPacket data(SMSG_REALM_SPLIT, 4+4+split_date.size()+1); - data << unk; - data << uint32(0x00000000); // realm split state - // split states: - // 0x0 realm normal - // 0x1 realm split - // 0x2 realm split pending - data << split_date; - SendPacket(&data); - //sLog.outDebug("response sent %u", unk); -} - -void WorldSession::HandleFarSightOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 1); - - sLog.outDebug("WORLD: CMSG_FAR_SIGHT"); - //recv_data.hexlike(); - - uint8 unk; - recv_data >> unk; - - switch(unk) - { - case 0: - //WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0) - //SendPacket(&data); - //_player->SetUInt64Value(PLAYER_FARSIGHT, 0); - sLog.outDebug("Removed FarSight from player %u", _player->GetGUIDLow()); - break; - case 1: - sLog.outDebug("Added FarSight " I64FMTD " to player %u", _player->GetUInt64Value(PLAYER_FARSIGHT), _player->GetGUIDLow()); - break; - } -} - -void WorldSession::HandleChooseTitleOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 4); - - sLog.outDebug("CMSG_SET_TITLE"); - - int32 title; - recv_data >> title; - - // -1 at none - if(title > 0 && title < 64) - { - if(!GetPlayer()->HasFlag64(PLAYER__FIELD_KNOWN_TITLES,uint64(1) << title)) - return; - } - else - title = 0; - - GetPlayer()->SetUInt32Value(PLAYER_CHOSEN_TITLE, title); -} - -void WorldSession::HandleAllowMoveAckOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 4+4); - - sLog.outDebug("CMSG_ALLOW_MOVE_ACK"); - - uint32 counter, time_; - recv_data >> counter >> time_; - - // time_ seems always more than getMSTime() - uint32 diff = getMSTimeDiff(getMSTime(),time_); - - sLog.outDebug("response sent: counter %u, time %u (HEX: %X), ms. time %u, diff %u", counter, time_, time_, getMSTime(), diff); -} - -void WorldSession::HandleResetInstancesOpcode( WorldPacket & /*recv_data*/ ) -{ - sLog.outDebug("WORLD: CMSG_RESET_INSTANCES"); - Group *pGroup = _player->GetGroup(); - if(pGroup) - { - if(pGroup->IsLeader(_player->GetGUID())) - pGroup->ResetInstances(INSTANCE_RESET_ALL, _player); - } - else - _player->ResetInstances(INSTANCE_RESET_ALL); -} - -void WorldSession::HandleDungeonDifficultyOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 4); - - sLog.outDebug("MSG_SET_DUNGEON_DIFFICULTY"); - - uint32 mode; - recv_data >> mode; - - if(mode == _player->GetDifficulty()) - return; - - if(mode > DIFFICULTY_HEROIC) - { - sLog.outError("WorldSession::HandleDungeonDifficultyOpcode: player %d sent an invalid instance mode %d!", _player->GetGUIDLow(), mode); - return; - } - - // cannot reset while in an instance - Map *map = _player->GetMap(); - if(map && map->IsDungeon()) - { - sLog.outError("WorldSession::HandleDungeonDifficultyOpcode: player %d tried to reset the instance while inside!", _player->GetGUIDLow()); - return; - } - - if(_player->getLevel() < LEVELREQUIREMENT_HEROIC) - return; - Group *pGroup = _player->GetGroup(); - if(pGroup) - { - if(pGroup->IsLeader(_player->GetGUID())) - { - // the difficulty is set even if the instances can't be reset - //_player->SendDungeonDifficulty(true); - pGroup->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY, _player); - pGroup->SetDifficulty(mode); - } - } - else - { - _player->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY); - _player->SetDifficulty(mode); - } -} - -void WorldSession::HandleNewUnknownOpcode( WorldPacket & recv_data ) -{ - sLog.outDebug("New Unknown Opcode %u", recv_data.GetOpcode()); - recv_data.hexlike(); - /* - New Unknown Opcode 837 - STORAGE_SIZE: 60 - 02 00 00 00 00 00 00 00 | 00 00 00 00 01 20 00 00 - 89 EB 33 01 71 5C 24 C4 | 15 03 35 45 74 47 8B 42 - BA B8 1B 40 00 00 00 00 | 00 00 00 00 77 66 42 BF - 23 91 26 3F 00 00 60 41 | 00 00 00 00 - - New Unknown Opcode 837 - STORAGE_SIZE: 44 - 02 00 00 00 00 00 00 00 | 00 00 00 00 00 00 80 00 - 7B 80 34 01 84 EA 2B C4 | 5F A1 36 45 C9 39 1C 42 - BA B8 1B 40 CE 06 00 00 | 00 00 80 3F - */ -} - -void WorldSession::HandleDismountOpcode( WorldPacket & /*recv_data*/ ) -{ - sLog.outDebug("WORLD: CMSG_CANCEL_MOUNT_AURA"); - //recv_data.hexlike(); - - //If player is not mounted, so go out :) - if (!_player->IsMounted()) // not blizz like; no any messages on blizz - { - ChatHandler(this).SendSysMessage(LANG_CHAR_NON_MOUNTED); - return; - } - - if(_player->isInFlight()) // not blizz like; no any messages on blizz - { - ChatHandler(this).SendSysMessage(LANG_YOU_IN_FLIGHT); - return; - } - - _player->Unmount(); - _player->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); -} - -void WorldSession::HandleMoveFlyModeChangeAckOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 8+4+4); - - // fly mode on/off - sLog.outDebug("WORLD: CMSG_MOVE_SET_CAN_FLY_ACK"); - //recv_data.hexlike(); - - uint64 guid; - uint32 unk; - uint32 flags; - - recv_data >> guid >> unk >> flags; - - _player->SetUnitMovementFlags(flags); - /* - on: - 25 00 00 00 00 00 00 00 | 00 00 00 00 00 00 80 00 - 85 4E A9 01 19 BA 7A C3 | 42 0D 70 44 44 B0 A8 42 - 78 15 94 40 39 03 00 00 | 00 00 80 3F - off: - 25 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 - 10 FD A9 01 19 BA 7A C3 | 42 0D 70 44 44 B0 A8 42 - 78 15 94 40 39 03 00 00 | 00 00 00 00 - */ -} - -void WorldSession::HandleRequestPetInfoOpcode( WorldPacket & /*recv_data */) -{ - /* - sLog.outDebug("WORLD: CMSG_REQUEST_PET_INFO"); - recv_data.hexlike(); - */ -} - -void WorldSession::HandleSetTaxiBenchmarkOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 1); - - uint8 mode; - recv_data >> mode; - - sLog.outDebug("Client used \"/timetest %d\" command", mode); -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Language.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "Player.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "WorldSession.h"
+#include "Auth/BigNumber.h"
+#include "Auth/Sha1.h"
+#include "UpdateData.h"
+#include "LootMgr.h"
+#include "Chat.h"
+#include "ScriptCalls.h"
+#include <zlib/zlib.h>
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "Object.h"
+#include "BattleGround.h"
+#include "SpellAuras.h"
+#include "Pet.h"
+#include "SocialMgr.h"
+
+void WorldSession::HandleRepopRequestOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug( "WORLD: Recvd CMSG_REPOP_REQUEST Message" );
+
+ if(GetPlayer()->isAlive()||GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
+ return;
+
+ // the world update order is sessions, players, creatures
+ // the netcode runs in parallel with all of these
+ // creatures can kill players
+ // so if the server is lagging enough the player can
+ // release spirit after he's killed but before he is updated
+ if(GetPlayer()->getDeathState() == JUST_DIED)
+ {
+ sLog.outDebug("HandleRepopRequestOpcode: got request after player %s(%d) was killed and before he was updated", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow());
+ GetPlayer()->KillPlayer();
+ }
+
+ //this is spirit release confirm?
+ GetPlayer()->RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
+ GetPlayer()->BuildPlayerRepop();
+ GetPlayer()->RepopAtGraveyard();
+}
+
+void WorldSession::HandleWhoOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+4+1+1+4+4+4+4);
+
+ sLog.outDebug( "WORLD: Recvd CMSG_WHO Message" );
+ //recv_data.hexlike();
+
+ uint32 clientcount = 0;
+
+ uint32 level_min, level_max, racemask, classmask, zones_count, str_count;
+ uint32 zoneids[10]; // 10 is client limit
+ std::string player_name, guild_name;
+
+ recv_data >> level_min; // maximal player level, default 0
+ recv_data >> level_max; // minimal player level, default 100
+ recv_data >> player_name; // player name, case sensitive...
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+1+4+4+4+4);
+
+ recv_data >> guild_name; // guild name, case sensitive...
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+(guild_name.size()+1)+4+4+4+4);
+
+ recv_data >> racemask; // race mask
+ recv_data >> classmask; // class mask
+ recv_data >> zones_count; // zones count, client limit=10 (2.0.10)
+
+ if(zones_count > 10)
+ return; // can't be received from real client or broken packet
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+(guild_name.size()+1)+4+4+4+(4*zones_count)+4);
+
+ for(uint32 i = 0; i < zones_count; i++)
+ {
+ uint32 temp;
+ recv_data >> temp; // zone id, 0 if zone is unknown...
+ zoneids[i] = temp;
+ sLog.outDebug("Zone %u: %u", i, zoneids[i]);
+ }
+
+ recv_data >> str_count; // user entered strings count, client limit=4 (checked on 2.0.10)
+
+ if(str_count > 4)
+ return; // can't be received from real client or broken packet
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+(guild_name.size()+1)+4+4+4+(4*zones_count)+4+(1*str_count));
+
+ sLog.outDebug("Minlvl %u, maxlvl %u, name %s, guild %s, racemask %u, classmask %u, zones %u, strings %u", level_min, level_max, player_name.c_str(), guild_name.c_str(), racemask, classmask, zones_count, str_count);
+
+ std::wstring str[4]; // 4 is client limit
+ for(uint32 i = 0; i < str_count; i++)
+ {
+ // recheck (have one more byte)
+ CHECK_PACKET_SIZE(recv_data,recv_data.rpos());
+
+ std::string temp;
+ recv_data >> temp; // user entered string, it used as universal search pattern(guild+player name)?
+
+ if(!Utf8toWStr(temp,str[i]))
+ continue;
+
+ wstrToLower(str[i]);
+
+ sLog.outDebug("String %u: %s", i, str[i].c_str());
+ }
+
+ std::wstring wplayer_name;
+ std::wstring wguild_name;
+ if(!(Utf8toWStr(player_name, wplayer_name) && Utf8toWStr(guild_name, wguild_name)))
+ return;
+ wstrToLower(wplayer_name);
+ wstrToLower(wguild_name);
+
+ // client send in case not set max level value 100 but mangos support 255 max level,
+ // update it to show GMs with characters after 100 level
+ if(level_max >= 100)
+ level_max = 255;
+
+ uint32 team = _player->GetTeam();
+ uint32 security = GetSecurity();
+ bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST);
+ bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST);
+
+ WorldPacket data( SMSG_WHO, 50 ); // guess size
+ data << clientcount; // clientcount place holder
+ data << clientcount; // clientcount place holder
+
+ //TODO: Guard Player map
+ HashMapHolder<Player>::MapType& m = ObjectAccessor::Instance().GetPlayers();
+ for(HashMapHolder<Player>::MapType::iterator itr = m.begin(); itr != m.end(); ++itr)
+ {
+ if (security == SEC_PLAYER)
+ {
+ // player can see member of other team only if CONFIG_ALLOW_TWO_SIDE_WHO_LIST
+ if (itr->second->GetTeam() != team && !allowTwoSideWhoList )
+ continue;
+
+ // player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if CONFIG_GM_IN_WHO_LIST
+ if ((itr->second->GetSession()->GetSecurity() > SEC_PLAYER && !gmInWhoList))
+ continue;
+ }
+
+ // check if target is globally visible for player
+ if (!(itr->second->IsVisibleGloballyFor(_player)))
+ continue;
+
+ // check if target's level is in level range
+ uint32 lvl = itr->second->getLevel();
+ if (lvl < level_min || lvl > level_max)
+ continue;
+
+ // check if class matches classmask
+ uint32 class_ = itr->second->getClass();
+ if (!(classmask & (1 << class_)))
+ continue;
+
+ // check if race matches racemask
+ uint32 race = itr->second->getRace();
+ if (!(racemask & (1 << race)))
+ continue;
+
+ uint32 pzoneid = itr->second->GetZoneId();
+
+ bool z_show = true;
+ for(uint32 i = 0; i < zones_count; i++)
+ {
+ if(zoneids[i] == pzoneid)
+ {
+ z_show = true;
+ break;
+ }
+
+ z_show = false;
+ }
+ if (!z_show)
+ continue;
+
+ std::string pname = itr->second->GetName();
+ std::wstring wpname;
+ if(!Utf8toWStr(pname,wpname))
+ continue;
+ wstrToLower(wpname);
+
+ if (!(wplayer_name.empty() || wpname.find(wplayer_name) != std::wstring::npos))
+ continue;
+
+ std::string gname = objmgr.GetGuildNameById(itr->second->GetGuildId());
+ std::wstring wgname;
+ if(!Utf8toWStr(gname,wgname))
+ continue;
+ wstrToLower(wgname);
+
+ if (!(wguild_name.empty() || wgname.find(wguild_name) != std::wstring::npos))
+ continue;
+
+ std::string aname;
+ if(AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(itr->second->GetZoneId()))
+ aname = areaEntry->area_name[GetSessionDbcLocale()];
+
+ bool s_show = true;
+ for(uint32 i = 0; i < str_count; i++)
+ {
+ if (!str[i].empty())
+ {
+ if (wgname.find(str[i]) != std::wstring::npos ||
+ wpname.find(str[i]) != std::wstring::npos ||
+ Utf8FitTo(aname, str[i]) )
+ {
+ s_show = true;
+ break;
+ }
+ s_show = false;
+ }
+ }
+ if (!s_show)
+ continue;
+
+ data << pname; // player name
+ data << gname; // guild name
+ data << uint32( lvl ); // player level
+ data << uint32( class_ ); // player class
+ data << uint32( race ); // player race
+ data << uint8(0); // new 2.4.0
+ data << uint32( pzoneid ); // player zone id
+
+ // 49 is maximum player count sent to client
+ if ((++clientcount) == 49)
+ break;
+ }
+
+ data.put( 0, clientcount ); //insert right count
+ data.put( sizeof(uint32), clientcount ); //insert right count
+
+ SendPacket(&data);
+ sLog.outDebug( "WORLD: Send SMSG_WHO Message" );
+}
+
+void WorldSession::HandleLogoutRequestOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug( "WORLD: Recvd CMSG_LOGOUT_REQUEST Message, security - %u", GetSecurity() );
+
+ if (uint64 lguid = GetPlayer()->GetLootGUID())
+ DoLootRelease(lguid);
+
+ //instant logout for admins, gm's, mod's
+ if( GetSecurity() > SEC_PLAYER )
+ {
+ LogoutPlayer(true);
+ return;
+ }
+
+ //Can not logout if...
+ if( GetPlayer()->isInCombat() || //...is in combat
+ GetPlayer()->duel || //...is in Duel
+ //...is jumping ...is falling
+ GetPlayer()->HasUnitMovementFlag(MOVEMENTFLAG_JUMPING | MOVEMENTFLAG_FALLING))
+ {
+ WorldPacket data( SMSG_LOGOUT_RESPONSE, (2+4) ) ;
+ data << (uint8)0xC;
+ data << uint32(0);
+ data << uint8(0);
+ SendPacket( &data );
+ LogoutRequest(0);
+ return;
+ }
+
+ //instant logout in taverns/cities or on taxi
+ if(GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || GetPlayer()->isInFlight())
+ {
+ LogoutPlayer(true);
+ return;
+ }
+
+ // not set flags if player can't free move to prevent lost state at logout cancel
+ if(GetPlayer()->CanFreeMove())
+ {
+ GetPlayer()->SetStandState(PLAYER_STATE_SIT);
+
+ WorldPacket data( SMSG_FORCE_MOVE_ROOT, (8+4) ); // guess size
+ data.append(GetPlayer()->GetPackGUID());
+ data << (uint32)2;
+ SendPacket( &data );
+ GetPlayer()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+ }
+
+ WorldPacket data( SMSG_LOGOUT_RESPONSE, 5 );
+ data << uint32(0);
+ data << uint8(0);
+ SendPacket( &data );
+ LogoutRequest(time(NULL));
+}
+
+void WorldSession::HandlePlayerLogoutOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug( "WORLD: Recvd CMSG_PLAYER_LOGOUT Message" );
+}
+
+void WorldSession::HandleLogoutCancelOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug( "WORLD: Recvd CMSG_LOGOUT_CANCEL Message" );
+
+ LogoutRequest(0);
+
+ WorldPacket data( SMSG_LOGOUT_CANCEL_ACK, 0 );
+ SendPacket( &data );
+
+ // not remove flags if can't free move - its not set in Logout request code.
+ if(GetPlayer()->CanFreeMove())
+ {
+ //!we can move again
+ data.Initialize( SMSG_FORCE_MOVE_UNROOT, 8 ); // guess size
+ data.append(GetPlayer()->GetPackGUID());
+ data << uint32(0);
+ SendPacket( &data );
+
+ //! Stand Up
+ GetPlayer()->SetStandState(PLAYER_STATE_NONE);
+
+ //! DISABLE_ROTATE
+ GetPlayer()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+ }
+
+ sLog.outDebug( "WORLD: sent SMSG_LOGOUT_CANCEL_ACK Message" );
+}
+
+void WorldSession::SendGMTicketGetTicket(uint32 status, char const* text)
+{
+ int len = text ? strlen(text) : 0;
+ WorldPacket data( SMSG_GMTICKET_GETTICKET, (4+len+1+4+2+4+4) );
+ data << uint32(status); // standard 0x0A, 0x06 if text present
+ if(status == 6)
+ {
+ data << text; // ticket text
+ data << uint8(0x7); // ticket category
+ data << float(0); // time from ticket creation?
+ data << float(0); // const
+ data << float(0); // const
+ data << uint8(0); // const
+ data << uint8(0); // const
+ }
+ SendPacket( &data );
+}
+
+void WorldSession::HandleGMTicketGetTicketOpcode( WorldPacket & /*recv_data*/ )
+{
+ WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 );
+ data << (uint32)time(NULL);
+ data << (uint32)0;
+ SendPacket( &data );
+
+ uint64 guid;
+ Field *fields;
+ guid = GetPlayer()->GetGUID();
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(ticket_id) FROM character_ticket WHERE guid = '%u'", GUID_LOPART(guid));
+
+ if (result)
+ {
+ int cnt;
+ fields = result->Fetch();
+ cnt = fields[0].GetUInt32();
+ delete result;
+
+ if ( cnt > 0 )
+ {
+ QueryResult *result2 = CharacterDatabase.PQuery("SELECT ticket_text FROM character_ticket WHERE guid = '%u'", GUID_LOPART(guid));
+ if(result2)
+ {
+ Field *fields2 = result2->Fetch();
+ SendGMTicketGetTicket(0x06,fields2[0].GetString());
+ delete result2;
+ }
+ }
+ else
+ SendGMTicketGetTicket(0x0A,0);
+ }
+}
+
+void WorldSession::HandleGMTicketUpdateTextOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ std::string ticketText;
+ recv_data >> ticketText;
+
+ CharacterDatabase.escape_string(ticketText);
+ CharacterDatabase.PExecute("UPDATE character_ticket SET ticket_text = '%s' WHERE guid = '%u'", ticketText.c_str(), _player->GetGUIDLow());
+}
+
+void WorldSession::HandleGMTicketDeleteOpcode( WorldPacket & /*recv_data*/ )
+{
+ uint32 guid = GetPlayer()->GetGUIDLow();
+
+ CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u' LIMIT 1",guid);
+
+ WorldPacket data( SMSG_GMTICKET_DELETETICKET, 4 );
+ data << uint32(9);
+ SendPacket( &data );
+
+ SendGMTicketGetTicket(0x0A, 0);
+}
+
+void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4*4+1+2*4);
+
+ uint32 map;
+ float x, y, z;
+ std::string ticketText = "";
+ uint32 unk1, unk2;
+
+ recv_data >> map >> x >> y >> z; // last check 2.4.3
+ recv_data >> ticketText;
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,4*4+(ticketText.size()+1)+2*4);
+
+ recv_data >> unk1 >> unk2;
+ // note: the packet might contain more data, but the exact structure of that is unknown
+
+ sLog.outDebug("TicketCreate: map %u, x %f, y %f, z %f, text %s, unk1 %u, unk2 %u", map, x, y, z, ticketText.c_str(), unk1, unk2);
+
+ CharacterDatabase.escape_string(ticketText);
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM character_ticket WHERE guid = '%u'", _player->GetGUIDLow());
+
+ if (result)
+ {
+ int cnt;
+ Field *fields = result->Fetch();
+ cnt = fields[0].GetUInt32();
+ delete result;
+
+ if ( cnt > 0 )
+ {
+ WorldPacket data( SMSG_GMTICKET_CREATE, 4 );
+ data << uint32(1);
+ SendPacket( &data );
+ }
+ else
+ {
+ CharacterDatabase.PExecute("INSERT INTO character_ticket (guid,ticket_text) VALUES ('%u', '%s')", _player->GetGUIDLow(), ticketText.c_str());
+
+ WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 );
+ data << (uint32)time(NULL);
+ data << (uint32)0;
+ SendPacket( &data );
+
+ data.Initialize( SMSG_GMTICKET_CREATE, 4 );
+ data << uint32(2);
+ SendPacket( &data );
+ DEBUG_LOG("update the ticket\n");
+
+ //TODO: Guard player map
+ HashMapHolder<Player>::MapType &m = ObjectAccessor::Instance().GetPlayers();
+ for(HashMapHolder<Player>::MapType::iterator itr = m.begin(); itr != m.end(); ++itr)
+ {
+ if(itr->second->GetSession()->GetSecurity() >= SEC_GAMEMASTER && itr->second->isAcceptTickets())
+ ChatHandler(itr->second).PSendSysMessage(LANG_COMMAND_TICKETNEW,GetPlayer()->GetName());
+ }
+ }
+ }
+}
+
+void WorldSession::HandleGMTicketSystemStatusOpcode( WorldPacket & /*recv_data*/ )
+{
+ WorldPacket data( SMSG_GMTICKET_SYSTEMSTATUS,4 );
+ data << uint32(1); // we can also disable ticket system by sending 0 value
+
+ SendPacket( &data );
+}
+
+void WorldSession::HandleGMSurveySubmit( WorldPacket & recv_data)
+{
+ // GM survey is shown after SMSG_GM_TICKET_STATUS_UPDATE with status = 3
+ CHECK_PACKET_SIZE(recv_data,4+4);
+ uint32 x;
+ recv_data >> x; // answer range? (6 = 0-5?)
+ sLog.outDebug("SURVEY: X = %u", x);
+
+ uint8 result[10];
+ memset(result, 0, sizeof(result));
+ for( int i = 0; i < 10; ++i)
+ {
+ CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+4);
+ uint32 questionID;
+ recv_data >> questionID; // GMSurveyQuestions.dbc
+ if (!questionID)
+ break;
+
+ CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+1+1);
+ uint8 value;
+ std::string unk_text;
+ recv_data >> value; // answer
+ recv_data >> unk_text; // always empty?
+
+ result[i] = value;
+ sLog.outDebug("SURVEY: ID %u, value %u, text %s", questionID, value, unk_text.c_str());
+ }
+
+ CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+1);
+ std::string comment;
+ recv_data >> comment; // addional comment
+ sLog.outDebug("SURVEY: comment %s", comment.c_str());
+
+ // TODO: chart this data in some way
+}
+
+void WorldSession::HandleTogglePvP( WorldPacket & recv_data )
+{
+ // this opcode can be used in two ways: Either set explicit new status or toggle old status
+ if(recv_data.size() == 1)
+ {
+ bool newPvPStatus;
+ recv_data >> newPvPStatus;
+ GetPlayer()->ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP, newPvPStatus);
+ }
+ else
+ {
+ GetPlayer()->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP);
+ }
+
+ if(GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP))
+ {
+ if(!GetPlayer()->IsPvP() || GetPlayer()->pvpInfo.endTimer != 0)
+ GetPlayer()->UpdatePvP(true, true);
+ }
+ else
+ {
+ if(!GetPlayer()->pvpInfo.inHostileArea && GetPlayer()->IsPvP())
+ GetPlayer()->pvpInfo.endTimer = time(NULL); // start toggle-off
+ }
+}
+
+void WorldSession::HandleZoneUpdateOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 newZone;
+ recv_data >> newZone;
+
+ sLog.outDetail("WORLD: Recvd ZONE_UPDATE: %u", newZone);
+
+ if(newZone != _player->GetZoneId())
+ GetPlayer()->SendInitWorldStates(); // only if really enters to new zone, not just area change, works strange...
+
+ GetPlayer()->UpdateZone(newZone);
+}
+
+void WorldSession::HandleSetTargetOpcode( WorldPacket & recv_data )
+{
+ // When this packet send?
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid ;
+ recv_data >> guid;
+
+ _player->SetUInt32Value(UNIT_FIELD_TARGET,guid);
+
+ // update reputation list if need
+ Unit* unit = ObjectAccessor::GetUnit(*_player, guid );
+ if(!unit)
+ return;
+
+ _player->SetFactionVisibleForFactionTemplateId(unit->getFaction());
+}
+
+void WorldSession::HandleSetSelectionOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+ recv_data >> guid;
+
+ _player->SetSelection(guid);
+
+ // update reputation list if need
+ Unit* unit = ObjectAccessor::GetUnit(*_player, guid );
+ if(!unit)
+ return;
+
+ _player->SetFactionVisibleForFactionTemplateId(unit->getFaction());
+}
+
+void WorldSession::HandleStandStateChangeOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ sLog.outDebug( "WORLD: Received CMSG_STAND_STATE_CHANGE" );
+ uint8 animstate;
+ recv_data >> animstate;
+
+ _player->SetStandState(animstate);
+}
+
+void WorldSession::HandleFriendListOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4);
+ sLog.outDebug( "WORLD: Received CMSG_CONTACT_LIST" );
+ uint32 unk;
+ recv_data >> unk;
+ sLog.outDebug("unk value is %u", unk);
+ _player->GetSocial()->SendSocialList();
+}
+
+void WorldSession::HandleAddFriendOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 1+1);
+
+ sLog.outDebug( "WORLD: Received CMSG_ADD_FRIEND" );
+
+ std::string friendName = GetMangosString(LANG_FRIEND_IGNORE_UNKNOWN);
+ std::string friendNote;
+ FriendsResult friendResult = FRIEND_NOT_FOUND;
+ Player *pFriend = NULL;
+ uint64 friendGuid = 0;
+
+ recv_data >> friendName;
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, (friendName.size()+1)+1);
+
+ recv_data >> friendNote;
+
+ if(!normalizePlayerName(friendName))
+ return;
+
+ CharacterDatabase.escape_string(friendName); // prevent SQL injection - normal name don't must changed by this call
+
+ sLog.outDebug( "WORLD: %s asked to add friend : '%s'",
+ GetPlayer()->GetName(), friendName.c_str() );
+
+ friendGuid = objmgr.GetPlayerGUIDByName(friendName);
+
+ if(friendGuid)
+ {
+ pFriend = ObjectAccessor::FindPlayer(friendGuid);
+ if(pFriend==GetPlayer())
+ friendResult = FRIEND_SELF;
+ else if(GetPlayer()->GetTeam()!=objmgr.GetPlayerTeamByGUID(friendGuid) && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND) && GetSecurity() < SEC_MODERATOR)
+ friendResult = FRIEND_ENEMY;
+ else if(GetPlayer()->GetSocial()->HasFriend(GUID_LOPART(friendGuid)))
+ friendResult = FRIEND_ALREADY;
+ }
+
+ if (friendGuid && friendResult==FRIEND_NOT_FOUND)
+ {
+ if( pFriend && pFriend->IsInWorld() && pFriend->IsVisibleGloballyFor(GetPlayer()))
+ friendResult = FRIEND_ADDED_ONLINE;
+ else
+ friendResult = FRIEND_ADDED_OFFLINE;
+
+ if(!_player->GetSocial()->AddToSocialList(GUID_LOPART(friendGuid), false))
+ {
+ friendResult = FRIEND_LIST_FULL;
+ sLog.outDebug( "WORLD: %s's friend list is full.", GetPlayer()->GetName());
+ }
+
+ _player->GetSocial()->SetFriendNote(GUID_LOPART(friendGuid), friendNote);
+
+ sLog.outDebug( "WORLD: %s Guid found '%u'.", friendName.c_str(), GUID_LOPART(friendGuid));
+ }
+ else if(friendResult==FRIEND_ALREADY)
+ {
+ sLog.outDebug( "WORLD: %s Guid Already a Friend.", friendName.c_str() );
+ }
+ else if(friendResult==FRIEND_SELF)
+ {
+ sLog.outDebug( "WORLD: %s Guid can't add himself.", friendName.c_str() );
+ }
+ else
+ {
+ sLog.outDebug( "WORLD: %s Guid not found.", friendName.c_str() );
+ }
+
+ sSocialMgr.SendFriendStatus(GetPlayer(), friendResult, GUID_LOPART(friendGuid), friendName, false);
+
+ sLog.outDebug( "WORLD: Sent (SMSG_FRIEND_STATUS)" );
+}
+
+void WorldSession::HandleDelFriendOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ uint64 FriendGUID;
+
+ sLog.outDebug( "WORLD: Received CMSG_DEL_FRIEND" );
+
+ recv_data >> FriendGUID;
+
+ _player->GetSocial()->RemoveFromSocialList(GUID_LOPART(FriendGUID), false);
+
+ sSocialMgr.SendFriendStatus(GetPlayer(), FRIEND_REMOVED, GUID_LOPART(FriendGUID), "", false);
+
+ sLog.outDebug( "WORLD: Sent motd (SMSG_FRIEND_STATUS)" );
+}
+
+void WorldSession::HandleAddIgnoreOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ sLog.outDebug( "WORLD: Received CMSG_ADD_IGNORE" );
+
+ std::string IgnoreName = GetMangosString(LANG_FRIEND_IGNORE_UNKNOWN);
+ FriendsResult ignoreResult = FRIEND_IGNORE_NOT_FOUND;
+ uint64 IgnoreGuid = 0;
+
+ recv_data >> IgnoreName;
+
+ if(!normalizePlayerName(IgnoreName))
+ return;
+
+ CharacterDatabase.escape_string(IgnoreName); // prevent SQL injection - normal name don't must changed by this call
+
+ sLog.outDebug( "WORLD: %s asked to Ignore: '%s'",
+ GetPlayer()->GetName(), IgnoreName.c_str() );
+
+ IgnoreGuid = objmgr.GetPlayerGUIDByName(IgnoreName);
+
+ if(IgnoreGuid)
+ {
+ if(IgnoreGuid==GetPlayer()->GetGUID())
+ ignoreResult = FRIEND_IGNORE_SELF;
+ else
+ {
+ if( GetPlayer()->GetSocial()->HasIgnore(GUID_LOPART(IgnoreGuid)) )
+ ignoreResult = FRIEND_IGNORE_ALREADY;
+ }
+ }
+
+ if (IgnoreGuid && ignoreResult == FRIEND_IGNORE_NOT_FOUND)
+ {
+ ignoreResult = FRIEND_IGNORE_ADDED;
+
+ _player->GetSocial()->AddToSocialList(GUID_LOPART(IgnoreGuid), true);
+ }
+ else if(ignoreResult==FRIEND_IGNORE_ALREADY)
+ {
+ sLog.outDebug( "WORLD: %s Guid Already Ignored.", IgnoreName.c_str() );
+ }
+ else if(ignoreResult==FRIEND_IGNORE_SELF)
+ {
+ sLog.outDebug( "WORLD: %s Guid can't add himself.", IgnoreName.c_str() );
+ }
+ else
+ {
+ sLog.outDebug( "WORLD: %s Guid not found.", IgnoreName.c_str() );
+ }
+
+ sSocialMgr.SendFriendStatus(GetPlayer(), ignoreResult, GUID_LOPART(IgnoreGuid), "", false);
+
+ sLog.outDebug( "WORLD: Sent (SMSG_FRIEND_STATUS)" );
+}
+
+void WorldSession::HandleDelIgnoreOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ uint64 IgnoreGUID;
+
+ sLog.outDebug( "WORLD: Received CMSG_DEL_IGNORE" );
+
+ recv_data >> IgnoreGUID;
+
+ _player->GetSocial()->RemoveFromSocialList(GUID_LOPART(IgnoreGUID), true);
+
+ sSocialMgr.SendFriendStatus(GetPlayer(), FRIEND_IGNORE_REMOVED, GUID_LOPART(IgnoreGUID), "", false);
+
+ sLog.outDebug( "WORLD: Sent motd (SMSG_FRIEND_STATUS)" );
+}
+
+void WorldSession::HandleSetFriendNoteOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8+1);
+ uint64 guid;
+ std::string note;
+ recv_data >> guid >> note;
+ _player->GetSocial()->SetFriendNote(guid, note);
+}
+
+void WorldSession::HandleBugOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+4+1+4+1);
+
+ uint32 suggestion, contentlen;
+ std::string content;
+ uint32 typelen;
+ std::string type;
+
+ recv_data >> suggestion >> contentlen >> content;
+
+ //recheck
+ CHECK_PACKET_SIZE(recv_data,4+4+(content.size()+1)+4+1);
+
+ recv_data >> typelen >> type;
+
+ if( suggestion == 0 )
+ sLog.outDebug( "WORLD: Received CMSG_BUG [Bug Report]" );
+ else
+ sLog.outDebug( "WORLD: Received CMSG_BUG [Suggestion]" );
+
+ sLog.outDebug( type.c_str( ) );
+ sLog.outDebug( content.c_str( ) );
+
+ CharacterDatabase.escape_string(type);
+ CharacterDatabase.escape_string(content);
+ CharacterDatabase.PExecute ("INSERT INTO bugreport (type,content) VALUES('%s', '%s')", type.c_str( ), content.c_str( ));
+}
+
+void WorldSession::HandleCorpseReclaimOpcode(WorldPacket &recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDetail("WORLD: Received CMSG_RECLAIM_CORPSE");
+ if (GetPlayer()->isAlive())
+ return;
+
+ if (BattleGround * bg = _player->GetBattleGround())
+ if(bg->isArena())
+ return;
+
+ // body not released yet
+ if(!GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
+ return;
+
+ Corpse *corpse = GetPlayer()->GetCorpse();
+
+ if (!corpse )
+ return;
+
+ // prevent resurrect before 30-sec delay after body release not finished
+ if(corpse->GetGhostTime() + GetPlayer()->GetCorpseReclaimDelay(corpse->GetType()==CORPSE_RESURRECTABLE_PVP) > time(NULL))
+ return;
+
+ float dist = corpse->GetDistance2d(GetPlayer());
+ sLog.outDebug("Corpse 2D Distance: \t%f",dist);
+ if (dist > CORPSE_RECLAIM_RADIUS)
+ return;
+
+ uint64 guid;
+ recv_data >> guid;
+
+ // resurrect
+ GetPlayer()->ResurrectPlayer(GetPlayer()->InBattleGround() ? 1.0f : 0.5f);
+
+ // spawn bones
+ GetPlayer()->SpawnCorpseBones();
+
+ GetPlayer()->SaveToDB();
+}
+
+void WorldSession::HandleResurrectResponseOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,8+1);
+
+ sLog.outDetail("WORLD: Received CMSG_RESURRECT_RESPONSE");
+
+ if(GetPlayer()->isAlive())
+ return;
+
+ uint64 guid;
+ uint8 status;
+ recv_data >> guid;
+ recv_data >> status;
+
+ if(status == 0)
+ {
+ GetPlayer()->clearResurrectRequestData(); // reject
+ return;
+ }
+
+ if(!GetPlayer()->isRessurectRequestedBy(guid))
+ return;
+
+ GetPlayer()->ResurectUsingRequestData();
+ GetPlayer()->SaveToDB();
+}
+
+void WorldSession::HandleAreaTriggerOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ sLog.outDebug("WORLD: Received CMSG_AREATRIGGER");
+
+ uint32 Trigger_ID;
+
+ recv_data >> Trigger_ID;
+ sLog.outDebug("Trigger ID:%u",Trigger_ID);
+
+ if(GetPlayer()->isInFlight())
+ {
+ sLog.outDebug("Player '%s' (GUID: %u) in flight, ignore Area Trigger ID:%u",GetPlayer()->GetName(),GetPlayer()->GetGUIDLow(), Trigger_ID);
+ return;
+ }
+
+ AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
+ if(!atEntry)
+ {
+ sLog.outDebug("Player '%s' (GUID: %u) send unknown (by DBC) Area Trigger ID:%u",GetPlayer()->GetName(),GetPlayer()->GetGUIDLow(), Trigger_ID);
+ return;
+ }
+
+ if (GetPlayer()->GetMapId()!=atEntry->mapid)
+ {
+ sLog.outDebug("Player '%s' (GUID: %u) too far (trigger map: %u player map: %u), ignore Area Trigger ID: %u", GetPlayer()->GetName(), atEntry->mapid, GetPlayer()->GetMapId(), GetPlayer()->GetGUIDLow(), Trigger_ID);
+ return;
+ }
+
+ // delta is safe radius
+ const float delta = 5.0f;
+ // check if player in the range of areatrigger
+ Player* pl = GetPlayer();
+
+ if (atEntry->radius > 0)
+ {
+ // if we have radius check it
+ float dist = pl->GetDistance(atEntry->x,atEntry->y,atEntry->z);
+ if(dist > atEntry->radius + delta)
+ {
+ sLog.outDebug("Player '%s' (GUID: %u) too far (radius: %f distance: %f), ignore Area Trigger ID: %u",
+ pl->GetName(), pl->GetGUIDLow(), atEntry->radius, dist, Trigger_ID);
+ return;
+ }
+ }
+ else
+ {
+ // we have only extent
+ float dx = pl->GetPositionX() - atEntry->x;
+ float dy = pl->GetPositionY() - atEntry->y;
+ float dz = pl->GetPositionZ() - atEntry->z;
+ double es = sin(atEntry->box_orientation);
+ double ec = cos(atEntry->box_orientation);
+ // calc rotated vector based on extent axis
+ double rotateDx = dx*ec - dy*es;
+ double rotateDy = dx*es + dy*ec;
+
+ if( (fabs(rotateDx) > atEntry->box_x/2 + delta) ||
+ (fabs(rotateDy) > atEntry->box_y/2 + delta) ||
+ (fabs(dz) > atEntry->box_z/2 + delta) )
+ {
+ sLog.outDebug("Player '%s' (GUID: %u) too far (1/2 box X: %f 1/2 box Y: %u 1/2 box Z: %u rotate dX: %f rotate dY: %f dZ:%f), ignore Area Trigger ID: %u",
+ pl->GetName(), pl->GetGUIDLow(), atEntry->box_x/2, atEntry->box_y/2, atEntry->box_z/2, rotateDx, rotateDy, dz, Trigger_ID);
+ return;
+ }
+ }
+
+ if(Script->scriptAreaTrigger(GetPlayer(), atEntry))
+ return;
+
+ uint32 quest_id = objmgr.GetQuestForAreaTrigger( Trigger_ID );
+ if( quest_id && GetPlayer()->isAlive() && GetPlayer()->IsActiveQuest(quest_id) )
+ {
+ Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
+ if( pQuest )
+ {
+ if(GetPlayer()->GetQuestStatus(quest_id) == QUEST_STATUS_INCOMPLETE)
+ GetPlayer()->AreaExploredOrEventHappens( quest_id );
+ }
+ }
+
+ if(objmgr.IsTavernAreaTrigger(Trigger_ID))
+ {
+ // set resting flag we are in the inn
+ GetPlayer()->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
+ GetPlayer()->InnEnter(time(NULL), atEntry->mapid, atEntry->x, atEntry->y, atEntry->z);
+ GetPlayer()->SetRestType(REST_TYPE_IN_TAVERN);
+
+ if(sWorld.IsFFAPvPRealm())
+ GetPlayer()->RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
+
+ return;
+ }
+
+ if(GetPlayer()->InBattleGround())
+ {
+ BattleGround* bg = GetPlayer()->GetBattleGround();
+ if(bg)
+ if(bg->GetStatus() == STATUS_IN_PROGRESS)
+ bg->HandleAreaTrigger(GetPlayer(), Trigger_ID);
+
+ return;
+ }
+
+ // NULL if all values default (non teleport trigger)
+ AreaTrigger const* at = objmgr.GetAreaTrigger(Trigger_ID);
+ if(!at)
+ return;
+
+ if(!GetPlayer()->isGameMaster())
+ {
+ uint32 missingLevel = 0;
+ if(GetPlayer()->getLevel() < at->requiredLevel && !sWorld.getConfig(CONFIG_INSTANCE_IGNORE_LEVEL))
+ missingLevel = at->requiredLevel;
+
+ // must have one or the other, report the first one that's missing
+ uint32 missingItem = 0;
+ if(at->requiredItem)
+ {
+ if(!GetPlayer()->HasItemCount(at->requiredItem, 1) &&
+ (!at->requiredItem2 || !GetPlayer()->HasItemCount(at->requiredItem2, 1)))
+ missingItem = at->requiredItem;
+ }
+ else if(at->requiredItem2 && !GetPlayer()->HasItemCount(at->requiredItem2, 1))
+ missingItem = at->requiredItem2;
+
+ uint32 missingKey = 0;
+ if(GetPlayer()->GetDifficulty() == DIFFICULTY_HEROIC)
+ {
+ if(at->heroicKey)
+ {
+ if(!GetPlayer()->HasItemCount(at->heroicKey, 1) &&
+ (!at->heroicKey2 || !GetPlayer()->HasItemCount(at->heroicKey2, 1)))
+ missingKey = at->heroicKey;
+ }
+ else if(at->heroicKey2 && !GetPlayer()->HasItemCount(at->heroicKey2, 1))
+ missingKey = at->heroicKey2;
+ }
+
+ uint32 missingQuest = 0;
+ if(at->requiredQuest && !GetPlayer()->GetQuestRewardStatus(at->requiredQuest))
+ missingQuest = at->requiredQuest;
+
+ if(missingLevel || missingItem || missingKey || missingQuest)
+ {
+ // TODO: all this is probably wrong
+ if(missingItem)
+ SendAreaTriggerMessage(GetMangosString(LANG_LEVEL_MINREQUIRED_AND_ITEM), at->requiredLevel, objmgr.GetItemPrototype(missingItem)->Name1);
+ else if(missingKey)
+ GetPlayer()->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_DIFFICULTY2);
+ else if(missingQuest)
+ SendAreaTriggerMessage(at->requiredFailedText.c_str());
+ else if(missingLevel)
+ SendAreaTriggerMessage(GetMangosString(LANG_LEVEL_MINREQUIRED), missingLevel);
+ return;
+ }
+ }
+
+ GetPlayer()->TeleportTo(at->target_mapId,at->target_X,at->target_Y,at->target_Z,at->target_Orientation,TELE_TO_NOT_LEAVE_TRANSPORT);
+}
+
+void WorldSession::HandleUpdateAccountData(WorldPacket &/*recv_data*/)
+{
+ sLog.outDetail("WORLD: Received CMSG_UPDATE_ACCOUNT_DATA");
+ //recv_data.hexlike();
+}
+
+void WorldSession::HandleRequestAccountData(WorldPacket& /*recv_data*/)
+{
+ sLog.outDetail("WORLD: Received CMSG_REQUEST_ACCOUNT_DATA");
+ //recv_data.hexlike();
+}
+
+void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,1+2+1+1);
+
+ sLog.outDebug( "WORLD: Received CMSG_SET_ACTION_BUTTON" );
+ uint8 button, misc, type;
+ uint16 action;
+ recv_data >> button >> action >> misc >> type;
+ sLog.outDetail( "BUTTON: %u ACTION: %u TYPE: %u MISC: %u", button, action, type, misc );
+ if(action==0)
+ {
+ sLog.outDetail( "MISC: Remove action from button %u", button );
+
+ GetPlayer()->removeActionButton(button);
+ }
+ else
+ {
+ if(type==ACTION_BUTTON_MACRO || type==ACTION_BUTTON_CMACRO)
+ {
+ sLog.outDetail( "MISC: Added Macro %u into button %u", action, button );
+ GetPlayer()->addActionButton(button,action,type,misc);
+ }
+ else if(type==ACTION_BUTTON_SPELL)
+ {
+ sLog.outDetail( "MISC: Added Action %u into button %u", action, button );
+ GetPlayer()->addActionButton(button,action,type,misc);
+ }
+ else if(type==ACTION_BUTTON_ITEM)
+ {
+ sLog.outDetail( "MISC: Added Item %u into button %u", action, button );
+ GetPlayer()->addActionButton(button,action,type,misc);
+ }
+ else
+ sLog.outError( "MISC: Unknown action button type %u for action %u into button %u", type, action, button );
+ }
+}
+
+void WorldSession::HandleCompleteCinema( WorldPacket & /*recv_data*/ )
+{
+ DEBUG_LOG( "WORLD: Player is watching cinema" );
+}
+
+void WorldSession::HandleNextCinematicCamera( WorldPacket & /*recv_data*/ )
+{
+ DEBUG_LOG( "WORLD: Which movie to play" );
+}
+
+void WorldSession::HandleMoveTimeSkippedOpcode( WorldPacket & /*recv_data*/ )
+{
+ /* WorldSession::Update( getMSTime() );*/
+ DEBUG_LOG( "WORLD: Time Lag/Synchronization Resent/Update" );
+
+ /*
+ CHECK_PACKET_SIZE(recv_data,8+4);
+ uint64 guid;
+ uint32 time_skipped;
+ recv_data >> guid;
+ recv_data >> time_skipped;
+ sLog.outDebug( "WORLD: CMSG_MOVE_TIME_SKIPPED" );
+
+ /// TODO
+ must be need use in mangos
+ We substract server Lags to move time ( AntiLags )
+ for exmaple
+ GetPlayer()->ModifyLastMoveTime( -int32(time_skipped) );
+ */
+}
+
+void WorldSession::HandleFeatherFallAck(WorldPacket &/*recv_data*/)
+{
+ DEBUG_LOG("WORLD: CMSG_MOVE_FEATHER_FALL_ACK");
+}
+
+void WorldSession::HandleMoveUnRootAck(WorldPacket&/* recv_data*/)
+{
+ /*
+ CHECK_PACKET_SIZE(recv_data,8+8+4+4+4+4+4);
+
+ sLog.outDebug( "WORLD: CMSG_FORCE_MOVE_UNROOT_ACK" );
+ recv_data.hexlike();
+ uint64 guid;
+ uint64 unknown1;
+ uint32 unknown2;
+ float PositionX;
+ float PositionY;
+ float PositionZ;
+ float Orientation;
+
+ recv_data >> guid;
+ recv_data >> unknown1;
+ recv_data >> unknown2;
+ recv_data >> PositionX;
+ recv_data >> PositionY;
+ recv_data >> PositionZ;
+ recv_data >> Orientation;
+
+ // TODO for later may be we can use for anticheat
+ DEBUG_LOG("Guid " I64FMTD,guid);
+ DEBUG_LOG("unknown1 " I64FMTD,unknown1);
+ DEBUG_LOG("unknown2 %u",unknown2);
+ DEBUG_LOG("X %f",PositionX);
+ DEBUG_LOG("Y %f",PositionY);
+ DEBUG_LOG("Z %f",PositionZ);
+ DEBUG_LOG("O %f",Orientation);
+ */
+}
+
+void WorldSession::HandleMoveRootAck(WorldPacket&/* recv_data*/)
+{
+ /*
+ CHECK_PACKET_SIZE(recv_data,8+8+4+4+4+4+4);
+
+ sLog.outDebug( "WORLD: CMSG_FORCE_MOVE_ROOT_ACK" );
+ recv_data.hexlike();
+ uint64 guid;
+ uint64 unknown1;
+ uint32 unknown2;
+ float PositionX;
+ float PositionY;
+ float PositionZ;
+ float Orientation;
+
+ recv_data >> guid;
+ recv_data >> unknown1;
+ recv_data >> unknown2;
+ recv_data >> PositionX;
+ recv_data >> PositionY;
+ recv_data >> PositionZ;
+ recv_data >> Orientation;
+
+ // for later may be we can use for anticheat
+ DEBUG_LOG("Guid " I64FMTD,guid);
+ DEBUG_LOG("unknown1 " I64FMTD,unknown1);
+ DEBUG_LOG("unknown1 %u",unknown2);
+ DEBUG_LOG("X %f",PositionX);
+ DEBUG_LOG("Y %f",PositionY);
+ DEBUG_LOG("Z %f",PositionZ);
+ DEBUG_LOG("O %f",Orientation);
+ */
+}
+
+void WorldSession::HandleMoveTeleportAck(WorldPacket&/* recv_data*/)
+{
+ /*
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ sLog.outDebug("MSG_MOVE_TELEPORT_ACK");
+ uint64 guid;
+ uint32 flags, time;
+
+ recv_data >> guid;
+ recv_data >> flags >> time;
+ DEBUG_LOG("Guid " I64FMTD,guid);
+ DEBUG_LOG("Flags %u, time %u",flags, time/1000);
+ */
+}
+
+void WorldSession::HandleSetActionBar(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ uint8 ActionBar;
+
+ recv_data >> ActionBar;
+
+ if(!GetPlayer()) // ignore until not logged (check needed because STATUS_AUTHED)
+ {
+ if(ActionBar!=0)
+ sLog.outError("WorldSession::HandleSetActionBar in not logged state with value: %u, ignored",uint32(ActionBar));
+ return;
+ }
+
+ GetPlayer()->SetByteValue(PLAYER_FIELD_BYTES, 2, ActionBar);
+}
+
+void WorldSession::HandleWardenDataOpcode(WorldPacket& /*recv_data*/)
+{
+ /*
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ uint8 tmp;
+ recv_data >> tmp;
+ sLog.outDebug("Received opcode CMSG_WARDEN_DATA, not resolve.uint8 = %u",tmp);
+ */
+}
+
+void WorldSession::HandlePlayedTime(WorldPacket& /*recv_data*/)
+{
+ uint32 TotalTimePlayed = GetPlayer()->GetTotalPlayedTime();
+ uint32 LevelPlayedTime = GetPlayer()->GetLevelPlayedTime();
+
+ WorldPacket data(SMSG_PLAYED_TIME, 8);
+ data << TotalTimePlayed;
+ data << LevelPlayedTime;
+ SendPacket(&data);
+}
+
+void WorldSession::HandleInspectOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ uint64 guid;
+ recv_data >> guid;
+ DEBUG_LOG("Inspected guid is " I64FMTD, guid);
+
+ _player->SetSelection(guid);
+
+ Player *plr = objmgr.GetPlayer(guid);
+ if(!plr) // wrong player
+ return;
+
+ uint32 talent_points = 0x3D;
+ uint32 guid_size = plr->GetPackGUID().size();
+ WorldPacket data(SMSG_INSPECT_TALENT, 4+talent_points);
+ data.append(plr->GetPackGUID());
+ data << uint32(talent_points);
+
+ // fill by 0 talents array
+ for(uint32 i = 0; i < talent_points; ++i)
+ data << uint8(0);
+
+ if(sWorld.getConfig(CONFIG_TALENTS_INSPECTING) || _player->isGameMaster())
+ {
+ // find class talent tabs (all players have 3 talent tabs)
+ uint32 const* talentTabIds = GetTalentTabPages(plr->getClass());
+
+ uint32 talentTabPos = 0; // pos of first talent rank in tab including all prev tabs
+ for(uint32 i = 0; i < 3; ++i)
+ {
+ uint32 talentTabId = talentTabIds[i];
+
+ // fill by real data
+ for(uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
+ {
+ TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
+ if(!talentInfo)
+ continue;
+
+ // skip another tab talents
+ if(talentInfo->TalentTab != talentTabId)
+ continue;
+
+ // find talent rank
+ uint32 curtalent_maxrank = 0;
+ for(uint32 k = 5; k > 0; --k)
+ {
+ if(talentInfo->RankID[k-1] && plr->HasSpell(talentInfo->RankID[k-1]))
+ {
+ curtalent_maxrank = k;
+ break;
+ }
+ }
+
+ // not learned talent
+ if(!curtalent_maxrank)
+ continue;
+
+ // 1 rank talent bit index
+ uint32 curtalent_index = talentTabPos + GetTalentInspectBitPosInTab(talentId);
+
+ uint32 curtalent_rank_index = curtalent_index+curtalent_maxrank-1;
+
+ // slot/offset in 7-bit bytes
+ uint32 curtalent_rank_slot7 = curtalent_rank_index / 7;
+ uint32 curtalent_rank_offset7 = curtalent_rank_index % 7;
+
+ // rank pos with skipped 8 bit
+ uint32 curtalent_rank_index2 = curtalent_rank_slot7 * 8 + curtalent_rank_offset7;
+
+ // slot/offset in 8-bit bytes with skipped high bit
+ uint32 curtalent_rank_slot = curtalent_rank_index2 / 8;
+ uint32 curtalent_rank_offset = curtalent_rank_index2 % 8;
+
+ // apply mask
+ uint32 val = data.read<uint8>(guid_size + 4 + curtalent_rank_slot);
+ val |= (1 << curtalent_rank_offset);
+ data.put<uint8>(guid_size + 4 + curtalent_rank_slot, val & 0xFF);
+ }
+
+ talentTabPos += GetTalentTabInspectBitSize(talentTabId);
+ }
+ }
+
+ SendPacket(&data);
+}
+
+void WorldSession::HandleInspectHonorStatsOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ uint64 guid;
+ recv_data >> guid;
+
+ Player *player = objmgr.GetPlayer(guid);
+
+ if(!player)
+ {
+ sLog.outError("InspectHonorStats: WTF, player not found...");
+ return;
+ }
+
+ WorldPacket data(MSG_INSPECT_HONOR_STATS, 8+1+4*4);
+ data << uint64(player->GetGUID());
+ data << uint8(player->GetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY));
+ data << uint32(player->GetUInt32Value(PLAYER_FIELD_KILLS));
+ data << uint32(player->GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION));
+ data << uint32(player->GetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION));
+ data << uint32(player->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS));
+ SendPacket(&data);
+}
+
+void WorldSession::HandleWorldTeleportOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4+4+4+4+4+4);
+
+ // write in client console: worldport 469 452 6454 2536 180 or /console worldport 469 452 6454 2536 180
+ // Received opcode CMSG_WORLD_TELEPORT
+ // Time is ***, map=469, x=452.000000, y=6454.000000, z=2536.000000, orient=3.141593
+
+ //sLog.outDebug("Received opcode CMSG_WORLD_TELEPORT");
+
+ if(GetPlayer()->isInFlight())
+ {
+ sLog.outDebug("Player '%s' (GUID: %u) in flight, ignore worldport command.",GetPlayer()->GetName(),GetPlayer()->GetGUIDLow());
+ return;
+ }
+
+ uint32 time;
+ uint32 mapid;
+ float PositionX;
+ float PositionY;
+ float PositionZ;
+ float Orientation;
+
+ recv_data >> time; // time in m.sec.
+ recv_data >> mapid;
+ recv_data >> PositionX;
+ recv_data >> PositionY;
+ recv_data >> PositionZ;
+ recv_data >> Orientation; // o (3.141593 = 180 degrees)
+ DEBUG_LOG("Time %u sec, map=%u, x=%f, y=%f, z=%f, orient=%f", time/1000, mapid, PositionX, PositionY, PositionZ, Orientation);
+
+ if (GetSecurity() >= SEC_ADMINISTRATOR)
+ GetPlayer()->TeleportTo(mapid,PositionX,PositionY,PositionZ,Orientation);
+ else
+ SendNotification(LANG_YOU_NOT_HAVE_PERMISSION);
+ sLog.outDebug("Received worldport command from player %s", GetPlayer()->GetName());
+}
+
+void WorldSession::HandleWhoisOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 1);
+
+ sLog.outDebug("Received opcode CMSG_WHOIS");
+ std::string charname;
+ recv_data >> charname;
+
+ if (GetSecurity() < SEC_ADMINISTRATOR)
+ {
+ SendNotification(LANG_YOU_NOT_HAVE_PERMISSION);
+ return;
+ }
+
+ if(charname.empty())
+ {
+ SendNotification(LANG_NEED_CHARACTER_NAME);
+ return;
+ }
+
+ Player *plr = objmgr.GetPlayer(charname.c_str());
+
+ if(!plr)
+ {
+ SendNotification(LANG_PLAYER_NOT_EXIST_OR_OFFLINE, charname.c_str());
+ return;
+ }
+
+ uint32 accid = plr->GetSession()->GetAccountId();
+
+ QueryResult *result = loginDatabase.PQuery("SELECT username,email,last_ip FROM account WHERE id=%u", accid);
+ if(!result)
+ {
+ SendNotification(LANG_ACCOUNT_FOR_PLAYER_NOT_FOUND, charname.c_str());
+ return;
+ }
+
+ Field *fields = result->Fetch();
+ std::string acc = fields[0].GetCppString();
+ if(acc.empty())
+ acc = "Unknown";
+ std::string email = fields[1].GetCppString();
+ if(email.empty())
+ email = "Unknown";
+ std::string lastip = fields[2].GetCppString();
+ if(lastip.empty())
+ lastip = "Unknown";
+
+ std::string msg = charname + "'s " + "account is " + acc + ", e-mail: " + email + ", last ip: " + lastip;
+
+ WorldPacket data(SMSG_WHOIS, msg.size()+1);
+ data << msg;
+ _player->GetSession()->SendPacket(&data);
+
+ delete result;
+
+ sLog.outDebug("Received whois command from player %s for character %s", GetPlayer()->GetName(), charname.c_str());
+}
+
+void WorldSession::HandleReportSpamOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 1+8);
+ sLog.outDebug("WORLD: CMSG_REPORT_SPAM");
+ recv_data.hexlike();
+
+ uint8 spam_type; // 0 - mail, 1 - chat
+ uint64 spammer_guid;
+ uint32 unk1, unk2, unk3, unk4 = 0;
+ std::string description = "";
+ recv_data >> spam_type; // unk 0x01 const, may be spam type (mail/chat)
+ recv_data >> spammer_guid; // player guid
+ switch(spam_type)
+ {
+ case 0:
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4);
+ recv_data >> unk1; // const 0
+ recv_data >> unk2; // probably mail id
+ recv_data >> unk3; // const 0
+ break;
+ case 1:
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4+4+1);
+ recv_data >> unk1; // probably language
+ recv_data >> unk2; // message type?
+ recv_data >> unk3; // probably channel id
+ recv_data >> unk4; // unk random value
+ recv_data >> description; // spam description string (messagetype, channel name, player name, message)
+ break;
+ }
+
+ // NOTE: all chat messages from this spammer automatically ignored by spam reporter until logout in case chat spam.
+ // if it's mail spam - ALL mails from this spammer automatically removed by client
+
+ // Complaint Received message
+ WorldPacket data(SMSG_COMPLAIN_RESULT, 1);
+ data << uint8(0);
+ SendPacket(&data);
+
+ sLog.outDebug("REPORT SPAM: type %u, guid %u, unk1 %u, unk2 %u, unk3 %u, unk4 %u, message %s", spam_type, GUID_LOPART(spammer_guid), unk1, unk2, unk3, unk4, description.c_str());
+}
+
+void WorldSession::HandleRealmStateRequestOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ sLog.outDebug("CMSG_REALM_SPLIT");
+
+ uint32 unk;
+ std::string split_date = "01/01/01";
+ recv_data >> unk;
+
+ WorldPacket data(SMSG_REALM_SPLIT, 4+4+split_date.size()+1);
+ data << unk;
+ data << uint32(0x00000000); // realm split state
+ // split states:
+ // 0x0 realm normal
+ // 0x1 realm split
+ // 0x2 realm split pending
+ data << split_date;
+ SendPacket(&data);
+ //sLog.outDebug("response sent %u", unk);
+}
+
+void WorldSession::HandleFarSightOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 1);
+
+ sLog.outDebug("WORLD: CMSG_FAR_SIGHT");
+ //recv_data.hexlike();
+
+ uint8 unk;
+ recv_data >> unk;
+
+ switch(unk)
+ {
+ case 0:
+ //WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0)
+ //SendPacket(&data);
+ //_player->SetUInt64Value(PLAYER_FARSIGHT, 0);
+ sLog.outDebug("Removed FarSight from player %u", _player->GetGUIDLow());
+ break;
+ case 1:
+ sLog.outDebug("Added FarSight " I64FMTD " to player %u", _player->GetUInt64Value(PLAYER_FARSIGHT), _player->GetGUIDLow());
+ break;
+ }
+}
+
+void WorldSession::HandleChooseTitleOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ sLog.outDebug("CMSG_SET_TITLE");
+
+ int32 title;
+ recv_data >> title;
+
+ // -1 at none
+ if(title > 0 && title < 64)
+ {
+ if(!GetPlayer()->HasFlag64(PLAYER__FIELD_KNOWN_TITLES,uint64(1) << title))
+ return;
+ }
+ else
+ title = 0;
+
+ GetPlayer()->SetUInt32Value(PLAYER_CHOSEN_TITLE, title);
+}
+
+void WorldSession::HandleAllowMoveAckOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4+4);
+
+ sLog.outDebug("CMSG_ALLOW_MOVE_ACK");
+
+ uint32 counter, time_;
+ recv_data >> counter >> time_;
+
+ // time_ seems always more than getMSTime()
+ uint32 diff = getMSTimeDiff(getMSTime(),time_);
+
+ sLog.outDebug("response sent: counter %u, time %u (HEX: %X), ms. time %u, diff %u", counter, time_, time_, getMSTime(), diff);
+}
+
+void WorldSession::HandleResetInstancesOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug("WORLD: CMSG_RESET_INSTANCES");
+ Group *pGroup = _player->GetGroup();
+ if(pGroup)
+ {
+ if(pGroup->IsLeader(_player->GetGUID()))
+ pGroup->ResetInstances(INSTANCE_RESET_ALL, _player);
+ }
+ else
+ _player->ResetInstances(INSTANCE_RESET_ALL);
+}
+
+void WorldSession::HandleDungeonDifficultyOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ sLog.outDebug("MSG_SET_DUNGEON_DIFFICULTY");
+
+ uint32 mode;
+ recv_data >> mode;
+
+ if(mode == _player->GetDifficulty())
+ return;
+
+ if(mode > DIFFICULTY_HEROIC)
+ {
+ sLog.outError("WorldSession::HandleDungeonDifficultyOpcode: player %d sent an invalid instance mode %d!", _player->GetGUIDLow(), mode);
+ return;
+ }
+
+ // cannot reset while in an instance
+ Map *map = _player->GetMap();
+ if(map && map->IsDungeon())
+ {
+ sLog.outError("WorldSession::HandleDungeonDifficultyOpcode: player %d tried to reset the instance while inside!", _player->GetGUIDLow());
+ return;
+ }
+
+ if(_player->getLevel() < LEVELREQUIREMENT_HEROIC)
+ return;
+ Group *pGroup = _player->GetGroup();
+ if(pGroup)
+ {
+ if(pGroup->IsLeader(_player->GetGUID()))
+ {
+ // the difficulty is set even if the instances can't be reset
+ //_player->SendDungeonDifficulty(true);
+ pGroup->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY, _player);
+ pGroup->SetDifficulty(mode);
+ }
+ }
+ else
+ {
+ _player->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY);
+ _player->SetDifficulty(mode);
+ }
+}
+
+void WorldSession::HandleNewUnknownOpcode( WorldPacket & recv_data )
+{
+ sLog.outDebug("New Unknown Opcode %u", recv_data.GetOpcode());
+ recv_data.hexlike();
+ /*
+ New Unknown Opcode 837
+ STORAGE_SIZE: 60
+ 02 00 00 00 00 00 00 00 | 00 00 00 00 01 20 00 00
+ 89 EB 33 01 71 5C 24 C4 | 15 03 35 45 74 47 8B 42
+ BA B8 1B 40 00 00 00 00 | 00 00 00 00 77 66 42 BF
+ 23 91 26 3F 00 00 60 41 | 00 00 00 00
+
+ New Unknown Opcode 837
+ STORAGE_SIZE: 44
+ 02 00 00 00 00 00 00 00 | 00 00 00 00 00 00 80 00
+ 7B 80 34 01 84 EA 2B C4 | 5F A1 36 45 C9 39 1C 42
+ BA B8 1B 40 CE 06 00 00 | 00 00 80 3F
+ */
+}
+
+void WorldSession::HandleDismountOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug("WORLD: CMSG_CANCEL_MOUNT_AURA");
+ //recv_data.hexlike();
+
+ //If player is not mounted, so go out :)
+ if (!_player->IsMounted()) // not blizz like; no any messages on blizz
+ {
+ ChatHandler(this).SendSysMessage(LANG_CHAR_NON_MOUNTED);
+ return;
+ }
+
+ if(_player->isInFlight()) // not blizz like; no any messages on blizz
+ {
+ ChatHandler(this).SendSysMessage(LANG_YOU_IN_FLIGHT);
+ return;
+ }
+
+ _player->Unmount();
+ _player->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
+}
+
+void WorldSession::HandleMoveFlyModeChangeAckOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8+4+4);
+
+ // fly mode on/off
+ sLog.outDebug("WORLD: CMSG_MOVE_SET_CAN_FLY_ACK");
+ //recv_data.hexlike();
+
+ uint64 guid;
+ uint32 unk;
+ uint32 flags;
+
+ recv_data >> guid >> unk >> flags;
+
+ _player->SetUnitMovementFlags(flags);
+ /*
+ on:
+ 25 00 00 00 00 00 00 00 | 00 00 00 00 00 00 80 00
+ 85 4E A9 01 19 BA 7A C3 | 42 0D 70 44 44 B0 A8 42
+ 78 15 94 40 39 03 00 00 | 00 00 80 3F
+ off:
+ 25 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
+ 10 FD A9 01 19 BA 7A C3 | 42 0D 70 44 44 B0 A8 42
+ 78 15 94 40 39 03 00 00 | 00 00 00 00
+ */
+}
+
+void WorldSession::HandleRequestPetInfoOpcode( WorldPacket & /*recv_data */)
+{
+ /*
+ sLog.outDebug("WORLD: CMSG_REQUEST_PET_INFO");
+ recv_data.hexlike();
+ */
+}
+
+void WorldSession::HandleSetTaxiBenchmarkOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 1);
+
+ uint8 mode;
+ recv_data >> mode;
+
+ sLog.outDebug("Client used \"/timetest %d\" command", mode);
+}
diff --git a/src/game/MotionMaster.cpp b/src/game/MotionMaster.cpp index cf910bf50b2..2a5cc05e986 100644 --- a/src/game/MotionMaster.cpp +++ b/src/game/MotionMaster.cpp @@ -1,342 +1,342 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "MotionMaster.h" -#include "CreatureAISelector.h" -#include "Creature.h" -#include "Traveller.h" - -#include "ConfusedMovementGenerator.h" -#include "FleeingMovementGenerator.h" -#include "HomeMovementGenerator.h" -#include "IdleMovementGenerator.h" -#include "PointMovementGenerator.h" -#include "TargetedMovementGenerator.h" -#include "WaypointMovementGenerator.h" - -#include <cassert> - -inline bool isStatic(MovementGenerator *mv) -{ - return (mv == &si_idleMovement); -} - -void -MotionMaster::Initialize() -{ - // clear ALL movement generators (including default) - while(!empty()) - { - MovementGenerator *curr = top(); - curr->Finalize(*i_owner); - pop(); - if( !isStatic( curr ) ) - delete curr; - } - - // set new default movement generator - if(i_owner->GetTypeId() == TYPEID_UNIT) - { - MovementGenerator* movement = FactorySelector::selectMovementGenerator((Creature*)i_owner); - push( movement == NULL ? &si_idleMovement : movement ); - top()->Initialize(*i_owner); - } - else - push(&si_idleMovement); -} - -MotionMaster::~MotionMaster() -{ - // clear ALL movement generators (including default) - while(!empty()) - { - MovementGenerator *curr = top(); - curr->Finalize(*i_owner); - pop(); - if( !isStatic( curr ) ) - delete curr; - } -} - -void -MotionMaster::UpdateMotion(const uint32 &diff) -{ - if( i_owner->hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED) ) - return; - assert( !empty() ); - if (!top()->Update(*i_owner, diff)) - MovementExpired(); -} - -void -MotionMaster::Clear(bool reset) -{ - while( !empty() && size() > 1 ) - { - MovementGenerator *curr = top(); - curr->Finalize(*i_owner); - pop(); - if( !isStatic( curr ) ) - delete curr; - } - - if (reset) - { - assert( !empty() ); - top()->Reset(*i_owner); - } -} - -void -MotionMaster::MovementExpired(bool reset) -{ - if( empty() || size() == 1 ) - return; - - MovementGenerator *curr = top(); - curr->Finalize(*i_owner); - pop(); - - if( !isStatic(curr) ) - delete curr; - - assert( !empty() ); - while( !empty() && top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE ) - { - // Should check if target is still valid? If not valid it will crash. - curr = top(); - curr->Finalize(*i_owner); - pop(); - delete curr; - } - if( empty() ) - Initialize(); - if (reset) top()->Reset(*i_owner); -} - -void MotionMaster::MoveIdle() -{ - if( empty() || !isStatic( top() ) ) - push( &si_idleMovement ); -} - -void -MotionMaster::MoveTargetedHome() -{ - if(i_owner->hasUnitState(UNIT_STAT_FLEEING)) - return; - - Clear(false); - - if(i_owner->GetTypeId()==TYPEID_UNIT && !((Creature*)i_owner)->GetCharmerOrOwnerGUID()) - { - DEBUG_LOG("Creature (Entry: %u GUID: %u) targeted home", i_owner->GetEntry(), i_owner->GetGUIDLow()); - Mutate(new HomeMovementGenerator<Creature>()); - } - else if(i_owner->GetTypeId()==TYPEID_UNIT && ((Creature*)i_owner)->GetCharmerOrOwnerGUID()) - { - sLog.outError("Pet or controlled creature (Entry: %u GUID: %u) attempt targeted home", - i_owner->GetEntry(), i_owner->GetGUIDLow() ); - } - else - { - sLog.outError("Player (GUID: %u) attempt targeted home", i_owner->GetGUIDLow() ); - } -} - -void -MotionMaster::MoveConfused() -{ - if(i_owner->GetTypeId()==TYPEID_PLAYER) - { - DEBUG_LOG("Player (GUID: %u) move confused", i_owner->GetGUIDLow() ); - Mutate(new ConfusedMovementGenerator<Player>()); - } - else - { - DEBUG_LOG("Creature (Entry: %u GUID: %u) move confused", - i_owner->GetEntry(), i_owner->GetGUIDLow() ); - Mutate(new ConfusedMovementGenerator<Creature>()); - } -} - -void -MotionMaster::MoveChase(Unit* target, float dist, float angle) -{ - // ignore movement request if target not exist - if(!target) - return; - - i_owner->clearUnitState(UNIT_STAT_FOLLOW); - if(i_owner->GetTypeId()==TYPEID_PLAYER) - { - DEBUG_LOG("Player (GUID: %u) chase to %s (GUID: %u)", - target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature", - target->GetTypeId()==TYPEID_PLAYER ? i_owner->GetGUIDLow() : ((Creature*)i_owner)->GetDBTableGUIDLow() ); - Mutate(new TargetedMovementGenerator<Player>(*target,dist,angle)); - } - else - { - DEBUG_LOG("Creature (Entry: %u GUID: %u) chase to %s (GUID: %u)", - i_owner->GetEntry(), i_owner->GetGUIDLow(), - target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature", - target->GetTypeId()==TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow() ); - Mutate(new TargetedMovementGenerator<Creature>(*target,dist,angle)); - } -} - -void -MotionMaster::MoveFollow(Unit* target, float dist, float angle) -{ - Clear(); - - // ignore movement request if target not exist - if(!target) - return; - - i_owner->addUnitState(UNIT_STAT_FOLLOW); - if(i_owner->GetTypeId()==TYPEID_PLAYER) - { - DEBUG_LOG("Player (GUID: %u) follow to %s (GUID: %u)", i_owner->GetGUIDLow(), - target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature", - target->GetTypeId()==TYPEID_PLAYER ? i_owner->GetGUIDLow() : ((Creature*)i_owner)->GetDBTableGUIDLow() ); - Mutate(new TargetedMovementGenerator<Player>(*target,dist,angle)); - } - else - { - DEBUG_LOG("Creature (Entry: %u GUID: %u) follow to %s (GUID: %u)", - i_owner->GetEntry(), i_owner->GetGUIDLow(), - target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature", - target->GetTypeId()==TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow() ); - Mutate(new TargetedMovementGenerator<Creature>(*target,dist,angle)); - } -} - -void -MotionMaster::MovePoint(uint32 id, float x, float y, float z) -{ - if(i_owner->GetTypeId()==TYPEID_PLAYER) - { - DEBUG_LOG("Player (GUID: %u) targeted point (Id: %u X: %f Y: %f Z: %f)", i_owner->GetGUIDLow(), id, x, y, z ); - Mutate(new PointMovementGenerator<Player>(id,x,y,z)); - } - else - { - DEBUG_LOG("Creature (Entry: %u GUID: %u) targeted point (ID: %u X: %f Y: %f Z: %f)", - i_owner->GetEntry(), i_owner->GetGUIDLow(), id, x, y, z ); - Mutate(new PointMovementGenerator<Creature>(id,x,y,z)); - } -} - -void -MotionMaster::MoveFleeing(Unit* enemy) -{ - if(!enemy) - return; - - if(i_owner->GetTypeId()==TYPEID_PLAYER) - { - DEBUG_LOG("Player (GUID: %u) flee from %s (GUID: %u)", i_owner->GetGUIDLow(), - enemy->GetTypeId()==TYPEID_PLAYER ? "player" : "creature", - enemy->GetTypeId()==TYPEID_PLAYER ? enemy->GetGUIDLow() : ((Creature*)enemy)->GetDBTableGUIDLow() ); - Mutate(new FleeingMovementGenerator<Player>(enemy->GetGUID())); - } - else - { - DEBUG_LOG("Creature (Entry: %u GUID: %u) flee from %s (GUID: %u)", - i_owner->GetEntry(), i_owner->GetGUIDLow(), - enemy->GetTypeId()==TYPEID_PLAYER ? "player" : "creature", - enemy->GetTypeId()==TYPEID_PLAYER ? enemy->GetGUIDLow() : ((Creature*)enemy)->GetDBTableGUIDLow() ); - Mutate(new FleeingMovementGenerator<Creature>(enemy->GetGUID())); - } -} - -void -MotionMaster::MoveTaxiFlight(uint32 path, uint32 pathnode) -{ - if(i_owner->GetTypeId()==TYPEID_PLAYER) - { - DEBUG_LOG("Player (GUID: %u) taxi to (Path %u node %u)", i_owner->GetGUIDLow(), path, pathnode); - FlightPathMovementGenerator* mgen = new FlightPathMovementGenerator(path,pathnode); - Mutate(mgen); - } - else - { - sLog.outError("Creature (Entry: %u GUID: %u) attempt taxi to (Path %u node %u)", - i_owner->GetEntry(), i_owner->GetGUIDLow(), path, pathnode ); - } -} - -void -MotionMaster::MoveDistract(uint32 timer) -{ - if(i_owner->GetTypeId()==TYPEID_PLAYER) - { - DEBUG_LOG("Player (GUID: %u) distracted (timer: %u)", i_owner->GetGUIDLow(), timer); - } - else - { - DEBUG_LOG("Creature (Entry: %u GUID: %u) (timer: %u)", - i_owner->GetEntry(), i_owner->GetGUIDLow(), timer); - } - - DistractMovementGenerator* mgen = new DistractMovementGenerator(timer); - Mutate(mgen); -} - -void MotionMaster::Mutate(MovementGenerator *m) -{ - if (!empty()) - { - switch(top()->GetMovementGeneratorType()) - { - // HomeMovement is not that important, delete it if meanwhile a new comes - case HOME_MOTION_TYPE: - // DistractMovement interrupted by any other movement - case DISTRACT_MOTION_TYPE: - MovementExpired(false); - } - } - m->Initialize(*i_owner); - push(m); -} - -void MotionMaster::propagateSpeedChange() -{ - Impl::container_type::iterator it = Impl::c.begin(); - for ( ;it != end(); ++it) - { - (*it)->unitSpeedChanged(); - } -} - -MovementGeneratorType MotionMaster::GetCurrentMovementGeneratorType() const -{ - if(empty()) - return IDLE_MOTION_TYPE; - - return top()->GetMovementGeneratorType(); -} - -bool MotionMaster::GetDestination(float &x, float &y, float &z) -{ - if(empty()) - return false; - - return top()->GetDestination(x,y,z); -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "MotionMaster.h"
+#include "CreatureAISelector.h"
+#include "Creature.h"
+#include "Traveller.h"
+
+#include "ConfusedMovementGenerator.h"
+#include "FleeingMovementGenerator.h"
+#include "HomeMovementGenerator.h"
+#include "IdleMovementGenerator.h"
+#include "PointMovementGenerator.h"
+#include "TargetedMovementGenerator.h"
+#include "WaypointMovementGenerator.h"
+
+#include <cassert>
+
+inline bool isStatic(MovementGenerator *mv)
+{
+ return (mv == &si_idleMovement);
+}
+
+void
+MotionMaster::Initialize()
+{
+ // clear ALL movement generators (including default)
+ while(!empty())
+ {
+ MovementGenerator *curr = top();
+ curr->Finalize(*i_owner);
+ pop();
+ if( !isStatic( curr ) )
+ delete curr;
+ }
+
+ // set new default movement generator
+ if(i_owner->GetTypeId() == TYPEID_UNIT)
+ {
+ MovementGenerator* movement = FactorySelector::selectMovementGenerator((Creature*)i_owner);
+ push( movement == NULL ? &si_idleMovement : movement );
+ top()->Initialize(*i_owner);
+ }
+ else
+ push(&si_idleMovement);
+}
+
+MotionMaster::~MotionMaster()
+{
+ // clear ALL movement generators (including default)
+ while(!empty())
+ {
+ MovementGenerator *curr = top();
+ curr->Finalize(*i_owner);
+ pop();
+ if( !isStatic( curr ) )
+ delete curr;
+ }
+}
+
+void
+MotionMaster::UpdateMotion(const uint32 &diff)
+{
+ if( i_owner->hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED) )
+ return;
+ assert( !empty() );
+ if (!top()->Update(*i_owner, diff))
+ MovementExpired();
+}
+
+void
+MotionMaster::Clear(bool reset)
+{
+ while( !empty() && size() > 1 )
+ {
+ MovementGenerator *curr = top();
+ curr->Finalize(*i_owner);
+ pop();
+ if( !isStatic( curr ) )
+ delete curr;
+ }
+
+ if (reset)
+ {
+ assert( !empty() );
+ top()->Reset(*i_owner);
+ }
+}
+
+void
+MotionMaster::MovementExpired(bool reset)
+{
+ if( empty() || size() == 1 )
+ return;
+
+ MovementGenerator *curr = top();
+ curr->Finalize(*i_owner);
+ pop();
+
+ if( !isStatic(curr) )
+ delete curr;
+
+ assert( !empty() );
+ while( !empty() && top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE )
+ {
+ // Should check if target is still valid? If not valid it will crash.
+ curr = top();
+ curr->Finalize(*i_owner);
+ pop();
+ delete curr;
+ }
+ if( empty() )
+ Initialize();
+ if (reset) top()->Reset(*i_owner);
+}
+
+void MotionMaster::MoveIdle()
+{
+ if( empty() || !isStatic( top() ) )
+ push( &si_idleMovement );
+}
+
+void
+MotionMaster::MoveTargetedHome()
+{
+ if(i_owner->hasUnitState(UNIT_STAT_FLEEING))
+ return;
+
+ Clear(false);
+
+ if(i_owner->GetTypeId()==TYPEID_UNIT && !((Creature*)i_owner)->GetCharmerOrOwnerGUID())
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) targeted home", i_owner->GetEntry(), i_owner->GetGUIDLow());
+ Mutate(new HomeMovementGenerator<Creature>());
+ }
+ else if(i_owner->GetTypeId()==TYPEID_UNIT && ((Creature*)i_owner)->GetCharmerOrOwnerGUID())
+ {
+ sLog.outError("Pet or controlled creature (Entry: %u GUID: %u) attempt targeted home",
+ i_owner->GetEntry(), i_owner->GetGUIDLow() );
+ }
+ else
+ {
+ sLog.outError("Player (GUID: %u) attempt targeted home", i_owner->GetGUIDLow() );
+ }
+}
+
+void
+MotionMaster::MoveConfused()
+{
+ if(i_owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) move confused", i_owner->GetGUIDLow() );
+ Mutate(new ConfusedMovementGenerator<Player>());
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) move confused",
+ i_owner->GetEntry(), i_owner->GetGUIDLow() );
+ Mutate(new ConfusedMovementGenerator<Creature>());
+ }
+}
+
+void
+MotionMaster::MoveChase(Unit* target, float dist, float angle)
+{
+ // ignore movement request if target not exist
+ if(!target)
+ return;
+
+ i_owner->clearUnitState(UNIT_STAT_FOLLOW);
+ if(i_owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) chase to %s (GUID: %u)",
+ target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature",
+ target->GetTypeId()==TYPEID_PLAYER ? i_owner->GetGUIDLow() : ((Creature*)i_owner)->GetDBTableGUIDLow() );
+ Mutate(new TargetedMovementGenerator<Player>(*target,dist,angle));
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) chase to %s (GUID: %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(),
+ target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature",
+ target->GetTypeId()==TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow() );
+ Mutate(new TargetedMovementGenerator<Creature>(*target,dist,angle));
+ }
+}
+
+void
+MotionMaster::MoveFollow(Unit* target, float dist, float angle)
+{
+ Clear();
+
+ // ignore movement request if target not exist
+ if(!target)
+ return;
+
+ i_owner->addUnitState(UNIT_STAT_FOLLOW);
+ if(i_owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) follow to %s (GUID: %u)", i_owner->GetGUIDLow(),
+ target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature",
+ target->GetTypeId()==TYPEID_PLAYER ? i_owner->GetGUIDLow() : ((Creature*)i_owner)->GetDBTableGUIDLow() );
+ Mutate(new TargetedMovementGenerator<Player>(*target,dist,angle));
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) follow to %s (GUID: %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(),
+ target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature",
+ target->GetTypeId()==TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow() );
+ Mutate(new TargetedMovementGenerator<Creature>(*target,dist,angle));
+ }
+}
+
+void
+MotionMaster::MovePoint(uint32 id, float x, float y, float z)
+{
+ if(i_owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) targeted point (Id: %u X: %f Y: %f Z: %f)", i_owner->GetGUIDLow(), id, x, y, z );
+ Mutate(new PointMovementGenerator<Player>(id,x,y,z));
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) targeted point (ID: %u X: %f Y: %f Z: %f)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(), id, x, y, z );
+ Mutate(new PointMovementGenerator<Creature>(id,x,y,z));
+ }
+}
+
+void
+MotionMaster::MoveFleeing(Unit* enemy)
+{
+ if(!enemy)
+ return;
+
+ if(i_owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) flee from %s (GUID: %u)", i_owner->GetGUIDLow(),
+ enemy->GetTypeId()==TYPEID_PLAYER ? "player" : "creature",
+ enemy->GetTypeId()==TYPEID_PLAYER ? enemy->GetGUIDLow() : ((Creature*)enemy)->GetDBTableGUIDLow() );
+ Mutate(new FleeingMovementGenerator<Player>(enemy->GetGUID()));
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) flee from %s (GUID: %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(),
+ enemy->GetTypeId()==TYPEID_PLAYER ? "player" : "creature",
+ enemy->GetTypeId()==TYPEID_PLAYER ? enemy->GetGUIDLow() : ((Creature*)enemy)->GetDBTableGUIDLow() );
+ Mutate(new FleeingMovementGenerator<Creature>(enemy->GetGUID()));
+ }
+}
+
+void
+MotionMaster::MoveTaxiFlight(uint32 path, uint32 pathnode)
+{
+ if(i_owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) taxi to (Path %u node %u)", i_owner->GetGUIDLow(), path, pathnode);
+ FlightPathMovementGenerator* mgen = new FlightPathMovementGenerator(path,pathnode);
+ Mutate(mgen);
+ }
+ else
+ {
+ sLog.outError("Creature (Entry: %u GUID: %u) attempt taxi to (Path %u node %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(), path, pathnode );
+ }
+}
+
+void
+MotionMaster::MoveDistract(uint32 timer)
+{
+ if(i_owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) distracted (timer: %u)", i_owner->GetGUIDLow(), timer);
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) (timer: %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(), timer);
+ }
+
+ DistractMovementGenerator* mgen = new DistractMovementGenerator(timer);
+ Mutate(mgen);
+}
+
+void MotionMaster::Mutate(MovementGenerator *m)
+{
+ if (!empty())
+ {
+ switch(top()->GetMovementGeneratorType())
+ {
+ // HomeMovement is not that important, delete it if meanwhile a new comes
+ case HOME_MOTION_TYPE:
+ // DistractMovement interrupted by any other movement
+ case DISTRACT_MOTION_TYPE:
+ MovementExpired(false);
+ }
+ }
+ m->Initialize(*i_owner);
+ push(m);
+}
+
+void MotionMaster::propagateSpeedChange()
+{
+ Impl::container_type::iterator it = Impl::c.begin();
+ for ( ;it != end(); ++it)
+ {
+ (*it)->unitSpeedChanged();
+ }
+}
+
+MovementGeneratorType MotionMaster::GetCurrentMovementGeneratorType() const
+{
+ if(empty())
+ return IDLE_MOTION_TYPE;
+
+ return top()->GetMovementGeneratorType();
+}
+
+bool MotionMaster::GetDestination(float &x, float &y, float &z)
+{
+ if(empty())
+ return false;
+
+ return top()->GetDestination(x,y,z);
+}
diff --git a/src/game/Object.cpp b/src/game/Object.cpp index 7266d5612e7..1b690902023 100644 --- a/src/game/Object.cpp +++ b/src/game/Object.cpp @@ -1,1447 +1,1447 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "Common.h" -#include "SharedDefines.h" -#include "WorldPacket.h" -#include "Opcodes.h" -#include "Log.h" -#include "World.h" -#include "Object.h" -#include "Creature.h" -#include "Player.h" -#include "ObjectMgr.h" -#include "WorldSession.h" -#include "UpdateData.h" -#include "UpdateMask.h" -#include "Util.h" -#include "MapManager.h" -#include "ObjectAccessor.h" -#include "Log.h" -#include "Transports.h" -#include "TargetedMovementGenerator.h" -#include "WaypointMovementGenerator.h" -#include "VMapFactory.h" -#include "CellImpl.h" -#include "GridNotifiers.h" -#include "GridNotifiersImpl.h" - -#include "TemporarySummon.h" - -uint32 GuidHigh2TypeId(uint32 guid_hi) -{ - switch(guid_hi) - { - case HIGHGUID_ITEM: return TYPEID_ITEM; - //case HIGHGUID_CONTAINER: return TYPEID_CONTAINER; HIGHGUID_CONTAINER==HIGHGUID_ITEM currently - case HIGHGUID_UNIT: return TYPEID_UNIT; - case HIGHGUID_PET: return TYPEID_UNIT; - case HIGHGUID_PLAYER: return TYPEID_PLAYER; - case HIGHGUID_GAMEOBJECT: return TYPEID_GAMEOBJECT; - case HIGHGUID_DYNAMICOBJECT:return TYPEID_DYNAMICOBJECT; - case HIGHGUID_CORPSE: return TYPEID_CORPSE; - case HIGHGUID_MO_TRANSPORT: return TYPEID_GAMEOBJECT; - } - return 10; // unknown -} - -Object::Object( ) -{ - m_objectTypeId = TYPEID_OBJECT; - m_objectType = TYPEMASK_OBJECT; - - m_uint32Values = 0; - m_uint32Values_mirror = 0; - m_valuesCount = 0; - - m_inWorld = false; - m_objectUpdated = false; - - m_PackGUID.clear(); - m_PackGUID.appendPackGUID(0); -} - -Object::~Object( ) -{ - if(m_objectUpdated) - ObjectAccessor::Instance().RemoveUpdateObject(this); - - if(m_uint32Values) - { - if(IsInWorld()) - { - ///- Do NOT call RemoveFromWorld here, if the object is a player it will crash - sLog.outError("Object::~Object - guid="I64FMTD", typeid=%d deleted but still in world!!", GetGUID(), GetTypeId()); - //assert(0); - } - - //DEBUG_LOG("Object desctr 1 check (%p)",(void*)this); - delete [] m_uint32Values; - delete [] m_uint32Values_mirror; - //DEBUG_LOG("Object desctr 2 check (%p)",(void*)this); - } -} - -void Object::_InitValues() -{ - m_uint32Values = new uint32[ m_valuesCount ]; - memset(m_uint32Values, 0, m_valuesCount*sizeof(uint32)); - - m_uint32Values_mirror = new uint32[ m_valuesCount ]; - memset(m_uint32Values_mirror, 0, m_valuesCount*sizeof(uint32)); - - m_objectUpdated = false; -} - -void Object::_Create( uint32 guidlow, uint32 entry, HighGuid guidhigh ) -{ - if(!m_uint32Values) _InitValues(); - - uint64 guid = MAKE_NEW_GUID(guidlow, entry, guidhigh); // required more changes to make it working - SetUInt64Value( OBJECT_FIELD_GUID, guid ); - SetUInt32Value( OBJECT_FIELD_TYPE, m_objectType ); - m_PackGUID.clear(); - m_PackGUID.appendPackGUID(GetGUID()); -} - -void Object::BuildMovementUpdateBlock(UpdateData * data, uint32 flags ) const -{ - ByteBuffer buf(500); - - buf << uint8( UPDATETYPE_MOVEMENT ); - buf << GetGUID(); - - _BuildMovementUpdate(&buf, flags, 0x00000000); - - data->AddUpdateBlock(buf); -} - -void Object::BuildCreateUpdateBlockForPlayer(UpdateData *data, Player *target) const -{ - if(!target) - { - return; - } - - uint8 updatetype = UPDATETYPE_CREATE_OBJECT; - uint8 flags = m_updateFlag; - uint32 flags2 = 0; - - /** lower flag1 **/ - if(target == this) // building packet for oneself - { - flags |= UPDATEFLAG_SELF; - - /*** temporary reverted - until real source of stack corruption will not found - updatetype = UPDATETYPE_CREATE_OBJECT2; - ****/ - } - - if(flags & UPDATEFLAG_HASPOSITION) - { - // UPDATETYPE_CREATE_OBJECT2 dynamic objects, corpses... - if(isType(TYPEMASK_DYNAMICOBJECT) || isType(TYPEMASK_CORPSE) || isType(TYPEMASK_PLAYER)) - updatetype = UPDATETYPE_CREATE_OBJECT2; - - // UPDATETYPE_CREATE_OBJECT2 for pets... - if(target->GetPetGUID() == GetGUID()) - updatetype = UPDATETYPE_CREATE_OBJECT2; - - // UPDATETYPE_CREATE_OBJECT2 for some gameobject types... - if(isType(TYPEMASK_GAMEOBJECT)) - { - switch(((GameObject*)this)->GetGoType()) - { - case GAMEOBJECT_TYPE_TRAP: - case GAMEOBJECT_TYPE_DUEL_ARBITER: - case GAMEOBJECT_TYPE_FLAGSTAND: - case GAMEOBJECT_TYPE_FLAGDROP: - updatetype = UPDATETYPE_CREATE_OBJECT2; - break; - case GAMEOBJECT_TYPE_TRANSPORT: - flags |= UPDATEFLAG_TRANSPORT; - break; - } - } - } - - //sLog.outDebug("BuildCreateUpdate: update-type: %u, object-type: %u got flags: %X, flags2: %X", updatetype, m_objectTypeId, flags, flags2); - - ByteBuffer buf(500); - buf << (uint8)updatetype; - //buf.append(GetPackGUID()); //client crashes when using this - buf << (uint8)0xFF << GetGUID(); - buf << (uint8)m_objectTypeId; - - _BuildMovementUpdate(&buf, flags, flags2); - - UpdateMask updateMask; - updateMask.SetCount( m_valuesCount ); - _SetCreateBits( &updateMask, target ); - _BuildValuesUpdate(updatetype, &buf, &updateMask, target ); - data->AddUpdateBlock(buf); -} - -void Object::BuildUpdate(UpdateDataMapType &update_players) -{ - ObjectAccessor::_buildUpdateObject(this,update_players); - ClearUpdateMask(true); -} - -void Object::SendUpdateToPlayer(Player* player) -{ - // send update to another players - SendUpdateObjectToAllExcept(player); - - // send create update to player - UpdateData upd; - WorldPacket packet; - - upd.Clear(); - BuildCreateUpdateBlockForPlayer(&upd, player); - upd.BuildPacket(&packet); - player->GetSession()->SendPacket(&packet); - - // now object updated/(create updated) -} - -void Object::BuildValuesUpdateBlockForPlayer(UpdateData *data, Player *target) const -{ - ByteBuffer buf(500); - - buf << (uint8) UPDATETYPE_VALUES; - //buf.append(GetPackGUID()); //client crashes when using this. but not have crash in debug mode - buf << (uint8)0xFF; - buf << GetGUID(); - - UpdateMask updateMask; - updateMask.SetCount( m_valuesCount ); - - _SetUpdateBits( &updateMask, target ); - _BuildValuesUpdate(UPDATETYPE_VALUES, &buf, &updateMask, target ); - - data->AddUpdateBlock(buf); -} - -void Object::BuildOutOfRangeUpdateBlock(UpdateData * data) const -{ - data->AddOutOfRangeGUID(GetGUID()); -} - -void Object::DestroyForPlayer(Player *target) const -{ - ASSERT(target); - - WorldPacket data(SMSG_DESTROY_OBJECT, 8); - data << GetGUID(); - target->GetSession()->SendPacket( &data ); -} - -void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 ) const -{ - *data << (uint8)flags; // update flags - - // 0x20 - if (flags & UPDATEFLAG_LIVING) - { - switch(GetTypeId()) - { - case TYPEID_UNIT: - { - flags2 = ((Unit*)this)->GetUnitMovementFlags(); - } - break; - case TYPEID_PLAYER: - { - flags2 = ((Player*)this)->GetUnitMovementFlags(); - - if(((Player*)this)->GetTransport()) - flags2 |= MOVEMENTFLAG_ONTRANSPORT; - else - flags2 &= ~MOVEMENTFLAG_ONTRANSPORT; - - // remove unknown, unused etc flags for now - flags2 &= ~MOVEMENTFLAG_SPLINE2; // will be set manually - - if(((Player*)this)->isInFlight()) - { - WPAssert(((Player*)this)->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE); - flags2 = (MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_SPLINE2); - } - } - break; - } - - *data << uint32(flags2); // movement flags - *data << uint8(0); // unk 2.3.0 - *data << uint32(getMSTime()); // time (in milliseconds) - } - - // 0x40 - if (flags & UPDATEFLAG_HASPOSITION) - { - // 0x02 - if(flags & UPDATEFLAG_TRANSPORT && ((GameObject*)this)->GetGoType() == GAMEOBJECT_TYPE_MO_TRANSPORT) - { - *data << (float)0; - *data << (float)0; - *data << (float)0; - *data << ((WorldObject *)this)->GetOrientation(); - } - else - { - *data << ((WorldObject *)this)->GetPositionX(); - *data << ((WorldObject *)this)->GetPositionY(); - *data << ((WorldObject *)this)->GetPositionZ(); - *data << ((WorldObject *)this)->GetOrientation(); - } - } - - // 0x20 - if(flags & UPDATEFLAG_LIVING) - { - // 0x00000200 - if(flags2 & MOVEMENTFLAG_ONTRANSPORT) - { - if(GetTypeId() == TYPEID_PLAYER) - { - *data << (uint64)((Player*)this)->GetTransport()->GetGUID(); - *data << (float)((Player*)this)->GetTransOffsetX(); - *data << (float)((Player*)this)->GetTransOffsetY(); - *data << (float)((Player*)this)->GetTransOffsetZ(); - *data << (float)((Player*)this)->GetTransOffsetO(); - *data << (uint32)((Player*)this)->GetTransTime(); - } - //MaNGOS currently not have support for other than player on transport - } - - // 0x02200000 - if(flags2 & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2)) - { - if(GetTypeId() == TYPEID_PLAYER) - *data << (float)((Player*)this)->m_movementInfo.s_pitch; - else - *data << (float)0; // is't part of movement packet, we must store and send it... - } - - if(GetTypeId() == TYPEID_PLAYER) - *data << (uint32)((Player*)this)->m_movementInfo.fallTime; - else - *data << (uint32)0; // last fall time - - // 0x00001000 - if(flags2 & MOVEMENTFLAG_JUMPING) - { - if(GetTypeId() == TYPEID_PLAYER) - { - *data << (float)((Player*)this)->m_movementInfo.j_unk; - *data << (float)((Player*)this)->m_movementInfo.j_sinAngle; - *data << (float)((Player*)this)->m_movementInfo.j_cosAngle; - *data << (float)((Player*)this)->m_movementInfo.j_xyspeed; - } - else - { - *data << (float)0; - *data << (float)0; - *data << (float)0; - *data << (float)0; - } - } - - // 0x04000000 - if(flags2 & MOVEMENTFLAG_SPLINE) - { - if(GetTypeId() == TYPEID_PLAYER) - *data << (float)((Player*)this)->m_movementInfo.u_unk1; - else - *data << (float)0; - } - - *data << ((Unit*)this)->GetSpeed( MOVE_WALK ); - *data << ((Unit*)this)->GetSpeed( MOVE_RUN ); - *data << ((Unit*)this)->GetSpeed( MOVE_SWIMBACK ); - *data << ((Unit*)this)->GetSpeed( MOVE_SWIM ); - *data << ((Unit*)this)->GetSpeed( MOVE_WALKBACK ); - *data << ((Unit*)this)->GetSpeed( MOVE_FLY ); - *data << ((Unit*)this)->GetSpeed( MOVE_FLYBACK ); - *data << ((Unit*)this)->GetSpeed( MOVE_TURN ); - - // 0x08000000 - if(flags2 & MOVEMENTFLAG_SPLINE2) - { - if(GetTypeId() != TYPEID_PLAYER) - { - sLog.outDebug("_BuildMovementUpdate: MOVEMENTFLAG_SPLINE2 for non-player"); - return; - } - - if(!((Player*)this)->isInFlight()) - { - sLog.outDebug("_BuildMovementUpdate: MOVEMENTFLAG_SPLINE2 but not in flight"); - return; - } - - WPAssert(((Player*)this)->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE); - - FlightPathMovementGenerator *fmg = (FlightPathMovementGenerator*)(((Player*)this)->GetMotionMaster()->top()); - - uint32 flags3 = 0x00000300; - - *data << uint32(flags3); // splines flag? - - if(flags3 & 0x10000) // probably x,y,z coords there - { - *data << (float)0; - *data << (float)0; - *data << (float)0; - } - - if(flags3 & 0x20000) // probably guid there - { - *data << uint64(0); - } - - if(flags3 & 0x40000) // may be orientation - { - *data << (float)0; - } - - Path &path = fmg->GetPath(); - - float x, y, z; - ((Player*)this)->GetPosition(x, y, z); - - uint32 inflighttime = uint32(path.GetPassedLength(fmg->GetCurrentNode(), x, y, z) * 32); - uint32 traveltime = uint32(path.GetTotalLength() * 32); - - *data << uint32(inflighttime); // passed move time? - *data << uint32(traveltime); // full move time? - *data << uint32(0); // ticks count? - - uint32 poscount = uint32(path.Size()); - - *data << uint32(poscount); // points count - - for(uint32 i = 0; i < poscount; ++i) - { - *data << path.GetNodes()[i].x; - *data << path.GetNodes()[i].y; - *data << path.GetNodes()[i].z; - } - - /*for(uint32 i = 0; i < poscount; i++) - { - // path points - *data << (float)0; - *data << (float)0; - *data << (float)0; - }*/ - - *data << path.GetNodes()[poscount-1].x; - *data << path.GetNodes()[poscount-1].y; - *data << path.GetNodes()[poscount-1].z; - - // target position (path end) - /**data << ((Unit*)this)->GetPositionX(); - *data << ((Unit*)this)->GetPositionY(); - *data << ((Unit*)this)->GetPositionZ();*/ - } - } - - // 0x8 - if(flags & UPDATEFLAG_LOWGUID) - { - switch(GetTypeId()) - { - case TYPEID_OBJECT: - case TYPEID_ITEM: - case TYPEID_CONTAINER: - case TYPEID_GAMEOBJECT: - case TYPEID_DYNAMICOBJECT: - case TYPEID_CORPSE: - *data << uint32(GetGUIDLow()); // GetGUIDLow() - break; - case TYPEID_UNIT: - *data << uint32(0x0000000B); // unk, can be 0xB or 0xC - break; - case TYPEID_PLAYER: - if(flags & UPDATEFLAG_SELF) - *data << uint32(0x00000015); // unk, can be 0x15 or 0x22 - else - *data << uint32(0x00000008); // unk, can be 0x7 or 0x8 - break; - default: - *data << uint32(0x00000000); // unk - break; - } - } - - // 0x10 - if(flags & UPDATEFLAG_HIGHGUID) - { - switch(GetTypeId()) - { - case TYPEID_OBJECT: - case TYPEID_ITEM: - case TYPEID_CONTAINER: - case TYPEID_GAMEOBJECT: - case TYPEID_DYNAMICOBJECT: - case TYPEID_CORPSE: - *data << uint32(GetGUIDHigh()); // GetGUIDHigh() - break; - default: - *data << uint32(0x00000000); // unk - break; - } - } - - // 0x4 - if(flags & UPDATEFLAG_FULLGUID) - { - *data << uint8(0); // packed guid (probably target guid) - } - - // 0x2 - if(flags & UPDATEFLAG_TRANSPORT) - { - *data << uint32(getMSTime()); // ms time - } -} - -void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask *updateMask, Player *target) const -{ - if(!target) - return; - - bool IsActivateToQuest = false; - if (updatetype == UPDATETYPE_CREATE_OBJECT || updatetype == UPDATETYPE_CREATE_OBJECT2) - { - if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsTransport()) - { - if ( ((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster()) - { - IsActivateToQuest = true; - updateMask->SetBit(GAMEOBJECT_DYN_FLAGS); - } - } - } - else //case UPDATETYPE_VALUES - { - if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsTransport()) - { - if ( ((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster()) - { - IsActivateToQuest = true; - } - updateMask->SetBit(GAMEOBJECT_DYN_FLAGS); - updateMask->SetBit(GAMEOBJECT_ANIMPROGRESS); - } - } - - WPAssert(updateMask && updateMask->GetCount() == m_valuesCount); - - *data << (uint8)updateMask->GetBlockCount(); - data->append( updateMask->GetMask(), updateMask->GetLength() ); - - // 2 specialized loops for speed optimization in non-unit case - if(isType(TYPEMASK_UNIT)) // unit (creature/player) case - { - for( uint16 index = 0; index < m_valuesCount; index ++ ) - { - if( updateMask->GetBit( index ) ) - { - // remove custom flag before send - if( index == UNIT_NPC_FLAGS ) - *data << uint32(m_uint32Values[ index ] & ~UNIT_NPC_FLAG_GUARD); - // FIXME: Some values at server stored in float format but must be sent to client in uint32 format - else if(index >= UNIT_FIELD_BASEATTACKTIME && index <= UNIT_FIELD_RANGEDATTACKTIME) - { - // convert from float to uint32 and send - *data << uint32(m_floatValues[ index ] < 0 ? 0 : m_floatValues[ index ]); - } - // there are some float values which may be negative or can't get negative due to other checks - else if(index >= UNIT_FIELD_NEGSTAT0 && index <= UNIT_FIELD_NEGSTAT4 || - index >= UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE && index <= (UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE + 6) || - index >= UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE && index <= (UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE + 6) || - index >= UNIT_FIELD_POSSTAT0 && index <= UNIT_FIELD_POSSTAT4) - { - *data << uint32(m_floatValues[ index ]); - } - // Gamemasters should be always able to select units - remove not selectable flag - else if(index == UNIT_FIELD_FLAGS && target->isGameMaster()) - { - *data << (m_uint32Values[ index ] & ~UNIT_FLAG_NOT_SELECTABLE); - } - // hide lootable animation for unallowed players - else if(index == UNIT_DYNAMIC_FLAGS && GetTypeId() == TYPEID_UNIT) - { - if(!target->isAllowedToLoot((Creature*)this)) - *data << (m_uint32Values[ index ] & ~UNIT_DYNFLAG_LOOTABLE); - else - *data << (m_uint32Values[ index ] & ~UNIT_DYNFLAG_OTHER_TAGGER); - } - else - { - // send in current format (float as float, uint32 as uint32) - *data << m_uint32Values[ index ]; - } - } - } - } - else if(isType(TYPEMASK_GAMEOBJECT)) // gameobject case - { - for( uint16 index = 0; index < m_valuesCount; index ++ ) - { - if( updateMask->GetBit( index ) ) - { - // send in current format (float as float, uint32 as uint32) - if ( index == GAMEOBJECT_DYN_FLAGS ) - { - if(IsActivateToQuest ) - { - switch(((GameObject*)this)->GetGoType()) - { - case GAMEOBJECT_TYPE_CHEST: - *data << uint32(9); // enable quest object. Represent 9, but 1 for client before 2.3.0 - break; - case GAMEOBJECT_TYPE_GOOBER: - *data << uint32(1); - break; - default: - *data << uint32(0); //unknown. not happen. - break; - } - } - else - *data << uint32(0); // disable quest object - } - else - *data << m_uint32Values[ index ]; // other cases - } - } - } - else // other objects case (no special index checks) - { - for( uint16 index = 0; index < m_valuesCount; index ++ ) - { - if( updateMask->GetBit( index ) ) - { - // send in current format (float as float, uint32 as uint32) - *data << m_uint32Values[ index ]; - } - } - } -} - -void Object::ClearUpdateMask(bool remove) -{ - for( uint16 index = 0; index < m_valuesCount; index ++ ) - { - if(m_uint32Values_mirror[index]!= m_uint32Values[index]) - m_uint32Values_mirror[index] = m_uint32Values[index]; - } - if(m_objectUpdated) - { - if(remove) - ObjectAccessor::Instance().RemoveUpdateObject(this); - m_objectUpdated = false; - } -} - -// Send current value fields changes to all viewers -void Object::SendUpdateObjectToAllExcept(Player* exceptPlayer) -{ - // changes will be send in create packet - if(!IsInWorld()) - return; - - // nothing do - if(!m_objectUpdated) - return; - - ObjectAccessor::UpdateObject(this,exceptPlayer); -} - -bool Object::LoadValues(const char* data) -{ - if(!m_uint32Values) _InitValues(); - - Tokens tokens = StrSplit(data, " "); - - if(tokens.size() != m_valuesCount) - return false; - - Tokens::iterator iter; - int index; - for (iter = tokens.begin(), index = 0; index < m_valuesCount; ++iter, ++index) - { - m_uint32Values[index] = atol((*iter).c_str()); - } - - return true; -} - -void Object::_SetUpdateBits(UpdateMask *updateMask, Player* /*target*/) const -{ - for( uint16 index = 0; index < m_valuesCount; index ++ ) - { - if(m_uint32Values_mirror[index]!= m_uint32Values[index]) - updateMask->SetBit(index); - } -} - -void Object::_SetCreateBits(UpdateMask *updateMask, Player* /*target*/) const -{ - for( uint16 index = 0; index < m_valuesCount; index++ ) - { - if(GetUInt32Value(index) != 0) - updateMask->SetBit(index); - } -} - -void Object::SetInt32Value( uint16 index, int32 value ) -{ - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); - - if(m_int32Values[ index ] != value) - { - m_int32Values[ index ] = value; - - if(m_inWorld) - { - if(!m_objectUpdated) - { - ObjectAccessor::Instance().AddUpdateObject(this); - m_objectUpdated = true; - } - } - } -} - -void Object::SetUInt32Value( uint16 index, uint32 value ) -{ - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); - - if(m_uint32Values[ index ] != value) - { - m_uint32Values[ index ] = value; - - if(m_inWorld) - { - if(!m_objectUpdated) - { - ObjectAccessor::Instance().AddUpdateObject(this); - m_objectUpdated = true; - } - } - } -} - -void Object::SetUInt64Value( uint16 index, const uint64 &value ) -{ - ASSERT( index + 1 < m_valuesCount || PrintIndexError( index , true ) ); - if(*((uint64*)&(m_uint32Values[ index ])) != value) - { - m_uint32Values[ index ] = *((uint32*)&value); - m_uint32Values[ index + 1 ] = *(((uint32*)&value) + 1); - - if(m_inWorld) - { - if(!m_objectUpdated) - { - ObjectAccessor::Instance().AddUpdateObject(this); - m_objectUpdated = true; - } - } - } -} - -void Object::SetFloatValue( uint16 index, float value ) -{ - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); - - if(m_floatValues[ index ] != value) - { - m_floatValues[ index ] = value; - - if(m_inWorld) - { - if(!m_objectUpdated) - { - ObjectAccessor::Instance().AddUpdateObject(this); - m_objectUpdated = true; - } - } - } -} - -void Object::SetByteValue( uint16 index, uint8 offset, uint8 value ) -{ - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); - - if(offset > 4) - { - sLog.outError("Object::SetByteValue: wrong offset %u", offset); - return; - } - - if(uint8(m_uint32Values[ index ] >> (offset * 8)) != value) - { - m_uint32Values[ index ] &= ~uint32(uint32(0xFF) << (offset * 8)); - m_uint32Values[ index ] |= uint32(uint32(value) << (offset * 8)); - - if(m_inWorld) - { - if(!m_objectUpdated) - { - ObjectAccessor::Instance().AddUpdateObject(this); - m_objectUpdated = true; - } - } - } -} - -void Object::SetUInt16Value( uint16 index, uint8 offset, uint16 value ) -{ - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); - - if(offset > 2) - { - sLog.outError("Object::SetUInt16Value: wrong offset %u", offset); - return; - } - - if(uint8(m_uint32Values[ index ] >> (offset * 16)) != value) - { - m_uint32Values[ index ] &= ~uint32(uint32(0xFFFF) << (offset * 16)); - m_uint32Values[ index ] |= uint32(uint32(value) << (offset * 16)); - - if(m_inWorld) - { - if(!m_objectUpdated) - { - ObjectAccessor::Instance().AddUpdateObject(this); - m_objectUpdated = true; - } - } - } -} - -void Object::SetStatFloatValue( uint16 index, float value) -{ - if(value < 0) - value = 0.0f; - - SetFloatValue(index, value); -} - -void Object::SetStatInt32Value( uint16 index, int32 value) -{ - if(value < 0) - value = 0; - - SetUInt32Value(index, uint32(value)); -} - -void Object::ApplyModUInt32Value(uint16 index, int32 val, bool apply) -{ - int32 cur = GetUInt32Value(index); - cur += (apply ? val : -val); - if(cur < 0) - cur = 0; - SetUInt32Value(index,cur); -} - -void Object::ApplyModInt32Value(uint16 index, int32 val, bool apply) -{ - int32 cur = GetInt32Value(index); - cur += (apply ? val : -val); - SetInt32Value(index,cur); -} - -void Object::ApplyModSignedFloatValue(uint16 index, float val, bool apply) -{ - float cur = GetFloatValue(index); - cur += (apply ? val : -val); - SetFloatValue(index,cur); -} - -void Object::ApplyModPositiveFloatValue(uint16 index, float val, bool apply) -{ - float cur = GetFloatValue(index); - cur += (apply ? val : -val); - if(cur < 0) - cur = 0; - SetFloatValue(index,cur); -} - -void Object::SetFlag( uint16 index, uint32 newFlag ) -{ - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); - uint32 oldval = m_uint32Values[ index ]; - uint32 newval = oldval | newFlag; - - if(oldval != newval) - { - m_uint32Values[ index ] = newval; - - if(m_inWorld) - { - if(!m_objectUpdated) - { - ObjectAccessor::Instance().AddUpdateObject(this); - m_objectUpdated = true; - } - } - } -} - -void Object::RemoveFlag( uint16 index, uint32 oldFlag ) -{ - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); - uint32 oldval = m_uint32Values[ index ]; - uint32 newval = oldval & ~oldFlag; - - if(oldval != newval) - { - m_uint32Values[ index ] = newval; - - if(m_inWorld) - { - if(!m_objectUpdated) - { - ObjectAccessor::Instance().AddUpdateObject(this); - m_objectUpdated = true; - } - } - } -} - -bool Object::PrintIndexError(uint32 index, bool set) const -{ - sLog.outError("ERROR: Attempt %s non-existed value field: %u (count: %u) for object typeid: %u type mask: %u",(set ? "set value to" : "get value from"),index,m_valuesCount,GetTypeId(),m_objectType); - - // assert must fail after function call - return false; -} - -WorldObject::WorldObject() -{ - m_positionX = 0.0f; - m_positionY = 0.0f; - m_positionZ = 0.0f; - m_orientation = 0.0f; - - m_mapId = 0; - m_InstanceId = 0; - - m_name = ""; - - mSemaphoreTeleport = false; -} - -void WorldObject::_Create( uint32 guidlow, HighGuid guidhigh, uint32 mapid ) -{ - Object::_Create(guidlow, 0, guidhigh); - - m_mapId = mapid; -} - -uint32 WorldObject::GetZoneId() const -{ - return MapManager::Instance().GetBaseMap(m_mapId)->GetZoneId(m_positionX,m_positionY); -} - -uint32 WorldObject::GetAreaId() const -{ - return MapManager::Instance().GetBaseMap(m_mapId)->GetAreaId(m_positionX,m_positionY); -} - -InstanceData* WorldObject::GetInstanceData() -{ - Map *map = MapManager::Instance().GetMap(m_mapId, this); - return map->IsDungeon() ? ((InstanceMap*)map)->GetInstanceData() : NULL; -} - - //slow -float WorldObject::GetDistance(const WorldObject* obj) const -{ - float dx = GetPositionX() - obj->GetPositionX(); - float dy = GetPositionY() - obj->GetPositionY(); - float dz = GetPositionZ() - obj->GetPositionZ(); - float sizefactor = GetObjectSize() + obj->GetObjectSize(); - float dist = sqrt((dx*dx) + (dy*dy) + (dz*dz)) - sizefactor; - return ( dist > 0 ? dist : 0); -} - -float WorldObject::GetDistance2d(float x, float y) const -{ - float dx = GetPositionX() - x; - float dy = GetPositionY() - y; - float sizefactor = GetObjectSize(); - float dist = sqrt((dx*dx) + (dy*dy)) - sizefactor; - return ( dist > 0 ? dist : 0); -} - -float WorldObject::GetDistance(const float x, const float y, const float z) const -{ - float dx = GetPositionX() - x; - float dy = GetPositionY() - y; - float dz = GetPositionZ() - z; - float sizefactor = GetObjectSize(); - float dist = sqrt((dx*dx) + (dy*dy) + (dz*dz)) - sizefactor; - return ( dist > 0 ? dist : 0); -} - -float WorldObject::GetDistance2d(const WorldObject* obj) const -{ - float dx = GetPositionX() - obj->GetPositionX(); - float dy = GetPositionY() - obj->GetPositionY(); - float sizefactor = GetObjectSize() + obj->GetObjectSize(); - float dist = sqrt((dx*dx) + (dy*dy)) - sizefactor; - return ( dist > 0 ? dist : 0); -} - -float WorldObject::GetDistanceZ(const WorldObject* obj) const -{ - float dz = fabs(GetPositionZ() - obj->GetPositionZ()); - float sizefactor = GetObjectSize() + obj->GetObjectSize(); - float dist = dz - sizefactor; - return ( dist > 0 ? dist : 0); -} - -bool WorldObject::IsWithinDistInMap(const WorldObject* obj, const float dist2compare) const -{ - if (!obj || !IsInMap(obj)) return false; - - float dx = GetPositionX() - obj->GetPositionX(); - float dy = GetPositionY() - obj->GetPositionY(); - float dz = GetPositionZ() - obj->GetPositionZ(); - float distsq = dx*dx + dy*dy + dz*dz; - float sizefactor = GetObjectSize() + obj->GetObjectSize(); - float maxdist = dist2compare + sizefactor; - - return distsq < maxdist * maxdist; -} - -bool WorldObject::IsWithinLOSInMap(const WorldObject* obj) const -{ - if (!IsInMap(obj)) return false; - float ox,oy,oz; - obj->GetPosition(ox,oy,oz); - return(IsWithinLOS(ox, oy, oz )); -} - -bool WorldObject::IsWithinLOS(const float ox, const float oy, const float oz ) const -{ - float x,y,z; - GetPosition(x,y,z); - VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager(); - return vMapManager->isInLineOfSight(GetMapId(), x, y, z+2.0f, ox, oy, oz+2.0f); -} - -float WorldObject::GetAngle(const WorldObject* obj) const -{ - if(!obj) return 0; - return GetAngle( obj->GetPositionX(), obj->GetPositionY() ); -} - -// Return angle in range 0..2*pi -float WorldObject::GetAngle( const float x, const float y ) const -{ - float dx = x - GetPositionX(); - float dy = y - GetPositionY(); - - float ang = atan2(dy, dx); - ang = (ang >= 0) ? ang : 2 * M_PI + ang; - return ang; -} - -bool WorldObject::HasInArc(const float arcangle, const WorldObject* obj) const -{ - float arc = arcangle; - - // move arc to range 0.. 2*pi - while( arc >= 2.0f * M_PI ) - arc -= 2.0f * M_PI; - while( arc < 0 ) - arc += 2.0f * M_PI; - - float angle = GetAngle( obj ); - angle -= m_orientation; - - // move angle to range -pi ... +pi - while( angle > M_PI) - angle -= 2.0f * M_PI; - while(angle < -M_PI) - angle += 2.0f * M_PI; - - float lborder = -1 * (arc/2.0f); // in range -pi..0 - float rborder = (arc/2.0f); // in range 0..pi - return (( angle >= lborder ) && ( angle <= rborder )); -} - -void WorldObject::GetRandomPoint( float x, float y, float z, float distance, float &rand_x, float &rand_y, float &rand_z) const -{ - if(distance==0) - { - rand_x = x; - rand_y = y; - rand_z = z; - return; - } - - // angle to face `obj` to `this` - float angle = rand_norm()*2*M_PI; - float new_dist = rand_norm()*distance; - - rand_x = x + new_dist * cos(angle); - rand_y = y + new_dist * sin(angle); - rand_z = z; - - MaNGOS::NormalizeMapCoord(rand_x); - MaNGOS::NormalizeMapCoord(rand_y); - UpdateGroundPositionZ(rand_x,rand_y,rand_z); // update to LOS height if available -} - -void WorldObject::UpdateGroundPositionZ(float x, float y, float &z) const -{ - float new_z = MapManager::Instance().GetBaseMap(GetMapId())->GetHeight(x,y,z,true); - if(new_z > INVALID_HEIGHT) - z = new_z+ 0.05f; // just to be sure that we are not a few pixel under the surface -} - -bool WorldObject::IsPositionValid() const -{ - return MaNGOS::IsValidMapCoord(m_positionX,m_positionY,m_positionZ,m_orientation); -} - -void WorldObject::MonsterSay(const char* text, uint32 language, uint64 TargetGuid) -{ - WorldPacket data(SMSG_MESSAGECHAT, 200); - BuildMonsterChat(&data,CHAT_MSG_MONSTER_SAY,text,language,GetName(),TargetGuid); - SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),true); -} - -void WorldObject::MonsterYell(const char* text, uint32 language, uint64 TargetGuid) -{ - WorldPacket data(SMSG_MESSAGECHAT, 200); - BuildMonsterChat(&data,CHAT_MSG_MONSTER_YELL,text,language,GetName(),TargetGuid); - SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL),true); -} - -void WorldObject::MonsterTextEmote(const char* text, uint64 TargetGuid, bool IsBossEmote) -{ - WorldPacket data(SMSG_MESSAGECHAT, 200); - BuildMonsterChat(&data,IsBossEmote ? CHAT_MSG_RAID_BOSS_EMOTE : CHAT_MSG_MONSTER_EMOTE,text,LANG_UNIVERSAL,GetName(),TargetGuid); - SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true); -} - -void WorldObject::MonsterWhisper(const char* text, uint64 receiver, bool IsBossWhisper) -{ - Player *player = objmgr.GetPlayer(receiver); - if(!player || !player->GetSession()) - return; - - WorldPacket data(SMSG_MESSAGECHAT, 200); - BuildMonsterChat(&data,IsBossWhisper ? CHAT_MSG_RAID_BOSS_WHISPER : CHAT_MSG_MONSTER_WHISPER,text,LANG_UNIVERSAL,GetName(),receiver); - - player->GetSession()->SendPacket(&data); -} - -void WorldObject::SendPlaySound(uint32 Sound, bool OnlySelf) -{ - WorldPacket data(SMSG_PLAY_SOUND, 4); - data << Sound; - if (OnlySelf && GetTypeId() == TYPEID_PLAYER ) - ((Player*)this)->GetSession()->SendPacket( &data ); - else - SendMessageToSet( &data, true ); // ToSelf ignored in this case -} - -namespace MaNGOS -{ - class MessageChatLocaleCacheDo - { - public: - MessageChatLocaleCacheDo(WorldObject const& obj, ChatMsg msgtype, int32 textId, uint32 language, uint64 targetGUID, float dist) - : i_object(obj), i_msgtype(msgtype), i_textId(textId), i_language(language), - i_targetGUID(targetGUID), i_dist(dist) - { - } - - ~MessageChatLocaleCacheDo() - { - for(int i = 0; i < i_data_cache.size(); ++i) - delete i_data_cache[i]; - } - - void operator()(Player* p) - { - // skip far away players - if(p->GetDistance(&i_object) > i_dist) - return; - - uint32 loc_idx = p->GetSession()->GetSessionDbLocaleIndex(); - uint32 cache_idx = loc_idx+1; - WorldPacket* data; - - // create if not cached yet - if(i_data_cache.size() < cache_idx+1 || !i_data_cache[cache_idx]) - { - if(i_data_cache.size() < cache_idx+1) - i_data_cache.resize(cache_idx+1); - - char const* text = objmgr.GetMangosString(i_textId,loc_idx); - - data = new WorldPacket(SMSG_MESSAGECHAT, 200); - - // TODO: i_object.GetName() also must be localized? - i_object.BuildMonsterChat(data,i_msgtype,text,i_language,i_object.GetName(),i_targetGUID); - - i_data_cache[cache_idx] = data; - } - else - data = i_data_cache[cache_idx]; - - p->SendDirectMessage(data); - } - - private: - WorldObject const& i_object; - ChatMsg i_msgtype; - int32 i_textId; - uint32 i_language; - uint64 i_targetGUID; - float i_dist; - std::vector<WorldPacket*> i_data_cache; // 0 = default, i => i-1 locale index - }; -} // namespace MaNGOS - -void WorldObject::MonsterSay(int32 textId, uint32 language, uint64 TargetGuid) -{ - CellPair p = MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()); - - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - - MaNGOS::MessageChatLocaleCacheDo say_do(*this, CHAT_MSG_MONSTER_SAY, textId,language,TargetGuid,sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY)); - MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo> say_worker(say_do); - TypeContainerVisitor<MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo>, WorldTypeMapContainer > message(say_worker); - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, message, *GetMap()); -} - -void WorldObject::MonsterYell(int32 textId, uint32 language, uint64 TargetGuid) -{ - CellPair p = MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()); - - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - - MaNGOS::MessageChatLocaleCacheDo say_do(*this, CHAT_MSG_MONSTER_YELL, textId,language,TargetGuid,sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL)); - MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo> say_worker(say_do); - TypeContainerVisitor<MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo>, WorldTypeMapContainer > message(say_worker); - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, message, *GetMap()); -} - -void WorldObject::MonsterTextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote) -{ - CellPair p = MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()); - - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - - MaNGOS::MessageChatLocaleCacheDo say_do(*this, IsBossEmote ? CHAT_MSG_RAID_BOSS_EMOTE : CHAT_MSG_MONSTER_EMOTE, textId,LANG_UNIVERSAL,TargetGuid,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE)); - MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo> say_worker(say_do); - TypeContainerVisitor<MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo>, WorldTypeMapContainer > message(say_worker); - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, message, *GetMap()); -} - -void WorldObject::MonsterWhisper(int32 textId, uint64 receiver, bool IsBossWhisper) -{ - Player *player = objmgr.GetPlayer(receiver); - if(!player || !player->GetSession()) - return; - - uint32 loc_idx = player->GetSession()->GetSessionDbLocaleIndex(); - char const* text = objmgr.GetMangosString(textId,loc_idx); - - WorldPacket data(SMSG_MESSAGECHAT, 200); - BuildMonsterChat(&data,IsBossWhisper ? CHAT_MSG_RAID_BOSS_WHISPER : CHAT_MSG_MONSTER_WHISPER,text,LANG_UNIVERSAL,GetName(),receiver); - - player->GetSession()->SendPacket(&data); -} - -void WorldObject::BuildMonsterChat(WorldPacket *data, uint8 msgtype, char const* text, uint32 language, char const* name, uint64 targetGuid) const -{ - bool pre = (msgtype==CHAT_MSG_MONSTER_EMOTE || msgtype==CHAT_MSG_RAID_BOSS_EMOTE); - - *data << (uint8)msgtype; - *data << (uint32)language; - *data << (uint64)GetGUID(); - *data << (uint32)0; //2.1.0 - *data << (uint32)(strlen(name)+1); - *data << name; - *data << (uint64)targetGuid; //Unit Target - if( targetGuid && !IS_PLAYER_GUID(targetGuid) ) - { - *data << (uint32)1; // target name length - *data << (uint8)0; // target name - } - *data << (uint32)(strlen(text)+1+(pre?3:0)); - if(pre) - data->append("%s ",3); - *data << text; - *data << (uint8)0; // ChatTag -} - -void WorldObject::BuildHeartBeatMsg(WorldPacket *data) const -{ - //Heartbeat message cannot be used for non-units - if (!isType(TYPEMASK_UNIT)) - return; - - data->Initialize(MSG_MOVE_HEARTBEAT, 32); - data->append(GetPackGUID()); - *data << uint32(((Unit*)this)->GetUnitMovementFlags()); // movement flags - *data << uint8(0); // 2.3.0 - *data << getMSTime(); // time - *data << m_positionX; - *data << m_positionY; - *data << m_positionZ; - *data << m_orientation; - *data << uint32(0); -} - -void WorldObject::BuildTeleportAckMsg(WorldPacket *data, float x, float y, float z, float ang) const -{ - //TeleportAck message cannot be used for non-units - if (!isType(TYPEMASK_UNIT)) - return; - - data->Initialize(MSG_MOVE_TELEPORT_ACK, 41); - data->append(GetPackGUID()); - *data << uint32(0); // this value increments every time - *data << uint32(((Unit*)this)->GetUnitMovementFlags()); // movement flags - *data << uint8(0); // 2.3.0 - *data << getMSTime(); // time - *data << x; - *data << y; - *data << z; - *data << ang; - *data << uint32(0); -} - -void WorldObject::SendMessageToSet(WorldPacket *data, bool /*bToSelf*/) -{ - MapManager::Instance().GetMap(m_mapId, this)->MessageBroadcast(this, data); -} - -void WorldObject::SendMessageToSetInRange(WorldPacket *data, float dist, bool /*bToSelf*/) -{ - MapManager::Instance().GetMap(m_mapId, this)->MessageDistBroadcast(this, data, dist); -} - -void WorldObject::SendObjectDeSpawnAnim(uint64 guid) -{ - WorldPacket data(SMSG_GAMEOBJECT_DESPAWN_ANIM, 8); - data << guid; - SendMessageToSet(&data, true); -} - -Map* WorldObject::GetMap() const -{ - return MapManager::Instance().GetMap(GetMapId(), this); -} - -Map const* WorldObject::GetBaseMap() const -{ - return MapManager::Instance().GetBaseMap(GetMapId()); -} - -void WorldObject::AddObjectToRemoveList() -{ - Map* map = GetMap(); - if(!map) - { - sLog.outError("Object (TypeId: %u Entry: %u GUID: %u) at attempt add to move list not have valid map (Id: %u).",GetTypeId(),GetEntry(),GetGUIDLow(),GetMapId()); - return; - } - - map->AddObjectToRemoveList(this); -} - -Creature* WorldObject::SummonCreature(uint32 id, float x, float y, float z, float ang,TempSummonType spwtype,uint32 despwtime) -{ - TemporarySummon* pCreature = new TemporarySummon(GetGUID()); - - pCreature->SetInstanceId(GetInstanceId()); - uint32 team = 0; - if (GetTypeId()==TYPEID_PLAYER) - team = ((Player*)this)->GetTeam(); - - if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), GetMap(), id, team)) - { - delete pCreature; - return NULL; - } - - if (x == 0.0f && y == 0.0f && z == 0.0f) - GetClosePoint(x, y, z, pCreature->GetObjectSize()); - - pCreature->Relocate(x, y, z, ang); - - if(!pCreature->IsPositionValid()) - { - sLog.outError("ERROR: Creature (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY()); - delete pCreature; - return NULL; - } - - pCreature->Summon(spwtype, despwtime); - - if(GetTypeId()==TYPEID_UNIT && ((Creature*)this)->AI()) - ((Creature*)this)->AI()->JustSummoned(pCreature); - - //return the creature therewith the summoner has access to it - return pCreature; -} - -void WorldObject::GetNearPoint2D(float &x, float &y, float distance2d, float absAngle ) const -{ - x = GetPositionX() + (GetObjectSize() + distance2d) * cos(absAngle); - y = GetPositionY() + (GetObjectSize() + distance2d) * sin(absAngle); - - MaNGOS::NormalizeMapCoord(x); - MaNGOS::NormalizeMapCoord(y); -} - -void WorldObject::GetNearPoint(WorldObject const* searcher, float &x, float &y, float &z, float searcher_size, float distance2d, float absAngle ) const -{ - GetNearPoint2D(x,y,distance2d+searcher_size,absAngle); - - z = GetPositionZ(); - - UpdateGroundPositionZ(x,y,z); -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "SharedDefines.h"
+#include "WorldPacket.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "World.h"
+#include "Object.h"
+#include "Creature.h"
+#include "Player.h"
+#include "ObjectMgr.h"
+#include "WorldSession.h"
+#include "UpdateData.h"
+#include "UpdateMask.h"
+#include "Util.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "Log.h"
+#include "Transports.h"
+#include "TargetedMovementGenerator.h"
+#include "WaypointMovementGenerator.h"
+#include "VMapFactory.h"
+#include "CellImpl.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+
+#include "TemporarySummon.h"
+
+uint32 GuidHigh2TypeId(uint32 guid_hi)
+{
+ switch(guid_hi)
+ {
+ case HIGHGUID_ITEM: return TYPEID_ITEM;
+ //case HIGHGUID_CONTAINER: return TYPEID_CONTAINER; HIGHGUID_CONTAINER==HIGHGUID_ITEM currently
+ case HIGHGUID_UNIT: return TYPEID_UNIT;
+ case HIGHGUID_PET: return TYPEID_UNIT;
+ case HIGHGUID_PLAYER: return TYPEID_PLAYER;
+ case HIGHGUID_GAMEOBJECT: return TYPEID_GAMEOBJECT;
+ case HIGHGUID_DYNAMICOBJECT:return TYPEID_DYNAMICOBJECT;
+ case HIGHGUID_CORPSE: return TYPEID_CORPSE;
+ case HIGHGUID_MO_TRANSPORT: return TYPEID_GAMEOBJECT;
+ }
+ return 10; // unknown
+}
+
+Object::Object( )
+{
+ m_objectTypeId = TYPEID_OBJECT;
+ m_objectType = TYPEMASK_OBJECT;
+
+ m_uint32Values = 0;
+ m_uint32Values_mirror = 0;
+ m_valuesCount = 0;
+
+ m_inWorld = false;
+ m_objectUpdated = false;
+
+ m_PackGUID.clear();
+ m_PackGUID.appendPackGUID(0);
+}
+
+Object::~Object( )
+{
+ if(m_objectUpdated)
+ ObjectAccessor::Instance().RemoveUpdateObject(this);
+
+ if(m_uint32Values)
+ {
+ if(IsInWorld())
+ {
+ ///- Do NOT call RemoveFromWorld here, if the object is a player it will crash
+ sLog.outError("Object::~Object - guid="I64FMTD", typeid=%d deleted but still in world!!", GetGUID(), GetTypeId());
+ //assert(0);
+ }
+
+ //DEBUG_LOG("Object desctr 1 check (%p)",(void*)this);
+ delete [] m_uint32Values;
+ delete [] m_uint32Values_mirror;
+ //DEBUG_LOG("Object desctr 2 check (%p)",(void*)this);
+ }
+}
+
+void Object::_InitValues()
+{
+ m_uint32Values = new uint32[ m_valuesCount ];
+ memset(m_uint32Values, 0, m_valuesCount*sizeof(uint32));
+
+ m_uint32Values_mirror = new uint32[ m_valuesCount ];
+ memset(m_uint32Values_mirror, 0, m_valuesCount*sizeof(uint32));
+
+ m_objectUpdated = false;
+}
+
+void Object::_Create( uint32 guidlow, uint32 entry, HighGuid guidhigh )
+{
+ if(!m_uint32Values) _InitValues();
+
+ uint64 guid = MAKE_NEW_GUID(guidlow, entry, guidhigh); // required more changes to make it working
+ SetUInt64Value( OBJECT_FIELD_GUID, guid );
+ SetUInt32Value( OBJECT_FIELD_TYPE, m_objectType );
+ m_PackGUID.clear();
+ m_PackGUID.appendPackGUID(GetGUID());
+}
+
+void Object::BuildMovementUpdateBlock(UpdateData * data, uint32 flags ) const
+{
+ ByteBuffer buf(500);
+
+ buf << uint8( UPDATETYPE_MOVEMENT );
+ buf << GetGUID();
+
+ _BuildMovementUpdate(&buf, flags, 0x00000000);
+
+ data->AddUpdateBlock(buf);
+}
+
+void Object::BuildCreateUpdateBlockForPlayer(UpdateData *data, Player *target) const
+{
+ if(!target)
+ {
+ return;
+ }
+
+ uint8 updatetype = UPDATETYPE_CREATE_OBJECT;
+ uint8 flags = m_updateFlag;
+ uint32 flags2 = 0;
+
+ /** lower flag1 **/
+ if(target == this) // building packet for oneself
+ {
+ flags |= UPDATEFLAG_SELF;
+
+ /*** temporary reverted - until real source of stack corruption will not found
+ updatetype = UPDATETYPE_CREATE_OBJECT2;
+ ****/
+ }
+
+ if(flags & UPDATEFLAG_HASPOSITION)
+ {
+ // UPDATETYPE_CREATE_OBJECT2 dynamic objects, corpses...
+ if(isType(TYPEMASK_DYNAMICOBJECT) || isType(TYPEMASK_CORPSE) || isType(TYPEMASK_PLAYER))
+ updatetype = UPDATETYPE_CREATE_OBJECT2;
+
+ // UPDATETYPE_CREATE_OBJECT2 for pets...
+ if(target->GetPetGUID() == GetGUID())
+ updatetype = UPDATETYPE_CREATE_OBJECT2;
+
+ // UPDATETYPE_CREATE_OBJECT2 for some gameobject types...
+ if(isType(TYPEMASK_GAMEOBJECT))
+ {
+ switch(((GameObject*)this)->GetGoType())
+ {
+ case GAMEOBJECT_TYPE_TRAP:
+ case GAMEOBJECT_TYPE_DUEL_ARBITER:
+ case GAMEOBJECT_TYPE_FLAGSTAND:
+ case GAMEOBJECT_TYPE_FLAGDROP:
+ updatetype = UPDATETYPE_CREATE_OBJECT2;
+ break;
+ case GAMEOBJECT_TYPE_TRANSPORT:
+ flags |= UPDATEFLAG_TRANSPORT;
+ break;
+ }
+ }
+ }
+
+ //sLog.outDebug("BuildCreateUpdate: update-type: %u, object-type: %u got flags: %X, flags2: %X", updatetype, m_objectTypeId, flags, flags2);
+
+ ByteBuffer buf(500);
+ buf << (uint8)updatetype;
+ //buf.append(GetPackGUID()); //client crashes when using this
+ buf << (uint8)0xFF << GetGUID();
+ buf << (uint8)m_objectTypeId;
+
+ _BuildMovementUpdate(&buf, flags, flags2);
+
+ UpdateMask updateMask;
+ updateMask.SetCount( m_valuesCount );
+ _SetCreateBits( &updateMask, target );
+ _BuildValuesUpdate(updatetype, &buf, &updateMask, target );
+ data->AddUpdateBlock(buf);
+}
+
+void Object::BuildUpdate(UpdateDataMapType &update_players)
+{
+ ObjectAccessor::_buildUpdateObject(this,update_players);
+ ClearUpdateMask(true);
+}
+
+void Object::SendUpdateToPlayer(Player* player)
+{
+ // send update to another players
+ SendUpdateObjectToAllExcept(player);
+
+ // send create update to player
+ UpdateData upd;
+ WorldPacket packet;
+
+ upd.Clear();
+ BuildCreateUpdateBlockForPlayer(&upd, player);
+ upd.BuildPacket(&packet);
+ player->GetSession()->SendPacket(&packet);
+
+ // now object updated/(create updated)
+}
+
+void Object::BuildValuesUpdateBlockForPlayer(UpdateData *data, Player *target) const
+{
+ ByteBuffer buf(500);
+
+ buf << (uint8) UPDATETYPE_VALUES;
+ //buf.append(GetPackGUID()); //client crashes when using this. but not have crash in debug mode
+ buf << (uint8)0xFF;
+ buf << GetGUID();
+
+ UpdateMask updateMask;
+ updateMask.SetCount( m_valuesCount );
+
+ _SetUpdateBits( &updateMask, target );
+ _BuildValuesUpdate(UPDATETYPE_VALUES, &buf, &updateMask, target );
+
+ data->AddUpdateBlock(buf);
+}
+
+void Object::BuildOutOfRangeUpdateBlock(UpdateData * data) const
+{
+ data->AddOutOfRangeGUID(GetGUID());
+}
+
+void Object::DestroyForPlayer(Player *target) const
+{
+ ASSERT(target);
+
+ WorldPacket data(SMSG_DESTROY_OBJECT, 8);
+ data << GetGUID();
+ target->GetSession()->SendPacket( &data );
+}
+
+void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 ) const
+{
+ *data << (uint8)flags; // update flags
+
+ // 0x20
+ if (flags & UPDATEFLAG_LIVING)
+ {
+ switch(GetTypeId())
+ {
+ case TYPEID_UNIT:
+ {
+ flags2 = ((Unit*)this)->GetUnitMovementFlags();
+ }
+ break;
+ case TYPEID_PLAYER:
+ {
+ flags2 = ((Player*)this)->GetUnitMovementFlags();
+
+ if(((Player*)this)->GetTransport())
+ flags2 |= MOVEMENTFLAG_ONTRANSPORT;
+ else
+ flags2 &= ~MOVEMENTFLAG_ONTRANSPORT;
+
+ // remove unknown, unused etc flags for now
+ flags2 &= ~MOVEMENTFLAG_SPLINE2; // will be set manually
+
+ if(((Player*)this)->isInFlight())
+ {
+ WPAssert(((Player*)this)->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE);
+ flags2 = (MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_SPLINE2);
+ }
+ }
+ break;
+ }
+
+ *data << uint32(flags2); // movement flags
+ *data << uint8(0); // unk 2.3.0
+ *data << uint32(getMSTime()); // time (in milliseconds)
+ }
+
+ // 0x40
+ if (flags & UPDATEFLAG_HASPOSITION)
+ {
+ // 0x02
+ if(flags & UPDATEFLAG_TRANSPORT && ((GameObject*)this)->GetGoType() == GAMEOBJECT_TYPE_MO_TRANSPORT)
+ {
+ *data << (float)0;
+ *data << (float)0;
+ *data << (float)0;
+ *data << ((WorldObject *)this)->GetOrientation();
+ }
+ else
+ {
+ *data << ((WorldObject *)this)->GetPositionX();
+ *data << ((WorldObject *)this)->GetPositionY();
+ *data << ((WorldObject *)this)->GetPositionZ();
+ *data << ((WorldObject *)this)->GetOrientation();
+ }
+ }
+
+ // 0x20
+ if(flags & UPDATEFLAG_LIVING)
+ {
+ // 0x00000200
+ if(flags2 & MOVEMENTFLAG_ONTRANSPORT)
+ {
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ *data << (uint64)((Player*)this)->GetTransport()->GetGUID();
+ *data << (float)((Player*)this)->GetTransOffsetX();
+ *data << (float)((Player*)this)->GetTransOffsetY();
+ *data << (float)((Player*)this)->GetTransOffsetZ();
+ *data << (float)((Player*)this)->GetTransOffsetO();
+ *data << (uint32)((Player*)this)->GetTransTime();
+ }
+ //MaNGOS currently not have support for other than player on transport
+ }
+
+ // 0x02200000
+ if(flags2 & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2))
+ {
+ if(GetTypeId() == TYPEID_PLAYER)
+ *data << (float)((Player*)this)->m_movementInfo.s_pitch;
+ else
+ *data << (float)0; // is't part of movement packet, we must store and send it...
+ }
+
+ if(GetTypeId() == TYPEID_PLAYER)
+ *data << (uint32)((Player*)this)->m_movementInfo.fallTime;
+ else
+ *data << (uint32)0; // last fall time
+
+ // 0x00001000
+ if(flags2 & MOVEMENTFLAG_JUMPING)
+ {
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ *data << (float)((Player*)this)->m_movementInfo.j_unk;
+ *data << (float)((Player*)this)->m_movementInfo.j_sinAngle;
+ *data << (float)((Player*)this)->m_movementInfo.j_cosAngle;
+ *data << (float)((Player*)this)->m_movementInfo.j_xyspeed;
+ }
+ else
+ {
+ *data << (float)0;
+ *data << (float)0;
+ *data << (float)0;
+ *data << (float)0;
+ }
+ }
+
+ // 0x04000000
+ if(flags2 & MOVEMENTFLAG_SPLINE)
+ {
+ if(GetTypeId() == TYPEID_PLAYER)
+ *data << (float)((Player*)this)->m_movementInfo.u_unk1;
+ else
+ *data << (float)0;
+ }
+
+ *data << ((Unit*)this)->GetSpeed( MOVE_WALK );
+ *data << ((Unit*)this)->GetSpeed( MOVE_RUN );
+ *data << ((Unit*)this)->GetSpeed( MOVE_SWIMBACK );
+ *data << ((Unit*)this)->GetSpeed( MOVE_SWIM );
+ *data << ((Unit*)this)->GetSpeed( MOVE_WALKBACK );
+ *data << ((Unit*)this)->GetSpeed( MOVE_FLY );
+ *data << ((Unit*)this)->GetSpeed( MOVE_FLYBACK );
+ *data << ((Unit*)this)->GetSpeed( MOVE_TURN );
+
+ // 0x08000000
+ if(flags2 & MOVEMENTFLAG_SPLINE2)
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ {
+ sLog.outDebug("_BuildMovementUpdate: MOVEMENTFLAG_SPLINE2 for non-player");
+ return;
+ }
+
+ if(!((Player*)this)->isInFlight())
+ {
+ sLog.outDebug("_BuildMovementUpdate: MOVEMENTFLAG_SPLINE2 but not in flight");
+ return;
+ }
+
+ WPAssert(((Player*)this)->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE);
+
+ FlightPathMovementGenerator *fmg = (FlightPathMovementGenerator*)(((Player*)this)->GetMotionMaster()->top());
+
+ uint32 flags3 = 0x00000300;
+
+ *data << uint32(flags3); // splines flag?
+
+ if(flags3 & 0x10000) // probably x,y,z coords there
+ {
+ *data << (float)0;
+ *data << (float)0;
+ *data << (float)0;
+ }
+
+ if(flags3 & 0x20000) // probably guid there
+ {
+ *data << uint64(0);
+ }
+
+ if(flags3 & 0x40000) // may be orientation
+ {
+ *data << (float)0;
+ }
+
+ Path &path = fmg->GetPath();
+
+ float x, y, z;
+ ((Player*)this)->GetPosition(x, y, z);
+
+ uint32 inflighttime = uint32(path.GetPassedLength(fmg->GetCurrentNode(), x, y, z) * 32);
+ uint32 traveltime = uint32(path.GetTotalLength() * 32);
+
+ *data << uint32(inflighttime); // passed move time?
+ *data << uint32(traveltime); // full move time?
+ *data << uint32(0); // ticks count?
+
+ uint32 poscount = uint32(path.Size());
+
+ *data << uint32(poscount); // points count
+
+ for(uint32 i = 0; i < poscount; ++i)
+ {
+ *data << path.GetNodes()[i].x;
+ *data << path.GetNodes()[i].y;
+ *data << path.GetNodes()[i].z;
+ }
+
+ /*for(uint32 i = 0; i < poscount; i++)
+ {
+ // path points
+ *data << (float)0;
+ *data << (float)0;
+ *data << (float)0;
+ }*/
+
+ *data << path.GetNodes()[poscount-1].x;
+ *data << path.GetNodes()[poscount-1].y;
+ *data << path.GetNodes()[poscount-1].z;
+
+ // target position (path end)
+ /**data << ((Unit*)this)->GetPositionX();
+ *data << ((Unit*)this)->GetPositionY();
+ *data << ((Unit*)this)->GetPositionZ();*/
+ }
+ }
+
+ // 0x8
+ if(flags & UPDATEFLAG_LOWGUID)
+ {
+ switch(GetTypeId())
+ {
+ case TYPEID_OBJECT:
+ case TYPEID_ITEM:
+ case TYPEID_CONTAINER:
+ case TYPEID_GAMEOBJECT:
+ case TYPEID_DYNAMICOBJECT:
+ case TYPEID_CORPSE:
+ *data << uint32(GetGUIDLow()); // GetGUIDLow()
+ break;
+ case TYPEID_UNIT:
+ *data << uint32(0x0000000B); // unk, can be 0xB or 0xC
+ break;
+ case TYPEID_PLAYER:
+ if(flags & UPDATEFLAG_SELF)
+ *data << uint32(0x00000015); // unk, can be 0x15 or 0x22
+ else
+ *data << uint32(0x00000008); // unk, can be 0x7 or 0x8
+ break;
+ default:
+ *data << uint32(0x00000000); // unk
+ break;
+ }
+ }
+
+ // 0x10
+ if(flags & UPDATEFLAG_HIGHGUID)
+ {
+ switch(GetTypeId())
+ {
+ case TYPEID_OBJECT:
+ case TYPEID_ITEM:
+ case TYPEID_CONTAINER:
+ case TYPEID_GAMEOBJECT:
+ case TYPEID_DYNAMICOBJECT:
+ case TYPEID_CORPSE:
+ *data << uint32(GetGUIDHigh()); // GetGUIDHigh()
+ break;
+ default:
+ *data << uint32(0x00000000); // unk
+ break;
+ }
+ }
+
+ // 0x4
+ if(flags & UPDATEFLAG_FULLGUID)
+ {
+ *data << uint8(0); // packed guid (probably target guid)
+ }
+
+ // 0x2
+ if(flags & UPDATEFLAG_TRANSPORT)
+ {
+ *data << uint32(getMSTime()); // ms time
+ }
+}
+
+void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask *updateMask, Player *target) const
+{
+ if(!target)
+ return;
+
+ bool IsActivateToQuest = false;
+ if (updatetype == UPDATETYPE_CREATE_OBJECT || updatetype == UPDATETYPE_CREATE_OBJECT2)
+ {
+ if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsTransport())
+ {
+ if ( ((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster())
+ {
+ IsActivateToQuest = true;
+ updateMask->SetBit(GAMEOBJECT_DYN_FLAGS);
+ }
+ }
+ }
+ else //case UPDATETYPE_VALUES
+ {
+ if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsTransport())
+ {
+ if ( ((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster())
+ {
+ IsActivateToQuest = true;
+ }
+ updateMask->SetBit(GAMEOBJECT_DYN_FLAGS);
+ updateMask->SetBit(GAMEOBJECT_ANIMPROGRESS);
+ }
+ }
+
+ WPAssert(updateMask && updateMask->GetCount() == m_valuesCount);
+
+ *data << (uint8)updateMask->GetBlockCount();
+ data->append( updateMask->GetMask(), updateMask->GetLength() );
+
+ // 2 specialized loops for speed optimization in non-unit case
+ if(isType(TYPEMASK_UNIT)) // unit (creature/player) case
+ {
+ for( uint16 index = 0; index < m_valuesCount; index ++ )
+ {
+ if( updateMask->GetBit( index ) )
+ {
+ // remove custom flag before send
+ if( index == UNIT_NPC_FLAGS )
+ *data << uint32(m_uint32Values[ index ] & ~UNIT_NPC_FLAG_GUARD);
+ // FIXME: Some values at server stored in float format but must be sent to client in uint32 format
+ else if(index >= UNIT_FIELD_BASEATTACKTIME && index <= UNIT_FIELD_RANGEDATTACKTIME)
+ {
+ // convert from float to uint32 and send
+ *data << uint32(m_floatValues[ index ] < 0 ? 0 : m_floatValues[ index ]);
+ }
+ // there are some float values which may be negative or can't get negative due to other checks
+ else if(index >= UNIT_FIELD_NEGSTAT0 && index <= UNIT_FIELD_NEGSTAT4 ||
+ index >= UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE && index <= (UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE + 6) ||
+ index >= UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE && index <= (UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE + 6) ||
+ index >= UNIT_FIELD_POSSTAT0 && index <= UNIT_FIELD_POSSTAT4)
+ {
+ *data << uint32(m_floatValues[ index ]);
+ }
+ // Gamemasters should be always able to select units - remove not selectable flag
+ else if(index == UNIT_FIELD_FLAGS && target->isGameMaster())
+ {
+ *data << (m_uint32Values[ index ] & ~UNIT_FLAG_NOT_SELECTABLE);
+ }
+ // hide lootable animation for unallowed players
+ else if(index == UNIT_DYNAMIC_FLAGS && GetTypeId() == TYPEID_UNIT)
+ {
+ if(!target->isAllowedToLoot((Creature*)this))
+ *data << (m_uint32Values[ index ] & ~UNIT_DYNFLAG_LOOTABLE);
+ else
+ *data << (m_uint32Values[ index ] & ~UNIT_DYNFLAG_OTHER_TAGGER);
+ }
+ else
+ {
+ // send in current format (float as float, uint32 as uint32)
+ *data << m_uint32Values[ index ];
+ }
+ }
+ }
+ }
+ else if(isType(TYPEMASK_GAMEOBJECT)) // gameobject case
+ {
+ for( uint16 index = 0; index < m_valuesCount; index ++ )
+ {
+ if( updateMask->GetBit( index ) )
+ {
+ // send in current format (float as float, uint32 as uint32)
+ if ( index == GAMEOBJECT_DYN_FLAGS )
+ {
+ if(IsActivateToQuest )
+ {
+ switch(((GameObject*)this)->GetGoType())
+ {
+ case GAMEOBJECT_TYPE_CHEST:
+ *data << uint32(9); // enable quest object. Represent 9, but 1 for client before 2.3.0
+ break;
+ case GAMEOBJECT_TYPE_GOOBER:
+ *data << uint32(1);
+ break;
+ default:
+ *data << uint32(0); //unknown. not happen.
+ break;
+ }
+ }
+ else
+ *data << uint32(0); // disable quest object
+ }
+ else
+ *data << m_uint32Values[ index ]; // other cases
+ }
+ }
+ }
+ else // other objects case (no special index checks)
+ {
+ for( uint16 index = 0; index < m_valuesCount; index ++ )
+ {
+ if( updateMask->GetBit( index ) )
+ {
+ // send in current format (float as float, uint32 as uint32)
+ *data << m_uint32Values[ index ];
+ }
+ }
+ }
+}
+
+void Object::ClearUpdateMask(bool remove)
+{
+ for( uint16 index = 0; index < m_valuesCount; index ++ )
+ {
+ if(m_uint32Values_mirror[index]!= m_uint32Values[index])
+ m_uint32Values_mirror[index] = m_uint32Values[index];
+ }
+ if(m_objectUpdated)
+ {
+ if(remove)
+ ObjectAccessor::Instance().RemoveUpdateObject(this);
+ m_objectUpdated = false;
+ }
+}
+
+// Send current value fields changes to all viewers
+void Object::SendUpdateObjectToAllExcept(Player* exceptPlayer)
+{
+ // changes will be send in create packet
+ if(!IsInWorld())
+ return;
+
+ // nothing do
+ if(!m_objectUpdated)
+ return;
+
+ ObjectAccessor::UpdateObject(this,exceptPlayer);
+}
+
+bool Object::LoadValues(const char* data)
+{
+ if(!m_uint32Values) _InitValues();
+
+ Tokens tokens = StrSplit(data, " ");
+
+ if(tokens.size() != m_valuesCount)
+ return false;
+
+ Tokens::iterator iter;
+ int index;
+ for (iter = tokens.begin(), index = 0; index < m_valuesCount; ++iter, ++index)
+ {
+ m_uint32Values[index] = atol((*iter).c_str());
+ }
+
+ return true;
+}
+
+void Object::_SetUpdateBits(UpdateMask *updateMask, Player* /*target*/) const
+{
+ for( uint16 index = 0; index < m_valuesCount; index ++ )
+ {
+ if(m_uint32Values_mirror[index]!= m_uint32Values[index])
+ updateMask->SetBit(index);
+ }
+}
+
+void Object::_SetCreateBits(UpdateMask *updateMask, Player* /*target*/) const
+{
+ for( uint16 index = 0; index < m_valuesCount; index++ )
+ {
+ if(GetUInt32Value(index) != 0)
+ updateMask->SetBit(index);
+ }
+}
+
+void Object::SetInt32Value( uint16 index, int32 value )
+{
+ ASSERT( index < m_valuesCount || PrintIndexError( index , true ) );
+
+ if(m_int32Values[ index ] != value)
+ {
+ m_int32Values[ index ] = value;
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+void Object::SetUInt32Value( uint16 index, uint32 value )
+{
+ ASSERT( index < m_valuesCount || PrintIndexError( index , true ) );
+
+ if(m_uint32Values[ index ] != value)
+ {
+ m_uint32Values[ index ] = value;
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+void Object::SetUInt64Value( uint16 index, const uint64 &value )
+{
+ ASSERT( index + 1 < m_valuesCount || PrintIndexError( index , true ) );
+ if(*((uint64*)&(m_uint32Values[ index ])) != value)
+ {
+ m_uint32Values[ index ] = *((uint32*)&value);
+ m_uint32Values[ index + 1 ] = *(((uint32*)&value) + 1);
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+void Object::SetFloatValue( uint16 index, float value )
+{
+ ASSERT( index < m_valuesCount || PrintIndexError( index , true ) );
+
+ if(m_floatValues[ index ] != value)
+ {
+ m_floatValues[ index ] = value;
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+void Object::SetByteValue( uint16 index, uint8 offset, uint8 value )
+{
+ ASSERT( index < m_valuesCount || PrintIndexError( index , true ) );
+
+ if(offset > 4)
+ {
+ sLog.outError("Object::SetByteValue: wrong offset %u", offset);
+ return;
+ }
+
+ if(uint8(m_uint32Values[ index ] >> (offset * 8)) != value)
+ {
+ m_uint32Values[ index ] &= ~uint32(uint32(0xFF) << (offset * 8));
+ m_uint32Values[ index ] |= uint32(uint32(value) << (offset * 8));
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+void Object::SetUInt16Value( uint16 index, uint8 offset, uint16 value )
+{
+ ASSERT( index < m_valuesCount || PrintIndexError( index , true ) );
+
+ if(offset > 2)
+ {
+ sLog.outError("Object::SetUInt16Value: wrong offset %u", offset);
+ return;
+ }
+
+ if(uint8(m_uint32Values[ index ] >> (offset * 16)) != value)
+ {
+ m_uint32Values[ index ] &= ~uint32(uint32(0xFFFF) << (offset * 16));
+ m_uint32Values[ index ] |= uint32(uint32(value) << (offset * 16));
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+void Object::SetStatFloatValue( uint16 index, float value)
+{
+ if(value < 0)
+ value = 0.0f;
+
+ SetFloatValue(index, value);
+}
+
+void Object::SetStatInt32Value( uint16 index, int32 value)
+{
+ if(value < 0)
+ value = 0;
+
+ SetUInt32Value(index, uint32(value));
+}
+
+void Object::ApplyModUInt32Value(uint16 index, int32 val, bool apply)
+{
+ int32 cur = GetUInt32Value(index);
+ cur += (apply ? val : -val);
+ if(cur < 0)
+ cur = 0;
+ SetUInt32Value(index,cur);
+}
+
+void Object::ApplyModInt32Value(uint16 index, int32 val, bool apply)
+{
+ int32 cur = GetInt32Value(index);
+ cur += (apply ? val : -val);
+ SetInt32Value(index,cur);
+}
+
+void Object::ApplyModSignedFloatValue(uint16 index, float val, bool apply)
+{
+ float cur = GetFloatValue(index);
+ cur += (apply ? val : -val);
+ SetFloatValue(index,cur);
+}
+
+void Object::ApplyModPositiveFloatValue(uint16 index, float val, bool apply)
+{
+ float cur = GetFloatValue(index);
+ cur += (apply ? val : -val);
+ if(cur < 0)
+ cur = 0;
+ SetFloatValue(index,cur);
+}
+
+void Object::SetFlag( uint16 index, uint32 newFlag )
+{
+ ASSERT( index < m_valuesCount || PrintIndexError( index , true ) );
+ uint32 oldval = m_uint32Values[ index ];
+ uint32 newval = oldval | newFlag;
+
+ if(oldval != newval)
+ {
+ m_uint32Values[ index ] = newval;
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+void Object::RemoveFlag( uint16 index, uint32 oldFlag )
+{
+ ASSERT( index < m_valuesCount || PrintIndexError( index , true ) );
+ uint32 oldval = m_uint32Values[ index ];
+ uint32 newval = oldval & ~oldFlag;
+
+ if(oldval != newval)
+ {
+ m_uint32Values[ index ] = newval;
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+bool Object::PrintIndexError(uint32 index, bool set) const
+{
+ sLog.outError("ERROR: Attempt %s non-existed value field: %u (count: %u) for object typeid: %u type mask: %u",(set ? "set value to" : "get value from"),index,m_valuesCount,GetTypeId(),m_objectType);
+
+ // assert must fail after function call
+ return false;
+}
+
+WorldObject::WorldObject()
+{
+ m_positionX = 0.0f;
+ m_positionY = 0.0f;
+ m_positionZ = 0.0f;
+ m_orientation = 0.0f;
+
+ m_mapId = 0;
+ m_InstanceId = 0;
+
+ m_name = "";
+
+ mSemaphoreTeleport = false;
+}
+
+void WorldObject::_Create( uint32 guidlow, HighGuid guidhigh, uint32 mapid )
+{
+ Object::_Create(guidlow, 0, guidhigh);
+
+ m_mapId = mapid;
+}
+
+uint32 WorldObject::GetZoneId() const
+{
+ return MapManager::Instance().GetBaseMap(m_mapId)->GetZoneId(m_positionX,m_positionY);
+}
+
+uint32 WorldObject::GetAreaId() const
+{
+ return MapManager::Instance().GetBaseMap(m_mapId)->GetAreaId(m_positionX,m_positionY);
+}
+
+InstanceData* WorldObject::GetInstanceData()
+{
+ Map *map = MapManager::Instance().GetMap(m_mapId, this);
+ return map->IsDungeon() ? ((InstanceMap*)map)->GetInstanceData() : NULL;
+}
+
+ //slow
+float WorldObject::GetDistance(const WorldObject* obj) const
+{
+ float dx = GetPositionX() - obj->GetPositionX();
+ float dy = GetPositionY() - obj->GetPositionY();
+ float dz = GetPositionZ() - obj->GetPositionZ();
+ float sizefactor = GetObjectSize() + obj->GetObjectSize();
+ float dist = sqrt((dx*dx) + (dy*dy) + (dz*dz)) - sizefactor;
+ return ( dist > 0 ? dist : 0);
+}
+
+float WorldObject::GetDistance2d(float x, float y) const
+{
+ float dx = GetPositionX() - x;
+ float dy = GetPositionY() - y;
+ float sizefactor = GetObjectSize();
+ float dist = sqrt((dx*dx) + (dy*dy)) - sizefactor;
+ return ( dist > 0 ? dist : 0);
+}
+
+float WorldObject::GetDistance(const float x, const float y, const float z) const
+{
+ float dx = GetPositionX() - x;
+ float dy = GetPositionY() - y;
+ float dz = GetPositionZ() - z;
+ float sizefactor = GetObjectSize();
+ float dist = sqrt((dx*dx) + (dy*dy) + (dz*dz)) - sizefactor;
+ return ( dist > 0 ? dist : 0);
+}
+
+float WorldObject::GetDistance2d(const WorldObject* obj) const
+{
+ float dx = GetPositionX() - obj->GetPositionX();
+ float dy = GetPositionY() - obj->GetPositionY();
+ float sizefactor = GetObjectSize() + obj->GetObjectSize();
+ float dist = sqrt((dx*dx) + (dy*dy)) - sizefactor;
+ return ( dist > 0 ? dist : 0);
+}
+
+float WorldObject::GetDistanceZ(const WorldObject* obj) const
+{
+ float dz = fabs(GetPositionZ() - obj->GetPositionZ());
+ float sizefactor = GetObjectSize() + obj->GetObjectSize();
+ float dist = dz - sizefactor;
+ return ( dist > 0 ? dist : 0);
+}
+
+bool WorldObject::IsWithinDistInMap(const WorldObject* obj, const float dist2compare) const
+{
+ if (!obj || !IsInMap(obj)) return false;
+
+ float dx = GetPositionX() - obj->GetPositionX();
+ float dy = GetPositionY() - obj->GetPositionY();
+ float dz = GetPositionZ() - obj->GetPositionZ();
+ float distsq = dx*dx + dy*dy + dz*dz;
+ float sizefactor = GetObjectSize() + obj->GetObjectSize();
+ float maxdist = dist2compare + sizefactor;
+
+ return distsq < maxdist * maxdist;
+}
+
+bool WorldObject::IsWithinLOSInMap(const WorldObject* obj) const
+{
+ if (!IsInMap(obj)) return false;
+ float ox,oy,oz;
+ obj->GetPosition(ox,oy,oz);
+ return(IsWithinLOS(ox, oy, oz ));
+}
+
+bool WorldObject::IsWithinLOS(const float ox, const float oy, const float oz ) const
+{
+ float x,y,z;
+ GetPosition(x,y,z);
+ VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager();
+ return vMapManager->isInLineOfSight(GetMapId(), x, y, z+2.0f, ox, oy, oz+2.0f);
+}
+
+float WorldObject::GetAngle(const WorldObject* obj) const
+{
+ if(!obj) return 0;
+ return GetAngle( obj->GetPositionX(), obj->GetPositionY() );
+}
+
+// Return angle in range 0..2*pi
+float WorldObject::GetAngle( const float x, const float y ) const
+{
+ float dx = x - GetPositionX();
+ float dy = y - GetPositionY();
+
+ float ang = atan2(dy, dx);
+ ang = (ang >= 0) ? ang : 2 * M_PI + ang;
+ return ang;
+}
+
+bool WorldObject::HasInArc(const float arcangle, const WorldObject* obj) const
+{
+ float arc = arcangle;
+
+ // move arc to range 0.. 2*pi
+ while( arc >= 2.0f * M_PI )
+ arc -= 2.0f * M_PI;
+ while( arc < 0 )
+ arc += 2.0f * M_PI;
+
+ float angle = GetAngle( obj );
+ angle -= m_orientation;
+
+ // move angle to range -pi ... +pi
+ while( angle > M_PI)
+ angle -= 2.0f * M_PI;
+ while(angle < -M_PI)
+ angle += 2.0f * M_PI;
+
+ float lborder = -1 * (arc/2.0f); // in range -pi..0
+ float rborder = (arc/2.0f); // in range 0..pi
+ return (( angle >= lborder ) && ( angle <= rborder ));
+}
+
+void WorldObject::GetRandomPoint( float x, float y, float z, float distance, float &rand_x, float &rand_y, float &rand_z) const
+{
+ if(distance==0)
+ {
+ rand_x = x;
+ rand_y = y;
+ rand_z = z;
+ return;
+ }
+
+ // angle to face `obj` to `this`
+ float angle = rand_norm()*2*M_PI;
+ float new_dist = rand_norm()*distance;
+
+ rand_x = x + new_dist * cos(angle);
+ rand_y = y + new_dist * sin(angle);
+ rand_z = z;
+
+ MaNGOS::NormalizeMapCoord(rand_x);
+ MaNGOS::NormalizeMapCoord(rand_y);
+ UpdateGroundPositionZ(rand_x,rand_y,rand_z); // update to LOS height if available
+}
+
+void WorldObject::UpdateGroundPositionZ(float x, float y, float &z) const
+{
+ float new_z = MapManager::Instance().GetBaseMap(GetMapId())->GetHeight(x,y,z,true);
+ if(new_z > INVALID_HEIGHT)
+ z = new_z+ 0.05f; // just to be sure that we are not a few pixel under the surface
+}
+
+bool WorldObject::IsPositionValid() const
+{
+ return MaNGOS::IsValidMapCoord(m_positionX,m_positionY,m_positionZ,m_orientation);
+}
+
+void WorldObject::MonsterSay(const char* text, uint32 language, uint64 TargetGuid)
+{
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildMonsterChat(&data,CHAT_MSG_MONSTER_SAY,text,language,GetName(),TargetGuid);
+ SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),true);
+}
+
+void WorldObject::MonsterYell(const char* text, uint32 language, uint64 TargetGuid)
+{
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildMonsterChat(&data,CHAT_MSG_MONSTER_YELL,text,language,GetName(),TargetGuid);
+ SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL),true);
+}
+
+void WorldObject::MonsterTextEmote(const char* text, uint64 TargetGuid, bool IsBossEmote)
+{
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildMonsterChat(&data,IsBossEmote ? CHAT_MSG_RAID_BOSS_EMOTE : CHAT_MSG_MONSTER_EMOTE,text,LANG_UNIVERSAL,GetName(),TargetGuid);
+ SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true);
+}
+
+void WorldObject::MonsterWhisper(const char* text, uint64 receiver, bool IsBossWhisper)
+{
+ Player *player = objmgr.GetPlayer(receiver);
+ if(!player || !player->GetSession())
+ return;
+
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildMonsterChat(&data,IsBossWhisper ? CHAT_MSG_RAID_BOSS_WHISPER : CHAT_MSG_MONSTER_WHISPER,text,LANG_UNIVERSAL,GetName(),receiver);
+
+ player->GetSession()->SendPacket(&data);
+}
+
+void WorldObject::SendPlaySound(uint32 Sound, bool OnlySelf)
+{
+ WorldPacket data(SMSG_PLAY_SOUND, 4);
+ data << Sound;
+ if (OnlySelf && GetTypeId() == TYPEID_PLAYER )
+ ((Player*)this)->GetSession()->SendPacket( &data );
+ else
+ SendMessageToSet( &data, true ); // ToSelf ignored in this case
+}
+
+namespace MaNGOS
+{
+ class MessageChatLocaleCacheDo
+ {
+ public:
+ MessageChatLocaleCacheDo(WorldObject const& obj, ChatMsg msgtype, int32 textId, uint32 language, uint64 targetGUID, float dist)
+ : i_object(obj), i_msgtype(msgtype), i_textId(textId), i_language(language),
+ i_targetGUID(targetGUID), i_dist(dist)
+ {
+ }
+
+ ~MessageChatLocaleCacheDo()
+ {
+ for(int i = 0; i < i_data_cache.size(); ++i)
+ delete i_data_cache[i];
+ }
+
+ void operator()(Player* p)
+ {
+ // skip far away players
+ if(p->GetDistance(&i_object) > i_dist)
+ return;
+
+ uint32 loc_idx = p->GetSession()->GetSessionDbLocaleIndex();
+ uint32 cache_idx = loc_idx+1;
+ WorldPacket* data;
+
+ // create if not cached yet
+ if(i_data_cache.size() < cache_idx+1 || !i_data_cache[cache_idx])
+ {
+ if(i_data_cache.size() < cache_idx+1)
+ i_data_cache.resize(cache_idx+1);
+
+ char const* text = objmgr.GetMangosString(i_textId,loc_idx);
+
+ data = new WorldPacket(SMSG_MESSAGECHAT, 200);
+
+ // TODO: i_object.GetName() also must be localized?
+ i_object.BuildMonsterChat(data,i_msgtype,text,i_language,i_object.GetName(),i_targetGUID);
+
+ i_data_cache[cache_idx] = data;
+ }
+ else
+ data = i_data_cache[cache_idx];
+
+ p->SendDirectMessage(data);
+ }
+
+ private:
+ WorldObject const& i_object;
+ ChatMsg i_msgtype;
+ int32 i_textId;
+ uint32 i_language;
+ uint64 i_targetGUID;
+ float i_dist;
+ std::vector<WorldPacket*> i_data_cache; // 0 = default, i => i-1 locale index
+ };
+} // namespace MaNGOS
+
+void WorldObject::MonsterSay(int32 textId, uint32 language, uint64 TargetGuid)
+{
+ CellPair p = MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY());
+
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::MessageChatLocaleCacheDo say_do(*this, CHAT_MSG_MONSTER_SAY, textId,language,TargetGuid,sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY));
+ MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo> say_worker(say_do);
+ TypeContainerVisitor<MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo>, WorldTypeMapContainer > message(say_worker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, message, *GetMap());
+}
+
+void WorldObject::MonsterYell(int32 textId, uint32 language, uint64 TargetGuid)
+{
+ CellPair p = MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY());
+
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::MessageChatLocaleCacheDo say_do(*this, CHAT_MSG_MONSTER_YELL, textId,language,TargetGuid,sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL));
+ MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo> say_worker(say_do);
+ TypeContainerVisitor<MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo>, WorldTypeMapContainer > message(say_worker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, message, *GetMap());
+}
+
+void WorldObject::MonsterTextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote)
+{
+ CellPair p = MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY());
+
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::MessageChatLocaleCacheDo say_do(*this, IsBossEmote ? CHAT_MSG_RAID_BOSS_EMOTE : CHAT_MSG_MONSTER_EMOTE, textId,LANG_UNIVERSAL,TargetGuid,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE));
+ MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo> say_worker(say_do);
+ TypeContainerVisitor<MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo>, WorldTypeMapContainer > message(say_worker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, message, *GetMap());
+}
+
+void WorldObject::MonsterWhisper(int32 textId, uint64 receiver, bool IsBossWhisper)
+{
+ Player *player = objmgr.GetPlayer(receiver);
+ if(!player || !player->GetSession())
+ return;
+
+ uint32 loc_idx = player->GetSession()->GetSessionDbLocaleIndex();
+ char const* text = objmgr.GetMangosString(textId,loc_idx);
+
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildMonsterChat(&data,IsBossWhisper ? CHAT_MSG_RAID_BOSS_WHISPER : CHAT_MSG_MONSTER_WHISPER,text,LANG_UNIVERSAL,GetName(),receiver);
+
+ player->GetSession()->SendPacket(&data);
+}
+
+void WorldObject::BuildMonsterChat(WorldPacket *data, uint8 msgtype, char const* text, uint32 language, char const* name, uint64 targetGuid) const
+{
+ bool pre = (msgtype==CHAT_MSG_MONSTER_EMOTE || msgtype==CHAT_MSG_RAID_BOSS_EMOTE);
+
+ *data << (uint8)msgtype;
+ *data << (uint32)language;
+ *data << (uint64)GetGUID();
+ *data << (uint32)0; //2.1.0
+ *data << (uint32)(strlen(name)+1);
+ *data << name;
+ *data << (uint64)targetGuid; //Unit Target
+ if( targetGuid && !IS_PLAYER_GUID(targetGuid) )
+ {
+ *data << (uint32)1; // target name length
+ *data << (uint8)0; // target name
+ }
+ *data << (uint32)(strlen(text)+1+(pre?3:0));
+ if(pre)
+ data->append("%s ",3);
+ *data << text;
+ *data << (uint8)0; // ChatTag
+}
+
+void WorldObject::BuildHeartBeatMsg(WorldPacket *data) const
+{
+ //Heartbeat message cannot be used for non-units
+ if (!isType(TYPEMASK_UNIT))
+ return;
+
+ data->Initialize(MSG_MOVE_HEARTBEAT, 32);
+ data->append(GetPackGUID());
+ *data << uint32(((Unit*)this)->GetUnitMovementFlags()); // movement flags
+ *data << uint8(0); // 2.3.0
+ *data << getMSTime(); // time
+ *data << m_positionX;
+ *data << m_positionY;
+ *data << m_positionZ;
+ *data << m_orientation;
+ *data << uint32(0);
+}
+
+void WorldObject::BuildTeleportAckMsg(WorldPacket *data, float x, float y, float z, float ang) const
+{
+ //TeleportAck message cannot be used for non-units
+ if (!isType(TYPEMASK_UNIT))
+ return;
+
+ data->Initialize(MSG_MOVE_TELEPORT_ACK, 41);
+ data->append(GetPackGUID());
+ *data << uint32(0); // this value increments every time
+ *data << uint32(((Unit*)this)->GetUnitMovementFlags()); // movement flags
+ *data << uint8(0); // 2.3.0
+ *data << getMSTime(); // time
+ *data << x;
+ *data << y;
+ *data << z;
+ *data << ang;
+ *data << uint32(0);
+}
+
+void WorldObject::SendMessageToSet(WorldPacket *data, bool /*bToSelf*/)
+{
+ MapManager::Instance().GetMap(m_mapId, this)->MessageBroadcast(this, data);
+}
+
+void WorldObject::SendMessageToSetInRange(WorldPacket *data, float dist, bool /*bToSelf*/)
+{
+ MapManager::Instance().GetMap(m_mapId, this)->MessageDistBroadcast(this, data, dist);
+}
+
+void WorldObject::SendObjectDeSpawnAnim(uint64 guid)
+{
+ WorldPacket data(SMSG_GAMEOBJECT_DESPAWN_ANIM, 8);
+ data << guid;
+ SendMessageToSet(&data, true);
+}
+
+Map* WorldObject::GetMap() const
+{
+ return MapManager::Instance().GetMap(GetMapId(), this);
+}
+
+Map const* WorldObject::GetBaseMap() const
+{
+ return MapManager::Instance().GetBaseMap(GetMapId());
+}
+
+void WorldObject::AddObjectToRemoveList()
+{
+ Map* map = GetMap();
+ if(!map)
+ {
+ sLog.outError("Object (TypeId: %u Entry: %u GUID: %u) at attempt add to move list not have valid map (Id: %u).",GetTypeId(),GetEntry(),GetGUIDLow(),GetMapId());
+ return;
+ }
+
+ map->AddObjectToRemoveList(this);
+}
+
+Creature* WorldObject::SummonCreature(uint32 id, float x, float y, float z, float ang,TempSummonType spwtype,uint32 despwtime)
+{
+ TemporarySummon* pCreature = new TemporarySummon(GetGUID());
+
+ pCreature->SetInstanceId(GetInstanceId());
+ uint32 team = 0;
+ if (GetTypeId()==TYPEID_PLAYER)
+ team = ((Player*)this)->GetTeam();
+
+ if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), GetMap(), id, team))
+ {
+ delete pCreature;
+ return NULL;
+ }
+
+ if (x == 0.0f && y == 0.0f && z == 0.0f)
+ GetClosePoint(x, y, z, pCreature->GetObjectSize());
+
+ pCreature->Relocate(x, y, z, ang);
+
+ if(!pCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
+ delete pCreature;
+ return NULL;
+ }
+
+ pCreature->Summon(spwtype, despwtime);
+
+ if(GetTypeId()==TYPEID_UNIT && ((Creature*)this)->AI())
+ ((Creature*)this)->AI()->JustSummoned(pCreature);
+
+ //return the creature therewith the summoner has access to it
+ return pCreature;
+}
+
+void WorldObject::GetNearPoint2D(float &x, float &y, float distance2d, float absAngle ) const
+{
+ x = GetPositionX() + (GetObjectSize() + distance2d) * cos(absAngle);
+ y = GetPositionY() + (GetObjectSize() + distance2d) * sin(absAngle);
+
+ MaNGOS::NormalizeMapCoord(x);
+ MaNGOS::NormalizeMapCoord(y);
+}
+
+void WorldObject::GetNearPoint(WorldObject const* searcher, float &x, float &y, float &z, float searcher_size, float distance2d, float absAngle ) const
+{
+ GetNearPoint2D(x,y,distance2d+searcher_size,absAngle);
+
+ z = GetPositionZ();
+
+ UpdateGroundPositionZ(x,y,z);
+}
diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 1bbca47d3b5..74a6a3ef684 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -6679,6 +6679,8 @@ void ObjectMgr::LoadTrainerSpell() itr->second.Clear();
m_mCacheTrainerSpellMap.clear();
+ std::set<uint32> skip_trainers;
+
QueryResult *result = WorldDatabase.PQuery("SELECT entry, spell,spellcost,reqskill,reqskillvalue,reqlevel FROM npc_trainer");
if( !result )
@@ -6714,7 +6716,11 @@ void ObjectMgr::LoadTrainerSpell() if(!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER))
{
- sLog.outErrorDb("Table `npc_trainer` have data for not creature template (Entry: %u) without trainer flag, ignore", entry);
+ if(skip_trainers.count(entry) == 0)
+ {
+ sLog.outErrorDb("Table `npc_trainer` have data for not creature template (Entry: %u) without trainer flag, ignore", entry);
+ skip_trainers.insert(entry);
+ }
continue;
}
@@ -6764,6 +6770,8 @@ void ObjectMgr::LoadVendors() itr->second.Clear();
m_mCacheVendorItemMap.clear();
+ std::set<uint32> skip_vendors;
+
QueryResult *result = WorldDatabase.PQuery("SELECT entry, item, maxcount, incrtime, ExtendedCost FROM npc_vendor");
if( !result )
{
@@ -6790,7 +6798,7 @@ void ObjectMgr::LoadVendors() uint32 incrtime = fields[3].GetUInt32();
uint32 ExtendedCost = fields[4].GetUInt32();
- if(!IsVendorItemValid(entry,item_id,maxcount,incrtime,ExtendedCost))
+ if(!IsVendorItemValid(entry,item_id,maxcount,incrtime,ExtendedCost,NULL,&skip_vendors))
continue;
VendorItemData& vList = m_mCacheVendorItemMap[entry];
@@ -6878,7 +6886,7 @@ bool ObjectMgr::RemoveVendorItem( uint32 entry,uint32 item ) return true;
}
-bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* pl ) const
+bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* pl, std::set<uint32>* skip_vendors ) const
{
CreatureInfo const* cInfo = GetCreatureTemplate(vendor_entry);
if(!cInfo)
@@ -6892,10 +6900,16 @@ bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 m if(!(cInfo->npcflag & UNIT_NPC_FLAG_VENDOR))
{
- if(pl)
- ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
- else
- sLog.outErrorDb("Table `npc_vendor` have data for not creature template (Entry: %u) without vendor flag, ignore", vendor_entry);
+ if(!skip_vendors || skip_vendors->count(vendor_entry)==0)
+ {
+ if(pl)
+ ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
+ else
+ sLog.outErrorDb("Table `npc_vendor` have data for not creature template (Entry: %u) without vendor flag, ignore", vendor_entry);
+
+ if(skip_vendors)
+ skip_vendors->insert(vendor_entry);
+ }
return false;
}
diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index 5bb3aafde72..f5d35009307 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -742,7 +742,7 @@ class ObjectMgr }
void AddVendorItem(uint32 entry,uint32 item, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost);
bool RemoveVendorItem(uint32 entry,uint32 item);
- bool IsVendorItemValid( uint32 vendor_entry, uint32 item, uint32 maxcount, uint32 ptime, uint32 ExtendedCost, Player* pl = NULL ) const;
+ bool IsVendorItemValid( uint32 vendor_entry, uint32 item, uint32 maxcount, uint32 ptime, uint32 ExtendedCost, Player* pl = NULL, std::set<uint32>* skip_vendors = NULL ) const;
protected:
uint32 m_auctionid;
uint32 m_mailid;
diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp index 3251d02d5b9..9ac00048d49 100644 --- a/src/game/PetHandler.cpp +++ b/src/game/PetHandler.cpp @@ -1,648 +1,649 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "Common.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "World.h" -#include "ObjectMgr.h" -#include "SpellMgr.h" -#include "Log.h" -#include "Opcodes.h" -#include "Spell.h" -#include "ObjectAccessor.h" -#include "MapManager.h" -#include "CreatureAI.h" -#include "Util.h" -#include "Pet.h" - -void WorldSession::HandlePetAction( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,8+2+2+8); - - uint64 guid1; - uint16 spellid; - uint16 flag; - uint64 guid2; - recv_data >> guid1; //pet guid - recv_data >> spellid; - recv_data >> flag; //delete = 0x0700 CastSpell = C100 - recv_data >> guid2; //tag guid - - // used also for charmed creature - Unit* pet= ObjectAccessor::GetUnit(*_player,guid1); - sLog.outDetail( "HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.\n", uint32(GUID_LOPART(guid1)), flag, spellid, uint32(GUID_LOPART(guid2)) ); - if(!pet) - { - sLog.outError( "Pet %u not exist.\n", uint32(GUID_LOPART(guid1)) ); - return; - } - - if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm()) - { - sLog.outError( "HandlePetAction.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid1)),GetPlayer()->GetName() ); - return; - } - - if(!pet->isAlive()) - return; - - if(pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK)) - return; - - CharmInfo *charmInfo = pet->GetCharmInfo(); - if(!charmInfo) - { - sLog.outError("WorldSession::HandlePetAction: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); - return; - } - - switch(flag) - { - case ACT_COMMAND: //0x0700 - switch(spellid) - { - case COMMAND_STAY: //flat=1792 //STAY - pet->StopMoving(); - pet->GetMotionMaster()->Clear(); - pet->GetMotionMaster()->MoveIdle(); - charmInfo->SetCommandState( COMMAND_STAY ); - break; - case COMMAND_FOLLOW: //spellid=1792 //FOLLOW - pet->AttackStop(); - pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); - charmInfo->SetCommandState( COMMAND_FOLLOW ); - break; - case COMMAND_ATTACK: //spellid=1792 //ATTACK - { - // only place where pet can be player - pet->clearUnitState(UNIT_STAT_FOLLOW); - uint64 selguid = _player->GetSelection(); - Unit *TargetUnit = ObjectAccessor::GetUnit(*_player, selguid); - if(!TargetUnit) - return; - - // not let attack friendly units. - if( GetPlayer()->IsFriendlyTo(TargetUnit)) - return; - - if(pet->getVictim()) - pet->AttackStop(); - - if(pet->GetTypeId() != TYPEID_PLAYER) - { - pet->GetMotionMaster()->Clear(); - if (((Creature*)pet)->AI()) - ((Creature*)pet)->AI()->AttackStart(TargetUnit); - - //10% chance to play special pet attack talk, else growl - if(((Creature*)pet)->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10) - pet->SendPetTalk((uint32)PET_TALK_ATTACK); - else - { - // 90% chance for pet and 100% chance for charmed creature - pet->SendPetAIReaction(guid1); - } - } - else // charmed player - { - pet->Attack(TargetUnit,true); - pet->SendPetAIReaction(guid1); - } - break; - } - case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) - if(((Creature*)pet)->isPet()) - { - Pet* p = (Pet*)pet; - if(p->getPetType() == HUNTER_PET) - _player->RemovePet(p,PET_SAVE_AS_DELETED); - else - //dismissing a summoned pet is like killing them (this prevents returning a soulshard...) - p->setDeathState(CORPSE); - } - else // charmed - _player->Uncharm(); - break; - default: - sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid); - } - break; - case ACT_REACTION: // 0x600 - switch(spellid) - { - case REACT_PASSIVE: //passive - case REACT_DEFENSIVE: //recovery - case REACT_AGGRESSIVE: //activete - charmInfo->SetReactState( ReactStates(spellid) ); - break; - } - break; - case ACT_DISABLED: //0x8100 spell (disabled), ignore - case ACT_CAST: //0x0100 - case ACT_ENABLED: //0xc100 spell - { - Unit* unit_target; - if(guid2) - unit_target = ObjectAccessor::GetUnit(*_player,guid2); - else - unit_target = NULL; - - // do not cast unknown spells - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid ); - if(!spellInfo) - { - sLog.outError("WORLD: unknown PET spell id %i\n", spellid); - return; - } - - for(uint32 i = 0; i < 3;i++) - { - if(spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) - return; - } - - // do not cast not learned spells - if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) - return; - - pet->clearUnitState(UNIT_STAT_FOLLOW); - - Spell *spell = new Spell(pet, spellInfo, false); - - int16 result = spell->PetCanCast(unit_target); - - //auto turn to target unless possessed - if(result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) - { - pet->SetInFront(unit_target); - if( unit_target->GetTypeId() == TYPEID_PLAYER ) - pet->SendUpdateToPlayer( (Player*)unit_target ); - if(Unit* powner = pet->GetCharmerOrOwner()) - if(powner->GetTypeId() == TYPEID_PLAYER) - pet->SendUpdateToPlayer((Player*)powner); - result = -1; - } - - if(result == -1) - { - ((Creature*)pet)->AddCreatureSpellCooldown(spellid); - if (((Creature*)pet)->isPet()) - ((Pet*)pet)->CheckLearning(spellid); - - unit_target = spell->m_targets.getUnitTarget(); - - //10% chance to play special pet attack talk, else growl - //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell - if(((Creature*)pet)->isPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10)) - pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); - else - { - pet->SendPetAIReaction(guid1); - } - - if( unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) - { - pet->clearUnitState(UNIT_STAT_FOLLOW); - if(pet->getVictim()) - pet->AttackStop(); - pet->GetMotionMaster()->Clear(); - if (((Creature*)pet)->AI()) - ((Creature*)pet)->AI()->AttackStart(unit_target); - } - - spell->prepare(&(spell->m_targets)); - } - else - { - if(pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) - { - WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); - data << uint32(spellid) << uint8(2) << uint8(result); - switch (result) - { - case SPELL_FAILED_REQUIRES_SPELL_FOCUS: - data << uint32(spellInfo->RequiresSpellFocus); - break; - case SPELL_FAILED_REQUIRES_AREA: - data << uint32(spellInfo->AreaId); - break; - } - SendPacket(&data); - } - else - pet->SendPetCastFail(spellid, result); - - if(!((Creature*)pet)->HasSpellCooldown(spellid)) - pet->SendPetClearCooldown(spellid); - - spell->finish(false); - delete spell; - } - break; - } - default: - sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid); - } -} - -void WorldSession::HandlePetNameQuery( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,4+8); - - sLog.outDetail( "HandlePetNameQuery. CMSG_PET_NAME_QUERY\n" ); - - uint32 petnumber; - uint64 petguid; - - recv_data >> petnumber; - recv_data >> petguid; - - SendPetNameQuery(petguid,petnumber); -} - -void WorldSession::SendPetNameQuery( uint64 petguid, uint32 petnumber) -{ - Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, petguid); - if(!pet || !pet->GetCharmInfo() || pet->GetCharmInfo()->GetPetNumber() != petnumber) - return; - - std::string name = pet->GetName(); - - WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+4+name.size()+1)); - data << uint32(petnumber); - data << name.c_str(); - data << uint32(pet->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP)); - - if( pet->isPet() && ((Pet*)pet)->GetDeclinedNames() ) - { - data << uint8(1); - for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) - data << ((Pet*)pet)->GetDeclinedNames()->name[i]; - } - else - data << uint8(0); - - _player->GetSession()->SendPacket(&data); -} - -void WorldSession::HandlePetSetAction( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,8+4+2+2); - - sLog.outDetail( "HandlePetSetAction. CMSG_PET_SET_ACTION\n" ); - - uint64 petguid; - uint32 position; - uint16 spell_id; - uint16 act_state; - uint8 count; - - recv_data >> petguid; - - // FIXME: charmed case - //Pet* pet = ObjectAccessor::Instance().GetPet(petguid); - if(ObjectAccessor::FindPlayer(petguid)) - return; - - Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, petguid); - - if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm())) - { - sLog.outError( "HandlePetSetAction: Unknown pet or pet owner.\n" ); - return; - } - - CharmInfo *charmInfo = pet->GetCharmInfo(); - if(!charmInfo) - { - sLog.outError("WorldSession::HandlePetSetAction: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); - return; - } - - count = (recv_data.size() == 24) ? 2 : 1; - for(uint8 i = 0; i < count; i++) - { - recv_data >> position; - recv_data >> spell_id; - recv_data >> act_state; - - sLog.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X\n", _player->GetName(), position, spell_id, act_state); - - //if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add - if(!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_CAST) && spell_id && !pet->HasSpell(spell_id))) - { - //sign for autocast - if(act_state == ACT_ENABLED && spell_id) - { - if(pet->isCharmed()) - charmInfo->ToggleCreatureAutocast(spell_id, true); - else - ((Pet*)pet)->ToggleAutocast(spell_id, true); - } - //sign for no/turn off autocast - else if(act_state == ACT_DISABLED && spell_id) - { - if(pet->isCharmed()) - charmInfo->ToggleCreatureAutocast(spell_id, false); - else - ((Pet*)pet)->ToggleAutocast(spell_id, false); - } - - charmInfo->GetActionBarEntry(position)->Type = act_state; - charmInfo->GetActionBarEntry(position)->SpellOrAction = spell_id; - } - } -} - -void WorldSession::HandlePetRename( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,8+1+1+1+1+1+1+1); - - sLog.outDetail( "HandlePetRename. CMSG_PET_RENAME\n" ); - - uint64 petguid; - uint8 isdeclined; - - std::string name; - DeclinedName declinedname; - - recv_data >> petguid; - recv_data >> name; - recv_data >> isdeclined; - - Pet* pet = ObjectAccessor::GetPet(petguid); - // check it! - if( !pet || !pet->isPet() || ((Pet*)pet)->getPetType()!= HUNTER_PET || - pet->GetByteValue(UNIT_FIELD_BYTES_2, 2) != UNIT_RENAME_ALLOWED || - pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo() ) - return; - - if((!ObjectMgr::IsValidPetName(name)) || (objmgr.IsReservedName(name))) - { - SendNotification("Invalid name"); - return; - } - pet->SetName(name); - - Unit *owner = pet->GetOwner(); - if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup()) - ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_NAME); - - pet->SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED); - - if(isdeclined) - { - for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) - recv_data >> declinedname.name[i]; - - std::wstring wname; - Utf8toWStr(name,wname); - if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname,0),declinedname)) - { - SendNotification("Invalid name"); - return; - } - } - - CharacterDatabase.BeginTransaction(); - if(isdeclined) - { - for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) - CharacterDatabase.escape_string(declinedname.name[i]); - CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber()); - CharacterDatabase.PExecute("INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%u','%s','%s','%s','%s','%s')", - pet->GetCharmInfo()->GetPetNumber(), _player->GetGUIDLow(), declinedname.name[0].c_str(),declinedname.name[1].c_str(),declinedname.name[2].c_str(),declinedname.name[3].c_str(),declinedname.name[4].c_str()); - } - - CharacterDatabase.escape_string(name); - CharacterDatabase.PExecute("UPDATE character_pet SET name = '%s', renamed = '1' WHERE owner = '%u' AND id = '%u'", name.c_str(),_player->GetGUIDLow(),pet->GetCharmInfo()->GetPetNumber() ); - CharacterDatabase.CommitTransaction(); - - pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL)); -} - -void WorldSession::HandlePetAbandon( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,8); - - uint64 guid; - recv_data >> guid; //pet guid - sLog.outDetail( "HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid) ); - - // pet/charmed - Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player, guid); - if(pet) - { - if(pet->isPet()) - { - if(pet->GetGUID() == _player->GetPetGUID()) - { - uint32 feelty = pet->GetPower(POWER_HAPPINESS); - pet->SetPower(POWER_HAPPINESS ,(feelty-50000) > 0 ?(feelty-50000) : 0); - } - - _player->RemovePet((Pet*)pet,PET_SAVE_AS_DELETED); - } - else if(pet->GetGUID() == _player->GetCharmGUID()) - { - _player->Uncharm(); - } - } -} - -void WorldSession::HandlePetUnlearnOpcode(WorldPacket& recvPacket) -{ - CHECK_PACKET_SIZE(recvPacket,8); - - sLog.outDetail("CMSG_PET_UNLEARN"); - uint64 guid; - recvPacket >> guid; - - Pet* pet = _player->GetPet(); - - if(!pet || pet->getPetType() != HUNTER_PET || pet->m_spells.size() <= 1) - return; - - if(guid != pet->GetGUID()) - { - sLog.outError( "HandlePetUnlearnOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); - return; - } - - CharmInfo *charmInfo = pet->GetCharmInfo(); - if(!charmInfo) - { - sLog.outError("WorldSession::HandlePetUnlearnOpcode: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); - return; - } - - uint32 cost = pet->resetTalentsCost(); - - if (GetPlayer()->GetMoney() < cost) - { - GetPlayer()->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0); - return; - } - - for(PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end();) - { - uint32 spell_id = itr->first; // Pet::removeSpell can invalidate iterator at erase NEW spell - ++itr; - pet->removeSpell(spell_id); - } - - pet->SetTP(pet->getLevel() * (pet->GetLoyaltyLevel() - 1)); - - for(uint8 i = 0; i < 10; i++) - { - if(charmInfo->GetActionBarEntry(i)->SpellOrAction && charmInfo->GetActionBarEntry(i)->Type == ACT_ENABLED || charmInfo->GetActionBarEntry(i)->Type == ACT_DISABLED) - charmInfo->GetActionBarEntry(i)->SpellOrAction = 0; - } - - // relearn pet passives - pet->LearnPetPassives(); - - pet->m_resetTalentsTime = time(NULL); - pet->m_resetTalentsCost = cost; - GetPlayer()->ModifyMoney(-(int32)cost); - - GetPlayer()->PetSpellInitialize(); -} - -void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket ) -{ - CHECK_PACKET_SIZE(recvPacket,8+2+2+1); - - sLog.outDetail("CMSG_PET_SPELL_AUTOCAST"); - uint64 guid; - uint16 spellid; - uint16 spellid2; //maybe second spell, automatically toggled off when first toggled on? - uint8 state; //1 for on, 0 for off - recvPacket >> guid >> spellid >> spellid2 >> state; - - if(!_player->GetPet() && !_player->GetCharm()) - return; - - if(ObjectAccessor::FindPlayer(guid)) - return; - - Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid); - - if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm())) - { - sLog.outError( "HandlePetSpellAutocastOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); - return; - } - - // do not add not learned spells/ passive spells - if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) - return; - - CharmInfo *charmInfo = pet->GetCharmInfo(); - if(!charmInfo) - { - sLog.outError("WorldSession::HandlePetSpellAutocastOpcod: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); - return; - } - - if(pet->isCharmed()) - //state can be used as boolean - pet->GetCharmInfo()->ToggleCreatureAutocast(spellid, state); - else - ((Pet*)pet)->ToggleAutocast(spellid, state); - - for(uint8 i = 0; i < 10; ++i) - { - if((charmInfo->GetActionBarEntry(i)->Type == ACT_ENABLED || charmInfo->GetActionBarEntry(i)->Type == ACT_DISABLED) && spellid == charmInfo->GetActionBarEntry(i)->SpellOrAction) - charmInfo->GetActionBarEntry(i)->Type = state ? ACT_ENABLED : ACT_DISABLED; - } -} - -void WorldSession::HandleAddDynamicTargetObsoleteOpcode( WorldPacket& recvPacket ) -{ - sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL"); - - CHECK_PACKET_SIZE(recvPacket,8+4); - uint64 guid; - uint32 spellid; - - recvPacket >> guid >> spellid; - - if(!_player->GetPet() && !_player->GetCharm()) - return; - - if(ObjectAccessor::FindPlayer(guid)) - return; - - Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid); - - if(!pet || (pet != _player->GetPet() && pet!= _player->GetCharm())) - { - sLog.outError( "HandleAddDynamicTargetObsoleteOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); - return; - } - - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); - if(!spellInfo) - { - sLog.outError("WORLD: unknown PET spell id %i\n", spellid); - return; - } - - // do not cast not learned spells - if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) - return; - - SpellCastTargets targets; - if(!targets.read(&recvPacket,pet)) - return; - - pet->clearUnitState(UNIT_STAT_FOLLOW); - - Spell *spell = new Spell(pet, spellInfo, false); - spell->m_targets = targets; - - int16 result = spell->PetCanCast(NULL); - if(result == -1) - { - pet->AddCreatureSpellCooldown(spellid); - if(pet->isPet()) - { - Pet* p = (Pet*)pet; - p->CheckLearning(spellid); - //10% chance to play special pet attack talk, else growl - //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell - if(p->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) - pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); - else - pet->SendPetAIReaction(guid); - } - - spell->prepare(&(spell->m_targets)); - } - else - { - pet->SendPetCastFail(spellid, result); - if(!pet->HasSpellCooldown(spellid)) - pet->SendPetClearCooldown(spellid); - - spell->finish(false); - delete spell; - } -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "Spell.h"
+#include "ObjectAccessor.h"
+#include "MapManager.h"
+#include "CreatureAI.h"
+#include "Util.h"
+#include "Pet.h"
+#include "Language.h"
+
+void WorldSession::HandlePetAction( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+2+2+8);
+
+ uint64 guid1;
+ uint16 spellid;
+ uint16 flag;
+ uint64 guid2;
+ recv_data >> guid1; //pet guid
+ recv_data >> spellid;
+ recv_data >> flag; //delete = 0x0700 CastSpell = C100
+ recv_data >> guid2; //tag guid
+
+ // used also for charmed creature
+ Unit* pet= ObjectAccessor::GetUnit(*_player,guid1);
+ sLog.outDetail( "HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.\n", uint32(GUID_LOPART(guid1)), flag, spellid, uint32(GUID_LOPART(guid2)) );
+ if(!pet)
+ {
+ sLog.outError( "Pet %u not exist.\n", uint32(GUID_LOPART(guid1)) );
+ return;
+ }
+
+ if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm())
+ {
+ sLog.outError( "HandlePetAction.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid1)),GetPlayer()->GetName() );
+ return;
+ }
+
+ if(!pet->isAlive())
+ return;
+
+ if(pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK))
+ return;
+
+ CharmInfo *charmInfo = pet->GetCharmInfo();
+ if(!charmInfo)
+ {
+ sLog.outError("WorldSession::HandlePetAction: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID());
+ return;
+ }
+
+ switch(flag)
+ {
+ case ACT_COMMAND: //0x0700
+ switch(spellid)
+ {
+ case COMMAND_STAY: //flat=1792 //STAY
+ pet->StopMoving();
+ pet->GetMotionMaster()->Clear();
+ pet->GetMotionMaster()->MoveIdle();
+ charmInfo->SetCommandState( COMMAND_STAY );
+ break;
+ case COMMAND_FOLLOW: //spellid=1792 //FOLLOW
+ pet->AttackStop();
+ pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
+ charmInfo->SetCommandState( COMMAND_FOLLOW );
+ break;
+ case COMMAND_ATTACK: //spellid=1792 //ATTACK
+ {
+ // only place where pet can be player
+ pet->clearUnitState(UNIT_STAT_FOLLOW);
+ uint64 selguid = _player->GetSelection();
+ Unit *TargetUnit = ObjectAccessor::GetUnit(*_player, selguid);
+ if(!TargetUnit)
+ return;
+
+ // not let attack friendly units.
+ if( GetPlayer()->IsFriendlyTo(TargetUnit))
+ return;
+
+ if(pet->getVictim())
+ pet->AttackStop();
+
+ if(pet->GetTypeId() != TYPEID_PLAYER)
+ {
+ pet->GetMotionMaster()->Clear();
+ if (((Creature*)pet)->AI())
+ ((Creature*)pet)->AI()->AttackStart(TargetUnit);
+
+ //10% chance to play special pet attack talk, else growl
+ if(((Creature*)pet)->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10)
+ pet->SendPetTalk((uint32)PET_TALK_ATTACK);
+ else
+ {
+ // 90% chance for pet and 100% chance for charmed creature
+ pet->SendPetAIReaction(guid1);
+ }
+ }
+ else // charmed player
+ {
+ pet->Attack(TargetUnit,true);
+ pet->SendPetAIReaction(guid1);
+ }
+ break;
+ }
+ case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet)
+ if(((Creature*)pet)->isPet())
+ {
+ Pet* p = (Pet*)pet;
+ if(p->getPetType() == HUNTER_PET)
+ _player->RemovePet(p,PET_SAVE_AS_DELETED);
+ else
+ //dismissing a summoned pet is like killing them (this prevents returning a soulshard...)
+ p->setDeathState(CORPSE);
+ }
+ else // charmed
+ _player->Uncharm();
+ break;
+ default:
+ sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid);
+ }
+ break;
+ case ACT_REACTION: // 0x600
+ switch(spellid)
+ {
+ case REACT_PASSIVE: //passive
+ case REACT_DEFENSIVE: //recovery
+ case REACT_AGGRESSIVE: //activete
+ charmInfo->SetReactState( ReactStates(spellid) );
+ break;
+ }
+ break;
+ case ACT_DISABLED: //0x8100 spell (disabled), ignore
+ case ACT_CAST: //0x0100
+ case ACT_ENABLED: //0xc100 spell
+ {
+ Unit* unit_target;
+ if(guid2)
+ unit_target = ObjectAccessor::GetUnit(*_player,guid2);
+ else
+ unit_target = NULL;
+
+ // do not cast unknown spells
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid );
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown PET spell id %i\n", spellid);
+ return;
+ }
+
+ for(uint32 i = 0; i < 3;i++)
+ {
+ if(spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED)
+ return;
+ }
+
+ // do not cast not learned spells
+ if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid))
+ return;
+
+ pet->clearUnitState(UNIT_STAT_FOLLOW);
+
+ Spell *spell = new Spell(pet, spellInfo, false);
+
+ int16 result = spell->PetCanCast(unit_target);
+
+ //auto turn to target unless possessed
+ if(result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS))
+ {
+ pet->SetInFront(unit_target);
+ if( unit_target->GetTypeId() == TYPEID_PLAYER )
+ pet->SendUpdateToPlayer( (Player*)unit_target );
+ if(Unit* powner = pet->GetCharmerOrOwner())
+ if(powner->GetTypeId() == TYPEID_PLAYER)
+ pet->SendUpdateToPlayer((Player*)powner);
+ result = -1;
+ }
+
+ if(result == -1)
+ {
+ ((Creature*)pet)->AddCreatureSpellCooldown(spellid);
+ if (((Creature*)pet)->isPet())
+ ((Pet*)pet)->CheckLearning(spellid);
+
+ unit_target = spell->m_targets.getUnitTarget();
+
+ //10% chance to play special pet attack talk, else growl
+ //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
+ if(((Creature*)pet)->isPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10))
+ pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
+ else
+ {
+ pet->SendPetAIReaction(guid1);
+ }
+
+ if( unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS))
+ {
+ pet->clearUnitState(UNIT_STAT_FOLLOW);
+ if(pet->getVictim())
+ pet->AttackStop();
+ pet->GetMotionMaster()->Clear();
+ if (((Creature*)pet)->AI())
+ ((Creature*)pet)->AI()->AttackStart(unit_target);
+ }
+
+ spell->prepare(&(spell->m_targets));
+ }
+ else
+ {
+ if(pet->HasAuraType(SPELL_AURA_MOD_POSSESS))
+ {
+ WorldPacket data(SMSG_CAST_FAILED, (4+1+1));
+ data << uint32(spellid) << uint8(2) << uint8(result);
+ switch (result)
+ {
+ case SPELL_FAILED_REQUIRES_SPELL_FOCUS:
+ data << uint32(spellInfo->RequiresSpellFocus);
+ break;
+ case SPELL_FAILED_REQUIRES_AREA:
+ data << uint32(spellInfo->AreaId);
+ break;
+ }
+ SendPacket(&data);
+ }
+ else
+ pet->SendPetCastFail(spellid, result);
+
+ if(!((Creature*)pet)->HasSpellCooldown(spellid))
+ pet->SendPetClearCooldown(spellid);
+
+ spell->finish(false);
+ delete spell;
+ }
+ break;
+ }
+ default:
+ sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid);
+ }
+}
+
+void WorldSession::HandlePetNameQuery( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+8);
+
+ sLog.outDetail( "HandlePetNameQuery. CMSG_PET_NAME_QUERY\n" );
+
+ uint32 petnumber;
+ uint64 petguid;
+
+ recv_data >> petnumber;
+ recv_data >> petguid;
+
+ SendPetNameQuery(petguid,petnumber);
+}
+
+void WorldSession::SendPetNameQuery( uint64 petguid, uint32 petnumber)
+{
+ Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, petguid);
+ if(!pet || !pet->GetCharmInfo() || pet->GetCharmInfo()->GetPetNumber() != petnumber)
+ return;
+
+ std::string name = pet->GetName();
+
+ WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+4+name.size()+1));
+ data << uint32(petnumber);
+ data << name.c_str();
+ data << uint32(pet->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP));
+
+ if( pet->isPet() && ((Pet*)pet)->GetDeclinedNames() )
+ {
+ data << uint8(1);
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ data << ((Pet*)pet)->GetDeclinedNames()->name[i];
+ }
+ else
+ data << uint8(0);
+
+ _player->GetSession()->SendPacket(&data);
+}
+
+void WorldSession::HandlePetSetAction( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+2+2);
+
+ sLog.outDetail( "HandlePetSetAction. CMSG_PET_SET_ACTION\n" );
+
+ uint64 petguid;
+ uint32 position;
+ uint16 spell_id;
+ uint16 act_state;
+ uint8 count;
+
+ recv_data >> petguid;
+
+ // FIXME: charmed case
+ //Pet* pet = ObjectAccessor::Instance().GetPet(petguid);
+ if(ObjectAccessor::FindPlayer(petguid))
+ return;
+
+ Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, petguid);
+
+ if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm()))
+ {
+ sLog.outError( "HandlePetSetAction: Unknown pet or pet owner.\n" );
+ return;
+ }
+
+ CharmInfo *charmInfo = pet->GetCharmInfo();
+ if(!charmInfo)
+ {
+ sLog.outError("WorldSession::HandlePetSetAction: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID());
+ return;
+ }
+
+ count = (recv_data.size() == 24) ? 2 : 1;
+ for(uint8 i = 0; i < count; i++)
+ {
+ recv_data >> position;
+ recv_data >> spell_id;
+ recv_data >> act_state;
+
+ sLog.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X\n", _player->GetName(), position, spell_id, act_state);
+
+ //if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add
+ if(!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_CAST) && spell_id && !pet->HasSpell(spell_id)))
+ {
+ //sign for autocast
+ if(act_state == ACT_ENABLED && spell_id)
+ {
+ if(pet->isCharmed())
+ charmInfo->ToggleCreatureAutocast(spell_id, true);
+ else
+ ((Pet*)pet)->ToggleAutocast(spell_id, true);
+ }
+ //sign for no/turn off autocast
+ else if(act_state == ACT_DISABLED && spell_id)
+ {
+ if(pet->isCharmed())
+ charmInfo->ToggleCreatureAutocast(spell_id, false);
+ else
+ ((Pet*)pet)->ToggleAutocast(spell_id, false);
+ }
+
+ charmInfo->GetActionBarEntry(position)->Type = act_state;
+ charmInfo->GetActionBarEntry(position)->SpellOrAction = spell_id;
+ }
+ }
+}
+
+void WorldSession::HandlePetRename( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+1+1+1+1+1+1+1);
+
+ sLog.outDetail( "HandlePetRename. CMSG_PET_RENAME\n" );
+
+ uint64 petguid;
+ uint8 isdeclined;
+
+ std::string name;
+ DeclinedName declinedname;
+
+ recv_data >> petguid;
+ recv_data >> name;
+ recv_data >> isdeclined;
+
+ Pet* pet = ObjectAccessor::GetPet(petguid);
+ // check it!
+ if( !pet || !pet->isPet() || ((Pet*)pet)->getPetType()!= HUNTER_PET ||
+ pet->GetByteValue(UNIT_FIELD_BYTES_2, 2) != UNIT_RENAME_ALLOWED ||
+ pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo() )
+ return;
+
+ if((!ObjectMgr::IsValidPetName(name)) || (objmgr.IsReservedName(name)))
+ {
+ SendNotification(LANG_PET_INVALID_NAME);
+ return;
+ }
+ pet->SetName(name);
+
+ Unit *owner = pet->GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_NAME);
+
+ pet->SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED);
+
+ if(isdeclined)
+ {
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ recv_data >> declinedname.name[i];
+
+ std::wstring wname;
+ Utf8toWStr(name,wname);
+ if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname,0),declinedname))
+ {
+ SendNotification(LANG_PET_INVALID_NAME);
+ return;
+ }
+ }
+
+ CharacterDatabase.BeginTransaction();
+ if(isdeclined)
+ {
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ CharacterDatabase.escape_string(declinedname.name[i]);
+ CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber());
+ CharacterDatabase.PExecute("INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%u','%s','%s','%s','%s','%s')",
+ pet->GetCharmInfo()->GetPetNumber(), _player->GetGUIDLow(), declinedname.name[0].c_str(),declinedname.name[1].c_str(),declinedname.name[2].c_str(),declinedname.name[3].c_str(),declinedname.name[4].c_str());
+ }
+
+ CharacterDatabase.escape_string(name);
+ CharacterDatabase.PExecute("UPDATE character_pet SET name = '%s', renamed = '1' WHERE owner = '%u' AND id = '%u'", name.c_str(),_player->GetGUIDLow(),pet->GetCharmInfo()->GetPetNumber() );
+ CharacterDatabase.CommitTransaction();
+
+ pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
+}
+
+void WorldSession::HandlePetAbandon( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+ recv_data >> guid; //pet guid
+ sLog.outDetail( "HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid) );
+
+ // pet/charmed
+ Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player, guid);
+ if(pet)
+ {
+ if(pet->isPet())
+ {
+ if(pet->GetGUID() == _player->GetPetGUID())
+ {
+ uint32 feelty = pet->GetPower(POWER_HAPPINESS);
+ pet->SetPower(POWER_HAPPINESS ,(feelty-50000) > 0 ?(feelty-50000) : 0);
+ }
+
+ _player->RemovePet((Pet*)pet,PET_SAVE_AS_DELETED);
+ }
+ else if(pet->GetGUID() == _player->GetCharmGUID())
+ {
+ _player->Uncharm();
+ }
+ }
+}
+
+void WorldSession::HandlePetUnlearnOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,8);
+
+ sLog.outDetail("CMSG_PET_UNLEARN");
+ uint64 guid;
+ recvPacket >> guid;
+
+ Pet* pet = _player->GetPet();
+
+ if(!pet || pet->getPetType() != HUNTER_PET || pet->m_spells.size() <= 1)
+ return;
+
+ if(guid != pet->GetGUID())
+ {
+ sLog.outError( "HandlePetUnlearnOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
+ return;
+ }
+
+ CharmInfo *charmInfo = pet->GetCharmInfo();
+ if(!charmInfo)
+ {
+ sLog.outError("WorldSession::HandlePetUnlearnOpcode: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID());
+ return;
+ }
+
+ uint32 cost = pet->resetTalentsCost();
+
+ if (GetPlayer()->GetMoney() < cost)
+ {
+ GetPlayer()->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
+ return;
+ }
+
+ for(PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end();)
+ {
+ uint32 spell_id = itr->first; // Pet::removeSpell can invalidate iterator at erase NEW spell
+ ++itr;
+ pet->removeSpell(spell_id);
+ }
+
+ pet->SetTP(pet->getLevel() * (pet->GetLoyaltyLevel() - 1));
+
+ for(uint8 i = 0; i < 10; i++)
+ {
+ if(charmInfo->GetActionBarEntry(i)->SpellOrAction && charmInfo->GetActionBarEntry(i)->Type == ACT_ENABLED || charmInfo->GetActionBarEntry(i)->Type == ACT_DISABLED)
+ charmInfo->GetActionBarEntry(i)->SpellOrAction = 0;
+ }
+
+ // relearn pet passives
+ pet->LearnPetPassives();
+
+ pet->m_resetTalentsTime = time(NULL);
+ pet->m_resetTalentsCost = cost;
+ GetPlayer()->ModifyMoney(-(int32)cost);
+
+ GetPlayer()->PetSpellInitialize();
+}
+
+void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket )
+{
+ CHECK_PACKET_SIZE(recvPacket,8+2+2+1);
+
+ sLog.outDetail("CMSG_PET_SPELL_AUTOCAST");
+ uint64 guid;
+ uint16 spellid;
+ uint16 spellid2; //maybe second spell, automatically toggled off when first toggled on?
+ uint8 state; //1 for on, 0 for off
+ recvPacket >> guid >> spellid >> spellid2 >> state;
+
+ if(!_player->GetPet() && !_player->GetCharm())
+ return;
+
+ if(ObjectAccessor::FindPlayer(guid))
+ return;
+
+ Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid);
+
+ if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm()))
+ {
+ sLog.outError( "HandlePetSpellAutocastOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
+ return;
+ }
+
+ // do not add not learned spells/ passive spells
+ if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid))
+ return;
+
+ CharmInfo *charmInfo = pet->GetCharmInfo();
+ if(!charmInfo)
+ {
+ sLog.outError("WorldSession::HandlePetSpellAutocastOpcod: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID());
+ return;
+ }
+
+ if(pet->isCharmed())
+ //state can be used as boolean
+ pet->GetCharmInfo()->ToggleCreatureAutocast(spellid, state);
+ else
+ ((Pet*)pet)->ToggleAutocast(spellid, state);
+
+ for(uint8 i = 0; i < 10; ++i)
+ {
+ if((charmInfo->GetActionBarEntry(i)->Type == ACT_ENABLED || charmInfo->GetActionBarEntry(i)->Type == ACT_DISABLED) && spellid == charmInfo->GetActionBarEntry(i)->SpellOrAction)
+ charmInfo->GetActionBarEntry(i)->Type = state ? ACT_ENABLED : ACT_DISABLED;
+ }
+}
+
+void WorldSession::HandleAddDynamicTargetObsoleteOpcode( WorldPacket& recvPacket )
+{
+ sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL");
+
+ CHECK_PACKET_SIZE(recvPacket,8+4);
+ uint64 guid;
+ uint32 spellid;
+
+ recvPacket >> guid >> spellid;
+
+ if(!_player->GetPet() && !_player->GetCharm())
+ return;
+
+ if(ObjectAccessor::FindPlayer(guid))
+ return;
+
+ Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid);
+
+ if(!pet || (pet != _player->GetPet() && pet!= _player->GetCharm()))
+ {
+ sLog.outError( "HandleAddDynamicTargetObsoleteOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
+ return;
+ }
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown PET spell id %i\n", spellid);
+ return;
+ }
+
+ // do not cast not learned spells
+ if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid))
+ return;
+
+ SpellCastTargets targets;
+ if(!targets.read(&recvPacket,pet))
+ return;
+
+ pet->clearUnitState(UNIT_STAT_FOLLOW);
+
+ Spell *spell = new Spell(pet, spellInfo, false);
+ spell->m_targets = targets;
+
+ int16 result = spell->PetCanCast(NULL);
+ if(result == -1)
+ {
+ pet->AddCreatureSpellCooldown(spellid);
+ if(pet->isPet())
+ {
+ Pet* p = (Pet*)pet;
+ p->CheckLearning(spellid);
+ //10% chance to play special pet attack talk, else growl
+ //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
+ if(p->getPetType() == SUMMON_PET && (urand(0, 100) < 10))
+ pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
+ else
+ pet->SendPetAIReaction(guid);
+ }
+
+ spell->prepare(&(spell->m_targets));
+ }
+ else
+ {
+ pet->SendPetCastFail(spellid, result);
+ if(!pet->HasSpellCooldown(spellid))
+ pet->SendPetClearCooldown(spellid);
+
+ spell->finish(false);
+ delete spell;
+ }
+}
diff --git a/src/game/PetitionsHandler.cpp b/src/game/PetitionsHandler.cpp index 6957634dc9e..2c5c4493c10 100644 --- a/src/game/PetitionsHandler.cpp +++ b/src/game/PetitionsHandler.cpp @@ -1,936 +1,936 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "Common.h" -#include "Language.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "World.h" -#include "ObjectMgr.h" -#include "Log.h" -#include "Opcodes.h" -#include "Guild.h" -#include "ArenaTeam.h" -#include "MapManager.h" -#include "GossipDef.h" -#include "SocialMgr.h" - -/*enum PetitionType // dbc data -{ - PETITION_TYPE_GUILD = 1, - PETITION_TYPE_ARENA_TEAM = 3 -};*/ - -// Charters ID in item_template -#define GUILD_CHARTER 5863 -#define GUILD_CHARTER_COST 1000 // 10 S -#define ARENA_TEAM_CHARTER_2v2 23560 -#define ARENA_TEAM_CHARTER_2v2_COST 800000 // 80 G -#define ARENA_TEAM_CHARTER_3v3 23561 -#define ARENA_TEAM_CHARTER_3v3_COST 1200000 // 120 G -#define ARENA_TEAM_CHARTER_5v5 23562 -#define ARENA_TEAM_CHARTER_5v5_COST 2000000 // 200 G - -void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data, 8+8+4+1+5*8+2+1+4+4); - - sLog.outDebug("Received opcode CMSG_PETITION_BUY"); - //recv_data.hexlike(); - - uint64 guidNPC; - uint64 unk1, unk3, unk4, unk5, unk6, unk7; - uint32 unk2; - std::string name; - uint16 unk8; - uint8 unk9; - uint32 unk10; // selected index - uint32 unk11; - recv_data >> guidNPC; // NPC GUID - recv_data >> unk1; // 0 - recv_data >> unk2; // 0 - recv_data >> name; // name - - // recheck - CHECK_PACKET_SIZE(recv_data, 8+8+4+(name.size()+1)+5*8+2+1+4+4); - - recv_data >> unk3; // 0 - recv_data >> unk4; // 0 - recv_data >> unk5; // 0 - recv_data >> unk6; // 0 - recv_data >> unk7; // 0 - recv_data >> unk8; // 0 - recv_data >> unk9; // 0 - recv_data >> unk10; // index - recv_data >> unk11; // 0 - sLog.outDebug("Petitioner with GUID %u tried sell petition: name %s", GUID_LOPART(guidNPC), name.c_str()); - - // prevent cheating - Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guidNPC,UNIT_NPC_FLAG_PETITIONER); - if (!pCreature) - { - sLog.outDebug("WORLD: HandlePetitionBuyOpcode - Unit (GUID: %u) not found or you can't interact with him.", GUID_LOPART(guidNPC)); - return; - } - - // remove fake death - if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - uint32 charterid = 0; - uint32 cost = 0; - uint32 type = 0; - if(pCreature->isTabardDesigner()) - { - // if tabard designer, then trying to buy a guild charter. - // do not let if already in guild. - if(_player->GetGuildId()) - return; - - charterid = GUILD_CHARTER; - cost = GUILD_CHARTER_COST; - type = 9; - } - else - { - // TODO: find correct opcode - if(_player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) - { - SendNotification(GetMangosString(LANG_ARENA_ONE_TOOLOW), 70); - return; - } - - for(uint8 i = 0; i < MAX_ARENA_SLOT; i++) - { - if(_player->GetArenaTeamId(i) && (i == (unk10-1))) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM); - return; - } - } - switch(unk10) - { - case 1: - charterid = ARENA_TEAM_CHARTER_2v2; - cost = ARENA_TEAM_CHARTER_2v2_COST; - type = 2; // 2v2 - break; - case 2: - charterid = ARENA_TEAM_CHARTER_3v3; - cost = ARENA_TEAM_CHARTER_3v3_COST; - type = 3; // 3v3 - break; - case 3: - charterid = ARENA_TEAM_CHARTER_5v5; - cost = ARENA_TEAM_CHARTER_5v5_COST; - type = 5; // 5v5 - break; - default: - sLog.outDebug("unknown selection at buy petition: %u", unk10); - return; - } - } - - if(type == 9) - { - if(objmgr.GetGuildByName(name)) - { - SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_EXISTS); - return; - } - if(objmgr.IsReservedName(name)) - { - SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_INVALID); - return; - } - if(!ObjectMgr::IsValidCharterName(name)) - { - SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_INVALID); - return; - } - } - else - { - if(objmgr.GetArenaTeamByName(name)) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S); - return; - } - if(objmgr.IsReservedName(name)) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_INVALID); - return; - } - if(!ObjectMgr::IsValidCharterName(name)) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_INVALID); - return; - } - } - - ItemPrototype const *pProto = objmgr.GetItemPrototype(charterid); - if(!pProto) - { - _player->SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, charterid, 0); - return; - } - - if(_player->GetMoney() < cost) - { //player hasn't got enough money - _player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, charterid, 0); - return; - } - - ItemPosCountVec dest; - uint8 msg = _player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, charterid, pProto->BuyCount ); - if(msg != EQUIP_ERR_OK) - { - _player->SendBuyError(msg, pCreature, charterid, 0); - return; - } - - _player->ModifyMoney(-(int32)cost); - Item *charter = _player->StoreNewItem(dest, charterid, true); - if(!charter) - return; - - charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT, charter->GetGUIDLow()); - // ITEM_FIELD_ENCHANTMENT is guild/arenateam id - // ITEM_FIELD_ENCHANTMENT+1 is current signatures count (showed on item) - charter->SetState(ITEM_CHANGED, _player); - _player->SendNewItem(charter, 1, true, false); - - // a petition is invalid, if both the owner and the type matches - QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE ownerguid = '%u' AND type = '%u'", _player->GetGUIDLow(), type); - - std::ostringstream ssInvalidPetitionGUIDs; - - if (result) - { - - do - { - Field *fields = result->Fetch(); - ssInvalidPetitionGUIDs << "'" << fields[0].GetUInt32() << "' , "; - } while (result->NextRow()); - - delete result; - } - - // delete petitions with the same guid as this one - ssInvalidPetitionGUIDs << "'" << charter->GetGUIDLow() << "'"; - - sLog.outDebug("Invalid petition GUIDs: %s", ssInvalidPetitionGUIDs.str().c_str()); - CharacterDatabase.escape_string(name); - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM petition WHERE petitionguid IN ( %s )", ssInvalidPetitionGUIDs.str().c_str()); - CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE petitionguid IN ( %s )", ssInvalidPetitionGUIDs.str().c_str()); - CharacterDatabase.PExecute("INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES ('%u', '%u', '%s', '%u')", - _player->GetGUIDLow(), charter->GetGUIDLow(), name.c_str(), type); - CharacterDatabase.CommitTransaction(); -} - -void WorldSession::HandlePetitionShowSignOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data, 8); - - // ok - sLog.outDebug("Received opcode CMSG_PETITION_SHOW_SIGNATURES"); - //recv_data.hexlike(); - - uint8 signs = 0; - uint64 petitionguid; - recv_data >> petitionguid; // petition guid - - // solve (possible) some strange compile problems with explicit use GUID_LOPART(petitionguid) at some GCC versions (wrong code optimization in compiler?) - uint32 petitionguid_low = GUID_LOPART(petitionguid); - - QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid, type FROM petition WHERE petitionguid = '%u'", petitionguid_low); - if(!result) - { - sLog.outError("any petition on server..."); - return; - } - Field *fields = result->Fetch(); - uint32 type = fields[1].GetUInt32(); - delete result; - // if guild petition and has guild => error, return; - if(type==9 && _player->GetGuildId()) - return; - - result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", petitionguid_low); - - // result==NULL also correct in case no sign yet - if(result) - signs = result->GetRowCount(); - - sLog.outDebug("CMSG_PETITION_SHOW_SIGNATURES petition entry: '%u'", petitionguid_low); - - WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8+8+4+1+signs*12)); - data << petitionguid; // petition guid - data << _player->GetGUID(); // owner guid - data << petitionguid_low; // guild guid (in mangos always same as GUID_LOPART(petitionguid) - data << signs; // sign's count - - for(uint8 i = 1; i <= signs; i++) - { - Field *fields = result->Fetch(); - uint64 plguid = fields[0].GetUInt64(); - - data << plguid; // Player GUID - data << (uint32)0; // there 0 ... - - result->NextRow(); - } - delete result; - SendPacket(&data); -} - -void WorldSession::HandlePetitionQueryOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data, 4+8); - - sLog.outDebug("Received opcode CMSG_PETITION_QUERY"); // ok - //recv_data.hexlike(); - - uint32 guildguid; - uint64 petitionguid; - recv_data >> guildguid; // in mangos always same as GUID_LOPART(petitionguid) - recv_data >> petitionguid; // petition guid - sLog.outDebug("CMSG_PETITION_QUERY Petition GUID %u Guild GUID %u", GUID_LOPART(petitionguid), guildguid); - - SendPetitionQueryOpcode(petitionguid); -} - -void WorldSession::SendPetitionQueryOpcode(uint64 petitionguid) -{ - uint64 ownerguid = 0; - uint32 type; - std::string name = "NO_NAME_FOR_GUID"; - uint8 signs = 0; - - QueryResult *result = CharacterDatabase.PQuery( - "SELECT ownerguid, name, " - " (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = '%u') AS signs " - "FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid), GUID_LOPART(petitionguid)); - - if(result) - { - Field* fields = result->Fetch(); - ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER); - name = fields[1].GetCppString(); - signs = fields[2].GetUInt8(); - delete result; - } - else - { - sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid)); - return; - } - - QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); - - if(result2) - { - Field* fields = result2->Fetch(); - type = fields[0].GetUInt32(); - delete result2; - } - else - { - sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid)); - return; - } - - WorldPacket data(SMSG_PETITION_QUERY_RESPONSE, (4+8+name.size()+1+1+4*13)); - data << GUID_LOPART(petitionguid); // guild/team guid (in mangos always same as GUID_LOPART(petition guid) - data << ownerguid; // charter owner guid - data << name; // name (guild/arena team) - data << uint8(0); // 1 - if(type == 9) - { - data << uint32(9); - data << uint32(9); - data << uint32(0); // bypass client - side limitation, a different value is needed here for each petition - } - else - { - data << type-1; - data << type-1; - data << type; // bypass client - side limitation, a different value is needed here for each petition - } - data << uint32(0); // 5 - data << uint32(0); // 6 - data << uint32(0); // 7 - data << uint32(0); // 8 - data << uint16(0); // 9 2 bytes field - data << uint32(0); // 10 - data << uint32(0); // 11 - data << uint32(0); // 13 count of next strings? - data << uint32(0); // 14 - if(type == 9) - data << uint32(0); // 15 0 - guild, 1 - arena team - else - data << uint32(1); - SendPacket(&data); -} - -void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data, 8+1); - - sLog.outDebug("Received opcode MSG_PETITION_RENAME"); // ok - //recv_data.hexlike(); - - uint64 petitionguid; - uint32 type; - std::string newname; - - recv_data >> petitionguid; // guid - recv_data >> newname; // new name - - Item *item = _player->GetItemByGuid(petitionguid); - if(!item) - return; - - QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); - - if(result2) - { - Field* fields = result2->Fetch(); - type = fields[0].GetUInt32(); - delete result2; - } - else - { - sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid)); - return; - } - - if(type == 9) - { - if(objmgr.GetGuildByName(newname)) - { - SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_EXISTS); - return; - } - if(objmgr.IsReservedName(newname)) - { - SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_INVALID); - return; - } - if(!ObjectMgr::IsValidCharterName(newname)) - { - SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_INVALID); - return; - } - } - else - { - if(objmgr.GetArenaTeamByName(newname)) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_EXISTS_S); - return; - } - if(objmgr.IsReservedName(newname)) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_INVALID); - return; - } - if(!ObjectMgr::IsValidCharterName(newname)) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_INVALID); - return; - } - } - - std::string db_newname = newname; - CharacterDatabase.escape_string(db_newname); - CharacterDatabase.PExecute("UPDATE petition SET name = '%s' WHERE petitionguid = '%u'", - db_newname.c_str(), GUID_LOPART(petitionguid)); - - sLog.outDebug("Petition (GUID: %u) renamed to '%s'", GUID_LOPART(petitionguid), newname.c_str()); - WorldPacket data(MSG_PETITION_RENAME, (8+newname.size()+1)); - data << petitionguid; - data << newname; - SendPacket(&data); -} - -void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data, 8+1); - - sLog.outDebug("Received opcode CMSG_PETITION_SIGN"); // ok - //recv_data.hexlike(); - - Field *fields; - uint64 petitionguid; - uint32 type; - uint8 unk; - uint64 ownerguid; - recv_data >> petitionguid; // petition guid - recv_data >> unk; - - uint8 signs = 0; - - QueryResult *result = CharacterDatabase.PQuery( - "SELECT ownerguid, " - " (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = '%u') AS signs " - "FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid), GUID_LOPART(petitionguid)); - - if(!result) - { - sLog.outError("any petition on server..."); - return; - } - - fields = result->Fetch(); - ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER); - signs = fields[1].GetUInt8(); - - delete result; - - uint32 plguidlo = _player->GetGUIDLow(); - if(GUID_LOPART(ownerguid) == plguidlo) - return; - - // not let enemies sign guild charter - if(!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != objmgr.GetPlayerTeamByGUID(ownerguid)) - return; - - QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); - - if(result2) - { - Field* fields = result2->Fetch(); - type = fields[0].GetUInt32(); - delete result2; - } - else - { - sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid)); - return; - } - - if(type != 9 && _player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) - { - // player is too low level to join an arena team - SendNotification("You must be level %u to join an arena team!",sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)); - return; - } - - signs += 1; - if(signs > type) // client signs maximum - return; - - //client doesn't allow to sign petition two times by one character, but not check sign by another character from same account - //not allow sign another player from already sign player account - result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE player_account = '%u' AND petitionguid = '%u'", GetAccountId(), GUID_LOPART(petitionguid)); - - if(result) - { - delete result; - WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8+8+4)); - data << petitionguid; - data << _player->GetGUID(); - data << (uint32)PETITION_SIGN_ALREADY_SIGNED; - - // close at signer side - SendPacket(&data); - - // update for owner if online - if(Player *owner = objmgr.GetPlayer(ownerguid)) - owner->GetSession()->SendPacket(&data); - return; - } - - CharacterDatabase.PExecute("INSERT INTO petition_sign (ownerguid,petitionguid, playerguid, player_account) VALUES ('%u', '%u', '%u','%u')", GUID_LOPART(ownerguid),GUID_LOPART(petitionguid), plguidlo,GetAccountId()); - - sLog.outDebug("PETITION SIGN: GUID %u by player: %s (GUID: %u Account: %u)", GUID_LOPART(petitionguid), _player->GetName(),plguidlo,GetAccountId()); - - WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8+8+4)); - data << petitionguid; - data << _player->GetGUID(); - data << (uint32)PETITION_SIGN_OK; - - // close at signer side - SendPacket(&data); - - // update signs count on charter, required testing... - //Item *item = _player->GetItemByGuid(petitionguid)); - //if(item) - // item->SetUInt32Value(ITEM_FIELD_ENCHANTMENT+1, signs); - - // update for owner if online - if(Player *owner = objmgr.GetPlayer(ownerguid)) - owner->GetSession()->SendPacket(&data); -} - -void WorldSession::HandlePetitionDeclineOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data, 8); - - sLog.outDebug("Received opcode MSG_PETITION_DECLINE"); // ok - //recv_data.hexlike(); - - uint64 petitionguid; - uint64 ownerguid; - recv_data >> petitionguid; // petition guid - sLog.outDebug("Petition %u declined by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow()); - - QueryResult *result = CharacterDatabase.PQuery("SELECT ownerguid FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); - if(!result) - return; - - Field *fields = result->Fetch(); - ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER); - delete result; - - Player *owner = objmgr.GetPlayer(ownerguid); - if(owner) // petition owner online - { - WorldPacket data(MSG_PETITION_DECLINE, 8); - data << _player->GetGUID(); - owner->GetSession()->SendPacket(&data); - } -} - -void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data, 4+8+8); - - sLog.outDebug("Received opcode CMSG_OFFER_PETITION"); // ok - //recv_data.hexlike(); - - uint8 signs = 0; - uint64 petitionguid, plguid; - uint32 petitiontype; - Player *player; - recv_data >> petitiontype; // 2.0.8 - petition type? - recv_data >> petitionguid; // petition guid - recv_data >> plguid; // player guid - sLog.outDebug("OFFER PETITION: type %u, GUID1 %u, to player id: %u", petitiontype, GUID_LOPART(petitionguid), GUID_LOPART(plguid)); - - player = ObjectAccessor::FindPlayer(plguid); - if(!player || player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow())) - return; - - // not let offer to enemies - if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != player->GetTeam() ) - return; - - QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); - if(!result) - { - sLog.outError("any petition on server..."); - return; - } - - delete result; - - result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); - // result==NULL also correct charter without signs - if(result) - signs = result->GetRowCount(); - - WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8+8+4+signs+signs*12)); - data << petitionguid; // petition guid - data << _player->GetGUID(); // owner guid - data << GUID_LOPART(petitionguid); // guild guid (in mangos always same as GUID_LOPART(petition guid) - data << signs; // sign's count - - for(uint8 i = 1; i <= signs; i++) - { - Field *fields = result->Fetch(); - uint64 plguid = fields[0].GetUInt64(); - - data << plguid; // Player GUID - data << (uint32)0; // there 0 ... - - result->NextRow(); - } - - delete result; - player->GetSession()->SendPacket(&data); -} - -void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data, 8); - - sLog.outDebug("Received opcode CMSG_TURN_IN_PETITION"); // ok - //recv_data.hexlike(); - - WorldPacket data; - uint64 petitionguid; - - uint32 ownerguidlo; - uint32 type; - std::string name; - - recv_data >> petitionguid; - - sLog.outDebug("Petition %u turned in by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow()); - - // data - QueryResult *result = CharacterDatabase.PQuery("SELECT ownerguid, name, type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); - if(result) - { - Field *fields = result->Fetch(); - ownerguidlo = fields[0].GetUInt32(); - name = fields[1].GetCppString(); - type = fields[2].GetUInt32(); - delete result; - } - else - { - sLog.outError("petition table has broken data!"); - return; - } - - if(type == 9) - { - if(_player->GetGuildId()) - { - data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4); - data << (uint32)PETITION_TURN_ALREADY_IN_GUILD; // already in guild - _player->GetSession()->SendPacket(&data); - return; - } - } - else - { - uint8 slot = ArenaTeam::GetSlotByType(type); - if(slot >= MAX_ARENA_SLOT) - return; - - if(_player->GetArenaTeamId(slot)) - { - //data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4); - //data << (uint32)PETITION_TURN_ALREADY_IN_GUILD; // already in guild - //_player->GetSession()->SendPacket(&data); - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM); - return; - } - } - - if(_player->GetGUIDLow() != ownerguidlo) - return; - - // signs - uint8 signs; - result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); - if(result) - signs = result->GetRowCount(); - else - signs = 0; - - uint32 count; - //if(signs < sWorld.getConfig(CONFIG_MIN_PETITION_SIGNS)) - if(type == 9) - count = sWorld.getConfig(CONFIG_MIN_PETITION_SIGNS); - else - count = type-1; - if(signs < count) - { - data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4); - data << (uint32)PETITION_TURN_NEED_MORE_SIGNATURES; // need more signatures... - SendPacket(&data); - delete result; - return; - } - - if(type == 9) - { - if(objmgr.GetGuildByName(name)) - { - SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_EXISTS); - delete result; - return; - } - } - else - { - if(objmgr.GetArenaTeamByName(name)) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S); - delete result; - return; - } - } - - // and at last charter item check - Item *item = _player->GetItemByGuid(petitionguid); - if(!item) - { - delete result; - return; - } - - // OK! - - // delete charter item - _player->DestroyItem(item->GetBagSlot(),item->GetSlot(), true); - - if(type == 9) // create guild - { - Guild* guild = new Guild; - if(!guild->create(_player->GetGUID(), name)) - { - delete guild; - delete result; - return; - } - - // register guild and add guildmaster - objmgr.AddGuild(guild); - - // add members - for(uint8 i = 0; i < signs; ++i) - { - Field* fields = result->Fetch(); - guild->AddMember(fields[0].GetUInt64(), guild->GetLowestRank()); - result->NextRow(); - } - } - else // or arena team - { - ArenaTeam* at = new ArenaTeam; - if(!at->create(_player->GetGUID(), type, name)) - { - sLog.outError("PetitionsHandler: arena team create failed."); - delete at; - delete result; - return; - } - - CHECK_PACKET_SIZE(recv_data, 8+5*4); - uint32 icon, iconcolor, border, bordercolor, backgroud; - recv_data >> backgroud >> icon >> iconcolor >> border >> bordercolor; - - at->SetEmblem(backgroud, icon, iconcolor, border, bordercolor); - - // register team and add captain - objmgr.AddArenaTeam(at); - sLog.outDebug("PetitonsHandler: arena team added to objmrg"); - - // add members - for(uint8 i = 0; i < signs; ++i) - { - Field* fields = result->Fetch(); - sLog.outDebug("PetitionsHandler: adding arena member %u", fields[0].GetUInt64()); - at->AddMember(fields[0].GetUInt64()); - result->NextRow(); - } - } - - delete result; - - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); - CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); - CharacterDatabase.CommitTransaction(); - - // created - sLog.outDebug("TURN IN PETITION GUID %u", GUID_LOPART(petitionguid)); - - data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4); - data << (uint32)PETITION_TURN_OK; - SendPacket(&data); -} - -void WorldSession::HandlePetitionShowListOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data, 8); - - sLog.outDebug("Received CMSG_PETITION_SHOWLIST"); // ok - //recv_data.hexlike(); - - uint64 guid; - recv_data >> guid; - - SendPetitionShowList(guid); -} - -void WorldSession::SendPetitionShowList(uint64 guid) -{ - Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_PETITIONER); - if (!pCreature) - { - sLog.outDebug("WORLD: HandlePetitionShowListOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid))); - return; - } - - // remove fake death - if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - uint8 count = 0; - if(pCreature->isTabardDesigner()) - count = 1; - else - count = 3; - - WorldPacket data(SMSG_PETITION_SHOWLIST, 8+1+4*6); - data << guid; // npc guid - data << count; // count - if(count == 1) - { - data << uint32(1); // index - data << uint32(GUILD_CHARTER); // charter entry - data << uint32(16161); // charter display id - data << uint32(GUILD_CHARTER_COST); // charter cost - data << uint32(0); // unknown - data << uint32(9); // required signs? - } - else - { - // 2v2 - data << uint32(1); // index - data << uint32(ARENA_TEAM_CHARTER_2v2); // charter entry - data << uint32(16161); // charter display id - data << uint32(ARENA_TEAM_CHARTER_2v2_COST); // charter cost - data << uint32(2); // unknown - data << uint32(2); // required signs? - // 3v3 - data << uint32(2); // index - data << uint32(ARENA_TEAM_CHARTER_3v3); // charter entry - data << uint32(16161); // charter display id - data << uint32(ARENA_TEAM_CHARTER_3v3_COST); // charter cost - data << uint32(3); // unknown - data << uint32(3); // required signs? - // 5v5 - data << uint32(3); // index - data << uint32(ARENA_TEAM_CHARTER_5v5); // charter entry - data << uint32(16161); // charter display id - data << uint32(ARENA_TEAM_CHARTER_5v5_COST); // charter cost - data << uint32(5); // unknown - data << uint32(5); // required signs? - } - //for(uint8 i = 0; i < count; i++) - //{ - // data << uint32(i); // index - // data << uint32(GUILD_CHARTER); // charter entry - // data << uint32(16161); // charter display id - // data << uint32(GUILD_CHARTER_COST+i); // charter cost - // data << uint32(0); // unknown - // data << uint32(9); // required signs? - //} - SendPacket(&data); - sLog.outDebug("Sent SMSG_PETITION_SHOWLIST"); -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Language.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "Guild.h"
+#include "ArenaTeam.h"
+#include "MapManager.h"
+#include "GossipDef.h"
+#include "SocialMgr.h"
+
+/*enum PetitionType // dbc data
+{
+ PETITION_TYPE_GUILD = 1,
+ PETITION_TYPE_ARENA_TEAM = 3
+};*/
+
+// Charters ID in item_template
+#define GUILD_CHARTER 5863
+#define GUILD_CHARTER_COST 1000 // 10 S
+#define ARENA_TEAM_CHARTER_2v2 23560
+#define ARENA_TEAM_CHARTER_2v2_COST 800000 // 80 G
+#define ARENA_TEAM_CHARTER_3v3 23561
+#define ARENA_TEAM_CHARTER_3v3_COST 1200000 // 120 G
+#define ARENA_TEAM_CHARTER_5v5 23562
+#define ARENA_TEAM_CHARTER_5v5_COST 2000000 // 200 G
+
+void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8+8+4+1+5*8+2+1+4+4);
+
+ sLog.outDebug("Received opcode CMSG_PETITION_BUY");
+ //recv_data.hexlike();
+
+ uint64 guidNPC;
+ uint64 unk1, unk3, unk4, unk5, unk6, unk7;
+ uint32 unk2;
+ std::string name;
+ uint16 unk8;
+ uint8 unk9;
+ uint32 unk10; // selected index
+ uint32 unk11;
+ recv_data >> guidNPC; // NPC GUID
+ recv_data >> unk1; // 0
+ recv_data >> unk2; // 0
+ recv_data >> name; // name
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, 8+8+4+(name.size()+1)+5*8+2+1+4+4);
+
+ recv_data >> unk3; // 0
+ recv_data >> unk4; // 0
+ recv_data >> unk5; // 0
+ recv_data >> unk6; // 0
+ recv_data >> unk7; // 0
+ recv_data >> unk8; // 0
+ recv_data >> unk9; // 0
+ recv_data >> unk10; // index
+ recv_data >> unk11; // 0
+ sLog.outDebug("Petitioner with GUID %u tried sell petition: name %s", GUID_LOPART(guidNPC), name.c_str());
+
+ // prevent cheating
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guidNPC,UNIT_NPC_FLAG_PETITIONER);
+ if (!pCreature)
+ {
+ sLog.outDebug("WORLD: HandlePetitionBuyOpcode - Unit (GUID: %u) not found or you can't interact with him.", GUID_LOPART(guidNPC));
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ uint32 charterid = 0;
+ uint32 cost = 0;
+ uint32 type = 0;
+ if(pCreature->isTabardDesigner())
+ {
+ // if tabard designer, then trying to buy a guild charter.
+ // do not let if already in guild.
+ if(_player->GetGuildId())
+ return;
+
+ charterid = GUILD_CHARTER;
+ cost = GUILD_CHARTER_COST;
+ type = 9;
+ }
+ else
+ {
+ // TODO: find correct opcode
+ if(_player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ SendNotification(LANG_ARENA_ONE_TOOLOW, 70);
+ return;
+ }
+
+ for(uint8 i = 0; i < MAX_ARENA_SLOT; i++)
+ {
+ if(_player->GetArenaTeamId(i) && (i == (unk10-1)))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM);
+ return;
+ }
+ }
+ switch(unk10)
+ {
+ case 1:
+ charterid = ARENA_TEAM_CHARTER_2v2;
+ cost = ARENA_TEAM_CHARTER_2v2_COST;
+ type = 2; // 2v2
+ break;
+ case 2:
+ charterid = ARENA_TEAM_CHARTER_3v3;
+ cost = ARENA_TEAM_CHARTER_3v3_COST;
+ type = 3; // 3v3
+ break;
+ case 3:
+ charterid = ARENA_TEAM_CHARTER_5v5;
+ cost = ARENA_TEAM_CHARTER_5v5_COST;
+ type = 5; // 5v5
+ break;
+ default:
+ sLog.outDebug("unknown selection at buy petition: %u", unk10);
+ return;
+ }
+ }
+
+ if(type == 9)
+ {
+ if(objmgr.GetGuildByName(name))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_EXISTS);
+ return;
+ }
+ if(objmgr.IsReservedName(name))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_INVALID);
+ return;
+ }
+ if(!ObjectMgr::IsValidCharterName(name))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_INVALID);
+ return;
+ }
+ }
+ else
+ {
+ if(objmgr.GetArenaTeamByName(name))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
+ return;
+ }
+ if(objmgr.IsReservedName(name))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_INVALID);
+ return;
+ }
+ if(!ObjectMgr::IsValidCharterName(name))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_INVALID);
+ return;
+ }
+ }
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(charterid);
+ if(!pProto)
+ {
+ _player->SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, charterid, 0);
+ return;
+ }
+
+ if(_player->GetMoney() < cost)
+ { //player hasn't got enough money
+ _player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, charterid, 0);
+ return;
+ }
+
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, charterid, pProto->BuyCount );
+ if(msg != EQUIP_ERR_OK)
+ {
+ _player->SendBuyError(msg, pCreature, charterid, 0);
+ return;
+ }
+
+ _player->ModifyMoney(-(int32)cost);
+ Item *charter = _player->StoreNewItem(dest, charterid, true);
+ if(!charter)
+ return;
+
+ charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT, charter->GetGUIDLow());
+ // ITEM_FIELD_ENCHANTMENT is guild/arenateam id
+ // ITEM_FIELD_ENCHANTMENT+1 is current signatures count (showed on item)
+ charter->SetState(ITEM_CHANGED, _player);
+ _player->SendNewItem(charter, 1, true, false);
+
+ // a petition is invalid, if both the owner and the type matches
+ QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE ownerguid = '%u' AND type = '%u'", _player->GetGUIDLow(), type);
+
+ std::ostringstream ssInvalidPetitionGUIDs;
+
+ if (result)
+ {
+
+ do
+ {
+ Field *fields = result->Fetch();
+ ssInvalidPetitionGUIDs << "'" << fields[0].GetUInt32() << "' , ";
+ } while (result->NextRow());
+
+ delete result;
+ }
+
+ // delete petitions with the same guid as this one
+ ssInvalidPetitionGUIDs << "'" << charter->GetGUIDLow() << "'";
+
+ sLog.outDebug("Invalid petition GUIDs: %s", ssInvalidPetitionGUIDs.str().c_str());
+ CharacterDatabase.escape_string(name);
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM petition WHERE petitionguid IN ( %s )", ssInvalidPetitionGUIDs.str().c_str());
+ CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE petitionguid IN ( %s )", ssInvalidPetitionGUIDs.str().c_str());
+ CharacterDatabase.PExecute("INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES ('%u', '%u', '%s', '%u')",
+ _player->GetGUIDLow(), charter->GetGUIDLow(), name.c_str(), type);
+ CharacterDatabase.CommitTransaction();
+}
+
+void WorldSession::HandlePetitionShowSignOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ // ok
+ sLog.outDebug("Received opcode CMSG_PETITION_SHOW_SIGNATURES");
+ //recv_data.hexlike();
+
+ uint8 signs = 0;
+ uint64 petitionguid;
+ recv_data >> petitionguid; // petition guid
+
+ // solve (possible) some strange compile problems with explicit use GUID_LOPART(petitionguid) at some GCC versions (wrong code optimization in compiler?)
+ uint32 petitionguid_low = GUID_LOPART(petitionguid);
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid, type FROM petition WHERE petitionguid = '%u'", petitionguid_low);
+ if(!result)
+ {
+ sLog.outError("any petition on server...");
+ return;
+ }
+ Field *fields = result->Fetch();
+ uint32 type = fields[1].GetUInt32();
+ delete result;
+ // if guild petition and has guild => error, return;
+ if(type==9 && _player->GetGuildId())
+ return;
+
+ result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", petitionguid_low);
+
+ // result==NULL also correct in case no sign yet
+ if(result)
+ signs = result->GetRowCount();
+
+ sLog.outDebug("CMSG_PETITION_SHOW_SIGNATURES petition entry: '%u'", petitionguid_low);
+
+ WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8+8+4+1+signs*12));
+ data << petitionguid; // petition guid
+ data << _player->GetGUID(); // owner guid
+ data << petitionguid_low; // guild guid (in mangos always same as GUID_LOPART(petitionguid)
+ data << signs; // sign's count
+
+ for(uint8 i = 1; i <= signs; i++)
+ {
+ Field *fields = result->Fetch();
+ uint64 plguid = fields[0].GetUInt64();
+
+ data << plguid; // Player GUID
+ data << (uint32)0; // there 0 ...
+
+ result->NextRow();
+ }
+ delete result;
+ SendPacket(&data);
+}
+
+void WorldSession::HandlePetitionQueryOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 4+8);
+
+ sLog.outDebug("Received opcode CMSG_PETITION_QUERY"); // ok
+ //recv_data.hexlike();
+
+ uint32 guildguid;
+ uint64 petitionguid;
+ recv_data >> guildguid; // in mangos always same as GUID_LOPART(petitionguid)
+ recv_data >> petitionguid; // petition guid
+ sLog.outDebug("CMSG_PETITION_QUERY Petition GUID %u Guild GUID %u", GUID_LOPART(petitionguid), guildguid);
+
+ SendPetitionQueryOpcode(petitionguid);
+}
+
+void WorldSession::SendPetitionQueryOpcode(uint64 petitionguid)
+{
+ uint64 ownerguid = 0;
+ uint32 type;
+ std::string name = "NO_NAME_FOR_GUID";
+ uint8 signs = 0;
+
+ QueryResult *result = CharacterDatabase.PQuery(
+ "SELECT ownerguid, name, "
+ " (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = '%u') AS signs "
+ "FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid), GUID_LOPART(petitionguid));
+
+ if(result)
+ {
+ Field* fields = result->Fetch();
+ ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ name = fields[1].GetCppString();
+ signs = fields[2].GetUInt8();
+ delete result;
+ }
+ else
+ {
+ sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
+ return;
+ }
+
+ QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+
+ if(result2)
+ {
+ Field* fields = result2->Fetch();
+ type = fields[0].GetUInt32();
+ delete result2;
+ }
+ else
+ {
+ sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
+ return;
+ }
+
+ WorldPacket data(SMSG_PETITION_QUERY_RESPONSE, (4+8+name.size()+1+1+4*13));
+ data << GUID_LOPART(petitionguid); // guild/team guid (in mangos always same as GUID_LOPART(petition guid)
+ data << ownerguid; // charter owner guid
+ data << name; // name (guild/arena team)
+ data << uint8(0); // 1
+ if(type == 9)
+ {
+ data << uint32(9);
+ data << uint32(9);
+ data << uint32(0); // bypass client - side limitation, a different value is needed here for each petition
+ }
+ else
+ {
+ data << type-1;
+ data << type-1;
+ data << type; // bypass client - side limitation, a different value is needed here for each petition
+ }
+ data << uint32(0); // 5
+ data << uint32(0); // 6
+ data << uint32(0); // 7
+ data << uint32(0); // 8
+ data << uint16(0); // 9 2 bytes field
+ data << uint32(0); // 10
+ data << uint32(0); // 11
+ data << uint32(0); // 13 count of next strings?
+ data << uint32(0); // 14
+ if(type == 9)
+ data << uint32(0); // 15 0 - guild, 1 - arena team
+ else
+ data << uint32(1);
+ SendPacket(&data);
+}
+
+void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8+1);
+
+ sLog.outDebug("Received opcode MSG_PETITION_RENAME"); // ok
+ //recv_data.hexlike();
+
+ uint64 petitionguid;
+ uint32 type;
+ std::string newname;
+
+ recv_data >> petitionguid; // guid
+ recv_data >> newname; // new name
+
+ Item *item = _player->GetItemByGuid(petitionguid);
+ if(!item)
+ return;
+
+ QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+
+ if(result2)
+ {
+ Field* fields = result2->Fetch();
+ type = fields[0].GetUInt32();
+ delete result2;
+ }
+ else
+ {
+ sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
+ return;
+ }
+
+ if(type == 9)
+ {
+ if(objmgr.GetGuildByName(newname))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_EXISTS);
+ return;
+ }
+ if(objmgr.IsReservedName(newname))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_INVALID);
+ return;
+ }
+ if(!ObjectMgr::IsValidCharterName(newname))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_INVALID);
+ return;
+ }
+ }
+ else
+ {
+ if(objmgr.GetArenaTeamByName(newname))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
+ return;
+ }
+ if(objmgr.IsReservedName(newname))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_INVALID);
+ return;
+ }
+ if(!ObjectMgr::IsValidCharterName(newname))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_INVALID);
+ return;
+ }
+ }
+
+ std::string db_newname = newname;
+ CharacterDatabase.escape_string(db_newname);
+ CharacterDatabase.PExecute("UPDATE petition SET name = '%s' WHERE petitionguid = '%u'",
+ db_newname.c_str(), GUID_LOPART(petitionguid));
+
+ sLog.outDebug("Petition (GUID: %u) renamed to '%s'", GUID_LOPART(petitionguid), newname.c_str());
+ WorldPacket data(MSG_PETITION_RENAME, (8+newname.size()+1));
+ data << petitionguid;
+ data << newname;
+ SendPacket(&data);
+}
+
+void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8+1);
+
+ sLog.outDebug("Received opcode CMSG_PETITION_SIGN"); // ok
+ //recv_data.hexlike();
+
+ Field *fields;
+ uint64 petitionguid;
+ uint32 type;
+ uint8 unk;
+ uint64 ownerguid;
+ recv_data >> petitionguid; // petition guid
+ recv_data >> unk;
+
+ uint8 signs = 0;
+
+ QueryResult *result = CharacterDatabase.PQuery(
+ "SELECT ownerguid, "
+ " (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = '%u') AS signs "
+ "FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid), GUID_LOPART(petitionguid));
+
+ if(!result)
+ {
+ sLog.outError("any petition on server...");
+ return;
+ }
+
+ fields = result->Fetch();
+ ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ signs = fields[1].GetUInt8();
+
+ delete result;
+
+ uint32 plguidlo = _player->GetGUIDLow();
+ if(GUID_LOPART(ownerguid) == plguidlo)
+ return;
+
+ // not let enemies sign guild charter
+ if(!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != objmgr.GetPlayerTeamByGUID(ownerguid))
+ return;
+
+ QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+
+ if(result2)
+ {
+ Field* fields = result2->Fetch();
+ type = fields[0].GetUInt32();
+ delete result2;
+ }
+ else
+ {
+ sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
+ return;
+ }
+
+ if(type != 9 && _player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ // player is too low level to join an arena team
+ SendNotification(LANG_YOUR_ARENA_LEVEL_REQ_ERROR,sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL));
+ return;
+ }
+
+ signs += 1;
+ if(signs > type) // client signs maximum
+ return;
+
+ //client doesn't allow to sign petition two times by one character, but not check sign by another character from same account
+ //not allow sign another player from already sign player account
+ result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE player_account = '%u' AND petitionguid = '%u'", GetAccountId(), GUID_LOPART(petitionguid));
+
+ if(result)
+ {
+ delete result;
+ WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8+8+4));
+ data << petitionguid;
+ data << _player->GetGUID();
+ data << (uint32)PETITION_SIGN_ALREADY_SIGNED;
+
+ // close at signer side
+ SendPacket(&data);
+
+ // update for owner if online
+ if(Player *owner = objmgr.GetPlayer(ownerguid))
+ owner->GetSession()->SendPacket(&data);
+ return;
+ }
+
+ CharacterDatabase.PExecute("INSERT INTO petition_sign (ownerguid,petitionguid, playerguid, player_account) VALUES ('%u', '%u', '%u','%u')", GUID_LOPART(ownerguid),GUID_LOPART(petitionguid), plguidlo,GetAccountId());
+
+ sLog.outDebug("PETITION SIGN: GUID %u by player: %s (GUID: %u Account: %u)", GUID_LOPART(petitionguid), _player->GetName(),plguidlo,GetAccountId());
+
+ WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8+8+4));
+ data << petitionguid;
+ data << _player->GetGUID();
+ data << (uint32)PETITION_SIGN_OK;
+
+ // close at signer side
+ SendPacket(&data);
+
+ // update signs count on charter, required testing...
+ //Item *item = _player->GetItemByGuid(petitionguid));
+ //if(item)
+ // item->SetUInt32Value(ITEM_FIELD_ENCHANTMENT+1, signs);
+
+ // update for owner if online
+ if(Player *owner = objmgr.GetPlayer(ownerguid))
+ owner->GetSession()->SendPacket(&data);
+}
+
+void WorldSession::HandlePetitionDeclineOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ sLog.outDebug("Received opcode MSG_PETITION_DECLINE"); // ok
+ //recv_data.hexlike();
+
+ uint64 petitionguid;
+ uint64 ownerguid;
+ recv_data >> petitionguid; // petition guid
+ sLog.outDebug("Petition %u declined by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow());
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT ownerguid FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+ if(!result)
+ return;
+
+ Field *fields = result->Fetch();
+ ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ delete result;
+
+ Player *owner = objmgr.GetPlayer(ownerguid);
+ if(owner) // petition owner online
+ {
+ WorldPacket data(MSG_PETITION_DECLINE, 8);
+ data << _player->GetGUID();
+ owner->GetSession()->SendPacket(&data);
+ }
+}
+
+void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 4+8+8);
+
+ sLog.outDebug("Received opcode CMSG_OFFER_PETITION"); // ok
+ //recv_data.hexlike();
+
+ uint8 signs = 0;
+ uint64 petitionguid, plguid;
+ uint32 petitiontype;
+ Player *player;
+ recv_data >> petitiontype; // 2.0.8 - petition type?
+ recv_data >> petitionguid; // petition guid
+ recv_data >> plguid; // player guid
+ sLog.outDebug("OFFER PETITION: type %u, GUID1 %u, to player id: %u", petitiontype, GUID_LOPART(petitionguid), GUID_LOPART(plguid));
+
+ player = ObjectAccessor::FindPlayer(plguid);
+ if(!player || player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
+ return;
+
+ // not let offer to enemies
+ if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != player->GetTeam() )
+ return;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+ if(!result)
+ {
+ sLog.outError("any petition on server...");
+ return;
+ }
+
+ delete result;
+
+ result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+ // result==NULL also correct charter without signs
+ if(result)
+ signs = result->GetRowCount();
+
+ WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8+8+4+signs+signs*12));
+ data << petitionguid; // petition guid
+ data << _player->GetGUID(); // owner guid
+ data << GUID_LOPART(petitionguid); // guild guid (in mangos always same as GUID_LOPART(petition guid)
+ data << signs; // sign's count
+
+ for(uint8 i = 1; i <= signs; i++)
+ {
+ Field *fields = result->Fetch();
+ uint64 plguid = fields[0].GetUInt64();
+
+ data << plguid; // Player GUID
+ data << (uint32)0; // there 0 ...
+
+ result->NextRow();
+ }
+
+ delete result;
+ player->GetSession()->SendPacket(&data);
+}
+
+void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ sLog.outDebug("Received opcode CMSG_TURN_IN_PETITION"); // ok
+ //recv_data.hexlike();
+
+ WorldPacket data;
+ uint64 petitionguid;
+
+ uint32 ownerguidlo;
+ uint32 type;
+ std::string name;
+
+ recv_data >> petitionguid;
+
+ sLog.outDebug("Petition %u turned in by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow());
+
+ // data
+ QueryResult *result = CharacterDatabase.PQuery("SELECT ownerguid, name, type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+ if(result)
+ {
+ Field *fields = result->Fetch();
+ ownerguidlo = fields[0].GetUInt32();
+ name = fields[1].GetCppString();
+ type = fields[2].GetUInt32();
+ delete result;
+ }
+ else
+ {
+ sLog.outError("petition table has broken data!");
+ return;
+ }
+
+ if(type == 9)
+ {
+ if(_player->GetGuildId())
+ {
+ data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
+ data << (uint32)PETITION_TURN_ALREADY_IN_GUILD; // already in guild
+ _player->GetSession()->SendPacket(&data);
+ return;
+ }
+ }
+ else
+ {
+ uint8 slot = ArenaTeam::GetSlotByType(type);
+ if(slot >= MAX_ARENA_SLOT)
+ return;
+
+ if(_player->GetArenaTeamId(slot))
+ {
+ //data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
+ //data << (uint32)PETITION_TURN_ALREADY_IN_GUILD; // already in guild
+ //_player->GetSession()->SendPacket(&data);
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM);
+ return;
+ }
+ }
+
+ if(_player->GetGUIDLow() != ownerguidlo)
+ return;
+
+ // signs
+ uint8 signs;
+ result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+ if(result)
+ signs = result->GetRowCount();
+ else
+ signs = 0;
+
+ uint32 count;
+ //if(signs < sWorld.getConfig(CONFIG_MIN_PETITION_SIGNS))
+ if(type == 9)
+ count = sWorld.getConfig(CONFIG_MIN_PETITION_SIGNS);
+ else
+ count = type-1;
+ if(signs < count)
+ {
+ data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
+ data << (uint32)PETITION_TURN_NEED_MORE_SIGNATURES; // need more signatures...
+ SendPacket(&data);
+ delete result;
+ return;
+ }
+
+ if(type == 9)
+ {
+ if(objmgr.GetGuildByName(name))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_EXISTS);
+ delete result;
+ return;
+ }
+ }
+ else
+ {
+ if(objmgr.GetArenaTeamByName(name))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
+ delete result;
+ return;
+ }
+ }
+
+ // and at last charter item check
+ Item *item = _player->GetItemByGuid(petitionguid);
+ if(!item)
+ {
+ delete result;
+ return;
+ }
+
+ // OK!
+
+ // delete charter item
+ _player->DestroyItem(item->GetBagSlot(),item->GetSlot(), true);
+
+ if(type == 9) // create guild
+ {
+ Guild* guild = new Guild;
+ if(!guild->create(_player->GetGUID(), name))
+ {
+ delete guild;
+ delete result;
+ return;
+ }
+
+ // register guild and add guildmaster
+ objmgr.AddGuild(guild);
+
+ // add members
+ for(uint8 i = 0; i < signs; ++i)
+ {
+ Field* fields = result->Fetch();
+ guild->AddMember(fields[0].GetUInt64(), guild->GetLowestRank());
+ result->NextRow();
+ }
+ }
+ else // or arena team
+ {
+ ArenaTeam* at = new ArenaTeam;
+ if(!at->create(_player->GetGUID(), type, name))
+ {
+ sLog.outError("PetitionsHandler: arena team create failed.");
+ delete at;
+ delete result;
+ return;
+ }
+
+ CHECK_PACKET_SIZE(recv_data, 8+5*4);
+ uint32 icon, iconcolor, border, bordercolor, backgroud;
+ recv_data >> backgroud >> icon >> iconcolor >> border >> bordercolor;
+
+ at->SetEmblem(backgroud, icon, iconcolor, border, bordercolor);
+
+ // register team and add captain
+ objmgr.AddArenaTeam(at);
+ sLog.outDebug("PetitonsHandler: arena team added to objmrg");
+
+ // add members
+ for(uint8 i = 0; i < signs; ++i)
+ {
+ Field* fields = result->Fetch();
+ sLog.outDebug("PetitionsHandler: adding arena member %u", fields[0].GetUInt64());
+ at->AddMember(fields[0].GetUInt64());
+ result->NextRow();
+ }
+ }
+
+ delete result;
+
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+ CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+ CharacterDatabase.CommitTransaction();
+
+ // created
+ sLog.outDebug("TURN IN PETITION GUID %u", GUID_LOPART(petitionguid));
+
+ data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
+ data << (uint32)PETITION_TURN_OK;
+ SendPacket(&data);
+}
+
+void WorldSession::HandlePetitionShowListOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ sLog.outDebug("Received CMSG_PETITION_SHOWLIST"); // ok
+ //recv_data.hexlike();
+
+ uint64 guid;
+ recv_data >> guid;
+
+ SendPetitionShowList(guid);
+}
+
+void WorldSession::SendPetitionShowList(uint64 guid)
+{
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_PETITIONER);
+ if (!pCreature)
+ {
+ sLog.outDebug("WORLD: HandlePetitionShowListOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ uint8 count = 0;
+ if(pCreature->isTabardDesigner())
+ count = 1;
+ else
+ count = 3;
+
+ WorldPacket data(SMSG_PETITION_SHOWLIST, 8+1+4*6);
+ data << guid; // npc guid
+ data << count; // count
+ if(count == 1)
+ {
+ data << uint32(1); // index
+ data << uint32(GUILD_CHARTER); // charter entry
+ data << uint32(16161); // charter display id
+ data << uint32(GUILD_CHARTER_COST); // charter cost
+ data << uint32(0); // unknown
+ data << uint32(9); // required signs?
+ }
+ else
+ {
+ // 2v2
+ data << uint32(1); // index
+ data << uint32(ARENA_TEAM_CHARTER_2v2); // charter entry
+ data << uint32(16161); // charter display id
+ data << uint32(ARENA_TEAM_CHARTER_2v2_COST); // charter cost
+ data << uint32(2); // unknown
+ data << uint32(2); // required signs?
+ // 3v3
+ data << uint32(2); // index
+ data << uint32(ARENA_TEAM_CHARTER_3v3); // charter entry
+ data << uint32(16161); // charter display id
+ data << uint32(ARENA_TEAM_CHARTER_3v3_COST); // charter cost
+ data << uint32(3); // unknown
+ data << uint32(3); // required signs?
+ // 5v5
+ data << uint32(3); // index
+ data << uint32(ARENA_TEAM_CHARTER_5v5); // charter entry
+ data << uint32(16161); // charter display id
+ data << uint32(ARENA_TEAM_CHARTER_5v5_COST); // charter cost
+ data << uint32(5); // unknown
+ data << uint32(5); // required signs?
+ }
+ //for(uint8 i = 0; i < count; i++)
+ //{
+ // data << uint32(i); // index
+ // data << uint32(GUILD_CHARTER); // charter entry
+ // data << uint32(16161); // charter display id
+ // data << uint32(GUILD_CHARTER_COST+i); // charter cost
+ // data << uint32(0); // unknown
+ // data << uint32(9); // required signs?
+ //}
+ SendPacket(&data);
+ sLog.outDebug("Sent SMSG_PETITION_SHOWLIST");
+}
diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 0da7f36c3eb..56e123f6e6f 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -265,8 +265,8 @@ Player::Player (WorldSession *session): Unit() if(GetSession()->GetSecurity() >= SEC_GAMEMASTER)
SetAcceptTicket(true);
- // players always and GM if set in config accept whispers by default
- if(GetSession()->GetSecurity() == SEC_PLAYER || sWorld.getConfig(CONFIG_GM_WISPERING_TO))
+ // players always accept
+ if(GetSession()->GetSecurity() == SEC_PLAYER)
SetAcceptWhispers(true);
m_curSelection = 0;
@@ -1415,7 +1415,7 @@ uint8 Player::chatTag() const // 0x4 - gm
// 0x2 - dnd
// 0x1 - afk
- if(isGameMaster())
+ if(isGMChat())
return 4;
else if(isDND())
return 3;
@@ -1555,10 +1555,13 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati }
}
- SetSemaphoreTeleport(false);
-
if(!GetSession()->PlayerLogout())
+ {
+ // don't reset teleport semaphore while logging out, otherwise m_teleport_dest won't be used in Player::SaveToDB
+ SetSemaphoreTeleport(false);
+
UpdateZone(GetZoneId());
+ }
// new zone
if(old_zone != GetZoneId())
@@ -9322,6 +9325,11 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo ItemPrototype const *pProto = pItem->GetProto();
if( pProto )
{
+ // May be here should be more stronger checks; STUNNED checked
+ // ROOT, CONFUSED, DISTRACTED, FLEEING this needs to be checked.
+ if (not_loading && hasUnitState(UNIT_STAT_STUNNED))
+ return EQUIP_ERR_YOU_ARE_STUNNED;
+
if(pItem->IsBindedNotWith(GetGUID()))
return EQUIP_ERR_DONT_OWN_THAT_ITEM;
@@ -9346,6 +9354,9 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo if(isInCombat()&& pProto->Class == ITEM_CLASS_WEAPON && m_weaponChangeTimer != 0)
return EQUIP_ERR_CANT_DO_RIGHT_NOW; // maybe exist better err
+ if(IsNonMeleeSpellCasted(false))
+ return EQUIP_ERR_CANT_DO_RIGHT_NOW;
+
uint8 eslot = FindEquipSlot( pProto, slot, swap );
if( eslot == NULL_SLOT )
return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
@@ -13798,16 +13809,45 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) {
switch(sWorld.getConfig(CONFIG_GM_LOGIN_STATE))
{
- case 0: // disable
- break;
- case 1: // enable
- SetGameMaster(true);
- break;
+ default:
+ case 0: break; // disable
+ case 1: SetGameMaster(true); break; // enable
case 2: // save state
- if(gmstate)
+ if(gmstate & PLAYER_EXTRA_GM_ON)
SetGameMaster(true);
break;
+ }
+
+ switch(sWorld.getConfig(CONFIG_GM_ACCEPT_TICKETS))
+ {
default:
+ case 0: break; // disable
+ case 1: SetAcceptTicket(true); break; // enable
+ case 2: // save state
+ if(gmstate & PLAYER_EXTRA_GM_ACCEPT_TICKETS)
+ SetAcceptTicket(true);
+ break;
+ }
+
+ switch(sWorld.getConfig(CONFIG_GM_CHAT))
+ {
+ default:
+ case 0: break; // disable
+ case 1: SetGMChat(true); break; // enable
+ case 2: // save state
+ if(gmstate & PLAYER_EXTRA_GM_CHAT)
+ SetGMChat(true);
+ break;
+ }
+
+ switch(sWorld.getConfig(CONFIG_GM_WISPERING_TO))
+ {
+ default:
+ case 0: break; // disable
+ case 1: SetAcceptWhispers(true); break; // enable
+ case 2: // save state
+ if(gmstate & PLAYER_EXTRA_ACCEPT_WHISPERS)
+ SetAcceptWhispers(true);
break;
}
}
@@ -14877,7 +14917,7 @@ void Player::SaveToDB() ss << "0";
ss << ", ";
- ss << (isGameMaster()? 1 : 0);
+ ss << m_ExtraFlags;
ss << ", ";
ss << uint32(m_stableSlots); // to prevent save uint8 as char
@@ -16387,13 +16427,15 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint return false;
}
- VendorItem const* crItem = vItems->FindItem(item);
- if(!crItem)
+ size_t vendor_slot = vItems->FindItemSlot(item);
+ if(vendor_slot >= vItems->GetItemCount())
{
SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
return false;
}
+ VendorItem const* crItem = vItems->m_items[vendor_slot];
+
// check current item amount if it limited
if( crItem->maxcount != 0 )
{
@@ -16520,7 +16562,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
data << pCreature->GetGUID();
- data << (uint32)crItem->item;
+ data << (uint32)(vendor_slot+1); // numbered from 1 at client
data << (uint32)(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
data << (uint32)count;
GetSession()->SendPacket(&data);
@@ -16559,7 +16601,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
data << pCreature->GetGUID();
- data << (uint32)crItem->item;
+ data << (uint32)(vendor_slot+1); // numbered from 1 at client
data << (uint32)(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
data << (uint32)count;
GetSession()->SendPacket(&data);
diff --git a/src/game/Player.h b/src/game/Player.h index 33ccc79e307..b8c35e941f7 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -496,6 +496,7 @@ enum PlayerExtraFlags PLAYER_EXTRA_ACCEPT_WHISPERS = 0x0004,
PLAYER_EXTRA_TAXICHEAT = 0x0008,
PLAYER_EXTRA_GM_INVISIBLE = 0x0010,
+ PLAYER_EXTRA_GM_CHAT = 0x0020, // Show GM badge in chat messages
// other states
PLAYER_EXTRA_PVP_DEATH = 0x0100 // store PvP death status until corpse creating.
@@ -954,6 +955,8 @@ class MANGOS_DLL_SPEC Player : public Unit void SetAcceptWhispers(bool on) { if(on) m_ExtraFlags |= PLAYER_EXTRA_ACCEPT_WHISPERS; else m_ExtraFlags &= ~PLAYER_EXTRA_ACCEPT_WHISPERS; }
bool isGameMaster() const { return m_ExtraFlags & PLAYER_EXTRA_GM_ON; }
void SetGameMaster(bool on);
+ bool isGMChat() const { return GetSession()->GetSecurity() >= SEC_MODERATOR && (m_ExtraFlags & PLAYER_EXTRA_GM_CHAT); }
+ void SetGMChat(bool on) { if(on) m_ExtraFlags |= PLAYER_EXTRA_GM_CHAT; else m_ExtraFlags &= ~PLAYER_EXTRA_GM_CHAT; }
bool isTaxiCheater() const { return m_ExtraFlags & PLAYER_EXTRA_TAXICHEAT; }
void SetTaxiCheater(bool on) { if(on) m_ExtraFlags |= PLAYER_EXTRA_TAXICHEAT; else m_ExtraFlags &= ~PLAYER_EXTRA_TAXICHEAT; }
bool isGMVisible() const { return !(m_ExtraFlags & PLAYER_EXTRA_GM_INVISIBLE); }
diff --git a/src/game/PointMovementGenerator.cpp b/src/game/PointMovementGenerator.cpp index b03263c8bb6..b0e685fa412 100644 --- a/src/game/PointMovementGenerator.cpp +++ b/src/game/PointMovementGenerator.cpp @@ -41,7 +41,7 @@ bool PointMovementGenerator<T>::Update(T &unit, const uint32 &diff) if(!&unit)
return false;
- if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED))
+ if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED))
return true;
Traveller<T> traveller(unit);
diff --git a/src/game/RandomMovementGenerator.cpp b/src/game/RandomMovementGenerator.cpp index 15273843ffc..e27368c73f3 100644 --- a/src/game/RandomMovementGenerator.cpp +++ b/src/game/RandomMovementGenerator.cpp @@ -126,7 +126,7 @@ template<> bool
RandomMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff)
{
- if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED))
+ if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED))
{
i_nextMoveTime.Update(i_nextMoveTime.GetExpiry()); // Expire the timer
creature.clearUnitState(UNIT_STAT_ROAMING);
diff --git a/src/game/SocialMgr.cpp b/src/game/SocialMgr.cpp index 85afb48108a..858e6af6239 100644 --- a/src/game/SocialMgr.cpp +++ b/src/game/SocialMgr.cpp @@ -1,309 +1,311 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "SocialMgr.h" -#include "Policies/SingletonImp.h" -#include "Database/DatabaseEnv.h" -#include "Opcodes.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Player.h" -#include "ObjectMgr.h" -#include "World.h" -#include "Util.h" - -INSTANTIATE_SINGLETON_1( SocialMgr ); - -PlayerSocial::PlayerSocial() -{ - m_playerGUID = 0; -} - -PlayerSocial::~PlayerSocial() -{ - m_playerSocialMap.clear(); -} - -bool PlayerSocial::AddToSocialList(uint32 friend_guid, bool ignore) -{ - // prevent list (client-side) overflow - if(m_playerSocialMap.size() >= (255-1)) - return false; - - uint32 flag = SOCIAL_FLAG_FRIEND; - if(ignore) - flag = SOCIAL_FLAG_IGNORED; - - PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid); - if(itr != m_playerSocialMap.end()) - { - CharacterDatabase.PExecute("UPDATE character_social SET flags = (flags | %u) WHERE guid = '%u' AND friend = '%u'", flag, GetPlayerGUID(), friend_guid); - m_playerSocialMap[friend_guid].Flags |= flag; - } - else - { - CharacterDatabase.PExecute("INSERT INTO character_social (guid, friend, flags) VALUES ('%u', '%u', '%u')", GetPlayerGUID(), friend_guid, flag); - FriendInfo fi; - fi.Flags |= flag; - m_playerSocialMap[friend_guid] = fi; - } - return true; -} - -void PlayerSocial::RemoveFromSocialList(uint32 friend_guid, bool ignore) -{ - PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid); - if(itr == m_playerSocialMap.end()) // not exist - return; - - uint32 flag = SOCIAL_FLAG_FRIEND; - if(ignore) - flag = SOCIAL_FLAG_IGNORED; - - itr->second.Flags &= ~flag; - if(itr->second.Flags == 0) - { - CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' AND friend = '%u'", GetPlayerGUID(), friend_guid); - m_playerSocialMap.erase(itr); - } - else - { - CharacterDatabase.PExecute("UPDATE character_social SET flags = (flags & ~%u) WHERE guid = '%u' AND friend = '%u'", flag, GetPlayerGUID(), friend_guid); - } -} - -void PlayerSocial::SetFriendNote(uint32 friend_guid, std::string note) -{ - PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid); - if(itr == m_playerSocialMap.end()) // not exist - return; - - utf8truncate(note,48); // DB and client size limitation - - CharacterDatabase.escape_string(note); - CharacterDatabase.PExecute("UPDATE character_social SET note = '%s' WHERE guid = '%u' AND friend = '%u'", note.c_str(), GetPlayerGUID(), friend_guid); - m_playerSocialMap[friend_guid].Note = note; -} - -void PlayerSocial::SendSocialList() -{ - Player *plr = objmgr.GetPlayer(GetPlayerGUID()); - if(!plr) - return; - - uint32 size = m_playerSocialMap.size(); - - WorldPacket data(SMSG_CONTACT_LIST, (4+4+size*25)); // just can guess size - data << uint32(7); // unk flag (0x1, 0x2, 0x4), 0x7 if it include ignore list - data << uint32(size); // friends count - - for(PlayerSocialMap::iterator itr = m_playerSocialMap.begin(); itr != m_playerSocialMap.end(); ++itr) - { - sSocialMgr.GetFriendInfo(plr, itr->first, itr->second); - - data << uint64(itr->first); // player guid - data << uint32(itr->second.Flags); // player flag (0x1-friend?, 0x2-ignored?, 0x4-muted?) - data << itr->second.Note; // string note - if(itr->second.Flags & SOCIAL_FLAG_FRIEND) // if IsFriend() - { - data << uint8(itr->second.Status); // online/offline/etc? - if(itr->second.Status) // if online - { - data << uint32(itr->second.Area); // player area - data << uint32(itr->second.Level); // player level - data << uint32(itr->second.Class); // player class - } - } - } - - plr->GetSession()->SendPacket(&data); - sLog.outDebug("WORLD: Sent SMSG_CONTACT_LIST"); -} - -bool PlayerSocial::HasFriend(uint32 friend_guid) -{ - PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid); - if(itr != m_playerSocialMap.end()) - return itr->second.Flags & SOCIAL_FLAG_FRIEND; - return false; -} - -bool PlayerSocial::HasIgnore(uint32 ignore_guid) -{ - PlayerSocialMap::iterator itr = m_playerSocialMap.find(ignore_guid); - if(itr != m_playerSocialMap.end()) - return itr->second.Flags & SOCIAL_FLAG_IGNORED; - return false; -} - -SocialMgr::SocialMgr() -{ - -} - -SocialMgr::~SocialMgr() -{ - -} - -void SocialMgr::RemovePlayerSocial(uint32 guid) -{ - SocialMap::iterator itr = m_socialMap.find(guid); - if(itr != m_socialMap.end()) - m_socialMap.erase(itr); -} - -void SocialMgr::GetFriendInfo(Player *player, uint32 friendGUID, FriendInfo &friendInfo) -{ - if(!player) - return; - - Player *pFriend = ObjectAccessor::FindPlayer(friendGUID); - - uint32 team = player->GetTeam(); - uint32 security = player->GetSession()->GetSecurity(); - bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST); - bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST) || security > SEC_PLAYER; - - // PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters - // MODERATOR, GAME MASTER, ADMINISTRATOR can see all - if( pFriend && pFriend->GetName() && - ( security > SEC_PLAYER || - ( pFriend->GetTeam() == team || allowTwoSideWhoList ) && - ( pFriend->GetSession()->GetSecurity() == SEC_PLAYER || gmInWhoList && pFriend->IsVisibleGloballyFor(player) ))) - { - friendInfo.Status = FRIEND_STATUS_ONLINE; - if(pFriend->isAFK()) - friendInfo.Status = FRIEND_STATUS_AFK; - if(pFriend->isDND()) - friendInfo.Status = FRIEND_STATUS_DND; - friendInfo.Area = pFriend->GetZoneId(); - friendInfo.Level = pFriend->getLevel(); - friendInfo.Class = pFriend->getClass(); - } - else - { - friendInfo.Status = FRIEND_STATUS_OFFLINE; - friendInfo.Area = 0; - friendInfo.Level = 0; - friendInfo.Class = 0; - } -} - -void SocialMgr::MakeFriendStatusPacket(FriendsResult result, uint32 guid, WorldPacket *data) -{ - data->Initialize(SMSG_FRIEND_STATUS, 5); - *data << uint8(result); - *data << uint64(guid); -} - -void SocialMgr::SendFriendStatus(Player *player, FriendsResult result, uint32 friend_guid, std::string name, bool broadcast) -{ - FriendInfo fi; - - WorldPacket data; - MakeFriendStatusPacket(result, friend_guid, &data); - switch(result) - { - case FRIEND_ONLINE: - GetFriendInfo(player, friend_guid, fi); - data << uint8(fi.Status); - data << uint32(fi.Area); - data << uint32(fi.Level); - data << uint32(fi.Class); - break; - case FRIEND_ADDED_ONLINE: - GetFriendInfo(player, friend_guid, fi); - data << name; - data << uint8(fi.Status); - data << uint32(fi.Area); - data << uint32(fi.Level); - data << uint32(fi.Class); - break; - case FRIEND_ADDED_OFFLINE: - data << name; - break; - } - - if(broadcast) - BroadcastToFriendListers(player, &data); - else - player->GetSession()->SendPacket(&data); -} - -void SocialMgr::BroadcastToFriendListers(Player *player, WorldPacket *packet) -{ - if(!player) - return; - - uint32 team = player->GetTeam(); - uint32 security = player->GetSession()->GetSecurity(); - uint32 guid = player->GetGUIDLow(); - bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST); - bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST); - - for(SocialMap::iterator itr = m_socialMap.begin(); itr != m_socialMap.end(); ++itr) - { - PlayerSocialMap::iterator itr2 = itr->second.m_playerSocialMap.find(guid); - if(itr2 != itr->second.m_playerSocialMap.end() && (itr2->second.Flags & SOCIAL_FLAG_FRIEND)) - { - Player *pFriend = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER)); - - // PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters - // MODERATOR, GAME MASTER, ADMINISTRATOR can see all - if( pFriend && pFriend->IsInWorld() && - ( pFriend->GetSession()->GetSecurity() > SEC_PLAYER || - ( pFriend->GetTeam() == team || allowTwoSideWhoList ) && - (security == SEC_PLAYER || gmInWhoList && player->IsVisibleGloballyFor(pFriend) ))) - { - pFriend->GetSession()->SendPacket(packet); - } - } - } -} - -PlayerSocial *SocialMgr::LoadFromDB(QueryResult *result, uint32 guid) -{ - PlayerSocial *social = &m_socialMap[guid]; - social->SetPlayerGUID(guid); - - if(!result) - return social; - - uint32 friend_guid = 0; - uint32 flags = 0; - std::string note = ""; - - do - { - Field *fields = result->Fetch(); - - friend_guid = fields[0].GetUInt32(); - flags = fields[1].GetUInt32(); - note = fields[2].GetCppString(); - - social->m_playerSocialMap[friend_guid] = FriendInfo(flags, note); - - // prevent list (client-side) overflow - if(social->m_playerSocialMap.size() >= 255) - break; - } - while( result->NextRow() ); - delete result; - return social; -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "SocialMgr.h"
+#include "Policies/SingletonImp.h"
+#include "Database/DatabaseEnv.h"
+#include "Opcodes.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Player.h"
+#include "ObjectMgr.h"
+#include "World.h"
+#include "Util.h"
+
+INSTANTIATE_SINGLETON_1( SocialMgr );
+
+PlayerSocial::PlayerSocial()
+{
+ m_playerGUID = 0;
+}
+
+PlayerSocial::~PlayerSocial()
+{
+ m_playerSocialMap.clear();
+}
+
+bool PlayerSocial::AddToSocialList(uint32 friend_guid, bool ignore)
+{
+ // client limit
+ if(m_playerSocialMap.size() >= 50)
+ return false;
+
+ uint32 flag = SOCIAL_FLAG_FRIEND;
+ if(ignore)
+ flag = SOCIAL_FLAG_IGNORED;
+
+ PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid);
+ if(itr != m_playerSocialMap.end())
+ {
+ CharacterDatabase.PExecute("UPDATE character_social SET flags = (flags | %u) WHERE guid = '%u' AND friend = '%u'", flag, GetPlayerGUID(), friend_guid);
+ m_playerSocialMap[friend_guid].Flags |= flag;
+ }
+ else
+ {
+ CharacterDatabase.PExecute("INSERT INTO character_social (guid, friend, flags) VALUES ('%u', '%u', '%u')", GetPlayerGUID(), friend_guid, flag);
+ FriendInfo fi;
+ fi.Flags |= flag;
+ m_playerSocialMap[friend_guid] = fi;
+ }
+ return true;
+}
+
+void PlayerSocial::RemoveFromSocialList(uint32 friend_guid, bool ignore)
+{
+ PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid);
+ if(itr == m_playerSocialMap.end()) // not exist
+ return;
+
+ uint32 flag = SOCIAL_FLAG_FRIEND;
+ if(ignore)
+ flag = SOCIAL_FLAG_IGNORED;
+
+ itr->second.Flags &= ~flag;
+ if(itr->second.Flags == 0)
+ {
+ CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' AND friend = '%u'", GetPlayerGUID(), friend_guid);
+ m_playerSocialMap.erase(itr);
+ }
+ else
+ {
+ CharacterDatabase.PExecute("UPDATE character_social SET flags = (flags & ~%u) WHERE guid = '%u' AND friend = '%u'", flag, GetPlayerGUID(), friend_guid);
+ }
+}
+
+void PlayerSocial::SetFriendNote(uint32 friend_guid, std::string note)
+{
+ PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid);
+ if(itr == m_playerSocialMap.end()) // not exist
+ return;
+
+ utf8truncate(note,48); // DB and client size limitation
+
+ CharacterDatabase.escape_string(note);
+ CharacterDatabase.PExecute("UPDATE character_social SET note = '%s' WHERE guid = '%u' AND friend = '%u'", note.c_str(), GetPlayerGUID(), friend_guid);
+ m_playerSocialMap[friend_guid].Note = note;
+}
+
+void PlayerSocial::SendSocialList()
+{
+ Player *plr = objmgr.GetPlayer(GetPlayerGUID());
+ if(!plr)
+ return;
+
+ uint32 size = m_playerSocialMap.size();
+
+ WorldPacket data(SMSG_CONTACT_LIST, (4+4+size*25)); // just can guess size
+ data << uint32(7); // unk flag (0x1, 0x2, 0x4), 0x7 if it include ignore list
+ data << uint32(size); // friends count
+
+ for(PlayerSocialMap::iterator itr = m_playerSocialMap.begin(); itr != m_playerSocialMap.end(); ++itr)
+ {
+ sSocialMgr.GetFriendInfo(plr, itr->first, itr->second);
+
+ data << uint64(itr->first); // player guid
+ data << uint32(itr->second.Flags); // player flag (0x1-friend?, 0x2-ignored?, 0x4-muted?)
+ data << itr->second.Note; // string note
+ if(itr->second.Flags & SOCIAL_FLAG_FRIEND) // if IsFriend()
+ {
+ data << uint8(itr->second.Status); // online/offline/etc?
+ if(itr->second.Status) // if online
+ {
+ data << uint32(itr->second.Area); // player area
+ data << uint32(itr->second.Level); // player level
+ data << uint32(itr->second.Class); // player class
+ }
+ }
+ }
+
+ plr->GetSession()->SendPacket(&data);
+ sLog.outDebug("WORLD: Sent SMSG_CONTACT_LIST");
+}
+
+bool PlayerSocial::HasFriend(uint32 friend_guid)
+{
+ PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid);
+ if(itr != m_playerSocialMap.end())
+ return itr->second.Flags & SOCIAL_FLAG_FRIEND;
+ return false;
+}
+
+bool PlayerSocial::HasIgnore(uint32 ignore_guid)
+{
+ PlayerSocialMap::iterator itr = m_playerSocialMap.find(ignore_guid);
+ if(itr != m_playerSocialMap.end())
+ return itr->second.Flags & SOCIAL_FLAG_IGNORED;
+ return false;
+}
+
+SocialMgr::SocialMgr()
+{
+
+}
+
+SocialMgr::~SocialMgr()
+{
+
+}
+
+void SocialMgr::RemovePlayerSocial(uint32 guid)
+{
+ SocialMap::iterator itr = m_socialMap.find(guid);
+ if(itr != m_socialMap.end())
+ m_socialMap.erase(itr);
+}
+
+void SocialMgr::GetFriendInfo(Player *player, uint32 friendGUID, FriendInfo &friendInfo)
+{
+ if(!player)
+ return;
+
+ Player *pFriend = ObjectAccessor::FindPlayer(friendGUID);
+
+ uint32 team = player->GetTeam();
+ uint32 security = player->GetSession()->GetSecurity();
+ bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST);
+ bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST) || security > SEC_PLAYER;
+
+ PlayerSocialMap::iterator itr = player->GetSocial()->m_playerSocialMap.find(friendGUID);
+ if(itr != player->GetSocial()->m_playerSocialMap.end())
+ friendInfo.Note = itr->second.Note;
+
+ // PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters
+ // MODERATOR, GAME MASTER, ADMINISTRATOR can see all
+ if( pFriend && pFriend->GetName() &&
+ ( security > SEC_PLAYER ||
+ ( pFriend->GetTeam() == team || allowTwoSideWhoList ) &&
+ ( pFriend->GetSession()->GetSecurity() == SEC_PLAYER || gmInWhoList && pFriend->IsVisibleGloballyFor(player) )))
+ {
+ friendInfo.Status = FRIEND_STATUS_ONLINE;
+ if(pFriend->isAFK())
+ friendInfo.Status = FRIEND_STATUS_AFK;
+ if(pFriend->isDND())
+ friendInfo.Status = FRIEND_STATUS_DND;
+ friendInfo.Area = pFriend->GetZoneId();
+ friendInfo.Level = pFriend->getLevel();
+ friendInfo.Class = pFriend->getClass();
+ }
+ else
+ {
+ friendInfo.Status = FRIEND_STATUS_OFFLINE;
+ friendInfo.Area = 0;
+ friendInfo.Level = 0;
+ friendInfo.Class = 0;
+ }
+}
+
+void SocialMgr::MakeFriendStatusPacket(FriendsResult result, uint32 guid, WorldPacket *data)
+{
+ data->Initialize(SMSG_FRIEND_STATUS, 5);
+ *data << uint8(result);
+ *data << uint64(guid);
+}
+
+void SocialMgr::SendFriendStatus(Player *player, FriendsResult result, uint32 friend_guid, std::string name, bool broadcast)
+{
+ FriendInfo fi;
+
+ WorldPacket data;
+ MakeFriendStatusPacket(result, friend_guid, &data);
+ GetFriendInfo(player, friend_guid, fi);
+ switch(result)
+ {
+ case FRIEND_ADDED_OFFLINE:
+ case FRIEND_ADDED_ONLINE:
+ data << fi.Note;
+ break;
+ }
+
+ switch(result)
+ {
+ case FRIEND_ADDED_ONLINE:
+ case FRIEND_ONLINE:
+ data << uint8(fi.Status);
+ data << uint32(fi.Area);
+ data << uint32(fi.Level);
+ data << uint32(fi.Class);
+ break;
+ }
+
+ if(broadcast)
+ BroadcastToFriendListers(player, &data);
+ else
+ player->GetSession()->SendPacket(&data);
+}
+
+void SocialMgr::BroadcastToFriendListers(Player *player, WorldPacket *packet)
+{
+ if(!player)
+ return;
+
+ uint32 team = player->GetTeam();
+ uint32 security = player->GetSession()->GetSecurity();
+ uint32 guid = player->GetGUIDLow();
+ bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST);
+ bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST);
+
+ for(SocialMap::iterator itr = m_socialMap.begin(); itr != m_socialMap.end(); ++itr)
+ {
+ PlayerSocialMap::iterator itr2 = itr->second.m_playerSocialMap.find(guid);
+ if(itr2 != itr->second.m_playerSocialMap.end() && (itr2->second.Flags & SOCIAL_FLAG_FRIEND))
+ {
+ Player *pFriend = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+
+ // PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters
+ // MODERATOR, GAME MASTER, ADMINISTRATOR can see all
+ if( pFriend && pFriend->IsInWorld() &&
+ ( pFriend->GetSession()->GetSecurity() > SEC_PLAYER ||
+ ( pFriend->GetTeam() == team || allowTwoSideWhoList ) &&
+ (security == SEC_PLAYER || gmInWhoList && player->IsVisibleGloballyFor(pFriend) )))
+ {
+ pFriend->GetSession()->SendPacket(packet);
+ }
+ }
+ }
+}
+
+PlayerSocial *SocialMgr::LoadFromDB(QueryResult *result, uint32 guid)
+{
+ PlayerSocial *social = &m_socialMap[guid];
+ social->SetPlayerGUID(guid);
+
+ if(!result)
+ return social;
+
+ uint32 friend_guid = 0;
+ uint32 flags = 0;
+ std::string note = "";
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ friend_guid = fields[0].GetUInt32();
+ flags = fields[1].GetUInt32();
+ note = fields[2].GetCppString();
+
+ social->m_playerSocialMap[friend_guid] = FriendInfo(flags, note);
+
+ // client limit
+ if(social->m_playerSocialMap.size() >= 50)
+ break;
+ }
+ while( result->NextRow() );
+ delete result;
+ return social;
+}
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 3be0b6989b2..ba5b5d97b47 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -986,7 +986,7 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) if( !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) )
{
- if(!unit->IsStandState() && !unit->hasUnitState(UNIT_STAT_STUNDED))
+ if(!unit->IsStandState() && !unit->hasUnitState(UNIT_STAT_STUNNED))
unit->SetStandState(PLAYER_STATE_NONE);
if(!unit->isInCombat() && unit->GetTypeId() != TYPEID_PLAYER && ((Creature*)unit)->AI())
@@ -2407,7 +2407,7 @@ void Spell::update(uint32 difftime) cancel();
// check for incapacitating player states
- if( m_caster->hasUnitState(UNIT_STAT_STUNDED | UNIT_STAT_CONFUSED))
+ if( m_caster->hasUnitState(UNIT_STAT_STUNNED | UNIT_STAT_CONFUSED))
cancel();
// check if player has turned if flag is set
diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index e5abe4d842f..d8623a6ebe2 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -1,6360 +1,6360 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "Common.h" -#include "Database/DatabaseEnv.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Opcodes.h" -#include "Log.h" -#include "UpdateMask.h" -#include "World.h" -#include "ObjectMgr.h" -#include "SpellMgr.h" -#include "Player.h" -#include "Unit.h" -#include "Spell.h" -#include "SpellAuras.h" -#include "DynamicObject.h" -#include "Group.h" -#include "UpdateData.h" -#include "MapManager.h" -#include "ObjectAccessor.h" -#include "Policies/SingletonImp.h" -#include "Totem.h" -#include "Creature.h" -#include "Formulas.h" -#include "BattleGround.h" -#include "CreatureAI.h" -#include "Util.h" -#include "GridNotifiers.h" -#include "GridNotifiersImpl.h" -#include "CellImpl.h" - -#define NULL_AURA_SLOT 0xFF - -pAuraHandler AuraHandler[TOTAL_AURAS]= -{ - &Aura::HandleNULL, // 0 SPELL_AURA_NONE - &Aura::HandleBindSight, // 1 SPELL_AURA_BIND_SIGHT - &Aura::HandleModPossess, // 2 SPELL_AURA_MOD_POSSESS - &Aura::HandlePeriodicDamage, // 3 SPELL_AURA_PERIODIC_DAMAGE - &Aura::HandleAuraDummy, // 4 SPELL_AURA_DUMMY - &Aura::HandleModConfuse, // 5 SPELL_AURA_MOD_CONFUSE - &Aura::HandleModCharm, // 6 SPELL_AURA_MOD_CHARM - &Aura::HandleModFear, // 7 SPELL_AURA_MOD_FEAR - &Aura::HandlePeriodicHeal, // 8 SPELL_AURA_PERIODIC_HEAL - &Aura::HandleModAttackSpeed, // 9 SPELL_AURA_MOD_ATTACKSPEED - &Aura::HandleModThreat, // 10 SPELL_AURA_MOD_THREAT - &Aura::HandleModTaunt, // 11 SPELL_AURA_MOD_TAUNT - &Aura::HandleAuraModStun, // 12 SPELL_AURA_MOD_STUN - &Aura::HandleModDamageDone, // 13 SPELL_AURA_MOD_DAMAGE_DONE - &Aura::HandleNoImmediateEffect, // 14 SPELL_AURA_MOD_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus - &Aura::HandleNoImmediateEffect, // 15 SPELL_AURA_DAMAGE_SHIELD implemented in Unit::DoAttackDamage - &Aura::HandleModStealth, // 16 SPELL_AURA_MOD_STEALTH - &Aura::HandleNoImmediateEffect, // 17 SPELL_AURA_MOD_STEALTH_DETECT - &Aura::HandleInvisibility, // 18 SPELL_AURA_MOD_INVISIBILITY - &Aura::HandleInvisibilityDetect, // 19 SPELL_AURA_MOD_INVISIBILITY_DETECTION - &Aura::HandleAuraModTotalHealthPercentRegen, // 20 SPELL_AURA_OBS_MOD_HEALTH - &Aura::HandleAuraModTotalManaPercentRegen, // 21 SPELL_AURA_OBS_MOD_MANA - &Aura::HandleAuraModResistance, // 22 SPELL_AURA_MOD_RESISTANCE - &Aura::HandlePeriodicTriggerSpell, // 23 SPELL_AURA_PERIODIC_TRIGGER_SPELL - &Aura::HandlePeriodicEnergize, // 24 SPELL_AURA_PERIODIC_ENERGIZE - &Aura::HandleAuraModPacify, // 25 SPELL_AURA_MOD_PACIFY - &Aura::HandleAuraModRoot, // 26 SPELL_AURA_MOD_ROOT - &Aura::HandleAuraModSilence, // 27 SPELL_AURA_MOD_SILENCE - &Aura::HandleNoImmediateEffect, // 28 SPELL_AURA_REFLECT_SPELLS implement in Unit::SpellHitResult - &Aura::HandleAuraModStat, // 29 SPELL_AURA_MOD_STAT - &Aura::HandleAuraModSkill, // 30 SPELL_AURA_MOD_SKILL - &Aura::HandleAuraModIncreaseSpeed, // 31 SPELL_AURA_MOD_INCREASE_SPEED - &Aura::HandleAuraModIncreaseMountedSpeed, // 32 SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED - &Aura::HandleAuraModDecreaseSpeed, // 33 SPELL_AURA_MOD_DECREASE_SPEED - &Aura::HandleAuraModIncreaseHealth, // 34 SPELL_AURA_MOD_INCREASE_HEALTH - &Aura::HandleAuraModIncreaseEnergy, // 35 SPELL_AURA_MOD_INCREASE_ENERGY - &Aura::HandleAuraModShapeshift, // 36 SPELL_AURA_MOD_SHAPESHIFT - &Aura::HandleAuraModEffectImmunity, // 37 SPELL_AURA_EFFECT_IMMUNITY - &Aura::HandleAuraModStateImmunity, // 38 SPELL_AURA_STATE_IMMUNITY - &Aura::HandleAuraModSchoolImmunity, // 39 SPELL_AURA_SCHOOL_IMMUNITY - &Aura::HandleAuraModDmgImmunity, // 40 SPELL_AURA_DAMAGE_IMMUNITY - &Aura::HandleAuraModDispelImmunity, // 41 SPELL_AURA_DISPEL_IMMUNITY - &Aura::HandleAuraProcTriggerSpell, // 42 SPELL_AURA_PROC_TRIGGER_SPELL implemented in Unit::ProcDamageAndSpellFor and Unit::HandleProcTriggerSpell - &Aura::HandleNoImmediateEffect, // 43 SPELL_AURA_PROC_TRIGGER_DAMAGE implemented in Unit::ProcDamageAndSpellFor - &Aura::HandleAuraTrackCreatures, // 44 SPELL_AURA_TRACK_CREATURES - &Aura::HandleAuraTrackResources, // 45 SPELL_AURA_TRACK_RESOURCES - &Aura::HandleUnused, // 46 SPELL_AURA_MOD_PARRY_SKILL obsolete? - &Aura::HandleAuraModParryPercent, // 47 SPELL_AURA_MOD_PARRY_PERCENT - &Aura::HandleUnused, // 48 SPELL_AURA_MOD_DODGE_SKILL obsolete? - &Aura::HandleAuraModDodgePercent, // 49 SPELL_AURA_MOD_DODGE_PERCENT - &Aura::HandleUnused, // 50 SPELL_AURA_MOD_BLOCK_SKILL obsolete? - &Aura::HandleAuraModBlockPercent, // 51 SPELL_AURA_MOD_BLOCK_PERCENT - &Aura::HandleAuraModCritPercent, // 52 SPELL_AURA_MOD_CRIT_PERCENT - &Aura::HandlePeriodicLeech, // 53 SPELL_AURA_PERIODIC_LEECH - &Aura::HandleModHitChance, // 54 SPELL_AURA_MOD_HIT_CHANCE - &Aura::HandleModSpellHitChance, // 55 SPELL_AURA_MOD_SPELL_HIT_CHANCE - &Aura::HandleAuraTransform, // 56 SPELL_AURA_TRANSFORM - &Aura::HandleModSpellCritChance, // 57 SPELL_AURA_MOD_SPELL_CRIT_CHANCE - &Aura::HandleAuraModIncreaseSwimSpeed, // 58 SPELL_AURA_MOD_INCREASE_SWIM_SPEED - &Aura::HandleNoImmediateEffect, // 59 SPELL_AURA_MOD_DAMAGE_DONE_CREATURE implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus - &Aura::HandleAuraModPacifyAndSilence, // 60 SPELL_AURA_MOD_PACIFY_SILENCE - &Aura::HandleAuraModScale, // 61 SPELL_AURA_MOD_SCALE - &Aura::HandleNULL, // 62 SPELL_AURA_PERIODIC_HEALTH_FUNNEL - &Aura::HandleUnused, // 63 SPELL_AURA_PERIODIC_MANA_FUNNEL obsolete? - &Aura::HandlePeriodicManaLeech, // 64 SPELL_AURA_PERIODIC_MANA_LEECH - &Aura::HandleModCastingSpeed, // 65 SPELL_AURA_MOD_CASTING_SPEED - &Aura::HandleFeignDeath, // 66 SPELL_AURA_FEIGN_DEATH - &Aura::HandleAuraModDisarm, // 67 SPELL_AURA_MOD_DISARM - &Aura::HandleAuraModStalked, // 68 SPELL_AURA_MOD_STALKED - &Aura::HandleSchoolAbsorb, // 69 SPELL_AURA_SCHOOL_ABSORB implemented in Unit::CalcAbsorbResist - &Aura::HandleUnused, // 70 SPELL_AURA_EXTRA_ATTACKS Useless, used by only one spell that has only visual effect - &Aura::HandleModSpellCritChanceShool, // 71 SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL - &Aura::HandleModPowerCostPCT, // 72 SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT - &Aura::HandleModPowerCost, // 73 SPELL_AURA_MOD_POWER_COST_SCHOOL - &Aura::HandleNoImmediateEffect, // 74 SPELL_AURA_REFLECT_SPELLS_SCHOOL implemented in Unit::SpellHitResult - &Aura::HandleNoImmediateEffect, // 75 SPELL_AURA_MOD_LANGUAGE - &Aura::HandleFarSight, // 76 SPELL_AURA_FAR_SIGHT - &Aura::HandleModMechanicImmunity, // 77 SPELL_AURA_MECHANIC_IMMUNITY - &Aura::HandleAuraMounted, // 78 SPELL_AURA_MOUNTED - &Aura::HandleModDamagePercentDone, // 79 SPELL_AURA_MOD_DAMAGE_PERCENT_DONE - &Aura::HandleModPercentStat, // 80 SPELL_AURA_MOD_PERCENT_STAT - &Aura::HandleNoImmediateEffect, // 81 SPELL_AURA_SPLIT_DAMAGE_PCT - &Aura::HandleWaterBreathing, // 82 SPELL_AURA_WATER_BREATHING - &Aura::HandleModBaseResistance, // 83 SPELL_AURA_MOD_BASE_RESISTANCE - &Aura::HandleModRegen, // 84 SPELL_AURA_MOD_REGEN - &Aura::HandleModPowerRegen, // 85 SPELL_AURA_MOD_POWER_REGEN - &Aura::HandleChannelDeathItem, // 86 SPELL_AURA_CHANNEL_DEATH_ITEM - &Aura::HandleNoImmediateEffect, // 87 SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus - &Aura::HandleNoImmediateEffect, // 88 SPELL_AURA_MOD_HEALTH_REGEN_PERCENT - &Aura::HandlePeriodicDamagePCT, // 89 SPELL_AURA_PERIODIC_DAMAGE_PERCENT - &Aura::HandleUnused, // 90 SPELL_AURA_MOD_RESIST_CHANCE Useless - &Aura::HandleNoImmediateEffect, // 91 SPELL_AURA_MOD_DETECT_RANGE implemented in Creature::GetAttackDistance - &Aura::HandlePreventFleeing, // 92 SPELL_AURA_PREVENTS_FLEEING - &Aura::HandleModUnattackable, // 93 SPELL_AURA_MOD_UNATTACKABLE - &Aura::HandleNoImmediateEffect, // 94 SPELL_AURA_INTERRUPT_REGEN implemented in Player::RegenerateAll - &Aura::HandleAuraGhost, // 95 SPELL_AURA_GHOST - &Aura::HandleNoImmediateEffect, // 96 SPELL_AURA_SPELL_MAGNET implemented in Spell::SelectMagnetTarget - &Aura::HandleManaShield, // 97 SPELL_AURA_MANA_SHIELD implemented in Unit::CalcAbsorbResist - &Aura::HandleAuraModSkill, // 98 SPELL_AURA_MOD_SKILL_TALENT - &Aura::HandleAuraModAttackPower, // 99 SPELL_AURA_MOD_ATTACK_POWER - &Aura::HandleUnused, //100 SPELL_AURA_AURAS_VISIBLE obsolete? all player can see all auras now - &Aura::HandleModResistancePercent, //101 SPELL_AURA_MOD_RESISTANCE_PCT - &Aura::HandleNoImmediateEffect, //102 SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS implemented in Unit::MeleeDamageBonus - &Aura::HandleAuraModTotalThreat, //103 SPELL_AURA_MOD_TOTAL_THREAT - &Aura::HandleAuraWaterWalk, //104 SPELL_AURA_WATER_WALK - &Aura::HandleAuraFeatherFall, //105 SPELL_AURA_FEATHER_FALL - &Aura::HandleAuraHover, //106 SPELL_AURA_HOVER - &Aura::HandleAddModifier, //107 SPELL_AURA_ADD_FLAT_MODIFIER - &Aura::HandleAddModifier, //108 SPELL_AURA_ADD_PCT_MODIFIER - &Aura::HandleNoImmediateEffect, //109 SPELL_AURA_ADD_TARGET_TRIGGER - &Aura::HandleModPowerRegenPCT, //110 SPELL_AURA_MOD_POWER_REGEN_PERCENT - &Aura::HandleNULL, //111 SPELL_AURA_ADD_CASTER_HIT_TRIGGER - &Aura::HandleNoImmediateEffect, //112 SPELL_AURA_OVERRIDE_CLASS_SCRIPTS - &Aura::HandleNoImmediateEffect, //113 SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus - &Aura::HandleNoImmediateEffect, //114 SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT implemented in Unit::MeleeDamageBonus - &Aura::HandleAuraHealing, //115 SPELL_AURA_MOD_HEALING - &Aura::HandleNoImmediateEffect, //116 SPELL_AURA_MOD_REGEN_DURING_COMBAT - &Aura::HandleNoImmediateEffect, //117 SPELL_AURA_MOD_MECHANIC_RESISTANCE implemented in Unit::MagicSpellHitResult - &Aura::HandleAuraHealingPct, //118 SPELL_AURA_MOD_HEALING_PCT - &Aura::HandleUnused, //119 SPELL_AURA_SHARE_PET_TRACKING useless - &Aura::HandleAuraUntrackable, //120 SPELL_AURA_UNTRACKABLE - &Aura::HandleAuraEmpathy, //121 SPELL_AURA_EMPATHY - &Aura::HandleModOffhandDamagePercent, //122 SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT - &Aura::HandleModTargetResistance, //123 SPELL_AURA_MOD_TARGET_RESISTANCE - &Aura::HandleAuraModRangedAttackPower, //124 SPELL_AURA_MOD_RANGED_ATTACK_POWER - &Aura::HandleNoImmediateEffect, //125 SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus - &Aura::HandleNoImmediateEffect, //126 SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT implemented in Unit::MeleeDamageBonus - &Aura::HandleNoImmediateEffect, //127 SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS implemented in Unit::MeleeDamageBonus - &Aura::HandleModPossessPet, //128 SPELL_AURA_MOD_POSSESS_PET - &Aura::HandleAuraModIncreaseSpeed, //129 SPELL_AURA_MOD_SPEED_ALWAYS - &Aura::HandleAuraModIncreaseMountedSpeed, //130 SPELL_AURA_MOD_MOUNTED_SPEED_ALWAYS - &Aura::HandleNoImmediateEffect, //131 SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS implemented in Unit::MeleeDamageBonus - &Aura::HandleAuraModIncreaseEnergyPercent, //132 SPELL_AURA_MOD_INCREASE_ENERGY_PERCENT - &Aura::HandleAuraModIncreaseHealthPercent, //133 SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT - &Aura::HandleAuraModRegenInterrupt, //134 SPELL_AURA_MOD_MANA_REGEN_INTERRUPT - &Aura::HandleModHealingDone, //135 SPELL_AURA_MOD_HEALING_DONE - &Aura::HandleAuraHealingPct, //136 SPELL_AURA_MOD_HEALING_DONE_PERCENT implemented in Unit::SpellHealingBonus - &Aura::HandleModTotalPercentStat, //137 SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE - &Aura::HandleHaste, //138 SPELL_AURA_MOD_HASTE - &Aura::HandleForceReaction, //139 SPELL_AURA_FORCE_REACTION - &Aura::HandleAuraModRangedHaste, //140 SPELL_AURA_MOD_RANGED_HASTE - &Aura::HandleRangedAmmoHaste, //141 SPELL_AURA_MOD_RANGED_AMMO_HASTE - &Aura::HandleAuraModBaseResistancePCT, //142 SPELL_AURA_MOD_BASE_RESISTANCE_PCT - &Aura::HandleAuraModResistanceExclusive, //143 SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE - &Aura::HandleNoImmediateEffect, //144 SPELL_AURA_SAFE_FALL implemented in WorldSession::HandleMovementOpcodes - &Aura::HandleUnused, //145 SPELL_AURA_CHARISMA obsolete? - &Aura::HandleUnused, //146 SPELL_AURA_PERSUADED obsolete? - &Aura::HandleNULL, //147 SPELL_AURA_ADD_CREATURE_IMMUNITY - &Aura::HandleAuraRetainComboPoints, //148 SPELL_AURA_RETAIN_COMBO_POINTS - &Aura::HandleNoImmediateEffect, //149 SPELL_AURA_RESIST_PUSHBACK - &Aura::HandleShieldBlockValue, //150 SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT - &Aura::HandleAuraTrackStealthed, //151 SPELL_AURA_TRACK_STEALTHED - &Aura::HandleNoImmediateEffect, //152 SPELL_AURA_MOD_DETECTED_RANGE implemented in Creature::GetAttackDistance - &Aura::HandleNoImmediateEffect, //153 SPELL_AURA_SPLIT_DAMAGE_FLAT - &Aura::HandleNoImmediateEffect, //154 SPELL_AURA_MOD_STEALTH_LEVEL - &Aura::HandleNoImmediateEffect, //155 SPELL_AURA_MOD_WATER_BREATHING - &Aura::HandleNoImmediateEffect, //156 SPELL_AURA_MOD_REPUTATION_GAIN - &Aura::HandleNULL, //157 SPELL_AURA_PET_DAMAGE_MULTI - &Aura::HandleShieldBlockValue, //158 SPELL_AURA_MOD_SHIELD_BLOCKVALUE - &Aura::HandleNoImmediateEffect, //159 SPELL_AURA_NO_PVP_CREDIT only for Honorless Target spell - &Aura::HandleNoImmediateEffect, //160 SPELL_AURA_MOD_AOE_AVOIDANCE implemended in Unit::MagicSpellHitResult - &Aura::HandleNoImmediateEffect, //161 SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT - &Aura::HandleAuraPowerBurn, //162 SPELL_AURA_POWER_BURN_MANA - &Aura::HandleNoImmediateEffect, //163 SPELL_AURA_MOD_CRIT_DAMAGE_BONUS_MELEE - &Aura::HandleUnused, //164 useless, only one test spell - &Aura::HandleAuraAttackPowerAttacker, //165 SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS implemented in Unit::MeleeDamageBonus - &Aura::HandleAuraModAttackPowerPercent, //166 SPELL_AURA_MOD_ATTACK_POWER_PCT - &Aura::HandleAuraModRangedAttackPowerPercent, //167 SPELL_AURA_MOD_RANGED_ATTACK_POWER_PCT - &Aura::HandleNoImmediateEffect, //168 SPELL_AURA_MOD_DAMAGE_DONE_VERSUS implemented in Unit::SpellDamageBonus, Unit::MeleeDamageBonus - &Aura::HandleNoImmediateEffect, //169 SPELL_AURA_MOD_CRIT_PERCENT_VERSUS implemented in Unit::DealDamageBySchool, Unit::DoAttackDamage, Unit::SpellCriticalBonus - &Aura::HandleNULL, //170 SPELL_AURA_DETECT_AMORE only for Detect Amore spell - &Aura::HandleAuraModIncreaseSpeed, //171 SPELL_AURA_MOD_SPEED_NOT_STACK - &Aura::HandleAuraModIncreaseMountedSpeed, //172 SPELL_AURA_MOD_MOUNTED_SPEED_NOT_STACK - &Aura::HandleUnused, //173 SPELL_AURA_ALLOW_CHAMPION_SPELLS only for Proclaim Champion spell - &Aura::HandleModSpellDamagePercentFromStat, //174 SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT implemented in Unit::SpellBaseDamageBonus (by defeult intelect, dependent from SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT) - &Aura::HandleModSpellHealingPercentFromStat, //175 SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT implemented in Unit::SpellBaseHealingBonus - &Aura::HandleSpiritOfRedemption, //176 SPELL_AURA_SPIRIT_OF_REDEMPTION only for Spirit of Redemption spell, die at aura end - &Aura::HandleNULL, //177 SPELL_AURA_AOE_CHARM - &Aura::HandleNoImmediateEffect, //178 SPELL_AURA_MOD_DEBUFF_RESISTANCE implemented in Unit::MagicSpellHitResult - &Aura::HandleNoImmediateEffect, //179 SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE implemented in Unit::SpellCriticalBonus - &Aura::HandleNoImmediateEffect, //180 SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS implemented in Unit::SpellDamageBonus - &Aura::HandleUnused, //181 SPELL_AURA_MOD_FLAT_SPELL_CRIT_DAMAGE_VERSUS unused - &Aura::HandleAuraModResistenceOfStatPercent, //182 SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT - &Aura::HandleNULL, //183 SPELL_AURA_MOD_CRITICAL_THREAT - &Aura::HandleNoImmediateEffect, //184 SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst - &Aura::HandleNoImmediateEffect, //185 SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst - &Aura::HandleNoImmediateEffect, //186 SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE implemented in Unit::MagicSpellHitResult - &Aura::HandleNoImmediateEffect, //187 SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE implemended in Unit::GetUnitCriticalChance - &Aura::HandleNoImmediateEffect, //188 SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE implemented in Unit::GetUnitCriticalChance - &Aura::HandleModRating, //189 SPELL_AURA_MOD_RATING - &Aura::HandleNULL, //190 SPELL_AURA_MOD_FACTION_REPUTATION_GAIN - &Aura::HandleAuraModUseNormalSpeed, //191 SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED - &Aura::HandleModMeleeRangedSpeedPct, //192 SPELL_AURA_HASTE_MELEE - &Aura::HandleModCombatSpeedPct, //193 SPELL_AURA_MELEE_SLOW (in fact combat (any type attack) speed pct) - &Aura::HandleUnused, //194 SPELL_AURA_MOD_DEPRICATED_1 not used now (old SPELL_AURA_MOD_SPELL_DAMAGE_OF_INTELLECT) - &Aura::HandleUnused, //195 SPELL_AURA_MOD_DEPRICATED_2 not used now (old SPELL_AURA_MOD_SPELL_HEALING_OF_INTELLECT) - &Aura::HandleNULL, //196 SPELL_AURA_MOD_COOLDOWN - &Aura::HandleNoImmediateEffect, //197 SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE implemented in Unit::SpellCriticalBonus Unit::GetUnitCriticalChance - &Aura::HandleUnused, //198 SPELL_AURA_MOD_ALL_WEAPON_SKILLS - &Aura::HandleNoImmediateEffect, //199 SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT implemented in Unit::MagicSpellHitResult - &Aura::HandleNoImmediateEffect, //200 SPELL_AURA_MOD_XP_PCT implemented in Player::GiveXP - &Aura::HandleAuraAllowFlight, //201 SPELL_AURA_FLY this aura enable flight mode... - &Aura::HandleNoImmediateEffect, //202 SPELL_AURA_CANNOT_BE_DODGED implemented in Unit::RollPhysicalOutcomeAgainst - &Aura::HandleNoImmediateEffect, //203 SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE implemented in Unit::DoAttackDamage - &Aura::HandleNoImmediateEffect, //204 SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE implemented in Unit::DoAttackDamage - &Aura::HandleNULL, //205 vulnerable to school dmg? - &Aura::HandleNULL, //206 SPELL_AURA_MOD_SPEED_MOUNTED - &Aura::HandleAuraModIncreaseFlightSpeed, //207 SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED - &Aura::HandleAuraModIncreaseFlightSpeed, //208 SPELL_AURA_MOD_SPEED_FLIGHT, used only in spell: Flight Form (Passive) - &Aura::HandleAuraModIncreaseFlightSpeed, //209 SPELL_AURA_MOD_FLIGHT_SPEED_ALWAYS - &Aura::HandleNULL, //210 Commentator's Command - &Aura::HandleAuraModIncreaseFlightSpeed, //211 SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK - &Aura::HandleAuraModRangedAttackPowerOfStatPercent, //212 SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT - &Aura::HandleNoImmediateEffect, //213 SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT implemented in Player::RewardRage - &Aura::HandleNULL, //214 Tamed Pet Passive - &Aura::HandleArenaPreparation, //215 SPELL_AURA_ARENA_PREPARATION - &Aura::HandleModCastingSpeed, //216 SPELL_AURA_HASTE_SPELLS - &Aura::HandleUnused, //217 unused - &Aura::HandleAuraModRangedHaste, //218 SPELL_AURA_HASTE_RANGED - &Aura::HandleModManaRegen, //219 SPELL_AURA_MOD_MANA_REGEN_FROM_STAT - &Aura::HandleNULL, //220 SPELL_AURA_MOD_RATING_FROM_STAT - &Aura::HandleNULL, //221 ignored - &Aura::HandleUnused, //222 unused - &Aura::HandleNULL, //223 Cold Stare - &Aura::HandleUnused, //224 unused - &Aura::HandleNoImmediateEffect, //225 SPELL_AURA_PRAYER_OF_MENDING - &Aura::HandleAuraPeriodicDummy, //226 SPELL_AURA_PERIODIC_DUMMY - &Aura::HandleNULL, //227 periodic trigger spell - &Aura::HandleNoImmediateEffect, //228 stealth detection - &Aura::HandleNULL, //229 SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE - &Aura::HandleAuraModIncreaseMaxHealth, //230 Commanding Shout - &Aura::HandleNULL, //231 - &Aura::HandleNoImmediateEffect, //232 SPELL_AURA_MECHANIC_DURATION_MOD implement in Unit::CalculateSpellDuration - &Aura::HandleNULL, //233 set model id to the one of the creature with id m_modifier.m_miscvalue - &Aura::HandleNoImmediateEffect, //234 SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK implement in Unit::CalculateSpellDuration - &Aura::HandleAuraModDispelResist, //235 SPELL_AURA_MOD_DISPEL_RESIST implement in Unit::MagicSpellHitResult - &Aura::HandleUnused, //236 unused - &Aura::HandleModSpellDamagePercentFromAttackPower, //237 SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER implemented in Unit::SpellBaseDamageBonus - &Aura::HandleModSpellHealingPercentFromAttackPower, //238 SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER implemented in Unit::SpellBaseHealingBonus - &Aura::HandleAuraModScale, //239 SPELL_AURA_MOD_SCALE_2 only in Noggenfogger Elixir (16595) before 2.3.0 aura 61 - &Aura::HandleAuraModExpertise, //240 SPELL_AURA_MOD_EXPERTISE - &Aura::HandleForceMoveForward, //241 Forces the player to move forward - &Aura::HandleUnused, //242 SPELL_AURA_MOD_SPELL_DAMAGE_FROM_HEALING - &Aura::HandleUnused, //243 used by two test spells - &Aura::HandleComprehendLanguage, //244 Comprehend language - &Aura::HandleUnused, //245 SPELL_AURA_MOD_DURATION_OF_MAGIC_EFFECTS - &Aura::HandleUnused, //246 unused - &Aura::HandleUnused, //247 unused - &Aura::HandleNoImmediateEffect, //248 SPELL_AURA_MOD_COMBAT_RESULT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst - &Aura::HandleNULL, //249 - &Aura::HandleAuraModIncreaseHealth, //250 SPELL_AURA_MOD_INCREASE_HEALTH_2 - &Aura::HandleNULL, //251 SPELL_AURA_MOD_ENEMY_DODGE - &Aura::HandleUnused, //252 unused - &Aura::HandleUnused, //253 unused - &Aura::HandleUnused, //254 unused - &Aura::HandleUnused, //255 unused - &Aura::HandleUnused, //256 unused - &Aura::HandleUnused, //257 unused - &Aura::HandleUnused, //258 unused - &Aura::HandleUnused, //259 unused - &Aura::HandleUnused, //260 unused - &Aura::HandleNULL //261 SPELL_AURA_261 some phased state (44856 spell) -}; - -Aura::Aura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster, Item* castItem) : -m_procCharges(0), m_spellmod(NULL), m_effIndex(eff), m_caster_guid(0), m_target(target), -m_timeCla(1000), m_castItemGuid(castItem?castItem->GetGUID():0), m_auraSlot(MAX_AURAS), -m_positive(false), m_permanent(false), m_isPeriodic(false), m_isTrigger(false), m_isAreaAura(false), -m_isPersistent(false), m_updated(false), m_removeMode(AURA_REMOVE_BY_DEFAULT), m_isRemovedOnShapeLost(true), m_in_use(false), -m_periodicTimer(0), m_PeriodicEventId(0), m_AuraDRGroup(DIMINISHING_NONE) -{ - assert(target); - - assert(spellproto && spellproto == sSpellStore.LookupEntry( spellproto->Id ) && "`info` must be pointer to sSpellStore element"); - - m_spellProto = spellproto; - - m_currentBasePoints = currentBasePoints ? *currentBasePoints : m_spellProto->EffectBasePoints[eff]; - - m_isPassive = IsPassiveSpell(GetId()); - m_positive = IsPositiveEffect(GetId(), m_effIndex); - - m_applyTime = time(NULL); - - int32 damage; - if(!caster) - { - m_caster_guid = target->GetGUID(); - damage = m_currentBasePoints+1; // stored value-1 - m_maxduration = target->CalculateSpellDuration(m_spellProto, m_effIndex, target); - } - else - { - m_caster_guid = caster->GetGUID(); - - damage = caster->CalculateSpellDamage(m_spellProto,m_effIndex,m_currentBasePoints,target); - m_maxduration = caster->CalculateSpellDuration(m_spellProto, m_effIndex, target); - - if (!damage && castItem && castItem->GetItemSuffixFactor()) - { - ItemRandomSuffixEntry const *item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(castItem->GetItemRandomPropertyId())); - if(item_rand_suffix) - { - for (int k=0; k<3; k++) - { - SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(item_rand_suffix->enchant_id[k]); - if(pEnchant) - { - for (int t=0; t<3; t++) - if(pEnchant->spellid[t] == m_spellProto->Id) - { - damage = uint32((item_rand_suffix->prefix[k]*castItem->GetItemSuffixFactor()) / 10000 ); - break; - } - } - - if(damage) - break; - } - } - } - } - - if(m_maxduration == -1 || m_isPassive && m_spellProto->DurationIndex == 0) - m_permanent = true; - - Player* modOwner = caster ? caster->GetSpellModOwner() : NULL; - - if(!m_permanent && modOwner) - modOwner->ApplySpellMod(GetId(), SPELLMOD_DURATION, m_maxduration); - - m_duration = m_maxduration; - - if(modOwner) - modOwner->ApplySpellMod(GetId(), SPELLMOD_ACTIVATION_TIME, m_periodicTimer); - - sLog.outDebug("Aura: construct Spellid : %u, Aura : %u Duration : %d Target : %d Damage : %d", m_spellProto->Id, m_spellProto->EffectApplyAuraName[eff], m_maxduration, m_spellProto->EffectImplicitTargetA[eff],damage); - - m_effIndex = eff; - SetModifier(AuraType(m_spellProto->EffectApplyAuraName[eff]), damage, m_spellProto->EffectAmplitude[eff], m_spellProto->EffectMiscValue[eff]); - - m_isDeathPersist = IsDeathPersistentSpell(m_spellProto); - - if(m_spellProto->procCharges) - { - m_procCharges = m_spellProto->procCharges; - - if(modOwner) - modOwner->ApplySpellMod(GetId(), SPELLMOD_CHARGES, m_procCharges); - } - else - m_procCharges = -1; - - m_isRemovedOnShapeLost = (m_caster_guid==m_target->GetGUID() && m_spellProto->Stances && - !(m_spellProto->AttributesEx2 & 0x80000) && !(m_spellProto->Attributes & 0x10000)); -} - -Aura::~Aura() -{ -} - -AreaAura::AreaAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, -Unit *caster, Item* castItem) : Aura(spellproto, eff, currentBasePoints, target, caster, castItem) -{ - m_isAreaAura = true; - - // caster==NULL in constructor args if target==caster in fact - Unit* caster_ptr = caster ? caster : target; - - m_radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(GetSpellProto()->EffectRadiusIndex[m_effIndex])); - if(Player* modOwner = caster_ptr->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), SPELLMOD_RADIUS, m_radius); - - switch(spellproto->Effect[eff]) - { - case SPELL_EFFECT_APPLY_AREA_AURA_PARTY: - m_areaAuraType = AREA_AURA_PARTY; - if(target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->isTotem()) - m_modifier.m_auraname = SPELL_AURA_NONE; - break; - case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND: - m_areaAuraType = AREA_AURA_FRIEND; - break; - case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY: - m_areaAuraType = AREA_AURA_ENEMY; - if(target == caster_ptr) - m_modifier.m_auraname = SPELL_AURA_NONE; // Do not do any effect on self - break; - case SPELL_EFFECT_APPLY_AREA_AURA_PET: - m_areaAuraType = AREA_AURA_PET; - break; - case SPELL_EFFECT_APPLY_AREA_AURA_OWNER: - m_areaAuraType = AREA_AURA_OWNER; - if(target == caster_ptr) - m_modifier.m_auraname = SPELL_AURA_NONE; - break; - default: - sLog.outError("Wrong spell effect in AreaAura constructor"); - ASSERT(false); - break; - } -} - -AreaAura::~AreaAura() -{ -} - -PersistentAreaAura::PersistentAreaAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, -Unit *caster, Item* castItem) : Aura(spellproto, eff, currentBasePoints, target, caster, castItem) -{ - m_isPersistent = true; -} - -PersistentAreaAura::~PersistentAreaAura() -{ -} - -SingleEnemyTargetAura::SingleEnemyTargetAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, -Unit *caster, Item* castItem) : Aura(spellproto, eff, currentBasePoints, target, caster, castItem) -{ - if (caster) - m_casters_target_guid = caster->GetTypeId()==TYPEID_PLAYER ? ((Player*)caster)->GetSelection() : caster->GetUInt64Value(UNIT_FIELD_TARGET); - else - m_casters_target_guid = 0; -} - -SingleEnemyTargetAura::~SingleEnemyTargetAura() -{ -} - -Unit* SingleEnemyTargetAura::GetTriggerTarget() const -{ - return ObjectAccessor::GetUnit(*m_target, m_casters_target_guid); -} - -Aura* CreateAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster, Item* castItem) -{ - if (IsAreaAuraEffect(spellproto->Effect[eff])) - return new AreaAura(spellproto, eff, currentBasePoints, target, caster, castItem); - - uint32 triggeredSpellId = spellproto->EffectTriggerSpell[eff]; - - SpellEntry const* triggredSpellInfo = sSpellStore.LookupEntry(triggeredSpellId); - if (triggredSpellInfo) - for (int i = 0; i < 3; ++i) - if (triggredSpellInfo->EffectImplicitTargetA[i] == TARGET_SINGLE_ENEMY) - return new SingleEnemyTargetAura(spellproto, eff, currentBasePoints, target, caster, castItem); - - return new Aura(spellproto, eff, currentBasePoints, target, caster, castItem); -} - -Unit* Aura::GetCaster() const -{ - if(m_caster_guid==m_target->GetGUID()) - return m_target; - - //return ObjectAccessor::GetUnit(*m_target,m_caster_guid); - //must return caster even if it's in another grid/map - Unit *unit = ObjectAccessor::GetObjectInWorld(m_caster_guid, (Unit*)NULL); - return unit && unit->IsInWorld() ? unit : NULL; -} - -void Aura::SetModifier(AuraType t, int32 a, uint32 pt, int32 miscValue) -{ - m_modifier.m_auraname = t; - m_modifier.m_amount = a; - m_modifier.m_miscvalue = miscValue; - m_modifier.periodictime = pt; -} - -void Aura::Update(uint32 diff) -{ - if (m_duration > 0) - { - m_duration -= diff; - if (m_duration < 0) - m_duration = 0; - m_timeCla -= diff; - - // GetEffIndex()==0 prevent double/triple apply manaPerSecond/manaPerSecondPerLevel to same spell with many auras - // all spells with manaPerSecond/manaPerSecondPerLevel have aura in effect 0 - if(GetEffIndex()==0 && m_timeCla <= 0) - { - if(Unit* caster = GetCaster()) - { - Powers powertype = Powers(m_spellProto->powerType); - int32 manaPerSecond = m_spellProto->manaPerSecond + m_spellProto->manaPerSecondPerLevel * caster->getLevel(); - m_timeCla = 1000; - if (manaPerSecond) - { - if(powertype==POWER_HEALTH) - caster->ModifyHealth(-manaPerSecond); - else - caster->ModifyPower(powertype,-manaPerSecond); - } - } - } - } - - // Channeled aura required check distance from caster - if(IsChanneledSpell(m_spellProto) && m_caster_guid != m_target->GetGUID()) - { - Unit* caster = GetCaster(); - if(!caster) - { - m_target->RemoveAura(GetId(),GetEffIndex()); - return; - } - - // Get spell range - float radius; - SpellModOp mod; - if (m_spellProto->EffectRadiusIndex[GetEffIndex()]) - { - radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellProto->EffectRadiusIndex[GetEffIndex()])); - mod = SPELLMOD_RADIUS; - } - else - { - radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellProto->rangeIndex)); - mod = SPELLMOD_RANGE; - } - - if(Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), mod, radius,NULL); - - if(!caster->IsWithinDistInMap(m_target,radius)) - { - m_target->RemoveAura(GetId(),GetEffIndex()); - return; - } - } - - if(m_isPeriodic && (m_duration >= 0 || m_isPassive || m_permanent)) - { - m_periodicTimer -= diff; - if(m_periodicTimer <= 0) // tick also at m_periodicTimer==0 to prevent lost last tick in case max m_duration == (max m_periodicTimer)*N - { - if( m_modifier.m_auraname == SPELL_AURA_MOD_REGEN || - m_modifier.m_auraname == SPELL_AURA_MOD_POWER_REGEN || - // Cannibalize, eating items and other spells - m_modifier.m_auraname == SPELL_AURA_OBS_MOD_HEALTH || - // Eating items and other spells - m_modifier.m_auraname == SPELL_AURA_OBS_MOD_MANA ) - { - ApplyModifier(true); - return; - } - // update before applying (aura can be removed in TriggerSpell or PeriodicTick calls) - m_periodicTimer += m_modifier.periodictime; - - if(m_isTrigger) - TriggerSpell(); - else - PeriodicTick(); - } - } -} - -void AreaAura::Update(uint32 diff) -{ - // update for the caster of the aura - if(m_caster_guid == m_target->GetGUID()) - { - Unit* caster = m_target; - - if( !caster->hasUnitState(UNIT_STAT_ISOLATED) ) - { - Unit* owner = caster->GetCharmerOrOwner(); - if (!owner) - owner = caster; - std::list<Unit *> targets; - - switch(m_areaAuraType) - { - case AREA_AURA_PARTY: - { - Group *pGroup = NULL; - - if (owner->GetTypeId() == TYPEID_PLAYER) - pGroup = ((Player*)owner)->GetGroup(); - - if( pGroup) - { - uint8 subgroup = ((Player*)owner)->GetSubGroup(); - for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player* Target = itr->getSource(); - if(Target && Target->isAlive() && Target->GetSubGroup()==subgroup && caster->IsFriendlyTo(Target)) - { - if(caster->IsWithinDistInMap(Target, m_radius)) - targets.push_back(Target); - Pet *pet = Target->GetPet(); - if(pet && pet->isAlive() && caster->IsWithinDistInMap(pet, m_radius)) - targets.push_back(pet); - } - } - } - else - { - // add owner - if( owner != caster && caster->IsWithinDistInMap(owner, m_radius) ) - targets.push_back(owner); - // add caster's pet - Unit* pet = caster->GetPet(); - if( pet && caster->IsWithinDistInMap(pet, m_radius)) - targets.push_back(pet); - } - break; - } - case AREA_AURA_FRIEND: - { - CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - - MaNGOS::AnyFriendlyUnitInObjectRangeCheck u_check(caster, owner, m_radius); - MaNGOS::UnitListSearcher<MaNGOS::AnyFriendlyUnitInObjectRangeCheck> searcher(targets, u_check); - TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyFriendlyUnitInObjectRangeCheck>, WorldTypeMapContainer > world_unit_searcher(searcher); - TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyFriendlyUnitInObjectRangeCheck>, GridTypeMapContainer > grid_unit_searcher(searcher); - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, world_unit_searcher, *MapManager::Instance().GetMap(caster->GetMapId(), caster)); - cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(caster->GetMapId(), caster)); - break; - } - case AREA_AURA_ENEMY: - { - CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - - MaNGOS::AnyAoETargetUnitInObjectRangeCheck u_check(caster, owner, m_radius); // No GetCharmer in searcher - MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck> searcher(targets, u_check); - TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck>, WorldTypeMapContainer > world_unit_searcher(searcher); - TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck>, GridTypeMapContainer > grid_unit_searcher(searcher); - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, world_unit_searcher, *MapManager::Instance().GetMap(caster->GetMapId(), caster)); - cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(caster->GetMapId(), caster)); - break; - } - case AREA_AURA_OWNER: - case AREA_AURA_PET: - { - if(owner != caster) - targets.push_back(owner); - break; - } - } - - for(std::list<Unit *>::iterator tIter = targets.begin(); tIter != targets.end(); tIter++) - { - if((*tIter)->HasAura(GetId(), m_effIndex)) - continue; - - if(SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(GetSpellProto(), (*tIter)->getLevel())) - { - int32 actualBasePoints = m_currentBasePoints; - // recalculate basepoints for lower rank (all AreaAura spell not use custom basepoints?) - if(actualSpellInfo != GetSpellProto()) - actualBasePoints = actualSpellInfo->EffectBasePoints[m_effIndex]; - AreaAura *aur = new AreaAura(actualSpellInfo, m_effIndex, &actualBasePoints, (*tIter), caster, NULL); - (*tIter)->AddAura(aur); - } - } - } - Aura::Update(diff); - } - else // aura at non-caster - { - Unit * tmp_target = m_target; - Unit* caster = GetCaster(); - uint32 tmp_spellId = GetId(), tmp_effIndex = m_effIndex; - - // WARNING: the aura may get deleted during the update - // DO NOT access its members after update! - Aura::Update(diff); - - // remove aura if out-of-range from caster (after teleport for example) - // or caster is isolated or caster no longer has the aura - // or caster is (no longer) friendly - bool needFriendly = (m_areaAuraType == AREA_AURA_ENEMY ? false : true); - if( !caster || caster->hasUnitState(UNIT_STAT_ISOLATED) || - !caster->IsWithinDistInMap(tmp_target, m_radius) || - !caster->HasAura(tmp_spellId, tmp_effIndex) || - caster->IsFriendlyTo(tmp_target) != needFriendly - ) - { - tmp_target->RemoveAura(tmp_spellId, tmp_effIndex); - } - else if( m_areaAuraType == AREA_AURA_PARTY) // check if in same sub group - { - // not check group if target == owner or target == pet - if (caster->GetCharmerOrOwnerGUID() != tmp_target->GetGUID() && caster->GetGUID() != tmp_target->GetCharmerOrOwnerGUID()) - { - Player* check = caster->GetCharmerOrOwnerPlayerOrPlayerItself(); - - Group *pGroup = check ? check->GetGroup() : NULL; - if( pGroup ) - { - Player* checkTarget = tmp_target->GetCharmerOrOwnerPlayerOrPlayerItself(); - if(!checkTarget || !pGroup->SameSubGroup(check, checkTarget)) - tmp_target->RemoveAura(tmp_spellId, tmp_effIndex); - } - else - tmp_target->RemoveAura(tmp_spellId, tmp_effIndex); - } - } - else if( m_areaAuraType == AREA_AURA_PET || m_areaAuraType == AREA_AURA_OWNER ) - { - if( tmp_target->GetGUID() != caster->GetCharmerOrOwnerGUID() ) - tmp_target->RemoveAura(tmp_spellId, tmp_effIndex); - } - } -} - -void PersistentAreaAura::Update(uint32 diff) -{ - bool remove = false; - - // remove the aura if its caster or the dynamic object causing it was removed - // or if the target moves too far from the dynamic object - Unit *caster = GetCaster(); - if (caster) - { - DynamicObject *dynObj = caster->GetDynObject(GetId(), GetEffIndex()); - if (dynObj) - { - if (!m_target->IsWithinDistInMap(dynObj, dynObj->GetRadius())) - remove = true; - } - else - remove = true; - } - else - remove = true; - - Unit *tmp_target = m_target; - uint32 tmp_id = GetId(), tmp_index = GetEffIndex(); - - // WARNING: the aura may get deleted during the update - // DO NOT access its members after update! - Aura::Update(diff); - - if(remove) - tmp_target->RemoveAura(tmp_id, tmp_index); -} - -void Aura::ApplyModifier(bool apply, bool Real) -{ - AuraType aura = m_modifier.m_auraname; - - m_in_use = true; - if(aura<TOTAL_AURAS) - (*this.*AuraHandler [aura])(apply,Real); - m_in_use = false; -} - -void Aura::UpdateAuraDuration() -{ - if(m_auraSlot >= MAX_AURAS || m_isPassive) - return; - - if( m_target->GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_UPDATE_AURA_DURATION, 5); - data << (uint8)m_auraSlot << (uint32)m_duration; - ((Player*)m_target)->SendDirectMessage(&data); - - data.Initialize(SMSG_SET_EXTRA_AURA_INFO, (8+1+4+4+4)); - data.append(m_target->GetPackGUID()); - data << uint8(m_auraSlot); - data << uint32(GetId()); - data << uint32(GetAuraMaxDuration()); - data << uint32(GetAuraDuration()); - ((Player*)m_target)->SendDirectMessage(&data); - } - - // not send in case player loading (will not work anyway until player not added to map), sent in visibility change code - if(m_target->GetTypeId() == TYPEID_PLAYER && ((Player*)m_target)->GetSession()->PlayerLoading()) - return; - - Unit* caster = GetCaster(); - - if(caster && caster->GetTypeId() == TYPEID_PLAYER && caster != m_target) - SendAuraDurationForCaster((Player*)caster); -} - -void Aura::SendAuraDurationForCaster(Player* caster) -{ - WorldPacket data(SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE, (8+1+4+4+4)); - data.append(m_target->GetPackGUID()); - data << uint8(m_auraSlot); - data << uint32(GetId()); - data << uint32(GetAuraMaxDuration()); // full - data << uint32(GetAuraDuration()); // remain - caster->GetSession()->SendPacket(&data); -} - -void Aura::_AddAura() -{ - if (!GetId()) - return; - if(!m_target) - return; - - // we can found aura in NULL_AURA_SLOT and then need store state instead check slot != NULL_AURA_SLOT - bool samespell = false; - bool secondaura = false; - uint8 slot = NULL_AURA_SLOT; - - for(uint8 i = 0; i < 3; i++) - { - Unit::spellEffectPair spair = Unit::spellEffectPair(GetId(), i); - for(Unit::AuraMap::const_iterator itr = m_target->GetAuras().lower_bound(spair); itr != m_target->GetAuras().upper_bound(spair); ++itr) - { - // allow use single slot only by auras from same caster - if(itr->second->GetCasterGUID()==GetCasterGUID()) - { - samespell = true; - if (m_effIndex > itr->second->GetEffIndex()) - secondaura = true; - slot = itr->second->GetAuraSlot(); - break; - } - } - - if(samespell) - break; - } - - // not call total regen auras at adding - switch (m_modifier.m_auraname) - { - case SPELL_AURA_OBS_MOD_HEALTH: - case SPELL_AURA_OBS_MOD_MANA: - m_periodicTimer = m_modifier.periodictime; - break; - case SPELL_AURA_MOD_REGEN: - case SPELL_AURA_MOD_POWER_REGEN: - case SPELL_AURA_MOD_MANA_REGEN_FROM_STAT: - m_periodicTimer = 5000; - break; - } - - // register aura - if (getDiminishGroup() != DIMINISHING_NONE ) - m_target->ApplyDiminishingAura(getDiminishGroup(),true); - - Unit* caster = GetCaster(); - - // passive auras (except totem auras) do not get placed in the slots - // area auras with SPELL_AURA_NONE are not shown on target - if((!m_isPassive || (caster && caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->isTotem())) && - (m_spellProto->Effect[GetEffIndex()] != SPELL_EFFECT_APPLY_AREA_AURA_ENEMY || m_target != caster)) - { - if(!samespell) // new slot need - { - if (IsPositive()) // empty positive slot - { - for (uint8 i = 0; i < MAX_POSITIVE_AURAS; i++) - { - if (m_target->GetUInt32Value((uint16)(UNIT_FIELD_AURA + i)) == 0) - { - slot = i; - break; - } - } - } - else // empty negative slot - { - for (uint8 i = MAX_POSITIVE_AURAS; i < MAX_AURAS; i++) - { - if (m_target->GetUInt32Value((uint16)(UNIT_FIELD_AURA + i)) == 0) - { - slot = i; - break; - } - } - } - - SetAuraSlot( slot ); - - // Not update fields for not first spell's aura, all data already in fields - if(!secondaura) - { - if(slot < MAX_AURAS) // slot found - { - SetAura(slot, false); - SetAuraFlag(slot, true); - SetAuraLevel(slot,caster ? caster->getLevel() : sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)); - UpdateAuraCharges(); - - // update for out of range group members - m_target->UpdateAuraForGroup(slot); - } - - UpdateAuraDuration(); - } - } - else // use found slot - { - SetAuraSlot( slot ); - // Not recalculate stack count for second aura of the same spell - if (!secondaura) - UpdateSlotCounterAndDuration(true); - } - - // Update Seals information - if( IsSealSpell(GetSpellProto()) ) - m_target->ModifyAuraState(AURA_STATE_JUDGEMENT, true); - - // Conflagrate aura state - if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 4)) - m_target->ModifyAuraState(AURA_STATE_IMMOLATE, true); - - if(GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID - && (GetSpellProto()->SpellFamilyFlags == 0x40 || GetSpellProto()->SpellFamilyFlags == 0x10)) - { - m_target->ModifyAuraState(AURA_STATE_SWIFTMEND, true); - } - } -} - -void Aura::_RemoveAura() -{ - // Remove all triggered by aura spells vs unlimited duration - // except same aura replace case - if(m_removeMode!=AURA_REMOVE_BY_STACK) - CleanupTriggeredSpells(); - - Unit* caster = GetCaster(); - - if(caster && IsPersistent()) - { - DynamicObject *dynObj = caster->GetDynObject(GetId(), GetEffIndex()); - if (dynObj) - dynObj->RemoveAffected(m_target); - } - - // unregister aura - if (getDiminishGroup() != DIMINISHING_NONE ) - m_target->ApplyDiminishingAura(getDiminishGroup(),false); - - //passive auras do not get put in slots - // Note: but totem can be not accessible for aura target in time remove (to far for find in grid) - //if(m_isPassive && !(caster && caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->isTotem())) - // return; - - uint8 slot = GetAuraSlot(); - - if(slot >= MAX_AURAS) // slot not set - return; - - if(m_target->GetUInt32Value((uint16)(UNIT_FIELD_AURA + slot)) == 0) - return; - - bool samespell = false; - bool sameaura = false; - - // find other aura in same slot (current already removed from list) - for(uint8 i = 0; i < 3; i++) - { - Unit::spellEffectPair spair = Unit::spellEffectPair(GetId(), i); - for(Unit::AuraMap::const_iterator itr = m_target->GetAuras().lower_bound(spair); itr != m_target->GetAuras().upper_bound(spair); ++itr) - { - if(itr->second->GetAuraSlot()==slot) - { - samespell = true; - - if(GetEffIndex()==i) - sameaura = true; - - break; - } - } - if(samespell) - break; - } - - // only remove icon when the last aura of the spell is removed (current aura already removed from list) - if (!samespell) - { - SetAura(slot, true); - SetAuraFlag(slot, false); - SetAuraLevel(slot,caster ? caster->getLevel() : sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)); - - SetAuraApplication(slot, 0); - // update for out of range group members - m_target->UpdateAuraForGroup(slot); - - if( IsSealSpell(GetSpellProto()) ) - m_target->ModifyAuraState(AURA_STATE_JUDGEMENT,false); - - // Conflagrate aura state - if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 4)) - m_target->ModifyAuraState(AURA_STATE_IMMOLATE, false); - - // Swiftmend aura state - if(GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID - && (GetSpellProto()->SpellFamilyFlags == 0x40 || GetSpellProto()->SpellFamilyFlags == 0x10)) - { - bool found = false; - Unit::AuraList const& RejorRegr = m_target->GetAurasByType(SPELL_AURA_PERIODIC_HEAL); - for(Unit::AuraList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i) - { - if((*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID - && ((*i)->GetSpellProto()->SpellFamilyFlags == 0x40 || (*i)->GetSpellProto()->SpellFamilyFlags == 0x10) ) - { - found = true; - break; - } - } - if(!found) - m_target->ModifyAuraState(AURA_STATE_SWIFTMEND, false); - } - - // reset cooldown state for spells - if(caster && caster->GetTypeId() == TYPEID_PLAYER) - { - if ( GetSpellProto()->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE ) - ((Player*)caster)->SendCooldownEvent(GetSpellProto()); - } - } - else if(sameaura) // decrease count for spell, only for same aura effect, or this spell auras in remove proccess. - UpdateSlotCounterAndDuration(false); -} - -void Aura::SetAuraFlag(uint32 slot, bool add) -{ - uint32 index = slot / 4; - uint32 byte = (slot % 4) * 8; - uint32 val = m_target->GetUInt32Value(UNIT_FIELD_AURAFLAGS + index); - val &= ~((uint32)AFLAG_MASK << byte); - if(add) - { - if (IsPositive()) - val |= ((uint32)AFLAG_POSITIVE << byte); - else - val |= ((uint32)AFLAG_NEGATIVE << byte); - } - m_target->SetUInt32Value(UNIT_FIELD_AURAFLAGS + index, val); -} - -void Aura::SetAuraLevel(uint32 slot,uint32 level) -{ - uint32 index = slot / 4; - uint32 byte = (slot % 4) * 8; - uint32 val = m_target->GetUInt32Value(UNIT_FIELD_AURALEVELS + index); - val &= ~(0xFF << byte); - val |= (level << byte); - m_target->SetUInt32Value(UNIT_FIELD_AURALEVELS + index, val); -} - -void Aura::SetAuraApplication(uint32 slot, int8 count) -{ - uint32 index = slot / 4; - uint32 byte = (slot % 4) * 8; - uint32 val = m_target->GetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS + index); - val &= ~(0xFF << byte); - val |= ((uint8(count)) << byte); - m_target->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS + index, val); -} - -void Aura::UpdateSlotCounterAndDuration(bool add) -{ - uint8 slot = GetAuraSlot(); - if(slot >= MAX_AURAS) - return; - - // calculate amount of similar auras by same effect index (similar different spells) - int8 count = 0; - - // calculate auras and update durations in case aura adding - Unit::AuraList const& aura_list = m_target->GetAurasByType(GetModifier()->m_auraname); - for(Unit::AuraList::const_iterator i = aura_list.begin();i != aura_list.end(); ++i) - { - if( (*i)->GetId()==GetId() && (*i)->GetEffIndex()==m_effIndex && - (*i)->GetCasterGUID()==GetCasterGUID() ) - { - ++count; - - if(add) - (*i)->SetAuraDuration(GetAuraDuration()); - } - } - - // at aura add aura not added yet, at aura remove aura already removed - // in field stored (count-1) - if(!add) - --count; - - SetAuraApplication(slot, count); - - UpdateAuraDuration(); -} - -/*********************************************************/ -/*** BASIC AURA FUNCTION ***/ -/*********************************************************/ -void Aura::HandleAddModifier(bool apply, bool Real) -{ - if(m_target->GetTypeId() != TYPEID_PLAYER || !Real) - return; - - SpellEntry const *spellInfo = GetSpellProto(); - if(!spellInfo) - return; - - if(m_modifier.m_miscvalue >= MAX_SPELLMOD) - return; - - if (apply) - { - // Add custom charges for some mod aura - switch (m_spellProto->Id) - { - case 17941: // Shadow Trance - case 22008: // Netherwind Focus - case 34936: // Backlash - m_procCharges = 1; - break; - } - - SpellModifier *mod = new SpellModifier; - mod->op = SpellModOp(m_modifier.m_miscvalue); - mod->value = m_modifier.m_amount; - mod->type = SpellModType(m_modifier.m_auraname); // SpellModType value == spell aura types - mod->spellId = GetId(); - mod->effectId = m_effIndex; - mod->lastAffected = NULL; - - uint64 spellAffectMask = spellmgr.GetSpellAffectMask(GetId(), m_effIndex); - - if (spellAffectMask) - mod->mask = spellAffectMask; - else - mod->mask = spellInfo->EffectItemType[m_effIndex]; - - if (m_procCharges > 0) - mod->charges = m_procCharges; - else - mod->charges = 0; - - m_spellmod = mod; - } - - uint64 spellFamilyMask = m_spellmod->mask; - - ((Player*)m_target)->AddSpellMod(m_spellmod, apply); - - // reapply some passive spells after add/remove related spellmods - if(spellInfo->SpellFamilyName==SPELLFAMILY_WARRIOR && (spellFamilyMask & 0x0000100000000000LL)) - { - m_target->RemoveAurasDueToSpell(45471); - - if(apply) - m_target->CastSpell(m_target,45471,true); - } -} - -void Aura::TriggerSpell() -{ - Unit* caster = GetCaster(); - Unit* target = GetTriggerTarget(); - - if(!caster || !target) - return; - - // generic casting code with custom spells and target/caster customs - uint32 trigger_spell_id = GetSpellProto()->EffectTriggerSpell[m_effIndex]; - - uint64 originalCasterGUID = GetCasterGUID(); - - SpellEntry const *triggredSpellInfo = sSpellStore.LookupEntry(trigger_spell_id); - SpellEntry const *auraSpellInfo = GetSpellProto(); - uint32 auraId = auraSpellInfo->Id; - - // specific code for cases with no trigger spell provided in field - if (triggredSpellInfo == NULL) - { - switch(auraSpellInfo->SpellFamilyName) - { - case SPELLFAMILY_GENERIC: - { - switch(auraId) - { - // Firestone Passive (1-5 rangs) - case 758: - case 17945: - case 17947: - case 17949: - case 27252: - { - if (caster->GetTypeId()!=TYPEID_PLAYER) - return; - Item* item = ((Player*)caster)->GetWeaponForAttack(BASE_ATTACK); - if (!item) - return; - uint32 enchant_id = 0; - switch (GetId()) - { - case 758: enchant_id = 1803; break; // Rank 1 - case 17945: enchant_id = 1823; break; // Rank 2 - case 17947: enchant_id = 1824; break; // Rank 3 - case 17949: enchant_id = 1825; break; // Rank 4 - case 27252: enchant_id = 2645; break; // Rank 5 - default: - return; - } - // remove old enchanting before applying new - ((Player*)caster)->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,false); - item->SetEnchantment(TEMP_ENCHANTMENT_SLOT, enchant_id, m_modifier.periodictime+1000, 0); - // add new enchanting - ((Player*)caster)->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,true); - return; - } -// // Periodic Mana Burn -// case 812: break; -// // Polymorphic Ray -// case 6965: break; -// // Fire Nova (1-7 Rangs) -// case 8350: -// case 8508: -// case 8509: -// case 11312: -// case 11313: -// case 25540: -// case 25544: -// break; - // Thaumaturgy Channel - case 9712: trigger_spell_id = 21029; break; -// // Egan's Blaster -// case 17368: break; -// // Haunted -// case 18347: break; -// // Ranshalla Waiting -// case 18953: break; -// // Inferno -// case 19695: break; -// // Frostwolf Muzzle DND -// case 21794: break; -// // Alterac Ram Collar DND -// case 21866: break; -// // Celebras Waiting -// case 21916: break; - // Brood Affliction: Bronze - case 23170: - { - m_target->CastSpell(m_target, 23171, true, 0, this); - return; - } -// // Mark of Frost -// case 23184: break; - // Restoration - case 23493: - { - int32 heal = caster->GetMaxHealth() / 10; - caster->ModifyHealth( heal ); - caster->SendHealSpellLog(caster, 23493, heal); - - int32 mana = caster->GetMaxPower(POWER_MANA); - if (mana) - { - mana /= 10; - caster->ModifyPower( POWER_MANA, mana ); - caster->SendEnergizeSpellLog(caster, 23493, mana, POWER_MANA); - } - break; - } -// // Stoneclaw Totem Passive TEST -// case 23792: break; -// // Axe Flurry -// case 24018: break; -// // Mark of Arlokk -// case 24210: break; -// // Restoration -// case 24379: break; -// // Happy Pet -// case 24716: break; -// // Dream Fog -// case 24780: break; -// // Cannon Prep -// case 24832: break; -// // Shadow Bolt Whirl -// case 24834: break; -// // Stink Trap -// case 24918: break; -// // Mark of Nature -// case 25041: break; -// // Agro Drones -// case 25152: break; -// // Consume -// case 25371: break; -// // Pain Spike -// case 25572: break; -// // Rotate 360 -// case 26009: break; -// // Rotate -360 -// case 26136: break; -// // Consume -// case 26196: break; -// // Berserk -// case 26615: break; -// // Defile -// case 27177: break; -// // Teleport: IF/UC -// case 27601: break; -// // Five Fat Finger Exploding Heart Technique -// case 27673: break; -// // Nitrous Boost -// case 27746: break; -// // Steam Tank Passive -// case 27747: break; -// // Frost Blast -// case 27808: break; -// // Detonate Mana -// case 27819: break; -// // Controller Timer -// case 28095: break; -// // Stalagg Chain -// case 28096: break; -// // Stalagg Tesla Passive -// case 28097: break; -// // Feugen Tesla Passive -// case 28109: break; -// // Feugen Chain -// case 28111: break; -// // Mark of Didier -// case 28114: break; -// // Communique Timer, camp -// case 28346: break; -// // Icebolt -// case 28522: break; -// // Silithyst -// case 29519: break; -// // Inoculate Nestlewood Owlkin - case 29528: trigger_spell_id = 28713; break; -// // Overload -// case 29768: break; -// // Return Fire -// case 29788: break; -// // Return Fire -// case 29793: break; -// // Return Fire -// case 29794: break; -// // Guardian of Icecrown Passive -// case 29897: break; - // Feed Captured Animal - case 29917: trigger_spell_id = 29916; break; -// // Flame Wreath -// case 29946: break; -// // Flame Wreath -// case 29947: break; -// // Mind Exhaustion Passive -// case 30025: break; -// // Nether Beam - Serenity -// case 30401: break; - // Extract Gas - case 30427: - { - // move loot to player inventory and despawn target - if(caster->GetTypeId() ==TYPEID_PLAYER && - target->GetTypeId() == TYPEID_UNIT && - ((Creature*)target)->GetCreatureInfo()->type == CREATURE_TYPE_GAS_CLOUD) - { - Player* player = (Player*)caster; - Creature* creature = (Creature*)target; - // missing lootid has been reported on startup - just return - if (!creature->GetCreatureInfo()->SkinLootId) - { - return; - } - Loot *loot = &creature->loot; - loot->clear(); - loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, NULL); - for(uint8 i=0;i<loot->items.size();i++) - { - LootItem *item = loot->LootItemInSlot(i,player); - ItemPosCountVec dest; - uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count ); - if ( msg == EQUIP_ERR_OK ) - { - Item * newitem = player->StoreNewItem( dest, item->itemid, true, item->randomPropertyId); - - player->SendNewItem(newitem, uint32(item->count), false, false, true); - } - else - player->SendEquipError( msg, NULL, NULL ); - } - creature->setDeathState(JUST_DIED); - creature->RemoveCorpse(); - creature->SetHealth(0); // just for nice GM-mode view - } - return; - break; - } - // Quake - case 30576: trigger_spell_id = 30571; break; -// // Burning Maul -// case 30598: break; -// // Regeneration -// case 30799: -// case 30800: -// case 30801: -// break; -// // Despawn Self - Smoke cloud -// case 31269: break; -// // Time Rift Periodic -// case 31320: break; -// // Corrupt Medivh -// case 31326: break; - // Doom - case 31347: - { - m_target->CastSpell(m_target,31350,true); - m_target->DealDamage(m_target, m_target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - return; - } - // Spellcloth - case 31373: - { - // Summon Elemental after create item - caster->SummonCreature(17870, 0, 0, 0, caster->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 0); - return; - } -// // Bloodmyst Tesla -// case 31611: break; -// // Doomfire -// case 31944: break; -// // Teleport Test -// case 32236: break; -// // Earthquake -// case 32686: break; -// // Possess -// case 33401: break; -// // Draw Shadows -// case 33563: break; -// // Murmur's Touch -// case 33711: break; - // Flame Quills - case 34229: - { - // cast 24 spells 34269-34289, 34314-34316 - for(uint32 spell_id = 34269; spell_id != 34290; ++spell_id) - caster->CastSpell(m_target,spell_id,true); - for(uint32 spell_id = 34314; spell_id != 34317; ++spell_id) - caster->CastSpell(m_target,spell_id,true); - return; - } -// // Gravity Lapse -// case 34480: break; -// // Tornado -// case 34683: break; -// // Frostbite Rotate -// case 34748: break; -// // Arcane Flurry -// case 34821: break; -// // Interrupt Shutdown -// case 35016: break; -// // Interrupt Shutdown -// case 35176: break; -// // Inferno -// case 35268: break; -// // Salaadin's Tesla -// case 35515: break; -// // Ethereal Channel (Red) -// case 35518: break; -// // Nether Vapor -// case 35879: break; -// // Dark Portal Storm -// case 36018: break; -// // Burning Maul -// case 36056: break; -// // Living Grove Defender Lifespan -// case 36061: break; -// // Professor Dabiri Talks -// case 36064: break; -// // Kael Gaining Power -// case 36091: break; -// // They Must Burn Bomb Aura -// case 36344: break; -// // They Must Burn Bomb Aura (self) -// case 36350: break; -// // Stolen Ravenous Ravager Egg -// case 36401: break; -// // Activated Cannon -// case 36410: break; -// // Stolen Ravenous Ravager Egg -// case 36418: break; -// // Enchanted Weapons -// case 36510: break; -// // Cursed Scarab Periodic -// case 36556: break; -// // Cursed Scarab Despawn Periodic -// case 36561: break; -// // Vision Guide -// case 36573: break; -// // Cannon Charging (platform) -// case 36785: break; -// // Cannon Charging (self) -// case 36860: break; - // Remote Toy - case 37027: trigger_spell_id = 37029; break; -// // Mark of Death -// case 37125: break; -// // Arcane Flurry -// case 37268: break; -// // Spout -// case 37429: break; -// // Spout -// case 37430: break; -// // Karazhan - Chess NPC AI, Snapshot timer -// case 37440: break; -// // Karazhan - Chess NPC AI, action timer -// case 37504: break; -// // Karazhan - Chess: Is Square OCCUPIED aura (DND) -// case 39400: break; -// // Banish -// case 37546: break; -// // Shriveling Gaze -// case 37589: break; -// // Fake Aggro Radius (2 yd) -// case 37815: break; -// // Corrupt Medivh -// case 37853: break; - // Eye of Grillok - case 38495: - { - m_target->CastSpell(m_target, 38530, true); - return; - } - // Absorb Eye of Grillok (Zezzak's Shard) - case 38554: - { - if(m_target->GetTypeId() != TYPEID_UNIT) - return; - - caster->CastSpell(caster, 38495, true); - - Creature* creatureTarget = (Creature*)m_target; - - creatureTarget->setDeathState(JUST_DIED); - creatureTarget->RemoveCorpse(); - creatureTarget->SetHealth(0); // just for nice GM-mode view - return; - } -// // Magic Sucker Device timer -// case 38672: break; -// // Tomb Guarding Charging -// case 38751: break; -// // Murmur's Touch -// case 38794: break; -// // Activate Nether-wraith Beacon (31742 Nether-wraith Beacon item) -// case 39105: break; -// // Drain World Tree Visual -// case 39140: break; -// // Quest - Dustin's Undead Dragon Visual aura -// case 39259: break; -// // Hellfire - The Exorcism, Jules releases darkness, aura -// case 39306: break; -// // Inferno -// case 39346: break; -// // Enchanted Weapons -// case 39489: break; -// // Shadow Bolt Whirl -// case 39630: break; -// // Shadow Bolt Whirl -// case 39634: break; -// // Shadow Inferno -// case 39645: break; - // Tear of Azzinoth Summon Channel - it's not really supposed to do anything,and this only prevents the console spam - case 39857: trigger_spell_id = 39856; break; -// // Soulgrinder Ritual Visual (Smashed) -// case 39974: break; -// // Simon Game Pre-game timer -// case 40041: break; -// // Knockdown Fel Cannon: The Aggro Check Aura -// case 40113: break; -// // Spirit Lance -// case 40157: break; -// // Demon Transform 2 -// case 40398: break; -// // Demon Transform 1 -// case 40511: break; -// // Ancient Flames -// case 40657: break; -// // Ethereal Ring Cannon: Cannon Aura -// case 40734: break; -// // Cage Trap -// case 40760: break; -// // Random Periodic -// case 40867: break; -// // Prismatic Shield -// case 40879: break; -// // Aura of Desire -// case 41350: break; -// // Dementia -// case 41404: break; -// // Chaos Form -// case 41629: break; -// // Alert Drums -// case 42177: break; -// // Spout -// case 42581: break; -// // Spout -// case 42582: break; -// // Return to the Spirit Realm -// case 44035: break; -// // Curse of Boundless Agony -// case 45050: break; -// // Earthquake -// case 46240: break; - // Personalized Weather - case 46736: trigger_spell_id = 46737; break; -// // Stay Submerged -// case 46981: break; -// // Dragonblight Ram -// case 47015: break; -// // Party G.R.E.N.A.D.E. -// case 51510: break; - default: - break; - } - break; - } - case SPELLFAMILY_MAGE: - { - switch(auraId) - { - // Invisibility - case 66: - { - if(!m_duration) - m_target->CastSpell(m_target, 32612, true, NULL, this); - return; - } - default: - break; - } - break; - } -// case SPELLFAMILY_WARRIOR: -// { -// switch(auraId) -// { -// // Wild Magic -// case 23410: break; -// // Corrupted Totems -// case 23425: break; -// default: -// break; -// } -// break; -// } -// case SPELLFAMILY_PRIEST: -// { -// switch(auraId) -// { -// // Blue Beam -// case 32930: break; -// // Fury of the Dreghood Elders -// case 35460: break; -// default: -// break; -// } - // break; - // } - case SPELLFAMILY_DRUID: - { - switch(auraId) - { - // Cat Form - // trigger_spell_id not set and unknown effect triggered in this case, ignoring for while - case 768: - return; - // Frenzied Regeneration - case 22842: - case 22895: - case 22896: - case 26999: - { - int32 LifePerRage = GetModifier()->m_amount; - - int32 lRage = m_target->GetPower(POWER_RAGE); - if(lRage > 100) // rage stored as rage*10 - lRage = 100; - m_target->ModifyPower(POWER_RAGE, -lRage); - int32 FRTriggerBasePoints = int32(lRage*LifePerRage/10); - m_target->CastCustomSpell(m_target,22845,&FRTriggerBasePoints,NULL,NULL,true,NULL,this); - return; - } - default: - break; - } - break; - } - -// case SPELLFAMILY_HUNTER: -// { -// switch(auraId) -// { -// //Frost Trap Aura -// case 13810: -// return; -// //Rizzle's Frost Trap -// case 39900: -// return; -// // Tame spells -// case 19597: // Tame Ice Claw Bear -// case 19676: // Tame Snow Leopard -// case 19677: // Tame Large Crag Boar -// case 19678: // Tame Adult Plainstrider -// case 19679: // Tame Prairie Stalker -// case 19680: // Tame Swoop -// case 19681: // Tame Dire Mottled Boar -// case 19682: // Tame Surf Crawler -// case 19683: // Tame Armored Scorpid -// case 19684: // Tame Webwood Lurker -// case 19685: // Tame Nightsaber Stalker -// case 19686: // Tame Strigid Screecher -// case 30100: // Tame Crazed Dragonhawk -// case 30103: // Tame Elder Springpaw -// case 30104: // Tame Mistbat -// case 30647: // Tame Barbed Crawler -// case 30648: // Tame Greater Timberstrider -// case 30652: // Tame Nightstalker -// return; -// default: -// break; -// } -// break; -// } - case SPELLFAMILY_SHAMAN: - { - switch(auraId) - { - // Lightning Shield (The Earthshatterer set trigger after cast Lighting Shield) - case 28820: - { - // Need remove self if Lightning Shield not active - Unit::AuraMap const& auras = target->GetAuras(); - for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - SpellEntry const* spell = itr->second->GetSpellProto(); - if( spell->SpellFamilyName == SPELLFAMILY_SHAMAN && - spell->SpellFamilyFlags & 0x0000000000000400L) - return; - } - target->RemoveAurasDueToSpell(28820); - return; - } - // Totemic Mastery (Skyshatter Regalia (Shaman Tier 6) - bonus) - case 38443: - { - bool all = true; - for(int i = 0; i < MAX_TOTEM; ++i) - { - if(!caster->m_TotemSlot[i]) - { - all = false; - break; - } - } - - if(all) - caster->CastSpell(caster,38437,true); - else - caster->RemoveAurasDueToSpell(38437); - return; - } - default: - break; - } - break; - } - default: - break; - } - // Reget trigger spell proto - triggredSpellInfo = sSpellStore.LookupEntry(trigger_spell_id); - if(triggredSpellInfo == NULL) - { - sLog.outError("Aura::TriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",GetId(),GetEffIndex()); - return; - } - } - else - { - // Spell exist but require costum code - switch(auraId) - { - // Curse of Idiocy - case 1010: - { - // TODO: spell casted by result in correct way mostly - // BUT: - // 1) target show casting at each triggered cast: target don't must show casting animation for any triggered spell - // but must show affect apply like item casting - // 2) maybe aura must be replace by new with accumulative stat mods insteed stacking - - // prevent cast by triggered auras - if(m_caster_guid == m_target->GetGUID()) - return; - - // stop triggering after each affected stats lost > 90 - int32 intelectLoss = 0; - int32 spiritLoss = 0; - - Unit::AuraList const& mModStat = m_target->GetAurasByType(SPELL_AURA_MOD_STAT); - for(Unit::AuraList::const_iterator i = mModStat.begin(); i != mModStat.end(); ++i) - { - if ((*i)->GetId() == 1010) - { - switch((*i)->GetModifier()->m_miscvalue) - { - case STAT_INTELLECT: intelectLoss += (*i)->GetModifier()->m_amount; break; - case STAT_SPIRIT: spiritLoss += (*i)->GetModifier()->m_amount; break; - default: break; - } - } - } - - if(intelectLoss <= -90 && spiritLoss <= -90) - return; - - caster = target; - originalCasterGUID = 0; - break; - } - // Mana Tide - case 16191: - { - caster->CastCustomSpell(target, trigger_spell_id, &m_modifier.m_amount, NULL, NULL, true, NULL, this, originalCasterGUID); - return; - } - } - } - // All ok cast by default case - Spell *spell = new Spell(caster, triggredSpellInfo, true, originalCasterGUID ); - - SpellCastTargets targets; - targets.setUnitTarget( target ); - - // if spell create dynamic object extract area from it - if(DynamicObject* dynObj = caster->GetDynObject(GetId())) - targets.setDestination(dynObj->GetPositionX(),dynObj->GetPositionY(),dynObj->GetPositionZ()); - - spell->prepare(&targets, this); -} - -/*********************************************************/ -/*** AURA EFFECTS ***/ -/*********************************************************/ - -void Aura::HandleAuraDummy(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - Unit* caster = GetCaster(); - - // AT APPLY - if(apply) - { - switch(GetId()) - { - case 1515: // Tame beast - // FIX_ME: this is 2.0.12 threat effect replaced in 2.1.x by dummy aura, must be checked for correctness - if( caster && m_target->CanHaveThreatList()) - m_target->AddThreat(caster, 10.0f); - return; - case 13139: // net-o-matic - // root to self part of (root_target->charge->root_self sequence - if(caster) - caster->CastSpell(caster,13138,true,NULL,this); - return; - case 39850: // Rocket Blast - if(roll_chance_i(20)) // backfire stun - m_target->CastSpell(m_target, 51581, true, NULL, this); - return; - case 46354: // Blood Elf Illusion - if(caster) - { - switch(caster->getGender()) - { - case GENDER_FEMALE: - caster->CastSpell(m_target,46356,true,NULL,this); - break; - case GENDER_MALE: - caster->CastSpell(m_target,46355,true,NULL,this); - break; - default: - break; - } - } - return; - case 46699: // Requires No Ammo - if(m_target->GetTypeId()==TYPEID_PLAYER) - ((Player*)m_target)->RemoveAmmo(); // not use ammo and not allow use - return; - } - - // Earth Shield - if ( caster && GetSpellProto()->SpellFamilyName == SPELLFAMILY_SHAMAN && (GetSpellProto()->SpellFamilyFlags & 0x40000000000LL)) - { - // prevent double apply bonuses - if(m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading()) - m_modifier.m_amount = caster->SpellHealingBonus(GetSpellProto(), m_modifier.m_amount, SPELL_DIRECT_DAMAGE, m_target); - return; - } - } - // AT REMOVE - else - { - if( m_target->GetTypeId() == TYPEID_PLAYER && - ( GetSpellProto()->Effect[0]==72 || GetSpellProto()->Effect[0]==6 && - ( GetSpellProto()->EffectApplyAuraName[0]==1 || GetSpellProto()->EffectApplyAuraName[0]==128 ) ) ) - { - // spells with SpellEffect=72 and aura=4: 6196, 6197, 21171, 21425 - m_target->SetUInt64Value(PLAYER_FARSIGHT, 0); - WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0); - ((Player*)m_target)->GetSession()->SendPacket(&data); - return; - } - - if( (IsQuestTameSpell(GetId())) && caster && caster->isAlive() && m_target->isAlive()) - { - uint32 finalSpelId = 0; - switch(GetId()) - { - case 19548: finalSpelId = 19597; break; - case 19674: finalSpelId = 19677; break; - case 19687: finalSpelId = 19676; break; - case 19688: finalSpelId = 19678; break; - case 19689: finalSpelId = 19679; break; - case 19692: finalSpelId = 19680; break; - case 19693: finalSpelId = 19684; break; - case 19694: finalSpelId = 19681; break; - case 19696: finalSpelId = 19682; break; - case 19697: finalSpelId = 19683; break; - case 19699: finalSpelId = 19685; break; - case 19700: finalSpelId = 19686; break; - case 30646: finalSpelId = 30647; break; - case 30653: finalSpelId = 30648; break; - case 30654: finalSpelId = 30652; break; - case 30099: finalSpelId = 30100; break; - case 30102: finalSpelId = 30103; break; - case 30105: finalSpelId = 30104; break; - } - - if(finalSpelId) - caster->CastSpell(m_target,finalSpelId,true,NULL,this); - return; - } - // Dark Fiend - if(GetId()==45934) - { - // Kill target if dispeled - if (m_removeMode==AURA_REMOVE_BY_DISPEL) - m_target->DealDamage(m_target, m_target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - return; - } - - // Burning Winds - if(GetId()==46308) // casted only at creatures at spawn - { - m_target->CastSpell(m_target,47287,true,NULL,this); - return; - } - } - - // AT APPLY & REMOVE - - switch(m_spellProto->SpellFamilyName) - { - case SPELLFAMILY_GENERIC: - { - // Unstable Power - if( GetId()==24658 ) - { - uint32 spellId = 24659; - if (apply) - { - const SpellEntry *spell = sSpellStore.LookupEntry(spellId); - if (!spell) - return; - for (int i=0; i < spell->StackAmount; ++i) - caster->CastSpell(m_target, spell->Id, true, NULL, NULL, GetCasterGUID()); - return; - } - m_target->RemoveAurasDueToSpell(spellId); - return; - } - // Restless Strength - if( GetId()==24661 ) - { - uint32 spellId = 24662; - if (apply) - { - const SpellEntry *spell = sSpellStore.LookupEntry(spellId); - if (!spell) - return; - for (int i=0; i < spell->StackAmount; ++i) - caster->CastSpell(m_target, spell->Id, true, NULL, NULL, GetCasterGUID()); - return; - } - m_target->RemoveAurasDueToSpell(spellId); - return; - } - // Victorious - if(GetId()==32216 && m_target->getClass()==CLASS_WARRIOR) - { - m_target->ModifyAuraState(AURA_STATE_WARRIOR_VICTORY_RUSH, apply); - return; - } - //Summon Fire Elemental - if (GetId() == 40133 && caster) - { - Unit *owner = caster->GetOwner(); - if (owner && owner->GetTypeId() == TYPEID_PLAYER) - { - if(apply) - owner->CastSpell(owner,8985,true); - else - ((Player*)owner)->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true); - } - return; - } - - //Summon Earth Elemental - if (GetId() == 40132 && caster) - { - Unit *owner = caster->GetOwner(); - if (owner && owner->GetTypeId() == TYPEID_PLAYER) - { - if(apply) - owner->CastSpell(owner,19704,true); - else - ((Player*)owner)->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true); - } - return; - } - break; - } - case SPELLFAMILY_MAGE: - { - // Hypothermia - if( GetId()==41425 ) - { - m_target->ModifyAuraState(AURA_STATE_HYPOTHERMIA,apply); - return; - } - break; - } - case SPELLFAMILY_DRUID: - { - // Lifebloom - if ( GetSpellProto()->SpellFamilyFlags & 0x1000000000LL ) - { - if ( apply ) - { - if ( caster ) - // prevent double apply bonuses - if(m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading()) - m_modifier.m_amount = caster->SpellHealingBonus(GetSpellProto(), m_modifier.m_amount, SPELL_DIRECT_DAMAGE, m_target); - } - else - { - // Final heal only on dispelled or duration end - if ( !(GetAuraDuration() <= 0 || m_removeMode==AURA_REMOVE_BY_DISPEL) ) - return; - - // have a look if there is still some other Lifebloom dummy aura - Unit::AuraList auras = m_target->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::iterator itr = auras.begin(); itr!=auras.end(); itr++) - if((*itr)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID && - (*itr)->GetSpellProto()->SpellFamilyFlags & 0x1000000000LL) - return; - - // final heal - m_target->CastCustomSpell(m_target,33778,&m_modifier.m_amount,NULL,NULL,true,NULL,this,GetCasterGUID()); - } - return; - } - - // Predatory Strikes - if(m_target->GetTypeId()==TYPEID_PLAYER && GetSpellProto()->SpellIconID == 1563) - { - ((Player*)m_target)->UpdateAttackPowerAndDamage(); - return; - } - // Idol of the Emerald Queen - if ( GetId() == 34246 && m_target->GetTypeId()==TYPEID_PLAYER ) - { - if(apply) - { - SpellModifier *mod = new SpellModifier; - mod->op = SPELLMOD_DOT; - mod->value = m_modifier.m_amount/7; - mod->type = SPELLMOD_FLAT; - mod->spellId = GetId(); - mod->effectId = m_effIndex; - mod->lastAffected = NULL; - mod->mask = 0x001000000000LL; - mod->charges = 0; - - m_spellmod = mod; - } - - ((Player*)m_target)->AddSpellMod(m_spellmod, apply); - return; - } - break; - } - case SPELLFAMILY_HUNTER: - { - // Improved Aspect of the Viper - if( GetId()==38390 && m_target->GetTypeId()==TYPEID_PLAYER ) - { - if(apply) - { - // + effect value for Aspect of the Viper - SpellModifier *mod = new SpellModifier; - mod->op = SPELLMOD_EFFECT1; - mod->value = m_modifier.m_amount; - mod->type = SPELLMOD_FLAT; - mod->spellId = GetId(); - mod->effectId = m_effIndex; - mod->lastAffected = NULL; - mod->mask = 0x4000000000000LL; - mod->charges = 0; - - m_spellmod = mod; - } - - ((Player*)m_target)->AddSpellMod(m_spellmod, apply); - return; - } - break; - } - case SPELLFAMILY_SHAMAN: - { - // Improved Weapon Totems - if( GetSpellProto()->SpellIconID == 57 && m_target->GetTypeId()==TYPEID_PLAYER ) - { - if(apply) - { - SpellModifier *mod = new SpellModifier; - mod->op = SPELLMOD_EFFECT1; - mod->value = m_modifier.m_amount; - mod->type = SPELLMOD_PCT; - mod->spellId = GetId(); - mod->effectId = m_effIndex; - mod->lastAffected = NULL; - switch (m_effIndex) - { - case 0: - mod->mask = 0x00200000000LL; // Windfury Totem - break; - case 1: - mod->mask = 0x00400000000LL; // Flametongue Totem - break; - } - mod->charges = 0; - - m_spellmod = mod; - } - - ((Player*)m_target)->AddSpellMod(m_spellmod, apply); - return; - } - break; - } - } - - // pet auras - if(PetAura const* petSpell = spellmgr.GetPetAura(GetId())) - { - if(apply) - m_target->AddPetAura(petSpell); - else - m_target->RemovePetAura(petSpell); - return; - } -} - -void Aura::HandleAuraPeriodicDummy(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - SpellEntry const*spell = GetSpellProto(); - switch( spell->SpellFamilyName) - { - case SPELLFAMILY_ROGUE: - { - // Master of Subtlety - if (spell->Id==31666 && !apply && Real) - { - m_target->RemoveAurasDueToSpell(31665); - break; - } - break; - } - case SPELLFAMILY_HUNTER: - { - // Aspect of the Viper - if (spell->SpellFamilyFlags&0x0004000000000000LL) - { - // Update regen on remove - if (!apply && m_target->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_target)->UpdateManaRegen(); - break; - } - break; - } - } - - m_isPeriodic = apply; -} - -void Aura::HandleAuraMounted(bool apply, bool Real) -{ - if(apply) - { - CreatureInfo const* ci = objmgr.GetCreatureTemplate(m_modifier.m_miscvalue); - if(!ci) - { - sLog.outErrorDb("AuraMounted: `creature_template`='%u' not found in database (only need it modelid)", m_modifier.m_miscvalue); - return; - } - - uint32 team = 0; - if (m_target->GetTypeId()==TYPEID_PLAYER) - team = ((Player*)m_target)->GetTeam(); - - uint32 display_id = objmgr.ChooseDisplayId(team,ci); - CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id); - if (minfo) - display_id = minfo->modelid; - - m_target->Mount(display_id); - } - else - { - m_target->Unmount(); - } -} - -void Aura::HandleAuraWaterWalk(bool apply, bool Real) -{ - // only at real add/remove aura - if(!Real) - return; - - WorldPacket data; - if(apply) - data.Initialize(SMSG_MOVE_WATER_WALK, 8+4); - else - data.Initialize(SMSG_MOVE_LAND_WALK, 8+4); - data.append(m_target->GetPackGUID()); - data << uint32(0); - m_target->SendMessageToSet(&data,true); -} - -void Aura::HandleAuraFeatherFall(bool apply, bool Real) -{ - // only at real add/remove aura - if(!Real) - return; - - WorldPacket data; - if(apply) - data.Initialize(SMSG_MOVE_FEATHER_FALL, 8+4); - else - data.Initialize(SMSG_MOVE_NORMAL_FALL, 8+4); - data.append(m_target->GetPackGUID()); - data << (uint32)0; - m_target->SendMessageToSet(&data,true); -} - -void Aura::HandleAuraHover(bool apply, bool Real) -{ - // only at real add/remove aura - if(!Real) - return; - - WorldPacket data; - if(apply) - data.Initialize(SMSG_MOVE_SET_HOVER, 8+4); - else - data.Initialize(SMSG_MOVE_UNSET_HOVER, 8+4); - data.append(m_target->GetPackGUID()); - data << uint32(0); - m_target->SendMessageToSet(&data,true); -} - -void Aura::HandleWaterBreathing(bool apply, bool Real) -{ - if(apply) - m_target->waterbreath = true; - else if(m_target->GetAurasByType(SPELL_AURA_WATER_BREATHING).empty()) - { - m_target->waterbreath = false; - - // update for enable timer in case not moving target - if(m_target->GetTypeId()==TYPEID_PLAYER && m_target->IsInWorld()) - { - ((Player*)m_target)->UpdateUnderwaterState(m_target->GetMap(),m_target->GetPositionX(),m_target->GetPositionY(),m_target->GetPositionZ()); - ((Player*)m_target)->HandleDrowning(); - } - } -} - -void Aura::HandleAuraModShapeshift(bool apply, bool Real) -{ - if(!Real) - return; - - uint32 modelid = 0; - Powers PowerType = POWER_MANA; - ShapeshiftForm form = ShapeshiftForm(m_modifier.m_miscvalue); - switch(form) - { - case FORM_CAT: - if(Player::TeamForRace(m_target->getRace())==ALLIANCE) - modelid = 892; - else - modelid = 8571; - PowerType = POWER_ENERGY; - break; - case FORM_TRAVEL: - modelid = 632; - break; - case FORM_AQUA: - if(Player::TeamForRace(m_target->getRace())==ALLIANCE) - modelid = 2428; - else - modelid = 2428; - break; - case FORM_BEAR: - if(Player::TeamForRace(m_target->getRace())==ALLIANCE) - modelid = 2281; - else - modelid = 2289; - PowerType = POWER_RAGE; - break; - case FORM_GHOUL: - if(Player::TeamForRace(m_target->getRace())==ALLIANCE) - modelid = 10045; - break; - case FORM_DIREBEAR: - if(Player::TeamForRace(m_target->getRace())==ALLIANCE) - modelid = 2281; - else - modelid = 2289; - PowerType = POWER_RAGE; - break; - case FORM_CREATUREBEAR: - modelid = 902; - break; - case FORM_GHOSTWOLF: - modelid = 4613; - break; - case FORM_FLIGHT: - if(Player::TeamForRace(m_target->getRace())==ALLIANCE) - modelid = 20857; - else - modelid = 20872; - break; - case FORM_MOONKIN: - if(Player::TeamForRace(m_target->getRace())==ALLIANCE) - modelid = 15374; - else - modelid = 15375; - break; - case FORM_FLIGHT_EPIC: - if(Player::TeamForRace(m_target->getRace())==ALLIANCE) - modelid = 21243; - else - modelid = 21244; - break; - case FORM_AMBIENT: - case FORM_SHADOW: - case FORM_STEALTH: - break; - case FORM_TREE: - modelid = 864; - break; - case FORM_BATTLESTANCE: - case FORM_BERSERKERSTANCE: - case FORM_DEFENSIVESTANCE: - PowerType = POWER_RAGE; - break; - case FORM_SPIRITOFREDEMPTION: - modelid = 16031; - break; - default: - sLog.outError("Auras: Unknown Shapeshift Type: %u", m_modifier.m_miscvalue); - } - - // remove polymorph before changing display id to keep new display id - switch ( form ) - { - case FORM_CAT: - case FORM_TREE: - case FORM_TRAVEL: - case FORM_AQUA: - case FORM_BEAR: - case FORM_DIREBEAR: - case FORM_FLIGHT_EPIC: - case FORM_FLIGHT: - case FORM_MOONKIN: - // remove movement affects - m_target->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); - m_target->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED); - - // and polymorphic affects - if(m_target->IsPolymorphed()) - m_target->RemoveAurasDueToSpell(m_target->getTransForm()); - break; - default: - break; - } - - if(apply) - { - // remove other shapeshift before applying a new one - if(m_target->m_ShapeShiftFormSpellId) - { - m_target->RemoveAurasDueToSpell(m_target->m_ShapeShiftFormSpellId,this); - } - - m_target->SetByteValue(UNIT_FIELD_BYTES_2, 3, form); - - if(modelid > 0) - { - m_target->SetDisplayId(modelid); - } - - if(PowerType != POWER_MANA) - { - // reset power to default values only at power change - if(m_target->getPowerType()!=PowerType) - m_target->setPowerType(PowerType); - - switch(form) - { - case FORM_CAT: - case FORM_BEAR: - case FORM_DIREBEAR: - { - // get furor proc chance - uint32 FurorChance = 0; - Unit::AuraList const& mDummy = m_target->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator i = mDummy.begin(); i != mDummy.end(); ++i) - { - if ((*i)->GetSpellProto()->SpellIconID == 238) - { - FurorChance = (*i)->GetModifier()->m_amount; - break; - } - } - - if (m_modifier.m_miscvalue == FORM_CAT) - { - m_target->SetPower(POWER_ENERGY,0); - if(urand(1,100) <= FurorChance) - { - m_target->CastSpell(m_target,17099,true,NULL,this); - } - } - else - { - m_target->SetPower(POWER_RAGE,0); - if(urand(1,100) <= FurorChance) - { - m_target->CastSpell(m_target,17057,true,NULL,this); - } - } - break; - } - case FORM_BATTLESTANCE: - case FORM_DEFENSIVESTANCE: - case FORM_BERSERKERSTANCE: - { - uint32 Rage_val = 0; - // Stance mastery + Tactical mastery (both passive, and last have aura only in defense stance, but need apply at any stance switch) - if(m_target->GetTypeId() == TYPEID_PLAYER) - { - PlayerSpellMap const& sp_list = ((Player *)m_target)->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) - { - if(itr->second->state == PLAYERSPELL_REMOVED) continue; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); - if (spellInfo && spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && spellInfo->SpellIconID == 139) - Rage_val += m_target->CalculateSpellDamage(spellInfo,0,spellInfo->EffectBasePoints[0],m_target) * 10; - } - } - - if (m_target->GetPower(POWER_RAGE) > Rage_val) - m_target->SetPower(POWER_RAGE,Rage_val); - break; - } - default: - break; - } - } - - m_target->m_ShapeShiftFormSpellId = GetId(); - m_target->m_form = form; - } - else - { - m_target->SetDisplayId(m_target->GetNativeDisplayId()); - m_target->SetByteValue(UNIT_FIELD_BYTES_2, 3, FORM_NONE); - if(m_target->getClass() == CLASS_DRUID) - m_target->setPowerType(POWER_MANA); - m_target->m_ShapeShiftFormSpellId = 0; - m_target->m_form = FORM_NONE; - - switch(form) - { - // Nordrassil Harness - bonus - case FORM_BEAR: - case FORM_DIREBEAR: - case FORM_CAT: - { - if(Aura* dummy = m_target->GetDummyAura(37315) ) - m_target->CastSpell(m_target,37316,true,NULL,dummy); - break; - } - // Nordrassil Regalia - bonus - case FORM_MOONKIN: - { - if(Aura* dummy = m_target->GetDummyAura(37324) ) - m_target->CastSpell(m_target,37325,true,NULL,dummy); - break; - } - } - } - - // adding/removing linked auras - // add/remove the shapeshift aura's boosts - HandleShapeshiftBoosts(apply); - - if(m_target->GetTypeId()==TYPEID_PLAYER) - ((Player*)m_target)->InitDataForForm(); -} - -void Aura::HandleAuraTransform(bool apply, bool Real) -{ - if (apply) - { - // special case (spell specific functionality) - if(m_modifier.m_miscvalue==0) - { - // player applied only - if(m_target->GetTypeId()!=TYPEID_PLAYER) - return; - - switch(GetId()) - { - // Orb of Deception - case 16739: - { - uint32 orb_model = m_target->GetNativeDisplayId(); - switch(orb_model) - { - // Troll Female - case 1479: m_target->SetDisplayId(10134); break; - // Troll Male - case 1478: m_target->SetDisplayId(10135); break; - // Tauren Male - case 59: m_target->SetDisplayId(10136); break; - // Human Male - case 49: m_target->SetDisplayId(10137); break; - // Human Female - case 50: m_target->SetDisplayId(10138); break; - // Orc Male - case 51: m_target->SetDisplayId(10139); break; - // Orc Female - case 52: m_target->SetDisplayId(10140); break; - // Dwarf Male - case 53: m_target->SetDisplayId(10141); break; - // Dwarf Female - case 54: m_target->SetDisplayId(10142); break; - // NightElf Male - case 55: m_target->SetDisplayId(10143); break; - // NightElf Female - case 56: m_target->SetDisplayId(10144); break; - // Undead Female - case 58: m_target->SetDisplayId(10145); break; - // Undead Male - case 57: m_target->SetDisplayId(10146); break; - // Tauren Female - case 60: m_target->SetDisplayId(10147); break; - // Gnome Male - case 1563: m_target->SetDisplayId(10148); break; - // Gnome Female - case 1564: m_target->SetDisplayId(10149); break; - // BloodElf Female - case 15475: m_target->SetDisplayId(17830); break; - // BloodElf Male - case 15476: m_target->SetDisplayId(17829); break; - // Dranei Female - case 16126: m_target->SetDisplayId(17828); break; - // Dranei Male - case 16125: m_target->SetDisplayId(17827); break; - default: break; - } - break; - } - // Murloc costume - case 42365: m_target->SetDisplayId(21723); break; - default: break; - } - } - else - { - CreatureInfo const * ci = objmgr.GetCreatureTemplate(m_modifier.m_miscvalue); - if(!ci) - { - //pig pink ^_^ - m_target->SetDisplayId(16358); - sLog.outError("Auras: unknown creature id = %d (only need its modelid) Form Spell Aura Transform in Spell ID = %d", m_modifier.m_miscvalue, GetId()); - } - else - { - // Will use the default model here - m_target->SetDisplayId(ci->DisplayID_A); - - // Dragonmaw Illusion (set mount model also) - if(GetId()==42016 && m_target->GetMountID() && !m_target->GetAurasByType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED).empty()) - m_target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,16314); - } - m_target->setTransForm(GetId()); - } - - // polymorph case - if( Real && m_target->GetTypeId() == TYPEID_PLAYER && m_target->IsPolymorphed()) - { - // for players, start regeneration after 1s (in polymorph fast regeneration case) - // only if caster is Player (after patch 2.4.2) - if(IS_PLAYER_GUID(GetCasterGUID()) ) - ((Player*)m_target)->setRegenTimer(1000); - - //dismount polymorphed target (after patch 2.4.2) - if (m_target->IsMounted()) - m_target->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); - } - } - else - { - Unit::AuraList const& otherTransforms = m_target->GetAurasByType(SPELL_AURA_TRANSFORM); - if(otherTransforms.empty()) - { - m_target->SetDisplayId(m_target->GetNativeDisplayId()); - m_target->setTransForm(0); - } - else - { - // look for other transform auras - Aura* handledAura = *otherTransforms.begin(); - for(Unit::AuraList::const_iterator i = otherTransforms.begin();i != otherTransforms.end(); ++i) - { - // negative auras are prefered - if(!IsPositiveSpell((*i)->GetSpellProto()->Id)) - { - handledAura = *i; - break; - } - } - handledAura->ApplyModifier(true); - } - - // Dragonmaw Illusion (restore mount model) - if(GetId()==42016 && m_target->GetMountID()==16314) - { - if(!m_target->GetAurasByType(SPELL_AURA_MOUNTED).empty()) - { - uint32 cr_id = m_target->GetAurasByType(SPELL_AURA_MOUNTED).front()->GetModifier()->m_miscvalue; - if(CreatureInfo const* ci = objmgr.GetCreatureTemplate(cr_id)) - { - uint32 team = 0; - if (m_target->GetTypeId()==TYPEID_PLAYER) - team = ((Player*)m_target)->GetTeam(); - - uint32 display_id = objmgr.ChooseDisplayId(team,ci); - CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id); - if (minfo) - display_id = minfo->modelid; - - m_target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,display_id); - } - } - } - } -} - -void Aura::HandleForceReaction(bool apply, bool Real) -{ - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - - if(!Real) - return; - - Player* player = (Player*)m_target; - - uint32 faction_id = m_modifier.m_miscvalue; - uint32 faction_rank = m_modifier.m_amount; - - if(apply) - player->m_forcedReactions[faction_id] = ReputationRank(faction_rank); - else - player->m_forcedReactions.erase(faction_id); - - WorldPacket data; - data.Initialize(SMSG_SET_FORCED_REACTIONS, 4+player->m_forcedReactions.size()*(4+4)); - data << uint32(player->m_forcedReactions.size()); - for(ForcedReactions::const_iterator itr = player->m_forcedReactions.begin(); itr != player->m_forcedReactions.end(); ++itr) - { - data << uint32(itr->first); // faction_id (Faction.dbc) - data << uint32(itr->second); // reputation rank - } - player->SendDirectMessage(&data); -} - -void Aura::HandleAuraModSkill(bool apply, bool Real) -{ - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 prot=GetSpellProto()->EffectMiscValue[m_effIndex]; - int32 points = GetModifier()->m_amount; - - ((Player*)m_target)->ModifySkillBonus(prot,(apply ? points: -points),m_modifier.m_auraname==SPELL_AURA_MOD_SKILL_TALENT); - if(prot == SKILL_DEFENSE) - ((Player*)m_target)->UpdateDefenseBonusesMod(); -} - -void Aura::HandleChannelDeathItem(bool apply, bool Real) -{ - if(Real && !apply) - { - Unit* caster = GetCaster(); - Unit* victim = GetTarget(); - if(!caster || caster->GetTypeId() != TYPEID_PLAYER || !victim || m_removeMode!=AURA_REMOVE_BY_DEATH) - return; - - SpellEntry const *spellInfo = GetSpellProto(); - if(spellInfo->EffectItemType[m_effIndex] == 0) - return; - - // Soul Shard only from non-grey units - if( spellInfo->EffectItemType[m_effIndex] == 6265 && - (victim->getLevel() <= MaNGOS::XP::GetGrayLevel(caster->getLevel()) || - victim->GetTypeId()==TYPEID_UNIT && !((Player*)caster)->isAllowedToLoot((Creature*)victim)) ) - return; - ItemPosCountVec dest; - uint8 msg = ((Player*)caster)->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, spellInfo->EffectItemType[m_effIndex], 1 ); - if( msg != EQUIP_ERR_OK ) - { - ((Player*)caster)->SendEquipError( msg, NULL, NULL ); - return; - } - - Item* newitem = ((Player*)caster)->StoreNewItem(dest, spellInfo->EffectItemType[m_effIndex], true); - ((Player*)caster)->SendNewItem(newitem, 1, true, false); - } -} - -void Aura::HandleBindSight(bool apply, bool Real) -{ - Unit* caster = GetCaster(); - if(!caster || caster->GetTypeId() != TYPEID_PLAYER) - return; - - caster->SetUInt64Value(PLAYER_FARSIGHT,apply ? m_target->GetGUID() : 0); -} - -void Aura::HandleFarSight(bool apply, bool Real) -{ - Unit* caster = GetCaster(); - if(!caster || caster->GetTypeId() != TYPEID_PLAYER) - return; - - caster->SetUInt64Value(PLAYER_FARSIGHT,apply ? m_modifier.m_miscvalue : 0); -} - -void Aura::HandleAuraTrackCreatures(bool apply, bool Real) -{ - if(m_target->GetTypeId()!=TYPEID_PLAYER) - return; - - if(apply) - m_target->RemoveNoStackAurasDueToAura(this); - m_target->SetUInt32Value(PLAYER_TRACK_CREATURES, apply ? ((uint32)1)<<(m_modifier.m_miscvalue-1) : 0 ); -} - -void Aura::HandleAuraTrackResources(bool apply, bool Real) -{ - if(m_target->GetTypeId()!=TYPEID_PLAYER) - return; - - if(apply) - m_target->RemoveNoStackAurasDueToAura(this); - m_target->SetUInt32Value(PLAYER_TRACK_RESOURCES, apply ? ((uint32)1)<<(m_modifier.m_miscvalue-1): 0 ); -} - -void Aura::HandleAuraTrackStealthed(bool apply, bool Real) -{ - if(m_target->GetTypeId()!=TYPEID_PLAYER) - return; - - if(apply) - m_target->RemoveNoStackAurasDueToAura(this); - - m_target->ApplyModFlag(PLAYER_FIELD_BYTES,PLAYER_FIELD_BYTE_TRACK_STEALTHED,apply); -} - -void Aura::HandleAuraModScale(bool apply, bool Real) -{ - m_target->ApplyPercentModFloatValue(OBJECT_FIELD_SCALE_X,m_modifier.m_amount,apply); -} - -void Aura::HandleModPossess(bool apply, bool Real) -{ - if(!Real) - return; - - if(m_target->getLevel() > m_modifier.m_amount) - return; - - // not possess yourself - if(GetCasterGUID() == m_target->GetGUID()) - return; - - Unit* caster = GetCaster(); - if(!caster) - return; - - if( apply ) - { - m_target->SetCharmerGUID(GetCasterGUID()); - m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,caster->getFaction()); - caster->SetCharm(m_target); - - m_target->CombatStop(); - m_target->DeleteThreatList(); - if(m_target->GetTypeId() == TYPEID_UNIT) - { - m_target->StopMoving(); - m_target->GetMotionMaster()->Clear(); - m_target->GetMotionMaster()->MoveIdle(); - CharmInfo *charmInfo = ((Creature*)m_target)->InitCharmInfo(m_target); - charmInfo->InitPossessCreateSpells(); - } - - if(caster->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)caster)->PossessSpellInitialize(); - } - } - else - { - m_target->SetCharmerGUID(0); - - if(m_target->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)m_target)->setFactionForRace(m_target->getRace()); - } - else if(m_target->GetTypeId() == TYPEID_UNIT) - { - CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo(); - m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,cinfo->faction_A); - } - - caster->SetCharm(0); - - if(caster->GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_PET_SPELLS, 8); - data << uint64(0); - ((Player*)caster)->GetSession()->SendPacket(&data); - } - if(m_target->GetTypeId() == TYPEID_UNIT) - { - ((Creature*)m_target)->AIM_Initialize(); - - if (((Creature*)m_target)->AI()) - ((Creature*)m_target)->AI()->AttackStart(caster); - } - } - if(caster->GetTypeId() == TYPEID_PLAYER) - caster->SetUInt64Value(PLAYER_FARSIGHT,apply ? m_target->GetGUID() : 0); -} - -void Aura::HandleModPossessPet(bool apply, bool Real) -{ - if(!Real) - return; - - Unit* caster = GetCaster(); - if(!caster || caster->GetTypeId() != TYPEID_PLAYER) - return; - if(caster->GetPet() != m_target) - return; - - if(apply) - { - caster->SetUInt64Value(PLAYER_FARSIGHT, m_target->GetGUID()); - m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN5); - } - else - { - caster->SetUInt64Value(PLAYER_FARSIGHT, 0); - m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN5); - } -} - -void Aura::HandleModCharm(bool apply, bool Real) -{ - if(!Real) - return; - - // not charm yourself - if(GetCasterGUID() == m_target->GetGUID()) - return; - - Unit* caster = GetCaster(); - if(!caster) - return; - - if(int32(m_target->getLevel()) <= m_modifier.m_amount) - { - if( apply ) - { - m_target->SetCharmerGUID(GetCasterGUID()); - m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,caster->getFaction()); - m_target->CastStop(m_target==caster ? GetId() : 0); - caster->SetCharm(m_target); - - m_target->CombatStop(); - m_target->DeleteThreatList(); - - if(m_target->GetTypeId() == TYPEID_UNIT) - { - ((Creature*)m_target)->AIM_Initialize(); - CharmInfo *charmInfo = ((Creature*)m_target)->InitCharmInfo(m_target); - charmInfo->InitCharmCreateSpells(); - charmInfo->SetReactState( REACT_DEFENSIVE ); - - if(caster->GetTypeId() == TYPEID_PLAYER && caster->getClass() == CLASS_WARLOCK) - { - CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo(); - if(cinfo && cinfo->type == CREATURE_TYPE_DEMON) - { - //to prevent client crash - m_target->SetFlag(UNIT_FIELD_BYTES_0, 2048); - //just to enable stat window - charmInfo->SetPetNumber(objmgr.GeneratePetNumber(), true); - //if charmed two demons the same session, the 2nd gets the 1st one's name - m_target->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL)); - } - } - } - - if(caster->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)caster)->CharmSpellInitialize(); - } - } - else - { - m_target->SetCharmerGUID(0); - - if(m_target->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)m_target)->setFactionForRace(m_target->getRace()); - } - else - { - CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo(); - - // restore faction - if(((Creature*)m_target)->isPet()) - { - if(Unit* owner = m_target->GetOwner()) - m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,owner->getFaction()); - else if(cinfo) - m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,cinfo->faction_A); - } - else if(cinfo) // normal creature - m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,cinfo->faction_A); - - // restore UNIT_FIELD_BYTES_0 - if(cinfo && caster->GetTypeId() == TYPEID_PLAYER && caster->getClass() == CLASS_WARLOCK && cinfo->type == CREATURE_TYPE_DEMON) - { - CreatureDataAddon const *cainfo = ((Creature*)m_target)->GetCreatureAddon(); - if(cainfo && cainfo->bytes0 != 0) - m_target->SetUInt32Value(UNIT_FIELD_BYTES_0, cainfo->bytes0); - else - m_target->RemoveFlag(UNIT_FIELD_BYTES_0, 2048); - - if(m_target->GetCharmInfo()) - m_target->GetCharmInfo()->SetPetNumber(0, true); - else - sLog.outError("Aura::HandleModCharm: target="I64FMTD" with typeid=%d has a charm aura but no charm info!", m_target->GetGUID(), m_target->GetTypeId()); - } - } - - caster->SetCharm(0); - - if(caster->GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_PET_SPELLS, 8); - data << uint64(0); - ((Player*)caster)->GetSession()->SendPacket(&data); - } - if(m_target->GetTypeId() == TYPEID_UNIT) - { - ((Creature*)m_target)->AIM_Initialize(); - if (((Creature*)m_target)->AI()) - ((Creature*)m_target)->AI()->AttackStart(caster); - } - } - } -} - -void Aura::HandleModConfuse(bool apply, bool Real) -{ - if(!Real) - return; - - m_target->SetConfused(apply, GetCasterGUID(), GetId()); -} - -void Aura::HandleModFear(bool apply, bool Real) -{ - if (!Real) - return; - - m_target->SetFeared(apply, GetCasterGUID(), GetId()); -} - -void Aura::HandleFeignDeath(bool apply, bool Real) -{ - if(!Real) - return; - - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - - if( apply ) - { - /* - WorldPacket data(SMSG_FEIGN_DEATH_RESISTED, 9); - data<<m_target->GetGUID(); - data<<uint8(0); - m_target->SendMessageToSet(&data,true); - */ - // blizz like 2.0.x - m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN6); - // blizz like 2.0.x - m_target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); - // blizz like 2.0.x - m_target->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); - - m_target->addUnitState(UNIT_STAT_DIED); - m_target->CombatStop(); - - // prevent interrupt message - if(m_caster_guid==m_target->GetGUID() && m_target->m_currentSpells[CURRENT_GENERIC_SPELL]) - m_target->m_currentSpells[CURRENT_GENERIC_SPELL]->finish(); - m_target->InterruptNonMeleeSpells(true); - m_target->getHostilRefManager().deleteReferences(); - } - else - { - /* - WorldPacket data(SMSG_FEIGN_DEATH_RESISTED, 9); - data<<m_target->GetGUID(); - data<<uint8(1); - m_target->SendMessageToSet(&data,true); - */ - // blizz like 2.0.x - m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN6); - // blizz like 2.0.x - m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); - // blizz like 2.0.x - m_target->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); - - m_target->clearUnitState(UNIT_STAT_DIED); - } -} - -void Aura::HandleAuraModDisarm(bool apply, bool Real) -{ - if(!Real) - return; - - if(!apply && m_target->HasAuraType(SPELL_AURA_MOD_DISARM)) - return; - - // not sure for it's correctness - if(apply) - m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED); - else - m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED); - - // only at real add/remove aura - if (m_target->GetTypeId() != TYPEID_PLAYER) - return; - - // main-hand attack speed already set to special value for feral form already and don't must chnage and reset at remove. - if (((Player *)m_target)->IsInFeralForm()) - return; - - if (apply) - m_target->SetAttackTime(BASE_ATTACK,BASE_ATTACK_TIME); - else - ((Player *)m_target)->SetRegularAttackTime(); - - m_target->UpdateDamagePhysical(BASE_ATTACK); -} - -void Aura::HandleAuraModStun(bool apply, bool Real) -{ - if(!Real) - return; - - if (apply) - { - m_target->addUnitState(UNIT_STAT_STUNDED); - m_target->SetUInt64Value(UNIT_FIELD_TARGET, 0); - - m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); - m_target->CastStop(m_target->GetGUID() == GetCasterGUID() ? GetId() : 0); - - // Creature specific - if(m_target->GetTypeId() != TYPEID_PLAYER) - ((Creature*)m_target)->StopMoving(); - else - m_target->SetUnitMovementFlags(0); //Clear movement flags - - WorldPacket data(SMSG_FORCE_MOVE_ROOT, 8); - - data.append(m_target->GetPackGUID()); - data << uint32(0); - m_target->SendMessageToSet(&data,true); - } - else - { - // Real remove called after current aura remove from lists, check if other similar auras active - if(m_target->HasAuraType(SPELL_AURA_MOD_STUN)) - return; - - m_target->clearUnitState(UNIT_STAT_STUNDED); - m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); - - if(!m_target->hasUnitState(UNIT_STAT_ROOT)) // prevent allow move if have also root effect - { - if(m_target->getVictim() && m_target->isAlive()) - m_target->SetUInt64Value(UNIT_FIELD_TARGET,m_target->getVictim()->GetGUID() ); - - WorldPacket data(SMSG_FORCE_MOVE_UNROOT, 8+4); - data.append(m_target->GetPackGUID()); - data << uint32(0); - m_target->SendMessageToSet(&data,true); - } - - // Wyvern Sting - if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_HUNTER && GetSpellProto()->SpellIconID == 1721) - { - Unit* caster = GetCaster(); - if( !caster || caster->GetTypeId()!=TYPEID_PLAYER ) - return; - - uint32 spell_id = 0; - - switch(GetId()) - { - case 19386: spell_id = 24131; break; - case 24132: spell_id = 24134; break; - case 24133: spell_id = 24135; break; - case 27068: spell_id = 27069; break; - default: - sLog.outError("Spell selection called for unexpected original spell %u, new spell for this spell family?",GetId()); - return; - } - - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id); - - if(!spellInfo) - return; - - caster->CastSpell(m_target,spellInfo,true,NULL,this); - return; - } - } -} - -void Aura::HandleModStealth(bool apply, bool Real) -{ - if(apply) - { - // drop flag at stealth in bg - if(Real && m_target->GetTypeId()==TYPEID_PLAYER && ((Player*)m_target)->InBattleGround()) - if(BattleGround *bg = ((Player*)m_target)->GetBattleGround()) - bg->EventPlayerDroppedFlag((Player*)m_target); - - // only at real aura add - if(Real) - { - m_target->SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x02); - if(m_target->GetTypeId()==TYPEID_PLAYER) - m_target->SetFlag(PLAYER_FIELD_BYTES2, 0x2000); - - // apply only if not in GM invisibility (and overwrite invisibility state) - if(m_target->GetVisibility()!=VISIBILITY_OFF) - { - m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT); - m_target->SetVisibility(VISIBILITY_GROUP_STEALTH); - } - - // for RACE_NIGHTELF stealth - if(m_target->GetTypeId()==TYPEID_PLAYER && GetId()==20580) - m_target->CastSpell(m_target, 21009, true, NULL, this); - } - } - else - { - // only at real aura remove - if(Real) - { - // for RACE_NIGHTELF stealth - if(m_target->GetTypeId()==TYPEID_PLAYER && GetId()==20580) - m_target->RemoveAurasDueToSpell(21009); - - // if last SPELL_AURA_MOD_STEALTH and no GM invisibility - if(!m_target->HasAuraType(SPELL_AURA_MOD_STEALTH) && m_target->GetVisibility()!=VISIBILITY_OFF) - { - m_target->SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x00); - if(m_target->GetTypeId()==TYPEID_PLAYER) - m_target->RemoveFlag(PLAYER_FIELD_BYTES2, 0x2000); - - // restore invisibility if any - if(m_target->HasAuraType(SPELL_AURA_MOD_INVISIBILITY)) - { - m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT); - m_target->SetVisibility(VISIBILITY_GROUP_INVISIBILITY); - } - else - m_target->SetVisibility(VISIBILITY_ON); - } - } - } - - // Master of Subtlety - Unit::AuraList const& mDummyAuras = m_target->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i) - { - if ((*i)->GetSpellProto()->SpellIconID == 2114) - { - if (apply) - { - int32 bp = (*i)->GetModifier()->m_amount; - m_target->CastCustomSpell(m_target,31665,&bp,NULL,NULL,true); - } - else - m_target->CastSpell(m_target,31666,true); - break; - } - } -} - -void Aura::HandleInvisibility(bool apply, bool Real) -{ - if(apply) - { - m_target->m_invisibilityMask |= (1 << m_modifier.m_miscvalue); - - if(Real && m_target->GetTypeId()==TYPEID_PLAYER) - { - // apply glow vision - m_target->SetFlag(PLAYER_FIELD_BYTES2,PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW); - - // drop flag at invisible in bg - if(((Player*)m_target)->InBattleGround()) - if(BattleGround *bg = ((Player*)m_target)->GetBattleGround()) - bg->EventPlayerDroppedFlag((Player*)m_target); - } - - // apply only if not in GM invisibility and not stealth - if(m_target->GetVisibility()==VISIBILITY_ON) - { - // Aura not added yet but visibility code expect temporary add aura - m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT); - m_target->SetVisibility(VISIBILITY_GROUP_INVISIBILITY); - } - } - else - { - // recalculate value at modifier remove (current aura already removed) - m_target->m_invisibilityMask = 0; - Unit::AuraList const& auras = m_target->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY); - for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - m_target->m_invisibilityMask |= (1 << m_modifier.m_miscvalue); - - // only at real aura remove and if not have different invisibility auras. - if(Real && m_target->m_invisibilityMask==0) - { - // remove glow vision - if(m_target->GetTypeId() == TYPEID_PLAYER) - m_target->RemoveFlag(PLAYER_FIELD_BYTES2,PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW); - - // apply only if not in GM invisibility & not stealthed while invisible - if(m_target->GetVisibility()!=VISIBILITY_OFF) - { - // if have stealth aura then already have stealth visibility - if(!m_target->HasAuraType(SPELL_AURA_MOD_STEALTH)) - m_target->SetVisibility(VISIBILITY_ON); - } - } - } -} - -void Aura::HandleInvisibilityDetect(bool apply, bool Real) -{ - if(apply) - { - m_target->m_detectInvisibilityMask |= (1 << m_modifier.m_miscvalue); - } - else - { - // recalculate value at modifier remove (current aura already removed) - m_target->m_detectInvisibilityMask = 0; - Unit::AuraList const& auras = m_target->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY_DETECTION); - for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - m_target->m_detectInvisibilityMask |= (1 << m_modifier.m_miscvalue); - } - if(Real && m_target->GetTypeId()==TYPEID_PLAYER) - ObjectAccessor::UpdateVisibilityForPlayer((Player*)m_target); -} - -void Aura::HandleAuraModRoot(bool apply, bool Real) -{ - // only at real add/remove aura - if(!Real) - return; - - uint32 apply_stat = UNIT_STAT_ROOT; - if (apply) - { - m_target->addUnitState(UNIT_STAT_ROOT); - m_target->SetUInt64Value (UNIT_FIELD_TARGET, 0); - // probably wrong - m_target->SetFlag(UNIT_FIELD_FLAGS,(apply_stat<<16)); - - //Save last orientation - if( m_target->getVictim() ) - m_target->SetOrientation(m_target->GetAngle(m_target->getVictim())); - - if(m_target->GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_FORCE_MOVE_ROOT, 10); - data.append(m_target->GetPackGUID()); - data << (uint32)2; - m_target->SendMessageToSet(&data,true); - - //Clear unit movement flags - m_target->SetUnitMovementFlags(0); - } - else - ((Creature *)m_target)->StopMoving(); - } - else - { - // Real remove called after current aura remove from lists, check if other similar auras active - if(m_target->HasAuraType(SPELL_AURA_MOD_ROOT)) - return; - - m_target->clearUnitState(UNIT_STAT_ROOT); - // probably wrong - m_target->RemoveFlag(UNIT_FIELD_FLAGS,(apply_stat<<16)); - - if(!m_target->hasUnitState(UNIT_STAT_STUNDED)) // prevent allow move if have also stun effect - { - if(m_target->getVictim() && m_target->isAlive()) - m_target->SetUInt64Value (UNIT_FIELD_TARGET,m_target->getVictim()->GetGUID() ); - - if(m_target->GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_FORCE_MOVE_UNROOT, 10); - data.append(m_target->GetPackGUID()); - data << (uint32)2; - m_target->SendMessageToSet(&data,true); - } - } - } -} - -void Aura::HandleAuraModSilence(bool apply, bool Real) -{ - // only at real add/remove aura - if(!Real) - return; - - if(apply) - { - m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED); - // Stop cast only spells vs PreventionType == SPELL_PREVENTION_TYPE_SILENCE - for (uint32 i = CURRENT_MELEE_SPELL; i < CURRENT_MAX_SPELL;i++) - { - Spell* currentSpell = m_target->m_currentSpells[i]; - if (currentSpell && currentSpell->m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE) - { - uint32 state = currentSpell->getState(); - // Stop spells on prepere or casting state - if ( state == SPELL_STATE_PREPARING || state == SPELL_STATE_CASTING ) - { - currentSpell->cancel(); - currentSpell->SetDeletable(true); - m_target->m_currentSpells[i] = NULL; - } - } - } - - switch (GetId()) - { - // Arcane Torrent (Energy) - case 25046: - { - Unit * caster = GetCaster(); - if (!caster) - return; - - // Search Mana Tap auras on caster - int32 energy = 0; - Unit::AuraList const& m_dummyAuras = caster->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i) - if ((*i)->GetId() == 28734) - ++energy; - if (energy) - { - energy *= 10; - caster->CastCustomSpell(caster, 25048, &energy, NULL, NULL, true); - caster->RemoveAurasDueToSpell(28734); - } - } - } - } - else - { - // Real remove called after current aura remove from lists, check if other similar auras active - if(m_target->HasAuraType(SPELL_AURA_MOD_SILENCE)) - return; - - m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED); - } -} - -void Aura::HandleModThreat(bool apply, bool Real) -{ - // only at real add/remove aura - if(!Real) - return; - - if(!m_target->isAlive()) - return; - - Unit* caster = GetCaster(); - - if(!caster || !caster->isAlive()) - return; - - int level_diff = 0; - int multiplier = 0; - switch (GetId()) - { - // Arcane Shroud - case 26400: - level_diff = m_target->getLevel() - 60; - multiplier = 2; - break; - // The Eye of Diminution - case 28862: - level_diff = m_target->getLevel() - 60; - multiplier = 1; - break; - } - if (level_diff > 0) - m_modifier.m_amount += multiplier * level_diff; - - for(int8 x=0;x < MAX_SPELL_SCHOOL;x++) - { - if(m_modifier.m_miscvalue & int32(1<<x)) - { - if(m_target->GetTypeId() == TYPEID_PLAYER) - ApplyPercentModFloatVar(m_target->m_threatModifier[x], m_positive ? m_modifier.m_amount : -m_modifier.m_amount, apply); - } - } -} - -void Aura::HandleAuraModTotalThreat(bool apply, bool Real) -{ - // only at real add/remove aura - if(!Real) - return; - - if(!m_target->isAlive() || m_target->GetTypeId()!= TYPEID_PLAYER) - return; - - Unit* caster = GetCaster(); - - if(!caster || !caster->isAlive()) - return; - - float threatMod = 0.0f; - if(apply) - threatMod = float(m_modifier.m_amount); - else - threatMod = float(-m_modifier.m_amount); - - m_target->getHostilRefManager().threatAssist(caster, threatMod); -} - -void Aura::HandleModTaunt(bool apply, bool Real) -{ - // only at real add/remove aura - if(!Real) - return; - - if(!m_target->isAlive() || !m_target->CanHaveThreatList()) - return; - - Unit* caster = GetCaster(); - - if(!caster || !caster->isAlive() || caster->GetTypeId() != TYPEID_PLAYER) - return; - - if(apply) - { - m_target->TauntApply(caster); - } - else - { - // When taunt aura fades out, mob will switch to previous target if current has less than 1.1 * secondthreat - m_target->TauntFadeOut(caster); - } -} - -/*********************************************************/ -/*** MODIFY SPEED ***/ -/*********************************************************/ -void Aura::HandleAuraModIncreaseSpeed(bool apply, bool Real) -{ - // all applied/removed only at real aura add/remove - if(!Real) - return; - - m_target->UpdateSpeed(MOVE_RUN, true); -} - -void Aura::HandleAuraModIncreaseMountedSpeed(bool apply, bool Real) -{ - // all applied/removed only at real aura add/remove - if(!Real) - return; - - m_target->UpdateSpeed(MOVE_RUN, true); -} - -void Aura::HandleAuraModIncreaseFlightSpeed(bool apply, bool Real) -{ - // all applied/removed only at real aura add/remove - if(!Real) - return; - - // Enable Fly mode for flying mounts - if (m_modifier.m_auraname == SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED) - { - WorldPacket data; - if(apply) - data.Initialize(SMSG_MOVE_SET_CAN_FLY, 12); - else - data.Initialize(SMSG_MOVE_UNSET_CAN_FLY, 12); - data.append(m_target->GetPackGUID()); - data << uint32(0); // unknown - m_target->SendMessageToSet(&data, true); - - //Players on flying mounts must be immune to polymorph - if (m_target->GetTypeId()==TYPEID_PLAYER) - m_target->ApplySpellImmune(GetId(),IMMUNITY_MECHANIC,MECHANIC_POLYMORPH,apply); - - // Dragonmaw Illusion (overwrite mount model, mounted aura already applied) - if( apply && m_target->HasAura(42016,0) && m_target->GetMountID()) - m_target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,16314); - } - - m_target->UpdateSpeed(MOVE_FLY, true); -} - -void Aura::HandleAuraModIncreaseSwimSpeed(bool apply, bool Real) -{ - // all applied/removed only at real aura add/remove - if(!Real) - return; - - m_target->UpdateSpeed(MOVE_SWIM, true); -} - -void Aura::HandleAuraModDecreaseSpeed(bool apply, bool Real) -{ - // all applied/removed only at real aura add/remove - if(!Real) - return; - - m_target->UpdateSpeed(MOVE_RUN, true); - m_target->UpdateSpeed(MOVE_SWIM, true); - m_target->UpdateSpeed(MOVE_FLY, true); -} - -void Aura::HandleAuraModUseNormalSpeed(bool apply, bool Real) -{ - // all applied/removed only at real aura add/remove - if(!Real) - return; - - m_target->UpdateSpeed(MOVE_RUN, true); - m_target->UpdateSpeed(MOVE_SWIM, true); - m_target->UpdateSpeed(MOVE_FLY, true); -} - -/*********************************************************/ -/*** IMMUNITY ***/ -/*********************************************************/ - -void Aura::HandleModMechanicImmunity(bool apply, bool Real) -{ - uint32 mechanic = 1 << m_modifier.m_miscvalue; - - //immune movement impairment and loss of control - if(GetId()==42292) - mechanic=IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; - - if(apply && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY) - { - Unit::AuraMap& Auras = m_target->GetAuras(); - for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next) - { - next = iter; - ++next; - SpellEntry const *spell = iter->second->GetSpellProto(); - if (!( spell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) // spells unaffected by invulnerability - && !iter->second->IsPositive() // only remove negative spells - && spell->Id != GetId()) - { - //check for mechanic mask - if(GetSpellMechanicMask(spell, iter->second->GetEffIndex()) & mechanic) - { - m_target->RemoveAurasDueToSpell(spell->Id); - if(Auras.empty()) - break; - else - next = Auras.begin(); - } - } - } - } - - m_target->ApplySpellImmune(GetId(),IMMUNITY_MECHANIC,m_modifier.m_miscvalue,apply); - - // special cases - switch(m_modifier.m_miscvalue) - { - case MECHANIC_INVULNERABILITY: - m_target->ModifyAuraState(AURA_STATE_FORBEARANCE,apply); - break; - case MECHANIC_SHIELD: - m_target->ModifyAuraState(AURA_STATE_WEAKENED_SOUL,apply); - break; - } - - // Bestial Wrath - if ( GetSpellProto()->SpellFamilyName == SPELLFAMILY_HUNTER && GetSpellProto()->SpellIconID == 1680) - { - // The Beast Within cast on owner if talent present - if ( Unit* owner = m_target->GetOwner() ) - { - // Search talent - Unit::AuraList const& m_dummyAuras = owner->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i) - { - if ( (*i)->GetSpellProto()->SpellIconID == 2229 ) - { - if (apply) - owner->CastSpell(owner, 34471, true, 0, this); - else - owner->RemoveAurasDueToSpell(34471); - break; - } - } - } - } - - // The Beast Within and Bestial Wrath - immunity - if(GetId() == 19574 || GetId() == 34471) - { - if(apply) - { - m_target->CastSpell(m_target,24395,true); - m_target->CastSpell(m_target,24396,true); - m_target->CastSpell(m_target,24397,true); - m_target->CastSpell(m_target,26592,true); - } - else - { - m_target->RemoveAurasDueToSpell(24395); - m_target->RemoveAurasDueToSpell(24396); - m_target->RemoveAurasDueToSpell(24397); - m_target->RemoveAurasDueToSpell(26592); - } - } -} - -void Aura::HandleAuraModEffectImmunity(bool apply, bool Real) -{ - if(!apply) - { - if(m_target->GetTypeId() == TYPEID_PLAYER) - { - if(((Player*)m_target)->InBattleGround()) - { - BattleGround *bg = ((Player*)m_target)->GetBattleGround(); - if(bg) - { - switch(bg->GetTypeID()) - { - case BATTLEGROUND_AV: - { - break; - } - case BATTLEGROUND_WS: - { - // Warsong Flag, horde // Silverwing Flag, alliance - if(GetId() == 23333 || GetId() == 23335) - bg->EventPlayerDroppedFlag(((Player*)m_target)); - break; - } - case BATTLEGROUND_AB: - { - break; - } - case BATTLEGROUND_EY: - { - if(GetId() == 34976) - bg->EventPlayerDroppedFlag(((Player*)m_target)); - break; - } - } - } - } - } - } - - m_target->ApplySpellImmune(GetId(),IMMUNITY_EFFECT,m_modifier.m_miscvalue,apply); -} - -void Aura::HandleAuraModStateImmunity(bool apply, bool Real) -{ - if(apply && Real && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY) - { - Unit::AuraList const& auraList = m_target->GetAurasByType(AuraType(m_modifier.m_miscvalue)); - for(Unit::AuraList::const_iterator itr = auraList.begin(); itr != auraList.end();) - { - if (auraList.front() != this) // skip itself aura (it already added) - { - m_target->RemoveAurasDueToSpell(auraList.front()->GetId()); - itr = auraList.begin(); - } - else - ++itr; - } - } - - m_target->ApplySpellImmune(GetId(),IMMUNITY_STATE,m_modifier.m_miscvalue,apply); -} - -void Aura::HandleAuraModSchoolImmunity(bool apply, bool Real) -{ - m_target->ApplySpellImmune(GetId(),IMMUNITY_SCHOOL,m_modifier.m_miscvalue,apply); - - if(Real && apply && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY) - { - if(IsPositiveSpell(GetId())) //Only positive immunity removes auras - { - uint32 school_mask = m_modifier.m_miscvalue; - Unit::AuraMap& Auras = m_target->GetAuras(); - for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next) - { - next = iter; - ++next; - SpellEntry const *spell = iter->second->GetSpellProto(); - if((GetSpellSchoolMask(spell) & school_mask)//Check for school mask - && !( spell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) //Spells unaffected by invulnerability - && !iter->second->IsPositive() //Don't remove positive spells - && spell->Id != GetId() ) //Don't remove self - { - m_target->RemoveAurasDueToSpell(spell->Id); - if(Auras.empty()) - break; - else - next = Auras.begin(); - } - } - } - } - if( Real && GetSpellProto()->Mechanic == MECHANIC_BANISH ) - { - if( apply ) - m_target->addUnitState(UNIT_STAT_ISOLATED); - else - m_target->clearUnitState(UNIT_STAT_ISOLATED); - } -} - -void Aura::HandleAuraModDmgImmunity(bool apply, bool Real) -{ - m_target->ApplySpellImmune(GetId(),IMMUNITY_DAMAGE,m_modifier.m_miscvalue,apply); -} - -void Aura::HandleAuraModDispelImmunity(bool apply, bool Real) -{ - // all applied/removed only at real aura add/remove - if(!Real) - return; - - m_target->ApplySpellDispelImmunity(m_spellProto, DispelType(m_modifier.m_miscvalue), apply); -} - -void Aura::HandleAuraProcTriggerSpell(bool apply, bool Real) -{ - if(!Real) - return; - - if(apply) - { - // some spell have charges by functionality not have its in spell data - switch (GetId()) - { - case 28200: // Ascendance (Talisman of Ascendance trinket) - m_procCharges = 6; - UpdateAuraCharges(); - break; - default: break; - } - } -} - -void Aura::HandleAuraModStalked(bool apply, bool Real) -{ - // used by spells: Hunter's Mark, Mind Vision, Syndicate Tracker (MURP) DND - if(apply) - m_target->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TRACK_UNIT); - else - m_target->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TRACK_UNIT); -} - -/*********************************************************/ -/*** PERIODIC ***/ -/*********************************************************/ - -void Aura::HandlePeriodicTriggerSpell(bool apply, bool Real) -{ - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; - - m_isPeriodic = apply; - m_isTrigger = apply; - - // Curse of the Plaguebringer - if (!apply && m_spellProto->Id == 29213 && m_removeMode!=AURA_REMOVE_BY_DISPEL) - { - // Cast Wrath of the Plaguebringer if not dispelled - m_target->CastSpell(m_target, 29214, true, 0, this); - } -} - -void Aura::HandlePeriodicEnergize(bool apply, bool Real) -{ - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; - - m_isPeriodic = apply; -} - -void Aura::HandlePeriodicHeal(bool apply, bool Real) -{ - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; - - m_isPeriodic = apply; - - // only at real apply - if (Real && apply && GetSpellProto()->Mechanic == MECHANIC_BANDAGE) - { - // provided m_target as original caster to prevent apply aura caster selection for this negative buff - m_target->CastSpell(m_target,11196,true,NULL,this,m_target->GetGUID()); - } - - // For prevent double apply bonuses - bool loading = (m_target->GetTypeId() == TYPEID_PLAYER && ((Player*)m_target)->GetSession()->PlayerLoading()); - - if(!loading && apply) - { - switch (m_spellProto->SpellFamilyName) - { - case SPELLFAMILY_DRUID: - { - // Rejuvenation - if(m_spellProto->SpellFamilyFlags & 0x0000000000000010LL) - { - if(Unit* caster = GetCaster()) - { - Unit::AuraList const& classScripts = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(Unit::AuraList::const_iterator k = classScripts.begin(); k != classScripts.end(); ++k) - { - int32 tickcount = GetSpellDuration(m_spellProto) / m_spellProto->EffectAmplitude[m_effIndex]; - switch((*k)->GetModifier()->m_miscvalue) - { - case 4953: // Increased Rejuvenation Healing - Harold's Rejuvenating Broach Aura - case 4415: // Increased Rejuvenation Healing - Idol of Rejuvenation Aura - { - m_modifier.m_amount += (*k)->GetModifier()->m_amount / tickcount; - break; - } - } - } - } - } - } - } - } -} - -void Aura::HandlePeriodicDamage(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; - - m_isPeriodic = apply; - - // For prevent double apply bonuses - bool loading = (m_target->GetTypeId() == TYPEID_PLAYER && ((Player*)m_target)->GetSession()->PlayerLoading()); - - Unit *caster = GetCaster(); - - switch (m_spellProto->SpellFamilyName) - { - case SPELLFAMILY_GENERIC: - { - // Pounce Bleed - if ( m_spellProto->SpellIconID == 147 && m_spellProto->SpellVisual == 0 ) - { - // $AP*0.18/6 bonus per tick - if (apply && !loading && caster) - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 3 / 100); - return; - } - break; - } - case SPELLFAMILY_WARRIOR: - { - // Rend - if (m_spellProto->SpellFamilyFlags & 0x0000000000000020LL) - { - // 0.00743*(($MWB+$mwb)/2+$AP/14*$MWS) bonus per tick - if (apply && !loading && caster) - { - float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK); - int32 mws = caster->GetAttackTime(BASE_ATTACK); - float mwb_min = caster->GetWeaponDamageRange(BASE_ATTACK,MINDAMAGE); - float mwb_max = caster->GetWeaponDamageRange(BASE_ATTACK,MAXDAMAGE); - // WARNING! in 3.0 multipler 0.00743f change to 0.6 - m_modifier.m_amount+=int32(((mwb_min+mwb_max)/2+ap*mws/14000)*0.00743f); - } - return; - } - break; - } - case SPELLFAMILY_DRUID: - { - // Rake - if (m_spellProto->SpellFamilyFlags & 0x0000000000001000LL) - { - // $AP*0.06/3 bonus per tick - if (apply && !loading && caster) - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 2 / 100); - return; - } - // Lacerate - if (m_spellProto->SpellFamilyFlags & 0x000000010000000000LL) - { - // $AP*0.05/5 bonus per tick - if (apply && !loading && caster) - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100); - return; - } - // Rip - if (m_spellProto->SpellFamilyFlags & 0x000000000000800000LL) - { - // $AP * min(0.06*$cp, 0.24)/6 [Yes, there is no difference, wheather 4 or 5 CPs are being used] - if (apply && !loading && caster && caster->GetTypeId() == TYPEID_PLAYER) - { - uint8 cp = ((Player*)caster)->GetComboPoints(); - - // Idol of Feral Shadows. Cant be handled as SpellMod in SpellAura:Dummy due its dependency from CPs - Unit::AuraList const& dummyAuras = caster->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr) - { - if((*itr)->GetId()==34241) - { - m_modifier.m_amount += cp * (*itr)->GetModifier()->m_amount; - break; - } - } - - if (cp > 4) cp = 4; - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * cp / 100); - } - return; - } - break; - } - case SPELLFAMILY_ROGUE: - { - // Deadly poison aura state - if((m_spellProto->SpellFamilyFlags & 0x10000) && m_spellProto->SpellVisual==5100) - { - if(apply) - m_target->ModifyAuraState(AURA_STATE_DEADLY_POISON,true); - else - { - // current aura already removed, search present of another - bool found = false; - Unit::AuraList const& auras = m_target->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - SpellEntry const* itr_spell = (*itr)->GetSpellProto(); - if(itr_spell && itr_spell->SpellFamilyName==SPELLFAMILY_ROGUE && (itr_spell->SpellFamilyFlags & 0x10000) && itr_spell->SpellVisual==5100) - { - found = true; - break; - } - } - // this has been last deadly poison aura - if(!found) - m_target->ModifyAuraState(AURA_STATE_DEADLY_POISON,false); - } - return; - } - // Rupture - if (m_spellProto->SpellFamilyFlags & 0x000000000000100000LL) - { - // Dmg/tick = $AP*min(0.01*$cp, 0.03) [Like Rip: only the first three CP inrease the contribution from AP] - if (apply && !loading && caster && caster->GetTypeId() == TYPEID_PLAYER) - { - uint8 cp = ((Player*)caster)->GetComboPoints(); - if (cp > 3) cp = 3; - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * cp / 100); - } - return; - } - // Garrote - if (m_spellProto->SpellFamilyFlags & 0x000000000000000100LL) - { - // $AP*0.18/6 bonus per tick - if (apply && !loading && caster) - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 3 / 100); - return; - } - break; - } - case SPELLFAMILY_HUNTER: - { - // Serpent Sting - if (m_spellProto->SpellFamilyFlags & 0x0000000000004000LL) - { - // $RAP*0.1/5 bonus per tick - if (apply && !loading && caster) - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 10 / 500); - return; - } - // Immolation Trap - if (m_spellProto->SpellFamilyFlags & 0x0000000000000004LL && m_spellProto->SpellIconID == 678) - { - // $RAP*0.1/5 bonus per tick - if (apply && !loading && caster) - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 10 / 500); - return; - } - break; - } - case SPELLFAMILY_PALADIN: - { - // Consecration - if (m_spellProto->SpellFamilyFlags & 0x0000000000000020LL) - { - if (apply && !loading) - { - if(Unit* caster = GetCaster()) - { - Unit::AuraList const& classScripts = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(Unit::AuraList::const_iterator k = classScripts.begin(); k != classScripts.end(); ++k) - { - int32 tickcount = GetSpellDuration(m_spellProto) / m_spellProto->EffectAmplitude[m_effIndex]; - switch((*k)->GetModifier()->m_miscvalue) - { - case 5147: // Improved Consecration - Libram of the Eternal Rest - { - m_modifier.m_amount += (*k)->GetModifier()->m_amount / tickcount; - break; - } - } - } - } - } - return; - } - break; - } - default: - break; - } -} - -void Aura::HandlePeriodicDamagePCT(bool apply, bool Real) -{ - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; - - m_isPeriodic = apply; -} - -void Aura::HandlePeriodicLeech(bool apply, bool Real) -{ - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; - - m_isPeriodic = apply; -} - -void Aura::HandlePeriodicManaLeech(bool apply, bool Real) -{ - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; - - m_isPeriodic = apply; -} - -/*********************************************************/ -/*** MODIFY STATS ***/ -/*********************************************************/ - -/********************************/ -/*** RESISTANCE ***/ -/********************************/ - -void Aura::HandleAuraModResistanceExclusive(bool apply, bool Real) -{ - for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++) - { - if(m_modifier.m_miscvalue & int32(1<<x)) - { - m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_VALUE, float(m_modifier.m_amount), apply); - if(m_target->GetTypeId() == TYPEID_PLAYER) - m_target->ApplyResistanceBuffModsMod(SpellSchools(x),m_positive,m_modifier.m_amount, apply); - } - } -} - -void Aura::HandleAuraModResistance(bool apply, bool Real) -{ - for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++) - { - if(m_modifier.m_miscvalue & int32(1<<x)) - { - m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), TOTAL_VALUE, float(m_modifier.m_amount), apply); - if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet()) - m_target->ApplyResistanceBuffModsMod(SpellSchools(x),m_positive,m_modifier.m_amount, apply); - } - } - - // Faerie Fire (druid versions) - if( m_spellProto->SpellIconID == 109 && - m_spellProto->SpellFamilyName == SPELLFAMILY_DRUID && - m_spellProto->SpellFamilyFlags & 0x0000000000000400LL ) - { - m_target->ModifyAuraState(AURA_STATE_FAERIE_FIRE,apply); - } -} - -void Aura::HandleAuraModBaseResistancePCT(bool apply, bool Real) -{ - // only players have base stats - if(m_target->GetTypeId() != TYPEID_PLAYER) - { - //pets only have base armor - if(((Creature*)m_target)->isPet() && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)) - { - m_target->HandleStatModifier(UNIT_MOD_ARMOR, BASE_PCT, float(m_modifier.m_amount), apply); - } - } - else - { - for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++) - { - if(m_modifier.m_miscvalue & int32(1<<x)) - { - m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_PCT, float(m_modifier.m_amount), apply); - } - } - } -} - -void Aura::HandleModResistancePercent(bool apply, bool Real) -{ - for(int8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++) - { - if(m_modifier.m_miscvalue & int32(1<<i)) - { - m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_PCT, float(m_modifier.m_amount), apply); - if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet()) - { - m_target->ApplyResistanceBuffModsPercentMod(SpellSchools(i),true,m_modifier.m_amount, apply); - m_target->ApplyResistanceBuffModsPercentMod(SpellSchools(i),false,m_modifier.m_amount, apply); - } - } - } -} - -void Aura::HandleModBaseResistance(bool apply, bool Real) -{ - // only players have base stats - if(m_target->GetTypeId() != TYPEID_PLAYER) - { - //only pets have base stats - if(((Creature*)m_target)->isPet() && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)) - m_target->HandleStatModifier(UNIT_MOD_ARMOR, TOTAL_VALUE, float(m_modifier.m_amount), apply); - } - else - { - for(int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++) - if(m_modifier.m_miscvalue & (1<<i)) - m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_VALUE, float(m_modifier.m_amount), apply); - } -} - -/********************************/ -/*** STAT ***/ -/********************************/ - -void Aura::HandleAuraModStat(bool apply, bool Real) -{ - if (m_modifier.m_miscvalue < -2 || m_modifier.m_miscvalue > 4) - { - sLog.outError("WARNING: Spell %u effect %u have unsupported misc value (%i) for SPELL_AURA_MOD_STAT ",GetId(),GetEffIndex(),m_modifier.m_miscvalue); - return; - } - - for(int32 i = STAT_STRENGTH; i < MAX_STATS; i++) - { - // -1 or -2 is all stats ( misc < -2 checked in function beginning ) - if (m_modifier.m_miscvalue < 0 || m_modifier.m_miscvalue == i) - { - //m_target->ApplyStatMod(Stats(i), m_modifier.m_amount,apply); - m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_VALUE, float(m_modifier.m_amount), apply); - if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet()) - m_target->ApplyStatBuffMod(Stats(i),m_modifier.m_amount,apply); - } - } -} - -void Aura::HandleModPercentStat(bool apply, bool Real) -{ - if (m_modifier.m_miscvalue < -1 || m_modifier.m_miscvalue > 4) - { - sLog.outError("WARNING: Misc Value for SPELL_AURA_MOD_PERCENT_STAT not valid"); - return; - } - - // only players have base stats - if (m_target->GetTypeId() != TYPEID_PLAYER) - return; - - for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i) - { - if(m_modifier.m_miscvalue == i || m_modifier.m_miscvalue == -1) - { - m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, float(m_modifier.m_amount), apply); - } - } -} - -void Aura::HandleModSpellDamagePercentFromStat(bool apply, bool Real) -{ - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - - // Magic damage modifiers implemented in Unit::SpellDamageBonus - // This information for client side use only - // Recalculate bonus - ((Player*)m_target)->UpdateSpellDamageAndHealingBonus(); -} - -void Aura::HandleModSpellHealingPercentFromStat(bool apply, bool Real) -{ - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - - // Recalculate bonus - ((Player*)m_target)->UpdateSpellDamageAndHealingBonus(); -} - -void Aura::HandleAuraModDispelResist(bool apply, bool Real) -{ - if(!Real || !apply) - return; - - if(GetId()==33206) - m_target->CastSpell(m_target,44416,true,NULL,this,GetCasterGUID()); -} - -void Aura::HandleModSpellDamagePercentFromAttackPower(bool apply, bool Real) -{ - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - - // Magic damage modifiers implemented in Unit::SpellDamageBonus - // This information for client side use only - // Recalculate bonus - ((Player*)m_target)->UpdateSpellDamageAndHealingBonus(); -} - -void Aura::HandleModSpellHealingPercentFromAttackPower(bool apply, bool Real) -{ - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - - // Recalculate bonus - ((Player*)m_target)->UpdateSpellDamageAndHealingBonus(); -} - -void Aura::HandleModHealingDone(bool apply, bool Real) -{ - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - // implemented in Unit::SpellHealingBonus - // this information is for client side only - ((Player*)m_target)->UpdateSpellDamageAndHealingBonus(); -} - -void Aura::HandleModTotalPercentStat(bool apply, bool Real) -{ - if (m_modifier.m_miscvalue < -1 || m_modifier.m_miscvalue > 4) - { - sLog.outError("WARNING: Misc Value for SPELL_AURA_MOD_PERCENT_STAT not valid"); - return; - } - - //save current and max HP before applying aura - uint32 curHPValue = m_target->GetHealth(); - uint32 maxHPValue = m_target->GetMaxHealth(); - - for (int32 i = STAT_STRENGTH; i < MAX_STATS; i++) - { - if(m_modifier.m_miscvalue == i || m_modifier.m_miscvalue == -1) - { - m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_PCT, float(m_modifier.m_amount), apply); - if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet()) - m_target->ApplyStatPercentBuffMod(Stats(i), m_modifier.m_amount, apply ); - } - } - - //recalculate current HP/MP after applying aura modifications (only for spells with 0x10 flag) - if ((m_modifier.m_miscvalue == STAT_STAMINA) && (maxHPValue > 0) && (m_spellProto->Attributes & 0x10)) - { - // newHP = (curHP / maxHP) * newMaxHP = (newMaxHP * curHP) / maxHP -> which is better because no int -> double -> int conversion is needed - uint32 newHPValue = (m_target->GetMaxHealth() * curHPValue) / maxHPValue; - m_target->SetHealth(newHPValue); - } -} - -void Aura::HandleAuraModResistenceOfStatPercent(bool apply, bool Real) -{ - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - - if(m_modifier.m_miscvalue != SPELL_SCHOOL_MASK_NORMAL) - { - // support required adding replace UpdateArmor by loop by UpdateResistence at intelect update - // and include in UpdateResistence same code as in UpdateArmor for aura mod apply. - sLog.outError("Aura SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT(182) need adding support for non-armor resistences!"); - return; - } - - // Recalculate Armor - m_target->UpdateArmor(); -} - -/********************************/ -/*** HEAL & ENERGIZE ***/ -/********************************/ -void Aura::HandleAuraModTotalHealthPercentRegen(bool apply, bool Real) -{ - /* - Need additional checking for auras who reduce or increase healing, magic effect like Dumpen Magic, - so this aura not fully working. - */ - if(apply) - { - if(!m_target->isAlive()) - return; - - if((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && !m_target->IsSitState()) - m_target->SetStandState(PLAYER_STATE_SIT); - - if(m_periodicTimer <= 0) - { - m_periodicTimer += m_modifier.periodictime; - - if(m_target->GetHealth() < m_target->GetMaxHealth()) - { - // PeriodicTick can cast triggered spells with stats changes - PeriodicTick(); - } - } - } - - m_isPeriodic = apply; -} - -void Aura::HandleAuraModTotalManaPercentRegen(bool apply, bool Real) -{ - if((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && apply && !m_target->IsSitState()) - m_target->SetStandState(PLAYER_STATE_SIT); - if(apply) - { - if(m_modifier.periodictime == 0) - m_modifier.periodictime = 1000; - if(m_periodicTimer <= 0 && m_target->getPowerType() == POWER_MANA) - { - m_periodicTimer += m_modifier.periodictime; - - if(m_target->GetPower(POWER_MANA) < m_target->GetMaxPower(POWER_MANA)) - { - // PeriodicTick can cast triggered spells with stats changes - PeriodicTick(); - } - } - } - - m_isPeriodic = apply; -} - -void Aura::HandleModRegen(bool apply, bool Real) // eating -{ - if(apply) - { - if(!m_target->isAlive()) - return; - - if ((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && !m_target->IsSitState()) - m_target->SetStandState(PLAYER_STATE_SIT); - - if(m_periodicTimer <= 0) - { - m_periodicTimer += 5000; - int32 gain = m_target->ModifyHealth(m_modifier.m_amount); - Unit *caster = GetCaster(); - if (caster) - { - SpellEntry const *spellProto = GetSpellProto(); - if (spellProto) - m_target->getHostilRefManager().threatAssist(caster, float(gain) * 0.5f, spellProto); - } - } - } - - m_isPeriodic = apply; -} - -void Aura::HandleModPowerRegen(bool apply, bool Real) // drinking -{ - if ((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && apply && !m_target->IsSitState()) - m_target->SetStandState(PLAYER_STATE_SIT); - - if(apply && m_periodicTimer <= 0) - { - m_periodicTimer += 2000; - - Powers pt = m_target->getPowerType(); - if(int32(pt) != m_modifier.m_miscvalue) - return; - - if ( GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED ) - { - // eating anim - m_target->HandleEmoteCommand(EMOTE_ONESHOT_EAT); - } - else if( GetId() == 20577 ) - { - // cannibalize anim - m_target->HandleEmoteCommand(398); - } - - // Warrior talent, gain 1 rage every 3 seconds while in combat - if(pt == POWER_RAGE && m_target->isInCombat()) - { - m_target->ModifyPower(pt, m_modifier.m_amount*10/17); - m_periodicTimer += 1000; - } - } - m_isPeriodic = apply; - if (Real && m_target->GetTypeId() == TYPEID_PLAYER && m_modifier.m_miscvalue == POWER_MANA) - ((Player*)m_target)->UpdateManaRegen(); -} - -void Aura::HandleModPowerRegenPCT(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - if (m_target->GetTypeId() != TYPEID_PLAYER) - return; - - // Update manaregen value - if (m_modifier.m_miscvalue == POWER_MANA) - ((Player*)m_target)->UpdateManaRegen(); -} - -void Aura::HandleModManaRegen(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - if (m_target->GetTypeId() != TYPEID_PLAYER) - return; - - //Note: an increase in regen does NOT cause threat. - ((Player*)m_target)->UpdateManaRegen(); -} - -void Aura::HandleComprehendLanguage(bool apply, bool Real) -{ - if(apply) - m_target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_COMPREHEND_LANG); - else - m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_COMPREHEND_LANG); -} - -void Aura::HandleAuraModIncreaseHealth(bool apply, bool Real) -{ - // Special case with temporary increase max/current health - switch(GetId()) - { - case 12976: // Warrior Last Stand triggered spell - case 28726: // Nightmare Seed ( Nightmare Seed ) - case 34511: // Valor (Bulwark of Kings, Bulwark of the Ancient Kings) - case 44055: // Tremendous Fortitude (Battlemaster's Alacrity) - { - if(Real) - { - if(apply) - { - m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_modifier.m_amount), apply); - m_target->ModifyHealth(m_modifier.m_amount); - } - else - { - if (int32(m_target->GetHealth()) > m_modifier.m_amount) - m_target->ModifyHealth(-m_modifier.m_amount); - else - m_target->SetHealth(1); - m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_modifier.m_amount), apply); - } - } - return; - } - } - - // generic case - m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_modifier.m_amount), apply); -} - -void Aura::HandleAuraModIncreaseMaxHealth(bool apply, bool Real) -{ - uint32 oldhealth = m_target->GetHealth(); - double healthPercentage = (double)oldhealth / (double)m_target->GetMaxHealth(); - - m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_modifier.m_amount), apply); - - // refresh percentage - if(oldhealth > 0) - { - uint32 newhealth = uint32(ceil((double)m_target->GetMaxHealth() * healthPercentage)); - if(newhealth==0) - newhealth = 1; - - m_target->SetHealth(newhealth); - } -} - -void Aura::HandleAuraModIncreaseEnergy(bool apply, bool Real) -{ - Powers powerType = m_target->getPowerType(); - if(int32(powerType) != m_modifier.m_miscvalue) - return; - - m_target->HandleStatModifier(UnitMods(UNIT_MOD_POWER_START + powerType), TOTAL_VALUE, float(m_modifier.m_amount), apply); -} - -void Aura::HandleAuraModIncreaseEnergyPercent(bool apply, bool Real) -{ - Powers powerType = m_target->getPowerType(); - if(int32(powerType) != m_modifier.m_miscvalue) - return; - - m_target->HandleStatModifier(UnitMods(UNIT_MOD_POWER_START + powerType), TOTAL_PCT, float(m_modifier.m_amount), apply); -} - -void Aura::HandleAuraModIncreaseHealthPercent(bool apply, bool Real) -{ - //m_target->ApplyMaxHealthPercentMod(m_modifier.m_amount,apply); - m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_PCT, float(m_modifier.m_amount), apply); -} - -/********************************/ -/*** FIGHT ***/ -/********************************/ - -void Aura::HandleAuraModParryPercent(bool apply, bool Real) -{ - if(m_target->GetTypeId()!=TYPEID_PLAYER) - return; - - ((Player*)m_target)->UpdateParryPercentage(); -} - -void Aura::HandleAuraModDodgePercent(bool apply, bool Real) -{ - if(m_target->GetTypeId()!=TYPEID_PLAYER) - return; - - ((Player*)m_target)->UpdateDodgePercentage(); - //sLog.outError("BONUS DODGE CHANCE: + %f", float(m_modifier.m_amount)); -} - -void Aura::HandleAuraModBlockPercent(bool apply, bool Real) -{ - if(m_target->GetTypeId()!=TYPEID_PLAYER) - return; - - ((Player*)m_target)->UpdateBlockPercentage(); - //sLog.outError("BONUS BLOCK CHANCE: + %f", float(m_modifier.m_amount)); -} - -void Aura::HandleAuraModRegenInterrupt(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - if(m_target->GetTypeId()!=TYPEID_PLAYER) - return; - - ((Player*)m_target)->UpdateManaRegen(); -} - -void Aura::HandleAuraModCritPercent(bool apply, bool Real) -{ - if(m_target->GetTypeId()!=TYPEID_PLAYER) - return; - - // apply item specific bonuses for already equipped weapon - if(Real) - { - for(int i = 0; i < MAX_ATTACK; ++i) - if(Item* pItem = ((Player*)m_target)->GetWeaponForAttack(WeaponAttackType(i))) - ((Player*)m_target)->_ApplyWeaponDependentAuraCritMod(pItem,WeaponAttackType(i),this,apply); - } - - // mods must be applied base at equipped weapon class and subclass comparison - // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask - // m_modifier.m_miscvalue comparison with item generated damage types - - if (GetSpellProto()->EquippedItemClass == -1) - { - ((Player*)m_target)->HandleBaseModValue(CRIT_PERCENTAGE, FLAT_MOD, float (m_modifier.m_amount), apply); - ((Player*)m_target)->HandleBaseModValue(OFFHAND_CRIT_PERCENTAGE, FLAT_MOD, float (m_modifier.m_amount), apply); - ((Player*)m_target)->HandleBaseModValue(RANGED_CRIT_PERCENTAGE, FLAT_MOD, float (m_modifier.m_amount), apply); - } - else - { - // done in Player::_ApplyWeaponDependentAuraMods - } -} - -void Aura::HandleModHitChance(bool apply, bool Real) -{ - m_target->m_modMeleeHitChance += apply ? m_modifier.m_amount : (-m_modifier.m_amount); - m_target->m_modRangedHitChance += apply ? m_modifier.m_amount : (-m_modifier.m_amount); -} - -void Aura::HandleModSpellHitChance(bool apply, bool Real) -{ - m_target->m_modSpellHitChance += apply ? m_modifier.m_amount: (-m_modifier.m_amount); -} - -void Aura::HandleModSpellCritChance(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - if(m_target->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)m_target)->UpdateAllSpellCritChances(); - } - else - { - m_target->m_baseSpellCritChance += apply ? m_modifier.m_amount:(-m_modifier.m_amount); - } -} - -void Aura::HandleModSpellCritChanceShool(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - - for(int school = SPELL_SCHOOL_NORMAL; school < MAX_SPELL_SCHOOL; ++school) - if (m_modifier.m_miscvalue & (1<<school)) - ((Player*)m_target)->UpdateSpellCritChance(school); -} - -/********************************/ -/*** ATTACK SPEED ***/ -/********************************/ - -void Aura::HandleModCastingSpeed(bool apply, bool Real) -{ - m_target->ApplyCastTimePercentMod(m_modifier.m_amount,apply); -} - -void Aura::HandleModMeleeRangedSpeedPct(bool apply, bool Real) -{ - m_target->ApplyAttackTimePercentMod(BASE_ATTACK,m_modifier.m_amount,apply); - m_target->ApplyAttackTimePercentMod(OFF_ATTACK,m_modifier.m_amount,apply); - m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_modifier.m_amount, apply); -} - -void Aura::HandleModCombatSpeedPct(bool apply, bool Real) -{ - m_target->ApplyCastTimePercentMod(m_modifier.m_amount,apply); - m_target->ApplyAttackTimePercentMod(BASE_ATTACK,m_modifier.m_amount,apply); - m_target->ApplyAttackTimePercentMod(OFF_ATTACK,m_modifier.m_amount,apply); - m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_modifier.m_amount, apply); -} - -void Aura::HandleModAttackSpeed(bool apply, bool Real) -{ - if(!m_target->isAlive() ) - return; - - m_target->ApplyAttackTimePercentMod(BASE_ATTACK,m_modifier.m_amount,apply); -} - -void Aura::HandleHaste(bool apply, bool Real) -{ - m_target->ApplyAttackTimePercentMod(BASE_ATTACK, m_modifier.m_amount,apply); - m_target->ApplyAttackTimePercentMod(OFF_ATTACK, m_modifier.m_amount,apply); - m_target->ApplyAttackTimePercentMod(RANGED_ATTACK,m_modifier.m_amount,apply); -} - -void Aura::HandleAuraModRangedHaste(bool apply, bool Real) -{ - m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_modifier.m_amount, apply); -} - -void Aura::HandleRangedAmmoHaste(bool apply, bool Real) -{ - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - m_target->ApplyAttackTimePercentMod(RANGED_ATTACK,m_modifier.m_amount, apply); -} - -/********************************/ -/*** ATTACK POWER ***/ -/********************************/ - -void Aura::HandleAuraModAttackPower(bool apply, bool Real) -{ - m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(m_modifier.m_amount), apply); -} - -void Aura::HandleAuraModRangedAttackPower(bool apply, bool Real) -{ - if((m_target->getClassMask() & CLASSMASK_WAND_USERS)!=0) - return; - - m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(m_modifier.m_amount), apply); -} - -void Aura::HandleAuraAttackPowerAttacker(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - Unit *caster = GetCaster(); - - if (!caster) - return; - - // Hunter's Mark - if (m_spellProto->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellProto->SpellFamilyFlags & 0x0000000000000400LL) - { - // Check Improved Hunter's Mark bonus on caster - Unit::AuraList const& mOverrideClassScript = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(Unit::AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) - { - Modifier* mod = (*i)->GetModifier(); - // mproved Hunter's Mark script from 5236 to 5240 - if (mod->m_miscvalue >= 5236 && mod->m_miscvalue <= 5240) - { - // Get amount of ranged bonus for this spell.. - int32 ranged_bonus = caster->CalculateSpellDamage(m_spellProto, 1, m_spellProto->EffectBasePoints[1], m_target); - // Set melee attack power bonus % from ranged depends from Improved mask aura - m_modifier.m_amount = mod->m_amount * ranged_bonus / 100; - m_currentBasePoints = m_modifier.m_amount; - break; - } - } - return; - } -} - -void Aura::HandleAuraModAttackPowerPercent(bool apply, bool Real) -{ - //UNIT_FIELD_ATTACK_POWER_MULTIPLIER = multiplier - 1 - m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, float(m_modifier.m_amount), apply); -} - -void Aura::HandleAuraModRangedAttackPowerPercent(bool apply, bool Real) -{ - if((m_target->getClassMask() & CLASSMASK_WAND_USERS)!=0) - return; - - //UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER = multiplier - 1 - m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_PCT, float(m_modifier.m_amount), apply); -} - -void Aura::HandleAuraModRangedAttackPowerOfStatPercent(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - if(m_target->GetTypeId() == TYPEID_PLAYER && (m_target->getClassMask() & CLASSMASK_WAND_USERS)!=0) - return; - - if(m_modifier.m_miscvalue != STAT_INTELLECT) - { - // support required adding UpdateAttackPowerAndDamage calls at stat update - sLog.outError("Aura SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT (212) need support non-intelect stats!"); - return; - } - - // Recalculate bonus - ((Player*)m_target)->UpdateAttackPowerAndDamage(true); -} - -/********************************/ -/*** DAMAGE BONUS ***/ -/********************************/ -void Aura::HandleModDamageDone(bool apply, bool Real) -{ - // apply item specific bonuses for already equipped weapon - if(Real && m_target->GetTypeId()==TYPEID_PLAYER) - { - for(int i = 0; i < MAX_ATTACK; ++i) - if(Item* pItem = ((Player*)m_target)->GetWeaponForAttack(WeaponAttackType(i))) - ((Player*)m_target)->_ApplyWeaponDependentAuraDamageMod(pItem,WeaponAttackType(i),this,apply); - } - - // m_modifier.m_miscvalue is bitmask of spell schools - // 1 ( 0-bit ) - normal school damage (SPELL_SCHOOL_MASK_NORMAL) - // 126 - full bitmask all magic damages (SPELL_SCHOOL_MASK_MAGIC) including wands - // 127 - full bitmask any damages - // - // mods must be applied base at equipped weapon class and subclass comparison - // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask - // m_modifier.m_miscvalue comparison with item generated damage types - - if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL) != 0) - { - // apply generic physical damage bonuses including wand case - if (GetSpellProto()->EquippedItemClass == -1 || m_target->GetTypeId() != TYPEID_PLAYER) - { - m_target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(m_modifier.m_amount), apply); - m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(m_modifier.m_amount), apply); - m_target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(m_modifier.m_amount), apply); - } - else - { - // done in Player::_ApplyWeaponDependentAuraMods - } - - if(m_target->GetTypeId() == TYPEID_PLAYER) - { - if(m_positive) - m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS,m_modifier.m_amount,apply); - else - m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG,m_modifier.m_amount,apply); - } - } - - // Skip non magic case for speedup - if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_MAGIC) == 0) - return; - - if( GetSpellProto()->EquippedItemClass != -1 || GetSpellProto()->EquippedItemInventoryTypeMask != 0 ) - { - // wand magic case (skip generic to all item spell bonuses) - // done in Player::_ApplyWeaponDependentAuraMods - - // Skip item specific requirements for not wand magic damage - return; - } - - // Magic damage modifiers implemented in Unit::SpellDamageBonus - // This information for client side use only - if(m_target->GetTypeId() == TYPEID_PLAYER) - { - if(m_positive) - { - for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++) - { - if((m_modifier.m_miscvalue & (1<<i)) != 0) - m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i,m_modifier.m_amount,apply); - } - } - else - { - for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++) - { - if((m_modifier.m_miscvalue & (1<<i)) != 0) - m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG+i,m_modifier.m_amount,apply); - } - } - Pet* pet = m_target->GetPet(); - if(pet) - pet->UpdateAttackPowerAndDamage(); - } -} - -void Aura::HandleModDamagePercentDone(bool apply, bool Real) -{ - sLog.outDebug("AURA MOD DAMAGE type:%u negative:%u", m_modifier.m_miscvalue, m_positive ? 0 : 1); - - // apply item specific bonuses for already equipped weapon - if(Real && m_target->GetTypeId()==TYPEID_PLAYER) - { - for(int i = 0; i < MAX_ATTACK; ++i) - if(Item* pItem = ((Player*)m_target)->GetWeaponForAttack(WeaponAttackType(i))) - ((Player*)m_target)->_ApplyWeaponDependentAuraDamageMod(pItem,WeaponAttackType(i),this,apply); - } - - // m_modifier.m_miscvalue is bitmask of spell schools - // 1 ( 0-bit ) - normal school damage (SPELL_SCHOOL_MASK_NORMAL) - // 126 - full bitmask all magic damages (SPELL_SCHOOL_MASK_MAGIC) including wand - // 127 - full bitmask any damages - // - // mods must be applied base at equipped weapon class and subclass comparison - // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask - // m_modifier.m_miscvalue comparison with item generated damage types - - if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL) != 0) - { - // apply generic physical damage bonuses including wand case - if (GetSpellProto()->EquippedItemClass == -1 || m_target->GetTypeId() != TYPEID_PLAYER) - { - m_target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, float(m_modifier.m_amount), apply); - m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(m_modifier.m_amount), apply); - m_target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_PCT, float(m_modifier.m_amount), apply); - } - else - { - // done in Player::_ApplyWeaponDependentAuraMods - } - // For show in client - if(m_target->GetTypeId() == TYPEID_PLAYER) - m_target->ApplyModSignedFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT,m_modifier.m_amount/100.0f,apply); - } - - // Skip non magic case for speedup - if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_MAGIC) == 0) - return; - - if( GetSpellProto()->EquippedItemClass != -1 || GetSpellProto()->EquippedItemInventoryTypeMask != 0 ) - { - // wand magic case (skip generic to all item spell bonuses) - // done in Player::_ApplyWeaponDependentAuraMods - - // Skip item specific requirements for not wand magic damage - return; - } - - // Magic damage percent modifiers implemented in Unit::SpellDamageBonus - // Send info to client - if(m_target->GetTypeId() == TYPEID_PLAYER) - for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) - m_target->ApplyModSignedFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT+i,m_modifier.m_amount/100.0f,apply); -} - -void Aura::HandleModOffhandDamagePercent(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - sLog.outDebug("AURA MOD OFFHAND DAMAGE"); - - m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(m_modifier.m_amount), apply); -} - -/********************************/ -/*** POWER COST ***/ -/********************************/ - -void Aura::HandleModPowerCostPCT(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - float amount = m_modifier.m_amount/100.0f; - for(int i = 0; i < MAX_SPELL_SCHOOL; ++i) - if(m_modifier.m_miscvalue & (1<<i)) - m_target->ApplyModSignedFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+i,amount,apply); -} - -void Aura::HandleModPowerCost(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - for(int i = 0; i < MAX_SPELL_SCHOOL; ++i) - if(m_modifier.m_miscvalue & (1<<i)) - m_target->ApplyModInt32Value(UNIT_FIELD_POWER_COST_MODIFIER+i,m_modifier.m_amount,apply); -} - -/*********************************************************/ -/*** OTHERS ***/ -/*********************************************************/ - -void Aura::HandleShapeshiftBoosts(bool apply) -{ - uint32 spellId = 0; - uint32 spellId2 = 0; - uint32 HotWSpellId = 0; - - switch(GetModifier()->m_miscvalue) - { - case FORM_CAT: - spellId = 3025; - HotWSpellId = 24900; - break; - case FORM_TREE: - spellId = 5420; - break; - case FORM_TRAVEL: - spellId = 5419; - break; - case FORM_AQUA: - spellId = 5421; - break; - case FORM_BEAR: - spellId = 1178; - spellId2 = 21178; - HotWSpellId = 24899; - break; - case FORM_DIREBEAR: - spellId = 9635; - spellId2 = 21178; - HotWSpellId = 24899; - break; - case FORM_BATTLESTANCE: - spellId = 21156; - break; - case FORM_DEFENSIVESTANCE: - spellId = 7376; - break; - case FORM_BERSERKERSTANCE: - spellId = 7381; - break; - case FORM_MOONKIN: - spellId = 24905; - // aura from effect trigger spell - spellId2 = 24907; - break; - case FORM_FLIGHT: - spellId = 33948; - break; - case FORM_FLIGHT_EPIC: - spellId = 40122; - spellId2 = 40121; - break; - case FORM_SPIRITOFREDEMPTION: - spellId = 27792; - spellId2 = 27795; // must be second, this important at aura remove to prevent to early iterator invalidation. - break; - case FORM_GHOSTWOLF: - case FORM_AMBIENT: - case FORM_GHOUL: - case FORM_SHADOW: - case FORM_STEALTH: - case FORM_CREATURECAT: - case FORM_CREATUREBEAR: - spellId = 0; - break; - } - - uint32 form = GetModifier()->m_miscvalue-1; - - if(apply) - { - if (spellId) m_target->CastSpell(m_target, spellId, true, NULL, this ); - if (spellId2) m_target->CastSpell(m_target, spellId2, true, NULL, this); - - if(m_target->GetTypeId() == TYPEID_PLAYER) - { - const PlayerSpellMap& sp_list = ((Player *)m_target)->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) - { - if(itr->second->state == PLAYERSPELL_REMOVED) continue; - if(itr->first==spellId || itr->first==spellId2) continue; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); - if (!spellInfo || !(spellInfo->Attributes & ((1<<6) | (1<<7)))) continue; - if (spellInfo->Stances & (1<<form)) - m_target->CastSpell(m_target, itr->first, true, NULL, this); - } - //LotP - if (((Player*)m_target)->HasSpell(17007)) - { - SpellEntry const *spellInfo = sSpellStore.LookupEntry(24932); - if (spellInfo && spellInfo->Stances & (1<<form)) - m_target->CastSpell(m_target, 24932, true, NULL, this); - } - // HotW - if (HotWSpellId) - { - Unit::AuraList const& mModTotalStatPct = m_target->GetAurasByType(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE); - for(Unit::AuraList::const_iterator i = mModTotalStatPct.begin(); i != mModTotalStatPct.end(); ++i) - { - if ((*i)->GetSpellProto()->SpellIconID == 240 && (*i)->GetModifier()->m_miscvalue == 3) - { - int32 HotWMod = (*i)->GetModifier()->m_amount; - if(GetModifier()->m_miscvalue == FORM_CAT) - HotWMod /= 2; - - m_target->CastCustomSpell(m_target, HotWSpellId, &HotWMod, NULL, NULL, true, NULL, this); - break; - } - } - } - } - } - else - { - m_target->RemoveAurasDueToSpell(spellId); - m_target->RemoveAurasDueToSpell(spellId2); - - Unit::AuraMap& tAuras = m_target->GetAuras(); - for (Unit::AuraMap::iterator itr = tAuras.begin(); itr != tAuras.end();) - { - if (itr->second->IsRemovedOnShapeLost()) - { - m_target->RemoveAurasDueToSpell(itr->second->GetId()); - itr = tAuras.begin(); - } - else - { - ++itr; - } - } - } - - /*double healthPercentage = (double)m_target->GetHealth() / (double)m_target->GetMaxHealth(); - m_target->SetHealth(uint32(ceil((double)m_target->GetMaxHealth() * healthPercentage)));*/ -} - -void Aura::HandleAuraEmpathy(bool apply, bool Real) -{ - if(m_target->GetTypeId() != TYPEID_UNIT) - return; - - CreatureInfo const * ci = objmgr.GetCreatureTemplate(m_target->GetEntry()); - if(ci && ci->type == CREATURE_TYPE_BEAST) - { - m_target->ApplyModUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_SPECIALINFO, apply); - } -} - -void Aura::HandleAuraUntrackable(bool apply, bool Real) -{ - if(apply) - m_target->SetFlag(UNIT_FIELD_BYTES_1, PLAYER_STATE_FLAG_UNTRACKABLE); - else - m_target->RemoveFlag(UNIT_FIELD_BYTES_1, PLAYER_STATE_FLAG_UNTRACKABLE); -} - -void Aura::HandleAuraModPacify(bool apply, bool Real) -{ - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - - if(apply) - m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED); - else - m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED); -} - -void Aura::HandleAuraModPacifyAndSilence(bool apply, bool Real) -{ - HandleAuraModPacify(apply,Real); - HandleAuraModSilence(apply,Real); -} - -void Aura::HandleAuraGhost(bool apply, bool Real) -{ - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - - if(apply) - { - m_target->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST); - } - else - { - m_target->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST); - } -} - -void Aura::HandleAuraAllowFlight(bool apply, bool Real) -{ - // all applied/removed only at real aura add/remove - if(!Real) - return; - - // allow fly - WorldPacket data; - if(apply) - data.Initialize(SMSG_MOVE_SET_CAN_FLY, 12); - else - data.Initialize(SMSG_MOVE_UNSET_CAN_FLY, 12); - data.append(m_target->GetPackGUID()); - data << uint32(0); // unk - m_target->SendMessageToSet(&data, true); -} - -void Aura::HandleModRating(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - - for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating) - if (m_modifier.m_miscvalue & (1 << rating)) - ((Player*)m_target)->ApplyRatingMod(CombatRating(rating), m_modifier.m_amount, apply); -} - -void Aura::HandleForceMoveForward(bool apply, bool Real) -{ - if(!Real || m_target->GetTypeId() != TYPEID_PLAYER) - return; - if(apply) - m_target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FORCE_MOVE); - else - m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FORCE_MOVE); -} - -void Aura::HandleAuraModExpertise(bool apply, bool Real) -{ - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)m_target)->UpdateExpertise(BASE_ATTACK); - ((Player*)m_target)->UpdateExpertise(OFF_ATTACK); -} - -void Aura::HandleModTargetResistance(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - // applied to damage as HandleNoImmediateEffect in Unit::CalcAbsorbResist and Unit::CalcArmorReducedDamage - - // show armor penetration - if (m_target->GetTypeId() == TYPEID_PLAYER && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)) - m_target->ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE,m_modifier.m_amount, apply); - - // show as spell penetration only full spell penetration bonuses (all resistances except armor and holy - if (m_target->GetTypeId() == TYPEID_PLAYER && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_SPELL)==SPELL_SCHOOL_MASK_SPELL) - m_target->ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE,m_modifier.m_amount, apply); -} - -//HandleNoImmediateEffect auras implementation to support new stat system -void Aura::HandleAuraHealing(bool apply, bool Real) -{ - //m_target->HandleStatModifier(UNIT_MOD_HEALING, TOTAL_VALUE, float(m_modifier.m_amount), apply); -} - -void Aura::HandleAuraHealingPct(bool apply, bool Real) -{ - //m_target->HandleStatModifier(UNIT_MOD_HEALING, TOTAL_PCT, float(m_modifier.m_amount), apply); -} - -void Aura::HandleShieldBlockValue(bool apply, bool Real) -{ - BaseModType modType = FLAT_MOD; - if(m_modifier.m_auraname == SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT) - modType = PCT_MOD; - - if(m_target->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_target)->HandleBaseModValue(SHIELD_BLOCK_VALUE, modType, float(m_modifier.m_amount), apply); -} - -void Aura::HandleAuraRetainComboPoints(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - if(m_target->GetTypeId() != TYPEID_PLAYER) - return; - - Player *target = (Player*)m_target; - - // combo points was added in SPELL_EFFECT_ADD_COMBO_POINTS handler - // remove only if aura expire by time (in case combo points amount change aura removed without combo points lost) - if( !apply && m_duration==0 && target->GetComboTarget()) - if(Unit* unit = ObjectAccessor::GetUnit(*m_target,target->GetComboTarget())) - target->AddComboPoints(unit, -m_modifier.m_amount); -} - -void Aura::HandleModUnattackable( bool Apply, bool Real ) -{ - if(Real && Apply) - m_target->CombatStop(); - - m_target->ApplyModFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE,Apply); -} - -void Aura::HandleSpiritOfRedemption( bool apply, bool Real ) -{ - // spells required only Real aura add/remove - if(!Real) - return; - - // prepare spirit state - if(apply) - { - if(m_target->GetTypeId()==TYPEID_PLAYER) - { - // disable breath/etc timers - ((Player*)m_target)->StopMirrorTimers(); - - // set stand state (expected in this form) - if(!m_target->IsStandState()) - m_target->SetStandState(PLAYER_STATE_NONE); - } - - m_target->SetHealth(1); - } - // die at aura end - else - m_target->DealDamage(m_target, m_target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, GetSpellProto(), false); -} - -void Aura::CleanupTriggeredSpells() -{ - uint32 tSpellId = m_spellProto->EffectTriggerSpell[GetEffIndex()]; - if(!tSpellId) - return; - - SpellEntry const* tProto = sSpellStore.LookupEntry(tSpellId); - if(!tProto) - return; - - if(GetSpellDuration(tProto) != -1) - return; - - // needed for spell 43680, maybe others - // TODO: is there a spell flag, which can solve this in a more sophisticated way? - if(m_spellProto->EffectApplyAuraName[GetEffIndex()] == SPELL_AURA_PERIODIC_TRIGGER_SPELL && - GetSpellDuration(m_spellProto) == m_spellProto->EffectAmplitude[GetEffIndex()]) - return; - m_target->RemoveAurasDueToSpell(tSpellId); -} - -void Aura::HandleAuraPowerBurn(bool apply, bool Real) -{ - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; - - m_isPeriodic = apply; -} - -void Aura::HandleSchoolAbsorb(bool apply, bool Real) -{ - if(!Real) - return; - - // prevent double apply bonuses - if(apply && (m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading())) - { - if(Unit* caster = GetCaster()) - { - float DoneActualBenefit = 0.0f; - switch(m_spellProto->SpellFamilyName) - { - case SPELLFAMILY_PRIEST: - if(m_spellProto->SpellFamilyFlags == 0x1) //PW:S - { - //+30% from +healing bonus - DoneActualBenefit = caster->SpellBaseHealingBonus(GetSpellSchoolMask(m_spellProto)) * 0.3f; - break; - } - break; - case SPELLFAMILY_MAGE: - if(m_spellProto->SpellFamilyFlags == 0x80100 || m_spellProto->SpellFamilyFlags == 0x8 || m_spellProto->SpellFamilyFlags == 0x100000000LL) - { - //frost ward, fire ward, ice barrier - //+10% from +spd bonus - DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.1f; - break; - } - break; - case SPELLFAMILY_WARLOCK: - if(m_spellProto->SpellFamilyFlags == 0x00) - { - //shadow ward - //+10% from +spd bonus - DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.1f; - break; - } - break; - default: - break; - } - - DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellProto()); - - m_modifier.m_amount += (int32)DoneActualBenefit; - } - } -} - -void Aura::PeriodicTick() -{ - if(!m_target->isAlive()) - return; - - switch(m_modifier.m_auraname) - { - case SPELL_AURA_PERIODIC_DAMAGE: - case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: - { - Unit *pCaster = GetCaster(); - if(!pCaster) - return; - - if( GetSpellProto()->Effect[GetEffIndex()]==SPELL_EFFECT_PERSISTENT_AREA_AURA && - pCaster->SpellHitResult(m_target,GetSpellProto(),false)!=SPELL_MISS_NONE) - return; - - // Check for immune (not use charges) - if(m_target->IsImmunedToDamage(GetSpellSchoolMask(GetSpellProto()))) - return; - - // some auras remove at specific health level or more - if(m_modifier.m_auraname==SPELL_AURA_PERIODIC_DAMAGE) - { - switch(GetId()) - { - case 43093: case 31956: case 38801: - case 35321: case 38363: case 39215: - if(m_target->GetHealth() == m_target->GetMaxHealth() ) - { - m_target->RemoveAurasDueToSpell(GetId()); - return; - } - break; - case 38772: - { - uint32 percent = - GetEffIndex() < 2 && GetSpellProto()->Effect[GetEffIndex()]==SPELL_EFFECT_DUMMY ? - pCaster->CalculateSpellDamage(GetSpellProto(),GetEffIndex()+1,GetSpellProto()->EffectBasePoints[GetEffIndex()+1],m_target) : - 100; - if(m_target->GetHealth()*100 >= m_target->GetMaxHealth()*percent ) - { - m_target->RemoveAurasDueToSpell(GetId()); - return; - } - break; - } - default: - break; - } - } - - uint32 absorb=0; - uint32 resist=0; - CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL ); - - // ignore non positive values (can be result apply spellmods to aura damage - uint32 amount = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0; - - uint32 pdamage; - - if(m_modifier.m_auraname == SPELL_AURA_PERIODIC_DAMAGE) - { - pdamage = amount; - - // Calculate armor mitigation if it is a physical spell - // But not for bleed mechanic spells - if ( GetSpellSchoolMask(GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL && - GetEffectMechanic(GetSpellProto(), m_effIndex) != MECHANIC_BLEED) - { - uint32 pdamageReductedArmor = pCaster->CalcArmorReducedDamage(m_target, pdamage); - cleanDamage.damage += pdamage - pdamageReductedArmor; - pdamage = pdamageReductedArmor; - } - - pdamage = pCaster->SpellDamageBonus(m_target,GetSpellProto(),pdamage,DOT); - - // Curse of Agony damage-per-tick calculation - if (GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 0x0000000000000400LL) && GetSpellProto()->SpellIconID==544) - { - // 1..4 ticks, 1/2 from normal tick damage - if (m_duration>=((m_maxduration-m_modifier.periodictime)*2/3)) - pdamage = pdamage/2; - // 9..12 ticks, 3/2 from normal tick damage - else if(m_duration<((m_maxduration-m_modifier.periodictime)/3)) - pdamage += (pdamage+1)/2; // +1 prevent 0.5 damage possible lost at 1..4 ticks - // 5..8 ticks have normal tick damage - } - } - else - pdamage = uint32(m_target->GetMaxHealth()*amount/100); - - //As of 2.2 resilience reduces damage from DoT ticks as much as the chance to not be critically hit - // Reduce dot damage from resilience for players - if (m_target->GetTypeId()==TYPEID_PLAYER) - pdamage-=((Player*)m_target)->GetDotDamageReduction(pdamage); - - pCaster->CalcAbsorbResist(m_target, GetSpellSchoolMask(GetSpellProto()), DOT, pdamage, &absorb, &resist); - - sLog.outDetail("PeriodicTick: %u (TypeId: %u) attacked %u (TypeId: %u) for %u dmg inflicted by %u abs is %u", - GetCasterGUID(), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId(),absorb); - - WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size - data.append(m_target->GetPackGUID()); - data.appendPackGUID(GetCasterGUID()); - data << uint32(GetId()); - data << uint32(1); - data << uint32(m_modifier.m_auraname); - data << (uint32)pdamage; - data << (uint32)GetSpellSchoolMask(GetSpellProto()); // will be mask in 2.4.x - data << (uint32)absorb; - data << (uint32)resist; - m_target->SendMessageToSet(&data,true); - - Unit* target = m_target; // aura can be deleted in DealDamage - SpellEntry const* spellProto = GetSpellProto(); - - pCaster->DealDamage(m_target, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), &cleanDamage, DOT, GetSpellSchoolMask(GetSpellProto()), GetSpellProto(), true); - - // DO NOT ACCESS MEMBERS OF THE AURA FROM NOW ON (DealDamage can delete aura) - - pCaster->ProcDamageAndSpell(target, PROC_FLAG_HIT_SPELL, PROC_FLAG_TAKE_DAMAGE, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), GetSpellSchoolMask(spellProto), spellProto); - break; - } - case SPELL_AURA_PERIODIC_LEECH: - { - Unit *pCaster = GetCaster(); - if(!pCaster) - return; - - if(!pCaster->isAlive()) - return; - - if( GetSpellProto()->Effect[GetEffIndex()]==SPELL_EFFECT_PERSISTENT_AREA_AURA && - pCaster->SpellHitResult(m_target,GetSpellProto(),false)!=SPELL_MISS_NONE) - return; - - // Check for immune (not use charges) - if(m_target->IsImmunedToDamage(GetSpellSchoolMask(GetSpellProto()))) - return; - - uint32 absorb=0; - uint32 resist=0; - CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL ); - - uint32 pdamage = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0; - - //Calculate armor mitigation if it is a physical spell - if (GetSpellSchoolMask(GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL) - { - uint32 pdamageReductedArmor = pCaster->CalcArmorReducedDamage(m_target, pdamage); - cleanDamage.damage += pdamage - pdamageReductedArmor; - pdamage = pdamageReductedArmor; - } - - pdamage = pCaster->SpellDamageBonus(m_target,GetSpellProto(),pdamage,DOT); - - // talent Soul Siphon add bonus to Drain Life spells - if( GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 0x8) ) - { - // find talent max bonus percentage - Unit::AuraList const& mClassScriptAuras = pCaster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(Unit::AuraList::const_iterator i = mClassScriptAuras.begin(); i != mClassScriptAuras.end(); ++i) - { - if ((*i)->GetModifier()->m_miscvalue == 4992 || (*i)->GetModifier()->m_miscvalue == 4993) - { - if((*i)->GetEffIndex()!=1) - { - sLog.outError("Expected spell %u structure change, need code update",(*i)->GetId()); - break; - } - - // effect 1 m_amount - int32 maxPercent = (*i)->GetModifier()->m_amount; - // effect 0 m_amount - int32 stepPercent = pCaster->CalculateSpellDamage((*i)->GetSpellProto(),0,(*i)->GetSpellProto()->EffectBasePoints[0],pCaster); - - // count affliction effects and calc additional damage in percentage - int32 modPercent = 0; - Unit::AuraMap const& victimAuras = m_target->GetAuras(); - for (Unit::AuraMap::const_iterator itr = victimAuras.begin(); itr != victimAuras.end(); ++itr) - { - Aura* aura = itr->second; - if (aura->IsPositive())continue; - SpellEntry const* m_spell = aura->GetSpellProto(); - if (m_spell->SpellFamilyName != SPELLFAMILY_WARLOCK) - continue; - - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(m_spell->Id); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(m_spell->Id); - - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) - { - if(_spell_idx->second->skillId == SKILL_AFFLICTION) - { - modPercent += stepPercent; - if (modPercent >= maxPercent) - { - modPercent = maxPercent; - break; - } - } - } - } - pdamage += (pdamage*modPercent/100); - break; - } - } - } - - //As of 2.2 resilience reduces damage from DoT ticks as much as the chance to not be critically hit - // Reduce dot damage from resilience for players - if (m_target->GetTypeId()==TYPEID_PLAYER) - pdamage-=((Player*)m_target)->GetDotDamageReduction(pdamage); - - pCaster->CalcAbsorbResist(m_target, GetSpellSchoolMask(GetSpellProto()), DOT, pdamage, &absorb, &resist); - - if(m_target->GetHealth() < pdamage) - pdamage = uint32(m_target->GetHealth()); - - sLog.outDetail("PeriodicTick: %u (TypeId: %u) health leech of %u (TypeId: %u) for %u dmg inflicted by %u abs is %u", - GetCasterGUID(), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId(),absorb); - - pCaster->SendSpellNonMeleeDamageLog(m_target, GetId(), pdamage, GetSpellSchoolMask(GetSpellProto()), absorb, resist, false, 0); - - - Unit* target = m_target; // aura can be deleted in DealDamage - SpellEntry const* spellProto = GetSpellProto(); - float multiplier = spellProto->EffectMultipleValue[GetEffIndex()] > 0 ? spellProto->EffectMultipleValue[GetEffIndex()] : 1; - - uint32 new_damage = pCaster->DealDamage(m_target, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), &cleanDamage, DOT, GetSpellSchoolMask(GetSpellProto()), GetSpellProto(), false); - - // DO NOT ACCESS MEMBERS OF THE AURA FROM NOW ON (DealDamage can delete aura) - - pCaster->ProcDamageAndSpell(target, PROC_FLAG_HIT_SPELL, PROC_FLAG_TAKE_DAMAGE, new_damage, GetSpellSchoolMask(spellProto), spellProto); - if (!target->isAlive() && pCaster->IsNonMeleeSpellCasted(false)) - { - for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++) - { - if (pCaster->m_currentSpells[i] && pCaster->m_currentSpells[i]->m_spellInfo->Id == spellProto->Id) - pCaster->m_currentSpells[i]->cancel(); - } - } - - - if(Player *modOwner = pCaster->GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_MULTIPLE_VALUE, multiplier); - - uint32 heal = pCaster->SpellHealingBonus(spellProto, uint32(new_damage * multiplier), DOT, pCaster); - - int32 gain = pCaster->ModifyHealth(heal); - pCaster->getHostilRefManager().threatAssist(pCaster, gain * 0.5f, spellProto); - - pCaster->SendHealSpellLog(pCaster, spellProto->Id, heal); - break; - } - case SPELL_AURA_PERIODIC_HEAL: - case SPELL_AURA_OBS_MOD_HEALTH: - { - Unit *pCaster = GetCaster(); - if(!pCaster) - return; - - // heal for caster damage (must be alive) - if(m_target != pCaster && GetSpellProto()->SpellVisual==163 && !pCaster->isAlive()) - return; - - // ignore non positive values (can be result apply spellmods to aura damage - uint32 amount = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0; - - uint32 pdamage; - - if(m_modifier.m_auraname==SPELL_AURA_OBS_MOD_HEALTH) - pdamage = uint32(m_target->GetMaxHealth() * amount/100); - else - pdamage = amount; - - pdamage = pCaster->SpellHealingBonus(GetSpellProto(), pdamage, DOT, m_target); - - sLog.outDetail("PeriodicTick: %u (TypeId: %u) heal of %u (TypeId: %u) for %u health inflicted by %u", - GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId()); - - WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size - data.append(m_target->GetPackGUID()); - data.appendPackGUID(GetCasterGUID()); - data << uint32(GetId()); - data << uint32(1); - data << uint32(m_modifier.m_auraname); - data << (uint32)pdamage; - m_target->SendMessageToSet(&data,true); - - int32 gain = m_target->ModifyHealth(pdamage); - - // add HoTs to amount healed in bgs - if( pCaster->GetTypeId() == TYPEID_PLAYER ) - if( BattleGround *bg = ((Player*)pCaster)->GetBattleGround() ) - bg->UpdatePlayerScore(((Player*)pCaster), SCORE_HEALING_DONE, gain); - - //Do check before because m_modifier.auraName can be invalidate by DealDamage. - bool procSpell = (m_modifier.m_auraname == SPELL_AURA_PERIODIC_HEAL && m_target != pCaster); - - m_target->getHostilRefManager().threatAssist(pCaster, float(gain) * 0.5f, GetSpellProto()); - - Unit* target = m_target; // aura can be deleted in DealDamage - SpellEntry const* spellProto = GetSpellProto(); - bool haveCastItem = GetCastItemGUID()!=0; - - // heal for caster damage - if(m_target!=pCaster && spellProto->SpellVisual==163) - { - uint32 dmg = spellProto->manaPerSecond; - if(pCaster->GetHealth() <= dmg && pCaster->GetTypeId()==TYPEID_PLAYER) - { - pCaster->RemoveAurasDueToSpell(GetId()); - - // finish current generic/channeling spells, don't affect autorepeat - if(pCaster->m_currentSpells[CURRENT_GENERIC_SPELL]) - { - pCaster->m_currentSpells[CURRENT_GENERIC_SPELL]->finish(); - } - if(pCaster->m_currentSpells[CURRENT_CHANNELED_SPELL]) - { - pCaster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0); - pCaster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish(); - } - } - else - { - pCaster->SendSpellNonMeleeDamageLog(pCaster, GetId(), gain, GetSpellSchoolMask(GetSpellProto()), 0, 0, false, 0, false); - - CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL ); - pCaster->DealDamage(pCaster, gain, &cleanDamage, NODAMAGE, GetSpellSchoolMask(GetSpellProto()), GetSpellProto(), true); - } - } - - // ignore item heals - if(procSpell && !haveCastItem) - pCaster->ProcDamageAndSpell(target,PROC_FLAG_HEAL, PROC_FLAG_HEALED, pdamage, SPELL_SCHOOL_MASK_NONE, spellProto); - break; - } - case SPELL_AURA_PERIODIC_MANA_LEECH: - { - Unit *pCaster = GetCaster(); - if(!pCaster) - return; - - if(!pCaster->isAlive()) - return; - - if( GetSpellProto()->Effect[GetEffIndex()]==SPELL_EFFECT_PERSISTENT_AREA_AURA && - pCaster->SpellHitResult(m_target,GetSpellProto(),false)!=SPELL_MISS_NONE) - return; - - // Check for immune (not use charges) - if(m_target->IsImmunedToDamage(GetSpellSchoolMask(GetSpellProto()))) - return; - - // ignore non positive values (can be result apply spellmods to aura damage - uint32 pdamage = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0; - - sLog.outDetail("PeriodicTick: %u (TypeId: %u) power leech of %u (TypeId: %u) for %u dmg inflicted by %u", - GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId()); - - if(m_modifier.m_miscvalue < 0 || m_modifier.m_miscvalue > 4) - break; - - Powers power = Powers(m_modifier.m_miscvalue); - - // power type might have changed between aura applying and tick (druid's shapeshift) - if(m_target->getPowerType() != power) - break; - - int32 drain_amount = m_target->GetPower(power) > pdamage ? pdamage : m_target->GetPower(power); - - // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) - if (power == POWER_MANA && m_target->GetTypeId() == TYPEID_PLAYER) - drain_amount -= ((Player*)m_target)->GetSpellCritDamageReduction(drain_amount); - - m_target->ModifyPower(power, -drain_amount); - - float gain_multiplier = 0; - - if(pCaster->GetMaxPower(power) > 0) - { - gain_multiplier = GetSpellProto()->EffectMultipleValue[GetEffIndex()]; - - if(Player *modOwner = pCaster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), SPELLMOD_MULTIPLE_VALUE, gain_multiplier); - } - - WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size - data.append(m_target->GetPackGUID()); - data.appendPackGUID(GetCasterGUID()); - data << uint32(GetId()); - data << uint32(1); - data << uint32(m_modifier.m_auraname); - data << (uint32)power; // power type - data << (uint32)drain_amount; - data << (float)gain_multiplier; - m_target->SendMessageToSet(&data,true); - - int32 gain_amount = int32(drain_amount*gain_multiplier); - - if(gain_amount) - { - int32 gain = pCaster->ModifyPower(power,gain_amount); - m_target->AddThreat(pCaster, float(gain) * 0.5f, GetSpellSchoolMask(GetSpellProto()), GetSpellProto()); - } - break; - } - case SPELL_AURA_PERIODIC_ENERGIZE: - { - // ignore non positive values (can be result apply spellmods to aura damage - uint32 pdamage = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0; - - sLog.outDetail("PeriodicTick: %u (TypeId: %u) energize %u (TypeId: %u) for %u dmg inflicted by %u", - GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId()); - - if(m_modifier.m_miscvalue < 0 || m_modifier.m_miscvalue > 4) - break; - - Powers power = Powers(m_modifier.m_miscvalue); - - if(m_target->GetMaxPower(power) == 0) - break; - - WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size - data.append(m_target->GetPackGUID()); - data.appendPackGUID(GetCasterGUID()); - data << uint32(GetId()); - data << uint32(1); - data << uint32(m_modifier.m_auraname); - data << (uint32)power; // power type - data << (uint32)pdamage; - m_target->SendMessageToSet(&data,true); - - int32 gain = m_target->ModifyPower(power,pdamage); - - if(Unit* pCaster = GetCaster()) - m_target->getHostilRefManager().threatAssist(pCaster, float(gain) * 0.5f, GetSpellProto()); - break; - } - case SPELL_AURA_OBS_MOD_MANA: - { - // ignore non positive values (can be result apply spellmods to aura damage - uint32 amount = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0; - - uint32 pdamage = uint32(m_target->GetMaxPower(POWER_MANA) * amount/100); - - sLog.outDetail("PeriodicTick: %u (TypeId: %u) energize %u (TypeId: %u) for %u mana inflicted by %u", - GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId()); - - if(m_target->GetMaxPower(POWER_MANA) == 0) - break; - - WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size - data.append(m_target->GetPackGUID()); - data.appendPackGUID(GetCasterGUID()); - data << uint32(GetId()); - data << uint32(1); - data << uint32(m_modifier.m_auraname); - data << (uint32)0; // ? - data << (uint32)pdamage; - m_target->SendMessageToSet(&data,true); - - int32 gain = m_target->ModifyPower(POWER_MANA, pdamage); - - if(Unit* pCaster = GetCaster()) - m_target->getHostilRefManager().threatAssist(pCaster, float(gain) * 0.5f, GetSpellProto()); - break; - } - case SPELL_AURA_POWER_BURN_MANA: - { - Unit *pCaster = GetCaster(); - if(!pCaster) - return; - - // Check for immune (not use charges) - if(m_target->IsImmunedToDamage(GetSpellSchoolMask(GetSpellProto()))) - return; - - int32 pdamage = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0; - - Powers powerType = Powers(m_modifier.m_miscvalue); - - if(!m_target->isAlive() || m_target->getPowerType() != powerType) - return; - - // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) - if (powerType == POWER_MANA && m_target->GetTypeId() == TYPEID_PLAYER) - pdamage -= ((Player*)m_target)->GetSpellCritDamageReduction(pdamage); - - uint32 gain = uint32(-m_target->ModifyPower(powerType, -pdamage)); - - gain = uint32(gain * GetSpellProto()->EffectMultipleValue[GetEffIndex()]); - - //maybe has to be sent different to client, but not by SMSG_PERIODICAURALOG - pCaster->SpellNonMeleeDamageLog(m_target, GetId(), gain); - break; - } - // Here tick dummy auras - case SPELL_AURA_PERIODIC_DUMMY: - { - PeriodicDummyTick(); - break; - } - default: - break; - } -} - -void Aura::PeriodicDummyTick() -{ - SpellEntry const* spell = GetSpellProto(); - switch (spell->Id) - { - // Drink - case 430: - case 431: - case 432: - case 1133: - case 1135: - case 1137: - case 10250: - case 22734: - case 27089: - case 34291: - case 43706: - case 46755: - { - if (m_target->GetTypeId() != TYPEID_PLAYER) - return; - // Search SPELL_AURA_MOD_POWER_REGEN aura for this spell and add bonus - Unit::AuraList const& aura = m_target->GetAurasByType(SPELL_AURA_MOD_POWER_REGEN); - for(Unit::AuraList::const_iterator i = aura.begin(); i != aura.end(); ++i) - { - if ((*i)->GetId() == GetId()) - { - // Get tick number - int32 tick = (m_maxduration - m_duration) / m_modifier.periodictime; - // Default case (not on arenas) - if (tick == 0) - { - (*i)->GetModifier()->m_amount = m_modifier.m_amount; - ((Player*)m_target)->UpdateManaRegen(); - // Disable continue - m_isPeriodic = false; - } - return; - //********************************************** - // Code commended since arena patch not added - // This feature uses only in arenas - //********************************************** - // Here need increase mana regen per tick (6 second rule) - // on 0 tick - 0 (handled in 2 second) - // on 1 tick - 166% (handled in 4 second) - // on 2 tick - 133% (handled in 6 second) - // Not need update after 3 tick - /* - if (tick > 3) - return; - // Apply bonus for 0 - 3 tick - switch (tick) - { - case 0: // 0% - (*i)->GetModifier()->m_amount = m_modifier.m_amount = 0; - break; - case 1: // 166% - (*i)->GetModifier()->m_amount = m_modifier.m_amount * 5 / 3; - break; - case 2: // 133% - (*i)->GetModifier()->m_amount = m_modifier.m_amount * 4 / 3; - break; - default: // 100% - normal regen - (*i)->GetModifier()->m_amount = m_modifier.m_amount; - break; - } - ((Player*)m_target)->UpdateManaRegen(); - return;*/ - } - } - return; - } -// // Panda -// case 19230: break; -// // Master of Subtlety -// case 31666: break; -// // Gossip NPC Periodic - Talk -// case 33208: break; -// // Gossip NPC Periodic - Despawn -// case 33209: break; -// // Force of Nature -// case 33831: break; - // Aspect of the Viper - case 34074: - { - if (m_target->GetTypeId() != TYPEID_PLAYER) - return; - // Should be manauser - if (m_target->getPowerType()!=POWER_MANA) - return; - Unit *caster = GetCaster(); - if (!caster) - return; - // Regen amount is max (100% from spell) on 21% or less mana and min on 92.5% or greater mana (20% from spell) - int mana = m_target->GetPower(POWER_MANA); - int max_mana = m_target->GetMaxPower(POWER_MANA); - int32 base_regen = caster->CalculateSpellDamage(m_spellProto, m_effIndex, m_currentBasePoints, m_target); - float regen_pct = 1.20f - 1.1f * mana / max_mana; - if (regen_pct > 1.0f) regen_pct = 1.0f; - else if (regen_pct < 0.2f) regen_pct = 0.2f; - m_modifier.m_amount = int32 (base_regen * regen_pct); - ((Player*)m_target)->UpdateManaRegen(); - return; - } -// // Steal Weapon -// case 36207: break; -// // Simon Game START timer, (DND) -// case 39993: break; -// // Harpooner's Mark -// case 40084: break; -// // Knockdown Fel Cannon: break; The Aggro Burst -// case 40119: break; -// // Old Mount Spell -// case 40154: break; -// // Magnetic Pull -// case 40581: break; -// // Ethereal Ring: break; The Bolt Burst -// case 40801: break; -// // Crystal Prison -// case 40846: break; -// // Copy Weapon -// case 41054: break; -// // Ethereal Ring Visual, Lightning Aura -// case 41477: break; -// // Ethereal Ring Visual, Lightning Aura (Fork) -// case 41525: break; -// // Ethereal Ring Visual, Lightning Jumper Aura -// case 41567: break; -// // No Man's Land -// case 41955: break; -// // Headless Horseman - Fire -// case 42074: break; -// // Headless Horseman - Visual - Large Fire -// case 42075: break; -// // Headless Horseman - Start Fire, Periodic Aura -// case 42140: break; -// // Ram Speed Boost -// case 42152: break; -// // Headless Horseman - Fires Out Victory Aura -// case 42235: break; -// // Pumpkin Life Cycle -// case 42280: break; -// // Brewfest Request Chick Chuck Mug Aura -// case 42537: break; -// // Squashling -// case 42596: break; -// // Headless Horseman Climax, Head: Periodic -// case 42603: break; -// // Fire Bomb -// case 42621: break; -// // Headless Horseman - Conflagrate, Periodic Aura -// case 42637: break; -// // Headless Horseman - Create Pumpkin Treats Aura -// case 42774: break; -// // Headless Horseman Climax - Summoning Rhyme Aura -// case 42879: break; -// // Tricky Treat -// case 42919: break; -// // Giddyup! -// case 42924: break; -// // Ram - Trot -// case 42992: break; -// // Ram - Canter -// case 42993: break; -// // Ram - Gallop -// case 42994: break; -// // Ram Level - Neutral -// case 43310: break; -// // Headless Horseman - Maniacal Laugh, Maniacal, Delayed 17 -// case 43884: break; -// // Headless Horseman - Maniacal Laugh, Maniacal, other, Delayed 17 -// case 44000: break; -// // Energy Feedback -// case 44328: break; -// // Romantic Picnic -// case 45102: break; -// // Romantic Picnic -// case 45123: break; -// // Looking for Love -// case 45124: break; -// // Kite - Lightning Strike Kite Aura -// case 45197: break; -// // Rocket Chicken -// case 45202: break; -// // Copy Offhand Weapon -// case 45205: break; -// // Upper Deck - Kite - Lightning Periodic Aura -// case 45207: break; -// // Kite -Sky Lightning Strike Kite Aura -// case 45251: break; -// // Ribbon Pole Dancer Check Aura -// case 45390: break; -// // Holiday - Midsummer, Ribbon Pole Periodic Visual -// case 45406: break; -// // Parachute -// case 45472: break; -// // Alliance Flag, Extra Damage Debuff -// case 45898: break; -// // Horde Flag, Extra Damage Debuff -// case 45899: break; -// // Ahune - Summoning Rhyme Aura -// case 45926: break; -// // Ahune - Slippery Floor -// case 45945: break; -// // Ahune's Shield -// case 45954: break; -// // Nether Vapor Lightning -// case 45960: break; -// // Darkness -// case 45996: break; -// // Summon Blood Elves Periodic -// case 46041: break; -// // Transform Visual Missile Periodic -// case 46205: break; -// // Find Opening Beam End -// case 46333: break; -// // Ice Spear Control Aura -// case 46371: break; -// // Hailstone Chill -// case 46458: break; -// // Hailstone Chill, Internal -// case 46465: break; -// // Chill, Internal Shifter -// case 46549: break; -// // Summon Ice Spear Knockback Delayer -// case 46878: break; -// // Burninate Effect -// case 47214: break; -// // Fizzcrank Practice Parachute -// case 47228: break; -// // Send Mug Control Aura -// case 47369: break; -// // Direbrew's Disarm (precast) -// case 47407: break; -// // Mole Machine Port Schedule -// case 47489: break; -// // Mole Machine Portal Schedule -// case 49466: break; -// // Drink Coffee -// case 49472: break; -// // Listening to Music -// case 50493: break; -// // Love Rocket Barrage -// case 50530: break; - default: - break; - } -} - -void Aura::HandlePreventFleeing(bool apply, bool Real) -{ - if(!Real) - return; - - Unit::AuraList const& fearAuras = m_target->GetAurasByType(SPELL_AURA_MOD_FEAR); - if( !fearAuras.empty() ) - { - if (apply) - m_target->SetFeared(false, fearAuras.front()->GetCasterGUID()); - else - m_target->SetFeared(true); - } -} - -void Aura::HandleManaShield(bool apply, bool Real) -{ - if(!Real) - return; - - // prevent double apply bonuses - if(apply && (m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading())) - { - if(Unit* caster = GetCaster()) - { - float DoneActualBenefit = 0.0f; - switch(m_spellProto->SpellFamilyName) - { - case SPELLFAMILY_MAGE: - if(m_spellProto->SpellFamilyFlags & 0x8000) - { - // Mana Shield - // +50% from +spd bonus - DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.5f; - break; - } - break; - default: - break; - } - - DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellProto()); - - m_modifier.m_amount += (int32)DoneActualBenefit; - } - } -} - -void Aura::HandleArenaPreparation(bool apply, bool Real) -{ - if(!Real) - return; - - if(apply) - m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION); - else - m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION); -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "UpdateMask.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Player.h"
+#include "Unit.h"
+#include "Spell.h"
+#include "SpellAuras.h"
+#include "DynamicObject.h"
+#include "Group.h"
+#include "UpdateData.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "Policies/SingletonImp.h"
+#include "Totem.h"
+#include "Creature.h"
+#include "Formulas.h"
+#include "BattleGround.h"
+#include "CreatureAI.h"
+#include "Util.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+
+#define NULL_AURA_SLOT 0xFF
+
+pAuraHandler AuraHandler[TOTAL_AURAS]=
+{
+ &Aura::HandleNULL, // 0 SPELL_AURA_NONE
+ &Aura::HandleBindSight, // 1 SPELL_AURA_BIND_SIGHT
+ &Aura::HandleModPossess, // 2 SPELL_AURA_MOD_POSSESS
+ &Aura::HandlePeriodicDamage, // 3 SPELL_AURA_PERIODIC_DAMAGE
+ &Aura::HandleAuraDummy, // 4 SPELL_AURA_DUMMY
+ &Aura::HandleModConfuse, // 5 SPELL_AURA_MOD_CONFUSE
+ &Aura::HandleModCharm, // 6 SPELL_AURA_MOD_CHARM
+ &Aura::HandleModFear, // 7 SPELL_AURA_MOD_FEAR
+ &Aura::HandlePeriodicHeal, // 8 SPELL_AURA_PERIODIC_HEAL
+ &Aura::HandleModAttackSpeed, // 9 SPELL_AURA_MOD_ATTACKSPEED
+ &Aura::HandleModThreat, // 10 SPELL_AURA_MOD_THREAT
+ &Aura::HandleModTaunt, // 11 SPELL_AURA_MOD_TAUNT
+ &Aura::HandleAuraModStun, // 12 SPELL_AURA_MOD_STUN
+ &Aura::HandleModDamageDone, // 13 SPELL_AURA_MOD_DAMAGE_DONE
+ &Aura::HandleNoImmediateEffect, // 14 SPELL_AURA_MOD_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus
+ &Aura::HandleNoImmediateEffect, // 15 SPELL_AURA_DAMAGE_SHIELD implemented in Unit::DoAttackDamage
+ &Aura::HandleModStealth, // 16 SPELL_AURA_MOD_STEALTH
+ &Aura::HandleNoImmediateEffect, // 17 SPELL_AURA_MOD_STEALTH_DETECT
+ &Aura::HandleInvisibility, // 18 SPELL_AURA_MOD_INVISIBILITY
+ &Aura::HandleInvisibilityDetect, // 19 SPELL_AURA_MOD_INVISIBILITY_DETECTION
+ &Aura::HandleAuraModTotalHealthPercentRegen, // 20 SPELL_AURA_OBS_MOD_HEALTH
+ &Aura::HandleAuraModTotalManaPercentRegen, // 21 SPELL_AURA_OBS_MOD_MANA
+ &Aura::HandleAuraModResistance, // 22 SPELL_AURA_MOD_RESISTANCE
+ &Aura::HandlePeriodicTriggerSpell, // 23 SPELL_AURA_PERIODIC_TRIGGER_SPELL
+ &Aura::HandlePeriodicEnergize, // 24 SPELL_AURA_PERIODIC_ENERGIZE
+ &Aura::HandleAuraModPacify, // 25 SPELL_AURA_MOD_PACIFY
+ &Aura::HandleAuraModRoot, // 26 SPELL_AURA_MOD_ROOT
+ &Aura::HandleAuraModSilence, // 27 SPELL_AURA_MOD_SILENCE
+ &Aura::HandleNoImmediateEffect, // 28 SPELL_AURA_REFLECT_SPELLS implement in Unit::SpellHitResult
+ &Aura::HandleAuraModStat, // 29 SPELL_AURA_MOD_STAT
+ &Aura::HandleAuraModSkill, // 30 SPELL_AURA_MOD_SKILL
+ &Aura::HandleAuraModIncreaseSpeed, // 31 SPELL_AURA_MOD_INCREASE_SPEED
+ &Aura::HandleAuraModIncreaseMountedSpeed, // 32 SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED
+ &Aura::HandleAuraModDecreaseSpeed, // 33 SPELL_AURA_MOD_DECREASE_SPEED
+ &Aura::HandleAuraModIncreaseHealth, // 34 SPELL_AURA_MOD_INCREASE_HEALTH
+ &Aura::HandleAuraModIncreaseEnergy, // 35 SPELL_AURA_MOD_INCREASE_ENERGY
+ &Aura::HandleAuraModShapeshift, // 36 SPELL_AURA_MOD_SHAPESHIFT
+ &Aura::HandleAuraModEffectImmunity, // 37 SPELL_AURA_EFFECT_IMMUNITY
+ &Aura::HandleAuraModStateImmunity, // 38 SPELL_AURA_STATE_IMMUNITY
+ &Aura::HandleAuraModSchoolImmunity, // 39 SPELL_AURA_SCHOOL_IMMUNITY
+ &Aura::HandleAuraModDmgImmunity, // 40 SPELL_AURA_DAMAGE_IMMUNITY
+ &Aura::HandleAuraModDispelImmunity, // 41 SPELL_AURA_DISPEL_IMMUNITY
+ &Aura::HandleAuraProcTriggerSpell, // 42 SPELL_AURA_PROC_TRIGGER_SPELL implemented in Unit::ProcDamageAndSpellFor and Unit::HandleProcTriggerSpell
+ &Aura::HandleNoImmediateEffect, // 43 SPELL_AURA_PROC_TRIGGER_DAMAGE implemented in Unit::ProcDamageAndSpellFor
+ &Aura::HandleAuraTrackCreatures, // 44 SPELL_AURA_TRACK_CREATURES
+ &Aura::HandleAuraTrackResources, // 45 SPELL_AURA_TRACK_RESOURCES
+ &Aura::HandleUnused, // 46 SPELL_AURA_MOD_PARRY_SKILL obsolete?
+ &Aura::HandleAuraModParryPercent, // 47 SPELL_AURA_MOD_PARRY_PERCENT
+ &Aura::HandleUnused, // 48 SPELL_AURA_MOD_DODGE_SKILL obsolete?
+ &Aura::HandleAuraModDodgePercent, // 49 SPELL_AURA_MOD_DODGE_PERCENT
+ &Aura::HandleUnused, // 50 SPELL_AURA_MOD_BLOCK_SKILL obsolete?
+ &Aura::HandleAuraModBlockPercent, // 51 SPELL_AURA_MOD_BLOCK_PERCENT
+ &Aura::HandleAuraModCritPercent, // 52 SPELL_AURA_MOD_CRIT_PERCENT
+ &Aura::HandlePeriodicLeech, // 53 SPELL_AURA_PERIODIC_LEECH
+ &Aura::HandleModHitChance, // 54 SPELL_AURA_MOD_HIT_CHANCE
+ &Aura::HandleModSpellHitChance, // 55 SPELL_AURA_MOD_SPELL_HIT_CHANCE
+ &Aura::HandleAuraTransform, // 56 SPELL_AURA_TRANSFORM
+ &Aura::HandleModSpellCritChance, // 57 SPELL_AURA_MOD_SPELL_CRIT_CHANCE
+ &Aura::HandleAuraModIncreaseSwimSpeed, // 58 SPELL_AURA_MOD_INCREASE_SWIM_SPEED
+ &Aura::HandleNoImmediateEffect, // 59 SPELL_AURA_MOD_DAMAGE_DONE_CREATURE implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus
+ &Aura::HandleAuraModPacifyAndSilence, // 60 SPELL_AURA_MOD_PACIFY_SILENCE
+ &Aura::HandleAuraModScale, // 61 SPELL_AURA_MOD_SCALE
+ &Aura::HandleNULL, // 62 SPELL_AURA_PERIODIC_HEALTH_FUNNEL
+ &Aura::HandleUnused, // 63 SPELL_AURA_PERIODIC_MANA_FUNNEL obsolete?
+ &Aura::HandlePeriodicManaLeech, // 64 SPELL_AURA_PERIODIC_MANA_LEECH
+ &Aura::HandleModCastingSpeed, // 65 SPELL_AURA_MOD_CASTING_SPEED
+ &Aura::HandleFeignDeath, // 66 SPELL_AURA_FEIGN_DEATH
+ &Aura::HandleAuraModDisarm, // 67 SPELL_AURA_MOD_DISARM
+ &Aura::HandleAuraModStalked, // 68 SPELL_AURA_MOD_STALKED
+ &Aura::HandleSchoolAbsorb, // 69 SPELL_AURA_SCHOOL_ABSORB implemented in Unit::CalcAbsorbResist
+ &Aura::HandleUnused, // 70 SPELL_AURA_EXTRA_ATTACKS Useless, used by only one spell that has only visual effect
+ &Aura::HandleModSpellCritChanceShool, // 71 SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL
+ &Aura::HandleModPowerCostPCT, // 72 SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT
+ &Aura::HandleModPowerCost, // 73 SPELL_AURA_MOD_POWER_COST_SCHOOL
+ &Aura::HandleNoImmediateEffect, // 74 SPELL_AURA_REFLECT_SPELLS_SCHOOL implemented in Unit::SpellHitResult
+ &Aura::HandleNoImmediateEffect, // 75 SPELL_AURA_MOD_LANGUAGE
+ &Aura::HandleFarSight, // 76 SPELL_AURA_FAR_SIGHT
+ &Aura::HandleModMechanicImmunity, // 77 SPELL_AURA_MECHANIC_IMMUNITY
+ &Aura::HandleAuraMounted, // 78 SPELL_AURA_MOUNTED
+ &Aura::HandleModDamagePercentDone, // 79 SPELL_AURA_MOD_DAMAGE_PERCENT_DONE
+ &Aura::HandleModPercentStat, // 80 SPELL_AURA_MOD_PERCENT_STAT
+ &Aura::HandleNoImmediateEffect, // 81 SPELL_AURA_SPLIT_DAMAGE_PCT
+ &Aura::HandleWaterBreathing, // 82 SPELL_AURA_WATER_BREATHING
+ &Aura::HandleModBaseResistance, // 83 SPELL_AURA_MOD_BASE_RESISTANCE
+ &Aura::HandleModRegen, // 84 SPELL_AURA_MOD_REGEN
+ &Aura::HandleModPowerRegen, // 85 SPELL_AURA_MOD_POWER_REGEN
+ &Aura::HandleChannelDeathItem, // 86 SPELL_AURA_CHANNEL_DEATH_ITEM
+ &Aura::HandleNoImmediateEffect, // 87 SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus
+ &Aura::HandleNoImmediateEffect, // 88 SPELL_AURA_MOD_HEALTH_REGEN_PERCENT
+ &Aura::HandlePeriodicDamagePCT, // 89 SPELL_AURA_PERIODIC_DAMAGE_PERCENT
+ &Aura::HandleUnused, // 90 SPELL_AURA_MOD_RESIST_CHANCE Useless
+ &Aura::HandleNoImmediateEffect, // 91 SPELL_AURA_MOD_DETECT_RANGE implemented in Creature::GetAttackDistance
+ &Aura::HandlePreventFleeing, // 92 SPELL_AURA_PREVENTS_FLEEING
+ &Aura::HandleModUnattackable, // 93 SPELL_AURA_MOD_UNATTACKABLE
+ &Aura::HandleNoImmediateEffect, // 94 SPELL_AURA_INTERRUPT_REGEN implemented in Player::RegenerateAll
+ &Aura::HandleAuraGhost, // 95 SPELL_AURA_GHOST
+ &Aura::HandleNoImmediateEffect, // 96 SPELL_AURA_SPELL_MAGNET implemented in Spell::SelectMagnetTarget
+ &Aura::HandleManaShield, // 97 SPELL_AURA_MANA_SHIELD implemented in Unit::CalcAbsorbResist
+ &Aura::HandleAuraModSkill, // 98 SPELL_AURA_MOD_SKILL_TALENT
+ &Aura::HandleAuraModAttackPower, // 99 SPELL_AURA_MOD_ATTACK_POWER
+ &Aura::HandleUnused, //100 SPELL_AURA_AURAS_VISIBLE obsolete? all player can see all auras now
+ &Aura::HandleModResistancePercent, //101 SPELL_AURA_MOD_RESISTANCE_PCT
+ &Aura::HandleNoImmediateEffect, //102 SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS implemented in Unit::MeleeDamageBonus
+ &Aura::HandleAuraModTotalThreat, //103 SPELL_AURA_MOD_TOTAL_THREAT
+ &Aura::HandleAuraWaterWalk, //104 SPELL_AURA_WATER_WALK
+ &Aura::HandleAuraFeatherFall, //105 SPELL_AURA_FEATHER_FALL
+ &Aura::HandleAuraHover, //106 SPELL_AURA_HOVER
+ &Aura::HandleAddModifier, //107 SPELL_AURA_ADD_FLAT_MODIFIER
+ &Aura::HandleAddModifier, //108 SPELL_AURA_ADD_PCT_MODIFIER
+ &Aura::HandleNoImmediateEffect, //109 SPELL_AURA_ADD_TARGET_TRIGGER
+ &Aura::HandleModPowerRegenPCT, //110 SPELL_AURA_MOD_POWER_REGEN_PERCENT
+ &Aura::HandleNULL, //111 SPELL_AURA_ADD_CASTER_HIT_TRIGGER
+ &Aura::HandleNoImmediateEffect, //112 SPELL_AURA_OVERRIDE_CLASS_SCRIPTS
+ &Aura::HandleNoImmediateEffect, //113 SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus
+ &Aura::HandleNoImmediateEffect, //114 SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT implemented in Unit::MeleeDamageBonus
+ &Aura::HandleAuraHealing, //115 SPELL_AURA_MOD_HEALING
+ &Aura::HandleNoImmediateEffect, //116 SPELL_AURA_MOD_REGEN_DURING_COMBAT
+ &Aura::HandleNoImmediateEffect, //117 SPELL_AURA_MOD_MECHANIC_RESISTANCE implemented in Unit::MagicSpellHitResult
+ &Aura::HandleAuraHealingPct, //118 SPELL_AURA_MOD_HEALING_PCT
+ &Aura::HandleUnused, //119 SPELL_AURA_SHARE_PET_TRACKING useless
+ &Aura::HandleAuraUntrackable, //120 SPELL_AURA_UNTRACKABLE
+ &Aura::HandleAuraEmpathy, //121 SPELL_AURA_EMPATHY
+ &Aura::HandleModOffhandDamagePercent, //122 SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT
+ &Aura::HandleModTargetResistance, //123 SPELL_AURA_MOD_TARGET_RESISTANCE
+ &Aura::HandleAuraModRangedAttackPower, //124 SPELL_AURA_MOD_RANGED_ATTACK_POWER
+ &Aura::HandleNoImmediateEffect, //125 SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus
+ &Aura::HandleNoImmediateEffect, //126 SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT implemented in Unit::MeleeDamageBonus
+ &Aura::HandleNoImmediateEffect, //127 SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS implemented in Unit::MeleeDamageBonus
+ &Aura::HandleModPossessPet, //128 SPELL_AURA_MOD_POSSESS_PET
+ &Aura::HandleAuraModIncreaseSpeed, //129 SPELL_AURA_MOD_SPEED_ALWAYS
+ &Aura::HandleAuraModIncreaseMountedSpeed, //130 SPELL_AURA_MOD_MOUNTED_SPEED_ALWAYS
+ &Aura::HandleNoImmediateEffect, //131 SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS implemented in Unit::MeleeDamageBonus
+ &Aura::HandleAuraModIncreaseEnergyPercent, //132 SPELL_AURA_MOD_INCREASE_ENERGY_PERCENT
+ &Aura::HandleAuraModIncreaseHealthPercent, //133 SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT
+ &Aura::HandleAuraModRegenInterrupt, //134 SPELL_AURA_MOD_MANA_REGEN_INTERRUPT
+ &Aura::HandleModHealingDone, //135 SPELL_AURA_MOD_HEALING_DONE
+ &Aura::HandleAuraHealingPct, //136 SPELL_AURA_MOD_HEALING_DONE_PERCENT implemented in Unit::SpellHealingBonus
+ &Aura::HandleModTotalPercentStat, //137 SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE
+ &Aura::HandleHaste, //138 SPELL_AURA_MOD_HASTE
+ &Aura::HandleForceReaction, //139 SPELL_AURA_FORCE_REACTION
+ &Aura::HandleAuraModRangedHaste, //140 SPELL_AURA_MOD_RANGED_HASTE
+ &Aura::HandleRangedAmmoHaste, //141 SPELL_AURA_MOD_RANGED_AMMO_HASTE
+ &Aura::HandleAuraModBaseResistancePCT, //142 SPELL_AURA_MOD_BASE_RESISTANCE_PCT
+ &Aura::HandleAuraModResistanceExclusive, //143 SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE
+ &Aura::HandleNoImmediateEffect, //144 SPELL_AURA_SAFE_FALL implemented in WorldSession::HandleMovementOpcodes
+ &Aura::HandleUnused, //145 SPELL_AURA_CHARISMA obsolete?
+ &Aura::HandleUnused, //146 SPELL_AURA_PERSUADED obsolete?
+ &Aura::HandleNULL, //147 SPELL_AURA_ADD_CREATURE_IMMUNITY
+ &Aura::HandleAuraRetainComboPoints, //148 SPELL_AURA_RETAIN_COMBO_POINTS
+ &Aura::HandleNoImmediateEffect, //149 SPELL_AURA_RESIST_PUSHBACK
+ &Aura::HandleShieldBlockValue, //150 SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT
+ &Aura::HandleAuraTrackStealthed, //151 SPELL_AURA_TRACK_STEALTHED
+ &Aura::HandleNoImmediateEffect, //152 SPELL_AURA_MOD_DETECTED_RANGE implemented in Creature::GetAttackDistance
+ &Aura::HandleNoImmediateEffect, //153 SPELL_AURA_SPLIT_DAMAGE_FLAT
+ &Aura::HandleNoImmediateEffect, //154 SPELL_AURA_MOD_STEALTH_LEVEL
+ &Aura::HandleNoImmediateEffect, //155 SPELL_AURA_MOD_WATER_BREATHING
+ &Aura::HandleNoImmediateEffect, //156 SPELL_AURA_MOD_REPUTATION_GAIN
+ &Aura::HandleNULL, //157 SPELL_AURA_PET_DAMAGE_MULTI
+ &Aura::HandleShieldBlockValue, //158 SPELL_AURA_MOD_SHIELD_BLOCKVALUE
+ &Aura::HandleNoImmediateEffect, //159 SPELL_AURA_NO_PVP_CREDIT only for Honorless Target spell
+ &Aura::HandleNoImmediateEffect, //160 SPELL_AURA_MOD_AOE_AVOIDANCE implemended in Unit::MagicSpellHitResult
+ &Aura::HandleNoImmediateEffect, //161 SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT
+ &Aura::HandleAuraPowerBurn, //162 SPELL_AURA_POWER_BURN_MANA
+ &Aura::HandleNoImmediateEffect, //163 SPELL_AURA_MOD_CRIT_DAMAGE_BONUS_MELEE
+ &Aura::HandleUnused, //164 useless, only one test spell
+ &Aura::HandleAuraAttackPowerAttacker, //165 SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS implemented in Unit::MeleeDamageBonus
+ &Aura::HandleAuraModAttackPowerPercent, //166 SPELL_AURA_MOD_ATTACK_POWER_PCT
+ &Aura::HandleAuraModRangedAttackPowerPercent, //167 SPELL_AURA_MOD_RANGED_ATTACK_POWER_PCT
+ &Aura::HandleNoImmediateEffect, //168 SPELL_AURA_MOD_DAMAGE_DONE_VERSUS implemented in Unit::SpellDamageBonus, Unit::MeleeDamageBonus
+ &Aura::HandleNoImmediateEffect, //169 SPELL_AURA_MOD_CRIT_PERCENT_VERSUS implemented in Unit::DealDamageBySchool, Unit::DoAttackDamage, Unit::SpellCriticalBonus
+ &Aura::HandleNULL, //170 SPELL_AURA_DETECT_AMORE only for Detect Amore spell
+ &Aura::HandleAuraModIncreaseSpeed, //171 SPELL_AURA_MOD_SPEED_NOT_STACK
+ &Aura::HandleAuraModIncreaseMountedSpeed, //172 SPELL_AURA_MOD_MOUNTED_SPEED_NOT_STACK
+ &Aura::HandleUnused, //173 SPELL_AURA_ALLOW_CHAMPION_SPELLS only for Proclaim Champion spell
+ &Aura::HandleModSpellDamagePercentFromStat, //174 SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT implemented in Unit::SpellBaseDamageBonus (by defeult intelect, dependent from SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT)
+ &Aura::HandleModSpellHealingPercentFromStat, //175 SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT implemented in Unit::SpellBaseHealingBonus
+ &Aura::HandleSpiritOfRedemption, //176 SPELL_AURA_SPIRIT_OF_REDEMPTION only for Spirit of Redemption spell, die at aura end
+ &Aura::HandleNULL, //177 SPELL_AURA_AOE_CHARM
+ &Aura::HandleNoImmediateEffect, //178 SPELL_AURA_MOD_DEBUFF_RESISTANCE implemented in Unit::MagicSpellHitResult
+ &Aura::HandleNoImmediateEffect, //179 SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE implemented in Unit::SpellCriticalBonus
+ &Aura::HandleNoImmediateEffect, //180 SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS implemented in Unit::SpellDamageBonus
+ &Aura::HandleUnused, //181 SPELL_AURA_MOD_FLAT_SPELL_CRIT_DAMAGE_VERSUS unused
+ &Aura::HandleAuraModResistenceOfStatPercent, //182 SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT
+ &Aura::HandleNULL, //183 SPELL_AURA_MOD_CRITICAL_THREAT
+ &Aura::HandleNoImmediateEffect, //184 SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst
+ &Aura::HandleNoImmediateEffect, //185 SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst
+ &Aura::HandleNoImmediateEffect, //186 SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE implemented in Unit::MagicSpellHitResult
+ &Aura::HandleNoImmediateEffect, //187 SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE implemended in Unit::GetUnitCriticalChance
+ &Aura::HandleNoImmediateEffect, //188 SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE implemented in Unit::GetUnitCriticalChance
+ &Aura::HandleModRating, //189 SPELL_AURA_MOD_RATING
+ &Aura::HandleNULL, //190 SPELL_AURA_MOD_FACTION_REPUTATION_GAIN
+ &Aura::HandleAuraModUseNormalSpeed, //191 SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED
+ &Aura::HandleModMeleeRangedSpeedPct, //192 SPELL_AURA_HASTE_MELEE
+ &Aura::HandleModCombatSpeedPct, //193 SPELL_AURA_MELEE_SLOW (in fact combat (any type attack) speed pct)
+ &Aura::HandleUnused, //194 SPELL_AURA_MOD_DEPRICATED_1 not used now (old SPELL_AURA_MOD_SPELL_DAMAGE_OF_INTELLECT)
+ &Aura::HandleUnused, //195 SPELL_AURA_MOD_DEPRICATED_2 not used now (old SPELL_AURA_MOD_SPELL_HEALING_OF_INTELLECT)
+ &Aura::HandleNULL, //196 SPELL_AURA_MOD_COOLDOWN
+ &Aura::HandleNoImmediateEffect, //197 SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE implemented in Unit::SpellCriticalBonus Unit::GetUnitCriticalChance
+ &Aura::HandleUnused, //198 SPELL_AURA_MOD_ALL_WEAPON_SKILLS
+ &Aura::HandleNoImmediateEffect, //199 SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT implemented in Unit::MagicSpellHitResult
+ &Aura::HandleNoImmediateEffect, //200 SPELL_AURA_MOD_XP_PCT implemented in Player::GiveXP
+ &Aura::HandleAuraAllowFlight, //201 SPELL_AURA_FLY this aura enable flight mode...
+ &Aura::HandleNoImmediateEffect, //202 SPELL_AURA_CANNOT_BE_DODGED implemented in Unit::RollPhysicalOutcomeAgainst
+ &Aura::HandleNoImmediateEffect, //203 SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE implemented in Unit::DoAttackDamage
+ &Aura::HandleNoImmediateEffect, //204 SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE implemented in Unit::DoAttackDamage
+ &Aura::HandleNULL, //205 vulnerable to school dmg?
+ &Aura::HandleNULL, //206 SPELL_AURA_MOD_SPEED_MOUNTED
+ &Aura::HandleAuraModIncreaseFlightSpeed, //207 SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED
+ &Aura::HandleAuraModIncreaseFlightSpeed, //208 SPELL_AURA_MOD_SPEED_FLIGHT, used only in spell: Flight Form (Passive)
+ &Aura::HandleAuraModIncreaseFlightSpeed, //209 SPELL_AURA_MOD_FLIGHT_SPEED_ALWAYS
+ &Aura::HandleNULL, //210 Commentator's Command
+ &Aura::HandleAuraModIncreaseFlightSpeed, //211 SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK
+ &Aura::HandleAuraModRangedAttackPowerOfStatPercent, //212 SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT
+ &Aura::HandleNoImmediateEffect, //213 SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT implemented in Player::RewardRage
+ &Aura::HandleNULL, //214 Tamed Pet Passive
+ &Aura::HandleArenaPreparation, //215 SPELL_AURA_ARENA_PREPARATION
+ &Aura::HandleModCastingSpeed, //216 SPELL_AURA_HASTE_SPELLS
+ &Aura::HandleUnused, //217 unused
+ &Aura::HandleAuraModRangedHaste, //218 SPELL_AURA_HASTE_RANGED
+ &Aura::HandleModManaRegen, //219 SPELL_AURA_MOD_MANA_REGEN_FROM_STAT
+ &Aura::HandleNULL, //220 SPELL_AURA_MOD_RATING_FROM_STAT
+ &Aura::HandleNULL, //221 ignored
+ &Aura::HandleUnused, //222 unused
+ &Aura::HandleNULL, //223 Cold Stare
+ &Aura::HandleUnused, //224 unused
+ &Aura::HandleNoImmediateEffect, //225 SPELL_AURA_PRAYER_OF_MENDING
+ &Aura::HandleAuraPeriodicDummy, //226 SPELL_AURA_PERIODIC_DUMMY
+ &Aura::HandleNULL, //227 periodic trigger spell
+ &Aura::HandleNoImmediateEffect, //228 stealth detection
+ &Aura::HandleNULL, //229 SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE
+ &Aura::HandleAuraModIncreaseMaxHealth, //230 Commanding Shout
+ &Aura::HandleNULL, //231
+ &Aura::HandleNoImmediateEffect, //232 SPELL_AURA_MECHANIC_DURATION_MOD implement in Unit::CalculateSpellDuration
+ &Aura::HandleNULL, //233 set model id to the one of the creature with id m_modifier.m_miscvalue
+ &Aura::HandleNoImmediateEffect, //234 SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK implement in Unit::CalculateSpellDuration
+ &Aura::HandleAuraModDispelResist, //235 SPELL_AURA_MOD_DISPEL_RESIST implement in Unit::MagicSpellHitResult
+ &Aura::HandleUnused, //236 unused
+ &Aura::HandleModSpellDamagePercentFromAttackPower, //237 SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER implemented in Unit::SpellBaseDamageBonus
+ &Aura::HandleModSpellHealingPercentFromAttackPower, //238 SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER implemented in Unit::SpellBaseHealingBonus
+ &Aura::HandleAuraModScale, //239 SPELL_AURA_MOD_SCALE_2 only in Noggenfogger Elixir (16595) before 2.3.0 aura 61
+ &Aura::HandleAuraModExpertise, //240 SPELL_AURA_MOD_EXPERTISE
+ &Aura::HandleForceMoveForward, //241 Forces the player to move forward
+ &Aura::HandleUnused, //242 SPELL_AURA_MOD_SPELL_DAMAGE_FROM_HEALING
+ &Aura::HandleUnused, //243 used by two test spells
+ &Aura::HandleComprehendLanguage, //244 Comprehend language
+ &Aura::HandleUnused, //245 SPELL_AURA_MOD_DURATION_OF_MAGIC_EFFECTS
+ &Aura::HandleUnused, //246 unused
+ &Aura::HandleUnused, //247 unused
+ &Aura::HandleNoImmediateEffect, //248 SPELL_AURA_MOD_COMBAT_RESULT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst
+ &Aura::HandleNULL, //249
+ &Aura::HandleAuraModIncreaseHealth, //250 SPELL_AURA_MOD_INCREASE_HEALTH_2
+ &Aura::HandleNULL, //251 SPELL_AURA_MOD_ENEMY_DODGE
+ &Aura::HandleUnused, //252 unused
+ &Aura::HandleUnused, //253 unused
+ &Aura::HandleUnused, //254 unused
+ &Aura::HandleUnused, //255 unused
+ &Aura::HandleUnused, //256 unused
+ &Aura::HandleUnused, //257 unused
+ &Aura::HandleUnused, //258 unused
+ &Aura::HandleUnused, //259 unused
+ &Aura::HandleUnused, //260 unused
+ &Aura::HandleNULL //261 SPELL_AURA_261 some phased state (44856 spell)
+};
+
+Aura::Aura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster, Item* castItem) :
+m_procCharges(0), m_spellmod(NULL), m_effIndex(eff), m_caster_guid(0), m_target(target),
+m_timeCla(1000), m_castItemGuid(castItem?castItem->GetGUID():0), m_auraSlot(MAX_AURAS),
+m_positive(false), m_permanent(false), m_isPeriodic(false), m_isTrigger(false), m_isAreaAura(false),
+m_isPersistent(false), m_updated(false), m_removeMode(AURA_REMOVE_BY_DEFAULT), m_isRemovedOnShapeLost(true), m_in_use(false),
+m_periodicTimer(0), m_PeriodicEventId(0), m_AuraDRGroup(DIMINISHING_NONE)
+{
+ assert(target);
+
+ assert(spellproto && spellproto == sSpellStore.LookupEntry( spellproto->Id ) && "`info` must be pointer to sSpellStore element");
+
+ m_spellProto = spellproto;
+
+ m_currentBasePoints = currentBasePoints ? *currentBasePoints : m_spellProto->EffectBasePoints[eff];
+
+ m_isPassive = IsPassiveSpell(GetId());
+ m_positive = IsPositiveEffect(GetId(), m_effIndex);
+
+ m_applyTime = time(NULL);
+
+ int32 damage;
+ if(!caster)
+ {
+ m_caster_guid = target->GetGUID();
+ damage = m_currentBasePoints+1; // stored value-1
+ m_maxduration = target->CalculateSpellDuration(m_spellProto, m_effIndex, target);
+ }
+ else
+ {
+ m_caster_guid = caster->GetGUID();
+
+ damage = caster->CalculateSpellDamage(m_spellProto,m_effIndex,m_currentBasePoints,target);
+ m_maxduration = caster->CalculateSpellDuration(m_spellProto, m_effIndex, target);
+
+ if (!damage && castItem && castItem->GetItemSuffixFactor())
+ {
+ ItemRandomSuffixEntry const *item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(castItem->GetItemRandomPropertyId()));
+ if(item_rand_suffix)
+ {
+ for (int k=0; k<3; k++)
+ {
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(item_rand_suffix->enchant_id[k]);
+ if(pEnchant)
+ {
+ for (int t=0; t<3; t++)
+ if(pEnchant->spellid[t] == m_spellProto->Id)
+ {
+ damage = uint32((item_rand_suffix->prefix[k]*castItem->GetItemSuffixFactor()) / 10000 );
+ break;
+ }
+ }
+
+ if(damage)
+ break;
+ }
+ }
+ }
+ }
+
+ if(m_maxduration == -1 || m_isPassive && m_spellProto->DurationIndex == 0)
+ m_permanent = true;
+
+ Player* modOwner = caster ? caster->GetSpellModOwner() : NULL;
+
+ if(!m_permanent && modOwner)
+ modOwner->ApplySpellMod(GetId(), SPELLMOD_DURATION, m_maxduration);
+
+ m_duration = m_maxduration;
+
+ if(modOwner)
+ modOwner->ApplySpellMod(GetId(), SPELLMOD_ACTIVATION_TIME, m_periodicTimer);
+
+ sLog.outDebug("Aura: construct Spellid : %u, Aura : %u Duration : %d Target : %d Damage : %d", m_spellProto->Id, m_spellProto->EffectApplyAuraName[eff], m_maxduration, m_spellProto->EffectImplicitTargetA[eff],damage);
+
+ m_effIndex = eff;
+ SetModifier(AuraType(m_spellProto->EffectApplyAuraName[eff]), damage, m_spellProto->EffectAmplitude[eff], m_spellProto->EffectMiscValue[eff]);
+
+ m_isDeathPersist = IsDeathPersistentSpell(m_spellProto);
+
+ if(m_spellProto->procCharges)
+ {
+ m_procCharges = m_spellProto->procCharges;
+
+ if(modOwner)
+ modOwner->ApplySpellMod(GetId(), SPELLMOD_CHARGES, m_procCharges);
+ }
+ else
+ m_procCharges = -1;
+
+ m_isRemovedOnShapeLost = (m_caster_guid==m_target->GetGUID() && m_spellProto->Stances &&
+ !(m_spellProto->AttributesEx2 & 0x80000) && !(m_spellProto->Attributes & 0x10000));
+}
+
+Aura::~Aura()
+{
+}
+
+AreaAura::AreaAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target,
+Unit *caster, Item* castItem) : Aura(spellproto, eff, currentBasePoints, target, caster, castItem)
+{
+ m_isAreaAura = true;
+
+ // caster==NULL in constructor args if target==caster in fact
+ Unit* caster_ptr = caster ? caster : target;
+
+ m_radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(GetSpellProto()->EffectRadiusIndex[m_effIndex]));
+ if(Player* modOwner = caster_ptr->GetSpellModOwner())
+ modOwner->ApplySpellMod(GetId(), SPELLMOD_RADIUS, m_radius);
+
+ switch(spellproto->Effect[eff])
+ {
+ case SPELL_EFFECT_APPLY_AREA_AURA_PARTY:
+ m_areaAuraType = AREA_AURA_PARTY;
+ if(target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->isTotem())
+ m_modifier.m_auraname = SPELL_AURA_NONE;
+ break;
+ case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND:
+ m_areaAuraType = AREA_AURA_FRIEND;
+ break;
+ case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY:
+ m_areaAuraType = AREA_AURA_ENEMY;
+ if(target == caster_ptr)
+ m_modifier.m_auraname = SPELL_AURA_NONE; // Do not do any effect on self
+ break;
+ case SPELL_EFFECT_APPLY_AREA_AURA_PET:
+ m_areaAuraType = AREA_AURA_PET;
+ break;
+ case SPELL_EFFECT_APPLY_AREA_AURA_OWNER:
+ m_areaAuraType = AREA_AURA_OWNER;
+ if(target == caster_ptr)
+ m_modifier.m_auraname = SPELL_AURA_NONE;
+ break;
+ default:
+ sLog.outError("Wrong spell effect in AreaAura constructor");
+ ASSERT(false);
+ break;
+ }
+}
+
+AreaAura::~AreaAura()
+{
+}
+
+PersistentAreaAura::PersistentAreaAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target,
+Unit *caster, Item* castItem) : Aura(spellproto, eff, currentBasePoints, target, caster, castItem)
+{
+ m_isPersistent = true;
+}
+
+PersistentAreaAura::~PersistentAreaAura()
+{
+}
+
+SingleEnemyTargetAura::SingleEnemyTargetAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target,
+Unit *caster, Item* castItem) : Aura(spellproto, eff, currentBasePoints, target, caster, castItem)
+{
+ if (caster)
+ m_casters_target_guid = caster->GetTypeId()==TYPEID_PLAYER ? ((Player*)caster)->GetSelection() : caster->GetUInt64Value(UNIT_FIELD_TARGET);
+ else
+ m_casters_target_guid = 0;
+}
+
+SingleEnemyTargetAura::~SingleEnemyTargetAura()
+{
+}
+
+Unit* SingleEnemyTargetAura::GetTriggerTarget() const
+{
+ return ObjectAccessor::GetUnit(*m_target, m_casters_target_guid);
+}
+
+Aura* CreateAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster, Item* castItem)
+{
+ if (IsAreaAuraEffect(spellproto->Effect[eff]))
+ return new AreaAura(spellproto, eff, currentBasePoints, target, caster, castItem);
+
+ uint32 triggeredSpellId = spellproto->EffectTriggerSpell[eff];
+
+ SpellEntry const* triggredSpellInfo = sSpellStore.LookupEntry(triggeredSpellId);
+ if (triggredSpellInfo)
+ for (int i = 0; i < 3; ++i)
+ if (triggredSpellInfo->EffectImplicitTargetA[i] == TARGET_SINGLE_ENEMY)
+ return new SingleEnemyTargetAura(spellproto, eff, currentBasePoints, target, caster, castItem);
+
+ return new Aura(spellproto, eff, currentBasePoints, target, caster, castItem);
+}
+
+Unit* Aura::GetCaster() const
+{
+ if(m_caster_guid==m_target->GetGUID())
+ return m_target;
+
+ //return ObjectAccessor::GetUnit(*m_target,m_caster_guid);
+ //must return caster even if it's in another grid/map
+ Unit *unit = ObjectAccessor::GetObjectInWorld(m_caster_guid, (Unit*)NULL);
+ return unit && unit->IsInWorld() ? unit : NULL;
+}
+
+void Aura::SetModifier(AuraType t, int32 a, uint32 pt, int32 miscValue)
+{
+ m_modifier.m_auraname = t;
+ m_modifier.m_amount = a;
+ m_modifier.m_miscvalue = miscValue;
+ m_modifier.periodictime = pt;
+}
+
+void Aura::Update(uint32 diff)
+{
+ if (m_duration > 0)
+ {
+ m_duration -= diff;
+ if (m_duration < 0)
+ m_duration = 0;
+ m_timeCla -= diff;
+
+ // GetEffIndex()==0 prevent double/triple apply manaPerSecond/manaPerSecondPerLevel to same spell with many auras
+ // all spells with manaPerSecond/manaPerSecondPerLevel have aura in effect 0
+ if(GetEffIndex()==0 && m_timeCla <= 0)
+ {
+ if(Unit* caster = GetCaster())
+ {
+ Powers powertype = Powers(m_spellProto->powerType);
+ int32 manaPerSecond = m_spellProto->manaPerSecond + m_spellProto->manaPerSecondPerLevel * caster->getLevel();
+ m_timeCla = 1000;
+ if (manaPerSecond)
+ {
+ if(powertype==POWER_HEALTH)
+ caster->ModifyHealth(-manaPerSecond);
+ else
+ caster->ModifyPower(powertype,-manaPerSecond);
+ }
+ }
+ }
+ }
+
+ // Channeled aura required check distance from caster
+ if(IsChanneledSpell(m_spellProto) && m_caster_guid != m_target->GetGUID())
+ {
+ Unit* caster = GetCaster();
+ if(!caster)
+ {
+ m_target->RemoveAura(GetId(),GetEffIndex());
+ return;
+ }
+
+ // Get spell range
+ float radius;
+ SpellModOp mod;
+ if (m_spellProto->EffectRadiusIndex[GetEffIndex()])
+ {
+ radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellProto->EffectRadiusIndex[GetEffIndex()]));
+ mod = SPELLMOD_RADIUS;
+ }
+ else
+ {
+ radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellProto->rangeIndex));
+ mod = SPELLMOD_RANGE;
+ }
+
+ if(Player* modOwner = caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(GetId(), mod, radius,NULL);
+
+ if(!caster->IsWithinDistInMap(m_target,radius))
+ {
+ m_target->RemoveAura(GetId(),GetEffIndex());
+ return;
+ }
+ }
+
+ if(m_isPeriodic && (m_duration >= 0 || m_isPassive || m_permanent))
+ {
+ m_periodicTimer -= diff;
+ if(m_periodicTimer <= 0) // tick also at m_periodicTimer==0 to prevent lost last tick in case max m_duration == (max m_periodicTimer)*N
+ {
+ if( m_modifier.m_auraname == SPELL_AURA_MOD_REGEN ||
+ m_modifier.m_auraname == SPELL_AURA_MOD_POWER_REGEN ||
+ // Cannibalize, eating items and other spells
+ m_modifier.m_auraname == SPELL_AURA_OBS_MOD_HEALTH ||
+ // Eating items and other spells
+ m_modifier.m_auraname == SPELL_AURA_OBS_MOD_MANA )
+ {
+ ApplyModifier(true);
+ return;
+ }
+ // update before applying (aura can be removed in TriggerSpell or PeriodicTick calls)
+ m_periodicTimer += m_modifier.periodictime;
+
+ if(m_isTrigger)
+ TriggerSpell();
+ else
+ PeriodicTick();
+ }
+ }
+}
+
+void AreaAura::Update(uint32 diff)
+{
+ // update for the caster of the aura
+ if(m_caster_guid == m_target->GetGUID())
+ {
+ Unit* caster = m_target;
+
+ if( !caster->hasUnitState(UNIT_STAT_ISOLATED) )
+ {
+ Unit* owner = caster->GetCharmerOrOwner();
+ if (!owner)
+ owner = caster;
+ std::list<Unit *> targets;
+
+ switch(m_areaAuraType)
+ {
+ case AREA_AURA_PARTY:
+ {
+ Group *pGroup = NULL;
+
+ if (owner->GetTypeId() == TYPEID_PLAYER)
+ pGroup = ((Player*)owner)->GetGroup();
+
+ if( pGroup)
+ {
+ uint8 subgroup = ((Player*)owner)->GetSubGroup();
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* Target = itr->getSource();
+ if(Target && Target->isAlive() && Target->GetSubGroup()==subgroup && caster->IsFriendlyTo(Target))
+ {
+ if(caster->IsWithinDistInMap(Target, m_radius))
+ targets.push_back(Target);
+ Pet *pet = Target->GetPet();
+ if(pet && pet->isAlive() && caster->IsWithinDistInMap(pet, m_radius))
+ targets.push_back(pet);
+ }
+ }
+ }
+ else
+ {
+ // add owner
+ if( owner != caster && caster->IsWithinDistInMap(owner, m_radius) )
+ targets.push_back(owner);
+ // add caster's pet
+ Unit* pet = caster->GetPet();
+ if( pet && caster->IsWithinDistInMap(pet, m_radius))
+ targets.push_back(pet);
+ }
+ break;
+ }
+ case AREA_AURA_FRIEND:
+ {
+ CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::AnyFriendlyUnitInObjectRangeCheck u_check(caster, owner, m_radius);
+ MaNGOS::UnitListSearcher<MaNGOS::AnyFriendlyUnitInObjectRangeCheck> searcher(targets, u_check);
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyFriendlyUnitInObjectRangeCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyFriendlyUnitInObjectRangeCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_unit_searcher, *MapManager::Instance().GetMap(caster->GetMapId(), caster));
+ cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(caster->GetMapId(), caster));
+ break;
+ }
+ case AREA_AURA_ENEMY:
+ {
+ CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::AnyAoETargetUnitInObjectRangeCheck u_check(caster, owner, m_radius); // No GetCharmer in searcher
+ MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck> searcher(targets, u_check);
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_unit_searcher, *MapManager::Instance().GetMap(caster->GetMapId(), caster));
+ cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(caster->GetMapId(), caster));
+ break;
+ }
+ case AREA_AURA_OWNER:
+ case AREA_AURA_PET:
+ {
+ if(owner != caster)
+ targets.push_back(owner);
+ break;
+ }
+ }
+
+ for(std::list<Unit *>::iterator tIter = targets.begin(); tIter != targets.end(); tIter++)
+ {
+ if((*tIter)->HasAura(GetId(), m_effIndex))
+ continue;
+
+ if(SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(GetSpellProto(), (*tIter)->getLevel()))
+ {
+ int32 actualBasePoints = m_currentBasePoints;
+ // recalculate basepoints for lower rank (all AreaAura spell not use custom basepoints?)
+ if(actualSpellInfo != GetSpellProto())
+ actualBasePoints = actualSpellInfo->EffectBasePoints[m_effIndex];
+ AreaAura *aur = new AreaAura(actualSpellInfo, m_effIndex, &actualBasePoints, (*tIter), caster, NULL);
+ (*tIter)->AddAura(aur);
+ }
+ }
+ }
+ Aura::Update(diff);
+ }
+ else // aura at non-caster
+ {
+ Unit * tmp_target = m_target;
+ Unit* caster = GetCaster();
+ uint32 tmp_spellId = GetId(), tmp_effIndex = m_effIndex;
+
+ // WARNING: the aura may get deleted during the update
+ // DO NOT access its members after update!
+ Aura::Update(diff);
+
+ // remove aura if out-of-range from caster (after teleport for example)
+ // or caster is isolated or caster no longer has the aura
+ // or caster is (no longer) friendly
+ bool needFriendly = (m_areaAuraType == AREA_AURA_ENEMY ? false : true);
+ if( !caster || caster->hasUnitState(UNIT_STAT_ISOLATED) ||
+ !caster->IsWithinDistInMap(tmp_target, m_radius) ||
+ !caster->HasAura(tmp_spellId, tmp_effIndex) ||
+ caster->IsFriendlyTo(tmp_target) != needFriendly
+ )
+ {
+ tmp_target->RemoveAura(tmp_spellId, tmp_effIndex);
+ }
+ else if( m_areaAuraType == AREA_AURA_PARTY) // check if in same sub group
+ {
+ // not check group if target == owner or target == pet
+ if (caster->GetCharmerOrOwnerGUID() != tmp_target->GetGUID() && caster->GetGUID() != tmp_target->GetCharmerOrOwnerGUID())
+ {
+ Player* check = caster->GetCharmerOrOwnerPlayerOrPlayerItself();
+
+ Group *pGroup = check ? check->GetGroup() : NULL;
+ if( pGroup )
+ {
+ Player* checkTarget = tmp_target->GetCharmerOrOwnerPlayerOrPlayerItself();
+ if(!checkTarget || !pGroup->SameSubGroup(check, checkTarget))
+ tmp_target->RemoveAura(tmp_spellId, tmp_effIndex);
+ }
+ else
+ tmp_target->RemoveAura(tmp_spellId, tmp_effIndex);
+ }
+ }
+ else if( m_areaAuraType == AREA_AURA_PET || m_areaAuraType == AREA_AURA_OWNER )
+ {
+ if( tmp_target->GetGUID() != caster->GetCharmerOrOwnerGUID() )
+ tmp_target->RemoveAura(tmp_spellId, tmp_effIndex);
+ }
+ }
+}
+
+void PersistentAreaAura::Update(uint32 diff)
+{
+ bool remove = false;
+
+ // remove the aura if its caster or the dynamic object causing it was removed
+ // or if the target moves too far from the dynamic object
+ Unit *caster = GetCaster();
+ if (caster)
+ {
+ DynamicObject *dynObj = caster->GetDynObject(GetId(), GetEffIndex());
+ if (dynObj)
+ {
+ if (!m_target->IsWithinDistInMap(dynObj, dynObj->GetRadius()))
+ remove = true;
+ }
+ else
+ remove = true;
+ }
+ else
+ remove = true;
+
+ Unit *tmp_target = m_target;
+ uint32 tmp_id = GetId(), tmp_index = GetEffIndex();
+
+ // WARNING: the aura may get deleted during the update
+ // DO NOT access its members after update!
+ Aura::Update(diff);
+
+ if(remove)
+ tmp_target->RemoveAura(tmp_id, tmp_index);
+}
+
+void Aura::ApplyModifier(bool apply, bool Real)
+{
+ AuraType aura = m_modifier.m_auraname;
+
+ m_in_use = true;
+ if(aura<TOTAL_AURAS)
+ (*this.*AuraHandler [aura])(apply,Real);
+ m_in_use = false;
+}
+
+void Aura::UpdateAuraDuration()
+{
+ if(m_auraSlot >= MAX_AURAS || m_isPassive)
+ return;
+
+ if( m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_UPDATE_AURA_DURATION, 5);
+ data << (uint8)m_auraSlot << (uint32)m_duration;
+ ((Player*)m_target)->SendDirectMessage(&data);
+
+ data.Initialize(SMSG_SET_EXTRA_AURA_INFO, (8+1+4+4+4));
+ data.append(m_target->GetPackGUID());
+ data << uint8(m_auraSlot);
+ data << uint32(GetId());
+ data << uint32(GetAuraMaxDuration());
+ data << uint32(GetAuraDuration());
+ ((Player*)m_target)->SendDirectMessage(&data);
+ }
+
+ // not send in case player loading (will not work anyway until player not added to map), sent in visibility change code
+ if(m_target->GetTypeId() == TYPEID_PLAYER && ((Player*)m_target)->GetSession()->PlayerLoading())
+ return;
+
+ Unit* caster = GetCaster();
+
+ if(caster && caster->GetTypeId() == TYPEID_PLAYER && caster != m_target)
+ SendAuraDurationForCaster((Player*)caster);
+}
+
+void Aura::SendAuraDurationForCaster(Player* caster)
+{
+ WorldPacket data(SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE, (8+1+4+4+4));
+ data.append(m_target->GetPackGUID());
+ data << uint8(m_auraSlot);
+ data << uint32(GetId());
+ data << uint32(GetAuraMaxDuration()); // full
+ data << uint32(GetAuraDuration()); // remain
+ caster->GetSession()->SendPacket(&data);
+}
+
+void Aura::_AddAura()
+{
+ if (!GetId())
+ return;
+ if(!m_target)
+ return;
+
+ // we can found aura in NULL_AURA_SLOT and then need store state instead check slot != NULL_AURA_SLOT
+ bool samespell = false;
+ bool secondaura = false;
+ uint8 slot = NULL_AURA_SLOT;
+
+ for(uint8 i = 0; i < 3; i++)
+ {
+ Unit::spellEffectPair spair = Unit::spellEffectPair(GetId(), i);
+ for(Unit::AuraMap::const_iterator itr = m_target->GetAuras().lower_bound(spair); itr != m_target->GetAuras().upper_bound(spair); ++itr)
+ {
+ // allow use single slot only by auras from same caster
+ if(itr->second->GetCasterGUID()==GetCasterGUID())
+ {
+ samespell = true;
+ if (m_effIndex > itr->second->GetEffIndex())
+ secondaura = true;
+ slot = itr->second->GetAuraSlot();
+ break;
+ }
+ }
+
+ if(samespell)
+ break;
+ }
+
+ // not call total regen auras at adding
+ switch (m_modifier.m_auraname)
+ {
+ case SPELL_AURA_OBS_MOD_HEALTH:
+ case SPELL_AURA_OBS_MOD_MANA:
+ m_periodicTimer = m_modifier.periodictime;
+ break;
+ case SPELL_AURA_MOD_REGEN:
+ case SPELL_AURA_MOD_POWER_REGEN:
+ case SPELL_AURA_MOD_MANA_REGEN_FROM_STAT:
+ m_periodicTimer = 5000;
+ break;
+ }
+
+ // register aura
+ if (getDiminishGroup() != DIMINISHING_NONE )
+ m_target->ApplyDiminishingAura(getDiminishGroup(),true);
+
+ Unit* caster = GetCaster();
+
+ // passive auras (except totem auras) do not get placed in the slots
+ // area auras with SPELL_AURA_NONE are not shown on target
+ if((!m_isPassive || (caster && caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->isTotem())) &&
+ (m_spellProto->Effect[GetEffIndex()] != SPELL_EFFECT_APPLY_AREA_AURA_ENEMY || m_target != caster))
+ {
+ if(!samespell) // new slot need
+ {
+ if (IsPositive()) // empty positive slot
+ {
+ for (uint8 i = 0; i < MAX_POSITIVE_AURAS; i++)
+ {
+ if (m_target->GetUInt32Value((uint16)(UNIT_FIELD_AURA + i)) == 0)
+ {
+ slot = i;
+ break;
+ }
+ }
+ }
+ else // empty negative slot
+ {
+ for (uint8 i = MAX_POSITIVE_AURAS; i < MAX_AURAS; i++)
+ {
+ if (m_target->GetUInt32Value((uint16)(UNIT_FIELD_AURA + i)) == 0)
+ {
+ slot = i;
+ break;
+ }
+ }
+ }
+
+ SetAuraSlot( slot );
+
+ // Not update fields for not first spell's aura, all data already in fields
+ if(!secondaura)
+ {
+ if(slot < MAX_AURAS) // slot found
+ {
+ SetAura(slot, false);
+ SetAuraFlag(slot, true);
+ SetAuraLevel(slot,caster ? caster->getLevel() : sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL));
+ UpdateAuraCharges();
+
+ // update for out of range group members
+ m_target->UpdateAuraForGroup(slot);
+ }
+
+ UpdateAuraDuration();
+ }
+ }
+ else // use found slot
+ {
+ SetAuraSlot( slot );
+ // Not recalculate stack count for second aura of the same spell
+ if (!secondaura)
+ UpdateSlotCounterAndDuration(true);
+ }
+
+ // Update Seals information
+ if( IsSealSpell(GetSpellProto()) )
+ m_target->ModifyAuraState(AURA_STATE_JUDGEMENT, true);
+
+ // Conflagrate aura state
+ if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 4))
+ m_target->ModifyAuraState(AURA_STATE_IMMOLATE, true);
+
+ if(GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID
+ && (GetSpellProto()->SpellFamilyFlags == 0x40 || GetSpellProto()->SpellFamilyFlags == 0x10))
+ {
+ m_target->ModifyAuraState(AURA_STATE_SWIFTMEND, true);
+ }
+ }
+}
+
+void Aura::_RemoveAura()
+{
+ // Remove all triggered by aura spells vs unlimited duration
+ // except same aura replace case
+ if(m_removeMode!=AURA_REMOVE_BY_STACK)
+ CleanupTriggeredSpells();
+
+ Unit* caster = GetCaster();
+
+ if(caster && IsPersistent())
+ {
+ DynamicObject *dynObj = caster->GetDynObject(GetId(), GetEffIndex());
+ if (dynObj)
+ dynObj->RemoveAffected(m_target);
+ }
+
+ // unregister aura
+ if (getDiminishGroup() != DIMINISHING_NONE )
+ m_target->ApplyDiminishingAura(getDiminishGroup(),false);
+
+ //passive auras do not get put in slots
+ // Note: but totem can be not accessible for aura target in time remove (to far for find in grid)
+ //if(m_isPassive && !(caster && caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->isTotem()))
+ // return;
+
+ uint8 slot = GetAuraSlot();
+
+ if(slot >= MAX_AURAS) // slot not set
+ return;
+
+ if(m_target->GetUInt32Value((uint16)(UNIT_FIELD_AURA + slot)) == 0)
+ return;
+
+ bool samespell = false;
+ bool sameaura = false;
+
+ // find other aura in same slot (current already removed from list)
+ for(uint8 i = 0; i < 3; i++)
+ {
+ Unit::spellEffectPair spair = Unit::spellEffectPair(GetId(), i);
+ for(Unit::AuraMap::const_iterator itr = m_target->GetAuras().lower_bound(spair); itr != m_target->GetAuras().upper_bound(spair); ++itr)
+ {
+ if(itr->second->GetAuraSlot()==slot)
+ {
+ samespell = true;
+
+ if(GetEffIndex()==i)
+ sameaura = true;
+
+ break;
+ }
+ }
+ if(samespell)
+ break;
+ }
+
+ // only remove icon when the last aura of the spell is removed (current aura already removed from list)
+ if (!samespell)
+ {
+ SetAura(slot, true);
+ SetAuraFlag(slot, false);
+ SetAuraLevel(slot,caster ? caster->getLevel() : sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL));
+
+ SetAuraApplication(slot, 0);
+ // update for out of range group members
+ m_target->UpdateAuraForGroup(slot);
+
+ if( IsSealSpell(GetSpellProto()) )
+ m_target->ModifyAuraState(AURA_STATE_JUDGEMENT,false);
+
+ // Conflagrate aura state
+ if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 4))
+ m_target->ModifyAuraState(AURA_STATE_IMMOLATE, false);
+
+ // Swiftmend aura state
+ if(GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID
+ && (GetSpellProto()->SpellFamilyFlags == 0x40 || GetSpellProto()->SpellFamilyFlags == 0x10))
+ {
+ bool found = false;
+ Unit::AuraList const& RejorRegr = m_target->GetAurasByType(SPELL_AURA_PERIODIC_HEAL);
+ for(Unit::AuraList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i)
+ {
+ if((*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID
+ && ((*i)->GetSpellProto()->SpellFamilyFlags == 0x40 || (*i)->GetSpellProto()->SpellFamilyFlags == 0x10) )
+ {
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ m_target->ModifyAuraState(AURA_STATE_SWIFTMEND, false);
+ }
+
+ // reset cooldown state for spells
+ if(caster && caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ if ( GetSpellProto()->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE )
+ ((Player*)caster)->SendCooldownEvent(GetSpellProto());
+ }
+ }
+ else if(sameaura) // decrease count for spell, only for same aura effect, or this spell auras in remove proccess.
+ UpdateSlotCounterAndDuration(false);
+}
+
+void Aura::SetAuraFlag(uint32 slot, bool add)
+{
+ uint32 index = slot / 4;
+ uint32 byte = (slot % 4) * 8;
+ uint32 val = m_target->GetUInt32Value(UNIT_FIELD_AURAFLAGS + index);
+ val &= ~((uint32)AFLAG_MASK << byte);
+ if(add)
+ {
+ if (IsPositive())
+ val |= ((uint32)AFLAG_POSITIVE << byte);
+ else
+ val |= ((uint32)AFLAG_NEGATIVE << byte);
+ }
+ m_target->SetUInt32Value(UNIT_FIELD_AURAFLAGS + index, val);
+}
+
+void Aura::SetAuraLevel(uint32 slot,uint32 level)
+{
+ uint32 index = slot / 4;
+ uint32 byte = (slot % 4) * 8;
+ uint32 val = m_target->GetUInt32Value(UNIT_FIELD_AURALEVELS + index);
+ val &= ~(0xFF << byte);
+ val |= (level << byte);
+ m_target->SetUInt32Value(UNIT_FIELD_AURALEVELS + index, val);
+}
+
+void Aura::SetAuraApplication(uint32 slot, int8 count)
+{
+ uint32 index = slot / 4;
+ uint32 byte = (slot % 4) * 8;
+ uint32 val = m_target->GetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS + index);
+ val &= ~(0xFF << byte);
+ val |= ((uint8(count)) << byte);
+ m_target->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS + index, val);
+}
+
+void Aura::UpdateSlotCounterAndDuration(bool add)
+{
+ uint8 slot = GetAuraSlot();
+ if(slot >= MAX_AURAS)
+ return;
+
+ // calculate amount of similar auras by same effect index (similar different spells)
+ int8 count = 0;
+
+ // calculate auras and update durations in case aura adding
+ Unit::AuraList const& aura_list = m_target->GetAurasByType(GetModifier()->m_auraname);
+ for(Unit::AuraList::const_iterator i = aura_list.begin();i != aura_list.end(); ++i)
+ {
+ if( (*i)->GetId()==GetId() && (*i)->GetEffIndex()==m_effIndex &&
+ (*i)->GetCasterGUID()==GetCasterGUID() )
+ {
+ ++count;
+
+ if(add)
+ (*i)->SetAuraDuration(GetAuraDuration());
+ }
+ }
+
+ // at aura add aura not added yet, at aura remove aura already removed
+ // in field stored (count-1)
+ if(!add)
+ --count;
+
+ SetAuraApplication(slot, count);
+
+ UpdateAuraDuration();
+}
+
+/*********************************************************/
+/*** BASIC AURA FUNCTION ***/
+/*********************************************************/
+void Aura::HandleAddModifier(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER || !Real)
+ return;
+
+ SpellEntry const *spellInfo = GetSpellProto();
+ if(!spellInfo)
+ return;
+
+ if(m_modifier.m_miscvalue >= MAX_SPELLMOD)
+ return;
+
+ if (apply)
+ {
+ // Add custom charges for some mod aura
+ switch (m_spellProto->Id)
+ {
+ case 17941: // Shadow Trance
+ case 22008: // Netherwind Focus
+ case 34936: // Backlash
+ m_procCharges = 1;
+ break;
+ }
+
+ SpellModifier *mod = new SpellModifier;
+ mod->op = SpellModOp(m_modifier.m_miscvalue);
+ mod->value = m_modifier.m_amount;
+ mod->type = SpellModType(m_modifier.m_auraname); // SpellModType value == spell aura types
+ mod->spellId = GetId();
+ mod->effectId = m_effIndex;
+ mod->lastAffected = NULL;
+
+ uint64 spellAffectMask = spellmgr.GetSpellAffectMask(GetId(), m_effIndex);
+
+ if (spellAffectMask)
+ mod->mask = spellAffectMask;
+ else
+ mod->mask = spellInfo->EffectItemType[m_effIndex];
+
+ if (m_procCharges > 0)
+ mod->charges = m_procCharges;
+ else
+ mod->charges = 0;
+
+ m_spellmod = mod;
+ }
+
+ uint64 spellFamilyMask = m_spellmod->mask;
+
+ ((Player*)m_target)->AddSpellMod(m_spellmod, apply);
+
+ // reapply some passive spells after add/remove related spellmods
+ if(spellInfo->SpellFamilyName==SPELLFAMILY_WARRIOR && (spellFamilyMask & 0x0000100000000000LL))
+ {
+ m_target->RemoveAurasDueToSpell(45471);
+
+ if(apply)
+ m_target->CastSpell(m_target,45471,true);
+ }
+}
+
+void Aura::TriggerSpell()
+{
+ Unit* caster = GetCaster();
+ Unit* target = GetTriggerTarget();
+
+ if(!caster || !target)
+ return;
+
+ // generic casting code with custom spells and target/caster customs
+ uint32 trigger_spell_id = GetSpellProto()->EffectTriggerSpell[m_effIndex];
+
+ uint64 originalCasterGUID = GetCasterGUID();
+
+ SpellEntry const *triggredSpellInfo = sSpellStore.LookupEntry(trigger_spell_id);
+ SpellEntry const *auraSpellInfo = GetSpellProto();
+ uint32 auraId = auraSpellInfo->Id;
+
+ // specific code for cases with no trigger spell provided in field
+ if (triggredSpellInfo == NULL)
+ {
+ switch(auraSpellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ switch(auraId)
+ {
+ // Firestone Passive (1-5 rangs)
+ case 758:
+ case 17945:
+ case 17947:
+ case 17949:
+ case 27252:
+ {
+ if (caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+ Item* item = ((Player*)caster)->GetWeaponForAttack(BASE_ATTACK);
+ if (!item)
+ return;
+ uint32 enchant_id = 0;
+ switch (GetId())
+ {
+ case 758: enchant_id = 1803; break; // Rank 1
+ case 17945: enchant_id = 1823; break; // Rank 2
+ case 17947: enchant_id = 1824; break; // Rank 3
+ case 17949: enchant_id = 1825; break; // Rank 4
+ case 27252: enchant_id = 2645; break; // Rank 5
+ default:
+ return;
+ }
+ // remove old enchanting before applying new
+ ((Player*)caster)->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,false);
+ item->SetEnchantment(TEMP_ENCHANTMENT_SLOT, enchant_id, m_modifier.periodictime+1000, 0);
+ // add new enchanting
+ ((Player*)caster)->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,true);
+ return;
+ }
+// // Periodic Mana Burn
+// case 812: break;
+// // Polymorphic Ray
+// case 6965: break;
+// // Fire Nova (1-7 Rangs)
+// case 8350:
+// case 8508:
+// case 8509:
+// case 11312:
+// case 11313:
+// case 25540:
+// case 25544:
+// break;
+ // Thaumaturgy Channel
+ case 9712: trigger_spell_id = 21029; break;
+// // Egan's Blaster
+// case 17368: break;
+// // Haunted
+// case 18347: break;
+// // Ranshalla Waiting
+// case 18953: break;
+// // Inferno
+// case 19695: break;
+// // Frostwolf Muzzle DND
+// case 21794: break;
+// // Alterac Ram Collar DND
+// case 21866: break;
+// // Celebras Waiting
+// case 21916: break;
+ // Brood Affliction: Bronze
+ case 23170:
+ {
+ m_target->CastSpell(m_target, 23171, true, 0, this);
+ return;
+ }
+// // Mark of Frost
+// case 23184: break;
+ // Restoration
+ case 23493:
+ {
+ int32 heal = caster->GetMaxHealth() / 10;
+ caster->ModifyHealth( heal );
+ caster->SendHealSpellLog(caster, 23493, heal);
+
+ int32 mana = caster->GetMaxPower(POWER_MANA);
+ if (mana)
+ {
+ mana /= 10;
+ caster->ModifyPower( POWER_MANA, mana );
+ caster->SendEnergizeSpellLog(caster, 23493, mana, POWER_MANA);
+ }
+ break;
+ }
+// // Stoneclaw Totem Passive TEST
+// case 23792: break;
+// // Axe Flurry
+// case 24018: break;
+// // Mark of Arlokk
+// case 24210: break;
+// // Restoration
+// case 24379: break;
+// // Happy Pet
+// case 24716: break;
+// // Dream Fog
+// case 24780: break;
+// // Cannon Prep
+// case 24832: break;
+// // Shadow Bolt Whirl
+// case 24834: break;
+// // Stink Trap
+// case 24918: break;
+// // Mark of Nature
+// case 25041: break;
+// // Agro Drones
+// case 25152: break;
+// // Consume
+// case 25371: break;
+// // Pain Spike
+// case 25572: break;
+// // Rotate 360
+// case 26009: break;
+// // Rotate -360
+// case 26136: break;
+// // Consume
+// case 26196: break;
+// // Berserk
+// case 26615: break;
+// // Defile
+// case 27177: break;
+// // Teleport: IF/UC
+// case 27601: break;
+// // Five Fat Finger Exploding Heart Technique
+// case 27673: break;
+// // Nitrous Boost
+// case 27746: break;
+// // Steam Tank Passive
+// case 27747: break;
+// // Frost Blast
+// case 27808: break;
+// // Detonate Mana
+// case 27819: break;
+// // Controller Timer
+// case 28095: break;
+// // Stalagg Chain
+// case 28096: break;
+// // Stalagg Tesla Passive
+// case 28097: break;
+// // Feugen Tesla Passive
+// case 28109: break;
+// // Feugen Chain
+// case 28111: break;
+// // Mark of Didier
+// case 28114: break;
+// // Communique Timer, camp
+// case 28346: break;
+// // Icebolt
+// case 28522: break;
+// // Silithyst
+// case 29519: break;
+// // Inoculate Nestlewood Owlkin
+ case 29528: trigger_spell_id = 28713; break;
+// // Overload
+// case 29768: break;
+// // Return Fire
+// case 29788: break;
+// // Return Fire
+// case 29793: break;
+// // Return Fire
+// case 29794: break;
+// // Guardian of Icecrown Passive
+// case 29897: break;
+ // Feed Captured Animal
+ case 29917: trigger_spell_id = 29916; break;
+// // Flame Wreath
+// case 29946: break;
+// // Flame Wreath
+// case 29947: break;
+// // Mind Exhaustion Passive
+// case 30025: break;
+// // Nether Beam - Serenity
+// case 30401: break;
+ // Extract Gas
+ case 30427:
+ {
+ // move loot to player inventory and despawn target
+ if(caster->GetTypeId() ==TYPEID_PLAYER &&
+ target->GetTypeId() == TYPEID_UNIT &&
+ ((Creature*)target)->GetCreatureInfo()->type == CREATURE_TYPE_GAS_CLOUD)
+ {
+ Player* player = (Player*)caster;
+ Creature* creature = (Creature*)target;
+ // missing lootid has been reported on startup - just return
+ if (!creature->GetCreatureInfo()->SkinLootId)
+ {
+ return;
+ }
+ Loot *loot = &creature->loot;
+ loot->clear();
+ loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, NULL);
+ for(uint8 i=0;i<loot->items.size();i++)
+ {
+ LootItem *item = loot->LootItemInSlot(i,player);
+ ItemPosCountVec dest;
+ uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count );
+ if ( msg == EQUIP_ERR_OK )
+ {
+ Item * newitem = player->StoreNewItem( dest, item->itemid, true, item->randomPropertyId);
+
+ player->SendNewItem(newitem, uint32(item->count), false, false, true);
+ }
+ else
+ player->SendEquipError( msg, NULL, NULL );
+ }
+ creature->setDeathState(JUST_DIED);
+ creature->RemoveCorpse();
+ creature->SetHealth(0); // just for nice GM-mode view
+ }
+ return;
+ break;
+ }
+ // Quake
+ case 30576: trigger_spell_id = 30571; break;
+// // Burning Maul
+// case 30598: break;
+// // Regeneration
+// case 30799:
+// case 30800:
+// case 30801:
+// break;
+// // Despawn Self - Smoke cloud
+// case 31269: break;
+// // Time Rift Periodic
+// case 31320: break;
+// // Corrupt Medivh
+// case 31326: break;
+ // Doom
+ case 31347:
+ {
+ m_target->CastSpell(m_target,31350,true);
+ m_target->DealDamage(m_target, m_target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ return;
+ }
+ // Spellcloth
+ case 31373:
+ {
+ // Summon Elemental after create item
+ caster->SummonCreature(17870, 0, 0, 0, caster->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 0);
+ return;
+ }
+// // Bloodmyst Tesla
+// case 31611: break;
+// // Doomfire
+// case 31944: break;
+// // Teleport Test
+// case 32236: break;
+// // Earthquake
+// case 32686: break;
+// // Possess
+// case 33401: break;
+// // Draw Shadows
+// case 33563: break;
+// // Murmur's Touch
+// case 33711: break;
+ // Flame Quills
+ case 34229:
+ {
+ // cast 24 spells 34269-34289, 34314-34316
+ for(uint32 spell_id = 34269; spell_id != 34290; ++spell_id)
+ caster->CastSpell(m_target,spell_id,true);
+ for(uint32 spell_id = 34314; spell_id != 34317; ++spell_id)
+ caster->CastSpell(m_target,spell_id,true);
+ return;
+ }
+// // Gravity Lapse
+// case 34480: break;
+// // Tornado
+// case 34683: break;
+// // Frostbite Rotate
+// case 34748: break;
+// // Arcane Flurry
+// case 34821: break;
+// // Interrupt Shutdown
+// case 35016: break;
+// // Interrupt Shutdown
+// case 35176: break;
+// // Inferno
+// case 35268: break;
+// // Salaadin's Tesla
+// case 35515: break;
+// // Ethereal Channel (Red)
+// case 35518: break;
+// // Nether Vapor
+// case 35879: break;
+// // Dark Portal Storm
+// case 36018: break;
+// // Burning Maul
+// case 36056: break;
+// // Living Grove Defender Lifespan
+// case 36061: break;
+// // Professor Dabiri Talks
+// case 36064: break;
+// // Kael Gaining Power
+// case 36091: break;
+// // They Must Burn Bomb Aura
+// case 36344: break;
+// // They Must Burn Bomb Aura (self)
+// case 36350: break;
+// // Stolen Ravenous Ravager Egg
+// case 36401: break;
+// // Activated Cannon
+// case 36410: break;
+// // Stolen Ravenous Ravager Egg
+// case 36418: break;
+// // Enchanted Weapons
+// case 36510: break;
+// // Cursed Scarab Periodic
+// case 36556: break;
+// // Cursed Scarab Despawn Periodic
+// case 36561: break;
+// // Vision Guide
+// case 36573: break;
+// // Cannon Charging (platform)
+// case 36785: break;
+// // Cannon Charging (self)
+// case 36860: break;
+ // Remote Toy
+ case 37027: trigger_spell_id = 37029; break;
+// // Mark of Death
+// case 37125: break;
+// // Arcane Flurry
+// case 37268: break;
+// // Spout
+// case 37429: break;
+// // Spout
+// case 37430: break;
+// // Karazhan - Chess NPC AI, Snapshot timer
+// case 37440: break;
+// // Karazhan - Chess NPC AI, action timer
+// case 37504: break;
+// // Karazhan - Chess: Is Square OCCUPIED aura (DND)
+// case 39400: break;
+// // Banish
+// case 37546: break;
+// // Shriveling Gaze
+// case 37589: break;
+// // Fake Aggro Radius (2 yd)
+// case 37815: break;
+// // Corrupt Medivh
+// case 37853: break;
+ // Eye of Grillok
+ case 38495:
+ {
+ m_target->CastSpell(m_target, 38530, true);
+ return;
+ }
+ // Absorb Eye of Grillok (Zezzak's Shard)
+ case 38554:
+ {
+ if(m_target->GetTypeId() != TYPEID_UNIT)
+ return;
+
+ caster->CastSpell(caster, 38495, true);
+
+ Creature* creatureTarget = (Creature*)m_target;
+
+ creatureTarget->setDeathState(JUST_DIED);
+ creatureTarget->RemoveCorpse();
+ creatureTarget->SetHealth(0); // just for nice GM-mode view
+ return;
+ }
+// // Magic Sucker Device timer
+// case 38672: break;
+// // Tomb Guarding Charging
+// case 38751: break;
+// // Murmur's Touch
+// case 38794: break;
+// // Activate Nether-wraith Beacon (31742 Nether-wraith Beacon item)
+// case 39105: break;
+// // Drain World Tree Visual
+// case 39140: break;
+// // Quest - Dustin's Undead Dragon Visual aura
+// case 39259: break;
+// // Hellfire - The Exorcism, Jules releases darkness, aura
+// case 39306: break;
+// // Inferno
+// case 39346: break;
+// // Enchanted Weapons
+// case 39489: break;
+// // Shadow Bolt Whirl
+// case 39630: break;
+// // Shadow Bolt Whirl
+// case 39634: break;
+// // Shadow Inferno
+// case 39645: break;
+ // Tear of Azzinoth Summon Channel - it's not really supposed to do anything,and this only prevents the console spam
+ case 39857: trigger_spell_id = 39856; break;
+// // Soulgrinder Ritual Visual (Smashed)
+// case 39974: break;
+// // Simon Game Pre-game timer
+// case 40041: break;
+// // Knockdown Fel Cannon: The Aggro Check Aura
+// case 40113: break;
+// // Spirit Lance
+// case 40157: break;
+// // Demon Transform 2
+// case 40398: break;
+// // Demon Transform 1
+// case 40511: break;
+// // Ancient Flames
+// case 40657: break;
+// // Ethereal Ring Cannon: Cannon Aura
+// case 40734: break;
+// // Cage Trap
+// case 40760: break;
+// // Random Periodic
+// case 40867: break;
+// // Prismatic Shield
+// case 40879: break;
+// // Aura of Desire
+// case 41350: break;
+// // Dementia
+// case 41404: break;
+// // Chaos Form
+// case 41629: break;
+// // Alert Drums
+// case 42177: break;
+// // Spout
+// case 42581: break;
+// // Spout
+// case 42582: break;
+// // Return to the Spirit Realm
+// case 44035: break;
+// // Curse of Boundless Agony
+// case 45050: break;
+// // Earthquake
+// case 46240: break;
+ // Personalized Weather
+ case 46736: trigger_spell_id = 46737; break;
+// // Stay Submerged
+// case 46981: break;
+// // Dragonblight Ram
+// case 47015: break;
+// // Party G.R.E.N.A.D.E.
+// case 51510: break;
+ default:
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_MAGE:
+ {
+ switch(auraId)
+ {
+ // Invisibility
+ case 66:
+ {
+ if(!m_duration)
+ m_target->CastSpell(m_target, 32612, true, NULL, this);
+ return;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+// case SPELLFAMILY_WARRIOR:
+// {
+// switch(auraId)
+// {
+// // Wild Magic
+// case 23410: break;
+// // Corrupted Totems
+// case 23425: break;
+// default:
+// break;
+// }
+// break;
+// }
+// case SPELLFAMILY_PRIEST:
+// {
+// switch(auraId)
+// {
+// // Blue Beam
+// case 32930: break;
+// // Fury of the Dreghood Elders
+// case 35460: break;
+// default:
+// break;
+// }
+ // break;
+ // }
+ case SPELLFAMILY_DRUID:
+ {
+ switch(auraId)
+ {
+ // Cat Form
+ // trigger_spell_id not set and unknown effect triggered in this case, ignoring for while
+ case 768:
+ return;
+ // Frenzied Regeneration
+ case 22842:
+ case 22895:
+ case 22896:
+ case 26999:
+ {
+ int32 LifePerRage = GetModifier()->m_amount;
+
+ int32 lRage = m_target->GetPower(POWER_RAGE);
+ if(lRage > 100) // rage stored as rage*10
+ lRage = 100;
+ m_target->ModifyPower(POWER_RAGE, -lRage);
+ int32 FRTriggerBasePoints = int32(lRage*LifePerRage/10);
+ m_target->CastCustomSpell(m_target,22845,&FRTriggerBasePoints,NULL,NULL,true,NULL,this);
+ return;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+
+// case SPELLFAMILY_HUNTER:
+// {
+// switch(auraId)
+// {
+// //Frost Trap Aura
+// case 13810:
+// return;
+// //Rizzle's Frost Trap
+// case 39900:
+// return;
+// // Tame spells
+// case 19597: // Tame Ice Claw Bear
+// case 19676: // Tame Snow Leopard
+// case 19677: // Tame Large Crag Boar
+// case 19678: // Tame Adult Plainstrider
+// case 19679: // Tame Prairie Stalker
+// case 19680: // Tame Swoop
+// case 19681: // Tame Dire Mottled Boar
+// case 19682: // Tame Surf Crawler
+// case 19683: // Tame Armored Scorpid
+// case 19684: // Tame Webwood Lurker
+// case 19685: // Tame Nightsaber Stalker
+// case 19686: // Tame Strigid Screecher
+// case 30100: // Tame Crazed Dragonhawk
+// case 30103: // Tame Elder Springpaw
+// case 30104: // Tame Mistbat
+// case 30647: // Tame Barbed Crawler
+// case 30648: // Tame Greater Timberstrider
+// case 30652: // Tame Nightstalker
+// return;
+// default:
+// break;
+// }
+// break;
+// }
+ case SPELLFAMILY_SHAMAN:
+ {
+ switch(auraId)
+ {
+ // Lightning Shield (The Earthshatterer set trigger after cast Lighting Shield)
+ case 28820:
+ {
+ // Need remove self if Lightning Shield not active
+ Unit::AuraMap const& auras = target->GetAuras();
+ for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ {
+ SpellEntry const* spell = itr->second->GetSpellProto();
+ if( spell->SpellFamilyName == SPELLFAMILY_SHAMAN &&
+ spell->SpellFamilyFlags & 0x0000000000000400L)
+ return;
+ }
+ target->RemoveAurasDueToSpell(28820);
+ return;
+ }
+ // Totemic Mastery (Skyshatter Regalia (Shaman Tier 6) - bonus)
+ case 38443:
+ {
+ bool all = true;
+ for(int i = 0; i < MAX_TOTEM; ++i)
+ {
+ if(!caster->m_TotemSlot[i])
+ {
+ all = false;
+ break;
+ }
+ }
+
+ if(all)
+ caster->CastSpell(caster,38437,true);
+ else
+ caster->RemoveAurasDueToSpell(38437);
+ return;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ // Reget trigger spell proto
+ triggredSpellInfo = sSpellStore.LookupEntry(trigger_spell_id);
+ if(triggredSpellInfo == NULL)
+ {
+ sLog.outError("Aura::TriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",GetId(),GetEffIndex());
+ return;
+ }
+ }
+ else
+ {
+ // Spell exist but require costum code
+ switch(auraId)
+ {
+ // Curse of Idiocy
+ case 1010:
+ {
+ // TODO: spell casted by result in correct way mostly
+ // BUT:
+ // 1) target show casting at each triggered cast: target don't must show casting animation for any triggered spell
+ // but must show affect apply like item casting
+ // 2) maybe aura must be replace by new with accumulative stat mods insteed stacking
+
+ // prevent cast by triggered auras
+ if(m_caster_guid == m_target->GetGUID())
+ return;
+
+ // stop triggering after each affected stats lost > 90
+ int32 intelectLoss = 0;
+ int32 spiritLoss = 0;
+
+ Unit::AuraList const& mModStat = m_target->GetAurasByType(SPELL_AURA_MOD_STAT);
+ for(Unit::AuraList::const_iterator i = mModStat.begin(); i != mModStat.end(); ++i)
+ {
+ if ((*i)->GetId() == 1010)
+ {
+ switch((*i)->GetModifier()->m_miscvalue)
+ {
+ case STAT_INTELLECT: intelectLoss += (*i)->GetModifier()->m_amount; break;
+ case STAT_SPIRIT: spiritLoss += (*i)->GetModifier()->m_amount; break;
+ default: break;
+ }
+ }
+ }
+
+ if(intelectLoss <= -90 && spiritLoss <= -90)
+ return;
+
+ caster = target;
+ originalCasterGUID = 0;
+ break;
+ }
+ // Mana Tide
+ case 16191:
+ {
+ caster->CastCustomSpell(target, trigger_spell_id, &m_modifier.m_amount, NULL, NULL, true, NULL, this, originalCasterGUID);
+ return;
+ }
+ }
+ }
+ // All ok cast by default case
+ Spell *spell = new Spell(caster, triggredSpellInfo, true, originalCasterGUID );
+
+ SpellCastTargets targets;
+ targets.setUnitTarget( target );
+
+ // if spell create dynamic object extract area from it
+ if(DynamicObject* dynObj = caster->GetDynObject(GetId()))
+ targets.setDestination(dynObj->GetPositionX(),dynObj->GetPositionY(),dynObj->GetPositionZ());
+
+ spell->prepare(&targets, this);
+}
+
+/*********************************************************/
+/*** AURA EFFECTS ***/
+/*********************************************************/
+
+void Aura::HandleAuraDummy(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ Unit* caster = GetCaster();
+
+ // AT APPLY
+ if(apply)
+ {
+ switch(GetId())
+ {
+ case 1515: // Tame beast
+ // FIX_ME: this is 2.0.12 threat effect replaced in 2.1.x by dummy aura, must be checked for correctness
+ if( caster && m_target->CanHaveThreatList())
+ m_target->AddThreat(caster, 10.0f);
+ return;
+ case 13139: // net-o-matic
+ // root to self part of (root_target->charge->root_self sequence
+ if(caster)
+ caster->CastSpell(caster,13138,true,NULL,this);
+ return;
+ case 39850: // Rocket Blast
+ if(roll_chance_i(20)) // backfire stun
+ m_target->CastSpell(m_target, 51581, true, NULL, this);
+ return;
+ case 46354: // Blood Elf Illusion
+ if(caster)
+ {
+ switch(caster->getGender())
+ {
+ case GENDER_FEMALE:
+ caster->CastSpell(m_target,46356,true,NULL,this);
+ break;
+ case GENDER_MALE:
+ caster->CastSpell(m_target,46355,true,NULL,this);
+ break;
+ default:
+ break;
+ }
+ }
+ return;
+ case 46699: // Requires No Ammo
+ if(m_target->GetTypeId()==TYPEID_PLAYER)
+ ((Player*)m_target)->RemoveAmmo(); // not use ammo and not allow use
+ return;
+ }
+
+ // Earth Shield
+ if ( caster && GetSpellProto()->SpellFamilyName == SPELLFAMILY_SHAMAN && (GetSpellProto()->SpellFamilyFlags & 0x40000000000LL))
+ {
+ // prevent double apply bonuses
+ if(m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading())
+ m_modifier.m_amount = caster->SpellHealingBonus(GetSpellProto(), m_modifier.m_amount, SPELL_DIRECT_DAMAGE, m_target);
+ return;
+ }
+ }
+ // AT REMOVE
+ else
+ {
+ if( m_target->GetTypeId() == TYPEID_PLAYER &&
+ ( GetSpellProto()->Effect[0]==72 || GetSpellProto()->Effect[0]==6 &&
+ ( GetSpellProto()->EffectApplyAuraName[0]==1 || GetSpellProto()->EffectApplyAuraName[0]==128 ) ) )
+ {
+ // spells with SpellEffect=72 and aura=4: 6196, 6197, 21171, 21425
+ m_target->SetUInt64Value(PLAYER_FARSIGHT, 0);
+ WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0);
+ ((Player*)m_target)->GetSession()->SendPacket(&data);
+ return;
+ }
+
+ if( (IsQuestTameSpell(GetId())) && caster && caster->isAlive() && m_target->isAlive())
+ {
+ uint32 finalSpelId = 0;
+ switch(GetId())
+ {
+ case 19548: finalSpelId = 19597; break;
+ case 19674: finalSpelId = 19677; break;
+ case 19687: finalSpelId = 19676; break;
+ case 19688: finalSpelId = 19678; break;
+ case 19689: finalSpelId = 19679; break;
+ case 19692: finalSpelId = 19680; break;
+ case 19693: finalSpelId = 19684; break;
+ case 19694: finalSpelId = 19681; break;
+ case 19696: finalSpelId = 19682; break;
+ case 19697: finalSpelId = 19683; break;
+ case 19699: finalSpelId = 19685; break;
+ case 19700: finalSpelId = 19686; break;
+ case 30646: finalSpelId = 30647; break;
+ case 30653: finalSpelId = 30648; break;
+ case 30654: finalSpelId = 30652; break;
+ case 30099: finalSpelId = 30100; break;
+ case 30102: finalSpelId = 30103; break;
+ case 30105: finalSpelId = 30104; break;
+ }
+
+ if(finalSpelId)
+ caster->CastSpell(m_target,finalSpelId,true,NULL,this);
+ return;
+ }
+ // Dark Fiend
+ if(GetId()==45934)
+ {
+ // Kill target if dispeled
+ if (m_removeMode==AURA_REMOVE_BY_DISPEL)
+ m_target->DealDamage(m_target, m_target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ return;
+ }
+
+ // Burning Winds
+ if(GetId()==46308) // casted only at creatures at spawn
+ {
+ m_target->CastSpell(m_target,47287,true,NULL,this);
+ return;
+ }
+ }
+
+ // AT APPLY & REMOVE
+
+ switch(m_spellProto->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ // Unstable Power
+ if( GetId()==24658 )
+ {
+ uint32 spellId = 24659;
+ if (apply)
+ {
+ const SpellEntry *spell = sSpellStore.LookupEntry(spellId);
+ if (!spell)
+ return;
+ for (int i=0; i < spell->StackAmount; ++i)
+ caster->CastSpell(m_target, spell->Id, true, NULL, NULL, GetCasterGUID());
+ return;
+ }
+ m_target->RemoveAurasDueToSpell(spellId);
+ return;
+ }
+ // Restless Strength
+ if( GetId()==24661 )
+ {
+ uint32 spellId = 24662;
+ if (apply)
+ {
+ const SpellEntry *spell = sSpellStore.LookupEntry(spellId);
+ if (!spell)
+ return;
+ for (int i=0; i < spell->StackAmount; ++i)
+ caster->CastSpell(m_target, spell->Id, true, NULL, NULL, GetCasterGUID());
+ return;
+ }
+ m_target->RemoveAurasDueToSpell(spellId);
+ return;
+ }
+ // Victorious
+ if(GetId()==32216 && m_target->getClass()==CLASS_WARRIOR)
+ {
+ m_target->ModifyAuraState(AURA_STATE_WARRIOR_VICTORY_RUSH, apply);
+ return;
+ }
+ //Summon Fire Elemental
+ if (GetId() == 40133 && caster)
+ {
+ Unit *owner = caster->GetOwner();
+ if (owner && owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ if(apply)
+ owner->CastSpell(owner,8985,true);
+ else
+ ((Player*)owner)->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
+ }
+ return;
+ }
+
+ //Summon Earth Elemental
+ if (GetId() == 40132 && caster)
+ {
+ Unit *owner = caster->GetOwner();
+ if (owner && owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ if(apply)
+ owner->CastSpell(owner,19704,true);
+ else
+ ((Player*)owner)->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
+ }
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_MAGE:
+ {
+ // Hypothermia
+ if( GetId()==41425 )
+ {
+ m_target->ModifyAuraState(AURA_STATE_HYPOTHERMIA,apply);
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_DRUID:
+ {
+ // Lifebloom
+ if ( GetSpellProto()->SpellFamilyFlags & 0x1000000000LL )
+ {
+ if ( apply )
+ {
+ if ( caster )
+ // prevent double apply bonuses
+ if(m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading())
+ m_modifier.m_amount = caster->SpellHealingBonus(GetSpellProto(), m_modifier.m_amount, SPELL_DIRECT_DAMAGE, m_target);
+ }
+ else
+ {
+ // Final heal only on dispelled or duration end
+ if ( !(GetAuraDuration() <= 0 || m_removeMode==AURA_REMOVE_BY_DISPEL) )
+ return;
+
+ // have a look if there is still some other Lifebloom dummy aura
+ Unit::AuraList auras = m_target->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::iterator itr = auras.begin(); itr!=auras.end(); itr++)
+ if((*itr)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID &&
+ (*itr)->GetSpellProto()->SpellFamilyFlags & 0x1000000000LL)
+ return;
+
+ // final heal
+ m_target->CastCustomSpell(m_target,33778,&m_modifier.m_amount,NULL,NULL,true,NULL,this,GetCasterGUID());
+ }
+ return;
+ }
+
+ // Predatory Strikes
+ if(m_target->GetTypeId()==TYPEID_PLAYER && GetSpellProto()->SpellIconID == 1563)
+ {
+ ((Player*)m_target)->UpdateAttackPowerAndDamage();
+ return;
+ }
+ // Idol of the Emerald Queen
+ if ( GetId() == 34246 && m_target->GetTypeId()==TYPEID_PLAYER )
+ {
+ if(apply)
+ {
+ SpellModifier *mod = new SpellModifier;
+ mod->op = SPELLMOD_DOT;
+ mod->value = m_modifier.m_amount/7;
+ mod->type = SPELLMOD_FLAT;
+ mod->spellId = GetId();
+ mod->effectId = m_effIndex;
+ mod->lastAffected = NULL;
+ mod->mask = 0x001000000000LL;
+ mod->charges = 0;
+
+ m_spellmod = mod;
+ }
+
+ ((Player*)m_target)->AddSpellMod(m_spellmod, apply);
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_HUNTER:
+ {
+ // Improved Aspect of the Viper
+ if( GetId()==38390 && m_target->GetTypeId()==TYPEID_PLAYER )
+ {
+ if(apply)
+ {
+ // + effect value for Aspect of the Viper
+ SpellModifier *mod = new SpellModifier;
+ mod->op = SPELLMOD_EFFECT1;
+ mod->value = m_modifier.m_amount;
+ mod->type = SPELLMOD_FLAT;
+ mod->spellId = GetId();
+ mod->effectId = m_effIndex;
+ mod->lastAffected = NULL;
+ mod->mask = 0x4000000000000LL;
+ mod->charges = 0;
+
+ m_spellmod = mod;
+ }
+
+ ((Player*)m_target)->AddSpellMod(m_spellmod, apply);
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_SHAMAN:
+ {
+ // Improved Weapon Totems
+ if( GetSpellProto()->SpellIconID == 57 && m_target->GetTypeId()==TYPEID_PLAYER )
+ {
+ if(apply)
+ {
+ SpellModifier *mod = new SpellModifier;
+ mod->op = SPELLMOD_EFFECT1;
+ mod->value = m_modifier.m_amount;
+ mod->type = SPELLMOD_PCT;
+ mod->spellId = GetId();
+ mod->effectId = m_effIndex;
+ mod->lastAffected = NULL;
+ switch (m_effIndex)
+ {
+ case 0:
+ mod->mask = 0x00200000000LL; // Windfury Totem
+ break;
+ case 1:
+ mod->mask = 0x00400000000LL; // Flametongue Totem
+ break;
+ }
+ mod->charges = 0;
+
+ m_spellmod = mod;
+ }
+
+ ((Player*)m_target)->AddSpellMod(m_spellmod, apply);
+ return;
+ }
+ break;
+ }
+ }
+
+ // pet auras
+ if(PetAura const* petSpell = spellmgr.GetPetAura(GetId()))
+ {
+ if(apply)
+ m_target->AddPetAura(petSpell);
+ else
+ m_target->RemovePetAura(petSpell);
+ return;
+ }
+}
+
+void Aura::HandleAuraPeriodicDummy(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ SpellEntry const*spell = GetSpellProto();
+ switch( spell->SpellFamilyName)
+ {
+ case SPELLFAMILY_ROGUE:
+ {
+ // Master of Subtlety
+ if (spell->Id==31666 && !apply && Real)
+ {
+ m_target->RemoveAurasDueToSpell(31665);
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_HUNTER:
+ {
+ // Aspect of the Viper
+ if (spell->SpellFamilyFlags&0x0004000000000000LL)
+ {
+ // Update regen on remove
+ if (!apply && m_target->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)m_target)->UpdateManaRegen();
+ break;
+ }
+ break;
+ }
+ }
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandleAuraMounted(bool apply, bool Real)
+{
+ if(apply)
+ {
+ CreatureInfo const* ci = objmgr.GetCreatureTemplate(m_modifier.m_miscvalue);
+ if(!ci)
+ {
+ sLog.outErrorDb("AuraMounted: `creature_template`='%u' not found in database (only need it modelid)", m_modifier.m_miscvalue);
+ return;
+ }
+
+ uint32 team = 0;
+ if (m_target->GetTypeId()==TYPEID_PLAYER)
+ team = ((Player*)m_target)->GetTeam();
+
+ uint32 display_id = objmgr.ChooseDisplayId(team,ci);
+ CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id);
+ if (minfo)
+ display_id = minfo->modelid;
+
+ m_target->Mount(display_id);
+ }
+ else
+ {
+ m_target->Unmount();
+ }
+}
+
+void Aura::HandleAuraWaterWalk(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ WorldPacket data;
+ if(apply)
+ data.Initialize(SMSG_MOVE_WATER_WALK, 8+4);
+ else
+ data.Initialize(SMSG_MOVE_LAND_WALK, 8+4);
+ data.append(m_target->GetPackGUID());
+ data << uint32(0);
+ m_target->SendMessageToSet(&data,true);
+}
+
+void Aura::HandleAuraFeatherFall(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ WorldPacket data;
+ if(apply)
+ data.Initialize(SMSG_MOVE_FEATHER_FALL, 8+4);
+ else
+ data.Initialize(SMSG_MOVE_NORMAL_FALL, 8+4);
+ data.append(m_target->GetPackGUID());
+ data << (uint32)0;
+ m_target->SendMessageToSet(&data,true);
+}
+
+void Aura::HandleAuraHover(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ WorldPacket data;
+ if(apply)
+ data.Initialize(SMSG_MOVE_SET_HOVER, 8+4);
+ else
+ data.Initialize(SMSG_MOVE_UNSET_HOVER, 8+4);
+ data.append(m_target->GetPackGUID());
+ data << uint32(0);
+ m_target->SendMessageToSet(&data,true);
+}
+
+void Aura::HandleWaterBreathing(bool apply, bool Real)
+{
+ if(apply)
+ m_target->waterbreath = true;
+ else if(m_target->GetAurasByType(SPELL_AURA_WATER_BREATHING).empty())
+ {
+ m_target->waterbreath = false;
+
+ // update for enable timer in case not moving target
+ if(m_target->GetTypeId()==TYPEID_PLAYER && m_target->IsInWorld())
+ {
+ ((Player*)m_target)->UpdateUnderwaterState(m_target->GetMap(),m_target->GetPositionX(),m_target->GetPositionY(),m_target->GetPositionZ());
+ ((Player*)m_target)->HandleDrowning();
+ }
+ }
+}
+
+void Aura::HandleAuraModShapeshift(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ uint32 modelid = 0;
+ Powers PowerType = POWER_MANA;
+ ShapeshiftForm form = ShapeshiftForm(m_modifier.m_miscvalue);
+ switch(form)
+ {
+ case FORM_CAT:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 892;
+ else
+ modelid = 8571;
+ PowerType = POWER_ENERGY;
+ break;
+ case FORM_TRAVEL:
+ modelid = 632;
+ break;
+ case FORM_AQUA:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 2428;
+ else
+ modelid = 2428;
+ break;
+ case FORM_BEAR:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 2281;
+ else
+ modelid = 2289;
+ PowerType = POWER_RAGE;
+ break;
+ case FORM_GHOUL:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 10045;
+ break;
+ case FORM_DIREBEAR:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 2281;
+ else
+ modelid = 2289;
+ PowerType = POWER_RAGE;
+ break;
+ case FORM_CREATUREBEAR:
+ modelid = 902;
+ break;
+ case FORM_GHOSTWOLF:
+ modelid = 4613;
+ break;
+ case FORM_FLIGHT:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 20857;
+ else
+ modelid = 20872;
+ break;
+ case FORM_MOONKIN:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 15374;
+ else
+ modelid = 15375;
+ break;
+ case FORM_FLIGHT_EPIC:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 21243;
+ else
+ modelid = 21244;
+ break;
+ case FORM_AMBIENT:
+ case FORM_SHADOW:
+ case FORM_STEALTH:
+ break;
+ case FORM_TREE:
+ modelid = 864;
+ break;
+ case FORM_BATTLESTANCE:
+ case FORM_BERSERKERSTANCE:
+ case FORM_DEFENSIVESTANCE:
+ PowerType = POWER_RAGE;
+ break;
+ case FORM_SPIRITOFREDEMPTION:
+ modelid = 16031;
+ break;
+ default:
+ sLog.outError("Auras: Unknown Shapeshift Type: %u", m_modifier.m_miscvalue);
+ }
+
+ // remove polymorph before changing display id to keep new display id
+ switch ( form )
+ {
+ case FORM_CAT:
+ case FORM_TREE:
+ case FORM_TRAVEL:
+ case FORM_AQUA:
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ case FORM_FLIGHT_EPIC:
+ case FORM_FLIGHT:
+ case FORM_MOONKIN:
+ // remove movement affects
+ m_target->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT);
+ m_target->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED);
+
+ // and polymorphic affects
+ if(m_target->IsPolymorphed())
+ m_target->RemoveAurasDueToSpell(m_target->getTransForm());
+ break;
+ default:
+ break;
+ }
+
+ if(apply)
+ {
+ // remove other shapeshift before applying a new one
+ if(m_target->m_ShapeShiftFormSpellId)
+ {
+ m_target->RemoveAurasDueToSpell(m_target->m_ShapeShiftFormSpellId,this);
+ }
+
+ m_target->SetByteValue(UNIT_FIELD_BYTES_2, 3, form);
+
+ if(modelid > 0)
+ {
+ m_target->SetDisplayId(modelid);
+ }
+
+ if(PowerType != POWER_MANA)
+ {
+ // reset power to default values only at power change
+ if(m_target->getPowerType()!=PowerType)
+ m_target->setPowerType(PowerType);
+
+ switch(form)
+ {
+ case FORM_CAT:
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ {
+ // get furor proc chance
+ uint32 FurorChance = 0;
+ Unit::AuraList const& mDummy = m_target->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = mDummy.begin(); i != mDummy.end(); ++i)
+ {
+ if ((*i)->GetSpellProto()->SpellIconID == 238)
+ {
+ FurorChance = (*i)->GetModifier()->m_amount;
+ break;
+ }
+ }
+
+ if (m_modifier.m_miscvalue == FORM_CAT)
+ {
+ m_target->SetPower(POWER_ENERGY,0);
+ if(urand(1,100) <= FurorChance)
+ {
+ m_target->CastSpell(m_target,17099,true,NULL,this);
+ }
+ }
+ else
+ {
+ m_target->SetPower(POWER_RAGE,0);
+ if(urand(1,100) <= FurorChance)
+ {
+ m_target->CastSpell(m_target,17057,true,NULL,this);
+ }
+ }
+ break;
+ }
+ case FORM_BATTLESTANCE:
+ case FORM_DEFENSIVESTANCE:
+ case FORM_BERSERKERSTANCE:
+ {
+ uint32 Rage_val = 0;
+ // Stance mastery + Tactical mastery (both passive, and last have aura only in defense stance, but need apply at any stance switch)
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ PlayerSpellMap const& sp_list = ((Player *)m_target)->GetSpellMap();
+ for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr)
+ {
+ if(itr->second->state == PLAYERSPELL_REMOVED) continue;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
+ if (spellInfo && spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && spellInfo->SpellIconID == 139)
+ Rage_val += m_target->CalculateSpellDamage(spellInfo,0,spellInfo->EffectBasePoints[0],m_target) * 10;
+ }
+ }
+
+ if (m_target->GetPower(POWER_RAGE) > Rage_val)
+ m_target->SetPower(POWER_RAGE,Rage_val);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ m_target->m_ShapeShiftFormSpellId = GetId();
+ m_target->m_form = form;
+ }
+ else
+ {
+ m_target->SetDisplayId(m_target->GetNativeDisplayId());
+ m_target->SetByteValue(UNIT_FIELD_BYTES_2, 3, FORM_NONE);
+ if(m_target->getClass() == CLASS_DRUID)
+ m_target->setPowerType(POWER_MANA);
+ m_target->m_ShapeShiftFormSpellId = 0;
+ m_target->m_form = FORM_NONE;
+
+ switch(form)
+ {
+ // Nordrassil Harness - bonus
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ case FORM_CAT:
+ {
+ if(Aura* dummy = m_target->GetDummyAura(37315) )
+ m_target->CastSpell(m_target,37316,true,NULL,dummy);
+ break;
+ }
+ // Nordrassil Regalia - bonus
+ case FORM_MOONKIN:
+ {
+ if(Aura* dummy = m_target->GetDummyAura(37324) )
+ m_target->CastSpell(m_target,37325,true,NULL,dummy);
+ break;
+ }
+ }
+ }
+
+ // adding/removing linked auras
+ // add/remove the shapeshift aura's boosts
+ HandleShapeshiftBoosts(apply);
+
+ if(m_target->GetTypeId()==TYPEID_PLAYER)
+ ((Player*)m_target)->InitDataForForm();
+}
+
+void Aura::HandleAuraTransform(bool apply, bool Real)
+{
+ if (apply)
+ {
+ // special case (spell specific functionality)
+ if(m_modifier.m_miscvalue==0)
+ {
+ // player applied only
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ switch(GetId())
+ {
+ // Orb of Deception
+ case 16739:
+ {
+ uint32 orb_model = m_target->GetNativeDisplayId();
+ switch(orb_model)
+ {
+ // Troll Female
+ case 1479: m_target->SetDisplayId(10134); break;
+ // Troll Male
+ case 1478: m_target->SetDisplayId(10135); break;
+ // Tauren Male
+ case 59: m_target->SetDisplayId(10136); break;
+ // Human Male
+ case 49: m_target->SetDisplayId(10137); break;
+ // Human Female
+ case 50: m_target->SetDisplayId(10138); break;
+ // Orc Male
+ case 51: m_target->SetDisplayId(10139); break;
+ // Orc Female
+ case 52: m_target->SetDisplayId(10140); break;
+ // Dwarf Male
+ case 53: m_target->SetDisplayId(10141); break;
+ // Dwarf Female
+ case 54: m_target->SetDisplayId(10142); break;
+ // NightElf Male
+ case 55: m_target->SetDisplayId(10143); break;
+ // NightElf Female
+ case 56: m_target->SetDisplayId(10144); break;
+ // Undead Female
+ case 58: m_target->SetDisplayId(10145); break;
+ // Undead Male
+ case 57: m_target->SetDisplayId(10146); break;
+ // Tauren Female
+ case 60: m_target->SetDisplayId(10147); break;
+ // Gnome Male
+ case 1563: m_target->SetDisplayId(10148); break;
+ // Gnome Female
+ case 1564: m_target->SetDisplayId(10149); break;
+ // BloodElf Female
+ case 15475: m_target->SetDisplayId(17830); break;
+ // BloodElf Male
+ case 15476: m_target->SetDisplayId(17829); break;
+ // Dranei Female
+ case 16126: m_target->SetDisplayId(17828); break;
+ // Dranei Male
+ case 16125: m_target->SetDisplayId(17827); break;
+ default: break;
+ }
+ break;
+ }
+ // Murloc costume
+ case 42365: m_target->SetDisplayId(21723); break;
+ default: break;
+ }
+ }
+ else
+ {
+ CreatureInfo const * ci = objmgr.GetCreatureTemplate(m_modifier.m_miscvalue);
+ if(!ci)
+ {
+ //pig pink ^_^
+ m_target->SetDisplayId(16358);
+ sLog.outError("Auras: unknown creature id = %d (only need its modelid) Form Spell Aura Transform in Spell ID = %d", m_modifier.m_miscvalue, GetId());
+ }
+ else
+ {
+ // Will use the default model here
+ m_target->SetDisplayId(ci->DisplayID_A);
+
+ // Dragonmaw Illusion (set mount model also)
+ if(GetId()==42016 && m_target->GetMountID() && !m_target->GetAurasByType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED).empty())
+ m_target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,16314);
+ }
+ m_target->setTransForm(GetId());
+ }
+
+ // polymorph case
+ if( Real && m_target->GetTypeId() == TYPEID_PLAYER && m_target->IsPolymorphed())
+ {
+ // for players, start regeneration after 1s (in polymorph fast regeneration case)
+ // only if caster is Player (after patch 2.4.2)
+ if(IS_PLAYER_GUID(GetCasterGUID()) )
+ ((Player*)m_target)->setRegenTimer(1000);
+
+ //dismount polymorphed target (after patch 2.4.2)
+ if (m_target->IsMounted())
+ m_target->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
+ }
+ }
+ else
+ {
+ Unit::AuraList const& otherTransforms = m_target->GetAurasByType(SPELL_AURA_TRANSFORM);
+ if(otherTransforms.empty())
+ {
+ m_target->SetDisplayId(m_target->GetNativeDisplayId());
+ m_target->setTransForm(0);
+ }
+ else
+ {
+ // look for other transform auras
+ Aura* handledAura = *otherTransforms.begin();
+ for(Unit::AuraList::const_iterator i = otherTransforms.begin();i != otherTransforms.end(); ++i)
+ {
+ // negative auras are prefered
+ if(!IsPositiveSpell((*i)->GetSpellProto()->Id))
+ {
+ handledAura = *i;
+ break;
+ }
+ }
+ handledAura->ApplyModifier(true);
+ }
+
+ // Dragonmaw Illusion (restore mount model)
+ if(GetId()==42016 && m_target->GetMountID()==16314)
+ {
+ if(!m_target->GetAurasByType(SPELL_AURA_MOUNTED).empty())
+ {
+ uint32 cr_id = m_target->GetAurasByType(SPELL_AURA_MOUNTED).front()->GetModifier()->m_miscvalue;
+ if(CreatureInfo const* ci = objmgr.GetCreatureTemplate(cr_id))
+ {
+ uint32 team = 0;
+ if (m_target->GetTypeId()==TYPEID_PLAYER)
+ team = ((Player*)m_target)->GetTeam();
+
+ uint32 display_id = objmgr.ChooseDisplayId(team,ci);
+ CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id);
+ if (minfo)
+ display_id = minfo->modelid;
+
+ m_target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,display_id);
+ }
+ }
+ }
+ }
+}
+
+void Aura::HandleForceReaction(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(!Real)
+ return;
+
+ Player* player = (Player*)m_target;
+
+ uint32 faction_id = m_modifier.m_miscvalue;
+ uint32 faction_rank = m_modifier.m_amount;
+
+ if(apply)
+ player->m_forcedReactions[faction_id] = ReputationRank(faction_rank);
+ else
+ player->m_forcedReactions.erase(faction_id);
+
+ WorldPacket data;
+ data.Initialize(SMSG_SET_FORCED_REACTIONS, 4+player->m_forcedReactions.size()*(4+4));
+ data << uint32(player->m_forcedReactions.size());
+ for(ForcedReactions::const_iterator itr = player->m_forcedReactions.begin(); itr != player->m_forcedReactions.end(); ++itr)
+ {
+ data << uint32(itr->first); // faction_id (Faction.dbc)
+ data << uint32(itr->second); // reputation rank
+ }
+ player->SendDirectMessage(&data);
+}
+
+void Aura::HandleAuraModSkill(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ uint32 prot=GetSpellProto()->EffectMiscValue[m_effIndex];
+ int32 points = GetModifier()->m_amount;
+
+ ((Player*)m_target)->ModifySkillBonus(prot,(apply ? points: -points),m_modifier.m_auraname==SPELL_AURA_MOD_SKILL_TALENT);
+ if(prot == SKILL_DEFENSE)
+ ((Player*)m_target)->UpdateDefenseBonusesMod();
+}
+
+void Aura::HandleChannelDeathItem(bool apply, bool Real)
+{
+ if(Real && !apply)
+ {
+ Unit* caster = GetCaster();
+ Unit* victim = GetTarget();
+ if(!caster || caster->GetTypeId() != TYPEID_PLAYER || !victim || m_removeMode!=AURA_REMOVE_BY_DEATH)
+ return;
+
+ SpellEntry const *spellInfo = GetSpellProto();
+ if(spellInfo->EffectItemType[m_effIndex] == 0)
+ return;
+
+ // Soul Shard only from non-grey units
+ if( spellInfo->EffectItemType[m_effIndex] == 6265 &&
+ (victim->getLevel() <= MaNGOS::XP::GetGrayLevel(caster->getLevel()) ||
+ victim->GetTypeId()==TYPEID_UNIT && !((Player*)caster)->isAllowedToLoot((Creature*)victim)) )
+ return;
+ ItemPosCountVec dest;
+ uint8 msg = ((Player*)caster)->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, spellInfo->EffectItemType[m_effIndex], 1 );
+ if( msg != EQUIP_ERR_OK )
+ {
+ ((Player*)caster)->SendEquipError( msg, NULL, NULL );
+ return;
+ }
+
+ Item* newitem = ((Player*)caster)->StoreNewItem(dest, spellInfo->EffectItemType[m_effIndex], true);
+ ((Player*)caster)->SendNewItem(newitem, 1, true, false);
+ }
+}
+
+void Aura::HandleBindSight(bool apply, bool Real)
+{
+ Unit* caster = GetCaster();
+ if(!caster || caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ caster->SetUInt64Value(PLAYER_FARSIGHT,apply ? m_target->GetGUID() : 0);
+}
+
+void Aura::HandleFarSight(bool apply, bool Real)
+{
+ Unit* caster = GetCaster();
+ if(!caster || caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ caster->SetUInt64Value(PLAYER_FARSIGHT,apply ? m_modifier.m_miscvalue : 0);
+}
+
+void Aura::HandleAuraTrackCreatures(bool apply, bool Real)
+{
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ if(apply)
+ m_target->RemoveNoStackAurasDueToAura(this);
+ m_target->SetUInt32Value(PLAYER_TRACK_CREATURES, apply ? ((uint32)1)<<(m_modifier.m_miscvalue-1) : 0 );
+}
+
+void Aura::HandleAuraTrackResources(bool apply, bool Real)
+{
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ if(apply)
+ m_target->RemoveNoStackAurasDueToAura(this);
+ m_target->SetUInt32Value(PLAYER_TRACK_RESOURCES, apply ? ((uint32)1)<<(m_modifier.m_miscvalue-1): 0 );
+}
+
+void Aura::HandleAuraTrackStealthed(bool apply, bool Real)
+{
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ if(apply)
+ m_target->RemoveNoStackAurasDueToAura(this);
+
+ m_target->ApplyModFlag(PLAYER_FIELD_BYTES,PLAYER_FIELD_BYTE_TRACK_STEALTHED,apply);
+}
+
+void Aura::HandleAuraModScale(bool apply, bool Real)
+{
+ m_target->ApplyPercentModFloatValue(OBJECT_FIELD_SCALE_X,m_modifier.m_amount,apply);
+}
+
+void Aura::HandleModPossess(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ if(m_target->getLevel() > m_modifier.m_amount)
+ return;
+
+ // not possess yourself
+ if(GetCasterGUID() == m_target->GetGUID())
+ return;
+
+ Unit* caster = GetCaster();
+ if(!caster)
+ return;
+
+ if( apply )
+ {
+ m_target->SetCharmerGUID(GetCasterGUID());
+ m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,caster->getFaction());
+ caster->SetCharm(m_target);
+
+ m_target->CombatStop();
+ m_target->DeleteThreatList();
+ if(m_target->GetTypeId() == TYPEID_UNIT)
+ {
+ m_target->StopMoving();
+ m_target->GetMotionMaster()->Clear();
+ m_target->GetMotionMaster()->MoveIdle();
+ CharmInfo *charmInfo = ((Creature*)m_target)->InitCharmInfo(m_target);
+ charmInfo->InitPossessCreateSpells();
+ }
+
+ if(caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)caster)->PossessSpellInitialize();
+ }
+ }
+ else
+ {
+ m_target->SetCharmerGUID(0);
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)m_target)->setFactionForRace(m_target->getRace());
+ }
+ else if(m_target->GetTypeId() == TYPEID_UNIT)
+ {
+ CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo();
+ m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,cinfo->faction_A);
+ }
+
+ caster->SetCharm(0);
+
+ if(caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_PET_SPELLS, 8);
+ data << uint64(0);
+ ((Player*)caster)->GetSession()->SendPacket(&data);
+ }
+ if(m_target->GetTypeId() == TYPEID_UNIT)
+ {
+ ((Creature*)m_target)->AIM_Initialize();
+
+ if (((Creature*)m_target)->AI())
+ ((Creature*)m_target)->AI()->AttackStart(caster);
+ }
+ }
+ if(caster->GetTypeId() == TYPEID_PLAYER)
+ caster->SetUInt64Value(PLAYER_FARSIGHT,apply ? m_target->GetGUID() : 0);
+}
+
+void Aura::HandleModPossessPet(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ Unit* caster = GetCaster();
+ if(!caster || caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+ if(caster->GetPet() != m_target)
+ return;
+
+ if(apply)
+ {
+ caster->SetUInt64Value(PLAYER_FARSIGHT, m_target->GetGUID());
+ m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN5);
+ }
+ else
+ {
+ caster->SetUInt64Value(PLAYER_FARSIGHT, 0);
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN5);
+ }
+}
+
+void Aura::HandleModCharm(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ // not charm yourself
+ if(GetCasterGUID() == m_target->GetGUID())
+ return;
+
+ Unit* caster = GetCaster();
+ if(!caster)
+ return;
+
+ if(int32(m_target->getLevel()) <= m_modifier.m_amount)
+ {
+ if( apply )
+ {
+ m_target->SetCharmerGUID(GetCasterGUID());
+ m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,caster->getFaction());
+ m_target->CastStop(m_target==caster ? GetId() : 0);
+ caster->SetCharm(m_target);
+
+ m_target->CombatStop();
+ m_target->DeleteThreatList();
+
+ if(m_target->GetTypeId() == TYPEID_UNIT)
+ {
+ ((Creature*)m_target)->AIM_Initialize();
+ CharmInfo *charmInfo = ((Creature*)m_target)->InitCharmInfo(m_target);
+ charmInfo->InitCharmCreateSpells();
+ charmInfo->SetReactState( REACT_DEFENSIVE );
+
+ if(caster->GetTypeId() == TYPEID_PLAYER && caster->getClass() == CLASS_WARLOCK)
+ {
+ CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo();
+ if(cinfo && cinfo->type == CREATURE_TYPE_DEMON)
+ {
+ //to prevent client crash
+ m_target->SetFlag(UNIT_FIELD_BYTES_0, 2048);
+ //just to enable stat window
+ charmInfo->SetPetNumber(objmgr.GeneratePetNumber(), true);
+ //if charmed two demons the same session, the 2nd gets the 1st one's name
+ m_target->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
+ }
+ }
+ }
+
+ if(caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)caster)->CharmSpellInitialize();
+ }
+ }
+ else
+ {
+ m_target->SetCharmerGUID(0);
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)m_target)->setFactionForRace(m_target->getRace());
+ }
+ else
+ {
+ CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo();
+
+ // restore faction
+ if(((Creature*)m_target)->isPet())
+ {
+ if(Unit* owner = m_target->GetOwner())
+ m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,owner->getFaction());
+ else if(cinfo)
+ m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,cinfo->faction_A);
+ }
+ else if(cinfo) // normal creature
+ m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,cinfo->faction_A);
+
+ // restore UNIT_FIELD_BYTES_0
+ if(cinfo && caster->GetTypeId() == TYPEID_PLAYER && caster->getClass() == CLASS_WARLOCK && cinfo->type == CREATURE_TYPE_DEMON)
+ {
+ CreatureDataAddon const *cainfo = ((Creature*)m_target)->GetCreatureAddon();
+ if(cainfo && cainfo->bytes0 != 0)
+ m_target->SetUInt32Value(UNIT_FIELD_BYTES_0, cainfo->bytes0);
+ else
+ m_target->RemoveFlag(UNIT_FIELD_BYTES_0, 2048);
+
+ if(m_target->GetCharmInfo())
+ m_target->GetCharmInfo()->SetPetNumber(0, true);
+ else
+ sLog.outError("Aura::HandleModCharm: target="I64FMTD" with typeid=%d has a charm aura but no charm info!", m_target->GetGUID(), m_target->GetTypeId());
+ }
+ }
+
+ caster->SetCharm(0);
+
+ if(caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_PET_SPELLS, 8);
+ data << uint64(0);
+ ((Player*)caster)->GetSession()->SendPacket(&data);
+ }
+ if(m_target->GetTypeId() == TYPEID_UNIT)
+ {
+ ((Creature*)m_target)->AIM_Initialize();
+ if (((Creature*)m_target)->AI())
+ ((Creature*)m_target)->AI()->AttackStart(caster);
+ }
+ }
+ }
+}
+
+void Aura::HandleModConfuse(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ m_target->SetConfused(apply, GetCasterGUID(), GetId());
+}
+
+void Aura::HandleModFear(bool apply, bool Real)
+{
+ if (!Real)
+ return;
+
+ m_target->SetFeared(apply, GetCasterGUID(), GetId());
+}
+
+void Aura::HandleFeignDeath(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if( apply )
+ {
+ /*
+ WorldPacket data(SMSG_FEIGN_DEATH_RESISTED, 9);
+ data<<m_target->GetGUID();
+ data<<uint8(0);
+ m_target->SendMessageToSet(&data,true);
+ */
+ // blizz like 2.0.x
+ m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN6);
+ // blizz like 2.0.x
+ m_target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH);
+ // blizz like 2.0.x
+ m_target->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD);
+
+ m_target->addUnitState(UNIT_STAT_DIED);
+ m_target->CombatStop();
+
+ // prevent interrupt message
+ if(m_caster_guid==m_target->GetGUID() && m_target->m_currentSpells[CURRENT_GENERIC_SPELL])
+ m_target->m_currentSpells[CURRENT_GENERIC_SPELL]->finish();
+ m_target->InterruptNonMeleeSpells(true);
+ m_target->getHostilRefManager().deleteReferences();
+ }
+ else
+ {
+ /*
+ WorldPacket data(SMSG_FEIGN_DEATH_RESISTED, 9);
+ data<<m_target->GetGUID();
+ data<<uint8(1);
+ m_target->SendMessageToSet(&data,true);
+ */
+ // blizz like 2.0.x
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN6);
+ // blizz like 2.0.x
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH);
+ // blizz like 2.0.x
+ m_target->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD);
+
+ m_target->clearUnitState(UNIT_STAT_DIED);
+ }
+}
+
+void Aura::HandleAuraModDisarm(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ if(!apply && m_target->HasAuraType(SPELL_AURA_MOD_DISARM))
+ return;
+
+ // not sure for it's correctness
+ if(apply)
+ m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED);
+ else
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED);
+
+ // only at real add/remove aura
+ if (m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // main-hand attack speed already set to special value for feral form already and don't must chnage and reset at remove.
+ if (((Player *)m_target)->IsInFeralForm())
+ return;
+
+ if (apply)
+ m_target->SetAttackTime(BASE_ATTACK,BASE_ATTACK_TIME);
+ else
+ ((Player *)m_target)->SetRegularAttackTime();
+
+ m_target->UpdateDamagePhysical(BASE_ATTACK);
+}
+
+void Aura::HandleAuraModStun(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ if (apply)
+ {
+ m_target->addUnitState(UNIT_STAT_STUNNED);
+ m_target->SetUInt64Value(UNIT_FIELD_TARGET, 0);
+
+ m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+ m_target->CastStop(m_target->GetGUID() == GetCasterGUID() ? GetId() : 0);
+
+ // Creature specific
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ ((Creature*)m_target)->StopMoving();
+ else
+ m_target->SetUnitMovementFlags(0); //Clear movement flags
+
+ WorldPacket data(SMSG_FORCE_MOVE_ROOT, 8);
+
+ data.append(m_target->GetPackGUID());
+ data << uint32(0);
+ m_target->SendMessageToSet(&data,true);
+ }
+ else
+ {
+ // Real remove called after current aura remove from lists, check if other similar auras active
+ if(m_target->HasAuraType(SPELL_AURA_MOD_STUN))
+ return;
+
+ m_target->clearUnitState(UNIT_STAT_STUNNED);
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+
+ if(!m_target->hasUnitState(UNIT_STAT_ROOT)) // prevent allow move if have also root effect
+ {
+ if(m_target->getVictim() && m_target->isAlive())
+ m_target->SetUInt64Value(UNIT_FIELD_TARGET,m_target->getVictim()->GetGUID() );
+
+ WorldPacket data(SMSG_FORCE_MOVE_UNROOT, 8+4);
+ data.append(m_target->GetPackGUID());
+ data << uint32(0);
+ m_target->SendMessageToSet(&data,true);
+ }
+
+ // Wyvern Sting
+ if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_HUNTER && GetSpellProto()->SpellIconID == 1721)
+ {
+ Unit* caster = GetCaster();
+ if( !caster || caster->GetTypeId()!=TYPEID_PLAYER )
+ return;
+
+ uint32 spell_id = 0;
+
+ switch(GetId())
+ {
+ case 19386: spell_id = 24131; break;
+ case 24132: spell_id = 24134; break;
+ case 24133: spell_id = 24135; break;
+ case 27068: spell_id = 27069; break;
+ default:
+ sLog.outError("Spell selection called for unexpected original spell %u, new spell for this spell family?",GetId());
+ return;
+ }
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id);
+
+ if(!spellInfo)
+ return;
+
+ caster->CastSpell(m_target,spellInfo,true,NULL,this);
+ return;
+ }
+ }
+}
+
+void Aura::HandleModStealth(bool apply, bool Real)
+{
+ if(apply)
+ {
+ // drop flag at stealth in bg
+ if(Real && m_target->GetTypeId()==TYPEID_PLAYER && ((Player*)m_target)->InBattleGround())
+ if(BattleGround *bg = ((Player*)m_target)->GetBattleGround())
+ bg->EventPlayerDroppedFlag((Player*)m_target);
+
+ // only at real aura add
+ if(Real)
+ {
+ m_target->SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x02);
+ if(m_target->GetTypeId()==TYPEID_PLAYER)
+ m_target->SetFlag(PLAYER_FIELD_BYTES2, 0x2000);
+
+ // apply only if not in GM invisibility (and overwrite invisibility state)
+ if(m_target->GetVisibility()!=VISIBILITY_OFF)
+ {
+ m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT);
+ m_target->SetVisibility(VISIBILITY_GROUP_STEALTH);
+ }
+
+ // for RACE_NIGHTELF stealth
+ if(m_target->GetTypeId()==TYPEID_PLAYER && GetId()==20580)
+ m_target->CastSpell(m_target, 21009, true, NULL, this);
+ }
+ }
+ else
+ {
+ // only at real aura remove
+ if(Real)
+ {
+ // for RACE_NIGHTELF stealth
+ if(m_target->GetTypeId()==TYPEID_PLAYER && GetId()==20580)
+ m_target->RemoveAurasDueToSpell(21009);
+
+ // if last SPELL_AURA_MOD_STEALTH and no GM invisibility
+ if(!m_target->HasAuraType(SPELL_AURA_MOD_STEALTH) && m_target->GetVisibility()!=VISIBILITY_OFF)
+ {
+ m_target->SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x00);
+ if(m_target->GetTypeId()==TYPEID_PLAYER)
+ m_target->RemoveFlag(PLAYER_FIELD_BYTES2, 0x2000);
+
+ // restore invisibility if any
+ if(m_target->HasAuraType(SPELL_AURA_MOD_INVISIBILITY))
+ {
+ m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT);
+ m_target->SetVisibility(VISIBILITY_GROUP_INVISIBILITY);
+ }
+ else
+ m_target->SetVisibility(VISIBILITY_ON);
+ }
+ }
+ }
+
+ // Master of Subtlety
+ Unit::AuraList const& mDummyAuras = m_target->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
+ {
+ if ((*i)->GetSpellProto()->SpellIconID == 2114)
+ {
+ if (apply)
+ {
+ int32 bp = (*i)->GetModifier()->m_amount;
+ m_target->CastCustomSpell(m_target,31665,&bp,NULL,NULL,true);
+ }
+ else
+ m_target->CastSpell(m_target,31666,true);
+ break;
+ }
+ }
+}
+
+void Aura::HandleInvisibility(bool apply, bool Real)
+{
+ if(apply)
+ {
+ m_target->m_invisibilityMask |= (1 << m_modifier.m_miscvalue);
+
+ if(Real && m_target->GetTypeId()==TYPEID_PLAYER)
+ {
+ // apply glow vision
+ m_target->SetFlag(PLAYER_FIELD_BYTES2,PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
+
+ // drop flag at invisible in bg
+ if(((Player*)m_target)->InBattleGround())
+ if(BattleGround *bg = ((Player*)m_target)->GetBattleGround())
+ bg->EventPlayerDroppedFlag((Player*)m_target);
+ }
+
+ // apply only if not in GM invisibility and not stealth
+ if(m_target->GetVisibility()==VISIBILITY_ON)
+ {
+ // Aura not added yet but visibility code expect temporary add aura
+ m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT);
+ m_target->SetVisibility(VISIBILITY_GROUP_INVISIBILITY);
+ }
+ }
+ else
+ {
+ // recalculate value at modifier remove (current aura already removed)
+ m_target->m_invisibilityMask = 0;
+ Unit::AuraList const& auras = m_target->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY);
+ for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ m_target->m_invisibilityMask |= (1 << m_modifier.m_miscvalue);
+
+ // only at real aura remove and if not have different invisibility auras.
+ if(Real && m_target->m_invisibilityMask==0)
+ {
+ // remove glow vision
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ m_target->RemoveFlag(PLAYER_FIELD_BYTES2,PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
+
+ // apply only if not in GM invisibility & not stealthed while invisible
+ if(m_target->GetVisibility()!=VISIBILITY_OFF)
+ {
+ // if have stealth aura then already have stealth visibility
+ if(!m_target->HasAuraType(SPELL_AURA_MOD_STEALTH))
+ m_target->SetVisibility(VISIBILITY_ON);
+ }
+ }
+ }
+}
+
+void Aura::HandleInvisibilityDetect(bool apply, bool Real)
+{
+ if(apply)
+ {
+ m_target->m_detectInvisibilityMask |= (1 << m_modifier.m_miscvalue);
+ }
+ else
+ {
+ // recalculate value at modifier remove (current aura already removed)
+ m_target->m_detectInvisibilityMask = 0;
+ Unit::AuraList const& auras = m_target->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY_DETECTION);
+ for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ m_target->m_detectInvisibilityMask |= (1 << m_modifier.m_miscvalue);
+ }
+ if(Real && m_target->GetTypeId()==TYPEID_PLAYER)
+ ObjectAccessor::UpdateVisibilityForPlayer((Player*)m_target);
+}
+
+void Aura::HandleAuraModRoot(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ uint32 apply_stat = UNIT_STAT_ROOT;
+ if (apply)
+ {
+ m_target->addUnitState(UNIT_STAT_ROOT);
+ m_target->SetUInt64Value (UNIT_FIELD_TARGET, 0);
+ // probably wrong
+ m_target->SetFlag(UNIT_FIELD_FLAGS,(apply_stat<<16));
+
+ //Save last orientation
+ if( m_target->getVictim() )
+ m_target->SetOrientation(m_target->GetAngle(m_target->getVictim()));
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_FORCE_MOVE_ROOT, 10);
+ data.append(m_target->GetPackGUID());
+ data << (uint32)2;
+ m_target->SendMessageToSet(&data,true);
+
+ //Clear unit movement flags
+ m_target->SetUnitMovementFlags(0);
+ }
+ else
+ ((Creature *)m_target)->StopMoving();
+ }
+ else
+ {
+ // Real remove called after current aura remove from lists, check if other similar auras active
+ if(m_target->HasAuraType(SPELL_AURA_MOD_ROOT))
+ return;
+
+ m_target->clearUnitState(UNIT_STAT_ROOT);
+ // probably wrong
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS,(apply_stat<<16));
+
+ if(!m_target->hasUnitState(UNIT_STAT_STUNNED)) // prevent allow move if have also stun effect
+ {
+ if(m_target->getVictim() && m_target->isAlive())
+ m_target->SetUInt64Value (UNIT_FIELD_TARGET,m_target->getVictim()->GetGUID() );
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_FORCE_MOVE_UNROOT, 10);
+ data.append(m_target->GetPackGUID());
+ data << (uint32)2;
+ m_target->SendMessageToSet(&data,true);
+ }
+ }
+ }
+}
+
+void Aura::HandleAuraModSilence(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ if(apply)
+ {
+ m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED);
+ // Stop cast only spells vs PreventionType == SPELL_PREVENTION_TYPE_SILENCE
+ for (uint32 i = CURRENT_MELEE_SPELL; i < CURRENT_MAX_SPELL;i++)
+ {
+ Spell* currentSpell = m_target->m_currentSpells[i];
+ if (currentSpell && currentSpell->m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE)
+ {
+ uint32 state = currentSpell->getState();
+ // Stop spells on prepere or casting state
+ if ( state == SPELL_STATE_PREPARING || state == SPELL_STATE_CASTING )
+ {
+ currentSpell->cancel();
+ currentSpell->SetDeletable(true);
+ m_target->m_currentSpells[i] = NULL;
+ }
+ }
+ }
+
+ switch (GetId())
+ {
+ // Arcane Torrent (Energy)
+ case 25046:
+ {
+ Unit * caster = GetCaster();
+ if (!caster)
+ return;
+
+ // Search Mana Tap auras on caster
+ int32 energy = 0;
+ Unit::AuraList const& m_dummyAuras = caster->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i)
+ if ((*i)->GetId() == 28734)
+ ++energy;
+ if (energy)
+ {
+ energy *= 10;
+ caster->CastCustomSpell(caster, 25048, &energy, NULL, NULL, true);
+ caster->RemoveAurasDueToSpell(28734);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Real remove called after current aura remove from lists, check if other similar auras active
+ if(m_target->HasAuraType(SPELL_AURA_MOD_SILENCE))
+ return;
+
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED);
+ }
+}
+
+void Aura::HandleModThreat(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ if(!m_target->isAlive())
+ return;
+
+ Unit* caster = GetCaster();
+
+ if(!caster || !caster->isAlive())
+ return;
+
+ int level_diff = 0;
+ int multiplier = 0;
+ switch (GetId())
+ {
+ // Arcane Shroud
+ case 26400:
+ level_diff = m_target->getLevel() - 60;
+ multiplier = 2;
+ break;
+ // The Eye of Diminution
+ case 28862:
+ level_diff = m_target->getLevel() - 60;
+ multiplier = 1;
+ break;
+ }
+ if (level_diff > 0)
+ m_modifier.m_amount += multiplier * level_diff;
+
+ for(int8 x=0;x < MAX_SPELL_SCHOOL;x++)
+ {
+ if(m_modifier.m_miscvalue & int32(1<<x))
+ {
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ ApplyPercentModFloatVar(m_target->m_threatModifier[x], m_positive ? m_modifier.m_amount : -m_modifier.m_amount, apply);
+ }
+ }
+}
+
+void Aura::HandleAuraModTotalThreat(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ if(!m_target->isAlive() || m_target->GetTypeId()!= TYPEID_PLAYER)
+ return;
+
+ Unit* caster = GetCaster();
+
+ if(!caster || !caster->isAlive())
+ return;
+
+ float threatMod = 0.0f;
+ if(apply)
+ threatMod = float(m_modifier.m_amount);
+ else
+ threatMod = float(-m_modifier.m_amount);
+
+ m_target->getHostilRefManager().threatAssist(caster, threatMod);
+}
+
+void Aura::HandleModTaunt(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ if(!m_target->isAlive() || !m_target->CanHaveThreatList())
+ return;
+
+ Unit* caster = GetCaster();
+
+ if(!caster || !caster->isAlive() || caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(apply)
+ {
+ m_target->TauntApply(caster);
+ }
+ else
+ {
+ // When taunt aura fades out, mob will switch to previous target if current has less than 1.1 * secondthreat
+ m_target->TauntFadeOut(caster);
+ }
+}
+
+/*********************************************************/
+/*** MODIFY SPEED ***/
+/*********************************************************/
+void Aura::HandleAuraModIncreaseSpeed(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ m_target->UpdateSpeed(MOVE_RUN, true);
+}
+
+void Aura::HandleAuraModIncreaseMountedSpeed(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ m_target->UpdateSpeed(MOVE_RUN, true);
+}
+
+void Aura::HandleAuraModIncreaseFlightSpeed(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ // Enable Fly mode for flying mounts
+ if (m_modifier.m_auraname == SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED)
+ {
+ WorldPacket data;
+ if(apply)
+ data.Initialize(SMSG_MOVE_SET_CAN_FLY, 12);
+ else
+ data.Initialize(SMSG_MOVE_UNSET_CAN_FLY, 12);
+ data.append(m_target->GetPackGUID());
+ data << uint32(0); // unknown
+ m_target->SendMessageToSet(&data, true);
+
+ //Players on flying mounts must be immune to polymorph
+ if (m_target->GetTypeId()==TYPEID_PLAYER)
+ m_target->ApplySpellImmune(GetId(),IMMUNITY_MECHANIC,MECHANIC_POLYMORPH,apply);
+
+ // Dragonmaw Illusion (overwrite mount model, mounted aura already applied)
+ if( apply && m_target->HasAura(42016,0) && m_target->GetMountID())
+ m_target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,16314);
+ }
+
+ m_target->UpdateSpeed(MOVE_FLY, true);
+}
+
+void Aura::HandleAuraModIncreaseSwimSpeed(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ m_target->UpdateSpeed(MOVE_SWIM, true);
+}
+
+void Aura::HandleAuraModDecreaseSpeed(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ m_target->UpdateSpeed(MOVE_RUN, true);
+ m_target->UpdateSpeed(MOVE_SWIM, true);
+ m_target->UpdateSpeed(MOVE_FLY, true);
+}
+
+void Aura::HandleAuraModUseNormalSpeed(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ m_target->UpdateSpeed(MOVE_RUN, true);
+ m_target->UpdateSpeed(MOVE_SWIM, true);
+ m_target->UpdateSpeed(MOVE_FLY, true);
+}
+
+/*********************************************************/
+/*** IMMUNITY ***/
+/*********************************************************/
+
+void Aura::HandleModMechanicImmunity(bool apply, bool Real)
+{
+ uint32 mechanic = 1 << m_modifier.m_miscvalue;
+
+ //immune movement impairment and loss of control
+ if(GetId()==42292)
+ mechanic=IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK;
+
+ if(apply && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY)
+ {
+ Unit::AuraMap& Auras = m_target->GetAuras();
+ for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next)
+ {
+ next = iter;
+ ++next;
+ SpellEntry const *spell = iter->second->GetSpellProto();
+ if (!( spell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) // spells unaffected by invulnerability
+ && !iter->second->IsPositive() // only remove negative spells
+ && spell->Id != GetId())
+ {
+ //check for mechanic mask
+ if(GetSpellMechanicMask(spell, iter->second->GetEffIndex()) & mechanic)
+ {
+ m_target->RemoveAurasDueToSpell(spell->Id);
+ if(Auras.empty())
+ break;
+ else
+ next = Auras.begin();
+ }
+ }
+ }
+ }
+
+ m_target->ApplySpellImmune(GetId(),IMMUNITY_MECHANIC,m_modifier.m_miscvalue,apply);
+
+ // special cases
+ switch(m_modifier.m_miscvalue)
+ {
+ case MECHANIC_INVULNERABILITY:
+ m_target->ModifyAuraState(AURA_STATE_FORBEARANCE,apply);
+ break;
+ case MECHANIC_SHIELD:
+ m_target->ModifyAuraState(AURA_STATE_WEAKENED_SOUL,apply);
+ break;
+ }
+
+ // Bestial Wrath
+ if ( GetSpellProto()->SpellFamilyName == SPELLFAMILY_HUNTER && GetSpellProto()->SpellIconID == 1680)
+ {
+ // The Beast Within cast on owner if talent present
+ if ( Unit* owner = m_target->GetOwner() )
+ {
+ // Search talent
+ Unit::AuraList const& m_dummyAuras = owner->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i)
+ {
+ if ( (*i)->GetSpellProto()->SpellIconID == 2229 )
+ {
+ if (apply)
+ owner->CastSpell(owner, 34471, true, 0, this);
+ else
+ owner->RemoveAurasDueToSpell(34471);
+ break;
+ }
+ }
+ }
+ }
+
+ // The Beast Within and Bestial Wrath - immunity
+ if(GetId() == 19574 || GetId() == 34471)
+ {
+ if(apply)
+ {
+ m_target->CastSpell(m_target,24395,true);
+ m_target->CastSpell(m_target,24396,true);
+ m_target->CastSpell(m_target,24397,true);
+ m_target->CastSpell(m_target,26592,true);
+ }
+ else
+ {
+ m_target->RemoveAurasDueToSpell(24395);
+ m_target->RemoveAurasDueToSpell(24396);
+ m_target->RemoveAurasDueToSpell(24397);
+ m_target->RemoveAurasDueToSpell(26592);
+ }
+ }
+}
+
+void Aura::HandleAuraModEffectImmunity(bool apply, bool Real)
+{
+ if(!apply)
+ {
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)m_target)->InBattleGround())
+ {
+ BattleGround *bg = ((Player*)m_target)->GetBattleGround();
+ if(bg)
+ {
+ switch(bg->GetTypeID())
+ {
+ case BATTLEGROUND_AV:
+ {
+ break;
+ }
+ case BATTLEGROUND_WS:
+ {
+ // Warsong Flag, horde // Silverwing Flag, alliance
+ if(GetId() == 23333 || GetId() == 23335)
+ bg->EventPlayerDroppedFlag(((Player*)m_target));
+ break;
+ }
+ case BATTLEGROUND_AB:
+ {
+ break;
+ }
+ case BATTLEGROUND_EY:
+ {
+ if(GetId() == 34976)
+ bg->EventPlayerDroppedFlag(((Player*)m_target));
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ m_target->ApplySpellImmune(GetId(),IMMUNITY_EFFECT,m_modifier.m_miscvalue,apply);
+}
+
+void Aura::HandleAuraModStateImmunity(bool apply, bool Real)
+{
+ if(apply && Real && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY)
+ {
+ Unit::AuraList const& auraList = m_target->GetAurasByType(AuraType(m_modifier.m_miscvalue));
+ for(Unit::AuraList::const_iterator itr = auraList.begin(); itr != auraList.end();)
+ {
+ if (auraList.front() != this) // skip itself aura (it already added)
+ {
+ m_target->RemoveAurasDueToSpell(auraList.front()->GetId());
+ itr = auraList.begin();
+ }
+ else
+ ++itr;
+ }
+ }
+
+ m_target->ApplySpellImmune(GetId(),IMMUNITY_STATE,m_modifier.m_miscvalue,apply);
+}
+
+void Aura::HandleAuraModSchoolImmunity(bool apply, bool Real)
+{
+ m_target->ApplySpellImmune(GetId(),IMMUNITY_SCHOOL,m_modifier.m_miscvalue,apply);
+
+ if(Real && apply && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY)
+ {
+ if(IsPositiveSpell(GetId())) //Only positive immunity removes auras
+ {
+ uint32 school_mask = m_modifier.m_miscvalue;
+ Unit::AuraMap& Auras = m_target->GetAuras();
+ for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next)
+ {
+ next = iter;
+ ++next;
+ SpellEntry const *spell = iter->second->GetSpellProto();
+ if((GetSpellSchoolMask(spell) & school_mask)//Check for school mask
+ && !( spell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) //Spells unaffected by invulnerability
+ && !iter->second->IsPositive() //Don't remove positive spells
+ && spell->Id != GetId() ) //Don't remove self
+ {
+ m_target->RemoveAurasDueToSpell(spell->Id);
+ if(Auras.empty())
+ break;
+ else
+ next = Auras.begin();
+ }
+ }
+ }
+ }
+ if( Real && GetSpellProto()->Mechanic == MECHANIC_BANISH )
+ {
+ if( apply )
+ m_target->addUnitState(UNIT_STAT_ISOLATED);
+ else
+ m_target->clearUnitState(UNIT_STAT_ISOLATED);
+ }
+}
+
+void Aura::HandleAuraModDmgImmunity(bool apply, bool Real)
+{
+ m_target->ApplySpellImmune(GetId(),IMMUNITY_DAMAGE,m_modifier.m_miscvalue,apply);
+}
+
+void Aura::HandleAuraModDispelImmunity(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ m_target->ApplySpellDispelImmunity(m_spellProto, DispelType(m_modifier.m_miscvalue), apply);
+}
+
+void Aura::HandleAuraProcTriggerSpell(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ if(apply)
+ {
+ // some spell have charges by functionality not have its in spell data
+ switch (GetId())
+ {
+ case 28200: // Ascendance (Talisman of Ascendance trinket)
+ m_procCharges = 6;
+ UpdateAuraCharges();
+ break;
+ default: break;
+ }
+ }
+}
+
+void Aura::HandleAuraModStalked(bool apply, bool Real)
+{
+ // used by spells: Hunter's Mark, Mind Vision, Syndicate Tracker (MURP) DND
+ if(apply)
+ m_target->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TRACK_UNIT);
+ else
+ m_target->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TRACK_UNIT);
+}
+
+/*********************************************************/
+/*** PERIODIC ***/
+/*********************************************************/
+
+void Aura::HandlePeriodicTriggerSpell(bool apply, bool Real)
+{
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+ m_isTrigger = apply;
+
+ // Curse of the Plaguebringer
+ if (!apply && m_spellProto->Id == 29213 && m_removeMode!=AURA_REMOVE_BY_DISPEL)
+ {
+ // Cast Wrath of the Plaguebringer if not dispelled
+ m_target->CastSpell(m_target, 29214, true, 0, this);
+ }
+}
+
+void Aura::HandlePeriodicEnergize(bool apply, bool Real)
+{
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandlePeriodicHeal(bool apply, bool Real)
+{
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+
+ // only at real apply
+ if (Real && apply && GetSpellProto()->Mechanic == MECHANIC_BANDAGE)
+ {
+ // provided m_target as original caster to prevent apply aura caster selection for this negative buff
+ m_target->CastSpell(m_target,11196,true,NULL,this,m_target->GetGUID());
+ }
+
+ // For prevent double apply bonuses
+ bool loading = (m_target->GetTypeId() == TYPEID_PLAYER && ((Player*)m_target)->GetSession()->PlayerLoading());
+
+ if(!loading && apply)
+ {
+ switch (m_spellProto->SpellFamilyName)
+ {
+ case SPELLFAMILY_DRUID:
+ {
+ // Rejuvenation
+ if(m_spellProto->SpellFamilyFlags & 0x0000000000000010LL)
+ {
+ if(Unit* caster = GetCaster())
+ {
+ Unit::AuraList const& classScripts = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator k = classScripts.begin(); k != classScripts.end(); ++k)
+ {
+ int32 tickcount = GetSpellDuration(m_spellProto) / m_spellProto->EffectAmplitude[m_effIndex];
+ switch((*k)->GetModifier()->m_miscvalue)
+ {
+ case 4953: // Increased Rejuvenation Healing - Harold's Rejuvenating Broach Aura
+ case 4415: // Increased Rejuvenation Healing - Idol of Rejuvenation Aura
+ {
+ m_modifier.m_amount += (*k)->GetModifier()->m_amount / tickcount;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void Aura::HandlePeriodicDamage(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+
+ // For prevent double apply bonuses
+ bool loading = (m_target->GetTypeId() == TYPEID_PLAYER && ((Player*)m_target)->GetSession()->PlayerLoading());
+
+ Unit *caster = GetCaster();
+
+ switch (m_spellProto->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ // Pounce Bleed
+ if ( m_spellProto->SpellIconID == 147 && m_spellProto->SpellVisual == 0 )
+ {
+ // $AP*0.18/6 bonus per tick
+ if (apply && !loading && caster)
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 3 / 100);
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_WARRIOR:
+ {
+ // Rend
+ if (m_spellProto->SpellFamilyFlags & 0x0000000000000020LL)
+ {
+ // 0.00743*(($MWB+$mwb)/2+$AP/14*$MWS) bonus per tick
+ if (apply && !loading && caster)
+ {
+ float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK);
+ int32 mws = caster->GetAttackTime(BASE_ATTACK);
+ float mwb_min = caster->GetWeaponDamageRange(BASE_ATTACK,MINDAMAGE);
+ float mwb_max = caster->GetWeaponDamageRange(BASE_ATTACK,MAXDAMAGE);
+ // WARNING! in 3.0 multipler 0.00743f change to 0.6
+ m_modifier.m_amount+=int32(((mwb_min+mwb_max)/2+ap*mws/14000)*0.00743f);
+ }
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_DRUID:
+ {
+ // Rake
+ if (m_spellProto->SpellFamilyFlags & 0x0000000000001000LL)
+ {
+ // $AP*0.06/3 bonus per tick
+ if (apply && !loading && caster)
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 2 / 100);
+ return;
+ }
+ // Lacerate
+ if (m_spellProto->SpellFamilyFlags & 0x000000010000000000LL)
+ {
+ // $AP*0.05/5 bonus per tick
+ if (apply && !loading && caster)
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100);
+ return;
+ }
+ // Rip
+ if (m_spellProto->SpellFamilyFlags & 0x000000000000800000LL)
+ {
+ // $AP * min(0.06*$cp, 0.24)/6 [Yes, there is no difference, wheather 4 or 5 CPs are being used]
+ if (apply && !loading && caster && caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ uint8 cp = ((Player*)caster)->GetComboPoints();
+
+ // Idol of Feral Shadows. Cant be handled as SpellMod in SpellAura:Dummy due its dependency from CPs
+ Unit::AuraList const& dummyAuras = caster->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr)
+ {
+ if((*itr)->GetId()==34241)
+ {
+ m_modifier.m_amount += cp * (*itr)->GetModifier()->m_amount;
+ break;
+ }
+ }
+
+ if (cp > 4) cp = 4;
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * cp / 100);
+ }
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_ROGUE:
+ {
+ // Deadly poison aura state
+ if((m_spellProto->SpellFamilyFlags & 0x10000) && m_spellProto->SpellVisual==5100)
+ {
+ if(apply)
+ m_target->ModifyAuraState(AURA_STATE_DEADLY_POISON,true);
+ else
+ {
+ // current aura already removed, search present of another
+ bool found = false;
+ Unit::AuraList const& auras = m_target->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
+ for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ {
+ SpellEntry const* itr_spell = (*itr)->GetSpellProto();
+ if(itr_spell && itr_spell->SpellFamilyName==SPELLFAMILY_ROGUE && (itr_spell->SpellFamilyFlags & 0x10000) && itr_spell->SpellVisual==5100)
+ {
+ found = true;
+ break;
+ }
+ }
+ // this has been last deadly poison aura
+ if(!found)
+ m_target->ModifyAuraState(AURA_STATE_DEADLY_POISON,false);
+ }
+ return;
+ }
+ // Rupture
+ if (m_spellProto->SpellFamilyFlags & 0x000000000000100000LL)
+ {
+ // Dmg/tick = $AP*min(0.01*$cp, 0.03) [Like Rip: only the first three CP inrease the contribution from AP]
+ if (apply && !loading && caster && caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ uint8 cp = ((Player*)caster)->GetComboPoints();
+ if (cp > 3) cp = 3;
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * cp / 100);
+ }
+ return;
+ }
+ // Garrote
+ if (m_spellProto->SpellFamilyFlags & 0x000000000000000100LL)
+ {
+ // $AP*0.18/6 bonus per tick
+ if (apply && !loading && caster)
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 3 / 100);
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_HUNTER:
+ {
+ // Serpent Sting
+ if (m_spellProto->SpellFamilyFlags & 0x0000000000004000LL)
+ {
+ // $RAP*0.1/5 bonus per tick
+ if (apply && !loading && caster)
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 10 / 500);
+ return;
+ }
+ // Immolation Trap
+ if (m_spellProto->SpellFamilyFlags & 0x0000000000000004LL && m_spellProto->SpellIconID == 678)
+ {
+ // $RAP*0.1/5 bonus per tick
+ if (apply && !loading && caster)
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 10 / 500);
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ // Consecration
+ if (m_spellProto->SpellFamilyFlags & 0x0000000000000020LL)
+ {
+ if (apply && !loading)
+ {
+ if(Unit* caster = GetCaster())
+ {
+ Unit::AuraList const& classScripts = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator k = classScripts.begin(); k != classScripts.end(); ++k)
+ {
+ int32 tickcount = GetSpellDuration(m_spellProto) / m_spellProto->EffectAmplitude[m_effIndex];
+ switch((*k)->GetModifier()->m_miscvalue)
+ {
+ case 5147: // Improved Consecration - Libram of the Eternal Rest
+ {
+ m_modifier.m_amount += (*k)->GetModifier()->m_amount / tickcount;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void Aura::HandlePeriodicDamagePCT(bool apply, bool Real)
+{
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandlePeriodicLeech(bool apply, bool Real)
+{
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandlePeriodicManaLeech(bool apply, bool Real)
+{
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+}
+
+/*********************************************************/
+/*** MODIFY STATS ***/
+/*********************************************************/
+
+/********************************/
+/*** RESISTANCE ***/
+/********************************/
+
+void Aura::HandleAuraModResistanceExclusive(bool apply, bool Real)
+{
+ for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++)
+ {
+ if(m_modifier.m_miscvalue & int32(1<<x))
+ {
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_VALUE, float(m_modifier.m_amount), apply);
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ m_target->ApplyResistanceBuffModsMod(SpellSchools(x),m_positive,m_modifier.m_amount, apply);
+ }
+ }
+}
+
+void Aura::HandleAuraModResistance(bool apply, bool Real)
+{
+ for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++)
+ {
+ if(m_modifier.m_miscvalue & int32(1<<x))
+ {
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet())
+ m_target->ApplyResistanceBuffModsMod(SpellSchools(x),m_positive,m_modifier.m_amount, apply);
+ }
+ }
+
+ // Faerie Fire (druid versions)
+ if( m_spellProto->SpellIconID == 109 &&
+ m_spellProto->SpellFamilyName == SPELLFAMILY_DRUID &&
+ m_spellProto->SpellFamilyFlags & 0x0000000000000400LL )
+ {
+ m_target->ModifyAuraState(AURA_STATE_FAERIE_FIRE,apply);
+ }
+}
+
+void Aura::HandleAuraModBaseResistancePCT(bool apply, bool Real)
+{
+ // only players have base stats
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ {
+ //pets only have base armor
+ if(((Creature*)m_target)->isPet() && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL))
+ {
+ m_target->HandleStatModifier(UNIT_MOD_ARMOR, BASE_PCT, float(m_modifier.m_amount), apply);
+ }
+ }
+ else
+ {
+ for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++)
+ {
+ if(m_modifier.m_miscvalue & int32(1<<x))
+ {
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_PCT, float(m_modifier.m_amount), apply);
+ }
+ }
+ }
+}
+
+void Aura::HandleModResistancePercent(bool apply, bool Real)
+{
+ for(int8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
+ {
+ if(m_modifier.m_miscvalue & int32(1<<i))
+ {
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_PCT, float(m_modifier.m_amount), apply);
+ if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet())
+ {
+ m_target->ApplyResistanceBuffModsPercentMod(SpellSchools(i),true,m_modifier.m_amount, apply);
+ m_target->ApplyResistanceBuffModsPercentMod(SpellSchools(i),false,m_modifier.m_amount, apply);
+ }
+ }
+ }
+}
+
+void Aura::HandleModBaseResistance(bool apply, bool Real)
+{
+ // only players have base stats
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ {
+ //only pets have base stats
+ if(((Creature*)m_target)->isPet() && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL))
+ m_target->HandleStatModifier(UNIT_MOD_ARMOR, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ }
+ else
+ {
+ for(int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
+ if(m_modifier.m_miscvalue & (1<<i))
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ }
+}
+
+/********************************/
+/*** STAT ***/
+/********************************/
+
+void Aura::HandleAuraModStat(bool apply, bool Real)
+{
+ if (m_modifier.m_miscvalue < -2 || m_modifier.m_miscvalue > 4)
+ {
+ sLog.outError("WARNING: Spell %u effect %u have unsupported misc value (%i) for SPELL_AURA_MOD_STAT ",GetId(),GetEffIndex(),m_modifier.m_miscvalue);
+ return;
+ }
+
+ for(int32 i = STAT_STRENGTH; i < MAX_STATS; i++)
+ {
+ // -1 or -2 is all stats ( misc < -2 checked in function beginning )
+ if (m_modifier.m_miscvalue < 0 || m_modifier.m_miscvalue == i)
+ {
+ //m_target->ApplyStatMod(Stats(i), m_modifier.m_amount,apply);
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet())
+ m_target->ApplyStatBuffMod(Stats(i),m_modifier.m_amount,apply);
+ }
+ }
+}
+
+void Aura::HandleModPercentStat(bool apply, bool Real)
+{
+ if (m_modifier.m_miscvalue < -1 || m_modifier.m_miscvalue > 4)
+ {
+ sLog.outError("WARNING: Misc Value for SPELL_AURA_MOD_PERCENT_STAT not valid");
+ return;
+ }
+
+ // only players have base stats
+ if (m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i)
+ {
+ if(m_modifier.m_miscvalue == i || m_modifier.m_miscvalue == -1)
+ {
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, float(m_modifier.m_amount), apply);
+ }
+ }
+}
+
+void Aura::HandleModSpellDamagePercentFromStat(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // Magic damage modifiers implemented in Unit::SpellDamageBonus
+ // This information for client side use only
+ // Recalculate bonus
+ ((Player*)m_target)->UpdateSpellDamageAndHealingBonus();
+}
+
+void Aura::HandleModSpellHealingPercentFromStat(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // Recalculate bonus
+ ((Player*)m_target)->UpdateSpellDamageAndHealingBonus();
+}
+
+void Aura::HandleAuraModDispelResist(bool apply, bool Real)
+{
+ if(!Real || !apply)
+ return;
+
+ if(GetId()==33206)
+ m_target->CastSpell(m_target,44416,true,NULL,this,GetCasterGUID());
+}
+
+void Aura::HandleModSpellDamagePercentFromAttackPower(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // Magic damage modifiers implemented in Unit::SpellDamageBonus
+ // This information for client side use only
+ // Recalculate bonus
+ ((Player*)m_target)->UpdateSpellDamageAndHealingBonus();
+}
+
+void Aura::HandleModSpellHealingPercentFromAttackPower(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // Recalculate bonus
+ ((Player*)m_target)->UpdateSpellDamageAndHealingBonus();
+}
+
+void Aura::HandleModHealingDone(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+ // implemented in Unit::SpellHealingBonus
+ // this information is for client side only
+ ((Player*)m_target)->UpdateSpellDamageAndHealingBonus();
+}
+
+void Aura::HandleModTotalPercentStat(bool apply, bool Real)
+{
+ if (m_modifier.m_miscvalue < -1 || m_modifier.m_miscvalue > 4)
+ {
+ sLog.outError("WARNING: Misc Value for SPELL_AURA_MOD_PERCENT_STAT not valid");
+ return;
+ }
+
+ //save current and max HP before applying aura
+ uint32 curHPValue = m_target->GetHealth();
+ uint32 maxHPValue = m_target->GetMaxHealth();
+
+ for (int32 i = STAT_STRENGTH; i < MAX_STATS; i++)
+ {
+ if(m_modifier.m_miscvalue == i || m_modifier.m_miscvalue == -1)
+ {
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_PCT, float(m_modifier.m_amount), apply);
+ if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet())
+ m_target->ApplyStatPercentBuffMod(Stats(i), m_modifier.m_amount, apply );
+ }
+ }
+
+ //recalculate current HP/MP after applying aura modifications (only for spells with 0x10 flag)
+ if ((m_modifier.m_miscvalue == STAT_STAMINA) && (maxHPValue > 0) && (m_spellProto->Attributes & 0x10))
+ {
+ // newHP = (curHP / maxHP) * newMaxHP = (newMaxHP * curHP) / maxHP -> which is better because no int -> double -> int conversion is needed
+ uint32 newHPValue = (m_target->GetMaxHealth() * curHPValue) / maxHPValue;
+ m_target->SetHealth(newHPValue);
+ }
+}
+
+void Aura::HandleAuraModResistenceOfStatPercent(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(m_modifier.m_miscvalue != SPELL_SCHOOL_MASK_NORMAL)
+ {
+ // support required adding replace UpdateArmor by loop by UpdateResistence at intelect update
+ // and include in UpdateResistence same code as in UpdateArmor for aura mod apply.
+ sLog.outError("Aura SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT(182) need adding support for non-armor resistences!");
+ return;
+ }
+
+ // Recalculate Armor
+ m_target->UpdateArmor();
+}
+
+/********************************/
+/*** HEAL & ENERGIZE ***/
+/********************************/
+void Aura::HandleAuraModTotalHealthPercentRegen(bool apply, bool Real)
+{
+ /*
+ Need additional checking for auras who reduce or increase healing, magic effect like Dumpen Magic,
+ so this aura not fully working.
+ */
+ if(apply)
+ {
+ if(!m_target->isAlive())
+ return;
+
+ if((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && !m_target->IsSitState())
+ m_target->SetStandState(PLAYER_STATE_SIT);
+
+ if(m_periodicTimer <= 0)
+ {
+ m_periodicTimer += m_modifier.periodictime;
+
+ if(m_target->GetHealth() < m_target->GetMaxHealth())
+ {
+ // PeriodicTick can cast triggered spells with stats changes
+ PeriodicTick();
+ }
+ }
+ }
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandleAuraModTotalManaPercentRegen(bool apply, bool Real)
+{
+ if((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && apply && !m_target->IsSitState())
+ m_target->SetStandState(PLAYER_STATE_SIT);
+ if(apply)
+ {
+ if(m_modifier.periodictime == 0)
+ m_modifier.periodictime = 1000;
+ if(m_periodicTimer <= 0 && m_target->getPowerType() == POWER_MANA)
+ {
+ m_periodicTimer += m_modifier.periodictime;
+
+ if(m_target->GetPower(POWER_MANA) < m_target->GetMaxPower(POWER_MANA))
+ {
+ // PeriodicTick can cast triggered spells with stats changes
+ PeriodicTick();
+ }
+ }
+ }
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandleModRegen(bool apply, bool Real) // eating
+{
+ if(apply)
+ {
+ if(!m_target->isAlive())
+ return;
+
+ if ((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && !m_target->IsSitState())
+ m_target->SetStandState(PLAYER_STATE_SIT);
+
+ if(m_periodicTimer <= 0)
+ {
+ m_periodicTimer += 5000;
+ int32 gain = m_target->ModifyHealth(m_modifier.m_amount);
+ Unit *caster = GetCaster();
+ if (caster)
+ {
+ SpellEntry const *spellProto = GetSpellProto();
+ if (spellProto)
+ m_target->getHostilRefManager().threatAssist(caster, float(gain) * 0.5f, spellProto);
+ }
+ }
+ }
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandleModPowerRegen(bool apply, bool Real) // drinking
+{
+ if ((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && apply && !m_target->IsSitState())
+ m_target->SetStandState(PLAYER_STATE_SIT);
+
+ if(apply && m_periodicTimer <= 0)
+ {
+ m_periodicTimer += 2000;
+
+ Powers pt = m_target->getPowerType();
+ if(int32(pt) != m_modifier.m_miscvalue)
+ return;
+
+ if ( GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED )
+ {
+ // eating anim
+ m_target->HandleEmoteCommand(EMOTE_ONESHOT_EAT);
+ }
+ else if( GetId() == 20577 )
+ {
+ // cannibalize anim
+ m_target->HandleEmoteCommand(398);
+ }
+
+ // Warrior talent, gain 1 rage every 3 seconds while in combat
+ if(pt == POWER_RAGE && m_target->isInCombat())
+ {
+ m_target->ModifyPower(pt, m_modifier.m_amount*10/17);
+ m_periodicTimer += 1000;
+ }
+ }
+ m_isPeriodic = apply;
+ if (Real && m_target->GetTypeId() == TYPEID_PLAYER && m_modifier.m_miscvalue == POWER_MANA)
+ ((Player*)m_target)->UpdateManaRegen();
+}
+
+void Aura::HandleModPowerRegenPCT(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if (m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // Update manaregen value
+ if (m_modifier.m_miscvalue == POWER_MANA)
+ ((Player*)m_target)->UpdateManaRegen();
+}
+
+void Aura::HandleModManaRegen(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if (m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ //Note: an increase in regen does NOT cause threat.
+ ((Player*)m_target)->UpdateManaRegen();
+}
+
+void Aura::HandleComprehendLanguage(bool apply, bool Real)
+{
+ if(apply)
+ m_target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_COMPREHEND_LANG);
+ else
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_COMPREHEND_LANG);
+}
+
+void Aura::HandleAuraModIncreaseHealth(bool apply, bool Real)
+{
+ // Special case with temporary increase max/current health
+ switch(GetId())
+ {
+ case 12976: // Warrior Last Stand triggered spell
+ case 28726: // Nightmare Seed ( Nightmare Seed )
+ case 34511: // Valor (Bulwark of Kings, Bulwark of the Ancient Kings)
+ case 44055: // Tremendous Fortitude (Battlemaster's Alacrity)
+ {
+ if(Real)
+ {
+ if(apply)
+ {
+ m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ m_target->ModifyHealth(m_modifier.m_amount);
+ }
+ else
+ {
+ if (int32(m_target->GetHealth()) > m_modifier.m_amount)
+ m_target->ModifyHealth(-m_modifier.m_amount);
+ else
+ m_target->SetHealth(1);
+ m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ }
+ }
+ return;
+ }
+ }
+
+ // generic case
+ m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraModIncreaseMaxHealth(bool apply, bool Real)
+{
+ uint32 oldhealth = m_target->GetHealth();
+ double healthPercentage = (double)oldhealth / (double)m_target->GetMaxHealth();
+
+ m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+
+ // refresh percentage
+ if(oldhealth > 0)
+ {
+ uint32 newhealth = uint32(ceil((double)m_target->GetMaxHealth() * healthPercentage));
+ if(newhealth==0)
+ newhealth = 1;
+
+ m_target->SetHealth(newhealth);
+ }
+}
+
+void Aura::HandleAuraModIncreaseEnergy(bool apply, bool Real)
+{
+ Powers powerType = m_target->getPowerType();
+ if(int32(powerType) != m_modifier.m_miscvalue)
+ return;
+
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_POWER_START + powerType), TOTAL_VALUE, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraModIncreaseEnergyPercent(bool apply, bool Real)
+{
+ Powers powerType = m_target->getPowerType();
+ if(int32(powerType) != m_modifier.m_miscvalue)
+ return;
+
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_POWER_START + powerType), TOTAL_PCT, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraModIncreaseHealthPercent(bool apply, bool Real)
+{
+ //m_target->ApplyMaxHealthPercentMod(m_modifier.m_amount,apply);
+ m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_PCT, float(m_modifier.m_amount), apply);
+}
+
+/********************************/
+/*** FIGHT ***/
+/********************************/
+
+void Aura::HandleAuraModParryPercent(bool apply, bool Real)
+{
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ ((Player*)m_target)->UpdateParryPercentage();
+}
+
+void Aura::HandleAuraModDodgePercent(bool apply, bool Real)
+{
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ ((Player*)m_target)->UpdateDodgePercentage();
+ //sLog.outError("BONUS DODGE CHANCE: + %f", float(m_modifier.m_amount));
+}
+
+void Aura::HandleAuraModBlockPercent(bool apply, bool Real)
+{
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ ((Player*)m_target)->UpdateBlockPercentage();
+ //sLog.outError("BONUS BLOCK CHANCE: + %f", float(m_modifier.m_amount));
+}
+
+void Aura::HandleAuraModRegenInterrupt(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ ((Player*)m_target)->UpdateManaRegen();
+}
+
+void Aura::HandleAuraModCritPercent(bool apply, bool Real)
+{
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ // apply item specific bonuses for already equipped weapon
+ if(Real)
+ {
+ for(int i = 0; i < MAX_ATTACK; ++i)
+ if(Item* pItem = ((Player*)m_target)->GetWeaponForAttack(WeaponAttackType(i)))
+ ((Player*)m_target)->_ApplyWeaponDependentAuraCritMod(pItem,WeaponAttackType(i),this,apply);
+ }
+
+ // mods must be applied base at equipped weapon class and subclass comparison
+ // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask
+ // m_modifier.m_miscvalue comparison with item generated damage types
+
+ if (GetSpellProto()->EquippedItemClass == -1)
+ {
+ ((Player*)m_target)->HandleBaseModValue(CRIT_PERCENTAGE, FLAT_MOD, float (m_modifier.m_amount), apply);
+ ((Player*)m_target)->HandleBaseModValue(OFFHAND_CRIT_PERCENTAGE, FLAT_MOD, float (m_modifier.m_amount), apply);
+ ((Player*)m_target)->HandleBaseModValue(RANGED_CRIT_PERCENTAGE, FLAT_MOD, float (m_modifier.m_amount), apply);
+ }
+ else
+ {
+ // done in Player::_ApplyWeaponDependentAuraMods
+ }
+}
+
+void Aura::HandleModHitChance(bool apply, bool Real)
+{
+ m_target->m_modMeleeHitChance += apply ? m_modifier.m_amount : (-m_modifier.m_amount);
+ m_target->m_modRangedHitChance += apply ? m_modifier.m_amount : (-m_modifier.m_amount);
+}
+
+void Aura::HandleModSpellHitChance(bool apply, bool Real)
+{
+ m_target->m_modSpellHitChance += apply ? m_modifier.m_amount: (-m_modifier.m_amount);
+}
+
+void Aura::HandleModSpellCritChance(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)m_target)->UpdateAllSpellCritChances();
+ }
+ else
+ {
+ m_target->m_baseSpellCritChance += apply ? m_modifier.m_amount:(-m_modifier.m_amount);
+ }
+}
+
+void Aura::HandleModSpellCritChanceShool(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ for(int school = SPELL_SCHOOL_NORMAL; school < MAX_SPELL_SCHOOL; ++school)
+ if (m_modifier.m_miscvalue & (1<<school))
+ ((Player*)m_target)->UpdateSpellCritChance(school);
+}
+
+/********************************/
+/*** ATTACK SPEED ***/
+/********************************/
+
+void Aura::HandleModCastingSpeed(bool apply, bool Real)
+{
+ m_target->ApplyCastTimePercentMod(m_modifier.m_amount,apply);
+}
+
+void Aura::HandleModMeleeRangedSpeedPct(bool apply, bool Real)
+{
+ m_target->ApplyAttackTimePercentMod(BASE_ATTACK,m_modifier.m_amount,apply);
+ m_target->ApplyAttackTimePercentMod(OFF_ATTACK,m_modifier.m_amount,apply);
+ m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_modifier.m_amount, apply);
+}
+
+void Aura::HandleModCombatSpeedPct(bool apply, bool Real)
+{
+ m_target->ApplyCastTimePercentMod(m_modifier.m_amount,apply);
+ m_target->ApplyAttackTimePercentMod(BASE_ATTACK,m_modifier.m_amount,apply);
+ m_target->ApplyAttackTimePercentMod(OFF_ATTACK,m_modifier.m_amount,apply);
+ m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_modifier.m_amount, apply);
+}
+
+void Aura::HandleModAttackSpeed(bool apply, bool Real)
+{
+ if(!m_target->isAlive() )
+ return;
+
+ m_target->ApplyAttackTimePercentMod(BASE_ATTACK,m_modifier.m_amount,apply);
+}
+
+void Aura::HandleHaste(bool apply, bool Real)
+{
+ m_target->ApplyAttackTimePercentMod(BASE_ATTACK, m_modifier.m_amount,apply);
+ m_target->ApplyAttackTimePercentMod(OFF_ATTACK, m_modifier.m_amount,apply);
+ m_target->ApplyAttackTimePercentMod(RANGED_ATTACK,m_modifier.m_amount,apply);
+}
+
+void Aura::HandleAuraModRangedHaste(bool apply, bool Real)
+{
+ m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_modifier.m_amount, apply);
+}
+
+void Aura::HandleRangedAmmoHaste(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+ m_target->ApplyAttackTimePercentMod(RANGED_ATTACK,m_modifier.m_amount, apply);
+}
+
+/********************************/
+/*** ATTACK POWER ***/
+/********************************/
+
+void Aura::HandleAuraModAttackPower(bool apply, bool Real)
+{
+ m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraModRangedAttackPower(bool apply, bool Real)
+{
+ if((m_target->getClassMask() & CLASSMASK_WAND_USERS)!=0)
+ return;
+
+ m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraAttackPowerAttacker(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+ Unit *caster = GetCaster();
+
+ if (!caster)
+ return;
+
+ // Hunter's Mark
+ if (m_spellProto->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellProto->SpellFamilyFlags & 0x0000000000000400LL)
+ {
+ // Check Improved Hunter's Mark bonus on caster
+ Unit::AuraList const& mOverrideClassScript = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ // mproved Hunter's Mark script from 5236 to 5240
+ if (mod->m_miscvalue >= 5236 && mod->m_miscvalue <= 5240)
+ {
+ // Get amount of ranged bonus for this spell..
+ int32 ranged_bonus = caster->CalculateSpellDamage(m_spellProto, 1, m_spellProto->EffectBasePoints[1], m_target);
+ // Set melee attack power bonus % from ranged depends from Improved mask aura
+ m_modifier.m_amount = mod->m_amount * ranged_bonus / 100;
+ m_currentBasePoints = m_modifier.m_amount;
+ break;
+ }
+ }
+ return;
+ }
+}
+
+void Aura::HandleAuraModAttackPowerPercent(bool apply, bool Real)
+{
+ //UNIT_FIELD_ATTACK_POWER_MULTIPLIER = multiplier - 1
+ m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraModRangedAttackPowerPercent(bool apply, bool Real)
+{
+ if((m_target->getClassMask() & CLASSMASK_WAND_USERS)!=0)
+ return;
+
+ //UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER = multiplier - 1
+ m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_PCT, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraModRangedAttackPowerOfStatPercent(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER && (m_target->getClassMask() & CLASSMASK_WAND_USERS)!=0)
+ return;
+
+ if(m_modifier.m_miscvalue != STAT_INTELLECT)
+ {
+ // support required adding UpdateAttackPowerAndDamage calls at stat update
+ sLog.outError("Aura SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT (212) need support non-intelect stats!");
+ return;
+ }
+
+ // Recalculate bonus
+ ((Player*)m_target)->UpdateAttackPowerAndDamage(true);
+}
+
+/********************************/
+/*** DAMAGE BONUS ***/
+/********************************/
+void Aura::HandleModDamageDone(bool apply, bool Real)
+{
+ // apply item specific bonuses for already equipped weapon
+ if(Real && m_target->GetTypeId()==TYPEID_PLAYER)
+ {
+ for(int i = 0; i < MAX_ATTACK; ++i)
+ if(Item* pItem = ((Player*)m_target)->GetWeaponForAttack(WeaponAttackType(i)))
+ ((Player*)m_target)->_ApplyWeaponDependentAuraDamageMod(pItem,WeaponAttackType(i),this,apply);
+ }
+
+ // m_modifier.m_miscvalue is bitmask of spell schools
+ // 1 ( 0-bit ) - normal school damage (SPELL_SCHOOL_MASK_NORMAL)
+ // 126 - full bitmask all magic damages (SPELL_SCHOOL_MASK_MAGIC) including wands
+ // 127 - full bitmask any damages
+ //
+ // mods must be applied base at equipped weapon class and subclass comparison
+ // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask
+ // m_modifier.m_miscvalue comparison with item generated damage types
+
+ if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL) != 0)
+ {
+ // apply generic physical damage bonuses including wand case
+ if (GetSpellProto()->EquippedItemClass == -1 || m_target->GetTypeId() != TYPEID_PLAYER)
+ {
+ m_target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ m_target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ }
+ else
+ {
+ // done in Player::_ApplyWeaponDependentAuraMods
+ }
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ if(m_positive)
+ m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS,m_modifier.m_amount,apply);
+ else
+ m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG,m_modifier.m_amount,apply);
+ }
+ }
+
+ // Skip non magic case for speedup
+ if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_MAGIC) == 0)
+ return;
+
+ if( GetSpellProto()->EquippedItemClass != -1 || GetSpellProto()->EquippedItemInventoryTypeMask != 0 )
+ {
+ // wand magic case (skip generic to all item spell bonuses)
+ // done in Player::_ApplyWeaponDependentAuraMods
+
+ // Skip item specific requirements for not wand magic damage
+ return;
+ }
+
+ // Magic damage modifiers implemented in Unit::SpellDamageBonus
+ // This information for client side use only
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ if(m_positive)
+ {
+ for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++)
+ {
+ if((m_modifier.m_miscvalue & (1<<i)) != 0)
+ m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i,m_modifier.m_amount,apply);
+ }
+ }
+ else
+ {
+ for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++)
+ {
+ if((m_modifier.m_miscvalue & (1<<i)) != 0)
+ m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG+i,m_modifier.m_amount,apply);
+ }
+ }
+ Pet* pet = m_target->GetPet();
+ if(pet)
+ pet->UpdateAttackPowerAndDamage();
+ }
+}
+
+void Aura::HandleModDamagePercentDone(bool apply, bool Real)
+{
+ sLog.outDebug("AURA MOD DAMAGE type:%u negative:%u", m_modifier.m_miscvalue, m_positive ? 0 : 1);
+
+ // apply item specific bonuses for already equipped weapon
+ if(Real && m_target->GetTypeId()==TYPEID_PLAYER)
+ {
+ for(int i = 0; i < MAX_ATTACK; ++i)
+ if(Item* pItem = ((Player*)m_target)->GetWeaponForAttack(WeaponAttackType(i)))
+ ((Player*)m_target)->_ApplyWeaponDependentAuraDamageMod(pItem,WeaponAttackType(i),this,apply);
+ }
+
+ // m_modifier.m_miscvalue is bitmask of spell schools
+ // 1 ( 0-bit ) - normal school damage (SPELL_SCHOOL_MASK_NORMAL)
+ // 126 - full bitmask all magic damages (SPELL_SCHOOL_MASK_MAGIC) including wand
+ // 127 - full bitmask any damages
+ //
+ // mods must be applied base at equipped weapon class and subclass comparison
+ // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask
+ // m_modifier.m_miscvalue comparison with item generated damage types
+
+ if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL) != 0)
+ {
+ // apply generic physical damage bonuses including wand case
+ if (GetSpellProto()->EquippedItemClass == -1 || m_target->GetTypeId() != TYPEID_PLAYER)
+ {
+ m_target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, float(m_modifier.m_amount), apply);
+ m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(m_modifier.m_amount), apply);
+ m_target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_PCT, float(m_modifier.m_amount), apply);
+ }
+ else
+ {
+ // done in Player::_ApplyWeaponDependentAuraMods
+ }
+ // For show in client
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ m_target->ApplyModSignedFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT,m_modifier.m_amount/100.0f,apply);
+ }
+
+ // Skip non magic case for speedup
+ if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_MAGIC) == 0)
+ return;
+
+ if( GetSpellProto()->EquippedItemClass != -1 || GetSpellProto()->EquippedItemInventoryTypeMask != 0 )
+ {
+ // wand magic case (skip generic to all item spell bonuses)
+ // done in Player::_ApplyWeaponDependentAuraMods
+
+ // Skip item specific requirements for not wand magic damage
+ return;
+ }
+
+ // Magic damage percent modifiers implemented in Unit::SpellDamageBonus
+ // Send info to client
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
+ m_target->ApplyModSignedFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT+i,m_modifier.m_amount/100.0f,apply);
+}
+
+void Aura::HandleModOffhandDamagePercent(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ sLog.outDebug("AURA MOD OFFHAND DAMAGE");
+
+ m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(m_modifier.m_amount), apply);
+}
+
+/********************************/
+/*** POWER COST ***/
+/********************************/
+
+void Aura::HandleModPowerCostPCT(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ float amount = m_modifier.m_amount/100.0f;
+ for(int i = 0; i < MAX_SPELL_SCHOOL; ++i)
+ if(m_modifier.m_miscvalue & (1<<i))
+ m_target->ApplyModSignedFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+i,amount,apply);
+}
+
+void Aura::HandleModPowerCost(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ for(int i = 0; i < MAX_SPELL_SCHOOL; ++i)
+ if(m_modifier.m_miscvalue & (1<<i))
+ m_target->ApplyModInt32Value(UNIT_FIELD_POWER_COST_MODIFIER+i,m_modifier.m_amount,apply);
+}
+
+/*********************************************************/
+/*** OTHERS ***/
+/*********************************************************/
+
+void Aura::HandleShapeshiftBoosts(bool apply)
+{
+ uint32 spellId = 0;
+ uint32 spellId2 = 0;
+ uint32 HotWSpellId = 0;
+
+ switch(GetModifier()->m_miscvalue)
+ {
+ case FORM_CAT:
+ spellId = 3025;
+ HotWSpellId = 24900;
+ break;
+ case FORM_TREE:
+ spellId = 5420;
+ break;
+ case FORM_TRAVEL:
+ spellId = 5419;
+ break;
+ case FORM_AQUA:
+ spellId = 5421;
+ break;
+ case FORM_BEAR:
+ spellId = 1178;
+ spellId2 = 21178;
+ HotWSpellId = 24899;
+ break;
+ case FORM_DIREBEAR:
+ spellId = 9635;
+ spellId2 = 21178;
+ HotWSpellId = 24899;
+ break;
+ case FORM_BATTLESTANCE:
+ spellId = 21156;
+ break;
+ case FORM_DEFENSIVESTANCE:
+ spellId = 7376;
+ break;
+ case FORM_BERSERKERSTANCE:
+ spellId = 7381;
+ break;
+ case FORM_MOONKIN:
+ spellId = 24905;
+ // aura from effect trigger spell
+ spellId2 = 24907;
+ break;
+ case FORM_FLIGHT:
+ spellId = 33948;
+ break;
+ case FORM_FLIGHT_EPIC:
+ spellId = 40122;
+ spellId2 = 40121;
+ break;
+ case FORM_SPIRITOFREDEMPTION:
+ spellId = 27792;
+ spellId2 = 27795; // must be second, this important at aura remove to prevent to early iterator invalidation.
+ break;
+ case FORM_GHOSTWOLF:
+ case FORM_AMBIENT:
+ case FORM_GHOUL:
+ case FORM_SHADOW:
+ case FORM_STEALTH:
+ case FORM_CREATURECAT:
+ case FORM_CREATUREBEAR:
+ spellId = 0;
+ break;
+ }
+
+ uint32 form = GetModifier()->m_miscvalue-1;
+
+ if(apply)
+ {
+ if (spellId) m_target->CastSpell(m_target, spellId, true, NULL, this );
+ if (spellId2) m_target->CastSpell(m_target, spellId2, true, NULL, this);
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ const PlayerSpellMap& sp_list = ((Player *)m_target)->GetSpellMap();
+ for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr)
+ {
+ if(itr->second->state == PLAYERSPELL_REMOVED) continue;
+ if(itr->first==spellId || itr->first==spellId2) continue;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
+ if (!spellInfo || !(spellInfo->Attributes & ((1<<6) | (1<<7)))) continue;
+ if (spellInfo->Stances & (1<<form))
+ m_target->CastSpell(m_target, itr->first, true, NULL, this);
+ }
+ //LotP
+ if (((Player*)m_target)->HasSpell(17007))
+ {
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(24932);
+ if (spellInfo && spellInfo->Stances & (1<<form))
+ m_target->CastSpell(m_target, 24932, true, NULL, this);
+ }
+ // HotW
+ if (HotWSpellId)
+ {
+ Unit::AuraList const& mModTotalStatPct = m_target->GetAurasByType(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE);
+ for(Unit::AuraList::const_iterator i = mModTotalStatPct.begin(); i != mModTotalStatPct.end(); ++i)
+ {
+ if ((*i)->GetSpellProto()->SpellIconID == 240 && (*i)->GetModifier()->m_miscvalue == 3)
+ {
+ int32 HotWMod = (*i)->GetModifier()->m_amount;
+ if(GetModifier()->m_miscvalue == FORM_CAT)
+ HotWMod /= 2;
+
+ m_target->CastCustomSpell(m_target, HotWSpellId, &HotWMod, NULL, NULL, true, NULL, this);
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ m_target->RemoveAurasDueToSpell(spellId);
+ m_target->RemoveAurasDueToSpell(spellId2);
+
+ Unit::AuraMap& tAuras = m_target->GetAuras();
+ for (Unit::AuraMap::iterator itr = tAuras.begin(); itr != tAuras.end();)
+ {
+ if (itr->second->IsRemovedOnShapeLost())
+ {
+ m_target->RemoveAurasDueToSpell(itr->second->GetId());
+ itr = tAuras.begin();
+ }
+ else
+ {
+ ++itr;
+ }
+ }
+ }
+
+ /*double healthPercentage = (double)m_target->GetHealth() / (double)m_target->GetMaxHealth();
+ m_target->SetHealth(uint32(ceil((double)m_target->GetMaxHealth() * healthPercentage)));*/
+}
+
+void Aura::HandleAuraEmpathy(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_UNIT)
+ return;
+
+ CreatureInfo const * ci = objmgr.GetCreatureTemplate(m_target->GetEntry());
+ if(ci && ci->type == CREATURE_TYPE_BEAST)
+ {
+ m_target->ApplyModUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_SPECIALINFO, apply);
+ }
+}
+
+void Aura::HandleAuraUntrackable(bool apply, bool Real)
+{
+ if(apply)
+ m_target->SetFlag(UNIT_FIELD_BYTES_1, PLAYER_STATE_FLAG_UNTRACKABLE);
+ else
+ m_target->RemoveFlag(UNIT_FIELD_BYTES_1, PLAYER_STATE_FLAG_UNTRACKABLE);
+}
+
+void Aura::HandleAuraModPacify(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(apply)
+ m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED);
+ else
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED);
+}
+
+void Aura::HandleAuraModPacifyAndSilence(bool apply, bool Real)
+{
+ HandleAuraModPacify(apply,Real);
+ HandleAuraModSilence(apply,Real);
+}
+
+void Aura::HandleAuraGhost(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(apply)
+ {
+ m_target->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST);
+ }
+ else
+ {
+ m_target->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST);
+ }
+}
+
+void Aura::HandleAuraAllowFlight(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ // allow fly
+ WorldPacket data;
+ if(apply)
+ data.Initialize(SMSG_MOVE_SET_CAN_FLY, 12);
+ else
+ data.Initialize(SMSG_MOVE_UNSET_CAN_FLY, 12);
+ data.append(m_target->GetPackGUID());
+ data << uint32(0); // unk
+ m_target->SendMessageToSet(&data, true);
+}
+
+void Aura::HandleModRating(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating)
+ if (m_modifier.m_miscvalue & (1 << rating))
+ ((Player*)m_target)->ApplyRatingMod(CombatRating(rating), m_modifier.m_amount, apply);
+}
+
+void Aura::HandleForceMoveForward(bool apply, bool Real)
+{
+ if(!Real || m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+ if(apply)
+ m_target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FORCE_MOVE);
+ else
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FORCE_MOVE);
+}
+
+void Aura::HandleAuraModExpertise(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ ((Player*)m_target)->UpdateExpertise(BASE_ATTACK);
+ ((Player*)m_target)->UpdateExpertise(OFF_ATTACK);
+}
+
+void Aura::HandleModTargetResistance(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+ // applied to damage as HandleNoImmediateEffect in Unit::CalcAbsorbResist and Unit::CalcArmorReducedDamage
+
+ // show armor penetration
+ if (m_target->GetTypeId() == TYPEID_PLAYER && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL))
+ m_target->ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE,m_modifier.m_amount, apply);
+
+ // show as spell penetration only full spell penetration bonuses (all resistances except armor and holy
+ if (m_target->GetTypeId() == TYPEID_PLAYER && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_SPELL)==SPELL_SCHOOL_MASK_SPELL)
+ m_target->ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE,m_modifier.m_amount, apply);
+}
+
+//HandleNoImmediateEffect auras implementation to support new stat system
+void Aura::HandleAuraHealing(bool apply, bool Real)
+{
+ //m_target->HandleStatModifier(UNIT_MOD_HEALING, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraHealingPct(bool apply, bool Real)
+{
+ //m_target->HandleStatModifier(UNIT_MOD_HEALING, TOTAL_PCT, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleShieldBlockValue(bool apply, bool Real)
+{
+ BaseModType modType = FLAT_MOD;
+ if(m_modifier.m_auraname == SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT)
+ modType = PCT_MOD;
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)m_target)->HandleBaseModValue(SHIELD_BLOCK_VALUE, modType, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraRetainComboPoints(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *target = (Player*)m_target;
+
+ // combo points was added in SPELL_EFFECT_ADD_COMBO_POINTS handler
+ // remove only if aura expire by time (in case combo points amount change aura removed without combo points lost)
+ if( !apply && m_duration==0 && target->GetComboTarget())
+ if(Unit* unit = ObjectAccessor::GetUnit(*m_target,target->GetComboTarget()))
+ target->AddComboPoints(unit, -m_modifier.m_amount);
+}
+
+void Aura::HandleModUnattackable( bool Apply, bool Real )
+{
+ if(Real && Apply)
+ m_target->CombatStop();
+
+ m_target->ApplyModFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE,Apply);
+}
+
+void Aura::HandleSpiritOfRedemption( bool apply, bool Real )
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ // prepare spirit state
+ if(apply)
+ {
+ if(m_target->GetTypeId()==TYPEID_PLAYER)
+ {
+ // disable breath/etc timers
+ ((Player*)m_target)->StopMirrorTimers();
+
+ // set stand state (expected in this form)
+ if(!m_target->IsStandState())
+ m_target->SetStandState(PLAYER_STATE_NONE);
+ }
+
+ m_target->SetHealth(1);
+ }
+ // die at aura end
+ else
+ m_target->DealDamage(m_target, m_target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, GetSpellProto(), false);
+}
+
+void Aura::CleanupTriggeredSpells()
+{
+ uint32 tSpellId = m_spellProto->EffectTriggerSpell[GetEffIndex()];
+ if(!tSpellId)
+ return;
+
+ SpellEntry const* tProto = sSpellStore.LookupEntry(tSpellId);
+ if(!tProto)
+ return;
+
+ if(GetSpellDuration(tProto) != -1)
+ return;
+
+ // needed for spell 43680, maybe others
+ // TODO: is there a spell flag, which can solve this in a more sophisticated way?
+ if(m_spellProto->EffectApplyAuraName[GetEffIndex()] == SPELL_AURA_PERIODIC_TRIGGER_SPELL &&
+ GetSpellDuration(m_spellProto) == m_spellProto->EffectAmplitude[GetEffIndex()])
+ return;
+ m_target->RemoveAurasDueToSpell(tSpellId);
+}
+
+void Aura::HandleAuraPowerBurn(bool apply, bool Real)
+{
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandleSchoolAbsorb(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ // prevent double apply bonuses
+ if(apply && (m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading()))
+ {
+ if(Unit* caster = GetCaster())
+ {
+ float DoneActualBenefit = 0.0f;
+ switch(m_spellProto->SpellFamilyName)
+ {
+ case SPELLFAMILY_PRIEST:
+ if(m_spellProto->SpellFamilyFlags == 0x1) //PW:S
+ {
+ //+30% from +healing bonus
+ DoneActualBenefit = caster->SpellBaseHealingBonus(GetSpellSchoolMask(m_spellProto)) * 0.3f;
+ break;
+ }
+ break;
+ case SPELLFAMILY_MAGE:
+ if(m_spellProto->SpellFamilyFlags == 0x80100 || m_spellProto->SpellFamilyFlags == 0x8 || m_spellProto->SpellFamilyFlags == 0x100000000LL)
+ {
+ //frost ward, fire ward, ice barrier
+ //+10% from +spd bonus
+ DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.1f;
+ break;
+ }
+ break;
+ case SPELLFAMILY_WARLOCK:
+ if(m_spellProto->SpellFamilyFlags == 0x00)
+ {
+ //shadow ward
+ //+10% from +spd bonus
+ DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.1f;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellProto());
+
+ m_modifier.m_amount += (int32)DoneActualBenefit;
+ }
+ }
+}
+
+void Aura::PeriodicTick()
+{
+ if(!m_target->isAlive())
+ return;
+
+ switch(m_modifier.m_auraname)
+ {
+ case SPELL_AURA_PERIODIC_DAMAGE:
+ case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
+ {
+ Unit *pCaster = GetCaster();
+ if(!pCaster)
+ return;
+
+ if( GetSpellProto()->Effect[GetEffIndex()]==SPELL_EFFECT_PERSISTENT_AREA_AURA &&
+ pCaster->SpellHitResult(m_target,GetSpellProto(),false)!=SPELL_MISS_NONE)
+ return;
+
+ // Check for immune (not use charges)
+ if(m_target->IsImmunedToDamage(GetSpellSchoolMask(GetSpellProto())))
+ return;
+
+ // some auras remove at specific health level or more
+ if(m_modifier.m_auraname==SPELL_AURA_PERIODIC_DAMAGE)
+ {
+ switch(GetId())
+ {
+ case 43093: case 31956: case 38801:
+ case 35321: case 38363: case 39215:
+ if(m_target->GetHealth() == m_target->GetMaxHealth() )
+ {
+ m_target->RemoveAurasDueToSpell(GetId());
+ return;
+ }
+ break;
+ case 38772:
+ {
+ uint32 percent =
+ GetEffIndex() < 2 && GetSpellProto()->Effect[GetEffIndex()]==SPELL_EFFECT_DUMMY ?
+ pCaster->CalculateSpellDamage(GetSpellProto(),GetEffIndex()+1,GetSpellProto()->EffectBasePoints[GetEffIndex()+1],m_target) :
+ 100;
+ if(m_target->GetHealth()*100 >= m_target->GetMaxHealth()*percent )
+ {
+ m_target->RemoveAurasDueToSpell(GetId());
+ return;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ uint32 absorb=0;
+ uint32 resist=0;
+ CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL );
+
+ // ignore non positive values (can be result apply spellmods to aura damage
+ uint32 amount = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0;
+
+ uint32 pdamage;
+
+ if(m_modifier.m_auraname == SPELL_AURA_PERIODIC_DAMAGE)
+ {
+ pdamage = amount;
+
+ // Calculate armor mitigation if it is a physical spell
+ // But not for bleed mechanic spells
+ if ( GetSpellSchoolMask(GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL &&
+ GetEffectMechanic(GetSpellProto(), m_effIndex) != MECHANIC_BLEED)
+ {
+ uint32 pdamageReductedArmor = pCaster->CalcArmorReducedDamage(m_target, pdamage);
+ cleanDamage.damage += pdamage - pdamageReductedArmor;
+ pdamage = pdamageReductedArmor;
+ }
+
+ pdamage = pCaster->SpellDamageBonus(m_target,GetSpellProto(),pdamage,DOT);
+
+ // Curse of Agony damage-per-tick calculation
+ if (GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 0x0000000000000400LL) && GetSpellProto()->SpellIconID==544)
+ {
+ // 1..4 ticks, 1/2 from normal tick damage
+ if (m_duration>=((m_maxduration-m_modifier.periodictime)*2/3))
+ pdamage = pdamage/2;
+ // 9..12 ticks, 3/2 from normal tick damage
+ else if(m_duration<((m_maxduration-m_modifier.periodictime)/3))
+ pdamage += (pdamage+1)/2; // +1 prevent 0.5 damage possible lost at 1..4 ticks
+ // 5..8 ticks have normal tick damage
+ }
+ }
+ else
+ pdamage = uint32(m_target->GetMaxHealth()*amount/100);
+
+ //As of 2.2 resilience reduces damage from DoT ticks as much as the chance to not be critically hit
+ // Reduce dot damage from resilience for players
+ if (m_target->GetTypeId()==TYPEID_PLAYER)
+ pdamage-=((Player*)m_target)->GetDotDamageReduction(pdamage);
+
+ pCaster->CalcAbsorbResist(m_target, GetSpellSchoolMask(GetSpellProto()), DOT, pdamage, &absorb, &resist);
+
+ sLog.outDetail("PeriodicTick: %u (TypeId: %u) attacked %u (TypeId: %u) for %u dmg inflicted by %u abs is %u",
+ GetCasterGUID(), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId(),absorb);
+
+ WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size
+ data.append(m_target->GetPackGUID());
+ data.appendPackGUID(GetCasterGUID());
+ data << uint32(GetId());
+ data << uint32(1);
+ data << uint32(m_modifier.m_auraname);
+ data << (uint32)pdamage;
+ data << (uint32)GetSpellSchoolMask(GetSpellProto()); // will be mask in 2.4.x
+ data << (uint32)absorb;
+ data << (uint32)resist;
+ m_target->SendMessageToSet(&data,true);
+
+ Unit* target = m_target; // aura can be deleted in DealDamage
+ SpellEntry const* spellProto = GetSpellProto();
+
+ pCaster->DealDamage(m_target, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), &cleanDamage, DOT, GetSpellSchoolMask(GetSpellProto()), GetSpellProto(), true);
+
+ // DO NOT ACCESS MEMBERS OF THE AURA FROM NOW ON (DealDamage can delete aura)
+
+ pCaster->ProcDamageAndSpell(target, PROC_FLAG_HIT_SPELL, PROC_FLAG_TAKE_DAMAGE, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), GetSpellSchoolMask(spellProto), spellProto);
+ break;
+ }
+ case SPELL_AURA_PERIODIC_LEECH:
+ {
+ Unit *pCaster = GetCaster();
+ if(!pCaster)
+ return;
+
+ if(!pCaster->isAlive())
+ return;
+
+ if( GetSpellProto()->Effect[GetEffIndex()]==SPELL_EFFECT_PERSISTENT_AREA_AURA &&
+ pCaster->SpellHitResult(m_target,GetSpellProto(),false)!=SPELL_MISS_NONE)
+ return;
+
+ // Check for immune (not use charges)
+ if(m_target->IsImmunedToDamage(GetSpellSchoolMask(GetSpellProto())))
+ return;
+
+ uint32 absorb=0;
+ uint32 resist=0;
+ CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL );
+
+ uint32 pdamage = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0;
+
+ //Calculate armor mitigation if it is a physical spell
+ if (GetSpellSchoolMask(GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL)
+ {
+ uint32 pdamageReductedArmor = pCaster->CalcArmorReducedDamage(m_target, pdamage);
+ cleanDamage.damage += pdamage - pdamageReductedArmor;
+ pdamage = pdamageReductedArmor;
+ }
+
+ pdamage = pCaster->SpellDamageBonus(m_target,GetSpellProto(),pdamage,DOT);
+
+ // talent Soul Siphon add bonus to Drain Life spells
+ if( GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 0x8) )
+ {
+ // find talent max bonus percentage
+ Unit::AuraList const& mClassScriptAuras = pCaster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator i = mClassScriptAuras.begin(); i != mClassScriptAuras.end(); ++i)
+ {
+ if ((*i)->GetModifier()->m_miscvalue == 4992 || (*i)->GetModifier()->m_miscvalue == 4993)
+ {
+ if((*i)->GetEffIndex()!=1)
+ {
+ sLog.outError("Expected spell %u structure change, need code update",(*i)->GetId());
+ break;
+ }
+
+ // effect 1 m_amount
+ int32 maxPercent = (*i)->GetModifier()->m_amount;
+ // effect 0 m_amount
+ int32 stepPercent = pCaster->CalculateSpellDamage((*i)->GetSpellProto(),0,(*i)->GetSpellProto()->EffectBasePoints[0],pCaster);
+
+ // count affliction effects and calc additional damage in percentage
+ int32 modPercent = 0;
+ Unit::AuraMap const& victimAuras = m_target->GetAuras();
+ for (Unit::AuraMap::const_iterator itr = victimAuras.begin(); itr != victimAuras.end(); ++itr)
+ {
+ Aura* aura = itr->second;
+ if (aura->IsPositive())continue;
+ SpellEntry const* m_spell = aura->GetSpellProto();
+ if (m_spell->SpellFamilyName != SPELLFAMILY_WARLOCK)
+ continue;
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(m_spell->Id);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(m_spell->Id);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ if(_spell_idx->second->skillId == SKILL_AFFLICTION)
+ {
+ modPercent += stepPercent;
+ if (modPercent >= maxPercent)
+ {
+ modPercent = maxPercent;
+ break;
+ }
+ }
+ }
+ }
+ pdamage += (pdamage*modPercent/100);
+ break;
+ }
+ }
+ }
+
+ //As of 2.2 resilience reduces damage from DoT ticks as much as the chance to not be critically hit
+ // Reduce dot damage from resilience for players
+ if (m_target->GetTypeId()==TYPEID_PLAYER)
+ pdamage-=((Player*)m_target)->GetDotDamageReduction(pdamage);
+
+ pCaster->CalcAbsorbResist(m_target, GetSpellSchoolMask(GetSpellProto()), DOT, pdamage, &absorb, &resist);
+
+ if(m_target->GetHealth() < pdamage)
+ pdamage = uint32(m_target->GetHealth());
+
+ sLog.outDetail("PeriodicTick: %u (TypeId: %u) health leech of %u (TypeId: %u) for %u dmg inflicted by %u abs is %u",
+ GetCasterGUID(), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId(),absorb);
+
+ pCaster->SendSpellNonMeleeDamageLog(m_target, GetId(), pdamage, GetSpellSchoolMask(GetSpellProto()), absorb, resist, false, 0);
+
+
+ Unit* target = m_target; // aura can be deleted in DealDamage
+ SpellEntry const* spellProto = GetSpellProto();
+ float multiplier = spellProto->EffectMultipleValue[GetEffIndex()] > 0 ? spellProto->EffectMultipleValue[GetEffIndex()] : 1;
+
+ uint32 new_damage = pCaster->DealDamage(m_target, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), &cleanDamage, DOT, GetSpellSchoolMask(GetSpellProto()), GetSpellProto(), false);
+
+ // DO NOT ACCESS MEMBERS OF THE AURA FROM NOW ON (DealDamage can delete aura)
+
+ pCaster->ProcDamageAndSpell(target, PROC_FLAG_HIT_SPELL, PROC_FLAG_TAKE_DAMAGE, new_damage, GetSpellSchoolMask(spellProto), spellProto);
+ if (!target->isAlive() && pCaster->IsNonMeleeSpellCasted(false))
+ {
+ for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
+ {
+ if (pCaster->m_currentSpells[i] && pCaster->m_currentSpells[i]->m_spellInfo->Id == spellProto->Id)
+ pCaster->m_currentSpells[i]->cancel();
+ }
+ }
+
+
+ if(Player *modOwner = pCaster->GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_MULTIPLE_VALUE, multiplier);
+
+ uint32 heal = pCaster->SpellHealingBonus(spellProto, uint32(new_damage * multiplier), DOT, pCaster);
+
+ int32 gain = pCaster->ModifyHealth(heal);
+ pCaster->getHostilRefManager().threatAssist(pCaster, gain * 0.5f, spellProto);
+
+ pCaster->SendHealSpellLog(pCaster, spellProto->Id, heal);
+ break;
+ }
+ case SPELL_AURA_PERIODIC_HEAL:
+ case SPELL_AURA_OBS_MOD_HEALTH:
+ {
+ Unit *pCaster = GetCaster();
+ if(!pCaster)
+ return;
+
+ // heal for caster damage (must be alive)
+ if(m_target != pCaster && GetSpellProto()->SpellVisual==163 && !pCaster->isAlive())
+ return;
+
+ // ignore non positive values (can be result apply spellmods to aura damage
+ uint32 amount = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0;
+
+ uint32 pdamage;
+
+ if(m_modifier.m_auraname==SPELL_AURA_OBS_MOD_HEALTH)
+ pdamage = uint32(m_target->GetMaxHealth() * amount/100);
+ else
+ pdamage = amount;
+
+ pdamage = pCaster->SpellHealingBonus(GetSpellProto(), pdamage, DOT, m_target);
+
+ sLog.outDetail("PeriodicTick: %u (TypeId: %u) heal of %u (TypeId: %u) for %u health inflicted by %u",
+ GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId());
+
+ WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size
+ data.append(m_target->GetPackGUID());
+ data.appendPackGUID(GetCasterGUID());
+ data << uint32(GetId());
+ data << uint32(1);
+ data << uint32(m_modifier.m_auraname);
+ data << (uint32)pdamage;
+ m_target->SendMessageToSet(&data,true);
+
+ int32 gain = m_target->ModifyHealth(pdamage);
+
+ // add HoTs to amount healed in bgs
+ if( pCaster->GetTypeId() == TYPEID_PLAYER )
+ if( BattleGround *bg = ((Player*)pCaster)->GetBattleGround() )
+ bg->UpdatePlayerScore(((Player*)pCaster), SCORE_HEALING_DONE, gain);
+
+ //Do check before because m_modifier.auraName can be invalidate by DealDamage.
+ bool procSpell = (m_modifier.m_auraname == SPELL_AURA_PERIODIC_HEAL && m_target != pCaster);
+
+ m_target->getHostilRefManager().threatAssist(pCaster, float(gain) * 0.5f, GetSpellProto());
+
+ Unit* target = m_target; // aura can be deleted in DealDamage
+ SpellEntry const* spellProto = GetSpellProto();
+ bool haveCastItem = GetCastItemGUID()!=0;
+
+ // heal for caster damage
+ if(m_target!=pCaster && spellProto->SpellVisual==163)
+ {
+ uint32 dmg = spellProto->manaPerSecond;
+ if(pCaster->GetHealth() <= dmg && pCaster->GetTypeId()==TYPEID_PLAYER)
+ {
+ pCaster->RemoveAurasDueToSpell(GetId());
+
+ // finish current generic/channeling spells, don't affect autorepeat
+ if(pCaster->m_currentSpells[CURRENT_GENERIC_SPELL])
+ {
+ pCaster->m_currentSpells[CURRENT_GENERIC_SPELL]->finish();
+ }
+ if(pCaster->m_currentSpells[CURRENT_CHANNELED_SPELL])
+ {
+ pCaster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
+ pCaster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish();
+ }
+ }
+ else
+ {
+ pCaster->SendSpellNonMeleeDamageLog(pCaster, GetId(), gain, GetSpellSchoolMask(GetSpellProto()), 0, 0, false, 0, false);
+
+ CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL );
+ pCaster->DealDamage(pCaster, gain, &cleanDamage, NODAMAGE, GetSpellSchoolMask(GetSpellProto()), GetSpellProto(), true);
+ }
+ }
+
+ // ignore item heals
+ if(procSpell && !haveCastItem)
+ pCaster->ProcDamageAndSpell(target,PROC_FLAG_HEAL, PROC_FLAG_HEALED, pdamage, SPELL_SCHOOL_MASK_NONE, spellProto);
+ break;
+ }
+ case SPELL_AURA_PERIODIC_MANA_LEECH:
+ {
+ Unit *pCaster = GetCaster();
+ if(!pCaster)
+ return;
+
+ if(!pCaster->isAlive())
+ return;
+
+ if( GetSpellProto()->Effect[GetEffIndex()]==SPELL_EFFECT_PERSISTENT_AREA_AURA &&
+ pCaster->SpellHitResult(m_target,GetSpellProto(),false)!=SPELL_MISS_NONE)
+ return;
+
+ // Check for immune (not use charges)
+ if(m_target->IsImmunedToDamage(GetSpellSchoolMask(GetSpellProto())))
+ return;
+
+ // ignore non positive values (can be result apply spellmods to aura damage
+ uint32 pdamage = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0;
+
+ sLog.outDetail("PeriodicTick: %u (TypeId: %u) power leech of %u (TypeId: %u) for %u dmg inflicted by %u",
+ GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId());
+
+ if(m_modifier.m_miscvalue < 0 || m_modifier.m_miscvalue > 4)
+ break;
+
+ Powers power = Powers(m_modifier.m_miscvalue);
+
+ // power type might have changed between aura applying and tick (druid's shapeshift)
+ if(m_target->getPowerType() != power)
+ break;
+
+ int32 drain_amount = m_target->GetPower(power) > pdamage ? pdamage : m_target->GetPower(power);
+
+ // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
+ if (power == POWER_MANA && m_target->GetTypeId() == TYPEID_PLAYER)
+ drain_amount -= ((Player*)m_target)->GetSpellCritDamageReduction(drain_amount);
+
+ m_target->ModifyPower(power, -drain_amount);
+
+ float gain_multiplier = 0;
+
+ if(pCaster->GetMaxPower(power) > 0)
+ {
+ gain_multiplier = GetSpellProto()->EffectMultipleValue[GetEffIndex()];
+
+ if(Player *modOwner = pCaster->GetSpellModOwner())
+ modOwner->ApplySpellMod(GetId(), SPELLMOD_MULTIPLE_VALUE, gain_multiplier);
+ }
+
+ WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size
+ data.append(m_target->GetPackGUID());
+ data.appendPackGUID(GetCasterGUID());
+ data << uint32(GetId());
+ data << uint32(1);
+ data << uint32(m_modifier.m_auraname);
+ data << (uint32)power; // power type
+ data << (uint32)drain_amount;
+ data << (float)gain_multiplier;
+ m_target->SendMessageToSet(&data,true);
+
+ int32 gain_amount = int32(drain_amount*gain_multiplier);
+
+ if(gain_amount)
+ {
+ int32 gain = pCaster->ModifyPower(power,gain_amount);
+ m_target->AddThreat(pCaster, float(gain) * 0.5f, GetSpellSchoolMask(GetSpellProto()), GetSpellProto());
+ }
+ break;
+ }
+ case SPELL_AURA_PERIODIC_ENERGIZE:
+ {
+ // ignore non positive values (can be result apply spellmods to aura damage
+ uint32 pdamage = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0;
+
+ sLog.outDetail("PeriodicTick: %u (TypeId: %u) energize %u (TypeId: %u) for %u dmg inflicted by %u",
+ GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId());
+
+ if(m_modifier.m_miscvalue < 0 || m_modifier.m_miscvalue > 4)
+ break;
+
+ Powers power = Powers(m_modifier.m_miscvalue);
+
+ if(m_target->GetMaxPower(power) == 0)
+ break;
+
+ WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size
+ data.append(m_target->GetPackGUID());
+ data.appendPackGUID(GetCasterGUID());
+ data << uint32(GetId());
+ data << uint32(1);
+ data << uint32(m_modifier.m_auraname);
+ data << (uint32)power; // power type
+ data << (uint32)pdamage;
+ m_target->SendMessageToSet(&data,true);
+
+ int32 gain = m_target->ModifyPower(power,pdamage);
+
+ if(Unit* pCaster = GetCaster())
+ m_target->getHostilRefManager().threatAssist(pCaster, float(gain) * 0.5f, GetSpellProto());
+ break;
+ }
+ case SPELL_AURA_OBS_MOD_MANA:
+ {
+ // ignore non positive values (can be result apply spellmods to aura damage
+ uint32 amount = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0;
+
+ uint32 pdamage = uint32(m_target->GetMaxPower(POWER_MANA) * amount/100);
+
+ sLog.outDetail("PeriodicTick: %u (TypeId: %u) energize %u (TypeId: %u) for %u mana inflicted by %u",
+ GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId());
+
+ if(m_target->GetMaxPower(POWER_MANA) == 0)
+ break;
+
+ WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size
+ data.append(m_target->GetPackGUID());
+ data.appendPackGUID(GetCasterGUID());
+ data << uint32(GetId());
+ data << uint32(1);
+ data << uint32(m_modifier.m_auraname);
+ data << (uint32)0; // ?
+ data << (uint32)pdamage;
+ m_target->SendMessageToSet(&data,true);
+
+ int32 gain = m_target->ModifyPower(POWER_MANA, pdamage);
+
+ if(Unit* pCaster = GetCaster())
+ m_target->getHostilRefManager().threatAssist(pCaster, float(gain) * 0.5f, GetSpellProto());
+ break;
+ }
+ case SPELL_AURA_POWER_BURN_MANA:
+ {
+ Unit *pCaster = GetCaster();
+ if(!pCaster)
+ return;
+
+ // Check for immune (not use charges)
+ if(m_target->IsImmunedToDamage(GetSpellSchoolMask(GetSpellProto())))
+ return;
+
+ int32 pdamage = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0;
+
+ Powers powerType = Powers(m_modifier.m_miscvalue);
+
+ if(!m_target->isAlive() || m_target->getPowerType() != powerType)
+ return;
+
+ // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
+ if (powerType == POWER_MANA && m_target->GetTypeId() == TYPEID_PLAYER)
+ pdamage -= ((Player*)m_target)->GetSpellCritDamageReduction(pdamage);
+
+ uint32 gain = uint32(-m_target->ModifyPower(powerType, -pdamage));
+
+ gain = uint32(gain * GetSpellProto()->EffectMultipleValue[GetEffIndex()]);
+
+ //maybe has to be sent different to client, but not by SMSG_PERIODICAURALOG
+ pCaster->SpellNonMeleeDamageLog(m_target, GetId(), gain);
+ break;
+ }
+ // Here tick dummy auras
+ case SPELL_AURA_PERIODIC_DUMMY:
+ {
+ PeriodicDummyTick();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void Aura::PeriodicDummyTick()
+{
+ SpellEntry const* spell = GetSpellProto();
+ switch (spell->Id)
+ {
+ // Drink
+ case 430:
+ case 431:
+ case 432:
+ case 1133:
+ case 1135:
+ case 1137:
+ case 10250:
+ case 22734:
+ case 27089:
+ case 34291:
+ case 43706:
+ case 46755:
+ {
+ if (m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+ // Search SPELL_AURA_MOD_POWER_REGEN aura for this spell and add bonus
+ Unit::AuraList const& aura = m_target->GetAurasByType(SPELL_AURA_MOD_POWER_REGEN);
+ for(Unit::AuraList::const_iterator i = aura.begin(); i != aura.end(); ++i)
+ {
+ if ((*i)->GetId() == GetId())
+ {
+ // Get tick number
+ int32 tick = (m_maxduration - m_duration) / m_modifier.periodictime;
+ // Default case (not on arenas)
+ if (tick == 0)
+ {
+ (*i)->GetModifier()->m_amount = m_modifier.m_amount;
+ ((Player*)m_target)->UpdateManaRegen();
+ // Disable continue
+ m_isPeriodic = false;
+ }
+ return;
+ //**********************************************
+ // Code commended since arena patch not added
+ // This feature uses only in arenas
+ //**********************************************
+ // Here need increase mana regen per tick (6 second rule)
+ // on 0 tick - 0 (handled in 2 second)
+ // on 1 tick - 166% (handled in 4 second)
+ // on 2 tick - 133% (handled in 6 second)
+ // Not need update after 3 tick
+ /*
+ if (tick > 3)
+ return;
+ // Apply bonus for 0 - 3 tick
+ switch (tick)
+ {
+ case 0: // 0%
+ (*i)->GetModifier()->m_amount = m_modifier.m_amount = 0;
+ break;
+ case 1: // 166%
+ (*i)->GetModifier()->m_amount = m_modifier.m_amount * 5 / 3;
+ break;
+ case 2: // 133%
+ (*i)->GetModifier()->m_amount = m_modifier.m_amount * 4 / 3;
+ break;
+ default: // 100% - normal regen
+ (*i)->GetModifier()->m_amount = m_modifier.m_amount;
+ break;
+ }
+ ((Player*)m_target)->UpdateManaRegen();
+ return;*/
+ }
+ }
+ return;
+ }
+// // Panda
+// case 19230: break;
+// // Master of Subtlety
+// case 31666: break;
+// // Gossip NPC Periodic - Talk
+// case 33208: break;
+// // Gossip NPC Periodic - Despawn
+// case 33209: break;
+// // Force of Nature
+// case 33831: break;
+ // Aspect of the Viper
+ case 34074:
+ {
+ if (m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+ // Should be manauser
+ if (m_target->getPowerType()!=POWER_MANA)
+ return;
+ Unit *caster = GetCaster();
+ if (!caster)
+ return;
+ // Regen amount is max (100% from spell) on 21% or less mana and min on 92.5% or greater mana (20% from spell)
+ int mana = m_target->GetPower(POWER_MANA);
+ int max_mana = m_target->GetMaxPower(POWER_MANA);
+ int32 base_regen = caster->CalculateSpellDamage(m_spellProto, m_effIndex, m_currentBasePoints, m_target);
+ float regen_pct = 1.20f - 1.1f * mana / max_mana;
+ if (regen_pct > 1.0f) regen_pct = 1.0f;
+ else if (regen_pct < 0.2f) regen_pct = 0.2f;
+ m_modifier.m_amount = int32 (base_regen * regen_pct);
+ ((Player*)m_target)->UpdateManaRegen();
+ return;
+ }
+// // Steal Weapon
+// case 36207: break;
+// // Simon Game START timer, (DND)
+// case 39993: break;
+// // Harpooner's Mark
+// case 40084: break;
+// // Knockdown Fel Cannon: break; The Aggro Burst
+// case 40119: break;
+// // Old Mount Spell
+// case 40154: break;
+// // Magnetic Pull
+// case 40581: break;
+// // Ethereal Ring: break; The Bolt Burst
+// case 40801: break;
+// // Crystal Prison
+// case 40846: break;
+// // Copy Weapon
+// case 41054: break;
+// // Ethereal Ring Visual, Lightning Aura
+// case 41477: break;
+// // Ethereal Ring Visual, Lightning Aura (Fork)
+// case 41525: break;
+// // Ethereal Ring Visual, Lightning Jumper Aura
+// case 41567: break;
+// // No Man's Land
+// case 41955: break;
+// // Headless Horseman - Fire
+// case 42074: break;
+// // Headless Horseman - Visual - Large Fire
+// case 42075: break;
+// // Headless Horseman - Start Fire, Periodic Aura
+// case 42140: break;
+// // Ram Speed Boost
+// case 42152: break;
+// // Headless Horseman - Fires Out Victory Aura
+// case 42235: break;
+// // Pumpkin Life Cycle
+// case 42280: break;
+// // Brewfest Request Chick Chuck Mug Aura
+// case 42537: break;
+// // Squashling
+// case 42596: break;
+// // Headless Horseman Climax, Head: Periodic
+// case 42603: break;
+// // Fire Bomb
+// case 42621: break;
+// // Headless Horseman - Conflagrate, Periodic Aura
+// case 42637: break;
+// // Headless Horseman - Create Pumpkin Treats Aura
+// case 42774: break;
+// // Headless Horseman Climax - Summoning Rhyme Aura
+// case 42879: break;
+// // Tricky Treat
+// case 42919: break;
+// // Giddyup!
+// case 42924: break;
+// // Ram - Trot
+// case 42992: break;
+// // Ram - Canter
+// case 42993: break;
+// // Ram - Gallop
+// case 42994: break;
+// // Ram Level - Neutral
+// case 43310: break;
+// // Headless Horseman - Maniacal Laugh, Maniacal, Delayed 17
+// case 43884: break;
+// // Headless Horseman - Maniacal Laugh, Maniacal, other, Delayed 17
+// case 44000: break;
+// // Energy Feedback
+// case 44328: break;
+// // Romantic Picnic
+// case 45102: break;
+// // Romantic Picnic
+// case 45123: break;
+// // Looking for Love
+// case 45124: break;
+// // Kite - Lightning Strike Kite Aura
+// case 45197: break;
+// // Rocket Chicken
+// case 45202: break;
+// // Copy Offhand Weapon
+// case 45205: break;
+// // Upper Deck - Kite - Lightning Periodic Aura
+// case 45207: break;
+// // Kite -Sky Lightning Strike Kite Aura
+// case 45251: break;
+// // Ribbon Pole Dancer Check Aura
+// case 45390: break;
+// // Holiday - Midsummer, Ribbon Pole Periodic Visual
+// case 45406: break;
+// // Parachute
+// case 45472: break;
+// // Alliance Flag, Extra Damage Debuff
+// case 45898: break;
+// // Horde Flag, Extra Damage Debuff
+// case 45899: break;
+// // Ahune - Summoning Rhyme Aura
+// case 45926: break;
+// // Ahune - Slippery Floor
+// case 45945: break;
+// // Ahune's Shield
+// case 45954: break;
+// // Nether Vapor Lightning
+// case 45960: break;
+// // Darkness
+// case 45996: break;
+// // Summon Blood Elves Periodic
+// case 46041: break;
+// // Transform Visual Missile Periodic
+// case 46205: break;
+// // Find Opening Beam End
+// case 46333: break;
+// // Ice Spear Control Aura
+// case 46371: break;
+// // Hailstone Chill
+// case 46458: break;
+// // Hailstone Chill, Internal
+// case 46465: break;
+// // Chill, Internal Shifter
+// case 46549: break;
+// // Summon Ice Spear Knockback Delayer
+// case 46878: break;
+// // Burninate Effect
+// case 47214: break;
+// // Fizzcrank Practice Parachute
+// case 47228: break;
+// // Send Mug Control Aura
+// case 47369: break;
+// // Direbrew's Disarm (precast)
+// case 47407: break;
+// // Mole Machine Port Schedule
+// case 47489: break;
+// // Mole Machine Portal Schedule
+// case 49466: break;
+// // Drink Coffee
+// case 49472: break;
+// // Listening to Music
+// case 50493: break;
+// // Love Rocket Barrage
+// case 50530: break;
+ default:
+ break;
+ }
+}
+
+void Aura::HandlePreventFleeing(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ Unit::AuraList const& fearAuras = m_target->GetAurasByType(SPELL_AURA_MOD_FEAR);
+ if( !fearAuras.empty() )
+ {
+ if (apply)
+ m_target->SetFeared(false, fearAuras.front()->GetCasterGUID());
+ else
+ m_target->SetFeared(true);
+ }
+}
+
+void Aura::HandleManaShield(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ // prevent double apply bonuses
+ if(apply && (m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading()))
+ {
+ if(Unit* caster = GetCaster())
+ {
+ float DoneActualBenefit = 0.0f;
+ switch(m_spellProto->SpellFamilyName)
+ {
+ case SPELLFAMILY_MAGE:
+ if(m_spellProto->SpellFamilyFlags & 0x8000)
+ {
+ // Mana Shield
+ // +50% from +spd bonus
+ DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.5f;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellProto());
+
+ m_modifier.m_amount += (int32)DoneActualBenefit;
+ }
+ }
+}
+
+void Aura::HandleArenaPreparation(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ if(apply)
+ m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION);
+ else
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION);
+}
diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 9816f683b05..06607e90fd8 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -1465,7 +1465,7 @@ void Spell::EffectDummy(uint32 i) if(!spell_proto)
return;
- if( !unitTarget->hasUnitState(UNIT_STAT_STUNDED) && m_caster->GetTypeId()==TYPEID_PLAYER)
+ if( !unitTarget->hasUnitState(UNIT_STAT_STUNNED) && m_caster->GetTypeId()==TYPEID_PLAYER)
{
// decreased damage (/2) for non-stunned target.
SpellModifier *mod = new SpellModifier;
@@ -3401,7 +3401,7 @@ void Spell::EffectDistract(uint32 /*i*/) return;
// target must be OK to do this
- if( unitTarget->hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_STUNDED | UNIT_STAT_FLEEING ) )
+ if( unitTarget->hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_STUNNED | UNIT_STAT_FLEEING ) )
return;
float angle = unitTarget->GetAngle(m_targets.m_destX, m_targets.m_destY);
diff --git a/src/game/TargetedMovementGenerator.cpp b/src/game/TargetedMovementGenerator.cpp index 9d465877a2a..8687de479f2 100644 --- a/src/game/TargetedMovementGenerator.cpp +++ b/src/game/TargetedMovementGenerator.cpp @@ -47,7 +47,7 @@ TargetedMovementGenerator<T>::_setTargetLocation(T &owner) if( !i_target.isValid() || !&owner )
return;
- if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED) )
+ if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED) )
return;
// prevent redundant micro-movement for pets, other followers.
@@ -127,7 +127,7 @@ TargetedMovementGenerator<T>::Update(T &owner, const uint32 & time_diff) if( !&owner || !owner.isAlive())
return true;
- if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_FLEEING | UNIT_STAT_DISTRACTED) )
+ if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_FLEEING | UNIT_STAT_DISTRACTED) )
return true;
// prevent movement while casting spells with cast time or channel time
diff --git a/src/game/TradeHandler.cpp b/src/game/TradeHandler.cpp index eb874f82ffb..4a4273c8750 100644 --- a/src/game/TradeHandler.cpp +++ b/src/game/TradeHandler.cpp @@ -1,633 +1,634 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 "Common.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "World.h" -#include "ObjectAccessor.h" -#include "Log.h" -#include "Opcodes.h" -#include "Player.h" -#include "Item.h" -#include "SocialMgr.h" - -enum TradeStatus -{ - TRADE_STATUS_BUSY = 0, - TRADE_STATUS_BEGIN_TRADE = 1, - TRADE_STATUS_OPEN_WINDOW = 2, - TRADE_STATUS_TRADE_CANCELED = 3, - TRADE_STATUS_TRADE_ACCEPT = 4, - TRADE_STATUS_BUSY_2 = 5, - TRADE_STATUS_NO_TARGET = 6, - TRADE_STATUS_BACK_TO_TRADE = 7, - TRADE_STATUS_TRADE_COMPLETE = 8, - // 9? - TRADE_STATUS_TARGET_TO_FAR = 10, - TRADE_STATUS_WRONG_FACTION = 11, - TRADE_STATUS_CLOSE_WINDOW = 12, - // 13? - TRADE_STATUS_IGNORE_YOU = 14, - TRADE_STATUS_YOU_STUNNED = 15, - TRADE_STATUS_TARGET_STUNNED = 16, - TRADE_STATUS_YOU_DEAD = 17, - TRADE_STATUS_TARGET_DEAD = 18, - TRADE_STATUS_YOU_LOGOUT = 19, - TRADE_STATUS_TARGET_LOGOUT = 20, - TRADE_STATUS_TRIAL_ACCOUNT = 21, // Trial accounts can not perform that action - TRADE_STATUS_ONLY_CONJURED = 22 // You can only trade conjured items... (cross realm BG related). -}; - -void WorldSession::SendTradeStatus(uint32 status) -{ - WorldPacket data; - - switch(status) - { - case TRADE_STATUS_BEGIN_TRADE: - data.Initialize(SMSG_TRADE_STATUS, 4+8); - data << uint32(status); - data << uint64(0); - break; - case TRADE_STATUS_OPEN_WINDOW: - data.Initialize(SMSG_TRADE_STATUS, 4+4); - data << uint32(status); - data << uint32(0); // added in 2.4.0 - break; - case TRADE_STATUS_CLOSE_WINDOW: - data.Initialize(SMSG_TRADE_STATUS, 4+4+1+4); - data << uint32(status); - data << uint32(0); - data << uint8(0); - data << uint32(0); - break; - case TRADE_STATUS_ONLY_CONJURED: - data.Initialize(SMSG_TRADE_STATUS, 4+1); - data << uint32(status); - data << uint8(0); - break; - default: - data.Initialize(SMSG_TRADE_STATUS, 4); - data << uint32(status); - break; - } - - SendPacket(&data); -} - -void WorldSession::HandleIgnoreTradeOpcode(WorldPacket& /*recvPacket*/) -{ - sLog.outDebug( "WORLD: Ignore Trade %u",_player->GetGUIDLow()); - // recvPacket.print_storage(); -} - -void WorldSession::HandleBusyTradeOpcode(WorldPacket& /*recvPacket*/) -{ - sLog.outDebug( "WORLD: Busy Trade %u",_player->GetGUIDLow()); - // recvPacket.print_storage(); -} - -void WorldSession::SendUpdateTrade() -{ - Item *item = NULL; - - if( !_player || !_player->pTrader ) - return; - - // reset trade status - if (_player->acceptTrade) - { - _player->acceptTrade = false; - SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - } - - if (_player->pTrader->acceptTrade) - { - _player->pTrader->acceptTrade = false; - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - } - - WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, (100)); // guess size - data << (uint8 ) 1; // can be different (only seen 0 and 1) - data << (uint32) 0; // added in 2.4.0, this value must be equal to value from TRADE_STATUS_OPEN_WINDOW status packet (different value for different players to block multiple trades?) - data << (uint32) TRADE_SLOT_COUNT; // trade slots count/number?, = next field in most cases - data << (uint32) TRADE_SLOT_COUNT; // trade slots count/number?, = prev field in most cases - data << (uint32) _player->pTrader->tradeGold; // trader gold - data << (uint32) 0; // spell casted on lowest slot item - - for(uint8 i = 0; i < TRADE_SLOT_COUNT; i++) - { - item = (_player->pTrader->tradeItems[i] != NULL_SLOT ? _player->pTrader->GetItemByPos( _player->pTrader->tradeItems[i] ) : NULL); - - data << (uint8) i; // trade slot number, if not specified, then end of packet - - if(item) - { - data << (uint32) item->GetProto()->ItemId; // entry - // display id - data << (uint32) item->GetProto()->DisplayInfoID; - // stack count - data << (uint32) item->GetUInt32Value(ITEM_FIELD_STACK_COUNT); - data << (uint32) 0; // probably gift=1, created_by=0? - // gift creator - data << (uint64) item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR); - data << (uint32) item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT); - for(uint8 j = 0; j < 3; ++j) - data << (uint32) 0; // enchantment id (permanent/gems?) - // creator - data << (uint64) item->GetUInt64Value(ITEM_FIELD_CREATOR); - data << (uint32) item->GetSpellCharges(); // charges - data << (uint32) item->GetItemSuffixFactor(); // SuffixFactor - // random properties id - data << (uint32) item->GetItemRandomPropertyId(); - data << (uint32) item->GetProto()->LockID; // lock id - // max durability - data << (uint32) item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY); - // durability - data << (uint32) item->GetUInt32Value(ITEM_FIELD_DURABILITY); - } - else - { - for(uint8 j = 0; j < 18; j++) - data << uint32(0); - } - } - SendPacket(&data); -} - -//============================================================== -// transfer the items to the players - -void WorldSession::moveItems(Item* myItems[], Item* hisItems[]) -{ - for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++) - { - ItemPosCountVec traderDst; - ItemPosCountVec playerDst; - bool traderCanTrade = (myItems[i]==NULL || _player->pTrader->CanStoreItem( NULL_BAG, NULL_SLOT, traderDst, myItems[i], false ) == EQUIP_ERR_OK); - bool playerCanTrade = (hisItems[i]==NULL || _player->CanStoreItem( NULL_BAG, NULL_SLOT, playerDst, hisItems[i], false ) == EQUIP_ERR_OK); - if(traderCanTrade && playerCanTrade ) - { - // Ok, if trade item exists and can be stored - // If we trade in both directions we had to check, if the trade will work before we actually do it - // A roll back is not possible after we stored it - if(myItems[i]) - { - // logging - sLog.outDebug("partner storing: %u",myItems[i]->GetGUIDLow()); - if( _player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) - sLog.outCommand("GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)", - _player->GetName(),_player->GetSession()->GetAccountId(), - myItems[i]->GetProto()->Name1,myItems[i]->GetEntry(),myItems[i]->GetCount(), - _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId()); - - // store - _player->pTrader->MoveItemToInventory( traderDst, myItems[i], true, true); - } - if(hisItems[i]) - { - // logging - sLog.outDebug("player storing: %u",hisItems[i]->GetGUIDLow()); - if( _player->pTrader->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) - sLog.outCommand("GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)", - _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId(), - hisItems[i]->GetProto()->Name1,hisItems[i]->GetEntry(),hisItems[i]->GetCount(), - _player->GetName(),_player->GetSession()->GetAccountId()); - - // store - _player->MoveItemToInventory( playerDst, hisItems[i], true, true); - } - } - else - { - // in case of fatal error log error message - // return the already removed items to the original owner - if(myItems[i]) - { - if(!traderCanTrade) - sLog.outError("trader can't store item: %u",myItems[i]->GetGUIDLow()); - if(_player->CanStoreItem( NULL_BAG, NULL_SLOT, playerDst, myItems[i], false ) == EQUIP_ERR_OK) - _player->MoveItemToInventory(playerDst, myItems[i], true, true); - else - sLog.outError("player can't take item back: %u",myItems[i]->GetGUIDLow()); - } - // return the already removed items to the original owner - if(hisItems[i]) - { - if(!playerCanTrade) - sLog.outError("player can't store item: %u",hisItems[i]->GetGUIDLow()); - if(_player->pTrader->CanStoreItem( NULL_BAG, NULL_SLOT, traderDst, hisItems[i], false ) == EQUIP_ERR_OK) - _player->pTrader->MoveItemToInventory(traderDst, hisItems[i], true, true); - else - sLog.outError("trader can't take item back: %u",hisItems[i]->GetGUIDLow()); - } - } - } -} - -//============================================================== - -void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) -{ - Item *myItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL }; - Item *hisItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL }; - bool myCanCompleteTrade=true,hisCanCompleteTrade=true; - - if ( !GetPlayer()->pTrader ) - return; - - // not accept case incorrect money amount - if( _player->tradeGold > _player->GetMoney() ) - { - SendNotification( "You do not have enough gold" ); - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - _player->acceptTrade = false; - return; - } - - // not accept case incorrect money amount - if( _player->pTrader->tradeGold > _player->pTrader->GetMoney() ) - { - _player->pTrader->GetSession( )->SendNotification( "You do not have enough gold" ); - SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - _player->pTrader->acceptTrade = false; - return; - } - - // not accept if some items now can't be trade (cheating) - for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++) - { - if(_player->tradeItems[i] != NULL_SLOT ) - { - if(Item* item =_player->GetItemByPos( _player->tradeItems[i] )) - { - if(!item->CanBeTraded()) - { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); - return; - } - } - } - if(_player->pTrader->tradeItems[i] != NULL_SLOT) - { - if(Item* item =_player->pTrader->GetItemByPos( _player->pTrader->tradeItems[i]) ) - { - if(!item->CanBeTraded()) - { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); - return; - } - } - } - } - - _player->acceptTrade = true; - if (_player->pTrader->acceptTrade ) - { - // inform partner client - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); - - // store items in local list and set 'in-trade' flag - for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++) - { - if(_player->tradeItems[i] != NULL_SLOT ) - { - sLog.outDebug("player trade item bag: %u slot: %u",_player->tradeItems[i] >> 8, _player->tradeItems[i] & 255 ); - //Can return NULL - myItems[i]=_player->GetItemByPos( _player->tradeItems[i] ); - if (myItems[i]) - myItems[i]->SetInTrade(); - } - if(_player->pTrader->tradeItems[i] != NULL_SLOT) - { - sLog.outDebug("partner trade item bag: %u slot: %u",_player->pTrader->tradeItems[i] >> 8,_player->pTrader->tradeItems[i] & 255); - //Can return NULL - hisItems[i]=_player->pTrader->GetItemByPos( _player->pTrader->tradeItems[i]); - if(hisItems[i]) - hisItems[i]->SetInTrade(); - } - } - - // test if item will fit in each inventory - hisCanCompleteTrade = (_player->pTrader->CanStoreItems( myItems,TRADE_SLOT_TRADED_COUNT )== EQUIP_ERR_OK); - myCanCompleteTrade = (_player->CanStoreItems( hisItems,TRADE_SLOT_TRADED_COUNT ) == EQUIP_ERR_OK); - - // clear 'in-trade' flag - for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++) - { - if(myItems[i]) myItems[i]->SetInTrade(false); - if(hisItems[i]) hisItems[i]->SetInTrade(false); - } - - // in case of missing space report error - if(!myCanCompleteTrade) - { - SendNotification("You do not have enough free slots"); - GetPlayer( )->pTrader->GetSession( )->SendNotification("Your partner does not have enough free bag slots"); - SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - return; - } - else if (!hisCanCompleteTrade) - { - SendNotification("Your partner does not have enough free bag slots"); - GetPlayer()->pTrader->GetSession()->SendNotification("You do not have enough free slots"); - SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - return; - } - - // execute trade: 1. remove - for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++) - { - if(myItems[i]) - { - myItems[i]->SetUInt64Value( ITEM_FIELD_GIFTCREATOR,_player->GetGUID()); - _player->MoveItemFromInventory(_player->tradeItems[i] >> 8, _player->tradeItems[i] & 255, true); - } - if(hisItems[i]) - { - hisItems[i]->SetUInt64Value( ITEM_FIELD_GIFTCREATOR,_player->pTrader->GetGUID()); - _player->pTrader->MoveItemFromInventory(_player->pTrader->tradeItems[i] >> 8, _player->pTrader->tradeItems[i] & 255, true); - } - } - - // execute trade: 2. store - moveItems(myItems, hisItems); - - // logging money - if(sWorld.getConfig(CONFIG_GM_LOG_TRADE)) - { - if( _player->GetSession()->GetSecurity() > SEC_PLAYER && _player->tradeGold > 0) - sLog.outCommand("GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)", - _player->GetName(),_player->GetSession()->GetAccountId(), - _player->tradeGold, - _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId()); - if( _player->pTrader->GetSession()->GetSecurity() > SEC_PLAYER && _player->pTrader->tradeGold > 0) - sLog.outCommand("GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)", - _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId(), - _player->pTrader->tradeGold, - _player->GetName(),_player->GetSession()->GetAccountId()); - } - - // update money - _player->ModifyMoney( -int32(_player->tradeGold) ); - _player->ModifyMoney(_player->pTrader->tradeGold ); - _player->pTrader->ModifyMoney( -int32(_player->pTrader->tradeGold) ); - _player->pTrader->ModifyMoney(_player->tradeGold ); - - _player->ClearTrade(); - _player->pTrader->ClearTrade(); - - // desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards) - CharacterDatabase.BeginTransaction(); - _player->SaveInventoryAndGoldToDB(); - _player->pTrader->SaveInventoryAndGoldToDB(); - CharacterDatabase.CommitTransaction(); - - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); - SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); - - _player->pTrader->pTrader = NULL; - _player->pTrader = NULL; - } - else - { - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); - } -} - -void WorldSession::HandleUnacceptTradeOpcode(WorldPacket& /*recvPacket*/) -{ - if ( !GetPlayer()->pTrader ) - return; - - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - _player->acceptTrade = false; -} - -void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/) -{ - if(!_player->pTrader) - return; - - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); - _player->pTrader->ClearTrade(); - - SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); - _player->ClearTrade(); -} - -void WorldSession::SendCancelTrade() -{ - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); -} - -void WorldSession::HandleCancelTradeOpcode(WorldPacket& /*recvPacket*/) -{ - // sended also after LOGOUT COMPLETE - if(_player) // needed because STATUS_AUTHED - _player->TradeCancel(true); -} - -void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) -{ - CHECK_PACKET_SIZE(recvPacket,8); - - if( GetPlayer()->pTrader ) - return; - - uint64 ID; - - if( !GetPlayer()->isAlive() ) - { - SendTradeStatus(TRADE_STATUS_YOU_DEAD); - return; - } - - if( GetPlayer()->hasUnitState(UNIT_STAT_STUNDED) ) - { - SendTradeStatus(TRADE_STATUS_YOU_STUNNED); - return; - } - - if( isLogingOut() ) - { - SendTradeStatus(TRADE_STATUS_YOU_LOGOUT); - return; - } - - if( GetPlayer()->isInFlight() ) - { - SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); - return; - } - - recvPacket >> ID; - - Player* pOther = ObjectAccessor::FindPlayer( ID ); - - if( !pOther ) - { - SendTradeStatus(TRADE_STATUS_NO_TARGET); - return; - } - - if( pOther == GetPlayer() || pOther->pTrader ) - { - SendTradeStatus(TRADE_STATUS_BUSY); - return; - } - - if( !pOther->isAlive() ) - { - SendTradeStatus(TRADE_STATUS_TARGET_DEAD); - return; - } - - if( pOther->isInFlight() ) - { - SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); - return; - } - - if( pOther->hasUnitState(UNIT_STAT_STUNDED) ) - { - SendTradeStatus(TRADE_STATUS_TARGET_STUNNED); - return; - } - - if( pOther->GetSession()->isLogingOut() ) - { - SendTradeStatus(TRADE_STATUS_TARGET_LOGOUT); - return; - } - - if( pOther->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()) ) - { - SendTradeStatus(TRADE_STATUS_IGNORE_YOU); - return; - } - - if(pOther->GetTeam() !=_player->GetTeam() ) - { - SendTradeStatus(TRADE_STATUS_WRONG_FACTION); - return; - } - - if( pOther->GetDistance2d( _player ) > 10.0f ) - { - SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); - return; - } - - // OK start trade - _player->pTrader = pOther; - pOther->pTrader =_player; - - WorldPacket data(SMSG_TRADE_STATUS, 12); - data << (uint32) TRADE_STATUS_BEGIN_TRADE; - data << (uint64)_player->GetGUID(); - _player->pTrader->GetSession()->SendPacket(&data); -} - -void WorldSession::HandleSetTradeGoldOpcode(WorldPacket& recvPacket) -{ - CHECK_PACKET_SIZE(recvPacket,4); - - if(!_player->pTrader) - return; - - uint32 gold; - - recvPacket >> gold; - - // gold can be incorrect, but this is checked at trade finished. - _player->tradeGold = gold; - - _player->pTrader->GetSession()->SendUpdateTrade(); -} - -void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) -{ - CHECK_PACKET_SIZE(recvPacket,1+1+1); - - if(!_player->pTrader) - return; - - // send update - uint8 tradeSlot; - uint8 bag; - uint8 slot; - - recvPacket >> tradeSlot; - recvPacket >> bag; - recvPacket >> slot; - - // invalid slot number - if(tradeSlot >= TRADE_SLOT_COUNT) - { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); - return; - } - - // check cheating, can't fail with correct client operations - Item* item = _player->GetItemByPos(bag,slot); - if(!item || tradeSlot!=TRADE_SLOT_NONTRADED && !item->CanBeTraded()) - { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); - return; - } - - uint16 pos = (bag << 8) | slot; - - // prevent place single item into many trade slots using cheating and client bugs - for(int i = 0; i < TRADE_SLOT_COUNT; ++i) - { - if(_player->tradeItems[i]==pos) - { - // cheating attempt - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); - return; - } - } - - _player->tradeItems[tradeSlot] = pos; - - _player->pTrader->GetSession()->SendUpdateTrade(); -} - -void WorldSession::HandleClearTradeItemOpcode(WorldPacket& recvPacket) -{ - CHECK_PACKET_SIZE(recvPacket,1); - - if(!_player->pTrader) - return; - - uint8 tradeSlot; - recvPacket >> tradeSlot; - - // invalid slot number - if(tradeSlot >= TRADE_SLOT_COUNT) - return; - - _player->tradeItems[tradeSlot] = NULL_SLOT; - - _player->pTrader->GetSession()->SendUpdateTrade(); -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectAccessor.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "Player.h"
+#include "Item.h"
+#include "SocialMgr.h"
+#include "Language.h"
+
+enum TradeStatus
+{
+ TRADE_STATUS_BUSY = 0,
+ TRADE_STATUS_BEGIN_TRADE = 1,
+ TRADE_STATUS_OPEN_WINDOW = 2,
+ TRADE_STATUS_TRADE_CANCELED = 3,
+ TRADE_STATUS_TRADE_ACCEPT = 4,
+ TRADE_STATUS_BUSY_2 = 5,
+ TRADE_STATUS_NO_TARGET = 6,
+ TRADE_STATUS_BACK_TO_TRADE = 7,
+ TRADE_STATUS_TRADE_COMPLETE = 8,
+ // 9?
+ TRADE_STATUS_TARGET_TO_FAR = 10,
+ TRADE_STATUS_WRONG_FACTION = 11,
+ TRADE_STATUS_CLOSE_WINDOW = 12,
+ // 13?
+ TRADE_STATUS_IGNORE_YOU = 14,
+ TRADE_STATUS_YOU_STUNNED = 15,
+ TRADE_STATUS_TARGET_STUNNED = 16,
+ TRADE_STATUS_YOU_DEAD = 17,
+ TRADE_STATUS_TARGET_DEAD = 18,
+ TRADE_STATUS_YOU_LOGOUT = 19,
+ TRADE_STATUS_TARGET_LOGOUT = 20,
+ TRADE_STATUS_TRIAL_ACCOUNT = 21, // Trial accounts can not perform that action
+ TRADE_STATUS_ONLY_CONJURED = 22 // You can only trade conjured items... (cross realm BG related).
+};
+
+void WorldSession::SendTradeStatus(uint32 status)
+{
+ WorldPacket data;
+
+ switch(status)
+ {
+ case TRADE_STATUS_BEGIN_TRADE:
+ data.Initialize(SMSG_TRADE_STATUS, 4+8);
+ data << uint32(status);
+ data << uint64(0);
+ break;
+ case TRADE_STATUS_OPEN_WINDOW:
+ data.Initialize(SMSG_TRADE_STATUS, 4+4);
+ data << uint32(status);
+ data << uint32(0); // added in 2.4.0
+ break;
+ case TRADE_STATUS_CLOSE_WINDOW:
+ data.Initialize(SMSG_TRADE_STATUS, 4+4+1+4);
+ data << uint32(status);
+ data << uint32(0);
+ data << uint8(0);
+ data << uint32(0);
+ break;
+ case TRADE_STATUS_ONLY_CONJURED:
+ data.Initialize(SMSG_TRADE_STATUS, 4+1);
+ data << uint32(status);
+ data << uint8(0);
+ break;
+ default:
+ data.Initialize(SMSG_TRADE_STATUS, 4);
+ data << uint32(status);
+ break;
+ }
+
+ SendPacket(&data);
+}
+
+void WorldSession::HandleIgnoreTradeOpcode(WorldPacket& /*recvPacket*/)
+{
+ sLog.outDebug( "WORLD: Ignore Trade %u",_player->GetGUIDLow());
+ // recvPacket.print_storage();
+}
+
+void WorldSession::HandleBusyTradeOpcode(WorldPacket& /*recvPacket*/)
+{
+ sLog.outDebug( "WORLD: Busy Trade %u",_player->GetGUIDLow());
+ // recvPacket.print_storage();
+}
+
+void WorldSession::SendUpdateTrade()
+{
+ Item *item = NULL;
+
+ if( !_player || !_player->pTrader )
+ return;
+
+ // reset trade status
+ if (_player->acceptTrade)
+ {
+ _player->acceptTrade = false;
+ SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ }
+
+ if (_player->pTrader->acceptTrade)
+ {
+ _player->pTrader->acceptTrade = false;
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ }
+
+ WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, (100)); // guess size
+ data << (uint8 ) 1; // can be different (only seen 0 and 1)
+ data << (uint32) 0; // added in 2.4.0, this value must be equal to value from TRADE_STATUS_OPEN_WINDOW status packet (different value for different players to block multiple trades?)
+ data << (uint32) TRADE_SLOT_COUNT; // trade slots count/number?, = next field in most cases
+ data << (uint32) TRADE_SLOT_COUNT; // trade slots count/number?, = prev field in most cases
+ data << (uint32) _player->pTrader->tradeGold; // trader gold
+ data << (uint32) 0; // spell casted on lowest slot item
+
+ for(uint8 i = 0; i < TRADE_SLOT_COUNT; i++)
+ {
+ item = (_player->pTrader->tradeItems[i] != NULL_SLOT ? _player->pTrader->GetItemByPos( _player->pTrader->tradeItems[i] ) : NULL);
+
+ data << (uint8) i; // trade slot number, if not specified, then end of packet
+
+ if(item)
+ {
+ data << (uint32) item->GetProto()->ItemId; // entry
+ // display id
+ data << (uint32) item->GetProto()->DisplayInfoID;
+ // stack count
+ data << (uint32) item->GetUInt32Value(ITEM_FIELD_STACK_COUNT);
+ data << (uint32) 0; // probably gift=1, created_by=0?
+ // gift creator
+ data << (uint64) item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR);
+ data << (uint32) item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT);
+ for(uint8 j = 0; j < 3; ++j)
+ data << (uint32) 0; // enchantment id (permanent/gems?)
+ // creator
+ data << (uint64) item->GetUInt64Value(ITEM_FIELD_CREATOR);
+ data << (uint32) item->GetSpellCharges(); // charges
+ data << (uint32) item->GetItemSuffixFactor(); // SuffixFactor
+ // random properties id
+ data << (uint32) item->GetItemRandomPropertyId();
+ data << (uint32) item->GetProto()->LockID; // lock id
+ // max durability
+ data << (uint32) item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
+ // durability
+ data << (uint32) item->GetUInt32Value(ITEM_FIELD_DURABILITY);
+ }
+ else
+ {
+ for(uint8 j = 0; j < 18; j++)
+ data << uint32(0);
+ }
+ }
+ SendPacket(&data);
+}
+
+//==============================================================
+// transfer the items to the players
+
+void WorldSession::moveItems(Item* myItems[], Item* hisItems[])
+{
+ for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++)
+ {
+ ItemPosCountVec traderDst;
+ ItemPosCountVec playerDst;
+ bool traderCanTrade = (myItems[i]==NULL || _player->pTrader->CanStoreItem( NULL_BAG, NULL_SLOT, traderDst, myItems[i], false ) == EQUIP_ERR_OK);
+ bool playerCanTrade = (hisItems[i]==NULL || _player->CanStoreItem( NULL_BAG, NULL_SLOT, playerDst, hisItems[i], false ) == EQUIP_ERR_OK);
+ if(traderCanTrade && playerCanTrade )
+ {
+ // Ok, if trade item exists and can be stored
+ // If we trade in both directions we had to check, if the trade will work before we actually do it
+ // A roll back is not possible after we stored it
+ if(myItems[i])
+ {
+ // logging
+ sLog.outDebug("partner storing: %u",myItems[i]->GetGUIDLow());
+ if( _player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ sLog.outCommand("GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
+ _player->GetName(),_player->GetSession()->GetAccountId(),
+ myItems[i]->GetProto()->Name1,myItems[i]->GetEntry(),myItems[i]->GetCount(),
+ _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId());
+
+ // store
+ _player->pTrader->MoveItemToInventory( traderDst, myItems[i], true, true);
+ }
+ if(hisItems[i])
+ {
+ // logging
+ sLog.outDebug("player storing: %u",hisItems[i]->GetGUIDLow());
+ if( _player->pTrader->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ sLog.outCommand("GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
+ _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId(),
+ hisItems[i]->GetProto()->Name1,hisItems[i]->GetEntry(),hisItems[i]->GetCount(),
+ _player->GetName(),_player->GetSession()->GetAccountId());
+
+ // store
+ _player->MoveItemToInventory( playerDst, hisItems[i], true, true);
+ }
+ }
+ else
+ {
+ // in case of fatal error log error message
+ // return the already removed items to the original owner
+ if(myItems[i])
+ {
+ if(!traderCanTrade)
+ sLog.outError("trader can't store item: %u",myItems[i]->GetGUIDLow());
+ if(_player->CanStoreItem( NULL_BAG, NULL_SLOT, playerDst, myItems[i], false ) == EQUIP_ERR_OK)
+ _player->MoveItemToInventory(playerDst, myItems[i], true, true);
+ else
+ sLog.outError("player can't take item back: %u",myItems[i]->GetGUIDLow());
+ }
+ // return the already removed items to the original owner
+ if(hisItems[i])
+ {
+ if(!playerCanTrade)
+ sLog.outError("player can't store item: %u",hisItems[i]->GetGUIDLow());
+ if(_player->pTrader->CanStoreItem( NULL_BAG, NULL_SLOT, traderDst, hisItems[i], false ) == EQUIP_ERR_OK)
+ _player->pTrader->MoveItemToInventory(traderDst, hisItems[i], true, true);
+ else
+ sLog.outError("trader can't take item back: %u",hisItems[i]->GetGUIDLow());
+ }
+ }
+ }
+}
+
+//==============================================================
+
+void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
+{
+ Item *myItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL };
+ Item *hisItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL };
+ bool myCanCompleteTrade=true,hisCanCompleteTrade=true;
+
+ if ( !GetPlayer()->pTrader )
+ return;
+
+ // not accept case incorrect money amount
+ if( _player->tradeGold > _player->GetMoney() )
+ {
+ SendNotification(LANG_NOT_ENOUGH_GOLD);
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ _player->acceptTrade = false;
+ return;
+ }
+
+ // not accept case incorrect money amount
+ if( _player->pTrader->tradeGold > _player->pTrader->GetMoney() )
+ {
+ _player->pTrader->GetSession( )->SendNotification(LANG_NOT_ENOUGH_GOLD);
+ SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ _player->pTrader->acceptTrade = false;
+ return;
+ }
+
+ // not accept if some items now can't be trade (cheating)
+ for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++)
+ {
+ if(_player->tradeItems[i] != NULL_SLOT )
+ {
+ if(Item* item =_player->GetItemByPos( _player->tradeItems[i] ))
+ {
+ if(!item->CanBeTraded())
+ {
+ SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
+ return;
+ }
+ }
+ }
+ if(_player->pTrader->tradeItems[i] != NULL_SLOT)
+ {
+ if(Item* item =_player->pTrader->GetItemByPos( _player->pTrader->tradeItems[i]) )
+ {
+ if(!item->CanBeTraded())
+ {
+ SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
+ return;
+ }
+ }
+ }
+ }
+
+ _player->acceptTrade = true;
+ if (_player->pTrader->acceptTrade )
+ {
+ // inform partner client
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT);
+
+ // store items in local list and set 'in-trade' flag
+ for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++)
+ {
+ if(_player->tradeItems[i] != NULL_SLOT )
+ {
+ sLog.outDebug("player trade item bag: %u slot: %u",_player->tradeItems[i] >> 8, _player->tradeItems[i] & 255 );
+ //Can return NULL
+ myItems[i]=_player->GetItemByPos( _player->tradeItems[i] );
+ if (myItems[i])
+ myItems[i]->SetInTrade();
+ }
+ if(_player->pTrader->tradeItems[i] != NULL_SLOT)
+ {
+ sLog.outDebug("partner trade item bag: %u slot: %u",_player->pTrader->tradeItems[i] >> 8,_player->pTrader->tradeItems[i] & 255);
+ //Can return NULL
+ hisItems[i]=_player->pTrader->GetItemByPos( _player->pTrader->tradeItems[i]);
+ if(hisItems[i])
+ hisItems[i]->SetInTrade();
+ }
+ }
+
+ // test if item will fit in each inventory
+ hisCanCompleteTrade = (_player->pTrader->CanStoreItems( myItems,TRADE_SLOT_TRADED_COUNT )== EQUIP_ERR_OK);
+ myCanCompleteTrade = (_player->CanStoreItems( hisItems,TRADE_SLOT_TRADED_COUNT ) == EQUIP_ERR_OK);
+
+ // clear 'in-trade' flag
+ for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++)
+ {
+ if(myItems[i]) myItems[i]->SetInTrade(false);
+ if(hisItems[i]) hisItems[i]->SetInTrade(false);
+ }
+
+ // in case of missing space report error
+ if(!myCanCompleteTrade)
+ {
+ SendNotification(LANG_NOT_FREE_TRADE_SLOTS);
+ GetPlayer( )->pTrader->GetSession( )->SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS);
+ SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ return;
+ }
+ else if (!hisCanCompleteTrade)
+ {
+ SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS);
+ GetPlayer()->pTrader->GetSession()->SendNotification(LANG_NOT_FREE_TRADE_SLOTS);
+ SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ return;
+ }
+
+ // execute trade: 1. remove
+ for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++)
+ {
+ if(myItems[i])
+ {
+ myItems[i]->SetUInt64Value( ITEM_FIELD_GIFTCREATOR,_player->GetGUID());
+ _player->MoveItemFromInventory(_player->tradeItems[i] >> 8, _player->tradeItems[i] & 255, true);
+ }
+ if(hisItems[i])
+ {
+ hisItems[i]->SetUInt64Value( ITEM_FIELD_GIFTCREATOR,_player->pTrader->GetGUID());
+ _player->pTrader->MoveItemFromInventory(_player->pTrader->tradeItems[i] >> 8, _player->pTrader->tradeItems[i] & 255, true);
+ }
+ }
+
+ // execute trade: 2. store
+ moveItems(myItems, hisItems);
+
+ // logging money
+ if(sWorld.getConfig(CONFIG_GM_LOG_TRADE))
+ {
+ if( _player->GetSession()->GetSecurity() > SEC_PLAYER && _player->tradeGold > 0)
+ sLog.outCommand("GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)",
+ _player->GetName(),_player->GetSession()->GetAccountId(),
+ _player->tradeGold,
+ _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId());
+ if( _player->pTrader->GetSession()->GetSecurity() > SEC_PLAYER && _player->pTrader->tradeGold > 0)
+ sLog.outCommand("GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)",
+ _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId(),
+ _player->pTrader->tradeGold,
+ _player->GetName(),_player->GetSession()->GetAccountId());
+ }
+
+ // update money
+ _player->ModifyMoney( -int32(_player->tradeGold) );
+ _player->ModifyMoney(_player->pTrader->tradeGold );
+ _player->pTrader->ModifyMoney( -int32(_player->pTrader->tradeGold) );
+ _player->pTrader->ModifyMoney(_player->tradeGold );
+
+ _player->ClearTrade();
+ _player->pTrader->ClearTrade();
+
+ // desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards)
+ CharacterDatabase.BeginTransaction();
+ _player->SaveInventoryAndGoldToDB();
+ _player->pTrader->SaveInventoryAndGoldToDB();
+ CharacterDatabase.CommitTransaction();
+
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE);
+ SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE);
+
+ _player->pTrader->pTrader = NULL;
+ _player->pTrader = NULL;
+ }
+ else
+ {
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT);
+ }
+}
+
+void WorldSession::HandleUnacceptTradeOpcode(WorldPacket& /*recvPacket*/)
+{
+ if ( !GetPlayer()->pTrader )
+ return;
+
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ _player->acceptTrade = false;
+}
+
+void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/)
+{
+ if(!_player->pTrader)
+ return;
+
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW);
+ _player->pTrader->ClearTrade();
+
+ SendTradeStatus(TRADE_STATUS_OPEN_WINDOW);
+ _player->ClearTrade();
+}
+
+void WorldSession::SendCancelTrade()
+{
+ SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
+}
+
+void WorldSession::HandleCancelTradeOpcode(WorldPacket& /*recvPacket*/)
+{
+ // sended also after LOGOUT COMPLETE
+ if(_player) // needed because STATUS_AUTHED
+ _player->TradeCancel(true);
+}
+
+void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,8);
+
+ if( GetPlayer()->pTrader )
+ return;
+
+ uint64 ID;
+
+ if( !GetPlayer()->isAlive() )
+ {
+ SendTradeStatus(TRADE_STATUS_YOU_DEAD);
+ return;
+ }
+
+ if( GetPlayer()->hasUnitState(UNIT_STAT_STUNNED) )
+ {
+ SendTradeStatus(TRADE_STATUS_YOU_STUNNED);
+ return;
+ }
+
+ if( isLogingOut() )
+ {
+ SendTradeStatus(TRADE_STATUS_YOU_LOGOUT);
+ return;
+ }
+
+ if( GetPlayer()->isInFlight() )
+ {
+ SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
+ return;
+ }
+
+ recvPacket >> ID;
+
+ Player* pOther = ObjectAccessor::FindPlayer( ID );
+
+ if( !pOther )
+ {
+ SendTradeStatus(TRADE_STATUS_NO_TARGET);
+ return;
+ }
+
+ if( pOther == GetPlayer() || pOther->pTrader )
+ {
+ SendTradeStatus(TRADE_STATUS_BUSY);
+ return;
+ }
+
+ if( !pOther->isAlive() )
+ {
+ SendTradeStatus(TRADE_STATUS_TARGET_DEAD);
+ return;
+ }
+
+ if( pOther->isInFlight() )
+ {
+ SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
+ return;
+ }
+
+ if( pOther->hasUnitState(UNIT_STAT_STUNNED) )
+ {
+ SendTradeStatus(TRADE_STATUS_TARGET_STUNNED);
+ return;
+ }
+
+ if( pOther->GetSession()->isLogingOut() )
+ {
+ SendTradeStatus(TRADE_STATUS_TARGET_LOGOUT);
+ return;
+ }
+
+ if( pOther->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()) )
+ {
+ SendTradeStatus(TRADE_STATUS_IGNORE_YOU);
+ return;
+ }
+
+ if(pOther->GetTeam() !=_player->GetTeam() )
+ {
+ SendTradeStatus(TRADE_STATUS_WRONG_FACTION);
+ return;
+ }
+
+ if( pOther->GetDistance2d( _player ) > 10.0f )
+ {
+ SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
+ return;
+ }
+
+ // OK start trade
+ _player->pTrader = pOther;
+ pOther->pTrader =_player;
+
+ WorldPacket data(SMSG_TRADE_STATUS, 12);
+ data << (uint32) TRADE_STATUS_BEGIN_TRADE;
+ data << (uint64)_player->GetGUID();
+ _player->pTrader->GetSession()->SendPacket(&data);
+}
+
+void WorldSession::HandleSetTradeGoldOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,4);
+
+ if(!_player->pTrader)
+ return;
+
+ uint32 gold;
+
+ recvPacket >> gold;
+
+ // gold can be incorrect, but this is checked at trade finished.
+ _player->tradeGold = gold;
+
+ _player->pTrader->GetSession()->SendUpdateTrade();
+}
+
+void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,1+1+1);
+
+ if(!_player->pTrader)
+ return;
+
+ // send update
+ uint8 tradeSlot;
+ uint8 bag;
+ uint8 slot;
+
+ recvPacket >> tradeSlot;
+ recvPacket >> bag;
+ recvPacket >> slot;
+
+ // invalid slot number
+ if(tradeSlot >= TRADE_SLOT_COUNT)
+ {
+ SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
+ return;
+ }
+
+ // check cheating, can't fail with correct client operations
+ Item* item = _player->GetItemByPos(bag,slot);
+ if(!item || tradeSlot!=TRADE_SLOT_NONTRADED && !item->CanBeTraded())
+ {
+ SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
+ return;
+ }
+
+ uint16 pos = (bag << 8) | slot;
+
+ // prevent place single item into many trade slots using cheating and client bugs
+ for(int i = 0; i < TRADE_SLOT_COUNT; ++i)
+ {
+ if(_player->tradeItems[i]==pos)
+ {
+ // cheating attempt
+ SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
+ return;
+ }
+ }
+
+ _player->tradeItems[tradeSlot] = pos;
+
+ _player->pTrader->GetSession()->SendUpdateTrade();
+}
+
+void WorldSession::HandleClearTradeItemOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,1);
+
+ if(!_player->pTrader)
+ return;
+
+ uint8 tradeSlot;
+ recvPacket >> tradeSlot;
+
+ // invalid slot number
+ if(tradeSlot >= TRADE_SLOT_COUNT)
+ return;
+
+ _player->tradeItems[tradeSlot] = NULL_SLOT;
+
+ _player->pTrader->GetSession()->SendUpdateTrade();
+}
diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 6acd2e077c7..bf4e98d427e 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -470,7 +470,7 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa if(pVictim != this)
RemoveSpellsCausingAura(SPELL_AURA_MOD_INVISIBILITY);
- if(pVictim->GetTypeId() == TYPEID_PLAYER && !pVictim->IsStandState() && !pVictim->hasUnitState(UNIT_STAT_STUNDED))
+ if(pVictim->GetTypeId() == TYPEID_PLAYER && !pVictim->IsStandState() && !pVictim->hasUnitState(UNIT_STAT_STUNNED))
pVictim->SetStandState(PLAYER_STATE_NONE);
}
@@ -2149,7 +2149,7 @@ void Unit::DoAttackDamage (Unit *pVictim, uint32 *damage, CleanDamage *cleanDama void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool extra )
{
- if(hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_STUNDED | UNIT_STAT_FLEEING) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) )
+ if(hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_STUNNED | UNIT_STAT_FLEEING) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) )
return;
if (!pVictim->isAlive())
@@ -2961,7 +2961,7 @@ uint32 Unit::GetDefenseSkillValue(Unit const* target) const float Unit::GetUnitDodgeChance() const
{
- if(hasUnitState(UNIT_STAT_STUNDED))
+ if(hasUnitState(UNIT_STAT_STUNNED))
return 0.0f;
if( GetTypeId() == TYPEID_PLAYER )
return GetFloatValue(PLAYER_DODGE_PERCENTAGE);
@@ -2980,7 +2980,7 @@ float Unit::GetUnitDodgeChance() const float Unit::GetUnitParryChance() const
{
- if ( IsNonMeleeSpellCasted(false) || hasUnitState(UNIT_STAT_STUNDED))
+ if ( IsNonMeleeSpellCasted(false) || hasUnitState(UNIT_STAT_STUNNED))
return 0.0f;
float chance = 0.0f;
@@ -3012,7 +3012,7 @@ float Unit::GetUnitParryChance() const float Unit::GetUnitBlockChance() const
{
- if ( IsNonMeleeSpellCasted(false) || hasUnitState(UNIT_STAT_STUNDED))
+ if ( IsNonMeleeSpellCasted(false) || hasUnitState(UNIT_STAT_STUNNED))
return 0.0f;
if(GetTypeId() == TYPEID_PLAYER)
@@ -7985,7 +7985,7 @@ bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges) //FIX ME this hack: don't get feared if stunned
if (spellInfo->Mechanic == MECHANIC_FEAR )
{
- if ( hasUnitState(UNIT_STAT_STUNDED) )
+ if ( hasUnitState(UNIT_STAT_STUNNED) )
return true;
}
@@ -8662,7 +8662,7 @@ bool Unit::isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList) return true;
//If a mob or player is stunned he will not be able to detect stealth
- if (u->hasUnitState(UNIT_STAT_STUNDED) && (u != this))
+ if (u->hasUnitState(UNIT_STAT_STUNNED) && (u != this))
return false;
// Creature can detect target only in aggro radius
@@ -9136,7 +9136,7 @@ bool Unit::SelectHostilTarget() if(target)
{
- if(!hasUnitState(UNIT_STAT_STUNDED))
+ if(!hasUnitState(UNIT_STAT_STUNNED))
SetInFront(target);
((Creature*)this)->AI()->AttackStart(target);
return true;
diff --git a/src/game/Unit.h b/src/game/Unit.h index a46a6de8106..5641f5a64df 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -346,7 +346,7 @@ enum UnitState UNIT_STAT_DIED = 0x0001,
UNIT_STAT_MELEE_ATTACKING = 0x0002, // player is melee attacking someone
//UNIT_STAT_MELEE_ATTACK_BY = 0x0004, // player is melee attack by someone
- UNIT_STAT_STUNDED = 0x0008,
+ UNIT_STAT_STUNNED = 0x0008,
UNIT_STAT_ROAMING = 0x0010,
UNIT_STAT_CHASE = 0x0020,
UNIT_STAT_SEARCHING = 0x0040,
@@ -756,7 +756,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject bool CanFreeMove() const
{
return !hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_FLEEING | UNIT_STAT_IN_FLIGHT |
- UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED ) && GetOwnerGUID()==0;
+ UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED ) && GetOwnerGUID()==0;
}
uint32 getLevel() const { return GetUInt32Value(UNIT_FIELD_LEVEL); }
diff --git a/src/game/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp index b998b61fa8a..fee4c216293 100644 --- a/src/game/WaypointMovementGenerator.cpp +++ b/src/game/WaypointMovementGenerator.cpp @@ -85,7 +85,7 @@ WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &di // Waypoint movement can be switched on/off
// This is quite handy for escort quests and other stuff
- if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED))
+ if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED))
return true;
// prevent a crash at empty waypoint path.
diff --git a/src/game/World.cpp b/src/game/World.cpp index 9ce6888a08a..b195e394f36 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -642,11 +642,14 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_MIN_PETITION_SIGNS] = 9;
}
- m_configs[CONFIG_GM_WISPERING_TO] = sConfig.GetBoolDefault("GM.WhisperingTo",false);
- m_configs[CONFIG_GM_IN_GM_LIST] = sConfig.GetBoolDefault("GM.InGMList",false);
- m_configs[CONFIG_GM_IN_WHO_LIST] = sConfig.GetBoolDefault("GM.InWhoList",false);
- m_configs[CONFIG_GM_LOGIN_STATE] = sConfig.GetIntDefault("GM.LoginState",2);
- m_configs[CONFIG_GM_LOG_TRADE] = sConfig.GetBoolDefault("GM.LogTrade", false);
+ m_configs[CONFIG_GM_LOGIN_STATE] = sConfig.GetIntDefault("GM.LoginState",2);
+ m_configs[CONFIG_GM_ACCEPT_TICKETS] = sConfig.GetIntDefault("GM.AcceptTickets",2);
+ m_configs[CONFIG_GM_CHAT] = sConfig.GetIntDefault("GM.Chat",2);
+ m_configs[CONFIG_GM_WISPERING_TO] = sConfig.GetIntDefault("GM.WhisperingTo",2);
+
+ m_configs[CONFIG_GM_IN_GM_LIST] = sConfig.GetBoolDefault("GM.InGMList",false);
+ m_configs[CONFIG_GM_IN_WHO_LIST] = sConfig.GetBoolDefault("GM.InWhoList",false);
+ m_configs[CONFIG_GM_LOG_TRADE] = sConfig.GetBoolDefault("GM.LogTrade", false);
m_configs[CONFIG_GROUP_VISIBILITY] = sConfig.GetIntDefault("Visibility.GroupMode",0);
diff --git a/src/game/World.h b/src/game/World.h index fd15391405a..8543c94648b 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -1,540 +1,542 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 - */ - -/// \addtogroup world The World -/// @{ -/// \file - -#ifndef __WORLD_H -#define __WORLD_H - -#include "Common.h" -#include "Timer.h" -#include "Policies/Singleton.h" - -#include <map> -#include <set> -#include <list> - -class Object; -class WorldPacket; -class WorldSession; -class Player; -class Weather; -struct ScriptAction; -struct ScriptInfo; -class CliCommandHolder; -class SqlResultQueue; -class QueryResult; -class WorldSocket; - -enum ShutdownMask -{ - SHUTDOWN_MASK_RESTART = 1, - SHUTDOWN_MASK_IDLE = 2, -}; - -/// 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_COUNT = 7 -}; - -/// Configuration elements -enum WorldConfigs -{ - CONFIG_COMPRESSION = 0, - CONFIG_GRID_UNLOAD, - CONFIG_INTERVAL_SAVE, - CONFIG_INTERVAL_GRIDCLEAN, - CONFIG_INTERVAL_MAPUPDATE, - CONFIG_INTERVAL_CHANGEWEATHER, - CONFIG_PORT_WORLD, - CONFIG_SOCKET_SELECTTIME, - CONFIG_GROUP_XP_DISTANCE, - CONFIG_SIGHT_MONSTER, - CONFIG_SIGHT_GUARDER, - CONFIG_GAME_TYPE, - CONFIG_REALM_ZONE, - CONFIG_ALLOW_TWO_SIDE_ACCOUNTS, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL, - CONFIG_ALLOW_TWO_SIDE_WHO_LIST, - CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND, - CONFIG_STRICT_PLAYER_NAMES, - CONFIG_STRICT_CHARTER_NAMES, - CONFIG_STRICT_PET_NAMES, - CONFIG_CHARACTERS_CREATING_DISABLED, - CONFIG_CHARACTERS_PER_ACCOUNT, - CONFIG_CHARACTERS_PER_REALM, - CONFIG_SKIP_CINEMATICS, - CONFIG_MAX_PLAYER_LEVEL, - CONFIG_START_PLAYER_LEVEL, - CONFIG_MAX_HONOR_POINTS, - CONFIG_MAX_ARENA_POINTS, - CONFIG_INSTANCE_IGNORE_LEVEL, - CONFIG_INSTANCE_IGNORE_RAID, - CONFIG_BATTLEGROUND_CAST_DESERTER, - CONFIG_INSTANCE_RESET_TIME_HOUR, - CONFIG_INSTANCE_UNLOAD_DELAY, - CONFIG_CAST_UNSTUCK, - CONFIG_MAX_PRIMARY_TRADE_SKILL, - CONFIG_MIN_PETITION_SIGNS, - CONFIG_GM_WISPERING_TO, - CONFIG_GM_IN_GM_LIST, - CONFIG_GM_IN_WHO_LIST, - CONFIG_GM_LOGIN_STATE, - CONFIG_GM_LOG_TRADE, - CONFIG_GROUP_VISIBILITY, - CONFIG_MAIL_DELIVERY_DELAY, - CONFIG_UPTIME_UPDATE, - CONFIG_SKILL_CHANCE_ORANGE, - CONFIG_SKILL_CHANCE_YELLOW, - CONFIG_SKILL_CHANCE_GREEN, - CONFIG_SKILL_CHANCE_GREY, - CONFIG_SKILL_CHANCE_MINING_STEPS, - CONFIG_SKILL_CHANCE_SKINNING_STEPS, - CONFIG_SKILL_PROSPECTING, - CONFIG_SKILL_GAIN_CRAFTING, - CONFIG_SKILL_GAIN_DEFENSE, - CONFIG_SKILL_GAIN_GATHERING, - CONFIG_SKILL_GAIN_WEAPON, - CONFIG_MAX_OVERSPEED_PINGS, - CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY, - CONFIG_WEATHER, - CONFIG_EXPANSION, - CONFIG_CHATFLOOD_MESSAGE_COUNT, - CONFIG_CHATFLOOD_MESSAGE_DELAY, - CONFIG_CHATFLOOD_MUTE_TIME, - CONFIG_EVENT_ANNOUNCE, - CONFIG_CREATURE_FAMILY_ASSISTEMCE_RADIUS, - CONFIG_WORLD_BOSS_LEVEL_DIFF, - CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF, - CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF, - CONFIG_RESTRICTED_LFG_CHANNEL, - CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL, - CONFIG_TALENTS_INSPECTING, - CONFIG_CHAT_FAKE_MESSAGE_PREVENTING, - CONFIG_TCP_NO_DELAY, - CONFIG_CORPSE_DECAY_NORMAL, - CONFIG_CORPSE_DECAY_RARE, - CONFIG_CORPSE_DECAY_ELITE, - CONFIG_CORPSE_DECAY_RAREELITE, - CONFIG_CORPSE_DECAY_WORLDBOSS, - CONFIG_ADDON_CHANNEL, - CONFIG_DEATH_SICKNESS_LEVEL, - CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP, - CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE, - CONFIG_THREAT_RADIUS, - CONFIG_DECLINED_NAMES_USED, - CONFIG_LISTEN_RANGE_SAY, - CONFIG_LISTEN_RANGE_TEXTEMOTE, - CONFIG_LISTEN_RANGE_YELL, - CONFIG_ARENA_MAX_RATING_DIFFERENCE, - CONFIG_ARENA_RATING_DISCARD_TIMER, - CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS, - CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS, - CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER, - CONFIG_VALUE_COUNT -}; - -/// Server rates -enum Rates -{ - RATE_HEALTH=0, - RATE_POWER_MANA, - RATE_POWER_RAGE_INCOME, - RATE_POWER_RAGE_LOSS, - RATE_POWER_FOCUS, - RATE_SKILL_DISCOVERY, - RATE_DROP_ITEM_POOR, - RATE_DROP_ITEM_NORMAL, - RATE_DROP_ITEM_UNCOMMON, - RATE_DROP_ITEM_RARE, - RATE_DROP_ITEM_EPIC, - RATE_DROP_ITEM_LEGENDARY, - RATE_DROP_ITEM_ARTIFACT, - RATE_DROP_ITEM_REFERENCED, - RATE_DROP_MONEY, - RATE_XP_KILL, - RATE_XP_QUEST, - RATE_XP_EXPLORE, - RATE_XP_PAST_70, - RATE_REPUTATION_GAIN, - RATE_CREATURE_NORMAL_HP, - RATE_CREATURE_ELITE_ELITE_HP, - RATE_CREATURE_ELITE_RAREELITE_HP, - RATE_CREATURE_ELITE_WORLDBOSS_HP, - RATE_CREATURE_ELITE_RARE_HP, - RATE_CREATURE_NORMAL_DAMAGE, - RATE_CREATURE_ELITE_ELITE_DAMAGE, - RATE_CREATURE_ELITE_RAREELITE_DAMAGE, - RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE, - RATE_CREATURE_ELITE_RARE_DAMAGE, - RATE_CREATURE_NORMAL_SPELLDAMAGE, - RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE, - RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE, - RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE, - RATE_CREATURE_ELITE_RARE_SPELLDAMAGE, - RATE_CREATURE_AGGRO, - RATE_REST_INGAME, - RATE_REST_OFFLINE_IN_TAVERN_OR_CITY, - RATE_REST_OFFLINE_IN_WILDERNESS, - RATE_DAMAGE_FALL, - RATE_AUCTION_TIME, - RATE_AUCTION_DEPOSIT, - RATE_AUCTION_CUT, - RATE_HONOR, - RATE_MINING_AMOUNT, - RATE_MINING_NEXT, - RATE_TALENT, - RATE_LOYALTY, - RATE_CORPSE_DECAY_LOOTED, - RATE_INSTANCE_RESET_TIME, - RATE_TARGET_POS_RECALCULATION_RANGE, - RATE_DURABILITY_LOSS_DAMAGE, - RATE_DURABILITY_LOSS_PARRY, - RATE_DURABILITY_LOSS_ABSORB, - RATE_DURABILITY_LOSS_BLOCK, - MAX_RATES -}; - -/// Type of server -enum RealmType -{ - REALM_TYPE_NORMAL = 0, - REALM_TYPE_PVP = 1, - REALM_TYPE_NORMAL2 = 4, - REALM_TYPE_RP = 6, - REALM_TYPE_RPPVP = 8, - REALM_TYPE_FFA_PVP = 16 // custom, free for all pvp mode like arena PvP in all zones except rest activated places and sanctuaries - // replaced by REALM_PVP in realm list -}; - -enum RealmZone -{ - REALM_ZONE_UNKNOWN = 0, // any language - REALM_ZONE_DEVELOPMENT = 1, // any language - REALM_ZONE_UNITED_STATES = 2, // extended-Latin - REALM_ZONE_OCEANIC = 3, // extended-Latin - REALM_ZONE_LATIN_AMERICA = 4, // extended-Latin - REALM_ZONE_TOURNAMENT_5 = 5, // basic-Latin at create, any at login - REALM_ZONE_KOREA = 6, // East-Asian - REALM_ZONE_TOURNAMENT_7 = 7, // basic-Latin at create, any at login - REALM_ZONE_ENGLISH = 8, // extended-Latin - REALM_ZONE_GERMAN = 9, // extended-Latin - REALM_ZONE_FRENCH = 10, // extended-Latin - REALM_ZONE_SPANISH = 11, // extended-Latin - REALM_ZONE_RUSSIAN = 12, // Cyrillic - REALM_ZONE_TOURNAMENT_13 = 13, // basic-Latin at create, any at login - REALM_ZONE_TAIWAN = 14, // East-Asian - REALM_ZONE_TOURNAMENT_15 = 15, // basic-Latin at create, any at login - REALM_ZONE_CHINA = 16, // East-Asian - REALM_ZONE_CN1 = 17, // basic-Latin at create, any at login - REALM_ZONE_CN2 = 18, // basic-Latin at create, any at login - REALM_ZONE_CN3 = 19, // basic-Latin at create, any at login - REALM_ZONE_CN4 = 20, // basic-Latin at create, any at login - REALM_ZONE_CN5 = 21, // basic-Latin at create, any at login - REALM_ZONE_CN6 = 22, // basic-Latin at create, any at login - REALM_ZONE_CN7 = 23, // basic-Latin at create, any at login - REALM_ZONE_CN8 = 24, // basic-Latin at create, any at login - REALM_ZONE_TOURNAMENT_25 = 25, // basic-Latin at create, any at login - REALM_ZONE_TEST_SERVER = 26, // any language - REALM_ZONE_TOURNAMENT_27 = 27, // basic-Latin at create, any at login - REALM_ZONE_QA_SERVER = 28, // any language - REALM_ZONE_CN9 = 29 // basic-Latin at create, any at login -}; - -/// Ban function return codes -enum BanReturn -{ - BAN_SUCCESS, - BAN_SYNTAX_ERROR, - BAN_NOTFOUND -}; - -// DB scripting commands -#define SCRIPT_COMMAND_TALK 0 // source = unit, target=any, datalong ( 0=say, 1=whisper, 2=yell, 3=emote text) -#define SCRIPT_COMMAND_EMOTE 1 // source = unit, datalong = anim_id -#define SCRIPT_COMMAND_FIELD_SET 2 // source = any, datalong = field_id, datalog2 = value -#define SCRIPT_COMMAND_MOVE_TO 3 // source = Creature, datalog2 = time, x/y/z -#define SCRIPT_COMMAND_FLAG_SET 4 // source = any, datalong = field_id, datalog2 = bitmask -#define SCRIPT_COMMAND_FLAG_REMOVE 5 // source = any, datalong = field_id, datalog2 = bitmask -#define SCRIPT_COMMAND_TELEPORT_TO 6 // source or target with Player, datalong = map_id, x/y/z -#define SCRIPT_COMMAND_QUEST_EXPLORED 7 // one from source or target must be Player, another GO/Creature, datalong=quest_id, datalong2=distance or 0 -#define SCRIPT_COMMAND_RESPAWN_GAMEOBJECT 9 // source = any (summoner), datalong=db_guid, datalong2=despawn_delay -#define SCRIPT_COMMAND_TEMP_SUMMON_CREATURE 10 // source = any (summoner), datalong=creature entry, datalong2=despawn_delay -#define SCRIPT_COMMAND_OPEN_DOOR 11 // source = unit, datalong=db_guid, datalong2=reset_delay -#define SCRIPT_COMMAND_CLOSE_DOOR 12 // source = unit, datalong=db_guid, datalong2=reset_delay -#define SCRIPT_COMMAND_ACTIVATE_OBJECT 13 // source = unit, target=GO -#define SCRIPT_COMMAND_REMOVE_AURA 14 // source (datalong2!=0) or target (datalong==0) unit, datalong = spell_id -#define SCRIPT_COMMAND_CAST_SPELL 15 // source (datalong2!=0) or target (datalong==0) unit, datalong = spell_id - -/// CLI related stuff, define here to prevent cyclic dependancies - -typedef int(* pPrintf)(const char*,...); -typedef void(* pCliFunc)(char *,pPrintf); - -/// Command Template class -struct CliCommand -{ - char const * cmd; - pCliFunc Func; - char const * description; -}; - -/// Storage class for commands issued for delayed execution -class CliCommandHolder -{ - private: - const CliCommand *cmd; - char *args; - pPrintf m_zprintf; - public: - CliCommandHolder(const CliCommand *command, const char *arguments, pPrintf p_zprintf) - : cmd(command), m_zprintf(p_zprintf) - { - size_t len = strlen(arguments)+1; - args = new char[len]; - memcpy(args, arguments, len); - } - ~CliCommandHolder() { delete[] args; } - void Execute() const { cmd->Func(args, m_zprintf); } - pPrintf GetOutputMethod() const {return (m_zprintf);} -}; - -/// The World -class World -{ - public: - static volatile bool m_stopEvent; - static volatile uint32 m_worldLoopCounter; - - World(); - ~World(); - - WorldSession* FindSession(uint32 id) const; - void AddSession(WorldSession *s); - bool RemoveSession(uint32 id); - /// Get the number of current active sessions - void UpdateMaxSessionCounters(); - uint32 GetActiveAndQueuedSessionCount() const { return m_sessions.size(); } - uint32 GetActiveSessionCount() const { return m_sessions.size() - m_QueuedPlayer.size(); } - uint32 GetQueuedSessionCount() const { return m_QueuedPlayer.size(); } - /// Get the maximum number of parallel sessions on the server since last reboot - uint32 GetMaxQueuedSessionCount() const { return m_maxQueuedSessionCount; } - uint32 GetMaxActiveSessionCount() const { return m_maxActiveSessionCount; } - Player* FindPlayerInZone(uint32 zone); - - Weather* FindWeather(uint32 id) const; - Weather* AddWeather(uint32 zone_id); - void RemoveWeather(uint32 zone_id); - - /// Get the active session server limit (or security level limitations) - uint32 GetPlayerAmountLimit() const { return m_playerLimit >= 0 ? m_playerLimit : 0; } - AccountTypes GetPlayerSecurityLimit() const { return m_playerLimit <= 0 ? AccountTypes(-m_playerLimit) : SEC_PLAYER; } - - /// Set the active session server limit (or security level limitation) - void SetPlayerLimit(int32 limit, bool needUpdate = false); - - //player Queue - typedef std::list<WorldSession*> Queue; - void AddQueuedPlayer(WorldSession*); - void RemoveQueuedPlayer(WorldSession*); - int32 GetQueuePos(WorldSession*); - uint32 GetQueueSize() const { return m_QueuedPlayer.size(); } - - /// \todo Actions on m_allowMovement still to be implemented - /// Is movement allowed? - bool getAllowMovement() const { return m_allowMovement; } - /// Allow/Disallow object movements - void SetAllowMovement(bool allow) { m_allowMovement = allow; } - - /// Set a new Message of the Day - void SetMotd(std::string motd) { m_motd = motd; } - /// Get the current Message of the Day - const char* GetMotd() const { return m_motd.c_str(); } - - uint32 GetDefaultDbcLocale() const { return m_defaultDbcLocale; } - - /// Get the path where data (dbc, maps) are stored on disk - std::string GetDataPath() const { return m_dataPath; } - - /// When server started? - time_t const& GetStartTime() const { return m_startTime; } - /// What time is it? - time_t const& GetGameTime() const { return m_gameTime; } - /// Uptime (in secs) - uint32 GetUptime() const { return uint32(m_gameTime - m_startTime); } - - /// Get the maximum skill level a player can reach - uint16 GetConfigMaxSkillValue() const - { - uint32 lvl = getConfig(CONFIG_MAX_PLAYER_LEVEL); - return lvl > 60 ? 300 + ((lvl - 60) * 75) / 10 : lvl*5; - } - - void SetInitialWorldSettings(); - void LoadConfigSettings(bool reload = false); - - void SendWorldText(int32 string_id, ...); - void SendGlobalMessage(WorldPacket *packet, WorldSession *self = 0, uint32 team = 0); - void SendZoneMessage(uint32 zone, WorldPacket *packet, WorldSession *self = 0, uint32 team = 0); - void SendZoneText(uint32 zone, const char *text, WorldSession *self = 0, uint32 team = 0); - void SendServerMessage(uint32 type, const char *text = "", Player* player = NULL); - - /// Are we in the middle of a shutdown? - uint32 GetShutdownMask() const { return m_ShutdownMask; } - bool IsShutdowning() const { return m_ShutdownTimer > 0; } - void ShutdownServ(uint32 time, uint32 options = 0); - void ShutdownCancel(); - void ShutdownMsg(bool show = false, Player* player = NULL); - - void Update(time_t diff); - - void UpdateSessions( time_t diff ); - /// Set a server rate (see #Rates) - void setRate(Rates rate,float value) { rate_values[rate]=value; } - /// Get a server rate (see #Rates) - float getRate(Rates rate) const { return rate_values[rate]; } - - /// Set a server configuration element (see #WorldConfigs) - void setConfig(uint32 index,uint32 value) - { - if(index<CONFIG_VALUE_COUNT) - m_configs[index]=value; - } - - /// Get a server configuration element (see #WorldConfigs) - uint32 getConfig(uint32 index) const - { - if(index<CONFIG_VALUE_COUNT) - return m_configs[index]; - else - return 0; - } - - /// Are we on a "Player versus Player" server? - bool IsPvPRealm() { return (getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP || getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP); } - bool IsFFAPvPRealm() { return getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP; } - - bool KickPlayer(std::string playerName); - void KickAll(); - void KickAllLess(AccountTypes sec); - void KickAllQueued(); - uint8 BanAccount(std::string type, std::string nameOrIP, std::string duration, std::string reason, std::string author); - bool RemoveBanAccount(std::string type, std::string nameOrIP); - - void ScriptsStart(std::map<uint32, std::multimap<uint32, ScriptInfo> > const& scripts, uint32 id, Object* source, Object* target); - void ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target); - bool IsScriptScheduled() const { return !m_scriptSchedule.empty(); } - - // for max speed access - static float GetMaxVisibleDistanceForCreature() { return m_MaxVisibleDistanceForCreature; } - static float GetMaxVisibleDistanceForPlayer() { return m_MaxVisibleDistanceForPlayer; } - static float GetMaxVisibleDistanceForObject() { return m_MaxVisibleDistanceForObject; } - static float GetMaxVisibleDistanceInFlight() { return m_MaxVisibleDistanceInFlight; } - static float GetVisibleUnitGreyDistance() { return m_VisibleUnitGreyDistance; } - static float GetVisibleObjectGreyDistance() { return m_VisibleObjectGreyDistance; } - - void ProcessCliCommands(); - void QueueCliCommand(CliCommandHolder* command) { cliCmdQueue.add(command); } - - void UpdateResultQueue(); - void InitResultQueue(); - - void UpdateRealmCharCount(uint32 accid); - - LocaleConstant GetAvailableDbcLocale(LocaleConstant locale) const { if(m_availableDbcLocaleMask & (1 << locale)) return locale; else return m_defaultDbcLocale; } - protected: - void _UpdateGameTime(); - void ScriptsProcess(); - // callback for UpdateRealmCharacters - void _UpdateRealmCharCount(QueryResult *resultCharCount, uint32 accountId); - - void InitDailyQuestResetTime(); - void ResetDailyQuests(); - private: - time_t m_startTime; - time_t m_gameTime; - IntervalTimer m_timers[WUPDATE_COUNT]; - uint32 mail_timer; - uint32 mail_timer_expires; - - typedef HM_NAMESPACE::hash_map<uint32, Weather*> WeatherMap; - WeatherMap m_weathers; - typedef HM_NAMESPACE::hash_map<uint32, WorldSession*> SessionMap; - SessionMap m_sessions; - std::set<WorldSession*> m_kicked_sessions; - uint32 m_maxActiveSessionCount; - uint32 m_maxQueuedSessionCount; - - std::multimap<time_t, ScriptAction> m_scriptSchedule; - - float rate_values[MAX_RATES]; - uint32 m_configs[CONFIG_VALUE_COUNT]; - int32 m_playerLimit; - LocaleConstant m_defaultDbcLocale; // from config for one from loaded DBC locales - uint32 m_availableDbcLocaleMask; // by loaded DBC - void DetectDBCLang(); - bool m_allowMovement; - std::string m_motd; - std::string m_dataPath; - - uint32 m_ShutdownTimer; - uint32 m_ShutdownMask; - - // for max speed access - static float m_MaxVisibleDistanceForCreature; - static float m_MaxVisibleDistanceForPlayer; - static float m_MaxVisibleDistanceForObject; - static float m_MaxVisibleDistanceInFlight; - static float m_VisibleUnitGreyDistance; - static float m_VisibleObjectGreyDistance; - - // CLI command holder to be thread safe - ZThread::LockedQueue<CliCommandHolder*, ZThread::FastMutex> cliCmdQueue; - SqlResultQueue *m_resultQueue; - - // next daily quests reset time - time_t m_NextDailyQuestReset; - - //Player Queue - Queue m_QueuedPlayer; - - //sessions that are added async - void AddSession_(WorldSession* s); - ZThread::LockedQueue<WorldSession*, ZThread::FastMutex> addSessQueue; -}; - -extern uint32 realmID; - -#define sWorld MaNGOS::Singleton<World>::Instance() -#endif -/// @} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/// \addtogroup world The World
+/// @{
+/// \file
+
+#ifndef __WORLD_H
+#define __WORLD_H
+
+#include "Common.h"
+#include "Timer.h"
+#include "Policies/Singleton.h"
+
+#include <map>
+#include <set>
+#include <list>
+
+class Object;
+class WorldPacket;
+class WorldSession;
+class Player;
+class Weather;
+struct ScriptAction;
+struct ScriptInfo;
+class CliCommandHolder;
+class SqlResultQueue;
+class QueryResult;
+class WorldSocket;
+
+enum ShutdownMask
+{
+ SHUTDOWN_MASK_RESTART = 1,
+ SHUTDOWN_MASK_IDLE = 2,
+};
+
+/// 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_COUNT = 7
+};
+
+/// Configuration elements
+enum WorldConfigs
+{
+ CONFIG_COMPRESSION = 0,
+ CONFIG_GRID_UNLOAD,
+ CONFIG_INTERVAL_SAVE,
+ CONFIG_INTERVAL_GRIDCLEAN,
+ CONFIG_INTERVAL_MAPUPDATE,
+ CONFIG_INTERVAL_CHANGEWEATHER,
+ CONFIG_PORT_WORLD,
+ CONFIG_SOCKET_SELECTTIME,
+ CONFIG_GROUP_XP_DISTANCE,
+ CONFIG_SIGHT_MONSTER,
+ CONFIG_SIGHT_GUARDER,
+ CONFIG_GAME_TYPE,
+ CONFIG_REALM_ZONE,
+ CONFIG_ALLOW_TWO_SIDE_ACCOUNTS,
+ CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT,
+ CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL,
+ CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP,
+ CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD,
+ CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION,
+ CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL,
+ CONFIG_ALLOW_TWO_SIDE_WHO_LIST,
+ CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND,
+ CONFIG_STRICT_PLAYER_NAMES,
+ CONFIG_STRICT_CHARTER_NAMES,
+ CONFIG_STRICT_PET_NAMES,
+ CONFIG_CHARACTERS_CREATING_DISABLED,
+ CONFIG_CHARACTERS_PER_ACCOUNT,
+ CONFIG_CHARACTERS_PER_REALM,
+ CONFIG_SKIP_CINEMATICS,
+ CONFIG_MAX_PLAYER_LEVEL,
+ CONFIG_START_PLAYER_LEVEL,
+ CONFIG_MAX_HONOR_POINTS,
+ CONFIG_MAX_ARENA_POINTS,
+ CONFIG_INSTANCE_IGNORE_LEVEL,
+ CONFIG_INSTANCE_IGNORE_RAID,
+ CONFIG_BATTLEGROUND_CAST_DESERTER,
+ CONFIG_INSTANCE_RESET_TIME_HOUR,
+ CONFIG_INSTANCE_UNLOAD_DELAY,
+ CONFIG_CAST_UNSTUCK,
+ CONFIG_MAX_PRIMARY_TRADE_SKILL,
+ CONFIG_MIN_PETITION_SIGNS,
+ CONFIG_GM_LOGIN_STATE,
+ CONFIG_GM_ACCEPT_TICKETS,
+ CONFIG_GM_CHAT,
+ CONFIG_GM_WISPERING_TO,
+ CONFIG_GM_IN_GM_LIST,
+ CONFIG_GM_IN_WHO_LIST,
+ CONFIG_GM_LOG_TRADE,
+ CONFIG_GROUP_VISIBILITY,
+ CONFIG_MAIL_DELIVERY_DELAY,
+ CONFIG_UPTIME_UPDATE,
+ CONFIG_SKILL_CHANCE_ORANGE,
+ CONFIG_SKILL_CHANCE_YELLOW,
+ CONFIG_SKILL_CHANCE_GREEN,
+ CONFIG_SKILL_CHANCE_GREY,
+ CONFIG_SKILL_CHANCE_MINING_STEPS,
+ CONFIG_SKILL_CHANCE_SKINNING_STEPS,
+ CONFIG_SKILL_PROSPECTING,
+ CONFIG_SKILL_GAIN_CRAFTING,
+ CONFIG_SKILL_GAIN_DEFENSE,
+ CONFIG_SKILL_GAIN_GATHERING,
+ CONFIG_SKILL_GAIN_WEAPON,
+ CONFIG_MAX_OVERSPEED_PINGS,
+ CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY,
+ CONFIG_WEATHER,
+ CONFIG_EXPANSION,
+ CONFIG_CHATFLOOD_MESSAGE_COUNT,
+ CONFIG_CHATFLOOD_MESSAGE_DELAY,
+ CONFIG_CHATFLOOD_MUTE_TIME,
+ CONFIG_EVENT_ANNOUNCE,
+ CONFIG_CREATURE_FAMILY_ASSISTEMCE_RADIUS,
+ CONFIG_WORLD_BOSS_LEVEL_DIFF,
+ CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF,
+ CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF,
+ CONFIG_RESTRICTED_LFG_CHANNEL,
+ CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL,
+ CONFIG_TALENTS_INSPECTING,
+ CONFIG_CHAT_FAKE_MESSAGE_PREVENTING,
+ CONFIG_TCP_NO_DELAY,
+ CONFIG_CORPSE_DECAY_NORMAL,
+ CONFIG_CORPSE_DECAY_RARE,
+ CONFIG_CORPSE_DECAY_ELITE,
+ CONFIG_CORPSE_DECAY_RAREELITE,
+ CONFIG_CORPSE_DECAY_WORLDBOSS,
+ CONFIG_ADDON_CHANNEL,
+ CONFIG_DEATH_SICKNESS_LEVEL,
+ CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP,
+ CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE,
+ CONFIG_THREAT_RADIUS,
+ CONFIG_DECLINED_NAMES_USED,
+ CONFIG_LISTEN_RANGE_SAY,
+ CONFIG_LISTEN_RANGE_TEXTEMOTE,
+ CONFIG_LISTEN_RANGE_YELL,
+ CONFIG_ARENA_MAX_RATING_DIFFERENCE,
+ CONFIG_ARENA_RATING_DISCARD_TIMER,
+ CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS,
+ CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS,
+ CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER,
+ CONFIG_VALUE_COUNT
+};
+
+/// Server rates
+enum Rates
+{
+ RATE_HEALTH=0,
+ RATE_POWER_MANA,
+ RATE_POWER_RAGE_INCOME,
+ RATE_POWER_RAGE_LOSS,
+ RATE_POWER_FOCUS,
+ RATE_SKILL_DISCOVERY,
+ RATE_DROP_ITEM_POOR,
+ RATE_DROP_ITEM_NORMAL,
+ RATE_DROP_ITEM_UNCOMMON,
+ RATE_DROP_ITEM_RARE,
+ RATE_DROP_ITEM_EPIC,
+ RATE_DROP_ITEM_LEGENDARY,
+ RATE_DROP_ITEM_ARTIFACT,
+ RATE_DROP_ITEM_REFERENCED,
+ RATE_DROP_MONEY,
+ RATE_XP_KILL,
+ RATE_XP_QUEST,
+ RATE_XP_EXPLORE,
+ RATE_XP_PAST_70,
+ RATE_REPUTATION_GAIN,
+ RATE_CREATURE_NORMAL_HP,
+ RATE_CREATURE_ELITE_ELITE_HP,
+ RATE_CREATURE_ELITE_RAREELITE_HP,
+ RATE_CREATURE_ELITE_WORLDBOSS_HP,
+ RATE_CREATURE_ELITE_RARE_HP,
+ RATE_CREATURE_NORMAL_DAMAGE,
+ RATE_CREATURE_ELITE_ELITE_DAMAGE,
+ RATE_CREATURE_ELITE_RAREELITE_DAMAGE,
+ RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE,
+ RATE_CREATURE_ELITE_RARE_DAMAGE,
+ RATE_CREATURE_NORMAL_SPELLDAMAGE,
+ RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE,
+ RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE,
+ RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE,
+ RATE_CREATURE_ELITE_RARE_SPELLDAMAGE,
+ RATE_CREATURE_AGGRO,
+ RATE_REST_INGAME,
+ RATE_REST_OFFLINE_IN_TAVERN_OR_CITY,
+ RATE_REST_OFFLINE_IN_WILDERNESS,
+ RATE_DAMAGE_FALL,
+ RATE_AUCTION_TIME,
+ RATE_AUCTION_DEPOSIT,
+ RATE_AUCTION_CUT,
+ RATE_HONOR,
+ RATE_MINING_AMOUNT,
+ RATE_MINING_NEXT,
+ RATE_TALENT,
+ RATE_LOYALTY,
+ RATE_CORPSE_DECAY_LOOTED,
+ RATE_INSTANCE_RESET_TIME,
+ RATE_TARGET_POS_RECALCULATION_RANGE,
+ RATE_DURABILITY_LOSS_DAMAGE,
+ RATE_DURABILITY_LOSS_PARRY,
+ RATE_DURABILITY_LOSS_ABSORB,
+ RATE_DURABILITY_LOSS_BLOCK,
+ MAX_RATES
+};
+
+/// Type of server
+enum RealmType
+{
+ REALM_TYPE_NORMAL = 0,
+ REALM_TYPE_PVP = 1,
+ REALM_TYPE_NORMAL2 = 4,
+ REALM_TYPE_RP = 6,
+ REALM_TYPE_RPPVP = 8,
+ REALM_TYPE_FFA_PVP = 16 // custom, free for all pvp mode like arena PvP in all zones except rest activated places and sanctuaries
+ // replaced by REALM_PVP in realm list
+};
+
+enum RealmZone
+{
+ REALM_ZONE_UNKNOWN = 0, // any language
+ REALM_ZONE_DEVELOPMENT = 1, // any language
+ REALM_ZONE_UNITED_STATES = 2, // extended-Latin
+ REALM_ZONE_OCEANIC = 3, // extended-Latin
+ REALM_ZONE_LATIN_AMERICA = 4, // extended-Latin
+ REALM_ZONE_TOURNAMENT_5 = 5, // basic-Latin at create, any at login
+ REALM_ZONE_KOREA = 6, // East-Asian
+ REALM_ZONE_TOURNAMENT_7 = 7, // basic-Latin at create, any at login
+ REALM_ZONE_ENGLISH = 8, // extended-Latin
+ REALM_ZONE_GERMAN = 9, // extended-Latin
+ REALM_ZONE_FRENCH = 10, // extended-Latin
+ REALM_ZONE_SPANISH = 11, // extended-Latin
+ REALM_ZONE_RUSSIAN = 12, // Cyrillic
+ REALM_ZONE_TOURNAMENT_13 = 13, // basic-Latin at create, any at login
+ REALM_ZONE_TAIWAN = 14, // East-Asian
+ REALM_ZONE_TOURNAMENT_15 = 15, // basic-Latin at create, any at login
+ REALM_ZONE_CHINA = 16, // East-Asian
+ REALM_ZONE_CN1 = 17, // basic-Latin at create, any at login
+ REALM_ZONE_CN2 = 18, // basic-Latin at create, any at login
+ REALM_ZONE_CN3 = 19, // basic-Latin at create, any at login
+ REALM_ZONE_CN4 = 20, // basic-Latin at create, any at login
+ REALM_ZONE_CN5 = 21, // basic-Latin at create, any at login
+ REALM_ZONE_CN6 = 22, // basic-Latin at create, any at login
+ REALM_ZONE_CN7 = 23, // basic-Latin at create, any at login
+ REALM_ZONE_CN8 = 24, // basic-Latin at create, any at login
+ REALM_ZONE_TOURNAMENT_25 = 25, // basic-Latin at create, any at login
+ REALM_ZONE_TEST_SERVER = 26, // any language
+ REALM_ZONE_TOURNAMENT_27 = 27, // basic-Latin at create, any at login
+ REALM_ZONE_QA_SERVER = 28, // any language
+ REALM_ZONE_CN9 = 29 // basic-Latin at create, any at login
+};
+
+/// Ban function return codes
+enum BanReturn
+{
+ BAN_SUCCESS,
+ BAN_SYNTAX_ERROR,
+ BAN_NOTFOUND
+};
+
+// DB scripting commands
+#define SCRIPT_COMMAND_TALK 0 // source = unit, target=any, datalong ( 0=say, 1=whisper, 2=yell, 3=emote text)
+#define SCRIPT_COMMAND_EMOTE 1 // source = unit, datalong = anim_id
+#define SCRIPT_COMMAND_FIELD_SET 2 // source = any, datalong = field_id, datalog2 = value
+#define SCRIPT_COMMAND_MOVE_TO 3 // source = Creature, datalog2 = time, x/y/z
+#define SCRIPT_COMMAND_FLAG_SET 4 // source = any, datalong = field_id, datalog2 = bitmask
+#define SCRIPT_COMMAND_FLAG_REMOVE 5 // source = any, datalong = field_id, datalog2 = bitmask
+#define SCRIPT_COMMAND_TELEPORT_TO 6 // source or target with Player, datalong = map_id, x/y/z
+#define SCRIPT_COMMAND_QUEST_EXPLORED 7 // one from source or target must be Player, another GO/Creature, datalong=quest_id, datalong2=distance or 0
+#define SCRIPT_COMMAND_RESPAWN_GAMEOBJECT 9 // source = any (summoner), datalong=db_guid, datalong2=despawn_delay
+#define SCRIPT_COMMAND_TEMP_SUMMON_CREATURE 10 // source = any (summoner), datalong=creature entry, datalong2=despawn_delay
+#define SCRIPT_COMMAND_OPEN_DOOR 11 // source = unit, datalong=db_guid, datalong2=reset_delay
+#define SCRIPT_COMMAND_CLOSE_DOOR 12 // source = unit, datalong=db_guid, datalong2=reset_delay
+#define SCRIPT_COMMAND_ACTIVATE_OBJECT 13 // source = unit, target=GO
+#define SCRIPT_COMMAND_REMOVE_AURA 14 // source (datalong2!=0) or target (datalong==0) unit, datalong = spell_id
+#define SCRIPT_COMMAND_CAST_SPELL 15 // source (datalong2!=0) or target (datalong==0) unit, datalong = spell_id
+
+/// CLI related stuff, define here to prevent cyclic dependancies
+
+typedef int(* pPrintf)(const char*,...);
+typedef void(* pCliFunc)(char *,pPrintf);
+
+/// Command Template class
+struct CliCommand
+{
+ char const * cmd;
+ pCliFunc Func;
+ char const * description;
+};
+
+/// Storage class for commands issued for delayed execution
+class CliCommandHolder
+{
+ private:
+ const CliCommand *cmd;
+ char *args;
+ pPrintf m_zprintf;
+ public:
+ CliCommandHolder(const CliCommand *command, const char *arguments, pPrintf p_zprintf)
+ : cmd(command), m_zprintf(p_zprintf)
+ {
+ size_t len = strlen(arguments)+1;
+ args = new char[len];
+ memcpy(args, arguments, len);
+ }
+ ~CliCommandHolder() { delete[] args; }
+ void Execute() const { cmd->Func(args, m_zprintf); }
+ pPrintf GetOutputMethod() const {return (m_zprintf);}
+};
+
+/// The World
+class World
+{
+ public:
+ static volatile bool m_stopEvent;
+ static volatile uint32 m_worldLoopCounter;
+
+ World();
+ ~World();
+
+ WorldSession* FindSession(uint32 id) const;
+ void AddSession(WorldSession *s);
+ bool RemoveSession(uint32 id);
+ /// Get the number of current active sessions
+ void UpdateMaxSessionCounters();
+ uint32 GetActiveAndQueuedSessionCount() const { return m_sessions.size(); }
+ uint32 GetActiveSessionCount() const { return m_sessions.size() - m_QueuedPlayer.size(); }
+ uint32 GetQueuedSessionCount() const { return m_QueuedPlayer.size(); }
+ /// Get the maximum number of parallel sessions on the server since last reboot
+ uint32 GetMaxQueuedSessionCount() const { return m_maxQueuedSessionCount; }
+ uint32 GetMaxActiveSessionCount() const { return m_maxActiveSessionCount; }
+ Player* FindPlayerInZone(uint32 zone);
+
+ Weather* FindWeather(uint32 id) const;
+ Weather* AddWeather(uint32 zone_id);
+ void RemoveWeather(uint32 zone_id);
+
+ /// Get the active session server limit (or security level limitations)
+ uint32 GetPlayerAmountLimit() const { return m_playerLimit >= 0 ? m_playerLimit : 0; }
+ AccountTypes GetPlayerSecurityLimit() const { return m_playerLimit <= 0 ? AccountTypes(-m_playerLimit) : SEC_PLAYER; }
+
+ /// Set the active session server limit (or security level limitation)
+ void SetPlayerLimit(int32 limit, bool needUpdate = false);
+
+ //player Queue
+ typedef std::list<WorldSession*> Queue;
+ void AddQueuedPlayer(WorldSession*);
+ void RemoveQueuedPlayer(WorldSession*);
+ int32 GetQueuePos(WorldSession*);
+ uint32 GetQueueSize() const { return m_QueuedPlayer.size(); }
+
+ /// \todo Actions on m_allowMovement still to be implemented
+ /// Is movement allowed?
+ bool getAllowMovement() const { return m_allowMovement; }
+ /// Allow/Disallow object movements
+ void SetAllowMovement(bool allow) { m_allowMovement = allow; }
+
+ /// Set a new Message of the Day
+ void SetMotd(std::string motd) { m_motd = motd; }
+ /// Get the current Message of the Day
+ const char* GetMotd() const { return m_motd.c_str(); }
+
+ uint32 GetDefaultDbcLocale() const { return m_defaultDbcLocale; }
+
+ /// Get the path where data (dbc, maps) are stored on disk
+ std::string GetDataPath() const { return m_dataPath; }
+
+ /// When server started?
+ time_t const& GetStartTime() const { return m_startTime; }
+ /// What time is it?
+ time_t const& GetGameTime() const { return m_gameTime; }
+ /// Uptime (in secs)
+ uint32 GetUptime() const { return uint32(m_gameTime - m_startTime); }
+
+ /// Get the maximum skill level a player can reach
+ uint16 GetConfigMaxSkillValue() const
+ {
+ uint32 lvl = getConfig(CONFIG_MAX_PLAYER_LEVEL);
+ return lvl > 60 ? 300 + ((lvl - 60) * 75) / 10 : lvl*5;
+ }
+
+ void SetInitialWorldSettings();
+ void LoadConfigSettings(bool reload = false);
+
+ void SendWorldText(int32 string_id, ...);
+ void SendGlobalMessage(WorldPacket *packet, WorldSession *self = 0, uint32 team = 0);
+ void SendZoneMessage(uint32 zone, WorldPacket *packet, WorldSession *self = 0, uint32 team = 0);
+ void SendZoneText(uint32 zone, const char *text, WorldSession *self = 0, uint32 team = 0);
+ void SendServerMessage(uint32 type, const char *text = "", Player* player = NULL);
+
+ /// Are we in the middle of a shutdown?
+ uint32 GetShutdownMask() const { return m_ShutdownMask; }
+ bool IsShutdowning() const { return m_ShutdownTimer > 0; }
+ void ShutdownServ(uint32 time, uint32 options = 0);
+ void ShutdownCancel();
+ void ShutdownMsg(bool show = false, Player* player = NULL);
+
+ void Update(time_t diff);
+
+ void UpdateSessions( time_t diff );
+ /// Set a server rate (see #Rates)
+ void setRate(Rates rate,float value) { rate_values[rate]=value; }
+ /// Get a server rate (see #Rates)
+ float getRate(Rates rate) const { return rate_values[rate]; }
+
+ /// Set a server configuration element (see #WorldConfigs)
+ void setConfig(uint32 index,uint32 value)
+ {
+ if(index<CONFIG_VALUE_COUNT)
+ m_configs[index]=value;
+ }
+
+ /// Get a server configuration element (see #WorldConfigs)
+ uint32 getConfig(uint32 index) const
+ {
+ if(index<CONFIG_VALUE_COUNT)
+ return m_configs[index];
+ else
+ return 0;
+ }
+
+ /// Are we on a "Player versus Player" server?
+ bool IsPvPRealm() { return (getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP || getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP); }
+ bool IsFFAPvPRealm() { return getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP; }
+
+ bool KickPlayer(std::string playerName);
+ void KickAll();
+ void KickAllLess(AccountTypes sec);
+ void KickAllQueued();
+ uint8 BanAccount(std::string type, std::string nameOrIP, std::string duration, std::string reason, std::string author);
+ bool RemoveBanAccount(std::string type, std::string nameOrIP);
+
+ void ScriptsStart(std::map<uint32, std::multimap<uint32, ScriptInfo> > const& scripts, uint32 id, Object* source, Object* target);
+ void ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target);
+ bool IsScriptScheduled() const { return !m_scriptSchedule.empty(); }
+
+ // for max speed access
+ static float GetMaxVisibleDistanceForCreature() { return m_MaxVisibleDistanceForCreature; }
+ static float GetMaxVisibleDistanceForPlayer() { return m_MaxVisibleDistanceForPlayer; }
+ static float GetMaxVisibleDistanceForObject() { return m_MaxVisibleDistanceForObject; }
+ static float GetMaxVisibleDistanceInFlight() { return m_MaxVisibleDistanceInFlight; }
+ static float GetVisibleUnitGreyDistance() { return m_VisibleUnitGreyDistance; }
+ static float GetVisibleObjectGreyDistance() { return m_VisibleObjectGreyDistance; }
+
+ void ProcessCliCommands();
+ void QueueCliCommand(CliCommandHolder* command) { cliCmdQueue.add(command); }
+
+ void UpdateResultQueue();
+ void InitResultQueue();
+
+ void UpdateRealmCharCount(uint32 accid);
+
+ LocaleConstant GetAvailableDbcLocale(LocaleConstant locale) const { if(m_availableDbcLocaleMask & (1 << locale)) return locale; else return m_defaultDbcLocale; }
+ protected:
+ void _UpdateGameTime();
+ void ScriptsProcess();
+ // callback for UpdateRealmCharacters
+ void _UpdateRealmCharCount(QueryResult *resultCharCount, uint32 accountId);
+
+ void InitDailyQuestResetTime();
+ void ResetDailyQuests();
+ private:
+ time_t m_startTime;
+ time_t m_gameTime;
+ IntervalTimer m_timers[WUPDATE_COUNT];
+ uint32 mail_timer;
+ uint32 mail_timer_expires;
+
+ typedef HM_NAMESPACE::hash_map<uint32, Weather*> WeatherMap;
+ WeatherMap m_weathers;
+ typedef HM_NAMESPACE::hash_map<uint32, WorldSession*> SessionMap;
+ SessionMap m_sessions;
+ std::set<WorldSession*> m_kicked_sessions;
+ uint32 m_maxActiveSessionCount;
+ uint32 m_maxQueuedSessionCount;
+
+ std::multimap<time_t, ScriptAction> m_scriptSchedule;
+
+ float rate_values[MAX_RATES];
+ uint32 m_configs[CONFIG_VALUE_COUNT];
+ int32 m_playerLimit;
+ LocaleConstant m_defaultDbcLocale; // from config for one from loaded DBC locales
+ uint32 m_availableDbcLocaleMask; // by loaded DBC
+ void DetectDBCLang();
+ bool m_allowMovement;
+ std::string m_motd;
+ std::string m_dataPath;
+
+ uint32 m_ShutdownTimer;
+ uint32 m_ShutdownMask;
+
+ // for max speed access
+ static float m_MaxVisibleDistanceForCreature;
+ static float m_MaxVisibleDistanceForPlayer;
+ static float m_MaxVisibleDistanceForObject;
+ static float m_MaxVisibleDistanceInFlight;
+ static float m_VisibleUnitGreyDistance;
+ static float m_VisibleObjectGreyDistance;
+
+ // CLI command holder to be thread safe
+ ZThread::LockedQueue<CliCommandHolder*, ZThread::FastMutex> cliCmdQueue;
+ SqlResultQueue *m_resultQueue;
+
+ // next daily quests reset time
+ time_t m_NextDailyQuestReset;
+
+ //Player Queue
+ Queue m_QueuedPlayer;
+
+ //sessions that are added async
+ void AddSession_(WorldSession* s);
+ ZThread::LockedQueue<WorldSession*, ZThread::FastMutex> addSessQueue;
+};
+
+extern uint32 realmID;
+
+#define sWorld MaNGOS::Singleton<World>::Instance()
+#endif
+/// @}
diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp index 9ec9624182c..b6585185eea 100644 --- a/src/game/WorldSession.cpp +++ b/src/game/WorldSession.cpp @@ -1,493 +1,511 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 - */ - -/** \file - \ingroup u2w -*/ - -#include "WorldSocket.h" -#include "Common.h" -#include "Database/DatabaseEnv.h" -#include "Log.h" -#include "Opcodes.h" -#include "WorldSocket.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Player.h" -#include "ObjectMgr.h" -#include "Group.h" -#include "Guild.h" -#include "World.h" -#include "MapManager.h" -#include "ObjectAccessor.h" -#include "BattleGroundMgr.h" -#include "Language.h" // for CMSG_CANCEL_MOUNT_AURA handler -#include "Chat.h" -#include "SocialMgr.h" - -/// WorldSession constructor -WorldSession::WorldSession(uint32 id, WorldSocket *sock, uint32 sec, bool tbc, time_t mute_time, LocaleConstant locale) : -LookingForGroup_auto_join(false), LookingForGroup_auto_add(false), m_muteTime(mute_time), -_player(NULL), m_Socket(sock),_security(sec), _accountId(id), m_isTBC(tbc), -m_sessionDbcLocale(sWorld.GetAvailableDbcLocale(locale)), m_sessionDbLocaleIndex(objmgr.GetIndexForLocale(locale)), -_logoutTime(0), m_playerLoading(false), m_playerLogout(false), m_playerRecentlyLogout(false), m_latency(0) -{ - if (sock) - { - m_Address = sock->GetRemoteAddress (); - sock->AddReference (); - } -} - -/// WorldSession destructor -WorldSession::~WorldSession() -{ - ///- unload player if not unloaded - if(_player) - LogoutPlayer(true); - - /// - If have unclosed socket, close it - if (m_Socket) - { - m_Socket->CloseSocket (); - m_Socket->RemoveReference (); - m_Socket = NULL; - } - - ///- empty incoming packet queue - while(!_recvQueue.empty()) - { - WorldPacket *packet = _recvQueue.next(); - delete packet; - } - - sWorld.RemoveQueuedPlayer(this); -} - -void WorldSession::SizeError(WorldPacket const& packet, uint32 size) const -{ - sLog.outError("Client (account %u) send packet %s (%u) with size %u but expected %u (attempt crash server?), skipped", - GetAccountId(),LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size); -} - -/// Get the player name -char const* WorldSession::GetPlayerName() const -{ - return GetPlayer() ? GetPlayer()->GetName() : "<none>"; -} - -/// Send a packet to the client -void WorldSession::SendPacket(WorldPacket const* packet) -{ - if (!m_Socket) - return; - #ifdef MANGOS_DEBUG - // Code for network use statistic - static uint64 sendPacketCount = 0; - static uint64 sendPacketBytes = 0; - - static time_t firstTime = time(NULL); - static time_t lastTime = firstTime; // next 60 secs start time - - static uint64 sendLastPacketCount = 0; - static uint64 sendLastPacketBytes = 0; - - time_t cur_time = time(NULL); - - if((cur_time - lastTime) < 60) - { - sendPacketCount+=1; - sendPacketBytes+=packet->size(); - - sendLastPacketCount+=1; - sendLastPacketBytes+=packet->size(); - } - else - { - uint64 minTime = uint64(cur_time - lastTime); - uint64 fullTime = uint64(lastTime - firstTime); - sLog.outDetail("Send all time packets count: " I64FMTD " bytes: " I64FMTD " avr.count/sec: %f avr.bytes/sec: %f time: %u",sendPacketCount,sendPacketBytes,float(sendPacketCount)/fullTime,float(sendPacketBytes)/fullTime,uint32(fullTime)); - sLog.outDetail("Send last min packets count: " I64FMTD " bytes: " I64FMTD " avr.count/sec: %f avr.bytes/sec: %f",sendLastPacketCount,sendLastPacketBytes,float(sendLastPacketCount)/minTime,float(sendLastPacketBytes)/minTime); - - lastTime = cur_time; - sendLastPacketCount = 1; - sendLastPacketBytes = packet->wpos(); // wpos is real written size - } -#endif // !MANGOS_DEBUG - - if (m_Socket->SendPacket (*packet) == -1) - { - m_Socket->CloseSocket (); - } -} - -/// Add an incoming packet to the queue -void WorldSession::QueuePacket(WorldPacket* new_packet) -{ - _recvQueue.add(new_packet); -} - -/// Logging helper for unexpected opcodes -void WorldSession::logUnexpectedOpcode(WorldPacket* packet, const char *reason) -{ - sLog.outError( "SESSION: received unexpected opcode %s (0x%.4X) %s", - LookupOpcodeName(packet->GetOpcode()), - packet->GetOpcode(), - reason); -} - -/// Update the WorldSession (triggered by World update) -bool WorldSession::Update(uint32 /*diff*/) -{ - if (m_Socket) - if (m_Socket->IsClosed ()) - { - m_Socket->RemoveReference (); - m_Socket = NULL; - } - - WorldPacket *packet; - - ///- Retrieve packets from the receive queue and call the appropriate handlers - /// \todo Is there a way to consolidate the OpcondeHandlerTable and the g_worldOpcodeNames to only maintain 1 list? - /// answer : there is a way, but this is better, because it would use redundant RAM - while (!_recvQueue.empty()) - { - packet = _recvQueue.next(); - - /*#if 1 - sLog.outError( "MOEP: %s (0x%.4X)", - LookupOpcodeName(packet->GetOpcode()), - packet->GetOpcode()); - #endif*/ - - if(packet->GetOpcode() >= NUM_MSG_TYPES) - { - sLog.outError( "SESSION: received non-existed opcode %s (0x%.4X)", - LookupOpcodeName(packet->GetOpcode()), - packet->GetOpcode()); - } - else - { - OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()]; - switch (opHandle.status) - { - case STATUS_LOGGEDIN: - if(!_player) - { - // skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets - if(!m_playerRecentlyLogout) - logUnexpectedOpcode(packet, "the player has not logged in yet"); - } - else if(_player->IsInWorld()) - (this->*opHandle.handler)(*packet); - // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer - break; - case STATUS_TRANSFER_PENDING: - if(!_player) - logUnexpectedOpcode(packet, "the player has not logged in yet"); - else if(_player->IsInWorld()) - logUnexpectedOpcode(packet, "the player is still in world"); - else - (this->*opHandle.handler)(*packet); - break; - case STATUS_AUTHED: - m_playerRecentlyLogout = false; - (this->*opHandle.handler)(*packet); - break; - case STATUS_NEVER: - sLog.outError( "SESSION: received not allowed opcode %s (0x%.4X)", - LookupOpcodeName(packet->GetOpcode()), - packet->GetOpcode()); - break; - } - } - - delete packet; - } - - ///- If necessary, log the player out - time_t currTime = time(NULL); - if (!m_Socket || (ShouldLogOut(currTime) && !m_playerLoading)) - LogoutPlayer(true); - - if (!m_Socket) - return false; //Will remove this session from the world session map - - return true; -} - -/// %Log the player out -void WorldSession::LogoutPlayer(bool Save) -{ - // finish pending transfers before starting the logout - while(_player && _player->IsBeingTeleported()) - HandleMoveWorldportAckOpcode(); - - m_playerLogout = true; - - if (_player) - { - ///- If the player just died before logging out, make him appear as a ghost - //FIXME: logout must be delayed in case lost connection with client in time of combat - if (_player->GetDeathTimer()) - { - _player->getHostilRefManager().deleteReferences(); - _player->BuildPlayerRepop(); - _player->RepopAtGraveyard(); - } - else if (!_player->getAttackers().empty()) - { - _player->CombatStop(); - _player->getHostilRefManager().setOnlineOfflineState(false); - _player->RemoveAllAurasOnDeath(); - - // build set of player who attack _player or who have pet attacking of _player - std::set<Player*> aset; - for(Unit::AttackerSet::const_iterator itr = _player->getAttackers().begin(); itr != _player->getAttackers().end(); ++itr) - { - Unit* owner = (*itr)->GetOwner(); // including player controlled case - if(owner) - { - if(owner->GetTypeId()==TYPEID_PLAYER) - aset.insert((Player*)owner); - } - else - if((*itr)->GetTypeId()==TYPEID_PLAYER) - aset.insert((Player*)(*itr)); - } - - _player->SetPvPDeath(!aset.empty()); - _player->KillPlayer(); - _player->BuildPlayerRepop(); - _player->RepopAtGraveyard(); - - // give honor to all attackers from set like group case - for(std::set<Player*>::const_iterator itr = aset.begin(); itr != aset.end(); ++itr) - (*itr)->RewardHonor(_player,aset.size()); - - // give bg rewards and update counters like kill by first from attackers - // this can't be called for all attackers. - if(!aset.empty()) - if(BattleGround *bg = _player->GetBattleGround()) - bg->HandleKillPlayer(_player,*aset.begin()); - } - else if(_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) - { - // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION - _player->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT); - //_player->SetDeathPvP(*); set at SPELL_AURA_SPIRIT_OF_REDEMPTION apply time - _player->KillPlayer(); - _player->BuildPlayerRepop(); - _player->RepopAtGraveyard(); - } - - ///- Remove player from battleground (teleport to entrance) - if(_player->InBattleGround()) - _player->LeaveBattleground(); - - for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - { - if(int32 bgTypeId = _player->GetBattleGroundQueueId(i)) - { - _player->RemoveBattleGroundQueueId(bgTypeId); - sBattleGroundMgr.m_BattleGroundQueues[ bgTypeId ].RemovePlayer(_player->GetGUID(), true); - } - } - - ///- Reset the online field in the account table - // no point resetting online in character table here as Player::SaveToDB() will set it to 1 since player has not been removed from world at this stage - //No SQL injection as AccountID is uint32 - loginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = '%u'", GetAccountId()); - - ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members - Guild *guild = objmgr.GetGuildById(_player->GetGuildId()); - if(guild) - { - guild->LoadPlayerStatsByGuid(_player->GetGUID()); - guild->UpdateLogoutTime(_player->GetGUID()); - - WorldPacket data(SMSG_GUILD_EVENT, (1+1+12+8)); // name limited to 12 in character table. - data<<(uint8)GE_SIGNED_OFF; - data<<(uint8)1; - data<<_player->GetName(); - data<<_player->GetGUID(); - guild->BroadcastPacket(&data); - } - - ///- Remove pet - _player->RemovePet(NULL,PET_SAVE_AS_CURRENT, true); - - ///- empty buyback items and save the player in the database - // some save parts only correctly work in case player present in map/player_lists (pets, etc) - if(Save) - { - uint32 eslot; - for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; j++) - { - eslot = j - BUYBACK_SLOT_START; - _player->SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1+eslot*2,0); - _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1+eslot,0); - _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1+eslot,0); - } - _player->SaveToDB(); - } - - ///- Leave all channels before player delete... - _player->CleanupChannels(); - - ///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group. - _player->UninviteFromGroup(); - - // remove player from the group if he is: - // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected) - if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket) - _player->RemoveFromGroup(); - - ///- Remove the player from the world - // the player may not be in the world when logging out - // e.g if he got disconnected during a transfer to another map - // calls to GetMap in this case may cause crashes - if(_player->IsInWorld()) MapManager::Instance().GetMap(_player->GetMapId(), _player)->Remove(_player, false); - // RemoveFromWorld does cleanup that requires the player to be in the accessor - ObjectAccessor::Instance().RemoveObject(_player); - - ///- Send update to group - if(_player->GetGroup()) - _player->GetGroup()->SendUpdate(); - - ///- Broadcast a logout message to the player's friends - sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), "", true); - - ///- Delete the player object - _player->CleanupsBeforeDelete(); // do some cleanup before deleting to prevent crash at crossreferences to already deleted data - - delete _player; - _player = NULL; - - ///- Send the 'logout complete' packet to the client - 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 - CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'", GetAccountId()); - sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" ); - } - - m_playerLogout = false; - m_playerRecentlyLogout = true; - LogoutRequest(0); -} - -/// Kick a player out of the World -void WorldSession::KickPlayer() -{ - if (m_Socket) - { - m_Socket->CloseSocket (); - } -} - -/// Cancel channeling handler - -void WorldSession::SendAreaTriggerMessage(const char* Text, ...) -{ - va_list ap; - char szStr [1024]; - szStr[0] = '\0'; - - va_start(ap, Text); - vsnprintf( szStr, 1024, Text, ap ); - va_end(ap); - - uint32 length = strlen(szStr)+1; - WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 4+length); - data << length; - data << szStr; - SendPacket(&data); -} - -void WorldSession::SendNotification(const char *format,...) -{ - if(format) - { - va_list ap; - char szStr [1024]; - szStr[0] = '\0'; - va_start(ap, format); - vsnprintf( szStr, 1024, format, ap ); - va_end(ap); - - WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1)); - data << szStr; - SendPacket(&data); - } -} - -const char * WorldSession::GetMangosString( int32 entry ) -{ - return objmgr.GetMangosString(entry,GetSessionDbLocaleIndex()); -} - -void WorldSession::Handle_NULL( WorldPacket& recvPacket ) -{ - sLog.outError( "SESSION: received unhandled opcode %s (0x%.4X)", - LookupOpcodeName(recvPacket.GetOpcode()), - recvPacket.GetOpcode()); -} - -void WorldSession::Handle_EarlyProccess( WorldPacket& recvPacket ) -{ - sLog.outError( "SESSION: received opcode %s (0x%.4X) that must be proccessed in WorldSocket::OnRead", - LookupOpcodeName(recvPacket.GetOpcode()), - recvPacket.GetOpcode()); -} - -void WorldSession::Handle_ServerSide( WorldPacket& recvPacket ) -{ - sLog.outError( "SESSION: received sever-side opcode %s (0x%.4X)", - LookupOpcodeName(recvPacket.GetOpcode()), - recvPacket.GetOpcode()); -} - -void WorldSession::Handle_Depricated( WorldPacket& recvPacket ) -{ - sLog.outError( "SESSION: received depricated opcode %s (0x%.4X)", - LookupOpcodeName(recvPacket.GetOpcode()), - recvPacket.GetOpcode()); -} - -void WorldSession::SendAuthWaitQue(uint32 position) - { - if(position == 0) - { - WorldPacket packet( SMSG_AUTH_RESPONSE, 1 ); - packet << uint8( AUTH_OK ); - SendPacket(&packet); - } - else - { - WorldPacket packet( SMSG_AUTH_RESPONSE, 5 ); - packet << uint8( AUTH_WAIT_QUEUE ); - packet << uint32 (position); - SendPacket(&packet); - } - } - - +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/** \file
+ \ingroup u2w
+*/
+
+#include "WorldSocket.h"
+#include "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "WorldSocket.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Player.h"
+#include "ObjectMgr.h"
+#include "Group.h"
+#include "Guild.h"
+#include "World.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "BattleGroundMgr.h"
+#include "Language.h" // for CMSG_CANCEL_MOUNT_AURA handler
+#include "Chat.h"
+#include "SocialMgr.h"
+
+/// WorldSession constructor
+WorldSession::WorldSession(uint32 id, WorldSocket *sock, uint32 sec, bool tbc, time_t mute_time, LocaleConstant locale) :
+LookingForGroup_auto_join(false), LookingForGroup_auto_add(false), m_muteTime(mute_time),
+_player(NULL), m_Socket(sock),_security(sec), _accountId(id), m_isTBC(tbc),
+m_sessionDbcLocale(sWorld.GetAvailableDbcLocale(locale)), m_sessionDbLocaleIndex(objmgr.GetIndexForLocale(locale)),
+_logoutTime(0), m_playerLoading(false), m_playerLogout(false), m_playerRecentlyLogout(false), m_latency(0)
+{
+ if (sock)
+ {
+ m_Address = sock->GetRemoteAddress ();
+ sock->AddReference ();
+ }
+}
+
+/// WorldSession destructor
+WorldSession::~WorldSession()
+{
+ ///- unload player if not unloaded
+ if(_player)
+ LogoutPlayer(true);
+
+ /// - If have unclosed socket, close it
+ if (m_Socket)
+ {
+ m_Socket->CloseSocket ();
+ m_Socket->RemoveReference ();
+ m_Socket = NULL;
+ }
+
+ ///- empty incoming packet queue
+ while(!_recvQueue.empty())
+ {
+ WorldPacket *packet = _recvQueue.next();
+ delete packet;
+ }
+
+ sWorld.RemoveQueuedPlayer(this);
+}
+
+void WorldSession::SizeError(WorldPacket const& packet, uint32 size) const
+{
+ sLog.outError("Client (account %u) send packet %s (%u) with size %u but expected %u (attempt crash server?), skipped",
+ GetAccountId(),LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size);
+}
+
+/// Get the player name
+char const* WorldSession::GetPlayerName() const
+{
+ return GetPlayer() ? GetPlayer()->GetName() : "<none>";
+}
+
+/// Send a packet to the client
+void WorldSession::SendPacket(WorldPacket const* packet)
+{
+ if (!m_Socket)
+ return;
+ #ifdef MANGOS_DEBUG
+ // Code for network use statistic
+ static uint64 sendPacketCount = 0;
+ static uint64 sendPacketBytes = 0;
+
+ static time_t firstTime = time(NULL);
+ static time_t lastTime = firstTime; // next 60 secs start time
+
+ static uint64 sendLastPacketCount = 0;
+ static uint64 sendLastPacketBytes = 0;
+
+ time_t cur_time = time(NULL);
+
+ if((cur_time - lastTime) < 60)
+ {
+ sendPacketCount+=1;
+ sendPacketBytes+=packet->size();
+
+ sendLastPacketCount+=1;
+ sendLastPacketBytes+=packet->size();
+ }
+ else
+ {
+ uint64 minTime = uint64(cur_time - lastTime);
+ uint64 fullTime = uint64(lastTime - firstTime);
+ sLog.outDetail("Send all time packets count: " I64FMTD " bytes: " I64FMTD " avr.count/sec: %f avr.bytes/sec: %f time: %u",sendPacketCount,sendPacketBytes,float(sendPacketCount)/fullTime,float(sendPacketBytes)/fullTime,uint32(fullTime));
+ sLog.outDetail("Send last min packets count: " I64FMTD " bytes: " I64FMTD " avr.count/sec: %f avr.bytes/sec: %f",sendLastPacketCount,sendLastPacketBytes,float(sendLastPacketCount)/minTime,float(sendLastPacketBytes)/minTime);
+
+ lastTime = cur_time;
+ sendLastPacketCount = 1;
+ sendLastPacketBytes = packet->wpos(); // wpos is real written size
+ }
+#endif // !MANGOS_DEBUG
+
+ if (m_Socket->SendPacket (*packet) == -1)
+ {
+ m_Socket->CloseSocket ();
+ }
+}
+
+/// Add an incoming packet to the queue
+void WorldSession::QueuePacket(WorldPacket* new_packet)
+{
+ _recvQueue.add(new_packet);
+}
+
+/// Logging helper for unexpected opcodes
+void WorldSession::logUnexpectedOpcode(WorldPacket* packet, const char *reason)
+{
+ sLog.outError( "SESSION: received unexpected opcode %s (0x%.4X) %s",
+ LookupOpcodeName(packet->GetOpcode()),
+ packet->GetOpcode(),
+ reason);
+}
+
+/// Update the WorldSession (triggered by World update)
+bool WorldSession::Update(uint32 /*diff*/)
+{
+ if (m_Socket)
+ if (m_Socket->IsClosed ())
+ {
+ m_Socket->RemoveReference ();
+ m_Socket = NULL;
+ }
+
+ WorldPacket *packet;
+
+ ///- Retrieve packets from the receive queue and call the appropriate handlers
+ /// \todo Is there a way to consolidate the OpcondeHandlerTable and the g_worldOpcodeNames to only maintain 1 list?
+ /// answer : there is a way, but this is better, because it would use redundant RAM
+ while (!_recvQueue.empty())
+ {
+ packet = _recvQueue.next();
+
+ /*#if 1
+ sLog.outError( "MOEP: %s (0x%.4X)",
+ LookupOpcodeName(packet->GetOpcode()),
+ packet->GetOpcode());
+ #endif*/
+
+ if(packet->GetOpcode() >= NUM_MSG_TYPES)
+ {
+ sLog.outError( "SESSION: received non-existed opcode %s (0x%.4X)",
+ LookupOpcodeName(packet->GetOpcode()),
+ packet->GetOpcode());
+ }
+ else
+ {
+ OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()];
+ switch (opHandle.status)
+ {
+ case STATUS_LOGGEDIN:
+ if(!_player)
+ {
+ // skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets
+ if(!m_playerRecentlyLogout)
+ logUnexpectedOpcode(packet, "the player has not logged in yet");
+ }
+ else if(_player->IsInWorld())
+ (this->*opHandle.handler)(*packet);
+ // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer
+ break;
+ case STATUS_TRANSFER_PENDING:
+ if(!_player)
+ logUnexpectedOpcode(packet, "the player has not logged in yet");
+ else if(_player->IsInWorld())
+ logUnexpectedOpcode(packet, "the player is still in world");
+ else
+ (this->*opHandle.handler)(*packet);
+ break;
+ case STATUS_AUTHED:
+ m_playerRecentlyLogout = false;
+ (this->*opHandle.handler)(*packet);
+ break;
+ case STATUS_NEVER:
+ sLog.outError( "SESSION: received not allowed opcode %s (0x%.4X)",
+ LookupOpcodeName(packet->GetOpcode()),
+ packet->GetOpcode());
+ break;
+ }
+ }
+
+ delete packet;
+ }
+
+ ///- If necessary, log the player out
+ time_t currTime = time(NULL);
+ if (!m_Socket || (ShouldLogOut(currTime) && !m_playerLoading))
+ LogoutPlayer(true);
+
+ if (!m_Socket)
+ return false; //Will remove this session from the world session map
+
+ return true;
+}
+
+/// %Log the player out
+void WorldSession::LogoutPlayer(bool Save)
+{
+ // finish pending transfers before starting the logout
+ while(_player && _player->IsBeingTeleported())
+ HandleMoveWorldportAckOpcode();
+
+ m_playerLogout = true;
+
+ if (_player)
+ {
+ ///- If the player just died before logging out, make him appear as a ghost
+ //FIXME: logout must be delayed in case lost connection with client in time of combat
+ if (_player->GetDeathTimer())
+ {
+ _player->getHostilRefManager().deleteReferences();
+ _player->BuildPlayerRepop();
+ _player->RepopAtGraveyard();
+ }
+ else if (!_player->getAttackers().empty())
+ {
+ _player->CombatStop();
+ _player->getHostilRefManager().setOnlineOfflineState(false);
+ _player->RemoveAllAurasOnDeath();
+
+ // build set of player who attack _player or who have pet attacking of _player
+ std::set<Player*> aset;
+ for(Unit::AttackerSet::const_iterator itr = _player->getAttackers().begin(); itr != _player->getAttackers().end(); ++itr)
+ {
+ Unit* owner = (*itr)->GetOwner(); // including player controlled case
+ if(owner)
+ {
+ if(owner->GetTypeId()==TYPEID_PLAYER)
+ aset.insert((Player*)owner);
+ }
+ else
+ if((*itr)->GetTypeId()==TYPEID_PLAYER)
+ aset.insert((Player*)(*itr));
+ }
+
+ _player->SetPvPDeath(!aset.empty());
+ _player->KillPlayer();
+ _player->BuildPlayerRepop();
+ _player->RepopAtGraveyard();
+
+ // give honor to all attackers from set like group case
+ for(std::set<Player*>::const_iterator itr = aset.begin(); itr != aset.end(); ++itr)
+ (*itr)->RewardHonor(_player,aset.size());
+
+ // give bg rewards and update counters like kill by first from attackers
+ // this can't be called for all attackers.
+ if(!aset.empty())
+ if(BattleGround *bg = _player->GetBattleGround())
+ bg->HandleKillPlayer(_player,*aset.begin());
+ }
+ else if(_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
+ {
+ // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION
+ _player->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
+ //_player->SetDeathPvP(*); set at SPELL_AURA_SPIRIT_OF_REDEMPTION apply time
+ _player->KillPlayer();
+ _player->BuildPlayerRepop();
+ _player->RepopAtGraveyard();
+ }
+
+ ///- Remove player from battleground (teleport to entrance)
+ if(_player->InBattleGround())
+ _player->LeaveBattleground();
+
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ if(int32 bgTypeId = _player->GetBattleGroundQueueId(i))
+ {
+ _player->RemoveBattleGroundQueueId(bgTypeId);
+ sBattleGroundMgr.m_BattleGroundQueues[ bgTypeId ].RemovePlayer(_player->GetGUID(), true);
+ }
+ }
+
+ ///- Reset the online field in the account table
+ // no point resetting online in character table here as Player::SaveToDB() will set it to 1 since player has not been removed from world at this stage
+ //No SQL injection as AccountID is uint32
+ loginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = '%u'", GetAccountId());
+
+ ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members
+ Guild *guild = objmgr.GetGuildById(_player->GetGuildId());
+ if(guild)
+ {
+ guild->LoadPlayerStatsByGuid(_player->GetGUID());
+ guild->UpdateLogoutTime(_player->GetGUID());
+
+ WorldPacket data(SMSG_GUILD_EVENT, (1+1+12+8)); // name limited to 12 in character table.
+ data<<(uint8)GE_SIGNED_OFF;
+ data<<(uint8)1;
+ data<<_player->GetName();
+ data<<_player->GetGUID();
+ guild->BroadcastPacket(&data);
+ }
+
+ ///- Remove pet
+ _player->RemovePet(NULL,PET_SAVE_AS_CURRENT, true);
+
+ ///- empty buyback items and save the player in the database
+ // some save parts only correctly work in case player present in map/player_lists (pets, etc)
+ if(Save)
+ {
+ uint32 eslot;
+ for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; j++)
+ {
+ eslot = j - BUYBACK_SLOT_START;
+ _player->SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1+eslot*2,0);
+ _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1+eslot,0);
+ _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1+eslot,0);
+ }
+ _player->SaveToDB();
+ }
+
+ ///- Leave all channels before player delete...
+ _player->CleanupChannels();
+
+ ///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group.
+ _player->UninviteFromGroup();
+
+ // remove player from the group if he is:
+ // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected)
+ if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket)
+ _player->RemoveFromGroup();
+
+ ///- Remove the player from the world
+ // the player may not be in the world when logging out
+ // e.g if he got disconnected during a transfer to another map
+ // calls to GetMap in this case may cause crashes
+ if(_player->IsInWorld()) MapManager::Instance().GetMap(_player->GetMapId(), _player)->Remove(_player, false);
+ // RemoveFromWorld does cleanup that requires the player to be in the accessor
+ ObjectAccessor::Instance().RemoveObject(_player);
+
+ ///- Send update to group
+ if(_player->GetGroup())
+ _player->GetGroup()->SendUpdate();
+
+ ///- Broadcast a logout message to the player's friends
+ sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), "", true);
+
+ ///- Delete the player object
+ _player->CleanupsBeforeDelete(); // do some cleanup before deleting to prevent crash at crossreferences to already deleted data
+
+ delete _player;
+ _player = NULL;
+
+ ///- Send the 'logout complete' packet to the client
+ 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
+ CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'", GetAccountId());
+ sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" );
+ }
+
+ m_playerLogout = false;
+ m_playerRecentlyLogout = true;
+ LogoutRequest(0);
+}
+
+/// Kick a player out of the World
+void WorldSession::KickPlayer()
+{
+ if (m_Socket)
+ {
+ m_Socket->CloseSocket ();
+ }
+}
+
+/// Cancel channeling handler
+
+void WorldSession::SendAreaTriggerMessage(const char* Text, ...)
+{
+ va_list ap;
+ char szStr [1024];
+ szStr[0] = '\0';
+
+ va_start(ap, Text);
+ vsnprintf( szStr, 1024, Text, ap );
+ va_end(ap);
+
+ uint32 length = strlen(szStr)+1;
+ WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 4+length);
+ data << length;
+ data << szStr;
+ SendPacket(&data);
+}
+
+void WorldSession::SendNotification(const char *format,...)
+{
+ if(format)
+ {
+ va_list ap;
+ char szStr [1024];
+ szStr[0] = '\0';
+ va_start(ap, format);
+ vsnprintf( szStr, 1024, format, ap );
+ va_end(ap);
+
+ WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
+ data << szStr;
+ SendPacket(&data);
+ }
+}
+
+void WorldSession::SendNotification(int32 string_id,...)
+{
+ char const* format = GetMangosString(string_id);
+ if(format)
+ {
+ va_list ap;
+ char szStr [1024];
+ szStr[0] = '\0';
+ va_start(ap, format);
+ vsnprintf( szStr, 1024, format, ap );
+ va_end(ap);
+
+ WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
+ data << szStr;
+ SendPacket(&data);
+ }
+}
+
+const char * WorldSession::GetMangosString( int32 entry )
+{
+ return objmgr.GetMangosString(entry,GetSessionDbLocaleIndex());
+}
+
+void WorldSession::Handle_NULL( WorldPacket& recvPacket )
+{
+ sLog.outError( "SESSION: received unhandled opcode %s (0x%.4X)",
+ LookupOpcodeName(recvPacket.GetOpcode()),
+ recvPacket.GetOpcode());
+}
+
+void WorldSession::Handle_EarlyProccess( WorldPacket& recvPacket )
+{
+ sLog.outError( "SESSION: received opcode %s (0x%.4X) that must be proccessed in WorldSocket::OnRead",
+ LookupOpcodeName(recvPacket.GetOpcode()),
+ recvPacket.GetOpcode());
+}
+
+void WorldSession::Handle_ServerSide( WorldPacket& recvPacket )
+{
+ sLog.outError( "SESSION: received sever-side opcode %s (0x%.4X)",
+ LookupOpcodeName(recvPacket.GetOpcode()),
+ recvPacket.GetOpcode());
+}
+
+void WorldSession::Handle_Depricated( WorldPacket& recvPacket )
+{
+ sLog.outError( "SESSION: received depricated opcode %s (0x%.4X)",
+ LookupOpcodeName(recvPacket.GetOpcode()),
+ recvPacket.GetOpcode());
+}
+
+void WorldSession::SendAuthWaitQue(uint32 position)
+ {
+ if(position == 0)
+ {
+ WorldPacket packet( SMSG_AUTH_RESPONSE, 1 );
+ packet << uint8( AUTH_OK );
+ SendPacket(&packet);
+ }
+ else
+ {
+ WorldPacket packet( SMSG_AUTH_RESPONSE, 5 );
+ packet << uint8( AUTH_WAIT_QUEUE );
+ packet << uint32 (position);
+ SendPacket(&packet);
+ }
+ }
+
+
diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index 0f46af26c94..637d833e2de 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -1,642 +1,643 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * 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 - */ - -/// \addtogroup u2w -/// @{ -/// \file - -#ifndef __WORLDSESSION_H -#define __WORLDSESSION_H - -#include "Common.h" - -class MailItemsInfo; -struct ItemPrototype; -struct AuctionEntry; - -class Creature; -class Item; -class Object; -class Player; -class Unit; -class WorldPacket; -class WorldSocket; -class WorldSession; -class QueryResult; -class LoginQueryHolder; -class CharacterHandler; - -#define CHECK_PACKET_SIZE(P,S) if((P).size() < (S)) return SizeError((P),(S)); - -enum PartyOperation -{ - PARTY_OP_INVITE = 0, - PARTY_OP_LEAVE = 2 -}; - -enum PartyResult -{ - PARTY_RESULT_OK = 0, - PARTY_RESULT_CANT_FIND_TARGET = 1, - PARTY_RESULT_NOT_IN_YOUR_PARTY = 2, - PARTY_RESULT_NOT_IN_YOUR_INSTANCE = 3, - PARTY_RESULT_PARTY_FULL = 4, - PARTY_RESULT_ALREADY_IN_GROUP = 5, - PARTY_RESULT_YOU_NOT_IN_GROUP = 6, - PARTY_RESULT_YOU_NOT_LEADER = 7, - PARTY_RESULT_TARGET_UNFRIENDLY = 8, - PARTY_RESULT_TARGET_IGNORE_YOU = 9, - PARTY_RESULT_INVITE_RESTRICTED = 13 -}; - -/// Player session in the World -class MANGOS_DLL_SPEC WorldSession -{ - friend class CharacterHandler; - public: - WorldSession(uint32 id, WorldSocket *sock, uint32 sec, bool tbc, time_t mute_time, LocaleConstant locale); - ~WorldSession(); - - bool PlayerLoading() const { return m_playerLoading; } - bool PlayerLogout() const { return m_playerLogout; } - - void SizeError(WorldPacket const& packet, uint32 size) const; - - void SendPacket(WorldPacket const* packet); - void SendNotification(const char *format,...) ATTR_PRINTF(2,3); - void SendLfgResult(uint32 type, uint32 entry, uint8 lfg_type); - void SendPartyResult(PartyOperation operation, std::string member, PartyResult res); - void SendAreaTriggerMessage(const char* Text, ...) ATTR_PRINTF(2,3); - - uint32 GetSecurity() const { return _security; } - uint32 GetAccountId() const { return _accountId; } - Player* GetPlayer() const { return _player; } - char const* GetPlayerName() const; - void SetSecurity(uint32 security) { _security = security; } - std::string& GetRemoteAddress() { return m_Address; } - void SetPlayer(Player *plr) { _player = plr; } - bool IsTBC() const { return m_isTBC; } - - /// Is the user engaged in a log out process? - bool isLogingOut() const { return _logoutTime || m_playerLogout; } - - /// Engage the logout process for the user - void LogoutRequest(time_t requestTime) - { - _logoutTime = requestTime; - } - - /// Is logout cooldown expired? - bool ShouldLogOut(time_t currTime) const - { - return (_logoutTime > 0 && currTime >= _logoutTime + 20); - } - - void LogoutPlayer(bool Save); - void KickPlayer(); - - void QueuePacket(WorldPacket* new_packet); - bool Update(uint32 diff); - - /// Handle the authentication waiting queue (to be completed) - void SendAuthWaitQue(uint32 position); - - //void SendTestCreatureQueryOpcode( uint32 entry, uint64 guid, uint32 testvalue ); - void SendNameQueryOpcode(Player* p); - void SendNameQueryOpcodeFromDB(uint64 guid); - static void SendNameQueryOpcodeFromDBCallBack(QueryResult *result, uint32 accountId); - - void SendTrainerList( uint64 guid ); - void SendTrainerList( uint64 guid,std::string strTitle ); - void SendListInventory( uint64 guid ); - void SendShowBank( uint64 guid ); - void SendTabardVendorActivate( uint64 guid ); - void SendSpiritResurrect(); - void SendBindPoint(Creature* npc); - void SendGMTicketGetTicket(uint32 status, char const* text); - - void SendAttackStop(Unit const* enemy); - - void SendBattlegGroundList( uint64 guid, uint32 bgTypeId ); - - void SendTradeStatus(uint32 status); - void SendCancelTrade(); - - void SendStablePet(uint64 guid ); - void SendPetitionQueryOpcode( uint64 petitionguid); - void SendUpdateTrade(); - - //pet - void SendPetNameQuery(uint64 guid, uint32 petnumber); - - //mail - //used with item_page table - bool SendItemInfo( uint32 itemid, WorldPacket data ); - static void SendReturnToSender(uint8 messageType, uint32 sender_acc, uint32 sender_guid, uint32 receiver_guid, std::string subject, uint32 itemTextId, MailItemsInfo *mi, uint32 money, uint32 COD, uint16 mailTemplateId = 0); - static void SendMailTo(Player* receiver, uint8 messageType, uint8 stationery, uint32 sender_guidlow_or_entry, uint32 received_guidlow, std::string subject, uint32 itemTextId, MailItemsInfo* mi, uint32 money, uint32 COD, uint32 checked, uint32 deliver_delay = 0, uint16 mailTemplateId = 0); - - //auction - void SendAuctionHello( uint64 guid, Creature * unit ); - void SendAuctionCommandResult( uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError = 0); - void SendAuctionBidderNotification( uint32 location, uint32 auctionId, uint64 bidder, uint32 bidSum, uint32 diff, uint32 item_template); - void SendAuctionOwnerNotification( AuctionEntry * auction ); - bool SendAuctionInfo(WorldPacket & data, AuctionEntry* auction); - void SendAuctionOutbiddedMail( AuctionEntry * auction, uint32 newPrice ); - void SendAuctionCancelledToBidderMail( AuctionEntry* auction ); - - //Item Enchantment - void SendEnchantmentLog(uint64 Target, uint64 Caster,uint32 ItemID,uint32 SpellID); - void SendItemEnchantTimeUpdate(uint64 Playerguid, uint64 Itemguid,uint32 slot,uint32 Duration); - - //Taxi - void SendTaxiStatus( uint64 guid ); - void SendTaxiMenu( Creature* unit ); - void SendDoFlight( uint16 MountId, uint32 path, uint32 pathNode = 0 ); - bool SendLearnNewTaxiNode( Creature* unit ); - - // Guild/Arena Team - void SendGuildCommandResult(uint32 typecmd,std::string str,uint32 cmdresult); - void SendArenaTeamCommandResult(uint32 unk1, std::string str1, std::string str2, uint32 unk3); - void BuildArenaTeamEventPacket(WorldPacket *data, uint8 eventid, uint8 str_count, std::string str1, std::string str2, std::string str3); - void SendNotInArenaTeamPacket(uint8 type); - void SendPetitionShowList( uint64 guid ); - void SendSaveGuildEmblem( uint32 msg ); - - // Looking For Group - // TRUE values set by client sending CMSG_LFG_SET_AUTOJOIN and CMSG_LFM_CLEAR_AUTOFILL before player login - bool LookingForGroup_auto_join; - bool LookingForGroup_auto_add; - - void BuildPartyMemberStatsChangedPacket(Player *player, WorldPacket *data); - - void DoLootRelease( uint64 lguid ); - - // Account mute time - time_t m_muteTime; - - // Locales - LocaleConstant GetSessionDbcLocale() { return m_sessionDbcLocale; } - int GetSessionDbLocaleIndex() { return m_sessionDbLocaleIndex; } - const char *GetMangosString(int32 entry); - - uint32 GetLatency() const { return m_latency; } - void SetLatency(uint32 latency) { m_latency = latency; } - uint32 getDialogStatus(Player *pPlayer, Object* questgiver, uint32 defstatus); - - public: // opcodes handlers - - void Handle_NULL(WorldPacket& recvPacket); // not used - void Handle_EarlyProccess( WorldPacket& recvPacket);// just mark packets processed in WorldSocket::OnRead - void Handle_ServerSide(WorldPacket& recvPacket); // sever side only, can't be accepted from client - void Handle_Depricated(WorldPacket& recvPacket); // never used anymore by client - - void HandleCharEnumOpcode(WorldPacket& recvPacket); - void HandleCharDeleteOpcode(WorldPacket& recvPacket); - void HandleCharCreateOpcode(WorldPacket& recvPacket); - void HandlePlayerLoginOpcode(WorldPacket& recvPacket); - void HandleCharEnum(QueryResult * result); - void HandlePlayerLogin(LoginQueryHolder * holder); - - // played time - void HandlePlayedTime(WorldPacket& recvPacket); - - // new - void HandleMoveUnRootAck(WorldPacket& recvPacket); - void HandleMoveRootAck(WorldPacket& recvPacket); - void HandleLookingForGroup(WorldPacket& recvPacket); - - // new inspect - void HandleInspectOpcode(WorldPacket& recvPacket); - - // new party stats - void HandleInspectHonorStatsOpcode(WorldPacket& recvPacket); - - void HandleMoveWaterWalkAck(WorldPacket& recvPacket); - void HandleFeatherFallAck(WorldPacket &recv_data); - - void HandleMoveHoverAck( WorldPacket & recv_data ); - - void HandleMountSpecialAnimOpcode(WorldPacket &recvdata); - - // character view - void HandleToggleHelmOpcode(WorldPacket& recv_data); - void HandleToggleCloakOpcode(WorldPacket& recv_data); - - // repair - void HandleRepairItemOpcode(WorldPacket& recvPacket); - - // Knockback - void HandleMoveKnockBackAck(WorldPacket& recvPacket); - - void HandleMoveTeleportAck(WorldPacket& recvPacket); - void HandleForceSpeedChangeAck( WorldPacket & recv_data ); - - void HandlePingOpcode(WorldPacket& recvPacket); - void HandleAuthSessionOpcode(WorldPacket& recvPacket); - void HandleRepopRequestOpcode(WorldPacket& recvPacket); - void HandleAutostoreLootItemOpcode(WorldPacket& recvPacket); - void HandleLootMoneyOpcode(WorldPacket& recvPacket); - void HandleLootOpcode(WorldPacket& recvPacket); - void HandleLootReleaseOpcode(WorldPacket& recvPacket); - void HandleLootMasterGiveOpcode(WorldPacket& recvPacket); - void HandleWhoOpcode(WorldPacket& recvPacket); - void HandleLogoutRequestOpcode(WorldPacket& recvPacket); - void HandlePlayerLogoutOpcode(WorldPacket& recvPacket); - void HandleLogoutCancelOpcode(WorldPacket& recvPacket); - void HandleGMTicketGetTicketOpcode(WorldPacket& recvPacket); - void HandleGMTicketCreateOpcode(WorldPacket& recvPacket); - void HandleGMTicketSystemStatusOpcode(WorldPacket& recvPacket); - - void HandleGMTicketDeleteOpcode(WorldPacket& recvPacket); - void HandleGMTicketUpdateTextOpcode(WorldPacket& recvPacket); - - void HandleGMSurveySubmit(WorldPacket& recvPacket); - - void HandleTogglePvP(WorldPacket& recvPacket); - - void HandleZoneUpdateOpcode(WorldPacket& recvPacket); - void HandleSetTargetOpcode(WorldPacket& recvPacket); - void HandleSetSelectionOpcode(WorldPacket& recvPacket); - void HandleStandStateChangeOpcode(WorldPacket& recvPacket); - void HandleEmoteOpcode(WorldPacket& recvPacket); - void HandleFriendListOpcode(WorldPacket& recvPacket); - void HandleAddFriendOpcode(WorldPacket& recvPacket); - void HandleDelFriendOpcode(WorldPacket& recvPacket); - void HandleAddIgnoreOpcode(WorldPacket& recvPacket); - void HandleDelIgnoreOpcode(WorldPacket& recvPacket); - void HandleSetFriendNoteOpcode(WorldPacket& recvPacket); - void HandleBugOpcode(WorldPacket& recvPacket); - void HandleSetAmmoOpcode(WorldPacket& recvPacket); - void HandleItemNameQueryOpcode(WorldPacket& recvPacket); - - void HandleAreaTriggerOpcode(WorldPacket& recvPacket); - - void HandleSetFactionAtWar( WorldPacket & recv_data ); - void HandleSetFactionCheat( WorldPacket & recv_data ); - void HandleSetWatchedFactionIndexOpcode(WorldPacket & recv_data); - void HandleSetWatchedFactionInactiveOpcode(WorldPacket & recv_data); - - void HandleUpdateAccountData(WorldPacket& recvPacket); - void HandleRequestAccountData(WorldPacket& recvPacket); - void HandleSetActionButtonOpcode(WorldPacket& recvPacket); - - void HandleGameObjectUseOpcode(WorldPacket& recPacket); - void HandleMeetingStoneInfo(WorldPacket& recPacket); - - void HandleNameQueryOpcode(WorldPacket& recvPacket); - - void HandleQueryTimeOpcode(WorldPacket& recvPacket); - - void HandleCreatureQueryOpcode(WorldPacket& recvPacket); - - void HandleGameObjectQueryOpcode(WorldPacket& recvPacket); - - void HandleMoveWorldportAckOpcode(WorldPacket& recvPacket); - void HandleMoveWorldportAckOpcode(); // for server-side calls - - void HandleMovementOpcodes(WorldPacket& recvPacket); - void HandleSetActiveMoverOpcode(WorldPacket &recv_data); - void HandleMoveTimeSkippedOpcode(WorldPacket &recv_data); - - void HandleRequestRaidInfoOpcode( WorldPacket & recv_data ); - - void HandleBattlefieldStatusOpcode(WorldPacket &recv_data); - void HandleBattleMasterHelloOpcode(WorldPacket &recv_data); - - void HandleGroupInviteOpcode(WorldPacket& recvPacket); - //void HandleGroupCancelOpcode(WorldPacket& recvPacket); - void HandleGroupAcceptOpcode(WorldPacket& recvPacket); - void HandleGroupDeclineOpcode(WorldPacket& recvPacket); - void HandleGroupUninviteNameOpcode(WorldPacket& recvPacket); - void HandleGroupUninviteGuidOpcode(WorldPacket& recvPacket); - void HandleGroupUninvite(uint64 guid, std::string name); - void HandleGroupSetLeaderOpcode(WorldPacket& recvPacket); - void HandleGroupLeaveOpcode(WorldPacket& recvPacket); - void HandleGroupPassOnLootOpcode( WorldPacket &recv_data ); - void HandleLootMethodOpcode(WorldPacket& recvPacket); - void HandleLootRoll( WorldPacket &recv_data ); - void HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data ); - void HandleRaidIconTargetOpcode( WorldPacket & recv_data ); - void HandleRaidReadyCheckOpcode( WorldPacket & recv_data ); - void HandleRaidReadyCheckFinishOpcode( WorldPacket & recv_data ); - void HandleRaidConvertOpcode( WorldPacket & recv_data ); - void HandleGroupChangeSubGroupOpcode( WorldPacket & recv_data ); - void HandleGroupAssistantOpcode( WorldPacket & recv_data ); - void HandleGroupPromoteOpcode( WorldPacket & recv_data ); - - void HandlePetitionBuyOpcode(WorldPacket& recv_data); - void HandlePetitionShowSignOpcode(WorldPacket& recv_data); - void HandlePetitionQueryOpcode(WorldPacket& recv_data); - void HandlePetitionRenameOpcode(WorldPacket& recv_data); - void HandlePetitionSignOpcode(WorldPacket& recv_data); - void HandlePetitionDeclineOpcode(WorldPacket& recv_data); - void HandleOfferPetitionOpcode(WorldPacket& recv_data); - void HandleTurnInPetitionOpcode(WorldPacket& recv_data); - - void HandleGuildQueryOpcode(WorldPacket& recvPacket); - void HandleGuildCreateOpcode(WorldPacket& recvPacket); - void HandleGuildInviteOpcode(WorldPacket& recvPacket); - void HandleGuildRemoveOpcode(WorldPacket& recvPacket); - void HandleGuildAcceptOpcode(WorldPacket& recvPacket); - void HandleGuildDeclineOpcode(WorldPacket& recvPacket); - void HandleGuildInfoOpcode(WorldPacket& recvPacket); - void HandleGuildEventLogOpcode(WorldPacket& recvPacket); - void HandleGuildRosterOpcode(WorldPacket& recvPacket); - void HandleGuildPromoteOpcode(WorldPacket& recvPacket); - void HandleGuildDemoteOpcode(WorldPacket& recvPacket); - void HandleGuildLeaveOpcode(WorldPacket& recvPacket); - void HandleGuildDisbandOpcode(WorldPacket& recvPacket); - void HandleGuildLeaderOpcode(WorldPacket& recvPacket); - void HandleGuildMOTDOpcode(WorldPacket& recvPacket); - void HandleGuildSetPublicNoteOpcode(WorldPacket& recvPacket); - void HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket); - void HandleGuildRankOpcode(WorldPacket& recvPacket); - void HandleGuildAddRankOpcode(WorldPacket& recvPacket); - void HandleGuildDelRankOpcode(WorldPacket& recvPacket); - void HandleGuildChangeInfoOpcode(WorldPacket& recvPacket); - void HandleGuildSaveEmblemOpcode(WorldPacket& recvPacket); - - void HandleTaxiNodeStatusQueryOpcode(WorldPacket& recvPacket); - void HandleTaxiQueryAvailableNodesOpcode(WorldPacket& recvPacket); - void HandleActivateTaxiOpcode(WorldPacket& recvPacket); - void HandleActivateTaxiFarOpcode(WorldPacket& recvPacket); - void HandleTaxiNextDestinationOpcode(WorldPacket& recvPacket); - - void HandleTabardVendorActivateOpcode(WorldPacket& recvPacket); - void HandleBankerActivateOpcode(WorldPacket& recvPacket); - void HandleBuyBankSlotOpcode(WorldPacket& recvPacket); - void HandleTrainerListOpcode(WorldPacket& recvPacket); - void HandleTrainerBuySpellOpcode(WorldPacket& recvPacket); - void HandlePetitionShowListOpcode(WorldPacket& recvPacket); - void HandleGossipHelloOpcode(WorldPacket& recvPacket); - void HandleGossipSelectOptionOpcode(WorldPacket& recvPacket); - void HandleSpiritHealerActivateOpcode(WorldPacket& recvPacket); - void HandleNpcTextQueryOpcode(WorldPacket& recvPacket); - void HandleBinderActivateOpcode(WorldPacket& recvPacket); - void HandleListStabledPetsOpcode(WorldPacket& recvPacket); - void HandleStablePet(WorldPacket& recvPacket); - void HandleUnstablePet(WorldPacket& recvPacket); - void HandleBuyStableSlot(WorldPacket& recvPacket); - void HandleStableRevivePet(WorldPacket& recvPacket); - void HandleStableSwapPet(WorldPacket& recvPacket); - - void HandleDuelAcceptedOpcode(WorldPacket& recvPacket); - void HandleDuelCancelledOpcode(WorldPacket& recvPacket); - - void HandleAcceptTradeOpcode(WorldPacket& recvPacket); - void HandleBeginTradeOpcode(WorldPacket& recvPacket); - void HandleBusyTradeOpcode(WorldPacket& recvPacket); - void HandleCancelTradeOpcode(WorldPacket& recvPacket); - void HandleClearTradeItemOpcode(WorldPacket& recvPacket); - void HandleIgnoreTradeOpcode(WorldPacket& recvPacket); - void HandleInitiateTradeOpcode(WorldPacket& recvPacket); - void HandleSetTradeGoldOpcode(WorldPacket& recvPacket); - void HandleSetTradeItemOpcode(WorldPacket& recvPacket); - void HandleUnacceptTradeOpcode(WorldPacket& recvPacket); - - void HandleAuctionHelloOpcode(WorldPacket& recvPacket); - void HandleAuctionListItems( WorldPacket & recv_data ); - void HandleAuctionListBidderItems( WorldPacket & recv_data ); - void HandleAuctionSellItem( WorldPacket & recv_data ); - void HandleAuctionRemoveItem( WorldPacket & recv_data ); - void HandleAuctionListOwnerItems( WorldPacket & recv_data ); - void HandleAuctionPlaceBid( WorldPacket & recv_data ); - - void HandleGetMail( WorldPacket & recv_data ); - void HandleSendMail( WorldPacket & recv_data ); - void HandleTakeMoney( WorldPacket & recv_data ); - void HandleTakeItem( WorldPacket & recv_data ); - void HandleMarkAsRead( WorldPacket & recv_data ); - void HandleReturnToSender( WorldPacket & recv_data ); - void HandleMailDelete( WorldPacket & recv_data ); - void HandleItemTextQuery( WorldPacket & recv_data); - void HandleMailCreateTextItem(WorldPacket & recv_data ); - void HandleMsgQueryNextMailtime(WorldPacket & recv_data ); - void HandleCancelChanneling(WorldPacket & recv_data ); - - void SendItemPageInfo( ItemPrototype *itemProto ); - void HandleSplitItemOpcode(WorldPacket& recvPacket); - void HandleSwapInvItemOpcode(WorldPacket& recvPacket); - void HandleDestroyItemOpcode(WorldPacket& recvPacket); - void HandleAutoEquipItemOpcode(WorldPacket& recvPacket); - void HandleItemQuerySingleOpcode(WorldPacket& recvPacket); - void HandleSellItemOpcode(WorldPacket& recvPacket); - void HandleBuyItemInSlotOpcode(WorldPacket& recvPacket); - void HandleBuyItemOpcode(WorldPacket& recvPacket); - void HandleListInventoryOpcode(WorldPacket& recvPacket); - void HandleAutoStoreBagItemOpcode(WorldPacket& recvPacket); - void HandleReadItem(WorldPacket& recvPacket); - void HandleAutoEquipItemSlotOpcode(WorldPacket & recvPacket); - void HandleSwapItem( WorldPacket & recvPacket); - void HandleBuybackItem(WorldPacket & recvPacket); - void HandleAutoBankItemOpcode(WorldPacket& recvPacket); - void HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket); - void HandleWrapItemOpcode(WorldPacket& recvPacket); - - void HandleAttackSwingOpcode(WorldPacket& recvPacket); - void HandleAttackStopOpcode(WorldPacket& recvPacket); - void HandleSetSheathedOpcode(WorldPacket& recvPacket); - - void HandleUseItemOpcode(WorldPacket& recvPacket); - void HandleOpenItemOpcode(WorldPacket& recvPacket); - void HandleCastSpellOpcode(WorldPacket& recvPacket); - void HandleCancelCastOpcode(WorldPacket& recvPacket); - void HandleCancelAuraOpcode(WorldPacket& recvPacket); - void HandleCancelGrowthAuraOpcode(WorldPacket& recvPacket); - void HandleCancelAutoRepeatSpellOpcode(WorldPacket& recvPacket); - - void HandleLearnTalentOpcode(WorldPacket& recvPacket); - void HandleTalentWipeOpcode(WorldPacket& recvPacket); - void HandleUnlearnSkillOpcode(WorldPacket& recvPacket); - - void HandleQuestgiverStatusQueryOpcode(WorldPacket& recvPacket); - void HandleQuestgiverStatusQueryMultipleOpcode(WorldPacket& recvPacket); - void HandleQuestgiverHelloOpcode(WorldPacket& recvPacket); - void HandleQuestgiverAcceptQuestOpcode(WorldPacket& recvPacket); - void HandleQuestgiverQuestQueryOpcode(WorldPacket& recvPacket); - void HandleQuestgiverChooseRewardOpcode(WorldPacket& recvPacket); - void HandleQuestgiverRequestRewardOpcode(WorldPacket& recvPacket); - void HandleQuestQueryOpcode(WorldPacket& recvPacket); - void HandleQuestgiverCancel(WorldPacket& recv_data ); - void HandleQuestLogSwapQuest(WorldPacket& recv_data ); - void HandleQuestLogRemoveQuest(WorldPacket& recv_data); - void HandleQuestConfirmAccept(WorldPacket& recv_data); - void HandleQuestComplete(WorldPacket& recv_data); - void HandleQuestAutoLaunch(WorldPacket& recvPacket); - void HandleQuestPushToParty(WorldPacket& recvPacket); - void HandleQuestPushResult(WorldPacket& recvPacket); - - void HandleMessagechatOpcode(WorldPacket& recvPacket); - void HandleTextEmoteOpcode(WorldPacket& recvPacket); - void HandleChatIgnoredOpcode(WorldPacket& recvPacket); - - void HandleCorpseReclaimOpcode( WorldPacket& recvPacket ); - void HandleCorpseQueryOpcode( WorldPacket& recvPacket ); - void HandleResurrectResponseOpcode(WorldPacket& recvPacket); - void HandleSummonResponseOpcode(WorldPacket& recv_data); - - void HandleChannelJoin(WorldPacket& recvPacket); - void HandleChannelLeave(WorldPacket& recvPacket); - void HandleChannelList(WorldPacket& recvPacket); - void HandleChannelPassword(WorldPacket& recvPacket); - void HandleChannelSetOwner(WorldPacket& recvPacket); - void HandleChannelOwner(WorldPacket& recvPacket); - void HandleChannelModerator(WorldPacket& recvPacket); - void HandleChannelUnmoderator(WorldPacket& recvPacket); - void HandleChannelMute(WorldPacket& recvPacket); - void HandleChannelUnmute(WorldPacket& recvPacket); - void HandleChannelInvite(WorldPacket& recvPacket); - void HandleChannelKick(WorldPacket& recvPacket); - void HandleChannelBan(WorldPacket& recvPacket); - void HandleChannelUnban(WorldPacket& recvPacket); - void HandleChannelAnnounce(WorldPacket& recvPacket); - void HandleChannelModerate(WorldPacket& recvPacket); - void HandleChannelRosterQuery(WorldPacket& recvPacket); - void HandleChannelInfoQuery(WorldPacket& recvPacket); - void HandleChannelJoinNotify(WorldPacket& recvPacket); - - void HandleCompleteCinema(WorldPacket& recvPacket); - void HandleNextCinematicCamera(WorldPacket& recvPacket); - - void HandlePageQuerySkippedOpcode(WorldPacket& recvPacket); - void HandlePageQueryOpcode(WorldPacket& recvPacket); - - void HandleTutorialFlag ( WorldPacket & recv_data ); - void HandleTutorialClear( WorldPacket & recv_data ); - void HandleTutorialReset( WorldPacket & recv_data ); - - //Pet - void HandlePetAction( WorldPacket & recv_data ); - void HandlePetNameQuery( WorldPacket & recv_data ); - void HandlePetSetAction( WorldPacket & recv_data ); - void HandlePetAbandon( WorldPacket & recv_data ); - void HandlePetRename( WorldPacket & recv_data ); - void HandlePetCancelAuraOpcode( WorldPacket& recvPacket ); - void HandlePetUnlearnOpcode( WorldPacket& recvPacket ); - void HandlePetSpellAutocastOpcode( WorldPacket& recvPacket ); - void HandleAddDynamicTargetObsoleteOpcode( WorldPacket& recvPacket ); - - void HandleSetActionBar(WorldPacket& recv_data); - - void HandleChangePlayerNameOpcode(WorldPacket& recv_data); - void HandleDeclinedPlayerNameOpcode(WorldPacket& recv_data); - - void HandleTotemDestroy(WorldPacket& recv_data); - - //BattleGround - void HandleBattleGroundHelloOpcode(WorldPacket &recv_data); - void HandleBattleGroundJoinOpcode(WorldPacket &recv_data); - void HandleBattleGroundPlayerPositionsOpcode(WorldPacket& recv_data); - void HandleBattleGroundPVPlogdataOpcode( WorldPacket &recv_data ); - void HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data ); - void HandleBattleGroundListOpcode( WorldPacket &recv_data ); - void HandleBattleGroundLeaveOpcode( WorldPacket &recv_data ); - void HandleBattleGroundArenaJoin( WorldPacket &recv_data ); - void HandleBattleGroundReportAFK( WorldPacket &recv_data ); - - void HandleWardenDataOpcode(WorldPacket& recv_data); - void HandleWorldTeleportOpcode(WorldPacket& recv_data); - void HandleMinimapPingOpcode(WorldPacket& recv_data); - void HandleRandomRollOpcode(WorldPacket& recv_data); - void HandleFarSightOpcode(WorldPacket& recv_data); - void HandleSetLfgOpcode(WorldPacket& recv_data); - void HandleDungeonDifficultyOpcode(WorldPacket& recv_data); - void HandleMoveFlyModeChangeAckOpcode(WorldPacket& recv_data); - void HandleLfgAutoJoinOpcode(WorldPacket& recv_data); - void HandleLfgCancelAutoJoinOpcode(WorldPacket& recv_data); - void HandleLfmAutoAddMembersOpcode(WorldPacket& recv_data); - void HandleLfmCancelAutoAddmembersOpcode(WorldPacket& recv_data); - void HandleLfgClearOpcode(WorldPacket& recv_data); - void HandleLfmSetNoneOpcode(WorldPacket& recv_data); - void HandleLfmSetOpcode(WorldPacket& recv_data); - void HandleLfgSetCommentOpcode(WorldPacket& recv_data); - void HandleNewUnknownOpcode(WorldPacket& recv_data); - void HandleChooseTitleOpcode(WorldPacket& recv_data); - void HandleRealmStateRequestOpcode(WorldPacket& recv_data); - void HandleAllowMoveAckOpcode(WorldPacket& recv_data); - void HandleWhoisOpcode(WorldPacket& recv_data); - void HandleResetInstancesOpcode(WorldPacket& recv_data); - - // Arena Team - void HandleInspectArenaStatsOpcode(WorldPacket& recv_data); - void HandleArenaTeamQueryOpcode(WorldPacket& recv_data); - void HandleArenaTeamRosterOpcode(WorldPacket& recv_data); - void HandleArenaTeamAddMemberOpcode(WorldPacket& recv_data); - void HandleArenaTeamInviteAcceptOpcode(WorldPacket& recv_data); - void HandleArenaTeamInviteDeclineOpcode(WorldPacket& recv_data); - void HandleArenaTeamLeaveOpcode(WorldPacket& recv_data); - void HandleArenaTeamRemoveFromTeamOpcode(WorldPacket& recv_data); - void HandleArenaTeamDisbandOpcode(WorldPacket& recv_data); - void HandleArenaTeamPromoteToCaptainOpcode(WorldPacket& recv_data); - - void HandleAreaSpiritHealerQueryOpcode(WorldPacket& recv_data); - void HandleAreaSpiritHealerQueueOpcode(WorldPacket& recv_data); - void HandleDismountOpcode(WorldPacket& recv_data); - void HandleSelfResOpcode(WorldPacket& recv_data); - void HandleReportSpamOpcode(WorldPacket& recv_data); - void HandleRequestPetInfoOpcode(WorldPacket& recv_data); - - // Socket gem - void HandleSocketOpcode(WorldPacket& recv_data); - - void HandleCancelTempItemEnchantmentOpcode(WorldPacket& recv_data); - - void HandleChannelEnableVoiceOpcode(WorldPacket & recv_data); - void HandleVoiceSettingsOpcode(WorldPacket& recv_data); - void HandleChannelVoiceChatQuery(WorldPacket& recv_data); - void HandleSetTaxiBenchmarkOpcode(WorldPacket& recv_data); - - // Guild Bank - void HandleGuildBankGetRights(WorldPacket& recv_data); - void HandleGuildBankGetMoneyAmount(WorldPacket& recv_data); - void HandleGuildBankQuery(WorldPacket& recv_data); - void HandleGuildBankTabColon(WorldPacket& recv_data); - void HandleGuildBankLog(WorldPacket& recv_data); - void HandleGuildBankDeposit(WorldPacket& recv_data); - void HandleGuildBankWithdraw(WorldPacket& recv_data); - void HandleGuildBankDepositItem(WorldPacket& recv_data); - void HandleGuildBankModifyTab(WorldPacket& recv_data); - void HandleGuildBankBuyTab(WorldPacket& recv_data); - void HandleGuildBankTabText(WorldPacket& recv_data); - void HandleGuildBankSetTabText(WorldPacket& recv_data); - private: - // private trade methods - void moveItems(Item* myItems[], Item* hisItems[]); - - // logging helper - void logUnexpectedOpcode(WorldPacket *packet, const char * reason); - Player *_player; - WorldSocket *m_Socket; - std::string m_Address; - - uint32 _security; - uint32 _accountId; - bool m_isTBC; - - time_t _logoutTime; - bool m_playerLoading; // code processed in LoginPlayer - bool m_playerLogout; // code processed in LogoutPlayer - bool m_playerRecentlyLogout; - LocaleConstant m_sessionDbcLocale; - int m_sessionDbLocaleIndex; - uint32 m_latency; - - ZThread::LockedQueue<WorldPacket*,ZThread::FastMutex> _recvQueue; -}; -#endif -/// @} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/// \addtogroup u2w
+/// @{
+/// \file
+
+#ifndef __WORLDSESSION_H
+#define __WORLDSESSION_H
+
+#include "Common.h"
+
+class MailItemsInfo;
+struct ItemPrototype;
+struct AuctionEntry;
+
+class Creature;
+class Item;
+class Object;
+class Player;
+class Unit;
+class WorldPacket;
+class WorldSocket;
+class WorldSession;
+class QueryResult;
+class LoginQueryHolder;
+class CharacterHandler;
+
+#define CHECK_PACKET_SIZE(P,S) if((P).size() < (S)) return SizeError((P),(S));
+
+enum PartyOperation
+{
+ PARTY_OP_INVITE = 0,
+ PARTY_OP_LEAVE = 2
+};
+
+enum PartyResult
+{
+ PARTY_RESULT_OK = 0,
+ PARTY_RESULT_CANT_FIND_TARGET = 1,
+ PARTY_RESULT_NOT_IN_YOUR_PARTY = 2,
+ PARTY_RESULT_NOT_IN_YOUR_INSTANCE = 3,
+ PARTY_RESULT_PARTY_FULL = 4,
+ PARTY_RESULT_ALREADY_IN_GROUP = 5,
+ PARTY_RESULT_YOU_NOT_IN_GROUP = 6,
+ PARTY_RESULT_YOU_NOT_LEADER = 7,
+ PARTY_RESULT_TARGET_UNFRIENDLY = 8,
+ PARTY_RESULT_TARGET_IGNORE_YOU = 9,
+ PARTY_RESULT_INVITE_RESTRICTED = 13
+};
+
+/// Player session in the World
+class MANGOS_DLL_SPEC WorldSession
+{
+ friend class CharacterHandler;
+ public:
+ WorldSession(uint32 id, WorldSocket *sock, uint32 sec, bool tbc, time_t mute_time, LocaleConstant locale);
+ ~WorldSession();
+
+ bool PlayerLoading() const { return m_playerLoading; }
+ bool PlayerLogout() const { return m_playerLogout; }
+
+ void SizeError(WorldPacket const& packet, uint32 size) const;
+
+ void SendPacket(WorldPacket const* packet);
+ void SendNotification(const char *format,...) ATTR_PRINTF(2,3);
+ void SendNotification(int32 string_id,...);
+ void SendLfgResult(uint32 type, uint32 entry, uint8 lfg_type);
+ void SendPartyResult(PartyOperation operation, std::string member, PartyResult res);
+ void SendAreaTriggerMessage(const char* Text, ...) ATTR_PRINTF(2,3);
+
+ uint32 GetSecurity() const { return _security; }
+ uint32 GetAccountId() const { return _accountId; }
+ Player* GetPlayer() const { return _player; }
+ char const* GetPlayerName() const;
+ void SetSecurity(uint32 security) { _security = security; }
+ std::string& GetRemoteAddress() { return m_Address; }
+ void SetPlayer(Player *plr) { _player = plr; }
+ bool IsTBC() const { return m_isTBC; }
+
+ /// Is the user engaged in a log out process?
+ bool isLogingOut() const { return _logoutTime || m_playerLogout; }
+
+ /// Engage the logout process for the user
+ void LogoutRequest(time_t requestTime)
+ {
+ _logoutTime = requestTime;
+ }
+
+ /// Is logout cooldown expired?
+ bool ShouldLogOut(time_t currTime) const
+ {
+ return (_logoutTime > 0 && currTime >= _logoutTime + 20);
+ }
+
+ void LogoutPlayer(bool Save);
+ void KickPlayer();
+
+ void QueuePacket(WorldPacket* new_packet);
+ bool Update(uint32 diff);
+
+ /// Handle the authentication waiting queue (to be completed)
+ void SendAuthWaitQue(uint32 position);
+
+ //void SendTestCreatureQueryOpcode( uint32 entry, uint64 guid, uint32 testvalue );
+ void SendNameQueryOpcode(Player* p);
+ void SendNameQueryOpcodeFromDB(uint64 guid);
+ static void SendNameQueryOpcodeFromDBCallBack(QueryResult *result, uint32 accountId);
+
+ void SendTrainerList( uint64 guid );
+ void SendTrainerList( uint64 guid,std::string strTitle );
+ void SendListInventory( uint64 guid );
+ void SendShowBank( uint64 guid );
+ void SendTabardVendorActivate( uint64 guid );
+ void SendSpiritResurrect();
+ void SendBindPoint(Creature* npc);
+ void SendGMTicketGetTicket(uint32 status, char const* text);
+
+ void SendAttackStop(Unit const* enemy);
+
+ void SendBattlegGroundList( uint64 guid, uint32 bgTypeId );
+
+ void SendTradeStatus(uint32 status);
+ void SendCancelTrade();
+
+ void SendStablePet(uint64 guid );
+ void SendPetitionQueryOpcode( uint64 petitionguid);
+ void SendUpdateTrade();
+
+ //pet
+ void SendPetNameQuery(uint64 guid, uint32 petnumber);
+
+ //mail
+ //used with item_page table
+ bool SendItemInfo( uint32 itemid, WorldPacket data );
+ static void SendReturnToSender(uint8 messageType, uint32 sender_acc, uint32 sender_guid, uint32 receiver_guid, std::string subject, uint32 itemTextId, MailItemsInfo *mi, uint32 money, uint32 COD, uint16 mailTemplateId = 0);
+ static void SendMailTo(Player* receiver, uint8 messageType, uint8 stationery, uint32 sender_guidlow_or_entry, uint32 received_guidlow, std::string subject, uint32 itemTextId, MailItemsInfo* mi, uint32 money, uint32 COD, uint32 checked, uint32 deliver_delay = 0, uint16 mailTemplateId = 0);
+
+ //auction
+ void SendAuctionHello( uint64 guid, Creature * unit );
+ void SendAuctionCommandResult( uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError = 0);
+ void SendAuctionBidderNotification( uint32 location, uint32 auctionId, uint64 bidder, uint32 bidSum, uint32 diff, uint32 item_template);
+ void SendAuctionOwnerNotification( AuctionEntry * auction );
+ bool SendAuctionInfo(WorldPacket & data, AuctionEntry* auction);
+ void SendAuctionOutbiddedMail( AuctionEntry * auction, uint32 newPrice );
+ void SendAuctionCancelledToBidderMail( AuctionEntry* auction );
+
+ //Item Enchantment
+ void SendEnchantmentLog(uint64 Target, uint64 Caster,uint32 ItemID,uint32 SpellID);
+ void SendItemEnchantTimeUpdate(uint64 Playerguid, uint64 Itemguid,uint32 slot,uint32 Duration);
+
+ //Taxi
+ void SendTaxiStatus( uint64 guid );
+ void SendTaxiMenu( Creature* unit );
+ void SendDoFlight( uint16 MountId, uint32 path, uint32 pathNode = 0 );
+ bool SendLearnNewTaxiNode( Creature* unit );
+
+ // Guild/Arena Team
+ void SendGuildCommandResult(uint32 typecmd,std::string str,uint32 cmdresult);
+ void SendArenaTeamCommandResult(uint32 unk1, std::string str1, std::string str2, uint32 unk3);
+ void BuildArenaTeamEventPacket(WorldPacket *data, uint8 eventid, uint8 str_count, std::string str1, std::string str2, std::string str3);
+ void SendNotInArenaTeamPacket(uint8 type);
+ void SendPetitionShowList( uint64 guid );
+ void SendSaveGuildEmblem( uint32 msg );
+
+ // Looking For Group
+ // TRUE values set by client sending CMSG_LFG_SET_AUTOJOIN and CMSG_LFM_CLEAR_AUTOFILL before player login
+ bool LookingForGroup_auto_join;
+ bool LookingForGroup_auto_add;
+
+ void BuildPartyMemberStatsChangedPacket(Player *player, WorldPacket *data);
+
+ void DoLootRelease( uint64 lguid );
+
+ // Account mute time
+ time_t m_muteTime;
+
+ // Locales
+ LocaleConstant GetSessionDbcLocale() { return m_sessionDbcLocale; }
+ int GetSessionDbLocaleIndex() { return m_sessionDbLocaleIndex; }
+ const char *GetMangosString(int32 entry);
+
+ uint32 GetLatency() const { return m_latency; }
+ void SetLatency(uint32 latency) { m_latency = latency; }
+ uint32 getDialogStatus(Player *pPlayer, Object* questgiver, uint32 defstatus);
+
+ public: // opcodes handlers
+
+ void Handle_NULL(WorldPacket& recvPacket); // not used
+ void Handle_EarlyProccess( WorldPacket& recvPacket);// just mark packets processed in WorldSocket::OnRead
+ void Handle_ServerSide(WorldPacket& recvPacket); // sever side only, can't be accepted from client
+ void Handle_Depricated(WorldPacket& recvPacket); // never used anymore by client
+
+ void HandleCharEnumOpcode(WorldPacket& recvPacket);
+ void HandleCharDeleteOpcode(WorldPacket& recvPacket);
+ void HandleCharCreateOpcode(WorldPacket& recvPacket);
+ void HandlePlayerLoginOpcode(WorldPacket& recvPacket);
+ void HandleCharEnum(QueryResult * result);
+ void HandlePlayerLogin(LoginQueryHolder * holder);
+
+ // played time
+ void HandlePlayedTime(WorldPacket& recvPacket);
+
+ // new
+ void HandleMoveUnRootAck(WorldPacket& recvPacket);
+ void HandleMoveRootAck(WorldPacket& recvPacket);
+ void HandleLookingForGroup(WorldPacket& recvPacket);
+
+ // new inspect
+ void HandleInspectOpcode(WorldPacket& recvPacket);
+
+ // new party stats
+ void HandleInspectHonorStatsOpcode(WorldPacket& recvPacket);
+
+ void HandleMoveWaterWalkAck(WorldPacket& recvPacket);
+ void HandleFeatherFallAck(WorldPacket &recv_data);
+
+ void HandleMoveHoverAck( WorldPacket & recv_data );
+
+ void HandleMountSpecialAnimOpcode(WorldPacket &recvdata);
+
+ // character view
+ void HandleToggleHelmOpcode(WorldPacket& recv_data);
+ void HandleToggleCloakOpcode(WorldPacket& recv_data);
+
+ // repair
+ void HandleRepairItemOpcode(WorldPacket& recvPacket);
+
+ // Knockback
+ void HandleMoveKnockBackAck(WorldPacket& recvPacket);
+
+ void HandleMoveTeleportAck(WorldPacket& recvPacket);
+ void HandleForceSpeedChangeAck( WorldPacket & recv_data );
+
+ void HandlePingOpcode(WorldPacket& recvPacket);
+ void HandleAuthSessionOpcode(WorldPacket& recvPacket);
+ void HandleRepopRequestOpcode(WorldPacket& recvPacket);
+ void HandleAutostoreLootItemOpcode(WorldPacket& recvPacket);
+ void HandleLootMoneyOpcode(WorldPacket& recvPacket);
+ void HandleLootOpcode(WorldPacket& recvPacket);
+ void HandleLootReleaseOpcode(WorldPacket& recvPacket);
+ void HandleLootMasterGiveOpcode(WorldPacket& recvPacket);
+ void HandleWhoOpcode(WorldPacket& recvPacket);
+ void HandleLogoutRequestOpcode(WorldPacket& recvPacket);
+ void HandlePlayerLogoutOpcode(WorldPacket& recvPacket);
+ void HandleLogoutCancelOpcode(WorldPacket& recvPacket);
+ void HandleGMTicketGetTicketOpcode(WorldPacket& recvPacket);
+ void HandleGMTicketCreateOpcode(WorldPacket& recvPacket);
+ void HandleGMTicketSystemStatusOpcode(WorldPacket& recvPacket);
+
+ void HandleGMTicketDeleteOpcode(WorldPacket& recvPacket);
+ void HandleGMTicketUpdateTextOpcode(WorldPacket& recvPacket);
+
+ void HandleGMSurveySubmit(WorldPacket& recvPacket);
+
+ void HandleTogglePvP(WorldPacket& recvPacket);
+
+ void HandleZoneUpdateOpcode(WorldPacket& recvPacket);
+ void HandleSetTargetOpcode(WorldPacket& recvPacket);
+ void HandleSetSelectionOpcode(WorldPacket& recvPacket);
+ void HandleStandStateChangeOpcode(WorldPacket& recvPacket);
+ void HandleEmoteOpcode(WorldPacket& recvPacket);
+ void HandleFriendListOpcode(WorldPacket& recvPacket);
+ void HandleAddFriendOpcode(WorldPacket& recvPacket);
+ void HandleDelFriendOpcode(WorldPacket& recvPacket);
+ void HandleAddIgnoreOpcode(WorldPacket& recvPacket);
+ void HandleDelIgnoreOpcode(WorldPacket& recvPacket);
+ void HandleSetFriendNoteOpcode(WorldPacket& recvPacket);
+ void HandleBugOpcode(WorldPacket& recvPacket);
+ void HandleSetAmmoOpcode(WorldPacket& recvPacket);
+ void HandleItemNameQueryOpcode(WorldPacket& recvPacket);
+
+ void HandleAreaTriggerOpcode(WorldPacket& recvPacket);
+
+ void HandleSetFactionAtWar( WorldPacket & recv_data );
+ void HandleSetFactionCheat( WorldPacket & recv_data );
+ void HandleSetWatchedFactionIndexOpcode(WorldPacket & recv_data);
+ void HandleSetWatchedFactionInactiveOpcode(WorldPacket & recv_data);
+
+ void HandleUpdateAccountData(WorldPacket& recvPacket);
+ void HandleRequestAccountData(WorldPacket& recvPacket);
+ void HandleSetActionButtonOpcode(WorldPacket& recvPacket);
+
+ void HandleGameObjectUseOpcode(WorldPacket& recPacket);
+ void HandleMeetingStoneInfo(WorldPacket& recPacket);
+
+ void HandleNameQueryOpcode(WorldPacket& recvPacket);
+
+ void HandleQueryTimeOpcode(WorldPacket& recvPacket);
+
+ void HandleCreatureQueryOpcode(WorldPacket& recvPacket);
+
+ void HandleGameObjectQueryOpcode(WorldPacket& recvPacket);
+
+ void HandleMoveWorldportAckOpcode(WorldPacket& recvPacket);
+ void HandleMoveWorldportAckOpcode(); // for server-side calls
+
+ void HandleMovementOpcodes(WorldPacket& recvPacket);
+ void HandleSetActiveMoverOpcode(WorldPacket &recv_data);
+ void HandleMoveTimeSkippedOpcode(WorldPacket &recv_data);
+
+ void HandleRequestRaidInfoOpcode( WorldPacket & recv_data );
+
+ void HandleBattlefieldStatusOpcode(WorldPacket &recv_data);
+ void HandleBattleMasterHelloOpcode(WorldPacket &recv_data);
+
+ void HandleGroupInviteOpcode(WorldPacket& recvPacket);
+ //void HandleGroupCancelOpcode(WorldPacket& recvPacket);
+ void HandleGroupAcceptOpcode(WorldPacket& recvPacket);
+ void HandleGroupDeclineOpcode(WorldPacket& recvPacket);
+ void HandleGroupUninviteNameOpcode(WorldPacket& recvPacket);
+ void HandleGroupUninviteGuidOpcode(WorldPacket& recvPacket);
+ void HandleGroupUninvite(uint64 guid, std::string name);
+ void HandleGroupSetLeaderOpcode(WorldPacket& recvPacket);
+ void HandleGroupLeaveOpcode(WorldPacket& recvPacket);
+ void HandleGroupPassOnLootOpcode( WorldPacket &recv_data );
+ void HandleLootMethodOpcode(WorldPacket& recvPacket);
+ void HandleLootRoll( WorldPacket &recv_data );
+ void HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data );
+ void HandleRaidIconTargetOpcode( WorldPacket & recv_data );
+ void HandleRaidReadyCheckOpcode( WorldPacket & recv_data );
+ void HandleRaidReadyCheckFinishOpcode( WorldPacket & recv_data );
+ void HandleRaidConvertOpcode( WorldPacket & recv_data );
+ void HandleGroupChangeSubGroupOpcode( WorldPacket & recv_data );
+ void HandleGroupAssistantOpcode( WorldPacket & recv_data );
+ void HandleGroupPromoteOpcode( WorldPacket & recv_data );
+
+ void HandlePetitionBuyOpcode(WorldPacket& recv_data);
+ void HandlePetitionShowSignOpcode(WorldPacket& recv_data);
+ void HandlePetitionQueryOpcode(WorldPacket& recv_data);
+ void HandlePetitionRenameOpcode(WorldPacket& recv_data);
+ void HandlePetitionSignOpcode(WorldPacket& recv_data);
+ void HandlePetitionDeclineOpcode(WorldPacket& recv_data);
+ void HandleOfferPetitionOpcode(WorldPacket& recv_data);
+ void HandleTurnInPetitionOpcode(WorldPacket& recv_data);
+
+ void HandleGuildQueryOpcode(WorldPacket& recvPacket);
+ void HandleGuildCreateOpcode(WorldPacket& recvPacket);
+ void HandleGuildInviteOpcode(WorldPacket& recvPacket);
+ void HandleGuildRemoveOpcode(WorldPacket& recvPacket);
+ void HandleGuildAcceptOpcode(WorldPacket& recvPacket);
+ void HandleGuildDeclineOpcode(WorldPacket& recvPacket);
+ void HandleGuildInfoOpcode(WorldPacket& recvPacket);
+ void HandleGuildEventLogOpcode(WorldPacket& recvPacket);
+ void HandleGuildRosterOpcode(WorldPacket& recvPacket);
+ void HandleGuildPromoteOpcode(WorldPacket& recvPacket);
+ void HandleGuildDemoteOpcode(WorldPacket& recvPacket);
+ void HandleGuildLeaveOpcode(WorldPacket& recvPacket);
+ void HandleGuildDisbandOpcode(WorldPacket& recvPacket);
+ void HandleGuildLeaderOpcode(WorldPacket& recvPacket);
+ void HandleGuildMOTDOpcode(WorldPacket& recvPacket);
+ void HandleGuildSetPublicNoteOpcode(WorldPacket& recvPacket);
+ void HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket);
+ void HandleGuildRankOpcode(WorldPacket& recvPacket);
+ void HandleGuildAddRankOpcode(WorldPacket& recvPacket);
+ void HandleGuildDelRankOpcode(WorldPacket& recvPacket);
+ void HandleGuildChangeInfoOpcode(WorldPacket& recvPacket);
+ void HandleGuildSaveEmblemOpcode(WorldPacket& recvPacket);
+
+ void HandleTaxiNodeStatusQueryOpcode(WorldPacket& recvPacket);
+ void HandleTaxiQueryAvailableNodesOpcode(WorldPacket& recvPacket);
+ void HandleActivateTaxiOpcode(WorldPacket& recvPacket);
+ void HandleActivateTaxiFarOpcode(WorldPacket& recvPacket);
+ void HandleTaxiNextDestinationOpcode(WorldPacket& recvPacket);
+
+ void HandleTabardVendorActivateOpcode(WorldPacket& recvPacket);
+ void HandleBankerActivateOpcode(WorldPacket& recvPacket);
+ void HandleBuyBankSlotOpcode(WorldPacket& recvPacket);
+ void HandleTrainerListOpcode(WorldPacket& recvPacket);
+ void HandleTrainerBuySpellOpcode(WorldPacket& recvPacket);
+ void HandlePetitionShowListOpcode(WorldPacket& recvPacket);
+ void HandleGossipHelloOpcode(WorldPacket& recvPacket);
+ void HandleGossipSelectOptionOpcode(WorldPacket& recvPacket);
+ void HandleSpiritHealerActivateOpcode(WorldPacket& recvPacket);
+ void HandleNpcTextQueryOpcode(WorldPacket& recvPacket);
+ void HandleBinderActivateOpcode(WorldPacket& recvPacket);
+ void HandleListStabledPetsOpcode(WorldPacket& recvPacket);
+ void HandleStablePet(WorldPacket& recvPacket);
+ void HandleUnstablePet(WorldPacket& recvPacket);
+ void HandleBuyStableSlot(WorldPacket& recvPacket);
+ void HandleStableRevivePet(WorldPacket& recvPacket);
+ void HandleStableSwapPet(WorldPacket& recvPacket);
+
+ void HandleDuelAcceptedOpcode(WorldPacket& recvPacket);
+ void HandleDuelCancelledOpcode(WorldPacket& recvPacket);
+
+ void HandleAcceptTradeOpcode(WorldPacket& recvPacket);
+ void HandleBeginTradeOpcode(WorldPacket& recvPacket);
+ void HandleBusyTradeOpcode(WorldPacket& recvPacket);
+ void HandleCancelTradeOpcode(WorldPacket& recvPacket);
+ void HandleClearTradeItemOpcode(WorldPacket& recvPacket);
+ void HandleIgnoreTradeOpcode(WorldPacket& recvPacket);
+ void HandleInitiateTradeOpcode(WorldPacket& recvPacket);
+ void HandleSetTradeGoldOpcode(WorldPacket& recvPacket);
+ void HandleSetTradeItemOpcode(WorldPacket& recvPacket);
+ void HandleUnacceptTradeOpcode(WorldPacket& recvPacket);
+
+ void HandleAuctionHelloOpcode(WorldPacket& recvPacket);
+ void HandleAuctionListItems( WorldPacket & recv_data );
+ void HandleAuctionListBidderItems( WorldPacket & recv_data );
+ void HandleAuctionSellItem( WorldPacket & recv_data );
+ void HandleAuctionRemoveItem( WorldPacket & recv_data );
+ void HandleAuctionListOwnerItems( WorldPacket & recv_data );
+ void HandleAuctionPlaceBid( WorldPacket & recv_data );
+
+ void HandleGetMail( WorldPacket & recv_data );
+ void HandleSendMail( WorldPacket & recv_data );
+ void HandleTakeMoney( WorldPacket & recv_data );
+ void HandleTakeItem( WorldPacket & recv_data );
+ void HandleMarkAsRead( WorldPacket & recv_data );
+ void HandleReturnToSender( WorldPacket & recv_data );
+ void HandleMailDelete( WorldPacket & recv_data );
+ void HandleItemTextQuery( WorldPacket & recv_data);
+ void HandleMailCreateTextItem(WorldPacket & recv_data );
+ void HandleMsgQueryNextMailtime(WorldPacket & recv_data );
+ void HandleCancelChanneling(WorldPacket & recv_data );
+
+ void SendItemPageInfo( ItemPrototype *itemProto );
+ void HandleSplitItemOpcode(WorldPacket& recvPacket);
+ void HandleSwapInvItemOpcode(WorldPacket& recvPacket);
+ void HandleDestroyItemOpcode(WorldPacket& recvPacket);
+ void HandleAutoEquipItemOpcode(WorldPacket& recvPacket);
+ void HandleItemQuerySingleOpcode(WorldPacket& recvPacket);
+ void HandleSellItemOpcode(WorldPacket& recvPacket);
+ void HandleBuyItemInSlotOpcode(WorldPacket& recvPacket);
+ void HandleBuyItemOpcode(WorldPacket& recvPacket);
+ void HandleListInventoryOpcode(WorldPacket& recvPacket);
+ void HandleAutoStoreBagItemOpcode(WorldPacket& recvPacket);
+ void HandleReadItem(WorldPacket& recvPacket);
+ void HandleAutoEquipItemSlotOpcode(WorldPacket & recvPacket);
+ void HandleSwapItem( WorldPacket & recvPacket);
+ void HandleBuybackItem(WorldPacket & recvPacket);
+ void HandleAutoBankItemOpcode(WorldPacket& recvPacket);
+ void HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket);
+ void HandleWrapItemOpcode(WorldPacket& recvPacket);
+
+ void HandleAttackSwingOpcode(WorldPacket& recvPacket);
+ void HandleAttackStopOpcode(WorldPacket& recvPacket);
+ void HandleSetSheathedOpcode(WorldPacket& recvPacket);
+
+ void HandleUseItemOpcode(WorldPacket& recvPacket);
+ void HandleOpenItemOpcode(WorldPacket& recvPacket);
+ void HandleCastSpellOpcode(WorldPacket& recvPacket);
+ void HandleCancelCastOpcode(WorldPacket& recvPacket);
+ void HandleCancelAuraOpcode(WorldPacket& recvPacket);
+ void HandleCancelGrowthAuraOpcode(WorldPacket& recvPacket);
+ void HandleCancelAutoRepeatSpellOpcode(WorldPacket& recvPacket);
+
+ void HandleLearnTalentOpcode(WorldPacket& recvPacket);
+ void HandleTalentWipeOpcode(WorldPacket& recvPacket);
+ void HandleUnlearnSkillOpcode(WorldPacket& recvPacket);
+
+ void HandleQuestgiverStatusQueryOpcode(WorldPacket& recvPacket);
+ void HandleQuestgiverStatusQueryMultipleOpcode(WorldPacket& recvPacket);
+ void HandleQuestgiverHelloOpcode(WorldPacket& recvPacket);
+ void HandleQuestgiverAcceptQuestOpcode(WorldPacket& recvPacket);
+ void HandleQuestgiverQuestQueryOpcode(WorldPacket& recvPacket);
+ void HandleQuestgiverChooseRewardOpcode(WorldPacket& recvPacket);
+ void HandleQuestgiverRequestRewardOpcode(WorldPacket& recvPacket);
+ void HandleQuestQueryOpcode(WorldPacket& recvPacket);
+ void HandleQuestgiverCancel(WorldPacket& recv_data );
+ void HandleQuestLogSwapQuest(WorldPacket& recv_data );
+ void HandleQuestLogRemoveQuest(WorldPacket& recv_data);
+ void HandleQuestConfirmAccept(WorldPacket& recv_data);
+ void HandleQuestComplete(WorldPacket& recv_data);
+ void HandleQuestAutoLaunch(WorldPacket& recvPacket);
+ void HandleQuestPushToParty(WorldPacket& recvPacket);
+ void HandleQuestPushResult(WorldPacket& recvPacket);
+
+ void HandleMessagechatOpcode(WorldPacket& recvPacket);
+ void HandleTextEmoteOpcode(WorldPacket& recvPacket);
+ void HandleChatIgnoredOpcode(WorldPacket& recvPacket);
+
+ void HandleCorpseReclaimOpcode( WorldPacket& recvPacket );
+ void HandleCorpseQueryOpcode( WorldPacket& recvPacket );
+ void HandleResurrectResponseOpcode(WorldPacket& recvPacket);
+ void HandleSummonResponseOpcode(WorldPacket& recv_data);
+
+ void HandleChannelJoin(WorldPacket& recvPacket);
+ void HandleChannelLeave(WorldPacket& recvPacket);
+ void HandleChannelList(WorldPacket& recvPacket);
+ void HandleChannelPassword(WorldPacket& recvPacket);
+ void HandleChannelSetOwner(WorldPacket& recvPacket);
+ void HandleChannelOwner(WorldPacket& recvPacket);
+ void HandleChannelModerator(WorldPacket& recvPacket);
+ void HandleChannelUnmoderator(WorldPacket& recvPacket);
+ void HandleChannelMute(WorldPacket& recvPacket);
+ void HandleChannelUnmute(WorldPacket& recvPacket);
+ void HandleChannelInvite(WorldPacket& recvPacket);
+ void HandleChannelKick(WorldPacket& recvPacket);
+ void HandleChannelBan(WorldPacket& recvPacket);
+ void HandleChannelUnban(WorldPacket& recvPacket);
+ void HandleChannelAnnounce(WorldPacket& recvPacket);
+ void HandleChannelModerate(WorldPacket& recvPacket);
+ void HandleChannelRosterQuery(WorldPacket& recvPacket);
+ void HandleChannelInfoQuery(WorldPacket& recvPacket);
+ void HandleChannelJoinNotify(WorldPacket& recvPacket);
+
+ void HandleCompleteCinema(WorldPacket& recvPacket);
+ void HandleNextCinematicCamera(WorldPacket& recvPacket);
+
+ void HandlePageQuerySkippedOpcode(WorldPacket& recvPacket);
+ void HandlePageQueryOpcode(WorldPacket& recvPacket);
+
+ void HandleTutorialFlag ( WorldPacket & recv_data );
+ void HandleTutorialClear( WorldPacket & recv_data );
+ void HandleTutorialReset( WorldPacket & recv_data );
+
+ //Pet
+ void HandlePetAction( WorldPacket & recv_data );
+ void HandlePetNameQuery( WorldPacket & recv_data );
+ void HandlePetSetAction( WorldPacket & recv_data );
+ void HandlePetAbandon( WorldPacket & recv_data );
+ void HandlePetRename( WorldPacket & recv_data );
+ void HandlePetCancelAuraOpcode( WorldPacket& recvPacket );
+ void HandlePetUnlearnOpcode( WorldPacket& recvPacket );
+ void HandlePetSpellAutocastOpcode( WorldPacket& recvPacket );
+ void HandleAddDynamicTargetObsoleteOpcode( WorldPacket& recvPacket );
+
+ void HandleSetActionBar(WorldPacket& recv_data);
+
+ void HandleChangePlayerNameOpcode(WorldPacket& recv_data);
+ void HandleDeclinedPlayerNameOpcode(WorldPacket& recv_data);
+
+ void HandleTotemDestroy(WorldPacket& recv_data);
+
+ //BattleGround
+ void HandleBattleGroundHelloOpcode(WorldPacket &recv_data);
+ void HandleBattleGroundJoinOpcode(WorldPacket &recv_data);
+ void HandleBattleGroundPlayerPositionsOpcode(WorldPacket& recv_data);
+ void HandleBattleGroundPVPlogdataOpcode( WorldPacket &recv_data );
+ void HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data );
+ void HandleBattleGroundListOpcode( WorldPacket &recv_data );
+ void HandleBattleGroundLeaveOpcode( WorldPacket &recv_data );
+ void HandleBattleGroundArenaJoin( WorldPacket &recv_data );
+ void HandleBattleGroundReportAFK( WorldPacket &recv_data );
+
+ void HandleWardenDataOpcode(WorldPacket& recv_data);
+ void HandleWorldTeleportOpcode(WorldPacket& recv_data);
+ void HandleMinimapPingOpcode(WorldPacket& recv_data);
+ void HandleRandomRollOpcode(WorldPacket& recv_data);
+ void HandleFarSightOpcode(WorldPacket& recv_data);
+ void HandleSetLfgOpcode(WorldPacket& recv_data);
+ void HandleDungeonDifficultyOpcode(WorldPacket& recv_data);
+ void HandleMoveFlyModeChangeAckOpcode(WorldPacket& recv_data);
+ void HandleLfgAutoJoinOpcode(WorldPacket& recv_data);
+ void HandleLfgCancelAutoJoinOpcode(WorldPacket& recv_data);
+ void HandleLfmAutoAddMembersOpcode(WorldPacket& recv_data);
+ void HandleLfmCancelAutoAddmembersOpcode(WorldPacket& recv_data);
+ void HandleLfgClearOpcode(WorldPacket& recv_data);
+ void HandleLfmSetNoneOpcode(WorldPacket& recv_data);
+ void HandleLfmSetOpcode(WorldPacket& recv_data);
+ void HandleLfgSetCommentOpcode(WorldPacket& recv_data);
+ void HandleNewUnknownOpcode(WorldPacket& recv_data);
+ void HandleChooseTitleOpcode(WorldPacket& recv_data);
+ void HandleRealmStateRequestOpcode(WorldPacket& recv_data);
+ void HandleAllowMoveAckOpcode(WorldPacket& recv_data);
+ void HandleWhoisOpcode(WorldPacket& recv_data);
+ void HandleResetInstancesOpcode(WorldPacket& recv_data);
+
+ // Arena Team
+ void HandleInspectArenaStatsOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamQueryOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamRosterOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamAddMemberOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamInviteAcceptOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamInviteDeclineOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamLeaveOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamRemoveFromTeamOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamDisbandOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamPromoteToCaptainOpcode(WorldPacket& recv_data);
+
+ void HandleAreaSpiritHealerQueryOpcode(WorldPacket& recv_data);
+ void HandleAreaSpiritHealerQueueOpcode(WorldPacket& recv_data);
+ void HandleDismountOpcode(WorldPacket& recv_data);
+ void HandleSelfResOpcode(WorldPacket& recv_data);
+ void HandleReportSpamOpcode(WorldPacket& recv_data);
+ void HandleRequestPetInfoOpcode(WorldPacket& recv_data);
+
+ // Socket gem
+ void HandleSocketOpcode(WorldPacket& recv_data);
+
+ void HandleCancelTempItemEnchantmentOpcode(WorldPacket& recv_data);
+
+ void HandleChannelEnableVoiceOpcode(WorldPacket & recv_data);
+ void HandleVoiceSettingsOpcode(WorldPacket& recv_data);
+ void HandleChannelVoiceChatQuery(WorldPacket& recv_data);
+ void HandleSetTaxiBenchmarkOpcode(WorldPacket& recv_data);
+
+ // Guild Bank
+ void HandleGuildBankGetRights(WorldPacket& recv_data);
+ void HandleGuildBankGetMoneyAmount(WorldPacket& recv_data);
+ void HandleGuildBankQuery(WorldPacket& recv_data);
+ void HandleGuildBankTabColon(WorldPacket& recv_data);
+ void HandleGuildBankLog(WorldPacket& recv_data);
+ void HandleGuildBankDeposit(WorldPacket& recv_data);
+ void HandleGuildBankWithdraw(WorldPacket& recv_data);
+ void HandleGuildBankDepositItem(WorldPacket& recv_data);
+ void HandleGuildBankModifyTab(WorldPacket& recv_data);
+ void HandleGuildBankBuyTab(WorldPacket& recv_data);
+ void HandleGuildBankTabText(WorldPacket& recv_data);
+ void HandleGuildBankSetTabText(WorldPacket& recv_data);
+ private:
+ // private trade methods
+ void moveItems(Item* myItems[], Item* hisItems[]);
+
+ // logging helper
+ void logUnexpectedOpcode(WorldPacket *packet, const char * reason);
+ Player *_player;
+ WorldSocket *m_Socket;
+ std::string m_Address;
+
+ uint32 _security;
+ uint32 _accountId;
+ bool m_isTBC;
+
+ time_t _logoutTime;
+ bool m_playerLoading; // code processed in LoginPlayer
+ bool m_playerLogout; // code processed in LogoutPlayer
+ bool m_playerRecentlyLogout;
+ LocaleConstant m_sessionDbcLocale;
+ int m_sessionDbLocaleIndex;
+ uint32 m_latency;
+
+ ZThread::LockedQueue<WorldPacket*,ZThread::FastMutex> _recvQueue;
+};
+#endif
+/// @}
diff --git a/src/shared/WheatyExceptionReport.cpp b/src/shared/WheatyExceptionReport.cpp index ced12f96d9c..27fae70e4c1 100644 --- a/src/shared/WheatyExceptionReport.cpp +++ b/src/shared/WheatyExceptionReport.cpp @@ -1,964 +1,1014 @@ -//========================================== -// Matt Pietrek -// MSDN Magazine, 2002 -// FILE: WheatyExceptionReport.CPP -//========================================== -#define WIN32_LEAN_AND_MEAN -#pragma warning(disable:4996) -#pragma warning(disable:4312) -#pragma warning(disable:4311) -#include <windows.h> -#include <stdio.h> -#include <tchar.h> -#define _NO_CVCONST_H -#include <dbghelp.h> -#include "WheatyExceptionReport.h" -#include "svn_revision.h" -#define CrashFolder _T("Crashs") -//#pragma comment(linker, "/defaultlib:dbghelp.lib") - -inline LPTSTR ErrorMessage(DWORD dw) -{ - LPVOID lpMsgBuf; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - dw, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &lpMsgBuf, - 0, NULL ); - return (LPTSTR)lpMsgBuf; -} - -//============================== Global Variables ============================= - -// -// Declare the static variables of the WheatyExceptionReport class -// -TCHAR WheatyExceptionReport::m_szLogFileName[MAX_PATH]; -LPTOP_LEVEL_EXCEPTION_FILTER WheatyExceptionReport::m_previousFilter; -HANDLE WheatyExceptionReport::m_hReportFile; -HANDLE WheatyExceptionReport::m_hProcess; - -// Declare global instance of class -WheatyExceptionReport g_WheatyExceptionReport; - -//============================== Class Methods ============================= - -WheatyExceptionReport::WheatyExceptionReport( ) // Constructor -{ - // Install the unhandled exception filter function - m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter); - m_hProcess = GetCurrentProcess(); -} - -//============ -// Destructor -//============ -WheatyExceptionReport::~WheatyExceptionReport( ) -{ - if(m_previousFilter) - SetUnhandledExceptionFilter( m_previousFilter ); -} - -//=========================================================== -// Entry point where control comes on an unhandled exception -//=========================================================== -LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter( -PEXCEPTION_POINTERS pExceptionInfo ) -{ - TCHAR module_folder_name[MAX_PATH]; - GetModuleFileName( 0, module_folder_name, MAX_PATH ); - TCHAR* pos = _tcsrchr(module_folder_name, '\\'); - if(!pos) - return 0; - pos[0] = '\0'; - ++pos; - - TCHAR crash_folder_path[MAX_PATH]; - sprintf(crash_folder_path, "%s\\%s", module_folder_name, CrashFolder); - if(!CreateDirectory(crash_folder_path, NULL)) - { - if(GetLastError() != ERROR_ALREADY_EXISTS) - return 0; - } - - SYSTEMTIME systime; - GetLocalTime(&systime); - sprintf(m_szLogFileName, "%s\\%s_[%u-%u_%u-%u-%u].txt", - crash_folder_path, pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond - ); - - m_hReportFile = CreateFile( m_szLogFileName, - GENERIC_WRITE, - 0, - 0, - OPEN_ALWAYS, - FILE_FLAG_WRITE_THROUGH, - 0 ); - - if ( m_hReportFile ) - { - SetFilePointer( m_hReportFile, 0, 0, FILE_END ); - - GenerateExceptionReport( pExceptionInfo ); - - CloseHandle( m_hReportFile ); - m_hReportFile = 0; - } - - if ( m_previousFilter ) - return m_previousFilter( pExceptionInfo ); - else - return EXCEPTION_EXECUTE_HANDLER/*EXCEPTION_CONTINUE_SEARCH*/; -} - -BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxcount) -{ - if(!sProcessorName) - return FALSE; - - HKEY hKey; - LONG lRet; - lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"), - 0, KEY_QUERY_VALUE, &hKey); - if (lRet != ERROR_SUCCESS) - return FALSE; - TCHAR szTmp[2048]; - DWORD cntBytes = sizeof(szTmp); - lRet = ::RegQueryValueEx(hKey, _T("ProcessorNameString"), NULL, NULL, - (LPBYTE)szTmp, &cntBytes); - if (lRet != ERROR_SUCCESS) - return FALSE; - ::RegCloseKey(hKey); - sProcessorName[0] = '\0'; - // Skip spaces - TCHAR* psz = szTmp; - while (iswspace(*psz)) - ++psz; - _tcsncpy(sProcessorName, psz, maxcount); - return TRUE; -} - -BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax) -{ - // Try calling GetVersionEx using the OSVERSIONINFOEX structure. - // If that fails, try using the OSVERSIONINFO structure. - OSVERSIONINFOEX osvi = { 0 }; - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - BOOL bOsVersionInfoEx; - bOsVersionInfoEx = ::GetVersionEx((LPOSVERSIONINFO)(&osvi)); - if (!bOsVersionInfoEx) - { - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - if (!::GetVersionEx((OSVERSIONINFO*)&osvi)) - return FALSE; - } - *szVersion = _T('\0'); - TCHAR wszTmp[128]; - switch (osvi.dwPlatformId) - { - // Windows NT product family. - case VER_PLATFORM_WIN32_NT: - // Test for the specific product family. - if (osvi.dwMajorVersion == 6) - _tcsncat(szVersion, _T("Windows Vista or Windows Server 2008 "), cntMax); - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) - _tcsncat(szVersion, _T("Microsoft Windows Server 2003 "), cntMax); - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) - _tcsncat(szVersion, _T("Microsoft Windows XP "), cntMax); - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) - _tcsncat(szVersion, _T("Microsoft Windows 2000 "), cntMax); - if (osvi.dwMajorVersion <= 4 ) - _tcsncat(szVersion, _T("Microsoft Windows NT "), cntMax); - - // Test for specific product on Windows NT 4.0 SP6 and later. - if (bOsVersionInfoEx) - { - // Test for the workstation type. - #if WINVER < 0x0500 - if (osvi.wReserved[1] == VER_NT_WORKSTATION) - #else - if (osvi.wProductType == VER_NT_WORKSTATION) - #endif // WINVER < 0x0500 - { - if (osvi.dwMajorVersion == 4) - _tcsncat(szVersion, _T("Workstation 4.0 "), cntMax); - #if WINVER < 0x0500 - else if (osvi.wReserved[0] & VER_SUITE_PERSONAL) - #else - else if (osvi.wSuiteMask & VER_SUITE_PERSONAL) - #endif // WINVER < 0x0500 - _tcsncat(szVersion, _T("Home Edition "), cntMax); - #if WINVER < 0x0500 - else if (osvi.wReserved[0] & VER_SUITE_EMBEDDEDNT) - #else - else if (osvi.wSuiteMask & VER_SUITE_EMBEDDEDNT) - #endif // WINVER < 0x0500 - _tcsncat(szVersion, _T("Embedded "), cntMax); - else - _tcsncat(szVersion, _T("Professional "), cntMax); - } - // Test for the server type. - #if WINVER < 0x0500 - else if (osvi.wReserved[1] == VER_NT_SERVER) - #else - else if (osvi.wProductType == VER_NT_SERVER) - #endif // WINVER < 0x0500 - { - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) - { - #if WINVER < 0x0500 - if (osvi.wReserved[0] & VER_SUITE_DATACENTER) - #else - if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - #endif // WINVER < 0x0500 - _tcsncat(szVersion, _T("Datacenter Edition "), cntMax); - #if WINVER < 0x0500 - else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE) - #else - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - #endif // WINVER < 0x0500 - _tcsncat(szVersion, _T("Enterprise Edition "), cntMax); - #if WINVER < 0x0500 - else if (osvi.wReserved[0] == VER_SUITE_BLADE) - #else - else if (osvi.wSuiteMask == VER_SUITE_BLADE) - #endif // WINVER < 0x0500 - _tcsncat(szVersion, _T("Web Edition "), cntMax); - else - _tcsncat(szVersion, _T("Standard Edition "), cntMax); - } - else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) - { - #if WINVER < 0x0500 - if (osvi.wReserved[0] & VER_SUITE_DATACENTER) - #else - if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - #endif // WINVER < 0x0500 - _tcsncat(szVersion, _T("Datacenter Server "), cntMax); - #if WINVER < 0x0500 - else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE ) - #else - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) - #endif // WINVER < 0x0500 - _tcsncat(szVersion, _T("Advanced Server "), cntMax); - else - _tcsncat(szVersion, _T("Server "), cntMax); - } - else // Windows NT 4.0 - { - #if WINVER < 0x0500 - if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE) - #else - if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - #endif // WINVER < 0x0500 - _tcsncat(szVersion, _T("Server 4.0, Enterprise Edition "), cntMax); - else - _tcsncat(szVersion, _T("Server 4.0 "), cntMax); - } - } - } - // Display service pack (if any) and build number. - if (osvi.dwMajorVersion == 4 && _tcsicmp(osvi.szCSDVersion, _T("Service Pack 6")) == 0) - { - HKEY hKey; - LONG lRet; - - // Test for SP6 versus SP6a. - lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey); - if (lRet == ERROR_SUCCESS) - { - _stprintf(wszTmp, _T("Service Pack 6a (Version %d.%d, Build %d)"), - osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); - _tcsncat(szVersion, wszTmp, cntMax); - } - else // Windows NT 4.0 prior to SP6a - { - _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"), - osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); - _tcsncat(szVersion, wszTmp, cntMax); - } - ::RegCloseKey(hKey); - } - else // Windows NT 3.51 and earlier or Windows 2000 and later - { - if (!_tcslen(osvi.szCSDVersion)) - _stprintf(wszTmp, _T("(Version %d.%d, Build %d)"), - osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); - else - _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"), - osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); - _tcsncat(szVersion, wszTmp, cntMax); - } - break; - default: - _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"), - osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); - _tcsncat(szVersion, wszTmp, cntMax); - break; - } - - return TRUE; -} - -void WheatyExceptionReport::PrintSystemInfo() -{ - SYSTEM_INFO SystemInfo; - ::GetSystemInfo(&SystemInfo); - - MEMORYSTATUS MemoryStatus; - MemoryStatus.dwLength = sizeof (MEMORYSTATUS); - ::GlobalMemoryStatus(&MemoryStatus); - TCHAR sString[1024]; - _tprintf(_T("//=====================================================\r\n")); - if (_GetProcessorName(sString, countof(sString))) - _tprintf(_T("*** Hardware ***\r\nProcessor: %s\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"), - sString, SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400); - else - _tprintf(_T("*** Hardware ***\r\nProcessor: <unknown>\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"), - SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400); - - if(_GetWindowsVersion(sString, countof(sString))) - _tprintf(_T("\r\n*** Operation System ***\r\n%s\r\n"), sString); - else - _tprintf(_T("\r\n*** Operation System:\r\n<unknown>\r\n")); -} - -//=========================================================================== -// Open the report file, and write the desired information to it. Called by -// WheatyUnhandledExceptionFilter -//=========================================================================== -void WheatyExceptionReport::GenerateExceptionReport( -PEXCEPTION_POINTERS pExceptionInfo ) -{ - SYSTEMTIME systime; - GetLocalTime(&systime); - - // Start out with a banner - _tprintf(_T("Revision: %s\r\n"), SVN_REVISION); - _tprintf(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute); - PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; - - PrintSystemInfo(); - // First print information about the type of fault - _tprintf(_T("\r\n//=====================================================\r\n")); - _tprintf( _T("Exception code: %08X %s\r\n"), - pExceptionRecord->ExceptionCode, - GetExceptionString(pExceptionRecord->ExceptionCode) ); - - // Now print information about where the fault occured - TCHAR szFaultingModule[MAX_PATH]; - DWORD section; - DWORD_PTR offset; - GetLogicalAddress( pExceptionRecord->ExceptionAddress, - szFaultingModule, - sizeof( szFaultingModule ), - section, offset ); - -#ifdef _M_IX86 - _tprintf( _T("Fault address: %08X %02X:%08X %s\r\n"), - pExceptionRecord->ExceptionAddress, - section, offset, szFaultingModule ); -#endif -#ifdef _M_X64 - _tprintf( _T("Fault address: %016I64X %02X:%016I64X %s\r\n"), - pExceptionRecord->ExceptionAddress, - section, offset, szFaultingModule ); -#endif - - PCONTEXT pCtx = pExceptionInfo->ContextRecord; - - // Show the registers - #ifdef _M_IX86 // X86 Only! - _tprintf( _T("\r\nRegisters:\r\n") ); - - _tprintf(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n") - ,pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, - pCtx->Esi, pCtx->Edi ); - - _tprintf( _T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip ); - _tprintf( _T("SS:ESP:%04X:%08X EBP:%08X\r\n"), - pCtx->SegSs, pCtx->Esp, pCtx->Ebp ); - _tprintf( _T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"), - pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs ); - _tprintf( _T("Flags:%08X\r\n"), pCtx->EFlags ); - #endif - - #ifdef _M_X64 - _tprintf( _T("\r\nRegisters:\r\n") ); - _tprintf(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n") - _T("R8: %016I64X\r\nR9: %016I64X\r\nR10:%016I64X\r\nR11:%016I64X\r\nR12:%016I64X\r\nR13:%016I64X\r\nR14:%016I64X\r\nR15:%016I64X\r\n") - ,pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx, - pCtx->Rsi, pCtx->Rdi ,pCtx->R9,pCtx->R10,pCtx->R11,pCtx->R12,pCtx->R13,pCtx->R14,pCtx->R15); - _tprintf( _T("CS:RIP:%04X:%016I64X\r\n"), pCtx->SegCs, pCtx->Rip ); - _tprintf( _T("SS:RSP:%04X:%016X RBP:%08X\r\n"), - pCtx->SegSs, pCtx->Rsp, pCtx->Rbp ); - _tprintf( _T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"), - pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs ); - _tprintf( _T("Flags:%08X\r\n"), pCtx->EFlags ); - #endif - - SymSetOptions( SYMOPT_DEFERRED_LOADS ); - - // Initialize DbgHelp - if ( !SymInitialize( GetCurrentProcess(), 0, TRUE ) ) - { - _tprintf(_T("\n\rCRITICAL ERROR.\n\r Couldn't initialize the symbol handler for process.\n\rError [%s].\n\r\n\r"), - ErrorMessage(GetLastError())); - } - - CONTEXT trashableContext = *pCtx; - - WriteStackDetails( &trashableContext, false ); - -// #ifdef _M_IX86 // X86 Only! - - _tprintf( _T("========================\r\n") ); - _tprintf( _T("Local Variables And Parameters\r\n") ); - - trashableContext = *pCtx; - WriteStackDetails( &trashableContext, true ); - - _tprintf( _T("========================\r\n") ); - _tprintf( _T("Global Variables\r\n") ); - - SymEnumSymbols( GetCurrentProcess(), - (DWORD64)GetModuleHandle(szFaultingModule), - 0, EnumerateSymbolsCallback, 0 ); - // #endif // X86 Only! - - SymCleanup( GetCurrentProcess() ); - - _tprintf( _T("\r\n") ); -} - -//====================================================================== -// Given an exception code, returns a pointer to a static string with a -// description of the exception -//====================================================================== -LPTSTR WheatyExceptionReport::GetExceptionString( DWORD dwCode ) -{ - #define EXCEPTION( x ) case EXCEPTION_##x: return _T(#x); - - switch ( dwCode ) - { - EXCEPTION( ACCESS_VIOLATION ) - EXCEPTION( DATATYPE_MISALIGNMENT ) - EXCEPTION( BREAKPOINT ) - EXCEPTION( SINGLE_STEP ) - EXCEPTION( ARRAY_BOUNDS_EXCEEDED ) - EXCEPTION( FLT_DENORMAL_OPERAND ) - EXCEPTION( FLT_DIVIDE_BY_ZERO ) - EXCEPTION( FLT_INEXACT_RESULT ) - EXCEPTION( FLT_INVALID_OPERATION ) - EXCEPTION( FLT_OVERFLOW ) - EXCEPTION( FLT_STACK_CHECK ) - EXCEPTION( FLT_UNDERFLOW ) - EXCEPTION( INT_DIVIDE_BY_ZERO ) - EXCEPTION( INT_OVERFLOW ) - EXCEPTION( PRIV_INSTRUCTION ) - EXCEPTION( IN_PAGE_ERROR ) - EXCEPTION( ILLEGAL_INSTRUCTION ) - EXCEPTION( NONCONTINUABLE_EXCEPTION ) - EXCEPTION( STACK_OVERFLOW ) - EXCEPTION( INVALID_DISPOSITION ) - EXCEPTION( GUARD_PAGE ) - EXCEPTION( INVALID_HANDLE ) - } - - // If not one of the "known" exceptions, try to get the string - // from NTDLL.DLL's message table. - - static TCHAR szBuffer[512] = { 0 }; - - FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, - GetModuleHandle( _T("NTDLL.DLL") ), - dwCode, 0, szBuffer, sizeof( szBuffer ), 0 ); - - return szBuffer; -} - -//============================================================================= -// Given a linear address, locates the module, section, and offset containing -// that address. -// -// Note: the szModule paramater buffer is an output buffer of length specified -// by the len parameter (in characters!) -//============================================================================= -BOOL WheatyExceptionReport::GetLogicalAddress( -PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset ) -{ - MEMORY_BASIC_INFORMATION mbi; - - if ( !VirtualQuery( addr, &mbi, sizeof(mbi) ) ) - return FALSE; - - DWORD_PTR hMod = (DWORD_PTR)mbi.AllocationBase; - - if ( !GetModuleFileName( (HMODULE)hMod, szModule, len ) ) - return FALSE; - - // Point to the DOS header in memory - PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod; - - // From the DOS header, find the NT (PE) header - PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + DWORD_PTR(pDosHdr->e_lfanew)); - - PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr ); - - DWORD_PTR rva = (DWORD_PTR)addr - hMod; // RVA is offset from module load address - - // Iterate through the section table, looking for the one that encompasses - // the linear address. - for ( unsigned i = 0; - i < pNtHdr->FileHeader.NumberOfSections; - i++, pSection++ ) - { - DWORD_PTR sectionStart = pSection->VirtualAddress; - DWORD_PTR sectionEnd = sectionStart - + DWORD_PTR(max(pSection->SizeOfRawData, pSection->Misc.VirtualSize)); - - // Is the address in this section??? - if ( (rva >= sectionStart) && (rva <= sectionEnd) ) - { - // Yes, address is in the section. Calculate section and offset, - // and store in the "section" & "offset" params, which were - // passed by reference. - section = i+1; - offset = rva - sectionStart; - return TRUE; - } - } - - return FALSE; // Should never get here! -} - -// It contains SYMBOL_INFO structure plus additional -// space for the name of the symbol -struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE -{ - CSymbolInfoPackage() - { - si.SizeOfStruct = sizeof(SYMBOL_INFO); - si.MaxNameLen = sizeof(name); - } -}; - -//============================================================ -// Walks the stack, and writes the results to the report file -//============================================================ -void WheatyExceptionReport::WriteStackDetails( -PCONTEXT pContext, -bool bWriteVariables ) // true if local/params should be output -{ - _tprintf( _T("\r\nCall stack:\r\n") ); - - _tprintf( _T("Address Frame Function SourceFile\r\n") ); - - DWORD dwMachineType = 0; - // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag - - STACKFRAME64 sf; - memset( &sf, 0, sizeof(sf) ); - - #ifdef _M_IX86 - // Initialize the STACKFRAME structure for the first call. This is only - // necessary for Intel CPUs, and isn't mentioned in the documentation. - sf.AddrPC.Offset = pContext->Eip; - sf.AddrPC.Mode = AddrModeFlat; - sf.AddrStack.Offset = pContext->Esp; - sf.AddrStack.Mode = AddrModeFlat; - sf.AddrFrame.Offset = pContext->Ebp; - sf.AddrFrame.Mode = AddrModeFlat; - - dwMachineType = IMAGE_FILE_MACHINE_I386; - #endif - -#ifdef _M_X64 - sf.AddrPC.Offset = pContext->Rip; - sf.AddrPC.Mode = AddrModeFlat; - sf.AddrStack.Offset = pContext->Rsp; - sf.AddrStack.Mode = AddrModeFlat; - sf.AddrFrame.Offset = pContext->Rbp; - sf.AddrFrame.Mode = AddrModeFlat; - dwMachineType = IMAGE_FILE_MACHINE_AMD64; -#endif - - while ( 1 ) - { - // Get the next stack frame - if ( ! StackWalk64( dwMachineType, - m_hProcess, - GetCurrentThread(), - &sf, - pContext, - 0, - SymFunctionTableAccess64, - SymGetModuleBase64, - 0 ) ) - break; - if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure - break; // the frame is OK. Bail if not. -#ifdef _M_IX86 - _tprintf( _T("%08X %08X "), sf.AddrPC.Offset, sf.AddrFrame.Offset ); -#endif -#ifdef _M_X64 - _tprintf( _T("%016I64X %016I64X "), sf.AddrPC.Offset, sf.AddrFrame.Offset ); -#endif - - DWORD64 symDisplacement = 0; // Displacement of the input address, - // relative to the start of the symbol - - // Get the name of the function for this stack frame entry - CSymbolInfoPackage sip; - if ( SymFromAddr( - m_hProcess, // Process handle of the current process - sf.AddrPC.Offset, // Symbol address - &symDisplacement, // Address of the variable that will receive the displacement - &sip.si // Address of the SYMBOL_INFO structure (inside "sip" object) - )) - { - _tprintf( _T("%hs+%I64X"), sip.si.Name, symDisplacement ); - - } - else // No symbol found. Print out the logical address instead. - { - TCHAR szModule[MAX_PATH] = _T(""); - DWORD section = 0; - DWORD_PTR offset = 0; - - GetLogicalAddress( (PVOID)sf.AddrPC.Offset, - szModule, sizeof(szModule), section, offset ); -#ifdef _M_IX86 - _tprintf( _T("%04X:%08X %s"), section, offset, szModule ); -#endif -#ifdef _M_X64 - _tprintf( _T("%04X:%016I64X %s"), section, offset, szModule ); -#endif - } - - // Get the source line for this stack frame entry - IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE) }; - DWORD dwLineDisplacement; - if ( SymGetLineFromAddr64( m_hProcess, sf.AddrPC.Offset, - &dwLineDisplacement, &lineInfo ) ) - { - _tprintf(_T(" %s line %u"),lineInfo.FileName,lineInfo.LineNumber); - } - - _tprintf( _T("\r\n") ); - - // Write out the variables, if desired - if ( bWriteVariables ) - { - // Use SymSetContext to get just the locals/params for this frame - IMAGEHLP_STACK_FRAME imagehlpStackFrame; - imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset; - SymSetContext( m_hProcess, &imagehlpStackFrame, 0 ); - - // Enumerate the locals/parameters - SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &sf ); - - _tprintf( _T("\r\n") ); - } - } - -} - -////////////////////////////////////////////////////////////////////////////// -// The function invoked by SymEnumSymbols -////////////////////////////////////////////////////////////////////////////// - -BOOL CALLBACK -WheatyExceptionReport::EnumerateSymbolsCallback( -PSYMBOL_INFO pSymInfo, -ULONG SymbolSize, -PVOID UserContext ) -{ - - char szBuffer[2048]; - - __try - { - if ( FormatSymbolValue( pSymInfo, (STACKFRAME*)UserContext, - szBuffer, sizeof(szBuffer) ) ) - _tprintf( _T("\t%s\r\n"), szBuffer ); - } - __except( 1 ) - { - _tprintf( _T("punting on symbol %s\r\n"), pSymInfo->Name ); - } - - return TRUE; -} - -////////////////////////////////////////////////////////////////////////////// -// Given a SYMBOL_INFO representing a particular variable, displays its -// contents. If it's a user defined type, display the members and their -// values. -////////////////////////////////////////////////////////////////////////////// -bool WheatyExceptionReport::FormatSymbolValue( -PSYMBOL_INFO pSym, -STACKFRAME * sf, -char * pszBuffer, -unsigned cbBuffer ) -{ - char * pszCurrBuffer = pszBuffer; - - // Indicate if the variable is a local or parameter - if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER ) - pszCurrBuffer += sprintf( pszCurrBuffer, "Parameter " ); - else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL ) - pszCurrBuffer += sprintf( pszCurrBuffer, "Local " ); - - // If it's a function, don't do anything. - if ( pSym->Tag == 5 ) // SymTagFunction from CVCONST.H from the DIA SDK - return false; - - DWORD_PTR pVariable = 0; // Will point to the variable's data in memory - - if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE ) - { - // if ( pSym->Register == 8 ) // EBP is the value 8 (in DBGHELP 5.1) - { // This may change!!! - pVariable = sf->AddrFrame.Offset; - pVariable += (DWORD_PTR)pSym->Address; - } - // else - // return false; - } - else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER ) - { - return false; // Don't try to report register variable - } - else - { - pVariable = (DWORD_PTR)pSym->Address; // It must be a global variable - } - - // Determine if the variable is a user defined type (UDT). IF so, bHandled - // will return true. - bool bHandled; - pszCurrBuffer = DumpTypeIndex(pszCurrBuffer,pSym->ModBase, pSym->TypeIndex, - 0, pVariable, bHandled, pSym->Name ); - - if ( !bHandled ) - { - // The symbol wasn't a UDT, so do basic, stupid formatting of the - // variable. Based on the size, we're assuming it's a char, WORD, or - // DWORD. - BasicType basicType = GetBasicType( pSym->TypeIndex, pSym->ModBase ); - pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]); - - // Emit the variable name - pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", pSym->Name ); - - pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType, pSym->Size, - (PVOID)pVariable ); - } - - return true; -} - -////////////////////////////////////////////////////////////////////////////// -// If it's a user defined type (UDT), recurse through its members until we're -// at fundamental types. When he hit fundamental types, return -// bHandled = false, so that FormatSymbolValue() will format them. -////////////////////////////////////////////////////////////////////////////// -char * WheatyExceptionReport::DumpTypeIndex( -char * pszCurrBuffer, -DWORD64 modBase, -DWORD dwTypeIndex, -unsigned nestingLevel, -DWORD_PTR offset, -bool & bHandled, -char* Name) -{ - bHandled = false; - - // Get the name of the symbol. This will either be a Type name (if a UDT), - // or the structure member name. - WCHAR * pwszTypeName; - if ( SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_SYMNAME, - &pwszTypeName ) ) - { - pszCurrBuffer += sprintf( pszCurrBuffer, " %ls", pwszTypeName ); - LocalFree( pwszTypeName ); - } - - // Determine how many children this type has. - DWORD dwChildrenCount = 0; - SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT, - &dwChildrenCount ); - - if ( !dwChildrenCount ) // If no children, we're done - return pszCurrBuffer; - - // Prepare to get an array of "TypeIds", representing each of the children. - // SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a - // TI_FINDCHILDREN_PARAMS struct has. Use derivation to accomplish this. - struct FINDCHILDREN : TI_FINDCHILDREN_PARAMS - { - ULONG MoreChildIds[1024]; - FINDCHILDREN(){Count = sizeof(MoreChildIds) / sizeof(MoreChildIds[0]);} - } children; - - children.Count = dwChildrenCount; - children.Start= 0; - - // Get the array of TypeIds, one for each child type - if ( !SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN, - &children ) ) - { - return pszCurrBuffer; - } - - // Append a line feed - pszCurrBuffer += sprintf( pszCurrBuffer, "\r\n" ); - - // Iterate through each of the children - for ( unsigned i = 0; i < dwChildrenCount; i++ ) - { - // Add appropriate indentation level (since this routine is recursive) - for ( unsigned j = 0; j <= nestingLevel+1; j++ ) - pszCurrBuffer += sprintf( pszCurrBuffer, "\t" ); - - // Recurse for each of the child types - bool bHandled2; - BasicType basicType = GetBasicType(children.ChildId[i], modBase ); - pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]); - - pszCurrBuffer = DumpTypeIndex( pszCurrBuffer, modBase, - children.ChildId[i], nestingLevel+1, - offset, bHandled2, ""/*Name */); - - // If the child wasn't a UDT, format it appropriately - if ( !bHandled2 ) - { - // Get the offset of the child member, relative to its parent - DWORD dwMemberOffset; - SymGetTypeInfo( m_hProcess, modBase, children.ChildId[i], - TI_GET_OFFSET, &dwMemberOffset ); - - // Get the real "TypeId" of the child. We need this for the - // SymGetTypeInfo( TI_GET_TYPEID ) call below. - DWORD typeId; - SymGetTypeInfo( m_hProcess, modBase, children.ChildId[i], - TI_GET_TYPEID, &typeId ); - - // Get the size of the child member - ULONG64 length; - SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_LENGTH,&length); - - // Calculate the address of the member - DWORD_PTR dwFinalOffset = offset + dwMemberOffset; - - // BasicType basicType = GetBasicType(children.ChildId[i], modBase ); - // - // pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]); - // - // Emit the variable name - // pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", Name ); - - pszCurrBuffer = FormatOutputValue( pszCurrBuffer, basicType, - length, (PVOID)dwFinalOffset ); - - pszCurrBuffer += sprintf( pszCurrBuffer, "\r\n" ); - } - } - - bHandled = true; - return pszCurrBuffer; -} - -char * WheatyExceptionReport::FormatOutputValue( char * pszCurrBuffer, -BasicType basicType, -DWORD64 length, -PVOID pAddress ) -{ - // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!) - if ( length == 1 ) - pszCurrBuffer += sprintf( pszCurrBuffer, " = %X", *(PBYTE)pAddress ); - else if ( length == 2 ) - pszCurrBuffer += sprintf( pszCurrBuffer, " = %X", *(PWORD)pAddress ); - else if ( length == 4 ) - { - if ( basicType == btFloat ) - { - pszCurrBuffer += sprintf(pszCurrBuffer," = %f", *(PFLOAT)pAddress); - } - else if ( basicType == btChar ) - { - if ( !IsBadStringPtr( *(PSTR*)pAddress, 32) ) - { - pszCurrBuffer += sprintf( pszCurrBuffer, " = \"%.31s\"", - *(PDWORD)pAddress ); - } - else - pszCurrBuffer += sprintf( pszCurrBuffer, " = %X", - *(PDWORD)pAddress ); - } - else - pszCurrBuffer += sprintf(pszCurrBuffer," = %X", *(PDWORD)pAddress); - } - else if ( length == 8 ) - { - if ( basicType == btFloat ) - { - pszCurrBuffer += sprintf( pszCurrBuffer, " = %lf", - *(double *)pAddress ); - } - else - pszCurrBuffer += sprintf( pszCurrBuffer, " = %I64X", - *(DWORD64*)pAddress ); - } - - return pszCurrBuffer; -} - -BasicType -WheatyExceptionReport::GetBasicType( DWORD typeIndex, DWORD64 modBase ) -{ - BasicType basicType; - if ( SymGetTypeInfo( m_hProcess, modBase, typeIndex, - TI_GET_BASETYPE, &basicType ) ) - { - return basicType; - } - - // Get the real "TypeId" of the child. We need this for the - // SymGetTypeInfo( TI_GET_TYPEID ) call below. - DWORD typeId; - if (SymGetTypeInfo(m_hProcess,modBase, typeIndex, TI_GET_TYPEID, &typeId)) - { - if ( SymGetTypeInfo( m_hProcess, modBase, typeId, TI_GET_BASETYPE, - &basicType ) ) - { - return basicType; - } - } - - return btNoType; -} - -//============================================================================ -// Helper function that writes to the report file, and allows the user to use -// printf style formating -//============================================================================ -int __cdecl WheatyExceptionReport::_tprintf(const TCHAR * format, ...) -{ - TCHAR szBuff[1024]; - int retValue; - DWORD cbWritten; - va_list argptr; - - va_start( argptr, format ); - retValue = vsprintf( szBuff, format, argptr ); - va_end( argptr ); - - WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0 ); - - return retValue; -} +//==========================================
+// Matt Pietrek
+// MSDN Magazine, 2002
+// FILE: WheatyExceptionReport.CPP
+//==========================================
+#define WIN32_LEAN_AND_MEAN
+#pragma warning(disable:4996)
+#pragma warning(disable:4312)
+#pragma warning(disable:4311)
+#include <windows.h>
+#include <tlhelp32.h>
+#include <stdio.h>
+#include <tchar.h>
+#define _NO_CVCONST_H
+#include <dbghelp.h>
+#include "WheatyExceptionReport.h"
+#include "svn_revision.h"
+#define CrashFolder _T("Crashs")
+//#pragma comment(linker, "/defaultlib:dbghelp.lib")
+
+inline LPTSTR ErrorMessage(DWORD dw)
+{
+ LPVOID lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ dw,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf,
+ 0, NULL );
+ return (LPTSTR)lpMsgBuf;
+}
+
+//============================== Global Variables =============================
+
+//
+// Declare the static variables of the WheatyExceptionReport class
+//
+TCHAR WheatyExceptionReport::m_szLogFileName[MAX_PATH];
+LPTOP_LEVEL_EXCEPTION_FILTER WheatyExceptionReport::m_previousFilter;
+HANDLE WheatyExceptionReport::m_hReportFile;
+HANDLE WheatyExceptionReport::m_hProcess;
+
+// Declare global instance of class
+WheatyExceptionReport g_WheatyExceptionReport;
+
+//============================== Class Methods =============================
+
+WheatyExceptionReport::WheatyExceptionReport( ) // Constructor
+{
+ // Install the unhandled exception filter function
+ m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter);
+ m_hProcess = GetCurrentProcess();
+}
+
+//============
+// Destructor
+//============
+WheatyExceptionReport::~WheatyExceptionReport( )
+{
+ if(m_previousFilter)
+ SetUnhandledExceptionFilter( m_previousFilter );
+}
+
+//===========================================================
+// Entry point where control comes on an unhandled exception
+//===========================================================
+LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter(
+PEXCEPTION_POINTERS pExceptionInfo )
+{
+ TCHAR module_folder_name[MAX_PATH];
+ GetModuleFileName( 0, module_folder_name, MAX_PATH );
+ TCHAR* pos = _tcsrchr(module_folder_name, '\\');
+ if(!pos)
+ return 0;
+ pos[0] = '\0';
+ ++pos;
+
+ TCHAR crash_folder_path[MAX_PATH];
+ sprintf(crash_folder_path, "%s\\%s", module_folder_name, CrashFolder);
+ if(!CreateDirectory(crash_folder_path, NULL))
+ {
+ if(GetLastError() != ERROR_ALREADY_EXISTS)
+ return 0;
+ }
+
+ SYSTEMTIME systime;
+ GetLocalTime(&systime);
+ sprintf(m_szLogFileName, "%s\\%s_[%u-%u_%u-%u-%u].txt",
+ crash_folder_path, pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond
+ );
+
+ m_hReportFile = CreateFile( m_szLogFileName,
+ GENERIC_WRITE,
+ 0,
+ 0,
+ OPEN_ALWAYS,
+ FILE_FLAG_WRITE_THROUGH,
+ 0 );
+
+ if ( m_hReportFile )
+ {
+ SetFilePointer( m_hReportFile, 0, 0, FILE_END );
+
+ GenerateExceptionReport( pExceptionInfo );
+
+ CloseHandle( m_hReportFile );
+ m_hReportFile = 0;
+ }
+
+ if ( m_previousFilter )
+ return m_previousFilter( pExceptionInfo );
+ else
+ return EXCEPTION_EXECUTE_HANDLER/*EXCEPTION_CONTINUE_SEARCH*/;
+}
+
+BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxcount)
+{
+ if(!sProcessorName)
+ return FALSE;
+
+ HKEY hKey;
+ LONG lRet;
+ lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
+ 0, KEY_QUERY_VALUE, &hKey);
+ if (lRet != ERROR_SUCCESS)
+ return FALSE;
+ TCHAR szTmp[2048];
+ DWORD cntBytes = sizeof(szTmp);
+ lRet = ::RegQueryValueEx(hKey, _T("ProcessorNameString"), NULL, NULL,
+ (LPBYTE)szTmp, &cntBytes);
+ if (lRet != ERROR_SUCCESS)
+ return FALSE;
+ ::RegCloseKey(hKey);
+ sProcessorName[0] = '\0';
+ // Skip spaces
+ TCHAR* psz = szTmp;
+ while (iswspace(*psz))
+ ++psz;
+ _tcsncpy(sProcessorName, psz, maxcount);
+ return TRUE;
+}
+
+BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax)
+{
+ // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
+ // If that fails, try using the OSVERSIONINFO structure.
+ OSVERSIONINFOEX osvi = { 0 };
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ BOOL bOsVersionInfoEx;
+ bOsVersionInfoEx = ::GetVersionEx((LPOSVERSIONINFO)(&osvi));
+ if (!bOsVersionInfoEx)
+ {
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (!::GetVersionEx((OSVERSIONINFO*)&osvi))
+ return FALSE;
+ }
+ *szVersion = _T('\0');
+ TCHAR wszTmp[128];
+ switch (osvi.dwPlatformId)
+ {
+ // Windows NT product family.
+ case VER_PLATFORM_WIN32_NT:
+ // Test for the specific product family.
+ if (osvi.dwMajorVersion == 6)
+ _tcsncat(szVersion, _T("Windows Vista or Windows Server 2008 "), cntMax);
+ if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
+ _tcsncat(szVersion, _T("Microsoft Windows Server 2003 "), cntMax);
+ if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
+ _tcsncat(szVersion, _T("Microsoft Windows XP "), cntMax);
+ if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
+ _tcsncat(szVersion, _T("Microsoft Windows 2000 "), cntMax);
+ if (osvi.dwMajorVersion <= 4 )
+ _tcsncat(szVersion, _T("Microsoft Windows NT "), cntMax);
+
+ // Test for specific product on Windows NT 4.0 SP6 and later.
+ if (bOsVersionInfoEx)
+ {
+ // Test for the workstation type.
+ #if WINVER < 0x0500
+ if (osvi.wReserved[1] == VER_NT_WORKSTATION)
+ #else
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ #endif // WINVER < 0x0500
+ {
+ if (osvi.dwMajorVersion == 4)
+ _tcsncat(szVersion, _T("Workstation 4.0 "), cntMax);
+ #if WINVER < 0x0500
+ else if (osvi.wReserved[0] & VER_SUITE_PERSONAL)
+ #else
+ else if (osvi.wSuiteMask & VER_SUITE_PERSONAL)
+ #endif // WINVER < 0x0500
+ _tcsncat(szVersion, _T("Home Edition "), cntMax);
+ #if WINVER < 0x0500
+ else if (osvi.wReserved[0] & VER_SUITE_EMBEDDEDNT)
+ #else
+ else if (osvi.wSuiteMask & VER_SUITE_EMBEDDEDNT)
+ #endif // WINVER < 0x0500
+ _tcsncat(szVersion, _T("Embedded "), cntMax);
+ else
+ _tcsncat(szVersion, _T("Professional "), cntMax);
+ }
+ // Test for the server type.
+ #if WINVER < 0x0500
+ else if (osvi.wReserved[1] == VER_NT_SERVER)
+ #else
+ else if (osvi.wProductType == VER_NT_SERVER)
+ #endif // WINVER < 0x0500
+ {
+ if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
+ {
+ #if WINVER < 0x0500
+ if (osvi.wReserved[0] & VER_SUITE_DATACENTER)
+ #else
+ if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+ #endif // WINVER < 0x0500
+ _tcsncat(szVersion, _T("Datacenter Edition "), cntMax);
+ #if WINVER < 0x0500
+ else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE)
+ #else
+ else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+ #endif // WINVER < 0x0500
+ _tcsncat(szVersion, _T("Enterprise Edition "), cntMax);
+ #if WINVER < 0x0500
+ else if (osvi.wReserved[0] == VER_SUITE_BLADE)
+ #else
+ else if (osvi.wSuiteMask == VER_SUITE_BLADE)
+ #endif // WINVER < 0x0500
+ _tcsncat(szVersion, _T("Web Edition "), cntMax);
+ else
+ _tcsncat(szVersion, _T("Standard Edition "), cntMax);
+ }
+ else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
+ {
+ #if WINVER < 0x0500
+ if (osvi.wReserved[0] & VER_SUITE_DATACENTER)
+ #else
+ if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+ #endif // WINVER < 0x0500
+ _tcsncat(szVersion, _T("Datacenter Server "), cntMax);
+ #if WINVER < 0x0500
+ else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE )
+ #else
+ else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
+ #endif // WINVER < 0x0500
+ _tcsncat(szVersion, _T("Advanced Server "), cntMax);
+ else
+ _tcsncat(szVersion, _T("Server "), cntMax);
+ }
+ else // Windows NT 4.0
+ {
+ #if WINVER < 0x0500
+ if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE)
+ #else
+ if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+ #endif // WINVER < 0x0500
+ _tcsncat(szVersion, _T("Server 4.0, Enterprise Edition "), cntMax);
+ else
+ _tcsncat(szVersion, _T("Server 4.0 "), cntMax);
+ }
+ }
+ }
+ // Display service pack (if any) and build number.
+ if (osvi.dwMajorVersion == 4 && _tcsicmp(osvi.szCSDVersion, _T("Service Pack 6")) == 0)
+ {
+ HKEY hKey;
+ LONG lRet;
+
+ // Test for SP6 versus SP6a.
+ lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey);
+ if (lRet == ERROR_SUCCESS)
+ {
+ _stprintf(wszTmp, _T("Service Pack 6a (Version %d.%d, Build %d)"),
+ osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
+ _tcsncat(szVersion, wszTmp, cntMax);
+ }
+ else // Windows NT 4.0 prior to SP6a
+ {
+ _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
+ osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
+ _tcsncat(szVersion, wszTmp, cntMax);
+ }
+ ::RegCloseKey(hKey);
+ }
+ else // Windows NT 3.51 and earlier or Windows 2000 and later
+ {
+ if (!_tcslen(osvi.szCSDVersion))
+ _stprintf(wszTmp, _T("(Version %d.%d, Build %d)"),
+ osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
+ else
+ _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
+ osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
+ _tcsncat(szVersion, wszTmp, cntMax);
+ }
+ break;
+ default:
+ _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
+ osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
+ _tcsncat(szVersion, wszTmp, cntMax);
+ break;
+ }
+
+ return TRUE;
+}
+
+void WheatyExceptionReport::PrintSystemInfo()
+{
+ SYSTEM_INFO SystemInfo;
+ ::GetSystemInfo(&SystemInfo);
+
+ MEMORYSTATUS MemoryStatus;
+ MemoryStatus.dwLength = sizeof (MEMORYSTATUS);
+ ::GlobalMemoryStatus(&MemoryStatus);
+ TCHAR sString[1024];
+ _tprintf(_T("//=====================================================\r\n"));
+ if (_GetProcessorName(sString, countof(sString)))
+ _tprintf(_T("*** Hardware ***\r\nProcessor: %s\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"),
+ sString, SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400);
+ else
+ _tprintf(_T("*** Hardware ***\r\nProcessor: <unknown>\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"),
+ SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400);
+
+ if(_GetWindowsVersion(sString, countof(sString)))
+ _tprintf(_T("\r\n*** Operation System ***\r\n%s\r\n"), sString);
+ else
+ _tprintf(_T("\r\n*** Operation System:\r\n<unknown>\r\n"));
+}
+
+//===========================================================================
+void WheatyExceptionReport::printTracesForAllThreads()
+{
+ HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
+ THREADENTRY32 te32;
+
+ DWORD dwOwnerPID = GetCurrentProcessId();
+ m_hProcess = GetCurrentProcess();
+ // Take a snapshot of all running threads
+ hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
+ if( hThreadSnap == INVALID_HANDLE_VALUE )
+ return;
+
+ // Fill in the size of the structure before using it.
+ te32.dwSize = sizeof(THREADENTRY32 );
+
+ // Retrieve information about the first thread,
+ // and exit if unsuccessful
+ if( !Thread32First( hThreadSnap, &te32 ) )
+ {
+ CloseHandle( hThreadSnap ); // Must clean up the
+ // snapshot object!
+ return;
+ }
+
+ // Now walk the thread list of the system,
+ // and display information about each thread
+ // associated with the specified process
+ do
+ {
+ if( te32.th32OwnerProcessID == dwOwnerPID )
+ {
+ CONTEXT context;
+ context.ContextFlags = 0xffffffff;
+ HANDLE threadHandle = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION,false, te32.th32ThreadID);
+ if(threadHandle && GetThreadContext(threadHandle, &context))
+ {
+ WriteStackDetails( &context, false, threadHandle );
+ }
+ CloseHandle(threadHandle);
+ }
+ } while( Thread32Next(hThreadSnap, &te32 ) );
+
+// Don't forget to clean up the snapshot object.
+ CloseHandle( hThreadSnap );
+}
+
+
+//===========================================================================
+// Open the report file, and write the desired information to it. Called by
+// WheatyUnhandledExceptionFilter
+//===========================================================================
+void WheatyExceptionReport::GenerateExceptionReport(
+PEXCEPTION_POINTERS pExceptionInfo )
+{
+ SYSTEMTIME systime;
+ GetLocalTime(&systime);
+
+ // Start out with a banner
+ _tprintf(_T("Revision: %s\r\n"), SVN_REVISION);
+ _tprintf(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute);
+ PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
+
+ PrintSystemInfo();
+ // First print information about the type of fault
+ _tprintf(_T("\r\n//=====================================================\r\n"));
+ _tprintf( _T("Exception code: %08X %s\r\n"),
+ pExceptionRecord->ExceptionCode,
+ GetExceptionString(pExceptionRecord->ExceptionCode) );
+
+ // Now print information about where the fault occured
+ TCHAR szFaultingModule[MAX_PATH];
+ DWORD section;
+ DWORD_PTR offset;
+ GetLogicalAddress( pExceptionRecord->ExceptionAddress,
+ szFaultingModule,
+ sizeof( szFaultingModule ),
+ section, offset );
+
+#ifdef _M_IX86
+ _tprintf( _T("Fault address: %08X %02X:%08X %s\r\n"),
+ pExceptionRecord->ExceptionAddress,
+ section, offset, szFaultingModule );
+#endif
+#ifdef _M_X64
+ _tprintf( _T("Fault address: %016I64X %02X:%016I64X %s\r\n"),
+ pExceptionRecord->ExceptionAddress,
+ section, offset, szFaultingModule );
+#endif
+
+ PCONTEXT pCtx = pExceptionInfo->ContextRecord;
+
+ // Show the registers
+ #ifdef _M_IX86 // X86 Only!
+ _tprintf( _T("\r\nRegisters:\r\n") );
+
+ _tprintf(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n")
+ ,pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx,
+ pCtx->Esi, pCtx->Edi );
+
+ _tprintf( _T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip );
+ _tprintf( _T("SS:ESP:%04X:%08X EBP:%08X\r\n"),
+ pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
+ _tprintf( _T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
+ pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs );
+ _tprintf( _T("Flags:%08X\r\n"), pCtx->EFlags );
+ #endif
+
+ #ifdef _M_X64
+ _tprintf( _T("\r\nRegisters:\r\n") );
+ _tprintf(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n")
+ _T("R8: %016I64X\r\nR9: %016I64X\r\nR10:%016I64X\r\nR11:%016I64X\r\nR12:%016I64X\r\nR13:%016I64X\r\nR14:%016I64X\r\nR15:%016I64X\r\n")
+ ,pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx,
+ pCtx->Rsi, pCtx->Rdi ,pCtx->R9,pCtx->R10,pCtx->R11,pCtx->R12,pCtx->R13,pCtx->R14,pCtx->R15);
+ _tprintf( _T("CS:RIP:%04X:%016I64X\r\n"), pCtx->SegCs, pCtx->Rip );
+ _tprintf( _T("SS:RSP:%04X:%016X RBP:%08X\r\n"),
+ pCtx->SegSs, pCtx->Rsp, pCtx->Rbp );
+ _tprintf( _T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
+ pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs );
+ _tprintf( _T("Flags:%08X\r\n"), pCtx->EFlags );
+ #endif
+
+ SymSetOptions( SYMOPT_DEFERRED_LOADS );
+
+ // Initialize DbgHelp
+ if ( !SymInitialize( GetCurrentProcess(), 0, TRUE ) )
+ {
+ _tprintf(_T("\n\rCRITICAL ERROR.\n\r Couldn't initialize the symbol handler for process.\n\rError [%s].\n\r\n\r"),
+ ErrorMessage(GetLastError()));
+ }
+
+ CONTEXT trashableContext = *pCtx;
+
+ WriteStackDetails( &trashableContext, false, NULL );
+ printTracesForAllThreads();
+
+// #ifdef _M_IX86 // X86 Only!
+
+ _tprintf( _T("========================\r\n") );
+ _tprintf( _T("Local Variables And Parameters\r\n") );
+
+ trashableContext = *pCtx;
+ WriteStackDetails( &trashableContext, true, NULL );
+
+ _tprintf( _T("========================\r\n") );
+ _tprintf( _T("Global Variables\r\n") );
+
+ SymEnumSymbols( GetCurrentProcess(),
+ (DWORD64)GetModuleHandle(szFaultingModule),
+ 0, EnumerateSymbolsCallback, 0 );
+ // #endif // X86 Only!
+
+ SymCleanup( GetCurrentProcess() );
+
+ _tprintf( _T("\r\n") );
+}
+
+//======================================================================
+// Given an exception code, returns a pointer to a static string with a
+// description of the exception
+//======================================================================
+LPTSTR WheatyExceptionReport::GetExceptionString( DWORD dwCode )
+{
+ #define EXCEPTION( x ) case EXCEPTION_##x: return _T(#x);
+
+ switch ( dwCode )
+ {
+ EXCEPTION( ACCESS_VIOLATION )
+ EXCEPTION( DATATYPE_MISALIGNMENT )
+ EXCEPTION( BREAKPOINT )
+ EXCEPTION( SINGLE_STEP )
+ EXCEPTION( ARRAY_BOUNDS_EXCEEDED )
+ EXCEPTION( FLT_DENORMAL_OPERAND )
+ EXCEPTION( FLT_DIVIDE_BY_ZERO )
+ EXCEPTION( FLT_INEXACT_RESULT )
+ EXCEPTION( FLT_INVALID_OPERATION )
+ EXCEPTION( FLT_OVERFLOW )
+ EXCEPTION( FLT_STACK_CHECK )
+ EXCEPTION( FLT_UNDERFLOW )
+ EXCEPTION( INT_DIVIDE_BY_ZERO )
+ EXCEPTION( INT_OVERFLOW )
+ EXCEPTION( PRIV_INSTRUCTION )
+ EXCEPTION( IN_PAGE_ERROR )
+ EXCEPTION( ILLEGAL_INSTRUCTION )
+ EXCEPTION( NONCONTINUABLE_EXCEPTION )
+ EXCEPTION( STACK_OVERFLOW )
+ EXCEPTION( INVALID_DISPOSITION )
+ EXCEPTION( GUARD_PAGE )
+ EXCEPTION( INVALID_HANDLE )
+ }
+
+ // If not one of the "known" exceptions, try to get the string
+ // from NTDLL.DLL's message table.
+
+ static TCHAR szBuffer[512] = { 0 };
+
+ FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
+ GetModuleHandle( _T("NTDLL.DLL") ),
+ dwCode, 0, szBuffer, sizeof( szBuffer ), 0 );
+
+ return szBuffer;
+}
+
+//=============================================================================
+// Given a linear address, locates the module, section, and offset containing
+// that address.
+//
+// Note: the szModule paramater buffer is an output buffer of length specified
+// by the len parameter (in characters!)
+//=============================================================================
+BOOL WheatyExceptionReport::GetLogicalAddress(
+PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset )
+{
+ MEMORY_BASIC_INFORMATION mbi;
+
+ if ( !VirtualQuery( addr, &mbi, sizeof(mbi) ) )
+ return FALSE;
+
+ DWORD_PTR hMod = (DWORD_PTR)mbi.AllocationBase;
+
+ if ( !GetModuleFileName( (HMODULE)hMod, szModule, len ) )
+ return FALSE;
+
+ // Point to the DOS header in memory
+ PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
+
+ // From the DOS header, find the NT (PE) header
+ PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + DWORD_PTR(pDosHdr->e_lfanew));
+
+ PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
+
+ DWORD_PTR rva = (DWORD_PTR)addr - hMod; // RVA is offset from module load address
+
+ // Iterate through the section table, looking for the one that encompasses
+ // the linear address.
+ for ( unsigned i = 0;
+ i < pNtHdr->FileHeader.NumberOfSections;
+ i++, pSection++ )
+ {
+ DWORD_PTR sectionStart = pSection->VirtualAddress;
+ DWORD_PTR sectionEnd = sectionStart
+ + DWORD_PTR(max(pSection->SizeOfRawData, pSection->Misc.VirtualSize));
+
+ // Is the address in this section???
+ if ( (rva >= sectionStart) && (rva <= sectionEnd) )
+ {
+ // Yes, address is in the section. Calculate section and offset,
+ // and store in the "section" & "offset" params, which were
+ // passed by reference.
+ section = i+1;
+ offset = rva - sectionStart;
+ return TRUE;
+ }
+ }
+
+ return FALSE; // Should never get here!
+}
+
+// It contains SYMBOL_INFO structure plus additional
+// space for the name of the symbol
+struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE
+{
+ CSymbolInfoPackage()
+ {
+ si.SizeOfStruct = sizeof(SYMBOL_INFO);
+ si.MaxNameLen = sizeof(name);
+ }
+};
+
+//============================================================
+// Walks the stack, and writes the results to the report file
+//============================================================
+void WheatyExceptionReport::WriteStackDetails(
+PCONTEXT pContext,
+bool bWriteVariables, HANDLE pThreadHandle) // true if local/params should be output
+{
+ _tprintf( _T("\r\nCall stack:\r\n") );
+
+ _tprintf( _T("Address Frame Function SourceFile\r\n") );
+
+ DWORD dwMachineType = 0;
+ // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
+
+ STACKFRAME64 sf;
+ memset( &sf, 0, sizeof(sf) );
+
+ #ifdef _M_IX86
+ // Initialize the STACKFRAME structure for the first call. This is only
+ // necessary for Intel CPUs, and isn't mentioned in the documentation.
+ sf.AddrPC.Offset = pContext->Eip;
+ sf.AddrPC.Mode = AddrModeFlat;
+ sf.AddrStack.Offset = pContext->Esp;
+ sf.AddrStack.Mode = AddrModeFlat;
+ sf.AddrFrame.Offset = pContext->Ebp;
+ sf.AddrFrame.Mode = AddrModeFlat;
+
+ dwMachineType = IMAGE_FILE_MACHINE_I386;
+ #endif
+
+#ifdef _M_X64
+ sf.AddrPC.Offset = pContext->Rip;
+ sf.AddrPC.Mode = AddrModeFlat;
+ sf.AddrStack.Offset = pContext->Rsp;
+ sf.AddrStack.Mode = AddrModeFlat;
+ sf.AddrFrame.Offset = pContext->Rbp;
+ sf.AddrFrame.Mode = AddrModeFlat;
+ dwMachineType = IMAGE_FILE_MACHINE_AMD64;
+#endif
+
+ while ( 1 )
+ {
+ // Get the next stack frame
+ if ( ! StackWalk64( dwMachineType,
+ m_hProcess,
+ pThreadHandle != NULL ? pThreadHandle : GetCurrentThread(),
+ &sf,
+ pContext,
+ 0,
+ SymFunctionTableAccess64,
+ SymGetModuleBase64,
+ 0 ) )
+ break;
+ if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure
+ break; // the frame is OK. Bail if not.
+#ifdef _M_IX86
+ _tprintf( _T("%08X %08X "), sf.AddrPC.Offset, sf.AddrFrame.Offset );
+#endif
+#ifdef _M_X64
+ _tprintf( _T("%016I64X %016I64X "), sf.AddrPC.Offset, sf.AddrFrame.Offset );
+#endif
+
+ DWORD64 symDisplacement = 0; // Displacement of the input address,
+ // relative to the start of the symbol
+
+ // Get the name of the function for this stack frame entry
+ CSymbolInfoPackage sip;
+ if ( SymFromAddr(
+ m_hProcess, // Process handle of the current process
+ sf.AddrPC.Offset, // Symbol address
+ &symDisplacement, // Address of the variable that will receive the displacement
+ &sip.si // Address of the SYMBOL_INFO structure (inside "sip" object)
+ ))
+ {
+ _tprintf( _T("%hs+%I64X"), sip.si.Name, symDisplacement );
+
+ }
+ else // No symbol found. Print out the logical address instead.
+ {
+ TCHAR szModule[MAX_PATH] = _T("");
+ DWORD section = 0;
+ DWORD_PTR offset = 0;
+
+ GetLogicalAddress( (PVOID)sf.AddrPC.Offset,
+ szModule, sizeof(szModule), section, offset );
+#ifdef _M_IX86
+ _tprintf( _T("%04X:%08X %s"), section, offset, szModule );
+#endif
+#ifdef _M_X64
+ _tprintf( _T("%04X:%016I64X %s"), section, offset, szModule );
+#endif
+ }
+
+ // Get the source line for this stack frame entry
+ IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE) };
+ DWORD dwLineDisplacement;
+ if ( SymGetLineFromAddr64( m_hProcess, sf.AddrPC.Offset,
+ &dwLineDisplacement, &lineInfo ) )
+ {
+ _tprintf(_T(" %s line %u"),lineInfo.FileName,lineInfo.LineNumber);
+ }
+
+ _tprintf( _T("\r\n") );
+
+ // Write out the variables, if desired
+ if ( bWriteVariables )
+ {
+ // Use SymSetContext to get just the locals/params for this frame
+ IMAGEHLP_STACK_FRAME imagehlpStackFrame;
+ imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
+ SymSetContext( m_hProcess, &imagehlpStackFrame, 0 );
+
+ // Enumerate the locals/parameters
+ SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &sf );
+
+ _tprintf( _T("\r\n") );
+ }
+ }
+
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// The function invoked by SymEnumSymbols
+//////////////////////////////////////////////////////////////////////////////
+
+BOOL CALLBACK
+WheatyExceptionReport::EnumerateSymbolsCallback(
+PSYMBOL_INFO pSymInfo,
+ULONG SymbolSize,
+PVOID UserContext )
+{
+
+ char szBuffer[2048];
+
+ __try
+ {
+ if ( FormatSymbolValue( pSymInfo, (STACKFRAME*)UserContext,
+ szBuffer, sizeof(szBuffer) ) )
+ _tprintf( _T("\t%s\r\n"), szBuffer );
+ }
+ __except( 1 )
+ {
+ _tprintf( _T("punting on symbol %s\r\n"), pSymInfo->Name );
+ }
+
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Given a SYMBOL_INFO representing a particular variable, displays its
+// contents. If it's a user defined type, display the members and their
+// values.
+//////////////////////////////////////////////////////////////////////////////
+bool WheatyExceptionReport::FormatSymbolValue(
+PSYMBOL_INFO pSym,
+STACKFRAME * sf,
+char * pszBuffer,
+unsigned cbBuffer )
+{
+ char * pszCurrBuffer = pszBuffer;
+
+ // Indicate if the variable is a local or parameter
+ if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER )
+ pszCurrBuffer += sprintf( pszCurrBuffer, "Parameter " );
+ else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL )
+ pszCurrBuffer += sprintf( pszCurrBuffer, "Local " );
+
+ // If it's a function, don't do anything.
+ if ( pSym->Tag == 5 ) // SymTagFunction from CVCONST.H from the DIA SDK
+ return false;
+
+ DWORD_PTR pVariable = 0; // Will point to the variable's data in memory
+
+ if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE )
+ {
+ // if ( pSym->Register == 8 ) // EBP is the value 8 (in DBGHELP 5.1)
+ { // This may change!!!
+ pVariable = sf->AddrFrame.Offset;
+ pVariable += (DWORD_PTR)pSym->Address;
+ }
+ // else
+ // return false;
+ }
+ else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER )
+ {
+ return false; // Don't try to report register variable
+ }
+ else
+ {
+ pVariable = (DWORD_PTR)pSym->Address; // It must be a global variable
+ }
+
+ // Determine if the variable is a user defined type (UDT). IF so, bHandled
+ // will return true.
+ bool bHandled;
+ pszCurrBuffer = DumpTypeIndex(pszCurrBuffer,pSym->ModBase, pSym->TypeIndex,
+ 0, pVariable, bHandled, pSym->Name );
+
+ if ( !bHandled )
+ {
+ // The symbol wasn't a UDT, so do basic, stupid formatting of the
+ // variable. Based on the size, we're assuming it's a char, WORD, or
+ // DWORD.
+ BasicType basicType = GetBasicType( pSym->TypeIndex, pSym->ModBase );
+ pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]);
+
+ // Emit the variable name
+ pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", pSym->Name );
+
+ pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType, pSym->Size,
+ (PVOID)pVariable );
+ }
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// If it's a user defined type (UDT), recurse through its members until we're
+// at fundamental types. When he hit fundamental types, return
+// bHandled = false, so that FormatSymbolValue() will format them.
+//////////////////////////////////////////////////////////////////////////////
+char * WheatyExceptionReport::DumpTypeIndex(
+char * pszCurrBuffer,
+DWORD64 modBase,
+DWORD dwTypeIndex,
+unsigned nestingLevel,
+DWORD_PTR offset,
+bool & bHandled,
+char* Name)
+{
+ bHandled = false;
+
+ // Get the name of the symbol. This will either be a Type name (if a UDT),
+ // or the structure member name.
+ WCHAR * pwszTypeName;
+ if ( SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_SYMNAME,
+ &pwszTypeName ) )
+ {
+ pszCurrBuffer += sprintf( pszCurrBuffer, " %ls", pwszTypeName );
+ LocalFree( pwszTypeName );
+ }
+
+ // Determine how many children this type has.
+ DWORD dwChildrenCount = 0;
+ SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT,
+ &dwChildrenCount );
+
+ if ( !dwChildrenCount ) // If no children, we're done
+ return pszCurrBuffer;
+
+ // Prepare to get an array of "TypeIds", representing each of the children.
+ // SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a
+ // TI_FINDCHILDREN_PARAMS struct has. Use derivation to accomplish this.
+ struct FINDCHILDREN : TI_FINDCHILDREN_PARAMS
+ {
+ ULONG MoreChildIds[1024];
+ FINDCHILDREN(){Count = sizeof(MoreChildIds) / sizeof(MoreChildIds[0]);}
+ } children;
+
+ children.Count = dwChildrenCount;
+ children.Start= 0;
+
+ // Get the array of TypeIds, one for each child type
+ if ( !SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,
+ &children ) )
+ {
+ return pszCurrBuffer;
+ }
+
+ // Append a line feed
+ pszCurrBuffer += sprintf( pszCurrBuffer, "\r\n" );
+
+ // Iterate through each of the children
+ for ( unsigned i = 0; i < dwChildrenCount; i++ )
+ {
+ // Add appropriate indentation level (since this routine is recursive)
+ for ( unsigned j = 0; j <= nestingLevel+1; j++ )
+ pszCurrBuffer += sprintf( pszCurrBuffer, "\t" );
+
+ // Recurse for each of the child types
+ bool bHandled2;
+ BasicType basicType = GetBasicType(children.ChildId[i], modBase );
+ pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]);
+
+ pszCurrBuffer = DumpTypeIndex( pszCurrBuffer, modBase,
+ children.ChildId[i], nestingLevel+1,
+ offset, bHandled2, ""/*Name */);
+
+ // If the child wasn't a UDT, format it appropriately
+ if ( !bHandled2 )
+ {
+ // Get the offset of the child member, relative to its parent
+ DWORD dwMemberOffset;
+ SymGetTypeInfo( m_hProcess, modBase, children.ChildId[i],
+ TI_GET_OFFSET, &dwMemberOffset );
+
+ // Get the real "TypeId" of the child. We need this for the
+ // SymGetTypeInfo( TI_GET_TYPEID ) call below.
+ DWORD typeId;
+ SymGetTypeInfo( m_hProcess, modBase, children.ChildId[i],
+ TI_GET_TYPEID, &typeId );
+
+ // Get the size of the child member
+ ULONG64 length;
+ SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_LENGTH,&length);
+
+ // Calculate the address of the member
+ DWORD_PTR dwFinalOffset = offset + dwMemberOffset;
+
+ // BasicType basicType = GetBasicType(children.ChildId[i], modBase );
+ //
+ // pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]);
+ //
+ // Emit the variable name
+ // pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", Name );
+
+ pszCurrBuffer = FormatOutputValue( pszCurrBuffer, basicType,
+ length, (PVOID)dwFinalOffset );
+
+ pszCurrBuffer += sprintf( pszCurrBuffer, "\r\n" );
+ }
+ }
+
+ bHandled = true;
+ return pszCurrBuffer;
+}
+
+char * WheatyExceptionReport::FormatOutputValue( char * pszCurrBuffer,
+BasicType basicType,
+DWORD64 length,
+PVOID pAddress )
+{
+ // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
+ if ( length == 1 )
+ pszCurrBuffer += sprintf( pszCurrBuffer, " = %X", *(PBYTE)pAddress );
+ else if ( length == 2 )
+ pszCurrBuffer += sprintf( pszCurrBuffer, " = %X", *(PWORD)pAddress );
+ else if ( length == 4 )
+ {
+ if ( basicType == btFloat )
+ {
+ pszCurrBuffer += sprintf(pszCurrBuffer," = %f", *(PFLOAT)pAddress);
+ }
+ else if ( basicType == btChar )
+ {
+ if ( !IsBadStringPtr( *(PSTR*)pAddress, 32) )
+ {
+ pszCurrBuffer += sprintf( pszCurrBuffer, " = \"%.31s\"",
+ *(PDWORD)pAddress );
+ }
+ else
+ pszCurrBuffer += sprintf( pszCurrBuffer, " = %X",
+ *(PDWORD)pAddress );
+ }
+ else
+ pszCurrBuffer += sprintf(pszCurrBuffer," = %X", *(PDWORD)pAddress);
+ }
+ else if ( length == 8 )
+ {
+ if ( basicType == btFloat )
+ {
+ pszCurrBuffer += sprintf( pszCurrBuffer, " = %lf",
+ *(double *)pAddress );
+ }
+ else
+ pszCurrBuffer += sprintf( pszCurrBuffer, " = %I64X",
+ *(DWORD64*)pAddress );
+ }
+
+ return pszCurrBuffer;
+}
+
+BasicType
+WheatyExceptionReport::GetBasicType( DWORD typeIndex, DWORD64 modBase )
+{
+ BasicType basicType;
+ if ( SymGetTypeInfo( m_hProcess, modBase, typeIndex,
+ TI_GET_BASETYPE, &basicType ) )
+ {
+ return basicType;
+ }
+
+ // Get the real "TypeId" of the child. We need this for the
+ // SymGetTypeInfo( TI_GET_TYPEID ) call below.
+ DWORD typeId;
+ if (SymGetTypeInfo(m_hProcess,modBase, typeIndex, TI_GET_TYPEID, &typeId))
+ {
+ if ( SymGetTypeInfo( m_hProcess, modBase, typeId, TI_GET_BASETYPE,
+ &basicType ) )
+ {
+ return basicType;
+ }
+ }
+
+ return btNoType;
+}
+
+//============================================================================
+// Helper function that writes to the report file, and allows the user to use
+// printf style formating
+//============================================================================
+int __cdecl WheatyExceptionReport::_tprintf(const TCHAR * format, ...)
+{
+ TCHAR szBuff[1024];
+ int retValue;
+ DWORD cbWritten;
+ va_list argptr;
+
+ va_start( argptr, format );
+ retValue = vsprintf( szBuff, format, argptr );
+ va_end( argptr );
+
+ WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0 );
+
+ return retValue;
+}
diff --git a/src/shared/WheatyExceptionReport.h b/src/shared/WheatyExceptionReport.h index aa77c951612..055da9a7a5d 100644 --- a/src/shared/WheatyExceptionReport.h +++ b/src/shared/WheatyExceptionReport.h @@ -1,117 +1,117 @@ -#ifndef _WHEATYEXCEPTIONREPORT_ -#define _WHEATYEXCEPTIONREPORT_ - -#include <dbghelp.h> - -#if _MSC_VER < 1400 -# define countof(array) (sizeof(array) / sizeof(array[0])) -#else -# include <stdlib.h> -# define countof _countof -#endif // _MSC_VER < 1400 - -enum BasicType // Stolen from CVCONST.H in the DIA 2.0 SDK -{ - btNoType = 0, - btVoid = 1, - btChar = 2, - btWChar = 3, - btInt = 6, - btUInt = 7, - btFloat = 8, - btBCD = 9, - btBool = 10, - btLong = 13, - btULong = 14, - btCurrency = 25, - btDate = 26, - btVariant = 27, - btComplex = 28, - btBit = 29, - btBSTR = 30, - btHresult = 31 -}; - -const char* const rgBaseType[] = -{ - " <user defined> ", // btNoType = 0, - " void ", // btVoid = 1, - " char* ", // btChar = 2, - " wchar_t* ", // btWChar = 3, - " signed char ", - " unsigned char ", - " int ", // btInt = 6, - " unsigned int ", // btUInt = 7, - " float ", // btFloat = 8, - " <BCD> ", // btBCD = 9, - " bool ", // btBool = 10, - " short ", - " unsigned short ", - " long ", // btLong = 13, - " unsigned long ", // btULong = 14, - " __int8 ", - " __int16 ", - " __int32 ", - " __int64 ", - " __int128 ", - " unsigned __int8 ", - " unsigned __int16 ", - " unsigned __int32 ", - " unsigned __int64 ", - " unsigned __int128 ", - " <currency> ", // btCurrency = 25, - " <date> ", // btDate = 26, - " VARIANT ", // btVariant = 27, - " <complex> ", // btComplex = 28, - " <bit> ", // btBit = 29, - " BSTR ", // btBSTR = 30, - " HRESULT " // btHresult = 31 -}; - -class WheatyExceptionReport -{ - public: - - WheatyExceptionReport( ); - ~WheatyExceptionReport( ); - - // entry point where control comes on an unhandled exception - static LONG WINAPI WheatyUnhandledExceptionFilter( - PEXCEPTION_POINTERS pExceptionInfo ); - - private: - - // where report info is extracted and generated - static void GenerateExceptionReport( PEXCEPTION_POINTERS pExceptionInfo ); - static void PrintSystemInfo(); - static BOOL _GetWindowsVersion(TCHAR* szVersion, DWORD cntMax); - static BOOL _GetProcessorName(TCHAR* sProcessorName, DWORD maxcount); - - // Helper functions - static LPTSTR GetExceptionString( DWORD dwCode ); - static BOOL GetLogicalAddress( PVOID addr, PTSTR szModule, DWORD len, - DWORD& section, DWORD_PTR& offset ); - - static void WriteStackDetails( PCONTEXT pContext, bool bWriteVariables ); - - static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO,ULONG, PVOID); - - static bool FormatSymbolValue( PSYMBOL_INFO, STACKFRAME *, char * pszBuffer, unsigned cbBuffer ); - - static char * DumpTypeIndex( char *, DWORD64, DWORD, unsigned, DWORD_PTR, bool & , char*); - - static char * FormatOutputValue( char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress ); - - static BasicType GetBasicType( DWORD typeIndex, DWORD64 modBase ); - - static int __cdecl _tprintf(const TCHAR * format, ...); - - // Variables used by the class - static TCHAR m_szLogFileName[MAX_PATH]; - static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter; - static HANDLE m_hReportFile; - static HANDLE m_hProcess; -}; - -extern WheatyExceptionReport g_WheatyExceptionReport; // global instance of class -#endif //WheatyExceptionReport +#ifndef _WHEATYEXCEPTIONREPORT_
+#define _WHEATYEXCEPTIONREPORT_
+
+#include <dbghelp.h>
+
+#if _MSC_VER < 1400
+# define countof(array) (sizeof(array) / sizeof(array[0]))
+#else
+# include <stdlib.h>
+# define countof _countof
+#endif // _MSC_VER < 1400
+
+enum BasicType // Stolen from CVCONST.H in the DIA 2.0 SDK
+{
+ btNoType = 0,
+ btVoid = 1,
+ btChar = 2,
+ btWChar = 3,
+ btInt = 6,
+ btUInt = 7,
+ btFloat = 8,
+ btBCD = 9,
+ btBool = 10,
+ btLong = 13,
+ btULong = 14,
+ btCurrency = 25,
+ btDate = 26,
+ btVariant = 27,
+ btComplex = 28,
+ btBit = 29,
+ btBSTR = 30,
+ btHresult = 31
+};
+
+const char* const rgBaseType[] =
+{
+ " <user defined> ", // btNoType = 0,
+ " void ", // btVoid = 1,
+ " char* ", // btChar = 2,
+ " wchar_t* ", // btWChar = 3,
+ " signed char ",
+ " unsigned char ",
+ " int ", // btInt = 6,
+ " unsigned int ", // btUInt = 7,
+ " float ", // btFloat = 8,
+ " <BCD> ", // btBCD = 9,
+ " bool ", // btBool = 10,
+ " short ",
+ " unsigned short ",
+ " long ", // btLong = 13,
+ " unsigned long ", // btULong = 14,
+ " __int8 ",
+ " __int16 ",
+ " __int32 ",
+ " __int64 ",
+ " __int128 ",
+ " unsigned __int8 ",
+ " unsigned __int16 ",
+ " unsigned __int32 ",
+ " unsigned __int64 ",
+ " unsigned __int128 ",
+ " <currency> ", // btCurrency = 25,
+ " <date> ", // btDate = 26,
+ " VARIANT ", // btVariant = 27,
+ " <complex> ", // btComplex = 28,
+ " <bit> ", // btBit = 29,
+ " BSTR ", // btBSTR = 30,
+ " HRESULT " // btHresult = 31
+};
+
+class WheatyExceptionReport
+{
+ public:
+
+ WheatyExceptionReport( );
+ ~WheatyExceptionReport( );
+
+ // entry point where control comes on an unhandled exception
+ static LONG WINAPI WheatyUnhandledExceptionFilter(
+ PEXCEPTION_POINTERS pExceptionInfo );
+
+ static void printTracesForAllThreads();
+ private:
+ // where report info is extracted and generated
+ static void GenerateExceptionReport( PEXCEPTION_POINTERS pExceptionInfo );
+ static void PrintSystemInfo();
+ static BOOL _GetWindowsVersion(TCHAR* szVersion, DWORD cntMax);
+ static BOOL _GetProcessorName(TCHAR* sProcessorName, DWORD maxcount);
+
+ // Helper functions
+ static LPTSTR GetExceptionString( DWORD dwCode );
+ static BOOL GetLogicalAddress( PVOID addr, PTSTR szModule, DWORD len,
+ DWORD& section, DWORD_PTR& offset );
+
+ static void WriteStackDetails( PCONTEXT pContext, bool bWriteVariables, HANDLE pThreadHandle);
+
+ static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO,ULONG, PVOID);
+
+ static bool FormatSymbolValue( PSYMBOL_INFO, STACKFRAME *, char * pszBuffer, unsigned cbBuffer );
+
+ static char * DumpTypeIndex( char *, DWORD64, DWORD, unsigned, DWORD_PTR, bool & , char*);
+
+ static char * FormatOutputValue( char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress );
+
+ static BasicType GetBasicType( DWORD typeIndex, DWORD64 modBase );
+
+ static int __cdecl _tprintf(const TCHAR * format, ...);
+
+ // Variables used by the class
+ static TCHAR m_szLogFileName[MAX_PATH];
+ static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter;
+ static HANDLE m_hReportFile;
+ static HANDLE m_hProcess;
+};
+
+extern WheatyExceptionReport g_WheatyExceptionReport; // global instance of class
+#endif //WheatyExceptionReport
diff --git a/src/trinitycore/TrinityCore.rc b/src/trinitycore/TrinityCore.rc index 80206f29567..4e6510c0407 100644 --- a/src/trinitycore/TrinityCore.rc +++ b/src/trinitycore/TrinityCore.rc @@ -52,8 +52,8 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,3,6731,680 - PRODUCTVERSION 0,3,6731,680 + FILEVERSION 0,4,6743,685 + PRODUCTVERSION 0,4,6743,685 FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BLOCK "080004b0" BEGIN VALUE "FileDescription", "TrinityCore" - VALUE "FileVersion", "0, 3, 6731, 680" + VALUE "FileVersion", "0, 4, 6743, 685" VALUE "InternalName", "TrinityCore" VALUE "LegalCopyright", "Copyright (C) 2008" VALUE "OriginalFilename", "TrinityCore.exe" VALUE "ProductName", "TrinityCore" - VALUE "ProductVersion", "0, 3, 6731, 680" + VALUE "ProductVersion", "0, 4, 6743, 685" END END BLOCK "VarFileInfo" diff --git a/src/trinityrealm/TrinityRealm.rc b/src/trinityrealm/TrinityRealm.rc index 385908d7e38..33c7eef719a 100644 --- a/src/trinityrealm/TrinityRealm.rc +++ b/src/trinityrealm/TrinityRealm.rc @@ -52,8 +52,8 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,3,6731,680 - PRODUCTVERSION 0,3,6731,680 + FILEVERSION 0,4,6743,685 + PRODUCTVERSION 0,4,6743,685 FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BLOCK "080004b0" BEGIN VALUE "FileDescription", "TrinityRealm" - VALUE "FileVersion", "0, 3, 6731, 680" + VALUE "FileVersion", "0, 4, 6743, 685" VALUE "InternalName", "TrinityRealm" VALUE "LegalCopyright", "Copyright (C) 2008" VALUE "OriginalFilename", "TrinityRealm.exe" VALUE "ProductName", "TrinityRealm" - VALUE "ProductVersion", "0, 3, 6731, 680" + VALUE "ProductVersion", "0, 4, 6743, 685" END END BLOCK "VarFileInfo" |